@theoremjs/react 0.3.0 → 0.3.1

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 (258) hide show
  1. package/README.md +728 -202
  2. package/client/composer-hints.js +1 -1
  3. package/client/decision-images.d.ts +76 -0
  4. package/client/decision-images.js +139 -0
  5. package/client/decision-transport.d.ts +26 -4
  6. package/client/decision-transport.js +79 -6
  7. package/client/embedding-input.d.ts +84 -0
  8. package/client/embedding-input.js +194 -0
  9. package/client/embedding-space.d.ts +82 -0
  10. package/client/embedding-space.js +389 -0
  11. package/client/embedding-transport.d.ts +40 -0
  12. package/client/embedding-transport.js +71 -0
  13. package/client/failure.d.ts +7 -10
  14. package/client/failure.js +9 -9
  15. package/client/host-call.d.ts +1 -1
  16. package/client/host-call.js +7 -2
  17. package/client/host-transport.d.ts +2 -0
  18. package/client/host-transport.js +3 -2
  19. package/client/index.d.ts +19 -11
  20. package/client/index.js +14 -10
  21. package/client/ink-waveform.d.ts +1 -1
  22. package/client/ink-waveform.js +1 -2
  23. package/client/live/apply-live-turn-tool-event.js +1 -1
  24. package/client/live/live-captions.d.ts +30 -0
  25. package/client/live/live-captions.js +105 -1
  26. package/client/live/live-mic-forward.d.ts +1 -0
  27. package/client/live/live-mic-forward.js +11 -2
  28. package/client/live/live-page-tool.d.ts +34 -0
  29. package/client/live/live-page-tool.js +11 -0
  30. package/client/live/live-state.d.ts +4 -2
  31. package/client/live/live-state.js +2 -0
  32. package/client/live/live-video.js +1 -1
  33. package/client/live/run-live-tool-call.d.ts +7 -2
  34. package/client/live/run-live-tool-call.js +48 -10
  35. package/client/live/use-live-caption-log.d.ts +17 -0
  36. package/client/live/use-live-caption-log.js +51 -0
  37. package/client/live-client.d.ts +52 -4
  38. package/client/live-client.js +250 -59
  39. package/client/live-messages.d.ts +51 -13
  40. package/client/live-messages.js +42 -9
  41. package/client/page-dispatch.d.ts +15 -0
  42. package/client/page-dispatch.js +63 -0
  43. package/client/run-commit.d.ts +16 -43
  44. package/client/run-commit.js +26 -101
  45. package/client/run-session.d.ts +40 -41
  46. package/client/run-session.js +104 -112
  47. package/client/schema-fields.js +27 -6
  48. package/client/shaped-data.js +37 -8
  49. package/client/source-citations.js +1 -1
  50. package/client/space-geometry.d.ts +39 -0
  51. package/client/space-geometry.js +183 -0
  52. package/client/state-fields.js +1 -1
  53. package/client/tool-result.js +30 -12
  54. package/client/tool-resume.d.ts +18 -1
  55. package/client/tool-resume.js +4 -1
  56. package/client/trace-feed.d.ts +1 -1
  57. package/client/trace-story.d.ts +2 -2
  58. package/client/trace-story.js +43 -19
  59. package/client/trace-view.d.ts +2 -2
  60. package/client/trace-view.js +56 -16
  61. package/client/transcript-groups.d.ts +28 -2
  62. package/client/transcript-groups.js +63 -13
  63. package/client/transcript-schema.d.ts +10 -0
  64. package/client/transcript-schema.js +120 -0
  65. package/client/transport.d.ts +114 -26
  66. package/client/transport.js +106 -32
  67. package/client/turn-client.d.ts +17 -8
  68. package/client/turn-client.js +26 -31
  69. package/client/voice-recorder.d.ts +1 -1
  70. package/client/voice-recorder.js +2 -5
  71. package/client/wire-line.js +5 -5
  72. package/client/worklets/mic-capture.js +9 -9
  73. package/components/InkWaveform.d.ts +12 -1
  74. package/components/InkWaveform.js +65 -42
  75. package/components/live/use-live-runner-controls.d.ts +3 -4
  76. package/components/live/use-live-runner-controls.js +6 -10
  77. package/components/live/use-live-runner-model.d.ts +10 -6
  78. package/components/live/use-live-runner-model.js +29 -14
  79. package/components/live/use-live-runner-ui.d.ts +10 -8
  80. package/components/live/use-live-runner-ui.js +19 -11
  81. package/components/live/use-live-session-client.d.ts +4 -4
  82. package/components/live/use-live-session-client.js +11 -8
  83. package/components/use-composer-voice.d.ts +4 -4
  84. package/components/use-composer-voice.js +8 -8
  85. package/components/use-editor-selection.js +4 -1
  86. package/components/use-voice-playback.d.ts +2 -1
  87. package/components/use-voice-playback.js +4 -4
  88. package/hooks/use-described.d.ts +5 -1
  89. package/hooks/use-described.js +11 -9
  90. package/hooks/use-embedding-index.d.ts +27 -0
  91. package/hooks/use-embedding-index.js +43 -0
  92. package/hooks/use-one-call.d.ts +31 -0
  93. package/hooks/use-one-call.js +76 -0
  94. package/hooks/use-page-tool-warnings.d.ts +6 -0
  95. package/hooks/use-page-tool-warnings.js +25 -0
  96. package/hooks/use-second-ticker.d.ts +2 -0
  97. package/hooks/use-second-ticker.js +13 -0
  98. package/hooks/use-theorem-chat-actions.d.ts +25 -21
  99. package/hooks/use-theorem-chat-actions.js +67 -45
  100. package/hooks/use-theorem-chat-state.d.ts +6 -6
  101. package/hooks/use-theorem-chat-state.js +13 -9
  102. package/hooks/use-theorem-chat.d.ts +48 -14
  103. package/hooks/use-theorem-chat.js +329 -141
  104. package/hooks/use-theorem-decision.d.ts +5 -10
  105. package/hooks/use-theorem-decision.js +4 -67
  106. package/hooks/use-theorem-embedding.d.ts +18 -0
  107. package/hooks/use-theorem-embedding.js +12 -0
  108. package/hooks/use-theorem-host.d.ts +5 -4
  109. package/hooks/use-theorem-host.js +26 -8
  110. package/hooks/use-theorem-interface.d.ts +6 -3
  111. package/hooks/use-theorem-interface.js +12 -22
  112. package/index.d.ts +6 -3
  113. package/index.js +5 -2
  114. package/live.d.ts +1 -0
  115. package/live.js +1 -1
  116. package/package.json +6 -5
  117. package/server/checkpoint-ownership.d.ts +6 -0
  118. package/server/checkpoint-ownership.js +42 -0
  119. package/server/conversation-store.d.ts +92 -0
  120. package/server/conversation-store.js +131 -0
  121. package/server/conversation.d.ts +193 -0
  122. package/server/conversation.js +552 -0
  123. package/server/decision-handler.d.ts +12 -4
  124. package/server/decision-handler.js +29 -38
  125. package/server/embedding-handler.d.ts +33 -0
  126. package/server/embedding-handler.js +45 -0
  127. package/server/handler.d.ts +58 -20
  128. package/server/handler.js +441 -238
  129. package/server/mod.d.ts +15 -6
  130. package/server/mod.js +13 -5
  131. package/server/page-dispatch.d.ts +22 -0
  132. package/server/page-dispatch.js +127 -0
  133. package/server/request-body.d.ts +2 -0
  134. package/server/request-body.js +42 -0
  135. package/server/request-check.d.ts +6 -1
  136. package/server/request-check.js +27 -12
  137. package/server/session-authority.d.ts +8 -0
  138. package/server/session-authority.js +78 -0
  139. package/server/session-boundary.d.ts +26 -0
  140. package/server/session-boundary.js +64 -0
  141. package/server/session-identity.d.ts +16 -0
  142. package/server/session-identity.js +41 -0
  143. package/server/session-limits.d.ts +12 -0
  144. package/server/session-limits.js +59 -0
  145. package/server/session-store.d.ts +69 -20
  146. package/server/session-store.js +220 -5
  147. package/server/single-action.d.ts +21 -0
  148. package/server/single-action.js +30 -0
  149. package/server/steer-inbox.js +1 -1
  150. package/server/tool-operations.d.ts +6 -0
  151. package/server/tool-operations.js +132 -0
  152. package/server/turn-input.d.ts +10 -0
  153. package/server/turn-input.js +29 -0
  154. package/styles/ink-controls.css +78 -78
  155. package/ui/CardWall.d.ts +10 -0
  156. package/ui/CardWall.js +61 -0
  157. package/ui/ChatComposerBar.d.ts +28 -5
  158. package/ui/ChatComposerBar.js +85 -30
  159. package/ui/ChatTranscript.d.ts +14 -1
  160. package/ui/ChatTranscript.js +110 -57
  161. package/ui/ConsoleFrame.d.ts +61 -4
  162. package/ui/ConsoleFrame.js +109 -9
  163. package/ui/DecisionAnswers.d.ts +40 -2
  164. package/ui/DecisionAnswers.js +166 -35
  165. package/ui/DecisionImages.d.ts +16 -0
  166. package/ui/DecisionImages.js +46 -0
  167. package/ui/EmbeddingDocuments.d.ts +78 -0
  168. package/ui/EmbeddingDocuments.js +278 -0
  169. package/ui/EmbeddingQuery.d.ts +27 -0
  170. package/ui/EmbeddingQuery.js +95 -0
  171. package/ui/EmbeddingResults.d.ts +41 -0
  172. package/ui/EmbeddingResults.js +165 -0
  173. package/ui/EmbeddingSpace.d.ts +26 -0
  174. package/ui/EmbeddingSpace.js +106 -0
  175. package/ui/LiveCaptionsPanel.d.ts +22 -0
  176. package/ui/LiveCaptionsPanel.js +27 -0
  177. package/ui/LiveRunner.d.ts +6 -4
  178. package/ui/LiveRunner.js +42 -55
  179. package/ui/PaneState.d.ts +33 -0
  180. package/ui/PaneState.js +50 -0
  181. package/ui/SchemaFields.d.ts +1 -1
  182. package/ui/SchemaFields.js +54 -26
  183. package/ui/ShapedData.js +73 -28
  184. package/ui/SidePanel.d.ts +74 -5
  185. package/ui/SidePanel.js +82 -9
  186. package/ui/StateFields.d.ts +10 -2
  187. package/ui/StateFields.js +56 -13
  188. package/ui/TheoremChat.d.ts +65 -14
  189. package/ui/TheoremChat.js +66 -78
  190. package/ui/TheoremDecision.d.ts +12 -20
  191. package/ui/TheoremDecision.js +78 -35
  192. package/ui/TheoremEmbedding.d.ts +27 -0
  193. package/ui/TheoremEmbedding.js +122 -0
  194. package/ui/TheoremHost.d.ts +22 -5
  195. package/ui/TheoremHost.js +127 -37
  196. package/ui/ToolGateCard.d.ts +5 -1
  197. package/ui/ToolGateCard.js +30 -23
  198. package/ui/ToolResult.js +12 -10
  199. package/ui/ToolResultCharts.js +29 -12
  200. package/ui/TraceCharts.d.ts +2 -2
  201. package/ui/TraceCharts.js +48 -17
  202. package/ui/TraceGuardrails.d.ts +3 -2
  203. package/ui/TraceGuardrails.js +24 -6
  204. package/ui/TraceInspector.d.ts +11 -1
  205. package/ui/TraceInspector.js +45 -28
  206. package/ui/TraceInspectorPanel.d.ts +31 -9
  207. package/ui/TraceInspectorPanel.js +77 -39
  208. package/ui/TraceOverview.d.ts +7 -3
  209. package/ui/TraceOverview.js +44 -14
  210. package/ui/TraceSpanDetail.js +24 -22
  211. package/ui/TraceStory.js +9 -8
  212. package/ui/TraceValues.js +19 -9
  213. package/ui/TraceWaterfall.js +12 -9
  214. package/ui/VoiceNote.js +4 -4
  215. package/ui/approval-heading.d.ts +4 -0
  216. package/ui/approval-heading.js +9 -0
  217. package/ui/arrive.d.ts +18 -0
  218. package/ui/arrive.js +46 -0
  219. package/ui/built/theme.css +86 -68
  220. package/ui/built/theme.css.d.ts +9 -0
  221. package/ui/built/theorem.d.ts +3 -3
  222. package/ui/built/theorem.js +34 -5
  223. package/ui/built/theorem.variants.d.ts +8 -2
  224. package/ui/code-language.d.ts +12 -0
  225. package/ui/code-language.js +50 -0
  226. package/ui/disclosure-motion.js +53 -9
  227. package/ui/embedding-space-gl.d.ts +37 -0
  228. package/ui/embedding-space-gl.js +663 -0
  229. package/ui/embedding-space-view.d.ts +8 -0
  230. package/ui/embedding-space-view.js +8 -0
  231. package/ui/glide.d.ts +7 -0
  232. package/ui/glide.js +44 -0
  233. package/ui/icons.js +2 -1
  234. package/ui/index.d.ts +20 -9
  235. package/ui/index.js +20 -10
  236. package/ui/labels-provider.js +5 -2
  237. package/ui/labels.d.ts +374 -85
  238. package/ui/labels.js +1028 -188
  239. package/ui/markdown-plugins.d.ts +9 -0
  240. package/ui/markdown-plugins.js +13 -0
  241. package/ui/mcp-icon.d.ts +6 -0
  242. package/ui/mcp-icon.js +8 -0
  243. package/ui/row-keys.d.ts +19 -0
  244. package/ui/row-keys.js +33 -0
  245. package/ui/theme.d.ts +1 -1
  246. package/ui/theme.js +2 -2
  247. package/ui/theorem-theme.js +32 -10
  248. package/ui/trace-palette.d.ts +2 -16
  249. package/ui/trace-palette.js +2 -16
  250. package/ui/trace-patterns.d.ts +11 -3
  251. package/ui/trace-patterns.js +16 -3
  252. package/ui/transcript-copy-text.js +1 -0
  253. package/ui/wall-columns.d.ts +8 -0
  254. package/ui/wall-columns.js +19 -0
  255. package/client/walk-away.d.ts +0 -34
  256. package/client/walk-away.js +0 -48
  257. package/server/walk-away.d.ts +0 -22
  258. package/server/walk-away.js +0 -41
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @generated by `astryx theme build` — do not edit manually.
3
+ * Source: src/ui/theorem-theme.ts
4
+ * Command: astryx theme build src/ui/theorem-theme.ts --out src/ui/built/theme.css
5
+ * CLI: @astryxdesign/cli@0.7.0
6
+ * Core: @astryxdesign/core@0.7.0
7
+ */
8
+
9
+ export {};
@@ -2,12 +2,12 @@
2
2
  * @generated by `astryx theme build` — do not edit manually.
