compressed-context 0.1.0 → 0.1.1

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 (55) hide show
  1. package/package.json +64 -64
  2. package/src/compression/active-summary-cache.ts +50 -50
  3. package/src/compression/adopt.ts +57 -57
  4. package/src/compression/auto.ts +372 -372
  5. package/src/compression/babel.ts +85 -85
  6. package/src/compression/block.ts +25 -25
  7. package/src/compression/cache-history.ts +75 -75
  8. package/src/compression/compaction.ts +189 -189
  9. package/src/compression/decompress-tool.ts +74 -74
  10. package/src/compression/decompress.ts +292 -292
  11. package/src/compression/deleted-sessions.ts +24 -24
  12. package/src/compression/distill.ts +220 -220
  13. package/src/compression/events.ts +275 -275
  14. package/src/compression/markdown.ts +53 -53
  15. package/src/compression/normalize.ts +114 -114
  16. package/src/compression/render.ts +70 -70
  17. package/src/compression/schema.ts +84 -84
  18. package/src/compression/select.ts +123 -123
  19. package/src/compression/session-message.ts +112 -112
  20. package/src/compression/settings-rpc.ts +111 -111
  21. package/src/compression/settings.ts +175 -175
  22. package/src/compression/state.ts +248 -248
  23. package/src/compression/summarize.ts +595 -595
  24. package/src/compression/summary.ts +73 -73
  25. package/src/compression/tool.ts +142 -142
  26. package/src/compression/transcript-store.ts +258 -258
  27. package/src/compression/transcript.ts +63 -63
  28. package/src/compression/validate.ts +81 -81
  29. package/src/i18n/en.ts +93 -93
  30. package/src/i18n/index.ts +28 -28
  31. package/src/i18n/types.ts +89 -89
  32. package/src/i18n/zh.ts +93 -93
  33. package/src/index.ts +108 -108
  34. package/src/shared/blocks.ts +17 -17
  35. package/src/shared/cache.ts +199 -199
  36. package/src/shared/format.ts +23 -23
  37. package/src/shared/media.ts +184 -184
  38. package/src/shared/parse.ts +25 -25
  39. package/src/shared/parts.ts +72 -72
  40. package/src/shared/rpc.ts +145 -145
  41. package/src/shared/scan.ts +32 -32
  42. package/src/shared/settings.ts +471 -471
  43. package/src/shared/stats.ts +142 -142
  44. package/src/shared/tokens.ts +259 -259
  45. package/src/tui/chart/cache-chart.ts +237 -237
  46. package/src/tui/chart/color.ts +58 -58
  47. package/src/tui/index.tsx +111 -111
  48. package/src/tui/panel/cache.tsx +203 -203
  49. package/src/tui/panel/data.ts +105 -105
  50. package/src/tui/panel/index.tsx +176 -176
  51. package/src/tui/panel/summaries.tsx +192 -192
  52. package/src/tui/settings/dialogs.tsx +215 -215
  53. package/src/tui/settings/options.ts +238 -238
  54. package/src/tui/settings.tsx +418 -418
  55. package/src/tui/types.ts +4 -4
