From cd48e43fd0ad2df31e2999eecfda7b74e9e1c2a2 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 8 Mar 2026 17:52:04 -0400 Subject: [PATCH 1/5] feat(yellow-brick): dashboard shell with tab navigation and top bar (WS5) - Replace sidebar layout with horizontal tab navigation (Dashboard, Calendar, Content, Social, Analytics, Settings) - New DashboardTopBar: 56px, ozskr.ai wordmark + wallet button, void black bg - New TabNavigation: gold underline active state, mobile scroll, ARIA - YellowBrick placeholder centered at top of every page (max-w-[672px]) - /content placeholder page (route previously missing) - layout.tsx: removes Sidebar/CommandBar, adds YellowBrick + tabs Assisted-by: Claude Code --- src/app/(dashboard)/content/page.tsx | 15 ++++ src/app/(dashboard)/layout.tsx | 41 +++++------ src/components/features/dashboard-top-bar.tsx | 36 +++++++++ src/components/features/tab-navigation.tsx | 73 +++++++++++++++++++ .../features/yellow-brick/YellowBrick.tsx | 32 ++++++++ src/components/features/yellow-brick/index.ts | 1 + 6 files changed, 175 insertions(+), 23 deletions(-) create mode 100644 src/app/(dashboard)/content/page.tsx create mode 100644 src/components/features/dashboard-top-bar.tsx create mode 100644 src/components/features/tab-navigation.tsx create mode 100644 src/components/features/yellow-brick/YellowBrick.tsx create mode 100644 src/components/features/yellow-brick/index.ts diff --git a/src/app/(dashboard)/content/page.tsx b/src/app/(dashboard)/content/page.tsx new file mode 100644 index 0000000..9572764 --- /dev/null +++ b/src/app/(dashboard)/content/page.tsx @@ -0,0 +1,15 @@ +/** + * Content Library Page + * Placeholder β€” full content browsing/management UI coming in a later sprint. + */ + +export default function ContentPage() { + return ( +
+

Content Library

+

+ Coming soon β€” ask your agent to create content, or browse your published posts here. +

+
+ ); +} diff --git a/src/app/(dashboard)/layout.tsx b/src/app/(dashboard)/layout.tsx index 20a5e95..6103c78 100644 --- a/src/app/(dashboard)/layout.tsx +++ b/src/app/(dashboard)/layout.tsx @@ -1,15 +1,13 @@ -'use client'; - /** * Dashboard Layout - * Authenticated layout with sidebar navigation and top bar + * Full-width tab navigation layout with YellowBrick command bar. + * Replaces sidebar-based layout with horizontal tab navigation. */ -import { useState } from 'react'; import { AuthGuard } from '@/components/features/auth-guard'; -import { Sidebar } from '@/components/features/sidebar'; -import { TopBar } from '@/components/features/top-bar'; -import { CommandBar } from '@/components/features/command-bar'; +import { DashboardTopBar } from '@/components/features/dashboard-top-bar'; +import { TabNavigation } from '@/components/features/tab-navigation'; +import { YellowBrick } from '@/components/features/yellow-brick'; import { AchievementToastProvider } from '@/features/gamification/components/achievement-toast'; import { FeedbackWidget } from '@/features/feedback/feedback-widget'; @@ -18,30 +16,27 @@ export default function DashboardLayout({ }: { children: React.ReactNode; }) { - const [commandBarOpen, setCommandBarOpen] = useState(false); - return ( -
- {/* Sidebar */} - +
+ - {/* Main Content Area */} -
- {/* Top Bar */} - setCommandBarOpen(true)} /> +
+ {/* YellowBrick β€” centered, max-w-[672px], full-width on mobile */} +
+ +
- {/* Page Content */} -
-
{children}
+ {/* Horizontal tab navigation */} + + + {/* Page content */} +
+ {children}
- {/* Command Bar */} - - - {/* Feedback Widget */}
diff --git a/src/components/features/dashboard-top-bar.tsx b/src/components/features/dashboard-top-bar.tsx new file mode 100644 index 0000000..8420f38 --- /dev/null +++ b/src/components/features/dashboard-top-bar.tsx @@ -0,0 +1,36 @@ +'use client'; + +/** + * DashboardTopBar + * 56px top bar β€” ozskr.ai wordmark (left) + wallet button (right). + * Replaces the old TopBar which included a command bar trigger. + * The command interaction is now handled by the YellowBrick component. + */ + +import { WalletButton } from '@/features/wallet/components/wallet-button'; + +export function DashboardTopBar() { + return ( +
+ {/* Wordmark */} +
+ + 🧱 + {' ozskr'} + .ai + +
+ + {/* Wallet */} +
+ +
+
+ ); +} diff --git a/src/components/features/tab-navigation.tsx b/src/components/features/tab-navigation.tsx new file mode 100644 index 0000000..5c6e924 --- /dev/null +++ b/src/components/features/tab-navigation.tsx @@ -0,0 +1,73 @@ +'use client'; + +/** + * TabNavigation + * Horizontal tab bar that replaces the sidebar as primary nav. + * Active tab indicated by gold bottom border (design system Brick Gold). + * Tabs scroll horizontally on small screens. + */ + +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; +import { cn } from '@/lib/utils'; + +interface NavTab { + label: string; + href: string; +} + +const TABS: NavTab[] = [ + { label: 'Dashboard', href: '/dashboard' }, + { label: 'Calendar', href: '/calendar' }, + { label: 'Content', href: '/content' }, + { label: 'Social', href: '/social' }, + { label: 'Analytics', href: '/analytics' }, + { label: 'Settings', href: '/settings' }, +]; + +export function TabNavigation() { + const pathname = usePathname(); + + function isActive(href: string): boolean { + if (href === '/dashboard') { + return pathname === '/dashboard'; + } + return pathname.startsWith(href); + } + + return ( + + ); +} diff --git a/src/components/features/yellow-brick/YellowBrick.tsx b/src/components/features/yellow-brick/YellowBrick.tsx new file mode 100644 index 0000000..2f32745 --- /dev/null +++ b/src/components/features/yellow-brick/YellowBrick.tsx @@ -0,0 +1,32 @@ +'use client'; + +/** + * YellowBrick β€” Primary command/query bar placeholder. + * WS1 will replace this with the full interactive implementation. + * This placeholder preserves exact dimensions so the layout is correct. + * + * TODO(WS1): Replace with full YellowBrick interactive component once WS1 merges. + */ + +export function YellowBrick() { + return ( +
+ ✨ + + Talk to your agent... + + ⌘K +
+ ); +} + +export default YellowBrick; diff --git a/src/components/features/yellow-brick/index.ts b/src/components/features/yellow-brick/index.ts new file mode 100644 index 0000000..7e4989d --- /dev/null +++ b/src/components/features/yellow-brick/index.ts @@ -0,0 +1 @@ +export { YellowBrick } from './YellowBrick'; From f4d3a78e37c7cda97ce40174e8b6b46b85cf8655 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 8 Mar 2026 17:57:36 -0400 Subject: [PATCH 2/5] feat(yellow-brick): implement Yellow Brick command bar component (WS1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Zustand store with YellowBrickContext, all state/actions, file attachment - VoiceInput: Web Speech API progressive enhancement, waveform bars animation - YellowBrick: 48px command bar, gold border states, ⌘K/Escape shortcuts, SSE streaming to /api/v1/agent/chat, slash commands, file DnD + chips, voice transcript integration, response panel with copy/dismiss - Barrel export updated with all types Assisted-by: Claude Code --- .../features/yellow-brick/VoiceInput.tsx | 194 +++++ .../features/yellow-brick/YellowBrick.tsx | 726 +++++++++++++++++- src/components/features/yellow-brick/index.ts | 4 + .../yellow-brick/yellow-brick-store.ts | 64 ++ 4 files changed, 969 insertions(+), 19 deletions(-) create mode 100644 src/components/features/yellow-brick/VoiceInput.tsx create mode 100644 src/components/features/yellow-brick/yellow-brick-store.ts diff --git a/src/components/features/yellow-brick/VoiceInput.tsx b/src/components/features/yellow-brick/VoiceInput.tsx new file mode 100644 index 0000000..235c23a --- /dev/null +++ b/src/components/features/yellow-brick/VoiceInput.tsx @@ -0,0 +1,194 @@ +'use client'; + +/** + * VoiceInput + * Progressive enhancement voice input using the Web Speech API. + * Returns null silently when SpeechRecognition is not available. + */ + +import { useCallback, useEffect, useRef, useState } from 'react'; +import { Mic } from 'lucide-react'; +import { cn } from '@/lib/utils'; +import { useYellowBrickStore } from './yellow-brick-store'; + +// ─── Web Speech API types (not in lib.dom.d.ts by default) ───────────────── + +interface SpeechRecognitionEvent extends Event { + readonly resultIndex: number; + readonly results: SpeechRecognitionResultList; +} + +interface SpeechRecognitionResultList { + readonly length: number; + item(index: number): SpeechRecognitionResult; + [index: number]: SpeechRecognitionResult; +} + +interface SpeechRecognitionResult { + readonly isFinal: boolean; + readonly length: number; + item(index: number): SpeechRecognitionAlternative; + [index: number]: SpeechRecognitionAlternative; +} + +interface SpeechRecognitionAlternative { + readonly transcript: string; + readonly confidence: number; +} + +interface SpeechRecognitionErrorEvent extends Event { + readonly error: string; +} + +interface SpeechRecognitionInstance extends EventTarget { + continuous: boolean; + interimResults: boolean; + lang: string; + onresult: ((event: SpeechRecognitionEvent) => void) | null; + onerror: ((event: SpeechRecognitionErrorEvent) => void) | null; + onend: (() => void) | null; + start(): void; + stop(): void; + abort(): void; +} + +interface SpeechRecognitionConstructor { + new (): SpeechRecognitionInstance; +} + +// ─── Feature detect ───────────────────────────────────────────────────────── + +function getSpeechRecognition(): SpeechRecognitionConstructor | null { + if (typeof window === 'undefined') return null; + const w = window as unknown as Record; + return ( + (w['SpeechRecognition'] as SpeechRecognitionConstructor | undefined) ?? + (w['webkitSpeechRecognition'] as SpeechRecognitionConstructor | undefined) ?? + null + ); +} + +// ─── Waveform bars ─────────────────────────────────────────────────────────── + +function WaveformBars() { + return ( +