assistant-ui 0.0.121 → 0.0.122

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/commands/add.d.ts.map +1 -1
  2. package/dist/commands/add.js +1 -2
  3. package/dist/commands/add.js.map +1 -1
  4. package/dist/commands/create.d.ts.map +1 -1
  5. package/dist/commands/create.js +2 -2
  6. package/dist/commands/create.js.map +1 -1
  7. package/dist/commands/info.js +1 -1
  8. package/dist/commands/init.d.ts.map +1 -1
  9. package/dist/commands/init.js +1 -2
  10. package/dist/commands/init.js.map +1 -1
  11. package/dist/commands/mcp.d.ts.map +1 -1
  12. package/dist/commands/mcp.js +95 -23
  13. package/dist/commands/mcp.js.map +1 -1
  14. package/dist/lib/agent-skill.d.ts.map +1 -1
  15. package/dist/lib/agent-skill.js +9 -27
  16. package/dist/lib/agent-skill.js.map +1 -1
  17. package/dist/lib/create-project.d.ts +0 -9
  18. package/dist/lib/create-project.d.ts.map +1 -1
  19. package/dist/lib/create-project.js +15 -46
  20. package/dist/lib/create-project.js.map +1 -1
  21. package/dist/lib/transform.d.ts +0 -4
  22. package/dist/lib/transform.d.ts.map +1 -1
  23. package/dist/lib/transform.js +7 -16
  24. package/dist/lib/transform.js.map +1 -1
  25. package/dist/lib/utils/download.d.ts +5 -0
  26. package/dist/lib/utils/download.d.ts.map +1 -0
  27. package/dist/lib/utils/download.js +41 -0
  28. package/dist/lib/utils/download.js.map +1 -0
  29. package/dist/lib/utils/file-scanner.d.ts +0 -1
  30. package/dist/lib/utils/file-scanner.d.ts.map +1 -1
  31. package/dist/lib/utils/file-scanner.js +1 -16
  32. package/dist/lib/utils/file-scanner.js.map +1 -1
  33. package/dist/lib/utils/package-manager.d.ts +7 -0
  34. package/dist/lib/utils/package-manager.d.ts.map +1 -1
  35. package/dist/lib/utils/package-manager.js +15 -1
  36. package/dist/lib/utils/package-manager.js.map +1 -1
  37. package/package.json +3 -3
  38. package/src/commands/add.ts +2 -1
  39. package/src/commands/create.ts +5 -3
  40. package/src/commands/init.ts +5 -2
  41. package/src/commands/mcp.ts +223 -56
  42. package/src/lib/agent-skill.ts +17 -43
  43. package/src/lib/create-project.test.ts +0 -17
  44. package/src/lib/create-project.ts +33 -98
  45. package/src/lib/transform-files.test.ts +1 -3
  46. package/src/lib/transform.ts +6 -20
  47. package/src/lib/utils/download.ts +57 -0
  48. package/src/lib/utils/file-scanner.ts +0 -26
  49. package/src/lib/utils/package-manager.ts +26 -0
  50. package/src/lib/utils/registry.test.ts +0 -6
  51. package/templates/cloud-harness/app/api/chat/route.ts +1 -0
  52. package/templates/cloud-harness/components/assistant-ui/elements/collapsible-root.tsx +19 -0
  53. package/templates/cloud-harness/components/assistant-ui/elements/markdown-text.tsx +1 -1
  54. package/templates/cloud-harness/components/assistant-ui/elements/tool-fallback.aui.tsx +198 -30
  55. package/templates/cloud-harness/package.json +12 -12
@@ -1,13 +1,8 @@
1
1
  import fs from "node:fs";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
- import { downloadTemplate } from "giget";
5
- import {
6
- DOWNLOAD_TIMEOUT_MS,
7
- dlxCommand,
8
- resolveGitHubAuthToken,
9
- } from "./create-project";
10
- import { type PackageManagerName } from "./utils/package-manager";
4
+ import { withStagedDownload } from "./utils/download";
5
+ import { dlxCommand, type PackageManagerName } from "./utils/package-manager";
11
6
 
12
7
  export const SKILLS_PACKAGE = "assistant-ui/skills";
13
8
  export const SKILLS_COMMIT = "139674dc888ee076982b6726e8e6f5d0fe0b5f67";