3
3
  * Source: src/ui/theorem-theme.ts
4
4
  * Command: astryx theme build src/ui/theorem-theme.ts --out src/ui/built/theme.css
5
- * CLI: @astryxdesign/cli@0.6.3
6
- * Core: @astryxdesign/core@0.6.3
5
+ * CLI: @astryxdesign/cli@0.7.0
6
+ * Core: @astryxdesign/core@0.7.0
7
7
  */
8
8
 
9
9
  /// <reference path="./theorem.variants.d.ts" />
10
10
  import type { DefinedTheme } from '@astryxdesign/core/theme';
11
11
  import type { IconRegistry } from '@astryxdesign/core/Icon';
12
- export declare const tablerIcons: IconRegistry;
12
+ export declare const themeIcons: IconRegistry;
13
13
  export declare const theoremTheme: DefinedTheme;
@@ -2,11 +2,13 @@
2
2
  * @generated by `astryx theme build` — do not edit manually.
3
3
  * Source: src/ui/theorem-theme.ts
4
4
  * Command: astryx theme build src/ui/theorem-theme.ts --out src/ui/built/theme.css
5
- * CLI: @astryxdesign/cli@0.6.3
6
- * Core: @astryxdesign/core@0.6.3
5
+ * CLI: @astryxdesign/cli@0.7.0
6
+ * Core: @astryxdesign/core@0.7.0
7
7
  */
