@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
@@ -89,11 +89,9 @@ export function filterVersionsBySearch(versions, search, describe) {
89
89
  * current, no restoring mid-fetch, no restoring stale/mismatched content). */
90
90
  export function isRestoreDisabled(input) {
91
91
  const { selectedVersion, restoring, loadingContent, selectedContentMatchesVersion } = input;
92
- return (!selectedVersion ||
93
- selectedVersion.current ||
94
- restoring ||
95
- loadingContent ||
96
- !selectedContentMatchesVersion);
92
+ if (!selectedVersion || selectedVersion.current)
93
+ return true;
94
+ return restoring || loadingContent || !selectedContentMatchesVersion;
97
95
  }
98
96
  /** Whether the currently-loaded preview content actually corresponds to the
99
97
  * currently-selected version (guards against acting on stale content
@@ -113,7 +113,8 @@ function CommentSideSelectBar({ selectedCount, sending, sendDisabled, queueOnSen
113
113
  const t = useT();
114
114
  if (selectedCount === 0)
115
115
  return null;
116
- return (_jsxs("div", { className: "comment-side-selectbar", "data-testid": "comment-side-selectbar", children: [_jsx("span", { className: "comment-side-selectcount", children: t('{n} selected', { n: selectedCount }) }), _jsx("button", { type: "button", className: "comment-side-clear", onClick: onClearSelection, children: t('Clear') }), _jsx("button", { type: "button", className: "comment-side-send", "data-testid": "comment-side-send-claude", disabled: sending || sendDisabled, onClick: () => void onSendSelected(), children: sending ? t('Sending…') : queueOnSend ? t('Queue') : t('Send to chat') })] }));
116
+ const idleSendLabel = queueOnSend ? t('Queue') : t('Send to chat');
117
+ return (_jsxs("div", { className: "comment-side-selectbar", "data-testid": "comment-side-selectbar", children: [_jsx("span", { className: "comment-side-selectcount", children: t('{n} selected', { n: selectedCount }) }), _jsx("button", { type: "button", className: "comment-side-clear", onClick: onClearSelection, children: t('Clear') }), _jsx("button", { type: "button", className: "comment-side-send", "data-testid": "comment-side-send-claude", disabled: sending || sendDisabled, onClick: () => void onSendSelected(), children: sending ? t('Sending…') : idleSendLabel })] }));
117
118
  }
118
119
  function CommentSideCreateForm({ renderCreateForm, draft, onDraftChange, onCreateComment, sending, sendDisabled, }) {
119
120
  const t = useT();
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { RemixIcon } from '../../../../react/components/RemixIcon.js';
3
+ import { withClassName } from '../../../../utils/class-names.js';
3
4
  /**
4
5
  * A small, always-visible group of toggle buttons, one active at a time.
5
6
  * This is the shared primitive behind two call sites that turned out to be
@@ -14,7 +15,7 @@ import { RemixIcon } from '../../../../react/components/RemixIcon.js';
14
15
  * primitives for a minor ARIA-pattern difference.
15
16
  */
