@lsamien/yaovi 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/build-info.json +22 -3
  2. package/desktop/main.mjs +1 -1
  3. package/dist/assets/{AboutDialog-CnNkIRo8.js → AboutDialog-2Zq3b7ma.js} +2 -2
  4. package/dist/assets/{AboutDialog-CnNkIRo8.js.map → AboutDialog-2Zq3b7ma.js.map} +1 -1
  5. package/dist/assets/{BotAutomationsView-J9Y7Unoq.js → BotAutomationsView-C4EwHtYy.js} +2 -2
  6. package/dist/assets/{BotAutomationsView-J9Y7Unoq.js.map → BotAutomationsView-C4EwHtYy.js.map} +1 -1
  7. package/dist/assets/{BotControlSettingsPanel-CtXD24FZ.js → BotControlSettingsPanel-DJ71O3Kh.js} +2 -2
  8. package/dist/assets/{BotControlSettingsPanel-CtXD24FZ.js.map → BotControlSettingsPanel-DJ71O3Kh.js.map} +1 -1
  9. package/dist/assets/BotPluginsDialog-Cgqn4RlP.js +2 -0
  10. package/dist/assets/{BotPluginsDialog-qRw8dCbX.js.map → BotPluginsDialog-Cgqn4RlP.js.map} +1 -1
  11. package/dist/assets/{BotSettingsPanel-DbCMOfAW.js → BotSettingsPanel-C_1zAGWJ.js} +3 -3
  12. package/dist/assets/{BotSettingsPanel-DbCMOfAW.js.map → BotSettingsPanel-C_1zAGWJ.js.map} +1 -1
  13. package/dist/assets/{ComputerPanel-CI-jDmC3.js → ComputerPanel-CuVl9oX7.js} +2 -2
  14. package/dist/assets/{ComputerPanel-CI-jDmC3.js.map → ComputerPanel-CuVl9oX7.js.map} +1 -1
  15. package/dist/assets/{ComputerViewerView-B76D6Fln.js → ComputerViewerView-BoI_aqLr.js} +2 -2
  16. package/dist/assets/{ComputerViewerView-B76D6Fln.js.map → ComputerViewerView-BoI_aqLr.js.map} +1 -1
  17. package/dist/assets/ConversationsView-aPMgqa0O.css +1 -0
  18. package/dist/assets/{ConversationsView-DsuaLKva.js → ConversationsView-kztie5lF.js} +88 -88
  19. package/dist/assets/ConversationsView-kztie5lF.js.map +1 -0
  20. package/dist/assets/{LocalVmSettingsPanel-BfXFdMYF.js → LocalVmSettingsPanel-BZ1Jeqqk.js} +4 -4
  21. package/dist/assets/{LocalVmSettingsPanel-BfXFdMYF.js.map → LocalVmSettingsPanel-BZ1Jeqqk.js.map} +1 -1
  22. package/dist/assets/{ModelNetworkChoice-DB6cLOBw.js → ModelNetworkChoice-BQ8m2N3V.js} +2 -2
  23. package/dist/assets/{ModelNetworkChoice-DB6cLOBw.js.map → ModelNetworkChoice-BQ8m2N3V.js.map} +1 -1
  24. package/dist/assets/{StandaloneDialog-BWUp2Wxo.js → StandaloneDialog-hF3z7bfi.js} +2 -2
  25. package/dist/assets/{StandaloneDialog-BWUp2Wxo.js.map → StandaloneDialog-hF3z7bfi.js.map} +1 -1
  26. package/dist/assets/{UpdateCheckDialog-CpZSPlUI.js → UpdateCheckDialog-Bz8JhRh4.js} +2 -2
  27. package/dist/assets/{UpdateCheckDialog-CpZSPlUI.js.map → UpdateCheckDialog-Bz8JhRh4.js.map} +1 -1
  28. package/dist/assets/{UsageView-BhiRCw7p.js → UsageView-ZsSbQzIY.js} +3 -3
  29. package/dist/assets/{UsageView-BhiRCw7p.js.map → UsageView-ZsSbQzIY.js.map} +1 -1
  30. package/dist/assets/WorkspaceKnowledgePanel-E549Ct1E.js +3 -0
  31. package/dist/assets/{WorkspaceKnowledgePanel-D34YxglF.js.map → WorkspaceKnowledgePanel-E549Ct1E.js.map} +1 -1
  32. package/dist/assets/WorkspaceRoutinesPanel-NFWzy1R-.js +2 -0
  33. package/dist/assets/{WorkspaceRoutinesPanel-Bn-W52Im.js.map → WorkspaceRoutinesPanel-NFWzy1R-.js.map} +1 -1
  34. package/dist/assets/{WorkstreamsView-CH6qDPvj.js → WorkstreamsView-CxZ8YUvP.js} +2 -2
  35. package/dist/assets/{WorkstreamsView-CH6qDPvj.js.map → WorkstreamsView-CxZ8YUvP.js.map} +1 -1
  36. package/dist/assets/index-C-5Gq82f.css +1 -0
  37. package/dist/assets/{index-DjhQ-RMG.js → index-P_kZCZ2-.js} +18 -18
  38. package/dist/assets/index-P_kZCZ2-.js.map +1 -0
  39. package/dist/assets/{pdf-wqiF06Qz.js → pdf-CNS_Rszb.js} +2 -2
  40. package/dist/assets/{pdf-wqiF06Qz.js.map → pdf-CNS_Rszb.js.map} +1 -1
  41. package/dist/index.html +2 -2
  42. package/dist-server/server/remoteAccess/dispatcher.js +5 -1
  43. package/dist-server/server/workspaceCollaboration.js +9 -12
  44. package/dist-server/server/workspaceStore.js +3 -1
  45. package/dist-server/shared/peerExchange.js +26 -0
  46. package/node_modules/@yaovi/remote-core/index.mjs +13 -10
  47. package/node_modules/@yaovi/remote-core/package.json +2 -2
  48. package/node_modules/@yaovi/remote-core/types/remote-core/src/control/recovery.d.ts +1 -1
  49. package/node_modules/@yaovi/remote-core/types/remote-core/src/control/remote.d.ts +2 -0
  50. package/node_modules/@yaovi/remote-core/types/remote-core/src/index.d.ts +1 -0
  51. package/node_modules/@yaovi/remote-core/types/remote-core/src/transport/lan.d.ts +10 -4
  52. package/node_modules/@yaovi/remote-core/types/remote-core/src/transport/packet.d.ts +2 -2
  53. package/package.json +2 -2
  54. package/release.json +3 -3
  55. package/vendor/yaovi-remote-core-0.2.0-remote.18.tgz +0 -0
  56. package/dist/assets/BotPluginsDialog-qRw8dCbX.js +0 -2
  57. package/dist/assets/ConversationsView-DoA0eNiz.css +0 -1
  58. package/dist/assets/ConversationsView-DsuaLKva.js.map +0 -1
  59. package/dist/assets/WorkspaceKnowledgePanel-D34YxglF.js +0 -3
  60. package/dist/assets/WorkspaceRoutinesPanel-Bn-W52Im.js +0 -2
  61. package/dist/assets/index-DjhQ-RMG.js.map +0 -1
  62. package/dist/assets/index-DrIgDgnN.css +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ModelNetworkChoice-DB6cLOBw.js","names":[],"sources":["../../src/client/api/modelNetwork.ts","../../src/client/components/app/ModelNetworkChoice.vue","../../src/client/components/app/ModelNetworkChoice.vue"],"sourcesContent":["import { apiRequest } from './client'\nimport type { ModelNetworkView, NetworkPolicy, NetworkTest, ModelProxy } from '@shared/modelNetwork'\nexport type ProxyDraft = Omit<ModelProxy, 'id' | 'hasPassword'> & { id?: string; password?: string; clearPassword?: boolean }\nconst base = '/api/app/admin/hermes/network'\nconst q = (bot?: string) => bot ? `?bot=${encodeURIComponent(bot)}` : ''\nexport const modelNetworkApi = {\n get: (bot?: string) => apiRequest<ModelNetworkView>(base + q(bot)),\n save: (revision: number, policy: NetworkPolicy, provider?: string, bot?: string) => apiRequest<ModelNetworkView>(`${base}/${provider ? 'connections/' + encodeURIComponent(provider) : 'default'}${q(bot)}`, { method: 'PUT', body: { revision, policy } }),\n proxy: (revision: number, proxy: ProxyDraft) => apiRequest<ModelNetworkView>(base + '/proxies', { method: 'POST', body: { revision, proxy } }),\n remove: (revision: number, id: string) => apiRequest<ModelNetworkView>(base + '/proxies/' + encodeURIComponent(id), { method: 'DELETE', body: { revision } }),\n test: (input: { provider?: string; policy?: NetworkPolicy; draft?: ProxyDraft }, bot?: string) => apiRequest<NetworkTest>(base + '/test' + q(bot), { method: 'POST', body: input }),\n}\nexport function networkLabel(view: ModelNetworkView, policy: NetworkPolicy): string {\n if (policy.mode === 'inherit') return '跟随默认 · ' + networkLabel(view, view.default)\n if (policy.mode === 'environment') return view.environmentDetected ? '服务器环境代理' : '服务器环境(未检测到代理)'\n if (policy.mode === 'direct') return '直接连接'\n return view.proxies.find(p => p.id === policy.proxyId)?.name ?? '代理已不可用'\n}\n","<script setup lang=\"ts\">\nimport { computed, ref, watch, useId } from 'vue'\nimport { modelNetworkApi, networkLabel } from '@/api/modelNetwork'\nimport type { ModelNetworkView, NetworkPolicy, NetworkTest } from '@shared/modelNetwork'\nconst props = defineProps<{ view: ModelNetworkView; modelValue: NetworkPolicy; provider?: string; bot?: string; disabled?: boolean; compact?: boolean; global?: boolean; summaryOnly?: boolean; hideTest?:boolean }>()\nconst emit = defineEmits<{ 'update:modelValue': [value: NetworkPolicy]; manage: []; busy: [value: boolean] }>()\nconst radioName=useId()\nconst testing = ref(false), result = ref<NetworkTest>(), error = ref('')\nconst summary = computed(() => networkLabel(props.view, props.modelValue))\nconst selected = computed(() => props.view.proxies.find(p => p.id === props.modelValue.proxyId))\nconst modes = computed(() => props.global ? [{ id: 'environment', name: '沿用服务器环境', hint: props.view.environmentDetected ? '已检测到环境代理,保留当前网络行为' : '未检测到环境代理' }, { id: 'direct', name: '直接连接', hint: '不使用代理' }, { id: 'proxy', name: '指定代理', hint: '通过已保存的代理连接' }] : [{ id: 'inherit', name: '跟随服务器默认', hint: networkLabel(props.view, props.view.default) }, { id: 'direct', name: '直接连接', hint: '此连接不使用代理' }, { id: 'proxy', name: '指定代理', hint: '仅此连接使用所选代理' }])\nfunction change(mode: string) { emit('update:modelValue', mode === 'proxy' ? { mode, proxyId: props.modelValue.proxyId ?? props.view.proxies[0]?.id } : { mode } as NetworkPolicy) }\nwatch(() => JSON.stringify([props.modelValue, props.view.revision]), () => { result.value = undefined; error.value = '' })\nasync function test() {\n if (testing.value) return\n testing.value = true; emit('busy', true); error.value = ''; result.value = undefined\n try { result.value = await modelNetworkApi.test({ provider: props.provider, policy: props.modelValue.mode === 'environment' ? undefined : props.modelValue }, props.bot) }\n catch (cause) { error.value = cause instanceof Error ? cause.message : '测试失败,请重试。' }\n finally { testing.value = false; emit('busy', false) }\n}\ndefineExpose({ summary, test })\n</script>\n<template>\n <div class=\"network-choice\" :class=\"{'global-choice':global}\">\n <label v-if=\"compact && !summaryOnly\" class=\"compact-choice\">{{ global?'默认连接方式':'连接网络' }}<select :value=\"modelValue.mode\" :disabled=\"disabled || testing\" @change=\"change(($event.target as HTMLSelectElement).value)\"><option v-for=\"mode in modes\" :key=\"mode.id\" :value=\"mode.id\">{{ mode.name }}</option></select></label>\n <fieldset v-else-if=\"!summaryOnly\" :disabled=\"disabled || testing\"><legend>{{ global ? '默认连接方式' : '连接方式' }}</legend><label v-for=\"mode in modes\" :key=\"mode.id\" class=\"network-option\" :class=\"{ selected: modelValue.mode === mode.id }\"><input type=\"radio\" :name=\"radioName\" :checked=\"modelValue.mode === mode.id\" :value=\"mode.id\" @change=\"change(mode.id)\" /><span><strong>{{ mode.name }}</strong><small>{{ mode.hint }}</small></span></label></fieldset>\n <div v-if=\"modelValue.mode === 'proxy' && !summaryOnly\" class=\"proxy-select\"><label><span v-if=\"compact || global\">使用代理</span><select aria-label=\"使用代理\" :value=\"modelValue.proxyId ?? ''\" :disabled=\"disabled || testing\" @change=\"emit('update:modelValue', {mode:'proxy', proxyId:($event.target as HTMLSelectElement).value})\"><option value=\"\" disabled>请选择代理</option><option v-for=\"p in view.proxies\" :key=\"p.id\" :value=\"p.id\">{{ p.name }}</option></select></label><small v-if=\"selected\">{{ selected.protocol.toUpperCase() }} · {{ selected.host }}:{{ selected.port }}</small><p v-if=\"!view.proxies.length\">尚未添加代理。请先在「环境与权限 → 模型网络」添加。</p></div>\n <p v-if=\"compact && !global && !summaryOnly\" class=\"summary\">{{ summary }}</p>\n <div v-if=\"!global && !hideTest\" class=\"network-diagnostics\"><div v-if=\"result\" class=\"test-result\" :class=\"{failed:result.proxy==='failed'||result.target==='failed'}\" role=\"status\"><strong>代理:{{ {passed:'端口可达',failed:'连接失败',skipped:'未检测'}[result.proxy] }}<template v-if=\"provider\"> · 目标服务:{{ {passed:'可达',failed:'不可达',skipped:'尚未检测'}[result.target] }}</template></strong><small>{{ result.message }}</small></div><div v-else><strong>连接测试</strong><small>由运行 Yaovi 的服务器发起</small></div><button type=\"button\" :disabled=\"disabled || testing || (modelValue.mode === 'proxy' && !selected)\" @click=\"test\">{{ testing ? '正在测试…' : '测试当前选择' }}</button></div>\n <p v-if=\"error\" role=\"alert\">{{ error }}</p>\n </div>\n</template>\n<style scoped>\n.network-choice:has(fieldset) .proxy-select{margin-left:36px;max-width:530px}\n.global-choice .compact-choice,.global-choice .proxy-select>label{display:grid;grid-template-columns:minmax(0,1fr) minmax(200px,460px);align-items:center;gap:24px}.global-choice .proxy-select>small{margin-left:auto}@media(max-width:600px){.global-choice .compact-choice,.global-choice .proxy-select>label{grid-template-columns:1fr;gap:8px}}\n.network-choice{display:grid;gap:8px;min-width:0;font-size:14px}.network-choice fieldset{border:0;padding:0;margin:0;display:grid;gap:0;min-width:0}.network-choice legend{font-weight:600;margin-bottom:12px}.network-option{display:flex;align-items:center;gap:12px;border:0;border-bottom:1px solid var(--line);border-radius:0;padding:8px 0;cursor:pointer}.network-option.selected{background:transparent}.network-option input{width:16px;height:16px;accent-color:var(--accent)}.network-option span,.network-diagnostics>div{display:grid;gap:4px}.network-option strong{font-weight:550}.network-choice small,.summary{color:var(--text-secondary);line-height:1.6}.network-choice p{margin:0}.proxy-select,.proxy-select label,.compact-choice{display:grid;gap:8px}.network-choice select,.network-choice button{font:inherit;min-height:44px;border:1px solid var(--line);border-radius:8px;padding:10px 12px;background:var(--surface);color:var(--text-primary);max-width:100%;box-sizing:border-box}.network-choice button{cursor:pointer}.network-diagnostics{display:flex;align-items:center;justify-content:space-between;gap:12px;border-top:1px solid var(--line);padding-top:16px}.test-result{display:grid;gap:5px}.test-result strong{font-size:13px;color:var(--success)}.test-result.failed strong{color:var(--danger)}.network-choice [role=alert]{color:var(--danger)}.network-choice :focus-visible{outline:2px solid var(--accent);outline-offset:3px}.network-choice :disabled{opacity:.55}@media(max-width:420px){.network-diagnostics{align-items:flex-start;flex-direction:column}}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, watch, useId } from 'vue'\nimport { modelNetworkApi, networkLabel } from '@/api/modelNetwork'\nimport type { ModelNetworkView, NetworkPolicy, NetworkTest } from '@shared/modelNetwork'\nconst props = defineProps<{ view: ModelNetworkView; modelValue: NetworkPolicy; provider?: string; bot?: string; disabled?: boolean; compact?: boolean; global?: boolean; summaryOnly?: boolean; hideTest?:boolean }>()\nconst emit = defineEmits<{ 'update:modelValue': [value: NetworkPolicy]; manage: []; busy: [value: boolean] }>()\nconst radioName=useId()\nconst testing = ref(false), result = ref<NetworkTest>(), error = ref('')\nconst summary = computed(() => networkLabel(props.view, props.modelValue))\nconst selected = computed(() => props.view.proxies.find(p => p.id === props.modelValue.proxyId))\nconst modes = computed(() => props.global ? [{ id: 'environment', name: '沿用服务器环境', hint: props.view.environmentDetected ? '已检测到环境代理,保留当前网络行为' : '未检测到环境代理' }, { id: 'direct', name: '直接连接', hint: '不使用代理' }, { id: 'proxy', name: '指定代理', hint: '通过已保存的代理连接' }] : [{ id: 'inherit', name: '跟随服务器默认', hint: networkLabel(props.view, props.view.default) }, { id: 'direct', name: '直接连接', hint: '此连接不使用代理' }, { id: 'proxy', name: '指定代理', hint: '仅此连接使用所选代理' }])\nfunction change(mode: string) { emit('update:modelValue', mode === 'proxy' ? { mode, proxyId: props.modelValue.proxyId ?? props.view.proxies[0]?.id } : { mode } as NetworkPolicy) }\nwatch(() => JSON.stringify([props.modelValue, props.view.revision]), () => { result.value = undefined; error.value = '' })\nasync function test() {\n if (testing.value) return\n testing.value = true; emit('busy', true); error.value = ''; result.value = undefined\n try { result.value = await modelNetworkApi.test({ provider: props.provider, policy: props.modelValue.mode === 'environment' ? undefined : props.modelValue }, props.bot) }\n catch (cause) { error.value = cause instanceof Error ? cause.message : '测试失败,请重试。' }\n finally { testing.value = false; emit('busy', false) }\n}\ndefineExpose({ summary, test })\n</script>\n<template>\n <div class=\"network-choice\" :class=\"{'global-choice':global}\">\n <label v-if=\"compact && !summaryOnly\" class=\"compact-choice\">{{ global?'默认连接方式':'连接网络' }}<select :value=\"modelValue.mode\" :disabled=\"disabled || testing\" @change=\"change(($event.target as HTMLSelectElement).value)\"><option v-for=\"mode in modes\" :key=\"mode.id\" :value=\"mode.id\">{{ mode.name }}</option></select></label>\n <fieldset v-else-if=\"!summaryOnly\" :disabled=\"disabled || testing\"><legend>{{ global ? '默认连接方式' : '连接方式' }}</legend><label v-for=\"mode in modes\" :key=\"mode.id\" class=\"network-option\" :class=\"{ selected: modelValue.mode === mode.id }\"><input type=\"radio\" :name=\"radioName\" :checked=\"modelValue.mode === mode.id\" :value=\"mode.id\" @change=\"change(mode.id)\" /><span><strong>{{ mode.name }}</strong><small>{{ mode.hint }}</small></span></label></fieldset>\n <div v-if=\"modelValue.mode === 'proxy' && !summaryOnly\" class=\"proxy-select\"><label><span v-if=\"compact || global\">使用代理</span><select aria-label=\"使用代理\" :value=\"modelValue.proxyId ?? ''\" :disabled=\"disabled || testing\" @change=\"emit('update:modelValue', {mode:'proxy', proxyId:($event.target as HTMLSelectElement).value})\"><option value=\"\" disabled>请选择代理</option><option v-for=\"p in view.proxies\" :key=\"p.id\" :value=\"p.id\">{{ p.name }}</option></select></label><small v-if=\"selected\">{{ selected.protocol.toUpperCase() }} · {{ selected.host }}:{{ selected.port }}</small><p v-if=\"!view.proxies.length\">尚未添加代理。请先在「环境与权限 → 模型网络」添加。</p></div>\n <p v-if=\"compact && !global && !summaryOnly\" class=\"summary\">{{ summary }}</p>\n <div v-if=\"!global && !hideTest\" class=\"network-diagnostics\"><div v-if=\"result\" class=\"test-result\" :class=\"{failed:result.proxy==='failed'||result.target==='failed'}\" role=\"status\"><strong>代理:{{ {passed:'端口可达',failed:'连接失败',skipped:'未检测'}[result.proxy] }}<template v-if=\"provider\"> · 目标服务:{{ {passed:'可达',failed:'不可达',skipped:'尚未检测'}[result.target] }}</template></strong><small>{{ result.message }}</small></div><div v-else><strong>连接测试</strong><small>由运行 Yaovi 的服务器发起</small></div><button type=\"button\" :disabled=\"disabled || testing || (modelValue.mode === 'proxy' && !selected)\" @click=\"test\">{{ testing ? '正在测试…' : '测试当前选择' }}</button></div>\n <p v-if=\"error\" role=\"alert\">{{ error }}</p>\n </div>\n</template>\n<style scoped>\n.network-choice:has(fieldset) .proxy-select{margin-left:36px;max-width:530px}\n.global-choice .compact-choice,.global-choice .proxy-select>label{display:grid;grid-template-columns:minmax(0,1fr) minmax(200px,460px);align-items:center;gap:24px}.global-choice .proxy-select>small{margin-left:auto}@media(max-width:600px){.global-choice .compact-choice,.global-choice .proxy-select>label{grid-template-columns:1fr;gap:8px}}\n.network-choice{display:grid;gap:8px;min-width:0;font-size:14px}.network-choice fieldset{border:0;padding:0;margin:0;display:grid;gap:0;min-width:0}.network-choice legend{font-weight:600;margin-bottom:12px}.network-option{display:flex;align-items:center;gap:12px;border:0;border-bottom:1px solid var(--line);border-radius:0;padding:8px 0;cursor:pointer}.network-option.selected{background:transparent}.network-option input{width:16px;height:16px;accent-color:var(--accent)}.network-option span,.network-diagnostics>div{display:grid;gap:4px}.network-option strong{font-weight:550}.network-choice small,.summary{color:var(--text-secondary);line-height:1.6}.network-choice p{margin:0}.proxy-select,.proxy-select label,.compact-choice{display:grid;gap:8px}.network-choice select,.network-choice button{font:inherit;min-height:44px;border:1px solid var(--line);border-radius:8px;padding:10px 12px;background:var(--surface);color:var(--text-primary);max-width:100%;box-sizing:border-box}.network-choice button{cursor:pointer}.network-diagnostics{display:flex;align-items:center;justify-content:space-between;gap:12px;border-top:1px solid var(--line);padding-top:16px}.test-result{display:grid;gap:5px}.test-result strong{font-size:13px;color:var(--success)}.test-result.failed strong{color:var(--danger)}.network-choice [role=alert]{color:var(--danger)}.network-choice :focus-visible{outline:2px solid var(--accent);outline-offset:3px}.network-choice :disabled{opacity:.55}@media(max-width:420px){.network-diagnostics{align-items:flex-start;flex-direction:column}}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"ModelNetworkChoice-BQ8m2N3V.js","names":[],"sources":["../../src/client/api/modelNetwork.ts","../../src/client/components/app/ModelNetworkChoice.vue","../../src/client/components/app/ModelNetworkChoice.vue"],"sourcesContent":["import { apiRequest } from './client'\nimport type { ModelNetworkView, NetworkPolicy, NetworkTest, ModelProxy } from '@shared/modelNetwork'\nexport type ProxyDraft = Omit<ModelProxy, 'id' | 'hasPassword'> & { id?: string; password?: string; clearPassword?: boolean }\nconst base = '/api/app/admin/hermes/network'\nconst q = (bot?: string) => bot ? `?bot=${encodeURIComponent(bot)}` : ''\nexport const modelNetworkApi = {\n get: (bot?: string) => apiRequest<ModelNetworkView>(base + q(bot)),\n save: (revision: number, policy: NetworkPolicy, provider?: string, bot?: string) => apiRequest<ModelNetworkView>(`${base}/${provider ? 'connections/' + encodeURIComponent(provider) : 'default'}${q(bot)}`, { method: 'PUT', body: { revision, policy } }),\n proxy: (revision: number, proxy: ProxyDraft) => apiRequest<ModelNetworkView>(base + '/proxies', { method: 'POST', body: { revision, proxy } }),\n remove: (revision: number, id: string) => apiRequest<ModelNetworkView>(base + '/proxies/' + encodeURIComponent(id), { method: 'DELETE', body: { revision } }),\n test: (input: { provider?: string; policy?: NetworkPolicy; draft?: ProxyDraft }, bot?: string) => apiRequest<NetworkTest>(base + '/test' + q(bot), { method: 'POST', body: input }),\n}\nexport function networkLabel(view: ModelNetworkView, policy: NetworkPolicy): string {\n if (policy.mode === 'inherit') return '跟随默认 · ' + networkLabel(view, view.default)\n if (policy.mode === 'environment') return view.environmentDetected ? '服务器环境代理' : '服务器环境(未检测到代理)'\n if (policy.mode === 'direct') return '直接连接'\n return view.proxies.find(p => p.id === policy.proxyId)?.name ?? '代理已不可用'\n}\n","<script setup lang=\"ts\">\nimport { computed, ref, watch, useId } from 'vue'\nimport { modelNetworkApi, networkLabel } from '@/api/modelNetwork'\nimport type { ModelNetworkView, NetworkPolicy, NetworkTest } from '@shared/modelNetwork'\nconst props = defineProps<{ view: ModelNetworkView; modelValue: NetworkPolicy; provider?: string; bot?: string; disabled?: boolean; compact?: boolean; global?: boolean; summaryOnly?: boolean; hideTest?:boolean }>()\nconst emit = defineEmits<{ 'update:modelValue': [value: NetworkPolicy]; manage: []; busy: [value: boolean] }>()\nconst radioName=useId()\nconst testing = ref(false), result = ref<NetworkTest>(), error = ref('')\nconst summary = computed(() => networkLabel(props.view, props.modelValue))\nconst selected = computed(() => props.view.proxies.find(p => p.id === props.modelValue.proxyId))\nconst modes = computed(() => props.global ? [{ id: 'environment', name: '沿用服务器环境', hint: props.view.environmentDetected ? '已检测到环境代理,保留当前网络行为' : '未检测到环境代理' }, { id: 'direct', name: '直接连接', hint: '不使用代理' }, { id: 'proxy', name: '指定代理', hint: '通过已保存的代理连接' }] : [{ id: 'inherit', name: '跟随服务器默认', hint: networkLabel(props.view, props.view.default) }, { id: 'direct', name: '直接连接', hint: '此连接不使用代理' }, { id: 'proxy', name: '指定代理', hint: '仅此连接使用所选代理' }])\nfunction change(mode: string) { emit('update:modelValue', mode === 'proxy' ? { mode, proxyId: props.modelValue.proxyId ?? props.view.proxies[0]?.id } : { mode } as NetworkPolicy) }\nwatch(() => JSON.stringify([props.modelValue, props.view.revision]), () => { result.value = undefined; error.value = '' })\nasync function test() {\n if (testing.value) return\n testing.value = true; emit('busy', true); error.value = ''; result.value = undefined\n try { result.value = await modelNetworkApi.test({ provider: props.provider, policy: props.modelValue.mode === 'environment' ? undefined : props.modelValue }, props.bot) }\n catch (cause) { error.value = cause instanceof Error ? cause.message : '测试失败,请重试。' }\n finally { testing.value = false; emit('busy', false) }\n}\ndefineExpose({ summary, test })\n</script>\n<template>\n <div class=\"network-choice\" :class=\"{'global-choice':global}\">\n <label v-if=\"compact && !summaryOnly\" class=\"compact-choice\">{{ global?'默认连接方式':'连接网络' }}<select :value=\"modelValue.mode\" :disabled=\"disabled || testing\" @change=\"change(($event.target as HTMLSelectElement).value)\"><option v-for=\"mode in modes\" :key=\"mode.id\" :value=\"mode.id\">{{ mode.name }}</option></select></label>\n <fieldset v-else-if=\"!summaryOnly\" :disabled=\"disabled || testing\"><legend>{{ global ? '默认连接方式' : '连接方式' }}</legend><label v-for=\"mode in modes\" :key=\"mode.id\" class=\"network-option\" :class=\"{ selected: modelValue.mode === mode.id }\"><input type=\"radio\" :name=\"radioName\" :checked=\"modelValue.mode === mode.id\" :value=\"mode.id\" @change=\"change(mode.id)\" /><span><strong>{{ mode.name }}</strong><small>{{ mode.hint }}</small></span></label></fieldset>\n <div v-if=\"modelValue.mode === 'proxy' && !summaryOnly\" class=\"proxy-select\"><label><span v-if=\"compact || global\">使用代理</span><select aria-label=\"使用代理\" :value=\"modelValue.proxyId ?? ''\" :disabled=\"disabled || testing\" @change=\"emit('update:modelValue', {mode:'proxy', proxyId:($event.target as HTMLSelectElement).value})\"><option value=\"\" disabled>请选择代理</option><option v-for=\"p in view.proxies\" :key=\"p.id\" :value=\"p.id\">{{ p.name }}</option></select></label><small v-if=\"selected\">{{ selected.protocol.toUpperCase() }} · {{ selected.host }}:{{ selected.port }}</small><p v-if=\"!view.proxies.length\">尚未添加代理。请先在「环境与权限 → 模型网络」添加。</p></div>\n <p v-if=\"compact && !global && !summaryOnly\" class=\"summary\">{{ summary }}</p>\n <div v-if=\"!global && !hideTest\" class=\"network-diagnostics\"><div v-if=\"result\" class=\"test-result\" :class=\"{failed:result.proxy==='failed'||result.target==='failed'}\" role=\"status\"><strong>代理:{{ {passed:'端口可达',failed:'连接失败',skipped:'未检测'}[result.proxy] }}<template v-if=\"provider\"> · 目标服务:{{ {passed:'可达',failed:'不可达',skipped:'尚未检测'}[result.target] }}</template></strong><small>{{ result.message }}</small></div><div v-else><strong>连接测试</strong><small>由运行 Yaovi 的服务器发起</small></div><button type=\"button\" :disabled=\"disabled || testing || (modelValue.mode === 'proxy' && !selected)\" @click=\"test\">{{ testing ? '正在测试…' : '测试当前选择' }}</button></div>\n <p v-if=\"error\" role=\"alert\">{{ error }}</p>\n </div>\n</template>\n<style scoped>\n.network-choice:has(fieldset) .proxy-select{margin-left:36px;max-width:530px}\n.global-choice .compact-choice,.global-choice .proxy-select>label{display:grid;grid-template-columns:minmax(0,1fr) minmax(200px,460px);align-items:center;gap:24px}.global-choice .proxy-select>small{margin-left:auto}@media(max-width:600px){.global-choice .compact-choice,.global-choice .proxy-select>label{grid-template-columns:1fr;gap:8px}}\n.network-choice{display:grid;gap:8px;min-width:0;font-size:14px}.network-choice fieldset{border:0;padding:0;margin:0;display:grid;gap:0;min-width:0}.network-choice legend{font-weight:600;margin-bottom:12px}.network-option{display:flex;align-items:center;gap:12px;border:0;border-bottom:1px solid var(--line);border-radius:0;padding:8px 0;cursor:pointer}.network-option.selected{background:transparent}.network-option input{width:16px;height:16px;accent-color:var(--accent)}.network-option span,.network-diagnostics>div{display:grid;gap:4px}.network-option strong{font-weight:550}.network-choice small,.summary{color:var(--text-secondary);line-height:1.6}.network-choice p{margin:0}.proxy-select,.proxy-select label,.compact-choice{display:grid;gap:8px}.network-choice select,.network-choice button{font:inherit;min-height:44px;border:1px solid var(--line);border-radius:8px;padding:10px 12px;background:var(--surface);color:var(--text-primary);max-width:100%;box-sizing:border-box}.network-choice button{cursor:pointer}.network-diagnostics{display:flex;align-items:center;justify-content:space-between;gap:12px;border-top:1px solid var(--line);padding-top:16px}.test-result{display:grid;gap:5px}.test-result strong{font-size:13px;color:var(--success)}.test-result.failed strong{color:var(--danger)}.network-choice [role=alert]{color:var(--danger)}.network-choice :focus-visible{outline:2px solid var(--accent);outline-offset:3px}.network-choice :disabled{opacity:.55}@media(max-width:420px){.network-diagnostics{align-items:flex-start;flex-direction:column}}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, watch, useId } from 'vue'\nimport { modelNetworkApi, networkLabel } from '@/api/modelNetwork'\nimport type { ModelNetworkView, NetworkPolicy, NetworkTest } from '@shared/modelNetwork'\nconst props = defineProps<{ view: ModelNetworkView; modelValue: NetworkPolicy; provider?: string; bot?: string; disabled?: boolean; compact?: boolean; global?: boolean; summaryOnly?: boolean; hideTest?:boolean }>()\nconst emit = defineEmits<{ 'update:modelValue': [value: NetworkPolicy]; manage: []; busy: [value: boolean] }>()\nconst radioName=useId()\nconst testing = ref(false), result = ref<NetworkTest>(), error = ref('')\nconst summary = computed(() => networkLabel(props.view, props.modelValue))\nconst selected = computed(() => props.view.proxies.find(p => p.id === props.modelValue.proxyId))\nconst modes = computed(() => props.global ? [{ id: 'environment', name: '沿用服务器环境', hint: props.view.environmentDetected ? '已检测到环境代理,保留当前网络行为' : '未检测到环境代理' }, { id: 'direct', name: '直接连接', hint: '不使用代理' }, { id: 'proxy', name: '指定代理', hint: '通过已保存的代理连接' }] : [{ id: 'inherit', name: '跟随服务器默认', hint: networkLabel(props.view, props.view.default) }, { id: 'direct', name: '直接连接', hint: '此连接不使用代理' }, { id: 'proxy', name: '指定代理', hint: '仅此连接使用所选代理' }])\nfunction change(mode: string) { emit('update:modelValue', mode === 'proxy' ? { mode, proxyId: props.modelValue.proxyId ?? props.view.proxies[0]?.id } : { mode } as NetworkPolicy) }\nwatch(() => JSON.stringify([props.modelValue, props.view.revision]), () => { result.value = undefined; error.value = '' })\nasync function test() {\n if (testing.value) return\n testing.value = true; emit('busy', true); error.value = ''; result.value = undefined\n try { result.value = await modelNetworkApi.test({ provider: props.provider, policy: props.modelValue.mode === 'environment' ? undefined : props.modelValue }, props.bot) }\n catch (cause) { error.value = cause instanceof Error ? cause.message : '测试失败,请重试。' }\n finally { testing.value = false; emit('busy', false) }\n}\ndefineExpose({ summary, test })\n</script>\n<template>\n <div class=\"network-choice\" :class=\"{'global-choice':global}\">\n <label v-if=\"compact && !summaryOnly\" class=\"compact-choice\">{{ global?'默认连接方式':'连接网络' }}<select :value=\"modelValue.mode\" :disabled=\"disabled || testing\" @change=\"change(($event.target as HTMLSelectElement).value)\"><option v-for=\"mode in modes\" :key=\"mode.id\" :value=\"mode.id\">{{ mode.name }}</option></select></label>\n <fieldset v-else-if=\"!summaryOnly\" :disabled=\"disabled || testing\"><legend>{{ global ? '默认连接方式' : '连接方式' }}</legend><label v-for=\"mode in modes\" :key=\"mode.id\" class=\"network-option\" :class=\"{ selected: modelValue.mode === mode.id }\"><input type=\"radio\" :name=\"radioName\" :checked=\"modelValue.mode === mode.id\" :value=\"mode.id\" @change=\"change(mode.id)\" /><span><strong>{{ mode.name }}</strong><small>{{ mode.hint }}</small></span></label></fieldset>\n <div v-if=\"modelValue.mode === 'proxy' && !summaryOnly\" class=\"proxy-select\"><label><span v-if=\"compact || global\">使用代理</span><select aria-label=\"使用代理\" :value=\"modelValue.proxyId ?? ''\" :disabled=\"disabled || testing\" @change=\"emit('update:modelValue', {mode:'proxy', proxyId:($event.target as HTMLSelectElement).value})\"><option value=\"\" disabled>请选择代理</option><option v-for=\"p in view.proxies\" :key=\"p.id\" :value=\"p.id\">{{ p.name }}</option></select></label><small v-if=\"selected\">{{ selected.protocol.toUpperCase() }} · {{ selected.host }}:{{ selected.port }}</small><p v-if=\"!view.proxies.length\">尚未添加代理。请先在「环境与权限 → 模型网络」添加。</p></div>\n <p v-if=\"compact && !global && !summaryOnly\" class=\"summary\">{{ summary }}</p>\n <div v-if=\"!global && !hideTest\" class=\"network-diagnostics\"><div v-if=\"result\" class=\"test-result\" :class=\"{failed:result.proxy==='failed'||result.target==='failed'}\" role=\"status\"><strong>代理:{{ {passed:'端口可达',failed:'连接失败',skipped:'未检测'}[result.proxy] }}<template v-if=\"provider\"> · 目标服务:{{ {passed:'可达',failed:'不可达',skipped:'尚未检测'}[result.target] }}</template></strong><small>{{ result.message }}</small></div><div v-else><strong>连接测试</strong><small>由运行 Yaovi 的服务器发起</small></div><button type=\"button\" :disabled=\"disabled || testing || (modelValue.mode === 'proxy' && !selected)\" @click=\"test\">{{ testing ? '正在测试…' : '测试当前选择' }}</button></div>\n <p v-if=\"error\" role=\"alert\">{{ error }}</p>\n </div>\n</template>\n<style scoped>\n.network-choice:has(fieldset) .proxy-select{margin-left:36px;max-width:530px}\n.global-choice .compact-choice,.global-choice .proxy-select>label{display:grid;grid-template-columns:minmax(0,1fr) minmax(200px,460px);align-items:center;gap:24px}.global-choice .proxy-select>small{margin-left:auto}@media(max-width:600px){.global-choice .compact-choice,.global-choice .proxy-select>label{grid-template-columns:1fr;gap:8px}}\n.network-choice{display:grid;gap:8px;min-width:0;font-size:14px}.network-choice fieldset{border:0;padding:0;margin:0;display:grid;gap:0;min-width:0}.network-choice legend{font-weight:600;margin-bottom:12px}.network-option{display:flex;align-items:center;gap:12px;border:0;border-bottom:1px solid var(--line);border-radius:0;padding:8px 0;cursor:pointer}.network-option.selected{background:transparent}.network-option input{width:16px;height:16px;accent-color:var(--accent)}.network-option span,.network-diagnostics>div{display:grid;gap:4px}.network-option strong{font-weight:550}.network-choice small,.summary{color:var(--text-secondary);line-height:1.6}.network-choice p{margin:0}.proxy-select,.proxy-select label,.compact-choice{display:grid;gap:8px}.network-choice select,.network-choice button{font:inherit;min-height:44px;border:1px solid var(--line);border-radius:8px;padding:10px 12px;background:var(--surface);color:var(--text-primary);max-width:100%;box-sizing:border-box}.network-choice button{cursor:pointer}.network-diagnostics{display:flex;align-items:center;justify-content:space-between;gap:12px;border-top:1px solid var(--line);padding-top:16px}.test-result{display:grid;gap:5px}.test-result strong{font-size:13px;color:var(--success)}.test-result.failed strong{color:var(--danger)}.network-choice [role=alert]{color:var(--danger)}.network-choice :focus-visible{outline:2px solid var(--accent);outline-offset:3px}.network-choice :disabled{opacity:.55}@media(max-width:420px){.network-diagnostics{align-items:flex-start;flex-direction:column}}\n</style>\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{A as e,D as t,Et as n,F as r,M as i,Ot as a,R as o,b as s,d as c,et as l,f as u,o as d,t as f,v as p}from"./_plugin-vue_export-helper-CD0r-vVb.js";import{I as m,ot as h}from"./index-DjhQ-RMG.js";var g=[`aria-label`],_=[`aria-label`],v={class:`standalone-dialog__content`},y=f(s({__name:`StandaloneDialog`,props:{title:{},compact:{type:Boolean},wide:{type:Boolean},settings:{type:Boolean},beforeClose:{type:Function}},emits:[`close`],setup(s,{emit:f}){let y=s,b=f,x=l(),S=document.activeElement instanceof HTMLElement?document.activeElement:void 0;function C(){y.beforeClose?.()!==!1&&b(`close`)}function w(e){if(e.target!==x.value)return;let t=x.value.getBoundingClientRect();(e.clientX<t.left||e.clientX>t.right||e.clientY<t.top||e.clientY>t.bottom)&&C()}return i(async()=>{await t(),x.value?.showModal()}),e(()=>{x.value?.close(),t(()=>{S?.isConnected&&S.focus()})}),(e,t)=>(r(),u(d,{to:`body`},[c(`dialog`,{ref_key:`dialog`,ref:x,class:n([`standalone-dialog`,{"standalone-dialog--compact":s.compact,"standalone-dialog--wide":s.wide,"standalone-dialog--settings":s.settings}]),"aria-label":s.title,onCancel:h(C,[`prevent`]),onClick:w},[c(`header`,null,[c(`h2`,null,a(s.title),1),c(`button`,{type:`button`,"aria-label":`关闭${s.title}`,autofocus:``,onClick:C},[p(m,{name:`close`,size:20})],8,_)]),c(`div`,v,[o(e.$slots,`default`,{},void 0,!0)])],42,g)]))}}),[[`__scopeId`,`data-v-1ade5d30`]]);export{y as t};
