@embedpdf/react 3.0.0-next.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 (141) hide show
  1. package/LICENSE +192 -0
  2. package/dist/AnnotationListSnapshot-W0RCsyDE-dWna4TTJ.d.cts +2593 -0
  3. package/dist/AnnotationListSnapshot-W0RCsyDE-dWna4TTJ.d.ts +2593 -0
  4. package/dist/annotation-B6jwfTCh.d.cts +173 -0
  5. package/dist/annotation-Ct6dFtzJ.cjs +913 -0
  6. package/dist/annotation-Ct6dFtzJ.cjs.map +1 -0
  7. package/dist/annotation-DjGWJljY.js +829 -0
  8. package/dist/annotation-DjGWJljY.js.map +1 -0
  9. package/dist/annotation-menu.cjs +115 -0
  10. package/dist/annotation-menu.cjs.map +1 -0
  11. package/dist/annotation-menu.d.cts +7 -0
  12. package/dist/annotation-menu.d.ts +7 -0
  13. package/dist/annotation-menu.js +99 -0
  14. package/dist/annotation-menu.js.map +1 -0
  15. package/dist/annotation-nUMefyjw.d.ts +173 -0
  16. package/dist/annotation.cjs +25 -0
  17. package/dist/annotation.d.cts +3 -0
  18. package/dist/annotation.d.ts +3 -0
  19. package/dist/annotation.js +4 -0
  20. package/dist/commands.cjs +59 -0
  21. package/dist/commands.cjs.map +1 -0
  22. package/dist/commands.d.cts +20 -0
  23. package/dist/commands.d.ts +20 -0
  24. package/dist/commands.js +47 -0
  25. package/dist/commands.js.map +1 -0
  26. package/dist/form.cjs +560 -0
  27. package/dist/form.cjs.map +1 -0
  28. package/dist/form.d.cts +52 -0
  29. package/dist/form.d.ts +52 -0
  30. package/dist/form.js +545 -0
  31. package/dist/form.js.map +1 -0
  32. package/dist/i18n.cjs +52 -0
  33. package/dist/i18n.cjs.map +1 -0
  34. package/dist/i18n.d.cts +30 -0
  35. package/dist/i18n.d.ts +30 -0
  36. package/dist/i18n.js +42 -0
  37. package/dist/i18n.js.map +1 -0
  38. package/dist/index.cjs +240 -0
  39. package/dist/index.d.cts +37 -0
  40. package/dist/index.d.ts +37 -0
  41. package/dist/index.js +37 -0
  42. package/dist/interaction.cjs +152 -0
  43. package/dist/interaction.cjs.map +1 -0
  44. package/dist/interaction.d.cts +46 -0
  45. package/dist/interaction.d.ts +46 -0
  46. package/dist/interaction.js +139 -0
  47. package/dist/interaction.js.map +1 -0
  48. package/dist/link.cjs +108 -0
  49. package/dist/link.cjs.map +1 -0
  50. package/dist/link.d.cts +16 -0
  51. package/dist/link.d.ts +16 -0
  52. package/dist/link.js +98 -0
  53. package/dist/link.js.map +1 -0
  54. package/dist/metadata.cjs +25 -0
  55. package/dist/metadata.cjs.map +1 -0
  56. package/dist/metadata.d.cts +11 -0
  57. package/dist/metadata.d.ts +11 -0
  58. package/dist/metadata.js +16 -0
  59. package/dist/metadata.js.map +1 -0
  60. package/dist/page-edit.cjs +20 -0
  61. package/dist/page-edit.cjs.map +1 -0
  62. package/dist/page-edit.d.cts +7 -0
  63. package/dist/page-edit.d.ts +7 -0
  64. package/dist/page-edit.js +11 -0
  65. package/dist/page-edit.js.map +1 -0
  66. package/dist/page-view.cjs +107 -0
  67. package/dist/page-view.cjs.map +1 -0
  68. package/dist/page-view.d.cts +23 -0
  69. package/dist/page-view.d.ts +23 -0
  70. package/dist/page-view.js +95 -0
  71. package/dist/page-view.js.map +1 -0
  72. package/dist/redaction.cjs +35 -0
  73. package/dist/redaction.cjs.map +1 -0
  74. package/dist/redaction.d.cts +14 -0
  75. package/dist/redaction.d.ts +14 -0
  76. package/dist/redaction.js +25 -0
  77. package/dist/redaction.js.map +1 -0
  78. package/dist/render.cjs +142 -0
  79. package/dist/render.cjs.map +1 -0
  80. package/dist/render.d.cts +32 -0
  81. package/dist/render.d.ts +32 -0
  82. package/dist/render.js +131 -0
  83. package/dist/render.js.map +1 -0
  84. package/dist/rolldown-runtime-DDXJien1.cjs +50 -0
  85. package/dist/runtime-CMxIuF2O.d.cts +195 -0
  86. package/dist/runtime-DV98q7I0.d.ts +195 -0
  87. package/dist/runtime.cjs +314 -0
  88. package/dist/runtime.cjs.map +1 -0
  89. package/dist/runtime.d.cts +3 -0
  90. package/dist/runtime.d.ts +3 -0
  91. package/dist/runtime.js +287 -0
  92. package/dist/runtime.js.map +1 -0
  93. package/dist/scrollbar.cjs +241 -0
  94. package/dist/scrollbar.cjs.map +1 -0
  95. package/dist/scrollbar.d.cts +42 -0
  96. package/dist/scrollbar.d.ts +42 -0
  97. package/dist/scrollbar.js +213 -0
  98. package/dist/scrollbar.js.map +1 -0
  99. package/dist/search.cjs +70 -0
  100. package/dist/search.cjs.map +1 -0
  101. package/dist/search.d.cts +35 -0
  102. package/dist/search.d.ts +35 -0
  103. package/dist/search.js +58 -0
  104. package/dist/search.js.map +1 -0
  105. package/dist/selection.cjs +60 -0
  106. package/dist/selection.cjs.map +1 -0
  107. package/dist/selection.d.cts +12 -0
  108. package/dist/selection.d.ts +12 -0
  109. package/dist/selection.js +49 -0
  110. package/dist/selection.js.map +1 -0
  111. package/dist/shell.cjs +57 -0
  112. package/dist/shell.cjs.map +1 -0
  113. package/dist/shell.d.cts +27 -0
  114. package/dist/shell.d.ts +27 -0
  115. package/dist/shell.js +46 -0
  116. package/dist/shell.js.map +1 -0
  117. package/dist/stage.cjs +343 -0
  118. package/dist/stage.cjs.map +1 -0
  119. package/dist/stage.d.cts +104 -0
  120. package/dist/stage.d.ts +104 -0
  121. package/dist/stage.js +327 -0
  122. package/dist/stage.js.map +1 -0
  123. package/dist/stamp.cjs +76 -0
  124. package/dist/stamp.cjs.map +1 -0
  125. package/dist/stamp.d.cts +28 -0
  126. package/dist/stamp.d.ts +28 -0
  127. package/dist/stamp.js +63 -0
  128. package/dist/stamp.js.map +1 -0
  129. package/dist/toolbar.cjs +515 -0
  130. package/dist/toolbar.cjs.map +1 -0
  131. package/dist/toolbar.d.cts +100 -0
  132. package/dist/toolbar.d.ts +100 -0
  133. package/dist/toolbar.js +505 -0
  134. package/dist/toolbar.js.map +1 -0
  135. package/dist/views.cjs +35 -0
  136. package/dist/views.cjs.map +1 -0
  137. package/dist/views.d.cts +21 -0
  138. package/dist/views.d.ts +21 -0
  139. package/dist/views.js +26 -0
  140. package/dist/views.js.map +1 -0
  141. package/package.json +158 -0