8
8
 
9
+ import { neutralTheme } from '@astryxdesign/theme-neutral/built';
9
10
  import { tablerIcons } from "../icons.js";
11
+ const themeIcons = {...neutralTheme["icons"], ...tablerIcons};
10
12
  /**
11
13
  * theorem theme — built by `npx astryx theme build`
12
14
  * Import the CSS file alongside this module:
@@ -488,6 +490,12 @@ export const theoremTheme = {
488
490
  "card": {
489
491
  "base": {
490
492
  "padding": "var(--spacing-3)"
493
+ },
494
+ "variant:bubble": {
495
+ "backgroundColor": "var(--color-neutral)",
496
+ "borderRadius": "var(--radius-chat)",
497
+ "paddingBlock": "var(--spacing-3)",
498
+ "paddingInline": "var(--spacing-4)"
491
499
  }
492
500
  },
493
501
  "section": {
@@ -536,12 +544,17 @@ export const theoremTheme = {
536
544
  }
537
545
  }
538
546
  },
547
+ "code-block": {
548
+ "container:card": {
549
+ "borderRadius": "var(--radius-container)"
550
+ }
551
+ },
539
552
  "layout-panel": {
540
553
  "base": {
541
554
  ":where([role=\"complementary\"])": {
542
555
  "transition": "width var(--duration-medium) var(--ease-standard)"
543
556
  },
544
- ":where([data-resizing] + [role=\"complementary\"])": {
557
+ ":where([data-resizing] + [role=\"complementary\"], [role=\"complementary\"]:has(+ [data-resizing]))": {
545
558
  "transition": "none"
546
559
  }
547
560
  }
@@ -572,6 +585,22 @@ export const theoremTheme = {
572
585
  "2xl": 1536
573
586
  },
574
587
  "rules": [
588
+ {
589
+ "when": {
590
+ "pointer": "coarse",
591
+ "width": {
592
+ "below": "lg"
593
+ }
594
+ },
595
+ "value": {
596
+ "typography": {
597
+ "scale": {
598
+ "base": 16,
599
+ "ratio": 1.1736
600
+ }
601
+ }
602
+ }
603
+ },
575
604
  {
576
605
  "when": {
577
606
  "motion": "reduce"
@@ -625,7 +654,7 @@ export const theoremTheme = {
625
654
  "ratio": 0.75
626
655
  }
627
656
  },
628
- icons: tablerIcons,
657
+ icons: themeIcons,
629
658
  };
630
659
 
631
- export { tablerIcons };
660
+ export { themeIcons };
@@ -2,8 +2,8 @@
2
2
  * @generated by `astryx theme build` — do not edit manually.
3
3
  * Source: src/ui/theorem-theme.ts
4
4
  * Command: astryx theme build src/ui/theorem-theme.ts --out src/ui/built/theme.css
5
- * CLI: @astryxdesign/cli@0.6.3
6
- * Core: @astryxdesign/core@0.6.3
5
+ * CLI: @astryxdesign/cli@0.7.0
6
+ * Core: @astryxdesign/core@0.7.0
7
7
  */
