diff --git a/docs/ROADMAP_2026_DECISIONS.md b/docs/ROADMAP_2026_DECISIONS.md new file mode 100644 index 00000000..4461b8bf --- /dev/null +++ b/docs/ROADMAP_2026_DECISIONS.md @@ -0,0 +1,115 @@ +# UI/UX Roadmap 2026 — Pragmatic Decisions + +These default decisions are recorded so implementation work can proceed +without blocking. They are **reversible** — any of them can be revisited +via an RFC in `docs/rfc/`. They are intentionally biased toward "ship +something free and reversible now, upgrade if needed." + +Companion to: + +- `learning_ai_uxui_web/docs/ROADMAP_2026.md` §10 TODO ledger (items #9–#13) + +--- + +## #9 — Storybook hosting: **self-hosted, free** + +**Decision.** Use Storybook 8 in each `@bytelyst/*` package, deployed +to Gitea Pages from CI. Skip Chromatic for now. + +**Why.** + +- Chromatic is $149/mo per project for the small plan and we have ~50 + packages — not justifiable until adoption + design-team usage is + proven. +- Visual regression is already covered for product surfaces by the + showcase's Playwright `toHaveScreenshot` baselines (48 today). +- Self-hosted SB on Gitea Pages costs $0 and uses infrastructure that + already runs every other ByteLyst static site. + +**Reopen if.** Designers start filing issues against story flaps that +visual-regression isn't catching, or we need cross-browser/device +coverage that local Playwright Chromium misses. + +--- + +## #10 — `useChat` protocol: **adopt Vercel AI SDK shape, abstract transport** + +**Decision.** `@bytelyst/ai-ui` exposes a `useChat` hook with the same +return shape as Vercel AI SDK's `useChat` (messages, input, +handleSubmit, stop, regenerate, isLoading, error). The **transport** is +pluggable — products inject a `fetcher` or `streamProtocol` so we are +not locked to Vercel's SSE wire format. + +**Why.** + +- Vercel AI SDK is the de-facto standard. Adopting the shape means + every React engineer who has written a chat UI in 2024–2026 already + knows our API. +- Abstracting transport gives us LysnrAI / JarvisJr / NoteLett the + freedom to use OpenAI-native, Anthropic-native, or our own server's + SSE protocol without forking the component. + +**Reopen if.** Vercel changes the hook shape in a breaking way, or +products start needing capabilities (e.g., tool-call streaming +semantics) that the Vercel shape can't express. + +--- + +## #11 — `react-auth` vs `auth-client`: **keep both for now, fold in Wave 7** + +**Decision.** Leave `@bytelyst/react-auth` and `@bytelyst/auth-client` +as separate packages through Waves 1–6. Plan to merge `react-auth` into +`auth-client` (as `auth-client/react` subpath export) in Wave 7 once +both APIs have stabilized. + +**Why.** + +- `auth-client` is framework-agnostic; `react-auth` is the React + binding. Today they have different version cadences and that's fine. +- Merging now means a coordinated major-bump across both packages plus + every product app — too much churn during foundational waves. + +**Reopen if.** Drift between the two packages causes a real bug or a +support burden between now and Wave 7. + +--- + +## #12 — `dashboard-shell` vs `dashboard-components`: **keep both, defer merge** + +**Decision.** Same as #11. Both packages stay independent through +Wave 6. Wave 7 re-evaluates whether to merge `dashboard-components` +into `dashboard-shell/*` subpath exports or vice versa. + +**Why.** Same reasoning — the API contract isn't stable enough yet to +justify the merge cost. + +--- + +## #13 — Mobile-native UI scope: **tokens-only sharing** + +**Decision.** This roadmap (`@bytelyst/*` shared **client** packages +and the web showcase) does **not** include iOS/Android UI components. +Those live in `kotlin-platform-sdk` and `swift-platform-sdk` with their +own roadmaps. **The only thing we share across web/iOS/Android is +`@bytelyst/design-tokens`** — already generating Kotlin and Swift +output from a single JSON source. + +**Why.** + +- Cross-platform UI frameworks (React Native, KMP-Compose) impose + architectural constraints the web platform doesn't have. +- Token-only sharing is the proven model in the industry (Adobe Spectrum, + Shopify Polaris, Salesforce Lightning). + +**Reopen if.** A product needs to ship a web + native experience in +parallel and the divergence cost gets large. + +--- + +## Status closing TODOs + +These decisions close TODO ledger items #9–#13 in +`learning_ai_uxui_web/docs/ROADMAP_2026.md`. The roadmap is updated to +strike them through with a link to this document. + +Last reviewed: 2026-05-27 diff --git a/packages/dashboard-shell/package.json b/packages/dashboard-shell/package.json index 607dace5..8e1d3b8d 100644 --- a/packages/dashboard-shell/package.json +++ b/packages/dashboard-shell/package.json @@ -1,6 +1,6 @@ { "name": "@bytelyst/dashboard-shell", - "version": "0.1.7", + "version": "0.2.0", "description": "Configurable Next.js dashboard layout with sidebar, profile, billing, and settings pages", "type": "module", "main": "./dist/index.js", diff --git a/packages/dashboard-shell/src/DashboardShell.tsx b/packages/dashboard-shell/src/DashboardShell.tsx index d7457da6..3bb7c290 100644 --- a/packages/dashboard-shell/src/DashboardShell.tsx +++ b/packages/dashboard-shell/src/DashboardShell.tsx @@ -13,6 +13,7 @@ export function DashboardShell({ features = {}, onSignOut, onNavigate, + routePrefix = '', sidebarFooter, topBarActions, children, @@ -43,6 +44,7 @@ export function DashboardShell({ footer={sidebarFooter} collapsed={sidebarCollapsed} onToggleCollapse={() => setSidebarCollapsed(!sidebarCollapsed)} + routePrefix={routePrefix} /> {/* Main content area */} @@ -54,6 +56,7 @@ export function DashboardShell({ onSignOut={onSignOut} onNavigate={onNavigate} actions={topBarActions} + routePrefix={routePrefix} /> {/* Page content */} diff --git a/packages/dashboard-shell/src/Sidebar.tsx b/packages/dashboard-shell/src/Sidebar.tsx index 4bc77430..ae52bf9e 100644 --- a/packages/dashboard-shell/src/Sidebar.tsx +++ b/packages/dashboard-shell/src/Sidebar.tsx @@ -85,7 +85,9 @@ export function Sidebar({ footer, collapsed = false, onToggleCollapse, + routePrefix = '', }: SidebarProps): ReactNode { + const settingsHref = `${routePrefix}/settings`; const sections: NavSection[] = isNavSections(nav) ? nav : [{ items: nav }]; const isActive = (href: string) => pathname === href || pathname.startsWith(href + '/'); @@ -93,7 +95,7 @@ export function Sidebar({ // Add built-in settings nav if enabled and not already present const hasSettings = features.settings !== false; const allItems = sections.flatMap(s => s.items); - const settingsExists = allItems.some(i => i.href === '/settings'); + const settingsExists = allItems.some(i => i.href === settingsHref || i.href === '/settings'); return (