dsh-data-domain-knowledge 0.1.0-alpha.2 → 0.1.0-alpha.4

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 (180) hide show
  1. package/README.md +147 -19
  2. package/cordis.patch.yml +25 -10
  3. package/lib/client.js +6400 -3326
  4. package/lib/doc-page.js +7761 -0
  5. package/lib/types/convert/host.d.ts.map +1 -1
  6. package/lib/types/convert/host.js +63 -11
  7. package/lib/types/convert/host.js.map +1 -1
  8. package/lib/types/convert/ingest.d.ts +7 -4
  9. package/lib/types/convert/ingest.d.ts.map +1 -1
  10. package/lib/types/convert/ingest.js +52 -14
  11. package/lib/types/convert/ingest.js.map +1 -1
  12. package/lib/types/convert/types.d.ts +2 -2
  13. package/lib/types/convert/types.d.ts.map +1 -1
  14. package/lib/types/convert/types.js +2 -2
  15. package/lib/types/convert/types.js.map +1 -1
  16. package/lib/types/diagnosis/cards.d.ts +8 -7
  17. package/lib/types/diagnosis/cards.d.ts.map +1 -1
  18. package/lib/types/diagnosis/cards.js +119 -49
  19. package/lib/types/diagnosis/cards.js.map +1 -1
  20. package/lib/types/diagnosis/run.d.ts +57 -0
  21. package/lib/types/diagnosis/run.d.ts.map +1 -0
  22. package/lib/types/diagnosis/run.js +245 -0
  23. package/lib/types/diagnosis/run.js.map +1 -0
  24. package/lib/types/diagnosis/schema.d.ts +25 -2
  25. package/lib/types/diagnosis/schema.d.ts.map +1 -1
  26. package/lib/types/diagnosis/schema.js +64 -25
  27. package/lib/types/diagnosis/schema.js.map +1 -1
  28. package/lib/types/diagnosis/seed-source.d.ts +17 -2
  29. package/lib/types/diagnosis/seed-source.d.ts.map +1 -1
  30. package/lib/types/diagnosis/seed-source.js +19 -2
  31. package/lib/types/diagnosis/seed-source.js.map +1 -1
  32. package/lib/types/diagnosis/seed.d.ts +32 -42
  33. package/lib/types/diagnosis/seed.d.ts.map +1 -1
  34. package/lib/types/diagnosis/seed.js +187 -51
  35. package/lib/types/diagnosis/seed.js.map +1 -1
  36. package/lib/types/diagnosis/store.d.ts +95 -25
  37. package/lib/types/diagnosis/store.d.ts.map +1 -1
  38. package/lib/types/diagnosis/store.js +297 -166
  39. package/lib/types/diagnosis/store.js.map +1 -1
  40. package/lib/types/diagnosis/types.d.ts +168 -16
  41. package/lib/types/diagnosis/types.d.ts.map +1 -1
  42. package/lib/types/diagnosis/types.js +1 -1
  43. package/lib/types/diagnosis/types.js.map +1 -1
  44. package/lib/types/document-page.d.ts +39 -0
  45. package/lib/types/document-page.d.ts.map +1 -0
  46. package/lib/types/document-page.js +43 -0
  47. package/lib/types/document-page.js.map +1 -0
  48. package/lib/types/extract-model.d.ts +67 -27
  49. package/lib/types/extract-model.d.ts.map +1 -1
  50. package/lib/types/extract-model.js +225 -97
  51. package/lib/types/extract-model.js.map +1 -1
  52. package/lib/types/extract.d.ts +16 -0
  53. package/lib/types/extract.d.ts.map +1 -1
  54. package/lib/types/extract.js +19 -2
  55. package/lib/types/extract.js.map +1 -1
  56. package/lib/types/fs-atomic.d.ts +11 -0
  57. package/lib/types/fs-atomic.d.ts.map +1 -0
  58. package/lib/types/fs-atomic.js +41 -0
  59. package/lib/types/fs-atomic.js.map +1 -0
  60. package/lib/types/index.d.ts.map +1 -1
  61. package/lib/types/index.js +466 -63
  62. package/lib/types/index.js.map +1 -1
  63. package/lib/types/intent/types.js +1 -1
  64. package/lib/types/intent/types.js.map +1 -1
  65. package/lib/types/knowledge-bases.d.ts +23 -12
  66. package/lib/types/knowledge-bases.d.ts.map +1 -1
  67. package/lib/types/knowledge-bases.js +37 -44
  68. package/lib/types/knowledge-bases.js.map +1 -1
  69. package/lib/types/layout.d.ts +3 -3
  70. package/lib/types/layout.d.ts.map +1 -1
  71. package/lib/types/layout.js +4 -4
  72. package/lib/types/layout.js.map +1 -1
  73. package/lib/types/migrate.d.ts +1 -1
  74. package/lib/types/migrate.js +2 -2
  75. package/lib/types/migrate.js.map +1 -1
  76. package/lib/types/model-text.d.ts +99 -0
  77. package/lib/types/model-text.d.ts.map +1 -0
  78. package/lib/types/model-text.js +103 -0
  79. package/lib/types/model-text.js.map +1 -0
  80. package/lib/types/present.d.ts +239 -0
  81. package/lib/types/present.d.ts.map +1 -0
  82. package/lib/types/present.js +255 -0
  83. package/lib/types/present.js.map +1 -0
  84. package/lib/types/recall-rank.d.ts +37 -0
  85. package/lib/types/recall-rank.d.ts.map +1 -1
  86. package/lib/types/recall-rank.js +41 -1
  87. package/lib/types/recall-rank.js.map +1 -1
  88. package/lib/types/runtime.d.ts +564 -31
  89. package/lib/types/runtime.d.ts.map +1 -1
  90. package/lib/types/runtime.js +2645 -292
  91. package/lib/types/runtime.js.map +1 -1
  92. package/lib/types/settings-schema.d.ts +4 -2
  93. package/lib/types/settings-schema.d.ts.map +1 -1
  94. package/lib/types/settings-schema.js +2 -0
  95. package/lib/types/settings-schema.js.map +1 -1
  96. package/lib/types/source-images.d.ts +118 -0
  97. package/lib/types/source-images.d.ts.map +1 -0
  98. package/lib/types/source-images.js +288 -0
  99. package/lib/types/source-images.js.map +1 -0
  100. package/lib/types/types.d.ts +356 -28
  101. package/lib/types/types.d.ts.map +1 -1
  102. package/lib/types/types.js +112 -18
  103. package/lib/types/types.js.map +1 -1
  104. package/lib/types/wiki-finalize.d.ts +140 -0
  105. package/lib/types/wiki-finalize.d.ts.map +1 -0
  106. package/lib/types/wiki-finalize.js +269 -0
  107. package/lib/types/wiki-finalize.js.map +1 -0
  108. package/lib/types/wiki-types.d.ts +10 -2
  109. package/lib/types/wiki-types.d.ts.map +1 -1
  110. package/lib/types/wiki-types.js +27 -30
  111. package/lib/types/wiki-types.js.map +1 -1
  112. package/package.json +4 -3
  113. package/preset/tita-data-agent/agent.cordis.yml +25 -10
  114. package/src/client/DirectoryPicker.tsx +12 -12
  115. package/src/client/DrawerSection.tsx +18 -0
  116. package/src/client/InfoHint.tsx +21 -8
  117. package/src/client/IntentTab.tsx +20 -26
  118. package/src/client/KnowledgeBaseTab.tsx +458 -158
  119. package/src/client/KnowledgePanel.tsx +44 -10
  120. package/src/client/KnowledgeStepRail.tsx +3 -0
  121. package/src/client/KnowledgeToolRow.tsx +331 -0
  122. package/src/client/NodeSettings.tsx +20 -22
  123. package/src/client/PreviewSections.tsx +178 -43
  124. package/src/client/RecallPanel.tsx +418 -127
  125. package/src/client/TypeDefinitionDrawer.tsx +154 -54
  126. package/src/client/WikiCardReview.tsx +266 -0
  127. package/src/client/WikiFlowCard.tsx +57 -95
  128. package/src/client/WikiTypeEditor.tsx +197 -95
  129. package/src/client/arch/AgentArchitectureGraph.tsx +30 -14
  130. package/src/client/arch/NodeLogicPanel.tsx +6 -3
  131. package/src/client/arch/model.ts +164 -26
  132. package/src/client/architecture.css +25 -10
  133. package/src/client/architecture.tsx +66 -41
  134. package/src/client/asset-images.ts +125 -0
  135. package/src/client/attribution/AttributionGraph.tsx +481 -34
  136. package/src/client/attribution/AttributionTab.tsx +501 -106
  137. package/src/client/attribution/CaseView.tsx +9 -14
  138. package/src/client/attribution/GraphStage.tsx +25 -18
  139. package/src/client/attribution/RunView.tsx +27 -9
  140. package/src/client/attribution/StageChrome.tsx +280 -140
  141. package/src/client/attribution/graph-options.ts +611 -47
  142. package/src/client/attribution/model.ts +559 -30
  143. package/src/client/convert/ConvertPanel.tsx +36 -4
  144. package/src/client/doc-page.css +134 -0
  145. package/src/client/doc-page.tsx +242 -0
  146. package/src/client/index.ts +18 -0
  147. package/src/client/panel-state.ts +105 -0
  148. package/src/client/problem-domains.ts +1 -1
  149. package/src/client/request.ts +36 -0
  150. package/src/client/styles.css +397 -54
  151. package/src/client/useFitScale.ts +15 -2
  152. package/src/client/wiki-type-groups.ts +86 -0
  153. package/src/convert/host.ts +68 -9
  154. package/src/convert/ingest.ts +61 -18
  155. package/src/convert/types.ts +2 -2
  156. package/src/diagnosis/cards.ts +120 -50
  157. package/src/diagnosis/run.ts +251 -0
  158. package/src/diagnosis/schema.ts +67 -26
  159. package/src/diagnosis/seed-source.ts +29 -4
  160. package/src/diagnosis/seed.ts +187 -85
  161. package/src/diagnosis/store.ts +310 -173
  162. package/src/diagnosis/types.ts +181 -16
  163. package/src/document-page.ts +51 -0
  164. package/src/extract-model.ts +254 -99
  165. package/src/extract.ts +35 -3
  166. package/src/fs-atomic.ts +41 -0
  167. package/src/index.ts +499 -62
  168. package/src/intent/types.ts +1 -1
  169. package/src/knowledge-bases.ts +50 -49
  170. package/src/layout.ts +4 -4
  171. package/src/migrate.ts +2 -2
  172. package/src/model-text.ts +126 -0
  173. package/src/present.ts +367 -0
  174. package/src/recall-rank.ts +57 -2
  175. package/src/runtime.ts +2802 -293
  176. package/src/settings-schema.ts +2 -0
  177. package/src/source-images.ts +326 -0
  178. package/src/types.ts +446 -45
  179. package/src/wiki-finalize.ts +310 -0
  180. package/src/wiki-types.ts +28 -32
