@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
|
@@ -1,36 +1,42 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { useT } from '../../../i18n/index.js';
|
|
4
|
-
import {
|
|
5
|
-
import { DEFAULT_SEARCH_DEBOUNCE_MS } from '../../constants.js';
|
|
6
|
-
import { buildFacetLabelMap, dayKeyFromTimestamp, resolveFacetLabel } from '../../rules.js';
|
|
4
|
+
import { dayKeyFromTimestamp, resolveFacetLabel } from '../../rules.js';
|
|
7
5
|
import { useWiredAssetGridData } from '../hooks/useAssetGridData.js';
|
|
8
6
|
import { useWiredAssetGridLiveUpdates } from '../hooks/useAssetGridLiveUpdates.js';
|
|
9
7
|
import { useAssetGridSelection } from '../hooks/useAssetGridSelection.js';
|
|
10
8
|
import { useRubberBandDrag } from '../hooks/useRubberBandDrag.js';
|
|
11
9
|
import { useAssetGridKeyboardShortcuts } from '../hooks/useAssetGridKeyboardShortcuts.js';
|
|
10
|
+
import { useAssetGridFilters } from './useAssetGridFilters.js';
|
|
12
11
|
import { AssetCard } from './AssetCard.js';
|
|
13
12
|
import { AssetGridBody } from './AssetGridBody.js';
|
|
14
13
|
import { AssetGridToolbar } from './AssetGridToolbar.js';
|
|
15
14
|
import { SelectionActionBar } from './SelectionActionBar.js';
|
|
16
15
|
import { SelectionBand } from './SelectionBand.js';
|
|
17
16
|
import { DeleteConfirmDialog } from './DeleteConfirmDialog.js';
|
|
17
|
+
function renderGridContent({ loading, emptyState, t, ...bodyProps }, _options = {}) {
|
|
18
|
+
if (loading && bodyProps.assets.length === 0)
|
|
19
|
+
return _jsx("p", { className: "asset-grid-empty", children: t('Loading…') });
|
|
20
|
+
if (bodyProps.assets.length === 0)
|
|
21
|
+
return emptyState ?? _jsx("div", { className: "asset-grid-empty", children: t('No assets yet.') });
|
|
22
|
+
return _jsx(AssetGridBody, { ...bodyProps });
|
|
23
|
+
}
|
|
18
24
|
/**
|
|
19
25
|
* A live-updating, filterable, multi-selectable asset grid — the generic
|
|
20
26
|
* pattern behind a photo library / DAM. See `packages/ui/source-map.md` for
|
|
21
27
|
* the full retained-behavior manifest against the original `LibrarySection.tsx`.
|
|
22
28
|
*/
|
|
23
|
-
export function AssetGrid({ active = true, selectors, dependencies, kindFacets
|
|
29
|
+
export function AssetGrid({ active = true, selectors, dependencies, kindFacets: configuredKindFacets, sourceFacets: configuredSourceFacets, renderThumbnail, renderThumbnailPlaceholder, renderCardExtra, renderBulkActions, onDeleteAsset, onDeleteSelected, onPreview, isPreviewOpen = false, toolbarActions, emptyState, searchPlaceholder, searchDebounceMs, liveUpdateCoalesceMs, initialViewMode = 'grid', useWiredAssetGridData: useWiredAssetGridDataHook = useWiredAssetGridData, useAssetGridSelection: useAssetGridSelectionHook = useAssetGridSelection, }) {
|
|
24
30
|
const t = useT();
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
const { kind, setKind, source, setSource, search, setSearch, debouncedSearch, filtersActive, kindFacets, sourceFacets, kindFacetLabels, sourceFacetLabels, } = useAssetGridFilters({
|
|
32
|
+
kindFacets: configuredKindFacets,
|
|
33
|
+
sourceFacets: configuredSourceFacets,
|
|
34
|
+
searchDebounceMs,
|
|
35
|
+
});
|
|
28
36
|
const [viewMode, setViewMode] = useState(initialViewMode);
|
|
29
37
|
const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false);
|
|
30
38
|
/** Message from a delete the host rejected. Rendered inline — see `runDelete`. */
|
|
31
39
|
const [deleteError, setDeleteError] = useState(null);
|
|
32
|
-
const debouncedSearch = useDebouncedValue({ value: search, delayMs: searchDebounceMs });
|
|
33
|
-
const filtersActive = !!(kind || source || debouncedSearch.trim());
|
|
34
40
|
// `dependencies` (host-supplied, or omitted to fall back to the package's
|
|
35
41
|
// in-memory fake) is resolved by each wirer independently — neither this
|
|
36
42
|
// component nor the real `useAssetGridData`/`useAssetGridLiveUpdates`
|
|
@@ -102,11 +108,16 @@ export function AssetGrid({ active = true, selectors, dependencies, kindFacets =
|
|
|
102
108
|
setSelectedIds(new Set());
|
|
103
109
|
});
|
|
104
110
|
}, [selectedIds, onDeleteSelected, setAssets, setSelectedIds, runDelete]);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
111
|
+
// Built only when the host supplied `onDeleteAsset`: the card shows its per-asset delete control
|
|
112
|
+
// only when it receives a handler, so there is no "no handler" path to route through `runDelete`
|
|
113
|
+
// here (the old inline `onDeleteAsset ? … : null` could never see `null` from a rendered card).
|
|
114
|
+
const handleDeleteAsset = useMemo(() => onDeleteAsset
|
|
115
|
+
? async (id) => {
|
|
116
|
+
await runDelete((batch) => onDeleteAsset(batch[0]), [id], () => {
|
|
117
|
+
setAssets((prev) => prev.filter((a) => a.id !== id));
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
: undefined, [onDeleteAsset, setAssets, runDelete]);
|
|
110
121
|
const handlePreview = useCallback((id) => {
|
|
111
122
|
const asset = assets.find((a) => a.id === id);
|
|
112
123
|
if (asset)
|
|
@@ -122,13 +133,11 @@ export function AssetGrid({ active = true, selectors, dependencies, kindFacets =
|
|
|
122
133
|
onClearSelection: clearSelection,
|
|
123
134
|
onRequestDeleteSelected: requestDeleteSelected,
|
|
124
135
|
});
|
|
125
|
-
const kindFacetLabels = useMemo(() => buildFacetLabelMap(kindFacets), [kindFacets]);
|
|
126
|
-
const sourceFacetLabels = useMemo(() => buildFacetLabelMap(sourceFacets), [sourceFacets]);
|
|
127
136
|
const getDayKey = useMemo(() => selectors.getDayKey ?? ((asset) => dayKeyFromTimestamp(selectors.getTimestamp(asset))), [selectors]);
|
|
128
137
|
const renderCard = useCallback((asset, index) => {
|
|
129
138
|
const kindValue = selectors.getKind(asset);
|
|
130
139
|
const sourceValue = selectors.getSource?.(asset);
|
|
131
|
-
return (_jsx(AssetCard, { asset: asset, index: index, selected: selectedIds.has(asset.id), title: selectors.getTitle(asset), subtitle: selectors.getSubtitle?.(asset), kindLabel: resolveFacetLabel(kindValue, kindFacetLabels), sourceLabel: sourceValue ? resolveFacetLabel(sourceValue, sourceFacetLabels) : undefined, renderThumbnail: renderThumbnail, renderThumbnailPlaceholder: renderThumbnailPlaceholder, onToggle: toggleOne, onRange: rangeTo, onPreview: handlePreview, onDeleteAsset:
|
|
140
|
+
return (_jsx(AssetCard, { asset: asset, index: index, selected: selectedIds.has(asset.id), title: selectors.getTitle(asset), subtitle: selectors.getSubtitle?.(asset), kindLabel: resolveFacetLabel(kindValue, kindFacetLabels), sourceLabel: sourceValue ? resolveFacetLabel(sourceValue, sourceFacetLabels) : undefined, renderThumbnail: renderThumbnail, renderThumbnailPlaceholder: renderThumbnailPlaceholder, onToggle: toggleOne, onRange: rangeTo, onPreview: handlePreview, onDeleteAsset: handleDeleteAsset, renderCardExtra: renderCardExtra }, asset.id));
|
|
132
141
|
}, [
|
|
133
142
|
selectedIds,
|
|
134
143
|
selectors,
|
|
@@ -143,6 +152,17 @@ export function AssetGrid({ active = true, selectors, dependencies, kindFacets =
|
|
|
143
152
|
handleDeleteAsset,
|
|
144
153
|
renderCardExtra,
|
|
145
154
|
]);
|
|
146
|
-
return (_jsxs("div", { className: "asset-grid-root", children: [_jsx(AssetGridToolbar, { search: search, onSearchChange: setSearch, searchPlaceholder: searchPlaceholder, kind: kind, onKindChange: setKind, kindFacets: kindFacets, source: source, onSourceChange: setSource, sourceFacets: sourceFacets, viewMode: viewMode, onViewModeChange: setViewMode, onRefresh: () => void reload(), loading: loading, toolbarActions: toolbarActions }), selectedIds.size > 0 && !dragging ? (_jsx(SelectionActionBar, { selectedIds: [...selectedIds], onSelectAll: selectAll, onClear: clearSelection, onRequestDelete: requestDeleteSelected, renderBulkActions: renderBulkActions })) : null,
|
|
155
|
+
return (_jsxs("div", { className: "asset-grid-root", children: [_jsx(AssetGridToolbar, { search: search, onSearchChange: setSearch, searchPlaceholder: searchPlaceholder, kind: kind, onKindChange: setKind, kindFacets: kindFacets, source: source, onSourceChange: setSource, sourceFacets: sourceFacets, viewMode: viewMode, onViewModeChange: setViewMode, onRefresh: () => void reload(), loading: loading, toolbarActions: toolbarActions }), selectedIds.size > 0 && !dragging ? (_jsx(SelectionActionBar, { selectedIds: [...selectedIds], onSelectAll: selectAll, onClear: clearSelection, onRequestDelete: requestDeleteSelected, renderBulkActions: renderBulkActions })) : null, renderGridContent({
|
|
156
|
+
loading,
|
|
157
|
+
emptyState,
|
|
158
|
+
t,
|
|
159
|
+
viewMode,
|
|
160
|
+
assets,
|
|
161
|
+
getDayKey,
|
|
162
|
+
containerRef,
|
|
163
|
+
onMouseDown,
|
|
164
|
+
selecting: selectedIds.size > 0,
|
|
165
|
+
renderCard,
|
|
166
|
+
}), _jsx(SelectionBand, { band: band }), deleteError ? (_jsx("p", { className: "jini-hint jini-hint-error", role: "alert", children: deleteError })) : null, confirmDeleteOpen ? (_jsx(DeleteConfirmDialog, { count: selectedIds.size, onCancel: () => setConfirmDeleteOpen(false), onConfirm: () => void confirmDelete() })) : null] }));
|
|
147
167
|
}
|
|
148
168
|
//# sourceMappingURL=AssetGrid.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AssetGridFacetOption } from '../../types.js';
|
|
2
|
+
/** Owns the editable filters and the debounced membership used by data/live updates. */
|
|
3
|
+
export declare function useAssetGridFilters({ kindFacets, sourceFacets, searchDebounceMs, }: {
|
|
4
|
+
kindFacets: AssetGridFacetOption[] | undefined;
|
|
5
|
+
sourceFacets: AssetGridFacetOption[] | undefined;
|
|
6
|
+
searchDebounceMs: number | undefined;
|
|
7
|
+
}, _options?: Record<string, never>): {
|
|
8
|
+
kind: string;
|
|
9
|
+
setKind: import("react").Dispatch<import("react").SetStateAction<string>>;
|
|
10
|
+
source: string;
|
|
11
|
+
setSource: import("react").Dispatch<import("react").SetStateAction<string>>;
|
|
12
|
+
search: string;
|
|
13
|
+
setSearch: import("react").Dispatch<import("react").SetStateAction<string>>;
|
|
14
|
+
debouncedSearch: string;
|
|
15
|
+
filtersActive: boolean;
|
|
16
|
+
kindFacets: AssetGridFacetOption[];
|
|
17
|
+
sourceFacets: AssetGridFacetOption[];
|
|
18
|
+
kindFacetLabels: Map<string, string>;
|
|
19
|
+
sourceFacetLabels: Map<string, string>;
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=useAssetGridFilters.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useMemo, useState } from 'react';
|
|
2
|
+
import { useDebouncedValue } from '../../../../react/hooks/useDebouncedValue.js';
|
|
3
|
+
import { DEFAULT_SEARCH_DEBOUNCE_MS } from '../../constants.js';
|
|
4
|
+
import { buildFacetLabelMap } from '../../rules.js';
|
|
5
|
+
/** Owns the editable filters and the debounced membership used by data/live updates. */
|
|
6
|
+
export function useAssetGridFilters({ kindFacets = [], sourceFacets = [], searchDebounceMs = DEFAULT_SEARCH_DEBOUNCE_MS, }, _options = {}) {
|
|
7
|
+
const [kind, setKind] = useState('');
|
|
8
|
+
const [source, setSource] = useState('');
|
|
9
|
+
const [search, setSearch] = useState('');
|
|
10
|
+
const debouncedSearch = useDebouncedValue({ value: search, delayMs: searchDebounceMs });
|
|
11
|
+
const filtersActive = !!(kind || source || debouncedSearch.trim());
|
|
12
|
+
const kindFacetLabels = useMemo(() => buildFacetLabelMap(kindFacets), [kindFacets]);
|
|
13
|
+
const sourceFacetLabels = useMemo(() => buildFacetLabelMap(sourceFacets), [sourceFacets]);
|
|
14
|
+
return {
|
|
15
|
+
kind, setKind, source, setSource, search, setSearch,
|
|
16
|
+
debouncedSearch, filtersActive, kindFacets, sourceFacets, kindFacetLabels, sourceFacetLabels,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=useAssetGridFilters.js.map
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { useGlobalKeydown } from '../../../../browser/index.js';
|
|
2
2
|
import { isTypingTarget } from '../../rules.js';
|
|
3
|
+
function handleSelectionKey({ event, typing, hasSelection, isPreviewOpen, onClearSelection, onRequestDeleteSelected }, _options = {}) {
|
|
4
|
+
if (event.key === 'Escape') {
|
|
5
|
+
if (isPreviewOpen)
|
|
6
|
+
return; // the preview surface owns Escape while it's open
|
|
7
|
+
if (hasSelection)
|
|
8
|
+
onClearSelection();
|
|
9
|
+
}
|
|
10
|
+
else if (event.key === 'Delete' || event.key === 'Backspace') {
|
|
11
|
+
if (typing || isPreviewOpen || !hasSelection)
|
|
12
|
+
return;
|
|
13
|
+
event.preventDefault();
|
|
14
|
+
onRequestDeleteSelected();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
3
17
|
/** Cmd/Ctrl+A select-all, Escape clear-selection, Delete/Backspace triggers the bulk-delete confirm flow. */
|
|
4
18
|
export function useAssetGridKeyboardShortcuts(params) {
|
|
5
19
|
const { active, enabled, hasAssets, hasSelection, isPreviewOpen, onSelectAll, onClearSelection, onRequestDeleteSelected, } = params;
|
|
@@ -14,17 +28,8 @@ export function useAssetGridKeyboardShortcuts(params) {
|
|
|
14
28
|
e.preventDefault();
|
|
15
29
|
onSelectAll();
|
|
16
30
|
}
|
|
17
|
-
else
|
|
18
|
-
|
|
19
|
-
return; // the preview surface owns Escape while it's open
|
|
20
|
-
if (hasSelection)
|
|
21
|
-
onClearSelection();
|
|
22
|
-
}
|
|
23
|
-
else if (e.key === 'Delete' || e.key === 'Backspace') {
|
|
24
|
-
if (typing || isPreviewOpen || !hasSelection)
|
|
25
|
-
return;
|
|
26
|
-
e.preventDefault();
|
|
27
|
-
onRequestDeleteSelected();
|
|
31
|
+
else {
|
|
32
|
+
handleSelectionKey({ event: e, typing, hasSelection, isPreviewOpen, onClearSelection, onRequestDeleteSelected });
|
|
28
33
|
}
|
|
29
34
|
},
|
|
30
35
|
}, { enabled: active });
|
|
@@ -19,6 +19,8 @@ export interface UseAssetGridLiveUpdatesParams<TAsset extends AssetGridItem> {
|
|
|
19
19
|
* id all fall back to a single full reload for that window — see
|
|
20
20
|
* `packages/ui/archived provenance ledger` for why (the original OD comment: "a filtered
|
|
21
21
|
* view can't predict membership client-side").
|
|
22
|
+
* Disposed subscriptions discard in-flight incremental reads. Failed passes report a
|
|
23
|
+
* fixed diagnostic and request a full reconciliation on the next live event.
|
|
22
24
|
*/
|
|
23
25
|
export declare function useAssetGridLiveUpdates<TAsset extends AssetGridItem>(params: UseAssetGridLiveUpdatesParams<TAsset>): void;
|
|
24
26
|
export type UseWiredAssetGridLiveUpdatesParams<TAsset extends AssetGridItem> = Omit<UseAssetGridLiveUpdatesParams<TAsset>, 'liveUpdates' | 'fetchAssetById'> & {
|
|
@@ -2,6 +2,18 @@ import { useEffect, useMemo, useRef } from 'react';
|
|
|
2
2
|
import { DEFAULT_LIVE_UPDATE_COALESCE_MS } from '../../constants.js';
|
|
3
3
|
import { mergeIngestedAssets } from '../../rules.js';
|
|
4
4
|
import { createFakeAssetGridDependencies } from '../../dependencies.js';
|
|
5
|
+
/** Drains pending deletes and cancels matching ingests before the pass can fetch.
|
|
6
|
+
* Mutates the pending sets and schedules the asset update. @complexity O(d + a) deletes and assets. */
|
|
7
|
+
function applyPendingDeletes({ pendingDelete, pendingIngest, setAssets }, _options = {}) {
|
|
8
|
+
// Deletes are free (no fetch); apply them first.
|
|
9
|
+
if (pendingDelete.size) {
|
|
10
|
+
const del = new Set(pendingDelete);
|
|
11
|
+
pendingDelete.clear();
|
|
12
|
+
for (const id of del)
|
|
13
|
+
pendingIngest.delete(id);
|
|
14
|
+
setAssets((prev) => prev.filter((a) => !del.has(a.id)));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
5
17
|
/**
|
|
6
18
|
* Subscribes to a live-update transport and reconciles incremental
|
|
7
19
|
* ingest/delete events into the current asset list. Events are coalesced
|
|
@@ -11,6 +23,8 @@ import { createFakeAssetGridDependencies } from '../../dependencies.js';
|
|
|
11
23
|
* id all fall back to a single full reload for that window — see
|
|
12
24
|
* `packages/ui/archived provenance ledger` for why (the original OD comment: "a filtered
|
|
13
25
|
* view can't predict membership client-side").
|
|
26
|
+
* Disposed subscriptions discard in-flight incremental reads. Failed passes report a
|
|
27
|
+
* fixed diagnostic and request a full reconciliation on the next live event.
|
|
14
28
|
*/
|
|
15
29
|
export function useAssetGridLiveUpdates(params) {
|
|
16
30
|
const { active, liveUpdates, filtersActive, fetchAssetById, setAssets, reload, coalesceMs = DEFAULT_LIVE_UPDATE_COALESCE_MS } = params;
|
|
@@ -33,19 +47,17 @@ export function useAssetGridLiveUpdates(params) {
|
|
|
33
47
|
useEffect(() => {
|
|
34
48
|
if (!active || !liveUpdates)
|
|
35
49
|
return;
|
|
50
|
+
let disposed = false;
|
|
36
51
|
let timer = null;
|
|
37
52
|
const pendingIngest = new Set();
|
|
38
53
|
const pendingDelete = new Set();
|
|
39
54
|
let pendingFull = false;
|
|
55
|
+
// No `disposed` check on entry: a pass only ever starts from `flush`, whose first pass runs
|
|
56
|
+
// synchronously from a timer that cleanup cancels, and whose later passes are gated by the loop's
|
|
57
|
+
// own `!disposed` condition — so a pass can never begin after disposal. The post-fetch check
|
|
58
|
+
// below still matters, because disposal can land while a pass is awaiting.
|
|
40
59
|
const runFlushPass = async () => {
|
|
41
|
-
|
|
42
|
-
if (pendingDelete.size) {
|
|
43
|
-
const del = new Set(pendingDelete);
|
|
44
|
-
pendingDelete.clear();
|
|
45
|
-
for (const id of del)
|
|
46
|
-
pendingIngest.delete(id);
|
|
47
|
-
setAssets((prev) => prev.filter((a) => !del.has(a.id)));
|
|
48
|
-
}
|
|
60
|
+
applyPendingDeletes({ pendingDelete, pendingIngest, setAssets });
|
|
49
61
|
if (pendingFull || filtersActiveRef.current) {
|
|
50
62
|
pendingFull = false;
|
|
51
63
|
pendingIngest.clear();
|
|
@@ -61,6 +73,9 @@ export function useAssetGridLiveUpdates(params) {
|
|
|
61
73
|
return;
|
|
62
74
|
}
|
|
63
75
|
const fetched = await Promise.all(ids.map((id) => fetchById(id)));
|
|
76
|
+
// The same state setter can outlive this subscription (for example after deactivation).
|
|
77
|
+
if (disposed)
|
|
78
|
+
return;
|
|
64
79
|
// A missing fetch is ambiguous (filtered out? race?) — reload instead.
|
|
65
80
|
if (fetched.some((a) => a === null)) {
|
|
66
81
|
await reloadRef.current();
|
|
@@ -105,15 +120,23 @@ export function useAssetGridLiveUpdates(params) {
|
|
|
105
120
|
try {
|
|
106
121
|
do {
|
|
107
122
|
rerun = false;
|
|
108
|
-
|
|
109
|
-
|
|
123
|
+
try {
|
|
124
|
+
await runFlushPass();
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
if (!disposed) {
|
|
128
|
+
pendingFull = true;
|
|
129
|
+
console.error('[asset-grid] Live update failed.');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} while (rerun && !disposed);
|
|
110
133
|
}
|
|
111
134
|
finally {
|
|
112
135
|
flushing = false;
|
|
113
136
|
}
|
|
114
137
|
};
|
|
115
138
|
const schedule = () => {
|
|
116
|
-
if (timer)
|
|
139
|
+
if (disposed || timer)
|
|
117
140
|
return;
|
|
118
141
|
timer = setTimeout(() => void flush(), coalesceMs);
|
|
119
142
|
};
|
|
@@ -132,6 +155,7 @@ export function useAssetGridLiveUpdates(params) {
|
|
|
132
155
|
},
|
|
133
156
|
});
|
|
134
157
|
return () => {
|
|
158
|
+
disposed = true;
|
|
135
159
|
if (timer)
|
|
136
160
|
clearTimeout(timer);
|
|
137
161
|
unsubscribe();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
2
|
import type { AssetTreeDependencies } from '../../ports.js';
|
|
3
3
|
import type { AssetTreeFileItem, AssetTreeFolderItem, AssetTreeKindConfigMap, AssetTreeNavState, AssetTreeSelectors, AssetTreeToolbarAction } from '../../types.js';
|
|
4
|
-
import { useAssetTreeNavigation } from '../hooks/useAssetTreeNavigation.js';
|
|
5
|
-
import { useAssetTreePreview } from '../hooks/useAssetTreePreview.js';
|
|
6
|
-
import { useAssetTreeRename } from '../hooks/useAssetTreeRename.js';
|
|
7
|
-
import { useAssetTreeSelection } from '../hooks/useAssetTreeSelection.js';
|
|
4
|
+
import type { useAssetTreeNavigation } from '../hooks/useAssetTreeNavigation.js';
|
|
5
|
+
import type { useAssetTreePreview } from '../hooks/useAssetTreePreview.js';
|
|
6
|
+
import type { useAssetTreeRename } from '../hooks/useAssetTreeRename.js';
|
|
7
|
+
import type { useAssetTreeSelection } from '../hooks/useAssetTreeSelection.js';
|
|
8
8
|
export interface AssetTreeBrowserProps<TFile extends AssetTreeFileItem> {
|
|
9
9
|
files: TFile[];
|
|
10
10
|
/** Persisted folders, including empty ones — see `AssetTreeFolderItem`'s doc comment. */
|
|
@@ -59,5 +59,5 @@ export interface AssetTreeBrowserProps<TFile extends AssetTreeFileItem> {
|
|
|
59
59
|
* section, project menu, rotating-tip footer, and file-kind-specific
|
|
60
60
|
* preview rendering).
|
|
61
61
|
*/
|
|
62
|
-
export declare function AssetTreeBrowser<TFile extends AssetTreeFileItem>(
|
|
62
|
+
export declare function AssetTreeBrowser<TFile extends AssetTreeFileItem>(props: AssetTreeBrowserProps<TFile>): import("react").JSX.Element;
|
|
63
63
|
//# sourceMappingURL=AssetTreeBrowser.d.ts.map
|
|
@@ -1,27 +1,8 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
2
|
-
import { useEffect, useMemo } from 'react';
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
2
|
import { useT } from '../../../i18n/index.js';
|
|
4
|
-
import { DEFAULT_KIND_CONFIG_MAP
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { useAssetTreeBatchActions } from '../hooks/useAssetTreeBatchActions.js';
|
|
8
|
-
import { useAssetTreeClipboardPasteUpload } from '../hooks/useAssetTreeClipboardPasteUpload.js';
|
|
9
|
-
import { useAssetTreeCopyLocalPath } from '../hooks/useAssetTreeCopyLocalPath.js';
|
|
10
|
-
import { useAssetTreeDragUpload } from '../hooks/useAssetTreeDragUpload.js';
|
|
11
|
-
import { useAssetTreeNavigation } from '../hooks/useAssetTreeNavigation.js';
|
|
12
|
-
import { useAssetTreePreview } from '../hooks/useAssetTreePreview.js';
|
|
13
|
-
import { useAssetTreeRename } from '../hooks/useAssetTreeRename.js';
|
|
14
|
-
import { useAssetTreeRowMenu } from '../hooks/useAssetTreeRowMenu.js';
|
|
15
|
-
import { useAssetTreeSelection } from '../hooks/useAssetTreeSelection.js';
|
|
16
|
-
import { AssetTreeBreadcrumbs } from './AssetTreeBreadcrumbs.js';
|
|
17
|
-
import { AssetTreeEmptyState } from './AssetTreeEmptyState.js';
|
|
18
|
-
import { AssetTreeFileRow } from './AssetTreeFileRow.js';
|
|
19
|
-
import { AssetTreeFolderRow } from './AssetTreeFolderRow.js';
|
|
20
|
-
import { AssetTreeRowMenu } from './AssetTreeRowMenu.js';
|
|
21
|
-
import { AssetTreeSelectionBar } from './AssetTreeSelectionBar.js';
|
|
22
|
-
import { AssetTreeToolbar } from './AssetTreeToolbar.js';
|
|
23
|
-
import { AssetTreeUploadErrorBanner } from './AssetTreeUploadErrorBanner.js';
|
|
24
|
-
import { FilePreviewPane } from './FilePreviewPane.js';
|
|
3
|
+
import { DEFAULT_KIND_CONFIG_MAP } from '../../constants.js';
|
|
4
|
+
import { AssetTreeBrowserBody, AssetTreeBrowserPreview, AssetTreeBrowserRowMenu, AssetTreeBrowserTopbar, } from './AssetTreeBrowserParts.js';
|
|
5
|
+
import { useAssetTreeBrowserController } from './useAssetTreeBrowserController.js';
|
|
25
6
|
/**
|
|
26
7
|
* A directory-navigable file tree: breadcrumbs, a host-supplied toolbar,
|
|
27
8
|
* kind-grouped sections with a pinned Folders section, row selection with
|
|
@@ -33,118 +14,18 @@ import { FilePreviewPane } from './FilePreviewPane.js';
|
|
|
33
14
|
* section, project menu, rotating-tip footer, and file-kind-specific
|
|
34
15
|
* preview rendering).
|
|
35
16
|
*/
|
|
36
|
-
export function AssetTreeBrowser(
|
|
17
|
+
export function AssetTreeBrowser(props) {
|
|
37
18
|
const t = useT();
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// wrapper. Deliberately not fixed here: unlike ConnectorsBrowser's `deps`
|
|
42
|
-
// (fed to exactly the 3 already-overridable sub-hooks), this `deps` also
|
|
43
|
-
// feeds `useAssetTreeRowMenu`, `useAssetTreeClipboardPasteUpload`, and
|
|
44
|
-
// `useAssetTreeCopyLocalPath` below — none of which currently have their
|
|
45
|
-
// own override props — so collapsing it into one wired hook is a real
|
|
46
|
-
// design decision (what belongs in the wired hook vs. stays overridable)
|
|
47
|
-
// rather than a mechanical port of the ConnectorsBrowser pattern. Lower
|
|
48
|
-
// severity than that one too: these are DOM/clipboard adapters, not
|
|
49
|
-
// remote data fetching.
|
|
50
|
-
const deps = useMemo(() => dependencies ?? createBrowserAssetTreeDependencies(), [dependencies]);
|
|
51
|
-
const navigation = useAssetTreeNavigationHook({
|
|
52
|
-
files,
|
|
53
|
-
folders,
|
|
54
|
-
sectionOrder,
|
|
55
|
-
getKind: selectors.getKind,
|
|
56
|
-
getModifiedAt: selectors.getModifiedAt,
|
|
57
|
-
navState,
|
|
58
|
-
onNavStateChange,
|
|
59
|
-
});
|
|
60
|
-
const preview = useAssetTreePreviewHook(files, selectInitialPreviewFile);
|
|
61
|
-
// Declared before `useAssetTreeSelection` so its in-flight `renaming.path`
|
|
62
|
-
// (if any) can be threaded into selection's prune logic as
|
|
63
|
-
// `pendingRenamePath` — see that hook's doc comment for the race this
|
|
64
|
-
// closes. `onRenamed` referencing `selection` here (declared below) is
|
|
65
|
-
// safe: it's a closure that only actually runs later, once `selection` is
|
|
66
|
-
// fully bound.
|
|
67
|
-
const rename = useAssetTreeRenameHook({
|
|
68
|
-
currentDir: navigation.currentDir,
|
|
69
|
-
onRenameFile,
|
|
70
|
-
onRenamed: (oldPath, renamedFile) => {
|
|
71
|
-
if (preview.previewPath === oldPath)
|
|
72
|
-
preview.setPreviewPath(renamedFile.path);
|
|
73
|
-
selection.renamePath(oldPath, renamedFile.path);
|
|
74
|
-
},
|
|
75
|
-
});
|
|
76
|
-
const selection = useAssetTreeSelectionHook(navigation.filesAtCurrentDir, navigation.currentDir, rename.renaming?.path ?? null);
|
|
77
|
-
const rowMenu = useAssetTreeRowMenu(deps.dom);
|
|
78
|
-
const dragUpload = useAssetTreeDragUpload(onUploadFiles);
|
|
79
|
-
useAssetTreeClipboardPasteUpload({
|
|
80
|
-
dom: deps.dom,
|
|
81
|
-
onUploadFiles,
|
|
82
|
-
onBeforeUpload: dragUpload.clearDropReadError,
|
|
83
|
-
});
|
|
84
|
-
const batchActions = useAssetTreeBatchActions({ selected: selection.selected, onDeleteFiles, downloadFiles });
|
|
85
|
-
const copyLocalPath = useAssetTreeCopyLocalPath(deps.clipboard);
|
|
86
|
-
useEffect(() => {
|
|
87
|
-
onSelectionChange?.(selection.selected);
|
|
88
|
-
}, [selection.selected, onSelectionChange]);
|
|
89
|
-
function findFile(path) {
|
|
90
|
-
return files.find((f) => f.path === path);
|
|
91
|
-
}
|
|
92
|
-
function openPath(path) {
|
|
93
|
-
const target = findFile(path);
|
|
94
|
-
if (target)
|
|
95
|
-
onOpenFile(target);
|
|
96
|
-
}
|
|
97
|
-
const menuFile = rowMenu.menuPos ? (findFile(rowMenu.menuPos.path) ?? null) : null;
|
|
19
|
+
const { files, folders, selectors, kindConfig = DEFAULT_KIND_CONFIG_MAP, getFileUrl } = props;
|
|
20
|
+
const controller = useAssetTreeBrowserController(props, props);
|
|
21
|
+
const { navigation, preview, selection, rowMenu, dragUpload } = controller;
|
|
98
22
|
const hasSelection = selection.selected.size > 0;
|
|
99
|
-
const isEmpty = files.length === 0 && (folders?.length ?? 0) === 0;
|
|
100
23
|
const previewedFile = preview.previewFile;
|
|
101
|
-
|
|
102
|
-
return (_jsxs("div", { className: `asset-tree-panel${previewedFile ? '' : ' no-preview'}${hasSelection ? ' has-selection' : ''}`, children: [reloading ? (_jsx("div", { className: "asset-tree-reloading-overlay", "data-testid": "asset-tree-reloading", children: t('Loading…') })) : null, _jsxs("div", { className: "asset-tree-main", children: [_jsxs("div", { className: "asset-tree-topbar", children: [_jsx("div", { className: "asset-tree-topbar-left", children: _jsx(AssetTreeBreadcrumbs, { currentDir: navigation.currentDir, rootLabel: rootLabel ?? t('Files'), onNavigate: navigation.setCurrentDir }) }), _jsx("div", { className: "asset-tree-topbar-right", children: _jsx(AssetTreeToolbar, { actions: toolbarActions }) })] }), _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, hasSelection ? (_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 })) : null, isEmpty ? (_jsx(AssetTreeEmptyState, { actions: emptyStateActions })) : (_jsxs(_Fragment, { children: [navigation.dirsAtCurrentDir.length > 0 ? (_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: navigation.dirsAtCurrentDir.length })] }), navigation.dirsAtCurrentDir.map((dirName) => {
|
|
103
|
-
const fullPath = navigation.currentDir === '' ? dirName : `${navigation.currentDir}/${dirName}`;
|
|
104
|
-
return (_jsx(AssetTreeFolderRow, { name: dirName, path: fullPath, fileCount: countFilesUnderDir(files, fullPath), onNavigate: navigation.setCurrentDir }, fullPath));
|
|
105
|
-
})] }, "folders")) : null, navigation.sections.map((section) => {
|
|
106
|
-
const config = resolveKindConfig(section.kind, kindConfig);
|
|
107
|
-
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)))] }, `kind:${section.kind}`));
|
|
108
|
-
})] })), footer] }), dragUpload.draggingFiles ? (_jsx("div", { className: "asset-tree-drop-overlay", "aria-hidden": true, "data-testid": "asset-tree-drop-overlay", children: _jsx("div", { className: "asset-tree-drop-overlay-card", children: _jsx("span", { className: "label", children: t('Drop to upload') }) }) })) : null] }), previewedFile && previewKindConfig ? (_jsx(FilePreviewPane
|
|
24
|
+
return (_jsxs("div", { className: `asset-tree-panel${previewedFile ? '' : ' no-preview'}${hasSelection ? ' has-selection' : ''}`, children: [props.reloading ? (_jsx("div", { className: "asset-tree-reloading-overlay", "data-testid": "asset-tree-reloading", children: t('Loading…') })) : null, _jsxs("div", { className: "asset-tree-main", children: [_jsx(AssetTreeBrowserTopbar, { currentDir: navigation.currentDir, rootLabel: props.rootLabel, onNavigate: navigation.setCurrentDir, toolbarActions: props.toolbarActions }), _jsx(AssetTreeBrowserBody, { controller: controller, files: files, folders: folders, selectors: selectors, kindConfig: kindConfig, downloadFiles: props.downloadFiles, emptyStateActions: props.emptyStateActions, footer: props.footer }), dragUpload.draggingFiles ? (_jsx("div", { className: "asset-tree-drop-overlay", "aria-hidden": true, "data-testid": "asset-tree-drop-overlay", children: _jsx("div", { className: "asset-tree-drop-overlay-card", children: _jsx("span", { className: "label", children: t('Drop to upload') }) }) })) : null] }), previewedFile ? (_jsx(AssetTreeBrowserPreview
|
|
109
25
|
// Keyed on the path so a host's `renderThumbnail` (which may mount
|
|
110
26
|
// a heavy/stateful element like an iframe or canvas) fully
|
|
111
27
|
// remounts when the user switches files, instead of React reusing
|
|
112
28
|
// the same DOM node across two different files.
|
|
113
|
-
, { file: previewedFile,
|
|
114
|
-
? { href: getFileUrl(menuFile), onClick: rowMenu.closeMenu }
|
|
115
|
-
: undefined, onOpen: () => {
|
|
116
|
-
const path = rowMenu.menuPos.path;
|
|
117
|
-
rowMenu.closeMenu();
|
|
118
|
-
openPath(path);
|
|
119
|
-
}, onRename: () => {
|
|
120
|
-
const path = rowMenu.menuPos.path;
|
|
121
|
-
rowMenu.closeMenu();
|
|
122
|
-
rename.startRename(path);
|
|
123
|
-
},
|
|
124
|
-
// Deliberately does NOT close the menu (unlike every other action
|
|
125
|
-
// here) — the origin `DesignFilesPanel`'s equivalent handler did
|
|
126
|
-
// call `setMenuPos(null)` before copying, which meant its
|
|
127
|
-
// "Copied" confirmation label could never actually become visible
|
|
128
|
-
// to a user (it only ever renders inside the now-closed popover).
|
|
129
|
-
// Since the whole point of `copiedPath`/the transient-confirmation
|
|
130
|
-
// UI is to be seen, this keeps the menu open so it can be; the
|
|
131
|
-
// existing outside-dismiss/Escape handling still closes it
|
|
132
|
-
// normally afterward.
|
|
133
|
-
onCopyLocalPath: () => {
|
|
134
|
-
const path = rowMenu.menuPos.path;
|
|
135
|
-
// `canCopyLocalPath` (passed below) already gates the button's
|
|
136
|
-
// `disabled` state on `!!menuFile`, so a click can only reach
|
|
137
|
-
// this handler when `menuFile` is non-null — asserting that
|
|
138
|
-
// instead of re-guarding it here avoids an unreachable `: null`
|
|
139
|
-
// ternary arm (a real click can never fire on a disabled
|
|
140
|
-
// button, so that arm was dead code, not defensive coding).
|
|
141
|
-
const localPath = selectors.getLocalPath?.(menuFile);
|
|
142
|
-
if (localPath)
|
|
143
|
-
void copyLocalPath.copyLocalPath(path, localPath);
|
|
144
|
-
}, onDelete: () => {
|
|
145
|
-
const path = rowMenu.menuPos.path;
|
|
146
|
-
rowMenu.closeMenu();
|
|
147
|
-
onDeleteFile(path);
|
|
148
|
-
} })) : null] }));
|
|
29
|
+
, { file: previewedFile, selectors: selectors, kindConfig: kindConfig, onOpenFile: props.onOpenFile, onClose: preview.clearPreview, getFileUrl: getFileUrl, renderPreviewThumbnail: props.renderPreviewThumbnail, thumbnailIsInteractive: props.thumbnailIsInteractive }, previewedFile.path)) : null, rowMenu.menuPos ? (_jsx(AssetTreeBrowserRowMenu, { controller: controller, menuPos: rowMenu.menuPos, selectors: selectors, getFileUrl: getFileUrl, onDeleteFile: props.onDeleteFile })) : null] }));
|
|
149
30
|
}
|
|
150
31
|
//# sourceMappingURL=AssetTreeBrowser.js.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { AssetTreeFileItem, AssetTreeFolderItem, AssetTreeKindConfigMap, AssetTreeMenuPosition, AssetTreeSelectors, AssetTreeToolbarAction } from '../../types.js';
|
|
3
|
+
import type { AssetTreeBrowserController } from './useAssetTreeBrowserController.js';
|
|
4
|
+
interface ControllerProps<TFile> {
|
|
5
|
+
controller: AssetTreeBrowserController<TFile>;
|
|
6
|
+
}
|
|
7
|
+
export interface AssetTreeBrowserTopbarProps {
|
|
8
|
+
currentDir: string;
|
|
9
|
+
rootLabel?: string | undefined;
|
|
10
|
+
onNavigate: (dir: string) => void;
|
|
11
|
+
toolbarActions?: readonly AssetTreeToolbarAction[] | undefined;
|
|
12
|
+
}
|
|
13
|
+
/** Breadcrumbs (root label defaults to `t('Files')`) plus the host toolbar. */
|
|
14
|
+
export declare function AssetTreeBrowserTopbar({ currentDir, rootLabel, onNavigate, toolbarActions, }: AssetTreeBrowserTopbarProps): import("react").JSX.Element;
|
|
15
|
+
export interface AssetTreeBrowserBodyProps<TFile extends AssetTreeFileItem> extends ControllerProps<TFile> {
|
|
16
|
+
files: TFile[];
|
|
17
|
+
folders?: AssetTreeFolderItem[] | undefined;
|
|
18
|
+
selectors: AssetTreeSelectors<TFile>;
|
|
19
|
+
kindConfig: AssetTreeKindConfigMap;
|
|
20
|
+
downloadFiles?: ((paths: string[]) => Promise<{
|
|
21
|
+
blob: Blob;
|
|
22
|
+
filename: string;
|
|
23
|
+
}>) | undefined;
|
|
24
|
+
emptyStateActions?: readonly AssetTreeToolbarAction[] | undefined;
|
|
25
|
+
footer?: ReactNode;
|
|
26
|
+
}
|
|
27
|
+
/** The drop-target listing area: upload-error banner, selection bar, folders + kind sections (or the empty state), footer. */
|
|
28
|
+
export declare function AssetTreeBrowserBody<TFile extends AssetTreeFileItem>(props: AssetTreeBrowserBodyProps<TFile>): import("react").JSX.Element;
|
|
29
|
+
export interface AssetTreeBrowserPreviewProps<TFile extends AssetTreeFileItem> {
|
|
30
|
+
file: TFile;
|
|
31
|
+
selectors: AssetTreeSelectors<TFile>;
|
|
32
|
+
kindConfig: AssetTreeKindConfigMap;
|
|
33
|
+
onOpenFile: (file: TFile) => void;
|
|
34
|
+
onClose: () => void;
|
|
35
|
+
getFileUrl?: ((file: TFile) => string) | undefined;
|
|
36
|
+
renderPreviewThumbnail?: ((file: TFile) => ReactNode) | undefined;
|
|
37
|
+
thumbnailIsInteractive?: boolean | undefined;
|
|
38
|
+
}
|
|
39
|
+
/** `FilePreviewPane` for the previewed `file`, with its kind display config resolved. */
|
|
40
|
+
export declare function AssetTreeBrowserPreview<TFile extends AssetTreeFileItem>({ file, selectors, kindConfig, onOpenFile, onClose, getFileUrl, renderPreviewThumbnail, thumbnailIsInteractive, }: AssetTreeBrowserPreviewProps<TFile>): import("react").JSX.Element;
|
|
41
|
+
export interface AssetTreeBrowserRowMenuProps<TFile extends AssetTreeFileItem> extends ControllerProps<TFile> {
|
|
42
|
+
menuPos: AssetTreeMenuPosition;
|
|
43
|
+
selectors: AssetTreeSelectors<TFile>;
|
|
44
|
+
getFileUrl?: ((file: TFile) => string) | undefined;
|
|
45
|
+
onDeleteFile: (path: string) => void;
|
|
46
|
+
}
|
|
47
|
+
/** The open row context menu for `menuPos.path`, with every action wired. */
|
|
48
|
+
export declare function AssetTreeBrowserRowMenu<TFile extends AssetTreeFileItem>({ controller, menuPos, selectors, getFileUrl, onDeleteFile, }: AssetTreeBrowserRowMenuProps<TFile>): import("react").JSX.Element;
|
|
49
|
+
export {};
|
|
50
|
+
//# sourceMappingURL=AssetTreeBrowserParts.d.ts.map
|