@ramme-io/ui 4.1.0 → 4.2.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.
Files changed (44) hide show
  1. package/README.md +4 -3
  2. package/dist/component-registry.js +55 -52
  3. package/dist/components/ai/AIChatThread/AIChatThread.js +3 -3
  4. package/dist/components/ai/NewsSummaryCard/NewsSummaryCard.js +17 -17
  5. package/dist/components/ai/StockChart/StockChart.js +35 -35
  6. package/dist/components/auth/LoginPage.d.ts +22 -0
  7. package/dist/components/auth/LoginPage.js +50 -42
  8. package/dist/components/data-display/StatusBadge.d.ts +4 -3
  9. package/dist/components/data-display/StatusBadge.js +18 -18
  10. package/dist/components/feedback/Alert.js +19 -19
  11. package/dist/components/forms/ComboBox.js +2 -2
  12. package/dist/components/forms/DatePicker.js +37 -32
  13. package/dist/components/forms/FileUpload.js +39 -37
  14. package/dist/components/forms/Input.js +19 -18
  15. package/dist/components/forms/MultiSelect.js +9 -8
  16. package/dist/components/forms/SegmentedControl.js +10 -10
  17. package/dist/components/forms/Select.js +9 -8
  18. package/dist/components/forms/Textarea.d.ts +13 -11
  19. package/dist/components/forms/Textarea.js +36 -16
  20. package/dist/components/layout/Accordion.js +25 -32
  21. package/dist/components/layout/Card.js +38 -21
  22. package/dist/components/layout/DetailDrawer.d.ts +9 -1
  23. package/dist/components/layout/DetailDrawer.js +60 -51
  24. package/dist/components/layout/Drawer.js +47 -39
  25. package/dist/components/layout/Modal.js +28 -23
  26. package/dist/components/layout/SettingsContainer.js +23 -13
  27. package/dist/components/layout/Sheet.d.ts +58 -0
  28. package/dist/components/layout/Sheet.js +146 -0
  29. package/dist/components/navigation/MobileNav.d.ts +44 -1
  30. package/dist/components/navigation/MobileNav.js +96 -70
  31. package/dist/components/navigation/Tabs.js +19 -12
  32. package/dist/components/ui/icon-loader.d.ts +1 -1
  33. package/dist/index.css +1 -1
  34. package/dist/index.d.ts +3 -0
  35. package/dist/index.js +218 -209
  36. package/dist/lib/hooks/useDialogFocus.d.ts +1 -0
  37. package/dist/lib/hooks/useDialogFocus.js +46 -0
  38. package/dist/lib/hooks/useSheetGesture.d.ts +81 -0
  39. package/dist/lib/hooks/useSheetGesture.js +70 -0
  40. package/dist/setupTests.d.ts +1 -0
  41. package/dist/wizardManifest.js +139 -14
  42. package/package.json +9 -2
  43. package/src/wizardManifest.ts +139 -14
  44. package/tailwind.preset.js +8 -0
package/README.md CHANGED
@@ -225,7 +225,7 @@ Props, variants and live examples are in Storybook (`pnpm run storybook`) and at
225
225
  | `MenuDivider` | `@atom/primitive/menu-divider` | A visual separator for grouping MenuItems within a Menu. |
226
226
  | `MenuItem` | `@atom/primitive/menu-item` | An individual selectable item within a Menu component. |
227
227
  | `Message` | `@molecule/ai/message` | Displays a single message in a conversation, including an avatar, author, content, and optional suggestions. |
228
- | `MobileNav` | `@molecule/nav/mobile-nav` | A fixed bottom tab bar for mobile screens, providing native-feeling navigation with primary icon+label tabs and a "More" drawer for secondary links. |
228
+ | `MobileNav` | `@molecule/nav/mobile-nav` | Mobile navigation, in two shapes. |
229
229
  | `MultiSelect` | `@molecule/form/multi-select` | A searchable dropdown for picking several options at once, rendering selections as removable chips. |
230
230
  | `NewsSummaryCard` | `@molecule/ai/news-summary-card` | AI-generated news summary card with recent headlines |
231
231
  | `PageHeader` | `@atom/templates-patterns/page-header` | Displays the main header section of a page, including a title, optional description, and a slot for action buttons. |
