@arbidocs/blocks 0.5.123 → 0.5.124

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 (117) hide show
  1. package/dist/{ClippingMediaViewer-IEIBT74X.cjs → ClippingMediaViewer-ZFEX5A3U.cjs} +11 -10
  2. package/dist/ClippingMediaViewer-ZFEX5A3U.cjs.map +1 -0
  3. package/dist/{ClippingPdfViewer-OGYLMVCE.cjs → ClippingPdfViewer-3LRTEPHV.cjs} +45 -11
  4. package/dist/ClippingPdfViewer-3LRTEPHV.cjs.map +1 -0
  5. package/dist/ConnectedNativeDocxComparison-QKHCYD6W.cjs +1 -1
  6. package/dist/{ConnectedPresentation-5DOJ35XL.cjs → ConnectedPresentation-OV2KBOLK.cjs} +27 -153
  7. package/dist/ConnectedPresentation-OV2KBOLK.cjs.map +1 -0
  8. package/dist/{ConnectedSlidesTab-7YMEDGUU.cjs → ConnectedSlidesTab-LUAGZ6FD.cjs} +29 -27
  9. package/dist/ConnectedSlidesTab-LUAGZ6FD.cjs.map +1 -0
  10. package/dist/ConnectedStreamedWordArtifact-AG62232A.cjs +1 -1
  11. package/dist/{FilesViewerDialog-2QCCRWF2.cjs → FilesViewerDialog-JWKNJLWG.cjs} +12 -9
  12. package/dist/FilesViewerDialog-JWKNJLWG.cjs.map +1 -0
  13. package/dist/SpotlightStrip-ED22UYTM.cjs +398 -0
  14. package/dist/SpotlightStrip-ED22UYTM.cjs.map +1 -0
  15. package/dist/agents/index.cjs +176 -176
  16. package/dist/{chunk-46P7IDBX.cjs → chunk-3IH55CDE.cjs} +3 -3
  17. package/dist/chunk-3IH55CDE.cjs.map +1 -0
  18. package/dist/chunk-3ZZZO4F7.cjs +145 -0
  19. package/dist/chunk-3ZZZO4F7.cjs.map +1 -0
  20. package/dist/{chunk-WFMWKPGY.cjs → chunk-ADPRIBSH.cjs} +9 -33
  21. package/dist/chunk-ADPRIBSH.cjs.map +1 -0
  22. package/dist/{chunk-JD2HX2BR.cjs → chunk-BX6G2ZCI.cjs} +58 -86
  23. package/dist/chunk-BX6G2ZCI.cjs.map +1 -0
  24. package/dist/{chunk-NQML7IUC.cjs → chunk-CQQQIZU4.cjs} +3 -3
  25. package/dist/{chunk-NQML7IUC.cjs.map → chunk-CQQQIZU4.cjs.map} +1 -1
  26. package/dist/{chunk-SNPK3R3O.cjs → chunk-EMRY62HS.cjs} +5 -5
  27. package/dist/{chunk-SNPK3R3O.cjs.map → chunk-EMRY62HS.cjs.map} +1 -1
  28. package/dist/{chunk-DWMWEALB.cjs → chunk-H236SNEV.cjs} +143 -1634
  29. package/dist/chunk-H236SNEV.cjs.map +1 -0
  30. package/dist/chunk-JITYC4OS.cjs +66 -0
  31. package/dist/chunk-JITYC4OS.cjs.map +1 -0
  32. package/dist/chunk-JY7UUZQG.cjs +1513 -0
  33. package/dist/chunk-JY7UUZQG.cjs.map +1 -0
  34. package/dist/chunk-MVXHTAJ4.cjs +111 -0
  35. package/dist/chunk-MVXHTAJ4.cjs.map +1 -0
  36. package/dist/{chunk-4TTX6ZNS.cjs → chunk-ORNLCBNE.cjs} +24 -2
  37. package/dist/chunk-ORNLCBNE.cjs.map +1 -0
  38. package/dist/{chunk-L4CIFLCU.cjs → chunk-QG2U2JGX.cjs} +4 -4
  39. package/dist/{chunk-L4CIFLCU.cjs.map → chunk-QG2U2JGX.cjs.map} +1 -1
  40. package/dist/chunk-WVZLAAIZ.cjs +31 -0
  41. package/dist/chunk-WVZLAAIZ.cjs.map +1 -0
  42. package/dist/{chunk-WV7BCGCK.cjs → chunk-XGM4VVNV.cjs} +19 -19
  43. package/dist/{chunk-WV7BCGCK.cjs.map → chunk-XGM4VVNV.cjs.map} +1 -1
  44. package/dist/chunk-ZZZITHGK.cjs +119 -0
  45. package/dist/chunk-ZZZITHGK.cjs.map +1 -0
  46. package/dist/connect/docx.cjs +1 -1
  47. package/dist/connect/index.cjs +175 -175
  48. package/dist/connect/viewer.cjs +10 -7
  49. package/dist/connection/presentation/ClippingMediaViewer.js +3 -3
  50. package/dist/connection/presentation/ClippingMediaViewer.js.map +2 -2
  51. package/dist/connection/presentation/ClippingPdfViewer.d.ts +4 -3
  52. package/dist/connection/presentation/ClippingPdfViewer.js +48 -9
  53. package/dist/connection/presentation/ClippingPdfViewer.js.map +2 -2
  54. package/dist/connection/presentation/SpotlightLayer.d.ts +5 -0
  55. package/dist/connection/presentation/SpotlightLayer.js +19 -0
  56. package/dist/connection/presentation/SpotlightLayer.js.map +7 -0
  57. package/dist/connection/presentation/SpotlightStrip.d.ts +10 -0
  58. package/dist/connection/presentation/SpotlightStrip.js +237 -0
  59. package/dist/connection/presentation/SpotlightStrip.js.map +7 -0
  60. package/dist/connection/presentation/useViewerClipping.d.ts +4 -3
  61. package/dist/connection/presentation/useViewerClipping.js +8 -4
  62. package/dist/connection/presentation/useViewerClipping.js.map +2 -2
  63. package/dist/connection/viewer/ConnectedDocumentViewer.js +1 -1
  64. package/dist/connection/viewer/ConnectedDocumentViewer.js.map +2 -2
  65. package/dist/connection/viewer/pdf/passageHighlights.d.ts +5 -0
  66. package/dist/connection/viewer/pdf/passageHighlights.js +14 -1
  67. package/dist/connection/viewer/pdf/passageHighlights.js.map +2 -2
  68. package/dist/connection/viewer/useCleanView.d.ts +3 -1
  69. package/dist/connection/viewer/useCleanView.js +6 -3
  70. package/dist/connection/viewer/useCleanView.js.map +2 -2
  71. package/dist/document-tools/DocumentToolButtons.js +1 -1
  72. package/dist/document-tools/DocumentToolButtons.js.map +1 -1
  73. package/dist/document-tools/index.cjs +13 -13
  74. package/dist/embed/index.cjs +2 -2
  75. package/dist/index.cjs +239 -239
  76. package/dist/presentation/ClipCaptureOverlay.d.ts +7 -3
  77. package/dist/presentation/ClipCaptureOverlay.js +43 -14
  78. package/dist/presentation/ClipCaptureOverlay.js.map +2 -2
  79. package/dist/presentation/PresentControls.d.ts +8 -0
  80. package/dist/presentation/PresentControls.js +1 -0
  81. package/dist/presentation/PresentControls.js.map +2 -2
  82. package/dist/presentation/PresentationStage.d.ts +10 -2
  83. package/dist/presentation/PresentationStage.js +6 -3
  84. package/dist/presentation/PresentationStage.js.map +2 -2
  85. package/dist/presentation/PresenterView.js +1 -22
  86. package/dist/presentation/PresenterView.js.map +2 -2
  87. package/dist/presentation/SpotlightView.d.ts +19 -0
  88. package/dist/presentation/SpotlightView.js +180 -0
  89. package/dist/presentation/SpotlightView.js.map +7 -0
  90. package/dist/presentation/passage-clip.d.ts +17 -0
  91. package/dist/presentation/passage-clip.js +25 -0
  92. package/dist/presentation/passage-clip.js.map +7 -0
  93. package/dist/presentation/spotlight.d.ts +58 -0
  94. package/dist/presentation/spotlight.js +110 -0
  95. package/dist/presentation/spotlight.js.map +7 -0
  96. package/dist/presentation/store.d.ts +5 -0
  97. package/dist/presentation/store.js +19 -0
  98. package/dist/presentation/store.js.map +2 -2
  99. package/dist/presentation/usePointerActive.d.ts +2 -0
  100. package/dist/presentation/usePointerActive.js +28 -0
  101. package/dist/presentation/usePointerActive.js.map +7 -0
  102. package/dist/styles.css +1 -1
  103. package/dist/workflow-canvas/index.cjs +12 -12
  104. package/node_modules/@arbidocs/coedit/package.json +1 -1
  105. package/package.json +5 -5
  106. package/dist/ClippingMediaViewer-IEIBT74X.cjs.map +0 -1
  107. package/dist/ClippingPdfViewer-OGYLMVCE.cjs.map +0 -1
  108. package/dist/ConnectedPresentation-5DOJ35XL.cjs.map +0 -1
  109. package/dist/ConnectedSlidesTab-7YMEDGUU.cjs.map +0 -1
  110. package/dist/FilesViewerDialog-2QCCRWF2.cjs.map +0 -1
  111. package/dist/chunk-46P7IDBX.cjs.map +0 -1
  112. package/dist/chunk-4TTX6ZNS.cjs.map +0 -1
  113. package/dist/chunk-DWMWEALB.cjs.map +0 -1
  114. package/dist/chunk-JD2HX2BR.cjs.map +0 -1
  115. package/dist/chunk-WFMWKPGY.cjs.map +0 -1
  116. package/dist/chunk-XK4TVJKD.cjs +0 -73
  117. package/dist/chunk-XK4TVJKD.cjs.map +0 -1
