@opengeni/react 3.8.0-canary.0 → 4.0.1-canary.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 (111) hide show
  1. package/README.md +11 -24
  2. package/dist/artifacts.js +7 -7
  3. package/dist/{browser-viewer-SS46LUAC.js → browser-viewer-NTD6UIPR.js} +3 -3
  4. package/dist/{chunk-VEPNNDNG.js → chunk-24M4YBCL.js} +4 -4
  5. package/dist/chunk-5M6VTFJ5.js +171 -0
  6. package/dist/chunk-5M6VTFJ5.js.map +1 -0
  7. package/dist/{chunk-IOLVN74D.js → chunk-6J56T3MG.js} +15 -1
  8. package/dist/chunk-6J56T3MG.js.map +1 -0
  9. package/dist/{chunk-ELIYWBZR.js → chunk-BV5A4OTE.js} +4 -4
  10. package/dist/{chunk-3HIF5IFC.js → chunk-HO6X4FHG.js} +2597 -1030
  11. package/dist/chunk-HO6X4FHG.js.map +1 -0
  12. package/dist/{chunk-BXJOQFCD.js → chunk-HUMIRRYI.js} +84 -221
  13. package/dist/chunk-HUMIRRYI.js.map +1 -0
  14. package/dist/{chunk-ZFOFY5ST.js → chunk-NLTS7JHI.js} +16 -31
  15. package/dist/chunk-NLTS7JHI.js.map +1 -0
  16. package/dist/chunk-NOSYBS2D.js +208 -0
  17. package/dist/chunk-NOSYBS2D.js.map +1 -0
  18. package/dist/chunk-YBM6CQRU.js +292 -0
  19. package/dist/chunk-YBM6CQRU.js.map +1 -0
  20. package/dist/{chunk-BQGIXRKV.js → chunk-Z7WBDB4E.js} +416 -290
  21. package/dist/chunk-Z7WBDB4E.js.map +1 -0
  22. package/dist/chunk-ZYRGEKWS.js +216 -0
  23. package/dist/chunk-ZYRGEKWS.js.map +1 -0
  24. package/dist/components/composer.d.ts +4 -2
  25. package/dist/components/human-input-form.d.ts +3 -1
  26. package/dist/components/human-input-surface.d.ts +3 -2
  27. package/dist/components/message-timeline.d.ts +9 -2
  28. package/dist/components/session-conversation.d.ts +2 -0
  29. package/dist/components/timeline-annotation-chrome.d.ts +25 -0
  30. package/dist/components/timeline-annotation-layout.d.ts +42 -0
  31. package/dist/components/timeline-annotation-markers.d.ts +8 -0
  32. package/dist/components/timeline-annotation-reveal-context.d.ts +4 -0
  33. package/dist/components/timeline-annotation-shared.d.ts +31 -0
  34. package/dist/components/timeline-annotations-dialog.d.ts +1 -1
  35. package/dist/components/timeline-annotations.d.ts +8 -2
  36. package/dist/composer.js +3 -4
  37. package/dist/{computer-viewer-FWEZISW4.js → computer-viewer-7TEZC6Y2.js} +3 -3
  38. package/dist/hooks/use-codex-accounts.d.ts +11 -1
  39. package/dist/hooks/use-composer.d.ts +2 -0
  40. package/dist/hooks/use-session-events.d.ts +2 -2
  41. package/dist/index.d.ts +1 -1
  42. package/dist/index.js +50 -37
  43. package/dist/index.js.map +1 -1
  44. package/dist/interaction.js +3 -3
  45. package/dist/machines.js +1 -1
  46. package/dist/older-history.d.ts +3 -2
  47. package/dist/{platform-activity-row-BJHUHXAT.js → platform-activity-row-27LKAWZD.js} +2 -2
  48. package/dist/platform-activity-row-27LKAWZD.js.map +1 -0
  49. package/dist/{queue-surface-implementation-XL2A6PBM.js → queue-surface-implementation-LK3S4BIW.js} +5 -2
  50. package/dist/{queue-surface-implementation-XL2A6PBM.js.map → queue-surface-implementation-LK3S4BIW.js.map} +1 -1
  51. package/dist/realtime.js +3 -3
  52. package/dist/session-ui.js +11 -11
  53. package/dist/session.js +2 -2
  54. package/dist/timeline/types.d.ts +3 -0
  55. package/dist/timeline-annotation-markers-DRSTLEM6.js +165 -0
  56. package/dist/timeline-annotation-markers-DRSTLEM6.js.map +1 -0
  57. package/dist/timeline-annotation-selection-IHLUGVND.js +236 -0
  58. package/dist/timeline-annotation-selection-IHLUGVND.js.map +1 -0
  59. package/dist/timeline-annotations-dialog-6Q53N5FG.js +262 -0
  60. package/dist/timeline-annotations-dialog-6Q53N5FG.js.map +1 -0
  61. package/package.json +2 -6
  62. package/src/components/chat-composer.tsx +5 -2
  63. package/src/components/composer.tsx +40 -5
  64. package/src/components/copy-button.tsx +1 -0
  65. package/src/components/human-input-form.tsx +185 -53
  66. package/src/components/human-input-surface.tsx +3 -0
  67. package/src/components/machines-dashboard.tsx +1 -16
  68. package/src/components/message-timeline.tsx +397 -302
  69. package/src/components/session-commands-panel.tsx +7 -1
  70. package/src/components/session-conversation.tsx +4 -1
  71. package/src/components/timeline-annotation-chrome.tsx +183 -0
  72. package/src/components/timeline-annotation-layout.ts +268 -0
  73. package/src/components/timeline-annotation-markers.tsx +185 -0
  74. package/src/components/timeline-annotation-reveal-context.ts +9 -0
  75. package/src/components/timeline-annotation-selection.tsx +99 -54
  76. package/src/components/timeline-annotation-shared.ts +245 -0
  77. package/src/components/timeline-annotations-dialog.tsx +172 -128
  78. package/src/components/timeline-annotations.tsx +178 -45
  79. package/src/hooks/use-codex-accounts.ts +32 -5
  80. package/src/hooks/use-composer.ts +17 -0
  81. package/src/hooks/use-session-events.ts +16 -277
  82. package/src/index.ts +0 -1
  83. package/src/older-history.ts +22 -4
  84. package/src/timeline/platform-activity-row.tsx +4 -1
  85. package/src/timeline/projection.ts +99 -9
  86. package/src/timeline/types.ts +3 -0
  87. package/styles/compiled.css +86 -22
  88. package/dist/chat.d.ts +0 -37
  89. package/dist/chat.js +0 -315
  90. package/dist/chat.js.map +0 -1
  91. package/dist/chunk-3HIF5IFC.js.map +0 -1
  92. package/dist/chunk-BQGIXRKV.js.map +0 -1
  93. package/dist/chunk-BXJOQFCD.js.map +0 -1
  94. package/dist/chunk-F2CFHN3Y.js +0 -1415
  95. package/dist/chunk-F2CFHN3Y.js.map +0 -1
  96. package/dist/chunk-HQV2I5HV.js +0 -124
  97. package/dist/chunk-HQV2I5HV.js.map +0 -1
  98. package/dist/chunk-IOLVN74D.js.map +0 -1
  99. package/dist/chunk-W3OUAH6M.js +0 -170
  100. package/dist/chunk-W3OUAH6M.js.map +0 -1
  101. package/dist/chunk-ZFOFY5ST.js.map +0 -1
  102. package/dist/platform-activity-row-BJHUHXAT.js.map +0 -1
  103. package/dist/timeline-annotation-selection-3L7LHGG2.js +0 -187
  104. package/dist/timeline-annotation-selection-3L7LHGG2.js.map +0 -1
  105. package/dist/timeline-annotations-dialog-U7EVLR75.js +0 -196
  106. package/dist/timeline-annotations-dialog-U7EVLR75.js.map +0 -1
  107. package/src/chat.tsx +0 -393
  108. /package/dist/{browser-viewer-SS46LUAC.js.map → browser-viewer-NTD6UIPR.js.map} +0 -0
  109. /package/dist/{chunk-VEPNNDNG.js.map → chunk-24M4YBCL.js.map} +0 -0
  110. /package/dist/{chunk-ELIYWBZR.js.map → chunk-BV5A4OTE.js.map} +0 -0
  111. /package/dist/{computer-viewer-FWEZISW4.js.map → computer-viewer-7TEZC6Y2.js.map} +0 -0
