@calliopelabs/ui 0.7.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +266 -3
- package/dist/appearance/AppearancePicker.js +4 -2
- package/dist/appearance/AppearanceProvider.d.ts +10 -2
- package/dist/appearance/AppearanceProvider.js +6 -2
- package/dist/appearance/AppearanceScript.d.ts +15 -4
- package/dist/appearance/AppearanceScript.js +11 -5
- package/dist/appearance/appearance.js +1 -1
- package/dist/appearance/boot.d.ts +13 -2
- package/dist/appearance/boot.js +35 -5
- package/dist/appearance/product.d.ts +13 -0
- package/dist/appearance/product.js +2 -2
- package/dist/appearance/runtime-brand.d.ts +106 -0
- package/dist/appearance/runtime-brand.js +420 -0
- package/dist/css/appearance.js +147 -0
- package/dist/css/calliope.css +1221 -0
- package/dist/css/components.css +729 -0
- package/dist/css/tokens.css +491 -0
- package/dist/data/DefinitionList.d.ts +16 -0
- package/dist/data/DefinitionList.js +9 -0
- package/dist/data/Stat.d.ts +25 -0
- package/dist/data/Stat.js +19 -0
- package/dist/data/Tag.d.ts +21 -0
- package/dist/data/Tag.js +18 -0
- package/dist/data/types.d.ts +3 -2
- package/dist/data/use-local-table.d.ts +36 -0
- package/dist/data/use-local-table.js +127 -0
- package/dist/index.d.ts +44 -3
- package/dist/index.js +40 -1
- package/dist/nav/Nav.d.ts +2 -1
- package/dist/nav/Nav.js +34 -15
- package/dist/nav/model.d.ts +21 -0
- package/dist/page/PageHeader.d.ts +5 -3
- package/dist/page/PageHeader.js +5 -4
- package/dist/page/Panel.d.ts +19 -0
- package/dist/page/Panel.js +12 -0
- package/dist/primitives/Avatar.d.ts +37 -0
- package/dist/primitives/Avatar.js +43 -0
- package/dist/primitives/Field.d.ts +33 -0
- package/dist/primitives/Field.js +30 -0
- package/dist/primitives/Segmented.d.ts +30 -0
- package/dist/primitives/Segmented.js +20 -0
- package/dist/primitives/Select.d.ts +40 -0
- package/dist/primitives/Select.js +47 -0
- package/dist/primitives/Switch.d.ts +7 -0
- package/dist/primitives/Switch.js +8 -0
- package/dist/primitives/Textarea.d.ts +3 -0
- package/dist/primitives/Textarea.js +6 -0
- package/dist/provider/messages.d.ts +430 -0
- package/dist/provider/messages.js +666 -0
- package/dist/shell/BrandMark.d.ts +25 -3
- package/dist/shell/BrandMark.js +34 -4
- package/dist/shell/RailPanel.js +2 -2
- package/dist/shell/RailTree.js +2 -2
- package/dist/shell/Sidebar.d.ts +9 -2
- package/dist/shell/Sidebar.js +10 -4
- package/dist/shell/UserMenu.d.ts +3 -1
- package/dist/shell/UserMenu.js +3 -11
- package/dist/shell/rail-boot.d.ts +28 -0
- package/dist/shell/rail-boot.js +45 -0
- package/dist/shell/rail-state.d.ts +6 -1
- package/dist/shell/rail-state.js +18 -2
- package/dist/styles/preset.css +4 -0
- package/dist/styles/theme.css +36 -13
- package/dist/theme/css-layer.d.ts +19 -0
- package/dist/theme/css-layer.js +171 -0
- package/dist/theme/css.d.ts +1 -0
- package/dist/theme/css.js +45 -9
- package/dist/workflow/ArtifactCard.d.ts +47 -0
- package/dist/workflow/ArtifactCard.js +45 -0
- package/dist/workflow/DocumentReview.d.ts +74 -0
- package/dist/workflow/DocumentReview.js +54 -0
- package/dist/workflow/FormReview.d.ts +53 -0
- package/dist/workflow/FormReview.js +83 -0
- package/dist/workflow/GateBadge.d.ts +11 -0
- package/dist/workflow/GateBadge.js +14 -0
- package/dist/workflow/MediaReview.d.ts +63 -0
- package/dist/workflow/MediaReview.js +166 -0
- package/dist/workflow/ReviewPanel.d.ts +41 -0
- package/dist/workflow/ReviewPanel.js +59 -0
- package/dist/workflow/RunTimeline.d.ts +25 -0
- package/dist/workflow/RunTimeline.js +56 -0
- package/dist/workflow/WorkflowCard.d.ts +39 -0
- package/dist/workflow/WorkflowCard.js +56 -0
- package/dist/workflow/WorkflowGallery.d.ts +43 -0
- package/dist/workflow/WorkflowGallery.js +69 -0
- package/dist/workflow/WorkflowQuestionCard.d.ts +18 -0
- package/dist/workflow/WorkflowQuestionCard.js +78 -0
- package/dist/workflow/canvas/WorkflowCanvas.d.ts +39 -0
- package/dist/workflow/canvas/WorkflowCanvas.js +648 -0
- package/dist/workflow/definition.d.ts +408 -0
- package/dist/workflow/definition.js +411 -0
- package/dist/workflow/editor/WorkflowDiff.d.ts +17 -0
- package/dist/workflow/editor/WorkflowDiff.js +25 -0
- package/dist/workflow/editor/WorkflowImportPreview.d.ts +19 -0
- package/dist/workflow/editor/WorkflowImportPreview.js +50 -0
- package/dist/workflow/editor/WorkflowInspector.d.ts +40 -0
- package/dist/workflow/editor/WorkflowInspector.js +349 -0
- package/dist/workflow/editor/WorkflowJsonView.d.ts +16 -0
- package/dist/workflow/editor/WorkflowJsonView.js +40 -0
- package/dist/workflow/editor/WorkflowPalette.d.ts +16 -0
- package/dist/workflow/editor/WorkflowPalette.js +36 -0
- package/dist/workflow/editor/WorkflowProblems.d.ts +20 -0
- package/dist/workflow/editor/WorkflowProblems.js +51 -0
- package/dist/workflow/editor/WorkflowWorkspace.d.ts +49 -0
- package/dist/workflow/editor/WorkflowWorkspace.js +154 -0
- package/dist/workflow/gallery.d.ts +21 -0
- package/dist/workflow/gallery.js +29 -0
- package/dist/workflow/layout.d.ts +49 -0
- package/dist/workflow/layout.js +162 -0
- package/dist/workflow/model.d.ts +50 -0
- package/dist/workflow/model.js +7 -0
- package/dist/workflow/palette.d.ts +34 -0
- package/dist/workflow/palette.js +70 -0
- package/dist/workflow/portable.d.ts +162 -0
- package/dist/workflow/portable.js +547 -0
- package/dist/workflow/problems.d.ts +38 -0
- package/dist/workflow/problems.js +409 -0
- package/dist/workflow/question.d.ts +26 -0
- package/dist/workflow/question.js +1 -0
- package/dist/workflow/run.d.ts +94 -0
- package/dist/workflow/run.js +31 -0
- package/dist/workflow/workspace.d.ts +33 -0
- package/dist/workflow/workspace.js +17 -0
- package/package.json +6 -1
|
@@ -1,14 +1,36 @@
|
|
|
1
1
|
import type * as React from "react";
|
|
2
|
+
import type { Product } from "../appearance/product.js";
|
|
3
|
+
import type { Mode } from "../theme/tokens.js";
|
|
4
|
+
/** A deployment's logo as `withBrand` puts it on the product: validated image URLs. */
|
|
5
|
+
export type BrandLogo = NonNullable<Product["logo"]>;
|
|
2
6
|
export interface BrandMarkProps {
|
|
3
7
|
/** Where the mark leads: the product's home. */
|
|
4
8
|
href: string;
|
|
5
|
-
/** The product's logo mark (an <img> or SVG). Decorative: the name or the label carries the meaning. */
|
|
9
|
+
/** The product's logo mark (an <img> or SVG). Decorative: the name or the label carries the meaning. Defaults to the product's `logo.mark`. */
|
|
6
10
|
mark?: React.ReactNode;
|
|
7
11
|
/** Defaults to the product's name from AppearanceProvider. */
|
|
8
12
|
name?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Image URLs in place of the product's runtime logo (a brand admin's draft).
|
|
15
|
+
* `mark` is the square mark; `onDark` and `onLight` are the full logo, drawn
|
|
16
|
+
* in place of the name on a dark or a light sidebar.
|
|
17
|
+
*/
|
|
18
|
+
logo?: BrandLogo;
|
|
19
|
+
/** Shown in the collapsed sidebar when there is no mark. Defaults to the product's `shortName`. */
|
|
20
|
+
shortName?: string;
|
|
21
|
+
/** A line under the name: an environment, an edition, a customer. Hidden when collapsed. */
|
|
22
|
+
tagline?: React.ReactNode;
|
|
9
23
|
/** The mark alone, for the collapsed sidebar. */
|
|
10
24
|
collapsed?: boolean;
|
|
11
25
|
className?: string;
|
|
12
26
|
}
|
|
13
|
-
/** The
|
|
14
|
-
export declare function
|
|
27
|
+
/** The full logo for the sidebar of one mode: the variant made for its lightness, else the other. */
|
|
28
|
+
export declare function logoForSidebar(logo: BrandLogo | undefined, product: Product | null, mode: Mode): string | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* The product's mark and name, heading the sidebar. A runtime brand's logo
|
|
31
|
+
* (`withBrand`) replaces them: its mark in the tile, its full logo in place of
|
|
32
|
+
* the name, the `onDark` or `onLight` variant chosen by the sidebar's
|
|
33
|
+
* lightness in each mode. Both variants are in the markup and CSS shows the
|
|
34
|
+
* one for the current mode, so the first paint is already right.
|
|
35
|
+
*/
|
|
36
|
+
export declare function BrandMark({ href, mark, name, logo, shortName, tagline, collapsed, className, }: BrandMarkProps): React.JSX.Element;
|
package/dist/shell/BrandMark.js
CHANGED
|
@@ -1,14 +1,44 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { inkFor } from "../appearance/appearance.js";
|
|
3
4
|
import { useProduct } from "../appearance/AppearanceProvider.js";
|
|
4
5
|
import { cn } from "../lib/cn.js";
|
|
5
6
|
import { useUI } from "../provider/UIProvider.js";
|
|
6
|
-
/**
|
|
7
|
-
|
|
7
|
+
/** True when the sidebar of `mode` is dark, so a logo drawn on it is the `onDark` one. */
|
|
8
|
+
function sidebarIsDark(product, mode) {
|
|
9
|
+
const sidebar = mode === "dark" ? product?.sidebar : product?.sidebarLight;
|
|
10
|
+
// Unbranded light grounds keep a neutral, light sidebar.
|
|
11
|
+
if (!sidebar)
|
|
12
|
+
return mode === "dark";
|
|
13
|
+
return inkFor(sidebar) === "#ffffff";
|
|
14
|
+
}
|
|
15
|
+
/** The full logo for the sidebar of one mode: the variant made for its lightness, else the other. */
|
|
16
|
+
export function logoForSidebar(logo, product, mode) {
|
|
17
|
+
if (!logo)
|
|
18
|
+
return undefined;
|
|
19
|
+
return sidebarIsDark(product, mode)
|
|
20
|
+
? (logo.onDark ?? logo.onLight)
|
|
21
|
+
: (logo.onLight ?? logo.onDark);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The product's mark and name, heading the sidebar. A runtime brand's logo
|
|
25
|
+
* (`withBrand`) replaces them: its mark in the tile, its full logo in place of
|
|
26
|
+
* the name, the `onDark` or `onLight` variant chosen by the sidebar's
|
|
27
|
+
* lightness in each mode. Both variants are in the markup and CSS shows the
|
|
28
|
+
* one for the current mode, so the first paint is already right.
|
|
29
|
+
*/
|
|
30
|
+
export function BrandMark({ href, mark, name, logo, shortName, tagline, collapsed = false, className, }) {
|
|
8
31
|
const { Link } = useUI();
|
|
9
32
|
const product = useProduct();
|
|
10
33
|
const title = name ?? product?.name;
|
|
11
34
|
if (!title)
|
|
12
35
|
throw new Error("BrandMark needs a name, or an AppearanceProvider with a product");
|
|
13
|
-
|
|
36
|
+
const brandLogo = logo ?? product?.logo;
|
|
37
|
+
const tile = mark ?? (brandLogo?.mark ? _jsx("img", { src: brandLogo.mark, alt: "", draggable: false }) : null);
|
|
38
|
+
const onDark = logoForSidebar(brandLogo, product, "dark");
|
|
39
|
+
const onLight = logoForSidebar(brandLogo, product, "light");
|
|
40
|
+
const full = !collapsed && (onDark || onLight);
|
|
41
|
+
const short = collapsed && !tile ? (shortName ?? product?.shortName) : undefined;
|
|
42
|
+
const logoClass = "h-6 w-auto max-w-full min-w-0 object-contain object-left";
|
|
43
|
+
return (_jsxs(Link, { href: href, "aria-label": collapsed || full ? title : undefined, className: cn("flex min-w-0 items-center gap-2 rounded-lg focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none", collapsed && "justify-center", className), children: [tile && (_jsx("span", { "aria-hidden": true, className: "flex size-7 shrink-0 items-center justify-center rounded-lg bg-sidebar-primary/10 ring-1 ring-sidebar-primary/30 [&_img]:size-4.5 [&_svg]:size-4.5", children: tile })), short && (_jsx("span", { "aria-hidden": true, className: "truncate font-head text-xs font-semibold tracking-tight", children: short })), !collapsed && (_jsxs("span", { className: "grid min-w-0", children: [full ? (onDark === onLight ? (_jsx("img", { src: onDark, alt: "", draggable: false, className: logoClass })) : (_jsxs(_Fragment, { children: [onDark && (_jsx("img", { src: onDark, alt: "", draggable: false, className: cn(logoClass, "in-data-[mode=light]:hidden") })), onLight && (_jsx("img", { src: onLight, alt: "", draggable: false, className: cn(logoClass, "hidden in-data-[mode=light]:block") }))] }))) : (_jsx("span", { className: "truncate font-head text-sm font-semibold tracking-tight", children: title })), tagline && " ", tagline && (_jsx("span", { className: "truncate text-2xs text-sidebar-muted-foreground", children: tagline }))] }))] }));
|
|
14
44
|
}
|
package/dist/shell/RailPanel.js
CHANGED
|
@@ -19,10 +19,10 @@ export function RailPanel({ label, title, actions, footer, children, collapsed,
|
|
|
19
19
|
if (collapsed && !inDrawer) {
|
|
20
20
|
if (collapsedMode === "hidden")
|
|
21
21
|
return null;
|
|
22
|
-
return (_jsxs("aside", { "aria-label": label, "data-slot": "rail-panel", "data-collapsed": true, className: cn("flex h-full w-8 shrink-0 flex-col items-center gap-2 border-l border-sidebar-border bg-sidebar py-2 text-muted-foreground", className), children: [_jsx("button", { type: "button", onClick: () => onCollapsedChange(false), "aria-label": messages.rail.show(label), title: `${messages.rail.show(label)} (])`, className: "flex size-7 items-center justify-center rounded-lg hover:bg-sidebar-accent hover:text-sidebar-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none", children: _jsx(ChevronsLeftIcon, { className: "size-4", "aria-hidden": true }) }), collapsedIcon && (_jsx("span", { "aria-hidden": true, className: "[&_svg]:size-4", children: collapsedIcon }))] }));
|
|
22
|
+
return (_jsxs("aside", { "aria-label": label, "data-slot": "rail-panel", "data-collapsed": true, className: cn("flex h-full w-8 shrink-0 flex-col items-center gap-2 border-l border-sidebar-border bg-sidebar py-2 text-sidebar-muted-foreground", className), children: [_jsx("button", { type: "button", onClick: () => onCollapsedChange(false), "aria-label": messages.rail.show(label), title: `${messages.rail.show(label)} (])`, className: "flex size-7 items-center justify-center rounded-lg hover:bg-sidebar-accent hover:text-sidebar-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none", children: _jsx(ChevronsLeftIcon, { className: "size-4", "aria-hidden": true }) }), collapsedIcon && (_jsx("span", { "aria-hidden": true, className: "[&_svg]:size-4", children: collapsedIcon }))] }));
|
|
23
23
|
}
|
|
24
24
|
const resizable = Boolean(onWidthChange) && !inDrawer && !expanded;
|
|
25
|
-
const iconButton = "flex size-7 shrink-0 items-center justify-center rounded-lg text-muted-foreground hover:bg-sidebar-accent hover:text-sidebar-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none";
|
|
25
|
+
const iconButton = "flex size-7 shrink-0 items-center justify-center rounded-lg text-sidebar-muted-foreground hover:bg-sidebar-accent hover:text-sidebar-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none";
|
|
26
26
|
return (_jsxs("aside", { "aria-label": label, "data-slot": "rail-panel", "data-expanded": expanded || undefined, style: inDrawer || expanded ? undefined : { width }, className: cn("relative flex h-full min-h-0 shrink-0 flex-col border-l border-sidebar-border bg-sidebar text-sidebar-foreground", (inDrawer || expanded) && "w-full min-w-0 flex-1", inDrawer && "border-l-0", className), children: [resizable && (_jsx(ResizeHandle, { label: messages.rail.resize(label), width: width, minWidth: minWidth, maxWidth: maxWidth, onWidthChange: (next) => onWidthChange?.(clamp(next)) })), _jsxs("div", { className: "flex min-w-0 items-center gap-2 border-b border-sidebar-border px-3 py-2", children: [_jsx("div", { className: "min-w-0 flex-1 truncate text-sm font-semibold", children: title ?? label }), actions, onExpandedChange && !inDrawer && (_jsx("button", { type: "button", onClick: () => onExpandedChange(!expanded), "aria-label": expanded ? messages.rail.restore(label) : messages.rail.expand(label), title: expanded ? messages.rail.restore(label) : messages.rail.expand(label), className: iconButton, children: expanded ? (_jsx(Minimize2Icon, { className: "size-4", "aria-hidden": true })) : (_jsx(Maximize2Icon, { className: "size-4", "aria-hidden": true })) })), !inDrawer && (_jsx("button", { type: "button", onClick: () => onCollapsedChange(true), "aria-label": messages.rail.hide(label), title: `${messages.rail.hide(label)} (])`, className: iconButton, children: _jsx(ChevronsRightIcon, { className: "size-4", "aria-hidden": true }) }))] }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", children: children }), footer && _jsx("div", { className: "border-t border-sidebar-border px-3 py-2", children: footer })] }));
|
|
27
27
|
}
|
|
28
28
|
/* A focusable separator is the WAI-ARIA window splitter, an interactive
|
package/dist/shell/RailTree.js
CHANGED
|
@@ -14,7 +14,7 @@ import { StatusDot } from "../status/StatusDot.js";
|
|
|
14
14
|
*/
|
|
15
15
|
export function RailTree({ label, groups, activeHref, loading = false, emptyText, className, }) {
|
|
16
16
|
const headingId = React.useId();
|
|
17
|
-
return (_jsxs("div", { className: cn("flex min-w-0 flex-col", className), children: [_jsx("div", { id: headingId, className: "px-3 pt-3 pb-1 text-2xs font-semibold tracking-wider text-muted-foreground uppercase", children: label }), _jsx("div", { className: "px-2 pb-2", children: loading ? (_jsx("div", { className: "flex flex-col gap-2 px-1 pt-1", "aria-busy": true, children: Array.from({ length: 4 }, (_, i) => (_jsx(Skeleton, { className: "h-6 w-full" }, i))) })) : groups.length === 0 ? (_jsx("p", { className: "px-2 py-2 text-xs text-muted-foreground", children: emptyText })) : (_jsx("ul", { "aria-labelledby": headingId, className: "flex flex-col gap-0.5", children: groups.map((group) => (_jsx(Branch, { group: group, activeHref: activeHref }, group.key))) })) })] }));
|
|
17
|
+
return (_jsxs("div", { className: cn("flex min-w-0 flex-col", className), children: [_jsx("div", { id: headingId, className: "px-3 pt-3 pb-1 text-2xs font-semibold tracking-wider text-sidebar-muted-foreground uppercase", children: label }), _jsx("div", { className: "px-2 pb-2", children: loading ? (_jsx("div", { className: "flex flex-col gap-2 px-1 pt-1", "aria-busy": true, children: Array.from({ length: 4 }, (_, i) => (_jsx(Skeleton, { className: "h-6 w-full" }, i))) })) : groups.length === 0 ? (_jsx("p", { className: "px-2 py-2 text-xs text-sidebar-muted-foreground", children: emptyText })) : (_jsx("ul", { "aria-labelledby": headingId, className: "flex flex-col gap-0.5", children: groups.map((group) => (_jsx(Branch, { group: group, activeHref: activeHref }, group.key))) })) })] }));
|
|
18
18
|
}
|
|
19
19
|
function Branch({ group, activeHref }) {
|
|
20
20
|
const { Link } = useUI();
|
|
@@ -22,7 +22,7 @@ function Branch({ group, activeHref }) {
|
|
|
22
22
|
const [open, setOpen] = React.useState(holdsActive);
|
|
23
23
|
const listId = React.useId();
|
|
24
24
|
const Chevron = open ? ChevronDownIcon : ChevronRightIcon;
|
|
25
|
-
return (_jsxs("li", { children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), "aria-expanded": open, "aria-controls": listId, className: "flex h-7 w-full min-w-0 items-center gap-1.5 rounded-lg px-1.5 text-left text-sm hover:bg-sidebar-accent/60 focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none", children: [_jsx(Chevron, { className: "size-3.5 shrink-0 text-muted-foreground", "aria-hidden": true }), _jsx("span", { className: "truncate font-medium", title: group.label, children: group.label }), " ", _jsx("span", { className: "ml-auto font-mono text-2xs text-muted-foreground", children: group.items.length })] }), _jsx("ul", { id: listId, hidden: !open, className: "ml-3 flex flex-col gap-0.5 border-l border-current/10 pl-2", children: group.items.map((item) => {
|
|
25
|
+
return (_jsxs("li", { children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), "aria-expanded": open, "aria-controls": listId, className: "flex h-7 w-full min-w-0 items-center gap-1.5 rounded-lg px-1.5 text-left text-sm hover:bg-sidebar-accent/60 focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none", children: [_jsx(Chevron, { className: "size-3.5 shrink-0 text-sidebar-muted-foreground", "aria-hidden": true }), _jsx("span", { className: "truncate font-medium", title: group.label, children: group.label }), " ", _jsx("span", { className: "ml-auto font-mono text-2xs text-sidebar-muted-foreground", children: group.items.length })] }), _jsx("ul", { id: listId, hidden: !open, className: "ml-3 flex flex-col gap-0.5 border-l border-current/10 pl-2", children: group.items.map((item) => {
|
|
26
26
|
const Icon = item.icon;
|
|
27
27
|
const isActive = item.href === activeHref;
|
|
28
28
|
return (_jsx("li", { children: _jsxs(Link, { href: item.href, "aria-current": isActive ? "page" : undefined, className: cn("flex h-7 min-w-0 items-center gap-2 rounded-lg px-1.5 text-sm focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none", isActive
|
package/dist/shell/Sidebar.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
import type { NavActive, NavModel } from "../nav/model.js";
|
|
3
3
|
export interface SidebarProps {
|
|
4
4
|
/** The model, filtered to what the viewer may open (`visibleNav`). */
|
|
@@ -15,6 +15,13 @@ export interface SidebarProps {
|
|
|
15
15
|
footer?: React.ReactNode;
|
|
16
16
|
/** The navigation landmark's name. Defaults to "Main". */
|
|
17
17
|
label?: string;
|
|
18
|
+
/**
|
|
19
|
+
* The `name` of a `RailBoot` passed to `AppearanceScript rails`: until React
|
|
20
|
+
* hydrates, the sidebar renders both its folded and open bodies and the
|
|
21
|
+
* stamped `data-rail-<name>` shows the right one, so the first paint is
|
|
22
|
+
* already the person's choice.
|
|
23
|
+
*/
|
|
24
|
+
railName?: string;
|
|
18
25
|
className?: string;
|
|
19
26
|
}
|
|
20
27
|
/**
|
|
@@ -23,4 +30,4 @@ export interface SidebarProps {
|
|
|
23
30
|
* row and the collapsed state are the caller's; `[` toggles it through the
|
|
24
31
|
* AppShell.
|
|
25
32
|
*/
|
|
26
|
-
export declare function Sidebar({ nav, active, collapsed: collapsedProp, onCollapsedChange, header, headerCollapsed, footer, label, className, }: SidebarProps): React.JSX.Element;
|
|
33
|
+
export declare function Sidebar({ nav, active, collapsed: collapsedProp, onCollapsedChange, header, headerCollapsed, footer, label, railName, className, }: SidebarProps): React.JSX.Element;
|
package/dist/shell/Sidebar.js
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { ChevronsLeftIcon, ChevronsRightIcon } from "lucide-react";
|
|
4
|
+
import * as React from "react";
|
|
4
5
|
import { cn } from "../lib/cn.js";
|
|
5
6
|
import { Nav } from "../nav/Nav.js";
|
|
6
7
|
import { useUI } from "../provider/UIProvider.js";
|
|
7
8
|
import { useRailPlacement } from "./placement.js";
|
|
9
|
+
const noSubscription = () => () => { };
|
|
8
10
|
/**
|
|
9
11
|
* The main rail, on the left (Astrolift layout D): the one brand surface.
|
|
10
12
|
* 220px with labels, 48px with icons. Pure: the filtered model, the active
|
|
11
13
|
* row and the collapsed state are the caller's; `[` toggles it through the
|
|
12
14
|
* AppShell.
|
|
13
15
|
*/
|
|
14
|
-
export function Sidebar({ nav, active, collapsed: collapsedProp, onCollapsedChange, header, headerCollapsed, footer, label, className, }) {
|
|
16
|
+
export function Sidebar({ nav, active, collapsed: collapsedProp, onCollapsedChange, header, headerCollapsed, footer, label, railName, className, }) {
|
|
15
17
|
const { messages } = useUI();
|
|
16
18
|
const inDrawer = useRailPlacement() === "drawer";
|
|
19
|
+
// True on the server and while hydrating, false once React owns the page.
|
|
20
|
+
const hydrating = React.useSyncExternalStore(noSubscription, () => false, () => true);
|
|
21
|
+
const pending = Boolean(railName) && hydrating && !inDrawer;
|
|
17
22
|
const collapsed = collapsedProp && !inDrawer;
|
|
18
|
-
|
|
23
|
+
const body = (folded) => (_jsxs(_Fragment, { children: [folded
|
|
19
24
|
? headerCollapsed && _jsx("div", { className: "flex justify-center py-3", children: headerCollapsed })
|
|
20
|
-
: header && _jsx("div", { className: "flex min-w-0 items-center px-3 py-3", children: header }), _jsx("nav", { "aria-label": label ?? messages.nav.label, className: "min-h-0 flex-1 overflow-y-auto px-2 py-1", children: _jsx(Nav, { nav: nav, active: active, collapsed:
|
|
25
|
+
: header && _jsx("div", { className: "flex min-w-0 items-center px-3 py-3", children: header }), _jsx("nav", { "aria-label": label ?? messages.nav.label, className: "min-h-0 flex-1 overflow-y-auto px-2 py-1", children: _jsx(Nav, { nav: nav, active: active, collapsed: folded }) }), (footer || !inDrawer) && (_jsxs("div", { className: "flex flex-col gap-1 border-t border-sidebar-border px-2 py-2", children: [!folded && footer, !inDrawer && (_jsx("button", { type: "button", onClick: () => onCollapsedChange(!folded), "aria-label": folded ? messages.nav.expand : messages.nav.collapse, title: folded ? messages.nav.expandTitle : messages.nav.collapseTitle, className: "flex h-8 items-center justify-center rounded-lg text-sidebar-muted-foreground hover:bg-sidebar-accent hover:text-sidebar-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none", children: folded ? (_jsx(ChevronsRightIcon, { className: "size-4", "aria-hidden": true })) : (_jsx(ChevronsLeftIcon, { className: "size-4", "aria-hidden": true })) }))] }))] }));
|
|
26
|
+
return (_jsx("div", { "data-slot": "sidebar", "data-collapsed": collapsed || undefined, "data-rail": pending ? railName : undefined, "data-rail-pending": pending || undefined, className: cn("flex h-full min-h-0 shrink-0 flex-col border-r border-sidebar-border bg-sidebar text-sidebar-foreground", inDrawer ? "w-full border-r-0" : "transition-[width] duration-(--motion-base)", !inDrawer && (collapsed ? "w-12" : "w-55"), className), children: pending ? (_jsxs(_Fragment, { children: [_jsx("div", { "data-rail-body": "expanded", className: "contents", children: body(false) }), _jsx("div", { "data-rail-body": "collapsed", className: "contents", children: body(true) })] })) : (body(collapsed)) }));
|
|
21
27
|
}
|
package/dist/shell/UserMenu.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export interface UserMenuItem {
|
|
|
11
11
|
export interface UserMenuProps {
|
|
12
12
|
name: string;
|
|
13
13
|
email: string;
|
|
14
|
+
/** The person's picture; their initials show without it, and while it loads. */
|
|
15
|
+
avatar?: string;
|
|
14
16
|
/** Groups of items, separated by rules: account pages, then help. */
|
|
15
17
|
groups?: readonly (readonly UserMenuItem[])[];
|
|
16
18
|
onSignOut: () => void;
|
|
@@ -19,4 +21,4 @@ export interface UserMenuProps {
|
|
|
19
21
|
className?: string;
|
|
20
22
|
}
|
|
21
23
|
/** The signed-in person's menu, at the foot of the sidebar. Pure: the person, the items and sign-out are the app's. */
|
|
22
|
-
export declare function UserMenu({ name, email, groups, onSignOut, collapsed, className, }: UserMenuProps): React.JSX.Element;
|
|
24
|
+
export declare function UserMenu({ name, email, avatar, groups, onSignOut, collapsed, className, }: UserMenuProps): React.JSX.Element;
|
package/dist/shell/UserMenu.js
CHANGED
|
@@ -2,25 +2,17 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { LogOutIcon } from "lucide-react";
|
|
4
4
|
import { cn } from "../lib/cn.js";
|
|
5
|
+
import { Avatar, initials } from "../primitives/Avatar.js";
|
|
5
6
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "../primitives/DropdownMenu.js";
|
|
6
7
|
import { useUI } from "../provider/UIProvider.js";
|
|
7
|
-
function initialsOf(name) {
|
|
8
|
-
return (name
|
|
9
|
-
.split(/\s+/)
|
|
10
|
-
.map((part) => part[0])
|
|
11
|
-
.filter(Boolean)
|
|
12
|
-
.slice(0, 2)
|
|
13
|
-
.join("")
|
|
14
|
-
.toUpperCase() || "?");
|
|
15
|
-
}
|
|
16
8
|
/** The signed-in person's menu, at the foot of the sidebar. Pure: the person, the items and sign-out are the app's. */
|
|
17
|
-
export function UserMenu({ name, email, groups = [], onSignOut, collapsed = false, className, }) {
|
|
9
|
+
export function UserMenu({ name, email, avatar, groups = [], onSignOut, collapsed = false, className, }) {
|
|
18
10
|
const { Link, messages } = useUI();
|
|
19
11
|
return (_jsxs(DropdownMenu, { children: [_jsxs(DropdownMenuTrigger
|
|
20
12
|
// Expanded, the visible name and email are the trigger's name (WCAG
|
|
21
13
|
// 2.5.3, label in name); collapsed, only the initials show, so it
|
|
22
14
|
// needs one.
|
|
23
|
-
, { "aria-label": collapsed ? messages.user.accountMenu(name,
|
|
15
|
+
, { "aria-label": collapsed ? messages.user.accountMenu(name, initials(name)) : undefined, className: cn("flex h-10 w-full min-w-0 items-center gap-2 rounded-lg px-1.5 text-left hover:bg-sidebar-accent/60 focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none", collapsed && "justify-center px-0", className), children: [_jsx(Avatar, { name: name, src: avatar, decorative: true }), !collapsed && (_jsxs("span", { className: "grid min-w-0 flex-1 leading-tight", children: [_jsx("span", { className: "truncate text-sm font-medium", children: name }), " ", _jsx("span", { className: "truncate text-xs text-sidebar-muted-foreground", children: email })] }))] }), _jsxs(DropdownMenuContent, { side: "right", align: "end", className: "min-w-56", children: [_jsxs(DropdownMenuLabel, { className: "grid font-normal", children: [_jsx("span", { className: "truncate text-sm font-medium text-popover-foreground", children: name }), _jsx("span", { className: "truncate text-xs text-muted-foreground", children: email })] }), groups.map((items, i) => (_jsx(MenuGroup, { items: items, Link: Link }, i))), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onSelect: onSignOut, children: [_jsx(LogOutIcon, { "aria-hidden": true }), " ", messages.user.signOut] })] })] }));
|
|
24
16
|
}
|
|
25
17
|
function MenuGroup({ items, Link, }) {
|
|
26
18
|
return (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuGroup, { children: items.map((item) => {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A rail whose fold is applied before first paint. `AppearanceScript` (with
|
|
3
|
+
* `rails`) reads the same localStorage entry `useRailState` writes and stamps
|
|
4
|
+
* `data-rail-<name>="collapsed" | "expanded"` on <html>; a Sidebar given
|
|
5
|
+
* `railName` paints from that attribute until React has hydrated, so a
|
|
6
|
+
* person who folded the rail never sees it open first. Pure, so a Server
|
|
7
|
+
* Component can build the list.
|
|
8
|
+
*/
|
|
9
|
+
export interface RailBoot {
|
|
10
|
+
/** The localStorage key, the same one passed to `useRailState`. */
|
|
11
|
+
key: string;
|
|
12
|
+
/** A lowercase slug: the attribute is `data-rail-<name>` and a Sidebar's `railName`. */
|
|
13
|
+
name: string;
|
|
14
|
+
/** Folded by default (until the person chooses) on viewports narrower than this many px. */
|
|
15
|
+
collapsedBelow?: number;
|
|
16
|
+
}
|
|
17
|
+
/** The rails, checked: a name lands in a CSS selector and an attribute name. */
|
|
18
|
+
export declare function checkRails(rails: readonly RailBoot[]): RailBoot[];
|
|
19
|
+
/** The media query for "narrower than `width`px", shared by the boot script and the hook. */
|
|
20
|
+
export declare function narrowerThan(width: number): string;
|
|
21
|
+
/** The attribute a rail's fold is stamped under on <html>. */
|
|
22
|
+
export declare function railAttribute(name: string): `data-rail-${string}`;
|
|
23
|
+
/**
|
|
24
|
+
* The CSS that paints a pending Sidebar (rendered on the server, not yet
|
|
25
|
+
* hydrated) from the stamped attribute: its width, and which of its two
|
|
26
|
+
* bodies shows.
|
|
27
|
+
*/
|
|
28
|
+
export declare function railStyles(rails: readonly RailBoot[]): string;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const SLUG = /^[a-z][a-z0-9-]*$/;
|
|
2
|
+
/** The rails, checked: a name lands in a CSS selector and an attribute name. */
|
|
3
|
+
export function checkRails(rails) {
|
|
4
|
+
return rails.map((rail) => {
|
|
5
|
+
if (!SLUG.test(rail.name))
|
|
6
|
+
throw new Error(`@calliopelabs/ui: rail name must be a lowercase slug, got ${rail.name}`);
|
|
7
|
+
if (typeof rail.key !== "string" || !rail.key)
|
|
8
|
+
throw new Error(`@calliopelabs/ui: rail ${rail.name} needs a storage key`);
|
|
9
|
+
if (rail.collapsedBelow !== undefined &&
|
|
10
|
+
!(Number.isFinite(rail.collapsedBelow) && rail.collapsedBelow > 0))
|
|
11
|
+
throw new Error(`@calliopelabs/ui: rail ${rail.name} collapsedBelow must be a positive width`);
|
|
12
|
+
const out = { key: rail.key, name: rail.name };
|
|
13
|
+
if (rail.collapsedBelow !== undefined)
|
|
14
|
+
out.collapsedBelow = rail.collapsedBelow;
|
|
15
|
+
return out;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
/** The media query for "narrower than `width`px", shared by the boot script and the hook. */
|
|
19
|
+
export function narrowerThan(width) {
|
|
20
|
+
return `(max-width: ${width - 0.02}px)`;
|
|
21
|
+
}
|
|
22
|
+
/** The attribute a rail's fold is stamped under on <html>. */
|
|
23
|
+
export function railAttribute(name) {
|
|
24
|
+
return `data-rail-${name}`;
|
|
25
|
+
}
|
|
26
|
+
/** Widths of the Sidebar, matching its `w-12` and `w-55`. */
|
|
27
|
+
const SIDEBAR_WIDTH = { collapsed: "3rem", expanded: "13.75rem" };
|
|
28
|
+
/**
|
|
29
|
+
* The CSS that paints a pending Sidebar (rendered on the server, not yet
|
|
30
|
+
* hydrated) from the stamped attribute: its width, and which of its two
|
|
31
|
+
* bodies shows.
|
|
32
|
+
*/
|
|
33
|
+
export function railStyles(rails) {
|
|
34
|
+
return checkRails(rails)
|
|
35
|
+
.map(({ name }) => {
|
|
36
|
+
const folded = `html[${railAttribute(name)}="collapsed"]`;
|
|
37
|
+
const open = `html:not([${railAttribute(name)}="collapsed"])`;
|
|
38
|
+
const pending = `[data-rail="${name}"][data-rail-pending]`;
|
|
39
|
+
return (`${folded} ${pending}{width:${SIDEBAR_WIDTH.collapsed}}` +
|
|
40
|
+
`${open} ${pending}{width:${SIDEBAR_WIDTH.expanded}}` +
|
|
41
|
+
`${folded} ${pending} [data-rail-body="expanded"],` +
|
|
42
|
+
`${open} ${pending} [data-rail-body="collapsed"]{display:none}`);
|
|
43
|
+
})
|
|
44
|
+
.join("");
|
|
45
|
+
}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
import { type RailBoot } from "./rail-boot.js";
|
|
1
2
|
/**
|
|
2
3
|
* One rail's collapsed state, remembered per person in this browser.
|
|
3
4
|
* `defaultCollapsed` applies until they choose: Astrolift's projects rail
|
|
4
5
|
* passes "narrower than 1280px". The server renders the rail expanded and the
|
|
5
6
|
* client applies the saved choice without a hydration mismatch; a change in
|
|
6
7
|
* one tab reaches the others.
|
|
8
|
+
*
|
|
9
|
+
* Given a `RailBoot` (the one passed to `AppearanceScript rails`) instead of a
|
|
10
|
+
* key, the default is its `collapsedBelow` width, and the state is kept on
|
|
11
|
+
* <html> as `data-rail-<name>`, which the boot script stamped before paint.
|
|
7
12
|
*/
|
|
8
|
-
export declare function useRailState(
|
|
13
|
+
export declare function useRailState(rail: string | RailBoot, defaultCollapsed?: () => boolean): readonly [boolean, (collapsed: boolean) => void];
|
|
9
14
|
/** A resizable rail's width in px, remembered like its collapsed state and held to its range. */
|
|
10
15
|
export declare function useRailWidth(storageKey: string, defaultWidth: number, range: {
|
|
11
16
|
min: number;
|
package/dist/shell/rail-state.js
CHANGED
|
@@ -1,18 +1,34 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { readLocal, subscribeLocal, writeLocal } from "../lib/local-store.js";
|
|
4
|
+
import { narrowerThan, railAttribute } from "./rail-boot.js";
|
|
5
|
+
function narrowerThanNow(width) {
|
|
6
|
+
return typeof window.matchMedia === "function" && window.matchMedia(narrowerThan(width)).matches;
|
|
7
|
+
}
|
|
4
8
|
/**
|
|
5
9
|
* One rail's collapsed state, remembered per person in this browser.
|
|
6
10
|
* `defaultCollapsed` applies until they choose: Astrolift's projects rail
|
|
7
11
|
* passes "narrower than 1280px". The server renders the rail expanded and the
|
|
8
12
|
* client applies the saved choice without a hydration mismatch; a change in
|
|
9
13
|
* one tab reaches the others.
|
|
14
|
+
*
|
|
15
|
+
* Given a `RailBoot` (the one passed to `AppearanceScript rails`) instead of a
|
|
16
|
+
* key, the default is its `collapsedBelow` width, and the state is kept on
|
|
17
|
+
* <html> as `data-rail-<name>`, which the boot script stamped before paint.
|
|
10
18
|
*/
|
|
11
|
-
export function useRailState(
|
|
19
|
+
export function useRailState(rail, defaultCollapsed) {
|
|
20
|
+
const storageKey = typeof rail === "string" ? rail : rail.key;
|
|
21
|
+
const name = typeof rail === "string" ? undefined : rail.name;
|
|
22
|
+
const below = typeof rail === "string" ? undefined : rail.collapsedBelow;
|
|
23
|
+
const fallback = React.useCallback(() => (defaultCollapsed ? defaultCollapsed() : below ? narrowerThanNow(below) : false), [defaultCollapsed, below]);
|
|
12
24
|
const collapsed = React.useSyncExternalStore(subscribeLocal, () => {
|
|
13
25
|
const saved = readLocal(storageKey);
|
|
14
|
-
return saved === "1" ? true : saved === "0" ? false :
|
|
26
|
+
return saved === "1" ? true : saved === "0" ? false : fallback();
|
|
15
27
|
}, () => false);
|
|
28
|
+
React.useEffect(() => {
|
|
29
|
+
if (name)
|
|
30
|
+
document.documentElement.setAttribute(railAttribute(name), collapsed ? "collapsed" : "expanded");
|
|
31
|
+
}, [name, collapsed]);
|
|
16
32
|
const set = React.useCallback((next) => writeLocal(storageKey, next ? "1" : "0"), [storageKey]);
|
|
17
33
|
return [collapsed, set];
|
|
18
34
|
}
|
package/dist/styles/preset.css
CHANGED
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
*/
|
|
11
11
|
@import "./theme.css";
|
|
12
12
|
|
|
13
|
+
/* The workflow canvas's structural styles (@xyflow/react), under the utilities so tokens win. */
|
|
14
|
+
@import "@xyflow/react/dist/base.css" layer(base);
|
|
15
|
+
|
|
13
16
|
/* The package's own components: their classes are generated with the app's. */
|
|
14
17
|
@source "../";
|
|
15
18
|
|
|
@@ -44,6 +47,7 @@
|
|
|
44
47
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
45
48
|
--color-sidebar-primary: var(--sidebar-primary);
|
|
46
49
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
50
|
+
--color-sidebar-muted-foreground: var(--sidebar-muted-foreground, var(--muted-foreground));
|
|
47
51
|
--color-sidebar-accent: var(--sidebar-accent);
|
|
48
52
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
49
53
|
--color-sidebar-border: var(--sidebar-border);
|
package/dist/styles/theme.css
CHANGED
|
@@ -35,6 +35,9 @@
|
|
|
35
35
|
--border: rgba(255, 255, 255, 0.1);
|
|
36
36
|
--input: rgba(255, 255, 255, 0.1);
|
|
37
37
|
--sidebar: var(--brand-sidebar, #0a0a0a);
|
|
38
|
+
--sidebar-foreground: var(--brand-sidebar-foreground, #f0f2f1);
|
|
39
|
+
--sidebar-primary: var(--brand-sidebar-primary, var(--brand-primary));
|
|
40
|
+
--sidebar-muted-foreground: var(--brand-sidebar-muted-foreground, #848b88);
|
|
38
41
|
--sidebar-accent: color-mix(in oklab, var(--primary) 16%, transparent);
|
|
39
42
|
--sidebar-border: rgba(255, 255, 255, 0.08);
|
|
40
43
|
--foreground: #f0f2f1;
|
|
@@ -53,8 +56,6 @@
|
|
|
53
56
|
--chart-3: #cbf39d;
|
|
54
57
|
--chart-4: #e0b34a;
|
|
55
58
|
--chart-5: #0b6644;
|
|
56
|
-
--sidebar-foreground: #f0f2f1;
|
|
57
|
-
--sidebar-primary: var(--brand-primary);
|
|
58
59
|
--sidebar-primary-foreground: var(--brand-on-primary);
|
|
59
60
|
--sidebar-accent-foreground: #f0f2f1;
|
|
60
61
|
--sidebar-ring: var(--brand-primary);
|
|
@@ -91,9 +92,12 @@
|
|
|
91
92
|
--muted-foreground: #5c5b58;
|
|
92
93
|
--border: #dfdedc;
|
|
93
94
|
--input: #dfdedc;
|
|
94
|
-
--sidebar: #f4f3f1;
|
|
95
|
-
--sidebar-
|
|
96
|
-
--sidebar-
|
|
95
|
+
--sidebar: var(--brand-sidebar-light, #f4f3f1);
|
|
96
|
+
--sidebar-foreground: var(--brand-sidebar-foreground, #1e1d1b);
|
|
97
|
+
--sidebar-primary: var(--brand-sidebar-primary, var(--brand-primary));
|
|
98
|
+
--sidebar-muted-foreground: var(--brand-sidebar-muted-foreground, #5c5b58);
|
|
99
|
+
--sidebar-accent: var(--brand-sidebar-accent, #f3ebe5);
|
|
100
|
+
--sidebar-border: var(--brand-sidebar-border, #dfdedc);
|
|
97
101
|
--foreground: #1e1d1b;
|
|
98
102
|
--card-foreground: #1e1d1b;
|
|
99
103
|
--popover-foreground: #1e1d1b;
|
|
@@ -110,8 +114,6 @@
|
|
|
110
114
|
--chart-3: #8a7a5c;
|
|
111
115
|
--chart-4: #b8860b;
|
|
112
116
|
--chart-5: #26803f;
|
|
113
|
-
--sidebar-foreground: #1e1d1b;
|
|
114
|
-
--sidebar-primary: var(--brand-primary);
|
|
115
117
|
--sidebar-primary-foreground: var(--brand-on-primary);
|
|
116
118
|
--sidebar-accent-foreground: #1e1d1b;
|
|
117
119
|
--sidebar-ring: var(--brand-primary);
|
|
@@ -148,6 +150,9 @@
|
|
|
148
150
|
--border: rgba(255, 255, 255, 0.1);
|
|
149
151
|
--input: rgba(255, 255, 255, 0.1);
|
|
150
152
|
--sidebar: var(--brand-sidebar, #0a0a0a);
|
|
153
|
+
--sidebar-foreground: var(--brand-sidebar-foreground, #f0f2f1);
|
|
154
|
+
--sidebar-primary: var(--brand-sidebar-primary, var(--brand-primary));
|
|
155
|
+
--sidebar-muted-foreground: var(--brand-sidebar-muted-foreground, #848b88);
|
|
151
156
|
--sidebar-accent: color-mix(in oklab, var(--primary) 16%, transparent);
|
|
152
157
|
--sidebar-border: rgba(255, 255, 255, 0.08);
|
|
153
158
|
}
|
|
@@ -164,6 +169,9 @@
|
|
|
164
169
|
--border: rgba(255, 255, 255, 0.1);
|
|
165
170
|
--input: rgba(255, 255, 255, 0.1);
|
|
166
171
|
--sidebar: var(--brand-sidebar, #161819);
|
|
172
|
+
--sidebar-foreground: var(--brand-sidebar-foreground, #f0f2f1);
|
|
173
|
+
--sidebar-primary: var(--brand-sidebar-primary, var(--brand-primary));
|
|
174
|
+
--sidebar-muted-foreground: var(--brand-sidebar-muted-foreground, #8b9194);
|
|
167
175
|
--sidebar-accent: color-mix(in oklab, var(--primary) 16%, transparent);
|
|
168
176
|
--sidebar-border: rgba(255, 255, 255, 0.08);
|
|
169
177
|
}
|
|
@@ -180,6 +188,9 @@
|
|
|
180
188
|
--border: rgba(255, 255, 255, 0.09);
|
|
181
189
|
--input: rgba(255, 255, 255, 0.1);
|
|
182
190
|
--sidebar: #161719;
|
|
191
|
+
--sidebar-foreground: #f0f2f1;
|
|
192
|
+
--sidebar-primary: var(--brand-primary);
|
|
193
|
+
--sidebar-muted-foreground: #8e9296;
|
|
183
194
|
--sidebar-accent: color-mix(in oklab, var(--primary) 14%, transparent);
|
|
184
195
|
--sidebar-border: rgba(255, 255, 255, 0.07);
|
|
185
196
|
}
|
|
@@ -196,6 +207,9 @@
|
|
|
196
207
|
--border: rgba(255, 255, 255, 0.1);
|
|
197
208
|
--input: rgba(255, 255, 255, 0.1);
|
|
198
209
|
--sidebar: var(--brand-sidebar, #0a1310);
|
|
210
|
+
--sidebar-foreground: var(--brand-sidebar-foreground, #f0f2f1);
|
|
211
|
+
--sidebar-primary: var(--brand-sidebar-primary, var(--brand-primary));
|
|
212
|
+
--sidebar-muted-foreground: var(--brand-sidebar-muted-foreground, #8ba199);
|
|
199
213
|
--sidebar-accent: color-mix(in oklab, var(--primary) 16%, transparent);
|
|
200
214
|
--sidebar-border: rgba(255, 255, 255, 0.08);
|
|
201
215
|
}
|
|
@@ -212,6 +226,9 @@
|
|
|
212
226
|
--border: rgba(255, 255, 255, 0.1);
|
|
213
227
|
--input: rgba(255, 255, 255, 0.1);
|
|
214
228
|
--sidebar: var(--brand-sidebar, #152141);
|
|
229
|
+
--sidebar-foreground: var(--brand-sidebar-foreground, #f0f2f1);
|
|
230
|
+
--sidebar-primary: var(--brand-sidebar-primary, var(--brand-primary));
|
|
231
|
+
--sidebar-muted-foreground: var(--brand-sidebar-muted-foreground, #97a6c2);
|
|
215
232
|
--sidebar-accent: color-mix(in oklab, var(--primary) 16%, transparent);
|
|
216
233
|
--sidebar-border: rgba(255, 255, 255, 0.08);
|
|
217
234
|
}
|
|
@@ -227,9 +244,12 @@
|
|
|
227
244
|
--muted-foreground: #5c5b58;
|
|
228
245
|
--border: #dedad2;
|
|
229
246
|
--input: #dedad2;
|
|
230
|
-
--sidebar: #f5f0e4;
|
|
231
|
-
--sidebar-
|
|
232
|
-
--sidebar-
|
|
247
|
+
--sidebar: var(--brand-sidebar-light, #f5f0e4);
|
|
248
|
+
--sidebar-foreground: var(--brand-sidebar-foreground, #1e1d1b);
|
|
249
|
+
--sidebar-primary: var(--brand-sidebar-primary, var(--brand-primary));
|
|
250
|
+
--sidebar-muted-foreground: var(--brand-sidebar-muted-foreground, #5c5b58);
|
|
251
|
+
--sidebar-accent: var(--brand-sidebar-accent, #f0e6da);
|
|
252
|
+
--sidebar-border: var(--brand-sidebar-border, #dedad2);
|
|
233
253
|
}
|
|
234
254
|
|
|
235
255
|
/* Ground: Mist (light) */
|
|
@@ -243,9 +263,12 @@
|
|
|
243
263
|
--muted-foreground: #5c5b58;
|
|
244
264
|
--border: #d6dcdf;
|
|
245
265
|
--input: #d6dcdf;
|
|
246
|
-
--sidebar: #e9f1f5;
|
|
247
|
-
--sidebar-
|
|
248
|
-
--sidebar-
|
|
266
|
+
--sidebar: var(--brand-sidebar-light, #e9f1f5);
|
|
267
|
+
--sidebar-foreground: var(--brand-sidebar-foreground, #1e1d1b);
|
|
268
|
+
--sidebar-primary: var(--brand-sidebar-primary, var(--brand-primary));
|
|
269
|
+
--sidebar-muted-foreground: var(--brand-sidebar-muted-foreground, #5c5b58);
|
|
270
|
+
--sidebar-accent: var(--brand-sidebar-accent, #dfeaf0);
|
|
271
|
+
--sidebar-border: var(--brand-sidebar-border, #d6dcdf);
|
|
249
272
|
}
|
|
250
273
|
|
|
251
274
|
/* Accent: Green (dark) */
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Product } from "../appearance/product.js";
|
|
2
|
+
/**
|
|
3
|
+
* The CSS layer: the same tokens and the same boot script, for pages that
|
|
4
|
+
* cannot render React (Django, Jinja, HTMX, Rails, Laravel, Phoenix). Both
|
|
5
|
+
* files are generated here from the token table and the boot source by
|
|
6
|
+
* `npm run theme`, and shipped as `@calliopelabs/ui/css/*`.
|
|
7
|
+
*/
|
|
8
|
+
/** The products the CSS layer knows by name (`data-product`): the family's brands. */
|
|
9
|
+
export declare const CSS_LAYER_PRODUCTS: readonly Product[];
|
|
10
|
+
/** The whole of css/tokens.css. */
|
|
11
|
+
export declare function renderTokensCss(): string;
|
|
12
|
+
/**
|
|
13
|
+
* The whole of css/appearance.js: the boot script AppearanceScript renders
|
|
14
|
+
* (the same source text), the boot configs of the built-in products, and a
|
|
15
|
+
* small API over them. `set` stores the merged preference and runs the boot
|
|
16
|
+
* script again, so first paint and every later change resolve through one
|
|
17
|
+
* code path. Plain ES5, like the boot script.
|
|
18
|
+
*/
|
|
19
|
+
export declare function renderAppearanceJs(): string;
|