@vllnt/ui 0.2.1-canary.bf41f62 → 0.2.1-canary.c065357

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 (34) hide show
  1. package/README.md +38 -24
  2. package/dist/components/border-beam/border-beam.js +2 -2
  3. package/dist/components/candlestick-chart/candlestick-chart.js +3 -3
  4. package/dist/components/canvas-shell/canvas-foundation-demo.js +5 -5
  5. package/dist/components/canvas-shell/canvas-shell.js +2 -2
  6. package/dist/components/chat-dock-section/chat-dock-section.js +1 -1
  7. package/dist/components/code-block/code-block.js +2 -1
  8. package/dist/components/code-playground/code-playground.js +1 -0
  9. package/dist/components/glass-panel/glass-panel.js +1 -1
  10. package/dist/components/index.js +6 -0
  11. package/dist/components/infinite-plane/infinite-plane.js +2 -2
  12. package/dist/components/kbd/kbd.js +1 -1
  13. package/dist/components/mdx-content/mdx-content.js +2 -1
  14. package/dist/components/object-card/object-card.js +2 -2
  15. package/dist/components/overview-board/overview-board.js +1 -1
  16. package/dist/components/sidebar-toggle/sidebar-toggle.js +2 -0
  17. package/dist/components/static-code/index.js +4 -0
  18. package/dist/components/static-code/static-code-copy.js +29 -0
  19. package/dist/components/static-code/static-code.js +41 -0
  20. package/dist/components/terminal/terminal.js +2 -0
  21. package/dist/components/theme-preset-provider/index.js +4 -0
  22. package/dist/components/theme-preset-provider/theme-preset-provider.js +55 -0
  23. package/dist/components/theme-switcher/index.js +4 -0
  24. package/dist/components/theme-switcher/theme-switcher.js +43 -0
  25. package/dist/components/viewport-bookmarks/viewport-bookmarks.js +3 -1
  26. package/dist/index.d.ts +89 -1
  27. package/dist/index.js +19 -1
  28. package/dist/lib/theme-presets.js +32 -0
  29. package/dist/lib/use-theme-preset.js +94 -0
  30. package/dist/tailwind-preset.js +19 -19
  31. package/package.json +3 -2
  32. package/styles.css +38 -38
  33. package/themes/default.css +39 -39
  34. package/themes/presets.css +547 -0
