aidos-ui 4.0.1 → 4.0.3

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 (47) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/dist/BaseButton.js +1 -1
  3. package/dist/BaseButton.js.map +1 -1
  4. package/dist/Card.js +1 -1
  5. package/dist/Card.js.map +1 -1
  6. package/dist/Dialog.js +7 -1
  7. package/dist/Dialog.js.map +1 -1
  8. package/dist/Dropdown.d.ts +1 -1
  9. package/dist/Dropdown.js +5 -4
  10. package/dist/Dropdown.js.map +1 -1
  11. package/dist/JSSComment.d.ts +10 -0
  12. package/dist/JSSComment.js +52 -0
  13. package/dist/JSSComment.js.map +1 -0
  14. package/dist/Popover.js +3 -1
  15. package/dist/Popover.js.map +1 -1
  16. package/dist/Storage.js +1 -3
  17. package/dist/Storage.js.map +1 -1
  18. package/dist/Tooltip.js +3 -2
  19. package/dist/Tooltip.js.map +1 -1
  20. package/dist/jss.d.ts +6 -1
  21. package/dist/jss.js +66 -11
  22. package/dist/jss.js.map +1 -1
  23. package/dist/jsx-dev-runtime.d.ts +2 -0
  24. package/dist/jsx-dev-runtime.js +48 -0
  25. package/dist/jsx-dev-runtime.js.map +1 -0
  26. package/dist/jsx-runtime.d.ts +1 -0
  27. package/dist/jsx-runtime.js +3 -0
  28. package/dist/jsx-runtime.js.map +1 -0
  29. package/dist/vite-plugin-jss-debug.d.ts +33 -0
  30. package/dist/vite-plugin-jss-debug.js +74 -0
  31. package/dist/vite-plugin-jss-debug.js.map +1 -0
  32. package/jss-debug-loader.mjs +44 -0
  33. package/next.config.js +12 -1
  34. package/package.json +19 -1
  35. package/src/BaseButton.tsx +1 -0
  36. package/src/Card.tsx +1 -1
  37. package/src/Dialog.tsx +7 -1
  38. package/src/Dropdown.tsx +5 -4
  39. package/src/JSSComment.tsx +56 -0
  40. package/src/Popover.tsx +3 -1
  41. package/src/Storage.tsx +1 -3
  42. package/src/Tooltip.tsx +3 -2
  43. package/src/jss.tsx +79 -14
  44. package/src/jsx-dev-runtime.ts +74 -0
  45. package/src/jsx-runtime.ts +2 -0
  46. package/src/vite-plugin-jss-debug.ts +75 -0
  47. package/tsconfig.json +6 -0