2
- //# sourceMappingURL=StandaloneDialog-BWUp2Wxo.js.map
1
+ import{A as e,D as t,Et as n,F as r,M as i,Ot as a,R as o,b as s,d as c,et as l,f as u,o as d,t as f,v as p}from"./_plugin-vue_export-helper-CD0r-vVb.js";import{B as m,lt as h}from"./index-P_kZCZ2-.js";var g=[`aria-label`],_=[`aria-label`],v={class:`standalone-dialog__content`},y=f(s({__name:`StandaloneDialog`,props:{title:{},compact:{type:Boolean},wide:{type:Boolean},settings:{type:Boolean},beforeClose:{type:Function}},emits:[`close`],setup(s,{emit:f}){let y=s,b=f,x=l(),S=document.activeElement instanceof HTMLElement?document.activeElement:void 0;function C(){y.beforeClose?.()!==!1&&b(`close`)}function w(e){if(e.target!==x.value)return;let t=x.value.getBoundingClientRect();(e.clientX<t.left||e.clientX>t.right||e.clientY<t.top||e.clientY>t.bottom)&&C()}return i(async()=>{await t(),x.value?.showModal()}),e(()=>{x.value?.close(),t(()=>{S?.isConnected&&S.focus()})}),(e,t)=>(r(),u(d,{to:`body`},[c(`dialog`,{ref_key:`dialog`,ref:x,class:n([`standalone-dialog`,{"standalone-dialog--compact":s.compact,"standalone-dialog--wide":s.wide,"standalone-dialog--settings":s.settings}]),"aria-label":s.title,onCancel:h(C,[`prevent`]),onClick:w},[c(`header`,null,[c(`h2`,null,a(s.title),1),c(`button`,{type:`button`,"aria-label":`关闭${s.title}`,autofocus:``,onClick:C},[p(m,{name:`close`,size:20})],8,_)]),c(`div`,v,[o(e.$slots,`default`,{},void 0,!0)])],42,g)]))}}),[[`__scopeId`,`data-v-1ade5d30`]]);export{y as t};
2
+ //# sourceMappingURL=StandaloneDialog-hF3z7bfi.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"StandaloneDialog-BWUp2Wxo.js","names":[],"sources":["../../src/client/components/common/StandaloneDialog.vue","../../src/client/components/common/StandaloneDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'\nimport AppIcon from './AppIcon.vue'\nconst props = defineProps<{ title: string; compact?: boolean; wide?: boolean; settings?: boolean; beforeClose?: () => boolean }>()\nconst emit = defineEmits<{ close: [] }>()\nconst dialog = ref<HTMLDialogElement>()\nconst returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : undefined\nfunction close() { if (props.beforeClose?.() !== false) emit('close') }\nfunction backdrop(event: MouseEvent) {\n if (event.target !== dialog.value) return\n const rect = dialog.value.getBoundingClientRect()\n if (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom) close()\n}\nonMounted(async () => { await nextTick(); dialog.value?.showModal() })\nonBeforeUnmount(() => { dialog.value?.close(); void nextTick(() => { if (returnFocus?.isConnected) returnFocus.focus() }) })\n</script>\n<template>\n <Teleport to=\"body\"><dialog ref=\"dialog\" class=\"standalone-dialog\" :class=\"{ 'standalone-dialog--compact': compact, 'standalone-dialog--wide': wide, 'standalone-dialog--settings': settings }\" :aria-label=\"title\" @cancel.prevent=\"close\" @click=\"backdrop\">\n <header><h2>{{ title }}</h2><button type=\"button\" :aria-label=\"`关闭${title}`\" autofocus @click=\"close\"><AppIcon name=\"close\" :size=\"20\" /></button></header>\n <div class=\"standalone-dialog__content\"><slot /></div>\n </dialog></Teleport>\n</template>\n<style scoped>\n.standalone-dialog--compact.standalone-dialog--wide{width:min(480px,calc(100vw - 32px))}\n.standalone-dialog{width:min(1040px,calc(100vw - 48px));height:min(780px,calc(100dvh - 48px));max-width:none;max-height:none;margin:auto;padding:0;border:1px solid var(--line);border-radius:22px;background:var(--surface);color:var(--text-primary);box-shadow:0 24px 100px #0005;overflow:hidden}.standalone-dialog[open]{display:flex;flex-direction:column}.standalone-dialog::backdrop{background:#0007;backdrop-filter:blur(3px)}header{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:20px 24px 16px;border-bottom:1px solid var(--line);flex-shrink:0}h2{font-size:21px;font-weight:650;margin:0;letter-spacing:-.02em}header button{display:grid;place-items:center;width:44px;height:44px;padding:0;flex-shrink:0;border:0;border-radius:10px;color:inherit;background:transparent;cursor:pointer}header button:hover{background:var(--surface-hover)}header button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.standalone-dialog__content{padding:24px;overflow:auto;min-height:0;flex:1}.standalone-dialog--compact{width:min(380px,calc(100vw - 32px));height:fit-content;max-height:calc(100dvh - 32px);border-radius:18px}.standalone-dialog--compact header{padding:10px 12px 10px 20px}.standalone-dialog--compact h2{font-size:15px}.standalone-dialog--compact .standalone-dialog__content{padding:20px 24px 28px}@media(max-width:600px){.standalone-dialog:not(.standalone-dialog--compact){width:100vw;height:100dvh;border:0;border-radius:0}header{padding:10px 12px 10px 20px}h2{font-size:18px}.standalone-dialog__content{padding:20px}}\n</style>\n<style scoped>\n.standalone-dialog--settings{width:min(900px,calc(100vw - 48px));height:min(650px,calc(100dvh - 64px));border-radius:14px;box-shadow:0 24px 72px #0003}.standalone-dialog--settings>header{height:60px;padding:8px 16px 8px 20px;box-sizing:border-box}.standalone-dialog--settings h2{font-size:18px;font-weight:650}.standalone-dialog--settings .standalone-dialog__content{padding:0;overflow:hidden}.standalone-dialog--settings::backdrop{background:var(--scrim);backdrop-filter:blur(3px)}@media(max-width:600px){.standalone-dialog--settings{width:100vw;height:100dvh;border-radius:0}.standalone-dialog--settings>header{padding-left:20px}}\n</style>\n","<script setup lang=\"ts\">\nimport { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'\nimport AppIcon from './AppIcon.vue'\nconst props = defineProps<{ title: string; compact?: boolean; wide?: boolean; settings?: boolean; beforeClose?: () => boolean }>()\nconst emit = defineEmits<{ close: [] }>()\nconst dialog = ref<HTMLDialogElement>()\nconst returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : undefined\nfunction close() { if (props.beforeClose?.() !== false) emit('close') }\nfunction backdrop(event: MouseEvent) {\n if (event.target !== dialog.value) return\n const rect = dialog.value.getBoundingClientRect()\n if (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom) close()\n}\nonMounted(async () => { await nextTick(); dialog.value?.showModal() })\nonBeforeUnmount(() => { dialog.value?.close(); void nextTick(() => { if (returnFocus?.isConnected) returnFocus.focus() }) })\n</script>\n<template>\n <Teleport to=\"body\"><dialog ref=\"dialog\" class=\"standalone-dialog\" :class=\"{ 'standalone-dialog--compact': compact, 'standalone-dialog--wide': wide, 'standalone-dialog--settings': settings }\" :aria-label=\"title\" @cancel.prevent=\"close\" @click=\"backdrop\">\n <header><h2>{{ title }}</h2><button type=\"button\" :aria-label=\"`关闭${title}`\" autofocus @click=\"close\"><AppIcon name=\"close\" :size=\"20\" /></button></header>\n <div class=\"standalone-dialog__content\"><slot /></div>\n </dialog></Teleport>\n</template>\n<style scoped>\n.standalone-dialog--compact.standalone-dialog--wide{width:min(480px,calc(100vw - 32px))}\n.standalone-dialog{width:min(1040px,calc(100vw - 48px));height:min(780px,calc(100dvh - 48px));max-width:none;max-height:none;margin:auto;padding:0;border:1px solid var(--line);border-radius:22px;background:var(--surface);color:var(--text-primary);box-shadow:0 24px 100px #0005;overflow:hidden}.standalone-dialog[open]{display:flex;flex-direction:column}.standalone-dialog::backdrop{background:#0007;backdrop-filter:blur(3px)}header{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:20px 24px 16px;border-bottom:1px solid var(--line);flex-shrink:0}h2{font-size:21px;font-weight:650;margin:0;letter-spacing:-.02em}header button{display:grid;place-items:center;width:44px;height:44px;padding:0;flex-shrink:0;border:0;border-radius:10px;color:inherit;background:transparent;cursor:pointer}header button:hover{background:var(--surface-hover)}header button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.standalone-dialog__content{padding:24px;overflow:auto;min-height:0;flex:1}.standalone-dialog--compact{width:min(380px,calc(100vw - 32px));height:fit-content;max-height:calc(100dvh - 32px);border-radius:18px}.standalone-dialog--compact header{padding:10px 12px 10px 20px}.standalone-dialog--compact h2{font-size:15px}.standalone-dialog--compact .standalone-dialog__content{padding:20px 24px 28px}@media(max-width:600px){.standalone-dialog:not(.standalone-dialog--compact){width:100vw;height:100dvh;border:0;border-radius:0}header{padding:10px 12px 10px 20px}h2{font-size:18px}.standalone-dialog__content{padding:20px}}\n</style>\n<style scoped>\n.standalone-dialog--settings{width:min(900px,calc(100vw - 48px));height:min(650px,calc(100dvh - 64px));border-radius:14px;box-shadow:0 24px 72px #0003}.standalone-dialog--settings>header{height:60px;padding:8px 16px 8px 20px;box-sizing:border-box}.standalone-dialog--settings h2{font-size:18px;font-weight:650}.standalone-dialog--settings .standalone-dialog__content{padding:0;overflow:hidden}.standalone-dialog--settings::backdrop{background:var(--scrim);backdrop-filter:blur(3px)}@media(max-width:600px){.standalone-dialog--settings{width:100vw;height:100dvh;border-radius:0}.standalone-dialog--settings>header{padding-left:20px}}\n</style>\n"],"mappings":"0cAGA,IAAM,EAAQ,EACR,EAAO,EACP,EAAS,EAAuB,EAChC,EAAc,SAAS,yBAAyB,YAAc,SAAS,cAAgB,IAAA,GAC7F,SAAS,GAAQ,CAAM,EAAM,cAAc,IAAM,IAAO,EAAK,OAAO,CAAE,CACtE,SAAS,EAAS,EAAmB,CACnC,GAAI,EAAM,SAAW,EAAO,MAAO,OACnC,IAAM,EAAO,EAAO,MAAM,sBAAsB,GAC5C,EAAM,QAAU,EAAK,MAAQ,EAAM,QAAU,EAAK,OAAS,EAAM,QAAU,EAAK,KAAO,EAAM,QAAU,EAAK,SAAQ,EAAM,CAChI,QACA,EAAU,SAAY,CAAE,MAAM,EAAS,EAAG,EAAO,OAAO,UAAU,CAAE,CAAC,EACrE,MAAsB,CAAE,EAAO,OAAO,MAAM,EAAG,MAAoB,CAAM,GAAa,aAAa,EAAY,MAAM,CAAE,CAAC,CAAE,CAAC,UAGzH,EAAA,EAAA,EAGoB,EAAA,CAHV,GAAG,MAAM,EAAA,CAAC,EAGX,SAAA,CAHuB,QAAA,SAAJ,IAAI,EAAS,MAAK,EAAA,CAAC,oBAAmB,CAAA,6BAAyC,EAAA,QAAO,0BAA6B,EAAA,KAAI,8BAAiC,EAAA,QAAQ,CAAA,CAAA,EAAK,aAAY,EAAA,MAAQ,SAAM,EAAU,EAAK,CAAA,SAAA,CAAA,EAAG,QAAO,CAClP,EAAA,CAAA,EAA2J,SAAA,KAAA,CAAnJ,EAAoB,KAAA,KAAA,EAAb,EAAA,KAAK,EAAA,CAAA,EAAQ,EAAsH,SAAA,CAA9G,KAAK,SAAU,aAAU,KAAO,EAAA,QAAS,UAAA,GAAW,QAAO,CAAO,EAAA,CAAA,EAAmC,EAAA,CAA1B,KAAK,QAAS,KAAM,EACnI,CAAA,CAAA,EAAA,EAAA,CAAA,CAAA,CAAA,EAAA,EAAsD,MAAtD,EAAsD,CAAd,EAAQ,EAAA,OAAA,UAAA,CAAA,EAAA,IAAA,GAAA,EAAA,CAAA,CAAA,CAAA,EAAA,GAAA,CAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"StandaloneDialog-hF3z7bfi.js","names":[],"sources":["../../src/client/components/common/StandaloneDialog.vue","../../src/client/components/common/StandaloneDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'\nimport AppIcon from './AppIcon.vue'\nconst props = defineProps<{ title: string; compact?: boolean; wide?: boolean; settings?: boolean; beforeClose?: () => boolean }>()\nconst emit = defineEmits<{ close: [] }>()\nconst dialog = ref<HTMLDialogElement>()\nconst returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : undefined\nfunction close() { if (props.beforeClose?.() !== false) emit('close') }\nfunction backdrop(event: MouseEvent) {\n if (event.target !== dialog.value) return\n const rect = dialog.value.getBoundingClientRect()\n if (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom) close()\n}\nonMounted(async () => { await nextTick(); dialog.value?.showModal() })\nonBeforeUnmount(() => { dialog.value?.close(); void nextTick(() => { if (returnFocus?.isConnected) returnFocus.focus() }) })\n</script>\n<template>\n <Teleport to=\"body\"><dialog ref=\"dialog\" class=\"standalone-dialog\" :class=\"{ 'standalone-dialog--compact': compact, 'standalone-dialog--wide': wide, 'standalone-dialog--settings': settings }\" :aria-label=\"title\" @cancel.prevent=\"close\" @click=\"backdrop\">\n <header><h2>{{ title }}</h2><button type=\"button\" :aria-label=\"`关闭${title}`\" autofocus @click=\"close\"><AppIcon name=\"close\" :size=\"20\" /></button></header>\n <div class=\"standalone-dialog__content\"><slot /></div>\n </dialog></Teleport>\n</template>\n<style scoped>\n.standalone-dialog--compact.standalone-dialog--wide{width:min(480px,calc(100vw - 32px))}\n.standalone-dialog{width:min(1040px,calc(100vw - 48px));height:min(780px,calc(100dvh - 48px));max-width:none;max-height:none;margin:auto;padding:0;border:1px solid var(--line);border-radius:22px;background:var(--surface);color:var(--text-primary);box-shadow:0 24px 100px #0005;overflow:hidden}.standalone-dialog[open]{display:flex;flex-direction:column}.standalone-dialog::backdrop{background:#0007;backdrop-filter:blur(3px)}header{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:20px 24px 16px;border-bottom:1px solid var(--line);flex-shrink:0}h2{font-size:21px;font-weight:650;margin:0;letter-spacing:-.02em}header button{display:grid;place-items:center;width:44px;height:44px;padding:0;flex-shrink:0;border:0;border-radius:10px;color:inherit;background:transparent;cursor:pointer}header button:hover{background:var(--surface-hover)}header button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.standalone-dialog__content{padding:24px;overflow:auto;min-height:0;flex:1}.standalone-dialog--compact{width:min(380px,calc(100vw - 32px));height:fit-content;max-height:calc(100dvh - 32px);border-radius:18px}.standalone-dialog--compact header{padding:10px 12px 10px 20px}.standalone-dialog--compact h2{font-size:15px}.standalone-dialog--compact .standalone-dialog__content{padding:20px 24px 28px}@media(max-width:600px){.standalone-dialog:not(.standalone-dialog--compact){width:100vw;height:100dvh;border:0;border-radius:0}header{padding:10px 12px 10px 20px}h2{font-size:18px}.standalone-dialog__content{padding:20px}}\n</style>\n<style scoped>\n.standalone-dialog--settings{width:min(900px,calc(100vw - 48px));height:min(650px,calc(100dvh - 64px));border-radius:14px;box-shadow:0 24px 72px #0003}.standalone-dialog--settings>header{height:60px;padding:8px 16px 8px 20px;box-sizing:border-box}.standalone-dialog--settings h2{font-size:18px;font-weight:650}.standalone-dialog--settings .standalone-dialog__content{padding:0;overflow:hidden}.standalone-dialog--settings::backdrop{background:var(--scrim);backdrop-filter:blur(3px)}@media(max-width:600px){.standalone-dialog--settings{width:100vw;height:100dvh;border-radius:0}.standalone-dialog--settings>header{padding-left:20px}}\n</style>\n","<script setup lang=\"ts\">\nimport { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'\nimport AppIcon from './AppIcon.vue'\nconst props = defineProps<{ title: string; compact?: boolean; wide?: boolean; settings?: boolean; beforeClose?: () => boolean }>()\nconst emit = defineEmits<{ close: [] }>()\nconst dialog = ref<HTMLDialogElement>()\nconst returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : undefined\nfunction close() { if (props.beforeClose?.() !== false) emit('close') }\nfunction backdrop(event: MouseEvent) {\n if (event.target !== dialog.value) return\n const rect = dialog.value.getBoundingClientRect()\n if (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom) close()\n}\nonMounted(async () => { await nextTick(); dialog.value?.showModal() })\nonBeforeUnmount(() => { dialog.value?.close(); void nextTick(() => { if (returnFocus?.isConnected) returnFocus.focus() }) })\n</script>\n<template>\n <Teleport to=\"body\"><dialog ref=\"dialog\" class=\"standalone-dialog\" :class=\"{ 'standalone-dialog--compact': compact, 'standalone-dialog--wide': wide, 'standalone-dialog--settings': settings }\" :aria-label=\"title\" @cancel.prevent=\"close\" @click=\"backdrop\">\n <header><h2>{{ title }}</h2><button type=\"button\" :aria-label=\"`关闭${title}`\" autofocus @click=\"close\"><AppIcon name=\"close\" :size=\"20\" /></button></header>\n <div class=\"standalone-dialog__content\"><slot /></div>\n </dialog></Teleport>\n</template>\n<style scoped>\n.standalone-dialog--compact.standalone-dialog--wide{width:min(480px,calc(100vw - 32px))}\n.standalone-dialog{width:min(1040px,calc(100vw - 48px));height:min(780px,calc(100dvh - 48px));max-width:none;max-height:none;margin:auto;padding:0;border:1px solid var(--line);border-radius:22px;background:var(--surface);color:var(--text-primary);box-shadow:0 24px 100px #0005;overflow:hidden}.standalone-dialog[open]{display:flex;flex-direction:column}.standalone-dialog::backdrop{background:#0007;backdrop-filter:blur(3px)}header{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:20px 24px 16px;border-bottom:1px solid var(--line);flex-shrink:0}h2{font-size:21px;font-weight:650;margin:0;letter-spacing:-.02em}header button{display:grid;place-items:center;width:44px;height:44px;padding:0;flex-shrink:0;border:0;border-radius:10px;color:inherit;background:transparent;cursor:pointer}header button:hover{background:var(--surface-hover)}header button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.standalone-dialog__content{padding:24px;overflow:auto;min-height:0;flex:1}.standalone-dialog--compact{width:min(380px,calc(100vw - 32px));height:fit-content;max-height:calc(100dvh - 32px);border-radius:18px}.standalone-dialog--compact header{padding:10px 12px 10px 20px}.standalone-dialog--compact h2{font-size:15px}.standalone-dialog--compact .standalone-dialog__content{padding:20px 24px 28px}@media(max-width:600px){.standalone-dialog:not(.standalone-dialog--compact){width:100vw;height:100dvh;border:0;border-radius:0}header{padding:10px 12px 10px 20px}h2{font-size:18px}.standalone-dialog__content{padding:20px}}\n</style>\n<style scoped>\n.standalone-dialog--settings{width:min(900px,calc(100vw - 48px));height:min(650px,calc(100dvh - 64px));border-radius:14px;box-shadow:0 24px 72px #0003}.standalone-dialog--settings>header{height:60px;padding:8px 16px 8px 20px;box-sizing:border-box}.standalone-dialog--settings h2{font-size:18px;font-weight:650}.standalone-dialog--settings .standalone-dialog__content{padding:0;overflow:hidden}.standalone-dialog--settings::backdrop{background:var(--scrim);backdrop-filter:blur(3px)}@media(max-width:600px){.standalone-dialog--settings{width:100vw;height:100dvh;border-radius:0}.standalone-dialog--settings>header{padding-left:20px}}\n</style>\n"],"mappings":"0cAGA,IAAM,EAAQ,EACR,EAAO,EACP,EAAS,EAAuB,EAChC,EAAc,SAAS,yBAAyB,YAAc,SAAS,cAAgB,IAAA,GAC7F,SAAS,GAAQ,CAAM,EAAM,cAAc,IAAM,IAAO,EAAK,OAAO,CAAE,CACtE,SAAS,EAAS,EAAmB,CACnC,GAAI,EAAM,SAAW,EAAO,MAAO,OACnC,IAAM,EAAO,EAAO,MAAM,sBAAsB,GAC5C,EAAM,QAAU,EAAK,MAAQ,EAAM,QAAU,EAAK,OAAS,EAAM,QAAU,EAAK,KAAO,EAAM,QAAU,EAAK,SAAQ,EAAM,CAChI,QACA,EAAU,SAAY,CAAE,MAAM,EAAS,EAAG,EAAO,OAAO,UAAU,CAAE,CAAC,EACrE,MAAsB,CAAE,EAAO,OAAO,MAAM,EAAG,MAAoB,CAAM,GAAa,aAAa,EAAY,MAAM,CAAE,CAAC,CAAE,CAAC,UAGzH,EAAA,EAAA,EAGoB,EAAA,CAHV,GAAG,MAAM,EAAA,CAAC,EAGX,SAAA,CAHuB,QAAA,SAAJ,IAAI,EAAS,MAAK,EAAA,CAAC,oBAAmB,CAAA,6BAAyC,EAAA,QAAO,0BAA6B,EAAA,KAAI,8BAAiC,EAAA,QAAQ,CAAA,CAAA,EAAK,aAAY,EAAA,MAAQ,SAAM,EAAU,EAAK,CAAA,SAAA,CAAA,EAAG,QAAO,CAClP,EAAA,CAAA,EAA2J,SAAA,KAAA,CAAnJ,EAAoB,KAAA,KAAA,EAAb,EAAA,KAAK,EAAA,CAAA,EAAQ,EAAsH,SAAA,CAA9G,KAAK,SAAU,aAAU,KAAO,EAAA,QAAS,UAAA,GAAW,QAAO,CAAO,EAAA,CAAA,EAAmC,EAAA,CAA1B,KAAK,QAAS,KAAM,EACnI,CAAA,CAAA,EAAA,EAAA,CAAA,CAAA,CAAA,EAAA,EAAsD,MAAtD,EAAsD,CAAd,EAAQ,EAAA,OAAA,UAAA,CAAA,EAAA,IAAA,GAAA,EAAA,CAAA,CAAA,CAAA,EAAA,GAAA,CAAA,CAAA,CAAA"}
@@ -1,2 +1,2 @@
1
- import{A as e,Et as t,F as n,M as r,Ot as i,W as a,_ as o,b as s,d as c,et as l,f as u,m as d,p as f,t as p,u as m,v as h}from"./_plugin-vue_export-helper-CD0r-vVb.js";import{I as g}from"./index-DjhQ-RMG.js";import{t as _}from"./StandaloneDialog-BWUp2Wxo.js";import{c as v,l as y}from"./ConversationsView-DsuaLKva.js";var b=[`aria-busy`],x={class:`update-check__status`,role:`status`,"aria-live":`polite`},S={"aria-label":`Web 版本信息`},C={key:0,class:`update-check__error`,role:`alert`},w={key:1,class:`update-check__note`},T=[`href`],E={class:`update-check__actions`},D=[`disabled`],O=p(s({__name:`UpdateCheckDialog`,emits:[`close`,`manage`],setup(s,{emit:p}){let O=p,k=l(),A=l(!0),j=l(!1),M=l(``),N=!1,P=m(()=>A.value?`正在检测更新…`:M.value?`检测失败,请重试`:k.value?.updateAvailable?`发现新版本 ${k.value.latest?.webVersion}`:`当前已是最新版本`),F=m(()=>{try{let e=new URL(k.value?.releasePageUrl||``);return e.protocol===`https:`?e.href:void 0}catch{return}});async function I(){A.value=!0,j.value=!1,M.value=``;try{let e=await v();if(N)return;k.value=e,j.value=!0}catch(e){N||(M.value=e instanceof Error?e.message:`无法连接更新服务`)}finally{N||(A.value=!1)}}return r(async()=>{try{let e=await y();N||(k.value=e)}catch{}N||await I()}),e(()=>{N=!0}),(e,r)=>(n(),u(_,{title:`检测更新`,compact:``,onClose:r[1]||=e=>O(`close`)},{default:a(()=>[c(`section`,{class:`update-check`,"aria-busy":A.value},[c(`div`,{class:t([`update-check__icon`,{spinning:A.value}])},[h(g,{name:A.value?`refresh`:M.value?`alert`:k.value?.updateAvailable?`download`:`check`,size:28},null,8,[`name`])],2),c(`p`,x,i(P.value),1),c(`dl`,S,[c(`div`,null,[r[2]||=c(`dt`,null,`当前 Web 版本`,-1),c(`dd`,null,i(k.value?.current.webVersion||`—`),1)]),c(`div`,null,[r[3]||=c(`dt`,null,`最新版本`,-1),c(`dd`,null,i(A.value?`检测中…`:j.value?k.value?.latest?.webVersion||k.value?.current.webVersion:`未获取`),1)])]),M.value?(n(),d(`p`,C,i(M.value),1)):f(``,!0),j.value&&k.value?.updateAvailable&&!k.value.supported?(n(),d(`p`,w,i(k.value.unsupportedReason||`请通过部署环境更新 Web。`),1)):f(``,!0),j.value&&F.value?(n(),d(`a`,{key:2,href:F.value,target:`_blank`,rel:`noopener noreferrer`},[r[4]||=o(`查看发布说明`,-1),h(g,{name:`external`,size:14})],8,T)):f(``,!0),c(`div`,E,[c(`button`,{type:`button`,disabled:A.value,onClick:I},i(A.value?`正在检测…`:M.value?`重试`:`重新检测`),9,D),j.value&&k.value?.updateAvailable&&k.value.supported?(n(),d(`button`,{key:0,class:`primary`,type:`button`,onClick:r[0]||=e=>O(`manage`)},`前往更新`)):f(``,!0)])],8,b)]),_:1}))}}),[[`__scopeId`,`data-v-bf7e6d73`]]);export{O as default};
2
- //# sourceMappingURL=UpdateCheckDialog-CpZSPlUI.js.map
1
+ import{A as e,Et as t,F as n,M as r,Ot as i,W as a,_ as o,b as s,d as c,et as l,f as u,m as d,p as f,t as p,u as m,v as h}from"./_plugin-vue_export-helper-CD0r-vVb.js";import{B as g}from"./index-P_kZCZ2-.js";import{t as _}from"./StandaloneDialog-hF3z7bfi.js";import{c as v,l as y}from"./ConversationsView-kztie5lF.js";var b=[`aria-busy`],x={class:`update-check__status`,role:`status`,"aria-live":`polite`},S={"aria-label":`Web 版本信息`},C={key:0,class:`update-check__error`,role:`alert`},w={key:1,class:`update-check__note`},T=[`href`],E={class:`update-check__actions`},D=[`disabled`],O=p(s({__name:`UpdateCheckDialog`,emits:[`close`,`manage`],setup(s,{emit:p}){let O=p,k=l(),A=l(!0),j=l(!1),M=l(``),N=!1,P=m(()=>A.value?`正在检测更新…`:M.value?`检测失败,请重试`:k.value?.updateAvailable?`发现新版本 ${k.value.latest?.webVersion}`:`当前已是最新版本`),F=m(()=>{try{let e=new URL(k.value?.releasePageUrl||``);return e.protocol===`https:`?e.href:void 0}catch{return}});async function I(){A.value=!0,j.value=!1,M.value=``;try{let e=await v();if(N)return;k.value=e,j.value=!0}catch(e){N||(M.value=e instanceof Error?e.message:`无法连接更新服务`)}finally{N||(A.value=!1)}}return r(async()=>{try{let e=await y();N||(k.value=e)}catch{}N||await I()}),e(()=>{N=!0}),(e,r)=>(n(),u(_,{title:`检测更新`,compact:``,onClose:r[1]||=e=>O(`close`)},{default:a(()=>[c(`section`,{class:`update-check`,"aria-busy":A.value},[c(`div`,{class:t([`update-check__icon`,{spinning:A.value}])},[h(g,{name:A.value?`refresh`:M.value?`alert`:k.value?.updateAvailable?`download`:`check`,size:28},null,8,[`name`])],2),c(`p`,x,i(P.value),1),c(`dl`,S,[c(`div`,null,[r[2]||=c(`dt`,null,`当前 Web 版本`,-1),c(`dd`,null,i(k.value?.current.webVersion||`—`),1)]),c(`div`,null,[r[3]||=c(`dt`,null,`最新版本`,-1),c(`dd`,null,i(A.value?`检测中…`:j.value?k.value?.latest?.webVersion||k.value?.current.webVersion:`未获取`),1)])]),M.value?(n(),d(`p`,C,i(M.value),1)):f(``,!0),j.value&&k.value?.updateAvailable&&!k.value.supported?(n(),d(`p`,w,i(k.value.unsupportedReason||`请通过部署环境更新 Web。`),1)):f(``,!0),j.value&&F.value?(n(),d(`a`,{key:2,href:F.value,target:`_blank`,rel:`noopener noreferrer`},[r[4]||=o(`查看发布说明`,-1),h(g,{name:`external`,size:14})],8,T)):f(``,!0),c(`div`,E,[c(`button`,{type:`button`,disabled:A.value,onClick:I},i(A.value?`正在检测…`:M.value?`重试`:`重新检测`),9,D),j.value&&k.value?.updateAvailable&&k.value.supported?(n(),d(`button`,{key:0,class:`primary`,type:`button`,onClick:r[0]||=e=>O(`manage`)},`前往更新`)):f(``,!0)])],8,b)]),_:1}))}}),[[`__scopeId`,`data-v-bf7e6d73`]]);export{O as default};
2
+ //# sourceMappingURL=UpdateCheckDialog-Bz8JhRh4.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"UpdateCheckDialog-CpZSPlUI.js","names":[],"sources":["../../src/client/components/app/UpdateCheckDialog.vue","../../src/client/components/app/UpdateCheckDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\nimport { checkSystemUpdate, systemUpdateStatus, type SystemUpdateStatus } from '@/api/systemUpdate'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport StandaloneDialog from '@/components/common/StandaloneDialog.vue'\n\nconst emit = defineEmits<{ close: []; manage: [] }>()\nconst status = ref<SystemUpdateStatus>()\nconst checking = ref(true)\nconst checked = ref(false)\nconst error = ref('')\nlet disposed = false\nconst message = computed(() => checking.value ? '正在检测更新…'\n : error.value ? '检测失败,请重试'\n : status.value?.updateAvailable ? `发现新版本 ${status.value.latest?.webVersion}` : '当前已是最新版本')\nconst releasePage = computed(() => {\n try {\n const url = new URL(status.value?.releasePageUrl || '')\n return url.protocol === 'https:' ? url.href : undefined\n } catch { return undefined }\n})\n\nasync function check() {\n checking.value = true\n checked.value = false\n error.value = ''\n try {\n const next = await checkSystemUpdate()\n if (disposed) return\n status.value = next\n checked.value = true\n } catch (cause) {\n if (!disposed) error.value = cause instanceof Error ? cause.message : '无法连接更新服务'\n } finally { if (!disposed) checking.value = false }\n}\nonMounted(async () => {\n // Keep the installed version visible even when the remote check fails.\n try { const current = await systemUpdateStatus(); if (!disposed) status.value = current } catch { /* The check below reports actionable errors. */ }\n if (!disposed) await check()\n})\nonBeforeUnmount(() => { disposed = true })\n</script>\n<template>\n <StandaloneDialog title=\"检测更新\" compact @close=\"emit('close')\">\n <section class=\"update-check\" :aria-busy=\"checking\">\n <div class=\"update-check__icon\" :class=\"{ spinning: checking }\"><AppIcon :name=\"checking ? 'refresh' : error ? 'alert' : status?.updateAvailable ? 'download' : 'check'\" :size=\"28\" /></div>\n <p class=\"update-check__status\" role=\"status\" aria-live=\"polite\">{{ message }}</p>\n <dl aria-label=\"Web 版本信息\"><div><dt>当前 Web 版本</dt><dd>{{ status?.current.webVersion || '—' }}</dd></div><div><dt>最新版本</dt><dd>{{ checking ? '检测中…' : !checked ? '未获取' : status?.latest?.webVersion || status?.current.webVersion }}</dd></div></dl>\n <p v-if=\"error\" class=\"update-check__error\" role=\"alert\">{{ error }}</p>\n <p v-if=\"checked && status?.updateAvailable && !status.supported\" class=\"update-check__note\">{{ status.unsupportedReason || '请通过部署环境更新 Web。' }}</p>\n <a v-if=\"checked && releasePage\" :href=\"releasePage\" target=\"_blank\" rel=\"noopener noreferrer\">查看发布说明<AppIcon name=\"external\" :size=\"14\" /></a>\n <div class=\"update-check__actions\">\n <button type=\"button\" :disabled=\"checking\" @click=\"check\">{{ checking ? '正在检测…' : error ? '重试' : '重新检测' }}</button>\n <button v-if=\"checked && status?.updateAvailable && status.supported\" class=\"primary\" type=\"button\" @click=\"emit('manage')\">前往更新</button>\n </div>\n </section>\n </StandaloneDialog>\n</template>\n<style scoped>\n.update-check{display:grid;gap:18px;text-align:center}.update-check__icon{display:grid;place-items:center;color:var(--accent)}.update-check__status{margin:0;font-size:16px;font-weight:600;line-height:1.5}dl{display:grid;gap:12px;margin:0;padding:16px 0;border-block:1px solid var(--line);font-size:13px}dl div{display:flex;justify-content:space-between;gap:12px}dt{color:var(--text-secondary)}dd{margin:0;font-variant-numeric:tabular-nums}.update-check__error,.update-check__note{margin:0;font-size:13px;line-height:1.6;overflow-wrap:anywhere}.update-check__error{color:var(--danger)}.update-check__note{color:var(--text-secondary)}a{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:44px;color:var(--accent);font-size:13px;text-decoration:none}a:hover{text-decoration:underline}.update-check__actions{display:flex;gap:10px;flex-wrap:wrap}button{flex:1;min-height:44px;padding:8px 14px;border:1px solid var(--line);border-radius:9px;background:var(--surface-raised);color:var(--text-primary);font:500 13px var(--font-ui);cursor:pointer}button:hover{background:var(--surface-hover)}button.primary{background:var(--accent);border-color:var(--accent);color:var(--text-on-solid)}button:disabled{opacity:.6;cursor:wait}button:focus-visible,a:focus-visible{outline:2px solid var(--accent);outline-offset:3px}.spinning{animation:update-spin 1s linear infinite}@keyframes update-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.spinning{animation:none}}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\nimport { checkSystemUpdate, systemUpdateStatus, type SystemUpdateStatus } from '@/api/systemUpdate'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport StandaloneDialog from '@/components/common/StandaloneDialog.vue'\n\nconst emit = defineEmits<{ close: []; manage: [] }>()\nconst status = ref<SystemUpdateStatus>()\nconst checking = ref(true)\nconst checked = ref(false)\nconst error = ref('')\nlet disposed = false\nconst message = computed(() => checking.value ? '正在检测更新…'\n : error.value ? '检测失败,请重试'\n : status.value?.updateAvailable ? `发现新版本 ${status.value.latest?.webVersion}` : '当前已是最新版本')\nconst releasePage = computed(() => {\n try {\n const url = new URL(status.value?.releasePageUrl || '')\n return url.protocol === 'https:' ? url.href : undefined\n } catch { return undefined }\n})\n\nasync function check() {\n checking.value = true\n checked.value = false\n error.value = ''\n try {\n const next = await checkSystemUpdate()\n if (disposed) return\n status.value = next\n checked.value = true\n } catch (cause) {\n if (!disposed) error.value = cause instanceof Error ? cause.message : '无法连接更新服务'\n } finally { if (!disposed) checking.value = false }\n}\nonMounted(async () => {\n // Keep the installed version visible even when the remote check fails.\n try { const current = await systemUpdateStatus(); if (!disposed) status.value = current } catch { /* The check below reports actionable errors. */ }\n if (!disposed) await check()\n})\nonBeforeUnmount(() => { disposed = true })\n</script>\n<template>\n <StandaloneDialog title=\"检测更新\" compact @close=\"emit('close')\">\n <section class=\"update-check\" :aria-busy=\"checking\">\n <div class=\"update-check__icon\" :class=\"{ spinning: checking }\"><AppIcon :name=\"checking ? 'refresh' : error ? 'alert' : status?.updateAvailable ? 'download' : 'check'\" :size=\"28\" /></div>\n <p class=\"update-check__status\" role=\"status\" aria-live=\"polite\">{{ message }}</p>\n <dl aria-label=\"Web 版本信息\"><div><dt>当前 Web 版本</dt><dd>{{ status?.current.webVersion || '—' }}</dd></div><div><dt>最新版本</dt><dd>{{ checking ? '检测中…' : !checked ? '未获取' : status?.latest?.webVersion || status?.current.webVersion }}</dd></div></dl>\n <p v-if=\"error\" class=\"update-check__error\" role=\"alert\">{{ error }}</p>\n <p v-if=\"checked && status?.updateAvailable && !status.supported\" class=\"update-check__note\">{{ status.unsupportedReason || '请通过部署环境更新 Web。' }}</p>\n <a v-if=\"checked && releasePage\" :href=\"releasePage\" target=\"_blank\" rel=\"noopener noreferrer\">查看发布说明<AppIcon name=\"external\" :size=\"14\" /></a>\n <div class=\"update-check__actions\">\n <button type=\"button\" :disabled=\"checking\" @click=\"check\">{{ checking ? '正在检测…' : error ? '重试' : '重新检测' }}</button>\n <button v-if=\"checked && status?.updateAvailable && status.supported\" class=\"primary\" type=\"button\" @click=\"emit('manage')\">前往更新</button>\n </div>\n </section>\n </StandaloneDialog>\n</template>\n<style scoped>\n.update-check{display:grid;gap:18px;text-align:center}.update-check__icon{display:grid;place-items:center;color:var(--accent)}.update-check__status{margin:0;font-size:16px;font-weight:600;line-height:1.5}dl{display:grid;gap:12px;margin:0;padding:16px 0;border-block:1px solid var(--line);font-size:13px}dl div{display:flex;justify-content:space-between;gap:12px}dt{color:var(--text-secondary)}dd{margin:0;font-variant-numeric:tabular-nums}.update-check__error,.update-check__note{margin:0;font-size:13px;line-height:1.6;overflow-wrap:anywhere}.update-check__error{color:var(--danger)}.update-check__note{color:var(--text-secondary)}a{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:44px;color:var(--accent);font-size:13px;text-decoration:none}a:hover{text-decoration:underline}.update-check__actions{display:flex;gap:10px;flex-wrap:wrap}button{flex:1;min-height:44px;padding:8px 14px;border:1px solid var(--line);border-radius:9px;background:var(--surface-raised);color:var(--text-primary);font:500 13px var(--font-ui);cursor:pointer}button:hover{background:var(--surface-hover)}button.primary{background:var(--accent);border-color:var(--accent);color:var(--text-on-solid)}button:disabled{opacity:.6;cursor:wait}button:focus-visible,a:focus-visible{outline:2px solid var(--accent);outline-offset:3px}.spinning{animation:update-spin 1s linear infinite}@keyframes update-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.spinning{animation:none}}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"UpdateCheckDialog-Bz8JhRh4.js","names":[],"sources":["../../src/client/components/app/UpdateCheckDialog.vue","../../src/client/components/app/UpdateCheckDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\nimport { checkSystemUpdate, systemUpdateStatus, type SystemUpdateStatus } from '@/api/systemUpdate'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport StandaloneDialog from '@/components/common/StandaloneDialog.vue'\n\nconst emit = defineEmits<{ close: []; manage: [] }>()\nconst status = ref<SystemUpdateStatus>()\nconst checking = ref(true)\nconst checked = ref(false)\nconst error = ref('')\nlet disposed = false\nconst message = computed(() => checking.value ? '正在检测更新…'\n : error.value ? '检测失败,请重试'\n : status.value?.updateAvailable ? `发现新版本 ${status.value.latest?.webVersion}` : '当前已是最新版本')\nconst releasePage = computed(() => {\n try {\n const url = new URL(status.value?.releasePageUrl || '')\n return url.protocol === 'https:' ? url.href : undefined\n } catch { return undefined }\n})\n\nasync function check() {\n checking.value = true\n checked.value = false\n error.value = ''\n try {\n const next = await checkSystemUpdate()\n if (disposed) return\n status.value = next\n checked.value = true\n } catch (cause) {\n if (!disposed) error.value = cause instanceof Error ? cause.message : '无法连接更新服务'\n } finally { if (!disposed) checking.value = false }\n}\nonMounted(async () => {\n // Keep the installed version visible even when the remote check fails.\n try { const current = await systemUpdateStatus(); if (!disposed) status.value = current } catch { /* The check below reports actionable errors. */ }\n if (!disposed) await check()\n})\nonBeforeUnmount(() => { disposed = true })\n</script>\n<template>\n <StandaloneDialog title=\"检测更新\" compact @close=\"emit('close')\">\n <section class=\"update-check\" :aria-busy=\"checking\">\n <div class=\"update-check__icon\" :class=\"{ spinning: checking }\"><AppIcon :name=\"checking ? 'refresh' : error ? 'alert' : status?.updateAvailable ? 'download' : 'check'\" :size=\"28\" /></div>\n <p class=\"update-check__status\" role=\"status\" aria-live=\"polite\">{{ message }}</p>\n <dl aria-label=\"Web 版本信息\"><div><dt>当前 Web 版本</dt><dd>{{ status?.current.webVersion || '—' }}</dd></div><div><dt>最新版本</dt><dd>{{ checking ? '检测中…' : !checked ? '未获取' : status?.latest?.webVersion || status?.current.webVersion }}</dd></div></dl>\n <p v-if=\"error\" class=\"update-check__error\" role=\"alert\">{{ error }}</p>\n <p v-if=\"checked && status?.updateAvailable && !status.supported\" class=\"update-check__note\">{{ status.unsupportedReason || '请通过部署环境更新 Web。' }}</p>\n <a v-if=\"checked && releasePage\" :href=\"releasePage\" target=\"_blank\" rel=\"noopener noreferrer\">查看发布说明<AppIcon name=\"external\" :size=\"14\" /></a>\n <div class=\"update-check__actions\">\n <button type=\"button\" :disabled=\"checking\" @click=\"check\">{{ checking ? '正在检测…' : error ? '重试' : '重新检测' }}</button>\n <button v-if=\"checked && status?.updateAvailable && status.supported\" class=\"primary\" type=\"button\" @click=\"emit('manage')\">前往更新</button>\n </div>\n </section>\n </StandaloneDialog>\n</template>\n<style scoped>\n.update-check{display:grid;gap:18px;text-align:center}.update-check__icon{display:grid;place-items:center;color:var(--accent)}.update-check__status{margin:0;font-size:16px;font-weight:600;line-height:1.5}dl{display:grid;gap:12px;margin:0;padding:16px 0;border-block:1px solid var(--line);font-size:13px}dl div{display:flex;justify-content:space-between;gap:12px}dt{color:var(--text-secondary)}dd{margin:0;font-variant-numeric:tabular-nums}.update-check__error,.update-check__note{margin:0;font-size:13px;line-height:1.6;overflow-wrap:anywhere}.update-check__error{color:var(--danger)}.update-check__note{color:var(--text-secondary)}a{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:44px;color:var(--accent);font-size:13px;text-decoration:none}a:hover{text-decoration:underline}.update-check__actions{display:flex;gap:10px;flex-wrap:wrap}button{flex:1;min-height:44px;padding:8px 14px;border:1px solid var(--line);border-radius:9px;background:var(--surface-raised);color:var(--text-primary);font:500 13px var(--font-ui);cursor:pointer}button:hover{background:var(--surface-hover)}button.primary{background:var(--accent);border-color:var(--accent);color:var(--text-on-solid)}button:disabled{opacity:.6;cursor:wait}button:focus-visible,a:focus-visible{outline:2px solid var(--accent);outline-offset:3px}.spinning{animation:update-spin 1s linear infinite}@keyframes update-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.spinning{animation:none}}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\nimport { checkSystemUpdate, systemUpdateStatus, type SystemUpdateStatus } from '@/api/systemUpdate'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport StandaloneDialog from '@/components/common/StandaloneDialog.vue'\n\nconst emit = defineEmits<{ close: []; manage: [] }>()\nconst status = ref<SystemUpdateStatus>()\nconst checking = ref(true)\nconst checked = ref(false)\nconst error = ref('')\nlet disposed = false\nconst message = computed(() => checking.value ? '正在检测更新…'\n : error.value ? '检测失败,请重试'\n : status.value?.updateAvailable ? `发现新版本 ${status.value.latest?.webVersion}` : '当前已是最新版本')\nconst releasePage = computed(() => {\n try {\n const url = new URL(status.value?.releasePageUrl || '')\n return url.protocol === 'https:' ? url.href : undefined\n } catch { return undefined }\n})\n\nasync function check() {\n checking.value = true\n checked.value = false\n error.value = ''\n try {\n const next = await checkSystemUpdate()\n if (disposed) return\n status.value = next\n checked.value = true\n } catch (cause) {\n if (!disposed) error.value = cause instanceof Error ? cause.message : '无法连接更新服务'\n } finally { if (!disposed) checking.value = false }\n}\nonMounted(async () => {\n // Keep the installed version visible even when the remote check fails.\n try { const current = await systemUpdateStatus(); if (!disposed) status.value = current } catch { /* The check below reports actionable errors. */ }\n if (!disposed) await check()\n})\nonBeforeUnmount(() => { disposed = true })\n</script>\n<template>\n <StandaloneDialog title=\"检测更新\" compact @close=\"emit('close')\">\n <section class=\"update-check\" :aria-busy=\"checking\">\n <div class=\"update-check__icon\" :class=\"{ spinning: checking }\"><AppIcon :name=\"checking ? 'refresh' : error ? 'alert' : status?.updateAvailable ? 'download' : 'check'\" :size=\"28\" /></div>\n <p class=\"update-check__status\" role=\"status\" aria-live=\"polite\">{{ message }}</p>\n <dl aria-label=\"Web 版本信息\"><div><dt>当前 Web 版本</dt><dd>{{ status?.current.webVersion || '—' }}</dd></div><div><dt>最新版本</dt><dd>{{ checking ? '检测中…' : !checked ? '未获取' : status?.latest?.webVersion || status?.current.webVersion }}</dd></div></dl>\n <p v-if=\"error\" class=\"update-check__error\" role=\"alert\">{{ error }}</p>\n <p v-if=\"checked && status?.updateAvailable && !status.supported\" class=\"update-check__note\">{{ status.unsupportedReason || '请通过部署环境更新 Web。' }}</p>\n <a v-if=\"checked && releasePage\" :href=\"releasePage\" target=\"_blank\" rel=\"noopener noreferrer\">查看发布说明<AppIcon name=\"external\" :size=\"14\" /></a>\n <div class=\"update-check__actions\">\n <button type=\"button\" :disabled=\"checking\" @click=\"check\">{{ checking ? '正在检测…' : error ? '重试' : '重新检测' }}</button>\n <button v-if=\"checked && status?.updateAvailable && status.supported\" class=\"primary\" type=\"button\" @click=\"emit('manage')\">前往更新</button>\n </div>\n </section>\n </StandaloneDialog>\n</template>\n<style scoped>\n.update-check{display:grid;gap:18px;text-align:center}.update-check__icon{display:grid;place-items:center;color:var(--accent)}.update-check__status{margin:0;font-size:16px;font-weight:600;line-height:1.5}dl{display:grid;gap:12px;margin:0;padding:16px 0;border-block:1px solid var(--line);font-size:13px}dl div{display:flex;justify-content:space-between;gap:12px}dt{color:var(--text-secondary)}dd{margin:0;font-variant-numeric:tabular-nums}.update-check__error,.update-check__note{margin:0;font-size:13px;line-height:1.6;overflow-wrap:anywhere}.update-check__error{color:var(--danger)}.update-check__note{color:var(--text-secondary)}a{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:44px;color:var(--accent);font-size:13px;text-decoration:none}a:hover{text-decoration:underline}.update-check__actions{display:flex;gap:10px;flex-wrap:wrap}button{flex:1;min-height:44px;padding:8px 14px;border:1px solid var(--line);border-radius:9px;background:var(--surface-raised);color:var(--text-primary);font:500 13px var(--font-ui);cursor:pointer}button:hover{background:var(--surface-hover)}button.primary{background:var(--accent);border-color:var(--accent);color:var(--text-on-solid)}button:disabled{opacity:.6;cursor:wait}button:focus-visible,a:focus-visible{outline:2px solid var(--accent);outline-offset:3px}.spinning{animation:update-spin 1s linear infinite}@keyframes update-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.spinning{animation:none}}\n</style>\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import{Dt as e,Et as t,F as n,H as r,L as i,M as a,Ot as o,_ as s,a as c,b as l,d as u,et as d,g as f,m as p,p as m,st as h,t as g,u as _,v}from"./_plugin-vue_export-helper-CD0r-vVb.js";import{D as y,I as b,J as x,Y as S}from"./index-DjhQ-RMG.js";var C=[{id:`daily`,label:`每天`},{id:`weekly`,label:`每周`},{id:`cumulative`,label:`累计总量`}],w=e=>`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,`0`)}-${String(e.getDate()).padStart(2,`0`)}`;function T(e){let[t,n,r]=e.split(`-`).map(Number);return new Date(t,n-1,r)}function E(e){let t=T(e);return`${t.getMonth()+1}月${t.getDate()}日`}function D(e){let t=e=>e.toFixed(1).replace(/\.0$/,``);return e>=1e8?`${t(e/1e8)}亿`:e>=1e4?`${t(e/1e4)}万`:Math.round(e).toLocaleString(`zh-CN`)}function O(e,t){return`${E(e)}\n${D(t)} 个 Token`}var k=e=>(e.getDay()+6)%7;function A(e,t=new Date){let n=new Map(e.map(e=>[e.date,e.total])),r=new Date(t.getFullYear(),t.getMonth(),t.getDate()),i=new Date(r);i.setDate(i.getDate()-364),i.setDate(i.getDate()-k(i));let a=[];for(let e=new Date(i);e<=r;e.setDate(e.getDate()+1)){let t=w(e);a.push({date:t,total:n.get(t)??0})}return a}function j(e,t){if(e<=0||t<=0)return 0;let n=e/t;return n<=.25?1:n<=.5?2:n<=.75?3:4}function M(e,t){let n=new Map,r=0,i=e.map((e,t)=>{let i=Math.floor(t/7);return n.set(i,(n.get(i)??0)+e.total),r+=e.total,{...e,week:i,weekday:t%7,cumulative:r,weekTotal:0}});for(let e of i)e.weekTotal=n.get(e.week)??0;let a=i.map(e=>t===`daily`?e.total:t===`weekly`?e.weekTotal:e.cumulative),o=Math.max(0,...a);return i.map((e,t)=>({date:e.date,dayTotal:e.total,weekTotal:e.weekTotal,cumulative:e.cumulative,value:a[t],level:j(a[t],o),week:e.week,weekday:e.weekday}))}function N(e){let t=[],n=-1;for(let r of e)r.date.endsWith(`-01`)&&r.week!==n&&(n=r.week,t.push({week:r.week,label:`${T(r.date).getMonth()+1}月`}));return t}function P(e,t){let n=t===`daily`?e.dayTotal:t===`weekly`?e.weekTotal:e.cumulative;return t===`weekly`?`${E(e.date)} 所在周\n${D(n)} 个 Token`:t===`cumulative`?`${E(e.date)}\n累计 ${D(n)} 个 Token`:O(e.date,n)}var F={class:`activity-heatmap`,"aria-label":`Token 活动`},I={class:`activity-header`},L={class:`activity-modes`,role:`tablist`,"aria-label":`活动视图`},R=[`aria-selected`,`onClick`],z={key:0,class:`activity-empty`},B={key:1,class:`activity-body`},V={class:`activity-scroll`},H=[`aria-label`],U=[`aria-label`,`onMouseenter`,`onMousemove`,`onFocus`],ee=g(l({__name:`UsageActivityHeatmap`,props:{days:{},now:{}},setup(r){let a=r,s=d(`daily`),l=d(null),g=_(()=>A(a.days,a.now?new Date(a.now):new Date)),v=_(()=>M(g.value,s.value)),y=_(()=>N(v.value)),b=_(()=>v.value.length?v.value[v.value.length-1].week+1:0),x=_(()=>g.value.some(e=>e.total>0));function S(e,t){let n=v.value[t];if(!n)return;let r=e.currentTarget.closest(`.activity-heatmap`)?.getBoundingClientRect(),i=e.currentTarget.getBoundingClientRect(),a=`clientX`in e?e.clientX-(r?.left??0):i.left+i.width/2-(r?.left??0),o=`clientY`in e?e.clientY-(r?.top??0):i.top-(r?.top??0);l.value={text:P(n,s.value),x:a,y:o}}function w(){l.value=null}return(r,a)=>(n(),p(`section`,F,[u(`header`,I,[a[0]||=u(`h2`,null,`Token 活动`,-1),u(`div`,L,[(n(!0),p(c,null,i(h(C),e=>(n(),p(`button`,{key:e.id,type:`button`,role:`tab`,"aria-selected":s.value===e.id,onClick:t=>s.value=e.id},o(e.label),9,R))),128))])]),x.value?(n(),p(`div`,B,[a[1]||=u(`div`,{class:`activity-weekdays`,"aria-hidden":`true`},[u(`span`,null,`一`),u(`span`,null,`三`),u(`span`,null,`五`)],-1),u(`div`,V,[u(`div`,{class:`activity-months`,style:e({gridTemplateColumns:`repeat(${b.value}, minmax(11px, 1fr))`})},[(n(!0),p(c,null,i(y.value,t=>(n(),p(`span`,{key:t.week+t.label,class:`activity-month`,style:e({gridColumn:t.week+1})},o(t.label),5))),128))],4),u(`div`,{class:`activity-grid`,role:`img`,"aria-label":`近 12 个月 Token 活动,${s.value===`daily`?`按天`:s.value===`weekly`?`按周`:`累计`}着色`,style:e({gridTemplateColumns:`repeat(${b.value}, minmax(11px, 1fr))`})},[(n(!0),p(c,null,i(v.value,(r,i)=>(n(),p(`button`,{key:r.date,type:`button`,class:t([`activity-cell`,`level-${r.level}`]),style:e({gridColumn:r.week+1,gridRow:r.weekday+1}),"aria-label":h(P)(r,s.value).replace(`
2
- `,`,`),onMouseenter:e=>S(e,i),onMousemove:e=>S(e,i),onMouseleave:w,onFocus:e=>S(e,i),onBlur:w},null,46,U))),128))],12,H)]),a[2]||=f(`<div class="activity-legend" aria-hidden="true" data-v-80043765><span data-v-80043765>少</span><i class="level-0" data-v-80043765></i><i class="level-1" data-v-80043765></i><i class="level-2" data-v-80043765></i><i class="level-3" data-v-80043765></i><i class="level-4" data-v-80043765></i><span data-v-80043765>多</span></div>`,1),l.value?(n(),p(`div`,{key:0,class:`activity-tip`,role:`tooltip`,style:e({left:l.value.x+`px`,top:l.value.y+`px`})},o(l.value.text),5)):m(``,!0)])):(n(),p(`p`,z,`近 12 个月还没有 Token 记录。开始对话后,这里会显示每天的用量。`))]))}}),[[`__scopeId`,`data-v-80043765`]]),te={class:`usage-page`,"aria-label":`Token 用量总览`},ne={class:`usage-header`},re={class:`segmented`,role:`tablist`,"aria-label":`时间范围`},ie=[`aria-selected`,`onClick`],ae=[`disabled`],W={class:`usage-body`},G={key:0,class:`usage-error`,role:`alert`},K={key:1,class:`usage-note`,role:`status`},q={class:`usage-note`},J={class:`usage-cards`,"aria-label":`总量`},oe={class:`usage-card`},se={class:`usage-card`},ce={class:`usage-card`},le={class:`usage-card`},ue={key:0,class:`usage-note estimated-note`},de={class:`usage-grid`},fe={class:`usage-panel`,"aria-label":`按模型`},pe={key:0,class:`usage-empty`},me={key:1,class:`share-list`},he={class:`share-head`},ge=[`title`],_e={class:`share-bar`},ve={class:`usage-panel`,"aria-label":`按 Bot`},ye={key:0,class:`usage-empty`},be={class:`share-list`},xe={class:`share-head`},Se={class:`share-bar`},Ce={class:`deleted-title`},we={class:`share-list deleted`},Te={class:`share-head`},Ee={class:`share-bar`},De={class:`usage-panel`,"aria-label":`按天趋势`},Oe={key:0,class:`usage-empty`},ke=[`aria-label`],Ae=[`title`],je={key:1,class:`usage-panel`,"aria-label":`历史(未分模型)`},Me={class:`usage-note`},Ne={class:`usage-table`},Pe={key:0,class:`badge`},Fe={class:`usage-panel`,"aria-label":`Bot 与模型明细`},Ie={key:0,class:`usage-empty`},Le={key:1,class:`table-scroll`},Re={class:`usage-table`},Y={key:0,class:`badge`},ze=[`title`],Be={key:0,class:`est`},Ve={key:2,class:`pager`},He=[`disabled`],Ue=[`disabled`],X=20,Z=g(l({__name:`UsageView`,setup(l){let f=S(),g=x(),C=[{id:`today`,label:`今天`},{id:`7d`,label:`7 天`},{id:`30d`,label:`30 天`},{id:`all`,label:`全部`}],w=d(`7d`),T=d(null),E=d(null),D=d([]),O=d(!0),k=d(``),A=d(!1),j=d(0),M=_(()=>typeof g.query.from==`string`&&/^\/conversations(?:\/[a-f0-9-]{36})?(?:\?taskId=[a-f0-9-]{36})?$/i.test(g.query.from)?g.query.from:`/conversations`),N=e=>e.toLocaleString(`zh-CN`),P=e=>e>=1e9?`${(e/1e9).toFixed(2)}B`:e>=1e6?`${(e/1e6).toFixed(2)}M`:e>=1e4?`${(e/1e3).toFixed(1)}K`:N(e),F=(e,t)=>t>0?Math.round(e/t*1e3)/10:0,I=e=>e.estimatedCalls>0,L=e=>new Date(e).toLocaleString(`zh-CN`,{hour12:!1}),R=_(()=>T.value?.byAgent.filter(e=>!e.deleted)??[]),z=_(()=>T.value?.byAgent.filter(e=>e.deleted)??[]),B=_(()=>Math.max(1,...T.value?.byDay.map(e=>e.total)??[0])),V=_(()=>Math.max(1,Math.ceil((E.value?.total??0)/X))),H=_(()=>T.value?.firstRecordedAt?`统计自 ${L(T.value.firstRecordedAt)} 起`:`尚无记录`),U=0;async function Z(){let e=++U;O.value=!0;try{let t=`range=${w.value}`,[n,r,i]=await Promise.all([y(`/api/app/usage/overview?${t}`),y(`/api/app/usage/details?${t}&limit=${X}&offset=${j.value*X}`),y(`/api/app/usage/overview?range=all`)]);if(e!==U)return;T.value=n,E.value=r,D.value=i.byDay.map(e=>({date:e.date,total:e.total})),k.value=``}catch(t){e===U&&(k.value=t instanceof Error?t.message:`无法读取 Token 用量`)}finally{e===U&&(O.value=!1)}}r(w,()=>{j.value=0,Z()});function Q(e){j.value=Math.min(V.value-1,Math.max(0,j.value+e)),Z()}async function We(){A.value=!0;try{let e=await y(`/api/app/usage/details?range=${w.value}&format=csv`),t=URL.createObjectURL(new Blob([e],{type:`text/csv;charset=utf-8`})),n=document.createElement(`a`);n.href=t,n.download=`yaovi-token-usage-${w.value}.csv`,n.click(),window.setTimeout(()=>URL.revokeObjectURL(t),1e3)}catch(e){k.value=e instanceof Error?e.message:`导出失败`}finally{A.value=!1}}let $=e=>`输入 ${N(e.input)} · 输出 ${N(e.output)}`;return a(Z),(r,a)=>(n(),p(`main`,te,[u(`header`,ne,[u(`button`,{class:`icon-button`,type:`button`,"aria-label":`返回聊天`,onClick:a[0]||=e=>h(f).push(M.value)},[v(b,{name:`chevron-left`,size:20})]),v(b,{name:`history`,size:23}),a[3]||=u(`h1`,null,`Token 用量`,-1),u(`div`,re,[(n(),p(c,null,i(C,e=>u(`button`,{key:e.id,role:`tab`,"aria-selected":w.value===e.id,onClick:t=>w.value=e.id},o(e.label),9,ie)),64))]),u(`button`,{class:`usage-action`,type:`button`,disabled:A.value||!T.value?.totals.calls,onClick:We},o(A.value?`导出中…`:`导出 CSV`),9,ae),u(`button`,{class:`icon-button`,type:`button`,"aria-label":`刷新用量`,onClick:Z},[v(b,{name:`refresh`,size:17})])]),u(`div`,W,[k.value?(n(),p(`p`,G,o(k.value),1)):m(``,!0),O.value&&!T.value?(n(),p(`p`,K,`正在读取 Token 用量…`)):m(``,!0),T.value?(n(),p(c,{key:2},[u(`p`,q,o(H.value)+`。合计 = 输入 + 输出;缓存和推理 Token 单独列出,不重复计入。`,1),u(`section`,J,[u(`div`,oe,[a[4]||=u(`small`,null,`合计`,-1),u(`strong`,null,o(P(T.value.totals.total)),1),u(`span`,null,o(I(T.value.totals)?`≈ `:``)+o(N(T.value.totals.calls))+` 次调用`,1)]),u(`div`,se,[a[5]||=u(`small`,null,`输入`,-1),u(`strong`,null,o(P(T.value.totals.input)),1),u(`span`,null,`缓存读 `+o(N(T.value.totals.cacheRead))+` · 缓存写 `+o(N(T.value.totals.cacheWrite)),1)]),u(`div`,ce,[a[6]||=u(`small`,null,`输出`,-1),u(`strong`,null,o(P(T.value.totals.output)),1),u(`span`,null,`推理 `+o(N(T.value.totals.reasoning)),1)]),u(`div`,le,[a[7]||=u(`small`,null,`已删除 Bot`,-1),u(`strong`,null,o(P(T.value.deletedTotals.total)),1),u(`span`,null,o(F(T.value.deletedTotals.total,T.value.totals.total))+`% 占比`,1)])]),T.value.totals.estimatedCalls?(n(),p(`p`,ue,`其中 `+o(N(T.value.totals.estimatedCalls))+` 次为本机估算值(供应商未返回用量),标记 ≈。`,1)):m(``,!0),v(ee,{days:D.value},null,8,[`days`]),u(`div`,de,[u(`section`,fe,[a[8]||=u(`h2`,null,`按模型`,-1),T.value.byModel.length?(n(),p(`ul`,me,[(n(!0),p(c,null,i(T.value.byModel,t=>(n(),p(`li`,{key:t.provider+t.model},[u(`div`,he,[u(`strong`,{title:t.provider},o(t.model),9,ge),u(`span`,null,o(I(t)?`≈ `:``)+o(N(t.total))+` · `+o(F(t.total,T.value.totals.total))+`%`,1)]),u(`div`,_e,[u(`i`,{style:e({width:F(t.total,T.value.totals.total)+`%`})},null,4)]),u(`small`,null,o(t.provider)+` · 输入 `+o(N(t.input))+` · 输出 `+o(N(t.output))+` · `+o(N(t.calls))+` 次`,1)]))),128))])):(n(),p(`p`,pe,`这个时间范围内还没有记录。`))]),u(`section`,ve,[a[11]||=u(`h2`,null,`按 Bot`,-1),T.value.byAgent.length?(n(),p(c,{key:1},[u(`ul`,be,[(n(!0),p(c,null,i(R.value,t=>(n(),p(`li`,{key:t.agentId},[u(`div`,xe,[u(`strong`,null,o(t.name),1),u(`span`,null,o(I(t)?`≈ `:``)+o(N(t.total))+` · `+o(F(t.total,T.value.totals.total))+`%`,1)]),u(`div`,Se,[u(`i`,{style:e({width:F(t.total,T.value.totals.total)+`%`})},null,4)]),u(`small`,null,o($(t))+` · `+o(N(t.calls))+` 次`,1)]))),128))]),z.value.length?(n(),p(c,{key:0},[u(`h3`,Ce,[a[9]||=s(`已删除的 Bot `,-1),u(`span`,null,o(N(T.value.deletedTotals.total)),1)]),u(`ul`,we,[(n(!0),p(c,null,i(z.value,t=>(n(),p(`li`,{key:t.agentId},[u(`div`,Te,[u(`strong`,null,[s(o(t.name)+` `,1),a[10]||=u(`em`,{class:`badge`},`已删除`,-1)]),u(`span`,null,o(I(t)?`≈ `:``)+o(N(t.total))+` · `+o(F(t.total,T.value.totals.total))+`%`,1)]),u(`div`,Ee,[u(`i`,{style:e({width:F(t.total,T.value.totals.total)+`%`})},null,4)]),u(`small`,null,o($(t))+` · `+o(N(t.calls))+` 次`,1)]))),128))])],64)):m(``,!0)],64)):(n(),p(`p`,ye,`这个时间范围内还没有记录。`))])]),u(`section`,De,[a[12]||=u(`h2`,null,`按天趋势`,-1),T.value.byDay.length?(n(),p(`div`,{key:1,class:`trend`,role:`img`,"aria-label":`按天 Token 用量,共 ${T.value.byDay.length} 天`},[(n(!0),p(c,null,i(T.value.byDay,t=>(n(),p(`div`,{key:t.date,class:`trend-col`,title:`${t.date}:${N(t.total)}(输入 ${N(t.input)},输出 ${N(t.output)})`},[u(`i`,{style:e({height:Math.max(2,t.total/B.value*100)+`%`})},null,4),u(`small`,null,o(t.date.slice(5)),1)],8,Ae))),128))],8,ke)):(n(),p(`p`,Oe,`这个时间范围内还没有记录。`))]),T.value.historical?(n(),p(`section`,je,[a[14]||=u(`h2`,null,`历史(未分模型)`,-1),u(`p`,Me,o(T.value.historical.before)+` 之前的旧统计,只有每个 Bot 的输入和输出,没有模型信息,也没有计入上面的总量。合计 `+o(N(T.value.historical.totals.total))+`(输入 `+o(N(T.value.historical.totals.input))+` · 输出 `+o(N(T.value.historical.totals.output))+`)。`,1),u(`table`,Ne,[a[13]||=u(`thead`,null,[u(`tr`,null,[u(`th`,null,`Bot`),u(`th`,null,`输入`),u(`th`,null,`输出`),u(`th`,null,`合计`)])],-1),u(`tbody`,null,[(n(!0),p(c,null,i(T.value.historical.byAgent,e=>(n(),p(`tr`,{key:e.agentId},[u(`td`,null,[s(o(e.name)+` `,1),e.deleted?(n(),p(`em`,Pe,`已删除`)):m(``,!0)]),u(`td`,null,o(N(e.input)),1),u(`td`,null,o(N(e.output)),1),u(`td`,null,o(N(e.total)),1)]))),128))])])])):m(``,!0),u(`section`,Fe,[a[16]||=u(`h2`,null,`明细:Bot × 模型`,-1),E.value?.rows.length?(n(),p(`div`,Le,[u(`table`,Re,[a[15]||=u(`thead`,null,[u(`tr`,null,[u(`th`,null,`Bot`),u(`th`,null,`模型`),u(`th`,null,`输入`),u(`th`,null,`输出`),u(`th`,null,`缓存读`),u(`th`,null,`缓存写`),u(`th`,null,`推理`),u(`th`,null,`合计`),u(`th`,null,`次数`),u(`th`,null,`最近使用`)])],-1),u(`tbody`,null,[(n(!0),p(c,null,i(E.value.rows,e=>(n(),p(`tr`,{key:e.agentId+e.provider+e.model,class:t({deleted:e.deleted})},[u(`td`,null,[s(o(e.name)+` `,1),e.deleted?(n(),p(`em`,Y,`已删除`)):m(``,!0)]),u(`td`,{title:e.provider},o(e.model),9,ze),u(`td`,null,o(N(e.input)),1),u(`td`,null,o(N(e.output)),1),u(`td`,null,o(N(e.cacheRead)),1),u(`td`,null,o(N(e.cacheWrite)),1),u(`td`,null,o(N(e.reasoning)),1),u(`td`,null,o(I(e)?`≈ `:``)+o(N(e.total)),1),u(`td`,null,[s(o(N(e.calls)),1),e.estimatedCalls?(n(),p(`span`,Be,` (估算 `+o(N(e.estimatedCalls))+`)`,1)):m(``,!0)]),u(`td`,null,o(L(e.lastAt)),1)],2))),128))])])])):(n(),p(`p`,Ie,`这个时间范围内还没有记录。`)),V.value>1?(n(),p(`div`,Ve,[u(`button`,{disabled:j.value===0||O.value,onClick:a[1]||=e=>Q(-1)},`上一页`,8,He),u(`span`,null,o(j.value+1)+` / `+o(V.value),1),u(`button`,{disabled:j.value>=V.value-1||O.value,onClick:a[2]||=e=>Q(1)},`下一页`,8,Ue)])):m(``,!0)]),a[17]||=u(`p`,{class:`usage-footer`},`不含辅助模型调用(标题、压缩、复盘、记忆提炼等)。已删除 Bot 的用量会保留。日期按 Host 本机时区划分。`,-1)],64)):m(``,!0)])]))}}),[[`__scopeId`,`data-v-61e5b2cf`]]);export{Z as default};
3
- //# sourceMappingURL=UsageView-BhiRCw7p.js.map
1
+ import{Dt as e,Et as t,F as n,H as r,L as i,M as a,Ot as o,_ as s,a as c,b as l,d as u,et as d,g as f,m as p,p as m,st as h,t as g,u as _,v}from"./_plugin-vue_export-helper-CD0r-vVb.js";import{B as y,Q as b,Z as x,j as S}from"./index-P_kZCZ2-.js";var C=[{id:`daily`,label:`每天`},{id:`weekly`,label:`每周`},{id:`cumulative`,label:`累计总量`}],w=e=>`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,`0`)}-${String(e.getDate()).padStart(2,`0`)}`;function T(e){let[t,n,r]=e.split(`-`).map(Number);return new Date(t,n-1,r)}function E(e){let t=T(e);return`${t.getMonth()+1}月${t.getDate()}日`}function D(e){let t=e=>e.toFixed(1).replace(/\.0$/,``);return e>=1e8?`${t(e/1e8)}亿`:e>=1e4?`${t(e/1e4)}万`:Math.round(e).toLocaleString(`zh-CN`)}function O(e,t){return`${E(e)}\n${D(t)} 个 Token`}var k=e=>(e.getDay()+6)%7;function A(e,t=new Date){let n=new Map(e.map(e=>[e.date,e.total])),r=new Date(t.getFullYear(),t.getMonth(),t.getDate()),i=new Date(r);i.setDate(i.getDate()-364),i.setDate(i.getDate()-k(i));let a=[];for(let e=new Date(i);e<=r;e.setDate(e.getDate()+1)){let t=w(e);a.push({date:t,total:n.get(t)??0})}return a}function j(e,t){if(e<=0||t<=0)return 0;let n=e/t;return n<=.25?1:n<=.5?2:n<=.75?3:4}function M(e,t){let n=new Map,r=0,i=e.map((e,t)=>{let i=Math.floor(t/7);return n.set(i,(n.get(i)??0)+e.total),r+=e.total,{...e,week:i,weekday:t%7,cumulative:r,weekTotal:0}});for(let e of i)e.weekTotal=n.get(e.week)??0;let a=i.map(e=>t===`daily`?e.total:t===`weekly`?e.weekTotal:e.cumulative),o=Math.max(0,...a);return i.map((e,t)=>({date:e.date,dayTotal:e.total,weekTotal:e.weekTotal,cumulative:e.cumulative,value:a[t],level:j(a[t],o),week:e.week,weekday:e.weekday}))}function N(e){let t=[],n=-1;for(let r of e)r.date.endsWith(`-01`)&&r.week!==n&&(n=r.week,t.push({week:r.week,label:`${T(r.date).getMonth()+1}月`}));return t}function P(e,t){let n=t===`daily`?e.dayTotal:t===`weekly`?e.weekTotal:e.cumulative;return t===`weekly`?`${E(e.date)} 所在周\n${D(n)} 个 Token`:t===`cumulative`?`${E(e.date)}\n累计 ${D(n)} 个 Token`:O(e.date,n)}var F={class:`activity-heatmap`,"aria-label":`Token 活动`},I={class:`activity-header`},L={class:`activity-modes`,role:`tablist`,"aria-label":`活动视图`},R=[`aria-selected`,`onClick`],z={key:0,class:`activity-empty`},B={key:1,class:`activity-body`},V={class:`activity-scroll`},H=[`aria-label`],U=[`aria-label`,`onMouseenter`,`onMousemove`,`onFocus`],ee=g(l({__name:`UsageActivityHeatmap`,props:{days:{},now:{}},setup(r){let a=r,s=d(`daily`),l=d(null),g=_(()=>A(a.days,a.now?new Date(a.now):new Date)),v=_(()=>M(g.value,s.value)),y=_(()=>N(v.value)),b=_(()=>v.value.length?v.value[v.value.length-1].week+1:0),x=_(()=>g.value.some(e=>e.total>0));function S(e,t){let n=v.value[t];if(!n)return;let r=e.currentTarget.closest(`.activity-heatmap`)?.getBoundingClientRect(),i=e.currentTarget.getBoundingClientRect(),a=`clientX`in e?e.clientX-(r?.left??0):i.left+i.width/2-(r?.left??0),o=`clientY`in e?e.clientY-(r?.top??0):i.top-(r?.top??0);l.value={text:P(n,s.value),x:a,y:o}}function w(){l.value=null}return(r,a)=>(n(),p(`section`,F,[u(`header`,I,[a[0]||=u(`h2`,null,`Token 活动`,-1),u(`div`,L,[(n(!0),p(c,null,i(h(C),e=>(n(),p(`button`,{key:e.id,type:`button`,role:`tab`,"aria-selected":s.value===e.id,onClick:t=>s.value=e.id},o(e.label),9,R))),128))])]),x.value?(n(),p(`div`,B,[a[1]||=u(`div`,{class:`activity-weekdays`,"aria-hidden":`true`},[u(`span`,null,`一`),u(`span`,null,`三`),u(`span`,null,`五`)],-1),u(`div`,V,[u(`div`,{class:`activity-months`,style:e({gridTemplateColumns:`repeat(${b.value}, minmax(11px, 1fr))`})},[(n(!0),p(c,null,i(y.value,t=>(n(),p(`span`,{key:t.week+t.label,class:`activity-month`,style:e({gridColumn:t.week+1})},o(t.label),5))),128))],4),u(`div`,{class:`activity-grid`,role:`img`,"aria-label":`近 12 个月 Token 活动,${s.value===`daily`?`按天`:s.value===`weekly`?`按周`:`累计`}着色`,style:e({gridTemplateColumns:`repeat(${b.value}, minmax(11px, 1fr))`})},[(n(!0),p(c,null,i(v.value,(r,i)=>(n(),p(`button`,{key:r.date,type:`button`,class:t([`activity-cell`,`level-${r.level}`]),style:e({gridColumn:r.week+1,gridRow:r.weekday+1}),"aria-label":h(P)(r,s.value).replace(`
2
+ `,`,`),onMouseenter:e=>S(e,i),onMousemove:e=>S(e,i),onMouseleave:w,onFocus:e=>S(e,i),onBlur:w},null,46,U))),128))],12,H)]),a[2]||=f(`<div class="activity-legend" aria-hidden="true" data-v-80043765><span data-v-80043765>少</span><i class="level-0" data-v-80043765></i><i class="level-1" data-v-80043765></i><i class="level-2" data-v-80043765></i><i class="level-3" data-v-80043765></i><i class="level-4" data-v-80043765></i><span data-v-80043765>多</span></div>`,1),l.value?(n(),p(`div`,{key:0,class:`activity-tip`,role:`tooltip`,style:e({left:l.value.x+`px`,top:l.value.y+`px`})},o(l.value.text),5)):m(``,!0)])):(n(),p(`p`,z,`近 12 个月还没有 Token 记录。开始对话后,这里会显示每天的用量。`))]))}}),[[`__scopeId`,`data-v-80043765`]]),te={class:`usage-page`,"aria-label":`Token 用量总览`},ne={class:`usage-header`},re={class:`segmented`,role:`tablist`,"aria-label":`时间范围`},ie=[`aria-selected`,`onClick`],ae=[`disabled`],W={class:`usage-body`},G={key:0,class:`usage-error`,role:`alert`},K={key:1,class:`usage-note`,role:`status`},q={class:`usage-note`},J={class:`usage-cards`,"aria-label":`总量`},oe={class:`usage-card`},se={class:`usage-card`},ce={class:`usage-card`},le={class:`usage-card`},ue={key:0,class:`usage-note estimated-note`},de={class:`usage-grid`},fe={class:`usage-panel`,"aria-label":`按模型`},pe={key:0,class:`usage-empty`},me={key:1,class:`share-list`},he={class:`share-head`},ge=[`title`],_e={class:`share-bar`},ve={class:`usage-panel`,"aria-label":`按 Bot`},ye={key:0,class:`usage-empty`},be={class:`share-list`},xe={class:`share-head`},Se={class:`share-bar`},Ce={class:`deleted-title`},we={class:`share-list deleted`},Te={class:`share-head`},Ee={class:`share-bar`},De={class:`usage-panel`,"aria-label":`按天趋势`},Oe={key:0,class:`usage-empty`},ke=[`aria-label`],Ae=[`title`],je={key:1,class:`usage-panel`,"aria-label":`历史(未分模型)`},Me={class:`usage-note`},Ne={class:`usage-table`},Pe={key:0,class:`badge`},Fe={class:`usage-panel`,"aria-label":`Bot 与模型明细`},Ie={key:0,class:`usage-empty`},Le={key:1,class:`table-scroll`},Re={class:`usage-table`},Y={key:0,class:`badge`},ze=[`title`],Be={key:0,class:`est`},Ve={key:2,class:`pager`},He=[`disabled`],Ue=[`disabled`],X=20,Z=g(l({__name:`UsageView`,setup(l){let f=b(),g=x(),C=[{id:`today`,label:`今天`},{id:`7d`,label:`7 天`},{id:`30d`,label:`30 天`},{id:`all`,label:`全部`}],w=d(`7d`),T=d(null),E=d(null),D=d([]),O=d(!0),k=d(``),A=d(!1),j=d(0),M=_(()=>typeof g.query.from==`string`&&/^\/conversations(?:\/[a-f0-9-]{36})?(?:\?taskId=[a-f0-9-]{36})?$/i.test(g.query.from)?g.query.from:`/conversations`),N=e=>e.toLocaleString(`zh-CN`),P=e=>e>=1e9?`${(e/1e9).toFixed(2)}B`:e>=1e6?`${(e/1e6).toFixed(2)}M`:e>=1e4?`${(e/1e3).toFixed(1)}K`:N(e),F=(e,t)=>t>0?Math.round(e/t*1e3)/10:0,I=e=>e.estimatedCalls>0,L=e=>new Date(e).toLocaleString(`zh-CN`,{hour12:!1}),R=_(()=>T.value?.byAgent.filter(e=>!e.deleted)??[]),z=_(()=>T.value?.byAgent.filter(e=>e.deleted)??[]),B=_(()=>Math.max(1,...T.value?.byDay.map(e=>e.total)??[0])),V=_(()=>Math.max(1,Math.ceil((E.value?.total??0)/X))),H=_(()=>T.value?.firstRecordedAt?`统计自 ${L(T.value.firstRecordedAt)} 起`:`尚无记录`),U=0;async function Z(){let e=++U;O.value=!0;try{let t=`range=${w.value}`,[n,r,i]=await Promise.all([S(`/api/app/usage/overview?${t}`),S(`/api/app/usage/details?${t}&limit=${X}&offset=${j.value*X}`),S(`/api/app/usage/overview?range=all`)]);if(e!==U)return;T.value=n,E.value=r,D.value=i.byDay.map(e=>({date:e.date,total:e.total})),k.value=``}catch(t){e===U&&(k.value=t instanceof Error?t.message:`无法读取 Token 用量`)}finally{e===U&&(O.value=!1)}}r(w,()=>{j.value=0,Z()});function Q(e){j.value=Math.min(V.value-1,Math.max(0,j.value+e)),Z()}async function We(){A.value=!0;try{let e=await S(`/api/app/usage/details?range=${w.value}&format=csv`),t=URL.createObjectURL(new Blob([e],{type:`text/csv;charset=utf-8`})),n=document.createElement(`a`);n.href=t,n.download=`yaovi-token-usage-${w.value}.csv`,n.click(),window.setTimeout(()=>URL.revokeObjectURL(t),1e3)}catch(e){k.value=e instanceof Error?e.message:`导出失败`}finally{A.value=!1}}let $=e=>`输入 ${N(e.input)} · 输出 ${N(e.output)}`;return a(Z),(r,a)=>(n(),p(`main`,te,[u(`header`,ne,[u(`button`,{class:`icon-button`,type:`button`,"aria-label":`返回聊天`,onClick:a[0]||=e=>h(f).push(M.value)},[v(y,{name:`chevron-left`,size:20})]),v(y,{name:`history`,size:23}),a[3]||=u(`h1`,null,`Token 用量`,-1),u(`div`,re,[(n(),p(c,null,i(C,e=>u(`button`,{key:e.id,role:`tab`,"aria-selected":w.value===e.id,onClick:t=>w.value=e.id},o(e.label),9,ie)),64))]),u(`button`,{class:`usage-action`,type:`button`,disabled:A.value||!T.value?.totals.calls,onClick:We},o(A.value?`导出中…`:`导出 CSV`),9,ae),u(`button`,{class:`icon-button`,type:`button`,"aria-label":`刷新用量`,onClick:Z},[v(y,{name:`refresh`,size:17})])]),u(`div`,W,[k.value?(n(),p(`p`,G,o(k.value),1)):m(``,!0),O.value&&!T.value?(n(),p(`p`,K,`正在读取 Token 用量…`)):m(``,!0),T.value?(n(),p(c,{key:2},[u(`p`,q,o(H.value)+`。合计 = 输入 + 输出;缓存和推理 Token 单独列出,不重复计入。`,1),u(`section`,J,[u(`div`,oe,[a[4]||=u(`small`,null,`合计`,-1),u(`strong`,null,o(P(T.value.totals.total)),1),u(`span`,null,o(I(T.value.totals)?`≈ `:``)+o(N(T.value.totals.calls))+` 次调用`,1)]),u(`div`,se,[a[5]||=u(`small`,null,`输入`,-1),u(`strong`,null,o(P(T.value.totals.input)),1),u(`span`,null,`缓存读 `+o(N(T.value.totals.cacheRead))+` · 缓存写 `+o(N(T.value.totals.cacheWrite)),1)]),u(`div`,ce,[a[6]||=u(`small`,null,`输出`,-1),u(`strong`,null,o(P(T.value.totals.output)),1),u(`span`,null,`推理 `+o(N(T.value.totals.reasoning)),1)]),u(`div`,le,[a[7]||=u(`small`,null,`已删除 Bot`,-1),u(`strong`,null,o(P(T.value.deletedTotals.total)),1),u(`span`,null,o(F(T.value.deletedTotals.total,T.value.totals.total))+`% 占比`,1)])]),T.value.totals.estimatedCalls?(n(),p(`p`,ue,`其中 `+o(N(T.value.totals.estimatedCalls))+` 次为本机估算值(供应商未返回用量),标记 ≈。`,1)):m(``,!0),v(ee,{days:D.value},null,8,[`days`]),u(`div`,de,[u(`section`,fe,[a[8]||=u(`h2`,null,`按模型`,-1),T.value.byModel.length?(n(),p(`ul`,me,[(n(!0),p(c,null,i(T.value.byModel,t=>(n(),p(`li`,{key:t.provider+t.model},[u(`div`,he,[u(`strong`,{title:t.provider},o(t.model),9,ge),u(`span`,null,o(I(t)?`≈ `:``)+o(N(t.total))+` · `+o(F(t.total,T.value.totals.total))+`%`,1)]),u(`div`,_e,[u(`i`,{style:e({width:F(t.total,T.value.totals.total)+`%`})},null,4)]),u(`small`,null,o(t.provider)+` · 输入 `+o(N(t.input))+` · 输出 `+o(N(t.output))+` · `+o(N(t.calls))+` 次`,1)]))),128))])):(n(),p(`p`,pe,`这个时间范围内还没有记录。`))]),u(`section`,ve,[a[11]||=u(`h2`,null,`按 Bot`,-1),T.value.byAgent.length?(n(),p(c,{key:1},[u(`ul`,be,[(n(!0),p(c,null,i(R.value,t=>(n(),p(`li`,{key:t.agentId},[u(`div`,xe,[u(`strong`,null,o(t.name),1),u(`span`,null,o(I(t)?`≈ `:``)+o(N(t.total))+` · `+o(F(t.total,T.value.totals.total))+`%`,1)]),u(`div`,Se,[u(`i`,{style:e({width:F(t.total,T.value.totals.total)+`%`})},null,4)]),u(`small`,null,o($(t))+` · `+o(N(t.calls))+` 次`,1)]))),128))]),z.value.length?(n(),p(c,{key:0},[u(`h3`,Ce,[a[9]||=s(`已删除的 Bot `,-1),u(`span`,null,o(N(T.value.deletedTotals.total)),1)]),u(`ul`,we,[(n(!0),p(c,null,i(z.value,t=>(n(),p(`li`,{key:t.agentId},[u(`div`,Te,[u(`strong`,null,[s(o(t.name)+` `,1),a[10]||=u(`em`,{class:`badge`},`已删除`,-1)]),u(`span`,null,o(I(t)?`≈ `:``)+o(N(t.total))+` · `+o(F(t.total,T.value.totals.total))+`%`,1)]),u(`div`,Ee,[u(`i`,{style:e({width:F(t.total,T.value.totals.total)+`%`})},null,4)]),u(`small`,null,o($(t))+` · `+o(N(t.calls))+` 次`,1)]))),128))])],64)):m(``,!0)],64)):(n(),p(`p`,ye,`这个时间范围内还没有记录。`))])]),u(`section`,De,[a[12]||=u(`h2`,null,`按天趋势`,-1),T.value.byDay.length?(n(),p(`div`,{key:1,class:`trend`,role:`img`,"aria-label":`按天 Token 用量,共 ${T.value.byDay.length} 天`},[(n(!0),p(c,null,i(T.value.byDay,t=>(n(),p(`div`,{key:t.date,class:`trend-col`,title:`${t.date}:${N(t.total)}(输入 ${N(t.input)},输出 ${N(t.output)})`},[u(`i`,{style:e({height:Math.max(2,t.total/B.value*100)+`%`})},null,4),u(`small`,null,o(t.date.slice(5)),1)],8,Ae))),128))],8,ke)):(n(),p(`p`,Oe,`这个时间范围内还没有记录。`))]),T.value.historical?(n(),p(`section`,je,[a[14]||=u(`h2`,null,`历史(未分模型)`,-1),u(`p`,Me,o(T.value.historical.before)+` 之前的旧统计,只有每个 Bot 的输入和输出,没有模型信息,也没有计入上面的总量。合计 `+o(N(T.value.historical.totals.total))+`(输入 `+o(N(T.value.historical.totals.input))+` · 输出 `+o(N(T.value.historical.totals.output))+`)。`,1),u(`table`,Ne,[a[13]||=u(`thead`,null,[u(`tr`,null,[u(`th`,null,`Bot`),u(`th`,null,`输入`),u(`th`,null,`输出`),u(`th`,null,`合计`)])],-1),u(`tbody`,null,[(n(!0),p(c,null,i(T.value.historical.byAgent,e=>(n(),p(`tr`,{key:e.agentId},[u(`td`,null,[s(o(e.name)+` `,1),e.deleted?(n(),p(`em`,Pe,`已删除`)):m(``,!0)]),u(`td`,null,o(N(e.input)),1),u(`td`,null,o(N(e.output)),1),u(`td`,null,o(N(e.total)),1)]))),128))])])])):m(``,!0),u(`section`,Fe,[a[16]||=u(`h2`,null,`明细:Bot × 模型`,-1),E.value?.rows.length?(n(),p(`div`,Le,[u(`table`,Re,[a[15]||=u(`thead`,null,[u(`tr`,null,[u(`th`,null,`Bot`),u(`th`,null,`模型`),u(`th`,null,`输入`),u(`th`,null,`输出`),u(`th`,null,`缓存读`),u(`th`,null,`缓存写`),u(`th`,null,`推理`),u(`th`,null,`合计`),u(`th`,null,`次数`),u(`th`,null,`最近使用`)])],-1),u(`tbody`,null,[(n(!0),p(c,null,i(E.value.rows,e=>(n(),p(`tr`,{key:e.agentId+e.provider+e.model,class:t({deleted:e.deleted})},[u(`td`,null,[s(o(e.name)+` `,1),e.deleted?(n(),p(`em`,Y,`已删除`)):m(``,!0)]),u(`td`,{title:e.provider},o(e.model),9,ze),u(`td`,null,o(N(e.input)),1),u(`td`,null,o(N(e.output)),1),u(`td`,null,o(N(e.cacheRead)),1),u(`td`,null,o(N(e.cacheWrite)),1),u(`td`,null,o(N(e.reasoning)),1),u(`td`,null,o(I(e)?`≈ `:``)+o(N(e.total)),1),u(`td`,null,[s(o(N(e.calls)),1),e.estimatedCalls?(n(),p(`span`,Be,` (估算 `+o(N(e.estimatedCalls))+`)`,1)):m(``,!0)]),u(`td`,null,o(L(e.lastAt)),1)],2))),128))])])])):(n(),p(`p`,Ie,`这个时间范围内还没有记录。`)),V.value>1?(n(),p(`div`,Ve,[u(`button`,{disabled:j.value===0||O.value,onClick:a[1]||=e=>Q(-1)},`上一页`,8,He),u(`span`,null,o(j.value+1)+` / `+o(V.value),1),u(`button`,{disabled:j.value>=V.value-1||O.value,onClick:a[2]||=e=>Q(1)},`下一页`,8,Ue)])):m(``,!0)]),a[17]||=u(`p`,{class:`usage-footer`},`不含辅助模型调用(标题、压缩、复盘、记忆提炼等)。已删除 Bot 的用量会保留。日期按 Host 本机时区划分。`,-1)],64)):m(``,!0)])]))}}),[[`__scopeId`,`data-v-61e5b2cf`]]);export{Z as default};
3
+ //# sourceMappingURL=UsageView-ZsSbQzIY.js.map