Desktop v2 design system (design.css tokens for both themes, ui/ds.tsx: PageHeader, Hero, Section, StatTile, Pipeline, StatusPill, EmptyState, Toolbar, Tabs, Dialog, tint and screenshot hooks), app shell: sidebar status dots, frosted top bar, app page Hero with screenshots and the two-store pipeline, pill tabs with blocker counts

This commit is contained in:
CRTkafa 2026-10-22 23:12:03 +08:00
parent 62366d95a7
commit bd316723f5
6 changed files with 559 additions and 172 deletions

View file

@ -23,6 +23,8 @@ import { type Command, Palette } from './ui/Palette';
import { ExportMenu, useExportItems, useRunExport } from './ui/Exports';
import { ErrorBoundary } from './ui/ErrorBoundary';
import { Toasts } from './ui/Toasts';
import { worstTone } from './ui/ds';
import { attention } from './lib/model';
const WS_ICON: Record<WorkspaceScreen, IconName> = { apps: 'folder', accounts: 'building', ignored: 'eyeOff', dependencies: 'package', settings: 'settings' };
export const WS_KEY: Record<WorkspaceScreen, Key> = { apps: 'nav.apps', accounts: 'nav.accounts', ignored: 'nav.ignored', dependencies: 'nav.dependencies', settings: 'nav.settings' };
@ -146,12 +148,13 @@ export function App({ onTheme }: { onTheme: () => void }) {
const appItem = (a: UnifiedApp) => {
const e = d.statusFor(a);
const missing = !!a.project?.missing;
const tone = missing ? 'bad' : (worstTone(attention(a.key, a.name, e, d.liveFor(a))) ?? (e ? 'ok' : null));
const bad = missing ? 1 : (e?.errors.length ?? 0) + (e?.check?.problems.length ?? 0);
return (
<div role="listitem" key={a.key}>
<button className="nav nav-app" aria-current={current({ screen: 'app', appId: a.key, tab: 'releases' })}
onClick={() => go({ screen: 'app', appId: a.key, tab: route.screen === 'app' && tabsFor(!!a.project).includes(route.tab) ? route.tab : 'releases' })}>
<AppIcon name={a.name} src={d.iconFor(a)} />
<span className="ds-side-ic"><AppIcon name={a.name} src={d.iconFor(a)} />{tone && <span className={`ds-sdot ${tone}`} role="img" aria-label={tone === 'bad' ? t('pf.blocker') : tone === 'warn' ? t('pf.warning') : t('pf.note')} />}</span>
<span className="grow">{a.name}</span>
{!a.project && <span className="so-dot" title={t('apps.storeOnlyHint')}><Icon name="cloud" /></span>}
{missing ? <span className="tag-count bad" title={t('folder.missingShort')} aria-label={t('folder.missingShort')}>!</span>