@plannotator/ui 0.43.0 → 0.43.2
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.
- package/HANDOFF.md +116 -2
- package/README.md +16 -2
- package/components/AnnotationToolbar.tsx +12 -3
- package/components/CommentPopover.tsx +2 -0
- package/components/MentionPicker.tsx +43 -0
- package/components/Viewer.tsx +37 -2
- package/components/html-viewer/HtmlViewer.tsx +17 -1
- package/components/html-viewer/useHtmlAnnotation.ts +4 -1
- package/hooks/useAnnotationHighlighter.ts +22 -3
- package/hooks/useMentionAutocomplete.ts +3 -1
- package/package.json +1 -1
- package/styles.css +1 -1
- package/types.ts +1 -0
- package/utils/mentions.ts +8 -0
package/HANDOFF.md
CHANGED
|
@@ -214,7 +214,7 @@ We deliberately did **not** restructure the exports map in this PR (move-don't-r
|
|
|
214
214
|
| `hooks/useHtmlRefresh` | Re-fetch a rendered HTML document through a host-supplied `fetchSnapshot`, remount the viewer on a reload generation, acknowledge the restore report once. See "HTML annotation parity seams". |
|
|
215
215
|
| `shortcuts` (`useHtmlAnnotateShortcuts`, `defineShortcutScope`, the scope registry) | The declarative keyboard-shortcut engine and the per-surface scopes, including the HTML annotate scope (Mod+Shift+A toggles annotate mode, Mod+Shift+X shows/hides the tools). Pure: React plus `utils/platform`; no backend. |
|
|
216
216
|
| `utils/selectionActions` + `components/SelectionActionsDropdown` | The host selection-actions seam: `SelectionAction`, `SelectionActionContext`, the pure `buildSelectionActionContext`, and the dropdown `AnnotationToolbar` opens. Pure React; no backend. *(Blessed in 0.43.0.)* |
|
|
217
|
-
| `utils/mentions` + `components/MentionPicker` + `hooks/useMentionAutocomplete` | The `@` mention seam behind `CommentPopover`'s `mentionSource
|
|
217
|
+
| `utils/mentions` + `components/MentionPicker` + `hooks/useMentionAutocomplete` | The `@` mention seam behind `CommentPopover`'s `mentionSource` (and, since 0.43.1, `Viewer`'s and `HtmlViewer`'s): the pure grammar (`mentionTrigger`, `mentionMatches`, `applyMentionPick`, `survivingMentions`), the portaled picker, and the keyboard state machine. Pure React; no backend. *(Blessed in 0.43.0.)* |
|
|
218
218
|
| `utils/inputMethod` (`getInputMethod`, `saveInputMethod`, `refreshInputMethodStamp`) | The per-surface pinpoint/drag input-method preference with its TTL. Persists through the `storageBackend` seam; no backend of its own. |
|
|
219
219
|
| `utils/codeHighlight` / `utils/codeBlockMark` / `utils/syntaxTheme` | The Shiki-based fence highlighter, swap-surviving annotation marks, and palette→Shiki theme mapping. Replaces all `.hljs` styling. *(Blessed in 0.29.0.)* |
|
|
220
220
|
| `utils/math` (`loadMathRenderer`, `getMathRenderer`, `getMathRendererSource`, `setMathRenderer`, `setMathRendererLoader`, `getMathRendererLoader`, `resetMathRenderer`) and `utils/math-eager` | The math renderer slot and its eager KaTeX registration. Import `utils/math-eager` for synchronous typesetting on the first commit; call `loadMathRenderer()` to pre-warm the lazy path. `resetMathRenderer()` empties the slot and keeps the registered loader; `setMathRendererLoader(null)` drops it. See "Lazy renderers and eager entries". |
|
|
@@ -956,6 +956,8 @@ that state out of `Viewer`, exactly as with `AnnotationToolstrip`'s
|
|
|
956
956
|
|
|
957
957
|
### 3. `CommentPopover` `mentionSource` — an `@` mention source for the composer
|
|
958
958
|
|
|
959
|
+
> **0.43.2 additions (opt-in, byte-identical when absent):** `MentionSource.heading?: string | null` draws one non-selectable heading row (`data-mention-heading`, e.g. "People in this workspace") above the list; `MentionPerson.avatar?: { url?, initials?, tint? }` draws an avatar (`data-mention-avatar="image" | "initials"`) before the label — an `<img>` when `url` is set, else `initials` (defaulting to the label's first letter) on a disc tinted with `tint` (any CSS color; absent means the muted surface). Neither the grammar, the keyboard state machine, `onMentionsChange`, `onPickBlocked` nor `Annotation.mentions` changes.
|
|
960
|
+
|
|
959
961
|
```ts
|
|
960
962
|
import type { MentionPerson, MentionSource } from "@plannotator/ui/types";
|
|
961
963
|
// (also @plannotator/ui/utils/mentions)
|
|
@@ -971,6 +973,7 @@ interface MentionPerson {
|
|
|
971
973
|
interface MentionSource {
|
|
972
974
|
readonly people: readonly MentionPerson[];
|
|
973
975
|
readonly emptyNotice?: string | null; // honest-empty row
|
|
976
|
+
readonly heading?: string | null; // 0.43.2: heading row above the list, absent → none
|
|
974
977
|
readonly onMentionsChange?: (ids: readonly string[]) => void;
|
|
975
978
|
readonly onPickBlocked?: (person: MentionPerson) => void;
|
|
976
979
|
}
|
|
@@ -1026,7 +1029,7 @@ the people and what a mention means.
|
|
|
1026
1029
|
|
|
1027
1030
|
### Threading points
|
|
1028
1031
|
|
|
1029
|
-
- `AnnotationToolbar` (`selectionActions`, `quickLabels`) — the props live here.
|
|
1032
|
+
- `AnnotationToolbar` (`selectionActions`, `selectionActionsIcon`, `quickLabels`) — the props live here. `selectionActionsIcon?: React.ReactNode` (0.43.1) is the glyph on the wand button, forwarded by `Viewer` (both toolbars) and `HtmlViewer`; absent → the package's own wand, which 0.43.1 also simplified to one thick diagonal with a single star (the six-spark glyph read as noise at 16px). Name, `data-selection-actions`, size and behavior of the button are untouched either way.
|
|
1030
1033
|
- `Viewer` forwards both to BOTH of its toolbars (the text-selection toolbar
|
|
1031
1034
|
and the code-block hover toolbar).
|
|
1032
1035
|
- `HtmlViewer` forwards `selectionActions` to its selection toolbar. It does
|
|
@@ -1038,6 +1041,7 @@ the people and what a mention means.
|
|
|
1038
1041
|
- `CommentPopover` (`mentionSource`). `Viewer` does NOT forward it: the viewer
|
|
1039
1042
|
owns several composers and a per-composer decision belongs to the host that
|
|
1040
1043
|
renders them. Ask if you would rather pass it once on `Viewer`.
|
|
1044
|
+
**Answered in 0.43.1 — both viewers now forward it; see the next section.**
|
|
1041
1045
|
|
|
1042
1046
|
### The no-op guarantee, and how it is pinned
|
|
1043
1047
|
|
|
@@ -1058,8 +1062,118 @@ Tests: `utils/mentions.test.ts` (11, DOM-free),
|
|
|
1058
1062
|
`components/AnnotationToolbar.selectionActions.test.tsx` (8, DOM-gated),
|
|
1059
1063
|
`components/CommentPopover.mentionSource.test.tsx` (11, DOM-gated).
|
|
1060
1064
|
|
|
1065
|
+
## `mentionSource` on the viewers (0.43.1)
|
|
1066
|
+
|
|
1067
|
+
0.43.0 left `mentionSource` on `CommentPopover` alone, with an open question
|
|
1068
|
+
("ask if you would rather pass it once on `Viewer`"). The answer is yes, so
|
|
1069
|
+
0.43.1 threads it one level up and gives the picked ids somewhere to land.
|
|
1070
|
+
Same ruling as 0.43.0: an opt-in host capability that changes nothing for
|
|
1071
|
+
Plannotator's own users when it is not supplied. `packages/editor` and
|
|
1072
|
+
`packages/review-editor` are untouched by this release; core is UNCHANGED at
|
|
1073
|
+
`0.25.5`, so **ui 0.43.1 publishes alone**.
|
|
1074
|
+
|
|
1075
|
+
### 1. The prop
|
|
1076
|
+
|
|
1077
|
+
`Viewer` and `HtmlViewer` each gain `mentionSource?: MentionSource` — the same
|
|
1078
|
+
type, unchanged, from `@plannotator/ui/types` (also `utils/mentions`) — and
|
|
1079
|
+
each forwards it to EVERY comment composer it mounts:
|
|
1080
|
+
|
|
1081
|
+
- `Viewer` → the text-selection composer (`useAnnotationHighlighter`'s) and the
|
|
1082
|
+
global / code-block one.
|
|
1083
|
+
- `HtmlViewer` → the pinpoint (selection) composer and the global one.
|
|
1084
|
+
|
|
1085
|
+
A host that wants mentions on a surface passes one prop instead of reaching
|
|
1086
|
+
into the viewer's composers. Passing it directly to a `CommentPopover` you
|
|
1087
|
+
mount yourself still works and is unchanged.
|
|
1088
|
+
|
|
1089
|
+
Deliberately NOT threaded: `plan-diff/PlanCleanDiffView`, `CodeFilePopout` and
|
|
1090
|
+
`goal-setup/GoalSetupSurface` mount composers too, but they are Plannotator-only
|
|
1091
|
+
surfaces off the supported-import list — the same line 0.43.0 drew for
|
|
1092
|
+
`selectionActions`. Ask if a host needs one.
|
|
1093
|
+
|
|
1094
|
+
### 2. `Annotation.mentions` — where the ids go
|
|
1095
|
+
|
|
1096
|
+
```ts
|
|
1097
|
+
interface Annotation {
|
|
1098
|
+
// …
|
|
1099
|
+
mentions?: readonly string[]; // opaque host ids, additive
|
|
1100
|
+
}
|
|
1101
|
+
```
|
|
1102
|
+
|
|
1103
|
+
`onSubmit(text, images?, mentions?)` used to stop at the viewer: the third
|
|
1104
|
+
argument was received and dropped. It now rides onto the annotation the viewer
|
|
1105
|
+
hands `onAddAnnotation`, on every creation path behind those composers
|
|
1106
|
+
(`createAnnotationFromSource`, `createAnnotationFromMathSource`, the code-block
|
|
1107
|
+
path, both global comments, and the HTML pinpoint comment).
|
|
1108
|
+
|
|
1109
|
+
**The presence rule** is the whole no-op guarantee, so it is worth stating
|
|
1110
|
+
exactly: the key exists only when a `mentionSource` was supplied AND at least
|
|
1111
|
+
one id survived to submit. No source → no third argument → no key. A source
|
|
1112
|
+
whose tokens the author deleted before submitting → `[]` from the composer →
|
|
1113
|
+
still no key, never an empty array. Every write is a conditional spread
|
|
1114
|
+
(`...(mentions && mentions.length > 0 ? { mentions } : {})`), not `mentions,`,
|
|
1115
|
+
because the bare shorthand would put the key on the object with an `undefined`
|
|
1116
|
+
value and `'mentions' in ann` would start answering true for Plannotator.
|
|
1117
|
+
|
|
1118
|
+
The ids are opaque to the package. Mapping one to a person, notifying them, or
|
|
1119
|
+
rendering an avatar is entirely the host's business — `MentionPerson.id` is
|
|
1120
|
+
reported back verbatim, exactly as it was handed in.
|
|
1121
|
+
|
|
1122
|
+
### 3. What the field does NOT touch
|
|
1123
|
+
|
|
1124
|
+
An id from a host's directory means nothing outside that host, so the field
|
|
1125
|
+
stays out of everything Plannotator produces:
|
|
1126
|
+
|
|
1127
|
+
- **Export.** `exportAnnotations`, `exportAnnotationEntry` and
|
|
1128
|
+
`exportLinkedDocAnnotations` never print it: an annotation carrying
|
|
1129
|
+
`mentions` exports byte-identically to the same annotation without it
|
|
1130
|
+
(`utils/parser.mentions.test.ts`). The readable `@Label` token is in the
|
|
1131
|
+
comment body, which is what the coding agent reads.
|
|
1132
|
+
- **Share links.** Dropped exactly like `htmlAnchor`, `elementContext` and
|
|
1133
|
+
`diagramAnchor` — the compact tuple format never carried extra fields, and a
|
|
1134
|
+
round trip restores `mentions: undefined` (pinned in
|
|
1135
|
+
`utils/sharing.multiTarget.test.ts`).
|
|
1136
|
+
- **External annotations.** `POST /api/external-annotations` builds its rows
|
|
1137
|
+
from an explicit field list and `PATCH` from an allowlist, so a `mentions`
|
|
1138
|
+
key on the wire is dropped as any unknown key is. No change was needed in
|
|
1139
|
+
`@plannotator/core/external-annotation`, in either runtime.
|
|
1140
|
+
- **The feedback archive.** `packages/shared/feedback-archive.ts` copies named
|
|
1141
|
+
fields into its record; `mentions` is not one of them and never reaches
|
|
1142
|
+
`index.jsonl` or a sidecar. No change needed.
|
|
1143
|
+
- **Drafts** carry it for free (annotations are opaque JSON to the draft
|
|
1144
|
+
transport), which is the behavior a host wants: a restored draft still knows
|
|
1145
|
+
who was named.
|
|
1146
|
+
|
|
1147
|
+
### 4. Edit and reply paths
|
|
1148
|
+
|
|
1149
|
+
There is none to thread in these two viewers: both `Viewer` composers and both
|
|
1150
|
+
`HtmlViewer` composers are CREATION composers. Editing an existing comment
|
|
1151
|
+
happens in `AnnotationPanel`'s card, a plain textarea that has never had an
|
|
1152
|
+
`@` picker and takes no `mentionSource`; replies (`inReplyTo`) are created by
|
|
1153
|
+
the WebMCP catalog, not by a composer. So no annotation's `mentions` is
|
|
1154
|
+
rewritten after creation by this package — a host that edits a comment owns
|
|
1155
|
+
the field from then on. If you want the panel editor to pick people too, that
|
|
1156
|
+
is a separate prop on `AnnotationPanel` and worth asking for.
|
|
1157
|
+
|
|
1158
|
+
### No-op guarantee, and how it is pinned
|
|
1159
|
+
|
|
1160
|
+
With no `mentionSource`, both viewers mount the composers they always did, no
|
|
1161
|
+
`@` listener is registered, no picker DOM exists, and the annotation object
|
|
1162
|
+
handed to `onAddAnnotation` has no `mentions` key at all (`'mentions' in ann`
|
|
1163
|
+
is false, asserted rather than `toBeUndefined()` — the difference between the
|
|
1164
|
+
conditional spread and the bare shorthand is invisible to the latter).
|
|
1165
|
+
|
|
1166
|
+
Tests (both DOM-gated, both in the workflow's DOM_TESTS step):
|
|
1167
|
+
`components/Viewer.mentionSource.test.tsx` (5) drives the real Viewer — the
|
|
1168
|
+
selection composer through the Vim toolbar's type-to-comment gesture and the
|
|
1169
|
+
global composer through its button — and
|
|
1170
|
+
`components/html-viewer/HtmlViewer.mentionSource.test.tsx` (3) drives the real
|
|
1171
|
+
HtmlViewer through a bridge pinpoint message and its global button. Plus the two DOM-free pins in
|
|
1172
|
+
§3 above.
|
|
1173
|
+
|
|
1061
1174
|
## Publishing & versioning
|
|
1062
1175
|
|
|
1176
|
+
- **ui 0.43.1 (`mentionSource` on the viewers): `@plannotator/ui` only — `@plannotator/core` is UNCHANGED at `0.25.5`, so this publishes alone** (core 0.25.5 must already be published). Purely additive over 0.43.0: `mentionSource` on `Viewer` and `HtmlViewer` (forwarded to every comment composer each mounts) and the optional `Annotation.mentions` field the picked ids land on, set only when a source was supplied and a token survived. Nothing is removed, no new modules, no export-, share- or archive-visible change, and Plannotator passes none of it. See "`mentionSource` on the viewers (0.43.1)".
|
|
1063
1177
|
- **ui 0.43.0 (host toolbar seams): `@plannotator/ui` only — `@plannotator/core` is UNCHANGED at `0.25.5`, so this publishes alone** (core 0.25.5 must already be published). Purely additive: `selectionActions` + `quickLabels` on `AnnotationToolbar` (forwarded by `Viewer`; `selectionActions` also by `HtmlViewer`), `mentionSource` on `CommentPopover`, an optional third `mentions` argument on that component's `onSubmit`, and the new supported modules `utils/selectionActions`, `utils/mentions`, `components/SelectionActionsDropdown`, `components/MentionPicker`, `hooks/useMentionAutocomplete`. Nothing is removed and Plannotator passes none of it. See "Host toolbar seams (0.43.0)".
|
|
1064
1178
|
- **core 0.25.5 / ui 0.42.0 (diagram FILES, `.mmd`/`.mermaid`/`.dot`/`.gv`): additive on both packages, so the next publish is core-first.** `@plannotator/core/annotatable` gains `DiagramRenderKind`, `diagramRenderKindForPath`, `isDiagramRenderKind` and `annotateDiagramRenderKind`, and its built-in annotatable sets now include the four diagram extensions (`shouldStripFrontmatter` returns false for them — Mermaid's `--- … ---` config block is content — and they can no longer be registered through `markdownExtensions`). `@plannotator/ui` gains `diagramDocumentBlocks(text, kind)` on `utils/parser` (the ONE `code` block a whole-file diagram source renders as), `shareableDocumentMarkdown(markdown, renderAs)` on `utils/sharing`, the `DocumentRenderAs` type on `types` (`'markdown' | 'html' | DiagramRenderKind`, re-exporting core's kind), and an optional `Block.diagramSourceLineOffset` that `DiagramBlock` prefers over `Block.startLine` when resolving a diagram comment's `sourceLine` (unset on every parser-produced fence, so fences are byte-identical). `useLinkedDoc`'s `renderAs`/`setRenderAs`/`LinkedDocLoadData.renderAs` widen from `'markdown' | 'html'` to `DocumentRenderAs` — source-compatible for a host that only ever passes the old two, but a host whose own state is typed `'markdown' | 'html'` must widen its setter. Since core changes, **publish `core` first** and update UI's exact core dependency before packing ui.
|
|
1065
1179
|
- **The current pair is `@plannotator/ui` `0.41.2` on `@plannotator/core` `0.25.4`.** Core is UNCHANGED from 0.41.1, so 0.41.2 publishes alone (`ui` only; core 0.25.4 must already be published). 0.41.2 ships the palette-derived Mermaid node shadow at a default of 70% (`DEFAULT_MERMAID_SHADOW_AMOUNT`) and the Settings → Display "Diagram Shadow" control (0 / 40 / 70 / 100); see "Node shadow (the one thing that is not a colour)" under "Theme-aware Mermaid diagrams (0.40.0)" for the mapping — no API removal, only additive exports (`buildMermaidShadow`, `DEFAULT_MERMAID_SHADOW_AMOUNT`, `utils/diagramShadow`).
|
package/README.md
CHANGED
|
@@ -238,10 +238,16 @@ in 0.42.0 (proven by diffing the mounted `outerHTML` against the base commit).
|
|
|
238
238
|
`startOffset: 0` on a surface with no blocks, such as raw HTML) — and the
|
|
239
239
|
toolbar closes. **The package creates no annotation:** what an action does is yours.
|
|
240
240
|
An empty array renders no button.
|
|
241
|
+
- **`AnnotationToolbar` `selectionActionsIcon`** (forwarded by `Viewer` and
|
|
242
|
+
`HtmlViewer`): the glyph on the `selectionActions` button, so a host can match
|
|
243
|
+
the wand it draws elsewhere. Absent means the package's own wand; nothing else
|
|
244
|
+
about the button changes.
|
|
241
245
|
- **`AnnotationToolbar` `quickLabels`** (default `true`): `false` hides the Zap
|
|
242
246
|
picker and makes the Alt+digit label shortcuts inert on that toolbar. The
|
|
243
247
|
one-click 👍 is unaffected, and mode state is still yours to clamp.
|
|
244
|
-
- **`CommentPopover` `mentionSource
|
|
248
|
+
- **`CommentPopover` `mentionSource`** (0.43.2 adds `heading?` above the list
|
|
249
|
+
and an optional `avatar?: { url?, initials?, tint? }` per person, drawn before
|
|
250
|
+
the label; both absent → the 0.43.1 rows): `{ people, emptyNotice?,
|
|
245
251
|
onMentionsChange?, onPickBlocked? }` over `MentionPerson`
|
|
246
252
|
(`{ id, kind, label, detail, canOpen }`). Typing `@` at a word boundary opens
|
|
247
253
|
a portaled picker measured from the textarea; picking inserts the readable
|
|
@@ -252,9 +258,17 @@ in 0.42.0 (proven by diffing the mounted `outerHTML` against the base commit).
|
|
|
252
258
|
no-access dialog) and inserts normally when you do not. Not wired to any
|
|
253
259
|
Plannotator data and not a `configurePlannotatorUI` seam — pass it where you
|
|
254
260
|
render the composer.
|
|
261
|
+
- **`Viewer` / `HtmlViewer` `mentionSource`** (0.43.1): the same prop on the two
|
|
262
|
+
viewers, forwarded to every comment composer each of them mounts, so a host
|
|
263
|
+
wires mentions once per surface instead of per composer. The ids the author
|
|
264
|
+
kept ride onto the created annotation as `Annotation.mentions`
|
|
265
|
+
(`readonly string[]`, present only when a source was supplied and at least
|
|
266
|
+
one token survived). It is host data: the package never renders, exports,
|
|
267
|
+
shares or archives it.
|
|
255
268
|
|
|
256
269
|
See HANDOFF.md § "Host toolbar seams (0.43.0)" for the grammar, the keyboard
|
|
257
|
-
rules and the threading points.
|
|
270
|
+
rules and the threading points, and § "mentionSource on the viewers (0.43.1)"
|
|
271
|
+
for the two viewer props and the annotation field.
|
|
258
272
|
|
|
259
273
|
### WebMCP provider (`@plannotator/ui/webmcp`; 0.32.0)
|
|
260
274
|
|
|
@@ -44,6 +44,12 @@ interface AnnotationToolbarProps {
|
|
|
44
44
|
* Absent or empty → no button, no dropdown: today's toolbar.
|
|
45
45
|
*/
|
|
46
46
|
selectionActions?: SelectionAction[];
|
|
47
|
+
/**
|
|
48
|
+
* Opt-in host capability: the glyph on the `selectionActions` button, so a
|
|
49
|
+
* host can match the wand it draws elsewhere. Absent → the package's own
|
|
50
|
+
* wand. Nothing else about the button changes (name, data attribute, size).
|
|
51
|
+
*/
|
|
52
|
+
selectionActionsIcon?: React.ReactNode;
|
|
47
53
|
/**
|
|
48
54
|
* Whether the package's own quick labels are offered (default true).
|
|
49
55
|
* `false` hides the Zap picker button and the Alt+digit label shortcuts on
|
|
@@ -69,6 +75,7 @@ export const AnnotationToolbar: React.FC<AnnotationToolbarProps> = ({
|
|
|
69
75
|
onRequestComment,
|
|
70
76
|
onQuickLabel,
|
|
71
77
|
selectionActions,
|
|
78
|
+
selectionActionsIcon,
|
|
72
79
|
quickLabels: quickLabelsEnabled = true,
|
|
73
80
|
copyText,
|
|
74
81
|
commentOnly = false,
|
|
@@ -273,7 +280,7 @@ export const AnnotationToolbar: React.FC<AnnotationToolbarProps> = ({
|
|
|
273
280
|
<ToolbarButton
|
|
274
281
|
ref={actionsButtonRef}
|
|
275
282
|
onClick={() => setShowSelectionActions(prev => !prev)}
|
|
276
|
-
icon={<WandIcon />}
|
|
283
|
+
icon={selectionActionsIcon ?? <WandIcon />}
|
|
277
284
|
label="Actions"
|
|
278
285
|
className={selectionActionsOpen ? "text-primary bg-primary/10" : "text-primary hover:bg-primary/10"}
|
|
279
286
|
dataAttributes={{ 'data-selection-actions': 'true' }}
|
|
@@ -370,9 +377,11 @@ const ZapIcon = () => (
|
|
|
370
377
|
</svg>
|
|
371
378
|
);
|
|
372
379
|
|
|
380
|
+
// One thick diagonal wand with a single four-point star at its tip: the
|
|
381
|
+
// earlier glyph carried six sparks and read as noise at 16px.
|
|
373
382
|
const WandIcon = () => (
|
|
374
|
-
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
375
|
-
<path strokeLinecap="round" strokeLinejoin="round" d="
|
|
383
|
+
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
|
|
384
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M3 21L13.5 10.5M17 3v7M13.5 6.5h7" />
|
|
376
385
|
</svg>
|
|
377
386
|
);
|
|
378
387
|
|
|
@@ -694,6 +694,7 @@ export const CommentPopover: React.FC<CommentPopoverProps> = ({
|
|
|
694
694
|
id={mentionListboxId}
|
|
695
695
|
people={mentionAc.menu.items}
|
|
696
696
|
emptyNotice={mentionAc.menu.emptyNotice}
|
|
697
|
+
heading={mentionAc.menu.heading}
|
|
697
698
|
active={mentionAc.menu.activeIndex}
|
|
698
699
|
anchor={mentionAc.menu.anchor}
|
|
699
700
|
onPick={(person) => {
|
|
@@ -858,6 +859,7 @@ export const CommentPopover: React.FC<CommentPopoverProps> = ({
|
|
|
858
859
|
id={mentionListboxId}
|
|
859
860
|
people={mentionAc.menu.items}
|
|
860
861
|
emptyNotice={mentionAc.menu.emptyNotice}
|
|
862
|
+
heading={mentionAc.menu.heading}
|
|
861
863
|
active={mentionAc.menu.activeIndex}
|
|
862
864
|
anchor={mentionAc.menu.anchor}
|
|
863
865
|
onPick={(person) => {
|
|
@@ -34,10 +34,45 @@ export function mentionAnchorOf(element: HTMLElement | null): MentionAnchor | nu
|
|
|
34
34
|
return { top: rect.top, bottom: rect.bottom, left: rect.left, width: rect.width };
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/** The optional avatar column: an image, else initials on a tinted disc. */
|
|
38
|
+
function MentionAvatar({
|
|
39
|
+
avatar,
|
|
40
|
+
label,
|
|
41
|
+
}: {
|
|
42
|
+
readonly avatar: NonNullable<MentionPerson['avatar']>;
|
|
43
|
+
readonly label: string;
|
|
44
|
+
}) {
|
|
45
|
+
if (avatar.url) {
|
|
46
|
+
return (
|
|
47
|
+
<img
|
|
48
|
+
data-mention-avatar="image"
|
|
49
|
+
src={avatar.url}
|
|
50
|
+
alt=""
|
|
51
|
+
aria-hidden="true"
|
|
52
|
+
className="h-4 w-4 shrink-0 rounded-full object-cover"
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
const initials = (avatar.initials ?? label.trim().charAt(0)).slice(0, 2).toUpperCase();
|
|
57
|
+
return (
|
|
58
|
+
<span
|
|
59
|
+
data-mention-avatar="initials"
|
|
60
|
+
aria-hidden="true"
|
|
61
|
+
style={avatar.tint ? { backgroundColor: avatar.tint } : undefined}
|
|
62
|
+
className={`flex h-4 w-4 shrink-0 items-center justify-center rounded-full text-[8px] font-semibold leading-none ${
|
|
63
|
+
avatar.tint ? 'text-white' : 'bg-muted text-foreground/80'
|
|
64
|
+
}`}
|
|
65
|
+
>
|
|
66
|
+
{initials}
|
|
67
|
+
</span>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
37
71
|
export function MentionPicker({
|
|
38
72
|
id,
|
|
39
73
|
people,
|
|
40
74
|
emptyNotice,
|
|
75
|
+
heading,
|
|
41
76
|
active,
|
|
42
77
|
anchor,
|
|
43
78
|
onPick,
|
|
@@ -48,6 +83,8 @@ export function MentionPicker({
|
|
|
48
83
|
readonly people: readonly MentionPerson[];
|
|
49
84
|
/** Shown as one non-selectable row when `people` is empty. */
|
|
50
85
|
readonly emptyNotice?: string | null;
|
|
86
|
+
/** Optional heading above the list. Absent → nothing rendered. */
|
|
87
|
+
readonly heading?: string | null;
|
|
51
88
|
/** Index of the arrow-focused row, or null for "nothing preselected". */
|
|
52
89
|
readonly active: number | null;
|
|
53
90
|
readonly anchor: MentionAnchor | null;
|
|
@@ -70,6 +107,11 @@ export function MentionPicker({
|
|
|
70
107
|
style={{ position: 'fixed', left: anchor.left, width: anchor.width, ...style }}
|
|
71
108
|
className="z-[120] max-h-48 overflow-y-auto rounded-md border border-border bg-popover p-1 shadow-xl"
|
|
72
109
|
>
|
|
110
|
+
{heading && (
|
|
111
|
+
<p data-mention-heading className="px-2 pb-1 pt-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground">
|
|
112
|
+
{heading}
|
|
113
|
+
</p>
|
|
114
|
+
)}
|
|
73
115
|
{people.length === 0 ? (
|
|
74
116
|
<p data-mention-empty className="px-2 py-1 text-[11px] text-muted-foreground">
|
|
75
117
|
{emptyNotice}
|
|
@@ -94,6 +136,7 @@ export function MentionPicker({
|
|
|
94
136
|
active === index ? 'bg-muted text-foreground' : 'text-foreground/85 hover:bg-muted/60'
|
|
95
137
|
}`}
|
|
96
138
|
>
|
|
139
|
+
{person.avatar && <MentionAvatar avatar={person.avatar} label={person.label} />}
|
|
97
140
|
<span className="min-w-0 flex-1 truncate">{person.label}</span>
|
|
98
141
|
{person.detail && (
|
|
99
142
|
// The NAME is what the person reads; the detail gives way first.
|
package/components/Viewer.tsx
CHANGED
|
@@ -60,6 +60,7 @@ import { isGraphvizLanguage, isMermaidLanguage } from './diagramLanguages';
|
|
|
60
60
|
import { getIdentity } from '../utils/identity';
|
|
61
61
|
import { type QuickLabel } from '../utils/quickLabels';
|
|
62
62
|
import type { SelectionAction } from '../utils/selectionActions';
|
|
63
|
+
import type { MentionSource } from '../utils/mentions';
|
|
63
64
|
import { DocBadges, type DocBadgesProps, type LinkedDocBadgeInfo } from './DocBadges';
|
|
64
65
|
import { PinpointOverlay } from './PinpointOverlay';
|
|
65
66
|
import { usePinpoint } from '../hooks/usePinpoint';
|
|
@@ -98,12 +99,22 @@ export interface ViewerProps {
|
|
|
98
99
|
* one wand button that opens the package's dropdown. Absent → unchanged.
|
|
99
100
|
*/
|
|
100
101
|
selectionActions?: SelectionAction[];
|
|
102
|
+
/** Opt-in host capability: the glyph on the `selectionActions` button on
|
|
103
|
+
* both toolbars. Absent → the package's own wand. */
|
|
104
|
+
selectionActionsIcon?: React.ReactNode;
|
|
101
105
|
/**
|
|
102
106
|
* Whether the package's quick labels are offered on the selection toolbars
|
|
103
107
|
* (default true). `false` hides the Zap picker and the Alt+digit label
|
|
104
108
|
* shortcuts; the 👍 button is unaffected.
|
|
105
109
|
*/
|
|
106
110
|
quickLabels?: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Opt-in host capability, forwarded to BOTH comment composers this viewer
|
|
113
|
+
* mounts (the text-selection composer and the global / code-block one): the
|
|
114
|
+
* `@` mention source for the composer's picker. The picked ids ride onto the
|
|
115
|
+
* created annotation as `Annotation.mentions`. Absent → unchanged.
|
|
116
|
+
*/
|
|
117
|
+
mentionSource?: MentionSource;
|
|
107
118
|
blocks: Block[];
|
|
108
119
|
markdown: string;
|
|
109
120
|
frontmatter?: Frontmatter | null;
|
|
@@ -374,7 +385,9 @@ export const Viewer = forwardRef<ViewerHandle, ViewerProps>(({
|
|
|
374
385
|
inputMethod = 'drag',
|
|
375
386
|
taterMode,
|
|
376
387
|
selectionActions,
|
|
388
|
+
selectionActionsIcon,
|
|
377
389
|
quickLabels,
|
|
390
|
+
mentionSource,
|
|
378
391
|
globalAttachments = [],
|
|
379
392
|
onAddGlobalAttachment,
|
|
380
393
|
onRemoveGlobalAttachment,
|
|
@@ -558,6 +571,7 @@ export const Viewer = forwardRef<ViewerHandle, ViewerProps>(({
|
|
|
558
571
|
images?: ImageAttachment[],
|
|
559
572
|
isQuickLabel?: boolean,
|
|
560
573
|
quickLabelTip?: string,
|
|
574
|
+
mentions?: readonly string[],
|
|
561
575
|
) => {
|
|
562
576
|
if (readOnlyRef.current) return;
|
|
563
577
|
|
|
@@ -577,6 +591,9 @@ export const Viewer = forwardRef<ViewerHandle, ViewerProps>(({
|
|
|
577
591
|
createdA: Date.now(),
|
|
578
592
|
author: getIdentity(),
|
|
579
593
|
images,
|
|
594
|
+
// Host capability: present only when a mentionSource was supplied AND a
|
|
595
|
+
// token survived, so a code-block comment without one is unchanged.
|
|
596
|
+
...(mentions && mentions.length > 0 ? { mentions } : {}),
|
|
580
597
|
...(isQuickLabel ? { isQuickLabel: true } : {}),
|
|
581
598
|
...(quickLabelTip ? { quickLabelTip } : {}),
|
|
582
599
|
};
|
|
@@ -957,7 +974,11 @@ export const Viewer = forwardRef<ViewerHandle, ViewerProps>(({
|
|
|
957
974
|
setCodeBlockToolbar(null);
|
|
958
975
|
};
|
|
959
976
|
|
|
960
|
-
const handleViewerCommentSubmit = (
|
|
977
|
+
const handleViewerCommentSubmit = (
|
|
978
|
+
text: string,
|
|
979
|
+
images?: ImageAttachment[],
|
|
980
|
+
mentions?: readonly string[],
|
|
981
|
+
) => {
|
|
961
982
|
if (readOnlyRef.current || !viewerCommentPopover) return;
|
|
962
983
|
|
|
963
984
|
if (viewerCommentPopover.isGlobal) {
|
|
@@ -975,12 +996,22 @@ export const Viewer = forwardRef<ViewerHandle, ViewerProps>(({
|
|
|
975
996
|
createdA: Date.now(),
|
|
976
997
|
author: getIdentity(),
|
|
977
998
|
images,
|
|
999
|
+
...(mentions && mentions.length > 0 ? { mentions } : {}),
|
|
978
1000
|
};
|
|
979
1001
|
onAddAnnotation(newAnnotation);
|
|
980
1002
|
} else if (viewerCommentPopover.codeBlock) {
|
|
981
1003
|
const codeEl = viewerCommentPopover.codeBlock.element.querySelector('code');
|
|
982
1004
|
if (codeEl) {
|
|
983
|
-
applyCodeBlockAnnotation(
|
|
1005
|
+
applyCodeBlockAnnotation(
|
|
1006
|
+
viewerCommentPopover.codeBlock.block.id,
|
|
1007
|
+
codeEl,
|
|
1008
|
+
AnnotationType.COMMENT,
|
|
1009
|
+
text,
|
|
1010
|
+
images,
|
|
1011
|
+
undefined,
|
|
1012
|
+
undefined,
|
|
1013
|
+
mentions,
|
|
1014
|
+
);
|
|
984
1015
|
}
|
|
985
1016
|
}
|
|
986
1017
|
|
|
@@ -1312,6 +1343,7 @@ export const Viewer = forwardRef<ViewerHandle, ViewerProps>(({
|
|
|
1312
1343
|
onRequestComment={handleRequestComment}
|
|
1313
1344
|
onQuickLabel={handleQuickLabel}
|
|
1314
1345
|
selectionActions={selectionActions}
|
|
1346
|
+
selectionActionsIcon={selectionActionsIcon}
|
|
1315
1347
|
quickLabels={quickLabels}
|
|
1316
1348
|
copyText={toolbarState.selectionText}
|
|
1317
1349
|
hideCopyButton={!isTouchDevice}
|
|
@@ -1369,6 +1401,7 @@ export const Viewer = forwardRef<ViewerHandle, ViewerProps>(({
|
|
|
1369
1401
|
onRequestComment={handleCodeBlockRequestComment}
|
|
1370
1402
|
onQuickLabel={handleCodeBlockQuickLabel}
|
|
1371
1403
|
selectionActions={selectionActions}
|
|
1404
|
+
selectionActionsIcon={selectionActionsIcon}
|
|
1372
1405
|
quickLabels={quickLabels}
|
|
1373
1406
|
isExiting={isCodeBlockToolbarExiting}
|
|
1374
1407
|
onMouseEnter={() => {
|
|
@@ -1447,6 +1480,7 @@ export const Viewer = forwardRef<ViewerHandle, ViewerProps>(({
|
|
|
1447
1480
|
draftKey={`plan:${commentDraftScope}:${hookCommentPopover.draftKey}`}
|
|
1448
1481
|
onSubmit={hookCommentSubmit}
|
|
1449
1482
|
onClose={hookCommentClose}
|
|
1483
|
+
mentionSource={mentionSource}
|
|
1450
1484
|
allowImages={allowImages}
|
|
1451
1485
|
skillReferences
|
|
1452
1486
|
onAskAI={onAskAI}
|
|
@@ -1471,6 +1505,7 @@ export const Viewer = forwardRef<ViewerHandle, ViewerProps>(({
|
|
|
1471
1505
|
}`}
|
|
1472
1506
|
onSubmit={handleViewerCommentSubmit}
|
|
1473
1507
|
onClose={handleViewerCommentClose}
|
|
1508
|
+
mentionSource={mentionSource}
|
|
1474
1509
|
allowImages={allowImages}
|
|
1475
1510
|
skillReferences
|
|
1476
1511
|
onAskAI={onAskAI}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
useMemo,
|
|
7
7
|
useRef,
|
|
8
8
|
useState,
|
|
9
|
+
type ReactNode,
|
|
9
10
|
} from "react";
|
|
10
11
|
import { createPortal } from "react-dom";
|
|
11
12
|
import { useVimDocumentFocus } from "../../hooks/useVimDocumentFocus";
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
} from "../../utils/vimHud";
|
|
26
27
|
import { AnnotationToolbar } from "../AnnotationToolbar";
|
|
27
28
|
import type { SelectionAction } from "../../utils/selectionActions";
|
|
29
|
+
import type { MentionSource } from "../../utils/mentions";
|
|
28
30
|
import { AttachmentsButton } from "../AttachmentsButton";
|
|
29
31
|
import {
|
|
30
32
|
CommentPopover,
|
|
@@ -270,6 +272,14 @@ export interface HtmlViewerProps {
|
|
|
270
272
|
* toolbar: the host's own commands for the current selection, rendered as
|
|
271
273
|
* one wand button that opens the package's dropdown. Absent → unchanged. */
|
|
272
274
|
selectionActions?: SelectionAction[];
|
|
275
|
+
/** Opt-in host capability: the glyph on the `selectionActions` button.
|
|
276
|
+
* Absent → the package's own wand. */
|
|
277
|
+
selectionActionsIcon?: ReactNode;
|
|
278
|
+
/** Opt-in host capability, forwarded to BOTH comment composers this viewer
|
|
279
|
+
* mounts (the pinpoint/selection composer and the global one): the `@`
|
|
280
|
+
* mention source for the composer's picker. The picked ids ride onto the
|
|
281
|
+
* created annotation as `Annotation.mentions`. Absent → unchanged. */
|
|
282
|
+
mentionSource?: MentionSource;
|
|
273
283
|
/** scrollIntoView behavior when a selected annotation is scrolled into
|
|
274
284
|
* view inside the page. Default 'smooth'; pass 'auto' to carry the
|
|
275
285
|
* parent's reduced-motion preference across the iframe boundary. */
|
|
@@ -358,6 +368,8 @@ export const HtmlViewer = forwardRef<ViewerHandle, HtmlViewerProps>(
|
|
|
358
368
|
onUnanchoredChange,
|
|
359
369
|
maxAdditionalTargets,
|
|
360
370
|
selectionActions,
|
|
371
|
+
selectionActionsIcon,
|
|
372
|
+
mentionSource,
|
|
361
373
|
scrollBehavior,
|
|
362
374
|
title = "HTML Plan Viewer",
|
|
363
375
|
bridgeScriptUrl,
|
|
@@ -995,7 +1007,7 @@ export const HtmlViewer = forwardRef<ViewerHandle, HtmlViewerProps>(
|
|
|
995
1007
|
}));
|
|
996
1008
|
|
|
997
1009
|
const handleGlobalCommentSubmit = useCallback(
|
|
998
|
-
(text: string, images?: ImageAttachment[]) => {
|
|
1010
|
+
(text: string, images?: ImageAttachment[], mentions?: readonly string[]) => {
|
|
999
1011
|
if (readOnly) return;
|
|
1000
1012
|
onAddAnnotation({
|
|
1001
1013
|
id: `global-${Date.now()}`,
|
|
@@ -1008,6 +1020,7 @@ export const HtmlViewer = forwardRef<ViewerHandle, HtmlViewerProps>(
|
|
|
1008
1020
|
author: getIdentity(),
|
|
1009
1021
|
createdA: Date.now(),
|
|
1010
1022
|
images,
|
|
1023
|
+
...(mentions && mentions.length > 0 ? { mentions } : {}),
|
|
1011
1024
|
});
|
|
1012
1025
|
setGlobalCommentPopover(null);
|
|
1013
1026
|
},
|
|
@@ -1209,6 +1222,7 @@ export const HtmlViewer = forwardRef<ViewerHandle, HtmlViewerProps>(
|
|
|
1209
1222
|
// future toolbar path can emit an arbitrary label here.
|
|
1210
1223
|
commentOnly
|
|
1211
1224
|
selectionActions={selectionActions}
|
|
1225
|
+
selectionActionsIcon={selectionActionsIcon}
|
|
1212
1226
|
onQuickLabel={(label) => {
|
|
1213
1227
|
if (label.id === THUMBS_UP_LABEL.id) hook.handleQuickLabel(label);
|
|
1214
1228
|
}}
|
|
@@ -1229,6 +1243,7 @@ export const HtmlViewer = forwardRef<ViewerHandle, HtmlViewerProps>(
|
|
|
1229
1243
|
isGlobal={false}
|
|
1230
1244
|
draftKey={`html:${hook.commentPopover.draftKey}`}
|
|
1231
1245
|
onSubmit={hook.handleCommentSubmit}
|
|
1246
|
+
mentionSource={mentionSource}
|
|
1232
1247
|
// Pinpoint clicks open this composer directly, so it carries
|
|
1233
1248
|
// the surface's one-click "Looks good" (the global composer
|
|
1234
1249
|
// does not: a document-wide thumbs-up is not a thing).
|
|
@@ -1260,6 +1275,7 @@ export const HtmlViewer = forwardRef<ViewerHandle, HtmlViewerProps>(
|
|
|
1260
1275
|
isGlobal={true}
|
|
1261
1276
|
onSubmit={handleGlobalCommentSubmit}
|
|
1262
1277
|
onClose={() => setGlobalCommentPopover(null)}
|
|
1278
|
+
mentionSource={mentionSource}
|
|
1263
1279
|
skillReferences
|
|
1264
1280
|
onAskAI={onAskAI}
|
|
1265
1281
|
askAIContext={{ kind: "general", label: "Document" }}
|
|
@@ -911,7 +911,7 @@ export function useHtmlAnnotation({
|
|
|
911
911
|
);
|
|
912
912
|
|
|
913
913
|
const handleCommentSubmit = useCallback(
|
|
914
|
-
(comment: string, images?: ImageAttachment[]) => {
|
|
914
|
+
(comment: string, images?: ImageAttachment[], mentions?: readonly string[]) => {
|
|
915
915
|
if (!enabledRef.current) return;
|
|
916
916
|
// Prefer the text captured when the popover opened — it can't be clobbered by
|
|
917
917
|
// a later selection change or clear while the user is composing the comment.
|
|
@@ -944,6 +944,9 @@ export function useHtmlAnnotation({
|
|
|
944
944
|
author: getIdentity(),
|
|
945
945
|
createdA: Date.now(),
|
|
946
946
|
images,
|
|
947
|
+
// Host capability: present only when a mentionSource was supplied AND
|
|
948
|
+
// a token survived, so a comment without one is unchanged.
|
|
949
|
+
...(mentions && mentions.length > 0 ? { mentions } : {}),
|
|
947
950
|
htmlAnchor: pendingAnchorRef.current ?? undefined,
|
|
948
951
|
elementContext: pendingContextRef.current ?? undefined,
|
|
949
952
|
htmlAdditionalTargets: additionalTargets,
|
|
@@ -631,7 +631,13 @@ export interface UseAnnotationHighlighterReturn {
|
|
|
631
631
|
handleQuickLabel: (label: QuickLabel) => void;
|
|
632
632
|
handleToolbarClose: () => void;
|
|
633
633
|
handleRequestComment: (initialChar?: string) => void;
|
|
634
|
-
|
|
634
|
+
/** The composer's submit. `mentions` arrives only from a `CommentPopover`
|
|
635
|
+
* the host gave a `mentionSource`; the ids ride onto the new annotation. */
|
|
636
|
+
handleCommentSubmit: (
|
|
637
|
+
text: string,
|
|
638
|
+
images?: ImageAttachment[],
|
|
639
|
+
mentions?: readonly string[],
|
|
640
|
+
) => void;
|
|
635
641
|
handleCommentClose: () => void;
|
|
636
642
|
handleFloatingQuickLabel: (label: QuickLabel) => void;
|
|
637
643
|
handleQuickLabelPickerDismiss: () => void;
|
|
@@ -870,6 +876,7 @@ export function useAnnotationHighlighter({
|
|
|
870
876
|
images?: ImageAttachment[],
|
|
871
877
|
isQuickLabel?: boolean,
|
|
872
878
|
quickLabelTip?: string,
|
|
879
|
+
mentions?: readonly string[],
|
|
873
880
|
) => {
|
|
874
881
|
const doms = highlighter.getDoms(source.id);
|
|
875
882
|
let blockId = '';
|
|
@@ -904,6 +911,9 @@ export function useAnnotationHighlighter({
|
|
|
904
911
|
startMeta: source.startMeta,
|
|
905
912
|
endMeta: source.endMeta,
|
|
906
913
|
images,
|
|
914
|
+
// Host capability: present only when a mentionSource was supplied AND a
|
|
915
|
+
// token survived, so an annotation created without one is unchanged.
|
|
916
|
+
...(mentions && mentions.length > 0 ? { mentions } : {}),
|
|
907
917
|
...(mathTargets.length > 0 ? {
|
|
908
918
|
mathTargets: mathTargets.map(target => ({
|
|
909
919
|
blockId: target.blockId,
|
|
@@ -933,6 +943,7 @@ export function useAnnotationHighlighter({
|
|
|
933
943
|
images?: ImageAttachment[],
|
|
934
944
|
isQuickLabel?: boolean,
|
|
935
945
|
quickLabelTip?: string,
|
|
946
|
+
mentions?: readonly string[],
|
|
936
947
|
) => {
|
|
937
948
|
const id = annotationId();
|
|
938
949
|
applyMathAnnotationClass(source.element, id, type, source.displayMode);
|
|
@@ -951,6 +962,7 @@ export function useAnnotationHighlighter({
|
|
|
951
962
|
createdA: Date.now(),
|
|
952
963
|
author: getIdentity(),
|
|
953
964
|
images,
|
|
965
|
+
...(mentions && mentions.length > 0 ? { mentions } : {}),
|
|
954
966
|
...(isQuickLabel ? { isQuickLabel: true } : {}),
|
|
955
967
|
...(quickLabelTip ? { quickLabelTip } : {}),
|
|
956
968
|
};
|
|
@@ -1695,7 +1707,11 @@ export function useAnnotationHighlighter({
|
|
|
1695
1707
|
setToolbarState(null);
|
|
1696
1708
|
};
|
|
1697
1709
|
|
|
1698
|
-
const handleCommentSubmit = (
|
|
1710
|
+
const handleCommentSubmit = (
|
|
1711
|
+
text: string,
|
|
1712
|
+
images?: ImageAttachment[],
|
|
1713
|
+
mentions?: readonly string[],
|
|
1714
|
+
) => {
|
|
1699
1715
|
if (!commentPopover) return;
|
|
1700
1716
|
if (isMathAnnotationSource(commentPopover.source)) {
|
|
1701
1717
|
createAnnotationFromMathSource(
|
|
@@ -1703,6 +1719,9 @@ export function useAnnotationHighlighter({
|
|
|
1703
1719
|
AnnotationType.COMMENT,
|
|
1704
1720
|
text,
|
|
1705
1721
|
images,
|
|
1722
|
+
undefined,
|
|
1723
|
+
undefined,
|
|
1724
|
+
mentions,
|
|
1706
1725
|
);
|
|
1707
1726
|
clearPendingSelection();
|
|
1708
1727
|
window.getSelection()?.removeAllRanges();
|
|
@@ -1712,7 +1731,7 @@ export function useAnnotationHighlighter({
|
|
|
1712
1731
|
if (commentPopover.source && highlighterRef.current) {
|
|
1713
1732
|
createAnnotationFromSource(
|
|
1714
1733
|
highlighterRef.current, commentPopover.source,
|
|
1715
|
-
AnnotationType.COMMENT, text, images
|
|
1734
|
+
AnnotationType.COMMENT, text, images, undefined, undefined, mentions
|
|
1716
1735
|
);
|
|
1717
1736
|
clearPendingSelection();
|
|
1718
1737
|
window.getSelection()?.removeAllRanges();
|