@elabs-ai/components-ai 5.4.0 → 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-VJZJWNEC.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-AOOIAFDT.js → chunk-OCZNA5SO.js} +2 -2
- 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-UHLVPAHG.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 +1 -1
- 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 +1 -1
- 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/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-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-VJZJWNEC.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-AOOIAFDT.js.map → chunk-OCZNA5SO.js.map} +0 -0
- /package/dist/{chunk-UHLVPAHG.js.map → chunk-WXUXFDJQ.js.map} +0 -0
- /package/dist/{chunk-X47QMM6K.js.map → chunk-YLN7E4OV.js.map} +0 -0
package/src/image.stories.tsx
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
import { Image } from "./image";
|
|
3
|
-
|
|
4
|
-
// A tiny 1x1 solid-red PNG — enough to exercise decode + skeleton timing
|
|
5
|
-
// without shipping a real asset in the story.
|
|
6
|
-
const RED_DOT =
|
|
7
|
-
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC";
|
|
8
|
-
|
|
9
|
-
const meta = {
|
|
10
|
-
title: "AI/Image",
|
|
11
|
-
component: Image,
|
|
12
|
-
parameters: { layout: "padded" },
|
|
13
|
-
} satisfies Meta<typeof Image>;
|
|
14
|
-
export default meta;
|
|
15
|
-
type Story = StoryObj<typeof meta>;
|
|
16
|
-
|
|
17
|
-
// `showSkeleton` (default `Boolean(width && height)` — true here, since both
|
|
18
|
-
// are given) covers the box with a `Skeleton` until the `<img>` finishes
|
|
19
|
-
// decoding, reserving the `width`/`height` box so there is no layout shift
|
|
20
|
-
// (loading-states.md, interaction-guidelines.md 'Images'). Without both
|
|
21
|
-
// dimensions there is no box to reserve, so the skeleton defaults off in that
|
|
22
|
-
// case rather than collapsing to nothing (see the `NoSkeleton` story). The
|
|
23
|
-
// Skeleton window is real-decode-timed (there is no consumer `loading` prop
|
|
24
|
-
// like Gallery's — see image.tsx), so it is too fast/racy to freeze reliably
|
|
25
|
-
// in a real-browser Storybook snapshot; it is instead exercised
|
|
26
|
-
// deterministically in image.test.tsx (jsdom never auto-resolves `<img>`
|
|
27
|
-
// loads, so the pending state holds until the test fires `load` itself).
|
|
28
|
-
export const Default: Story = {
|
|
29
|
-
args: {
|
|
30
|
-
base64: RED_DOT,
|
|
31
|
-
mediaType: "image/png",
|
|
32
|
-
uint8Array: new Uint8Array(),
|
|
33
|
-
alt: "A generated red square",
|
|
34
|
-
width: 128,
|
|
35
|
-
height: 128,
|
|
36
|
-
},
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
// BROKEN — a genuinely undecodable payload settles into the `onError`
|
|
40
|
-
// fallback (an `ImageOff` glyph on a muted box, sized to `width`/`height`)
|
|
41
|
-
// instead of leaving the browser's native broken-image glyph bleeding through
|
|
42
|
-
// a perpetually-pulsing Skeleton.
|
|
43
|
-
export const Broken: Story = {
|
|
44
|
-
args: {
|
|
45
|
-
base64: "not-a-real-image",
|
|
46
|
-
mediaType: "image/png",
|
|
47
|
-
uint8Array: new Uint8Array(),
|
|
48
|
-
alt: "A generated image",
|
|
49
|
-
width: 128,
|
|
50
|
-
height: 128,
|
|
51
|
-
},
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
// Disabling `showSkeleton` opts out for a context that already has its own
|
|
55
|
-
// placeholder (e.g. a Gallery tile).
|
|
56
|
-
export const NoSkeleton: Story = {
|
|
57
|
-
name: "showSkeleton=false",
|
|
58
|
-
args: {
|
|
59
|
-
base64: RED_DOT,
|
|
60
|
-
mediaType: "image/png",
|
|
61
|
-
uint8Array: new Uint8Array(),
|
|
62
|
-
alt: "A generated red square",
|
|
63
|
-
width: 128,
|
|
64
|
-
height: 128,
|
|
65
|
-
showSkeleton: false,
|
|
66
|
-
},
|
|
67
|
-
};
|
package/src/image.test.tsx
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { fireEvent, render } from "@testing-library/react";
|
|
3
|
-
import { createRef } from "react";
|
|
4
|
-
import { Image, type ImageProps } from "./image";
|
|
5
|
-
|
|
6
|
-
const RED_DOT =
|
|
7
|
-
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC";
|
|
8
|
-
|
|
9
|
-
const BASE_PROPS: Pick<ImageProps, "base64" | "mediaType" | "uint8Array"> = {
|
|
10
|
-
base64: RED_DOT,
|
|
11
|
-
mediaType: "image/png",
|
|
12
|
-
uint8Array: new Uint8Array(),
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
describe("Image", () => {
|
|
16
|
-
it("renders the img with the base64 data URI, alt, width and height", () => {
|
|
17
|
-
const { container } = render(
|
|
18
|
-
<Image {...BASE_PROPS} alt="A red square" width={64} height={64} />,
|
|
19
|
-
);
|
|
20
|
-
const img = container.querySelector("img");
|
|
21
|
-
expect(img).not.toBeNull();
|
|
22
|
-
expect(img?.getAttribute("src")).toBe(`data:image/png;base64,${RED_DOT}`);
|
|
23
|
-
expect(img?.getAttribute("alt")).toBe("A red square");
|
|
24
|
-
expect(img?.getAttribute("width")).toBe("64");
|
|
25
|
-
expect(img?.getAttribute("height")).toBe("64");
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("defaults alt to empty (decorative) rather than omitting it", () => {
|
|
29
|
-
const { container } = render(<Image {...BASE_PROPS} />);
|
|
30
|
-
expect(container.querySelector("img")?.getAttribute("alt")).toBe("");
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("forwards a ref to the underlying <img>", () => {
|
|
34
|
-
const ref = createRef<HTMLImageElement>();
|
|
35
|
-
render(<Image ref={ref} {...BASE_PROPS} />);
|
|
36
|
-
expect(ref.current).toBeInstanceOf(HTMLImageElement);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
describe("Image showSkeleton (#269, loading-states.md)", () => {
|
|
41
|
-
it("shows a Skeleton (decorative + a live region) until onLoad fires, when width+height are given", () => {
|
|
42
|
-
const { container } = render(<Image {...BASE_PROPS} alt="" width={64} height={64} />);
|
|
43
|
-
const skeleton = container.querySelector(".animate-pulse");
|
|
44
|
-
expect(skeleton).not.toBeNull();
|
|
45
|
-
expect(skeleton).toHaveAttribute("aria-hidden", "true");
|
|
46
|
-
// Exactly one live region for the region.
|
|
47
|
-
expect(container.querySelectorAll('[role="status"]')).toHaveLength(1);
|
|
48
|
-
|
|
49
|
-
const img = container.querySelector("img")!;
|
|
50
|
-
fireEvent.load(img);
|
|
51
|
-
expect(container.querySelector(".animate-pulse")).toBeNull();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("defaults to no skeleton (and no live region) when width/height are not given — nothing to reserve", () => {
|
|
55
|
-
const { container } = render(<Image {...BASE_PROPS} alt="" />);
|
|
56
|
-
expect(container.querySelector(".animate-pulse")).toBeNull();
|
|
57
|
-
expect(container.querySelector('[role="status"]')).toBeNull();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("skips the skeleton entirely when showSkeleton is explicitly false, even with width+height", () => {
|
|
61
|
-
const { container } = render(
|
|
62
|
-
<Image {...BASE_PROPS} alt="" width={64} height={64} showSkeleton={false} />,
|
|
63
|
-
);
|
|
64
|
-
expect(container.querySelector(".animate-pulse")).toBeNull();
|
|
65
|
-
expect(container.querySelector('[role="status"]')).toBeNull();
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("shows the skeleton when explicitly enabled even without width/height", () => {
|
|
69
|
-
const { container } = render(<Image {...BASE_PROPS} alt="" showSkeleton />);
|
|
70
|
-
expect(container.querySelector(".animate-pulse")).not.toBeNull();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it("does not wrap the <img> in a sizing span when no skeleton is shown, so className keeps resolving against the real parent", () => {
|
|
74
|
-
const { container } = render(<Image {...BASE_PROPS} alt="" className="w-full" />);
|
|
75
|
-
expect(container.firstElementChild?.tagName).toBe("IMG");
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it("calls the consumer's onLoad in addition to clearing the skeleton", () => {
|
|
79
|
-
const onLoad = vi.fn();
|
|
80
|
-
const { container } = render(
|
|
81
|
-
<Image {...BASE_PROPS} alt="" width={64} height={64} onLoad={onLoad} />,
|
|
82
|
-
);
|
|
83
|
-
fireEvent.load(container.querySelector("img")!);
|
|
84
|
-
expect(onLoad).toHaveBeenCalledTimes(1);
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
describe("Image onError fallback (visual regression: broken image bled through the pulsing Skeleton)", () => {
|
|
89
|
-
it("replaces the img with an ImageOff fallback box instead of leaving the Skeleton pulsing forever", () => {
|
|
90
|
-
const { container } = render(<Image {...BASE_PROPS} alt="A generated image" />);
|
|
91
|
-
fireEvent.error(container.querySelector("img")!);
|
|
92
|
-
// No native <img> (and its browser broken-image glyph) remains in the DOM.
|
|
93
|
-
expect(container.querySelector("img")).toBeNull();
|
|
94
|
-
// No skeleton keeps pulsing behind a failed image.
|
|
95
|
-
expect(container.querySelector(".animate-pulse")).toBeNull();
|
|
96
|
-
expect(container.querySelector("svg")).not.toBeNull();
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it("calls the consumer's onError in addition to showing the fallback", () => {
|
|
100
|
-
const onError = vi.fn();
|
|
101
|
-
const { container } = render(<Image {...BASE_PROPS} alt="" onError={onError} />);
|
|
102
|
-
fireEvent.error(container.querySelector("img")!);
|
|
103
|
-
expect(onError).toHaveBeenCalledTimes(1);
|
|
104
|
-
});
|
|
105
|
-
});
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|