mirror of
https://github.com/zyycn/codex-proxy-rs.git
synced 2026-11-06 08:06:19 +00:00
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>
79 lines
2.9 KiB
Vue
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>
|