M internal/server/web/dist/index.html => internal/server/web/dist/index.html +2 -2
@@ 10,8 10,8 @@
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500;700&display=swap"
rel="stylesheet"
/>
- <script type="module" crossorigin src="/assets/index-C1oZtiYo.js"></script>
- <link rel="stylesheet" crossorigin href="/assets/index-C5WzPe--.css">
+ <script type="module" crossorigin src="/assets/index-Ch2vbLAm.js"></script>
+ <link rel="stylesheet" crossorigin href="/assets/index-BjmMFw4W.css">
</head>
<body class="density-compact">
<div id="root"></div>
M web/src/features/projects/ProjectsTable.tsx => web/src/features/projects/ProjectsTable.tsx +1 -9
@@ 1,5 1,4 @@
import React from 'react'
-import { useNavigate } from '@tanstack/react-router'
import { EmptyState, ToolDot, Spark } from '../../primitives'
import type { Project } from '../../api/adapters'
@@ 39,8 38,6 @@ function formatLastActive(iso: string): string {
}
export function ProjectsTable({ projects, cursor, onCursor, onOpen }: ProjectsTableProps): React.JSX.Element {
- const navigate = useNavigate()
-
if (projects.length === 0) {
return (
<div className="body body-pad">
@@ 49,11 46,6 @@ export function ProjectsTable({ projects, cursor, onCursor, onOpen }: ProjectsTa
)
}
- function handleOpen(p: Project) {
- onOpen(p)
- void navigate({ to: '/project/$', params: { _splat: encodeURIComponent(p.cwd) } })
- }
-
const maxSessions = Math.max(...projects.map(p => p.sessions), 1)
return (
@@ 75,7 67,7 @@ export function ProjectsTable({ projects, cursor, onCursor, onOpen }: ProjectsTa
key={p.cwd}
className={'projects-row projects-cols' + (isCursor ? ' cursor' : '')}
style={{ gridTemplateColumns: COLS }}
- onClick={() => { onCursor(i); handleOpen(p) }}
+ onClick={() => { onCursor(i); onOpen(p) }}
onMouseEnter={() => onCursor(i)}
>
<span className="mono truncate">{p.cwd}</span>
M web/src/primitives/HorizontalBars.tsx => web/src/primitives/HorizontalBars.tsx +21 -12
@@ 1,34 1,43 @@
import type React from 'react'
-import { Link } from '@tanstack/react-router'
interface HorizontalBarsRow {
label: string
count: number
- href?: string
}
-interface HorizontalBarsProps {
- rows: HorizontalBarsRow[]
+interface HorizontalBarsProps<R extends HorizontalBarsRow = HorizontalBarsRow> {
+ rows: R[]
max: number
+ onActivate?: (row: R) => void
}
-export function HorizontalBars({ rows, max }: HorizontalBarsProps): React.JSX.Element {
+export function HorizontalBars<R extends HorizontalBarsRow>(
+ { rows, max, onActivate }: HorizontalBarsProps<R>,
+): React.JSX.Element {
const safeMax = max > 0 ? max : 1
return (
<div>
{rows.map((row, i) => {
const ratio = row.count / safeMax
- const label = row.href != null ? (
- <Link
- to={row.href}
+ const activatable = onActivate != null
+ const labelStyle: React.CSSProperties = {
+ fontSize: 11,
+ ...(activatable
+ ? { color: 'var(--accent-ink)', cursor: 'pointer' }
+ : {}),
+ }
+ const label = (
+ <span
className="mono truncate"
- style={{ fontSize: 11, color: 'var(--accent-ink)', textDecoration: 'none' }}
+ style={labelStyle}
+ onClick={activatable ? () => onActivate(row) : undefined}
+ role={activatable ? 'button' : undefined}
+ tabIndex={activatable ? 0 : undefined}
+ onKeyDown={activatable ? (e) => { if (e.key === 'Enter') onActivate(row) } : undefined}
>
{row.label}
- </Link>
- ) : (
- <span className="mono truncate" style={{ fontSize: 11 }}>{row.label}</span>
+ </span>
)
return (
M web/src/routes/stats.tsx => web/src/routes/stats.tsx +8 -2
@@ 245,7 245,7 @@ function StatsRoute(): React.JSX.Element {
const topCwdRows = s.topCwd.map(r => ({
label: r.cwd,
count: r.count,
- href: `/project/${encodeURIComponent(r.cwd)}`,
+ cwd: r.cwd,
}))
const topCwdCard = (
<div className="card">
@@ 254,7 254,13 @@ function StatsRoute(): React.JSX.Element {
{s.topCwd.length === 0 ? (
<EmptyState glyph="∅" copy="no cwd data in this range" />
) : (
- <HorizontalBars rows={topCwdRows} max={topCwdMax} />
+ <HorizontalBars
+ rows={topCwdRows}
+ max={topCwdMax}
+ onActivate={(row) => {
+ void navigate({ to: '/project/$', params: { _splat: encodeURIComponent(row.cwd) } })
+ }}
+ />
)}
</div>
</div>
M web/src/styles/stats.css => web/src/styles/stats.css +1 -23
@@ 12,29 12,7 @@
grid-column: 1 / -1;
}
-/* Card base (mirrors prototype.css) */
-.card {
- border: 1px solid var(--rule-2);
- border-radius: 4px;
- background: var(--paper-4);
-}
-
-.card .card-head {
- font-family: var(--mono);
- font-size: 10px;
- text-transform: uppercase;
- letter-spacing: 0.05em;
- color: var(--ink-3);
- padding: 8px 10px 6px;
- border-bottom: 1px solid var(--rule-2);
- display: flex;
- align-items: center;
- gap: 8px;
-}
-
-.card .card-body {
- padding: 10px;
-}
+/* .card / .card-head / .card-body live in shell.css (loaded globally). */
/* Per-tool rollup rows */
.per-tool-row {