Skip to content

Commit ad7d758

Browse files
edyedy
authored andcommitted
fix(tui): per-file polish round 4 — context/
1 parent 673f13a commit ad7d758

7 files changed

Lines changed: 40 additions & 42 deletions

File tree

MERGE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ fork 与上游改了同一处(常见于 TUI 视觉/UX、core 加固逻辑)
199199
| 逐文件打磨轮1:`ui/{spinner,dialog-select,dialog-prompt,dialog-export-options,link,icon,glyphs}.ts(x)` + `{app,app-commands,clipboard,editor,logo,audio}.ts(x)` + `util/{collapse-tool-output,filetype,presentation}.ts` + `config/keybind.ts` || 保留 bug 修复:spinner 共享 RGBA 常量原地突变改克隆;dialog-select `selectedForeground` 惰值改响应式 + setTimeout 补 onCleanup;collapse-tool-output 负 hiddenCount 防护;app-commands KV 快照改响应式读 + heap snapshot undefined 提示 + isVersionGreater 多连字符预发布;clipboard GNU screen 用平 DCS 透传;editor $EDITOR 引号感知拆分;filetype 支持无扩展名/复合后缀;presentation 复用 logo.ts + 缺 sessionID 略去 Continue 行;删除死代码(icon.tsx 未用组件/映射表、glyphs 死导出、logo.marks、audio.stopVoice、Keybinds 壳、死 onCancel prop、app.tsx console.log)(上游若已做可取上游版本) |
200200
| 逐文件打磨轮2:`component/{dialog-workspace-create,dialog-console-org,dialog-mcp,dialog-retry-action,dialog-session-rename,dialog-workspace-unavailable,dialog-status,spinner,bg-pulse-render,error-component,todo-item,workspace-label}.ts(x)` || 保留 bug 修复:dialog-workspace-create 最近工作区按连接状态过滤 + 空 adapters 用 `<Show>` 包裹;dialog-console-org 切换失败弹 error toast;dialog-session-rename 重命名失败弹 error toast;dialog-workspace-unavailable 恢复成功后关闭对话框;dialog-retry-action `selectedForeground` 惰值改响应式;error-component 复制失败重置 Copied 态 + ✖ 改 `GLYPH.cross`;todo-item icon/color/attrs 改派生函数(响应式);删除死代码(dialog-mcp 未用 setRef、dialog-status 空 Props 类型、spinner SPINNER_FRAMES 别名、workspace-label 未用组件、bg-pulse-render 死导出与未用 cache:false 分支)(上游若已做可取上游版本) |
201201
| 逐文件打磨轮3:`component/prompt/{autocomplete,index,move,workspace}.tsx` + 删除 `component/prompt/creating-dots.ts`、`component/workspace-label.tsx` | 中 | 保留 bug 修复:autocomplete `<Index>` 行内 8 个 setup 期常量改派生函数(行复用时 label/图标/颜色冻结)+ files resource 源补 `store.visible`(弹窗打开且查询未变时不刷新);index shell/斜杠命令失败补 error toast(原静默丢输入)+ extmark 样式 ID 改访问器(主题切换后指向已销毁 SyntaxStyle)+ interrupt 双击计数器补 clearTimeout/onCleanup(陈旧定时器清零新计数)+ 内联 reduce 换用 `expandPastedTextPlaceholders` + `basename ?? "image"` 改 `\|\|`;workspace 死 notice 改为成功 warp 弹 success toast(原 showNotice 从未渲染);删除死代码(Autocomplete sessionID prop、AutocompleteOption.disabled、move creatingDots/pendingNew、workspace label memo、creating-dots.ts 与 workspace-label.tsx 整文件)(上游若已做可取上游版本) |
202+
| 逐文件打磨轮4:`context/{sync,sdk,project,local,theme,route}.tsx` | 高/中 | 保留 bug 修复:sync `message.removed`/`message.part.removed``if (!messages/!parts) break`(未加载会话/被驱逐消息触发 `search(undefined)` 抛错杀死事件循环)+ `lsp.updated` 改无条件调用 debounce(默认无 workspace 时 LSP 状态不再刷新的回归)+ 会话列表排序改用与二分查找一致的码位比较(localeCompare 大小写次序不一致);sdk `onCleanup` 移出 `await` 之后(owner 已失效导致订阅永不清理)+ SSE 重连循环连接/迭代包 try/catch 并在收到事件后重置退避(一次网络错误即永久断连);project/local 事件订阅补 onCleanup;删除死代码(LocalTheme、RouteContext、SyncContext 解构、theme_mode 只写不读的持久化)(上游若已做可取上游版本) |
202203