@@ -0,0 +1,505 @@
1
+ import { useCapability, useDocumentId, useKernel, useKernelValue } from "./runtime.js";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { useEffect, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
5
+ import { CommandsToken, resolvedCommandsEqual } from "@embedpdf/plugin-commands";
6
+ import { filterBar, normalizeBar, projectOverflow, projectStrip, solve } from "@embedpdf/core-ui";
7
+ export * from "@embedpdf/core-ui";
8
+ //#region src/toolbar.tsx
9
+ const stripGroupsEqual = (a, b) => a.length === b.length && a.every((g, i) => g.id === b[i].id && g.commands.length === b[i].commands.length && g.commands.every((c, j) => resolvedCommandsEqual(c, b[i].commands[j])));
10
+ const NO_STRIP_GROUPS = [];
11
+ /**
12
+ * The live projection of a bar through the command registry — <Toolbar>'s
13
+ * un-measured sibling, for contextual strips (ChromeSchema.strips). The schema
14
+ * declares what COULD appear; each command's `visible` derivation decides what
15
+ * DOES; null means nothing currently applies, so `if (!view) return null` is
16
+ * the caller's entire show/hide logic. `execute` is pre-bound to this
17
+ * subtree's document. Reads are value-equal (resolvedCommandsEqual), so
18
+ * consumers re-render on real change, not on every store action.
19
+ */
20
+ function useStripView(bar) {
21
+ const commands = useCapability(CommandsToken);
22
+ const documentId = useDocumentId();
23
+ const normalized = useMemo(() => bar ? normalizeBar(bar) : null, [bar]);
24
+ const groups = useKernelValue(() => {
25
+ if (!normalized) return NO_STRIP_GROUPS;
26
+ const resolved = /* @__PURE__ */ new Map();
27
+ const visible = (id) => {
28
+ const cmd = commands.resolve(id, documentId ?? void 0);
29
+ if (cmd) resolved.set(id, cmd);
30
+ return cmd?.visible === true;
31
+ };
32
+ return projectStrip(normalized, visible).map((g) => ({
33
+ id: g.id,
34
+ labelKey: g.labelKey,
35
+ commands: g.commands.map((id) => resolved.get(id))
36
+ }));
37
+ }, stripGroupsEqual);
38
+ return useMemo(() => groups.length === 0 ? null : {
39
+ groups,
40
+ execute: (id) => commands.execute(id, documentId ?? void 0)
41
+ }, [
42
+ groups,
43
+ commands,
44
+ documentId
45
+ ]);
46
+ }
47
+ /** Report this node's border-box width under `k`, live, via ResizeObserver. */
48
+ function Measured({ k, onWidth, children }) {
49
+ const ref = useRef(null);
50
+ useLayoutEffect(() => {
51
+ const el = ref.current;
52
+ if (!el) return;
53
+ const report = () => onWidth(k, el.getBoundingClientRect().width);
54
+ report();
55
+ const observer = new ResizeObserver(report);
56
+ observer.observe(el);
57
+ return () => observer.disconnect();
58
+ }, [k, onWidth]);
59
+ return /* @__PURE__ */ jsx("span", {
60
+ ref,
61
+ style: {
62
+ display: "inline-flex",
63
+ flexShrink: 0
64
+ },
65
+ children
66
+ });
67
+ }
68
+ /**
69
+ * The socket for an unregistered custom unit: a real `<slot>` element. Inside
70
+ * the <embedpdf-viewer> shadow root the browser projects same-named light-DOM
71
+ * children into it; anywhere else it just displays its fallback (the terminal
72
+ * command). Measured live — the projected content lives in the HOST's world,
73
+ * so its width can only be observed where it actually renders.
74
+ */
75
+ function NativeSlotSocket({ name, k, onWidth, children }) {
76
+ const ref = useRef(null);
77
+ useLayoutEffect(() => {
78
+ const el = ref.current;
79
+ if (!el) return;
80
+ const report = () => onWidth(k, el.getBoundingClientRect().width);
81
+ report();
82
+ const observer = new ResizeObserver(report);
83
+ observer.observe(el);
84
+ return () => observer.disconnect();
85
+ }, [k, onWidth]);
86
+ return /* @__PURE__ */ jsx("slot", {
87
+ ref,
88
+ name,
89
+ style: {
90
+ display: "inline-flex",
91
+ alignItems: "center",
92
+ flexShrink: 0
93
+ },
94
+ children
95
+ });
96
+ }
97
+ const measureLayerStyle = {
98
+ position: "absolute",
99
+ left: 0,
100
+ top: 0,
101
+ height: 0,
102
+ overflow: "hidden",
103
+ visibility: "hidden",
104
+ pointerEvents: "none",
105
+ display: "flex",
106
+ whiteSpace: "nowrap"
107
+ };
108
+ const defaultRenderCommand = (cmd, variant, run) => /* @__PURE__ */ jsxs("button", {
109
+ type: "button",
110
+ onClick: run,
111
+ disabled: !cmd.enabled,
112
+ "aria-pressed": cmd.active || void 0,
113
+ "aria-haspopup": cmd.menu ? "menu" : void 0,
114
+ title: cmd.label,
115
+ style: {
116
+ display: "inline-flex",
117
+ alignItems: "center",
118
+ gap: 4,
119
+ padding: "4px 8px",
120
+ whiteSpace: "nowrap",
121
+ background: cmd.active ? "rgba(0,0,0,0.12)" : "transparent",
122
+ border: "1px solid rgba(0,0,0,0.15)",
123
+ borderRadius: 4,
124
+ cursor: cmd.enabled ? "pointer" : "default",
125
+ opacity: cmd.enabled ? 1 : .4
126
+ },
127
+ children: [variant === "label" ? cmd.label : cmd.icon ?? cmd.label, variant === "icon+label" ? ` ${cmd.label}` : null]
128
+ });
129
+ const defaultRenderSeparator = () => /* @__PURE__ */ jsx("span", { style: {
130
+ width: 1,
131
+ alignSelf: "stretch",
132
+ background: "currentColor",
133
+ opacity: .2
134
+ } });
135
+ const defaultRenderOverflowTrigger = (isOpen, toggle) => /* @__PURE__ */ jsx("button", {
136
+ type: "button",
137
+ onClick: toggle,
138
+ "aria-haspopup": "menu",
139
+ "aria-expanded": isOpen,
140
+ title: "More",
141
+ style: {
142
+ display: "inline-flex",
143
+ alignItems: "center",
144
+ padding: "4px 8px",
145
+ border: "1px solid rgba(0,0,0,0.15)",
146
+ borderRadius: 4,
147
+ background: isOpen ? "rgba(0,0,0,0.12)" : "transparent",
148
+ cursor: "pointer"
149
+ },
150
+ children: "⋯"
151
+ });
152
+ function DefaultOverflowMenu({ view }) {
153
+ if (!view.isOpen) return null;
154
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
155
+ style: {
156
+ position: "fixed",
157
+ inset: 0,
158
+ zIndex: 40
159
+ },
160
+ onClick: view.close
161
+ }), /* @__PURE__ */ jsx("div", {
162
+ role: "menu",
163
+ style: {
164
+ position: "absolute",
165
+ right: 0,
166
+ top: "100%",
167
+ zIndex: 41,
168
+ minWidth: 200,
169
+ padding: 4,
170
+ background: "white",
171
+ border: "1px solid rgba(0,0,0,0.15)",
172
+ borderRadius: 6,
173
+ boxShadow: "0 4px 16px rgba(0,0,0,0.15)"
174
+ },
175
+ children: view.sections.map((section, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [i > 0 && /* @__PURE__ */ jsx("div", { style: {
176
+ height: 1,
177
+ background: "rgba(0,0,0,0.1)",
178
+ margin: "4px 0"
179
+ } }), section.rows.map((row) => {
180
+ const cmd = view.resolve(row.command);
181
+ if (!cmd) return null;
182
+ const isSubmenu = row.type === "submenu";
183
+ return /* @__PURE__ */ jsxs("button", {
184
+ type: "button",
185
+ role: section.role === "radio" ? "menuitemradio" : "menuitem",
186
+ "aria-checked": section.role === "radio" ? cmd.active : void 0,
187
+ disabled: !cmd.enabled,
188
+ onClick: () => {
189
+ view.execute(row.command);
190
+ if (!isSubmenu) view.close();
191
+ },
192
+ style: {
193
+ display: "flex",
194
+ width: "100%",
195
+ alignItems: "center",
196
+ justifyContent: "space-between",
197
+ gap: 16,
198
+ padding: "6px 8px",
199
+ border: "none",
200
+ background: "transparent",
201
+ cursor: cmd.enabled ? "pointer" : "default",
202
+ opacity: cmd.enabled ? 1 : .4,
203
+ whiteSpace: "nowrap"
204
+ },
205
+ children: [/* @__PURE__ */ jsxs("span", { children: [cmd.active && section.role === "radio" ? "• " : "", cmd.label] }), isSubmenu ? /* @__PURE__ */ jsx("span", { children: "▸" }) : null]
206
+ }, row.command);
207
+ })] }, i))
208
+ })] });
209
+ }
210
+ function DefaultCollapsed({ view }) {
211
+ if (view.collapse === "select") return /* @__PURE__ */ jsx("select", {
212
+ value: view.commands.find((c) => c.active)?.id ?? "",
213
+ onChange: (e) => view.execute(e.target.value),
214
+ style: {
215
+ padding: "4px 6px",
216
+ borderRadius: 4
217
+ },
218
+ children: view.commands.map((c) => /* @__PURE__ */ jsx("option", {
219
+ value: c.id,
220
+ disabled: !c.enabled,
221
+ children: c.label
222
+ }, c.id))
223
+ });
224
+ return /* @__PURE__ */ jsx(CollapsedMenuButton, { view });
225
+ }
226
+ function CollapsedMenuButton({ view }) {
227
+ const [isOpen, setOpen] = useState(false);
228
+ const sections = [{
229
+ labelKey: view.labelKey,
230
+ role: view.role === "tabs" ? "radio" : void 0,
231
+ rows: view.commands.map((c) => ({
232
+ type: "command",
233
+ command: c.id
234
+ }))
235
+ }];
236
+ return /* @__PURE__ */ jsxs("span", {
237
+ style: {
238
+ position: "relative",
239
+ display: "inline-flex"
240
+ },
241
+ children: [defaultRenderOverflowTrigger(isOpen, () => setOpen((o) => !o)), /* @__PURE__ */ jsx(DefaultOverflowMenu, { view: {
242
+ sections,
243
+ isOpen,
244
+ close: () => setOpen(false),
245
+ resolve: (id) => view.commands.find((c) => c.id === id) ?? null,
246
+ execute: view.execute
247
+ } })]
248
+ });
249
+ }
250
+ /** Default shed-group disclosure: a chevron opening a radio menu of the
251
+ * hidden children — v2's overflow-tabs-button, derived. */
252
+ function DefaultGroupTrigger({ view }) {
253
+ const [isOpen, setOpen] = useState(false);
254
+ const someActive = view.commands.some((c) => c.active);
255
+ const sections = [{
256
+ labelKey: view.labelKey,
257
+ role: view.role === "tabs" ? "radio" : void 0,
258
+ rows: view.commands.map((c) => ({
259
+ type: "command",
260
+ command: c.id
261
+ }))
262
+ }];
263
+ return /* @__PURE__ */ jsxs("span", {
264
+ style: {
265
+ position: "relative",
266
+ display: "inline-flex"
267
+ },
268
+ children: [/* @__PURE__ */ jsx("button", {
269
+ type: "button",
270
+ onClick: () => setOpen((o) => !o),
271
+ "aria-haspopup": "menu",
272
+ "aria-expanded": isOpen,
273
+ title: "More",
274
+ style: {
275
+ display: "inline-flex",
276
+ alignItems: "center",
277
+ padding: "4px 6px",
278
+ border: "1px solid rgba(0,0,0,0.15)",
279
+ borderRadius: 4,
280
+ background: isOpen || someActive ? "rgba(0,0,0,0.12)" : "transparent",
281
+ cursor: "pointer"
282
+ },
283
+ children: "▾"
284
+ }), /* @__PURE__ */ jsx(DefaultOverflowMenu, { view: {
285
+ sections,
286
+ isOpen,
287
+ close: () => setOpen(false),
288
+ resolve: (id) => view.commands.find((c) => c.id === id) ?? null,
289
+ execute: view.execute
290
+ } })]
291
+ });
292
+ }
293
+ const unitKey = (u, variant) => `u:${u.key}@${variant}`;
294
+ const groupKey = (id) => `g:${id}`;
295
+ const groupTriggerKey = (id) => `gt:${id}`;
296
+ const TRIGGER_KEY = "t:";
297
+ function Toolbar({ bar, gap = 8, separatorWidth = 1, className, style, renderCommand = defaultRenderCommand, renderCustom, renderCollapsed, renderGroupTrigger, renderSeparator = defaultRenderSeparator, renderOverflowTrigger = defaultRenderOverflowTrigger, renderOverflowMenu }) {
298
+ const kernel = useKernel();
299
+ const commands = useCapability(CommandsToken);
300
+ const documentId = useDocumentId();
301
+ const [, force] = useReducer((x) => x + 1, 0);
302
+ useEffect(() => kernel.subscribe(force), [kernel]);
303
+ const containerRef = useRef(null);
304
+ const [containerWidth, setContainerWidth] = useState(0);
305
+ useLayoutEffect(() => {
306
+ const el = containerRef.current;
307
+ if (!el) return;
308
+ const observer = new ResizeObserver((entries) => {
309
+ const w = entries[entries.length - 1]?.contentRect.width;
310
+ if (w !== void 0) setContainerWidth((prev) => Math.abs(prev - w) > .5 ? w : prev);
311
+ });
312
+ observer.observe(el);
313
+ setContainerWidth(el.clientWidth);
314
+ return () => observer.disconnect();
315
+ }, []);
316
+ const widthsRef = useRef(/* @__PURE__ */ new Map());
317
+ const [, bumpMeasureVersion] = useReducer((x) => x + 1, 0);
318
+ const onWidth = useMemo(() => (key, width) => {
319
+ const prev = widthsRef.current.get(key);
320
+ if (prev !== void 0 && Math.abs(prev - width) <= .5) return;
321
+ widthsRef.current.set(key, width);
322
+ bumpMeasureVersion();
323
+ }, []);
324
+ const resolveCmd = (id) => commands.resolve(id, documentId ?? void 0);
325
+ const executeCmd = (id) => commands.execute(id, documentId ?? void 0);
326
+ const commandOf = (u) => u.kind === "command" ? u.command : u.terminal;
327
+ const visibleBar = filterBar(useMemo(() => normalizeBar(bar), [bar]), (u) => resolveCmd(commandOf(u))?.visible === true);
328
+ const fit = solve(visibleBar, {
329
+ unit: (key, variant) => widthsRef.current.get(`u:${key}@${variant}`),
330
+ groupCollapsed: (id) => widthsRef.current.get(groupKey(id)),
331
+ groupTrigger: (id) => widthsRef.current.get(groupTriggerKey(id)),
332
+ overflowTrigger: widthsRef.current.get(TRIGGER_KEY) ?? 32,
333
+ gap,
334
+ separator: separatorWidth + gap
335
+ }, containerWidth);
336
+ const overflowSections = projectOverflow(visibleBar, fit, (id) => commands.menuTarget(id));
337
+ const [overflowOpen, setOverflowOpen] = useState(false);
338
+ useEffect(() => {
339
+ if (!fit.hasOverflow) setOverflowOpen(false);
340
+ }, [fit.hasOverflow]);
341
+ /** External = custom unit with no registered renderer → native socket,
342
+ * live-measured (it must NOT appear in the measurement layer: only the
343
+ * first same-named <slot> in tree order receives the projected nodes). */
344
+ const isExternalSlot = (u) => u.kind === "custom" && !renderCustom?.[u.slot];
345
+ const renderUnitAt = (u, variant, layer) => {
346
+ if (u.kind === "custom") {
347
+ const registered = renderCustom?.[u.slot];
348
+ if (registered) {
349
+ const custom = registered(variant, {
350
+ layer,
351
+ measure: (width) => onWidth(unitKey(u, variant), width)
352
+ });
353
+ if (custom !== void 0) return custom;
354
+ } else if (layer === "live") {
355
+ const cmd = resolveCmd(u.terminal);
356
+ return /* @__PURE__ */ jsx(NativeSlotSocket, {
357
+ name: u.slot,
358
+ k: unitKey(u, variant),
359
+ onWidth,
360
+ children: cmd ? renderCommand(cmd, "icon", () => executeCmd(u.terminal)) : null
361
+ });
362
+ } else return null;
363
+ const cmd = resolveCmd(u.terminal);
364
+ return cmd ? renderCommand(cmd, "icon", () => executeCmd(u.terminal)) : null;
365
+ }
366
+ const cmd = resolveCmd(u.command);
367
+ if (!cmd) return renderCommand({
368
+ id: u.command,
369
+ label: u.command,
370
+ shortcuts: [],
371
+ enabled: false,
372
+ active: false,
373
+ visible: true,
374
+ categories: []
375
+ }, variant, () => {});
376
+ return renderCommand(cmd, variant, () => executeCmd(u.command));
377
+ };
378
+ const collapsedView = (g) => ({
379
+ id: g.id,
380
+ labelKey: g.labelKey,
381
+ collapse: g.collapse ?? "menu",
382
+ role: g.role,
383
+ commands: g.units.map((u) => resolveCmd(commandOf(u))).filter((c) => c !== null && c.visible),
384
+ execute: executeCmd
385
+ });
386
+ const renderCollapsedGroup = (g) => renderCollapsed ? renderCollapsed(collapsedView(g)) : /* @__PURE__ */ jsx(DefaultCollapsed, { view: collapsedView(g) });
387
+ /** The disclosure view: shed children for the live trigger; the WHOLE group
388
+ * for the measured trigger, so width is budgeted at its fullest content. */
389
+ const disclosureView = (g, allChildren) => ({
390
+ id: g.id,
391
+ labelKey: g.labelKey,
392
+ role: g.role,
393
+ commands: g.units.filter((u) => allChildren || fit.units.get(u.key)?.kind === "shed").map((u) => resolveCmd(commandOf(u))).filter((c) => c !== null && c.visible),
394
+ execute: executeCmd
395
+ });
396
+ const renderDisclosure = (g, allChildren) => renderGroupTrigger ? renderGroupTrigger(disclosureView(g, allChildren)) : /* @__PURE__ */ jsx(DefaultGroupTrigger, { view: disclosureView(g, allChildren) });
397
+ const renderLiveGroup = (g) => {
398
+ const assignment = fit.groups.get(g.id);
399
+ if (!assignment || assignment.overflowed) return [];
400
+ if (assignment.collapsed) return [/* @__PURE__ */ jsx(React.Fragment, { children: renderCollapsedGroup(g) }, g.id)];
401
+ const nodes = [];
402
+ for (const u of g.units) {
403
+ const a = fit.units.get(u.key);
404
+ if (a?.kind !== "variant") continue;
405
+ nodes.push(/* @__PURE__ */ jsx(React.Fragment, { children: renderUnitAt(u, a.variant, "live") }, u.key));
406
+ }
407
+ if (assignment.shedCount > 0) nodes.push(/* @__PURE__ */ jsx(React.Fragment, { children: renderDisclosure(g, false) }, `${g.id}::trigger`));
408
+ return nodes.length ? [/* @__PURE__ */ jsx(React.Fragment, { children: nodes }, g.id)] : [];
409
+ };
410
+ const renderLiveSection = (section) => {
411
+ if (!section) return null;
412
+ return section.groups.map((g) => ({
413
+ id: g.id,
414
+ nodes: renderLiveGroup(g)
415
+ })).filter((g) => g.nodes.length > 0).map((g, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [i > 0 && renderSeparator(), g.nodes] }, g.id));
416
+ };
417
+ const sectionByName = (name) => visibleBar.sections.find((s) => s.name === name);
418
+ /**
419
+ * Segment layout — v2's spacer model, natively. The center segment carries
420
+ * auto margins: it BALANCES in the leftover space (true center when the
421
+ * flanks are symmetric) and yields when they aren't, collapsing to zero
422
+ * before anything can overlap. The container itself has NO gap — segments
423
+ * space themselves — so the solver's global budget (sum of unit widths +
424
+ * per-unit gaps) is exactly the layout's minimum width: what the solver
425
+ * says fits, fits. Its cross-segment gap allowance (≤ 2×gap) becomes slack
426
+ * the auto margins absorb, keeping ≥ gap between segments at the floor.
427
+ * Segments never grow or shrink: fit is the SOLVER's job, not flexbox's.
428
+ */
429
+ const sectionStyle = (position) => ({
430
+ display: "flex",
431
+ alignItems: "center",
432
+ gap,
433
+ flex: "0 0 auto",
434
+ ...position === "center" ? {
435
+ marginLeft: "auto",
436
+ marginRight: "auto"
437
+ } : null
438
+ });
439
+ const overflowView = {
440
+ sections: overflowSections,
441
+ isOpen: overflowOpen,
442
+ close: () => setOverflowOpen(false),
443
+ resolve: resolveCmd,
444
+ execute: executeCmd
445
+ };
446
+ return /* @__PURE__ */ jsxs("div", {
447
+ ref: containerRef,
448
+ className,
449
+ style: {
450
+ position: "relative",
451
+ display: "flex",
452
+ alignItems: "center",
453
+ ...style
454
+ },
455
+ children: [
456
+ /* @__PURE__ */ jsx("div", {
457
+ style: sectionStyle("start"),
458
+ children: renderLiveSection(sectionByName("start"))
459
+ }),
460
+ /* @__PURE__ */ jsx("div", {
461
+ style: sectionStyle("center"),
462
+ children: renderLiveSection(sectionByName("center"))
463
+ }),
464
+ /* @__PURE__ */ jsxs("div", {
465
+ style: sectionStyle("end"),
466
+ children: [renderLiveSection(sectionByName("end")), fit.hasOverflow && /* @__PURE__ */ jsxs("span", {
467
+ style: {
468
+ position: "relative",
469
+ display: "inline-flex"
470
+ },
471
+ children: [renderOverflowTrigger(overflowOpen, () => setOverflowOpen((o) => !o)), renderOverflowMenu ? renderOverflowMenu(overflowView) : /* @__PURE__ */ jsx(DefaultOverflowMenu, { view: overflowView })]
472
+ })]
473
+ }),
474
+ /* @__PURE__ */ jsxs("div", {
475
+ "aria-hidden": true,
476
+ style: measureLayerStyle,
477
+ children: [visibleBar.sections.flatMap((s) => s.groups.flatMap((g) => [
478
+ ...g.units.filter((u) => !isExternalSlot(u)).flatMap((u) => u.variants.map((variant) => /* @__PURE__ */ jsx(Measured, {
479
+ k: unitKey(u, variant),
480
+ onWidth,
481
+ children: renderUnitAt(u, variant, "measure")
482
+ }, unitKey(u, variant)))),
483
+ ...g.collapse ? [/* @__PURE__ */ jsx(Measured, {
484
+ k: groupKey(g.id),
485
+ onWidth,
486
+ children: renderCollapsedGroup(g)
487
+ }, groupKey(g.id))] : [],
488
+ ...g.shed ? [/* @__PURE__ */ jsx(Measured, {
489
+ k: groupTriggerKey(g.id),
490
+ onWidth,
491
+ children: renderDisclosure(g, true)
492
+ }, groupTriggerKey(g.id))] : []
493
+ ])), /* @__PURE__ */ jsx(Measured, {
494
+ k: TRIGGER_KEY,
495
+ onWidth,
496
+ children: renderOverflowTrigger(false, () => {})
497
+ })]
498
+ })
499
+ ]
500
+ });
501
+ }
502
+ //#endregion
503
+ export { Toolbar, useStripView };
504
+
505
+ //# sourceMappingURL=toolbar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolbar.js","names":[],"sources":["../src/toolbar.tsx"],"sourcesContent":["/**\n * The headless <Toolbar> — where measurement meets the pure solver.\n *\n * The schema is structure-only; what fits is OBSERVED, not configured:\n *\n * 1. a hidden measurement layer renders every unit in every variant (plus\n * collapsed group forms and the overflow trigger), each wrapped in a\n * ResizeObserver — so locale flips, font loads, browser zoom, and\n * embedder CSS all re-measure themselves with zero handling code;\n * 2. ui-core's solve() assigns each unit a variant / collapsed / overflow —\n * pure, deterministic, tested in isolation;\n * 3. the live row renders the assignment; the overflow menu is DERIVED\n * (projectOverflow) — the complement of the visible set, never authored.\n *\n * Rendering is render-prop driven with functional defaults: the app owns the\n * pixels, this component owns the physics.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/core-ui';\nimport * as React from 'react';\nimport { useEffect, useLayoutEffect, useMemo, useReducer, useRef, useState } from 'react';\nimport { filterBar, normalizeBar, projectOverflow, projectStrip, solve } from '@embedpdf/core-ui';\nimport type {\n BarSchema,\n FitMetrics,\n NormalizedGroup,\n NormalizedSection,\n NormalizedUnit,\n OverflowSection,\n} from '@embedpdf/core-ui';\nimport { CommandsToken, resolvedCommandsEqual } from '@embedpdf/plugin-commands';\nimport type { ResolvedCommand } from '@embedpdf/plugin-commands';\nimport { useCapability, useDocumentId, useKernel, useKernelValue } from './runtime';\n\n// ── public render-prop contracts ─────────────────────────────────────────────\n\nexport interface CollapsedGroupView {\n readonly id: string;\n readonly labelKey?: string;\n readonly collapse: 'menu' | 'select';\n readonly role: 'buttons' | 'tabs';\n /** The group's commands (custom items via their terminal), resolved live. */\n readonly commands: readonly ResolvedCommand[];\n execute(id: string): void;\n}\n\nexport interface OverflowMenuView {\n readonly sections: readonly OverflowSection[];\n readonly isOpen: boolean;\n close(): void;\n resolve(id: string): ResolvedCommand | null;\n execute(id: string): void;\n}\n\n/**\n * A shed group's disclosure — the derived trigger rendered INSIDE the group\n * (v2's overflow-tabs-button). `commands` is what sits behind the trigger:\n * the shed children in bar order, resolved live. In the measurement layer the\n * trigger is measured with the FULL group as content, so a count-dependent\n * trigger (e.g. a \"+3\" badge) is budgeted at its widest form.\n */\nexport interface GroupDisclosureView {\n readonly id: string;\n readonly labelKey?: string;\n readonly role: 'buttons' | 'tabs';\n readonly commands: readonly ResolvedCommand[];\n execute(id: string): void;\n}\n\n// ── the strip view — a bar projected through the registry, live ──────────────\n\nexport interface StripViewGroup {\n readonly id: string;\n readonly labelKey?: string;\n /** Visible commands, bar order. Groups are separator boundaries. */\n readonly commands: readonly ResolvedCommand[];\n}\n\n/** A contextual strip, resolved: only visible commands, only non-empty groups. */\nexport interface StripView {\n readonly groups: readonly StripViewGroup[];\n execute(id: string): void;\n}\n\nconst stripGroupsEqual = (a: readonly StripViewGroup[], b: readonly StripViewGroup[]): boolean =>\n a.length === b.length &&\n a.every(\n (g, i) =>\n g.id === b[i].id &&\n g.commands.length === b[i].commands.length &&\n g.commands.every((c, j) => resolvedCommandsEqual(c, b[i].commands[j])),\n );\n\nconst NO_STRIP_GROUPS: readonly StripViewGroup[] = [];\n\n/**\n * The live projection of a bar through the command registry — <Toolbar>'s\n * un-measured sibling, for contextual strips (ChromeSchema.strips). The schema\n * declares what COULD appear; each command's `visible` derivation decides what\n * DOES; null means nothing currently applies, so `if (!view) return null` is\n * the caller's entire show/hide logic. `execute` is pre-bound to this\n * subtree's document. Reads are value-equal (resolvedCommandsEqual), so\n * consumers re-render on real change, not on every store action.\n */\nexport function useStripView(bar: BarSchema | undefined): StripView | null {\n const commands = useCapability(CommandsToken);\n const documentId = useDocumentId();\n const normalized = useMemo(() => (bar ? normalizeBar(bar) : null), [bar]);\n const groups = useKernelValue(() => {\n if (!normalized) return NO_STRIP_GROUPS;\n const resolved = new Map<string, ResolvedCommand>();\n const visible = (id: string) => {\n const cmd = commands.resolve(id, documentId ?? undefined);\n if (cmd) resolved.set(id, cmd);\n return cmd?.visible === true;\n };\n return projectStrip(normalized, visible).map((g) => ({\n id: g.id,\n labelKey: g.labelKey,\n commands: g.commands.map((id) => resolved.get(id)!),\n }));\n }, stripGroupsEqual);\n return useMemo(\n () =>\n groups.length === 0\n ? null\n : { groups, execute: (id: string) => commands.execute(id, documentId ?? undefined) },\n [groups, commands, documentId],\n );\n}\n\nexport interface ToolbarProps {\n bar: BarSchema;\n /** Px between adjacent items (CSS flex gap; the solver budgets the same number). */\n gap?: number;\n /** Width of the separator ELEMENT your renderSeparator draws. Default 1. */\n separatorWidth?: number;\n className?: string;\n style?: React.CSSProperties;\n /** A command button at a given variant. Default: a plain <button>. */\n renderCommand?: (cmd: ResolvedCommand, variant: string, run: () => void) => React.ReactNode;\n /**\n * Renderers for custom slots, per named variant. A custom unit with NO\n * entry here renders as a native `<slot name={slot}>` socket with its\n * terminal command as fallback content: in light DOM that displays the\n * fallback (today's behavior); inside a shadow root the host's light-DOM\n * children project into it — the children-as-slots contract. Sockets are\n * measured LIVE (they can't be duplicated into the measurement layer:\n * only the first same-named slot in tree order gets the projected nodes).\n */\n renderCustom?: Record<string, (variant: string, ctx: CustomSlotCtx) => React.ReactNode>;\n /** A group in its collapsed form. Default: <select> for 'select', menu button for 'menu'. */\n renderCollapsed?: (view: CollapsedGroupView) => React.ReactNode;\n /** A shed group's disclosure trigger (+ its popover — the renderer owns the\n * open state). Default: a chevron button opening a radio menu. */\n renderGroupTrigger?: (view: GroupDisclosureView) => React.ReactNode;\n /** Derived separator between adjacent visible groups. Default: a 1px line. */\n renderSeparator?: () => React.ReactNode;\n renderOverflowTrigger?: (isOpen: boolean, toggle: () => void) => React.ReactNode;\n /** The derived overflow menu. Default: a minimal popover. */\n renderOverflowMenu?: (view: OverflowMenuView) => React.ReactNode;\n}\n\n/** Passed to custom-slot renderers alongside the variant. */\nexport interface CustomSlotCtx {\n /** 'live' = the visible row; 'measure' = the hidden measurement layer. */\n layer: 'live' | 'measure';\n /** Report this unit's width to the solver — for content the measurement\n * layer can't duplicate. Identity is NOT stable across renders; capture\n * via ref if used in an effect. */\n measure: (width: number) => void;\n}\n\n// ── measurement ───────────────────────────────────────────────────────────────\n\n/** Report this node's border-box width under `k`, live, via ResizeObserver. */\nfunction Measured({\n k,\n onWidth,\n children,\n}: {\n k: string;\n onWidth: (key: string, width: number) => void;\n children: React.ReactNode;\n}) {\n const ref = useRef<HTMLSpanElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n const report = () => onWidth(k, el.getBoundingClientRect().width);\n report();\n const observer = new ResizeObserver(report);\n observer.observe(el);\n return () => observer.disconnect();\n }, [k, onWidth]);\n return (\n <span ref={ref} style={{ display: 'inline-flex', flexShrink: 0 }}>\n {children}\n </span>\n );\n}\n\n/**\n * The socket for an unregistered custom unit: a real `<slot>` element. Inside\n * the <embedpdf-viewer> shadow root the browser projects same-named light-DOM\n * children into it; anywhere else it just displays its fallback (the terminal\n * command). Measured live — the projected content lives in the HOST's world,\n * so its width can only be observed where it actually renders.\n */\nfunction NativeSlotSocket({\n name,\n k,\n onWidth,\n children,\n}: {\n name: string;\n k: string;\n onWidth: (key: string, width: number) => void;\n children: React.ReactNode;\n}) {\n const ref = useRef<HTMLSlotElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n const report = () => onWidth(k, el.getBoundingClientRect().width);\n report();\n // Projection changes (a child slotted in/out) resize the socket's\n // inline-flex box, so one observer covers both content and assignment.\n const observer = new ResizeObserver(report);\n observer.observe(el);\n return () => observer.disconnect();\n }, [k, onWidth]);\n return (\n <slot\n ref={ref}\n name={name}\n style={{ display: 'inline-flex', alignItems: 'center', flexShrink: 0 }}\n >\n {children}\n </slot>\n );\n}\n\nconst measureLayerStyle: React.CSSProperties = {\n position: 'absolute',\n left: 0,\n top: 0,\n height: 0,\n overflow: 'hidden',\n visibility: 'hidden',\n pointerEvents: 'none',\n display: 'flex',\n whiteSpace: 'nowrap',\n};\n\n// ── defaults (functional, unstyled-ish; products replace them) ───────────────\n\nconst defaultRenderCommand = (\n cmd: ResolvedCommand,\n variant: string,\n run: () => void,\n): React.ReactNode => (\n <button\n type=\"button\"\n onClick={run}\n disabled={!cmd.enabled}\n aria-pressed={cmd.active || undefined}\n aria-haspopup={cmd.menu ? 'menu' : undefined}\n title={cmd.label}\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: 4,\n padding: '4px 8px',\n whiteSpace: 'nowrap',\n background: cmd.active ? 'rgba(0,0,0,0.12)' : 'transparent',\n border: '1px solid rgba(0,0,0,0.15)',\n borderRadius: 4,\n cursor: cmd.enabled ? 'pointer' : 'default',\n opacity: cmd.enabled ? 1 : 0.4,\n }}\n >\n {variant === 'label' ? cmd.label : (cmd.icon ?? cmd.label)}\n {variant === 'icon+label' ? ` ${cmd.label}` : null}\n </button>\n);\n\nconst defaultRenderSeparator = (): React.ReactNode => (\n <span style={{ width: 1, alignSelf: 'stretch', background: 'currentColor', opacity: 0.2 }} />\n);\n\nconst defaultRenderOverflowTrigger = (isOpen: boolean, toggle: () => void): React.ReactNode => (\n <button\n type=\"button\"\n onClick={toggle}\n aria-haspopup=\"menu\"\n aria-expanded={isOpen}\n title=\"More\"\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n padding: '4px 8px',\n border: '1px solid rgba(0,0,0,0.15)',\n borderRadius: 4,\n background: isOpen ? 'rgba(0,0,0,0.12)' : 'transparent',\n cursor: 'pointer',\n }}\n >\n ⋯\n </button>\n);\n\nfunction DefaultOverflowMenu({ view }: { view: OverflowMenuView }) {\n if (!view.isOpen) return null;\n return (\n <>\n <div style={{ position: 'fixed', inset: 0, zIndex: 40 }} onClick={view.close} />\n <div\n role=\"menu\"\n style={{\n position: 'absolute',\n right: 0,\n top: '100%',\n zIndex: 41,\n minWidth: 200,\n padding: 4,\n background: 'white',\n border: '1px solid rgba(0,0,0,0.15)',\n borderRadius: 6,\n boxShadow: '0 4px 16px rgba(0,0,0,0.15)',\n }}\n >\n {view.sections.map((section, i) => (\n <React.Fragment key={i}>\n {i > 0 && <div style={{ height: 1, background: 'rgba(0,0,0,0.1)', margin: '4px 0' }} />}\n {section.rows.map((row) => {\n const cmd = view.resolve(row.command);\n if (!cmd) return null;\n const isSubmenu = row.type === 'submenu';\n return (\n <button\n key={row.command}\n type=\"button\"\n role={section.role === 'radio' ? 'menuitemradio' : 'menuitem'}\n aria-checked={section.role === 'radio' ? cmd.active : undefined}\n disabled={!cmd.enabled}\n onClick={() => {\n view.execute(row.command);\n if (!isSubmenu) view.close();\n }}\n style={{\n display: 'flex',\n width: '100%',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 16,\n padding: '6px 8px',\n border: 'none',\n background: 'transparent',\n cursor: cmd.enabled ? 'pointer' : 'default',\n opacity: cmd.enabled ? 1 : 0.4,\n whiteSpace: 'nowrap',\n }}\n >\n <span>\n {cmd.active && section.role === 'radio' ? '• ' : ''}\n {cmd.label}\n </span>\n {isSubmenu ? <span>▸</span> : null}\n </button>\n );\n })}\n </React.Fragment>\n ))}\n </div>\n </>\n );\n}\n\nfunction DefaultCollapsed({ view }: { view: CollapsedGroupView }) {\n if (view.collapse === 'select') {\n const active = view.commands.find((c) => c.active);\n return (\n <select\n value={active?.id ?? ''}\n onChange={(e) => view.execute(e.target.value)}\n style={{ padding: '4px 6px', borderRadius: 4 }}\n >\n {view.commands.map((c) => (\n <option key={c.id} value={c.id} disabled={!c.enabled}>\n {c.label}\n </option>\n ))}\n </select>\n );\n }\n // 'menu': reuse the overflow popover, scoped to this group's commands.\n return <CollapsedMenuButton view={view} />;\n}\n\nfunction CollapsedMenuButton({ view }: { view: CollapsedGroupView }) {\n const [isOpen, setOpen] = useState(false);\n const sections: OverflowSection[] = [\n {\n labelKey: view.labelKey,\n role: view.role === 'tabs' ? 'radio' : undefined,\n rows: view.commands.map((c) => ({ type: 'command' as const, command: c.id })),\n },\n ];\n return (\n <span style={{ position: 'relative', display: 'inline-flex' }}>\n {defaultRenderOverflowTrigger(isOpen, () => setOpen((o) => !o))}\n <DefaultOverflowMenu\n view={{\n sections,\n isOpen,\n close: () => setOpen(false),\n resolve: (id) => view.commands.find((c) => c.id === id) ?? null,\n execute: view.execute,\n }}\n />\n </span>\n );\n}\n\n/** Default shed-group disclosure: a chevron opening a radio menu of the\n * hidden children — v2's overflow-tabs-button, derived. */\nfunction DefaultGroupTrigger({ view }: { view: GroupDisclosureView }) {\n const [isOpen, setOpen] = useState(false);\n const someActive = view.commands.some((c) => c.active);\n const sections: OverflowSection[] = [\n {\n labelKey: view.labelKey,\n role: view.role === 'tabs' ? 'radio' : undefined,\n rows: view.commands.map((c) => ({ type: 'command' as const, command: c.id })),\n },\n ];\n return (\n <span style={{ position: 'relative', display: 'inline-flex' }}>\n <button\n type=\"button\"\n onClick={() => setOpen((o) => !o)}\n aria-haspopup=\"menu\"\n aria-expanded={isOpen}\n title=\"More\"\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n padding: '4px 6px',\n border: '1px solid rgba(0,0,0,0.15)',\n borderRadius: 4,\n // hint that the active item is hiding in here\n background: isOpen || someActive ? 'rgba(0,0,0,0.12)' : 'transparent',\n cursor: 'pointer',\n }}\n >\n ▾\n </button>\n <DefaultOverflowMenu\n view={{\n sections,\n isOpen,\n close: () => setOpen(false),\n resolve: (id) => view.commands.find((c) => c.id === id) ?? null,\n execute: view.execute,\n }}\n />\n </span>\n );\n}\n\n// ── the toolbar ───────────────────────────────────────────────────────────────\n\nconst unitKey = (u: NormalizedUnit, variant: string) => `u:${u.key}@${variant}`;\nconst groupKey = (id: string) => `g:${id}`;\nconst groupTriggerKey = (id: string) => `gt:${id}`;\nconst TRIGGER_KEY = 't:';\n\nexport function Toolbar({\n bar,\n gap = 8,\n separatorWidth = 1,\n className,\n style,\n renderCommand = defaultRenderCommand,\n renderCustom,\n renderCollapsed,\n renderGroupTrigger,\n renderSeparator = defaultRenderSeparator,\n renderOverflowTrigger = defaultRenderOverflowTrigger,\n renderOverflowMenu,\n}: ToolbarProps) {\n const kernel = useKernel();\n const commands = useCapability(CommandsToken);\n const documentId = useDocumentId();\n\n // Command state is derived-on-read; re-render on the kernel's change stream\n // so labels/active/visible (and thus the hidden layer's measurements) stay\n // live. The toolbar is small — a per-action render is the simple, correct\n // baseline.\n const [, force] = useReducer((x: number) => x + 1, 0);\n useEffect(() => kernel.subscribe(force), [kernel]);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const [containerWidth, setContainerWidth] = useState(0);\n useLayoutEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n const observer = new ResizeObserver((entries) => {\n const w = entries[entries.length - 1]?.contentRect.width;\n if (w !== undefined) setContainerWidth((prev) => (Math.abs(prev - w) > 0.5 ? w : prev));\n });\n observer.observe(el);\n setContainerWidth(el.clientWidth);\n return () => observer.disconnect();\n }, []);\n\n const widthsRef = useRef(new Map<string, number>());\n const [, bumpMeasureVersion] = useReducer((x: number) => x + 1, 0);\n const onWidth = useMemo(\n () => (key: string, width: number) => {\n const prev = widthsRef.current.get(key);\n if (prev !== undefined && Math.abs(prev - width) <= 0.5) return;\n widthsRef.current.set(key, width);\n bumpMeasureVersion();\n },\n [],\n );\n\n const resolveCmd = (id: string): ResolvedCommand | null =>\n commands.resolve(id, documentId ?? undefined);\n const executeCmd = (id: string) => commands.execute(id, documentId ?? undefined);\n const commandOf = (u: NormalizedUnit) => (u.kind === 'command' ? u.command : u.terminal);\n\n // Structure → visible structure → fit. Cheap enough to run per render; all\n // heavy lifting is O(items), and items is ~dozens.\n const normalized = useMemo(() => normalizeBar(bar), [bar]);\n // `=== true` and not `!== false`: an UNREGISTERED command resolves to null,\n // and a unit the registry can't name must not render or consume budget —\n // the schema validator's dev warning is the feedback channel, not raw text\n // in the toolbar.\n const visibleBar = filterBar(normalized, (u) => resolveCmd(commandOf(u))?.visible === true);\n const metrics: FitMetrics = {\n unit: (key, variant) => widthsRef.current.get(`u:${key}@${variant}`),\n groupCollapsed: (id) => widthsRef.current.get(groupKey(id)),\n groupTrigger: (id) => widthsRef.current.get(groupTriggerKey(id)),\n overflowTrigger: widthsRef.current.get(TRIGGER_KEY) ?? 32,\n gap,\n // The separator is one extra flex child: its element width plus one flex gap.\n separator: separatorWidth + gap,\n };\n const fit = solve(visibleBar, metrics, containerWidth);\n const overflowSections = projectOverflow(visibleBar, fit, (id) => commands.menuTarget(id));\n\n const [overflowOpen, setOverflowOpen] = useState(false);\n useEffect(() => {\n if (!fit.hasOverflow) setOverflowOpen(false);\n }, [fit.hasOverflow]);\n\n /** External = custom unit with no registered renderer → native socket,\n * live-measured (it must NOT appear in the measurement layer: only the\n * first same-named <slot> in tree order receives the projected nodes). */\n const isExternalSlot = (u: NormalizedUnit): boolean =>\n u.kind === 'custom' && !renderCustom?.[u.slot];\n\n // ── unit / group rendering (shared by live row and measure layer) ──────────\n const renderUnitAt = (\n u: NormalizedUnit,\n variant: string,\n layer: 'live' | 'measure',\n ): React.ReactNode => {\n if (u.kind === 'custom') {\n const registered = renderCustom?.[u.slot];\n if (registered) {\n const custom = registered(variant, {\n layer,\n measure: (width) => onWidth(unitKey(u, variant), width),\n });\n if (custom !== undefined) return custom;\n } else if (layer === 'live') {\n const cmd = resolveCmd(u.terminal);\n return (\n <NativeSlotSocket name={u.slot} k={unitKey(u, variant)} onWidth={onWidth}>\n {cmd ? renderCommand(cmd, 'icon', () => executeCmd(u.terminal)) : null}\n </NativeSlotSocket>\n );\n } else {\n return null;\n }\n const cmd = resolveCmd(u.terminal);\n return cmd ? renderCommand(cmd, 'icon', () => executeCmd(u.terminal)) : null;\n }\n const cmd = resolveCmd(u.command);\n if (!cmd)\n return renderCommand(\n {\n id: u.command,\n label: u.command,\n shortcuts: [],\n enabled: false,\n active: false,\n visible: true,\n categories: [],\n },\n variant,\n () => {},\n );\n return renderCommand(cmd, variant, () => executeCmd(u.command));\n };\n\n const collapsedView = (g: NormalizedGroup): CollapsedGroupView => ({\n id: g.id,\n labelKey: g.labelKey,\n collapse: g.collapse ?? 'menu',\n role: g.role,\n commands: g.units\n .map((u) => resolveCmd(commandOf(u)))\n .filter((c): c is ResolvedCommand => c !== null && c.visible),\n execute: executeCmd,\n });\n\n const renderCollapsedGroup = (g: NormalizedGroup): React.ReactNode =>\n renderCollapsed ? (\n renderCollapsed(collapsedView(g))\n ) : (\n <DefaultCollapsed view={collapsedView(g)} />\n );\n\n /** The disclosure view: shed children for the live trigger; the WHOLE group\n * for the measured trigger, so width is budgeted at its fullest content. */\n const disclosureView = (g: NormalizedGroup, allChildren: boolean): GroupDisclosureView => ({\n id: g.id,\n labelKey: g.labelKey,\n role: g.role,\n commands: g.units\n .filter((u) => allChildren || fit.units.get(u.key)?.kind === 'shed')\n .map((u) => resolveCmd(commandOf(u)))\n .filter((c): c is ResolvedCommand => c !== null && c.visible),\n execute: executeCmd,\n });\n\n const renderDisclosure = (g: NormalizedGroup, allChildren: boolean): React.ReactNode =>\n renderGroupTrigger ? (\n renderGroupTrigger(disclosureView(g, allChildren))\n ) : (\n <DefaultGroupTrigger view={disclosureView(g, allChildren)} />\n );\n\n const renderLiveGroup = (g: NormalizedGroup): React.ReactNode[] => {\n const assignment = fit.groups.get(g.id);\n if (!assignment || assignment.overflowed) return [];\n if (assignment.collapsed)\n return [<React.Fragment key={g.id}>{renderCollapsedGroup(g)}</React.Fragment>];\n const nodes: React.ReactNode[] = [];\n for (const u of g.units) {\n const a = fit.units.get(u.key);\n if (a?.kind !== 'variant') continue;\n nodes.push(<React.Fragment key={u.key}>{renderUnitAt(u, a.variant, 'live')}</React.Fragment>);\n }\n // The derived group-local disclosure (v2's overflow-tabs-button).\n if (assignment.shedCount > 0) {\n nodes.push(\n <React.Fragment key={`${g.id}::trigger`}>{renderDisclosure(g, false)}</React.Fragment>,\n );\n }\n return nodes.length ? [<React.Fragment key={g.id}>{nodes}</React.Fragment>] : [];\n };\n\n const renderLiveSection = (section: NormalizedSection | undefined): React.ReactNode => {\n if (!section) return null;\n const rendered = section.groups\n .map((g) => ({ id: g.id, nodes: renderLiveGroup(g) }))\n .filter((g) => g.nodes.length > 0);\n return rendered.map((g, i) => (\n <React.Fragment key={g.id}>\n {i > 0 && renderSeparator()}\n {g.nodes}\n </React.Fragment>\n ));\n };\n\n const sectionByName = (name: 'start' | 'center' | 'end') =>\n visibleBar.sections.find((s) => s.name === name);\n\n /**\n * Segment layout — v2's spacer model, natively. The center segment carries\n * auto margins: it BALANCES in the leftover space (true center when the\n * flanks are symmetric) and yields when they aren't, collapsing to zero\n * before anything can overlap. The container itself has NO gap — segments\n * space themselves — so the solver's global budget (sum of unit widths +\n * per-unit gaps) is exactly the layout's minimum width: what the solver\n * says fits, fits. Its cross-segment gap allowance (≤ 2×gap) becomes slack\n * the auto margins absorb, keeping ≥ gap between segments at the floor.\n * Segments never grow or shrink: fit is the SOLVER's job, not flexbox's.\n */\n const sectionStyle = (position: 'start' | 'center' | 'end'): React.CSSProperties => ({\n display: 'flex',\n alignItems: 'center',\n gap,\n flex: '0 0 auto',\n ...(position === 'center' ? { marginLeft: 'auto', marginRight: 'auto' } : null),\n });\n\n const overflowView: OverflowMenuView = {\n sections: overflowSections,\n isOpen: overflowOpen,\n close: () => setOverflowOpen(false),\n resolve: resolveCmd,\n execute: executeCmd,\n };\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={{ position: 'relative', display: 'flex', alignItems: 'center', ...style }}\n >\n <div style={sectionStyle('start')}>{renderLiveSection(sectionByName('start'))}</div>\n <div style={sectionStyle('center')}>{renderLiveSection(sectionByName('center'))}</div>\n <div style={sectionStyle('end')}>\n {renderLiveSection(sectionByName('end'))}\n {fit.hasOverflow && (\n <span style={{ position: 'relative', display: 'inline-flex' }}>\n {renderOverflowTrigger(overflowOpen, () => setOverflowOpen((o) => !o))}\n {renderOverflowMenu ? (\n renderOverflowMenu(overflowView)\n ) : (\n <DefaultOverflowMenu view={overflowView} />\n )}\n </span>\n )}\n </div>\n\n {/* The measurement layer: every unit in every variant, every collapsed\n group form, and the trigger — hidden, inert, observed. Text reflow\n (locale, fonts, zoom) fires the observers; no change-handling code. */}\n <div aria-hidden style={measureLayerStyle}>\n {visibleBar.sections.flatMap((s) =>\n s.groups.flatMap((g) => [\n // External sockets are excluded: they are live-measured, and a\n // duplicate <slot> here would steal the projection from the row.\n ...g.units\n .filter((u) => !isExternalSlot(u))\n .flatMap((u) =>\n u.variants.map((variant) => (\n <Measured key={unitKey(u, variant)} k={unitKey(u, variant)} onWidth={onWidth}>\n {renderUnitAt(u, variant, 'measure')}\n </Measured>\n )),\n ),\n ...(g.collapse\n ? [\n <Measured key={groupKey(g.id)} k={groupKey(g.id)} onWidth={onWidth}>\n {renderCollapsedGroup(g)}\n </Measured>,\n ]\n : []),\n ...(g.shed\n ? [\n <Measured key={groupTriggerKey(g.id)} k={groupTriggerKey(g.id)} onWidth={onWidth}>\n {renderDisclosure(g, true)}\n </Measured>,\n ]\n : []),\n ]),\n )}\n <Measured k={TRIGGER_KEY} onWidth={onWidth}>\n {renderOverflowTrigger(false, () => {})}\n </Measured>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;AAqFA,MAAM,oBAAoB,GAA8B,MACtD,EAAE,WAAW,EAAE,UACf,EAAE,OACC,GAAG,MACF,EAAE,OAAO,EAAE,EAAE,CAAC,MACd,EAAE,SAAS,WAAW,EAAE,EAAE,CAAC,SAAS,UACpC,EAAE,SAAS,OAAO,GAAG,MAAM,sBAAsB,GAAG,EAAE,EAAE,CAAC,SAAS,EAAE,CAAC,CACzE;AAEF,MAAM,kBAA6C,CAAC;;;;;;;;;;AAWpD,SAAgB,aAAa,KAA8C;CACzE,MAAM,WAAW,cAAc,aAAa;CAC5C,MAAM,aAAa,cAAc;CACjC,MAAM,aAAa,cAAe,MAAM,aAAa,GAAG,IAAI,MAAO,CAAC,GAAG,CAAC;CACxE,MAAM,SAAS,qBAAqB;EAClC,IAAI,CAAC,YAAY,OAAO;EACxB,MAAM,2BAAW,IAAI,IAA6B;EAClD,MAAM,WAAW,OAAe;GAC9B,MAAM,MAAM,SAAS,QAAQ,IAAI,cAAc,KAAA,CAAS;GACxD,IAAI,KAAK,SAAS,IAAI,IAAI,GAAG;GAC7B,OAAO,KAAK,YAAY;EAC1B;EACA,OAAO,aAAa,YAAY,OAAO,CAAC,CAAC,KAAK,OAAO;GACnD,IAAI,EAAE;GACN,UAAU,EAAE;GACZ,UAAU,EAAE,SAAS,KAAK,OAAO,SAAS,IAAI,EAAE,CAAE;EACpD,EAAE;CACJ,GAAG,gBAAgB;CACnB,OAAO,cAEH,OAAO,WAAW,IACd,OACA;EAAE;EAAQ,UAAU,OAAe,SAAS,QAAQ,IAAI,cAAc,KAAA,CAAS;CAAE,GACvF;EAAC;EAAQ;EAAU;CAAU,CAC/B;AACF;;AA+CA,SAAS,SAAS,EAChB,GACA,SACA,YAKC;CACD,MAAM,MAAM,OAAwB,IAAI;CACxC,sBAAsB;EACpB,MAAM,KAAK,IAAI;EACf,IAAI,CAAC,IAAI;EACT,MAAM,eAAe,QAAQ,GAAG,GAAG,sBAAsB,CAAC,CAAC,KAAK;EAChE,OAAO;EACP,MAAM,WAAW,IAAI,eAAe,MAAM;EAC1C,SAAS,QAAQ,EAAE;EACnB,aAAa,SAAS,WAAW;CACnC,GAAG,CAAC,GAAG,OAAO,CAAC;CACf,OACE,oBAAC,QAAD;EAAW;EAAK,OAAO;GAAE,SAAS;GAAe,YAAY;EAAE;EAC5D;CACG,CAAA;AAEV;;;;;;;;AASA,SAAS,iBAAiB,EACxB,MACA,GACA,SACA,YAMC;CACD,MAAM,MAAM,OAAwB,IAAI;CACxC,sBAAsB;EACpB,MAAM,KAAK,IAAI;EACf,IAAI,CAAC,IAAI;EACT,MAAM,eAAe,QAAQ,GAAG,GAAG,sBAAsB,CAAC,CAAC,KAAK;EAChE,OAAO;EAGP,MAAM,WAAW,IAAI,eAAe,MAAM;EAC1C,SAAS,QAAQ,EAAE;EACnB,aAAa,SAAS,WAAW;CACnC,GAAG,CAAC,GAAG,OAAO,CAAC;CACf,OACE,oBAAC,QAAD;EACO;EACC;EACN,OAAO;GAAE,SAAS;GAAe,YAAY;GAAU,YAAY;EAAE;EAEpE;CACG,CAAA;AAEV;AAEA,MAAM,oBAAyC;CAC7C,UAAU;CACV,MAAM;CACN,KAAK;CACL,QAAQ;CACR,UAAU;CACV,YAAY;CACZ,eAAe;CACf,SAAS;CACT,YAAY;AACd;AAIA,MAAM,wBACJ,KACA,SACA,QAEA,qBAAC,UAAD;CACE,MAAK;CACL,SAAS;CACT,UAAU,CAAC,IAAI;CACf,gBAAc,IAAI,UAAU,KAAA;CAC5B,iBAAe,IAAI,OAAO,SAAS,KAAA;CACnC,OAAO,IAAI;CACX,OAAO;EACL,SAAS;EACT,YAAY;EACZ,KAAK;EACL,SAAS;EACT,YAAY;EACZ,YAAY,IAAI,SAAS,qBAAqB;EAC9C,QAAQ;EACR,cAAc;EACd,QAAQ,IAAI,UAAU,YAAY;EAClC,SAAS,IAAI,UAAU,IAAI;CAC7B;CAlBF,UAAA,CAoBG,YAAY,UAAU,IAAI,QAAS,IAAI,QAAQ,IAAI,OACnD,YAAY,eAAe,IAAI,IAAI,UAAU,IACxC;;AAGV,MAAM,+BACJ,oBAAC,QAAD,EAAM,OAAO;CAAE,OAAO;CAAG,WAAW;CAAW,YAAY;CAAgB,SAAS;AAAI,EAAI,CAAA;AAG9F,MAAM,gCAAgC,QAAiB,WACrD,oBAAC,UAAD;CACE,MAAK;CACL,SAAS;CACT,iBAAc;CACd,iBAAe;CACf,OAAM;CACN,OAAO;EACL,SAAS;EACT,YAAY;EACZ,SAAS;EACT,QAAQ;EACR,cAAc;EACd,YAAY,SAAS,qBAAqB;EAC1C,QAAQ;CACV;CACD,UAAA;AAEO,CAAA;AAGV,SAAS,oBAAoB,EAAE,QAAoC;CACjE,IAAI,CAAC,KAAK,QAAQ,OAAO;CACzB,OACE,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAS,OAAO;GAAG,QAAQ;EAAG;EAAG,SAAS,KAAK;CAAQ,CAAA,GAC/E,oBAAC,OAAD;EACE,MAAK;EACL,OAAO;GACL,UAAU;GACV,OAAO;GACP,KAAK;GACL,QAAQ;GACR,UAAU;GACV,SAAS;GACT,YAAY;GACZ,QAAQ;GACR,cAAc;GACd,WAAW;EACb;EAEC,UAAA,KAAK,SAAS,KAAK,SAAS,MAC3B,qBAAC,MAAM,UAAP,EAAA,UAAA,CACG,IAAI,KAAK,oBAAC,OAAD,EAAK,OAAO;GAAE,QAAQ;GAAG,YAAY;GAAmB,QAAQ;EAAQ,EAAI,CAAA,GACrF,QAAQ,KAAK,KAAK,QAAQ;GACzB,MAAM,MAAM,KAAK,QAAQ,IAAI,OAAO;GACpC,IAAI,CAAC,KAAK,OAAO;GACjB,MAAM,YAAY,IAAI,SAAS;GAC/B,OACE,qBAAC,UAAD;IAEE,MAAK;IACL,MAAM,QAAQ,SAAS,UAAU,kBAAkB;IACnD,gBAAc,QAAQ,SAAS,UAAU,IAAI,SAAS,KAAA;IACtD,UAAU,CAAC,IAAI;IACf,eAAe;KACb,KAAK,QAAQ,IAAI,OAAO;KACxB,IAAI,CAAC,WAAW,KAAK,MAAM;IAC7B;IACA,OAAO;KACL,SAAS;KACT,OAAO;KACP,YAAY;KACZ,gBAAgB;KAChB,KAAK;KACL,SAAS;KACT,QAAQ;KACR,YAAY;KACZ,QAAQ,IAAI,UAAU,YAAY;KAClC,SAAS,IAAI,UAAU,IAAI;KAC3B,YAAY;IACd;IAtBF,UAAA,CAwBE,qBAAC,QAAD,EAAA,UAAA,CACG,IAAI,UAAU,QAAQ,SAAS,UAAU,OAAO,IAChD,IAAI,KACD,EAAA,CAAA,GACL,YAAY,oBAAC,QAAD,EAAA,UAAM,IAAO,CAAA,IAAI,IACxB;GA5BD,GAAA,IAAI,OA4BH;EAEZ,CAAC,CACa,EAAA,GAvCK,CAuCL,CACjB;CACE,CAAA,CACL,EAAA,CAAA;AAEN;AAEA,SAAS,iBAAiB,EAAE,QAAsC;CAChE,IAAI,KAAK,aAAa,UAEpB,OACE,oBAAC,UAAD;EACE,OAHW,KAAK,SAAS,MAAM,MAAM,EAAE,MAG3B,CAAC,EAAE,MAAM;EACrB,WAAW,MAAM,KAAK,QAAQ,EAAE,OAAO,KAAK;EAC5C,OAAO;GAAE,SAAS;GAAW,cAAc;EAAE;EAE5C,UAAA,KAAK,SAAS,KAAK,MAClB,oBAAC,UAAD;GAAmB,OAAO,EAAE;GAAI,UAAU,CAAC,EAAE;GAC1C,UAAA,EAAE;EACG,GAFK,EAAE,EAEP,CACT;CACK,CAAA;CAIZ,OAAO,oBAAC,qBAAD,EAA2B,KAAO,CAAA;AAC3C;AAEA,SAAS,oBAAoB,EAAE,QAAsC;CACnE,MAAM,CAAC,QAAQ,WAAW,SAAS,KAAK;CACxC,MAAM,WAA8B,CAClC;EACE,UAAU,KAAK;EACf,MAAM,KAAK,SAAS,SAAS,UAAU,KAAA;EACvC,MAAM,KAAK,SAAS,KAAK,OAAO;GAAE,MAAM;GAAoB,SAAS,EAAE;EAAG,EAAE;CAC9E,CACF;CACA,OACE,qBAAC,QAAD;EAAM,OAAO;GAAE,UAAU;GAAY,SAAS;EAAc;EAA5D,UAAA,CACG,6BAA6B,cAAc,SAAS,MAAM,CAAC,CAAC,CAAC,GAC9D,oBAAC,qBAAD,EACE,MAAM;GACJ;GACA;GACA,aAAa,QAAQ,KAAK;GAC1B,UAAU,OAAO,KAAK,SAAS,MAAM,MAAM,EAAE,OAAO,EAAE,KAAK;GAC3D,SAAS,KAAK;EAChB,EACD,CAAA,CACG;;AAEV;;;AAIA,SAAS,oBAAoB,EAAE,QAAuC;CACpE,MAAM,CAAC,QAAQ,WAAW,SAAS,KAAK;CACxC,MAAM,aAAa,KAAK,SAAS,MAAM,MAAM,EAAE,MAAM;CACrD,MAAM,WAA8B,CAClC;EACE,UAAU,KAAK;EACf,MAAM,KAAK,SAAS,SAAS,UAAU,KAAA;EACvC,MAAM,KAAK,SAAS,KAAK,OAAO;GAAE,MAAM;GAAoB,SAAS,EAAE;EAAG,EAAE;CAC9E,CACF;CACA,OACE,qBAAC,QAAD;EAAM,OAAO;GAAE,UAAU;GAAY,SAAS;EAAc;EAA5D,UAAA,CACE,oBAAC,UAAD;GACE,MAAK;GACL,eAAe,SAAS,MAAM,CAAC,CAAC;GAChC,iBAAc;GACd,iBAAe;GACf,OAAM;GACN,OAAO;IACL,SAAS;IACT,YAAY;IACZ,SAAS;IACT,QAAQ;IACR,cAAc;IAEd,YAAY,UAAU,aAAa,qBAAqB;IACxD,QAAQ;GACV;GACD,UAAA;EAEO,CAAA,GACR,oBAAC,qBAAD,EACE,MAAM;GACJ;GACA;GACA,aAAa,QAAQ,KAAK;GAC1B,UAAU,OAAO,KAAK,SAAS,MAAM,MAAM,EAAE,OAAO,EAAE,KAAK;GAC3D,SAAS,KAAK;EAChB,EACD,CAAA,CACG;;AAEV;AAIA,MAAM,WAAW,GAAmB,YAAoB,KAAK,EAAE,IAAI,GAAG;AACtE,MAAM,YAAY,OAAe,KAAK;AACtC,MAAM,mBAAmB,OAAe,MAAM;AAC9C,MAAM,cAAc;AAEpB,SAAgB,QAAQ,EACtB,KACA,MAAM,GACN,iBAAiB,GACjB,WACA,OACA,gBAAgB,sBAChB,cACA,iBACA,oBACA,kBAAkB,wBAClB,wBAAwB,8BACxB,sBACe;CACf,MAAM,SAAS,UAAU;CACzB,MAAM,WAAW,cAAc,aAAa;CAC5C,MAAM,aAAa,cAAc;CAMjC,MAAM,GAAG,SAAS,YAAY,MAAc,IAAI,GAAG,CAAC;CACpD,gBAAgB,OAAO,UAAU,KAAK,GAAG,CAAC,MAAM,CAAC;CAEjD,MAAM,eAAe,OAAuB,IAAI;CAChD,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,CAAC;CACtD,sBAAsB;EACpB,MAAM,KAAK,aAAa;EACxB,IAAI,CAAC,IAAI;EACT,MAAM,WAAW,IAAI,gBAAgB,YAAY;GAC/C,MAAM,IAAI,QAAQ,QAAQ,SAAS,EAAE,EAAE,YAAY;GACnD,IAAI,MAAM,KAAA,GAAW,mBAAmB,SAAU,KAAK,IAAI,OAAO,CAAC,IAAI,KAAM,IAAI,IAAK;EACxF,CAAC;EACD,SAAS,QAAQ,EAAE;EACnB,kBAAkB,GAAG,WAAW;EAChC,aAAa,SAAS,WAAW;CACnC,GAAG,CAAC,CAAC;CAEL,MAAM,YAAY,uBAAO,IAAI,IAAoB,CAAC;CAClD,MAAM,GAAG,sBAAsB,YAAY,MAAc,IAAI,GAAG,CAAC;CACjE,MAAM,UAAU,eACP,KAAa,UAAkB;EACpC,MAAM,OAAO,UAAU,QAAQ,IAAI,GAAG;EACtC,IAAI,SAAS,KAAA,KAAa,KAAK,IAAI,OAAO,KAAK,KAAK,IAAK;EACzD,UAAU,QAAQ,IAAI,KAAK,KAAK;EAChC,mBAAmB;CACrB,GACA,CAAC,CACH;CAEA,MAAM,cAAc,OAClB,SAAS,QAAQ,IAAI,cAAc,KAAA,CAAS;CAC9C,MAAM,cAAc,OAAe,SAAS,QAAQ,IAAI,cAAc,KAAA,CAAS;CAC/E,MAAM,aAAa,MAAuB,EAAE,SAAS,YAAY,EAAE,UAAU,EAAE;CAS/E,MAAM,aAAa,UALA,cAAc,aAAa,GAAG,GAAG,CAAC,GAAG,CAKlB,IAAI,MAAM,WAAW,UAAU,CAAC,CAAC,CAAC,EAAE,YAAY,IAAI;CAU1F,MAAM,MAAM,MAAM,YAAY;EAR5B,OAAO,KAAK,YAAY,UAAU,QAAQ,IAAI,KAAK,IAAI,GAAG,SAAS;EACnE,iBAAiB,OAAO,UAAU,QAAQ,IAAI,SAAS,EAAE,CAAC;EAC1D,eAAe,OAAO,UAAU,QAAQ,IAAI,gBAAgB,EAAE,CAAC;EAC/D,iBAAiB,UAAU,QAAQ,IAAI,WAAW,KAAK;EACvD;EAEA,WAAW,iBAAiB;CAEM,GAAG,cAAc;CACrD,MAAM,mBAAmB,gBAAgB,YAAY,MAAM,OAAO,SAAS,WAAW,EAAE,CAAC;CAEzF,MAAM,CAAC,cAAc,mBAAmB,SAAS,KAAK;CACtD,gBAAgB;EACd,IAAI,CAAC,IAAI,aAAa,gBAAgB,KAAK;CAC7C,GAAG,CAAC,IAAI,WAAW,CAAC;;;;CAKpB,MAAM,kBAAkB,MACtB,EAAE,SAAS,YAAY,CAAC,eAAe,EAAE;CAG3C,MAAM,gBACJ,GACA,SACA,UACoB;EACpB,IAAI,EAAE,SAAS,UAAU;GACvB,MAAM,aAAa,eAAe,EAAE;GACpC,IAAI,YAAY;IACd,MAAM,SAAS,WAAW,SAAS;KACjC;KACA,UAAU,UAAU,QAAQ,QAAQ,GAAG,OAAO,GAAG,KAAK;IACxD,CAAC;IACD,IAAI,WAAW,KAAA,GAAW,OAAO;GACnC,OAAO,IAAI,UAAU,QAAQ;IAC3B,MAAM,MAAM,WAAW,EAAE,QAAQ;IACjC,OACE,oBAAC,kBAAD;KAAkB,MAAM,EAAE;KAAM,GAAG,QAAQ,GAAG,OAAO;KAAY;KAC9D,UAAA,MAAM,cAAc,KAAK,cAAc,WAAW,EAAE,QAAQ,CAAC,IAAI;IAClD,CAAA;GAEtB,OACE,OAAO;GAET,MAAM,MAAM,WAAW,EAAE,QAAQ;GACjC,OAAO,MAAM,cAAc,KAAK,cAAc,WAAW,EAAE,QAAQ,CAAC,IAAI;EAC1E;EACA,MAAM,MAAM,WAAW,EAAE,OAAO;EAChC,IAAI,CAAC,KACH,OAAO,cACL;GACE,IAAI,EAAE;GACN,OAAO,EAAE;GACT,WAAW,CAAC;GACZ,SAAS;GACT,QAAQ;GACR,SAAS;GACT,YAAY,CAAC;EACf,GACA,eACM,CAAC,CACT;EACF,OAAO,cAAc,KAAK,eAAe,WAAW,EAAE,OAAO,CAAC;CAChE;CAEA,MAAM,iBAAiB,OAA4C;EACjE,IAAI,EAAE;EACN,UAAU,EAAE;EACZ,UAAU,EAAE,YAAY;EACxB,MAAM,EAAE;EACR,UAAU,EAAE,MACT,KAAK,MAAM,WAAW,UAAU,CAAC,CAAC,CAAC,CAAC,CACpC,QAAQ,MAA4B,MAAM,QAAQ,EAAE,OAAO;EAC9D,SAAS;CACX;CAEA,MAAM,wBAAwB,MAC5B,kBACE,gBAAgB,cAAc,CAAC,CAAC,IAEhC,oBAAC,kBAAD,EAAkB,MAAM,cAAc,CAAC,EAAI,CAAA;;;CAK/C,MAAM,kBAAkB,GAAoB,iBAA+C;EACzF,IAAI,EAAE;EACN,UAAU,EAAE;EACZ,MAAM,EAAE;EACR,UAAU,EAAE,MACT,QAAQ,MAAM,eAAe,IAAI,MAAM,IAAI,EAAE,GAAG,CAAC,EAAE,SAAS,MAAM,CAAC,CACnE,KAAK,MAAM,WAAW,UAAU,CAAC,CAAC,CAAC,CAAC,CACpC,QAAQ,MAA4B,MAAM,QAAQ,EAAE,OAAO;EAC9D,SAAS;CACX;CAEA,MAAM,oBAAoB,GAAoB,gBAC5C,qBACE,mBAAmB,eAAe,GAAG,WAAW,CAAC,IAEjD,oBAAC,qBAAD,EAAqB,MAAM,eAAe,GAAG,WAAW,EAAI,CAAA;CAGhE,MAAM,mBAAmB,MAA0C;EACjE,MAAM,aAAa,IAAI,OAAO,IAAI,EAAE,EAAE;EACtC,IAAI,CAAC,cAAc,WAAW,YAAY,OAAO,CAAC;EAClD,IAAI,WAAW,WACb,OAAO,CAAC,oBAAC,MAAM,UAAP,EAAA,UAA4B,qBAAqB,CAAC,EAAkB,GAA/C,EAAE,EAA6C,CAAC;EAC/E,MAAM,QAA2B,CAAC;EAClC,KAAK,MAAM,KAAK,EAAE,OAAO;GACvB,MAAM,IAAI,IAAI,MAAM,IAAI,EAAE,GAAG;GAC7B,IAAI,GAAG,SAAS,WAAW;GAC3B,MAAM,KAAK,oBAAC,MAAM,UAAP,EAAA,UAA6B,aAAa,GAAG,EAAE,SAAS,MAAM,EAAkB,GAA3D,EAAE,GAAyD,CAAC;EAC9F;EAEA,IAAI,WAAW,YAAY,GACzB,MAAM,KACJ,oBAAC,MAAM,UAAP,EAAA,UAA0C,iBAAiB,GAAG,KAAK,EAAkB,GAAhE,GAAG,EAAE,GAAG,UAAwD,CACvF;EAEF,OAAO,MAAM,SAAS,CAAC,oBAAC,MAAM,UAAP,EAAA,UAA4B,MAAsB,GAA7B,EAAE,EAA2B,CAAC,IAAI,CAAC;CACjF;CAEA,MAAM,qBAAqB,YAA4D;EACrF,IAAI,CAAC,SAAS,OAAO;EAIrB,OAHiB,QAAQ,OACtB,KAAK,OAAO;GAAE,IAAI,EAAE;GAAI,OAAO,gBAAgB,CAAC;EAAE,EAAE,CAAC,CACrD,QAAQ,MAAM,EAAE,MAAM,SAAS,CACpB,CAAC,CAAC,KAAK,GAAG,MACtB,qBAAC,MAAM,UAAP,EAAA,UAAA,CACG,IAAI,KAAK,gBAAgB,GACzB,EAAE,KACW,EAAA,GAHK,EAAE,EAGP,CACjB;CACH;CAEA,MAAM,iBAAiB,SACrB,WAAW,SAAS,MAAM,MAAM,EAAE,SAAS,IAAI;;;;;;;;;;;;CAajD,MAAM,gBAAgB,cAA+D;EACnF,SAAS;EACT,YAAY;EACZ;EACA,MAAM;EACN,GAAI,aAAa,WAAW;GAAE,YAAY;GAAQ,aAAa;EAAO,IAAI;CAC5E;CAEA,MAAM,eAAiC;EACrC,UAAU;EACV,QAAQ;EACR,aAAa,gBAAgB,KAAK;EAClC,SAAS;EACT,SAAS;CACX;CAEA,OACE,qBAAC,OAAD;EACE,KAAK;EACM;EACX,OAAO;GAAE,UAAU;GAAY,SAAS;GAAQ,YAAY;GAAU,GAAG;EAAM;EAHjF,UAAA;GAKE,oBAAC,OAAD;IAAK,OAAO,aAAa,OAAO;IAAI,UAAA,kBAAkB,cAAc,OAAO,CAAC;GAAO,CAAA;GACnF,oBAAC,OAAD;IAAK,OAAO,aAAa,QAAQ;IAAI,UAAA,kBAAkB,cAAc,QAAQ,CAAC;GAAO,CAAA;GACrF,qBAAC,OAAD;IAAK,OAAO,aAAa,KAAK;IAA9B,UAAA,CACG,kBAAkB,cAAc,KAAK,CAAC,GACtC,IAAI,eACH,qBAAC,QAAD;KAAM,OAAO;MAAE,UAAU;MAAY,SAAS;KAAc;KAA5D,UAAA,CACG,sBAAsB,oBAAoB,iBAAiB,MAAM,CAAC,CAAC,CAAC,GACpE,qBACC,mBAAmB,YAAY,IAE/B,oBAAC,qBAAD,EAAqB,MAAM,aAAe,CAAA,CAExC;IAEL,CAAA,CAAA;;GAKL,qBAAC,OAAD;IAAK,eAAA;IAAY,OAAO;IAAxB,UAAA,CACG,WAAW,SAAS,SAAS,MAC5B,EAAE,OAAO,SAAS,MAAM;KAGtB,GAAG,EAAE,MACF,QAAQ,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,CACjC,SAAS,MACR,EAAE,SAAS,KAAK,YACd,oBAAC,UAAD;MAAoC,GAAG,QAAQ,GAAG,OAAO;MAAY;MAClE,UAAA,aAAa,GAAG,SAAS,SAAS;KAC3B,GAFK,QAAQ,GAAG,OAAO,CAEvB,CACX,CACH;KACF,GAAI,EAAE,WACF,CACE,oBAAC,UAAD;MAA+B,GAAG,SAAS,EAAE,EAAE;MAAY;MACxD,UAAA,qBAAqB,CAAC;KACf,GAFK,SAAS,EAAE,EAAE,CAElB,CACZ,IACA,CAAC;KACL,GAAI,EAAE,OACF,CACE,oBAAC,UAAD;MAAsC,GAAG,gBAAgB,EAAE,EAAE;MAAY;MACtE,UAAA,iBAAiB,GAAG,IAAI;KACjB,GAFK,gBAAgB,EAAE,EAAE,CAEzB,CACZ,IACA,CAAC;IACP,CAAC,CACH,GACA,oBAAC,UAAD;KAAU,GAAG;KAAsB;KAChC,UAAA,sBAAsB,aAAa,CAAC,CAAC;IAC9B,CAAA,CACP;;EACF;;AAET"}
package/dist/views.cjs ADDED
@@ -0,0 +1,35 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ require("./rolldown-runtime-DDXJien1.cjs");
3
+ const require_runtime = require("./runtime.cjs");
4
+ let _embedpdf_plugin_view_manager = require("@embedpdf/plugin-view-manager");
5
+ //#region src/views.ts
6
+ const sameView = (a, b) => a.id === b.id && a.activeDocumentId === b.activeDocumentId && a.documentIds.length === b.documentIds.length && a.documentIds.every((d, i) => d === b.documentIds[i]);
7
+ function useViews() {
8
+ const vm = require_runtime.useKernel().capability(_embedpdf_plugin_view_manager.ViewManagerToken);
9
+ return {
10
+ views: require_runtime.useKernelValue(() => vm.list(), (a, b) => a.length === b.length && a.every((v, i) => sameView(v, b[i]))),
11
+ focusedViewId: require_runtime.useKernelValue(() => vm.focusedViewId()),
12
+ documentView: vm.documentView,
13
+ createView: vm.createView,
14
+ removeView: vm.removeView,
15
+ moveView: vm.moveView,
16
+ setFocused: vm.setFocused,
17
+ setActiveDocument: vm.setActiveDocument,
18
+ addDocument: vm.addDocument,
19
+ removeDocument: vm.removeDocument,
20
+ moveDocumentWithin: vm.moveDocumentWithin,
21
+ moveDocumentBetween: vm.moveDocumentBetween
22
+ };
23
+ }
24
+ //#endregion
25
+ exports.useViews = useViews;
26
+ Object.keys(_embedpdf_plugin_view_manager).forEach(function(k) {
27
+ if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
28
+ enumerable: true,
29
+ get: function() {
30
+ return _embedpdf_plugin_view_manager[k];
31
+ }
32
+ });
33
+ });
34
+
35
+ //# sourceMappingURL=views.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"views.cjs","names":["useKernel","ViewManagerToken","useKernelValue"],"sources":["../src/views.ts"],"sourcesContent":["/**\n * useViews — the React view of @embedpdf/plugin-view-manager.\n *\n * Reactive pane list + intents. The adapter stays headless: it gives you the\n * views; YOU lay them out (split, grid, stacked), render each pane's tab strip\n * over `view.documentIds`, and wrap the body in a <DocumentScope id={activeId}>\n * so its Stage binds to that pane's active document.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-view-manager';\nimport { ViewManagerToken } from '@embedpdf/plugin-view-manager';\nimport type { ViewInfo } from '@embedpdf/plugin-view-manager';\nimport { useKernel, useKernelValue } from './runtime';\n\nexport interface UseViews {\n views: ViewInfo[];\n focusedViewId: string | null;\n documentView: (documentId: string) => string | null;\n createView: () => string;\n removeView: (id: string) => void;\n moveView: (id: string, toIndex: number) => void;\n setFocused: (id: string) => void;\n setActiveDocument: (viewId: string, documentId: string | null) => void;\n addDocument: (viewId: string, documentId: string, index?: number) => void;\n removeDocument: (viewId: string, documentId: string) => void;\n moveDocumentWithin: (viewId: string, documentId: string, toIndex: number) => void;\n moveDocumentBetween: (\n fromViewId: string,\n toViewId: string,\n documentId: string,\n toIndex?: number,\n ) => void;\n}\n\nconst sameView = (a: ViewInfo, b: ViewInfo): boolean =>\n a.id === b.id &&\n a.activeDocumentId === b.activeDocumentId &&\n a.documentIds.length === b.documentIds.length &&\n a.documentIds.every((d, i) => d === b.documentIds[i]);\n\nexport function useViews(): UseViews {\n const kernel = useKernel();\n const vm = kernel.capability(ViewManagerToken);\n const views = useKernelValue(\n () => vm.list(),\n (a, b) => a.length === b.length && a.every((v, i) => sameView(v, b[i])),\n );\n const focusedViewId = useKernelValue(() => vm.focusedViewId());\n return {\n views,\n focusedViewId,\n documentView: vm.documentView,\n createView: vm.createView,\n removeView: vm.removeView,\n moveView: vm.moveView,\n setFocused: vm.setFocused,\n setActiveDocument: vm.setActiveDocument,\n addDocument: vm.addDocument,\n removeDocument: vm.removeDocument,\n moveDocumentWithin: vm.moveDocumentWithin,\n moveDocumentBetween: vm.moveDocumentBetween,\n };\n}\n"],"mappings":";;;;;AAmCA,MAAM,YAAY,GAAa,MAC7B,EAAE,OAAO,EAAE,MACX,EAAE,qBAAqB,EAAE,oBACzB,EAAE,YAAY,WAAW,EAAE,YAAY,UACvC,EAAE,YAAY,OAAO,GAAG,MAAM,MAAM,EAAE,YAAY,EAAE;AAEtD,SAAgB,WAAqB;CAEnC,MAAM,KADSA,gBAAAA,UACC,CAAC,CAAC,WAAWC,8BAAAA,gBAAgB;CAM7C,OAAO;EACL,OANYC,gBAAAA,qBACN,GAAG,KAAK,IACb,GAAG,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,SAAS,GAAG,EAAE,EAAE,CAAC,CAIlE;EACJ,eAHoBA,gBAAAA,qBAAqB,GAAG,cAAc,CAG9C;EACZ,cAAc,GAAG;EACjB,YAAY,GAAG;EACf,YAAY,GAAG;EACf,UAAU,GAAG;EACb,YAAY,GAAG;EACf,mBAAmB,GAAG;EACtB,aAAa,GAAG;EAChB,gBAAgB,GAAG;EACnB,oBAAoB,GAAG;EACvB,qBAAqB,GAAG;CAC1B;AACF"}
@@ -0,0 +1,21 @@
1
+ import { ViewInfo } from "@embedpdf/plugin-view-manager";
2
+ export * from "@embedpdf/plugin-view-manager";
3
+ //#region src/views.d.ts
4
+ interface UseViews {
5
+ views: ViewInfo[];
6
+ focusedViewId: string | null;
7
+ documentView: (documentId: string) => string | null;
8
+ createView: () => string;
9
+ removeView: (id: string) => void;
10
+ moveView: (id: string, toIndex: number) => void;
11
+ setFocused: (id: string) => void;
12
+ setActiveDocument: (viewId: string, documentId: string | null) => void;
13
+ addDocument: (viewId: string, documentId: string, index?: number) => void;
14
+ removeDocument: (viewId: string, documentId: string) => void;
15
+ moveDocumentWithin: (viewId: string, documentId: string, toIndex: number) => void;
16
+ moveDocumentBetween: (fromViewId: string, toViewId: string, documentId: string, toIndex?: number) => void;
17
+ }
18
+ declare function useViews(): UseViews;
19
+ //#endregion
20
+ export { UseViews, useViews };
21
+ //# sourceMappingURL=views.d.cts.map