8
8
 
9
9
  // Generated by astryx theme build
@@ -14,3 +14,9 @@ declare module '@astryxdesign/core/Badge' {
14
14
  'gray': true;
15
15
  }
16
16
  }
17
+
18
+ declare module '@astryxdesign/core/Card' {
19
+ interface CardVariantMap {
20
+ 'bubble': true;
21
+ }
22
+ }
@@ -0,0 +1,12 @@
1
+ import type { MarkdownAstRoot } from '@astryxdesign/core/Markdown';
2
+ import type { MarkdownExtensionNode } from '@astryxdesign/core/Markdown/plugins';
3
+ /** The language of untagged code, or `undefined` when the detector is unsure or the language would not be coloured. */
4
+ export declare function detectedCodeLanguage(code: string): string | undefined;
5
+ /** The document with its untagged fences labelled. Unsettled while streaming: the last block may be an open fence. */
6
+ export declare function labelUntaggedCode(document: MarkdownAstRoot<MarkdownExtensionNode>, isFinal: boolean): MarkdownAstRoot<MarkdownExtensionNode>;
7
+ /**
8
+ * A Markdown plugin that labels fenced code the reply left untagged, so it is coloured like a tagged
9
+ * fence. A fence with a language is left as written. While the reply streams, the last block is left
10
+ * alone: it may be a fence still open, and its language would change as it grows.
11
+ */
12
+ export declare const codeLanguagePlugin: import("@astryxdesign/core/Markdown/plugins").MarkdownPluginEntry<never>;
@@ -0,0 +1,50 @@
1
+ import { createMarkdownPlugin } from '@astryxdesign/core/Markdown/plugins';
2
+ import { detectLanguage } from '@speed-highlight/core/detect';
3
+ /** The languages Astryx's `CodeBlock` colours, as the detector names them. A guess outside it would only add a label. */
4
+ const COLOURED = new Set(['js', 'ts', 'py', 'bash', 'json', 'css', 'html', 'xml', 'yaml', 'md']);
5
+ /** The language of untagged code, or `undefined` when the detector is unsure or the language would not be coloured. */
6
+ export function detectedCodeLanguage(code) {
7
+ const language = detectLanguage(code);
8
+ return COLOURED.has(language) ? language : undefined;
9
+ }
10
+ function tagged(node) {
11
+ if (node.type === 'code') {
12
+ const code = node;
13
+ if (code.lang !== null)
14
+ return node;
15
+ const lang = detectedCodeLanguage(code.value);
16
+ return lang === undefined ? node : { ...code, lang };
17
+ }
18
+ if (node.children === undefined)
19
+ return node;
20
+ let changed = false;
21
+ const children = node.children.map((child) => {
22
+ const next = tagged(child);
23
+ if (next !== child)
24
+ changed = true;
25
+ return next;
26
+ });
27
+ return changed ? { ...node, children } : node;
28
+ }
29
+ /** The document with its untagged fences labelled. Unsettled while streaming: the last block may be an open fence. */
30
+ export function labelUntaggedCode(document, isFinal) {
31
+ const { children } = document;
32
+ const settled = isFinal ? children : children.slice(0, -1);
33
+ const next = settled.map((child) => tagged(child));
34
+ if (next.every((child, index) => child === settled[index]))
35
+ return document;
36
+ return {
37
+ ...document,
38
+ children: [...next, ...children.slice(settled.length)],
39
+ };
40
+ }
41
+ /**
42
+ * A Markdown plugin that labels fenced code the reply left untagged, so it is coloured like a tagged
43
+ * fence. A fence with a language is left as written. While the reply streams, the last block is left
44
+ * alone: it may be a fence still open, and its language would change as it grows.
45
+ */
46
+ export const codeLanguagePlugin = createMarkdownPlugin({
47
+ apiVersion: 1,
48
+ name: 'theorem-code-language',
49
+ transform: (document, { isFinal }) => labelUntaggedCode(document, isFinal),
50
+ });
@@ -11,10 +11,13 @@ function panelOf(trigger) {
11
11
  const id = trigger.getAttribute('aria-controls');
12
12
  if (id)
13
13
  return document.getElementById(id);
14
- return trigger.closest('[role="treeitem"]')?.querySelector(':scope > [role="group"]') ?? null;
14
+ return (trigger.closest('[role="treeitem"]')?.querySelector(':scope > [role="group"]') ??
15
+ null);
15
16
  }
