@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.
Files changed (183) hide show
  1. package/dist/browser/useDismissOnOutsideOrEscape.js +1 -1
  2. package/dist/browser/useGlobalKeydown.js +2 -2
  3. package/dist/browser/useModalWindowDragGuard.js +1 -1
  4. package/dist/core/theme/css-name.d.ts +3 -0
  5. package/dist/core/theme/css-name.js +5 -0
  6. package/dist/core/theme/stylesheet.js +2 -1
  7. package/dist/core/theme/validation.js +15 -4
  8. package/dist/features/about/rules.js +2 -1
  9. package/dist/features/admin-widgets/components/InfoTip.hooks.js +22 -12
  10. package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.js +1 -1
  11. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.js +1 -1
  12. package/dist/features/admin-widgets/components/SeeMore/SeeMore.hooks.js +2 -2
  13. package/dist/features/admin-widgets/components/SeeMore/SeeMore.js +4 -2
  14. package/dist/features/admin-widgets/components/Select/Select.hooks.js +2 -1
  15. package/dist/features/admin-widgets/components/Select/Select.js +2 -1
  16. package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.js +4 -2
  17. package/dist/features/admin-widgets/components/useDebouncedHtmlPreview.hooks.js +3 -2
  18. package/dist/features/asset-grid/react/components/AssetGrid.js +8 -3
  19. package/dist/features/asset-grid/react/hooks/useAssetGridKeyboardShortcuts.js +7 -6
  20. package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.js +8 -10
  21. package/dist/features/asset-grid/react/hooks/useRubberBandDrag.js +1 -1
  22. package/dist/features/asset-tree-browser/react/components/AssetTreeFileRow.js +6 -8
  23. package/dist/features/asset-tree-browser/react/hooks/useAssetTreeRowMenu.js +1 -1
  24. package/dist/features/asset-tree-browser/rules.js +1 -3
  25. package/dist/features/browser-chrome/rules.js +10 -8
  26. package/dist/features/charts/browser.js +2 -1
  27. package/dist/features/charts/react.js +3 -1
  28. package/dist/features/connectors/ConnectorsBrowser.js +3 -1
  29. package/dist/features/connectors/hooks/useConnectorAuthorization.js +1 -1
  30. package/dist/features/connectors/hooks/useConnectorCatalog.js +2 -2
  31. package/dist/features/connectors/rules.js +10 -14
  32. package/dist/features/execution/react/components/AgentDiagnosticRow.js +2 -0
  33. package/dist/features/execution/react/components/ByokProviderForm.js +1 -3
  34. package/dist/features/execution/react/components/LocalCliAgentSettings.js +2 -1
  35. package/dist/features/execution/react/hooks/use-byok-field-validation.hooks.js +6 -2
  36. package/dist/features/execution/react/hooks/useExecutionTab.js +9 -5
  37. package/dist/features/execution/react/hooks/useLocalCliAgentCard.js +3 -6
  38. package/dist/features/execution/react/hooks/useReasoningControl.js +3 -3
  39. package/dist/features/execution/rules.js +23 -18
  40. package/dist/features/external-mcp/react/components/ExternalMcpTab.js +10 -1
  41. package/dist/features/file-dropzone/rules.js +3 -2
  42. package/dist/features/html-editor/canvas-embed-placeholders.js +2 -3
  43. package/dist/features/html-editor/css.js +7 -5
  44. package/dist/features/html-editor/editor-blocks.js +60 -6
  45. package/dist/features/html-editor/protected-elements.js +5 -3
  46. package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.js +3 -3
  47. package/dist/features/html-editor/source-splice.js +6 -4
  48. package/dist/features/iframe-pool/react/components/IframeKeepAliveProvider.js +5 -5
  49. package/dist/features/iframe-pool/react/dom-sync.js +2 -2
  50. package/dist/features/integrations/react/components/IntegrationsTab.js +3 -1
  51. package/dist/features/integrations/rules.js +6 -4
  52. package/dist/features/interactive-ui/providers/shadcn/data-table.js +10 -5
  53. package/dist/features/interactive-ui/table-data.js +1 -1
  54. package/dist/features/lexical-rich-text-editor/deserialize.js +2 -2
  55. package/dist/features/lexical-rich-text-editor/mention-parser.js +4 -4
  56. package/dist/features/lexical-rich-text-editor/react/components/RichTextInput.js +2 -1
  57. package/dist/features/lexical-rich-text-editor/react/hooks/useCaretFloatingLayerPosition.js +1 -1
  58. package/dist/features/lexical-rich-text-editor/react/hooks/useMentionColorStamping.js +1 -1
  59. package/dist/features/lexical-rich-text-editor/serialize.js +13 -10
  60. package/dist/features/list-detail-panel/react/components/ListDetailPanel.js +1 -1
  61. package/dist/features/mcp-ui/confirmation-store.js +3 -6
  62. package/dist/features/mcp-ui/surfaces/confirmation.d.ts +5 -0
  63. package/dist/features/mcp-ui/surfaces/confirmation.js +1 -0
  64. package/dist/features/mcp-ui/surfaces/document.d.ts +3 -15
  65. package/dist/features/mcp-ui/surfaces/document.js +37 -6
  66. package/dist/features/mcp-ui/surfaces/fields.js +2 -0
  67. package/dist/features/mcp-ui/surfaces/host-theme.js +2 -1
  68. package/dist/features/mcp-ui/surfaces/index.d.ts +1 -0
  69. package/dist/features/mcp-ui/surfaces/index.js +1 -0
  70. package/dist/features/mcp-ui/surfaces/outcome.js +1 -1
  71. package/dist/features/mcp-ui/surfaces/select.js +2 -1
  72. package/dist/features/mcp-ui/surfaces/structured-details.d.ts +40 -0
  73. package/dist/features/mcp-ui/surfaces/structured-details.js +159 -0
  74. package/dist/features/mcp-ui/surfaces/text-input.js +1 -1
  75. package/dist/features/mcp-ui/surfaces/tool-approval.d.ts +21 -0
  76. package/dist/features/mcp-ui/surfaces/tool-approval.js +41 -0
  77. package/dist/features/media-providers/react/hooks/useMediaProvidersTab.js +2 -1
  78. package/dist/features/memory/async-commit-guard.js +4 -1
  79. package/dist/features/memory/formatters.js +5 -1
  80. package/dist/features/memory/react/components/MemoryConnectedPanel.js +3 -1
  81. package/dist/features/memory/react/components/MemoryEntryCard.js +1 -1
  82. package/dist/features/memory/react/components/MemorySettingsPanel.js +3 -1
  83. package/dist/features/memory/react/hooks/useMemoryConfig.hooks.js +12 -10
  84. package/dist/features/memory/react/hooks/useMemoryConnectors.hooks.js +2 -5
  85. package/dist/features/memory/react/hooks/useMemoryConnectors.rules.js +10 -9
  86. package/dist/features/memory/react/hooks/useMemoryEntries.hooks.js +4 -2
  87. package/dist/features/memory/react/hooks/useMemoryFlash.hooks.js +1 -1
  88. package/dist/features/memory/rules.js +2 -1
  89. package/dist/features/panel-kit/fetch-query/adapter.react.js +3 -2
  90. package/dist/features/panel-kit/fetch-query/adapter.tanstack.js +3 -2
  91. package/dist/features/panel-kit/fetch-query/binding.react.js +2 -1
  92. package/dist/features/panel-kit/helpers/standing-draft-recovery-message.js +13 -4
  93. package/dist/features/panel-kit/hooks/use-dirty-guard.hooks.js +1 -1
  94. package/dist/features/panel-kit/hooks/use-focus-trap.hooks.js +1 -1
  95. package/dist/features/panel-kit/hooks/use-settings-slice.hooks.js +14 -10
  96. package/dist/features/panel-kit/hooks/use-standing-draft-autosave.hooks.js +2 -2
  97. package/dist/features/progress-card/components/ProgressCard.d.ts +1 -1
  98. package/dist/features/progress-card/reference-adapters.js +23 -13
  99. package/dist/features/progress-card/rules.d.ts +0 -1
  100. package/dist/features/progress-card/rules.js +14 -20
  101. package/dist/features/progress-card/types.d.ts +1 -1
  102. package/dist/features/project-locations/dependencies.js +6 -1
  103. package/dist/features/project-locations/react/components/ProjectLocationsTab.js +2 -1
  104. package/dist/features/project-locations/rules.js +2 -1
  105. package/dist/features/resource-dashboard/react/components/ResourceBoardToolbar.js +8 -1
  106. package/dist/features/resource-dashboard/react/components/ResourceCardParts.js +2 -1
  107. package/dist/features/resource-dashboard/react/components/ResourceRowListItem.js +3 -1
  108. package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.js +2 -1
  109. package/dist/features/resource-dashboard/react/components/ResourceRowListView.js +1 -1
  110. package/dist/features/resource-dashboard/react/hooks/useResourceBoard.js +1 -1
  111. package/dist/features/schedule-picker/rules.js +13 -7
  112. package/dist/features/sketch-editor/constants.d.ts +0 -6
  113. package/dist/features/sketch-editor/constants.js +17 -15
  114. package/dist/features/sketch-editor/dom.js +4 -7
  115. package/dist/features/sketch-editor/react/components/SketchMainMenu.js +2 -1
  116. package/dist/features/sketch-editor/react/components/SketchSaveStateBadge.js +2 -1
  117. package/dist/features/sketch-editor/react/hooks/useSketchDomEnhancements.js +1 -1
  118. package/dist/features/sketch-editor/rules.js +13 -12
  119. package/dist/features/skills/dependencies.js +9 -4
  120. package/dist/features/skills/react/components/SkillRow.js +2 -1
  121. package/dist/features/skills/react/components/SkillsTab.js +2 -1
  122. package/dist/features/skills/rules.js +3 -3
  123. package/dist/features/source-config-list/agent-handles.js +4 -3
  124. package/dist/features/source-config-list/react/components/SourceConfigItemCard.js +3 -1
  125. package/dist/features/tab-strip/react/components/TabBar.hooks.js +4 -2
  126. package/dist/features/version-manager/react/components/VersionPreviewFrame.js +4 -1
  127. package/dist/features/version-manager/react/components/VersionSidebar.js +8 -6
  128. package/dist/features/version-manager/react/hooks/usePreviewCanvasSize.js +1 -1
  129. package/dist/features/version-manager/react/hooks/useVersionManager.js +5 -7
  130. package/dist/features/version-manager/rules.js +3 -5
  131. package/dist/features/viewer-shell/react/components/CommentSidePanel.js +2 -1
  132. package/dist/features/viewer-shell/react/components/SegmentedToggle.js +2 -1
  133. package/dist/features/viewer-shell/react/components/ViewerShell.js +2 -1
  134. package/dist/features/viewer-shell/rules.js +8 -7
  135. package/dist/react/components/BrandLogo.js +10 -2
  136. package/dist/react/components/CustomSelect.js +1 -1
  137. package/dist/react/components/EditorIcon.js +17 -14
  138. package/dist/react/components/Loading.js +2 -1
  139. package/dist/react/components/OnboardingChipField.js +5 -1
  140. package/dist/react/components/OnboardingDropdown.js +6 -4
  141. package/dist/react/components/PaletteTweaks.js +7 -6
  142. package/dist/react/components/PillButton.js +2 -1
  143. package/dist/react/components/PopoverMenu.js +2 -1
  144. package/dist/react/components/RemixIcon.js +2 -1
  145. package/dist/react/components/SecretInput.hooks.d.ts +2 -25
  146. package/dist/react/components/SecretInput.hooks.js +2 -24
  147. package/dist/react/components/SecretInput.js +5 -4
  148. package/dist/react/components/Toast.js +4 -2
  149. package/dist/react/components/TooltipLayer.js +4 -1
  150. package/dist/react/components/WorkingDirPicker.js +5 -4
  151. package/dist/react/components/remixicon-font-recovery.js +9 -1
  152. package/dist/react/components/select-popover.js +2 -2
  153. package/dist/react/hooks/useBrandFonts.js +1 -1
  154. package/dist/react/hooks/useEdgeAutoScroll.js +1 -1
  155. package/dist/react/hooks/useInView.js +9 -9
  156. package/dist/react/hooks/useResizableSplitPane.js +12 -20
  157. package/dist/react/mcp-ui/useHostSurfaceTokens.js +1 -1
  158. package/dist/renderers/annotation-canvas/react/components/AnnotationCanvas.js +7 -1
  159. package/dist/renderers/annotation-canvas/react/hooks/useAnnotationCanvas.controller.js +4 -2
  160. package/dist/renderers/annotation-canvas/react/hooks/useAnnotationCanvas.js +9 -5
  161. package/dist/renderers/annotation-canvas/rules.js +9 -13
  162. package/dist/renderers/annotation-canvas/theme.js +5 -2
  163. package/dist/renderers/renderers/index.js +1 -1
  164. package/dist/renderers/renderers/markdown.js +4 -6
  165. package/dist/theme/apply.js +7 -3
  166. package/dist/utils/class-names.d.ts +3 -0
  167. package/dist/utils/class-names.js +5 -0
  168. package/dist/utils/design-md.js +3 -3
  169. package/dist/utils/ime-composing.d.ts +1 -1
  170. package/dist/utils/ime-composing.js +1 -2
  171. package/dist/utils/localized-url.js +2 -11
  172. package/dist/utils/markdown-scroll-sync.js +1 -1
  173. package/dist/utils/omit-keys.d.ts +3 -0
  174. package/dist/utils/omit-keys.js +8 -0
  175. package/dist/utils/polygon-selection.js +1 -1
  176. package/dist/utils/sse.js +3 -0
  177. package/dist/utils/trim-chars.d.ts +13 -0
  178. package/dist/utils/trim-chars.js +30 -0
  179. package/dist/utils/unhandled-case.d.ts +10 -0
  180. package/dist/utils/unhandled-case.js +12 -0
  181. package/dist/utils/unit-bezier.js +1 -3
  182. package/dist/utils/zip.js +6 -10
  183. 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();
