@jini-ai/ui 0.4.9 → 0.5.1

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 (56) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/features/charts/browser.d.ts +39 -0
  3. package/dist/features/charts/browser.js +142 -0
  4. package/dist/features/charts/contract.d.ts +223 -0
  5. package/dist/features/charts/contract.js +21 -0
  6. package/dist/features/charts/index.d.ts +5 -0
  7. package/dist/features/charts/index.js +5 -0
  8. package/dist/features/charts/react.d.ts +27 -0
  9. package/dist/features/charts/react.js +73 -0
  10. package/dist/features/charts/registry.d.ts +35 -0
  11. package/dist/features/charts/registry.js +36 -0
  12. package/dist/features/charts/table.d.ts +14 -0
  13. package/dist/features/charts/table.js +60 -0
  14. package/dist/features/charts/wire.d.ts +88 -0
  15. package/dist/features/charts/wire.js +38 -0
  16. package/dist/features/html-viewer/dependencies.d.ts +1 -1
  17. package/dist/features/html-viewer/dependencies.js +2 -2
  18. package/dist/features/html-viewer/react/hooks/useDeckNavigation.d.ts +2 -2
  19. package/dist/features/html-viewer/react/hooks/useDeckNavigation.js +3 -3
  20. package/dist/features/i18n/universal.d.ts +3 -0
  21. package/dist/features/i18n/universal.js +3 -0
  22. package/dist/features/interactive-ui/index.d.ts +3 -18
  23. package/dist/features/interactive-ui/index.js +12 -6
  24. package/dist/features/interactive-ui/interactive-ui.css +1 -1
  25. package/dist/features/interactive-ui/manifests.d.ts +3 -3
  26. package/dist/features/interactive-ui/manifests.js +6 -6
  27. package/dist/features/interactive-ui/providers/shadcn/button.d.ts +1 -1
  28. package/dist/features/interactive-ui/registry.d.ts +12 -10
  29. package/dist/features/interactive-ui/registry.js +8 -6
  30. package/dist/features/panel-kit/fetch-query/adapter.tanstack.js +47 -18
  31. package/dist/features/settings/dialog/styles/settings-dialog.css +19 -3
  32. package/dist/features/tab-strip/react/components/TabBar.hooks.d.ts +16 -1
  33. package/dist/features/tab-strip/react/components/TabBar.hooks.js +31 -0
  34. package/dist/features/tab-strip/react/components/TabBar.js +3 -2
  35. package/dist/styles/variables.css +1 -1
  36. package/package.json +31 -12
  37. package/dist/features/interactive-ui/providers/recharts/bar-chart.d.ts +0 -8
  38. package/dist/features/interactive-ui/providers/recharts/bar-chart.js +0 -12
  39. package/dist/features/interactive-ui/providers/recharts/bar-chart.manifest.d.ts +0 -21
  40. package/dist/features/interactive-ui/providers/recharts/bar-chart.manifest.js +0 -18
  41. package/dist/features/interactive-ui/providers/recharts/chart-config.d.ts +0 -40
  42. package/dist/features/interactive-ui/providers/recharts/chart-config.js +0 -32
  43. package/dist/features/interactive-ui/providers/recharts/chart-tokens.d.ts +0 -49
  44. package/dist/features/interactive-ui/providers/recharts/chart-tokens.js +0 -39
  45. package/dist/features/interactive-ui/providers/recharts/chart.d.ts +0 -31
  46. package/dist/features/interactive-ui/providers/recharts/chart.js +0 -34
  47. package/dist/features/interactive-ui/providers/recharts/index.d.ts +0 -7
  48. package/dist/features/interactive-ui/providers/recharts/index.js +0 -7
  49. package/dist/features/interactive-ui/providers/recharts/line-chart.d.ts +0 -8
  50. package/dist/features/interactive-ui/providers/recharts/line-chart.js +0 -14
  51. package/dist/features/interactive-ui/providers/recharts/line-chart.manifest.d.ts +0 -20
  52. package/dist/features/interactive-ui/providers/recharts/line-chart.manifest.js +0 -17
  53. package/dist/features/interactive-ui/providers/recharts/pie-chart.d.ts +0 -10
  54. package/dist/features/interactive-ui/providers/recharts/pie-chart.js +0 -12
  55. package/dist/features/interactive-ui/providers/recharts/pie-chart.manifest.d.ts +0 -41
  56. package/dist/features/interactive-ui/providers/recharts/pie-chart.manifest.js +0 -15
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  /** Optional TanStack translation layer. No library types escape the fetch-query contract. */
3
3
  import { createContext, useCallback, useContext, useEffect, useMemo, useSyncExternalStore } from 'react';
4
- import { isCancelledError, QueryClient, QueryClientProvider, useMutation, useQuery } from '@tanstack/react-query';
4
+ import { isCancelledError, replaceEqualDeep, QueryClient, QueryClientProvider, useMutation, useQuery } from '@tanstack/react-query';
5
5
  import { DEFAULT_STALE_TIME, FetchQueryCache, IDLE_RETENTION, toQueryError } from '@jini-ai/core/fetch';
6
6
  import { browserFetchQueryEnvironment } from './environment.browser.js';
7
7
  import { resolveFetchQueryError, resolveFetchQueryStatus } from './adapter.react.js';