@@ -0,0 +1,398 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var chunk3ZZZO4F7_cjs = require('./chunk-3ZZZO4F7.cjs');
5
+ var chunkJY7UUZQG_cjs = require('./chunk-JY7UUZQG.cjs');
6
+ require('./chunk-WOOOMTTA.cjs');
7
+ require('./chunk-5GCCUXMT.cjs');
8
+ var chunkMVXHTAJ4_cjs = require('./chunk-MVXHTAJ4.cjs');
9
+ require('./chunk-ADPRIBSH.cjs');
10
+ var chunkORNLCBNE_cjs = require('./chunk-ORNLCBNE.cjs');
11
+ require('./chunk-GFUOCGY6.cjs');
12
+ var chunkVZOOGA47_cjs = require('./chunk-VZOOGA47.cjs');
13
+ var react = require('react');
14
+ var lucideReact = require('lucide-react');
15
+ var react$1 = require('@arbidocs/react');
16
+ var ui = require('@arbidocs/react/ui');
17
+ var jsxRuntime = require('react/jsx-runtime');
18
+
19
+ var SPOTLIGHT_MS = 300;
20
+ var EASING = "cubic-bezier(0.2, 0, 0, 1)";
21
+ var prefersReducedMotion = (view) => !!view?.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
22
+ function SpotlightView({ fromRect }) {
23
+ const clips = chunkMVXHTAJ4_cjs.useSpotlight((s) => s.clips);
24
+ const note = chunkMVXHTAJ4_cjs.useSpotlight((s) => s.note);
25
+ const step = chunkMVXHTAJ4_cjs.useSpotlight((s) => s.step);
26
+ const captions = chunkORNLCBNE_cjs.usePresentation((s) => s.captions);
27
+ const theme = chunkORNLCBNE_cjs.usePresentation((s) => s.theme);
28
+ const steps = react.useMemo(() => chunkMVXHTAJ4_cjs.spotlightSteps(clips), [clips]);
29
+ const ids = steps[step] ?? [];
30
+ const stepKey = ids.join();
31
+ const slide = react.useMemo(
32
+ () => chunkMVXHTAJ4_cjs.spotlightSlide(clips, ids, note, { captions, theme }),
33
+ // `ids` is new each render; its key says when it changed.
34
+ // eslint-disable-next-line react-hooks/exhaustive-deps
35
+ [clips, stepKey, note, captions, theme]
36
+ );
37
+ const show = react.useMemo(() => ({ slide, clips }), [slide, clips]);
38
+ const [view, setView] = react.useState(null);
39
+ const root = react.useRef(null);
40
+ const rootRef = react.useCallback((node) => {
41
+ root.current = node;
42
+ if (node) setView(node.ownerDocument.defaultView);
43
+ }, []);
44
+ const active = chunk3ZZZO4F7_cjs.usePointerActive(view);
45
+ const [kept, setKept] = react.useState(null);
46
+ const last = react.useRef(/* @__PURE__ */ new Map());
47
+ const fromRectRef = react.useRef(fromRect);
48
+ fromRectRef.current = fromRect;
49
+ react.useLayoutEffect(() => {
50
+ const node = root.current;
51
+ if (!node) return;
52
+ const reduce = prefersReducedMotion(node.ownerDocument.defaultView);
53
+ const next = /* @__PURE__ */ new Map();
54
+ for (const id of stepKey.split(",").filter(Boolean)) {
55
+ const element = node.querySelector(`[data-testid="stage-item-${id}"]`);
56
+ const clip = chunkMVXHTAJ4_cjs.useSpotlight.getState().clips.find((entry) => entry.id === id);
57
+ if (!element || !clip) continue;
58
+ const to = element.getBoundingClientRect();
59
+ next.set(id, to);
60
+ const from = last.current.get(id) ?? fromRectRef.current(clip);
61
+ if (reduce || !from || !to.width || !to.height || typeof element.animate !== "function") {
62
+ continue;
63
+ }
64
+ element.animate(
65
+ [
66
+ {
67
+ transform: `translate(${from.left - to.left}px, ${from.top - to.top}px) scale(${from.width / to.width}, ${from.height / to.height})`
68
+ },
69
+ { transform: "none" }
70
+ ],
71
+ { duration: SPOTLIGHT_MS, easing: EASING }
72
+ );
73
+ }
74
+ last.current = next;
75
+ }, [stepKey]);
76
+ const backdrop = react.useRef(null);
77
+ react.useLayoutEffect(() => {
78
+ const element = backdrop.current;
79
+ if (!element || typeof element.animate !== "function") return;
80
+ if (prefersReducedMotion(element.ownerDocument.defaultView)) return;
81
+ element.animate([{ opacity: 0 }, { opacity: 1 }], { duration: SPOTLIGHT_MS, easing: EASING });
82
+ }, []);
83
+ react.useEffect(() => {
84
+ if (!view) return;
85
+ const onKeyDown = (event) => {
86
+ if (event.metaKey || event.ctrlKey || event.altKey) return;
87
+ const target = event.target;
88
+ if (target?.closest?.('input, textarea, select, [contenteditable="true"]')) return;
89
+ const state = chunkMVXHTAJ4_cjs.useSpotlight.getState();
90
+ if (event.key === "Escape") state.hide();
91
+ else if (event.key === "ArrowRight" || event.key === "PageDown") state.goTo(state.step + 1);
92
+ else if (event.key === "ArrowLeft" || event.key === "PageUp") state.goTo(state.step - 1);
93
+ else return;
94
+ event.preventDefault();
95
+ event.stopPropagation();
96
+ };
97
+ view.addEventListener("keydown", onKeyDown, true);
98
+ return () => view.removeEventListener("keydown", onKeyDown, true);
99
+ }, [view]);
100
+ const actions = chunkMVXHTAJ4_cjs.useSpotlight.getState();
101
+ const isKept = kept === `${stepKey}|${note}`;
102
+ return /* @__PURE__ */ jsxRuntime.jsxs(
103
+ "div",
104
+ {
105
+ ref: rootRef,
106
+ className: "absolute inset-0 z-30 select-none",
107
+ style: { cursor: active ? "default" : "none" },
108
+ role: "dialog",
109
+ "aria-label": "Spotlight",
110
+ "data-testid": "spotlight",
111
+ children: [
112
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: backdrop, className: "absolute inset-0 bg-black/85" }),
113
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 p-4 pb-16", children: /* @__PURE__ */ jsxRuntime.jsx(chunkJY7UUZQG_cjs.PresentationStage, { show, className: "bg-transparent" }) }),
114
+ /* @__PURE__ */ jsxRuntime.jsxs(
115
+ "div",
116
+ {
117
+ className: ui.cn(
118
+ "absolute bottom-3 left-1/2 z-10 flex -translate-x-1/2 items-center gap-0.5 rounded-full bg-black/60 p-1 text-white shadow-lg backdrop-blur transition-opacity duration-300",
119
+ active ? "opacity-100" : "pointer-events-none opacity-0"
120
+ ),
121
+ role: "toolbar",
122
+ "aria-label": "Spotlight",
123
+ "data-testid": "spotlight-controls",
124
+ children: [
125
+ /* @__PURE__ */ jsxRuntime.jsx(
126
+ chunk3ZZZO4F7_cjs.Control,
127
+ {
128
+ label: "Previous (\u2190)",
129
+ onClick: () => actions.goTo(step - 1),
130
+ disabled: step === 0,
131
+ testId: "spotlight-previous",
132
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "size-4" })
133
+ }
134
+ ),
135
+ /* @__PURE__ */ jsxRuntime.jsxs(
136
+ "span",
137
+ {
138
+ className: "min-w-12 text-center text-xs tabular-nums text-white/85",
139
+ "data-testid": "spotlight-position",
140
+ children: [
141
+ step + 1,
142
+ " / ",
143
+ steps.length
144
+ ]
145
+ }
146
+ ),
147
+ /* @__PURE__ */ jsxRuntime.jsx(
148
+ chunk3ZZZO4F7_cjs.Control,
149
+ {
150
+ label: "Next (\u2192)",
151
+ onClick: () => actions.goTo(step + 1),
152
+ disabled: step >= steps.length - 1,
153
+ testId: "spotlight-next",
154
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "size-4" })
155
+ }
156
+ ),
157
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-1 h-4 w-px bg-white/25" }),
158
+ /* @__PURE__ */ jsxRuntime.jsxs(
159
+ "button",
160
+ {
161
+ type: "button",
162
+ className: "inline-flex h-8 items-center gap-1.5 whitespace-nowrap rounded-full px-3 text-xs text-white/85 hover:bg-white/15 hover:text-white disabled:opacity-60 disabled:hover:bg-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/60 pointer-coarse:h-11",
163
+ disabled: isKept,
164
+ onClick: () => {
165
+ actions.keep();
166
+ setKept(`${stepKey}|${note}`);
167
+ },
168
+ title: "Add this as a slide of the open presentation",
169
+ "data-testid": "spotlight-keep",
170
+ children: [
171
+ isKept ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Presentation, { className: "size-3.5" }),
172
+ isKept ? "Kept as slide" : "Keep as slide"
173
+ ]
174
+ }
175
+ ),
176
+ /* @__PURE__ */ jsxRuntime.jsx(chunk3ZZZO4F7_cjs.Control, { label: "Back to the document (Esc)", onClick: actions.hide, testId: "spotlight-close", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-4" }) })
177
+ ]
178
+ }
179
+ )
180
+ ]
181
+ }
182
+ );
183
+ }
184
+ var frameOf = (node) => node?.closest('[data-slot="viewer-frame"]') ?? null;
185
+ var isTyping = (target) => !!target?.closest?.('input, textarea, select, [contenteditable="true"]');
186
+ function clipOnPage(frame, clip) {
187
+ const clipped = Array.from(frame.querySelectorAll("[data-clip-document]")).find(
188
+ (element) => element.getAttribute("data-clip-document") === clip.documentId
189
+ );
190
+ const page = clipped?.querySelector(`[data-page-number="${clip.page}"]`);
191
+ if (!page) return null;
192
+ const box = page.getBoundingClientRect();
193
+ const view = frame.ownerDocument.defaultView;
194
+ const rect = {
195
+ left: box.left + clip.rect.x * box.width,
196
+ top: box.top + clip.rect.y * box.height,
197
+ width: clip.rect.w * box.width,
198
+ height: clip.rect.h * box.height
199
+ };
200
+ const visible = rect.width > 0 && rect.top + rect.height > 0 && rect.left + rect.width > 0 && (!view || rect.top < view.innerHeight && rect.left < view.innerWidth);
201
+ return visible ? rect : null;
202
+ }
203
+ function useDocumentSwitcher(clips) {
204
+ const session = react$1.useArbiSessionStore();
205
+ const { data: documents } = react$1.useDocuments();
206
+ const groups = react.useMemo(() => {
207
+ const all = (documents ?? []).map((doc) => ({
208
+ id: doc.external_id,
209
+ title: ui.getDocumentTitle(doc, "Untitled"),
210
+ fileName: doc.file_name
211
+ })).sort((a, b) => a.title.localeCompare(b.title));
212
+ const clipped = new Set(clips.map((clip) => clip.documentId));
213
+ const from = all.filter((doc) => clipped.has(doc.id));
214
+ return [
215
+ ...from.length ? [{ id: "clipped", heading: "Clipped from", docs: from }] : [],
216
+ { id: "all", heading: "Documents", docs: all }
217
+ ];
218
+ }, [documents, clips]);
219
+ const open = (id) => session.setViewingDocument({ id, page: null });
220
+ return { groups, open };
221
+ }
222
+ function SpotlightStrip() {
223
+ const clips = chunkMVXHTAJ4_cjs.useSpotlight((s) => s.clips);
224
+ const note = chunkMVXHTAJ4_cjs.useSpotlight((s) => s.note);
225
+ const showing = chunkMVXHTAJ4_cjs.useSpotlight((s) => s.showing);
226
+ const actions = chunkMVXHTAJ4_cjs.useSpotlight.getState();
227
+ const switcher = useDocumentSwitcher(clips);
228
+ const [noteOpen, setNoteOpen] = react.useState(false);
229
+ const strip = react.useRef(null);
230
+ const [view, setView] = react.useState(null);
231
+ const stripRef = react.useCallback((node) => {
232
+ strip.current = node;
233
+ if (node) setView(node.ownerDocument.defaultView);
234
+ }, []);
235
+ react.useEffect(() => {
236
+ if (!view || showing) return;
237
+ const onKeyDown = (event) => {
238
+ if (event.metaKey || event.ctrlKey || event.altKey || isTyping(event.target)) return;
239
+ if (event.key !== "s" && event.key !== "S") return;
240
+ event.preventDefault();
241
+ chunkMVXHTAJ4_cjs.useSpotlight.getState().show();
242
+ };
243
+ view.addEventListener("keydown", onKeyDown);
244
+ return () => view.removeEventListener("keydown", onKeyDown);
245
+ }, [view, showing]);
246
+ const scrolled = react.useRef(null);
247
+ react.useEffect(() => {
248
+ const scroller = frameOf(strip.current)?.querySelector(
249
+ '[data-slot="pdf-scroller"]'
250
+ );
251
+ if (showing) {
252
+ scrolled.current = scroller ? { element: scroller, top: scroller.scrollTop, left: scroller.scrollLeft } : null;
253
+ return;
254
+ }
255
+ const before = scrolled.current;
256
+ scrolled.current = null;
257
+ if (before?.element.isConnected) {
258
+ before.element.scrollTop = before.top;
259
+ before.element.scrollLeft = before.left;
260
+ }
261
+ }, [showing]);
262
+ const fromRect = react.useCallback((clip) => {
263
+ const frame = frameOf(strip.current);
264
+ const onPage = frame ? clipOnPage(frame, clip) : null;
265
+ return onPage ?? strip.current?.querySelector(`[data-spotlight-clip="${clip.id}"]`)?.getBoundingClientRect() ?? null;
266
+ }, []);
267
+ const count = `${clips.length} clip${clips.length === 1 ? "" : "s"}`;
268
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
269
+ /* @__PURE__ */ jsxRuntime.jsxs(
270
+ "div",
271
+ {
272
+ ref: stripRef,
273
+ className: ui.cn(
274
+ "absolute bottom-[4.5rem] left-1/2 z-20 flex max-w-[calc(100%-1.5rem)] -translate-x-1/2 items-center gap-1.5 rounded-full border border-border bg-background/95 p-1 pl-1.5 shadow-lg backdrop-blur",
275
+ showing && "invisible"
276
+ ),
277
+ role: "toolbar",
278
+ "aria-label": "Gathered clips",
279
+ "data-testid": "spotlight-strip",
280
+ children: [
281
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex min-w-0 items-center gap-1 overflow-x-auto [scrollbar-width:none]", children: clips.map((clip, index) => /* @__PURE__ */ jsxRuntime.jsxs(
282
+ "li",
283
+ {
284
+ className: "group relative h-8 shrink-0 overflow-hidden rounded-md border border-border bg-white",
285
+ style: { width: `${Math.min(4.5, Math.max(1.5, 2 * clip.aspect))}rem` },
286
+ title: clip.caption,
287
+ "data-spotlight-clip": clip.id,
288
+ "data-testid": `spotlight-chip-${index}`,
289
+ children: [
290
+ /* @__PURE__ */ jsxRuntime.jsx(chunkJY7UUZQG_cjs.ClipImage, { clip, className: "h-full w-full" }),
291
+ /* @__PURE__ */ jsxRuntime.jsx(
292
+ "button",
293
+ {
294
+ type: "button",
295
+ className: "absolute inset-0 flex items-center justify-center bg-black/50 text-white opacity-0 transition-opacity focus-visible:opacity-100 group-hover:opacity-100",
296
+ onClick: () => actions.remove(clip.id),
297
+ "aria-label": `Remove ${clip.caption}`,
298
+ "data-testid": `spotlight-remove-${index}`,
299
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-3.5" })
300
+ }
301
+ )
302
+ ]
303
+ },
304
+ clip.id
305
+ )) }),
306
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs text-muted-foreground", "data-testid": "spotlight-count", children: count }),
307
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Popover, { open: noteOpen, onOpenChange: setNoteOpen, children: [
308
+ /* @__PURE__ */ jsxRuntime.jsx(ui.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
309
+ ui.Button,
310
+ {
311
+ type: "button",
312
+ variant: note ? "secondary" : "ghost",
313
+ size: "sm",
314
+ className: "h-7 max-w-40 shrink-0 gap-1 rounded-full px-2 text-xs",
315
+ title: note ? `Note: ${note}` : "Type a note to show as the title",
316
+ "data-testid": "spotlight-note",
317
+ children: [
318
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.StickyNote, { className: "size-3.5 shrink-0" }),
319
+ note ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: note }) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Note" })
320
+ ]
321
+ }
322
+ ) }),
323
+ /* @__PURE__ */ jsxRuntime.jsx(ui.PopoverContent, { side: "top", className: "w-72 p-2", children: /* @__PURE__ */ jsxRuntime.jsx(
324
+ ui.Input,
325
+ {
326
+ autoFocus: true,
327
+ value: note,
328
+ placeholder: "Type a note",
329
+ onChange: (event) => actions.setNote(event.target.value),
330
+ onKeyDown: (event) => {
331
+ if (event.key === "Enter") setNoteOpen(false);
332
+ },
333
+ "data-testid": "spotlight-note-input"
334
+ }
335
+ ) })
336
+ ] }),
337
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-44 shrink-0 [&_[role=combobox]]:h-7 [&_[role=combobox]]:rounded-full [&_[role=combobox]]:text-xs", children: /* @__PURE__ */ jsxRuntime.jsx(
338
+ chunkVZOOGA47_cjs.DocumentPicker,
339
+ {
340
+ groups: switcher.groups,
341
+ value: "",
342
+ onValueChange: switcher.open,
343
+ labels: { placeholder: "Clip another document" }
344
+ }
345
+ ) }),
346
+ /* @__PURE__ */ jsxRuntime.jsxs(
347
+ ui.Button,
348
+ {
349
+ type: "button",
350
+ size: "sm",
351
+ className: "h-7 shrink-0 gap-1 rounded-full px-3 text-xs",
352
+ onClick: actions.show,
353
+ title: "Show the clips over the document (S)",
354
+ "data-testid": "spotlight-show",
355
+ children: [
356
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Presentation, { className: "size-3.5" }),
357
+ "Show"
358
+ ]
359
+ }
360
+ ),
361
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
362
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
363
+ ui.Button,
364
+ {
365
+ type: "button",
366
+ variant: "ghost",
367
+ size: "icon",
368
+ className: "size-7 shrink-0 rounded-full",
369
+ "aria-label": "More",
370
+ "data-testid": "spotlight-more",
371
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MoreHorizontal, { className: "size-4" })
372
+ }
373
+ ) }),
374
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenuContent, { align: "end", side: "top", children: [
375
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenuItem, { onSelect: actions.keep, "data-testid": "spotlight-menu-keep", children: [
376
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Presentation, { className: "mr-2 size-4" }),
377
+ " Keep as slide"
378
+ ] }),
379
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenuItem, { onSelect: actions.addToTray, "data-testid": "spotlight-menu-tray", children: [
380
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Inbox, { className: "mr-2 size-4" }),
381
+ " Add to the presentation tray"
382
+ ] }),
383
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenuItem, { onSelect: actions.clear, "data-testid": "spotlight-menu-clear", children: [
384
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eraser, { className: "mr-2 size-4" }),
385
+ " Clear the clips"
386
+ ] })
387
+ ] })
388
+ ] })
389
+ ]
390
+ }
391
+ ),
392
+ showing && /* @__PURE__ */ jsxRuntime.jsx(SpotlightView, { fromRect })
393
+ ] });
394
+ }
395
+
396
+ exports.SpotlightStrip = SpotlightStrip;
397
+ //# sourceMappingURL=SpotlightStrip-ED22UYTM.cjs.map
398
+ //# sourceMappingURL=SpotlightStrip-ED22UYTM.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/presentation/SpotlightView.tsx","../src/connection/presentation/SpotlightStrip.tsx"],"names":["useSpotlight","usePresentation","useMemo","spotlightSteps","spotlightSlide","useState","useRef","useCallback","usePointerActive","useLayoutEffect","useEffect","jsxs","jsx","PresentationStage","cn","Control","ChevronLeft","ChevronRight","Check","Presentation","X","useArbiSessionStore","useDocuments","getDocumentTitle","Fragment","ClipImage","Popover","PopoverTrigger","Button","StickyNote","PopoverContent","Input","DocumentPicker","DropdownMenu","DropdownMenuTrigger","MoreHorizontal","DropdownMenuContent","DropdownMenuItem","Inbox","Eraser"],"mappings":"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","file":"SpotlightStrip-ED22UYTM.cjs","sourcesContent":["/**\n * The spotlight on screen: the document dims and the gathered clips fly from where they are on\n * their pages into a slide over it, in the same window (a call sharing it goes on sharing it). It is\n * the presentation's own stage (`PresentationStage`), so it looks as a slide of the deck would.\n *\n * ← → step through the clips together and each alone, Esc goes back to the document. Each move is\n * a FLIP: a clip is drawn where it ends and animated from where it was (its page, or its place on\n * the previous step) by a transform, about 300 ms; with reduced motion it simply appears.\n */\n\nimport { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'\nimport { Check, ChevronLeft, ChevronRight, Presentation, X } from 'lucide-react'\nimport { cn } from '@arbidocs/react/ui'\nimport type { Clip } from './model'\nimport { Control } from './PresentControls'\nimport { PresentationStage } from './PresentationStage'\nimport { spotlightSlide, spotlightSteps, useSpotlight } from './spotlight'\nimport { usePresentation } from './store'\nimport { usePointerActive } from './usePointerActive'\n\n/** How long a clip takes to fly into place. */\nexport const SPOTLIGHT_MS = 300\nconst EASING = 'cubic-bezier(0.2, 0, 0, 1)'\n\n/** A box on screen, in CSS pixels. */\nexport type ScreenRect = Pick<DOMRect, 'left' | 'top' | 'width' | 'height'>\n\nexport interface SpotlightViewProps {\n /** Where a clip is on screen before the spotlight (its passage on the page), if it shows. */\n fromRect: (clip: Clip) => ScreenRect | null\n}\n\nconst prefersReducedMotion = (view: Window | null) =>\n !!view?.matchMedia?.('(prefers-reduced-motion: reduce)').matches\n\nexport function SpotlightView({ fromRect }: SpotlightViewProps) {\n const clips = useSpotlight((s) => s.clips)\n const note = useSpotlight((s) => s.note)\n const step = useSpotlight((s) => s.step)\n const captions = usePresentation((s) => s.captions)\n const theme = usePresentation((s) => s.theme)\n const steps = useMemo(() => spotlightSteps(clips), [clips])\n const ids = steps[step] ?? []\n const stepKey = ids.join()\n const slide = useMemo(\n () => spotlightSlide(clips, ids, note, { captions, theme }),\n // `ids` is new each render; its key says when it changed.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [clips, stepKey, note, captions, theme]\n )\n const show = useMemo(() => ({ slide, clips }), [slide, clips])\n\n const [view, setView] = useState<Window | null>(null)\n const root = useRef<HTMLDivElement | null>(null)\n const rootRef = useCallback((node: HTMLDivElement | null) => {\n root.current = node\n if (node) setView(node.ownerDocument.defaultView)\n }, [])\n const active = usePointerActive(view)\n const [kept, setKept] = useState<string | null>(null)\n\n // FLIP: each clip from where it was (on its page, or on the step before) to where it is now.\n const last = useRef(new Map<string, ScreenRect>())\n const fromRectRef = useRef(fromRect)\n fromRectRef.current = fromRect\n useLayoutEffect(() => {\n const node = root.current\n if (!node) return\n const reduce = prefersReducedMotion(node.ownerDocument.defaultView)\n const next = new Map<string, ScreenRect>()\n for (const id of stepKey.split(',').filter(Boolean)) {\n const element = node.querySelector<HTMLElement>(`[data-testid=\"stage-item-${id}\"]`)\n const clip = useSpotlight.getState().clips.find((entry) => entry.id === id)\n if (!element || !clip) continue\n const to = element.getBoundingClientRect()\n next.set(id, to)\n const from = last.current.get(id) ?? fromRectRef.current(clip)\n if (reduce || !from || !to.width || !to.height || typeof element.animate !== 'function') {\n continue\n }\n element.animate(\n [\n {\n transform: `translate(${from.left - to.left}px, ${from.top - to.top}px) scale(${\n from.width / to.width\n }, ${from.height / to.height})`,\n },\n { transform: 'none' },\n ],\n { duration: SPOTLIGHT_MS, easing: EASING }\n )\n }\n last.current = next\n }, [stepKey])\n\n // The document dims as the clips arrive.\n const backdrop = useRef<HTMLDivElement>(null)\n useLayoutEffect(() => {\n const element = backdrop.current\n if (!element || typeof element.animate !== 'function') return\n if (prefersReducedMotion(element.ownerDocument.defaultView)) return\n element.animate([{ opacity: 0 }, { opacity: 1 }], { duration: SPOTLIGHT_MS, easing: EASING })\n }, [])\n\n // Esc back to the document, ← → through the steps; taken before the viewer's own keys.\n useEffect(() => {\n if (!view) return\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.metaKey || event.ctrlKey || event.altKey) return\n const target = event.target as HTMLElement | null\n if (target?.closest?.('input, textarea, select, [contenteditable=\"true\"]')) return\n const state = useSpotlight.getState()\n if (event.key === 'Escape') state.hide()\n else if (event.key === 'ArrowRight' || event.key === 'PageDown') state.goTo(state.step + 1)\n else if (event.key === 'ArrowLeft' || event.key === 'PageUp') state.goTo(state.step - 1)\n else return\n event.preventDefault()\n event.stopPropagation()\n }\n view.addEventListener('keydown', onKeyDown, true)\n return () => view.removeEventListener('keydown', onKeyDown, true)\n }, [view])\n\n const actions = useSpotlight.getState()\n const isKept = kept === `${stepKey}|${note}`\n return (\n <div\n ref={rootRef}\n className=\"absolute inset-0 z-30 select-none\"\n style={{ cursor: active ? 'default' : 'none' }}\n role=\"dialog\"\n aria-label=\"Spotlight\"\n data-testid=\"spotlight\"\n >\n <div ref={backdrop} className=\"absolute inset-0 bg-black/85\" />\n <div className=\"absolute inset-0 p-4 pb-16\">\n <PresentationStage show={show} className=\"bg-transparent\" />\n </div>\n <div\n className={cn(\n 'absolute bottom-3 left-1/2 z-10 flex -translate-x-1/2 items-center gap-0.5 rounded-full bg-black/60 p-1 text-white shadow-lg backdrop-blur transition-opacity duration-300',\n active ? 'opacity-100' : 'pointer-events-none opacity-0'\n )}\n role=\"toolbar\"\n aria-label=\"Spotlight\"\n data-testid=\"spotlight-controls\"\n >\n <Control\n label=\"Previous (←)\"\n onClick={() => actions.goTo(step - 1)}\n disabled={step === 0}\n testId=\"spotlight-previous\"\n >\n <ChevronLeft className=\"size-4\" />\n </Control>\n <span\n className=\"min-w-12 text-center text-xs tabular-nums text-white/85\"\n data-testid=\"spotlight-position\"\n >\n {step + 1} / {steps.length}\n </span>\n <Control\n label=\"Next (→)\"\n onClick={() => actions.goTo(step + 1)}\n disabled={step >= steps.length - 1}\n testId=\"spotlight-next\"\n >\n <ChevronRight className=\"size-4\" />\n </Control>\n <div className=\"mx-1 h-4 w-px bg-white/25\" />\n <button\n type=\"button\"\n className=\"inline-flex h-8 items-center gap-1.5 whitespace-nowrap rounded-full px-3 text-xs text-white/85 hover:bg-white/15 hover:text-white disabled:opacity-60 disabled:hover:bg-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/60 pointer-coarse:h-11\"\n disabled={isKept}\n onClick={() => {\n actions.keep()\n setKept(`${stepKey}|${note}`)\n }}\n title=\"Add this as a slide of the open presentation\"\n data-testid=\"spotlight-keep\"\n >\n {isKept ? <Check className=\"size-3.5\" /> : <Presentation className=\"size-3.5\" />}\n {isKept ? 'Kept as slide' : 'Keep as slide'}\n </button>\n <Control label=\"Back to the document (Esc)\" onClick={actions.hide} testId=\"spotlight-close\">\n <X className=\"size-4\" />\n </Control>\n </div>\n </div>\n )\n}\n","/**\n * The clips gathered in the viewer, in a small strip over the document: each clip (× takes it\n * out), a note to show as the spotlight's title, another document to clip from, and Show (or S),\n * which puts them in the spotlight over this same viewer. Rarer actions — keep them as a slide,\n * add them to the presentation's tray, clear them — are in its \"…\" menu.\n *\n * The strip and the spotlight stay over whichever document the viewer shows, so clips are gathered\n * from several documents in a row. Esc brings the document back where it was scrolled to.\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { Eraser, MoreHorizontal, Inbox, Presentation, StickyNote, X } from 'lucide-react'\nimport { useArbiSessionStore, useDocuments } from '@arbidocs/react'\nimport {\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n Input,\n Popover,\n PopoverContent,\n PopoverTrigger,\n cn,\n getDocumentTitle,\n} from '@arbidocs/react/ui'\nimport { ClipImage } from '../../presentation/ClipImage'\nimport type { Clip } from '../../presentation/model'\nimport { SpotlightView, type ScreenRect } from '../../presentation/SpotlightView'\nimport { useSpotlight } from '../../presentation/spotlight'\nimport { DocumentPicker, type PickerGroup } from '../../settings/DocumentPicker'\n\n/** The viewer this strip is over (its frame), found from the strip's own element. */\nconst frameOf = (node: HTMLElement | null) =>\n node?.closest<HTMLElement>('[data-slot=\"viewer-frame\"]') ?? null\n\nconst isTyping = (target: EventTarget | null) =>\n !!(target as HTMLElement | null)?.closest?.('input, textarea, select, [contenteditable=\"true\"]')\n\n/**\n * Where a clip is on screen now: its area of its page, while its document is clipped in the viewer\n * (`data-clip-document`) and that page is in view.\n */\nfunction clipOnPage(frame: HTMLElement, clip: Clip): ScreenRect | null {\n const clipped = Array.from(frame.querySelectorAll('[data-clip-document]')).find(\n (element) => element.getAttribute('data-clip-document') === clip.documentId\n )\n const page = clipped?.querySelector<HTMLElement>(`[data-page-number=\"${clip.page}\"]`)\n if (!page) return null\n const box = page.getBoundingClientRect()\n const view = frame.ownerDocument.defaultView\n const rect = {\n left: box.left + clip.rect.x * box.width,\n top: box.top + clip.rect.y * box.height,\n width: clip.rect.w * box.width,\n height: clip.rect.h * box.height,\n }\n const visible =\n rect.width > 0 &&\n rect.top + rect.height > 0 &&\n rect.left + rect.width > 0 &&\n (!view || (rect.top < view.innerHeight && rect.left < view.innerWidth))\n return visible ? rect : null\n}\n\nfunction useDocumentSwitcher(clips: readonly Clip[]) {\n const session = useArbiSessionStore()\n const { data: documents } = useDocuments()\n const groups = useMemo<PickerGroup[]>(() => {\n const all = (documents ?? [])\n .map((doc) => ({\n id: doc.external_id,\n title: getDocumentTitle(doc, 'Untitled'),\n fileName: doc.file_name,\n }))\n .sort((a, b) => a.title.localeCompare(b.title))\n const clipped = new Set(clips.map((clip) => clip.documentId))\n const from = all.filter((doc) => clipped.has(doc.id))\n return [\n ...(from.length ? [{ id: 'clipped', heading: 'Clipped from', docs: from }] : []),\n { id: 'all', heading: 'Documents', docs: all },\n ]\n }, [documents, clips])\n const open = (id: string) => session.setViewingDocument({ id, page: null })\n return { groups, open }\n}\n\nexport function SpotlightStrip() {\n const clips = useSpotlight((s) => s.clips)\n const note = useSpotlight((s) => s.note)\n const showing = useSpotlight((s) => s.showing)\n const actions = useSpotlight.getState()\n const switcher = useDocumentSwitcher(clips)\n const [noteOpen, setNoteOpen] = useState(false)\n\n const strip = useRef<HTMLDivElement | null>(null)\n const [view, setView] = useState<Window | null>(null)\n const stripRef = useCallback((node: HTMLDivElement | null) => {\n strip.current = node\n if (node) setView(node.ownerDocument.defaultView)\n }, [])\n\n // S shows the spotlight (not while typing).\n useEffect(() => {\n if (!view || showing) return\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.metaKey || event.ctrlKey || event.altKey || isTyping(event.target)) return\n if (event.key !== 's' && event.key !== 'S') return\n event.preventDefault()\n useSpotlight.getState().show()\n }\n view.addEventListener('keydown', onKeyDown)\n return () => view.removeEventListener('keydown', onKeyDown)\n }, [view, showing])\n\n // Back from the spotlight, the document is where it was scrolled to.\n const scrolled = useRef<{ element: HTMLElement; top: number; left: number } | null>(null)\n useEffect(() => {\n const scroller = frameOf(strip.current)?.querySelector<HTMLElement>(\n '[data-slot=\"pdf-scroller\"]'\n )\n if (showing) {\n scrolled.current = scroller\n ? { element: scroller, top: scroller.scrollTop, left: scroller.scrollLeft }\n : null\n return\n }\n const before = scrolled.current\n scrolled.current = null\n if (before?.element.isConnected) {\n before.element.scrollTop = before.top\n before.element.scrollLeft = before.left\n }\n }, [showing])\n\n const fromRect = useCallback((clip: Clip) => {\n const frame = frameOf(strip.current)\n const onPage = frame ? clipOnPage(frame, clip) : null\n // Not on screen (another document's clip): from its chip in the strip.\n return (\n onPage ??\n strip.current?.querySelector(`[data-spotlight-clip=\"${clip.id}\"]`)?.getBoundingClientRect() ??\n null\n )\n }, [])\n\n const count = `${clips.length} clip${clips.length === 1 ? '' : 's'}`\n return (\n <>\n <div\n ref={stripRef}\n className={cn(\n 'absolute bottom-[4.5rem] left-1/2 z-20 flex max-w-[calc(100%-1.5rem)] -translate-x-1/2 items-center gap-1.5 rounded-full border border-border bg-background/95 p-1 pl-1.5 shadow-lg backdrop-blur',\n showing && 'invisible'\n )}\n role=\"toolbar\"\n aria-label=\"Gathered clips\"\n data-testid=\"spotlight-strip\"\n >\n <ul className=\"flex min-w-0 items-center gap-1 overflow-x-auto [scrollbar-width:none]\">\n {clips.map((clip, index) => (\n <li\n key={clip.id}\n className=\"group relative h-8 shrink-0 overflow-hidden rounded-md border border-border bg-white\"\n style={{ width: `${Math.min(4.5, Math.max(1.5, 2 * clip.aspect))}rem` }}\n title={clip.caption}\n data-spotlight-clip={clip.id}\n data-testid={`spotlight-chip-${index}`}\n >\n <ClipImage clip={clip} className=\"h-full w-full\" />\n <button\n type=\"button\"\n className=\"absolute inset-0 flex items-center justify-center bg-black/50 text-white opacity-0 transition-opacity focus-visible:opacity-100 group-hover:opacity-100\"\n onClick={() => actions.remove(clip.id)}\n aria-label={`Remove ${clip.caption}`}\n data-testid={`spotlight-remove-${index}`}\n >\n <X className=\"size-3.5\" />\n </button>\n </li>\n ))}\n </ul>\n <span className=\"shrink-0 text-xs text-muted-foreground\" data-testid=\"spotlight-count\">\n {count}\n </span>\n <Popover open={noteOpen} onOpenChange={setNoteOpen}>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant={note ? 'secondary' : 'ghost'}\n size=\"sm\"\n className=\"h-7 max-w-40 shrink-0 gap-1 rounded-full px-2 text-xs\"\n title={note ? `Note: ${note}` : 'Type a note to show as the title'}\n data-testid=\"spotlight-note\"\n >\n <StickyNote className=\"size-3.5 shrink-0\" />\n {note ? <span className=\"truncate\">{note}</span> : <span>Note</span>}\n </Button>\n </PopoverTrigger>\n <PopoverContent side=\"top\" className=\"w-72 p-2\">\n <Input\n autoFocus\n value={note}\n placeholder=\"Type a note\"\n onChange={(event) => actions.setNote(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') setNoteOpen(false)\n }}\n data-testid=\"spotlight-note-input\"\n />\n </PopoverContent>\n </Popover>\n <div className=\"w-44 shrink-0 [&_[role=combobox]]:h-7 [&_[role=combobox]]:rounded-full [&_[role=combobox]]:text-xs\">\n <DocumentPicker\n groups={switcher.groups}\n value=\"\"\n onValueChange={switcher.open}\n labels={{ placeholder: 'Clip another document' }}\n />\n </div>\n <Button\n type=\"button\"\n size=\"sm\"\n className=\"h-7 shrink-0 gap-1 rounded-full px-3 text-xs\"\n onClick={actions.show}\n title=\"Show the clips over the document (S)\"\n data-testid=\"spotlight-show\"\n >\n <Presentation className=\"size-3.5\" />\n Show\n </Button>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-7 shrink-0 rounded-full\"\n aria-label=\"More\"\n data-testid=\"spotlight-more\"\n >\n <MoreHorizontal className=\"size-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" side=\"top\">\n <DropdownMenuItem onSelect={actions.keep} data-testid=\"spotlight-menu-keep\">\n <Presentation className=\"mr-2 size-4\" /> Keep as slide\n </DropdownMenuItem>\n <DropdownMenuItem onSelect={actions.addToTray} data-testid=\"spotlight-menu-tray\">\n <Inbox className=\"mr-2 size-4\" /> Add to the presentation tray\n </DropdownMenuItem>\n <DropdownMenuItem onSelect={actions.clear} data-testid=\"spotlight-menu-clear\">\n <Eraser className=\"mr-2 size-4\" /> Clear the clips\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n {showing && <SpotlightView fromRect={fromRect} />}\n </>\n )\n}\n"]}