@aglyn/besigner-ui 1.0.0-beta.143
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/LICENSE +201 -0
- package/README.md +7 -0
- package/package.json +73 -0
- package/src/besigner.d.ts +88 -0
- package/src/besigner.js +89 -0
- package/src/besigner.js.map +1 -0
- package/src/index.d.ts +17 -0
- package/src/index.js +18 -0
- package/src/index.js.map +1 -0
- package/src/lib/box-styler/box-styler.d.ts +57 -0
- package/src/lib/box-styler/box-styler.js +240 -0
- package/src/lib/box-styler/box-styler.js.map +1 -0
- package/src/lib/box-styler/components/box-diagram.d.ts +89 -0
- package/src/lib/box-styler/components/box-diagram.js +661 -0
- package/src/lib/box-styler/components/box-diagram.js.map +1 -0
- package/src/lib/box-styler/components/legend.d.ts +56 -0
- package/src/lib/box-styler/components/legend.js +130 -0
- package/src/lib/box-styler/components/legend.js.map +1 -0
- package/src/lib/box-styler/components/spacing-editor.d.ts +55 -0
- package/src/lib/box-styler/components/spacing-editor.js +347 -0
- package/src/lib/box-styler/components/spacing-editor.js.map +1 -0
- package/src/lib/box-styler/constants.d.ts +17 -0
- package/src/lib/box-styler/constants.js +30 -0
- package/src/lib/box-styler/constants.js.map +1 -0
- package/src/lib/box-styler/index.d.ts +2 -0
- package/src/lib/box-styler/index.js +4 -0
- package/src/lib/box-styler/index.js.map +1 -0
- package/src/lib/box-styler/region-fills.d.ts +107 -0
- package/src/lib/box-styler/region-fills.js +139 -0
- package/src/lib/box-styler/region-fills.js.map +1 -0
- package/src/lib/box-styler/spacing-value.d.ts +104 -0
- package/src/lib/box-styler/spacing-value.js +223 -0
- package/src/lib/box-styler/spacing-value.js.map +1 -0
- package/src/lib/box-styler/types.d.ts +29 -0
- package/src/lib/box-styler/types.js +18 -0
- package/src/lib/box-styler/types.js.map +1 -0
- package/src/lib/components/accordion-list.component.d.ts +50 -0
- package/src/lib/components/accordion-list.component.js +228 -0
- package/src/lib/components/accordion-list.component.js.map +1 -0
- package/src/lib/components/add-controls.component.d.ts +22 -0
- package/src/lib/components/add-controls.component.js +50 -0
- package/src/lib/components/add-controls.component.js.map +1 -0
- package/src/lib/components/app-bar-breadcrumbs.component.d.ts +29 -0
- package/src/lib/components/app-bar-breadcrumbs.component.js +279 -0
- package/src/lib/components/app-bar-breadcrumbs.component.js.map +1 -0
- package/src/lib/components/app-bar-primary.component.d.ts +21 -0
- package/src/lib/components/app-bar-primary.component.js +108 -0
- package/src/lib/components/app-bar-primary.component.js.map +1 -0
- package/src/lib/components/app-bar-secondary.component.d.ts +21 -0
- package/src/lib/components/app-bar-secondary.component.js +94 -0
- package/src/lib/components/app-bar-secondary.component.js.map +1 -0
- package/src/lib/components/aside-panel.component.d.ts +23 -0
- package/src/lib/components/aside-panel.component.js +505 -0
- package/src/lib/components/aside-panel.component.js.map +1 -0
- package/src/lib/components/besigner-conflict-alert.component.d.ts +36 -0
- package/src/lib/components/besigner-conflict-alert.component.js +51 -0
- package/src/lib/components/besigner-conflict-alert.component.js.map +1 -0
- package/src/lib/components/besigner-dnd-context.component.d.ts +32 -0
- package/src/lib/components/besigner-dnd-context.component.js +75 -0
- package/src/lib/components/besigner-dnd-context.component.js.map +1 -0
- package/src/lib/components/besigner-draft-alert.component.d.ts +70 -0
- package/src/lib/components/besigner-draft-alert.component.js +155 -0
- package/src/lib/components/besigner-draft-alert.component.js.map +1 -0
- package/src/lib/components/besigner-root-provider.component.d.ts +33 -0
- package/src/lib/components/besigner-root-provider.component.js +68 -0
- package/src/lib/components/besigner-root-provider.component.js.map +1 -0
- package/src/lib/components/binding-preview-controls.component.d.ts +28 -0
- package/src/lib/components/binding-preview-controls.component.js +63 -0
- package/src/lib/components/binding-preview-controls.component.js.map +1 -0
- package/src/lib/components/closeable-drawer.component.d.ts +32 -0
- package/src/lib/components/closeable-drawer.component.js +134 -0
- package/src/lib/components/closeable-drawer.component.js.map +1 -0
- package/src/lib/components/component-accordion-list.d.ts +28 -0
- package/src/lib/components/component-accordion-list.js +205 -0
- package/src/lib/components/component-accordion-list.js.map +1 -0
- package/src/lib/components/component-icon.component.d.ts +32 -0
- package/src/lib/components/component-icon.component.js +51 -0
- package/src/lib/components/component-icon.component.js.map +1 -0
- package/src/lib/components/component-picker.d.ts +27 -0
- package/src/lib/components/component-picker.js +368 -0
- package/src/lib/components/component-picker.js.map +1 -0
- package/src/lib/components/components-grid-list.component.d.ts +31 -0
- package/src/lib/components/components-grid-list.component.js +162 -0
- package/src/lib/components/components-grid-list.component.js.map +1 -0
- package/src/lib/components/custom-css-form.component.d.ts +115 -0
- package/src/lib/components/custom-css-form.component.js +699 -0
- package/src/lib/components/custom-css-form.component.js.map +1 -0
- package/src/lib/components/device-preview-controls.component.d.ts +22 -0
- package/src/lib/components/device-preview-controls.component.js +150 -0
- package/src/lib/components/device-preview-controls.component.js.map +1 -0
- package/src/lib/components/dnd/canvas-drop-indicator.d.ts +20 -0
- package/src/lib/components/dnd/canvas-drop-indicator.js +61 -0
- package/src/lib/components/dnd/canvas-drop-indicator.js.map +1 -0
- package/src/lib/components/dnd/draggable-droppable.d.ts +31 -0
- package/src/lib/components/dnd/draggable-droppable.js +383 -0
- package/src/lib/components/dnd/draggable-droppable.js.map +1 -0
- package/src/lib/components/dnd/draggable.d.ts +42 -0
- package/src/lib/components/dnd/draggable.js +66 -0
- package/src/lib/components/dnd/draggable.js.map +1 -0
- package/src/lib/components/dnd/drop-indicator.d.ts +31 -0
- package/src/lib/components/dnd/drop-indicator.js +139 -0
- package/src/lib/components/dnd/drop-indicator.js.map +1 -0
- package/src/lib/components/dnd/droppable.d.ts +43 -0
- package/src/lib/components/dnd/droppable.js +63 -0
- package/src/lib/components/dnd/droppable.js.map +1 -0
- package/src/lib/components/element-card.component.d.ts +23 -0
- package/src/lib/components/element-card.component.js +49 -0
- package/src/lib/components/element-card.component.js.map +1 -0
- package/src/lib/components/element-classes-field.component.d.ts +41 -0
- package/src/lib/components/element-classes-field.component.js +140 -0
- package/src/lib/components/element-classes-field.component.js.map +1 -0
- package/src/lib/components/element-detail-overlay.component.d.ts +55 -0
- package/src/lib/components/element-detail-overlay.component.js +157 -0
- package/src/lib/components/element-detail-overlay.component.js.map +1 -0
- package/src/lib/components/element-detail.component.d.ts +56 -0
- package/src/lib/components/element-detail.component.js +171 -0
- package/src/lib/components/element-detail.component.js.map +1 -0
- package/src/lib/components/element-info-details.component.d.ts +18 -0
- package/src/lib/components/element-info-details.component.js +169 -0
- package/src/lib/components/element-info-details.component.js.map +1 -0
- package/src/lib/components/element-interactions-form.component.d.ts +21 -0
- package/src/lib/components/element-interactions-form.component.js +199 -0
- package/src/lib/components/element-interactions-form.component.js.map +1 -0
- package/src/lib/components/element-preview.component.d.ts +75 -0
- package/src/lib/components/element-preview.component.js +346 -0
- package/src/lib/components/element-preview.component.js.map +1 -0
- package/src/lib/components/element-props-form.component.d.ts +565 -0
- package/src/lib/components/element-props-form.component.js +2392 -0
- package/src/lib/components/element-props-form.component.js.map +1 -0
- package/src/lib/components/element-styles-form-template.component.d.ts +27 -0
- package/src/lib/components/element-styles-form-template.component.js +89 -0
- package/src/lib/components/element-styles-form-template.component.js.map +1 -0
- package/src/lib/components/element-styles-form.component.d.ts +52 -0
- package/src/lib/components/element-styles-form.component.js +1350 -0
- package/src/lib/components/element-styles-form.component.js.map +1 -0
- package/src/lib/components/empty-document-slot.d.ts +39 -0
- package/src/lib/components/empty-document-slot.js +121 -0
- package/src/lib/components/empty-document-slot.js.map +1 -0
- package/src/lib/components/empty-results.d.ts +22 -0
- package/src/lib/components/empty-results.js +131 -0
- package/src/lib/components/empty-results.js.map +1 -0
- package/src/lib/components/history-controls.component.d.ts +21 -0
- package/src/lib/components/history-controls.component.js +67 -0
- package/src/lib/components/history-controls.component.js.map +1 -0
- package/src/lib/components/inline-markdown-editor.component.d.ts +17 -0
- package/src/lib/components/inline-markdown-editor.component.js +220 -0
- package/src/lib/components/inline-markdown-editor.component.js.map +1 -0
- package/src/lib/components/inline-text-editor.component.d.ts +48 -0
- package/src/lib/components/inline-text-editor.component.js +905 -0
- package/src/lib/components/inline-text-editor.component.js.map +1 -0
- package/src/lib/components/insert-token-menu.component.d.ts +86 -0
- package/src/lib/components/insert-token-menu.component.js +232 -0
- package/src/lib/components/insert-token-menu.component.js.map +1 -0
- package/src/lib/components/instance-attr-overrides.component.d.ts +50 -0
- package/src/lib/components/instance-attr-overrides.component.js +298 -0
- package/src/lib/components/instance-attr-overrides.component.js.map +1 -0
- package/src/lib/components/interact-controls.component.d.ts +22 -0
- package/src/lib/components/interact-controls.component.js +74 -0
- package/src/lib/components/interact-controls.component.js.map +1 -0
- package/src/lib/components/markdown-attribute-field.component.d.ts +40 -0
- package/src/lib/components/markdown-attribute-field.component.js +103 -0
- package/src/lib/components/markdown-attribute-field.component.js.map +1 -0
- package/src/lib/components/node-card.d.ts +24 -0
- package/src/lib/components/node-card.js +86 -0
- package/src/lib/components/node-card.js.map +1 -0
- package/src/lib/components/node-context-menu.d.ts +25 -0
- package/src/lib/components/node-context-menu.js +558 -0
- package/src/lib/components/node-context-menu.js.map +1 -0
- package/src/lib/components/node-leaf.d.ts +39 -0
- package/src/lib/components/node-leaf.js +628 -0
- package/src/lib/components/node-leaf.js.map +1 -0
- package/src/lib/components/node-outline.d.ts +24 -0
- package/src/lib/components/node-outline.js +156 -0
- package/src/lib/components/node-outline.js.map +1 -0
- package/src/lib/components/node-overlay.d.ts +23 -0
- package/src/lib/components/node-overlay.js +299 -0
- package/src/lib/components/node-overlay.js.map +1 -0
- package/src/lib/components/node-quick-actions.d.ts +24 -0
- package/src/lib/components/node-quick-actions.js +284 -0
- package/src/lib/components/node-quick-actions.js.map +1 -0
- package/src/lib/components/node-tree-view.d.ts +106 -0
- package/src/lib/components/node-tree-view.js +820 -0
- package/src/lib/components/node-tree-view.js.map +1 -0
- package/src/lib/components/panel-controls.component.d.ts +22 -0
- package/src/lib/components/panel-controls.component.js +76 -0
- package/src/lib/components/panel-controls.component.js.map +1 -0
- package/src/lib/components/picker-search-field.d.ts +38 -0
- package/src/lib/components/picker-search-field.js +79 -0
- package/src/lib/components/picker-search-field.js.map +1 -0
- package/src/lib/components/plugin-settings-field.component.d.ts +38 -0
- package/src/lib/components/plugin-settings-field.component.js +264 -0
- package/src/lib/components/plugin-settings-field.component.js.map +1 -0
- package/src/lib/components/properties-dialog.component.d.ts +22 -0
- package/src/lib/components/properties-dialog.component.js +40 -0
- package/src/lib/components/properties-dialog.component.js.map +1 -0
- package/src/lib/components/property-binding-field.component.d.ts +142 -0
- package/src/lib/components/property-binding-field.component.js +325 -0
- package/src/lib/components/property-binding-field.component.js.map +1 -0
- package/src/lib/components/property-values-form.component.d.ts +43 -0
- package/src/lib/components/property-values-form.component.js +223 -0
- package/src/lib/components/property-values-form.component.js.map +1 -0
- package/src/lib/components/scheme-preview-controls.component.d.ts +26 -0
- package/src/lib/components/scheme-preview-controls.component.js +56 -0
- package/src/lib/components/scheme-preview-controls.component.js.map +1 -0
- package/src/lib/components/screen-link-field.component.d.ts +118 -0
- package/src/lib/components/screen-link-field.component.js +282 -0
- package/src/lib/components/screen-link-field.component.js.map +1 -0
- package/src/lib/components/site-theme-color-tokens-provider.component.d.ts +33 -0
- package/src/lib/components/site-theme-color-tokens-provider.component.js +53 -0
- package/src/lib/components/site-theme-color-tokens-provider.component.js.map +1 -0
- package/src/lib/components/subtree-json-dialog.component.d.ts +46 -0
- package/src/lib/components/subtree-json-dialog.component.js +125 -0
- package/src/lib/components/subtree-json-dialog.component.js.map +1 -0
- package/src/lib/components/token-pill.component.d.ts +51 -0
- package/src/lib/components/token-pill.component.js +164 -0
- package/src/lib/components/token-pill.component.js.map +1 -0
- package/src/lib/components/token-text-field.component.d.ts +146 -0
- package/src/lib/components/token-text-field.component.js +603 -0
- package/src/lib/components/token-text-field.component.js.map +1 -0
- package/src/lib/components/tree/tree-list.d.ts +6 -0
- package/src/lib/components/tree/tree-list.js +108 -0
- package/src/lib/components/tree/tree-list.js.map +1 -0
- package/src/lib/components/viewport-canvas.component.d.ts +23 -0
- package/src/lib/components/viewport-canvas.component.js +128 -0
- package/src/lib/components/viewport-canvas.component.js.map +1 -0
- package/src/lib/components/viewport-frame.component.d.ts +47 -0
- package/src/lib/components/viewport-frame.component.js +332 -0
- package/src/lib/components/viewport-frame.component.js.map +1 -0
- package/src/lib/components/viewport-root.component.d.ts +22 -0
- package/src/lib/components/viewport-root.component.js +112 -0
- package/src/lib/components/viewport-root.component.js.map +1 -0
- package/src/lib/components/viewport-zoom-controls.d.ts +29 -0
- package/src/lib/components/viewport-zoom-controls.js +147 -0
- package/src/lib/components/viewport-zoom-controls.js.map +1 -0
- package/src/lib/components/workspace-editor.component.d.ts +23 -0
- package/src/lib/components/workspace-editor.component.js +191 -0
- package/src/lib/components/workspace-editor.component.js.map +1 -0
- package/src/lib/components/workspace-panel.component.d.ts +28 -0
- package/src/lib/components/workspace-panel.component.js +147 -0
- package/src/lib/components/workspace-panel.component.js.map +1 -0
- package/src/lib/constants/sample-data.d.ts +18 -0
- package/src/lib/constants/sample-data.js +106 -0
- package/src/lib/constants/sample-data.js.map +1 -0
- package/src/lib/constants/shared.d.ts +17 -0
- package/src/lib/constants/shared.js +18 -0
- package/src/lib/constants/shared.js.map +1 -0
- package/src/lib/contexts/besigner-app-context.d.ts +30 -0
- package/src/lib/contexts/besigner-app-context.js +41 -0
- package/src/lib/contexts/besigner-app-context.js.map +1 -0
- package/src/lib/contexts/binding-picker-context.d.ts +78 -0
- package/src/lib/contexts/binding-picker-context.js +21 -0
- package/src/lib/contexts/binding-picker-context.js.map +1 -0
- package/src/lib/contexts/canvas-host-tokens-context.d.ts +49 -0
- package/src/lib/contexts/canvas-host-tokens-context.js +63 -0
- package/src/lib/contexts/canvas-host-tokens-context.js.map +1 -0
- package/src/lib/contexts/canvas-reveal-context.d.ts +32 -0
- package/src/lib/contexts/canvas-reveal-context.js +34 -0
- package/src/lib/contexts/canvas-reveal-context.js.map +1 -0
- package/src/lib/contexts/component-promotion-context.d.ts +89 -0
- package/src/lib/contexts/component-promotion-context.js +21 -0
- package/src/lib/contexts/component-promotion-context.js.map +1 -0
- package/src/lib/contexts/components-drawer-context.provider.d.ts +32 -0
- package/src/lib/contexts/components-drawer-context.provider.js +80 -0
- package/src/lib/contexts/components-drawer-context.provider.js.map +1 -0
- package/src/lib/contexts/element-drawer-context.d.ts +1005 -0
- package/src/lib/contexts/element-drawer-context.js +45 -0
- package/src/lib/contexts/element-drawer-context.js.map +1 -0
- package/src/lib/contexts/inspected-selection.d.ts +32 -0
- package/src/lib/contexts/inspected-selection.js +38 -0
- package/src/lib/contexts/inspected-selection.js.map +1 -0
- package/src/lib/contexts/inspector-extras-context.d.ts +56 -0
- package/src/lib/contexts/inspector-extras-context.js +35 -0
- package/src/lib/contexts/inspector-extras-context.js.map +1 -0
- package/src/lib/contexts/interactions-context.d.ts +99 -0
- package/src/lib/contexts/interactions-context.js +22 -0
- package/src/lib/contexts/interactions-context.js.map +1 -0
- package/src/lib/contexts/layout-chrome-context.d.ts +37 -0
- package/src/lib/contexts/layout-chrome-context.js +75 -0
- package/src/lib/contexts/layout-chrome-context.js.map +1 -0
- package/src/lib/contexts/media-asset-facts-context.d.ts +74 -0
- package/src/lib/contexts/media-asset-facts-context.js +27 -0
- package/src/lib/contexts/media-asset-facts-context.js.map +1 -0
- package/src/lib/contexts/media-picker-context.d.ts +88 -0
- package/src/lib/contexts/media-picker-context.js +21 -0
- package/src/lib/contexts/media-picker-context.js.map +1 -0
- package/src/lib/contexts/rendered-canvas-elements.d.ts +42 -0
- package/src/lib/contexts/rendered-canvas-elements.js +79 -0
- package/src/lib/contexts/rendered-canvas-elements.js.map +1 -0
- package/src/lib/contexts/repeat-record-context.d.ts +38 -0
- package/src/lib/contexts/repeat-record-context.js +21 -0
- package/src/lib/contexts/repeat-record-context.js.map +1 -0
- package/src/lib/contexts/repeat-rows-context.d.ts +53 -0
- package/src/lib/contexts/repeat-rows-context.js +38 -0
- package/src/lib/contexts/repeat-rows-context.js.map +1 -0
- package/src/lib/contexts/toolbar-extras-context.d.ts +31 -0
- package/src/lib/contexts/toolbar-extras-context.js +33 -0
- package/src/lib/contexts/toolbar-extras-context.js.map +1 -0
- package/src/lib/drafts/besigner-draft-store.d.ts +110 -0
- package/src/lib/drafts/besigner-draft-store.js +125 -0
- package/src/lib/drafts/besigner-draft-store.js.map +1 -0
- package/src/lib/drafts/besigner-server-draft.d.ts +130 -0
- package/src/lib/drafts/besigner-server-draft.js +211 -0
- package/src/lib/drafts/besigner-server-draft.js.map +1 -0
- package/src/lib/form-fields/components/shared/inline-form-control.d.ts +32 -0
- package/src/lib/form-fields/components/shared/inline-form-control.js +85 -0
- package/src/lib/form-fields/components/shared/inline-form-control.js.map +1 -0
- package/src/lib/form-fields/components/shared/toggle-button-form-control.d.ts +33 -0
- package/src/lib/form-fields/components/shared/toggle-button-form-control.js +83 -0
- package/src/lib/form-fields/components/shared/toggle-button-form-control.js.map +1 -0
- package/src/lib/form-fields/form-fields.d.ts +20 -0
- package/src/lib/form-fields/form-fields.js +21 -0
- package/src/lib/form-fields/form-fields.js.map +1 -0
- package/src/lib/form-fields/index.d.ts +1 -0
- package/src/lib/form-fields/index.js +3 -0
- package/src/lib/form-fields/index.js.map +1 -0
- package/src/lib/hooks/use-add-element-drawer-callback.d.ts +28 -0
- package/src/lib/hooks/use-add-element-drawer-callback.js +112 -0
- package/src/lib/hooks/use-add-element-drawer-callback.js.map +1 -0
- package/src/lib/hooks/use-aglyn-besigner-flag.d.ts +23 -0
- package/src/lib/hooks/use-aglyn-besigner-flag.js +47 -0
- package/src/lib/hooks/use-aglyn-besigner-flag.js.map +1 -0
- package/src/lib/hooks/use-aglyn-besigner-panel-value.d.ts +23 -0
- package/src/lib/hooks/use-aglyn-besigner-panel-value.js +54 -0
- package/src/lib/hooks/use-aglyn-besigner-panel-value.js.map +1 -0
- package/src/lib/hooks/use-aglyn-besigner-panel.d.ts +23 -0
- package/src/lib/hooks/use-aglyn-besigner-panel.js +47 -0
- package/src/lib/hooks/use-aglyn-besigner-panel.js.map +1 -0
- package/src/lib/hooks/use-anchored-rect.d.ts +68 -0
- package/src/lib/hooks/use-anchored-rect.js +131 -0
- package/src/lib/hooks/use-anchored-rect.js.map +1 -0
- package/src/lib/hooks/use-besigner-app-context.d.ts +18 -0
- package/src/lib/hooks/use-besigner-app-context.js +19 -0
- package/src/lib/hooks/use-besigner-app-context.js.map +1 -0
- package/src/lib/hooks/use-besigner-document.d.ts +309 -0
- package/src/lib/hooks/use-besigner-document.js +500 -0
- package/src/lib/hooks/use-besigner-document.js.map +1 -0
- package/src/lib/hooks/use-besigner-draft.d.ts +209 -0
- package/src/lib/hooks/use-besigner-draft.js +429 -0
- package/src/lib/hooks/use-besigner-draft.js.map +1 -0
- package/src/lib/hooks/use-clipboard-callbacks.d.ts +29 -0
- package/src/lib/hooks/use-clipboard-callbacks.js +82 -0
- package/src/lib/hooks/use-clipboard-callbacks.js.map +1 -0
- package/src/lib/hooks/use-clipboard-shortcuts.d.ts +52 -0
- package/src/lib/hooks/use-clipboard-shortcuts.js +102 -0
- package/src/lib/hooks/use-clipboard-shortcuts.js.map +1 -0
- package/src/lib/hooks/use-component-propagation-notice.d.ts +96 -0
- package/src/lib/hooks/use-component-propagation-notice.js +177 -0
- package/src/lib/hooks/use-component-propagation-notice.js.map +1 -0
- package/src/lib/hooks/use-debounced-commit.d.ts +43 -0
- package/src/lib/hooks/use-debounced-commit.js +88 -0
- package/src/lib/hooks/use-debounced-commit.js.map +1 -0
- package/src/lib/hooks/use-delete-element-callback.d.ts +24 -0
- package/src/lib/hooks/use-delete-element-callback.js +82 -0
- package/src/lib/hooks/use-delete-element-callback.js.map +1 -0
- package/src/lib/hooks/use-detail-hover-intent.d.ts +76 -0
- package/src/lib/hooks/use-detail-hover-intent.js +123 -0
- package/src/lib/hooks/use-detail-hover-intent.js.map +1 -0
- package/src/lib/hooks/use-document-repair-callbacks.d.ts +50 -0
- package/src/lib/hooks/use-document-repair-callbacks.js +237 -0
- package/src/lib/hooks/use-document-repair-callbacks.js.map +1 -0
- package/src/lib/hooks/use-insert-token-options.d.ts +52 -0
- package/src/lib/hooks/use-insert-token-options.js +230 -0
- package/src/lib/hooks/use-insert-token-options.js.map +1 -0
- package/src/lib/hooks/use-leaf-drag.d.ts +24 -0
- package/src/lib/hooks/use-leaf-drag.js +28 -0
- package/src/lib/hooks/use-leaf-drag.js.map +1 -0
- package/src/lib/hooks/use-leaf-drop.d.ts +31 -0
- package/src/lib/hooks/use-leaf-drop.js +43 -0
- package/src/lib/hooks/use-leaf-drop.js.map +1 -0
- package/src/lib/hooks/use-markdown-media-picker.d.ts +63 -0
- package/src/lib/hooks/use-markdown-media-picker.js +110 -0
- package/src/lib/hooks/use-markdown-media-picker.js.map +1 -0
- package/src/lib/hooks/use-media-asset-facts-overlay.d.ts +43 -0
- package/src/lib/hooks/use-media-asset-facts-overlay.js +133 -0
- package/src/lib/hooks/use-media-asset-facts-overlay.js.map +1 -0
- package/src/lib/hooks/use-move-node-callbacks.d.ts +22 -0
- package/src/lib/hooks/use-move-node-callbacks.js +57 -0
- package/src/lib/hooks/use-move-node-callbacks.js.map +1 -0
- package/src/lib/hooks/use-node-with-host-tokens.d.ts +42 -0
- package/src/lib/hooks/use-node-with-host-tokens.js +59 -0
- package/src/lib/hooks/use-node-with-host-tokens.js.map +1 -0
- package/src/lib/hooks/use-picker-filter.d.ts +61 -0
- package/src/lib/hooks/use-picker-filter.js +122 -0
- package/src/lib/hooks/use-picker-filter.js.map +1 -0
- package/src/lib/hooks/use-repeat-preview.d.ts +64 -0
- package/src/lib/hooks/use-repeat-preview.js +214 -0
- package/src/lib/hooks/use-repeat-preview.js.map +1 -0
- package/src/lib/hooks/use-repeat-sources.d.ts +17 -0
- package/src/lib/hooks/use-repeat-sources.js +41 -0
- package/src/lib/hooks/use-repeat-sources.js.map +1 -0
- package/src/lib/hooks/use-visible-component-categories.d.ts +37 -0
- package/src/lib/hooks/use-visible-component-categories.js +79 -0
- package/src/lib/hooks/use-visible-component-categories.js.map +1 -0
- package/src/lib/utils/active-host-theme.d.ts +28 -0
- package/src/lib/utils/active-host-theme.js +77 -0
- package/src/lib/utils/active-host-theme.js.map +1 -0
- package/src/lib/utils/attr-target.d.ts +170 -0
- package/src/lib/utils/attr-target.js +229 -0
- package/src/lib/utils/attr-target.js.map +1 -0
- package/src/lib/utils/attribute-field-value.d.ts +21 -0
- package/src/lib/utils/attribute-field-value.js +28 -0
- package/src/lib/utils/attribute-field-value.js.map +1 -0
- package/src/lib/utils/canvas-context-menu.d.ts +28 -0
- package/src/lib/utils/canvas-context-menu.js +56 -0
- package/src/lib/utils/canvas-context-menu.js.map +1 -0
- package/src/lib/utils/canvas-reveal.d.ts +115 -0
- package/src/lib/utils/canvas-reveal.js +165 -0
- package/src/lib/utils/canvas-reveal.js.map +1 -0
- package/src/lib/utils/describe-element.d.ts +72 -0
- package/src/lib/utils/describe-element.js +159 -0
- package/src/lib/utils/describe-element.js.map +1 -0
- package/src/lib/utils/device-preview-styles.d.ts +89 -0
- package/src/lib/utils/device-preview-styles.js +228 -0
- package/src/lib/utils/device-preview-styles.js.map +1 -0
- package/src/lib/utils/docs-help.d.ts +22 -0
- package/src/lib/utils/docs-help.generated.d.ts +47 -0
- package/src/lib/utils/docs-help.generated.js +229 -0
- package/src/lib/utils/docs-help.generated.js.map +1 -0
- package/src/lib/utils/docs-help.js +37 -0
- package/src/lib/utils/docs-help.js.map +1 -0
- package/src/lib/utils/held-answers-store.d.ts +59 -0
- package/src/lib/utils/held-answers-store.js +94 -0
- package/src/lib/utils/held-answers-store.js.map +1 -0
- package/src/lib/utils/in-place-edit-surface.d.ts +118 -0
- package/src/lib/utils/in-place-edit-surface.js +164 -0
- package/src/lib/utils/in-place-edit-surface.js.map +1 -0
- package/src/lib/utils/inline-markdown-edit.store.d.ts +71 -0
- package/src/lib/utils/inline-markdown-edit.store.js +90 -0
- package/src/lib/utils/inline-markdown-edit.store.js.map +1 -0
- package/src/lib/utils/inline-text-edit.store.d.ts +85 -0
- package/src/lib/utils/inline-text-edit.store.js +78 -0
- package/src/lib/utils/inline-text-edit.store.js.map +1 -0
- package/src/lib/utils/instance-leaf-hit.d.ts +39 -0
- package/src/lib/utils/instance-leaf-hit.js +56 -0
- package/src/lib/utils/instance-leaf-hit.js.map +1 -0
- package/src/lib/utils/muted-classes.d.ts +76 -0
- package/src/lib/utils/muted-classes.js +108 -0
- package/src/lib/utils/muted-classes.js.map +1 -0
- package/src/lib/utils/muted-styles.d.ts +125 -0
- package/src/lib/utils/muted-styles.js +193 -0
- package/src/lib/utils/muted-styles.js.map +1 -0
- package/src/lib/utils/numeric-text-value.d.ts +42 -0
- package/src/lib/utils/numeric-text-value.js +47 -0
- package/src/lib/utils/numeric-text-value.js.map +1 -0
- package/src/lib/utils/preset-interactions.d.ts +37 -0
- package/src/lib/utils/preset-interactions.js +87 -0
- package/src/lib/utils/preset-interactions.js.map +1 -0
- package/src/lib/utils/publish-failure-message.d.ts +35 -0
- package/src/lib/utils/publish-failure-message.js +51 -0
- package/src/lib/utils/publish-failure-message.js.map +1 -0
- package/src/lib/utils/rank-picker-results.d.ts +45 -0
- package/src/lib/utils/rank-picker-results.js +136 -0
- package/src/lib/utils/rank-picker-results.js.map +1 -0
- package/src/lib/utils/responsive-sx.d.ts +65 -0
- package/src/lib/utils/responsive-sx.js +129 -0
- package/src/lib/utils/responsive-sx.js.map +1 -0
- package/src/lib/utils/rich-text-commands.d.ts +28 -0
- package/src/lib/utils/rich-text-commands.js +43 -0
- package/src/lib/utils/rich-text-commands.js.map +1 -0
- package/src/lib/utils/sanitize-rich-text.d.ts +52 -0
- package/src/lib/utils/sanitize-rich-text.js +173 -0
- package/src/lib/utils/sanitize-rich-text.js.map +1 -0
- package/src/lib/utils/state-sx.d.ts +255 -0
- package/src/lib/utils/state-sx.js +338 -0
- package/src/lib/utils/state-sx.js.map +1 -0
- package/src/lib/utils/style-field-groups.d.ts +136 -0
- package/src/lib/utils/style-field-groups.js +874 -0
- package/src/lib/utils/style-field-groups.js.map +1 -0
- package/src/lib/utils/style-field-search.d.ts +84 -0
- package/src/lib/utils/style-field-search.js +505 -0
- package/src/lib/utils/style-field-search.js.map +1 -0
- package/src/lib/utils/style-target.d.ts +69 -0
- package/src/lib/utils/style-target.js +203 -0
- package/src/lib/utils/style-target.js.map +1 -0
- package/src/lib/utils/theme-scale-options.d.ts +169 -0
- package/src/lib/utils/theme-scale-options.js +702 -0
- package/src/lib/utils/theme-scale-options.js.map +1 -0
- package/src/lib/utils/token-editable-dom.d.ts +82 -0
- package/src/lib/utils/token-editable-dom.js +280 -0
- package/src/lib/utils/token-editable-dom.js.map +1 -0
- package/src/lib/utils/token-segments.d.ts +111 -0
- package/src/lib/utils/token-segments.js +246 -0
- package/src/lib/utils/token-segments.js.map +1 -0
- package/src/lib/utils/visibility-styles.d.ts +38 -0
- package/src/lib/utils/visibility-styles.js +54 -0
- package/src/lib/utils/visibility-styles.js.map +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/besigner/feature/designer/src/lib/hooks/use-besigner-draft.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type * as Aglyn from '@aglyn/aglyn'\nimport { canvas, hasConcurrentWrite } from '@aglyn/aglyn'\nimport type { Firestore } from 'firebase/firestore'\nimport isEqual from 'lodash-es/isEqual.js'\nimport { autorun } from 'mobx'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport {\n type BesignerDraftIds,\n besignerDraftKey,\n clearBesignerDraft,\n pruneBesignerDrafts,\n readBesignerDraft,\n writeBesignerDraft,\n} from '../drafts/besigner-draft-store'\nimport {\n clearServerDraft,\n readServerDraft,\n} from '../drafts/besigner-server-draft'\nimport { useDebouncedCommit } from './use-debounced-commit'\n\n/**\n * Trailing debounce on the snapshot. Longer than the AGL-567 attribute\n * debounce because this is disk work, not model work, and nobody is waiting\n * on it: a snapshot one second behind the canvas is still a snapshot.\n */\nexport const DRAFT_WRITE_DEBOUNCE_MS = 1_000\n\n/**\n * …and an upper bound, because a trailing debounce alone never fires during\n * sustained activity. Dragging a section around for two minutes without a\n * one-second pause must not mean two minutes of unprotected work.\n */\nexport const DRAFT_WRITE_MAX_WAIT_MS = 15_000\n\n/**\n * Why a recovered draft may not be put back on the canvas (AGL-2486). Null\n * when restoring is a private act and safe to offer.\n */\nexport type BesignerDraftRestoreBlock =\n /**\n * Someone else SAVED this document after the draft was taken, so the draft\n * predates work that is now in the stored document.\n *\n * The only remaining verdict, and it applies only when this editor is\n * ALONE in the room: every shared case now withholds the whole prompt\n * rather than a button (see `roomIsShared`). A `'live-session'` member\n * used to sit beside this one and became unreachable the moment the\n * signal that produced it also suppressed the offer — so it is gone\n * rather than left as a branch nothing can enter.\n */\n 'saved-since'\n\n/** Which store an offered draft came from. See `BesignerDraftState.origin`. */\nexport type BesignerDraftOrigin = 'browser' | 'shared'\n\n/**\n * A draft on offer, from either store.\n *\n * `takenAt` is when the draft was WRITTEN, as its own store recorded it: the\n * crash net's `updatedAt`, or the shared draft's server stamp. Null when the\n * store did not say, and the banner then names no age rather than inventing\n * one (AGL-2868).\n */\ninterface DraftOffer {\n nodes: Aglyn.ProcessableNodes\n baseStamp: string | null\n takenAt: number | null\n}\n\nexport interface BesignerDraftState {\n /**\n * Unsaved work from a previous session is on hand and the author has not\n * decided about it yet. Undefined ids (no draft support wired) or no draft\n * both read as false.\n */\n available: boolean\n /**\n * When the offered draft was written, per its own store; null when there is\n * no offer, or when the store recorded no time.\n */\n takenAt: number | null\n /**\n * The stored document has moved on since the draft was taken — someone\n * else saved while it was stranded (AGL-674).\n */\n staleAgainstDocument: boolean\n /**\n * WHERE the offered draft came from, which is the difference between two\n * things the banner used to describe in one voice (AGL-2508).\n *\n * * `browser` — the local crash net. Work that was never saved, held in\n * this browser only, offered back after a reload or a crash.\n * * `shared` — the WORKING DRAFT on the server. The author pressed Save\n * draft, was told it saved, and it did: this is that document coming\n * back, from any browser, for anyone with access.\n *\n * Telling an author their deliberately saved draft was \"recovered from\n * this browser\" reads as a save that failed and a browser that rescued\n * them. It is the opposite on both counts.\n */\n origin: BesignerDraftOrigin\n /**\n * Why {@link restore} is withheld, or null when it is safe to offer\n * (AGL-2486). A blocked restore is a no-op, not merely an unrendered\n * button: the offer and the action have to agree.\n */\n restoreBlockedBy: BesignerDraftRestoreBlock | null\n /**\n * A SAVED draft exists that this session has neither opened nor discarded\n * (AGL-2874).\n *\n * True even while the offer is withheld from a shared room, which is why it\n * is not folded into {@link available}: what it protects is the draft, not\n * the prompt. A save or publish from a canvas that never took the draft in\n * must not delete it on the way past.\n */\n sharedDraftUnopened: boolean\n /** Puts the draft into the canvas as an undoable, unsaved change. */\n restore: () => void\n /** Drops the offer and the stored draft. */\n discard: () => void\n}\n\nconst EMPTY_STATE: BesignerDraftState = {\n available: false,\n takenAt: null,\n staleAgainstDocument: false,\n origin: 'browser',\n restoreBlockedBy: null,\n sharedDraftUnopened: false,\n restore: () => undefined,\n discard: () => undefined,\n}\n\n/**\n * How many OTHER editing sessions are in this room, from the point of view\n * of the recovery prompt — or null while that is not yet known (AGL-2486).\n *\n * Presence is the input, but its statuses do not map onto \"am I alone\" one\n * for one, and the difference decides whether a person who crashed gets\n * their work back:\n *\n * * `live` — the room is known. However many sessions it holds is the\n * answer, zero included.\n * * `idle` / `connecting` — NOT known yet. Null, so the prompt waits rather\n * than offering a Discard for the second before presence lands.\n * * `unauthorized` / `error` / `unconfigured` — presence cannot answer and\n * never will on this load. Zero, deliberately: a deployment with no\n * Realtime Database has no co-edit mirror either, so there is nothing to\n * have already restored the work, and withholding recovery there would\n * break the crash net for every self-host that never configured presence\n * (`project_self_host_docker_byo_firebase`).\n */\nexport function recoverableRoomSessions(\n status: string,\n otherSessions: number,\n): number | null {\n if (status === 'live') return otherSessions\n if (status === 'idle' || status === 'connecting') return null\n return 0\n}\n\nexport interface UseBesignerDraftOptions {\n /** Omit to disable drafts for this document. */\n ids?: BesignerDraftIds\n /**\n * Omit to use the local crash net alone (AGL-1152).\n *\n * Given one, the SHARED working draft is consulted too and PREFERRED when\n * both exist. That ordering is the point rather than a tie-break: a local\n * draft is residue from a browser that stopped, while a server draft is work\n * somebody deliberately saved — possibly a colleague, possibly this person\n * on another machine. Offering the crash residue over it would hand back the\n * older of the two and look like data loss.\n */\n firestore?: Firestore\n /**\n * True once the stored document has been pushed into the canvas. Nothing\n * is read or written before this: writing earlier would snapshot an empty\n * canvas over a real draft, and reading earlier could not tell \"no draft\"\n * from \"not loaded yet\".\n */\n loaded: boolean\n /** The canvas differs from the last agreed state. */\n dirty: boolean\n /** Current stored-document stamp, in `Aglyn.versionStamp` form. */\n storedStamp: string | null\n /**\n * The stored document's node map, in stored shape (AGL-2874).\n *\n * Lets a draft tell a write that moved the TREE from one that only moved\n * the stamp — a component's declared properties, a version's layout\n * binding. Only the first makes restoring a draft a rollback. Omitted, every\n * stamp change counts, which is the conservative answer.\n */\n storedNodes?: unknown\n /**\n * Other live editing sessions in this room, or null while unknown — see\n * {@link recoverableRoomSessions}, which is how a caller with a presence\n * state derives it (AGL-2486).\n *\n * Omitted means ZERO, not unknown: an editor with no presence at all (the\n * system-email besigner has no honest orgId for a room) is a single-player\n * session by construction, and must keep its crash net.\n */\n roomSessions?: number | null\n}\n\n/**\n * Keeps a single local snapshot of unsaved canvas work per document, and\n * offers it back after a crash (AGL-1256).\n *\n * See `besigner-draft-store` for why this is a crash net and not version\n * history — in particular why the draft is destroyed the instant a save\n * succeeds, which is the property that keeps it from being a free stand-in\n * for the paid versioning feature.\n *\n * **Two tabs on one document write one draft key, last writer wins.** That\n * is deliberate: both tabs are editing the same version and the newest\n * unsaved state is the one worth recovering. Nothing here tries to merge\n * them; merging whole node maps is AGL-677's problem, not a crash net's.\n *\n * ## Where this sits next to live co-editing (AGL-2486)\n *\n * There are two \"unsaved\" states in a besigner editor and they have\n * different scopes, which is why the prompt says \"from this browser\" out\n * loud:\n *\n * * The **co-edit mirror** (AGL-677) is the SHARED one. It carries unsaved\n * work per node between sessions and replays it to whoever joins, so\n * opening a document someone else is editing already shows their\n * in-progress state — and shows you your own again after a crash, before\n * this prompt is read.\n * * This draft is the PRIVATE one: one snapshot, in one browser, for the\n * case where nothing else survived.\n *\n * They are not merged and this one is not made shareable — the mirror\n * already is that, per node, without a merge of two whole maps.\n *\n * The private snapshot is not OFFERED at all while the room is shared\n * (AGL-2486). Withholding only Restore is not enough: that leaves a prompt\n * whose one remaining button is Discard, standing over work several people\n * are still doing. Recovery is a crash story, a crash means nothing else\n * survived, and a live room is proof that something did. See\n * `roomIsShared` in the body for the two signals that decide it, and\n * {@link BesignerDraftState.restoreBlockedBy} for the narrower verdicts that\n * still apply when this editor is alone.\n */\nexport function useBesignerDraft(\n options: UseBesignerDraftOptions,\n): BesignerDraftState {\n const { ids, loaded, dirty, storedStamp, storedNodes, firestore } = options\n // `??` would fold null into 0 and lose the distinction the whole rule\n // rests on: null is \"presence has not answered yet\", 0 is \"it answered,\n // and you are alone\".\n const roomSessions =\n options.roomSessions === undefined ? 0 : options.roomSessions\n const key = ids ? besignerDraftKey(ids) : null\n\n /** The draft being offered, held in memory for the life of the offer. */\n const [offer, setOffer] = useState<DraftOffer | null>(null)\n /**\n * Which store {@link offer} came from (AGL-2508). Tracked beside the offer\n * rather than folded into `BesignerDraft`, because the stored shape is what\n * both stores persist and neither one records where it ended up being read\n * from — that is a fact about this load, not about the document.\n */\n const [offerOrigin, setOfferOrigin] = useState<BesignerDraftOrigin>('browser')\n /** Guards the read so it happens exactly once per document, before writes. */\n const readKeyRef = useRef<string | null>(null)\n /**\n * The same guard for the SHARED draft, kept apart from the local one\n * because the two become readable at different moments (AGL-2874).\n *\n * An editor passes `firestore` only once its PARENT document has said that\n * this is the live version, and the version document can reach the canvas\n * before that. With one latch for both, the local read takes it and the\n * shared draft is never read that session — no offer, and nothing standing\n * between the draft and a save that overwrites it.\n */\n const serverReadKeyRef = useRef<string | null>(null)\n const idsRef = useRef(ids)\n idsRef.current = ids\n const storedStampRef = useRef(storedStamp)\n storedStampRef.current = storedStamp\n /**\n * Whether this session has ever written the draft slot. Without it the\n * clean-canvas cleanup below would delete the draft it was just offered:\n * a freshly loaded document is clean by definition, so \"clean\" only means\n * \"nothing left to protect\" once we have actually put something there.\n */\n const wroteRef = useRef(false)\n\n const writeDraft = useCallback(() => {\n const currentIds = idsRef.current\n if (!currentIds) return\n wroteRef.current = true\n // Serialising here rather than in the autorun is the whole point of the\n // debounce: the tracking read is cheap to repeat, `JSON.stringify` of a\n // 200-node map plus a synchronous `setItem` is not (AGL-567).\n const nodes = canvas.toJSON().nodes as Aglyn.ProcessableNodes\n writeBesignerDraft(currentIds, {\n nodes,\n baseStamp: storedStampRef.current,\n })\n }, [])\n\n const { schedule, flush } = useDebouncedCommit(\n writeDraft,\n DRAFT_WRITE_DEBOUNCE_MS,\n { maxWait: DRAFT_WRITE_MAX_WAIT_MS },\n )\n\n // Read the existing draft once the document is in the canvas, and before\n // anything can overwrite it.\n // Keyed on the storage key rather than the ids object: callers build that\n // object inline, so a dependency on it would re-run this on every render.\n useEffect(() => {\n const currentIds = idsRef.current\n if (!key || !currentIds || !loaded) return undefined\n if (readKeyRef.current !== key) {\n readKeyRef.current = key\n pruneBesignerDrafts()\n const local = readBesignerDraft(currentIds)\n setOffer(\n local\n ? {\n nodes: local.nodes,\n baseStamp: local.baseStamp,\n takenAt: local.updatedAt ?? null,\n }\n : null,\n )\n setOfferOrigin('browser')\n }\n // The shared draft answers late, and wins when it answers. It is read at\n // most once per document too, so a slow reply cannot re-offer a draft\n // the author has meanwhile restored or discarded.\n if (!firestore || serverReadKeyRef.current === key) return undefined\n serverReadKeyRef.current = key\n let live = true\n let answered = false\n void readServerDraft(firestore, currentIds)\n .then((server) => {\n answered = true\n if (!live || !server) return\n setOfferOrigin('shared')\n setOffer({\n nodes: server.nodes,\n baseStamp: server.baseStamp,\n // The server's stamp, so the banner tells the draft's real age.\n // Expiry never reads it: `DRAFT_MAX_AGE_MS` belongs to the crash\n // net, and a saved draft lives until it is published or discarded.\n takenAt: server.updatedAt,\n })\n })\n .catch(() => {\n answered = true\n })\n return () => {\n live = false\n // A read torn down before it answered never happened, so the next run\n // asks again rather than finding the latch already taken.\n if (!answered) serverReadKeyRef.current = null\n }\n }, [key, loaded, firestore])\n\n // Snapshot on canvas change. `autorun` re-runs whenever anything the\n // tracked read touched changes, and `toJSON()` touches every node — which\n // is the same depth of read the existing `isInitialSame` computed already\n // performs on every change, so this adds a constant factor to a path that\n // is already O(nodes), not a new order of cost.\n useEffect(() => {\n if (!key || !loaded) return undefined\n return autorun(() => {\n canvas.toJSON()\n if (canvas.isInitialSame) return\n schedule()\n })\n }, [key, loaded, schedule])\n\n // Returning to the saved state (an undo back to it, or a save landing)\n // means there is no unsaved work left IN THIS BROWSER to protect. Dropping\n // the crash net here is what stops it lingering as a restore point for a\n // document that has moved past it.\n //\n // The SHARED draft is not this effect's to drop (AGL-2874). It is work\n // somebody deliberately saved, and a canvas back at the stored document says\n // nothing about it: edit anything and undo it, and this runs with the shared\n // draft still unopened. Deciding about it belongs to Discard, and to a save\n // or publish of a canvas that took the draft in.\n useEffect(() => {\n const currentIds = idsRef.current\n if (!key || !currentIds || !loaded || dirty) return\n if (!wroteRef.current) return\n // Flush before clearing, not after: a timer left pending would land\n // *after* the delete and re-create a draft whose content is identical to\n // the saved document — offered on the next load as unsaved work that\n // never existed.\n flush()\n clearBesignerDraft(currentIds)\n wroteRef.current = false\n }, [key, loaded, dirty, flush])\n\n // A crash is not the only way a tab dies; closing it is the common one,\n // and `pagehide` is the last reliable moment to get the pending snapshot\n // onto disk. `visibilitychange` covers the mobile/background case where\n // `pagehide` may never fire.\n useEffect(() => {\n if (!key || !loaded) return undefined\n const flushNow = () => {\n if (!canvas.isInitialSame) flush()\n }\n const onVisibility = () => {\n if (document.visibilityState === 'hidden') flushNow()\n }\n window.addEventListener('pagehide', flushNow)\n document.addEventListener('visibilitychange', onVisibility)\n return () => {\n window.removeEventListener('pagehide', flushNow)\n document.removeEventListener('visibilitychange', onVisibility)\n }\n }, [key, loaded, flush])\n\n /**\n * A peer's live change is on this canvas. Read OUTSIDE the memo below so\n * the observer that renders the prompt re-runs it when the first remote\n * change lands — a mobx read inside a `useMemo` whose deps never mention\n * it would keep serving the verdict from mount.\n */\n const canvasHasRemoteEdits = canvas.hasRemoteEdits\n /**\n * Every stamp the stored document has carried this session while its tree\n * stayed exactly as it is now (AGL-2874).\n *\n * A write that leaves `nodes` alone still moves `updatedAt` — declaring a\n * component's properties is one — and a draft judged on the stamp alone\n * becomes unrestorable the moment an author does that, though putting it\n * back could roll nothing back. A draft taken against any stamp in this set\n * is a draft of this very tree. Reset whenever the tree itself changes, so\n * someone else's saved nodes still block it.\n *\n * Kept in a ref and brought up to date during render: the answer has to be\n * right in the same render the new stamp arrives in, or the banner shows a\n * blocked draft for a frame. Adding a stamp twice changes nothing, so a\n * repeated render lands in the same place.\n */\n const sameTreeStampsRef = useRef<\n { nodes: unknown; stamps: Set<string> } | undefined\n >(undefined)\n if (storedNodes !== undefined && storedStamp) {\n const current = sameTreeStampsRef.current\n if (\n !current ||\n (current.nodes !== storedNodes && !isEqual(current.nodes, storedNodes))\n ) {\n sameTreeStampsRef.current = {\n nodes: storedNodes,\n stamps: new Set([storedStamp]),\n }\n } else {\n current.nodes = storedNodes\n current.stamps.add(storedStamp)\n }\n }\n const draftOfThisTree = Boolean(\n offer?.baseStamp && sameTreeStampsRef.current?.stamps.has(offer.baseStamp),\n )\n const staleAgainstDocument = Boolean(\n offer &&\n hasConcurrentWrite(offer.baseStamp, storedStamp) &&\n !draftOfThisTree,\n )\n /**\n * Restoring is a WHOLE-MAP replace, and in a shared session that is not a\n * private act (AGL-2486). Measured on the running editor: a peer created a\n * node, the other session pressed Restore, and the node was deleted on the\n * peer's own screen; with a stale draft the same click reverted a\n * colleague's SAVED work and survived the reload the conflict banner asks\n * for, with Save re-enabled and no warning left on screen.\n *\n * So the offer is withheld rather than merely re-worded. What the author\n * loses by that is small and the mirror mostly covers it: unsaved work in a\n * co-edited room is republished to whoever rejoins, which is how their own\n * changes are already back on the canvas before this prompt is even read.\n * What they would lose by the other choice is someone else's work.\n */\n const restoreBlockedBy: BesignerDraftRestoreBlock | null =\n staleAgainstDocument ? 'saved-since' : null\n\n /**\n * Whether this room is somebody else's too, i.e. whether the mirror\n * already has the work this prompt is about (AGL-2486).\n *\n * The recovery prompt exists for a CRASH, and a crash is the case where\n * nothing else survived. When something else demonstrably did — a third tab\n * opening onto a document two other tabs are editing unsaved — there is no\n * recovery to offer, and both buttons can only do harm to work that is\n * still in progress.\n *\n * TWO signals, OR'd, because they cover different halves of \"the mirror\n * already has this\" and neither implies the other:\n *\n * * **Presence membership.** Another session is in the room right now. It\n * may not have published anything since this tab joined — a colleague\n * who is reading rather than typing publishes nothing — but the mirror\n * is live and their unsaved work is in it, so a whole-map replace from\n * an older private snapshot would still be a replace of shared state.\n * * **Mirror-entry presence** (`canvas.hasRemoteEdits`). The mirror has\n * already replayed another session's unsaved work ONTO this canvas.\n * This is the half that catches the room whose other sessions have since\n * closed: their work came back on join, and it is on screen now.\n *\n * Presence alone misses the second; `hasRemoteEdits` alone misses the\n * first, which is any tab that joins a live room before anyone in it types\n * again. Unknown (null) counts as shared until presence says otherwise —\n * the cost of waiting a second is a delayed offer, the cost of guessing\n * wrong is a Discard button over other people's work.\n *\n * What is NOT withheld: a person alone in the room after a browser quit\n * gets the full offer, Restore included. That case is the entire reason\n * the feature exists, and nothing here narrows it.\n */\n const roomIsShared =\n roomSessions === null || roomSessions > 0 || canvasHasRemoteEdits\n\n const restore = useCallback(() => {\n const draft = offer\n if (!draft) return\n // The action agrees with the offer. A component that renders Restore\n // without consulting `restoreBlockedBy` still cannot roll a colleague\n // back through this path.\n if (restoreBlockedBy || roomIsShared) return\n // `applyNodes` snapshots history first, so restoring is undoable and\n // leaves the canvas dirty. Dirty matters beyond the save button: it is\n // what keeps \"Save the canvas before creating a version\" firing, so a\n // restored draft can never be swept into a version snapshot.\n canvas.applyNodes(draft.nodes)\n setOffer(null)\n }, [offer, restoreBlockedBy, roomIsShared])\n\n const discard = useCallback(() => {\n const currentIds = idsRef.current\n setOffer(null)\n if (!currentIds) return\n clearBesignerDraft(currentIds)\n // Whichever store the offer came from. The shared draft is PREFERRED over\n // the local one when both exist, so declining an offer that clearing only\n // localStorage cannot reach leaves the author declining the same thing on\n // every reload, with no way to stop being asked.\n if (firestore) void clearServerDraft(firestore, currentIds)\n }, [firestore])\n\n const sharedDraftUnopened = Boolean(offer && offerOrigin === 'shared')\n\n return useMemo(() => {\n if (!key || !offer) return EMPTY_STATE\n // No prompt at all in a shared room — not a re-worded one. The draft is\n // deliberately LEFT ON DISK: nothing about the room makes this browser's\n // snapshot wrong, only unofferable, and reaping it here would take away\n // the crash net of the very tab that is still holding the work.\n if (roomIsShared) {\n return sharedDraftUnopened\n ? { ...EMPTY_STATE, sharedDraftUnopened }\n : EMPTY_STATE\n }\n return {\n available: true,\n takenAt: offer.takenAt,\n staleAgainstDocument,\n origin: offerOrigin,\n restoreBlockedBy,\n sharedDraftUnopened,\n restore,\n discard,\n }\n }, [\n key,\n offer,\n offerOrigin,\n roomIsShared,\n staleAgainstDocument,\n restoreBlockedBy,\n sharedDraftUnopened,\n restore,\n discard,\n ])\n}\n\nexport default useBesignerDraft\n"],"names":["canvas","hasConcurrentWrite","isEqual","autorun","useCallback","useEffect","useMemo","useRef","useState","besignerDraftKey","clearBesignerDraft","pruneBesignerDrafts","readBesignerDraft","writeBesignerDraft","clearServerDraft","readServerDraft","useDebouncedCommit","DRAFT_WRITE_DEBOUNCE_MS","DRAFT_WRITE_MAX_WAIT_MS","EMPTY_STATE","available","takenAt","staleAgainstDocument","origin","restoreBlockedBy","sharedDraftUnopened","restore","undefined","discard","recoverableRoomSessions","status","otherSessions","useBesignerDraft","options","sameTreeStampsRef","ids","loaded","dirty","storedStamp","storedNodes","firestore","roomSessions","key","offer","setOffer","offerOrigin","setOfferOrigin","readKeyRef","serverReadKeyRef","idsRef","current","storedStampRef","wroteRef","writeDraft","currentIds","nodes","toJSON","baseStamp","schedule","flush","maxWait","local","updatedAt","live","answered","then","server","catch","isInitialSame","flushNow","onVisibility","document","visibilityState","window","addEventListener","removeEventListener","canvasHasRemoteEdits","hasRemoteEdits","stamps","Set","add","draftOfThisTree","Boolean","has","roomIsShared","draft","applyNodes"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type * as Aglyn from '@aglyn/aglyn';
|
|
18
|
+
/**
|
|
19
|
+
* Copy the given nodes (or the current selection) to the besigner clipboard
|
|
20
|
+
* (AGL-1202), reporting the result the way the Add-element flow does.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useCopyElementsCallback(): (nodes?: Aglyn.NodeSchema<any>[]) => number;
|
|
23
|
+
/**
|
|
24
|
+
* Paste the clipboard at `target` (defaulting to the current selection) and
|
|
25
|
+
* select what landed. A rejected paste — unknown element, forbidden nesting,
|
|
26
|
+
* empty clipboard — writes nothing and explains itself in a snackbar.
|
|
27
|
+
*/
|
|
28
|
+
export declare function usePasteElementsCallback(): (target?: Aglyn.NodeSchema<any>) => Aglyn.NodeSchema<any>[];
|
|
29
|
+
export default useCopyElementsCallback;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import * as Besigner from "@aglyn/besigner";
|
|
17
|
+
import { useSnackbar } from "@aglyn/shared-ui-snackstack";
|
|
18
|
+
import { useCallback } from "react";
|
|
19
|
+
/**
|
|
20
|
+
* Copy the given nodes (or the current selection) to the besigner clipboard
|
|
21
|
+
* (AGL-1202), reporting the result the way the Add-element flow does.
|
|
22
|
+
*/ export function useCopyElementsCallback() {
|
|
23
|
+
var _useSnackbar;
|
|
24
|
+
// Null-safe: surfaces render without a snackbar provider in tests.
|
|
25
|
+
const { enqueueSnackbar } = (_useSnackbar = useSnackbar()) != null ? _useSnackbar : {};
|
|
26
|
+
return useCallback((nodes)=>{
|
|
27
|
+
const targets = (nodes == null ? void 0 : nodes.length) ? nodes : Besigner.focus.getSelected();
|
|
28
|
+
const count = Besigner.clipboard.copyNodes(targets);
|
|
29
|
+
if (!count) {
|
|
30
|
+
enqueueSnackbar == null ? void 0 : enqueueSnackbar('Nothing to copy', {
|
|
31
|
+
variant: 'warning',
|
|
32
|
+
persist: false
|
|
33
|
+
});
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
36
|
+
const labels = Besigner.clipboard.getLabels();
|
|
37
|
+
enqueueSnackbar == null ? void 0 : enqueueSnackbar(count === 1 ? `Copied ${labels[0]}` : `Copied ${count} elements`, {
|
|
38
|
+
variant: 'success',
|
|
39
|
+
persist: false
|
|
40
|
+
});
|
|
41
|
+
return count;
|
|
42
|
+
}, [
|
|
43
|
+
enqueueSnackbar
|
|
44
|
+
]);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Paste the clipboard at `target` (defaulting to the current selection) and
|
|
48
|
+
* select what landed. A rejected paste — unknown element, forbidden nesting,
|
|
49
|
+
* empty clipboard — writes nothing and explains itself in a snackbar.
|
|
50
|
+
*/ export function usePasteElementsCallback() {
|
|
51
|
+
var _useSnackbar;
|
|
52
|
+
const { enqueueSnackbar } = (_useSnackbar = useSnackbar()) != null ? _useSnackbar : {};
|
|
53
|
+
return useCallback((target)=>{
|
|
54
|
+
var _ref;
|
|
55
|
+
var _result_nodes_;
|
|
56
|
+
const into = target != null ? target : Besigner.focus.getLastSelected();
|
|
57
|
+
const result = Besigner.clipboard.pasteInto(into);
|
|
58
|
+
if (result.error) {
|
|
59
|
+
enqueueSnackbar == null ? void 0 : enqueueSnackbar(result.error, {
|
|
60
|
+
variant: 'warning',
|
|
61
|
+
persist: false
|
|
62
|
+
});
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
// Select what was pasted so the next action (retext, restyle, move)
|
|
66
|
+
// acts on it, matching what Add element does.
|
|
67
|
+
Besigner.focus.clearSelection();
|
|
68
|
+
for (const node of result.nodes){
|
|
69
|
+
Besigner.focus.setSelectedNode(node, true);
|
|
70
|
+
}
|
|
71
|
+
enqueueSnackbar == null ? void 0 : enqueueSnackbar(result.nodes.length === 1 ? `Pasted ${(_ref = (_result_nodes_ = result.nodes[0]) == null ? void 0 : _result_nodes_.labelShort) != null ? _ref : 'element'}` : `Pasted ${result.nodes.length} elements`, {
|
|
72
|
+
variant: 'success',
|
|
73
|
+
persist: false
|
|
74
|
+
});
|
|
75
|
+
return result.nodes;
|
|
76
|
+
}, [
|
|
77
|
+
enqueueSnackbar
|
|
78
|
+
]);
|
|
79
|
+
}
|
|
80
|
+
export default useCopyElementsCallback;
|
|
81
|
+
|
|
82
|
+
//# sourceMappingURL=use-clipboard-callbacks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/besigner/feature/designer/src/lib/hooks/use-clipboard-callbacks.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type * as Aglyn from '@aglyn/aglyn'\nimport * as Besigner from '@aglyn/besigner'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useCallback } from 'react'\n\n/**\n * Copy the given nodes (or the current selection) to the besigner clipboard\n * (AGL-1202), reporting the result the way the Add-element flow does.\n */\nexport function useCopyElementsCallback(): (\n nodes?: Aglyn.NodeSchema<any>[],\n) => number {\n // Null-safe: surfaces render without a snackbar provider in tests.\n const { enqueueSnackbar } = useSnackbar() ?? {}\n\n return useCallback(\n (nodes?: Aglyn.NodeSchema<any>[]) => {\n const targets = nodes?.length ? nodes : Besigner.focus.getSelected()\n const count = Besigner.clipboard.copyNodes(targets)\n if (!count) {\n enqueueSnackbar?.('Nothing to copy', {\n variant: 'warning',\n persist: false,\n })\n return 0\n }\n const labels = Besigner.clipboard.getLabels()\n enqueueSnackbar?.(\n count === 1 ? `Copied ${labels[0]}` : `Copied ${count} elements`,\n { variant: 'success', persist: false },\n )\n return count\n },\n [enqueueSnackbar],\n )\n}\n\n/**\n * Paste the clipboard at `target` (defaulting to the current selection) and\n * select what landed. A rejected paste — unknown element, forbidden nesting,\n * empty clipboard — writes nothing and explains itself in a snackbar.\n */\nexport function usePasteElementsCallback(): (\n target?: Aglyn.NodeSchema<any>,\n) => Aglyn.NodeSchema<any>[] {\n const { enqueueSnackbar } = useSnackbar() ?? {}\n\n return useCallback(\n (target?: Aglyn.NodeSchema<any>) => {\n const into = target ?? Besigner.focus.getLastSelected()\n const result = Besigner.clipboard.pasteInto(into)\n if (result.error) {\n enqueueSnackbar?.(result.error, {\n variant: 'warning',\n persist: false,\n })\n return []\n }\n // Select what was pasted so the next action (retext, restyle, move)\n // acts on it, matching what Add element does.\n Besigner.focus.clearSelection()\n for (const node of result.nodes) {\n Besigner.focus.setSelectedNode(node, true)\n }\n enqueueSnackbar?.(\n result.nodes.length === 1\n ? `Pasted ${result.nodes[0]?.labelShort ?? 'element'}`\n : `Pasted ${result.nodes.length} elements`,\n { variant: 'success', persist: false },\n )\n return result.nodes\n },\n [enqueueSnackbar],\n )\n}\n\nexport default useCopyElementsCallback\n"],"names":["Besigner","useSnackbar","useCallback","useCopyElementsCallback","enqueueSnackbar","nodes","targets","length","focus","getSelected","count","clipboard","copyNodes","variant","persist","labels","getLabels","usePasteElementsCallback","target","result","into","getLastSelected","pasteInto","error","clearSelection","node","setSelectedNode","labelShort"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAGD,YAAYA,cAAc,kBAAiB;AAC3C,SAASC,WAAW,QAAQ,8BAA6B;AACzD,SAASC,WAAW,QAAQ,QAAO;AAEnC;;;CAGC,GACD,OAAO,SAASC;QAIcF;IAD5B,mEAAmE;IACnE,MAAM,EAAEG,eAAe,EAAE,IAAGH,eAAAA,yBAAAA,eAAiB,CAAC;IAE9C,OAAOC,YACL,CAACG;QACC,MAAMC,UAAUD,CAAAA,yBAAAA,MAAOE,MAAM,IAAGF,QAAQL,SAASQ,KAAK,CAACC,WAAW;QAClE,MAAMC,QAAQV,SAASW,SAAS,CAACC,SAAS,CAACN;QAC3C,IAAI,CAACI,OAAO;YACVN,mCAAAA,gBAAkB,mBAAmB;gBACnCS,SAAS;gBACTC,SAAS;YACX;YACA,OAAO;QACT;QACA,MAAMC,SAASf,SAASW,SAAS,CAACK,SAAS;QAC3CZ,mCAAAA,gBACEM,UAAU,IAAI,CAAC,OAAO,EAAEK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,OAAO,EAAEL,MAAM,SAAS,CAAC,EAChE;YAAEG,SAAS;YAAWC,SAAS;QAAM;QAEvC,OAAOJ;IACT,GACA;QAACN;KAAgB;AAErB;AAEA;;;;CAIC,GACD,OAAO,SAASa;QAGchB;IAA5B,MAAM,EAAEG,eAAe,EAAE,IAAGH,eAAAA,yBAAAA,eAAiB,CAAC;IAE9C,OAAOC,YACL,CAACgB;;YAkBiBC;QAjBhB,MAAMC,OAAOF,iBAAAA,SAAUlB,SAASQ,KAAK,CAACa,eAAe;QACrD,MAAMF,SAASnB,SAASW,SAAS,CAACW,SAAS,CAACF;QAC5C,IAAID,OAAOI,KAAK,EAAE;YAChBnB,mCAAAA,gBAAkBe,OAAOI,KAAK,EAAE;gBAC9BV,SAAS;gBACTC,SAAS;YACX;YACA,OAAO,EAAE;QACX;QACA,oEAAoE;QACpE,8CAA8C;QAC9Cd,SAASQ,KAAK,CAACgB,cAAc;QAC7B,KAAK,MAAMC,QAAQN,OAAOd,KAAK,CAAE;YAC/BL,SAASQ,KAAK,CAACkB,eAAe,CAACD,MAAM;QACvC;QACArB,mCAAAA,gBACEe,OAAOd,KAAK,CAACE,MAAM,KAAK,IACpB,CAAC,OAAO,WAAEY,iBAAAA,OAAOd,KAAK,CAAC,EAAE,qBAAfc,eAAiBQ,UAAU,mBAAI,WAAW,GACpD,CAAC,OAAO,EAAER,OAAOd,KAAK,CAACE,MAAM,CAAC,SAAS,CAAC,EAC5C;YAAEM,SAAS;YAAWC,SAAS;QAAM;QAEvC,OAAOK,OAAOd,KAAK;IACrB,GACA;QAACD;KAAgB;AAErB;AAEA,eAAeD,wBAAuB"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* True when the keystroke belongs to text the user is editing, not to the
|
|
19
|
+
* element tree — in which case the browser's own copy/paste must win.
|
|
20
|
+
*
|
|
21
|
+
* Three surfaces have to be recognised. The attributes panel, every dialog
|
|
22
|
+
* and the raw JSON editor live in the light DOM, so the event target and
|
|
23
|
+
* `document.activeElement` identify them — that is
|
|
24
|
+
* {@link Besigner.isTextEntryElement} / {@link Besigner.isTextEntryFocused},
|
|
25
|
+
* which enumerate the real surfaces (fields, `contenteditable`, ARIA text
|
|
26
|
+
* roles, and **Monaco**, whose focused node under the EditContext API is a
|
|
27
|
+
* bare `<div class="native-edit-context">`; missing it is what made Cmd+C in
|
|
28
|
+
* the raw JSON editor copy canvas ELEMENTS — AGL-2211).
|
|
29
|
+
*
|
|
30
|
+
* The canvas is the exception: it renders into a CLOSED shadow root, so a
|
|
31
|
+
* keydown from an inline text editor retargets to the shadow host and looks
|
|
32
|
+
* exactly like "the canvas has focus". The inline editor's own store is the
|
|
33
|
+
* only honest signal there, so it is consulted directly.
|
|
34
|
+
*
|
|
35
|
+
* The in-place markdown editor (AGL-1624) needs the same treatment for a
|
|
36
|
+
* second reason: its toolbar buttons, link popover and URL dialog take focus,
|
|
37
|
+
* so `document.activeElement` is often a BUTTON while the author is very much
|
|
38
|
+
* editing a document — and Cmd+C would then copy canvas ELEMENTS.
|
|
39
|
+
*
|
|
40
|
+
* @param target - Optional event target — the keydown's own target, which is
|
|
41
|
+
* more precise than `activeElement` and is checked first.
|
|
42
|
+
*/
|
|
43
|
+
export declare function isEditingText(target?: EventTarget | null): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Cmd/Ctrl+C and Cmd/Ctrl+V for canvas elements (AGL-1202), plus Cmd/Ctrl+A
|
|
46
|
+
* for the same-depth "select all" the focus manager already implements.
|
|
47
|
+
*
|
|
48
|
+
* Mounted once by the besigner root provider, so every besigner surface
|
|
49
|
+
* (screens, layouts, components, templates, emails) gets the same shortcuts.
|
|
50
|
+
*/
|
|
51
|
+
export declare function useClipboardShortcuts(): void;
|
|
52
|
+
export default useClipboardShortcuts;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import * as Besigner from "@aglyn/besigner";
|
|
17
|
+
import { useEffect } from "react";
|
|
18
|
+
import { inlineMarkdownEdit } from "../utils/inline-markdown-edit.store.js";
|
|
19
|
+
import { inlineTextEdit } from "../utils/inline-text-edit.store.js";
|
|
20
|
+
import { useCopyElementsCallback, usePasteElementsCallback } from "./use-clipboard-callbacks.js";
|
|
21
|
+
/**
|
|
22
|
+
* True when the keystroke belongs to text the user is editing, not to the
|
|
23
|
+
* element tree — in which case the browser's own copy/paste must win.
|
|
24
|
+
*
|
|
25
|
+
* Three surfaces have to be recognised. The attributes panel, every dialog
|
|
26
|
+
* and the raw JSON editor live in the light DOM, so the event target and
|
|
27
|
+
* `document.activeElement` identify them — that is
|
|
28
|
+
* {@link Besigner.isTextEntryElement} / {@link Besigner.isTextEntryFocused},
|
|
29
|
+
* which enumerate the real surfaces (fields, `contenteditable`, ARIA text
|
|
30
|
+
* roles, and **Monaco**, whose focused node under the EditContext API is a
|
|
31
|
+
* bare `<div class="native-edit-context">`; missing it is what made Cmd+C in
|
|
32
|
+
* the raw JSON editor copy canvas ELEMENTS — AGL-2211).
|
|
33
|
+
*
|
|
34
|
+
* The canvas is the exception: it renders into a CLOSED shadow root, so a
|
|
35
|
+
* keydown from an inline text editor retargets to the shadow host and looks
|
|
36
|
+
* exactly like "the canvas has focus". The inline editor's own store is the
|
|
37
|
+
* only honest signal there, so it is consulted directly.
|
|
38
|
+
*
|
|
39
|
+
* The in-place markdown editor (AGL-1624) needs the same treatment for a
|
|
40
|
+
* second reason: its toolbar buttons, link popover and URL dialog take focus,
|
|
41
|
+
* so `document.activeElement` is often a BUTTON while the author is very much
|
|
42
|
+
* editing a document — and Cmd+C would then copy canvas ELEMENTS.
|
|
43
|
+
*
|
|
44
|
+
* @param target - Optional event target — the keydown's own target, which is
|
|
45
|
+
* more precise than `activeElement` and is checked first.
|
|
46
|
+
*/ export function isEditingText(target) {
|
|
47
|
+
if (inlineTextEdit.node) return true;
|
|
48
|
+
if (inlineMarkdownEdit.node) return true;
|
|
49
|
+
if (Besigner.isTextEntryElement(target)) return true;
|
|
50
|
+
if (Besigner.isTextEntryFocused()) return true;
|
|
51
|
+
if (!document.activeElement) return false;
|
|
52
|
+
// A real text selection means the user is copying words, not structure.
|
|
53
|
+
const selection = window.getSelection();
|
|
54
|
+
return !!selection && !selection.isCollapsed && !!selection.toString();
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Cmd/Ctrl+C and Cmd/Ctrl+V for canvas elements (AGL-1202), plus Cmd/Ctrl+A
|
|
58
|
+
* for the same-depth "select all" the focus manager already implements.
|
|
59
|
+
*
|
|
60
|
+
* Mounted once by the besigner root provider, so every besigner surface
|
|
61
|
+
* (screens, layouts, components, templates, emails) gets the same shortcuts.
|
|
62
|
+
*/ export function useClipboardShortcuts() {
|
|
63
|
+
const copyElements = useCopyElementsCallback();
|
|
64
|
+
const pasteElements = usePasteElementsCallback();
|
|
65
|
+
useEffect(()=>{
|
|
66
|
+
const onKeyDown = (event)=>{
|
|
67
|
+
if (event.defaultPrevented) return;
|
|
68
|
+
if (!(event.metaKey || event.ctrlKey) || event.altKey) return;
|
|
69
|
+
const key = event.key.toLowerCase();
|
|
70
|
+
if (key !== 'c' && key !== 'v' && key !== 'a') return;
|
|
71
|
+
if (isEditingText(event.target)) return;
|
|
72
|
+
if (key === 'a') {
|
|
73
|
+
// Nothing selected means no anchor to pick a depth from, and
|
|
74
|
+
// swallowing Cmd+A then would break select-all everywhere else.
|
|
75
|
+
if (!Besigner.focus.getSelected().length) return;
|
|
76
|
+
event.preventDefault();
|
|
77
|
+
Besigner.focus.selectAllAtDepth();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (key === 'c') {
|
|
81
|
+
const selected = Besigner.focus.getSelected();
|
|
82
|
+
if (!selected.length) return;
|
|
83
|
+
event.preventDefault();
|
|
84
|
+
copyElements(selected);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// Paste targets the current selection; with nothing selected the
|
|
88
|
+
// canvas root receives it, matching Add element.
|
|
89
|
+
if (!Besigner.clipboard.hasContent()) return;
|
|
90
|
+
event.preventDefault();
|
|
91
|
+
pasteElements(Besigner.focus.getLastSelected());
|
|
92
|
+
};
|
|
93
|
+
window.addEventListener('keydown', onKeyDown);
|
|
94
|
+
return ()=>window.removeEventListener('keydown', onKeyDown);
|
|
95
|
+
}, [
|
|
96
|
+
copyElements,
|
|
97
|
+
pasteElements
|
|
98
|
+
]);
|
|
99
|
+
}
|
|
100
|
+
export default useClipboardShortcuts;
|
|
101
|
+
|
|
102
|
+
//# sourceMappingURL=use-clipboard-shortcuts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/besigner/feature/designer/src/lib/hooks/use-clipboard-shortcuts.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Besigner from '@aglyn/besigner'\nimport { useEffect } from 'react'\nimport { inlineMarkdownEdit } from '../utils/inline-markdown-edit.store'\nimport { inlineTextEdit } from '../utils/inline-text-edit.store'\nimport {\n useCopyElementsCallback,\n usePasteElementsCallback,\n} from './use-clipboard-callbacks'\n\n/**\n * True when the keystroke belongs to text the user is editing, not to the\n * element tree — in which case the browser's own copy/paste must win.\n *\n * Three surfaces have to be recognised. The attributes panel, every dialog\n * and the raw JSON editor live in the light DOM, so the event target and\n * `document.activeElement` identify them — that is\n * {@link Besigner.isTextEntryElement} / {@link Besigner.isTextEntryFocused},\n * which enumerate the real surfaces (fields, `contenteditable`, ARIA text\n * roles, and **Monaco**, whose focused node under the EditContext API is a\n * bare `<div class=\"native-edit-context\">`; missing it is what made Cmd+C in\n * the raw JSON editor copy canvas ELEMENTS — AGL-2211).\n *\n * The canvas is the exception: it renders into a CLOSED shadow root, so a\n * keydown from an inline text editor retargets to the shadow host and looks\n * exactly like \"the canvas has focus\". The inline editor's own store is the\n * only honest signal there, so it is consulted directly.\n *\n * The in-place markdown editor (AGL-1624) needs the same treatment for a\n * second reason: its toolbar buttons, link popover and URL dialog take focus,\n * so `document.activeElement` is often a BUTTON while the author is very much\n * editing a document — and Cmd+C would then copy canvas ELEMENTS.\n *\n * @param target - Optional event target — the keydown's own target, which is\n * more precise than `activeElement` and is checked first.\n */\nexport function isEditingText(target?: EventTarget | null): boolean {\n if (inlineTextEdit.node) return true\n if (inlineMarkdownEdit.node) return true\n\n if (Besigner.isTextEntryElement(target)) return true\n if (Besigner.isTextEntryFocused()) return true\n if (!document.activeElement) return false\n\n // A real text selection means the user is copying words, not structure.\n const selection = window.getSelection()\n return !!selection && !selection.isCollapsed && !!selection.toString()\n}\n\n/**\n * Cmd/Ctrl+C and Cmd/Ctrl+V for canvas elements (AGL-1202), plus Cmd/Ctrl+A\n * for the same-depth \"select all\" the focus manager already implements.\n *\n * Mounted once by the besigner root provider, so every besigner surface\n * (screens, layouts, components, templates, emails) gets the same shortcuts.\n */\nexport function useClipboardShortcuts(): void {\n const copyElements = useCopyElementsCallback()\n const pasteElements = usePasteElementsCallback()\n\n useEffect(() => {\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.defaultPrevented) return\n if (!(event.metaKey || event.ctrlKey) || event.altKey) return\n const key = event.key.toLowerCase()\n if (key !== 'c' && key !== 'v' && key !== 'a') return\n if (isEditingText(event.target)) return\n\n if (key === 'a') {\n // Nothing selected means no anchor to pick a depth from, and\n // swallowing Cmd+A then would break select-all everywhere else.\n if (!Besigner.focus.getSelected().length) return\n event.preventDefault()\n Besigner.focus.selectAllAtDepth()\n return\n }\n if (key === 'c') {\n const selected = Besigner.focus.getSelected()\n if (!selected.length) return\n event.preventDefault()\n copyElements(selected)\n return\n }\n // Paste targets the current selection; with nothing selected the\n // canvas root receives it, matching Add element.\n if (!Besigner.clipboard.hasContent()) return\n event.preventDefault()\n pasteElements(Besigner.focus.getLastSelected())\n }\n\n window.addEventListener('keydown', onKeyDown)\n return () => window.removeEventListener('keydown', onKeyDown)\n }, [copyElements, pasteElements])\n}\n\nexport default useClipboardShortcuts\n"],"names":["Besigner","useEffect","inlineMarkdownEdit","inlineTextEdit","useCopyElementsCallback","usePasteElementsCallback","isEditingText","target","node","isTextEntryElement","isTextEntryFocused","document","activeElement","selection","window","getSelection","isCollapsed","toString","useClipboardShortcuts","copyElements","pasteElements","onKeyDown","event","defaultPrevented","metaKey","ctrlKey","altKey","key","toLowerCase","focus","getSelected","length","preventDefault","selectAllAtDepth","selected","clipboard","hasContent","getLastSelected","addEventListener","removeEventListener"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,cAAc,kBAAiB;AAC3C,SAASC,SAAS,QAAQ,QAAO;AACjC,SAASC,kBAAkB,QAAQ,yCAAqC;AACxE,SAASC,cAAc,QAAQ,qCAAiC;AAChE,SACEC,uBAAuB,EACvBC,wBAAwB,QACnB,+BAA2B;AAElC;;;;;;;;;;;;;;;;;;;;;;;;;CAyBC,GACD,OAAO,SAASC,cAAcC,MAA2B;IACvD,IAAIJ,eAAeK,IAAI,EAAE,OAAO;IAChC,IAAIN,mBAAmBM,IAAI,EAAE,OAAO;IAEpC,IAAIR,SAASS,kBAAkB,CAACF,SAAS,OAAO;IAChD,IAAIP,SAASU,kBAAkB,IAAI,OAAO;IAC1C,IAAI,CAACC,SAASC,aAAa,EAAE,OAAO;IAEpC,wEAAwE;IACxE,MAAMC,YAAYC,OAAOC,YAAY;IACrC,OAAO,CAAC,CAACF,aAAa,CAACA,UAAUG,WAAW,IAAI,CAAC,CAACH,UAAUI,QAAQ;AACtE;AAEA;;;;;;CAMC,GACD,OAAO,SAASC;IACd,MAAMC,eAAef;IACrB,MAAMgB,gBAAgBf;IAEtBJ,UAAU;QACR,MAAMoB,YAAY,CAACC;YACjB,IAAIA,MAAMC,gBAAgB,EAAE;YAC5B,IAAI,CAAED,CAAAA,MAAME,OAAO,IAAIF,MAAMG,OAAO,AAAD,KAAMH,MAAMI,MAAM,EAAE;YACvD,MAAMC,MAAML,MAAMK,GAAG,CAACC,WAAW;YACjC,IAAID,QAAQ,OAAOA,QAAQ,OAAOA,QAAQ,KAAK;YAC/C,IAAIrB,cAAcgB,MAAMf,MAAM,GAAG;YAEjC,IAAIoB,QAAQ,KAAK;gBACf,6DAA6D;gBAC7D,gEAAgE;gBAChE,IAAI,CAAC3B,SAAS6B,KAAK,CAACC,WAAW,GAAGC,MAAM,EAAE;gBAC1CT,MAAMU,cAAc;gBACpBhC,SAAS6B,KAAK,CAACI,gBAAgB;gBAC/B;YACF;YACA,IAAIN,QAAQ,KAAK;gBACf,MAAMO,WAAWlC,SAAS6B,KAAK,CAACC,WAAW;gBAC3C,IAAI,CAACI,SAASH,MAAM,EAAE;gBACtBT,MAAMU,cAAc;gBACpBb,aAAae;gBACb;YACF;YACA,iEAAiE;YACjE,iDAAiD;YACjD,IAAI,CAAClC,SAASmC,SAAS,CAACC,UAAU,IAAI;YACtCd,MAAMU,cAAc;YACpBZ,cAAcpB,SAAS6B,KAAK,CAACQ,eAAe;QAC9C;QAEAvB,OAAOwB,gBAAgB,CAAC,WAAWjB;QACnC,OAAO,IAAMP,OAAOyB,mBAAmB,CAAC,WAAWlB;IACrD,GAAG;QAACF;QAAcC;KAAc;AAClC;AAEA,eAAeF,sBAAqB"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type * as Aglyn from '@aglyn/aglyn';
|
|
2
|
+
/** What happened to one definition this document renders. */
|
|
3
|
+
export type ComponentPropagationKind = 'updated' | 'appeared' | 'removed';
|
|
4
|
+
export interface ComponentPropagationChange {
|
|
5
|
+
/** Definition (host component) id. */
|
|
6
|
+
id: string;
|
|
7
|
+
/** Its display name when known, else the id — never blank. */
|
|
8
|
+
name: string;
|
|
9
|
+
kind: ComponentPropagationKind;
|
|
10
|
+
}
|
|
11
|
+
export interface ComponentPropagationNoticeOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Node maps this canvas draws. More than one because the component an
|
|
14
|
+
* author edits is usually in the LAYOUT — a site nav reached from a
|
|
15
|
+
* screen — so a screen besigner passes its own nodes AND its bound
|
|
16
|
+
* layout's, and a surface with only one passes one.
|
|
17
|
+
*/
|
|
18
|
+
documents: (Record<string, unknown> | undefined | null)[];
|
|
19
|
+
/**
|
|
20
|
+
* The live host definitions map, `undefined` until the first snapshot
|
|
21
|
+
* settles. Loading and "this host has none" must not look the same here:
|
|
22
|
+
* treating loading as an empty map would report every component on the
|
|
23
|
+
* page as removed and then re-appeared.
|
|
24
|
+
*/
|
|
25
|
+
definitions: Record<string, Aglyn.ReusableComponentTree> | undefined;
|
|
26
|
+
/** Display names by definition id, for the copy. */
|
|
27
|
+
names?: Record<string, string | undefined> | undefined;
|
|
28
|
+
/** Called once per snapshot that actually changed something rendered here. */
|
|
29
|
+
onPropagated: (changes: ComponentPropagationChange[]) => void;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The changes one snapshot made to definitions THIS document renders
|
|
33
|
+
* (AGL-1898 phase 2), given the previous snapshot.
|
|
34
|
+
*
|
|
35
|
+
* Pulled out of the hook because the interesting rules are all here and all
|
|
36
|
+
* testable without a renderer:
|
|
37
|
+
*
|
|
38
|
+
* - **Only what this document renders.** The definitions map is per HOST, so
|
|
39
|
+
* it changes whenever any component in the site is published — most of
|
|
40
|
+
* them not on this page. The referenced set is transitive
|
|
41
|
+
* ({@link Aglyn.collectReferencedComponentIds}), because the shared
|
|
42
|
+
* component an author edits is usually reached through a nav or a card
|
|
43
|
+
* rather than placed on the screen itself. Filtering on direct instances
|
|
44
|
+
* would stay silent for exactly the case the feature exists for.
|
|
45
|
+
* - **Only what changed what is drawn.** Compared by render signature, not
|
|
46
|
+
* object identity: `useHostComponentDefinitions` rebuilds every definition
|
|
47
|
+
* object on every snapshot, so identity always differs and would report
|
|
48
|
+
* the whole page as changed each time a colleague published anything.
|
|
49
|
+
* - **A newly PLACED instance is not a change.** The baseline covers the
|
|
50
|
+
* whole map rather than the referenced subset, so dragging in a component
|
|
51
|
+
* that was already published reports nothing — its definition's signature
|
|
52
|
+
* never moved.
|
|
53
|
+
*
|
|
54
|
+
* The reference walk runs over previous ∪ current definitions so a component
|
|
55
|
+
* nested inside one that was just deleted is still reachable, rather than
|
|
56
|
+
* dropping out of the referenced set at the moment it most needs reporting.
|
|
57
|
+
*/
|
|
58
|
+
export declare function diffRenderedComponentDefinitions(options: {
|
|
59
|
+
documents: (Record<string, unknown> | undefined | null)[];
|
|
60
|
+
previous: Record<string, Aglyn.ReusableComponentTree>;
|
|
61
|
+
next: Record<string, Aglyn.ReusableComponentTree>;
|
|
62
|
+
names?: Record<string, string | undefined> | undefined;
|
|
63
|
+
}): ComponentPropagationChange[];
|
|
64
|
+
/**
|
|
65
|
+
* The line an author reads when a component they are using changed under
|
|
66
|
+
* them (AGL-1898 phase 2).
|
|
67
|
+
*
|
|
68
|
+
* It says the canvas ALREADY updated, in the past tense, because it has —
|
|
69
|
+
* the graft re-ran before this message could be composed. The loop's real
|
|
70
|
+
* weakness was never the transport but the silence: an author publishes in
|
|
71
|
+
* the component tab, switches back, and cannot tell a working propagation
|
|
72
|
+
* from a broken one, because a component edit is usually too small to spot.
|
|
73
|
+
*
|
|
74
|
+
* Named, not counted. "A component updated" sends the author hunting; the
|
|
75
|
+
* name tells them where to look, and is the only part of this message that
|
|
76
|
+
* does any work.
|
|
77
|
+
*/
|
|
78
|
+
export declare function describeComponentPropagation(changes: ComponentPropagationChange[]): string;
|
|
79
|
+
/**
|
|
80
|
+
* Tells an author when a component this document renders changed under them
|
|
81
|
+
* (AGL-1898 phase 2).
|
|
82
|
+
*
|
|
83
|
+
* Phase 2's transport already works: `useHostComponentDefinitions` is a live
|
|
84
|
+
* listener, so publishing a component re-grafts every open canvas rendering
|
|
85
|
+
* it. What was missing is any sign that it happened. This adds NO reads —
|
|
86
|
+
* it is derived entirely from the snapshot stream the canvas already
|
|
87
|
+
* consumes — and deliberately reports only PUBLISHED changes, because that
|
|
88
|
+
* is what the parent doc carries and what the live site serves.
|
|
89
|
+
*
|
|
90
|
+
* The first settle is silent. Recording a baseline rather than reporting
|
|
91
|
+
* against an empty map is what stops a page announcing its own components on
|
|
92
|
+
* load, and is the reason `definitions === undefined` is not treated as
|
|
93
|
+
* `{}`.
|
|
94
|
+
*/
|
|
95
|
+
export declare function useComponentPropagationNotice(options: ComponentPropagationNoticeOptions): void;
|
|
96
|
+
export default useComponentPropagationNotice;
|