@jini-ai/ui 0.5.4 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/core/theme/validation.js +40 -38
- package/dist/features/a2ui/renderer.js +15 -13
- package/dist/features/about/react/components/AboutTab.js +64 -34
- package/dist/features/about/rules.d.ts +2 -2
- package/dist/features/about/rules.js +156 -129
- package/dist/features/admin-widgets/components/ImagePreviewModal.d.ts +4 -3
- package/dist/features/admin-widgets/components/ImagePreviewModal.js +6 -10
- package/dist/features/admin-widgets/components/InfoTip.d.ts +3 -2
- package/dist/features/admin-widgets/components/InfoTip.hooks.js +2 -0
- package/dist/features/admin-widgets/components/InfoTip.js +34 -7
- package/dist/features/admin-widgets/components/InfoTip.rules.d.ts +13 -0
- package/dist/features/admin-widgets/components/InfoTip.rules.js +43 -10
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.d.ts +30 -0
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.d.ts +40 -0
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.js +128 -0
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.js +18 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.d.ts +14 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.js +12 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.rules.d.ts +20 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.rules.js +21 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.d.ts +18 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.d.ts +16 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.js +45 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.js +18 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.rules.d.ts +32 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.rules.js +26 -0
- package/dist/features/admin-widgets/components/Select/Select.hooks.d.ts +21 -0
- package/dist/features/admin-widgets/components/Select/Select.hooks.js +24 -2
- package/dist/features/admin-widgets/components/Select/Select.js +4 -3
- package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.hooks.js +29 -14
- package/dist/features/admin-widgets/index.d.ts +14 -2
- package/dist/features/admin-widgets/index.js +7 -1
- package/dist/features/admin-widgets/styles/admin-widgets.css +1 -0
- package/dist/features/asset-grid/react/components/AssetCard.js +7 -1
- package/dist/features/asset-grid/react/components/AssetGrid.d.ts +1 -1
- package/dist/features/asset-grid/react/components/AssetGrid.js +38 -18
- package/dist/features/asset-grid/react/components/useAssetGridFilters.d.ts +21 -0
- package/dist/features/asset-grid/react/components/useAssetGridFilters.js +19 -0
- package/dist/features/asset-grid/react/hooks/useAssetGridKeyboardShortcuts.js +16 -11
- package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.d.ts +2 -0
- package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.js +35 -11
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.d.ts +6 -6
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.js +10 -129
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.d.ts +50 -0
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.js +95 -0
- package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.d.ts +56 -0
- package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.js +80 -0
- package/dist/features/asset-tree-browser/rules.js +40 -22
- package/dist/features/browser-chrome/rules.js +74 -70
- package/dist/features/charts/browser.js +11 -5
- package/dist/features/charts/react.js +2 -2
- package/dist/features/charts/table.js +2 -2
- package/dist/features/command-palette/react/hooks/useCommandPalette.js +3 -9
- package/dist/features/connectors/ConnectorsBrowser.d.ts +4 -4
- package/dist/features/connectors/ConnectorsBrowser.js +26 -14
- package/dist/features/connectors/components/ConnectorCard.js +67 -38
- package/dist/features/connectors/components/ConnectorDetailDrawer.js +62 -31
- package/dist/features/connectors/components/ConnectorGrid.js +11 -6
- package/dist/features/connectors/hooks/useConnectorAuthorization.js +6 -0
- package/dist/features/connectors/hooks/useConnectorCatalog.d.ts +2 -0
- package/dist/features/connectors/hooks/useConnectorCatalog.js +32 -13
- package/dist/features/connectors/hooks/useWiredConnectorsBrowser.d.ts +3 -3
- package/dist/features/connectors/rules.d.ts +8 -1
- package/dist/features/connectors/rules.js +42 -20
- package/dist/features/connectors/types.d.ts +11 -0
- package/dist/features/execution/constants.d.ts +4 -10
- package/dist/features/execution/constants.js +9 -94
- package/dist/features/execution/react/components/ByokProviderForm.d.ts +2 -4
- package/dist/features/execution/react/components/ByokProviderForm.js +140 -43
- package/dist/features/execution/react/components/ExecutionTab.d.ts +1 -2
- package/dist/features/execution/react/components/ExecutionTab.js +52 -30
- package/dist/features/execution/react/components/LocalCliAgentCard.js +12 -3
- package/dist/features/execution/react/components/LocalCliAgentList.js +14 -3
- package/dist/features/execution/react/components/LocalCliAgentSettings.js +14 -3
- package/dist/features/execution/react/hooks/useLocalCliAgentCard.js +54 -36
- package/dist/features/execution/react/hooks/useReasoningControl.d.ts +1 -1
- package/dist/features/execution/react/hooks/useReasoningControl.js +116 -75
- package/dist/features/execution/rules.d.ts +13 -2
- package/dist/features/execution/rules.js +51 -11
- package/dist/features/execution/types.d.ts +6 -42
- package/dist/features/execution/types.js +0 -17
- package/dist/features/external-mcp/react/components/ExternalMcpTab.d.ts +6 -2
- package/dist/features/external-mcp/react/components/ExternalMcpTab.js +9 -2
- package/dist/features/file-dropzone/react/components/FileDropzone.js +24 -25
- package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.d.ts +2 -2
- package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.js +35 -30
- package/dist/features/file-dropzone/react/components/FileDropzoneThumbnailGrid.js +30 -24
- package/dist/features/file-dropzone/react/components/FileDropzoneZone.d.ts +14 -0
- package/dist/features/file-dropzone/react/components/FileDropzoneZone.js +26 -0
- package/dist/features/html-editor/canvas-content-wrapper.js +13 -13
- package/dist/features/html-editor/canvas-embed-placeholders.js +46 -37
- package/dist/features/html-editor/css.js +76 -69
- package/dist/features/html-editor/editor-blocks.js +47 -33
- package/dist/features/html-editor/editor-serialization.js +6 -5
- package/dist/features/html-editor/protected-elements.js +39 -28
- package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.js +4 -4
- package/dist/features/html-editor/source-splice.js +51 -30
- package/dist/features/i18n/dictionaries/settings-dialog.ja.js +1 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +1 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +1 -0
- package/dist/features/i18n/locale.js +20 -16
- package/dist/features/iframe-pool/react/dom-sync.js +8 -6
- package/dist/features/integrations/react/components/CodexInstallToggleButton.js +19 -4
- package/dist/features/integrations/react/components/IntegrationsTab.js +60 -14
- package/dist/features/interactive-ui/interactive-ui.css +1 -1
- package/dist/features/interactive-ui/providers/native/data-table.manifest.d.ts +6 -6
- package/dist/features/interactive-ui/providers/shadcn/data-table.js +10 -7
- package/dist/features/interactive-ui/providers/shadcn/data-table.manifest.d.ts +6 -6
- package/dist/features/lexical-rich-text-editor/mention-parser.js +60 -57
- package/dist/features/lexical-rich-text-editor/react/hooks/useKeyboardCommands.js +33 -38
- package/dist/features/lexical-rich-text-editor/rules.js +87 -74
- package/dist/features/lexical-rich-text-editor/serialize.js +27 -25
- package/dist/features/mcp-ui/secret-card.js +111 -61
- package/dist/features/mcp-ui/surfaces/confirmation.js +39 -21
- package/dist/features/mcp-ui/surfaces/document.d.ts +1 -1
- package/dist/features/mcp-ui/surfaces/document.js +10 -2
- package/dist/features/mcp-ui/surfaces/fields.js +30 -50
- package/dist/features/mcp-ui/surfaces/form.d.ts +6 -0
- package/dist/features/mcp-ui/surfaces/form.js +43 -21
- package/dist/features/mcp-ui/surfaces/outcome.js +30 -20
- package/dist/features/mcp-ui/surfaces/text-input.js +33 -21
- package/dist/features/mcp-ui/surfaces/tokens.d.ts +6 -1
- package/dist/features/mcp-ui/surfaces/tokens.js +5 -2
- package/dist/features/media-providers/constants.d.ts +1 -0
- package/dist/features/media-providers/constants.js +2 -81
- package/dist/features/media-providers/index.d.ts +1 -1
- package/dist/features/media-providers/ports.d.ts +2 -0
- package/dist/features/media-providers/react/components/MediaProviderCard.d.ts +22 -0
- package/dist/features/media-providers/react/components/MediaProviderCard.js +50 -0
- package/dist/features/media-providers/react/components/MediaProvidersTab.d.ts +5 -6
- package/dist/features/media-providers/react/components/MediaProvidersTab.js +40 -72
- package/dist/features/media-providers/react/components/media-providers-tab-labels.d.ts +11 -0
- package/dist/features/media-providers/react/components/media-providers-tab-labels.js +57 -0
- package/dist/features/media-providers/react/hooks/useMediaProvidersTab.d.ts +4 -2
- package/dist/features/media-providers/react/hooks/useMediaProvidersTab.js +37 -23
- package/dist/features/media-providers/rules.js +17 -10
- package/dist/features/media-providers/types.d.ts +4 -22
- package/dist/features/media-providers/types.js +3 -8
- package/dist/features/memory/formatters.js +127 -92
- package/dist/features/memory/react/components/MemoryConnectedPanel.js +92 -36
- package/dist/features/memory/react/components/MemoryList.js +37 -18
- package/dist/features/memory/react/hooks/useMemoryExtractions.store.js +42 -36
- package/dist/features/notifications/react/components/NotificationsTab.js +28 -28
- package/dist/features/observability/long-task.d.ts +0 -12
- package/dist/features/observability/long-task.js +15 -19
- package/dist/features/observability/resource-error.d.ts +0 -10
- package/dist/features/observability/resource-error.js +18 -13
- package/dist/features/observability/stuck-run.js +10 -6
- package/dist/features/observability/telemetry-url.d.ts +6 -0
- package/dist/features/observability/telemetry-url.js +16 -0
- package/dist/features/observability/white-screen.js +12 -24
- package/dist/features/panel-kit/fetch-query/adapter.react.d.ts +2 -2
- package/dist/features/panel-kit/fetch-query/adapter.react.js +4 -2
- package/dist/features/panel-kit/fetch-query/adapter.tanstack.js +46 -33
- package/dist/features/panel-kit/helpers/standing-draft-local-backup.js +3 -1
- package/dist/features/panel-kit/hooks/use-settings-slice.hooks.js +3 -1
- package/dist/features/privacy/react/components/PrivacyTab.js +24 -13
- package/dist/features/progress-card/components/ProgressCard.js +7 -3
- package/dist/features/progress-card/reference-adapters.js +59 -54
- package/dist/features/project-locations/react/components/ProjectLocationsTab.js +24 -15
- package/dist/features/project-locations/react/hooks/useProjectLocationsTab.d.ts +8 -0
- package/dist/features/project-locations/react/hooks/useProjectLocationsTab.js +31 -23
- package/dist/features/resource-dashboard/react/components/ResourceBoard.js +6 -47
- package/dist/features/resource-dashboard/react/components/ResourceBoardView.d.ts +2 -2
- package/dist/features/resource-dashboard/react/components/ResourceBoardView.js +3 -6
- package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.d.ts +15 -0
- package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.js +35 -0
- package/dist/features/resource-dashboard/react/components/ResourceCard.js +5 -16
- package/dist/features/resource-dashboard/react/components/ResourceCardParts.d.ts +34 -0
- package/dist/features/resource-dashboard/react/components/ResourceCardParts.js +34 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowList.js +3 -1
- package/dist/features/resource-dashboard/react/components/ResourceRowListItem.js +2 -2
- package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.d.ts +19 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.js +11 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowListView.d.ts +1 -1
- package/dist/features/resource-dashboard/react/components/ResourceRowListView.js +4 -4
- package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.d.ts +10 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.js +20 -0
- package/dist/features/resource-dashboard/react/components/resourceBoardProps.d.ts +48 -0
- package/dist/features/resource-dashboard/react/components/resourceBoardProps.js +70 -0
- package/dist/features/resource-dashboard/react/components/resourceRowListProps.d.ts +19 -0
- package/dist/features/resource-dashboard/react/components/resourceRowListProps.js +14 -0
- package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.d.ts +16 -0
- package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.js +13 -0
- package/dist/features/resource-dashboard/rules.js +2 -8
- package/dist/features/sketch-editor/dom.js +150 -99
- package/dist/features/sketch-editor/react/components/SketchEditor.js +26 -15
- package/dist/features/skills/react/components/SkillDraftForm.js +13 -19
- package/dist/features/skills/react/components/SkillDraftForm.labels.d.ts +31 -0
- package/dist/features/skills/react/components/SkillDraftForm.labels.js +54 -0
- package/dist/features/skills/react/components/SkillRow.js +38 -18
- package/dist/features/skills/react/components/SkillRow.labels.d.ts +7 -0
- package/dist/features/skills/react/components/SkillRow.labels.js +18 -0
- package/dist/features/skills/react/components/SkillsTab.js +24 -14
- package/dist/features/skills/react/components/SkillsTab.labels.d.ts +7 -0
- package/dist/features/skills/react/components/SkillsTab.labels.js +11 -0
- package/dist/features/skills/rules.js +32 -23
- package/dist/features/source-config-list/agent-handles.js +2 -1
- package/dist/features/source-config-list/dependencies.js +53 -49
- package/dist/features/source-config-list/index.d.ts +3 -3
- package/dist/features/source-config-list/react/components/SourceConfigAddForm.d.ts +6 -5
- package/dist/features/source-config-list/react/components/SourceConfigAddForm.js +38 -7
- package/dist/features/source-config-list/react/components/SourceConfigField.js +69 -25
- package/dist/features/source-config-list/react/components/SourceConfigItemCard.d.ts +6 -5
- package/dist/features/source-config-list/react/components/SourceConfigItemCard.js +76 -20
- package/dist/features/source-config-list/react/components/SourceConfigListView.d.ts +5 -3
- package/dist/features/source-config-list/react/components/SourceConfigListView.js +33 -3
- package/dist/features/source-config-list/react/components/SourceConfigTestControl.js +17 -1
- package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.d.ts +11 -0
- package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.js +12 -0
- package/dist/features/source-config-list/types.d.ts +14 -0
- package/dist/features/tab-launcher-menu/react/components/TabLauncherMenu.js +25 -1
- package/dist/features/tab-launcher-menu/react/hooks/useTabLauncherMenu.js +15 -16
- package/dist/features/tab-strip/react/components/TabBar.js +8 -1
- package/dist/features/tabbed-dialog/react/components/TabbedDialog.js +8 -30
- package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.d.ts +15 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.js +8 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.d.ts +12 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.js +6 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.d.ts +27 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.js +12 -0
- package/dist/features/tabbed-dialog/react/components/tabbedDialogView.d.ts +43 -0
- package/dist/features/tabbed-dialog/react/components/tabbedDialogView.js +59 -0
- package/dist/features/version-manager/dependencies.d.ts +3 -1
- package/dist/features/version-manager/dependencies.js +3 -2
- package/dist/features/version-manager/react/components/VersionManagerModal.js +10 -6
- package/dist/features/version-manager/react/hooks/useVersionManager.js +7 -4
- package/dist/features/viewer-shell/react/components/CommentSidePanel.js +103 -50
- package/dist/features/viewer-shell/react/components/MarkdownSplitPane.js +28 -8
- package/dist/features/viewer-shell/react/hooks/useCommentReorder.js +22 -16
- package/dist/features/viewer-shell/rules.js +19 -23
- package/dist/react/components/CustomSelect.d.ts +38 -1
- package/dist/react/components/CustomSelect.js +94 -38
- package/dist/react/components/OnboardingDropdown.js +20 -8
- package/dist/react/components/RemixIcon.stylesheet.js +3 -1
- package/dist/react/components/Toast.js +13 -2
- package/dist/react/components/TooltipLayer.js +5 -14
- package/dist/react/components/WorkingDirPicker.js +78 -50
- package/dist/react/components/remixicon-font-recovery.js +38 -30
- package/dist/react/components/select-popover.d.ts +8 -6
- package/dist/react/components/select-popover.js +33 -29
- package/dist/react/mcp-ui/McpUiHost.d.ts +1 -1
- package/dist/react/mcp-ui/McpUiHost.js +10 -11
- package/dist/react/mcp-ui/host-message-source.d.ts +14 -2
- package/dist/react/mcp-ui/host-message-source.js +15 -5
- package/dist/react/mcp-ui/mcp-ui-host-frame.d.ts +41 -0
- package/dist/react/mcp-ui/mcp-ui-host-frame.js +39 -0
- package/dist/renderers/annotation-canvas/react/components/AnnotationCanvas.js +97 -69
- package/dist/renderers/preview-modal-shell/react/components/PreviewModalShell.js +59 -30
- package/dist/renderers/preview-modal-shell/react/components/icons.js +4 -1
- package/dist/renderers/react/components/ArtifactView.js +9 -6
- package/dist/renderers/srcdoc/build.d.ts +4 -1
- package/dist/renderers/srcdoc/build.js +62 -48
- package/dist/styles/admin.css +29 -14
- package/dist/styles/lightbox.css +142 -0
- package/dist/styles/variables.css +128 -0
- package/dist/theme/apply.js +9 -5
- package/dist/utils/auto-open-file.d.ts +2 -1
- package/dist/utils/auto-open-file.js +69 -85
- package/dist/utils/markdown-scroll-sync.js +108 -87
- package/dist/utils/polygon-selection.js +25 -26
- package/dist/utils/smooth-scroll-to-top.d.ts +1 -1
- package/dist/utils/smooth-scroll-to-top.js +2 -32
- package/dist/utils/sse.js +26 -26
- package/dist/utils/unit-bezier.d.ts +7 -0
- package/dist/utils/unit-bezier.js +38 -0
- package/package.json +7 -10
- package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.d.ts +0 -13
- package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.js +0 -46
|
@@ -145,3 +145,131 @@
|
|
|
145
145
|
--jini-modal-padding: 24px;
|
|
146
146
|
--jini-sidebar-width: 240px;
|
|
147
147
|
}
|
|
148
|
+
|
|
149
|
+
/* Material/role contract (ADR-069). Theme inputs are owned by admin's default theme or
|
|
150
|
+
a host theme. Structural consumers opt into these roles at their existing paint owners.
|
|
151
|
+
Existing palette fallbacks above remain for hosts that have not imported a theme yet. */
|
|
152
|
+
:root, [data-admin-theme], [data-color-scheme] {
|
|
153
|
+
--jini-app-bg: var(--jini-theme-app-bg, var(--jini-default-app-bg)); /* Application canvas background. */
|
|
154
|
+
--jini-shell-bg: var(--jini-theme-shell-bg, var(--jini-bg)); /* Shell over the application canvas. */
|
|
155
|
+
--jini-panel-bg: var(--jini-theme-panel-bg, var(--jini-default-panel-bg)); /* Card/panel fill. */
|
|
156
|
+
--jini-panel-border: var(--jini-theme-panel-border, var(--jini-default-panel-border)); /* Card/panel border color. */
|
|
157
|
+
--jini-panel-shadow: var(--jini-theme-panel-shadow, var(--jini-default-panel-shadow)); /* Card/panel elevation. */
|
|
158
|
+
--jini-panel-backdrop: var(--jini-theme-panel-backdrop); /* Panel backdrop-filter. */
|
|
159
|
+
--jini-panel-texture: var(--jini-theme-panel-texture); /* Panel background-image layer. */
|
|
160
|
+
--jini-rail-bg: var(--jini-theme-rail-bg, var(--jini-panel-bg)); /* Navigation rail fill. */
|
|
161
|
+
--jini-rail-border: var(--jini-theme-rail-border, var(--jini-panel-border)); /* Navigation rail edge. */
|
|
162
|
+
--jini-rail-shadow: var(--jini-theme-rail-shadow, none); /* Navigation rail elevation. */
|
|
163
|
+
--jini-rail-backdrop: var(--jini-theme-rail-backdrop, var(--jini-panel-backdrop, none)); /* Rail frost layer. */
|
|
164
|
+
--jini-overlay-bg: var(--jini-theme-overlay-bg, var(--jini-default-overlay-bg)); /* Modal scrim background. */
|
|
165
|
+
--jini-overlay-backdrop: var(--jini-theme-overlay-backdrop); /* Modal scrim backdrop-filter. */
|
|
166
|
+
--jini-overlay-shadow: var(--jini-theme-overlay-shadow, var(--jini-default-overlay-shadow)); /* Floating dialog elevation. */
|
|
167
|
+
--jini-control-preview-backdrop: var(--jini-theme-control-preview-backdrop, blur(6px)); /* Preview action filter. */
|
|
168
|
+
--jini-control-bg: var(--jini-theme-control-bg, var(--jini-default-control-bg)); /* Neutral input/control fill. */
|
|
169
|
+
--jini-control-border: var(--jini-theme-control-border, var(--jini-default-control-border)); /* Neutral input/control border color. */
|
|
170
|
+
--jini-control-shadow: var(--jini-theme-control-shadow); /* Resting control shadow. */
|
|
171
|
+
--jini-control-shadow-hover: var(--jini-theme-control-shadow-hover); /* Hovered control shadow. */
|
|
172
|
+
--jini-control-shadow-pressed: var(--jini-theme-control-shadow-pressed); /* Pressed control shadow. */
|
|
173
|
+
--jini-control-shadow-inset: var(--jini-theme-control-shadow-inset); /* Inset control shadow. */
|
|
174
|
+
--jini-border-width: var(--jini-theme-border-width); /* Standard primitive border width. */
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* Additive paint/elevation roles (ADR-069 step 2). New roles have exact compatibility
|
|
178
|
+
fallbacks here; existing material defaults remain admin-owned and unchanged. Theme inputs
|
|
179
|
+
win over fallbacks, including when inherited. Keep complete shadows and SVG images intact:
|
|
180
|
+
custom properties cannot interpolate into a data-URL SVG's stroke attribute. */
|
|
181
|
+
:root, [data-admin-theme], [data-color-scheme] {
|
|
182
|
+
--jini-control-chevron-image: var(--jini-theme-control-chevron-image, url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 14 14' fill='none'%3E%3Cpath d='M3.5 5.5 7 9l3.5-3.5' stroke='%23666C75' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"));
|
|
183
|
+
--jini-overlay-tooltip-bg: var(--jini-theme-overlay-tooltip-bg, #fff);
|
|
184
|
+
--jini-overlay-tooltip-text: var(--jini-theme-overlay-tooltip-text, #111);
|
|
185
|
+
--jini-overlay-tooltip-border: var(--jini-theme-overlay-tooltip-border, rgba(0, 0, 0, 0.1));
|
|
186
|
+
--jini-overlay-bg-preview: var(--jini-theme-overlay-bg-preview, oklch(0% 0 0 / 0.55));
|
|
187
|
+
--jini-overlay-bg-navigation: var(--jini-theme-overlay-bg-navigation, oklch(0% 0 0 / 0.4));
|
|
188
|
+
--jini-code-string: var(--jini-theme-code-string, #4caf7a);
|
|
189
|
+
--jini-code-number: var(--jini-theme-code-number, #d1975a);
|
|
190
|
+
--jini-control-thumb-bg: var(--jini-theme-control-thumb-bg, #fff);
|
|
191
|
+
--jini-control-solid-ink: var(--jini-theme-control-solid-ink, #fff);
|
|
192
|
+
--jini-overlay-bg-lightbox: var(--jini-theme-overlay-bg-lightbox, oklch(0% 0 0 / 0.6));
|
|
193
|
+
--jini-lightbox-bg: var(--jini-theme-lightbox-bg, oklch(23% 0.005 256));
|
|
194
|
+
--jini-lightbox-ink: var(--jini-theme-lightbox-ink, oklch(97% 0 0));
|
|
195
|
+
--jini-lightbox-ink-muted: var(--jini-theme-lightbox-ink-muted, oklch(97% 0 0 / 0.58));
|
|
196
|
+
--jini-lightbox-media-bg: var(--jini-theme-lightbox-media-bg, oklch(0% 0 0 / 0.25));
|
|
197
|
+
--jini-lightbox-document-bg: var(--jini-theme-lightbox-document-bg, #fff);
|
|
198
|
+
--jini-lightbox-button-border: var(--jini-theme-lightbox-button-border, oklch(100% 0 0 / 0.16));
|
|
199
|
+
--jini-lightbox-button-bg: var(--jini-theme-lightbox-button-bg, oklch(100% 0 0 / 0.07));
|
|
200
|
+
--jini-lightbox-button-bg-hover: var(--jini-theme-lightbox-button-bg-hover, oklch(100% 0 0 / 0.15));
|
|
201
|
+
--jini-lightbox-button-border-hover: var(--jini-theme-lightbox-button-border-hover, oklch(100% 0 0 / 0.28));
|
|
202
|
+
--jini-control-media-bg: var(--jini-theme-control-media-bg, oklch(0% 0 0 / 0.55));
|
|
203
|
+
--jini-control-media-ink: var(--jini-theme-control-media-ink, oklch(100% 0 0));
|
|
204
|
+
--jini-control-media-border: var(--jini-theme-control-media-border, oklch(100% 0 0 / 0.35));
|
|
205
|
+
--jini-control-media-bg-hover: var(--jini-theme-control-media-bg-hover, oklch(0% 0 0 / 0.72));
|
|
206
|
+
--jini-overlay-bg-source: var(--jini-theme-overlay-bg-source, oklch(0% 0 0 / 0.48));
|
|
207
|
+
--jini-file-icon-folder: var(--jini-theme-file-icon-folder, oklch(72% 0.12 75));
|
|
208
|
+
--jini-file-icon-json: var(--jini-theme-file-icon-json, oklch(72% 0.13 90));
|
|
209
|
+
--jini-file-icon-markdown: var(--jini-theme-file-icon-markdown, oklch(62% 0.12 245));
|
|
210
|
+
--jini-file-icon-javascript: var(--jini-theme-file-icon-javascript, oklch(76% 0.15 95));
|
|
211
|
+
--jini-file-icon-typescript: var(--jini-theme-file-icon-typescript, oklch(60% 0.13 250));
|
|
212
|
+
--jini-file-icon-config: var(--jini-theme-file-icon-config, oklch(62% 0.14 20));
|
|
213
|
+
--jini-file-icon-html: var(--jini-theme-file-icon-html, oklch(64% 0.17 40));
|
|
214
|
+
--jini-file-icon-css: var(--jini-theme-file-icon-css, oklch(62% 0.13 280));
|
|
215
|
+
--jini-file-icon-image: var(--jini-theme-file-icon-image, oklch(64% 0.12 160));
|
|
216
|
+
--jini-file-icon-shell: var(--jini-theme-file-icon-shell, oklch(64% 0.12 145));
|
|
217
|
+
--jini-status-complete-ink-fallback: var(--jini-theme-status-complete-ink-fallback, oklch(99% 0 none));
|
|
218
|
+
--jini-control-preview-ink: var(--jini-theme-control-preview-ink, #fff);
|
|
219
|
+
--jini-control-preview-bg: var(--jini-theme-control-preview-bg, rgba(17, 17, 17, 0.42));
|
|
220
|
+
--jini-control-preview-bg-hover: var(--jini-theme-control-preview-bg-hover, rgba(17, 17, 17, 0.72));
|
|
221
|
+
--jini-control-preview-tip-bg: var(--jini-theme-control-preview-tip-bg, rgba(17, 17, 17, 0.88));
|
|
222
|
+
--jini-panel-border-muted: var(--jini-theme-panel-border-muted, #d9d9d9);
|
|
223
|
+
--jini-control-composer-border-focus: var(--jini-theme-control-composer-border-focus, oklch(70% 0.012 255));
|
|
224
|
+
--jini-overlay-bg-chat: var(--jini-theme-overlay-bg-chat, rgb(0 0 0 / 45%));
|
|
225
|
+
--jini-control-hover-neutral-bg: var(--jini-theme-control-hover-neutral-bg, rgba(127, 127, 127, 0.15));
|
|
226
|
+
--jini-control-recording-bg: var(--jini-theme-control-recording-bg, #dc2626);
|
|
227
|
+
--jini-control-recording-ink: var(--jini-theme-control-recording-ink, #ffffff);
|
|
228
|
+
--jini-control-recording-bg-hover: var(--jini-theme-control-recording-bg-hover, #b91c1c);
|
|
229
|
+
--jini-control-shadow-focus: var(--jini-theme-control-shadow-focus, 0 0 0 3px var(--jini-accent-ring));
|
|
230
|
+
--jini-overlay-tooltip-shadow: var(--jini-theme-overlay-tooltip-shadow, 0 4px 16px rgba(0, 0, 0, 0.25));
|
|
231
|
+
--jini-control-shadow-focus-subtle: var(--jini-theme-control-shadow-focus-subtle, 0 0 0 2px var(--jini-neutral-accent-soft));
|
|
232
|
+
--jini-overlay-toast-shadow: var(--jini-theme-overlay-toast-shadow, 0 8px 24px rgb(0 0 0 / 22%));
|
|
233
|
+
--jini-status-danger-shadow: var(--jini-theme-status-danger-shadow, inset 3px 0 0 var(--jini-danger));
|
|
234
|
+
--jini-control-thumb-shadow-strong: var(--jini-theme-control-thumb-shadow-strong, 0 1px 2px rgba(28, 27, 26, 0.28));
|
|
235
|
+
--jini-panel-shadow-divider: var(--jini-theme-panel-shadow-divider, inset 0 -1px 0 var(--jini-border));
|
|
236
|
+
--jini-control-shadow-focus-accent: var(--jini-theme-control-shadow-focus-accent, 0 0 0 3px color-mix(in srgb, var(--jini-accent) 18%, transparent));
|
|
237
|
+
--jini-overlay-menu-shadow-above: var(--jini-theme-overlay-menu-shadow-above, 0 -4px 14px rgba(20,28,45,0.09), 0 -1px 4px rgba(20,28,45,0.06));
|
|
238
|
+
--jini-control-shadow-selected: var(--jini-theme-control-shadow-selected, 0 0 0 1px var(--jini-accent));
|
|
239
|
+
--jini-control-shadow-swatch-selected: var(--jini-theme-control-shadow-swatch-selected, 0 0 0 2px var(--jini-bg-panel), 0 0 0 4px var(--jini-accent));
|
|
240
|
+
--jini-control-shadow-segment: var(--jini-theme-control-shadow-segment, 0 1px 2px rgb(0 0 0 / 8%));
|
|
241
|
+
--jini-panel-shadow-sticky: var(--jini-theme-panel-shadow-sticky, 0 6px 8px -6px rgb(0 0 0 / 0.14));
|
|
242
|
+
--jini-overlay-menu-shadow: var(--jini-theme-overlay-menu-shadow, 0 12px 32px rgb(0 0 0 / 12%));
|
|
243
|
+
--jini-control-shadow-segment-raised: var(--jini-theme-control-shadow-segment-raised, 0 1px 2px rgb(0 0 0 / 12%));
|
|
244
|
+
--jini-control-thumb-shadow: var(--jini-theme-control-thumb-shadow, 0 1px 2px rgb(0 0 0 / 25%));
|
|
245
|
+
--jini-panel-shadow-banner: var(--jini-theme-panel-shadow-banner, 0 2px 8px rgb(0 0 0 / 0.12));
|
|
246
|
+
--jini-status-warning-shadow: var(--jini-theme-status-warning-shadow, inset 2px 0 0 var(--jini-warning));
|
|
247
|
+
--jini-panel-shadow-flat: var(--jini-theme-panel-shadow-flat, none);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* Chat uses its own stylesheet so hosts can load it independently of the admin palette.
|
|
251
|
+
Aliases live on :root only so nested theme/scheme scopes inherit ChatPane's component-local
|
|
252
|
+
values. Moving that component wiring is a later step. Classic retains its existing scoped
|
|
253
|
+
host bridge in Tovu. */
|
|
254
|
+
:root {
|
|
255
|
+
--jini-chat-font-body: var(--jini-theme-chat-font-body, -apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei UI", "Noto Sans", Roboto, "Helvetica Neue", Arial, sans-serif);
|
|
256
|
+
--jini-chat-font-mono: var(--jini-theme-chat-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
257
|
+
--jini-chat-popover-shadow: var(--jini-theme-chat-popover-shadow, 0 18px 46px rgba(13, 12, 10, .15), 0 2px 8px rgba(13, 12, 10, .06));
|
|
258
|
+
--jini-chat-bg: var(--jini-theme-chat-bg);
|
|
259
|
+
--jini-chat-panel: var(--jini-theme-chat-panel);
|
|
260
|
+
--jini-chat-text: var(--jini-theme-chat-text);
|
|
261
|
+
--jini-chat-text-strong: var(--jini-theme-chat-text-strong);
|
|
262
|
+
--jini-chat-muted: var(--jini-theme-chat-muted);
|
|
263
|
+
--jini-chat-faint: var(--jini-theme-chat-faint);
|
|
264
|
+
--jini-chat-border: var(--jini-theme-chat-border);
|
|
265
|
+
--jini-chat-border-strong: var(--jini-theme-chat-border-strong);
|
|
266
|
+
--jini-chat-border-soft: var(--jini-theme-chat-border-soft);
|
|
267
|
+
--jini-chat-subtle: var(--jini-theme-chat-subtle);
|
|
268
|
+
--jini-chat-accent: var(--jini-theme-chat-accent);
|
|
269
|
+
--jini-chat-accent-soft: var(--jini-theme-chat-accent-soft);
|
|
270
|
+
--jini-chat-danger: var(--jini-theme-chat-danger);
|
|
271
|
+
--jini-chat-radius: var(--jini-theme-chat-radius);
|
|
272
|
+
--jini-chat-radius-lg: var(--jini-theme-chat-radius-lg);
|
|
273
|
+
--jini-chat-user-bubble: var(--jini-theme-chat-user-bubble);
|
|
274
|
+
--jini-chat-user-bubble-ink: var(--jini-theme-chat-user-bubble-ink);
|
|
275
|
+
}
|
package/dist/theme/apply.js
CHANGED
|
@@ -26,11 +26,7 @@ function acquireFont(document, href) {
|
|
|
26
26
|
leases.delete(absolute);
|
|
27
27
|
} };
|
|
28
28
|
}
|
|
29
|
-
|
|
30
|
-
export function applyAdminTheme({ theme }, { target, document }) {
|
|
31
|
-
const checked = validateAdminTheme({ theme });
|
|
32
|
-
if (!checked.valid)
|
|
33
|
-
throw new TypeError(checked.errors.join('; '));
|
|
29
|
+
function themeValues({ theme }, _options = {}) {
|
|
34
30
|
const values = {};
|
|
35
31
|
for (const scheme of ['light', 'dark'])
|
|
36
32
|
for (const key of adminThemeColorKeys) {
|
|
@@ -41,6 +37,14 @@ export function applyAdminTheme({ theme }, { target, document }) {
|
|
|
41
37
|
values['--jini-font-mono'] = theme.fonts.mono ?? defaultAdminTheme.fonts.mono;
|
|
42
38
|
values['--jini-radius'] = theme.radius ?? defaultAdminTheme.radius;
|
|
43
39
|
values['--jini-density'] = String(theme.density ?? defaultAdminTheme.density);
|
|
40
|
+
return values;
|
|
41
|
+
}
|
|
42
|
+
/** Install both palettes on the injected scope and load fonts. Cleanup restores the host. */
|
|
43
|
+
export function applyAdminTheme({ theme }, { target, document }) {
|
|
44
|
+
const checked = validateAdminTheme({ theme });
|
|
45
|
+
if (!checked.valid)
|
|
46
|
+
throw new TypeError(checked.errors.join('; '));
|
|
47
|
+
const values = themeValues({ theme });
|
|
44
48
|
const before = Object.keys(values).map((key) => ({ key, value: target.style.getPropertyValue(key), priority: target.style.getPropertyPriority(key) }));
|
|
45
49
|
const previousName = target.getAttribute('data-admin-theme');
|
|
46
50
|
const releases = [];
|
|
@@ -7,10 +7,11 @@ interface CandidateFile {
|
|
|
7
7
|
interface AutoOpenOptions {
|
|
8
8
|
readonly moduleFileNames?: ReadonlySet<string>;
|
|
9
9
|
}
|
|
10
|
-
|
|
10
|
+
type AutoOpenDecision = {
|
|
11
11
|
shouldOpen: boolean;
|
|
12
12
|
fileName: string | null;
|
|
13
13
|
};
|
|
14
|
+
export declare function decideAutoOpenAfterWrite(filePath: string, nextFiles: ReadonlyArray<CandidateFile>, options?: AutoOpenOptions): AutoOpenDecision;
|
|
14
15
|
export interface SelectAutoOpenOptions {
|
|
15
16
|
readonly preferSiteEntry?: boolean;
|
|
16
17
|
}
|
|
@@ -24,59 +24,51 @@ function basenameOf(p) {
|
|
|
24
24
|
// rather than tested around.
|
|
25
25
|
return p.split('/').pop();
|
|
26
26
|
}
|
|
27
|
+
function decline() {
|
|
28
|
+
return { shouldOpen: false, fileName: null };
|
|
29
|
+
}
|
|
30
|
+
function relPathOf(file) {
|
|
31
|
+
return file.path ?? file.name;
|
|
32
|
+
}
|
|
33
|
+
// Does the agent's `filePath` positively identify the project file at `rel`?
|
|
34
|
+
function isPathSuffixMatch({ filePath, rel }) {
|
|
35
|
+
if (!rel)
|
|
36
|
+
return false;
|
|
37
|
+
// Require segment alignment: filePath ends with "/${rel}" so that
|
|
38
|
+
// "subdir/App.jsx" matches ".../subdir/App.jsx" but not
|
|
39
|
+
// ".../notsubdir/App.jsx".
|
|
40
|
+
return filePath === rel || filePath.endsWith('/' + rel);
|
|
41
|
+
}
|
|
42
|
+
// Resolve a positive identification into an open decision, declining files
|
|
43
|
+
// that are modules of a multi-file HTML entry rather than standalone pages.
|
|
44
|
+
// More than one match means multiple project files plausibly correspond to
|
|
45
|
+
// this path — refuse rather than open the wrong one.
|
|
46
|
+
function resolveUniqueMatch({ matches, moduleFileNames }) {
|
|
47
|
+
if (matches.length !== 1)
|
|
48
|
+
return decline();
|
|
49
|
+
const fileName = matches[0].name;
|
|
50
|
+
return moduleFileNames.has(fileName) ? decline() : { shouldOpen: true, fileName };
|
|
51
|
+
}
|
|
27
52
|
export function decideAutoOpenAfterWrite(filePath, nextFiles, options = {}) {
|
|
28
53
|
const moduleFileNames = options.moduleFileNames ?? NO_MODULES;
|
|
29
|
-
// Resolve a positive identification into an open decision, declining files
|
|
30
|
-
// that are modules of a multi-file HTML entry rather than standalone pages.
|
|
31
|
-
const resolve = (fileName) => moduleFileNames.has(fileName)
|
|
32
|
-
? { shouldOpen: false, fileName: null }
|
|
33
|
-
: { shouldOpen: true, fileName };
|
|
34
54
|
if (!filePath)
|
|
35
|
-
return
|
|
55
|
+
return decline();
|
|
36
56
|
// 1) Path-suffix match against full project-relative paths.
|
|
37
|
-
const suffixMatches =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (!rel)
|
|
41
|
-
continue;
|
|
42
|
-
if (filePath === rel) {
|
|
43
|
-
suffixMatches.push(f);
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
// Require segment alignment: filePath ends with "/${rel}" so that
|
|
47
|
-
// "subdir/App.jsx" matches ".../subdir/App.jsx" but not
|
|
48
|
-
// ".../notsubdir/App.jsx".
|
|
49
|
-
if (filePath.length > rel.length && filePath.endsWith('/' + rel)) {
|
|
50
|
-
suffixMatches.push(f);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
if (suffixMatches.length === 1) {
|
|
54
|
-
return resolve(suffixMatches[0].name);
|
|
55
|
-
}
|
|
56
|
-
if (suffixMatches.length > 1) {
|
|
57
|
-
// Multiple project files plausibly correspond to this path — refuse
|
|
58
|
-
// rather than open the wrong one.
|
|
59
|
-
return { shouldOpen: false, fileName: null };
|
|
60
|
-
}
|
|
57
|
+
const suffixMatches = nextFiles.filter((f) => isPathSuffixMatch({ filePath, rel: relPathOf(f) }));
|
|
58
|
+
if (suffixMatches.length > 0)
|
|
59
|
+
return resolveUniqueMatch({ matches: suffixMatches, moduleFileNames });
|
|
61
60
|
// 2) Basename fallback only when filePath itself is just a basename.
|
|
62
61
|
// If filePath contains a slash but didn't path-suffix-match anything,
|
|
63
62
|
// it's an external edit that happens to share a basename — declining
|
|
64
63
|
// is the whole point of the guard.
|
|
65
|
-
if (filePath.includes('/'))
|
|
66
|
-
return
|
|
67
|
-
|
|
68
|
-
const basenameMatches = nextFiles.filter((f) =>
|
|
69
|
-
|
|
70
|
-
return rel ? basenameOf(rel) === filePath : false;
|
|
71
|
-
});
|
|
72
|
-
if (basenameMatches.length === 1) {
|
|
73
|
-
return resolve(basenameMatches[0].name);
|
|
74
|
-
}
|
|
75
|
-
return { shouldOpen: false, fileName: null };
|
|
64
|
+
if (filePath.includes('/'))
|
|
65
|
+
return decline();
|
|
66
|
+
// A blank rel's basename is '' and can never equal the non-empty filePath.
|
|
67
|
+
const basenameMatches = nextFiles.filter((f) => basenameOf(relPathOf(f)) === filePath);
|
|
68
|
+
return resolveUniqueMatch({ matches: basenameMatches, moduleFileNames });
|
|
76
69
|
}
|
|
77
70
|
function isHtmlPreviewFile(file) {
|
|
78
|
-
|
|
79
|
-
return file.kind === 'html' || /\.html?$/i.test(path);
|
|
71
|
+
return file.kind === 'html' || /\.html?$/i.test(relPathOf(file));
|
|
80
72
|
}
|
|
81
73
|
// Markdown documents (plan.md, report.md, DESIGN.md, …) often render inline
|
|
82
74
|
// in a viewer, so a turn that produces one should surface it just like an
|
|
@@ -84,8 +76,7 @@ function isHtmlPreviewFile(file) {
|
|
|
84
76
|
// whose backend maps both `.md` and `.txt` to the same generic text kind
|
|
85
77
|
// still opens markdown while deliberately leaving plain `.txt` alone.
|
|
86
78
|
function isMarkdownPreviewFile(file) {
|
|
87
|
-
|
|
88
|
-
return /\.(md|markdown)$/i.test(path);
|
|
79
|
+
return /\.(md|markdown)$/i.test(relPathOf(file));
|
|
89
80
|
}
|
|
90
81
|
// Auto-open priority for a turn's produced files. Higher wins. HTML is the
|
|
91
82
|
// primary visual deliverable, so when a turn writes both an HTML page and a
|
|
@@ -104,58 +95,51 @@ function autoOpenPreviewRank(file) {
|
|
|
104
95
|
// files. Depth orders competing entries so the site root wins over a locale
|
|
105
96
|
// or section subtree's own index.
|
|
106
97
|
function siteEntryDepth(file) {
|
|
107
|
-
const path = file
|
|
98
|
+
const path = relPathOf(file);
|
|
108
99
|
if (!/(^|\/)index\.html?$/i.test(path))
|
|
109
100
|
return null;
|
|
110
101
|
return path.split('/').length;
|
|
111
102
|
}
|
|
103
|
+
// Missing or non-finite mtimes count as 0 for the newest-wins tie-break.
|
|
104
|
+
function finiteMtime(file) {
|
|
105
|
+
return typeof file.mtime === 'number' && Number.isFinite(file.mtime) ? file.mtime : 0;
|
|
106
|
+
}
|
|
107
|
+
// The file with the highest `score` (null = not a candidate); equal scores
|
|
108
|
+
// break to the newest mtime, and an mtime tie goes to the later file.
|
|
109
|
+
function pickHighestScoring({ files, score }) {
|
|
110
|
+
let best = null;
|
|
111
|
+
for (const file of files) {
|
|
112
|
+
const fileScore = score(file);
|
|
113
|
+
if (fileScore === null)
|
|
114
|
+
continue;
|
|
115
|
+
const beatsBest = best === null ||
|
|
116
|
+
fileScore > best.score ||
|
|
117
|
+
(fileScore === best.score && finiteMtime(file) >= finiteMtime(best.file));
|
|
118
|
+
if (beatsBest)
|
|
119
|
+
best = { file, score: fileScore };
|
|
120
|
+
}
|
|
121
|
+
return best?.file ?? null;
|
|
122
|
+
}
|
|
123
|
+
// Every site entry is an `index.html`/`.htm`, so it is always an HTML preview
|
|
124
|
+
// file too; the shallower entry scores higher.
|
|
125
|
+
function siteEntryScore(file) {
|
|
126
|
+
const depth = siteEntryDepth(file);
|
|
127
|
+
return depth === null ? null : -depth;
|
|
128
|
+
}
|
|
129
|
+
function previewRankScore(file) {
|
|
130
|
+
const rank = autoOpenPreviewRank(file);
|
|
131
|
+
return rank === 0 ? null : rank;
|
|
132
|
+
}
|
|
112
133
|
// Pick which of a turn's produced files to auto-open in the viewer. Among
|
|
113
134
|
// previewable files, a higher-priority kind always beats a lower one; ties
|
|
114
135
|
// break to the most recently written file (newest mtime). Returns null when
|
|
115
136
|
// the turn produced nothing previewable.
|
|
116
137
|
export function selectAutoOpenProducedArtifact(producedFiles, options = {}) {
|
|
117
138
|
if (options.preferSiteEntry) {
|
|
118
|
-
|
|
119
|
-
let entryDepth = Number.POSITIVE_INFINITY;
|
|
120
|
-
for (const file of producedFiles) {
|
|
121
|
-
if (!isHtmlPreviewFile(file))
|
|
122
|
-
continue;
|
|
123
|
-
const depth = siteEntryDepth(file);
|
|
124
|
-
if (depth === null)
|
|
125
|
-
continue;
|
|
126
|
-
if (depth < entryDepth) {
|
|
127
|
-
entry = file;
|
|
128
|
-
entryDepth = depth;
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
if (depth > entryDepth || !entry)
|
|
132
|
-
continue;
|
|
133
|
-
const nextMtime = typeof file.mtime === 'number' && Number.isFinite(file.mtime) ? file.mtime : 0;
|
|
134
|
-
const entryMtime = typeof entry.mtime === 'number' && Number.isFinite(entry.mtime) ? entry.mtime : 0;
|
|
135
|
-
if (nextMtime >= entryMtime)
|
|
136
|
-
entry = file;
|
|
137
|
-
}
|
|
139
|
+
const entry = pickHighestScoring({ files: producedFiles, score: siteEntryScore });
|
|
138
140
|
if (entry)
|
|
139
141
|
return entry.name;
|
|
140
142
|
}
|
|
141
|
-
|
|
142
|
-
let selectedRank = 0;
|
|
143
|
-
for (const file of producedFiles) {
|
|
144
|
-
const rank = autoOpenPreviewRank(file);
|
|
145
|
-
if (rank === 0)
|
|
146
|
-
continue;
|
|
147
|
-
if (!selected || rank > selectedRank) {
|
|
148
|
-
selected = file;
|
|
149
|
-
selectedRank = rank;
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
if (rank < selectedRank)
|
|
153
|
-
continue;
|
|
154
|
-
const nextMtime = typeof file.mtime === 'number' && Number.isFinite(file.mtime) ? file.mtime : 0;
|
|
155
|
-
const selectedMtime = typeof selected.mtime === 'number' && Number.isFinite(selected.mtime) ? selected.mtime : 0;
|
|
156
|
-
if (nextMtime >= selectedMtime)
|
|
157
|
-
selected = file;
|
|
158
|
-
}
|
|
159
|
-
return selected?.name ?? null;
|
|
143
|
+
return pickHighestScoring({ files: producedFiles, score: previewRankScore })?.name ?? null;
|
|
160
144
|
}
|
|
161
145
|
//# sourceMappingURL=auto-open-file.js.map
|
|
@@ -56,72 +56,50 @@ function hasVerticalProgression(offsets) {
|
|
|
56
56
|
const first = offsets[0];
|
|
57
57
|
return offsets.some((offset, index) => index > 0 && offset > first + 0.5);
|
|
58
58
|
}
|
|
59
|
-
|
|
60
|
-
* 1-based source start line of every top-level markdown block, in document
|
|
61
|
-
* order. Nested blocks (list items, blockquote contents) collapse into their
|
|
62
|
-
* top-level container so the result lines up 1:1 with the preview article's
|
|
63
|
-
* direct element children.
|
|
64
|
-
*/
|
|
65
|
-
export function extractMarkdownBlockLines(markdown) {
|
|
66
|
-
if (!markdown)
|
|
67
|
-
return [];
|
|
68
|
-
let events;
|
|
59
|
+
function parseMarkdownEvents({ markdown }) {
|
|
69
60
|
try {
|
|
70
|
-
|
|
61
|
+
return postprocess(parse({ extensions: [gfm()] })
|
|
71
62
|
.document()
|
|
72
63
|
.write(preprocess()(markdown, undefined, true)));
|
|
73
64
|
}
|
|
74
65
|
catch {
|
|
75
|
-
return
|
|
66
|
+
return null;
|
|
76
67
|
}
|
|
68
|
+
}
|
|
69
|
+
// Nesting depth after a container token's enter/exit event.
|
|
70
|
+
function nextContainerDepth({ phase, depth }) {
|
|
71
|
+
return phase === 'enter' ? depth + 1 : Math.max(0, depth - 1);
|
|
72
|
+
}
|
|
73
|
+
function collectTopLevelBlockLines({ events }) {
|
|
77
74
|
const lines = [];
|
|
78
75
|
let depth = 0;
|
|
79
|
-
for (const
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
if (CONTAINER_TOKENS.has(type)) {
|
|
84
|
-
if (phase === 'enter') {
|
|
85
|
-
if (depth === 0)
|
|
86
|
-
lines.push(token.start.line);
|
|
87
|
-
depth += 1;
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
depth = Math.max(0, depth - 1);
|
|
91
|
-
}
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
if (phase === 'enter' && depth === 0 && FLOW_BLOCK_TOKENS.has(type)) {
|
|
76
|
+
for (const [phase, token] of events) {
|
|
77
|
+
const isContainer = CONTAINER_TOKENS.has(token.type);
|
|
78
|
+
const isTopLevelStart = phase === 'enter' && depth === 0;
|
|
79
|
+
if (isTopLevelStart && (isContainer || FLOW_BLOCK_TOKENS.has(token.type)))
|
|
95
80
|
lines.push(token.start.line);
|
|
96
|
-
|
|
81
|
+
if (isContainer)
|
|
82
|
+
depth = nextContainerDepth({ phase, depth });
|
|
97
83
|
}
|
|
98
84
|
return lines;
|
|
99
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* 1-based source start line of every top-level markdown block, in document
|
|
88
|
+
* order. Nested blocks (list items, blockquote contents) collapse into their
|
|
89
|
+
* top-level container so the result lines up 1:1 with the preview article's
|
|
90
|
+
* direct element children.
|
|
91
|
+
*/
|
|
92
|
+
export function extractMarkdownBlockLines(markdown) {
|
|
93
|
+
if (!markdown)
|
|
94
|
+
return [];
|
|
95
|
+
const events = parseMarkdownEvents({ markdown });
|
|
96
|
+
return events ? collectTopLevelBlockLines({ events }) : [];
|
|
97
|
+
}
|
|
100
98
|
function escapeMirrorText(value) {
|
|
101
99
|
return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
102
100
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
* start line, accounting for soft-wrapping. Measured with a hidden mirror that
|
|
106
|
-
* mimics the textarea's wrapping so the offsets stay accurate even when long
|
|
107
|
-
* lines wrap. Returns `null` when measurement is not possible.
|
|
108
|
-
*/
|
|
109
|
-
export function measureEditorBlockOffsets(textarea, blockLines, text) {
|
|
110
|
-
// The `typeof document === 'undefined'` half of this guard is real,
|
|
111
|
-
// intentional SSR-safety API contract (documented in this function's own
|
|
112
|
-
// JSDoc), but is empirically unreachable through this repo's actual call
|
|
113
|
-
// graph today: its only caller (`useMarkdownScrollSync`) only ever
|
|
114
|
-
// invokes it with an already browser-mounted `HTMLTextAreaElement` ref,
|
|
115
|
-
// which cannot exist without `document` already existing. A real test
|
|
116
|
-
// would require either reintroducing this package's own documented
|
|
117
|
-
// `@vitest/coverage-v8` jsdom/node branch-merge bug (see
|
|
118
|
-
// `features/html-viewer/dependencies`'s precedent in
|
|
119
|
-
// packages/ui/archived provenance ledger) or hand-faking this function's full
|
|
120
|
-
// `innerHTML`/`querySelectorAll`/`getComputedStyle` surface under plain
|
|
121
|
-
// Node — disproportionate to a defensive guard with no reachable path.
|
|
122
|
-
// See packages/ui/archived provenance ledger's 2026-07-22 dated entry.
|
|
123
|
-
if (blockLines.length === 0 || typeof document === 'undefined')
|
|
124
|
-
return null;
|
|
101
|
+
// A hidden off-screen div styled to wrap text exactly like `textarea`.
|
|
102
|
+
function createMirror({ textarea }) {
|
|
125
103
|
const computed = window.getComputedStyle(textarea);
|
|
126
104
|
const mirror = document.createElement('div');
|
|
127
105
|
const style = mirror.style;
|
|
@@ -142,23 +120,30 @@ export function measureEditorBlockOffsets(textarea, blockLines, text) {
|
|
|
142
120
|
style.width = `${textarea.clientWidth}px`;
|
|
143
121
|
if (style.whiteSpace === 'normal' || !style.whiteSpace)
|
|
144
122
|
style.whiteSpace = 'pre-wrap';
|
|
145
|
-
|
|
123
|
+
return mirror;
|
|
124
|
+
}
|
|
125
|
+
// Block indices grouped by the (clamped, 0-based) source line they start on.
|
|
126
|
+
function groupBlocksByLine({ blockLines, lineCount }) {
|
|
146
127
|
const markersByLine = new Map();
|
|
147
128
|
blockLines.forEach((line, blockIndex) => {
|
|
148
|
-
const lineIndex = Math.max(0, Math.min(
|
|
129
|
+
const lineIndex = Math.max(0, Math.min(lineCount - 1, line - 1));
|
|
149
130
|
const existing = markersByLine.get(lineIndex);
|
|
150
131
|
if (existing)
|
|
151
132
|
existing.push(blockIndex);
|
|
152
133
|
else
|
|
153
134
|
markersByLine.set(lineIndex, [blockIndex]);
|
|
154
135
|
});
|
|
136
|
+
return markersByLine;
|
|
137
|
+
}
|
|
138
|
+
// The escaped `text` with an empty `data-md-block` marker span in front of
|
|
139
|
+
// every block's start line.
|
|
140
|
+
function buildMirrorMarkup({ blockLines, text }) {
|
|
141
|
+
const lines = text.split('\n');
|
|
142
|
+
const markersByLine = groupBlocksByLine({ blockLines, lineCount: lines.length });
|
|
155
143
|
let buffer = '';
|
|
156
144
|
for (let i = 0; i < lines.length; i += 1) {
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
for (const blockIndex of markers)
|
|
160
|
-
buffer += `<span data-md-block="${blockIndex}"></span>`;
|
|
161
|
-
}
|
|
145
|
+
for (const blockIndex of markersByLine.get(i) ?? [])
|
|
146
|
+
buffer += `<span data-md-block="${blockIndex}"></span>`;
|
|
162
147
|
// `text.split('\n')` always yields a dense array (never a hole), so
|
|
163
148
|
// `lines[i]` for `i` in `[0, lines.length)` is always a defined string
|
|
164
149
|
// (possibly `''`) — the `?? ''` fallback this used to have was dead
|
|
@@ -168,25 +153,51 @@ export function measureEditorBlockOffsets(textarea, blockLines, text) {
|
|
|
168
153
|
if (i < lines.length - 1)
|
|
169
154
|
buffer += '\n';
|
|
170
155
|
}
|
|
171
|
-
|
|
156
|
+
return buffer;
|
|
157
|
+
}
|
|
158
|
+
// Lay the mirror out in the document just long enough to read each marker's
|
|
159
|
+
// offsetTop. Every marker span comes from `buildMirrorMarkup` (the text itself
|
|
160
|
+
// is escaped, so it cannot inject one), so each index is a valid block index.
|
|
161
|
+
function readMarkerOffsets({ mirror, blockCount }) {
|
|
172
162
|
document.body.appendChild(mirror);
|
|
173
|
-
const offsets = new Array(
|
|
163
|
+
const offsets = new Array(blockCount).fill(0);
|
|
174
164
|
try {
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
const blockIndex = Number(marker.getAttribute('data-md-block'));
|
|
178
|
-
if (Number.isInteger(blockIndex) && blockIndex >= 0 && blockIndex < offsets.length) {
|
|
179
|
-
offsets[blockIndex] = marker.offsetTop;
|
|
180
|
-
}
|
|
165
|
+
for (const marker of Array.from(mirror.querySelectorAll('span[data-md-block]'))) {
|
|
166
|
+
offsets[Number(marker.getAttribute('data-md-block'))] = marker.offsetTop;
|
|
181
167
|
}
|
|
182
168
|
}
|
|
183
169
|
finally {
|
|
184
170
|
document.body.removeChild(mirror);
|
|
185
171
|
}
|
|
186
|
-
if (!hasVerticalProgression(offsets))
|
|
187
|
-
return null;
|
|
188
172
|
return offsets;
|
|
189
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Pixel offset (within the textarea's scrollable content) of each block's
|
|
176
|
+
* start line, accounting for soft-wrapping. Measured with a hidden mirror that
|
|
177
|
+
* mimics the textarea's wrapping so the offsets stay accurate even when long
|
|
178
|
+
* lines wrap. Returns `null` when measurement is not possible.
|
|
179
|
+
*/
|
|
180
|
+
export function measureEditorBlockOffsets(textarea, blockLines, text) {
|
|
181
|
+
// The `typeof document === 'undefined'` half of this guard is real,
|
|
182
|
+
// intentional SSR-safety API contract (documented in this function's own
|
|
183
|
+
// JSDoc), but is empirically unreachable through this repo's actual call
|
|
184
|
+
// graph today: its only caller (`useMarkdownScrollSync`) only ever
|
|
185
|
+
// invokes it with an already browser-mounted `HTMLTextAreaElement` ref,
|
|
186
|
+
// which cannot exist without `document` already existing. A real test
|
|
187
|
+
// would require either reintroducing this package's own documented
|
|
188
|
+
// `@vitest/coverage-v8` jsdom/node branch-merge bug (see
|
|
189
|
+
// `features/html-viewer/dependencies`'s precedent in
|
|
190
|
+
// packages/ui/archived provenance ledger) or hand-faking this function's full
|
|
191
|
+
// `innerHTML`/`querySelectorAll`/`getComputedStyle` surface under plain
|
|
192
|
+
// Node — disproportionate to a defensive guard with no reachable path.
|
|
193
|
+
// See packages/ui/archived provenance ledger's 2026-07-22 dated entry.
|
|
194
|
+
if (blockLines.length === 0 || typeof document === 'undefined')
|
|
195
|
+
return null;
|
|
196
|
+
const mirror = createMirror({ textarea });
|
|
197
|
+
mirror.innerHTML = buildMirrorMarkup({ blockLines, text });
|
|
198
|
+
const offsets = readMarkerOffsets({ mirror, blockCount: blockLines.length });
|
|
199
|
+
return hasVerticalProgression(offsets) ? offsets : null;
|
|
200
|
+
}
|
|
190
201
|
/**
|
|
191
202
|
* Pixel offset (within the preview pane's scrollable content) of each rendered
|
|
192
203
|
* top-level block element. Returns `null` when the rendered article's direct
|
|
@@ -231,6 +242,25 @@ export function buildScrollAnchors(blockOffsets, scrollHeight) {
|
|
|
231
242
|
}
|
|
232
243
|
return anchors;
|
|
233
244
|
}
|
|
245
|
+
// Anchor value at `index`, reading an array hole as 0.
|
|
246
|
+
function anchorAt({ anchors, index }) {
|
|
247
|
+
return anchors[index] ?? 0;
|
|
248
|
+
}
|
|
249
|
+
// Binary-search the lower index of the segment bracketing `value`, given
|
|
250
|
+
// `source[0] <= value < source[count - 1]`; the segment's upper index is
|
|
251
|
+
// always this plus one.
|
|
252
|
+
function findBracketLow({ value, source, count }) {
|
|
253
|
+
let low = 0;
|
|
254
|
+
let high = count - 1;
|
|
255
|
+
while (high - low > 1) {
|
|
256
|
+
const mid = (low + high) >> 1;
|
|
257
|
+
if (anchorAt({ anchors: source, index: mid }) <= value)
|
|
258
|
+
low = mid;
|
|
259
|
+
else
|
|
260
|
+
high = mid;
|
|
261
|
+
}
|
|
262
|
+
return low;
|
|
263
|
+
}
|
|
234
264
|
/**
|
|
235
265
|
* Map a scroll position from the source pane to the target pane using paired
|
|
236
266
|
* anchor arrays (same length, both monotonic). Linear interpolation within the
|
|
@@ -240,25 +270,16 @@ export function mapScrollPosition(value, source, target) {
|
|
|
240
270
|
const count = Math.min(source.length, target.length);
|
|
241
271
|
if (count === 0)
|
|
242
272
|
return value;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if ((source[mid] ?? 0) <= value)
|
|
254
|
-
low = mid;
|
|
255
|
-
else
|
|
256
|
-
high = mid;
|
|
257
|
-
}
|
|
258
|
-
const sourceLow = source[low] ?? 0;
|
|
259
|
-
const sourceHigh = source[high] ?? 0;
|
|
260
|
-
const targetLow = target[low] ?? 0;
|
|
261
|
-
const targetHigh = target[high] ?? 0;
|
|
273
|
+
const last = count - 1;
|
|
274
|
+
if (count === 1 || value <= anchorAt({ anchors: source, index: 0 }))
|
|
275
|
+
return anchorAt({ anchors: target, index: 0 });
|
|
276
|
+
if (value >= anchorAt({ anchors: source, index: last }))
|
|
277
|
+
return anchorAt({ anchors: target, index: last });
|
|
278
|
+
const low = findBracketLow({ value, source, count });
|
|
279
|
+
const sourceLow = anchorAt({ anchors: source, index: low });
|
|
280
|
+
const sourceHigh = anchorAt({ anchors: source, index: low + 1 });
|
|
281
|
+
const targetLow = anchorAt({ anchors: target, index: low });
|
|
282
|
+
const targetHigh = anchorAt({ anchors: target, index: low + 1 });
|
|
262
283
|
// `span` is provably > 0 here, for *any* input array (not just a
|
|
263
284
|
// monotonic one): the early clamp checks above guarantee
|
|
264
285
|
// `source[0] <= value < source[count - 1]` before the search loop ever
|