Skip to content

Commit cdc1ad4

Browse files
edyedy
authored andcommitted
fix(tui): per-file polish round 2 — component/ top level
1 parent ba4cf2a commit cdc1ad4

13 files changed

Lines changed: 58 additions & 65 deletions

MERGE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ fork 与上游改了同一处(常见于 TUI 视觉/UX、core 加固逻辑)
197197
| `packages/tui/src/{audio,attention}.ts` (错误日志级别) || 保留 `console.error` 替代 `console.debug`(上游若已做可取上游版本) |
198198
| `packages/tui/src/util/markdown.ts` + `test/markdown-polish.test.ts` (LLM markdown 打磨 + splitProseAndCode) || fork 新增(LaTeX→Unicode、CJK 强调符修复、`splitProseAndCode` 分段供代码块面板化),保留;若上游引入同类能力对抗审计 |
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)(上游若已做可取上游版本) |
200+
| 逐文件打磨轮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 分支)(上游若已做可取上游版本) |
200201

201202
### 其他
202203

packages/tui/src/component/bg-pulse-render.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,18 +69,17 @@ const LOGO_TEMPLATE: LogoTemplateCell[] = LOGO_LINES.flatMap((line, y) =>
6969
.filter((cell): cell is LogoTemplateCell => !!cell),
7070
)
7171

72-
export type Rgb = [number, number, number]
72+
type Rgb = [number, number, number]
7373

