@jini-ai/ui 0.5.5 → 0.6.10
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 +91 -2
- 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 +9 -12
- package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.d.ts +0 -13
- package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.js +0 -46
|
@@ -27,109 +27,116 @@ export function prettifyCss(css) {
|
|
|
27
27
|
let atDeclStart = false;
|
|
28
28
|
let i = 0;
|
|
29
29
|
const n = css.length;
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const peekNextNonSpace = (from) => {
|
|
34
|
-
let j = from;
|
|
35
|
-
while (j < n && /\s/.test(css[j]))
|
|
36
|
-
j++;
|
|
37
|
-
return css[j];
|
|
30
|
+
const skipWhitespace = (_input, _options = {}) => {
|
|
31
|
+
while (i < n && /\s/.test(css[i]))
|
|
32
|
+
i++;
|
|
38
33
|
};
|
|
39
|
-
|
|
40
|
-
const ch = css[i];
|
|
34
|
+
const consumeValue = ({ ch }, _options = {}) => {
|
|
41
35
|
if (quote) {
|
|
42
36
|
out += ch;
|
|
43
37
|
if (ch === '\\' && i + 1 < n) {
|
|
44
38
|
out += css[i + 1];
|
|
45
39
|
i += 2;
|
|
46
|
-
|
|
40
|
+
return true;
|
|
47
41
|
}
|
|
48
42
|
if (ch === quote)
|
|
49
43
|
quote = null;
|
|
50
44
|
i++;
|
|
51
|
-
|
|
45
|
+
return true;
|
|
52
46
|
}
|
|
53
47
|
if (ch === '"' || ch === "'") {
|
|
54
48
|
quote = ch;
|
|
55
49
|
out += ch;
|
|
56
50
|
i++;
|
|
57
|
-
|
|
51
|
+
return true;
|
|
58
52
|
}
|
|
59
53
|
if (ch === '(') {
|
|
60
54
|
parenDepth++;
|
|
61
55
|
out += ch;
|
|
62
56
|
i++;
|
|
63
|
-
|
|
57
|
+
return true;
|
|
64
58
|
}
|
|
65
59
|
if (ch === ')') {
|
|
66
60
|
parenDepth = Math.max(0, parenDepth - 1);
|
|
67
61
|
out += ch;
|
|
68
62
|
i++;
|
|
69
|
-
|
|
63
|
+
return true;
|
|
70
64
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
65
|
+
return false;
|
|
66
|
+
};
|
|
67
|
+
const openRule = (_input, _options = {}) => {
|
|
68
|
+
out = out.replace(/[ \t]+$/, '');
|
|
69
|
+
out += ' {';
|
|
70
|
+
depth++;
|
|
71
|
+
atDeclStart = true;
|
|
72
|
+
i++;
|
|
73
|
+
skipWhitespace({}, {});
|
|
74
|
+
if (css[i] !== '}')
|
|
75
|
+
out += '\n' + INDENT.repeat(depth);
|
|
76
|
+
return true;
|
|
77
|
+
};
|
|
78
|
+
const closeRule = (_input, _options = {}) => {
|
|
79
|
+
depth = Math.max(0, depth - 1);
|
|
80
|
+
out = out.replace(/[ \t]+$/, '');
|
|
81
|
+
// A `}` immediately after `{` (an empty rule) needs no newline before it; otherwise close on
|
|
82
|
+
// its own indented line.
|
|
83
|
+
if (!out.endsWith('{'))
|
|
84
|
+
out += '\n' + INDENT.repeat(depth);
|
|
85
|
+
out += '}';
|
|
86
|
+
atDeclStart = false;
|
|
87
|
+
i++;
|
|
88
|
+
skipWhitespace({}, {});
|
|
89
|
+
const nextCh = css[i];
|
|
90
|
+
// A following `}` (closing a parent at-rule) manages its own leading newline via the same
|
|
91
|
+
// trailing-space trim above — adding one here too would double up into a stray blank line.
|
|
92
|
+
if (nextCh !== undefined && nextCh !== '}') {
|
|
93
|
+
out += depth === 0 ? '\n\n' : '\n' + INDENT.repeat(depth);
|
|
82
94
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
while (i < n && /\s/.test(css[i]))
|
|
94
|
-
i++;
|
|
95
|
-
const nextCh = css[i];
|
|
96
|
-
// A following `}` (closing a parent at-rule) manages its own leading newline via the same
|
|
97
|
-
// trimTrailingSpace() above — adding one here too would double up into a stray blank line.
|
|
98
|
-
if (nextCh !== undefined && nextCh !== '}') {
|
|
99
|
-
out += depth === 0 ? '\n\n' : '\n' + INDENT.repeat(depth);
|
|
100
|
-
}
|
|
101
|
-
continue;
|
|
95
|
+
return true;
|
|
96
|
+
};
|
|
97
|
+
const endDeclaration = (_input, _options = {}) => {
|
|
98
|
+
out += ';';
|
|
99
|
+
i++;
|
|
100
|
+
skipWhitespace({}, {});
|
|
101
|
+
const nextCh = css[i];
|
|
102
|
+
if (nextCh !== '}' && nextCh !== undefined) {
|
|
103
|
+
out += '\n' + INDENT.repeat(depth);
|
|
104
|
+
atDeclStart = true;
|
|
102
105
|
}
|
|
103
|
-
|
|
104
|
-
|
|
106
|
+
return true;
|
|
107
|
+
};
|
|
108
|
+
// Only the FIRST colon of a declaration (the property/value separator) gets a space after it —
|
|
109
|
+
// a selector's own colon (`.gm-btn:hover`, matched at `depth === 0`, before any `{`) is left
|
|
110
|
+
// untouched, and only `atDeclStart` (cleared the moment this fires) keeps a later colon inside
|
|
111
|
+
// the same value (there are none in practice here, but the guard costs nothing) from being
|
|
112
|
+
// treated as another property separator.
|
|
113
|
+
const separateValue = (_input, _options = {}) => {
|
|
114
|
+
out += ': ';
|
|
115
|
+
atDeclStart = false;
|
|
116
|
+
i++;
|
|
117
|
+
while (i < n && css[i] === ' ')
|
|
105
118
|
i++;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
119
|
+
return true;
|
|
120
|
+
};
|
|
121
|
+
const consumeStructure = ({ ch }, _options = {}) => {
|
|
122
|
+
if (parenDepth !== 0)
|
|
123
|
+
return false;
|
|
124
|
+
switch (ch) {
|
|
125
|
+
case '{': return openRule({}, {});
|
|
126
|
+
case '}': return closeRule({}, {});
|
|
127
|
+
case ';': return endDeclaration({}, {});
|
|
128
|
+
case ':': return depth > 0 && atDeclStart ? separateValue({}, {}) : false;
|
|
129
|
+
default: return false;
|
|
114
130
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// treated as another property separator.
|
|
120
|
-
if (parenDepth === 0 && ch === ':' && depth > 0 && atDeclStart) {
|
|
121
|
-
out += ': ';
|
|
122
|
-
atDeclStart = false;
|
|
123
|
-
i++;
|
|
124
|
-
while (i < n && css[i] === ' ')
|
|
125
|
-
i++;
|
|
131
|
+
};
|
|
132
|
+
while (i < n) {
|
|
133
|
+
const ch = css[i];
|
|
134
|
+
if (consumeValue({ ch }, {}) || consumeStructure({ ch }, {}))
|
|
126
135
|
continue;
|
|
127
|
-
}
|
|
128
136
|
if (/\s/.test(ch)) {
|
|
129
137
|
out += ' ';
|
|
130
138
|
i++;
|
|
131
|
-
|
|
132
|
-
i++;
|
|
139
|
+
skipWhitespace({}, {});
|
|
133
140
|
continue;
|
|
134
141
|
}
|
|
135
142
|
out += ch;
|
|
@@ -48,6 +48,42 @@ const isEditableEffect = (el) => {
|
|
|
48
48
|
return true;
|
|
49
49
|
return (TEXT_EFFECT_TAGS.has(el.tagName) || PHRASING_TAGS.has(el.tagName)) && isPhrasingOnly(el);
|
|
50
50
|
};
|
|
51
|
+
function runtimeBlockReason({ el, isLive }, _options = {}) {
|
|
52
|
+
if (isLive)
|
|
53
|
+
return undefined;
|
|
54
|
+
if (Object.keys(el.attributes).some(name => name.startsWith('data-kui')) && !isEditableEffect(el))
|
|
55
|
+
return 'kuinetic';
|
|
56
|
+
if (el.tagName === 'canvas')
|
|
57
|
+
return 'canvas';
|
|
58
|
+
if (el.tagName === 'video' && 'autoplay' in el.attributes)
|
|
59
|
+
return 'autoplay-video';
|
|
60
|
+
if (el.attributes['data-editor'] === 'block')
|
|
61
|
+
return 'scripted';
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
function describeBlock({ el, isLive, hasLiveDescendants }, options = {}) {
|
|
65
|
+
const view = sourceElementAttributeView(el, {});
|
|
66
|
+
const host = options.describeHostElement?.(view);
|
|
67
|
+
const embed = !!host || !!options.isProtectedElement?.(view);
|
|
68
|
+
let reason = runtimeBlockReason({ el, isLive }, {});
|
|
69
|
+
if (reason && hasLiveDescendants)
|
|
70
|
+
reason = undefined;
|
|
71
|
+
if (!reason && embed)
|
|
72
|
+
reason = 'embed';
|
|
73
|
+
return { reason, host };
|
|
74
|
+
}
|
|
75
|
+
function createEditorBlock({ el, reason, host }, options = {}) {
|
|
76
|
+
const t = options.translate ?? ((key) => key);
|
|
77
|
+
const id = el.attributes.id;
|
|
78
|
+
const identity = id ? `#${id}` : el.attributes.class ? `.${el.attributes.class.trim().split(/\s+/).slice(0, 2).join('.')}` : el.tagName;
|
|
79
|
+
return {
|
|
80
|
+
...el.outerRange, tagName: el.tagName, ...(id ? { id } : {}), reason, element: el,
|
|
81
|
+
descriptor: reason === 'embed' && host ? host : {
|
|
82
|
+
kindLabel: t(LABELS[reason]), identityLabel: identity.slice(0, 64), presentation: reason === 'embed' ? 'embed' : 'runtime',
|
|
83
|
+
guidanceLabel: t('Locked · Edit in HTML · Preview plays it'), inline: INLINE_TAGS.has(el.tagName),
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
51
87
|
/** Outermost disjoint roots; explicit live islands veto aggregation but never host embed safety. */
|
|
52
88
|
export function detectEditorBlocks({ html }, options = {}) {
|
|
53
89
|
const elements = indexSourceElements({ html }, {});
|
|
@@ -60,48 +96,22 @@ export function detectEditorBlocks({ html }, options = {}) {
|
|
|
60
96
|
};
|
|
61
97
|
elements.forEach(annotate);
|
|
62
98
|
let unsupported = false;
|
|
63
|
-
const walk = (el, live, invalidAncestor) => {
|
|
99
|
+
const walk = ({ el, live, invalidAncestor }, _options = {}) => {
|
|
64
100
|
const mode = el.attributes['data-editor'];
|
|
65
101
|
const isLive = mode === 'live' || (mode !== 'block' && live);
|
|
66
|
-
const
|
|
67
|
-
const host = options.describeHostElement?.(view);
|
|
68
|
-
const embed = !!host || !!options.isProtectedElement?.(view);
|
|
69
|
-
let reason;
|
|
70
|
-
if (!isLive) {
|
|
71
|
-
if (Object.keys(el.attributes).some(name => name.startsWith('data-kui')) && !isEditableEffect(el))
|
|
72
|
-
reason = 'kuinetic';
|
|
73
|
-
else if (el.tagName === 'canvas')
|
|
74
|
-
reason = 'canvas';
|
|
75
|
-
else if (el.tagName === 'video' && 'autoplay' in el.attributes)
|
|
76
|
-
reason = 'autoplay-video';
|
|
77
|
-
else if (mode === 'block')
|
|
78
|
-
reason = 'scripted';
|
|
79
|
-
}
|
|
80
|
-
if (reason && liveIslands.get(el))
|
|
81
|
-
reason = undefined;
|
|
82
|
-
if (!reason && embed)
|
|
83
|
-
reason = 'embed';
|
|
102
|
+
const { reason, host } = describeBlock({ el, isLive, hasLiveDescendants: !!liveIslands.get(el) }, options);
|
|
84
103
|
const invalid = invalidAncestor || !el.outerRange || UNSUPPORTED_CONTEXTS.has(el.tagName);
|
|
85
104
|
if (reason) {
|
|
86
105
|
if (invalid || !el.sourceSupported) {
|
|
87
106
|
unsupported = true;
|
|
88
107
|
return;
|
|
89
108
|
}
|
|
90
|
-
|
|
91
|
-
const id = el.attributes.id;
|
|
92
|
-
const identity = id ? `#${id}` : el.attributes.class ? `.${el.attributes.class.trim().split(/\s+/).slice(0, 2).join('.')}` : el.tagName;
|
|
93
|
-
blocks.push({
|
|
94
|
-
...el.outerRange, tagName: el.tagName, ...(id ? { id } : {}), reason, element: el,
|
|
95
|
-
descriptor: reason === 'embed' && host ? host : {
|
|
96
|
-
kindLabel: t(LABELS[reason]), identityLabel: identity.slice(0, 64), presentation: reason === 'embed' ? 'embed' : 'runtime',
|
|
97
|
-
guidanceLabel: t('Locked · Edit in HTML · Preview plays it'), inline: INLINE_TAGS.has(el.tagName),
|
|
98
|
-
},
|
|
99
|
-
});
|
|
109
|
+
blocks.push(createEditorBlock({ el, reason, host }, options));
|
|
100
110
|
return;
|
|
101
111
|
}
|
|
102
|
-
el.children.forEach(child => walk(child, isLive, invalid));
|
|
112
|
+
el.children.forEach(child => walk({ el: child, live: isLive, invalidAncestor: invalid }, {}));
|
|
103
113
|
};
|
|
104
|
-
elements.forEach(el => walk(el, false, false));
|
|
114
|
+
elements.forEach(el => walk({ el, live: false, invalidAncestor: false }, {}));
|
|
105
115
|
return unsupported || (blocks.length > 0 && elements.some(el => !el.sourceSupported))
|
|
106
116
|
? { ok: false, problem: "Can't edit this source visually. Use HTML." } : { ok: true, blocks, elements };
|
|
107
117
|
}
|
|
@@ -147,8 +157,12 @@ export function prepareEditorSource({ html }, options = {}) {
|
|
|
147
157
|
let namespace = 0;
|
|
148
158
|
while (html.toLowerCase().includes(markerAttribute))
|
|
149
159
|
markerAttribute = `data-jini-raw-source-${++namespace}`;
|
|
160
|
+
const prepared = renderSourceShells({ html, instances, blocks: detected.blocks, markerAttribute }, {});
|
|
161
|
+
return { ok: true, html: prepared, markerAttribute, instances, canvasCss: css.join('\n') };
|
|
162
|
+
}
|
|
163
|
+
function renderSourceShells({ html, instances, blocks, markerAttribute }, _options = {}) {
|
|
150
164
|
let prepared = html;
|
|
151
|
-
const roots = new Map(
|
|
165
|
+
const roots = new Map(blocks.map(block => [block.outerStart, block]));
|
|
152
166
|
for (let i = instances.length - 1; i >= 0; i--) {
|
|
153
167
|
const instance = instances[i];
|
|
154
168
|
const block = roots.get(instance.outerStart);
|
|
@@ -164,7 +178,7 @@ export function prepareEditorSource({ html }, options = {}) {
|
|
|
164
178
|
const shell = `<${tag} ${markerAttribute}="${i}"${layout}>${voidTag && !instance.nonvisual ? '' : `</${tag}>`}`;
|
|
165
179
|
prepared = prepared.slice(0, instance.outerStart) + shell + prepared.slice(instance.outerEnd);
|
|
166
180
|
}
|
|
167
|
-
return
|
|
181
|
+
return prepared;
|
|
168
182
|
}
|
|
169
183
|
/** Exact authored opening tag and its source offsets; no regex-based HTML parsing or id guessing. */
|
|
170
184
|
export function findEditorBlockOpening({ html, target }, _options = {}) {
|
|
@@ -32,10 +32,11 @@ function serializeCleaned(editor) {
|
|
|
32
32
|
// GrapesJS's getCss typing omits its JSON return type; successful init supplies a wrapper.
|
|
33
33
|
const rules = editor.getCss({ json: true, avoidProtected: true, keepUnusedStyles: true });
|
|
34
34
|
const css = rules.map(rule => rule.toCSS()).join('');
|
|
35
|
-
|
|
36
|
-
// `
|
|
37
|
-
//
|
|
38
|
-
|
|
35
|
+
// `getWrapper()` is undefined only before/without a successful init, which cannot be true once this
|
|
36
|
+
// hook's `editor` has fired `update` — and `serializeEditorSource` has already walked the wrapper
|
|
37
|
+
// (`assertProtectedSourceMetadata`) before reaching here. The old `: editor.getHtml()` fallback for a
|
|
38
|
+
// missing wrapper was therefore unreachable and is gone.
|
|
39
|
+
const html = editor.getWrapper().getInnerHTML();
|
|
39
40
|
return css ? `<style>${prettifyCss(css)}</style>${html}` : html;
|
|
40
41
|
}
|
|
41
42
|
/**
|
|
@@ -86,7 +87,7 @@ function serializeWithSplice(editor, original, dirty, structuralChange, sourcePl
|
|
|
86
87
|
let result = original;
|
|
87
88
|
for (const edit of ordered) {
|
|
88
89
|
const range = originalIndex.get(pathKey(edit.path));
|
|
89
|
-
if (range && sourcePlan
|
|
90
|
+
if (range && sourcePlan.instances.some(raw => range.start < raw.outerEnd && range.end > raw.outerStart)) {
|
|
90
91
|
warnFallback('text edit overlaps a protected source region');
|
|
91
92
|
return serializeCleaned(editor);
|
|
92
93
|
}
|
|
@@ -71,6 +71,38 @@ export function protectedCanvasDescriber({ editor }, _options = {}) {
|
|
|
71
71
|
});
|
|
72
72
|
return el => descriptors.get(el);
|
|
73
73
|
}
|
|
74
|
+
function selectedSourceStart({ wrapper, selected, original, html }, _options = {}) {
|
|
75
|
+
// Search only exact authored slices. Copies share bytes, so count preceding equal instances
|
|
76
|
+
// in model order; no mapping export mode or persistent source tokens are needed.
|
|
77
|
+
let copiesBefore = 0;
|
|
78
|
+
let reached = false;
|
|
79
|
+
visit(wrapper, component => {
|
|
80
|
+
if (component === selected)
|
|
81
|
+
reached = true;
|
|
82
|
+
else if (!reached && component.is(PROTECTED_ELEMENT_TYPE) && protectedElementHtml({ component }, {}) === original)
|
|
83
|
+
copiesBefore++;
|
|
84
|
+
});
|
|
85
|
+
let start = -1;
|
|
86
|
+
for (let i = 0; i <= copiesBefore; i++)
|
|
87
|
+
start = html.indexOf(original, start + 1);
|
|
88
|
+
if (start < 0)
|
|
89
|
+
throw new EditorSourceError();
|
|
90
|
+
return start;
|
|
91
|
+
}
|
|
92
|
+
function selectedSourceTarget({ selected, html, start }, _options = {}) {
|
|
93
|
+
const tagName = selected.get('sourceTagName');
|
|
94
|
+
const id = selected.get('sourceId');
|
|
95
|
+
let occurrenceIndex = 0;
|
|
96
|
+
for (;;) {
|
|
97
|
+
const range = findEditorBlockOpening({ html, target: { occurrenceIndex, tagName, ...(id ? { id } : {}) } }, {});
|
|
98
|
+
if (!range || range.start > start)
|
|
99
|
+
throw new EditorSourceError();
|
|
100
|
+
if (range.start === start)
|
|
101
|
+
break;
|
|
102
|
+
occurrenceIndex++;
|
|
103
|
+
}
|
|
104
|
+
return { occurrenceIndex, tagName, ...(id ? { id } : {}) };
|
|
105
|
+
}
|
|
74
106
|
/**
|
|
75
107
|
* Registers the `protected-element` component type so any parsed element `isProtectedElement`
|
|
76
108
|
* recognizes becomes one atomic block: its content can never be edited (excluded from RTE
|
|
@@ -104,33 +136,9 @@ export function registerProtectedElementType({ editor, sourcePlan }, options = {
|
|
|
104
136
|
return;
|
|
105
137
|
const original = protectedElementHtml({ component: selected }, {});
|
|
106
138
|
const html = wrapper.getInnerHTML();
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
let reached = false;
|
|
111
|
-
visit(wrapper, component => {
|
|
112
|
-
if (component === selected)
|
|
113
|
-
reached = true;
|
|
114
|
-
else if (!reached && component.is(PROTECTED_ELEMENT_TYPE) && protectedElementHtml({ component }, {}) === original)
|
|
115
|
-
copiesBefore++;
|
|
116
|
-
});
|
|
117
|
-
let start = -1;
|
|
118
|
-
for (let i = 0; i <= copiesBefore; i++)
|
|
119
|
-
start = html.indexOf(original, start + 1);
|
|
120
|
-
if (start < 0)
|
|
121
|
-
throw new EditorSourceError();
|
|
122
|
-
const tagName = selected.get('sourceTagName');
|
|
123
|
-
const id = selected.get('sourceId');
|
|
124
|
-
let occurrenceIndex = 0;
|
|
125
|
-
for (;;) {
|
|
126
|
-
const range = findEditorBlockOpening({ html, target: { occurrenceIndex, tagName, ...(id ? { id } : {}) } }, {});
|
|
127
|
-
if (!range || range.start > start)
|
|
128
|
-
throw new EditorSourceError();
|
|
129
|
-
if (range.start === start)
|
|
130
|
-
break;
|
|
131
|
-
occurrenceIndex++;
|
|
132
|
-
}
|
|
133
|
-
await requestEditHtml({ occurrenceIndex, tagName, ...(id ? { id } : {}) }, {});
|
|
139
|
+
const start = selectedSourceStart({ wrapper, selected, original, html }, {});
|
|
140
|
+
const target = selectedSourceTarget({ selected, html, start }, {});
|
|
141
|
+
await requestEditHtml(target, {});
|
|
134
142
|
}
|
|
135
143
|
catch (error) {
|
|
136
144
|
options.onSerializationError?.(error instanceof EditorSourceError ? error : new EditorSourceError());
|
|
@@ -161,7 +169,10 @@ export function registerProtectedElementType({ editor, sourcePlan }, options = {
|
|
|
161
169
|
init() {
|
|
162
170
|
// Keep default move/copy/delete toolbar commands, then append the host navigation action.
|
|
163
171
|
if (!this.get('sourceNonvisual') && options.onRequestEditHtml) {
|
|
164
|
-
|
|
172
|
+
// GrapesJS's `Component.initToolbar` fills `toolbar` (even over an explicit `null`) before a
|
|
173
|
+
// type's `init` runs, and a component cannot be constructed without the editor model it
|
|
174
|
+
// needs for that — so the old `?? []` fallback here could not run.
|
|
175
|
+
const toolbar = this.get('toolbar');
|
|
165
176
|
this.set('toolbar', [...toolbar.filter(item => item.command !== command), {
|
|
166
177
|
label: options.editHtmlLabel ?? 'Edit HTML', command,
|
|
167
178
|
// GrapesJS sizes every toolbar item as a 26px icon square; a text label overflowed past the
|
|
@@ -259,10 +259,10 @@ export function useInteractiveHtmlEditor(html, onChange, isProtectedElement, can
|
|
|
259
259
|
};
|
|
260
260
|
const serialize = () => {
|
|
261
261
|
try {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
return
|
|
262
|
+
// No separate marker-leak check here: `serializeEditorSource` already throws an
|
|
263
|
+
// `EditorSourceError` when its result carries `sourcePlan.markerAttribute`, so a second
|
|
264
|
+
// identical check on the same result could never fire.
|
|
265
|
+
return serializeEditorSource({ editor, html, edits: dirty, sourcePlan }, { structuralChange });
|
|
266
266
|
}
|
|
267
267
|
catch (error) {
|
|
268
268
|
const failure = error instanceof EditorSourceError ? error : new EditorSourceError();
|
|
@@ -11,6 +11,20 @@ function isElement(node) {
|
|
|
11
11
|
function hasChildNodes(node) {
|
|
12
12
|
return 'childNodes' in node;
|
|
13
13
|
}
|
|
14
|
+
function recordInnerRange({ node, currentPath, out }, _options = {}) {
|
|
15
|
+
const location = node.sourceCodeLocation;
|
|
16
|
+
// parse5 attaches a location only to elements created from a start-tag token, so a located
|
|
17
|
+
// element always has `startTag` (implied elements — a `tbody`, a `</p>`- or `</br>`-created
|
|
18
|
+
// element — carry no location at all and are skipped). Requiring `startTag` here replaces the old
|
|
19
|
+
// `: location.endOffset` fallback, which no parsed element could reach.
|
|
20
|
+
if (location?.startTag) {
|
|
21
|
+
// Void elements (`<br>`, `<img>`) have no `endTag` and no inner content: their range is
|
|
22
|
+
// empty (`start === end`), anchored at the end of the start tag.
|
|
23
|
+
const start = location.startTag.endOffset;
|
|
24
|
+
const end = location.endTag ? location.endTag.startOffset : start;
|
|
25
|
+
out.set(pathKey(currentPath), { start, end, tagName: node.tagName });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
14
28
|
/**
|
|
15
29
|
* Walks one level of element-only children (recursing into each), assigning each a path and
|
|
16
30
|
* recording its source range. `style`/`script` children are skipped without consuming a path slot.
|
|
@@ -25,14 +39,7 @@ function indexChildren(children, parentPath, out) {
|
|
|
25
39
|
if (UNINDEXED_TAGS.has(node.tagName))
|
|
26
40
|
continue;
|
|
27
41
|
const currentPath = [...parentPath, elementIndex];
|
|
28
|
-
|
|
29
|
-
if (location) {
|
|
30
|
-
// Void elements (`<br>`, `<img>`) have no `endTag` and no inner content: their range is
|
|
31
|
-
// empty (`start === end`), anchored at the end of the start tag.
|
|
32
|
-
const start = location.startTag ? location.startTag.endOffset : location.endOffset;
|
|
33
|
-
const end = location.endTag ? location.endTag.startOffset : start;
|
|
34
|
-
out.set(pathKey(currentPath), { start, end, tagName: node.tagName });
|
|
35
|
-
}
|
|
42
|
+
recordInnerRange({ node, currentPath, out }, {});
|
|
36
43
|
if (hasChildNodes(node))
|
|
37
44
|
indexChildren(node.childNodes, currentPath, out);
|
|
38
45
|
elementIndex++;
|
|
@@ -87,6 +94,35 @@ export function spliceElementInner(html, path, expectedTagName, newInner) {
|
|
|
87
94
|
return html.slice(0, range.start) + newInner + html.slice(range.end);
|
|
88
95
|
}
|
|
89
96
|
const VOID_TAGS = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
|
|
97
|
+
function sourceOuterEnd({ node, html }, _options = {}) {
|
|
98
|
+
const loc = node.sourceCodeLocation;
|
|
99
|
+
const start = loc?.startTag;
|
|
100
|
+
const foreignSelfClosing = node.namespaceURI !== 'http://www.w3.org/1999/xhtml' && !!start
|
|
101
|
+
&& html.slice(start.startOffset, start.endOffset).endsWith('/>');
|
|
102
|
+
if (VOID_TAGS.has(node.tagName) || foreignSelfClosing)
|
|
103
|
+
return start?.endOffset;
|
|
104
|
+
return loc?.endTag?.endOffset;
|
|
105
|
+
}
|
|
106
|
+
function sourceOuterRange({ node, html, parentStart, parentEnd }, _options = {}) {
|
|
107
|
+
const start = node.sourceCodeLocation?.startTag;
|
|
108
|
+
const outerEnd = sourceOuterEnd({ node, html }, {});
|
|
109
|
+
const boundsValid = !!start && outerEnd !== undefined && start.startOffset >= parentStart && outerEnd <= parentEnd;
|
|
110
|
+
// Template content is a separate fragment; it has no ordinary component path/shell here.
|
|
111
|
+
if (!boundsValid || node.tagName === 'template')
|
|
112
|
+
return null;
|
|
113
|
+
return { outerStart: start.startOffset, outerEnd: outerEnd, openingEnd: start.endOffset };
|
|
114
|
+
}
|
|
115
|
+
function invalidateOverlappingSiblings({ elements }, _options = {}) {
|
|
116
|
+
// Foster parenting can move a valid-looking node ahead of a source-earlier table. Neither
|
|
117
|
+
// sibling can be trusted as a model-addressable source owner in that recovered ordering.
|
|
118
|
+
for (let i = 1; i < elements.length; i++) {
|
|
119
|
+
const previous = elements[i - 1];
|
|
120
|
+
const current = elements[i];
|
|
121
|
+
if (previous.outerRange && current.outerRange && previous.outerRange.outerEnd > current.outerRange.outerStart) {
|
|
122
|
+
previous.sourceSupported = current.sourceSupported = false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
90
126
|
/**
|
|
91
127
|
* Extends the inner-splice index without changing its published range shape. Missing end tags,
|
|
92
128
|
* implicit nodes and reordered/foster-parented children cannot establish an opaque source owner.
|
|
@@ -94,41 +130,26 @@ const VOID_TAGS = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'i
|
|
|
94
130
|
*/
|
|
95
131
|
export function indexSourceElements({ html }, _options = {}) {
|
|
96
132
|
const fragment = parseFragment(html, { sourceCodeLocationInfo: true });
|
|
97
|
-
const walk = (nodes, parentPath, parentStart, parentEnd) => {
|
|
133
|
+
const walk = ({ nodes, parentPath, parentStart, parentEnd }, _options = {}) => {
|
|
98
134
|
let slot = 0;
|
|
99
135
|
const elements = nodes.filter(isElement).map(node => {
|
|
100
136
|
const atom = UNINDEXED_TAGS.has(node.tagName);
|
|
101
137
|
const path = atom ? null : [...parentPath, slot++];
|
|
102
138
|
const loc = node.sourceCodeLocation;
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
const voidTag = VOID_TAGS.has(node.tagName) || (node.namespaceURI !== 'http://www.w3.org/1999/xhtml'
|
|
106
|
-
&& !!start && html.slice(start.startOffset, start.endOffset).endsWith('/>'));
|
|
107
|
-
const outerEnd = voidTag ? start?.endOffset : end?.endOffset;
|
|
108
|
-
const boundsValid = !!start && outerEnd !== undefined && start.startOffset >= parentStart && outerEnd <= parentEnd;
|
|
109
|
-
const children = walk(node.childNodes, path ?? parentPath, start?.endOffset ?? parentStart, end?.startOffset ?? parentEnd);
|
|
110
|
-
// Template content is a separate fragment; it has no ordinary component path/shell here.
|
|
111
|
-
const valid = boundsValid && node.tagName !== 'template';
|
|
139
|
+
const children = walk({ nodes: node.childNodes, parentPath: path ?? parentPath, parentStart: loc?.startTag?.endOffset ?? parentStart, parentEnd: loc?.endTag?.startOffset ?? parentEnd }, {});
|
|
140
|
+
const outerRange = sourceOuterRange({ node, html, parentStart, parentEnd }, {});
|
|
112
141
|
return {
|
|
113
142
|
tagName: node.tagName,
|
|
114
143
|
attributes: Object.fromEntries(node.attrs.map(attr => [attr.name, attr.value])),
|
|
115
144
|
path,
|
|
116
|
-
outerRange
|
|
117
|
-
sourceSupported:
|
|
145
|
+
outerRange,
|
|
146
|
+
sourceSupported: !!outerRange && children.every(child => child.sourceSupported),
|
|
118
147
|
children,
|
|
119
148
|
};
|
|
120
149
|
});
|
|
121
|
-
|
|
122
|
-
// sibling can be trusted as a model-addressable source owner in that recovered ordering.
|
|
123
|
-
for (let i = 1; i < elements.length; i++) {
|
|
124
|
-
const previous = elements[i - 1];
|
|
125
|
-
const current = elements[i];
|
|
126
|
-
if (previous.outerRange && current.outerRange && previous.outerRange.outerEnd > current.outerRange.outerStart) {
|
|
127
|
-
previous.sourceSupported = current.sourceSupported = false;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
150
|
+
invalidateOverlappingSiblings({ elements }, {});
|
|
130
151
|
return elements;
|
|
131
152
|
};
|
|
132
|
-
return walk(fragment.childNodes, [], 0, html.length);
|
|
153
|
+
return walk({ nodes: fragment.childNodes, parentPath: [], parentStart: 0, parentEnd: html.length }, {});
|
|
133
154
|
}
|
|
134
155
|
//# sourceMappingURL=source-splice.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export const SETTINGS_DIALOG_JA = {
|
|
2
2
|
"I removed what looked like a key from your message. If it was real, rotate it. Use the secure form to save it.": "メッセージからキーと思われる文字列を削除しました。本物のキーだった場合は再発行してください。保存には安全なフォームを使ってください。",
|
|
3
|
+
// eslint-disable-next-line sonarjs/no-hardcoded-passwords -- Translation of user-facing credential-safety guidance; the value is prose, not a password.
|
|
3
4
|
"Use the secure form to save keys, tokens, passwords, and secrets. Chat answers are sent to the assistant.": "キー、トークン、パスワード、シークレットの保存には安全なフォームを使ってください。チャットの回答はアシスタントに送信されます。",
|
|
4
5
|
'# My skill\n\n1. Explain the workflow.\n2. Describe the inputs and outputs.': '# マイスキル\n\n1. ワークフローを説明してください。\n2. 入力と出力を説明してください。',
|
|
5
6
|
'A custom path override did not work, so this used the automatically detected binary instead.': 'カスタムパスの上書きが機能しなかったため、自動検出されたバイナリを代わりに使用しました。',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export const SETTINGS_DIALOG_ZH_CN = {
|
|
2
2
|
"I removed what looked like a key from your message. If it was real, rotate it. Use the secure form to save it.": "我已从你的消息中移除疑似密钥的内容。如果是真实密钥,请更换它。请使用安全表单保存。",
|
|
3
|
+
// eslint-disable-next-line sonarjs/no-hardcoded-passwords -- Translation of user-facing credential-safety guidance; the value is prose, not a password.
|
|
3
4
|
"Use the secure form to save keys, tokens, passwords, and secrets. Chat answers are sent to the assistant.": "请使用安全表单保存密钥、令牌、密码和机密信息。聊天回答会发送给助手。",
|
|
4
5
|
'# My skill\n\n1. Explain the workflow.\n2. Describe the inputs and outputs.': '# 我的技能\n\n1. 说明工作流程。\n2. 描述输入和输出。',
|
|
5
6
|
'A custom path override did not work, so this used the automatically detected binary instead.': '自定义路径覆盖未生效,因此改用自动检测到的二进制文件。',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export const SETTINGS_DIALOG_ZH_TW = {
|
|
2
2
|
"I removed what looked like a key from your message. If it was real, rotate it. Use the secure form to save it.": "我已從你的訊息中移除疑似金鑰的內容。如果是真實金鑰,請更換它。請使用安全表單儲存。",
|
|
3
|
+
// eslint-disable-next-line sonarjs/no-hardcoded-passwords -- Translation of user-facing credential-safety guidance; the value is prose, not a password.
|
|
3
4
|
"Use the secure form to save keys, tokens, passwords, and secrets. Chat answers are sent to the assistant.": "請使用安全表單儲存金鑰、權杖、密碼和機密資訊。聊天回答會傳送給助理。",
|
|
4
5
|
'# My skill\n\n1. Explain the workflow.\n2. Describe the inputs and outputs.': '# 我的技能\n\n1. 說明工作流程。\n2. 描述輸入與輸出。',
|
|
5
6
|
'A custom path override did not work, so this used the automatically detected binary instead.': '自訂路徑覆寫未生效,因此改用自動偵測到的二進位檔。',
|
|
@@ -21,25 +21,29 @@ export function resolveSystemLocale(languages, supportedLocales) {
|
|
|
21
21
|
const normalized = raw.trim();
|
|
22
22
|
if (!normalized)
|
|
23
23
|
continue;
|
|
24
|
-
const
|
|
25
|
-
if (
|
|
26
|
-
return
|
|
27
|
-
const [language, regionOrScript] = normalized.toLowerCase().split('-');
|
|
28
|
-
if (language === 'zh') {
|
|
29
|
-
const isTraditionalScript = regionOrScript === 'hant' ||
|
|
30
|
-
regionOrScript === 'tw' ||
|
|
31
|
-
regionOrScript === 'hk' ||
|
|
32
|
-
regionOrScript === 'mo';
|
|
33
|
-
const candidate = isTraditionalScript ? 'zh-TW' : 'zh-CN';
|
|
34
|
-
if (supportedLocales.includes(candidate))
|
|
35
|
-
return candidate;
|
|
36
|
-
}
|
|
37
|
-
const baseMatch = supportedLocales.find((locale) => locale.toLowerCase().split('-')[0] === language);
|
|
38
|
-
if (baseMatch)
|
|
39
|
-
return baseMatch;
|
|
24
|
+
const match = matchPreferredLocale({ normalized, supportedLocales });
|
|
25
|
+
if (match)
|
|
26
|
+
return match;
|
|
40
27
|
}
|
|
41
28
|
return null;
|
|
42
29
|
}
|
|
30
|
+
function matchPreferredLocale({ normalized, supportedLocales }, _options = {}) {
|
|
31
|
+
const exact = supportedLocales.find((locale) => locale.toLowerCase() === normalized.toLowerCase());
|
|
32
|
+
if (exact)
|
|
33
|
+
return exact;
|
|
34
|
+
const [language, regionOrScript] = normalized.toLowerCase().split('-');
|
|
35
|
+
if (language === 'zh') {
|
|
36
|
+
const isTraditionalScript = regionOrScript === 'hant' ||
|
|
37
|
+
regionOrScript === 'tw' ||
|
|
38
|
+
regionOrScript === 'hk' ||
|
|
39
|
+
regionOrScript === 'mo';
|
|
40
|
+
const candidate = isTraditionalScript ? 'zh-TW' : 'zh-CN';
|
|
41
|
+
if (supportedLocales.includes(candidate))
|
|
42
|
+
return candidate;
|
|
43
|
+
}
|
|
44
|
+
const baseMatch = supportedLocales.find((locale) => locale.toLowerCase().split('-')[0] === language);
|
|
45
|
+
return baseMatch || null;
|
|
46
|
+
}
|
|
43
47
|
/** Reads `navigator.languages` (falling back to `navigator.language`). */
|
|
44
48
|
function browserPreferredLanguages() {
|
|
45
49
|
if (typeof navigator === 'undefined')
|