@echothink-ui/documents 0.1.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,715 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.tsx
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ AgentLockedDocumentPanel: () => AgentLockedDocumentPanel,
34
+ DocumentEditorShell: () => DocumentEditorShell,
35
+ DocumentLockBadge: () => DocumentLockBadge,
36
+ DocumentOutline: () => DocumentOutline,
37
+ DocumentToolbar: () => DocumentToolbar,
38
+ DocumentViewer: () => DocumentViewer,
39
+ DocumentWorkspaceTemplate: () => DocumentWorkspaceTemplate,
40
+ DocumentsComponentNames: () => DocumentsComponentNames
41
+ });
42
+ module.exports = __toCommonJS(index_exports);
43
+
44
+ // src/components/DocumentEditorShell.tsx
45
+ var React = __toESM(require("react"), 1);
46
+ var import_core2 = require("@echothink-ui/core");
47
+ var import_icons3 = require("@echothink-ui/icons");
48
+
49
+ // src/components/DocumentLockBadge.tsx
50
+ var import_core = require("@echothink-ui/core");
51
+ var import_icons2 = require("@echothink-ui/icons");
52
+
53
+ // src/components/utils.tsx
54
+ var import_icons = require("@echothink-ui/icons");
55
+ var import_jsx_runtime = require("react/jsx-runtime");
56
+ function cx(...classes) {
57
+ return classes.filter(Boolean).join(" ");
58
+ }
59
+ function documentRefLabel(documentRef) {
60
+ if (typeof documentRef === "string") return documentRef;
61
+ if (documentRef.label) return documentRef.label;
62
+ return documentRef.id;
63
+ }
64
+ function formatDateTime(value) {
65
+ if (!value) return "Unknown";
66
+ const date = new Date(value);
67
+ if (Number.isNaN(date.getTime())) return value;
68
+ return date.toLocaleString(void 0, {
69
+ dateStyle: "medium",
70
+ timeStyle: "short"
71
+ });
72
+ }
73
+ function modeLabel(mode) {
74
+ return mode.charAt(0).toUpperCase() + mode.slice(1);
75
+ }
76
+ function actionIcon(action) {
77
+ const key = `${action.id} ${action.label}`.toLowerCase();
78
+ if (key.includes("bold"))
79
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { className: "eth-doc-toolbar__format-symbol", "aria-hidden": true, children: "B" });
80
+ if (key.includes("italic"))
81
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("em", { className: "eth-doc-toolbar__format-symbol", "aria-hidden": true, children: "I" });
82
+ if (key.includes("underline"))
83
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
84
+ "span",
85
+ {
86
+ className: "eth-doc-toolbar__format-symbol eth-doc-toolbar__format-symbol--underline",
87
+ "aria-hidden": true,
88
+ children: "U"
89
+ }
90
+ );
91
+ if (key.includes("strike"))
92
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
93
+ "span",
94
+ {
95
+ className: "eth-doc-toolbar__format-symbol eth-doc-toolbar__format-symbol--strike",
96
+ "aria-hidden": true,
97
+ children: "S"
98
+ }
99
+ );
100
+ if (key.includes("code"))
101
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "eth-doc-toolbar__format-symbol", "aria-hidden": true, children: "<>" });
102
+ if (key.includes("download") || key.includes("export")) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.DownloadIcon, { size: 16 });
103
+ if (key.includes("share") || key.includes("open")) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.ExternalLinkIcon, { size: 16 });
104
+ if (key.includes("link") || action.href) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.ExternalLinkIcon, { size: 16 });
105
+ if (key.includes("search") || key.includes("find")) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.SearchIcon, { size: 16 });
106
+ if (key.includes("agent")) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.AgentRunningIcon, { size: 16 });
107
+ if (key.includes("approve") || key.includes("review")) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.ApprovalRequiredIcon, { size: 16 });
108
+ if (key.includes("status")) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.StatusIcon, { size: 16 });
109
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.DocumentIcon, { size: 16 });
110
+ }
111
+
112
+ // src/components/DocumentLockBadge.tsx
113
+ var import_jsx_runtime2 = require("react/jsx-runtime");
114
+ var lockConfig = {
115
+ unlocked: {
116
+ label: "Unlocked",
117
+ severity: "success",
118
+ tooltip: "This document is available for editing."
119
+ },
120
+ "locked-by-user": {
121
+ label: "Locked by you",
122
+ severity: "info",
123
+ tooltip: "You currently hold the document lock."
124
+ },
125
+ "locked-by-agent": {
126
+ label: "Agent lock",
127
+ severity: "warning",
128
+ tooltip: "An agent currently holds the document lock."
129
+ },
130
+ "locked-by-other": {
131
+ label: "Locked",
132
+ severity: "danger",
133
+ tooltip: "Another user currently holds the document lock."
134
+ }
135
+ };
136
+ function DocumentLockBadge({
137
+ lockState,
138
+ owner,
139
+ since,
140
+ density = "compact",
141
+ className,
142
+ ...props
143
+ }) {
144
+ const config = lockConfig[lockState];
145
+ const Icon = lockState === "unlocked" ? import_icons2.UnlockIcon : import_icons2.LockIcon;
146
+ const visibleDetail = getVisibleDetail(lockState, owner, since);
147
+ const ownerText = owner ? ` Owner: ${owner.label}.` : "";
148
+ const sinceText = since ? ` Since: ${formatDateTime(since)}.` : "";
149
+ const tooltip = `${config.tooltip}${ownerText}${sinceText}`;
150
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_core.Tooltip, { label: tooltip, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
151
+ "span",
152
+ {
153
+ ...props,
154
+ className: cx("eth-doc-lock-badge", `eth-doc-lock-badge--${density}`, className),
155
+ "aria-label": tooltip,
156
+ "data-lock-state": lockState,
157
+ "data-eth-component": "DocumentLockBadge",
158
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
159
+ import_core.Badge,
160
+ {
161
+ severity: config.severity,
162
+ className: cx("eth-doc-lock-badge__tag", `eth-doc-lock-badge__tag--${lockState}`),
163
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "eth-doc-lock-badge__content", children: [
164
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Icon, { className: "eth-doc-lock-badge__icon", size: 12 }),
165
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "eth-doc-lock-badge__label", children: config.label }),
166
+ visibleDetail ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "eth-doc-lock-badge__detail", children: visibleDetail }) : null
167
+ ] })
168
+ }
169
+ )
170
+ }
171
+ ) });
172
+ }
173
+ function getVisibleDetail(lockState, owner, since) {
174
+ const sinceLabel = since ? formatDateTime(since) : void 0;
175
+ if (lockState === "unlocked") return sinceLabel;
176
+ if (lockState === "locked-by-user") return sinceLabel;
177
+ return [owner?.label, sinceLabel].filter(Boolean).join(" / ") || void 0;
178
+ }
179
+
180
+ // src/components/DocumentEditorShell.tsx
181
+ var import_jsx_runtime3 = require("react/jsx-runtime");
182
+ function DocumentEditorShell({
183
+ title,
184
+ documentRef,
185
+ mode = "edit",
186
+ lockState = "unlocked",
187
+ lockOwner,
188
+ toolbar,
189
+ outline,
190
+ inspector,
191
+ children,
192
+ density = "default",
193
+ className,
194
+ ...props
195
+ }) {
196
+ const [outlineOpen, setOutlineOpen] = React.useState(Boolean(outline));
197
+ const hasOutline = Boolean(outline);
198
+ const hasInspector = Boolean(inspector);
199
+ const gridTemplateColumns = [
200
+ hasOutline && outlineOpen ? "minmax(180px, 260px)" : null,
201
+ "minmax(0, 1fr)",
202
+ hasInspector ? "minmax(220px, 320px)" : null
203
+ ].filter(Boolean).join(" ");
204
+ const gridStyle = {
205
+ "--eth-doc-editor-shell-grid-template": gridTemplateColumns
206
+ };
207
+ const ethComponent = props["data-eth-component"] ?? "DocumentEditorShell";
208
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
209
+ "section",
210
+ {
211
+ ...props,
212
+ className: cx(
213
+ "eth-doc-editor-shell",
214
+ `eth-doc-editor-shell--${density}`,
215
+ hasOutline && "eth-doc-editor-shell--has-outline",
216
+ hasOutline && outlineOpen && "eth-doc-editor-shell--outline-open",
217
+ hasInspector && "eth-doc-editor-shell--has-inspector",
218
+ className
219
+ ),
220
+ "data-eth-component": ethComponent,
221
+ children: [
222
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "eth-doc-editor-shell__bar", children: [
223
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "eth-doc-editor-shell__title", children: [
224
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "eth-eyebrow", children: documentRefLabel(documentRef) }),
225
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: title })
226
+ ] }),
227
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "eth-doc-editor-shell__status", children: [
228
+ hasOutline ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
229
+ import_core2.IconButton,
230
+ {
231
+ label: outlineOpen ? "Collapse outline" : "Expand outline",
232
+ intent: "ghost",
233
+ density,
234
+ icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
235
+ import_icons3.ChevronRightIcon,
236
+ {
237
+ size: 16,
238
+ style: { transform: outlineOpen ? "rotate(180deg)" : void 0 }
239
+ }
240
+ ),
241
+ "aria-expanded": outlineOpen,
242
+ onClick: () => setOutlineOpen((current) => !current)
243
+ }
244
+ ) : null,
245
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_core2.Badge, { severity: "neutral", children: modeLabel(mode) }),
246
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DocumentLockBadge, { lockState, owner: lockOwner })
247
+ ] })
248
+ ] }),
249
+ toolbar ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "eth-doc-editor-shell__toolbar", children: toolbar }) : null,
250
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "eth-doc-editor-shell__grid", style: gridStyle, children: [
251
+ hasOutline && outlineOpen ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("aside", { className: "eth-doc-editor-shell__outline", "aria-label": "Document outline", children: outline }) : null,
252
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("main", { className: "eth-doc-editor-shell__editor", "aria-label": "Document editor", children }),
253
+ inspector ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("aside", { className: "eth-doc-editor-shell__inspector", "aria-label": "Document inspector", children: inspector }) : null
254
+ ] })
255
+ ]
256
+ }
257
+ );
258
+ }
259
+
260
+ // src/components/DocumentViewer.tsx
261
+ var React2 = __toESM(require("react"), 1);
262
+ var import_core3 = require("@echothink-ui/core");
263
+ var import_jsx_runtime4 = require("react/jsx-runtime");
264
+ function DocumentViewer({
265
+ title,
266
+ content,
267
+ metadata = [],
268
+ versionRef,
269
+ annotations = [],
270
+ className,
271
+ ...props
272
+ }) {
273
+ const titleId = React2.useId();
274
+ const meta = versionRef ? [...metadata, { label: "Version", value: versionRef }] : metadata;
275
+ const labelledBy = props["aria-labelledby"] ?? titleId;
276
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
277
+ "article",
278
+ {
279
+ ...props,
280
+ "aria-labelledby": labelledBy,
281
+ className: cx(
282
+ "eth-doc-viewer",
283
+ annotations.length > 0 && "eth-doc-viewer--with-annotations",
284
+ className
285
+ ),
286
+ "data-eth-component": "DocumentViewer",
287
+ children: [
288
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("header", { className: "eth-doc-viewer__header", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { id: titleId, children: title }) }),
289
+ meta.length ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("dl", { className: "eth-meta-grid eth-doc-viewer__metadata", children: meta.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
290
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("dt", { children: item.label }),
291
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("dd", { children: item.value })
292
+ ] }, `${String(item.label)}-${index}`)) }) : null,
293
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "eth-doc-viewer__body", children: [
294
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "eth-doc-viewer__content", children: content }),
295
+ annotations.length ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("aside", { className: "eth-doc-viewer__annotations", "aria-label": "Annotations", children: [
296
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { children: "Annotations" }),
297
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { children: annotations.map((annotation) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("li", { className: "eth-doc-viewer__annotation", children: [
298
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "eth-doc-viewer__annotation-header", children: [
299
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: annotation.author }),
300
+ annotation.range ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_core3.Badge, { severity: "neutral", children: annotation.range }) : null,
301
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("time", { dateTime: annotation.createdAt, children: formatDateTime(annotation.createdAt) })
302
+ ] }),
303
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: annotation.comment })
304
+ ] }, annotation.id)) })
305
+ ] }) : null
306
+ ] })
307
+ ]
308
+ }
309
+ );
310
+ }
311
+
312
+ // src/components/DocumentToolbar.tsx
313
+ var import_core4 = require("@echothink-ui/core");
314
+ var import_jsx_runtime5 = require("react/jsx-runtime");
315
+ var modes = ["edit", "review", "view"];
316
+ function DocumentToolbar({
317
+ actions,
318
+ formattingActions = [],
319
+ mode,
320
+ onModeChange,
321
+ density = "default",
322
+ className,
323
+ ...props
324
+ }) {
325
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
326
+ "div",
327
+ {
328
+ ...props,
329
+ className: cx("eth-doc-toolbar", className),
330
+ role: "toolbar",
331
+ "aria-label": "Document toolbar",
332
+ "data-eth-component": "DocumentToolbar",
333
+ children: [
334
+ formattingActions.length ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
335
+ "div",
336
+ {
337
+ className: "eth-doc-toolbar__group eth-doc-toolbar__group--formatting",
338
+ role: "group",
339
+ "aria-label": "Formatting",
340
+ children: formattingActions.map((action) => renderToolbarAction(action, density, "icon"))
341
+ }
342
+ ) : null,
343
+ mode && onModeChange ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
344
+ "div",
345
+ {
346
+ className: "eth-doc-toolbar__group eth-doc-toolbar__group--mode",
347
+ role: "group",
348
+ "aria-label": "Document mode",
349
+ children: modes.map((candidate) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
350
+ import_core4.Button,
351
+ {
352
+ className: "eth-doc-toolbar__mode-button",
353
+ density,
354
+ intent: candidate === mode ? "primary" : "secondary",
355
+ "aria-pressed": candidate === mode,
356
+ onClick: () => onModeChange(candidate),
357
+ children: modeLabel(candidate)
358
+ },
359
+ candidate
360
+ ))
361
+ }
362
+ ) : null,
363
+ actions.length ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
364
+ "div",
365
+ {
366
+ className: "eth-doc-toolbar__group eth-doc-toolbar__group--actions",
367
+ role: "group",
368
+ "aria-label": "Document actions",
369
+ children: actions.map((action) => renderToolbarAction(action, density, "button"))
370
+ }
371
+ ) : null
372
+ ]
373
+ }
374
+ );
375
+ }
376
+ function renderToolbarAction(action, density, variant) {
377
+ const icon = actionIcon(action);
378
+ if (variant === "button") {
379
+ if (action.href) {
380
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
381
+ import_core4.LinkButton,
382
+ {
383
+ href: action.href,
384
+ density,
385
+ intent: action.intent ?? "secondary",
386
+ "aria-disabled": action.disabled,
387
+ tabIndex: action.disabled ? -1 : void 0,
388
+ className: cx(
389
+ "eth-doc-toolbar__action",
390
+ action.disabled && "eth-doc-toolbar__link--disabled"
391
+ ),
392
+ onClick: (event) => {
393
+ if (action.disabled) {
394
+ event.preventDefault();
395
+ return;
396
+ }
397
+ action.onSelect?.();
398
+ },
399
+ children: [
400
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "eth-doc-toolbar__action-icon", "aria-hidden": "true", children: icon }),
401
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: action.label })
402
+ ]
403
+ },
404
+ action.id
405
+ );
406
+ }
407
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
408
+ import_core4.Button,
409
+ {
410
+ className: "eth-doc-toolbar__action",
411
+ density,
412
+ intent: action.intent ?? "secondary",
413
+ icon,
414
+ disabled: action.disabled,
415
+ onClick: action.onSelect,
416
+ children: action.label
417
+ },
418
+ action.id
419
+ );
420
+ }
421
+ if (action.href) {
422
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
423
+ "a",
424
+ {
425
+ href: action.href,
426
+ "aria-label": action.label,
427
+ tabIndex: action.disabled ? -1 : void 0,
428
+ className: cx(
429
+ "eth-button",
430
+ "eth-icon-button",
431
+ "eth-doc-toolbar__icon-link",
432
+ `eth-button--${action.intent ?? "ghost"}`,
433
+ `eth-button--${density}`,
434
+ action.disabled && "eth-doc-toolbar__link--disabled"
435
+ ),
436
+ "aria-disabled": action.disabled,
437
+ onClick: (event) => {
438
+ if (action.disabled) {
439
+ event.preventDefault();
440
+ return;
441
+ }
442
+ action.onSelect?.();
443
+ },
444
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "eth-button__icon", children: icon })
445
+ },
446
+ action.id
447
+ );
448
+ }
449
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
450
+ import_core4.IconButton,
451
+ {
452
+ density,
453
+ intent: action.intent ?? "ghost",
454
+ label: action.label,
455
+ icon,
456
+ disabled: action.disabled,
457
+ onClick: action.onSelect
458
+ },
459
+ action.id
460
+ );
461
+ }
462
+
463
+ // src/components/DocumentOutline.tsx
464
+ var React3 = __toESM(require("react"), 1);
465
+ var import_jsx_runtime6 = require("react/jsx-runtime");
466
+ function DocumentOutline({
467
+ headings,
468
+ activeHeadingId,
469
+ onSelect,
470
+ className,
471
+ "aria-label": ariaLabel,
472
+ ...props
473
+ }) {
474
+ const tree = React3.useMemo(() => buildOutlineTree(headings), [headings]);
475
+ const headingCountLabel = `${headings.length} section${headings.length === 1 ? "" : "s"}`;
476
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
477
+ "nav",
478
+ {
479
+ ...props,
480
+ className: cx("eth-doc-outline", className),
481
+ "aria-label": ariaLabel ?? "Document outline",
482
+ "data-eth-component": "DocumentOutline",
483
+ children: [
484
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "eth-doc-outline__header", children: [
485
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "eth-doc-outline__label", children: "On this page" }),
486
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "eth-doc-outline__count", children: headingCountLabel })
487
+ ] }),
488
+ tree.length ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ol", { className: "eth-doc-outline__list", children: renderNodes(tree, activeHeadingId, onSelect) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "eth-doc-outline__empty", role: "status", children: "No headings available." })
489
+ ]
490
+ }
491
+ );
492
+ }
493
+ function renderNodes(nodes, activeHeadingId, onSelect) {
494
+ return nodes.map((node) => {
495
+ const href = node.href ?? `#${node.id}`;
496
+ const isActive = node.id === activeHeadingId;
497
+ const containsActiveHeading = activeHeadingId ? node.children.some((child) => containsHeading(child, activeHeadingId)) : false;
498
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
499
+ "li",
500
+ {
501
+ className: cx(
502
+ "eth-doc-outline__item",
503
+ `eth-doc-outline__item--level-${node.level}`,
504
+ isActive && "eth-doc-outline__item--active",
505
+ containsActiveHeading && "eth-doc-outline__item--contains-active"
506
+ ),
507
+ children: [
508
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
509
+ "a",
510
+ {
511
+ className: "eth-doc-outline__link",
512
+ href,
513
+ "aria-current": isActive ? "location" : void 0,
514
+ onClick: (event) => {
515
+ if (!href.startsWith("#") && !onSelect) return;
516
+ event.preventDefault();
517
+ onSelect?.(node.id);
518
+ scrollHeadingIntoView(node.href, node.id);
519
+ },
520
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "eth-doc-outline__text", children: node.text })
521
+ }
522
+ ),
523
+ node.children.length ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ol", { className: "eth-doc-outline__list", children: renderNodes(node.children, activeHeadingId, onSelect) }) : null
524
+ ]
525
+ },
526
+ node.id
527
+ );
528
+ });
529
+ }
530
+ function containsHeading(node, id) {
531
+ return node.id === id || node.children.some((child) => containsHeading(child, id));
532
+ }
533
+ function buildOutlineTree(headings) {
534
+ const roots = [];
535
+ const stack = [{ level: 0, children: roots }];
536
+ for (const heading of headings) {
537
+ const node = { ...heading, children: [] };
538
+ while (stack.length > 1 && stack[stack.length - 1].level >= heading.level) stack.pop();
539
+ stack[stack.length - 1].children.push(node);
540
+ stack.push(node);
541
+ }
542
+ return roots;
543
+ }
544
+ function scrollHeadingIntoView(href, id) {
545
+ const targetId = href?.startsWith("#") ? href.slice(1) : id;
546
+ const target = globalThis.document?.getElementById(targetId);
547
+ target?.scrollIntoView({ block: "start", behavior: "smooth" });
548
+ }
549
+
550
+ // src/components/AgentLockedDocumentPanel.tsx
551
+ var React4 = __toESM(require("react"), 1);
552
+ var import_core5 = require("@echothink-ui/core");
553
+ var import_jsx_runtime7 = require("react/jsx-runtime");
554
+ function AgentLockedDocumentPanel({
555
+ documentRef,
556
+ lockOwner,
557
+ pendingChanges = [],
558
+ onTakeover,
559
+ onRelease,
560
+ onReviewChanges,
561
+ density = "default",
562
+ className,
563
+ ...props
564
+ }) {
565
+ const headingId = React4.useId();
566
+ const ownerLabel = lockOwner?.label ?? "An agent";
567
+ const documentLabel = documentRefLabel(documentRef);
568
+ const documentVersion = typeof documentRef === "string" ? void 0 : documentRef.version;
569
+ const pendingCountLabel = `${pendingChanges.length} ${pendingChanges.length === 1 ? "change" : "changes"}`;
570
+ const hasActions = Boolean(onReviewChanges || onRelease || onTakeover);
571
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
572
+ import_core5.Surface,
573
+ {
574
+ ...props,
575
+ title: "Agent document lock",
576
+ subtitle: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
577
+ ownerLabel,
578
+ " holds the editing lock for ",
579
+ documentLabel,
580
+ "."
581
+ ] }),
582
+ severity: "warning",
583
+ density,
584
+ metadata: [
585
+ {
586
+ label: "Lock state",
587
+ value: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(DocumentLockBadge, { lockState: "locked-by-agent", owner: lockOwner })
588
+ },
589
+ { label: "Document", value: documentLabel },
590
+ ...documentVersion ? [{ label: "Version", value: documentVersion }] : [],
591
+ { label: "Pending changes", value: pendingCountLabel }
592
+ ],
593
+ className: cx("eth-doc-agent-lock-panel", className),
594
+ "data-eth-component": "AgentLockedDocumentPanel",
595
+ children: [
596
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "eth-doc-agent-lock-panel__notice", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_core5.InlineNotification, { severity: "warning", title: "Document locked by agent", children: [
597
+ ownerLabel,
598
+ " is preparing changes for ",
599
+ documentLabel,
600
+ ". Review staged updates before releasing the lock or taking over."
601
+ ] }) }),
602
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("section", { className: "eth-doc-agent-lock-panel__changes-section", "aria-labelledby": headingId, children: [
603
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "eth-doc-agent-lock-panel__changes-header", children: [
604
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { id: headingId, children: "Pending changes" }),
605
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "eth-doc-agent-lock-panel__count", children: pendingCountLabel })
606
+ ] }),
607
+ pendingChanges.length ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ol", { className: "eth-doc-agent-lock-panel__changes", children: pendingChanges.map((change) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("li", { className: "eth-doc-agent-lock-panel__change", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "eth-doc-agent-lock-panel__change-body", children: [
608
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("strong", { className: "eth-doc-agent-lock-panel__summary", children: change.summary }),
609
+ change.diffSnippet ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("pre", { className: "eth-doc-agent-lock-panel__diff", children: change.diffSnippet }) : null
610
+ ] }) }, change.id)) }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "eth-doc-agent-lock-panel__empty", role: "status", children: "The agent holds the lock but has not staged changes." })
611
+ ] }),
612
+ hasActions ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "eth-actions eth-doc-agent-lock-panel__actions", children: [
613
+ onReviewChanges ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_core5.Button, { intent: "primary", density, onClick: onReviewChanges, children: "Review changes" }) : null,
614
+ onRelease ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_core5.Button, { intent: "secondary", density, onClick: onRelease, children: "Release lock" }) : null,
615
+ onTakeover ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_core5.Button, { intent: "danger", density, onClick: onTakeover, children: "Take over" }) : null
616
+ ] }) : null
617
+ ]
618
+ }
619
+ );
620
+ }
621
+
622
+ // src/components/DocumentWorkspaceTemplate.tsx
623
+ var import_core6 = require("@echothink-ui/core");
624
+ var import_jsx_runtime8 = require("react/jsx-runtime");
625
+ function DocumentWorkspaceTemplate({
626
+ title,
627
+ documentRef,
628
+ mode = "edit",
629
+ lockState = "unlocked",
630
+ lockOwner,
631
+ actions = [],
632
+ formattingActions,
633
+ onModeChange,
634
+ headings = [],
635
+ activeHeadingId,
636
+ onSelectHeading,
637
+ inspector,
638
+ toolbar,
639
+ metadata,
640
+ versionRef,
641
+ annotations,
642
+ content,
643
+ children,
644
+ density,
645
+ ...props
646
+ }) {
647
+ const outline = headings.length ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
648
+ DocumentOutline,
649
+ {
650
+ headings,
651
+ activeHeadingId,
652
+ onSelect: onSelectHeading
653
+ }
654
+ ) : null;
655
+ const resolvedToolbar = toolbar ?? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
656
+ DocumentToolbar,
657
+ {
658
+ actions,
659
+ formattingActions,
660
+ mode,
661
+ onModeChange,
662
+ density
663
+ }
664
+ );
665
+ const body = mode === "view" && content !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
666
+ DocumentViewer,
667
+ {
668
+ title,
669
+ content,
670
+ metadata,
671
+ versionRef,
672
+ annotations
673
+ }
674
+ ) : children ?? content ?? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_core6.EmptyState, { title: "No document content" });
675
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
676
+ DocumentEditorShell,
677
+ {
678
+ ...props,
679
+ title,
680
+ documentRef,
681
+ mode,
682
+ lockState,
683
+ lockOwner,
684
+ toolbar: resolvedToolbar,
685
+ outline,
686
+ inspector,
687
+ density,
688
+ "data-eth-component": "DocumentWorkspaceTemplate",
689
+ children: body
690
+ }
691
+ );
692
+ }
693
+
694
+ // src/index.tsx
695
+ var DocumentsComponentNames = [
696
+ "DocumentEditorShell",
697
+ "DocumentViewer",
698
+ "DocumentToolbar",
699
+ "DocumentOutline",
700
+ "DocumentLockBadge",
701
+ "AgentLockedDocumentPanel",
702
+ "DocumentWorkspaceTemplate"
703
+ ];
704
+ // Annotate the CommonJS export names for ESM import in node:
705
+ 0 && (module.exports = {
706
+ AgentLockedDocumentPanel,
707
+ DocumentEditorShell,
708
+ DocumentLockBadge,
709
+ DocumentOutline,
710
+ DocumentToolbar,
711
+ DocumentViewer,
712
+ DocumentWorkspaceTemplate,
713
+ DocumentsComponentNames
714
+ });
715
+ //# sourceMappingURL=index.cjs.map