74-
export type GoUpsellArtRenderOptions = {
74+
type GoUpsellArtRenderOptions = {
7575
deltaTime?: number
7676
rgb?: boolean
77-
cache?: boolean
7877
}
7978

8079
const CACHE_FRAME_COUNT = Math.round(PERIOD / (1000 / 30))
8180
const CACHE_FRAMES_PER_RENDER = 1
8281

83-
export function toRgb(color: RGBA): Rgb {
82+
function toRgb(color: RGBA): Rgb {
8483
const [r, g, b] = color.toInts()
8584
return [r, g, b]
8685
}
@@ -171,12 +170,7 @@ export class GoUpsellArtPainter {
171170
const rgb = options.rgb === true
172171
this.elapsed = (this.elapsed + (options.deltaTime ?? 0)) % PERIOD
173172
this.rebuildGeometry(frameBuffer, rgb)
174-
if (options.cache !== false) {
175-
this.drawCached(frameBuffer, rgb)
176-
return
177-
}
178-
this.drawBackground(frameBuffer, this.elapsed)
179-
this.drawLogo(frameBuffer, this.elapsed, rgb)
173+
this.drawCached(frameBuffer, rgb)
180174
}
181175

182176
private invalidateCache() {

packages/tui/src/component/dialog-console-org.tsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,23 @@ export function DialogConsoleOrg() {
9595
return
9696
}
9797

98-
await sdk.client.experimental.console.switchOrg(
99-
{
100-
accountID: item.accountID,
101-
orgID: item.orgID,
102-
},
103-
{ throwOnError: true },
104-
)
105-
106-
await sdk.client.instance.dispose()
98+
try {
99+
await sdk.client.experimental.console.switchOrg(
100+
{
101+
accountID: item.accountID,
102+
orgID: item.orgID,
103+
},
104+
{ throwOnError: true },
105+
)
106+
await sdk.client.instance.dispose()
107+
} catch (error) {
108+
toast.show({
109+
title: "Failed to switch org",
110+
message: errorMessage(error),
111+
variant: "error",
112+
})
113+
return
114+
}
107115
toast.show({
108116
message: `Switched to ${item.orgName}`,
109117
variant: "info",

packages/tui/src/component/dialog-mcp.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { createMemo, createSignal } from "solid-js"
22
import { useLocal } from "../context/local"
33
import { useSync } from "../context/sync"
44
import { map, pipe, entries, sortBy } from "remeda"
5-
import { DialogSelect, type DialogSelectRef, type DialogSelectOption } from "../ui/dialog-select"
5+
import { DialogSelect, type DialogSelectOption } from "../ui/dialog-select"
66
import { useTheme } from "../context/theme"
77
import { TextAttributes } from "@opentui/core"
88
import { useSDK } from "../context/sdk"
@@ -24,7 +24,6 @@ export function DialogMcp() {
2424
const sync = useSync()
2525
const sdk = useSDK()
2626
const { theme } = useTheme()
27-
const [, setRef] = createSignal<DialogSelectRef<unknown>>()
2827
const [loading, setLoading] = createSignal<string | null>(null)
2928

3029
const options = createMemo(() => {
@@ -39,7 +38,7 @@ export function DialogMcp() {
3938
map(([name, status]) => ({
4039
value: name,
4140
title: name,
42-
description: status.status === "failed" ? "failed" : status.status,
41+
description: status.status,
4342
footer: <Status enabled={local.mcp.isEnabled(name)} loading={loadingMcp === name} />,
4443
category: undefined,
4544
})),
@@ -75,7 +74,6 @@ export function DialogMcp() {
7574

7675
return (
7776
<DialogSelect
78-
ref={setRef}
7977
title="MCPs"
8078
options={options()}
8179
emptyView={

packages/tui/src/component/dialog-retry-action.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ function panelOverlay(color: RGBA) {
3939
export function DialogRetryAction(props: DialogRetryActionProps) {
4040
const dialog = useDialog()
4141
const { theme } = useTheme()
42-
const fg = selectedForeground(theme)
42+
const fg = () => selectedForeground(theme)
4343
const showGoTreatment = () => props.link === GO_URL
4444
const textBg = () => (showGoTreatment() ? panelOverlay(theme.backgroundPanel) : undefined)
4545
const [selected, setSelected] = createSignal<"dismiss" | "action">("action")
@@ -119,7 +119,7 @@ export function DialogRetryAction(props: DialogRetryActionProps) {
119119
onMouseUp={() => dismiss(props, dialog)}
120120
>
121121
<text
122-
fg={selected() === "dismiss" ? fg : theme.textMuted}
122+
fg={selected() === "dismiss" ? fg() : theme.textMuted}
123123
bg={selected() === "dismiss" ? undefined : textBg()}
124124
attributes={selected() === "dismiss" ? TextAttributes.BOLD : undefined}
125125
>
@@ -134,7 +134,7 @@ export function DialogRetryAction(props: DialogRetryActionProps) {
134134
onMouseUp={() => runAction(props, dialog)}
135135
>
136136
<text
137-
fg={selected() === "action" ? fg : theme.text}
137+
fg={selected() === "action" ? fg() : theme.text}
138138
bg={selected() === "action" ? undefined : textBg()}
139139
attributes={selected() === "action" ? TextAttributes.BOLD : undefined}
140140
>

packages/tui/src/component/dialog-session-rename.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { useDialog } from "../ui/dialog"
33
import { useSync } from "../context/sync"
44
import { createMemo } from "solid-js"
55
import { useSDK } from "../context/sdk"
6+
import { useToast } from "../ui/toast"
7+
import { errorMessage } from "../util/error"
68

79
interface DialogSessionRenameProps {
810
session: string
@@ -12,17 +14,22 @@ export function DialogSessionRename(props: DialogSessionRenameProps) {
1214
const dialog = useDialog()
1315
const sync = useSync()
1416
const sdk = useSDK()
17+
const toast = useToast()
1518
const session = createMemo(() => sync.session.get(props.session))
1619

1720
return (
1821
<DialogPrompt
1922
title="Rename Session"
2023
value={session()?.title}
2124
onConfirm={(value) => {
22-
void sdk.client.session.update({
23-
sessionID: props.session,
24-
title: value,
25-
})
25+
sdk.client.session
26+
.update({
27+
sessionID: props.session,
28+
title: value,
29+
})
30+
.catch((error) => {
31+
toast.show({ title: "Failed to rename session", message: errorMessage(error), variant: "error" })
32+
})
2633
dialog.clear()
2734
}}
2835
/>

packages/tui/src/component/dialog-status.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import { useSync } from "../context/sync"
66
import { For, Match, Switch, Show, createMemo } from "solid-js"
77
import { GLYPH } from "../ui/glyphs"
88

9-
export type DialogStatusProps = {}
10-
119
export function DialogStatus() {
1210
const sync = useSync()
1311
const { theme } = useTheme()

packages/tui/src/component/dialog-workspace-create.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { DialogSelect, type DialogSelectOption } from "../ui/dialog-select"
44
import { useSync } from "../context/sync"
55
import { useProject } from "../context/project"
66
import { useRoute } from "../context/route"
7-
import { createMemo, createSignal, onMount } from "solid-js"
7+
import { createMemo, createSignal, onMount, Show } from "solid-js"
88
import { errorMessage } from "../util/error"
99
import { useSDK } from "../context/sdk"
1010
import { useToast } from "../ui/toast"
@@ -38,7 +38,9 @@ export function recentConnectedWorkspaces<WorkspaceInfo extends { id: string; ti
3838
limit?: number
3939
omitWorkspaceID?: string
4040
}) {
41-
const allWorkspaces = input.workspaces.filter((workspace) => input.status(workspace.id) === "connected")
41+
const allWorkspaces = input.workspaces.filter(
42+
(workspace) => workspace.id !== input.omitWorkspaceID && input.status(workspace.id) === "connected",
43+
)
4244
const workspaces = allWorkspaces.toSorted((a, b) => Number(b.timeUsed) - Number(a.timeUsed))
4345
const recent = workspaces.slice(0, input.limit ?? 3)
4446

@@ -243,9 +245,9 @@ export function DialogWorkspaceSelect(props: {
243245
]
244246
})
245247

246-
if (!adapters()) return null
247248
return (
248-
<DialogSelect<WorkspaceSelectValue>
249+
<Show when={adapters()}>
250+
<DialogSelect<WorkspaceSelectValue>
249251
title="Warp"
250252
skipFilter={true}
251253
renderFilter={false}
@@ -270,6 +272,7 @@ export function DialogWorkspaceSelect(props: {
270272
))
271273
}}
272274
/>
275+
</Show>
273276
)
274277
}
275278

packages/tui/src/component/dialog-workspace-unavailable.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export function DialogWorkspaceUnavailable(props: { onRestore?: () => boolean |
2121
}
2222
const result = await props.onRestore?.()
2323
if (result === false) return
24+
dialog.clear()
2425
}
2526

2627
useBindings(() => ({

packages/tui/src/component/error-component.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { InstallationVersion } from "@opencode-ai/core/installation/version"
77
import { useExit } from "../context/exit"
88
import { describeOS, describeTerminal } from "../util/system"
99
import { AnimatedIcon } from "../ui/icon"
10+
import { GLYPH } from "../ui/glyphs"
1011

1112
export function ErrorComponent(props: { error: Error; reset: () => void; mode?: "dark" | "light" }) {
1213
const term = useTerminalDimensions()
@@ -46,7 +47,9 @@ export function ErrorComponent(props: { error: Error; reset: () => void; mode?:
4647
const issueURL = buildIssueURL(message, stack)
4748

4849
const copyReport = () => {
49-
void clipboard.write?.(issueURL.toString()).then(() => setCopied(true))
50+
void clipboard.write?.(issueURL.toString())
51+
.then(() => setCopied(true))
52+
.catch(() => setCopied(false))
5053
}
5154

5255
const actions = [
@@ -111,7 +114,7 @@ export function ErrorComponent(props: { error: Error; reset: () => void; mode?:
111114
<box flexDirection="column" alignItems="center" flexShrink={0} gap={0}>
112115
<box flexDirection="row" gap={1} alignItems="center">
113116
<text fg={colors.error} attributes={TextAttributes.BOLD}>
114-
117+
{GLYPH.cross}
115118
</text>
116119
<text attributes={TextAttributes.BOLD} fg={colors.text}>
117120
opencode crashed

0 commit comments

Comments
 (0)