package/src/tui/index.tsx CHANGED
@@ -1,111 +1,111 @@
1
- import { Plugin } from "@opencode/plugin/tui"
2
-
3
- import { initialSettings, normalizeSettings, type Settings } from "../shared/settings.ts"
4
- import { Panel } from "./panel/index.tsx"
5
- import { SettingsCommand } from "./settings.tsx"
6
- import type { UpdateSettings } from "./types.ts"
7
-
8
- /**
9
- * 输入:当前设置 + 写入函数。
10
- * 输出:无。
11
- * 预期:旧版本存下来的设置可能缺字段(例如没有 contextLimit),补齐并写回。
12
- */
13
- function migrate(settings: Settings, update: UpdateSettings): void {
14
- const normalized = normalizeSettings({
15
- enabled: settings.enabled,
16
- expanded: settings.expanded,
17
- locale: settings.locale,
18
- contextLimit: settings.contextLimit,
19
- triggerPercent: settings.triggerPercent,
20
- summaryBlockPercent: settings.summaryBlockPercent,
21
- summaryTotalPercent: settings.summaryTotalPercent,
22
- summaryLanguage: settings.summaryLanguage,
23
- summaryFormat: settings.summaryFormat,
24
- autoCompress: settings.autoCompress,
25
- enableCompressTool: settings.enableCompressTool,
26
- enableDecompressTool: settings.enableDecompressTool,
27
- showTimeline: settings.showTimeline,
28
- showBlockTokens: settings.showBlockTokens,
29
- cacheExpanded: settings.cacheExpanded,
30
- showCacheChart: settings.showCacheChart,
31
- cacheChartExpanded: settings.cacheChartExpanded,
32
- showCacheLog: settings.showCacheLog,
33
- cacheLogExpanded: settings.cacheLogExpanded,
34
- showSummaries: settings.showSummaries,
35
- summariesExpanded: settings.summariesExpanded,
36
- ...(settings.expandedBlockId === undefined ? {} : { expandedBlockId: settings.expandedBlockId }),
37
- decompressInlineMax: settings.decompressInlineMax,
38
- decompressTempCap: settings.decompressTempCap,
39
- decompressShowTokens: settings.decompressShowTokens,
40
- })
41
- if (
42
- settings.enabled === normalized.enabled &&
43
- settings.expanded === normalized.expanded &&
44
- settings.locale === normalized.locale &&
45
- settings.contextLimit === normalized.contextLimit &&
46
- settings.triggerPercent === normalized.triggerPercent &&
47
- settings.summaryBlockPercent === normalized.summaryBlockPercent &&
48
- settings.summaryTotalPercent === normalized.summaryTotalPercent &&
49
- settings.summaryLanguage === normalized.summaryLanguage &&
50
- settings.summaryFormat === normalized.summaryFormat &&
51
- settings.autoCompress === normalized.autoCompress &&
52
- settings.enableCompressTool === normalized.enableCompressTool &&
53
- settings.enableDecompressTool === normalized.enableDecompressTool &&
54
- settings.showTimeline === normalized.showTimeline &&
55
- settings.showBlockTokens === normalized.showBlockTokens &&
56
- settings.cacheExpanded === normalized.cacheExpanded &&
57
- settings.showCacheChart === normalized.showCacheChart &&
58
- settings.cacheChartExpanded === normalized.cacheChartExpanded &&
59
- settings.showCacheLog === normalized.showCacheLog &&
60
- settings.cacheLogExpanded === normalized.cacheLogExpanded &&
61
- settings.showSummaries === normalized.showSummaries &&
62
- settings.summariesExpanded === normalized.summariesExpanded &&
63
- settings.expandedBlockId === normalized.expandedBlockId &&
64
- settings.decompressInlineMax === normalized.decompressInlineMax &&
65
- settings.decompressTempCap === normalized.decompressTempCap &&
66
- settings.decompressShowTokens === normalized.decompressShowTokens
67
- ) {
68
- return
69
- }
70
- void update((draft) => {
71
- Object.assign(draft, normalized)
72
- })
73
- }
74
-
75
- export default Plugin.define({
76
- id: "compressed-context.tui",
77
-
78
- /**
79
- * 输入:TUI 插件上下文(含插件 options)。
80
- * 输出:侧栏面板 + /compressed-context 设置命令。
81
- * 预期:设置存在 TUI storage 里;options 只作为首次默认值。
82
- */
83
- setup(context) {
84
- const [settings, updateSettings] = context.storage.store<Settings>("settings", {
85
- initial: initialSettings(context.options),
86
- })
87
- migrate(settings, updateSettings)
88
-
89
- const disposePanel = context.ui.slot({
90
- append: "sidebar.content",
91
- render: (input) => (
92
- <Panel
93
- context={context}
94
- sessionID={input.sessionID}
95
- settings={settings}
96
- updateSettings={updateSettings}
97
- />
98
- ),
99
- })
100
-
101
- const disposeCommand = context.ui.slot({
102
- append: "app",
103
- render: () => <SettingsCommand context={context} settings={settings} updateSettings={updateSettings} />,
104
- })
105
-
106
- return () => {
107
- disposePanel()
108
- disposeCommand()
109
- }
110
- },
111
- })
1
+ import { Plugin } from "@opencode/plugin/tui"
2
+
3
+ import { initialSettings, normalizeSettings, type Settings } from "../shared/settings.ts"
4
+ import { Panel } from "./panel/index.tsx"
5
+ import { SettingsCommand } from "./settings.tsx"
6
+ import type { UpdateSettings } from "./types.ts"
7
+
8
+ /**
9
+ * 输入:当前设置 + 写入函数。
10
+ * 输出:无。
11
+ * 预期:旧版本存下来的设置可能缺字段(例如没有 contextLimit),补齐并写回。
12
+ */
13
+ function migrate(settings: Settings, update: UpdateSettings): void {
14
+ const normalized = normalizeSettings({
15
+ enabled: settings.enabled,
16
+ expanded: settings.expanded,
17
+ locale: settings.locale,
18
+ contextLimit: settings.contextLimit,
19
+ triggerPercent: settings.triggerPercent,
20
+ summaryBlockPercent: settings.summaryBlockPercent,
21
+ summaryTotalPercent: settings.summaryTotalPercent,
22
+ summaryLanguage: settings.summaryLanguage,
23
+ summaryFormat: settings.summaryFormat,
24
+ autoCompress: settings.autoCompress,
25
+ enableCompressTool: settings.enableCompressTool,
26
+ enableDecompressTool: settings.enableDecompressTool,
27
+ showTimeline: settings.showTimeline,
28
+ showBlockTokens: settings.showBlockTokens,
29
+ cacheExpanded: settings.cacheExpanded,
30
+ showCacheChart: settings.showCacheChart,
31
+ cacheChartExpanded: settings.cacheChartExpanded,
32
+ showCacheLog: settings.showCacheLog,
33
+ cacheLogExpanded: settings.cacheLogExpanded,
34
+ showSummaries: settings.showSummaries,
35
+ summariesExpanded: settings.summariesExpanded,
36
+ ...(settings.expandedBlockId === undefined ? {} : { expandedBlockId: settings.expandedBlockId }),
37
+ decompressInlineMax: settings.decompressInlineMax,
38
+ decompressTempCap: settings.decompressTempCap,
39
+ decompressShowTokens: settings.decompressShowTokens,
40
+ })
41
+ if (
42
+ settings.enabled === normalized.enabled &&
43
+ settings.expanded === normalized.expanded &&
44
+ settings.locale === normalized.locale &&
45
+ settings.contextLimit === normalized.contextLimit &&
46
+ settings.triggerPercent === normalized.triggerPercent &&
47
+ settings.summaryBlockPercent === normalized.summaryBlockPercent &&
48
+ settings.summaryTotalPercent === normalized.summaryTotalPercent &&
49
+ settings.summaryLanguage === normalized.summaryLanguage &&
50
+ settings.summaryFormat === normalized.summaryFormat &&
51
+ settings.autoCompress === normalized.autoCompress &&
52
+ settings.enableCompressTool === normalized.enableCompressTool &&
53
+ settings.enableDecompressTool === normalized.enableDecompressTool &&
54
+ settings.showTimeline === normalized.showTimeline &&
55
+ settings.showBlockTokens === normalized.showBlockTokens &&
56
+ settings.cacheExpanded === normalized.cacheExpanded &&
57
+ settings.showCacheChart === normalized.showCacheChart &&
58
+ settings.cacheChartExpanded === normalized.cacheChartExpanded &&
59
+ settings.showCacheLog === normalized.showCacheLog &&
60
+ settings.cacheLogExpanded === normalized.cacheLogExpanded &&
61
+ settings.showSummaries === normalized.showSummaries &&
62
+ settings.summariesExpanded === normalized.summariesExpanded &&
63
+ settings.expandedBlockId === normalized.expandedBlockId &&
64
+ settings.decompressInlineMax === normalized.decompressInlineMax &&
65
+ settings.decompressTempCap === normalized.decompressTempCap &&
66
+ settings.decompressShowTokens === normalized.decompressShowTokens
67
+ ) {
68
+ return
69
+ }
70
+ void update((draft) => {
71
+ Object.assign(draft, normalized)
72
+ })
73
+ }
74
+
75
+ export default Plugin.define({
76
+ id: "compressed-context.tui",
77
+
78
+ /**
79
+ * 输入:TUI 插件上下文(含插件 options)。
80
+ * 输出:侧栏面板 + /compressed-context 设置命令。
81
+ * 预期:设置存在 TUI storage 里;options 只作为首次默认值。
82
+ */
83
+ setup(context) {
84
+ const [settings, updateSettings] = context.storage.store<Settings>("settings", {
85
+ initial: initialSettings(context.options),
86
+ })
87
+ migrate(settings, updateSettings)
88
+
89
+ const disposePanel = context.ui.slot({
90
+ append: "sidebar.content",
91
+ render: (input) => (
92
+ <Panel
93
+ context={context}
94
+ sessionID={input.sessionID}
95
+ settings={settings}
96
+ updateSettings={updateSettings}
97
+ />
98
+ ),
99
+ })
100
+
101
+ const disposeCommand = context.ui.slot({
102
+ append: "app",
103
+ render: () => <SettingsCommand context={context} settings={settings} updateSettings={updateSettings} />,
104
+ })
105
+
106
+ return () => {
107
+ disposePanel()
108
+ disposeCommand()
109
+ }
110
+ },
111
+ })
@@ -1,203 +1,203 @@
1
- import type { Context } from "@opencode/plugin/tui/context"
2
- import { createMemo, For, Show } from "solid-js"
3
-
4
- import type { Messages } from "../../i18n/index.ts"
5
- import { formatHitRate, type CacheSample } from "../../shared/cache.ts"
6
- import { formatTokens } from "../../shared/format.ts"
7
- import type { Settings } from "../../shared/settings.ts"
8
- import {
9
- CHART_HEIGHT,
10
- CHART_WIDTH,
11
- chartAxis,
12
- hitRateColor,
13
- renderBrailleChart,
14
- xAxisLabels,
15
- type ChartCell,
16
- } from "../chart/cache-chart.ts"
17
- import { themeHex } from "../chart/color.ts"
18
- import type { UpdateSettings } from "../types.ts"
19
-
20
- /** 输入:一行 chart cell;输出:可直接放进 `<text>` 的 span 序列。 */
21
- function chartLine(row: readonly ChartCell[]) {
22
- return row.map((cell) =>
23
- cell.color === "" ? cell.char : <span style={{ fg: cell.color }}>{cell.char}</span>,
24
- )
25
- }
26
-
27
- /**
28
- * 输入:主题 + 文案 + 设置 + 写入函数 + 缓存样本。
29
- * 输出:可折叠的缓存记录区(Braille 折线 + 最近 10 次日志)。
30
- * 预期:样本由服务端持久化,这里只负责展示;0% 红 → 100% 绿。
31
- */
32
- export function CacheSection(props: {
33
- readonly theme: Context["theme"]
34
- readonly copy: Messages
35
- readonly settings: Settings
36
- readonly updateSettings: UpdateSettings
37
- readonly samples: readonly CacheSample[]
38
- }) {
39
- const chartColors = createMemo(() => ({
40
- success: themeHex(props.theme.text.feedback.success.base, [34, 197, 94]),
41
- warning: themeHex(props.theme.text.feedback.warning.base, [234, 179, 8]),
42
- error: themeHex(props.theme.text.feedback.error.base, [239, 68, 68]),
43
- }))
44
-
45
- const latestHit = createMemo(() => props.samples.at(-1)?.hitRate)
46
- const axis = chartAxis(CHART_HEIGHT)
47
- const chartRows = createMemo(() => {
48
- if (props.samples.length === 0) return []
49
- return renderBrailleChart(
50
- props.samples.map((sample) => sample.hitRate),
51
- CHART_WIDTH,
52
- CHART_HEIGHT,
53
- (pct) => hitRateColor(pct, chartColors()),
54
- axis.gridRows,
55
- themeHex(props.theme.text.muted, [128, 128, 128]),
56
- )
57
- })
58
-
59
- /** 输入:折叠键;输出:无;预期:翻转后写回 TUI storage。 */
60
- const toggleFold = (
61
- key: "cacheExpanded" | "cacheChartExpanded" | "cacheLogExpanded",
62
- ): void => {
63
- void props.updateSettings((draft) => {
64
- draft[key] = !draft[key]
65
- })
66
- }
67
-
68
- return (
69
- <Show when={props.settings.showCacheChart || props.settings.showCacheLog}>
70
- <box width="100%" flexDirection="column">
71
- <box
72
- flexDirection="row"
73
- height={1}
74
- onMouseDown={() => {
75
- toggleFold("cacheExpanded")
76
- }}
77
- >
78
- <box flexDirection="row" gap={1}>
79
- <text fg={props.theme.text.base}>{props.settings.cacheExpanded ? "▼" : "▶"}</text>
80
- <text fg={props.theme.text.base}>
81
- <b>{props.copy.cacheTitle}</b>
82
- </text>
83
- <Show
84
- when={latestHit() !== undefined}
85
- fallback={<text fg={props.theme.text.muted}>{"(--)"}</text>}
86
- >
87
- <text fg={hitRateColor(latestHit() ?? 0, chartColors())}>
88
- {`(${formatHitRate(latestHit() ?? 0)}%)`}
89
- </text>
90
- </Show>
91
- </box>
92
- </box>
93
- <Show when={props.settings.cacheExpanded}>
94
- <box paddingLeft={2} flexDirection="column" width="100%">
95
- <Show when={props.settings.showCacheChart}>
96
- <box flexDirection="column" width="100%">
97
- <box
98
- flexDirection="row"
99
- height={1}
100
- gap={1}
101
- onMouseDown={() => {
102
- toggleFold("cacheChartExpanded")
103
- }}
104
- >
105
- <text fg={props.theme.text.base}>
106
- {props.settings.cacheChartExpanded ? "▼" : "▶"}
107
- </text>
108
- <text fg={props.theme.text.base}>{props.copy.cacheChart}</text>
109
- </box>
110
- <Show when={props.settings.cacheChartExpanded}>
111
- <Show
112
- when={chartRows().length > 0}
113
- fallback={<text fg={props.theme.text.muted}>{props.copy.noData}</text>}
114
- >
115
- <For each={chartRows()}>
116
- {(row, index) => {
117
- const label = axis.labels.find((item) => item.row === index())?.text ?? " "
118
- return (
119
- <box flexDirection="row" height={1}>
120
- <text fg={props.theme.text.muted}>{`${label} `}</text>
121
- <text fg={props.theme.text.muted}>{"┤"}</text>
122
- <text>{chartLine(row)}</text>
123
- </box>
124
- )
125
- }}
126
- </For>
127
- <text fg={props.theme.text.muted}>{` 0% └${"─".repeat(CHART_WIDTH)}`}</text>
128
- <text fg={props.theme.text.muted}>
129
- {`${" ".repeat(6)}${xAxisLabels(props.samples.length, CHART_WIDTH)}`}
130
- </text>
131
- </Show>
132
- </Show>
133
- </box>
134
- </Show>
135
-
136
- <Show when={props.settings.showCacheLog}>
137
- <box flexDirection="column" width="100%">
138
- <box
139
- flexDirection="row"
140
- height={1}
141
- gap={1}
142
- onMouseDown={() => {
143
- toggleFold("cacheLogExpanded")
144
- }}
145
- >
146
- <text fg={props.theme.text.base}>
147
- {props.settings.cacheLogExpanded ? "▼" : "▶"}
148
- </text>
149
- <text fg={props.theme.text.base}>{props.copy.cacheLog}</text>
150
- </box>
151
- <Show when={props.settings.cacheLogExpanded}>
152
- <Show
153
- when={props.samples.length > 0}
154
- fallback={<text fg={props.theme.text.muted}>{props.copy.noData}</text>}
155
- >
156
- <box flexDirection="column" width="100%" paddingLeft={2}>
157
- <box flexDirection="row" gap={1}>
158
- <text width={5} textAlign="left" fg={props.theme.text.muted}>
159
- {props.copy.cacheInput}
160
- </text>
161
- <text width={5} textAlign="left" fg={props.theme.text.muted}>
162
- {props.copy.cacheOutput}
163
- </text>
164
- <text width={5} textAlign="left" fg={props.theme.text.muted}>
165
- {props.copy.cacheCached}
166
- </text>
167
- <text width={7} textAlign="left" fg={props.theme.text.muted}>
168
- {props.copy.cacheHit}
169
- </text>
170
- </box>
171
- <For each={props.samples}>
172
- {(sample) => (
173
- <box flexDirection="row" gap={1}>
174
- <text width={5} textAlign="left" fg={props.theme.text.muted}>
175
- {formatTokens(sample.input)}
176
- </text>
177
- <text width={5} textAlign="left" fg={props.theme.text.muted}>
178
- {formatTokens(sample.output)}
179
- </text>
180
- <text width={5} textAlign="left" fg={props.theme.text.muted}>
181
- {formatTokens(sample.cached)}
182
- </text>
183
- <text
184
- width={7}
185
- textAlign="left"
186
- fg={hitRateColor(sample.hitRate, chartColors())}
187
- >
188
- {`${formatHitRate(sample.hitRate)}%`}
189
- </text>
190
- </box>
191
- )}
192
- </For>
193
- </box>
194
- </Show>
195
- </Show>
196
- </box>
197
- </Show>
198
- </box>
199
- </Show>
200
- </box>
201
- </Show>
202
- )
203
- }
1
+ import type { Context } from "@opencode/plugin/tui/context"
2
+ import { createMemo, For, Show } from "solid-js"
3
+
4
+ import type { Messages } from "../../i18n/index.ts"
5
+ import { formatHitRate, type CacheSample } from "../../shared/cache.ts"
6
+ import { formatTokens } from "../../shared/format.ts"
7
+ import type { Settings } from "../../shared/settings.ts"
8
+ import {
9
+ CHART_HEIGHT,
10
+ CHART_WIDTH,
11
+ chartAxis,
12
+ hitRateColor,
13
+ renderBrailleChart,
14
+ xAxisLabels,
15
+ type ChartCell,
16
+ } from "../chart/cache-chart.ts"
17
+ import { themeHex } from "../chart/color.ts"
18
+ import type { UpdateSettings } from "../types.ts"
19
+
20
+ /** 输入:一行 chart cell;输出:可直接放进 `<text>` 的 span 序列。 */
21
+ function chartLine(row: readonly ChartCell[]) {
22
+ return row.map((cell) =>
23
+ cell.color === "" ? cell.char : <span style={{ fg: cell.color }}>{cell.char}</span>,
24
+ )
25
+ }
26
+
27
+ /**
28
+ * 输入:主题 + 文案 + 设置 + 写入函数 + 缓存样本。
29
+ * 输出:可折叠的缓存记录区(Braille 折线 + 最近 10 次日志)。
30
+ * 预期:样本由服务端持久化,这里只负责展示;0% 红 → 100% 绿。
31
+ */
32
+ export function CacheSection(props: {
33
+ readonly theme: Context["theme"]
34
+ readonly copy: Messages
35
+ readonly settings: Settings
36
+ readonly updateSettings: UpdateSettings
37
+ readonly samples: readonly CacheSample[]
38
+ }) {
39
+ const chartColors = createMemo(() => ({
40
+ success: themeHex(props.theme.text.feedback.success.base, [34, 197, 94]),
41
+ warning: themeHex(props.theme.text.feedback.warning.base, [234, 179, 8]),
42
+ error: themeHex(props.theme.text.feedback.error.base, [239, 68, 68]),
43
+ }))
44
+
45
+ const latestHit = createMemo(() => props.samples.at(-1)?.hitRate)
46
+ const axis = chartAxis(CHART_HEIGHT)
47
+ const chartRows = createMemo(() => {
48
+ if (props.samples.length === 0) return []
49
+ return renderBrailleChart(
50
+ props.samples.map((sample) => sample.hitRate),
51
+ CHART_WIDTH,
52
+ CHART_HEIGHT,
53
+ (pct) => hitRateColor(pct, chartColors()),
54
+ axis.gridRows,
55
+ themeHex(props.theme.text.muted, [128, 128, 128]),
56
+ )
57
+ })
58
+
59
+ /** 输入:折叠键;输出:无;预期:翻转后写回 TUI storage。 */
60
+ const toggleFold = (
61
+ key: "cacheExpanded" | "cacheChartExpanded" | "cacheLogExpanded",
62
+ ): void => {
63
+ void props.updateSettings((draft) => {
64
+ draft[key] = !draft[key]
65
+ })
66
+ }
67
+
68
+ return (
69
+ <Show when={props.settings.showCacheChart || props.settings.showCacheLog}>
70
+ <box width="100%" flexDirection="column">
71
+ <box
72
+ flexDirection="row"
73
+ height={1}
74
+ onMouseDown={() => {
75
+ toggleFold("cacheExpanded")
76
+ }}
77
+ >
78
+ <box flexDirection="row" gap={1}>
79
+ <text fg={props.theme.text.base}>{props.settings.cacheExpanded ? "▼" : "▶"}</text>
80
+ <text fg={props.theme.text.base}>
81
+ <b>{props.copy.cacheTitle}</b>
82
+ </text>
83
+ <Show
84
+ when={latestHit() !== undefined}
85
+ fallback={<text fg={props.theme.text.muted}>{"(--)"}</text>}
86
+ >
87
+ <text fg={hitRateColor(latestHit() ?? 0, chartColors())}>
88
+ {`(${formatHitRate(latestHit() ?? 0)}%)`}
89
+ </text>
90
+ </Show>
91
+ </box>
92
+ </box>
93
+ <Show when={props.settings.cacheExpanded}>
94
+ <box paddingLeft={2} flexDirection="column" width="100%">
95
+ <Show when={props.settings.showCacheChart}>
96
+ <box flexDirection="column" width="100%">
97
+ <box
98
+ flexDirection="row"
99
+ height={1}
100
+ gap={1}
101
+ onMouseDown={() => {
102
+ toggleFold("cacheChartExpanded")
103
+ }}
104
+ >
105
+ <text fg={props.theme.text.base}>
106
+ {props.settings.cacheChartExpanded ? "▼" : "▶"}
107
+ </text>
108
+ <text fg={props.theme.text.base}>{props.copy.cacheChart}</text>
109
+ </box>
110
+ <Show when={props.settings.cacheChartExpanded}>
111
+ <Show
112
+ when={chartRows().length > 0}
113
+ fallback={<text fg={props.theme.text.muted}>{props.copy.noData}</text>}
114
+ >
115
+ <For each={chartRows()}>
116
+ {(row, index) => {
117
+ const label = axis.labels.find((item) => item.row === index())?.text ?? " "
118
+ return (
119
+ <box flexDirection="row" height={1}>
120
+ <text fg={props.theme.text.muted}>{`${label} `}</text>
121
+ <text fg={props.theme.text.muted}>{"┤"}</text>
122
+ <text>{chartLine(row)}</text>
123
+ </box>
124
+ )
125
+ }}
126
+ </For>
127
+ <text fg={props.theme.text.muted}>{` 0% └${"─".repeat(CHART_WIDTH)}`}</text>
128
+ <text fg={props.theme.text.muted}>
129
+ {`${" ".repeat(6)}${xAxisLabels(props.samples.length, CHART_WIDTH)}`}
130
+ </text>
131
+ </Show>
132
+ </Show>
133
+ </box>
134
+ </Show>
135
+
136
+ <Show when={props.settings.showCacheLog}>
137
+ <box flexDirection="column" width="100%">
138
+ <box
139
+ flexDirection="row"
140
+ height={1}
141
+ gap={1}
142
+ onMouseDown={() => {
143
+ toggleFold("cacheLogExpanded")
144
+ }}
145
+ >
146
+ <text fg={props.theme.text.base}>
147
+ {props.settings.cacheLogExpanded ? "▼" : "▶"}
148
+ </text>
149
+ <text fg={props.theme.text.base}>{props.copy.cacheLog}</text>
150
+ </box>
151
+ <Show when={props.settings.cacheLogExpanded}>
152
+ <Show
153
+ when={props.samples.length > 0}
154
+ fallback={<text fg={props.theme.text.muted}>{props.copy.noData}</text>}
155
+ >
156
+ <box flexDirection="column" width="100%" paddingLeft={2}>
157
+ <box flexDirection="row" gap={1}>
158
+ <text width={5} textAlign="left" fg={props.theme.text.muted}>
159
+ {props.copy.cacheInput}
160
+ </text>
161
+ <text width={5} textAlign="left" fg={props.theme.text.muted}>
162
+ {props.copy.cacheOutput}
163
+ </text>
164
+ <text width={5} textAlign="left" fg={props.theme.text.muted}>
165
+ {props.copy.cacheCached}
166
+ </text>
167
+ <text width={7} textAlign="left" fg={props.theme.text.muted}>
168
+ {props.copy.cacheHit}
169
+ </text>
170
+ </box>
171
+ <For each={props.samples}>
172
+ {(sample) => (
173
+ <box flexDirection="row" gap={1}>
174
+ <text width={5} textAlign="left" fg={props.theme.text.muted}>
175
+ {formatTokens(sample.input)}
176
+ </text>
177
+ <text width={5} textAlign="left" fg={props.theme.text.muted}>
178
+ {formatTokens(sample.output)}
179
+ </text>
180
+ <text width={5} textAlign="left" fg={props.theme.text.muted}>
181
+ {formatTokens(sample.cached)}
182
+ </text>
183
+ <text
184
+ width={7}
185
+ textAlign="left"
186
+ fg={hitRateColor(sample.hitRate, chartColors())}
187
+ >
188
+ {`${formatHitRate(sample.hitRate)}%`}
189
+ </text>
190
+ </box>
191
+ )}
192
+ </For>
193
+ </box>
194
+ </Show>
195
+ </Show>
196
+ </box>
197
+ </Show>
198
+ </box>
199
+ </Show>
200
+ </box>
201
+ </Show>
202
+ )
203
+ }