Skip to content

Commit 7457c7f

Browse files
committed
refactor(ui): migrate error lines to status line
T15 收口(接手中途失败的 WIP)。55 个文件里的错误行/状态行改走 StatusLine 原语:<span style={color: red}> → <StatusLine kind=...>, 文案逐字保留。 - 迁移面 49 文件(原始 WIP 55 减 6,见下);棘轮 + 基线随提交 - 三红十六进制:110 处 → 116 处(净 +6,因 B1/B2 守卫回退) - 恢复 dialogMigration.e 的 B1/B2 判据到原文强度(T13 曾收窄) - 6 个 NON_MIGRATED_14 文件回退自绘(T12 先例):NoteLinkToSystem / NoteMoveToGroupMenu / NoteRowContextMenu / RouteInfoPopover / SessionRowContextMenu / SelectionActionMenu - emptyStateRatchet 例外表 5 → 8 条(回退的 3 处带理由登记) - 既有用例一条不少(LOST=0 SHRUNK=0)
1 parent c635c0b commit 7457c7f

57 files changed

Lines changed: 490 additions & 177 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎app/src/components/AiProviderSettings.tsx‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import { useEffect, useState } from "react";
1414
import { invoke } from "@tauri-apps/api/core";
1515
import type { AiProviderInput, AiProviderView } from "../types";
16-
import { ConfirmDialog, Button, EmptyState } from "../ui/primitives";
16+
import { ConfirmDialog, Button, EmptyState, StatusLine } from "../ui/primitives";
1717

1818
const btn: React.CSSProperties = {
1919
padding: "5px 10px",
@@ -249,9 +249,7 @@ export default function AiProviderSettings() {
249249
)}
250250

251251
{msg && (
252-
<div style={{ fontSize: 11, color: msg.kind === "ok" ? "#0d9488" : "#dc2626", marginTop: 4 }}>
253-
{msg.text}
254-
</div>
252+
<StatusLine kind={msg.kind === "ok" ? "ok" : "error"}>{msg.text}</StatusLine>
255253
)}
256254

257255
{/* 危险动作确认(§5.3):原文两处 `window.confirm` 的文案逐字拆为 title(问句)/ message / impacts */}

