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
@@ -1,192 +1,192 @@
1
- import type { Context } from "@opencode/plugin/tui/context"
2
- import { SyntaxStyle, type RGBA } from "@opentui/core"
3
- import { createEffect, createMemo, createSignal, For, onCleanup, Show } from "solid-js"
4
-
5
- import type { Messages } from "../../i18n/index.ts"
6
- import type { SummaryBlock } from "../../shared/blocks.ts"
7
- import { formatTimestamp, formatTokens } from "../../shared/format.ts"
8
- import type { Settings } from "../../shared/settings.ts"
9
- import type { UpdateSettings } from "../types.ts"
10
-
11
- /** 输入:摘要等级;输出:对应主题色;T1 蓝、T2 黄、T3 红。 */
12
- function tierColor(tier: SummaryBlock["tier"], theme: Context["theme"]): RGBA {
13
- if (tier === 1) return theme.text.feedback.info.base
14
- if (tier === 2) return theme.text.feedback.warning.base
15
- return theme.text.feedback.error.base
16
- }
17
-
18
- /** 输入:无;输出:共享的 SyntaxStyle;预期:markdown 渲染器需要它,懒加载避免重复创建。 */
19
- let sharedSyntaxStyle: SyntaxStyle | undefined
20
- function getSyntaxStyle(): SyntaxStyle {
21
- sharedSyntaxStyle ??= SyntaxStyle.create()
22
- return sharedSyntaxStyle
23
- }
24
-
25
- /**
26
- * 输入:TUI 上下文 + 主题 + 块。
27
- * 输出:占满可用高度的阅读弹窗。
28
- * 预期:标题保持 `[T1] b1 : title`;Babel 按纯文本显示,其余交给 markdown 渲染器,esc 关闭。
29
- */
30
- export function SummaryDialog(props: {
31
- readonly context: Context
32
- readonly theme: Context["theme"]
33
- readonly block: SummaryBlock
34
- }) {
35
- const height = createMemo(() => Math.max(8, Math.min(30, props.context.renderer.height - 8)))
36
- return (
37
- <box paddingLeft={3} paddingRight={3} paddingBottom={1} gap={1}>
38
- <box flexDirection="row" justifyContent="space-between" gap={1}>
39
- <text fg={props.theme.text.base} wrapMode="word">
40
- <b>{`[T${props.block.tier.toString()}] ${props.block.id} : ${props.block.title}`}</b>
41
- </text>
42
- <text
43
- fg={props.theme.text.muted}
44
- onMouseUp={() => {
45
- props.context.ui.dialog.clear()
46
- }}
47
- >
48
- esc
49
- </text>
50
- </box>
51
- <scrollbox height={height()} scrollY={true} focused={true} width="100%">
52
- <Show
53
- when={props.block.format === "babel"}
54
- fallback={
55
- <markdown
56
- content={props.block.body}
57
- syntaxStyle={getSyntaxStyle()}
58
- fg={props.theme.text.base}
59
- conceal={true}
60
- tableOptions={{ widthMode: "full", style: "columns" }}
61
- />
62
- }
63
- >
64
- <text fg={props.theme.text.base} wrapMode="word">
65
- {props.block.body}
66
- </text>
67
- </Show>
68
- </scrollbox>
69
- </box>
70
- )
71
- }
72
-
73
- /**
74
- * 输入:主题 + 文案 + 设置 + 写入函数 + 摘要块。
75
- * 输出:可折叠的摘要块列表;点击标题打开 markdown 阅读弹窗。
76
- * 预期:只展示服务端返回的 active 链;hover 只让箭头动,标题和背景保持稳定。
77
- */
78
- export function SummarySection(props: {
79
- readonly context: Context
80
- readonly theme: Context["theme"]
81
- readonly copy: Messages
82
- readonly settings: Settings
83
- readonly updateSettings: UpdateSettings
84
- readonly blocks: readonly SummaryBlock[]
85
- }) {
86
- const [hovered, setHovered] = createSignal<string | undefined>(undefined)
87
- const [frame, setFrame] = createSignal(0)
88
-
89
- // 只有 hover 时跑两帧;鼠标移出或销毁时清理,避免后台定时器。
90
- createEffect(() => {
91
- if (hovered() === undefined) {
92
- setFrame(0)
93
- return
94
- }
95
- const timer = setInterval(() => setFrame((value) => (value + 1) % 2), 180)
96
- onCleanup(() => {
97
- clearInterval(timer)
98
- })
99
- })
100
-
101
- /** 输入:无;输出:无;预期:翻转摘要区折叠状态。 */
102
- const toggleFold = (): void => {
103
- void props.updateSettings((draft) => {
104
- draft.summariesExpanded = !draft.summariesExpanded
105
- })
106
- }
107
-
108
- /** 输入:块;输出:无;预期:打开该块的 markdown 阅读弹窗。 */
109
- const openSummary = (block: SummaryBlock): void => {
110
- props.context.ui.dialog.show(() => (
111
- <SummaryDialog context={props.context} theme={props.theme} block={block} />
112
- ))
113
- props.context.ui.dialog.set({ size: "large", centered: true })
114
- }
115
-
116
- return (
117
- <Show when={props.settings.showSummaries}>
118
- <box width="100%" flexDirection="column">
119
- <box flexDirection="row" height={1} onMouseDown={toggleFold}>
120
- <box flexDirection="row" gap={1}>
121
- <text fg={props.theme.text.base}>{props.settings.summariesExpanded ? "▼" : "▶"}</text>
122
- <text fg={props.theme.text.base}>
123
- <b>{props.copy.summariesTitle}</b>
124
- </text>
125
- <text fg={props.theme.text.muted}>{`(${props.blocks.length.toString()})`}</text>
126
- </box>
127
- </box>
128
- <Show when={props.settings.summariesExpanded}>
129
- <Show
130
- when={props.blocks.length > 0}
131
- fallback={<text fg={props.theme.text.muted}>{props.copy.noData}</text>}
132
- >
133
- <box paddingLeft={2} flexDirection="column" width="100%">
134
- <For each={props.blocks}>
135
- {(block) => {
136
- const meta = (): string => {
137
- const parts: string[] = []
138
- if (props.settings.showTimeline) parts.push(formatTimestamp(block.at))
139
- if (block.sourceBlockIds !== undefined && block.sourceBlockIds.length > 0) {
140
- parts.push(`merged ${block.sourceBlockIds.map((id) => `#${id}`).join(" + ")}`)
141
- }
142
- return parts.join(" ")
143
- }
144
- return (
145
- <box flexDirection="column" width="100%">
146
- <box
147
- flexDirection="row"
148
- gap={1}
149
- onMouseOver={() => {
150
- setHovered(block.id)
151
- }}
152
- onMouseOut={() => {
153
- setHovered((current) => (current === block.id ? undefined : current))
154
- }}
155
- onMouseUp={() => {
156
- openSummary(block)
157
- }}
158
- >
159
- <text width={2} fg={tierColor(block.tier, props.theme)}>
160
- {hovered() === block.id ? (frame() === 0 ? "▸ " : " ▸") : " "}
161
- </text>
162
- <text fg={props.theme.text.base} wrapMode="word" flexGrow={1}>
163
- <span style={{ fg: tierColor(block.tier, props.theme) }}>
164
- <b>{`[T${block.tier.toString()}]`}</b>
165
- </span>
166
- <span style={{ fg: props.theme.text.muted }}>
167
- {` ${block.id} : `}
168
- </span>
169
- <span style={{ fg: props.theme.text.base }}>{block.title}</span>
170
- <Show when={props.settings.showBlockTokens}>
171
- <span style={{ fg: props.theme.text.muted }}>
172
- {` ≈${formatTokens(block.tokensAfter)}`}
173
- </span>
174
- </Show>
175
- </text>
176
- </box>
177
- <Show when={meta() !== ""}>
178
- <text fg={props.theme.text.muted} wrapMode="word">
179
- {` ${meta()}`}
180
- </text>
181
- </Show>
182
- </box>
183
- )
184
- }}
185
- </For>
186
- </box>
187
- </Show>
188
- </Show>
189
- </box>
190
- </Show>
191
- )
192
- }
1
+ import type { Context } from "@opencode/plugin/tui/context"
2
+ import { SyntaxStyle, type RGBA } from "@opentui/core"
3
+ import { createEffect, createMemo, createSignal, For, onCleanup, Show } from "solid-js"
4
+
5
+ import type { Messages } from "../../i18n/index.ts"
6
+ import type { SummaryBlock } from "../../shared/blocks.ts"
7
+ import { formatTimestamp, formatTokens } from "../../shared/format.ts"
8
+ import type { Settings } from "../../shared/settings.ts"
9
+ import type { UpdateSettings } from "../types.ts"
10
+
11
+ /** 输入:摘要等级;输出:对应主题色;T1 蓝、T2 黄、T3 红。 */
12
+ function tierColor(tier: SummaryBlock["tier"], theme: Context["theme"]): RGBA {
13
+ if (tier === 1) return theme.text.feedback.info.base
14
+ if (tier === 2) return theme.text.feedback.warning.base
15
+ return theme.text.feedback.error.base
16
+ }
17
+
18
+ /** 输入:无;输出:共享的 SyntaxStyle;预期:markdown 渲染器需要它,懒加载避免重复创建。 */
19
+ let sharedSyntaxStyle: SyntaxStyle | undefined
20
+ function getSyntaxStyle(): SyntaxStyle {
21
+ sharedSyntaxStyle ??= SyntaxStyle.create()
22
+ return sharedSyntaxStyle
23
+ }
24
+
25
+ /**
26
+ * 输入:TUI 上下文 + 主题 + 块。
27
+ * 输出:占满可用高度的阅读弹窗。
28
+ * 预期:标题保持 `[T1] b1 : title`;Babel 按纯文本显示,其余交给 markdown 渲染器,esc 关闭。
29
+ */
30
+ export function SummaryDialog(props: {
31
+ readonly context: Context
32
+ readonly theme: Context["theme"]
33
+ readonly block: SummaryBlock
34
+ }) {
35
+ const height = createMemo(() => Math.max(8, Math.min(30, props.context.renderer.height - 8)))
36
+ return (
37
+ <box paddingLeft={3} paddingRight={3} paddingBottom={1} gap={1}>
38
+ <box flexDirection="row" justifyContent="space-between" gap={1}>
39
+ <text fg={props.theme.text.base} wrapMode="word">
40
+ <b>{`[T${props.block.tier.toString()}] ${props.block.id} : ${props.block.title}`}</b>
41
+ </text>
42
+ <text
43
+ fg={props.theme.text.muted}
44
+ onMouseUp={() => {
45
+ props.context.ui.dialog.clear()
46
+ }}
47
+ >
48
+ esc
49
+ </text>
50
+ </box>
51
+ <scrollbox height={height()} scrollY={true} focused={true} width="100%">
52
+ <Show
53
+ when={props.block.format === "babel"}
54
+ fallback={
55
+ <markdown
56
+ content={props.block.body}
57
+ syntaxStyle={getSyntaxStyle()}
58
+ fg={props.theme.text.base}
59
+ conceal={true}
60
+ tableOptions={{ widthMode: "full", style: "columns" }}
61
+ />
62
+ }
63
+ >
64
+ <text fg={props.theme.text.base} wrapMode="word">
65
+ {props.block.body}
66
+ </text>
67
+ </Show>
68
+ </scrollbox>
69
+ </box>
70
+ )
71
+ }
72
+
73
+ /**
74
+ * 输入:主题 + 文案 + 设置 + 写入函数 + 摘要块。
75
+ * 输出:可折叠的摘要块列表;点击标题打开 markdown 阅读弹窗。
76
+ * 预期:只展示服务端返回的 active 链;hover 只让箭头动,标题和背景保持稳定。
77
+ */
78
+ export function SummarySection(props: {
79
+ readonly context: Context
80
+ readonly theme: Context["theme"]
81
+ readonly copy: Messages
82
+ readonly settings: Settings
83
+ readonly updateSettings: UpdateSettings
84
+ readonly blocks: readonly SummaryBlock[]
85
+ }) {
86
+ const [hovered, setHovered] = createSignal<string | undefined>(undefined)
87
+ const [frame, setFrame] = createSignal(0)
88
+
89
+ // 只有 hover 时跑两帧;鼠标移出或销毁时清理,避免后台定时器。
90
+ createEffect(() => {
91
+ if (hovered() === undefined) {
92
+ setFrame(0)
93
+ return
94
+ }
95
+ const timer = setInterval(() => setFrame((value) => (value + 1) % 2), 180)
96
+ onCleanup(() => {
97
+ clearInterval(timer)
98
+ })
99
+ })
100
+
101
+ /** 输入:无;输出:无;预期:翻转摘要区折叠状态。 */
102
+ const toggleFold = (): void => {
103
+ void props.updateSettings((draft) => {
104
+ draft.summariesExpanded = !draft.summariesExpanded
105
+ })
106
+ }
107
+
108
+ /** 输入:块;输出:无;预期:打开该块的 markdown 阅读弹窗。 */
109
+ const openSummary = (block: SummaryBlock): void => {
110
+ props.context.ui.dialog.show(() => (
111
+ <SummaryDialog context={props.context} theme={props.theme} block={block} />
112
+ ))
113
+ props.context.ui.dialog.set({ size: "large", centered: true })
114
+ }
115
+
116
+ return (
117
+ <Show when={props.settings.showSummaries}>
118
+ <box width="100%" flexDirection="column">
119
+ <box flexDirection="row" height={1} onMouseDown={toggleFold}>
120
+ <box flexDirection="row" gap={1}>
121
+ <text fg={props.theme.text.base}>{props.settings.summariesExpanded ? "▼" : "▶"}</text>
122
+ <text fg={props.theme.text.base}>
123
+ <b>{props.copy.summariesTitle}</b>
124
+ </text>
125
+ <text fg={props.theme.text.muted}>{`(${props.blocks.length.toString()})`}</text>
126
+ </box>
127
+ </box>
128
+ <Show when={props.settings.summariesExpanded}>
129
+ <Show
130
+ when={props.blocks.length > 0}
131
+ fallback={<text fg={props.theme.text.muted}>{props.copy.noData}</text>}
132
+ >
133
+ <box paddingLeft={2} flexDirection="column" width="100%">
134
+ <For each={props.blocks}>
135
+ {(block) => {
136
+ const meta = (): string => {
137
+ const parts: string[] = []
138
+ if (props.settings.showTimeline) parts.push(formatTimestamp(block.at))
139
+ if (block.sourceBlockIds !== undefined && block.sourceBlockIds.length > 0) {
140
+ parts.push(`merged ${block.sourceBlockIds.map((id) => `#${id}`).join(" + ")}`)
141
+ }
142
+ return parts.join(" ")
143
+ }
144
+ return (
145
+ <box flexDirection="column" width="100%">
146
+ <box
147
+ flexDirection="row"
148
+ gap={1}
149
+ onMouseOver={() => {
150
+ setHovered(block.id)
151
+ }}
152
+ onMouseOut={() => {
153
+ setHovered((current) => (current === block.id ? undefined : current))
154
+ }}
155
+ onMouseUp={() => {
156
+ openSummary(block)
157
+ }}
158
+ >
159
+ <text width={2} fg={tierColor(block.tier, props.theme)}>
160
+ {hovered() === block.id ? (frame() === 0 ? "▸ " : " ▸") : " "}
161
+ </text>
162
+ <text fg={props.theme.text.base} wrapMode="word" flexGrow={1}>
163
+ <span style={{ fg: tierColor(block.tier, props.theme) }}>
164
+ <b>{`[T${block.tier.toString()}]`}</b>
165
+ </span>
166
+ <span style={{ fg: props.theme.text.muted }}>
167
+ {` ${block.id} : `}
168
+ </span>
169
+ <span style={{ fg: props.theme.text.base }}>{block.title}</span>
170
+ <Show when={props.settings.showBlockTokens}>
171
+ <span style={{ fg: props.theme.text.muted }}>
172
+ {` ≈${formatTokens(block.tokensAfter)}`}
173
+ </span>
174
+ </Show>
175
+ </text>
176
+ </box>
177
+ <Show when={meta() !== ""}>
178
+ <text fg={props.theme.text.muted} wrapMode="word">
179
+ {` ${meta()}`}
180
+ </text>
181
+ </Show>
182
+ </box>
183
+ )
184
+ }}
185
+ </For>
186
+ </box>
187
+ </Show>
188
+ </Show>
189
+ </box>
190
+ </Show>
191
+ )
192
+ }