@sero-ai/ui 0.4.2 → 0.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.
@@ -7,13 +7,14 @@
7
7
  // any entrypoint no longer resolves. Run after `pnpm build`.
8
8
 
9
9
  import { createRequire } from "node:module";
10
- import { existsSync, readFileSync } from "node:fs";
10
+ import { existsSync, readFileSync, statSync } from "node:fs";
11
11
  import { fileURLToPath } from "node:url";
12
12
  import { dirname, resolve } from "node:path";
13
13
 
14
14
  const require = createRequire(import.meta.url);
15
15
  const here = dirname(fileURLToPath(import.meta.url));
16
16
  const dist = resolve(here, "..", "dist");
17
+ const packageRoot = resolve(here, "..");
17
18
 
18
19
  const fail = (msg) => {
19
20
  console.error(`✗ ${msg}`);
@@ -25,6 +26,105 @@ if (!existsSync(dist)) {
25
26
  process.exit(1);
26
27
  }
27
28
 
29
+ // The lightweight root must not re-export specialized dependency graphs.
30
+ const rootEntries = ["index.js", "index.cjs", "index.d.ts", "index.d.cts"];
31
+ const forbiddenRootImports = [
32
+ "@sero-ai/ui/ai-elements",
33
+ "@sero-ai/ui/model-selection",
34
+ "@sero-ai/ui/components/context-editor",
35
+ "components/ai-elements",
36
+ "components/model-selection",
37
+ "components/context-editor",
38
+ "mermaid",
39
+ "shiki",
40
+ "react-jsx-parser",
41
+ "streamdown",
42
+ "unified",
43
+ ];
44
+
45
+ const importPatterns = [
46
+ /(?:from\s+|import\s*)["'](\.[^"']+)["']/g,
47
+ /(?:require|import)\(\s*["'](\.[^"']+)["']\s*\)/g,
48
+ ];
49
+
50
+ function localImports(contents) {
51
+ return importPatterns.flatMap((pattern) =>
52
+ [...contents.matchAll(pattern)].map((match) => match[1]),
53
+ );
54
+ }
55
+
56
+ function emittedExtension(file) {
57
+ if (file.endsWith(".d.cts")) return ".d.cts";
58
+ if (file.endsWith(".d.ts")) return ".d.ts";
59
+ if (file.endsWith(".cjs")) return ".cjs";
60
+ return ".js";
61
+ }
62
+
63
+ function resolveLocalImport(fromFile, specifier) {
64
+ const base = resolve(dirname(fromFile), specifier);
65
+ const extension = emittedExtension(fromFile);
66
+ const candidates = [];
67
+
68
+ if (extension.startsWith(".d.") && specifier.endsWith(".js")) {
69
+ candidates.push(base.replace(/\.js$/, extension));
70
+ }
71
+ candidates.push(base, `${base}${extension}`, resolve(base, `index${extension}`));
72
+
73
+ return candidates.find(
74
+ (candidate) => existsSync(candidate) && statSync(candidate).isFile(),
75
+ );
76
+ }
77
+
78
+ function inspectRootGraph(entry) {
79
+ const pending = [resolve(dist, entry)];
80
+ const visited = new Set();
81
+
82
+ while (pending.length > 0) {
83
+ const file = pending.pop();
84
+ if (visited.has(file)) continue;
85
+ visited.add(file);
86
+
87
+ const contents = readFileSync(file, "utf8");
88
+ const relativeFile = file.slice(dist.length + 1);
89
+ const leak = forbiddenRootImports.find(
90
+ (dependency) =>
91
+ relativeFile.includes(dependency) || contents.includes(dependency),
92
+ );
93
+ if (leak) fail(`${relativeFile} exposes unrelated dependency ${leak}`);
94
+
95
+ for (const specifier of localImports(contents)) {
96
+ const dependency = resolveLocalImport(file, specifier);
97
+ if (dependency) pending.push(dependency);
98
+ else fail(`${relativeFile} has unresolved local import ${specifier}`);
99
+ }
100
+ }
101
+ }
102
+
103
+ for (const file of rootEntries) {
104
+ inspectRootGraph(file);
105
+ }
106
+
107
+ // Specialized components remain available through stable public subpaths.
108
+ const packageJson = JSON.parse(
109
+ readFileSync(resolve(packageRoot, "package.json"), "utf8"),
110
+ );
111
+ for (const [subpath, output] of [
112
+ ["./ai-elements/*", "components/ai-elements/message.js"],
113
+ ["./model-selection/*", "components/model-selection/available-model-picker.js"],
114
+ ]) {
115
+ if (!packageJson.publishConfig.exports[subpath]) {
116
+ fail(`publishConfig is missing ${subpath}`);
117
+ }
118
+ if (!existsSync(resolve(dist, output))) fail(`${output} not emitted to dist`);
119
+ }
120
+ for (const style of [
121
+ "styles/ai-elements.css",
122
+ "styles/model-selection.css",
123
+ "styles/context-editor.css",
124
+ ]) {
125
+ if (!existsSync(resolve(dist, style))) fail(`${style} not emitted to dist`);
126
+ }
127
+
28
128
  // 1. Reference entrypoint resolves as ESM and exposes the example widgets.
29
129
  const EXPECTED = [
30
130
  "StarterExample",
@@ -64,5 +164,7 @@ else {
64
164
  if (process.exitCode) {
65
165
  console.error("Smoke test failed.");
66
166
  } else {
67
- console.log("✓ dist entrypoints resolve (reference esm+cjs, catalog json)");
167
+ console.log(
168
+ "✓ dist entrypoints resolve and the root excludes specialized dependencies",
169
+ );
68
170
  }
package/src/index.ts CHANGED
@@ -1,12 +1,11 @@
1
1
  // @sero-ai/ui — Shared UI components, AI elements, and utilities
2
2
  //
3
- // Prefer importing common primitives from the barrel export:
3
+ // Import primitives, dashboard components, hooks, and utilities from the root:
4
4
  // import { Button, Card, Input, cn, useIsMobile } from "@sero-ai/ui"
5
5
  //
6
- // Deep subpath imports are still supported when needed. Because this package is
7
- // native ESM, consumers should use runtime .js extensions for deep imports:
8
- // import { Button } from "@sero-ai/ui/components/ui/button.js"
9
- // import { cn } from "@sero-ai/ui/lib/utils.js"
6
+ // AI elements have stable public subpaths so their editor and rendering
7
+ // dependencies are loaded only by consumers that use them:
8
+ // import { Message } from "@sero-ai/ui/ai-elements/message"
10
9
 
11
10
  // ── Utilities ──
12
11
  export { cn } from "./lib/utils";
@@ -77,60 +76,6 @@ export * from "./components/ui/toggle-group";
77
76
  export * from "./components/ui/toggle";
78
77
  export * from "./components/ui/tooltip";
79
78
  export * from "./components/ui/tree";
80
- // ── AI Elements ──
81
- export * from "./components/ai-elements/agent";
82
- export * from "./components/ai-elements/artifact";
83
- export * from "./components/ai-elements/attachments";
84
- export * from "./components/ai-elements/audio-player";
85
- export * from "./components/ai-elements/canvas";
86
- export * from "./components/ai-elements/chain-of-thought";
87
- export * from "./components/ai-elements/checkpoint";
88
- export * from "./components/ai-elements/code-block";
89
- export * from "./components/ai-elements/commit";
90
- export * from "./components/ai-elements/confirmation";
91
- export * from "./components/ai-elements/connection";
92
- export * from "./components/ai-elements/context";
93
- export * from "./components/ai-elements/controls";
94
- export * from "./components/ai-elements/conversation";
95
- export * from "./components/ai-elements/edge";
96
- export * from "./components/ai-elements/environment-variables";
97
- export * from "./components/ai-elements/file-tree";
98
- export * from "./components/ai-elements/image";
99
- export * from "./components/ai-elements/inline-citation";
100
- export * from "./components/ai-elements/jsx-preview";
101
- export * from "./components/ai-elements/message";
102
- export * from "./components/ai-elements/mic-selector";
103
- export * from "./components/ai-elements/model-selector";
104
- export * from "./components/ai-elements/node";
105
- export * from "./components/ai-elements/open-in-chat";
106
- export * from "./components/ai-elements/package-info";
107
- export * from "./components/ai-elements/panel";
108
- export * from "./components/ai-elements/persona";
109
- export * from "./components/ai-elements/plan";
110
- export * from "./components/ai-elements/prompt-input-context";
111
- export * from "./components/ai-elements/prompt-input-elements";
112
- export * from "./components/ai-elements/prompt-input-textarea";
113
- export * from "./components/ai-elements/prompt-input";
114
- export * from "./components/ai-elements/queue";
115
- export * from "./components/ai-elements/reasoning";
116
- export * from "./components/ai-elements/sandbox";
117
- export * from "./components/ai-elements/schema-display";
118
- export * from "./components/ai-elements/shimmer";
119
- export * from "./components/ai-elements/snippet";
120
- export * from "./components/ai-elements/sources";
121
- export * from "./components/ai-elements/speech-input";
122
- export * from "./components/ai-elements/stack-trace";
123
- export * from "./components/ai-elements/suggestion";
124
- export * from "./components/ai-elements/task";
125
- export * from "./components/ai-elements/terminal";
126
- export * from "./components/ai-elements/test-results";
127
- export * from "./components/ai-elements/tool";
128
- export * from "./components/ai-elements/toolbar";
129
- export * from "./components/ai-elements/transcription";
130
- export * from "./components/ai-elements/voice-selector-accent";
131
- export * from "./components/ai-elements/voice-selector";
132
- export * from "./components/ai-elements/web-preview";
133
-
134
79
  // ── Dashboard Components ──
135
80
  // Compact presentation components for dashboard widgets and full plugin views.
136
81
  // The discovery catalogue is plain data at "@sero-ai/ui/dashboard-catalog.json";
@@ -138,9 +83,5 @@ export * from "./components/ai-elements/web-preview";
138
83
  export * from "./components/dashboard";
139
84
  export type { DashboardComponentCatalogEntry } from "./components/dashboard/catalog";
140
85
 
141
- // ── Model Selection Components ──
142
- export * from "./components/model-selection/available-model-picker";
143
- export * from "./components/model-selection/model-warning-list";
144
- export * from "./components/model-selection/thinking-level-picker";
145
86
  export { PluginStyleScope, type PluginStyleScopeProps } from './plugin-style-scope';
146
87
  export { usePluginPortalContainer } from './plugin-style-scope-context';
@@ -0,0 +1,2 @@
1
+ /* Import after plugin.css when a federated plugin uses @sero-ai/ui/ai-elements/*. */
2
+ @source "../components/ai-elements";
@@ -0,0 +1,2 @@
1
+ /* Import after plugin.css when a federated plugin uses components/context-editor/*. */
2
+ @source "../components/context-editor";
@@ -0,0 +1,2 @@
1
+ /* Import after plugin.css when a federated plugin uses @sero-ai/ui/model-selection/*. */
2
+ @source "../components/model-selection";
@@ -3,7 +3,10 @@
3
3
  @import "shadcn/tailwind.css";
4
4
  @import "./plugin-theme.css";
5
5
 
6
- @source "../components";
6
+ /* Match the lightweight package root. Specialized components opt into their
7
+ own source files through a matching stylesheet under styles/. */
8
+ @source "../components/ui";
9
+ @source "../components/dashboard";
7
10
 
8
11
  @custom-variant dark (&:is(.dark *));
9
12