@jini-ai/ui 0.5.4 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/core/theme/validation.js +40 -38
- package/dist/features/a2ui/renderer.js +15 -13
- package/dist/features/about/react/components/AboutTab.js +64 -34
- package/dist/features/about/rules.d.ts +2 -2
- package/dist/features/about/rules.js +156 -129
- package/dist/features/admin-widgets/components/ImagePreviewModal.d.ts +4 -3
- package/dist/features/admin-widgets/components/ImagePreviewModal.js +6 -10
- package/dist/features/admin-widgets/components/InfoTip.d.ts +3 -2
- package/dist/features/admin-widgets/components/InfoTip.hooks.js +2 -0
- package/dist/features/admin-widgets/components/InfoTip.js +34 -7
- package/dist/features/admin-widgets/components/InfoTip.rules.d.ts +13 -0
- package/dist/features/admin-widgets/components/InfoTip.rules.js +43 -10
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.d.ts +30 -0
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.d.ts +40 -0
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.js +128 -0
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.js +18 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.d.ts +14 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.js +12 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.rules.d.ts +20 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.rules.js +21 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.d.ts +18 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.d.ts +16 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.js +45 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.js +18 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.rules.d.ts +32 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.rules.js +26 -0
- package/dist/features/admin-widgets/components/Select/Select.hooks.d.ts +21 -0
- package/dist/features/admin-widgets/components/Select/Select.hooks.js +24 -2
- package/dist/features/admin-widgets/components/Select/Select.js +4 -3
- package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.hooks.js +29 -14
- package/dist/features/admin-widgets/index.d.ts +14 -2
- package/dist/features/admin-widgets/index.js +7 -1
- package/dist/features/admin-widgets/styles/admin-widgets.css +1 -0
- package/dist/features/asset-grid/react/components/AssetCard.js +7 -1
- package/dist/features/asset-grid/react/components/AssetGrid.d.ts +1 -1
- package/dist/features/asset-grid/react/components/AssetGrid.js +38 -18
- package/dist/features/asset-grid/react/components/useAssetGridFilters.d.ts +21 -0
- package/dist/features/asset-grid/react/components/useAssetGridFilters.js +19 -0
- package/dist/features/asset-grid/react/hooks/useAssetGridKeyboardShortcuts.js +16 -11
- package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.d.ts +2 -0
- package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.js +35 -11
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.d.ts +6 -6
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.js +10 -129
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.d.ts +50 -0
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.js +95 -0
- package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.d.ts +56 -0
- package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.js +80 -0
- package/dist/features/asset-tree-browser/rules.js +40 -22
- package/dist/features/browser-chrome/rules.js +74 -70
- package/dist/features/charts/browser.js +11 -5
- package/dist/features/charts/react.js +2 -2
- package/dist/features/charts/table.js +2 -2
- package/dist/features/command-palette/react/hooks/useCommandPalette.js +3 -9
- package/dist/features/connectors/ConnectorsBrowser.d.ts +4 -4
- package/dist/features/connectors/ConnectorsBrowser.js +26 -14
- package/dist/features/connectors/components/ConnectorCard.js +67 -38
- package/dist/features/connectors/components/ConnectorDetailDrawer.js +62 -31
- package/dist/features/connectors/components/ConnectorGrid.js +11 -6
- package/dist/features/connectors/hooks/useConnectorAuthorization.js +6 -0
- package/dist/features/connectors/hooks/useConnectorCatalog.d.ts +2 -0
- package/dist/features/connectors/hooks/useConnectorCatalog.js +32 -13
- package/dist/features/connectors/hooks/useWiredConnectorsBrowser.d.ts +3 -3
- package/dist/features/connectors/rules.d.ts +8 -1
- package/dist/features/connectors/rules.js +42 -20
- package/dist/features/connectors/types.d.ts +11 -0
- package/dist/features/execution/constants.d.ts +4 -10
- package/dist/features/execution/constants.js +9 -94
- package/dist/features/execution/react/components/ByokProviderForm.d.ts +2 -4
- package/dist/features/execution/react/components/ByokProviderForm.js +140 -43
- package/dist/features/execution/react/components/ExecutionTab.d.ts +1 -2
- package/dist/features/execution/react/components/ExecutionTab.js +52 -30
- package/dist/features/execution/react/components/LocalCliAgentCard.js +12 -3
- package/dist/features/execution/react/components/LocalCliAgentList.js +14 -3
- package/dist/features/execution/react/components/LocalCliAgentSettings.js +14 -3
- package/dist/features/execution/react/hooks/useLocalCliAgentCard.js +54 -36
- package/dist/features/execution/react/hooks/useReasoningControl.d.ts +1 -1
- package/dist/features/execution/react/hooks/useReasoningControl.js +116 -75
- package/dist/features/execution/rules.d.ts +13 -2
- package/dist/features/execution/rules.js +51 -11
- package/dist/features/execution/types.d.ts +6 -42
- package/dist/features/execution/types.js +0 -17
- package/dist/features/external-mcp/react/components/ExternalMcpTab.d.ts +6 -2
- package/dist/features/external-mcp/react/components/ExternalMcpTab.js +9 -2
- package/dist/features/file-dropzone/react/components/FileDropzone.js +24 -25
- package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.d.ts +2 -2
- package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.js +35 -30
- package/dist/features/file-dropzone/react/components/FileDropzoneThumbnailGrid.js +30 -24
- package/dist/features/file-dropzone/react/components/FileDropzoneZone.d.ts +14 -0
- package/dist/features/file-dropzone/react/components/FileDropzoneZone.js +26 -0
- package/dist/features/html-editor/canvas-content-wrapper.js +13 -13
- package/dist/features/html-editor/canvas-embed-placeholders.js +46 -37
- package/dist/features/html-editor/css.js +76 -69
- package/dist/features/html-editor/editor-blocks.js +47 -33
- package/dist/features/html-editor/editor-serialization.js +6 -5
- package/dist/features/html-editor/protected-elements.js +39 -28
- package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.js +4 -4
- package/dist/features/html-editor/source-splice.js +51 -30
- package/dist/features/i18n/dictionaries/settings-dialog.ja.js +1 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +1 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +1 -0
- package/dist/features/i18n/locale.js +20 -16
- package/dist/features/iframe-pool/react/dom-sync.js +8 -6
- package/dist/features/integrations/react/components/CodexInstallToggleButton.js +19 -4
- package/dist/features/integrations/react/components/IntegrationsTab.js +60 -14
- package/dist/features/interactive-ui/interactive-ui.css +1 -1
- package/dist/features/interactive-ui/providers/native/data-table.manifest.d.ts +6 -6
- package/dist/features/interactive-ui/providers/shadcn/data-table.js +10 -7
- package/dist/features/interactive-ui/providers/shadcn/data-table.manifest.d.ts +6 -6
- package/dist/features/lexical-rich-text-editor/mention-parser.js +60 -57
- package/dist/features/lexical-rich-text-editor/react/hooks/useKeyboardCommands.js +33 -38
- package/dist/features/lexical-rich-text-editor/rules.js +87 -74
- package/dist/features/lexical-rich-text-editor/serialize.js +27 -25
- package/dist/features/mcp-ui/secret-card.js +111 -61
- package/dist/features/mcp-ui/surfaces/confirmation.js +39 -21
- package/dist/features/mcp-ui/surfaces/document.d.ts +1 -1
- package/dist/features/mcp-ui/surfaces/document.js +10 -2
- package/dist/features/mcp-ui/surfaces/fields.js +30 -50
- package/dist/features/mcp-ui/surfaces/form.d.ts +6 -0
- package/dist/features/mcp-ui/surfaces/form.js +43 -21
- package/dist/features/mcp-ui/surfaces/outcome.js +30 -20
- package/dist/features/mcp-ui/surfaces/text-input.js +33 -21
- package/dist/features/mcp-ui/surfaces/tokens.d.ts +6 -1
- package/dist/features/mcp-ui/surfaces/tokens.js +5 -2
- package/dist/features/media-providers/constants.d.ts +1 -0
- package/dist/features/media-providers/constants.js +2 -81
- package/dist/features/media-providers/index.d.ts +1 -1
- package/dist/features/media-providers/ports.d.ts +2 -0
- package/dist/features/media-providers/react/components/MediaProviderCard.d.ts +22 -0
- package/dist/features/media-providers/react/components/MediaProviderCard.js +50 -0
- package/dist/features/media-providers/react/components/MediaProvidersTab.d.ts +5 -6
- package/dist/features/media-providers/react/components/MediaProvidersTab.js +40 -72
- package/dist/features/media-providers/react/components/media-providers-tab-labels.d.ts +11 -0
- package/dist/features/media-providers/react/components/media-providers-tab-labels.js +57 -0
- package/dist/features/media-providers/react/hooks/useMediaProvidersTab.d.ts +4 -2
- package/dist/features/media-providers/react/hooks/useMediaProvidersTab.js +37 -23
- package/dist/features/media-providers/rules.js +17 -10
- package/dist/features/media-providers/types.d.ts +4 -22
- package/dist/features/media-providers/types.js +3 -8
- package/dist/features/memory/formatters.js +127 -92
- package/dist/features/memory/react/components/MemoryConnectedPanel.js +92 -36
- package/dist/features/memory/react/components/MemoryList.js +37 -18
- package/dist/features/memory/react/hooks/useMemoryExtractions.store.js +42 -36
- package/dist/features/notifications/react/components/NotificationsTab.js +28 -28
- package/dist/features/observability/long-task.d.ts +0 -12
- package/dist/features/observability/long-task.js +15 -19
- package/dist/features/observability/resource-error.d.ts +0 -10
- package/dist/features/observability/resource-error.js +18 -13
- package/dist/features/observability/stuck-run.js +10 -6
- package/dist/features/observability/telemetry-url.d.ts +6 -0
- package/dist/features/observability/telemetry-url.js +16 -0
- package/dist/features/observability/white-screen.js +12 -24
- package/dist/features/panel-kit/fetch-query/adapter.react.d.ts +2 -2
- package/dist/features/panel-kit/fetch-query/adapter.react.js +4 -2
- package/dist/features/panel-kit/fetch-query/adapter.tanstack.js +46 -33
- package/dist/features/panel-kit/helpers/standing-draft-local-backup.js +3 -1
- package/dist/features/panel-kit/hooks/use-settings-slice.hooks.js +3 -1
- package/dist/features/privacy/react/components/PrivacyTab.js +24 -13
- package/dist/features/progress-card/components/ProgressCard.js +7 -3
- package/dist/features/progress-card/reference-adapters.js +59 -54
- package/dist/features/project-locations/react/components/ProjectLocationsTab.js +24 -15
- package/dist/features/project-locations/react/hooks/useProjectLocationsTab.d.ts +8 -0
- package/dist/features/project-locations/react/hooks/useProjectLocationsTab.js +31 -23
- package/dist/features/resource-dashboard/react/components/ResourceBoard.js +6 -47
- package/dist/features/resource-dashboard/react/components/ResourceBoardView.d.ts +2 -2
- package/dist/features/resource-dashboard/react/components/ResourceBoardView.js +3 -6
- package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.d.ts +15 -0
- package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.js +35 -0
- package/dist/features/resource-dashboard/react/components/ResourceCard.js +5 -16
- package/dist/features/resource-dashboard/react/components/ResourceCardParts.d.ts +34 -0
- package/dist/features/resource-dashboard/react/components/ResourceCardParts.js +34 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowList.js +3 -1
- package/dist/features/resource-dashboard/react/components/ResourceRowListItem.js +2 -2
- package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.d.ts +19 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.js +11 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowListView.d.ts +1 -1
- package/dist/features/resource-dashboard/react/components/ResourceRowListView.js +4 -4
- package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.d.ts +10 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.js +20 -0
- package/dist/features/resource-dashboard/react/components/resourceBoardProps.d.ts +48 -0
- package/dist/features/resource-dashboard/react/components/resourceBoardProps.js +70 -0
- package/dist/features/resource-dashboard/react/components/resourceRowListProps.d.ts +19 -0
- package/dist/features/resource-dashboard/react/components/resourceRowListProps.js +14 -0
- package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.d.ts +16 -0
- package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.js +13 -0
- package/dist/features/resource-dashboard/rules.js +2 -8
- package/dist/features/sketch-editor/dom.js +150 -99
- package/dist/features/sketch-editor/react/components/SketchEditor.js +26 -15
- package/dist/features/skills/react/components/SkillDraftForm.js +13 -19
- package/dist/features/skills/react/components/SkillDraftForm.labels.d.ts +31 -0
- package/dist/features/skills/react/components/SkillDraftForm.labels.js +54 -0
- package/dist/features/skills/react/components/SkillRow.js +38 -18
- package/dist/features/skills/react/components/SkillRow.labels.d.ts +7 -0
- package/dist/features/skills/react/components/SkillRow.labels.js +18 -0
- package/dist/features/skills/react/components/SkillsTab.js +24 -14
- package/dist/features/skills/react/components/SkillsTab.labels.d.ts +7 -0
- package/dist/features/skills/react/components/SkillsTab.labels.js +11 -0
- package/dist/features/skills/rules.js +32 -23
- package/dist/features/source-config-list/agent-handles.js +2 -1
- package/dist/features/source-config-list/dependencies.js +53 -49
- package/dist/features/source-config-list/index.d.ts +3 -3
- package/dist/features/source-config-list/react/components/SourceConfigAddForm.d.ts +6 -5
- package/dist/features/source-config-list/react/components/SourceConfigAddForm.js +38 -7
- package/dist/features/source-config-list/react/components/SourceConfigField.js +69 -25
- package/dist/features/source-config-list/react/components/SourceConfigItemCard.d.ts +6 -5
- package/dist/features/source-config-list/react/components/SourceConfigItemCard.js +76 -20
- package/dist/features/source-config-list/react/components/SourceConfigListView.d.ts +5 -3
- package/dist/features/source-config-list/react/components/SourceConfigListView.js +33 -3
- package/dist/features/source-config-list/react/components/SourceConfigTestControl.js +17 -1
- package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.d.ts +11 -0
- package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.js +12 -0
- package/dist/features/source-config-list/types.d.ts +14 -0
- package/dist/features/tab-launcher-menu/react/components/TabLauncherMenu.js +25 -1
- package/dist/features/tab-launcher-menu/react/hooks/useTabLauncherMenu.js +15 -16
- package/dist/features/tab-strip/react/components/TabBar.js +8 -1
- package/dist/features/tabbed-dialog/react/components/TabbedDialog.js +8 -30
- package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.d.ts +15 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.js +8 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.d.ts +12 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.js +6 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.d.ts +27 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.js +12 -0
- package/dist/features/tabbed-dialog/react/components/tabbedDialogView.d.ts +43 -0
- package/dist/features/tabbed-dialog/react/components/tabbedDialogView.js +59 -0
- package/dist/features/version-manager/dependencies.d.ts +3 -1
- package/dist/features/version-manager/dependencies.js +3 -2
- package/dist/features/version-manager/react/components/VersionManagerModal.js +10 -6
- package/dist/features/version-manager/react/hooks/useVersionManager.js +7 -4
- package/dist/features/viewer-shell/react/components/CommentSidePanel.js +103 -50
- package/dist/features/viewer-shell/react/components/MarkdownSplitPane.js +28 -8
- package/dist/features/viewer-shell/react/hooks/useCommentReorder.js +22 -16
- package/dist/features/viewer-shell/rules.js +19 -23
- package/dist/react/components/CustomSelect.d.ts +38 -1
- package/dist/react/components/CustomSelect.js +94 -38
- package/dist/react/components/OnboardingDropdown.js +20 -8
- package/dist/react/components/RemixIcon.stylesheet.js +3 -1
- package/dist/react/components/Toast.js +13 -2
- package/dist/react/components/TooltipLayer.js +5 -14
- package/dist/react/components/WorkingDirPicker.js +78 -50
- package/dist/react/components/remixicon-font-recovery.js +38 -30
- package/dist/react/components/select-popover.d.ts +8 -6
- package/dist/react/components/select-popover.js +33 -29
- package/dist/react/mcp-ui/McpUiHost.d.ts +1 -1
- package/dist/react/mcp-ui/McpUiHost.js +10 -11
- package/dist/react/mcp-ui/host-message-source.d.ts +14 -2
- package/dist/react/mcp-ui/host-message-source.js +15 -5
- package/dist/react/mcp-ui/mcp-ui-host-frame.d.ts +41 -0
- package/dist/react/mcp-ui/mcp-ui-host-frame.js +39 -0
- package/dist/renderers/annotation-canvas/react/components/AnnotationCanvas.js +97 -69
- package/dist/renderers/preview-modal-shell/react/components/PreviewModalShell.js +59 -30
- package/dist/renderers/preview-modal-shell/react/components/icons.js +4 -1
- package/dist/renderers/react/components/ArtifactView.js +9 -6
- package/dist/renderers/srcdoc/build.d.ts +4 -1
- package/dist/renderers/srcdoc/build.js +62 -48
- package/dist/styles/admin.css +29 -14
- package/dist/styles/lightbox.css +142 -0
- package/dist/styles/variables.css +128 -0
- package/dist/theme/apply.js +9 -5
- package/dist/utils/auto-open-file.d.ts +2 -1
- package/dist/utils/auto-open-file.js +69 -85
- package/dist/utils/markdown-scroll-sync.js +108 -87
- package/dist/utils/polygon-selection.js +25 -26
- package/dist/utils/smooth-scroll-to-top.d.ts +1 -1
- package/dist/utils/smooth-scroll-to-top.js +2 -32
- package/dist/utils/sse.js +26 -26
- package/dist/utils/unit-bezier.d.ts +7 -0
- package/dist/utils/unit-bezier.js +38 -0
- package/package.json +7 -10
- package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.d.ts +0 -13
- package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.js +0 -46
|
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
/** Optional TanStack translation layer. No library types escape the fetch-query contract. */
|
|
3
3
|
import { createContext, useCallback, useContext, useEffect, useMemo, useSyncExternalStore } from 'react';
|
|
4
4
|
import { isCancelledError, replaceEqualDeep, QueryClient, QueryClientProvider, useMutation, useQuery } from '@tanstack/react-query';
|
|
5
|
-
import { DEFAULT_STALE_TIME, FetchQueryCache, IDLE_RETENTION, toQueryError } from '@jini-ai/core/fetch';
|
|
5
|
+
import { DEFAULT_STALE_TIME, FetchQueryCache, IDLE_RETENTION, toQueryError, withCachedLoaderFetch } from '@jini-ai/core/fetch';
|
|
6
6
|
import { browserFetchQueryEnvironment } from './environment.browser.js';
|
|
7
7
|
import { resolveFetchQueryError, resolveFetchQueryStatus } from './adapter.react.js';
|
|
8
8
|
import { FetchQueryBinding } from './binding.react.js';
|
|
@@ -42,12 +42,12 @@ export function FetchQueryProvider({ children, environment = browserFetchQueryEn
|
|
|
42
42
|
}, [scope, environment]);
|
|
43
43
|
return _jsx(QueryClientProvider, { client: scope.client, children: _jsx(ScopeContext.Provider, { value: scope, children: _jsx(FetchQueryBinding, { adapter: adapter, children: children }) }) });
|
|
44
44
|
}
|
|
45
|
-
/**
|
|
45
|
+
/** The provider's scope. This adapter's hooks are not exported: the only route to them is the
|
|
46
|
+
* `adapter` object this module's own `FetchQueryProvider` binds INSIDE its `ScopeContext`, and the
|
|
47
|
+
* public hooks already fail closed without a provider (`binding.react.tsx`'s `useAdapter`). So the
|
|
48
|
+
* old "no scope" throw here was unreachable and is gone; the guarantee is that binding order. */
|
|
46
49
|
function useScope() {
|
|
47
|
-
|
|
48
|
-
if (!scope)
|
|
49
|
-
throw new Error('Fetch-query hooks require FetchQueryProvider');
|
|
50
|
-
return scope;
|
|
50
|
+
return useContext(ScopeContext);
|
|
51
51
|
}
|
|
52
52
|
/** Observe only this provider's connectivity; paused reads do not claim network activity. */
|
|
53
53
|
function useOnline({ environment }) {
|
|
@@ -174,40 +174,53 @@ function useFetchMutation({ run }, { invalidates } = {}) {
|
|
|
174
174
|
}, [mutateAsync]);
|
|
175
175
|
return { mutate, status: mutation.status, error: mutation.status === 'error' ? toQueryError(mutation.error) : null, reset };
|
|
176
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* One loader read of `required.key`, unwrapped to its value. Lifted out of `useCachedLoader`'s `load`
|
|
179
|
+
* verbatim so the missing-fetch guard (`withCachedLoaderFetch`) wrapping it does not push its
|
|
180
|
+
* callbacks past the function-nesting limit.
|
|
181
|
+
*/
|
|
182
|
+
function loadLoaderQuery({ scope, options, required, staleTime }) {
|
|
183
|
+
const selected = { ...options, staleTime, queryKey: required.key, queryFn: () => read({ scope, key: required.key, fetch: required.fetch }),
|
|
184
|
+
structuralSharing: (previous, incoming) => saveResult({ scope, key: required.key, previous, incoming }) };
|
|
185
|
+
return scope.client.fetchQuery(selected).catch(error => {
|
|
186
|
+
// A forced refresh can supersede a loader's request without aborting its transport.
|
|
187
|
+
// Follow the new request instead of exposing TanStack's cancellation vocabulary.
|
|
188
|
+
if (!isCancelledError(error))
|
|
189
|
+
throw error;
|
|
190
|
+
// Invalidation also supersedes loader-only reads. Join (or start) the post-write
|
|
191
|
+
// request rather than returning an obsolete value while the key is invalidated.
|
|
192
|
+
const query = scope.client.getQueryCache().find({ queryKey: required.key, exact: true });
|
|
193
|
+
if (query.state.isInvalidated)
|
|
194
|
+
return scope.client.fetchQuery(selected);
|
|
195
|
+
return (query.promise ?? scope.client.getQueryData(required.key));
|
|
196
|
+
}).then(result => {
|
|
197
|
+
const query = scope.client.getQueryCache().find({ queryKey: required.key, exact: true });
|
|
198
|
+
const history = historyFor({ scope, query });
|
|
199
|
+
// Query.fetch returns the transport envelope, not structuralSharing's accepted value.
|
|
200
|
+
// Unwrap against the current generation so awaiting loaders agree with observers.
|
|
201
|
+
if (result.generation !== history.replacement)
|
|
202
|
+
return scope.client.getQueryData(required.key).value;
|
|
203
|
+
if ('error' in result)
|
|
204
|
+
throw result.error;
|
|
205
|
+
return result.value;
|
|
206
|
+
});
|
|
207
|
+
}
|
|
177
208
|
/** Loader shares reactive reads, retains fresh idle values, and protects replacement writes. */
|
|
178
209
|
function useCachedLoader({ key, fetch }, { staleTime = DEFAULT_STALE_TIME } = {}) {
|
|
179
210
|
const scope = useScope();
|
|
180
211
|
return useMemo(() => {
|
|
181
|
-
|
|
212
|
+
// The template every loader query starts from. It carries no `queryFn`: `load` always supplies
|
|
213
|
+
// its own (for `required.key`), an observer that later mounts on a `replace`-created query
|
|
214
|
+
// installs the observer's, and an unobserved query is never refetched — so a template `queryFn`
|
|
215
|
+
// here could never run. Its `structuralSharing` does run: `replace` on a cold key builds the query
|
|
216
|
+
// from this template and publishes through it.
|
|
217
|
+
const options = { queryKey: key,
|
|
182
218
|
structuralSharing: (previous, incoming) => saveResult({ scope, key, previous, incoming }), staleTime, gcTime: gcTimeFor({ staleTime }) };
|
|
183
219
|
return {
|
|
184
220
|
peek: () => scope.client.getQueryData(key)?.value,
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
return scope.client.fetchQuery(selected).catch(error => {
|
|
189
|
-
// A forced refresh can supersede a loader's request without aborting its transport.
|
|
190
|
-
// Follow the new request instead of exposing TanStack's cancellation vocabulary.
|
|
191
|
-
if (!isCancelledError(error))
|
|
192
|
-
throw error;
|
|
193
|
-
// Invalidation also supersedes loader-only reads. Join (or start) the post-write
|
|
194
|
-
// request rather than returning an obsolete value while the key is invalidated.
|
|
195
|
-
const query = scope.client.getQueryCache().find({ queryKey: required.key, exact: true });
|
|
196
|
-
if (query.state.isInvalidated)
|
|
197
|
-
return scope.client.fetchQuery(selected);
|
|
198
|
-
return (query.promise ?? scope.client.getQueryData(required.key));
|
|
199
|
-
}).then(result => {
|
|
200
|
-
const query = scope.client.getQueryCache().find({ queryKey: required.key, exact: true });
|
|
201
|
-
const history = historyFor({ scope, query });
|
|
202
|
-
// Query.fetch returns the transport envelope, not structuralSharing's accepted value.
|
|
203
|
-
// Unwrap against the current generation so awaiting loaders agree with observers.
|
|
204
|
-
if (result.generation !== history.replacement)
|
|
205
|
-
return scope.client.getQueryData(required.key).value;
|
|
206
|
-
if ('error' in result)
|
|
207
|
-
throw result.error;
|
|
208
|
-
return result.value;
|
|
209
|
-
});
|
|
210
|
-
},
|
|
221
|
+
// `fetch` is the loader's optional default; `withCachedLoaderFetch` rejects an argument-less read
|
|
222
|
+
// that has none before any query is built (see `CachedLoaderDefaults`).
|
|
223
|
+
load: (requested = { key, fetch }, { staleTime: readStaleTime = staleTime } = {}) => withCachedLoaderFetch(requested, required => loadLoaderQuery({ scope, options, required, staleTime: readStaleTime })),
|
|
211
224
|
replace: ({ value }) => {
|
|
212
225
|
const query = scope.client.getQueryCache().build(scope.client, options);
|
|
213
226
|
const history = historyFor({ scope, query });
|
|
@@ -26,8 +26,10 @@ export const LOCAL_BACKUP_PRINCIPAL_ID = "local-tab-backup";
|
|
|
26
26
|
* @complexity O(draft size) for reads/writes; O(1) for clears.
|
|
27
27
|
*/
|
|
28
28
|
export function createStandingDraftLocalBackup({ keyPrefix: KEY_PREFIX, getStorage }, _options = {}) {
|
|
29
|
+
// Never sees a `null` principal: write/read/clear each return before reaching storage for one, so
|
|
30
|
+
// the old `principalId ?? ""` fallback here could not run.
|
|
29
31
|
function keyFor(entryId, principalId) {
|
|
30
|
-
return principalId === undefined ? `${KEY_PREFIX}${entryId}` : `${KEY_PREFIX}${encodeURIComponent(principalId
|
|
32
|
+
return principalId === undefined ? `${KEY_PREFIX}${entryId}` : `${KEY_PREFIX}${encodeURIComponent(principalId)}.${encodeURIComponent(entryId)}`;
|
|
31
33
|
}
|
|
32
34
|
/** Mirrors a refused draft for this browser profile. Silent no-op on any storage failure.
|
|
33
35
|
* @complexity Time/space: O(size of the draft) for the one `JSON.stringify`. */
|
|
@@ -261,7 +261,9 @@ export function useSettingsSlice(options, _optional = {}) {
|
|
|
261
261
|
// left to paint. The write itself still has to happen — through the same
|
|
262
262
|
// lane as the debounce path, so it diffs against the base that is
|
|
263
263
|
// current when it RUNS, not the one that was current when it was queued.
|
|
264
|
-
void writes.run({ task: () => runSave().then(() => { },
|
|
264
|
+
void writes.run({ task: () => runSave().then(() => { },
|
|
265
|
+
// No view remains to show the failure; settings and native errors may contain secrets.
|
|
266
|
+
() => { console.error('[settings] Final save failed after unmount.'); }) });
|
|
265
267
|
}, [runSave, writes]);
|
|
266
268
|
const onChange = useCallback((next) => {
|
|
267
269
|
setValue(next);
|
|
@@ -6,6 +6,27 @@ import { hasMadeConsentDecision, isSharingEnabled, nextStateForDeclineAll, nextS
|
|
|
6
6
|
function ToggleRow({ label, hint, checked, onChange, agentHandle }) {
|
|
7
7
|
return (_jsxs("button", { type: "button", className: `jini-toggle-row${checked ? ' on' : ''}`, onClick: () => onChange(!checked), "aria-pressed": checked, ...agentHandleProps({}, { base: agentHandle, ...({ role: 'checkbox', label }) }), children: [_jsxs("div", { className: "jini-toggle-row-text", children: [_jsx("span", { className: "jini-toggle-row-label", children: label }), _jsx("span", { className: "jini-toggle-row-hint", children: hint })] }), _jsx("span", { className: "jini-toggle-row-switch", "aria-hidden": true })] }));
|
|
8
8
|
}
|
|
9
|
+
function resolveConsentLabels({ labels }, { t }) {
|
|
10
|
+
return {
|
|
11
|
+
consentKicker: labels.consentKicker ?? t('Help improve this product'),
|
|
12
|
+
consentLead: labels.consentLead ?? t('Choose what you share. You can change this anytime.'),
|
|
13
|
+
consentFooter: labels.consentFooter ?? t('Nothing is shared unless you opt in.'),
|
|
14
|
+
declineLabel: labels.declineLabel ?? t("Don't share"),
|
|
15
|
+
shareLabel: labels.shareLabel ?? t('Share usage'),
|
|
16
|
+
metricsLabel: labels.metricsLabel ?? t('Anonymous metrics'),
|
|
17
|
+
metricsHint: labels.metricsHint ?? t('Feature usage and performance data, with no personal content.'),
|
|
18
|
+
contentLabel: labels.contentLabel ?? t('Conversation and tool content'),
|
|
19
|
+
contentHint: labels.contentHint ?? t('Helps improve model quality. Off by default.'),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function resolveInstallationLabels({ labels }, { t }) {
|
|
23
|
+
return {
|
|
24
|
+
installationIdLabel: labels.installationIdLabel ?? t('Installation ID'),
|
|
25
|
+
installationIdHint: labels.installationIdHint ?? t('An anonymous id used to group your shared data.'),
|
|
26
|
+
optedOutLabel: labels.optedOutLabel ?? t('Not sharing'),
|
|
27
|
+
deleteMyDataLabel: labels.deleteMyDataLabel ?? t('Delete my data'),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
9
30
|
/**
|
|
10
31
|
* Telemetry consent card (share/decline + two per-category toggles) over a
|
|
11
32
|
* generic `{ metrics, content }` shape, plus an installation-id
|
|
@@ -19,19 +40,9 @@ export function PrivacyTab({ state, onChange, labels, now = Date.now, agentHandl
|
|
|
19
40
|
const t = useT();
|
|
20
41
|
const decided = hasMadeConsentDecision(state);
|
|
21
42
|
const sharing = decided ? isSharingEnabled(state.telemetry) : undefined;
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
const declineLabel = labels?.declineLabel ?? t("Don't share");
|
|
26
|
-
const shareLabel = labels?.shareLabel ?? t('Share usage');
|
|
27
|
-
const metricsLabel = labels?.metricsLabel ?? t('Anonymous metrics');
|
|
28
|
-
const metricsHint = labels?.metricsHint ?? t('Feature usage and performance data, with no personal content.');
|
|
29
|
-
const contentLabel = labels?.contentLabel ?? t('Conversation and tool content');
|
|
30
|
-
const contentHint = labels?.contentHint ?? t('Helps improve model quality. Off by default.');
|
|
31
|
-
const installationIdLabel = labels?.installationIdLabel ?? t('Installation ID');
|
|
32
|
-
const installationIdHint = labels?.installationIdHint ?? t('An anonymous id used to group your shared data.');
|
|
33
|
-
const optedOutLabel = labels?.optedOutLabel ?? t('Not sharing');
|
|
34
|
-
const deleteMyDataLabel = labels?.deleteMyDataLabel ?? t('Delete my data');
|
|
43
|
+
const labelOverrides = labels ?? {};
|
|
44
|
+
const { consentKicker, consentLead, consentFooter, declineLabel, shareLabel, metricsLabel, metricsHint, contentLabel, contentHint, } = resolveConsentLabels({ labels: labelOverrides }, { t });
|
|
45
|
+
const { installationIdLabel, installationIdHint, optedOutLabel, deleteMyDataLabel, } = resolveInstallationLabels({ labels: labelOverrides }, { t });
|
|
35
46
|
return (_jsxs("section", { className: "jini-settings-section jini-settings-privacy", children: [_jsxs("div", { className: "jini-settings-subsection", children: [_jsx("div", { className: "jini-section-head", children: _jsxs("div", { children: [_jsx("h4", { children: consentKicker }), _jsx("p", { className: "jini-hint", children: consentLead })] }) }), _jsxs("dl", { className: "jini-settings-privacy-disclosure", children: [_jsxs("div", { children: [_jsx("dt", { children: metricsLabel }), _jsx("dd", { children: metricsHint })] }), _jsxs("div", { children: [_jsx("dt", { children: contentLabel }), _jsx("dd", { children: contentHint })] })] }), _jsx("p", { className: "jini-hint", children: consentFooter }), _jsxs("div", { className: "jini-privacy-consent-actions", role: "group", "aria-label": consentKicker, children: [_jsx("button", { type: "button", className: `jini-privacy-consent-action${sharing === false ? ' is-active' : ''}`, "aria-pressed": sharing === false, onClick: () => onChange(nextStateForDeclineAll(state, now())), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'decline', role: 'button', label: declineLabel }) }), children: declineLabel }), _jsx("button", { type: "button", className: `jini-privacy-consent-action jini-privacy-consent-action--primary${sharing === true ? ' is-active' : ''}`, "aria-pressed": sharing === true, onClick: () => onChange(nextStateForShareAll(state, now())), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'share', role: 'button', label: shareLabel }) }), children: shareLabel })] })] }), decided ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "jini-settings-privacy-toggles", children: [_jsx(ToggleRow, { label: metricsLabel, hint: metricsHint, checked: state.telemetry.metrics === true, onChange: (v) => onChange(nextStateForTelemetryPatch(state, { metrics: v }, now())), ...(agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'metrics-toggle' }) } : {}) }), _jsx(ToggleRow, { label: contentLabel, hint: contentHint, checked: state.telemetry.content === true, onChange: (v) => onChange(nextStateForTelemetryPatch(state, { content: v }, now())), ...(agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'content-toggle' }) } : {}) })] }), _jsxs("div", { className: "jini-settings-subsection", children: [_jsx("div", { className: "jini-section-head", children: _jsxs("div", { children: [_jsx("h4", { children: installationIdLabel }), _jsx("p", { className: "jini-hint", children: installationIdHint })] }) }), _jsx("div", { className: "jini-settings-field", children: _jsx("input", { type: "text", readOnly: true, value: state.installationId ?? optedOutLabel, "aria-label": installationIdLabel, ...agentHandleProps({}, { base: agentHandle, ...({ action: 'installation-id', role: 'field', label: installationIdLabel }) }) }) }), _jsxs("button", { type: "button", className: "jini-button jini-button-ghost", onClick: () => onChange(nextStateForDeleteMyData(state, now())), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'delete-my-data', role: 'button', label: deleteMyDataLabel }) }), children: [_jsx(Icon, { name: "trash", size: 13 }), _jsx("span", { children: deleteMyDataLabel })] })] })] })) : null] }));
|
|
36
47
|
}
|
|
37
48
|
//# sourceMappingURL=PrivacyTab.js.map
|
|
@@ -14,11 +14,15 @@ export function ProgressCard({ data, maxSteps = 6, maxSecondaryItems = 5 }) {
|
|
|
14
14
|
const title = data.title ?? t(defaultProgressCardTitle(data.status));
|
|
15
15
|
const detail = data.detail ?? t(defaultProgressCardDetail(data.status));
|
|
16
16
|
const secondaryHeading = t(data.secondaryItemsLabel ?? 'Files touched');
|
|
17
|
-
const widthPercent = progressBarWidthPercent(data.progress);
|
|
18
|
-
const ariaValueNow = progressBarAriaValueNow(data.progress);
|
|
19
17
|
const visibleSteps = data.steps.slice(0, maxSteps);
|
|
20
18
|
const visibleSecondaryItems = (data.secondaryItems ?? []).slice(0, maxSecondaryItems);
|
|
21
|
-
return (_jsxs("section", { className: `progress-card is-${data.status}`, "data-progress-card-id": data.id, children: [_jsxs("div", { className: "progress-card-head", children: [_jsx(Icon, { name: progressCardStatusIcon(data.status) }), _jsxs("span", { children: [_jsx("strong", { children: title }), _jsx("small", { children: detail })] })] }), _jsx(
|
|
19
|
+
return (_jsxs("section", { className: `progress-card is-${data.status}`, "data-progress-card-id": data.id, children: [_jsxs("div", { className: "progress-card-head", children: [_jsx(Icon, { name: progressCardStatusIcon(data.status) }), _jsxs("span", { children: [_jsx("strong", { children: title }), _jsx("small", { children: detail })] })] }), _jsx(ProgressCardBar, { progress: data.progress, label: t('{title} progress', { title }) }), visibleSteps.length > 0 ? _jsx(ProgressCardItemList, { items: visibleSteps, className: "progress-card-steps" }) : null, visibleSecondaryItems.length > 0 ? (_jsxs("div", { className: "progress-card-secondary-items", children: [_jsx("span", { children: secondaryHeading }), _jsx(ProgressCardItemList, { items: visibleSecondaryItems, className: "progress-card-secondary-item-list", asCode: true })] })) : null] }));
|
|
20
|
+
}
|
|
21
|
+
/** Presents the existing progress rules as a determinate or indeterminate accessible bar. */
|
|
22
|
+
function ProgressCardBar({ progress, label }) {
|
|
23
|
+
const widthPercent = progressBarWidthPercent(progress);
|
|
24
|
+
const ariaValueNow = progressBarAriaValueNow(progress);
|
|
25
|
+
return (_jsx("div", { className: `progress-card-bar${widthPercent === null ? ' is-indeterminate' : ''}`, role: "progressbar", "aria-label": label, "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": ariaValueNow, children: _jsx("span", { style: widthPercent === null ? undefined : { width: `${widthPercent}%` } }) }));
|
|
22
26
|
}
|
|
23
27
|
function ProgressCardItemList({ items, className, asCode, }) {
|
|
24
28
|
const Tag = asCode ? 'code' : 'span';
|
|
@@ -29,17 +29,13 @@ export function parseTodoWriteInput(input) {
|
|
|
29
29
|
if (!todo || typeof todo !== 'object')
|
|
30
30
|
return null;
|
|
31
31
|
const todoRecord = todo;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
? todoRecord.label
|
|
40
|
-
: typeof todoRecord.text === 'string'
|
|
41
|
-
? todoRecord.text
|
|
42
|
-
: '';
|
|
32
|
+
let content = '';
|
|
33
|
+
for (const key of ['content', 'step', 'description', 'label', 'text']) {
|
|
34
|
+
if (typeof todoRecord[key] === 'string') {
|
|
35
|
+
content = todoRecord[key];
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
43
39
|
if (!content)
|
|
44
40
|
return null;
|
|
45
41
|
return { content, status: normalizeTodoStatus(todoRecord.status) };
|
|
@@ -118,48 +114,55 @@ export function dedupeToolUsesById(events) {
|
|
|
118
114
|
let deduped = null;
|
|
119
115
|
for (let index = 0; index < events.length; index += 1) {
|
|
120
116
|
const event = events[index];
|
|
121
|
-
if (isToolUseEvent(event)) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
117
|
+
if (!isToolUseEvent(event)) {
|
|
118
|
+
deduped?.push(event);
|
|
119
|
+
}
|
|
120
|
+
else if (seen.has(event.id)) {
|
|
121
|
+
deduped ??= events.slice(0, index);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
127
124
|
seen.add(event.id);
|
|
125
|
+
deduped?.push(event);
|
|
128
126
|
}
|
|
129
|
-
if (deduped)
|
|
130
|
-
deduped.push(event);
|
|
131
127
|
}
|
|
132
128
|
return deduped ?? events;
|
|
133
129
|
}
|
|
130
|
+
function fileOpFromAgentEvent({ event, resultByToolUseId }, _options = {}) {
|
|
131
|
+
if (!isToolUseEvent(event))
|
|
132
|
+
return null;
|
|
133
|
+
const kind = classifyFileOp(event.name);
|
|
134
|
+
if (!kind)
|
|
135
|
+
return null;
|
|
136
|
+
const fullPath = extractFileOpPath(event.input);
|
|
137
|
+
if (!fullPath)
|
|
138
|
+
return null;
|
|
139
|
+
const result = resultByToolUseId.get(event.id);
|
|
140
|
+
const status = result === undefined ? 'running' : result.isError ? 'error' : 'done';
|
|
141
|
+
return { fullPath, kind, status };
|
|
142
|
+
}
|
|
134
143
|
export function deriveFileOpsFromAgentEvents(events) {
|
|
135
144
|
if (!events || events.length === 0)
|
|
136
145
|
return [];
|
|
137
146
|
const dedupedEvents = dedupeToolUsesById(events);
|
|
138
|
-
const resultByToolUseId = new Map();
|
|
139
|
-
for (const event of dedupedEvents) {
|
|
140
|
-
if (isToolResultEvent(event))
|
|
141
|
-
resultByToolUseId.set(event.toolUseId, event);
|
|
142
|
-
}
|
|
147
|
+
const resultByToolUseId = new Map(dedupedEvents.filter(isToolResultEvent).map((event) => [event.toolUseId, event]));
|
|
143
148
|
const byPath = new Map();
|
|
144
149
|
for (const event of dedupedEvents) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const kind = classifyFileOp(event.name);
|
|
148
|
-
if (!kind)
|
|
149
|
-
continue;
|
|
150
|
-
const fullPath = extractFileOpPath(event.input);
|
|
151
|
-
if (!fullPath)
|
|
150
|
+
const entry = fileOpFromAgentEvent({ event, resultByToolUseId });
|
|
151
|
+
if (!entry)
|
|
152
152
|
continue;
|
|
153
|
-
const
|
|
154
|
-
const status = result === undefined ? 'running' : result.isError ? 'error' : 'done';
|
|
155
|
-
const existing = byPath.get(fullPath);
|
|
153
|
+
const existing = byPath.get(entry.fullPath);
|
|
156
154
|
if (existing) {
|
|
157
|
-
if (!existing.ops.includes(kind))
|
|
158
|
-
existing.ops.push(kind);
|
|
159
|
-
existing.status = mergeFileOpStatus(existing.status, status);
|
|
155
|
+
if (!existing.ops.includes(entry.kind))
|
|
156
|
+
existing.ops.push(entry.kind);
|
|
157
|
+
existing.status = mergeFileOpStatus(existing.status, entry.status);
|
|
160
158
|
continue;
|
|
161
159
|
}
|
|
162
|
-
byPath.set(fullPath, {
|
|
160
|
+
byPath.set(entry.fullPath, {
|
|
161
|
+
path: basename(entry.fullPath),
|
|
162
|
+
fullPath: entry.fullPath,
|
|
163
|
+
ops: [entry.kind],
|
|
164
|
+
status: entry.status,
|
|
165
|
+
});
|
|
163
166
|
}
|
|
164
167
|
return Array.from(byPath.values());
|
|
165
168
|
}
|
|
@@ -204,15 +207,7 @@ function fallbackChatActivitySteps(status, fileOps) {
|
|
|
204
207
|
{
|
|
205
208
|
id: 'update-files',
|
|
206
209
|
label: 'Update files',
|
|
207
|
-
status: hasError
|
|
208
|
-
? 'failed'
|
|
209
|
-
: hasMutation
|
|
210
|
-
? fileOps.some((entry) => entry.status === 'running')
|
|
211
|
-
? 'running'
|
|
212
|
-
: 'succeeded'
|
|
213
|
-
: status === 'running'
|
|
214
|
-
? 'pending'
|
|
215
|
-
: 'succeeded',
|
|
210
|
+
status: fallbackUpdateStatus({ status, fileOps, hasError, hasMutation }),
|
|
216
211
|
},
|
|
217
212
|
{
|
|
218
213
|
id: 'finalize',
|
|
@@ -221,6 +216,22 @@ function fallbackChatActivitySteps(status, fileOps) {
|
|
|
221
216
|
},
|
|
222
217
|
];
|
|
223
218
|
}
|
|
219
|
+
function fallbackUpdateStatus({ status, fileOps, hasError, hasMutation }, _options = {}) {
|
|
220
|
+
if (hasError)
|
|
221
|
+
return 'failed';
|
|
222
|
+
if (hasMutation)
|
|
223
|
+
return fileOps.some((entry) => entry.status === 'running') ? 'running' : 'succeeded';
|
|
224
|
+
return status === 'running' ? 'pending' : 'succeeded';
|
|
225
|
+
}
|
|
226
|
+
function chatActivitySteps({ todos, status, fileOps }, _options = {}) {
|
|
227
|
+
if (todos.length === 0)
|
|
228
|
+
return fallbackChatActivitySteps(status, fileOps);
|
|
229
|
+
return todos.map((todo, index) => ({
|
|
230
|
+
id: `${todo.content}-${index}`,
|
|
231
|
+
label: todo.content,
|
|
232
|
+
status: todoStatusToProgressStatus(todo.status),
|
|
233
|
+
}));
|
|
234
|
+
}
|
|
224
235
|
/** Maps a chat message's agent-event stream onto `ProgressCardData`. Returns
|
|
225
236
|
* `null` when there's nothing worth showing (mirrors the source's own
|
|
226
237
|
* `hasActivity` early-return — a message with no run, no todos, no file
|
|
@@ -234,13 +245,7 @@ export function chatActivityToProgressCard(message, options) {
|
|
|
234
245
|
const hasActivity = options.active || todos.length > 0 || fileOps.length > 0 || statusDetail !== null || status === 'failed';
|
|
235
246
|
if (!hasActivity)
|
|
236
247
|
return null;
|
|
237
|
-
const steps = todos
|
|
238
|
-
? todos.map((todo, index) => ({
|
|
239
|
-
id: `${todo.content}-${index}`,
|
|
240
|
-
label: todo.content,
|
|
241
|
-
status: todoStatusToProgressStatus(todo.status),
|
|
242
|
-
}))
|
|
243
|
-
: fallbackChatActivitySteps(status, fileOps);
|
|
248
|
+
const steps = chatActivitySteps({ todos, status, fileOps });
|
|
244
249
|
const secondaryItems = fileOps.map((entry) => ({
|
|
245
250
|
id: entry.fullPath,
|
|
246
251
|
label: entry.path,
|
|
@@ -3,6 +3,29 @@ import { useT } from '../../../i18n/index.js';
|
|
|
3
3
|
import { Icon } from '../../../../react/components/Icon.js';
|
|
4
4
|
import { locationLabel } from '../../rules.js';
|
|
5
5
|
import { useProjectLocationsTab } from '../hooks/useProjectLocationsTab.js';
|
|
6
|
+
function resolveLabels({ t }, labels = {}) {
|
|
7
|
+
const copy = {
|
|
8
|
+
title: 'Project locations',
|
|
9
|
+
description: 'Where new projects are created and existing ones are found.',
|
|
10
|
+
builtInLabel: 'Default location',
|
|
11
|
+
defaultBadge: 'Default',
|
|
12
|
+
makeDefaultLabel: 'Make default',
|
|
13
|
+
deleteLabel: 'Delete',
|
|
14
|
+
addFolderLabel: 'Add folder',
|
|
15
|
+
rowHint: 'Used as a project root.',
|
|
16
|
+
noFolderSelectedLabel: 'No folder selected.',
|
|
17
|
+
duplicateLabel: 'That folder is already a project location.',
|
|
18
|
+
savedLabel: 'Saved.',
|
|
19
|
+
defaultSavedLabel: 'Default location updated.',
|
|
20
|
+
saveErrorLabel: 'Could not save project locations.',
|
|
21
|
+
scanErrorLabel: 'Could not scan the new folder for existing projects.',
|
|
22
|
+
scanCompleteTemplate: 'Imported {imported}, {existing} already tracked.',
|
|
23
|
+
};
|
|
24
|
+
for (const key of Object.keys(copy)) {
|
|
25
|
+
copy[key] = labels?.[key] ?? t(copy[key]);
|
|
26
|
+
}
|
|
27
|
+
return copy;
|
|
28
|
+
}
|
|
6
29
|
/**
|
|
7
30
|
* Configured project roots: the host's built-in default plus operator-added
|
|
8
31
|
* folders, each markable as the new-project default and removable. Origin:
|
|
@@ -13,21 +36,7 @@ import { useProjectLocationsTab } from '../hooks/useProjectLocationsTab.js';
|
|
|
13
36
|
export function ProjectLocationsTab({ port, defaultLocationId, onDefaultLocationIdChange, labels }) {
|
|
14
37
|
const t = useT();
|
|
15
38
|
const { drafts, builtIn, effectiveDefaultLocationId, saving, result, addFolder, removeDraft, setDefaultLocationId } = useProjectLocationsTab({ port, defaultLocationId, onDefaultLocationIdChange });
|
|
16
|
-
const title
|
|
17
|
-
const description = labels?.description ?? t('Where new projects are created and existing ones are found.');
|
|
18
|
-
const builtInLabel = labels?.builtInLabel ?? t('Default location');
|
|
19
|
-
const defaultBadge = labels?.defaultBadge ?? t('Default');
|
|
20
|
-
const makeDefaultLabel = labels?.makeDefaultLabel ?? t('Make default');
|
|
21
|
-
const deleteLabel = labels?.deleteLabel ?? t('Delete');
|
|
22
|
-
const addFolderLabel = labels?.addFolderLabel ?? t('Add folder');
|
|
23
|
-
const rowHint = labels?.rowHint ?? t('Used as a project root.');
|
|
24
|
-
const noFolderSelectedLabel = labels?.noFolderSelectedLabel ?? t('No folder selected.');
|
|
25
|
-
const duplicateLabel = labels?.duplicateLabel ?? t('That folder is already a project location.');
|
|
26
|
-
const savedLabel = labels?.savedLabel ?? t('Saved.');
|
|
27
|
-
const defaultSavedLabel = labels?.defaultSavedLabel ?? t('Default location updated.');
|
|
28
|
-
const saveErrorLabel = labels?.saveErrorLabel ?? t('Could not save project locations.');
|
|
29
|
-
const scanErrorLabel = labels?.scanErrorLabel ?? t('Could not scan the new folder for existing projects.');
|
|
30
|
-
const scanCompleteTemplate = labels?.scanCompleteTemplate ?? t('Imported {imported}, {existing} already tracked.');
|
|
39
|
+
const { title, description, builtInLabel, defaultBadge, makeDefaultLabel, deleteLabel, addFolderLabel, rowHint, noFolderSelectedLabel, duplicateLabel, savedLabel, defaultSavedLabel, saveErrorLabel, scanErrorLabel, scanCompleteTemplate, } = resolveLabels({ t }, labels);
|
|
31
40
|
function defaultControlLabel(active) {
|
|
32
41
|
return active ? defaultBadge : makeDefaultLabel;
|
|
33
42
|
}
|
|
@@ -26,6 +26,14 @@ export interface UseProjectLocationsTabResult {
|
|
|
26
26
|
removeDraft: (index: number) => void;
|
|
27
27
|
setDefaultLocationId: (id: string) => void;
|
|
28
28
|
}
|
|
29
|
+
/** Scan a saved location; disposed callers cannot publish success or failure. */
|
|
30
|
+
export declare function scanAddedLocation({ port, alive, setResult }: {
|
|
31
|
+
port: ProjectLocationsPort;
|
|
32
|
+
alive: {
|
|
33
|
+
readonly current: boolean;
|
|
34
|
+
};
|
|
35
|
+
setResult: (result: ProjectLocationsActionResult) => void;
|
|
36
|
+
}, _options?: {}): Promise<void>;
|
|
29
37
|
/**
|
|
30
38
|
* Owns the tab's async edges: the initial locations fetch, the add-folder ->
|
|
31
39
|
* duplicate-check -> save -> (optional) scan sequence, and removal. Origin:
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { DEFAULT_LOCATION_ID, externalLocations, isDuplicatePath, resolveDefaultLocationId, saveableDrafts, } from '../../rules.js';
|
|
3
|
+
/** Scan a saved location; disposed callers cannot publish success or failure. */
|
|
4
|
+
export async function scanAddedLocation({ port, alive, setResult }, _options = {}) {
|
|
5
|
+
const scan = port.scanLocations;
|
|
6
|
+
if (!scan)
|
|
7
|
+
return;
|
|
8
|
+
try {
|
|
9
|
+
const scanResult = await scan.call(port);
|
|
10
|
+
if (!alive.current)
|
|
11
|
+
return;
|
|
12
|
+
setResult({ status: 'scan-complete', imported: scanResult.imported.length, existing: scanResult.existing.length });
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
if (!alive.current)
|
|
16
|
+
return;
|
|
17
|
+
setResult({ status: 'scan-error', message: error instanceof Error ? error.message : String(error) });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
3
20
|
/**
|
|
4
21
|
* Owns the tab's async edges: the initial locations fetch, the add-folder ->
|
|
5
22
|
* duplicate-check -> save -> (optional) scan sequence, and removal. Origin:
|
|
@@ -56,11 +73,7 @@ export function useProjectLocationsTab({ port, defaultLocationId, onDefaultLocat
|
|
|
56
73
|
};
|
|
57
74
|
}, [port]);
|
|
58
75
|
const persist = useCallback(async (nextDrafts) => {
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
const saved = await portRef.current.saveLocations(saveableDrafts(nextDrafts));
|
|
62
|
-
if (!alive.current)
|
|
63
|
-
return null;
|
|
76
|
+
const applySaved = (saved) => {
|
|
64
77
|
setLocations(saved);
|
|
65
78
|
setDrafts(externalLocations(saved));
|
|
66
79
|
setResult({ status: 'saved' });
|
|
@@ -72,17 +85,25 @@ export function useProjectLocationsTab({ port, defaultLocationId, onDefaultLocat
|
|
|
72
85
|
if (effective !== configured)
|
|
73
86
|
onDefaultLocationIdChangeRef.current?.(effective);
|
|
74
87
|
return saved;
|
|
88
|
+
};
|
|
89
|
+
setSaving(true);
|
|
90
|
+
// One return after the try statement (null unless a still-mounted save succeeded), so no
|
|
91
|
+
// unreachable continuation follows a try and a catch that both return.
|
|
92
|
+
let persisted = null;
|
|
93
|
+
try {
|
|
94
|
+
const saved = await portRef.current.saveLocations(saveableDrafts(nextDrafts));
|
|
95
|
+
if (alive.current)
|
|
96
|
+
persisted = applySaved(saved);
|
|
75
97
|
}
|
|
76
98
|
catch (error) {
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
setResult({ status: 'save-error', message: error instanceof Error ? error.message : String(error) });
|
|
80
|
-
return null;
|
|
99
|
+
if (alive.current)
|
|
100
|
+
setResult({ status: 'save-error', message: error instanceof Error ? error.message : String(error) });
|
|
81
101
|
}
|
|
82
102
|
finally {
|
|
83
103
|
if (alive.current)
|
|
84
104
|
setSaving(false);
|
|
85
105
|
}
|
|
106
|
+
return persisted;
|
|
86
107
|
}, []);
|
|
87
108
|
const addFolder = useCallback(() => {
|
|
88
109
|
void (async () => {
|
|
@@ -111,20 +132,7 @@ export function useProjectLocationsTab({ port, defaultLocationId, onDefaultLocat
|
|
|
111
132
|
setDrafts(previous);
|
|
112
133
|
return;
|
|
113
134
|
}
|
|
114
|
-
|
|
115
|
-
if (!scan)
|
|
116
|
-
return;
|
|
117
|
-
try {
|
|
118
|
-
const scanResult = await scan.call(portRef.current);
|
|
119
|
-
if (!alive.current)
|
|
120
|
-
return;
|
|
121
|
-
setResult({ status: 'scan-complete', imported: scanResult.imported.length, existing: scanResult.existing.length });
|
|
122
|
-
}
|
|
123
|
-
catch (error) {
|
|
124
|
-
if (!alive.current)
|
|
125
|
-
return;
|
|
126
|
-
setResult({ status: 'scan-error', message: error instanceof Error ? error.message : String(error) });
|
|
127
|
-
}
|
|
135
|
+
await scanAddedLocation({ port: portRef.current, alive, setResult });
|
|
128
136
|
})();
|
|
129
137
|
}, [persist]);
|
|
130
138
|
const removeDraft = useCallback((index) => {
|
|
@@ -2,7 +2,8 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useT } from '../../../i18n/index.js';
|
|
3
3
|
import { useWiredResourceBoard } from '../hooks/useResourceBoard.js';
|
|
4
4
|
import { ResourceBoardView } from './ResourceBoardView.js';
|
|
5
|
-
import {
|
|
5
|
+
import { boardCreateProps, buildKanbanColumns, createItemActionHandler, resourceBoardHookParams, statusLabelFor } from './resourceBoardProps.js';
|
|
6
|
+
import { translatedErrorLabelProps } from './translatedErrorLabelProps.js';
|
|
6
7
|
/**
|
|
7
8
|
* The DesignsTab-shaped orchestrator: wires `useWiredResourceBoard` +
|
|
8
9
|
* `useT()` + `ResourceBoardView`. `rename`/`duplicate`/`delete` are the
|
|
@@ -14,51 +15,9 @@ import { UNMATCHED_STATUS_BUCKET } from '../../constants.js';
|
|
|
14
15
|
*/
|
|
15
16
|
export function ResourceBoard({ dependencies, statusOptions, toneMap, defaultStatus, normalizeStatus, sortOptions = [], initialSort, storageScopeKey, defaultViewMode, enableBulkDelete = true, onOpenItem, onCreate, createLabel, onRenameRequest, onCustomItemAction, refreshToken, renderBody, useWiredResourceBoard: useWiredResourceBoardHook = useWiredResourceBoard, }) {
|
|
16
17
|
const t = useT();
|
|
17
|
-
const board = useWiredResourceBoardHook({
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
...(normalizeStatus !== undefined ? { normalizeStatus } : {}),
|
|
22
|
-
...(initialSort !== undefined ? { initialSort } : {}),
|
|
23
|
-
...(storageScopeKey !== undefined ? { storageScopeKey } : {}),
|
|
24
|
-
...(defaultViewMode !== undefined ? { defaultViewMode } : {}),
|
|
25
|
-
...(refreshToken !== undefined ? { refreshToken } : {}),
|
|
26
|
-
});
|
|
27
|
-
const kanbanColumns = statusOptions.map((option) => ({
|
|
28
|
-
status: option.value,
|
|
29
|
-
label: t(option.label),
|
|
30
|
-
// Non-null: `useWiredResourceBoard` above is called with `statusOrder:
|
|
31
|
-
// statusOptions.map(option => option.value)`, and `groupItemsByStatus`
|
|
32
|
-
// (rules.ts) always seeds one Map entry per `statusOrder` value before
|
|
33
|
-
// placing any item — so every `option.value` here is guaranteed present.
|
|
34
|
-
items: board.kanbanColumns.get(option.value),
|
|
35
|
-
}));
|
|
36
|
-
const unmatched = board.kanbanColumns.get(UNMATCHED_STATUS_BUCKET);
|
|
37
|
-
if (unmatched && unmatched.length > 0) {
|
|
38
|
-
kanbanColumns.push({ status: UNMATCHED_STATUS_BUCKET, label: t('Other'), items: unmatched });
|
|
39
|
-
}
|
|
40
|
-
const handleItemAction = (id, kind) => {
|
|
41
|
-
if (kind === 'delete') {
|
|
42
|
-
void board.remove(id);
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
if (kind === 'duplicate') {
|
|
46
|
-
void board.duplicate(id);
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
if (kind === 'rename') {
|
|
50
|
-
board.closeMenu();
|
|
51
|
-
// Non-null: `id` only ever reaches this handler via a menu-action
|
|
52
|
-
// click closure `ResourceBoardView` builds per rendered item (from
|
|
53
|
-
// this SAME render's `board.visibleItems`), so the item is always
|
|
54
|
-
// still present in the array at lookup time.
|
|
55
|
-
const current = board.visibleItems.find((item) => item.id === id);
|
|
56
|
-
onRenameRequest?.(id, current.title);
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
board.closeMenu();
|
|
60
|
-
onCustomItemAction?.(id, kind);
|
|
61
|
-
};
|
|
62
|
-
return (_jsx(ResourceBoardView, { loading: board.loading, error: board.error, ...(board.error ? { errorLabel: t(board.error) } : {}), actionError: board.actionError, ...(board.actionError ? { actionErrorLabel: t(board.actionError) } : {}), hasAnyItems: board.totalCount > 0, items: board.visibleItems, kanbanColumns: kanbanColumns, sortOptions: sortOptions, ...(board.sort !== undefined ? { activeSort: board.sort } : {}), onSortChange: board.setSort, sortAriaLabel: t('Sort'), query: board.query, onQueryChange: board.setQuery, searchPlaceholder: t('Search…'), viewMode: board.viewMode, onViewModeChange: board.setViewMode, viewToggleAriaLabel: t('View'), gridViewLabel: t('Grid view'), kanbanViewLabel: t('Kanban view'), enableBulkDelete: enableBulkDelete, selectMode: board.selectMode, selected: board.selected, bulkDeleteBusy: board.bulkDeleteBusy, onEnterSelectMode: board.enterSelectMode, onExitSelectMode: board.exitSelectMode, onBulkDelete: () => void board.bulkDelete(), selectModeLabel: t('Select'), selectedCountLabel: (n) => t('{n} selected', { n }), deleteSelectedLabel: t('Delete selected'), cancelSelectLabel: t('Cancel'), ...(createLabel !== undefined || onCreate ? { createLabel: createLabel ?? t('New') } : {}), ...(onCreate ? { onCreate } : {}), openMenuId: board.openMenuId, menuContainerRef: board.menuContainerRef, onToggleMenu: board.toggleMenu, onItemAction: handleItemAction, isItemBusy: board.isItemBusy, onOpenItem: onOpenItem, onToggleSelected: board.toggleSelected, onKanbanDelete: (id) => void board.remove(id), statusLabel: (status) => t(statusOptions.find((option) => option.value === status)?.label ?? status), ...(toneMap ? { toneMap } : {}), menuActionLabel: (_kind, fallback) => t(fallback), moreLabel: t('More'), deleteLabel: t('Delete'), deleteAriaLabel: (title) => t('Delete {name}', { name: title }), emptyColumnLabel: t('No items in this column.'), emptyStateTitle: t('No items yet.'), emptyStateNoMatch: t('No items match your search.'), ...(renderBody ? { renderBody } : {}) }));
|
|
18
|
+
const board = useWiredResourceBoardHook(resourceBoardHookParams({ dependencies, statusOptions }, { defaultStatus, normalizeStatus, initialSort, storageScopeKey, defaultViewMode, refreshToken }));
|
|
19
|
+
const kanbanColumns = buildKanbanColumns({ statusOptions, columns: board.kanbanColumns, t });
|
|
20
|
+
const handleItemAction = createItemActionHandler({ board }, { onRenameRequest, onCustomItemAction });
|
|
21
|
+
return (_jsx(ResourceBoardView, { loading: board.loading, error: board.error, actionError: board.actionError, ...translatedErrorLabelProps({ t, error: board.error, actionError: board.actionError }), hasAnyItems: board.totalCount > 0, items: board.visibleItems, kanbanColumns: kanbanColumns, sortOptions: sortOptions, ...(board.sort !== undefined ? { activeSort: board.sort } : {}), onSortChange: board.setSort, sortAriaLabel: t('Sort'), query: board.query, onQueryChange: board.setQuery, searchPlaceholder: t('Search…'), viewMode: board.viewMode, onViewModeChange: board.setViewMode, viewToggleAriaLabel: t('View'), gridViewLabel: t('Grid view'), kanbanViewLabel: t('Kanban view'), enableBulkDelete: enableBulkDelete, selectMode: board.selectMode, selected: board.selected, bulkDeleteBusy: board.bulkDeleteBusy, onEnterSelectMode: board.enterSelectMode, onExitSelectMode: board.exitSelectMode, onBulkDelete: () => void board.bulkDelete(), selectModeLabel: t('Select'), selectedCountLabel: (n) => t('{n} selected', { n }), deleteSelectedLabel: t('Delete selected'), cancelSelectLabel: t('Cancel'), ...boardCreateProps({ t }, { createLabel, onCreate }), openMenuId: board.openMenuId, menuContainerRef: board.menuContainerRef, onToggleMenu: board.toggleMenu, onItemAction: handleItemAction, isItemBusy: board.isItemBusy, onOpenItem: onOpenItem, onToggleSelected: board.toggleSelected, onKanbanDelete: (id) => void board.remove(id), statusLabel: statusLabelFor({ t, statusOptions }), ...(toneMap ? { toneMap } : {}), menuActionLabel: (_kind, fallback) => t(fallback), moreLabel: t('More'), deleteLabel: t('Delete'), deleteAriaLabel: (title) => t('Delete {name}', { name: title }), emptyColumnLabel: t('No items in this column.'), emptyStateTitle: t('No items yet.'), emptyStateNoMatch: t('No items match your search.'), ...(renderBody ? { renderBody } : {}) }));
|
|
63
22
|
}
|
|
64
23
|
//# sourceMappingURL=ResourceBoard.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReactNode, RefObject } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import type { ResourceKanbanColumn } from './ResourceKanbanBoard.js';
|
|
3
3
|
import type { ResourceBoardItem, ResourceBoardViewMode, ResourceSortOption, ResourceStatusToneMap } from '../../types.js';
|
|
4
4
|
export interface ResourceBoardViewProps<TBody = unknown> {
|
|
5
5
|
loading: boolean;
|
|
@@ -61,5 +61,5 @@ export interface ResourceBoardViewProps<TBody = unknown> {
|
|
|
61
61
|
* state, the card grid, or the kanban board — DesignsTab's own
|
|
62
62
|
* `filtered.length === 0 ? empty : view === "grid" ? grid : kanban` branch.
|
|
63
63
|
*/
|
|
64
|
-
export declare function ResourceBoardView<TBody = unknown>(
|
|
64
|
+
export declare function ResourceBoardView<TBody = unknown>(props: ResourceBoardViewProps<TBody>): import("react").JSX.Element;
|
|
65
65
|
//# sourceMappingURL=ResourceBoardView.d.ts.map
|