@elabs-ai/components-ai 4.0.0 → 4.2.0
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/README.md +62 -12
- package/dist/{_audio-player-media-chrome-KA5DY54G.js → _audio-player-media-chrome-T3XVXWRZ.js} +8 -4
- package/dist/_audio-player-media-chrome-T3XVXWRZ.js.map +1 -0
- package/dist/{_flow-boundary-D63PJ65S.js → _flow-boundary-SHNWLQG5.js} +32 -43
- package/dist/_flow-boundary-SHNWLQG5.js.map +1 -0
- package/dist/{_persona-rive-RFR2EUWP.js → _persona-rive-JEG44YHX.js} +9 -5
- package/dist/_persona-rive-JEG44YHX.js.map +1 -0
- package/dist/index.d.ts +1152 -218
- package/dist/index.js +5601 -3241
- package/dist/index.js.map +1 -1
- package/package.json +30 -15
- package/src/__contract__/audio-visualizer.contract.test.tsx +49 -0
- package/src/__contract__/chat-shell.contract.test.tsx +49 -0
- package/src/__contract__/grouped-parts.contract.test.tsx +49 -0
- package/src/__contract__/image.contract.test.tsx +49 -0
- package/src/__contract__/markdown-view.contract.test.tsx +49 -0
- package/src/__contract__/message-feedback.contract.test.tsx +49 -0
- package/src/__contract__/message-form.contract.test.tsx +49 -0
- package/src/__contract__/message-table.contract.test.tsx +49 -0
- package/src/__contract__/model-provider-logo.contract.test.tsx +49 -0
- package/src/__contract__/persona.contract.test.tsx +49 -0
- package/src/__contract__/prompt-input-effort.contract.test.tsx +49 -0
- package/src/__contract__/prompt-input-mode.contract.test.tsx +49 -0
- package/src/_audio-player-media-chrome.tsx +102 -15
- package/src/_chat-shell-rail.tsx +2 -2
- package/src/_flow-boundary.tsx +68 -49
- package/src/_lazy-boundary-conformance.ts +38 -0
- package/src/_lazy-cjk.test.ts +43 -0
- package/src/_lazy-cjk.ts +73 -0
- package/src/_lazy-engine-boundary.tsx +61 -0
- package/src/_lazy-math.test.ts +63 -0
- package/src/_lazy-math.ts +90 -0
- package/src/_lazy-mermaid-absent.test.ts +53 -0
- package/src/_lazy-mermaid.test.ts +15 -0
- package/src/_lazy-mermaid.ts +24 -1
- package/src/_mermaid-error-panel.test.tsx +50 -0
- package/src/_mermaid-error-panel.tsx +66 -0
- package/src/_persona-rive.tsx +62 -10
- package/src/_streamdown-i18n.ts +94 -22
- package/src/_streamdown-safety.ts +170 -0
- package/src/_theme-scope-store.test.ts +83 -0
- package/src/_theme-scope-store.ts +103 -0
- package/src/agent-event.stories.tsx +97 -0
- package/src/agent-event.test.tsx +145 -0
- package/src/agent-event.tsx +187 -0
- package/src/agent-timeline.stories.tsx +9 -1
- package/src/agent.test.tsx +47 -0
- package/src/agent.tsx +43 -24
- package/src/agentic-workspace.stories.tsx +27 -20
- package/src/artifact.tsx +17 -14
- package/src/asset-preview.test.tsx +40 -0
- package/src/asset-preview.tsx +83 -12
- package/src/attachments.tsx +7 -4
- package/src/audio-player.test.tsx +78 -0
- package/src/audio-player.tsx +243 -56
- package/src/audio-visualizer.stories.tsx +126 -0
- package/src/audio-visualizer.test.tsx +438 -0
- package/src/audio-visualizer.tsx +367 -0
- package/src/canvas.stories.tsx +150 -1
- package/src/chat-shell.stories.tsx +18 -3
- package/src/chat.stories.tsx +16 -2
- package/src/code-block.stories.tsx +9 -1
- package/src/code-block.test.tsx +100 -1
- package/src/code-block.tsx +166 -103
- package/src/commit.tsx +30 -41
- package/src/composer.stories.tsx +372 -9
- package/src/composer.test.tsx +357 -6
- package/src/composer.tsx +217 -35
- package/src/confirmation.stories.tsx +72 -1
- package/src/confirmation.test.tsx +216 -2
- package/src/confirmation.tsx +264 -4
- package/src/context-panel.stories.tsx +9 -1
- package/src/context-panel.tsx +3 -2
- package/src/conversation.stories.tsx +86 -2
- package/src/conversation.test.tsx +66 -0
- package/src/conversation.tsx +52 -9
- package/src/diff-view.stories.tsx +196 -0
- package/src/diff-view.test.tsx +238 -0
- package/src/diff-view.tsx +697 -0
- package/src/environment-variables.tsx +20 -37
- package/src/file-tree.test.tsx +21 -0
- package/src/file-tree.tsx +12 -2
- package/src/gallery.tsx +4 -2
- package/src/index.ts +14 -4
- package/src/inline-citation.tsx +5 -5
- package/src/jsx-preview.stories.tsx +2 -2
- package/src/jsx-preview.tsx +151 -43
- package/src/markdown-view.stories.tsx +92 -1
- package/src/markdown-view.test.tsx +237 -2
- package/src/markdown-view.tsx +157 -6
- package/src/message-compare.stories.tsx +175 -0
- package/src/message-compare.test.tsx +207 -0
- package/src/message-compare.tsx +453 -0
- package/src/message-form.stories.tsx +65 -4
- package/src/message-form.test.tsx +8 -2
- package/src/message-form.tsx +15 -7
- package/src/message-table.stories.tsx +2 -2
- package/src/message-table.test.tsx +7 -0
- package/src/message-table.tsx +8 -4
- package/src/message.stories.tsx +9 -1
- package/src/message.test.tsx +247 -1
- package/src/message.tsx +125 -15
- package/src/microcopy.test.tsx +40 -0
- package/src/{model-selector.stories.tsx → model-provider-logo.stories.tsx} +17 -8
- package/src/model-provider-logo.test.tsx +109 -0
- package/src/model-provider-logo.tsx +183 -0
- package/src/open-in-chat.tsx +50 -29
- package/src/package-info.tsx +12 -12
- package/src/permission-mode-select.stories.tsx +82 -0
- package/src/permission-mode-select.test.tsx +100 -0
- package/src/permission-mode-select.tsx +137 -0
- package/src/persona-missing-peer.test.tsx +54 -0
- package/src/persona.tsx +68 -22
- package/src/plan.stories.tsx +166 -0
- package/src/plan.test.tsx +267 -0
- package/src/plan.tsx +182 -20
- package/src/prompt-input-effort.stories.tsx +123 -0
- package/src/prompt-input-effort.test.tsx +83 -0
- package/src/prompt-input-effort.tsx +136 -0
- package/src/prompt-input-mode.stories.tsx +108 -0
- package/src/prompt-input-mode.test.tsx +99 -0
- package/src/prompt-input-mode.tsx +169 -0
- package/src/prompt-input-slash.stories.tsx +211 -0
- package/src/prompt-input-slash.test.tsx +262 -0
- package/src/prompt-input-slash.tsx +541 -0
- package/src/prompt-input.stories.tsx +2 -2
- package/src/prompt-input.test.tsx +67 -1
- package/src/prompt-input.tsx +42 -4
- package/src/queue.tsx +4 -4
- package/src/reasoning.tsx +42 -17
- package/src/sandbox.stories.tsx +9 -1
- package/src/sandbox.tsx +3 -3
- package/src/schema-display.test.tsx +56 -0
- package/src/schema-display.tsx +71 -37
- package/src/selection-toolbar.stories.tsx +9 -1
- package/src/session-header.stories.tsx +128 -0
- package/src/session-header.test.tsx +138 -0
- package/src/session-header.tsx +243 -0
- package/src/session-status-bar.stories.tsx +73 -0
- package/src/session-status-bar.test.tsx +94 -0
- package/src/session-status-bar.tsx +165 -0
- package/src/snippet.stories.tsx +9 -1
- package/src/snippet.test.tsx +6 -2
- package/src/snippet.tsx +14 -34
- package/src/speech-input.test.tsx +109 -0
- package/src/speech-input.tsx +31 -3
- package/src/stack-trace.tsx +24 -36
- package/src/streamdown-i18n.test.tsx +24 -1
- package/src/task.stories.tsx +13 -3
- package/src/templates-ai-assistant.stories.tsx +21 -1
- package/src/test-results.tsx +47 -27
- package/src/token-usage.stories.tsx +24 -0
- package/src/token-usage.test.tsx +92 -0
- package/src/{context.tsx → token-usage.tsx} +73 -59
- package/src/tool-result-card.stories.tsx +9 -1
- package/src/tool.stories.tsx +13 -3
- package/src/tool.test.tsx +65 -0
- package/src/tool.tsx +73 -30
- package/src/transcription.tsx +1 -1
- package/src/turn-status.stories.tsx +124 -0
- package/src/turn-status.test.tsx +74 -0
- package/src/turn-status.tsx +174 -0
- package/src/use-audio-level.ts +104 -0
- package/src/voice-selector.tsx +5 -5
- package/src/web-preview.test.tsx +51 -1
- package/src/web-preview.tsx +46 -10
- package/dist/_audio-player-media-chrome-KA5DY54G.js.map +0 -1
- package/dist/_flow-boundary-D63PJ65S.js.map +0 -1
- package/dist/_interactive-terminal-xterm-ATJ5EW3G.js +0 -11
- package/dist/_interactive-terminal-xterm-ATJ5EW3G.js.map +0 -1
- package/dist/_persona-rive-RFR2EUWP.js.map +0 -1
- package/src/_interactive-terminal-xterm.ts +0 -32
- package/src/blocks-ai-composer.stories.tsx +0 -83
- package/src/context.stories.tsx +0 -16
- package/src/interactive-terminal.stories.tsx +0 -165
- package/src/interactive-terminal.test.tsx +0 -448
- package/src/interactive-terminal.tsx +0 -444
- package/src/model-selector.test.tsx +0 -55
- package/src/model-selector.tsx +0 -225
- package/src/terminal.tsx +0 -244
package/README.md
CHANGED
|
@@ -6,15 +6,14 @@
|
|
|
6
6
|
> ChatShell, Conversation, Message, PromptInput, Tool, Reasoning, citations.
|
|
7
7
|
|
|
8
8
|
Part of **brand-ui**, a source-owned, token-driven React component system.
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
Published to the **public npm registry** under the `@elabs-ai` scope — it
|
|
10
|
+
installs like any other npm dependency, with no registry configuration and
|
|
11
|
+
no token required. See `docs/CONSUMING.md`.
|
|
11
12
|
|
|
12
13
|
## Install
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
```json
|
|
17
|
-
"@elabs-ai/components-ai": "workspace:*"
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add @elabs-ai/components-tokens @elabs-ai/components-ai
|
|
18
17
|
```
|
|
19
18
|
|
|
20
19
|
## Set up styling (do not skip)
|
|
@@ -39,12 +38,13 @@ import { ThemeProvider } from "@elabs-ai/components-tokens";
|
|
|
39
38
|
|
|
40
39
|
## This package specifically
|
|
41
40
|
|
|
42
|
-
- `ai` (Vercel AI SDK)
|
|
43
|
-
- `@xyflow/react` is a peer too, if you render the agent canvas.
|
|
41
|
+
- `ai` (Vercel AI SDK) is a **types-only, optional** peer — your app owns the model calls.
|
|
42
|
+
- `@xyflow/react` is a peer too (required), if you render the agent canvas.
|
|
43
|
+
- `mermaid`, `@rive-app/react-webgl2`, `@xterm/xterm` + `@xterm/addon-fit` and `media-chrome` are optional peers reached only through a lazy `import()` (ADR 0019) — see "Only install what you render" below.
|
|
44
44
|
|
|
45
45
|
## What's in it
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
442 exported components — including `Agent`, `AgentContent`, `AgentEvent`, `AgentHeader`, `AgentInstructions`.
|
|
48
48
|
|
|
49
49
|
Don't guess the API — ask the CLI:
|
|
50
50
|
|
|
@@ -74,18 +74,68 @@ prompt for migrating an existing project: `docs/CONSUMING.md`.
|
|
|
74
74
|
|
|
75
75
|
## License
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
MIT
|
|
78
78
|
|
|
79
79
|
<!-- brand-ui:gen:readme:end -->
|
|
80
80
|
|
|
81
81
|
---
|
|
82
82
|
|
|
83
|
+
## Only install what you render
|
|
84
|
+
|
|
85
|
+
Six peers are optional. The package installs and builds with none of them —
|
|
86
|
+
each is reached only through a lazy `import()` (ADR 0019), and a feature whose
|
|
87
|
+
peer is absent renders an actionable message naming the package to install,
|
|
88
|
+
never a blank component or an unhandled rejection (ADR
|
|
89
|
+
[0032](../../docs/ADR/0032-optional-peer-dependency-policy.md)).
|
|
90
|
+
|
|
91
|
+
| Peer | Unlocks |
|
|
92
|
+
| ----------------------------------- | ----------------------------------------------------------- |
|
|
93
|
+
| `mermaid` | Mermaid diagrams in streamed markdown |
|
|
94
|
+
| `@rive-app/react-webgl2` | `Persona`'s animated avatar |
|
|
95
|
+
| `@xterm/xterm` + `@xterm/addon-fit` | `InteractiveTerminal` |
|
|
96
|
+
| `media-chrome` | `AudioPlayer`'s native-feeling controls |
|
|
97
|
+
| `ai` | Types for the Vercel AI SDK's `UIMessage` — no runtime cost |
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
pnpm add mermaid
|
|
101
|
+
pnpm add @rive-app/react-webgl2
|
|
102
|
+
pnpm add @xterm/xterm @xterm/addon-fit
|
|
103
|
+
pnpm add media-chrome
|
|
104
|
+
pnpm add ai
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`@xyflow/react` (the agent-canvas set) stays a **required** peer — install it
|
|
108
|
+
whenever you import from this package.
|
|
109
|
+
|
|
110
|
+
**Known limitation:** `mermaid`'s bytes are always installed, even when you
|
|
111
|
+
skip the peer — **two** of this package's own plain `dependencies` (not of
|
|
112
|
+
your app), `streamdown` and `@streamdown/mermaid`, each declare `mermaid` as
|
|
113
|
+
their own plain, non-optional dependency, so every package manager resolves
|
|
114
|
+
it regardless of what your app's manifest says. A hoisting layout
|
|
115
|
+
additionally makes `import("mermaid")` resolve, so the runtime capability-gap
|
|
116
|
+
panel never renders for you; skipping the peer still means you never have to
|
|
117
|
+
declare `mermaid` yourself, and a genuinely unresolved engine still fails
|
|
118
|
+
actionably rather than crashing. `pnpm check --rule optional-peer-transitives` (issue #94)
|
|
119
|
+
tracks this as a defeated optional peer and will fail the day it is fixed
|
|
120
|
+
upstream.
|
|
121
|
+
|
|
122
|
+
Full detail (exact peer version ranges, per-feature notes):
|
|
123
|
+
[`docs/CONSUMING.md`](../../docs/CONSUMING.md) §6.
|
|
124
|
+
|
|
83
125
|
## Package detail
|
|
84
126
|
|
|
85
127
|
Presentational AI/chat components — `ChatShell`, `Conversation`, `Message`,
|
|
86
128
|
`PromptInput`/`Composer`, `Tool`, `Reasoning`, `Sources`, `Artifact`,
|
|
87
129
|
`ContextPanel`, and the workspace/agent set.
|
|
88
130
|
|
|
131
|
+
**`Composer` is the chat input. Every control the `PromptInput` family ships is
|
|
132
|
+
reachable from a `Composer` prop; drop to `PromptInput` only for a bespoke
|
|
133
|
+
shell.** The slots: `modelPicker` (pass a `ModelPicker` from
|
|
134
|
+
`@elabs-ai/components-ui`), `mode` (`PromptInputMode`), `effort`
|
|
135
|
+
(`PromptInputEffort`) and `slashCommands` (`PromptInputSlash`). The footer order
|
|
136
|
+
is `attach · modelPicker · mode · effort │ voice · send`, matching
|
|
137
|
+
`TerminalComposer` so the chat and console skins agree.
|
|
138
|
+
|
|
89
139
|
The components render the AI SDK `UIMessage` data model. **Your app owns the model
|
|
90
140
|
calls** (`useChat`, transport, providers) — `@elabs-ai/components-ai` imports the `ai` SDK as
|
|
91
141
|
**types only**, never at runtime (D6 / ADR-0008). See `docs/DECISIONS.md` §D5.
|
|
@@ -101,7 +151,7 @@ restrictive CSP these fail — usually silently.
|
|
|
101
151
|
|
|
102
152
|
| What | Origin | Directive | Escape hatch |
|
|
103
153
|
| ---------------------------------- | ------------------------------------------------------------- | ----------------- | --------------------------------------------------------------- |
|
|
104
|
-
| `
|
|
154
|
+
| `ModelProviderLogo` provider logos | `models.dev` | `img-src` | `src` for a self-hosted asset, or `fallback` |
|
|
105
155
|
| `Persona` Rive artwork | `*.public.blob.vercel-storage.com` | `connect-src` | self-host and pass `src`; blocked loads render a fallback orb |
|
|
106
156
|
| `OpenIn*` deep links | chatgpt.com, claude.ai, cursor.com, scira.ai, t3.chat, v0.app | none (navigation) | don't render the component — note the prompt travels in the URL |
|
|
107
157
|
|
|
@@ -128,6 +178,6 @@ Heavy engines load on demand rather than riding in your entry chunk:
|
|
|
128
178
|
`preloadMermaid()` to warm it early on a surface you know renders diagrams.
|
|
129
179
|
- **The Rive WebGL2 runtime** loads only when a `Persona` mounts.
|
|
130
180
|
|
|
131
|
-
Both are enforced by `pnpm heavy-deps
|
|
181
|
+
Both are enforced by `pnpm check --rule eager-heavy-deps`. See ADR 0019. The remaining eager
|
|
132
182
|
engines (React Flow, xterm, media-chrome) are tracked in that gate's baseline
|
|
133
183
|
(the React Flow canvas set is a separate, intentional surface — see ADR 0018).
|
package/dist/{_audio-player-media-chrome-KA5DY54G.js → _audio-player-media-chrome-T3XVXWRZ.js}
RENAMED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
// src/_audio-player-media-chrome.tsx
|
|
5
5
|
import { Button, ButtonGroup, ButtonGroupText } from "@elabs-ai/components-ui";
|
|
6
6
|
import { cn } from "@elabs-ai/components-ui/lib/cn";
|
|
7
|
-
import
|
|
7
|
+
import * as MediaChromeReactModule from "media-chrome/react";
|
|
8
|
+
import { jsx } from "react/jsx-runtime";
|
|
9
|
+
var {
|
|
8
10
|
MediaControlBar,
|
|
9
11
|
MediaController,
|
|
10
12
|
MediaDurationDisplay,
|
|
@@ -15,8 +17,10 @@ import {
|
|
|
15
17
|
MediaTimeDisplay,
|
|
16
18
|
MediaTimeRange,
|
|
17
19
|
MediaVolumeRange
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
+
} = MediaChromeReactModule;
|
|
21
|
+
if (!MediaController) {
|
|
22
|
+
throw new Error("Cannot find module 'media-chrome'");
|
|
23
|
+
}
|
|
20
24
|
var AudioPlayer = ({ children, style, ...props }) => /* @__PURE__ */ jsx(
|
|
21
25
|
MediaController,
|
|
22
26
|
{
|
|
@@ -78,4 +82,4 @@ export {
|
|
|
78
82
|
AudioPlayerTimeRange,
|
|
79
83
|
AudioPlayerVolumeRange
|
|
80
84
|
};
|
|
81
|
-
//# sourceMappingURL=_audio-player-media-chrome-
|
|
85
|
+
//# sourceMappingURL=_audio-player-media-chrome-T3XVXWRZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/_audio-player-media-chrome.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * The media-chrome half of `AudioPlayer`, split out so it can be `lazy()`-loaded.\n *\n * `media-chrome` declares no `sideEffects`, so a static import from\n * `audio-player.tsx` put the whole custom-element library (and the\n * `media-chrome/react` wrappers that register it) into the entry chunk of every\n * `@elabs-ai/components-ai` consumer — including the vast majority that\n * never render an `AudioPlayer`. Keeping every media-chrome *value* import in\n * this module, reached only through\n * `lazy(() => import(\"./_audio-player-media-chrome\"))`, confines it to its own\n * chunk. `audio-player.tsx` owns the public prop types, and — since issue #101\n * — owns them as OWNED types (`AudioPlayerPartProps` and its ten aliases) that\n * never reference `media-chrome/react`'s own types; this module still imports\n * the REAL media-chrome values (below) and, at the bottom of the file, proves\n * every owned type stays assignable to its real counterpart.\n *\n * See ADR 0019 and `pnpm heavy-deps:check`.\n *\n * @lazy-boundary This module must only ever be reached via `import()`. The gate\n * fails if anything imports it statically, which would put media-chrome back in\n * the entry chunk and make the `lazy()` pointless.\n */\nimport { Button, ButtonGroup, ButtonGroupText } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n// A NAMED import (`import { MediaController } from \"media-chrome/react\"`) is a\n// static ESM binding a bundler must resolve at build time. Now that\n// `media-chrome` is a genuinely optional peer (issue #33), a consumer who has\n// not installed it hits that resolution at the worst possible time: Vite's own\n// optional-peer-dependency handling swaps in a build-time stub with no\n// exports, and Rollup's static named-export check then fails the WHOLE APP\n// BUILD — not a runtime error any `.catch()`/error boundary could ever see\n// (confirmed against `fixtures/consumer-smoke`'s real Vite build). A namespace\n// import defers every one of these to a plain property lookup, which Rollup\n// does not statically validate, so the build always succeeds; a genuinely\n// missing peer instead resolves the destructured names below to `undefined`,\n// and the guard turns that into a render-phase throw `LazyEngineBoundary`\n// already catches (see `persona.tsx`'s identical pattern).\nimport * as MediaChromeReactModule from \"media-chrome/react\";\nimport type { ComponentProps, CSSProperties } from \"react\";\n\nimport type { AssertAssignable } from \"./_lazy-boundary-conformance\";\nimport type {\n AudioPlayerControlBarProps,\n AudioPlayerDurationDisplayProps,\n AudioPlayerMuteButtonProps,\n AudioPlayerPlayButtonProps,\n AudioPlayerProps,\n AudioPlayerSeekBackwardButtonProps,\n AudioPlayerSeekForwardButtonProps,\n AudioPlayerTimeDisplayProps,\n AudioPlayerTimeRangeProps,\n AudioPlayerVolumeRangeProps,\n} from \"./audio-player\";\n\nconst {\n MediaControlBar,\n MediaController,\n MediaDurationDisplay,\n MediaMuteButton,\n MediaPlayButton,\n MediaSeekBackwardButton,\n MediaSeekForwardButton,\n MediaTimeDisplay,\n MediaTimeRange,\n MediaVolumeRange,\n} = MediaChromeReactModule;\n\n// The peer is genuinely absent (Vite's build-time stub, or any other bundler\n// that resolves an optional peer to an empty module) — surface a message\n// `isModuleNotFoundMessage` recognizes, so the caller's `renderMissing` still\n// gets the actionable \"install media-chrome\" copy rather than a raw\n// \"MediaController is not a valid JSX element\" crash.\nif (!MediaController) {\n throw new Error(\"Cannot find module 'media-chrome'\");\n}\n\nexport const AudioPlayer = ({ children, style, ...props }: AudioPlayerProps) => (\n <MediaController\n audio\n style={\n {\n \"--media-background-color\": \"transparent\",\n \"--media-button-icon-height\": \"1rem\",\n \"--media-button-icon-width\": \"1rem\",\n \"--media-control-background\": \"transparent\",\n \"--media-control-hover-background\": \"var(--color-accent)\",\n \"--media-control-padding\": \"0\",\n \"--media-font\": \"var(--font-sans)\",\n \"--media-font-size\": \"10px\",\n \"--media-icon-color\": \"currentColor\",\n \"--media-preview-time-background\": \"var(--color-background)\",\n \"--media-preview-time-border-radius\": \"var(--radius-md)\",\n \"--media-preview-time-text-shadow\": \"none\",\n \"--media-primary-color\": \"var(--color-primary)\",\n \"--media-range-bar-color\": \"var(--color-primary)\",\n \"--media-range-track-background\": \"var(--color-secondary)\",\n \"--media-secondary-color\": \"var(--color-secondary)\",\n \"--media-text-color\": \"var(--color-foreground)\",\n \"--media-tooltip-arrow-display\": \"none\",\n \"--media-tooltip-background\": \"var(--color-background)\",\n \"--media-tooltip-border-radius\": \"var(--radius-md)\",\n ...style,\n } as CSSProperties\n }\n {...props}\n >\n {children}\n </MediaController>\n);\n\nexport const AudioPlayerControlBar = ({ children, ...props }: AudioPlayerControlBarProps) => (\n <MediaControlBar {...props}>\n <ButtonGroup orientation=\"horizontal\">{children}</ButtonGroup>\n </MediaControlBar>\n);\n\nexport const AudioPlayerPlayButton = ({ className, ...props }: AudioPlayerPlayButtonProps) => (\n <Button asChild size=\"icon-sm\" variant=\"outline\">\n <MediaPlayButton className={cn(\"bg-transparent\", className)} {...props} />\n </Button>\n);\n\nexport const AudioPlayerSeekBackwardButton = ({\n seekOffset = 10,\n ...props\n}: AudioPlayerSeekBackwardButtonProps) => (\n <Button asChild size=\"icon-sm\" variant=\"outline\">\n <MediaSeekBackwardButton seekOffset={seekOffset} {...props} />\n </Button>\n);\n\nexport const AudioPlayerSeekForwardButton = ({\n seekOffset = 10,\n ...props\n}: AudioPlayerSeekForwardButtonProps) => (\n <Button asChild size=\"icon-sm\" variant=\"outline\">\n <MediaSeekForwardButton seekOffset={seekOffset} {...props} />\n </Button>\n);\n\nexport const AudioPlayerTimeDisplay = ({ className, ...props }: AudioPlayerTimeDisplayProps) => (\n <ButtonGroupText asChild className=\"bg-transparent\">\n <MediaTimeDisplay className={cn(\"tabular-nums\", className)} {...props} />\n </ButtonGroupText>\n);\n\nexport const AudioPlayerTimeRange = ({ className, ...props }: AudioPlayerTimeRangeProps) => (\n <ButtonGroupText asChild className=\"bg-transparent\">\n <MediaTimeRange className={cn(\"\", className)} {...props} />\n </ButtonGroupText>\n);\n\nexport const AudioPlayerDurationDisplay = ({\n className,\n ...props\n}: AudioPlayerDurationDisplayProps) => (\n <ButtonGroupText asChild className=\"bg-transparent\">\n <MediaDurationDisplay className={cn(\"tabular-nums\", className)} {...props} />\n </ButtonGroupText>\n);\n\nexport const AudioPlayerMuteButton = ({ className, ...props }: AudioPlayerMuteButtonProps) => (\n <ButtonGroupText asChild className=\"bg-transparent\">\n <MediaMuteButton className={cn(\"\", className)} {...props} />\n </ButtonGroupText>\n);\n\nexport const AudioPlayerVolumeRange = ({ className, ...props }: AudioPlayerVolumeRangeProps) => (\n <ButtonGroupText asChild className=\"bg-transparent\">\n <MediaVolumeRange className={cn(\"\", className)} {...props} />\n </ButtonGroupText>\n);\n\n/**\n * Conformance assertions (issue #101): compile-time proof that every OWNED\n * type declared in `audio-player.tsx` stays assignable to its REAL\n * `media-chrome/react` counterpart — this module still has the real values\n * (and therefore the real types) in scope, since it is the one place they are\n * allowed to be (see the module doc comment above). `AssertAssignable`'s type\n * parameter is constrained (`TOwned extends TReal`), so if a future\n * media-chrome release narrows an element's props in a way the owned type no\n * longer satisfies, ONE of these ten lines fails to typecheck — caught by\n * `pnpm --filter @elabs-ai/components-ai typecheck` locally, never shipped as\n * a silent mismatch to a consumer using the real component underneath.\n *\n * `AssertAssignable` itself is shared with `_persona-rive.tsx` via\n * `_lazy-boundary-conformance.ts` — see that module's doc comment for what\n * this check can and cannot prove (it is one-directional: it cannot catch the\n * owned type being NARROWER than the real one, which is exactly what these\n * owned types are — see the CHANGELOG's \"Breaking (types)\" entry).\n */\nexport type _AudioPlayerPropsConformance = AssertAssignable<\n AudioPlayerProps,\n Omit<ComponentProps<typeof MediaController>, \"audio\">\n>;\nexport type _AudioPlayerControlBarPropsConformance = AssertAssignable<\n AudioPlayerControlBarProps,\n ComponentProps<typeof MediaControlBar>\n>;\nexport type _AudioPlayerPlayButtonPropsConformance = AssertAssignable<\n AudioPlayerPlayButtonProps,\n ComponentProps<typeof MediaPlayButton>\n>;\nexport type _AudioPlayerSeekBackwardButtonPropsConformance = AssertAssignable<\n AudioPlayerSeekBackwardButtonProps,\n ComponentProps<typeof MediaSeekBackwardButton>\n>;\nexport type _AudioPlayerSeekForwardButtonPropsConformance = AssertAssignable<\n AudioPlayerSeekForwardButtonProps,\n ComponentProps<typeof MediaSeekForwardButton>\n>;\nexport type _AudioPlayerTimeDisplayPropsConformance = AssertAssignable<\n AudioPlayerTimeDisplayProps,\n ComponentProps<typeof MediaTimeDisplay>\n>;\nexport type _AudioPlayerTimeRangePropsConformance = AssertAssignable<\n AudioPlayerTimeRangeProps,\n ComponentProps<typeof MediaTimeRange>\n>;\nexport type _AudioPlayerDurationDisplayPropsConformance = AssertAssignable<\n AudioPlayerDurationDisplayProps,\n ComponentProps<typeof MediaDurationDisplay>\n>;\nexport type _AudioPlayerMuteButtonPropsConformance = AssertAssignable<\n AudioPlayerMuteButtonProps,\n ComponentProps<typeof MediaMuteButton>\n>;\nexport type _AudioPlayerVolumeRangePropsConformance = AssertAssignable<\n AudioPlayerVolumeRangeProps,\n ComponentProps<typeof MediaVolumeRange>\n>;\n"],"mappings":";;;;AAwBA,SAAS,QAAQ,aAAa,uBAAuB;AACrD,SAAS,UAAU;AAcnB,YAAY,4BAA4B;AAwCtC;AAvBF,IAAM;AAAA,EACJ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,IAAI;AAOJ,IAAI,CAAC,iBAAiB;AACpB,QAAM,IAAI,MAAM,mCAAmC;AACrD;AAEO,IAAM,cAAc,CAAC,EAAE,UAAU,OAAO,GAAG,MAAM,MACtD;AAAA,EAAC;AAAA;AAAA,IACC,OAAK;AAAA,IACL,OACE;AAAA,MACE,4BAA4B;AAAA,MAC5B,8BAA8B;AAAA,MAC9B,6BAA6B;AAAA,MAC7B,8BAA8B;AAAA,MAC9B,oCAAoC;AAAA,MACpC,2BAA2B;AAAA,MAC3B,gBAAgB;AAAA,MAChB,qBAAqB;AAAA,MACrB,sBAAsB;AAAA,MACtB,mCAAmC;AAAA,MACnC,sCAAsC;AAAA,MACtC,oCAAoC;AAAA,MACpC,yBAAyB;AAAA,MACzB,2BAA2B;AAAA,MAC3B,kCAAkC;AAAA,MAClC,2BAA2B;AAAA,MAC3B,sBAAsB;AAAA,MACtB,iCAAiC;AAAA,MACjC,8BAA8B;AAAA,MAC9B,iCAAiC;AAAA,MACjC,GAAG;AAAA,IACL;AAAA,IAED,GAAG;AAAA,IAEH;AAAA;AACH;AAGK,IAAM,wBAAwB,CAAC,EAAE,UAAU,GAAG,MAAM,MACzD,oBAAC,mBAAiB,GAAG,OACnB,8BAAC,eAAY,aAAY,cAAc,UAAS,GAClD;AAGK,IAAM,wBAAwB,CAAC,EAAE,WAAW,GAAG,MAAM,MAC1D,oBAAC,UAAO,SAAO,MAAC,MAAK,WAAU,SAAQ,WACrC,8BAAC,mBAAgB,WAAW,GAAG,kBAAkB,SAAS,GAAI,GAAG,OAAO,GAC1E;AAGK,IAAM,gCAAgC,CAAC;AAAA,EAC5C,aAAa;AAAA,EACb,GAAG;AACL,MACE,oBAAC,UAAO,SAAO,MAAC,MAAK,WAAU,SAAQ,WACrC,8BAAC,2BAAwB,YAAyB,GAAG,OAAO,GAC9D;AAGK,IAAM,+BAA+B,CAAC;AAAA,EAC3C,aAAa;AAAA,EACb,GAAG;AACL,MACE,oBAAC,UAAO,SAAO,MAAC,MAAK,WAAU,SAAQ,WACrC,8BAAC,0BAAuB,YAAyB,GAAG,OAAO,GAC7D;AAGK,IAAM,yBAAyB,CAAC,EAAE,WAAW,GAAG,MAAM,MAC3D,oBAAC,mBAAgB,SAAO,MAAC,WAAU,kBACjC,8BAAC,oBAAiB,WAAW,GAAG,gBAAgB,SAAS,GAAI,GAAG,OAAO,GACzE;AAGK,IAAM,uBAAuB,CAAC,EAAE,WAAW,GAAG,MAAM,MACzD,oBAAC,mBAAgB,SAAO,MAAC,WAAU,kBACjC,8BAAC,kBAAe,WAAW,GAAG,IAAI,SAAS,GAAI,GAAG,OAAO,GAC3D;AAGK,IAAM,6BAA6B,CAAC;AAAA,EACzC;AAAA,EACA,GAAG;AACL,MACE,oBAAC,mBAAgB,SAAO,MAAC,WAAU,kBACjC,8BAAC,wBAAqB,WAAW,GAAG,gBAAgB,SAAS,GAAI,GAAG,OAAO,GAC7E;AAGK,IAAM,wBAAwB,CAAC,EAAE,WAAW,GAAG,MAAM,MAC1D,oBAAC,mBAAgB,SAAO,MAAC,WAAU,kBACjC,8BAAC,mBAAgB,WAAW,GAAG,IAAI,SAAS,GAAI,GAAG,OAAO,GAC5D;AAGK,IAAM,yBAAyB,CAAC,EAAE,WAAW,GAAG,MAAM,MAC3D,oBAAC,mBAAgB,SAAO,MAAC,WAAU,kBACjC,8BAAC,oBAAiB,WAAW,GAAG,IAAI,SAAS,GAAI,GAAG,OAAO,GAC7D;","names":[]}
|
|
@@ -78,53 +78,42 @@ var EdgeTemporary = ({
|
|
|
78
78
|
}
|
|
79
79
|
);
|
|
80
80
|
};
|
|
81
|
-
var
|
|
82
|
-
const
|
|
83
|
-
const handle =
|
|
84
|
-
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
|
|
81
|
+
var getHandleAnchor = (node, handleType, preferred) => {
|
|
82
|
+
const bounds = node.internals.handleBounds?.[handleType] ?? [];
|
|
83
|
+
const handle = bounds.find((h) => h.position === preferred) ?? bounds[0];
|
|
84
|
+
const origin = node.internals.positionAbsolute;
|
|
85
|
+
if (handle) {
|
|
86
|
+
const offsetX = handle.position === Position.Left ? 0 : handle.position === Position.Right ? handle.width : handle.width / 2;
|
|
87
|
+
const offsetY = handle.position === Position.Top ? 0 : handle.position === Position.Bottom ? handle.height : handle.height / 2;
|
|
88
|
+
return {
|
|
89
|
+
position: handle.position,
|
|
90
|
+
x: origin.x + handle.x + offsetX,
|
|
91
|
+
y: origin.y + handle.y + offsetY
|
|
92
|
+
};
|
|
88
93
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
switch (
|
|
92
|
-
case Position.Left:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
case Position.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
case Position.Top: {
|
|
101
|
-
offsetY = 0;
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
case Position.Bottom: {
|
|
105
|
-
offsetY = handle.height;
|
|
106
|
-
break;
|
|
107
|
-
}
|
|
108
|
-
default: {
|
|
109
|
-
throw new Error(`Invalid handle position: ${handlePosition}`);
|
|
110
|
-
}
|
|
94
|
+
const width = node.measured.width ?? 0;
|
|
95
|
+
const height = node.measured.height ?? 0;
|
|
96
|
+
switch (preferred) {
|
|
97
|
+
case Position.Left:
|
|
98
|
+
return { position: preferred, x: origin.x, y: origin.y + height / 2 };
|
|
99
|
+
case Position.Right:
|
|
100
|
+
return { position: preferred, x: origin.x + width, y: origin.y + height / 2 };
|
|
101
|
+
case Position.Top:
|
|
102
|
+
return { position: preferred, x: origin.x + width / 2, y: origin.y };
|
|
103
|
+
default:
|
|
104
|
+
return { position: preferred, x: origin.x + width / 2, y: origin.y + height };
|
|
111
105
|
}
|
|
112
|
-
const x = node.internals.positionAbsolute.x + handle.x + offsetX;
|
|
113
|
-
const y = node.internals.positionAbsolute.y + handle.y + offsetY;
|
|
114
|
-
return [x, y];
|
|
115
106
|
};
|
|
116
107
|
var getEdgeParams = (source, target) => {
|
|
117
|
-
const
|
|
118
|
-
const
|
|
119
|
-
const targetPos = Position.Left;
|
|
120
|
-
const [tx, ty] = getHandleCoordsByPosition(target, targetPos);
|
|
108
|
+
const from = getHandleAnchor(source, "source", Position.Right);
|
|
109
|
+
const to = getHandleAnchor(target, "target", Position.Left);
|
|
121
110
|
return {
|
|
122
|
-
sourcePos,
|
|
123
|
-
sx,
|
|
124
|
-
sy,
|
|
125
|
-
targetPos,
|
|
126
|
-
tx,
|
|
127
|
-
ty
|
|
111
|
+
sourcePos: from.position,
|
|
112
|
+
sx: from.x,
|
|
113
|
+
sy: from.y,
|
|
114
|
+
targetPos: to.position,
|
|
115
|
+
tx: to.x,
|
|
116
|
+
ty: to.y
|
|
128
117
|
};
|
|
129
118
|
};
|
|
130
119
|
var EdgeAnimated = ({ id, source, target, markerEnd, style }) => {
|
|
@@ -183,4 +172,4 @@ export {
|
|
|
183
172
|
Panel,
|
|
184
173
|
Toolbar
|
|
185
174
|
};
|
|
186
|
-
//# sourceMappingURL=_flow-boundary-
|
|
175
|
+
//# sourceMappingURL=_flow-boundary-SHNWLQG5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/_flow-boundary.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * The React Flow half of `@elabs-ai/components-ai`'s in-chat agent\n * workspace graph — `Canvas`, `Controls`, `Edge`, `Node`, `Panel`, `Toolbar` —\n * split out so the engine can be `lazy()`-loaded.\n *\n * `@xyflow/react` declares no `sideEffects`, so the six static imports it used to\n * have (one per public module, plus `@xyflow/react/dist/style.css`) put the whole\n * engine into the entry chunk of every consumer, canvas rendered or not. **The\n * stylesheet import has to live here too**: a bare\n * `import \"@xyflow/react/dist/style.css\"` in `canvas.tsx` keeps the edge alive on\n * its own.\n *\n * This is ONE boundary for all six parts on purpose (see `_flow-lazy.ts`): six\n * boundaries would be six chunks all pulling the same engine. The public modules\n * keep their `import type { … } from \"@xyflow/react\"` lines (types erase) and\n * their prop types, and render a `lazy()` wrapper over the implementations here.\n * `connection.tsx` is type-only already and needs no boundary.\n *\n * See ADR 0019 and `pnpm heavy-deps:check`.\n *\n * @lazy-boundary This module must only ever be reached via `import()`. The gate\n * fails if anything imports it statically, which would put React Flow back in\n * the entry chunk and make the `lazy()` pointless.\n */\nimport { Card } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport type { EdgeProps, InternalNode, Node as FlowNode } from \"@xyflow/react\";\nimport {\n Background,\n BaseEdge,\n Controls as ControlsPrimitive,\n getBezierPath,\n getSimpleBezierPath,\n Handle,\n NodeToolbar,\n Panel as PanelPrimitive,\n Position,\n ReactFlow,\n useInternalNode,\n} from \"@xyflow/react\";\n\nimport \"@xyflow/react/dist/style.css\";\n\nimport type { CanvasProps } from \"./canvas\";\nimport type { ControlsProps } from \"./controls\";\nimport type { NodeProps } from \"./node\";\nimport type { PanelProps } from \"./panel\";\nimport type { ToolbarProps } from \"./toolbar\";\n\nconst deleteKeyCode = [\"Backspace\", \"Delete\"];\n\n/**\n * The React Flow attribution badge is hidden on both canvas surfaces (here and\n * `@elabs-ai/components-flow`'s `CanvasShell`). `@xyflow/react` is\n * MIT — the licence requires the notice in source copies, not a rendered badge —\n * and xyflow asks that the badge only be hidden under a React Flow Pro\n * subscription. Keeping it is therefore a product/commercial decision, and this\n * repo's is to hide it. A consumer can restore it per-canvas with\n * `proOptions={{ hideAttribution: false }}`, which still wins via `...props`.\n */\nconst proOptions = { hideAttribution: true };\n\nexport const Canvas = ({ children, ...props }: CanvasProps) => (\n <ReactFlow\n deleteKeyCode={deleteKeyCode}\n fitView\n panOnDrag={false}\n panOnScroll\n proOptions={proOptions}\n selectionOnDrag={true}\n zoomOnDoubleClick={false}\n {...props}\n >\n <Background bgColor=\"var(--sidebar)\" />\n {children}\n </ReactFlow>\n);\n\nexport const Controls = ({ className, ...props }: ControlsProps) => (\n <ControlsPrimitive\n className={cn(\n \"gap-px overflow-hidden rounded-md border bg-card p-1 shadow-none!\",\n \"[&>button]:rounded-md [&>button]:border-none! [&>button]:bg-transparent! [&>button]:hover:bg-secondary!\",\n className,\n )}\n {...props}\n />\n);\n\nexport const EdgeTemporary = ({\n id,\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n}: EdgeProps) => {\n const [edgePath] = getSimpleBezierPath({\n sourcePosition,\n sourceX,\n sourceY,\n targetPosition,\n targetX,\n targetY,\n });\n\n return (\n <BaseEdge\n className=\"stroke-1 stroke-ring\"\n id={id}\n path={edgePath}\n style={{\n strokeDasharray: \"5, 5\",\n }}\n />\n );\n};\n\n/**\n * The point on `node` where an edge of `handleType` should attach, plus the side\n * it leaves from.\n *\n * Read from React Flow's **measured** `handleBounds` — the DOM box of the\n * painted dot — and taken at the box's OUTER edge, which is React Flow's own\n * convention (the anchor lands on the dot's rim, which keeps a `markerEnd`\n * visible).\n *\n * The two fallbacks matter as much as the happy path. `preferred` is only a\n * preference: `nodeTypes` is an open prop, so a consumer node may legitimately\n * put its handles on the top and bottom, and the shipped `Node`'s left/right\n * pair is a convenience rather than a constraint. And `handleBounds` is empty\n * until React Flow's first measurement pass, which on this canvas is a real\n * window because the engine arrives in a lazy chunk (ADR 0019). Both used to\n * resolve to `[0, 0]` — the CANVAS ORIGIN — so the edge was drawn hundreds of\n * pixels from either node (measured at 498.8px in the `VerticalHandles` story).\n * Falling back to any handle of the right type, and then to the node's own\n * border, keeps the line on the node in every case.\n */\nconst getHandleAnchor = (\n node: InternalNode<FlowNode>,\n handleType: \"source\" | \"target\",\n preferred: Position,\n) => {\n const bounds = node.internals.handleBounds?.[handleType] ?? [];\n const handle = bounds.find((h) => h.position === preferred) ?? bounds[0];\n const origin = node.internals.positionAbsolute;\n\n if (handle) {\n // Offset to the handle box's outer edge on its own side; the other axis is\n // centred.\n const offsetX =\n handle.position === Position.Left\n ? 0\n : handle.position === Position.Right\n ? handle.width\n : handle.width / 2;\n const offsetY =\n handle.position === Position.Top\n ? 0\n : handle.position === Position.Bottom\n ? handle.height\n : handle.height / 2;\n\n return {\n position: handle.position,\n x: origin.x + handle.x + offsetX,\n y: origin.y + handle.y + offsetY,\n };\n }\n\n // No measured handle at all — anchor on the node's own border midpoint.\n const width = node.measured.width ?? 0;\n const height = node.measured.height ?? 0;\n switch (preferred) {\n case Position.Left:\n return { position: preferred, x: origin.x, y: origin.y + height / 2 };\n case Position.Right:\n return { position: preferred, x: origin.x + width, y: origin.y + height / 2 };\n case Position.Top:\n return { position: preferred, x: origin.x + width / 2, y: origin.y };\n default:\n return { position: preferred, x: origin.x + width / 2, y: origin.y + height };\n }\n};\n\nconst getEdgeParams = (source: InternalNode<FlowNode>, target: InternalNode<FlowNode>) => {\n const from = getHandleAnchor(source, \"source\", Position.Right);\n const to = getHandleAnchor(target, \"target\", Position.Left);\n\n return {\n sourcePos: from.position,\n sx: from.x,\n sy: from.y,\n targetPos: to.position,\n tx: to.x,\n ty: to.y,\n };\n};\n\nexport const EdgeAnimated = ({ id, source, target, markerEnd, style }: EdgeProps) => {\n const sourceNode = useInternalNode(source);\n const targetNode = useInternalNode(target);\n\n if (!(sourceNode && targetNode)) {\n return null;\n }\n\n const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(sourceNode, targetNode);\n\n const [edgePath] = getBezierPath({\n sourcePosition: sourcePos,\n sourceX: sx,\n sourceY: sy,\n targetPosition: targetPos,\n targetX: tx,\n targetY: ty,\n });\n\n return (\n <>\n <BaseEdge id={id} markerEnd={markerEnd} path={edgePath} style={style} />\n <circle fill=\"var(--primary)\" r=\"4\">\n <animateMotion dur=\"2s\" path={edgePath} repeatCount=\"indefinite\" />\n </circle>\n </>\n );\n};\n\nexport const Node = ({ handles, className, ...props }: NodeProps) => (\n <Card\n className={cn(\"node-container relative size-full h-auto w-sm gap-0 rounded-md p-0\", className)}\n {...props}\n >\n {handles.target && <Handle position={Position.Left} type=\"target\" />}\n {handles.source && <Handle position={Position.Right} type=\"source\" />}\n {props.children}\n </Card>\n);\n\nexport const Panel = ({ className, ...props }: PanelProps) => (\n <PanelPrimitive\n className={cn(\"m-4 overflow-hidden rounded-md border bg-card p-1\", className)}\n {...props}\n />\n);\n\nexport const Toolbar = ({ className, ...props }: ToolbarProps) => (\n <NodeToolbar\n className={cn(\"flex items-center gap-1 rounded-sm border bg-background p-1.5\", className)}\n position={Position.Bottom}\n {...props}\n />\n);\n"],"mappings":"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","names":[]}
|
|
@@ -4,15 +4,19 @@
|
|
|
4
4
|
// src/_persona-rive.tsx
|
|
5
5
|
import { cn } from "@elabs-ai/components-ui/lib/cn";
|
|
6
6
|
import { useReducedMotion } from "@elabs-ai/components-tokens";
|
|
7
|
-
import
|
|
7
|
+
import * as RiveModule from "@rive-app/react-webgl2";
|
|
8
|
+
import { memo, useEffect, useMemo, useRef, useState } from "react";
|
|
9
|
+
import { jsx } from "react/jsx-runtime";
|
|
10
|
+
var {
|
|
8
11
|
useRive,
|
|
9
12
|
useStateMachineInput,
|
|
10
13
|
useViewModel,
|
|
11
14
|
useViewModelInstance,
|
|
12
15
|
useViewModelInstanceColor
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
} = RiveModule;
|
|
17
|
+
if (!useRive) {
|
|
18
|
+
throw new Error("Cannot find module '@rive-app/react-webgl2'");
|
|
19
|
+
}
|
|
16
20
|
var useStrictModeSafeInit = () => {
|
|
17
21
|
const [ready, setReady] = useState(false);
|
|
18
22
|
useEffect(() => {
|
|
@@ -161,4 +165,4 @@ var persona_rive_default = PersonaRive;
|
|
|
161
165
|
export {
|
|
162
166
|
persona_rive_default as default
|
|
163
167
|
};
|
|
164
|
-
//# sourceMappingURL=_persona-rive-
|
|
168
|
+
//# sourceMappingURL=_persona-rive-JEG44YHX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/_persona-rive.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * The Rive half of `Persona`, split out so it can be `lazy()`-loaded.\n *\n * `@rive-app/react-webgl2` declares no `sideEffects`, so a static import from\n * `persona.tsx` put the whole WebGL2 runtime (and its `rive.wasm`) into the entry\n * chunk of every `@elabs-ai/components-ai` consumer — including the vast majority that never\n * render a `Persona`. Keeping every Rive *value* import in this module, reached\n * only through `lazy(() => import(\"./_persona-rive\"))`, confines it to its own\n * chunk. `persona.tsx` may still `import type` from Rive (types erase).\n *\n * See ADR 0019 and `pnpm heavy-deps:check`.\n *\n * @lazy-boundary This module must only ever be reached via `import()`. The gate\n * fails if anything imports it statically, which would put Rive back in the\n * entry chunk and make the `lazy()` pointless.\n */\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { useReducedMotion } from \"@elabs-ai/components-tokens\";\nimport type { RiveParameters } from \"@rive-app/react-webgl2\";\n// `import type` from an optional peer is safe HERE ONLY because this module is\n// reached exclusively through `lazy(() => import(\"./_persona-rive\"))` — it is\n// never statically imported, so nothing in this file reaches the barrel's\n// `.d.ts` declaration graph. `persona.tsx`'s PUBLIC types must never do this\n// (issue #101) — see its module doc comment and `PersonaRiveEvent`/\n// `PersonaRiveEventCallback`, which this module imports below and proves\n// conformant against the real peer type at the bottom of this file.\n// A NAMED import (`import { useRive } from \"@rive-app/react-webgl2\"`) is a\n// static ESM binding a bundler must resolve at build time. Now that\n// `@rive-app/react-webgl2` is a genuinely optional peer (issue #33), a\n// consumer who has not installed it hits that resolution at the worst\n// possible time: Vite's own optional-peer-dependency handling swaps in a\n// build-time stub with no exports, and Rollup's static named-export check\n// then fails the WHOLE APP BUILD — not a runtime error `LazyEngineBoundary`\n// could ever see (confirmed against `fixtures/consumer-smoke`'s real Vite\n// build). A namespace import defers every one of these to a plain property\n// lookup, which Rollup does not statically validate, so the build always\n// succeeds; a genuinely missing peer instead resolves the destructured hooks\n// below to `undefined`, and the guard turns that into the render-phase throw\n// `LazyEngineBoundary` (see `persona.tsx`) already catches.\nimport * as RiveModule from \"@rive-app/react-webgl2\";\nimport type { ReactNode } from \"react\";\nimport { memo, useEffect, useMemo, useRef, useState } from \"react\";\n\nimport type { AssertAssignable } from \"./_lazy-boundary-conformance\";\nimport type { PersonaRiveEventCallback } from \"./persona\";\nimport type { PersonaSource, PersonaState } from \"./persona-sources\";\n\nconst {\n useRive,\n useStateMachineInput,\n useViewModel,\n useViewModelInstance,\n useViewModelInstanceColor,\n} = RiveModule;\n\nif (!useRive) {\n throw new Error(\"Cannot find module '@rive-app/react-webgl2'\");\n}\n\n// Delays Rive initialization by one frame so that React Strict Mode's\n// immediate unmount cycle never creates a WebGL2 context. Only the\n// second (real) mount will initialise, avoiding context exhaustion.\nconst useStrictModeSafeInit = () => {\n const [ready, setReady] = useState(false);\n\n useEffect(() => {\n const id = requestAnimationFrame(() => setReady(true));\n return () => {\n cancelAnimationFrame(id);\n setReady(false);\n };\n }, []);\n\n return ready;\n};\n\n// The state machine name is always 'default' for Elements AI visuals\nconst stateMachine = \"default\";\n\nconst getCurrentTheme = (): \"light\" | \"dark\" => {\n if (typeof window !== \"undefined\") {\n if (document.documentElement.classList.contains(\"dark\")) {\n return \"dark\";\n }\n if (window.matchMedia?.(\"(prefers-color-scheme: dark)\").matches) {\n return \"dark\";\n }\n }\n return \"light\";\n};\n\nconst useTheme = (enabled: boolean) => {\n const [theme, setTheme] = useState<\"light\" | \"dark\">(getCurrentTheme);\n\n useEffect(() => {\n // Skip if not enabled (avoids unnecessary observers for non-dynamic-color variants)\n if (!enabled) {\n return;\n }\n\n // Watch for classList changes\n const observer = new MutationObserver(() => {\n setTheme(getCurrentTheme());\n });\n\n observer.observe(document.documentElement, {\n attributeFilter: [\"class\"],\n attributes: true,\n });\n\n // Watch for OS-level theme changes\n let mql: MediaQueryList | null = null;\n const handleMediaChange = () => {\n setTheme(getCurrentTheme());\n };\n\n if (window.matchMedia) {\n mql = window.matchMedia(\"(prefers-color-scheme: dark)\");\n mql.addEventListener(\"change\", handleMediaChange);\n }\n\n return () => {\n observer.disconnect();\n if (mql) {\n mql.removeEventListener(\"change\", handleMediaChange);\n }\n };\n }, [enabled]);\n\n return theme;\n};\n\ninterface PersonaWithModelProps {\n rive: ReturnType<typeof useRive>[\"rive\"];\n source: PersonaSource;\n children: ReactNode;\n}\n\nconst PersonaWithModel = memo(({ rive, source, children }: PersonaWithModelProps) => {\n const theme = useTheme(source.dynamicColor);\n const viewModel = useViewModel(rive, { useDefault: true });\n const viewModelInstance = useViewModelInstance(viewModel, {\n rive,\n useDefault: true,\n });\n const viewModelInstanceColor = useViewModelInstanceColor(\"color\", viewModelInstance);\n\n useEffect(() => {\n if (!(viewModelInstanceColor && source.dynamicColor)) {\n return;\n }\n\n const [r, g, b] = theme === \"dark\" ? [255, 255, 255] : [0, 0, 0];\n viewModelInstanceColor.setRgb(r, g, b);\n }, [viewModelInstanceColor, theme, source.dynamicColor]);\n\n return children;\n});\n\nPersonaWithModel.displayName = \"PersonaWithModel\";\n\nconst PersonaWithoutModel = memo(({ children }: { children: ReactNode }) => children);\n\nPersonaWithoutModel.displayName = \"PersonaWithoutModel\";\n\nexport interface PersonaRiveProps {\n className?: string;\n onLoad?: PersonaRiveEventCallback;\n onLoadError?: PersonaRiveEventCallback;\n onPause?: PersonaRiveEventCallback;\n onPlay?: PersonaRiveEventCallback;\n onReady?: () => void;\n onStop?: PersonaRiveEventCallback;\n source: PersonaSource;\n /** Resolved `.riv` URL — `Persona`'s `src` override, or the variant default. */\n src: string;\n state: PersonaState;\n}\n\nconst PersonaRive = ({\n className,\n onLoad,\n onLoadError,\n onPause,\n onPlay,\n onReady,\n onStop,\n source,\n src,\n state,\n}: PersonaRiveProps) => {\n // Stabilize callbacks to prevent useRive from reinitializing\n const callbacksRef = useRef({ onLoad, onLoadError, onPause, onPlay, onReady, onStop });\n\n useEffect(() => {\n callbacksRef.current = { onLoad, onLoadError, onPause, onPlay, onReady, onStop };\n }, [onLoad, onLoadError, onPause, onPlay, onReady, onStop]);\n\n const stableCallbacks = useMemo(\n () => ({\n onLoad: ((loadedRive) =>\n callbacksRef.current.onLoad?.(loadedRive)) as RiveParameters[\"onLoad\"],\n onLoadError: ((err) =>\n callbacksRef.current.onLoadError?.(err)) as RiveParameters[\"onLoadError\"],\n onPause: ((event) => callbacksRef.current.onPause?.(event)) as RiveParameters[\"onPause\"],\n onPlay: ((event) => callbacksRef.current.onPlay?.(event)) as RiveParameters[\"onPlay\"],\n onReady: () => callbacksRef.current.onReady?.(),\n onStop: ((event) => callbacksRef.current.onStop?.(event)) as RiveParameters[\"onStop\"],\n }),\n [],\n );\n\n // Delay initialisation by one frame to avoid creating (and leaking)\n // a WebGL2 context during React Strict Mode's first throw-away mount.\n const ready = useStrictModeSafeInit();\n const prefersReducedMotion = useReducedMotion();\n\n const { rive, RiveComponent } = useRive(\n ready\n ? {\n autoplay: !prefersReducedMotion,\n onLoad: stableCallbacks.onLoad,\n onLoadError: stableCallbacks.onLoadError,\n onPause: stableCallbacks.onPause,\n onPlay: stableCallbacks.onPlay,\n onRiveReady: stableCallbacks.onReady,\n onStop: stableCallbacks.onStop,\n src,\n stateMachines: stateMachine,\n }\n : null,\n );\n\n // `autoplay` only governs the INITIAL mount — honor a mid-session\n // reduced-motion preference change by pausing/resuming the running\n // instance. The state-machine inputs below keep updating either way, so\n // the artwork still shows the correct resting pose while paused.\n useEffect(() => {\n if (!rive) return;\n if (prefersReducedMotion) {\n rive.pause();\n } else {\n rive.play();\n }\n }, [rive, prefersReducedMotion]);\n\n const listeningInput = useStateMachineInput(rive, stateMachine, \"listening\");\n const thinkingInput = useStateMachineInput(rive, stateMachine, \"thinking\");\n const speakingInput = useStateMachineInput(rive, stateMachine, \"speaking\");\n const asleepInput = useStateMachineInput(rive, stateMachine, \"asleep\");\n\n // Rive state machine inputs are mutable objects that must be set via direct\n // property assignment — this is the intended Rive API, not a React anti-pattern.\n useEffect(() => {\n if (listeningInput) {\n listeningInput.value = state === \"listening\";\n }\n if (thinkingInput) {\n thinkingInput.value = state === \"thinking\";\n }\n if (speakingInput) {\n speakingInput.value = state === \"speaking\";\n }\n if (asleepInput) {\n asleepInput.value = state === \"asleep\";\n }\n }, [state, listeningInput, thinkingInput, speakingInput, asleepInput]);\n\n const Wrapper = source.hasModel ? PersonaWithModel : PersonaWithoutModel;\n\n return (\n <Wrapper rive={rive} source={source}>\n <RiveComponent className={cn(\"size-16 shrink-0\", className)} />\n </Wrapper>\n );\n};\n\nexport default PersonaRive;\n\n/**\n * Conformance assertion (issue #101): compile-time proof that the owned\n * `PersonaRiveEventCallback` (declared in `persona.tsx`, structurally free of\n * `@rive-app/react-webgl2`/`@rive-app/webgl2`) is still assignable to the REAL\n * `RiveParameters[\"onLoad\"]` — and, since `onLoad`/`onLoadError`/`onPause`/\n * `onPlay`/`onStop` all share Rive's one `EventCallback` type, this single\n * check covers all five. `AssertAssignable`'s type parameter is constrained\n * (`TOwned extends TReal`), so if a future Rive release changes `Event`'s\n * shape in a way `PersonaRiveEvent` no longer satisfies, this line fails to\n * typecheck — caught by `pnpm --filter @elabs-ai/components-ai typecheck`\n * locally, never shipped as a silent mismatch to a consumer.\n *\n * `AssertAssignable` itself is shared with `_audio-player-media-chrome.tsx`\n * via `_lazy-boundary-conformance.ts` — see that module's doc comment for\n * what this check can and cannot prove (it is one-directional: it cannot\n * catch the owned type being NARROWER than the real one, which is exactly\n * what `PersonaRiveEvent[\"data\"]` is — see the CHANGELOG's \"Breaking (types)\"\n * entry).\n */\nexport type _PersonaRiveEventCallbackConformance = AssertAssignable<\n PersonaRiveEventCallback,\n NonNullable<RiveParameters[\"onLoad\"]>\n>;\n"],"mappings":";;;;AAkBA,SAAS,UAAU;AACnB,SAAS,wBAAwB;AAsBjC,YAAY,gBAAgB;AAE5B,SAAS,MAAM,WAAW,SAAS,QAAQ,gBAAgB;AAuOrD;AAjON,IAAM;AAAA,EACJ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,IAAI;AAEJ,IAAI,CAAC,SAAS;AACZ,QAAM,IAAI,MAAM,6CAA6C;AAC/D;AAKA,IAAM,wBAAwB,MAAM;AAClC,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,KAAK;AAExC,YAAU,MAAM;AACd,UAAM,KAAK,sBAAsB,MAAM,SAAS,IAAI,CAAC;AACrD,WAAO,MAAM;AACX,2BAAqB,EAAE;AACvB,eAAS,KAAK;AAAA,IAChB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO;AACT;AAGA,IAAM,eAAe;AAErB,IAAM,kBAAkB,MAAwB;AAC9C,MAAI,OAAO,WAAW,aAAa;AACjC,QAAI,SAAS,gBAAgB,UAAU,SAAS,MAAM,GAAG;AACvD,aAAO;AAAA,IACT;AACA,QAAI,OAAO,aAAa,8BAA8B,EAAE,SAAS;AAC/D,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAM,WAAW,CAAC,YAAqB;AACrC,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA2B,eAAe;AAEpE,YAAU,MAAM;AAEd,QAAI,CAAC,SAAS;AACZ;AAAA,IACF;AAGA,UAAM,WAAW,IAAI,iBAAiB,MAAM;AAC1C,eAAS,gBAAgB,CAAC;AAAA,IAC5B,CAAC;AAED,aAAS,QAAQ,SAAS,iBAAiB;AAAA,MACzC,iBAAiB,CAAC,OAAO;AAAA,MACzB,YAAY;AAAA,IACd,CAAC;AAGD,QAAI,MAA6B;AACjC,UAAM,oBAAoB,MAAM;AAC9B,eAAS,gBAAgB,CAAC;AAAA,IAC5B;AAEA,QAAI,OAAO,YAAY;AACrB,YAAM,OAAO,WAAW,8BAA8B;AACtD,UAAI,iBAAiB,UAAU,iBAAiB;AAAA,IAClD;AAEA,WAAO,MAAM;AACX,eAAS,WAAW;AACpB,UAAI,KAAK;AACP,YAAI,oBAAoB,UAAU,iBAAiB;AAAA,MACrD;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO;AACT;AAQA,IAAM,mBAAmB,KAAK,CAAC,EAAE,MAAM,QAAQ,SAAS,MAA6B;AACnF,QAAM,QAAQ,SAAS,OAAO,YAAY;AAC1C,QAAM,YAAY,aAAa,MAAM,EAAE,YAAY,KAAK,CAAC;AACzD,QAAM,oBAAoB,qBAAqB,WAAW;AAAA,IACxD;AAAA,IACA,YAAY;AAAA,EACd,CAAC;AACD,QAAM,yBAAyB,0BAA0B,SAAS,iBAAiB;AAEnF,YAAU,MAAM;AACd,QAAI,EAAE,0BAA0B,OAAO,eAAe;AACpD;AAAA,IACF;AAEA,UAAM,CAAC,GAAG,GAAG,CAAC,IAAI,UAAU,SAAS,CAAC,KAAK,KAAK,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC;AAC/D,2BAAuB,OAAO,GAAG,GAAG,CAAC;AAAA,EACvC,GAAG,CAAC,wBAAwB,OAAO,OAAO,YAAY,CAAC;AAEvD,SAAO;AACT,CAAC;AAED,iBAAiB,cAAc;AAE/B,IAAM,sBAAsB,KAAK,CAAC,EAAE,SAAS,MAA+B,QAAQ;AAEpF,oBAAoB,cAAc;AAgBlC,IAAM,cAAc,CAAC;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAwB;AAEtB,QAAM,eAAe,OAAO,EAAE,QAAQ,aAAa,SAAS,QAAQ,SAAS,OAAO,CAAC;AAErF,YAAU,MAAM;AACd,iBAAa,UAAU,EAAE,QAAQ,aAAa,SAAS,QAAQ,SAAS,OAAO;AAAA,EACjF,GAAG,CAAC,QAAQ,aAAa,SAAS,QAAQ,SAAS,MAAM,CAAC;AAE1D,QAAM,kBAAkB;AAAA,IACtB,OAAO;AAAA,MACL,SAAS,CAAC,eACR,aAAa,QAAQ,SAAS,UAAU;AAAA,MAC1C,cAAc,CAAC,QACb,aAAa,QAAQ,cAAc,GAAG;AAAA,MACxC,UAAU,CAAC,UAAU,aAAa,QAAQ,UAAU,KAAK;AAAA,MACzD,SAAS,CAAC,UAAU,aAAa,QAAQ,SAAS,KAAK;AAAA,MACvD,SAAS,MAAM,aAAa,QAAQ,UAAU;AAAA,MAC9C,SAAS,CAAC,UAAU,aAAa,QAAQ,SAAS,KAAK;AAAA,IACzD;AAAA,IACA,CAAC;AAAA,EACH;AAIA,QAAM,QAAQ,sBAAsB;AACpC,QAAM,uBAAuB,iBAAiB;AAE9C,QAAM,EAAE,MAAM,cAAc,IAAI;AAAA,IAC9B,QACI;AAAA,MACE,UAAU,CAAC;AAAA,MACX,QAAQ,gBAAgB;AAAA,MACxB,aAAa,gBAAgB;AAAA,MAC7B,SAAS,gBAAgB;AAAA,MACzB,QAAQ,gBAAgB;AAAA,MACxB,aAAa,gBAAgB;AAAA,MAC7B,QAAQ,gBAAgB;AAAA,MACxB;AAAA,MACA,eAAe;AAAA,IACjB,IACA;AAAA,EACN;AAMA,YAAU,MAAM;AACd,QAAI,CAAC,KAAM;AACX,QAAI,sBAAsB;AACxB,WAAK,MAAM;AAAA,IACb,OAAO;AACL,WAAK,KAAK;AAAA,IACZ;AAAA,EACF,GAAG,CAAC,MAAM,oBAAoB,CAAC;AAE/B,QAAM,iBAAiB,qBAAqB,MAAM,cAAc,WAAW;AAC3E,QAAM,gBAAgB,qBAAqB,MAAM,cAAc,UAAU;AACzE,QAAM,gBAAgB,qBAAqB,MAAM,cAAc,UAAU;AACzE,QAAM,cAAc,qBAAqB,MAAM,cAAc,QAAQ;AAIrE,YAAU,MAAM;AACd,QAAI,gBAAgB;AAClB,qBAAe,QAAQ,UAAU;AAAA,IACnC;AACA,QAAI,eAAe;AACjB,oBAAc,QAAQ,UAAU;AAAA,IAClC;AACA,QAAI,eAAe;AACjB,oBAAc,QAAQ,UAAU;AAAA,IAClC;AACA,QAAI,aAAa;AACf,kBAAY,QAAQ,UAAU;AAAA,IAChC;AAAA,EACF,GAAG,CAAC,OAAO,gBAAgB,eAAe,eAAe,WAAW,CAAC;AAErE,QAAM,UAAU,OAAO,WAAW,mBAAmB;AAErD,SACE,oBAAC,WAAQ,MAAY,QACnB,8BAAC,iBAAc,WAAW,GAAG,oBAAoB,SAAS,GAAG,GAC/D;AAEJ;AAEA,IAAO,uBAAQ;","names":[]}
|