@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
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useT } from '../../../i18n/index.js';
|
|
3
|
+
import { EMPTY_TOOLBAR_ACTIONS } from '../../constants.js';
|
|
4
|
+
import { basenameForRename, canCopyLocalPath, countFilesUnderDir, resolveKindConfig } from '../../rules.js';
|
|
5
|
+
import { AssetTreeBreadcrumbs } from './AssetTreeBreadcrumbs.js';
|
|
6
|
+
import { AssetTreeEmptyState } from './AssetTreeEmptyState.js';
|
|
7
|
+
import { AssetTreeFileRow } from './AssetTreeFileRow.js';
|
|
8
|
+
import { AssetTreeFolderRow } from './AssetTreeFolderRow.js';
|
|
9
|
+
import { AssetTreeRowMenu } from './AssetTreeRowMenu.js';
|
|
10
|
+
import { AssetTreeSelectionBar } from './AssetTreeSelectionBar.js';
|
|
11
|
+
import { AssetTreeToolbar } from './AssetTreeToolbar.js';
|
|
12
|
+
import { AssetTreeUploadErrorBanner } from './AssetTreeUploadErrorBanner.js';
|
|
13
|
+
import { FilePreviewPane } from './FilePreviewPane.js';
|
|
14
|
+
/** Breadcrumbs (root label defaults to `t('Files')`) plus the host toolbar. */
|
|
15
|
+
export function AssetTreeBrowserTopbar({ currentDir, rootLabel, onNavigate, toolbarActions = EMPTY_TOOLBAR_ACTIONS, }) {
|
|
16
|
+
const t = useT();
|
|
17
|
+
return (_jsxs("div", { className: "asset-tree-topbar", children: [_jsx("div", { className: "asset-tree-topbar-left", children: _jsx(AssetTreeBreadcrumbs, { currentDir: currentDir, rootLabel: rootLabel ?? t('Files'), onNavigate: onNavigate }) }), _jsx("div", { className: "asset-tree-topbar-right", children: _jsx(AssetTreeToolbar, { actions: toolbarActions }) })] }));
|
|
18
|
+
}
|
|
19
|
+
/** The drop-target listing area: upload-error banner, selection bar, folders + kind sections (or the empty state), footer. */
|
|
20
|
+
export function AssetTreeBrowserBody(props) {
|
|
21
|
+
const { controller, downloadFiles, footer } = props;
|
|
22
|
+
const { dragUpload, preview } = controller;
|
|
23
|
+
return (_jsxs("div", { className: "asset-tree-body", "data-testid": "asset-tree-body", onDragEnter: dragUpload.onDragEnter, onDragOver: dragUpload.onDragOver, onDragLeave: dragUpload.onDragLeave, onDrop: dragUpload.onDrop, children: [dragUpload.dropReadError && !preview.previewPath ? (_jsx(AssetTreeUploadErrorBanner, { message: dragUpload.dropReadError, onDismiss: dragUpload.clearDropReadError })) : null, _jsx(AssetTreeBrowserSelectionBar, { controller: controller, downloadFiles: downloadFiles }), _jsx(AssetTreeBrowserListing, { ...props }), footer] }));
|
|
24
|
+
}
|
|
25
|
+
function AssetTreeBrowserSelectionBar({ controller, downloadFiles, }) {
|
|
26
|
+
const { selection, batchActions } = controller;
|
|
27
|
+
if (selection.selected.size === 0)
|
|
28
|
+
return null;
|
|
29
|
+
return (_jsx(AssetTreeSelectionBar, { count: selection.selected.size, onClear: selection.clearSelection, onDelete: () => void batchActions.deleteSelected(), deleting: batchActions.deleting, onDownload: downloadFiles ? () => void batchActions.downloadSelected() : undefined, downloading: batchActions.downloading, downloadError: batchActions.downloadError }));
|
|
30
|
+
}
|
|
31
|
+
function AssetTreeBrowserListing({ controller, files, folders, selectors, kindConfig, emptyStateActions = EMPTY_TOOLBAR_ACTIONS, }) {
|
|
32
|
+
const { navigation } = controller;
|
|
33
|
+
const isEmpty = files.length === 0 && (folders?.length ?? 0) === 0;
|
|
34
|
+
if (isEmpty)
|
|
35
|
+
return _jsx(AssetTreeEmptyState, { actions: emptyStateActions });
|
|
36
|
+
return (_jsxs(_Fragment, { children: [_jsx(AssetTreeFoldersSection, { files: files, dirs: navigation.dirsAtCurrentDir, currentDir: navigation.currentDir, onNavigate: navigation.setCurrentDir }, "folders"), navigation.sections.map((section) => (_jsx(AssetTreeKindSection, { controller: controller, section: section, selectors: selectors, kindConfig: kindConfig }, `kind:${section.kind}`)))] }));
|
|
37
|
+
}
|
|
38
|
+
function AssetTreeFoldersSection({ files, dirs, currentDir, onNavigate, }) {
|
|
39
|
+
const t = useT();
|
|
40
|
+
if (dirs.length === 0)
|
|
41
|
+
return null;
|
|
42
|
+
return (_jsxs("div", { className: "asset-tree-section", children: [_jsxs("div", { className: "asset-tree-section-label", children: [t('Folders'), _jsx("span", { className: "asset-tree-section-count", children: dirs.length })] }), dirs.map((dirName) => {
|
|
43
|
+
const fullPath = currentDir === '' ? dirName : `${currentDir}/${dirName}`;
|
|
44
|
+
return (_jsx(AssetTreeFolderRow, { name: dirName, path: fullPath, fileCount: countFilesUnderDir(files, fullPath), onNavigate: onNavigate }, fullPath));
|
|
45
|
+
})] }));
|
|
46
|
+
}
|
|
47
|
+
function AssetTreeKindSection({ controller, section, selectors, kindConfig, }) {
|
|
48
|
+
const t = useT();
|
|
49
|
+
const { navigation, preview, selection, rename, rowMenu, openPath } = controller;
|
|
50
|
+
const config = resolveKindConfig(section.kind, kindConfig);
|
|
51
|
+
return (_jsxs("div", { className: "asset-tree-section", children: [_jsxs("div", { className: "asset-tree-section-label", children: [t(config.label), _jsx("span", { className: "asset-tree-section-count", children: section.files.length })] }), section.files.map((f) => (_jsx(AssetTreeFileRow, { path: f.path, displayName: basenameForRename(f.path, navigation.currentDir), active: preview.previewPath === f.path, selected: selection.selected.has(f.path), kindLabel: t(config.label), kindGlyph: config.glyph, size: selectors.getSize(f), modifiedAt: selectors.getModifiedAt(f), renaming: rename.renaming?.path === f.path ? rename.renaming : null, onSelectPreview: preview.setPreviewPath, onOpen: openPath, onToggleSelect: selection.toggleSelect, onOpenMenu: rowMenu.openMenuFor, onRenameDraftChange: rename.updateDraft, onCommitRename: () => void rename.commitRename(), onCancelRename: rename.cancelRename }, f.path)))] }));
|
|
52
|
+
}
|
|
53
|
+
/** `FilePreviewPane` for the previewed `file`, with its kind display config resolved. */
|
|
54
|
+
export function AssetTreeBrowserPreview({ file, selectors, kindConfig, onOpenFile, onClose, getFileUrl, renderPreviewThumbnail, thumbnailIsInteractive, }) {
|
|
55
|
+
const t = useT();
|
|
56
|
+
const previewKindConfig = resolveKindConfig(selectors.getKind(file), kindConfig);
|
|
57
|
+
return (_jsx(FilePreviewPane, { file: file, path: file.path, kindLabel: t(previewKindConfig.label), kindGlyph: previewKindConfig.glyph, size: selectors.getSize(file), modifiedAt: selectors.getModifiedAt(file), onOpen: () => onOpenFile(file), onClose: onClose, downloadHref: getFileUrl?.(file), renderThumbnail: renderPreviewThumbnail, thumbnailIsInteractive: thumbnailIsInteractive }));
|
|
58
|
+
}
|
|
59
|
+
/** The open row context menu for `menuPos.path`, with every action wired. */
|
|
60
|
+
export function AssetTreeBrowserRowMenu({ controller, menuPos, selectors, getFileUrl, onDeleteFile, }) {
|
|
61
|
+
const { navigation, rowMenu, rename, copyLocalPath, findFile, openPath } = controller;
|
|
62
|
+
const { path } = menuPos;
|
|
63
|
+
const menuFile = findFile(path) ?? null;
|
|
64
|
+
return (_jsx(AssetTreeRowMenu, { path: path, displayName: basenameForRename(path, navigation.currentDir), top: menuPos.top, left: menuPos.left, containerRef: rowMenu.containerRef, canCopyLocalPath: !!menuFile && canCopyLocalPath(menuFile, selectors), copied: copyLocalPath.copiedPath === path, download: getFileUrl && menuFile ? { href: getFileUrl(menuFile), onClick: rowMenu.closeMenu } : undefined, onOpen: () => {
|
|
65
|
+
rowMenu.closeMenu();
|
|
66
|
+
openPath(path);
|
|
67
|
+
}, onRename: () => {
|
|
68
|
+
rowMenu.closeMenu();
|
|
69
|
+
rename.startRename(path);
|
|
70
|
+
},
|
|
71
|
+
// Deliberately does NOT close the menu (unlike every other action
|
|
72
|
+
// here) — the origin `DesignFilesPanel`'s equivalent handler did
|
|
73
|
+
// call `setMenuPos(null)` before copying, which meant its
|
|
74
|
+
// "Copied" confirmation label could never actually become visible
|
|
75
|
+
// to a user (it only ever renders inside the now-closed popover).
|
|
76
|
+
// Since the whole point of `copiedPath`/the transient-confirmation
|
|
77
|
+
// UI is to be seen, this keeps the menu open so it can be; the
|
|
78
|
+
// existing outside-dismiss/Escape handling still closes it
|
|
79
|
+
// normally afterward.
|
|
80
|
+
onCopyLocalPath: () => {
|
|
81
|
+
// `canCopyLocalPath` (passed below) already gates the button's
|
|
82
|
+
// `disabled` state on `!!menuFile`, so a click can only reach
|
|
83
|
+
// this handler when `menuFile` is non-null — asserting that
|
|
84
|
+
// instead of re-guarding it here avoids an unreachable `: null`
|
|
85
|
+
// ternary arm (a real click can never fire on a disabled
|
|
86
|
+
// button, so that arm was dead code, not defensive coding).
|
|
87
|
+
const localPath = selectors.getLocalPath?.(menuFile);
|
|
88
|
+
if (localPath)
|
|
89
|
+
void copyLocalPath.copyLocalPath(path, localPath);
|
|
90
|
+
}, onDelete: () => {
|
|
91
|
+
rowMenu.closeMenu();
|
|
92
|
+
onDeleteFile(path);
|
|
93
|
+
} }));
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=AssetTreeBrowserParts.js.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { AssetTreeDependencies } from '../../ports.js';
|
|
2
|
+
import type { AssetTreeFileItem, AssetTreeFolderItem, AssetTreeNavState, AssetTreeSelectors } from '../../types.js';
|
|
3
|
+
import { type UseAssetTreeBatchActionsResult } from '../hooks/useAssetTreeBatchActions.js';
|
|
4
|
+
import { type UseAssetTreeCopyLocalPathResult } from '../hooks/useAssetTreeCopyLocalPath.js';
|
|
5
|
+
import { type UseAssetTreeDragUploadResult } from '../hooks/useAssetTreeDragUpload.js';
|
|
6
|
+
import { useAssetTreeNavigation, type UseAssetTreeNavigationResult } from '../hooks/useAssetTreeNavigation.js';
|
|
7
|
+
import { useAssetTreePreview, type UseAssetTreePreviewResult } from '../hooks/useAssetTreePreview.js';
|
|
8
|
+
import { useAssetTreeRename, type UseAssetTreeRenameResult } from '../hooks/useAssetTreeRename.js';
|
|
9
|
+
import { type UseAssetTreeRowMenuResult } from '../hooks/useAssetTreeRowMenu.js';
|
|
10
|
+
import { useAssetTreeSelection, type UseAssetTreeSelectionResult } from '../hooks/useAssetTreeSelection.js';
|
|
11
|
+
/** The `AssetTreeBrowser` props every controller run needs. */
|
|
12
|
+
export interface AssetTreeBrowserControllerInput<TFile extends AssetTreeFileItem> {
|
|
13
|
+
files: TFile[];
|
|
14
|
+
selectors: AssetTreeSelectors<TFile>;
|
|
15
|
+
onOpenFile: (file: TFile) => void;
|
|
16
|
+
onRenameFile: (path: string, nextPath: string) => Promise<TFile | null> | TFile | null;
|
|
17
|
+
onDeleteFiles: (paths: string[]) => Promise<void> | void;
|
|
18
|
+
onUploadFiles: (files: File[]) => void;
|
|
19
|
+
}
|
|
20
|
+
/** The optional `AssetTreeBrowser` props the controller consumes, with the same defaults the component documents. */
|
|
21
|
+
export interface AssetTreeBrowserControllerOptions<TFile extends AssetTreeFileItem> {
|
|
22
|
+
folders?: AssetTreeFolderItem[];
|
|
23
|
+
sectionOrder?: readonly string[];
|
|
24
|
+
dependencies?: AssetTreeDependencies;
|
|
25
|
+
navState?: AssetTreeNavState;
|
|
26
|
+
onNavStateChange?: (state: AssetTreeNavState) => void;
|
|
27
|
+
onSelectionChange?: (paths: Set<string>) => void;
|
|
28
|
+
downloadFiles?: (paths: string[]) => Promise<{
|
|
29
|
+
blob: Blob;
|
|
30
|
+
filename: string;
|
|
31
|
+
}>;
|
|
32
|
+
selectInitialPreviewFile?: (files: TFile[]) => TFile | null;
|
|
33
|
+
useAssetTreeNavigation?: typeof useAssetTreeNavigation;
|
|
34
|
+
useAssetTreePreview?: typeof useAssetTreePreview;
|
|
35
|
+
useAssetTreeRename?: typeof useAssetTreeRename;
|
|
36
|
+
useAssetTreeSelection?: typeof useAssetTreeSelection;
|
|
37
|
+
}
|
|
38
|
+
export interface AssetTreeBrowserController<TFile> {
|
|
39
|
+
navigation: UseAssetTreeNavigationResult<TFile>;
|
|
40
|
+
preview: UseAssetTreePreviewResult<TFile>;
|
|
41
|
+
rename: UseAssetTreeRenameResult;
|
|
42
|
+
selection: UseAssetTreeSelectionResult;
|
|
43
|
+
rowMenu: UseAssetTreeRowMenuResult;
|
|
44
|
+
dragUpload: UseAssetTreeDragUploadResult;
|
|
45
|
+
batchActions: UseAssetTreeBatchActionsResult;
|
|
46
|
+
copyLocalPath: UseAssetTreeCopyLocalPathResult;
|
|
47
|
+
findFile: (path: string) => TFile | undefined;
|
|
48
|
+
/** Resolves `path` against `files` and opens it; a path no longer in `files` is a no-op. */
|
|
49
|
+
openPath: (path: string) => void;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Every hook `AssetTreeBrowser` composes, wired together in the one order
|
|
53
|
+
* their cross-references need, so the component itself is pure rendering.
|
|
54
|
+
*/
|
|
55
|
+
export declare function useAssetTreeBrowserController<TFile extends AssetTreeFileItem>(input: AssetTreeBrowserControllerInput<TFile>, { folders, sectionOrder, dependencies, navState, onNavStateChange, onSelectionChange, downloadFiles, selectInitialPreviewFile, useAssetTreeNavigation: useAssetTreeNavigationHook, useAssetTreePreview: useAssetTreePreviewHook, useAssetTreeRename: useAssetTreeRenameHook, useAssetTreeSelection: useAssetTreeSelectionHook, }: AssetTreeBrowserControllerOptions<TFile>): AssetTreeBrowserController<TFile>;
|
|
56
|
+
//# sourceMappingURL=useAssetTreeBrowserController.d.ts.map
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { useEffect, useMemo } from 'react';
|
|
2
|
+
import { DEFAULT_SECTION_ORDER } from '../../constants.js';
|
|
3
|
+
import { createBrowserAssetTreeDependencies } from '../../dependencies.js';
|
|
4
|
+
import { useAssetTreeBatchActions } from '../hooks/useAssetTreeBatchActions.js';
|
|
5
|
+
import { useAssetTreeClipboardPasteUpload } from '../hooks/useAssetTreeClipboardPasteUpload.js';
|
|
6
|
+
import { useAssetTreeCopyLocalPath } from '../hooks/useAssetTreeCopyLocalPath.js';
|
|
7
|
+
import { useAssetTreeDragUpload } from '../hooks/useAssetTreeDragUpload.js';
|
|
8
|
+
import { useAssetTreeNavigation } from '../hooks/useAssetTreeNavigation.js';
|
|
9
|
+
import { useAssetTreePreview } from '../hooks/useAssetTreePreview.js';
|
|
10
|
+
import { useAssetTreeRename } from '../hooks/useAssetTreeRename.js';
|
|
11
|
+
import { useAssetTreeRowMenu } from '../hooks/useAssetTreeRowMenu.js';
|
|
12
|
+
import { useAssetTreeSelection } from '../hooks/useAssetTreeSelection.js';
|
|
13
|
+
/**
|
|
14
|
+
* Every hook `AssetTreeBrowser` composes, wired together in the one order
|
|
15
|
+
* their cross-references need, so the component itself is pure rendering.
|
|
16
|
+
*/
|
|
17
|
+
export function useAssetTreeBrowserController(input, { folders, sectionOrder = DEFAULT_SECTION_ORDER, dependencies, navState, onNavStateChange, onSelectionChange, downloadFiles, selectInitialPreviewFile, useAssetTreeNavigation: useAssetTreeNavigationHook = useAssetTreeNavigation, useAssetTreePreview: useAssetTreePreviewHook = useAssetTreePreview, useAssetTreeRename: useAssetTreeRenameHook = useAssetTreeRename, useAssetTreeSelection: useAssetTreeSelectionHook = useAssetTreeSelection, }) {
|
|
18
|
+
const { files, selectors, onOpenFile, onRenameFile, onDeleteFiles, onUploadFiles } = input;
|
|
19
|
+
// KNOWN FOLLOW-UP (flagged by the 2026-07-22 verification pass, not yet
|
|
20
|
+
// fixed): this has the same useWiredX gap ConnectorsBrowser had — `deps`
|
|
21
|
+
// resolution is still inline instead of behind a `useWiredAssetTreeBrowser`
|
|
22
|
+
// wrapper. Deliberately not fixed here: unlike ConnectorsBrowser's `deps`
|
|
23
|
+
// (fed to exactly the 3 already-overridable sub-hooks), this `deps` also
|
|
24
|
+
// feeds `useAssetTreeRowMenu`, `useAssetTreeClipboardPasteUpload`, and
|
|
25
|
+
// `useAssetTreeCopyLocalPath` below — none of which currently have their
|
|
26
|
+
// own override props — so collapsing it into one wired hook is a real
|
|
27
|
+
// design decision (what belongs in the wired hook vs. stays overridable)
|
|
28
|
+
// rather than a mechanical port of the ConnectorsBrowser pattern. Lower
|
|
29
|
+
// severity than that one too: these are DOM/clipboard adapters, not
|
|
30
|
+
// remote data fetching.
|
|
31
|
+
const deps = useMemo(() => dependencies ?? createBrowserAssetTreeDependencies(), [dependencies]);
|
|
32
|
+
const navigation = useAssetTreeNavigationHook({
|
|
33
|
+
files,
|
|
34
|
+
folders,
|
|
35
|
+
sectionOrder,
|
|
36
|
+
getKind: selectors.getKind,
|
|
37
|
+
getModifiedAt: selectors.getModifiedAt,
|
|
38
|
+
navState,
|
|
39
|
+
onNavStateChange,
|
|
40
|
+
});
|
|
41
|
+
const preview = useAssetTreePreviewHook(files, selectInitialPreviewFile);
|
|
42
|
+
// Declared before `useAssetTreeSelection` so its in-flight `renaming.path`
|
|
43
|
+
// (if any) can be threaded into selection's prune logic as
|
|
44
|
+
// `pendingRenamePath` — see that hook's doc comment for the race this
|
|
45
|
+
// closes. `onRenamed` referencing `selection` here (declared below) is
|
|
46
|
+
// safe: it's a closure that only actually runs later, once `selection` is
|
|
47
|
+
// fully bound.
|
|
48
|
+
const rename = useAssetTreeRenameHook({
|
|
49
|
+
currentDir: navigation.currentDir,
|
|
50
|
+
onRenameFile,
|
|
51
|
+
onRenamed: (oldPath, renamedFile) => {
|
|
52
|
+
if (preview.previewPath === oldPath)
|
|
53
|
+
preview.setPreviewPath(renamedFile.path);
|
|
54
|
+
selection.renamePath(oldPath, renamedFile.path);
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
const selection = useAssetTreeSelectionHook(navigation.filesAtCurrentDir, navigation.currentDir, rename.renaming?.path ?? null);
|
|
58
|
+
const rowMenu = useAssetTreeRowMenu(deps.dom);
|
|
59
|
+
const dragUpload = useAssetTreeDragUpload(onUploadFiles);
|
|
60
|
+
useAssetTreeClipboardPasteUpload({
|
|
61
|
+
dom: deps.dom,
|
|
62
|
+
onUploadFiles,
|
|
63
|
+
onBeforeUpload: dragUpload.clearDropReadError,
|
|
64
|
+
});
|
|
65
|
+
const batchActions = useAssetTreeBatchActions({ selected: selection.selected, onDeleteFiles, downloadFiles });
|
|
66
|
+
const copyLocalPath = useAssetTreeCopyLocalPath(deps.clipboard);
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
onSelectionChange?.(selection.selected);
|
|
69
|
+
}, [selection.selected, onSelectionChange]);
|
|
70
|
+
function findFile(path) {
|
|
71
|
+
return files.find((f) => f.path === path);
|
|
72
|
+
}
|
|
73
|
+
function openPath(path) {
|
|
74
|
+
const target = findFile(path);
|
|
75
|
+
if (target)
|
|
76
|
+
onOpenFile(target);
|
|
77
|
+
}
|
|
78
|
+
return { navigation, preview, rename, selection, rowMenu, dragUpload, batchActions, copyLocalPath, findFile, openPath };
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=useAssetTreeBrowserController.js.map
|
|
@@ -25,36 +25,54 @@ import { DEFAULT_KIND_GLYPH, DOUBLE_ACTIVATION_WINDOW_MS, ROW_MENU_ESTIMATED_HEI
|
|
|
25
25
|
* this asymmetry looks surprising but is exactly what the source does.
|
|
26
26
|
*/
|
|
27
27
|
export function deriveTreeChildren(files, folders, currentDir) {
|
|
28
|
-
const
|
|
28
|
+
const isRoot = currentDir === '';
|
|
29
|
+
const prefix = isRoot ? '' : `${currentDir}/`;
|
|
29
30
|
const dirs = new Set();
|
|
31
|
+
const localFiles = collectFileChildren({ files, prefix, isRoot, dirs });
|
|
32
|
+
collectFolderChildren({ folders, prefix, dirs });
|
|
33
|
+
return {
|
|
34
|
+
dirsAtCurrentDir: [...dirs].sort((a, b) => a.localeCompare(b)),
|
|
35
|
+
filesAtCurrentDir: localFiles,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** The first path component of `path` below `prefix`, or `null` when `path` is outside `prefix`. */
|
|
39
|
+
function childSegment(path, prefix) {
|
|
40
|
+
if (!path.startsWith(prefix))
|
|
41
|
+
return null;
|
|
42
|
+
const remainder = path.slice(prefix.length);
|
|
43
|
+
const slashIdx = remainder.indexOf('/');
|
|
44
|
+
if (slashIdx === -1)
|
|
45
|
+
return { head: remainder, nested: false };
|
|
46
|
+
return { head: remainder.slice(0, slashIdx), nested: true };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* File half of {@link deriveTreeChildren}: adds each nested file's top
|
|
50
|
+
* segment to `dirs` and returns the files listed at this level (every
|
|
51
|
+
* in-prefix file at the root, only one-level-deep files elsewhere).
|
|
52
|
+
*/
|
|
53
|
+
function collectFileChildren(input) {
|
|
30
54
|
const localFiles = [];
|
|
31
|
-
for (const f of files) {
|
|
32
|
-
|
|
55
|
+
for (const f of input.files) {
|
|
56
|
+
const segment = childSegment(f.path, input.prefix);
|
|
57
|
+
if (!segment)
|
|
33
58
|
continue;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (
|
|
59
|
+
if (segment.nested)
|
|
60
|
+
input.dirs.add(segment.head);
|
|
61
|
+
if (!segment.nested || input.isRoot)
|
|
37
62
|
localFiles.push(f);
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
dirs.add(remainder.slice(0, slashIdx));
|
|
41
|
-
if (currentDir === '')
|
|
42
|
-
localFiles.push(f);
|
|
43
|
-
}
|
|
44
63
|
}
|
|
45
|
-
|
|
46
|
-
|
|
64
|
+
return localFiles;
|
|
65
|
+
}
|
|
66
|
+
/** Folder half of {@link deriveTreeChildren}: adds each persisted folder's top segment below `prefix` to `dirs`. */
|
|
67
|
+
function collectFolderChildren(input) {
|
|
68
|
+
for (const folder of input.folders) {
|
|
69
|
+
const segment = childSegment(folder.path, input.prefix);
|
|
70
|
+
if (!segment)
|
|
47
71
|
continue;
|
|
48
|
-
|
|
49
|
-
if (!remainder)
|
|
72
|
+
if (!segment.nested && !segment.head)
|
|
50
73
|
continue; // the current directory itself
|
|
51
|
-
|
|
52
|
-
dirs.add(slashIdx === -1 ? remainder : remainder.slice(0, slashIdx));
|
|
74
|
+
input.dirs.add(segment.head);
|
|
53
75
|
}
|
|
54
|
-
return {
|
|
55
|
-
dirsAtCurrentDir: [...dirs].sort((a, b) => a.localeCompare(b)),
|
|
56
|
-
filesAtCurrentDir: localFiles,
|
|
57
|
-
};
|
|
58
76
|
}
|
|
59
77
|
/** Total files (at every depth, not just the immediate level) under `dirPath` — what a folder row's "N files" subtitle counts. */
|
|
60
78
|
export function countFilesUnderDir(files, dirPath) {
|
|
@@ -2,27 +2,26 @@ import { DEFAULT_HOME_NAVIGATION_ENTRY, EMPTY_URL, HISTORY_LIMIT } from './const
|
|
|
2
2
|
function escapeRegExp(value) {
|
|
3
3
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
4
4
|
}
|
|
5
|
+
/** Resolves a `/`-rooted address: against `appOrigin` when it matches a configured app-route prefix, else as a local `file://` path. */
|
|
6
|
+
function resolveAbsolutePath(value, options) {
|
|
7
|
+
const { appRoutePrefixes = [], appOrigin } = options;
|
|
8
|
+
if (appOrigin && appRoutePrefixes.length > 0) {
|
|
9
|
+
const prefixPattern = new RegExp(`^/(${appRoutePrefixes.map(escapeRegExp).join('|')})(/|$)`);
|
|
10
|
+
if (prefixPattern.test(value))
|
|
11
|
+
return new URL(value, appOrigin).toString();
|
|
12
|
+
}
|
|
13
|
+
return `file://${encodeURI(value)}`;
|
|
14
|
+
}
|
|
5
15
|
export function normalizeBrowserAddress(rawAddress, options = {}) {
|
|
6
16
|
const value = rawAddress.trim();
|
|
7
|
-
if (!value)
|
|
8
|
-
return EMPTY_URL;
|
|
9
|
-
if (value === EMPTY_URL)
|
|
17
|
+
if (!value || value === EMPTY_URL)
|
|
10
18
|
return EMPTY_URL;
|
|
11
19
|
if (/^(https?|file):\/\//i.test(value))
|
|
12
20
|
return value;
|
|
13
|
-
if (/^localhost(:\d+)?(\/.*)?$/i.test(value))
|
|
21
|
+
if (/^(localhost|127\.0\.0\.1|0\.0\.0\.0)(:\d+)?(\/.*)?$/i.test(value))
|
|
14
22
|
return `http://${value}`;
|
|
15
|
-
if (
|
|
16
|
-
return
|
|
17
|
-
if (value.startsWith('/')) {
|
|
18
|
-
const { appRoutePrefixes = [], appOrigin } = options;
|
|
19
|
-
if (appOrigin && appRoutePrefixes.length > 0) {
|
|
20
|
-
const prefixPattern = new RegExp(`^/(${appRoutePrefixes.map(escapeRegExp).join('|')})(/|$)`);
|
|
21
|
-
if (prefixPattern.test(value))
|
|
22
|
-
return new URL(value, appOrigin).toString();
|
|
23
|
-
}
|
|
24
|
-
return `file://${encodeURI(value)}`;
|
|
25
|
-
}
|
|
23
|
+
if (value.startsWith('/'))
|
|
24
|
+
return resolveAbsolutePath(value, options);
|
|
26
25
|
if (/^[\w.-]+\.[a-z]{2,}(:\d+)?(\/.*)?$/i.test(value))
|
|
27
26
|
return `https://${value}`;
|
|
28
27
|
return `https://www.google.com/search?q=${encodeURIComponent(value)}`;
|
|
@@ -137,37 +136,37 @@ export function mergeHistoryEntry(history, url, meta = {}, options = {}, now = D
|
|
|
137
136
|
if (!isHistoryUrl(url))
|
|
138
137
|
return history;
|
|
139
138
|
const existing = history.find((entry) => sameUrl(entry.url, url));
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const nextTitle = meta.title && meta.title.trim()
|
|
143
|
-
? meta.title.trim()
|
|
144
|
-
: existing?.title || labelFromUrl(url);
|
|
145
|
-
const nextIconUrl = cleanIconUrl(meta.iconUrl) || existing?.iconUrl || faviconUrl(url);
|
|
146
|
-
const visitIncrement = options.countVisit === false ? 0 : 1;
|
|
147
|
-
const entry = existing
|
|
148
|
-
? {
|
|
149
|
-
...existing,
|
|
150
|
-
...(nextIconUrl !== undefined ? { iconUrl: nextIconUrl } : {}),
|
|
151
|
-
title: nextTitle,
|
|
152
|
-
lastVisitedAt: visitIncrement > 0 ? now : existing.lastVisitedAt,
|
|
153
|
-
visitCount: existing.visitCount + visitIncrement,
|
|
154
|
-
}
|
|
155
|
-
: {
|
|
156
|
-
...(nextIconUrl !== undefined ? { iconUrl: nextIconUrl } : {}),
|
|
157
|
-
title: nextTitle,
|
|
158
|
-
url,
|
|
159
|
-
lastVisitedAt: now,
|
|
160
|
-
visitCount: 1,
|
|
161
|
-
};
|
|
162
|
-
if (existing
|
|
163
|
-
&& existing.title === entry.title
|
|
164
|
-
&& existing.iconUrl === entry.iconUrl
|
|
165
|
-
&& existing.lastVisitedAt === entry.lastVisitedAt
|
|
166
|
-
&& existing.visitCount === entry.visitCount) {
|
|
139
|
+
const entry = buildHistoryEntry({ url, meta, now, countVisit: options.countVisit !== false }, { existing });
|
|
140
|
+
if (existing && sameHistoryEntryState(existing, entry))
|
|
167
141
|
return history;
|
|
168
|
-
}
|
|
169
142
|
return [entry, ...history.filter((item) => !sameUrl(item.url, url))].slice(0, HISTORY_LIMIT);
|
|
170
143
|
}
|
|
144
|
+
/** Builds the merged entry for `url`: an update of `existing` when present, otherwise a fresh first-visit entry. */
|
|
145
|
+
function buildHistoryEntry({ url, meta, now, countVisit }, { existing }) {
|
|
146
|
+
const resolved = resolveHistoryMeta({ url, meta }, { existing });
|
|
147
|
+
if (!existing)
|
|
148
|
+
return { ...resolved, url, lastVisitedAt: now, visitCount: 1 };
|
|
149
|
+
return {
|
|
150
|
+
...existing,
|
|
151
|
+
...resolved,
|
|
152
|
+
lastVisitedAt: countVisit ? now : existing.lastVisitedAt,
|
|
153
|
+
visitCount: existing.visitCount + (countVisit ? 1 : 0),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/** Title and icon for a merged entry: the caller's meta wins, then the existing entry's, then values derived from `url`. */
|
|
157
|
+
function resolveHistoryMeta({ url, meta }, { existing }) {
|
|
158
|
+
// `isHistoryUrl` above already excludes `EMPTY_URL`, so `labelFromUrl`'s
|
|
159
|
+
// home-label branch can never trigger here — no `homeLabel` option to pass.
|
|
160
|
+
const title = meta.title?.trim() || existing?.title || labelFromUrl(url);
|
|
161
|
+
const iconUrl = cleanIconUrl(meta.iconUrl) || existing?.iconUrl || faviconUrl(url);
|
|
162
|
+
return iconUrl !== undefined ? { iconUrl, title } : { title };
|
|
163
|
+
}
|
|
164
|
+
function sameHistoryEntryState(left, right) {
|
|
165
|
+
return (left.title === right.title
|
|
166
|
+
&& left.iconUrl === right.iconUrl
|
|
167
|
+
&& left.lastVisitedAt === right.lastVisitedAt
|
|
168
|
+
&& left.visitCount === right.visitCount);
|
|
169
|
+
}
|
|
171
170
|
// ---------------------------------------------------------------------------
|
|
172
171
|
// Navigation stack
|
|
173
172
|
// ---------------------------------------------------------------------------
|
|
@@ -201,6 +200,20 @@ export function recordNavigation(state, url, title, options = {}) {
|
|
|
201
200
|
if (url !== EMPTY_URL && !isHistoryUrl(url))
|
|
202
201
|
return state;
|
|
203
202
|
const { navigationStack: stack, navigationIndex: index } = state;
|
|
203
|
+
const nextTitle = navigationTitle({ url }, { title, homeLabel: options.homeLabel });
|
|
204
|
+
const rejoin = findRejoinTarget({ state, url }, options);
|
|
205
|
+
if (rejoin) {
|
|
206
|
+
const next = stack.slice();
|
|
207
|
+
// `nextTitle` is only ever '' for EMPTY_URL with an explicit '' homeLabel;
|
|
208
|
+
// keep the rejoined entry's own title rather than blanking it.
|
|
209
|
+
next[rejoin.index] = { title: nextTitle || rejoin.entry.title, url };
|
|
210
|
+
return { navigationStack: next, navigationIndex: rejoin.index };
|
|
211
|
+
}
|
|
212
|
+
const base = index >= 0 ? stack.slice(0, index + 1) : [];
|
|
213
|
+
const nextStack = [...base, { title: nextTitle, url }].slice(-HISTORY_LIMIT);
|
|
214
|
+
return { navigationStack: nextStack, navigationIndex: nextStack.length - 1 };
|
|
215
|
+
}
|
|
216
|
+
function navigationTitle({ url }, { title, homeLabel }) {
|
|
204
217
|
// Bug fix: this used to hardcode DEFAULT_HOME_NAVIGATION_ENTRY.title here,
|
|
205
218
|
// ignoring a caller-supplied `options.homeLabel` — so a host configuring a
|
|
206
219
|
// custom `homeEntry` (via useBrowserNavigationStack) would see the entry's
|
|
@@ -208,34 +221,25 @@ export function recordNavigation(state, url, title, options = {}) {
|
|
|
208
221
|
// even though `initialNavigationState`/`labelFromUrl` both honor the
|
|
209
222
|
// custom label correctly. Falls back to the generic default only when no
|
|
210
223
|
// homeLabel is supplied.
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
};
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
return { navigationStack: updateEntry(stack, index), navigationIndex: index };
|
|
227
|
-
}
|
|
228
|
-
const previousIndex = index - 1;
|
|
229
|
-
if (previousIndex >= 0 && sameUrl(stack[previousIndex]?.url ?? '', url)) {
|
|
230
|
-
return { navigationStack: updateEntry(stack, previousIndex), navigationIndex: previousIndex };
|
|
231
|
-
}
|
|
232
|
-
const nextIndex = index + 1;
|
|
233
|
-
if (nextIndex < stack.length && sameUrl(stack[nextIndex]?.url ?? '', url)) {
|
|
234
|
-
return { navigationStack: updateEntry(stack, nextIndex), navigationIndex: nextIndex };
|
|
224
|
+
if (url === EMPTY_URL)
|
|
225
|
+
return homeLabel ?? DEFAULT_HOME_NAVIGATION_ENTRY.title;
|
|
226
|
+
return title?.trim() || labelFromUrl(url, homeLabel);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* The stack entry `url` should update in place instead of appending: the
|
|
230
|
+
* current entry (same url, or the pending-load target being replaced), else
|
|
231
|
+
* the adjacent back entry, else the adjacent forward entry. An out-of-range
|
|
232
|
+
* index reads as `undefined`, which never matches.
|
|
233
|
+
*/
|
|
234
|
+
function findRejoinTarget({ state, url }, { replacePendingTarget, pendingTarget }) {
|
|
235
|
+
const { navigationStack: stack, navigationIndex: index } = state;
|
|
236
|
+
const current = stack[index];
|
|
237
|
+
if (current && (sameUrl(current.url, url) || (replacePendingTarget && pendingTarget && sameUrl(current.url, pendingTarget)))) {
|
|
238
|
+
return { index, entry: current };
|
|
235
239
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
240
|
+
return [index - 1, index + 1]
|
|
241
|
+
.map((candidate) => ({ index: candidate, entry: stack[candidate] }))
|
|
242
|
+
.find((target) => target.entry !== undefined && sameUrl(target.entry.url, url));
|
|
239
243
|
}
|
|
240
244
|
export function updateCurrentNavigationTitle(state, title) {
|
|
241
245
|
const trimmedTitle = title?.trim();
|
|
@@ -59,6 +59,12 @@ export function mountChart({ element, spec, registry }, options = {}) {
|
|
|
59
59
|
function candidates() {
|
|
60
60
|
return selectChartProviders({ registry, spec: input.spec }, { siteProvider: options.siteProvider }).filter(({ id }) => !failed.has(id));
|
|
61
61
|
}
|
|
62
|
+
function isStaleMount({ token }, _options = {}) {
|
|
63
|
+
return destroyed || token !== generation;
|
|
64
|
+
}
|
|
65
|
+
function isCurrentUpdate({ current, updateRevision }, _options = {}) {
|
|
66
|
+
return !destroyed && active === current && updateRevision === revision;
|
|
67
|
+
}
|
|
62
68
|
/** Async renderer errors arrive after mount; defer teardown outside the provider's commit. */
|
|
63
69
|
function renderError(entry, error, token) {
|
|
64
70
|
queueMicrotask(() => {
|
|
@@ -76,7 +82,7 @@ export function mountChart({ element, spec, registry }, options = {}) {
|
|
|
76
82
|
let renderRevision = revision;
|
|
77
83
|
try {
|
|
78
84
|
const provider = await entry.load({});
|
|
79
|
-
if (
|
|
85
|
+
if (isStaleMount({ token }))
|
|
80
86
|
return;
|
|
81
87
|
const frame = element.ownerDocument.createElement('div');
|
|
82
88
|
renderer.replaceChildren(frame);
|
|
@@ -84,13 +90,13 @@ export function mountChart({ element, spec, registry }, options = {}) {
|
|
|
84
90
|
const instance = provider.mount({ element: frame, ...input }, { onError: (error) => renderError(entry, error, token) });
|
|
85
91
|
active = { entry, instance };
|
|
86
92
|
await instance.ready;
|
|
87
|
-
if (
|
|
93
|
+
if (isStaleMount({ token }) || renderRevision !== revision)
|
|
88
94
|
return;
|
|
89
95
|
fallback.hidden = true;
|
|
90
96
|
return;
|
|
91
97
|
}
|
|
92
98
|
catch (error) {
|
|
93
|
-
if (
|
|
99
|
+
if (isStaleMount({ token }) || renderRevision !== revision)
|
|
94
100
|
return;
|
|
95
101
|
failed.add(entry.id);
|
|
96
102
|
report(entry, error);
|
|
@@ -113,12 +119,12 @@ export function mountChart({ element, spec, registry }, options = {}) {
|
|
|
113
119
|
const current = active;
|
|
114
120
|
try {
|
|
115
121
|
await current.instance.update(input);
|
|
116
|
-
if (
|
|
122
|
+
if (isCurrentUpdate({ current, updateRevision }))
|
|
117
123
|
fallback.hidden = true;
|
|
118
124
|
return;
|
|
119
125
|
}
|
|
120
126
|
catch (error) {
|
|
121
|
-
if (
|
|
127
|
+
if (!isCurrentUpdate({ current, updateRevision }))
|
|
122
128
|
return;
|
|
123
129
|
failed.add(current.entry.id);
|
|
124
130
|
report(current.entry, error);
|
|
@@ -13,8 +13,8 @@ export function ChartHost({ spec, registry, theme = DEFAULT_CHART_THEME, sitePro
|
|
|
13
13
|
const instance = useRef(undefined);
|
|
14
14
|
const previous = useRef({ spec, theme });
|
|
15
15
|
useEffect(() => {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
// The container `<div>` below is rendered unconditionally, so React has always attached the ref
|
|
17
|
+
// by the time this effect runs; the old `if (!element.current) return` could never fire.
|
|
18
18
|
const mounted = mountChart({ element: element.current, spec, registry }, {
|
|
19
19
|
theme, siteProvider, ...(onProviderError ? { onProviderError } : {}),
|
|
20
20
|
});
|
|
@@ -41,8 +41,8 @@ export function renderChartTable({ element, spec }, _options = {}) {
|
|
|
41
41
|
const tr = body.insertRow();
|
|
42
42
|
for (const { key } of columns) {
|
|
43
43
|
const cell = tr.insertCell();
|
|
44
|
-
const value = row[key];
|
|
45
|
-
cell.textContent =
|
|
44
|
+
const value = row[key] ?? '';
|
|
45
|
+
cell.textContent = typeof value === 'object' ? JSON.stringify(value) : String(value);
|
|
46
46
|
cell.dataset.numeric = String(numeric.has(key));
|
|
47
47
|
}
|
|
48
48
|
}
|
|
@@ -30,18 +30,12 @@ export function useCommandPalette(options, dependencies) {
|
|
|
30
30
|
onClose();
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
|
-
if (event.key === 'ArrowDown') {
|
|
33
|
+
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
34
34
|
event.preventDefault();
|
|
35
35
|
if (results.length === 0)
|
|
36
36
|
return;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
if (event.key === 'ArrowUp') {
|
|
41
|
-
event.preventDefault();
|
|
42
|
-
if (results.length === 0)
|
|
43
|
-
return;
|
|
44
|
-
setCursor((current) => nextCursor(current, results.length, -1));
|
|
37
|
+
const direction = event.key === 'ArrowDown' ? 1 : -1;
|
|
38
|
+
setCursor((current) => nextCursor(current, results.length, direction));
|
|
45
39
|
return;
|
|
46
40
|
}
|
|
47
41
|
if (event.key === 'Enter') {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ConnectorsDependencies } from './ports.js';
|
|
2
2
|
import type { Connector, ConnectorAuthResultEvent, ProviderTab } from './types.js';
|
|
3
|
-
import { useConnectorCatalog } from './hooks/useConnectorCatalog.js';
|
|
4
|
-
import { useConnectorAuthorization } from './hooks/useConnectorAuthorization.js';
|
|
5
|
-
import { useConnectorDetail } from './hooks/useConnectorDetail.js';
|
|
3
|
+
import type { useConnectorCatalog } from './hooks/useConnectorCatalog.js';
|
|
4
|
+
import type { useConnectorAuthorization } from './hooks/useConnectorAuthorization.js';
|
|
5
|
+
import type { useConnectorDetail } from './hooks/useConnectorDetail.js';
|
|
6
6
|
import type { ConnectorGateProps } from './components/ConnectorGate.js';
|
|
7
7
|
export interface ConnectorsBrowserProps {
|
|
8
8
|
/** Whether the host's provider is configured (e.g. an API key is saved) — gates enrichment + unmasks the grid. */
|
|
@@ -36,5 +36,5 @@ export interface ConnectorsBrowserProps {
|
|
|
36
36
|
* messages. Ported from OD's `ConnectorsBrowser.tsx` — see
|
|
37
37
|
* `packages/ui/source-map.md` for full provenance.
|
|
38
38
|
*/
|
|
39
|
-
export declare function ConnectorsBrowser({ unlocked, providerTabs, catalogRefreshKey, dependencies, getCategoryLabel, getDisplayableAccountLabel, gate, onProviderTabClick, onConnectorAuthResult, onConnectorsChanged, useConnectorCatalog
|
|
39
|
+
export declare function ConnectorsBrowser({ unlocked, providerTabs, catalogRefreshKey, dependencies, getCategoryLabel, getDisplayableAccountLabel, gate, onProviderTabClick, onConnectorAuthResult, onConnectorsChanged, useConnectorCatalog, useConnectorAuthorization, useConnectorDetail, agentHandle, }: ConnectorsBrowserProps): import("react").JSX.Element;
|
|
40
40
|
//# sourceMappingURL=ConnectorsBrowser.d.ts.map
|