@@ -253,7 +253,7 @@ Props, variants and live examples are in Storybook (`pnpm run storybook`) and at
253
253
  | `Tabs` | `@molecule/nav/tabs` | A navigation component that allows users to switch between different content panels within the same area. |
254
254
  | `TelemetryGraph` | `@molecule/ai/telemetry-graph` | AI-generated telemetry visualization component |
255
255
  | `TelemetryLog` | `@molecule/iot/telemetry-log` | A scrolling, newest-first table of raw sensor readings with a bounded row count. |
256
- | `Textarea` | `@atom/form/textarea` | A theme-aware textarea component for multi-line text input. |
256
+ | `Textarea` | `@atom/form/textarea` | A theme-aware multi-line text input with label, helper text, and validation state support. |
257
257
  | `TippyTooltip` | `@molecule/feedback/tippy-tooltip` | A flexible and theme-aware tooltip component that displays additional information on hover. |
258
258
  | `Toast` | `@molecule/feedback/toast` | A single, transient notification message that appears briefly to provide feedback. |
259
259
  | `ToastProvider` | `@molecule/feedback/toast-provider` | Provides a context for displaying transient notifications (toasts). |
@@ -262,7 +262,7 @@ Props, variants and live examples are in Storybook (`pnpm run storybook`) and at
262
262
  | `WatchlistEngine` | `@molecule/block/watchlist-engine` | A self-contained financial watchlist showing tickers with live price, day change, market cap, and a risk grade. |
263
263
  | `WaterPumpControl` | `@molecule/iot/water-pump-control` | An irrigation control block: choose automatic or manual operation and set the soil-moisture target the controller should hold. |
264
264
 
265
- ### Layouts (32)
265
+ ### Layouts (33)
266
266
 
267
267
  | Name | SKU | What it is |
268
268
  | --- | --- | --- |
@@ -294,6 +294,7 @@ Props, variants and live examples are in Storybook (`pnpm run storybook`) and at
294
294
  | `Modal` | `@chassis/layout/modal` | A centered dialog that blocks interaction with the page until dismissed. |
295
295
  | `PageContainer` | `@chassis/layout/page-container` | Standard responsive padding wrapper for the content inside a page. |
296
296
  | `SettingsContainer` | `@chassis/layout/settings-container` | The canonical settings page arrangement, with a fixed slot per section so every app's settings read the same way. |
297
+ | `Sheet` | `@chassis/layout/sheet` | A bottom sheet that opens at a partial height and grows to full screen as you drag or scroll it up. |
297
298
  | `Sidebar` | `@chassis/layout/sidebar` | A collapsible, theme-aware application navigation rail. |
298
299
  | `Statusbar` | `@chassis/layout/statusbar` | The persistent strip pinned to the bottom of the app shell. |
299
300
  | `Toolbar` | `@chassis/layout/toolbar` | The secondary action strip that sits beneath the header. |
@@ -22,8 +22,8 @@ import { AuthLayout as g } from "./components/auth/AuthLayout.js";
22
22
  import { LoginPage as b } from "./components/auth/LoginPage.js";
23
23
  import { SignupPage as C } from "./components/auth/SignupPage.js";
24
24
  import { ActivityFeed as y } from "./components/data-display/ActivityFeed.js";
25
- import { AreaChart as v } from "./components/data-display/AreaChart.js";
26
- import { BarChart as S } from "./components/data-display/BarChart.js";
25
+ import { AreaChart as S } from "./components/data-display/AreaChart.js";
26
+ import { BarChart as v } from "./components/data-display/BarChart.js";
27
27
  import { ChartWithTable as T } from "./components/data-display/ChartWithTable.js";
28
28
  import { CodeBlock as D } from "./components/data-display/CodeBlock.js";
29
29
  import { DataTable as A } from "./components/data-display/DataTable.js";
@@ -77,25 +77,26 @@ import { PageContainer as dr } from "./components/layout/PageContainer.js";
77
77
  import { PageHeader as lr } from "./components/layout/PageHeader.js";
78
78
  import { SectionHeader as fr } from "./components/layout/SectionHeader.js";
79
79
  import { SettingsContainer as sr } from "./components/layout/SettingsContainer.js";