@@ -70,42 +65,21 @@ export async function ensureSkillsPlugin(): Promise<string> {
70
65
  process.once("SIGINT", removeStagingOnSignal);
71
66
  process.once("SIGTERM", removeStagingOnSignal);
72
67
 
73
- // giget logs to console.debug whenever DEBUG is set, which the `debug`
74
- // package does at module load for an unrelated namespace.
75
- const origDebug = process.env.DEBUG;
76
- delete process.env.DEBUG;
77
- let timer: ReturnType<typeof setTimeout> | undefined;
78
- let downloadSettled = false;
79
- let download: Promise<unknown> | undefined;
80
- try {
81
- const authToken = resolveGitHubAuthToken();
82
- download = downloadTemplate(SKILLS_PLUGIN_SOURCE, {
83
- dir: staging,
84
- preferOffline: true,
85
- silent: true,
86
- ...(authToken ? { auth: authToken } : {}),
87
- }).finally(() => {
88
- downloadSettled = true;
89
- });
90
- const timeout = new Promise<never>((_, reject) => {
91
- timer = setTimeout(
92
- () => reject(new Error("Download timed out.")),
93
- DOWNLOAD_TIMEOUT_MS,
94
- );
95
- });
96
- await Promise.race([download, timeout]).catch((error: unknown) => {
97
- throw new Error(
98
- "Could not fetch the assistant-ui skills from GitHub. Check your network, or set GITHUB_TOKEN if you are rate limited.",
99
- { cause: error },
100
- );
101
- });
102
- await publish(staging, dir, marker);
103
- } finally {
104
- clearTimeout(timer);
105
- if (origDebug !== undefined) process.env.DEBUG = origDebug;
106
- if (!download || downloadSettled) removeStaging();
107
- else void download.then(removeStaging, removeStaging);
108
- }
68
+ await withStagedDownload(
69
+ SKILLS_PLUGIN_SOURCE,
70
+ { dir: staging, preferOffline: true, silent: true },
71
+ "Download timed out.",
72
+ removeStaging,
73
+ async (download) => {
74
+ await download.catch((error: unknown) => {
75
+ throw new Error(
76
+ "Could not fetch the assistant-ui skills from GitHub. Check your network, or set GITHUB_TOKEN if you are rate limited.",
77
+ { cause: error },
78
+ );
79
+ });
80
+ await publish(staging, dir, marker);
81
+ },
82
+ );
109
83
 
110
84
  return dir;
111
85
  }
