dsh-data-domain-knowledge 0.1.0-alpha.13 → 0.1.0-alpha.15

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 (266) hide show
  1. package/README.md +89 -56
  2. package/cordis.patch.yml +36 -129
  3. package/eval/benchmark-extraction-format.mjs +20 -0
  4. package/lib/client.js +3262 -4142
  5. package/lib/doc-page.js +1 -1
  6. package/lib/pages.js +1147 -364
  7. package/lib/types/axis-registry.d.ts +8 -1
  8. package/lib/types/axis-registry.d.ts.map +1 -1
  9. package/lib/types/axis-registry.js +62 -7
  10. package/lib/types/axis-registry.js.map +1 -1
  11. package/lib/types/convert/host.d.ts.map +1 -1
  12. package/lib/types/convert/host.js +3 -0
  13. package/lib/types/convert/host.js.map +1 -1
  14. package/lib/types/convert/ingest.d.ts +9 -0
  15. package/lib/types/convert/ingest.d.ts.map +1 -1
  16. package/lib/types/convert/ingest.js +68 -9
  17. package/lib/types/convert/ingest.js.map +1 -1
  18. package/lib/types/diagnosis/cards.d.ts +7 -3
  19. package/lib/types/diagnosis/cards.d.ts.map +1 -1
  20. package/lib/types/diagnosis/cards.js +13 -8
  21. package/lib/types/diagnosis/cards.js.map +1 -1
  22. package/lib/types/diagnosis/run.d.ts +4 -13
  23. package/lib/types/diagnosis/run.d.ts.map +1 -1
  24. package/lib/types/diagnosis/run.js +26 -16
  25. package/lib/types/diagnosis/run.js.map +1 -1
  26. package/lib/types/diagnosis/schema.js +2 -2
  27. package/lib/types/diagnosis/schema.js.map +1 -1
  28. package/lib/types/diagnosis/seed-source.d.ts +1 -1
  29. package/lib/types/diagnosis/seed-source.d.ts.map +1 -1
  30. package/lib/types/diagnosis/seed-source.js +5 -4
  31. package/lib/types/diagnosis/seed-source.js.map +1 -1
  32. package/lib/types/diagnosis/seed.d.ts +4 -4
  33. package/lib/types/diagnosis/seed.d.ts.map +1 -1
  34. package/lib/types/diagnosis/seed.js +6 -8
  35. package/lib/types/diagnosis/seed.js.map +1 -1
  36. package/lib/types/diagnosis/store.d.ts +18 -17
  37. package/lib/types/diagnosis/store.d.ts.map +1 -1
  38. package/lib/types/diagnosis/store.js +94 -66
  39. package/lib/types/diagnosis/store.js.map +1 -1
  40. package/lib/types/diagnosis/types.d.ts +10 -16
  41. package/lib/types/diagnosis/types.d.ts.map +1 -1
  42. package/lib/types/diagnosis/types.js.map +1 -1
  43. package/lib/types/environment/dsh-python.d.ts +77 -0
  44. package/lib/types/environment/dsh-python.d.ts.map +1 -0
  45. package/lib/types/environment/dsh-python.js +139 -0
  46. package/lib/types/environment/dsh-python.js.map +1 -0
  47. package/lib/types/environment/install.d.ts +9 -1
  48. package/lib/types/environment/install.d.ts.map +1 -1
  49. package/lib/types/environment/install.js +149 -24
  50. package/lib/types/environment/install.js.map +1 -1
  51. package/lib/types/environment/model-catalog.d.ts +34 -2
  52. package/lib/types/environment/model-catalog.d.ts.map +1 -1
  53. package/lib/types/environment/model-catalog.js +40 -2
  54. package/lib/types/environment/model-catalog.js.map +1 -1
  55. package/lib/types/environment/service.d.ts +13 -3
  56. package/lib/types/environment/service.d.ts.map +1 -1
  57. package/lib/types/environment/service.js +127 -23
  58. package/lib/types/environment/service.js.map +1 -1
  59. package/lib/types/environment/types.d.ts +9 -0
  60. package/lib/types/environment/types.d.ts.map +1 -1
  61. package/lib/types/events.d.ts +2 -0
  62. package/lib/types/events.d.ts.map +1 -1
  63. package/lib/types/events.js +13 -0
  64. package/lib/types/events.js.map +1 -1
  65. package/lib/types/extract-contract.d.ts +1 -1
  66. package/lib/types/extract-contract.d.ts.map +1 -1
  67. package/lib/types/extract-contract.js +34 -31
  68. package/lib/types/extract-contract.js.map +1 -1
  69. package/lib/types/extract-model.d.ts +2 -0
  70. package/lib/types/extract-model.d.ts.map +1 -1
  71. package/lib/types/extract-model.js.map +1 -1
  72. package/lib/types/extract-table-contract.d.ts +3 -0
  73. package/lib/types/extract-table-contract.d.ts.map +1 -0
  74. package/lib/types/extract-table-contract.js +30 -0
  75. package/lib/types/extract-table-contract.js.map +1 -0
  76. package/lib/types/extract-table.d.ts +8 -0
  77. package/lib/types/extract-table.d.ts.map +1 -0
  78. package/lib/types/extract-table.js +151 -0
  79. package/lib/types/extract-table.js.map +1 -0
  80. package/lib/types/extraction-runs.d.ts +30 -6
  81. package/lib/types/extraction-runs.d.ts.map +1 -1
  82. package/lib/types/extraction-runs.js +69 -8
  83. package/lib/types/extraction-runs.js.map +1 -1
  84. package/lib/types/fs-atomic.d.ts +0 -7
  85. package/lib/types/fs-atomic.d.ts.map +1 -1
  86. package/lib/types/fs-atomic.js +17 -2
  87. package/lib/types/fs-atomic.js.map +1 -1
  88. package/lib/types/images.d.ts +1 -1
  89. package/lib/types/images.d.ts.map +1 -1
  90. package/lib/types/images.js +2 -2
  91. package/lib/types/images.js.map +1 -1
  92. package/lib/types/index.d.ts.map +1 -1
  93. package/lib/types/index.js +232 -50
  94. package/lib/types/index.js.map +1 -1
  95. package/lib/types/intent/scope.d.ts.map +1 -1
  96. package/lib/types/intent/scope.js +1 -0
  97. package/lib/types/intent/scope.js.map +1 -1
  98. package/lib/types/intent/tools.d.ts +19 -0
  99. package/lib/types/intent/tools.d.ts.map +1 -1
  100. package/lib/types/intent/tools.js +37 -1
  101. package/lib/types/intent/tools.js.map +1 -1
  102. package/lib/types/knowledge-bases.d.ts.map +1 -1
  103. package/lib/types/knowledge-bases.js +5 -16
  104. package/lib/types/knowledge-bases.js.map +1 -1
  105. package/lib/types/knowledge.d.ts +3 -0
  106. package/lib/types/knowledge.d.ts.map +1 -1
  107. package/lib/types/knowledge.js +21 -5
  108. package/lib/types/knowledge.js.map +1 -1
  109. package/lib/types/materialize-snapshot.d.ts +49 -0
  110. package/lib/types/materialize-snapshot.d.ts.map +1 -0
  111. package/lib/types/materialize-snapshot.js +2 -0
  112. package/lib/types/materialize-snapshot.js.map +1 -0
  113. package/lib/types/model-text.d.ts +1 -1
  114. package/lib/types/model-text.d.ts.map +1 -1
  115. package/lib/types/model-text.js +5 -4
  116. package/lib/types/model-text.js.map +1 -1
  117. package/lib/types/oplog.d.ts.map +1 -1
  118. package/lib/types/oplog.js +7 -0
  119. package/lib/types/oplog.js.map +1 -1
  120. package/lib/types/pipeline/engine.d.ts +60 -0
  121. package/lib/types/pipeline/engine.d.ts.map +1 -0
  122. package/lib/types/pipeline/engine.js +338 -0
  123. package/lib/types/pipeline/engine.js.map +1 -0
  124. package/lib/types/pipeline/store.d.ts +73 -0
  125. package/lib/types/pipeline/store.d.ts.map +1 -0
  126. package/lib/types/pipeline/store.js +143 -0
  127. package/lib/types/pipeline/store.js.map +1 -0
  128. package/lib/types/pipeline/types.d.ts +101 -0
  129. package/lib/types/pipeline/types.d.ts.map +1 -0
  130. package/lib/types/pipeline/types.js +5 -0
  131. package/lib/types/pipeline/types.js.map +1 -0
  132. package/lib/types/present.d.ts +2 -0
  133. package/lib/types/present.d.ts.map +1 -1
  134. package/lib/types/present.js +9 -4
  135. package/lib/types/present.js.map +1 -1
  136. package/lib/types/retention.d.ts +39 -0
  137. package/lib/types/retention.d.ts.map +1 -0
  138. package/lib/types/retention.js +42 -0
  139. package/lib/types/retention.js.map +1 -0
  140. package/lib/types/runtime.d.ts +115 -5
  141. package/lib/types/runtime.d.ts.map +1 -1
  142. package/lib/types/runtime.js +862 -132
  143. package/lib/types/runtime.js.map +1 -1
  144. package/lib/types/source-evidence.d.ts +9 -1
  145. package/lib/types/source-evidence.d.ts.map +1 -1
  146. package/lib/types/source-evidence.js +13 -5
  147. package/lib/types/source-evidence.js.map +1 -1
  148. package/lib/types/store/axis-references.js +3 -3
  149. package/lib/types/store/axis-references.js.map +1 -1
  150. package/lib/types/store/derived.d.ts +1 -1
  151. package/lib/types/store/derived.d.ts.map +1 -1
  152. package/lib/types/store/derived.js +41 -7
  153. package/lib/types/store/derived.js.map +1 -1
  154. package/lib/types/store/knowledge-db.d.ts +18 -0
  155. package/lib/types/store/knowledge-db.d.ts.map +1 -1
  156. package/lib/types/store/knowledge-db.js +111 -6
  157. package/lib/types/store/knowledge-db.js.map +1 -1
  158. package/lib/types/store/migrate.js +1 -1
  159. package/lib/types/store/migrate.js.map +1 -1
  160. package/lib/types/trajectory-types.d.ts +172 -0
  161. package/lib/types/trajectory-types.d.ts.map +1 -0
  162. package/lib/types/trajectory-types.js +36 -0
  163. package/lib/types/trajectory-types.js.map +1 -0
  164. package/lib/types/types.d.ts +3 -4
  165. package/lib/types/types.d.ts.map +1 -1
  166. package/lib/types/types.js.map +1 -1
  167. package/lib/types/wiki-finalize.d.ts +16 -0
  168. package/lib/types/wiki-finalize.d.ts.map +1 -1
  169. package/lib/types/wiki-finalize.js +26 -0
  170. package/lib/types/wiki-finalize.js.map +1 -1
  171. package/lib/types/wiki-materialize.d.ts.map +1 -1
  172. package/lib/types/wiki-materialize.js +3 -15
  173. package/lib/types/wiki-materialize.js.map +1 -1
  174. package/lib/types/wiki-trial-types.d.ts +3 -0
  175. package/lib/types/wiki-trial-types.d.ts.map +1 -1
  176. package/lib/types/wiki-trial.d.ts +2 -0
  177. package/lib/types/wiki-trial.d.ts.map +1 -1
  178. package/lib/types/wiki-trial.js +31 -7
  179. package/lib/types/wiki-trial.js.map +1 -1
  180. package/package.json +6 -3
  181. package/preset/tita-data-agent/agent.cordis.yml +36 -129
  182. package/src/axis-registry.ts +57 -9
  183. package/src/client/AttributionEventGuide.tsx +10 -9
  184. package/src/client/CompileDialogs.tsx +10 -2
  185. package/src/client/EnvironmentSetup.tsx +76 -16
  186. package/src/client/ExpansionGraph.tsx +13 -7
  187. package/src/client/ExtractionTasksPage.tsx +543 -156
  188. package/src/client/ImportFilesDialog.tsx +2 -13
  189. package/src/client/IntentTab.tsx +3 -1
  190. package/src/client/KnowledgeBaseTab.tsx +139 -122
  191. package/src/client/KnowledgeLayerSection.tsx +19 -2
  192. package/src/client/KnowledgePanel.tsx +17 -3
  193. package/src/client/ListPager.tsx +15 -5
  194. package/src/client/MaterializeTrajectoryView.tsx +124 -0
  195. package/src/client/NodeSettings.tsx +90 -25
  196. package/src/client/NormalizeTrajectoryView.tsx +136 -0
  197. package/src/client/PipelineNotice.tsx +26 -0
  198. package/src/client/RecallPanel.tsx +55 -25
  199. package/src/client/TabGuide.tsx +4 -4
  200. package/src/client/TrialReportView.tsx +61 -16
  201. package/src/client/TypeDefinitionDrawer.tsx +33 -9
  202. package/src/client/WikiCardPreview.tsx +43 -10
  203. package/src/client/WikiFlowPanel.tsx +4 -4
  204. package/src/client/architecture.tsx +1 -1
  205. package/src/client/attribution/AttributionTab.tsx +37 -36
  206. package/src/client/attribution/CaseView.tsx +3 -1
  207. package/src/client/attribution/EventView.tsx +14 -4
  208. package/src/client/attribution/RunView.tsx +2 -2
  209. package/src/client/attribution/ViewSwitch.tsx +20 -5
  210. package/src/client/attribution/graph-options.ts +38 -31
  211. package/src/client/attribution/model.ts +16 -9
  212. package/src/client/graph/palette.ts +165 -0
  213. package/src/client/pages.css +224 -28
  214. package/src/client/pages.tsx +33 -11
  215. package/src/client/styles.css +61 -6
  216. package/src/client/wiki-trial.ts +30 -1
  217. package/src/convert/host.ts +3 -0
  218. package/src/convert/ingest.ts +73 -9
  219. package/src/diagnosis/cards.ts +13 -8
  220. package/src/diagnosis/run.ts +27 -17
  221. package/src/diagnosis/schema.ts +2 -2
  222. package/src/diagnosis/seed-source.ts +5 -4
  223. package/src/diagnosis/seed.ts +6 -8
  224. package/src/diagnosis/store.ts +99 -69
  225. package/src/diagnosis/types.ts +10 -16
  226. package/src/environment/dsh-python.ts +156 -0
  227. package/src/environment/install.ts +134 -19
  228. package/src/environment/model-catalog.ts +45 -3
  229. package/src/environment/service.ts +111 -23
  230. package/src/environment/types.ts +6 -0
  231. package/src/events.ts +12 -0
  232. package/src/extract-contract.ts +6 -3
  233. package/src/extract-model.ts +2 -0
  234. package/src/extract-table-contract.ts +29 -0
  235. package/src/extract-table.ts +126 -0
  236. package/src/extraction-runs.ts +70 -11
  237. package/src/fs-atomic.ts +19 -2
  238. package/src/images.ts +2 -2
  239. package/src/index.ts +207 -72
  240. package/src/intent/scope.ts +1 -0
  241. package/src/intent/tools.ts +38 -1
  242. package/src/knowledge-bases.ts +4 -12
  243. package/src/knowledge.ts +24 -4
  244. package/src/materialize-snapshot.ts +46 -0
  245. package/src/model-text.ts +5 -3
  246. package/src/oplog.ts +7 -0
  247. package/src/pipeline/engine.ts +312 -0
  248. package/src/pipeline/store.ts +147 -0
  249. package/src/pipeline/types.ts +92 -0
  250. package/src/present.ts +10 -4
  251. package/src/retention.ts +76 -0
  252. package/src/runtime.ts +853 -131
  253. package/src/source-evidence.ts +12 -5
  254. package/src/store/axis-references.ts +3 -3
  255. package/src/store/derived.ts +40 -7
  256. package/src/store/knowledge-db.ts +111 -5
  257. package/src/store/migrate.ts +1 -1
  258. package/src/trajectory-types.ts +142 -0
  259. package/src/types.ts +3 -4
  260. package/src/wiki-finalize.ts +28 -0
  261. package/src/wiki-materialize.ts +3 -14
  262. package/src/wiki-trial-types.ts +3 -0
  263. package/src/wiki-trial.ts +26 -7
  264. package/src/client/convert/ConvertPanel.tsx +0 -583
  265. package/src/client/convert/styles.css +0 -138
  266. package/src/client/convert/styles.ts +0 -49
