@jini-ai/ui 0.5.4 → 0.5.6
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/CHANGELOG.md +10 -0
- package/dist/core/theme/validation.js +40 -38
- package/dist/features/a2ui/renderer.js +15 -13
- package/dist/features/about/react/components/AboutTab.js +64 -34
- package/dist/features/about/rules.d.ts +2 -2
- package/dist/features/about/rules.js +156 -129
- package/dist/features/admin-widgets/components/ImagePreviewModal.d.ts +4 -3
- package/dist/features/admin-widgets/components/ImagePreviewModal.js +6 -10
- package/dist/features/admin-widgets/components/InfoTip.d.ts +3 -2
- package/dist/features/admin-widgets/components/InfoTip.hooks.js +2 -0
- package/dist/features/admin-widgets/components/InfoTip.js +34 -7
- package/dist/features/admin-widgets/components/InfoTip.rules.d.ts +13 -0
- package/dist/features/admin-widgets/components/InfoTip.rules.js +43 -10
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.d.ts +30 -0
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.d.ts +40 -0
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.js +128 -0
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.js +18 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.d.ts +14 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.js +12 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.rules.d.ts +20 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.rules.js +21 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.d.ts +18 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.d.ts +16 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.js +45 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.js +18 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.rules.d.ts +32 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.rules.js +26 -0
- package/dist/features/admin-widgets/components/Select/Select.hooks.d.ts +21 -0
- package/dist/features/admin-widgets/components/Select/Select.hooks.js +24 -2
- package/dist/features/admin-widgets/components/Select/Select.js +4 -3
- package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.hooks.js +29 -14
- package/dist/features/admin-widgets/index.d.ts +14 -2
- package/dist/features/admin-widgets/index.js +7 -1
- package/dist/features/admin-widgets/styles/admin-widgets.css +1 -0
- package/dist/features/asset-grid/react/components/AssetCard.js +7 -1
- package/dist/features/asset-grid/react/components/AssetGrid.d.ts +1 -1
- package/dist/features/asset-grid/react/components/AssetGrid.js +38 -18
- package/dist/features/asset-grid/react/components/useAssetGridFilters.d.ts +21 -0
- package/dist/features/asset-grid/react/components/useAssetGridFilters.js +19 -0
- package/dist/features/asset-grid/react/hooks/useAssetGridKeyboardShortcuts.js +16 -11
- package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.d.ts +2 -0
- package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.js +35 -11
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.d.ts +6 -6
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.js +10 -129
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.d.ts +50 -0
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.js +95 -0
- package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.d.ts +56 -0
- package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.js +80 -0
- package/dist/features/asset-tree-browser/rules.js +40 -22
- package/dist/features/browser-chrome/rules.js +74 -70
- package/dist/features/charts/browser.js +11 -5
- package/dist/features/charts/react.js +2 -2
- package/dist/features/charts/table.js +2 -2
- package/dist/features/command-palette/react/hooks/useCommandPalette.js +3 -9
- package/dist/features/connectors/ConnectorsBrowser.d.ts +4 -4
- package/dist/features/connectors/ConnectorsBrowser.js +26 -14
- package/dist/features/connectors/components/ConnectorCard.js +67 -38
- package/dist/features/connectors/components/ConnectorDetailDrawer.js +62 -31
- package/dist/features/connectors/components/ConnectorGrid.js +11 -6
- package/dist/features/connectors/hooks/useConnectorAuthorization.js +6 -0
- package/dist/features/connectors/hooks/useConnectorCatalog.d.ts +2 -0
- package/dist/features/connectors/hooks/useConnectorCatalog.js +32 -13
- package/dist/features/connectors/hooks/useWiredConnectorsBrowser.d.ts +3 -3
- package/dist/features/connectors/rules.d.ts +8 -1
- package/dist/features/connectors/rules.js +42 -20
- package/dist/features/connectors/types.d.ts +11 -0
- package/dist/features/execution/constants.d.ts +4 -10
- package/dist/features/execution/constants.js +9 -94
- package/dist/features/execution/react/components/ByokProviderForm.d.ts +2 -4
- package/dist/features/execution/react/components/ByokProviderForm.js +140 -43
- package/dist/features/execution/react/components/ExecutionTab.d.ts +1 -2
- package/dist/features/execution/react/components/ExecutionTab.js +52 -30
- package/dist/features/execution/react/components/LocalCliAgentCard.js +12 -3
- package/dist/features/execution/react/components/LocalCliAgentList.js +14 -3
- package/dist/features/execution/react/components/LocalCliAgentSettings.js +14 -3
- package/dist/features/execution/react/hooks/useLocalCliAgentCard.js +54 -36
- package/dist/features/execution/react/hooks/useReasoningControl.d.ts +1 -1
- package/dist/features/execution/react/hooks/useReasoningControl.js +116 -75
- package/dist/features/execution/rules.d.ts +13 -2
- package/dist/features/execution/rules.js +51 -11
- package/dist/features/execution/types.d.ts +6 -42
- package/dist/features/execution/types.js +0 -17
- package/dist/features/external-mcp/react/components/ExternalMcpTab.d.ts +6 -2
- package/dist/features/external-mcp/react/components/ExternalMcpTab.js +9 -2
- package/dist/features/file-dropzone/react/components/FileDropzone.js +24 -25
- package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.d.ts +2 -2
- package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.js +35 -30
- package/dist/features/file-dropzone/react/components/FileDropzoneThumbnailGrid.js +30 -24
- package/dist/features/file-dropzone/react/components/FileDropzoneZone.d.ts +14 -0
- package/dist/features/file-dropzone/react/components/FileDropzoneZone.js +26 -0
- package/dist/features/html-editor/canvas-content-wrapper.js +13 -13
- package/dist/features/html-editor/canvas-embed-placeholders.js +46 -37
- package/dist/features/html-editor/css.js +76 -69
- package/dist/features/html-editor/editor-blocks.js +47 -33
- package/dist/features/html-editor/editor-serialization.js +6 -5
- package/dist/features/html-editor/protected-elements.js +39 -28
- package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.js +4 -4
- package/dist/features/html-editor/source-splice.js +51 -30
- package/dist/features/i18n/dictionaries/settings-dialog.ja.js +1 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +1 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +1 -0
- package/dist/features/i18n/locale.js +20 -16
- package/dist/features/iframe-pool/react/dom-sync.js +8 -6
- package/dist/features/integrations/react/components/CodexInstallToggleButton.js +19 -4
- package/dist/features/integrations/react/components/IntegrationsTab.js +60 -14
- package/dist/features/interactive-ui/interactive-ui.css +1 -1
- package/dist/features/interactive-ui/providers/native/data-table.manifest.d.ts +6 -6
- package/dist/features/interactive-ui/providers/shadcn/data-table.js +10 -7
- package/dist/features/interactive-ui/providers/shadcn/data-table.manifest.d.ts +6 -6
- package/dist/features/lexical-rich-text-editor/mention-parser.js +60 -57
- package/dist/features/lexical-rich-text-editor/react/hooks/useKeyboardCommands.js +33 -38
- package/dist/features/lexical-rich-text-editor/rules.js +87 -74
- package/dist/features/lexical-rich-text-editor/serialize.js +27 -25
- package/dist/features/mcp-ui/secret-card.js +111 -61
- package/dist/features/mcp-ui/surfaces/confirmation.js +39 -21
- package/dist/features/mcp-ui/surfaces/document.d.ts +1 -1
- package/dist/features/mcp-ui/surfaces/document.js +10 -2
- package/dist/features/mcp-ui/surfaces/fields.js +30 -50
- package/dist/features/mcp-ui/surfaces/form.d.ts +6 -0
- package/dist/features/mcp-ui/surfaces/form.js +43 -21
- package/dist/features/mcp-ui/surfaces/outcome.js +30 -20
- package/dist/features/mcp-ui/surfaces/text-input.js +33 -21
- package/dist/features/mcp-ui/surfaces/tokens.d.ts +6 -1
- package/dist/features/mcp-ui/surfaces/tokens.js +5 -2
- package/dist/features/media-providers/constants.d.ts +1 -0
- package/dist/features/media-providers/constants.js +2 -81
- package/dist/features/media-providers/index.d.ts +1 -1
- package/dist/features/media-providers/ports.d.ts +2 -0
- package/dist/features/media-providers/react/components/MediaProviderCard.d.ts +22 -0
- package/dist/features/media-providers/react/components/MediaProviderCard.js +50 -0
- package/dist/features/media-providers/react/components/MediaProvidersTab.d.ts +5 -6
- package/dist/features/media-providers/react/components/MediaProvidersTab.js +40 -72
- package/dist/features/media-providers/react/components/media-providers-tab-labels.d.ts +11 -0
- package/dist/features/media-providers/react/components/media-providers-tab-labels.js +57 -0
- package/dist/features/media-providers/react/hooks/useMediaProvidersTab.d.ts +4 -2
- package/dist/features/media-providers/react/hooks/useMediaProvidersTab.js +37 -23
- package/dist/features/media-providers/rules.js +17 -10
- package/dist/features/media-providers/types.d.ts +4 -22
- package/dist/features/media-providers/types.js +3 -8
- package/dist/features/memory/formatters.js +127 -92
- package/dist/features/memory/react/components/MemoryConnectedPanel.js +92 -36
- package/dist/features/memory/react/components/MemoryList.js +37 -18
- package/dist/features/memory/react/hooks/useMemoryExtractions.store.js +42 -36
- package/dist/features/notifications/react/components/NotificationsTab.js +28 -28
- package/dist/features/observability/long-task.d.ts +0 -12
- package/dist/features/observability/long-task.js +15 -19
- package/dist/features/observability/resource-error.d.ts +0 -10
- package/dist/features/observability/resource-error.js +18 -13
- package/dist/features/observability/stuck-run.js +10 -6
- package/dist/features/observability/telemetry-url.d.ts +6 -0
- package/dist/features/observability/telemetry-url.js +16 -0
- package/dist/features/observability/white-screen.js +12 -24
- package/dist/features/panel-kit/fetch-query/adapter.react.d.ts +2 -2
- package/dist/features/panel-kit/fetch-query/adapter.react.js +4 -2
- package/dist/features/panel-kit/fetch-query/adapter.tanstack.js +46 -33
- package/dist/features/panel-kit/helpers/standing-draft-local-backup.js +3 -1
- package/dist/features/panel-kit/hooks/use-settings-slice.hooks.js +3 -1
- package/dist/features/privacy/react/components/PrivacyTab.js +24 -13
- package/dist/features/progress-card/components/ProgressCard.js +7 -3
- package/dist/features/progress-card/reference-adapters.js +59 -54
- package/dist/features/project-locations/react/components/ProjectLocationsTab.js +24 -15
- package/dist/features/project-locations/react/hooks/useProjectLocationsTab.d.ts +8 -0
- package/dist/features/project-locations/react/hooks/useProjectLocationsTab.js +31 -23
- package/dist/features/resource-dashboard/react/components/ResourceBoard.js +6 -47
- package/dist/features/resource-dashboard/react/components/ResourceBoardView.d.ts +2 -2
- package/dist/features/resource-dashboard/react/components/ResourceBoardView.js +3 -6
- package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.d.ts +15 -0
- package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.js +35 -0
- package/dist/features/resource-dashboard/react/components/ResourceCard.js +5 -16
- package/dist/features/resource-dashboard/react/components/ResourceCardParts.d.ts +34 -0
- package/dist/features/resource-dashboard/react/components/ResourceCardParts.js +34 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowList.js +3 -1
- package/dist/features/resource-dashboard/react/components/ResourceRowListItem.js +2 -2
- package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.d.ts +19 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.js +11 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowListView.d.ts +1 -1
- package/dist/features/resource-dashboard/react/components/ResourceRowListView.js +4 -4
- package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.d.ts +10 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.js +20 -0
- package/dist/features/resource-dashboard/react/components/resourceBoardProps.d.ts +48 -0
- package/dist/features/resource-dashboard/react/components/resourceBoardProps.js +70 -0
- package/dist/features/resource-dashboard/react/components/resourceRowListProps.d.ts +19 -0
- package/dist/features/resource-dashboard/react/components/resourceRowListProps.js +14 -0
- package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.d.ts +16 -0
- package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.js +13 -0
- package/dist/features/resource-dashboard/rules.js +2 -8
- package/dist/features/sketch-editor/dom.js +150 -99
- package/dist/features/sketch-editor/react/components/SketchEditor.js +26 -15
- package/dist/features/skills/react/components/SkillDraftForm.js +13 -19
- package/dist/features/skills/react/components/SkillDraftForm.labels.d.ts +31 -0
- package/dist/features/skills/react/components/SkillDraftForm.labels.js +54 -0
- package/dist/features/skills/react/components/SkillRow.js +38 -18
- package/dist/features/skills/react/components/SkillRow.labels.d.ts +7 -0
- package/dist/features/skills/react/components/SkillRow.labels.js +18 -0
- package/dist/features/skills/react/components/SkillsTab.js +24 -14
- package/dist/features/skills/react/components/SkillsTab.labels.d.ts +7 -0
- package/dist/features/skills/react/components/SkillsTab.labels.js +11 -0
- package/dist/features/skills/rules.js +32 -23
- package/dist/features/source-config-list/agent-handles.js +2 -1
- package/dist/features/source-config-list/dependencies.js +53 -49
- package/dist/features/source-config-list/index.d.ts +3 -3
- package/dist/features/source-config-list/react/components/SourceConfigAddForm.d.ts +6 -5
- package/dist/features/source-config-list/react/components/SourceConfigAddForm.js +38 -7
- package/dist/features/source-config-list/react/components/SourceConfigField.js +69 -25
- package/dist/features/source-config-list/react/components/SourceConfigItemCard.d.ts +6 -5
- package/dist/features/source-config-list/react/components/SourceConfigItemCard.js +76 -20
- package/dist/features/source-config-list/react/components/SourceConfigListView.d.ts +5 -3
- package/dist/features/source-config-list/react/components/SourceConfigListView.js +33 -3
- package/dist/features/source-config-list/react/components/SourceConfigTestControl.js +17 -1
- package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.d.ts +11 -0
- package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.js +12 -0
- package/dist/features/source-config-list/types.d.ts +14 -0
- package/dist/features/tab-launcher-menu/react/components/TabLauncherMenu.js +25 -1
- package/dist/features/tab-launcher-menu/react/hooks/useTabLauncherMenu.js +15 -16
- package/dist/features/tab-strip/react/components/TabBar.js +8 -1
- package/dist/features/tabbed-dialog/react/components/TabbedDialog.js +8 -30
- package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.d.ts +15 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.js +8 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.d.ts +12 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.js +6 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.d.ts +27 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.js +12 -0
- package/dist/features/tabbed-dialog/react/components/tabbedDialogView.d.ts +43 -0
- package/dist/features/tabbed-dialog/react/components/tabbedDialogView.js +59 -0
- package/dist/features/version-manager/dependencies.d.ts +3 -1
- package/dist/features/version-manager/dependencies.js +3 -2
- package/dist/features/version-manager/react/components/VersionManagerModal.js +10 -6
- package/dist/features/version-manager/react/hooks/useVersionManager.js +7 -4
- package/dist/features/viewer-shell/react/components/CommentSidePanel.js +103 -50
- package/dist/features/viewer-shell/react/components/MarkdownSplitPane.js +28 -8
- package/dist/features/viewer-shell/react/hooks/useCommentReorder.js +22 -16
- package/dist/features/viewer-shell/rules.js +19 -23
- package/dist/react/components/CustomSelect.d.ts +38 -1
- package/dist/react/components/CustomSelect.js +94 -38
- package/dist/react/components/OnboardingDropdown.js +20 -8
- package/dist/react/components/RemixIcon.stylesheet.js +3 -1
- package/dist/react/components/Toast.js +13 -2
- package/dist/react/components/TooltipLayer.js +5 -14
- package/dist/react/components/WorkingDirPicker.js +78 -50
- package/dist/react/components/remixicon-font-recovery.js +38 -30
- package/dist/react/components/select-popover.d.ts +8 -6
- package/dist/react/components/select-popover.js +33 -29
- package/dist/react/mcp-ui/McpUiHost.d.ts +1 -1
- package/dist/react/mcp-ui/McpUiHost.js +10 -11
- package/dist/react/mcp-ui/host-message-source.d.ts +14 -2
- package/dist/react/mcp-ui/host-message-source.js +15 -5
- package/dist/react/mcp-ui/mcp-ui-host-frame.d.ts +41 -0
- package/dist/react/mcp-ui/mcp-ui-host-frame.js +39 -0
- package/dist/renderers/annotation-canvas/react/components/AnnotationCanvas.js +97 -69
- package/dist/renderers/preview-modal-shell/react/components/PreviewModalShell.js +59 -30
- package/dist/renderers/preview-modal-shell/react/components/icons.js +4 -1
- package/dist/renderers/react/components/ArtifactView.js +9 -6
- package/dist/renderers/srcdoc/build.d.ts +4 -1
- package/dist/renderers/srcdoc/build.js +62 -48
- package/dist/styles/admin.css +29 -14
- package/dist/styles/lightbox.css +142 -0
- package/dist/styles/variables.css +128 -0
- package/dist/theme/apply.js +9 -5
- package/dist/utils/auto-open-file.d.ts +2 -1
- package/dist/utils/auto-open-file.js +69 -85
- package/dist/utils/markdown-scroll-sync.js +108 -87
- package/dist/utils/polygon-selection.js +25 -26
- package/dist/utils/smooth-scroll-to-top.d.ts +1 -1
- package/dist/utils/smooth-scroll-to-top.js +2 -32
- package/dist/utils/sse.js +26 -26
- package/dist/utils/unit-bezier.d.ts +7 -0
- package/dist/utils/unit-bezier.js +38 -0
- package/package.json +7 -10
- package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.d.ts +0 -13
- package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.js +0 -46
|
@@ -91,16 +91,18 @@ export declare function openSelectOnKey({ event, open, openPanel }: {
|
|
|
91
91
|
}, { disabled }?: {
|
|
92
92
|
disabled?: boolean | undefined;
|
|
93
93
|
}): boolean;
|
|
94
|
+
type SelectNavigationActions = {
|
|
95
|
+
preventDefault: () => void;
|
|
96
|
+
move: (delta: 1 | -1) => void;
|
|
97
|
+
edge: (edge: 'first' | 'last') => void;
|
|
98
|
+
choose: () => void;
|
|
99
|
+
};
|
|
94
100
|
/** Shared keyboard dispatch; adapters map navigation onto their enabled-value or filtered-index contract. */
|
|
95
101
|
export declare function applySelectNavigationKey({ key, actions }: {
|
|
96
102
|
key: string;
|
|
97
|
-
actions:
|
|
98
|
-
preventDefault: () => void;
|
|
99
|
-
move: (delta: 1 | -1) => void;
|
|
100
|
-
edge: (edge: 'first' | 'last') => void;
|
|
101
|
-
choose: () => void;
|
|
102
|
-
};
|
|
103
|
+
actions: SelectNavigationActions;
|
|
103
104
|
}, { spaceChooses }?: {
|
|
104
105
|
spaceChooses?: boolean;
|
|
105
106
|
}): boolean;
|
|
107
|
+
export {};
|
|
106
108
|
//# sourceMappingURL=select-popover.d.ts.map
|
|
@@ -8,11 +8,15 @@ const FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), input:not([disabled
|
|
|
8
8
|
export function focusableInDomOrder({ exclude }, { root = document } = {}) {
|
|
9
9
|
return Array.from(root.querySelectorAll(FOCUSABLE_SELECTOR)).filter((el) => !exclude || !exclude.contains(el));
|
|
10
10
|
}
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/** The open dialog that owns the trigger, but only when it is modal (its neighbours are inert). */
|
|
12
|
+
function modalDialogOf({ trigger }) {
|
|
13
13
|
const dialog = trigger?.closest("dialog[open]");
|
|
14
14
|
// An open non-modal dialog permits focus outside it; only showModal() creates inert neighbours.
|
|
15
|
-
|
|
15
|
+
return dialog?.matches(":modal") ? dialog : null;
|
|
16
|
+
}
|
|
17
|
+
/** Find the trigger's Tab neighbour, wrapping within its modal dialog to avoid inert page controls. */
|
|
18
|
+
export function resolveTabTarget({ panel, trigger, shiftKey }, _optional = {}) {
|
|
19
|
+
const modalDialog = modalDialogOf({ trigger });
|
|
16
20
|
const nodes = focusableInDomOrder({ exclude: panel }, { root: modalDialog ?? document });
|
|
17
21
|
const triggerIndex = trigger ? nodes.indexOf(trigger) : -1;
|
|
18
22
|
if (triggerIndex < 0)
|
|
@@ -28,23 +32,25 @@ export function resolveSelectPortalContainer({ trigger }, { container } = {}) {
|
|
|
28
32
|
export function isSelectEventInside({ target, trigger, panel }, _optional = {}) {
|
|
29
33
|
return Boolean(trigger?.contains(target) || panel?.contains(target));
|
|
30
34
|
}
|
|
35
|
+
/** True when the trigger's rect lies wholly outside the viewport on any side. */
|
|
36
|
+
function isOutsideViewport(rect) {
|
|
37
|
+
return rect.bottom <= 0 || rect.top >= window.innerHeight || rect.right <= 0 || rect.left >= window.innerWidth;
|
|
38
|
+
}
|
|
39
|
+
/** True when hit-testing is available and something unrelated to the trigger is topmost at its centre. */
|
|
40
|
+
function isObscured({ trigger, rect }) {
|
|
41
|
+
if (typeof document.elementFromPoint !== 'function')
|
|
42
|
+
return false;
|
|
43
|
+
const topmostAtCenter = document.elementFromPoint(rect.left + rect.width / 2, rect.top + rect.height / 2);
|
|
44
|
+
return !topmostAtCenter || !(trigger.contains(topmostAtCenter) || topmostAtCenter.contains(trigger));
|
|
45
|
+
}
|
|
31
46
|
/** Apply the variant's geometry port; widget selects additionally dismiss a clipped/obscured trigger. */
|
|
32
47
|
export function repositionSelectPopover({ trigger, measurePosition, setPosition }, { onOutOfView } = {}) {
|
|
33
48
|
if (onOutOfView) {
|
|
34
49
|
const rect = trigger.getBoundingClientRect();
|
|
35
|
-
|
|
36
|
-
if (outOfViewport) {
|
|
50
|
+
if (isOutsideViewport(rect) || isObscured({ trigger, rect })) {
|
|
37
51
|
onOutOfView();
|
|
38
52
|
return;
|
|
39
53
|
}
|
|
40
|
-
if (typeof document.elementFromPoint === 'function') {
|
|
41
|
-
const topmostAtCenter = document.elementFromPoint(rect.left + rect.width / 2, rect.top + rect.height / 2);
|
|
42
|
-
const obscured = !topmostAtCenter || !(trigger.contains(topmostAtCenter) || topmostAtCenter.contains(trigger));
|
|
43
|
-
if (obscured) {
|
|
44
|
-
onOutOfView();
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
54
|
}
|
|
49
55
|
setPosition(measurePosition({ trigger }));
|
|
50
56
|
}
|
|
@@ -188,23 +194,21 @@ export function openSelectOnKey({ event, open, openPanel }, { disabled } = {}) {
|
|
|
188
194
|
openPanel();
|
|
189
195
|
return true;
|
|
190
196
|
}
|
|
197
|
+
// A Map rather than an object literal so an inherited key such as `toString` never matches.
|
|
198
|
+
const NAVIGATION_KEY_ACTIONS = new Map([
|
|
199
|
+
['ArrowDown', (actions) => actions.move(1)],
|
|
200
|
+
['ArrowUp', (actions) => actions.move(-1)],
|
|
201
|
+
['Home', (actions) => actions.edge('first')],
|
|
202
|
+
['End', (actions) => actions.edge('last')],
|
|
203
|
+
['Enter', (actions) => actions.choose()],
|
|
204
|
+
]);
|
|
191
205
|
/** Shared keyboard dispatch; adapters map navigation onto their enabled-value or filtered-index contract. */
|
|
192
206
|
export function applySelectNavigationKey({ key, actions }, { spaceChooses = false } = {}) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
actions.preventDefault();
|
|
200
|
-
actions.edge(key === 'Home' ? 'first' : 'last');
|
|
201
|
-
return true;
|
|
202
|
-
}
|
|
203
|
-
if (key === 'Enter' || (spaceChooses && key === ' ')) {
|
|
204
|
-
actions.preventDefault();
|
|
205
|
-
actions.choose();
|
|
206
|
-
return true;
|
|
207
|
-
}
|
|
208
|
-
return false;
|
|
207
|
+
const apply = spaceChooses && key === ' ' ? NAVIGATION_KEY_ACTIONS.get('Enter') : NAVIGATION_KEY_ACTIONS.get(key);
|
|
208
|
+
if (!apply)
|
|
209
|
+
return false;
|
|
210
|
+
actions.preventDefault();
|
|
211
|
+
apply(actions);
|
|
212
|
+
return true;
|
|
209
213
|
}
|
|
210
214
|
//# sourceMappingURL=select-popover.js.map
|
|
@@ -8,7 +8,7 @@ export interface McpUiHostProps extends McpUiHostOptions {
|
|
|
8
8
|
/** Follow the View's reported height. Defaults to `true`. */
|
|
9
9
|
readonly autoResize?: boolean;
|
|
10
10
|
/** Ceiling for the followed height, so a View reporting an enormous size cannot push the rest of the page off-screen. */
|
|
11
|
-
readonly maxHeight?: number;
|
|
11
|
+
readonly maxHeight?: number | undefined;
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* Renders a View and runs its protocol session.
|
|
@@ -16,28 +16,27 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
16
16
|
*/
|
|
17
17
|
import { AppRenderer } from '@mcp-ui/client';
|
|
18
18
|
import { useRef } from 'react';
|
|
19
|
-
import { applyHostSurfaceTokens } from '../../features/mcp-ui/surfaces/host-theme.js';
|
|
20
19
|
import { useHostSurfaceTokens } from './useHostSurfaceTokens.js';
|
|
20
|
+
import { resolveFrameHeight, resolveThemedViewSession } from './mcp-ui-host-frame.js';
|
|
21
21
|
import { useMcpUiHost } from './useMcpUiHost.js';
|
|
22
|
-
const DEFAULT_INITIAL_HEIGHT = 220;
|
|
23
|
-
const DEFAULT_MAX_HEIGHT = 720;
|
|
24
22
|
/**
|
|
25
23
|
* Renders a View and runs its protocol session.
|
|
26
24
|
*
|
|
27
25
|
* @param props - See {@link McpUiHostProps}.
|
|
28
26
|
*/
|
|
29
27
|
export function McpUiHost(props) {
|
|
30
|
-
const { title, className
|
|
28
|
+
const { title, className } = props;
|
|
31
29
|
const wrapperRef = useRef(null);
|
|
32
30
|
const hostTokens = useHostSurfaceTokens(wrapperRef);
|
|
33
|
-
const html
|
|
34
|
-
// Only a document the theme actually changed takes the theme into its key, so a third-party View
|
|
35
|
-
// (left untouched) never remounts when the host's theme moves.
|
|
36
|
-
const sessionKey = `${String(props.sessionKey ?? props.html)}${html === props.html ? '' : JSON.stringify(hostTokens)}`;
|
|
31
|
+
const { html, sessionKey } = resolveThemedViewSession({ html: props.html, sessionKey: props.sessionKey, hostTokens });
|
|
37
32
|
const host = useMcpUiHost({ ...props, html, sessionKey });
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
const height = resolveFrameHeight({ reported: host.size?.height }, props);
|
|
34
|
+
return (_jsx("div", { ref: wrapperRef, className: className, "data-mcpui-host": "", "data-mcpui-state": host.state, "aria-label": title,
|
|
35
|
+
// `colorScheme: 'normal'` (inherited by the iframe) keeps the frame see-through on a dark
|
|
36
|
+
// host. Chrome paints an iframe's canvas OPAQUE (white) whenever the frame element's used
|
|
37
|
+
// color-scheme differs from its document's, and a surface declares none -- so under a host's
|
|
38
|
+
// `:root { color-scheme: dark }` every card sat in a white 16px frame (owner, 2026-10-21).
|
|
39
|
+
style: { display: 'block', width: '100%', height, colorScheme: 'normal' }, children: hostTokens !== undefined && _jsx(AppRenderer
|
|
41
40
|
// Keyed by the session so replacing the document remounts `AppRenderer` — a fresh
|
|
42
41
|
// `AppBridge`, a fresh sandbox iframe, a fresh handshake. `AppRenderer` itself provides no
|
|
43
42
|
// "replace the document in place" mode; remount via `key` is the supported way to start a
|
|
@@ -14,10 +14,22 @@
|
|
|
14
14
|
* Doing this in a `useEffect` instead loses every message posted between mount-commit and effect-run
|
|
15
15
|
* — a window that a lazy import, a Suspense boundary, or one expensive sibling render makes real.
|
|
16
16
|
*
|
|
17
|
-
* The
|
|
17
|
+
* The missing-`window` guard in `forwardWindowMessages` is for SSR: importing this module during a server render must not throw.
|
|
18
18
|
* Nothing subscribes there, and the module has no other side effect.
|
|
19
19
|
*/
|
|
20
|
-
import { type BufferedWindowMessage } from '../../features/mcp-ui/early-message-buffer.js';
|
|
20
|
+
import { type BufferedWindowMessage, type EarlyMessageBuffer } from '../../features/mcp-ui/early-message-buffer.js';
|
|
21
|
+
/** The slice of `window` the listener needs. */
|
|
22
|
+
export interface MessageEventTarget {
|
|
23
|
+
addEventListener(type: 'message', listener: (event: MessageEvent) => void): void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Forwards every `message` event on `scope.window` into `into`. A scope without a `window` (a
|
|
27
|
+
* server render) is a no-op — that is the SSR guard, and taking the global scope as a parameter
|
|
28
|
+
* lets both the browser and the server path be exercised from one test environment.
|
|
29
|
+
*/
|
|
30
|
+
export declare function forwardWindowMessages(scope: {
|
|
31
|
+
readonly window?: MessageEventTarget;
|
|
32
|
+
}, into: EarlyMessageBuffer): void;
|
|
21
33
|
/** How a Host receives raw window messages. Injectable so a test can drive one without a real `postMessage`. */
|
|
22
34
|
export type ViewMessageSource = (handler: (message: BufferedWindowMessage) => void) => () => void;
|
|
23
35
|
/** The default {@link ViewMessageSource}: the shared, module-scope buffer above. */
|
|
@@ -14,16 +14,26 @@
|
|
|
14
14
|
* Doing this in a `useEffect` instead loses every message posted between mount-commit and effect-run
|
|
15
15
|
* — a window that a lazy import, a Suspense boundary, or one expensive sibling render makes real.
|
|
16
16
|
*
|
|
17
|
-
* The
|
|
17
|
+
* The missing-`window` guard in `forwardWindowMessages` is for SSR: importing this module during a server render must not throw.
|
|
18
18
|
* Nothing subscribes there, and the module has no other side effect.
|
|
19
19
|
*/
|
|
20
20
|
import { createEarlyMessageBuffer, } from '../../features/mcp-ui/early-message-buffer.js';
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Forwards every `message` event on `scope.window` into `into`. A scope without a `window` (a
|
|
23
|
+
* server render) is a no-op — that is the SSR guard, and taking the global scope as a parameter
|
|
24
|
+
* lets both the browser and the server path be exercised from one test environment.
|
|
25
|
+
*/
|
|
26
|
+
export function forwardWindowMessages(scope, into) {
|
|
27
|
+
const target = scope.window;
|
|
28
|
+
if (!target)
|
|
29
|
+
return;
|
|
30
|
+
target.addEventListener('message', (event) => {
|
|
31
|
+
into.push({ data: event.data, origin: event.origin, source: event.source });
|
|
25
32
|
});
|
|
26
33
|
}
|
|
34
|
+
const buffer = createEarlyMessageBuffer();
|
|
35
|
+
// Module scope, on purpose — see the header: this runs at first import, before any Host mounts.
|
|
36
|
+
forwardWindowMessages(globalThis, buffer);
|
|
27
37
|
/** The default {@link ViewMessageSource}: the shared, module-scope buffer above. */
|
|
28
38
|
export const subscribeToViewMessages = (handler) => buffer.subscribe(handler);
|
|
29
39
|
/** Backlogged message count — diagnostics and tests only. */
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module react/mcp-ui/mcp-ui-host-frame
|
|
3
|
+
*
|
|
4
|
+
* Pure decisions behind `McpUiHost.tsx`'s chrome — which document a View is handed, which session
|
|
5
|
+
* key it runs under, and how tall its frame is — kept out of the component so each rule is one
|
|
6
|
+
* small, directly testable function. No protocol or sandbox decision lives here.
|
|
7
|
+
*/
|
|
8
|
+
import { type HostSurfaceTokens } from '../../features/mcp-ui/surfaces/host-theme.js';
|
|
9
|
+
/** The document a View is mounted with, and the session key that remounts it when it changes. */
|
|
10
|
+
export interface ThemedViewSession {
|
|
11
|
+
readonly html: string;
|
|
12
|
+
readonly sessionKey: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Writes the host's live theme into a Jini-built surface document and derives the session key.
|
|
16
|
+
*
|
|
17
|
+
* @param input - The caller's document, its optional session key, and the host's tokens
|
|
18
|
+
* (`undefined` until the wrapper has been measured).
|
|
19
|
+
* @returns The document to mount and the key to mount it under.
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveThemedViewSession(input: {
|
|
22
|
+
readonly html: string;
|
|
23
|
+
readonly sessionKey: string | number | undefined;
|
|
24
|
+
readonly hostTokens: HostSurfaceTokens | undefined;
|
|
25
|
+
}): ThemedViewSession;
|
|
26
|
+
/**
|
|
27
|
+
* The frame height: the View's reported height (clamped to `[1, maxHeight]`) while following it,
|
|
28
|
+
* otherwise the fixed initial height.
|
|
29
|
+
*
|
|
30
|
+
* @param input - The height the View last reported, if any.
|
|
31
|
+
* @param options - `initialHeight` (default 220), `autoResize` (default `true`), `maxHeight` (default 720).
|
|
32
|
+
* @returns The frame height in pixels.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveFrameHeight(input: {
|
|
35
|
+
readonly reported: number | undefined;
|
|
36
|
+
}, options?: {
|
|
37
|
+
readonly initialHeight?: number;
|
|
38
|
+
readonly autoResize?: boolean;
|
|
39
|
+
readonly maxHeight?: number | undefined;
|
|
40
|
+
}): number;
|
|
41
|
+
//# sourceMappingURL=mcp-ui-host-frame.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module react/mcp-ui/mcp-ui-host-frame
|
|
3
|
+
*
|
|
4
|
+
* Pure decisions behind `McpUiHost.tsx`'s chrome — which document a View is handed, which session
|
|
5
|
+
* key it runs under, and how tall its frame is — kept out of the component so each rule is one
|
|
6
|
+
* small, directly testable function. No protocol or sandbox decision lives here.
|
|
7
|
+
*/
|
|
8
|
+
import { applyHostSurfaceTokens } from '../../features/mcp-ui/surfaces/host-theme.js';
|
|
9
|
+
const DEFAULT_INITIAL_HEIGHT = 220;
|
|
10
|
+
const DEFAULT_MAX_HEIGHT = 720;
|
|
11
|
+
/**
|
|
12
|
+
* Writes the host's live theme into a Jini-built surface document and derives the session key.
|
|
13
|
+
*
|
|
14
|
+
* @param input - The caller's document, its optional session key, and the host's tokens
|
|
15
|
+
* (`undefined` until the wrapper has been measured).
|
|
16
|
+
* @returns The document to mount and the key to mount it under.
|
|
17
|
+
*/
|
|
18
|
+
export function resolveThemedViewSession(input) {
|
|
19
|
+
const { hostTokens } = input;
|
|
20
|
+
const html = hostTokens === undefined ? input.html : applyHostSurfaceTokens(input.html, hostTokens);
|
|
21
|
+
// Only a document the theme actually changed takes the theme into its key, so a third-party View
|
|
22
|
+
// (left untouched) never remounts when the host's theme moves.
|
|
23
|
+
const sessionKey = `${String(input.sessionKey ?? input.html)}${html === input.html ? '' : JSON.stringify(hostTokens)}`;
|
|
24
|
+
return { html, sessionKey };
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The frame height: the View's reported height (clamped to `[1, maxHeight]`) while following it,
|
|
28
|
+
* otherwise the fixed initial height.
|
|
29
|
+
*
|
|
30
|
+
* @param input - The height the View last reported, if any.
|
|
31
|
+
* @param options - `initialHeight` (default 220), `autoResize` (default `true`), `maxHeight` (default 720).
|
|
32
|
+
* @returns The frame height in pixels.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveFrameHeight(input, options = {}) {
|
|
35
|
+
const { initialHeight = DEFAULT_INITIAL_HEIGHT, autoResize = true, maxHeight = DEFAULT_MAX_HEIGHT } = options;
|
|
36
|
+
const { reported } = input;
|
|
37
|
+
return autoResize && reported !== undefined ? Math.min(Math.max(reported, 1), maxHeight) : initialHeight;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=mcp-ui-host-frame.js.map
|
|
@@ -10,7 +10,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
10
10
|
* `../../source-map.md`.
|
|
11
11
|
*/
|
|
12
12
|
import { createPortal } from 'react-dom';
|
|
13
|
-
import { useAnnotationCanvas } from '../hooks/useAnnotationCanvas.js';
|
|
13
|
+
import { useAnnotationCanvas, } from '../hooks/useAnnotationCanvas.js';
|
|
14
14
|
import { DEFAULT_ANNOTATION_CANVAS_ICONS } from './icons.js';
|
|
15
15
|
import { useT } from '../../../react/i18n.js';
|
|
16
16
|
function maybePortal(node, host) {
|
|
@@ -18,7 +18,6 @@ function maybePortal(node, host) {
|
|
|
18
18
|
}
|
|
19
19
|
export function AnnotationCanvas(props) {
|
|
20
20
|
const { children, toolbarHost, icons: iconOverrides, className } = props;
|
|
21
|
-
const t = useT();
|
|
22
21
|
const icons = { ...DEFAULT_ANNOTATION_CANVAS_ICONS, ...iconOverrides };
|
|
23
22
|
const c = useAnnotationCanvas(props);
|
|
24
23
|
// `c.imagePreviews[c.previewIndex]` is a TS-only possibly-undefined lookup
|
|
@@ -29,74 +28,103 @@ export function AnnotationCanvas(props) {
|
|
|
29
28
|
// unconditionally resets `previewIndex` to `null` in that same update — so
|
|
30
29
|
// this can never actually be `undefined` while `previewIndex !== null`.
|
|
31
30
|
const activePreview = c.previewIndex !== null ? c.imagePreviews[c.previewIndex] : null;
|
|
32
|
-
return (_jsxs("div", { ref: c.wrapRef, className: className, "data-annotation-canvas-active": props.active ? 'true' : 'false', style: { position: 'absolute', inset: 0, width: '100%', height: '100%' }, children: [children, c.showCanvas ?
|
|
33
|
-
|
|
34
|
-
inset: 0,
|
|
35
|
-
pointerEvents: props.active ? 'auto' : 'none',
|
|
36
|
-
cursor: props.active ? (c.markTool === 'text' ? 'text' : 'crosshair') : 'default',
|
|
37
|
-
visibility: c.chromeHidden ? 'hidden' : 'visible',
|
|
38
|
-
touchAction: 'none',
|
|
39
|
-
userSelect: 'none',
|
|
40
|
-
} })) : null, c.textLayerVisible ? (_jsx("div", { style: { position: 'absolute', inset: 0, pointerEvents: 'none', visibility: c.chromeHidden ? 'hidden' : 'visible' }, children: c.textMarks.map((mark) => (_jsxs("div", { onPointerDown: (e) => c.onTextPointerDown(e, mark), onPointerMove: (e) => c.onTextPointerMove(e, mark), onPointerUp: (e) => c.onTextPointerUp(e, mark), onPointerCancel: (e) => c.onTextPointerUp(e, mark), style: {
|
|
41
|
-
position: 'absolute',
|
|
42
|
-
left: `${mark.x * 100}%`,
|
|
43
|
-
top: `${mark.y * 100}%`,
|
|
44
|
-
pointerEvents: c.markTool === 'text' ? 'auto' : 'none',
|
|
45
|
-
cursor: mark.editing ? 'default' : 'move',
|
|
46
|
-
touchAction: 'none',
|
|
47
|
-
}, children: [_jsx("textarea", { ref: (el) => {
|
|
48
|
-
c.registerTextArea(mark.id, el);
|
|
49
|
-
if (el)
|
|
50
|
-
c.autosizeTextArea(el);
|
|
51
|
-
}, value: mark.text, wrap: "off", rows: 1, spellCheck: false, readOnly: !mark.editing, "aria-label": t('Text annotation'), onChange: (e) => {
|
|
52
|
-
c.updateTextMark(mark.id, e.target.value);
|
|
53
|
-
c.autosizeTextArea(e.currentTarget);
|
|
54
|
-
}, onBlur: () => c.handleTextBlur(mark.id), onKeyDown: (e) => {
|
|
55
|
-
if (e.key === 'Escape') {
|
|
56
|
-
e.stopPropagation();
|
|
57
|
-
c.handleTextEscape(mark.id, e.currentTarget);
|
|
58
|
-
}
|
|
59
|
-
}, style: {
|
|
60
|
-
display: 'block',
|
|
61
|
-
margin: 0,
|
|
62
|
-
padding: 0,
|
|
63
|
-
border: 'none',
|
|
64
|
-
outline: 'none',
|
|
65
|
-
background: 'transparent',
|
|
66
|
-
resize: 'none',
|
|
67
|
-
overflow: 'hidden',
|
|
68
|
-
whiteSpace: 'pre',
|
|
69
|
-
color: 'var(--jini-danger)',
|
|
70
|
-
caretColor: 'var(--jini-danger)',
|
|
71
|
-
fontWeight: 600,
|
|
72
|
-
fontSize: c.textFontPx,
|
|
73
|
-
lineHeight: 1.25,
|
|
74
|
-
minWidth: Math.max(2, Math.round(c.textFontPx * 0.5)),
|
|
75
|
-
pointerEvents: mark.editing ? 'auto' : 'none',
|
|
76
|
-
userSelect: mark.editing ? 'text' : 'none',
|
|
77
|
-
cursor: mark.editing ? 'text' : 'move',
|
|
78
|
-
} }), c.markTool === 'text' ? (_jsx("button", { type: "button", onPointerDown: (e) => {
|
|
79
|
-
e.preventDefault();
|
|
80
|
-
e.stopPropagation();
|
|
81
|
-
}, onClick: () => c.removeTextMark(mark.id), "aria-label": t('Remove text annotation'), title: t('Remove text annotation'), children: icons.close() })) : null] }, mark.id))) })) : null, props.active
|
|
82
|
-
? maybePortal(_jsxs("div", { ref: c.dockRef, "data-annotation-canvas-dock-layout": c.dockPlacement.layout, "data-annotation-canvas-dock-side": c.dockPlacement.side ?? undefined, style: { position: 'absolute', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8, ...c.dockPlacement.style }, children: [c.captureWarning ? (_jsx("div", { role: "status", "aria-live": "polite", style: { visibility: c.chromeHidden ? 'hidden' : undefined }, children: _jsx("span", { children: c.captureWarning.message }) })) : null, c.imagePreviews.length > 0 ? (_jsx("div", { "aria-label": t('Attached images'), style: { display: 'flex', gap: 6, visibility: c.chromeHidden ? 'hidden' : undefined }, children: c.imagePreviews.map((item, i) => (_jsxs("div", { style: { position: 'relative' }, children: [_jsx("button", { type: "button", onClick: () => c.setPreviewIndex(i), disabled: c.sending, title: item.file.name, "aria-label": item.file.name, children: _jsx("img", { src: item.url, alt: "", "aria-hidden": true, style: { width: 44, height: 44, objectFit: 'cover' } }) }), _jsx("button", { type: "button", onClick: () => c.removeExtraFile(i), disabled: c.sending, "aria-label": t('Remove attached image'), title: t('Remove attached image'), children: icons.close() })] }, item.url))) })) : null, _jsxs("div", { role: "toolbar", "aria-label": t('Annotation tools'), style: { display: 'flex', alignItems: 'center', gap: 6, visibility: c.chromeHidden ? 'hidden' : undefined }, children: [_jsxs("div", { style: { position: 'relative' }, ref: c.markToolMenuRef, children: [_jsxs("button", { type: "button", onClick: () => c.setMarkToolMenuOpen(!c.markToolMenuOpen), disabled: c.sending, "aria-haspopup": "menu", "aria-expanded": c.markToolMenuOpen, "aria-label": c.currentMarkTool.label, title: c.currentMarkTool.label, children: [icons[c.currentMarkTool.tool](), icons['chevron-down']()] }), c.markToolMenuOpen ? (_jsx("div", { role: "menu", "aria-label": c.currentMarkTool.label, children: c.markToolOptions.map((item) => {
|
|
83
|
-
const isActive = c.markTool === item.tool;
|
|
84
|
-
return (_jsxs("button", { type: "button", role: "menuitemradio", "aria-checked": isActive, "aria-label": item.label, disabled: c.sending, onClick: () => c.selectMarkTool(item.tool), children: [icons[item.tool](), _jsx("span", { children: item.label }), isActive ? icons.check() : null] }, item.tool));
|
|
85
|
-
}) })) : null] }), _jsx("button", { type: "button", onClick: c.undoStroke, disabled: !c.canUndo, "aria-label": t('Undo'), title: t('Undo'), children: icons.undo() }), _jsx("button", { type: "button", onClick: c.redoStroke, disabled: !c.canRedo, "aria-label": t('Redo'), title: t('Redo'), children: icons.redo() }), _jsx("input", { ref: c.fileInputRef, type: "file", accept: "image/*", multiple: true, style: { display: 'none' }, onChange: c.onFileInputChange }), _jsx("button", { type: "button", onClick: () => {
|
|
86
|
-
props.onToolbarClick?.('attach_image');
|
|
87
|
-
c.fileInputRef.current?.click();
|
|
88
|
-
}, disabled: c.sending, "aria-label": t('Attach image'), title: t('Attach image'), children: icons.attach() }), _jsx("input", { value: c.note, onChange: (e) => c.setNote(e.target.value), onPaste: c.onNotePaste, disabled: c.sending, placeholder: t('Add a note…'), "aria-label": t('Annotation note'), onCompositionStart: c.onCompositionStart, onCompositionEnd: c.onCompositionEnd, onKeyDown: c.onNoteKeyDown }), _jsxs("div", { style: { position: 'relative', display: 'inline-flex' }, ref: c.submitMenuRef, children: [_jsxs("button", { type: "button", onClick: () => void c.send(c.submitAction), disabled: c.sending || !c.currentSubmit.enabled, "aria-label": c.sending ? c.currentSubmit.pendingLabel : c.currentSubmit.label, title: c.sending ? c.currentSubmit.pendingLabel : c.currentSubmit.title, children: [c.sending ? icons.spinner() : icons[c.submitAction](), _jsx("span", { children: c.currentSubmit.label })] }), _jsx("button", { type: "button", onClick: () => c.setSubmitMenuOpen(!c.submitMenuOpen), disabled: c.sending || !c.canSubmit, "aria-haspopup": "menu", "aria-expanded": c.submitMenuOpen, "aria-label": t('Submit options'), title: t('Submit options'), children: c.submitMenuOpen ? icons['chevron-down']() : icons['chevron-up']() }), c.submitMenuOpen ? (_jsx("div", { role: "menu", "aria-label": t('Submit options'), children: c.submitOptions.map((opt) => {
|
|
89
|
-
const isActive = c.submitAction === opt.action;
|
|
90
|
-
return (_jsxs("button", { type: "button", role: "menuitemradio", "aria-checked": isActive, "aria-label": opt.label, disabled: !c.sending && opt.enabled ? undefined : true, title: opt.title, onClick: () => c.chooseSubmitAction(opt.action), children: [icons[opt.action](), _jsx("span", { children: opt.label }), isActive ? icons.check() : null] }, opt.action));
|
|
91
|
-
}) })) : null] })] }), _jsx("button", { type: "button", onClick: () => {
|
|
92
|
-
props.onToolbarClick?.('exit');
|
|
93
|
-
c.closeOverlay();
|
|
94
|
-
}, disabled: c.sending, "aria-label": t('Close annotation tools'), title: t('Close annotation tools'), children: icons.close() })] }), toolbarHost)
|
|
31
|
+
return (_jsxs("div", { ref: c.wrapRef, className: className, "data-annotation-canvas-active": props.active ? 'true' : 'false', style: { position: 'absolute', inset: 0, width: '100%', height: '100%' }, children: [children, c.showCanvas ? _jsx(DrawingCanvas, { c: c, active: props.active }) : null, c.textLayerVisible ? _jsx(TextLayer, { c: c, icons: icons }) : null, props.active
|
|
32
|
+
? maybePortal(_jsx(AnnotationDock, { c: c, icons: icons, onToolbarClick: props.onToolbarClick }), toolbarHost)
|
|
95
33
|
: null, activePreview && typeof document !== 'undefined'
|
|
96
|
-
? createPortal(_jsx(
|
|
97
|
-
if (e.target === e.currentTarget)
|
|
98
|
-
c.setPreviewIndex(null);
|
|
99
|
-
}, children: _jsxs("div", { children: [_jsxs("div", { children: [_jsx("span", { title: activePreview.file.name, children: activePreview.file.name }), _jsx("button", { type: "button", onClick: () => c.setPreviewIndex(null), "aria-label": t('Close'), title: t('Close'), children: icons.close() })] }), _jsx("img", { src: activePreview.url, alt: activePreview.file.name })] }) }), document.body)
|
|
34
|
+
? createPortal(_jsx(ImagePreviewDialog, { c: c, icons: icons, preview: activePreview }), document.body)
|
|
100
35
|
: null] }));
|
|
101
36
|
}
|
|
37
|
+
function DrawingCanvas({ c, active }) {
|
|
38
|
+
return (_jsx("canvas", { ref: c.canvasRef, onPointerDown: c.onPointerDown, onPointerMove: c.onPointerMove, onPointerUp: c.onPointerUp, onPointerCancel: c.onPointerUp, style: {
|
|
39
|
+
position: 'absolute',
|
|
40
|
+
inset: 0,
|
|
41
|
+
pointerEvents: active ? 'auto' : 'none',
|
|
42
|
+
cursor: active ? (c.markTool === 'text' ? 'text' : 'crosshair') : 'default',
|
|
43
|
+
visibility: c.chromeHidden ? 'hidden' : 'visible',
|
|
44
|
+
touchAction: 'none',
|
|
45
|
+
userSelect: 'none',
|
|
46
|
+
} }));
|
|
47
|
+
}
|
|
48
|
+
function TextLayer({ c, icons }) {
|
|
49
|
+
return (_jsx("div", { style: { position: 'absolute', inset: 0, pointerEvents: 'none', visibility: c.chromeHidden ? 'hidden' : 'visible' }, children: c.textMarks.map((mark) => (_jsx(TextMarkItem, { c: c, icons: icons, mark: mark }, mark.id))) }));
|
|
50
|
+
}
|
|
51
|
+
function TextMarkItem({ c, icons, mark }) {
|
|
52
|
+
const t = useT();
|
|
53
|
+
return (_jsxs("div", { onPointerDown: (e) => c.onTextPointerDown(e, mark), onPointerMove: (e) => c.onTextPointerMove(e, mark), onPointerUp: (e) => c.onTextPointerUp(e, mark), onPointerCancel: (e) => c.onTextPointerUp(e, mark), style: {
|
|
54
|
+
position: 'absolute',
|
|
55
|
+
left: `${mark.x * 100}%`,
|
|
56
|
+
top: `${mark.y * 100}%`,
|
|
57
|
+
pointerEvents: c.markTool === 'text' ? 'auto' : 'none',
|
|
58
|
+
cursor: mark.editing ? 'default' : 'move',
|
|
59
|
+
touchAction: 'none',
|
|
60
|
+
}, children: [_jsx("textarea", { ref: (el) => {
|
|
61
|
+
c.registerTextArea(mark.id, el);
|
|
62
|
+
if (el)
|
|
63
|
+
c.autosizeTextArea(el);
|
|
64
|
+
}, value: mark.text, wrap: "off", rows: 1, spellCheck: false, readOnly: !mark.editing, "aria-label": t('Text annotation'), onChange: (e) => {
|
|
65
|
+
c.updateTextMark(mark.id, e.target.value);
|
|
66
|
+
c.autosizeTextArea(e.currentTarget);
|
|
67
|
+
}, onBlur: () => c.handleTextBlur(mark.id), onKeyDown: (e) => {
|
|
68
|
+
if (e.key === 'Escape') {
|
|
69
|
+
e.stopPropagation();
|
|
70
|
+
c.handleTextEscape(mark.id, e.currentTarget);
|
|
71
|
+
}
|
|
72
|
+
}, style: {
|
|
73
|
+
display: 'block',
|
|
74
|
+
margin: 0,
|
|
75
|
+
padding: 0,
|
|
76
|
+
border: 'none',
|
|
77
|
+
outline: 'none',
|
|
78
|
+
background: 'transparent',
|
|
79
|
+
resize: 'none',
|
|
80
|
+
overflow: 'hidden',
|
|
81
|
+
whiteSpace: 'pre',
|
|
82
|
+
color: 'var(--jini-danger)',
|
|
83
|
+
caretColor: 'var(--jini-danger)',
|
|
84
|
+
fontWeight: 600,
|
|
85
|
+
fontSize: c.textFontPx,
|
|
86
|
+
lineHeight: 1.25,
|
|
87
|
+
minWidth: Math.max(2, Math.round(c.textFontPx * 0.5)),
|
|
88
|
+
pointerEvents: mark.editing ? 'auto' : 'none',
|
|
89
|
+
userSelect: mark.editing ? 'text' : 'none',
|
|
90
|
+
cursor: mark.editing ? 'text' : 'move',
|
|
91
|
+
} }), c.markTool === 'text' ? (_jsx("button", { type: "button", onPointerDown: (e) => {
|
|
92
|
+
e.preventDefault();
|
|
93
|
+
e.stopPropagation();
|
|
94
|
+
}, onClick: () => c.removeTextMark(mark.id), "aria-label": t('Remove text annotation'), title: t('Remove text annotation'), children: icons.close() })) : null] }));
|
|
95
|
+
}
|
|
96
|
+
function AnnotationDock({ c, icons, onToolbarClick, }) {
|
|
97
|
+
const t = useT();
|
|
98
|
+
return (_jsxs("div", { ref: c.dockRef, "data-annotation-canvas-dock-layout": c.dockPlacement.layout, "data-annotation-canvas-dock-side": c.dockPlacement.side ?? undefined, style: { position: 'absolute', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8, ...c.dockPlacement.style }, children: [c.captureWarning ? (_jsx("div", { role: "status", "aria-live": "polite", style: { visibility: c.chromeHidden ? 'hidden' : undefined }, children: _jsx("span", { children: c.captureWarning.message }) })) : null, c.imagePreviews.length > 0 ? _jsx(AttachedImages, { c: c, icons: icons }) : null, _jsxs("div", { role: "toolbar", "aria-label": t('Annotation tools'), style: { display: 'flex', alignItems: 'center', gap: 6, visibility: c.chromeHidden ? 'hidden' : undefined }, children: [_jsx(MarkToolPicker, { c: c, icons: icons }), _jsx("button", { type: "button", onClick: c.undoStroke, disabled: !c.canUndo, "aria-label": t('Undo'), title: t('Undo'), children: icons.undo() }), _jsx("button", { type: "button", onClick: c.redoStroke, disabled: !c.canRedo, "aria-label": t('Redo'), title: t('Redo'), children: icons.redo() }), _jsx("input", { ref: c.fileInputRef, type: "file", accept: "image/*", multiple: true, style: { display: 'none' }, onChange: c.onFileInputChange }), _jsx("button", { type: "button", onClick: () => {
|
|
99
|
+
onToolbarClick?.('attach_image');
|
|
100
|
+
c.fileInputRef.current?.click();
|
|
101
|
+
}, disabled: c.sending, "aria-label": t('Attach image'), title: t('Attach image'), children: icons.attach() }), _jsx("input", { value: c.note, onChange: (e) => c.setNote(e.target.value), onPaste: c.onNotePaste, disabled: c.sending, placeholder: t('Add a note…'), "aria-label": t('Annotation note'), onCompositionStart: c.onCompositionStart, onCompositionEnd: c.onCompositionEnd, onKeyDown: c.onNoteKeyDown }), _jsx(SubmitPicker, { c: c, icons: icons })] }), _jsx("button", { type: "button", onClick: () => {
|
|
102
|
+
onToolbarClick?.('exit');
|
|
103
|
+
c.closeOverlay();
|
|
104
|
+
}, disabled: c.sending, "aria-label": t('Close annotation tools'), title: t('Close annotation tools'), children: icons.close() })] }));
|
|
105
|
+
}
|
|
106
|
+
function AttachedImages({ c, icons }) {
|
|
107
|
+
const t = useT();
|
|
108
|
+
return (_jsx("div", { "aria-label": t('Attached images'), style: { display: 'flex', gap: 6, visibility: c.chromeHidden ? 'hidden' : undefined }, children: c.imagePreviews.map((item, i) => (_jsxs("div", { style: { position: 'relative' }, children: [_jsx("button", { type: "button", onClick: () => c.setPreviewIndex(i), disabled: c.sending, title: item.file.name, "aria-label": item.file.name, children: _jsx("img", { src: item.url, alt: "", "aria-hidden": true, style: { width: 44, height: 44, objectFit: 'cover' } }) }), _jsx("button", { type: "button", onClick: () => c.removeExtraFile(i), disabled: c.sending, "aria-label": t('Remove attached image'), title: t('Remove attached image'), children: icons.close() })] }, item.url))) }));
|
|
109
|
+
}
|
|
110
|
+
function MarkToolPicker({ c, icons }) {
|
|
111
|
+
return (_jsxs("div", { style: { position: 'relative' }, ref: c.markToolMenuRef, children: [_jsxs("button", { type: "button", onClick: () => c.setMarkToolMenuOpen(!c.markToolMenuOpen), disabled: c.sending, "aria-haspopup": "menu", "aria-expanded": c.markToolMenuOpen, "aria-label": c.currentMarkTool.label, title: c.currentMarkTool.label, children: [icons[c.currentMarkTool.tool](), icons['chevron-down']()] }), c.markToolMenuOpen ? (_jsx("div", { role: "menu", "aria-label": c.currentMarkTool.label, children: c.markToolOptions.map((item) => {
|
|
112
|
+
const isActive = c.markTool === item.tool;
|
|
113
|
+
return (_jsxs("button", { type: "button", role: "menuitemradio", "aria-checked": isActive, "aria-label": item.label, disabled: c.sending, onClick: () => c.selectMarkTool(item.tool), children: [icons[item.tool](), _jsx("span", { children: item.label }), isActive ? icons.check() : null] }, item.tool));
|
|
114
|
+
}) })) : null] }));
|
|
115
|
+
}
|
|
116
|
+
function SubmitPicker({ c, icons }) {
|
|
117
|
+
const t = useT();
|
|
118
|
+
return (_jsxs("div", { style: { position: 'relative', display: 'inline-flex' }, ref: c.submitMenuRef, children: [_jsxs("button", { type: "button", onClick: () => void c.send(c.submitAction), disabled: c.sending || !c.currentSubmit.enabled, "aria-label": c.sending ? c.currentSubmit.pendingLabel : c.currentSubmit.label, title: c.sending ? c.currentSubmit.pendingLabel : c.currentSubmit.title, children: [c.sending ? icons.spinner() : icons[c.submitAction](), _jsx("span", { children: c.currentSubmit.label })] }), _jsx("button", { type: "button", onClick: () => c.setSubmitMenuOpen(!c.submitMenuOpen), disabled: c.sending || !c.canSubmit, "aria-haspopup": "menu", "aria-expanded": c.submitMenuOpen, "aria-label": t('Submit options'), title: t('Submit options'), children: c.submitMenuOpen ? icons['chevron-down']() : icons['chevron-up']() }), c.submitMenuOpen ? (_jsx("div", { role: "menu", "aria-label": t('Submit options'), children: c.submitOptions.map((opt) => {
|
|
119
|
+
const isActive = c.submitAction === opt.action;
|
|
120
|
+
return (_jsxs("button", { type: "button", role: "menuitemradio", "aria-checked": isActive, "aria-label": opt.label, disabled: !c.sending && opt.enabled ? undefined : true, title: opt.title, onClick: () => c.chooseSubmitAction(opt.action), children: [icons[opt.action](), _jsx("span", { children: opt.label }), isActive ? icons.check() : null] }, opt.action));
|
|
121
|
+
}) })) : null] }));
|
|
122
|
+
}
|
|
123
|
+
function ImagePreviewDialog({ c, icons, preview }) {
|
|
124
|
+
const t = useT();
|
|
125
|
+
return (_jsx("div", { role: "dialog", "aria-modal": "true", "aria-label": preview.file.name, onMouseDown: (e) => {
|
|
126
|
+
if (e.target === e.currentTarget)
|
|
127
|
+
c.setPreviewIndex(null);
|
|
128
|
+
}, children: _jsxs("div", { children: [_jsxs("div", { children: [_jsx("span", { title: preview.file.name, children: preview.file.name }), _jsx("button", { type: "button", onClick: () => c.setPreviewIndex(null), "aria-label": t('Close'), title: t('Close'), children: icons.close() })] }), _jsx("img", { src: preview.url, alt: preview.file.name })] }) }));
|
|
129
|
+
}
|
|
102
130
|
//# sourceMappingURL=AnnotationCanvas.js.map
|