@@ -24,23 +24,6 @@ describe("reconcileAssistantUIImportLayout", () => {
24
24
  const read = (file: string) =>
25
25
  fs.readFileSync(path.join(projectDir, file), "utf8");
26
26
 
27
- it("rewrites a legacy import when only the elements layout exists", async () => {
28
- write(
29
- "app/page.tsx",
30
- 'import { Thread } from "@/components/assistant-ui/thread";\n' +
31
- 'import { ThreadList } from "@/components/assistant-ui/thread-list";\n',
32
- );
33
- write("components/assistant-ui/elements/thread.aui.tsx", "export {};");
34
- write("components/assistant-ui/elements/thread-list.aui.tsx", "export {};");
35
-
36
- await reconcileAssistantUIImportLayout(projectDir);
37
-
38
- expect(read("app/page.tsx")).toBe(
39
- 'import { Thread } from "@/components/assistant-ui/elements/thread.aui";\n' +
40
- 'import { ThreadList } from "@/components/assistant-ui/elements/thread-list.aui";\n',
41
- );
42
- });
43
-
44
27
  it("rewrites module declarations without changing import-like source text", async () => {
45
28
  write(
46
29
  "app/page.tsx",
@@ -1,7 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as os from "node:os";
3
3
  import * as path from "node:path";
4
- import { downloadTemplate } from "giget";
5
4
  import {
6
5
  parse as parseJsonc,
7
6
  printParseErrorCode,
@@ -9,26 +8,14 @@ import {
9
8
  } from "jsonc-parser";
10
9
  import { logger } from "./utils/logger";
11
10
  import { runSpawn, SpawnExitError, SpawnSignalError } from "./run-spawn";
12
- import { type PackageManagerName } from "./utils/package-manager";
11
+ import { dlxCommand, type PackageManagerName } from "./utils/package-manager";
12
+ import { resolveGitHubAuthToken, withStagedDownload } from "./utils/download";
13
13
  import { readProjectFiles } from "./utils/file-scanner";
14
14
  import {
15
15
  detectRegistryPlatform,
16
16
  resolveRegistryItemUrl,
17
17
  } from "./utils/registry";
18
18
 
19
- export function dlxCommand(pm: PackageManagerName): [string, string[]] {
20
- switch (pm) {
21
- case "pnpm":
22
- return ["pnpm", ["dlx"]];
23
- case "yarn":
24
- return ["yarn", ["dlx"]];
25
- case "bun":
26
- return ["bunx", []];
27
- case "npm":
28
- return ["npx", ["--yes"]];
29
- }
30
- }
31
-
32
19
  export interface TransformOptions {
33
20
  hasLocalComponents: boolean;
34
21
  skipInstall?: boolean;
@@ -56,26 +43,6 @@ const LOCAL_PROJECT_ARTIFACT_GLOB_IGNORES = LOCAL_PROJECT_ARTIFACT_DIRS.map(
56
43
  (dir) => `**/${dir}/**`,
57
44
  );
58
45
 
59
- export function resolvePackageManager(opts: {
60
- useNpm?: boolean;
61
- usePnpm?: boolean;
62
- useYarn?: boolean;
63
- useBun?: boolean;
64
- }): PackageManagerName | undefined {
65
- if (opts.useNpm) return "npm";
66
- if (opts.usePnpm) return "pnpm";
67
- if (opts.useYarn) return "yarn";
68
- if (opts.useBun) return "bun";
69
- return undefined;
70
- }
71
-
72
- export function resolveGitHubAuthToken(): string | undefined {
73
- const token =
74
- process.env.GIGET_AUTH ?? process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;
75
- const trimmed = token?.trim();
76
- return trimmed || undefined;
77
- }
78
-
79
46
  function toBearerAuthHeader(token: string): string {
80
47
  return token.toLowerCase().startsWith("bearer ") ? token : `Bearer ${token}`;
81
48
  }
@@ -97,7 +64,6 @@ export async function resolveLatestReleaseRef(): Promise<string | undefined> {
97
64
  }
98
65
  }
99
66
 
100
- export const DOWNLOAD_TIMEOUT_MS = 30_000;
101
67
  const pendingDownloadCleanups = new Set<() => void>();
102
68
 
103
69
  export function cleanupPendingProjectDownloads(): void {
@@ -117,16 +83,9 @@ export async function downloadProject(
117
83
  ? `gh:assistant-ui/assistant-ui/${repoPath}#${ref}`
118
84
  : `gh:assistant-ui/assistant-ui/${repoPath}`;
119
85
 
120
- // Suppress giget's debug output. The `debug` package (used by the upgrade
121
- // command) sets process.env.DEBUG at module-load time, and giget logs to
122
- // console.debug whenever that env var is truthy — even for unrelated
123
- // namespaces. Temporarily unsetting it targets the root cause.
124
- const origDebug = process.env.DEBUG;
125
- delete process.env.DEBUG;
126
86
  let destinationCreated = false;
127
87
  let stagingDir: string | undefined;
128
- let downloadPromise: Promise<unknown> | undefined;
129
- let downloadFinished = false;
88
+ let downloadStarted = false;
130
89
  let downloadCommitted = false;
131
90
  let cleanupRequested = false;
132
91
  const removeCleanupListeners = () => {
@@ -183,62 +142,38 @@ export async function downloadProject(
183
142
  process.once("exit", cleanupOnExit);
184
143
  pendingDownloadCleanups.add(cleanupOnExit);
185
144
 
186
- const authToken = resolveGitHubAuthToken();
187
- downloadPromise = downloadTemplate(source, {
188
- dir: stagingDir,
189
- force: true,
190
- silent: true,
191
- ...(authToken ? { auth: authToken } : {}),
192
- }).finally(() => {
193
- downloadFinished = true;
194
- });
195
-
196
- let timer: ReturnType<typeof setTimeout>;
197
- const timeoutPromise = new Promise<never>((_, reject) => {
198
- timer = setTimeout(
199
- () =>
200
- reject(
201
- new Error(
202
- "Download timed out. This may be due to GitHub rate limiting or a network issue. Try again in a few minutes.",
203
- ),
204
- ),
205
- DOWNLOAD_TIMEOUT_MS,
206
- );
207
- });
208
-
209
- try {
210
- await Promise.race([downloadPromise, timeoutPromise]);
211
- if (cleanupRequested) throw new Error("Download was interrupted.");
212
- destinationCreated = !fs.existsSync(destDir);
213
- await fs.promises.mkdir(destDir, { recursive: true });
214
- for (const entry of await fs.promises.readdir(stagingDir)) {
215
- const target = path.join(destDir, entry);
216
- const sourceEntry = path.join(stagingDir, entry);
217
- await fs.promises.rm(target, { recursive: true, force: true });
218
- try {
219
- await fs.promises.rename(sourceEntry, target);
220
- } catch (error) {
221
- if ((error as NodeJS.ErrnoException).code !== "EXDEV") throw error;
222
- await fs.promises.cp(sourceEntry, target, {
223
- recursive: true,
224
- force: true,
225
- });
226
- await fs.promises.rm(sourceEntry, { recursive: true, force: true });
145
+ const currentStagingDir = stagingDir;
146
+ downloadStarted = true;
147
+ await withStagedDownload(
148
+ source,
149
+ { dir: currentStagingDir, force: true, silent: true },
150
+ "Download timed out. This may be due to GitHub rate limiting or a network issue. Try again in a few minutes.",
151
+ removeStagingDir,
152
+ async (download) => {
153
+ await download;
154
+ if (cleanupRequested) throw new Error("Download was interrupted.");
155
+ destinationCreated = !fs.existsSync(destDir);
156
+ await fs.promises.mkdir(destDir, { recursive: true });
157
+ for (const entry of await fs.promises.readdir(currentStagingDir)) {
158
+ const target = path.join(destDir, entry);
159
+ const sourceEntry = path.join(currentStagingDir, entry);
160
+ await fs.promises.rm(target, { recursive: true, force: true });
161
+ try {
162
+ await fs.promises.rename(sourceEntry, target);
163
+ } catch (error) {
164
+ if ((error as NodeJS.ErrnoException).code !== "EXDEV") throw error;
165
+ await fs.promises.cp(sourceEntry, target, {
166
+ recursive: true,
167
+ force: true,
168
+ });
169
+ await fs.promises.rm(sourceEntry, { recursive: true, force: true });
170
+ }
227
171
  }
228
- }
229
- downloadCommitted = true;
230
- } finally {
231
- clearTimeout(timer!);
232
- }
172
+ downloadCommitted = true;
173
+ },
174
+ );
233
175
  } finally {
234
- if (!downloadPromise || downloadFinished) {
235
- await removeStagingDir();
236
- } else {
237
- void downloadPromise.then(removeStagingDir, removeStagingDir);
238
- }
239
- if (origDebug !== undefined) {
240
- process.env.DEBUG = origDebug;
241
- }
176
+ if (!downloadStarted) await removeStagingDir();
242
177
  }
243
178
  }
244
179
 
@@ -2,7 +2,7 @@ import { describe, expect, it, onTestFinished } from "vitest";
2
2
  import { mkdirSync, mkdtempSync, writeFileSync, rmSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { dirname, join, relative } from "node:path";
5
- import { getRelevantFiles, countFilesToProcess } from "./transform";
5
+ import { getRelevantFiles } from "./transform";
6
6
 
7
7
  const fixture = () => {
8
8
  const directory = mkdtempSync(join(tmpdir(), "aui-transform-files-"));
@@ -26,7 +26,6 @@ describe("codemod file discovery", () => {
26
26
  const { write } = fixture();
27
27
  const file = write(`my app.${extension}`);
28
28
  expect(getRelevantFiles(file)).toEqual([file]);
29
- expect(countFilesToProcess(file)).toBe(1);
30
29
  expect(getRelevantFiles(relative(process.cwd(), file))).toEqual([file]);
31
30
  },
32
31
  );
@@ -54,7 +53,6 @@ describe("codemod file discovery", () => {
54
53
  const { write } = fixture();
55
54
  const file = write("app.tsx", source);
56
55
  expect(getRelevantFiles(file)).toEqual([file]);
57
- expect(countFilesToProcess(file)).toBe(1);
58
56
  });
59
57
 
60
58
  it("skips unsupported explicit files", () => {
@@ -3,8 +3,8 @@ import path from "node:path";
3
3
  import type { TransformOptions } from "./transform-options";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import * as fs from "node:fs";
6
- import { sync as globSync } from "glob";
7
6
  import { runSpawnCapture, SpawnExitError, SpawnSignalError } from "./run-spawn";
7
+ import { readProjectFiles } from "./utils/file-scanner";
8
8
 
9
9
  const log = debug("codemod:transform");
10
10
 
@@ -22,7 +22,8 @@ export function getRelevantFiles(source: string): string[] {
22
22
  }
23
23
 
24
24
  const pattern = "**/*.{js,jsx,ts,tsx}";
25
- const files = globSync(pattern, {
25
+ const relevantFiles: string[] = [];
26
+ for (const { fullPath, content } of readProjectFiles(pattern, {
26
27
  cwd: target,
27
28
  ignore: [
28
29
  "**/node_modules/**",
@@ -31,27 +32,12 @@ export function getRelevantFiles(source: string): string[] {
31
32
  "**/*.min.js",
32
33
  "**/*.bundle.js",
33
34
  ],
34
- }).map((file) => path.join(target, file));
35
-
36
- const relevantFiles = files.filter((file) => {
37
- try {
38
- const content = fs.readFileSync(file, "utf8");
39
- return content.includes("assistant-ui");
40
- } catch {
41
- return false;
42
- }
43
- });
44
-
35
+ })) {
36
+ if (content.includes("assistant-ui")) relevantFiles.push(fullPath);
37
+ }
45
38
  return relevantFiles;
46
39
  }
47
40
 
48
- /**
49
- * Counts the number of files that need to be processed
50
- */
51
- export function countFilesToProcess(source: string): number {
52
- return getRelevantFiles(source).length;
53
- }
54
-
55
41
  function buildCommand(
56
42
  codemodPath: string,
57
43
  targetFiles: string[],
@@ -0,0 +1,57 @@
1
+ import { downloadTemplate } from "giget";
2
+
3
+ export const DOWNLOAD_TIMEOUT_MS = 30_000;
4
+
5
+ export function resolveGitHubAuthToken(): string | undefined {
6
+ const token =
7
+ process.env.GIGET_AUTH ?? process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;
8
+ const trimmed = token?.trim();
9
+ return trimmed || undefined;
10
+ }
11
+
12
+ export async function withStagedDownload<T>(
13
+ source: string,
14
+ options: NonNullable<Parameters<typeof downloadTemplate>[1]>,
15
+ timeoutMessage: string,
16
+ cleanup: () => void | Promise<void>,
17
+ consumeDownload: (download: Promise<unknown>) => Promise<T>,
18
+ ): Promise<T> {
19
+ // giget logs to console.debug whenever DEBUG is set, which the `debug`
20
+ // package does at module load for an unrelated namespace.
21
+ const origDebug = process.env.DEBUG;
22
+ delete process.env.DEBUG;
23
+ let download: Promise<unknown> | undefined;
24
+ let settled = false;
25
+ try {
26
+ const authToken = resolveGitHubAuthToken();
27
+ download = downloadTemplate(source, {
28
+ ...options,
29
+ ...(authToken ? { auth: authToken } : {}),
30
+ }).finally(() => {
31
+ settled = true;
32
+ });
33
+
34
+ let timer: ReturnType<typeof setTimeout>;
35
+ const timeout = new Promise<never>((_, reject) => {
36
+ timer = setTimeout(
37
+ () => reject(new Error(timeoutMessage)),
38
+ DOWNLOAD_TIMEOUT_MS,
39
+ );
40
+ });
41
+ try {
42
+ return await consumeDownload(Promise.race([download, timeout]));
43
+ } finally {
44
+ clearTimeout(timer!);
45
+ }
46
+ } finally {
47
+ try {
48
+ if (!download || settled) {
49
+ await cleanup();
50
+ } else {
51
+ void download.then(cleanup, cleanup);
52
+ }
53
+ } finally {
54
+ if (origDebug !== undefined) process.env.DEBUG = origDebug;
55
+ }
56
+ }
57
+ }
@@ -49,29 +49,3 @@ export function scanForImport(
49
49
 
50
50
  return false;
51
51
  }
52
-
53
- export function getFilesContaining(
54
- searchString: string,
55
- options: ScanOptions = {},
56
- ): string[] {
57
- const cwd = options.cwd || process.cwd();
58
- const pattern = options.pattern || "**/*.{js,jsx,ts,tsx}";
59
- const ignore = options.ignore || [
60
- "**/node_modules/**",
61
- "**/dist/**",
62
- "**/build/**",
63
- ];
64
-
65
- const result: string[] = [];
66
-
67
- for (const { fullPath, content } of readProjectFiles(pattern, {
68
- cwd,
69
- ignore,
70
- })) {
71
- if (content.includes(searchString)) {
72
- result.push(fullPath);
73
- }
74
- }
75
-
76
- return result;
77
- }
@@ -7,6 +7,32 @@ import { runSpawn, SpawnSignalError } from "../run-spawn";
7
7
 
8
8
  export type PackageManagerName = "npm" | "pnpm" | "yarn" | "bun";
9
9
 
10
+ export function dlxCommand(pm: PackageManagerName): [string, string[]] {
11
+ switch (pm) {
12
+ case "pnpm":
13
+ return ["pnpm", ["dlx"]];
14
+ case "yarn":
15
+ return ["yarn", ["dlx"]];
16
+ case "bun":
17
+ return ["bunx", []];
18
+ case "npm":
19
+ return ["npx", ["--yes"]];
20
+ }
21
+ }
22
+
23
+ export function resolvePackageManager(opts: {
24
+ useNpm?: boolean;
25
+ usePnpm?: boolean;
26
+ useYarn?: boolean;
27
+ useBun?: boolean;
28
+ }): PackageManagerName | undefined {
29
+ if (opts.useNpm) return "npm";
30
+ if (opts.usePnpm) return "pnpm";
31
+ if (opts.useYarn) return "yarn";
32
+ if (opts.useBun) return "bun";
33
+ return undefined;
34
+ }
35
+
10
36
  export function askQuestion(query: string): Promise<string> {
11
37
  return new Promise((resolve) => {
12
38
  // A stream only reaches EOF once, so a run that already consumed stdin gets
@@ -144,12 +144,6 @@ describe("resolveRegistryItemUrl", () => {
144
144
  );
145
145
  });
146
146
 
147
- it("uses the base URL for an explicit undefined style", () => {
148
- expect(resolveRegistryItemUrl("thread", undefined)).toBe(
149
- "https://r.assistant-ui.com/base/thread.json",
150
- );
151
- });
152
-
153
147
  it("keeps the style inside a single path segment", () => {
154
148
  expect(resolveRegistryItemUrl("thread", "base-nova?preview=1")).toBe(
155
149
  "https://r.assistant-ui.com/styles/base-nova%3Fpreview%3D1/thread.json",
@@ -6,6 +6,7 @@ export const maxDuration = 30;
6
6
  export const POST = async (request: Request) => {
7
7
  const { messages } = (await request.json()) as { messages: UIMessage[] };
8
8
  const result = streamText({
9
+ abortSignal: request.signal,
9
10
  model: openai.responses("gpt-6-luna"),
10
11
  messages: await convertToModelMessages(messages),
11
12
  });
@@ -0,0 +1,19 @@
1
+ "use client";
2
+
3
+ import { Collapsible } from "@base-ui/react/collapsible";
4
+ import { forwardRef } from "react";
5
+
6
+ export const CollapsibleRoot = forwardRef<
7
+ React.ComponentRef<typeof Collapsible.Root>,
8
+ React.ComponentPropsWithoutRef<typeof Collapsible.Root>
9
+ >(function CollapsibleRoot(props, ref) {
10
+ return <Collapsible.Root ref={ref} data-slot="collapsible" {...props} />;
11
+ });
12
+
13
+ export function CollapsibleTrigger({ ...props }: Collapsible.Trigger.Props) {
14
+ return <Collapsible.Trigger data-slot="collapsible-trigger" {...props} />;
15
+ }
16
+
17
+ export function CollapsibleContent({ ...props }: Collapsible.Panel.Props) {
18
+ return <Collapsible.Panel data-slot="collapsible-content" {...props} />;
19
+ }
@@ -5,7 +5,7 @@ import "@assistant-ui/react-markdown/styles/dot.css";
5
5
  import {
6
6
  type CodeHeaderProps,
7
7
  MarkdownTextPrimitive,
8
- unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
8
+ memoizeMarkdownComponents,
9
9
  useIsMarkdownCodeBlock,
10
10
  } from "@assistant-ui/react-markdown";
11
11
  import remarkGfm from "remark-gfm";