@usetheo/ui 0.12.0-next.0 → 0.13.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/CHANGELOG.md +111 -0
- package/README.md +20 -19
- package/dist/chunk-6ZQKEY54.js +149 -0
- package/dist/chunk-6ZQKEY54.js.map +1 -0
- package/dist/chunk-AVPHVQZS.js +73 -0
- package/dist/chunk-AVPHVQZS.js.map +1 -0
- package/dist/{chunk-ZNILW4G5.js → chunk-DW247T3Q.js} +2 -2
- package/dist/{chunk-ZNILW4G5.js.map → chunk-DW247T3Q.js.map} +1 -1
- package/dist/chunk-GXBFGWQN.js +81 -0
- package/dist/chunk-GXBFGWQN.js.map +1 -0
- package/dist/chunk-I32I36LW.js +113 -0
- package/dist/chunk-I32I36LW.js.map +1 -0
- package/dist/chunk-JPTPIZ5V.js +120 -0
- package/dist/chunk-JPTPIZ5V.js.map +1 -0
- package/dist/{chunk-TO3UAT6O.js → chunk-K7PYLTMP.js} +3 -3
- package/dist/{chunk-TO3UAT6O.js.map → chunk-K7PYLTMP.js.map} +1 -1
- package/dist/{chunk-R2PAGRDP.js → chunk-MYEHGDC2.js} +2 -2
- package/dist/{chunk-R2PAGRDP.js.map → chunk-MYEHGDC2.js.map} +1 -1
- package/dist/{chunk-IPEYGWA7.js → chunk-PTHRL242.js} +4 -4
- package/dist/{chunk-IPEYGWA7.js.map → chunk-PTHRL242.js.map} +1 -1
- package/dist/chunk-RC5XME4T.js +33 -0
- package/dist/chunk-RC5XME4T.js.map +1 -0
- package/dist/chunk-UK27KR35.js +73 -0
- package/dist/chunk-UK27KR35.js.map +1 -0
- package/dist/chunk-UOMQPIB4.js +48 -0
- package/dist/chunk-UOMQPIB4.js.map +1 -0
- package/dist/{chunk-TNBJ36XJ.js → chunk-XZKEGEPT.js} +4 -4
- package/dist/{chunk-TNBJ36XJ.js.map → chunk-XZKEGEPT.js.map} +1 -1
- package/dist/components.css +1 -1
- package/dist/composites/agent-editor/index.js +2 -2
- package/dist/composites/data-table/index.js +1 -1
- package/dist/composites/rule-editor/index.js +3 -3
- package/dist/composites/skill-editor/index.js +3 -3
- package/dist/composites/stability-bundle-viewer/index.js +4 -0
- package/dist/composites/stability-bundle-viewer/index.js.map +1 -0
- package/dist/index.d.ts +162 -1
- package/dist/index.js +44 -36
- package/dist/index.js.map +1 -1
- package/dist/preset-v3-legacy.js.map +1 -1
- package/dist/primitives/branch-indicator/index.js +4 -0
- package/dist/primitives/branch-indicator/index.js.map +1 -0
- package/dist/primitives/channel-card/index.js +4 -0
- package/dist/primitives/channel-card/index.js.map +1 -0
- package/dist/primitives/export-chat-dialog/index.js +4 -0
- package/dist/primitives/export-chat-dialog/index.js.map +1 -0
- package/dist/primitives/gateway-status-indicator/index.js +4 -0
- package/dist/primitives/gateway-status-indicator/index.js.map +1 -0
- package/dist/primitives/pin-input/index.js +1 -1
- package/dist/primitives/run-status-pill/index.js +4 -0
- package/dist/primitives/run-status-pill/index.js.map +1 -0
- package/dist/primitives/thinking-level-selector/index.js +4 -0
- package/dist/primitives/thinking-level-selector/index.js.map +1 -0
- package/dist/primitives/update-banner/index.js +4 -0
- package/dist/primitives/update-banner/index.js.map +1 -0
- package/package.json +126 -92
- package/registry/r/data-table.json +1 -1
- package/registry/r/pin-input.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/styles/tailwind-preset.ts","../src/preset-v3-legacy.ts"],"names":[],"mappings":"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","file":"preset-v3-legacy.js","sourcesContent":["/**\n * Theo UI Tailwind preset — Violet Forge identity.\n *\n * Single source of truth for the design system's utility-level tokens:\n * - colors mapped to CSS variables (HSL split via `hsl(var(--x) / <alpha>)`)\n * - Geist-inspired typescale (display / title / body / label / code tiers)\n * - radii, shadows, motion timing & duration, keyframes\n * - tailwindcss-animate plugin\n *\n * Consumed by:\n * - `tailwind.config.ts` (this repo) via `presets: [theoUIPreset]`\n * - registry/r/tailwind-preset.json (shipped to consumers via shadcn CLI)\n *\n * Consumers integrate as:\n *\n * import type { Config } from \"tailwindcss\";\n * import { theoUIPreset } from \"./styles/tailwind-preset\";\n *\n * export default {\n * darkMode: \"class\",\n * content: [\"./src/**\\/*.{ts,tsx}\"],\n * presets: [theoUIPreset],\n * } satisfies Config;\n *\n * Note: `darkMode` and `content` are NOT in the preset — they are consumer\n * decisions. The preset only contains `theme.extend.*` and `plugins`.\n */\n\nimport type { Config } from \"tailwindcss\";\nimport animate from \"tailwindcss-animate\";\n\nconst hsl = (token: string) => `hsl(var(${token}) / <alpha-value>)`;\n\nexport const theoUIPreset: Partial<Config> = {\n theme: {\n container: {\n center: true,\n padding: \"1rem\",\n screens: {\n \"2xl\": \"1280px\",\n },\n },\n extend: {\n colors: {\n background: hsl(\"--background\"),\n foreground: hsl(\"--foreground\"),\n card: {\n DEFAULT: hsl(\"--card\"),\n foreground: hsl(\"--card-foreground\"),\n },\n popover: {\n DEFAULT: hsl(\"--popover\"),\n foreground: hsl(\"--popover-foreground\"),\n },\n primary: {\n DEFAULT: hsl(\"--primary\"),\n deep: hsl(\"--primary-deep\"),\n glow: hsl(\"--primary-glow\"),\n foreground: hsl(\"--primary-foreground\"),\n },\n secondary: {\n DEFAULT: hsl(\"--secondary\"),\n foreground: hsl(\"--secondary-foreground\"),\n },\n accent: {\n DEFAULT: hsl(\"--accent\"),\n deep: hsl(\"--accent-deep\"),\n foreground: hsl(\"--accent-foreground\"),\n },\n muted: {\n DEFAULT: hsl(\"--muted\"),\n foreground: hsl(\"--muted-foreground\"),\n },\n success: {\n DEFAULT: hsl(\"--success\"),\n foreground: hsl(\"--success-foreground\"),\n },\n warning: {\n DEFAULT: hsl(\"--warning\"),\n foreground: hsl(\"--warning-foreground\"),\n },\n destructive: {\n DEFAULT: hsl(\"--destructive\"),\n foreground: hsl(\"--destructive-foreground\"),\n },\n info: {\n DEFAULT: hsl(\"--info\"),\n foreground: hsl(\"--info-foreground\"),\n },\n border: hsl(\"--border\"),\n input: hsl(\"--input\"),\n ring: hsl(\"--ring\"),\n },\n fontFamily: {\n display: \"var(--font-display)\",\n sans: \"var(--font-body)\",\n mono: \"var(--font-mono)\",\n },\n /* Geist-inspired Violet Forge typescale.\n *\n * Three strict weights: 400 (body), 500 (UI), 600 (display/headings).\n * Letter-spacing scales with size — aggressive negative on display.\n * Mirrors the Vercel/Geist vocabulary while keeping Theo's identity.\n */\n fontSize: {\n // Display tier — aggressive compression, content-led headlines\n \"display-2xl\": [\"64px\", { lineHeight: \"1\", letterSpacing: \"-0.0464em\", fontWeight: \"600\" }],\n \"display-xl\": [\"48px\", { lineHeight: \"1.05\", letterSpacing: \"-0.05em\", fontWeight: \"600\" }],\n \"display-lg\": [\"40px\", { lineHeight: \"1.1\", letterSpacing: \"-0.05em\", fontWeight: \"600\" }],\n \"display-md\": [\"32px\", { lineHeight: \"1.2\", letterSpacing: \"-0.04em\", fontWeight: \"600\" }],\n headline: [\"28px\", { lineHeight: \"1.25\", letterSpacing: \"-0.035em\", fontWeight: \"600\" }],\n // Title tier — section / card heads\n \"title-lg\": [\"24px\", { lineHeight: \"1.33\", letterSpacing: \"-0.04em\", fontWeight: \"600\" }],\n \"title-md\": [\"20px\", { lineHeight: \"1.4\", letterSpacing: \"-0.03em\", fontWeight: \"600\" }],\n // Body tier — FAANG-density realignment 2026-05-22: body-md is the\n // industry-standard 14px (shadcn / Vercel Geist / Linear / Stripe /\n // Mantine). The previous 15px was idiosyncratic. body-sm (14px label\n // weight) remains separate via its line-height / weight signature.\n \"body-lg\": [\"18px\", { lineHeight: \"1.56\", letterSpacing: \"-0.01em\", fontWeight: \"400\" }],\n \"body-md\": [\"14px\", { lineHeight: \"1.43\", letterSpacing: \"0\", fontWeight: \"400\" }],\n \"body-sm\": [\"13px\", { lineHeight: \"1.46\", fontWeight: \"400\" }],\n // Label tier — used on buttons, nav, secondary actions\n label: [\"14px\", { lineHeight: \"1.43\", fontWeight: \"500\" }],\n \"label-caps\": [\"12px\", { lineHeight: \"1.33\", letterSpacing: \"0.04em\", fontWeight: \"500\" }],\n // Mono — code surfaces, technical labels\n \"code-md\": [\"14px\", { lineHeight: \"1.5\", fontWeight: \"400\" }],\n \"code-sm\": [\"13px\", { lineHeight: \"1.54\", fontWeight: \"500\" }],\n },\n borderRadius: {\n none: \"var(--radius-none)\",\n sm: \"var(--radius-sm)\",\n md: \"var(--radius-md)\",\n lg: \"var(--radius-lg)\",\n xl: \"var(--radius-xl)\",\n \"2xl\": \"var(--radius-2xl)\",\n full: \"var(--radius-full)\",\n },\n boxShadow: {\n sm: \"var(--shadow-sm)\",\n md: \"var(--shadow-md)\",\n lg: \"var(--shadow-lg)\",\n glow: \"var(--shadow-glow)\",\n \"glow-strong\": \"var(--shadow-glow-strong)\",\n },\n transitionTimingFunction: {\n \"out-soft\": \"var(--ease-out-soft)\",\n snap: \"var(--ease-snap)\",\n },\n transitionDuration: {\n fast: \"var(--duration-fast)\",\n base: \"var(--duration-base)\",\n slow: \"var(--duration-slow)\",\n },\n keyframes: {\n \"fade-in-up\": {\n \"0%\": { opacity: \"0\", transform: \"translateY(8px)\" },\n \"100%\": { opacity: \"1\", transform: \"translateY(0)\" },\n },\n \"pulse-glow\": {\n \"0%, 100%\": { boxShadow: \"0 0 0 0 hsl(var(--primary) / 0.5)\" },\n \"50%\": { boxShadow: \"0 0 0 8px hsl(var(--primary) / 0)\" },\n },\n },\n animation: {\n \"fade-in-up\": \"fade-in-up var(--duration-base) var(--ease-out-soft) both\",\n \"pulse-glow\": \"pulse-glow 1.5s var(--ease-in-out) infinite\",\n },\n },\n },\n plugins: [animate],\n};\n","/**\n * `@usetheo/ui/preset-v3-legacy` — Tailwind v3 JS preset (legacy path).\n *\n * Default-export a `Partial<Config>` mirroring the design tokens shipped\n * in `@usetheo/ui/tokens.css`. Adds a `content` field covering the\n * library's published artifact tree so v3-based Tailwind builds emit\n * the utilities used by `@usetheo/ui` components.\n *\n * **Tailwind v4 consumers MUST use `@usetheo/ui/preset.css`**, not this\n * file — Tailwind v4 dropped the JS preset format entirely. This file\n * exists for any remaining `tailwindcss@^3` consumer (notably the\n * shadcn-style copy-paste registry path); new code should not import\n * from this subpath.\n *\n * The token surface is delegated to `./styles/tailwind-preset.ts` (the\n * existing source of truth used by the local Ladle dev surface and the\n * shadcn registry).\n *\n * v3 consumer usage:\n *\n * // tailwind.config.ts (Tailwind v3 only)\n * import preset from \"@usetheo/ui/preset-v3-legacy\";\n * export default {\n * presets: [preset],\n * content: [\"./app/**\\/*.{ts,tsx}\"],\n * };\n *\n * See RFC 0008 follow-up.\n */\nimport type { Config } from \"tailwindcss\";\nimport { theoUIPreset } from \"./styles/tailwind-preset.js\";\n\nconst LIBRARY_CONTENT_GLOBS: string[] = [\n // Resolved relative to the consumer's `tailwind.config.{ts,js}`.\n \"./node_modules/@usetheo/ui/dist/**/*.{js,mjs,cjs}\",\n // Yarn PnP / pnpm hoist fallback — Tailwind's globbing tolerates both.\n \"./node_modules/@usetheo/ui/dist/**/*.{ts,tsx}\",\n];\n\nconst preset: Partial<Config> = {\n ...theoUIPreset,\n content: LIBRARY_CONTENT_GLOBS,\n};\n\nexport default preset;\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/styles/tailwind-preset.ts","../src/preset-v3-legacy.ts"],"names":[],"mappings":"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","file":"preset-v3-legacy.js","sourcesContent":["/**\n * Theo UI Tailwind preset — Violet Forge identity.\n *\n * Single source of truth for the design system's utility-level tokens:\n * - colors mapped to CSS variables (HSL split via `hsl(var(--x) / <alpha>)`)\n * - Geist-inspired typescale (display / title / body / label / code tiers)\n * - radii, shadows, motion timing & duration, keyframes\n * - tailwindcss-animate plugin\n *\n * Consumed by:\n * - `tailwind.config.ts` (this repo) via `presets: [theoUIPreset]`\n * - registry/r/tailwind-preset.json (shipped to consumers via shadcn CLI)\n *\n * Consumers integrate as:\n *\n * import type { Config } from \"tailwindcss\";\n * import { theoUIPreset } from \"./styles/tailwind-preset\";\n *\n * export default {\n * darkMode: \"class\",\n * content: [\"./src/**\\/*.{ts,tsx}\"],\n * presets: [theoUIPreset],\n * } satisfies Config;\n *\n * Note: `darkMode` and `content` are NOT in the preset — they are consumer\n * decisions. The preset only contains `theme.extend.*` and `plugins`.\n */\n\nimport type { Config } from \"tailwindcss\";\nimport animate from \"tailwindcss-animate\";\n\nconst hsl = (token: string) => `hsl(var(${token}) / <alpha-value>)`;\n\nexport const theoUIPreset: Partial<Config> = {\n theme: {\n container: {\n center: true,\n padding: \"1rem\",\n screens: {\n \"2xl\": \"1280px\",\n },\n },\n extend: {\n colors: {\n background: hsl(\"--background\"),\n foreground: hsl(\"--foreground\"),\n card: {\n DEFAULT: hsl(\"--card\"),\n foreground: hsl(\"--card-foreground\"),\n },\n popover: {\n DEFAULT: hsl(\"--popover\"),\n foreground: hsl(\"--popover-foreground\"),\n },\n primary: {\n DEFAULT: hsl(\"--primary\"),\n deep: hsl(\"--primary-deep\"),\n glow: hsl(\"--primary-glow\"),\n foreground: hsl(\"--primary-foreground\"),\n },\n secondary: {\n DEFAULT: hsl(\"--secondary\"),\n foreground: hsl(\"--secondary-foreground\"),\n },\n accent: {\n DEFAULT: hsl(\"--accent\"),\n deep: hsl(\"--accent-deep\"),\n foreground: hsl(\"--accent-foreground\"),\n },\n muted: {\n DEFAULT: hsl(\"--muted\"),\n foreground: hsl(\"--muted-foreground\"),\n },\n success: {\n DEFAULT: hsl(\"--success\"),\n foreground: hsl(\"--success-foreground\"),\n },\n warning: {\n DEFAULT: hsl(\"--warning\"),\n foreground: hsl(\"--warning-foreground\"),\n },\n destructive: {\n DEFAULT: hsl(\"--destructive\"),\n foreground: hsl(\"--destructive-foreground\"),\n },\n info: {\n DEFAULT: hsl(\"--info\"),\n foreground: hsl(\"--info-foreground\"),\n },\n border: hsl(\"--border\"),\n input: hsl(\"--input\"),\n ring: hsl(\"--ring\"),\n },\n fontFamily: {\n display: \"var(--font-display)\",\n sans: \"var(--font-body)\",\n mono: \"var(--font-mono)\",\n },\n /* Geist-inspired Violet Forge typescale.\n *\n * Three strict weights: 400 (body), 500 (UI), 600 (display/headings).\n * Letter-spacing scales with size — aggressive negative on display.\n * Mirrors the Vercel/Geist vocabulary while keeping Theo's identity.\n */\n fontSize: {\n // Display tier — aggressive compression, content-led headlines\n \"display-2xl\": [\"64px\", { lineHeight: \"1\", letterSpacing: \"-0.0464em\", fontWeight: \"600\" }],\n \"display-xl\": [\"48px\", { lineHeight: \"1.05\", letterSpacing: \"-0.05em\", fontWeight: \"600\" }],\n \"display-lg\": [\"40px\", { lineHeight: \"1.1\", letterSpacing: \"-0.05em\", fontWeight: \"600\" }],\n \"display-md\": [\"32px\", { lineHeight: \"1.2\", letterSpacing: \"-0.04em\", fontWeight: \"600\" }],\n headline: [\"28px\", { lineHeight: \"1.25\", letterSpacing: \"-0.035em\", fontWeight: \"600\" }],\n // Title tier — section / card heads\n \"title-lg\": [\"24px\", { lineHeight: \"1.33\", letterSpacing: \"-0.04em\", fontWeight: \"600\" }],\n \"title-md\": [\"20px\", { lineHeight: \"1.4\", letterSpacing: \"-0.03em\", fontWeight: \"600\" }],\n // Body tier — FAANG-density realignment 2026-05-22: body-md is the\n // industry-standard 14px (shadcn / Vercel Geist / Linear / Stripe /\n // Mantine). The previous 15px was idiosyncratic. body-sm (14px label\n // weight) remains separate via its line-height / weight signature.\n \"body-lg\": [\"18px\", { lineHeight: \"1.56\", letterSpacing: \"-0.01em\", fontWeight: \"400\" }],\n \"body-md\": [\"14px\", { lineHeight: \"1.43\", letterSpacing: \"0\", fontWeight: \"400\" }],\n \"body-sm\": [\"13px\", { lineHeight: \"1.46\", fontWeight: \"400\" }],\n // Label tier — used on buttons, nav, secondary actions\n label: [\"14px\", { lineHeight: \"1.43\", fontWeight: \"500\" }],\n \"label-caps\": [\"12px\", { lineHeight: \"1.33\", letterSpacing: \"0.04em\", fontWeight: \"500\" }],\n // Mono — code surfaces, technical labels\n \"code-md\": [\"14px\", { lineHeight: \"1.5\", fontWeight: \"400\" }],\n \"code-sm\": [\"13px\", { lineHeight: \"1.54\", fontWeight: \"500\" }],\n },\n borderRadius: {\n none: \"var(--radius-none)\",\n sm: \"var(--radius-sm)\",\n md: \"var(--radius-md)\",\n lg: \"var(--radius-lg)\",\n xl: \"var(--radius-xl)\",\n \"2xl\": \"var(--radius-2xl)\",\n full: \"var(--radius-full)\",\n },\n boxShadow: {\n sm: \"var(--shadow-sm)\",\n md: \"var(--shadow-md)\",\n lg: \"var(--shadow-lg)\",\n glow: \"var(--shadow-glow)\",\n \"glow-strong\": \"var(--shadow-glow-strong)\",\n },\n transitionTimingFunction: {\n \"out-soft\": \"var(--ease-out-soft)\",\n snap: \"var(--ease-snap)\",\n },\n transitionDuration: {\n fast: \"var(--duration-fast)\",\n base: \"var(--duration-base)\",\n slow: \"var(--duration-slow)\",\n },\n keyframes: {\n \"fade-in-up\": {\n \"0%\": { opacity: \"0\", transform: \"translateY(8px)\" },\n \"100%\": { opacity: \"1\", transform: \"translateY(0)\" },\n },\n \"pulse-glow\": {\n \"0%, 100%\": { boxShadow: \"0 0 0 0 hsl(var(--primary) / 0.5)\" },\n \"50%\": { boxShadow: \"0 0 0 8px hsl(var(--primary) / 0)\" },\n },\n },\n animation: {\n \"fade-in-up\": \"fade-in-up var(--duration-base) var(--ease-out-soft) both\",\n \"pulse-glow\": \"pulse-glow 1.5s var(--ease-in-out) infinite\",\n },\n },\n },\n plugins: [animate],\n};\n","/**\n * `@usetheo/ui/preset-v3-legacy` — Tailwind v3 JS preset (legacy path).\n *\n * Default-export a `Partial<Config>` mirroring the design tokens shipped\n * in `@usetheo/ui/tokens.css`. Adds a `content` field covering the\n * library's published artifact tree so v3-based Tailwind builds emit\n * the utilities used by `@usetheo/ui` components.\n *\n * **Tailwind v4 consumers MUST use `@usetheo/ui/preset.css`**, not this\n * file — Tailwind v4 dropped the JS preset format entirely. This file\n * exists for any remaining `tailwindcss@^3` consumer (notably the\n * shadcn-style copy-paste registry path); new code should not import\n * from this subpath.\n *\n * The token surface is delegated to `./styles/tailwind-preset.ts` (the\n * existing source of truth used by the local Ladle dev surface and the\n * shadcn registry).\n *\n * v3 consumer usage:\n *\n * // tailwind.config.ts (Tailwind v3 only)\n * import preset from \"@usetheo/ui/preset-v3-legacy\";\n * export default {\n * presets: [preset],\n * content: [\"./app/**\\/*.{ts,tsx}\"],\n * };\n *\n * See RFC 0008 follow-up.\n */\nimport type { Config } from \"tailwindcss\";\nimport { theoUIPreset } from \"./styles/tailwind-preset.js\";\n\nconst LIBRARY_CONTENT_GLOBS: string[] = [\n // Resolved relative to the consumer's `tailwind.config.{ts,js}`.\n \"./node_modules/@usetheo/ui/dist/**/*.{js,mjs,cjs}\",\n // Yarn PnP / pnpm hoist fallback — Tailwind's globbing tolerates both.\n \"./node_modules/@usetheo/ui/dist/**/*.{ts,tsx}\",\n];\n\n// `theoUIPreset` is shape-compatible with v3 `Partial<Config>` at runtime\n// (tokens, theme, plugins, darkMode, etc are all carried through), but the\n// pnpm hoist places `tailwindcss@3` types alongside `tailwindcss@4` types in\n// the workspace — `theoUIPreset`'s inferred shape uses the v4 `UserConfig`\n// presets array type, which TS sees as incompatible with v3's `Config.presets`\n// (different element type). This file is the v3 LEGACY entry; its runtime\n// works for v3 consumers regardless. The cast bridges the v3/v4 type seam.\nconst preset = {\n ...theoUIPreset,\n content: LIBRARY_CONTENT_GLOBS,\n} as unknown as Partial<Config>;\n\nexport default preset;\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usetheo/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Theo UI — framework-agnostic React component library with the Violet Forge design system. Focused on AI-agent interfaces, cloud dashboards, and developer-tooling surfaces.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -12,6 +12,17 @@
|
|
|
12
12
|
"types": "./dist/index.d.ts",
|
|
13
13
|
"import": "./dist/index.js"
|
|
14
14
|
},
|
|
15
|
+
"./styles.css": "./dist/styles.css",
|
|
16
|
+
"./styles-v3-legacy.css": "./dist/styles-v3-legacy.css",
|
|
17
|
+
"./components.css": "./dist/components.css",
|
|
18
|
+
"./tokens.css": "./dist/tokens.css",
|
|
19
|
+
"./tokens-v4.css": "./dist/tokens-v4.css",
|
|
20
|
+
"./preset.css": "./dist/preset.css",
|
|
21
|
+
"./preset": "./dist/preset.css",
|
|
22
|
+
"./fonts.css": "./dist/fonts.css",
|
|
23
|
+
"./fonts-cdn.css": "./dist/fonts-cdn.css",
|
|
24
|
+
"./slide/themes/default.css": "./dist/slide/themes/default.css",
|
|
25
|
+
"./slide/themes/violet-forge.css": "./dist/slide/themes/violet-forge.css",
|
|
15
26
|
"./account-menu": {
|
|
16
27
|
"types": "./dist/index.d.ts",
|
|
17
28
|
"import": "./dist/composites/account-menu/index.js"
|
|
@@ -92,6 +103,10 @@
|
|
|
92
103
|
"types": "./dist/index.d.ts",
|
|
93
104
|
"import": "./dist/primitives/badge/index.js"
|
|
94
105
|
},
|
|
106
|
+
"./branch-indicator": {
|
|
107
|
+
"types": "./dist/index.d.ts",
|
|
108
|
+
"import": "./dist/primitives/branch-indicator/index.js"
|
|
109
|
+
},
|
|
95
110
|
"./browser-controls": {
|
|
96
111
|
"types": "./dist/index.d.ts",
|
|
97
112
|
"import": "./dist/primitives/browser-controls/index.js"
|
|
@@ -112,6 +127,10 @@
|
|
|
112
127
|
"types": "./dist/index.d.ts",
|
|
113
128
|
"import": "./dist/primitives/card/index.js"
|
|
114
129
|
},
|
|
130
|
+
"./channel-card": {
|
|
131
|
+
"types": "./dist/index.d.ts",
|
|
132
|
+
"import": "./dist/primitives/channel-card/index.js"
|
|
133
|
+
},
|
|
115
134
|
"./chat-composer": {
|
|
116
135
|
"types": "./dist/index.d.ts",
|
|
117
136
|
"import": "./dist/composites/chat-composer/index.js"
|
|
@@ -136,7 +155,6 @@
|
|
|
136
155
|
"types": "./dist/index.d.ts",
|
|
137
156
|
"import": "./dist/composites/command-palette/index.js"
|
|
138
157
|
},
|
|
139
|
-
"./components.css": "./dist/components.css",
|
|
140
158
|
"./confirm-dialog": {
|
|
141
159
|
"types": "./dist/index.d.ts",
|
|
142
160
|
"import": "./dist/composites/confirm-dialog/index.js"
|
|
@@ -205,6 +223,10 @@
|
|
|
205
223
|
"types": "./dist/index.d.ts",
|
|
206
224
|
"import": "./dist/composites/env-var-editor/index.js"
|
|
207
225
|
},
|
|
226
|
+
"./export-chat-dialog": {
|
|
227
|
+
"types": "./dist/index.d.ts",
|
|
228
|
+
"import": "./dist/primitives/export-chat-dialog/index.js"
|
|
229
|
+
},
|
|
208
230
|
"./folder-context-card": {
|
|
209
231
|
"types": "./dist/index.d.ts",
|
|
210
232
|
"import": "./dist/primitives/folder-context-card/index.js"
|
|
@@ -213,12 +235,14 @@
|
|
|
213
235
|
"types": "./dist/index.d.ts",
|
|
214
236
|
"import": "./dist/primitives/folder-selector/index.js"
|
|
215
237
|
},
|
|
216
|
-
"./fonts-cdn.css": "./dist/fonts-cdn.css",
|
|
217
|
-
"./fonts.css": "./dist/fonts.css",
|
|
218
238
|
"./form-field": {
|
|
219
239
|
"types": "./dist/index.d.ts",
|
|
220
240
|
"import": "./dist/primitives/form-field/index.js"
|
|
221
241
|
},
|
|
242
|
+
"./gateway-status-indicator": {
|
|
243
|
+
"types": "./dist/index.d.ts",
|
|
244
|
+
"import": "./dist/primitives/gateway-status-indicator/index.js"
|
|
245
|
+
},
|
|
222
246
|
"./hook-config": {
|
|
223
247
|
"types": "./dist/index.d.ts",
|
|
224
248
|
"import": "./dist/primitives/hook-config/index.js"
|
|
@@ -299,12 +323,6 @@
|
|
|
299
323
|
"types": "./dist/index.d.ts",
|
|
300
324
|
"import": "./dist/primitives/plan-badge/index.js"
|
|
301
325
|
},
|
|
302
|
-
"./preset": "./dist/preset.css",
|
|
303
|
-
"./preset-v3-legacy": {
|
|
304
|
-
"types": "./dist/preset-v3-legacy.d.ts",
|
|
305
|
-
"import": "./dist/preset-v3-legacy.js"
|
|
306
|
-
},
|
|
307
|
-
"./preset.css": "./dist/preset.css",
|
|
308
326
|
"./preview-env-card": {
|
|
309
327
|
"types": "./dist/index.d.ts",
|
|
310
328
|
"import": "./dist/composites/preview-env-card/index.js"
|
|
@@ -357,6 +375,10 @@
|
|
|
357
375
|
"types": "./dist/index.d.ts",
|
|
358
376
|
"import": "./dist/primitives/run-stats/index.js"
|
|
359
377
|
},
|
|
378
|
+
"./run-status-pill": {
|
|
379
|
+
"types": "./dist/index.d.ts",
|
|
380
|
+
"import": "./dist/primitives/run-status-pill/index.js"
|
|
381
|
+
},
|
|
360
382
|
"./running-tasks-panel": {
|
|
361
383
|
"types": "./dist/index.d.ts",
|
|
362
384
|
"import": "./dist/primitives/running-tasks-panel/index.js"
|
|
@@ -401,36 +423,14 @@
|
|
|
401
423
|
"types": "./dist/index.d.ts",
|
|
402
424
|
"import": "./dist/composites/skills-list/index.js"
|
|
403
425
|
},
|
|
404
|
-
"./slide": {
|
|
405
|
-
"types": "./dist/slide/index.d.ts",
|
|
406
|
-
"import": "./dist/slide/index.js"
|
|
407
|
-
},
|
|
408
|
-
"./slide-deck": {
|
|
409
|
-
"types": "./dist/slide-deck/index.d.ts",
|
|
410
|
-
"import": "./dist/slide-deck/index.js"
|
|
411
|
-
},
|
|
412
|
-
"./slide/plugins/emoji": {
|
|
413
|
-
"types": "./dist/slide/plugins/emoji/index.d.ts",
|
|
414
|
-
"import": "./dist/slide/plugins/emoji/index.js"
|
|
415
|
-
},
|
|
416
|
-
"./slide/plugins/math": {
|
|
417
|
-
"types": "./dist/slide/plugins/math/index.d.ts",
|
|
418
|
-
"import": "./dist/slide/plugins/math/index.js"
|
|
419
|
-
},
|
|
420
|
-
"./slide/plugins/mermaid": {
|
|
421
|
-
"types": "./dist/slide/plugins/mermaid/index.d.ts",
|
|
422
|
-
"import": "./dist/slide/plugins/mermaid/index.js"
|
|
423
|
-
},
|
|
424
|
-
"./slide/plugins/shiki": {
|
|
425
|
-
"types": "./dist/slide/plugins/shiki/index.d.ts",
|
|
426
|
-
"import": "./dist/slide/plugins/shiki/index.js"
|
|
427
|
-
},
|
|
428
|
-
"./slide/themes/default.css": "./dist/slide/themes/default.css",
|
|
429
|
-
"./slide/themes/violet-forge.css": "./dist/slide/themes/violet-forge.css",
|
|
430
426
|
"./social-auth-row": {
|
|
431
427
|
"types": "./dist/index.d.ts",
|
|
432
428
|
"import": "./dist/primitives/social-auth-row/index.js"
|
|
433
429
|
},
|
|
430
|
+
"./stability-bundle-viewer": {
|
|
431
|
+
"types": "./dist/index.d.ts",
|
|
432
|
+
"import": "./dist/composites/stability-bundle-viewer/index.js"
|
|
433
|
+
},
|
|
434
434
|
"./stat-tile": {
|
|
435
435
|
"types": "./dist/index.d.ts",
|
|
436
436
|
"import": "./dist/primitives/stat-tile/index.js"
|
|
@@ -443,8 +443,6 @@
|
|
|
443
443
|
"types": "./dist/index.d.ts",
|
|
444
444
|
"import": "./dist/primitives/steps-rail/index.js"
|
|
445
445
|
},
|
|
446
|
-
"./styles-v3-legacy.css": "./dist/styles-v3-legacy.css",
|
|
447
|
-
"./styles.css": "./dist/styles.css",
|
|
448
446
|
"./sub-agent-dispatch": {
|
|
449
447
|
"types": "./dist/index.d.ts",
|
|
450
448
|
"import": "./dist/primitives/sub-agent-dispatch/index.js"
|
|
@@ -481,6 +479,10 @@
|
|
|
481
479
|
"types": "./dist/index.d.ts",
|
|
482
480
|
"import": "./dist/primitives/textarea/index.js"
|
|
483
481
|
},
|
|
482
|
+
"./thinking-level-selector": {
|
|
483
|
+
"types": "./dist/index.d.ts",
|
|
484
|
+
"import": "./dist/primitives/thinking-level-selector/index.js"
|
|
485
|
+
},
|
|
484
486
|
"./timestamp": {
|
|
485
487
|
"types": "./dist/index.d.ts",
|
|
486
488
|
"import": "./dist/primitives/timestamp/index.js"
|
|
@@ -493,8 +495,6 @@
|
|
|
493
495
|
"types": "./dist/index.d.ts",
|
|
494
496
|
"import": "./dist/primitives/token-usage-chart/index.js"
|
|
495
497
|
},
|
|
496
|
-
"./tokens-v4.css": "./dist/tokens-v4.css",
|
|
497
|
-
"./tokens.css": "./dist/tokens.css",
|
|
498
498
|
"./tool-call": {
|
|
499
499
|
"types": "./dist/index.d.ts",
|
|
500
500
|
"import": "./dist/primitives/tool-call/index.js"
|
|
@@ -519,17 +519,49 @@
|
|
|
519
519
|
"types": "./dist/index.d.ts",
|
|
520
520
|
"import": "./dist/primitives/topnav/index.js"
|
|
521
521
|
},
|
|
522
|
+
"./update-banner": {
|
|
523
|
+
"types": "./dist/index.d.ts",
|
|
524
|
+
"import": "./dist/primitives/update-banner/index.js"
|
|
525
|
+
},
|
|
522
526
|
"./usage-meter": {
|
|
523
527
|
"types": "./dist/index.d.ts",
|
|
524
528
|
"import": "./dist/composites/usage-meter/index.js"
|
|
525
529
|
},
|
|
530
|
+
"./whiteboard": {
|
|
531
|
+
"types": "./dist/whiteboard/index.d.ts",
|
|
532
|
+
"import": "./dist/whiteboard/index.js"
|
|
533
|
+
},
|
|
534
|
+
"./slide": {
|
|
535
|
+
"types": "./dist/slide/index.d.ts",
|
|
536
|
+
"import": "./dist/slide/index.js"
|
|
537
|
+
},
|
|
538
|
+
"./slide/plugins/shiki": {
|
|
539
|
+
"types": "./dist/slide/plugins/shiki/index.d.ts",
|
|
540
|
+
"import": "./dist/slide/plugins/shiki/index.js"
|
|
541
|
+
},
|
|
542
|
+
"./slide/plugins/math": {
|
|
543
|
+
"types": "./dist/slide/plugins/math/index.d.ts",
|
|
544
|
+
"import": "./dist/slide/plugins/math/index.js"
|
|
545
|
+
},
|
|
546
|
+
"./slide/plugins/mermaid": {
|
|
547
|
+
"types": "./dist/slide/plugins/mermaid/index.d.ts",
|
|
548
|
+
"import": "./dist/slide/plugins/mermaid/index.js"
|
|
549
|
+
},
|
|
550
|
+
"./slide/plugins/emoji": {
|
|
551
|
+
"types": "./dist/slide/plugins/emoji/index.d.ts",
|
|
552
|
+
"import": "./dist/slide/plugins/emoji/index.js"
|
|
553
|
+
},
|
|
554
|
+
"./slide-deck": {
|
|
555
|
+
"types": "./dist/slide-deck/index.d.ts",
|
|
556
|
+
"import": "./dist/slide-deck/index.js"
|
|
557
|
+
},
|
|
526
558
|
"./vite-plugin": {
|
|
527
559
|
"types": "./dist/vite-plugin.d.ts",
|
|
528
560
|
"import": "./dist/vite-plugin.js"
|
|
529
561
|
},
|
|
530
|
-
"./
|
|
531
|
-
"types": "./dist/
|
|
532
|
-
"import": "./dist/
|
|
562
|
+
"./preset-v3-legacy": {
|
|
563
|
+
"types": "./dist/preset-v3-legacy.d.ts",
|
|
564
|
+
"import": "./dist/preset-v3-legacy.js"
|
|
533
565
|
}
|
|
534
566
|
},
|
|
535
567
|
"files": [
|
|
@@ -542,43 +574,6 @@
|
|
|
542
574
|
"llms.txt",
|
|
543
575
|
"DESIGN.md"
|
|
544
576
|
],
|
|
545
|
-
"scripts": {
|
|
546
|
-
"build": "tsup && tsx scripts/regen-subpath-exports.ts",
|
|
547
|
-
"dev": "ladle serve",
|
|
548
|
-
"ladle:build": "ladle build",
|
|
549
|
-
"ladle:preview": "ladle preview",
|
|
550
|
-
"playground": "vite --config playground/vite.config.ts",
|
|
551
|
-
"playground:build": "vite build --config playground/vite.config.ts",
|
|
552
|
-
"playground:preview": "vite preview --config playground/vite.config.ts",
|
|
553
|
-
"typecheck": "tsc --noEmit",
|
|
554
|
-
"lint": "biome check src",
|
|
555
|
-
"lint:ci": "biome ci src scripts .ladle playground",
|
|
556
|
-
"lint:fix": "biome check --write src",
|
|
557
|
-
"format": "biome format --write src scripts .ladle package.json tsconfig.json tailwind.config.ts vitest.config.ts tsup.config.ts biome.json",
|
|
558
|
-
"format:check": "biome format src scripts .ladle package.json tsconfig.json tailwind.config.ts vitest.config.ts tsup.config.ts biome.json",
|
|
559
|
-
"test": "vitest run",
|
|
560
|
-
"test:watch": "vitest",
|
|
561
|
-
"test:ui": "vitest --ui",
|
|
562
|
-
"registry:build": "tsx scripts/build-registry.ts",
|
|
563
|
-
"registry:validate": "tsx scripts/validate-registry.ts",
|
|
564
|
-
"sync:readme": "tsx scripts/sync-readme.ts",
|
|
565
|
-
"sync:exports": "tsx scripts/sync-exports.ts",
|
|
566
|
-
"test:registry": "tsx scripts/test-registry-install.ts",
|
|
567
|
-
"test:coverage": "vitest run --coverage",
|
|
568
|
-
"quality:structure": "tsx scripts/validate-quality-gates.ts",
|
|
569
|
-
"quality:bundle": "tsx scripts/validate-bundle-size.ts",
|
|
570
|
-
"quality:bundle:update": "tsx scripts/validate-bundle-size.ts --update",
|
|
571
|
-
"quality:a11y": "vitest run src/test/ladle-axe.test.tsx",
|
|
572
|
-
"dogfood:whiteboard": "tsx scripts/dogfood-whiteboard.ts",
|
|
573
|
-
"dogfood:slide": "tsx scripts/dogfood-slide.ts",
|
|
574
|
-
"dogfood:slide-deck": "tsx scripts/dogfood-slide-deck.ts",
|
|
575
|
-
"dogfood:slide-rich": "tsx scripts/dogfood-slide-rich.ts",
|
|
576
|
-
"dogfood:v4-zero-config": "tsx scripts/dogfood-v4-zero-config.ts",
|
|
577
|
-
"dogfood:v4-real-build": "bash scripts/dogfood-v4-real-build.sh",
|
|
578
|
-
"dogfood:precompiled-utilities": "tsx scripts/dogfood-precompiled-utilities.ts",
|
|
579
|
-
"quality:gates": "pnpm format:check && pnpm lint:ci && pnpm typecheck && pnpm test && pnpm build && pnpm registry:build && pnpm registry:validate && pnpm quality:structure && pnpm quality:bundle && pnpm quality:a11y && pnpm ladle:build && pnpm dogfood:whiteboard && pnpm dogfood:slide && pnpm dogfood:slide-deck && pnpm dogfood:slide-rich && pnpm dogfood:v4-zero-config && pnpm dogfood:precompiled-utilities",
|
|
580
|
-
"quality:gates:fast": "pnpm format:check && pnpm lint:ci && pnpm typecheck && pnpm registry:build && pnpm registry:validate && pnpm quality:structure"
|
|
581
|
-
},
|
|
582
577
|
"peerDependencies": {
|
|
583
578
|
"@tailwindcss/vite": "^4.0.0",
|
|
584
579
|
"hast-util-from-html": "^2.0.0",
|
|
@@ -688,6 +683,7 @@
|
|
|
688
683
|
"zod": "^4.4.3"
|
|
689
684
|
},
|
|
690
685
|
"devDependencies": {
|
|
686
|
+
"@arethetypeswrong/cli": "^0.18.2",
|
|
691
687
|
"@biomejs/biome": "^1.9.4",
|
|
692
688
|
"@ladle/react": "^4.1.2",
|
|
693
689
|
"@tailwindcss/cli": "^4.3.0",
|
|
@@ -708,12 +704,14 @@
|
|
|
708
704
|
"hast-util-from-html": "^2.0.3",
|
|
709
705
|
"hast-util-sanitize": "^5.0.2",
|
|
710
706
|
"hast-util-to-jsx-runtime": "^2.3.6",
|
|
707
|
+
"knip": "^6.14.2",
|
|
711
708
|
"mdast-util-from-markdown": "^2.0.3",
|
|
712
709
|
"mdast-util-gfm": "^3.1.0",
|
|
713
710
|
"mdast-util-to-hast": "^13.2.1",
|
|
714
711
|
"micromark-extension-gfm": "^3.0.0",
|
|
715
712
|
"perfect-freehand": "1.2.3",
|
|
716
713
|
"postcss": "^8.5.14",
|
|
714
|
+
"publint": "^0.3.21",
|
|
717
715
|
"react": "^18.3.1",
|
|
718
716
|
"react-dom": "^18.3.1",
|
|
719
717
|
"roughjs": "4.6.6",
|
|
@@ -731,16 +729,6 @@
|
|
|
731
729
|
"engines": {
|
|
732
730
|
"node": ">=20"
|
|
733
731
|
},
|
|
734
|
-
"pnpm": {
|
|
735
|
-
"onlyBuiltDependencies": [
|
|
736
|
-
"@biomejs/biome",
|
|
737
|
-
"@swc/core",
|
|
738
|
-
"esbuild"
|
|
739
|
-
],
|
|
740
|
-
"overrides": {
|
|
741
|
-
"postcss": ">=8.5.10"
|
|
742
|
-
}
|
|
743
|
-
},
|
|
744
732
|
"keywords": [
|
|
745
733
|
"react",
|
|
746
734
|
"components",
|
|
@@ -756,5 +744,51 @@
|
|
|
756
744
|
"publishConfig": {
|
|
757
745
|
"access": "public"
|
|
758
746
|
},
|
|
759
|
-
"
|
|
760
|
-
|
|
747
|
+
"scripts": {
|
|
748
|
+
"build": "tsup && tsx scripts/regen-subpath-exports.ts",
|
|
749
|
+
"dev": "ladle serve",
|
|
750
|
+
"ladle:build": "ladle build",
|
|
751
|
+
"ladle:preview": "ladle preview",
|
|
752
|
+
"playground": "vite --config playground/vite.config.ts",
|
|
753
|
+
"playground:build": "vite build --config playground/vite.config.ts",
|
|
754
|
+
"playground:preview": "vite preview --config playground/vite.config.ts",
|
|
755
|
+
"typecheck": "tsc --noEmit",
|
|
756
|
+
"lint": "biome check src",
|
|
757
|
+
"lint:ci": "biome ci src scripts .ladle playground",
|
|
758
|
+
"inventory": "node scripts/inventory-components.mjs",
|
|
759
|
+
"stories:check": "node scripts/generate-missing-stories.mjs --check",
|
|
760
|
+
"stories:generate": "node scripts/generate-missing-stories.mjs --write",
|
|
761
|
+
"stories:test": "node scripts/__tests__/generate-missing-stories.test.mjs",
|
|
762
|
+
"lint:fix": "biome check --write src",
|
|
763
|
+
"format": "biome format --write src scripts .ladle package.json tsconfig.json tailwind.config.ts vitest.config.ts tsup.config.ts biome.json",
|
|
764
|
+
"format:check": "biome format src scripts .ladle package.json tsconfig.json tailwind.config.ts vitest.config.ts tsup.config.ts biome.json",
|
|
765
|
+
"test": "vitest run",
|
|
766
|
+
"test:watch": "vitest",
|
|
767
|
+
"test:ui": "vitest --ui",
|
|
768
|
+
"test:contract": "vitest run tests/contract",
|
|
769
|
+
"validate:exports": "node scripts/validate-exports.mjs",
|
|
770
|
+
"registry:build": "tsx scripts/build-registry.ts",
|
|
771
|
+
"registry:validate": "tsx scripts/validate-registry.ts",
|
|
772
|
+
"sync:readme": "tsx scripts/sync-readme.ts",
|
|
773
|
+
"sync:exports": "tsx scripts/sync-exports.ts",
|
|
774
|
+
"test:registry": "tsx scripts/test-registry-install.ts",
|
|
775
|
+
"test:coverage": "vitest run --coverage",
|
|
776
|
+
"quality:structure": "tsx scripts/validate-quality-gates.ts",
|
|
777
|
+
"quality:bundle": "tsx scripts/validate-bundle-size.ts",
|
|
778
|
+
"quality:bundle:update": "tsx scripts/validate-bundle-size.ts --update",
|
|
779
|
+
"quality:a11y": "vitest run src/test/ladle-axe.test.tsx",
|
|
780
|
+
"dogfood:whiteboard": "tsx scripts/dogfood-whiteboard.ts",
|
|
781
|
+
"dogfood:slide": "tsx scripts/dogfood-slide.ts",
|
|
782
|
+
"dogfood:slide-deck": "tsx scripts/dogfood-slide-deck.ts",
|
|
783
|
+
"dogfood:slide-rich": "tsx scripts/dogfood-slide-rich.ts",
|
|
784
|
+
"dogfood:v4-zero-config": "tsx scripts/dogfood-v4-zero-config.ts",
|
|
785
|
+
"dogfood:v4-real-build": "bash scripts/dogfood-v4-real-build.sh",
|
|
786
|
+
"dogfood:precompiled-utilities": "tsx scripts/dogfood-precompiled-utilities.ts",
|
|
787
|
+
"quality:gates": "pnpm format:check && pnpm lint:ci && pnpm typecheck && pnpm quality:knip && pnpm test && pnpm build && pnpm quality:publint && pnpm registry:build && pnpm registry:validate && pnpm quality:structure && pnpm quality:bundle && pnpm quality:a11y && pnpm ladle:build && pnpm dogfood:whiteboard && pnpm dogfood:slide && pnpm dogfood:slide-deck && pnpm dogfood:slide-rich && pnpm dogfood:v4-zero-config && pnpm dogfood:precompiled-utilities",
|
|
788
|
+
"quality:gates:fast": "pnpm format:check && pnpm lint:ci && pnpm typecheck && pnpm quality:knip && pnpm registry:build && pnpm registry:validate && pnpm quality:structure",
|
|
789
|
+
"quality:knip": "knip",
|
|
790
|
+
"quality:knip:fix": "knip --fix",
|
|
791
|
+
"quality:publint": "publint --strict",
|
|
792
|
+
"quality:attw": "attw --pack . --profile esm-only"
|
|
793
|
+
}
|
|
794
|
+
}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"path": "components/composites/data-table/data-table.tsx",
|
|
22
22
|
"type": "registry:ui",
|
|
23
23
|
"target": "components/ui/data-table.tsx",
|
|
24
|
-
"content": "import { ChevronDown, ChevronRight, MoreHorizontal } from \"lucide-react\";\nimport { Fragment, useMemo, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"@/lib/cn\";\nimport { DropdownMenu } from \"@/components/ui/dropdown-menu\";\nimport { EmptyState } from \"@/components/ui/empty-state\";\nimport { Pagination } from \"@/components/ui/pagination\";\nimport { Skeleton } from \"@/components/ui/skeleton\";\nimport { Table } from \"@/components/ui/table\";\n\n/**\n * DataTable — generic, sortable, expandable composite over `<Table>`.\n *\n * Adds operator-grade entity-list patterns on top of the plain Table\n * primitive: sortable headers, sticky header, expandable rows\n * (multi-row by default), row action menus (Dropdown), client-side\n * pagination, loading skeleton rows, empty state. Both sort and\n * pagination support controlled OR uncontrolled mode (consumer\n * passes onSortChange / onPageChange to take over state).\n *\n * @example\n * <DataTable\n * columns={[\n * { key: \"name\", label: \"Name\", sortable: true },\n * { key: \"status\", label: \"Status\" },\n * ]}\n * data={domains}\n * rowKey={(d) => d.id}\n * expandable={(d) => d.status === \"pending\" ? <DnsRecords domain={d} /> : null}\n * rowActions={(d) => (\n * <>\n * <DropdownMenu.Item onSelect={() => editDomain(d)}>Edit</DropdownMenu.Item>\n * <DropdownMenu.Item onSelect={() => deleteDomain(d)}>Delete</DropdownMenu.Item>\n * </>\n * )}\n * />\n */\nexport interface DataTableColumn<T> {\n key: string;\n label: ReactNode;\n align?: \"left\" | \"center\" | \"right\";\n sortable?: boolean;\n width?: string;\n render?: (row: T) => ReactNode;\n className?: string;\n}\n\nexport interface DataTableSort {\n key: string;\n direction: \"asc\" | \"desc\";\n}\n\nexport interface DataTableProps<T> {\n data: T[];\n columns: DataTableColumn<T>[];\n rowKey: (row: T) => string;\n stickyHeader?: boolean;\n expandable?: (row: T) => ReactNode | null;\n expandMode?: \"single\" | \"multiple\";\n rowActions?: (row: T) => ReactNode;\n pagination?: {\n pageSize: number;\n controlledPage?: number;\n onPageChange?: (page: number) => void;\n } | null;\n defaultSort?: DataTableSort;\n sort?: DataTableSort | null;\n onSortChange?: (sort: DataTableSort | null) => void;\n loading?: boolean;\n emptyState?: ReactNode;\n className?: string;\n}\n\nfunction compareValues(a: unknown, b: unknown): number {\n if (a === b) return 0;\n if (a === null || a === undefined) return -1;\n if (b === null || b === undefined) return 1;\n if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n return String(a).localeCompare(String(b));\n}\n\nfunction DataTable<T>(props: DataTableProps<T>): ReactNode {\n const {\n data,\n columns,\n rowKey,\n stickyHeader = true,\n expandable,\n expandMode = \"multiple\",\n rowActions,\n pagination,\n defaultSort,\n sort: controlledSort,\n onSortChange,\n loading = false,\n emptyState,\n className,\n } = props;\n\n const isControlledSort = onSortChange !== undefined;\n const [uncontrolledSort, setUncontrolledSort] = useState<DataTableSort | null>(\n defaultSort ?? null,\n );\n const sort = isControlledSort ? (controlledSort ?? null) : uncontrolledSort;\n\n const isControlledPage = pagination?.controlledPage !== undefined;\n const [uncontrolledPage, setUncontrolledPage] = useState(0);\n const currentPage = isControlledPage ? (pagination?.controlledPage ?? 0) : uncontrolledPage;\n\n // EC-9: clamp pageSize to >= 1 to avoid divide-by-zero / infinite render\n const effectivePageSize = Math.max(1, pagination?.pageSize ?? 10);\n\n const [expanded, setExpanded] = useState<Set<string>>(new Set());\n\n function handleSort(columnKey: string) {\n // Cycle: none → asc → desc → none\n let nextSort: DataTableSort | null;\n if (sort?.key !== columnKey) {\n nextSort = { key: columnKey, direction: \"asc\" };\n } else if (sort.direction === \"asc\") {\n nextSort = { key: columnKey, direction: \"desc\" };\n } else {\n nextSort = null;\n }\n if (isControlledSort) {\n onSortChange?.(nextSort);\n } else {\n setUncontrolledSort(nextSort);\n // EC-8: sort change resets pagination to page 0\n if (!isControlledPage) setUncontrolledPage(0);\n }\n }\n\n function handlePageChange(page: number) {\n // Pagination uses 1-indexed; internal state 0-indexed\n const zeroIdx = page - 1;\n if (isControlledPage) {\n pagination?.onPageChange?.(zeroIdx);\n } else {\n setUncontrolledPage(zeroIdx);\n }\n }\n\n function toggleExpand(key: string) {\n if (expandMode === \"single\") {\n setExpanded((prev) => (prev.has(key) ? new Set() : new Set([key])));\n } else {\n setExpanded((prev) => {\n const next = new Set(prev);\n if (next.has(key)) {\n next.delete(key);\n } else {\n next.add(key);\n }\n return next;\n });\n }\n }\n\n // Apply client-side sort in uncontrolled mode\n const sortedData = useMemo(() => {\n if (isControlledSort || sort === null) return data;\n const col = columns.find((c) => c.key === sort.key);\n if (!col) return data;\n const sorted = [...data].sort((a, b) => {\n const aVal = col.render\n ? null\n : (a as Record<string, unknown>)[sort.key as keyof T as string];\n const bVal = col.render\n ? null\n : (b as Record<string, unknown>)[sort.key as keyof T as string];\n const cmp = compareValues(aVal, bVal);\n return sort.direction === \"asc\" ? cmp : -cmp;\n });\n return sorted;\n }, [data, sort, isControlledSort, columns]);\n\n // Apply client-side pagination in uncontrolled mode\n const visibleData = useMemo(() => {\n if (!pagination) return sortedData;\n if (isControlledPage) return sortedData; // consumer pre-sliced\n const start = currentPage * effectivePageSize;\n return sortedData.slice(start, start + effectivePageSize);\n }, [sortedData, pagination, isControlledPage, currentPage, effectivePageSize]);\n\n // EC-1 fix: compute colSpan accounting for chevron + actions columns\n const extraCols = (expandable ? 1 : 0) + (rowActions ? 1 : 0);\n const expandedColSpan = columns.length + extraCols;\n const totalCols = columns.length + extraCols;\n\n // Loading state (EC-7: loading > empty)\n if (loading) {\n return (\n <div className={cn(\"w-full\", className)}>\n <Table>\n <Table.Header className={stickyHeader ? \"sticky top-0 bg-card\" : undefined}>\n <Table.Row>\n {expandable ? <Table.HeaderCell aria-label=\"Expand\" /> : null}\n {columns.map((col) => (\n <Table.HeaderCell key={col.key} align={col.align}>\n {col.label}\n </Table.HeaderCell>\n ))}\n {rowActions ? <Table.HeaderCell aria-label=\"Actions\" /> : null}\n </Table.Row>\n </Table.Header>\n <Table.Body>\n {Array.from({ length: 5 }, (_, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: skeleton rows are positional placeholders\n <Table.Row key={`skeleton-${i}`}>\n {Array.from({ length: totalCols }, (_, j) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: skeleton cells are positional placeholders\n <Table.Cell key={`s-${i}-${j}`}>\n <Skeleton className=\"h-4 w-full\" />\n </Table.Cell>\n ))}\n </Table.Row>\n ))}\n </Table.Body>\n </Table>\n </div>\n );\n }\n\n // Empty state (after loading check)\n if (sortedData.length === 0) {\n return (\n <div className={cn(\"w-full\", className)}>\n {emptyState ?? <EmptyState title=\"No data\" description=\"There's nothing here yet.\" />}\n </div>\n );\n }\n\n const totalPages = pagination ? Math.ceil(sortedData.length / effectivePageSize) : 1;\n\n return (\n <div className={cn(\"w-full\", className)}>\n <Table>\n <Table.Header className={stickyHeader ? \"sticky top-0 z-10 bg-card\" : undefined}>\n <Table.Row>\n {expandable ? <Table.HeaderCell aria-label=\"Expand\" /> : null}\n {columns.map((col) => {\n const isSortable = col.sortable === true;\n const isActive = sort?.key === col.key;\n return (\n <Table.HeaderCell\n key={col.key}\n align={col.align}\n onSort={isSortable ? () => handleSort(col.key) : undefined}\n sortDirection={isSortable ? (isActive ? sort?.direction : \"none\") : undefined}\n style={col.width ? { width: col.width } : undefined}\n >\n {col.label}\n </Table.HeaderCell>\n );\n })}\n {rowActions ? <Table.HeaderCell aria-label=\"Actions\" /> : null}\n </Table.Row>\n </Table.Header>\n <Table.Body>\n {visibleData.map((row) => {\n const key = rowKey(row);\n const expandedContent = expandable ? expandable(row) : null;\n const isExpandable = expandedContent !== null && expandedContent !== undefined;\n const isExpanded = expanded.has(key);\n return (\n <Fragment key={key}>\n <Table.Row>\n {expandable ? (\n <Table.Cell>\n {isExpandable ? (\n <button\n type=\"button\"\n onClick={() => toggleExpand(key)}\n aria-expanded={isExpanded}\n aria-controls={`expanded-${key}`}\n aria-label={isExpanded ? \"Collapse row\" : \"Expand row\"}\n className=\"inline-flex items-center justify-center rounded-md p-0.5 hover:bg-muted\"\n >\n {isExpanded ? (\n <ChevronDown aria-hidden=\"true\" className=\"size-4\" />\n ) : (\n <ChevronRight aria-hidden=\"true\" className=\"size-4\" />\n )}\n </button>\n ) : null}\n </Table.Cell>\n ) : null}\n {columns.map((col) => (\n <Table.Cell key={col.key} align={col.align} className={col.className}>\n {col.render\n ? col.render(row)\n : String((row as Record<string, unknown>)[col.key] ?? \"\")}\n </Table.Cell>\n ))}\n {rowActions ? (\n <Table.Cell align=\"right\">\n <DropdownMenu>\n <DropdownMenu.Trigger\n aria-label=\"Row actions\"\n className={cn(\n \"inline-flex size-7 items-center justify-center rounded-md\",\n \"text-muted-foreground hover:bg-muted hover:text-foreground\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n )}\n >\n <MoreHorizontal aria-hidden=\"true\" className=\"size-4\" />\n </DropdownMenu.Trigger>\n <DropdownMenu.Content align=\"end\">{rowActions(row)}</DropdownMenu.Content>\n </DropdownMenu>\n </Table.Cell>\n ) : null}\n </Table.Row>\n {isExpanded && isExpandable ? (\n <tr id={`expanded-${key}`}>\n <td colSpan={expandedColSpan} className=\"bg-muted/30 p-4\">\n {expandedContent}\n </td>\n </tr>\n ) : null}\n </Fragment>\n );\n })}\n </Table.Body>\n </Table>\n {pagination && totalPages > 1 ? (\n <div className=\"mt-4 flex items-center justify-end\">\n <Pagination\n currentPage={currentPage + 1}\n totalPages={totalPages}\n onPageChange={handlePageChange}\n />\n </div>\n ) : null}\n </div>\n );\n}\n\nexport { DataTable };\n"
|
|
24
|
+
"content": "import { ChevronDown, ChevronRight, MoreHorizontal } from \"lucide-react\";\nimport { Fragment, useMemo, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"@/lib/cn\";\nimport { DropdownMenu } from \"@/components/ui/dropdown-menu\";\nimport { EmptyState } from \"@/components/ui/empty-state\";\nimport { Pagination } from \"@/components/ui/pagination\";\nimport { Skeleton } from \"@/components/ui/skeleton\";\nimport { Table } from \"@/components/ui/table\";\n\n/**\n * DataTable — generic, sortable, expandable composite over `<Table>`.\n *\n * Adds operator-grade entity-list patterns on top of the plain Table\n * primitive: sortable headers, sticky header, expandable rows\n * (multi-row by default), row action menus (Dropdown), client-side\n * pagination, loading skeleton rows, empty state. Both sort and\n * pagination support controlled OR uncontrolled mode (consumer\n * passes onSortChange / onPageChange to take over state).\n *\n * @example\n * <DataTable\n * columns={[\n * { key: \"name\", label: \"Name\", sortable: true },\n * { key: \"status\", label: \"Status\" },\n * ]}\n * data={domains}\n * rowKey={(d) => d.id}\n * expandable={(d) => d.status === \"pending\" ? <DnsRecords domain={d} /> : null}\n * rowActions={(d) => (\n * <>\n * <DropdownMenu.Item onSelect={() => editDomain(d)}>Edit</DropdownMenu.Item>\n * <DropdownMenu.Item onSelect={() => deleteDomain(d)}>Delete</DropdownMenu.Item>\n * </>\n * )}\n * />\n */\nexport interface DataTableColumn<T> {\n key: string;\n label: ReactNode;\n align?: \"left\" | \"center\" | \"right\";\n sortable?: boolean;\n width?: string;\n render?: (row: T) => ReactNode;\n className?: string;\n}\n\nexport interface DataTableSort {\n key: string;\n direction: \"asc\" | \"desc\";\n}\n\nexport interface DataTableProps<T> {\n data: T[];\n columns: DataTableColumn<T>[];\n rowKey: (row: T) => string;\n stickyHeader?: boolean;\n expandable?: (row: T) => ReactNode | null;\n expandMode?: \"single\" | \"multiple\";\n rowActions?: (row: T) => ReactNode;\n pagination?: {\n pageSize: number;\n controlledPage?: number;\n onPageChange?: (page: number) => void;\n } | null;\n defaultSort?: DataTableSort;\n sort?: DataTableSort | null;\n onSortChange?: (sort: DataTableSort | null) => void;\n loading?: boolean;\n emptyState?: ReactNode;\n className?: string;\n}\n\nfunction compareValues(a: unknown, b: unknown): number {\n if (a === b) return 0;\n if (a === null || a === undefined) return -1;\n if (b === null || b === undefined) return 1;\n if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n return String(a).localeCompare(String(b));\n}\n\nfunction DataTable<T>(props: DataTableProps<T>): ReactNode {\n const {\n data,\n columns,\n rowKey,\n stickyHeader = true,\n expandable,\n expandMode = \"multiple\",\n rowActions,\n pagination,\n defaultSort,\n sort: controlledSort,\n onSortChange,\n loading = false,\n emptyState,\n className,\n } = props;\n\n const isControlledSort = onSortChange !== undefined;\n const [uncontrolledSort, setUncontrolledSort] = useState<DataTableSort | null>(\n defaultSort ?? null,\n );\n const sort = isControlledSort ? (controlledSort ?? null) : uncontrolledSort;\n\n const isControlledPage = pagination?.controlledPage !== undefined;\n const [uncontrolledPage, setUncontrolledPage] = useState(0);\n const currentPage = isControlledPage ? (pagination?.controlledPage ?? 0) : uncontrolledPage;\n\n // EC-9: clamp pageSize to >= 1 to avoid divide-by-zero / infinite render\n const effectivePageSize = Math.max(1, pagination?.pageSize ?? 10);\n\n const [expanded, setExpanded] = useState<Set<string>>(new Set());\n\n function handleSort(columnKey: string) {\n // Cycle: none → asc → desc → none\n let nextSort: DataTableSort | null;\n if (sort?.key !== columnKey) {\n nextSort = { key: columnKey, direction: \"asc\" };\n } else if (sort.direction === \"asc\") {\n nextSort = { key: columnKey, direction: \"desc\" };\n } else {\n nextSort = null;\n }\n if (isControlledSort) {\n onSortChange?.(nextSort);\n } else {\n setUncontrolledSort(nextSort);\n // EC-8: sort change resets pagination to page 0\n if (!isControlledPage) setUncontrolledPage(0);\n }\n }\n\n function handlePageChange(page: number) {\n // Pagination uses 1-indexed; internal state 0-indexed\n const zeroIdx = page - 1;\n if (isControlledPage) {\n pagination?.onPageChange?.(zeroIdx);\n } else {\n setUncontrolledPage(zeroIdx);\n }\n }\n\n function toggleExpand(key: string) {\n if (expandMode === \"single\") {\n setExpanded((prev) => (prev.has(key) ? new Set() : new Set([key])));\n } else {\n setExpanded((prev) => {\n const next = new Set(prev);\n if (next.has(key)) {\n next.delete(key);\n } else {\n next.add(key);\n }\n return next;\n });\n }\n }\n\n // Apply client-side sort in uncontrolled mode\n const sortedData = useMemo(() => {\n if (isControlledSort || sort === null) return data;\n const col = columns.find((c) => c.key === sort.key);\n if (!col) return data;\n const sorted = [...data].sort((a, b) => {\n const aVal = col.render\n ? null\n : (a as Record<string, unknown>)[sort.key as keyof T as string];\n const bVal = col.render\n ? null\n : (b as Record<string, unknown>)[sort.key as keyof T as string];\n const cmp = compareValues(aVal, bVal);\n return sort.direction === \"asc\" ? cmp : -cmp;\n });\n return sorted;\n }, [data, sort, isControlledSort, columns]);\n\n // Apply client-side pagination in uncontrolled mode\n const visibleData = useMemo(() => {\n if (!pagination) return sortedData;\n if (isControlledPage) return sortedData; // consumer pre-sliced\n const start = currentPage * effectivePageSize;\n return sortedData.slice(start, start + effectivePageSize);\n }, [sortedData, pagination, isControlledPage, currentPage, effectivePageSize]);\n\n // EC-1 fix: compute colSpan accounting for chevron + actions columns\n const extraCols = (expandable ? 1 : 0) + (rowActions ? 1 : 0);\n const expandedColSpan = columns.length + extraCols;\n const totalCols = columns.length + extraCols;\n\n // Loading state (EC-7: loading > empty)\n if (loading) {\n return (\n <div className={cn(\"w-full\", className)}>\n <Table>\n <Table.Header className={stickyHeader ? \"sticky top-0 bg-card\" : undefined}>\n <Table.Row>\n {expandable ? (\n <Table.HeaderCell>\n <span className=\"sr-only\">Expand</span>\n </Table.HeaderCell>\n ) : null}\n {columns.map((col) => (\n <Table.HeaderCell key={col.key} align={col.align}>\n {col.label}\n </Table.HeaderCell>\n ))}\n {rowActions ? (\n <Table.HeaderCell>\n <span className=\"sr-only\">Actions</span>\n </Table.HeaderCell>\n ) : null}\n </Table.Row>\n </Table.Header>\n <Table.Body>\n {Array.from({ length: 5 }, (_, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: skeleton rows are positional placeholders\n <Table.Row key={`skeleton-${i}`}>\n {Array.from({ length: totalCols }, (_, j) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: skeleton cells are positional placeholders\n <Table.Cell key={`s-${i}-${j}`}>\n <Skeleton className=\"h-4 w-full\" />\n </Table.Cell>\n ))}\n </Table.Row>\n ))}\n </Table.Body>\n </Table>\n </div>\n );\n }\n\n // Empty state (after loading check)\n if (sortedData.length === 0) {\n return (\n <div className={cn(\"w-full\", className)}>\n {emptyState ?? <EmptyState title=\"No data\" description=\"There's nothing here yet.\" />}\n </div>\n );\n }\n\n const totalPages = pagination ? Math.ceil(sortedData.length / effectivePageSize) : 1;\n\n return (\n <div className={cn(\"w-full\", className)}>\n <Table>\n <Table.Header className={stickyHeader ? \"sticky top-0 z-10 bg-card\" : undefined}>\n <Table.Row>\n {expandable ? (\n <Table.HeaderCell>\n <span className=\"sr-only\">Expand</span>\n </Table.HeaderCell>\n ) : null}\n {columns.map((col) => {\n const isSortable = col.sortable === true;\n const isActive = sort?.key === col.key;\n return (\n <Table.HeaderCell\n key={col.key}\n align={col.align}\n onSort={isSortable ? () => handleSort(col.key) : undefined}\n sortDirection={isSortable ? (isActive ? sort?.direction : \"none\") : undefined}\n style={col.width ? { width: col.width } : undefined}\n >\n {col.label}\n </Table.HeaderCell>\n );\n })}\n {rowActions ? (\n <Table.HeaderCell>\n <span className=\"sr-only\">Actions</span>\n </Table.HeaderCell>\n ) : null}\n </Table.Row>\n </Table.Header>\n <Table.Body>\n {visibleData.map((row) => {\n const key = rowKey(row);\n const expandedContent = expandable ? expandable(row) : null;\n const isExpandable = expandedContent !== null && expandedContent !== undefined;\n const isExpanded = expanded.has(key);\n return (\n <Fragment key={key}>\n <Table.Row>\n {expandable ? (\n <Table.Cell>\n {isExpandable ? (\n <button\n type=\"button\"\n onClick={() => toggleExpand(key)}\n aria-expanded={isExpanded}\n aria-controls={`expanded-${key}`}\n aria-label={isExpanded ? \"Collapse row\" : \"Expand row\"}\n className=\"inline-flex items-center justify-center rounded-md p-0.5 hover:bg-muted\"\n >\n {isExpanded ? (\n <ChevronDown aria-hidden=\"true\" className=\"size-4\" />\n ) : (\n <ChevronRight aria-hidden=\"true\" className=\"size-4\" />\n )}\n </button>\n ) : null}\n </Table.Cell>\n ) : null}\n {columns.map((col) => (\n <Table.Cell key={col.key} align={col.align} className={col.className}>\n {col.render\n ? col.render(row)\n : String((row as Record<string, unknown>)[col.key] ?? \"\")}\n </Table.Cell>\n ))}\n {rowActions ? (\n <Table.Cell align=\"right\">\n <DropdownMenu>\n <DropdownMenu.Trigger\n aria-label=\"Row actions\"\n className={cn(\n \"inline-flex size-7 items-center justify-center rounded-md\",\n \"text-muted-foreground hover:bg-muted hover:text-foreground\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n )}\n >\n <MoreHorizontal aria-hidden=\"true\" className=\"size-4\" />\n </DropdownMenu.Trigger>\n <DropdownMenu.Content align=\"end\">{rowActions(row)}</DropdownMenu.Content>\n </DropdownMenu>\n </Table.Cell>\n ) : null}\n </Table.Row>\n {isExpanded && isExpandable ? (\n <tr id={`expanded-${key}`}>\n <td colSpan={expandedColSpan} className=\"bg-muted/30 p-4\">\n {expandedContent}\n </td>\n </tr>\n ) : null}\n </Fragment>\n );\n })}\n </Table.Body>\n </Table>\n {pagination && totalPages > 1 ? (\n <div className=\"mt-4 flex items-center justify-end\">\n <Pagination\n currentPage={currentPage + 1}\n totalPages={totalPages}\n onPageChange={handlePageChange}\n />\n </div>\n ) : null}\n </div>\n );\n}\n\nexport { DataTable };\n"
|
|
25
25
|
}
|
|
26
26
|
]
|
|
27
27
|
}
|