@@ -125,11 +125,24 @@ body {
125
125
  box-shadow: 0 1px 2px rgb(15 23 42 / .12), inset 0 0 0 1px var(--tita-primary-shadow);
126
126
  }
127
127
  .tita-tabsList .tita-uiTabsTrigger[data-state='active'] svg { opacity: 1; }
128
- /* 抽屉里那一枚是**行内次级控件**:它站在「内容」这个小节标题旁边,所以比顶栏那枚小
129
- 一号(顶栏是面板级导航,这里是同一个分块里的两种读法)。放在基础规则之后覆盖 ——
130
- 选择器权重相同,靠顺序取胜。 */
128
+ /* 抽屉里那一枚是**行内次级控件**:它站在正文块自己的「读法工具栏」上(0042 之前挤在
129
+ 「内容」小节标题那一行里),所以比顶栏那枚小一号(顶栏是面板级导航,这里是同一个
130
+ 分块里的两种读法)。放在基础规则之后覆盖 —— 选择器权重相同,靠顺序取胜。 */
131
131
  .tita-kbViewTabs { min-height: 24px; }
132
- .tita-kbViewTabs .tita-uiTabsTrigger { min-height: 20px; padding: 0 8px; font-size: 11px; }
132
+ .tita-kbViewTabs .tita-uiTabsTrigger { min-height: 22px; padding: 0 9px; font-size: 11px; }
133
+ /*
134
+ * wiki 栏内部的二级 tab(0042):**要比面板一级 tab 明显低一层**。
135
+ * 两排 tab 同时出现在一屏里时,层级差不是好不好看的问题,是会不会点错的问题 ——
136
+ * 所以这里用更小的字、更矮的按钮、更轻的底色,且左对齐(一级 tab 也是左对齐)。
137
+ */
138
+ .tita-kbSubTabs { display: grid; gap: 10px; }
139
+ .tita-kbSubTabsList { justify-self: start; min-height: 28px; }
140
+ .tita-kbSubTabsList .tita-uiTabsTrigger { min-height: 24px; padding: 0 12px; font-size: 11.5px; }
141
+ /* 「新页面打开」是抽屉头上的**出口**,不是主操作:它和标题同一行、比面板级按钮小一号,
142
+ 不去抢标题。0042 之前它长在「内容」小节里,且**只在正文被截断时**才渲染 ——
143
+ 于是短文档上它根本不存在(用户报告的原话是「那个按钮我也没看到」)。
144
+ 入口的显隐不能依赖内容长度:位置与可见性稳定,人才学得到「全文在那个整页里」。 */
145
+ .tita-drawerHeadAction { height: 26px; min-height: 26px; flex: 0 0 auto; gap: 4px; padding: 0 9px; font-size: 11px; }
133
146
 
134
147
  /* ---------------------------------------------------------------- header */
135
148
  /* 标题行只占一行:图标 + 标题 + 一句说明。状态徽标与 ⋯ 菜单已撤(0022)。 */
