chore: add husky hook

This commit is contained in:
Acbox
2026-02-18 22:21:32 +08:00
parent 3a441aaac2
commit b19df8156a
12 changed files with 255 additions and 69 deletions
+7
View File
@@ -0,0 +1,7 @@
#!/usr/bin/env sh
export PATH="$HOME/.local/share/mise/shims:$HOME/.local/bin:$HOME/.npm-global/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"
pnpm lint:fix || true
pnpm web:build
+6 -4
View File
@@ -9,14 +9,15 @@
"docs:dev": "pnpm --filter @memoh/docs dev", "docs:dev": "pnpm --filter @memoh/docs dev",
"docs:build": "pnpm --filter @memoh/docs build", "docs:build": "pnpm --filter @memoh/docs build",
"docs:preview": "pnpm --filter @memoh/docs preview", "docs:preview": "pnpm --filter @memoh/docs preview",
"agent:dev": "go run ./agent-go/cmd/agent_gateway", "agent:dev": "pnpm --filter @memoh/agent-gateway dev",
"agent:build": "go build ./agent-go/cmd/agent_gateway", "agent:build": "pnpm --filter @memoh/agent-gateway build",
"agent:start": "go run ./agent-go/cmd/agent_gateway", "agent:start": "pnpm --filter @memoh/agent-gateway start",
"release": "bumpp --all", "release": "bumpp --all",
"generate-sdk": "openapi-ts", "generate-sdk": "openapi-ts",
"lint": "eslint .", "lint": "eslint .",
"lint:fix": "eslint . --fix", "lint:fix": "eslint . --fix",
"test": "vitest" "test": "vitest",
"prepare": "husky"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",
@@ -27,6 +28,7 @@
"bumpp": "^10.4.1", "bumpp": "^10.4.1",
"eslint": "^9.39.2", "eslint": "^9.39.2",
"eslint-plugin-vue": "^10.6.2", "eslint-plugin-vue": "^10.6.2",
"husky": "^9.1.7",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"typescript-eslint": "^8.52.0", "typescript-eslint": "^8.52.0",
"vitest": "^4.0.16", "vitest": "^4.0.16",
@@ -4,7 +4,9 @@
<div class="w-64 shrink-0 flex flex-col border rounded-lg overflow-hidden bg-card max-h-full"> <div class="w-64 shrink-0 flex flex-col border rounded-lg overflow-hidden bg-card max-h-full">
<div class="p-3 border-b space-y-3 shrink-0"> <div class="p-3 border-b space-y-3 shrink-0">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<h4 class="text-sm font-medium">{{ $t('bots.memory.files') }}</h4> <h4 class="text-sm font-medium">
{{ $t('bots.memory.files') }}
</h4>
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<Button <Button
variant="ghost" variant="ghost"
@@ -152,7 +154,10 @@
:disabled="actionLoading || !isDirty" :disabled="actionLoading || !isDirty"
@click="handleSave" @click="handleSave"
> >
<Spinner v-if="actionLoading" class="mr-1.5 size-3" /> <Spinner
v-if="actionLoading"
class="mr-1.5 size-3"
/>
{{ $t('common.save') }} {{ $t('common.save') }}
</Button> </Button>
</div> </div>
@@ -169,12 +174,16 @@
<!-- Charts Section --> <!-- Charts Section -->
<div class="h-[240px] border-t flex flex-col bg-muted/5 shrink-0"> <div class="h-[240px] border-t flex flex-col bg-muted/5 shrink-0">
<div class="px-3 py-1.5 border-b bg-muted/10 flex items-center justify-between shrink-0"> <div class="px-3 py-1.5 border-b bg-muted/10 flex items-center justify-between shrink-0">
<h5 class="text-[10px] font-bold uppercase tracking-wider text-muted-foreground/70">Vector Manifold</h5> <h5 class="text-[10px] font-bold uppercase tracking-wider text-muted-foreground/70">
Vector Manifold
</h5>
</div> </div>
<div class="flex-1 flex min-h-0 divide-x overflow-hidden"> <div class="flex-1 flex min-h-0 divide-x overflow-hidden">
<!-- Top K Buckets (Bar Chart) --> <!-- Top K Buckets (Bar Chart) -->
<div class="flex-1 flex flex-col p-3 min-w-0"> <div class="flex-1 flex flex-col p-3 min-w-0">
<p class="text-[9px] font-semibold text-muted-foreground/60 mb-2 uppercase shrink-0">Top-K Bucket</p> <p class="text-[9px] font-semibold text-muted-foreground/60 mb-2 uppercase shrink-0">
Top-K Bucket
</p>
<div class="flex-1 flex items-end gap-0.5 relative group min-h-0 pt-2 pb-4"> <div class="flex-1 flex items-end gap-0.5 relative group min-h-0 pt-2 pb-4">
<div <div
v-for="(bucket, idx) in selectedMemory.top_k_buckets" v-for="(bucket, idx) in selectedMemory.top_k_buckets"
@@ -187,7 +196,9 @@
> >
<!-- Tooltip for Bar --> <!-- Tooltip for Bar -->
<div class="absolute z-20 bottom-full left-1/2 -translate-x-1/2 mb-1 bg-popover border text-popover-foreground px-2 py-1 rounded shadow-lg text-[10px] hidden group-hover/bar:block whitespace-nowrap pointer-events-none"> <div class="absolute z-20 bottom-full left-1/2 -translate-x-1/2 mb-1 bg-popover border text-popover-foreground px-2 py-1 rounded shadow-lg text-[10px] hidden group-hover/bar:block whitespace-nowrap pointer-events-none">
<p class="font-bold text-primary">Index: {{ bucket.index }}</p> <p class="font-bold text-primary">
Index: {{ bucket.index }}
</p>
<p>Value: {{ bucket.value.toFixed(6) }}</p> <p>Value: {{ bucket.value.toFixed(6) }}</p>
</div> </div>
</div> </div>
@@ -201,11 +212,25 @@
<!-- CDF Curve (Line Chart) --> <!-- CDF Curve (Line Chart) -->
<div class="flex-1 flex flex-col p-3 min-w-0"> <div class="flex-1 flex flex-col p-3 min-w-0">
<p class="text-[9px] font-semibold text-muted-foreground/60 mb-2 uppercase shrink-0">Energy Gradient (CDF)</p> <p class="text-[9px] font-semibold text-muted-foreground/60 mb-2 uppercase shrink-0">
Energy Gradient (CDF)
</p>
<div class="flex-1 relative min-h-0 pt-2 pb-4 group/cdf"> <div class="flex-1 relative min-h-0 pt-2 pb-4 group/cdf">
<svg class="w-full h-full overflow-visible" viewBox="0 0 100 100" preserveAspectRatio="none"> <svg
class="w-full h-full overflow-visible"
viewBox="0 0 100 100"
preserveAspectRatio="none"
>
<!-- Grid lines --> <!-- Grid lines -->
<line x1="0" y1="50" x2="100" y2="50" stroke="currentColor" class="text-muted-foreground/5" stroke-width="0.5" /> <line
x1="0"
y1="50"
x2="100"
y2="50"
stroke="currentColor"
class="text-muted-foreground/5"
stroke-width="0.5"
/>
<!-- Area under curve --> <!-- Area under curve -->
<path <path
@@ -273,8 +298,12 @@
transform: 'translate(-50%, -140%)' transform: 'translate(-50%, -140%)'
}" }"
> >
<p class="font-bold text-primary">K: {{ hoveredCdfPoint.k }}</p> <p class="font-bold text-primary">
<p class="font-mono">P: {{ hoveredCdfPoint.cumulative.toFixed(6) }}</p> K: {{ hoveredCdfPoint.k }}
</p>
<p class="font-mono">
P: {{ hoveredCdfPoint.cumulative.toFixed(6) }}
</p>
</div> </div>
<!-- Axis labels --> <!-- Axis labels -->
@@ -301,7 +330,9 @@
class="size-6 opacity-20" class="size-6 opacity-20"
/> />
</div> </div>
<h3 class="text-sm font-medium text-foreground">{{ $t('bots.memory.title') }}</h3> <h3 class="text-sm font-medium text-foreground">
{{ $t('bots.memory.title') }}
</h3>
<p class="text-xs mt-1 max-w-[240px]"> <p class="text-xs mt-1 max-w-[240px]">
Select a file from the sidebar to view or edit, or create a new one to persist long-term information for your bot. Select a file from the sidebar to view or edit, or create a new one to persist long-term information for your bot.
</p> </p>
@@ -403,7 +434,10 @@
:disabled="actionLoading || !newMemoryContent.trim()" :disabled="actionLoading || !newMemoryContent.trim()"
@click="handleCreateMemory" @click="handleCreateMemory"
> >
<Spinner v-if="actionLoading" class="mr-1.5 size-3" /> <Spinner
v-if="actionLoading"
class="mr-1.5 size-3"
/>
{{ $t('common.confirm') }} {{ $t('common.confirm') }}
</Button> </Button>
</DialogFooter> </DialogFooter>
@@ -432,7 +466,10 @@
class="flex items-start gap-3 p-3 rounded-md border cursor-pointer hover:bg-muted/50 transition-colors" class="flex items-start gap-3 p-3 rounded-md border cursor-pointer hover:bg-muted/50 transition-colors"
:class="{ 'bg-muted border-primary': compactRatio === '0.8' }" :class="{ 'bg-muted border-primary': compactRatio === '0.8' }"
> >
<RadioGroupItem value="0.8" class="mt-1" /> <RadioGroupItem
value="0.8"
class="mt-1"
/>
<div class="min-w-0"> <div class="min-w-0">
<p class="text-sm font-medium">{{ $t('bots.memory.compactRatioLight') }}</p> <p class="text-sm font-medium">{{ $t('bots.memory.compactRatioLight') }}</p>
<p class="text-xs text-muted-foreground">{{ $t('bots.memory.compactRatioLightDesc') }}</p> <p class="text-xs text-muted-foreground">{{ $t('bots.memory.compactRatioLightDesc') }}</p>
@@ -442,7 +479,10 @@
class="flex items-start gap-3 p-3 rounded-md border cursor-pointer hover:bg-muted/50 transition-colors" class="flex items-start gap-3 p-3 rounded-md border cursor-pointer hover:bg-muted/50 transition-colors"
:class="{ 'bg-muted border-primary': compactRatio === '0.5' }" :class="{ 'bg-muted border-primary': compactRatio === '0.5' }"
> >
<RadioGroupItem value="0.5" class="mt-1" /> <RadioGroupItem
value="0.5"
class="mt-1"
/>
<div class="min-w-0"> <div class="min-w-0">
<p class="text-sm font-medium">{{ $t('bots.memory.compactRatioMedium') }}</p> <p class="text-sm font-medium">{{ $t('bots.memory.compactRatioMedium') }}</p>
<p class="text-xs text-muted-foreground">{{ $t('bots.memory.compactRatioMediumDesc') }}</p> <p class="text-xs text-muted-foreground">{{ $t('bots.memory.compactRatioMediumDesc') }}</p>
@@ -452,7 +492,10 @@
class="flex items-start gap-3 p-3 rounded-md border cursor-pointer hover:bg-muted/50 transition-colors" class="flex items-start gap-3 p-3 rounded-md border cursor-pointer hover:bg-muted/50 transition-colors"
:class="{ 'bg-muted border-primary': compactRatio === '0.3' }" :class="{ 'bg-muted border-primary': compactRatio === '0.3' }"
> >
<RadioGroupItem value="0.3" class="mt-1" /> <RadioGroupItem
value="0.3"
class="mt-1"
/>
<div class="min-w-0"> <div class="min-w-0">
<p class="text-sm font-medium">{{ $t('bots.memory.compactRatioAggressive') }}</p> <p class="text-sm font-medium">{{ $t('bots.memory.compactRatioAggressive') }}</p>
<p class="text-xs text-muted-foreground">{{ $t('bots.memory.compactRatioAggressiveDesc') }}</p> <p class="text-xs text-muted-foreground">{{ $t('bots.memory.compactRatioAggressiveDesc') }}</p>
@@ -468,7 +511,10 @@
type="date" type="date"
class="w-full" class="w-full"
/> />
<p v-if="compactDecayDays > 0" class="text-[10px] text-muted-foreground"> <p
v-if="compactDecayDays > 0"
class="text-[10px] text-muted-foreground"
>
Calculated: {{ compactDecayDays }} days old Calculated: {{ compactDecayDays }} days old
</p> </p>
</div> </div>
@@ -485,7 +531,10 @@
:disabled="compactLoading" :disabled="compactLoading"
@click="handleCompact" @click="handleCompact"
> >
<Spinner v-if="compactLoading" class="mr-1.5 size-3" /> <Spinner
v-if="compactLoading"
class="mr-1.5 size-3"
/>
{{ $t('common.confirm') }} {{ $t('common.confirm') }}
</Button> </Button>
</DialogFooter> </DialogFooter>
@@ -870,7 +919,7 @@ function generateSmoothPath(data: any[], closePath: boolean = false) {
} }
if (closePath) { if (closePath) {
d += ` L 100,100 L 0,100 Z` d += ' L 100,100 L 0,100 Z'
} }
return d return d
@@ -125,7 +125,10 @@
variant="destructive" variant="destructive"
:disabled="deleteLoading" :disabled="deleteLoading"
> >
<Spinner v-if="deleteLoading" class="mr-1.5" /> <Spinner
v-if="deleteLoading"
class="mr-1.5"
/>
{{ $t('bots.settings.deleteBot') }} {{ $t('bots.settings.deleteBot') }}
</Button> </Button>
</template> </template>
+72 -24
View File
@@ -138,7 +138,9 @@
<div class="rounded-md border p-4"> <div class="rounded-md border p-4">
<div class="flex items-center justify-between gap-2"> <div class="flex items-center justify-between gap-2">
<div> <div>
<p class="text-sm font-medium">{{ $t('bots.checks.title') }}</p> <p class="text-sm font-medium">
{{ $t('bots.checks.title') }}
</p>
<p class="text-sm text-muted-foreground"> <p class="text-sm text-muted-foreground">
{{ $t('bots.checks.subtitle') }} {{ $t('bots.checks.subtitle') }}
</p> </p>
@@ -191,7 +193,9 @@
> >
<div class="flex items-center justify-between gap-2"> <div class="flex items-center justify-between gap-2">
<div class="min-w-0"> <div class="min-w-0">
<p class="font-mono text-xs">{{ checkTitleLabel(item) }}</p> <p class="font-mono text-xs">
{{ checkTitleLabel(item) }}
</p>
<p <p
v-if="item.subtitle" v-if="item.subtitle"
class="mt-0.5 text-xs text-muted-foreground" class="mt-0.5 text-xs text-muted-foreground"
@@ -206,7 +210,9 @@
{{ checkStatusLabel(item.status) }} {{ checkStatusLabel(item.status) }}
</Badge> </Badge>
</div> </div>
<p class="mt-2 text-sm">{{ item.summary }}</p> <p class="mt-2 text-sm">
{{ item.summary }}
</p>
<p <p
v-if="item.detail" v-if="item.detail"
class="mt-1 text-xs text-muted-foreground break-all" class="mt-1 text-xs text-muted-foreground break-all"
@@ -335,39 +341,65 @@
<div class="rounded-md border p-4"> <div class="rounded-md border p-4">
<dl class="grid grid-cols-1 gap-3 text-sm sm:grid-cols-2"> <dl class="grid grid-cols-1 gap-3 text-sm sm:grid-cols-2">
<div class="space-y-1"> <div class="space-y-1">
<dt class="text-muted-foreground">{{ $t('bots.container.fields.id') }}</dt> <dt class="text-muted-foreground">
<dd class="font-mono break-all">{{ containerInfo.container_id }}</dd> {{ $t('bots.container.fields.id') }}
</dt>
<dd class="font-mono break-all">
{{ containerInfo.container_id }}
</dd>
</div> </div>
<div class="space-y-1"> <div class="space-y-1">
<dt class="text-muted-foreground">{{ $t('bots.container.fields.status') }}</dt> <dt class="text-muted-foreground">
{{ $t('bots.container.fields.status') }}
</dt>
<dd>{{ containerStatusText }}</dd> <dd>{{ containerStatusText }}</dd>
</div> </div>
<div class="space-y-1"> <div class="space-y-1">
<dt class="text-muted-foreground">{{ $t('bots.container.fields.task') }}</dt> <dt class="text-muted-foreground">
{{ $t('bots.container.fields.task') }}
</dt>
<dd>{{ containerTaskText }}</dd> <dd>{{ containerTaskText }}</dd>
</div> </div>
<div class="space-y-1"> <div class="space-y-1">
<dt class="text-muted-foreground">{{ $t('bots.container.fields.namespace') }}</dt> <dt class="text-muted-foreground">
{{ $t('bots.container.fields.namespace') }}
</dt>
<dd>{{ containerInfo.namespace }}</dd> <dd>{{ containerInfo.namespace }}</dd>
</div> </div>
<div class="space-y-1 sm:col-span-2"> <div class="space-y-1 sm:col-span-2">
<dt class="text-muted-foreground">{{ $t('bots.container.fields.image') }}</dt> <dt class="text-muted-foreground">
<dd class="break-all">{{ containerInfo.image }}</dd> {{ $t('bots.container.fields.image') }}
</dt>
<dd class="break-all">
{{ containerInfo.image }}
</dd>
</div> </div>
<div class="space-y-1 sm:col-span-2"> <div class="space-y-1 sm:col-span-2">
<dt class="text-muted-foreground">{{ $t('bots.container.fields.hostPath') }}</dt> <dt class="text-muted-foreground">
<dd class="break-all">{{ containerInfo.host_path || '-' }}</dd> {{ $t('bots.container.fields.hostPath') }}
</dt>
<dd class="break-all">
{{ containerInfo.host_path || '-' }}
</dd>
</div> </div>
<div class="space-y-1 sm:col-span-2"> <div class="space-y-1 sm:col-span-2">
<dt class="text-muted-foreground">{{ $t('bots.container.fields.containerPath') }}</dt> <dt class="text-muted-foreground">
<dd class="break-all">{{ containerInfo.container_path }}</dd> {{ $t('bots.container.fields.containerPath') }}
</dt>
<dd class="break-all">
{{ containerInfo.container_path }}
</dd>
</div> </div>
<div class="space-y-1"> <div class="space-y-1">
<dt class="text-muted-foreground">{{ $t('bots.container.fields.createdAt') }}</dt> <dt class="text-muted-foreground">
{{ $t('bots.container.fields.createdAt') }}
</dt>
<dd>{{ formatDate(containerInfo.created_at) }}</dd> <dd>{{ formatDate(containerInfo.created_at) }}</dd>
</div> </div>
<div class="space-y-1"> <div class="space-y-1">
<dt class="text-muted-foreground">{{ $t('bots.container.fields.updatedAt') }}</dt> <dt class="text-muted-foreground">
{{ $t('bots.container.fields.updatedAt') }}
</dt>
<dd>{{ formatDate(containerInfo.updated_at) }}</dd> <dd>{{ formatDate(containerInfo.updated_at) }}</dd>
</div> </div>
</dl> </dl>
@@ -414,10 +446,18 @@
<table class="w-full text-sm"> <table class="w-full text-sm">
<thead class="bg-muted/50 text-left"> <thead class="bg-muted/50 text-left">
<tr> <tr>
<th class="px-3 py-2 font-medium">{{ $t('bots.container.snapshotColumns.name') }}</th> <th class="px-3 py-2 font-medium">
<th class="px-3 py-2 font-medium">{{ $t('bots.container.snapshotColumns.kind') }}</th> {{ $t('bots.container.snapshotColumns.name') }}
<th class="px-3 py-2 font-medium">{{ $t('bots.container.snapshotColumns.parent') }}</th> </th>
<th class="px-3 py-2 font-medium">{{ $t('bots.container.snapshotColumns.createdAt') }}</th> <th class="px-3 py-2 font-medium">
{{ $t('bots.container.snapshotColumns.kind') }}
</th>
<th class="px-3 py-2 font-medium">
{{ $t('bots.container.snapshotColumns.parent') }}
</th>
<th class="px-3 py-2 font-medium">
{{ $t('bots.container.snapshotColumns.createdAt') }}
</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -426,10 +466,18 @@
:key="`${item.snapshotter}:${item.name}`" :key="`${item.snapshotter}:${item.name}`"
class="border-t" class="border-t"
> >
<td class="px-3 py-2 font-mono text-xs break-all">{{ item.name }}</td> <td class="px-3 py-2 font-mono text-xs break-all">
<td class="px-3 py-2">{{ item.kind }}</td> {{ item.name }}
<td class="px-3 py-2 break-all">{{ item.parent || '-' }}</td> </td>
<td class="px-3 py-2">{{ formatDate(item.created_at) }}</td> <td class="px-3 py-2">
{{ item.kind }}
</td>
<td class="px-3 py-2 break-all">
{{ item.parent || '-' }}
</td>
<td class="px-3 py-2">
{{ formatDate(item.created_at) }}
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@@ -17,7 +17,7 @@
:alt="String(att.name ?? 'image')" :alt="String(att.name ?? 'image')"
class="w-full h-full object-contain pointer-events-none" class="w-full h-full object-contain pointer-events-none"
loading="lazy" loading="lazy"
/> >
<video <video
v-else v-else
:src="getUrl(att)" :src="getUrl(att)"
@@ -6,8 +6,12 @@
class="flex-1 flex items-center justify-center text-muted-foreground" class="flex-1 flex items-center justify-center text-muted-foreground"
> >
<div class="text-center"> <div class="text-center">
<p class="text-lg">{{ $t('chat.selectBot') }}</p> <p class="text-lg">
<p class="text-sm mt-1">{{ $t('chat.selectBotHint') }}</p> {{ $t('chat.selectBot') }}
</p>
<p class="text-sm mt-1">
{{ $t('chat.selectBotHint') }}
</p>
</div> </div>
</div> </div>
@@ -108,7 +112,10 @@
class="ml-1 text-muted-foreground hover:text-foreground" class="ml-1 text-muted-foreground hover:text-foreground"
@click="pendingFiles.splice(i, 1)" @click="pendingFiles.splice(i, 1)"
> >
<FontAwesomeIcon :icon="['fas', 'xmark']" class="size-3" /> <FontAwesomeIcon
:icon="['fas', 'xmark']"
class="size-3"
/>
</button> </button>
</div> </div>
</div> </div>
@@ -128,7 +135,7 @@
multiple multiple
accept="image/*,audio/*,video/*,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.csv,.zip" accept="image/*,audio/*,video/*,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.csv,.zip"
@change="handleFileSelect" @change="handleFileSelect"
/> >
<div class="absolute right-2 bottom-2 flex items-center gap-1"> <div class="absolute right-2 bottom-2 flex items-center gap-1">
<Button <Button
v-if="!streaming" v-if="!streaming"
@@ -137,7 +144,10 @@
:disabled="!currentBotId || activeChatReadOnly" :disabled="!currentBotId || activeChatReadOnly"
@click="fileInput?.click()" @click="fileInput?.click()"
> >
<FontAwesomeIcon :icon="['fas', 'paperclip']" class="size-3.5" /> <FontAwesomeIcon
:icon="['fas', 'paperclip']"
class="size-3.5"
/>
</Button> </Button>
<Button <Button
v-if="!streaming" v-if="!streaming"
@@ -145,7 +155,10 @@
:disabled="(!inputText.trim() && !pendingFiles.length) || !currentBotId || activeChatReadOnly" :disabled="(!inputText.trim() && !pendingFiles.length) || !currentBotId || activeChatReadOnly"
@click="handleSend" @click="handleSend"
> >
<FontAwesomeIcon :icon="['fas', 'paper-plane']" class="size-3.5" /> <FontAwesomeIcon
:icon="['fas', 'paper-plane']"
class="size-3.5"
/>
</Button> </Button>
<Button <Button
v-else v-else
@@ -153,7 +166,10 @@
variant="destructive" variant="destructive"
@click="chatStore.abort()" @click="chatStore.abort()"
> >
<FontAwesomeIcon :icon="['fas', 'spinner']" class="size-3.5 animate-spin" /> <FontAwesomeIcon
:icon="['fas', 'spinner']"
class="size-3.5 animate-spin"
/>
</Button> </Button>
</div> </div>
</div> </div>
@@ -12,7 +12,10 @@
aria-label="Close" aria-label="Close"
@click="close" @click="close"
> >
<FontAwesomeIcon :icon="['fas', 'xmark']" class="size-6" /> <FontAwesomeIcon
:icon="['fas', 'xmark']"
class="size-6"
/>
</button> </button>
<!-- Prev --> <!-- Prev -->
@@ -22,7 +25,10 @@
aria-label="Previous" aria-label="Previous"
@click.stop="prev" @click.stop="prev"
> >
<FontAwesomeIcon :icon="['fas', 'chevron-left']" class="size-6" /> <FontAwesomeIcon
:icon="['fas', 'chevron-left']"
class="size-6"
/>
</button> </button>
<!-- Next --> <!-- Next -->
@@ -32,7 +38,10 @@
aria-label="Next" aria-label="Next"
@click.stop="next" @click.stop="next"
> >
<FontAwesomeIcon :icon="['fas', 'chevron-right']" class="size-6" /> <FontAwesomeIcon
:icon="['fas', 'chevron-right']"
class="size-6"
/>
</button> </button>
<!-- Media content --> <!-- Media content -->
@@ -44,7 +53,7 @@
class="max-w-full max-h-[90vh] object-contain select-none" class="max-w-full max-h-[90vh] object-contain select-none"
draggable="false" draggable="false"
@click.stop @click.stop
/> >
<video <video
v-else-if="currentItem?.type === 'video'" v-else-if="currentItem?.type === 'video'"
:src="currentItem.src" :src="currentItem.src"
@@ -27,8 +27,11 @@
</div> </div>
<!-- Input (collapsible) --> <!-- Input (collapsible) -->
<Collapsible v-if="block.input" v-model:open="inputOpen"> <Collapsible
<CollapsibleTrigger class="flex items-center gap-1.5 px-3 py-1.5 text-xs text-muted-foreground hover:text-foreground cursor-pointer w-full"> v-if="block.input"
v-model:open="inputOpen"
>
<CollapsibleTrigger class="flex items-center gap-1.5 px-3 py-1.5 text-xs text-muted-foreground hover:text-foreground cursor-pointer w-full">
<FontAwesomeIcon <FontAwesomeIcon
:icon="['fas', 'chevron-right']" :icon="['fas', 'chevron-right']"
class="size-2.5 transition-transform" class="size-2.5 transition-transform"
@@ -42,8 +45,11 @@
</Collapsible> </Collapsible>
<!-- Result (collapsible) --> <!-- Result (collapsible) -->
<Collapsible v-if="block.done && block.result != null" v-model:open="resultOpen"> <Collapsible
<CollapsibleTrigger class="flex items-center gap-1.5 px-3 py-1.5 text-xs text-muted-foreground hover:text-foreground cursor-pointer w-full border-t border-muted"> v-if="block.done && block.result != null"
v-model:open="resultOpen"
>
<CollapsibleTrigger class="flex items-center gap-1.5 px-3 py-1.5 text-xs text-muted-foreground hover:text-foreground cursor-pointer w-full border-t border-muted">
<FontAwesomeIcon <FontAwesomeIcon
:icon="['fas', 'chevron-right']" :icon="['fas', 'chevron-right']"
class="size-2.5 transition-transform" class="size-2.5 transition-transform"
+14 -4
View File
@@ -10,8 +10,12 @@
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectGroup> <SelectGroup>
<SelectItem value="en">English</SelectItem> <SelectItem value="en">
<SelectItem value="zh">中文</SelectItem> English
</SelectItem>
<SelectItem value="zh">
中文
</SelectItem>
</SelectGroup> </SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
@@ -20,8 +24,14 @@
size="icon" size="icon"
@click="toggleTheme" @click="toggleTheme"
> >
<Sun v-if="theme === 'dark'" class="size-5" /> <Sun
<Moon v-else class="size-5" /> v-if="theme === 'dark'"
class="size-5"
/>
<Moon
v-else
class="size-5"
/>
</Button> </Button>
</div> </div>
<section class="w-full max-w-sm flex flex-col gap-10 "> <section class="w-full max-w-sm flex flex-col gap-10 ">
@@ -2,7 +2,10 @@
<div class="space-y-4"> <div class="space-y-4">
<div class="space-y-2"> <div class="space-y-2">
<Label>API Key</Label> <Label>API Key</Label>
<Input v-model="localConfig.api_key" type="password" /> <Input
v-model="localConfig.api_key"
type="password"
/>
</div> </div>
<div class="space-y-2"> <div class="space-y-2">
<Label>Base URL</Label> <Label>Base URL</Label>
@@ -10,7 +13,11 @@
</div> </div>
<div class="space-y-2"> <div class="space-y-2">
<Label>Timeout (seconds)</Label> <Label>Timeout (seconds)</Label>
<Input v-model.number="localConfig.timeout_seconds" type="number" :min="1" /> <Input
v-model.number="localConfig.timeout_seconds"
type="number"
:min="1"
/>
</div> </div>
</div> </div>
</template> </template>
+29
View File
@@ -30,6 +30,9 @@ importers:
eslint-plugin-vue: eslint-plugin-vue:
specifier: ^10.6.2 specifier: ^10.6.2
version: 10.6.2(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1))) version: 10.6.2(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1)))
husky:
specifier: ^9.1.7
version: 9.1.7
typescript: typescript:
specifier: ^5.9.3 specifier: ^5.9.3
version: 5.9.3 version: 5.9.3
@@ -1743,56 +1746,67 @@ packages:
resolution: {integrity: sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==} resolution: {integrity: sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.54.0': '@rollup/rollup-linux-arm-musleabihf@4.54.0':
resolution: {integrity: sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==} resolution: {integrity: sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.54.0': '@rollup/rollup-linux-arm64-gnu@4.54.0':
resolution: {integrity: sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==} resolution: {integrity: sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.54.0': '@rollup/rollup-linux-arm64-musl@4.54.0':
resolution: {integrity: sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==} resolution: {integrity: sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.54.0': '@rollup/rollup-linux-loong64-gnu@4.54.0':
resolution: {integrity: sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==} resolution: {integrity: sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==}
cpu: [loong64] cpu: [loong64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-gnu@4.54.0': '@rollup/rollup-linux-ppc64-gnu@4.54.0':
resolution: {integrity: sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==} resolution: {integrity: sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-gnu@4.54.0': '@rollup/rollup-linux-riscv64-gnu@4.54.0':
resolution: {integrity: sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==} resolution: {integrity: sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.54.0': '@rollup/rollup-linux-riscv64-musl@4.54.0':
resolution: {integrity: sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==} resolution: {integrity: sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.54.0': '@rollup/rollup-linux-s390x-gnu@4.54.0':
resolution: {integrity: sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==} resolution: {integrity: sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.54.0': '@rollup/rollup-linux-x64-gnu@4.54.0':
resolution: {integrity: sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==} resolution: {integrity: sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.54.0': '@rollup/rollup-linux-x64-musl@4.54.0':
resolution: {integrity: sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==} resolution: {integrity: sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-openharmony-arm64@4.54.0': '@rollup/rollup-openharmony-arm64@4.54.0':
resolution: {integrity: sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==} resolution: {integrity: sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==}
@@ -1941,24 +1955,28 @@ packages:
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-arm64-musl@4.1.18': '@tailwindcss/oxide-linux-arm64-musl@4.1.18':
resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@tailwindcss/oxide-linux-x64-gnu@4.1.18': '@tailwindcss/oxide-linux-x64-gnu@4.1.18':
resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-x64-musl@4.1.18': '@tailwindcss/oxide-linux-x64-musl@4.1.18':
resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@tailwindcss/oxide-wasm32-wasi@4.1.18': '@tailwindcss/oxide-wasm32-wasi@4.1.18':
resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==}
@@ -3439,6 +3457,11 @@ packages:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'} engines: {node: '>= 14'}
husky@9.1.7:
resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==}
engines: {node: '>=18'}
hasBin: true
iconv-lite@0.6.3: iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -3686,24 +3709,28 @@ packages:
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
lightningcss-linux-arm64-musl@1.30.2: lightningcss-linux-arm64-musl@1.30.2:
resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
lightningcss-linux-x64-gnu@1.30.2: lightningcss-linux-x64-gnu@1.30.2:
resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
lightningcss-linux-x64-musl@1.30.2: lightningcss-linux-x64-musl@1.30.2:
resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
lightningcss-win32-arm64-msvc@1.30.2: lightningcss-win32-arm64-msvc@1.30.2:
resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
@@ -8329,6 +8356,8 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
husky@9.1.7: {}
iconv-lite@0.6.3: iconv-lite@0.6.3:
dependencies: dependencies:
safer-buffer: 2.1.2 safer-buffer: 2.1.2