@ramme-io/ui 4.0.1 → 4.1.1

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 (83) hide show
  1. package/README.md +159 -7
  2. package/dist/blocks/ai/Actions.js +2 -2
  3. package/dist/blocks/ai/PromptInput.js +6 -6
  4. package/dist/component-registry.js +389 -359
  5. package/dist/components/ai/AIChatThread/AIChatThread.js +81 -73
  6. package/dist/components/ai/ArchitectureReferenceCard/ArchitectureReferenceCard.d.ts +28 -0
  7. package/dist/components/ai/ArchitectureReferenceCard/ArchitectureReferenceCard.js +70 -0
  8. package/dist/components/ai/ArchitectureReferenceCard/index.d.ts +2 -0
  9. package/dist/components/ai/CounterpartyGraphCard/CounterpartyGraphCard.d.ts +30 -0
  10. package/dist/components/ai/CounterpartyGraphCard/CounterpartyGraphCard.js +82 -0
  11. package/dist/components/ai/CounterpartyGraphCard/index.d.ts +2 -0
  12. package/dist/components/ai/DivergenceMetricsCard/DivergenceMetricsCard.d.ts +29 -0
  13. package/dist/components/ai/DivergenceMetricsCard/DivergenceMetricsCard.js +69 -0
  14. package/dist/components/ai/DivergenceMetricsCard/index.d.ts +2 -0
  15. package/dist/components/ai/KeyInjectionOverlay.d.ts +10 -0
  16. package/dist/components/ai/KeyInjectionOverlay.js +11 -11
  17. package/dist/components/ai/NewsSummaryCard/NewsSummaryCard.js +17 -17
  18. package/dist/components/ai/StockChart/StockChart.js +4 -4
  19. package/dist/components/auth/AuthLayout.js +4 -4
  20. package/dist/components/auth/LoginPage.d.ts +22 -0
  21. package/dist/components/auth/LoginPage.js +50 -42
  22. package/dist/components/data-display/CodeBlock.d.ts +33 -7
  23. package/dist/components/data-display/CodeBlock.js +134 -41
  24. package/dist/components/data-display/Table.js +8 -8
  25. package/dist/components/feedback/EmptyState.js +9 -9
  26. package/dist/components/feedback/ToastProvider.js +4 -4
  27. package/dist/components/forms/Checkbox.js +5 -5
  28. package/dist/components/forms/ComboBox.js +2 -2
  29. package/dist/components/forms/DatePicker.js +37 -32
  30. package/dist/components/forms/FileUpload.js +39 -37
  31. package/dist/components/forms/Input.js +19 -18
  32. package/dist/components/forms/MultiSelect.js +9 -8
  33. package/dist/components/forms/Radio.js +6 -6
  34. package/dist/components/forms/SegmentedControl.d.ts +1 -1
  35. package/dist/components/forms/SegmentedControl.js +28 -11
  36. package/dist/components/forms/Select.js +9 -8
  37. package/dist/components/forms/Textarea.d.ts +13 -11
  38. package/dist/components/forms/Textarea.js +36 -16
  39. package/dist/components/forms/ToggleSwitch.js +9 -9
  40. package/dist/components/layout/AboutRamme.d.ts +8 -0
  41. package/dist/components/layout/Accordion.js +25 -32
  42. package/dist/components/layout/AppShell.js +7 -7
  43. package/dist/components/layout/Card.js +38 -21
  44. package/dist/components/layout/DetailDrawer.js +37 -38
  45. package/dist/components/layout/Drawer.js +50 -51
  46. package/dist/components/layout/List.js +9 -9
  47. package/dist/components/layout/Modal.js +5 -5
  48. package/dist/components/layout/PageHeader.js +7 -7
  49. package/dist/components/layout/SectionHeader.js +4 -4
  50. package/dist/components/layout/SettingsContainer.d.ts +12 -0
  51. package/dist/components/layout/SettingsContainer.js +30 -18
  52. package/dist/components/layout/Sheet.d.ts +58 -0
  53. package/dist/components/layout/Sheet.js +144 -0
  54. package/dist/components/layout/Sidebar.js +1 -1
  55. package/dist/components/layout/UserNav.d.ts +13 -0
  56. package/dist/components/layout/UserNav.js +17 -17
  57. package/dist/components/molecular/ConnectorOverlay.d.ts +12 -0
  58. package/dist/components/navigation/Breadcrumbs.js +8 -8
  59. package/dist/components/navigation/Menu.js +6 -6
  60. package/dist/components/navigation/MobileNav.d.ts +44 -1
  61. package/dist/components/navigation/MobileNav.js +97 -71
  62. package/dist/components/navigation/Pagination.js +46 -41
  63. package/dist/components/navigation/Stepper.js +17 -17
  64. package/dist/components/navigation/Tabs.js +18 -11
  65. package/dist/components/theme/ThemeLab.d.ts +7 -1
  66. package/dist/components/theme/ThemeLab.js +175 -154
  67. package/dist/components/ui/icon-loader.d.ts +1 -1
  68. package/dist/contexts/ThemeContext.js +111 -82
  69. package/dist/data/mockData.d.ts +0 -3
  70. package/dist/index.css +1 -1
  71. package/dist/index.d.ts +10 -0
  72. package/dist/index.js +222 -197
  73. package/dist/lib/hooks/useBodyScrollLock.d.ts +27 -0
  74. package/dist/lib/hooks/useBodyScrollLock.js +18 -0
  75. package/dist/lib/hooks/useSheetGesture.d.ts +81 -0
  76. package/dist/lib/hooks/useSheetGesture.js +70 -0
  77. package/dist/lib/theme/themeUtils.d.ts +21 -0
  78. package/dist/lib/theme/themeUtils.js +31 -17
  79. package/dist/setupTests.d.ts +1 -0
  80. package/dist/wizardManifest.js +364 -19
  81. package/package.json +10 -4
  82. package/src/wizardManifest.ts +364 -19
  83. package/tailwind.preset.js +98 -10