16
17
  export function SegmentedToggle({ options, value, onChange, ariaLabel, className, }) {
17
- return (_jsx("div", { className: `viewer-segmented-toggle${className ? ` ${className}` : ''}`, role: "group", "aria-label": ariaLabel, children: options.map((option) => {
18
+ return (_jsx("div", { className: withClassName('viewer-segmented-toggle', className), role: "group", "aria-label": ariaLabel, children: options.map((option) => {
18
19
  const selected = option.value === value;
19
20
  const label = option.title ?? option.label;
20
21
  return (_jsxs("button", { type: "button", className: `viewer-segmented-toggle-button${selected ? ' active' : ''}`, "aria-pressed": selected, "aria-label": label, title: label, onClick: () => onChange(option.value), children: [option.icon ? _jsx(RemixIcon, { name: option.icon, size: 14 }) : null, _jsx("span", { children: option.label })] }, option.value));
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { withClassName } from '../../../../utils/class-names.js';
2
3
  /**
3
4
  * The "viewer-toolbar + viewer-body" chrome independently repeated across
4
5
  * 8 media-viewer components in the source file (`BinaryViewer`,
@@ -9,7 +10,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
10
  * layout wrapper.
10
11
  */
11
12
  export function ViewerShell({ kindClassName, toolbarLeft, toolbarActions, bodyClassName, children }) {
12
- return (_jsxs("div", { className: `viewer${kindClassName ? ` ${kindClassName}` : ''}`, children: [_jsxs("div", { className: "viewer-toolbar", children: [_jsx("div", { className: "viewer-toolbar-left", children: toolbarLeft }), toolbarActions ? _jsx("div", { className: "viewer-toolbar-actions", children: toolbarActions }) : null] }), _jsx("div", { className: `viewer-body${bodyClassName ? ` ${bodyClassName}` : ''}`, children: children })] }));
13
+ return (_jsxs("div", { className: withClassName('viewer', kindClassName), children: [_jsxs("div", { className: "viewer-toolbar", children: [_jsx("div", { className: "viewer-toolbar-left", children: toolbarLeft }), toolbarActions ? _jsx("div", { className: "viewer-toolbar-actions", children: toolbarActions }) : null] }), _jsx("div", { className: withClassName('viewer-body', bodyClassName), children: children })] }));
13
14
  }
14
15
  /** Trivial "nothing to show" placeholder shared by every viewer body. */
15
16
  export function ViewerEmptyState({ children }) {
@@ -97,13 +97,14 @@ export function hasPrecisionSensitiveJsonNumberText(text) {
97
97
  }
98
98
  numberTokenPattern.lastIndex = i;
99
99
  const match = numberTokenPattern.exec(text);
100
- if (!match) {
100
+ if (match) {
101
+ if (isPrecisionSensitiveNumberLiteral(match[0]))
102
+ return true;
103
+ i = numberTokenPattern.lastIndex;
104
+ }
105
+ else {
101
106
  i += 1;
102
- continue;
103
107
  }
104
- if (isPrecisionSensitiveNumberLiteral(match[0]))
105
- return true;
106
- i = numberTokenPattern.lastIndex;
107
108
  }
108
109
  return false;
109
110
  }
@@ -111,10 +112,10 @@ export function hasPrecisionSensitiveJsonNumberText(text) {
111
112
  * `start` (honouring `\` escapes), or `text.length` when it is unterminated. */
112
113
  function indexAfterJsonString(text, start) {
113
114
  for (let i = start; i < text.length; i += 1) {
115
+ if (text[i] === '"')
116
+ return i + 1;
114
117
  if (text[i] === '\\')
115
118
  i += 1;
116
- else if (text[i] === '"')
117
- return i + 1;
118
119
  }
119
120
  return text.length;
120
121
  }
@@ -18,12 +18,20 @@ export function brandMonogram(name) {
18
18
  }
19
19
  /** The stage to start at, given which sources are available. */
20
20
  export function firstLogoStage(opts) {
21
- return opts.canUseBrandStage ? 'brand' : opts.logoSrc ? 'custom' : opts.host ? 'favicon' : 'letter';
21
+ if (opts.canUseBrandStage)
22
+ return 'brand';
23
+ return fallbackLogoStage(opts);
24
+ }
25
+ /** The first non-brand stage whose source is available. */
26
+ function fallbackLogoStage(opts) {
27
+ if (opts.logoSrc)
28
+ return 'custom';
29
+ return opts.host ? 'favicon' : 'letter';
22
30
  }
23
31
  /** The next stage to try after the current one fails to load. */
24
32
  export function advanceLogoStage(stage, opts) {
25
33
  if (stage === 'brand')
26
- return opts.logoSrc ? 'custom' : opts.host ? 'favicon' : 'letter';
34
+ return fallbackLogoStage(opts);
27
35
  if (stage === 'custom')
28
36
  return 'favicon';
29
37
  return 'letter';
@@ -233,7 +233,7 @@ export function useCustomSelectOriginTheme({ buttonRef, open, portal }, _optiona
233
233
  const [attributes, setAttributes] = useState({});
234
234
  useLayoutEffect(() => {
235
235
  if (!open || !portal)
236
- return;
236
+ return undefined;
237
237
  const update = () => {
238
238
  const next = resolveCustomSelectOriginTheme({ trigger: buttonRef.current });
239
239
  setAttributes((current) => current['data-admin-theme'] === next['data-admin-theme']
@@ -36,21 +36,24 @@ const xcodePath = 'M19.06 5.3327c.4517-.1936.7744-.2581 1.097-.1936.5163.1291.77
36
36
  const webstormPath = 'M0 0v24h24V0H0zm17.889 2.889c1.444 0 2.667.444 3.667 1.278l-1.111 1.667c-.889-.611-1.722-1-2.556-1s-1.278.389-1.278.889v.056c0 .667.444.889 2.111 1.333 2 .556 3.111 1.278 3.111 3v.056c0 2-1.5 3.111-3.611 3.111-1.5-.056-3-.611-4.167-1.667l1.278-1.556c.889.722 1.833 1.222 2.944 1.222.889 0 1.389-.333 1.389-.944v-.056c0-.556-.333-.833-2-1.278-2-.5-3.222-1.056-3.222-3.056v-.056c0-1.833 1.444-3 3.444-3zm-16.111.222h2.278l1.5 5.778 1.722-5.778h1.667l1.667 5.778 1.5-5.778h2.333l-2.833 9.944H9.723L8.112 7.277l-1.667 5.778H4.612L1.779 3.111zm.5 16.389h9V21h-9v-1.5z';
37
37
  const ideaPath = 'M0 0v24h24V0zm3.723 3.111h5v1.834h-1.39v6.277h1.39v1.834h-5v-1.834h1.444V4.945H3.723zm11.055 0H17v6.5c0 .612-.055 1.111-.222 1.556-.167.444-.39.777-.723 1.11-.277.279-.666.557-1.11.668a3.933 3.933 0 0 1-1.445.278c-.778 0-1.444-.167-1.944-.445a4.81 4.81 0 0 1-1.279-1.056l1.39-1.555c.277.334.555.555.833.722.277.167.611.278.945.278.389 0 .721-.111 1-.389.221-.278.333-.667.333-1.278zM2.222 19.5h9V21h-9z';
38
38
  const warpPath = 'M12.035 2.723h9.253A2.712 2.712 0 0 1 24 5.435v10.529a2.712 2.712 0 0 1-2.712 2.713H8.047Zm-1.681 2.6L6.766 19.677h5.598l-.399 1.6H2.712A2.712 2.712 0 0 1 0 18.565V8.036a2.712 2.712 0 0 1 2.712-2.712Z';
39
+ const PRIMARY = 'var(--jini-primary)';
40
+ const BG = 'var(--jini-bg)';
41
+ const TEXT = 'var(--jini-text)';
39
42
  const EDITORS = {
40
- vscode: { bg: 'var(--jini-primary)', fg: 'var(--jini-bg)', glyph: vscodeLogo },
41
- cursor: { bg: 'var(--jini-text)', fg: 'var(--jini-bg)', glyph: simplePath(cursorPath) },
42
- windsurf: { bg: 'var(--jini-bg)', fg: 'var(--jini-text)', glyph: simplePath(windsurfPath) },
43
- zed: { bg: 'var(--jini-primary)', fg: 'var(--jini-bg)', glyph: simplePath(zedPath) },
44
- qoder: { bg: 'var(--jini-warning)', fg: 'var(--jini-text)', glyph: qoderLogo },
45
- antigravity: { bg: 'var(--jini-bg)', fg: 'var(--jini-text)', glyph: antigravityLogo },
46
- webstorm: { bg: 'var(--jini-text)', fg: 'var(--jini-bg)', glyph: simplePath(webstormPath) },
47
- idea: { bg: 'var(--jini-text)', fg: 'var(--jini-bg)', glyph: simplePath(ideaPath) },
48
- xcode: { bg: 'var(--jini-primary)', fg: 'var(--jini-bg)', glyph: simplePath(xcodePath) },
49
- finder: { bg: 'var(--jini-primary)', fg: 'var(--jini-bg)', glyph: finderLogo },
50
- explorer: { bg: 'var(--jini-warning)', fg: 'var(--jini-text)', glyph: folderLogo },
51
- 'file-manager': { bg: 'var(--jini-muted)', fg: 'var(--jini-bg)', glyph: folderLogo },
52
- terminal: { bg: 'var(--jini-text)', fg: 'var(--jini-success)', glyph: terminalLogo },
53
- warp: { bg: 'var(--jini-primary)', fg: 'var(--jini-bg)', glyph: simplePath(warpPath) },
43
+ vscode: { bg: PRIMARY, fg: BG, glyph: vscodeLogo },
44
+ cursor: { bg: TEXT, fg: BG, glyph: simplePath(cursorPath) },
45
+ windsurf: { bg: BG, fg: TEXT, glyph: simplePath(windsurfPath) },
46
+ zed: { bg: PRIMARY, fg: BG, glyph: simplePath(zedPath) },
47
+ qoder: { bg: 'var(--jini-warning)', fg: TEXT, glyph: qoderLogo },
48
+ antigravity: { bg: BG, fg: TEXT, glyph: antigravityLogo },
49
+ webstorm: { bg: TEXT, fg: BG, glyph: simplePath(webstormPath) },
50
+ idea: { bg: TEXT, fg: BG, glyph: simplePath(ideaPath) },
51
+ xcode: { bg: PRIMARY, fg: BG, glyph: simplePath(xcodePath) },
52
+ finder: { bg: PRIMARY, fg: BG, glyph: finderLogo },
53
+ explorer: { bg: 'var(--jini-warning)', fg: TEXT, glyph: folderLogo },
54
+ 'file-manager': { bg: 'var(--jini-muted)', fg: BG, glyph: folderLogo },
55
+ terminal: { bg: TEXT, fg: 'var(--jini-success)', glyph: terminalLogo },
56
+ warp: { bg: PRIMARY, fg: BG, glyph: simplePath(warpPath) },
54
57
  };
55
58
  /**
56
59
  * Renders an editor/target glyph by string key using the host's theme, with a
@@ -1,10 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Icon } from './Icon.js';
3
+ import { withClassName } from '../../utils/class-names.js';
3
4
  export function Spinner({ size = 14, label }) {
4
5
  return (_jsxs("span", { className: "loading-spinner", role: "status", "aria-live": "polite", children: [_jsx(Icon, { name: "spinner", size: size }), label ? _jsx("span", { className: "loading-spinner-label", children: label }) : null] }));
5
6
  }
6
7
  export function Skeleton({ width, height = 14, radius = 6, className }) {
7
- return (_jsx("span", { className: `skeleton-block${className ? ` ${className}` : ''}`, style: { width, height, borderRadius: radius }, "aria-hidden": true }));
8
+ return (_jsx("span", { className: withClassName('skeleton-block', className), style: { width, height, borderRadius: radius }, "aria-hidden": true }));
8
9
  }
9
10
  /**
10
11
  * Card-shaped skeleton for grid layouts (thumbnail area over a row of meta
@@ -1,7 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /** A single-select value as a selection list; an empty string selects nothing. */
3
+ function singleSelection(value) {
4
+ return value ? [value] : [];
5
+ }
2
6
  export function OnboardingChipField(props) {
3
7
  const { label, options, className } = props;
4
- const selected = props.multiple ? props.value : props.value ? [props.value] : [];
8
+ const selected = props.multiple ? props.value : singleSelection(props.value);
5
9
  return (_jsxs("div", { className: ['onboarding-chip-field', className].filter(Boolean).join(' '), children: [_jsx("span", { className: "onboarding-chip-field__label", children: label }), _jsx("div", { className: "onboarding-chip-field__chips", children: options.map((option) => {
6
10
  const active = selected.includes(option.value);
7
11
  return (_jsx("button", { type: "button", className: `onboarding-chip${active ? ' is-selected' : ''}`, "aria-pressed": active, onClick: () => {
@@ -51,7 +51,9 @@ export const ONBOARDING_MENU_FALLBACK_VIEWPORT = 720;
51
51
  * string (or otherwise falsy value) becomes an empty selection.
52
52
  */
53
53
  export function resolveOnboardingSelectedValues(value) {
54
- return Array.isArray(value) ? value : value ? [value] : [];
54
+ if (Array.isArray(value))
55
+ return value;
56
+ return value ? [value] : [];
55
57
  }
56
58
  /**
57
59
  * Compute the trigger's visible label. In multiple mode it joins every
@@ -121,7 +123,7 @@ export function useOnboardingDropdownPlacement(rootRef, open, placement, optionC
121
123
  });
122
124
  useLayoutEffect(() => {
123
125
  if (!open)
124
- return;
126
+ return undefined;
125
127
  function measureMenu() {
126
128
  const node = rootRef.current;
127
129
  if (!node)
@@ -153,11 +155,11 @@ export function useOnboardingDropdown(props) {
153
155
  const [open, setOpen] = useState(false);
154
156
  const [query, setQuery] = useState('');
155
157
  const rootRef = useRef(null);
156
- const dropdownIdRef = useRef(`onboarding-dropdown-${Math.random().toString(36).slice(2)}`);
158
+ const dropdownIdRef = useRef(`onboarding-dropdown-${globalThis.crypto.getRandomValues(new Uint32Array(1))[0].toString(36)}`);
157
159
  const { placement: resolvedPlacement, maxHeight: menuMaxHeight } = useOnboardingDropdownPlacement(rootRef, open, placement, options.length);
158
160
  useEffect(() => {
159
161
  if (!open)
160
- return;
162
+ return undefined;
161
163
  function handlePointerDown(event) {
162
164
  if (!rootRef.current?.contains(event.target)) {
163
165
  setOpen(false);
@@ -10,12 +10,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
10
  // hook-testable in isolation.
11
11
  import { useCallback, useEffect, useRef, useState } from 'react';
12
12
  import { Icon } from './Icon.js';
13
+ const PRIMARY = 'var(--jini-primary)';
13
14
  export const PALETTE_TWEAKS_SWATCHES = [
14
- { id: 'coral', label: 'Coral - default', stripe: ['var(--jini-primary)', 'var(--jini-primary)', 'var(--jini-primary)', 'var(--jini-primary)'] },
15
- { id: 'electric', label: 'Electric', stripe: ['var(--jini-primary)', 'var(--jini-primary)', 'var(--jini-primary)', 'var(--jini-primary)'] },
16
- { id: 'acid-forest', label: 'Acid forest', stripe: ['var(--jini-primary)', 'var(--jini-primary)', 'var(--jini-primary)', 'var(--jini-primary)'] },
17
- { id: 'risograph', label: 'Risograph', stripe: ['var(--jini-primary)', 'var(--jini-primary)', 'var(--jini-primary)', 'var(--jini-primary)'] },
18
- { id: 'mono-noir', label: 'Mono noir', stripe: ['var(--jini-text)', 'var(--jini-primary)', 'var(--jini-primary)', 'var(--jini-primary)'] },
15
+ { id: 'coral', label: 'Coral - default', stripe: [PRIMARY, PRIMARY, PRIMARY, PRIMARY] },
16
+ { id: 'electric', label: 'Electric', stripe: [PRIMARY, PRIMARY, PRIMARY, PRIMARY] },
17
+ { id: 'acid-forest', label: 'Acid forest', stripe: [PRIMARY, PRIMARY, PRIMARY, PRIMARY] },
18
+ { id: 'risograph', label: 'Risograph', stripe: [PRIMARY, PRIMARY, PRIMARY, PRIMARY] },
19
+ { id: 'mono-noir', label: 'Mono noir', stripe: ['var(--jini-text)', PRIMARY, PRIMARY, PRIMARY] },
19
20
  ];
20
21
  // --- Pure derivations (module-level, directly unit-testable) --------------
21
22
  /**
@@ -71,7 +72,7 @@ export function usePaletteDismiss(options) {
71
72
  const rootRef = useRef(null);
72
73
  useEffect(() => {
73
74
  if (!open)
74
- return;
75
+ return undefined;
75
76
  function onDoc(ev) {
76
77
  if (!rootRef.current)
77
78
  return;
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { withClassName } from '../../utils/class-names.js';
2
3
  /**
3
4
  * Generic labeled trigger pill: icon + label (+ optional trailing icon),
4
5
  * togglable `active` state, and an optional anchored children slot for a
@@ -9,6 +10,6 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
10
  * (recon r6 §1.19) — generic popover-trigger chrome, no product types.
10
11
  */
11
12
  export function PillButton({ icon, label, active = false, 'aria-label': ariaLabel, trailingIcon, onClick, disabled = false, className, children, }) {
12
- return (_jsxs("div", { className: `jini-pill-wrap${className ? ` ${className}` : ''}`, children: [_jsxs("button", { type: "button", className: `jini-pill${active ? ' is-active' : ''}`, "aria-label": ariaLabel, "aria-expanded": children ? active : undefined, onClick: onClick, disabled: disabled, children: [icon, _jsx("span", { className: "jini-pill__label", children: label }), trailingIcon] }), children] }));
13
+ return (_jsxs("div", { className: withClassName('jini-pill-wrap', className), children: [_jsxs("button", { type: "button", className: `jini-pill${active ? ' is-active' : ''}`, "aria-label": ariaLabel, "aria-expanded": children ? active : undefined, onClick: onClick, disabled: disabled, children: [icon, _jsx("span", { className: "jini-pill__label", children: label }), trailingIcon] }), children] }));
13
14
  }
14
15
  //# sourceMappingURL=PillButton.js.map
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { withClassName } from '../../utils/class-names.js';
2
3
  /**
3
4
  * Generic popover panel shell: a plain positioned wrapper around a list of
4
5
  * `PopoverItem`s (or any other content). No open/closed state of its own —
@@ -8,6 +9,6 @@ import { jsx as _jsx } from "react/jsx-runtime";
8
9
  * (recon r6 §1.19) — generic, no product types.
9
10
  */
10
11
  export function PopoverMenu({ children, className }) {
11
- return _jsx("div", { className: `jini-popover${className ? ` ${className}` : ''}`, children: children });
12
+ return _jsx("div", { className: withClassName('jini-popover', className), children: children });
12
13
  }
13
14
  //# sourceMappingURL=PopoverMenu.js.map
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect } from 'react';
3
3
  import { ensureRemixIconStylesheet } from './RemixIcon.stylesheet.js';
4
+ import { withClassName } from '../../utils/class-names.js';
4
5
  export { installRemixIconStylesheet, REMIXICON_STYLESHEET_MARKER } from './RemixIcon.stylesheet.js';
5
6
  /**
6
7
  * Thin wrapper around a RemixIcon webfont glyph (`ri-<name>`). Loads this package's own default
@@ -12,7 +13,7 @@ export function RemixIcon({ name, size = 14, className, style }) {
12
13
  useEffect(() => {
13
14
  ensureRemixIconStylesheet({});
14
15
  }, []);
15
- return (_jsx("i", { className: `ri-${name}${className ? ` ${className}` : ''}`, "aria-hidden": "true", style: {
16
+ return (_jsx("i", { className: withClassName(`ri-${name}`, className), "aria-hidden": "true", style: {
16
17
  fontSize: size,
17
18
  lineHeight: 1,
18
19
  width: size,
@@ -1,26 +1,3 @@
1
- export interface SecretRevealRequired {
2
- /** The input's current value. A transition to `''` re-hides (the usual sign a save succeeded). */
3
- readonly value: unknown;
4
- /** The toggle's full accessible names, translated ("Show API key" / "Hide API key"). */
5
- readonly labels: {
6
- readonly show: string;
7
- readonly hide: string;
8
- };
9
- }
10
- export interface SecretRevealOptional {
11
- /** Controlled mode: the host owns the shown/hidden state (e.g. to reset it per provider). */
12
- readonly revealed?: boolean;
13
- readonly onRevealedChange?: (revealed: boolean) => void;
14
- }
15
- /**
16
- * The show/hide state behind `SecretInput`'s eye toggle. Starts hidden. Uncontrolled, it re-hides on
17
- * the value's transition to empty only — an empty field may still be revealed before pasting.
18
- * @complexity O(1).
19
- */
20
- export declare function useSecretReveal(required: SecretRevealRequired, optional?: SecretRevealOptional): {
21
- readonly revealed: boolean;
22
- readonly inputType: "text" | "password";
23
- readonly toggleLabel: string;
24
- readonly toggle: () => void;
25
- };
1
+ /** Compatibility entry; ui-kit owns the shared secret reveal lifecycle. */
2
+ export { useSecretReveal, type SecretRevealRequired, type SecretRevealOptional } from "@jini-ai/ui-kit/react";
26
3
  //# sourceMappingURL=SecretInput.hooks.d.ts.map
@@ -1,25 +1,3 @@
1
- import { useState } from 'react';
2
- /**
3
- * The show/hide state behind `SecretInput`'s eye toggle. Starts hidden. Uncontrolled, it re-hides on
4
- * the value's transition to empty only — an empty field may still be revealed before pasting.
5
- * @complexity O(1).
6
- */
7
- export function useSecretReveal(required, optional = {}) {
8
- const [ownRevealed, setOwnRevealed] = useState(false);
9
- const [lastValue, setLastValue] = useState(required.value);
10
- if (required.value !== lastValue) {
11
- setLastValue(required.value);
12
- if (required.value === '')
13
- setOwnRevealed(false);
14
- }
15
- const revealed = optional.revealed ?? ownRevealed;
16
- const toggleLabel = revealed ? required.labels.hide : required.labels.show;
17
- const toggle = () => {
18
- if (optional.onRevealedChange)
19
- optional.onRevealedChange(!revealed);
20
- else
21
- setOwnRevealed(!revealed);
22
- };
23
- return { revealed, inputType: revealed ? 'text' : 'password', toggleLabel, toggle };
24
- }
1
+ /** Compatibility entry; ui-kit owns the shared secret reveal lifecycle. */
2
+ export { useSecretReveal } from "@jini-ai/ui-kit/react";
25
3
  //# sourceMappingURL=SecretInput.hooks.js.map
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { forwardRef } from 'react';
3
+ import { SecretInput as KitSecretInput } from '@jini-ai/ui-kit/react';
3
4
  import { useT } from '../../features/i18n/index.js';
4
5
  import { Icon } from './Icon.js';
5
6
  import { useSecretReveal } from './SecretInput.hooks.js';
@@ -13,10 +14,10 @@ function useSecretToggle({ label, toggleLabels, toggleAttrs, revealed, onReveale
13
14
  return { reveal, button };
14
15
  }
15
16
  export const SecretInput = forwardRef(function SecretInput(props, ref) {
16
- // The toggle props are pulled off so they never reach the DOM `<input>`.
17
- const { label, toggleLabels, toggleAttrs, revealed, onRevealedChange, ...inputProps } = props;
18
- const { reveal, button } = useSecretToggle(props, { value: inputProps.value, disabled: inputProps.disabled });
19
- return (_jsxs("span", { className: "jini-secret", children: [_jsx("input", { ...inputProps, spellCheck: false, ref: ref, type: reveal.inputType }), button] }));
17
+ const t = useT();
18
+ return _jsx(KitSecretInput, { ...props, ref: ref, toggleLabels: props.toggleLabels ?? {
19
+ show: `${t('Show')} ${props.label}`, hide: `${t('Hide')} ${props.label}`,
20
+ } });
20
21
  });
21
22
  export const SecretTextarea = forwardRef(function SecretTextarea(props, ref) {
22
23
  const { label, toggleLabels, toggleAttrs, revealed, onRevealedChange, style, maskedValue, ...textareaProps } = props;
@@ -15,6 +15,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
15
  // hook-testable in isolation.
16
16
  import { useCallback, useEffect, useRef, useState, } from 'react';
17
17
  import { Icon } from './Icon.js';
18
+ import { withClassName } from '../../utils/class-names.js';
18
19
  export const TOAST_DEFAULT_TTL = 4000;
19
20
  // Exit fade duration — kept in sync with the `.jini-toast.leaving` CSS
20
21
  // animation the host supplies. The fade plays inside the TTL window (it
@@ -65,7 +66,8 @@ export function toastFadeDelay(effectiveTtl) {
65
66
  /** The root `className` string, composed from tone/placement/extra/leaving. */
66
67
  export function toastClassName(options) {
67
68
  const { tone, placement, className, leaving } = options;
68
- return `jini-toast tone-${tone} placement-${placement}${className ? ` ${className}` : ''}${leaving ? ' leaving' : ''}`;
69
+ const base = withClassName(`jini-toast tone-${tone} placement-${placement}`, className);
70
+ return leaving ? `${base} leaving` : base;
69
71
  }
70
72
  // --- Hooks (co-located, exported) -----------------------------------------
71
73
  /**
@@ -105,7 +107,7 @@ export function useToastAutoDismiss(options) {
105
107
  // prior leaving state before re-arming the timers.
106
108
  setLeaving(false);
107
109
  if (!toastShouldAutoDismiss(hasDismiss, effectiveTtl))
108
- return;
110
+ return undefined;
109
111
  const fadeId = window.setTimeout(() => setLeaving(true), toastFadeDelay(effectiveTtl));
110
112
  const dismissId = window.setTimeout(fireDismiss, effectiveTtl);
111
113
  return () => {
@@ -46,6 +46,9 @@ export function positionTooltip(target, tooltip, placement) {
46
46
  left = rect.right + TOOLTIP_GAP;
47
47
  top = rect.top + rect.height / 2 - tip.height / 2;
48
48
  }
49
+ else {
50
+ // 'top': the centred-above position computed above already applies.
51
+ }
49
52
  return {
50
53
  x: Math.round(clamp(left, TOOLTIP_MARGIN, maxLeft)),
51
54
  y: Math.round(clamp(top, TOOLTIP_MARGIN, maxTop)),
@@ -180,7 +183,7 @@ export function useTooltipLayer() {
180
183
  }, [restoreNativeTitle]);
181
184
  useEffect(() => {
182
185
  if (!state?.target || typeof MutationObserver === 'undefined')
183
- return;
186
+ return undefined;
184
187
  const target = state.target;
185
188
  const observer = new MutationObserver(() => {
186
189
  if (!isTooltipTarget(target)) {
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useId, useRef, useState, } from 'react';
3
3
  import { Icon } from './Icon.js';
4
+ import { withClassName } from '../../utils/class-names.js';
4
5
  export const DEFAULT_WORKING_DIR_LABELS = {
5
6
  missing: 'Directory no longer exists',
6
7
  hint: 'Select a working directory',
@@ -45,7 +46,7 @@ export function useDismissablePanel(onOpen) {
45
46
  }, [open, onOpen]);
46
47
  useEffect(() => {
47
48
  if (!open)
48
- return;
49
+ return undefined;
49
50
  function onPointer(event) {
50
51
  if (wrapRef.current?.contains(event.target))
51
52
  return;
@@ -175,10 +176,10 @@ function useWorkingDirFocusHandoff({ open, triggerRef, panelRef }) {
175
176
  if (open) {
176
177
  wasOpenRef.current = true;
177
178
  panelRef.current?.querySelector(MENU_ITEM_SELECTOR)?.focus();
178
- return;
179
+ return undefined;
179
180
  }
180
181
  if (!wasOpenRef.current)
181
- return;
182
+ return undefined;
182
183
  wasOpenRef.current = false;
183
184
  const timeout = window.setTimeout(() => triggerRef.current?.focus(), 0);
184
185
  return () => window.clearTimeout(timeout);
@@ -212,6 +213,6 @@ export function WorkingDirPicker({ workingDir, recentDirs, onPickDirectory, onSe
212
213
  return;
213
214
  handleRecentFlyoutKey({ event, showRecent, hideRecent });
214
215
  }, [hideRecent, showRecent]);
215
- return (_jsxs("div", { ref: wrapRef, className: `jini-working-dir-picker${className ? ` ${className}` : ''}`, "data-testid": "working-dir-picker", children: [_jsx(WorkingDirTrigger, { triggerRef: triggerRef, workingDir: workingDir, invalid: invalid, open: open, panelId: panelId, t: t, toggle: toggle }), open ? (_jsx(WorkingDirPanel, { panelRef: panelRef, panelId: panelId, placement: placement, workingDir: workingDir, recentDirs: recentDirs, canClear: Boolean(workingDir && onClear), t: t, picker: picker, onKeyDown: handlePanelKeyDown })) : null] }));
216
+ return (_jsxs("div", { ref: wrapRef, className: withClassName('jini-working-dir-picker', className), "data-testid": "working-dir-picker", children: [_jsx(WorkingDirTrigger, { triggerRef: triggerRef, workingDir: workingDir, invalid: invalid, open: open, panelId: panelId, t: t, toggle: toggle }), open ? (_jsx(WorkingDirPanel, { panelRef: panelRef, panelId: panelId, placement: placement, workingDir: workingDir, recentDirs: recentDirs, canClear: Boolean(workingDir && onClear), t: t, picker: picker, onKeyDown: handlePanelKeyDown })) : null] }));
216
217
  }
217
218
  //# sourceMappingURL=WorkingDirPicker.js.map
@@ -85,7 +85,15 @@ function isRemixIconFaceRule(rule) {
85
85
  function findRemixIconFontSource({ rules }) {
86
86
  const face = Array.from(rules).find(isRemixIconFaceRule);
87
87
  // This package owns one url(...) WOFF2 source, not a general CSS font-source parser.
88
- return face?.style.getPropertyValue('src').match(/url\(\s*["']?([^"')]+)["']?\s*\)/)?.[1];
88
+ const source = face?.style.getPropertyValue('src');
89
+ if (!source)
90
+ return undefined;
91
+ const open = source.indexOf('url(');
92
+ const close = source.indexOf(')', open + 4);
93
+ if (open < 0 || close < 0)
94
+ return undefined;
95
+ // URL construction below already discards surrounding whitespace. Keep quotes outside the URL.
96
+ return /^["']?([^"']+)["']?$/.exec(source.slice(open + 4, close).trim())?.[1];
89
97
  }
90
98
  /** The source URL tagged with the attempt number; only http(s) URLs accept a query parameter. */
91
99
  function retryFontUrl({ url, attempt }) {
@@ -103,7 +103,7 @@ export function useSelectPopoverPosition(required, optional = {}) {
103
103
  // biome-ignore lint/correctness/useExhaustiveDependencies: scroll/resize listener wiring — see comment above; onOutOfView is read through latest; only lifecycle/ref changes re-arm it.
104
104
  useEffect(() => {
105
105
  if (!open || !portal)
106
- return;
106
+ return undefined;
107
107
  function reposition() {
108
108
  const trigger = triggerRef.current;
109
109
  if (!trigger)
@@ -146,7 +146,7 @@ export function useSelectPopover(required, optional = {}) {
146
146
  }
147
147
  useEffect(() => {
148
148
  if (!open)
149
- return;
149
+ return undefined;
150
150
  function onOutside(event) {
151
151
  if (isSelectEventInside({ target: event.target, trigger: triggerRef.current, panel: panelRef.current }))
152
152
  return;
@@ -33,7 +33,7 @@ export function useBrandFonts({ fonts }, options = {}) {
33
33
  }, [googleUrls, document]);
34
34
  useEffect(() => {
35
35
  if (!projectId || !resolveProjectAssetUrl)
36
- return;
36
+ return undefined;
37
37
  let cancelled = false;
38
38
  let styleEl = null;
39
39
  void (async () => {
@@ -52,7 +52,7 @@ export function useEdgeAutoScroll(contentKey) {
52
52
  useEffect(() => {
53
53
  const el = scrollRef.current;
54
54
  if (!el)
55
- return;
55
+ return undefined;
56
56
  const updateEdges = () => {
57
57
  const maxScroll = el.scrollWidth - el.clientWidth;
58
58
  setEdges({
@@ -10,21 +10,21 @@ export function useInView(options = {}) {
10
10
  useEffect(() => {
11
11
  const node = ref.current;
12
12
  if (!node)
13
- return;
13
+ return undefined;
14
14
  if (typeof IntersectionObserver === 'undefined') {
15
15
  setInView(true);
16
- return;
16
+ return undefined;
17
17
  }
18
18
  const observer = new IntersectionObserver((entries) => {
19
19
  for (const entry of entries) {
20
- if (entry.isIntersecting) {
21
- setInView(true);
22
- if (options.once !== false) {
23
- observer.disconnect();
24
- }
20
+ if (!entry.isIntersecting) {
21
+ if (options.once === false)
22
+ setInView(false);
23
+ continue;
25
24
  }
26
- else if (options.once === false) {
27
- setInView(false);
25
+ setInView(true);
26
+ if (options.once !== false) {
27
+ observer.disconnect();
28
28
  }
29
29
  }
30
30
  }, {
@@ -114,12 +114,9 @@ export function useResizableSplitPane(options = {}) {
114
114
  return undefined;
115
115
  const updateAllowedWidth = () => {
116
116
  const containerWidth = container.clientWidth;
117
- const nextSecondaryMin = !Number.isFinite(containerWidth) || containerWidth <= 0
118
- ? minSecondaryWidth
119
- : containerWidth < minNormalContainerWidth
120
- ? 0
121
- : minSecondaryWidth;
122
- const nextMax = !Number.isFinite(containerWidth) || containerWidth <= 0
117
+ const unmeasured = !Number.isFinite(containerWidth) || containerWidth <= 0;
118
+ const nextSecondaryMin = !unmeasured && containerWidth < minNormalContainerWidth ? 0 : minSecondaryWidth;
119
+ const nextMax = unmeasured
123
120
  ? maxWidthOption
124
121
  : Math.max(0, Math.min(maxWidthOption, Math.floor(containerWidth - handleWidth - nextSecondaryMin)));
125
122
  maxWidthRef.current = nextMax;
@@ -219,22 +216,17 @@ export function useResizableSplitPane(options = {}) {
219
216
  finishResize(false);
220
217
  }, [finishResize, renderPreferredWidth]);
221
218
  const onHandleKeyDown = useCallback((event) => {
222
- let nextWidth = null;
223
219
  const container = containerRef.current;
224
220
  const isRtl = container ? window.getComputedStyle(container).direction === 'rtl' : false;
225
- if (event.key === 'ArrowLeft') {
226
- nextWidth = widthRef.current + (isRtl ? 1 : -1) * keyboardStep;
227
- }
228
- else if (event.key === 'ArrowRight') {
229
- nextWidth = widthRef.current + (isRtl ? -1 : 1) * keyboardStep;
230
- }
231
- else if (event.key === 'Home') {
232
- nextWidth = minWidth;
233
- }
234
- else if (event.key === 'End') {
235
- nextWidth = maxWidthRef.current;
236
- }
237
- if (nextWidth === null)
221
+ // ArrowRight grows the pane in LTR and shrinks it in RTL; ArrowLeft is the mirror.
222
+ const rightStep = (isRtl ? -1 : 1) * keyboardStep;
223
+ const nextWidth = new Map([
224
+ ['ArrowLeft', widthRef.current - rightStep],
225
+ ['ArrowRight', widthRef.current + rightStep],
226
+ ['Home', minWidth],
227
+ ['End', maxWidthRef.current],
228
+ ]).get(event.key);
229
+ if (nextWidth === undefined)
238
230
  return;
239
231
  event.preventDefault();
240
232
  const next = applyWidth(nextWidth);