@mastra/playground-ui 68.0.0-alpha.2 → 68.0.0

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 (479) hide show
  1. package/dist/{FilterBar-CQl04O6t.cjs → FilterBar-BSP2UTjc.cjs} +2 -2
  2. package/dist/{FilterBar-CQl04O6t.cjs.map → FilterBar-BSP2UTjc.cjs.map} +1 -1
  3. package/dist/{FilterBar-BgW2dqRQ.js → FilterBar-PSQ1OwqH.js} +2 -2
  4. package/dist/{FilterBar-BgW2dqRQ.js.map → FilterBar-PSQ1OwqH.js.map} +1 -1
  5. package/dist/KnowledgePageGraph.css +55 -0
  6. package/dist/{MarkdownRenderer-2GOXQJEv.js → MarkdownRenderer-CI30RqdB.js} +19 -6
  7. package/dist/MarkdownRenderer-CI30RqdB.js.map +1 -0
  8. package/dist/{MarkdownRenderer-CB8Z_CHc.cjs → MarkdownRenderer-y65WGBhC.cjs} +19 -6
  9. package/dist/MarkdownRenderer-y65WGBhC.cjs.map +1 -0
  10. package/dist/{MetricsCard-CEB-f2sv.cjs → MetricsCard-BEwiS7Pe.cjs} +8 -8
  11. package/dist/MetricsCard-BEwiS7Pe.cjs.map +1 -0
  12. package/dist/{MetricsCard-M8awJ2B_.js → MetricsCard-ua6C6b8a.js} +10 -10
  13. package/dist/MetricsCard-ua6C6b8a.js.map +1 -0
  14. package/dist/{MetricsLineChart-BhHoduO0.js → MetricsLineChart-4SwxBbQx.js} +14 -3
  15. package/dist/MetricsLineChart-4SwxBbQx.js.map +1 -0
  16. package/dist/{MetricsLineChart-CjBm4IXu.cjs → MetricsLineChart-tZxsauau.cjs} +13 -2
  17. package/dist/MetricsLineChart-tZxsauau.cjs.map +1 -0
  18. package/dist/{MetricsStackedBarChart-BdMlKv6G.cjs → MetricsStackedBarChart-CDLE76IB.cjs} +2 -2
  19. package/dist/{MetricsStackedBarChart-BdMlKv6G.cjs.map → MetricsStackedBarChart-CDLE76IB.cjs.map} +1 -1
  20. package/dist/{MetricsStackedBarChart-BRuA6MTE.js → MetricsStackedBarChart-D2QyS4-z.js} +2 -2
  21. package/dist/{MetricsStackedBarChart-BRuA6MTE.js.map → MetricsStackedBarChart-D2QyS4-z.js.map} +1 -1
  22. package/dist/{activity-rJ1NQl_2.js → activity-C8Ql81WM.js} +2 -2
  23. package/dist/{activity-rJ1NQl_2.js.map → activity-C8Ql81WM.js.map} +1 -1
  24. package/dist/{activity-DyB5H3gF.cjs → activity-CcQZeDYi.cjs} +2 -2
  25. package/dist/{activity-DyB5H3gF.cjs.map → activity-CcQZeDYi.cjs.map} +1 -1
  26. package/dist/components/DateTimeRangePicker.cjs.js +1 -1
  27. package/dist/components/DateTimeRangePicker.es.js +1 -1
  28. package/dist/components/FilterBar.cjs.js +2 -2
  29. package/dist/components/FilterBar.es.js +2 -2
  30. package/dist/components/MarkdownRenderer.cjs.js +1 -1
  31. package/dist/components/MarkdownRenderer.es.js +1 -1
  32. package/dist/components/MetricsCard.cjs.js +1 -1
  33. package/dist/components/MetricsCard.es.js +1 -1
  34. package/dist/components/MetricsLineChart.cjs.js +1 -1
  35. package/dist/components/MetricsLineChart.es.js +1 -1
  36. package/dist/components/MetricsStackedBarChart.cjs.js +1 -1
  37. package/dist/components/MetricsStackedBarChart.es.js +1 -1
  38. package/dist/components/SavedViews.cjs.js +1 -1
  39. package/dist/components/SavedViews.es.js +1 -1
  40. package/dist/components/ai/activity.cjs.js +1 -1
  41. package/dist/components/ai/activity.es.js +1 -1
  42. package/dist/components/ai/plan.cjs.js +1 -1
  43. package/dist/components/ai/plan.es.js +1 -1
  44. package/dist/components/ai/tool-approval.cjs.js +1 -1
  45. package/dist/components/ai/tool-approval.es.js +1 -1
  46. package/dist/components/ai/tool-call.cjs.js +2 -2
  47. package/dist/components/ai/tool-call.es.js +2 -2
  48. package/dist/{date-time-range-picker-CTt7Od0P.cjs → date-time-range-picker-CWGjoR3j.cjs} +40 -13
  49. package/dist/date-time-range-picker-CWGjoR3j.cjs.map +1 -0
  50. package/dist/{date-time-range-picker-DKh1K9Iy.js → date-time-range-picker-CdSiXwB4.js} +40 -13
  51. package/dist/date-time-range-picker-CdSiXwB4.js.map +1 -0
  52. package/dist/domains/chat/activity/activity-stretch-line.cjs.js +1 -1
  53. package/dist/domains/chat/activity/activity-stretch-line.es.js +1 -1
  54. package/dist/domains/chat/activity/components/activity-fold.cjs.js +1 -1
  55. package/dist/domains/chat/activity/components/activity-fold.es.js +1 -1
  56. package/dist/domains/chat/activity/components/activity-timed-trigger.cjs.js +1 -1
  57. package/dist/domains/chat/activity/components/activity-timed-trigger.es.js +1 -1
  58. package/dist/domains/chat/activity/components/calls-panel.cjs.js +1 -1
  59. package/dist/domains/chat/activity/components/calls-panel.es.js +1 -1
  60. package/dist/domains/chat/activity/components/stretch-content.cjs.js +1 -1
  61. package/dist/domains/chat/activity/components/stretch-content.es.js +1 -1
  62. package/dist/domains/chat/activity/components/stretch-thought.cjs.js +1 -1
  63. package/dist/domains/chat/activity/components/stretch-thought.es.js +1 -1
  64. package/dist/domains/chat/activity/model/activity-segments.cjs.js +1 -1
  65. package/dist/domains/chat/activity/model/activity-segments.es.js +1 -1
  66. package/dist/domains/chat/activity/model/activity-words.cjs.js +2 -2
  67. package/dist/domains/chat/activity/model/activity-words.es.js +2 -2
  68. package/dist/domains/chat/components/badge-wrapper.cjs.js +1 -1
  69. package/dist/domains/chat/components/badge-wrapper.es.js +1 -1
  70. package/dist/domains/chat/knowledge/knowledge-links.cjs.js +52 -6
  71. package/dist/domains/chat/knowledge/knowledge-links.cjs.js.map +1 -1
  72. package/dist/domains/chat/knowledge/knowledge-links.es.js +53 -9
  73. package/dist/domains/chat/knowledge/knowledge-links.es.js.map +1 -1
  74. package/dist/domains/chat/knowledge/pinned-knowledge-data.cjs.js +10 -3
  75. package/dist/domains/chat/knowledge/pinned-knowledge-data.cjs.js.map +1 -1
  76. package/dist/domains/chat/knowledge/pinned-knowledge-data.es.js +10 -3
  77. package/dist/domains/chat/knowledge/pinned-knowledge-data.es.js.map +1 -1
  78. package/dist/domains/chat/knowledge/pinned-knowledge.cjs.js +8 -3
  79. package/dist/domains/chat/knowledge/pinned-knowledge.cjs.js.map +1 -1
  80. package/dist/domains/chat/knowledge/pinned-knowledge.es.js +8 -3
  81. package/dist/domains/chat/knowledge/pinned-knowledge.es.js.map +1 -1
  82. package/dist/domains/chat/knowledge.cjs.js +2 -0
  83. package/dist/domains/chat/knowledge.es.js +2 -2
  84. package/dist/domains/chat/messages/message-row.cjs.js +3 -3
  85. package/dist/domains/chat/messages/message-row.es.js +3 -3
  86. package/dist/domains/chat/messages/notification-signal-notice.cjs.js +1 -1
  87. package/dist/domains/chat/messages/notification-signal-notice.es.js +1 -1
  88. package/dist/domains/chat/messages/reasoning-content.cjs.js +1 -1
  89. package/dist/domains/chat/messages/reasoning-content.es.js +1 -1
  90. package/dist/domains/chat/messages/renderers/message-text.cjs.js +35 -1
  91. package/dist/domains/chat/messages/renderers/message-text.cjs.js.map +1 -1
  92. package/dist/domains/chat/messages/renderers/message-text.es.js +36 -2
  93. package/dist/domains/chat/messages/renderers/message-text.es.js.map +1 -1
  94. package/dist/domains/chat/messages/renderers/reasoning-part-renderer.cjs.js +1 -1
  95. package/dist/domains/chat/messages/renderers/reasoning-part-renderer.es.js +1 -1
  96. package/dist/domains/chat/messages/signal-badge.cjs.js +1 -1
  97. package/dist/domains/chat/messages/signal-badge.es.js +1 -1
  98. package/dist/domains/chat/messages/skill-message.cjs.js +2 -2
  99. package/dist/domains/chat/messages/skill-message.es.js +2 -2
  100. package/dist/domains/chat/messages/system-reminder-badge.cjs.js +1 -1
  101. package/dist/domains/chat/messages/system-reminder-badge.es.js +1 -1
  102. package/dist/domains/chat/tools/badges/agent-badge-wrapper.cjs.js +1 -1
  103. package/dist/domains/chat/tools/badges/agent-badge-wrapper.es.js +1 -1
  104. package/dist/domains/chat/tools/badges/agent-badge.cjs.js +1 -1
  105. package/dist/domains/chat/tools/badges/agent-badge.es.js +1 -1
  106. package/dist/domains/chat/tools/badges/file-tree-badge.cjs.js +2 -2
  107. package/dist/domains/chat/tools/badges/file-tree-badge.es.js +2 -2
  108. package/dist/domains/chat/tools/badges/knowledge-body-range.cjs.js +80 -0
  109. package/dist/domains/chat/tools/badges/knowledge-body-range.cjs.js.map +1 -0
  110. package/dist/domains/chat/tools/badges/knowledge-body-range.d.ts +1 -0
  111. package/dist/domains/chat/tools/badges/knowledge-body-range.es.js +78 -0
  112. package/dist/domains/chat/tools/badges/knowledge-body-range.es.js.map +1 -0
  113. package/dist/domains/chat/tools/badges/knowledge-tool-badge.cjs.js +163 -13
  114. package/dist/domains/chat/tools/badges/knowledge-tool-badge.cjs.js.map +1 -1
  115. package/dist/domains/chat/tools/badges/knowledge-tool-badge.es.js +165 -15
  116. package/dist/domains/chat/tools/badges/knowledge-tool-badge.es.js.map +1 -1
  117. package/dist/domains/chat/tools/badges/mcp-input-request-badge.cjs.js +51 -0
  118. package/dist/domains/chat/tools/badges/mcp-input-request-badge.cjs.js.map +1 -0
  119. package/dist/domains/chat/tools/badges/mcp-input-request-badge.d.ts +1 -0
  120. package/dist/domains/chat/tools/badges/mcp-input-request-badge.es.js +49 -0
  121. package/dist/domains/chat/tools/badges/mcp-input-request-badge.es.js.map +1 -0
  122. package/dist/domains/chat/tools/badges/mcp-task-chip.cjs.js +7 -2
  123. package/dist/domains/chat/tools/badges/mcp-task-chip.cjs.js.map +1 -1
  124. package/dist/domains/chat/tools/badges/mcp-task-chip.es.js +7 -2
  125. package/dist/domains/chat/tools/badges/mcp-task-chip.es.js.map +1 -1
  126. package/dist/domains/chat/tools/badges/observation-marker-badge.cjs.js +2 -2
  127. package/dist/domains/chat/tools/badges/observation-marker-badge.es.js +2 -2
  128. package/dist/domains/chat/tools/badges/observation-marker-details.cjs.js +2 -2
  129. package/dist/domains/chat/tools/badges/observation-marker-details.es.js +2 -2
  130. package/dist/domains/chat/tools/badges/observation-renderer.cjs.js +1 -1
  131. package/dist/domains/chat/tools/badges/observation-renderer.es.js +1 -1
  132. package/dist/domains/chat/tools/badges/sandbox-execution-badge.cjs.js +2 -2
  133. package/dist/domains/chat/tools/badges/sandbox-execution-badge.es.js +2 -2
  134. package/dist/domains/chat/tools/badges/tool-approval-badge.cjs.js +1 -1
  135. package/dist/domains/chat/tools/badges/tool-approval-badge.es.js +1 -1
  136. package/dist/domains/chat/tools/badges/tool-badge.cjs.js +9 -4
  137. package/dist/domains/chat/tools/badges/tool-badge.cjs.js.map +1 -1
  138. package/dist/domains/chat/tools/badges/tool-badge.es.js +9 -4
  139. package/dist/domains/chat/tools/badges/tool-badge.es.js.map +1 -1
  140. package/dist/domains/chat/tools/badges/use-deferred-input-request.cjs.js +81 -0
  141. package/dist/domains/chat/tools/badges/use-deferred-input-request.cjs.js.map +1 -0
  142. package/dist/domains/chat/tools/badges/use-deferred-input-request.d.ts +1 -0
  143. package/dist/domains/chat/tools/badges/use-deferred-input-request.es.js +77 -0
  144. package/dist/domains/chat/tools/badges/use-deferred-input-request.es.js.map +1 -0
  145. package/dist/domains/chat/tools/badges/use-mcp-task-progress.cjs.js +11 -2
  146. package/dist/domains/chat/tools/badges/use-mcp-task-progress.cjs.js.map +1 -1
  147. package/dist/domains/chat/tools/badges/use-mcp-task-progress.es.js +11 -2
  148. package/dist/domains/chat/tools/badges/use-mcp-task-progress.es.js.map +1 -1
  149. package/dist/domains/chat/tools/badges/workflow-badge.cjs.js +1 -1
  150. package/dist/domains/chat/tools/badges/workflow-badge.es.js +1 -1
  151. package/dist/domains/chat/tools/connect-prompt-tool.cjs.js +79 -4
  152. package/dist/domains/chat/tools/connect-prompt-tool.cjs.js.map +1 -1
  153. package/dist/domains/chat/tools/connect-prompt-tool.es.js +79 -4
  154. package/dist/domains/chat/tools/connect-prompt-tool.es.js.map +1 -1
  155. package/dist/domains/chat/tools/mcp-input-request-tool.cjs.js +238 -0
  156. package/dist/domains/chat/tools/mcp-input-request-tool.cjs.js.map +1 -0
  157. package/dist/domains/chat/tools/mcp-input-request-tool.d.ts +1 -0
  158. package/dist/domains/chat/tools/mcp-input-request-tool.es.js +237 -0
  159. package/dist/domains/chat/tools/mcp-input-request-tool.es.js.map +1 -0
  160. package/dist/domains/chat/tools/mcp-input-request.cjs.js +40 -0
  161. package/dist/domains/chat/tools/mcp-input-request.cjs.js.map +1 -0
  162. package/dist/domains/chat/tools/mcp-input-request.d.ts +1 -0
  163. package/dist/domains/chat/tools/mcp-input-request.es.js +33 -0
  164. package/dist/domains/chat/tools/mcp-input-request.es.js.map +1 -0
  165. package/dist/domains/chat/tools/tool-card-kind.cjs.js +9 -4
  166. package/dist/domains/chat/tools/tool-card-kind.cjs.js.map +1 -1
  167. package/dist/domains/chat/tools/tool-card-kind.es.js +9 -5
  168. package/dist/domains/chat/tools/tool-card-kind.es.js.map +1 -1
  169. package/dist/domains/chat/tools/tool-card.cjs.js +1 -1
  170. package/dist/domains/chat/tools/tool-card.es.js +1 -1
  171. package/dist/domains/chat/tools/tool-groups.cjs.js +1 -1
  172. package/dist/domains/chat/tools/tool-groups.es.js +1 -1
  173. package/dist/domains/chat.cjs.js +5 -3
  174. package/dist/domains/chat.es.js +4 -4
  175. package/dist/domains/connect/types.cjs.js.map +1 -1
  176. package/dist/domains/connect/types.es.js.map +1 -1
  177. package/dist/domains/connect/use-user-connect.cjs.js +19 -2
  178. package/dist/domains/connect/use-user-connect.cjs.js.map +1 -1
  179. package/dist/domains/connect/use-user-connect.es.js +19 -2
  180. package/dist/domains/connect/use-user-connect.es.js.map +1 -1
  181. package/dist/domains/dashboards/components/dashboard-renderer.cjs.js +4 -3
  182. package/dist/domains/dashboards/components/dashboard-renderer.cjs.js.map +1 -1
  183. package/dist/domains/dashboards/components/dashboard-renderer.es.js +4 -3
  184. package/dist/domains/dashboards/components/dashboard-renderer.es.js.map +1 -1
  185. package/dist/domains/dashboards/components/widget-chrome.cjs.js +1 -1
  186. package/dist/domains/dashboards/components/widget-chrome.es.js +1 -1
  187. package/dist/domains/dashboards/components/widget-error.cjs.js +1 -1
  188. package/dist/domains/dashboards/components/widget-error.es.js +1 -1
  189. package/dist/domains/dashboards/hooks/use-widget-views.cjs.js +4 -3
  190. package/dist/domains/dashboards/hooks/use-widget-views.cjs.js.map +1 -1
  191. package/dist/domains/dashboards/hooks/use-widget-views.es.js +4 -3
  192. package/dist/domains/dashboards/hooks/use-widget-views.es.js.map +1 -1
  193. package/dist/domains/dashboards/kinds/scores/scores-widget.cjs.js +9 -6
  194. package/dist/domains/dashboards/kinds/scores/scores-widget.cjs.js.map +1 -1
  195. package/dist/domains/dashboards/kinds/scores/scores-widget.es.js +9 -6
  196. package/dist/domains/dashboards/kinds/scores/scores-widget.es.js.map +1 -1
  197. package/dist/domains/dashboards/kinds/table/table-tabs.cjs.js +2 -2
  198. package/dist/domains/dashboards/kinds/table/table-tabs.es.js +2 -2
  199. package/dist/domains/dashboards/kinds/table/table-widget.cjs.js +1 -1
  200. package/dist/domains/dashboards/kinds/table/table-widget.es.js +1 -1
  201. package/dist/domains/dashboards/kinds/timeseries/timeseries-widget.cjs.js +6 -5
  202. package/dist/domains/dashboards/kinds/timeseries/timeseries-widget.cjs.js.map +1 -1
  203. package/dist/domains/dashboards/kinds/timeseries/timeseries-widget.es.js +6 -5
  204. package/dist/domains/dashboards/kinds/timeseries/timeseries-widget.es.js.map +1 -1
  205. package/dist/domains/dashboards/lib/chrome.cjs.js +9 -2
  206. package/dist/domains/dashboards/lib/chrome.cjs.js.map +1 -1
  207. package/dist/domains/dashboards/lib/chrome.es.js +9 -2
  208. package/dist/domains/dashboards/lib/chrome.es.js.map +1 -1
  209. package/dist/domains/dashboards/widget-kind.cjs.js.map +1 -1
  210. package/dist/domains/dashboards/widget-kind.es.js.map +1 -1
  211. package/dist/domains/knowledge/components/KnowledgePageCard.cjs.js +48 -0
  212. package/dist/domains/knowledge/components/KnowledgePageCard.cjs.js.map +1 -0
  213. package/dist/domains/knowledge/components/KnowledgePageCard.d.ts +1 -0
  214. package/dist/domains/knowledge/components/KnowledgePageCard.es.js +47 -0
  215. package/dist/domains/knowledge/components/KnowledgePageCard.es.js.map +1 -0
  216. package/dist/domains/knowledge/components/KnowledgePageDetail.cjs.js +63 -5
  217. package/dist/domains/knowledge/components/KnowledgePageDetail.cjs.js.map +1 -1
  218. package/dist/domains/knowledge/components/KnowledgePageDetail.es.js +64 -6
  219. package/dist/domains/knowledge/components/KnowledgePageDetail.es.js.map +1 -1
  220. package/dist/domains/knowledge/components/KnowledgePageExplorer.cjs.js +485 -47
  221. package/dist/domains/knowledge/components/KnowledgePageExplorer.cjs.js.map +1 -1
  222. package/dist/domains/knowledge/components/KnowledgePageExplorer.es.js +487 -50
  223. package/dist/domains/knowledge/components/KnowledgePageExplorer.es.js.map +1 -1
  224. package/dist/domains/knowledge/components/KnowledgePageGraph.cjs.js +1051 -123
  225. package/dist/domains/knowledge/components/KnowledgePageGraph.cjs.js.map +1 -1
  226. package/dist/domains/knowledge/components/KnowledgePageGraph.es.js +1054 -126
  227. package/dist/domains/knowledge/components/KnowledgePageGraph.es.js.map +1 -1
  228. package/dist/domains/knowledge/components/KnowledgePageTree.cjs.js +75 -3
  229. package/dist/domains/knowledge/components/KnowledgePageTree.cjs.js.map +1 -1
  230. package/dist/domains/knowledge/components/KnowledgePageTree.es.js +76 -4
  231. package/dist/domains/knowledge/components/KnowledgePageTree.es.js.map +1 -1
  232. package/dist/domains/knowledge/lib/circleText.cjs.js +9 -0
  233. package/dist/domains/knowledge/lib/circleText.cjs.js.map +1 -0
  234. package/dist/domains/knowledge/lib/circleText.d.ts +1 -0
  235. package/dist/domains/knowledge/lib/circleText.es.js +8 -0
  236. package/dist/domains/knowledge/lib/circleText.es.js.map +1 -0
  237. package/dist/domains/knowledge/lib/fitViewport.cjs.js +82 -0
  238. package/dist/domains/knowledge/lib/fitViewport.cjs.js.map +1 -0
  239. package/dist/domains/knowledge/lib/fitViewport.d.ts +1 -0
  240. package/dist/domains/knowledge/lib/fitViewport.es.js +73 -0
  241. package/dist/domains/knowledge/lib/fitViewport.es.js.map +1 -0
  242. package/dist/domains/knowledge/lib/graphShape.cjs.js +524 -0
  243. package/dist/domains/knowledge/lib/graphShape.cjs.js.map +1 -0
  244. package/dist/domains/knowledge/lib/graphShape.d.ts +1 -0
  245. package/dist/domains/knowledge/lib/graphShape.es.js +499 -0
  246. package/dist/domains/knowledge/lib/graphShape.es.js.map +1 -0
  247. package/dist/domains/knowledge/lib/linkCountsText.cjs.js +11 -0
  248. package/dist/domains/knowledge/lib/linkCountsText.cjs.js.map +1 -0
  249. package/dist/domains/knowledge/lib/linkCountsText.d.ts +1 -0
  250. package/dist/domains/knowledge/lib/linkCountsText.es.js +10 -0
  251. package/dist/domains/knowledge/lib/linkCountsText.es.js.map +1 -0
  252. package/dist/domains/knowledge/lib/pageAddress.cjs.js +29 -0
  253. package/dist/domains/knowledge/lib/pageAddress.cjs.js.map +1 -0
  254. package/dist/domains/knowledge/lib/pageAddress.d.ts +1 -0
  255. package/dist/domains/knowledge/lib/pageAddress.es.js +27 -0
  256. package/dist/domains/knowledge/lib/pageAddress.es.js.map +1 -0
  257. package/dist/domains/knowledge/lib/pageGraph.cjs.js +11 -11
  258. package/dist/domains/knowledge/lib/pageGraph.cjs.js.map +1 -1
  259. package/dist/domains/knowledge/lib/pageGraph.es.js +11 -11
  260. package/dist/domains/knowledge/lib/pageGraph.es.js.map +1 -1
  261. package/dist/domains/knowledge/lib/pageRefs.cjs.js +69 -0
  262. package/dist/domains/knowledge/lib/pageRefs.cjs.js.map +1 -0
  263. package/dist/domains/knowledge/lib/pageRefs.d.ts +1 -0
  264. package/dist/domains/knowledge/lib/pageRefs.es.js +66 -0
  265. package/dist/domains/knowledge/lib/pageRefs.es.js.map +1 -0
  266. package/dist/domains/knowledge/lib/pageTree.cjs.js +5 -0
  267. package/dist/domains/knowledge/lib/pageTree.cjs.js.map +1 -1
  268. package/dist/domains/knowledge/lib/pageTree.es.js +5 -1
  269. package/dist/domains/knowledge/lib/pageTree.es.js.map +1 -1
  270. package/dist/domains/knowledge/lib/remarkKnowledgeLinks.cjs.js +82 -0
  271. package/dist/domains/knowledge/lib/remarkKnowledgeLinks.cjs.js.map +1 -0
  272. package/dist/domains/knowledge/lib/remarkKnowledgeLinks.d.ts +1 -0
  273. package/dist/domains/knowledge/lib/remarkKnowledgeLinks.es.js +78 -0
  274. package/dist/domains/knowledge/lib/remarkKnowledgeLinks.es.js.map +1 -0
  275. package/dist/domains/knowledge/lib/tooltipSide.cjs.js +77 -0
  276. package/dist/domains/knowledge/lib/tooltipSide.cjs.js.map +1 -0
  277. package/dist/domains/knowledge/lib/tooltipSide.d.ts +1 -0
  278. package/dist/domains/knowledge/lib/tooltipSide.es.js +75 -0
  279. package/dist/domains/knowledge/lib/tooltipSide.es.js.map +1 -0
  280. package/dist/domains/knowledge/lib/useLeavingNodes.cjs.js +65 -0
  281. package/dist/domains/knowledge/lib/useLeavingNodes.cjs.js.map +1 -0
  282. package/dist/domains/knowledge/lib/useLeavingNodes.d.ts +1 -0
  283. package/dist/domains/knowledge/lib/useLeavingNodes.es.js +62 -0
  284. package/dist/domains/knowledge/lib/useLeavingNodes.es.js.map +1 -0
  285. package/dist/domains/knowledge/lib/useStableWhile.cjs.js +15 -0
  286. package/dist/domains/knowledge/lib/useStableWhile.cjs.js.map +1 -0
  287. package/dist/domains/knowledge/lib/useStableWhile.d.ts +1 -0
  288. package/dist/domains/knowledge/lib/useStableWhile.es.js +14 -0
  289. package/dist/domains/knowledge/lib/useStableWhile.es.js.map +1 -0
  290. package/dist/domains/metrics/components/agent-runs-card.cjs.js +3 -3
  291. package/dist/domains/metrics/components/agent-runs-card.es.js +3 -3
  292. package/dist/domains/metrics/components/breakdown/breakdown-tab-body.cjs.js +1 -1
  293. package/dist/domains/metrics/components/breakdown/breakdown-tab-body.es.js +1 -1
  294. package/dist/domains/metrics/components/breakdown-card.cjs.js +1 -1
  295. package/dist/domains/metrics/components/breakdown-card.es.js +1 -1
  296. package/dist/domains/metrics/components/chart-area.cjs.js +1 -1
  297. package/dist/domains/metrics/components/chart-area.es.js +1 -1
  298. package/dist/domains/metrics/components/chart-card-error.cjs.js +1 -1
  299. package/dist/domains/metrics/components/chart-card-error.es.js +1 -1
  300. package/dist/domains/metrics/components/chart-card.cjs.js +1 -1
  301. package/dist/domains/metrics/components/chart-card.es.js +1 -1
  302. package/dist/domains/metrics/components/date-range-selector.cjs.js +1 -1
  303. package/dist/domains/metrics/components/date-range-selector.es.js +1 -1
  304. package/dist/domains/metrics/components/distinct-ids/distinct-ids-chart.cjs.js +3 -2
  305. package/dist/domains/metrics/components/distinct-ids/distinct-ids-chart.cjs.js.map +1 -1
  306. package/dist/domains/metrics/components/distinct-ids/distinct-ids-chart.es.js +3 -2
  307. package/dist/domains/metrics/components/distinct-ids/distinct-ids-chart.es.js.map +1 -1
  308. package/dist/domains/metrics/components/failure-rate-card.cjs.js +3 -2
  309. package/dist/domains/metrics/components/failure-rate-card.cjs.js.map +1 -1
  310. package/dist/domains/metrics/components/failure-rate-card.es.js +3 -2
  311. package/dist/domains/metrics/components/failure-rate-card.es.js.map +1 -1
  312. package/dist/domains/metrics/components/latency-card.cjs.js +3 -2
  313. package/dist/domains/metrics/components/latency-card.cjs.js.map +1 -1
  314. package/dist/domains/metrics/components/latency-card.es.js +3 -2
  315. package/dist/domains/metrics/components/latency-card.es.js.map +1 -1
  316. package/dist/domains/metrics/components/metrics-time-range-chip.cjs.js +1 -1
  317. package/dist/domains/metrics/components/metrics-time-range-chip.es.js +1 -1
  318. package/dist/domains/metrics/components/scores-card.cjs.js +2 -1
  319. package/dist/domains/metrics/components/scores-card.cjs.js.map +1 -1
  320. package/dist/domains/metrics/components/scores-card.es.js +2 -1
  321. package/dist/domains/metrics/components/scores-card.es.js.map +1 -1
  322. package/dist/domains/metrics/components/token-usage-card.cjs.js +3 -3
  323. package/dist/domains/metrics/components/token-usage-card.es.js +3 -3
  324. package/dist/domains/metrics/components/trace-aggregate-card.cjs.js +1 -1
  325. package/dist/domains/metrics/components/trace-aggregate-card.es.js +1 -1
  326. package/dist/domains/observability/components/observability-histogram.cjs.js +1 -1
  327. package/dist/domains/observability/components/observability-histogram.es.js +1 -1
  328. package/dist/domains/scores/components/trace-scores-list.cjs.js +2 -2
  329. package/dist/domains/scores/components/trace-scores-list.es.js +2 -2
  330. package/dist/domains/skills/skill-simple-form.cjs.js +1 -1
  331. package/dist/domains/skills/skill-simple-form.es.js +1 -1
  332. package/dist/domains/spans/components/spans-filter-bar.cjs.js +2 -2
  333. package/dist/domains/spans/components/spans-filter-bar.es.js +2 -2
  334. package/dist/domains/spans/hooks/use-spans-filter-bar.cjs.js +1 -1
  335. package/dist/domains/spans/hooks/use-spans-filter-bar.es.js +1 -1
  336. package/dist/domains/threads/components/threads-filter-bar.cjs.js +2 -2
  337. package/dist/domains/threads/components/threads-filter-bar.es.js +2 -2
  338. package/dist/domains/threads/hooks/use-threads-filter-bar.cjs.js +1 -1
  339. package/dist/domains/threads/hooks/use-threads-filter-bar.es.js +1 -1
  340. package/dist/domains/traces/components/span-payload/span-error-renderer.cjs.js +1 -1
  341. package/dist/domains/traces/components/span-payload/span-error-renderer.es.js +1 -1
  342. package/dist/domains/traces/components/span-payload/span-input-renderers.cjs.js +1 -1
  343. package/dist/domains/traces/components/span-payload/span-input-renderers.es.js +1 -1
  344. package/dist/domains/traces/components/span-payload/span-output-renderers.cjs.js +2 -2
  345. package/dist/domains/traces/components/span-payload/span-output-renderers.es.js +2 -2
  346. package/dist/domains/traces/components/span-payload/span-payload-messages.cjs.js +2 -2
  347. package/dist/domains/traces/components/span-payload/span-payload-messages.es.js +2 -2
  348. package/dist/domains/traces/components/span-payload/span-payload-primitives.cjs.js +1 -1
  349. package/dist/domains/traces/components/span-payload/span-payload-primitives.es.js +1 -1
  350. package/dist/domains/traces/components/span-payload/span-payload-processor.cjs.js +1 -1
  351. package/dist/domains/traces/components/span-payload/span-payload-processor.es.js +1 -1
  352. package/dist/domains/traces/components/span-payload/span-payload-tool.cjs.js +1 -1
  353. package/dist/domains/traces/components/span-payload/span-payload-tool.es.js +1 -1
  354. package/dist/domains/traces/components/span-payload/span-processor-mutations.cjs.js +1 -1
  355. package/dist/domains/traces/components/span-payload/span-processor-mutations.es.js +1 -1
  356. package/dist/domains/traces/components/span-payload/span-tools-section.cjs.js +1 -1
  357. package/dist/domains/traces/components/span-payload/span-tools-section.es.js +1 -1
  358. package/dist/domains/traces/components/span-timeline-row.cjs.js +1 -1
  359. package/dist/domains/traces/components/span-timeline-row.es.js +1 -1
  360. package/dist/domains/traces/components/span-timing-hover-card.cjs.js +1 -1
  361. package/dist/domains/traces/components/span-timing-hover-card.es.js +1 -1
  362. package/dist/domains/traces/components/trace-data-panel-view.cjs.js +8 -2
  363. package/dist/domains/traces/components/trace-data-panel-view.cjs.js.map +1 -1
  364. package/dist/domains/traces/components/trace-data-panel-view.es.js +8 -2
  365. package/dist/domains/traces/components/trace-data-panel-view.es.js.map +1 -1
  366. package/dist/domains/traces/components/trace-error-cell.cjs.js +1 -1
  367. package/dist/domains/traces/components/trace-error-cell.es.js +1 -1
  368. package/dist/domains/traces/components/traces-explorer/traces-explorer-filter-bar.cjs.js +2 -2
  369. package/dist/domains/traces/components/traces-explorer/traces-explorer-filter-bar.es.js +2 -2
  370. package/dist/domains/traces/components/traces-explorer/use-traces-filter-bar.cjs.js +1 -1
  371. package/dist/domains/traces/components/traces-explorer/use-traces-filter-bar.es.js +1 -1
  372. package/dist/domains/workflows/runs/workflow-run-list.cjs.js +1 -1
  373. package/dist/domains/workflows/runs/workflow-run-list.es.js +1 -1
  374. package/dist/domains/workflows/workflow/input/workflow-processor-input.cjs.js +2 -2
  375. package/dist/domains/workflows/workflow/input/workflow-processor-input.es.js +2 -2
  376. package/dist/domains/workflows/workflow/use-workflow-trigger.cjs.js +3 -3
  377. package/dist/domains/workflows/workflow/use-workflow-trigger.es.js +3 -3
  378. package/dist/domains/workflows/workflow/workflow-input-data.cjs.js +1 -1
  379. package/dist/domains/workflows/workflow/workflow-input-data.es.js +1 -1
  380. package/dist/domains/workflows/workflow/workflow-suspended-steps.cjs.js +2 -2
  381. package/dist/domains/workflows/workflow/workflow-suspended-steps.es.js +2 -2
  382. package/dist/domains/workflows/workflow/workflow-time-travel-form.cjs.js +2 -2
  383. package/dist/domains/workflows/workflow/workflow-time-travel-form.es.js +2 -2
  384. package/dist/domains/workspace/components/workspace-add-skill-dialog.cjs.js +1 -1
  385. package/dist/domains/workspace/components/workspace-add-skill-dialog.es.js +1 -1
  386. package/dist/domains/workspace/components/workspace-markdown-preview.cjs.js +1 -1
  387. package/dist/domains/workspace/components/workspace-markdown-preview.es.js +1 -1
  388. package/dist/{filter-bar-time-range-mg9JeYue.js → filter-bar-time-range-qlV8p7hp.js} +2 -2
  389. package/dist/{filter-bar-time-range-mg9JeYue.js.map → filter-bar-time-range-qlV8p7hp.js.map} +1 -1
  390. package/dist/{filter-bar-time-range-BA-pXMlo.cjs → filter-bar-time-range-zdM024JD.cjs} +2 -2
  391. package/dist/{filter-bar-time-range-BA-pXMlo.cjs.map → filter-bar-time-range-zdM024JD.cjs.map} +1 -1
  392. package/dist/lib/form/dynamic-form.cjs.js +2 -2
  393. package/dist/lib/form/dynamic-form.es.js +2 -2
  394. package/dist/lib/form/json-draft.cjs.js +1 -1
  395. package/dist/lib/form/json-draft.es.js +1 -1
  396. package/dist/lib/form/zod-provider.cjs.js +1 -1
  397. package/dist/lib/form/zod-provider.es.js +1 -1
  398. package/dist/{span-payload-primitives-B3RdahsC.js → span-payload-primitives-CKKIAVPJ.js} +4 -4
  399. package/dist/{span-payload-primitives-B3RdahsC.js.map → span-payload-primitives-CKKIAVPJ.js.map} +1 -1
  400. package/dist/{span-payload-primitives-_2d-pAOD.cjs → span-payload-primitives-D4tHWN2W.cjs} +4 -4
  401. package/dist/{span-payload-primitives-_2d-pAOD.cjs.map → span-payload-primitives-D4tHWN2W.cjs.map} +1 -1
  402. package/dist/src/domains/chat/knowledge/knowledge-links.d.ts +32 -4
  403. package/dist/src/domains/chat/knowledge/pinned-knowledge-data.d.ts +2 -0
  404. package/dist/src/domains/chat/tools/badges/knowledge-body-range.d.ts +12 -0
  405. package/dist/src/domains/chat/tools/badges/knowledge-tool-badge.d.ts +5 -2
  406. package/dist/src/domains/chat/tools/badges/mcp-input-request-badge.d.ts +18 -0
  407. package/dist/src/domains/chat/tools/badges/tool-badge.d.ts +4 -1
  408. package/dist/src/domains/chat/tools/badges/use-deferred-input-request.d.ts +44 -0
  409. package/dist/src/domains/chat/tools/badges/use-mcp-task-progress.d.ts +6 -1
  410. package/dist/src/domains/chat/tools/connect-prompt-tool.d.ts +10 -1
  411. package/dist/src/domains/chat/tools/mcp-input-request-tool.d.ts +18 -0
  412. package/dist/src/domains/chat/tools/mcp-input-request.d.ts +40 -0
  413. package/dist/src/domains/chat/tools/tool-card-kind.d.ts +4 -2
  414. package/dist/src/domains/chat/tools/tool-card.d.ts +1 -1
  415. package/dist/src/domains/connect/types.d.ts +16 -0
  416. package/dist/src/domains/dashboards/components/dashboard-renderer.d.ts +4 -2
  417. package/dist/src/domains/dashboards/hooks/use-widget-views.d.ts +4 -3
  418. package/dist/src/domains/dashboards/index.d.ts +1 -1
  419. package/dist/src/domains/dashboards/kinds/scores/scores-widget.d.ts +1 -1
  420. package/dist/src/domains/dashboards/lib/chrome.d.ts +6 -4
  421. package/dist/src/domains/dashboards/widget-kind.d.ts +12 -0
  422. package/dist/src/domains/knowledge/components/KnowledgePageCard.d.ts +23 -0
  423. package/dist/src/domains/knowledge/components/KnowledgePageExplorer.d.ts +29 -0
  424. package/dist/src/domains/knowledge/components/KnowledgePageGraph.d.ts +27 -1
  425. package/dist/src/domains/knowledge/components/KnowledgePageTree.d.ts +2 -1
  426. package/dist/src/domains/knowledge/lib/circleText.d.ts +2 -0
  427. package/dist/src/domains/knowledge/lib/fitViewport.d.ts +67 -0
  428. package/dist/src/domains/knowledge/lib/graphShape.d.ts +131 -0
  429. package/dist/src/domains/knowledge/lib/linkCountsText.d.ts +5 -0
  430. package/dist/src/domains/knowledge/lib/pageAddress.d.ts +5 -0
  431. package/dist/src/domains/knowledge/lib/pageGraph.d.ts +4 -4
  432. package/dist/src/domains/knowledge/lib/pageRefs.d.ts +15 -0
  433. package/dist/src/domains/knowledge/lib/pageTree.d.ts +2 -0
  434. package/dist/src/domains/knowledge/lib/remarkKnowledgeLinks.d.ts +13 -0
  435. package/dist/src/domains/knowledge/lib/tooltipSide.d.ts +40 -0
  436. package/dist/src/domains/knowledge/lib/useLeavingNodes.d.ts +14 -0
  437. package/dist/src/domains/knowledge/lib/useStableWhile.d.ts +5 -0
  438. package/dist/src/domains/knowledge/pageTypes.d.ts +6 -1
  439. package/dist/src/ds/components/DateTimeRangePicker/date-time-range-picker.d.ts +12 -1
  440. package/dist/src/ds/components/MarkdownRenderer/markdown-renderer.d.ts +8 -1
  441. package/dist/src/ds/components/MetricsCard/metrics-card-headline.d.ts +1 -1
  442. package/dist/src/ds/components/MetricsLineChart/metrics-line-chart.d.ts +8 -0
  443. package/dist/src/ds/components/ai/tool-call/tool-phrases.d.ts +6 -0
  444. package/dist/style.css +73 -2
  445. package/dist/{tool-approval-1qiB4hhG.cjs → tool-approval-CJvPDL7D.cjs} +4 -4
  446. package/dist/{tool-approval-1qiB4hhG.cjs.map → tool-approval-CJvPDL7D.cjs.map} +1 -1
  447. package/dist/{tool-approval-B6MFcaKV.js → tool-approval-DyN11vFA.js} +4 -4
  448. package/dist/{tool-approval-B6MFcaKV.js.map → tool-approval-DyN11vFA.js.map} +1 -1
  449. package/dist/{tool-call-Bu66PkD-.cjs → tool-call-DBn14X-3.cjs} +9 -3
  450. package/dist/{tool-call-Bu66PkD-.cjs.map → tool-call-DBn14X-3.cjs.map} +1 -1
  451. package/dist/{tool-call-Ch9-jjXU.js → tool-call-Dsj43lvJ.js} +9 -3
  452. package/dist/{tool-call-Ch9-jjXU.js.map → tool-call-Dsj43lvJ.js.map} +1 -1
  453. package/dist/{tool-card-DrqPZfPQ.cjs → tool-card-BOLXeKIp.cjs} +167 -126
  454. package/dist/tool-card-BOLXeKIp.cjs.map +1 -0
  455. package/dist/{tool-card-sO6P2Qj4.js → tool-card-HZ5bm7ue.js} +168 -127
  456. package/dist/tool-card-HZ5bm7ue.js.map +1 -0
  457. package/dist/{tool-presentation-DvCsb_kz.js → tool-presentation-CvibOP1J.js} +6 -1
  458. package/dist/tool-presentation-CvibOP1J.js.map +1 -0
  459. package/dist/{tool-presentation-CQlDEwgS.cjs → tool-presentation-D6nzVruN.cjs} +6 -1
  460. package/dist/tool-presentation-D6nzVruN.cjs.map +1 -0
  461. package/package.json +11 -11
  462. package/dist/MarkdownRenderer-2GOXQJEv.js.map +0 -1
  463. package/dist/MarkdownRenderer-CB8Z_CHc.cjs.map +0 -1
  464. package/dist/MetricsCard-CEB-f2sv.cjs.map +0 -1
  465. package/dist/MetricsCard-M8awJ2B_.js.map +0 -1
  466. package/dist/MetricsLineChart-BhHoduO0.js.map +0 -1
  467. package/dist/MetricsLineChart-CjBm4IXu.cjs.map +0 -1
  468. package/dist/date-time-range-picker-CTt7Od0P.cjs.map +0 -1
  469. package/dist/date-time-range-picker-DKh1K9Iy.js.map +0 -1
  470. package/dist/domains/knowledge/lib/pageLayout.cjs.js +0 -49
  471. package/dist/domains/knowledge/lib/pageLayout.cjs.js.map +0 -1
  472. package/dist/domains/knowledge/lib/pageLayout.d.ts +0 -1
  473. package/dist/domains/knowledge/lib/pageLayout.es.js +0 -48
  474. package/dist/domains/knowledge/lib/pageLayout.es.js.map +0 -1
  475. package/dist/src/domains/knowledge/lib/pageLayout.d.ts +0 -24
  476. package/dist/tool-card-DrqPZfPQ.cjs.map +0 -1
  477. package/dist/tool-card-sO6P2Qj4.js.map +0 -1
  478. package/dist/tool-presentation-CQlDEwgS.cjs.map +0 -1
  479. package/dist/tool-presentation-DvCsb_kz.js.map +0 -1