80
- import { Sidebar as zr } from "./components/layout/Sidebar.js";
81
- import { Statusbar as Jr } from "./components/layout/Statusbar.js";
82
- import { Toolbar as Vr } from "./components/layout/Toolbar.js";
80
+ import { Sheet as zr } from "./components/layout/Sheet.js";
81
+ import { Sidebar as Jr } from "./components/layout/Sidebar.js";
82
+ import { Statusbar as Vr } from "./components/layout/Statusbar.js";
83
+ import { Toolbar as Xr } from "./components/layout/Toolbar.js";
83
84
  import { ButtonItem as ur, UserNav as hr } from "./components/layout/UserNav.js";
84
85
  import { ConnectorOverlay as gr } from "./components/molecular/ConnectorOverlay.js";
85
86
  import { DataCard as br } from "./components/molecular/DataCard.js";
86
87
  import { StateMatrix as Cr } from "./components/molecular/StateMatrix.js";
87
- import { Breadcrumbs as Xr, BreadcrumbItem as yr } from "./components/navigation/Breadcrumbs.js";
88
- import { Menu as Zr, MenuItem as vr, MenuDivider as Sr } from "./components/navigation/Menu.js";
88
+ import { Breadcrumbs as Zr, BreadcrumbItem as yr } from "./components/navigation/Breadcrumbs.js";
89
+ import { Menu as $r, MenuItem as Sr, MenuDivider as vr } from "./components/navigation/Menu.js";
89
90
  import { MobileNav as Tr } from "./components/navigation/MobileNav.js";
90
- import { Pagination as $r } from "./components/navigation/Pagination.js";
91
- import { Stepper as rt } from "./components/navigation/Stepper.js";
92
- import { Tabs as tt, TabPanel as Dr } from "./components/navigation/Tabs.js";
93
- import { Avatar as et } from "./components/ui/Avatar.js";
94
- import { Badge as ot } from "./components/ui/Badge.js";
91
+ import { Pagination as rt } from "./components/navigation/Pagination.js";
92
+ import { Stepper as tt } from "./components/navigation/Stepper.js";
93
+ import { Tabs as et, TabPanel as Dr } from "./components/navigation/Tabs.js";
94
+ import { Avatar as ot } from "./components/ui/Avatar.js";
95
+ import { Badge as at } from "./components/ui/Badge.js";
95
96
  import { BrandLogo as Ar } from "./components/ui/BrandLogo.js";