package/README.md CHANGED
@@ -24,31 +24,12 @@ ancestor. Components are styled with Tailwind v4 utilities mapped onto the
24
24
  tokens, Radix primitives for behavior, and Motion for state-communicating
25
25
  animation. Override the tokens to rebrand everything.
26
26
 
27
- ## Drop-in chat (`@opengeni/react/chat`)
27
+ ## Conversation UI
28
28
 
29
- The smallest surface. `OpenGeniChat` talks only to your own endpoint served by
30
- `createChatHandler` from `@opengeni/sdk/chat`; it needs no provider, no SDK
31
- client, and no proxy of OpenGeni routes:
32
-
33
- ```tsx
34
- import { OpenGeniChat } from "@opengeni/react/chat";
35
-
36
- <OpenGeniChat handlerUrl="/api/chat" conversation="c_9" placeholder="Ask anything" />;
37
- ```
38
-
39
- It posts `{ message }`, renders the streamed reply with `Markdown`, and shows a
40
- card with buttons when the agent stops for an approval or a structured
41
- human-input request (answered through `POST /api/chat/respond`). Optional props:
42
- `headers` for the host's auth header, `authKey`, `className`, and `renderMessage`.
43
- Changed header values clear messages, pending cards, drafts and in-flight
44
- requests before restoring the new identity's history. Header callbacks are
45
- resolved on each host render; re-render when their credentials change. For
46
- cookie authentication, pass `authKey={JSON.stringify([tenantId, userId])}` and
47
- change it on sign-in, sign-out and tenant switches, even when `conversation`
48
- stays the same. `authKey` is a local reset key and is never sent to the handler.
49
- `SessionConversation` below is the full-surface upgrade on the same session
50
- (files, queue, model picker, approvals with policies) once the host proxies the
51
- session routes.
29
+ Use `SessionConversation` for an existing session, or compose `MessageTimeline`
30
+ and `ChatComposer` with the session hooks. These use the normal SDK through
31
+ your authenticated host routes. For custom or compatible frontends, the backend
32
+ `@opengeni/sdk/chat` adapters provide the `createChatHandler` protocol.
52
33
 