@@ -1,22 +1,22 @@
1
- import { jsx as e, jsxs as r } from "react/jsx-runtime";
1
+ import { jsx as e, jsxs as t } from "react/jsx-runtime";
2
2
  import { useState as n } from "react";
3
3
  import { Button as m } from "../ui/Button.js";
4
4
  import { Input as c } from "../forms/Input.js";
5
5
  import { Card as u } from "../layout/Card.js";
6
6
  import { Icon as f } from "../ui/Icon.js";
7
- function I({ status: i, setSessionKey: o }) {
8
- const [s, a] = n(""), [l, d] = n(!0);
9
- return i !== "BYOK_REQUIRED" && i !== "KEY_EXPIRED" ? null : /* @__PURE__ */ e("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm", children: /* @__PURE__ */ e(u, { className: "w-[450px] p-6 shadow-xl border-destructive/20 border", children: /* @__PURE__ */ r("form", { onSubmit: (t) => {
10
- t.preventDefault(), s.trim() && o(s.trim());
7
+ function I({ status: i, setSessionKey: a }) {
8
+ const [s, o] = n(""), [l, d] = n(!0);
9
+ return i !== "BYOK_REQUIRED" && i !== "KEY_EXPIRED" ? null : /* @__PURE__ */ e("div", { className: "fixed inset-0 z-drawer flex items-center justify-center bg-background/80 backdrop-blur-sm", children: /* @__PURE__ */ e(u, { className: "w-[450px] p-6 shadow-xl border-destructive/20 border", children: /* @__PURE__ */ t("form", { onSubmit: (r) => {
10
+ r.preventDefault(), s.trim() && a(s.trim());
11
11
  }, className: "space-y-4", children: [
12
- /* @__PURE__ */ r("div", { className: "space-y-2", children: [
13
- /* @__PURE__ */ r("div", { className: "flex items-center gap-2 text-destructive", children: [
12
+ /* @__PURE__ */ t("div", { className: "space-y-2", children: [
13
+ /* @__PURE__ */ t("div", { className: "flex items-center gap-2 text-destructive", children: [
14
14
  /* @__PURE__ */ e(f, { name: "alert-triangle", size: 20 }),
15
15
  /* @__PURE__ */ e("h3", { className: "font-semibold text-lg", children: "AI Key Required" })
16
16
  ] }),
17
17
  /* @__PURE__ */ e("p", { className: "text-sm text-muted-foreground", children: i === "KEY_EXPIRED" ? "Your AI API key has expired or is invalid. Please provide a new key." : "This terminal requires an AI API key to function. We do not bundle keys in the client for security." })
18
18
  ] }),
19
- /* @__PURE__ */ r("div", { className: "space-y-2", children: [
19
+ /* @__PURE__ */ t("div", { className: "space-y-2", children: [
20
20
  /* @__PURE__ */ e("label", { className: "text-sm font-medium text-foreground", children: "API Key" }),
21
21
  /* @__PURE__ */ e(
22
22
  c,
@@ -24,20 +24,20 @@ function I({ status: i, setSessionKey: o }) {
24
24
  type: "password",
25
25
  placeholder: "Paste your AI provider key here...",
26
26
  value: s,
27
- onChange: (t) => a(t.target.value),
27
+ onChange: (r) => o(r.target.value),
28
28
  className: "font-mono w-full font-medium",
29
29
  autoComplete: "off"
30
30
  }
31
31
  )
32
32
  ] }),
33
- /* @__PURE__ */ r("div", { className: "flex items-center gap-2", children: [
33
+ /* @__PURE__ */ t("div", { className: "flex items-center gap-2", children: [
34
34
  /* @__PURE__ */ e(
35
35
  "input",
36
36
  {
37
37
  type: "checkbox",
38
38
  id: "session-only",
39
39
  checked: l,
40
- onChange: (t) => d(t.target.checked),
40
+ onChange: (r) => d(r.target.checked),
41
41
  className: "rounded border-input text-primary focus:ring-primary"
42
42
  }
43
43
  ),
@@ -1,41 +1,41 @@
1
1
  import { jsxs as t, jsx as e } from "react/jsx-runtime";
2
2
  import { useState as i, useEffect as g } from "react";
3
3
  import { Card as N, CardHeader as b, CardTitle as w, CardContent as y } from "../../layout/Card.js";
4
- import { Icon as a } from "../../ui/Icon.js";
4
+ import { Icon as s } from "../../ui/Icon.js";
5
5
  const d = { BASE_URL: "/", DEV: !1, MODE: "production", PROD: !0, SSR: !1 }, k = ({ params: u }) => {
6
- const { ticker: n } = u, [c, x] = i([]), [l, m] = i(!0), [o, p] = i(null);
6
+ const { ticker: a } = u, [c, x] = i([]), [l, m] = i(!0), [n, p] = i(null);
7
7
  return g(() => {
8
8
  (async () => {
9
9
  try {
10
10
  m(!0), p(null);
11
- const s = (d == null ? void 0 : d.VITE_API_URL) ?? "http://localhost:8000", f = await fetch(`${s}/companies/${n.toUpperCase()}/news?limit=5`);
11
+ const o = (d == null ? void 0 : d.VITE_API_URL) ?? "http://localhost:8000", f = await fetch(`${o}/companies/${a.toUpperCase()}/news?limit=5`);
12
12
  if (!f.ok)
13
- throw new Error(`No news available for ${n.toUpperCase()}`);
13
+ throw new Error(`No news available for ${a.toUpperCase()}`);
14
14
  const h = await f.json();
15
15
  x(h);
16
- } catch (s) {
17
- p(s instanceof Error ? s.message : "Failed to fetch news");
16
+ } catch (o) {
17
+ p(o instanceof Error ? o.message : "Failed to fetch news");
18
18
  } finally {
19
19
  m(!1);
20
20
  }
21
21
  })();
22
- }, [n]), /* @__PURE__ */ t(N, { className: "w-full min-w-[320px] max-w-2xl shadow-lg border-primary/20", children: [
22
+ }, [a]), /* @__PURE__ */ t(N, { className: "w-full min-w-[320px] max-w-2xl shadow-lg border-primary/20", children: [
23
23
  /* @__PURE__ */ e(b, { className: "pb-3", children: /* @__PURE__ */ t(w, { className: "text-sm font-medium text-muted-foreground flex items-center gap-2", children: [
24
- /* @__PURE__ */ e(a, { name: "newspaper", size: 14 }),
24
+ /* @__PURE__ */ e(s, { name: "newspaper", size: 14 }),
25
25
  "Recent News · ",
26
- n.toUpperCase()
26
+ a.toUpperCase()
27
27
  ] }) }),
28
28
  /* @__PURE__ */ t(y, { className: "pt-0", children: [
29
- l && /* @__PURE__ */ e("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ e(a, { name: "loader", size: 24, className: "animate-spin text-muted-foreground" }) }),
30
- o && /* @__PURE__ */ t("div", { className: "flex items-start gap-2 rounded-lg border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: [
31
- /* @__PURE__ */ e(a, { name: "alert-circle", size: 14, className: "mt-px shrink-0" }),
32
- /* @__PURE__ */ e("span", { children: o })
29
+ l && /* @__PURE__ */ e("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ e(s, { name: "loader", size: 24, className: "animate-spin text-muted-foreground" }) }),
30
+ n && /* @__PURE__ */ t("div", { className: "flex items-start gap-2 rounded-lg border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: [
31
+ /* @__PURE__ */ e(s, { name: "alert-circle", size: 14, className: "mt-px shrink-0" }),
32
+ /* @__PURE__ */ e("span", { children: n })
33
33
  ] }),
34
- c.length === 0 && !l && !o && /* @__PURE__ */ t("div", { className: "flex flex-col items-center justify-center py-8 gap-2 text-muted-foreground", children: [
35
- /* @__PURE__ */ e(a, { name: "inbox", size: 24, opacity: 0.5 }),
34
+ c.length === 0 && !l && !n && /* @__PURE__ */ t("div", { className: "flex flex-col items-center justify-center py-8 gap-2 text-muted-foreground", children: [
35
+ /* @__PURE__ */ e(s, { name: "inbox", size: 24, opacity: 0.5 }),
36
36
  /* @__PURE__ */ e("p", { className: "text-sm", children: "No recent news available" })
37
37
  ] }),
38
- c.length > 0 && !l && !o && /* @__PURE__ */ e("div", { className: "space-y-3", children: c.map((r, s) => /* @__PURE__ */ e(
38
+ c.length > 0 && !l && !n && /* @__PURE__ */ e("div", { className: "space-y-3", children: c.map((r) => /* @__PURE__ */ e(
39
39
  "div",
40
40
  {
41
41
  className: "border-b border-border/50 pb-3 last:border-0 last:pb-0",
@@ -64,7 +64,7 @@ const d = { BASE_URL: "/", DEV: !1, MODE: "production", PROD: !0, SSR: !1 }, k =
64
64
  ] })
65
65
  ] })
66
66
  ] }),
67
- r.url && /* @__PURE__ */ e(a, { name: "external-link", size: 12, className: "text-muted-foreground/50 flex-shrink-0 mt-0.5" })
67
+ r.url && /* @__PURE__ */ e(s, { name: "external-link", size: 12, className: "text-muted-foreground/50 flex-shrink-0 mt-0.5" })
68
68
  ] })
69
69
  },
70
70
  r.id
@@ -1,6 +1,6 @@
1
1
  import { jsxs as a, jsx as e } from "react/jsx-runtime";
2
- import F from "react";
3
- import { ResponsiveContainer as W, ComposedChart as _, CartesianGrid as K, XAxis as U, YAxis as A, Tooltip as $, Legend as j, Line as S, Bar as z } from "recharts";
2
+ import _ from "react";
3
+ import { ResponsiveContainer as F, ComposedChart as W, CartesianGrid as K, XAxis as U, YAxis as A, Tooltip as $, Legend as j, Line as S, Bar as z } from "recharts";
4
4
  import { Card as E, CardHeader as H, CardTitle as L, CardContent as V } from "../../layout/Card.js";
5
5
  import { useChartTheme as Y } from "../../../lib/hooks/useChartTheme.js";
6
6
  function G(s, p, C) {
@@ -59,7 +59,7 @@ const b = {
59
59
  data: C
60
60
  }) => {
61
61
  var y, k;
62
- const { ticker: N, timeRange: M, metrics: c } = s, { textColor: g, gridColor: l } = Y(), h = N || s.symbol || "UNKNOWN", r = M || "1M", o = c && Array.isArray(c) && c.length > 0 ? c : ["price", "volume"], x = F.useMemo(
62
+ const { ticker: N, timeRange: M, metrics: c } = s, { textColor: g, gridColor: l } = Y(), h = N || s.symbol || "UNKNOWN", r = M || "1M", o = c && Array.isArray(c) && c.length > 0 ? c : ["price", "volume"], x = _.useMemo(
63
63
  () => {
64
64
  const t = C || G(r, o);
65
65
  return console.log("[StockChart] Data generated:", {
@@ -98,7 +98,7 @@ const b = {
98
98
  ] })
99
99
  ] }) }),
100
100
  /* @__PURE__ */ a(V, { className: "pt-0", children: [
101
- /* @__PURE__ */ e("div", { className: "h-64 w-full bg-background", style: { minHeight: "256px", minWidth: "350px" }, children: /* @__PURE__ */ e(W, { width: "100%", height: 256, children: /* @__PURE__ */ a(_, { data: x, margin: { top: 5, right: 20, bottom: 5, left: 0 }, children: [
101
+ /* @__PURE__ */ e("div", { className: "h-64 w-full bg-background", style: { minHeight: "256px", minWidth: "350px" }, children: /* @__PURE__ */ e(F, { width: "100%", height: 256, children: /* @__PURE__ */ a(W, { data: x, margin: { top: 5, right: 20, bottom: 5, left: 0 }, children: [
102
102
  /* @__PURE__ */ e(K, { strokeDasharray: "3 3", stroke: l, opacity: 0.3 }),
103
103
  /* @__PURE__ */ e(
104
104
  U,
@@ -3,7 +3,7 @@ import { Outlet as c } from "react-router-dom";
3
3
  import { useTheme as d } from "../../contexts/ThemeContext.js";
4
4
  import { Button as t } from "../ui/Button.js";
5
5
  const m = () => {
6
- const { setTheme: n, customTheme: a, setCustomTheme: i } = d(), s = {
6
+ const { setTheme: a, customTheme: n, setCustomTheme: i } = d(), s = {
7
7
  name: "Cyberpunk",
8
8
  colors: {
9
9
  primary: "255 238 88",
@@ -26,19 +26,19 @@ const m = () => {
26
26
  },
27
27
  borderRadius: "0px"
28
28
  };
29
- return /* @__PURE__ */ o("div", { className: "absolute top-4 right-4 flex gap-2 z-50", children: [
29
+ return /* @__PURE__ */ o("div", { className: "absolute top-4 right-4 flex gap-2 z-drawer", children: [
30
30
  ["light", "dark", "system"].map((r) => /* @__PURE__ */ e(
31
31
  t,
32
32
  {
33
33
  size: "sm",
34
34
  variant: "ghost",
35
- onClick: () => n(r),
35
+ onClick: () => a(r),
36
36
  className: "opacity-50 hover:opacity-100 capitalize",
37
37
  children: r
38
38
  },
39
39
  r
40
40
  )),
41
- /* @__PURE__ */ e(t, { size: "sm", variant: "ghost", onClick: () => i(a ? null : s), className: "opacity-50 hover:opacity-100", children: "AI" })
41
+ /* @__PURE__ */ e(t, { size: "sm", variant: "ghost", onClick: () => i(n ? null : s), className: "opacity-50 hover:opacity-100", children: "AI" })
42
42
  ] });
43
43
  }, g = () => /* @__PURE__ */ o("div", { className: "min-h-dvh w-full flex items-center justify-center bg-background text-foreground relative transition-colors duration-300", children: [
44
44
  /* @__PURE__ */ e("div", { className: "absolute inset-0 z-0 opacity-[0.03] pointer-events-none bg-[radial-gradient(#888_1px,transparent_1px)] [background-size:16px_16px]" }),
@@ -10,6 +10,9 @@ import React from 'react';
10
10
  * @param {string} error - Message shown above the form when sign-in fails.
11
11
  * @param {boolean} loading - Disables the form and shows progress while authenticating.
12
12
  * @param {QuickProfile[]} quickProfiles - Optional one-click demo identities, each `{ name, email, role }`.
13
+ * @param {string} signupHref - Where "Create one" points. Pass `null` to hide that line entirely, which is what an invite-only product wants. Defaults to `/auth/signup`.
14
+ * @param {string} signupLabel - Text for that link. Defaults to "Create one".
15
+ * @param {string} forgotHref - Where "Forgot?" points. Pass `null` to hide it — a product with no reset email has nothing to link to. Defaults to `#`.
13
16
  */
14
17
  export interface QuickProfile {
15
18
  name: string;
@@ -21,5 +24,24 @@ export interface LoginPageProps {
21
24
  error?: string | null;
22
25
  loading?: boolean;
23
26
  quickProfiles?: QuickProfile[];
27
+ /**
28
+ * Where "Create one" goes, or `null` to hide the line.
29
+ *
30
+ * Defaults to `/auth/signup` so every product that already renders this keeps
31
+ * exactly what it has. Invite-only products pass `null`: ramme.io deleted its
32
+ * signup route when accounts became invite-only, and this link kept pointing at
33
+ * it — a dead end offered to everyone who could not get in.
34
+ */
35
+ signupHref?: string | null;
36
+ signupLabel?: string;
37
+ /**
38
+ * Where "Forgot?" goes, or `null` to hide it.
39
+ *
40
+ * Defaults to `#`, which is what it has always been — a link that does nothing.
41
+ * Worth being able to remove: on a product with no reset email, offering a
42
+ * recovery link that goes nowhere is worse than saying so in plain words
43
+ * underneath the form.
44
+ */
45
+ forgotHref?: string | null;
24
46
  }
25
47
  export declare const LoginPage: React.FC<LoginPageProps>;
@@ -1,98 +1,106 @@
1
- import { jsxs as t, jsx as e } from "react/jsx-runtime";
2
- import { useState as u } from "react";
3
- import { Link as p } from "react-router-dom";
4
- import { Button as g } from "../ui/Button.js";
5
- import { Input as h } from "../forms/Input.js";
6
- import { Alert as b } from "../feedback/Alert.js";
1
+ import { jsxs as a, jsx as e } from "react/jsx-runtime";
2
+ import { useState as h } from "react";
3
+ import { Link as x } from "react-router-dom";
4
+ import { Button as v } from "../ui/Button.js";
5
+ import { Input as f } from "../forms/Input.js";
6
+ import { Alert as y } from "../feedback/Alert.js";
7
7
  import { Icon as s } from "../ui/Icon.js";
8
- const S = ({ onLogin: f, error: l, loading: i, quickProfiles: r }) => {
9
- const [n, o] = u(""), [c, d] = u(""), x = (a) => {
10
- a.preventDefault(), f(n, c);
11
- }, m = "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 mb-2 block text-foreground/90";
12
- return /* @__PURE__ */ t("div", { className: "w-full max-w-[400px] space-y-6 animate-in fade-in zoom-in-95 duration-300", children: [
13
- /* @__PURE__ */ t("div", { className: "flex flex-col items-center space-y-2 text-center mb-8", children: [
8
+ const A = ({
9
+ onLogin: g,
10
+ error: l,
11
+ loading: i,
12
+ quickProfiles: r,
13
+ signupHref: n = "/auth/signup",
14
+ signupLabel: b = "Create one",
15
+ forgotHref: o = "#"
16
+ }) => {
17
+ const [c, d] = h(""), [m, u] = h(""), N = (t) => {
18
+ t.preventDefault(), g(c, m);
19
+ }, p = "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 mb-2 block text-foreground/90";
20
+ return /* @__PURE__ */ a("div", { className: "w-full max-w-[400px] space-y-6 animate-in fade-in zoom-in-95 duration-300", children: [
21
+ /* @__PURE__ */ a("div", { className: "flex flex-col items-center space-y-2 text-center mb-8", children: [
14
22
  /* @__PURE__ */ e("div", { className: "h-10 w-10 bg-primary/10 rounded-xl flex items-center justify-center mb-2 border border-primary/20", children: /* @__PURE__ */ e(s, { name: "hexagon", className: "text-primary fill-primary/20", size: 24, strokeWidth: 2.5 }) }),
15
23
  /* @__PURE__ */ e("h1", { className: "text-2xl font-bold tracking-tight", children: "Welcome back" }),
16
24
  /* @__PURE__ */ e("p", { className: "text-sm text-muted-foreground", children: "Enter your credentials to access your workspace" })
17
25
  ] }),
18
- /* @__PURE__ */ t("div", { className: "bg-card text-card-foreground border shadow-sm rounded-xl overflow-hidden", children: [
19
- /* @__PURE__ */ e("div", { className: "p-6 pt-6", children: /* @__PURE__ */ t("form", { onSubmit: x, className: "space-y-4", children: [
20
- l && /* @__PURE__ */ e(b, { variant: "danger", title: "Authentication Error", className: "mb-4", children: l }),
21
- /* @__PURE__ */ t("div", { className: "space-y-1", children: [
22
- /* @__PURE__ */ e("label", { htmlFor: "email", className: m, children: "Email" }),
23
- /* @__PURE__ */ t("div", { className: "relative group", children: [
26
+ /* @__PURE__ */ a("div", { className: "bg-card text-card-foreground border shadow-sm rounded-xl overflow-hidden", children: [
27
+ /* @__PURE__ */ e("div", { className: "p-6 pt-6", children: /* @__PURE__ */ a("form", { onSubmit: N, className: "space-y-4", children: [
28
+ l && /* @__PURE__ */ e(y, { variant: "danger", title: "Authentication Error", className: "mb-4", children: l }),
29
+ /* @__PURE__ */ a("div", { className: "space-y-1", children: [
30
+ /* @__PURE__ */ e("label", { htmlFor: "email", className: p, children: "Email" }),
31
+ /* @__PURE__ */ a("div", { className: "relative group", children: [
24
32
  /* @__PURE__ */ e(s, { name: "mail", className: "absolute left-3 top-2.5 h-4 w-4 text-muted-foreground group-focus-within:text-primary transition-colors" }),
25
33
  /* @__PURE__ */ e(
26
- h,
34
+ f,
27
35
  {
28
36
  id: "email",
29
37
  type: "email",
30
38
  placeholder: "name@example.com",
31
39
  className: "pl-9 h-10 bg-background/50",
32
- value: n,
33
- onChange: (a) => o(a.target.value),
40
+ value: c,
41
+ onChange: (t) => d(t.target.value),
34
42
  required: !0
35
43
  }
36
44
  )
37
45
  ] })
38
46
  ] }),
39
- /* @__PURE__ */ t("div", { className: "space-y-1", children: [
40
- /* @__PURE__ */ t("div", { className: "flex items-center justify-between", children: [
41
- /* @__PURE__ */ e("label", { htmlFor: "password", className: m, children: "Password" }),
42
- /* @__PURE__ */ e(p, { to: "#", className: "text-xs text-primary hover:underline font-medium", children: "Forgot?" })
47
+ /* @__PURE__ */ a("div", { className: "space-y-1", children: [
48
+ /* @__PURE__ */ a("div", { className: "flex items-center justify-between", children: [
49
+ /* @__PURE__ */ e("label", { htmlFor: "password", className: p, children: "Password" }),
50
+ o !== null && /* @__PURE__ */ e(x, { to: o, className: "text-xs text-primary hover:underline font-medium", children: "Forgot?" })
43
51
  ] }),
44
- /* @__PURE__ */ t("div", { className: "relative group", children: [
52
+ /* @__PURE__ */ a("div", { className: "relative group", children: [
45
53
  /* @__PURE__ */ e(s, { name: "lock", className: "absolute left-3 top-2.5 h-4 w-4 text-muted-foreground group-focus-within:text-primary transition-colors" }),
46
54
  /* @__PURE__ */ e(
47
- h,
55
+ f,
48
56
  {
49
57
  id: "password",
50
58
  type: "password",
51
59
  className: "pl-9 h-10 bg-background/50",
52
- value: c,
53
- onChange: (a) => d(a.target.value),
60
+ value: m,
61
+ onChange: (t) => u(t.target.value),
54
62
  required: !0
55
63
  }
56
64
  )
57
65
  ] })
58
66
  ] }),
59
- /* @__PURE__ */ e(g, { type: "submit", className: "w-full mt-2", size: "lg", disabled: i, children: i ? "Authenticating..." : "Sign In" })
67
+ /* @__PURE__ */ e(v, { type: "submit", className: "w-full mt-2", size: "lg", disabled: i, children: i ? "Authenticating..." : "Sign In" })
60
68
  ] }) }),
61
- r && r.length > 0 && /* @__PURE__ */ t("div", { className: "px-6 pb-6 space-y-3", children: [
62
- /* @__PURE__ */ t("div", { className: "relative mb-4", children: [
69
+ r && r.length > 0 && /* @__PURE__ */ a("div", { className: "px-6 pb-6 space-y-3", children: [
70
+ /* @__PURE__ */ a("div", { className: "relative mb-4", children: [
63
71
  /* @__PURE__ */ e("div", { className: "absolute inset-0 flex items-center", children: /* @__PURE__ */ e("span", { className: "w-full border-t border-border/50" }) }),
64
72
  /* @__PURE__ */ e("div", { className: "relative flex justify-center text-xs uppercase", children: /* @__PURE__ */ e("span", { className: "bg-card px-2 text-muted-foreground", children: "Quick Select Profiles" }) })
65
73
  ] }),
66
- /* @__PURE__ */ e("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3", children: r.map((a) => /* @__PURE__ */ t(
74
+ /* @__PURE__ */ e("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3", children: r.map((t) => /* @__PURE__ */ a(
67
75
  "button",
68
76
  {
69
77
  type: "button",
70
78
  onClick: () => {
71
- o(a.email), d("demo");
79
+ d(t.email), u("demo");
72
80
  },
73
81
  className: "flex flex-col items-start p-3 text-left border rounded-lg hover:border-primary hover:bg-primary/5 transition-all w-full focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-1 focus:ring-offset-card",
74
82
  children: [
75
- /* @__PURE__ */ e("span", { className: "font-medium text-sm text-foreground", children: a.name }),
76
- /* @__PURE__ */ e("span", { className: "text-xs text-muted-foreground", children: a.role })
83
+ /* @__PURE__ */ e("span", { className: "font-medium text-sm text-foreground", children: t.name }),
84
+ /* @__PURE__ */ e("span", { className: "text-xs text-muted-foreground", children: t.role })
77
85
  ]
78
86
  },
79
- a.email
87
+ t.email
80
88
  )) })
81
89
  ] }),
82
- /* @__PURE__ */ t("div", { className: "p-6 pt-0 text-center", children: [
83
- /* @__PURE__ */ t("div", { className: "relative mb-4", children: [
90
+ n !== null && /* @__PURE__ */ a("div", { className: "p-6 pt-0 text-center", children: [
91
+ /* @__PURE__ */ a("div", { className: "relative mb-4", children: [
84
92
  /* @__PURE__ */ e("div", { className: "absolute inset-0 flex items-center", children: /* @__PURE__ */ e("span", { className: "w-full border-t" }) }),
85
93
  /* @__PURE__ */ e("div", { className: "relative flex justify-center text-xs uppercase", children: /* @__PURE__ */ e("span", { className: "bg-card px-2 text-muted-foreground", children: "Or continue with" }) })
86
94
  ] }),
87
- /* @__PURE__ */ t("p", { className: "text-sm text-muted-foreground", children: [
95
+ /* @__PURE__ */ a("p", { className: "text-sm text-muted-foreground", children: [
88
96
  "Don't have an account?",
89
97
  " ",
90
- /* @__PURE__ */ e(p, { to: "/auth/signup", className: "font-semibold text-primary hover:underline transition-all", children: "Create one" })
98
+ /* @__PURE__ */ e(x, { to: n, className: "font-semibold text-primary hover:underline transition-all", children: b })
91
99
  ] })
92
100
  ] })
93
101
  ] })
94
102
  ] });
95
103
  };
96
104
  export {
97
- S as LoginPage
105
+ A as LoginPage
98
106
  };
@@ -1,23 +1,49 @@
1
1
  import React from 'react';
2
- interface CodeBlockProps {
2
+ export interface CodeBlockProps {
3
+ /** The snippet. Leading/trailing blank lines are trimmed. */
3
4
  code: string;
4
- language: string;
5
+ /** Prism language id — `tsx`, `bash`, `css`, `json`, … Defaults to `tsx`. */
6
+ language?: string;
7
+ /** Shown in the header. Prefer a real path (`src/App.tsx`); falls back to the language. */
8
+ filename?: string;
9
+ /** Gutter line numbers. Worth it when prose refers to "line 12". */
10
+ showLineNumbers?: boolean;
11
+ /** 1-based lines to emphasise — the "change this line" case in a tutorial. */
12
+ highlightLines?: number[];
13
+ /** Wrap long lines instead of scrolling. Off by default: wrapping a shell command misrepresents it. */
14
+ wrap?: boolean;
15
+ /** Hide the copy button. Only for snippets that are not meant to be run. */
16
+ hideCopy?: boolean;
5
17
  className?: string;
6
18
  }
7
19
  /**
8
20
  * @wizard
9
21
  * @name CodeBlock
10
- * @description A component for displaying formatted and syntax-highlighted code snippets.
11
- * @tags ui, data-display, code
22
+ * @description The one way to show code. Syntax-highlighted, theme-aware, with a copy button that works on touch and outside secure contexts. Use for every snippet in docs and tutorials — do not hand-roll a <pre>. Long lines scroll inside the block so the page never scrolls sideways on a phone.
23
+ * @tags ui, data-display, code, docs, tutorial
12
24
  * @props
13
25
  * - name: code
14
26
  * type: string
15
- * description: The string of code to display.
27
+ * description: The snippet to display. Outer blank lines are trimmed.
16
28
  * - name: language
17
29
  * type: string
18
- * description: The programming language for syntax highlighting (e.g., 'tsx', 'css', 'javascript').
30
+ * description: Prism language id — 'tsx', 'bash', 'css', 'json'. Defaults to 'tsx'.
31
+ * - name: filename
32
+ * type: string
33
+ * description: Header label. Prefer a real path like 'src/App.tsx'; falls back to the language.
34
+ * - name: showLineNumbers
35
+ * type: boolean
36
+ * description: Show gutter line numbers. Use when the surrounding prose cites line numbers.
37
+ * - name: highlightLines
38
+ * type: number[]
39
+ * description: 1-based lines to emphasise, for "change this line" steps.
40
+ * - name: wrap
41
+ * type: boolean
42
+ * description: Wrap long lines instead of scrolling them. Off by default.
43
+ * - name: hideCopy
44
+ * type: boolean
45
+ * description: Hide the copy button, for snippets that are not meant to be run.
19
46
  * @category ui
20
47
  * @id code-block
21
48
  */
22
49
  export declare const CodeBlock: React.FC<CodeBlockProps>;
23
- export {};
@@ -1,45 +1,138 @@
1
- import { jsxs as c, jsx as o } from "react/jsx-runtime";
2
- import l from "react";
3
- import { Prism as d } from "react-syntax-highlighter";
4
- import { vscDarkPlus as p, coy as f } from "react-syntax-highlighter/dist/esm/styles/prism";
5
- import { useTheme as h } from "../../contexts/ThemeContext.js";
6
- import { Card as u } from "../layout/Card.js";
7
- import { Icon as i } from "../ui/Icon.js";
8
- const T = ({ code: e, language: s, className: m }) => {
9
- const { theme: r } = h(), [a, t] = l.useState(!1), n = () => {
10
- navigator.clipboard.writeText(e), t(!0), setTimeout(() => t(!1), 2e3);
11
- };
12
- return /* @__PURE__ */ c(u, { className: `relative overflow-hidden ${m}`, children: [
13
- /* @__PURE__ */ o(
14
- "button",
15
- {
16
- onClick: n,
17
- className: "absolute top-2 right-2 p-1.5 bg-card/50 hover:bg-border rounded-md transition-colors",
18
- children: a ? /* @__PURE__ */ o(i, { name: "check", size: 16 }) : /* @__PURE__ */ o(i, { name: "copy", size: 16 })
19
- }
20
- ),
21
- /* @__PURE__ */ o(
22
- d,
23
- {
24
- language: s,
25
- style: r === "dark" || r === "midnight" ? p : f,
26
- showLineNumbers: !1,
27
- customStyle: {
28
- margin: 0,
29
- padding: "1.5rem",
30
- backgroundColor: "transparent",
31
- fontSize: "0.875rem"
32
- },
33
- codeTagProps: {
34
- style: {
35
- fontFamily: "var(--app-font-mono)"
1
+ import { jsxs as f, jsx as n } from "react/jsx-runtime";
2
+ import t from "react";
3
+ import { Prism as x } from "react-syntax-highlighter";
4
+ import { oneDark as k, oneLight as w } from "react-syntax-highlighter/dist/esm/styles/prism";
5
+ import { cn as C } from "../../utils/cn.js";
6
+ import { Icon as S } from "../ui/Icon.js";
7
+ function T() {
8
+ const [r, e] = t.useState(
9
+ () => typeof document < "u" && document.documentElement.classList.contains("dark")
10
+ );
11
+ return t.useEffect(() => {
12
+ const o = document.documentElement, s = () => e(o.classList.contains("dark"));
13
+ s();
14
+ const i = new MutationObserver(s);
15
+ return i.observe(o, { attributes: !0, attributeFilter: ["class"] }), () => i.disconnect();
16
+ }, []), r;
17
+ }
18
+ async function N(r) {
19
+ var o;
20
+ if ((o = navigator.clipboard) != null && o.writeText) {
21
+ await navigator.clipboard.writeText(r);
22
+ return;
23
+ }
24
+ const e = document.createElement("textarea");
25
+ e.value = r, e.setAttribute("readonly", ""), e.style.position = "fixed", e.style.opacity = "0", document.body.appendChild(e), e.select(), document.execCommand("copy"), document.body.removeChild(e);
26
+ }
27
+ const B = ({
28
+ code: r,
29
+ language: e = "tsx",
30
+ filename: o,
31
+ showLineNumbers: s = !1,
32
+ highlightLines: i,
33
+ wrap: b = !1,
34
+ hideCopy: v = !1,
35
+ className: g
36
+ }) => {
37
+ const y = T(), [a, m] = t.useState(!1), d = t.useRef(), p = t.useMemo(() => r.replace(/^\n+|\s+$/g, ""), [r]), u = o || e;
38
+ t.useEffect(() => () => clearTimeout(d.current), []);
39
+ const h = async () => {
40
+ try {
41
+ await N(p), m(!0), clearTimeout(d.current), d.current = setTimeout(() => m(!1), 2e3);
42
+ } catch (l) {
43
+ console.warn("[CodeBlock] Copy failed", l);
44
+ }
45
+ }, c = t.useMemo(
46
+ () => new Set(i ?? []),
47
+ [i]
48
+ );
49
+ return /* @__PURE__ */ f(
50
+ "div",
51
+ {
52
+ className: C(
53
+ "group relative overflow-hidden rounded-lg border border-border bg-card",
54
+ g
55
+ ),
56
+ children: [
57
+ /* @__PURE__ */ f("div", { className: "flex items-center justify-between gap-2 border-b border-border bg-surface-muted pl-4 pr-1.5", children: [
58
+ /* @__PURE__ */ n("span", { className: "truncate py-2 font-mono text-2xs font-medium uppercase tracking-wider text-muted-foreground", children: u }),
59
+ !v && /* @__PURE__ */ n(
60
+ "button",
61
+ {
62
+ type: "button",
63
+ onClick: h,
64
+ className: "inline-flex min-h-11 min-w-11 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-surface-hover hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
65
+ "aria-label": a ? "Copied" : `Copy ${u} snippet to clipboard`,
66
+ children: /* @__PURE__ */ n(
67
+ S,
68
+ {
69
+ name: a ? "check" : "copy",
70
+ size: 16,
71
+ className: a ? "text-success" : void 0
72
+ }
73
+ )
74
+ }
75
+ )
76
+ ] }),
77
+ /* @__PURE__ */ n(
78
+ "div",
79
+ {
80
+ className: "overflow-x-auto overscroll-x-contain focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset",
81
+ tabIndex: 0,
82
+ role: "region",
83
+ "aria-label": `${u} code`,
84
+ children: /* @__PURE__ */ n(
85
+ x,
86
+ {
87
+ language: e,
88
+ style: y ? k : w,
89
+ showLineNumbers: s,
90
+ wrapLines: c.size > 0,
91
+ lineProps: c.size > 0 ? (l) => ({
92
+ style: {
93
+ display: "block",
94
+ // Semantic tokens, so the emphasis survives all five themes.
95
+ backgroundColor: c.has(l) ? "rgb(var(--app-primary-color) / 0.12)" : void 0,
96
+ boxShadow: c.has(l) ? "inset 2px 0 0 0 rgb(var(--app-primary-color))" : void 0
97
+ }
98
+ }) : void 0,
99
+ lineNumberStyle: {
100
+ minWidth: "2.5em",
101
+ paddingRight: "1em",
102
+ color: "rgb(var(--app-fg-muted) / 0.6)",
103
+ userSelect: "none"
104
+ },
105
+ customStyle: {
106
+ margin: 0,
107
+ padding: "1rem 1.25rem",
108
+ background: "transparent",
109
+ // Sizes come from the fluid scale, never a hardcoded rem — the three
110
+ // implementations this replaces used 0.875rem, 0.85rem and text-sm.
111
+ fontSize: "var(--app-step--1)",
112
+ lineHeight: "var(--app-leading-relaxed)",
113
+ tabSize: 2,
114
+ ...b ? { whiteSpace: "pre-wrap", wordBreak: "break-word" } : {}
115
+ },
116
+ codeTagProps: {
117
+ style: {
118
+ // The single place the code typeface is decided. Swapping the
119
+ // token in index.css re-fonts every snippet in every product.
120
+ fontFamily: "var(--app-font-mono)",
121
+ // Ligatures off: a tutorial shows the characters you must type,
122
+ // and a font that draws `=>` as one arrow misleads a beginner.
123
+ fontVariantLigatures: "none"
124
+ }
125
+ },
126
+ children: p
127
+ }
128
+ )
36
129
  }
37
- },
38
- children: e.trim()
39
- }
40
- )
41
- ] });
130
+ ),
131
+ /* @__PURE__ */ n("span", { "aria-live": "polite", className: "sr-only", children: a ? "Copied to clipboard" : "" })
132
+ ]
133
+ }
134
+ );
42
135
  };
43
136
  export {
44
- T as CodeBlock
137
+ B as CodeBlock
45
138
  };