@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":"UsageView-BhiRCw7p.js","names":[],"sources":["../../src/client/utils/usageActivity.ts","../../src/client/components/workspace/UsageActivityHeatmap.vue","../../src/client/components/workspace/UsageActivityHeatmap.vue","../../src/client/views/UsageView.vue","../../src/client/views/UsageView.vue"],"sourcesContent":["/** Calendar heatmap helpers for Token activity (Monday-first, host-local date keys YYYY-MM-DD). */\n\nexport type UsageActivityMode = 'daily' | 'weekly' | 'cumulative'\nexport type UsageActivityDay = { date: string; total: number }\n\nexport const usageActivityModes: Array<{ id: UsageActivityMode; label: string }> = [\n { id: 'daily', label: '每天' },\n { id: 'weekly', label: '每周' },\n { id: 'cumulative', label: '累计总量' },\n]\n\nexport const usageDateKey = (at: Date): string =>\n `${at.getFullYear()}-${String(at.getMonth() + 1).padStart(2, '0')}-${String(at.getDate()).padStart(2, '0')}`\n\n/** Parse a YYYY-MM-DD key as local midnight. */\nexport function parseUsageDate(date: string): Date {\n const [y, m, d] = date.split('-').map(Number)\n return new Date(y!, m! - 1, d!)\n}\n\n/** Chinese day label like 7月7日. */\nexport function formatUsageActivityDate(date: string): string {\n const d = parseUsageDate(date)\n return `${d.getMonth() + 1}月${d.getDate()}日`\n}\n\n/** Compact Chinese count for tooltips: 14.6万 / 1.2亿 / 1,234. */\nexport function formatUsageActivityTokens(n: number): string {\n const trim = (value: number) => value.toFixed(1).replace(/\\.0$/, '')\n if (n >= 1e8) return `${trim(n / 1e8)}亿`\n if (n >= 1e4) return `${trim(n / 1e4)}万`\n return Math.round(n).toLocaleString('zh-CN')\n}\n\nexport function usageActivityTooltip(date: string, tokens: number): string {\n return `${formatUsageActivityDate(date)}\\n${formatUsageActivityTokens(tokens)} 个 Token`\n}\n\n/** Monday-first week index: Mon=0 … Sun=6. */\nexport const mondayIndex = (day: Date): number => (day.getDay() + 6) % 7\n\n/**\n * Build the last ~12 months as a Monday-aligned week grid ending on `now`.\n * Missing days are filled with total 0 so empty cells stay visible.\n */\nexport function buildUsageActivityWindow(byDay: readonly UsageActivityDay[], now = new Date()): UsageActivityDay[] {\n const totals = new Map(byDay.map(d => [d.date, d.total]))\n const end = new Date(now.getFullYear(), now.getMonth(), now.getDate())\n const start = new Date(end)\n start.setDate(start.getDate() - 364)\n start.setDate(start.getDate() - mondayIndex(start))\n const days: UsageActivityDay[] = []\n for (const cursor = new Date(start); cursor <= end; cursor.setDate(cursor.getDate() + 1)) {\n const key = usageDateKey(cursor)\n days.push({ date: key, total: totals.get(key) ?? 0 })\n }\n return days\n}\n\nexport interface UsageActivityCell {\n date: string\n /** Value used for colouring in the active mode. */\n value: number\n /** Day total (always the raw daily amount). */\n dayTotal: number\n /** Week total containing this day (for weekly tooltips). */\n weekTotal: number\n /** Running sum from the window start through this day. */\n cumulative: number\n level: 0 | 1 | 2 | 3 | 4\n week: number\n weekday: number\n}\n\nfunction levelFor(value: number, max: number): 0 | 1 | 2 | 3 | 4 {\n if (value <= 0 || max <= 0) return 0\n const ratio = value / max\n if (ratio <= 0.25) return 1\n if (ratio <= 0.5) return 2\n if (ratio <= 0.75) return 3\n return 4\n}\n\n/** Compute cell values and colour levels for the selected mode. */\nexport function buildUsageActivityCells(days: readonly UsageActivityDay[], mode: UsageActivityMode): UsageActivityCell[] {\n const weekTotals = new Map<number, number>()\n let running = 0\n const prepared = days.map((day, index) => {\n const week = Math.floor(index / 7)\n weekTotals.set(week, (weekTotals.get(week) ?? 0) + day.total)\n running += day.total\n return { ...day, week, weekday: index % 7, cumulative: running, weekTotal: 0 }\n })\n for (const cell of prepared) cell.weekTotal = weekTotals.get(cell.week) ?? 0\n const values = prepared.map(cell => mode === 'daily' ? cell.total : mode === 'weekly' ? cell.weekTotal : cell.cumulative)\n const max = Math.max(0, ...values)\n return prepared.map((cell, i) => ({\n date: cell.date,\n dayTotal: cell.total,\n weekTotal: cell.weekTotal,\n cumulative: cell.cumulative,\n value: values[i]!,\n level: levelFor(values[i]!, max),\n week: cell.week,\n weekday: cell.weekday,\n }))\n}\n\nexport interface UsageActivityMonthLabel { week: number; label: string }\n\n/** Place a Chinese month label on the first week that contains day 1 of that month. */\nexport function usageActivityMonthLabels(cells: readonly UsageActivityCell[]): UsageActivityMonthLabel[] {\n const labels: UsageActivityMonthLabel[] = []\n let last = -1\n for (const cell of cells) {\n if (!cell.date.endsWith('-01')) continue\n if (cell.week === last) continue\n last = cell.week\n labels.push({ week: cell.week, label: `${parseUsageDate(cell.date).getMonth() + 1}月` })\n }\n return labels\n}\n\nexport function usageActivityTooltipFor(cell: UsageActivityCell, mode: UsageActivityMode): string {\n const tokens = mode === 'daily' ? cell.dayTotal : mode === 'weekly' ? cell.weekTotal : cell.cumulative\n if (mode === 'weekly') {\n return `${formatUsageActivityDate(cell.date)} 所在周\\n${formatUsageActivityTokens(tokens)} 个 Token`\n }\n if (mode === 'cumulative') {\n return `${formatUsageActivityDate(cell.date)}\\n累计 ${formatUsageActivityTokens(tokens)} 个 Token`\n }\n return usageActivityTooltip(cell.date, tokens)\n}\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport {\n buildUsageActivityCells, buildUsageActivityWindow, usageActivityModes, usageActivityMonthLabels,\n usageActivityTooltipFor, type UsageActivityDay, type UsageActivityMode,\n} from '@/utils/usageActivity'\n\nconst props = defineProps<{ days: UsageActivityDay[]; now?: number }>()\nconst mode = ref<UsageActivityMode>('daily')\nconst tip = ref<{ text: string; x: number; y: number } | null>(null)\nconst windowDays = computed(() => buildUsageActivityWindow(props.days, props.now ? new Date(props.now) : new Date()))\nconst cells = computed(() => buildUsageActivityCells(windowDays.value, mode.value))\nconst months = computed(() => usageActivityMonthLabels(cells.value))\nconst weekCount = computed(() => (cells.value.length ? cells.value[cells.value.length - 1]!.week + 1 : 0))\nconst hasAny = computed(() => windowDays.value.some(d => d.total > 0))\n\nfunction showTip(event: MouseEvent | FocusEvent, index: number) {\n const cell = cells.value[index]\n if (!cell) return\n const host = (event.currentTarget as HTMLElement).closest('.activity-heatmap') as HTMLElement | null\n const rect = host?.getBoundingClientRect()\n const target = (event.currentTarget as HTMLElement).getBoundingClientRect()\n const x = 'clientX' in event ? event.clientX - (rect?.left ?? 0) : target.left + target.width / 2 - (rect?.left ?? 0)\n const y = 'clientY' in event ? event.clientY - (rect?.top ?? 0) : target.top - (rect?.top ?? 0)\n tip.value = { text: usageActivityTooltipFor(cell, mode.value), x, y }\n}\nfunction hideTip() { tip.value = null }\n</script>\n\n<template>\n <section class=\"activity-heatmap\" aria-label=\"Token 活动\">\n <header class=\"activity-header\">\n <h2>Token 活动</h2>\n <div class=\"activity-modes\" role=\"tablist\" aria-label=\"活动视图\">\n <button v-for=\"item in usageActivityModes\" :key=\"item.id\" type=\"button\" role=\"tab\" :aria-selected=\"mode === item.id\" @click=\"mode = item.id\">{{ item.label }}</button>\n </div>\n </header>\n <p v-if=\"!hasAny\" class=\"activity-empty\">近 12 个月还没有 Token 记录。开始对话后,这里会显示每天的用量。</p>\n <div v-else class=\"activity-body\">\n <div class=\"activity-weekdays\" aria-hidden=\"true\"><span>一</span><span>三</span><span>五</span></div>\n <div class=\"activity-scroll\">\n <div class=\"activity-months\" :style=\"{ gridTemplateColumns: `repeat(${weekCount}, minmax(11px, 1fr))` }\">\n <span v-for=\"m in months\" :key=\"m.week + m.label\" class=\"activity-month\" :style=\"{ gridColumn: m.week + 1 }\">{{ m.label }}</span>\n </div>\n <div\n class=\"activity-grid\"\n role=\"img\"\n :aria-label=\"`近 12 个月 Token 活动,${mode === 'daily' ? '按天' : mode === 'weekly' ? '按周' : '累计'}着色`\"\n :style=\"{ gridTemplateColumns: `repeat(${weekCount}, minmax(11px, 1fr))` }\"\n >\n <button\n v-for=\"(cell, index) in cells\"\n :key=\"cell.date\"\n type=\"button\"\n class=\"activity-cell\"\n :class=\"`level-${cell.level}`\"\n :style=\"{ gridColumn: cell.week + 1, gridRow: cell.weekday + 1 }\"\n :aria-label=\"usageActivityTooltipFor(cell, mode).replace('\\n', ',')\"\n @mouseenter=\"showTip($event, index)\"\n @mousemove=\"showTip($event, index)\"\n @mouseleave=\"hideTip\"\n @focus=\"showTip($event, index)\"\n @blur=\"hideTip\"\n />\n </div>\n </div>\n <div class=\"activity-legend\" aria-hidden=\"true\"><span>少</span><i class=\"level-0\" /><i class=\"level-1\" /><i class=\"level-2\" /><i class=\"level-3\" /><i class=\"level-4\" /><span>多</span></div>\n <div v-if=\"tip\" class=\"activity-tip\" role=\"tooltip\" :style=\"{ left: tip.x + 'px', top: tip.y + 'px' }\">{{ tip.text }}</div>\n </div>\n </section>\n</template>\n\n<style scoped>\n.activity-heatmap{position:relative;background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:16px;min-width:0}\n.activity-header{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:12px}\n.activity-header h2{margin:0;font-size:15px}\n.activity-modes{display:flex;gap:14px}\n.activity-modes button{border:0;background:transparent;color:var(--text-secondary);padding:0;min-height:auto;font:inherit;cursor:pointer}\n.activity-modes button[aria-selected=true]{color:var(--text-primary);font-weight:600}\n.activity-empty{margin:0;color:var(--text-secondary);font-size:13px}\n.activity-body{display:grid;grid-template-columns:18px minmax(0,1fr);grid-template-rows:auto auto;column-gap:8px;row-gap:8px;align-items:start}\n.activity-weekdays{display:grid;grid-template-rows:repeat(7,11px);gap:3px;padding-top:18px;font-size:10px;color:var(--text-muted);line-height:11px}\n.activity-weekdays span:nth-child(1){grid-row:1}.activity-weekdays span:nth-child(2){grid-row:3}.activity-weekdays span:nth-child(3){grid-row:5}\n.activity-scroll{grid-column:2;overflow-x:auto;min-width:0}\n.activity-months{display:grid;gap:3px;height:16px;margin-bottom:2px;font-size:11px;color:var(--text-secondary)}\n.activity-month{white-space:nowrap}\n.activity-grid{display:grid;grid-template-rows:repeat(7,11px);gap:3px;min-width:max-content}\n.activity-cell{width:100%;min-width:11px;height:11px;padding:0;border:0;border-radius:3px;cursor:default;background:var(--surface-soft);box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--line) 70%, transparent)}\n.activity-cell:focus-visible{outline:2px solid var(--workflow-accent);outline-offset:1px}\n.activity-cell.level-1,.activity-legend i.level-1{background:color-mix(in srgb, var(--workflow-accent) 22%, var(--surface-soft))}\n.activity-cell.level-2,.activity-legend i.level-2{background:color-mix(in srgb, var(--workflow-accent) 45%, var(--surface-soft))}\n.activity-cell.level-3,.activity-legend i.level-3{background:color-mix(in srgb, var(--workflow-accent) 70%, var(--surface-soft))}\n.activity-cell.level-4,.activity-legend i.level-4{background:var(--workflow-accent)}\n.activity-legend{grid-column:2;display:flex;align-items:center;justify-content:flex-end;gap:4px;font-size:11px;color:var(--text-muted)}\n.activity-legend i{display:block;width:11px;height:11px;border-radius:3px;background:var(--surface-soft);box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--line) 70%, transparent)}\n.activity-tip{position:absolute;z-index:5;transform:translate(-50%,calc(-100% - 10px));padding:8px 10px;border-radius:8px;background:var(--text-primary);color:var(--text-on-solid);font-size:12px;line-height:1.35;white-space:pre-line;pointer-events:none;box-shadow:var(--shadow-float);max-width:220px}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport {\n buildUsageActivityCells, buildUsageActivityWindow, usageActivityModes, usageActivityMonthLabels,\n usageActivityTooltipFor, type UsageActivityDay, type UsageActivityMode,\n} from '@/utils/usageActivity'\n\nconst props = defineProps<{ days: UsageActivityDay[]; now?: number }>()\nconst mode = ref<UsageActivityMode>('daily')\nconst tip = ref<{ text: string; x: number; y: number } | null>(null)\nconst windowDays = computed(() => buildUsageActivityWindow(props.days, props.now ? new Date(props.now) : new Date()))\nconst cells = computed(() => buildUsageActivityCells(windowDays.value, mode.value))\nconst months = computed(() => usageActivityMonthLabels(cells.value))\nconst weekCount = computed(() => (cells.value.length ? cells.value[cells.value.length - 1]!.week + 1 : 0))\nconst hasAny = computed(() => windowDays.value.some(d => d.total > 0))\n\nfunction showTip(event: MouseEvent | FocusEvent, index: number) {\n const cell = cells.value[index]\n if (!cell) return\n const host = (event.currentTarget as HTMLElement).closest('.activity-heatmap') as HTMLElement | null\n const rect = host?.getBoundingClientRect()\n const target = (event.currentTarget as HTMLElement).getBoundingClientRect()\n const x = 'clientX' in event ? event.clientX - (rect?.left ?? 0) : target.left + target.width / 2 - (rect?.left ?? 0)\n const y = 'clientY' in event ? event.clientY - (rect?.top ?? 0) : target.top - (rect?.top ?? 0)\n tip.value = { text: usageActivityTooltipFor(cell, mode.value), x, y }\n}\nfunction hideTip() { tip.value = null }\n</script>\n\n<template>\n <section class=\"activity-heatmap\" aria-label=\"Token 活动\">\n <header class=\"activity-header\">\n <h2>Token 活动</h2>\n <div class=\"activity-modes\" role=\"tablist\" aria-label=\"活动视图\">\n <button v-for=\"item in usageActivityModes\" :key=\"item.id\" type=\"button\" role=\"tab\" :aria-selected=\"mode === item.id\" @click=\"mode = item.id\">{{ item.label }}</button>\n </div>\n </header>\n <p v-if=\"!hasAny\" class=\"activity-empty\">近 12 个月还没有 Token 记录。开始对话后,这里会显示每天的用量。</p>\n <div v-else class=\"activity-body\">\n <div class=\"activity-weekdays\" aria-hidden=\"true\"><span>一</span><span>三</span><span>五</span></div>\n <div class=\"activity-scroll\">\n <div class=\"activity-months\" :style=\"{ gridTemplateColumns: `repeat(${weekCount}, minmax(11px, 1fr))` }\">\n <span v-for=\"m in months\" :key=\"m.week + m.label\" class=\"activity-month\" :style=\"{ gridColumn: m.week + 1 }\">{{ m.label }}</span>\n </div>\n <div\n class=\"activity-grid\"\n role=\"img\"\n :aria-label=\"`近 12 个月 Token 活动,${mode === 'daily' ? '按天' : mode === 'weekly' ? '按周' : '累计'}着色`\"\n :style=\"{ gridTemplateColumns: `repeat(${weekCount}, minmax(11px, 1fr))` }\"\n >\n <button\n v-for=\"(cell, index) in cells\"\n :key=\"cell.date\"\n type=\"button\"\n class=\"activity-cell\"\n :class=\"`level-${cell.level}`\"\n :style=\"{ gridColumn: cell.week + 1, gridRow: cell.weekday + 1 }\"\n :aria-label=\"usageActivityTooltipFor(cell, mode).replace('\\n', ',')\"\n @mouseenter=\"showTip($event, index)\"\n @mousemove=\"showTip($event, index)\"\n @mouseleave=\"hideTip\"\n @focus=\"showTip($event, index)\"\n @blur=\"hideTip\"\n />\n </div>\n </div>\n <div class=\"activity-legend\" aria-hidden=\"true\"><span>少</span><i class=\"level-0\" /><i class=\"level-1\" /><i class=\"level-2\" /><i class=\"level-3\" /><i class=\"level-4\" /><span>多</span></div>\n <div v-if=\"tip\" class=\"activity-tip\" role=\"tooltip\" :style=\"{ left: tip.x + 'px', top: tip.y + 'px' }\">{{ tip.text }}</div>\n </div>\n </section>\n</template>\n\n<style scoped>\n.activity-heatmap{position:relative;background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:16px;min-width:0}\n.activity-header{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:12px}\n.activity-header h2{margin:0;font-size:15px}\n.activity-modes{display:flex;gap:14px}\n.activity-modes button{border:0;background:transparent;color:var(--text-secondary);padding:0;min-height:auto;font:inherit;cursor:pointer}\n.activity-modes button[aria-selected=true]{color:var(--text-primary);font-weight:600}\n.activity-empty{margin:0;color:var(--text-secondary);font-size:13px}\n.activity-body{display:grid;grid-template-columns:18px minmax(0,1fr);grid-template-rows:auto auto;column-gap:8px;row-gap:8px;align-items:start}\n.activity-weekdays{display:grid;grid-template-rows:repeat(7,11px);gap:3px;padding-top:18px;font-size:10px;color:var(--text-muted);line-height:11px}\n.activity-weekdays span:nth-child(1){grid-row:1}.activity-weekdays span:nth-child(2){grid-row:3}.activity-weekdays span:nth-child(3){grid-row:5}\n.activity-scroll{grid-column:2;overflow-x:auto;min-width:0}\n.activity-months{display:grid;gap:3px;height:16px;margin-bottom:2px;font-size:11px;color:var(--text-secondary)}\n.activity-month{white-space:nowrap}\n.activity-grid{display:grid;grid-template-rows:repeat(7,11px);gap:3px;min-width:max-content}\n.activity-cell{width:100%;min-width:11px;height:11px;padding:0;border:0;border-radius:3px;cursor:default;background:var(--surface-soft);box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--line) 70%, transparent)}\n.activity-cell:focus-visible{outline:2px solid var(--workflow-accent);outline-offset:1px}\n.activity-cell.level-1,.activity-legend i.level-1{background:color-mix(in srgb, var(--workflow-accent) 22%, var(--surface-soft))}\n.activity-cell.level-2,.activity-legend i.level-2{background:color-mix(in srgb, var(--workflow-accent) 45%, var(--surface-soft))}\n.activity-cell.level-3,.activity-legend i.level-3{background:color-mix(in srgb, var(--workflow-accent) 70%, var(--surface-soft))}\n.activity-cell.level-4,.activity-legend i.level-4{background:var(--workflow-accent)}\n.activity-legend{grid-column:2;display:flex;align-items:center;justify-content:flex-end;gap:4px;font-size:11px;color:var(--text-muted)}\n.activity-legend i{display:block;width:11px;height:11px;border-radius:3px;background:var(--surface-soft);box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--line) 70%, transparent)}\n.activity-tip{position:absolute;z-index:5;transform:translate(-50%,calc(-100% - 10px));padding:8px 10px;border-radius:8px;background:var(--text-primary);color:var(--text-on-solid);font-size:12px;line-height:1.35;white-space:pre-line;pointer-events:none;box-shadow:var(--shadow-float);max-width:220px}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref, watch } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport { apiRequest } from '@/api/client'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport UsageActivityHeatmap from '@/components/workspace/UsageActivityHeatmap.vue'\nimport type { WorkspaceUsageAgentRow, WorkspaceUsageDayRow, WorkspaceUsageDetails, WorkspaceUsageOverview, WorkspaceUsageRange, WorkspaceUsageTotals } from '@shared/workspace'\n\nconst router = useRouter(), route = useRoute()\nconst ranges: Array<{ id: WorkspaceUsageRange; label: string }> = [{ id: 'today', label: '今天' }, { id: '7d', label: '7 天' }, { id: '30d', label: '30 天' }, { id: 'all', label: '全部' }]\nconst range = ref<WorkspaceUsageRange>('7d')\nconst overview = ref<WorkspaceUsageOverview | null>(null), details = ref<WorkspaceUsageDetails | null>(null)\nconst calendarDays = ref<Array<Pick<WorkspaceUsageDayRow, 'date' | 'total'>>>([])\nconst loading = ref(true), error = ref(''), exporting = ref(false), page = ref(0)\nconst pageSize = 20\nconst backPath = computed(() => typeof route.query.from === 'string' && /^\\/conversations(?:\\/[a-f0-9-]{36})?(?:\\?taskId=[a-f0-9-]{36})?$/i.test(route.query.from) ? route.query.from : '/conversations')\nconst fmt = (n: number) => n.toLocaleString('zh-CN')\nconst compact = (n: number) => n >= 1e9 ? `${(n / 1e9).toFixed(2)}B` : n >= 1e6 ? `${(n / 1e6).toFixed(2)}M` : n >= 1e4 ? `${(n / 1e3).toFixed(1)}K` : fmt(n)\nconst share = (value: number, total: number) => total > 0 ? Math.round(value / total * 1000) / 10 : 0\nconst estimated = (t: WorkspaceUsageTotals) => t.estimatedCalls > 0\nconst when = (at: number) => new Date(at).toLocaleString('zh-CN', { hour12: false })\nconst liveAgents = computed(() => overview.value?.byAgent.filter(a => !a.deleted) ?? [])\nconst deletedAgents = computed(() => overview.value?.byAgent.filter(a => a.deleted) ?? [])\nconst maxDay = computed(() => Math.max(1, ...(overview.value?.byDay.map(d => d.total) ?? [0])))\nconst pages = computed(() => Math.max(1, Math.ceil((details.value?.total ?? 0) / pageSize)))\nconst sinceLabel = computed(() => overview.value?.firstRecordedAt ? `统计自 ${when(overview.value.firstRecordedAt)} 起` : '尚无记录')\n\nlet generation = 0\nasync function load() {\n const current = ++generation\n loading.value = true\n try {\n const query = `range=${range.value}`\n const [o, d, calendar] = await Promise.all([\n apiRequest<WorkspaceUsageOverview>(`/api/app/usage/overview?${query}`),\n apiRequest<WorkspaceUsageDetails>(`/api/app/usage/details?${query}&limit=${pageSize}&offset=${page.value * pageSize}`),\n // Heatmap always looks at the last ~12 months; ignore the page range filter.\n apiRequest<WorkspaceUsageOverview>('/api/app/usage/overview?range=all'),\n ])\n if (current !== generation) return\n overview.value = o; details.value = d\n calendarDays.value = calendar.byDay.map(day => ({ date: day.date, total: day.total }))\n error.value = ''\n } catch (e) { if (current === generation) error.value = e instanceof Error ? e.message : '无法读取 Token 用量' }\n finally { if (current === generation) loading.value = false }\n}\nwatch(range, () => { page.value = 0; void load() })\nfunction turn(delta: number) { page.value = Math.min(pages.value - 1, Math.max(0, page.value + delta)); void load() }\nasync function exportCsv() {\n exporting.value = true\n try {\n const text = await apiRequest<string>(`/api/app/usage/details?range=${range.value}&format=csv`)\n const url = URL.createObjectURL(new Blob([text], { type: 'text/csv;charset=utf-8' }))\n const link = document.createElement('a')\n link.href = url; link.download = `yaovi-token-usage-${range.value}.csv`; link.click()\n window.setTimeout(() => URL.revokeObjectURL(url), 1000)\n } catch (e) { error.value = e instanceof Error ? e.message : '导出失败' }\n finally { exporting.value = false }\n}\nconst agentLine = (a: WorkspaceUsageAgentRow) => `输入 ${fmt(a.input)} · 输出 ${fmt(a.output)}`\nonMounted(load)\n</script>\n\n<template>\n <main class=\"usage-page\" aria-label=\"Token 用量总览\">\n <header class=\"usage-header\">\n <button class=\"icon-button\" type=\"button\" aria-label=\"返回聊天\" @click=\"router.push(backPath)\"><AppIcon name=\"chevron-left\" :size=\"20\" /></button>\n <AppIcon name=\"history\" :size=\"23\" /><h1>Token 用量</h1>\n <div class=\"segmented\" role=\"tablist\" aria-label=\"时间范围\"><button v-for=\"item in ranges\" :key=\"item.id\" role=\"tab\" :aria-selected=\"range === item.id\" @click=\"range = item.id\">{{ item.label }}</button></div>\n <button class=\"usage-action\" type=\"button\" :disabled=\"exporting || !overview?.totals.calls\" @click=\"exportCsv\">{{ exporting ? '导出中…' : '导出 CSV' }}</button>\n <button class=\"icon-button\" type=\"button\" aria-label=\"刷新用量\" @click=\"load\"><AppIcon name=\"refresh\" :size=\"17\" /></button>\n </header>\n <div class=\"usage-body\">\n <p v-if=\"error\" class=\"usage-error\" role=\"alert\">{{ error }}</p>\n <p v-if=\"loading && !overview\" class=\"usage-note\" role=\"status\">正在读取 Token 用量…</p>\n <template v-if=\"overview\">\n <p class=\"usage-note\">{{ sinceLabel }}。合计 = 输入 + 输出;缓存和推理 Token 单独列出,不重复计入。</p>\n <section class=\"usage-cards\" aria-label=\"总量\">\n <div class=\"usage-card\"><small>合计</small><strong>{{ compact(overview.totals.total) }}</strong><span>{{ estimated(overview.totals) ? '≈ ' : '' }}{{ fmt(overview.totals.calls) }} 次调用</span></div>\n <div class=\"usage-card\"><small>输入</small><strong>{{ compact(overview.totals.input) }}</strong><span>缓存读 {{ fmt(overview.totals.cacheRead) }} · 缓存写 {{ fmt(overview.totals.cacheWrite) }}</span></div>\n <div class=\"usage-card\"><small>输出</small><strong>{{ compact(overview.totals.output) }}</strong><span>推理 {{ fmt(overview.totals.reasoning) }}</span></div>\n <div class=\"usage-card\"><small>已删除 Bot</small><strong>{{ compact(overview.deletedTotals.total) }}</strong><span>{{ share(overview.deletedTotals.total, overview.totals.total) }}% 占比</span></div>\n </section>\n <p v-if=\"overview.totals.estimatedCalls\" class=\"usage-note estimated-note\">其中 {{ fmt(overview.totals.estimatedCalls) }} 次为本机估算值(供应商未返回用量),标记 ≈。</p>\n\n <UsageActivityHeatmap :days=\"calendarDays\" />\n\n <div class=\"usage-grid\">\n <section class=\"usage-panel\" aria-label=\"按模型\">\n <h2>按模型</h2>\n <p v-if=\"!overview.byModel.length\" class=\"usage-empty\">这个时间范围内还没有记录。</p>\n <ul v-else class=\"share-list\">\n <li v-for=\"m in overview.byModel\" :key=\"m.provider + m.model\">\n <div class=\"share-head\"><strong :title=\"m.provider\">{{ m.model }}</strong><span>{{ estimated(m) ? '≈ ' : '' }}{{ fmt(m.total) }} · {{ share(m.total, overview.totals.total) }}%</span></div>\n <div class=\"share-bar\"><i :style=\"{ width: share(m.total, overview.totals.total) + '%' }\" /></div>\n <small>{{ m.provider }} · 输入 {{ fmt(m.input) }} · 输出 {{ fmt(m.output) }} · {{ fmt(m.calls) }} 次</small>\n </li>\n </ul>\n </section>\n\n <section class=\"usage-panel\" aria-label=\"按 Bot\">\n <h2>按 Bot</h2>\n <p v-if=\"!overview.byAgent.length\" class=\"usage-empty\">这个时间范围内还没有记录。</p>\n <template v-else>\n <ul class=\"share-list\">\n <li v-for=\"a in liveAgents\" :key=\"a.agentId\">\n <div class=\"share-head\"><strong>{{ a.name }}</strong><span>{{ estimated(a) ? '≈ ' : '' }}{{ fmt(a.total) }} · {{ share(a.total, overview.totals.total) }}%</span></div>\n <div class=\"share-bar\"><i :style=\"{ width: share(a.total, overview.totals.total) + '%' }\" /></div>\n <small>{{ agentLine(a) }} · {{ fmt(a.calls) }} 次</small>\n </li>\n </ul>\n <template v-if=\"deletedAgents.length\">\n <h3 class=\"deleted-title\">已删除的 Bot <span>{{ fmt(overview.deletedTotals.total) }}</span></h3>\n <ul class=\"share-list deleted\">\n <li v-for=\"a in deletedAgents\" :key=\"a.agentId\">\n <div class=\"share-head\"><strong>{{ a.name }} <em class=\"badge\">已删除</em></strong><span>{{ estimated(a) ? '≈ ' : '' }}{{ fmt(a.total) }} · {{ share(a.total, overview.totals.total) }}%</span></div>\n <div class=\"share-bar\"><i :style=\"{ width: share(a.total, overview.totals.total) + '%' }\" /></div>\n <small>{{ agentLine(a) }} · {{ fmt(a.calls) }} 次</small>\n </li>\n </ul>\n </template>\n </template>\n </section>\n </div>\n\n <section class=\"usage-panel\" aria-label=\"按天趋势\">\n <h2>按天趋势</h2>\n <p v-if=\"!overview.byDay.length\" class=\"usage-empty\">这个时间范围内还没有记录。</p>\n <div v-else class=\"trend\" role=\"img\" :aria-label=\"`按天 Token 用量,共 ${overview.byDay.length} 天`\">\n <div v-for=\"day in overview.byDay\" :key=\"day.date\" class=\"trend-col\" :title=\"`${day.date}:${fmt(day.total)}(输入 ${fmt(day.input)},输出 ${fmt(day.output)})`\">\n <i :style=\"{ height: Math.max(2, day.total / maxDay * 100) + '%' }\" /><small>{{ day.date.slice(5) }}</small>\n </div>\n </div>\n </section>\n\n <section v-if=\"overview.historical\" class=\"usage-panel\" aria-label=\"历史(未分模型)\">\n <h2>历史(未分模型)</h2>\n <p class=\"usage-note\">{{ overview.historical.before }} 之前的旧统计,只有每个 Bot 的输入和输出,没有模型信息,也没有计入上面的总量。合计 {{ fmt(overview.historical.totals.total) }}(输入 {{ fmt(overview.historical.totals.input) }} · 输出 {{ fmt(overview.historical.totals.output) }})。</p>\n <table class=\"usage-table\"><thead><tr><th>Bot</th><th>输入</th><th>输出</th><th>合计</th></tr></thead>\n <tbody><tr v-for=\"a in overview.historical.byAgent\" :key=\"a.agentId\"><td>{{ a.name }} <em v-if=\"a.deleted\" class=\"badge\">已删除</em></td><td>{{ fmt(a.input) }}</td><td>{{ fmt(a.output) }}</td><td>{{ fmt(a.total) }}</td></tr></tbody></table>\n </section>\n\n <section class=\"usage-panel\" aria-label=\"Bot 与模型明细\">\n <h2>明细:Bot × 模型</h2>\n <p v-if=\"!details?.rows.length\" class=\"usage-empty\">这个时间范围内还没有记录。</p>\n <div v-else class=\"table-scroll\"><table class=\"usage-table\">\n <thead><tr><th>Bot</th><th>模型</th><th>输入</th><th>输出</th><th>缓存读</th><th>缓存写</th><th>推理</th><th>合计</th><th>次数</th><th>最近使用</th></tr></thead>\n <tbody><tr v-for=\"r in details.rows\" :key=\"r.agentId + r.provider + r.model\" :class=\"{ deleted: r.deleted }\">\n <td>{{ r.name }} <em v-if=\"r.deleted\" class=\"badge\">已删除</em></td><td :title=\"r.provider\">{{ r.model }}</td>\n <td>{{ fmt(r.input) }}</td><td>{{ fmt(r.output) }}</td><td>{{ fmt(r.cacheRead) }}</td><td>{{ fmt(r.cacheWrite) }}</td><td>{{ fmt(r.reasoning) }}</td>\n <td>{{ estimated(r) ? '≈ ' : '' }}{{ fmt(r.total) }}</td><td>{{ fmt(r.calls) }}<span v-if=\"r.estimatedCalls\" class=\"est\"> (估算 {{ fmt(r.estimatedCalls) }})</span></td><td>{{ when(r.lastAt) }}</td>\n </tr></tbody></table></div>\n <div v-if=\"pages > 1\" class=\"pager\"><button :disabled=\"page === 0 || loading\" @click=\"turn(-1)\">上一页</button><span>{{ page + 1 }} / {{ pages }}</span><button :disabled=\"page >= pages - 1 || loading\" @click=\"turn(1)\">下一页</button></div>\n </section>\n <p class=\"usage-footer\">不含辅助模型调用(标题、压缩、复盘、记忆提炼等)。已删除 Bot 的用量会保留。日期按 Host 本机时区划分。</p>\n </template>\n </div>\n </main>\n</template>\n\n<style scoped>\n.usage-page{height:100dvh;display:flex;flex-direction:column;background:var(--canvas);color:var(--text-primary);min-width:0}\n.usage-header{display:flex;align-items:center;gap:12px;min-height:72px;box-sizing:border-box;padding:12px 24px;border-bottom:1px solid var(--line);background:var(--surface);flex-wrap:wrap}\nh1{font-size:20px;margin:0 20px 0 0;letter-spacing:-.02em}\nh2{font-size:15px;margin:0 0 12px}\n.usage-header button,.pager button{border:1px solid var(--line);background:var(--surface);color:var(--text-primary);border-radius:10px;min-height:36px;padding:0 14px;cursor:pointer}\n.usage-header button:disabled,.pager button:disabled{opacity:.5;cursor:default}\n.icon-button{padding:8px!important;width:40px;flex-shrink:0}\n.segmented{display:flex;gap:3px;border:1px solid var(--line);border-radius:10px;padding:3px;background:var(--surface);margin-left:auto}\n.segmented button{min-height:32px;border:0!important;background:transparent!important;white-space:nowrap}\n.segmented button[aria-selected=true]{background:var(--surface-soft)!important;font-weight:600}\n.usage-action{background:var(--accent)!important;color:var(--text-on-solid)!important;border-color:transparent!important}\n.usage-body{flex:1;overflow:auto;padding:20px 24px 32px;display:flex;flex-direction:column;gap:16px}\n.usage-note,.usage-footer,.usage-empty,small{color:var(--text-secondary);font-size:13px;margin:0}\n.estimated-note{color:var(--text-muted)}\n.usage-error{color:var(--danger);margin:0}\n.usage-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px}\n.usage-card,.usage-panel{background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:16px;min-width:0}\n.usage-card{display:flex;flex-direction:column;gap:4px}.usage-card strong{font-size:26px;letter-spacing:-.02em}.usage-card span{color:var(--text-secondary);font-size:12px}\n.usage-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(340px,1fr));gap:16px}\n.share-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}\n.share-head{display:flex;justify-content:space-between;gap:12px;font-size:14px}.share-head strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.share-head span{color:var(--text-secondary);white-space:nowrap}\n.share-bar{height:6px;border-radius:3px;background:var(--surface-soft);margin:6px 0 4px;overflow:hidden}.share-bar i{display:block;height:100%;background:var(--accent);border-radius:3px}\n.deleted-title{font-size:13px;margin:16px 0 8px;color:var(--text-secondary);display:flex;justify-content:space-between}\n.deleted .share-bar i{background:var(--text-muted)}\n.badge{font-style:normal;font-size:11px;border:1px solid var(--line);border-radius:6px;padding:1px 6px;color:var(--text-secondary);margin-left:4px}\n.trend{display:flex;align-items:flex-end;gap:4px;height:140px;overflow-x:auto;padding-bottom:2px}\n.trend-col{flex:1 0 22px;max-width:48px;height:100%;display:flex;flex-direction:column;justify-content:flex-end;align-items:center;gap:4px}\n.trend-col i{display:block;width:100%;background:var(--accent);border-radius:4px 4px 0 0;min-height:2px}.trend-col small{font-size:10px}\n.table-scroll{overflow-x:auto}\n.usage-table{width:100%;border-collapse:collapse;font-size:13px}\n.usage-table th,.usage-table td{text-align:left;padding:8px 10px;border-bottom:1px solid var(--line);white-space:nowrap;font-variant-numeric:tabular-nums}\n.usage-table th{color:var(--text-secondary);font-weight:500}\n.usage-table tr.deleted td{color:var(--text-secondary)}\n.est{color:var(--text-muted)}\n.pager{display:flex;align-items:center;justify-content:flex-end;gap:12px;margin-top:12px;font-size:13px}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref, watch } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport { apiRequest } from '@/api/client'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport UsageActivityHeatmap from '@/components/workspace/UsageActivityHeatmap.vue'\nimport type { WorkspaceUsageAgentRow, WorkspaceUsageDayRow, WorkspaceUsageDetails, WorkspaceUsageOverview, WorkspaceUsageRange, WorkspaceUsageTotals } from '@shared/workspace'\n\nconst router = useRouter(), route = useRoute()\nconst ranges: Array<{ id: WorkspaceUsageRange; label: string }> = [{ id: 'today', label: '今天' }, { id: '7d', label: '7 天' }, { id: '30d', label: '30 天' }, { id: 'all', label: '全部' }]\nconst range = ref<WorkspaceUsageRange>('7d')\nconst overview = ref<WorkspaceUsageOverview | null>(null), details = ref<WorkspaceUsageDetails | null>(null)\nconst calendarDays = ref<Array<Pick<WorkspaceUsageDayRow, 'date' | 'total'>>>([])\nconst loading = ref(true), error = ref(''), exporting = ref(false), page = ref(0)\nconst pageSize = 20\nconst backPath = computed(() => typeof route.query.from === 'string' && /^\\/conversations(?:\\/[a-f0-9-]{36})?(?:\\?taskId=[a-f0-9-]{36})?$/i.test(route.query.from) ? route.query.from : '/conversations')\nconst fmt = (n: number) => n.toLocaleString('zh-CN')\nconst compact = (n: number) => n >= 1e9 ? `${(n / 1e9).toFixed(2)}B` : n >= 1e6 ? `${(n / 1e6).toFixed(2)}M` : n >= 1e4 ? `${(n / 1e3).toFixed(1)}K` : fmt(n)\nconst share = (value: number, total: number) => total > 0 ? Math.round(value / total * 1000) / 10 : 0\nconst estimated = (t: WorkspaceUsageTotals) => t.estimatedCalls > 0\nconst when = (at: number) => new Date(at).toLocaleString('zh-CN', { hour12: false })\nconst liveAgents = computed(() => overview.value?.byAgent.filter(a => !a.deleted) ?? [])\nconst deletedAgents = computed(() => overview.value?.byAgent.filter(a => a.deleted) ?? [])\nconst maxDay = computed(() => Math.max(1, ...(overview.value?.byDay.map(d => d.total) ?? [0])))\nconst pages = computed(() => Math.max(1, Math.ceil((details.value?.total ?? 0) / pageSize)))\nconst sinceLabel = computed(() => overview.value?.firstRecordedAt ? `统计自 ${when(overview.value.firstRecordedAt)} 起` : '尚无记录')\n\nlet generation = 0\nasync function load() {\n const current = ++generation\n loading.value = true\n try {\n const query = `range=${range.value}`\n const [o, d, calendar] = await Promise.all([\n apiRequest<WorkspaceUsageOverview>(`/api/app/usage/overview?${query}`),\n apiRequest<WorkspaceUsageDetails>(`/api/app/usage/details?${query}&limit=${pageSize}&offset=${page.value * pageSize}`),\n // Heatmap always looks at the last ~12 months; ignore the page range filter.\n apiRequest<WorkspaceUsageOverview>('/api/app/usage/overview?range=all'),\n ])\n if (current !== generation) return\n overview.value = o; details.value = d\n calendarDays.value = calendar.byDay.map(day => ({ date: day.date, total: day.total }))\n error.value = ''\n } catch (e) { if (current === generation) error.value = e instanceof Error ? e.message : '无法读取 Token 用量' }\n finally { if (current === generation) loading.value = false }\n}\nwatch(range, () => { page.value = 0; void load() })\nfunction turn(delta: number) { page.value = Math.min(pages.value - 1, Math.max(0, page.value + delta)); void load() }\nasync function exportCsv() {\n exporting.value = true\n try {\n const text = await apiRequest<string>(`/api/app/usage/details?range=${range.value}&format=csv`)\n const url = URL.createObjectURL(new Blob([text], { type: 'text/csv;charset=utf-8' }))\n const link = document.createElement('a')\n link.href = url; link.download = `yaovi-token-usage-${range.value}.csv`; link.click()\n window.setTimeout(() => URL.revokeObjectURL(url), 1000)\n } catch (e) { error.value = e instanceof Error ? e.message : '导出失败' }\n finally { exporting.value = false }\n}\nconst agentLine = (a: WorkspaceUsageAgentRow) => `输入 ${fmt(a.input)} · 输出 ${fmt(a.output)}`\nonMounted(load)\n</script>\n\n<template>\n <main class=\"usage-page\" aria-label=\"Token 用量总览\">\n <header class=\"usage-header\">\n <button class=\"icon-button\" type=\"button\" aria-label=\"返回聊天\" @click=\"router.push(backPath)\"><AppIcon name=\"chevron-left\" :size=\"20\" /></button>\n <AppIcon name=\"history\" :size=\"23\" /><h1>Token 用量</h1>\n <div class=\"segmented\" role=\"tablist\" aria-label=\"时间范围\"><button v-for=\"item in ranges\" :key=\"item.id\" role=\"tab\" :aria-selected=\"range === item.id\" @click=\"range = item.id\">{{ item.label }}</button></div>\n <button class=\"usage-action\" type=\"button\" :disabled=\"exporting || !overview?.totals.calls\" @click=\"exportCsv\">{{ exporting ? '导出中…' : '导出 CSV' }}</button>\n <button class=\"icon-button\" type=\"button\" aria-label=\"刷新用量\" @click=\"load\"><AppIcon name=\"refresh\" :size=\"17\" /></button>\n </header>\n <div class=\"usage-body\">\n <p v-if=\"error\" class=\"usage-error\" role=\"alert\">{{ error }}</p>\n <p v-if=\"loading && !overview\" class=\"usage-note\" role=\"status\">正在读取 Token 用量…</p>\n <template v-if=\"overview\">\n <p class=\"usage-note\">{{ sinceLabel }}。合计 = 输入 + 输出;缓存和推理 Token 单独列出,不重复计入。</p>\n <section class=\"usage-cards\" aria-label=\"总量\">\n <div class=\"usage-card\"><small>合计</small><strong>{{ compact(overview.totals.total) }}</strong><span>{{ estimated(overview.totals) ? '≈ ' : '' }}{{ fmt(overview.totals.calls) }} 次调用</span></div>\n <div class=\"usage-card\"><small>输入</small><strong>{{ compact(overview.totals.input) }}</strong><span>缓存读 {{ fmt(overview.totals.cacheRead) }} · 缓存写 {{ fmt(overview.totals.cacheWrite) }}</span></div>\n <div class=\"usage-card\"><small>输出</small><strong>{{ compact(overview.totals.output) }}</strong><span>推理 {{ fmt(overview.totals.reasoning) }}</span></div>\n <div class=\"usage-card\"><small>已删除 Bot</small><strong>{{ compact(overview.deletedTotals.total) }}</strong><span>{{ share(overview.deletedTotals.total, overview.totals.total) }}% 占比</span></div>\n </section>\n <p v-if=\"overview.totals.estimatedCalls\" class=\"usage-note estimated-note\">其中 {{ fmt(overview.totals.estimatedCalls) }} 次为本机估算值(供应商未返回用量),标记 ≈。</p>\n\n <UsageActivityHeatmap :days=\"calendarDays\" />\n\n <div class=\"usage-grid\">\n <section class=\"usage-panel\" aria-label=\"按模型\">\n <h2>按模型</h2>\n <p v-if=\"!overview.byModel.length\" class=\"usage-empty\">这个时间范围内还没有记录。</p>\n <ul v-else class=\"share-list\">\n <li v-for=\"m in overview.byModel\" :key=\"m.provider + m.model\">\n <div class=\"share-head\"><strong :title=\"m.provider\">{{ m.model }}</strong><span>{{ estimated(m) ? '≈ ' : '' }}{{ fmt(m.total) }} · {{ share(m.total, overview.totals.total) }}%</span></div>\n <div class=\"share-bar\"><i :style=\"{ width: share(m.total, overview.totals.total) + '%' }\" /></div>\n <small>{{ m.provider }} · 输入 {{ fmt(m.input) }} · 输出 {{ fmt(m.output) }} · {{ fmt(m.calls) }} 次</small>\n </li>\n </ul>\n </section>\n\n <section class=\"usage-panel\" aria-label=\"按 Bot\">\n <h2>按 Bot</h2>\n <p v-if=\"!overview.byAgent.length\" class=\"usage-empty\">这个时间范围内还没有记录。</p>\n <template v-else>\n <ul class=\"share-list\">\n <li v-for=\"a in liveAgents\" :key=\"a.agentId\">\n <div class=\"share-head\"><strong>{{ a.name }}</strong><span>{{ estimated(a) ? '≈ ' : '' }}{{ fmt(a.total) }} · {{ share(a.total, overview.totals.total) }}%</span></div>\n <div class=\"share-bar\"><i :style=\"{ width: share(a.total, overview.totals.total) + '%' }\" /></div>\n <small>{{ agentLine(a) }} · {{ fmt(a.calls) }} 次</small>\n </li>\n </ul>\n <template v-if=\"deletedAgents.length\">\n <h3 class=\"deleted-title\">已删除的 Bot <span>{{ fmt(overview.deletedTotals.total) }}</span></h3>\n <ul class=\"share-list deleted\">\n <li v-for=\"a in deletedAgents\" :key=\"a.agentId\">\n <div class=\"share-head\"><strong>{{ a.name }} <em class=\"badge\">已删除</em></strong><span>{{ estimated(a) ? '≈ ' : '' }}{{ fmt(a.total) }} · {{ share(a.total, overview.totals.total) }}%</span></div>\n <div class=\"share-bar\"><i :style=\"{ width: share(a.total, overview.totals.total) + '%' }\" /></div>\n <small>{{ agentLine(a) }} · {{ fmt(a.calls) }} 次</small>\n </li>\n </ul>\n </template>\n </template>\n </section>\n </div>\n\n <section class=\"usage-panel\" aria-label=\"按天趋势\">\n <h2>按天趋势</h2>\n <p v-if=\"!overview.byDay.length\" class=\"usage-empty\">这个时间范围内还没有记录。</p>\n <div v-else class=\"trend\" role=\"img\" :aria-label=\"`按天 Token 用量,共 ${overview.byDay.length} 天`\">\n <div v-for=\"day in overview.byDay\" :key=\"day.date\" class=\"trend-col\" :title=\"`${day.date}:${fmt(day.total)}(输入 ${fmt(day.input)},输出 ${fmt(day.output)})`\">\n <i :style=\"{ height: Math.max(2, day.total / maxDay * 100) + '%' }\" /><small>{{ day.date.slice(5) }}</small>\n </div>\n </div>\n </section>\n\n <section v-if=\"overview.historical\" class=\"usage-panel\" aria-label=\"历史(未分模型)\">\n <h2>历史(未分模型)</h2>\n <p class=\"usage-note\">{{ overview.historical.before }} 之前的旧统计,只有每个 Bot 的输入和输出,没有模型信息,也没有计入上面的总量。合计 {{ fmt(overview.historical.totals.total) }}(输入 {{ fmt(overview.historical.totals.input) }} · 输出 {{ fmt(overview.historical.totals.output) }})。</p>\n <table class=\"usage-table\"><thead><tr><th>Bot</th><th>输入</th><th>输出</th><th>合计</th></tr></thead>\n <tbody><tr v-for=\"a in overview.historical.byAgent\" :key=\"a.agentId\"><td>{{ a.name }} <em v-if=\"a.deleted\" class=\"badge\">已删除</em></td><td>{{ fmt(a.input) }}</td><td>{{ fmt(a.output) }}</td><td>{{ fmt(a.total) }}</td></tr></tbody></table>\n </section>\n\n <section class=\"usage-panel\" aria-label=\"Bot 与模型明细\">\n <h2>明细:Bot × 模型</h2>\n <p v-if=\"!details?.rows.length\" class=\"usage-empty\">这个时间范围内还没有记录。</p>\n <div v-else class=\"table-scroll\"><table class=\"usage-table\">\n <thead><tr><th>Bot</th><th>模型</th><th>输入</th><th>输出</th><th>缓存读</th><th>缓存写</th><th>推理</th><th>合计</th><th>次数</th><th>最近使用</th></tr></thead>\n <tbody><tr v-for=\"r in details.rows\" :key=\"r.agentId + r.provider + r.model\" :class=\"{ deleted: r.deleted }\">\n <td>{{ r.name }} <em v-if=\"r.deleted\" class=\"badge\">已删除</em></td><td :title=\"r.provider\">{{ r.model }}</td>\n <td>{{ fmt(r.input) }}</td><td>{{ fmt(r.output) }}</td><td>{{ fmt(r.cacheRead) }}</td><td>{{ fmt(r.cacheWrite) }}</td><td>{{ fmt(r.reasoning) }}</td>\n <td>{{ estimated(r) ? '≈ ' : '' }}{{ fmt(r.total) }}</td><td>{{ fmt(r.calls) }}<span v-if=\"r.estimatedCalls\" class=\"est\"> (估算 {{ fmt(r.estimatedCalls) }})</span></td><td>{{ when(r.lastAt) }}</td>\n </tr></tbody></table></div>\n <div v-if=\"pages > 1\" class=\"pager\"><button :disabled=\"page === 0 || loading\" @click=\"turn(-1)\">上一页</button><span>{{ page + 1 }} / {{ pages }}</span><button :disabled=\"page >= pages - 1 || loading\" @click=\"turn(1)\">下一页</button></div>\n </section>\n <p class=\"usage-footer\">不含辅助模型调用(标题、压缩、复盘、记忆提炼等)。已删除 Bot 的用量会保留。日期按 Host 本机时区划分。</p>\n </template>\n </div>\n </main>\n</template>\n\n<style scoped>\n.usage-page{height:100dvh;display:flex;flex-direction:column;background:var(--canvas);color:var(--text-primary);min-width:0}\n.usage-header{display:flex;align-items:center;gap:12px;min-height:72px;box-sizing:border-box;padding:12px 24px;border-bottom:1px solid var(--line);background:var(--surface);flex-wrap:wrap}\nh1{font-size:20px;margin:0 20px 0 0;letter-spacing:-.02em}\nh2{font-size:15px;margin:0 0 12px}\n.usage-header button,.pager button{border:1px solid var(--line);background:var(--surface);color:var(--text-primary);border-radius:10px;min-height:36px;padding:0 14px;cursor:pointer}\n.usage-header button:disabled,.pager button:disabled{opacity:.5;cursor:default}\n.icon-button{padding:8px!important;width:40px;flex-shrink:0}\n.segmented{display:flex;gap:3px;border:1px solid var(--line);border-radius:10px;padding:3px;background:var(--surface);margin-left:auto}\n.segmented button{min-height:32px;border:0!important;background:transparent!important;white-space:nowrap}\n.segmented button[aria-selected=true]{background:var(--surface-soft)!important;font-weight:600}\n.usage-action{background:var(--accent)!important;color:var(--text-on-solid)!important;border-color:transparent!important}\n.usage-body{flex:1;overflow:auto;padding:20px 24px 32px;display:flex;flex-direction:column;gap:16px}\n.usage-note,.usage-footer,.usage-empty,small{color:var(--text-secondary);font-size:13px;margin:0}\n.estimated-note{color:var(--text-muted)}\n.usage-error{color:var(--danger);margin:0}\n.usage-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px}\n.usage-card,.usage-panel{background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:16px;min-width:0}\n.usage-card{display:flex;flex-direction:column;gap:4px}.usage-card strong{font-size:26px;letter-spacing:-.02em}.usage-card span{color:var(--text-secondary);font-size:12px}\n.usage-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(340px,1fr));gap:16px}\n.share-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}\n.share-head{display:flex;justify-content:space-between;gap:12px;font-size:14px}.share-head strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.share-head span{color:var(--text-secondary);white-space:nowrap}\n.share-bar{height:6px;border-radius:3px;background:var(--surface-soft);margin:6px 0 4px;overflow:hidden}.share-bar i{display:block;height:100%;background:var(--accent);border-radius:3px}\n.deleted-title{font-size:13px;margin:16px 0 8px;color:var(--text-secondary);display:flex;justify-content:space-between}\n.deleted .share-bar i{background:var(--text-muted)}\n.badge{font-style:normal;font-size:11px;border:1px solid var(--line);border-radius:6px;padding:1px 6px;color:var(--text-secondary);margin-left:4px}\n.trend{display:flex;align-items:flex-end;gap:4px;height:140px;overflow-x:auto;padding-bottom:2px}\n.trend-col{flex:1 0 22px;max-width:48px;height:100%;display:flex;flex-direction:column;justify-content:flex-end;align-items:center;gap:4px}\n.trend-col i{display:block;width:100%;background:var(--accent);border-radius:4px 4px 0 0;min-height:2px}.trend-col small{font-size:10px}\n.table-scroll{overflow-x:auto}\n.usage-table{width:100%;border-collapse:collapse;font-size:13px}\n.usage-table th,.usage-table td{text-align:left;padding:8px 10px;border-bottom:1px solid var(--line);white-space:nowrap;font-variant-numeric:tabular-nums}\n.usage-table th{color:var(--text-secondary);font-weight:500}\n.usage-table tr.deleted td{color:var(--text-secondary)}\n.est{color:var(--text-muted)}\n.pager{display:flex;align-items:center;justify-content:flex-end;gap:12px;margin-top:12px;font-size:13px}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"UsageView-ZsSbQzIY.js","names":[],"sources":["../../src/client/utils/usageActivity.ts","../../src/client/components/workspace/UsageActivityHeatmap.vue","../../src/client/components/workspace/UsageActivityHeatmap.vue","../../src/client/views/UsageView.vue","../../src/client/views/UsageView.vue"],"sourcesContent":["/** Calendar heatmap helpers for Token activity (Monday-first, host-local date keys YYYY-MM-DD). */\n\nexport type UsageActivityMode = 'daily' | 'weekly' | 'cumulative'\nexport type UsageActivityDay = { date: string; total: number }\n\nexport const usageActivityModes: Array<{ id: UsageActivityMode; label: string }> = [\n { id: 'daily', label: '每天' },\n { id: 'weekly', label: '每周' },\n { id: 'cumulative', label: '累计总量' },\n]\n\nexport const usageDateKey = (at: Date): string =>\n `${at.getFullYear()}-${String(at.getMonth() + 1).padStart(2, '0')}-${String(at.getDate()).padStart(2, '0')}`\n\n/** Parse a YYYY-MM-DD key as local midnight. */\nexport function parseUsageDate(date: string): Date {\n const [y, m, d] = date.split('-').map(Number)\n return new Date(y!, m! - 1, d!)\n}\n\n/** Chinese day label like 7月7日. */\nexport function formatUsageActivityDate(date: string): string {\n const d = parseUsageDate(date)\n return `${d.getMonth() + 1}月${d.getDate()}日`\n}\n\n/** Compact Chinese count for tooltips: 14.6万 / 1.2亿 / 1,234. */\nexport function formatUsageActivityTokens(n: number): string {\n const trim = (value: number) => value.toFixed(1).replace(/\\.0$/, '')\n if (n >= 1e8) return `${trim(n / 1e8)}亿`\n if (n >= 1e4) return `${trim(n / 1e4)}万`\n return Math.round(n).toLocaleString('zh-CN')\n}\n\nexport function usageActivityTooltip(date: string, tokens: number): string {\n return `${formatUsageActivityDate(date)}\\n${formatUsageActivityTokens(tokens)} 个 Token`\n}\n\n/** Monday-first week index: Mon=0 … Sun=6. */\nexport const mondayIndex = (day: Date): number => (day.getDay() + 6) % 7\n\n/**\n * Build the last ~12 months as a Monday-aligned week grid ending on `now`.\n * Missing days are filled with total 0 so empty cells stay visible.\n */\nexport function buildUsageActivityWindow(byDay: readonly UsageActivityDay[], now = new Date()): UsageActivityDay[] {\n const totals = new Map(byDay.map(d => [d.date, d.total]))\n const end = new Date(now.getFullYear(), now.getMonth(), now.getDate())\n const start = new Date(end)\n start.setDate(start.getDate() - 364)\n start.setDate(start.getDate() - mondayIndex(start))\n const days: UsageActivityDay[] = []\n for (const cursor = new Date(start); cursor <= end; cursor.setDate(cursor.getDate() + 1)) {\n const key = usageDateKey(cursor)\n days.push({ date: key, total: totals.get(key) ?? 0 })\n }\n return days\n}\n\nexport interface UsageActivityCell {\n date: string\n /** Value used for colouring in the active mode. */\n value: number\n /** Day total (always the raw daily amount). */\n dayTotal: number\n /** Week total containing this day (for weekly tooltips). */\n weekTotal: number\n /** Running sum from the window start through this day. */\n cumulative: number\n level: 0 | 1 | 2 | 3 | 4\n week: number\n weekday: number\n}\n\nfunction levelFor(value: number, max: number): 0 | 1 | 2 | 3 | 4 {\n if (value <= 0 || max <= 0) return 0\n const ratio = value / max\n if (ratio <= 0.25) return 1\n if (ratio <= 0.5) return 2\n if (ratio <= 0.75) return 3\n return 4\n}\n\n/** Compute cell values and colour levels for the selected mode. */\nexport function buildUsageActivityCells(days: readonly UsageActivityDay[], mode: UsageActivityMode): UsageActivityCell[] {\n const weekTotals = new Map<number, number>()\n let running = 0\n const prepared = days.map((day, index) => {\n const week = Math.floor(index / 7)\n weekTotals.set(week, (weekTotals.get(week) ?? 0) + day.total)\n running += day.total\n return { ...day, week, weekday: index % 7, cumulative: running, weekTotal: 0 }\n })\n for (const cell of prepared) cell.weekTotal = weekTotals.get(cell.week) ?? 0\n const values = prepared.map(cell => mode === 'daily' ? cell.total : mode === 'weekly' ? cell.weekTotal : cell.cumulative)\n const max = Math.max(0, ...values)\n return prepared.map((cell, i) => ({\n date: cell.date,\n dayTotal: cell.total,\n weekTotal: cell.weekTotal,\n cumulative: cell.cumulative,\n value: values[i]!,\n level: levelFor(values[i]!, max),\n week: cell.week,\n weekday: cell.weekday,\n }))\n}\n\nexport interface UsageActivityMonthLabel { week: number; label: string }\n\n/** Place a Chinese month label on the first week that contains day 1 of that month. */\nexport function usageActivityMonthLabels(cells: readonly UsageActivityCell[]): UsageActivityMonthLabel[] {\n const labels: UsageActivityMonthLabel[] = []\n let last = -1\n for (const cell of cells) {\n if (!cell.date.endsWith('-01')) continue\n if (cell.week === last) continue\n last = cell.week\n labels.push({ week: cell.week, label: `${parseUsageDate(cell.date).getMonth() + 1}月` })\n }\n return labels\n}\n\nexport function usageActivityTooltipFor(cell: UsageActivityCell, mode: UsageActivityMode): string {\n const tokens = mode === 'daily' ? cell.dayTotal : mode === 'weekly' ? cell.weekTotal : cell.cumulative\n if (mode === 'weekly') {\n return `${formatUsageActivityDate(cell.date)} 所在周\\n${formatUsageActivityTokens(tokens)} 个 Token`\n }\n if (mode === 'cumulative') {\n return `${formatUsageActivityDate(cell.date)}\\n累计 ${formatUsageActivityTokens(tokens)} 个 Token`\n }\n return usageActivityTooltip(cell.date, tokens)\n}\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport {\n buildUsageActivityCells, buildUsageActivityWindow, usageActivityModes, usageActivityMonthLabels,\n usageActivityTooltipFor, type UsageActivityDay, type UsageActivityMode,\n} from '@/utils/usageActivity'\n\nconst props = defineProps<{ days: UsageActivityDay[]; now?: number }>()\nconst mode = ref<UsageActivityMode>('daily')\nconst tip = ref<{ text: string; x: number; y: number } | null>(null)\nconst windowDays = computed(() => buildUsageActivityWindow(props.days, props.now ? new Date(props.now) : new Date()))\nconst cells = computed(() => buildUsageActivityCells(windowDays.value, mode.value))\nconst months = computed(() => usageActivityMonthLabels(cells.value))\nconst weekCount = computed(() => (cells.value.length ? cells.value[cells.value.length - 1]!.week + 1 : 0))\nconst hasAny = computed(() => windowDays.value.some(d => d.total > 0))\n\nfunction showTip(event: MouseEvent | FocusEvent, index: number) {\n const cell = cells.value[index]\n if (!cell) return\n const host = (event.currentTarget as HTMLElement).closest('.activity-heatmap') as HTMLElement | null\n const rect = host?.getBoundingClientRect()\n const target = (event.currentTarget as HTMLElement).getBoundingClientRect()\n const x = 'clientX' in event ? event.clientX - (rect?.left ?? 0) : target.left + target.width / 2 - (rect?.left ?? 0)\n const y = 'clientY' in event ? event.clientY - (rect?.top ?? 0) : target.top - (rect?.top ?? 0)\n tip.value = { text: usageActivityTooltipFor(cell, mode.value), x, y }\n}\nfunction hideTip() { tip.value = null }\n</script>\n\n<template>\n <section class=\"activity-heatmap\" aria-label=\"Token 活动\">\n <header class=\"activity-header\">\n <h2>Token 活动</h2>\n <div class=\"activity-modes\" role=\"tablist\" aria-label=\"活动视图\">\n <button v-for=\"item in usageActivityModes\" :key=\"item.id\" type=\"button\" role=\"tab\" :aria-selected=\"mode === item.id\" @click=\"mode = item.id\">{{ item.label }}</button>\n </div>\n </header>\n <p v-if=\"!hasAny\" class=\"activity-empty\">近 12 个月还没有 Token 记录。开始对话后,这里会显示每天的用量。</p>\n <div v-else class=\"activity-body\">\n <div class=\"activity-weekdays\" aria-hidden=\"true\"><span>一</span><span>三</span><span>五</span></div>\n <div class=\"activity-scroll\">\n <div class=\"activity-months\" :style=\"{ gridTemplateColumns: `repeat(${weekCount}, minmax(11px, 1fr))` }\">\n <span v-for=\"m in months\" :key=\"m.week + m.label\" class=\"activity-month\" :style=\"{ gridColumn: m.week + 1 }\">{{ m.label }}</span>\n </div>\n <div\n class=\"activity-grid\"\n role=\"img\"\n :aria-label=\"`近 12 个月 Token 活动,${mode === 'daily' ? '按天' : mode === 'weekly' ? '按周' : '累计'}着色`\"\n :style=\"{ gridTemplateColumns: `repeat(${weekCount}, minmax(11px, 1fr))` }\"\n >\n <button\n v-for=\"(cell, index) in cells\"\n :key=\"cell.date\"\n type=\"button\"\n class=\"activity-cell\"\n :class=\"`level-${cell.level}`\"\n :style=\"{ gridColumn: cell.week + 1, gridRow: cell.weekday + 1 }\"\n :aria-label=\"usageActivityTooltipFor(cell, mode).replace('\\n', ',')\"\n @mouseenter=\"showTip($event, index)\"\n @mousemove=\"showTip($event, index)\"\n @mouseleave=\"hideTip\"\n @focus=\"showTip($event, index)\"\n @blur=\"hideTip\"\n />\n </div>\n </div>\n <div class=\"activity-legend\" aria-hidden=\"true\"><span>少</span><i class=\"level-0\" /><i class=\"level-1\" /><i class=\"level-2\" /><i class=\"level-3\" /><i class=\"level-4\" /><span>多</span></div>\n <div v-if=\"tip\" class=\"activity-tip\" role=\"tooltip\" :style=\"{ left: tip.x + 'px', top: tip.y + 'px' }\">{{ tip.text }}</div>\n </div>\n </section>\n</template>\n\n<style scoped>\n.activity-heatmap{position:relative;background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:16px;min-width:0}\n.activity-header{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:12px}\n.activity-header h2{margin:0;font-size:15px}\n.activity-modes{display:flex;gap:14px}\n.activity-modes button{border:0;background:transparent;color:var(--text-secondary);padding:0;min-height:auto;font:inherit;cursor:pointer}\n.activity-modes button[aria-selected=true]{color:var(--text-primary);font-weight:600}\n.activity-empty{margin:0;color:var(--text-secondary);font-size:13px}\n.activity-body{display:grid;grid-template-columns:18px minmax(0,1fr);grid-template-rows:auto auto;column-gap:8px;row-gap:8px;align-items:start}\n.activity-weekdays{display:grid;grid-template-rows:repeat(7,11px);gap:3px;padding-top:18px;font-size:10px;color:var(--text-muted);line-height:11px}\n.activity-weekdays span:nth-child(1){grid-row:1}.activity-weekdays span:nth-child(2){grid-row:3}.activity-weekdays span:nth-child(3){grid-row:5}\n.activity-scroll{grid-column:2;overflow-x:auto;min-width:0}\n.activity-months{display:grid;gap:3px;height:16px;margin-bottom:2px;font-size:11px;color:var(--text-secondary)}\n.activity-month{white-space:nowrap}\n.activity-grid{display:grid;grid-template-rows:repeat(7,11px);gap:3px;min-width:max-content}\n.activity-cell{width:100%;min-width:11px;height:11px;padding:0;border:0;border-radius:3px;cursor:default;background:var(--surface-soft);box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--line) 70%, transparent)}\n.activity-cell:focus-visible{outline:2px solid var(--workflow-accent);outline-offset:1px}\n.activity-cell.level-1,.activity-legend i.level-1{background:color-mix(in srgb, var(--workflow-accent) 22%, var(--surface-soft))}\n.activity-cell.level-2,.activity-legend i.level-2{background:color-mix(in srgb, var(--workflow-accent) 45%, var(--surface-soft))}\n.activity-cell.level-3,.activity-legend i.level-3{background:color-mix(in srgb, var(--workflow-accent) 70%, var(--surface-soft))}\n.activity-cell.level-4,.activity-legend i.level-4{background:var(--workflow-accent)}\n.activity-legend{grid-column:2;display:flex;align-items:center;justify-content:flex-end;gap:4px;font-size:11px;color:var(--text-muted)}\n.activity-legend i{display:block;width:11px;height:11px;border-radius:3px;background:var(--surface-soft);box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--line) 70%, transparent)}\n.activity-tip{position:absolute;z-index:5;transform:translate(-50%,calc(-100% - 10px));padding:8px 10px;border-radius:8px;background:var(--text-primary);color:var(--text-on-solid);font-size:12px;line-height:1.35;white-space:pre-line;pointer-events:none;box-shadow:var(--shadow-float);max-width:220px}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport {\n buildUsageActivityCells, buildUsageActivityWindow, usageActivityModes, usageActivityMonthLabels,\n usageActivityTooltipFor, type UsageActivityDay, type UsageActivityMode,\n} from '@/utils/usageActivity'\n\nconst props = defineProps<{ days: UsageActivityDay[]; now?: number }>()\nconst mode = ref<UsageActivityMode>('daily')\nconst tip = ref<{ text: string; x: number; y: number } | null>(null)\nconst windowDays = computed(() => buildUsageActivityWindow(props.days, props.now ? new Date(props.now) : new Date()))\nconst cells = computed(() => buildUsageActivityCells(windowDays.value, mode.value))\nconst months = computed(() => usageActivityMonthLabels(cells.value))\nconst weekCount = computed(() => (cells.value.length ? cells.value[cells.value.length - 1]!.week + 1 : 0))\nconst hasAny = computed(() => windowDays.value.some(d => d.total > 0))\n\nfunction showTip(event: MouseEvent | FocusEvent, index: number) {\n const cell = cells.value[index]\n if (!cell) return\n const host = (event.currentTarget as HTMLElement).closest('.activity-heatmap') as HTMLElement | null\n const rect = host?.getBoundingClientRect()\n const target = (event.currentTarget as HTMLElement).getBoundingClientRect()\n const x = 'clientX' in event ? event.clientX - (rect?.left ?? 0) : target.left + target.width / 2 - (rect?.left ?? 0)\n const y = 'clientY' in event ? event.clientY - (rect?.top ?? 0) : target.top - (rect?.top ?? 0)\n tip.value = { text: usageActivityTooltipFor(cell, mode.value), x, y }\n}\nfunction hideTip() { tip.value = null }\n</script>\n\n<template>\n <section class=\"activity-heatmap\" aria-label=\"Token 活动\">\n <header class=\"activity-header\">\n <h2>Token 活动</h2>\n <div class=\"activity-modes\" role=\"tablist\" aria-label=\"活动视图\">\n <button v-for=\"item in usageActivityModes\" :key=\"item.id\" type=\"button\" role=\"tab\" :aria-selected=\"mode === item.id\" @click=\"mode = item.id\">{{ item.label }}</button>\n </div>\n </header>\n <p v-if=\"!hasAny\" class=\"activity-empty\">近 12 个月还没有 Token 记录。开始对话后,这里会显示每天的用量。</p>\n <div v-else class=\"activity-body\">\n <div class=\"activity-weekdays\" aria-hidden=\"true\"><span>一</span><span>三</span><span>五</span></div>\n <div class=\"activity-scroll\">\n <div class=\"activity-months\" :style=\"{ gridTemplateColumns: `repeat(${weekCount}, minmax(11px, 1fr))` }\">\n <span v-for=\"m in months\" :key=\"m.week + m.label\" class=\"activity-month\" :style=\"{ gridColumn: m.week + 1 }\">{{ m.label }}</span>\n </div>\n <div\n class=\"activity-grid\"\n role=\"img\"\n :aria-label=\"`近 12 个月 Token 活动,${mode === 'daily' ? '按天' : mode === 'weekly' ? '按周' : '累计'}着色`\"\n :style=\"{ gridTemplateColumns: `repeat(${weekCount}, minmax(11px, 1fr))` }\"\n >\n <button\n v-for=\"(cell, index) in cells\"\n :key=\"cell.date\"\n type=\"button\"\n class=\"activity-cell\"\n :class=\"`level-${cell.level}`\"\n :style=\"{ gridColumn: cell.week + 1, gridRow: cell.weekday + 1 }\"\n :aria-label=\"usageActivityTooltipFor(cell, mode).replace('\\n', ',')\"\n @mouseenter=\"showTip($event, index)\"\n @mousemove=\"showTip($event, index)\"\n @mouseleave=\"hideTip\"\n @focus=\"showTip($event, index)\"\n @blur=\"hideTip\"\n />\n </div>\n </div>\n <div class=\"activity-legend\" aria-hidden=\"true\"><span>少</span><i class=\"level-0\" /><i class=\"level-1\" /><i class=\"level-2\" /><i class=\"level-3\" /><i class=\"level-4\" /><span>多</span></div>\n <div v-if=\"tip\" class=\"activity-tip\" role=\"tooltip\" :style=\"{ left: tip.x + 'px', top: tip.y + 'px' }\">{{ tip.text }}</div>\n </div>\n </section>\n</template>\n\n<style scoped>\n.activity-heatmap{position:relative;background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:16px;min-width:0}\n.activity-header{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:12px}\n.activity-header h2{margin:0;font-size:15px}\n.activity-modes{display:flex;gap:14px}\n.activity-modes button{border:0;background:transparent;color:var(--text-secondary);padding:0;min-height:auto;font:inherit;cursor:pointer}\n.activity-modes button[aria-selected=true]{color:var(--text-primary);font-weight:600}\n.activity-empty{margin:0;color:var(--text-secondary);font-size:13px}\n.activity-body{display:grid;grid-template-columns:18px minmax(0,1fr);grid-template-rows:auto auto;column-gap:8px;row-gap:8px;align-items:start}\n.activity-weekdays{display:grid;grid-template-rows:repeat(7,11px);gap:3px;padding-top:18px;font-size:10px;color:var(--text-muted);line-height:11px}\n.activity-weekdays span:nth-child(1){grid-row:1}.activity-weekdays span:nth-child(2){grid-row:3}.activity-weekdays span:nth-child(3){grid-row:5}\n.activity-scroll{grid-column:2;overflow-x:auto;min-width:0}\n.activity-months{display:grid;gap:3px;height:16px;margin-bottom:2px;font-size:11px;color:var(--text-secondary)}\n.activity-month{white-space:nowrap}\n.activity-grid{display:grid;grid-template-rows:repeat(7,11px);gap:3px;min-width:max-content}\n.activity-cell{width:100%;min-width:11px;height:11px;padding:0;border:0;border-radius:3px;cursor:default;background:var(--surface-soft);box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--line) 70%, transparent)}\n.activity-cell:focus-visible{outline:2px solid var(--workflow-accent);outline-offset:1px}\n.activity-cell.level-1,.activity-legend i.level-1{background:color-mix(in srgb, var(--workflow-accent) 22%, var(--surface-soft))}\n.activity-cell.level-2,.activity-legend i.level-2{background:color-mix(in srgb, var(--workflow-accent) 45%, var(--surface-soft))}\n.activity-cell.level-3,.activity-legend i.level-3{background:color-mix(in srgb, var(--workflow-accent) 70%, var(--surface-soft))}\n.activity-cell.level-4,.activity-legend i.level-4{background:var(--workflow-accent)}\n.activity-legend{grid-column:2;display:flex;align-items:center;justify-content:flex-end;gap:4px;font-size:11px;color:var(--text-muted)}\n.activity-legend i{display:block;width:11px;height:11px;border-radius:3px;background:var(--surface-soft);box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--line) 70%, transparent)}\n.activity-tip{position:absolute;z-index:5;transform:translate(-50%,calc(-100% - 10px));padding:8px 10px;border-radius:8px;background:var(--text-primary);color:var(--text-on-solid);font-size:12px;line-height:1.35;white-space:pre-line;pointer-events:none;box-shadow:var(--shadow-float);max-width:220px}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref, watch } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport { apiRequest } from '@/api/client'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport UsageActivityHeatmap from '@/components/workspace/UsageActivityHeatmap.vue'\nimport type { WorkspaceUsageAgentRow, WorkspaceUsageDayRow, WorkspaceUsageDetails, WorkspaceUsageOverview, WorkspaceUsageRange, WorkspaceUsageTotals } from '@shared/workspace'\n\nconst router = useRouter(), route = useRoute()\nconst ranges: Array<{ id: WorkspaceUsageRange; label: string }> = [{ id: 'today', label: '今天' }, { id: '7d', label: '7 天' }, { id: '30d', label: '30 天' }, { id: 'all', label: '全部' }]\nconst range = ref<WorkspaceUsageRange>('7d')\nconst overview = ref<WorkspaceUsageOverview | null>(null), details = ref<WorkspaceUsageDetails | null>(null)\nconst calendarDays = ref<Array<Pick<WorkspaceUsageDayRow, 'date' | 'total'>>>([])\nconst loading = ref(true), error = ref(''), exporting = ref(false), page = ref(0)\nconst pageSize = 20\nconst backPath = computed(() => typeof route.query.from === 'string' && /^\\/conversations(?:\\/[a-f0-9-]{36})?(?:\\?taskId=[a-f0-9-]{36})?$/i.test(route.query.from) ? route.query.from : '/conversations')\nconst fmt = (n: number) => n.toLocaleString('zh-CN')\nconst compact = (n: number) => n >= 1e9 ? `${(n / 1e9).toFixed(2)}B` : n >= 1e6 ? `${(n / 1e6).toFixed(2)}M` : n >= 1e4 ? `${(n / 1e3).toFixed(1)}K` : fmt(n)\nconst share = (value: number, total: number) => total > 0 ? Math.round(value / total * 1000) / 10 : 0\nconst estimated = (t: WorkspaceUsageTotals) => t.estimatedCalls > 0\nconst when = (at: number) => new Date(at).toLocaleString('zh-CN', { hour12: false })\nconst liveAgents = computed(() => overview.value?.byAgent.filter(a => !a.deleted) ?? [])\nconst deletedAgents = computed(() => overview.value?.byAgent.filter(a => a.deleted) ?? [])\nconst maxDay = computed(() => Math.max(1, ...(overview.value?.byDay.map(d => d.total) ?? [0])))\nconst pages = computed(() => Math.max(1, Math.ceil((details.value?.total ?? 0) / pageSize)))\nconst sinceLabel = computed(() => overview.value?.firstRecordedAt ? `统计自 ${when(overview.value.firstRecordedAt)} 起` : '尚无记录')\n\nlet generation = 0\nasync function load() {\n const current = ++generation\n loading.value = true\n try {\n const query = `range=${range.value}`\n const [o, d, calendar] = await Promise.all([\n apiRequest<WorkspaceUsageOverview>(`/api/app/usage/overview?${query}`),\n apiRequest<WorkspaceUsageDetails>(`/api/app/usage/details?${query}&limit=${pageSize}&offset=${page.value * pageSize}`),\n // Heatmap always looks at the last ~12 months; ignore the page range filter.\n apiRequest<WorkspaceUsageOverview>('/api/app/usage/overview?range=all'),\n ])\n if (current !== generation) return\n overview.value = o; details.value = d\n calendarDays.value = calendar.byDay.map(day => ({ date: day.date, total: day.total }))\n error.value = ''\n } catch (e) { if (current === generation) error.value = e instanceof Error ? e.message : '无法读取 Token 用量' }\n finally { if (current === generation) loading.value = false }\n}\nwatch(range, () => { page.value = 0; void load() })\nfunction turn(delta: number) { page.value = Math.min(pages.value - 1, Math.max(0, page.value + delta)); void load() }\nasync function exportCsv() {\n exporting.value = true\n try {\n const text = await apiRequest<string>(`/api/app/usage/details?range=${range.value}&format=csv`)\n const url = URL.createObjectURL(new Blob([text], { type: 'text/csv;charset=utf-8' }))\n const link = document.createElement('a')\n link.href = url; link.download = `yaovi-token-usage-${range.value}.csv`; link.click()\n window.setTimeout(() => URL.revokeObjectURL(url), 1000)\n } catch (e) { error.value = e instanceof Error ? e.message : '导出失败' }\n finally { exporting.value = false }\n}\nconst agentLine = (a: WorkspaceUsageAgentRow) => `输入 ${fmt(a.input)} · 输出 ${fmt(a.output)}`\nonMounted(load)\n</script>\n\n<template>\n <main class=\"usage-page\" aria-label=\"Token 用量总览\">\n <header class=\"usage-header\">\n <button class=\"icon-button\" type=\"button\" aria-label=\"返回聊天\" @click=\"router.push(backPath)\"><AppIcon name=\"chevron-left\" :size=\"20\" /></button>\n <AppIcon name=\"history\" :size=\"23\" /><h1>Token 用量</h1>\n <div class=\"segmented\" role=\"tablist\" aria-label=\"时间范围\"><button v-for=\"item in ranges\" :key=\"item.id\" role=\"tab\" :aria-selected=\"range === item.id\" @click=\"range = item.id\">{{ item.label }}</button></div>\n <button class=\"usage-action\" type=\"button\" :disabled=\"exporting || !overview?.totals.calls\" @click=\"exportCsv\">{{ exporting ? '导出中…' : '导出 CSV' }}</button>\n <button class=\"icon-button\" type=\"button\" aria-label=\"刷新用量\" @click=\"load\"><AppIcon name=\"refresh\" :size=\"17\" /></button>\n </header>\n <div class=\"usage-body\">\n <p v-if=\"error\" class=\"usage-error\" role=\"alert\">{{ error }}</p>\n <p v-if=\"loading && !overview\" class=\"usage-note\" role=\"status\">正在读取 Token 用量…</p>\n <template v-if=\"overview\">\n <p class=\"usage-note\">{{ sinceLabel }}。合计 = 输入 + 输出;缓存和推理 Token 单独列出,不重复计入。</p>\n <section class=\"usage-cards\" aria-label=\"总量\">\n <div class=\"usage-card\"><small>合计</small><strong>{{ compact(overview.totals.total) }}</strong><span>{{ estimated(overview.totals) ? '≈ ' : '' }}{{ fmt(overview.totals.calls) }} 次调用</span></div>\n <div class=\"usage-card\"><small>输入</small><strong>{{ compact(overview.totals.input) }}</strong><span>缓存读 {{ fmt(overview.totals.cacheRead) }} · 缓存写 {{ fmt(overview.totals.cacheWrite) }}</span></div>\n <div class=\"usage-card\"><small>输出</small><strong>{{ compact(overview.totals.output) }}</strong><span>推理 {{ fmt(overview.totals.reasoning) }}</span></div>\n <div class=\"usage-card\"><small>已删除 Bot</small><strong>{{ compact(overview.deletedTotals.total) }}</strong><span>{{ share(overview.deletedTotals.total, overview.totals.total) }}% 占比</span></div>\n </section>\n <p v-if=\"overview.totals.estimatedCalls\" class=\"usage-note estimated-note\">其中 {{ fmt(overview.totals.estimatedCalls) }} 次为本机估算值(供应商未返回用量),标记 ≈。</p>\n\n <UsageActivityHeatmap :days=\"calendarDays\" />\n\n <div class=\"usage-grid\">\n <section class=\"usage-panel\" aria-label=\"按模型\">\n <h2>按模型</h2>\n <p v-if=\"!overview.byModel.length\" class=\"usage-empty\">这个时间范围内还没有记录。</p>\n <ul v-else class=\"share-list\">\n <li v-for=\"m in overview.byModel\" :key=\"m.provider + m.model\">\n <div class=\"share-head\"><strong :title=\"m.provider\">{{ m.model }}</strong><span>{{ estimated(m) ? '≈ ' : '' }}{{ fmt(m.total) }} · {{ share(m.total, overview.totals.total) }}%</span></div>\n <div class=\"share-bar\"><i :style=\"{ width: share(m.total, overview.totals.total) + '%' }\" /></div>\n <small>{{ m.provider }} · 输入 {{ fmt(m.input) }} · 输出 {{ fmt(m.output) }} · {{ fmt(m.calls) }} 次</small>\n </li>\n </ul>\n </section>\n\n <section class=\"usage-panel\" aria-label=\"按 Bot\">\n <h2>按 Bot</h2>\n <p v-if=\"!overview.byAgent.length\" class=\"usage-empty\">这个时间范围内还没有记录。</p>\n <template v-else>\n <ul class=\"share-list\">\n <li v-for=\"a in liveAgents\" :key=\"a.agentId\">\n <div class=\"share-head\"><strong>{{ a.name }}</strong><span>{{ estimated(a) ? '≈ ' : '' }}{{ fmt(a.total) }} · {{ share(a.total, overview.totals.total) }}%</span></div>\n <div class=\"share-bar\"><i :style=\"{ width: share(a.total, overview.totals.total) + '%' }\" /></div>\n <small>{{ agentLine(a) }} · {{ fmt(a.calls) }} 次</small>\n </li>\n </ul>\n <template v-if=\"deletedAgents.length\">\n <h3 class=\"deleted-title\">已删除的 Bot <span>{{ fmt(overview.deletedTotals.total) }}</span></h3>\n <ul class=\"share-list deleted\">\n <li v-for=\"a in deletedAgents\" :key=\"a.agentId\">\n <div class=\"share-head\"><strong>{{ a.name }} <em class=\"badge\">已删除</em></strong><span>{{ estimated(a) ? '≈ ' : '' }}{{ fmt(a.total) }} · {{ share(a.total, overview.totals.total) }}%</span></div>\n <div class=\"share-bar\"><i :style=\"{ width: share(a.total, overview.totals.total) + '%' }\" /></div>\n <small>{{ agentLine(a) }} · {{ fmt(a.calls) }} 次</small>\n </li>\n </ul>\n </template>\n </template>\n </section>\n </div>\n\n <section class=\"usage-panel\" aria-label=\"按天趋势\">\n <h2>按天趋势</h2>\n <p v-if=\"!overview.byDay.length\" class=\"usage-empty\">这个时间范围内还没有记录。</p>\n <div v-else class=\"trend\" role=\"img\" :aria-label=\"`按天 Token 用量,共 ${overview.byDay.length} 天`\">\n <div v-for=\"day in overview.byDay\" :key=\"day.date\" class=\"trend-col\" :title=\"`${day.date}:${fmt(day.total)}(输入 ${fmt(day.input)},输出 ${fmt(day.output)})`\">\n <i :style=\"{ height: Math.max(2, day.total / maxDay * 100) + '%' }\" /><small>{{ day.date.slice(5) }}</small>\n </div>\n </div>\n </section>\n\n <section v-if=\"overview.historical\" class=\"usage-panel\" aria-label=\"历史(未分模型)\">\n <h2>历史(未分模型)</h2>\n <p class=\"usage-note\">{{ overview.historical.before }} 之前的旧统计,只有每个 Bot 的输入和输出,没有模型信息,也没有计入上面的总量。合计 {{ fmt(overview.historical.totals.total) }}(输入 {{ fmt(overview.historical.totals.input) }} · 输出 {{ fmt(overview.historical.totals.output) }})。</p>\n <table class=\"usage-table\"><thead><tr><th>Bot</th><th>输入</th><th>输出</th><th>合计</th></tr></thead>\n <tbody><tr v-for=\"a in overview.historical.byAgent\" :key=\"a.agentId\"><td>{{ a.name }} <em v-if=\"a.deleted\" class=\"badge\">已删除</em></td><td>{{ fmt(a.input) }}</td><td>{{ fmt(a.output) }}</td><td>{{ fmt(a.total) }}</td></tr></tbody></table>\n </section>\n\n <section class=\"usage-panel\" aria-label=\"Bot 与模型明细\">\n <h2>明细:Bot × 模型</h2>\n <p v-if=\"!details?.rows.length\" class=\"usage-empty\">这个时间范围内还没有记录。</p>\n <div v-else class=\"table-scroll\"><table class=\"usage-table\">\n <thead><tr><th>Bot</th><th>模型</th><th>输入</th><th>输出</th><th>缓存读</th><th>缓存写</th><th>推理</th><th>合计</th><th>次数</th><th>最近使用</th></tr></thead>\n <tbody><tr v-for=\"r in details.rows\" :key=\"r.agentId + r.provider + r.model\" :class=\"{ deleted: r.deleted }\">\n <td>{{ r.name }} <em v-if=\"r.deleted\" class=\"badge\">已删除</em></td><td :title=\"r.provider\">{{ r.model }}</td>\n <td>{{ fmt(r.input) }}</td><td>{{ fmt(r.output) }}</td><td>{{ fmt(r.cacheRead) }}</td><td>{{ fmt(r.cacheWrite) }}</td><td>{{ fmt(r.reasoning) }}</td>\n <td>{{ estimated(r) ? '≈ ' : '' }}{{ fmt(r.total) }}</td><td>{{ fmt(r.calls) }}<span v-if=\"r.estimatedCalls\" class=\"est\"> (估算 {{ fmt(r.estimatedCalls) }})</span></td><td>{{ when(r.lastAt) }}</td>\n </tr></tbody></table></div>\n <div v-if=\"pages > 1\" class=\"pager\"><button :disabled=\"page === 0 || loading\" @click=\"turn(-1)\">上一页</button><span>{{ page + 1 }} / {{ pages }}</span><button :disabled=\"page >= pages - 1 || loading\" @click=\"turn(1)\">下一页</button></div>\n </section>\n <p class=\"usage-footer\">不含辅助模型调用(标题、压缩、复盘、记忆提炼等)。已删除 Bot 的用量会保留。日期按 Host 本机时区划分。</p>\n </template>\n </div>\n </main>\n</template>\n\n<style scoped>\n.usage-page{height:100dvh;display:flex;flex-direction:column;background:var(--canvas);color:var(--text-primary);min-width:0}\n.usage-header{display:flex;align-items:center;gap:12px;min-height:72px;box-sizing:border-box;padding:12px 24px;border-bottom:1px solid var(--line);background:var(--surface);flex-wrap:wrap}\nh1{font-size:20px;margin:0 20px 0 0;letter-spacing:-.02em}\nh2{font-size:15px;margin:0 0 12px}\n.usage-header button,.pager button{border:1px solid var(--line);background:var(--surface);color:var(--text-primary);border-radius:10px;min-height:36px;padding:0 14px;cursor:pointer}\n.usage-header button:disabled,.pager button:disabled{opacity:.5;cursor:default}\n.icon-button{padding:8px!important;width:40px;flex-shrink:0}\n.segmented{display:flex;gap:3px;border:1px solid var(--line);border-radius:10px;padding:3px;background:var(--surface);margin-left:auto}\n.segmented button{min-height:32px;border:0!important;background:transparent!important;white-space:nowrap}\n.segmented button[aria-selected=true]{background:var(--surface-soft)!important;font-weight:600}\n.usage-action{background:var(--accent)!important;color:var(--text-on-solid)!important;border-color:transparent!important}\n.usage-body{flex:1;overflow:auto;padding:20px 24px 32px;display:flex;flex-direction:column;gap:16px}\n.usage-note,.usage-footer,.usage-empty,small{color:var(--text-secondary);font-size:13px;margin:0}\n.estimated-note{color:var(--text-muted)}\n.usage-error{color:var(--danger);margin:0}\n.usage-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px}\n.usage-card,.usage-panel{background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:16px;min-width:0}\n.usage-card{display:flex;flex-direction:column;gap:4px}.usage-card strong{font-size:26px;letter-spacing:-.02em}.usage-card span{color:var(--text-secondary);font-size:12px}\n.usage-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(340px,1fr));gap:16px}\n.share-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}\n.share-head{display:flex;justify-content:space-between;gap:12px;font-size:14px}.share-head strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.share-head span{color:var(--text-secondary);white-space:nowrap}\n.share-bar{height:6px;border-radius:3px;background:var(--surface-soft);margin:6px 0 4px;overflow:hidden}.share-bar i{display:block;height:100%;background:var(--accent);border-radius:3px}\n.deleted-title{font-size:13px;margin:16px 0 8px;color:var(--text-secondary);display:flex;justify-content:space-between}\n.deleted .share-bar i{background:var(--text-muted)}\n.badge{font-style:normal;font-size:11px;border:1px solid var(--line);border-radius:6px;padding:1px 6px;color:var(--text-secondary);margin-left:4px}\n.trend{display:flex;align-items:flex-end;gap:4px;height:140px;overflow-x:auto;padding-bottom:2px}\n.trend-col{flex:1 0 22px;max-width:48px;height:100%;display:flex;flex-direction:column;justify-content:flex-end;align-items:center;gap:4px}\n.trend-col i{display:block;width:100%;background:var(--accent);border-radius:4px 4px 0 0;min-height:2px}.trend-col small{font-size:10px}\n.table-scroll{overflow-x:auto}\n.usage-table{width:100%;border-collapse:collapse;font-size:13px}\n.usage-table th,.usage-table td{text-align:left;padding:8px 10px;border-bottom:1px solid var(--line);white-space:nowrap;font-variant-numeric:tabular-nums}\n.usage-table th{color:var(--text-secondary);font-weight:500}\n.usage-table tr.deleted td{color:var(--text-secondary)}\n.est{color:var(--text-muted)}\n.pager{display:flex;align-items:center;justify-content:flex-end;gap:12px;margin-top:12px;font-size:13px}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref, watch } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport { apiRequest } from '@/api/client'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport UsageActivityHeatmap from '@/components/workspace/UsageActivityHeatmap.vue'\nimport type { WorkspaceUsageAgentRow, WorkspaceUsageDayRow, WorkspaceUsageDetails, WorkspaceUsageOverview, WorkspaceUsageRange, WorkspaceUsageTotals } from '@shared/workspace'\n\nconst router = useRouter(), route = useRoute()\nconst ranges: Array<{ id: WorkspaceUsageRange; label: string }> = [{ id: 'today', label: '今天' }, { id: '7d', label: '7 天' }, { id: '30d', label: '30 天' }, { id: 'all', label: '全部' }]\nconst range = ref<WorkspaceUsageRange>('7d')\nconst overview = ref<WorkspaceUsageOverview | null>(null), details = ref<WorkspaceUsageDetails | null>(null)\nconst calendarDays = ref<Array<Pick<WorkspaceUsageDayRow, 'date' | 'total'>>>([])\nconst loading = ref(true), error = ref(''), exporting = ref(false), page = ref(0)\nconst pageSize = 20\nconst backPath = computed(() => typeof route.query.from === 'string' && /^\\/conversations(?:\\/[a-f0-9-]{36})?(?:\\?taskId=[a-f0-9-]{36})?$/i.test(route.query.from) ? route.query.from : '/conversations')\nconst fmt = (n: number) => n.toLocaleString('zh-CN')\nconst compact = (n: number) => n >= 1e9 ? `${(n / 1e9).toFixed(2)}B` : n >= 1e6 ? `${(n / 1e6).toFixed(2)}M` : n >= 1e4 ? `${(n / 1e3).toFixed(1)}K` : fmt(n)\nconst share = (value: number, total: number) => total > 0 ? Math.round(value / total * 1000) / 10 : 0\nconst estimated = (t: WorkspaceUsageTotals) => t.estimatedCalls > 0\nconst when = (at: number) => new Date(at).toLocaleString('zh-CN', { hour12: false })\nconst liveAgents = computed(() => overview.value?.byAgent.filter(a => !a.deleted) ?? [])\nconst deletedAgents = computed(() => overview.value?.byAgent.filter(a => a.deleted) ?? [])\nconst maxDay = computed(() => Math.max(1, ...(overview.value?.byDay.map(d => d.total) ?? [0])))\nconst pages = computed(() => Math.max(1, Math.ceil((details.value?.total ?? 0) / pageSize)))\nconst sinceLabel = computed(() => overview.value?.firstRecordedAt ? `统计自 ${when(overview.value.firstRecordedAt)} 起` : '尚无记录')\n\nlet generation = 0\nasync function load() {\n const current = ++generation\n loading.value = true\n try {\n const query = `range=${range.value}`\n const [o, d, calendar] = await Promise.all([\n apiRequest<WorkspaceUsageOverview>(`/api/app/usage/overview?${query}`),\n apiRequest<WorkspaceUsageDetails>(`/api/app/usage/details?${query}&limit=${pageSize}&offset=${page.value * pageSize}`),\n // Heatmap always looks at the last ~12 months; ignore the page range filter.\n apiRequest<WorkspaceUsageOverview>('/api/app/usage/overview?range=all'),\n ])\n if (current !== generation) return\n overview.value = o; details.value = d\n calendarDays.value = calendar.byDay.map(day => ({ date: day.date, total: day.total }))\n error.value = ''\n } catch (e) { if (current === generation) error.value = e instanceof Error ? e.message : '无法读取 Token 用量' }\n finally { if (current === generation) loading.value = false }\n}\nwatch(range, () => { page.value = 0; void load() })\nfunction turn(delta: number) { page.value = Math.min(pages.value - 1, Math.max(0, page.value + delta)); void load() }\nasync function exportCsv() {\n exporting.value = true\n try {\n const text = await apiRequest<string>(`/api/app/usage/details?range=${range.value}&format=csv`)\n const url = URL.createObjectURL(new Blob([text], { type: 'text/csv;charset=utf-8' }))\n const link = document.createElement('a')\n link.href = url; link.download = `yaovi-token-usage-${range.value}.csv`; link.click()\n window.setTimeout(() => URL.revokeObjectURL(url), 1000)\n } catch (e) { error.value = e instanceof Error ? e.message : '导出失败' }\n finally { exporting.value = false }\n}\nconst agentLine = (a: WorkspaceUsageAgentRow) => `输入 ${fmt(a.input)} · 输出 ${fmt(a.output)}`\nonMounted(load)\n</script>\n\n<template>\n <main class=\"usage-page\" aria-label=\"Token 用量总览\">\n <header class=\"usage-header\">\n <button class=\"icon-button\" type=\"button\" aria-label=\"返回聊天\" @click=\"router.push(backPath)\"><AppIcon name=\"chevron-left\" :size=\"20\" /></button>\n <AppIcon name=\"history\" :size=\"23\" /><h1>Token 用量</h1>\n <div class=\"segmented\" role=\"tablist\" aria-label=\"时间范围\"><button v-for=\"item in ranges\" :key=\"item.id\" role=\"tab\" :aria-selected=\"range === item.id\" @click=\"range = item.id\">{{ item.label }}</button></div>\n <button class=\"usage-action\" type=\"button\" :disabled=\"exporting || !overview?.totals.calls\" @click=\"exportCsv\">{{ exporting ? '导出中…' : '导出 CSV' }}</button>\n <button class=\"icon-button\" type=\"button\" aria-label=\"刷新用量\" @click=\"load\"><AppIcon name=\"refresh\" :size=\"17\" /></button>\n </header>\n <div class=\"usage-body\">\n <p v-if=\"error\" class=\"usage-error\" role=\"alert\">{{ error }}</p>\n <p v-if=\"loading && !overview\" class=\"usage-note\" role=\"status\">正在读取 Token 用量…</p>\n <template v-if=\"overview\">\n <p class=\"usage-note\">{{ sinceLabel }}。合计 = 输入 + 输出;缓存和推理 Token 单独列出,不重复计入。</p>\n <section class=\"usage-cards\" aria-label=\"总量\">\n <div class=\"usage-card\"><small>合计</small><strong>{{ compact(overview.totals.total) }}</strong><span>{{ estimated(overview.totals) ? '≈ ' : '' }}{{ fmt(overview.totals.calls) }} 次调用</span></div>\n <div class=\"usage-card\"><small>输入</small><strong>{{ compact(overview.totals.input) }}</strong><span>缓存读 {{ fmt(overview.totals.cacheRead) }} · 缓存写 {{ fmt(overview.totals.cacheWrite) }}</span></div>\n <div class=\"usage-card\"><small>输出</small><strong>{{ compact(overview.totals.output) }}</strong><span>推理 {{ fmt(overview.totals.reasoning) }}</span></div>\n <div class=\"usage-card\"><small>已删除 Bot</small><strong>{{ compact(overview.deletedTotals.total) }}</strong><span>{{ share(overview.deletedTotals.total, overview.totals.total) }}% 占比</span></div>\n </section>\n <p v-if=\"overview.totals.estimatedCalls\" class=\"usage-note estimated-note\">其中 {{ fmt(overview.totals.estimatedCalls) }} 次为本机估算值(供应商未返回用量),标记 ≈。</p>\n\n <UsageActivityHeatmap :days=\"calendarDays\" />\n\n <div class=\"usage-grid\">\n <section class=\"usage-panel\" aria-label=\"按模型\">\n <h2>按模型</h2>\n <p v-if=\"!overview.byModel.length\" class=\"usage-empty\">这个时间范围内还没有记录。</p>\n <ul v-else class=\"share-list\">\n <li v-for=\"m in overview.byModel\" :key=\"m.provider + m.model\">\n <div class=\"share-head\"><strong :title=\"m.provider\">{{ m.model }}</strong><span>{{ estimated(m) ? '≈ ' : '' }}{{ fmt(m.total) }} · {{ share(m.total, overview.totals.total) }}%</span></div>\n <div class=\"share-bar\"><i :style=\"{ width: share(m.total, overview.totals.total) + '%' }\" /></div>\n <small>{{ m.provider }} · 输入 {{ fmt(m.input) }} · 输出 {{ fmt(m.output) }} · {{ fmt(m.calls) }} 次</small>\n </li>\n </ul>\n </section>\n\n <section class=\"usage-panel\" aria-label=\"按 Bot\">\n <h2>按 Bot</h2>\n <p v-if=\"!overview.byAgent.length\" class=\"usage-empty\">这个时间范围内还没有记录。</p>\n <template v-else>\n <ul class=\"share-list\">\n <li v-for=\"a in liveAgents\" :key=\"a.agentId\">\n <div class=\"share-head\"><strong>{{ a.name }}</strong><span>{{ estimated(a) ? '≈ ' : '' }}{{ fmt(a.total) }} · {{ share(a.total, overview.totals.total) }}%</span></div>\n <div class=\"share-bar\"><i :style=\"{ width: share(a.total, overview.totals.total) + '%' }\" /></div>\n <small>{{ agentLine(a) }} · {{ fmt(a.calls) }} 次</small>\n </li>\n </ul>\n <template v-if=\"deletedAgents.length\">\n <h3 class=\"deleted-title\">已删除的 Bot <span>{{ fmt(overview.deletedTotals.total) }}</span></h3>\n <ul class=\"share-list deleted\">\n <li v-for=\"a in deletedAgents\" :key=\"a.agentId\">\n <div class=\"share-head\"><strong>{{ a.name }} <em class=\"badge\">已删除</em></strong><span>{{ estimated(a) ? '≈ ' : '' }}{{ fmt(a.total) }} · {{ share(a.total, overview.totals.total) }}%</span></div>\n <div class=\"share-bar\"><i :style=\"{ width: share(a.total, overview.totals.total) + '%' }\" /></div>\n <small>{{ agentLine(a) }} · {{ fmt(a.calls) }} 次</small>\n </li>\n </ul>\n </template>\n </template>\n </section>\n </div>\n\n <section class=\"usage-panel\" aria-label=\"按天趋势\">\n <h2>按天趋势</h2>\n <p v-if=\"!overview.byDay.length\" class=\"usage-empty\">这个时间范围内还没有记录。</p>\n <div v-else class=\"trend\" role=\"img\" :aria-label=\"`按天 Token 用量,共 ${overview.byDay.length} 天`\">\n <div v-for=\"day in overview.byDay\" :key=\"day.date\" class=\"trend-col\" :title=\"`${day.date}:${fmt(day.total)}(输入 ${fmt(day.input)},输出 ${fmt(day.output)})`\">\n <i :style=\"{ height: Math.max(2, day.total / maxDay * 100) + '%' }\" /><small>{{ day.date.slice(5) }}</small>\n </div>\n </div>\n </section>\n\n <section v-if=\"overview.historical\" class=\"usage-panel\" aria-label=\"历史(未分模型)\">\n <h2>历史(未分模型)</h2>\n <p class=\"usage-note\">{{ overview.historical.before }} 之前的旧统计,只有每个 Bot 的输入和输出,没有模型信息,也没有计入上面的总量。合计 {{ fmt(overview.historical.totals.total) }}(输入 {{ fmt(overview.historical.totals.input) }} · 输出 {{ fmt(overview.historical.totals.output) }})。</p>\n <table class=\"usage-table\"><thead><tr><th>Bot</th><th>输入</th><th>输出</th><th>合计</th></tr></thead>\n <tbody><tr v-for=\"a in overview.historical.byAgent\" :key=\"a.agentId\"><td>{{ a.name }} <em v-if=\"a.deleted\" class=\"badge\">已删除</em></td><td>{{ fmt(a.input) }}</td><td>{{ fmt(a.output) }}</td><td>{{ fmt(a.total) }}</td></tr></tbody></table>\n </section>\n\n <section class=\"usage-panel\" aria-label=\"Bot 与模型明细\">\n <h2>明细:Bot × 模型</h2>\n <p v-if=\"!details?.rows.length\" class=\"usage-empty\">这个时间范围内还没有记录。</p>\n <div v-else class=\"table-scroll\"><table class=\"usage-table\">\n <thead><tr><th>Bot</th><th>模型</th><th>输入</th><th>输出</th><th>缓存读</th><th>缓存写</th><th>推理</th><th>合计</th><th>次数</th><th>最近使用</th></tr></thead>\n <tbody><tr v-for=\"r in details.rows\" :key=\"r.agentId + r.provider + r.model\" :class=\"{ deleted: r.deleted }\">\n <td>{{ r.name }} <em v-if=\"r.deleted\" class=\"badge\">已删除</em></td><td :title=\"r.provider\">{{ r.model }}</td>\n <td>{{ fmt(r.input) }}</td><td>{{ fmt(r.output) }}</td><td>{{ fmt(r.cacheRead) }}</td><td>{{ fmt(r.cacheWrite) }}</td><td>{{ fmt(r.reasoning) }}</td>\n <td>{{ estimated(r) ? '≈ ' : '' }}{{ fmt(r.total) }}</td><td>{{ fmt(r.calls) }}<span v-if=\"r.estimatedCalls\" class=\"est\"> (估算 {{ fmt(r.estimatedCalls) }})</span></td><td>{{ when(r.lastAt) }}</td>\n </tr></tbody></table></div>\n <div v-if=\"pages > 1\" class=\"pager\"><button :disabled=\"page === 0 || loading\" @click=\"turn(-1)\">上一页</button><span>{{ page + 1 }} / {{ pages }}</span><button :disabled=\"page >= pages - 1 || loading\" @click=\"turn(1)\">下一页</button></div>\n </section>\n <p class=\"usage-footer\">不含辅助模型调用(标题、压缩、复盘、记忆提炼等)。已删除 Bot 的用量会保留。日期按 Host 本机时区划分。</p>\n </template>\n </div>\n </main>\n</template>\n\n<style scoped>\n.usage-page{height:100dvh;display:flex;flex-direction:column;background:var(--canvas);color:var(--text-primary);min-width:0}\n.usage-header{display:flex;align-items:center;gap:12px;min-height:72px;box-sizing:border-box;padding:12px 24px;border-bottom:1px solid var(--line);background:var(--surface);flex-wrap:wrap}\nh1{font-size:20px;margin:0 20px 0 0;letter-spacing:-.02em}\nh2{font-size:15px;margin:0 0 12px}\n.usage-header button,.pager button{border:1px solid var(--line);background:var(--surface);color:var(--text-primary);border-radius:10px;min-height:36px;padding:0 14px;cursor:pointer}\n.usage-header button:disabled,.pager button:disabled{opacity:.5;cursor:default}\n.icon-button{padding:8px!important;width:40px;flex-shrink:0}\n.segmented{display:flex;gap:3px;border:1px solid var(--line);border-radius:10px;padding:3px;background:var(--surface);margin-left:auto}\n.segmented button{min-height:32px;border:0!important;background:transparent!important;white-space:nowrap}\n.segmented button[aria-selected=true]{background:var(--surface-soft)!important;font-weight:600}\n.usage-action{background:var(--accent)!important;color:var(--text-on-solid)!important;border-color:transparent!important}\n.usage-body{flex:1;overflow:auto;padding:20px 24px 32px;display:flex;flex-direction:column;gap:16px}\n.usage-note,.usage-footer,.usage-empty,small{color:var(--text-secondary);font-size:13px;margin:0}\n.estimated-note{color:var(--text-muted)}\n.usage-error{color:var(--danger);margin:0}\n.usage-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px}\n.usage-card,.usage-panel{background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:16px;min-width:0}\n.usage-card{display:flex;flex-direction:column;gap:4px}.usage-card strong{font-size:26px;letter-spacing:-.02em}.usage-card span{color:var(--text-secondary);font-size:12px}\n.usage-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(340px,1fr));gap:16px}\n.share-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}\n.share-head{display:flex;justify-content:space-between;gap:12px;font-size:14px}.share-head strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.share-head span{color:var(--text-secondary);white-space:nowrap}\n.share-bar{height:6px;border-radius:3px;background:var(--surface-soft);margin:6px 0 4px;overflow:hidden}.share-bar i{display:block;height:100%;background:var(--accent);border-radius:3px}\n.deleted-title{font-size:13px;margin:16px 0 8px;color:var(--text-secondary);display:flex;justify-content:space-between}\n.deleted .share-bar i{background:var(--text-muted)}\n.badge{font-style:normal;font-size:11px;border:1px solid var(--line);border-radius:6px;padding:1px 6px;color:var(--text-secondary);margin-left:4px}\n.trend{display:flex;align-items:flex-end;gap:4px;height:140px;overflow-x:auto;padding-bottom:2px}\n.trend-col{flex:1 0 22px;max-width:48px;height:100%;display:flex;flex-direction:column;justify-content:flex-end;align-items:center;gap:4px}\n.trend-col i{display:block;width:100%;background:var(--accent);border-radius:4px 4px 0 0;min-height:2px}.trend-col small{font-size:10px}\n.table-scroll{overflow-x:auto}\n.usage-table{width:100%;border-collapse:collapse;font-size:13px}\n.usage-table th,.usage-table td{text-align:left;padding:8px 10px;border-bottom:1px solid var(--line);white-space:nowrap;font-variant-numeric:tabular-nums}\n.usage-table th{color:var(--text-secondary);font-weight:500}\n.usage-table tr.deleted td{color:var(--text-secondary)}\n.est{color:var(--text-muted)}\n.pager{display:flex;align-items:center;justify-content:flex-end;gap:12px;margin-top:12px;font-size:13px}\n</style>\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import{A as e,B as t,D as n,Et as r,F as i,G as a,H as o,L as s,M as ee,Ot as c,W as te,_ as l,a as u,b as d,d as f,et as p,f as m,m as h,p as g,st as _,t as v,u as y,v as b}from"./_plugin-vue_export-helper-CD0r-vVb.js";import{B as x,at as S,i as ne,j as C,lt as w,nt as T,o as re,ot as E}from"./index-P_kZCZ2-.js";var D={xmlns:`http://www.w3.org/2000/svg`,"xmlns:xlink":`http://www.w3.org/1999/xlink`,viewBox:`0 0 512 512`},O=d({name:`ChevronForwardOutline`,render:function(e,t){return i(),h(`svg`,D,t[0]||=[f(`path`,{fill:`none`,stroke:`currentColor`,"stroke-linecap":`round`,"stroke-linejoin":`round`,"stroke-width":`48`,d:`M184 112l144 144l-144 144`},null,-1)])}}),k={class:`memory-files`,"aria-label":`Markdown 记忆文件`},ie={key:0,role:`alert`},ae={key:1,role:`status`},A={class:`file-list`},j=[`disabled`,`onClick`],M=[`disabled`],N=[`disabled`],P=[`disabled`],F={class:`actions`},I=[`disabled`],L=[`disabled`],R=[`disabled`],oe=[`disabled`,`onClick`],z=`/api/app/workspace`,se=v(d({__name:`MemoryFilesPanel`,props:{agentId:{},scope:{},projectId:{}},emits:[`dirty-change`,`changed`],setup(t,{emit:n}){let r=t,ee=n,te=p([]),d=p(),m=p(``),_=p(`memory/notes.md`),v=p(``),y=p(``),b=p(!1),x=p([]),S=0,T=()=>({scope:r.scope,agentId:r.agentId,...r.scope===`project`?{projectId:r.projectId}:{}}),re=(e={})=>new URLSearchParams({...T(),...e});async function D(e){if(!b.value){b.value=!0,v.value=``,y.value=``;try{await e()}catch(e){v.value=e instanceof Error?e.message:`操作失败`}finally{b.value=!1}}}async function O(){let e=++S;if(!r.agentId||r.scope===`project`&&!r.projectId){te.value=[];return}try{let t=await C(`${z}/memory-files?${re()}`);e===S&&(te.value=t.files)}catch(t){e===S&&(v.value=String(t))}}async function se(e){await D(async()=>{let t=S,n=await C(`${z}/memory-file?${re({path:e})}`);t===S&&(d.value=n,m.value=n.content,x.value=[])})}async function B(){d.value&&await D(async()=>{d.value=await C(`/api/app/workspace/memory-file`,{method:`PUT`,body:{...T(),requestId:ne(),path:d.value.path,content:m.value,expectedHash:d.value.hash}}),m.value=d.value.content,y.value=`文件已保存,下一轮对话生效。`,ee(`changed`),await O()})}async function V(){await D(async()=>{x.value=(await C(`${z}/memory-file-history?${re()}`)).revisions.filter(e=>e.path===d.value?.path)})}async function H(e){await D(async()=>{d.value=await C(`/api/app/workspace/memory-file-revert`,{method:`POST`,body:{...T(),requestId:ne(),id:e.id,expectedHash:d.value.hash}}),m.value=d.value.content,y.value=`已撤销该次修改。`,await O(),ee(`changed`)})}return o(()=>[r.scope,r.agentId,r.projectId],()=>{d.value=void 0,m.value=``,x.value=[],O()},{immediate:!0}),o(()=>!!d.value&&m.value!==d.value.content,e=>ee(`dirty-change`,e)),e(()=>{S++}),(e,n)=>(i(),h(`section`,k,[n[7]||=f(`h3`,null,`记忆文件`,-1),n[8]||=f(`p`,null,`MEMORY.md 自动加载;主题和日记按需读取。可以直接编辑 Markdown。`,-1),v.value?(i(),h(`p`,ie,c(v.value),1)):g(``,!0),y.value?(i(),h(`p`,ae,c(y.value),1)):g(``,!0),d.value?(i(),h(u,{key:3},[f(`label`,null,[l(c(d.value.path),1),a(f(`textarea`,{"onUpdate:modelValue":n[2]||=e=>m.value=e,disabled:b.value,rows:`16`,spellcheck:`false`,"aria-label":`记忆 Markdown 内容`},null,8,P),[[E,m.value]])]),f(`div`,F,[f(`button`,{disabled:b.value||m.value===d.value.content,onClick:B},` 保存`,8,I),f(`button`,{disabled:b.value,onClick:n[3]||=e=>{d.value=void 0,x.value=[]}},` 取消编辑`,8,L),f(`button`,{disabled:b.value,onClick:V},`修改历史`,8,R)]),(i(!0),h(u,null,s(x.value,e=>(i(),h(`details`,{key:e.id},[f(`summary`,null,c(new Date(e.at).toLocaleString()),1),n[5]||=f(`p`,null,`修改前`,-1),f(`pre`,null,c(e.before),1),n[6]||=f(`p`,null,`修改后`,-1),f(`pre`,null,c(e.after),1),f(`button`,{disabled:b.value||e.afterHash!==d.value.hash||m.value!==d.value.content,onClick:t=>H(e)},` 撤销此修改 `,8,oe)]))),128))],64)):(i(),h(u,{key:2},[f(`div`,A,[(i(!0),h(u,null,s(te.value,e=>(i(),h(`button`,{key:e.path,disabled:b.value,onClick:t=>se(e.path)},c(e.path)+` · `+c(e.bytes)+` B `,9,j))),128))]),f(`form`,{onSubmit:n[1]||=w(e=>se(_.value),[`prevent`])},[f(`label`,null,[n[4]||=l(`新文件路径`,-1),a(f(`input`,{"onUpdate:modelValue":n[0]||=e=>_.value=e,placeholder:`memory/主题.md`,disabled:b.value},null,8,M),[[E,_.value]])]),f(`button`,{disabled:b.value||!t.agentId},`创建或打开`,8,N)],32)],64))]))}}),[[`__scopeId`,`data-v-3820f19a`]]);function B(e){return e.status===`failed`?`投递或执行失败`:e.status===`stopped`?`协作已停止`:e.hasReply?`已收到回复`:e.replyTo?`回复已投递`:{queued:`已投递 · 排队中`,running:`正在处理`,waiting:`等待补充信息`,complete:`执行已结束 · 暂无回复`}[e.status??``]??`协作消息`}function V(e){return{queued:`排队中`,pending:`待执行`,failed:`执行失败`,interrupted:`已中断`,running:`进行中`,review:`待复核`,waiting:`需要补充信息`,blocked:`遇到阻碍`,complete:`已完成`,cancelling:`正在停止`,cancelled:`已停止`}[e]??`状态待确认`}var H={bot_created:`创建了 Bot`,helper_created:`创建了临时助手`,team_created:`组建了团队`,assignment_created:`派发了任务`};function U(e){return e===`incoming`}function ce(e){let t=[],n=e.coordinatorId?e.name(e.coordinatorId):`负责人`;for(let n of e.activities??[]){let r=n.activity;!r||r.kind===`assignment_created`&&e.assignments.length||t.push({id:`activity:${n.id}`,title:`${r.actorName} ${H[r.kind]} ${r.targetName}`,status:`已记录`,content:r.description,at:n.createdAt})}for(let r of e.assignments){let i=e.name(r.agentId);t.push({id:`assignment:${r.id}:created`,title:`${n} → ${i} · 派发任务`,status:V(r.status),content:[r.title,r.brief].filter(Boolean).join(`
2
+ `),at:r.createdAt??0}),r.result&&t.push({id:`assignment:${r.id}:result`,title:`${i} → ${n} · 回传结果`,status:`已回传`,content:`${r.title}\n${r.result}`,at:r.updatedAt??r.createdAt??0}),r.review&&t.push({id:`assignment:${r.id}:review`,title:`${n} → ${i} · 复核意见`,status:V(r.status),content:r.review,at:r.updatedAt??r.createdAt??0})}for(let n of e.peers)t.push({id:`peer:${n.id}`,title:`${n.fromName||e.name(n.fromAgentId)} → ${n.targetName||(n.toAgentId?e.name(n.toAgentId):`群聊`)}`,status:B(n),content:n.content,at:n.createdAt??0,error:n.error,peer:n});return[...new Map(t.map(e=>[e.id,e])).values()].sort((e,t)=>e.at-t.at)}var le={key:0},ue={key:1,"aria-label":`记忆与项目分类`},de=[`aria-pressed`,`onClick`],fe=[`aria-busy`],pe={key:0,class:`actions`},me={key:1,role:`alert`,class:`knowledge-error`},he={key:0,class:`project-heading`},ge=[`value`],_e=[`value`,`disabled`],ve={key:0,class:`check`},ye={class:`actions`},be=[`disabled`],xe={key:0,class:`project-list`,"aria-label":`项目列表`},Se=[`aria-pressed`,`onClick`],Ce={key:1,class:`project-details`,"aria-label":`当前项目详情`},we={key:0,class:`project-description`},Te={class:`project-detail-row`},Ee={class:`project-members`},De={key:0},Oe={class:`project-detail-row`},ke={class:`project-detail-row`},Ae={class:`project-detail-actions`},je={class:`actions`},Me=[`onClick`],Ne=[`onClick`],Pe={key:5,class:`empty`},Fe={class:`scope-note`},Ie={class:`pre-wrap`},Le={key:0},Re={key:1,class:`knowledge-error`},ze=[`disabled`,`onClick`],Be={key:0,class:`empty`},Ve={class:`filters`},He={key:0},Ue=[`disabled`],We=[`value`],Ge={key:1},Ke=[`disabled`],qe=[`value`],Je={class:`scope-note`},Ye={key:0,class:`check`},Xe=[`checked`,`disabled`],Ze={key:1,role:`status`},Qe={key:2,class:`actions`},$e=[`disabled`],et=[`href`],tt={key:0},nt=[`value`],rt={class:`actions`},it=[`disabled`],at={class:`pre-wrap`},ot={key:0,role:`status`},st={key:1},ct=[`href`],lt={key:0},ut={class:`actions`},dt=[`onClick`],ft=[`onClick`,`disabled`],pt=[`onClick`,`disabled`],mt={key:4,class:`actions`},ht={role:`status`},gt=[`disabled`],_t=[`disabled`],vt={key:5,class:`empty`},yt={key:6,class:`knowledge-editor`},bt={key:7},xt={key:0},St={key:1},Ct=[`disabled`,`onClick`],W=v(d({__name:`WorkspaceKnowledgePanel`,props:{agents:{},conversations:{},conversationId:{},taskId:{},assignments:{},goal:{},revision:{},embedded:{type:Boolean},initialTab:{},selectedAgentId:{},hideAgentControls:{type:Boolean}},emits:[`changed`,`dirty-change`],setup(d,{expose:v,emit:D}){let k=d,ie=D,ae=p(),A=p(!1),j=p(!1),M=p(``),N=p(`agent`),P=p(``),F=p(``),I=p(``),L=p([]),R=p([]),oe=p([]),z=p([]),B=p([]),V=p(),H=p(),U=p(0),W=p(),G=p(!1),K=p(!1),wt=p([]),q=0,Tt,J=y(()=>k.agents.filter(e=>!e.archived&&!e.temporaryGoalId)),Et=y(()=>k.agents.find(e=>e.id===P.value)),Y=y(()=>L.value.find(e=>e.id===F.value)),X=y(()=>[`agent`,`user`,`project`].includes(N.value)?N.value:`agent`),Z=e=>k.agents.find(t=>t.id===e)?.name??`原贡献者`,Dt=y(()=>ce({peers:oe.value,activities:wt.value,assignments:k.assignments??[],coordinatorId:k.goal?.coordinatorId,name:Z})),Ot=()=>new URLSearchParams({scope:X.value,...N.value===`agent`&&P.value?{agentId:P.value}:{},...N.value===`project`&&F.value?{projectId:F.value}:{},...I.value?{search:I.value}:{}}).toString(),kt={queued:`已排队`,pending:`待提炼`,running:`处理中`,waiting:`等待同伴`,complete:`已完成`,failed:`失败`,stopped:`已停止`,skipped:`已跳过`},At={source_missing:`找不到对应的来源消息`,quote_mismatch:`引用与原文不一致`,sensitive_content:`包含凭据等敏感内容`,project_unbound:`项目记忆缺少关联项目`,user_not_explicit:`缺少用户明确表达的长期信息`,forgotten:`已遗忘的内容或来源不再保存`,stale:`推理期间记忆已更新,本次旧提炼结果未应用`};function jt(e){if(e.status!==`complete`)return kt[e.status]??e.status;let t=e.result;return t?t.extractedCount?t.writtenCount?`已保存 ${t.writtenCount} 条记忆`:t.replayedCount?`重试已确认 ${t.replayedCount} 条记录`:t.duplicateCount?`未新增记忆,已有相同内容`:`候选记忆均未保存`:`本轮没有可记录内容`:`已结束(旧记录未统计保存结果)`}function Mt(e){return Object.entries(e.result?.skippedReasons??{}).filter(([,e])=>e>0).map(([e,t])=>`${At[e]??`其他原因`}:${t} 条`)}async function Q(e=!1){if(e&&G.value&&N.value!==`collaboration`)return;let t=++q;e||(M.value=``);try{let e=await C(`/api/app/workspace/projects`);if(t!==q||!A.value)return;if(L.value=e.projects,F.value||=L.value.find(e=>!e.archived)?.id??``,N.value===`collaboration`){let[e,n]=await Promise.all([C(`/api/app/workspace/collaboration?${new URLSearchParams({...k.conversationId?{conversationId:k.conversationId}:{},...k.taskId?{taskId:k.taskId}:{}})}`),k.conversationId?C(`/api/app/conversations/${encodeURIComponent(k.conversationId)}/activities${k.taskId?`?taskId=${encodeURIComponent(k.taskId)}`:``}`):Promise.resolve({messages:[]})]);t===q&&(oe.value=e.messages,wt.value=n.messages)}else if(N.value!==`projects`&&(N.value!==`project`||F.value)){let e=await C(`/api/app/workspace/memories?${Ot()}`);t===q&&(R.value=e.memories,U.value=e.total??e.memories.length,W.value=e.nextOffset,G.value=!1)}else R.value=[],U.value=0,W.value=void 0,G.value=!1;if(P.value){let e=await C(`/api/app/workspace/memory-jobs?agentId=${P.value}`);t===q&&(z.value=e.jobs)}}catch(e){t===q&&(M.value=String(e))}}async function Nt(){if(j.value||W.value===void 0)return;let e=q,t=Ot(),n=W.value;j.value=!0,M.value=``;try{let r=await C(`/api/app/workspace/memories?${t}&offset=${n}&limit=100`);if(e!==q||!A.value||t!==Ot())return;let i=new Set(R.value.map(e=>`${e.scope}:${e.agentId}:${e.id}`));R.value.push(...r.memories.filter(e=>!i.has(`${e.scope}:${e.agentId}:${e.id}`))),U.value=r.total,W.value=r.nextOffset,G.value=!0}catch(t){e===q&&(M.value=String(t))}finally{j.value=!1}}async function Pt(e=`agent`,t,r){N.value=e,P.value=t??J.value[0]?.id??``,F.value=r??``,A.value=!0,V.value=void 0,H.value=void 0,B.value=[],I.value=``,await n(),k.embedded||ae.value?.showModal(),await Q(),clearInterval(Tt),Tt=setInterval(()=>{!j.value&&!V.value&&!H.value&&Q(!0)},5e3)}function Ft(){if(K.value){M.value=`请先保存文件或取消编辑。`;return}A.value=!1,q++,clearInterval(Tt),k.embedded||ae.value?.close()}function It(e){K.value||(N.value=e,V.value=void 0,H.value=void 0,B.value=[],Q())}async function $(e){if(!j.value){j.value=!0,M.value=``;try{await e(),ie(`changed`),await Q()}catch(e){M.value=String(e)}finally{j.value=!1}}}function Lt(e){B.value=[],V.value=e?{id:e.id,expectedRevision:e.revision,agentId:e.agentId,content:e.content,tier:e.tier}:{agentId:P.value||J.value[0]?.id||``,content:``,tier:`log`}}async function Rt(){let e=V.value;if(!e?.content.trim()){M.value=`请输入需要记住的事实`;return}await $(async()=>{await C(`/api/app/workspace/memories`,{method:`POST`,body:{requestId:ne(),scope:X.value,...X.value===`project`?{projectId:F.value}:{},...e}}),V.value=void 0})}async function zt(e){await $(async()=>{await C(`/api/app/workspace/memories/forget`,{method:`POST`,body:{requestId:ne(),scope:e.scope,agentId:e.agentId,...e.projectId?{projectId:e.projectId}:{},id:e.id,expectedRevision:e.revision}})})}async function Bt(e){let t=new URLSearchParams({scope:e.scope,agentId:e.agentId,...e.projectId?{projectId:e.projectId}:{}});await $(async()=>{B.value=(await C(`/api/app/workspace/memories/${e.id}/revisions?${t}`)).revisions})}function Vt(e){H.value=e?{...e,memberIds:[...e.memberIds],groupIds:[...e.groupIds]}:{id:``,name:``,description:``,memberIds:[],groupIds:[],revision:0,archived:!1,createdAt:0,updatedAt:0}}async function Ht(){let e=H.value;if(!e?.name.trim()){M.value=`请输入项目名称`;return}await $(async()=>{let t=await C(`/api/app/workspace/projects`,{method:`POST`,body:{requestId:ne(),...e.id?{id:e.id,expectedRevision:e.revision}:{},name:e.name,description:e.description,memberIds:e.memberIds,groupIds:e.groupIds,archived:e.archived}});F.value=t.project.id,H.value=void 0})}async function Ut(e,t){await $(async()=>{await C(`/api/app/agents/${P.value}`,{method:`PATCH`,body:{[e]:t}})})}return e(()=>{q++,clearInterval(Tt)}),ee(()=>{k.embedded&&Pt(k.initialTab??`projects`,k.selectedAgentId)}),o(()=>k.initialTab,e=>{k.embedded&&e&&It(e)}),o(()=>k.revision,()=>{A.value&&!j.value&&!V.value&&!H.value&&Q(!0)}),o(()=>[k.conversationId,k.taskId],()=>{q++,oe.value=[],wt.value=[],A.value&&N.value===`collaboration`&&Q()}),o(()=>K.value||!!V.value||!!H.value,e=>ie(`dirty-change`,e)),v({open:Pt,close:Ft}),(e,n)=>(i(),m(t(d.embedded?`section`:`dialog`),{ref_key:`dialog`,ref:ae,class:r([`knowledge-dialog`,{"knowledge-dialog--embedded":d.embedded}]),"aria-labelledby":d.embedded?void 0:`knowledge-title`,onCancel:w(Ft,[`prevent`]),onClose:n[27]||=e=>A.value=!1},{default:te(()=>[d.embedded?g(``,!0):(i(),h(`header`,le,[n[28]||=f(`div`,null,[f(`h2`,{id:`knowledge-title`},`Bot 记忆与协作`),f(`p`,null,`管理长期事实、项目和同伴请求`)],-1),f(`button`,{type:`button`,onClick:Ft,"aria-label":`关闭记忆与协作`},`关闭`)])),d.embedded?g(``,!0):(i(),h(`nav`,ue,[(i(),h(u,null,s([[`agent`,`Bot 记忆`],[`user`,`用户记忆`],[`projects`,`项目`],[`project`,`项目记忆`],[`collaboration`,`协作`]],e=>f(`button`,{key:e[0],"aria-pressed":N.value===e[0],onClick:t=>It(e[0])},c(e[1]),9,de)),64))])),f(`div`,{class:`knowledge-body`,"aria-busy":j.value},[d.embedded&&N.value===`project`?(i(),h(`div`,pe,[f(`button`,{type:`button`,onClick:n[0]||=e=>It(`projects`)},`返回项目`),n[29]||=f(`h3`,null,`项目记忆`,-1)])):g(``,!0),M.value?(i(),h(`p`,me,c(M.value),1)):g(``,!0),N.value===`projects`?(i(),h(u,{key:2},[d.embedded?(i(),h(`div`,he,[n[30]||=f(`div`,null,[f(`h3`,null,`项目`),f(`p`,null,`让同一项目的 Bot 共享决策与约定。`)],-1),H.value?g(``,!0):(i(),h(`button`,{key:0,type:`button`,class:`project-primary`,onClick:n[1]||=e=>Vt()},`新建项目`))])):H.value?g(``,!0):(i(),h(`button`,{key:1,onClick:n[2]||=e=>Vt()},`新建项目`)),H.value?(i(),h(`form`,{key:2,class:`knowledge-editor`,onSubmit:w(Ht,[`prevent`])},[f(`h3`,null,c(H.value.id?`编辑项目`:`新建项目`),1),f(`label`,null,[n[31]||=l(`名称`,-1),a(f(`input`,{"onUpdate:modelValue":n[3]||=e=>H.value.name=e,required:``,maxlength:`100`},null,512),[[E,H.value.name]])]),f(`label`,null,[n[32]||=l(`说明`,-1),a(f(`textarea`,{"onUpdate:modelValue":n[4]||=e=>H.value.description=e,rows:`3`,maxlength:`16000`},null,512),[[E,H.value.description]])]),f(`fieldset`,null,[n[33]||=f(`legend`,null,`项目成员`,-1),(i(!0),h(u,null,s(J.value,e=>(i(),h(`label`,{key:e.id,class:`check`},[a(f(`input`,{"onUpdate:modelValue":n[5]||=e=>H.value.memberIds=e,type:`checkbox`,value:e.id},null,8,ge),[[T,H.value.memberIds]]),l(c(e.name),1)]))),128))]),f(`fieldset`,null,[n[34]||=f(`legend`,null,`关联群聊`,-1),n[35]||=f(`p`,null,`群内所有 Bot 需要先加入此项目。`,-1),(i(!0),h(u,null,s(d.conversations.filter(e=>e.kind===`group`&&!e.archived),e=>(i(),h(`label`,{key:e.id,class:`check`},[a(f(`input`,{"onUpdate:modelValue":n[6]||=e=>H.value.groupIds=e,type:`checkbox`,value:e.id,disabled:e.memberIds.some(e=>!H.value.memberIds.includes(e))||!!e.projectId&&e.projectId!==H.value.id},null,8,_e),[[T,H.value.groupIds]]),l(c(e.name),1)]))),128))]),H.value.id?(i(),h(`label`,ve,[a(f(`input`,{"onUpdate:modelValue":n[7]||=e=>H.value.archived=e,type:`checkbox`},null,512),[[T,H.value.archived]]),n[36]||=l(`归档项目`,-1)])):g(``,!0),f(`div`,ye,[f(`button`,{type:`submit`,disabled:j.value},`保存项目`,8,be),f(`button`,{type:`button`,onClick:n[8]||=e=>H.value=void 0},`取消`)])],32)):g(``,!0),d.embedded&&!H.value?(i(),h(u,{key:3},[L.value.length?(i(),h(`div`,xe,[(i(!0),h(u,null,s(L.value,e=>(i(),h(`button`,{key:e.id,type:`button`,"aria-pressed":F.value===e.id,onClick:t=>F.value=e.id},[b(x,{name:`files`,size:21}),f(`span`,null,[f(`strong`,null,c(e.name)+c(e.archived?` · 已归档`:``),1),f(`small`,null,c(e.memberIds.length)+` 位 Bot · `+c(e.groupIds.length)+` 个群聊`,1)]),b(_(O),{class:`project-chevron`,"aria-hidden":`true`})],8,Se))),128))])):g(``,!0),Y.value?(i(),h(`section`,Ce,[f(`h3`,null,c(Y.value.name),1),Y.value.description?(i(),h(`p`,we,c(Y.value.description),1)):g(``,!0),f(`div`,Te,[n[37]||=f(`span`,null,`项目成员`,-1),f(`div`,Ee,[(i(!0),h(u,null,s(Y.value.memberIds,e=>(i(),h(`span`,{key:e},[b(re,{name:Z(e),avatar:d.agents.find(t=>t.id===e)?.avatar||``,size:24},null,8,[`name`,`avatar`]),l(c(Z(e)),1)]))),128)),Y.value.memberIds.length?g(``,!0):(i(),h(`small`,De,`尚无成员`))])]),f(`div`,Oe,[n[38]||=f(`span`,null,`关联群聊`,-1),f(`div`,null,c(Y.value.groupIds.map(e=>d.conversations.find(t=>t.id===e)?.name??`原群聊`).join(`、`)||`尚未关联群聊`),1)]),f(`div`,ke,[n[39]||=f(`span`,null,`项目记忆`,-1),f(`div`,null,[f(`button`,{type:`button`,onClick:n[9]||=e=>It(`project`)},`查看记忆`)])]),f(`div`,Ae,[f(`button`,{type:`button`,class:`project-primary`,onClick:n[10]||=e=>Vt(Y.value)},`编辑项目`)])])):g(``,!0)],64)):d.embedded?g(``,!0):(i(!0),h(u,{key:4},s(L.value,e=>(i(),h(`article`,{key:e.id},[f(`h3`,null,c(e.name)+c(e.archived?` · 已归档`:``),1),f(`p`,null,c(e.description||`暂无说明`),1),f(`small`,null,c(e.memberIds.map(e=>Z(e)).join(`、`)||`尚无成员`)+` · `+c(e.groupIds.length)+` 个群`,1),f(`div`,je,[f(`button`,{onClick:t=>Vt(e)},`编辑`,8,Me),f(`button`,{onClick:t=>{F.value=e.id,It(`project`)}},`查看记忆`,8,Ne)])]))),128)),!L.value.length&&!H.value?(i(),h(`p`,Pe,`创建项目后,多个群可以共享该项目的决策和约定。`)):g(``,!0)],64)):N.value===`collaboration`?(i(),h(u,{key:3},[f(`p`,Fe,c(d.taskId?`当前话题`:`当前聊天`)+` · `+c(Dt.value.length)+` 条协作记录`,1),(i(!0),h(u,null,s(Dt.value,e=>(i(),h(`article`,{key:e.id},[f(`details`,null,[f(`summary`,null,c(e.title)+` · `+c(e.status),1),f(`p`,Ie,c(e.content),1),e.at?(i(),h(`small`,Le,c(new Date(e.at).toLocaleString()),1)):g(``,!0),e.error?(i(),h(`p`,Re,c(e.error),1)):g(``,!0)]),e.peer&&[`queued`,`running`,`waiting`].includes(e.peer.status)?(i(),h(`button`,{key:0,disabled:j.value,onClick:t=>$(async()=>{await _(C)(`/api/app/workspace/collaboration/${encodeURIComponent(e.peer.id)}/stop`,{method:`POST`})})},`停止此次协作`,8,ze)):g(``,!0)]))),128)),Dt.value.length?g(``,!0):(i(),h(`p`,Be,`暂无协作记录。你可以在聊天中让 Bot 联系同伴,或派发任务。`))],64)):(i(),h(u,{key:4},[P.value&&(X.value!==`project`||F.value)?(i(),m(se,{key:0,"agent-id":P.value,scope:X.value,"project-id":F.value,onDirtyChange:n[11]||=e=>K.value=e,onChanged:n[12]||=e=>{ie(`changed`),Q()}},null,8,[`agent-id`,`scope`,`project-id`])):g(``,!0),f(`div`,Ve,[[`agent`,`user`,`project`].includes(N.value)&&!d.selectedAgentId?(i(),h(`label`,He,[n[40]||=l(`记忆贡献 Bot`,-1),a(f(`select`,{disabled:K.value,"onUpdate:modelValue":n[13]||=e=>P.value=e,onChange:n[14]||=e=>{V.value=void 0,Q()}},[(i(!0),h(u,null,s(J.value,e=>(i(),h(`option`,{key:e.id,value:e.id},c(e.name),9,We))),128))],40,Ue),[[S,P.value]])])):g(``,!0),N.value===`project`?(i(),h(`label`,Ge,[n[41]||=l(`项目`,-1),a(f(`select`,{disabled:K.value,"onUpdate:modelValue":n[15]||=e=>F.value=e,onChange:n[16]||=e=>{V.value=void 0,Q()}},[(i(!0),h(u,null,s(L.value,e=>(i(),h(`option`,{key:e.id,value:e.id},c(e.name),9,qe))),128))],40,Ke),[[S,F.value]])])):g(``,!0),f(`form`,{onSubmit:n[18]||=w(e=>Q(),[`prevent`])},[f(`label`,null,[n[42]||=l(`搜索记忆`,-1),a(f(`input`,{"onUpdate:modelValue":n[17]||=e=>I.value=e,type:`search`,placeholder:`搜索事实、偏好或决策`},null,512),[[E,I.value]])]),n[43]||=f(`button`,{type:`submit`},`搜索`,-1)],32)]),f(`p`,Je,c(N.value===`agent`?`仅此 Bot 使用,可跨单聊和群聊延续。`:N.value===`user`?`同一账号的 Bot 共享,按贡献者保存。`:`仅「${Y.value?.name??`当前项目`}」成员读取。`),1),N.value===`agent`&&Et.value?(i(),h(u,{key:1},[d.hideAgentControls?g(``,!0):(i(),h(`label`,Ye,[f(`input`,{type:`checkbox`,checked:Et.value.memoryEnabled!==!1,disabled:j.value,onChange:n[19]||=e=>Ut(`memoryEnabled`,e.target.checked)},null,40,Xe),n[44]||=l(`自动记录长期事实`,-1)])),Et.value.memoryStatus===`upgrade_required`?(i(),h(`p`,Ze,`执行节点需要升级后才能启用隔离记忆;已保存的文件仍可管理。`)):g(``,!0)],64)):g(``,!0),N.value!==`project`||F.value?(i(),h(`div`,Qe,[f(`button`,{onClick:n[20]||=e=>Lt(),disabled:!J.value.length},`添加记忆`,8,$e),f(`a`,{href:`/api/app/workspace/memory-export?${Ot()}`,download:`bot-memory.md`},`导出 Markdown`,8,et)])):g(``,!0),V.value?(i(),h(`form`,{key:3,class:`knowledge-editor`,onSubmit:w(Rt,[`prevent`])},[f(`h3`,null,c(V.value.id?`编辑记忆`:`添加记忆`),1),!V.value.id&&N.value!==`agent`?(i(),h(`label`,tt,[n[45]||=l(`贡献 Bot`,-1),a(f(`select`,{"onUpdate:modelValue":n[21]||=e=>V.value.agentId=e},[(i(!0),h(u,null,s(J.value.filter(e=>N.value!==`project`||Y.value?.memberIds.includes(e.id)),e=>(i(),h(`option`,{key:e.id,value:e.id},c(e.name),9,nt))),128))],512),[[S,V.value.agentId]])])):g(``,!0),f(`label`,null,[n[46]||=l(`事实`,-1),a(f(`textarea`,{"onUpdate:modelValue":n[22]||=e=>V.value.content=e,required:``,rows:`4`,maxlength:`2000`},null,512),[[E,V.value.content]])]),f(`label`,null,[n[48]||=l(`类别`,-1),a(f(`select`,{"onUpdate:modelValue":n[23]||=e=>V.value.tier=e},[...n[47]||=[f(`option`,{value:`profile`},`长期事实`,-1),f(`option`,{value:`log`},`历史记录`,-1),f(`option`,{value:`note`},`补充笔记`,-1)]],512),[[S,V.value.tier]])]),f(`div`,rt,[f(`button`,{type:`submit`,disabled:j.value},`保存`,8,it),f(`button`,{type:`button`,onClick:n[24]||=e=>V.value=void 0},`取消`)])],32)):g(``,!0),(i(!0),h(u,null,s(R.value,e=>(i(),h(`article`,{key:`${e.scope}:${e.agentId}:${e.id}`},[f(`p`,at,c(e.content),1),e.conflict?(i(),h(`p`,ot,`同主题有不同贡献,请核对来源。`)):g(``,!0),f(`small`,null,c(Z(e.agentId))+` · `+c(e.origin===`manual`?`用户维护`:e.origin===`explicit`?`明确保存`:`自动提炼`)+` · `+c(new Date(e.updatedAt).toLocaleString())+` · 版本 `+c(e.revision),1),e.sources.length?(i(),h(`details`,st,[n[49]||=f(`summary`,null,`查看来源`,-1),(i(!0),h(u,null,s(e.sources,e=>(i(),h(`p`,{key:e.messageId},[f(`a`,{href:`/conversations/${e.conversationId}${e.taskId?`?taskId=${e.taskId}`:``}`},c(e.quote||`查看原话题`),9,ct),e.at?(i(),h(`small`,lt,` · 来源 `+c(new Date(e.at).toLocaleString()),1)):g(``,!0)]))),128))])):g(``,!0),f(`div`,ut,[f(`button`,{onClick:t=>Lt(e)},`编辑`,8,dt),f(`button`,{onClick:t=>Bt(e),disabled:j.value},`修订记录`,8,ft),f(`button`,{onClick:t=>zt(e),disabled:j.value},`遗忘`,8,pt)])]))),128)),R.value.length?(i(),h(`div`,mt,[f(`small`,ht,`已显示 `+c(R.value.length)+` / `+c(U.value)+` 条`,1),W.value===void 0?g(``,!0):(i(),h(`button`,{key:0,disabled:j.value,onClick:Nt},`加载更多记忆`,8,gt)),G.value?(i(),h(`button`,{key:1,disabled:j.value,onClick:n[25]||=e=>Q()},`刷新结果`,8,_t)):g(``,!0)])):g(``,!0),!R.value.length&&!V.value?(i(),h(`p`,vt,`暂无记忆。可手动添加,也可在对话中说明需要长期记住的事实。`)):g(``,!0),B.value.length?(i(),h(`section`,yt,[n[50]||=f(`h3`,null,`修订记录`,-1),(i(!0),h(u,null,s(B.value,e=>(i(),h(`article`,{key:e.id},[f(`small`,null,`版本 `+c(e.revision)+` · `+c(e.operation===`forget`?`遗忘`:`保存`)+` · `+c(new Date(e.at).toLocaleString()),1),f(`p`,null,c(e.after?.content??e.before?.content),1)]))),128)),f(`button`,{onClick:n[26]||=e=>B.value=[]},`收起`)])):g(``,!0),N.value===`agent`&&z.value.length?(i(),h(`details`,bt,[n[51]||=f(`summary`,null,`后台提炼记录`,-1),(i(!0),h(u,null,s(z.value,e=>(i(),h(`article`,{key:e.id},[f(`small`,null,c(new Date(e.createdAt).toLocaleString())+` · `+c(jt(e)),1),e.result?(i(),h(`p`,xt,`本次提炼 `+c(e.result.extractedCount)+` 条 · 新增保存 `+c(e.result.writtenCount)+` 条 · 重复 `+c(e.result.duplicateCount)+` 条 · 重试确认 `+c(e.result.replayedCount)+` 条`,1)):g(``,!0),(i(!0),h(u,null,s(Mt(e),e=>(i(),h(`p`,{key:e},c(e),1))),128)),e.error?(i(),h(`p`,St,c(e.error),1)):g(``,!0),e.status===`failed`?(i(),h(`button`,{key:2,disabled:j.value,onClick:t=>$(async()=>{await _(C)(`/api/app/workspace/memory-jobs/${e.id}/retry`,{method:`POST`})})},`重试`,8,Ct)):g(``,!0)]))),128))])):g(``,!0)],64))],8,fe)]),_:1},40,[`class`,`aria-labelledby`]))}}),[[`__scopeId`,`data-v-454b8349`]]);export{V as a,B as i,H as n,se as o,U as r,O as s,W as t};
3
+ //# sourceMappingURL=WorkspaceKnowledgePanel-E549Ct1E.js.map