@@ -328,17 +341,76 @@ body {
328
341
  */
329
342
  .tita-drawer { width: min(440px, 100vw); }
330
343
  /*
331
- * 正文分块:块与块之间 14px。
344
+ * 抽屉头:左边是「这是什么」(标题 + 路径),右边是**出口**(新页面打开)。
345
+ * `min-width: 0` 是必需的:路径可以很长,没有它这一行会被撑破而不是省略。
346
+ */
347
+ .tita-drawerHead { display: flex; min-width: 0; flex: 1 1 auto; align-items: flex-start; justify-content: space-between; gap: 10px; }
348
+ .tita-drawerHeadCopy { min-width: 0; flex: 1 1 auto; }
349
+ /*
350
+ * 编译那一枚按钮的下拉(0042)。
351
+ *
352
+ * **宽度不能跟触发器走。** 共享组件的 `DropdownMenuContent` 默认是
353
+ * `w-(--radix-dropdown-menu-trigger-width)` —— 触发器是「编译 ⌄」这么宽的一枚按钮,
354
+ * 于是菜单里每一句话都被挤进 ~110px 的竖条里,一句话断成六行(实测截图就是这样)。
355
+ * 这里把宽度还给菜单自己,并把每一项排成「名字 + 一行说明」。
356
+ */
357
+ .tita-kbCompileMenu { width: 268px; max-width: calc(100vw - 24px); }
358
+ .tita-kbCompileMenu [data-slot='dropdown-menu-item'] { display: block; padding: 6px 8px; }
359
+ .tita-kbCompileItem { display: grid; gap: 1px; }
360
+ .tita-kbCompileItem b { color: var(--tita-ink); font-size: 11.5px; font-weight: 650; }
361
+ .tita-kbCompileItem small { color: var(--tita-ink-muted); font-size: 10.5px; line-height: 1.5; }
362
+ /*
363
+ * 正文分块:块与块之间 14px,块与块之间**画一条分隔线**(0042)。
332
364
  *
333
365
  * 它必须挂在抽屉正文上、而**分块容器**挂在每一块上 —— 因为一块(比如「元信息」)是
334
366
  * 「小标题 + 它自己的表」两个同级节点,只在外层给统一 gap 会把标题和它自己的内容也
335
367
  * 拉开 14px,读起来标题就不再管着下面那块了。
368
+ *
369
+ * 0042 补上分隔线:三块的分界原来只靠 14px 的间距,而小节头与正文又是同字号,
370
+ * 于是一条长抽屉读起来是一整段文字。分隔线是「这三块是三件事」最省字的表达。
336
371
  */
337
372
  .tita-drawerStack { display: grid; gap: 14px; align-content: start; }
338
- /* 一块内部:标题与自己的内容贴紧。 */
339
- .tita-drawerSection { display: grid; gap: 6px; align-content: start; }
373
+ /* 一块内部:标题与自己的内容贴紧。上分隔线画在块上,第一块不画(它顶着抽屉头)。 */
374
+ .tita-drawerSection { display: grid; gap: 6px; align-content: start; border-top: 1px solid var(--tita-line); padding-top: 12px; }
375
+ .tita-drawerSection:first-child { border-top: 0; padding-top: 0; }
340
376
  /* 分块之后,标题的上边距交给块间距 —— 否则 14 + 16 会在两处叠出一个空洞。 */
341
377
  .tita-drawerSection > .tita-kbSubHead { margin: 0; }
378
+
379
+ /*
380
+ * 小节头:**三种层级里的中间那层**。标题 15px(UI 包的 `uiDrawerTitle`)/
381
+ * 小节头 13px / 正文 12px。
382
+ *
383
+ * 0042 之前这里是 12px/700 —— 与正文**同字号**,只差一个字重,中间也没有分隔线,
384
+ * 所以「内容」两个字读起来是正文的第 0 行。字号差 1px 是刻意的:再大就和小节里的
385
+ * 正文抢,再小就回到原来那个问题。
386
+ */
387
+ .tita-drawerSubHead { display: flex; min-width: 0; align-items: baseline; justify-content: space-between; gap: 8px; }
388
+ .tita-drawerSubHead h4 { display: inline-flex; min-width: 0; align-items: center; gap: 6px; margin: 0; color: var(--tita-ink); font-size: 13px; font-weight: 700; }
389
+ .tita-drawerSubHead h4 svg { flex: 0 0 auto; color: var(--tita-ink-faint); }
390
+ /* 小节右侧的**一组**东西(事实 + 动作)。成组是为了让 `space-between` 只分一次:
391
+ 三个平级子节点会把中间那个摆到行正中,看起来像另一栏。 */
392
+ .tita-drawerHeadAside { display: inline-flex; flex: 0 0 auto; align-items: center; gap: 8px; }
393
+
394
+ /* 小节右侧的事实(字符数 / 条目数):与标题同一行、比标题轻。 */
395
+ .tita-drawerHeadFact { flex: 0 0 auto; color: var(--tita-ink-faint); font-size: 10.5px; font-variant-numeric: tabular-nums; }
396
+
397
+ /* 读法工具栏:正文块自己的两种读法(Markdown / 文本)+ 本地展开。 */
398
+ .tita-drawerToolbar { display: flex; min-width: 0; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; }
399
+ .tita-drawerMore { height: 22px; min-height: 22px; flex: 0 0 auto; gap: 4px; padding: 0 7px; font-size: 10.5px; }
400
+
401
+ /*
402
+ * 正文块:**锁高内滚 + 阅读栏上限。**
403
+ *
404
+ * 0042 之前正文没有高度上限,整条抽屉体(`.tita-uiDrawerBody`)是一根长滚动条 ——
405
+ * 正文有多长,「版本」就被推到多远,而版本正是要动手的地方(回滚)。
406
+ * 现在正文自己滚,下面的分块位置不再随正文长度漂移。
407
+ * `min(45vh, 460px)`:矮窗口下按视口走,高窗口下不占满整屏。
408
+ *
409
+ * `max-width: 68ch` 是另一半:抽屉壳**可以拖宽**(宽表格需要宽度,那是壳的属性),
410
+ * 但正文的行宽不该跟着长 —— 12px 下一行 68 个字符,是能一眼读完的长度。
411
+ * 这个上限只在把抽屉拖宽之后才咬得住,默认 440px 下正文本来就更窄。
412
+ */
413
+ .tita-drawerBody { max-height: min(45vh, 460px); max-width: 68ch; overflow: auto; overscroll-behavior: contain; }
342
414
  .tita-toneBlue { background: var(--tita-primary-soft); color: var(--tita-primary); }
343
415
  .tita-toneViolet { background: var(--tita-tone-violet-tint); color: var(--tita-tone-violet); }
344
416
  .tita-toneGreen { background: var(--tita-tone-green-tint); color: var(--tita-tone-green); }
@@ -381,15 +453,19 @@ body {
381
453
  * 把 `max-w-xs` 删掉,限宽于是彻底失效(实测宽度从 320px 变成 996px)。双写是这里唯一
382
454
  * 与样式表顺序无关的写法。
383
455
  *
384
- * 限宽**不往窄了压**(这里 320px,和 UI 包默认同值):说明要出现在图标**上方**,而第一
385
- * 小节的标题离视口顶部只有 80 多像素 —— 压到 260px 会多出一行,整块高度超过那段空间,
386
- * 顶部就被视口裁掉(实测裁了 21px)。让它宽一点、少一行,才既在上方又不被裁。
456
+ * 限宽**不往窄了压**(这里 320px,和 UI 包默认同值):说明是整句中文,窄一档就多一行,
457
+ * 而多出来的那行会把它推向视口边缘。位置本身现在由 radix 决定(上方放不下就翻到下方,
458
+ * 见 `InfoHint`),所以这里只需要管「一行多长」这一件事。
387
459
  */
388
460
  .tita-infoHintPanel.tita-infoHintPanel { display: block; max-width: 320px; padding: 8px 12px; line-height: 1.65; text-align: left; white-space: normal; }
389
461
  /* 小节标题旁的说明入口:图标常驻、说明悬停才出现(见 `InfoHint`)。 */
390
462
  .tita-infoHint { display: inline-flex; width: 16px; height: 16px; align-items: center; justify-content: center; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--tita-subtle); cursor: help; }
391
463
  .tita-infoHint:hover { background: var(--tita-surface-muted); color: var(--tita-ink-muted); }
392
464
  .tita-infoHint:focus-visible { outline: 2px solid var(--tita-blue); outline-offset: 1px; }
465
+ /* 标题里的那枚(`<h3>来源类型<InfoHint/></h3>`、设置小节同理):与标题文字留一格气,
466
+ 并对齐文字的光学中线 —— 它与标题同在一行,不是独立一行。 */
467
+ .tita-settingsSectionHeader h3 > .tita-infoHint,
468
+ .tita-settingsSectionHeader h4 > .tita-infoHint { margin-left: 6px; vertical-align: -2px; }
393
469
  .tita-settingsSectionHeader p { margin: 4px 0 0; color: var(--tita-ink-muted); font-size: 11px; }
394
470
  .tita-settingMeta { display: inline-flex; flex: 0 0 auto; align-items: center; gap: 4px; color: var(--tita-ink-faint); font-size: 10px; text-align: right; }
395
471
  /* 写失败时那一行不是「状态」,是**待处理的事**,所以给它警示色而不是灰。 */
@@ -405,17 +481,18 @@ body {
405
481
  * 一列之后每项的宽度、标签基线、控件左边缘都一致,占两列那个变体也就没有意义了。
406
482
  */
407
483
  .tita-settingsGrid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; }
408
- .tita-settingField { display: grid; min-width: 0; gap: 5px; }
409
- .tita-settingField > span { color: var(--tita-ink-muted); font-size: 11px; font-weight: 600; }
410
- .tita-settingField input { width: 100%; min-height: 34px; border: 1px solid var(--tita-line-strong); border-radius: 8px; background: var(--tita-surface); color: var(--tita-ink); padding: 0 9px; font: inherit; font-size: 12px; }
411
- .tita-settingField input:disabled { background: var(--tita-surface-muted); color: var(--tita-ink-faint); cursor: not-allowed; }
412
- /* 只读的路径框:它读起来必须像「值」,不像「待填的空」—— 所以底色是沉的,
413
- 边框是浅的,光标是文本光标(能选中复制)。 */
414
- .tita-settingField input[readonly] { border-color: var(--tita-line); background: var(--tita-surface-muted); color: var(--tita-ink); cursor: text; }
415
- /* 开关不该被网格拉伸成整行宽:它是控件本身,不是「一整行」。 */
416
- .tita-settingField > button[role='switch'] { justify-self: start; }
417
- /* 字段说明:贴在字段下面,一行小字。 */
418
- .tita-settingHint { color: var(--tita-ink-faint); font-size: 10px; line-height: 1.5; }
484
+ /*
485
+ * 字段本身(标题 / 控件 / 说明)撤掉了,改用 `packages/ui` 的 `Field`。
486
+ *
487
+ * 这里曾经有过一整套 `.tita-settingField` + `.tita-settingHint`:标题 11px/600、
488
+ * 说明 10px、输入框圆角 8px / 底色 `--tita-surface`。而弹层里的 `.tita-kbField` 是
489
+ * 12px/400 的标题、11px 的说明,`Input` 自己是 7px 圆角 / 白底 —— **同一个 `Input`
490
+ * 组件,放进设置行和放进弹层就长两个样**,而那个分叉完全由「它套在哪个容器里」决定。
491
+ * `.tita-settingField input` 那条还以 (0,1,1) 压过 `Input` 自己的工具类,所以设置抽屉
492
+ * 里的输入框实际渲染的是 8px 圆角,不是共享控件声明的 7px。
493
+ *
494
+ * 现在只有一处定义(`Field`),边框、圆角、字号、只读态都从共享控件来。
495
+ */
419
496
 
420
497
  /* --------------------------------------------------- 根路径:选择 + 预览 */
421
498
  .tita-pickerRow { display: flex; min-width: 0; align-items: center; gap: 8px; }
@@ -517,7 +594,6 @@ body {
517
594
  .tita-skillGrid { grid-template-columns: minmax(0, 1fr); }
518
595
  .tita-skillHero { align-items: flex-start; flex-wrap: wrap; }
519
596
  .tita-skillHero > .tita-uiButton { width: 100%; }
520
- .tita-settingFieldWide { grid-column: auto; }
521
597
  .tita-inputRow { align-items: stretch; flex-direction: column; }
522
598
  .tita-inputRow .tita-uiButton { width: 100%; }
523
599
  }
@@ -557,6 +633,14 @@ body {
557
633
  .tita-kbEyebrow { display: inline-flex; align-items: center; gap: 5px; color: var(--tita-ink-faint); font-size: 9.5px; font-weight: 700; letter-spacing: .09em; }
558
634
  .tita-kbCardHead h3 { margin: 3px 0 0; color: var(--tita-ink); font-size: 14px; font-weight: 750; letter-spacing: -.01em; }
559
635
  .tita-kbCount { flex: 0 0 auto; color: var(--tita-ink-faint); font-size: 11px; }
636
+ /*
637
+ * 行右侧的次要事实(类型 · 时间 / N 个来源 / N 张)。
638
+ *
639
+ * 0042 补的定义:这个类名被五处用着(卡片行、原文抽屉的编译产物、意图任务、
640
+ * 类型定义、Wiki 类型编辑器)却**一直没有样式** —— 于是它只传递了一个名字,
641
+ * 右对齐与「比主文字轻」全靠父级的 flex 碰运气。一次定义到位。
642
+ */
643
+ .tita-kbTypeCount { flex: 0 0 auto; margin-left: auto; color: var(--tita-ink-faint); font-size: 10.5px; white-space: nowrap; font-variant-numeric: tabular-nums; }
560
644
  .tita-kbHint { margin: 8px 0 0; color: var(--tita-ink-muted); font-size: 11px; line-height: 1.65; }
561
645
  .tita-kbHint b { color: var(--tita-ink-muted); }
562
646
  .tita-kbEmpty { margin: 12px 0 0; border: 1px dashed var(--tita-line); border-radius: 9px; color: var(--tita-ink-faint); padding: 12px; font-size: 11.5px; text-align: center; }
@@ -579,12 +663,11 @@ body {
579
663
  .tita-kbPromptSteps { margin: 8px 0 0; padding-left: 18px; color: var(--tita-ink); font-size: 11px; line-height: 1.85; }
580
664
  .tita-kbPromptSteps code { border-radius: 4px; background: var(--tita-surface-muted); padding: 0 4px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10px; }
581
665
  .tita-kbPromptOff { color: var(--tita-danger); text-decoration: line-through; }
582
- .tita-kbTextarea { width: 100%; min-height: 58px; resize: vertical; border: 1px solid var(--tita-line); border-radius: 8px; background: var(--tita-surface); color: var(--tita-ink); padding: 7px 9px; font-family: inherit; font-size: 11.5px; line-height: 1.6; }
583
- .tita-kbTextarea:focus { border-color: var(--tita-primary-line); outline: 2px solid var(--tita-focus-ring); }
584
666
  .tita-kbSourcePick { display: inline-flex; align-items: center; gap: 6px; color: var(--tita-ink-muted); font-size: 11px; }
585
667
  .tita-kbSourcePick select { min-width: 150px; }
586
- .tita-kbSourceAdd { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; margin-top: 10px; }
587
- .tita-kbSourceAdd .tita-kbHint { margin: 0; }
668
+ /* 「新增类型」现在独占这一行:它旁边那段常驻说明搬进了节标题的说明图标,
669
+ 所以这里只负责把按钮放到右侧(原来是 `space-between` 把说明和按钮分到两头)。 */
670
+ .tita-kbSourceAdd { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 10px; }
588
671
 
589
672
  /* 筛选行:搜索 · 来源分类 · 文件类型 · 刷新 · 添加文件。
590
673
  0027 之前这里是两行(上传行 + 筛选行),而「来源分类」在两个地方各有一个控件;
@@ -606,14 +689,22 @@ body {
606
689
  .tita-kbDocMeta { overflow: hidden; color: var(--tita-ink-faint); font-size: 10.5px; text-overflow: ellipsis; white-space: nowrap; }
607
690
  .tita-kbDocActions { display: flex; flex: 0 0 auto; align-items: center; gap: 2px; }
608
691
 
692
+ /* 弹层里的字段**一格一行**。字段本身(标题 / 控件 / 说明)由 `packages/ui` 的
693
+ `Field` 提供,这里只剩「一组字段之间的间距」—— `.tita-kbField` 撤掉了,
694
+ 它和 `Field` 是同一件事的两份写法。 */
609
695
  .tita-kbForm { display: grid; gap: 10px; }
610
- .tita-kbField { display: grid; gap: 5px; }
696
+ /* 元数据字段声明(0048):一行 = 键 / 显示名 / 必填 / 删除。
697
+ 键那一列给得比显示名宽 —— 它要装 `sources[0].last_modified` 这种路径。 */
698
+ .tita-kbFieldRows { display: grid; gap: 6px; justify-items: start; }
699
+ .tita-kbFieldRow { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr) auto auto; align-items: center; gap: 6px; width: 100%; }
700
+ .tita-kbFieldRequired { display: inline-flex; align-items: center; gap: 4px; color: var(--tita-ink-faint); font-size: 11px; white-space: nowrap; }
611
701
  .tita-kbKv { display: grid; margin: 0; gap: 4px; }
612
702
  .tita-kbKv > div { display: grid; grid-template-columns: 92px minmax(0, 1fr); gap: 8px; }
613
703
  .tita-kbKv dt { color: var(--tita-ink-faint); font-size: 11px; }
614
704
  .tita-kbKv dd { margin: 0; overflow-wrap: anywhere; color: var(--tita-ink); font-size: 11.5px; }
615
705
  .tita-kbCode { border-radius: 4px; background: var(--tita-surface-muted); padding: 1px 4px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10px; overflow-wrap: anywhere; }
616
- .tita-kbAssets { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 8px; margin-top: 12px; }
706
+ .tita-kbAssetsNote { margin: 12px 0 0; color: var(--tita-ink-muted); font-size: 10.5px; }
707
+ .tita-kbAssets { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 8px; margin-top: 12px; max-height: 260px; overflow: auto; }
617
708
  .tita-kbAsset { margin: 0; border: 1px solid var(--tita-line); border-radius: 8px; padding: 4px; }
618
709
  .tita-kbAsset img { display: block; width: 100%; height: 62px; object-fit: contain; }
619
710
  .tita-kbAsset figcaption { margin-top: 3px; color: var(--tita-ink-faint); font-size: 10px; text-align: center; }
@@ -626,6 +717,9 @@ body {
626
717
  .tita-kbMarkdown > :last-child { margin-bottom: 0; }
627
718
  .tita-kbMarkdown img { max-width: 100%; border-radius: 6px; }
628
719
  .tita-kbPre { max-height: 320px; margin: 0; border: 1px solid var(--tita-line); border-radius: 9px; background: var(--tita-surface); color: var(--tita-ink); padding: 10px; overflow: auto; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10.5px; line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere; }
720
+ /* 文本视图的 `<pre>` 自带 max-height(它同时被意图 / 类型定义两处用),进了正文块之后
721
+ 由外层那一层负责滚 —— 同一件事不画两条滚动条。 */
722
+ .tita-drawerBody .tita-kbPre { max-height: none; }
629
723
 
630
724
  /* ------------------------------------------------------ intent (issue 0019) */
631
725
  .tita-intentHead { display: flex; align-items: center; gap: 8px; }
@@ -715,36 +809,107 @@ body {
715
809
 
716
810
  /* Wiki 类型注册表(issue 0024):这张界面就是"SourceRaw → Wiki"的约束入口。 */
717
811
  .tita-wikiTypeBar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 10px; }
718
- .tita-wikiTypeMode { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; }
719
- .tita-wikiTypeMode > span { display: grid; gap: 1px; }
720
- .tita-wikiTypeMode strong { color: var(--tita-ink); font-size: 11.5px; }
721
- .tita-wikiTypeMode small { color: var(--tita-ink-faint); font-size: 10px; }
722
812
  .tita-wikiTypeIssueCount { border: 1px solid var(--tita-tone-amber-line); border-radius: 999px; background: var(--tita-tone-amber-tint); color: var(--tita-tone-amber); padding: 2px 9px; font-size: 10.5px; }
723
813
  .tita-wikiTypeList { display: grid; margin: 8px 0 0; padding: 0; list-style: none; gap: 6px; }
724
814
  /*
725
- * 一行一个类型。**窄栏优先**:这里是 380–420px 的宿主侧边栏,不是宽屏表格。
815
+ * 一行一个类型。**一行就是一行**:名字 + id + 层级 / 自定义标签 + 说明图标,
816
+ * 右边是张数、开关、编辑、删除。
726
817
  *
727
- * 原来这一行是 6 列 grid,首列 `minmax(200px, auto)`、第三列固定 56px、后面还有
728
- * 三个 auto 列(开关 + 两个图标按钮):光最小宽度就是
729
- * 200 + 0 + 56 + 3×28 + 5×8(gap) + 18(padding) ≈ 398px,正好卡满整个面板 ——
730
- * 于是中间那列说明文字被压成 0 宽,描述和「必填小节」直接看不见,整块看起来就是
731
- * 「间距异常」。
818
+ * 这个格子换过两次形,两次都是为了同一件事 —— 窄栏(380–420px)里放不下第四个东西:
819
+ * 1. 最早是 6 列 grid,最小宽度约 398px,中间那列说明被压成 0 宽、整块看起来像「间距异常」。
820
+ * 2. 改成 flex 两行:第一行身份 + 操作,说明用 `order: 1` 换到第二行占满整宽。
821
+ * 3. 现在(2026-09-26)说明也搬进行首那枚图标:十一个类型本来要读三十多行小字,
822
+ * 而这一页问的是「有哪些类型、哪个没启用」。要定义、必填小节与必填字段时,
823
+ * 悬停或聚焦那枚图标就有 —— 内容没删,只是不再常驻。
732
824
  *
733
- * 现在改成 flex:第 1 行是身份 + 操作,说明用 `order: 1` 换到第 2 行占满整宽。
734
- * 视觉顺序变成「名字 → 操作 → 说明」,而 DOM 顺序仍是「名字 → 说明 → 操作」,
735
- * 读屏照旧先听到描述。最长的文本拿到整宽才读得下去,第一行也不再有 200px 硬下限。
825
+ * `align-items: baseline` 保留:名字、id、层级标签三样要坐在同一条基线上才读得成一行。
826
+ * 说明图标自己 `align-self: center`,因为一个只有 svg 的按钮没有可用的文字基线。
736
827
  */
737
828
  .tita-wikiTypeRow { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; border: 1px solid var(--tita-line); border-radius: 9px; background: var(--tita-surface); padding: 8px 10px; }
738
829
  .tita-wikiTypeName { display: flex; flex: 1 1 auto; min-width: 0; flex-wrap: wrap; align-items: baseline; gap: 6px; }
739
830
  .tita-wikiTypeLabel { color: var(--tita-ink); font-size: 12px; font-weight: 620; }
740
831
  .tita-wikiTypeTag { flex: 0 0 auto; border-radius: 4px; background: var(--tita-primary-soft); color: var(--tita-primary); padding: 1px 5px; font-size: 9.5px; }
741
- /* 说明排到最后一行、占满整宽:它是最长的文本,只有整宽才读得下去。 */
742
- .tita-wikiTypeMeta { order: 1; flex: 1 1 100%; min-width: 0; color: var(--tita-ink-faint); font-size: 10.5px; line-height: 1.6; }
832
+ /* 行首那枚说明图标:长名字不把它挤扁(它跟着的是「这一行是什么」),
833
+ 也不与文字比基线 —— 一个只装 svg 的按钮没有可用的基线。 */
834
+ .tita-kbSourceName > .tita-infoHint,
835
+ .tita-wikiTypeName > .tita-infoHint { flex: 0 0 auto; align-self: center; }
743
836
  .tita-wikiTypeIssues { display: grid; margin: 8px 0 0; padding: 0; list-style: none; gap: 5px; }
744
837
  .tita-wikiTypeIssues li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; color: var(--tita-ink-muted); font-size: 11px; line-height: 1.6; }
745
838
  .tita-wikiTypeIssues code { border-radius: 4px; background: var(--tita-surface-muted); padding: 1px 4px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10px; }
746
839
  .tita-wikiTypeIssueLevel { border-radius: 4px; background: var(--tita-surface-muted); color: var(--tita-ink-muted); padding: 0 5px; font-size: 9.5px; }
747
840
 
841
+ /*
842
+ * 按 id 前缀折成的一组(0040 §4.3)。
843
+ *
844
+ * 分组只做一件事:让「这五个是同一套 schema」在列表里看得出来。所以组**不是一个
845
+ * 盒子**:它没有边框、没有底色,只有一条左缩进与一枚可点的组名行。真正承载内容的
846
+ * 还是里面那些 `.tita-wikiTypeRow` —— 给组再加一层描边,一组五个人就会看起来像
847
+ * 卡片套卡片,而这一屏本来就已经是列表套列表。
848
+ *
849
+ * 缩进是唯一的结构信号。组的 `<details>` 默认展开(`open`):折叠是给"太长"用的,
850
+ * 而这里折叠是**默认的阅读顺序**,不是隐藏内容。
851
+ */
852
+ .tita-wikiTypeGroup { margin: 0; }
853
+ .tita-wikiTypeGroupBox { display: grid; gap: 5px; }
854
+ .tita-wikiTypeGroupHead { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; border-radius: 6px; padding: 2px 4px; cursor: pointer; list-style: none; }
855
+ .tita-wikiTypeGroupHead::-webkit-details-marker { display: none; }
856
+ /* 展开态把箭头转 90°(`▸` → `▾`):图形只有一个,方向由状态决定。
857
+ `transition` 与按钮那套时长一致,转起来不抢注意力。 */
858
+ .tita-wikiTypeGroupCaret { flex: 0 0 auto; color: var(--tita-ink-faint); transition: transform 150ms cubic-bezier(0.22, 0.61, 0.36, 1); }
859
+ .tita-wikiTypeGroupBox[open] > .tita-wikiTypeGroupHead .tita-wikiTypeGroupCaret { transform: rotate(90deg); }
860
+ @media (prefers-reduced-motion: reduce) { .tita-wikiTypeGroupCaret { transition: none; } }
861
+ .tita-wikiTypeGroupHead:hover { background: var(--tita-surface-muted); }
862
+ /* 组名比成员更重、比整个抽屉的节标题更轻:它在"有哪些类型"这一层里是一个中间层级。 */
863
+ .tita-wikiTypeGroupLabel { color: var(--tita-ink-muted); font-size: 11px; font-weight: 650; letter-spacing: .01em; }
864
+ .tita-wikiTypeGroupCount { margin-left: auto; color: var(--tita-ink-faint); font-size: 10px; }
865
+ /* 成员缩进 12px:正好是组名那枚 `▾` 加间距的宽度,缩进后成员与组名对齐成一条轴。 */
866
+ .tita-wikiTypeGroupList { display: grid; margin: 0; padding: 0 0 0 12px; list-style: none; gap: 6px; }
867
+
868
+ /*
869
+ * 模板卡预览(0040 §4.4 第 3、4 条)。它是**只读块**,不是控件:
870
+ * 沉底(`--tita-surface-muted`)+ 虚线描边 + 等宽内容,与旁边那几个白底实边框的
871
+ * `Input` / `Textarea` 一眼可分。虚线而不是实线,是因为实线在这个面板里已经
872
+ * 专门表示"可编辑的输入框"。
873
+ *
874
+ * 两块(front matter / 正文)共用同一套排版,只有标题与说明不同 —— 它们的分工
875
+ * 靠**标题**说清,不靠两套长相。
876
+ */
877
+ .tita-wikiPreview { display: grid; gap: 8px; }
878
+ .tita-wikiPreviewHead { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; }
879
+ .tita-wikiPreviewHead h4 { margin: 0; color: var(--tita-ink); font-size: 12px; }
880
+ .tita-wikiPreviewOrigin { color: var(--tita-ink-faint); font-size: 10.5px; }
881
+ .tita-wikiPreviewBlock { display: grid; gap: 5px; border: 1px dashed var(--tita-line-strong); border-radius: 9px; background: var(--tita-surface-muted); padding: 9px 10px; }
882
+ .tita-wikiPreviewBlockTitle { margin: 0; color: var(--tita-ink-muted); font-size: 11px; font-weight: 700; }
883
+ .tita-wikiPreviewBlockNote { margin: 0; color: var(--tita-ink-faint); font-size: 10.5px; line-height: 1.6; }
884
+ .tita-wikiPreviewFields { display: grid; margin: 2px 0 0; gap: 3px; }
885
+ .tita-wikiPreviewField { display: flex; min-width: 0; flex-wrap: wrap; align-items: baseline; gap: 6px; }
886
+ .tita-wikiPreviewField dt { flex: 0 0 auto; margin: 0; color: var(--tita-ink); font-family: var(--font-tita-mono); font-size: 10.5px; }
887
+ .tita-wikiPreviewField dd { margin: 0; color: var(--tita-ink-muted); font-size: 10.5px; line-height: 1.6; overflow-wrap: anywhere; }
888
+ .tita-wikiPreviewSections { display: grid; margin: 2px 0 0; padding: 0; list-style: none; gap: 3px; }
889
+ .tita-wikiPreviewSections li { display: flex; min-width: 0; flex-wrap: wrap; align-items: baseline; gap: 6px; }
890
+ .tita-wikiPreviewSections code { color: var(--tita-ink); font-family: var(--font-tita-mono); font-size: 10.5px; }
891
+ .tita-wikiPreviewPlaceholder { color: var(--tita-ink-faint); font-size: 10.5px; }
892
+ .tita-wikiPreviewEmpty { margin: 2px 0 0; color: var(--tita-ink-faint); font-size: 10.5px; line-height: 1.6; }
893
+
894
+ /*
895
+ * 只读字段的形态(0040 §4.4 第 1 条)。
896
+ *
897
+ * `Input` 的 `readonly` 样式(`packages/ui`)已经给了沉底 + 浅描边 + 文本光标,
898
+ * 这里补的是"这不能改"那一点:文字收到 `--tita-ink-muted`。**不用 opacity** ——
899
+ * 靠透明度变灰会把对比度一起压下去,而 4.5:1 是硬线;`--tita-ink-muted` 在
900
+ * `--tita-surface-muted` 上是 6.98:1(按主题真值实算),灰得明确又读得清。
901
+ *
902
+ * 选择器写 `[data-tita-readonly='true']` 而不是 `input[readonly]`:这条规约只作用于
903
+ * **声明了它**的字段,不能顺手把别处(目录选择器那种"当前值"框)的只读输入框也改掉。
904
+ * 对话框走 Portal,不在 `.tita-panel` 里,所以两处都写上。
905
+ */
906
+ .tita-panel input[data-tita-readonly='true'],
907
+ .tita-uiPortal input[data-tita-readonly='true'] { border-color: var(--tita-line); background: var(--tita-surface-muted); color: var(--tita-ink-muted); }
908
+
909
+ /* 组名行是 `<summary>`,也就是一个可聚焦的按钮:焦点环走全局面板规则,
910
+ 但它的圆角只有 6px,偏移 2px 会顶到相邻行,所以这一处收一点。 */
911
+ .tita-wikiTypeGroupHead:focus-visible { outline-offset: 1px; }
912
+
748
913
  /* 意图分类注册表(issue 0023):预置项是模型的匹配依据,所以定义与"例如"要露在行上。 */
749
914
  .tita-intentCategoryList { display: grid; margin: 10px 0 0; padding: 0; list-style: none; gap: 6px; }
750
915
  .tita-intentCategoryRow { display: grid; grid-template-columns: minmax(210px, auto) minmax(0, 1fr) auto auto auto; align-items: center; gap: 8px; border: 1px solid var(--tita-line); border-radius: 9px; background: var(--tita-surface); padding: 7px 9px; }
@@ -753,12 +918,10 @@ body {
753
918
  .tita-intentCategoryExample { overflow: hidden; min-width: 0; color: var(--tita-ink-faint); font-size: 10.5px; line-height: 1.6; text-overflow: ellipsis; white-space: nowrap; }
754
919
 
755
920
  /* --------------------------------------------------- recall routes (3-way) */
756
- .tita-routeRow { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 11px; }
757
921
  .tita-routeChip { display: inline-flex; align-items: center; gap: 5px; border: 1px dashed var(--tita-line); border-radius: 999px; background: var(--tita-surface); color: var(--tita-ink-faint); padding: 3px 9px; font-size: 10.5px; }
758
922
  .tita-routeChipOn { border-style: solid; border-color: var(--tita-tone-green-line); background: var(--tita-tone-green-tint); color: var(--tita-tone-green); font-weight: 620; }
759
923
  /* 降级不是「没跑」,也不是「跑了」:它要能被看见,并且带上原因(芯片的 title)。 */
760
924
  .tita-routeChipOff { border-style: solid; border-color: var(--tita-tone-amber-line); background: var(--tita-tone-amber-tint); color: var(--tita-tone-amber); font-weight: 620; }
761
- .tita-routeBackend { margin-left: auto; color: var(--tita-ink-faint); font-size: 10px; font-weight: 700; letter-spacing: .08em; }
762
925
 
763
926
 
764
927
  /* 节点指标:顶部链路计数条下沉到节点后的落点(0022)。数字跟着它描述的那个节点走。 */
@@ -788,6 +951,43 @@ body {
788
951
  .tita-hitRouteGraph { background: var(--tita-tone-green-tint); color: var(--tita-tone-green); }
789
952
  .tita-hitRouteHybrid { background: var(--tita-tone-amber-tint); color: var(--tita-warning); }
790
953
  .tita-hitScore { margin-left: auto; color: var(--tita-ink-faint); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10px; }
954
+ /* 账本行(0050):名次、以及**被丢弃**的标记。
955
+ 丢弃不靠降透明度表达 —— 那是「不可用」,而这里要表达的是「算过、没过线」:
956
+ 虚线边框 + 淡色标题 + 一句原因,信息还在,只是退到后面。 */
957
+ .tita-hitRank { flex: 0 0 auto; min-width: 16px; color: var(--tita-ink-faint); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10px; text-align: right; }
958
+ .tita-hitDropped { flex: 0 0 auto; border-radius: 999px; background: var(--tita-surface-muted); color: var(--tita-ink-faint); padding: 1px 7px; font-size: 9.5px; }
959
+ .tita-hitRowDropped { border-style: dashed; background: transparent; }
960
+ .tita-hitRowDropped .tita-hitTitle { color: var(--tita-ink-muted); font-weight: 560; }
961
+
962
+ /* 召回栏的步骤轨比知识库那条更紧凑(0050 第二版):三步平分整行,不需要 132px 的
963
+ 基准宽,也要把纵向高度让给下面的列表。只覆盖布局与尺寸,配色仍来自 `.tita-kbStep`。 */
964
+ .tita-recallSteps .tita-kbStep { flex: 1 1 96px; gap: 1px; padding: 5px 8px; }
965
+ .tita-recallSteps .tita-kbStepTop { gap: 4px; }
966
+ .tita-recallSteps .tita-kbStepTop b { font-size: 11.5px; }
967
+ .tita-recallSteps .tita-kbStepVerb { font-size: 9px; padding: 0 3px; }
968
+ .tita-recallSteps .tita-kbStepFacts { gap: 4px; }
969
+ .tita-recallSteps .tita-kbStepValue { font-size: 9.5px; }
970
+ .tita-recallSteps .tita-kbStepNote { font-size: 9px; }
971
+ /* 每一步的耗时:右对齐、等宽数字。它与计数同色但更轻 —— 它是读数,不是结论。 */
972
+ .tita-kbStepCost { margin-left: auto; color: var(--tita-ink-faint); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 9px; font-style: normal; white-space: nowrap; }
973
+ .tita-recallTiming { margin-left: auto; color: var(--tita-ink-faint); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10px; }
974
+
975
+ /* 命中行的正文默认两行:一条笔记的片段常有几百字,全摊开一屏只能看两条。
976
+ `-webkit-line-clamp` 是截**显示**不是截数据,展开后是同一段文字。 */
977
+ .tita-hitSnippetClamp { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
978
+ .tita-hitToggle { border: 0; background: none; color: var(--tita-primary); padding: 0; font: inherit; font-size: 10px; cursor: pointer; }
979
+ .tita-hitToggle:hover { text-decoration: underline; text-underline-offset: 2px; }
980
+ .tita-hitToggle:focus-visible { outline: 2px solid var(--tita-focus-ring); outline-offset: 1px; border-radius: 3px; }
981
+ /* 卡片标题在「扩展」那一栏是可以点开的:做成链接的样子,但用按钮的语义。 */
982
+ .tita-cardLink { min-width: 0; overflow: hidden; border: 0; background: none; padding: 0; color: var(--tita-primary); font: inherit; font-size: 11.5px; font-weight: 640; text-align: left; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; }
983
+ .tita-cardLink:hover { text-decoration: underline; text-underline-offset: 2px; }
984
+ .tita-cardLink:focus-visible { outline: 2px solid var(--tita-focus-ring); outline-offset: 1px; border-radius: 3px; }
985
+
986
+ /* 召回栏的三个 tab(0050):召回 / 重排 / 扩展。分段控件与工具栏同一套样式。 */
987
+ .tita-recallTabs { margin-top: 12px; }
988
+ .tita-recallRoutes { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 10px; }
989
+ .tita-recallRoutes .tita-routeChip { cursor: pointer; font: inherit; font-size: 10.5px; }
990
+ .tita-recallRoutes .tita-routeChip:hover { border-color: var(--tita-line-strong); color: var(--tita-ink); }
791
991
  .tita-hitSnippet { margin: 0; color: var(--tita-ink-muted); font-size: 11px; line-height: 1.6; overflow-wrap: anywhere; }
792
992
  .tita-hitFoot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; color: var(--tita-ink-faint); font-size: 10px; }
793
993
 
@@ -804,16 +1004,60 @@ body {
804
1004
  /* 记忆:真实落盘计数(04_memory)与「示意」卡片分开摆,避免把占位数字读成真实数据。 */
805
1005
  .tita-memoryReal { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 14px; }
806
1006
 
807
- /* 知识库文档详情:该文档编译出的 wiki 卡片(对应 WeKnora 的「分块」列表)。 */
808
- .tita-cardList { display: grid; margin: 8px 0 0; padding: 0; list-style: none; gap: 6px; }
809
- .tita-cardRow { display: grid; min-width: 0; gap: 4px; border: 1px solid var(--tita-line); border-radius: 9px; background: var(--tita-surface); padding: 8px 9px; }
1007
+ /*
1008
+ * 相关卡片(0049):卡片抽屉里那一份可点的出链 / 入链。
1009
+ *
1010
+ * 一行一个**按钮**,常态没有边框 —— 与 `.tita-cardRow` 同一个理由:一屏里十来个
1011
+ * 描边盒子会变成「框套框」,而这里问的是「它跟谁有关」。hover / 聚焦才显边框与浅底,
1012
+ * 所以它读起来是一份列表,不是一堆卡片。
1013
+ *
1014
+ * 分组标签(「它引用」/「引用它」)比副标题更轻:它们只回答方向,不是内容。
1015
+ */
1016
+ .tita-kbRelated { display: grid; gap: 3px; margin-top: 8px; }
1017
+ .tita-kbRelatedGroup { margin: 4px 0 1px; color: var(--tita-ink-faint); font-size: 10px; font-weight: 700; letter-spacing: .04em; }
1018
+ .tita-kbRelatedList { display: grid; margin: 0; padding: 0; list-style: none; gap: 2px; }
1019
+ .tita-kbRelatedRow {
1020
+ display: flex;
1021
+ width: 100%;
1022
+ min-width: 0;
1023
+ flex-wrap: wrap;
1024
+ align-items: baseline;
1025
+ gap: 6px;
1026
+ border: 1px solid transparent;
1027
+ border-radius: 7px;
1028
+ background: none;
1029
+ padding: 4px 7px;
1030
+ color: inherit;
1031
+ font: inherit;
1032
+ text-align: left;
1033
+ cursor: pointer;
1034
+ }
1035
+ .tita-kbRelatedRow:hover { border-color: var(--tita-line); background: var(--tita-surface-muted); }
1036
+ .tita-kbRelatedRow:focus-within { border-color: var(--tita-primary-line); background: var(--tita-surface-muted); }
1037
+ .tita-kbRelatedTitle { min-width: 0; overflow: hidden; color: var(--tita-ink); font-size: 11.5px; font-weight: 620; text-overflow: ellipsis; white-space: nowrap; }
1038
+ .tita-kbRelatedMeta { margin-left: auto; color: var(--tita-ink-faint); font-size: 10px; white-space: nowrap; }
1039
+
1040
+ /* 知识库文档详情:该文档编译出的 wiki 卡片(对应 WeKnora 的「分块」列表)。
1041
+ 两处用同一套类名:wiki 栏的卡片列表,以及原文抽屉里的「编译产物」列表。 */
1042
+ .tita-cardList { display: grid; margin: 8px 0 0; padding: 0; list-style: none; gap: 4px; }
1043
+ /*
1044
+ * **一条列表,不是一堆卡片**(0042)。
1045
+ *
1046
+ * 0042 之前每一行都有一圈常驻边框 + 圆角,外面还套着一层 `Card`(`.tita-kbCard` 也有
1047
+ * 边框)—— 3 张卡片就是 3 个框、行与行之间又各画一圈,看到的是「框套框」。
1048
+ * 现在照 `.tita-kbDocRow` 的做法:**常态边框透明**,hover 才显边框 + 浅底。
1049
+ * 顺带修掉原来的空转规则:`:hover` 给的取值与常态完全相同,等于没有反馈。
1050
+ */
1051
+ .tita-cardRow { display: grid; min-width: 0; gap: 4px; border: 1px solid transparent; border-radius: 8px; padding: 6px 8px; }
810
1052
  .tita-cardRowHead { display: flex; min-width: 0; flex-wrap: wrap; align-items: center; gap: 6px; }
811
1053
  .tita-cardRowTitle { min-width: 0; overflow: hidden; color: var(--tita-ink); font-size: 11.5px; font-weight: 620; text-overflow: ellipsis; white-space: nowrap; }
1054
+ /* 摘要只出现在**抽屉里**那份列表(那一屏是逐条核对的,不是扫的)。 */
812
1055
  .tita-cardRowText { margin: 0; color: var(--tita-ink-muted); font-size: 10.5px; line-height: 1.6; overflow-wrap: anywhere; }
813
1056
  /* 0025:卡片行变成可点入口(点开看内容与版本)。按钮要抹掉浏览器默认外观,
814
1057
  同时保留 `.tita-cardRow` 的网格排版 —— 否则文字会掉到按钮默认的居中样式里。 */
815
1058
  .tita-cardRowMain { display: grid; width: 100%; gap: 4px; border: 0; background: none; padding: 0; color: inherit; font: inherit; text-align: left; cursor: pointer; }
816
- .tita-cardRow:hover { border-color: var(--tita-line); background: var(--tita-surface); }
1059
+ .tita-cardRow:hover { border-color: var(--tita-line); background: var(--tita-surface-muted); }
1060
+ .tita-cardRow:focus-within { border-color: var(--tita-primary-line); background: var(--tita-surface-muted); }
817
1061
 
818
1062
  /* 知识库的步骤轨:**一条流水线,不是一排并列的 tab**。
819
1063
  收录 → 编译 → 关联 → 检索 → 精排,步与步之间用箭头连起来 —— 方向本身就是信息
@@ -871,11 +1115,6 @@ body {
871
1115
  .tita-graphArrow { color: var(--tita-ink-faint); font-size: 11px; }
872
1116
 
873
1117
  /* 重排分栏:进入上下文包的 topN。序号是它的阅读顺序,所以用等宽数字列。 */
874
- .tita-contextList { display: grid; gap: 8px; margin: 12px 0 0; padding: 0; list-style: none; }
875
- .tita-contextRow { display: grid; grid-template-columns: 22px minmax(0, 1fr); gap: 9px; border: 1px solid var(--tita-line); border-radius: 10px; background: var(--tita-surface); padding: 9px 10px; }
876
- .tita-contextRank { display: inline-flex; height: 20px; align-items: center; justify-content: center; border-radius: 6px; background: var(--tita-primary-soft); color: var(--tita-primary); font-size: 10.5px; font-weight: 700; }
877
- .tita-contextCopy { display: grid; min-width: 0; gap: 4px; }
878
- .tita-contextHead { display: flex; min-width: 0; align-items: center; gap: 6px; }
879
1118
 
880
1119
  /* ======================================================================
881
1120
  台账世界的基线(issues/0026)
@@ -938,3 +1177,107 @@ body {
938
1177
  .tita-panel ::-webkit-scrollbar-thumb { border: 3px solid transparent; border-radius: 999px; background-clip: content-box; background-color: var(--tita-line-strong); }
939
1178
  .tita-panel ::-webkit-scrollbar-thumb:hover { background-color: var(--tita-ink-faint); }
940
1179
  .tita-panel ::-webkit-scrollbar-corner { background: transparent; }
1180
+
1181
+ /* ════════════════════════════════════════════════════════════════════════════
1182
+ 智能归因图谱 · 动线视角的泳道带(0021 §7.7)
1183
+
1184
+ 它叠在 G6 画布上,只负责「这一段是什么、这一段有几个、哪一段断在这里」。
1185
+ 两条约束决定它必须这么写:
1186
+
1187
+ 1. **整层不吃事件**(`pointer-events: none`,§7.4 第 10 条):带子只负责读,
1188
+ 命中一律落到画布上,否则点节点、拖画布都会被这层吞掉。
1189
+ 2. **坐标由 TS 算**(`laneLayoutOf` 的 `top/left/width/height` 走行内 style):
1190
+ 这里只描述「长什么样」,不描述「摆在哪」。同一份几何同时喂画布与这一层,
1191
+ 两处不可能对不上。
1192
+
1193
+ 颜色全部是 `color-mix()` 混出来的,基底取共享令牌 —— 主题一换,带子跟着走,
1194
+ 不需要第二份颜色。`--tita-band-tint` 由 React 按段给(实例层偏琥珀,其余偏蓝)。
1195
+ ════════════════════════════════════════════════════════════════════════════ */
1196
+ .tita-attrBand {
1197
+ position: absolute;
1198
+ /* 5% 是「看得出有分区、但绝不和节点抢注意力」的那一档;段头带稍重一点(9%),
1199
+ 因为它是这一段唯一的文字锚点。 */
1200
+ background: color-mix(in srgb, var(--tita-band-tint, var(--tita-tone-blue)) 5%, var(--tita-surface));
1201
+ border-radius: 8px;
1202
+ }
1203
+ .tita-attrBandHeader {
1204
+ display: flex;
1205
+ width: 100%;
1206
+ min-width: 0;
1207
+ align-items: center;
1208
+ /* 不换行:段头高 20px,一换行就把段名顶出带子。放不下的部分由各段自己省略。 */
1209
+ flex-wrap: nowrap;
1210
+ gap: 6px;
1211
+ padding: 0 8px;
1212
+ background: color-mix(in srgb, var(--tita-band-tint, var(--tita-tone-blue)) 9%, var(--tita-surface));
1213
+ border-radius: 8px 8px 0 0;
1214
+ }
1215
+ /* 段头左侧那一块(段名 + 计数 + 读法)是一个按钮:点它收起 / 展开这一段。
1216
+ 按钮的默认外观必须显式抹掉 —— 没有 preflight 时它的字体、字号、对齐、边框、
1217
+ 背景都会退回浏览器默认值,表现出来就是「段头的字比别处大一号、还带一圈近黑描边」。 */
1218
+ .tita-attrBandHeadMain {
1219
+ display: flex;
1220
+ min-width: 0;
1221
+ flex: 1 1 auto;
1222
+ align-items: center;
1223
+ gap: 6px;
1224
+ padding: 0;
1225
+ border: 0;
1226
+ background: transparent;
1227
+ font: inherit;
1228
+ color: inherit;
1229
+ text-align: left;
1230
+ cursor: pointer;
1231
+ }
1232
+ /* 不能收起 / 展开的段头(空段、被切面关掉的段、只剩一个节点的段):纯读数,不该看起来能点。 */
1233
+ .tita-attrBandHeadMain:disabled { cursor: default; }
1234
+ .tita-attrBandHeadMain:not(:disabled):hover .tita-attrBandName { color: var(--tita-primary); }
1235
+ .tita-attrBandHeadMain:focus-visible { outline: 2px solid var(--tita-primary); outline-offset: 1px; border-radius: 4px; }
1236
+ /* ① 段的「换一个现象」入口。它是**换一条动线**,不是筛选 —— 所以做成段头上的小按钮,
1237
+ 不进左栏的筛选组(那会让人以为图被裁掉了)。 */
1238
+ .tita-attrBandPicks { display: flex; flex: 0 0 auto; align-items: center; gap: 4px; white-space: nowrap; }
1239
+ .tita-attrBandPicksLabel { color: var(--tita-ink-faint); font-size: 10px; }
1240
+ .tita-attrBandPick {
1241
+ padding: 1px 5px;
1242
+ border: 1px solid var(--tita-line-strong);
1243
+ border-radius: 5px;
1244
+ background: var(--tita-surface);
1245
+ color: var(--tita-ink-muted);
1246
+ font-family: var(--tita-mono);
1247
+ font-size: 10px;
1248
+ line-height: 14px;
1249
+ cursor: pointer;
1250
+ }
1251
+ .tita-attrBandPick:hover { border-color: var(--tita-primary); color: var(--tita-primary); }
1252
+ .tita-attrBandPick:focus-visible { outline: 2px solid var(--tita-primary); outline-offset: 1px; }
1253
+ /* 被收起 / 被关掉的段:带子沉下去,与「这一段是空的」区分开 ——
1254
+ 空段仍然是正常底色(它是一条待办),而收起是用户的选择,视觉上要退到后面。 */
1255
+ .tita-attrBand[data-tita-lane-state='collapsed'],
1256
+ .tita-attrBand[data-tita-lane-state='off'] { opacity: .72; }
1257
+ /* 段名:这一段唯一必须读到的字。11px / 650 让它压得住节点标题(10.5px / 600)。 */
1258
+ .tita-attrBandName { flex: 0 0 auto; white-space: nowrap; color: var(--tita-ink); font-size: 11px; font-weight: 650; letter-spacing: -.01em; }
1259
+ /* 计数:等宽 + 表格数字,段与段之间扫一眼就能比大小。 */
1260
+ .tita-attrBandCount { flex: 0 0 auto; color: var(--tita-ink); font-family: var(--tita-mono); font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums; }
1261
+ .tita-attrBandCountDim { color: var(--tita-ink-faint); font-weight: 500; }
1262
+ /* 排序依据(或空段说明):段头右侧的弱化文字。它是「这一段怎么读」的读法,
1263
+ 不是标题,所以不抢墨色;但仍然是 10px 的正文级对比度,不用 ad-hoc 灰。 */
1264
+ .tita-attrBandHint {
1265
+ min-width: 0;
1266
+ flex: 1 1 auto;
1267
+ overflow: hidden;
1268
+ color: var(--tita-ink-faint);
1269
+ font-size: 10px;
1270
+ line-height: 1.3;
1271
+ text-align: right;
1272
+ white-space: nowrap;
1273
+ text-overflow: ellipsis;
1274
+ }
1275
+ /* 内容左沿的一条竖线:段头文字与节点左对齐到同一列,这一段才读起来像一段。
1276
+ 1px 且只有段高的一半 —— 它是对齐提示,不是分隔线。 */
1277
+ .tita-attrBandRule {
1278
+ position: absolute;
1279
+ top: 100%;
1280
+ bottom: 0;
1281
+ width: 1px;
1282
+ background: color-mix(in srgb, var(--tita-band-tint, var(--tita-tone-blue)) 26%, transparent);
1283
+ }