@opengeni/react 0.48.0 → 0.57.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/README.md +138 -2
- package/dist/artifacts-document.d.ts +3 -0
- package/dist/artifacts-document.js +17 -0
- package/dist/artifacts-document.js.map +1 -0
- package/dist/artifacts-presentation.d.ts +3 -0
- package/dist/artifacts-presentation.js +17 -0
- package/dist/artifacts-presentation.js.map +1 -0
- package/dist/artifacts-spreadsheet.d.ts +3 -0
- package/dist/artifacts-spreadsheet.js +17 -0
- package/dist/artifacts-spreadsheet.js.map +1 -0
- package/dist/artifacts.d.ts +2 -0
- package/dist/artifacts.js +199 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/{chunk-NJ7M2B6Z.js → chunk-5UQPNLIO.js} +16 -8
- package/dist/{chunk-NJ7M2B6Z.js.map → chunk-5UQPNLIO.js.map} +1 -1
- package/dist/{chunk-LHLYWG5R.js → chunk-6KLX2RH5.js} +461 -38
- package/dist/chunk-6KLX2RH5.js.map +1 -0
- package/dist/chunk-7WCLAEFV.js +2828 -0
- package/dist/chunk-7WCLAEFV.js.map +1 -0
- package/dist/{chunk-I4MPFDRP.js → chunk-B6WPZSTT.js} +104 -104
- package/dist/{chunk-I4MPFDRP.js.map → chunk-B6WPZSTT.js.map} +1 -1
- package/dist/{chunk-EKZH6IDG.js → chunk-C5ZPSSS5.js} +111 -1
- package/dist/chunk-C5ZPSSS5.js.map +1 -0
- package/dist/{chunk-OL6QVXGF.js → chunk-HA46CYQK.js} +152 -8
- package/dist/chunk-HA46CYQK.js.map +1 -0
- package/dist/chunk-HUIKIDM5.js +2148 -0
- package/dist/chunk-HUIKIDM5.js.map +1 -0
- package/dist/{chunk-FWXGSNXT.js → chunk-IGC6A2ZI.js} +585 -34
- package/dist/chunk-IGC6A2ZI.js.map +1 -0
- package/dist/chunk-JVYQRRC4.js +53 -0
- package/dist/chunk-JVYQRRC4.js.map +1 -0
- package/dist/chunk-KCGXPP5V.js +157 -0
- package/dist/chunk-KCGXPP5V.js.map +1 -0
- package/dist/chunk-LLOLUHVG.js +5937 -0
- package/dist/chunk-LLOLUHVG.js.map +1 -0
- package/dist/{chunk-WYA65Y3F.js → chunk-MKPIAGVL.js} +13 -3
- package/dist/chunk-MKPIAGVL.js.map +1 -0
- package/dist/chunk-PEI6RANI.js +3234 -0
- package/dist/chunk-PEI6RANI.js.map +1 -0
- package/dist/chunk-R3AZOAB5.js +933 -0
- package/dist/chunk-R3AZOAB5.js.map +1 -0
- package/dist/{chunk-O4VXOBGF.js → chunk-WSK7G5LE.js} +70 -2
- package/dist/chunk-WSK7G5LE.js.map +1 -0
- package/dist/{chunk-23EJ676W.js → chunk-XBZWOM2K.js} +9 -4
- package/dist/chunk-XBZWOM2K.js.map +1 -0
- package/dist/{chunk-XCNTXWBB.js → chunk-Y7OEDUJ5.js} +856 -338
- package/dist/chunk-Y7OEDUJ5.js.map +1 -0
- package/dist/client.d.ts +10 -2
- package/dist/clipboard.d.ts +1 -0
- package/dist/clipboard.js +7 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/commands/index.d.ts +3 -3
- package/dist/commands/registry.d.ts +1 -1
- package/dist/commands/types.d.ts +1 -1
- package/dist/components/approval-surface.d.ts +2 -2
- package/dist/components/artifacts/artifact-surface.d.ts +18 -0
- package/dist/components/artifacts/document-editor.d.ts +172 -0
- package/dist/components/artifacts/editable-artifact-ui.d.ts +13 -0
- package/dist/components/artifacts/editable-artifact-workbench.d.ts +49 -0
- package/dist/components/artifacts/editable-document.d.ts +24 -0
- package/dist/components/artifacts/editable-presentation.d.ts +24 -0
- package/dist/components/artifacts/editable-spreadsheet.d.ts +32 -0
- package/dist/components/artifacts/index.d.ts +9 -0
- package/dist/components/artifacts/presentation-editor.d.ts +297 -0
- package/dist/components/artifacts/published-html-artifact-frame.d.ts +14 -0
- package/dist/components/artifacts/spreadsheet-canvas.d.ts +120 -0
- package/dist/components/artifacts/spreadsheet-grid.d.ts +113 -0
- package/dist/components/browser-input.d.ts +7 -0
- package/dist/components/browser-viewer.d.ts +30 -0
- package/dist/components/chat-composer.d.ts +8 -8
- package/dist/components/code-editor.d.ts +25 -3
- package/dist/components/command-palette.d.ts +2 -2
- package/dist/components/composer-transcription-control.d.ts +2 -2
- package/dist/components/composer.d.ts +17 -17
- package/dist/components/computer-viewer.d.ts +24 -0
- package/dist/components/copy-button.d.ts +2 -2
- package/dist/components/desktop-viewer.d.ts +50 -22
- package/dist/components/diff-view.d.ts +1 -1
- package/dist/components/enrollment-consent.d.ts +1 -1
- package/dist/components/enrollment-device-flow.d.ts +1 -1
- package/dist/components/file-browser.d.ts +2 -2
- package/dist/components/fleet-tile.d.ts +1 -1
- package/dist/components/generated-video-player.d.ts +10 -0
- package/dist/components/human-input-form.d.ts +1 -1
- package/dist/components/human-input-surface.d.ts +2 -2
- package/dist/components/interaction-intervention-banner.d.ts +11 -0
- package/dist/components/interactive-workbench-viewers.d.ts +2 -0
- package/dist/components/machine-card.d.ts +2 -2
- package/dist/components/machine-dock-bar.d.ts +3 -3
- package/dist/components/machine-health-pill.d.ts +2 -2
- package/dist/components/machine-input-display.d.ts +1 -1
- package/dist/components/machine-metrics.d.ts +2 -2
- package/dist/components/machine-status-pill.d.ts +4 -4
- package/dist/components/machines/health.d.ts +1 -1
- package/dist/components/machines/machine-detail.d.ts +3 -3
- package/dist/components/machines/metric-history-chart.d.ts +1 -1
- package/dist/components/machines/metric-sparkline.d.ts +2 -2
- package/dist/components/machines/series.d.ts +2 -2
- package/dist/components/machines-dashboard.d.ts +2 -2
- package/dist/components/markdown.d.ts +1 -1
- package/dist/components/message-timeline.d.ts +12 -5
- package/dist/components/model-picker.d.ts +2 -2
- package/dist/components/model-policy-picker.d.ts +7 -7
- package/dist/components/pierre-diff.d.ts +1 -1
- package/dist/components/pierre-file.d.ts +1 -1
- package/dist/components/queue-draft-policy.d.ts +1 -1
- package/dist/components/queue-surface-implementation.d.ts +3 -3
- package/dist/components/queue-surface-state.d.ts +4 -4
- package/dist/components/queue-surface.d.ts +5 -5
- package/dist/components/sandbox-files.d.ts +3 -3
- package/dist/components/sandbox-terminal.d.ts +24 -4
- package/dist/components/sandbox-workspace.d.ts +28 -9
- package/dist/components/session-chrome.d.ts +4 -4
- package/dist/components/session-status.d.ts +2 -2
- package/dist/components/timeline-annotation-selection.d.ts +9 -0
- package/dist/components/timeline-annotations-dialog.d.ts +16 -0
- package/dist/components/timeline-annotations.d.ts +13 -0
- package/dist/components/tooltip.d.ts +4 -4
- package/dist/components/user-message-body.d.ts +2 -2
- package/dist/components/workbench-changes.d.ts +2 -2
- package/dist/components/workspace-dock.d.ts +1 -1
- package/dist/composer.d.ts +11 -11
- package/dist/composer.js +7 -7
- package/dist/hooks/internal.d.ts +1 -1
- package/dist/hooks/use-attached-browsers.d.ts +21 -0
- package/dist/hooks/use-auth-runs.d.ts +32 -0
- package/dist/hooks/use-available-models.d.ts +2 -2
- package/dist/hooks/use-billing-usage.d.ts +1 -1
- package/dist/hooks/use-browser-downloads.d.ts +25 -0
- package/dist/hooks/use-browser-frame-stream.d.ts +28 -0
- package/dist/hooks/use-browser-identities.d.ts +30 -0
- package/dist/hooks/use-browser-session.d.ts +29 -0
- package/dist/hooks/use-browser-sessions.d.ts +32 -0
- package/dist/hooks/use-channels.d.ts +26 -0
- package/dist/hooks/use-codex-accounts.d.ts +2 -2
- package/dist/hooks/use-composer.d.ts +36 -9
- package/dist/hooks/use-computer-frame-stream.d.ts +24 -0
- package/dist/hooks/use-computer-session.d.ts +29 -0
- package/dist/hooks/use-computer-sessions.d.ts +28 -0
- package/dist/hooks/use-desktop-stream.d.ts +5 -1
- package/dist/hooks/use-environments.d.ts +1 -1
- package/dist/hooks/use-file-attachments.d.ts +1 -1
- package/dist/hooks/use-goal.d.ts +2 -2
- package/dist/hooks/use-human-input.d.ts +2 -2
- package/dist/hooks/use-interaction-interventions.d.ts +27 -0
- package/dist/hooks/use-interaction-invalidation.d.ts +19 -0
- package/dist/hooks/use-last-started-turn-policy.d.ts +2 -2
- package/dist/hooks/use-machine-chip.d.ts +2 -2
- package/dist/hooks/use-machines.d.ts +4 -4
- package/dist/hooks/use-network-routes.d.ts +25 -0
- package/dist/hooks/use-packs.d.ts +7 -2
- package/dist/hooks/use-rigs.d.ts +1 -1
- package/dist/hooks/use-sandbox-files.d.ts +6 -4
- package/dist/hooks/use-sandbox-git.d.ts +6 -5
- package/dist/hooks/use-sandbox-terminal.d.ts +1 -1
- package/dist/hooks/use-scheduled-tasks.d.ts +1 -1
- package/dist/hooks/use-session-capabilities.d.ts +5 -4
- package/dist/hooks/use-session-control.d.ts +1 -1
- package/dist/hooks/use-session-events.d.ts +2 -2
- package/dist/hooks/use-session-lineage.d.ts +1 -1
- package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
- package/dist/hooks/use-session.d.ts +2 -2
- package/dist/hooks/use-site-auth-connections.d.ts +25 -0
- package/dist/hooks/use-slash-commands.d.ts +1 -1
- package/dist/hooks/use-supergrok-accounts.d.ts +26 -0
- package/dist/hooks/use-terminal-stream.d.ts +11 -2
- package/dist/hooks/use-turn-queue.d.ts +2 -2
- package/dist/hooks/use-video-artifact-playback.d.ts +9 -0
- package/dist/hooks/use-voice-input.d.ts +1 -1
- package/dist/hooks/use-workspace-capture.d.ts +1 -1
- package/dist/hooks/use-workspace-edit.d.ts +1 -1
- package/dist/hooks/use-workspace-sessions.d.ts +1 -1
- package/dist/hooks/use-workspaces.d.ts +1 -1
- package/dist/index.d.ts +162 -155
- package/dist/index.js +1822 -1703
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +33 -0
- package/dist/interaction.js +550 -0
- package/dist/interaction.js.map +1 -0
- package/dist/interactive-workbench-viewers-TOHBXJEE.js +15 -0
- package/dist/interactive-workbench-viewers-TOHBXJEE.js.map +1 -0
- package/dist/lib/host-platform.d.ts +5 -0
- package/dist/lib/relay-wire.d.ts +24 -1
- package/dist/lib/sandbox-liveness.d.ts +9 -0
- package/dist/lib/terminal-capability.d.ts +5 -0
- package/dist/lib/xterm-theme.d.ts +1 -1
- package/dist/machines.d.ts +30 -30
- package/dist/machines.js +2 -2
- package/dist/model-policy.d.ts +1 -1
- package/dist/model-policy.js +1 -1
- package/dist/provider.d.ts +6 -5
- package/dist/{queue-surface-implementation-U5JFG7HT.js → queue-surface-implementation-2KVJTYXH.js} +100 -48
- package/dist/queue-surface-implementation-2KVJTYXH.js.map +1 -0
- package/dist/realtime/dropdown-menu.d.ts +8 -8
- package/dist/realtime/realtime-control.d.ts +8 -8
- package/dist/realtime.d.ts +4 -4
- package/dist/realtime.js +38 -11
- package/dist/realtime.js.map +1 -1
- package/dist/session-context.d.ts +28 -2
- package/dist/session-ui.d.ts +14 -14
- package/dist/session-ui.js +5 -4
- package/dist/session.d.ts +28 -28
- package/dist/session.js +10 -10
- package/dist/timeline/activity-rail.d.ts +5 -3
- package/dist/timeline/disclosure-context.d.ts +1 -1
- package/dist/timeline/fleet-decision-projection.d.ts +1 -1
- package/dist/timeline/fleet-decision-row.d.ts +3 -3
- package/dist/timeline/index.d.ts +19 -19
- package/dist/timeline/parsers.d.ts +4 -1
- package/dist/timeline/projection.d.ts +3 -3
- package/dist/timeline/registry.d.ts +8 -2
- package/dist/timeline/screenshot-lightbox.d.ts +2 -2
- package/dist/timeline/shared.d.ts +21 -10
- package/dist/timeline/tool-diff.d.ts +2 -2
- package/dist/timeline/tool-renderers.d.ts +1 -1
- package/dist/timeline/turn-summary.d.ts +3 -3
- package/dist/timeline/types.d.ts +44 -3
- package/dist/timeline-annotation-selection-3L7LHGG2.js +187 -0
- package/dist/timeline-annotation-selection-3L7LHGG2.js.map +1 -0
- package/dist/timeline-annotations-dialog-U7EVLR75.js +196 -0
- package/dist/timeline-annotations-dialog-U7EVLR75.js.map +1 -0
- package/package.json +36 -7
- package/src/artifacts-document.ts +31 -0
- package/src/artifacts-presentation.ts +36 -0
- package/src/artifacts-spreadsheet.ts +21 -0
- package/src/artifacts.ts +89 -0
- package/src/client.ts +157 -1
- package/src/clipboard.ts +2 -0
- package/src/components/artifacts/artifact-surface.tsx +93 -0
- package/src/components/artifacts/document-editor.tsx +2255 -0
- package/src/components/artifacts/editable-artifact-ui.tsx +89 -0
- package/src/components/artifacts/editable-artifact-workbench.tsx +203 -0
- package/src/components/artifacts/editable-document.tsx +657 -0
- package/src/components/artifacts/editable-presentation.tsx +1020 -0
- package/src/components/artifacts/editable-spreadsheet.tsx +733 -0
- package/src/components/artifacts/index.ts +104 -0
- package/src/components/artifacts/presentation-editor.tsx +3148 -0
- package/src/components/artifacts/published-html-artifact-frame.tsx +38 -0
- package/src/components/artifacts/spreadsheet-canvas.ts +799 -0
- package/src/components/artifacts/spreadsheet-grid.tsx +2129 -0
- package/src/components/browser-input.ts +65 -0
- package/src/components/browser-viewer.tsx +2702 -0
- package/src/components/chat-composer.tsx +13 -0
- package/src/components/code-editor.tsx +89 -30
- package/src/components/composer-transcription-control.tsx +10 -6
- package/src/components/computer-viewer.tsx +1636 -0
- package/src/components/desktop-viewer.tsx +215 -74
- package/src/components/generated-video-player.tsx +89 -0
- package/src/components/interaction-intervention-banner.tsx +99 -0
- package/src/components/interactive-workbench-viewers.ts +2 -0
- package/src/components/machine-input-display.ts +3 -0
- package/src/components/machines/machine-detail.tsx +1 -1
- package/src/components/message-timeline.tsx +289 -32
- package/src/components/model-policy-picker.tsx +15 -0
- package/src/components/queue-surface-implementation.tsx +116 -60
- package/src/components/sandbox-files.tsx +2 -2
- package/src/components/sandbox-terminal.tsx +128 -46
- package/src/components/sandbox-workspace.tsx +256 -90
- package/src/components/session-chrome.tsx +53 -13
- package/src/components/timeline-annotation-selection.tsx +221 -0
- package/src/components/timeline-annotations-dialog.tsx +213 -0
- package/src/components/timeline-annotations.tsx +84 -0
- package/src/components/workbench-changes.tsx +188 -31
- package/src/components/workspace-dock.tsx +59 -5
- package/src/hooks/use-attached-browsers.ts +154 -0
- package/src/hooks/use-auth-runs.ts +278 -0
- package/src/hooks/use-browser-downloads.ts +218 -0
- package/src/hooks/use-browser-frame-stream.ts +425 -0
- package/src/hooks/use-browser-identities.ts +266 -0
- package/src/hooks/use-browser-session.ts +543 -0
- package/src/hooks/use-browser-sessions.ts +286 -0
- package/src/hooks/use-channels.ts +99 -0
- package/src/hooks/use-composer.ts +769 -24
- package/src/hooks/use-computer-frame-stream.ts +434 -0
- package/src/hooks/use-computer-session.ts +462 -0
- package/src/hooks/use-computer-sessions.ts +251 -0
- package/src/hooks/use-desktop-stream.ts +39 -7
- package/src/hooks/use-interaction-interventions.ts +242 -0
- package/src/hooks/use-interaction-invalidation.ts +66 -0
- package/src/hooks/use-machine-chip.ts +15 -3
- package/src/hooks/use-machines.ts +33 -3
- package/src/hooks/use-network-routes.ts +214 -0
- package/src/hooks/use-packs.ts +51 -0
- package/src/hooks/use-sandbox-files.ts +98 -55
- package/src/hooks/use-sandbox-git.ts +93 -38
- package/src/hooks/use-sandbox-terminal.ts +3 -2
- package/src/hooks/use-session-capabilities.ts +59 -46
- package/src/hooks/use-site-auth-connections.ts +226 -0
- package/src/hooks/use-supergrok-accounts.ts +81 -0
- package/src/hooks/use-terminal-stream.ts +359 -47
- package/src/hooks/use-video-artifact-playback.ts +28 -0
- package/src/hooks/use-workspace-edit.ts +2 -1
- package/src/index.ts +18 -0
- package/src/interaction.ts +101 -0
- package/src/lib/host-platform.ts +10 -0
- package/src/lib/relay-wire.ts +47 -4
- package/src/lib/sandbox-liveness.ts +11 -0
- package/src/lib/terminal-capability.ts +10 -0
- package/src/model-policy.ts +19 -4
- package/src/provider.tsx +133 -1
- package/src/realtime/realtime-control.tsx +34 -6
- package/src/session-context.ts +156 -1
- package/src/timeline/activity-rail.tsx +25 -3
- package/src/timeline/index.ts +3 -0
- package/src/timeline/parsers.ts +14 -1
- package/src/timeline/projection.ts +269 -14
- package/src/timeline/registry.ts +13 -1
- package/src/timeline/screenshot-lightbox.tsx +19 -9
- package/src/timeline/shared.tsx +56 -8
- package/src/timeline/tool-renderers.tsx +303 -50
- package/src/timeline/types.ts +57 -3
- package/styles/compiled.css +312 -21
- package/styles/index.css +15 -0
- package/styles/responsive.css +13 -1
- package/dist/chunk-23EJ676W.js.map +0 -1
- package/dist/chunk-EKZH6IDG.js.map +0 -1
- package/dist/chunk-FWXGSNXT.js.map +0 -1
- package/dist/chunk-LHLYWG5R.js.map +0 -1
- package/dist/chunk-O4VXOBGF.js.map +0 -1
- package/dist/chunk-OL6QVXGF.js.map +0 -1
- package/dist/chunk-WYA65Y3F.js.map +0 -1
- package/dist/chunk-XCNTXWBB.js.map +0 -1
- package/dist/fleet-decision-projection-SLNZMS42.js +0 -241
- package/dist/fleet-decision-projection-SLNZMS42.js.map +0 -1
- package/dist/queue-surface-implementation-U5JFG7HT.js.map +0 -1
|
@@ -0,0 +1,2148 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArtifactSurface,
|
|
3
|
+
EditableArtifactMessage,
|
|
4
|
+
asEditableArtifactError,
|
|
5
|
+
editableArtifactAccessRevoked,
|
|
6
|
+
editableArtifactProjectionKey,
|
|
7
|
+
editableArtifactStatusLabel,
|
|
8
|
+
useEditableArtifactView
|
|
9
|
+
} from "./chunk-KCGXPP5V.js";
|
|
10
|
+
import {
|
|
11
|
+
cn
|
|
12
|
+
} from "./chunk-22KP6LR5.js";
|
|
13
|
+
|
|
14
|
+
// src/components/artifacts/document-editor.tsx
|
|
15
|
+
import {
|
|
16
|
+
BoldIcon,
|
|
17
|
+
Columns2Icon,
|
|
18
|
+
ItalicIcon,
|
|
19
|
+
MessageSquareTextIcon,
|
|
20
|
+
PilcrowIcon,
|
|
21
|
+
Rows3Icon,
|
|
22
|
+
UnderlineIcon
|
|
23
|
+
} from "lucide-react";
|
|
24
|
+
import {
|
|
25
|
+
memo,
|
|
26
|
+
useCallback,
|
|
27
|
+
useEffect,
|
|
28
|
+
useLayoutEffect,
|
|
29
|
+
useMemo,
|
|
30
|
+
useRef,
|
|
31
|
+
useState
|
|
32
|
+
} from "react";
|
|
33
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
34
|
+
var POINT_TO_CSS_PIXEL = 4 / 3;
|
|
35
|
+
var DEFAULT_VIEWPORT_HEIGHT = 640;
|
|
36
|
+
var RESPONSIVE_CONTINUOUS_LAYOUT_WIDTH = 640;
|
|
37
|
+
var PAGINATED_GAP = 24;
|
|
38
|
+
var CONTINUOUS_GAP = 1;
|
|
39
|
+
var MAX_PLAIN_TEXT_TRANSFER = 1e6;
|
|
40
|
+
var MAX_PROJECTION_BLOCKS = 1e5;
|
|
41
|
+
var MAX_PROJECTION_TABLE_CELLS = 1e6;
|
|
42
|
+
var MAX_PROJECTION_ANNOTATIONS = 1e5;
|
|
43
|
+
var MAX_PROJECTION_TEXT = 1e6;
|
|
44
|
+
var EMPTY_COMMENTS = [];
|
|
45
|
+
var EMPTY_CHANGES = [];
|
|
46
|
+
function DocumentEditor(props) {
|
|
47
|
+
return /* @__PURE__ */ jsx(DocumentEditorCore, { ...props, document: asDocumentView(props.document) });
|
|
48
|
+
}
|
|
49
|
+
function DocumentProjectionEditor({
|
|
50
|
+
projection,
|
|
51
|
+
commit,
|
|
52
|
+
onCommandError,
|
|
53
|
+
onSelectionChange,
|
|
54
|
+
readOnly = false,
|
|
55
|
+
...props
|
|
56
|
+
}) {
|
|
57
|
+
const view = useMemo(() => documentViewFromProjection(projection), [projection]);
|
|
58
|
+
return /* @__PURE__ */ jsx(
|
|
59
|
+
DocumentEditorCore,
|
|
60
|
+
{
|
|
61
|
+
...props,
|
|
62
|
+
document: view.document,
|
|
63
|
+
revisionToken: projection.revision,
|
|
64
|
+
readOnly: readOnly || !commit,
|
|
65
|
+
onCommandError,
|
|
66
|
+
blockIdForView: (blockId) => view.externalIdByInternal.get(blockId) ?? blockId,
|
|
67
|
+
onSelectionChange: (selection) => onSelectionChange?.({
|
|
68
|
+
...selection,
|
|
69
|
+
blockId: view.externalIdByInternal.get(selection.blockId) ?? selection.blockId
|
|
70
|
+
}),
|
|
71
|
+
onCommit: (command) => commit?.(mapDocumentCommit(command, view.externalIdByInternal))
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
function DocumentEditorCore({
|
|
76
|
+
document: document2,
|
|
77
|
+
revisionToken,
|
|
78
|
+
layout: controlledLayout,
|
|
79
|
+
defaultLayout,
|
|
80
|
+
onLayoutChange,
|
|
81
|
+
onSelectionChange,
|
|
82
|
+
onCommit,
|
|
83
|
+
onCommandError,
|
|
84
|
+
blockIdForView = identity,
|
|
85
|
+
readOnly = false,
|
|
86
|
+
ariaLabel = "Document editor",
|
|
87
|
+
viewportHeight = DEFAULT_VIEWPORT_HEIGHT,
|
|
88
|
+
overscanPages = 1,
|
|
89
|
+
className
|
|
90
|
+
}) {
|
|
91
|
+
const [uncontrolledLayout, setUncontrolledLayout] = useState(
|
|
92
|
+
defaultLayout ?? "paginated"
|
|
93
|
+
);
|
|
94
|
+
const [scrollTop, setScrollTop] = useState(0);
|
|
95
|
+
const [measuredViewport, setMeasuredViewport] = useState({ width: 0, height: 0 });
|
|
96
|
+
const [localRevision, setLocalRevision] = useState(document2.revision);
|
|
97
|
+
const [measuredPageHeights, setMeasuredPageHeights] = useState(
|
|
98
|
+
() => /* @__PURE__ */ new Map()
|
|
99
|
+
);
|
|
100
|
+
const [selectedBlockId, setSelectedBlockId] = useState(null);
|
|
101
|
+
const [hasTextSelection, setHasTextSelection] = useState(false);
|
|
102
|
+
const selectionRef = useRef(null);
|
|
103
|
+
const [openThreadId, setOpenThreadId] = useState(null);
|
|
104
|
+
const [pendingCommands, setPendingCommands] = useState(0);
|
|
105
|
+
const [commandFailure, setCommandFailure] = useState(null);
|
|
106
|
+
const editorNodes = useRef(/* @__PURE__ */ new Map());
|
|
107
|
+
const viewportRef = useRef(null);
|
|
108
|
+
const pendingScrollTop = useRef(0);
|
|
109
|
+
const scrollFrame = useRef(null);
|
|
110
|
+
const pendingMeasurementAnchor = useRef(null);
|
|
111
|
+
const responsiveLayoutInitialized = useRef(false);
|
|
112
|
+
const measuredPageHeightsRef = useRef(/* @__PURE__ */ new Map());
|
|
113
|
+
const mountedRef = useRef(true);
|
|
114
|
+
const commandScopeRef = useRef(0);
|
|
115
|
+
const commandVersionsRef = useRef(/* @__PURE__ */ new Map());
|
|
116
|
+
const layout = controlledLayout ?? uncontrolledLayout;
|
|
117
|
+
const documentRevision = revisionToken ?? document2.revision;
|
|
118
|
+
const pageGap = layout === "paginated" ? PAGINATED_GAP : CONTINUOUS_GAP;
|
|
119
|
+
const basePages = useMemo(
|
|
120
|
+
() => projectPages(document2, layout),
|
|
121
|
+
// The revision values intentionally invalidate projections over the mutable public model.
|
|
122
|
+
// oxlint-disable-next-line react-hooks/exhaustive-deps
|
|
123
|
+
[document2, documentRevision, layout, localRevision]
|
|
124
|
+
);
|
|
125
|
+
const naturalPages = useMemo(
|
|
126
|
+
() => basePages.map((page) => ({
|
|
127
|
+
...page,
|
|
128
|
+
// Estimates seed unmounted pages only. Once mounted, intrinsic content
|
|
129
|
+
// may correct the page in either direction while preserving paper size.
|
|
130
|
+
height: measuredPageHeights.has(page.key) ? Math.max(page.minimumHeight, measuredPageHeights.get(page.key) ?? page.height) : page.height
|
|
131
|
+
})),
|
|
132
|
+
[basePages, measuredPageHeights]
|
|
133
|
+
);
|
|
134
|
+
const pages = useMemo(
|
|
135
|
+
() => naturalPages.map((page) => {
|
|
136
|
+
const renderScale = layout === "paginated" ? responsiveDocumentPageScale(page.page, measuredViewport.width) : 1;
|
|
137
|
+
return {
|
|
138
|
+
...page,
|
|
139
|
+
naturalHeight: page.height,
|
|
140
|
+
renderScale,
|
|
141
|
+
height: page.height * renderScale,
|
|
142
|
+
minimumHeight: page.minimumHeight * renderScale
|
|
143
|
+
};
|
|
144
|
+
}),
|
|
145
|
+
[layout, measuredViewport.width, naturalPages]
|
|
146
|
+
);
|
|
147
|
+
const annotations = useMemo(
|
|
148
|
+
() => indexAnnotations(document2),
|
|
149
|
+
// The revision values intentionally invalidate indexes over the mutable public model.
|
|
150
|
+
// oxlint-disable-next-line react-hooks/exhaustive-deps
|
|
151
|
+
[document2, documentRevision, localRevision]
|
|
152
|
+
);
|
|
153
|
+
const axis = useMemo(() => buildPageAxis(pages, pageGap), [pages, pageGap]);
|
|
154
|
+
const pagesRef = useRef(pages);
|
|
155
|
+
const axisRef = useRef(axis);
|
|
156
|
+
pagesRef.current = pages;
|
|
157
|
+
axisRef.current = axis;
|
|
158
|
+
const blockPageIndex = useMemo(() => {
|
|
159
|
+
const index = /* @__PURE__ */ new Map();
|
|
160
|
+
pages.forEach((page, pageIndex) => {
|
|
161
|
+
for (const { block } of page.blocks) index.set(block.id, pageIndex);
|
|
162
|
+
});
|
|
163
|
+
return index;
|
|
164
|
+
}, [pages]);
|
|
165
|
+
const activePage = selectedBlockId ? blockPageIndex.get(selectedBlockId) ?? -1 : -1;
|
|
166
|
+
const mountedIndexes = useMemo(
|
|
167
|
+
() => visiblePageIndexes(
|
|
168
|
+
axis,
|
|
169
|
+
pages.length,
|
|
170
|
+
scrollTop,
|
|
171
|
+
Math.max(1, measuredViewport.height || viewportHeight),
|
|
172
|
+
Math.max(0, Math.floor(overscanPages)),
|
|
173
|
+
activePage
|
|
174
|
+
),
|
|
175
|
+
[
|
|
176
|
+
activePage,
|
|
177
|
+
axis,
|
|
178
|
+
measuredViewport.height,
|
|
179
|
+
overscanPages,
|
|
180
|
+
pages.length,
|
|
181
|
+
scrollTop,
|
|
182
|
+
viewportHeight
|
|
183
|
+
]
|
|
184
|
+
);
|
|
185
|
+
useLayoutEffect(() => {
|
|
186
|
+
const element = viewportRef.current;
|
|
187
|
+
if (!element) return;
|
|
188
|
+
const measure = () => {
|
|
189
|
+
const width = element.clientWidth;
|
|
190
|
+
const height = element.clientHeight;
|
|
191
|
+
if (width <= 0 && height <= 0) return;
|
|
192
|
+
setMeasuredViewport(
|
|
193
|
+
(current) => current.width === width && current.height === height ? current : { width, height }
|
|
194
|
+
);
|
|
195
|
+
};
|
|
196
|
+
measure();
|
|
197
|
+
const observer = globalThis.ResizeObserver ? new globalThis.ResizeObserver(measure) : null;
|
|
198
|
+
observer?.observe(element);
|
|
199
|
+
globalThis.addEventListener?.("resize", measure);
|
|
200
|
+
return () => {
|
|
201
|
+
observer?.disconnect();
|
|
202
|
+
globalThis.removeEventListener?.("resize", measure);
|
|
203
|
+
};
|
|
204
|
+
}, []);
|
|
205
|
+
useLayoutEffect(() => {
|
|
206
|
+
if (responsiveLayoutInitialized.current || controlledLayout !== void 0 || defaultLayout !== void 0 || measuredViewport.width <= 0) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
responsiveLayoutInitialized.current = true;
|
|
210
|
+
if (measuredViewport.width < RESPONSIVE_CONTINUOUS_LAYOUT_WIDTH) {
|
|
211
|
+
setUncontrolledLayout("continuous");
|
|
212
|
+
onLayoutChange?.("continuous");
|
|
213
|
+
}
|
|
214
|
+
}, [controlledLayout, defaultLayout, measuredViewport.width, onLayoutChange]);
|
|
215
|
+
useEffect(() => {
|
|
216
|
+
commandScopeRef.current += 1;
|
|
217
|
+
commandVersionsRef.current.clear();
|
|
218
|
+
selectionRef.current = null;
|
|
219
|
+
setSelectedBlockId(null);
|
|
220
|
+
setHasTextSelection(false);
|
|
221
|
+
setOpenThreadId(null);
|
|
222
|
+
setLocalRevision(document2.revision);
|
|
223
|
+
setPendingCommands(0);
|
|
224
|
+
setCommandFailure(null);
|
|
225
|
+
}, [document2, revisionToken]);
|
|
226
|
+
useEffect(() => {
|
|
227
|
+
mountedRef.current = true;
|
|
228
|
+
return () => {
|
|
229
|
+
mountedRef.current = false;
|
|
230
|
+
if (scrollFrame.current !== null && globalThis.cancelAnimationFrame) {
|
|
231
|
+
globalThis.cancelAnimationFrame(scrollFrame.current);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}, []);
|
|
235
|
+
const handleScroll = useCallback((event) => {
|
|
236
|
+
pendingScrollTop.current = event.currentTarget.scrollTop;
|
|
237
|
+
if (scrollFrame.current !== null) return;
|
|
238
|
+
if (!globalThis.requestAnimationFrame) {
|
|
239
|
+
scrollFrame.current = -1;
|
|
240
|
+
queueMicrotask(() => {
|
|
241
|
+
scrollFrame.current = null;
|
|
242
|
+
setScrollTop(pendingScrollTop.current);
|
|
243
|
+
});
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
scrollFrame.current = globalThis.requestAnimationFrame(() => {
|
|
247
|
+
scrollFrame.current = null;
|
|
248
|
+
setScrollTop(pendingScrollTop.current);
|
|
249
|
+
});
|
|
250
|
+
}, []);
|
|
251
|
+
const clearSelection = useCallback(() => {
|
|
252
|
+
selectionRef.current = null;
|
|
253
|
+
setSelectedBlockId(null);
|
|
254
|
+
setHasTextSelection(false);
|
|
255
|
+
}, []);
|
|
256
|
+
const recordPageHeight = useCallback((key, height) => {
|
|
257
|
+
if (!Number.isFinite(height) || height <= 0) return;
|
|
258
|
+
const rounded = Math.ceil(height);
|
|
259
|
+
const current = measuredPageHeightsRef.current;
|
|
260
|
+
if (Math.abs((current.get(key) ?? 0) - rounded) <= 1) return;
|
|
261
|
+
const currentPages = pagesRef.current;
|
|
262
|
+
if (!currentPages.some((page) => page.key === key)) return;
|
|
263
|
+
const viewport = viewportRef.current;
|
|
264
|
+
if (viewport && pendingMeasurementAnchor.current === null) {
|
|
265
|
+
const currentAxis = axisRef.current;
|
|
266
|
+
const anchorIndex = pageAtOffset(currentAxis, currentPages.length, viewport.scrollTop);
|
|
267
|
+
const anchorPage = currentPages[anchorIndex];
|
|
268
|
+
if (anchorPage) {
|
|
269
|
+
pendingMeasurementAnchor.current = {
|
|
270
|
+
axis: currentAxis,
|
|
271
|
+
key: anchorPage.key,
|
|
272
|
+
offset: viewport.scrollTop - currentAxis.offsets[anchorIndex],
|
|
273
|
+
scrollTop: viewport.scrollTop
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
const next = /* @__PURE__ */ new Map();
|
|
278
|
+
for (const page of currentPages) {
|
|
279
|
+
const measured = current.get(page.key);
|
|
280
|
+
if (measured !== void 0) next.set(page.key, measured);
|
|
281
|
+
}
|
|
282
|
+
next.set(key, rounded);
|
|
283
|
+
measuredPageHeightsRef.current = next;
|
|
284
|
+
setMeasuredPageHeights(next);
|
|
285
|
+
}, []);
|
|
286
|
+
useLayoutEffect(() => {
|
|
287
|
+
const anchor = pendingMeasurementAnchor.current;
|
|
288
|
+
if (!anchor || anchor.axis === axis) return;
|
|
289
|
+
pendingMeasurementAnchor.current = null;
|
|
290
|
+
const viewport = viewportRef.current;
|
|
291
|
+
if (!viewport || Math.abs(viewport.scrollTop - anchor.scrollTop) > 1) return;
|
|
292
|
+
const anchorIndex = pages.findIndex((page) => page.key === anchor.key);
|
|
293
|
+
if (anchorIndex < 0) return;
|
|
294
|
+
const nextScrollTop = Math.max(0, axis.offsets[anchorIndex] + anchor.offset);
|
|
295
|
+
if (Math.abs(viewport.scrollTop - nextScrollTop) > 0.5) {
|
|
296
|
+
viewport.scrollTop = nextScrollTop;
|
|
297
|
+
}
|
|
298
|
+
const correctedScrollTop = viewport.scrollTop;
|
|
299
|
+
pendingScrollTop.current = correctedScrollTop;
|
|
300
|
+
setScrollTop(
|
|
301
|
+
(current) => Math.abs(current - correctedScrollTop) > 0.5 ? correctedScrollTop : current
|
|
302
|
+
);
|
|
303
|
+
}, [axis, pages]);
|
|
304
|
+
const publishSelection = useCallback(
|
|
305
|
+
(next) => {
|
|
306
|
+
selectionRef.current = next;
|
|
307
|
+
setSelectedBlockId((current) => current === next.blockId ? current : next.blockId);
|
|
308
|
+
const nextHasTextSelection = next.end > next.start;
|
|
309
|
+
setHasTextSelection(
|
|
310
|
+
(current) => current === nextHasTextSelection ? current : nextHasTextSelection
|
|
311
|
+
);
|
|
312
|
+
onSelectionChange?.(next);
|
|
313
|
+
},
|
|
314
|
+
[onSelectionChange]
|
|
315
|
+
);
|
|
316
|
+
const readDomSelection = useCallback(
|
|
317
|
+
(blockId) => {
|
|
318
|
+
const node = editorNodes.current.get(blockId);
|
|
319
|
+
const offsets = node ? selectionOffsets(node) : null;
|
|
320
|
+
const next = {
|
|
321
|
+
blockId,
|
|
322
|
+
start: offsets?.start ?? 0,
|
|
323
|
+
end: offsets?.end ?? 0
|
|
324
|
+
};
|
|
325
|
+
publishSelection(next);
|
|
326
|
+
return next;
|
|
327
|
+
},
|
|
328
|
+
[publishSelection]
|
|
329
|
+
);
|
|
330
|
+
const runCommit = useCallback(
|
|
331
|
+
(command) => {
|
|
332
|
+
if (!onCommit) return;
|
|
333
|
+
const scope = commandScopeRef.current;
|
|
334
|
+
const submit = () => {
|
|
335
|
+
const key = command.blockId;
|
|
336
|
+
const version = (commandVersionsRef.current.get(key) ?? 0) + 1;
|
|
337
|
+
commandVersionsRef.current.set(key, version);
|
|
338
|
+
setCommandFailure(null);
|
|
339
|
+
const fail = (cause) => {
|
|
340
|
+
if (!mountedRef.current || commandScopeRef.current !== scope || commandVersionsRef.current.get(key) !== version) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
const error = asDocumentError(cause);
|
|
344
|
+
setCommandFailure({ message: error.message, retry: submit });
|
|
345
|
+
onCommandError?.(error);
|
|
346
|
+
};
|
|
347
|
+
let result;
|
|
348
|
+
try {
|
|
349
|
+
result = onCommit(command);
|
|
350
|
+
} catch (cause) {
|
|
351
|
+
fail(cause);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
if (!result || typeof result.then !== "function") return;
|
|
355
|
+
setPendingCommands((current) => current + 1);
|
|
356
|
+
void Promise.resolve(result).then(
|
|
357
|
+
() => {
|
|
358
|
+
if (!mountedRef.current || commandScopeRef.current !== scope) return;
|
|
359
|
+
setPendingCommands((current) => Math.max(0, current - 1));
|
|
360
|
+
},
|
|
361
|
+
(cause) => {
|
|
362
|
+
if (!mountedRef.current || commandScopeRef.current !== scope) return;
|
|
363
|
+
setPendingCommands((current) => Math.max(0, current - 1));
|
|
364
|
+
fail(cause);
|
|
365
|
+
}
|
|
366
|
+
);
|
|
367
|
+
};
|
|
368
|
+
submit();
|
|
369
|
+
},
|
|
370
|
+
[onCommandError, onCommit]
|
|
371
|
+
);
|
|
372
|
+
const commitText = useCallback(
|
|
373
|
+
(paragraph, text) => {
|
|
374
|
+
if (readOnly || paragraph.text === text) return;
|
|
375
|
+
paragraph.text = text;
|
|
376
|
+
runCommit({
|
|
377
|
+
kind: "text",
|
|
378
|
+
blockId: paragraph.id,
|
|
379
|
+
text,
|
|
380
|
+
revision: revisionToken ?? document2.revision
|
|
381
|
+
});
|
|
382
|
+
},
|
|
383
|
+
[document2, readOnly, revisionToken, runCommit]
|
|
384
|
+
);
|
|
385
|
+
const applyFormat = useCallback(
|
|
386
|
+
(property) => {
|
|
387
|
+
const selection = selectionRef.current;
|
|
388
|
+
if (readOnly || !selection) return;
|
|
389
|
+
let block;
|
|
390
|
+
try {
|
|
391
|
+
block = document2.resolve(selection.blockId);
|
|
392
|
+
} catch {
|
|
393
|
+
clearSelection();
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (!isDocumentParagraph(block)) return;
|
|
397
|
+
const node = editorNodes.current.get(block.id);
|
|
398
|
+
if (node) commitText(block, node.textContent ?? "");
|
|
399
|
+
const current = selectionOffsets(node) ?? selection;
|
|
400
|
+
const start = Math.max(0, Math.min(current.start, block.text.length));
|
|
401
|
+
const end = Math.max(start, Math.min(current.end, block.text.length));
|
|
402
|
+
if (end <= start) return;
|
|
403
|
+
const effectiveEnd = end;
|
|
404
|
+
const effectiveStart = start;
|
|
405
|
+
const style = toggledTextStyle(block, effectiveStart, effectiveEnd, property);
|
|
406
|
+
block.format({ start: effectiveStart, end: effectiveEnd, style });
|
|
407
|
+
runCommit({
|
|
408
|
+
kind: "format",
|
|
409
|
+
blockId: block.id,
|
|
410
|
+
range: { start: effectiveStart, end: effectiveEnd },
|
|
411
|
+
style,
|
|
412
|
+
revision: revisionToken ?? document2.revision
|
|
413
|
+
});
|
|
414
|
+
queueMicrotask(() => restoreSelection(editorNodes.current.get(block.id), start, end));
|
|
415
|
+
},
|
|
416
|
+
[clearSelection, commitText, document2, readOnly, revisionToken, runCommit]
|
|
417
|
+
);
|
|
418
|
+
const handleEditorKeyDown = useCallback(
|
|
419
|
+
(event, paragraph) => {
|
|
420
|
+
const modifier = event.metaKey || event.ctrlKey;
|
|
421
|
+
if (!modifier) return;
|
|
422
|
+
const key = event.key.toLowerCase();
|
|
423
|
+
if (key !== "b" && key !== "i" && key !== "u") return;
|
|
424
|
+
event.preventDefault();
|
|
425
|
+
const current = readDomSelection(paragraph.id);
|
|
426
|
+
selectionRef.current = current;
|
|
427
|
+
const property = key === "b" ? "bold" : key === "i" ? "italic" : "underline";
|
|
428
|
+
const start = current.start;
|
|
429
|
+
const end = current.end;
|
|
430
|
+
if (end <= start || readOnly) return;
|
|
431
|
+
const effectiveStart = start;
|
|
432
|
+
const effectiveEnd = end;
|
|
433
|
+
const style = toggledTextStyle(paragraph, effectiveStart, effectiveEnd, property);
|
|
434
|
+
paragraph.format({ start: effectiveStart, end: effectiveEnd, style });
|
|
435
|
+
runCommit({
|
|
436
|
+
kind: "format",
|
|
437
|
+
blockId: paragraph.id,
|
|
438
|
+
range: { start: effectiveStart, end: effectiveEnd },
|
|
439
|
+
style,
|
|
440
|
+
revision: revisionToken ?? document2.revision
|
|
441
|
+
});
|
|
442
|
+
queueMicrotask(() => restoreSelection(editorNodes.current.get(paragraph.id), start, end));
|
|
443
|
+
},
|
|
444
|
+
[document2, readDomSelection, readOnly, revisionToken, runCommit]
|
|
445
|
+
);
|
|
446
|
+
const changeLayout = (next) => {
|
|
447
|
+
if (controlledLayout === void 0) setUncontrolledLayout(next);
|
|
448
|
+
onLayoutChange?.(next);
|
|
449
|
+
};
|
|
450
|
+
const addFirstParagraph = () => {
|
|
451
|
+
if (readOnly) return;
|
|
452
|
+
const paragraph = document2.blocks.addParagraph("");
|
|
453
|
+
setLocalRevision(document2.revision);
|
|
454
|
+
publishSelection({ blockId: paragraph.id, start: 0, end: 0 });
|
|
455
|
+
runCommit({
|
|
456
|
+
kind: "insert-paragraph",
|
|
457
|
+
blockId: paragraph.id,
|
|
458
|
+
revision: revisionToken ?? document2.revision
|
|
459
|
+
});
|
|
460
|
+
};
|
|
461
|
+
return /* @__PURE__ */ jsxs(
|
|
462
|
+
"div",
|
|
463
|
+
{
|
|
464
|
+
"data-og-document-editor": "",
|
|
465
|
+
"data-og-document-layout": layout,
|
|
466
|
+
"data-og-command-state": commandFailure ? "error" : pendingCommands > 0 ? "pending" : "idle",
|
|
467
|
+
"aria-busy": pendingCommands > 0 ? "true" : void 0,
|
|
468
|
+
className: cn("flex h-full min-h-0 min-w-0 flex-col bg-og-surface-2", className),
|
|
469
|
+
children: [
|
|
470
|
+
/* @__PURE__ */ jsxs(
|
|
471
|
+
"div",
|
|
472
|
+
{
|
|
473
|
+
role: "toolbar",
|
|
474
|
+
"aria-label": "Document formatting",
|
|
475
|
+
className: "flex min-h-10 shrink-0 items-center gap-1 border-b border-og-border bg-og-surface-1 px-2",
|
|
476
|
+
children: [
|
|
477
|
+
/* @__PURE__ */ jsx(
|
|
478
|
+
ToolbarButton,
|
|
479
|
+
{
|
|
480
|
+
label: "Bold",
|
|
481
|
+
shortcut: "\u2318B",
|
|
482
|
+
disabled: readOnly || !selectedBlockId || !hasTextSelection,
|
|
483
|
+
onPress: () => applyFormat("bold"),
|
|
484
|
+
children: /* @__PURE__ */ jsx(BoldIcon, { className: "size-4" })
|
|
485
|
+
}
|
|
486
|
+
),
|
|
487
|
+
/* @__PURE__ */ jsx(
|
|
488
|
+
ToolbarButton,
|
|
489
|
+
{
|
|
490
|
+
label: "Italic",
|
|
491
|
+
shortcut: "\u2318I",
|
|
492
|
+
disabled: readOnly || !selectedBlockId || !hasTextSelection,
|
|
493
|
+
onPress: () => applyFormat("italic"),
|
|
494
|
+
children: /* @__PURE__ */ jsx(ItalicIcon, { className: "size-4" })
|
|
495
|
+
}
|
|
496
|
+
),
|
|
497
|
+
/* @__PURE__ */ jsx(
|
|
498
|
+
ToolbarButton,
|
|
499
|
+
{
|
|
500
|
+
label: "Underline",
|
|
501
|
+
shortcut: "\u2318U",
|
|
502
|
+
disabled: readOnly || !selectedBlockId || !hasTextSelection,
|
|
503
|
+
onPress: () => applyFormat("underline"),
|
|
504
|
+
children: /* @__PURE__ */ jsx(UnderlineIcon, { className: "size-4" })
|
|
505
|
+
}
|
|
506
|
+
),
|
|
507
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "mx-1 h-5 w-px bg-og-border" }),
|
|
508
|
+
/* @__PURE__ */ jsx(
|
|
509
|
+
ToolbarButton,
|
|
510
|
+
{
|
|
511
|
+
label: "Continuous layout",
|
|
512
|
+
pressed: layout === "continuous",
|
|
513
|
+
onPress: () => changeLayout("continuous"),
|
|
514
|
+
children: /* @__PURE__ */ jsx(Rows3Icon, { className: "size-4" })
|
|
515
|
+
}
|
|
516
|
+
),
|
|
517
|
+
/* @__PURE__ */ jsx(
|
|
518
|
+
ToolbarButton,
|
|
519
|
+
{
|
|
520
|
+
label: "Paginated layout",
|
|
521
|
+
pressed: layout === "paginated",
|
|
522
|
+
onPress: () => changeLayout("paginated"),
|
|
523
|
+
children: /* @__PURE__ */ jsx(Columns2Icon, { className: "size-4" })
|
|
524
|
+
}
|
|
525
|
+
),
|
|
526
|
+
/* @__PURE__ */ jsxs("span", { className: "ml-auto truncate text-og-xs text-og-fg-muted", children: [
|
|
527
|
+
commandFailure ? "Change not saved" : pendingCommands > 0 ? "Saving\u2026" : readOnly ? "Read only" : "Editing",
|
|
528
|
+
" ",
|
|
529
|
+
"\xB7 ",
|
|
530
|
+
document2.blocks.items.length.toLocaleString(),
|
|
531
|
+
" blocks"
|
|
532
|
+
] })
|
|
533
|
+
]
|
|
534
|
+
}
|
|
535
|
+
),
|
|
536
|
+
/* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", className: "sr-only", children: commandFailure?.message ?? (pendingCommands > 0 ? "Saving document changes" : "") }),
|
|
537
|
+
/* @__PURE__ */ jsxs(
|
|
538
|
+
"div",
|
|
539
|
+
{
|
|
540
|
+
ref: viewportRef,
|
|
541
|
+
role: "document",
|
|
542
|
+
"aria-label": ariaLabel,
|
|
543
|
+
"aria-readonly": readOnly,
|
|
544
|
+
tabIndex: -1,
|
|
545
|
+
onPointerDown: (event) => {
|
|
546
|
+
const target = event.target;
|
|
547
|
+
if (target instanceof Element && !target.closest("[data-og-paragraph]")) {
|
|
548
|
+
clearSelection();
|
|
549
|
+
}
|
|
550
|
+
},
|
|
551
|
+
onScroll: handleScroll,
|
|
552
|
+
className: cn(
|
|
553
|
+
"relative min-h-0 min-w-0 flex-1 overflow-auto overscroll-contain outline-hidden [overflow-anchor:none]",
|
|
554
|
+
layout === "paginated" ? "bg-og-surface-3" : "bg-og-surface-1"
|
|
555
|
+
),
|
|
556
|
+
style: { height: Math.max(1, viewportHeight) },
|
|
557
|
+
children: [
|
|
558
|
+
commandFailure ? /* @__PURE__ */ jsxs(
|
|
559
|
+
"div",
|
|
560
|
+
{
|
|
561
|
+
role: "alert",
|
|
562
|
+
className: "absolute left-2 top-2 z-30 flex w-fit items-center gap-2 rounded-og-sm border border-og-status-failed/30 bg-og-surface-1/95 px-2 py-1 text-og-xs text-og-status-failed shadow-og-sm",
|
|
563
|
+
style: { maxWidth: "calc(100% - 1rem)" },
|
|
564
|
+
children: [
|
|
565
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: commandFailure.message }),
|
|
566
|
+
/* @__PURE__ */ jsx(
|
|
567
|
+
"button",
|
|
568
|
+
{
|
|
569
|
+
type: "button",
|
|
570
|
+
onClick: commandFailure.retry,
|
|
571
|
+
className: "shrink-0 rounded-og-xs px-1.5 py-0.5 font-medium outline-hidden hover:bg-og-surface-3 focus-visible:ring-2 focus-visible:ring-og-accent",
|
|
572
|
+
children: "Retry"
|
|
573
|
+
}
|
|
574
|
+
)
|
|
575
|
+
]
|
|
576
|
+
}
|
|
577
|
+
) : null,
|
|
578
|
+
pages.length === 0 ? /* @__PURE__ */ jsx("div", { className: "grid h-full place-items-center p-8 text-center", children: /* @__PURE__ */ jsxs("div", { children: [
|
|
579
|
+
/* @__PURE__ */ jsx(PilcrowIcon, { "aria-hidden": true, className: "mx-auto mb-3 size-7 text-og-fg-subtle" }),
|
|
580
|
+
/* @__PURE__ */ jsx("p", { className: "text-og-sm text-og-fg-muted", children: "This document is empty." }),
|
|
581
|
+
!readOnly ? /* @__PURE__ */ jsx(
|
|
582
|
+
"button",
|
|
583
|
+
{
|
|
584
|
+
type: "button",
|
|
585
|
+
onClick: addFirstParagraph,
|
|
586
|
+
className: "mt-3 rounded-og-md border border-og-border bg-og-surface-1 px-3 py-1.5 text-og-sm text-og-fg hover:bg-og-surface-2 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent",
|
|
587
|
+
children: "Start writing"
|
|
588
|
+
}
|
|
589
|
+
) : null
|
|
590
|
+
] }) }) : /* @__PURE__ */ jsx("div", { className: "relative min-w-full", style: { height: axis.total }, children: mountedIndexes.map((pageIndex) => {
|
|
591
|
+
const page = pages[pageIndex];
|
|
592
|
+
const top = axis.offsets[pageIndex];
|
|
593
|
+
return /* @__PURE__ */ jsx(
|
|
594
|
+
"article",
|
|
595
|
+
{
|
|
596
|
+
"aria-label": `${layout === "paginated" ? "Page" : "Section"} ${pageIndex + 1} of ${pages.length}`,
|
|
597
|
+
"aria-posinset": pageIndex + 1,
|
|
598
|
+
"aria-setsize": pages.length,
|
|
599
|
+
"data-og-document-page": pageIndex + 1,
|
|
600
|
+
"data-og-page-width-pt": page.page.widthPt,
|
|
601
|
+
"data-og-page-height-pt": page.page.heightPt,
|
|
602
|
+
className: cn(
|
|
603
|
+
"absolute overflow-hidden",
|
|
604
|
+
layout === "paginated" ? "left-1/2 -translate-x-1/2 border shadow-og-sm" : "left-0 right-0 border-b border-og-border bg-og-surface-1 text-og-fg"
|
|
605
|
+
),
|
|
606
|
+
style: {
|
|
607
|
+
top,
|
|
608
|
+
color: layout === "paginated" ? "#171717" : void 0,
|
|
609
|
+
backgroundColor: layout === "paginated" ? "#fff" : void 0,
|
|
610
|
+
borderColor: layout === "paginated" ? "#0000001a" : void 0,
|
|
611
|
+
width: layout === "paginated" ? page.page.widthPt * POINT_TO_CSS_PIXEL * page.renderScale : void 0,
|
|
612
|
+
height: page.height
|
|
613
|
+
},
|
|
614
|
+
children: /* @__PURE__ */ jsx(
|
|
615
|
+
MeasuredPageBody,
|
|
616
|
+
{
|
|
617
|
+
pageKey: page.key,
|
|
618
|
+
measureToken: documentRevision,
|
|
619
|
+
onHeight: recordPageHeight,
|
|
620
|
+
className: cn(
|
|
621
|
+
layout === "paginated" ? "absolute left-0 top-0 overflow-hidden" : "mx-auto"
|
|
622
|
+
),
|
|
623
|
+
style: {
|
|
624
|
+
...pageContentStyle(page.page, layout),
|
|
625
|
+
maxWidth: layout === "continuous" ? "68rem" : void 0,
|
|
626
|
+
...layout === "paginated" ? {
|
|
627
|
+
width: page.page.widthPt * POINT_TO_CSS_PIXEL,
|
|
628
|
+
minHeight: page.naturalHeight,
|
|
629
|
+
transform: `scale(${page.renderScale})`,
|
|
630
|
+
transformOrigin: "top left"
|
|
631
|
+
} : {}
|
|
632
|
+
},
|
|
633
|
+
children: page.blocks.map(({ block, listOrdinal }) => /* @__PURE__ */ jsx(
|
|
634
|
+
DocumentBlockView,
|
|
635
|
+
{
|
|
636
|
+
block,
|
|
637
|
+
viewBlockId: blockIdForView(block.id),
|
|
638
|
+
comments: annotations.comments.get(block.id) ?? EMPTY_COMMENTS,
|
|
639
|
+
changes: annotations.changes.get(block.id) ?? EMPTY_CHANGES,
|
|
640
|
+
projectionRevision: localRevision,
|
|
641
|
+
listOrdinal,
|
|
642
|
+
readOnly,
|
|
643
|
+
selected: selectedBlockId === block.id,
|
|
644
|
+
openThreadId,
|
|
645
|
+
registerEditor: (node) => {
|
|
646
|
+
if (node) editorNodes.current.set(block.id, node);
|
|
647
|
+
else editorNodes.current.delete(block.id);
|
|
648
|
+
},
|
|
649
|
+
onThreadToggle: (threadId) => setOpenThreadId((current) => current === threadId ? null : threadId),
|
|
650
|
+
onInput: (paragraph, text) => commitText(paragraph, text),
|
|
651
|
+
onBlur: () => setLocalRevision(document2.revision),
|
|
652
|
+
onSelection: (paragraph) => readDomSelection(paragraph.id),
|
|
653
|
+
onKeyDown: handleEditorKeyDown
|
|
654
|
+
},
|
|
655
|
+
block.id
|
|
656
|
+
))
|
|
657
|
+
}
|
|
658
|
+
)
|
|
659
|
+
},
|
|
660
|
+
page.key
|
|
661
|
+
);
|
|
662
|
+
}) })
|
|
663
|
+
]
|
|
664
|
+
}
|
|
665
|
+
)
|
|
666
|
+
]
|
|
667
|
+
}
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
function DocumentArtifactSurface({
|
|
671
|
+
document: document2,
|
|
672
|
+
title,
|
|
673
|
+
subtitle,
|
|
674
|
+
busy,
|
|
675
|
+
className,
|
|
676
|
+
editorClassName,
|
|
677
|
+
...editorProps
|
|
678
|
+
}) {
|
|
679
|
+
return /* @__PURE__ */ jsx(
|
|
680
|
+
ArtifactSurface,
|
|
681
|
+
{
|
|
682
|
+
modality: "document",
|
|
683
|
+
title,
|
|
684
|
+
subtitle,
|
|
685
|
+
busy,
|
|
686
|
+
className,
|
|
687
|
+
children: /* @__PURE__ */ jsx(
|
|
688
|
+
DocumentEditor,
|
|
689
|
+
{
|
|
690
|
+
...editorProps,
|
|
691
|
+
document: document2,
|
|
692
|
+
ariaLabel: `${title} document`,
|
|
693
|
+
className: editorClassName
|
|
694
|
+
}
|
|
695
|
+
)
|
|
696
|
+
}
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
function DocumentProjectionArtifactSurface({
|
|
700
|
+
projection,
|
|
701
|
+
title,
|
|
702
|
+
subtitle,
|
|
703
|
+
busy,
|
|
704
|
+
className,
|
|
705
|
+
editorClassName,
|
|
706
|
+
...editorProps
|
|
707
|
+
}) {
|
|
708
|
+
return /* @__PURE__ */ jsx(
|
|
709
|
+
ArtifactSurface,
|
|
710
|
+
{
|
|
711
|
+
modality: "document",
|
|
712
|
+
title,
|
|
713
|
+
subtitle,
|
|
714
|
+
busy,
|
|
715
|
+
className,
|
|
716
|
+
children: /* @__PURE__ */ jsx(
|
|
717
|
+
DocumentProjectionEditor,
|
|
718
|
+
{
|
|
719
|
+
...editorProps,
|
|
720
|
+
projection,
|
|
721
|
+
ariaLabel: `${title} document`,
|
|
722
|
+
className: editorClassName
|
|
723
|
+
}
|
|
724
|
+
)
|
|
725
|
+
}
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
function MeasuredPageBody({
|
|
729
|
+
pageKey,
|
|
730
|
+
measureToken,
|
|
731
|
+
onHeight,
|
|
732
|
+
className,
|
|
733
|
+
style,
|
|
734
|
+
children
|
|
735
|
+
}) {
|
|
736
|
+
const nodeRef = useRef(null);
|
|
737
|
+
useLayoutEffect(() => {
|
|
738
|
+
const node = nodeRef.current;
|
|
739
|
+
if (!node) return;
|
|
740
|
+
const report = () => onHeight(pageKey, Math.max(node.clientHeight, node.scrollHeight));
|
|
741
|
+
report();
|
|
742
|
+
if (!globalThis.ResizeObserver) return;
|
|
743
|
+
const observer = new globalThis.ResizeObserver(report);
|
|
744
|
+
observer.observe(node);
|
|
745
|
+
return () => observer.disconnect();
|
|
746
|
+
}, [measureToken, onHeight, pageKey]);
|
|
747
|
+
return /* @__PURE__ */ jsx("div", { ref: nodeRef, className, style, children });
|
|
748
|
+
}
|
|
749
|
+
function DocumentBlockView({
|
|
750
|
+
block,
|
|
751
|
+
viewBlockId,
|
|
752
|
+
comments,
|
|
753
|
+
changes,
|
|
754
|
+
projectionRevision,
|
|
755
|
+
listOrdinal,
|
|
756
|
+
readOnly,
|
|
757
|
+
selected,
|
|
758
|
+
openThreadId,
|
|
759
|
+
registerEditor,
|
|
760
|
+
onThreadToggle,
|
|
761
|
+
onInput,
|
|
762
|
+
onBlur,
|
|
763
|
+
onSelection,
|
|
764
|
+
onKeyDown
|
|
765
|
+
}) {
|
|
766
|
+
if (isDocumentTable(block)) {
|
|
767
|
+
return /* @__PURE__ */ jsx(DocumentTableView, { table: block, viewBlockId });
|
|
768
|
+
}
|
|
769
|
+
const unresolved = comments.filter((thread) => !thread.resolved);
|
|
770
|
+
const commentPanelOpen = comments.some((thread) => thread.id === openThreadId);
|
|
771
|
+
const paragraphStyle = toParagraphStyle(block);
|
|
772
|
+
const segments = annotateRuns(block, unresolved, changes);
|
|
773
|
+
const marker = block.style.list ? block.style.list.kind === "number" ? `${listOrdinal ?? 1}.` : "\u2022" : null;
|
|
774
|
+
return /* @__PURE__ */ jsxs(
|
|
775
|
+
"div",
|
|
776
|
+
{
|
|
777
|
+
"data-og-document-block": viewBlockId,
|
|
778
|
+
"data-og-block-kind": "paragraph",
|
|
779
|
+
className: cn(
|
|
780
|
+
"group relative flex min-h-7 min-w-0 gap-2 rounded-sm px-1 transition-colors",
|
|
781
|
+
selected && "bg-og-accent-soft ring-1 ring-inset ring-og-accent"
|
|
782
|
+
),
|
|
783
|
+
children: [
|
|
784
|
+
marker ? /* @__PURE__ */ jsx(
|
|
785
|
+
"span",
|
|
786
|
+
{
|
|
787
|
+
"aria-hidden": true,
|
|
788
|
+
className: "w-7 shrink-0 select-none text-right opacity-70",
|
|
789
|
+
style: {
|
|
790
|
+
paddingLeft: `${(block.style.list?.level ?? 0) * 12}px`,
|
|
791
|
+
paddingTop: "0.1em"
|
|
792
|
+
},
|
|
793
|
+
children: marker
|
|
794
|
+
}
|
|
795
|
+
) : null,
|
|
796
|
+
/* @__PURE__ */ jsx(
|
|
797
|
+
ParagraphLeaf,
|
|
798
|
+
{
|
|
799
|
+
paragraph: block,
|
|
800
|
+
viewBlockId,
|
|
801
|
+
segments,
|
|
802
|
+
paragraphStyle,
|
|
803
|
+
listOrdinal,
|
|
804
|
+
readOnly,
|
|
805
|
+
registerEditor,
|
|
806
|
+
onInput,
|
|
807
|
+
onBlur,
|
|
808
|
+
onSelection,
|
|
809
|
+
onKeyDown
|
|
810
|
+
},
|
|
811
|
+
`${block.id}:${projectionRevision}`
|
|
812
|
+
),
|
|
813
|
+
comments.length > 0 || changes.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "relative flex w-7 shrink-0 flex-col items-center gap-1 pt-0.5", children: [
|
|
814
|
+
comments.length > 0 ? /* @__PURE__ */ jsx(
|
|
815
|
+
"button",
|
|
816
|
+
{
|
|
817
|
+
type: "button",
|
|
818
|
+
"aria-label": unresolved.length > 0 ? `${unresolved.length} unresolved ${unresolved.length === 1 ? "comment" : "comments"}` : `${comments.length} resolved ${comments.length === 1 ? "comment" : "comments"}`,
|
|
819
|
+
"aria-expanded": commentPanelOpen,
|
|
820
|
+
onClick: () => onThreadToggle((unresolved[0] ?? comments[0]).id),
|
|
821
|
+
className: cn(
|
|
822
|
+
"grid size-6 place-items-center rounded-full focus-visible:outline-hidden focus-visible:ring-2",
|
|
823
|
+
unresolved.length > 0 ? "bg-og-accent-soft text-og-status-waiting hover:bg-og-surface-3 focus-visible:ring-og-status-waiting" : "bg-og-surface-3 text-og-fg-muted hover:text-og-fg focus-visible:ring-og-accent"
|
|
824
|
+
),
|
|
825
|
+
children: /* @__PURE__ */ jsx(MessageSquareTextIcon, { className: "size-3.5" })
|
|
826
|
+
}
|
|
827
|
+
) : null,
|
|
828
|
+
changes.length > 0 ? /* @__PURE__ */ jsx(
|
|
829
|
+
"span",
|
|
830
|
+
{
|
|
831
|
+
role: "img",
|
|
832
|
+
"aria-label": `${changes.length} tracked ${changes.length === 1 ? "change" : "changes"}`,
|
|
833
|
+
className: "mt-1 h-4 w-1 rounded-full bg-og-status-idle"
|
|
834
|
+
}
|
|
835
|
+
) : null,
|
|
836
|
+
commentPanelOpen ? /* @__PURE__ */ jsxs(
|
|
837
|
+
"aside",
|
|
838
|
+
{
|
|
839
|
+
"aria-label": "Comment thread",
|
|
840
|
+
className: "absolute right-0 top-8 z-20 w-64 rounded-og-md border border-og-border bg-og-surface-1 p-3 text-og-xs text-og-fg shadow-og-lg",
|
|
841
|
+
children: [
|
|
842
|
+
/* @__PURE__ */ jsx("div", { className: "mb-2 font-medium", children: comments.length === 1 ? "Comment" : `${comments.length} comments` }),
|
|
843
|
+
/* @__PURE__ */ jsx("ol", { className: "max-h-72 space-y-3 overflow-y-auto", children: comments.map((thread) => /* @__PURE__ */ jsxs("li", { className: "space-y-2", children: [
|
|
844
|
+
/* @__PURE__ */ jsx("div", { className: "text-og-fg-muted", children: thread.resolved ? "Resolved" : "Open" }),
|
|
845
|
+
thread.replies.map((reply, index) => (
|
|
846
|
+
// Reply position is stable within the append-only comment thread.
|
|
847
|
+
// oxlint-disable-next-line react/no-array-index-key
|
|
848
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
849
|
+
/* @__PURE__ */ jsx("div", { className: "text-og-fg-muted", children: reply.author }),
|
|
850
|
+
/* @__PURE__ */ jsx("div", { className: "whitespace-pre-wrap", children: reply.text })
|
|
851
|
+
] }, `${reply.createdAt}:${index}`)
|
|
852
|
+
))
|
|
853
|
+
] }, thread.id)) })
|
|
854
|
+
]
|
|
855
|
+
}
|
|
856
|
+
) : null
|
|
857
|
+
] }) : null
|
|
858
|
+
]
|
|
859
|
+
}
|
|
860
|
+
);
|
|
861
|
+
}
|
|
862
|
+
var ParagraphLeaf = memo(
|
|
863
|
+
function ParagraphLeaf2({
|
|
864
|
+
paragraph,
|
|
865
|
+
viewBlockId,
|
|
866
|
+
segments,
|
|
867
|
+
paragraphStyle,
|
|
868
|
+
listOrdinal,
|
|
869
|
+
readOnly,
|
|
870
|
+
registerEditor,
|
|
871
|
+
onInput,
|
|
872
|
+
onBlur,
|
|
873
|
+
onSelection,
|
|
874
|
+
onKeyDown
|
|
875
|
+
}) {
|
|
876
|
+
const composing = useRef(false);
|
|
877
|
+
const Tag = headingTag(paragraph.style.headingLevel);
|
|
878
|
+
const label = paragraph.style.list?.kind === "number" ? `Numbered list item ${listOrdinal ?? 1}` : paragraph.style.list?.kind === "bullet" ? "Bullet list item" : paragraph.style.headingLevel ? `Heading ${paragraph.style.headingLevel}` : "Paragraph";
|
|
879
|
+
return /* @__PURE__ */ jsx(
|
|
880
|
+
Tag,
|
|
881
|
+
{
|
|
882
|
+
ref: registerEditor,
|
|
883
|
+
contentEditable: !readOnly,
|
|
884
|
+
suppressContentEditableWarning: true,
|
|
885
|
+
tabIndex: readOnly ? 0 : void 0,
|
|
886
|
+
role: !readOnly && !paragraph.style.headingLevel ? "textbox" : void 0,
|
|
887
|
+
"aria-label": label,
|
|
888
|
+
"aria-multiline": !readOnly || void 0,
|
|
889
|
+
"aria-readonly": readOnly,
|
|
890
|
+
spellCheck: !readOnly,
|
|
891
|
+
"data-og-paragraph": viewBlockId,
|
|
892
|
+
onFocus: () => onSelection(paragraph),
|
|
893
|
+
onSelect: () => onSelection(paragraph),
|
|
894
|
+
onKeyUp: () => onSelection(paragraph),
|
|
895
|
+
onMouseUp: () => onSelection(paragraph),
|
|
896
|
+
onCompositionStart: () => {
|
|
897
|
+
composing.current = true;
|
|
898
|
+
},
|
|
899
|
+
onCompositionEnd: (event) => {
|
|
900
|
+
composing.current = false;
|
|
901
|
+
onInput(paragraph, event.currentTarget.textContent ?? "");
|
|
902
|
+
onSelection(paragraph);
|
|
903
|
+
},
|
|
904
|
+
onPaste: (event) => {
|
|
905
|
+
if (readOnly) return;
|
|
906
|
+
event.preventDefault();
|
|
907
|
+
const text = event.clipboardData.getData("text/plain").slice(0, MAX_PLAIN_TEXT_TRANSFER);
|
|
908
|
+
if (!text) return;
|
|
909
|
+
insertPlainText(event.currentTarget, text);
|
|
910
|
+
onInput(paragraph, event.currentTarget.textContent ?? "");
|
|
911
|
+
onSelection(paragraph);
|
|
912
|
+
},
|
|
913
|
+
onDragOver: (event) => {
|
|
914
|
+
if (!readOnly) event.preventDefault();
|
|
915
|
+
},
|
|
916
|
+
onDrop: (event) => {
|
|
917
|
+
if (!readOnly) event.preventDefault();
|
|
918
|
+
},
|
|
919
|
+
onKeyDown: (event) => {
|
|
920
|
+
onKeyDown(event, paragraph);
|
|
921
|
+
if (event.defaultPrevented || readOnly) return;
|
|
922
|
+
if (event.key === "Enter" && !event.metaKey && !event.ctrlKey && !event.altKey) {
|
|
923
|
+
event.preventDefault();
|
|
924
|
+
insertPlainText(event.currentTarget, "\n");
|
|
925
|
+
onInput(paragraph, event.currentTarget.textContent ?? "");
|
|
926
|
+
onSelection(paragraph);
|
|
927
|
+
}
|
|
928
|
+
},
|
|
929
|
+
onInput: (event) => {
|
|
930
|
+
if (!composing.current && !event.nativeEvent.isComposing) {
|
|
931
|
+
onInput(paragraph, event.currentTarget.textContent ?? "");
|
|
932
|
+
}
|
|
933
|
+
},
|
|
934
|
+
onBlur,
|
|
935
|
+
className: cn(
|
|
936
|
+
"min-w-0 flex-1 whitespace-pre-wrap break-words rounded-sm outline-hidden",
|
|
937
|
+
!readOnly && "cursor-text focus-visible:ring-2 focus-visible:ring-og-accent"
|
|
938
|
+
),
|
|
939
|
+
style: paragraphStyle,
|
|
940
|
+
children: segments.map((segment, index) => /* @__PURE__ */ jsx(
|
|
941
|
+
"span",
|
|
942
|
+
{
|
|
943
|
+
"data-og-commented": segment.commented || void 0,
|
|
944
|
+
"data-og-change": segment.changeKind,
|
|
945
|
+
style: segment.style,
|
|
946
|
+
children: segment.text
|
|
947
|
+
},
|
|
948
|
+
`${segment.start}:${index}`
|
|
949
|
+
))
|
|
950
|
+
}
|
|
951
|
+
);
|
|
952
|
+
},
|
|
953
|
+
(previous, next) => {
|
|
954
|
+
if (next.readOnly || previous.viewBlockId !== next.viewBlockId) return false;
|
|
955
|
+
const active = globalThis.document?.activeElement;
|
|
956
|
+
return active instanceof HTMLElement && active.dataset.ogParagraph === next.viewBlockId;
|
|
957
|
+
}
|
|
958
|
+
);
|
|
959
|
+
function DocumentTableView({
|
|
960
|
+
table,
|
|
961
|
+
viewBlockId
|
|
962
|
+
}) {
|
|
963
|
+
const columnWidths = table.style.columnWidthsPt;
|
|
964
|
+
return /* @__PURE__ */ jsx(
|
|
965
|
+
"div",
|
|
966
|
+
{
|
|
967
|
+
"data-og-document-block": viewBlockId,
|
|
968
|
+
"data-og-block-kind": "table",
|
|
969
|
+
className: "overflow-x-auto py-2",
|
|
970
|
+
children: /* @__PURE__ */ jsxs(
|
|
971
|
+
"table",
|
|
972
|
+
{
|
|
973
|
+
className: "w-full border-collapse text-left",
|
|
974
|
+
style: {
|
|
975
|
+
fontSize: "0.92em",
|
|
976
|
+
width: table.style.widthPt ? `${table.style.widthPt}pt` : void 0
|
|
977
|
+
},
|
|
978
|
+
children: [
|
|
979
|
+
columnWidths ? /* @__PURE__ */ jsx("colgroup", { children: columnWidths.map((width, index) => (
|
|
980
|
+
// Column position is its identity in the document table model.
|
|
981
|
+
// oxlint-disable-next-line react/no-array-index-key
|
|
982
|
+
/* @__PURE__ */ jsx("col", { style: { width: `${width}pt` } }, index)
|
|
983
|
+
)) }) : null,
|
|
984
|
+
/* @__PURE__ */ jsx("tbody", { children: table.rows.map((row, rowIndex) => (
|
|
985
|
+
// Row position is its identity in the document table model.
|
|
986
|
+
// oxlint-disable-next-line react/no-array-index-key
|
|
987
|
+
/* @__PURE__ */ jsx("tr", { children: row.map((cell, columnIndex) => {
|
|
988
|
+
const header = rowIndex < (table.style.headerRows ?? 0);
|
|
989
|
+
const Cell = header ? "th" : "td";
|
|
990
|
+
return /* @__PURE__ */ jsx(
|
|
991
|
+
Cell,
|
|
992
|
+
{
|
|
993
|
+
scope: header ? "col" : void 0,
|
|
994
|
+
className: cn("border align-middle", header && "font-semibold"),
|
|
995
|
+
style: {
|
|
996
|
+
borderColor: table.style.borderColor ?? "#d1d5db",
|
|
997
|
+
background: header ? table.style.headerFill ?? "#f3f4f6" : void 0,
|
|
998
|
+
padding: `${table.style.cellPaddingPt ?? 6}pt`
|
|
999
|
+
},
|
|
1000
|
+
children: cell.map((run, index) => (
|
|
1001
|
+
// Run position is stable within the immutable cell projection.
|
|
1002
|
+
// oxlint-disable-next-line react/no-array-index-key
|
|
1003
|
+
/* @__PURE__ */ jsx("span", { style: toRunStyle(run.style), children: run.text }, index)
|
|
1004
|
+
))
|
|
1005
|
+
},
|
|
1006
|
+
columnIndex
|
|
1007
|
+
);
|
|
1008
|
+
}) }, rowIndex)
|
|
1009
|
+
)) })
|
|
1010
|
+
]
|
|
1011
|
+
}
|
|
1012
|
+
)
|
|
1013
|
+
}
|
|
1014
|
+
);
|
|
1015
|
+
}
|
|
1016
|
+
function ToolbarButton({
|
|
1017
|
+
label,
|
|
1018
|
+
shortcut,
|
|
1019
|
+
pressed,
|
|
1020
|
+
disabled,
|
|
1021
|
+
onPress,
|
|
1022
|
+
children
|
|
1023
|
+
}) {
|
|
1024
|
+
return /* @__PURE__ */ jsx(
|
|
1025
|
+
"button",
|
|
1026
|
+
{
|
|
1027
|
+
type: "button",
|
|
1028
|
+
"aria-label": label,
|
|
1029
|
+
"aria-pressed": pressed,
|
|
1030
|
+
title: shortcut ? `${label} (${shortcut})` : label,
|
|
1031
|
+
disabled,
|
|
1032
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
1033
|
+
onClick: onPress,
|
|
1034
|
+
className: cn(
|
|
1035
|
+
"grid size-7 place-items-center rounded-og-sm text-og-fg-muted outline-hidden transition-colors hover:bg-og-surface-3 hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent disabled:pointer-events-none disabled:opacity-40",
|
|
1036
|
+
pressed && "bg-og-surface-3 text-og-fg"
|
|
1037
|
+
),
|
|
1038
|
+
children
|
|
1039
|
+
}
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
function projectPages(document2, layout) {
|
|
1043
|
+
const pages = [];
|
|
1044
|
+
const sections = document2.sections.items;
|
|
1045
|
+
let sectionIndex = 0;
|
|
1046
|
+
let pageGeometry = sections[0]?.page ?? document2.page;
|
|
1047
|
+
let blocks = [];
|
|
1048
|
+
let used = 0;
|
|
1049
|
+
let pageIndex = 0;
|
|
1050
|
+
let numberOrdinal = 0;
|
|
1051
|
+
const flush = (force = false) => {
|
|
1052
|
+
if (blocks.length === 0 && !force) return;
|
|
1053
|
+
const physicalPageHeight = pageGeometry.heightPt * POINT_TO_CSS_PIXEL;
|
|
1054
|
+
const pageVerticalChrome = (pageGeometry.marginTopPt + pageGeometry.marginBottomPt) * POINT_TO_CSS_PIXEL;
|
|
1055
|
+
pages.push({
|
|
1056
|
+
key: `${layout}:${sections[sectionIndex]?.id ?? "default"}:${pageIndex++}:${blocks[0]?.block.id ?? "blank"}`,
|
|
1057
|
+
blocks,
|
|
1058
|
+
height: layout === "paginated" ? Math.max(physicalPageHeight, used + pageVerticalChrome) : Math.max(1, used + 48),
|
|
1059
|
+
minimumHeight: layout === "paginated" ? physicalPageHeight : 1,
|
|
1060
|
+
page: { ...pageGeometry }
|
|
1061
|
+
});
|
|
1062
|
+
blocks = [];
|
|
1063
|
+
used = 0;
|
|
1064
|
+
};
|
|
1065
|
+
for (let blockIndex = 0; blockIndex < document2.blocks.items.length; blockIndex += 1) {
|
|
1066
|
+
while (sectionIndex + 1 < sections.length && sections[sectionIndex + 1].startBlockIndex <= blockIndex) {
|
|
1067
|
+
flush();
|
|
1068
|
+
sectionIndex += 1;
|
|
1069
|
+
pageGeometry = sections[sectionIndex].page;
|
|
1070
|
+
}
|
|
1071
|
+
const block = document2.blocks.items[blockIndex];
|
|
1072
|
+
if (isDocumentPageBreak(block)) {
|
|
1073
|
+
flush(true);
|
|
1074
|
+
numberOrdinal = 0;
|
|
1075
|
+
continue;
|
|
1076
|
+
}
|
|
1077
|
+
if (isDocumentParagraph(block) && block.style.pageBreakBefore) flush();
|
|
1078
|
+
const height = estimateBlockHeight(block);
|
|
1079
|
+
const physicalPageHeight = pageGeometry.heightPt * POINT_TO_CSS_PIXEL;
|
|
1080
|
+
const usableHeight = Math.max(
|
|
1081
|
+
120,
|
|
1082
|
+
physicalPageHeight - (pageGeometry.marginTopPt + pageGeometry.marginBottomPt) * POINT_TO_CSS_PIXEL
|
|
1083
|
+
);
|
|
1084
|
+
const targetHeight = layout === "paginated" ? usableHeight : Math.max(560, usableHeight);
|
|
1085
|
+
if (blocks.length > 0 && used + height > targetHeight) flush();
|
|
1086
|
+
if (isDocumentParagraph(block) && block.style.list?.kind === "number") {
|
|
1087
|
+
numberOrdinal += 1;
|
|
1088
|
+
} else {
|
|
1089
|
+
numberOrdinal = 0;
|
|
1090
|
+
}
|
|
1091
|
+
blocks.push({
|
|
1092
|
+
block,
|
|
1093
|
+
height,
|
|
1094
|
+
listOrdinal: isDocumentParagraph(block) && block.style.list?.kind === "number" ? numberOrdinal : void 0
|
|
1095
|
+
});
|
|
1096
|
+
used += height;
|
|
1097
|
+
}
|
|
1098
|
+
flush();
|
|
1099
|
+
return pages;
|
|
1100
|
+
}
|
|
1101
|
+
function indexAnnotations(document2) {
|
|
1102
|
+
const comments = /* @__PURE__ */ new Map();
|
|
1103
|
+
const changes = /* @__PURE__ */ new Map();
|
|
1104
|
+
for (const thread of document2.comments.items) {
|
|
1105
|
+
const items = comments.get(thread.blockId);
|
|
1106
|
+
if (items) items.push(thread);
|
|
1107
|
+
else comments.set(thread.blockId, [thread]);
|
|
1108
|
+
}
|
|
1109
|
+
for (const change of document2.changes.items) {
|
|
1110
|
+
const items = changes.get(change.blockId);
|
|
1111
|
+
if (items) items.push(change);
|
|
1112
|
+
else changes.set(change.blockId, [change]);
|
|
1113
|
+
}
|
|
1114
|
+
return { comments, changes };
|
|
1115
|
+
}
|
|
1116
|
+
function estimateBlockHeight(block) {
|
|
1117
|
+
if (isDocumentTable(block)) {
|
|
1118
|
+
return Math.max(48, block.rows.length * 38 + 16);
|
|
1119
|
+
}
|
|
1120
|
+
const fontSize = block.runs.reduce(
|
|
1121
|
+
(largest, run) => Math.max(largest, run.style.fontSizePt ?? 11),
|
|
1122
|
+
block.style.headingLevel ? Math.max(14, 26 - block.style.headingLevel * 2) : 11
|
|
1123
|
+
);
|
|
1124
|
+
const lineHeight = fontSize * POINT_TO_CSS_PIXEL * (block.style.lineHeight ?? 1.35);
|
|
1125
|
+
const lines = Math.max(
|
|
1126
|
+
1,
|
|
1127
|
+
block.text.split("\n").reduce((total, line) => total + Math.max(1, Math.ceil(line.length / 72)), 0)
|
|
1128
|
+
);
|
|
1129
|
+
return Math.max(
|
|
1130
|
+
28,
|
|
1131
|
+
lines * lineHeight + ((block.style.spaceBeforePt ?? 2) + (block.style.spaceAfterPt ?? 6)) * POINT_TO_CSS_PIXEL
|
|
1132
|
+
);
|
|
1133
|
+
}
|
|
1134
|
+
function buildPageAxis(pages, gap) {
|
|
1135
|
+
const offsets = new Float64Array(pages.length + 1);
|
|
1136
|
+
for (let index = 0; index < pages.length; index += 1) {
|
|
1137
|
+
offsets[index + 1] = offsets[index] + pages[index].height + gap;
|
|
1138
|
+
}
|
|
1139
|
+
return { offsets, total: Math.max(1, (offsets[pages.length] ?? 0) - gap) };
|
|
1140
|
+
}
|
|
1141
|
+
function pageAtOffset(axis, count, offset) {
|
|
1142
|
+
if (count <= 1) return 0;
|
|
1143
|
+
let low = 0;
|
|
1144
|
+
let high = count;
|
|
1145
|
+
while (low < high) {
|
|
1146
|
+
const middle = low + high >>> 1;
|
|
1147
|
+
if (axis.offsets[middle + 1] <= offset) low = middle + 1;
|
|
1148
|
+
else high = middle;
|
|
1149
|
+
}
|
|
1150
|
+
return Math.max(0, Math.min(count - 1, low));
|
|
1151
|
+
}
|
|
1152
|
+
function visiblePageIndexes(axis, count, scrollTop, viewportHeight, overscan, required) {
|
|
1153
|
+
if (count === 0) return [];
|
|
1154
|
+
const start = Math.max(0, pageAtOffset(axis, count, scrollTop) - overscan);
|
|
1155
|
+
const end = Math.min(count, pageAtOffset(axis, count, scrollTop + viewportHeight) + overscan + 1);
|
|
1156
|
+
const indexes = Array.from({ length: end - start }, (_, index) => start + index);
|
|
1157
|
+
if (required >= 0 && (required < start || required >= end)) indexes.push(required);
|
|
1158
|
+
return indexes.sort((left, right) => left - right);
|
|
1159
|
+
}
|
|
1160
|
+
function responsiveDocumentPageScale(page, viewportWidth) {
|
|
1161
|
+
if (viewportWidth <= 0) return 1;
|
|
1162
|
+
const naturalWidth = page.widthPt * POINT_TO_CSS_PIXEL;
|
|
1163
|
+
return Math.min(1, Math.max(0.1, (viewportWidth - 32) / naturalWidth));
|
|
1164
|
+
}
|
|
1165
|
+
function pageContentStyle(page, layout) {
|
|
1166
|
+
if (layout === "continuous") {
|
|
1167
|
+
return {
|
|
1168
|
+
padding: "24px clamp(24px, 8vw, 96px)"
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
return {
|
|
1172
|
+
paddingTop: `${page.marginTopPt * POINT_TO_CSS_PIXEL}px`,
|
|
1173
|
+
paddingRight: `${page.marginRightPt * POINT_TO_CSS_PIXEL}px`,
|
|
1174
|
+
paddingBottom: `${page.marginBottomPt * POINT_TO_CSS_PIXEL}px`,
|
|
1175
|
+
paddingLeft: `${page.marginLeftPt * POINT_TO_CSS_PIXEL}px`
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
function headingTag(level) {
|
|
1179
|
+
if (!level) return "p";
|
|
1180
|
+
return `h${Math.max(1, Math.min(6, level))}`;
|
|
1181
|
+
}
|
|
1182
|
+
function toParagraphStyle(paragraph) {
|
|
1183
|
+
const firstRun = paragraph.runs[0];
|
|
1184
|
+
return {
|
|
1185
|
+
textAlign: paragraph.style.alignment,
|
|
1186
|
+
marginTop: `${paragraph.style.spaceBeforePt ?? 0}pt`,
|
|
1187
|
+
marginBottom: `${paragraph.style.spaceAfterPt ?? 0}pt`,
|
|
1188
|
+
lineHeight: paragraph.style.lineHeight ?? 1.35,
|
|
1189
|
+
breakBefore: paragraph.style.pageBreakBefore ? "page" : void 0,
|
|
1190
|
+
fontFamily: firstRun?.style.fontFamily,
|
|
1191
|
+
fontSize: firstRun?.style.fontSizePt ? `${firstRun.style.fontSizePt}pt` : void 0,
|
|
1192
|
+
fontWeight: paragraph.style.headingLevel ? 650 : void 0
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
function toRunStyle(style) {
|
|
1196
|
+
return {
|
|
1197
|
+
fontFamily: style.fontFamily,
|
|
1198
|
+
fontSize: style.fontSizePt ? `${style.fontSizePt}pt` : void 0,
|
|
1199
|
+
color: style.color,
|
|
1200
|
+
fontWeight: style.bold ? 700 : void 0,
|
|
1201
|
+
fontStyle: style.italic ? "italic" : void 0,
|
|
1202
|
+
textDecoration: [style.underline ? "underline" : "", style.strike ? "line-through" : ""].filter(Boolean).join(" ") || void 0
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1205
|
+
function annotateRuns(paragraph, comments, changes) {
|
|
1206
|
+
const segments = [];
|
|
1207
|
+
let runStart = 0;
|
|
1208
|
+
for (const run of paragraph.runs) {
|
|
1209
|
+
const runEnd = runStart + run.text.length;
|
|
1210
|
+
const boundaries = /* @__PURE__ */ new Set([runStart, runEnd]);
|
|
1211
|
+
for (const annotation of [...comments, ...changes]) {
|
|
1212
|
+
if (annotation.start > runStart && annotation.start < runEnd)
|
|
1213
|
+
boundaries.add(annotation.start);
|
|
1214
|
+
if (annotation.end > runStart && annotation.end < runEnd) boundaries.add(annotation.end);
|
|
1215
|
+
}
|
|
1216
|
+
const ordered = [...boundaries].sort((left, right) => left - right);
|
|
1217
|
+
for (let index = 0; index < ordered.length - 1; index += 1) {
|
|
1218
|
+
const start = ordered[index];
|
|
1219
|
+
const end = ordered[index + 1];
|
|
1220
|
+
const commented = comments.some((comment) => comment.start < end && comment.end > start);
|
|
1221
|
+
const change = changes.find((item) => item.start < end && item.end > start);
|
|
1222
|
+
const baseStyle = toRunStyle(run.style);
|
|
1223
|
+
segments.push({
|
|
1224
|
+
text: run.text.slice(start - runStart, end - runStart),
|
|
1225
|
+
start,
|
|
1226
|
+
commented,
|
|
1227
|
+
changeKind: change?.kind,
|
|
1228
|
+
style: {
|
|
1229
|
+
...baseStyle,
|
|
1230
|
+
backgroundColor: commented ? "color-mix(in oklch, var(--og-color-status-waiting) 18%, transparent)" : void 0,
|
|
1231
|
+
textDecoration: change?.kind === "delete" ? "line-through" : change?.kind === "insert" ? "underline" : baseStyle.textDecoration,
|
|
1232
|
+
textDecorationColor: change?.kind === "delete" ? "var(--og-color-status-failed)" : change?.kind === "insert" ? "var(--og-color-status-idle)" : void 0
|
|
1233
|
+
}
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
runStart = runEnd;
|
|
1237
|
+
}
|
|
1238
|
+
if (segments.length === 0) {
|
|
1239
|
+
segments.push({ text: "", start: 0, style: {}, commented: false });
|
|
1240
|
+
}
|
|
1241
|
+
return segments;
|
|
1242
|
+
}
|
|
1243
|
+
function selectionOffsets(root) {
|
|
1244
|
+
if (!root) return null;
|
|
1245
|
+
const selection = globalThis.getSelection?.();
|
|
1246
|
+
if (!selection || selection.rangeCount === 0) return null;
|
|
1247
|
+
const range = selection.getRangeAt(0);
|
|
1248
|
+
if (!root.contains(range.startContainer) || !root.contains(range.endContainer)) return null;
|
|
1249
|
+
const beforeStart = range.cloneRange();
|
|
1250
|
+
beforeStart.selectNodeContents(root);
|
|
1251
|
+
beforeStart.setEnd(range.startContainer, range.startOffset);
|
|
1252
|
+
const beforeEnd = range.cloneRange();
|
|
1253
|
+
beforeEnd.selectNodeContents(root);
|
|
1254
|
+
beforeEnd.setEnd(range.endContainer, range.endOffset);
|
|
1255
|
+
return {
|
|
1256
|
+
start: beforeStart.toString().length,
|
|
1257
|
+
end: beforeEnd.toString().length
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
function insertPlainText(root, text) {
|
|
1261
|
+
const selection = globalThis.getSelection?.();
|
|
1262
|
+
if (!selection || selection.rangeCount === 0) return;
|
|
1263
|
+
const range = selection.getRangeAt(0);
|
|
1264
|
+
if (!root.contains(range.startContainer) || !root.contains(range.endContainer)) return;
|
|
1265
|
+
range.deleteContents();
|
|
1266
|
+
const node = globalThis.document.createTextNode(text);
|
|
1267
|
+
range.insertNode(node);
|
|
1268
|
+
range.setStartAfter(node);
|
|
1269
|
+
range.collapse(true);
|
|
1270
|
+
selection.removeAllRanges();
|
|
1271
|
+
selection.addRange(range);
|
|
1272
|
+
}
|
|
1273
|
+
function restoreSelection(root, requestedStart, requestedEnd) {
|
|
1274
|
+
if (!root || !globalThis.getSelection) return;
|
|
1275
|
+
const textLength = root.textContent?.length ?? 0;
|
|
1276
|
+
const start = Math.max(0, Math.min(requestedStart, textLength));
|
|
1277
|
+
const end = Math.max(start, Math.min(requestedEnd, textLength));
|
|
1278
|
+
const startPoint = textPointAt(root, start);
|
|
1279
|
+
const endPoint = textPointAt(root, end);
|
|
1280
|
+
if (!startPoint || !endPoint) return;
|
|
1281
|
+
const range = document.createRange();
|
|
1282
|
+
range.setStart(startPoint.node, startPoint.offset);
|
|
1283
|
+
range.setEnd(endPoint.node, endPoint.offset);
|
|
1284
|
+
const domSelection = globalThis.getSelection();
|
|
1285
|
+
domSelection?.removeAllRanges();
|
|
1286
|
+
domSelection?.addRange(range);
|
|
1287
|
+
}
|
|
1288
|
+
function textPointAt(root, target) {
|
|
1289
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
|
|
1290
|
+
let remaining = target;
|
|
1291
|
+
let node = walker.nextNode();
|
|
1292
|
+
while (node) {
|
|
1293
|
+
const length = node.textContent?.length ?? 0;
|
|
1294
|
+
if (remaining <= length) return { node, offset: remaining };
|
|
1295
|
+
remaining -= length;
|
|
1296
|
+
node = walker.nextNode();
|
|
1297
|
+
}
|
|
1298
|
+
return { node: root, offset: root.childNodes.length };
|
|
1299
|
+
}
|
|
1300
|
+
function toggledTextStyle(paragraph, start, end, property) {
|
|
1301
|
+
let allEnabled = end > start;
|
|
1302
|
+
let offset = 0;
|
|
1303
|
+
for (const run of paragraph.runs) {
|
|
1304
|
+
const runEnd = offset + run.text.length;
|
|
1305
|
+
if (runEnd > start && offset < end && run.style[property] !== true) {
|
|
1306
|
+
allEnabled = false;
|
|
1307
|
+
break;
|
|
1308
|
+
}
|
|
1309
|
+
offset = runEnd;
|
|
1310
|
+
}
|
|
1311
|
+
return { [property]: !allEnabled };
|
|
1312
|
+
}
|
|
1313
|
+
function documentViewFromProjection(projection) {
|
|
1314
|
+
if (projection.blocks.length > MAX_PROJECTION_BLOCKS) {
|
|
1315
|
+
throw new Error(`Document projection exceeds ${MAX_PROJECTION_BLOCKS} blocks`);
|
|
1316
|
+
}
|
|
1317
|
+
if ((projection.comments?.length ?? 0) > MAX_PROJECTION_ANNOTATIONS || (projection.changes?.length ?? 0) > MAX_PROJECTION_ANNOTATIONS) {
|
|
1318
|
+
throw new Error(`Document projection exceeds ${MAX_PROJECTION_ANNOTATIONS} annotations`);
|
|
1319
|
+
}
|
|
1320
|
+
const sections = [...projection.sections ?? []].sort(
|
|
1321
|
+
(left, right) => left.startBlockIndex - right.startBlockIndex
|
|
1322
|
+
);
|
|
1323
|
+
if (sections.some(
|
|
1324
|
+
(section, index) => section.startBlockIndex < 0 || index > 0 && section.startBlockIndex <= sections[index - 1].startBlockIndex
|
|
1325
|
+
)) {
|
|
1326
|
+
throw new Error("Document projection sections must have unique increasing block indexes");
|
|
1327
|
+
}
|
|
1328
|
+
const firstPage = sections[0]?.startBlockIndex === 0 ? sections[0].page : projection.page;
|
|
1329
|
+
validateProjectionPage(firstPage);
|
|
1330
|
+
const externalIdByInternal = /* @__PURE__ */ new Map();
|
|
1331
|
+
const paragraphByExternal = /* @__PURE__ */ new Map();
|
|
1332
|
+
const objects = /* @__PURE__ */ new Map();
|
|
1333
|
+
const blocks = [];
|
|
1334
|
+
const documentSections = [];
|
|
1335
|
+
const comments = [];
|
|
1336
|
+
const changes = [];
|
|
1337
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
1338
|
+
const remember = (internalId, externalId, value) => {
|
|
1339
|
+
if (!externalId || seenIds.has(externalId)) {
|
|
1340
|
+
throw new Error(`Document projection contains an invalid or duplicate id: ${externalId}`);
|
|
1341
|
+
}
|
|
1342
|
+
seenIds.add(externalId);
|
|
1343
|
+
externalIdByInternal.set(internalId, externalId);
|
|
1344
|
+
objects.set(internalId, value);
|
|
1345
|
+
};
|
|
1346
|
+
let revision = 0;
|
|
1347
|
+
let localParagraphId = 0;
|
|
1348
|
+
const changed = () => {
|
|
1349
|
+
revision += 1;
|
|
1350
|
+
};
|
|
1351
|
+
const document2 = {
|
|
1352
|
+
get revision() {
|
|
1353
|
+
return revision;
|
|
1354
|
+
},
|
|
1355
|
+
page: copyDocumentPage(firstPage),
|
|
1356
|
+
blocks: {
|
|
1357
|
+
items: blocks,
|
|
1358
|
+
addParagraph(text = "") {
|
|
1359
|
+
let id;
|
|
1360
|
+
do {
|
|
1361
|
+
localParagraphId += 1;
|
|
1362
|
+
id = `projection-paragraph-${localParagraphId}`;
|
|
1363
|
+
} while (objects.has(id));
|
|
1364
|
+
const paragraph = new ProjectionParagraphView(id, [{ text, style: {} }], {}, changed);
|
|
1365
|
+
blocks.push(paragraph);
|
|
1366
|
+
objects.set(id, paragraph);
|
|
1367
|
+
changed();
|
|
1368
|
+
return paragraph;
|
|
1369
|
+
}
|
|
1370
|
+
},
|
|
1371
|
+
sections: { items: documentSections },
|
|
1372
|
+
comments: { items: comments },
|
|
1373
|
+
changes: { items: changes },
|
|
1374
|
+
resolve(id) {
|
|
1375
|
+
const value = objects.get(id);
|
|
1376
|
+
if (!value) throw new Error(`Unknown document object id: ${id}`);
|
|
1377
|
+
return value;
|
|
1378
|
+
}
|
|
1379
|
+
};
|
|
1380
|
+
let sectionIndex = sections[0]?.startBlockIndex === 0 ? 1 : 0;
|
|
1381
|
+
if (sections[0]?.startBlockIndex === 0) {
|
|
1382
|
+
const projectedSection = sections[0];
|
|
1383
|
+
validateProjectionPage(projectedSection.page);
|
|
1384
|
+
const section = {
|
|
1385
|
+
...projectedSection,
|
|
1386
|
+
page: copyDocumentPage(projectedSection.page)
|
|
1387
|
+
};
|
|
1388
|
+
documentSections.push(section);
|
|
1389
|
+
remember(section.id, projectedSection.id, section);
|
|
1390
|
+
} else {
|
|
1391
|
+
documentSections.push({
|
|
1392
|
+
id: "projection-default-section",
|
|
1393
|
+
startBlockIndex: 0,
|
|
1394
|
+
page: copyDocumentPage(firstPage)
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
for (let blockIndex = 0; blockIndex < projection.blocks.length; blockIndex += 1) {
|
|
1398
|
+
while (sections[sectionIndex]?.startBlockIndex === blockIndex) {
|
|
1399
|
+
const projectedSection = sections[sectionIndex];
|
|
1400
|
+
validateProjectionPage(projectedSection.page);
|
|
1401
|
+
const section = {
|
|
1402
|
+
...projectedSection,
|
|
1403
|
+
page: copyDocumentPage(projectedSection.page)
|
|
1404
|
+
};
|
|
1405
|
+
documentSections.push(section);
|
|
1406
|
+
remember(section.id, projectedSection.id, section);
|
|
1407
|
+
sectionIndex += 1;
|
|
1408
|
+
}
|
|
1409
|
+
const projectedBlock = projection.blocks[blockIndex];
|
|
1410
|
+
if (projectedBlock.kind === "paragraph") {
|
|
1411
|
+
const paragraph = new ProjectionParagraphView(
|
|
1412
|
+
projectedBlock.id,
|
|
1413
|
+
projectedBlock.runs.length > 0 ? projectedBlock.runs.map((run) => ({
|
|
1414
|
+
text: run.text.slice(0, MAX_PROJECTION_TEXT),
|
|
1415
|
+
style: { ...run.style ?? {} }
|
|
1416
|
+
})) : [{ text: "", style: {} }],
|
|
1417
|
+
{ ...projectedBlock.style ?? {} },
|
|
1418
|
+
changed
|
|
1419
|
+
);
|
|
1420
|
+
blocks.push(paragraph);
|
|
1421
|
+
remember(paragraph.id, projectedBlock.id, paragraph);
|
|
1422
|
+
paragraphByExternal.set(projectedBlock.id, paragraph);
|
|
1423
|
+
} else if (projectedBlock.kind === "table") {
|
|
1424
|
+
const columns = projectedBlock.rows.reduce(
|
|
1425
|
+
(maximum, row) => Math.max(maximum, row.length),
|
|
1426
|
+
0
|
|
1427
|
+
);
|
|
1428
|
+
if (projectedBlock.rows.length === 0 || columns === 0 || projectedBlock.rows.some((row) => row.length !== columns)) {
|
|
1429
|
+
throw new Error(`Document table ${projectedBlock.id} must be a non-empty rectangle`);
|
|
1430
|
+
}
|
|
1431
|
+
if (projectedBlock.rows.length * columns > MAX_PROJECTION_TABLE_CELLS) {
|
|
1432
|
+
throw new Error(`Document table ${projectedBlock.id} exceeds the projected cell limit`);
|
|
1433
|
+
}
|
|
1434
|
+
const table = {
|
|
1435
|
+
kind: "table",
|
|
1436
|
+
id: projectedBlock.id,
|
|
1437
|
+
rows: projectedBlock.rows.map(
|
|
1438
|
+
(row) => row.map(
|
|
1439
|
+
(cell) => cell.length > 0 ? cell.map((run) => ({
|
|
1440
|
+
text: run.text.slice(0, MAX_PROJECTION_TEXT),
|
|
1441
|
+
style: { ...run.style ?? {} }
|
|
1442
|
+
})) : [{ text: "", style: {} }]
|
|
1443
|
+
)
|
|
1444
|
+
),
|
|
1445
|
+
style: { ...projectedBlock.style ?? {} }
|
|
1446
|
+
};
|
|
1447
|
+
blocks.push(table);
|
|
1448
|
+
remember(table.id, projectedBlock.id, table);
|
|
1449
|
+
} else {
|
|
1450
|
+
const pageBreak = {
|
|
1451
|
+
kind: "pageBreak",
|
|
1452
|
+
id: projectedBlock.id
|
|
1453
|
+
};
|
|
1454
|
+
blocks.push(pageBreak);
|
|
1455
|
+
remember(pageBreak.id, projectedBlock.id, pageBreak);
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
while (sections[sectionIndex]?.startBlockIndex === projection.blocks.length) {
|
|
1459
|
+
const projectedSection = sections[sectionIndex];
|
|
1460
|
+
validateProjectionPage(projectedSection.page);
|
|
1461
|
+
const section = {
|
|
1462
|
+
...projectedSection,
|
|
1463
|
+
page: copyDocumentPage(projectedSection.page)
|
|
1464
|
+
};
|
|
1465
|
+
documentSections.push(section);
|
|
1466
|
+
remember(section.id, projectedSection.id, section);
|
|
1467
|
+
sectionIndex += 1;
|
|
1468
|
+
}
|
|
1469
|
+
if (sectionIndex < sections.length) {
|
|
1470
|
+
throw new Error("Document projection section starts beyond the projected blocks");
|
|
1471
|
+
}
|
|
1472
|
+
for (const projected of projection.comments ?? []) {
|
|
1473
|
+
const paragraph = paragraphByExternal.get(projected.blockId);
|
|
1474
|
+
const first = projected.replies[0];
|
|
1475
|
+
if (!paragraph || !first) continue;
|
|
1476
|
+
if (!Number.isSafeInteger(projected.start) || !Number.isSafeInteger(projected.end) || projected.start < 0 || projected.end < projected.start || projected.end > paragraph.text.length) {
|
|
1477
|
+
throw new Error(`Document comment ${projected.id} has an invalid range`);
|
|
1478
|
+
}
|
|
1479
|
+
const thread = {
|
|
1480
|
+
...projected,
|
|
1481
|
+
replies: projected.replies.map((reply) => ({ ...reply }))
|
|
1482
|
+
};
|
|
1483
|
+
comments.push(thread);
|
|
1484
|
+
remember(thread.id, projected.id, thread);
|
|
1485
|
+
}
|
|
1486
|
+
for (const projected of projection.changes ?? []) {
|
|
1487
|
+
const paragraph = paragraphByExternal.get(projected.blockId);
|
|
1488
|
+
if (!paragraph) continue;
|
|
1489
|
+
if (!Number.isSafeInteger(projected.start) || !Number.isSafeInteger(projected.end) || projected.start < 0 || projected.end <= projected.start || projected.end > paragraph.text.length) {
|
|
1490
|
+
throw new Error(`Document tracked change ${projected.id} has an invalid range`);
|
|
1491
|
+
}
|
|
1492
|
+
const change = { ...projected };
|
|
1493
|
+
changes.push(change);
|
|
1494
|
+
remember(change.id, projected.id, change);
|
|
1495
|
+
}
|
|
1496
|
+
return { document: document2, externalIdByInternal };
|
|
1497
|
+
}
|
|
1498
|
+
function copyDocumentPage(page) {
|
|
1499
|
+
return {
|
|
1500
|
+
widthPt: page.widthPt,
|
|
1501
|
+
heightPt: page.heightPt,
|
|
1502
|
+
marginTopPt: page.marginTopPt,
|
|
1503
|
+
marginRightPt: page.marginRightPt,
|
|
1504
|
+
marginBottomPt: page.marginBottomPt,
|
|
1505
|
+
marginLeftPt: page.marginLeftPt,
|
|
1506
|
+
...page.headerPt === void 0 ? {} : { headerPt: page.headerPt },
|
|
1507
|
+
...page.footerPt === void 0 ? {} : { footerPt: page.footerPt },
|
|
1508
|
+
...page.gutterPt === void 0 ? {} : { gutterPt: page.gutterPt }
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1511
|
+
var ProjectionParagraphView = class {
|
|
1512
|
+
constructor(id, runs, style, changed) {
|
|
1513
|
+
this.id = id;
|
|
1514
|
+
this.changed = changed;
|
|
1515
|
+
this.runStorage = normalizeProjectionRuns(runs);
|
|
1516
|
+
this.style = { ...style, ...style.list ? { list: { ...style.list } } : {} };
|
|
1517
|
+
}
|
|
1518
|
+
kind = "paragraph";
|
|
1519
|
+
runStorage;
|
|
1520
|
+
style;
|
|
1521
|
+
get runs() {
|
|
1522
|
+
return this.runStorage;
|
|
1523
|
+
}
|
|
1524
|
+
get text() {
|
|
1525
|
+
return this.runStorage.map((run) => run.text).join("");
|
|
1526
|
+
}
|
|
1527
|
+
set text(value) {
|
|
1528
|
+
const current = this.text;
|
|
1529
|
+
if (current === value) return;
|
|
1530
|
+
let start = 0;
|
|
1531
|
+
while (start < current.length && start < value.length && current[start] === value[start]) {
|
|
1532
|
+
start += 1;
|
|
1533
|
+
}
|
|
1534
|
+
let currentEnd = current.length;
|
|
1535
|
+
let valueEnd = value.length;
|
|
1536
|
+
while (currentEnd > start && valueEnd > start && current[currentEnd - 1] === value[valueEnd - 1]) {
|
|
1537
|
+
currentEnd -= 1;
|
|
1538
|
+
valueEnd -= 1;
|
|
1539
|
+
}
|
|
1540
|
+
const inheritedStyle = projectionStyleAt(this.runStorage, start);
|
|
1541
|
+
this.runStorage = normalizeProjectionRuns([
|
|
1542
|
+
...sliceProjectionRuns(this.runStorage, 0, start),
|
|
1543
|
+
...valueEnd > start ? [{ text: value.slice(start, valueEnd), style: inheritedStyle }] : [],
|
|
1544
|
+
...sliceProjectionRuns(this.runStorage, currentEnd, current.length)
|
|
1545
|
+
]);
|
|
1546
|
+
this.changed();
|
|
1547
|
+
}
|
|
1548
|
+
format({
|
|
1549
|
+
start,
|
|
1550
|
+
end,
|
|
1551
|
+
style
|
|
1552
|
+
}) {
|
|
1553
|
+
const textLength = this.text.length;
|
|
1554
|
+
if (start < 0 || end <= start || end > textLength || Object.keys(style).length === 0) return;
|
|
1555
|
+
this.runStorage = normalizeProjectionRuns([
|
|
1556
|
+
...sliceProjectionRuns(this.runStorage, 0, start),
|
|
1557
|
+
...sliceProjectionRuns(this.runStorage, start, end).map((run) => ({
|
|
1558
|
+
text: run.text,
|
|
1559
|
+
style: { ...run.style, ...style }
|
|
1560
|
+
})),
|
|
1561
|
+
...sliceProjectionRuns(this.runStorage, end, textLength)
|
|
1562
|
+
]);
|
|
1563
|
+
this.changed();
|
|
1564
|
+
}
|
|
1565
|
+
};
|
|
1566
|
+
function sliceProjectionRuns(runs, start, end) {
|
|
1567
|
+
if (end <= start) return [];
|
|
1568
|
+
const result = [];
|
|
1569
|
+
let offset = 0;
|
|
1570
|
+
for (const run of runs) {
|
|
1571
|
+
const runEnd = offset + run.text.length;
|
|
1572
|
+
const overlapStart = Math.max(start, offset);
|
|
1573
|
+
const overlapEnd = Math.min(end, runEnd);
|
|
1574
|
+
if (overlapEnd > overlapStart) {
|
|
1575
|
+
result.push({
|
|
1576
|
+
text: run.text.slice(overlapStart - offset, overlapEnd - offset),
|
|
1577
|
+
style: { ...run.style }
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
1580
|
+
offset = runEnd;
|
|
1581
|
+
if (offset >= end) break;
|
|
1582
|
+
}
|
|
1583
|
+
return result;
|
|
1584
|
+
}
|
|
1585
|
+
function normalizeProjectionRuns(runs) {
|
|
1586
|
+
const normalized = [];
|
|
1587
|
+
for (const run of runs) {
|
|
1588
|
+
if (!run.text && runs.length > 1) continue;
|
|
1589
|
+
const style = { ...run.style };
|
|
1590
|
+
const previous = normalized.at(-1);
|
|
1591
|
+
if (previous && sameProjectionStyle(previous.style, style)) {
|
|
1592
|
+
normalized[normalized.length - 1] = { text: previous.text + run.text, style };
|
|
1593
|
+
} else {
|
|
1594
|
+
normalized.push({ text: run.text, style });
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
return normalized.length > 0 ? normalized : [{ text: "", style: {} }];
|
|
1598
|
+
}
|
|
1599
|
+
function projectionStyleAt(runs, offset) {
|
|
1600
|
+
let cursor = 0;
|
|
1601
|
+
for (const run of runs) {
|
|
1602
|
+
const end = cursor + run.text.length;
|
|
1603
|
+
if (offset <= end) return { ...run.style };
|
|
1604
|
+
cursor = end;
|
|
1605
|
+
}
|
|
1606
|
+
return { ...runs.at(-1)?.style ?? {} };
|
|
1607
|
+
}
|
|
1608
|
+
function sameProjectionStyle(left, right) {
|
|
1609
|
+
return left.fontFamily === right.fontFamily && left.fontSizePt === right.fontSizePt && left.color === right.color && left.bold === right.bold && left.italic === right.italic && left.underline === right.underline && left.strike === right.strike;
|
|
1610
|
+
}
|
|
1611
|
+
function validateProjectionPage(page) {
|
|
1612
|
+
const values = [
|
|
1613
|
+
page.widthPt,
|
|
1614
|
+
page.heightPt,
|
|
1615
|
+
page.marginTopPt,
|
|
1616
|
+
page.marginRightPt,
|
|
1617
|
+
page.marginBottomPt,
|
|
1618
|
+
page.marginLeftPt
|
|
1619
|
+
];
|
|
1620
|
+
if (values.some((value) => !Number.isFinite(value) || value < 0) || page.widthPt <= 0 || page.heightPt <= 0 || page.marginLeftPt + page.marginRightPt >= page.widthPt || page.marginTopPt + page.marginBottomPt >= page.heightPt) {
|
|
1621
|
+
throw new Error("Document projection contains invalid page geometry");
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
function isDocumentParagraph(value) {
|
|
1625
|
+
return isDocumentBlockKind(value, "paragraph");
|
|
1626
|
+
}
|
|
1627
|
+
function isDocumentTable(value) {
|
|
1628
|
+
return isDocumentBlockKind(value, "table");
|
|
1629
|
+
}
|
|
1630
|
+
function isDocumentPageBreak(value) {
|
|
1631
|
+
return isDocumentBlockKind(value, "pageBreak");
|
|
1632
|
+
}
|
|
1633
|
+
function isDocumentBlockKind(value, kind) {
|
|
1634
|
+
return typeof value === "object" && value !== null && "kind" in value && value.kind === kind;
|
|
1635
|
+
}
|
|
1636
|
+
function asDocumentView(document2) {
|
|
1637
|
+
return document2;
|
|
1638
|
+
}
|
|
1639
|
+
function mapDocumentCommit(command, externalIdByInternal) {
|
|
1640
|
+
return {
|
|
1641
|
+
...command,
|
|
1642
|
+
blockId: externalIdByInternal.get(command.blockId) ?? command.blockId
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
function identity(value) {
|
|
1646
|
+
return value;
|
|
1647
|
+
}
|
|
1648
|
+
function asDocumentError(cause) {
|
|
1649
|
+
return cause instanceof Error ? cause : new Error("Document change failed");
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
// src/components/artifacts/editable-document.tsx
|
|
1653
|
+
import {
|
|
1654
|
+
DOCUMENT_ARTIFACT_COMMAND_VERSION
|
|
1655
|
+
} from "@opengeni/sdk/editable-artifacts";
|
|
1656
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
1657
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
1658
|
+
var DOCUMENT_PAGE_ITEMS = 512;
|
|
1659
|
+
var DOCUMENT_MAX_BLOCKS = 4096;
|
|
1660
|
+
var DOCUMENT_MAX_SECTIONS = 1024;
|
|
1661
|
+
var DOCUMENT_MAX_REVIEW_ITEMS = 4096;
|
|
1662
|
+
var DOCUMENT_MAX_TEXT_UTF16 = 1e6;
|
|
1663
|
+
var DOCUMENT_MAX_TABLE_CELLS = 1e5;
|
|
1664
|
+
var DOCUMENT_MAX_QUERY_PAGES = 64;
|
|
1665
|
+
var DOCUMENT_COMPOSITION_ATTEMPTS = 3;
|
|
1666
|
+
function EditableDocumentArtifactSurface({
|
|
1667
|
+
session,
|
|
1668
|
+
title,
|
|
1669
|
+
subtitle,
|
|
1670
|
+
readOnly = false,
|
|
1671
|
+
onCommit,
|
|
1672
|
+
onCommandError,
|
|
1673
|
+
...surfaceProps
|
|
1674
|
+
}) {
|
|
1675
|
+
const view = useEditableArtifactView(session);
|
|
1676
|
+
const invalidator = editableArtifactProjectionKey(view);
|
|
1677
|
+
const [retryEpoch, setRetryEpoch] = useState2(0);
|
|
1678
|
+
const [state, setState] = useState2({
|
|
1679
|
+
session,
|
|
1680
|
+
projection: null,
|
|
1681
|
+
loading: true,
|
|
1682
|
+
error: null
|
|
1683
|
+
});
|
|
1684
|
+
const loadGeneration = useRef2(0);
|
|
1685
|
+
const commandStateRef = useRef2(null);
|
|
1686
|
+
if (commandStateRef.current?.session !== session) {
|
|
1687
|
+
commandStateRef.current = {
|
|
1688
|
+
session,
|
|
1689
|
+
tail: Promise.resolve(),
|
|
1690
|
+
paragraphText: /* @__PURE__ */ new Map(),
|
|
1691
|
+
localParagraphIds: /* @__PURE__ */ new Set(),
|
|
1692
|
+
optimisticParagraphIds: /* @__PURE__ */ new Map()
|
|
1693
|
+
};
|
|
1694
|
+
}
|
|
1695
|
+
const commandState = commandStateRef.current;
|
|
1696
|
+
useEffect2(() => {
|
|
1697
|
+
const generation = ++loadGeneration.current;
|
|
1698
|
+
let cancelled = false;
|
|
1699
|
+
setState(
|
|
1700
|
+
(current) => current.session === session ? { ...current, loading: true, error: null } : { session, projection: null, loading: true, error: null }
|
|
1701
|
+
);
|
|
1702
|
+
void composeDocumentEditorProjection(session).then(
|
|
1703
|
+
(projection2) => {
|
|
1704
|
+
if (cancelled || generation !== loadGeneration.current || commandStateRef.current !== commandState) {
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
1707
|
+
const nextParagraphText = paragraphTextById(projection2);
|
|
1708
|
+
for (const [localId, canonicalId] of commandState.optimisticParagraphIds) {
|
|
1709
|
+
if (nextParagraphText.has(canonicalId)) continue;
|
|
1710
|
+
const optimisticText = commandState.paragraphText.get(canonicalId) ?? commandState.paragraphText.get(localId);
|
|
1711
|
+
if (optimisticText !== void 0) nextParagraphText.set(canonicalId, optimisticText);
|
|
1712
|
+
}
|
|
1713
|
+
commandState.paragraphText = nextParagraphText;
|
|
1714
|
+
setState({ session, projection: projection2, loading: false, error: null });
|
|
1715
|
+
},
|
|
1716
|
+
(cause) => {
|
|
1717
|
+
if (cancelled || generation !== loadGeneration.current || commandStateRef.current !== commandState) {
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1720
|
+
setState({
|
|
1721
|
+
session,
|
|
1722
|
+
projection: null,
|
|
1723
|
+
loading: false,
|
|
1724
|
+
error: asEditableArtifactError(cause, "Could not open this document")
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
);
|
|
1728
|
+
return () => {
|
|
1729
|
+
cancelled = true;
|
|
1730
|
+
};
|
|
1731
|
+
}, [commandState, invalidator, retryEpoch, session]);
|
|
1732
|
+
const refresh = useCallback2(() => setRetryEpoch((value) => value + 1), []);
|
|
1733
|
+
const writable = !readOnly && view.writable;
|
|
1734
|
+
const applyCommit = useCallback2(
|
|
1735
|
+
(commit) => {
|
|
1736
|
+
const projection2 = state.session === session ? state.projection : null;
|
|
1737
|
+
if (!projection2) return Promise.reject(new Error("Document projection is not ready"));
|
|
1738
|
+
if (!writable) return Promise.reject(new Error("This document is read only"));
|
|
1739
|
+
if (String(commit.revision) !== String(projection2.revision)) {
|
|
1740
|
+
return Promise.reject(
|
|
1741
|
+
new Error("The document changed; retry this edit on the latest version")
|
|
1742
|
+
);
|
|
1743
|
+
}
|
|
1744
|
+
const execute = async () => {
|
|
1745
|
+
if (commandStateRef.current !== commandState) {
|
|
1746
|
+
throw new Error("The document session changed before this edit could be saved");
|
|
1747
|
+
}
|
|
1748
|
+
const allocateLocalParagraph = async (localId) => {
|
|
1749
|
+
const allocated = commandState.optimisticParagraphIds.get(localId);
|
|
1750
|
+
if (allocated !== void 0) return allocated;
|
|
1751
|
+
if (!commandState.localParagraphIds.has(localId)) {
|
|
1752
|
+
throw new Error("The edited paragraph is no longer available");
|
|
1753
|
+
}
|
|
1754
|
+
const created = await session.createDocumentParagraph();
|
|
1755
|
+
if (commandStateRef.current !== commandState) {
|
|
1756
|
+
throw new Error("The document session changed before this edit could be saved");
|
|
1757
|
+
}
|
|
1758
|
+
commandState.optimisticParagraphIds.set(localId, created.paragraphId);
|
|
1759
|
+
commandState.paragraphText.set(created.paragraphId, "");
|
|
1760
|
+
return created.paragraphId;
|
|
1761
|
+
};
|
|
1762
|
+
if (commit.kind === "insert-paragraph") {
|
|
1763
|
+
commandState.localParagraphIds.add(commit.blockId);
|
|
1764
|
+
await allocateLocalParagraph(commit.blockId);
|
|
1765
|
+
notifyDocumentCommit(onCommit, onCommandError, commit);
|
|
1766
|
+
refresh();
|
|
1767
|
+
return;
|
|
1768
|
+
}
|
|
1769
|
+
const targetId = commandState.localParagraphIds.has(commit.blockId) ? await allocateLocalParagraph(commit.blockId) : commit.blockId;
|
|
1770
|
+
let command = null;
|
|
1771
|
+
let rollbackText = null;
|
|
1772
|
+
if (commit.kind === "text") {
|
|
1773
|
+
const before = commandState.paragraphText.get(targetId);
|
|
1774
|
+
if (before === void 0) {
|
|
1775
|
+
throw new Error("The edited paragraph is no longer available");
|
|
1776
|
+
}
|
|
1777
|
+
const edit = minimalUtf16TextEdit(before, commit.text);
|
|
1778
|
+
if (!edit) return;
|
|
1779
|
+
commandState.paragraphText.set(targetId, commit.text);
|
|
1780
|
+
rollbackText = () => {
|
|
1781
|
+
if (commandState.paragraphText.get(targetId) === commit.text) {
|
|
1782
|
+
commandState.paragraphText.set(targetId, before);
|
|
1783
|
+
}
|
|
1784
|
+
};
|
|
1785
|
+
command = {
|
|
1786
|
+
kind: "paragraph.edit",
|
|
1787
|
+
id: targetId,
|
|
1788
|
+
range: edit.range,
|
|
1789
|
+
replacement: edit.replacement,
|
|
1790
|
+
style: null
|
|
1791
|
+
};
|
|
1792
|
+
} else if (commit.kind === "format") {
|
|
1793
|
+
command = {
|
|
1794
|
+
kind: "paragraph.format",
|
|
1795
|
+
id: targetId,
|
|
1796
|
+
range: commit.range,
|
|
1797
|
+
style: documentTextStylePatch(commit.style)
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1800
|
+
if (!command) throw new Error("Unsupported document edit");
|
|
1801
|
+
try {
|
|
1802
|
+
await session.applyDocumentCommands({
|
|
1803
|
+
version: DOCUMENT_ARTIFACT_COMMAND_VERSION,
|
|
1804
|
+
commands: [command]
|
|
1805
|
+
});
|
|
1806
|
+
if (commandStateRef.current !== commandState) return;
|
|
1807
|
+
notifyDocumentCommit(onCommit, onCommandError, commit);
|
|
1808
|
+
refresh();
|
|
1809
|
+
} catch (cause) {
|
|
1810
|
+
rollbackText?.();
|
|
1811
|
+
throw cause;
|
|
1812
|
+
}
|
|
1813
|
+
};
|
|
1814
|
+
const operation = commandState.tail.then(execute, execute);
|
|
1815
|
+
commandState.tail = operation;
|
|
1816
|
+
void operation.then(
|
|
1817
|
+
() => {
|
|
1818
|
+
if (commandState.tail === operation) commandState.tail = Promise.resolve();
|
|
1819
|
+
},
|
|
1820
|
+
() => {
|
|
1821
|
+
if (commandState.tail === operation) commandState.tail = Promise.resolve();
|
|
1822
|
+
}
|
|
1823
|
+
);
|
|
1824
|
+
return operation;
|
|
1825
|
+
},
|
|
1826
|
+
[commandState, onCommandError, onCommit, refresh, session, state, writable]
|
|
1827
|
+
);
|
|
1828
|
+
const projection = state.session === session ? state.projection : null;
|
|
1829
|
+
const status = editableArtifactStatusLabel(view);
|
|
1830
|
+
const accessRevoked = editableArtifactAccessRevoked(view);
|
|
1831
|
+
return projection && !accessRevoked ? /* @__PURE__ */ jsx2(
|
|
1832
|
+
DocumentProjectionArtifactSurface,
|
|
1833
|
+
{
|
|
1834
|
+
...surfaceProps,
|
|
1835
|
+
title,
|
|
1836
|
+
subtitle: subtitle ?? `${projection.blocks.length} block${projection.blocks.length === 1 ? "" : "s"}${writable ? "" : " \xB7 Read only"}`,
|
|
1837
|
+
projection,
|
|
1838
|
+
commit: writable ? applyCommit : void 0,
|
|
1839
|
+
readOnly: !writable,
|
|
1840
|
+
busy: state.loading,
|
|
1841
|
+
onCommandError
|
|
1842
|
+
}
|
|
1843
|
+
) : /* @__PURE__ */ jsx2(
|
|
1844
|
+
ArtifactSurface,
|
|
1845
|
+
{
|
|
1846
|
+
modality: "document",
|
|
1847
|
+
title,
|
|
1848
|
+
subtitle: subtitle ?? status,
|
|
1849
|
+
busy: !accessRevoked && !state.error,
|
|
1850
|
+
className: surfaceProps.className,
|
|
1851
|
+
children: /* @__PURE__ */ jsx2(
|
|
1852
|
+
EditableArtifactMessage,
|
|
1853
|
+
{
|
|
1854
|
+
title: accessRevoked ? "Access changed" : state.error ? "Could not open this document" : "Opening document",
|
|
1855
|
+
detail: accessRevoked ? status : state.error?.message ?? status,
|
|
1856
|
+
retry: !accessRevoked && state.error ? refresh : void 0
|
|
1857
|
+
}
|
|
1858
|
+
)
|
|
1859
|
+
}
|
|
1860
|
+
);
|
|
1861
|
+
}
|
|
1862
|
+
async function composeDocumentEditorProjection(session) {
|
|
1863
|
+
if (session.modality !== "document") throw new Error("Expected a document artifact session");
|
|
1864
|
+
let lastRevisionError = null;
|
|
1865
|
+
for (let attempt = 0; attempt < DOCUMENT_COMPOSITION_ATTEMPTS; attempt += 1) {
|
|
1866
|
+
try {
|
|
1867
|
+
return await composeDocumentAttempt(session);
|
|
1868
|
+
} catch (cause) {
|
|
1869
|
+
const error = asEditableArtifactError(cause, "Could not project this document");
|
|
1870
|
+
if (!isRevisionDrift(error) || attempt === DOCUMENT_COMPOSITION_ATTEMPTS - 1) throw error;
|
|
1871
|
+
lastRevisionError = error;
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
throw lastRevisionError ?? new Error("Document changed while it was opening");
|
|
1875
|
+
}
|
|
1876
|
+
async function composeDocumentAttempt(session) {
|
|
1877
|
+
const summaryPage = await session.queryDocument({ kind: "summary" });
|
|
1878
|
+
const summary = summaryItem(summaryPage);
|
|
1879
|
+
const revision = summaryPage.revision;
|
|
1880
|
+
if (summary.revision !== revision) throw new Error("Document projection revision drifted");
|
|
1881
|
+
if (summary.blockCount > DOCUMENT_MAX_BLOCKS) {
|
|
1882
|
+
throw new Error(`Document exceeds the interactive limit of ${DOCUMENT_MAX_BLOCKS} blocks`);
|
|
1883
|
+
}
|
|
1884
|
+
if (summary.sectionCount > DOCUMENT_MAX_SECTIONS) {
|
|
1885
|
+
throw new Error(`Document exceeds the interactive limit of ${DOCUMENT_MAX_SECTIONS} sections`);
|
|
1886
|
+
}
|
|
1887
|
+
if (summary.commentCount + summary.trackedChangeCount > DOCUMENT_MAX_REVIEW_ITEMS) {
|
|
1888
|
+
throw new Error(
|
|
1889
|
+
`Document exceeds the interactive limit of ${DOCUMENT_MAX_REVIEW_ITEMS} review items`
|
|
1890
|
+
);
|
|
1891
|
+
}
|
|
1892
|
+
const body = await collectDocumentPages(
|
|
1893
|
+
session,
|
|
1894
|
+
"body",
|
|
1895
|
+
revision,
|
|
1896
|
+
DOCUMENT_MAX_BLOCKS,
|
|
1897
|
+
DOCUMENT_MAX_TEXT_UTF16,
|
|
1898
|
+
DOCUMENT_MAX_TABLE_CELLS,
|
|
1899
|
+
(cursor, limits) => ({ kind: "body", startBlock: cursor, limits })
|
|
1900
|
+
);
|
|
1901
|
+
const sections = await collectDocumentPages(
|
|
1902
|
+
session,
|
|
1903
|
+
"sections",
|
|
1904
|
+
revision,
|
|
1905
|
+
DOCUMENT_MAX_SECTIONS,
|
|
1906
|
+
DOCUMENT_MAX_TEXT_UTF16 - body.text,
|
|
1907
|
+
DOCUMENT_MAX_TABLE_CELLS - body.cells,
|
|
1908
|
+
(cursor, limits) => ({ kind: "sections", startSection: cursor, limits })
|
|
1909
|
+
);
|
|
1910
|
+
const review = await collectDocumentPages(
|
|
1911
|
+
session,
|
|
1912
|
+
"review",
|
|
1913
|
+
revision,
|
|
1914
|
+
DOCUMENT_MAX_REVIEW_ITEMS,
|
|
1915
|
+
DOCUMENT_MAX_TEXT_UTF16 - body.text - sections.text,
|
|
1916
|
+
DOCUMENT_MAX_TABLE_CELLS - body.cells - sections.cells,
|
|
1917
|
+
(cursor, limits) => ({ kind: "review", startItem: cursor, limits })
|
|
1918
|
+
);
|
|
1919
|
+
if (body.items.length !== summary.blockCount) {
|
|
1920
|
+
throw new Error("Document body changed while it was opening");
|
|
1921
|
+
}
|
|
1922
|
+
if (sections.items.length !== summary.sectionCount) {
|
|
1923
|
+
throw new Error("Document sections changed while they were opening");
|
|
1924
|
+
}
|
|
1925
|
+
if (review.items.length !== summary.commentCount + summary.trackedChangeCount) {
|
|
1926
|
+
throw new Error("Document review state changed while it was opening");
|
|
1927
|
+
}
|
|
1928
|
+
const totalText = body.text + sections.text + review.text;
|
|
1929
|
+
const totalCells = body.cells + sections.cells + review.cells;
|
|
1930
|
+
if (totalText > DOCUMENT_MAX_TEXT_UTF16) {
|
|
1931
|
+
throw new Error(
|
|
1932
|
+
`Document exceeds the interactive limit of ${DOCUMENT_MAX_TEXT_UTF16} UTF-16 text units`
|
|
1933
|
+
);
|
|
1934
|
+
}
|
|
1935
|
+
if (totalCells > DOCUMENT_MAX_TABLE_CELLS) {
|
|
1936
|
+
throw new Error(
|
|
1937
|
+
`Document exceeds the interactive limit of ${DOCUMENT_MAX_TABLE_CELLS} table cells`
|
|
1938
|
+
);
|
|
1939
|
+
}
|
|
1940
|
+
return Object.freeze({
|
|
1941
|
+
revision: revision.toString(),
|
|
1942
|
+
page: projectDocumentPage(summary.page),
|
|
1943
|
+
blocks: Object.freeze(body.items.map(projectDocumentBlock)),
|
|
1944
|
+
sections: Object.freeze(sections.items.map(projectDocumentSection)),
|
|
1945
|
+
comments: Object.freeze(
|
|
1946
|
+
review.items.filter(isProjectionKind("comment")).map((item) => ({
|
|
1947
|
+
id: item.id,
|
|
1948
|
+
blockId: item.paragraphId,
|
|
1949
|
+
start: item.range.start,
|
|
1950
|
+
end: item.range.end,
|
|
1951
|
+
resolved: item.resolved,
|
|
1952
|
+
replies: item.replies.map((reply) => ({ ...reply }))
|
|
1953
|
+
}))
|
|
1954
|
+
),
|
|
1955
|
+
changes: Object.freeze(
|
|
1956
|
+
review.items.filter(isProjectionKind("tracked-change")).map((item) => ({
|
|
1957
|
+
id: item.id,
|
|
1958
|
+
blockId: item.paragraphId,
|
|
1959
|
+
kind: item.changeKind,
|
|
1960
|
+
start: item.range.start,
|
|
1961
|
+
end: item.range.end,
|
|
1962
|
+
author: item.author,
|
|
1963
|
+
createdAt: item.createdAt
|
|
1964
|
+
}))
|
|
1965
|
+
)
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
async function collectDocumentPages(session, label, revision, maxItems, maxText, maxCells, query) {
|
|
1969
|
+
const output = { items: [], text: 0, cells: 0 };
|
|
1970
|
+
let cursor = 0;
|
|
1971
|
+
for (let pageIndex = 0; pageIndex < DOCUMENT_MAX_QUERY_PAGES; pageIndex += 1) {
|
|
1972
|
+
if (output.items.length >= maxItems && cursor > 0) {
|
|
1973
|
+
throw new Error(`Document ${label} exceeds its interactive item limit`);
|
|
1974
|
+
}
|
|
1975
|
+
const page = await session.queryDocument(
|
|
1976
|
+
query(cursor, {
|
|
1977
|
+
maxItems: Math.min(DOCUMENT_PAGE_ITEMS, Math.max(1, maxItems - output.items.length)),
|
|
1978
|
+
maxTextUtf16: Math.max(1, maxText - output.text),
|
|
1979
|
+
maxTableCells: Math.max(1, maxCells - output.cells)
|
|
1980
|
+
})
|
|
1981
|
+
);
|
|
1982
|
+
assertDocumentRevision(page, revision);
|
|
1983
|
+
assertDocumentPageKinds(label, page.items);
|
|
1984
|
+
if (output.items.length + page.items.length > maxItems) {
|
|
1985
|
+
throw new Error(`Document ${label} exceeds its interactive item limit`);
|
|
1986
|
+
}
|
|
1987
|
+
output.items.push(...page.items);
|
|
1988
|
+
output.text += page.projectedTextUtf16;
|
|
1989
|
+
output.cells += page.projectedTableCells;
|
|
1990
|
+
if (output.text > maxText || output.cells > maxCells) {
|
|
1991
|
+
throw new Error(`Document ${label} exceeds its interactive content limit`);
|
|
1992
|
+
}
|
|
1993
|
+
if (page.nextCursor === null) return output;
|
|
1994
|
+
if (page.nextCursor <= cursor || page.items.length === 0) {
|
|
1995
|
+
throw new Error(`Document ${label} pagination did not advance`);
|
|
1996
|
+
}
|
|
1997
|
+
cursor = page.nextCursor;
|
|
1998
|
+
}
|
|
1999
|
+
throw new Error(`Document ${label} exceeds ${DOCUMENT_MAX_QUERY_PAGES} query pages`);
|
|
2000
|
+
}
|
|
2001
|
+
function summaryItem(projection) {
|
|
2002
|
+
if (projection.items.length !== 1 || projection.items[0]?.kind !== "summary" || projection.nextCursor !== null || projection.truncated) {
|
|
2003
|
+
throw new Error("Document summary projection is malformed");
|
|
2004
|
+
}
|
|
2005
|
+
return projection.items[0];
|
|
2006
|
+
}
|
|
2007
|
+
function assertDocumentRevision(page, revision) {
|
|
2008
|
+
if (page.revision !== revision) throw new Error("Document projection revision drifted");
|
|
2009
|
+
}
|
|
2010
|
+
function assertDocumentPageKinds(label, items) {
|
|
2011
|
+
const valid = label === "body" ? /* @__PURE__ */ new Set(["paragraph", "table", "page-break"]) : label === "sections" ? /* @__PURE__ */ new Set(["section"]) : /* @__PURE__ */ new Set(["comment", "tracked-change"]);
|
|
2012
|
+
if (items.some((item) => !valid.has(item.kind))) {
|
|
2013
|
+
throw new Error(`Document ${label} projection contains an unexpected item`);
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
function projectDocumentBlock(item) {
|
|
2017
|
+
if (item.kind === "paragraph") {
|
|
2018
|
+
const { headingLevel, list, ...paragraphStyle } = item.style;
|
|
2019
|
+
return {
|
|
2020
|
+
kind: "paragraph",
|
|
2021
|
+
id: item.id,
|
|
2022
|
+
runs: item.runs.map((run) => ({ text: run.text, style: { ...run.style } })),
|
|
2023
|
+
style: {
|
|
2024
|
+
...paragraphStyle,
|
|
2025
|
+
...headingLevel === void 0 ? {} : { headingLevel: documentHeadingLevel(headingLevel) },
|
|
2026
|
+
...list ? {
|
|
2027
|
+
list: {
|
|
2028
|
+
kind: list.kind,
|
|
2029
|
+
...list.level === null ? {} : { level: list.level },
|
|
2030
|
+
...list.instanceId === null ? {} : { instanceId: list.instanceId }
|
|
2031
|
+
}
|
|
2032
|
+
} : {}
|
|
2033
|
+
}
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
if (item.kind === "table") {
|
|
2037
|
+
return {
|
|
2038
|
+
kind: "table",
|
|
2039
|
+
id: item.id,
|
|
2040
|
+
rows: item.rows.map(
|
|
2041
|
+
(row) => row.map((cell) => cell.map((run) => ({ text: run.text, style: { ...run.style } })))
|
|
2042
|
+
),
|
|
2043
|
+
style: { ...item.style }
|
|
2044
|
+
};
|
|
2045
|
+
}
|
|
2046
|
+
if (item.kind === "page-break") return { kind: "pageBreak", id: item.id };
|
|
2047
|
+
throw new Error("Document body projection contains an unexpected item");
|
|
2048
|
+
}
|
|
2049
|
+
function documentHeadingLevel(value) {
|
|
2050
|
+
if (value < 1 || value > 6 || !Number.isInteger(value)) {
|
|
2051
|
+
throw new Error("Document projection contains an invalid heading level");
|
|
2052
|
+
}
|
|
2053
|
+
return value;
|
|
2054
|
+
}
|
|
2055
|
+
function projectDocumentSection(item) {
|
|
2056
|
+
if (item.kind !== "section") {
|
|
2057
|
+
throw new Error("Document section projection contains an unexpected item");
|
|
2058
|
+
}
|
|
2059
|
+
return {
|
|
2060
|
+
id: item.id,
|
|
2061
|
+
startBlockIndex: item.startBlockIndex,
|
|
2062
|
+
page: projectDocumentPage(item.page),
|
|
2063
|
+
titlePage: item.titlePage,
|
|
2064
|
+
headerBlockCounts: item.headerBlockCounts,
|
|
2065
|
+
footerBlockCounts: item.footerBlockCounts
|
|
2066
|
+
};
|
|
2067
|
+
}
|
|
2068
|
+
function projectDocumentPage(page) {
|
|
2069
|
+
const points = (value) => Number(value) / 1e3;
|
|
2070
|
+
return {
|
|
2071
|
+
widthPt: points(page.widthMillipoints),
|
|
2072
|
+
heightPt: points(page.heightMillipoints),
|
|
2073
|
+
marginTopPt: points(page.marginTopMillipoints),
|
|
2074
|
+
marginRightPt: points(page.marginRightMillipoints),
|
|
2075
|
+
marginBottomPt: points(page.marginBottomMillipoints),
|
|
2076
|
+
marginLeftPt: points(page.marginLeftMillipoints),
|
|
2077
|
+
headerPt: points(page.headerMillipoints),
|
|
2078
|
+
footerPt: points(page.footerMillipoints),
|
|
2079
|
+
gutterPt: points(page.gutterMillipoints)
|
|
2080
|
+
};
|
|
2081
|
+
}
|
|
2082
|
+
function isProjectionKind(kind) {
|
|
2083
|
+
return (item) => item.kind === kind;
|
|
2084
|
+
}
|
|
2085
|
+
function paragraphTextById(projection) {
|
|
2086
|
+
return new Map(
|
|
2087
|
+
projection.blocks.filter((block) => block.kind === "paragraph").map((paragraph) => [paragraph.id, paragraph.runs.map((run) => run.text).join("")])
|
|
2088
|
+
);
|
|
2089
|
+
}
|
|
2090
|
+
function minimalUtf16TextEdit(before, after) {
|
|
2091
|
+
if (before === after) return null;
|
|
2092
|
+
let start = 0;
|
|
2093
|
+
const maximumStart = Math.min(before.length, after.length);
|
|
2094
|
+
while (start < maximumStart && before.charCodeAt(start) === after.charCodeAt(start)) start += 1;
|
|
2095
|
+
while (start > 0 && (splitsSurrogate(before, start) || splitsSurrogate(after, start))) start -= 1;
|
|
2096
|
+
let suffix = 0;
|
|
2097
|
+
while (suffix < before.length - start && suffix < after.length - start && before.charCodeAt(before.length - suffix - 1) === after.charCodeAt(after.length - suffix - 1)) {
|
|
2098
|
+
suffix += 1;
|
|
2099
|
+
}
|
|
2100
|
+
while (suffix > 0 && (splitsSurrogate(before, before.length - suffix) || splitsSurrogate(after, after.length - suffix))) {
|
|
2101
|
+
suffix -= 1;
|
|
2102
|
+
}
|
|
2103
|
+
return {
|
|
2104
|
+
range: { start, end: before.length - suffix },
|
|
2105
|
+
replacement: after.slice(start, after.length - suffix)
|
|
2106
|
+
};
|
|
2107
|
+
}
|
|
2108
|
+
function splitsSurrogate(value, index) {
|
|
2109
|
+
if (index <= 0 || index >= value.length) return false;
|
|
2110
|
+
const left = value.charCodeAt(index - 1);
|
|
2111
|
+
const right = value.charCodeAt(index);
|
|
2112
|
+
return left >= 55296 && left <= 56319 && right >= 56320 && right <= 57343;
|
|
2113
|
+
}
|
|
2114
|
+
function documentTextStylePatch(style) {
|
|
2115
|
+
return {
|
|
2116
|
+
...style.fontFamily === void 0 ? {} : { fontFamily: style.fontFamily },
|
|
2117
|
+
...style.fontSizePt === void 0 ? {} : { fontSizePt: style.fontSizePt },
|
|
2118
|
+
...style.color === void 0 ? {} : { color: style.color },
|
|
2119
|
+
...style.bold === void 0 ? {} : { bold: style.bold },
|
|
2120
|
+
...style.italic === void 0 ? {} : { italic: style.italic },
|
|
2121
|
+
...style.underline === void 0 ? {} : { underline: style.underline },
|
|
2122
|
+
...style.strike === void 0 ? {} : { strike: style.strike }
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
function isRevisionDrift(error) {
|
|
2126
|
+
return error.message.includes("changed while") || error.message.includes("revision drifted");
|
|
2127
|
+
}
|
|
2128
|
+
function notifyDocumentCommit(observer, onError, commit) {
|
|
2129
|
+
try {
|
|
2130
|
+
const result = observer?.(commit);
|
|
2131
|
+
if (result && typeof result.then === "function") {
|
|
2132
|
+
void Promise.resolve(result).catch(
|
|
2133
|
+
(cause) => onError?.(asEditableArtifactError(cause, "Document commit observer failed"))
|
|
2134
|
+
);
|
|
2135
|
+
}
|
|
2136
|
+
} catch (cause) {
|
|
2137
|
+
onError?.(asEditableArtifactError(cause, "Document commit observer failed"));
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
export {
|
|
2142
|
+
DocumentEditor,
|
|
2143
|
+
DocumentProjectionEditor,
|
|
2144
|
+
DocumentArtifactSurface,
|
|
2145
|
+
DocumentProjectionArtifactSurface,
|
|
2146
|
+
EditableDocumentArtifactSurface
|
|
2147
|
+
};
|
|
2148
|
+
//# sourceMappingURL=chunk-HUIKIDM5.js.map
|