@@ -64,8 +64,8 @@ function historyFor({ scope, query }) {
64
64
  }
65
65
  return history;
66
66
  }
67
- /** Replacement writes win over either outcome of an older transport, across all loaders
68
- * and reactive reads. Weak keys follow TanStack's own query eviction lifecycle. */
67
+ /** Tag transport outcomes; final arbitration belongs to TanStack's synchronous save boundary.
68
+ * Weak query keys follow TanStack's own eviction lifecycle. */
69
69
  async function read({ scope, key, fetch }) {
70
70
  const query = scope.client.getQueryCache().find({ queryKey: key, exact: true });
71
71
  const history = historyFor({ scope, query });
@@ -77,21 +77,36 @@ async function read({ scope, key, fetch }) {
77
77
  return scope.client.getQueryData(key);
78
78
  if (value === undefined)
79
79
  throw new Error(`${JSON.stringify(key)} data is undefined`);
80
- if (request === history.request)
81
- history.error = null;
82
- return value;
80
+ return { generation, request, value };
83
81
  }
84
82
  catch (error) {
85
83
  if (generation !== history.replacement)
86
84
  return scope.client.getQueryData(key);
87
- // Earlier data remains useful, but a failed refresh is not a fresh cache hit.
88
- // The next loader/mount must retry even when its staleTime would retain that data.
89
- if (request === history.request) {
90
- history.error = toQueryError(error);
85
+ return { generation, request, error };
86
+ }
87
+ }
88
+ /** Structural sharing runs inside setData, immediately before publication. A check in read()
89
+ * alone leaves a promise-continuation gap, even when the retryer has already resolved.
90
+ * Current failures throw here; stale failures resolve to the newer replacement like stale data.
91
+ * @returns The accepted envelope, reusing TanStack's normal deep structural sharing.
92
+ */
93
+ function saveResult({ scope, key, previous, incoming }) {
94
+ const query = scope.client.getQueryCache().find({ queryKey: key, exact: true });
95
+ const history = historyFor({ scope, query });
96
+ const result = incoming;
97
+ if (result.generation !== history.replacement)
98
+ return scope.client.getQueryData(key);
99
+ if ('error' in result) {
100
+ // Earlier data stays useful, but a failed refresh is not a fresh cache hit.
101
+ if (result.request === history.request) {
102
+ history.error = toQueryError(result.error);
91
103
  query.invalidate();
92
104
  }
93
- throw error;
105
+ throw result.error;
94
106
  }
107
+ if (result.request === history.request)
108
+ history.error = null;
109
+ return replaceEqualDeep(previous, result);
95
110
  }
96
111
  /** Match the built-in cache's idle retention. TanStack's default gcTime (5 min) would
97
112
  * otherwise evict an unobserved entry before a longer staleTime says it is stale. */
@@ -102,7 +117,8 @@ function gcTimeFor({ staleTime }) {
102
117
  function useFetchQuery({ key, fetch }, { enabled = true, staleTime = DEFAULT_STALE_TIME, refetchOnWindowFocus = false } = {}) {
103
118
  const scope = useScope();
104
119
  const online = useOnline(scope);
105
- const query = useQuery({ queryKey: key, queryFn: () => read({ scope, key, fetch }), enabled: enabled && online, staleTime, gcTime: gcTimeFor({ staleTime }) }, scope.client);
120
+ const query = useQuery({ queryKey: key, queryFn: () => read({ scope, key, fetch }),
121
+ structuralSharing: (previous, incoming) => saveResult({ scope, key, previous, incoming }), enabled: enabled && online, staleTime, gcTime: gcTimeFor({ staleTime }) }, scope.client);
106
122
  const { refetch: refresh } = query;
107
123
  const refetch = useCallback(() => {
108
124
  if (scope.environment.isOnline()) {
@@ -127,9 +143,10 @@ function useFetchQuery({ key, fetch }, { enabled = true, staleTime = DEFAULT_STA
127
143
  // recovery, so a retry is never mistaken for evidence that the last failure is gone.
128
144
  const cached = scope.client.getQueryCache().find({ queryKey: key, exact: true });
129
145
  const history = historyFor({ scope, query: cached });
130
- const hasData = query.data !== undefined;
146
+ const data = query.data && 'value' in query.data ? query.data.value : undefined;
147
+ const hasData = data !== undefined;
131
148
  return {
132
- data: query.data,
149
+ data,
133
150
  status: resolveFetchQueryStatus({ disabled: !enabled, hasData, status: query.status }),
134
151
  error: resolveFetchQueryError({ error: history.error ?? query.error, disabled: !enabled, hasData }),
135
152
  isFetching: online && query.isFetching, refetch,
@@ -161,11 +178,13 @@ function useFetchMutation({ run }, { invalidates } = {}) {
161
178
  function useCachedLoader({ key, fetch }, { staleTime = DEFAULT_STALE_TIME } = {}) {
162
179
  const scope = useScope();
163
180
  return useMemo(() => {
164
- const options = { queryKey: key, queryFn: () => read({ scope, key, fetch }), staleTime, gcTime: gcTimeFor({ staleTime }) };
181
+ const options = { queryKey: key, queryFn: () => read({ scope, key, fetch }),
182
+ structuralSharing: (previous, incoming) => saveResult({ scope, key, previous, incoming }), staleTime, gcTime: gcTimeFor({ staleTime }) };
165
183
  return {
166
- peek: () => scope.client.getQueryData(key),
184
+ peek: () => scope.client.getQueryData(key)?.value,
167
185
  load: (required = { key, fetch }) => {
168
- const selected = { ...options, queryKey: required.key, queryFn: () => read({ scope, key: required.key, fetch: required.fetch }) };
186
+ const selected = { ...options, queryKey: required.key, queryFn: () => read({ scope, key: required.key, fetch: required.fetch }),
187
+ structuralSharing: (previous, incoming) => saveResult({ scope, key: required.key, previous, incoming }) };
169
188
  return scope.client.fetchQuery(selected).catch(error => {
170
189
  // A forced refresh can supersede a loader's request without aborting its transport.
171
190
  // Follow the new request instead of exposing TanStack's cancellation vocabulary.
@@ -177,6 +196,16 @@ function useCachedLoader({ key, fetch }, { staleTime = DEFAULT_STALE_TIME } = {}
177
196
  if (query.state.isInvalidated)
178
197
  return scope.client.fetchQuery(selected);
179
198
  return (query.promise ?? scope.client.getQueryData(required.key));
199
+ }).then(result => {
200
+ const query = scope.client.getQueryCache().find({ queryKey: required.key, exact: true });
201
+ const history = historyFor({ scope, query });
202
+ // Query.fetch returns the transport envelope, not structuralSharing's accepted value.
203
+ // Unwrap against the current generation so awaiting loaders agree with observers.
204
+ if (result.generation !== history.replacement)
205
+ return scope.client.getQueryData(required.key).value;
206
+ if ('error' in result)
207
+ throw result.error;
208
+ return result.value;
180
209
  });
181
210
  },
182
211
  replace: ({ value }) => {
@@ -184,7 +213,7 @@ function useCachedLoader({ key, fetch }, { staleTime = DEFAULT_STALE_TIME } = {}
184
213
  const history = historyFor({ scope, query });
185
214
  history.replacement += 1;
186
215
  history.error = null;
187
- scope.client.setQueryData(key, value);
216
+ scope.client.setQueryData(key, { generation: history.replacement, request: history.request, value });
188
217
  },
189
218
  };
190
219
  }, [scope, key, fetch, staleTime]);
@@ -1718,9 +1718,11 @@ select.jini-input {
1718
1718
  position: relative;
1719
1719
  }
1720
1720
 
1721
+ /* The right padding keeps the absolutely positioned copy button off the wrapped code; on a phone
1722
+ that much side padding would halve the line, so the code starts below the button instead. */
1721
1723
  .jini-snippet-pre {
1722
1724
  margin: 0;
1723
- padding: 14px 16px;
1725
+ padding: 14px 104px 14px 16px;
1724
1726
  overflow-x: auto;
1725
1727
  background: var(--jini-text-strong);
1726
1728
  border-radius: var(--jini-radius);
@@ -1748,6 +1750,12 @@ select.jini-input {
1748
1750
  background: color-mix(in srgb, var(--jini-text-strong) 75%, transparent);
1749
1751
  }
1750
1752
 
1753
+ @media (max-width: 640px) {
1754
+ .jini-snippet-pre {
1755
+ padding: 56px 16px 14px;
1756
+ }
1757
+ }
1758
+
1751
1759
  /* ============================================================
1752
1760
  Connectors tab — PORTED from OD's real source, not authored by eye.
1753
1761
  Sources: `foundry/integrations/open-design/reference/od-web-src.orig/
@@ -3585,13 +3593,21 @@ button.connector-action.is-loading { cursor: wait; }
3585
3593
  flex-wrap: wrap;
3586
3594
  }
3587
3595
 
3596
+ /* A wrapping row, not a fixed column grid: a card holds two or three fields (the model field only
3597
+ when the provider lists models) plus Clear, and the old three-column grid squeezed each input to
3598
+ a few characters on a phone and pushed Clear onto its own full-column row everywhere. Each field
3599
+ asks for 11rem, so a phone gets one field per line, wider screens fit the whole row, and the cap
3600
+ keeps a desktop field from stretching across the card. */
3588
3601
  .jini-media-provider-fields {
3589
- display: grid;
3590
- grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
3602
+ display: flex;
3603
+ flex-wrap: wrap;
3604
+ align-items: center;
3591
3605
  gap: 6px;
3592
3606
  }
3593
3607
 
3594
3608
  .jini-media-provider-field {
3609
+ flex: 1 1 11rem;
3610
+ max-width: 28rem;
3595
3611
  min-width: 0;
3596
3612
  }
3597
3613
 
@@ -1,4 +1,4 @@
1
- import type { KeyboardEvent as ReactKeyboardEvent } from "react";
1
+ import { type KeyboardEvent as ReactKeyboardEvent, type RefObject } from "react";
2
2
  import type { TabBarTab } from "./TabBar.js";
3
3
  /**
4
4
  * @file TabBar's derived props and WAI-ARIA automatic-activation keyboard behavior.
@@ -74,4 +74,19 @@ export declare function useTabBarKeyboard({ tabs, activeId, onChange }: {
74
74
  activeId: string;
75
75
  onChange: (id: string) => void;
76
76
  }, _optional?: Record<string, never>): TabBarKeyboardHandlers;
77
+ /**
78
+ * Keeps the selected tab visible when the strip scrolls sideways — a host may turn the wrapping
79
+ * row into one swipeable row on phones, and a deep link to a later tab (`?tab=…`) otherwise opens
80
+ * with its own label cut off at the edge. Only the strip's own `scrollLeft` moves: `scrollIntoView`
81
+ * would also scroll the page vertically to the strip, a jump on every tab change below the fold.
82
+ * A strip that wraps (no horizontal overflow) is left alone.
83
+ *
84
+ * @param input.activeId - The currently active tab's id; the strip re-checks when it changes.
85
+ * @returns A ref for the tablist container.
86
+ * @complexity O(n) per active-id change in the strip's tab count (one selector query).
87
+ * @example const stripRef = useActiveTabInView({ activeId });
88
+ */
89
+ export declare function useActiveTabInView({ activeId }: {
90
+ activeId: string;
91
+ }, _optional?: Record<string, never>): RefObject<HTMLDivElement | null>;
77
92
  //# sourceMappingURL=TabBar.hooks.d.ts.map
@@ -1,3 +1,4 @@
1
+ import { useEffect, useRef } from "react";
1
2
  import { agentHandle } from "@jini-ai/agentic";
2
3
  /**
3
4
  * @file TabBar's derived props and WAI-ARIA automatic-activation keyboard behavior.
@@ -112,4 +113,34 @@ export function useTabBarKeyboard({ tabs, activeId, onChange }, _optional = {})
112
113
  }
113
114
  return { onKeyDown };
114
115
  }
116
+ /**
117
+ * Keeps the selected tab visible when the strip scrolls sideways — a host may turn the wrapping
118
+ * row into one swipeable row on phones, and a deep link to a later tab (`?tab=…`) otherwise opens
119
+ * with its own label cut off at the edge. Only the strip's own `scrollLeft` moves: `scrollIntoView`
120
+ * would also scroll the page vertically to the strip, a jump on every tab change below the fold.
121
+ * A strip that wraps (no horizontal overflow) is left alone.
122
+ *
123
+ * @param input.activeId - The currently active tab's id; the strip re-checks when it changes.
124
+ * @returns A ref for the tablist container.
125
+ * @complexity O(n) per active-id change in the strip's tab count (one selector query).
126
+ * @example const stripRef = useActiveTabInView({ activeId });
127
+ */
128
+ export function useActiveTabInView({ activeId }, _optional = {}) {
129
+ const stripRef = useRef(null);
130
+ useEffect(() => {
131
+ const strip = stripRef.current;
132
+ const tab = strip?.querySelector('[role="tab"][aria-selected="true"]');
133
+ if (!strip || !tab || strip.scrollWidth <= strip.clientWidth)
134
+ return;
135
+ const stripBox = strip.getBoundingClientRect();
136
+ const tabBox = tab.getBoundingClientRect();
137
+ // A host's end padding (e.g. room for an edge fade) counts as off-screen.
138
+ const visibleRight = stripBox.right - (parseFloat(getComputedStyle(strip).paddingRight) || 0);
139
+ if (tabBox.left < stripBox.left)
140
+ strip.scrollLeft += tabBox.left - stripBox.left;
141
+ else if (tabBox.right > visibleRight)
142
+ strip.scrollLeft += tabBox.right - visibleRight;
143
+ }, [activeId]);
144
+ return stripRef;
145
+ }
115
146
  //# sourceMappingURL=TabBar.hooks.js.map
@@ -1,7 +1,7 @@
1
1
  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
- import { resolveTabBarTabIndex, tabHandleProps, useTabBarKeyboard } from "./TabBar.hooks.js";
4
+ import { resolveTabBarTabIndex, tabHandleProps, useActiveTabInView, useTabBarKeyboard } from "./TabBar.hooks.js";
5
5
  /** The visually-hidden accessible-name SUFFIX {@link TabBarTab.dot} adds — e.g. "GitHub Pages,
6
6
  * Connected", never "Connected GitHub Pages". Deliberately its own function rather than folded into
7
7
  * the visual dot span rendered before the label (`TabBarButton` below): the visual dot is
@@ -24,6 +24,7 @@ function TabBarButton({ tab, active, tabIndex, onChange, }) {
24
24
  /** Controlled button-based selection strip, including roving focus and optional agent handles. */
25
25
  export function TabBar({ tabs, activeId, onChange, ariaLabel, containerHandle }, _optional = {}) {
26
26
  const { onKeyDown } = useTabBarKeyboard({ tabs: tabs, activeId: activeId, onChange: onChange });
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))) }));
27
+ const stripRef = useActiveTabInView({ activeId: activeId });
28
+ return (_jsx(TabStripFrame, { ref: stripRef, 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))) }));
28
29
  }
29
30
  //# sourceMappingURL=TabBar.js.map
@@ -28,7 +28,7 @@
28
28
  }
29
29
 
30
30
  /* Default chart palette — what `--jini-chart-1`..`-6` fall back to when a host sets none (see
31
- interactive-ui/providers/recharts/chart-tokens.ts). Separate `-default-` names, so a host's own
31
+ the private charts-recharts/src/chart-tokens.ts). Separate `-default-` names, so a host's own
32
32
  `--jini-chart-N` always wins regardless of cascade order. Series 1 is the warm Claude orange, not
33
33
  the near-black `--jini-primary` ("Claude orange should be the default color", owner, 2026-10-05);
34
34
  2-6 are fixed-order categorical hues at matched lightness. Each mode was run through the dataviz
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jini-ai/ui",
3
- "version": "0.4.9",
3
+ "version": "0.5.1",
4
4
  "description": "Generic, product-neutral React UI primitives and feature domains. Framework-free types, decision rules, catalogs, and host port contracts live behind ./core (no React, no DOM). The Excalidraw and Lexical editors live behind ./sketch-editor and ./lexical-rich-text-editor; the GrapesJS-backed inline HTML editor lives behind ./html-editor.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -15,6 +15,7 @@
15
15
  "entries": {
16
16
  ".": "browser",
17
17
  "./core": "universal",
18
+ "./i18n": "universal",
18
19
  "./sketch-editor": "browser",
19
20
  "./lexical-rich-text-editor": "browser",
20
21
  "./html-editor": "browser",
@@ -36,7 +37,10 @@
36
37
  "./tab-strip": "browser",
37
38
  "./admin-widgets.css": "browser",
38
39
  "./theme": "browser",
39
- "./styles/*": "browser"
40
+ "./styles/*": "browser",
41
+ "./charts": "universal",
42
+ "./charts/browser": "browser",
43
+ "./charts/react": "browser"
40
44
  },
41
45
  "isolation": {
42
46
  "entries": {
@@ -132,6 +136,11 @@
132
136
  "import": "./dist/features/admin-widgets/index.js",
133
137
  "default": "./dist/features/admin-widgets/index.js"
134
138
  },
139
+ "./i18n": {
140
+ "types": "./dist/features/i18n/universal.d.ts",
141
+ "import": "./dist/features/i18n/universal.js",
142
+ "default": "./dist/features/i18n/universal.js"
143
+ },
135
144
  "./panel-kit": {
136
145
  "types": "./dist/features/panel-kit/index.d.ts",
137
146
  "import": "./dist/features/panel-kit/index.js",
@@ -158,7 +167,22 @@
158
167
  "import": "./dist/theme/index.js",
159
168
  "default": "./dist/theme/index.js"
160
169
  },
161
- "./styles/*": "./dist/styles/*"
170
+ "./styles/*": "./dist/styles/*",
171
+ "./charts": {
172
+ "types": "./dist/features/charts/index.d.ts",
173
+ "import": "./dist/features/charts/index.js",
174
+ "default": "./dist/features/charts/index.js"
175
+ },
176
+ "./charts/browser": {
177
+ "types": "./dist/features/charts/browser.d.ts",
178
+ "import": "./dist/features/charts/browser.js",
179
+ "default": "./dist/features/charts/browser.js"
180
+ },
181
+ "./charts/react": {
182
+ "types": "./dist/features/charts/react.d.ts",
183
+ "import": "./dist/features/charts/react.js",
184
+ "default": "./dist/features/charts/react.js"
185
+ }
162
186
  },
163
187
  "files": [
164
188
  "dist",
@@ -200,7 +224,6 @@
200
224
  "lexical": "0.36.2",
201
225
  "react": "^19.2.0",
202
226
  "react-dom": "^19.2.0",
203
- "recharts": "^3.10.1",
204
227
  "tailwindcss": "^4.1.16",
205
228
  "class-variance-authority": "^0.7.1",
206
229
  "clsx": "^2.1.1",
@@ -210,13 +233,13 @@
210
233
  "shiki": "^1.24.4",
211
234
  "tailwind-merge": "^2.6.0",
212
235
  "zod": "^3.25.76",
213
- "@jini-ai/integrations": "^0.4.2",
236
+ "@jini-ai/platform": "^0.5.6",
214
237
  "@jini-ai/agentic": "^0.4.4",
215
- "@jini-ai/platform": "^0.5.3",
216
- "@jini-ai/core": "^0.4.3"
238
+ "@jini-ai/core": "^0.4.5",
239
+ "@jini-ai/integrations": "^0.4.2"
217
240
  },
218
241
  "dependencies": {
219
- "@jini-ai/core": "^0.4.3"
242
+ "@jini-ai/core": "^0.4.5"
220
243
  },
221
244
  "peerDependencies": {
222
245
  "@jini-ai/integrations": "^0.4.2",
@@ -234,7 +257,6 @@
234
257
  "lexical": "0.36.2",
235
258
  "react": "^18.3.0 || ^19.0.0",
236
259
  "react-dom": "^18.3.0 || ^19.0.0",
237
- "recharts": "^3.10.1",
238
260
  "@jini-ai/platform": "^0.5.3",
239
261
  "@jini-ai/agentic": "^0.4.4",
240
262
  "class-variance-authority": "^0.7.1",
@@ -292,9 +314,6 @@
292
314
  "react-dom": {
293
315
  "optional": true
294
316
  },
295
- "recharts": {
296
- "optional": true
297
- },
298
317
  "@jini-ai/platform": {
299
318
  "optional": true
300
319
  },
@@ -1,8 +0,0 @@
1
- export interface BarChartProps {
2
- readonly data: readonly Record<string, unknown>[];
3
- readonly categoryKey: string;
4
- readonly valueKey: string;
5
- readonly color?: string;
6
- }
7
- export declare function BarChart({ data, categoryKey, valueKey, color }: BarChartProps): import("react").JSX.Element;
8
- //# sourceMappingURL=bar-chart.d.ts.map
@@ -1,12 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Bar, BarChart as RechartsBarChart, CartesianGrid, Tooltip, XAxis, YAxis } from 'recharts';
3
- import { CHART_AXIS_PROPS, CHART_CURSOR, CHART_GRID_STROKE, CHART_PRIMARY_COLOR } from './chart-tokens.js';
4
- import { chartTickFormatter, createChartConfig } from './chart-config.js';
5
- import { ChartContainer, ChartTooltipContent } from './chart.js';
6
- export function BarChart({ data, categoryKey, valueKey, color = CHART_PRIMARY_COLOR }) {
7
- const config = createChartConfig({ series: [{ key: valueKey, label: valueKey }] }, { color });
8
- const title = `${valueKey} by ${categoryKey}`;
9
- const description = `Bar chart with ${data.length} categories. Use arrow keys to explore values.`;
10
- return (_jsx(ChartContainer, { config: config, title: title, description: description, children: _jsxs(RechartsBarChart, { data: data, accessibilityLayer: true, title: title, desc: description, margin: { top: 16, right: 12, bottom: 8, left: 0 }, barCategoryGap: "24%", children: [_jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: CHART_GRID_STROKE, strokeOpacity: 0.6, vertical: false }), _jsx(XAxis, { dataKey: categoryKey, interval: "preserveStartEnd", minTickGap: 8, ...CHART_AXIS_PROPS }), _jsx(YAxis, { ...CHART_AXIS_PROPS, tickFormatter: chartTickFormatter, width: 48 }), _jsx(Tooltip, { cursor: CHART_CURSOR, content: _jsx(ChartTooltipContent, {}), isAnimationActive: false }), _jsx(Bar, { dataKey: valueKey, fill: config[valueKey].color, radius: [6, 6, 0, 0], maxBarSize: 40, isAnimationActive: false })] }) }));
11
- }
12
- //# sourceMappingURL=bar-chart.js.map
@@ -1,21 +0,0 @@
1
- import { z } from 'zod';
2
- import type { InteractiveComponentManifest } from '../../types.js';
3
- /** Same `.passthrough()`/`z.record` reasoning as `data-table.manifest.ts` — rows are arbitrary agent data. */
4
- export declare const rechartsBarChartPropsSchema: z.ZodObject<{
5
- data: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">;
6
- categoryKey: z.ZodString;
7
- valueKey: z.ZodString;
8
- color: z.ZodOptional<z.ZodString>;
9
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
10
- data: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">;
11
- categoryKey: z.ZodString;
12
- valueKey: z.ZodString;
13
- color: z.ZodOptional<z.ZodString>;
14
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
15
- data: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">;
16
- categoryKey: z.ZodString;
17
- valueKey: z.ZodString;
18
- color: z.ZodOptional<z.ZodString>;
19
- }, z.ZodTypeAny, "passthrough">>;
20
- export declare const rechartsBarChartManifest: InteractiveComponentManifest;
21
- //# sourceMappingURL=bar-chart.manifest.d.ts.map
@@ -1,18 +0,0 @@
1
- import { z } from 'zod';
2
- /** Same `.passthrough()`/`z.record` reasoning as `data-table.manifest.ts` — rows are arbitrary agent data. */
3
- export const rechartsBarChartPropsSchema = z
4
- .object({
5
- data: z.array(z.record(z.unknown())).min(1),
6
- categoryKey: z.string(),
7
- valueKey: z.string(),
8
- color: z.string().optional(),
9
- })
10
- .passthrough();
11
- export const rechartsBarChartManifest = {
12
- id: 'recharts.bar-chart',
13
- provider: 'recharts',
14
- capabilities: ['chart', 'bar-chart', 'graph'],
15
- propsSchema: rechartsBarChartPropsSchema,
16
- description: 'recharts BarChart (real npm dependency): one categorical axis, one numeric value per bar.',
17
- };
18
- //# sourceMappingURL=bar-chart.manifest.js.map
@@ -1,40 +0,0 @@
1
- export interface ChartSeries {
2
- readonly label: string;
3
- readonly color: string;
4
- readonly value?: number;
5
- }
6
- /** Provider-local configuration shared by marks, tooltip swatches and the legend. */
7
- export type ChartConfig = Readonly<Record<string, ChartSeries>>;
8
- interface SeriesInput {
9
- readonly key: string;
10
- readonly label: string;
11
- readonly value?: number;
12
- }
13
- /**
14
- * Assign the existing CSS palette to named series without mutating agent data.
15
- * @param input Series in display order, with stable keys independent of display labels.
16
- * @param options Optional legacy color override, applied to every series.
17
- * @returns A config keyed by series identity, including duplicate display labels.
18
- * @complexity O(n) time and space for n series; no I/O.
19
- */
20
- export declare function createChartConfig({ series }: {
21
- readonly series: readonly SeriesInput[];
22
- }, { color }?: {
23
- readonly color?: string | undefined;
24
- }): ChartConfig;
25
- /**
26
- * Format chart numbers with Intl; preserve categorical strings without numeric coercion.
27
- * @param input The value supplied by an axis, tooltip, legend or donut total.
28
- * @param options Compact notation for ticks/totals, standard notation for exact values.
29
- * @returns Display text, or an empty string for a missing value.
30
- * @complexity O(k) time and space for k characters in the formatted value; no I/O.
31
- */
32
- export declare function formatChartValue({ value }: {
33
- readonly value: unknown;
34
- }, { notation }?: {
35
- readonly notation?: 'standard' | 'compact';
36
- }): string;
37
- /** Recharts callback convention is positional; all formatting delegates to the shared owner. */
38
- export declare const chartTickFormatter: (value: unknown) => string;
39
- export {};
40
- //# sourceMappingURL=chart-config.d.ts.map
@@ -1,32 +0,0 @@
1
- import { CHART_SERIES_COLORS } from './chart-tokens.js';
2
- /**
3
- * Assign the existing CSS palette to named series without mutating agent data.
4
- * @param input Series in display order, with stable keys independent of display labels.
5
- * @param options Optional legacy color override, applied to every series.
6
- * @returns A config keyed by series identity, including duplicate display labels.
7
- * @complexity O(n) time and space for n series; no I/O.
8
- */
9
- export function createChartConfig({ series }, { color } = {}) {
10
- return Object.fromEntries(series.map((entry, index) => [entry.key, {
11
- label: entry.label,
12
- color: color ?? CHART_SERIES_COLORS[index % CHART_SERIES_COLORS.length],
13
- ...(entry.value === undefined ? {} : { value: entry.value }),
14
- }]));
15
- }
16
- /**
17
- * Format chart numbers with Intl; preserve categorical strings without numeric coercion.
18
- * @param input The value supplied by an axis, tooltip, legend or donut total.
19
- * @param options Compact notation for ticks/totals, standard notation for exact values.
20
- * @returns Display text, or an empty string for a missing value.
21
- * @complexity O(k) time and space for k characters in the formatted value; no I/O.
22
- */
23
- export function formatChartValue({ value }, { notation = 'standard' } = {}) {
24
- if (value == null)
25
- return '';
26
- if (typeof value !== 'number')
27
- return String(value);
28
- return new Intl.NumberFormat('en-US', { notation, maximumFractionDigits: notation === 'compact' ? 1 : 20 }).format(value);
29
- }
30
- /** Recharts callback convention is positional; all formatting delegates to the shared owner. */
31
- export const chartTickFormatter = (value) => formatChartValue({ value }, { notation: 'compact' });
32
- //# sourceMappingURL=chart-config.js.map
@@ -1,49 +0,0 @@
1
- /**
2
- * @module chart-tokens
3
- *
4
- * The CSS variables every recharts provider paints with — the host's theme owns the colors.
5
- *
6
- * Why `--jini-chart-*` with fallbacks rather than bare `--jini-primary`: a host that styles its own
7
- * chrome and never maps Jini's base tokens (an adopting admin shell) got Jini's near-black default bars, in
8
- * dark mode too ("the color is ugly", demo V3, 2026-10-05). A host sets `--jini-chart-1`..`-6`,
9
- * `--jini-chart-grid`, `--jini-chart-axis`, `--jini-chart-cursor`, `--jini-chart-surface` and
10
- * `--jini-chart-text` in its own CSS; a host that sets none keeps the base-token look for the
11
- * neutral tokens.
12
- *
13
- * Series colors fall back to Jini's default palette (`--jini-chart-default-1`..`-6`, warm orange
14
- * first, light + dark, in `styles/variables.css`), not to `--jini-primary` and the status tokens:
15
- * "Claude orange should be the default color" (owner, 2026-10-05). The values live in CSS, never
16
- * here, so a host's theme stays the single owner of color.
17
- */
18
- /** Categorical series colors in fixed order; larger datasets repeat the palette with named legends. */
19
- export declare const CHART_SERIES_COLORS: readonly ["var(--jini-chart-1, var(--jini-chart-default-1))", "var(--jini-chart-2, var(--jini-chart-default-2))", "var(--jini-chart-3, var(--jini-chart-default-3))", "var(--jini-chart-4, var(--jini-chart-default-4))", "var(--jini-chart-5, var(--jini-chart-default-5))", "var(--jini-chart-6, var(--jini-chart-default-6))"];
20
- /** The single-series color (bar fill, line stroke). */
21
- export declare const CHART_PRIMARY_COLOR: "var(--jini-chart-1, var(--jini-chart-default-1))";
22
- /** Grid lines and axis lines: recessive. */
23
- export declare const CHART_GRID_STROKE = "var(--jini-chart-grid, var(--jini-border))";
24
- /** Axis tick label props: muted ink, never the series color. */
25
- export declare const CHART_AXIS_TICK: {
26
- readonly fill: "var(--jini-chart-axis, var(--jini-muted))";
27
- readonly fontSize: 12;
28
- };
29
- /** Shared axis chrome; category visibility and numeric formatting remain separate concerns. */
30
- export declare const CHART_AXIS_PROPS: {
31
- readonly tick: {
32
- readonly fill: "var(--jini-chart-axis, var(--jini-muted))";
33
- readonly fontSize: 12;
34
- };
35
- readonly tickLine: false;
36
- readonly axisLine: false;
37
- readonly tickMargin: 10;
38
- };
39
- /** Hover points use the same series ink, with a surface halo in either color scheme. */
40
- export declare const CHART_ACTIVE_DOT: {
41
- readonly r: 5;
42
- readonly stroke: "var(--jini-chart-surface, var(--jini-bg))";
43
- readonly strokeWidth: 2;
44
- };
45
- /** The hover band behind a hovered bar (recharts' default is an opaque grey block). */
46
- export declare const CHART_CURSOR: {
47
- readonly fill: "var(--jini-chart-cursor, var(--jini-accent-soft))";
48
- };
49
- //# sourceMappingURL=chart-tokens.d.ts.map
@@ -1,39 +0,0 @@
1
- /**
2
- * @module chart-tokens
3
- *
4
- * The CSS variables every recharts provider paints with — the host's theme owns the colors.
5
- *
6
- * Why `--jini-chart-*` with fallbacks rather than bare `--jini-primary`: a host that styles its own
7
- * chrome and never maps Jini's base tokens (an adopting admin shell) got Jini's near-black default bars, in
8
- * dark mode too ("the color is ugly", demo V3, 2026-10-05). A host sets `--jini-chart-1`..`-6`,
9
- * `--jini-chart-grid`, `--jini-chart-axis`, `--jini-chart-cursor`, `--jini-chart-surface` and
10
- * `--jini-chart-text` in its own CSS; a host that sets none keeps the base-token look for the
11
- * neutral tokens.
12
- *
13
- * Series colors fall back to Jini's default palette (`--jini-chart-default-1`..`-6`, warm orange
14
- * first, light + dark, in `styles/variables.css`), not to `--jini-primary` and the status tokens:
15
- * "Claude orange should be the default color" (owner, 2026-10-05). The values live in CSS, never
16
- * here, so a host's theme stays the single owner of color.
17
- */
18
- /** Categorical series colors in fixed order; larger datasets repeat the palette with named legends. */
19
- export const CHART_SERIES_COLORS = [
20
- 'var(--jini-chart-1, var(--jini-chart-default-1))',
21
- 'var(--jini-chart-2, var(--jini-chart-default-2))',
22
- 'var(--jini-chart-3, var(--jini-chart-default-3))',
23
- 'var(--jini-chart-4, var(--jini-chart-default-4))',
24
- 'var(--jini-chart-5, var(--jini-chart-default-5))',
25
- 'var(--jini-chart-6, var(--jini-chart-default-6))',
26
- ];
27
- /** The single-series color (bar fill, line stroke). */
28
- export const CHART_PRIMARY_COLOR = CHART_SERIES_COLORS[0];
29
- /** Grid lines and axis lines: recessive. */
30
- export const CHART_GRID_STROKE = 'var(--jini-chart-grid, var(--jini-border))';
31
- /** Axis tick label props: muted ink, never the series color. */
32
- export const CHART_AXIS_TICK = { fill: 'var(--jini-chart-axis, var(--jini-muted))', fontSize: 12 };
33
- /** Shared axis chrome; category visibility and numeric formatting remain separate concerns. */
34
- export const CHART_AXIS_PROPS = { tick: CHART_AXIS_TICK, tickLine: false, axisLine: false, tickMargin: 10 };
35
- /** Hover points use the same series ink, with a surface halo in either color scheme. */
36
- export const CHART_ACTIVE_DOT = { r: 5, stroke: 'var(--jini-chart-surface, var(--jini-bg))', strokeWidth: 2 };
37
- /** The hover band behind a hovered bar (recharts' default is an opaque grey block). */
38
- export const CHART_CURSOR = { fill: 'var(--jini-chart-cursor, var(--jini-accent-soft))' };
39
- //# sourceMappingURL=chart-tokens.js.map
@@ -1,31 +0,0 @@
1
- import { type ReactElement, type ReactNode } from 'react';
2
- import { type ChartConfig } from './chart-config.js';
3
- interface ChartContainerProps {
4
- readonly config: ChartConfig;
5
- readonly title: string;
6
- readonly description: string;
7
- readonly children: ReactElement;
8
- }
9
- /** Shared, measurable chart frame with an accessible caption and a wrapping legend. */
10
- export declare function ChartContainer({ config, title, description, children }: ChartContainerProps): ReactElement;
11
- interface ChartTooltipEntry {
12
- readonly dataKey?: unknown;
13
- readonly name?: string | number;
14
- readonly value?: unknown;
15
- readonly color?: string;
16
- readonly type?: string;
17
- readonly payload?: {
18
- readonly _jiniChartKey?: string;
19
- };
20
- }
21
- interface ChartTooltipContentProps {
22
- readonly active?: boolean;
23
- readonly label?: ReactNode;
24
- readonly payload?: readonly ChartTooltipEntry[];
25
- }
26
- /** Shadcn-style tooltip card; series config owns both labels and swatch colors. */
27
- export declare function ChartTooltipContent({ active, label, payload }: ChartTooltipContentProps): ReactElement | null;
28
- /** Compact named legend; pie values remain readable without hover or outside labels. */
29
- export declare function ChartLegendContent(): ReactElement | null;
30
- export {};
31
- //# sourceMappingURL=chart.d.ts.map