53
34
  ## Editable Office artifacts
54
35
 
@@ -844,3 +825,9 @@ thinking labels through `messages`, and override payment descriptions through
844
825
  Subscription descriptions appear once per provider group. Free models carry a
845
826
  Free badge. Pass `hasImageAttachments` for the current draft to show an image
846
827
  compatibility warning only when the selected model cannot view those images.
828
+
829
+ `MessageTimeline.renderMessageActions(item)` places host-owned controls beside
830
+ Copy and the timestamp for user messages and completed assistant messages.
831
+ The host owns feedback, fork authorization, and mutations; streaming assistant
832
+ messages omit this slot. Use the `group/copy` hover/focus state and preserve
833
+ visible touch targets when styling actions.
package/dist/artifacts.js CHANGED
@@ -1,10 +1,3 @@
1
- import {
2
- EditablePresentationArtifactSurface,
3
- PresentationArtifactSurface,
4
- PresentationEditor,
5
- PresentationProjectionArtifactSurface,
6
- PresentationProjectionEditor
7
- } from "./chunk-PEI6RANI.js";
8
1
  import {
9
2
  DocumentArtifactSurface,
10
3
  DocumentEditor,
@@ -19,6 +12,13 @@ import {
19
12
  SpreadsheetGrid,
20
13
  SpreadsheetProjectionGrid
21
14
  } from "./chunk-ICA6R6TW.js";
15
+ import {
16
+ EditablePresentationArtifactSurface,
17
+ PresentationArtifactSurface,
18
+ PresentationEditor,
19
+ PresentationProjectionArtifactSurface,
20
+ PresentationProjectionEditor
21
+ } from "./chunk-PEI6RANI.js";
22
22
  import {
23
23
  ArtifactSurface,
24
24
  EditableArtifactMessage
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  BrowserViewer
3
- } from "./chunk-VEPNNDNG.js";
3
+ } from "./chunk-24M4YBCL.js";
4
4
  import "./chunk-H5YGYNXS.js";
5
5
  import "./chunk-5YK2ZDIG.js";
6
+ import "./chunk-JVYQRRC4.js";
6
7
  import "./chunk-PLYRL5ER.js";
7
8
  import "./chunk-KZ73RL76.js";
8
9
  import "./chunk-NEXLLTP4.js";
9
- import "./chunk-JVYQRRC4.js";
10
10
  import "./chunk-22KP6LR5.js";
11
11
  export {
12
12
  BrowserViewer
13
13
  };