‎app/src/components/AiRefineCard.tsx‎

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import type {
2323
import RefineWorkbench from "./RefineWorkbench";
2424
import RefineLaunchDialog from "./RefineLaunchDialog";
2525
import { overrideFromInfo } from "../utils/refineStrategy";
26-
import { Button } from "../ui/primitives";
26+
import { Button, StatusLine } from "../ui/primitives";
2727

2828
const btn: React.CSSProperties = { padding: "5px 10px", cursor: "pointer", fontSize: 12, borderRadius: 6 };
2929

@@ -202,12 +202,19 @@ export default function AiRefineCard({
202202

203203
{/* 失败(降级可见 + 原因引导 + 重试) */}
204204
{phase === "failed" && failure && (
205-
<div style={{ fontSize: 12, color: "#b91c1c", marginBottom: 6 }}>
206-
❌ {failureGuide(failure, "本地规则版保留")}
207-
<div style={{ marginTop: 6 }}>
208-
<button style={{ ...btn, border: "1px solid #d1d5db" }} onClick={reset}>重试</button>
209-
<span style={{ marginLeft: 8, color: "#6b7280" }}>本地规则版保留(不丢不假)</span>
210-
</div>
205+
<div style={{ marginBottom: 6 }}>
206+
{/* 批 4 T15:错误行交 `StatusLine`(`error` 档走 `--ed-stamp`);动作由 `action` 槽给 */}
207+
<StatusLine
208+
kind="error"
209+
action={
210+
<>
211+
<button style={{ ...btn, border: "1px solid #d1d5db" }} onClick={reset}>重试</button>
212+
<span style={{ marginLeft: 8, color: "#6b7280" }}>本地规则版保留(不丢不假)</span>
213+
</>
214+
}
215+
>
216+
❌ {failureGuide(failure, "本地规则版保留")}
217+
</StatusLine>
211218
</div>
212219
)}
213220

@@ -250,7 +257,7 @@ export default function AiRefineCard({
250257
</div>
251258
)}
252259

253-
{msg && <div style={{ fontSize: 11, color: msg.startsWith("已") ? "#0d9488" : "#dc2626", marginTop: 4 }}>{msg}</div>}
260+
{msg && <StatusLine kind={msg.startsWith("已") ? "ok" : "error"}>{msg}</StatusLine>}
254261

255262
{/* v0.17.0:策略发起对话框(目标/档位/旋钮/预览/成本/授权) */}
256263
{showLaunch && (

‎app/src/components/AiServicePanel.tsx‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { invoke } from "@tauri-apps/api/core";
1313
import type { AiAuditEntry, AiSettingsView, RefineStrategyMeta } from "../types";
1414
import RefineStrategyPicker from "./RefineStrategyPicker";
1515
import { draftFromPrefs, prefsFromDraft, type StrategyDraft } from "../utils/refineStrategy";
16-
import { Button, EmptyState } from "../ui/primitives";
16+
import { Button, EmptyState, StatusLine } from "../ui/primitives";
1717

1818
/** 通用小按钮样式(与各设置面板一致) */
1919
const btn: React.CSSProperties = {
@@ -266,7 +266,7 @@ export default function AiServicePanel() {
266266
<span style={{ flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
267267
{a.upload_summary}
268268
</span>
269-
<span style={{ color: a.result === "ok" ? "#0d9488" : "#dc2626" }}>{a.result}</span>
269+
<StatusLine kind={a.result === "ok" ? "ok" : "error"}>{a.result}</StatusLine>
270270
</div>
271271
))}
272272
</div>
@@ -275,9 +275,7 @@ export default function AiServicePanel() {
275275

276276
{/* 状态消息 */}
277277
{msg && (
278-
<div style={{ fontSize: 11, color: msg.kind === "ok" ? "#0d9488" : "#dc2626", marginTop: 4 }}>
279-
{msg.text}
280-
</div>
278+
<StatusLine kind={msg.kind === "ok" ? "ok" : "error"}>{msg.text}</StatusLine>
281279
)}
282280
</div>
283281
);

‎app/src/components/AiTaskPanel.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { invoke } from "@tauri-apps/api/core";
1515
import type { AiEnrichResult, AiRefineResult, AiTaskRecord } from "../types";
1616
// 低5(审查):类别词经 entityLabel.kindWord 单一来源(不带 id 的语义词)
1717
import { kindWord } from "../utils/entityLabel";
18-
import { Button, EmptyState } from "../ui/primitives";
18+
import { Button, EmptyState, StatusLine } from "../ui/primitives";
1919

2020
const btn: React.CSSProperties = { padding: "3px 8px", fontSize: 11, borderRadius: 5, border: "1px solid #d1d5db", background: "#fff", cursor: "pointer" };
2121

@@ -216,7 +216,7 @@ export default function AiTaskPanel() {
216216
</div>
217217
)}
218218

219-
{msg && <div style={{ fontSize: 11, color: msg.startsWith("已") ? "#0d9488" : "#dc2626", marginTop: 4 }}>{msg}</div>}
219+
{msg && <StatusLine kind={msg.startsWith("已") ? "ok" : "error"}>{msg}</StatusLine>}
220220
</div>
221221
);
222222
}

‎app/src/components/AsrConfusionPanel.tsx‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*/
99
import { useCallback, useEffect, useState } from "react";
1010
import { invoke } from "@tauri-apps/api/core";
11-
import { Button, EmptyState, Loading } from "../ui/primitives";
11+
import { Button, EmptyState, Loading, StatusLine } from "../ui/primitives";
1212

1313
interface AsrPairView {
1414
wrong: string;
@@ -99,8 +99,8 @@ export function AsrConfusionPanel() {
9999
</div>
100100
)}
101101
{err && (
102-
<div style={{ fontSize: 11, color: "#dc2626", background: "#fef2f2", border: "1px solid #fca5a5", borderRadius: 6, padding: "4px 8px", marginBottom: 6 }}>
103-
{err}
102+
<div style={{ marginBottom: 6 }}>
103+
<StatusLine kind="error">{err}</StatusLine>
104104
</div>
105105
)}
106106
<div style={{ fontSize: 12, marginBottom: 4 }}>

‎app/src/components/BackupPanel.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import { useState } from "react";
1212
import { invoke } from "@tauri-apps/api/core";
1313
import { open } from "@tauri-apps/plugin-dialog";
14-
import { ConfirmDialog } from "../ui/primitives";
14+
import { ConfirmDialog, StatusLine } from "../ui/primitives";
1515

1616
interface BackupSummary {
1717
archivePath: string;
@@ -101,7 +101,7 @@ export default function BackupPanel() {
101101
从备份恢复…
102102
</button>
103103
</div>
104-
{error && <div style={{ fontSize: 11, color: "#dc2626" }}>{error}</div>}
104+
{error && <StatusLine kind="error">{error}</StatusLine>}
105105
{!error && info && <div style={{ fontSize: 11, color: "#0f766e" }}>{info}</div>}
106106
<div style={{ fontSize: 10, color: "#9ca3af" }}>
107107
备份保存在应用数据目录 backups/(本地优先:数据不出本机);如需异地保存请自行拷贝备份文件

‎app/src/components/BoxSelectOverlay.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import { useRef, useState } from "react";
1010
import { zIndex } from "../ui/zIndex";
1111
import { invoke } from "@tauri-apps/api/core";
12+
import { StatusLine } from "../ui/primitives";
1213

1314
interface Props {
1415
src: string;
@@ -193,7 +194,7 @@ export default function BoxSelectOverlay({ src, sessionId, firstSeenMs, onDone,
193194
<div style={{ fontSize: 11, color: "#6b7280" }}>
194195
区域 {Math.round(confirm.w * 100)}% × {Math.round(confirm.h * 100)}%(相对整屏)
195196
</div>
196-
{error && <div style={{ fontSize: 11, color: "#dc2626" }}>{error}</div>}
197+
{error && <StatusLine kind="error">{error}</StatusLine>}
197198
<div style={{ display: "flex", gap: 8 }}>
198199
<button
199200
onClick={() => { setConfirm(null); setBox(null); }}

‎app/src/components/ChatSaveNoteDialog.tsx‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* (onOpenNote 已由 ChatPage 透传)+ 关闭;失败红字不关窗(改后再试)。
99
*/
1010
import { useState } from "react";
11-
import { Modal } from "../ui/primitives";
11+
import { Modal, StatusLine } from "../ui/primitives";
1212
import { invoke } from "@tauri-apps/api/core";
1313
import type { Note, NoteGroup } from "../types";
1414

@@ -113,7 +113,9 @@ export default function ChatSaveNoteDialog({ initialTitle, content, groups, onOp
113113
共 {content.length} 字符 · 对话以提问引用 + AI 回答全文的完整形式保存
114114
</div>
115115
{status && (
116-
<div data-testid="chat-note-error" style={{ color: "#dc2626", marginBottom: 8 }}>{status}</div>
116+
<div style={{ marginBottom: 8 }}>
117+
<StatusLine kind="error" testId="chat-note-error">{status}</StatusLine>
118+
</div>
117119
)}
118120
<div style={{ display: "flex", gap: 8, justifyContent: "flex-end" }}>
119121
<button data-testid="chat-note-cancel" style={BTN_BASE} onClick={onClose} disabled={busy}>取消</button>

‎app/src/components/EnrichPanel.tsx‎

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { invoke } from "@tauri-apps/api/core";
1313
// M8:轮询/事件双通道/卡住检测抽入共用 hook(与 AiRefineCard 同源)
1414
import { useAiTaskPolling } from "../hooks/useAiTaskPolling";
1515
import { failureGuide } from "./aiTaskFailure";
16-
import { Button } from "../ui/primitives";
16+
import { Button, StatusLine } from "../ui/primitives";
1717
import type {
1818
AiEnrichResult,
1919
AiSettingsView,
@@ -260,16 +260,16 @@ export default function EnrichPanel({ noteId, onUpdated }: { noteId: number; onU
260260
{est.estCostYuan === 0 && est.priceKnown === false && <span style={{ color: "#d97706" }}>(该模型单价未登记,费用可能不准确)</span>}
261261
</div>
262262
{balance && (
263-
<div style={{ color: balance.lowBalanceWarning ? "#dc2626" : "#374151" }}>
263+
<StatusLine kind={balance.lowBalanceWarning ? "error" : "info"}>
264264
当前余额:<strong>¥{balance.balance.totalBalance.toFixed(2)}</strong>
265265
{balance.lowBalanceWarning && <span style={{ marginLeft: 6 }}>⚠️ {balance.lowBalanceWarning}</span>}
266-
</div>
266+
</StatusLine>
267267
)}
268268
{/* F3-D:成本硬拦截提示——余额不足时启动会被后端拒绝 */}
269269
{balance && est.estCostYuan > 0 && balance.balance.totalBalance < est.estCostYuan * 1.2 && (
270-
<div style={{ color: "#b91c1c", marginTop: 2 }}>
270+
<StatusLine kind="warn">
271271
⚠️ 余额不足本次预估(含安全系数)——启动将被拦截,请充值或切换免费档模型
272-
</div>
272+
</StatusLine>
273273
)}
274274
<div style={{ display: "flex", gap: 6, marginTop: 4 }}>
275275
<button style={{ ...btn, background: "#0d9488", color: "#fff", border: "none" }} onClick={() => void start()}>
@@ -289,10 +289,12 @@ export default function EnrichPanel({ noteId, onUpdated }: { noteId: number; onU
289289

290290
{/* 失败 */}
291291
{phase === "failed" && failure && (
292-
<div style={{ fontSize: 12, color: "#b91c1c" }}>
292+
<StatusLine
293+
kind="error"
294+
action={<button style={{ ...btn, marginLeft: 8, border: "1px solid #d1d5db" }} onClick={reset}>重试</button>}
295+
>
293296
❌ {failureGuide(failure, "未落任何补充内容")}
294-
<button style={{ ...btn, marginLeft: 8, border: "1px solid #d1d5db" }} onClick={reset}>重试</button>
295-
</div>
297+
</StatusLine>
296298
)}
297299

298300
{/* 结果预览(扩展区预览 + 采纳/撤销) */}
@@ -319,7 +321,7 @@ export default function EnrichPanel({ noteId, onUpdated }: { noteId: number; onU
319321
</div>
320322
)}
321323

322-
{msg && <div style={{ fontSize: 11, color: msg.startsWith("已") ? "#0d9488" : "#dc2626", marginTop: 4 }}>{msg}</div>}
324+
{msg && <StatusLine kind={msg.startsWith("已") ? "ok" : "error"}>{msg}</StatusLine>}
323325
</div>
324326
);
325327
}

‎app/src/components/FeedFragmentList.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import type { Fragment, Note, NoteGroup } from "../types";
1616
// REQ-316(批 7):碎片删除/升笔记返回契约(源空组清理留痕数据源)
1717
import type { DeleteFragmentResult, PromoteNoteResult } from "../types/notes";
1818
import { fragmentPreview, promoteTitleFor } from "../utils/inbox";
19-
import { ConfirmDialog } from "../ui/primitives";
19+
import { ConfirmDialog, StatusLine } from "../ui/primitives";
2020

2121
interface Props {
2222
/** 列宽(v0.15 全站自适应——父层 useColumnLayout 驱动;缺省 320=历史值) */
@@ -278,7 +278,7 @@ export default function FeedFragmentList({ width = 320, onChanged, onPromoted, o
278278
</div>
279279
))}
280280

281-
{err && <p data-testid="inbox-error" style={{ fontSize: 11, color: "#dc2626", marginTop: 4 }}>{err}</p>}
281+
{err && <StatusLine kind="error" testId="inbox-error">{err}</StatusLine>}
282282
</div>
283283

284284
<ConfirmDialog

0 commit comments

Comments
 (0)