96
- import { Button as at } from "./components/ui/Button.js";
97
- import { Icon as it } from "./components/ui/Icon.js";
98
- const Je = {
97
+ import { Button as it } from "./components/ui/Button.js";
98
+ import { Icon as mt } from "./components/ui/Icon.js";
99
+ const Xe = {
99
100
  Suggestion: Pr,
100
101
  suggestion: Pr,
101
102
  Actions: Br,
@@ -166,12 +167,12 @@ const Je = {
166
167
  ActivityFeed: y,
167
168
  "activity-feed": y,
168
169
  activityfeed: y,
169
- AreaChart: v,
170
- "area-chart": v,
171
- areachart: v,
172
- BarChart: S,
173
- "bar-chart": S,
174
- barchart: S,
170
+ AreaChart: S,
171
+ "area-chart": S,
172
+ areachart: S,
173
+ BarChart: v,
174
+ "bar-chart": v,
175
+ barchart: v,
175
176
  ChartWithTable: T,
176
177
  "chart-with-table": T,
177
178
  chartwithtable: T,
@@ -346,12 +347,14 @@ const Je = {
346
347
  SettingsContainer: sr,
347
348
  "settings-container": sr,
348
349
  settingscontainer: sr,
349
- Sidebar: zr,
350
- sidebar: zr,
351
- Statusbar: Jr,
352
- statusbar: Jr,
353
- Toolbar: Vr,
354
- toolbar: Vr,
350
+ Sheet: zr,
351
+ sheet: zr,
352
+ Sidebar: Jr,
353
+ sidebar: Jr,
354
+ Statusbar: Vr,
355
+ statusbar: Vr,
356
+ Toolbar: Xr,
357
+ toolbar: Xr,
355
358
  UserNav: hr,
356
359
  "user-nav": hr,
357
360
  usernav: hr,
@@ -370,40 +373,40 @@ const Je = {
370
373
  BreadcrumbItem: yr,
371
374
  "breadcrumb-item": yr,
372
375
  breadcrumbitem: yr,
373
- Breadcrumbs: Xr,
374
- breadcrumbs: Xr,
375
- MenuDivider: Sr,
376
- "menu-divider": Sr,
377
- menudivider: Sr,
378
- MenuItem: vr,
379
- "menu-item": vr,
380
- menuitem: vr,
381
- Menu: Zr,
382
- menu: Zr,
376
+ Breadcrumbs: Zr,
377
+ breadcrumbs: Zr,
378
+ MenuDivider: vr,
379
+ "menu-divider": vr,
380
+ menudivider: vr,
381
+ MenuItem: Sr,
382
+ "menu-item": Sr,
383
+ menuitem: Sr,
384
+ Menu: $r,
385
+ menu: $r,
383
386
  MobileNav: Tr,
384
387
  "mobile-nav": Tr,
385
388
  mobilenav: Tr,
386
- Pagination: $r,
387
- pagination: $r,
388
- Stepper: rt,
389
- stepper: rt,
389
+ Pagination: rt,
390
+ pagination: rt,
391
+ Stepper: tt,
392
+ stepper: tt,
390
393
  TabPanel: Dr,
391
394
  "tab-panel": Dr,
392
395
  tabpanel: Dr,
393
- Tabs: tt,
394
- tabs: tt,
395
- Avatar: et,
396
- avatar: et,
397
- Badge: ot,
398
- badge: ot,
396
+ Tabs: et,
397
+ tabs: et,
398
+ Avatar: ot,
399
+ avatar: ot,
400
+ Badge: at,
401
+ badge: at,
399
402
  BrandLogo: Ar,
400
403
  "brand-logo": Ar,
401
404
  brandlogo: Ar,
402
- Button: at,
403
- button: at,
404
- Icon: it,
405
- icon: it
405
+ Button: it,
406
+ button: it,
407
+ Icon: mt,
408
+ icon: mt
406
409
  };
407
410
  export {
408
- Je as AUTO_REGISTRY
411
+ Xe as AUTO_REGISTRY
409
412
  };
@@ -1,5 +1,5 @@
1
1
  import { jsxs as c, jsx as t, Fragment as w } from "react/jsx-runtime";
2
- import { useState as R, useRef as B, useEffect as D, useCallback as N } from "react";
2
+ import { useState as B, useRef as R, useEffect as D, useCallback as N } from "react";
3
3
  import { CodeBlock as G } from "../../data-display/CodeBlock.js";
4
4
  import { Icon as C } from "../../ui/Icon.js";
5
5
  import { PromptInput as $ } from "../../../blocks/ai/PromptInput.js";
@@ -91,12 +91,12 @@ const W = () => /* @__PURE__ */ t("div", { className: "flex items-center gap-1 p
91
91
  activeModel: i,
92
92
  connectionStatus: d,
93
93
  renderMessage: u,
94
- placeholder: f = "Ask Ramme AI...",
94
+ placeholder: f = "Ask AI…",
95
95
  className: h,
96
96
  suggestions: s,
97
97
  onSuggestionClick: k
98
98
  }) => {
99
- const _ = d ?? (a ? "error" : l ? "streaming" : "idle"), [b, y] = R(""), v = B(null);
99
+ const _ = d ?? (a ? "error" : l ? "streaming" : "idle"), [b, y] = B(""), v = R(null);
100
100
  D(() => {
101
101
  var r;
102
102
  (r = v.current) == null || r.scrollIntoView({ behavior: "smooth", block: "end" });
@@ -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,9 +1,9 @@
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";
1
+ import { jsxs as s, jsx as e } from "react/jsx-runtime";
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
- function G(s, p, C) {
6
+ function G(a, p, C) {
7
7
  const N = Date.now(), M = p && Array.isArray(p) && p.length > 0 ? p : ["price", "volume"], c = {
8
8
  "1D": { duration: 6.5 * 60 * 60 * 1e3, points: 78, basePrice: 150 },
9
9
  // 5-minute intervals (market hours)
@@ -13,30 +13,30 @@ function G(s, p, C) {
13
13
  // Daily close
14
14
  "1Y": { duration: 365 * 24 * 60 * 60 * 1e3, points: 52, basePrice: 100 }
15
15
  // Weekly close
16
- }, g = c[s] || c["1M"], { duration: l, points: h, basePrice: r } = g, o = l / h, x = [];
16
+ }, g = c[a] || c["1M"], { duration: l, points: u, basePrice: r } = g, o = l / u, x = [];
17
17
  let n = r;
18
- const u = [];
19
- for (let d = 0; d < h; d++) {
18
+ const h = [];
19
+ for (let d = 0; d < u; d++) {
20
20
  const v = N - l + d * o, m = {
21
21
  timestamp: new Date(v).toLocaleString("en-US", {
22
- ...s === "1D" && { hour: "2-digit", minute: "2-digit" },
23
- ...s === "1W" && { weekday: "short", hour: "2-digit" },
24
- ...s === "1M" && { month: "short", day: "numeric" },
25
- ...s === "1Y" && { month: "short", year: "2-digit" }
22
+ ...a === "1D" && { hour: "2-digit", minute: "2-digit" },
23
+ ...a === "1W" && { weekday: "short", hour: "2-digit" },
24
+ ...a === "1M" && { month: "short", day: "numeric" },
25
+ ...a === "1Y" && { month: "short", year: "2-digit" }
26
26
  })
27
27
  };
28
28
  M.forEach((i) => {
29
29
  switch (i) {
30
30
  case "price":
31
- const P = r * 0.02, y = 1e-4 * r, k = (Math.random() - 0.48) * P + y;
32
- n = Math.max(n + k, r * 0.7), u.push(n), m[i] = Number(n.toFixed(2));
31
+ const P = r * 0.02, y = 1e-4 * r, b = (Math.random() - 0.48) * P + y;
32
+ n = Math.max(n + b, r * 0.7), h.push(n), m[i] = Number(n.toFixed(2));
33
33
  break;
34
34
  case "volume":
35
35
  const t = 1e6, f = Math.random() > 0.7 ? Math.random() * 2 : 1;
36
36
  m[i] = Math.round((t + Math.random() * t * 2) * f);
37
37
  break;
38
38
  case "moving_average":
39
- const w = Math.min(20, d + 1), D = u.slice(-w).reduce((I, T) => I + T, 0) / w;
39
+ const w = Math.min(20, d + 1), D = h.slice(-w).reduce((I, T) => I + T, 0) / w;
40
40
  m[i] = Number(D.toFixed(2));
41
41
  break;
42
42
  default:
@@ -46,7 +46,7 @@ function G(s, p, C) {
46
46
  }
47
47
  return x;
48
48
  }
49
- const b = {
49
+ const k = {
50
50
  price: { label: "Price", unit: "USD", color: "#10b981" },
51
51
  // Emerald/green
52
52
  volume: { label: "Volume", unit: "shares", color: "#f59e0b" },
@@ -54,16 +54,16 @@ const b = {
54
54
  moving_average: { label: "MA(20)", unit: "", color: "#ef4444" }
55
55
  // Red
56
56
  }, Q = ({
57
- params: s,
57
+ params: a,
58
58
  title: p,
59
59
  data: C
60
60
  }) => {
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(
61
+ var y, b;
62
+ const { ticker: N, timeRange: M, metrics: c } = a, { textColor: g, gridColor: l } = Y(), u = N || a.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:", {
66
- ticker: h,
66
+ ticker: u,
67
67
  timeRange: r,
68
68
  metrics: o,
69
69
  dataPoints: t.length,
@@ -71,20 +71,20 @@ const b = {
71
71
  lastPoint: t[t.length - 1]
72
72
  }), t;
73
73
  },
74
- [C, r, o, h]
75
- ), n = x.filter((t) => t.price !== void 0), u = ((y = n[0]) == null ? void 0 : y.price) || 0, d = ((k = n[n.length - 1]) == null ? void 0 : k.price) || 0, v = d - u, m = u > 0 ? (v / u * 100).toFixed(2) : "0.00", i = v >= 0, P = p || `${h.toUpperCase()} - ${r}`;
76
- return /* @__PURE__ */ a(E, { className: "w-full max-w-2xl shadow-lg border-primary/20", children: [
77
- /* @__PURE__ */ e(H, { className: "pb-2", children: /* @__PURE__ */ a("div", { className: "flex items-center justify-between", children: [
74
+ [C, r, o, u]
75
+ ), n = x.filter((t) => t.price !== void 0), h = ((y = n[0]) == null ? void 0 : y.price) || 0, d = ((b = n[n.length - 1]) == null ? void 0 : b.price) || 0, v = d - h, m = h > 0 ? (v / h * 100).toFixed(2) : "0.00", i = v >= 0, P = p || `${u.toUpperCase()} - ${r}`;
76
+ return /* @__PURE__ */ s(E, { className: "w-full max-w-2xl shadow-lg border-primary/20", children: [
77
+ /* @__PURE__ */ e(H, { className: "pb-2", children: /* @__PURE__ */ s("div", { className: "flex items-center justify-between", children: [
78
78
  /* @__PURE__ */ e(L, { className: "text-sm font-medium text-foreground/80", children: P }),
79
- o.includes("price") && /* @__PURE__ */ a("div", { className: "flex items-center gap-2", children: [
80
- /* @__PURE__ */ a("span", { className: "text-base font-semibold", children: [
79
+ o.includes("price") && /* @__PURE__ */ s("div", { className: "flex items-center gap-2", children: [
80
+ /* @__PURE__ */ s("span", { className: "text-base font-semibold", children: [
81
81
  "$",
82
82
  d.toFixed(2)
83
83
  ] }),
84
- /* @__PURE__ */ a(
84
+ /* @__PURE__ */ s(
85
85
  "span",
86
86
  {
87
- className: `text-xs font-medium ${i ? "text-green-600 dark:text-green-400" : "text-red-600 dark:text-red-400"}`,
87
+ className: `text-xs font-medium ${i ? "text-success-strong" : "text-destructive-strong"}`,
88
88
  children: [
89
89
  i ? "+" : "",
90
90
  v.toFixed(2),
@@ -97,8 +97,8 @@ const b = {
97
97
  )
98
98
  ] })
99
99
  ] }) }),
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: [
100
+ /* @__PURE__ */ s(V, { className: "pt-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__ */ s(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,
@@ -136,7 +136,7 @@ const b = {
136
136
  yAxisId: "price",
137
137
  type: "monotone",
138
138
  dataKey: "price",
139
- stroke: b.price.color,
139
+ stroke: k.price.color,
140
140
  strokeWidth: 2,
141
141
  dot: !1,
142
142
  name: "Price (USD)"
@@ -148,7 +148,7 @@ const b = {
148
148
  yAxisId: "price",
149
149
  type: "monotone",
150
150
  dataKey: "moving_average",
151
- stroke: b.moving_average.color,
151
+ stroke: k.moving_average.color,
152
152
  strokeWidth: 2,
153
153
  dot: !1,
154
154
  name: "MA(20)",
@@ -160,15 +160,15 @@ const b = {
160
160
  {
161
161
  yAxisId: "volume",
162
162
  dataKey: "volume",
163
- fill: b.volume.color,
163
+ fill: k.volume.color,
164
164
  opacity: 0.6,
165
165
  name: "Volume"
166
166
  }
167
167
  )
168
168
  ] }) }) }),
169
169
  /* @__PURE__ */ e("div", { className: "mt-3 flex flex-wrap gap-2", children: o.map((t) => {
170
- const f = b[t];
171
- return /* @__PURE__ */ a(
170
+ const f = k[t];
171
+ return /* @__PURE__ */ s(
172
172
  "div",
173
173
  {
174
174
  className: "inline-flex items-center gap-1.5 rounded-full bg-muted/50 px-2.5 py-1 text-xs",
@@ -181,7 +181,7 @@ const b = {
181
181
  }
182
182
  ),
183
183
  /* @__PURE__ */ e("span", { className: "font-medium", children: f.label }),
184
- f.unit && /* @__PURE__ */ a("span", { className: "text-muted-foreground", children: [
184
+ f.unit && /* @__PURE__ */ s("span", { className: "text-muted-foreground", children: [
185
185
  "(",
186
186
  f.unit,
187
187
  ")"
@@ -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>;