16
17
  function ownsMotion(panel) {
17
- return getComputedStyle(panel).transitionDuration.split(',').some((d) => Number.parseFloat(d) > 0);
18
+ return getComputedStyle(panel)
19
+ .transitionDuration.split(',')
20
+ .some((d) => Number.parseFloat(d) > 0);
18
21
  }
19
22
  function isToggleKey(event) {
20
23
  return !(event instanceof KeyboardEvent) || event.key === 'Enter' || event.key === ' ';
@@ -73,7 +76,7 @@ function ease(panel, direction) {
73
76
  return panel.animate(frames.map((frame) => ({ ...frame, boxSizing: 'border-box', overflow: 'clip' })), {
74
77
  duration: ms(style.getPropertyValue('--duration-medium')),
75
78
  easing: style.getPropertyValue('--ease-standard').trim(),
76
- // A shut panel stays shut until its unmount; an open one hands back to its natural height.
79
+ // why: A shut panel stays shut until its unmount; an open one hands back to its natural height.
77
80
  fill: direction === 'close' ? 'forwards' : 'none',
78
81
  });
79
82
  }
@@ -81,6 +84,46 @@ function ease(panel, direction) {
81
84
  function settled(panel) {
82
85
  return Promise.allSettled(panel.getAnimations().map((animation) => animation.finished));
83
86
  }
87
+ /** The attribute Astryx's chat sets on its scroller while it follows new content to the bottom. */
88
+ const FOLLOWING = 'data-astryx-chat-following';
89
+ /** How far from its bottom a scroll up must land for Astryx's chat to stop following: past its 10px lock threshold. */
90
+ const LET_GO = 11;
91
+ /**
92
+ * Keeps a trigger the reader opened where it was while its panel grows. A
93
+ * chat that follows its bottom would spring past it, so the follow is let go
94
+ * first: a scroll up just past the chat's threshold reads as the reader's.
95
+ * The panel's growth then gives the room back, and the trigger ends where it
96
+ * was. A chat too short to scroll yet is held after each layout instead,
97
+ * after the follow's own frame, which also lets it go.
98
+ */
99
+ function holdInPlace(trigger, panel, motion) {
100
+ const scroller = trigger.closest(`[${FOLLOWING}]`);
101
+ if (!scroller)
102
+ return;
103
+ const top = trigger.getBoundingClientRect().top;
104
+ const room = () => scroller.scrollHeight - scroller.clientHeight - scroller.scrollTop;
105
+ scroller.scrollTop -= Math.max(0, LET_GO - room());
106
+ let done = false;
107
+ void motion.then(() => setTimeout(() => (done = true), 1000));
108
+ const held = new ResizeObserver(() => {
109
+ const drift = trigger.getBoundingClientRect().top - top;
110
+ if (drift && scroller.hasAttribute(FOLLOWING))
111
+ scroller.scrollTop += drift;
112
+ });
113
+ held.observe(panel);
114
+ const giveBack = () => {
115
+ const drift = trigger.getBoundingClientRect().top - top;
116
+ // invariant: Never back within the threshold, or the chat would follow again.
117
+ if (drift > 0 && !scroller.hasAttribute(FOLLOWING)) {
118
+ scroller.scrollTop += Math.max(0, Math.min(drift, room() - LET_GO));
119
+ }
120
+ if (done || (drift <= 0.5 && !scroller.hasAttribute(FOLLOWING)))
121
+ held.disconnect();
122
+ else
123
+ requestAnimationFrame(giveBack);
124
+ };
125
+ requestAnimationFrame(giveBack);
126
+ }
84
127
  /**
85
128
  * Eases every disclosure under `ref` open and shut. `onOpened` runs once the
86
129
  * new panel is in the DOM, with a promise for its motion finishing.
@@ -93,12 +136,12 @@ export function useDisclosureMotion(ref, onOpened) {
93
136
  const still = matchMedia('(prefers-reduced-motion: reduce)');
94
137
  const closing = new WeakSet();
95
138
  const replaying = new WeakSet();
96
- // The trigger the reader just pressed to open; opens the page makes itself aren't eased or followed.
139
+ // why: The trigger the reader just pressed to open; opens the page makes itself aren't eased or followed.
97
140
  let opening;
98
141
  /** The trigger the reader pressed opens: note it until the toggle commits, within this event. */
99
142
  const noteOpening = (trigger) => {
100
143
  opening = trigger;
101
- // A stale note must not claim a later change.
144
+ // why: A stale note must not claim a later change.
102
145
  setTimeout(() => {
103
146
  if (opening === trigger)
104
147
  opening = undefined;
@@ -109,7 +152,6 @@ export function useDisclosureMotion(ref, onOpened) {
109
152
  const panel = panelOf(trigger);
110
153
  return panel && !still.matches && !ownsMotion(panel) ? panel : undefined;
111
154
  };
112
- // Capture, before the component toggles: note what the trigger was, and hold a close.
113
155
  const before = (event) => {
114
156
  if (!isToggleKey(event) || replaying.has(event))
115
157
  return;
@@ -137,11 +179,11 @@ export function useDisclosureMotion(ref, onOpened) {
137
179
  target.dispatchEvent(replay);
138
180
  });
139
181
  };
140
- // After the component commits the toggle (a component may stop the event, so
182
+ // why: After the component commits the toggle (a component may stop the event, so
141
183
  // watch the attribute rather than wait for the event to bubble): ease the new panel.
142
184
  const opened = new MutationObserver(() => {
143
185
  const trigger = opening;
144
- if (!trigger || trigger.getAttribute('aria-expanded') !== 'true')
186
+ if (trigger?.getAttribute('aria-expanded') !== 'true')
145
187
  return;
146
188
  opening = undefined;
147
189
  const panel = panelOf(trigger);
@@ -149,7 +191,9 @@ export function useDisclosureMotion(ref, onOpened) {
149
191
  return;
150
192
  if (!still.matches && !ownsMotion(panel))
151
193
  ease(panel, 'open');
152
- onOpened?.(trigger, panel, settled(panel));
194
+ const motion = settled(panel);
195
+ holdInPlace(trigger, panel, motion);
196
+ onOpened?.(trigger, panel, motion);
153
197
  });
154
198
  opened.observe(root, { subtree: true, attributeFilter: ['aria-expanded'] });
155
199
  root.addEventListener('click', before, true);
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Draws the embedding space with WebGL2 and nothing else: anti-aliased lines,
3
+ * solid dots and the query's white dot, laid over the card with premultiplied
4
+ * alpha so the card shows through. Dots cover whatever lies behind them, and
5
+ * every line stops just short of the dot it reaches; the far side of the
6
+ * ball draws smaller and fainter. No React here: the component owns one
7
+ * renderer and feeds it.
8
+ *
9
+ * @module
10
+ */
11
+ import type { EmbeddingSpace, SpacePoint } from '../client/embedding-space.ts';
12
+ import { type QueryMatch } from '../client/space-geometry.ts';
13
+ /** What the space highlights: a query's matches, and the document under the pointer. */
14
+ export type SpaceFocus = {
15
+ query: SpacePoint | null;
16
+ matches: readonly QueryMatch[];
17
+ hovered: number | null;
18
+ };
19
+ export type SpaceRenderer = {
20
+ /** Shows a new index: its clusters grow in, one after another. */
21
+ setSpace(space: EmbeddingSpace): void;
22
+ setFocus(focus: SpaceFocus): void;
23
+ /** The document nearest a point in CSS pixels from the canvas's corner, if one is close and facing. */
24
+ pick(x: number, y: number): number | null;
25
+ /** Turns the sphere as a drag moves, in CSS pixels. */
26
+ drag(dx: number, dy: number): void;
27
+ release(): void;
28
+ };
29
+ /**
30
+ * One renderer for one canvas. Throws {@link SpaceUnavailable} when the
31
+ * browser cannot draw it, so the caller shows the list instead.
32
+ */
33
+ export declare function createSpaceRenderer(canvas: HTMLCanvasElement, { still }: {
34
+ still: boolean;
35
+ }): SpaceRenderer & {
36
+ destroy(): void;
37
+ };