@elabs-ai/components-ai 5.3.1 → 5.5.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 +4 -6
- package/dist/_lazy-engine-boundary.js +1 -1
- package/dist/_persona-rive.js.map +1 -1
- package/dist/_streamdown-i18n.js +2 -2
- package/dist/agent.js +2 -2
- package/dist/asset-preview.js +5 -5
- package/dist/attachments.js +1 -1
- package/dist/audio-player.js +1 -2
- package/dist/{chunk-7RL6KZDF.js → chunk-24M6XWVJ.js} +25 -17
- package/dist/chunk-24M6XWVJ.js.map +1 -0
- package/dist/{chunk-WU5BLGTF.js → chunk-3KXIWGHW.js} +3 -3
- package/dist/{chunk-TWPHAJNM.js → chunk-3UHR3RLZ.js} +12 -6
- package/dist/chunk-3UHR3RLZ.js.map +1 -0
- package/dist/{chunk-V4DORGWJ.js → chunk-5AMSNG2T.js} +2 -2
- package/dist/chunk-D7BZMRI3.js +2 -0
- package/dist/chunk-D7BZMRI3.js.map +1 -0
- package/dist/{chunk-PQBF6ZIQ.js → chunk-DI645WR4.js} +4 -4
- package/dist/{chunk-NCL2UZKI.js → chunk-FFA7OPEH.js} +2 -2
- package/dist/{chunk-34CQ2VHM.js → chunk-FO44KY7Y.js} +10 -7
- package/dist/chunk-FO44KY7Y.js.map +1 -0
- package/dist/{chunk-RB7G65JC.js → chunk-GP7ZZCID.js} +7 -5
- package/dist/chunk-GP7ZZCID.js.map +1 -0
- package/dist/{chunk-6PB2MXCI.js → chunk-HLODND65.js} +2 -2
- package/dist/{chunk-ADOYAGGK.js → chunk-IFVO7LQJ.js} +2 -2
- package/dist/{chunk-2XOIG5TG.js → chunk-JSEV46SH.js} +18 -12
- package/dist/chunk-JSEV46SH.js.map +1 -0
- package/dist/{chunk-CZP3RHC4.js → chunk-LY7O4BMP.js} +2 -2
- package/dist/{chunk-PTWCTSZL.js → chunk-OCZNA5SO.js} +4 -4
- package/dist/chunk-OCZNA5SO.js.map +1 -0
- package/dist/{chunk-S3ISPW3A.js → chunk-PWQPVOZJ.js} +1 -1
- package/dist/chunk-PWQPVOZJ.js.map +1 -0
- package/dist/chunk-RLV64EWK.js +41 -0
- package/dist/chunk-RLV64EWK.js.map +1 -0
- package/dist/chunk-SSES3F4Q.js +126 -0
- package/dist/chunk-SSES3F4Q.js.map +1 -0
- package/dist/{chunk-3XNPMDZY.js → chunk-TBCMCF5O.js} +9 -56
- package/dist/chunk-TBCMCF5O.js.map +1 -0
- package/dist/{chunk-FERYDFZA.js → chunk-WXUXFDJQ.js} +2 -2
- package/dist/{chunk-X47QMM6K.js → chunk-YLN7E4OV.js} +2 -2
- package/dist/code-block.js +1 -1
- package/dist/diff-view.js +2 -2
- package/dist/gallery.js +1 -1
- package/dist/generated-image.js +9 -0
- package/dist/generated-image.js.map +1 -0
- package/dist/grouped-parts.js +6 -6
- package/dist/image.js +4 -4
- package/dist/index.d.ts +92 -150
- package/dist/index.js +67 -65
- package/dist/markdown-view.js +3 -3
- package/dist/message.js +3 -3
- package/dist/model-provider-logo.js +1 -1
- package/dist/persona.js +2 -2
- package/dist/queue.js +1 -1
- package/dist/reasoning.js +3 -3
- package/dist/sandbox.js +3 -3
- package/dist/tool.js +2 -2
- package/package.json +8 -13
- package/schemas/a2ui-surface.v1.schema.json +5 -2
- package/src/__contract__/{image.contract.test.tsx → attachments.contract.test.tsx} +7 -7
- package/src/__contract__/generated-image.contract.test.tsx +49 -0
- package/src/_lazy-boundary-conformance.ts +12 -18
- package/src/_lazy-engine-boundary.tsx +1 -2
- package/src/_lazy-mermaid-absent.test.ts +2 -2
- package/src/_media-fixtures.ts +47 -0
- package/src/_persona-rive.tsx +1 -2
- package/src/a2ui/catalog.source.json +3 -2
- package/src/asset-preview.stories.tsx +12 -0
- package/src/asset-preview.test.tsx +14 -0
- package/src/asset-preview.tsx +4 -2
- package/src/attachments.stories.tsx +147 -0
- package/src/attachments.test.tsx +104 -0
- package/src/attachments.tsx +28 -20
- package/src/audio-player.stories.tsx +10 -45
- package/src/audio-player.test.tsx +166 -61
- package/src/audio-player.tsx +175 -266
- package/src/chat.stories.tsx +75 -3
- package/src/code-block.test.tsx +32 -0
- package/src/code-block.tsx +22 -5
- package/src/gallery.tsx +13 -77
- package/src/generated-image.stories.tsx +67 -0
- package/src/generated-image.test.tsx +107 -0
- package/src/generated-image.tsx +57 -0
- package/src/image.tsx +2 -115
- package/src/index.ts +1 -0
- package/src/model-provider-logo.tsx +20 -18
- package/src/queue.test.tsx +24 -0
- package/src/queue.tsx +8 -5
- package/src/tool.test.tsx +26 -0
- package/src/tool.tsx +2 -2
- package/dist/_audio-player-media-chrome.js +0 -85
- package/dist/_audio-player-media-chrome.js.map +0 -1
- package/dist/chunk-2XOIG5TG.js.map +0 -1
- package/dist/chunk-34CQ2VHM.js.map +0 -1
- package/dist/chunk-3XNPMDZY.js.map +0 -1
- package/dist/chunk-7RL6KZDF.js.map +0 -1
- package/dist/chunk-NSCLRHES.js +0 -102
- package/dist/chunk-NSCLRHES.js.map +0 -1
- package/dist/chunk-PTWCTSZL.js.map +0 -1
- package/dist/chunk-RB7G65JC.js.map +0 -1
- package/dist/chunk-S3ISPW3A.js.map +0 -1
- package/dist/chunk-TWPHAJNM.js.map +0 -1
- package/dist/chunk-V6E5DYZD.js +0 -80
- package/dist/chunk-V6E5DYZD.js.map +0 -1
- package/src/_audio-player-media-chrome.tsx +0 -233
- package/src/image.stories.tsx +0 -67
- package/src/image.test.tsx +0 -105
- /package/dist/{chunk-WU5BLGTF.js.map → chunk-3KXIWGHW.js.map} +0 -0
- /package/dist/{chunk-V4DORGWJ.js.map → chunk-5AMSNG2T.js.map} +0 -0
- /package/dist/{chunk-PQBF6ZIQ.js.map → chunk-DI645WR4.js.map} +0 -0
- /package/dist/{chunk-NCL2UZKI.js.map → chunk-FFA7OPEH.js.map} +0 -0
- /package/dist/{chunk-6PB2MXCI.js.map → chunk-HLODND65.js.map} +0 -0
- /package/dist/{chunk-ADOYAGGK.js.map → chunk-IFVO7LQJ.js.map} +0 -0
- /package/dist/{chunk-CZP3RHC4.js.map → chunk-LY7O4BMP.js.map} +0 -0
- /package/dist/{chunk-FERYDFZA.js.map → chunk-WXUXFDJQ.js.map} +0 -0
- /package/dist/{chunk-X47QMM6K.js.map → chunk-YLN7E4OV.js.map} +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
MarkdownView
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-5AMSNG2T.js";
|
|
5
5
|
import {
|
|
6
6
|
useAssetPreviewRenderer
|
|
7
7
|
} from "./chunk-NPUCMAG2.js";
|
|
8
8
|
import {
|
|
9
9
|
CodeBlock
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-3UHR3RLZ.js";
|
|
11
11
|
import {
|
|
12
12
|
Artifact,
|
|
13
13
|
ArtifactActions,
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
// src/asset-preview.tsx
|
|
20
20
|
import {
|
|
21
21
|
Button,
|
|
22
|
+
Image,
|
|
22
23
|
Table,
|
|
23
24
|
TableBody,
|
|
24
25
|
TableCell,
|
|
@@ -161,12 +162,13 @@ var AssetPreview = ({
|
|
|
161
162
|
}
|
|
162
163
|
case "image": {
|
|
163
164
|
body = /* @__PURE__ */ jsx(
|
|
164
|
-
|
|
165
|
+
Image,
|
|
165
166
|
{
|
|
166
167
|
src: content || asset.path,
|
|
167
168
|
alt: asset.name,
|
|
169
|
+
fit: "contain",
|
|
168
170
|
loading: "lazy",
|
|
169
|
-
className: "
|
|
171
|
+
className: "rounded-md"
|
|
170
172
|
}
|
|
171
173
|
);
|
|
172
174
|
break;
|
|
@@ -210,4 +212,4 @@ var AssetPreview = ({
|
|
|
210
212
|
export {
|
|
211
213
|
AssetPreview
|
|
212
214
|
};
|
|
213
|
-
//# sourceMappingURL=chunk-
|
|
215
|
+
//# sourceMappingURL=chunk-GP7ZZCID.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/asset-preview.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * AssetPreview — type-keyed preview of one produced asset (#193, research 04\n * §5 ASSET-4). Reuses the `Artifact` chrome for the header/actions; the body\n * switches on `ContextAsset.type`:\n *\n * markdown → `MarkdownView` (the branded renderer — a document, never\n * Shiki source; the ASSET-2 fix)\n * code/sql → `CodeBlock` (Shiki)\n * csv → a small token-styled table + row-count summary\n * image → an image preview\n *\n * A Preview / Raw toggle serves users who want the source (Raw = `CodeBlock`,\n * soft-wrapped for narrow embeds). Selection state lives in the\n * `ContextPanelProvider` — the tree and this preview are the two levels of the\n * drill-in.\n *\n * Formats beyond that switch — a PDF, a spreadsheet, a video — arrive through\n * `renderPreview` (prop, or injected once on `ContextPanelProvider`), because\n * `@elabs-ai/components-viewer` is a layer PEER of this package and\n * neither may import the other (ADR 0024 §6). A renderer that returns `null`\n * declines, and everything below runs exactly as before.\n */\nimport {\n Button,\n Image,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n useLocale,\n} from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { useMemo, useState } from \"react\";\nimport type { BundledLanguage } from \"shiki\";\nimport {\n Artifact,\n ArtifactActions,\n ArtifactContent,\n ArtifactHeader,\n ArtifactTitle,\n} from \"./artifact\";\nimport { CodeBlock } from \"./code-block\";\nimport type { AssetPreviewRenderer, ContextAsset } from \"./context-panel\";\nimport { useAssetPreviewRenderer } from \"./context-panel\";\nimport { MarkdownView } from \"./markdown-view\";\n\nexport type AssetPreviewMode = \"preview\" | \"raw\";\n\nconst DEFAULT_CODE_LANGUAGE: BundledLanguage = \"typescript\";\n\n/** Raw-mode (and code-preview) Shiki language for an asset. */\nfunction assetLanguage(asset: ContextAsset): BundledLanguage {\n switch (asset.type) {\n case \"markdown\":\n return \"markdown\";\n case \"sql\":\n return \"sql\";\n case \"csv\":\n return \"csv\";\n case \"code\":\n return (asset.language as BundledLanguage) ?? DEFAULT_CODE_LANGUAGE;\n default:\n return DEFAULT_CODE_LANGUAGE;\n }\n}\n\ninterface ParsedCsv {\n header: string[];\n rows: string[][];\n}\n\n/**\n * A small RFC 4180-ish CSV tokenizer: handles quoted fields (commas and\n * newlines inside quotes don't end the field/row) and the `\"\"` escaped-quote\n * convention. Single pass over the raw string — no line pre-split, so a\n * quoted field spanning multiple lines survives intact. Still preview-grade\n * (unquoted cells are trimmed for the glance table), not a full CSV grammar.\n */\nfunction parseCsvRows(content: string): string[][] {\n const rows: string[][] = [];\n let row: string[] = [];\n let field = \"\";\n let wasQuoted = false;\n let inQuotes = false;\n\n const endField = () => {\n row.push(wasQuoted ? field : field.trim());\n field = \"\";\n wasQuoted = false;\n };\n const endRow = () => {\n endField();\n rows.push(row);\n row = [];\n };\n\n for (let i = 0; i < content.length; i++) {\n const ch = content[i];\n\n if (inQuotes) {\n if (ch === '\"') {\n if (content[i + 1] === '\"') {\n field += '\"';\n i += 1;\n } else {\n inQuotes = false;\n }\n } else {\n field += ch;\n }\n continue;\n }\n\n if (ch === '\"' && field === \"\") {\n inQuotes = true;\n wasQuoted = true;\n continue;\n }\n\n if (ch === \",\") {\n endField();\n continue;\n }\n\n if (ch === \"\\r\") {\n continue;\n }\n\n if (ch === \"\\n\") {\n endRow();\n continue;\n }\n\n field += ch;\n }\n\n // A trailing field/row with no terminating newline.\n if (field !== \"\" || row.length > 0) {\n endRow();\n }\n\n return rows;\n}\n\nfunction parseCsv(content: string): ParsedCsv {\n const rows = parseCsvRows(content.trim()).filter((r) => !(r.length === 1 && r[0] === \"\"));\n const [header = [], ...rest] = rows;\n return { header, rows: rest };\n}\n\nconst CsvPreview = ({ content }: { content: string }) => {\n const { t } = useLocale();\n const { header, rows } = useMemo(() => parseCsv(content), [content]);\n return (\n <div className=\"flex flex-col gap-2\">\n <Table className=\"text-body tabular-nums\">\n <TableHeader>\n <TableRow>\n {header.map((cell) => (\n <TableHead key={cell}>{cell}</TableHead>\n ))}\n </TableRow>\n </TableHeader>\n <TableBody>\n {rows.map((row, rowIndex) => (\n // Rows have no stable identity in raw CSV — index is the key.\n // oxlint-disable-next-line eslint-plugin-react(no-array-index-key)\n <TableRow key={`row-${rowIndex}`}>\n {row.map((cell, cellIndex) => (\n // oxlint-disable-next-line eslint-plugin-react(no-array-index-key)\n <TableCell key={`cell-${rowIndex}-${cellIndex}`}>{cell}</TableCell>\n ))}\n </TableRow>\n ))}\n </TableBody>\n </Table>\n <p className=\"text-meta text-muted-foreground\">\n {t(\"ai.assetPreview.rowCount\", { count: rows.length })}\n </p>\n </div>\n );\n};\n\nexport interface AssetPreviewProps extends HTMLAttributes<HTMLDivElement> {\n asset: ContextAsset;\n /** Initial mode. Default `\"preview\"`. */\n defaultMode?: AssetPreviewMode;\n /**\n * Draw this asset's preview body yourself; return `null` to decline and get\n * the built-in rendering. Wins over a renderer injected on\n * `ContextPanelProvider`, so one rail-wide default can still be overridden\n * for a single preview.\n */\n renderPreview?: AssetPreviewRenderer;\n}\n\nexport const AssetPreview = ({\n asset,\n defaultMode = \"preview\",\n renderPreview,\n className,\n ...props\n}: AssetPreviewProps) => {\n const { t } = useLocale();\n const [mode, setMode] = useState<AssetPreviewMode>(defaultMode);\n const injectedRenderer = useAssetPreviewRenderer();\n const content = asset.content ?? \"\";\n // Raw source only exists for text assets with content.\n const hasRaw = asset.type !== \"image\" && content.length > 0;\n const showRaw = mode === \"raw\" && hasRaw;\n\n // Raw is \"show me the source\", so an injection never intercepts it.\n const render = renderPreview ?? injectedRenderer;\n const injected = !showRaw && render ? render(asset) : null;\n\n let body: ReactNode;\n if (injected !== null) {\n body = injected;\n } else if (asset.type !== \"image\" && content.length === 0) {\n body = <p className=\"text-body text-muted-foreground\">{t(\"ai.assetPreview.noPreview\")}</p>;\n } else if (showRaw) {\n body = <CodeBlock code={content} language={assetLanguage(asset)} wrap />;\n } else {\n switch (asset.type) {\n case \"markdown\": {\n // Constrained heading rung inside the narrow rail (research 09 §G.2).\n body = <MarkdownView baseHeadingLevel={2}>{content}</MarkdownView>;\n break;\n }\n case \"code\":\n case \"sql\": {\n body = <CodeBlock code={content} language={assetLanguage(asset)} showLineNumbers wrap />;\n break;\n }\n case \"csv\": {\n body = <CsvPreview content={content} />;\n break;\n }\n case \"image\": {\n body = (\n <Image\n src={content || asset.path}\n alt={asset.name}\n fit=\"contain\"\n loading=\"lazy\"\n className=\"rounded-md\"\n />\n );\n break;\n }\n }\n }\n\n return (\n <Artifact data-slot=\"asset-preview\" className={className} {...props}>\n <ArtifactHeader>\n <ArtifactTitle className=\"min-w-0 truncate\">{asset.name}</ArtifactTitle>\n {hasRaw ? (\n <ArtifactActions>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-pressed={!showRaw}\n className={cn(!showRaw && \"bg-accent text-accent-foreground\")}\n onClick={() => setMode(\"preview\")}\n >\n {t(\"ai.assetPreview.preview\")}\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-pressed={showRaw}\n className={cn(showRaw && \"bg-accent text-accent-foreground\")}\n onClick={() => setMode(\"raw\")}\n >\n {t(\"ai.assetPreview.raw\")}\n </Button>\n </ArtifactActions>\n ) : null}\n </ArtifactHeader>\n <ArtifactContent>{body}</ArtifactContent>\n </Artifact>\n );\n};\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
highlightCode
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3UHR3RLZ.js";
|
|
5
5
|
import {
|
|
6
6
|
Shimmer
|
|
7
7
|
} from "./chunk-3YPT3V7L.js";
|
|
@@ -479,4 +479,4 @@ export {
|
|
|
479
479
|
diffRowVariants,
|
|
480
480
|
DiffView
|
|
481
481
|
};
|
|
482
|
-
//# sourceMappingURL=chunk-
|
|
482
|
+
//# sourceMappingURL=chunk-HLODND65.js.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
useStreamdownMermaidOptions,
|
|
5
5
|
useStreamdownPlugins,
|
|
6
6
|
useStreamdownTranslations
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-DI645WR4.js";
|
|
8
8
|
import {
|
|
9
9
|
stripSanitizerOverrides,
|
|
10
10
|
warnOnTrustedPluginSlots
|
|
@@ -479,4 +479,4 @@ export {
|
|
|
479
479
|
MessageResponse,
|
|
480
480
|
MessageToolbar
|
|
481
481
|
};
|
|
482
|
-
//# sourceMappingURL=chunk-
|
|
482
|
+
//# sourceMappingURL=chunk-IFVO7LQJ.js.map
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/model-provider-logo.tsx
|
|
4
|
+
import { Image } from "@elabs-ai/components-ui";
|
|
4
5
|
import { cn } from "@elabs-ai/components-ui/lib/cn";
|
|
5
6
|
import { resolveThemeIsDark } from "@elabs-ai/components-tokens";
|
|
6
7
|
import { BotIcon } from "lucide-react";
|
|
7
8
|
import { useEffect, useState } from "react";
|
|
8
|
-
import {
|
|
9
|
+
import { jsx } from "react/jsx-runtime";
|
|
9
10
|
function useIsDarkTheme() {
|
|
10
11
|
const [isDark, setIsDark] = useState(false);
|
|
11
12
|
useEffect(() => {
|
|
@@ -28,22 +29,27 @@ var ModelProviderLogo = ({
|
|
|
28
29
|
onError,
|
|
29
30
|
...props
|
|
30
31
|
}) => {
|
|
31
|
-
const [failed, setFailed] = useState(false);
|
|
32
32
|
const isDark = useIsDarkTheme();
|
|
33
|
-
|
|
34
|
-
return /* @__PURE__ */ jsx(Fragment, { children: fallback ?? /* @__PURE__ */ jsx(BotIcon, { "aria-label": `${provider} logo`, className: cn("size-3", className), role: "img" }) });
|
|
35
|
-
}
|
|
33
|
+
const label = `${provider} logo`;
|
|
36
34
|
return /* @__PURE__ */ jsx(
|
|
37
|
-
|
|
35
|
+
Image,
|
|
38
36
|
{
|
|
39
37
|
...props,
|
|
40
|
-
|
|
38
|
+
"data-slot": "model-provider-logo",
|
|
39
|
+
alt: label,
|
|
41
40
|
className: cn("size-3", isDark && "invert", className),
|
|
41
|
+
fallback: fallback ?? /* @__PURE__ */ jsx(
|
|
42
|
+
BotIcon,
|
|
43
|
+
{
|
|
44
|
+
"data-slot": "model-provider-logo",
|
|
45
|
+
"aria-label": label,
|
|
46
|
+
className: cn("size-3", className),
|
|
47
|
+
role: "img"
|
|
48
|
+
}
|
|
49
|
+
),
|
|
42
50
|
height: 12,
|
|
43
|
-
onError
|
|
44
|
-
|
|
45
|
-
onError?.(event);
|
|
46
|
-
},
|
|
51
|
+
onError,
|
|
52
|
+
showSkeleton: false,
|
|
47
53
|
src: src ?? `${MODEL_PROVIDER_LOGO_BASE_URL}/${provider}.svg`,
|
|
48
54
|
width: 12
|
|
49
55
|
}
|
|
@@ -69,4 +75,4 @@ export {
|
|
|
69
75
|
ModelProviderLogo,
|
|
70
76
|
ModelProviderLogoGroup
|
|
71
77
|
};
|
|
72
|
-
//# sourceMappingURL=chunk-
|
|
78
|
+
//# sourceMappingURL=chunk-JSEV46SH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/model-provider-logo.tsx"],"sourcesContent":["\"use client\";\n\nimport { Image } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { resolveThemeIsDark } from \"@elabs-ai/components-tokens\";\nimport { BotIcon } from \"lucide-react\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { useEffect, useState } from \"react\";\n\n/**\n * Tracks the ACTIVE theme's own `color-scheme` (`resolveThemeIsDark`), not the\n * two shipped `light`/`dark` themes — a consumer-authored dark theme still\n * gets an inverted (visible) logo with no registration step (theming.md:\n * \"Use semantic tokens, not `dark:` — `@custom-variant dark` covers only the\n * themes shipped HERE\"). Mirrors `Toaster`'s `useDocumentThemeMode`\n * (`packages/ui/src/components/sonner/sonner.tsx`).\n */\nfunction useIsDarkTheme(): boolean {\n const [isDark, setIsDark] = useState(false);\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n const el = document.documentElement;\n const read = () => setIsDark(resolveThemeIsDark(el));\n read();\n const obs = new MutationObserver(read);\n obs.observe(el, { attributes: true, attributeFilter: [\"data-theme\"] });\n return () => obs.disconnect();\n }, []);\n return isDark;\n}\n\n/**\n * Where provider logos are fetched from by default.\n *\n * This is a REMOTE origin: a deployment with a restrictive `img-src` blocks it.\n * Self-host the SVGs and pass `src` (or set a `fallback`) — see\n * `docs/CSP-AND-NETWORK.md`.\n */\nexport const MODEL_PROVIDER_LOGO_BASE_URL = \"https://models.dev/logos\";\n\nexport type ModelProviderLogoProps = Omit<ComponentProps<\"img\">, \"alt\"> & {\n /**\n * Override the logo URL — point at a self-hosted or bundled asset when a CSP\n * blocks `models.dev`. Defaults to `${MODEL_PROVIDER_LOGO_BASE_URL}/<provider>.svg`.\n */\n src?: string;\n /**\n * Rendered when the logo fails to load (blocked, offline, or unknown\n * provider). Defaults to a neutral Lucide glyph, so the row never collapses\n * to a broken image.\n */\n fallback?: ReactNode;\n provider:\n | \"moonshotai-cn\"\n | \"lucidquery\"\n | \"moonshotai\"\n | \"zai-coding-plan\"\n | \"alibaba\"\n | \"xai\"\n | \"vultr\"\n | \"nvidia\"\n | \"upstage\"\n | \"groq\"\n | \"github-copilot\"\n | \"mistral\"\n | \"vercel\"\n | \"nebius\"\n | \"deepseek\"\n | \"alibaba-cn\"\n | \"google-vertex-anthropic\"\n | \"venice\"\n | \"chutes\"\n | \"cortecs\"\n | \"github-models\"\n | \"togetherai\"\n | \"azure\"\n | \"baseten\"\n | \"huggingface\"\n | \"opencode\"\n | \"fastrouter\"\n | \"google\"\n | \"google-vertex\"\n | \"cloudflare-workers-ai\"\n | \"inception\"\n | \"wandb\"\n | \"openai\"\n | \"zhipuai-coding-plan\"\n | \"perplexity\"\n | \"openrouter\"\n | \"zenmux\"\n | \"v0\"\n | \"iflowcn\"\n | \"synthetic\"\n | \"deepinfra\"\n | \"zhipuai\"\n | \"submodel\"\n | \"zai\"\n | \"inference\"\n | \"requesty\"\n | \"morph\"\n | \"lmstudio\"\n | \"anthropic\"\n | \"aihubmix\"\n | \"fireworks-ai\"\n | \"modelscope\"\n | \"llama\"\n | \"scaleway\"\n | \"amazon-bedrock\"\n | \"cerebras\"\n // oxlint-disable-next-line typescript-eslint(ban-types) -- intentional pattern for autocomplete-friendly string union\n | (string & {});\n};\n\n/**\n * The mark of an AI model provider (`anthropic`, `openai`, `google`, …), sized\n * for a row in a model list.\n *\n * **`ModelProviderLogo` vs `ServiceLogo` (`@elabs-ai/components-icons`).** An AI\n * provider mark with a zero-config remote default — name the provider and a logo\n * appears, no registration step — is this component. Any other third-party\n * service mark, resolved from a registry the app supplies and never fetched, is\n * `ServiceLogo`. The two are not interchangeable: `ServiceLogo` has no\n * per-instance `src` and no `onError`, and this one reaches a remote origin by\n * default (see `docs/CSP-AND-NETWORK.md`).\n */\nexport const ModelProviderLogo = ({\n provider,\n className,\n src,\n fallback,\n onError,\n ...props\n}: ModelProviderLogoProps) => {\n const isDark = useIsDarkTheme();\n const label = `${provider} logo`;\n\n // A blocked/missing logo must not leave a broken-image glyph in the row:\n // ui `Image` renders `fallback` AS the root on a terminal error (or at once\n // when there is no `src`).\n return (\n <Image\n {...props}\n data-slot=\"model-provider-logo\"\n alt={label}\n className={cn(\"size-3\", isDark && \"invert\", className)}\n fallback={\n fallback ?? (\n <BotIcon\n data-slot=\"model-provider-logo\"\n aria-label={label}\n className={cn(\"size-3\", className)}\n role=\"img\"\n />\n )\n }\n height={12}\n onError={onError}\n showSkeleton={false}\n // AFTER the spread, so the caller's `src` wins via the destructured\n // default rather than being silently overwritten.\n src={src ?? `${MODEL_PROVIDER_LOGO_BASE_URL}/${provider}.svg`}\n width={12}\n />\n );\n};\n\nexport type ModelProviderLogoGroupProps = ComponentProps<\"div\">;\n\nexport const ModelProviderLogoGroup = ({ className, ...props }: ModelProviderLogoGroupProps) => {\n // Same active-theme signal as the single logo above (not the two shipped\n // `light`/`dark` themes) — the backdrop swaps to `--foreground` so a\n // transparent-background SVG still reads against a dark chip in ANY dark\n // theme a consumer registers.\n const isDark = useIsDarkTheme();\n return (\n <div\n className={cn(\n \"flex shrink-0 items-center -space-x-1 [&>img]:rounded-full [&>img]:p-px [&>img]:ring-1\",\n isDark ? \"[&>img]:bg-foreground\" : \"[&>img]:bg-background\",\n className,\n )}\n {...props}\n />\n );\n};\n"],"mappings":";;;AAEA,SAAS,aAAa;AACtB,SAAS,UAAU;AACnB,SAAS,0BAA0B;AACnC,SAAS,eAAe;AAExB,SAAS,WAAW,gBAAgB;AA4I1B;AAlIV,SAAS,iBAA0B;AACjC,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAC1C,YAAU,MAAM;AACd,QAAI,OAAO,aAAa,YAAa;AACrC,UAAM,KAAK,SAAS;AACpB,UAAM,OAAO,MAAM,UAAU,mBAAmB,EAAE,CAAC;AACnD,SAAK;AACL,UAAM,MAAM,IAAI,iBAAiB,IAAI;AACrC,QAAI,QAAQ,IAAI,EAAE,YAAY,MAAM,iBAAiB,CAAC,YAAY,EAAE,CAAC;AACrE,WAAO,MAAM,IAAI,WAAW;AAAA,EAC9B,GAAG,CAAC,CAAC;AACL,SAAO;AACT;AASO,IAAM,+BAA+B;AAuFrC,IAAM,oBAAoB,CAAC;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAA8B;AAC5B,QAAM,SAAS,eAAe;AAC9B,QAAM,QAAQ,GAAG,QAAQ;AAKzB,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,aAAU;AAAA,MACV,KAAK;AAAA,MACL,WAAW,GAAG,UAAU,UAAU,UAAU,SAAS;AAAA,MACrD,UACE,YACE;AAAA,QAAC;AAAA;AAAA,UACC,aAAU;AAAA,UACV,cAAY;AAAA,UACZ,WAAW,GAAG,UAAU,SAAS;AAAA,UACjC,MAAK;AAAA;AAAA,MACP;AAAA,MAGJ,QAAQ;AAAA,MACR;AAAA,MACA,cAAc;AAAA,MAGd,KAAK,OAAO,GAAG,4BAA4B,IAAI,QAAQ;AAAA,MACvD,OAAO;AAAA;AAAA,EACT;AAEJ;AAIO,IAAM,yBAAyB,CAAC,EAAE,WAAW,GAAG,MAAM,MAAmC;AAK9F,QAAM,SAAS,eAAe;AAC9B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,SAAS,0BAA0B;AAAA,QACnC;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
CodeBlock
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3UHR3RLZ.js";
|
|
5
5
|
|
|
6
6
|
// src/agent.tsx
|
|
7
7
|
import {
|
|
@@ -71,4 +71,4 @@ export {
|
|
|
71
71
|
AgentTool,
|
|
72
72
|
AgentOutput
|
|
73
73
|
};
|
|
74
|
-
//# sourceMappingURL=chunk-
|
|
74
|
+
//# sourceMappingURL=chunk-LY7O4BMP.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
CodeBlock
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3UHR3RLZ.js";
|
|
5
5
|
|
|
6
6
|
// src/tool.tsx
|
|
7
7
|
import { Skeleton, StatusBadge, useLocale } from "@elabs-ai/components-ui";
|
|
@@ -61,8 +61,8 @@ var ToolHeader = ({
|
|
|
61
61
|
children: [
|
|
62
62
|
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
63
63
|
/* @__PURE__ */ jsx(WrenchIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
|
|
64
|
-
/* @__PURE__ */ jsx("span", { className: "
|
|
65
|
-
|
|
64
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 truncate text-body font-medium", children: title ?? derivedName }),
|
|
65
|
+
/* @__PURE__ */ jsx(StatusBadge, { status: statusFromToolState(state), className: "shrink-0" }),
|
|
66
66
|
summary ? /* @__PURE__ */ jsx("span", { className: "truncate text-meta text-muted-foreground", children: summary }) : null
|
|
67
67
|
] }),
|
|
68
68
|
/* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-4 shrink-0 text-muted-foreground transition-transform group-data-[state=open]:rotate-180" })
|
|
@@ -170,4 +170,4 @@ export {
|
|
|
170
170
|
ToolInput,
|
|
171
171
|
ToolOutput
|
|
172
172
|
};
|
|
173
|
-
//# sourceMappingURL=chunk-
|
|
173
|
+
//# sourceMappingURL=chunk-OCZNA5SO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tool.tsx"],"sourcesContent":["\"use client\";\n\nimport { Skeleton, StatusBadge, useLocale, type Status } from \"@elabs-ai/components-ui\";\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport type { DynamicToolUIPart, ToolUIPart } from \"ai\";\nimport { ChevronDownIcon, WrenchIcon } from \"lucide-react\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { isValidElement } from \"react\";\nimport type { BundledLanguage } from \"shiki\";\n\nimport { CodeBlock } from \"./code-block\";\n\nexport type ToolProps = ComponentProps<typeof Collapsible>;\n\nexport const Tool = ({ className, ...props }: ToolProps) => (\n <Collapsible\n className={cn(\"group not-prose mb-4 w-full rounded-md border\", className)}\n {...props}\n />\n);\n\nexport type ToolPart = ToolUIPart | DynamicToolUIPart;\n\n/**\n * `JSON.stringify` throws on a circular reference or a `BigInt` — both\n * realistic shapes for tool input/output payloads a model produced. Falls\n * back to a readable placeholder instead of crashing the message render.\n */\nfunction safeJsonStringify(value: unknown): string {\n try {\n return JSON.stringify(value, (_key, v) => (typeof v === \"bigint\" ? `${v.toString()}n` : v), 2);\n } catch {\n return String(value);\n }\n}\n\nexport type ToolHeaderProps = {\n title?: string;\n /**\n * The business summary line (\"3 documents found\", \"8 rows reconciled\"),\n * shown beside the name + StatusBadge (#192, research 10 §B.5). Falls back\n * to nothing — the derived tool name still labels the row.\n */\n summary?: ReactNode;\n className?: string;\n} & (\n | { type: ToolUIPart[\"type\"]; state: ToolUIPart[\"state\"]; toolName?: never }\n | {\n type: DynamicToolUIPart[\"type\"];\n state: DynamicToolUIPart[\"state\"];\n toolName: string;\n }\n);\n\n/**\n * Map the AI-SDK `ToolUIPart` 7-state machine onto the canonical `Status`\n * enum (#189, research 10 §B.1 mapping a). Lives here — not in `@elabs-ai/components-ui` —\n * because it is typed against the SDK union; the `ai` import stays TYPES-ONLY\n * (D6, gate-enforced by `pnpm ai:types-only`).\n */\nexport const statusFromToolState = (state: ToolPart[\"state\"]): Status => {\n switch (state) {\n case \"input-streaming\":\n return \"pending\";\n case \"input-available\":\n return \"running\";\n case \"approval-requested\":\n return \"awaiting-approval\";\n case \"approval-responded\":\n return \"running\";\n case \"output-available\":\n return \"complete\";\n case \"output-denied\":\n return \"denied\";\n case \"output-error\":\n return \"failed\";\n }\n};\n\n/** Same signature as before #189; renders the canonical StatusBadge. */\nexport const getStatusBadge = (status: ToolPart[\"state\"]) => (\n <StatusBadge status={statusFromToolState(status)} />\n);\n\nexport const ToolHeader = ({\n className,\n title,\n summary,\n type,\n state,\n toolName,\n ...props\n}: ToolHeaderProps) => {\n const derivedName = type === \"dynamic-tool\" ? toolName : type.split(\"-\").slice(1).join(\"-\");\n\n return (\n <CollapsibleTrigger\n className={cn(\"flex w-full items-center justify-between gap-4 p-3\", className)}\n {...props}\n >\n <div className=\"flex min-w-0 items-center gap-2\">\n <WrenchIcon className=\"size-4 shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 truncate text-body font-medium\">{title ?? derivedName}</span>\n <StatusBadge status={statusFromToolState(state)} className=\"shrink-0\" />\n {summary ? (\n <span className=\"truncate text-meta text-muted-foreground\">{summary}</span>\n ) : null}\n </div>\n <ChevronDownIcon className=\"size-4 shrink-0 text-muted-foreground transition-transform group-data-[state=open]:rotate-180\" />\n </CollapsibleTrigger>\n );\n};\n\nexport type ToolContentProps = ComponentProps<typeof CollapsibleContent>;\n\nexport const ToolContent = ({ className, ...props }: ToolContentProps) => (\n <CollapsibleContent\n className={cn(\n \"data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 space-y-4 p-4 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in data-[state=open]:[--tw-ease:var(--ease-entrance)] data-[state=closed]:[--tw-ease:var(--ease-exit)]\",\n className,\n )}\n {...props}\n />\n);\n\nexport type ToolDetailsProps = ComponentProps<typeof Collapsible> & {\n /** The disclosure label. */\n label?: ReactNode;\n};\n\n/**\n * The technical view, behind disclosure — the PACKAGE DEFAULT for tool JSON\n * (#192, research 10 §B.5): a nested collapsible, COLLAPSED by default,\n * holding `ToolInput`/`ToolOutput`. The header carries the business `summary`;\n * the raw payload is one expand away, never the headline.\n */\nexport const ToolDetails = ({\n className,\n label,\n defaultOpen = false,\n children,\n ...props\n}: ToolDetailsProps) => {\n const { t } = useLocale();\n return (\n <Collapsible\n className={cn(\"group/tool-details not-prose\", className)}\n defaultOpen={defaultOpen}\n {...props}\n >\n <CollapsibleTrigger className=\"flex items-center gap-1 rounded-sm text-meta text-muted-foreground transition-colors hover:text-foreground focus-ring\">\n <ChevronDownIcon className=\"size-3.5 transition-transform group-data-[state=open]/tool-details:rotate-180\" />\n {label ?? t(\"ai.tool.showTechnicalDetails\")}\n </CollapsibleTrigger>\n <CollapsibleContent className=\"mt-3 space-y-4\">{children}</CollapsibleContent>\n </Collapsible>\n );\n};\n\nexport type ToolInputProps = ComponentProps<\"div\"> & {\n input: ToolPart[\"input\"];\n};\n\nexport const ToolInput = ({ className, input, ...props }: ToolInputProps) => {\n const { t } = useLocale();\n return (\n <div className={cn(\"space-y-2 overflow-hidden\", className)} {...props}>\n <h4 className=\"text-eyebrow uppercase text-muted-foreground\">\n {t(\"ai.schemaDisplay.parameters\")}\n </h4>\n <div className=\"rounded-md bg-muted/50\">\n <CodeBlock code={safeJsonStringify(input)} language=\"json\" />\n </div>\n </div>\n );\n};\n\nexport type ToolOutputProps = ComponentProps<\"div\"> & {\n /**\n * The tool result. Objects/strings render as JSON inside the technical view.\n *\n * @deprecated Passing a pre-rendered **React element** here (the \"rich\n * output under a muted Result heading\" path) is deprecated since #192\n * (research 10 §B.5): a produced artifact is the HEADLINE, not a technical\n * detail — host it in a `<ToolResultCard>` and keep `ToolOutput` for the\n * JSON payload behind `<ToolDetails>`. The element path still renders for\n * existing consumers (e.g. the copy-owned `ai-chart` registry block) but\n * will be removed in a future release.\n */\n output: ToolPart[\"output\"];\n errorText: ToolPart[\"errorText\"];\n /**\n * The call has not produced output yet (loading-states.md `isStreaming`) —\n * derive it from the existing `statusFromToolState(state)` mapping\n * (`\"input-streaming\"`/`\"input-available\"` → not yet `\"complete\"`/`\"failed\"`)\n * rather than a second source of truth. While true and no `output`/\n * `errorText` has arrived, renders a layout-shaped skeleton in the Result\n * slot instead of `null`, so the technical view reserves its space. A\n * still-running call is never a terminal failure — the error branch is\n * suppressed while `isStreaming` per the loading-states.md error rule, even\n * if a stale `errorText` is still set from a previous render.\n * @default false\n */\n isStreaming?: boolean;\n};\n\nexport const ToolOutput = ({\n className,\n output,\n errorText,\n isStreaming = false,\n ...props\n}: ToolOutputProps) => {\n const { t } = useLocale();\n\n // `output` is a defined-but-falsy result (`0`, `false`, `\"\"`) for plenty of\n // real tools (a count, a boolean check, an empty-string field) — only\n // `undefined` means \"no output (yet)\".\n const hasOutput = output !== undefined;\n\n if (!(hasOutput || errorText || isStreaming)) {\n return null;\n }\n\n const showError = !isStreaming && Boolean(errorText);\n const pending = isStreaming && !hasOutput && !errorText;\n\n let Output: ReactNode = null;\n\n if (hasOutput) {\n if (isValidElement(output)) {\n Output = output;\n } else if (typeof output === \"string\") {\n // A tool result is arbitrary text, not guaranteed JSON — forcing the\n // JSON highlighter on it mis-colours ordinary strings. Shiki's own\n // `BundledLanguage` union (grammar-backed languages) omits its\n // hard-coded plain-text pseudo-languages (`isPlainLang`:\n // \"plaintext\" | \"txt\" | \"text\" | \"plain\") — `createHighlighter`/\n // `codeToTokens` accept and special-case them with no grammar load\n // (verified: `getLoadedLanguages()` stays empty, no throw), so this\n // is a type-only gap, not a runtime one.\n Output = <CodeBlock code={output} language={\"text\" as BundledLanguage} />;\n } else {\n // Objects, arrays, numbers, booleans, null, bigint — all safe to\n // stringify for display.\n Output = <CodeBlock code={safeJsonStringify(output)} language=\"json\" />;\n }\n }\n\n return (\n <div className={cn(\"space-y-2\", className)} {...props}>\n <h4 className=\"text-eyebrow uppercase text-muted-foreground\">\n {showError ? t(\"ai.tool.error\") : t(\"ai.tool.result\")}\n </h4>\n {pending ? (\n <div className=\"space-y-2 rounded-md bg-muted/50 p-3\" role=\"status\" aria-live=\"polite\">\n <span className=\"sr-only\">{t(\"loading\")}</span>\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-1/2\" />\n </div>\n ) : (\n <div\n className={cn(\n \"overflow-x-auto rounded-md text-caption [&_table]:w-full\",\n // #124: this colours the ambient text of the error/result body —\n // running text, so the error branch takes the ink rung.\n showError ? \"bg-destructive/10 text-destructive-text\" : \"bg-muted/50 text-foreground\",\n )}\n >\n {showError && <div>{errorText}</div>}\n {Output}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/_lazy-engine-boundary.tsx"],"sourcesContent":["\"use client\";\n\nimport { Component, type ErrorInfo, type ReactNode } from \"react\";\n\nexport interface LazyEngineBoundaryProps {\n children: ReactNode;\n /**\n * Called once a wrapped `lazy()` import rejects (an optional peer that is\n * not installed, or any other load-time failure). Return what to render in\n * its place — the boundary owns no default visual, since the right\n * stand-in differs by surface (an orb placeholder for `Persona`).\n */\n renderMissing: (error: unknown) => ReactNode;\n}\n\ninterface LazyEngineBoundaryState {\n // Wrapped in an object so `{ value: undefined }` (a boundary that caught\n // an error whose value happens to be `undefined`) is distinguishable from\n // \"nothing caught yet\" (`error: null`).\n error: { value: unknown } | null;\n}\n\n/**\n * Catches a `React.lazy()` load failure — an optional peer dependency that is\n * not installed (issue #33), or any other dynamic-import rejection — and\n * renders a caller-supplied fallback instead of unmounting the tree.\n *\n * `Suspense` alone does not do this: it only covers the PENDING state. A\n * REJECTED lazy import throws during render, and with no boundary above it\n * that throw propagates to the nearest ancestor boundary React DOES find —\n * by default, the whole app. This is the one place in `@elabs-ai/components-ai`\n * that needs a real render-phase error boundary; Mermaid and the interactive\n * terminal fail via an awaited promise instead (Streamdown's `errorComponent`\n * and a `.catch()` on the mount effect, respectively), which don't need one.\n */\nexport class LazyEngineBoundary extends Component<\n LazyEngineBoundaryProps,\n LazyEngineBoundaryState\n> {\n override state: LazyEngineBoundaryState = { error: null };\n\n static getDerivedStateFromError(error: unknown): LazyEngineBoundaryState {\n return { error: { value: error } };\n }\n\n override componentDidCatch(error: unknown, info: ErrorInfo): void {\n console.error(\n \"[@elabs-ai/components-ai] a lazy engine failed to load:\",\n error,\n info.componentStack,\n );\n }\n\n override render(): ReactNode {\n if (this.state.error) {\n return this.props.renderMissing(this.state.error.value);\n }\n return this.props.children;\n }\n}\n"],"mappings":";;;AAEA,SAAS,iBAAiD;AAiCnD,IAAM,qBAAN,cAAiC,UAGtC;AAAA,EACS,QAAiC,EAAE,OAAO,KAAK;AAAA,EAExD,OAAO,yBAAyB,OAAyC;AACvE,WAAO,EAAE,OAAO,EAAE,OAAO,MAAM,EAAE;AAAA,EACnC;AAAA,EAES,kBAAkB,OAAgB,MAAuB;AAChE,YAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA,KAAK;AAAA,IACP;AAAA,EACF;AAAA,EAES,SAAoB;AAC3B,QAAI,KAAK,MAAM,OAAO;AACpB,aAAO,KAAK,MAAM,cAAc,KAAK,MAAM,MAAM,KAAK;AAAA,IACxD;AACA,WAAO,KAAK,MAAM;AAAA,EACpB;AACF;","names":[]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/generated-image.tsx
|
|
4
|
+
import { Image } from "@elabs-ai/components-ui";
|
|
5
|
+
import { cn } from "@elabs-ai/components-ui/lib/cn";
|
|
6
|
+
import { forwardRef } from "react";
|
|
7
|
+
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
var GeneratedImage = forwardRef(
|
|
9
|
+
function GeneratedImage2({
|
|
10
|
+
base64,
|
|
11
|
+
uint8Array: _uint8Array,
|
|
12
|
+
mediaType,
|
|
13
|
+
alt = "",
|
|
14
|
+
showSkeleton,
|
|
15
|
+
width,
|
|
16
|
+
height,
|
|
17
|
+
className,
|
|
18
|
+
...props
|
|
19
|
+
}, ref) {
|
|
20
|
+
return /* @__PURE__ */ jsx(
|
|
21
|
+
Image,
|
|
22
|
+
{
|
|
23
|
+
...props,
|
|
24
|
+
ref,
|
|
25
|
+
"data-slot": "generated-image",
|
|
26
|
+
src: `data:${mediaType};base64,${base64}`,
|
|
27
|
+
alt,
|
|
28
|
+
width,
|
|
29
|
+
height,
|
|
30
|
+
showSkeleton: showSkeleton ?? Boolean(width && height),
|
|
31
|
+
className: cn("rounded-md", className)
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
GeneratedImage.displayName = "GeneratedImage";
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
GeneratedImage
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=chunk-RLV64EWK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/generated-image.tsx"],"sourcesContent":["\"use client\";\n\nimport { Image, type ImageProps } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport type { Experimental_GeneratedImage } from \"ai\";\nimport { forwardRef } from \"react\";\n\nexport type GeneratedImageProps = Experimental_GeneratedImage &\n Omit<ImageProps, \"src\" | \"alt\" | \"aspectRatio\" | \"showSkeleton\"> & {\n /** Accessible text. Omit (or `\"\"`) for a decorative image. @default \"\" */\n alt?: string;\n /**\n * Show a `Skeleton` in the reserved `width` × `height` box until the base64\n * payload decodes, so it does not pop in. Without both dimensions there is\n * no box to reserve, so the default is off in that case.\n * @default Boolean(width && height)\n */\n showSkeleton?: boolean;\n };\n\n/**\n * An AI-SDK generated image (`Experimental_GeneratedImage`) rendered through\n * the ui `Image` primitive as a base64 data URL — decode-aware skeleton,\n * `ImageOff` fallback on a terminal error.\n */\nexport const GeneratedImage = forwardRef<HTMLImageElement, GeneratedImageProps>(\n function GeneratedImage(\n {\n base64,\n uint8Array: _uint8Array,\n mediaType,\n alt = \"\",\n showSkeleton,\n width,\n height,\n className,\n ...props\n },\n ref,\n ) {\n return (\n <Image\n {...props}\n ref={ref}\n data-slot=\"generated-image\"\n src={`data:${mediaType};base64,${base64}`}\n alt={alt}\n width={width}\n height={height}\n showSkeleton={showSkeleton ?? Boolean(width && height)}\n className={cn(\"rounded-md\", className)}\n />\n );\n },\n);\n\nGeneratedImage.displayName = \"GeneratedImage\";\n"],"mappings":";;;AAEA,SAAS,aAA8B;AACvC,SAAS,UAAU;AAEnB,SAAS,kBAAkB;AAoCrB;AAhBC,IAAM,iBAAiB;AAAA,EAC5B,SAASA,gBACP;AAAA,IACE;AAAA,IACA,YAAY;AAAA,IACZ;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,GACA,KACA;AACA,WACE;AAAA,MAAC;AAAA;AAAA,QACE,GAAG;AAAA,QACJ;AAAA,QACA,aAAU;AAAA,QACV,KAAK,QAAQ,SAAS,WAAW,MAAM;AAAA,QACvC;AAAA,QACA;AAAA,QACA;AAAA,QACA,cAAc,gBAAgB,QAAQ,SAAS,MAAM;AAAA,QACrD,WAAW,GAAG,cAAc,SAAS;AAAA;AAAA,IACvC;AAAA,EAEJ;AACF;AAEA,eAAe,cAAc;","names":["GeneratedImage"]}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/audio-player.tsx
|
|
4
|
+
import {
|
|
5
|
+
MediaPlayer,
|
|
6
|
+
MediaPlayerControls,
|
|
7
|
+
MediaPlayerElement,
|
|
8
|
+
MediaPlayerError,
|
|
9
|
+
MediaPlayerLoading,
|
|
10
|
+
MediaPlayerMuteButton,
|
|
11
|
+
MediaPlayerPlayButton,
|
|
12
|
+
MediaPlayerSeekButton,
|
|
13
|
+
MediaPlayerTime,
|
|
14
|
+
MediaPlayerTimeSlider,
|
|
15
|
+
MediaPlayerVolumeSlider,
|
|
16
|
+
useLocale,
|
|
17
|
+
useMediaPlayer
|
|
18
|
+
} from "@elabs-ai/components-ui";
|
|
19
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
20
|
+
function AudioPlayerErrorRung() {
|
|
21
|
+
const { t } = useLocale();
|
|
22
|
+
const { state } = useMediaPlayer();
|
|
23
|
+
if (!state.error) return null;
|
|
24
|
+
return /* @__PURE__ */ jsx(MediaPlayerError, { title: t("ai.audioPlayer.renderError") });
|
|
25
|
+
}
|
|
26
|
+
var AudioPlayer = ({
|
|
27
|
+
keyboardShortcuts,
|
|
28
|
+
noHotkeys,
|
|
29
|
+
keyboardControl,
|
|
30
|
+
children,
|
|
31
|
+
// Accepted and ignored (former legacy controller attributes).
|
|
32
|
+
autohide: _autohide,
|
|
33
|
+
autohideOverControls: _autohideOverControls,
|
|
34
|
+
breakpoints: _breakpoints,
|
|
35
|
+
breakpointsComputed: _breakpointsComputed,
|
|
36
|
+
defaultDuration: _defaultDuration,
|
|
37
|
+
defaultStreamType: _defaultStreamType,
|
|
38
|
+
defaultSubtitles: _defaultSubtitles,
|
|
39
|
+
gesturesDisabled: _gesturesDisabled,
|
|
40
|
+
keysUsed: _keysUsed,
|
|
41
|
+
liveEdgeOffset: _liveEdgeOffset,
|
|
42
|
+
noAutohide: _noAutohide,
|
|
43
|
+
noAutoSeekToLive: _noAutoSeekToLive,
|
|
44
|
+
noDefaultStore: _noDefaultStore,
|
|
45
|
+
noMutedPref: _noMutedPref,
|
|
46
|
+
noSubtitlesLangPref: _noSubtitlesLangPref,
|
|
47
|
+
noVolumePref: _noVolumePref,
|
|
48
|
+
resolvedLang: _resolvedLang,
|
|
49
|
+
userInteractive: _userInteractive,
|
|
50
|
+
...props
|
|
51
|
+
}) => /* @__PURE__ */ jsxs(
|
|
52
|
+
MediaPlayer,
|
|
53
|
+
{
|
|
54
|
+
kind: "audio",
|
|
55
|
+
"data-slot": "media-player",
|
|
56
|
+
keyboardShortcuts: (keyboardShortcuts ?? true) && !noHotkeys && keyboardControl !== false,
|
|
57
|
+
...props,
|
|
58
|
+
children: [
|
|
59
|
+
children,
|
|
60
|
+
/* @__PURE__ */ jsx(MediaPlayerLoading, {}),
|
|
61
|
+
/* @__PURE__ */ jsx(AudioPlayerErrorRung, {})
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
var AudioPlayerElement = ({ ref, ...props }) => {
|
|
66
|
+
const { src, data, ...rest } = props;
|
|
67
|
+
return /* @__PURE__ */ jsx(
|
|
68
|
+
MediaPlayerElement,
|
|
69
|
+
{
|
|
70
|
+
"data-slot": "media-player-element",
|
|
71
|
+
...rest,
|
|
72
|
+
ref,
|
|
73
|
+
src: data ? `data:${data.mediaType};base64,${data.base64}` : src
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
var AudioPlayerControlBar = (props) => /* @__PURE__ */ jsx(MediaPlayerControls, { "data-slot": "media-player-controls", ...props });
|
|
78
|
+
var AudioPlayerPlayButton = (props) => /* @__PURE__ */ jsx(MediaPlayerPlayButton, { "data-slot": "media-player-play-button", ...props });
|
|
79
|
+
var AudioPlayerSeekBackwardButton = ({
|
|
80
|
+
offset,
|
|
81
|
+
seekOffset,
|
|
82
|
+
...props
|
|
83
|
+
}) => /* @__PURE__ */ jsx(
|
|
84
|
+
MediaPlayerSeekButton,
|
|
85
|
+
{
|
|
86
|
+
"data-slot": "media-player-seek-button",
|
|
87
|
+
...props,
|
|
88
|
+
offset: -Math.abs(offset ?? seekOffset ?? 10)
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
var AudioPlayerSeekForwardButton = ({
|
|
92
|
+
offset,
|
|
93
|
+
seekOffset,
|
|
94
|
+
mediaController: _mediaController,
|
|
95
|
+
mediaCurrentTime: _mediaCurrentTime,
|
|
96
|
+
noTooltip: _noTooltip,
|
|
97
|
+
preventClick: _preventClick,
|
|
98
|
+
...props
|
|
99
|
+
}) => /* @__PURE__ */ jsx(
|
|
100
|
+
MediaPlayerSeekButton,
|
|
101
|
+
{
|
|
102
|
+
"data-slot": "media-player-seek-button",
|
|
103
|
+
...props,
|
|
104
|
+
offset: Math.abs(offset ?? seekOffset ?? 10)
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
var AudioPlayerTimeDisplay = (props) => /* @__PURE__ */ jsx(MediaPlayerTime, { "data-slot": "media-player-time", ...props, mode: "current" });
|
|
108
|
+
var AudioPlayerTimeRange = (props) => /* @__PURE__ */ jsx(MediaPlayerTimeSlider, { "data-slot": "media-player-time-slider", ...props });
|
|
109
|
+
var AudioPlayerDurationDisplay = (props) => /* @__PURE__ */ jsx(MediaPlayerTime, { "data-slot": "media-player-time", ...props, mode: "duration" });
|
|
110
|
+
var AudioPlayerMuteButton = (props) => /* @__PURE__ */ jsx(MediaPlayerMuteButton, { "data-slot": "media-player-mute-button", ...props });
|
|
111
|
+
var AudioPlayerVolumeRange = (props) => /* @__PURE__ */ jsx(MediaPlayerVolumeSlider, { "data-slot": "media-player-volume-slider", ...props });
|
|
112
|
+
|
|
113
|
+
export {
|
|
114
|
+
AudioPlayer,
|
|
115
|
+
AudioPlayerElement,
|
|
116
|
+
AudioPlayerControlBar,
|
|
117
|
+
AudioPlayerPlayButton,
|
|
118
|
+
AudioPlayerSeekBackwardButton,
|
|
119
|
+
AudioPlayerSeekForwardButton,
|
|
120
|
+
AudioPlayerTimeDisplay,
|
|
121
|
+
AudioPlayerTimeRange,
|
|
122
|
+
AudioPlayerDurationDisplay,
|
|
123
|
+
AudioPlayerMuteButton,
|
|
124
|
+
AudioPlayerVolumeRange
|
|
125
|
+
};
|
|
126
|
+
//# sourceMappingURL=chunk-SSES3F4Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/audio-player.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n MediaPlayer,\n MediaPlayerControls,\n MediaPlayerElement,\n MediaPlayerError,\n MediaPlayerLoading,\n MediaPlayerMuteButton,\n MediaPlayerPlayButton,\n MediaPlayerSeekButton,\n MediaPlayerTime,\n MediaPlayerTimeSlider,\n MediaPlayerVolumeSlider,\n useLocale,\n useMediaPlayer,\n type MediaPlayerButtonProps,\n type MediaPlayerControlsProps,\n type MediaPlayerElementProps,\n type MediaPlayerProps,\n type MediaPlayerTimeProps,\n type MediaPlayerTimeSliderProps,\n type MediaPlayerVolumeSliderProps,\n} from \"@elabs-ai/components-ui\";\nimport type { Experimental_SpeechResult as SpeechResult } from \"ai\";\nimport type { ComponentProps, HTMLAttributes, Ref } from \"react\";\n\n/**\n * `AudioPlayer*` — the chat-facing names for spoken agent replies, as thin\n * presets over ui's `MediaPlayer*` compound parts (ADR 0041). Each preset\n * emits the SHARED slot of the ui part it wraps (`media-player*`, spelled out\n * so this module declares what it renders); the former `audio-player*`\n * selectors are gone (maintainer decision 2026-09-23, ADR 0041 §7). The ui\n * part underneath owns the behaviour, labels and styling.\n */\n\n/**\n * @deprecated The shared base of the former custom-element parts. Each part now\n * has its own prop type (`AudioPlayerPlayButtonProps`, …); removed in the next\n * major.\n */\nexport type AudioPlayerPartProps = HTMLAttributes<HTMLElement>;\n\n/**\n * The former custom-element controller attributes. Kept so existing\n * call sites typecheck; `noHotkeys`/`keyboardControl` still map onto\n * `keyboardShortcuts`, every other member is accepted and ignored. All are\n * removed in the next major.\n */\ninterface AudioPlayerLegacyProps {\n /** @deprecated Ignored (legacy controller attribute); removed in the next major. */\n autohide?: string;\n /** @deprecated Ignored (legacy controller attribute); removed in the next major. */\n autohideOverControls?: boolean;\n /** @deprecated Ignored (legacy controller attribute); removed in the next major. */\n breakpoints?: string;\n /** @deprecated Ignored (legacy controller attribute); removed in the next major. */\n breakpointsComputed?: boolean;\n /** @deprecated Ignored (legacy controller attribute); removed in the next major. */\n defaultDuration?: number;\n /** @deprecated Ignored (legacy controller attribute); removed in the next major. */\n defaultStreamType?: string;\n /** @deprecated Ignored (legacy controller attribute); removed in the next major. */\n defaultSubtitles?: boolean;\n /** @deprecated Ignored (legacy controller attribute); removed in the next major. */\n gesturesDisabled?: boolean;\n /** @deprecated Use `keyboardShortcuts`. `false` turns the player's keyboard map off. */\n keyboardControl?: boolean;\n /** @deprecated Ignored (legacy controller attribute); removed in the next major. */\n keysUsed?: string;\n /** @deprecated Ignored (legacy controller attribute); removed in the next major. */\n liveEdgeOffset?: number;\n /** @deprecated Ignored (legacy controller attribute); removed in the next major. */\n noAutohide?: boolean;\n /** @deprecated Ignored (legacy controller attribute); removed in the next major. */\n noAutoSeekToLive?: boolean;\n /** @deprecated Ignored (legacy controller attribute); removed in the next major. */\n noDefaultStore?: boolean;\n /** @deprecated Use `keyboardShortcuts={false}`. */\n noHotkeys?: boolean;\n /** @deprecated Ignored (legacy controller attribute); removed in the next major. */\n noMutedPref?: boolean;\n /** @deprecated Ignored (legacy controller attribute); removed in the next major. */\n noSubtitlesLangPref?: boolean;\n /** @deprecated Ignored (legacy controller attribute); removed in the next major. */\n noVolumePref?: boolean;\n /** @deprecated Ignored (legacy controller attribute); removed in the next major. */\n resolvedLang?: string;\n /** @deprecated Ignored (legacy controller attribute); removed in the next major. */\n userInteractive?: boolean;\n}\n\nexport interface AudioPlayerProps extends Omit<MediaPlayerProps, \"kind\">, AudioPlayerLegacyProps {}\n\n/** Replaces the controls with the compact error panel once playback has terminally failed. */\nfunction AudioPlayerErrorRung() {\n const { t } = useLocale();\n const { state } = useMediaPlayer();\n if (!state.error) return null;\n return <MediaPlayerError title={t(\"ai.audioPlayer.renderError\")} />;\n}\n\n/**\n * The player root (`MediaPlayer kind=\"audio\"`): transparent, so put it in the\n * bubble or card that is its surface. Compose `AudioPlayerElement` plus an\n * `AudioPlayerControlBar` inside.\n */\nexport const AudioPlayer = ({\n keyboardShortcuts,\n noHotkeys,\n keyboardControl,\n children,\n // Accepted and ignored (former legacy controller attributes).\n autohide: _autohide,\n autohideOverControls: _autohideOverControls,\n breakpoints: _breakpoints,\n breakpointsComputed: _breakpointsComputed,\n defaultDuration: _defaultDuration,\n defaultStreamType: _defaultStreamType,\n defaultSubtitles: _defaultSubtitles,\n gesturesDisabled: _gesturesDisabled,\n keysUsed: _keysUsed,\n liveEdgeOffset: _liveEdgeOffset,\n noAutohide: _noAutohide,\n noAutoSeekToLive: _noAutoSeekToLive,\n noDefaultStore: _noDefaultStore,\n noMutedPref: _noMutedPref,\n noSubtitlesLangPref: _noSubtitlesLangPref,\n noVolumePref: _noVolumePref,\n resolvedLang: _resolvedLang,\n userInteractive: _userInteractive,\n ...props\n}: AudioPlayerProps) => (\n <MediaPlayer\n kind=\"audio\"\n data-slot=\"media-player\"\n keyboardShortcuts={(keyboardShortcuts ?? true) && !noHotkeys && keyboardControl !== false}\n {...props}\n >\n {children}\n <MediaPlayerLoading />\n <AudioPlayerErrorRung />\n </MediaPlayer>\n);\n\nexport type AudioPlayerElementProps = Omit<ComponentProps<\"audio\">, \"src\"> &\n (\n | {\n data: SpeechResult[\"audio\"];\n }\n | {\n src: string;\n }\n );\n\n/**\n * The `<audio>` element. Pass a URL as `src`, or an AI SDK speech result's\n * `audio` as `data` (converted to a base64 data URL).\n */\nexport const AudioPlayerElement = ({ ref, ...props }: AudioPlayerElementProps) => {\n const { src, data, ...rest } = props as typeof props & {\n src?: string;\n data?: SpeechResult[\"audio\"];\n };\n return (\n <MediaPlayerElement\n data-slot=\"media-player-element\"\n {...(rest as MediaPlayerElementProps)}\n ref={ref as Ref<HTMLMediaElement> | undefined}\n src={data ? `data:${data.mediaType};base64,${data.base64}` : src}\n />\n );\n};\n\nexport type AudioPlayerControlBarProps = MediaPlayerControlsProps;\n\nexport const AudioPlayerControlBar = (props: AudioPlayerControlBarProps) => (\n <MediaPlayerControls data-slot=\"media-player-controls\" {...props} />\n);\n\nexport type AudioPlayerPlayButtonProps = MediaPlayerButtonProps;\n\nexport const AudioPlayerPlayButton = (props: AudioPlayerPlayButtonProps) => (\n <MediaPlayerPlayButton data-slot=\"media-player-play-button\" {...props} />\n);\n\n/** Props shared by the two seek presets. */\ninterface AudioPlayerSeekPresetProps extends MediaPlayerButtonProps {\n /** Seconds to skip, as a positive distance; the button sets the direction. Default 10. */\n offset?: number;\n /** @deprecated Use `offset`. */\n seekOffset?: number;\n}\n\nexport type AudioPlayerSeekBackwardButtonProps = AudioPlayerSeekPresetProps;\n\nexport const AudioPlayerSeekBackwardButton = ({\n offset,\n seekOffset,\n ...props\n}: AudioPlayerSeekBackwardButtonProps) => (\n <MediaPlayerSeekButton\n data-slot=\"media-player-seek-button\"\n {...props}\n offset={-Math.abs(offset ?? seekOffset ?? 10)}\n />\n);\n\nexport interface AudioPlayerSeekForwardButtonProps extends AudioPlayerSeekPresetProps {\n /** @deprecated Ignored (legacy attribute); removed in the next major. */\n mediaController?: string;\n /** @deprecated Ignored (legacy attribute); removed in the next major. */\n mediaCurrentTime?: number;\n /** @deprecated Ignored (legacy attribute); removed in the next major. */\n noTooltip?: boolean;\n /** @deprecated Ignored (legacy attribute); removed in the next major. */\n preventClick?: boolean;\n}\n\nexport const AudioPlayerSeekForwardButton = ({\n offset,\n seekOffset,\n mediaController: _mediaController,\n mediaCurrentTime: _mediaCurrentTime,\n noTooltip: _noTooltip,\n preventClick: _preventClick,\n ...props\n}: AudioPlayerSeekForwardButtonProps) => (\n <MediaPlayerSeekButton\n data-slot=\"media-player-seek-button\"\n {...props}\n offset={Math.abs(offset ?? seekOffset ?? 10)}\n />\n);\n\nexport type AudioPlayerTimeDisplayProps = Omit<MediaPlayerTimeProps, \"mode\">;\n\n/** The current playback position. */\nexport const AudioPlayerTimeDisplay = (props: AudioPlayerTimeDisplayProps) => (\n <MediaPlayerTime data-slot=\"media-player-time\" {...props} mode=\"current\" />\n);\n\nexport type AudioPlayerTimeRangeProps = MediaPlayerTimeSliderProps;\n\nexport const AudioPlayerTimeRange = (props: AudioPlayerTimeRangeProps) => (\n <MediaPlayerTimeSlider data-slot=\"media-player-time-slider\" {...props} />\n);\n\nexport type AudioPlayerDurationDisplayProps = Omit<MediaPlayerTimeProps, \"mode\">;\n\n/** The total duration. */\nexport const AudioPlayerDurationDisplay = (props: AudioPlayerDurationDisplayProps) => (\n <MediaPlayerTime data-slot=\"media-player-time\" {...props} mode=\"duration\" />\n);\n\nexport type AudioPlayerMuteButtonProps = MediaPlayerButtonProps;\n\nexport const AudioPlayerMuteButton = (props: AudioPlayerMuteButtonProps) => (\n <MediaPlayerMuteButton data-slot=\"media-player-mute-button\" {...props} />\n);\n\nexport type AudioPlayerVolumeRangeProps = MediaPlayerVolumeSliderProps;\n\nexport const AudioPlayerVolumeRange = (props: AudioPlayerVolumeRangeProps) => (\n <MediaPlayerVolumeSlider data-slot=\"media-player-volume-slider\" {...props} />\n);\n"],"mappings":"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","names":[]}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
DialogContent,
|
|
15
15
|
DialogDescription,
|
|
16
16
|
DialogTitle,
|
|
17
|
+
Image,
|
|
17
18
|
Skeleton,
|
|
18
19
|
downloadUrl
|
|
19
20
|
} from "@elabs-ai/components-ui";
|
|
@@ -41,48 +42,6 @@ function useGallery() {
|
|
|
41
42
|
function triggerDownload(image) {
|
|
42
43
|
void downloadUrl(image.downloadUrl ?? image.src, image.downloadName);
|
|
43
44
|
}
|
|
44
|
-
function GalleryImg({
|
|
45
|
-
src,
|
|
46
|
-
alt,
|
|
47
|
-
className,
|
|
48
|
-
loading,
|
|
49
|
-
showSkeleton = true
|
|
50
|
-
}) {
|
|
51
|
-
const [failed, setFailed] = useState(false);
|
|
52
|
-
const [loaded, setLoaded] = useState(false);
|
|
53
|
-
const imgRef = useRef(null);
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
if (imgRef.current?.complete) setLoaded(true);
|
|
56
|
-
}, []);
|
|
57
|
-
if (failed) {
|
|
58
|
-
return /* @__PURE__ */ jsx(
|
|
59
|
-
"div",
|
|
60
|
-
{
|
|
61
|
-
className: cn(
|
|
62
|
-
"flex flex-col items-center justify-center gap-1 bg-muted text-muted-foreground",
|
|
63
|
-
className
|
|
64
|
-
),
|
|
65
|
-
...alt ? { role: "img", "aria-label": alt } : { "aria-hidden": true },
|
|
66
|
-
children: /* @__PURE__ */ jsx(ImageOff, { className: "size-6", "aria-hidden": "true" })
|
|
67
|
-
}
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
71
|
-
showSkeleton && !loaded ? /* @__PURE__ */ jsx(Skeleton, { className: "absolute inset-0 size-full rounded-none" }) : null,
|
|
72
|
-
/* @__PURE__ */ jsx(
|
|
73
|
-
"img",
|
|
74
|
-
{
|
|
75
|
-
ref: imgRef,
|
|
76
|
-
src,
|
|
77
|
-
alt,
|
|
78
|
-
loading,
|
|
79
|
-
onError: () => setFailed(true),
|
|
80
|
-
onLoad: () => setLoaded(true),
|
|
81
|
-
className
|
|
82
|
-
}
|
|
83
|
-
)
|
|
84
|
-
] });
|
|
85
|
-
}
|
|
86
45
|
function GalleryDownloadButton({
|
|
87
46
|
image,
|
|
88
47
|
className,
|
|
@@ -182,15 +141,7 @@ function GallerySingle() {
|
|
|
182
141
|
const image = images[0];
|
|
183
142
|
if (!image) return null;
|
|
184
143
|
return /* @__PURE__ */ jsxs(GalleryTilt, { children: [
|
|
185
|
-
/* @__PURE__ */ jsx(AspectRatio, { ratio: aspectRatio, className: "overflow-hidden rounded-lg", children: /* @__PURE__ */ jsx(
|
|
186
|
-
GalleryImg,
|
|
187
|
-
{
|
|
188
|
-
src: image.src,
|
|
189
|
-
alt: image.alt,
|
|
190
|
-
loading: "lazy",
|
|
191
|
-
className: "size-full object-cover"
|
|
192
|
-
}
|
|
193
|
-
) }),
|
|
144
|
+
/* @__PURE__ */ jsx(AspectRatio, { ratio: aspectRatio, className: "overflow-hidden rounded-lg", children: /* @__PURE__ */ jsx(Image, { src: image.src, alt: image.alt, fit: "cover", loading: "lazy", showSkeleton: true }) }),
|
|
194
145
|
/* @__PURE__ */ jsxs("div", { className: "absolute end-2 top-2 z-10 flex items-center gap-1", children: [
|
|
195
146
|
hideDownload ? null : /* @__PURE__ */ jsx(GalleryDownloadButton, { image, className: HOVER_TOOLBAR }),
|
|
196
147
|
/* @__PURE__ */ jsx(
|
|
@@ -221,12 +172,13 @@ function GalleryTile({ image, index }) {
|
|
|
221
172
|
"aria-label": label,
|
|
222
173
|
className: "block w-full rounded-lg focus-ring",
|
|
223
174
|
children: /* @__PURE__ */ jsx(AspectRatio, { ratio: aspectRatio, className: "overflow-hidden rounded-lg", children: /* @__PURE__ */ jsx(
|
|
224
|
-
|
|
175
|
+
Image,
|
|
225
176
|
{
|
|
226
177
|
src: image.thumbnailSrc ?? image.src,
|
|
227
178
|
alt: "",
|
|
179
|
+
fit: "cover",
|
|
228
180
|
loading: "lazy",
|
|
229
|
-
|
|
181
|
+
showSkeleton: true
|
|
230
182
|
}
|
|
231
183
|
) })
|
|
232
184
|
}
|
|
@@ -344,12 +296,13 @@ function GalleryCarousel() {
|
|
|
344
296
|
"aria-label": `${index + 1} of ${images.length}`,
|
|
345
297
|
children: /* @__PURE__ */ jsxs("div", { className: "group/slide relative flex items-center justify-center px-4", children: [
|
|
346
298
|
/* @__PURE__ */ jsx(
|
|
347
|
-
|
|
299
|
+
Image,
|
|
348
300
|
{
|
|
349
301
|
src: image.src,
|
|
350
302
|
alt: image.alt,
|
|
303
|
+
fit: "contain",
|
|
351
304
|
showSkeleton: false,
|
|
352
|
-
className: "max-h-[80vh] w-auto max-w-full
|
|
305
|
+
className: "max-h-[80vh] w-auto max-w-full"
|
|
353
306
|
}
|
|
354
307
|
),
|
|
355
308
|
hideDownload ? null : /* @__PURE__ */ jsx(
|
|
@@ -475,4 +428,4 @@ var Gallery = forwardRef(function Gallery2({
|
|
|
475
428
|
export {
|
|
476
429
|
Gallery
|
|
477
430
|
};
|
|
478
|
-
//# sourceMappingURL=chunk-
|
|
431
|
+
//# sourceMappingURL=chunk-TBCMCF5O.js.map
|