package/README.md CHANGED
@@ -44,7 +44,7 @@ export default {
44
44
  ```
45
45
 
46
46
  The preset configures:
47
- - **Colors**: `primary`, `secondary`, `muted`, `accent`, `destructive`, `background`, `foreground`, `card`, `popover`, `border`, `input`, `ring` — all as `hsl(var(--name))` for theming
47
+ - **Colors**: `primary`, `secondary`, `muted`, `accent`, `destructive`, `background`, `foreground`, `card`, `popover`, `border`, `input`, `ring` — all as `oklch(var(--name) / <alpha-value>)` for theming
48
48
  - **Border radius**: `lg`, `md`, `sm` via `--radius` CSS variable
49
49
  - **Animations**: `accordion-down`, `accordion-up`, `shimmer`
50
50
  - **Font**: `mono` family via `--font-mono` variable
@@ -83,45 +83,59 @@ export function Example() {
83
83
  | `@vllnt/ui/tailwind-preset` | Tailwind CSS preset config |
84
84
  | `@vllnt/ui/styles.css` | Full styles (Tailwind base + theme variables + utilities) |
85
85
  | `@vllnt/ui/themes/default.css` | Theme CSS variables only (no Tailwind base) |
86
+ | `@vllnt/ui/themes/presets.css` | Built-in color presets (applied via `data-theme`) |
86
87
 
87
88
  ## Theming
88
89
 
89
- All colors use HSL CSS variables. Override them after importing styles:
90
+ All colors are **OKLCH channel** CSS variables (`L C H`), consumed as
91
+ `oklch(var(--name) / <alpha-value>)`. Override them after importing styles:
90
92
 
91
93
  ```css
92
94
  /* Light theme */
93
95
  :root {
94
- --background: 0 0% 100%;
95
- --foreground: 0 0% 3.9%;
96
- --primary: 222.2 47.4% 11.2%;
97
- --primary-foreground: 210 40% 98%;
98
- --secondary: 210 40% 96.1%;
99
- --secondary-foreground: 222.2 47.4% 11.2%;
100
- --muted: 210 40% 96.1%;
101
- --muted-foreground: 215.4 16.3% 46.9%;
102
- --accent: 210 40% 96.1%;
103
- --accent-foreground: 222.2 47.4% 11.2%;
104
- --destructive: 0 84.2% 60.2%;
105
- --destructive-foreground: 0 0% 98%;
106
- --border: 214.3 31.8% 91.4%;
107
- --input: 214.3 31.8% 91.4%;
108
- --ring: 222.2 84% 4.9%;
96
+ --background: 1 0 0;
97
+ --foreground: 0.1445 0 0;
98
+ --primary: 0.2044 0 0;
99
+ --primary-foreground: 0.9848 0 0;
100
+ --secondary: 0.9703 0 0;
101
+ --secondary-foreground: 0.2044 0 0;
102
+ --muted: 0.9703 0 0;
103
+ --muted-foreground: 0.5555 0 0;
104
+ --accent: 0.9703 0 0;
105
+ --accent-foreground: 0.2044 0 0;
106
+ --destructive: 0.6368 0.2078 25.326;
107
+ --destructive-foreground: 0.9848 0 0;
108
+ --border: 0.9219 0 0;
109
+ --input: 0.9219 0 0;
110
+ --ring: 0.1445 0 0;
109
111
  --radius: 0.5rem;
110
- --card: 0 0% 100%;
111
- --card-foreground: 0 0% 3.9%;
112
- --popover: 0 0% 100%;
113
- --popover-foreground: 0 0% 3.9%;
112
+ --card: 1 0 0;
113
+ --card-foreground: 0.1445 0 0;
114
+ --popover: 1 0 0;
115
+ --popover-foreground: 0.1445 0 0;
114
116
  }
115
117
 
116
118
  /* Dark theme (applied via class="dark" on html/body) */
117
119
  .dark {
118
- --background: 0 0% 3.9%;
119
- --foreground: 0 0% 98%;
120
+ --background: 0 0 0;
121
+ --foreground: 0.9848 0 0;
120
122
  /* ... override other variables */
121
123
  }
122
124
  ```
123
125
 
124
- Use `@vllnt/ui/themes/default.css` instead of `@vllnt/ui/styles.css` if you only want the variables without Tailwind base layer styles.
126
+ Use `@vllnt/ui/themes/default.css` instead of `@vllnt/ui/styles.css` if you only
127
+ want the variables without the Tailwind base layer.
128
+
129
+ ### Runtime presets & the theme editor
130
+
131
+ Import `@vllnt/ui/themes/presets.css` and switch between built-in color presets
132
+ at runtime with `<ThemeSwitcher />` (or `setThemePreset` / `useThemePreset`),
133
+ which set `data-theme` on the document root. Render `<ThemePresetProvider />`
134
+ once near the root to restore the saved preset before paint.
135
+
136
+ To design and export your own theme, use the visual editor at
137
+ [`/themes`](https://ui.vllnt.ai/themes) — it exports a CSS block, a
138
+ `npx shadcn add` command, or design tokens.
125
139
 
126
140
  ## Component Patterns
127
141
 
@@ -5,8 +5,8 @@ const BorderBeam = React.forwardRef(
5
5
  ({
6
6
  borderWidth = 1,
7
7
  className,
8
- colorFrom = "hsl(var(--primary) / 0.85)",
9
- colorTo = "hsl(var(--ring) / 0.25)",
8
+ colorFrom = "oklch(var(--primary) / 0.85)",
9
+ colorTo = "oklch(var(--ring) / 0.25)",
10
10
  delay = 0,
11
11
  duration = 6,
12
12
  reverse = false,
@@ -59,7 +59,7 @@ function PriceGrid({
59
59
  /* @__PURE__ */ jsx(
60
60
  "line",
61
61
  {
62
- stroke: "hsl(var(--border))",
62
+ stroke: "oklch(var(--border))",
63
63
  strokeDasharray: "4 6",
64
64
  strokeOpacity: "0.8",
65
65
  x1: "0",
@@ -71,7 +71,7 @@ function PriceGrid({
71
71
  /* @__PURE__ */ jsx(
72
72
  "text",
73
73
  {
74
- fill: "hsl(var(--muted-foreground))",
74
+ fill: "oklch(var(--muted-foreground))",
75
75
  fontSize: "11",
76
76
  textAnchor: "end",
77
77
  x: width - 6,
@@ -127,7 +127,7 @@ function CandleMarks({
127
127
  /* @__PURE__ */ jsx(
128
128
  "text",
129
129
  {
130
- fill: "hsl(var(--muted-foreground))",
130
+ fill: "oklch(var(--muted-foreground))",
131
131
  fontSize: "11",
132
132
  textAnchor: "middle",
133
133
  x: centerX,
@@ -85,7 +85,7 @@ function DemoRightBar() {
85
85
  ]
86
86
  }
87
87
  ),
88
- /* @__PURE__ */ jsxs("div", { className: "rounded-2xl border border-border/70 bg-background/75 p-4 shadow-[0_10px_35px_hsl(var(--foreground)/0.06)] backdrop-blur-xl", children: [
88
+ /* @__PURE__ */ jsxs("div", { className: "rounded-2xl border border-border/70 bg-background/75 p-4 shadow-[0_10px_35px_oklch(var(--foreground)/0.06)] backdrop-blur-xl", children: [
89
89
  /* @__PURE__ */ jsx("div", { className: "text-[11px] font-medium uppercase tracking-[0.24em] text-muted-foreground", children: "Selected context" }),
90
90
  /* @__PURE__ */ jsx("div", { className: "mt-2 text-sm font-medium text-foreground", children: "Inbox triage" }),
91
91
  /* @__PURE__ */ jsx("div", { className: "mt-2 text-sm leading-6 text-muted-foreground", children: "Landing route keeps the assistant close to the operational queue instead of taking over the center canvas." })
@@ -111,8 +111,8 @@ function DemoBottomBar() {
111
111
  ) });
112
112
  }
113
113
  function DemoCanvasObjects() {
114
- return /* @__PURE__ */ jsxs("div", { className: "relative h-[1200px] w-[1600px] overflow-hidden rounded-[28px] border border-border/12 bg-[radial-gradient(circle_at_top,hsl(var(--primary)/0.07),transparent_38%),linear-gradient(180deg,hsl(var(--background)/0.18),hsl(var(--background)/0.04))]", children: [
115
- /* @__PURE__ */ jsxs("div", { className: "absolute left-[14%] top-[22%] w-72 rounded-[2rem] border border-border/18 bg-background/18 p-5 shadow-[0_22px_80px_hsl(var(--foreground)/0.05)] backdrop-blur-xl", children: [
114
+ return /* @__PURE__ */ jsxs("div", { className: "relative h-[1200px] w-[1600px] overflow-hidden rounded-[28px] border border-border/12 bg-[radial-gradient(circle_at_top,oklch(var(--primary)/0.07),transparent_38%),linear-gradient(180deg,oklch(var(--background)/0.18),oklch(var(--background)/0.04))]", children: [
115
+ /* @__PURE__ */ jsxs("div", { className: "absolute left-[14%] top-[22%] w-72 rounded-[2rem] border border-border/18 bg-background/18 p-5 shadow-[0_22px_80px_oklch(var(--foreground)/0.05)] backdrop-blur-xl", children: [
116
116
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
117
117
  /* @__PURE__ */ jsx("div", { className: "flex size-11 items-center justify-center rounded-2xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(Layers3, { className: "size-5" }) }),
118
118
  /* @__PURE__ */ jsx("div", { className: "size-2 rounded-full bg-primary/50" })
@@ -126,7 +126,7 @@ function DemoCanvasObjects() {
126
126
  /* @__PURE__ */ jsx("div", { className: "h-10 rounded-[1.25rem] border border-border/18 bg-background/30" })
127
127
  ] })
128
128
  ] }),
129
- /* @__PURE__ */ jsxs("div", { className: "absolute right-[15%] top-[18%] w-56 rounded-[1.75rem] border border-border/18 bg-background/14 p-4 shadow-[0_18px_60px_hsl(var(--foreground)/0.04)] backdrop-blur-xl", children: [
129
+ /* @__PURE__ */ jsxs("div", { className: "absolute right-[15%] top-[18%] w-56 rounded-[1.75rem] border border-border/18 bg-background/14 p-4 shadow-[0_18px_60px_oklch(var(--foreground)/0.04)] backdrop-blur-xl", children: [
130
130
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
131
131
  /* @__PURE__ */ jsx("div", { className: "flex size-10 items-center justify-center rounded-2xl bg-background/60 text-foreground/70", children: /* @__PURE__ */ jsx(Activity, { className: "size-4" }) }),
132
132
  /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5", children: [
@@ -144,7 +144,7 @@ function DemoCanvasObjects() {
144
144
  ] })
145
145
  ] })
146
146
  ] }),
147
- /* @__PURE__ */ jsxs("div", { className: "absolute bottom-[18%] left-[22%] w-64 rounded-[1.75rem] border border-border/18 bg-background/12 p-4 shadow-[0_18px_60px_hsl(var(--foreground)/0.04)] backdrop-blur-xl", children: [
147
+ /* @__PURE__ */ jsxs("div", { className: "absolute bottom-[18%] left-[22%] w-64 rounded-[1.75rem] border border-border/18 bg-background/12 p-4 shadow-[0_18px_60px_oklch(var(--foreground)/0.04)] backdrop-blur-xl", children: [
148
148
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
149
149
  /* @__PURE__ */ jsx("div", { className: "flex size-10 items-center justify-center rounded-2xl bg-background/55 text-foreground/70", children: /* @__PURE__ */ jsx(Sparkles, { className: "size-4" }) }),
150
150
  /* @__PURE__ */ jsx("div", { className: "h-7 w-16 rounded-full border border-border/18 bg-background/38" })
@@ -214,14 +214,14 @@ function renderFloatingCanvasShell({
214
214
  "section",
215
215
  {
216
216
  className: cn(
217
- "relative isolate flex min-h-[720px] w-full overflow-hidden bg-[radial-gradient(circle_at_top,hsl(var(--background)/0.94),hsl(var(--muted)/0.6))]",
217
+ "relative isolate flex min-h-[720px] w-full overflow-hidden bg-[radial-gradient(circle_at_top,oklch(var(--background)/0.94),oklch(var(--muted)/0.6))]",
218
218
  className
219
219
  ),
220
220
  ref,
221
221
  style: mergedStyle,
222
222
  ...props,
223
223
  children: [
224
- /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[linear-gradient(180deg,hsl(var(--background)/0.94),hsl(var(--background)/0.8))]" }),
224
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[linear-gradient(180deg,oklch(var(--background)/0.94),oklch(var(--background)/0.8))]" }),
225
225
  /* @__PURE__ */ jsx(
226
226
  CanvasShellChromeBefore,
227
227
  {
@@ -15,7 +15,7 @@ const ChatDockSection = forwardRef(
15
15
  "section",
16
16
  {
17
17
  className: cn(
18
- "flex flex-col gap-4 rounded-2xl border border-border/70 bg-background/75 p-4 shadow-[0_10px_35px_hsl(var(--foreground)/0.06)] backdrop-blur-xl",
18
+ "flex flex-col gap-4 rounded-2xl border border-border/70 bg-background/75 p-4 shadow-[0_10px_35px_oklch(var(--foreground)/0.06)] backdrop-blur-xl",
19
19
  className
20
20
  ),
21
21
  ref,
@@ -90,7 +90,7 @@ function CodeBlock({
90
90
  }
91
91
  },
92
92
  customStyle: {
93
- background: "hsl(var(--background))",
93
+ background: "oklch(var(--background))",
94
94
  fontSize: "0.875rem",
95
95
  margin: 0,
96
96
  minWidth: "fit-content",
@@ -107,6 +107,7 @@ function CodeBlock({
107
107
  /* @__PURE__ */ jsx(
108
108
  Button,
109
109
  {
110
+ "aria-label": copied ? "Copied" : "Copy code",
110
111
  className: "size-8",
111
112
  onClick: handleCopy,
112
113
  size: "icon",
@@ -49,6 +49,7 @@ function CodePlayground({
49
49
  /* @__PURE__ */ jsx(
50
50
  Button,
51
51
  {
52
+ "aria-label": copied ? "Copied" : "Copy code",
52
53
  className: "size-8",
53
54
  onClick: handleCopy,
54
55
  size: "icon",
@@ -6,7 +6,7 @@ const GlassPanel = forwardRef(
6
6
  "div",
7
7
  {
8
8
  className: cn(
9
- "rounded-2xl border border-border/60 bg-background/70 shadow-[0_12px_40px_hsl(var(--foreground)/0.08)] backdrop-blur-xl",
9
+ "rounded-2xl border border-border/60 bg-background/70 shadow-[0_12px_40px_oklch(var(--foreground)/0.08)] backdrop-blur-xl",
10
10
  className
11
11
  ),
12
12
  ref,
@@ -325,6 +325,7 @@ import {
325
325
  useAgentStepStatus
326
326
  } from "./agent-activity";
327
327
  import { StatCard, statCardVariants } from "./stat-card";
328
+ import { StaticCode } from "./static-code";
328
329
  import {
329
330
  dotVariants,
330
331
  StatusIndicator,
@@ -467,7 +468,9 @@ import {
467
468
  } from "./share-section";
468
469
  import { SearchDialog } from "./search-dialog";
469
470
  import { LangProvider } from "./lang-provider";
471
+ import { ThemePresetProvider } from "./theme-preset-provider";
470
472
  import { ThemeProvider } from "./theme-provider";
473
+ import { ThemeSwitcher } from "./theme-switcher";
471
474
  import { ThemeToggle } from "./theme-toggle";
472
475
  import {
473
476
  CandlestickChart
@@ -1263,6 +1266,7 @@ export {
1263
1266
  Spinner,
1264
1267
  StatCard,
1265
1268
  StateBadgeOverlay,
1269
+ StaticCode,
1266
1270
  StatusBoard,
1267
1271
  StatusIndicator,
1268
1272
  Step,
@@ -1293,7 +1297,9 @@ export {
1293
1297
  TagsInput,
1294
1298
  Terminal,
1295
1299
  Textarea,
1300
+ ThemePresetProvider,
1296
1301
  ThemeProvider,
1302
+ ThemeSwitcher,
1297
1303
  ThemeToggle,
1298
1304
  ThinkingBlock,
1299
1305
  ThreadBubble,
@@ -25,13 +25,13 @@ const buildBackground = (input) => {
25
25
  const pos = `${input.translate.x}px ${input.translate.y}px`;
26
26
  if (input.pattern === "grid") {
27
27
  return {
28
- backgroundImage: "linear-gradient(to right, hsl(var(--border)) 1px, transparent 1px), linear-gradient(to bottom, hsl(var(--border)) 1px, transparent 1px)",
28
+ backgroundImage: "linear-gradient(to right, oklch(var(--border)) 1px, transparent 1px), linear-gradient(to bottom, oklch(var(--border)) 1px, transparent 1px)",
29
29
  backgroundPosition: pos,
30
30
  backgroundSize: `${size}px ${size}px`
31
31
  };
32
32
  }
33
33
  return {
34
- backgroundImage: "radial-gradient(circle, hsl(var(--border)) 1px, transparent 1px)",
34
+ backgroundImage: "radial-gradient(circle, oklch(var(--border)) 1px, transparent 1px)",
35
35
  backgroundPosition: pos,
36
36
  backgroundSize: `${size}px ${size}px`
37
37
  };
@@ -7,7 +7,7 @@ import {
7
7
  import { cva } from "class-variance-authority";
8
8
  import { cn } from "../../lib/utils";
9
9
  const kbdVariants = cva(
10
- "inline-flex select-none items-center justify-center rounded border border-border bg-muted font-mono font-medium text-foreground shadow-[0_1px_0_0_hsl(var(--border))] [&>svg]:h-3 [&>svg]:w-3",
10
+ "inline-flex select-none items-center justify-center rounded border border-border bg-muted font-mono font-medium text-foreground shadow-[0_1px_0_0_oklch(var(--border))] [&>svg]:h-3 [&>svg]:w-3",
11
11
  {
12
12
  defaultVariants: {
13
13
  size: "md"
@@ -4,6 +4,7 @@ import * as runtime from "react/jsx-runtime";
4
4
  import ReactMarkdown from "react-markdown";
5
5
  import remarkGfm from "remark-gfm";
6
6
  import { CodeBlock } from "../code-block/code-block";
7
+ import { StaticCode } from "../static-code/static-code";
7
8
  const MDXComponents = {
8
9
  a: ({ children, href, ...props }) => /* @__PURE__ */ jsx(
9
10
  "a",
@@ -26,7 +27,7 @@ const MDXComponents = {
26
27
  if (typeof className === "string" && className.startsWith("language-")) {
27
28
  const language = className.replace(/^language-/, "");
28
29
  const text = typeof children === "string" ? children.replace(/\n$/, "") : String(children ?? "");
29
- return /* @__PURE__ */ jsx(CodeBlock, { language, children: text });
30
+ return /* @__PURE__ */ jsx(StaticCode, { code: text, language });
30
31
  }
31
32
  return /* @__PURE__ */ jsx(
32
33
  "code",
@@ -94,14 +94,14 @@ const ObjectCard = forwardRef(
94
94
  "article",
95
95
  {
96
96
  className: cn(
97
- "group relative flex min-w-[320px] max-w-[420px] flex-col gap-4 rounded-[1.5rem] border border-border/70 bg-[linear-gradient(180deg,hsl(var(--background)),hsl(var(--muted)/0.22))] p-5 shadow-[0_24px_80px_hsl(var(--foreground)/0.08)] transition-transform duration-200 hover:-translate-y-0.5",
97
+ "group relative flex min-w-[320px] max-w-[420px] flex-col gap-4 rounded-[1.5rem] border border-border/70 bg-[linear-gradient(180deg,oklch(var(--background)),oklch(var(--muted)/0.22))] p-5 shadow-[0_24px_80px_oklch(var(--foreground)/0.08)] transition-transform duration-200 hover:-translate-y-0.5",
98
98
  className
99
99
  ),
100
100
  "data-state": state,
101
101
  ref,
102
102
  ...props,
103
103
  children: [
104
- /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-x-5 top-0 h-px bg-[linear-gradient(90deg,transparent,hsl(var(--foreground)/0.22),transparent)]" }),
104
+ /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-x-5 top-0 h-px bg-[linear-gradient(90deg,transparent,oklch(var(--foreground)/0.22),transparent)]" }),
105
105
  /* @__PURE__ */ jsx(
106
106
  ObjectCardHeader,
107
107
  {
@@ -28,7 +28,7 @@ const OverviewCard = forwardRef(
28
28
  "section",
29
29
  {
30
30
  className: cn(
31
- "flex min-h-[172px] flex-col gap-4 rounded-2xl border p-5 shadow-[0_8px_30px_hsl(var(--foreground)/0.06)] backdrop-blur-xl",
31
+ "flex min-h-[172px] flex-col gap-4 rounded-2xl border p-5 shadow-[0_8px_30px_oklch(var(--foreground)/0.06)] backdrop-blur-xl",
32
32
  toneClassNames[tone],
33
33
  className
34
34
  ),
@@ -12,6 +12,7 @@ function SidebarToggle({
12
12
  /* @__PURE__ */ jsx(
13
13
  Button,
14
14
  {
15
+ "aria-label": open ? "Close menu" : "Open menu",
15
16
  className: cn("lg:hidden", className),
16
17
  onClick: onToggle,
17
18
  size: "icon",
@@ -22,6 +23,7 @@ function SidebarToggle({
22
23
  /* @__PURE__ */ jsx(
23
24
  Button,
24
25
  {
26
+ "aria-label": "Toggle sidebar",
25
27
  className: cn("hidden lg:flex", className),
26
28
  onClick: onToggle,
27
29
  size: "icon",
@@ -0,0 +1,4 @@
1
+ import { StaticCode } from "./static-code";
2
+ export {
3
+ StaticCode
4
+ };
@@ -0,0 +1,29 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { Check, Copy } from "lucide-react";
5
+ import { Button } from "../button/button";
6
+ function StaticCodeCopy({ value }) {
7
+ const [copied, setCopied] = useState(false);
8
+ const handleCopy = async () => {
9
+ await navigator.clipboard.writeText(value);
10
+ setCopied(true);
11
+ setTimeout(() => {
12
+ setCopied(false);
13
+ }, 2e3);
14
+ };
15
+ return /* @__PURE__ */ jsx(
16
+ Button,
17
+ {
18
+ "aria-label": copied ? "Copied" : "Copy code",
19
+ className: "absolute right-2 top-2 size-8",
20
+ onClick: handleCopy,
21
+ size: "icon",
22
+ variant: "ghost",
23
+ children: copied ? /* @__PURE__ */ jsx(Check, { className: "size-3" }) : /* @__PURE__ */ jsx(Copy, { className: "size-3" })
24
+ }
25
+ );
26
+ }
27
+ export {
28
+ StaticCodeCopy
29
+ };
@@ -0,0 +1,41 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
3
+ import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism";
4
+ import { cn } from "../../lib/utils";
5
+ import { StaticCodeCopy } from "./static-code-copy";
6
+ function StaticCode({
7
+ className,
8
+ code,
9
+ language = "tsx"
10
+ }) {
11
+ return /* @__PURE__ */ jsxs(
12
+ "div",
13
+ {
14
+ className: cn(
15
+ "group relative overflow-x-auto rounded-lg border border-border bg-[#282c34]",
16
+ className
17
+ ),
18
+ children: [
19
+ /* @__PURE__ */ jsx(
20
+ SyntaxHighlighter,
21
+ {
22
+ customStyle: {
23
+ background: "transparent",
24
+ fontSize: "0.875rem",
25
+ margin: 0,
26
+ padding: "1rem"
27
+ },
28
+ language,
29
+ PreTag: "div",
30
+ style: oneDark,
31
+ children: code
32
+ }
33
+ ),
34
+ /* @__PURE__ */ jsx(StaticCodeCopy, { value: code })
35
+ ]
36
+ }
37
+ );
38
+ }
39
+ export {
40
+ StaticCode
41
+ };
@@ -59,6 +59,7 @@ function Terminal({
59
59
  copyable && commands.length > 0 ? /* @__PURE__ */ jsx(
60
60
  Button,
61
61
  {
62
+ "aria-label": copied ? "Copied" : "Copy commands",
62
63
  className: "absolute top-2 right-2 size-8 bg-zinc-800 hover:bg-zinc-700 text-zinc-300",
63
64
  onClick: handleCopy,
64
65
  size: "icon",
@@ -125,6 +126,7 @@ function SimpleTerminal({
125
126
  commands.length > 0 && /* @__PURE__ */ jsx(
126
127
  Button,
127
128
  {
129
+ "aria-label": copied ? "Copied" : "Copy commands",
128
130
  className: "absolute top-2 right-2 size-8 bg-zinc-800 hover:bg-zinc-700 text-zinc-300",
129
131
  onClick: handleCopy,
130
132
  size: "icon",
@@ -0,0 +1,4 @@
1
+ import { ThemePresetProvider } from "./theme-preset-provider";
2
+ export {
3
+ ThemePresetProvider
4
+ };
@@ -0,0 +1,55 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect } from "react";
4
+ import {
5
+ CUSTOM_THEME_NAME,
6
+ DEFAULT_THEME_PRESET,
7
+ isThemePresetName,
8
+ THEME_CUSTOM_CSS_STORAGE_KEY,
9
+ THEME_CUSTOM_STYLE_ID,
10
+ THEME_PRESET_STORAGE_KEY
11
+ } from "../../lib/theme-presets";
12
+ import { setThemePreset } from "../../lib/use-theme-preset";
13
+ const FOUC_SCRIPT = `(function(){try{var v=localStorage.getItem(${JSON.stringify(
14
+ THEME_PRESET_STORAGE_KEY
15
+ )});if(!v||v===${JSON.stringify(
16
+ DEFAULT_THEME_PRESET
17
+ )})return;document.documentElement.setAttribute("data-theme",v);if(v===${JSON.stringify(
18
+ CUSTOM_THEME_NAME
19
+ )}){var c=localStorage.getItem(${JSON.stringify(
20
+ THEME_CUSTOM_CSS_STORAGE_KEY
21
+ )});if(c){var s=document.createElement("style");s.id=${JSON.stringify(
22
+ THEME_CUSTOM_STYLE_ID
23
+ )};s.textContent=c;document.head.appendChild(s);}}}catch(e){}})();`;
24
+ function ThemePresetProvider({
25
+ children,
26
+ defaultPreset
27
+ }) {
28
+ useEffect(() => {
29
+ if (!defaultPreset || defaultPreset === DEFAULT_THEME_PRESET) {
30
+ return;
31
+ }
32
+ let stored = null;
33
+ try {
34
+ stored = window.localStorage.getItem(THEME_PRESET_STORAGE_KEY);
35
+ } catch {
36
+ stored = null;
37
+ }
38
+ if (!stored && isThemePresetName(defaultPreset)) {
39
+ setThemePreset(defaultPreset);
40
+ }
41
+ }, [defaultPreset]);
42
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
43
+ /* @__PURE__ */ jsx(
44
+ "script",
45
+ {
46
+ dangerouslySetInnerHTML: { __html: FOUC_SCRIPT },
47
+ suppressHydrationWarning: true
48
+ }
49
+ ),
50
+ children
51
+ ] });
52
+ }
53
+ export {
54
+ ThemePresetProvider
55
+ };
@@ -0,0 +1,4 @@
1
+ import { ThemeSwitcher } from "./theme-switcher";
2
+ export {
3
+ ThemeSwitcher
4
+ };
@@ -0,0 +1,43 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { useMounted } from "../../lib/use-mounted";
4
+ import { useThemePreset } from "../../lib/use-theme-preset";
5
+ import { cn } from "../../lib/utils";
6
+ function ThemeSwitcher({ className }) {
7
+ const mounted = useMounted();
8
+ const { preset, presets, setPreset } = useThemePreset();
9
+ return /* @__PURE__ */ jsx(
10
+ "div",
11
+ {
12
+ "aria-label": "Theme preset",
13
+ className: cn("flex items-center gap-1.5", className),
14
+ role: "radiogroup",
15
+ children: presets.map((item) => {
16
+ const active = mounted && preset === item.name;
17
+ return /* @__PURE__ */ jsx(
18
+ "button",
19
+ {
20
+ "aria-checked": active,
21
+ "aria-label": item.label,
22
+ className: cn(
23
+ "size-6 rounded-full border transition-transform hover:scale-110",
24
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
25
+ active ? "border-foreground ring-2 ring-ring ring-offset-2 ring-offset-background" : "border-border"
26
+ ),
27
+ onClick: () => {
28
+ setPreset(item.name);
29
+ },
30
+ role: "radio",
31
+ style: { backgroundColor: item.swatch },
32
+ title: item.label,
33
+ type: "button"
34
+ },
35
+ item.name
36
+ );
37
+ })
38
+ }
39
+ );
40
+ }
41
+ export {
42
+ ThemeSwitcher
43
+ };
@@ -51,7 +51,9 @@ const RowBody = (props) => {
51
51
  {
52
52
  "aria-hidden": "true",
53
53
  className: "size-1.5 rounded-full",
54
- style: { backgroundColor: bookmark.color ?? "hsl(var(--foreground))" }
54
+ style: {
55
+ backgroundColor: bookmark.color ?? "oklch(var(--foreground))"
56
+ }
55
57
  }
56
58
  ),
57
59
  /* @__PURE__ */ jsxs("span", { className: "flex flex-1 flex-col text-left", children: [