@jini-ai/ui 0.5.5 → 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 +4 -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/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.js +114 -78
- package/dist/features/execution/rules.d.ts +2 -2
- package/dist/features/execution/rules.js +31 -15
- package/dist/features/execution/types.d.ts +4 -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/shadcn/data-table.js +10 -7
- 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/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
|
@@ -33,6 +33,16 @@ export function setTooltipAttribute(target, name, value) {
|
|
|
33
33
|
return;
|
|
34
34
|
target.setAttribute(name, value);
|
|
35
35
|
}
|
|
36
|
+
function resolveTooltipTarget(trigger, entry) {
|
|
37
|
+
return entry.target === 'closest-label' ? trigger.closest('label') : trigger;
|
|
38
|
+
}
|
|
39
|
+
function decorateTooltipTarget({ target, trigger, label, placement, }) {
|
|
40
|
+
setTooltipAttribute(target, 'data-tooltip', label);
|
|
41
|
+
setTooltipAttribute(target, 'data-tooltip-placement', placement);
|
|
42
|
+
if (!target.classList.contains('jini-tooltip'))
|
|
43
|
+
target.classList.add('jini-tooltip');
|
|
44
|
+
setTooltipAttribute(trigger, 'aria-label', label);
|
|
45
|
+
}
|
|
36
46
|
/**
|
|
37
47
|
* Drives ONLY the shared `TooltipLayer` contract (`.jini-tooltip[data-tooltip]`):
|
|
38
48
|
* it renders a single styled, viewport-aware bubble and suppresses the
|
|
@@ -42,47 +52,111 @@ export function setTooltipAttribute(target, name, value) {
|
|
|
42
52
|
export function applySketchEditorTooltips(root, labels, targets) {
|
|
43
53
|
const decorated = new Set();
|
|
44
54
|
for (const entry of targets) {
|
|
45
|
-
|
|
46
|
-
|
|
55
|
+
const triggers = Array.from(root.querySelectorAll(entry.selector));
|
|
56
|
+
const label = normalizeTooltipLabel(labels[entry.label]);
|
|
57
|
+
if (!label)
|
|
58
|
+
continue;
|
|
59
|
+
for (const trigger of triggers) {
|
|
60
|
+
const target = resolveTooltipTarget(trigger, entry);
|
|
47
61
|
if (!target || decorated.has(target))
|
|
48
62
|
continue;
|
|
49
|
-
|
|
50
|
-
if (!label)
|
|
51
|
-
continue;
|
|
52
|
-
setTooltipAttribute(target, 'data-tooltip', label);
|
|
53
|
-
setTooltipAttribute(target, 'data-tooltip-placement', entry.placement ?? 'bottom');
|
|
54
|
-
if (!target.classList.contains('jini-tooltip'))
|
|
55
|
-
target.classList.add('jini-tooltip');
|
|
56
|
-
setTooltipAttribute(trigger, 'aria-label', label);
|
|
63
|
+
decorateTooltipTarget({ target, trigger, label, placement: entry.placement ?? 'bottom' });
|
|
57
64
|
decorated.add(target);
|
|
58
65
|
}
|
|
59
66
|
}
|
|
60
67
|
}
|
|
68
|
+
function readPopoverOrigin(popover, rect) {
|
|
69
|
+
const left = Number.parseFloat(popover.style.left || `${rect.left}`);
|
|
70
|
+
const top = Number.parseFloat(popover.style.top || `${rect.top}`);
|
|
71
|
+
return Number.isFinite(left) && Number.isFinite(top) ? { left, top } : null;
|
|
72
|
+
}
|
|
73
|
+
/** Shifts `value` so the `[start, end]` span fits inside `[min, max]`: the
|
|
74
|
+
* overflow past `max` is subtracted first, then the underflow before `min`
|
|
75
|
+
* is added, in that order. */
|
|
76
|
+
function clampAxis({ value, start, end }, { min, max }) {
|
|
77
|
+
let next = value;
|
|
78
|
+
if (end > max)
|
|
79
|
+
next -= end - max;
|
|
80
|
+
if (start < min)
|
|
81
|
+
next += min - start;
|
|
82
|
+
return next;
|
|
83
|
+
}
|
|
84
|
+
function writePopoverCoordinate({ popover, side, next }) {
|
|
85
|
+
if (next !== Number.parseFloat(popover.style[side] || 'NaN'))
|
|
86
|
+
popover.style[side] = `${Math.max(0, Math.round(next))}px`;
|
|
87
|
+
}
|
|
61
88
|
export function clampSketchContextPopover(popover, viewportRoot) {
|
|
62
89
|
const viewportRect = viewportRoot.getBoundingClientRect();
|
|
63
90
|
const rect = popover.getBoundingClientRect();
|
|
64
91
|
if (!rect.width || !rect.height)
|
|
65
92
|
return;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (!Number.isFinite(nextLeft) || !Number.isFinite(nextTop))
|
|
93
|
+
const origin = readPopoverOrigin(popover, rect);
|
|
94
|
+
if (!origin)
|
|
69
95
|
return;
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
96
|
+
const nextLeft = clampAxis({ value: origin.left, start: rect.left, end: rect.right }, { min: viewportRect.left + SKETCH_CONTEXT_MENU_MARGIN, max: viewportRect.right - SKETCH_CONTEXT_MENU_MARGIN });
|
|
97
|
+
const nextTop = clampAxis({ value: origin.top, start: rect.top, end: rect.bottom }, { min: viewportRect.top + SKETCH_CONTEXT_MENU_MARGIN, max: viewportRect.bottom - SKETCH_CONTEXT_MENU_MARGIN });
|
|
98
|
+
writePopoverCoordinate({ popover, side: 'left', next: nextLeft });
|
|
99
|
+
writePopoverCoordinate({ popover, side: 'top', next: nextTop });
|
|
100
|
+
}
|
|
101
|
+
function hasRecognizedContextMenuAction(menu, recognized) {
|
|
102
|
+
// `li[data-testid]` is an attribute selector: every `item` it matches is
|
|
103
|
+
// guaranteed to carry the attribute, so `getAttribute` can't return null
|
|
104
|
+
// here — only typed nullable because `getAttribute` is nullable in general.
|
|
105
|
+
return Array.from(menu.querySelectorAll('li[data-testid]')).some((item) => recognized.has(item.getAttribute('data-testid')));
|
|
106
|
+
}
|
|
107
|
+
/** Removes every `<hr>` and every non-allow-listed `<li>` from `menu`,
|
|
108
|
+
* returning the surviving `<li>`s keyed by action name. */
|
|
109
|
+
function pruneContextMenuItems(menu, allowed) {
|
|
110
|
+
const byAction = new Map();
|
|
111
|
+
for (const child of Array.from(menu.children)) {
|
|
112
|
+
if (child instanceof HTMLHRElement) {
|
|
113
|
+
child.remove();
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (!(child instanceof HTMLLIElement))
|
|
117
|
+
continue;
|
|
118
|
+
const actionName = child.getAttribute('data-testid') ?? '';
|
|
119
|
+
if (!allowed.has(actionName)) {
|
|
120
|
+
child.remove();
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
byAction.set(actionName, child);
|
|
124
|
+
}
|
|
125
|
+
return byAction;
|
|
126
|
+
}
|
|
127
|
+
function isContextMenuOrdered(menu, orderedItems) {
|
|
128
|
+
const currentItems = Array.from(menu.children).filter((child) => child instanceof HTMLLIElement);
|
|
129
|
+
return currentItems.length === orderedItems.length && currentItems.every((item, index) => item === orderedItems[index]);
|
|
130
|
+
}
|
|
131
|
+
function simplifyContextMenu({ menu, viewportRoot, allowList }, { allowed, recognized }) {
|
|
132
|
+
// `menu` was found via `root.querySelectorAll(...)`, so it is always a
|
|
133
|
+
// strict descendant of `root` and therefore always has a parent element
|
|
134
|
+
// — `.parentElement` is only typed nullable because `Element` itself
|
|
135
|
+
// permits detached nodes, not because that's reachable here.
|
|
136
|
+
const popover = menu.closest('.popover') ?? menu.parentElement;
|
|
137
|
+
if (!hasRecognizedContextMenuAction(menu, recognized))
|
|
138
|
+
return;
|
|
139
|
+
menu.classList.add('jini-sketch-context-menu');
|
|
140
|
+
popover.classList.add('jini-sketch-context-popover');
|
|
141
|
+
const byAction = pruneContextMenuItems(menu, allowed);
|
|
142
|
+
// `orderContextMenuActions` only returns names present in `byAction`'s
|
|
143
|
+
// keys, so every lookup here is defined.
|
|
144
|
+
const orderedItems = orderContextMenuActions(Array.from(byAction.keys()), allowList).map((action) => byAction.get(action));
|
|
145
|
+
if (orderedItems.length === 0) {
|
|
146
|
+
popover.remove();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
// Only touch the DOM when the order is actually wrong. This runs on
|
|
150
|
+
// every MutationObserver tick, and re-appending nodes already in place
|
|
151
|
+
// detaches/re-inserts them each frame; because that re-triggers the
|
|
152
|
+
// observer, an unconditional reorder becomes a self-sustaining
|
|
153
|
+
// per-frame churn that cancels the in-flight pointer interaction,
|
|
154
|
+
// making the menu impossible to click.
|
|
155
|
+
if (!isContextMenuOrdered(menu, orderedItems)) {
|
|
156
|
+
for (const item of orderedItems)
|
|
157
|
+
menu.appendChild(item);
|
|
158
|
+
}
|
|
159
|
+
clampSketchContextPopover(popover, viewportRoot);
|
|
86
160
|
}
|
|
87
161
|
/**
|
|
88
162
|
* Simplifies Excalidraw's built-in right-click context menu down to
|
|
@@ -91,67 +165,18 @@ export function clampSketchContextPopover(popover, viewportRoot) {
|
|
|
91
165
|
* Excalidraw's own.
|
|
92
166
|
*/
|
|
93
167
|
export function applySketchContextMenuSimplification(root, viewportRoot, allowList, recognizedActions) {
|
|
168
|
+
const sets = { allowed: new Set(allowList), recognized: new Set(recognizedActions) };
|
|
94
169
|
for (const menu of Array.from(root.querySelectorAll('ul.context-menu'))) {
|
|
95
|
-
|
|
96
|
-
// strict descendant of `root` and therefore always has a parent element
|
|
97
|
-
// — `.parentElement` is only typed nullable because `Element` itself
|
|
98
|
-
// permits detached nodes, not because that's reachable here.
|
|
99
|
-
const popover = menu.closest('.popover') ?? menu.parentElement;
|
|
100
|
-
const recognized = new Set(recognizedActions);
|
|
101
|
-
// `li[data-testid]` is an attribute selector: every `item` it matches is
|
|
102
|
-
// guaranteed to carry the attribute, so `getAttribute` can't return null
|
|
103
|
-
// here — only typed nullable because `getAttribute` is nullable in general.
|
|
104
|
-
const hasRecognizedAction = Array.from(menu.querySelectorAll('li[data-testid]')).some((item) => recognized.has(item.getAttribute('data-testid')));
|
|
105
|
-
if (!hasRecognizedAction)
|
|
106
|
-
continue;
|
|
107
|
-
menu.classList.add('jini-sketch-context-menu');
|
|
108
|
-
popover.classList.add('jini-sketch-context-popover');
|
|
109
|
-
const allowed = new Set(allowList);
|
|
110
|
-
const byAction = new Map();
|
|
111
|
-
for (const child of Array.from(menu.children)) {
|
|
112
|
-
if (child instanceof HTMLHRElement) {
|
|
113
|
-
child.remove();
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
if (!(child instanceof HTMLLIElement))
|
|
117
|
-
continue;
|
|
118
|
-
const actionName = child.getAttribute('data-testid') ?? '';
|
|
119
|
-
if (!allowed.has(actionName)) {
|
|
120
|
-
child.remove();
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
byAction.set(actionName, child);
|
|
124
|
-
}
|
|
125
|
-
const orderedActions = orderContextMenuActions(Array.from(byAction.keys()), allowList);
|
|
126
|
-
const orderedItems = orderedActions.map((action) => byAction.get(action)).filter(Boolean);
|
|
127
|
-
if (orderedItems.length === 0) {
|
|
128
|
-
popover.remove();
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
// Only touch the DOM when the order is actually wrong. This runs on
|
|
132
|
-
// every MutationObserver tick, and re-appending nodes already in place
|
|
133
|
-
// detaches/re-inserts them each frame; because that re-triggers the
|
|
134
|
-
// observer, an unconditional reorder becomes a self-sustaining
|
|
135
|
-
// per-frame churn that cancels the in-flight pointer interaction,
|
|
136
|
-
// making the menu impossible to click.
|
|
137
|
-
const currentItems = Array.from(menu.children).filter((child) => child instanceof HTMLLIElement);
|
|
138
|
-
const alreadyOrdered = currentItems.length === orderedItems.length && currentItems.every((item, index) => item === orderedItems[index]);
|
|
139
|
-
if (!alreadyOrdered) {
|
|
140
|
-
for (const item of orderedItems)
|
|
141
|
-
menu.appendChild(item);
|
|
142
|
-
}
|
|
143
|
-
clampSketchContextPopover(popover, viewportRoot);
|
|
170
|
+
simplifyContextMenu({ menu, viewportRoot, allowList }, sets);
|
|
144
171
|
}
|
|
145
172
|
}
|
|
146
|
-
/**
|
|
147
|
-
*
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (!overrides || Object.keys(overrides).length === 0 || typeof document === 'undefined')
|
|
154
|
-
return;
|
|
173
|
+
/** The override for `value`, or `null` when there is none or it would not
|
|
174
|
+
* change anything (so callers skip a no-op DOM write). */
|
|
175
|
+
function resolveTextOverride(value, overrides) {
|
|
176
|
+
const next = translateDomTextValue(value, overrides);
|
|
177
|
+
return next !== null && next !== value ? next : null;
|
|
178
|
+
}
|
|
179
|
+
function rewriteTextNodes(root, overrides) {
|
|
155
180
|
const textWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
|
|
156
181
|
let node = textWalker.nextNode();
|
|
157
182
|
while (node) {
|
|
@@ -159,23 +184,38 @@ export function applySketchDomTextOverrides(root, overrides) {
|
|
|
159
184
|
// `nodeValue` is always a string per spec (only `Document`/`DocumentType`
|
|
160
185
|
// nodes ever have a null `nodeValue`) — `Node.nodeValue`'s broader type
|
|
161
186
|
// is nullable only because it covers those other node kinds too.
|
|
162
|
-
const next =
|
|
163
|
-
if (next !== null
|
|
187
|
+
const next = resolveTextOverride(node.nodeValue, overrides);
|
|
188
|
+
if (next !== null)
|
|
164
189
|
node.nodeValue = next;
|
|
165
190
|
node = textWalker.nextNode();
|
|
166
191
|
}
|
|
167
|
-
|
|
192
|
+
}
|
|
193
|
+
function rewriteOverrideAttributes(root, overrides) {
|
|
194
|
+
const descendants = Array.from(root.querySelectorAll('*'));
|
|
195
|
+
const elements = root instanceof Element ? [root, ...descendants] : descendants;
|
|
168
196
|
for (const element of elements) {
|
|
169
197
|
for (const attr of SKETCH_TEXT_OVERRIDE_ATTRS) {
|
|
170
198
|
const value = element.getAttribute(attr);
|
|
171
199
|
if (!value)
|
|
172
200
|
continue;
|
|
173
|
-
const next =
|
|
174
|
-
if (next !== null
|
|
201
|
+
const next = resolveTextOverride(value, overrides);
|
|
202
|
+
if (next !== null)
|
|
175
203
|
element.setAttribute(attr, next);
|
|
176
204
|
}
|
|
177
205
|
}
|
|
178
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* Walks `root`'s text nodes and a fixed set of text-bearing attributes,
|
|
209
|
+
* rewriting any that match `overrides`. `overrides` is entirely
|
|
210
|
+
* host-supplied — no translated copy ships as a default (see
|
|
211
|
+
* `packages/ui/archived provenance ledger`).
|
|
212
|
+
*/
|
|
213
|
+
export function applySketchDomTextOverrides(root, overrides) {
|
|
214
|
+
if (!overrides || Object.keys(overrides).length === 0 || typeof document === 'undefined')
|
|
215
|
+
return;
|
|
216
|
+
rewriteTextNodes(root, overrides);
|
|
217
|
+
rewriteOverrideAttributes(root, overrides);
|
|
218
|
+
}
|
|
179
219
|
export function rewriteExcalidrawUnableToEmbedToasts(root, replacement, additionalPhrases = []) {
|
|
180
220
|
const normalizedReplacement = normalizeTooltipLabel(replacement);
|
|
181
221
|
if (!normalizedReplacement)
|
|
@@ -241,20 +281,31 @@ export function enhanceSketchExcalidrawPortals(closeLabel, onClose, domTextOverr
|
|
|
241
281
|
}
|
|
242
282
|
}
|
|
243
283
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
284
|
+
function isCommandEnter(event) {
|
|
285
|
+
return event.key === 'Enter' && (event.metaKey || event.ctrlKey) && !event.altKey;
|
|
286
|
+
}
|
|
287
|
+
/** The `.Modal__content` around `target`, but only when it sits inside one
|
|
288
|
+
* of our marked `.jini-sketch-modal` portals and holds a `<textarea>`. */
|
|
289
|
+
function resolvePortalEditorContent(target) {
|
|
248
290
|
if (!(target instanceof Element))
|
|
249
|
-
return;
|
|
291
|
+
return null;
|
|
250
292
|
const portal = target.closest('.jini-sketch-modal');
|
|
251
293
|
const content = target.closest('.Modal__content');
|
|
252
294
|
if (!portal || !content || !portal.contains(content))
|
|
295
|
+
return null;
|
|
296
|
+
return content.querySelector('textarea') ? content : null;
|
|
297
|
+
}
|
|
298
|
+
function isButtonEnabled(button) {
|
|
299
|
+
return !button.disabled && button.getAttribute('aria-disabled') !== 'true';
|
|
300
|
+
}
|
|
301
|
+
export function handleSketchPortalCommandEnter(event, insertLabelPattern) {
|
|
302
|
+
if (!isCommandEnter(event))
|
|
253
303
|
return;
|
|
254
|
-
|
|
304
|
+
const content = resolvePortalEditorContent(event.target);
|
|
305
|
+
if (!content)
|
|
255
306
|
return;
|
|
256
307
|
const insertButton = findSketchMermaidInsertButton(content, insertLabelPattern);
|
|
257
|
-
if (!insertButton || insertButton
|
|
308
|
+
if (!insertButton || !isButtonEnabled(insertButton))
|
|
258
309
|
return;
|
|
259
310
|
event.preventDefault();
|
|
260
311
|
event.stopPropagation();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useRef } from 'react';
|
|
3
3
|
import { useI18n } from '../../../i18n/index.js';
|
|
4
4
|
import { Toast } from '../../../../react/components/Toast.js';
|
|
5
5
|
import { defaultSketchEditorDependencies } from '../../dependencies.js';
|
|
@@ -10,6 +10,29 @@ import { useSketchScene } from '../hooks/useSketchScene.js';
|
|
|
10
10
|
import { useSketchTheme } from '../hooks/useSketchTheme.js';
|
|
11
11
|
import { SketchMainMenu } from './SketchMainMenu.js';
|
|
12
12
|
import { SketchSaveStateBadge } from './SketchSaveStateBadge.js';
|
|
13
|
+
const EXCALIDRAW_UI_OPTIONS = {
|
|
14
|
+
canvasActions: {
|
|
15
|
+
saveToActiveFile: false,
|
|
16
|
+
loadScene: false,
|
|
17
|
+
toggleTheme: false,
|
|
18
|
+
saveAsImage: false,
|
|
19
|
+
export: false,
|
|
20
|
+
},
|
|
21
|
+
tools: { image: true },
|
|
22
|
+
};
|
|
23
|
+
/** `saving` wins over `dirty`; only the active label is translated. */
|
|
24
|
+
function resolveSaveStatus({ saving, dirty, t }) {
|
|
25
|
+
if (saving)
|
|
26
|
+
return { state: 'saving', label: t('Saving…') };
|
|
27
|
+
if (dirty)
|
|
28
|
+
return { state: 'dirty', label: t('Unsaved changes') };
|
|
29
|
+
return { state: 'saved', label: t('Saved') };
|
|
30
|
+
}
|
|
31
|
+
/** An export toast carries a file to open: it lingers longer and offers an
|
|
32
|
+
* "Open file" action; every other toast is a short-lived notice. */
|
|
33
|
+
function sketchToastActionProps({ actionFileName, t, onAction, }) {
|
|
34
|
+
return actionFileName ? { ttlMs: 5000, actionLabel: t('Open file'), onAction } : { ttlMs: 2200 };
|
|
35
|
+
}
|
|
13
36
|
/**
|
|
14
37
|
* An Excalidraw-backed sketch surface: theme sync, dirty/save/export
|
|
15
38
|
* orchestration with content-signature deduping, and a DOM-enhancement
|
|
@@ -53,22 +76,10 @@ export function SketchEditor({ scene, onSceneChange, onClear, onSave, onExportIm
|
|
|
53
76
|
mermaidInsertLabelPattern,
|
|
54
77
|
onCloseActiveDialog: closeActiveDialog,
|
|
55
78
|
});
|
|
56
|
-
const excalidrawUIOptions = useMemo(() => ({
|
|
57
|
-
canvasActions: {
|
|
58
|
-
saveToActiveFile: false,
|
|
59
|
-
loadScene: false,
|
|
60
|
-
toggleTheme: false,
|
|
61
|
-
saveAsImage: false,
|
|
62
|
-
export: false,
|
|
63
|
-
},
|
|
64
|
-
tools: { image: true },
|
|
65
|
-
}), []);
|
|
66
79
|
const hasContent = sketchSceneHasContent(scene);
|
|
67
|
-
const canClear = hasContent;
|
|
68
80
|
const canSave = dirty || hasContent;
|
|
69
|
-
const
|
|
70
|
-
const saveStateLabel = saving ? t('Saving…') : dirty ? t('Unsaved changes') : t('Saved');
|
|
81
|
+
const saveStatus = resolveSaveStatus({ saving, dirty, t });
|
|
71
82
|
const { Excalidraw, MainMenu } = engine;
|
|
72
|
-
return (_jsxs("div", { className: "sketch-editor", children: [_jsx("div", { ref: canvasWrapRef, className: "sketch-canvas-wrap sketch-excalidraw-wrap", "data-testid": "sketch-excalidraw-editor", children: _jsx(Excalidraw, { initialData: initialData, excalidrawAPI: handleExcalidrawAPI, onChange: handleChange, langCode: excalidrawLangCode(locale), theme: theme, detectScroll: false, handleKeyboardGlobally: false, autoFocus: true, name: fileName, UIOptions:
|
|
83
|
+
return (_jsxs("div", { className: "sketch-editor", children: [_jsx("div", { ref: canvasWrapRef, className: "sketch-canvas-wrap sketch-excalidraw-wrap", "data-testid": "sketch-excalidraw-editor", children: _jsx(Excalidraw, { initialData: initialData, excalidrawAPI: handleExcalidrawAPI, onChange: handleChange, langCode: excalidrawLangCode(locale), theme: theme, detectScroll: false, handleKeyboardGlobally: false, autoFocus: true, name: fileName, UIOptions: EXCALIDRAW_UI_OPTIONS, renderTopRightUI: () => _jsx(SketchSaveStateBadge, { ...saveStatus }), validateEmbeddable: validateSketchEmbeddableUrl, children: _jsx(SketchMainMenu, { MainMenu: MainMenu, t: t, saving: saving, showSaved: showSaved, canSave: canSave, onSave: () => void handleSave(), exportAvailable: Boolean(onExportImage), exporting: exporting, canExport: hasContent, onExportImage: () => void handleExportImage(), canClear: hasContent, onClear: handleClear }) }, editorInstanceKey) }), toast ? (_jsx(Toast, { message: toast.message, details: toast.details ?? null, tone: toast.tone, onDismiss: dismissToast, ...sketchToastActionProps({ actionFileName: toast.actionFileName, t, onAction: handleToastAction }) })) : null] }));
|
|
73
84
|
}
|
|
74
85
|
//# sourceMappingURL=SketchEditor.js.map
|
|
@@ -1,30 +1,24 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { agentHandleProps } from '@jini-ai/agentic';
|
|
3
3
|
import { useT } from '../../../i18n/index.js';
|
|
4
|
+
import { resolveSkillDraftFormLabels, skillDraftErrorText, skillDraftSubmitLabel } from './SkillDraftForm.labels.js';
|
|
4
5
|
/**
|
|
5
6
|
* The create/edit form for one skill — shared between the top-of-list
|
|
6
7
|
* "New skill" panel and an expanded row's inline edit. Origin:
|
|
7
8
|
* `SkillDraftForm` in `SkillsSection.tsx`.
|
|
8
9
|
*/
|
|
9
10
|
export function SkillDraftForm({ heading, subheading, draft, onDraftChange, error, saving, isEdit, isBuiltInOverride = false, onCancel, onSubmit, labels, agentHandle, }) {
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
const overrideSaveLabel = labels?.overrideSaveLabel ?? t('Save as override');
|
|
23
|
-
const savingLabel = labels?.savingLabel ?? t('Saving…');
|
|
24
|
-
const nameRequiredError = labels?.nameRequiredError ?? t('Name is required.');
|
|
25
|
-
const bodyRequiredError = labels?.bodyRequiredError ?? t('Body is required.');
|
|
26
|
-
const submitLabel = saving ? savingLabel : isEdit ? (isBuiltInOverride ? overrideSaveLabel : saveLabel) : createLabel;
|
|
27
|
-
const errorText = error?.kind === 'name-required' ? nameRequiredError : error?.kind === 'body-required' ? bodyRequiredError : (error?.message ?? null);
|
|
28
|
-
return (_jsxs("div", { className: "jini-settings-subsection jini-skills-draft", "data-testid": isEdit ? 'skills-edit-form' : 'skills-create-form', children: [_jsx("div", { className: "jini-section-head", children: _jsxs("div", { children: [_jsx("h4", { children: heading }), subheading ? _jsx("p", { className: "jini-hint", children: subheading }) : null] }) }), _jsx("div", { className: "jini-settings-field", children: _jsxs("label", { children: [_jsx("span", { children: nameLabel }), _jsx("input", { type: "text", value: draft.name, onChange: (event) => onDraftChange((current) => ({ ...current, name: event.target.value })), placeholder: namePlaceholder, disabled: isEdit, ...agentHandleProps({}, { base: agentHandle, ...({ action: 'name', role: 'field', label: nameLabel }) }) })] }) }), _jsx("div", { className: "jini-settings-field", children: _jsxs("label", { children: [_jsx("span", { children: triggersLabel }), _jsx("input", { type: "text", value: draft.triggers, onChange: (event) => onDraftChange((current) => ({ ...current, triggers: event.target.value })), placeholder: triggersPlaceholder, ...agentHandleProps({}, { base: agentHandle, ...({ action: 'triggers', role: 'field', label: triggersLabel }) }) })] }) }), _jsx("div", { className: "jini-settings-field", children: _jsxs("label", { children: [_jsx("span", { children: descriptionLabel }), _jsx("textarea", { rows: 2, value: draft.description, onChange: (event) => onDraftChange((current) => ({ ...current, description: event.target.value })), placeholder: descriptionPlaceholder, ...agentHandleProps({}, { base: agentHandle, ...({ action: 'description', role: 'field', label: descriptionLabel }) }) })] }) }), _jsx("div", { className: "jini-settings-field", children: _jsxs("label", { children: [_jsx("span", { children: bodyLabel }), _jsx("textarea", { rows: 14, value: draft.body, onChange: (event) => onDraftChange((current) => ({ ...current, body: event.target.value })), placeholder: bodyPlaceholder, ...agentHandleProps({}, { base: agentHandle, ...({ action: 'body', role: 'field', label: bodyLabel }) }) })] }) }), errorText ? (_jsx("p", { className: "jini-hint jini-hint-error", role: "alert", children: errorText })) : null, _jsxs("div", { className: "jini-skills-draft-actions", children: [_jsx("button", { type: "button", className: "jini-button jini-button-ghost", onClick: onCancel, disabled: saving, ...agentHandleProps({}, { base: agentHandle, ...({ action: 'cancel', role: 'button', label: cancelLabel }) }), children: cancelLabel }), _jsx("button", { type: "button", className: "jini-button jini-button-primary", onClick: onSubmit, disabled: saving, "data-testid": "skills-save", ...agentHandleProps({}, { base: agentHandle, ...({ action: 'submit', role: 'button', label: submitLabel }) }), children: submitLabel })] })] }));
|
|
11
|
+
const l = resolveSkillDraftFormLabels({ t: useT() }, { labels });
|
|
12
|
+
const submitLabel = skillDraftSubmitLabel({ labels: l, saving, isEdit, isBuiltInOverride });
|
|
13
|
+
const errorText = skillDraftErrorText({ labels: l, error });
|
|
14
|
+
const fieldProps = { draft, onDraftChange, agentHandle };
|
|
15
|
+
return (_jsxs("div", { className: "jini-settings-subsection jini-skills-draft", "data-testid": isEdit ? 'skills-edit-form' : 'skills-create-form', children: [_jsx("div", { className: "jini-section-head", children: _jsxs("div", { children: [_jsx("h4", { children: heading }), subheading ? _jsx("p", { className: "jini-hint", children: subheading }) : null] }) }), _jsx(SkillDraftField, { ...fieldProps, field: "name", label: l.nameLabel, placeholder: l.namePlaceholder, disabled: isEdit }), _jsx(SkillDraftField, { ...fieldProps, field: "triggers", label: l.triggersLabel, placeholder: l.triggersPlaceholder }), _jsx(SkillDraftField, { ...fieldProps, field: "description", label: l.descriptionLabel, placeholder: l.descriptionPlaceholder, rows: 2 }), _jsx(SkillDraftField, { ...fieldProps, field: "body", label: l.bodyLabel, placeholder: l.bodyPlaceholder, rows: 14 }), errorText ? (_jsx("p", { className: "jini-hint jini-hint-error", role: "alert", children: errorText })) : null, _jsxs("div", { className: "jini-skills-draft-actions", children: [_jsx("button", { type: "button", className: "jini-button jini-button-ghost", onClick: onCancel, disabled: saving, ...agentHandleProps({}, { base: agentHandle, action: 'cancel', role: 'button', label: l.cancelLabel }), children: l.cancelLabel }), _jsx("button", { type: "button", className: "jini-button jini-button-primary", onClick: onSubmit, disabled: saving, "data-testid": "skills-save", ...agentHandleProps({}, { base: agentHandle, action: 'submit', role: 'button', label: submitLabel }), children: submitLabel })] })] }));
|
|
16
|
+
}
|
|
17
|
+
/** One labelled draft field. Attribute order matches the original inline
|
|
18
|
+
* markup exactly so the rendered HTML is unchanged. */
|
|
19
|
+
function SkillDraftField({ draft, onDraftChange, agentHandle, field, label, placeholder, disabled, rows }) {
|
|
20
|
+
const onChange = (event) => onDraftChange((current) => ({ ...current, [field]: event.target.value }));
|
|
21
|
+
const handle = agentHandleProps({}, { base: agentHandle, action: field, role: 'field', label });
|
|
22
|
+
return (_jsx("div", { className: "jini-settings-field", children: _jsxs("label", { children: [_jsx("span", { children: label }), rows === undefined ? (_jsx("input", { type: "text", value: draft[field], onChange: onChange, placeholder: placeholder, disabled: disabled, ...handle })) : (_jsx("textarea", { rows: rows, value: draft[field], onChange: onChange, placeholder: placeholder, ...handle }))] }) }));
|
|
29
23
|
}
|
|
30
24
|
//# sourceMappingURL=SkillDraftForm.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { SkillDraftError } from '../../types.js';
|
|
2
|
+
import type { SkillDraftFormLabels } from './SkillDraftForm.js';
|
|
3
|
+
/** `defaults` with each entry replaced by the host's label when it supplied
|
|
4
|
+
* one, else by the translated default. `t` runs only for the gaps. Shared by
|
|
5
|
+
* every skills component that takes a `labels` override bag. */
|
|
6
|
+
export declare function resolveLabels<K extends string>({ defaults, t }: {
|
|
7
|
+
defaults: Record<K, string>;
|
|
8
|
+
t: (key: string) => string;
|
|
9
|
+
}, { labels }: {
|
|
10
|
+
labels?: Partial<Record<NoInfer<K>, string | undefined>> | undefined;
|
|
11
|
+
}): Record<K, string>;
|
|
12
|
+
/** The form's labels with every gap filled from the translated defaults. */
|
|
13
|
+
export declare function resolveSkillDraftFormLabels({ t }: {
|
|
14
|
+
t: (key: string) => string;
|
|
15
|
+
}, { labels }: {
|
|
16
|
+
labels?: SkillDraftFormLabels | undefined;
|
|
17
|
+
}): Required<SkillDraftFormLabels>;
|
|
18
|
+
/** Saving wins over everything; then edit (override vs. plain save) vs. create. */
|
|
19
|
+
export declare function skillDraftSubmitLabel({ labels, saving, isEdit, isBuiltInOverride, }: {
|
|
20
|
+
labels: Required<SkillDraftFormLabels>;
|
|
21
|
+
saving: boolean;
|
|
22
|
+
isEdit: boolean;
|
|
23
|
+
isBuiltInOverride: boolean;
|
|
24
|
+
}): string;
|
|
25
|
+
/** The alert text for `error`: the two validation kinds use their labels, a
|
|
26
|
+
* submit failure shows the host's own message; `null` means no alert. */
|
|
27
|
+
export declare function skillDraftErrorText({ labels, error, }: {
|
|
28
|
+
labels: Required<SkillDraftFormLabels>;
|
|
29
|
+
error: SkillDraftError | null;
|
|
30
|
+
}): string | null;
|
|
31
|
+
//# sourceMappingURL=SkillDraftForm.labels.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Every `SkillDraftFormLabels` key with its untranslated default. A host
|
|
2
|
+
* label wins; otherwise the default is passed through `t` (lazily, only
|
|
3
|
+
* when the host left that label out). */
|
|
4
|
+
const DEFAULT_SKILL_DRAFT_FORM_LABELS = {
|
|
5
|
+
nameLabel: 'Name',
|
|
6
|
+
namePlaceholder: 'my-skill',
|
|
7
|
+
triggersLabel: 'Triggers',
|
|
8
|
+
triggersPlaceholder: 'search the web, summarize',
|
|
9
|
+
descriptionLabel: 'Description',
|
|
10
|
+
descriptionPlaceholder: 'What does this skill do? When should it be used?',
|
|
11
|
+
bodyLabel: 'Body',
|
|
12
|
+
bodyPlaceholder: '# My skill\n\n1. Explain the workflow.\n2. Describe the inputs and outputs.',
|
|
13
|
+
cancelLabel: 'Cancel',
|
|
14
|
+
createLabel: 'Create',
|
|
15
|
+
saveLabel: 'Save',
|
|
16
|
+
overrideSaveLabel: 'Save as override',
|
|
17
|
+
savingLabel: 'Saving…',
|
|
18
|
+
nameRequiredError: 'Name is required.',
|
|
19
|
+
bodyRequiredError: 'Body is required.',
|
|
20
|
+
};
|
|
21
|
+
/** `defaults` with each entry replaced by the host's label when it supplied
|
|
22
|
+
* one, else by the translated default. `t` runs only for the gaps. Shared by
|
|
23
|
+
* every skills component that takes a `labels` override bag. */
|
|
24
|
+
export function resolveLabels({ defaults, t }, { labels }) {
|
|
25
|
+
const resolved = { ...defaults };
|
|
26
|
+
for (const key of Object.keys(defaults)) {
|
|
27
|
+
resolved[key] = labels?.[key] ?? t(defaults[key]);
|
|
28
|
+
}
|
|
29
|
+
return resolved;
|
|
30
|
+
}
|
|
31
|
+
/** The form's labels with every gap filled from the translated defaults. */
|
|
32
|
+
export function resolveSkillDraftFormLabels({ t }, { labels }) {
|
|
33
|
+
return resolveLabels({ defaults: DEFAULT_SKILL_DRAFT_FORM_LABELS, t }, { labels });
|
|
34
|
+
}
|
|
35
|
+
/** Saving wins over everything; then edit (override vs. plain save) vs. create. */
|
|
36
|
+
export function skillDraftSubmitLabel({ labels, saving, isEdit, isBuiltInOverride, }) {
|
|
37
|
+
if (saving)
|
|
38
|
+
return labels.savingLabel;
|
|
39
|
+
if (!isEdit)
|
|
40
|
+
return labels.createLabel;
|
|
41
|
+
return isBuiltInOverride ? labels.overrideSaveLabel : labels.saveLabel;
|
|
42
|
+
}
|
|
43
|
+
/** The alert text for `error`: the two validation kinds use their labels, a
|
|
44
|
+
* submit failure shows the host's own message; `null` means no alert. */
|
|
45
|
+
export function skillDraftErrorText({ labels, error, }) {
|
|
46
|
+
if (error === null)
|
|
47
|
+
return null;
|
|
48
|
+
if (error.kind === 'name-required')
|
|
49
|
+
return labels.nameRequiredError;
|
|
50
|
+
if (error.kind === 'body-required')
|
|
51
|
+
return labels.bodyRequiredError;
|
|
52
|
+
return error.message;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=SkillDraftForm.labels.js.map
|
|
@@ -4,6 +4,8 @@ import { formatSkillFileSize, humanizeSkillCategory, isBuiltInSkill, isDeletable
|
|
|
4
4
|
import { useT } from '../../../i18n/index.js';
|
|
5
5
|
import { Icon } from '../../../../react/components/Icon.js';
|
|
6
6
|
import { SkillDraftForm } from './SkillDraftForm.js';
|
|
7
|
+
import { resolveLabels } from './SkillDraftForm.labels.js';
|
|
8
|
+
import { DEFAULT_SKILL_ROW_LABELS } from './SkillRow.labels.js';
|
|
7
9
|
/**
|
|
8
10
|
* One collapsible skill row: header (enable toggle, name, mode/category/
|
|
9
11
|
* source badges, edit/delete actions) always visible; body preview + file
|
|
@@ -11,25 +13,43 @@ import { SkillDraftForm } from './SkillDraftForm.js';
|
|
|
11
13
|
* `SkillRow` in `SkillsSection.tsx`.
|
|
12
14
|
*/
|
|
13
15
|
export function SkillRow({ skill, locale, enabled, expanded, editing, body, bodyLoading, files, filesLoading, confirmDelete, confirmBuiltInEdit, draft, draftError, draftSaving, onDraftChange, onToggleExpanded, onToggleEnabled, onStartEdit, onConfirmBuiltInEdit, onCancelBuiltInEdit, onArmDelete, onCancelDelete, onCommitDelete, onCancelEdit, onSubmitEdit, labels, agentHandle, }) {
|
|
14
|
-
const
|
|
16
|
+
const l = resolveLabels({ defaults: DEFAULT_SKILL_ROW_LABELS, t: useT() }, { labels });
|
|
17
|
+
const builtIn = isBuiltInSkill(skill);
|
|
18
|
+
// Built-in skills are never edited in place — "editing" one creates a
|
|
19
|
+
// user-owned override, so every edit affordance says so.
|
|
20
|
+
const editActionLabel = builtIn ? l.overrideCreateLabel : l.editLabel;
|
|
21
|
+
return (_jsxs("div", { className: skillRowClassName({ enabled, expanded }), "data-testid": `skill-row-${skill.id}`, children: [_jsxs("div", { className: "jini-skills-row-head", children: [_jsx(SkillRowSummaryButton, { skill: skill, locale: locale, expanded: expanded, onToggleExpanded: onToggleExpanded, labels: l, agentHandle: agentHandle }), _jsxs("div", { className: "jini-skills-row-actions", children: [isDeletableSkill(skill) && confirmDelete ? (_jsx(SkillRowDeleteConfirm, { onCommitDelete: onCommitDelete, onCancelDelete: onCancelDelete, labels: l, agentHandle: agentHandle })) : (_jsx(SkillRowEditDeleteActions, { skill: skill, editActionLabel: editActionLabel, onStartEdit: onStartEdit, onArmDelete: onArmDelete, labels: l, agentHandle: agentHandle })), _jsxs("label", { className: "toggle-switch toggle-switch-sm jini-skills-row-enable", title: l.enableToggleLabel, children: [_jsx("input", { type: "checkbox", checked: enabled, onChange: (event) => onToggleEnabled(event.target.checked), "aria-label": l.enableToggleLabel, ...agentHandleProps({}, { base: agentHandle, action: 'enabled', role: 'checkbox', label: l.enableToggleLabel }) }), _jsx("span", { className: "toggle-slider" })] })] })] }), confirmBuiltInEdit ? (_jsx(SkillRowBuiltInEditWarning, { onCancelBuiltInEdit: onCancelBuiltInEdit, onConfirmBuiltInEdit: onConfirmBuiltInEdit, labels: l, agentHandle: agentHandle })) : null, expanded && !editing ? (_jsx(SkillRowDetail, { body: body, bodyLoading: bodyLoading, files: files, filesLoading: filesLoading, labels: l })) : null, editing && draft ? (_jsx(SkillDraftForm, { heading: editActionLabel, subheading: skill.id, draft: draft, onDraftChange: onDraftChange, error: draftError, saving: draftSaving, isEdit: true, isBuiltInOverride: builtIn, onCancel: onCancelEdit, onSubmit: onSubmitEdit, labels: labels, ...(agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'edit-form' }) } : {}) })) : null] }));
|
|
22
|
+
}
|
|
23
|
+
function skillRowClassName({ enabled, expanded }) {
|
|
24
|
+
return `jini-skills-row${enabled ? '' : ' jini-skills-row-disabled'}${expanded ? ' jini-skills-row-expanded' : ''}`;
|
|
25
|
+
}
|
|
26
|
+
/** The always-visible header button: name, mode/category/source badges, description. */
|
|
27
|
+
function SkillRowSummaryButton({ skill, locale, expanded, onToggleExpanded, labels: l, agentHandle, }) {
|
|
15
28
|
const name = localizedSkillName(skill, locale) || skill.id;
|
|
16
29
|
const description = localizedSkillDescription(skill, locale);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return (_jsxs("div", { className:
|
|
30
|
+
return (_jsxs("button", { type: "button", className: "jini-skills-row-summary-btn", onClick: onToggleExpanded, "aria-expanded": expanded, title: expanded ? l.collapseLabel : l.expandLabel, ...agentHandleProps({}, { base: agentHandle, action: 'expand', role: 'button', label: name }), children: [_jsx(Icon, { name: "puzzle", size: 14, "aria-hidden": "true" }), _jsxs("span", { className: "jini-skills-row-summary", children: [_jsxs("span", { className: "jini-skills-row-summary-line", children: [_jsx("strong", { children: name }), _jsx("span", { className: "jini-skills-row-summary-mode", children: skill.mode }), skill.category ? (_jsx("span", { className: "jini-skills-row-summary-category", title: humanizeSkillCategory(skill.category), children: humanizeSkillCategory(skill.category) })) : null, skill.source === 'user' ? _jsx("span", { className: "jini-skills-row-summary-source", children: l.userBadge }) : null] }), description ? _jsx("span", { className: "jini-hint", children: description }) : null] }), _jsx(Icon, { name: "chevron-down", size: 14, "aria-hidden": "true" })] }));
|
|
31
|
+
}
|
|
32
|
+
/** The armed-delete confirm/cancel pair that replaces the row actions. */
|
|
33
|
+
function SkillRowDeleteConfirm({ onCommitDelete, onCancelDelete, labels: l, agentHandle, }) {
|
|
34
|
+
return (_jsxs("span", { className: "jini-skills-delete-confirm", role: "group", children: [_jsx("button", { type: "button", className: "jini-button jini-button-danger", onClick: onCommitDelete, "data-testid": "skills-delete-confirm", ...agentHandleProps({}, { base: agentHandle, action: 'delete-confirm', role: 'button', label: l.deleteConfirmLabel }), children: l.deleteConfirmLabel }), _jsx("button", { type: "button", className: "jini-button jini-button-ghost", onClick: onCancelDelete, ...agentHandleProps({}, { base: agentHandle, action: 'delete-cancel', role: 'button', label: l.cancelLabel }), children: l.cancelLabel })] }));
|
|
35
|
+
}
|
|
36
|
+
/** Edit (or create-override) plus, for a user-owned skill only, delete. */
|
|
37
|
+
function SkillRowEditDeleteActions({ skill, editActionLabel, onStartEdit, onArmDelete, labels: l, agentHandle, }) {
|
|
38
|
+
return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "jini-button jini-button-ghost", onClick: onStartEdit, "aria-label": editActionLabel, title: editActionLabel, "data-testid": "skills-edit", ...agentHandleProps({}, { base: agentHandle, action: 'edit', role: 'button', label: editActionLabel }), children: _jsx(Icon, { name: "edit", size: 13 }) }), isDeletableSkill(skill) ? (_jsx("button", { type: "button", className: "jini-button jini-button-ghost", onClick: onArmDelete, "aria-label": l.deleteLabel, title: l.deleteLabel, "data-testid": "skills-delete", ...agentHandleProps({}, { base: agentHandle, action: 'delete', role: 'button', label: l.deleteLabel }), children: _jsx(Icon, { name: "close", size: 13 }) })) : null] }));
|
|
39
|
+
}
|
|
40
|
+
/** The "editing a built-in creates an override" confirmation card. */
|
|
41
|
+
function SkillRowBuiltInEditWarning({ onCancelBuiltInEdit, onConfirmBuiltInEdit, labels: l, agentHandle, }) {
|
|
42
|
+
return (_jsxs("div", { className: "jini-empty-card jini-empty-card-warning", role: "alert", "data-testid": "skills-edit-builtin-warning", children: [_jsx("p", { children: l.overrideWarning }), _jsxs("div", { className: "jini-skills-draft-actions", children: [_jsx("button", { type: "button", className: "jini-button jini-button-ghost", onClick: onCancelBuiltInEdit, "data-testid": "skills-edit-builtin-cancel", ...agentHandleProps({}, { base: agentHandle, action: 'builtin-edit-cancel', role: 'button', label: l.cancelLabel }), children: l.cancelLabel }), _jsx("button", { type: "button", className: "jini-button jini-button-primary", onClick: onConfirmBuiltInEdit, "data-testid": "skills-edit-builtin-confirm", ...agentHandleProps({}, { base: agentHandle, action: 'builtin-edit-confirm', role: 'button', label: l.overrideCreateLabel }), children: l.overrideCreateLabel })] })] }));
|
|
43
|
+
}
|
|
44
|
+
/** The expanded, non-editing detail: SKILL.md preview plus the file tree. */
|
|
45
|
+
function SkillRowDetail({ body, bodyLoading, files, filesLoading, labels: l, }) {
|
|
46
|
+
return (_jsxs("div", { className: "jini-skills-row-detail", children: [_jsxs("div", { className: "jini-settings-subsection", children: [_jsx("h5", { children: l.bodyHeading }), bodyLoading ? _jsx("p", { className: "jini-hint", children: l.loadingLabel }) : _jsx("pre", { className: "jini-skills-body-preview", children: body ?? '' })] }), _jsxs("div", { className: "jini-settings-subsection", children: [_jsx("h5", { children: l.filesHeading }), _jsx(SkillFileTree, { files: files, filesLoading: filesLoading, labels: l })] })] }));
|
|
47
|
+
}
|
|
48
|
+
function SkillFileTree({ files, filesLoading, labels: l, }) {
|
|
49
|
+
if (filesLoading)
|
|
50
|
+
return _jsx("p", { className: "jini-hint", children: l.loadingLabel });
|
|
51
|
+
if (!files || files.length === 0)
|
|
52
|
+
return _jsx("p", { className: "jini-hint", children: l.noFilesLabel });
|
|
53
|
+
return (_jsx("ul", { className: "jini-skills-file-tree", children: files.map((entry) => (_jsxs("li", { className: "jini-skills-file-entry", style: { paddingLeft: skillFileTreeIndent(entry.path) }, children: [_jsx(Icon, { name: entry.kind === 'directory' ? 'folder' : 'file', size: 12 }), _jsx("span", { children: skillFileLeafName(entry.path) }), entry.kind === 'file' && typeof entry.size === 'number' ? (_jsx("span", { className: "jini-hint", children: formatSkillFileSize(entry.size) })) : null] }, entry.path))) }));
|
|
34
54
|
}
|
|
35
55
|
//# sourceMappingURL=SkillRow.js.map
|