@@ -0,0 +1,165 @@
1
+ /**
2
+ * 画布上用的**颜色字面量**,只有这一份。
3
+ *
4
+ * ## 为什么是字面量,不是 CSS 变量
5
+ *
6
+ * G6 的 canvas 渲染器读不到 CSS 自定义属性:`var(--tita-primary)` 会被 `parseColor`
7
+ * 解析成全透明黑 —— 表现是「节点边框凭空消失」,而且不报错。所以这些值必须以
8
+ * `#rrggbb` 的形式交给画布。
9
+ *
10
+ * ## 为什么集中到一个模块
11
+ *
12
+ * 它们原来是三份互不相干的字面量:`attribution/graph-options.ts`(边与徽标)、
13
+ * `attribution/model.ts`(节点取色)、`ExpansionGraph.tsx`(扩展图的圆点)。三份各自
14
+ * 演化,于是同一个「中性文字」在三处的灰度不同,而**没有任何一处能看出它们本该相同**。
15
+ * 现在三处都从这里取,改一次就三处一起改。
16
+ *
17
+ * ## 必须守住的对比度
18
+ *
19
+ * 画布没有 CSS 的兜底,所以这里的值**由测量决定**,不是由手感决定。背景按
20
+ * `--tita-surface` = `#ffffff` 算(深浅主题下画布底色都由外层容器给,浅色是默认):
21
+ *
22
+ * - 标签墨色(`LABEL_INK` / `LABEL_FAINT` / `BADGE_INK`):≥ 4.5:1(正文级)。
23
+ * 它们画的是 9–11px 的字,比大字号更需要余量。
24
+ * - 静音描边(`OUTLINE_MUTED`):≥ 3:1(非文字 UI 的门槛)。
25
+ * - 引用层描边(`NODE_REFERENCE_STROKE`)与它的底色(`NODE_REFERENCE_FILL`)之间:
26
+ * ≥ 3:1。这一对原来只有 1.64:1 —— 边和填充几乎同色,节点看起来是一块平色。
27
+ * - 命中色(`HIT_FILL`):≥ 3:1;它同时是圆点填充和它的描边。
28
+ *
29
+ * 改动任何一个值之前先算一遍:`(L1 + 0.05) / (L2 + 0.05)`,`L` 是 WCAG 的相对亮度。
30
+ * 这条注释留在文件里的理由很具体 —— 「颜色慢慢漂到不达标」这件事没有编译期检查,
31
+ * 只有某个人真的去量。2026-10-18 量过一轮,下面每个值都过了。
32
+ */
33
+
34
+ /* ─────────────────────────────────────────────────────────── 画布底色上的文字与描边 */
35
+
36
+ /** 画布主文字色(节点标题、命中圆点的标签)。白底 7.7:1。 */
37
+ export const LABEL_INK = '#101828'
38
+
39
+ /** 次级文字色(扩展图里「预算外」邻居的标签、边的计数标签底色)。白底 4.9:1。 */
40
+ export const LABEL_FAINT = '#667085'
41
+
42
+ /** 静音描边:预算外的邻居、僵尸边。白底 5.0:1(原 `#98a2b3` 只有 2.58:1,不达标)。 */
43
+ export const OUTLINE_MUTED = '#667085'
44
+
45
+ /** 滑块式的圆角底:边标签垫在它上面。它是**底色**不是文字,所以浅。 */
46
+ export const LABEL_PLATE = '#f6f8fb'
47
+
48
+ /* ───────────────────────────────────────────────────────────── 扩展图的命中 / 邻居 */
49
+
50
+ /** 命中卡片:填充与描边同色(它是一个实心点,不是带框的卡片)。白底 5.4:1。 */
51
+ export const HIT_FILL = '#4868b2'
52
+
53
+ /** 预算内邻居:白底 + 命中色描边,靠描边区分「这是邻居」。 */
54
+ export const NEIGHBOR_FILL = '#ffffff'
55
+
56
+ /** 节点被选中的那一圈。白底 5.2:1。 */
57
+ export const SELECTION_RING = '#2563eb'
58
+
59
+ /* ───────────────────────────────────────────────────────────────────── 节点的取色 */
60
+
61
+ /** 中性节点(没有分类、没有失败模式的那一类):白底、浅描边、静音文字。 */
62
+ export const NEUTRAL_NODE = { fill: '#ffffff', stroke: '#d6dce6', text: LABEL_FAINT }
63
+
64
+ /**
65
+ * 引用层节点:**底色与描边必须分得开**。
66
+ *
67
+ * 原来是 `#eef4ff` 配 `#9ec2ff`(1.64:1),几乎是一块平色;描边提到 `#2563eb`
68
+ * 之后是 4.68:1,白底上也有 5.17:1。
69
+ */
70
+ export const NODE_REFERENCE_FILL = '#eef4ff'
71
+ export const NODE_REFERENCE_STROKE = '#2563eb'
72
+ export const NODE_REFERENCE_TEXT = '#1d4ed8'
73
+
74
+ /** 现象节点:琥珀。 */
75
+ export const NODE_SYMPTOM = { fill: '#fff7ea', stroke: '#b96d14', text: '#96580f' }
76
+
77
+ /** 检查点节点:紫。 */
78
+ export const NODE_CHECK_POINT = { fill: '#f5f1ff', stroke: '#c9b8ff', text: '#5b45be' }
79
+
80
+ /** 空区假设:描边是虚线,颜色与失败原因里的红同一档(它要人来看一眼)。 */
81
+ export const GAP_STROKE = '#b42318'
82
+
83
+ /** 僵尸边(90 天没走过):灰、虚线。与 `OUTLINE_MUTED` 分开命名是因为它还会变淡。 */
84
+ export const ZOMBIE_STROKE = '#9aa4b2'
85
+
86
+ /** 失败模式的取色。与 `model.ts` 的 `FAILURE_REASON_LABEL` 一一对应,顺序也一致。 */
87
+ export const FAILURE_REASON_FILL = {
88
+ 'upstream-missing': '#eef4ff',
89
+ 'spec-change': '#f3f0ff',
90
+ 'data-processing': '#eafaf2',
91
+ consumption: '#fff7ea',
92
+ permission: '#fdecea',
93
+ } as const
94
+
95
+ export const FAILURE_REASON_STROKE = {
96
+ 'upstream-missing': '#2563eb',
97
+ 'spec-change': '#6b54cf',
98
+ 'data-processing': '#12875f',
99
+ consumption: '#b96d14',
100
+ permission: '#b42318',
101
+ } as const
102
+
103
+ export const FAILURE_REASON_TEXT = {
104
+ 'upstream-missing': '#1d4ed8',
105
+ 'spec-change': '#5b45be',
106
+ 'data-processing': '#0f6b4d',
107
+ consumption: '#96580f',
108
+ permission: '#98200f',
109
+ } as const
110
+
111
+ /* ─────────────────────────────────────────────────────────────────────────── 边 */
112
+
113
+ /**
114
+ * 十二种边各自的外观色。
115
+ *
116
+ * 这里只有**颜色**;线宽与线型留在 `graph-options.ts` 的 `EDGE_STYLE` 里 —— 那些是
117
+ * 几何,不是调色板的一部分。名字与 `AttributionEdgeKind` 逐字相同,漏一种在类型上
118
+ * 就报错。
119
+ */
120
+ export const EDGE_STROKE = {
121
+ may_be_caused_by: '#93b4f8',
122
+ verified_by: '#6b54cf',
123
+ categorized_as: '#c9b8ff',
124
+ queries: '#9ec2ff',
125
+ produces: '#b96d14',
126
+ supports: '#12875f',
127
+ refutes: '#b42318',
128
+ confirmed_as: '#0f766e',
129
+ instance_of: '#b96d14',
130
+ /**
131
+ * 反查索引。原来是 `#9ec2ff`(白底 1.8:1)—— 与「要去查」那条边的颜色完全相同,
132
+ * 两条语义不同的边在画布上分不出来。现在深一档(4.68:1),与引用层节点描边同色:
133
+ * 它画的正是「这张表被哪条假设引用」。
134
+ */
135
+ indexed_from: '#2563eb',
136
+ } as const
137
+
138
+ /* ─────────────────────────────────────────────────────────────────────────── 徽标 */
139
+
140
+ /**
141
+ * 节点徽标(挂在节点上沿 / 下沿的小字牌)。
142
+ *
143
+ * `fontSize: 9` 是最小可读档,所以它的墨色必须比正文更保守:`#748091` 在白底上
144
+ * 只有 4.01:1(不达 4.5),换成 `#475467`(7.7:1)。
145
+ */
146
+ export const BADGE_INK = '#475467'
147
+ export const BADGE_PLATE = '#ffffff'
148
+ export const BADGE_PLATE_STROKE = '#e5e9f0'
149
+
150
+ /** 空区徽标的红:与 `GAP_STROKE` 同色,浅红描边把它从白底上托起来。 */
151
+ export const BADGE_GAP_STROKE = '#f0b7ad'
152
+
153
+ /** 问题分类徽标:琥珀文字 + 浅琥珀描边。 */
154
+ export const BADGE_PROBLEM_INK = '#96580f'
155
+ export const BADGE_PROBLEM_STROKE = '#f2ddb8'
156
+
157
+ /** 「N min · 自动 / 人工」徽标:自动走绿、人工走琥珀(同一档语义色)。 */
158
+ export const BADGE_AUTO_INK = '#12875f'
159
+ export const BADGE_MANUAL_INK = '#b96d14'
160
+
161
+ /** 边上的「N 例」计数标签:琥珀。 */
162
+ export const EDGE_COUNT_INK = '#b96d14'
163
+
164
+ /** 边上的「僵尸边 N 天未走」标签:静音灰。 */
165
+ export const EDGE_ZOMBIE_INK = '#9aa4b2'
@@ -50,6 +50,7 @@ body {
50
50
  .page-column { width: 100%; margin-inline: auto; }
51
51
  .page-column-help { max-width: 860px; }
52
52
  .page-column-trial { max-width: 1080px; }
53
+ .page-column-tasks { max-width: 1220px; }
53
54
  /* 架构说明页是**一块画布**,不是一栏正文:画布设计宽 1172px,再加右列 400px,
54
55
  窄于 1560 就只能靠缩字把图塞进来(实测 0.55 倍,卡片上的字读不出)。 */
55
56
  .page-column-guide { max-width: 1560px; }
@@ -90,36 +91,228 @@ body {
90
91
  }
91
92
  .page-loading { margin: 0; color: var(--tita-ink-muted); font-size: 12px; }
92
93
 
93
- .extractTasksHead { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; border-bottom: 1px solid var(--tita-line); padding: 2px 0 17px; }
94
- .extractTasksHead h2 { margin: 0; color: var(--tita-ink); font-size: 18px; font-weight: 730; letter-spacing: -.02em; }
95
- .extractTasksHead p { margin: 3px 0 0; color: var(--tita-ink-muted); font-size: 11.5px; }
96
- .extractTasksNote { margin: 12px 0 6px; color: var(--tita-ink-muted); font-size: 11px; }
97
- .extractTasksEmpty { display: flex; align-items: center; gap: 9px; border-bottom: 1px solid var(--tita-line); padding: 22px 0; color: var(--tita-ink-muted); font-size: 12px; }
98
- .extractTasksEmpty svg { flex: 0 0 auto; color: var(--tita-ink-faint); }
99
- .extractTaskList { display: grid; }
100
- .extractTaskRow { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 10px 22px; border-bottom: 1px solid var(--tita-line); padding: 15px 0; }
101
- .extractTaskMain { display: grid; min-width: 0; gap: 4px; }
102
- .extractTaskTitle { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
103
- .extractTaskTitle strong { color: var(--tita-ink); font-size: 12px; font-weight: 710; }
104
- .extractTaskMain > p { margin: 0; color: var(--tita-ink-muted); font-size: 11.5px; overflow-wrap: anywhere; }
105
- .extractTaskTime { display: inline-flex; align-items: center; gap: 5px; color: var(--tita-ink-faint); font-size: 10.5px; font-variant-numeric: tabular-nums; }
106
- .extractTaskActions { display: flex; flex: 0 0 auto; align-items: center; gap: 7px; }
107
- .extractTaskControls { display: flex; align-items: center; justify-content: flex-end; gap: 7px; }
108
- .extractTaskStepToggle { min-height: 30px; gap: 4px; padding-inline: 7px; color: var(--tita-ink-muted); font-size: 10.5px; }
94
+ /* ---------------------------------------------------------- 知识处理与运行记录
95
+ *
96
+ * 2026-10-18 重排。这一页只有三层,而每一层回答一个问题:
97
+ *
98
+ * 1. **页头** —— 现在什么状态(一行实时结论)+ 两个动作。**没有标题**:标题在常驻窄条上,
99
+ * 内容区再写一遍就是同一句话说两次。
100
+ * 2. **步骤轨** —— 四段各差多少。它横贯整页而不是缩在左栏里:这一页的骨架是一条有方向的
101
+ * 流水线,把骨架塞进左栏,右栏就变成了「第二根柱子」。
102
+ * 3. **两栏** —— 左栏「这一段跑了什么」,右栏「我要跑哪些」+「以前跑过什么」。
103
+ *
104
+ * 上一版的问题是**同一件事说了三遍**(页签徽标 / 段头 / 结果摘要的第一行都是「部分失败」),
105
+ * 而真正有用的「还剩多少待办」被压成一行 10.5px 的灰字,模型原始日志却占着一屏。
106
+ * 所以这里守一条:**一个事实只出现一次**。阶段状态只住在步骤轨上,待办计数只住在同一格的
107
+ * 第二行,段名只住在段头。
108
+ */
109
+ .extractTasksPage { display: grid; min-width: 0; gap: 15px; color: var(--tita-ink); }
110
+
111
+ .extractTasksHead { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 18px; }
112
+ /* 结论行:12.5px 而不是 13px —— 它是一句状态,不是正文。宽度上限 76ch:
113
+ 整页 1220px 时这一句会独占一整行,读起来像标题。 */
114
+ .extractTasksHeadline { max-width: 76ch; margin: 0; color: var(--tita-ink-muted); font-size: 12.5px; line-height: 1.6; }
115
+ .extractTasksHeadActions { display: flex; flex: 0 0 auto; align-items: center; gap: 6px; }
116
+ .extractTasksError { margin: 0; border: 1px solid color-mix(in srgb, var(--tita-danger) 32%, var(--tita-surface)); border-radius: 8px; background: color-mix(in srgb, var(--tita-danger) 8%, var(--tita-surface)); padding: 9px 12px; color: var(--tita-danger); font-size: 12px; }
117
+
118
+ /* ---- 步骤轨
119
+ *
120
+ * 复用 `KnowledgeStepRail` 那一套语言(节点 + 箭头 + 当前步描边),因为它们是同一件事:
121
+ * 一条流水线上「你现在站在哪一段」。区别只有一处 —— 这里的节点是 Radix 的 trigger,
122
+ * 所以键盘、`aria-controls`、方向键切换都是现成的。
123
+ *
124
+ * 默认的 `TabsList` 是「一排等宽按钮」(等宽 pill + 32px 高):它表达的是「几选一」,
125
+ * 而这里是「谁接谁」。所以把 pill 的底、内边距、固定高全部收掉,节点自己长成带描边的一格。
126
+ */
127
+ .extractTasksWork { display: flex; min-width: 0; flex-direction: column; gap: 14px; }
128
+ .extractTasksRailList {
129
+ display: flex;
130
+ width: 100%;
131
+ height: auto;
132
+ align-items: stretch;
133
+ gap: 6px;
134
+ border-radius: 0;
135
+ background: transparent;
136
+ padding: 0;
137
+ }
138
+ .extractTasksRailArrow { display: inline-flex; flex: 0 0 auto; align-self: center; align-items: center; color: var(--tita-ink-faint); }
139
+ .extractTasksRailStep {
140
+ display: grid;
141
+ flex: 1 1 0;
142
+ box-sizing: border-box;
143
+ min-width: 0;
144
+ height: auto;
145
+ align-content: start;
146
+ justify-items: start;
147
+ gap: 7px;
148
+ border: 1px solid var(--tita-line);
149
+ border-radius: 9px;
150
+ background: var(--tita-surface);
151
+ padding: 10px 12px;
152
+ color: var(--tita-ink-muted);
153
+ text-align: left;
154
+ white-space: normal;
155
+ transition: border-color 120ms ease-out, background-color 120ms ease-out;
156
+ }
157
+ .extractTasksRailStep:hover { border-color: var(--tita-line-strong); background: var(--tita-surface-muted); color: var(--tita-ink); }
158
+ .extractTasksRailStep:focus-visible { outline: 2px solid var(--tita-focus-ring); outline-offset: 1px; }
159
+ /* 当前步不是「已选中」,而是「你站在哪一段」。用主色描边 + 浅底,不用左侧色条 ——
160
+ 色条会把这一格读成一条「提示卡」,而它不是提示。两个属性都写上:Radix 给的是
161
+ `data-state="active"`,新版 shadcn 的类名等的是 `data-active`。 */
162
+ .extractTasksRailStep[data-state="active"],
163
+ .extractTasksRailStep[data-active] {
164
+ border-color: var(--tita-primary);
165
+ background: var(--tita-primary-soft);
166
+ box-shadow: inset 0 0 0 1px var(--tita-primary);
167
+ color: var(--tita-ink);
168
+ }
169
+ .extractTasksRailName { min-width: 0; color: inherit; font-size: 12.5px; font-weight: 680; line-height: 1.4; letter-spacing: -.01em; }
170
+ .extractTasksRailFacts { display: flex; min-width: 0; flex-wrap: wrap; align-items: center; gap: 6px; }
171
+ /* 待办计数:等宽数字,**比徽标轻** —— 徽标说的是「上次跑成什么样」(结论),
172
+ 这一句说的是「现在还剩多少」(读数)。两件事不同重,才读得出哪句是结论。 */
173
+ .extractTasksRailPending { color: var(--tita-ink-faint); font-size: 10.5px; line-height: 1.4; white-space: nowrap; font-variant-numeric: tabular-nums; }
174
+
175
+ /* ---- 两栏 */
176
+ .extractTasksBody { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(316px, 1fr); align-items: start; gap: 18px; }
177
+ .extractTasksMain { min-width: 0; }
178
+ .extractTasksSide { display: grid; min-width: 0; gap: 14px; }
179
+
180
+ /* ---- 阶段卡
181
+ *
182
+ * 卡里**不再套卡**:输入范围 / 结果摘要 / 抽取报告 / 运行输出 都是这张卡里的**分节**,
183
+ * 用一条发丝线分开,不各长一圈描边。卡片套卡片读起来是四个同级的东西,
184
+ * 而它们其实是同一件事的四段。
185
+ */
186
+ .extractTasksStage { min-width: 0; border: 1px solid var(--tita-line); border-radius: 10px; background: var(--tita-surface); padding: 15px 17px 17px; }
187
+ .extractTasksStageIntro { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; }
188
+ .extractTasksStageIntro > div { min-width: 0; }
189
+ .extractTasksStageIntro h2 { margin: 0; color: var(--tita-ink); font-size: 14.5px; font-weight: 700; line-height: 1.4; letter-spacing: -.015em; }
190
+ .extractTasksStageIntro p { margin: 3px 0 0; color: var(--tita-ink-muted); font-size: 11.5px; line-height: 1.55; }
191
+
192
+ .extractTasksFacts { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0; border-top: 1px solid var(--tita-line); margin-top: 13px; }
193
+ .extractTasksFacts > section { min-width: 0; padding-top: 13px; }
194
+ .extractTasksFacts > section + section { border-left: 1px solid var(--tita-line); padding-left: 20px; }
195
+ .extractTasksFacts h3 { margin: 0 0 9px; color: var(--tita-ink-muted); font-size: 10.5px; font-weight: 700; letter-spacing: .02em; }
196
+ .extractTasksFacts dl { display: grid; gap: 7px; margin: 0; }
197
+ .extractTasksFacts dl > div { display: grid; grid-template-columns: 68px minmax(0, 1fr); align-items: start; gap: 10px; }
198
+ .extractTasksFacts dt { color: var(--tita-ink-faint); font-size: 10.5px; line-height: 1.55; }
199
+ .extractTasksFacts dd { min-width: 0; margin: 0; color: var(--tita-ink); font-size: 11px; line-height: 1.55; overflow-wrap: anywhere; }
200
+
201
+ /* ---- 抽取报告 */
202
+ .extractTasksReports { border-top: 1px solid var(--tita-line); margin-top: 15px; padding-top: 13px; }
203
+ .extractTasksSubhead { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
204
+ .extractTasksSubhead > div { min-width: 0; }
205
+ .extractTasksSubhead h3 { margin: 0; color: var(--tita-ink); font-size: 11.5px; font-weight: 700; }
206
+ .extractTasksSubhead p { margin: 3px 0 0; color: var(--tita-ink-faint); font-size: 10.5px; line-height: 1.5; }
207
+ .extractTasksCount { flex: 0 0 auto; color: var(--tita-ink-muted); font-size: 11px; font-variant-numeric: tabular-nums; }
208
+ .extractTasksEmpty { margin: 0; padding: 14px 0; color: var(--tita-ink-muted); font-size: 11.5px; }
209
+ /* 这一行是一个 `<button>`:应用内路径不能靠 `<a href>` 开(见 `ExtractionTasksPage`
210
+ 里那段注释)。所以要把裸按钮的浏览器默认样式复位掉,外观保持原样。 */
211
+ .extractReportRow { display: flex; width: 100%; min-width: 0; align-items: flex-start; justify-content: space-between; gap: 14px; border: 0; border-bottom: 1px solid var(--tita-line); border-radius: 0; background: none; padding: 11px 0; color: var(--tita-ink); font: inherit; text-align: left; text-decoration: none; cursor: pointer; }
212
+ .extractReportRow:last-child { border-bottom: 0; padding-bottom: 2px; }
213
+ .extractReportRow:hover .extractReportMain strong { color: var(--tita-primary); text-decoration: underline; text-underline-offset: 3px; }
214
+ .extractReportRow:focus-visible { outline: 2px solid var(--tita-focus-ring); outline-offset: 2px; border-radius: 6px; }
215
+ .extractReportMain { display: grid; min-width: 0; gap: 3px; }
216
+ .extractReportMain strong { font-size: 12px; font-weight: 650; line-height: 1.5; overflow-wrap: anywhere; }
217
+ .extractReportMain > span { color: var(--tita-ink-muted); font-size: 11px; line-height: 1.55; overflow-wrap: anywhere; }
218
+ .extractReportMeta { display: inline-flex; flex: 0 0 auto; align-items: center; gap: 6px; color: var(--tita-ink-faint); font-size: 10.5px; font-variant-numeric: tabular-nums; white-space: nowrap; }
219
+
220
+ /* ---- 运行输出(默认收起) */
221
+ .extractTasksOutput { border-top: 1px solid var(--tita-line); margin-top: 15px; padding-top: 4px; }
222
+ .extractTasksOutputToggle { gap: 5px; padding-inline: 2px 6px; color: var(--tita-ink-muted); font-size: 11px; font-weight: 650; }
223
+ .extractTasksOutputToggle:hover { color: var(--tita-ink); }
224
+ /* 第二段是「展开 / 收起」的补充说明:它是提示,不是按钮名,所以更轻。 */
225
+ .extractTasksOutputToggle > span { color: var(--tita-ink-faint); font-size: 10.5px; font-weight: 500; }
226
+ .extractTasksOutputToggle svg { transition: transform 140ms ease-out; }
227
+ .extractTasksOutputToggle[aria-expanded="true"] svg { transform: rotate(180deg); }
228
+ .extractTasksOutputBody { display: grid; gap: 8px; margin-top: 6px; }
229
+ .extractTasksOutputBody > p { margin: 0; color: var(--tita-ink-muted); font-size: 11px; line-height: 1.55; }
230
+ /* 模型原始返回:等宽、可滚、限高。**限高 320px** —— 它是底料,不是结论,
231
+ 但也不能矮到看不见上下文。 */
232
+ .extractTasksPre { margin: 0; max-height: 320px; overflow: auto; border-radius: 8px; background: var(--tita-surface-muted); padding: 11px 13px; color: var(--tita-ink); font-family: var(--tita-mono); font-size: 11.5px; line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere; tab-size: 2; }
233
+ .extractTasksReportsInline { display: flex; flex-wrap: wrap; gap: 14px; }
234
+ .extractTasksLink { height: auto; padding-inline: 0; color: var(--tita-primary); font-size: 11px; font-weight: 500; text-decoration: underline; text-underline-offset: 2px; }
235
+ .extractTasksLink:hover { background: transparent; color: var(--tita-primary); }
236
+
237
+ /* ---- 右侧两栏:面板 */
238
+ .extractTasksPanel { min-width: 0; overflow: hidden; border: 1px solid var(--tita-line); border-radius: 10px; background: var(--tita-surface); }
239
+ .extractTasksPanelHead { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; border-bottom: 1px solid var(--tita-line); padding: 13px 15px 11px; }
240
+ .extractTasksPanelHead > div { min-width: 0; }
241
+ .extractTasksPanelHead h2 { margin: 0; color: var(--tita-ink); font-size: 13px; font-weight: 700; letter-spacing: -.01em; }
242
+ .extractTasksPanelHead p { margin: 3px 0 0; color: var(--tita-ink-muted); font-size: 11px; line-height: 1.5; }
243
+ .extractTasksPanelBody { min-width: 0; padding: 0 15px; }
244
+
245
+ /* ---- 处理范围
246
+ *
247
+ * 每一行原来是一枚「选择」按钮 + 名字 + 徽标:N 行就有 N 枚写着同一个词的按钮,
248
+ * 而真正要读的是那一行的名字。现在整行就是按钮,前面一枚方框表示选中
249
+ * (`aria-pressed` 与视觉同时成立,键盘 Tab 也走得通)。
250
+ */
251
+ .extractTasksScopeTab { margin-top: 12px; }
252
+ .extractTasksScopeNote { margin: 0 0 2px; color: var(--tita-ink-muted); font-size: 11px; line-height: 1.5; }
253
+ .extractTasksChoiceList { display: grid; max-height: 260px; overflow: auto; margin: 0; padding: 0; list-style: none; scrollbar-color: var(--tita-line-strong) transparent; scrollbar-width: thin; }
254
+ .extractTasksChoiceList > li { border-bottom: 1px solid var(--tita-line); }
255
+ .extractTasksChoiceList > li:last-child { border-bottom: 0; }
256
+ .extractTasksPick { display: flex; width: 100%; min-width: 0; align-items: center; gap: 9px; border: 0; border-radius: 7px; background: none; padding: 9px 4px 9px 2px; color: inherit; font: inherit; text-align: left; cursor: pointer; }
257
+ .extractTasksPick:hover { background: var(--tita-surface-muted); }
258
+ .extractTasksPick:focus-visible { outline: 2px solid var(--tita-focus-ring); outline-offset: 1px; }
259
+ .extractTasksPickBox { display: inline-flex; width: 14px; height: 14px; flex: 0 0 auto; align-items: center; justify-content: center; border: 1px solid var(--tita-line-strong); border-radius: 4px; background: var(--tita-surface); color: var(--tita-primary); }
260
+ .extractTasksPick[aria-pressed="true"] .extractTasksPickBox { border-color: var(--tita-primary); background: var(--tita-primary-soft); }
261
+ .extractTasksChoiceName { display: grid; min-width: 0; flex: 1 1 auto; gap: 2px; color: var(--tita-ink); font-size: 11.5px; line-height: 1.5; overflow-wrap: anywhere; }
262
+ .extractTasksChoiceName > span { color: var(--tita-ink-muted); font-size: 10.5px; }
263
+ .extractTasksChoiceName > span.extractTasksGroupReason { color: var(--tita-warning); }
264
+ .extractTasksChoiceName > strong { font-weight: 620; }
265
+ .extractTasksGroupList { max-height: 330px; }
266
+ .extractTasksScopeActions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; border-top: 1px solid var(--tita-line); margin-top: 4px; padding: 10px 0 11px; }
267
+
268
+ /* ---- 运行记录
269
+ *
270
+ * 一行一条,不设左右两列:这一栏只有 316–420px,右侧再挂一排动作会把摘要压成三行。
271
+ * 所以身份 / 摘要 / 时间 / 动作四行顺着写下来,动作在最下面一行左对齐 ——
272
+ * 视线从「哪一段、跑成什么样」自然落到「还能对它做什么」。
273
+ */
274
+ .extractTaskRow { display: grid; gap: 5px; border-bottom: 1px solid var(--tita-line); padding: 12px 0; }
275
+ .extractTaskRow:last-of-type { border-bottom: 0; }
276
+ .extractTaskTitle { display: flex; min-width: 0; align-items: center; flex-wrap: wrap; gap: 7px; }
277
+ .extractTaskTitle strong { color: var(--tita-ink); font-size: 12px; font-weight: 680; }
278
+ .extractTaskSummary { margin: 0; color: var(--tita-ink-muted); font-size: 11px; line-height: 1.55; overflow-wrap: anywhere; }
279
+ .extractTaskTime { margin: 0; color: var(--tita-ink-faint); font-size: 10.5px; line-height: 1.5; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
280
+ .extractTaskControls { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; margin-top: 2px; }
281
+ .extractTaskStepToggle { gap: 4px; padding-inline: 6px; color: var(--tita-ink-muted); font-size: 10.5px; }
109
282
  .extractTaskStepToggle[aria-expanded="true"] svg { transform: rotate(180deg); }
110
283
  .extractTaskStepToggle svg { transition: transform 120ms ease-out; }
111
- .extractTaskSteps { grid-column: 1 / -1; min-width: 0; border-top: 1px solid var(--tita-line); margin-top: 2px; padding-top: 3px; }
112
- .extractTaskSteps .tita-compileSteps { margin-top: 8px; }
113
- .extractTaskActionError { max-width: 210px; color: var(--tita-danger); font-size: 10.5px; }
114
- .extractTaskActions .tita-uiButton { min-height: 30px; padding-inline: 9px; font-size: 11px; }
115
- .extractTaskSpin { animation: extractTaskSpin .9s linear infinite; }
116
- @keyframes extractTaskSpin { to { transform: rotate(360deg); } }
117
- @media (max-width: 680px) {
284
+ .extractTaskSteps { min-width: 0; }
285
+
286
+ /* ---- 窄屏 */
287
+ @media (max-width: 1080px) {
288
+ /* 两栏并排需要约 950px 才站得住(左栏 560 + 右栏 316 + 间隔)。再窄就把右栏落到下面 ——
289
+ 硬挤的结果是左栏的结论被压成一行两个字。 */
290
+ .extractTasksBody { grid-template-columns: minmax(0, 1fr); }
291
+ }
292
+
293
+ @media (max-width: 720px) {
118
294
  .page-main { padding: 18px 16px 70px; }
119
295
  .page-bar { padding-inline: 16px; }
120
- .extractTaskRow { grid-template-columns: minmax(0, 1fr); gap: 10px; }
121
- .extractTaskControls { justify-content: flex-end; }
122
- .extractTaskSteps { grid-column: 1; }
296
+ .extractTasksHead { align-items: flex-start; }
297
+ /* 窄屏把步骤轨排成 2×2:四个节点横排时每个不到 90px,段名会被折成三行。
298
+ 箭头在这一档去掉 —— 网格的阅读顺序已经是 1→2→3→4,再画箭头只会插在两行之间空转。 */
299
+ .extractTasksRailList { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
300
+ .extractTasksRailArrow { display: none; }
301
+ .extractTasksStageIntro { flex-direction: column; }
302
+ .extractTasksFacts { grid-template-columns: minmax(0, 1fr); }
303
+ .extractTasksFacts > section + section { border-top: 1px solid var(--tita-line); border-left: 0; margin-top: 13px; padding-left: 0; }
304
+ .extractReportRow { flex-direction: column; gap: 4px; }
305
+ .extractReportMeta { white-space: normal; }
306
+ .extractTaskStepToggle { padding-inline: 0 4px; }
307
+ }
308
+
309
+ @media (max-width: 420px) {
310
+ .extractTasksFacts dl > div { grid-template-columns: 62px minmax(0, 1fr); gap: 8px; }
311
+ .extractTasksRailStep { padding: 9px 10px; }
312
+ }
313
+
314
+ @media (prefers-reduced-motion: reduce) {
315
+ .extractTasksRailStep, .extractTaskStepToggle svg, .extractTasksOutputToggle svg { transition: none; }
123
316
  }
124
317
 
125
318
  /* ---------------------------------------------------------- attribution event guide
@@ -144,7 +337,9 @@ body {
144
337
  .attributionGuideSectionHead p:last-child { max-width: 72ch; margin: 0; color: var(--tita-ink-muted); font-size: 12px; line-height: 1.7; }
145
338
  .attributionGuidePipeline { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1.25fr) auto minmax(0, 1fr); align-items: center; gap: 9px; border: 1px solid var(--tita-line); border-radius: 14px; background: var(--tita-surface); padding: 18px 16px; }
146
339
  .attributionGuideBranches { display: grid; gap: 10px; border: 1px dashed var(--tita-line-strong); border-radius: 12px; background: var(--tita-surface-muted); padding: 10px; }
147
- .attributionGuideNode { display: grid; min-width: 0; gap: 3px; border-left: 3px solid var(--tita-line-strong); padding: 2px 0 2px 11px; }
340
+ /* 1px 发丝线,不是 3px 色条:这一页其余的分隔全是发丝线,一条 3px 的竖条会把它读成
341
+ 另一个东西(而且那是模板的记号)。段位身份色仍然在下面那枚小徽标上,没有丢。 */
342
+ .attributionGuideNode { display: grid; min-width: 0; gap: 3px; border-left: 1px solid var(--tita-line-strong); padding: 2px 0 2px 11px; }
148
343
  .attributionGuideNode-source { border-left-color: var(--tita-tone-blue); }
149
344
  .attributionGuideNode-event { border-left-color: var(--tita-tone-green); }
150
345
  .attributionGuideNode-knowledge { border-left-color: var(--tita-tone-violet); }
@@ -162,7 +357,8 @@ body {
162
357
  .attributionGuideRelations { display: flex; flex-wrap: wrap; gap: 8px 18px; border-top: 1px solid var(--tita-line); padding-top: 11px; color: var(--tita-ink-muted); font-size: 10.5px; }
163
358
  .attributionGuideRelations span { display: inline-flex; align-items: center; gap: 5px; }
164
359
  .attributionGuideRelations svg { color: var(--tita-primary); }
165
- .attributionGuideCaseIntro { display: flex; align-items: center; justify-content: space-between; gap: 14px; border-left: 3px solid var(--tita-tone-green); background: var(--tita-tone-green-tint); padding: 10px 13px; }
360
+ /* 底色留着(它说的是「这一块是案例」),竖条收成 1px。 */
361
+ .attributionGuideCaseIntro { display: flex; align-items: center; justify-content: space-between; gap: 14px; border-left: 1px solid var(--tita-tone-green); background: var(--tita-tone-green-tint); padding: 10px 13px; }
166
362
  .attributionGuideCaseIntro > div { display: grid; gap: 3px; }
167
363
  .attributionGuideLabel { color: var(--tita-ink-faint); font-family: var(--tita-mono); font-size: 9.5px; }
168
364
  .attributionGuideCaseIntro p { margin: 0; color: var(--tita-ink-muted); font-size: 10.5px; }
@@ -45,8 +45,11 @@ import { TabGuidePage } from './TabGuide.tsx'
45
45
  import { WikiFlowPanel } from './WikiFlowPanel.tsx'
46
46
  import { WikiTrialRun, type TrialDocumentOption } from './WikiTrialRun.tsx'
47
47
  import { TrialReport } from './TrialReportView.tsx'
48
+ import { NormalizeTrajectory } from './NormalizeTrajectoryView.tsx'
49
+ import { MaterializeTrajectory } from './MaterializeTrajectoryView.tsx'
48
50
  import { ExtractionTasksPage } from './ExtractionTasksPage.tsx'
49
- import { requestExtractionRun, type ExtractionRunRecord } from './wiki-trial.ts'
51
+ import { requestExtractionRun, type ExtractionRunRecord, type TrajectoryReportView } from './wiki-trial.ts'
52
+ import { TRAJECTORY_STAGE_LABEL, type MaterializeTrajectoryReport, type NormalizeTrajectoryReport } from '../trajectory-types.js'
50
53
  import { formatDateTime } from './format.ts'
51
54
 
52
55
  /** 宿主把这一族页面都挂在这个前缀下。 */
@@ -98,7 +101,7 @@ async function getJson<T>(url: string): Promise<T> {
98
101
  function PageShell({ title, meta, column, children }: {
99
102
  title: string
100
103
  meta?: string
101
- column: 'help' | 'trial' | 'guide'
104
+ column: 'help' | 'trial' | 'tasks' | 'guide'
102
105
  children: ReactNode
103
106
  }): ReactNode {
104
107
  return <>
@@ -168,12 +171,18 @@ function TrialPage({ kb }: { kb: string }): ReactNode {
168
171
  }
169
172
 
170
173
  /**
171
- * 一次抽取的完整报告(2026-10-06)。
174
+ * 一次运行的完整轨迹(2026-10-06;2026-10-18 起三段共用这一页)。
172
175
  *
173
- * 它是 SourceRaw 详情里「抽取轨迹」那一行的落点:点一行就开在这里。
176
+ * 它是 SourceRaw 详情与任务页里「轨迹」那一行的落点:点一行就开在这里。
174
177
  * 此前报告是**就地展开在抽屉里**的 —— 报告几十 KB,400px 的栏里滚动着读不了它,
175
178
  * 而这份页面有整页宽度、常驻标题条、能 Cmd+F(与「新页面打开」读原文是同一条路)。
176
179
  *
180
+ * ## 为什么一页装三段
181
+ *
182
+ * 三段轨迹的形状不同(见 `trajectory-types.ts`),但**看它们的方式相同**:点开一条记录,
183
+ * 在浏览器里读它的全过程。所以地址只有一种(`?kb=&run=`),标题与正文按记录自己的
184
+ * `stage` 分派 —— 三个地址等于让人先学会三套入口规则,而收益只是 URL 好看一点。
185
+ *
177
186
  * 页面自己按 `?kb=&run=` 取数,与面板共用同一条 `/knowledge/extraction-run` 接口。
178
187
  */
179
188
  function RunPage({ kb, runId }: { kb: string; runId: string }): ReactNode {
@@ -207,20 +216,33 @@ function RunPage({ kb, runId }: { kb: string; runId: string }): ReactNode {
207
216
  }
208
217
 
209
218
  if (run === null) {
210
- return <PageShell title="抽取轨迹" meta="正在读取…" column="trial">
211
- <p className="page-loading" role="status">正在读取这次抽取的报告…</p>
219
+ return <PageShell title="轨迹" meta="正在读取…" column="trial">
220
+ <p className="page-loading" role="status">正在读取这次运行的轨迹…</p>
212
221
  </PageShell>
213
222
  }
214
223
 
215
224
  return <PageShell
216
- title="抽取轨迹"
217
- meta={`${formatDateTime(run.createdAt)} · ${run.actor === 'agent' ? 'Agent 发起' : '面板发起'}`}
225
+ title={TRAJECTORY_STAGE_LABEL[run.stage]}
226
+ meta={`${run.title ? `${run.title} · ` : ''}${formatDateTime(run.createdAt)} · ${run.actor === 'agent' ? 'Agent 发起' : '面板发起'}`}
218
227
  column="trial"
219
228
  >
220
- <TrialReport report={run.report} />
229
+ {trajectoryBody(run.stage, run.report)}
221
230
  </PageShell>
222
231
  }
223
232
 
233
+ /**
234
+ * 按 `stage` 分派正文。
235
+ *
236
+ * 三个 payload 的形状不同,而列表接口只给「这是一条什么记录」,所以这里的收窄是**断言**:
237
+ * 服务端在落库时用的就是这套形状(`stage` 与 `report` 由同一个调用点一起写),
238
+ * 而不是两个互不相干的字段。断言集中在这一个函数里,别的组件拿到的都是具体类型。
239
+ */
240
+ function trajectoryBody(stage: ExtractionRunRecord['stage'], report: TrajectoryReportView): ReactNode {
241
+ if (stage === 'normalize') return <NormalizeTrajectory report={report as NormalizeTrajectoryReport} />
242
+ if (stage === 'materialize') return <MaterializeTrajectory report={report as MaterializeTrajectoryReport} />
243
+ return <TrialReport report={report as Parameters<typeof TrialReport>[0]['report']} />
244
+ }
245
+
224
246
  function App(): ReactNode {
225
247
  const route = routeOf(window.location)
226
248
 
@@ -228,7 +250,7 @@ function App(): ReactNode {
228
250
 
229
251
  if (route.kind === 'run') return <RunPage kb={route.kb} runId={route.run} />
230
252
 
231
- if (route.kind === 'tasks') return <PageShell title="抽取任务" column="trial"><ExtractionTasksPage kb={route.kb} /></PageShell>
253
+ if (route.kind === 'tasks') return <PageShell title="知识处理" column="tasks"><ExtractionTasksPage kb={route.kb} /></PageShell>
232
254
 
233
255
  if (route.kind === 'help') {
234
256
  if (route.topic === 'overview') {
@@ -248,7 +270,7 @@ function App(): ReactNode {
248
270
  <p>整页的地址只有这几种:</p>
249
271
  <p><code>/knowledge/pages/trial?kb=&lt;知识库 id&gt;</code> · 抽取试跑<br />
250
272
  <code>/knowledge/pages/run?kb=&lt;知识库 id&gt;&amp;run=&lt;轨迹 id&gt;</code> · 一次抽取的报告<br />
251
- <code>/knowledge/pages/tasks?kb=&lt;知识库 id&gt;</code> · 抽取任务<br />
273
+ <code>/knowledge/pages/tasks?kb=&lt;知识库 id&gt;</code> · 知识处理与运行记录<br />
252
274
  <code>/knowledge/pages/help/compile</code> · 编译和召回链路<br />
253
275
  <code>/knowledge/pages/help/intent</code> · 意图的逻辑<br />
254
276
  <code>/knowledge/pages/help/attribution</code> · 智能归因事件<br />