203204
### 其他
204205

packages/tui/src/context/local.tsx

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createStore } from "solid-js/store"
22
import { createSimpleContext } from "./helper"
3-
import { batch, createEffect, createMemo } from "solid-js"
3+
import { batch, createEffect, createMemo, onCleanup } from "solid-js"
44
import { useSync } from "./sync"
55
import { useEvent } from "./event"
66
import path from "path"
@@ -14,16 +14,6 @@ import { useToast } from "../ui/toast"
1414
import { useRoute } from "./route"
1515
import { usePermission } from "./permission"
1616

17-
export type LocalTheme = {
18-
secondary: RGBA
19-
accent: RGBA
20-
success: RGBA
21-
warning: RGBA
22-
primary: RGBA
23-
error: RGBA
24-
info: RGBA
25-
}
26-
2717
export function parseModel(model: string) {
2818
const [providerID, ...rest] = model.split("/")
2919
return {
@@ -466,9 +456,11 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
466456
})
467457
}
468458

469-
event.on("session.deleted", (evt) => {
470-
prune(evt.properties.info.id)
471-
})
459+
onCleanup(
460+
event.on("session.deleted", (evt) => {
461+
prune(evt.properties.info.id)
462+
}),
463+
)
472464

473465
return {
474466
get ready() {

packages/tui/src/context/project.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { batch } from "solid-js"
1+
import { batch, onCleanup } from "solid-js"
22
import type { Path, Workspace } from "@opencode-ai/sdk/v2"
33
import { createStore, reconcile } from "solid-js/store"
44
import { createSimpleContext } from "./helper"
@@ -67,11 +67,13 @@ export const { use: useProject, provider: ProjectProvider } = createSimpleContex
6767
})
6868
}
6969

70-
sdk.event.on("event", (event) => {
71-
if (event.payload.type === "workspace.status") {
72-
setStore("workspace", "status", event.payload.properties.workspaceID, event.payload.properties.status)
73-
}
74-
})
70+
onCleanup(
71+
sdk.event.on("event", (event) => {
72+
if (event.payload.type === "workspace.status") {
73+
setStore("workspace", "status", event.payload.properties.workspaceID, event.payload.properties.status)
74+
}
75+
}),
76+
)
7577

7678
return {
7779
data: store,

packages/tui/src/context/route.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@ function initialRoute(value: unknown): Route | undefined {
5252
}
5353
}
5454

55-
export type RouteContext = ReturnType<typeof useRoute>
56-
5755
export function useRouteData<T extends Route["type"]>(type: T) {
5856
const route = useRoute()
5957
return route.data as Extract<Route, { type: typeof type }>

packages/tui/src/context/sdk.tsx

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,19 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
8787
while (true) {
8888
if (abort.signal.aborted || ctrl.signal.aborted) break
8989

90-
const events = await sdk.global.event({
91-
signal: ctrl.signal,
92-
sseMaxRetryAttempts: 0,
93-
})
94-
95-
for await (const event of events.stream) {
96-
if (ctrl.signal.aborted) break
97-
handleEvent(event)
90+
try {
91+
const events = await sdk.global.event({
92+
signal: ctrl.signal,
93+
sseMaxRetryAttempts: 0,
94+
})
95+
96+
for await (const event of events.stream) {
97+
if (ctrl.signal.aborted) break
98+
attempt = 0
99+
handleEvent(event)
100+
}
101+
} catch {
102+
// fall through to backoff and reconnect
98103
}
99104

100105
if (timer) clearTimeout(timer)
@@ -109,13 +114,15 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
109114
})().catch(() => {})
110115
}
111116