@@ -1,10 +1,19 @@
1
1
  import { t as Txt } from "../../../Txt-BM6TVV_c.js";
2
+ import { linkCountsText } from "../lib/linkCountsText.es.js";
3
+ import { KnowledgePageCard } from "./KnowledgePageCard.es.js";
4
+ import { childCountLabel } from "../lib/pageTree.es.js";
5
+ import { branchColors, crowdScale, graphShape, isChildPlaceholder, isRestCircle, isScopeGroup, radialLayout } from "../lib/graphShape.es.js";
6
+ import { fitWords } from "../lib/circleText.es.js";
7
+ import { prefersMotion, useLeavingNodes } from "../lib/useLeavingNodes.es.js";
8
+ import { INITIAL_SETTLE_MS, fitAllViewport, fitDuration, fitViewport, nudgeAxis } from "../lib/fitViewport.es.js";
9
+ import { runLayout } from "../lib/layout.es.js";
10
+ import { pagePath, pageRef } from "../lib/pageAddress.es.js";
2
11
  import { grantGraph, pageGraphEdges, pageNeighbourIds, toPageFlow } from "../lib/pageGraph.es.js";
3
- import { layoutPageGraph } from "../lib/pageLayout.es.js";
4
- import { Fragment, memo, useCallback, useEffect, useMemo, useRef } from "react";
12
+ import { chooseTooltipSide } from "../lib/tooltipSide.es.js";
13
+ import { Fragment, memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
5
14
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
6
- import { Maximize, Pin } from "lucide-react";
7
- import { Background, BackgroundVariant, BaseEdge, ControlButton, Controls, Handle, MarkerType, Position, ReactFlow, ReactFlowProvider, getViewportForBounds, useReactFlow } from "@xyflow/react";
15
+ import { ArrowUpToLine, Layers, Maximize, Pin } from "lucide-react";
16
+ import { Background, BackgroundVariant, BaseEdge, ControlButton, Controls, Handle, MarkerType, Position, ReactFlow, ReactFlowProvider, useReactFlow, useStore } from "@xyflow/react";
8
17
  import "@xyflow/react/dist/style.css";
9
18
  import '../../../KnowledgePageGraph.css';// src/domains/knowledge/components/KnowledgePageGraph.tsx
10
19
  var SCOPE_STYLE = {
@@ -12,12 +21,29 @@ var SCOPE_STYLE = {
12
21
  background: "color-mix(in oklab, var(--chart-blue) 16%, var(--card))"
13
22
  };
14
23
  var PAGE_STYLE = { "--knowledge-color": "var(--border-strong)" };
24
+ /** A farther page drawn as a named circle: the legend's "Page in another branch" dot grown, same solid grey fill, name in the page background colour to read on it. */
25
+ var FAR_FILL = "var(--muted-foreground)";
26
+ var FAR_STYLE = {
27
+ "--knowledge-color": FAR_FILL,
28
+ background: FAR_FILL
29
+ };
15
30
  var PARENT_EDGE_STYLE = {
16
31
  stroke: "var(--foreground)",
17
32
  strokeOpacity: .45,
18
33
  strokeDasharray: "4 4",
19
34
  strokeWidth: 1.25
20
35
  };
36
+ var UP_EDGE_COLOR = "var(--chart-cyan)";
37
+ var UP_EDGE_STYLE = {
38
+ stroke: UP_EDGE_COLOR,
39
+ strokeWidth: 2.25
40
+ };
41
+ var UP_ARROW = {
42
+ type: MarkerType.ArrowClosed,
43
+ color: UP_EDGE_COLOR,
44
+ width: 14,
45
+ height: 14
46
+ };
21
47
  var MENTION_EDGE_COLOR = "var(--chart-blue)";
22
48
  var PARENT_ARROW_COLOR = "var(--muted-foreground)";
23
49
  var PARENT_ARROW = {
@@ -39,6 +65,38 @@ var GRANT_ARROW = {
39
65
  width: 13,
40
66
  height: 13
41
67
  };
68
+ var LINK_EDGE_COLOR = "var(--chart-green)";
69
+ var LINK_EDGE_STYLE = {
70
+ stroke: LINK_EDGE_COLOR,
71
+ strokeWidth: 1.25
72
+ };
73
+ var LINK_ARROW = {
74
+ type: MarkerType.ArrowClosed,
75
+ color: LINK_EDGE_COLOR,
76
+ width: 12,
77
+ height: 12
78
+ };
79
+ var DIRECTION_EDGE_STYLE = {
80
+ out: { stroke: LINK_EDGE_COLOR },
81
+ in: {
82
+ stroke: "var(--chart-orange)",
83
+ strokeDasharray: "6 3"
84
+ }
85
+ };
86
+ var DIRECTION_ARROW = {
87
+ out: LINK_ARROW,
88
+ in: {
89
+ type: MarkerType.ArrowClosed,
90
+ color: "var(--chart-orange)",
91
+ width: 12,
92
+ height: 12
93
+ }
94
+ };
95
+ var DOT_PARENT_EDGE_STYLE = {
96
+ stroke: "var(--foreground)",
97
+ strokeOpacity: .2,
98
+ strokeWidth: 1
99
+ };
42
100
  var PLACEHOLDER_STYLE = {
43
101
  background: "var(--card)",
44
102
  borderColor: "var(--chart-purple)",
@@ -61,44 +119,291 @@ function pinBadgeStyle(size) {
61
119
  }
62
120
  };
63
121
  }
64
- /** A scope or page: its kind on top, its name below, both inside the circle. */
122
+ /** How long a focus change loads before the graph says so. */
123
+ var LOADING_HINT_DELAY_MS = 150;
124
+ /** How long it holds the old layout for pages that never arrive before drawing what it has. */
125
+ var LOADING_RELEASE_MS = 4e3;
126
+ /** Most a tooltip is enlarged to offset a zoomed-out graph. */
127
+ var TOOLTIP_MAX_SCALE = 3;
128
+ /** Screen-space box of an element. */
129
+ function boxOf(el) {
130
+ const { left, top, right, bottom } = el.getBoundingClientRect();
131
+ return {
132
+ left,
133
+ top,
134
+ right,
135
+ bottom
136
+ };
137
+ }
138
+ /** The highlighted lines of the graph as screen-space segments, where the browser can measure them. */
139
+ function drawnLines(root) {
140
+ const lines = [];
141
+ for (const path of root.querySelectorAll(".react-flow__edge-path")) {
142
+ if (typeof path.getTotalLength !== "function" || typeof path.getScreenCTM !== "function") continue;
143
+ const matrix = path.getScreenCTM();
144
+ if (!matrix) continue;
145
+ const length = path.getTotalLength();
146
+ const start = path.getPointAtLength(0);
147
+ const end = path.getPointAtLength(length);
148
+ lines.push({
149
+ from: {
150
+ x: matrix.a * start.x + matrix.c * start.y + matrix.e,
151
+ y: matrix.b * start.x + matrix.d * start.y + matrix.f
152
+ },
153
+ to: {
154
+ x: matrix.a * end.x + matrix.c * end.y + matrix.e,
155
+ y: matrix.b * end.x + matrix.d * end.y + matrix.f
156
+ }
157
+ });
158
+ }
159
+ return lines;
160
+ }
161
+ /** Where a tooltip sits against its dot, and which way it grows from it. */
162
+ var TOOLTIP_PLACEMENT = {
163
+ top: {
164
+ tip: "bottom-full left-1/2 mb-2",
165
+ transform: "translateX(-50%)",
166
+ origin: "bottom center",
167
+ tail: "top-full left-1/2 -translate-x-1/2 -translate-y-1/2 border-r border-b"
168
+ },
169
+ bottom: {
170
+ tip: "top-full left-1/2 mt-2",
171
+ transform: "translateX(-50%)",
172
+ origin: "top center",
173
+ tail: "bottom-full left-1/2 -translate-x-1/2 translate-y-1/2 border-l border-t"
174
+ },
175
+ right: {
176
+ tip: "left-full top-1/2 ml-2",
177
+ transform: "translateY(-50%)",
178
+ origin: "center left",
179
+ tail: "right-full top-1/2 translate-x-1/2 -translate-y-1/2 border-l border-b"
180
+ },
181
+ left: {
182
+ tip: "right-full top-1/2 mr-2",
183
+ transform: "translateY(-50%)",
184
+ origin: "center right",
185
+ tail: "left-full top-1/2 -translate-x-1/2 -translate-y-1/2 border-r border-t"
186
+ }
187
+ };
188
+ /** The mark on a circle for a page that sits above others: layers for a scope, an up arrow for an ancestor page. */
189
+ function LevelIcon({ level }) {
190
+ return /* @__PURE__ */ jsx(level === "scope" ? Layers : ArrowUpToLine, {
191
+ "aria-hidden": true,
192
+ "data-testid": `knowledge-level-icon-${level}`,
193
+ className: "mr-1 inline-block size-3 align-text-bottom"
194
+ });
195
+ }
196
+ /** A page far from the focus: a small coloured dot that names itself on hover. */
197
+ function DotNode({ data }) {
198
+ const { page, size, color, path, ref, links, notShown } = data;
199
+ const [hover, setHover] = useState(false);
200
+ const dotRef = useRef(null);
201
+ const tipRef = useRef(null);
202
+ const [side, setSide] = useState("top");
203
+ const zoom = useStore((state) => state.transform[2]);
204
+ const tipScale = Math.min(Math.max(1 / zoom, 1), TOOLTIP_MAX_SCALE);
205
+ const standIn = isChildPlaceholder(page);
206
+ const group = isScopeGroup(page);
207
+ const hint = standIn ? "Hover or click to load them" : group ? "Click to show them" : null;
208
+ const children = !hint && page.childCount ? `${childCountLabel(page)} ${page.childCount === 1 ? "child" : "children"}` : null;
209
+ const kind = page.isScope ? page.displayLabel ?? "Scope" : "Page";
210
+ const linkText = links && (links.in > 0 || links.out > 0) ? linkCountsText(links, notShown) : null;
211
+ const label = [
212
+ page.name,
213
+ hint ?? path,
214
+ children,
215
+ linkText
216
+ ].filter(Boolean).join(" · ");
217
+ useLayoutEffect(() => {
218
+ const dot = dotRef.current;
219
+ const tip = tipRef.current;
220
+ const graph = dot?.closest(".knowledge-graph");
221
+ if (!hover || !dot || !tip || !graph) return;
222
+ const dotBox = boxOf(dot);
223
+ const tipBox = boxOf(tip);
224
+ const scaledWidth = tip.offsetWidth * tipScale;
225
+ const scaledHeight = tip.offsetHeight * tipScale;
226
+ if (!scaledWidth || !scaledHeight) return;
227
+ const obstacles = [...graph.querySelectorAll("[data-testid=\"knowledge-page-node\"], [data-testid=\"knowledge-page-dot\"]"), ...(graph.parentElement ?? graph).querySelectorAll("[aria-label=\"Legend\"], [data-testid=\"knowledge-graph-back\"], .react-flow__controls")].map(boxOf).filter((box) => box.right > box.left && !(box.left === tipBox.left && box.top === tipBox.top) && !(box.left === dotBox.left && box.top === dotBox.top));
228
+ setSide(chooseTooltipSide({
229
+ dot: dotBox,
230
+ tip: {
231
+ width: scaledWidth,
232
+ height: scaledHeight
233
+ },
234
+ gap: 8,
235
+ bounds: boxOf(graph),
236
+ obstacles,
237
+ lines: drawnLines(graph)
238
+ }));
239
+ }, [hover, tipScale]);
240
+ const placement = TOOLTIP_PLACEMENT[side];
241
+ return /* @__PURE__ */ jsxs("div", {
242
+ ref: dotRef,
243
+ "data-testid": "knowledge-page-dot",
244
+ "data-page-id": page.id,
245
+ "data-page-type": standIn ? "stand-in" : group ? "group" : page.isScope ? "scope" : "page",
246
+ "aria-label": label,
247
+ className: "relative cursor-pointer transition-opacity",
248
+ style: {
249
+ width: size,
250
+ height: size,
251
+ opacity: standIn ? .5 : void 0
252
+ },
253
+ onPointerEnter: () => setHover(true),
254
+ onPointerLeave: () => setHover(false),
255
+ children: [
256
+ /* @__PURE__ */ jsx("span", {
257
+ "aria-hidden": true,
258
+ "data-testid": "knowledge-dot-hit-area",
259
+ className: "absolute rounded-full",
260
+ style: { inset: -8 }
261
+ }),
262
+ /* @__PURE__ */ jsx("div", {
263
+ className: "size-full rounded-full border",
264
+ style: {
265
+ background: standIn ? "transparent" : color ?? "var(--muted-foreground)",
266
+ borderColor: color ?? "var(--muted-foreground)",
267
+ borderStyle: standIn ? "dashed" : "solid"
268
+ },
269
+ children: data.showCount && !standIn && !group && links && links.in + links.out > 0 ? /* @__PURE__ */ jsx(Txt, {
270
+ as: "span",
271
+ variant: "meta",
272
+ "data-testid": "knowledge-dot-count",
273
+ className: "flex size-full items-center justify-center",
274
+ style: { color: "var(--background)" },
275
+ children: links.in + links.out
276
+ }) : null
277
+ }),
278
+ hover ? /* @__PURE__ */ jsxs("div", {
279
+ role: "tooltip",
280
+ ref: tipRef,
281
+ "data-side": side,
282
+ className: `pointer-events-none absolute ${placement.tip} flex w-max max-w-72 flex-col rounded-md border border-border bg-card px-2 py-1 shadow-overlay`,
283
+ style: {
284
+ transform: `${placement.transform} scale(${tipScale})`,
285
+ transformOrigin: placement.origin
286
+ },
287
+ children: [/* @__PURE__ */ jsx("span", {
288
+ "data-testid": "knowledge-tooltip-tail",
289
+ "aria-hidden": true,
290
+ className: `absolute size-2 rotate-45 border-border bg-card ${placement.tail}`
291
+ }), hint ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Txt, {
292
+ as: "span",
293
+ variant: "label",
294
+ tone: "ink",
295
+ children: page.name
296
+ }), /* @__PURE__ */ jsx(Txt, {
297
+ as: "span",
298
+ variant: "meta",
299
+ tone: "muted",
300
+ children: group ? `${path} · ${hint}` : hint
301
+ })] }) : /* @__PURE__ */ jsx(KnowledgePageCard, {
302
+ name: page.name,
303
+ kind,
304
+ pageRef: ref,
305
+ path,
306
+ childCount: page.childCount ? childCountLabel(page) : void 0,
307
+ links,
308
+ notShown
309
+ })]
310
+ }) : null,
311
+ /* @__PURE__ */ jsx(Handle, {
312
+ type: "target",
313
+ position: Position.Top,
314
+ className: "!invisible"
315
+ }),
316
+ /* @__PURE__ */ jsx(Handle, {
317
+ type: "source",
318
+ position: Position.Bottom,
319
+ className: "!invisible"
320
+ })
321
+ ]
322
+ });
323
+ }
324
+ /** Below this a circle uses the small type and two lines of name. */
325
+ var COMPACT_CIRCLE_SIZE = 96;
326
+ /** The share of a circle's width its text may use; a circle is narrower toward the top and bottom. */
327
+ var CIRCLE_TEXT_SHARE = .74;
328
+ /** A scope or page: its kind on top, its name below, both inside the circle; far pages draw as dots. */
65
329
  function PageNodeComponent({ data }) {
66
- const { page, size, selected, pinned, dimmed } = data;
330
+ if (data.form === "dot") return /* @__PURE__ */ jsx(DotNode, { data });
331
+ const { page, size, selected, pinned, badge } = data;
332
+ const group = isScopeGroup(page);
67
333
  const kind = page.isScope ? page.displayLabel ?? "Scope" : "Page";
68
334
  const placeholder = isPlaceholder(page);
335
+ const rest = isRestCircle(page);
336
+ const compact = size < COMPACT_CIRCLE_SIZE;
337
+ const fit = Math.max(4, Math.floor(size * CIRCLE_TEXT_SHARE / (compact ? 6.5 : 7.5)));
69
338
  return /* @__PURE__ */ jsxs("div", {
70
339
  "data-testid": "knowledge-page-node",
71
340
  "data-page-id": page.id,
72
- "data-page-type": placeholder ? "placeholder" : page.isScope ? "scope" : "page",
341
+ "data-page-type": placeholder ? "placeholder" : rest ? "rest" : group ? "group" : page.isScope ? "scope" : "page",
73
342
  className: `relative transition-opacity ${placeholder ? "cursor-default" : "cursor-pointer"}`,
74
343
  style: {
75
344
  width: size,
76
- height: size,
77
- opacity: dimmed ? .35 : 1
345
+ height: size
78
346
  },
79
- title: placeholder ? `${kind} · ${page.address} · nothing written to it yet` : page.isScope && page.address ? `${kind} · ${page.address}` : page.name,
347
+ title: placeholder ? `${kind} · ${page.address} · nothing written to it yet` : rest ? `Show ${Math.min(12, page.childCount ?? 12)} more of ${page.childCount ?? "the rest"}` : group ? `${page.name}: click to show them` : page.isScope && page.address ? `${kind} · ${page.address}` : data.links && data.links.in + data.links.out > 0 ? `${page.name} · ${linkCountsText(data.links, data.notShown)}` : page.name,
80
348
  children: [
81
349
  /* @__PURE__ */ jsxs("div", {
82
- className: "knowledge-circle flex size-full flex-col items-center justify-center gap-0.5 overflow-hidden rounded-full border-2 px-3 text-center shadow-raised",
350
+ className: `knowledge-circle flex size-full flex-col items-center justify-center gap-0.5 overflow-hidden rounded-full border-2 text-center shadow-raised ${rest ? "hover:border-foreground" : ""}`,
83
351
  "data-selected": selected || void 0,
84
- style: placeholder ? PLACEHOLDER_STYLE : page.isScope ? SCOPE_STYLE : PAGE_STYLE,
85
- children: [/* @__PURE__ */ jsx(Txt, {
352
+ "data-far": data.far || void 0,
353
+ style: {
354
+ ...placeholder || rest ? PLACEHOLDER_STYLE : data.far ? FAR_STYLE : page.isScope ? SCOPE_STYLE : PAGE_STYLE,
355
+ paddingInline: size * (.26 / 2)
356
+ },
357
+ children: [data.far ? null : /* @__PURE__ */ jsxs(Txt, {
86
358
  as: "span",
87
359
  variant: "meta",
88
360
  tone: page.isScope ? void 0 : "muted",
89
- className: "pointer-events-none line-clamp-2 max-w-full tracking-wide uppercase",
361
+ className: "pointer-events-none inline-flex max-w-full items-center justify-center gap-1 whitespace-nowrap uppercase",
90
362
  style: page.isScope ? { color: "var(--chart-blue)" } : void 0,
91
363
  "data-testid": page.isScope ? "knowledge-scope-caption" : "knowledge-page-caption",
92
- children: kind
364
+ children: [data.level ? /* @__PURE__ */ jsx(LevelIcon, { level: data.level }) : null, /* @__PURE__ */ jsx("span", {
365
+ className: "truncate",
366
+ children: rest ? "More" : data.isParent ? "Parent" : kind
367
+ })]
93
368
  }), /* @__PURE__ */ jsx(Txt, {
94
369
  as: "span",
95
- variant: "label",
370
+ variant: compact ? "meta" : "label",
96
371
  tone: "ink",
97
- className: "pointer-events-none line-clamp-3 max-w-full break-words",
98
- children: page.isScope && page.address ? /* @__PURE__ */ jsx(BreakAtColons, { text: page.address }) : page.name
372
+ className: `pointer-events-none max-w-full overflow-hidden [overflow-wrap:normal] ${compact ? "line-clamp-2" : "line-clamp-3"}`,
373
+ style: data.far ? { color: "var(--background)" } : void 0,
374
+ "data-testid": "knowledge-circle-name",
375
+ children: page.isScope && page.address && !group ? /* @__PURE__ */ jsx(BreakAtColons, { text: fitWords(page.address, fit) }) : fitWords(page.name, fit)
99
376
  })]
100
377
  }),
101
378
  pinned ? /* @__PURE__ */ jsx(PinBadge, { size }) : null,
379
+ data.hiddenLinks && (data.hiddenLinks.in > 0 || data.hiddenLinks.out > 0) ? /* @__PURE__ */ jsxs("span", {
380
+ "data-testid": "knowledge-hidden-links",
381
+ title: linkCountsText(data.hiddenLinks),
382
+ "aria-label": linkCountsText(data.hiddenLinks),
383
+ className: "absolute bottom-full left-1/2 -mb-1 flex -translate-x-1/2 gap-1 rounded-full border border-border bg-card px-1.5 shadow-raised",
384
+ children: [data.hiddenLinks.in > 0 ? /* @__PURE__ */ jsxs(Txt, {
385
+ as: "span",
386
+ variant: "meta",
387
+ style: { color: "var(--chart-orange)" },
388
+ children: ["→", data.hiddenLinks.in]
389
+ }) : null, data.hiddenLinks.out > 0 ? /* @__PURE__ */ jsxs(Txt, {
390
+ as: "span",
391
+ variant: "meta",
392
+ style: { color: LINK_EDGE_COLOR },
393
+ children: [data.hiddenLinks.out, "→"]
394
+ }) : null]
395
+ }) : null,
396
+ badge ? /* @__PURE__ */ jsx("span", {
397
+ "data-testid": "knowledge-child-count-badge",
398
+ "aria-label": `${badge} children, not all drawn`,
399
+ className: "absolute top-full left-1/2 -mt-1 -translate-x-1/2 rounded-full border border-border bg-card px-1.5 shadow-raised",
400
+ children: /* @__PURE__ */ jsx(Txt, {
401
+ as: "span",
402
+ variant: "meta",
403
+ tone: "ink",
404
+ children: badge
405
+ })
406
+ }) : null,
102
407
  /* @__PURE__ */ jsx(Handle, {
103
408
  type: "target",
104
409
  position: Position.Top,
@@ -155,10 +460,7 @@ function RimEdgeComponent({ id, data, style, markerEnd }) {
155
460
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(BaseEdge, {
156
461
  id,
157
462
  path,
158
- style: {
159
- ...style,
160
- opacity: data.dimmed ? .2 : 1
161
- },
463
+ style,
162
464
  markerEnd,
163
465
  interactionWidth: mention ? 20 : 0
164
466
  }), data.kind === "grant" && data.role ? /* @__PURE__ */ jsx("path", {
@@ -172,7 +474,7 @@ function RimEdgeComponent({ id, data, style, markerEnd }) {
172
474
  children: /* @__PURE__ */ jsx("title", { children: `Grants ${data.role}` })
173
475
  }) : null] });
174
476
  }
175
- var edgeTypes = { rim: RimEdgeComponent };
477
+ var edgeTypes = { rim: memo(RimEdgeComponent) };
176
478
  var nodeTypes = { page: memo(PageNodeComponent) };
177
479
  /** The box around laid-out circles, in graph coordinates; `null` when there are none. */
178
480
  function graphBounds(nodes) {
@@ -196,7 +498,7 @@ function graphBounds(nodes) {
196
498
  }
197
499
  var LEGEND_SWATCH = "knowledge-circle inline-block size-3 shrink-0 rounded-full border-2";
198
500
  /** What the circles, lines and badge mean. */
199
- function GraphLegend({ grants }) {
501
+ function GraphLegend({ drawn, counts, dots, linkEnds, focused, levels, branches }) {
200
502
  return /* @__PURE__ */ jsxs("ul", {
201
503
  "aria-label": "Legend",
202
504
  className: "pointer-events-none absolute top-2 left-2 z-10 flex flex-col gap-1 rounded-md border border-border bg-card/90 px-2 py-1.5 shadow-raised",
@@ -214,6 +516,32 @@ function GraphLegend({ grants }) {
214
516
  children: "Scope"
215
517
  })]
216
518
  }),
519
+ levels.includes("scope") ? /* @__PURE__ */ jsxs("li", {
520
+ className: "flex items-center gap-2",
521
+ "data-testid": "knowledge-legend-level-scope",
522
+ children: [/* @__PURE__ */ jsx(Layers, {
523
+ "aria-hidden": true,
524
+ className: "size-3 shrink-0 text-muted-foreground"
525
+ }), /* @__PURE__ */ jsx(Txt, {
526
+ as: "span",
527
+ variant: "meta",
528
+ tone: "muted",
529
+ children: "Scope, above its pages"
530
+ })]
531
+ }) : null,
532
+ levels.includes("ancestor") ? /* @__PURE__ */ jsxs("li", {
533
+ className: "flex items-center gap-2",
534
+ "data-testid": "knowledge-legend-level-ancestor",
535
+ children: [/* @__PURE__ */ jsx(ArrowUpToLine, {
536
+ "aria-hidden": true,
537
+ className: "size-3 shrink-0 text-muted-foreground"
538
+ }), /* @__PURE__ */ jsx(Txt, {
539
+ as: "span",
540
+ variant: "meta",
541
+ tone: "muted",
542
+ children: "Page above the focus"
543
+ })]
544
+ }) : null,
217
545
  /* @__PURE__ */ jsxs("li", {
218
546
  className: "flex items-center gap-2",
219
547
  children: [/* @__PURE__ */ jsx("span", {
@@ -227,8 +555,122 @@ function GraphLegend({ grants }) {
227
555
  children: "Page"
228
556
  })]
229
557
  }),
230
- /* @__PURE__ */ jsxs("li", {
558
+ focused ? /* @__PURE__ */ jsxs("li", {
559
+ className: "flex items-center gap-2",
560
+ "data-testid": "knowledge-legend-focused",
561
+ children: [/* @__PURE__ */ jsx("span", {
562
+ className: LEGEND_SWATCH,
563
+ style: {
564
+ ...PAGE_STYLE,
565
+ outline: "2px solid var(--knowledge-color)",
566
+ outlineOffset: 2,
567
+ borderWidth: 3
568
+ },
569
+ "aria-hidden": true
570
+ }), /* @__PURE__ */ jsx(Txt, {
571
+ as: "span",
572
+ variant: "meta",
573
+ tone: "muted",
574
+ children: "Focused page"
575
+ })]
576
+ }) : null,
577
+ dots || linkEnds ? /* @__PURE__ */ jsxs("li", {
578
+ className: "flex items-center gap-2",
579
+ "data-testid": "knowledge-legend-dot",
580
+ children: [/* @__PURE__ */ jsx("span", {
581
+ className: "inline-block size-2 shrink-0 rounded-full bg-muted-foreground",
582
+ "aria-hidden": true
583
+ }), /* @__PURE__ */ jsx(Txt, {
584
+ as: "span",
585
+ variant: "meta",
586
+ tone: "muted",
587
+ children: "Page in another branch"
588
+ })]
589
+ }) : null,
590
+ branches.slice(0, LEGEND_BRANCH_CAP).map((branch) => /* @__PURE__ */ jsxs("li", {
591
+ className: "flex items-center gap-2 pl-0.5",
592
+ "data-testid": "knowledge-legend-branch",
593
+ children: [/* @__PURE__ */ jsx("span", {
594
+ className: "inline-block size-2 shrink-0 rounded-full",
595
+ style: { background: branch.color },
596
+ "aria-hidden": true
597
+ }), /* @__PURE__ */ jsx(Txt, {
598
+ as: "span",
599
+ variant: "meta",
600
+ tone: "muted",
601
+ className: "max-w-40 truncate",
602
+ title: branch.name,
603
+ children: branches.length > 1 ? `Under ${branch.name}` : branch.scope ? "Under parent scope" : "Under parent page"
604
+ })]
605
+ }, branch.id)),
606
+ branches.length > LEGEND_BRANCH_CAP ? /* @__PURE__ */ jsx("li", {
607
+ className: "pl-0.5",
608
+ children: /* @__PURE__ */ jsxs(Txt, {
609
+ as: "span",
610
+ variant: "meta",
611
+ tone: "muted",
612
+ children: [
613
+ "+",
614
+ branches.length - LEGEND_BRANCH_CAP,
615
+ " more branches"
616
+ ]
617
+ })
618
+ }) : null,
619
+ drawn.has("out") || drawn.has("in") ? /* @__PURE__ */ jsxs(Fragment$1, { children: [drawn.has("out") ? /* @__PURE__ */ jsxs("li", {
231
620
  className: "flex items-center gap-2",
621
+ "data-testid": "knowledge-legend-out",
622
+ children: [/* @__PURE__ */ jsxs("svg", {
623
+ width: "16",
624
+ height: "8",
625
+ "aria-hidden": true,
626
+ className: "shrink-0",
627
+ children: [/* @__PURE__ */ jsx("line", {
628
+ x1: "0",
629
+ y1: "4",
630
+ x2: "12",
631
+ y2: "4",
632
+ stroke: LINK_EDGE_COLOR,
633
+ strokeWidth: "1.5"
634
+ }), /* @__PURE__ */ jsx("polygon", {
635
+ points: "11,1 16,4 11,7",
636
+ fill: LINK_EDGE_COLOR
637
+ })]
638
+ }), /* @__PURE__ */ jsx(Txt, {
639
+ as: "span",
640
+ variant: "meta",
641
+ tone: "muted",
642
+ children: "Links out of the selected page"
643
+ })]
644
+ }) : null, drawn.has("in") ? /* @__PURE__ */ jsxs("li", {
645
+ className: "flex items-center gap-2",
646
+ "data-testid": "knowledge-legend-in",
647
+ children: [/* @__PURE__ */ jsxs("svg", {
648
+ width: "16",
649
+ height: "8",
650
+ "aria-hidden": true,
651
+ className: "shrink-0",
652
+ children: [/* @__PURE__ */ jsx("line", {
653
+ x1: "0",
654
+ y1: "4",
655
+ x2: "12",
656
+ y2: "4",
657
+ stroke: "var(--chart-orange)",
658
+ strokeWidth: "1.5",
659
+ strokeDasharray: "3 2"
660
+ }), /* @__PURE__ */ jsx("polygon", {
661
+ points: "11,1 16,4 11,7",
662
+ fill: "var(--chart-orange)"
663
+ })]
664
+ }), /* @__PURE__ */ jsx(Txt, {
665
+ as: "span",
666
+ variant: "meta",
667
+ tone: "muted",
668
+ children: "Links into it"
669
+ })]
670
+ }) : null] }) : null,
671
+ drawn.has("mention") ? /* @__PURE__ */ jsxs("li", {
672
+ className: "flex items-center gap-2",
673
+ "data-testid": "knowledge-legend-mention",
232
674
  children: [/* @__PURE__ */ jsx("svg", {
233
675
  width: "12",
234
676
  height: "4",
@@ -248,8 +690,81 @@ function GraphLegend({ grants }) {
248
690
  tone: "muted",
249
691
  children: "Mentioned in notes"
250
692
  })]
251
- }),
252
- /* @__PURE__ */ jsxs("li", {
693
+ }) : null,
694
+ drawn.has("link") ? /* @__PURE__ */ jsxs("li", {
695
+ className: "flex items-center gap-2",
696
+ children: [/* @__PURE__ */ jsxs("svg", {
697
+ width: "16",
698
+ height: "8",
699
+ "aria-hidden": true,
700
+ className: "shrink-0",
701
+ "data-testid": "knowledge-legend-link",
702
+ children: [/* @__PURE__ */ jsx("line", {
703
+ x1: "0",
704
+ y1: "4",
705
+ x2: "12",
706
+ y2: "4",
707
+ ...LINK_EDGE_STYLE
708
+ }), /* @__PURE__ */ jsx("polygon", {
709
+ points: "11,1 16,4 11,7",
710
+ fill: LINK_EDGE_COLOR
711
+ })]
712
+ }), /* @__PURE__ */ jsx(Txt, {
713
+ as: "span",
714
+ variant: "meta",
715
+ tone: "muted",
716
+ children: "Linked in page body"
717
+ })]
718
+ }) : null,
719
+ drawn.has("dot-parent") ? /* @__PURE__ */ jsxs("li", {
720
+ className: "flex items-center gap-2",
721
+ "data-testid": "knowledge-legend-dot-parent",
722
+ children: [/* @__PURE__ */ jsx("svg", {
723
+ width: "16",
724
+ height: "4",
725
+ "aria-hidden": true,
726
+ className: "shrink-0",
727
+ children: /* @__PURE__ */ jsx("line", {
728
+ x1: "0",
729
+ y1: "2",
730
+ x2: "16",
731
+ y2: "2",
732
+ ...DOT_PARENT_EDGE_STYLE,
733
+ strokeOpacity: .5
734
+ })
735
+ }), /* @__PURE__ */ jsx(Txt, {
736
+ as: "span",
737
+ variant: "meta",
738
+ tone: "muted",
739
+ children: "Child page (dot)"
740
+ })]
741
+ }) : null,
742
+ drawn.has("up") ? /* @__PURE__ */ jsxs("li", {
743
+ className: "flex items-center gap-2",
744
+ "data-testid": "knowledge-legend-up",
745
+ children: [/* @__PURE__ */ jsxs("svg", {
746
+ width: "16",
747
+ height: "8",
748
+ "aria-hidden": true,
749
+ className: "shrink-0",
750
+ children: [/* @__PURE__ */ jsx("line", {
751
+ x1: "0",
752
+ y1: "4",
753
+ x2: "12",
754
+ y2: "4",
755
+ ...UP_EDGE_STYLE
756
+ }), /* @__PURE__ */ jsx("polygon", {
757
+ points: "9,1 15,4 9,7",
758
+ fill: UP_EDGE_COLOR
759
+ })]
760
+ }), /* @__PURE__ */ jsx(Txt, {
761
+ as: "span",
762
+ variant: "meta",
763
+ tone: "muted",
764
+ children: "Parent of the focused page"
765
+ })]
766
+ }) : null,
767
+ drawn.has("parent") ? /* @__PURE__ */ jsxs("li", {
253
768
  className: "flex items-center gap-2",
254
769
  children: [/* @__PURE__ */ jsxs("svg", {
255
770
  width: "16",
@@ -278,8 +793,32 @@ function GraphLegend({ grants }) {
278
793
  tone: "muted",
279
794
  children: "Parent → child"
280
795
  })]
281
- }),
282
- grants ? /* @__PURE__ */ jsxs("li", {
796
+ }) : null,
797
+ counts ? /* @__PURE__ */ jsxs("li", {
798
+ className: "flex items-center gap-2",
799
+ "data-testid": "knowledge-legend-link-counts",
800
+ children: [
801
+ /* @__PURE__ */ jsx(Txt, {
802
+ as: "span",
803
+ variant: "meta",
804
+ style: { color: "var(--chart-orange)" },
805
+ children: "→4"
806
+ }),
807
+ /* @__PURE__ */ jsx(Txt, {
808
+ as: "span",
809
+ variant: "meta",
810
+ style: { color: LINK_EDGE_COLOR },
811
+ children: "14→"
812
+ }),
813
+ /* @__PURE__ */ jsx(Txt, {
814
+ as: "span",
815
+ variant: "meta",
816
+ tone: "muted",
817
+ children: "Links in, links out (not drawn)"
818
+ })
819
+ ]
820
+ }) : null,
821
+ drawn.has("grant") ? /* @__PURE__ */ jsxs("li", {
283
822
  className: "flex items-center gap-2",
284
823
  children: [/* @__PURE__ */ jsxs("svg", {
285
824
  width: "16",
@@ -324,14 +863,82 @@ function GraphLegend({ grants }) {
324
863
  }
325
864
  /** How far the pointer may move between press and release and still count as a click, not a pan. */
326
865
  var CLICK_SLOP_PX = 4;
866
+ /** Marks the nodes and lines that stay lit while the rest of the graph fades (see knowledge.css). */
867
+ var NEIGHBOUR_CLASS = "knowledge-neighbour";
868
+ /**
869
+ * The same edge objects while their content is unchanged. Pointing at a page changes the style of the
870
+ * few lines touching it; handing React Flow fresh objects for every line would redraw them all.
871
+ */
872
+ function useStableEdges(edges) {
873
+ const seen = useRef(/* @__PURE__ */ new Map());
874
+ return useMemo(() => {
875
+ const next = /* @__PURE__ */ new Map();
876
+ const result = edges.map((edge) => {
877
+ const sig = JSON.stringify(edge);
878
+ const before = seen.current.get(edge.id);
879
+ const entry = before && before.sig === sig ? before : {
880
+ sig,
881
+ edge
882
+ };
883
+ next.set(edge.id, entry);
884
+ return entry.edge;
885
+ });
886
+ seen.current = next;
887
+ return result;
888
+ }, [edges]);
889
+ }
890
+ /** The focused page's circle is this much larger than its neighbours'. */
891
+ var FOCUSED_SCALE = 1.2;
892
+ /** Pages in other branches that the focus links to draw a little smaller than its own neighbours. */
893
+ var LINK_END_SCALE = .8;
894
+ /** Most branch colours the legend names; the rest are counted. */
895
+ var LEGEND_BRANCH_CAP = 4;
896
+ /** Most links a hovered page draws that aren't drawn already. */
897
+ var HOVER_LINK_CAP = 40;
327
898
  /**
328
899
  * The schema-4 Knowledge page graph: scopes and pages as circles sized by how often they are mentioned,
329
900
  * parent links as faint edges and mentions as solid ones. The first layout is deterministic for the same pages; later updates start from where pages already sit.
330
901
  */
331
902
  function KnowledgePageGraph(props) {
332
- return /* @__PURE__ */ jsx(ReactFlowProvider, { children: /* @__PURE__ */ jsx(KnowledgePageGraphInner, { ...props }) });
903
+ const { settled = true } = props;
904
+ const held = useRef(props);
905
+ const [released, setReleased] = useState(false);
906
+ const holding = !settled && !released;
907
+ if (!holding) held.current = props;
908
+ const shown = !holding ? props : {
909
+ ...props,
910
+ pages: held.current.pages,
911
+ edges: held.current.edges,
912
+ pinnedIds: held.current.pinnedIds,
913
+ grants: held.current.grants,
914
+ selectedPageId: held.current.selectedPageId,
915
+ centerId: held.current.centerId,
916
+ hiddenLinks: held.current.hiddenLinks,
917
+ knownLinks: held.current.knownLinks,
918
+ nearIds: held.current.nearIds
919
+ };
920
+ const [slow, setSlow] = useState(false);
921
+ useEffect(() => {
922
+ if (settled) {
923
+ setSlow(false);
924
+ setReleased(false);
925
+ return;
926
+ }
927
+ const hint = setTimeout(() => setSlow(true), LOADING_HINT_DELAY_MS);
928
+ const deadline = setTimeout(() => setReleased(true), LOADING_RELEASE_MS);
929
+ return () => {
930
+ clearTimeout(hint);
931
+ clearTimeout(deadline);
932
+ };
933
+ }, [settled, props.selectedPageId]);
934
+ return /* @__PURE__ */ jsxs(ReactFlowProvider, { children: [/* @__PURE__ */ jsx(KnowledgePageGraphInner, { ...shown }), slow && holding ? /* @__PURE__ */ jsx("p", {
935
+ role: "status",
936
+ "data-testid": "knowledge-graph-loading",
937
+ className: "text-ui-sm pointer-events-none absolute bottom-3 left-1/2 z-10 -translate-x-1/2 rounded border border-border bg-card px-3 py-1 text-muted-foreground shadow-overlay",
938
+ children: "Loading pages…"
939
+ }) : null] });
333
940
  }
334
- function KnowledgePageGraphInner({ pages, edges, pinnedIds = [], grants, selectedPageId, onSelect, onOpen, onClearSelection, onMentionSelect }) {
941
+ function KnowledgePageGraphInner({ pages, edges, pinnedIds = [], grants, selectedPageId, centerId, hiddenLinks, knownLinks, nearIds, exactLinkCounts, fetchedLinks, onHover, showCounts, onPreview, onSelect, onOpen, onClearSelection, onMentionSelect }) {
335
942
  const { graphPages, allEdges } = useMemo(() => {
336
943
  const granted = grantGraph(pages, grants);
337
944
  return {
@@ -343,28 +950,167 @@ function KnowledgePageGraphInner({ pages, edges, pinnedIds = [], grants, selecte
343
950
  edges,
344
951
  grants
345
952
  ]);
346
- const lastLayout = useRef(void 0);
953
+ const linkSource = knownLinks ?? edges;
954
+ const linkCounts = useMemo(() => {
955
+ const counts = /* @__PURE__ */ new Map();
956
+ const pairs = /* @__PURE__ */ new Set();
957
+ for (const edge of linkSource) {
958
+ if (edge.kind !== "mention" && edge.kind !== "link") continue;
959
+ if (edge.source === edge.target || pairs.has(`${edge.source}\u0000${edge.target}`)) continue;
960
+ pairs.add(`${edge.source}\u0000${edge.target}`);
961
+ const out = counts.get(edge.source) ?? {
962
+ in: 0,
963
+ out: 0
964
+ };
965
+ out.out++;
966
+ counts.set(edge.source, out);
967
+ const into = counts.get(edge.target) ?? {
968
+ in: 0,
969
+ out: 0
970
+ };
971
+ into.in++;
972
+ counts.set(edge.target, into);
973
+ }
974
+ for (const [id, exact] of exactLinkCounts ?? []) counts.set(id, exact);
975
+ return counts;
976
+ }, [linkSource, exactLinkCounts]);
977
+ const hoverLinks = useMemo(() => {
978
+ const touching = /* @__PURE__ */ new Map();
979
+ const add = (id, edge) => {
980
+ const list = touching.get(id);
981
+ if (list) list.push(edge);
982
+ else touching.set(id, [edge]);
983
+ };
984
+ for (const edge of [...linkSource, ...fetchedLinks ?? []]) {
985
+ if (edge.kind !== "mention" && edge.kind !== "link") continue;
986
+ add(edge.source, edge);
987
+ if (edge.target !== edge.source) add(edge.target, edge);
988
+ }
989
+ return touching;
990
+ }, [linkSource, fetchedLinks]);
347
991
  const laidOut = useMemo(() => {
992
+ const shape = graphShape(graphPages, selectedPageId ?? centerId ?? null, nearIds);
993
+ const colors = branchColors(graphPages, shape);
348
994
  const flow = toPageFlow(graphPages, allEdges);
349
- const layout = layoutPageGraph(lastLayout.current, flow.nodes.map((node) => ({
995
+ for (const node of flow.nodes) if (shape.form.get(node.id) === "dot") {
996
+ const links = linkCounts.get(node.id);
997
+ const counted = showCounts && !isChildPlaceholder(node.data.page) && !!links && links.in + links.out > 0;
998
+ node.data.size = counted ? 28 : 20;
999
+ } else if (node.id === shape.center) node.data.size = Math.round(node.data.size * FOCUSED_SCALE);
1000
+ else node.data.size = Math.round(108 * (shape.linked.has(node.id) ? LINK_END_SCALE : 1));
1001
+ const ring = flow.nodes.filter((node) => shape.depth.get(node.id) === 1 && shape.form.get(node.id) === "full");
1002
+ const crowd = crowdScale(ring.length);
1003
+ if (crowd < 1) for (const node of ring) node.data.size = Math.round(node.data.size * crowd);
1004
+ const sizes = new Map(flow.nodes.map((node) => [node.id, node.data.size]));
1005
+ const radial = radialLayout(graphPages, shape, (page) => sizes.get(page.id) ?? 20, nearIds);
1006
+ const positions = runLayout(flow.nodes.map((node) => ({
350
1007
  id: node.id,
351
- size: node.data.size
352
- })), flow.edges.map((edge) => ({
353
- source: edge.source,
354
- target: edge.target
355
- })));
356
- lastLayout.current = layout;
1008
+ size: node.data.size,
1009
+ padding: shape.form.get(node.id) === "dot" ? 4 : 24,
1010
+ initial: radial.get(node.id)
1011
+ })), [], { ticks: 0 });
357
1012
  return {
358
1013
  nodes: flow.nodes,
359
1014
  edges: flow.edges,
360
- positions: layout.positions
1015
+ positions,
1016
+ shape,
1017
+ colors
361
1018
  };
362
- }, [graphPages, allEdges]);
363
- const pinned = useMemo(() => new Set(pinnedIds), [pinnedIds]);
364
- const neighbours = useMemo(() => selectedPageId && laidOut.positions.has(selectedPageId) ? pageNeighbourIds(selectedPageId, allEdges) : null, [
1019
+ }, [
1020
+ graphPages,
1021
+ allEdges,
365
1022
  selectedPageId,
1023
+ centerId,
1024
+ linkCounts,
1025
+ showCounts,
1026
+ nearIds
1027
+ ]);
1028
+ const pageById = useMemo(() => new Map(graphPages.map((page) => [page.id, page])), [graphPages]);
1029
+ const focusParentId = useMemo(() => {
1030
+ const parentId = laidOut.shape.center ? pageById.get(laidOut.shape.center)?.parentId : void 0;
1031
+ return parentId && laidOut.positions.has(parentId) ? parentId : null;
1032
+ }, [laidOut, pageById]);
1033
+ const drawnLevels = useMemo(() => {
1034
+ const kinds = /* @__PURE__ */ new Set();
1035
+ for (const [id, level] of laidOut.shape.level) if (laidOut.shape.form.get(id) === "full") kinds.add(level);
1036
+ return [...kinds];
1037
+ }, [laidOut]);
1038
+ const dotBranches = useMemo(() => {
1039
+ const seen = /* @__PURE__ */ new Map();
1040
+ for (const node of laidOut.nodes) {
1041
+ if (laidOut.shape.form.get(node.id) !== "dot" || isChildPlaceholder(node.data.page)) continue;
1042
+ const branch = laidOut.shape.branch.get(node.id);
1043
+ const color = branch ? laidOut.colors.get(branch) : void 0;
1044
+ const owner = branch ? pageById.get(branch) : void 0;
1045
+ if (branch && color && owner && !seen.has(branch)) seen.set(branch, {
1046
+ id: branch,
1047
+ name: owner.displayLabel ?? owner.name,
1048
+ color,
1049
+ scope: !!owner.isScope
1050
+ });
1051
+ }
1052
+ return [...seen.values()];
1053
+ }, [laidOut, pageById]);
1054
+ const pinned = useMemo(() => new Set(pinnedIds), [pinnedIds]);
1055
+ const childrenUndrawn = useMemo(() => {
1056
+ const drawn = /* @__PURE__ */ new Map();
1057
+ for (const page of graphPages) if (page.parentId && !isChildPlaceholder(page) && !isRestCircle(page)) drawn.set(page.parentId, (drawn.get(page.parentId) ?? 0) + 1);
1058
+ return new Set(graphPages.filter((page) => !isScopeGroup(page) && !isRestCircle(page) && (page.childCountCapped || (page.childCount ?? 0) > (drawn.get(page.id) ?? 0))).map((page) => page.id));
1059
+ }, [graphPages]);
1060
+ const [hoveredId, setHoveredId] = useState(null);
1061
+ useEffect(() => onHover?.(hoveredId), [hoveredId, onHover]);
1062
+ const hoverExtra = useMemo(() => {
1063
+ if (!hoveredId) return [];
1064
+ const drawnIds = new Set(laidOut.nodes.map((node) => node.id));
1065
+ const have = new Set(laidOut.edges.map((edge) => edge.id));
1066
+ const extra = [];
1067
+ for (const edge of hoverLinks.get(hoveredId) ?? []) {
1068
+ const id = `${edge.kind}:${edge.source}->${edge.target}`;
1069
+ if (have.has(id) || !drawnIds.has(edge.source) || !drawnIds.has(edge.target)) continue;
1070
+ have.add(id);
1071
+ extra.push({
1072
+ id,
1073
+ source: edge.source,
1074
+ target: edge.target,
1075
+ data: {
1076
+ kind: edge.kind,
1077
+ count: edge.count ?? 1
1078
+ }
1079
+ });
1080
+ if (extra.length >= HOVER_LINK_CAP) break;
1081
+ }
1082
+ return extra;
1083
+ }, [
1084
+ hoveredId,
1085
+ hoverLinks,
1086
+ laidOut
1087
+ ]);
1088
+ const hoveredNotShown = useMemo(() => {
1089
+ const total = hoveredId ? linkCounts.get(hoveredId) : void 0;
1090
+ if (!hoveredId || !total) return 0;
1091
+ const pairs = /* @__PURE__ */ new Set();
1092
+ for (const edge of [...laidOut.edges, ...hoverExtra]) {
1093
+ const kind = edge.data?.kind;
1094
+ if (kind !== "mention" && kind !== "link" || edge.source !== hoveredId && edge.target !== hoveredId) continue;
1095
+ pairs.add(`${edge.source}\u0000${edge.target}`);
1096
+ }
1097
+ return Math.max(0, total.in + total.out - pairs.size);
1098
+ }, [
1099
+ hoveredId,
1100
+ linkCounts,
366
1101
  laidOut,
367
- allEdges
1102
+ hoverExtra
1103
+ ]);
1104
+ const emphasisId = hoveredId && laidOut.positions.has(hoveredId) ? hoveredId : selectedPageId ?? null;
1105
+ const neighbours = useMemo(() => emphasisId && laidOut.positions.has(emphasisId) ? pageNeighbourIds(emphasisId, [...allEdges, ...hoverExtra.map((edge) => ({
1106
+ kind: edge.data?.kind ?? "link",
1107
+ source: edge.source,
1108
+ target: edge.target
1109
+ }))]) : null, [
1110
+ emphasisId,
1111
+ laidOut,
1112
+ allEdges,
1113
+ hoverExtra
368
1114
  ]);
369
1115
  const nodes = useMemo(() => laidOut.nodes.map((node) => {
370
1116
  const center = laidOut.positions.get(node.id) ?? {
@@ -398,16 +1144,32 @@ function KnowledgePageGraphInner({ pages, edges, pinnedIds = [], grants, selecte
398
1144
  ...node.data,
399
1145
  selected: node.id === selectedPageId,
400
1146
  pinned: pinned.has(node.id),
401
- dimmed: !!neighbours && !neighbours.has(node.id)
1147
+ form: laidOut.shape.form.get(node.id) ?? "full",
1148
+ color: laidOut.colors.get(laidOut.shape.branch.get(node.id) ?? ""),
1149
+ badge: childrenUndrawn.has(node.id) ? childCountLabel(node.data.page) : void 0,
1150
+ path: pagePath(node.data.page, pageById),
1151
+ hiddenLinks: laidOut.shape.form.get(node.id) === "full" ? hiddenLinks?.get(node.id) : void 0,
1152
+ ref: pageRef(node.data.page, pageById),
1153
+ links: linkCounts.get(node.id),
1154
+ showCount: showCounts,
1155
+ level: laidOut.shape.form.get(node.id) === "full" ? laidOut.shape.level.get(node.id) : void 0,
1156
+ isParent: node.id === focusParentId,
1157
+ far: laidOut.shape.linked.has(node.id) || void 0
402
1158
  }
403
1159
  };
404
1160
  }), [
405
1161
  laidOut,
406
1162
  selectedPageId,
407
1163
  pinned,
408
- neighbours
1164
+ childrenUndrawn,
1165
+ pageById,
1166
+ hiddenLinks,
1167
+ linkCounts,
1168
+ showCounts,
1169
+ focusParentId
409
1170
  ]);
410
- const flowEdges = useMemo(() => {
1171
+ const { flowEdges: builtEdges, drawnEdgeStyles } = useMemo(() => {
1172
+ const drawn = /* @__PURE__ */ new Set();
411
1173
  const sizes = new Map(laidOut.nodes.map((node) => [node.id, node.data.size]));
412
1174
  const circle = (id) => {
413
1175
  return {
@@ -418,71 +1180,168 @@ function KnowledgePageGraphInner({ pages, edges, pinnedIds = [], grants, selecte
418
1180
  r: (sizes.get(id) ?? 0) / 2
419
1181
  };
420
1182
  };
421
- return laidOut.edges.map((edge) => {
422
- const mention = edge.data?.kind === "mention";
423
- const grant = edge.data?.kind === "grant";
424
- const dimmed = !!neighbours && !(neighbours.has(edge.source) && neighbours.has(edge.target));
425
- return {
426
- ...edge,
427
- type: "rim",
428
- data: {
429
- kind: edge.data?.kind ?? "parent",
430
- count: edge.data?.count ?? 1,
431
- ...edge.data?.role ? { role: edge.data.role } : {},
432
- from: circle(edge.source),
433
- to: circle(edge.target),
434
- dimmed
435
- },
436
- ...mention && onMentionSelect ? { className: "cursor-pointer" } : {},
437
- style: mention ? {
438
- stroke: MENTION_EDGE_COLOR,
439
- strokeWidth: Math.min(1.5 + (edge.data?.count ?? 1) * .5, 4)
440
- } : grant ? GRANT_EDGE_STYLE : PARENT_EDGE_STYLE,
441
- markerEnd: mention ? {
442
- type: MarkerType.ArrowClosed,
443
- color: MENTION_EDGE_COLOR,
444
- width: 14,
445
- height: 14
446
- } : grant ? GRANT_ARROW : PARENT_ARROW
447
- };
448
- });
1183
+ return {
1184
+ flowEdges: [...laidOut.edges, ...hoverExtra].map((edge) => {
1185
+ const mention = edge.data?.kind === "mention";
1186
+ const grant = edge.data?.kind === "grant";
1187
+ const link = edge.data?.kind === "link";
1188
+ const toDot = laidOut.shape.form.get(edge.target) === "dot";
1189
+ const direction = (mention || link) && emphasisId ? edge.source === emphasisId ? "out" : edge.target === emphasisId ? "in" : void 0 : void 0;
1190
+ const up = !mention && !grant && !link && edge.source === focusParentId && edge.target === laidOut.shape.center;
1191
+ drawn.add(direction ?? (up ? "up" : mention ? "mention" : grant ? "grant" : link ? "link" : toDot ? "dot-parent" : "parent"));
1192
+ return {
1193
+ ...edge,
1194
+ type: "rim",
1195
+ data: {
1196
+ kind: edge.data?.kind ?? "parent",
1197
+ count: edge.data?.count ?? 1,
1198
+ ...edge.data?.role ? { role: edge.data.role } : {},
1199
+ from: circle(edge.source),
1200
+ to: circle(edge.target)
1201
+ },
1202
+ ...direction === "in" ? { zIndex: 1 } : {},
1203
+ ...mention && onMentionSelect ? { className: "cursor-pointer" } : {},
1204
+ style: direction ? {
1205
+ ...DIRECTION_EDGE_STYLE[direction],
1206
+ strokeWidth: mention ? 2 : 1.5
1207
+ } : mention ? {
1208
+ stroke: MENTION_EDGE_COLOR,
1209
+ strokeWidth: Math.min(1.5 + (edge.data?.count ?? 1) * .5, 4)
1210
+ } : up ? UP_EDGE_STYLE : grant ? GRANT_EDGE_STYLE : link ? LINK_EDGE_STYLE : toDot ? DOT_PARENT_EDGE_STYLE : PARENT_EDGE_STYLE,
1211
+ markerEnd: direction ? DIRECTION_ARROW[direction] : up ? UP_ARROW : mention ? {
1212
+ type: MarkerType.ArrowClosed,
1213
+ color: MENTION_EDGE_COLOR,
1214
+ width: 14,
1215
+ height: 14
1216
+ } : grant ? GRANT_ARROW : link ? LINK_ARROW : toDot ? void 0 : PARENT_ARROW
1217
+ };
1218
+ }),
1219
+ drawnEdgeStyles: drawn
1220
+ };
449
1221
  }, [
450
1222
  laidOut,
1223
+ hoverExtra,
451
1224
  onMentionSelect,
452
- neighbours
1225
+ emphasisId,
1226
+ focusParentId
1227
+ ]);
1228
+ const flowEdges = useStableEdges(builtEdges);
1229
+ const emphasisedNodes = useMemo(() => nodes.map((node) => {
1230
+ const near = neighbours?.has(node.id);
1231
+ const hovered = node.id === hoveredId && hoveredNotShown > 0;
1232
+ if (!near && !hovered) return node;
1233
+ return {
1234
+ ...node,
1235
+ ...near ? { className: NEIGHBOUR_CLASS } : {},
1236
+ ...hovered ? { data: {
1237
+ ...node.data,
1238
+ notShown: hoveredNotShown
1239
+ } } : {}
1240
+ };
1241
+ }), [
1242
+ nodes,
1243
+ neighbours,
1244
+ hoveredId,
1245
+ hoveredNotShown
453
1246
  ]);
1247
+ const emphasisedEdges = useMemo(() => neighbours ? flowEdges.map((edge) => neighbours.has(edge.source) && neighbours.has(edge.target) ? {
1248
+ ...edge,
1249
+ className: [edge.className, NEIGHBOUR_CLASS].filter(Boolean).join(" ")
1250
+ } : edge) : flowEdges, [flowEdges, neighbours]);
454
1251
  const pagesById = useMemo(() => new Map(pages.map((page) => [page.id, page])), [pages]);
455
1252
  const reactFlow = useReactFlow();
456
1253
  const containerRef = useRef(null);
457
1254
  const hasNodes = nodes.length > 0;
1255
+ const drawnNodes = useLeavingNodes(emphasisedNodes, prefersMotion());
1256
+ const linkedIds = laidOut.shape.linked;
458
1257
  const fitState = useRef({
459
1258
  nodes,
460
- allEdges,
461
1259
  selectedPageId,
462
- reactFlow
1260
+ centerId,
1261
+ reactFlow,
1262
+ linkedIds
463
1263
  });
464
1264
  fitState.current = {
465
1265
  nodes,
466
- allEdges,
467
1266
  selectedPageId,
468
- reactFlow
1267
+ centerId,
1268
+ reactFlow,
1269
+ linkedIds
469
1270
  };
470
1271
  const fitPending = useRef(false);
471
- const fit = useCallback((focusSelection, duration) => {
1272
+ const paneAndNodes = () => {
472
1273
  const container = containerRef.current;
473
1274
  const width = container?.clientWidth ?? 0;
474
1275
  const height = container?.clientHeight ?? 0;
475
1276
  if (width === 0 || height === 0) {
476
1277
  fitPending.current = true;
477
- return;
1278
+ return null;
478
1279
  }
479
1280
  fitPending.current = false;
480
- const { nodes: current, allEdges: edgesNow, selectedPageId: selected, reactFlow: flow } = fitState.current;
481
- const focus = focusSelection && selected ? pageNeighbourIds(selected, edgesNow) : null;
482
- const bounds = graphBounds(focus ? current.filter((node) => focus.has(node.id)) : current);
483
- if (!bounds) return;
484
- const viewport = getViewportForBounds(bounds, width, height, .1, focus ? 1.5 : 2, focus ? .3 : .1);
485
- flow.setViewport(viewport, duration ? { duration } : void 0);
1281
+ const { nodes: current, reactFlow: flow } = fitState.current;
1282
+ const bounds = graphBounds(current);
1283
+ return bounds ? {
1284
+ container,
1285
+ width,
1286
+ height,
1287
+ current,
1288
+ flow,
1289
+ bounds
1290
+ } : null;
1291
+ };
1292
+ const fit = useCallback((duration) => {
1293
+ const view = paneAndNodes();
1294
+ if (!view) return;
1295
+ const { selectedPageId: selected, centerId: centre, linkedIds: linked } = fitState.current;
1296
+ const anchor = view.current.find((node) => node.id === (selected ?? centre));
1297
+ const ring = !!selected && view.current.some((node) => linked.has(node.id));
1298
+ const viewport = fitViewport({
1299
+ bounds: view.bounds,
1300
+ width: view.width,
1301
+ height: view.height,
1302
+ ring,
1303
+ anchor
1304
+ });
1305
+ view.flow.setViewport(viewport, duration ? { duration } : void 0);
1306
+ }, []);
1307
+ const fitInitial = useCallback(() => {
1308
+ const view = paneAndNodes();
1309
+ if (!view) return;
1310
+ const box = view.container?.getBoundingClientRect();
1311
+ const legend = view.container?.querySelector("[aria-label=\"Legend\"]")?.getBoundingClientRect();
1312
+ const viewport = fitAllViewport({
1313
+ bounds: view.bounds,
1314
+ nodes: view.current.map((node) => ({
1315
+ x: node.position.x,
1316
+ y: node.position.y,
1317
+ width: node.width ?? 0,
1318
+ height: node.height ?? 0
1319
+ })),
1320
+ width: view.width,
1321
+ height: view.height,
1322
+ legend: box && legend ? {
1323
+ right: legend.right - box.left + 8,
1324
+ bottom: legend.bottom - box.top + 8
1325
+ } : void 0
1326
+ });
1327
+ view.flow.setViewport(viewport);
1328
+ }, []);
1329
+ const nudgeToSelection = useCallback((duration) => {
1330
+ const container = containerRef.current;
1331
+ const width = container?.clientWidth ?? 0;
1332
+ const height = container?.clientHeight ?? 0;
1333
+ const { nodes: current, selectedPageId: selected, centerId: centre, reactFlow: flow } = fitState.current;
1334
+ const anchor = current.find((node) => node.id === (selected ?? centre));
1335
+ if (!anchor || width === 0 || height === 0) return;
1336
+ const view = flow.getViewport();
1337
+ const dx = nudgeAxis(anchor.position.x * view.zoom + view.x, (anchor.width ?? 0) * view.zoom, width);
1338
+ const dy = nudgeAxis(anchor.position.y * view.zoom + view.y, (anchor.height ?? 0) * view.zoom, height);
1339
+ if (dx === 0 && dy === 0) return;
1340
+ flow.setViewport({
1341
+ zoom: view.zoom,
1342
+ x: view.x + dx,
1343
+ y: view.y + dy
1344
+ }, duration ? { duration } : void 0);
486
1345
  }, []);
487
1346
  useEffect(() => {
488
1347
  const container = containerRef.current;
@@ -492,10 +1351,12 @@ function KnowledgePageGraphInner({ pages, edges, pinnedIds = [], grants, selecte
492
1351
  height: container.clientHeight
493
1352
  };
494
1353
  let frame;
1354
+ let needsFit = false;
495
1355
  const observer = new ResizeObserver(([entry]) => {
496
1356
  if (!entry) return;
497
1357
  const { width, height } = entry.contentRect;
498
1358
  const changed = width !== last.width || height !== last.height;
1359
+ if (last.width === 0 || last.height === 0) needsFit = true;
499
1360
  last = {
500
1361
  width,
501
1362
  height
@@ -504,7 +1365,10 @@ function KnowledgePageGraphInner({ pages, edges, pinnedIds = [], grants, selecte
504
1365
  if (frame !== void 0) cancelAnimationFrame(frame);
505
1366
  frame = requestAnimationFrame(() => {
506
1367
  frame = void 0;
507
- fit(true);
1368
+ if (needsFit || fitPending.current) {
1369
+ needsFit = false;
1370
+ fitInitial();
1371
+ } else nudgeToSelection();
508
1372
  });
509
1373
  });
510
1374
  observer.observe(container);
@@ -512,33 +1376,51 @@ function KnowledgePageGraphInner({ pages, edges, pinnedIds = [], grants, selecte
512
1376
  observer.disconnect();
513
1377
  if (frame !== void 0) cancelAnimationFrame(frame);
514
1378
  };
515
- }, [hasNodes, fit]);
1379
+ }, [
1380
+ hasNodes,
1381
+ fitInitial,
1382
+ nudgeToSelection
1383
+ ]);
516
1384
  const selectedHere = useRef(null);
517
1385
  const selectedIsLoaded = !!selectedPageId && pagesById.has(selectedPageId);
518
1386
  const initialized = useRef(false);
1387
+ const settleUntil = useRef(0);
1388
+ const settledOn = useRef(null);
1389
+ const fittedLayout = useRef("");
1390
+ const stopSettling = useCallback(() => {
1391
+ settleUntil.current = 0;
1392
+ }, []);
1393
+ useEffect(() => {
1394
+ if (!initialized.current) return;
1395
+ if ((selectedPageId ?? null) !== settledOn.current) settleUntil.current = 0;
1396
+ if (Date.now() >= settleUntil.current) return;
1397
+ const layout = nodes.map((node) => `${node.id}@${node.position.x},${node.position.y}`).join("|");
1398
+ if (layout === fittedLayout.current) return;
1399
+ fittedLayout.current = layout;
1400
+ fitInitial();
1401
+ }, [
1402
+ nodes,
1403
+ selectedPageId,
1404
+ fitInitial
1405
+ ]);
519
1406
  useEffect(() => {
520
1407
  if (!initialized.current || !selectedIsLoaded || !selectedPageId) return;
521
1408
  const fromGraph = selectedHere.current === selectedPageId;
522
1409
  selectedHere.current = null;
523
1410
  if (fromGraph) return;
524
- fit(true, 300);
1411
+ nudgeToSelection(fitDuration(prefersMotion()));
525
1412
  }, [
526
1413
  selectedPageId,
527
1414
  selectedIsLoaded,
528
- fit
1415
+ nudgeToSelection
529
1416
  ]);
530
1417
  const activate = (page) => {
1418
+ if (isChildPlaceholder(page) || isScopeGroup(page) || isRestCircle(page)) return onSelect(page);
531
1419
  if (page.id === selectedPageId && onOpen) return onOpen(page);
532
1420
  selectedHere.current = page.id;
533
1421
  onSelect(page);
534
1422
  };
535
1423
  const pressedAt = useRef(null);
536
- const onPaneClick = (event) => {
537
- const start = pressedAt.current;
538
- pressedAt.current = null;
539
- if (start && Math.hypot(event.clientX - start.x, event.clientY - start.y) > CLICK_SLOP_PX) return;
540
- onClearSelection?.();
541
- };
542
1424
  const onKeyDown = (event) => {
543
1425
  if (event.key !== "Enter" && event.key !== " ") return;
544
1426
  const id = event.target.closest(".react-flow__node")?.dataset.id;
@@ -547,6 +1429,56 @@ function KnowledgePageGraphInner({ pages, edges, pinnedIds = [], grants, selecte
547
1429
  event.preventDefault();
548
1430
  activate(page);
549
1431
  };
1432
+ const latest = useRef({
1433
+ activate,
1434
+ onPreview,
1435
+ onMentionSelect,
1436
+ onClearSelection,
1437
+ laidOut,
1438
+ pagesById
1439
+ });
1440
+ latest.current = {
1441
+ activate,
1442
+ onPreview,
1443
+ onMentionSelect,
1444
+ onClearSelection,
1445
+ laidOut,
1446
+ pagesById
1447
+ };
1448
+ const onNodeMouseEnter = useCallback((_, node) => {
1449
+ setHoveredId(node.id);
1450
+ if (latest.current.laidOut.shape.form.get(node.id) === "dot") latest.current.onPreview?.(node.data.page);
1451
+ }, []);
1452
+ const onNodeMouseLeave = useCallback(() => setHoveredId(null), []);
1453
+ const onNodeClick = useCallback((_, node) => {
1454
+ if (!isPlaceholder(node.data.page)) latest.current.activate(node.data.page);
1455
+ }, []);
1456
+ const onEdgeClick = useCallback((event, edge) => {
1457
+ const { onMentionSelect, pagesById } = latest.current;
1458
+ if (edge.data?.kind !== "mention" || !onMentionSelect) return;
1459
+ const source = pagesById.get(edge.source);
1460
+ const target = pagesById.get(edge.target);
1461
+ if (!source || !target) return;
1462
+ const box = containerRef.current?.getBoundingClientRect();
1463
+ onMentionSelect({
1464
+ source,
1465
+ target,
1466
+ point: {
1467
+ x: event.clientX - (box?.left ?? 0),
1468
+ y: event.clientY - (box?.top ?? 0)
1469
+ },
1470
+ pane: {
1471
+ width: box?.width ?? 0,
1472
+ height: box?.height ?? 0
1473
+ }
1474
+ });
1475
+ }, []);
1476
+ const onPaneClick = useCallback((event) => {
1477
+ const start = pressedAt.current;
1478
+ pressedAt.current = null;
1479
+ if (start && Math.hypot(event.clientX - start.x, event.clientY - start.y) > CLICK_SLOP_PX) return;
1480
+ latest.current.onClearSelection?.();
1481
+ }, []);
550
1482
  if (!hasNodes) return /* @__PURE__ */ jsx("div", {
551
1483
  className: "flex h-full items-center justify-center p-6",
552
1484
  children: /* @__PURE__ */ jsx(Txt, {
@@ -557,51 +1489,47 @@ function KnowledgePageGraphInner({ pages, edges, pinnedIds = [], grants, selecte
557
1489
  });
558
1490
  return /* @__PURE__ */ jsxs("div", {
559
1491
  ref: containerRef,
560
- className: "relative size-full",
1492
+ className: "knowledge-graph relative size-full",
1493
+ "data-emphasis": neighbours ? "on" : void 0,
561
1494
  "data-testid": "knowledge-page-graph",
562
1495
  onKeyDown,
1496
+ onWheelCapture: stopSettling,
563
1497
  onPointerDownCapture: (event) => {
1498
+ stopSettling();
564
1499
  pressedAt.current = {
565
1500
  x: event.clientX,
566
1501
  y: event.clientY
567
1502
  };
568
1503
  },
569
- children: [/* @__PURE__ */ jsx(GraphLegend, { grants: allEdges.some((edge) => edge.kind === "grant") }), /* @__PURE__ */ jsxs(ReactFlow, {
570
- nodes,
571
- edges: flowEdges,
1504
+ children: [/* @__PURE__ */ jsx(GraphLegend, {
1505
+ branches: dotBranches,
1506
+ levels: drawnLevels,
1507
+ focused: !!selectedPageId && laidOut.positions.has(selectedPageId),
1508
+ dots: [...laidOut.shape.form.values()].includes("dot"),
1509
+ linkEnds: laidOut.shape.linked.size > 0,
1510
+ drawn: drawnEdgeStyles,
1511
+ counts: [...hiddenLinks ?? []].some(([id, hidden]) => laidOut.shape.form.get(id) === "full" && (hidden.in > 0 || hidden.out > 0))
1512
+ }), /* @__PURE__ */ jsxs(ReactFlow, {
1513
+ nodes: drawnNodes,
1514
+ edges: emphasisedEdges,
572
1515
  nodeTypes,
573
1516
  edgeTypes,
574
1517
  onInit: () => {
575
1518
  initialized.current = true;
576
- fit(true);
1519
+ settleUntil.current = Date.now() + INITIAL_SETTLE_MS;
1520
+ settledOn.current = fitState.current.selectedPageId ?? null;
1521
+ fittedLayout.current = fitState.current.nodes.map((node) => `${node.id}@${node.position.x},${node.position.y}`).join("|");
1522
+ fitInitial();
577
1523
  },
578
1524
  minZoom: .1,
579
1525
  proOptions: { hideAttribution: true },
580
1526
  nodesConnectable: false,
581
1527
  nodesDraggable: false,
582
1528
  onPaneClick,
583
- onNodeClick: (_, node) => {
584
- if (!isPlaceholder(node.data.page)) activate(node.data.page);
585
- },
586
- onEdgeClick: (event, edge) => {
587
- if (edge.data?.kind !== "mention" || !onMentionSelect) return;
588
- const source = pagesById.get(edge.source);
589
- const target = pagesById.get(edge.target);
590
- if (!source || !target) return;
591
- const box = containerRef.current?.getBoundingClientRect();
592
- onMentionSelect({
593
- source,
594
- target,
595
- point: {
596
- x: event.clientX - (box?.left ?? 0),
597
- y: event.clientY - (box?.top ?? 0)
598
- },
599
- pane: {
600
- width: box?.width ?? 0,
601
- height: box?.height ?? 0
602
- }
603
- });
604
- },
1529
+ onNodeMouseEnter,
1530
+ onNodeMouseLeave,
1531
+ onNodeClick,
1532
+ onEdgeClick,
605
1533
  children: [/* @__PURE__ */ jsx(Background, {
606
1534
  variant: BackgroundVariant.Dots,
607
1535
  gap: 26,
@@ -611,7 +1539,7 @@ function KnowledgePageGraphInner({ pages, edges, pinnedIds = [], grants, selecte
611
1539
  showInteractive: false,
612
1540
  showFitView: false,
613
1541
  children: /* @__PURE__ */ jsx(ControlButton, {
614
- onClick: () => fit(false, 300),
1542
+ onClick: () => fit(fitDuration(prefersMotion())),
615
1543
  title: "Fit view",
616
1544
  "aria-label": "Fit view",
617
1545
  children: /* @__PURE__ */ jsx(Maximize, { "aria-hidden": true })