Files
codex-proxy-rs/frontend/src/components/base/BaseButton.vue
T
ZyyandCodex 93dc417b79 fix(observability): preserve failure causes and complete log retention
Preserve safe failure stages and causes through OpenAI and xAI transport
mapping, Core traces and persisted errors without changing replay safety.
Distinguish missing WebSocket close handshakes from TCP resets and surface
connection facts in the borderless diagnostics panel.

Retain complete UTC date groups instead of limiting segment counts. Default
application and OAuth recovery logs to seven days and full payload logs to
one day. Compress closed segments, drain bounded file queues on shutdown
and report write or maintenance failures through the logging health probe.
Normalize file names and expose an independent OAuth recovery switch that
defaults to off. Remove max_files; leave old file names for manual cleanup.

Validation: workspace backend tests with isolated PostgreSQL, final Host
and application tests (104 passed), OpenAI tests (542 passed), xAI tests
(371 passed), rustfmt and strict Clippy. Frontend lint/build, light/dark
and mobile browser checks, and Compose validation passed. Redis integration
tests were not configured and followed the existing skip convention.

Co-authored-by: Codex <noreply@openai.com>
2026-09-08 17:15:42 +08:00

79 lines
2.9 KiB
Vue

<script setup lang="ts">
import { LoaderCircle } from '@lucide/vue'
import { computed } from 'vue'
type ButtonVariant = 'primary' | 'secondary' | 'soft' | 'ghost' | 'destructive'
type ButtonSize = 'sm' | 'md' | 'lg'
const props = withDefaults(
defineProps<{
variant?: ButtonVariant
size?: ButtonSize
loading?: boolean
disabled?: boolean
type?: 'button' | 'submit' | 'reset'
}>(),
{
variant: 'secondary',
size: 'md',
loading: false,
disabled: false,
type: 'button',
},
)
defineSlots<{
icon?: () => unknown
loading?: () => unknown
default: () => unknown
}>()
const variantClasses: Record<ButtonVariant, string> = {
primary:
'bg-(--cp-button-primary-bg) text-(--cp-button-primary-color) shadow-cp-tertiary hover:bg-(--cp-button-primary-hover-bg) active:bg-(--cp-button-primary-active-bg)',
secondary:
'bg-cp-fill-tertiary text-cp-text shadow-cp-tertiary hover:bg-cp-bg-text-active active:bg-cp-bg-text-active',
soft:
'bg-cp-primary-container text-cp-primary-on-container shadow-none hover:bg-cp-primary-container-hover active:bg-cp-primary-container-hover',
ghost:
'bg-transparent text-cp-text-secondary shadow-none hover:bg-cp-fill-quaternary hover:text-cp-text active:bg-cp-fill-tertiary',
destructive: 'bg-cp-error-container text-cp-error-on-container shadow-none hover:bg-cp-error-container-hover active:bg-cp-error-container-active',
}
const sizeClasses: Record<ButtonSize, string> = {
sm: 'h-cp-control-sm gap-1.5 px-3 text-xs',
md: 'h-cp-control gap-2 px-4 text-cp',
lg: 'h-cp-control-lg gap-2.5 px-5 text-cp-lg',
}
const spinnerSizes: Record<ButtonSize, number> = {
sm: 14,
md: 15,
lg: 17,
}
const classes = computed(() => [
'inline-flex shrink-0 touch-manipulation items-center justify-center rounded-cp-sm border-0 font-bold leading-none outline-none transition-[background-color,box-shadow,color,opacity,transform] duration-150 motion-safe:active:translate-y-px motion-safe:active:scale-[0.985] motion-reduce:transition-none',
'focus-visible:ring-2 focus-visible:ring-cp-control-outline focus-visible:ring-offset-2 focus-visible:ring-offset-cp-bg-container',
'disabled:cursor-not-allowed disabled:transform-none disabled:bg-cp-bg-container-disabled disabled:text-cp-text-disabled disabled:shadow-none',
sizeClasses[props.size],
variantClasses[props.variant],
])
</script>
<template>
<button :type="type" :class="classes" :disabled="disabled || loading" :aria-busy="loading || undefined">
<span v-if="loading" class="inline-grid shrink-0 place-items-center leading-none">
<slot name="loading">
<LoaderCircle class="animate-spin motion-reduce:animate-none" :size="spinnerSizes[size]" />
</slot>
</span>
<span v-else-if="$slots.icon" class="inline-grid shrink-0 place-items-center">
<slot name="icon" />
</span>
<span class="inline-flex min-w-0 items-center justify-center gap-2">
<slot />
</span>
</button>
</template>