112-
onMount(async () => {
117+
onMount(() => {
113118
if (props.events) {
114-
const unsub = await props.events.subscribe(handleEvent)
115-
onCleanup(unsub)
116-
} else {
117-
startSSE()
119+
const pending = props.events.subscribe(handleEvent)
120+
onCleanup(() => {
121+
void pending.then((unsub) => unsub())
122+
})
123+
return
118124
}
125+
startSSE()
119126
})
120127

121128
onCleanup(() => {

packages/tui/src/context/sync.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ function search<T>(items: T[], target: string, key: (item: T) => string) {
5252
}
5353

5454
export const {
55-
context: SyncContext,
5655
use: useSync,
5756
provider: SyncProvider,
5857
} = createSimpleContext({
@@ -145,7 +144,7 @@ export const {
145144
const syncingSessions = new Map<string, Promise<void>>()
146145
const hydratingSessions = new Map<string, { messages: Set<string>; parts: Set<string> }>()
147146
let lspStatusTimer: ReturnType<typeof setTimeout> | undefined
148-
const debouncedLspStatus = (workspace: string) => {
147+
const debouncedLspStatus = (workspace: string | undefined) => {
149148
if (lspStatusTimer) clearTimeout(lspStatusTimer)
150149
lspStatusTimer = setTimeout(() => {
151150
void sdk.client.lsp.status({ workspace }).then(
@@ -177,7 +176,7 @@ export const {
177176
function listSessions() {
178177
return sdk.client.session
179178
.list({ start: Date.now() - 30 * 24 * 60 * 60 * 1000, ...sessionListQuery() })
180-
.then((x) => (x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id)))
179+
.then((x) => (x.data ?? []).toSorted((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)))
181180
}
182181

183182
const unsubscribe = event.subscribe((event, { directory, workspace }) => {
@@ -368,6 +367,7 @@ export const {
368367
case "message.removed": {
369368
touchMessage(event.properties.sessionID, event.properties.messageID)
370369
const messages = store.message[event.properties.sessionID]
370+
if (!messages) break
371371
const result = search(messages, event.properties.messageID, (m) => m.id)
372372
if (result.found) {
373373
setStore(
@@ -424,6 +424,7 @@ export const {
424424
case "message.part.removed": {
425425
touchPart(event.properties.sessionID, event.properties.partID)
426426
const parts = store.part[event.properties.messageID]
427+
if (!parts) break
427428
const result = search(parts, event.properties.partID, (p) => p.id)
428429
if (result.found) {
429430
setStore(
@@ -438,8 +439,7 @@ export const {
438439
}
439440

440441
case "lsp.updated": {
441-
const workspace = project.workspace.current()
442-
if (workspace) debouncedLspStatus(workspace)
442+
debouncedLspStatus(project.workspace.current())
443443
break
444444
}
445445

packages/tui/src/context/theme.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,6 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
200200
}
201201

202202
function apply(mode: "dark" | "light") {
203-
if (store.lock !== undefined) kv.set("theme_mode", mode)
204203
if (store.mode === mode) return
205204
setStore("mode", mode)
206205
refreshSystemTheme(mode)
@@ -215,7 +214,6 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
215214
function free() {
216215
setStore("lock", undefined)
217216
kv.set("theme_mode_lock", undefined)
218-
kv.set("theme_mode", undefined)
219217
refreshSystemTheme(renderer.themeMode ?? store.mode)
220218
}
221219

0 commit comments

Comments
 (0)