14
- //# sourceMappingURL=browser-viewer-SS46LUAC.js.map
14
+ //# sourceMappingURL=browser-viewer-NTD6UIPR.js.map
@@ -15,6 +15,9 @@ import {
15
15
  encodeStreamOpen,
16
16
  interactionHostPlatform
17
17
  } from "./chunk-5YK2ZDIG.js";
18
+ import {
19
+ copyTextToClipboard
20
+ } from "./chunk-JVYQRRC4.js";
18
21
  import {
19
22
  formatBytes
20
23
  } from "./chunk-PLYRL5ER.js";
@@ -24,9 +27,6 @@ import {
24
27
  import {
25
28
  useEmbeddedBrowserInteraction
26
29
  } from "./chunk-NEXLLTP4.js";
27
- import {
28
- copyTextToClipboard
29
- } from "./chunk-JVYQRRC4.js";
30
30
  import {
31
31
  cn
32
32
  } from "./chunk-22KP6LR5.js";
@@ -3904,4 +3904,4 @@ export {
3904
3904
  useSiteAuthConnections,
3905
3905
  BrowserViewer
3906
3906
  };
3907
- //# sourceMappingURL=chunk-VEPNNDNG.js.map
3907
+ //# sourceMappingURL=chunk-24M4YBCL.js.map
@@ -0,0 +1,171 @@
1
+ import {
2
+ annotationNoteNeedsDisclosure
3
+ } from "./chunk-ZYRGEKWS.js";
4
+ import {
5
+ annotationSourceLabel,
6
+ revealLoadedAnnotationSource,
7
+ truncateAnnotationQuote
8
+ } from "./chunk-NOSYBS2D.js";
9
+ import {
10
+ cn
11
+ } from "./chunk-22KP6LR5.js";
12
+
13
+ // src/components/timeline-annotation-chrome.tsx
14
+ import { useLayoutEffect, useRef, useState } from "react";
15
+
16
+ // src/components/timeline-annotation-reveal-context.ts
17
+ import { createContext, useContext } from "react";
18
+ var TimelineAnnotationSourceRootContext = createContext(null);
19
+ function useTimelineAnnotationSourceRoot() {
20
+ return useContext(TimelineAnnotationSourceRootContext);
21
+ }
22
+
23
+ // src/components/timeline-annotation-chrome.tsx
24
+ import { jsx, jsxs } from "react/jsx-runtime";
25
+ var NOTE_MAX_HEIGHT_PX = 160;
26
+ function autosizeNote(textarea) {
27
+ if (!textarea) return;
28
+ textarea.style.height = "auto";
29
+ textarea.style.height = `${Math.min(Math.max(textarea.scrollHeight, 32), NOTE_MAX_HEIGHT_PX)}px`;
30
+ }
31
+ function quotePreview(quote) {
32
+ const trimmed = quote.trim();
33
+ const multiline = trimmed.includes("\n");
34
+ return {
35
+ text: multiline ? trimmed : trimmed.replace(/\s+/g, " "),
36
+ multiline
37
+ };
38
+ }
39
+ function AnnotationQuoteSourceButton({
40
+ annotation,
41
+ lines = 2,
42
+ onRevealSource,
43
+ onUnavailable
44
+ }) {
45
+ const preview = quotePreview(annotation.quote);
46
+ const sourceLabel = annotationSourceLabel(annotation.source);
47
+ const sourceRoot = useTimelineAnnotationSourceRoot();
48
+ return /* @__PURE__ */ jsxs(
49
+ "button",
50
+ {
51
+ type: "button",
52
+ title: annotation.quote,
53
+ "aria-label": `View ${sourceLabel} source: ${truncateAnnotationQuote(annotation.quote, 88)}`,
54
+ className: "block w-full min-w-0 rounded-sm border-0 bg-transparent text-left text-og-sm leading-5 text-og-fg-muted outline-hidden transition-colors hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:min-h-[44px]",
55
+ onClick: () => {
56
+ const revealed = onRevealSource?.(annotation.source) ?? (sourceRoot ? revealLoadedAnnotationSource(annotation.source, sourceRoot.current) : revealLoadedAnnotationSource(annotation.source));
57
+ onUnavailable(revealed ? null : annotation.id);
58
+ },
59
+ children: [
60
+ /* @__PURE__ */ jsx(
61
+ "span",
62
+ {
63
+ className: cn(
64
+ "block min-w-0 max-w-full break-words",
65
+ preview.multiline ? "whitespace-pre-wrap" : "whitespace-normal",
66
+ lines === 3 ? "line-clamp-3" : "line-clamp-2"
67
+ ),
68
+ children: preview.text
69
+ }
70
+ ),
71
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: " view source" })
72
+ ]
73
+ }
74
+ );
75
+ }
76
+ function AnnotationAccentRow({
77
+ children,
78
+ className
79
+ }) {
80
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex gap-2.5", className), children: [
81
+ /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "w-0.5 shrink-0 rounded-full bg-og-accent/55" }),
82
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children })
83
+ ] });
84
+ }
85
+ function AnnotationNotePreview({
86
+ note,
87
+ annotationId,
88
+ ordinal,
89
+ className
90
+ }) {
91
+ const [expanded, setExpanded] = useState(false);
92
+ if (!note) return null;
93
+ const collapsible = annotationNoteNeedsDisclosure(note);
94
+ const contentId = `og-annotation-note-${annotationId}`;
95
+ return /* @__PURE__ */ jsxs("div", { className, children: [
96
+ /* @__PURE__ */ jsx(
97
+ "p",
98
+ {
99
+ id: contentId,
100
+ "data-og-annotation-note": "",
101
+ "data-og-annotation-note-expanded": expanded ? "true" : "false",
102
+ className: cn(
103
+ "mt-0.5 break-words whitespace-pre-wrap text-og-sm leading-5 text-og-fg",
104
+ !expanded && "line-clamp-4"
105
+ ),
106
+ children: note
107
+ }
108
+ ),
109
+ collapsible ? /* @__PURE__ */ jsx(
110
+ "button",
111
+ {
112
+ type: "button",
113
+ className: "mt-0.5 inline-flex min-h-7 items-center rounded-og-sm px-1 text-og-xs font-medium text-og-fg-muted outline-hidden hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:min-h-11",
114
+ "aria-expanded": expanded,
115
+ "aria-controls": contentId,
116
+ "aria-label": expanded ? `Show less annotation ${ordinal}` : `Show more annotation ${ordinal}`,
117
+ onClick: () => setExpanded((current) => !current),
118
+ children: expanded ? "Show less" : "Show more"
119
+ }
120
+ ) : null
121
+ ] });
122
+ }
123
+ function AnnotationNoteField({
124
+ annotation,
125
+ inputRef,
126
+ onUpdate,
127
+ onCommit
128
+ }) {
129
+ const localRef = useRef(null);
130
+ const setRef = (node) => {
131
+ localRef.current = node;
132
+ inputRef?.(node);
133
+ autosizeNote(node);
134
+ };
135
+ useLayoutEffect(() => {
136
+ autosizeNote(localRef.current);
137
+ }, [annotation.note]);
138
+ return /* @__PURE__ */ jsx(
139
+ "textarea",
140
+ {
141
+ ref: setRef,
142
+ value: annotation.note,
143
+ rows: 1,
144
+ maxLength: 2048,
145
+ placeholder: "Add a note\u2026",
146
+ "aria-label": "Note",
147
+ className: "mt-0.5 max-h-40 min-h-8 w-full resize-none overflow-y-auto break-words border-0 bg-transparent px-0 py-1 text-og-sm leading-5 text-og-fg outline-hidden placeholder:text-og-fg-subtle focus-visible:ring-0",
148
+ onInput: (event) => {
149
+ autosizeNote(event.currentTarget);
150
+ onUpdate(annotation.id, event.currentTarget.value);
151
+ },
152
+ onKeyDown: (event) => {
153
+ if (event.key !== "Enter" || event.shiftKey || event.nativeEvent.isComposing) return;
154
+ event.preventDefault();
155
+ event.stopPropagation();
156
+ if (event.currentTarget.value.trim().length === 0) return;
157
+ onCommit?.();
158
+ }
159
+ }
160
+ );
161
+ }
162
+
163
+ export {
164
+ TimelineAnnotationSourceRootContext,
165
+ useTimelineAnnotationSourceRoot,
166
+ AnnotationQuoteSourceButton,
167
+ AnnotationAccentRow,
168
+ AnnotationNotePreview,
169
+ AnnotationNoteField
170
+ };
171
+ //# sourceMappingURL=chunk-5M6VTFJ5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/timeline-annotation-chrome.tsx","../src/components/timeline-annotation-reveal-context.ts"],"sourcesContent":["import type {\n DraftTimelineAnnotation,\n TimelineAnnotation,\n TimelineAnnotationSource,\n} from \"@opengeni/sdk\";\nimport { useLayoutEffect, useRef, useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { annotationNoteNeedsDisclosure } from \"./timeline-annotation-layout\";\nimport { useTimelineAnnotationSourceRoot } from \"./timeline-annotation-reveal-context\";\nimport {\n annotationSourceLabel,\n revealLoadedAnnotationSource,\n truncateAnnotationQuote,\n} from \"./timeline-annotation-shared\";\n\nexport type TimelineAnnotationLike = DraftTimelineAnnotation | TimelineAnnotation;\n\nconst NOTE_MAX_HEIGHT_PX = 160;\n\nfunction autosizeNote(textarea: HTMLTextAreaElement | null): void {\n if (!textarea) return;\n textarea.style.height = \"auto\";\n textarea.style.height = `${Math.min(Math.max(textarea.scrollHeight, 32), NOTE_MAX_HEIGHT_PX)}px`;\n}\n\nfunction quotePreview(quote: string): { text: string; multiline: boolean } {\n const trimmed = quote.trim();\n const multiline = trimmed.includes(\"\\n\");\n return {\n text: multiline ? trimmed : trimmed.replace(/\\s+/g, \" \"),\n multiline,\n };\n}\n\nexport function AnnotationQuoteSourceButton({\n annotation,\n lines = 2,\n onRevealSource,\n onUnavailable,\n}: {\n annotation: TimelineAnnotationLike;\n lines?: 2 | 3;\n onRevealSource?: ((source: TimelineAnnotationSource) => boolean) | undefined;\n onUnavailable: (id: string | null) => void;\n}) {\n const preview = quotePreview(annotation.quote);\n const sourceLabel = annotationSourceLabel(annotation.source);\n const sourceRoot = useTimelineAnnotationSourceRoot();\n return (\n <button\n type=\"button\"\n title={annotation.quote}\n aria-label={`View ${sourceLabel} source: ${truncateAnnotationQuote(annotation.quote, 88)}`}\n className=\"block w-full min-w-0 rounded-sm border-0 bg-transparent text-left text-og-sm leading-5 text-og-fg-muted outline-hidden transition-colors hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:min-h-[44px]\"\n onClick={() => {\n const revealed =\n onRevealSource?.(annotation.source) ??\n (sourceRoot\n ? revealLoadedAnnotationSource(annotation.source, sourceRoot.current)\n : revealLoadedAnnotationSource(annotation.source));\n onUnavailable(revealed ? null : annotation.id);\n }}\n >\n <span\n className={cn(\n \"block min-w-0 max-w-full break-words\",\n preview.multiline ? \"whitespace-pre-wrap\" : \"whitespace-normal\",\n lines === 3 ? \"line-clamp-3\" : \"line-clamp-2\",\n )}\n >\n {preview.text}\n </span>\n <span className=\"sr-only\"> view source</span>\n </button>\n );\n}\n\nexport function AnnotationAccentRow({\n children,\n className,\n}: {\n children: ReactNode;\n className?: string | undefined;\n}) {\n return (\n <div className={cn(\"flex gap-2.5\", className)}>\n <div aria-hidden=\"true\" className=\"w-0.5 shrink-0 rounded-full bg-og-accent/55\" />\n <div className=\"min-w-0 flex-1\">{children}</div>\n </div>\n );\n}\n\nexport function AnnotationNotePreview({\n note,\n annotationId,\n ordinal,\n className,\n}: {\n note: string;\n annotationId: string;\n ordinal: number;\n className?: string | undefined;\n}) {\n const [expanded, setExpanded] = useState(false);\n if (!note) return null;\n const collapsible = annotationNoteNeedsDisclosure(note);\n const contentId = `og-annotation-note-${annotationId}`;\n return (\n <div className={className}>\n <p\n id={contentId}\n data-og-annotation-note=\"\"\n data-og-annotation-note-expanded={expanded ? \"true\" : \"false\"}\n className={cn(\n \"mt-0.5 break-words whitespace-pre-wrap text-og-sm leading-5 text-og-fg\",\n !expanded && \"line-clamp-4\",\n )}\n >\n {note}\n </p>\n {collapsible ? (\n <button\n type=\"button\"\n className=\"mt-0.5 inline-flex min-h-7 items-center rounded-og-sm px-1 text-og-xs font-medium text-og-fg-muted outline-hidden hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:min-h-11\"\n aria-expanded={expanded}\n aria-controls={contentId}\n aria-label={\n expanded ? `Show less annotation ${ordinal}` : `Show more annotation ${ordinal}`\n }\n onClick={() => setExpanded((current) => !current)}\n >\n {expanded ? \"Show less\" : \"Show more\"}\n </button>\n ) : null}\n </div>\n );\n}\n\nexport function AnnotationNoteField({\n annotation,\n inputRef,\n onUpdate,\n onCommit,\n}: {\n annotation: TimelineAnnotationLike;\n inputRef?: ((node: HTMLTextAreaElement | null) => void) | undefined;\n onUpdate: (id: string, note: string) => void;\n onCommit?: (() => void) | undefined;\n}) {\n const localRef = useRef<HTMLTextAreaElement | null>(null);\n const setRef = (node: HTMLTextAreaElement | null) => {\n localRef.current = node;\n inputRef?.(node);\n autosizeNote(node);\n };\n\n useLayoutEffect(() => {\n autosizeNote(localRef.current);\n }, [annotation.note]);\n\n return (\n <textarea\n ref={setRef}\n value={annotation.note}\n rows={1}\n maxLength={2048}\n placeholder=\"Add a note…\"\n aria-label=\"Note\"\n className=\"mt-0.5 max-h-40 min-h-8 w-full resize-none overflow-y-auto break-words border-0 bg-transparent px-0 py-1 text-og-sm leading-5 text-og-fg outline-hidden placeholder:text-og-fg-subtle focus-visible:ring-0\"\n onInput={(event) => {\n autosizeNote(event.currentTarget);\n onUpdate(annotation.id, event.currentTarget.value);\n }}\n onKeyDown={(event) => {\n if (event.key !== \"Enter\" || event.shiftKey || event.nativeEvent.isComposing) return;\n event.preventDefault();\n event.stopPropagation();\n if (event.currentTarget.value.trim().length === 0) return;\n onCommit?.();\n }}\n />\n );\n}\n","import { createContext, useContext, type RefObject } from \"react\";\n\n/** Owning timeline scroller for quote-source navigation. */\nexport const TimelineAnnotationSourceRootContext =\n createContext<RefObject<HTMLElement | null> | null>(null);\n\nexport function useTimelineAnnotationSourceRoot(): RefObject<HTMLElement | null> | null {\n return useContext(TimelineAnnotationSourceRootContext);\n}\n"],"mappings":"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","names":[]}
@@ -1794,6 +1794,19 @@ function useComposer(sessionId, options = {}) {
1794
1794
  const clearAnnotationReviewTarget = useCallback(() => {
1795
1795
  setAnnotationReviewTargetId(null);
1796
1796
  }, []);
1797
+ const requestAnnotationReview = useCallback((id) => {
1798
+ const current = annotationsRef.current;
1799
+ if (current.length === 0) {
1800
+ setAnnotationReviewTargetId(null);
1801
+ return;
1802
+ }
1803
+ if (id && current.some((annotation) => annotation.id === id)) {
1804
+ setAnnotationReviewTargetId(id);
1805
+ return;
1806
+ }
1807
+ const incomplete = current.find((annotation) => annotation.note.trim().length === 0);
1808
+ setAnnotationReviewTargetId(incomplete?.id ?? current[0].id);
1809
+ }, []);
1797
1810
  const updatePolicy = useCallback(
1798
1811
  (next) => {
1799
1812
  if (targetKeyRef.current !== targetKey) return;
@@ -1941,6 +1954,7 @@ function useComposer(sessionId, options = {}) {
1941
1954
  removeAnnotation,
1942
1955
  annotationReviewTargetId: identityMatches ? annotationReviewTargetId : null,
1943
1956
  clearAnnotationReviewTarget,
1957
+ requestAnnotationReview,
1944
1958
  hasDraftContent,
1945
1959
  send,
1946
1960
  optimisticMessages: visibleOptimisticMessages,
@@ -2073,4 +2087,4 @@ export {
2073
2087
  shouldSubmitOnKey,
2074
2088
  shouldSteerOnKey
2075
2089
  };
2076
- //# sourceMappingURL=chunk-IOLVN74D.js.map
2090
+ //# sourceMappingURL=chunk-6J56T3MG.js.map