feat(ui): wire platform core primitives

This commit is contained in:
Saravana Achu Mac 2026-05-06 11:12:09 -07:00
parent 5009a22675
commit b73c969d14
3 changed files with 181 additions and 0 deletions

View File

@ -12,6 +12,8 @@
"smoke:compose": "bash scripts/compose-smoke.sh",
"seed:bootstrap": "pnpm --filter @notelett/backend run bootstrap:seed",
"audit:release-guards": "bash scripts/release-guard-audit.sh",
"audit:ui": "bash scripts/ui-drift-audit.sh",
"audit:ui:strict": "bash scripts/ui-drift-audit.sh --strict",
"dependency:health": "bash scripts/dependency-health.sh",
"verify": "pnpm run typecheck && pnpm run test && pnpm run build",
"prepare": "husky"

42
scripts/ui-drift-audit.sh Normal file
View File

@ -0,0 +1,42 @@
#!/usr/bin/env bash
set -euo pipefail
STRICT=0
if [[ "${1:-}" == "--strict" ]]; then
STRICT=1
fi
ROOT="$(git rev-parse --show-toplevel)"
cd "$ROOT"
report() {
local title="$1"
local pattern="$2"
shift 2
local matches
matches="$(rg -n "$pattern" "$@" --glob '!**/*.test.*' || true)"
echo "=== $title ==="
if [[ -z "$matches" ]]; then
echo "ok: no matches"
return 0
fi
echo "$matches"
echo
return 1
}
failures=0
report "Raw interactive controls" '<button|<input|<textarea|<select' web/src/app web/src/components || failures=$((failures + 1))
report "Legacy global surface classes" 'className="[^"]*(badge|surface-card|surface-muted|input-shell)' web/src/app web/src/components || failures=$((failures + 1))
report "Hardcoded color literals" '#[0-9a-fA-F]{3,8}|rgba?\(' web/src/app web/src/components || failures=$((failures + 1))
report "Direct @bytelyst/ui imports outside adapter" 'from "@bytelyst/ui"|from '\''@bytelyst/ui'\''' web/src/app web/src/components --glob '!web/src/components/ui/Primitives.tsx' || failures=$((failures + 1))
if [[ "$STRICT" == "1" && "$failures" -gt 0 ]]; then
echo "UI drift audit failed in strict mode with $failures category/categories containing matches." >&2
exit 1
fi
echo "UI drift audit completed with $failures non-empty category/categories."

View File

@ -2,9 +2,37 @@ import {
Badge as BytelystBadge,
Button as BytelystButton,
Card as BytelystCard,
DiffCard as BytelystDiffCard,
IconButton as BytelystIconButton,
Input as BytelystInput,
Label as BytelystLabel,
ListItemButton as BytelystListItemButton,
Panel as BytelystPanel,
PanelBody as BytelystPanelBody,
PanelDescription as BytelystPanelDescription,
PanelHeader as BytelystPanelHeader,
PanelTitle as BytelystPanelTitle,
Select as BytelystSelect,
StatusBadge as BytelystStatusBadge,
Textarea as BytelystTextarea,
Timeline as BytelystTimeline,
type BadgeProps,
type ButtonProps,
type CardProps,
type DiffCardProps,
type IconButtonProps,
type InputProps,
type LabelProps,
type ListItemButtonProps,
type PanelBodyProps,
type PanelDescriptionProps,
type PanelHeaderProps,
type PanelProps,
type PanelTitleProps,
type SelectProps,
type StatusBadgeProps,
type TextareaProps,
type TimelineProps,
} from "@bytelyst/ui";
function mergeClassNames(...classes: Array<string | undefined>) {
@ -46,3 +74,112 @@ export function Card({ className, ...props }: CardProps) {
/>
);
}
export function Panel({ className, ...props }: PanelProps) {
return (
<BytelystPanel
className={mergeClassNames(
"rounded-[var(--nl-radius-md)] border-[var(--nl-border-default)] bg-[var(--nl-surface-card-translucent)] shadow-[var(--nl-elevation-md)]",
className,
)}
{...props}
/>
);
}
export function PanelHeader({ className, ...props }: PanelHeaderProps) {
return <BytelystPanelHeader className={mergeClassNames("gap-[var(--nl-space-3)]", className)} {...props} />;
}
export function PanelBody({ className, ...props }: PanelBodyProps) {
return <BytelystPanelBody className={mergeClassNames("gap-[var(--nl-space-3)]", className)} {...props} />;
}
export function PanelTitle({ className, ...props }: PanelTitleProps) {
return <BytelystPanelTitle className={mergeClassNames("text-[var(--nl-text-primary)]", className)} {...props} />;
}
export function PanelDescription({ className, ...props }: PanelDescriptionProps) {
return <BytelystPanelDescription className={mergeClassNames("text-[var(--nl-text-secondary)]", className)} {...props} />;
}
export function IconButton({ className, ...props }: IconButtonProps) {
return (
<BytelystIconButton
className={mergeClassNames(
"rounded-[var(--nl-radius-sm)] focus-visible:ring-[var(--nl-accent-primary)]",
className,
)}
{...props}
/>
);
}
export function ListItemButton({ className, ...props }: ListItemButtonProps) {
return (
<BytelystListItemButton
className={mergeClassNames(
"rounded-[var(--nl-radius-sm)] border-[var(--nl-border-default)] bg-[var(--nl-surface-muted-translucent)] text-[var(--nl-text-primary)]",
className,
)}
{...props}
/>
);
}
export function StatusBadge({ className, ...props }: StatusBadgeProps) {
return (
<BytelystStatusBadge
className={mergeClassNames("border-[var(--nl-border-default)] text-[var(--nl-text-primary)]", className)}
{...props}
/>
);
}
export function Input({ className, ...props }: InputProps) {
return (
<BytelystInput
className={mergeClassNames(
"rounded-[var(--nl-radius-sm)] border-[var(--nl-border-default)] bg-[var(--nl-input-bg)] text-[var(--nl-text-primary)] focus:ring-[var(--nl-accent-primary)]",
className,
)}
{...props}
/>
);
}
export function Textarea({ className, ...props }: TextareaProps) {
return (
<BytelystTextarea
className={mergeClassNames(
"rounded-[var(--nl-radius-sm)] border-[var(--nl-border-default)] bg-[var(--nl-input-bg)] text-[var(--nl-text-primary)] focus:ring-[var(--nl-accent-primary)]",
className,
)}
{...props}
/>
);
}
export function Select({ className, ...props }: SelectProps) {
return (
<BytelystSelect
className={mergeClassNames(
"rounded-[var(--nl-radius-sm)] border-[var(--nl-border-default)] bg-[var(--nl-input-bg)] text-[var(--nl-text-primary)] focus:ring-[var(--nl-accent-primary)]",
className,
)}
{...props}
/>
);
}
export function Label({ className, ...props }: LabelProps) {
return <BytelystLabel className={mergeClassNames("text-[var(--nl-text-secondary)]", className)} {...props} />;
}
export function Timeline({ className, ...props }: TimelineProps) {
return <BytelystTimeline className={mergeClassNames("gap-[var(--nl-space-3)]", className)} {...props} />;
}
export function DiffCard({ className, ...props }: DiffCardProps) {
return <BytelystDiffCard className={mergeClassNames("gap-[var(--nl-space-3)]", className)} {...props} />;
}