@@ -0,0 +1,3 @@
1
+ /** `camelCase` theme key → `kebab-case` CSS custom-property segment (`surfaceMuted` → `surface-muted`). */
2
+ export declare function kebabThemeKey(key: string): string;
3
+ //# sourceMappingURL=css-name.d.ts.map
@@ -0,0 +1,5 @@
1
+ /** `camelCase` theme key → `kebab-case` CSS custom-property segment (`surfaceMuted` → `surface-muted`). */
2
+ export function kebabThemeKey(key) {
3
+ return key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
4
+ }
5
+ //# sourceMappingURL=css-name.js.map
@@ -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.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}:${theme[colorScheme][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) => typeof value === 'string' && value.trim().length > 0
4
- && value.length <= 512 && !/[;{}\x00-\x1f<>\\]/.test(value) && !/url\s*\(|\/\*|\*\//i.test(value);
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 }) => record(fonts) && safeValue(fonts.body) && safeValue(fonts.heading) && (fonts.mono === undefined || safeValue(fonts.mono));
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 }) => density === undefined || (typeof density === 'number' && Number.isFinite(density) && density >= 0.5 && density <= 2);
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 ? (payload ? K.installRestart : K.installNow) : null,
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
- /** The close of the one tip open app-wide. A pinned or focused tip used to stay up beside the one
11
- * the pointer had just reached (owner, 2026-10-25: two "Soon" tips open at once). */
12
- let closeOpenTip = null;
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
- if (closeOpenTip && closeOpenTip !== close)
22
- closeOpenTip();
23
- closeOpenTip = close;
24
- return () => {
25
- if (closeOpenTip === close)
26
- closeOpenTip = null;
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: `see-more-text${expanded ? " is-expanded" : ""}${textClassName ? ` ${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), ...(handle ? agentHandle({ handle: handle }, { role: "button", label: toggleAriaLabel ?? view.toggleLabel }) : {}), children: view.toggleLabel })) : null] }));
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
- setHighlightedIndex(initialIndex >= 0 ? initialIndex : options.length ? 0 : -1);
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
- 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), ...(searchHandle ? agentHandle({ handle: searchHandle }, { role: "field", label: "Search the option list" }) : {}) })) : 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)))) })] }));
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
- return onKeyDown?.(event);
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 }); }, () => { if (current)
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
- const idSet = new Set(ids);
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((prev) => prev.filter((a) => a.id !== id));
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
- else if (event.key === 'Delete' || event.key === 'Backspace') {
11
- if (typing || isPreviewOpen || !hasSelection)
12
- return;
13
- event.preventDefault();
14
- onRequestDeleteSelected();
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
- try {
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 === 'Enter') {
52
- event.preventDefault();
53
- event.currentTarget.dataset.skipRenameCommit = '1';
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.replace(/\/+$/, ''), title: cleanTitle };
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.replace(/\/+$/, '') === right.replace(/\/+$/, '');
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
- return (typeof record.url === 'string'
102
- && typeof record.title === 'string'
103
- && typeof record.lastVisitedAt === 'number'
104
- && typeof record.visitCount === 'number'
105
- && (record.iconUrl === undefined || typeof record.iconUrl === 'string'));
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
- if (current && (sameUrl(current.url, url) || (replacePendingTarget && pendingTarget && sameUrl(current.url, pendingTarget)))) {
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
- const updateRevision = ++revision;
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 = kind === 'bar' ? 'bar' : kind === 'line' ? 'line' : kind === 'pie' ? 'pie' : undefined;
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
- 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, ...(getCategoryLabel ? { getCategoryLabel } : {}), onClearSearch: () => setFilter(''), ...(gate ? { gate: { ...gate, onClick: handleGateClick } } : {}), ...subHandleProp({ base: agentHandle, action: 'connector' }) })), _jsx(ConnectorsDetailPane, { detail: detail, authorization: authorization, unlocked: unlocked, actions: actions, getCategoryLabel: getCategoryLabel, getDisplayableAccountLabel: getDisplayableAccountLabel, agentHandle: agentHandle })] }));
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 () => {