@@ -0,0 +1,74 @@
1
+ import path from "path";
2
+ /**
3
+ * Vite plugin that enables JSS debug tooling:
4
+ *
5
+ * 1. Injects component names into toClassnames() calls so they appear as CSS classes.
6
+ * toClassnames(jss) → toClassnames(jss, "Card")
7
+ *
8
+ * 2. Configures the custom JSX dev runtime that injects <!-- ComponentName -->
9
+ * HTML comments at component boundaries (same approach as the Next.js setup).
10
+ *
11
+ * Usage in vite.config.ts:
12
+ * import jssDebug from "aidos-ui/vite-plugin-jss-debug";
13
+ * export default defineConfig({ plugins: [jssDebug()] });
14
+ */
15
+ export default function jssDebug() {
16
+ return [
17
+ {
18
+ name: "jss-debug-jsx",
19
+ config() {
20
+ return {
21
+ esbuild: {
22
+ jsx: "automatic",
23
+ jsxDev: true,
24
+ jsxImportSource: "aidos-ui",
25
+ },
26
+ };
27
+ },
28
+ },
29
+ {
30
+ name: "jss-debug-classnames",
31
+ enforce: "pre",
32
+ transform(code, id) {
33
+ if (id.includes("node_modules"))
34
+ return;
35
+ if (!id.match(/\.[jt]sx?$/))
36
+ return;
37
+ if (!code.includes("toClassnames("))
38
+ return;
39
+ const filename = path.basename(id, path.extname(id));
40
+ const name = JSON.stringify(filename);
41
+ const needle = "toClassnames(";
42
+ let result = "";
43
+ let i = 0;
44
+ while (i < code.length) {
45
+ const idx = code.indexOf(needle, i);
46
+ if (idx === -1) {
47
+ result += code.slice(i);
48
+ break;
49
+ }
50
+ // Copy everything up to and including "toClassnames("
51
+ result += code.slice(i, idx + needle.length);
52
+ i = idx + needle.length;
53
+ // Track parens to find the matching closing )
54
+ let depth = 1;
55
+ let j = i;
56
+ while (j < code.length && depth > 0) {
57
+ const ch = code[j];
58
+ if (ch === "(")
59
+ depth++;
60
+ else if (ch === ")")
61
+ depth--;
62
+ if (depth > 0)
63
+ j++;
64
+ }
65
+ // Copy the original arguments, then inject the component name
66
+ result += code.slice(i, j) + ", " + name;
67
+ i = j;
68
+ }
69
+ return { code: result, map: null };
70
+ },
71
+ },
72
+ ];
73
+ }
74
+ //# sourceMappingURL=vite-plugin-jss-debug.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-plugin-jss-debug.js","sourceRoot":"","sources":["../src/vite-plugin-jss-debug.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,OAAO,UAAU,QAAQ;IAC9B,OAAO;QACL;YACE,IAAI,EAAE,eAAe;YACrB,MAAM;gBACJ,OAAO;oBACL,OAAO,EAAE;wBACP,GAAG,EAAE,WAAW;wBAChB,MAAM,EAAE,IAAI;wBACZ,eAAe,EAAE,UAAU;qBAC5B;iBACF,CAAC;YACJ,CAAC;SACF;QACD;YACE,IAAI,EAAE,sBAAsB;YAC5B,OAAO,EAAE,KAAc;YAEvB,SAAS,CAAC,IAAY,EAAE,EAAU;gBAChC,IAAI,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC;oBAAE,OAAO;gBACxC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC;oBAAE,OAAO;gBACpC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC;oBAAE,OAAO;gBAE5C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;gBACrD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;gBACtC,MAAM,MAAM,GAAG,eAAe,CAAC;gBAC/B,IAAI,MAAM,GAAG,EAAE,CAAC;gBAChB,IAAI,CAAC,GAAG,CAAC,CAAC;gBAEV,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;oBACvB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;oBACpC,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;wBACf,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;wBACxB,MAAM;oBACR,CAAC;oBAED,sDAAsD;oBACtD,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;oBAC7C,CAAC,GAAG,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC;oBAExB,8CAA8C;oBAC9C,IAAI,KAAK,GAAG,CAAC,CAAC;oBACd,IAAI,CAAC,GAAG,CAAC,CAAC;oBACV,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;wBACpC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;wBACnB,IAAI,EAAE,KAAK,GAAG;4BAAE,KAAK,EAAE,CAAC;6BACnB,IAAI,EAAE,KAAK,GAAG;4BAAE,KAAK,EAAE,CAAC;wBAC7B,IAAI,KAAK,GAAG,CAAC;4BAAE,CAAC,EAAE,CAAC;oBACrB,CAAC;oBAED,8DAA8D;oBAC9D,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;oBACzC,CAAC,GAAG,CAAC,CAAC;gBACR,CAAC;gBAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;YACrC,CAAC;SACF;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,44 @@
1
+ import path from "path";
2
+
3
+ /**
4
+ * Webpack/Turbopack loader that injects component names into toClassnames() calls.
5
+ *
6
+ * Rewrites:
7
+ * toClassnames(jss) → toClassnames(jss, "Card")
8
+ *
9
+ * The component name is inferred from the filename (Card.tsx → "Card").
10
+ */
11
+ export default function jssDebugLoader(source) {
12
+ if (!source.includes("toClassnames(")) return source;
13
+
14
+ const filename = path.basename(this.resourcePath, path.extname(this.resourcePath));
15
+ const name = JSON.stringify(filename);
16
+ const needle = "toClassnames(";
17
+ let result = "";
18
+ let i = 0;
19
+
20
+ while (i < source.length) {
21
+ const idx = source.indexOf(needle, i);
22
+ if (idx === -1) {
23
+ result += source.slice(i);
24
+ break;
25
+ }
26
+
27
+ result += source.slice(i, idx + needle.length);
28
+ i = idx + needle.length;
29
+
30
+ let depth = 1;
31
+ let j = i;
32
+ while (j < source.length && depth > 0) {
33
+ const ch = source[j];
34
+ if (ch === "(") depth++;
35
+ else if (ch === ")") depth--;
36
+ if (depth > 0) j++;
37
+ }
38
+
39
+ result += source.slice(i, j) + ", " + name;
40
+ i = j;
41
+ }
42
+
43
+ return result;
44
+ }
package/next.config.js CHANGED
@@ -15,7 +15,18 @@ const withMDX = mdx({
15
15
  const nextConfig = {
16
16
  pageExtensions: ["ts", "tsx", "js", "jsx", "md", "mdx"],
17
17
  reactStrictMode: true,
18
- turbopack: {},
18
+ turbopack: {
19
+ rules: {
20
+ "*.tsx": {
21
+ loaders: [{ loader: "./jss-debug-loader.mjs" }],
22
+ as: "*.tsx",
23
+ },
24
+ },
25
+ resolveAlias: {
26
+ "aidos-ui-jsx/jsx-dev-runtime": "./src/jsx-dev-runtime.ts",
27
+ "aidos-ui-jsx/jsx-runtime": "./src/jsx-runtime.ts",
28
+ },
29
+ },
19
30
  };
20
31
 
21
32
  // Merge MDX config with Next.js config
package/package.json CHANGED
@@ -1,9 +1,27 @@
1
1
  {
2
2
  "name": "aidos-ui",
3
- "version": "4.0.1",
3
+ "version": "4.0.3",
4
4
  "description": "A simple UI framework",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ },
12
+ "./jsx-runtime": {
13
+ "types": "./dist/jsx-runtime.d.ts",
14
+ "default": "./dist/jsx-runtime.js"
15
+ },
16
+ "./jsx-dev-runtime": {
17
+ "types": "./dist/jsx-dev-runtime.d.ts",
18
+ "default": "./dist/jsx-dev-runtime.js"
19
+ },
20
+ "./vite-plugin-jss-debug": {
21
+ "types": "./dist/vite-plugin-jss-debug.d.ts",
22
+ "default": "./dist/vite-plugin-jss-debug.js"
23
+ }
24
+ },
7
25
  "scripts": {
8
26
  "embeddings": "node ./scripts/embeddings.js",
9
27
  "dev": "next dev",
@@ -32,6 +32,7 @@ export const BaseButton = React.forwardRef(
32
32
  ) => {
33
33
  return (
34
34
  <button
35
+ type="button"
35
36
  {...otherProps}
36
37
  aria-disabled={disabled ? true : undefined}
37
38
  ref={ref}
package/src/Card.tsx CHANGED
@@ -19,7 +19,7 @@ export function Card({
19
19
  {
20
20
  backgroundColor: cssVar("--overlay-background"),
21
21
  borderRadius: cssVar("--border-radius-l"),
22
- boxShadow: "1px 1px 4px 1px #e0e0e0",
22
+ boxShadow: cssVar("--shadow-sm"),
23
23
  },
24
24
  jss,
25
25
  ]}
package/src/Dialog.tsx CHANGED
@@ -11,8 +11,14 @@ import { toClassnames } from "./jss";
11
11
  const jsss = {
12
12
  dialog: {
13
13
  border: "1px solid var(--divider)",
14
- borderRadius: "var(--border-radius-m)",
14
+ borderRadius: "var(--border-radius-l)",
15
15
  padding: 0,
16
+ boxShadow: "var(--shadow-lg)",
17
+ animation: "dialogFadeIn 0.2s ease-out",
18
+ "::backdrop": {
19
+ backgroundColor: "rgba(0, 0, 0, 0.4)",
20
+ animation: "backdropFadeIn 0.2s ease-out",
21
+ },
16
22
  },
17
23
  root: {
18
24
  display: "grid",
package/src/Dropdown.tsx CHANGED
@@ -1,7 +1,6 @@
1
- import React, { useEffect, useRef, useState } from "react";
1
+ import React, { useEffect, useId, useRef, useState } from "react";
2
2
 
3
3
  import { BaseView } from "./BaseView";
4
- import { guid } from "./guid";
5
4
  import { Button } from "./Button";
6
5
  import { Column } from "./Column";
7
6
  import { useNavigation } from "./useNavigation";
@@ -25,12 +24,12 @@ const jsss: { [key: string]: JSS } = {
25
24
  left: 0,
26
25
  right: 0,
27
26
  borderRadius: "var(--border-radius-m)",
28
- boxShadow: "0px 1px 2px var(--divider)",
27
+ boxShadow: "var(--shadow-sm)",
29
28
  zIndex: 1,
30
29
  },
31
30
  };
32
31
  export function Dropdown<T extends string | number>({
33
- id = guid(),
32
+ id: propId,
34
33
  label,
35
34
  options,
36
35
  selection,
@@ -54,6 +53,8 @@ export function Dropdown<T extends string | number>({
54
53
  gap?: Gap;
55
54
  size?: Size;
56
55
  }) {
56
+ const hookId = useId();
57
+ const id = propId ?? hookId;
57
58
  const triggerRef = useRef(null);
58
59
  const [expanded, setExpanded] = useState(false);
59
60
  const dropdownRef = useNavigation({ autofocus: true });
@@ -0,0 +1,56 @@
1
+ import React, { useEffect, useRef } from "react";
2
+
3
+ /**
4
+ * Injects a real HTML comment node (<!-- ComponentName -->) into the DOM.
5
+ *
6
+ * Renders nothing (null). Uses the React fiber tree to locate the sibling
7
+ * component's first DOM node, then inserts a comment before it.
8
+ * Client-only — no SSR output, no hydration mismatch.
9
+ */
10
+ export function JSSComment({ name }: { name: string }) {
11
+ const fiberRef = useRef<any>(null);
12
+
13
+ // Capture our fiber during render
14
+ try {
15
+ const internals = (React as any)
16
+ .__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
17
+ fiberRef.current = internals?.ReactCurrentOwner?.current;
18
+ } catch {}
19
+
20
+ useEffect(() => {
21
+ const fiber = fiberRef.current;
22
+ if (!fiber) return;
23
+
24
+ // Our sibling in the Fragment is the actual component
25
+ const sibling = fiber.sibling;
26
+ if (!sibling) return;
27
+
28
+ // Walk down the sibling's fiber tree to find its first DOM node
29
+ const hostNode = findFirstDOMNode(sibling);
30
+ if (!hostNode?.parentNode) return;
31
+
32
+ const comment = document.createComment(` ${name} `);
33
+ hostNode.parentNode.insertBefore(comment, hostNode);
34
+
35
+ return () => {
36
+ comment.remove();
37
+ };
38
+ }, [name]);
39
+
40
+ return null;
41
+ }
42
+
43
+ function findFirstDOMNode(fiber: any): Node | null {
44
+ let current = fiber;
45
+ while (current) {
46
+ if (current.stateNode instanceof Node) {
47
+ return current.stateNode;
48
+ }
49
+ if (current.child) {
50
+ current = current.child;
51
+ continue;
52
+ }
53
+ break;
54
+ }
55
+ return null;
56
+ }
package/src/Popover.tsx CHANGED
@@ -10,9 +10,11 @@ const jsss: { [key: string]: JSS } = {
10
10
  padding: 0,
11
11
  zIndex: 1,
12
12
  backgroundColor: "var(--primary-background)",
13
- borderRadius: "var(--border-radius-m)",
13
+ borderRadius: "var(--border-radius-l)",
14
14
  border: "1px solid var(--divider)",
15
15
  overflow: "hidden",
16
+ boxShadow: "var(--shadow-lg)",
17
+ animation: "popoverFadeIn 0.15s ease-out",
16
18
  },
17
19
  };
18
20
 
package/src/Storage.tsx CHANGED
@@ -99,9 +99,7 @@ export function createStorage<State>({
99
99
  useEffect(() => {
100
100
  return subscribe((newState) => {
101
101
  const newLocalState = selector(newState);
102
- if (newLocalState != localState) {
103
- setLocalState(selector(newState));
104
- }
102
+ setLocalState((prev) => (newLocalState != prev ? newLocalState : prev));
105
103
  });
106
104
  }, []);
107
105
 
package/src/Tooltip.tsx CHANGED
@@ -10,11 +10,12 @@ const jsss: { [key: string]: JSS } = {
10
10
  top: "-100%",
11
11
  transform: "translateY(50%)",
12
12
  backgroundColor: cssVar("--primary-background"),
13
- padding: cssVar("--spacing-s"),
13
+ padding: `${cssVar("--spacing-s")} ${cssVar("--spacing-m")}`,
14
14
  borderRadius: cssVar("--border-radius-m"),
15
15
  border: `1px solid ${cssVar("--divider")}`,
16
- boxShadow: `0px 1px 2px ${cssVar("--divider")}`,
16
+ boxShadow: cssVar("--shadow-md"),
17
17
  overflow: "hidden",
18
+ animation: "popoverFadeIn 0.15s ease-out",
18
19
  },
19
20
  };
20
21
 
package/src/jss.tsx CHANGED
@@ -75,7 +75,7 @@ const aliases: {
75
75
  },
76
76
  };
77
77
 
78
- const rawHashMap = new Map<string, string>();
78
+ const rawHashMap = new Map<string, { selector: string; key: string }>();
79
79
 
80
80
  function createStyleNode(content: string) {
81
81
  if (isServer()) {
@@ -111,6 +111,9 @@ const pixelStyles = new Set([
111
111
  "left",
112
112
  "right",
113
113
  "outline-offset",
114
+ "gap",
115
+ "row-gap",
116
+ "column-gap",
114
117
  ]);
115
118
 
116
119
  const toPixelValue = (key: string, value): string => {
@@ -141,20 +144,25 @@ const flattenJSS = (jss: JSS): Styles => {
141
144
  if (!jss) {
142
145
  return {};
143
146
  } else if (Array.isArray(jss)) {
144
- // @ts-ignore
145
- return jss.reduce((acc: Styles, jss: JSS) => {
146
- return { ...acc, ...flattenJSS(jss) };
147
- }, {});
147
+ const result: Styles = {};
148
+ for (const item of jss) {
149
+ Object.assign(result, flattenJSS(item));
150
+ }
151
+ return result;
148
152
  } else {
149
153
  return jss;
150
154
  }
151
155
  };
152
156
 
153
- export const toClassnames = (jss: JSS): string => {
157
+ export const toClassnames = (jss: JSS, componentName?: string): string => {
154
158
  jss = flattenJSS(jss);
155
159
 
156
160
  const stylesStack = Object.entries(jss);
157
- const classNames = [];
161
+ const classNames: string[] = [];
162
+
163
+ if (componentName) {
164
+ classNames.push(componentName);
165
+ }
158
166
 
159
167
  while (stylesStack.length) {
160
168
  const [key, value] = stylesStack.pop();
@@ -164,17 +172,22 @@ export const toClassnames = (jss: JSS): string => {
164
172
  continue;
165
173
  }
166
174
 
167
- const rawHash = hash(key + JSON.stringify(value)).toString(32);
168
- const cachedSelector = rawHashMap.get(rawHash);
169
-
170
- if (cachedSelector != null) {
171
- classNames.push(cachedSelector);
175
+ const inputKey = key + JSON.stringify(value);
176
+ const rawHash = hash(inputKey).toString(32);
177
+ const cached = rawHashMap.get(rawHash);
178
+
179
+ if (cached != null) {
180
+ if (cached.key !== inputKey) {
181
+ console.warn(
182
+ `[jss] Hash collision detected: "${inputKey}" collides with "${cached.key}"`
183
+ );
184
+ }
185
+ classNames.push(cached.selector);
172
186
  continue;
173
187
  }
174
188
 
175
- // const selector = numberToBase(rawHashMap.size);
176
189
  const selector = `x${rawHash}`;
177
- rawHashMap.set(rawHash, selector);
190
+ rawHashMap.set(rawHash, { selector, key: inputKey });
178
191
  classNames.push(selector);
179
192
 
180
193
  if (typeof value === "number" || typeof value === "string") {
@@ -405,6 +418,13 @@ export type Theme = {
405
418
  ["--border-radius-m"]: string;
406
419
  ["--border-radius-l"]: string;
407
420
  ["--nav-bar-height"]: string;
421
+ /* Shadows */
422
+ ["--shadow-sm"]: string;
423
+ ["--shadow-md"]: string;
424
+ ["--shadow-lg"]: string;
425
+ /* Transitions */
426
+ ["--transition-fast"]: string;
427
+ ["--transition-normal"]: string;
408
428
  };
409
429
 
410
430
  export const lightTheme: Theme = {
@@ -454,6 +474,13 @@ export const lightTheme: Theme = {
454
474
  ["--border-radius-m"]: "4px",
455
475
  ["--border-radius-l"]: "8px",
456
476
  ["--nav-bar-height"]: "50px",
477
+ /* Shadows */
478
+ ["--shadow-sm"]: "0 1px 2px rgba(0, 0, 0, 0.05), 0 1px 3px rgba(0, 0, 0, 0.1)",
479
+ ["--shadow-md"]: "0 4px 6px rgba(0, 0, 0, 0.07), 0 2px 4px rgba(0, 0, 0, 0.05)",
480
+ ["--shadow-lg"]: "0 10px 25px rgba(0, 0, 0, 0.15), 0 4px 10px rgba(0, 0, 0, 0.1)",
481
+ /* Transitions */
482
+ ["--transition-fast"]: "0.15s ease",
483
+ ["--transition-normal"]: "0.2s ease",
457
484
  };
458
485
 
459
486
  export const darkTheme: Theme = {
@@ -503,6 +530,13 @@ export const darkTheme: Theme = {
503
530
  ["--border-radius-m"]: "4px",
504
531
  ["--border-radius-l"]: "8px",
505
532
  ["--nav-bar-height"]: "50px",
533
+ /* Shadows - stronger for dark mode to be visible */
534
+ ["--shadow-sm"]: "0 1px 2px rgba(0, 0, 0, 0.2), 0 1px 3px rgba(0, 0, 0, 0.3)",
535
+ ["--shadow-md"]: "0 4px 6px rgba(0, 0, 0, 0.25), 0 2px 4px rgba(0, 0, 0, 0.2)",
536
+ ["--shadow-lg"]: "0 10px 25px rgba(0, 0, 0, 0.35), 0 4px 10px rgba(0, 0, 0, 0.25)",
537
+ /* Transitions */
538
+ ["--transition-fast"]: "0.15s ease",
539
+ ["--transition-normal"]: "0.2s ease",
506
540
  };
507
541
 
508
542
  export const baseStyles = `
@@ -527,6 +561,37 @@ body {
527
561
  background-color: ${cssVar("--primary-background")};
528
562
  }
529
563
 
564
+ @keyframes dialogFadeIn {
565
+ from {
566
+ opacity: 0;
567
+ transform: scale(0.95) translateY(-10px);
568
+ }
569
+ to {
570
+ opacity: 1;
571
+ transform: scale(1) translateY(0);
572
+ }
573
+ }
574
+
575
+ @keyframes backdropFadeIn {
576
+ from {
577
+ opacity: 0;
578
+ }
579
+ to {
580
+ opacity: 1;
581
+ }
582
+ }
583
+
584
+ @keyframes popoverFadeIn {
585
+ from {
586
+ opacity: 0;
587
+ transform: scale(0.95);
588
+ }
589
+ to {
590
+ opacity: 1;
591
+ transform: scale(1);
592
+ }
593
+ }
594
+
530
595
  `;
531
596
 
532
597
  const backgroundStyles = {
@@ -0,0 +1,74 @@
1
+ // Custom JSX dev runtime that injects <!-- ComponentName --> comments
2
+ // into the DOM at component boundaries.
3
+ //
4
+ // Usage: in tsconfig.json, set "jsxImportSource": "aidos-ui/src"
5
+ // (or wherever this file is importable from)
6
+
7
+ export { Fragment } from "react/jsx-dev-runtime";
8
+ import { jsxDEV as _jsxDEV, Fragment } from "react/jsx-dev-runtime";
9
+ import { JSSComment } from "./JSSComment";
10
+
11
+ const wrapCache = new WeakMap<Function, Function>();
12
+
13
+ function getWrapped(type: Function): Function {
14
+ let wrapped = wrapCache.get(type);
15
+ if (wrapped) return wrapped;
16
+
17
+ const name = (type as any).displayName || type.name;
18
+
19
+ wrapped = function JSSBoundary(props: any) {
20
+ return _jsxDEV(
21
+ Fragment,
22
+ {
23
+ children: [
24
+ _jsxDEV(JSSComment, { name }, undefined, false, undefined, undefined),
25
+ _jsxDEV(type as any, props, undefined, false, undefined, undefined),
26
+ ],
27
+ },
28
+ undefined,
29
+ true,
30
+ undefined,
31
+ undefined
32
+ );
33
+ };
34
+
35
+ (wrapped as any).displayName = name;
36
+ wrapCache.set(type, wrapped);
37
+
38
+ return wrapped;
39
+ }
40
+
41
+ // Skip our own wrapper components by name
42
+ const SKIP_NAMES = new Set(["JSSComment", "JSSBoundary"]);
43
+
44
+ function isUserComponent(type: any, source: any): boolean {
45
+ if (typeof type !== "function") return false;
46
+ const name = type.displayName || type.name;
47
+ if (!name || !/^[A-Z]/.test(name)) return false;
48
+ if (SKIP_NAMES.has(name)) return false;
49
+ // Use the source fileName from the JSX transform to skip node_modules
50
+ if (source?.fileName?.includes("node_modules")) return false;
51
+ return true;
52
+ }
53
+
54
+ export function jsxDEV(
55
+ type: any,
56
+ props: any,
57
+ key: any,
58
+ isStaticChildren: boolean,
59
+ source: any,
60
+ self: any
61
+ ) {
62
+ if (isUserComponent(type, source)) {
63
+ return _jsxDEV(
64
+ getWrapped(type) as any,
65
+ props,
66
+ key,
67
+ isStaticChildren,
68
+ source,
69
+ self
70
+ );
71
+ }
72
+
73
+ return _jsxDEV(type, props, key, isStaticChildren, source, self);
74
+ }
@@ -0,0 +1,2 @@
1
+ // Production JSX runtime — straight passthrough, no component annotations.
2
+ export { jsx, jsxs, Fragment } from "react/jsx-runtime";
@@ -0,0 +1,75 @@
1
+ import path from "path";
2
+
3
+ /**
4
+ * Vite plugin that enables JSS debug tooling:
5
+ *
6
+ * 1. Injects component names into toClassnames() calls so they appear as CSS classes.
7
+ * toClassnames(jss) → toClassnames(jss, "Card")
8
+ *
9
+ * 2. Configures the custom JSX dev runtime that injects <!-- ComponentName -->
10
+ * HTML comments at component boundaries (same approach as the Next.js setup).
11
+ *
12
+ * Usage in vite.config.ts:
13
+ * import jssDebug from "aidos-ui/vite-plugin-jss-debug";
14
+ * export default defineConfig({ plugins: [jssDebug()] });
15
+ */
16
+ export default function jssDebug() {
17
+ return [
18
+ {
19
+ name: "jss-debug-jsx",
20
+ config() {
21
+ return {
22
+ esbuild: {
23
+ jsx: "automatic",
24
+ jsxDev: true,
25
+ jsxImportSource: "aidos-ui",
26
+ },
27
+ };
28
+ },
29
+ },
30
+ {
31
+ name: "jss-debug-classnames",
32
+ enforce: "pre" as const,
33
+
34
+ transform(code: string, id: string) {
35
+ if (id.includes("node_modules")) return;
36
+ if (!id.match(/\.[jt]sx?$/)) return;
37
+ if (!code.includes("toClassnames(")) return;
38
+
39
+ const filename = path.basename(id, path.extname(id));
40
+ const name = JSON.stringify(filename);
41
+ const needle = "toClassnames(";
42
+ let result = "";
43
+ let i = 0;
44
+
45
+ while (i < code.length) {
46
+ const idx = code.indexOf(needle, i);
47
+ if (idx === -1) {
48
+ result += code.slice(i);
49
+ break;
50
+ }
51
+
52
+ // Copy everything up to and including "toClassnames("
53
+ result += code.slice(i, idx + needle.length);
54
+ i = idx + needle.length;
55
+
56
+ // Track parens to find the matching closing )
57
+ let depth = 1;
58
+ let j = i;
59
+ while (j < code.length && depth > 0) {
60
+ const ch = code[j];
61
+ if (ch === "(") depth++;
62
+ else if (ch === ")") depth--;
63
+ if (depth > 0) j++;
64
+ }
65
+
66
+ // Copy the original arguments, then inject the component name
67
+ result += code.slice(i, j) + ", " + name;
68
+ i = j;
69
+ }
70
+
71
+ return { code: result, map: null };
72
+ },
73
+ },
74
+ ];
75
+ }
package/tsconfig.json CHANGED
@@ -11,6 +11,12 @@
11
11
  "sourceMap": true,
12
12
  "outDir": "dist/",
13
13
  "jsx": "react-jsx",
14
+ "jsxImportSource": "aidos-ui-jsx",
15
+ "baseUrl": ".",
16
+ "paths": {
17
+ "aidos-ui-jsx/jsx-dev-runtime": ["./src/jsx-dev-runtime"],
18
+ "aidos-ui-jsx/jsx-runtime": ["./src/jsx-runtime"]
19
+ },
14
20
  "moduleResolution": "node",
15
21
  "esModuleInterop": true,
16
22
  "declaration": true,