@jini-ai/ui 0.6.15-rc.2 → 0.6.15
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/dist/browser/useDismissOnOutsideOrEscape.js +1 -1
- package/dist/browser/useGlobalKeydown.js +2 -2
- package/dist/browser/useModalWindowDragGuard.js +1 -1
- package/dist/core/theme/css-name.d.ts +3 -0
- package/dist/core/theme/css-name.js +5 -0
- package/dist/core/theme/stylesheet.js +2 -1
- package/dist/core/theme/validation.js +15 -4
- package/dist/features/about/rules.js +2 -1
- package/dist/features/admin-widgets/components/InfoTip.hooks.js +22 -12
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.js +1 -1
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.js +1 -1
- package/dist/features/admin-widgets/components/SeeMore/SeeMore.hooks.js +2 -2
- package/dist/features/admin-widgets/components/SeeMore/SeeMore.js +4 -2
- package/dist/features/admin-widgets/components/Select/Select.hooks.js +2 -1
- package/dist/features/admin-widgets/components/Select/Select.js +2 -1
- package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.js +4 -2
- package/dist/features/admin-widgets/components/useDebouncedHtmlPreview.hooks.js +3 -2
- package/dist/features/asset-grid/react/components/AssetGrid.js +8 -3
- package/dist/features/asset-grid/react/hooks/useAssetGridKeyboardShortcuts.js +7 -6
- package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.js +8 -10
- package/dist/features/asset-grid/react/hooks/useRubberBandDrag.js +1 -1
- package/dist/features/asset-tree-browser/react/components/AssetTreeFileRow.js +6 -8
- package/dist/features/asset-tree-browser/react/hooks/useAssetTreeRowMenu.js +1 -1
- package/dist/features/asset-tree-browser/rules.js +1 -3
- package/dist/features/browser-chrome/rules.js +10 -8
- package/dist/features/charts/browser.js +2 -1
- package/dist/features/charts/react.js +3 -1
- package/dist/features/connectors/ConnectorsBrowser.js +3 -1
- package/dist/features/connectors/hooks/useConnectorAuthorization.js +1 -1
- package/dist/features/connectors/hooks/useConnectorCatalog.js +2 -2
- package/dist/features/connectors/rules.js +10 -14
- package/dist/features/execution/react/components/AgentDiagnosticRow.js +2 -0
- package/dist/features/execution/react/components/ByokProviderForm.js +1 -3
- package/dist/features/execution/react/components/LocalCliAgentSettings.js +2 -1
- package/dist/features/execution/react/hooks/use-byok-field-validation.hooks.js +6 -2
- package/dist/features/execution/react/hooks/useExecutionTab.js +9 -5
- package/dist/features/execution/react/hooks/useLocalCliAgentCard.js +3 -6
- package/dist/features/execution/react/hooks/useReasoningControl.js +3 -3
- package/dist/features/execution/rules.js +23 -18
- package/dist/features/external-mcp/react/components/ExternalMcpTab.js +10 -1
- package/dist/features/file-dropzone/rules.js +3 -2
- package/dist/features/html-editor/canvas-embed-placeholders.js +2 -3
- package/dist/features/html-editor/css.js +7 -5
- package/dist/features/html-editor/editor-blocks.js +60 -6
- package/dist/features/html-editor/protected-elements.js +5 -3
- package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.js +3 -3
- package/dist/features/html-editor/source-splice.js +6 -4
- package/dist/features/iframe-pool/react/components/IframeKeepAliveProvider.js +5 -5
- package/dist/features/iframe-pool/react/dom-sync.js +2 -2
- package/dist/features/integrations/react/components/IntegrationsTab.js +3 -1
- package/dist/features/integrations/rules.js +6 -4
- package/dist/features/interactive-ui/providers/shadcn/data-table.js +10 -5
- package/dist/features/interactive-ui/table-data.js +1 -1
- package/dist/features/lexical-rich-text-editor/deserialize.js +2 -2
- package/dist/features/lexical-rich-text-editor/mention-parser.js +4 -4
- package/dist/features/lexical-rich-text-editor/react/components/RichTextInput.js +2 -1
- package/dist/features/lexical-rich-text-editor/react/hooks/useCaretFloatingLayerPosition.js +1 -1
- package/dist/features/lexical-rich-text-editor/react/hooks/useMentionColorStamping.js +1 -1
- package/dist/features/lexical-rich-text-editor/serialize.js +13 -10
- package/dist/features/list-detail-panel/react/components/ListDetailPanel.js +1 -1
- package/dist/features/mcp-ui/confirmation-store.js +3 -6
- package/dist/features/mcp-ui/surfaces/confirmation.d.ts +5 -0
- package/dist/features/mcp-ui/surfaces/confirmation.js +1 -0
- package/dist/features/mcp-ui/surfaces/document.d.ts +3 -15
- package/dist/features/mcp-ui/surfaces/document.js +37 -6
- package/dist/features/mcp-ui/surfaces/fields.js +2 -0
- package/dist/features/mcp-ui/surfaces/host-theme.js +2 -1
- package/dist/features/mcp-ui/surfaces/index.d.ts +1 -0
- package/dist/features/mcp-ui/surfaces/index.js +1 -0
- package/dist/features/mcp-ui/surfaces/outcome.js +1 -1
- package/dist/features/mcp-ui/surfaces/select.js +2 -1
- package/dist/features/mcp-ui/surfaces/structured-details.d.ts +40 -0
- package/dist/features/mcp-ui/surfaces/structured-details.js +159 -0
- package/dist/features/mcp-ui/surfaces/text-input.js +1 -1
- package/dist/features/mcp-ui/surfaces/tool-approval.d.ts +21 -0
- package/dist/features/mcp-ui/surfaces/tool-approval.js +41 -0
- package/dist/features/media-providers/react/hooks/useMediaProvidersTab.js +2 -1
- package/dist/features/memory/async-commit-guard.js +4 -1
- package/dist/features/memory/formatters.js +5 -1
- package/dist/features/memory/react/components/MemoryConnectedPanel.js +3 -1
- package/dist/features/memory/react/components/MemoryEntryCard.js +1 -1
- package/dist/features/memory/react/components/MemorySettingsPanel.js +3 -1
- package/dist/features/memory/react/hooks/useMemoryConfig.hooks.js +12 -10
- package/dist/features/memory/react/hooks/useMemoryConnectors.hooks.js +2 -5
- package/dist/features/memory/react/hooks/useMemoryConnectors.rules.js +10 -9
- package/dist/features/memory/react/hooks/useMemoryEntries.hooks.js +4 -2
- package/dist/features/memory/react/hooks/useMemoryFlash.hooks.js +1 -1
- package/dist/features/memory/rules.js +2 -1
- package/dist/features/panel-kit/fetch-query/adapter.react.js +3 -2
- package/dist/features/panel-kit/fetch-query/adapter.tanstack.js +3 -2
- package/dist/features/panel-kit/fetch-query/binding.react.js +2 -1
- package/dist/features/panel-kit/helpers/standing-draft-recovery-message.js +13 -4
- package/dist/features/panel-kit/hooks/use-dirty-guard.hooks.js +1 -1
- package/dist/features/panel-kit/hooks/use-focus-trap.hooks.js +1 -1
- package/dist/features/panel-kit/hooks/use-settings-slice.hooks.js +14 -10
- package/dist/features/panel-kit/hooks/use-standing-draft-autosave.hooks.js +2 -2
- package/dist/features/progress-card/components/ProgressCard.d.ts +1 -1
- package/dist/features/progress-card/reference-adapters.js +23 -13
- package/dist/features/progress-card/rules.d.ts +0 -1
- package/dist/features/progress-card/rules.js +14 -20
- package/dist/features/progress-card/types.d.ts +1 -1
- package/dist/features/project-locations/dependencies.js +6 -1
- package/dist/features/project-locations/react/components/ProjectLocationsTab.js +2 -1
- package/dist/features/project-locations/rules.js +2 -1
- package/dist/features/resource-dashboard/react/components/ResourceBoardToolbar.js +8 -1
- package/dist/features/resource-dashboard/react/components/ResourceCardParts.js +2 -1
- package/dist/features/resource-dashboard/react/components/ResourceRowListItem.js +3 -1
- package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.js +2 -1
- package/dist/features/resource-dashboard/react/components/ResourceRowListView.js +1 -1
- package/dist/features/resource-dashboard/react/hooks/useResourceBoard.js +1 -1
- package/dist/features/schedule-picker/rules.js +13 -7
- package/dist/features/sketch-editor/constants.d.ts +0 -6
- package/dist/features/sketch-editor/constants.js +17 -15
- package/dist/features/sketch-editor/dom.js +4 -7
- package/dist/features/sketch-editor/react/components/SketchMainMenu.js +2 -1
- package/dist/features/sketch-editor/react/components/SketchSaveStateBadge.js +2 -1
- package/dist/features/sketch-editor/react/hooks/useSketchDomEnhancements.js +1 -1
- package/dist/features/sketch-editor/rules.js +13 -12
- package/dist/features/skills/dependencies.js +9 -4
- package/dist/features/skills/react/components/SkillRow.js +2 -1
- package/dist/features/skills/react/components/SkillsTab.js +2 -1
- package/dist/features/skills/rules.js +3 -3
- package/dist/features/source-config-list/agent-handles.js +4 -3
- package/dist/features/source-config-list/react/components/SourceConfigItemCard.js +3 -1
- package/dist/features/tab-strip/react/components/TabBar.hooks.js +4 -2
- package/dist/features/version-manager/react/components/VersionPreviewFrame.js +4 -1
- package/dist/features/version-manager/react/components/VersionSidebar.js +8 -6
- package/dist/features/version-manager/react/hooks/usePreviewCanvasSize.js +1 -1
- package/dist/features/version-manager/react/hooks/useVersionManager.js +5 -7
- package/dist/features/version-manager/rules.js +3 -5
- package/dist/features/viewer-shell/react/components/CommentSidePanel.js +2 -1
- package/dist/features/viewer-shell/react/components/SegmentedToggle.js +2 -1
- package/dist/features/viewer-shell/react/components/ViewerShell.js +2 -1
- package/dist/features/viewer-shell/rules.js +8 -7
- package/dist/react/components/BrandLogo.js +10 -2
- package/dist/react/components/CustomSelect.js +1 -1
- package/dist/react/components/EditorIcon.js +17 -14
- package/dist/react/components/Loading.js +2 -1
- package/dist/react/components/OnboardingChipField.js +5 -1
- package/dist/react/components/OnboardingDropdown.js +6 -4
- package/dist/react/components/PaletteTweaks.js +7 -6
- package/dist/react/components/PillButton.js +2 -1
- package/dist/react/components/PopoverMenu.js +2 -1
- package/dist/react/components/RemixIcon.js +2 -1
- package/dist/react/components/SecretInput.hooks.d.ts +2 -25
- package/dist/react/components/SecretInput.hooks.js +2 -24
- package/dist/react/components/SecretInput.js +5 -4
- package/dist/react/components/Toast.js +4 -2
- package/dist/react/components/TooltipLayer.js +4 -1
- package/dist/react/components/WorkingDirPicker.js +5 -4
- package/dist/react/components/remixicon-font-recovery.js +9 -1
- package/dist/react/components/select-popover.js +2 -2
- package/dist/react/hooks/useBrandFonts.js +1 -1
- package/dist/react/hooks/useEdgeAutoScroll.js +1 -1
- package/dist/react/hooks/useInView.js +9 -9
- package/dist/react/hooks/useResizableSplitPane.js +12 -20
- package/dist/react/mcp-ui/useHostSurfaceTokens.js +1 -1
- package/dist/renderers/annotation-canvas/react/components/AnnotationCanvas.js +7 -1
- package/dist/renderers/annotation-canvas/react/hooks/useAnnotationCanvas.controller.js +4 -2
- package/dist/renderers/annotation-canvas/react/hooks/useAnnotationCanvas.js +9 -5
- package/dist/renderers/annotation-canvas/rules.js +9 -13
- package/dist/renderers/annotation-canvas/theme.js +5 -2
- package/dist/renderers/renderers/index.js +1 -1
- package/dist/renderers/renderers/markdown.js +4 -6
- package/dist/theme/apply.js +7 -3
- package/dist/utils/class-names.d.ts +3 -0
- package/dist/utils/class-names.js +5 -0
- package/dist/utils/design-md.js +3 -3
- package/dist/utils/ime-composing.d.ts +1 -1
- package/dist/utils/ime-composing.js +1 -2
- package/dist/utils/localized-url.js +2 -11
- package/dist/utils/markdown-scroll-sync.js +1 -1
- package/dist/utils/omit-keys.d.ts +3 -0
- package/dist/utils/omit-keys.js +8 -0
- package/dist/utils/polygon-selection.js +1 -1
- package/dist/utils/sse.js +3 -0
- package/dist/utils/trim-chars.d.ts +13 -0
- package/dist/utils/trim-chars.js +30 -0
- package/dist/utils/unhandled-case.d.ts +10 -0
- package/dist/utils/unhandled-case.js +12 -0
- package/dist/utils/unit-bezier.js +1 -3
- package/dist/utils/zip.js +6 -10
- package/package.json +10 -9
|
@@ -39,7 +39,7 @@ export function useDismissOnOutsideOrEscape({ onDismiss }, options = {}) {
|
|
|
39
39
|
}, [onDismiss]);
|
|
40
40
|
useEffect(() => {
|
|
41
41
|
if (!enabled)
|
|
42
|
-
return;
|
|
42
|
+
return undefined;
|
|
43
43
|
return subscription.subscribe({ containerRef, onDismiss: () => onDismissRef.current() });
|
|
44
44
|
}, [enabled, containerRef, subscription]);
|
|
45
45
|
}
|
|
@@ -36,11 +36,11 @@ export function useGlobalKeydown({ handler }, options = {}) {
|
|
|
36
36
|
}, [handler]);
|
|
37
37
|
useEffect(() => {
|
|
38
38
|
if (!enabled)
|
|
39
|
-
return;
|
|
39
|
+
return undefined;
|
|
40
40
|
const eventTarget = resolveTarget({ target });
|
|
41
41
|
// A host may intentionally provide no target while its surface is detached.
|
|
42
42
|
if (typeof eventTarget === 'undefined')
|
|
43
|
-
return;
|
|
43
|
+
return undefined;
|
|
44
44
|
function onKeyDown(event) {
|
|
45
45
|
handlerRef.current(event);
|
|
46
46
|
}
|
|
@@ -32,7 +32,7 @@ export function useModalWindowDragGuard(options) {
|
|
|
32
32
|
const { backdropSelector, enabled = true } = options;
|
|
33
33
|
useEffect(() => {
|
|
34
34
|
if (!enabled)
|
|
35
|
-
return;
|
|
35
|
+
return undefined;
|
|
36
36
|
const stopBackdropDismissInDragStrip = (event) => {
|
|
37
37
|
if (eventHitsModalWindowDragStrip(event, backdropSelector)) {
|
|
38
38
|
event.stopPropagation();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { adminThemeColorKeys, validateAdminTheme } from './validation.js';
|
|
2
2
|
import { defaultAdminTheme } from './default.js';
|
|
3
|
+
import { kebabThemeKey } from './css-name.js';
|
|
3
4
|
/** Internal document adapter: isolated previews cannot inherit the admin's stylesheet. */
|
|
4
5
|
export function renderAdminThemeVariables({ theme }, { colorScheme = 'light' } = {}) {
|
|
5
6
|
if (colorScheme !== 'light' && colorScheme !== 'dark')
|
|
@@ -7,7 +8,7 @@ export function renderAdminThemeVariables({ theme }, { colorScheme = 'light' } =
|
|
|
7
8
|
const checked = validateAdminTheme({ theme });
|
|
8
9
|
if (!checked.valid)
|
|
9
10
|
throw new TypeError(checked.errors.join('; '));
|
|
10
|
-
const values = adminThemeColorKeys.map((key) => `--jini-${key
|
|
11
|
+
const values = adminThemeColorKeys.map((key) => `--jini-${kebabThemeKey(key)}:${theme[colorScheme][key]};`);
|
|
11
12
|
values.push(`--jini-font-body:${theme.fonts.body};`, `--jini-font-heading:${theme.fonts.heading};`, `--jini-font-mono:${theme.fonts.mono ?? defaultAdminTheme.fonts.mono};`, `--jini-radius:${theme.radius ?? defaultAdminTheme.radius};`);
|
|
12
13
|
return `:root{color-scheme:${colorScheme};${values.join('')}}`;
|
|
13
14
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
export const adminThemeColorKeys = ['primary', 'primaryInk', 'bg', 'surface', 'text', 'muted', 'border', 'danger', 'success', 'warning'];
|
|
2
2
|
const record = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
3
|
-
const safeValue = (value) =>
|
|
4
|
-
|
|
3
|
+
const safeValue = (value) => {
|
|
4
|
+
if (typeof value !== 'string' || value.trim().length === 0 || value.length > 512)
|
|
5
|
+
return false;
|
|
6
|
+
return !/[;{}\x00-\x1f<>\\]/.test(value) && !/url\s*\(|\/\*|\*\//i.test(value);
|
|
7
|
+
};
|
|
5
8
|
// Concrete palette values, deliberately excluding dependent var()/url() expressions.
|
|
6
9
|
const component = '(?:[+-]?(?:\\d+(?:\\.\\d+)?|\\.\\d+)(?:%|deg|rad|grad|turn)?|none)';
|
|
7
10
|
const components = new RegExp(`^(?:rgb|rgba|hsl|hsla|oklch|oklab|lab|lch)\\(\\s*${component}(?:\\s*,\\s*|\\s+)${component}(?:\\s*,\\s*|\\s+)${component}(?:(?:\\s*[,/]\\s*)${component})?\\s*\\)$`, 'i');
|
|
@@ -9,7 +12,11 @@ const colorValue = (value) => safeValue(value)
|
|
|
9
12
|
&& (/^#[\da-f]{3}(?:[\da-f]|[\da-f]{3}|[\da-f]{5})?$/i.test(value)
|
|
10
13
|
|| components.test(value)
|
|
11
14
|
|| /^(?:transparent|black|white|red|green|blue|gray|grey)$/i.test(value));
|
|
12
|
-
const fontStacksValid = ({ fonts }) =>
|
|
15
|
+
const fontStacksValid = ({ fonts }) => {
|
|
16
|
+
if (!record(fonts) || !safeValue(fonts.body) || !safeValue(fonts.heading))
|
|
17
|
+
return false;
|
|
18
|
+
return fonts.mono === undefined || safeValue(fonts.mono);
|
|
19
|
+
};
|
|
13
20
|
const stylesheetUrlValid = ({ url }) => {
|
|
14
21
|
if (typeof url !== 'string' || url.length > 4096 || /[\x00-\x20<>]/.test(url))
|
|
15
22
|
return false;
|
|
@@ -39,7 +46,11 @@ function paletteErrors({ scheme, colors }) {
|
|
|
39
46
|
return [...invalid, ...unknown];
|
|
40
47
|
}
|
|
41
48
|
const radiusValid = ({ radius }) => radius === undefined || (typeof radius === 'string' && /^(?:0|\d+(?:\.\d+)?(?:px|rem|em))$/.test(radius));
|
|
42
|
-
const densityValid = ({ density }) =>
|
|
49
|
+
const densityValid = ({ density }) => {
|
|
50
|
+
if (density === undefined)
|
|
51
|
+
return true;
|
|
52
|
+
return typeof density === 'number' && Number.isFinite(density) && density >= 0.5 && density <= 2;
|
|
53
|
+
};
|
|
43
54
|
/** Validate serializable theme data before any DOM or font-loading effect. */
|
|
44
55
|
export function validateAdminTheme({ theme }) {
|
|
45
56
|
if (!record(theme))
|
|
@@ -92,9 +92,10 @@ function deriveDownloaded(model) {
|
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
94
|
const installable = canInstall(model);
|
|
95
|
+
const installLabelKey = payload ? K.installRestart : K.installNow;
|
|
95
96
|
return {
|
|
96
97
|
primaryAction: installable ? 'install' : null,
|
|
97
|
-
primaryLabelKey: installable ?
|
|
98
|
+
primaryLabelKey: installable ? installLabelKey : null,
|
|
98
99
|
showReleaseLink: true,
|
|
99
100
|
statusKey: model.availableVersion ? K.ready : K.readyUnknown,
|
|
100
101
|
statusTone: 'success',
|
|
@@ -7,9 +7,9 @@ import { placeInfoTipBubble } from "./InfoTip.rules.js";
|
|
|
7
7
|
export const TIP_BOUNDS_ATTRIBUTE = "data-jini-tip-bounds";
|
|
8
8
|
/** How long a tap-opened tip stays up on its own; touch has no pointer-leave to close it. */
|
|
9
9
|
export const TAP_TIP_MS = 2500;
|
|
10
|
-
/**
|
|
11
|
-
*
|
|
12
|
-
|
|
10
|
+
/** A pinned or focused tip used to stay up beside the one the pointer had just reached
|
|
11
|
+
* (owner, 2026-10-25: two "Soon" tips open at once). Each claim owns its document listener. */
|
|
12
|
+
const OPEN_TIP_EVENT = "jini:claim-open-tip";
|
|
13
13
|
/**
|
|
14
14
|
* Makes `close` the one open tip, closing whichever tip held that slot before. Hosts with their own
|
|
15
15
|
* tip state (e.g. a dock-header tip) can call it too, to join the same one-open rule.
|
|
@@ -18,13 +18,23 @@ let closeOpenTip = null;
|
|
|
18
18
|
* @complexity O(1).
|
|
19
19
|
*/
|
|
20
20
|
export function claimOpenTip(close) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
const target = document;
|
|
22
|
+
const claim = { close };
|
|
23
|
+
const release = () => target.removeEventListener(OPEN_TIP_EVENT, onClaim);
|
|
24
|
+
const onClaim = (event) => {
|
|
25
|
+
const next = event.detail;
|
|
26
|
+
if (next.close === close) {
|
|
27
|
+
next.release = release;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
release();
|
|
31
|
+
close();
|
|
27
32
|
};
|
|
33
|
+
target.dispatchEvent(new CustomEvent(OPEN_TIP_EVENT, { detail: claim }));
|
|
34
|
+
if (claim.release)
|
|
35
|
+
return claim.release;
|
|
36
|
+
target.addEventListener(OPEN_TIP_EVENT, onClaim);
|
|
37
|
+
return release;
|
|
28
38
|
}
|
|
29
39
|
/** Prefer a host's placement when the measured bubble fits; default preserves automatic flipping.
|
|
30
40
|
* @complexity Time/space: O(1), with one icon and one bubble measurement.
|
|
@@ -93,7 +103,7 @@ export function useInfoTip(_input = {}, { placement: preferredPlacement } = {})
|
|
|
93
103
|
};
|
|
94
104
|
useEffect(() => {
|
|
95
105
|
if (!open)
|
|
96
|
-
return;
|
|
106
|
+
return undefined;
|
|
97
107
|
return claimOpenTip(hide);
|
|
98
108
|
}, [open]);
|
|
99
109
|
// A tapped-open tip stays up until the operator taps elsewhere or it times out; the bubble
|
|
@@ -101,7 +111,7 @@ export function useInfoTip(_input = {}, { placement: preferredPlacement } = {})
|
|
|
101
111
|
useDismissOnOutsideOrEscape({ onDismiss: hide }, { enabled: pinned !== false, containerRef: iconRef });
|
|
102
112
|
useEffect(() => {
|
|
103
113
|
if (pinned !== "tap")
|
|
104
|
-
return;
|
|
114
|
+
return undefined;
|
|
105
115
|
const timer = setTimeout(hide, TAP_TIP_MS);
|
|
106
116
|
return () => clearTimeout(timer);
|
|
107
117
|
}, [pinned]);
|
|
@@ -111,7 +121,7 @@ export function useInfoTip(_input = {}, { placement: preferredPlacement } = {})
|
|
|
111
121
|
// leaves it be. A resize re-measures instead, so it keeps following its icon.
|
|
112
122
|
useEffect(() => {
|
|
113
123
|
if (!open)
|
|
114
|
-
return;
|
|
124
|
+
return undefined;
|
|
115
125
|
const closeIfMoved = (e) => {
|
|
116
126
|
const icon = iconRef.current;
|
|
117
127
|
if (icon && e.target instanceof Node && !e.target.contains(icon))
|
|
@@ -89,7 +89,7 @@ export function useLightbox(input) {
|
|
|
89
89
|
useEffect(() => {
|
|
90
90
|
const dialog = dialogRef.current;
|
|
91
91
|
if (!dialog || !input.open)
|
|
92
|
-
return;
|
|
92
|
+
return undefined;
|
|
93
93
|
const onKey = (event) => {
|
|
94
94
|
if (event.key !== "ArrowLeft" && event.key !== "ArrowRight")
|
|
95
95
|
return;
|
|
@@ -13,7 +13,7 @@ export function useScrollJump({ container }, { reducedMotion = prefersReducedMot
|
|
|
13
13
|
const [visibility, setVisibility] = useState(HIDDEN);
|
|
14
14
|
useEffect(() => {
|
|
15
15
|
if (!container)
|
|
16
|
-
return;
|
|
16
|
+
return undefined;
|
|
17
17
|
// Same-value updates keep the previous object so a scroll that changes nothing does not re-render.
|
|
18
18
|
const update = () => setVisibility((previous) => {
|
|
19
19
|
const next = scrollJumpVisibility(container);
|
|
@@ -39,7 +39,7 @@ export function useSeeMoreClamp({ lines, children }) {
|
|
|
39
39
|
}, [expanded, lineCount, children, measure]);
|
|
40
40
|
useEffect(() => {
|
|
41
41
|
if (expanded)
|
|
42
|
-
return;
|
|
42
|
+
return undefined;
|
|
43
43
|
const el = textRef.current;
|
|
44
44
|
// Guarded rather than assumed, the same way `Select.tsx` guards `elementFromPoint` and
|
|
45
45
|
// `scrollIntoView`: this app's jsdom test harness implements no `ResizeObserver` at all, so an
|
|
@@ -47,7 +47,7 @@ export function useSeeMoreClamp({ lines, children }) {
|
|
|
47
47
|
// Where it is missing, the layout effect above still measures on mount and on content change —
|
|
48
48
|
// only reflow-from-width-change goes unnoticed, which jsdom has no layout engine to produce.
|
|
49
49
|
if (!el || typeof ResizeObserver !== "function")
|
|
50
|
-
return;
|
|
50
|
+
return undefined;
|
|
51
51
|
const observer = new ResizeObserver(() => measure());
|
|
52
52
|
observer.observe(el);
|
|
53
53
|
return () => observer.disconnect();
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { agentHandle } from "@jini-ai/agentic";
|
|
3
3
|
import { useSeeMoreClamp } from "./SeeMore.hooks.js";
|
|
4
|
+
import { withClassName } from "../../../../utils/class-names.js";
|
|
4
5
|
const DEFAULT_LINES = 2;
|
|
5
6
|
// Presentation depends only on props and expanded state, so this decision needs no React render.
|
|
6
7
|
export function resolveSeeMoreView({ expanded, }, { moreLabel = "See more", lessLabel = "See less", className, textClassName, toggleClassName, } = {}) {
|
|
7
8
|
return {
|
|
8
9
|
wrapperClassName: className ? `see-more ${className}` : "see-more",
|
|
9
|
-
textClassName:
|
|
10
|
+
textClassName: withClassName(expanded ? "see-more-text is-expanded" : "see-more-text", textClassName),
|
|
10
11
|
toggleClassName: toggleClassName ? `see-more-toggle ${toggleClassName}` : "see-more-toggle",
|
|
11
12
|
toggleLabel: expanded ? lessLabel : moreLabel,
|
|
12
13
|
};
|
|
@@ -16,9 +17,10 @@ export function SeeMore({ useClamp = useSeeMoreClamp, agentHandle: handle, ...pr
|
|
|
16
17
|
const { children, lines = DEFAULT_LINES, moreLabel, lessLabel, className, textClassName, toggleClassName, toggleAriaLabel } = props;
|
|
17
18
|
const { expanded, setExpanded, overflows, textRef, regionId, lineCount } = useClamp({ lines, children });
|
|
18
19
|
const view = resolveSeeMoreView({ expanded }, { moreLabel, lessLabel, className, textClassName, toggleClassName });
|
|
20
|
+
const toggleHandleProps = handle ? agentHandle({ handle: handle }, { role: "button", label: toggleAriaLabel ?? view.toggleLabel }) : {};
|
|
19
21
|
return (_jsxs("div", { className: view.wrapperClassName, children: [_jsx("div", { ref: textRef, id: regionId, className: view.textClassName,
|
|
20
22
|
// The line count rides a custom property rather than a class-per-N (`.see-more-text--3`),
|
|
21
23
|
// so `lines` can be any integer a caller needs without this file growing a rule for each.
|
|
22
|
-
style: { "--see-more-lines": lineCount }, children: children }), overflows ? (_jsx("button", { type: "button", className: view.toggleClassName, "aria-expanded": expanded, "aria-controls": regionId, "aria-label": toggleAriaLabel, onClick: () => setExpanded((current) => !current), ...
|
|
24
|
+
style: { "--see-more-lines": lineCount }, children: children }), overflows ? (_jsx("button", { type: "button", className: view.toggleClassName, "aria-expanded": expanded, "aria-controls": regionId, "aria-label": toggleAriaLabel, onClick: () => setExpanded((current) => !current), ...toggleHandleProps, children: view.toggleLabel })) : null] }));
|
|
23
25
|
}
|
|
24
26
|
//# sourceMappingURL=SeeMore.js.map
|
|
@@ -149,7 +149,8 @@ export function useSelectDropdown({ value, onChange, options, }, { disabled } =
|
|
|
149
149
|
const initialIndex = options.findIndex((o) => o.value === value);
|
|
150
150
|
setQuery("");
|
|
151
151
|
popover.openPanel();
|
|
152
|
-
|
|
152
|
+
const firstIndex = options.length ? 0 : -1;
|
|
153
|
+
setHighlightedIndex(initialIndex >= 0 ? initialIndex : firstIndex);
|
|
153
154
|
}
|
|
154
155
|
function selectOption(option) {
|
|
155
156
|
onChange(option.value);
|
|
@@ -9,7 +9,8 @@ function SelectOptionRow({ option, index, isSelected, isHighlighted, optionId, s
|
|
|
9
9
|
return (_jsxs("li", { ref: (el) => setOptionRef(index, el), id: optionId, role: "option", "aria-selected": isSelected, className: `select-option${isSelected ? " is-selected" : ""}${isHighlighted ? " is-highlighted" : ""}`, onMouseEnter: () => onHighlight(index), onClick: () => onSelect(option), ...(optionHandle ? agentHandle({ handle: optionHandle }, { role: "button", label: option.label }) : {}), children: [_jsx("span", { className: "select-option-label", children: option.label }), option.detail ? _jsx("span", { className: "select-option-detail", children: option.detail }) : null, option.badge ? _jsx("span", { className: "select-option-badge", children: option.badge }) : null, isSelected ? (_jsx("svg", { className: "select-option-check", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M3.5 8.5 6.5 11.5 12.5 4.5", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }) })) : null] }));
|
|
10
10
|
}
|
|
11
11
|
function SelectPanel({ panelRef, position, showSearch, searchInputRef, query, setQuery, filtered, value, highlightedIndex, listboxId, optionId, setOptionRef, onHighlight, onSelect, onKeyDown, searchHandle, optionHandles, t, }) {
|
|
12
|
-
|
|
12
|
+
const searchHandleProps = searchHandle ? agentHandle({ handle: searchHandle }, { role: "field", label: "Search the option list" }) : {};
|
|
13
|
+
return (_jsxs("div", { ref: panelRef, className: "select-panel", style: { left: position.left, width: position.width, top: position.top, bottom: position.bottom, maxHeight: position.maxHeight }, tabIndex: -1, onKeyDown: onKeyDown, children: [showSearch ? (_jsx("input", { ref: searchInputRef, type: "text", className: "select-search", "aria-label": t("Search options"), placeholder: t("Search…"), value: query, onChange: (e) => setQuery(e.target.value), ...searchHandleProps })) : null, _jsx("ul", { className: "select-list", role: "listbox", id: listboxId, children: filtered.length === 0 ? (_jsx("li", { className: "select-empty", role: "presentation", children: t("No matches") })) : (filtered.map((option, index) => (_jsxs(Fragment, { children: [groupHeadingAt({ options: filtered, index }) ? (_jsx("li", { className: "select-group-label", role: "presentation", children: option.group })) : null, _jsx(SelectOptionRow, { option: option, index: index, isSelected: option.value === value, isHighlighted: index === highlightedIndex, optionId: optionId(index), setOptionRef: setOptionRef, onHighlight: onHighlight, onSelect: onSelect, agentHandle: optionHandles?.[index] })] }, option.value)))) })] }));
|
|
13
14
|
}
|
|
14
15
|
export function resolveSelectTriggerLabel({ selectedOption, placeholder }, { t = (key) => key } = {}) {
|
|
15
16
|
if (selectedOption)
|
|
@@ -9,8 +9,10 @@ export function SuggestInput({ value, onValueChange, suggestions, onPick, onKeyD
|
|
|
9
9
|
const listId = useId();
|
|
10
10
|
const optionId = (index) => `${listId}-opt-${index}`;
|
|
11
11
|
return (_jsxs("span", { className: className ? `jini-suggest-input ${className}` : 'jini-suggest-input', children: [_jsx("input", { type: "text", role: "combobox", "aria-autocomplete": "list", "aria-expanded": vm.open, "aria-controls": listId, "aria-activedescendant": vm.open && vm.highlighted >= 0 ? optionId(vm.highlighted) : undefined, "aria-label": rest['aria-label'], placeholder: placeholder, disabled: disabled, value: value, autoComplete: "off", onChange: (event) => { onValueChange(event.target.value); vm.openList(); }, onFocus: vm.openList, onBlur: vm.closeList, onKeyDown: (event) => {
|
|
12
|
-
if (event.nativeEvent.isComposing)
|
|
13
|
-
|
|
12
|
+
if (event.nativeEvent.isComposing) {
|
|
13
|
+
onKeyDown?.(event);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
14
16
|
const result = vm.handleKey(event.key);
|
|
15
17
|
if (result.consumed) {
|
|
16
18
|
event.preventDefault();
|
|
@@ -14,11 +14,12 @@ export function useDebouncedHtmlPreview({ source, render }, { revision = '', del
|
|
|
14
14
|
useEffect(() => {
|
|
15
15
|
let current = true;
|
|
16
16
|
const controller = new AbortController();
|
|
17
|
+
const fail = () => { if (current)
|
|
18
|
+
setState(previous => ({ ...previous, error: 'Preview unavailable.', settled: key })); };
|
|
17
19
|
const timer = setTimeout(() => {
|
|
18
20
|
// Promise.resolve also maps a synchronously throwing host adapter to the error state.
|
|
19
21
|
void Promise.resolve().then(() => render({ source }, { signal: controller.signal })).then(document => { if (current)
|
|
20
|
-
setState({ document, error: null, settled: key }); },
|
|
21
|
-
setState(previous => ({ ...previous, error: 'Preview unavailable.', settled: key })); });
|
|
22
|
+
setState({ document, error: null, settled: key }); }, fail);
|
|
22
23
|
}, delayMs);
|
|
23
24
|
return () => { current = false; clearTimeout(timer); controller.abort(); };
|
|
24
25
|
}, [key, source, render, delayMs]);
|
|
@@ -21,6 +21,9 @@ function renderGridContent({ loading, emptyState, t, ...bodyProps }, _options =
|
|
|
21
21
|
return emptyState ?? _jsx("div", { className: "asset-grid-empty", children: t('No assets yet.') });
|
|
22
22
|
return _jsx(AssetGridBody, { ...bodyProps });
|
|
23
23
|
}
|
|
24
|
+
function withoutAssetIds({ assets, ids }, _options = {}) {
|
|
25
|
+
return assets.filter(asset => !ids.has(asset.id));
|
|
26
|
+
}
|
|
24
27
|
/**
|
|
25
28
|
* A live-updating, filterable, multi-selectable asset grid — the generic
|
|
26
29
|
* pattern behind a photo library / DAM. See `packages/ui/source-map.md` for
|
|
@@ -102,9 +105,10 @@ export function AssetGrid({ active = true, selectors, dependencies, kindFacets:
|
|
|
102
105
|
const ids = [...selectedIds];
|
|
103
106
|
if (!ids.length)
|
|
104
107
|
return;
|
|
108
|
+
const idSet = new Set(ids);
|
|
109
|
+
const removeDeleted = (prev) => withoutAssetIds({ assets: prev, ids: idSet });
|
|
105
110
|
await runDelete(onDeleteSelected ? (batch) => onDeleteSelected([...batch]) : null, ids, () => {
|
|
106
|
-
|
|
107
|
-
setAssets((prev) => prev.filter((a) => !idSet.has(a.id)));
|
|
111
|
+
setAssets(removeDeleted);
|
|
108
112
|
setSelectedIds(new Set());
|
|
109
113
|
});
|
|
110
114
|
}, [selectedIds, onDeleteSelected, setAssets, setSelectedIds, runDelete]);
|
|
@@ -113,8 +117,9 @@ export function AssetGrid({ active = true, selectors, dependencies, kindFacets:
|
|
|
113
117
|
// here (the old inline `onDeleteAsset ? … : null` could never see `null` from a rendered card).
|
|
114
118
|
const handleDeleteAsset = useMemo(() => onDeleteAsset
|
|
115
119
|
? async (id) => {
|
|
120
|
+
const removeDeleted = (prev) => withoutAssetIds({ assets: prev, ids: new Set([id]) });
|
|
116
121
|
await runDelete((batch) => onDeleteAsset(batch[0]), [id], () => {
|
|
117
|
-
setAssets(
|
|
122
|
+
setAssets(removeDeleted);
|
|
118
123
|
});
|
|
119
124
|
}
|
|
120
125
|
: undefined, [onDeleteAsset, setAssets, runDelete]);
|
|
@@ -6,13 +6,14 @@ function handleSelectionKey({ event, typing, hasSelection, isPreviewOpen, onClea
|
|
|
6
6
|
return; // the preview surface owns Escape while it's open
|
|
7
7
|
if (hasSelection)
|
|
8
8
|
onClearSelection();
|
|
9
|
+
return;
|
|
9
10
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
if (event.key !== 'Delete' && event.key !== 'Backspace')
|
|
12
|
+
return;
|
|
13
|
+
if (typing || isPreviewOpen || !hasSelection)
|
|
14
|
+
return;
|
|
15
|
+
event.preventDefault();
|
|
16
|
+
onRequestDeleteSelected();
|
|
16
17
|
}
|
|
17
18
|
/** Cmd/Ctrl+A select-all, Escape clear-selection, Delete/Backspace triggers the bulk-delete confirm flow. */
|
|
18
19
|
export function useAssetGridKeyboardShortcuts(params) {
|
|
@@ -46,7 +46,7 @@ export function useAssetGridLiveUpdates(params) {
|
|
|
46
46
|
}, [fetchAssetById]);
|
|
47
47
|
useEffect(() => {
|
|
48
48
|
if (!active || !liveUpdates)
|
|
49
|
-
return;
|
|
49
|
+
return undefined;
|
|
50
50
|
let disposed = false;
|
|
51
51
|
let timer = null;
|
|
52
52
|
const pendingIngest = new Set();
|
|
@@ -110,6 +110,12 @@ export function useAssetGridLiveUpdates(params) {
|
|
|
110
110
|
*/
|
|
111
111
|
let flushing = false;
|
|
112
112
|
let rerun = false;
|
|
113
|
+
const reportFlushFailure = () => {
|
|
114
|
+
if (!disposed) {
|
|
115
|
+
pendingFull = true;
|
|
116
|
+
console.error('[asset-grid] Live update failed.');
|
|
117
|
+
}
|
|
118
|
+
};
|
|
113
119
|
const flush = async () => {
|
|
114
120
|
timer = null;
|
|
115
121
|
if (flushing) {
|
|
@@ -120,15 +126,7 @@ export function useAssetGridLiveUpdates(params) {
|
|
|
120
126
|
try {
|
|
121
127
|
do {
|
|
122
128
|
rerun = false;
|
|
123
|
-
|
|
124
|
-
await runFlushPass();
|
|
125
|
-
}
|
|
126
|
-
catch {
|
|
127
|
-
if (!disposed) {
|
|
128
|
-
pendingFull = true;
|
|
129
|
-
console.error('[asset-grid] Live update failed.');
|
|
130
|
-
}
|
|
131
|
-
}
|
|
129
|
+
await runFlushPass().catch(reportFlushFailure);
|
|
132
130
|
} while (rerun && !disposed);
|
|
133
131
|
}
|
|
134
132
|
finally {
|
|
@@ -36,7 +36,7 @@ export function useRubberBandDrag({ containerRef, selectedIds, setSelectedIds, }
|
|
|
36
36
|
}, [selectedIds, containerRef]);
|
|
37
37
|
useEffect(() => {
|
|
38
38
|
if (!dragging)
|
|
39
|
-
return;
|
|
39
|
+
return undefined;
|
|
40
40
|
let raf = 0;
|
|
41
41
|
// `dragging` only ever flips true inside `onMouseDown` above, which sets
|
|
42
42
|
// `dragRef.current` synchronously in that same callback before calling
|
|
@@ -48,16 +48,14 @@ export function AssetTreeFileRow({ path, displayName, active, selected, kindLabe
|
|
|
48
48
|
return;
|
|
49
49
|
onCommitRename();
|
|
50
50
|
}, onKeyDown: (event) => {
|
|
51
|
-
if (event.key
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
if (event.key !== 'Enter' && event.key !== 'Escape')
|
|
52
|
+
return;
|
|
53
|
+
event.preventDefault();
|
|
54
|
+
event.currentTarget.dataset.skipRenameCommit = '1';
|
|
55
|
+
if (event.key === 'Enter')
|
|
54
56
|
onCommitRename();
|
|
55
|
-
|
|
56
|
-
else if (event.key === 'Escape') {
|
|
57
|
-
event.preventDefault();
|
|
58
|
-
event.currentTarget.dataset.skipRenameCommit = '1';
|
|
57
|
+
else
|
|
59
58
|
onCancelRename();
|
|
60
|
-
}
|
|
61
59
|
} })) : (_jsx("button", { type: "button", className: "asset-tree-row-name-btn", onClick: () => onSelectPreview(path), onDoubleClick: () => onOpen(path), onKeyDown: handleNameKeyDown, children: _jsxs("span", { className: "asset-tree-row-name-wrap", children: [_jsx("span", { className: "asset-tree-row-name", title: displayName, children: displayName }), _jsx("span", { className: "asset-tree-row-sub", children: kindLabel })] }) })) }), _jsx("span", { className: "asset-tree-row-size", onClick: () => onSelectPreview(path), onDoubleClick: () => onOpen(path), children: humanBytes(size) }), _jsx("span", { className: "asset-tree-row-time", onClick: () => onSelectPreview(path), onDoubleClick: () => onOpen(path), children: timeLabel }), _jsx("span", { "data-testid": `asset-tree-file-menu-${path}`, className: "asset-tree-row-menu", role: "button", tabIndex: 0, "aria-label": t('Row actions for {name}', { name: displayName }), onClick: handleMenuTrigger, onKeyDown: (event) => {
|
|
62
60
|
if (event.key === 'Enter' || event.key === ' ') {
|
|
63
61
|
event.preventDefault();
|
|
@@ -16,7 +16,7 @@ export function useAssetTreeRowMenu(dom) {
|
|
|
16
16
|
const closeMenu = useCallback(() => setMenuPos(null), []);
|
|
17
17
|
useEffect(() => {
|
|
18
18
|
if (!menuPos)
|
|
19
|
-
return;
|
|
19
|
+
return undefined;
|
|
20
20
|
return dom.subscribeOutsideDismiss(containerRef, closeMenu);
|
|
21
21
|
}, [menuPos, dom, closeMenu]);
|
|
22
22
|
return { menuPos, containerRef, openMenuFor, closeMenu };
|
|
@@ -67,9 +67,7 @@ function collectFileChildren(input) {
|
|
|
67
67
|
function collectFolderChildren(input) {
|
|
68
68
|
for (const folder of input.folders) {
|
|
69
69
|
const segment = childSegment(folder.path, input.prefix);
|
|
70
|
-
if (!segment)
|
|
71
|
-
continue;
|
|
72
|
-
if (!segment.nested && !segment.head)
|
|
70
|
+
if (!segment || (!segment.nested && !segment.head))
|
|
73
71
|
continue; // the current directory itself
|
|
74
72
|
input.dirs.add(segment.head);
|
|
75
73
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_HOME_NAVIGATION_ENTRY, EMPTY_URL, HISTORY_LIMIT } from './constants.js';
|
|
2
|
+
import { trimTrailingChars } from '../../utils/trim-chars.js';
|
|
2
3
|
function escapeRegExp(value) {
|
|
3
4
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
4
5
|
}
|
|
@@ -46,7 +47,7 @@ export function formatAddressDisplayParts(url, title, homeLabel = DEFAULT_HOME_N
|
|
|
46
47
|
const fallback = labelFromUrl(url, homeLabel);
|
|
47
48
|
if (cleanTitle === fallback || cleanTitle === url)
|
|
48
49
|
return { url };
|
|
49
|
-
return { url: url
|
|
50
|
+
return { url: trimTrailingChars(url, '/'), title: cleanTitle };
|
|
50
51
|
}
|
|
51
52
|
export function formatAddressDisplay(url, title, homeLabel = DEFAULT_HOME_NAVIGATION_ENTRY.title) {
|
|
52
53
|
const parts = formatAddressDisplayParts(url, title, homeLabel);
|
|
@@ -81,7 +82,7 @@ export function isHistoryUrl(url) {
|
|
|
81
82
|
return url !== EMPTY_URL && (isHttpLikeUrl(url) || /^file:\/\//i.test(url));
|
|
82
83
|
}
|
|
83
84
|
export function sameUrl(left, right) {
|
|
84
|
-
return left
|
|
85
|
+
return trimTrailingChars(left, '/') === trimTrailingChars(right, '/');
|
|
85
86
|
}
|
|
86
87
|
function cleanIconUrl(url) {
|
|
87
88
|
const value = url?.trim();
|
|
@@ -98,11 +99,11 @@ export function isHistoryEntry(value) {
|
|
|
98
99
|
if (typeof value !== 'object' || value == null || Array.isArray(value))
|
|
99
100
|
return false;
|
|
100
101
|
const record = value;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
if (typeof record.url !== 'string' || typeof record.title !== 'string')
|
|
103
|
+
return false;
|
|
104
|
+
if (typeof record.lastVisitedAt !== 'number' || typeof record.visitCount !== 'number')
|
|
105
|
+
return false;
|
|
106
|
+
return record.iconUrl === undefined || typeof record.iconUrl === 'string';
|
|
106
107
|
}
|
|
107
108
|
/** Namespace + scope key -> storage key. Namespace defaults to a generic Jini prefix (see `constants.ts`); scope key is host-supplied (e.g. a project or tab id). */
|
|
108
109
|
export function historyStorageKey(namespace, scopeKey) {
|
|
@@ -234,7 +235,8 @@ function navigationTitle({ url }, { title, homeLabel }) {
|
|
|
234
235
|
function findRejoinTarget({ state, url }, { replacePendingTarget, pendingTarget }) {
|
|
235
236
|
const { navigationStack: stack, navigationIndex: index } = state;
|
|
236
237
|
const current = stack[index];
|
|
237
|
-
|
|
238
|
+
const replacingCurrent = current && replacePendingTarget && pendingTarget && sameUrl(current.url, pendingTarget);
|
|
239
|
+
if (current && (sameUrl(current.url, url) || replacingCurrent)) {
|
|
238
240
|
return { index, entry: current };
|
|
239
241
|
}
|
|
240
242
|
return [index - 1, index + 1]
|
|
@@ -111,7 +111,8 @@ export function mountChart({ element, spec, registry }, options = {}) {
|
|
|
111
111
|
async update({ spec: nextSpec, theme = input.theme }, _options = {}) {
|
|
112
112
|
if (destroyed)
|
|
113
113
|
return;
|
|
114
|
-
|
|
114
|
+
revision += 1;
|
|
115
|
+
const updateRevision = revision;
|
|
115
116
|
input = { spec: nextSpec, theme };
|
|
116
117
|
showData();
|
|
117
118
|
const preferred = candidates()[0];
|
|
@@ -34,9 +34,11 @@ export function ChartHost({ spec, registry, theme = DEFAULT_CHART_THEME, sitePro
|
|
|
34
34
|
/** Known wire kinds use the established shapes. Other providers register spec-based entries without
|
|
35
35
|
* adding fields or kinds to the core grammar. This closure is built once per host snapshot.
|
|
36
36
|
*/
|
|
37
|
+
/** Wire kinds that predate spec-based entries and keep their established prop shapes. */
|
|
38
|
+
const LEGACY_CHART_KINDS = ['bar', 'line', 'pie'];
|
|
37
39
|
function chartComponent(kind, providers, options) {
|
|
38
40
|
return function RegisteredChart(props) {
|
|
39
|
-
const legacyKind =
|
|
41
|
+
const legacyKind = LEGACY_CHART_KINDS.find((legacy) => legacy === kind);
|
|
40
42
|
const spec = legacyKind ? legacyChartSpec({ kind: legacyKind, props }) : chartSpecSchema.parse(props.spec);
|
|
41
43
|
return _jsx(ChartHost, { spec: spec, registry: providers, ...options });
|
|
42
44
|
};
|
|
@@ -67,6 +67,8 @@ export function ConnectorsBrowser({ unlocked, providerTabs = DEFAULT_PROVIDER_TA
|
|
|
67
67
|
onCancelAuthorization: (connectorId) => void authorization.cancelAuthorization(connectorId),
|
|
68
68
|
onOpenExternalUrl: (url) => void deps.data.openExternalUrl(url),
|
|
69
69
|
};
|
|
70
|
-
|
|
70
|
+
const categoryLabelProps = getCategoryLabel ? { getCategoryLabel } : {};
|
|
71
|
+
const gateProps = gate ? { gate: { ...gate, onClick: handleGateClick } } : {};
|
|
72
|
+
return (_jsxs("div", { className: "tab-panel connectors-panel connectors-panel-embedded", children: [_jsxs("div", { className: "tab-panel-toolbar", children: [_jsx("div", { className: "toolbar-left connectors-heading", children: _jsxs("div", { children: [_jsx("h2", { children: t('Connectors') }), _jsx("p", { children: t('Connect third-party tools and services.') })] }) }), _jsxs("div", { className: "toolbar-right", children: [_jsx(ProviderTabBar, { tabs: providerTabs, selectedId: selectedProvider, onSelect: handleProviderTabSelect, ...subHandleProp({ base: agentHandle, action: 'provider' }) }), _jsx(ConnectorSearchBar, { value: filter, onChange: setFilter, disabled: !unlocked, onFocus: () => onProviderTabClick?.('search_connectors'), ...subHandleProp({ base: agentHandle, action: 'search' }) })] })] }), _jsx(ConnectorAlertList, { alerts: alerts, onOpenDetails: detail.openDetails }), catalog.loading ? (_jsx(CenteredLoader, { label: t('Loading…') })) : (_jsx(ConnectorGrid, { connectors: filteredConnectors, locked: !unlocked, hasNoResults: hasNoResults, searchQuery: filter, pendingConnectorAction: authorization.pendingConnectorAction, authorizationPending: authorization.pending, authorizationCancelFailed: authorization.cancelFailed, toolsLoaded: catalog.enriched, ...actions, onOpenDetails: detail.openDetails, ...categoryLabelProps, onClearSearch: () => setFilter(''), ...gateProps, ...subHandleProp({ base: agentHandle, action: 'connector' }) })), _jsx(ConnectorsDetailPane, { detail: detail, authorization: authorization, unlocked: unlocked, actions: actions, getCategoryLabel: getCategoryLabel, getDisplayableAccountLabel: getDisplayableAccountLabel, agentHandle: agentHandle })] }));
|
|
71
73
|
}
|
|
72
74
|
//# sourceMappingURL=ConnectorsBrowser.js.map
|
|
@@ -176,7 +176,7 @@ export function useConnectorAuthorization(port, authPendingStorage, authBridge,
|
|
|
176
176
|
// Poll statuses while any authorization is in flight.
|
|
177
177
|
useEffect(() => {
|
|
178
178
|
if (Object.keys(pending).length === 0)
|
|
179
|
-
return;
|
|
179
|
+
return undefined;
|
|
180
180
|
const interval = setInterval(() => {
|
|
181
181
|
setPending((curr) => pruneConnectorAuthorizationPending(curr, Date.now()));
|
|
182
182
|
void reloadStatuses();
|
|
@@ -42,10 +42,10 @@ export function useConnectorCatalog(port, options) {
|
|
|
42
42
|
if (!unlocked || !port.fetchConnectorEnrichment) {
|
|
43
43
|
setEnriched(false);
|
|
44
44
|
setEnriching(false);
|
|
45
|
-
return;
|
|
45
|
+
return undefined;
|
|
46
46
|
}
|
|
47
47
|
if (enriched)
|
|
48
|
-
return;
|
|
48
|
+
return undefined;
|
|
49
49
|
let cancelled = false;
|
|
50
50
|
setEnriching(true);
|
|
51
51
|
void (async () => {
|