@jini-ai/ui 0.4.4 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +20 -0
- package/dist/core/theme/default.d.ts +4 -0
- package/dist/core/theme/default.js +11 -0
- package/dist/core/theme/stylesheet.d.ts +8 -0
- package/dist/core/theme/stylesheet.js +14 -0
- package/dist/core/theme/types.d.ts +39 -0
- package/dist/core/theme/types.js +2 -0
- package/dist/core/theme/validation.d.ts +15 -0
- package/dist/core/theme/validation.js +55 -0
- package/dist/features/admin-widgets/components/Select/Select.d.ts +11 -3
- package/dist/features/admin-widgets/components/Select/Select.hooks.d.ts +8 -13
- package/dist/features/admin-widgets/components/Select/Select.hooks.js +37 -215
- package/dist/features/admin-widgets/components/Select/Select.js +4 -3
- package/dist/features/appearance/constants.js +1 -1
- package/dist/features/connectors/rules.d.ts +1 -6
- package/dist/features/connectors/types.d.ts +1 -6
- package/dist/features/connectors/types.js +1 -6
- package/dist/features/execution/http-adapter.d.ts +111 -0
- package/dist/features/execution/http-adapter.js +160 -0
- package/dist/features/execution/index.d.ts +4 -0
- package/dist/features/execution/index.js +2 -0
- package/dist/features/execution/react/hooks/use-execution-credential.hooks.d.ts +232 -0
- package/dist/features/execution/react/hooks/use-execution-credential.hooks.js +217 -0
- package/dist/features/html-editor/canvas-style.d.ts +1 -1
- package/dist/features/html-editor/canvas-style.js +2 -2
- package/dist/features/i18n/dictionaries/settings-dialog.ar.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.bn.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.de.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.en.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.es.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.fa.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.fr.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.hi.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.hu.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.id.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.it.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ja.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ko.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.pl.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.pt-BR.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ru.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.th.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.tr.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.uk.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ur.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +2 -0
- package/dist/features/i18n/dictionary-translator.d.ts +5 -2
- package/dist/features/i18n/dictionary-translator.js +1 -1
- package/dist/features/i18n/index.d.ts +1 -1
- package/dist/features/i18n/index.js +1 -1
- package/dist/features/i18n/template-i18n.d.ts +11 -0
- package/dist/features/i18n/template-i18n.js +12 -0
- package/dist/features/interactive-ui/providers/recharts/chart-tokens.d.ts +1 -1
- package/dist/features/interactive-ui/providers/recharts/chart-tokens.js +1 -1
- package/dist/features/lexical-rich-text-editor/deserialize.d.ts +0 -5
- package/dist/features/lexical-rich-text-editor/deserialize.js +0 -5
- package/dist/features/lexical-rich-text-editor/mention-node.d.ts +5 -17
- package/dist/features/lexical-rich-text-editor/mention-node.js +5 -17
- package/dist/features/lexical-rich-text-editor/mention-parser.d.ts +2 -4
- package/dist/features/lexical-rich-text-editor/serialize.d.ts +0 -4
- package/dist/features/lexical-rich-text-editor/serialize.js +0 -4
- package/dist/features/lexical-rich-text-editor/types.d.ts +3 -13
- package/dist/features/lexical-rich-text-editor/types.js +3 -13
- package/dist/features/mcp-ui/early-message-buffer.d.ts +1 -2
- package/dist/features/mcp-ui/early-message-buffer.js +1 -2
- package/dist/features/mcp-ui/escape.d.ts +4 -0
- package/dist/features/mcp-ui/escape.js +2 -9
- package/dist/features/mcp-ui/index.d.ts +1 -0
- package/dist/features/mcp-ui/index.js +1 -0
- package/dist/features/mcp-ui/protocol.d.ts +2 -5
- package/dist/features/mcp-ui/protocol.js +2 -5
- package/dist/features/mcp-ui/resource.d.ts +47 -7
- package/dist/features/mcp-ui/resource.js +5 -8
- package/dist/features/mcp-ui/secret-card.d.ts +115 -0
- package/dist/features/mcp-ui/secret-card.js +137 -0
- package/dist/features/mcp-ui/surfaces/confirmation.js +5 -0
- package/dist/features/mcp-ui/surfaces/fields.d.ts +5 -2
- package/dist/features/mcp-ui/surfaces/fields.js +3 -1
- package/dist/features/mcp-ui/surfaces/form.d.ts +4 -1
- package/dist/features/mcp-ui/surfaces/form.js +65 -3
- package/dist/features/mcp-ui/surfaces/outcome.d.ts +2 -0
- package/dist/features/mcp-ui/surfaces/text-input.d.ts +14 -6
- package/dist/features/mcp-ui/surfaces/text-input.js +16 -2
- package/dist/features/mcp-ui/surfaces/tokens.js +1 -1
- package/dist/features/memory/react/components/MemoryConnectedPanel.d.ts +1 -1
- package/dist/features/memory/react/components/MemoryConnectedPanel.js +0 -1
- package/dist/features/panel-kit/helpers/format-timestamp.d.ts +5 -3
- package/dist/features/panel-kit/helpers/format-timestamp.js +13 -4
- package/dist/features/panel-kit/helpers/permissions.js +2 -0
- package/dist/features/panel-kit/helpers/resolve-active-tab-id.js +2 -0
- package/dist/features/panel-kit/helpers/standing-draft-local-backup.d.ts +51 -0
- package/dist/features/panel-kit/helpers/standing-draft-local-backup.js +77 -0
- package/dist/features/panel-kit/helpers/standing-draft-recovery-message.d.ts +8 -0
- package/dist/features/panel-kit/helpers/standing-draft-recovery-message.js +10 -0
- package/dist/features/panel-kit/hooks/use-async-action.hooks.d.ts +1 -0
- package/dist/features/panel-kit/hooks/use-dirty-guard.hooks.d.ts +1 -0
- package/dist/features/panel-kit/hooks/use-dirty-guard.hooks.js +1 -0
- package/dist/features/panel-kit/hooks/use-serial-writes.hooks.d.ts +8 -0
- package/dist/features/panel-kit/hooks/use-serial-writes.hooks.js +3 -0
- package/dist/features/panel-kit/hooks/use-settings-slice.hooks.d.ts +211 -0
- package/dist/features/panel-kit/hooks/use-settings-slice.hooks.js +382 -0
- package/dist/features/panel-kit/hooks/use-standing-draft-autosave.hooks.d.ts +179 -0
- package/dist/features/panel-kit/hooks/use-standing-draft-autosave.hooks.js +300 -0
- package/dist/features/panel-kit/index.d.ts +7 -1
- package/dist/features/panel-kit/index.js +5 -1
- package/dist/features/resource-dashboard/types.d.ts +8 -41
- package/dist/features/resource-dashboard/types.js +3 -26
- package/dist/features/tab-strip/constants.d.ts +3 -4
- package/dist/features/tab-strip/constants.js +3 -4
- package/dist/features/tab-strip/dependencies.d.ts +1 -6
- package/dist/features/tab-strip/dependencies.js +1 -6
- package/dist/features/tab-strip/react/components/TabBar.d.ts +33 -1
- package/dist/features/tab-strip/react/components/TabBar.hooks.d.ts +57 -5
- package/dist/features/tab-strip/react/components/TabBar.hooks.js +63 -5
- package/dist/features/tab-strip/react/components/TabBar.js +12 -1
- package/dist/features/tab-strip/rules.d.ts +4 -17
- package/dist/features/tab-strip/rules.js +4 -17
- package/dist/features/tab-strip/types.d.ts +2 -13
- package/dist/icon-name.d.ts +1 -1
- package/dist/icon-name.js +1 -0
- package/dist/index.d.ts +42 -417
- package/dist/index.js +42 -430
- package/dist/react/components/CustomSelect.d.ts +6 -2
- package/dist/react/components/CustomSelect.js +20 -91
- package/dist/react/components/Icon.d.ts +6 -0
- package/dist/react/components/Icon.js +12 -0
- package/dist/react/components/select-popover.d.ts +106 -0
- package/dist/react/components/select-popover.js +210 -0
- package/dist/renderers/sandbox-scripts.d.ts +29 -0
- package/dist/renderers/sandbox-scripts.js +127 -0
- package/dist/renderers/sandboxed-document.js +3 -86
- package/dist/renderers/srcdoc/build.d.ts +10 -17
- package/dist/renderers/srcdoc/build.js +31 -234
- package/dist/styles/variables.css +2 -2
- package/dist/theme/apply.d.ts +1 -1
- package/dist/theme/apply.js +2 -2
- package/dist/theme/color-scheme.d.ts +1 -1
- package/dist/theme/default.d.ts +1 -3
- package/dist/theme/default.js +2 -10
- package/dist/theme/index.d.ts +3 -3
- package/dist/theme/index.js +2 -2
- package/dist/theme/stylesheet.d.ts +1 -7
- package/dist/theme/stylesheet.js +2 -13
- package/dist/theme/types.d.ts +1 -38
- package/dist/theme/types.js +2 -1
- package/dist/theme/validation.d.ts +1 -14
- package/dist/theme/validation.js +2 -54
- package/dist/utils/endpoint-policy.d.ts +21 -39
- package/dist/utils/endpoint-policy.js +19 -107
- package/dist/utils/file-system-errors.d.ts +1 -2
- package/dist/utils/file-system-errors.js +1 -2
- package/dist/utils/ime-composing.d.ts +0 -3
- package/dist/utils/ime-composing.js +0 -3
- package/dist/utils/platform.d.ts +1 -4
- package/dist/utils/platform.js +1 -4
- package/dist/utils/smooth-scroll-to-top.d.ts +1 -3
- package/dist/utils/smooth-scroll-to-top.js +3 -13
- package/dist/utils/timezone.js +1 -5
- package/dist/utils/uuid.d.ts +0 -3
- package/dist/utils/uuid.js +0 -3
- package/dist/utils/visual-stability.d.ts +1 -3
- package/dist/utils/visual-stability.js +1 -3
- package/package.json +74 -15
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
import type { TabStripHapticsPort } from './ports.js';
|
|
2
|
-
/**
|
|
3
|
-
* Real browser haptics implementation — ported from `WorkspaceTabsBar.tsx`'s
|
|
4
|
-
* `pulseTabDragHaptic`: SSR-guarded, opportunistic (a `navigator.vibrate`
|
|
5
|
-
* failure/absence is silently swallowed, matching the source's own
|
|
6
|
-
* "unsupported environments should keep dragging normally" comment).
|
|
7
|
-
*/
|
|
2
|
+
/** SSR-safe, opportunistic browser haptics. Unavailable or failed vibration must never stop dragging. */
|
|
8
3
|
export declare function createBrowserTabStripHaptics(): TabStripHapticsPort;
|
|
9
4
|
//# sourceMappingURL=dependencies.d.ts.map
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Real browser haptics implementation — ported from `WorkspaceTabsBar.tsx`'s
|
|
3
|
-
* `pulseTabDragHaptic`: SSR-guarded, opportunistic (a `navigator.vibrate`
|
|
4
|
-
* failure/absence is silently swallowed, matching the source's own
|
|
5
|
-
* "unsupported environments should keep dragging normally" comment).
|
|
6
|
-
*/
|
|
1
|
+
/** SSR-safe, opportunistic browser haptics. Unavailable or failed vibration must never stop dragging. */
|
|
7
2
|
export function createBrowserTabStripHaptics() {
|
|
8
3
|
return {
|
|
9
4
|
pulse(durationMs) {
|
|
@@ -1,14 +1,45 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* @file Controlled horizontal tabs: id/label pairs, active id and change callback, with no routing,
|
|
4
|
+
* persistence or panel lifecycle. SettingsDialogShell also owns a header/sidebar/dialog lifecycle;
|
|
5
|
+
* hosts needing only a tab row can use this standalone component.
|
|
6
|
+
* Optional agent handles tag only elements whose handles are supplied.
|
|
7
|
+
*/
|
|
2
8
|
export interface TabBarTab {
|
|
3
9
|
readonly id: string;
|
|
4
10
|
readonly label: string;
|
|
11
|
+
/** Inline icon before the label. Always aria-hidden because adjacent text already names the
|
|
12
|
+
* tab. Omit for the plain text layout; this slot does not require SettingsDialogShell. */
|
|
5
13
|
readonly icon?: ReactNode | undefined;
|
|
14
|
+
/** Shown next to the label when present (e.g. a theme count per tier). Omit to show none. */
|
|
6
15
|
readonly count?: number | undefined;
|
|
16
|
+
/** Pill after the label, using the admin Sidebar's shared "Soon" styling. A tagged tab stays
|
|
17
|
+
* clickable so its panel can explain status; use disabled when there is no destination. */
|
|
7
18
|
readonly tag?: string | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Renders this tab greyed out and non-interactive: no `onChange` call, not part of the tab
|
|
21
|
+
* order (native `disabled`), `aria-disabled` set for assistive tech. For scaffolding a future
|
|
22
|
+
* tab (e.g. a "Marketplace (soon)" placeholder with no real destination yet) that should be
|
|
23
|
+
* visibly present without inviting a click into nothing.
|
|
24
|
+
*/
|
|
8
25
|
readonly disabled?: boolean | undefined;
|
|
26
|
+
/** Publishes this tab button as agent-addressable via `agentHandle()` (`@jini-ai/agentic`). Omit
|
|
27
|
+
* to leave the tab untagged. */
|
|
9
28
|
readonly handle?: string | undefined;
|
|
29
|
+
/** Plain-English description of what switching to this tab does, paired with {@link handle}.
|
|
30
|
+
* Defaults to `label` (untranslated tab labels, e.g. Deployment's, still read fine as a
|
|
31
|
+
* description; a caller with a translated `label` should pass its own). Ignored if `handle` is
|
|
32
|
+
* omitted. */
|
|
10
33
|
readonly handleLabel?: string | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* A filled dot before the label exposes configuration state across panels shown one at a time,
|
|
36
|
+
* without making the tab row denser as providers are added. Meaning comes from presence rather
|
|
37
|
+
* than color alone. The dot is aria-hidden; dotLabel supplies its text equivalent as a suffix
|
|
38
|
+
* to the tab's accessible name.
|
|
39
|
+
*/
|
|
11
40
|
readonly dot?: boolean | undefined;
|
|
41
|
+
/** Visually-hidden text appended to this tab's accessible name when {@link dot} is set — e.g.
|
|
42
|
+
* "Connected". Required whenever `dot` is true; ignored otherwise. */
|
|
12
43
|
readonly dotLabel?: string | undefined;
|
|
13
44
|
}
|
|
14
45
|
export interface TabBarProps {
|
|
@@ -16,8 +47,9 @@ export interface TabBarProps {
|
|
|
16
47
|
activeId: string;
|
|
17
48
|
onChange: (id: string) => void;
|
|
18
49
|
ariaLabel: string;
|
|
50
|
+
/** Publishes the tablist container itself as agent-addressable. Omit to leave it untagged. */
|
|
19
51
|
containerHandle?: string | undefined;
|
|
20
52
|
}
|
|
21
53
|
/** Controlled button-based selection strip, including roving focus and optional agent handles. */
|
|
22
|
-
export declare function TabBar({ tabs, activeId, onChange, ariaLabel, containerHandle }: TabBarProps): import("react").JSX.Element;
|
|
54
|
+
export declare function TabBar({ tabs, activeId, onChange, ariaLabel, containerHandle }: TabBarProps, _optional?: Record<string, never>): import("react").JSX.Element;
|
|
23
55
|
//# sourceMappingURL=TabBar.d.ts.map
|
|
@@ -1,25 +1,77 @@
|
|
|
1
1
|
import type { KeyboardEvent as ReactKeyboardEvent } from "react";
|
|
2
2
|
import type { TabBarTab } from "./TabBar.js";
|
|
3
|
+
/**
|
|
4
|
+
* @file TabBar's derived props and WAI-ARIA automatic-activation keyboard behavior.
|
|
5
|
+
* Data and event logic live here; TabBar.tsx keeps the JSX render helpers. Arrow keys activate
|
|
6
|
+
* immediately and move focus, while disabled tabs are skipped and non-tab controls keep their keys.
|
|
7
|
+
*/
|
|
8
|
+
/** The `TabBarButton` component's own `agentHandle()` spread, as a plain function rather than an
|
|
9
|
+
* inline ternary in its JSX — one more small piece pulled out of `TabBar.tsx` for the same
|
|
10
|
+
* complexity-gate reason that component's own doc comment gives.
|
|
11
|
+
* @example tabHandleProps({ tab: { id: "a", label: "A", handle: "tab-a" } });
|
|
12
|
+
*/
|
|
3
13
|
export declare function tabHandleProps({ tab }: {
|
|
4
14
|
tab: TabBarTab;
|
|
5
|
-
}): {};
|
|
6
|
-
/**
|
|
15
|
+
}, _optional?: Record<string, never>): {};
|
|
16
|
+
/**
|
|
17
|
+
* Resolve arrow/Home/End navigation in enabled-tab order; null means no navigation.
|
|
18
|
+
*
|
|
19
|
+
* Resolves which tab a keydown on the tablist should move to, for the WAI-ARIA "automatic
|
|
20
|
+
* activation" tabs pattern (arrow keys change the selection immediately, not just focus).
|
|
21
|
+
*
|
|
22
|
+
* @param input.tabs - The tab row in DOM order.
|
|
23
|
+
* @param input.activeId - The currently active tab's id.
|
|
24
|
+
* @param input.key - `KeyboardEvent.key` from the tablist's own `onKeyDown`.
|
|
25
|
+
* @returns The target tab's index into `tabs`, or `null` when `key` isn't a nav key or every tab
|
|
26
|
+
* is disabled. Disabled tabs are never a target. Home/End always resolve to the first/last
|
|
27
|
+
* enabled tab. When `activeId` matches no enabled tab (unset, unknown id, or a disabled tab),
|
|
28
|
+
* ArrowRight/Home resolve to the first enabled tab and ArrowLeft/End resolve to the last —
|
|
29
|
+
* the same "nothing to move from" case Home/End already answer explicitly.
|
|
30
|
+
* @complexity O(n²) time, O(n) space (the enabled-index reducer copies its accumulator) in `tabs.length`.
|
|
31
|
+
* @example resolveTabBarKeyTarget({ tabs, activeId: "a", key: "ArrowRight" });
|
|
32
|
+
*/
|
|
7
33
|
export declare function resolveTabBarKeyTarget({ tabs, activeId, key }: {
|
|
8
34
|
tabs: readonly TabBarTab[];
|
|
9
35
|
activeId: string;
|
|
10
36
|
key: string;
|
|
11
|
-
}): number | null;
|
|
37
|
+
}, _optional?: Record<string, never>): number | null;
|
|
38
|
+
/**
|
|
39
|
+
* Resolves one tab's roving-tabindex value — exactly one tab in a `TabBar` is ever in the native
|
|
40
|
+
* Tab order at a time (WAI-ARIA APG roving tabindex), so arrowing between tabs never lets a plain
|
|
41
|
+
* Tab keypress land on more than one of them.
|
|
42
|
+
*
|
|
43
|
+
* @param input.tabs - The tab row in DOM order.
|
|
44
|
+
* @param input.activeId - The currently active tab's id.
|
|
45
|
+
* @param input.tab - The tab being rendered.
|
|
46
|
+
* @returns `0` for the enabled active tab, or for the first enabled tab when `activeId` matches
|
|
47
|
+
* no enabled tab; `-1` otherwise, including every disabled tab.
|
|
48
|
+
* @complexity O(n) in `tabs.length`.
|
|
49
|
+
* @example resolveTabBarTabIndex({ tabs, activeId: "a", tab: tabs[0]! });
|
|
50
|
+
*/
|
|
12
51
|
export declare function resolveTabBarTabIndex({ tabs, activeId, tab }: {
|
|
13
52
|
tabs: readonly TabBarTab[];
|
|
14
53
|
activeId: string;
|
|
15
54
|
tab: TabBarTab;
|
|
16
|
-
}): 0 | -1;
|
|
55
|
+
}, _optional?: Record<string, never>): 0 | -1;
|
|
17
56
|
export interface TabBarKeyboardHandlers {
|
|
18
57
|
onKeyDown: (e: ReactKeyboardEvent<HTMLDivElement>) => void;
|
|
19
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Wires {@link resolveTabBarKeyTarget} into a tablist's `onKeyDown`: on a nav key, changes the
|
|
61
|
+
* active tab and moves DOM focus to the new tab button, so focus and selection never drift apart.
|
|
62
|
+
* Keydowns that did not originate inside a `[role="tab"]` are ignored, so a non-tab control hosted
|
|
63
|
+
* in the same tablist keeps its own arrow/Home/End keys.
|
|
64
|
+
*
|
|
65
|
+
* @param input.tabs - The tab row in DOM order.
|
|
66
|
+
* @param input.activeId - The currently active tab's id.
|
|
67
|
+
* @param input.onChange - `TabBarProps.onChange`, called with the target tab's id.
|
|
68
|
+
* @returns `onKeyDown`, for the tablist container's own React event prop.
|
|
69
|
+
* @complexity O(n²) time, O(n) space (the enabled-index reducer copies its accumulator) in `tabs.length` per keydown (bounded by the on-screen tab count).
|
|
70
|
+
* @example useTabBarKeyboard({ tabs, activeId, onChange });
|
|
71
|
+
*/
|
|
20
72
|
export declare function useTabBarKeyboard({ tabs, activeId, onChange }: {
|
|
21
73
|
tabs: readonly TabBarTab[];
|
|
22
74
|
activeId: string;
|
|
23
75
|
onChange: (id: string) => void;
|
|
24
|
-
}): TabBarKeyboardHandlers;
|
|
76
|
+
}, _optional?: Record<string, never>): TabBarKeyboardHandlers;
|
|
25
77
|
//# sourceMappingURL=TabBar.hooks.d.ts.map
|
|
@@ -1,16 +1,43 @@
|
|
|
1
1
|
import { agentHandle } from "@jini-ai/agentic";
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* @file TabBar's derived props and WAI-ARIA automatic-activation keyboard behavior.
|
|
4
|
+
* Data and event logic live here; TabBar.tsx keeps the JSX render helpers. Arrow keys activate
|
|
5
|
+
* immediately and move focus, while disabled tabs are skipped and non-tab controls keep their keys.
|
|
6
|
+
*/
|
|
7
|
+
/** The `TabBarButton` component's own `agentHandle()` spread, as a plain function rather than an
|
|
8
|
+
* inline ternary in its JSX — one more small piece pulled out of `TabBar.tsx` for the same
|
|
9
|
+
* complexity-gate reason that component's own doc comment gives.
|
|
10
|
+
* @example tabHandleProps({ tab: { id: "a", label: "A", handle: "tab-a" } });
|
|
11
|
+
*/
|
|
12
|
+
export function tabHandleProps({ tab }, _optional = {}) {
|
|
3
13
|
return tab.handle ? agentHandle({ handle: tab.handle }, { role: "button", label: tab.handleLabel ?? tab.label }) : {};
|
|
4
14
|
}
|
|
5
15
|
const TAB_BAR_NAV_KEYS = ["ArrowRight", "ArrowLeft", "Home", "End"];
|
|
6
16
|
function isTabBarNavKey(key) {
|
|
7
17
|
return TAB_BAR_NAV_KEYS.includes(key);
|
|
8
18
|
}
|
|
19
|
+
/** Indices of every non-disabled tab, in `tabs` order — the only positions a key can land on. */
|
|
9
20
|
function enabledTabIndices(tabs) {
|
|
10
21
|
return tabs.reduce((acc, tab, i) => (tab.disabled ? acc : [...acc, i]), []);
|
|
11
22
|
}
|
|
12
|
-
/**
|
|
13
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Resolve arrow/Home/End navigation in enabled-tab order; null means no navigation.
|
|
25
|
+
*
|
|
26
|
+
* Resolves which tab a keydown on the tablist should move to, for the WAI-ARIA "automatic
|
|
27
|
+
* activation" tabs pattern (arrow keys change the selection immediately, not just focus).
|
|
28
|
+
*
|
|
29
|
+
* @param input.tabs - The tab row in DOM order.
|
|
30
|
+
* @param input.activeId - The currently active tab's id.
|
|
31
|
+
* @param input.key - `KeyboardEvent.key` from the tablist's own `onKeyDown`.
|
|
32
|
+
* @returns The target tab's index into `tabs`, or `null` when `key` isn't a nav key or every tab
|
|
33
|
+
* is disabled. Disabled tabs are never a target. Home/End always resolve to the first/last
|
|
34
|
+
* enabled tab. When `activeId` matches no enabled tab (unset, unknown id, or a disabled tab),
|
|
35
|
+
* ArrowRight/Home resolve to the first enabled tab and ArrowLeft/End resolve to the last —
|
|
36
|
+
* the same "nothing to move from" case Home/End already answer explicitly.
|
|
37
|
+
* @complexity O(n²) time, O(n) space (the enabled-index reducer copies its accumulator) in `tabs.length`.
|
|
38
|
+
* @example resolveTabBarKeyTarget({ tabs, activeId: "a", key: "ArrowRight" });
|
|
39
|
+
*/
|
|
40
|
+
export function resolveTabBarKeyTarget({ tabs, activeId, key }, _optional = {}) {
|
|
14
41
|
if (!isTabBarNavKey(key))
|
|
15
42
|
return null;
|
|
16
43
|
const enabled = enabledTabIndices(tabs);
|
|
@@ -32,7 +59,20 @@ export function resolveTabBarKeyTarget({ tabs, activeId, key }) {
|
|
|
32
59
|
return enabled[enabled.length - 1];
|
|
33
60
|
return enabled[(activePos - 1 + enabled.length) % enabled.length];
|
|
34
61
|
}
|
|
35
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Resolves one tab's roving-tabindex value — exactly one tab in a `TabBar` is ever in the native
|
|
64
|
+
* Tab order at a time (WAI-ARIA APG roving tabindex), so arrowing between tabs never lets a plain
|
|
65
|
+
* Tab keypress land on more than one of them.
|
|
66
|
+
*
|
|
67
|
+
* @param input.tabs - The tab row in DOM order.
|
|
68
|
+
* @param input.activeId - The currently active tab's id.
|
|
69
|
+
* @param input.tab - The tab being rendered.
|
|
70
|
+
* @returns `0` for the enabled active tab, or for the first enabled tab when `activeId` matches
|
|
71
|
+
* no enabled tab; `-1` otherwise, including every disabled tab.
|
|
72
|
+
* @complexity O(n) in `tabs.length`.
|
|
73
|
+
* @example resolveTabBarTabIndex({ tabs, activeId: "a", tab: tabs[0]! });
|
|
74
|
+
*/
|
|
75
|
+
export function resolveTabBarTabIndex({ tabs, activeId, tab }, _optional = {}) {
|
|
36
76
|
if (tab.disabled)
|
|
37
77
|
return -1;
|
|
38
78
|
if (tabs.some((t) => t.id === activeId && !t.disabled))
|
|
@@ -40,8 +80,26 @@ export function resolveTabBarTabIndex({ tabs, activeId, tab }) {
|
|
|
40
80
|
const firstEnabled = tabs.find((t) => !t.disabled);
|
|
41
81
|
return firstEnabled?.id === tab.id ? 0 : -1;
|
|
42
82
|
}
|
|
43
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Wires {@link resolveTabBarKeyTarget} into a tablist's `onKeyDown`: on a nav key, changes the
|
|
85
|
+
* active tab and moves DOM focus to the new tab button, so focus and selection never drift apart.
|
|
86
|
+
* Keydowns that did not originate inside a `[role="tab"]` are ignored, so a non-tab control hosted
|
|
87
|
+
* in the same tablist keeps its own arrow/Home/End keys.
|
|
88
|
+
*
|
|
89
|
+
* @param input.tabs - The tab row in DOM order.
|
|
90
|
+
* @param input.activeId - The currently active tab's id.
|
|
91
|
+
* @param input.onChange - `TabBarProps.onChange`, called with the target tab's id.
|
|
92
|
+
* @returns `onKeyDown`, for the tablist container's own React event prop.
|
|
93
|
+
* @complexity O(n²) time, O(n) space (the enabled-index reducer copies its accumulator) in `tabs.length` per keydown (bounded by the on-screen tab count).
|
|
94
|
+
* @example useTabBarKeyboard({ tabs, activeId, onChange });
|
|
95
|
+
*/
|
|
96
|
+
export function useTabBarKeyboard({ tabs, activeId, onChange }, _optional = {}) {
|
|
44
97
|
function onKeyDown(e) {
|
|
98
|
+
// A tablist may legitimately hold a control that is NOT a tab — `AccessTokensTab.tsx`'s
|
|
99
|
+
// "+ Add custom provider" button sits inside its `role="tablist"` div and is Tab-reachable.
|
|
100
|
+
// React's onKeyDown here fires for keydowns anywhere in the subtree, so without this origin
|
|
101
|
+
// check an arrow press on that button would change the selection and pull focus onto a tab,
|
|
102
|
+
// silently moving the operator off the control they were standing on.
|
|
45
103
|
// Controls inside the strip that are not tabs keep their own navigation keys.
|
|
46
104
|
if (!(e.target instanceof Element) || !e.target.closest('[role="tab"]'))
|
|
47
105
|
return;
|
|
@@ -2,16 +2,27 @@ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { TabStripFrame } from "./TabStripFrame.js";
|
|
3
3
|
import { agentHandle } from "@jini-ai/agentic";
|
|
4
4
|
import { resolveTabBarTabIndex, tabHandleProps, useTabBarKeyboard } from "./TabBar.hooks.js";
|
|
5
|
+
/** The visually-hidden accessible-name SUFFIX {@link TabBarTab.dot} adds — e.g. "GitHub Pages,
|
|
6
|
+
* Connected", never "Connected GitHub Pages". Deliberately its own function rather than folded into
|
|
7
|
+
* the visual dot span rendered before the label (`TabBarButton` below): the visual dot is
|
|
8
|
+
* `aria-hidden` and belongs immediately before the label, where a leading bullet reads naturally,
|
|
9
|
+
* but the ACCESSIBLE text has to come AFTER the label text in DOM order for the tab's accessible
|
|
10
|
+
* name (label + this suffix, concatenated in DOM order) to read as a sentence rather than a
|
|
11
|
+
* fragment stitched on backwards. Split out for the same complexity-gate reason {@link
|
|
12
|
+
* TabBarButton}'s own doc gives. */
|
|
5
13
|
function tabDotAccessibleSuffix(tab) {
|
|
6
14
|
if (!tab.dot || !tab.dotLabel)
|
|
7
15
|
return null;
|
|
8
16
|
return _jsxs("span", { className: "visually-hidden", children: [", ", tab.dotLabel] });
|
|
9
17
|
}
|
|
18
|
+
/** One tab button. A named top-level component avoids the nesting penalty for branches inside
|
|
19
|
+
* an inline map callback under the cyclomatic/cognitive complexity gate. Derived props live in
|
|
20
|
+
* TabBar.hooks.tsx; tabDotAccessibleSuffix stays here because it renders JSX. */
|
|
10
21
|
function TabBarButton({ tab, active, tabIndex, onChange, }) {
|
|
11
22
|
return (_jsxs("button", { type: "button", role: "tab", className: "tab-bar-item", "aria-selected": active, "aria-label": tab.dot && tab.dotLabel ? [tab.label + ", " + tab.dotLabel, tab.tag, tab.count].filter((part) => part !== undefined).join(" ") : undefined, "aria-disabled": tab.disabled || undefined, disabled: tab.disabled, tabIndex: tabIndex, onClick: tab.disabled ? undefined : () => onChange(tab.id), ...tabHandleProps({ tab: tab }), children: [tab.dot ? _jsx("span", { className: "tab-bar-dot", "aria-hidden": "true" }) : null, tab.icon ? (_jsx("span", { className: "tab-bar-icon", "aria-hidden": "true", children: tab.icon })) : null, tab.label, tabDotAccessibleSuffix(tab), tab.tag ? _jsx("span", { className: "tab-bar-tag", children: tab.tag }) : null, tab.count !== undefined ? _jsx("span", { className: "tab-bar-count", children: tab.count }) : null] }));
|
|
12
23
|
}
|
|
13
24
|
/** Controlled button-based selection strip, including roving focus and optional agent handles. */
|
|
14
|
-
export function TabBar({ tabs, activeId, onChange, ariaLabel, containerHandle }) {
|
|
25
|
+
export function TabBar({ tabs, activeId, onChange, ariaLabel, containerHandle }, _optional = {}) {
|
|
15
26
|
const { onKeyDown } = useTabBarKeyboard({ tabs: tabs, activeId: activeId, onChange: onChange });
|
|
16
27
|
return (_jsx(TabStripFrame, { className: "tab-bar", role: "tablist", "aria-label": ariaLabel, onKeyDown: onKeyDown, ...(containerHandle ? agentHandle({ handle: containerHandle }, { role: "region", label: ariaLabel }) : {}), children: tabs.map((tab) => (_jsx(TabBarButton, { tab: tab, active: activeId === tab.id, tabIndex: resolveTabBarTabIndex({ tabs: tabs, activeId: activeId, tab: tab }), onChange: onChange }, tab.id))) }));
|
|
17
28
|
}
|
|
@@ -1,21 +1,9 @@
|
|
|
1
1
|
import type { TabStripDragTarget, TabStripDropEdge, TabStripElementRect, TabStripTab } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
*
|
|
4
|
-
* `WorkspaceTabsBar.tsx`'s `tabDropEdgeFromElement` and
|
|
5
|
-
* `FileWorkspace.tsx`'s `tabDropEdgeFromEvent` — both compute this exact
|
|
6
|
-
* "left half vs right half of the target's bounding rect" split, just
|
|
7
|
-
* against a `DragEvent` directly rather than a plain `clientX` number. The
|
|
8
|
-
* pure form here takes the already-extracted `clientX`/rect so it needs no
|
|
9
|
-
* DOM/event types.
|
|
10
|
-
*/
|
|
2
|
+
/** Compare the pointer with the target rectangle's midpoint. Plain coordinates keep this rule
|
|
3
|
+
* independent of DOM/event types. */
|
|
11
4
|
export declare function dropEdgeFromPointerX(pointerX: number, rect: Pick<TabStripElementRect, 'left' | 'width'>): TabStripDropEdge;
|
|
12
|
-
/**
|
|
13
|
-
*
|
|
14
|
-
* landing after it instead — pinned tabs always stay leftmost. Generalizes
|
|
15
|
-
* `WorkspaceTabsBar.tsx`'s `findTabDropTarget`'s `resolveTarget` closure
|
|
16
|
-
* (there hardcoded to the single entry tab; here driven by the host-
|
|
17
|
-
* supplied `pinned` field on any number of tabs).
|
|
18
|
-
*/
|
|
5
|
+
/** Land after a pinned target even when its left half was hit; pinned tabs must stay leftmost.
|
|
6
|
+
* Hosts supply the pinned ids, so the rule does not assume one special entry tab. */
|
|
19
7
|
export declare function coercePinnedDropTarget(target: TabStripDragTarget, pinnedTabIds: ReadonlySet<string>): TabStripDragTarget;
|
|
20
8
|
/**
|
|
21
9
|
* Moves `sourceId` to just before/after `targetId` in `tabIds`, preserving
|
|
@@ -28,7 +16,6 @@ export declare function coercePinnedDropTarget(target: TabStripDragTarget, pinne
|
|
|
28
16
|
* back in at index (+1 for 'after')" algorithm in both files.
|
|
29
17
|
*/
|
|
30
18
|
export declare function reorderTabIds(tabIds: readonly string[], sourceId: string, targetId: string, edge: TabStripDropEdge): string[];
|
|
31
|
-
/** Ported verbatim (both files have an identical helper under this name). */
|
|
32
19
|
export declare function arraysEqual(a: readonly string[], b: readonly string[]): boolean;
|
|
33
20
|
/** A stable string key for a drag target, used to detect "did the hovered
|
|
34
21
|
* target actually change" without a deep-equal check (ported from
|
|
@@ -1,22 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
* `WorkspaceTabsBar.tsx`'s `tabDropEdgeFromElement` and
|
|
4
|
-
* `FileWorkspace.tsx`'s `tabDropEdgeFromEvent` — both compute this exact
|
|
5
|
-
* "left half vs right half of the target's bounding rect" split, just
|
|
6
|
-
* against a `DragEvent` directly rather than a plain `clientX` number. The
|
|
7
|
-
* pure form here takes the already-extracted `clientX`/rect so it needs no
|
|
8
|
-
* DOM/event types.
|
|
9
|
-
*/
|
|
1
|
+
/** Compare the pointer with the target rectangle's midpoint. Plain coordinates keep this rule
|
|
2
|
+
* independent of DOM/event types. */
|
|
10
3
|
export function dropEdgeFromPointerX(pointerX, rect) {
|
|
11
4
|
return pointerX > rect.left + rect.width / 2 ? 'after' : 'before';
|
|
12
5
|
}
|
|
13
|
-
/**
|
|
14
|
-
*
|
|
15
|
-
* landing after it instead — pinned tabs always stay leftmost. Generalizes
|
|
16
|
-
* `WorkspaceTabsBar.tsx`'s `findTabDropTarget`'s `resolveTarget` closure
|
|
17
|
-
* (there hardcoded to the single entry tab; here driven by the host-
|
|
18
|
-
* supplied `pinned` field on any number of tabs).
|
|
19
|
-
*/
|
|
6
|
+
/** Land after a pinned target even when its left half was hit; pinned tabs must stay leftmost.
|
|
7
|
+
* Hosts supply the pinned ids, so the rule does not assume one special entry tab. */
|
|
20
8
|
export function coercePinnedDropTarget(target, pinnedTabIds) {
|
|
21
9
|
if (target.edge === 'before' && pinnedTabIds.has(target.tabId)) {
|
|
22
10
|
return { tabId: target.tabId, edge: 'after' };
|
|
@@ -46,7 +34,6 @@ export function reorderTabIds(tabIds, sourceId, targetId, edge) {
|
|
|
46
34
|
nextIds.splice(edge === 'after' ? targetIndex + 1 : targetIndex, 0, sourceId);
|
|
47
35
|
return arraysEqual(nextIds, tabIds) ? original : nextIds;
|
|
48
36
|
}
|
|
49
|
-
/** Ported verbatim (both files have an identical helper under this name). */
|
|
50
37
|
export function arraysEqual(a, b) {
|
|
51
38
|
if (a.length !== b.length)
|
|
52
39
|
return false;
|
|
@@ -1,22 +1,11 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
-
/**
|
|
3
|
-
* Which side of the drop-target tab the dragged tab lands on. Shared
|
|
4
|
-
* concept across both source shapes this feature consolidates
|
|
5
|
-
* (`WorkspaceTabsBar.tsx`'s `TabDropEdge`, `FileWorkspace.tsx`'s
|
|
6
|
-
* `TabDropEdge`) — identical meaning in both.
|
|
7
|
-
*/
|
|
2
|
+
/** Which side of the target tab receives the dragged item. */
|
|
8
3
|
export type TabStripDropEdge = 'before' | 'after';
|
|
9
4
|
export interface TabStripDragTarget {
|
|
10
5
|
tabId: string;
|
|
11
6
|
edge: TabStripDropEdge;
|
|
12
7
|
}
|
|
13
|
-
/**
|
|
14
|
-
* One tab-strip item. `content` is host-injected — the primitive never
|
|
15
|
-
* renders an icon or label itself, it just lays out whatever the host gives
|
|
16
|
-
* it (see `ADS-memory/reports/jini-port/god-components-extraction-plan.md`'s consolidation
|
|
17
|
-
* map: "the specific parts become host-injected config, not part of the
|
|
18
|
-
* primitive").
|
|
19
|
-
*/
|
|
8
|
+
/** One tab item. The host owns icon/label/meta content; this primitive only lays it out. */
|
|
20
9
|
export interface TabStripTab {
|
|
21
10
|
id: string;
|
|
22
11
|
/** Icon + label + meta + badges — anything the host wants inside the tab. */
|
package/dist/icon-name.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* that must not drag a React import across the boundary. `Icon.tsx` re-exports
|
|
8
8
|
* this type, so existing importers are unchanged.
|
|
9
9
|
*/
|
|
10
|
-
export type IconName = 'alert-triangle' | 'arrow-left' | 'arrow-up' | 'attach' | 'bell' | 'blocks' | 'check' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'close' | 'copy' | 'comment' | 'message-circle' | 'discord' | 'download' | 'draw' | 'edit' | 'external-link' | 'eye' | 'eye-off' | 'file' | 'file-code' | 'file-text' | 'folder' | 'folder-filled' | 'fork' | 'github' | 'github-filled' | 'grip-vertical' | 'grid' | 'globe' | 'hammer' | 'help-circle' | 'history' | 'home' | 'home-filled' | 'image' | 'import' | 'info' | 'kanban' | 'layers-filled' | 'languages' | 'layout' | 'lightbulb' | 'link' | 'lock' | 'log-out' | 'integrations-filled' | 'maximize' | 'mic' | 'minimize' | 'minus' | 'more-horizontal' | 'orbit' | 'paint-bucket' | 'panel-left' | 'palette' | 'palette-filled' | 'pencil' | 'plus' | 'plus-filled' | 'puzzle' | 'slides' | 'star' | 'swatchbook' | 'play' | 'present' | 'refresh' | 'reload' | 'search' | 'send' | 'settings' | 'share' | 'sliders' | 'smartphone' | 'spinner' | 'sparkles' | 'stop' | 'sun' | 'moon' | 'sun-moon' | 'terminal' | 'thumbs-down' | 'thumbs-up' | 'tweaks' | 'upload' | 'trash' | 'volume' | 'zoom-in' | 'zoom-out';
|
|
10
|
+
export type IconName = 'package-check' | 'storefront' | 'package-outline' | 'trash-compact' | 'chevron-right-wide' | 'check-long' | 'alert-triangle' | 'arrow-left' | 'arrow-up' | 'attach' | 'bell' | 'blocks' | 'check' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'close' | 'copy' | 'comment' | 'message-circle' | 'discord' | 'download' | 'draw' | 'edit' | 'external-link' | 'eye' | 'eye-off' | 'file' | 'file-code' | 'file-text' | 'folder' | 'folder-filled' | 'fork' | 'github' | 'github-filled' | 'grip-vertical' | 'grid' | 'globe' | 'hammer' | 'help-circle' | 'history' | 'home' | 'home-filled' | 'image' | 'import' | 'info' | 'kanban' | 'layers-filled' | 'languages' | 'layout' | 'lightbulb' | 'link' | 'lock' | 'log-out' | 'integrations-filled' | 'maximize' | 'mic' | 'minimize' | 'minus' | 'more-horizontal' | 'orbit' | 'paint-bucket' | 'panel-left' | 'palette' | 'palette-filled' | 'pencil' | 'plus' | 'plus-filled' | 'puzzle' | 'slides' | 'star' | 'swatchbook' | 'play' | 'present' | 'refresh' | 'reload' | 'search' | 'send' | 'settings' | 'share' | 'sliders' | 'smartphone' | 'spinner' | 'sparkles' | 'stop' | 'sun' | 'moon' | 'sun-moon' | 'terminal' | 'thumbs-down' | 'thumbs-up' | 'tweaks' | 'upload' | 'trash' | 'volume' | 'zoom-in' | 'zoom-out';
|
|
11
11
|
/**
|
|
12
12
|
* Runtime enumeration of every {@link IconName}. A string union has no
|
|
13
13
|
* runtime representation of its own, so this exists as the one place a
|
package/dist/icon-name.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* compile-time one, since TS can't derive a value from a type.
|
|
9
9
|
*/
|
|
10
10
|
export const ICON_NAMES = [
|
|
11
|
+
'package-check', 'storefront', 'package-outline', 'trash-compact', 'chevron-right-wide', 'check-long',
|
|
11
12
|
'alert-triangle', 'arrow-left', 'arrow-up', 'attach', 'bell', 'blocks', 'check',
|
|
12
13
|
'chevron-down', 'chevron-left', 'chevron-right', 'close', 'copy', 'comment',
|
|
13
14
|
'message-circle', 'discord', 'download', 'draw', 'edit', 'external-link', 'eye',
|