UI & Pages
The shadcn/ui stack, command palette, layout, and built-in pages.
The renderer is a standard React SPA: Tailwind v4, shadcn/ui, react-router with lazy routes, a cmdk command palette, and sonner toasts. This page maps the UI layer and inventories the pages you inherit.
The stack
| Concern | Library | Where |
|---|---|---|
| Styling | Tailwind v4 + tw-animate-css | app/styles/globals.css |
| Components | shadcn/ui (new-york style) | app/components/ui/, components.json |
| Command palette | cmdk | app/components/command-palette/ |
| Toasts | sonner | app/components/ui/ (Toaster) + use-toast |
| Animation | framer-motion | onboarding, transitions |
| Routing | react-router 7 (HashRouter) | app/app.tsx |
Routes use HashRouter — the renderer is served from a file:// document in
packaged builds, where hash routing avoids server-side path resolution. Add
shadcn components with pnpm dlx shadcn@latest add <component>; the new-york
style and CSS-variable theming are already configured in components.json. See
Theming for the accent-color layer.
Layout & sidebar
AppShell (app/components/layout/) wraps the authenticated pages with the
custom titlebar, sidebar, and content area. Auth screens use ShellLessLayout
instead. The sidebar is data-driven from sidebar-items.ts, grouped into
Workspace, Template (showcase, flag-gated), and Application:
// app/components/layout/sidebar-items.ts
export const SIDEBAR_GROUPS = [
{ id: 'workspace', items: [{ id: 'home', to: '/home', /* ... */ }] },
// [template-demo] Template group — present only when showcase is enabled
{ id: 'application', items: [/* billing (flag-gated), logs, help, settings, about */] },
]Billing appears only when billingConfig.enabled, and the Template group only
when showcaseConfig.enabled — so a stripped build has no dead entries.
Command palette
The palette opens with Cmd/Ctrl+K (an in-window shortcut, not a global
hotkey — see Notifications & Shortcuts).
Its contents come from command sources in command-sources.ts:
| Source | Provides |
|---|---|
routesSource | App routes (Home, Settings, About, Account, Logs, Help, Update, Billing when enabled). |
settingsSource | Every searchable setting, deep-linking to its tab (/settings?tab=…). |
recentFilesSource | Recently opened files. |
showcaseSource | Showcase demos (flag-gated, [template-demo]). |
Register your own commands from app code with registerCommandSource, which
returns an unregister function:
import { registerCommandSource } from '@/app/components/command-palette/command-sources'
const off = registerCommandSource((ctx) => [
{ id: 'export', title: ctx.t('common:actions.export'), group: 'navigation', perform: () => {/* ... */} },
])Page inventory
Routes are code-split (React.lazy) so the initial bundle carries only the
shell; each page resolves on first navigation.
| Page | Route | What it is |
|---|---|---|
| Home | /home | Landing dashboard: a rebrand checklist, neutral quick actions (Logs/Settings/Help), and — when showcase is on — the capability catalog. |
| Help | /help | Documentation link, feedback mailto, and a bug-report link to ${REPO_URL}/issues/new, plus a jump to Logs. |
| About | /about | App/system diagnostics with a redacted "Copy diagnostics" button. |
| Update | /update | Update status and manual check/download/install. See Auto Update. |
| Logs | /logs | Log file path + "Open log folder". See Observability. |
| Settings | /settings | The settings center. See Settings Store. |
| Account | /account | Profile + avatar. See Example Features. |
| Billing | /billing | Plans and entitlement (flag-gated). See Billing. |
| Error / NotFound | /error, * | Themed ErrorPage and NotFoundPage. |
The Help page's support surfaces are brand-driven: the feedback mailto uses
SUPPORT_EMAIL and the bug report uses REPO_URL from shared/constants.ts, so
rebranding redirects them automatically.
Onboarding
OnboardingGate (app/components/onboarding/) reads the persisted settings and
shows a first-run flow until onboardingCompleted is set. It optionally gates on
a EULA: leave EULA_URL empty in shared/constants.ts to ship without one
(the default), or set it (with EULA_VERSION) to require acceptance — bumping
the version forces re-acceptance. Completion is persisted through the settings
store, so it shows once per install.
Adding a page
Add the route
Add a lazy import and a <Route> inside the AppShell element in app/app.tsx.
Add it to navigation
Add a sidebar-items.ts entry (and/or let routesSource surface it in the
palette).
Add i18n keys
Add the nav label and page copy to app/locales/{en,zh}/*.json. See
Internationalization.
(If deep-linkable) allow-list the route
Add it to navigationRouteSchema in event-schema.ts so a deep link can reach
it. See Deep Links.
