rvlib-shipkit 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +68 -0
- package/README.md +17 -17
- package/package.json +3 -2
- package/src/check.ts +68 -95
- package/src/checks/SK001-package-json-shape.check.ts +6 -2
- package/src/checks/SK002-contracts-fulfillness.check.ts +4 -0
- package/src/checks/SK003-claude-md-shape.check.ts +5 -1
- package/src/checks/SK004-gitignore-policy.check.ts +7 -4
- package/src/checks/SK005-rvlib-versions.check.ts +53 -21
- package/src/checks/SK006-npm-publish-safety.check.ts +4 -0
- package/src/checks/SK007-github-visibility.check.ts +4 -2
- package/src/checks/SK008-reviewer-agent.check.ts +6 -4
- package/src/checks/SK009-commit-ai-tells.check.ts +4 -2
- package/src/checks/SK010-undeclared-unit.check.ts +4 -0
- package/src/checks/SK011-sibling-unit-ranges.check.ts +4 -0
- package/src/checks/SK012-config-barrel-import.check.ts +5 -1
- package/src/checks/SK013-one-way-in-policy.check.ts +7 -3
- package/src/checks/SK014-prose-ste.check.ts +4 -0
- package/src/checks/SK015-nested-repo.check.ts +4 -2
- package/src/checks/SK016-legacy-check-shapes.check.ts +13 -12
- package/src/checks/SK017-vscode-hub-task.check.ts +22 -7
- package/src/checks/SK018-test-files-run.check.ts +6 -2
- package/src/checks/SK019-tests-run-in-units.check.ts +8 -4
- package/src/checks/SK020-self-spawning-check.check.ts +4 -0
- package/src/checks/SK022-machine-runs-the-pin.check.ts +6 -4
- package/src/checks/SK024-identity-colors.check.ts +5 -3
- package/src/checks/SK025-release-drift.check.ts +4 -5
- package/src/checks/SK026-asks-autoclose.check.ts +4 -4
- package/src/checks/SK027-agent-wiki-links.check.ts +4 -1
- package/src/checks/SK028-readme.check.ts +5 -1
- package/src/checks/SK029-explicit-check-files.check.ts +95 -0
- package/src/cli/cmd_Asks.ts +5 -5
- package/src/cli/cmd_Assets.ts +7 -7
- package/src/cli/cmd_Bump.ts +12 -11
- package/src/cli/cmd_Check.ts +8 -7
- package/src/cli/cmd_Checks.ts +7 -6
- package/src/cli/cmd_Ci.ts +12 -11
- package/src/cli/cmd_Commands.ts +16 -13
- package/src/cli/cmd_ConfigKeys.ts +7 -6
- package/src/cli/cmd_Contracts.ts +3 -3
- package/src/cli/cmd_CrossRepoActivity.ts +6 -5
- package/src/cli/cmd_Fleet.ts +9 -7
- package/src/cli/cmd_GlobalProcesses.ts +116 -4
- package/src/cli/cmd_Home.ts +23 -17
- package/src/cli/cmd_Hooks.ts +6 -5
- package/src/cli/cmd_Init.ts +5 -4
- package/src/cli/cmd_Keys.ts +8 -7
- package/src/cli/cmd_Mv.ts +7 -6
- package/src/cli/cmd_Needs.ts +4 -3
- package/src/cli/cmd_Overview.ts +4 -3
- package/src/cli/cmd_Pb.ts +2 -2
- package/src/cli/cmd_Planned.ts +6 -5
- package/src/cli/cmd_Prose.ts +5 -5
- package/src/cli/cmd_Reflect.ts +7 -7
- package/src/cli/cmd_Release.ts +6 -5
- package/src/cli/cmd_Releases.ts +5 -4
- package/src/cli/cmd_Serve.ts +4 -3
- package/src/cli/cmd_Smoke.ts +5 -4
- package/src/cli/cmd_Start.ts +7 -6
- package/src/cli/cmd_Status.ts +4 -3
- package/src/cli/cmd_Tui.ts +3 -2
- package/src/cli/cmd_When.ts +71 -0
- package/src/cli/cmd_Wiki.ts +4 -4
- package/src/cli.ts +7 -2
- package/src/collections/checks.ts +2 -0
- package/src/collections/collection-types.ts +9 -4
- package/src/core/canonical-scripts.ts +1 -1
- package/src/core/check-runner.ts +37 -20
- package/src/core/config-rewrite.ts +37 -24
- package/src/core/discover-checks.ts +23 -2
- package/src/core/legacy-shapes.ts +39 -10
- package/src/core/one-way-in-policy.ts +2 -2
- package/src/core/outcome-words.ts +18 -2
- package/src/core/port-check-cli.ts +52 -0
- package/src/core/port-check-file.ts +295 -0
- package/src/core/port-check-files.ts +31 -0
- package/src/core/retention.ts +0 -14
- package/src/core/run-check.ts +12 -9
- package/src/core/run-ci.ts +8 -4
- package/src/core/run-fix.ts +30 -23
- package/src/core/run-fixers.ts +42 -5
- package/src/core/runs-matrix.ts +179 -0
- package/src/core/runs.ts +242 -0
- package/src/core/update-policy.ts +20 -0
- package/src/core/when-block.ts +29 -30
- package/src/core/when.ts +41 -103
- package/src/core/wire.ts +10 -14
- package/src/fleet/scan-fleet.ts +20 -4
- package/src/fleet/update-fleet.ts +6 -6
- package/src/home/home-launchd.ts +20 -4
- package/src/home/home-routes.ts +12 -1
- package/src/home/hubs.ts +5 -5
- package/src/home/pm.ts +8 -0
- package/src/home/run-pm.ts +1 -0
- package/src/home/serve-web.ts +2 -2
- package/src/home/supervise.ts +24 -1
- package/src/hooks/run-hooks.ts +48 -4
- package/src/init-repo.ts +6 -6
- package/src/ledger/gate-checks.ts +113 -59
- package/src/ledger/git-key.ts +5 -1
- package/src/ledger/ledger.ts +5 -5
- package/src/ledger/toolchain-identity.ts +45 -27
- package/src/plan/repo-source.ts +18 -39
- package/src/release/run-release.ts +5 -0
- package/src/repo-config.ts +30 -55
- package/src/rules/rule.ts +2 -2
- package/src/scaffold/files/agent-coding.md +1 -1
- package/src/scaffold/files/agent-index.md +1 -1
- package/src/scaffold/files/verify-skill.md +2 -2
- package/src/serve/hub-app/activity-st.ts +5 -4
- package/src/serve/hub-app/activity-view.tsx +1 -1
- package/src/serve/hub-app/admin-view.tsx +13 -11
- package/src/serve/hub-app/check-view.tsx +66 -46
- package/src/serve/hub-app/check-words.ts +1 -11
- package/src/serve/hub-app/checks-svc.ts +14 -35
- package/src/serve/hub-app/col-tables-view.tsx +13 -6
- package/src/serve/hub-app/cols-svc.ts +5 -4
- package/src/serve/hub-app/dock-st.ts +3 -2
- package/src/serve/hub-app/dock-view.tsx +36 -0
- package/src/serve/hub-app/dock.css +6 -0
- package/src/serve/hub-app/float-cards.tsx +30 -0
- package/src/serve/hub-app/floating.tsx +41 -4
- package/src/serve/hub-app/git-st.ts +42 -0
- package/src/serve/hub-app/git-view.tsx +61 -0
- package/src/serve/hub-app/hash-route.ts +45 -1
- package/src/serve/hub-app/header-bar.tsx +62 -72
- package/src/serve/hub-app/health-svc.ts +3 -2
- package/src/serve/hub-app/home-st.ts +14 -2
- package/src/serve/hub-app/home-strip.css +43 -8
- package/src/serve/hub-app/home-strip.tsx +111 -47
- package/src/serve/hub-app/home.html +1 -0
- package/src/serve/hub-app/hub-app.tsx +14 -4
- package/src/serve/hub-app/hub-types.ts +5 -2
- package/src/serve/hub-app/hub.css +65 -43
- package/src/serve/hub-app/icons.tsx +11 -0
- package/src/serve/hub-app/index.html +1 -0
- package/src/serve/hub-app/keys.ts +5 -2
- package/src/serve/hub-app/live-svc.ts +3 -2
- package/src/serve/hub-app/machine-st.ts +18 -22
- package/src/serve/hub-app/plan-view.tsx +51 -0
- package/src/serve/hub-app/planned-st.ts +9 -4
- package/src/serve/hub-app/processes-view.tsx +38 -0
- package/src/serve/hub-app/projects-svc.ts +15 -10
- package/src/serve/hub-app/prose-hits-st.ts +3 -2
- package/src/serve/hub-app/reach.ts +36 -0
- package/src/serve/hub-app/readme-svc.ts +3 -2
- package/src/serve/hub-app/repo-sections.ts +117 -0
- package/src/serve/hub-app/repo-st.ts +46 -9
- package/src/serve/hub-app/sidebar.tsx +12 -37
- package/src/serve/hub-app/timeline-svc.ts +3 -2
- package/src/serve/hub-app/tree-build.ts +149 -155
- package/src/serve/hub-app/tree-row.tsx +39 -9
- package/src/serve/hub-app/tree-svc.ts +70 -49
- package/src/serve/hub-app/when-view.tsx +89 -0
- package/src/serve/hub-app/when.css +17 -0
- package/src/serve/hub-runtime.ts +3 -16
- package/src/serve/run-serve.ts +1 -1
- package/src/serve/serve-hub.ts +4 -14
- package/src/serve/hub-app/processes-svc.ts +0 -63
- package/src/serve/hub-app/repo-sections.tsx +0 -61
- package/src/serve/repo-processes.ts +0 -89
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
// the home's strip over every page it serves: one line that never wraps. On a
|
|
2
2
|
// repo page the shown repo (its icon, its name in its own colour, the two
|
|
3
|
-
// swatches that set those colours),
|
|
4
|
-
// on the right its processes
|
|
5
|
-
// (header-bar.tsx); on the overview and the plan, the page's name.
|
|
6
|
-
//
|
|
7
|
-
// daemon
|
|
3
|
+
// swatches that set those colours), its check… button and a spinner while
|
|
4
|
+
// it runs, then on the right its processes and its editor icons
|
|
5
|
+
// (header-bar.tsx); on the overview and the plan, the page's name. The strip
|
|
6
|
+
// carries no trouble text: the shown hub's trouble and the machine's (the
|
|
7
|
+
// daemon, pm or the web not answering, a child or a global process crashed, a
|
|
8
|
+
// failed poll) are cards floating under it at the top right, only while they
|
|
9
|
+
// last (float-cards.tsx). The machine not answering is said there and nowhere
|
|
10
|
+
// else (reach.ts). The places themselves
|
|
8
11
|
// are the place buttons at the top of the project list (the picker's ⌘J,
|
|
9
12
|
// then ⌘K, ⌘L, ⌘') and the first three entries of the
|
|
10
13
|
// picker. What administers one repo (reload its hub, keep it active, archive
|
|
11
14
|
// it) is its admin page (admin-view.tsx). NotUp stands in for the hub page
|
|
12
15
|
// while its hub cannot answer, with the fix. Every link of the home is a
|
|
13
|
-
// page of the same app: a plain click moves it in place.
|
|
14
|
-
// down is said in MachineToasts, floating over the page column's bottom
|
|
15
|
-
// right corner, taking no room.
|
|
16
|
+
// page of the same app: a plain click moves it in place.
|
|
16
17
|
import { observer } from 'mobx-react-lite'
|
|
17
18
|
import type { ReactNode } from 'react'
|
|
18
|
-
import type { ProcessIcon, ResourceView } from '../../home/resources.ts'
|
|
19
|
+
import type { OpenHow, ProcessIcon, ResourceView } from '../../home/resources.ts'
|
|
19
20
|
import type { WorkerView } from '../../home/hubs.ts'
|
|
20
21
|
import { NOT_INSTALLED } from '../../home/home-words.ts'
|
|
21
22
|
import { splitWidgetHead } from '../../home/widget-text.ts'
|
|
@@ -25,8 +26,10 @@ import { processIcon, stateWords, type ProcessRow } from '../../home/process-row
|
|
|
25
26
|
import { plainClick, repoPage, type HomeSt } from './home-st.ts'
|
|
26
27
|
import { stampWords } from './time-ago.tsx'
|
|
27
28
|
import { Icon, ICON } from './icons.tsx'
|
|
28
|
-
import { Tip } from './floating.tsx'
|
|
29
|
-
import {
|
|
29
|
+
import { Pop, Tip } from './floating.tsx'
|
|
30
|
+
import { renderMd } from './md.ts'
|
|
31
|
+
import { FloatCards, type Card } from './float-cards.tsx'
|
|
32
|
+
import { hubCards, notUp, RepoOpeners, RepoRun, RepoTools } from './header-bar.tsx'
|
|
30
33
|
|
|
31
34
|
const STATUS_WORD: Record<WorkerView['status'], string> = { up: 'up', starting: 'starting', crashed: 'crashed', down: 'down', stopping: 'stopping' }
|
|
32
35
|
|
|
@@ -42,9 +45,9 @@ export const HomeLink = ({ app, to, className, title, children }: { app: HomeSt;
|
|
|
42
45
|
}}>{children}</a>
|
|
43
46
|
)
|
|
44
47
|
|
|
45
|
-
const Log = ({ lines }: { lines: string[] }) => (lines.length === 0 ? null : <pre className="hlog">{lines.join('\n')}</pre>)
|
|
48
|
+
export const Log = ({ lines }: { lines: string[] }) => (lines.length === 0 ? null : <pre className="hlog">{lines.join('\n')}</pre>)
|
|
46
49
|
|
|
47
|
-
/**
|
|
50
|
+
/** a crashed global process's switch (start it again); its log floats under it on hover */
|
|
48
51
|
export const ResourceChip = observer(({ m, r }: { m: MachineSt; r: ResourceView }) => {
|
|
49
52
|
const on = r.status === 'up' || r.status === 'starting'
|
|
50
53
|
const title = [r.description, r.where != null ? `on ${r.where}` : null, r.note].filter((x) => x != null).join(' · ')
|
|
@@ -249,18 +252,92 @@ const ShownRepo = observer(({ app, name }: { app: HomeSt; name: string }) => {
|
|
|
249
252
|
)
|
|
250
253
|
})
|
|
251
254
|
|
|
252
|
-
/**
|
|
253
|
-
*
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
255
|
+
/** the machine not answering, as ONE card: the web (pm behind it is then
|
|
256
|
+
* unknown, not down), the daemon, pm, or the daemon and pm together */
|
|
257
|
+
const downCard = (m: MachineSt): Card[] => {
|
|
258
|
+
const web = m.down !== '' && m.downWho === 'web'
|
|
259
|
+
const pm = m.pmDown !== '' && !web
|
|
260
|
+
if (m.down === '' && !pm) return []
|
|
261
|
+
const head = m.down === '' ? 'pm down' : pm ? 'daemon and pm down' : `${m.downWho} down`
|
|
262
|
+
return [{ key: 'down', tone: 'red', head, body: m.down === '' ? m.pmDown : pm ? <>{m.down}<br />{m.pmDown}</> : m.down }]
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** what is wrong with the machine itself, one card each: the machine not
|
|
266
|
+
* answering, a crashed child of pm, a crashed global process (its switch
|
|
267
|
+
* starts it again), a failed poll. While pm is silent its last answer says
|
|
268
|
+
* nothing here */
|
|
269
|
+
const machineCards = (m: MachineSt): Card[] => {
|
|
270
|
+
const pm = m.pmDown !== '' ? null : m.pm
|
|
271
|
+
return [
|
|
272
|
+
...downCard(m),
|
|
273
|
+
...(pm?.children ?? []).filter((c) => c.status === 'crashed' || c.status === 'down').map((c): Card => ({ key: `child:${c.id}`, tone: 'red', head: `${c.id} ${STATUS_WORD[c.status]}`, body: c.note ?? c.tail.at(-1) ?? '' })),
|
|
274
|
+
...(pm?.resources ?? []).filter((r) => r.status === 'crashed').map((r): Card => ({ key: `res:${r.id}`, tone: 'red', head: 'crashed', body: <ResourceChip m={m} r={r} /> })),
|
|
275
|
+
...(m.error !== '' ? [{ key: 'poll', tone: 'red' as const, body: m.error }] : []),
|
|
276
|
+
]
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** a global process's menu: start or stop, restart, its url opened three
|
|
280
|
+
* ways (each starts it first when it is down), then its declared actions and
|
|
281
|
+
* take over. Every entry is a click on purpose, never the chip itself */
|
|
282
|
+
/** `lastLog: false` where the whole log is drawn under it, so its last line is not said twice */
|
|
283
|
+
export const ProcessMenu = observer(({ m, r, close, lastLog = true }: { m: MachineSt; r: ProcessRow; close: () => void; lastLog?: boolean }) => {
|
|
284
|
+
const on = r.status === 'up' || r.status === 'starting'
|
|
285
|
+
const first = r.status === 'up' ? '' : ', starts it first'
|
|
286
|
+
const act = (a: Parameters<MachineSt['processAct']>[1], action?: string) => () => { close(); void m.processAct(r.id, a, action) }
|
|
287
|
+
const open = (how: OpenHow) => () => { close(); void m.openProcess(r.id, how) }
|
|
288
|
+
const log = lastLog ? r.lastLog : null
|
|
289
|
+
const about = r.description != null && r.description !== '' || r.note != null || log != null
|
|
290
|
+
return (
|
|
291
|
+
<div className="hmenu2">
|
|
292
|
+
<div className="hmenu hmacts">
|
|
293
|
+
<div className="hmhead"><i className={`pdot ${r.status}`} /><b>{r.label}</b><span>{r.status}</span></div>
|
|
294
|
+
{on ? (
|
|
295
|
+
<button className="hmitem" disabled={r.status === 'stopping' || !r.canStop} onClick={act('stop')}>
|
|
296
|
+
<Icon d={ICON.stop} /><span><b>stop</b>{r.canStop ? null : <small>started outside pm, so pm cannot stop it</small>}</span>
|
|
297
|
+
</button>
|
|
298
|
+
) : (
|
|
299
|
+
<button className="hmitem" disabled={r.status === 'unknown'} onClick={act('start')}><Icon d={ICON.play} /><span><b>start</b></span></button>
|
|
300
|
+
)}
|
|
301
|
+
{r.canRestart ? <button className="hmitem" onClick={act('restart')}><Icon d={ICON.restart} /><span><b>restart</b></span></button> : null}
|
|
302
|
+
{r.url == null ? null : (
|
|
303
|
+
<>
|
|
304
|
+
<button className="hmitem" onClick={open('browser')}><Icon d={ICON.browser} /><span><b>open in a browser tab</b><small>{r.host}{first}</small></span></button>
|
|
305
|
+
<button className="hmitem" onClick={open('chrome')}><Icon d={ICON.browser} /><span><b>open in Chrome</b><small>{r.host}{first}</small></span></button>
|
|
306
|
+
<button className="hmitem" disabled={r.appBusy} onClick={open('app')}><Icon d={ICON.window} /><span><b>open as an app</b><small>{r.appBusy ? `building the ${r.label} app` : `${r.label} in the dock${first}`}</small></span></button>
|
|
307
|
+
</>
|
|
308
|
+
)}
|
|
309
|
+
{r.actions.map((a) => <button key={a.id} className="hmitem" disabled={r.running != null} onClick={act('action', a.id)}><Icon d={ICON.process} /><span><b>{a.label}</b></span></button>)}
|
|
310
|
+
{r.takeOver?.ok === true ? <button className="hmitem" onClick={act('takeover')}><Icon d={ICON.restart} /><span><b>take over</b><small>stop the copy started elsewhere and start one pm supervises</small></span></button> : null}
|
|
311
|
+
</div>
|
|
312
|
+
{about ? (
|
|
313
|
+
<div className="hmabout">
|
|
314
|
+
{r.description != null && r.description !== '' ? <div className="md" dangerouslySetInnerHTML={{ __html: renderMd(r.description) }} /> : null}
|
|
315
|
+
{r.note != null ? <small className="hmnote">{r.note}</small> : null}
|
|
316
|
+
{log != null ? <code className="hmlog" title="its last log line">{log}</code> : null}
|
|
317
|
+
</div>
|
|
318
|
+
) : null}
|
|
319
|
+
</div>
|
|
320
|
+
)
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
/** the shown repo's global processes, one chip each, right after its name: its
|
|
324
|
+
* dot and label, and a click opens its menu (ProcessMenu). pm runs them, so
|
|
325
|
+
* they show whether the repo's hub answers or not */
|
|
326
|
+
const RepoProcesses = observer(({ m, name }: { m: MachineSt; name: string }) => {
|
|
327
|
+
const rows = m.processGroups.find((g) => g.core !== true && g.repo === name)?.rows ?? []
|
|
328
|
+
if (rows.length === 0) return null
|
|
329
|
+
return (
|
|
330
|
+
<span className="hprocs hgprocs">
|
|
331
|
+
{rows.map((r) => (
|
|
332
|
+
<span key={r.id} className="hproc">
|
|
333
|
+
<Pop className={`ghost ${r.status}`} popClass="fpop fpopw" aria-label={`${r.label}, ${r.status}: open its menu`} label={<><i className={`pdot ${r.status}`} />{r.label}</>}>
|
|
334
|
+
{(close) => <ProcessMenu m={m} r={r} close={close} />}
|
|
335
|
+
</Pop>
|
|
336
|
+
</span>
|
|
337
|
+
))}
|
|
338
|
+
</span>
|
|
339
|
+
)
|
|
340
|
+
})
|
|
264
341
|
|
|
265
342
|
export const HomeStrip = observer(({ app }: { app: HomeSt }) => {
|
|
266
343
|
const m = app.machine
|
|
@@ -273,33 +350,17 @@ export const HomeStrip = observer(({ app }: { app: HomeSt }) => {
|
|
|
273
350
|
<>
|
|
274
351
|
<div className="hstrip">
|
|
275
352
|
{name == null ? <span className="hplace">{app.page === 'overview' ? 'overview' : app.page}</span> : <ShownRepo app={app} name={name} />}
|
|
276
|
-
{
|
|
353
|
+
{name == null ? null : <RepoProcesses m={m} name={name} />}
|
|
277
354
|
<span className="sp" />
|
|
278
355
|
{live == null ? null : <RepoTools app={live} />}
|
|
279
|
-
<
|
|
356
|
+
{live == null ? null : <RepoRun app={live} />}
|
|
280
357
|
{live == null ? null : <RepoOpeners app={live} />}
|
|
281
358
|
</div>
|
|
282
|
-
<
|
|
359
|
+
<FloatCards cards={[...(live == null ? [] : hubCards(live)), ...machineCards(m)]} />
|
|
283
360
|
</>
|
|
284
361
|
)
|
|
285
362
|
})
|
|
286
363
|
|
|
287
|
-
/** the daemon or pm not answering, as toasts floating over the bottom right
|
|
288
|
-
* corner of the page column, just above the dock. They take no room, so
|
|
289
|
-
* one coming or going moves nothing, and nothing is drawn while all is
|
|
290
|
-
* well. Only a failed probe sets down or pmDown: before the first answer
|
|
291
|
-
* there is none */
|
|
292
|
-
export const MachineToasts = observer(({ app }: { app: HomeSt }) => {
|
|
293
|
-
const m = app.machine
|
|
294
|
-
if (m == null || (m.down === '' && m.pmDown === '')) return null
|
|
295
|
-
return (
|
|
296
|
-
<div className="htoasts">
|
|
297
|
-
{m.down !== '' ? <div className="htoast" role="alert" title={m.down}><b>daemon down</b> {m.down}</div> : null}
|
|
298
|
-
{m.pmDown !== '' ? <div className="htoast" role="alert" title={m.pmDown}><b>pm down</b> {m.pmDown}</div> : null}
|
|
299
|
-
</div>
|
|
300
|
-
)
|
|
301
|
-
})
|
|
302
|
-
|
|
303
364
|
/** archive the repo or bring it back: `archived` in its repo.config.ts, and
|
|
304
365
|
* only that key. Archived, it stays listed and nothing runs there */
|
|
305
366
|
export const ArchiveButton = observer(({ app, name }: { app: HomeSt; name: string }) => {
|
|
@@ -326,7 +387,7 @@ export const NotUp = observer(({ app }: { app: HomeSt }) => {
|
|
|
326
387
|
<ArchiveButton app={app} name={name} />
|
|
327
388
|
</div>
|
|
328
389
|
)
|
|
329
|
-
if (w == null) return <div className="hwait"><p className="twait"><span className="spin" />{
|
|
390
|
+
if (w == null) return <div className="hwait"><p className="twait"><span className="spin" />{`looking for ${name}…`}</p></div>
|
|
330
391
|
return (
|
|
331
392
|
<div className="hwait">
|
|
332
393
|
{w.blocked != null ? <p>{w.blocked}</p> : <p><Dot s={w.status} />{name}'s hub is {STATUS_WORD[w.status]}{w.note != null ? `: ${w.note}` : ''}</p>}
|
|
@@ -366,9 +427,12 @@ export const Overview = observer(({ app }: { app: HomeSt }) => {
|
|
|
366
427
|
const m = app.machine
|
|
367
428
|
if (m == null) return null
|
|
368
429
|
// the table waits for pm's first word and for the list's order, so its
|
|
369
|
-
// rows never arrive or reorder after it paints
|
|
430
|
+
// rows never arrive or reorder after it paints. The daemon silent, it
|
|
431
|
+
// draws whatever it last had: the down card says the rest
|
|
370
432
|
const p = app.projects
|
|
371
|
-
|
|
433
|
+
const nothing = m.data == null && m.pm == null && m.widgets == null
|
|
434
|
+
const first = m.data == null || (m.pm == null && m.pmDown === '') || (p.list.length === 0 && !p.fresh && p.error === '')
|
|
435
|
+
if (nothing || (m.down === '' && first)) return <div className="hwait"><p className="twait"><span className="spin" />loading…</p></div>
|
|
372
436
|
return (
|
|
373
437
|
<div className="hall">
|
|
374
438
|
<div className="hstack">
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
<link rel="stylesheet" href="./prose-hits.css" />
|
|
10
10
|
<link rel="stylesheet" href="./home-strip.css" />
|
|
11
11
|
<link rel="stylesheet" href="./planned.css" />
|
|
12
|
+
<link rel="stylesheet" href="./when.css" />
|
|
12
13
|
<link rel="stylesheet" href="./activity.css" />
|
|
13
14
|
<link rel="stylesheet" href="./readme.css" />
|
|
14
15
|
<link rel="stylesheet" href="./dock.css" />
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
// app for every page the home serves ('/', '/planned', '/activity', '/r/<repo>/') and for a
|
|
3
3
|
// hub serving on its own at '/'. mobx+react per build/app-state-tree: HomeSt
|
|
4
4
|
// is the single root (window.app), each repo a RepoSt under it, services own
|
|
5
|
-
// state, components read and call. A repo's page is
|
|
6
|
-
//
|
|
7
|
-
// state in the label itself,
|
|
5
|
+
// state, components read and call. A repo's page is ONE tree whose top rows
|
|
6
|
+
// are its sections and whose rows under them are every repo resource
|
|
7
|
+
// (sidebar.tsx), coloured by state in the label itself, under one filter
|
|
8
|
+
// input and one severity filter persisted at
|
|
8
9
|
// ?f=needs · ?f=red. A row opens its item view, an asset row opens the atelier.
|
|
9
10
|
import { runInAction } from 'mobx'
|
|
10
11
|
import { observer } from 'mobx-react-lite'
|
|
@@ -24,6 +25,10 @@ import { TestCard } from './test-view.tsx'
|
|
|
24
25
|
import { TestsPage } from './tests-view.tsx'
|
|
25
26
|
import { AssetStage, CatGrid, InfoChips, MediaTopbar, ToolsBar, VariantsBar } from './media-view.tsx'
|
|
26
27
|
import { Sidebar } from './sidebar.tsx'
|
|
28
|
+
import { ProcessesPage } from './processes-view.tsx'
|
|
29
|
+
import { PlanPage } from './plan-view.tsx'
|
|
30
|
+
import { WhenPage } from './when-view.tsx'
|
|
31
|
+
import { GitPage } from './git-view.tsx'
|
|
27
32
|
import { AdminPage } from './admin-view.tsx'
|
|
28
33
|
import { ReadmeView } from './readme-view.tsx'
|
|
29
34
|
import { ProjectList } from './project-list.tsx'
|
|
@@ -35,7 +40,7 @@ import { ActivityView } from './activity-view.tsx'
|
|
|
35
40
|
import { Palette } from './palette-view.tsx'
|
|
36
41
|
import { SCREEN_WAIT_MS } from './screen-cache.ts'
|
|
37
42
|
import { isSearchKey, pageKey, sectionKey } from './keys.ts'
|
|
38
|
-
import { repoSections } from './repo-sections.
|
|
43
|
+
import { repoSections } from './repo-sections.ts'
|
|
39
44
|
import { Dock } from './dock-view.tsx'
|
|
40
45
|
import { DOCK } from './dock-st.ts'
|
|
41
46
|
import { animateDock } from './dock-anim.ts'
|
|
@@ -93,6 +98,11 @@ const Main = observer(({ app }: { app: RepoSt }) => {
|
|
|
93
98
|
)
|
|
94
99
|
if (app.sel?.t === 'tests') return <main>{errs}<div className="stagewrap"><TestsPage app={app} check={app.sel.check} /></div></main>
|
|
95
100
|
if (app.sel?.t === 'admin') return <main>{errs}<div className="stagewrap"><AdminPage app={app} /></div></main>
|
|
101
|
+
if (app.sel?.t === 'plan') return <main>{errs}<div className="stagewrap"><PlanPage app={app} /></div></main>
|
|
102
|
+
if (app.sel?.t === 'git') return <main>{errs}<div className="stagewrap"><GitPage app={app} /></div></main>
|
|
103
|
+
if (app.sel?.t === 'checks') return <main>{errs}<CheckTable app={app} id="checks" from={app.sel.from} /></main>
|
|
104
|
+
if (app.sel?.t === 'when') return <main>{errs}<div className="stagewrap"><WhenPage app={app} /></div></main>
|
|
105
|
+
if (app.sel?.t === 'processes') return <main>{errs}<div className="stagewrap"><ProcessesPage app={app} id={app.sel.id} /></div></main>
|
|
96
106
|
if (app.sel?.t === 'status') return <main>{errs}<div className="stagewrap"><StatusCard app={app} /></div></main>
|
|
97
107
|
if (app.sel?.t === 'cat') return <main>{errs}<CatGrid app={app} /></main>
|
|
98
108
|
if (app.sel?.t === 'asset') return (
|
|
@@ -73,9 +73,10 @@ export type RecentRun = { at: number; ms: number; status: CheckStatus; replayed:
|
|
|
73
73
|
// engine reaches the bundle: two declarations of one vocabulary is how a
|
|
74
74
|
// switch ends up writing a word the other half does not know
|
|
75
75
|
import type { WhenAt, WhenSource } from '../../core/when.ts'
|
|
76
|
+
import type { RunsCells } from '../../core/runs-matrix.ts'
|
|
76
77
|
import type { Line } from '../../core/wire.ts'
|
|
77
78
|
import type { CheckFrom } from '../../check.ts'
|
|
78
|
-
export type { CheckFrom, WhenAt, WhenSource }
|
|
79
|
+
export type { CheckFrom, WhenAt, WhenSource, RunsCells }
|
|
79
80
|
|
|
80
81
|
/** a gate check's declaration and its numbers (collections/collection-types.ts
|
|
81
82
|
* CheckFacts is the source; this is the wire mirror the page reads) */
|
|
@@ -87,6 +88,8 @@ export type CheckFacts = {
|
|
|
87
88
|
/** the wire lines of its last run, rendered as inline markdown */
|
|
88
89
|
lastLines: string[]
|
|
89
90
|
gate: boolean
|
|
91
|
+
/** every context it runs in and how it caches */
|
|
92
|
+
runs: RunsCells
|
|
90
93
|
/** a build step: the gate runs it, the health pass does not */
|
|
91
94
|
build: boolean
|
|
92
95
|
blocking: boolean | null
|
|
@@ -138,7 +141,7 @@ export type GateBlock = {
|
|
|
138
141
|
path: string
|
|
139
142
|
hash: string
|
|
140
143
|
state: 'missing-file' | 'missing-block' | 'stale' | 'current'
|
|
141
|
-
|
|
144
|
+
off: Record<string, string>
|
|
142
145
|
tests: { budgetMs?: number; slow?: SlowSuite[]; serial?: { file: string; why: string }[] }
|
|
143
146
|
declaredOutside: boolean
|
|
144
147
|
dirty: boolean
|
|
@@ -38,7 +38,6 @@ a,a:visited{color:var(--accent)}
|
|
|
38
38
|
.pjpanel,.appcol,.pside,.pmain{height:100%;min-width:0}
|
|
39
39
|
.apptop,.pdock{height:100%;min-height:0}
|
|
40
40
|
.pjpanel{display:flex}
|
|
41
|
-
/* the box the machine toasts float in (home-strip.css .htoasts) */
|
|
42
41
|
.appcol{position:relative;display:flex;flex-direction:column}
|
|
43
42
|
/* one region per repo visited, the shown one alone displayed */
|
|
44
43
|
.repo{flex:1;min-height:0;display:flex;flex-direction:column}
|
|
@@ -50,10 +49,9 @@ a,a:visited{color:var(--accent)}
|
|
|
50
49
|
.pmain > main{flex:1}
|
|
51
50
|
|
|
52
51
|
/* ── a repo's bar: on the home the strip (home-strip.css .hstrip), on a hub
|
|
53
|
-
of its own the header (.hdr). Its name,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
run state to its spinner ── */
|
|
52
|
+
of its own the header (.hdr). Its name, its check… button and a spinner
|
|
53
|
+
while a run goes, then its processes and its editor icons, on one line.
|
|
54
|
+
Its trouble floats under it (home-strip.css .htoasts) ── */
|
|
57
55
|
/* the repo's processes (serve.processes): a dot, its id, a link once up */
|
|
58
56
|
.hprocs{display:inline-flex;gap:var(--s1);align-items:center}
|
|
59
57
|
.hproc{display:inline-flex;align-items:center;gap:var(--s0)}
|
|
@@ -64,7 +62,7 @@ a,a:visited{color:var(--accent)}
|
|
|
64
62
|
.hdr{display:flex;gap:var(--s2);align-items:center;height:36px;flex:none;padding:0 var(--s3);border-bottom:1px solid var(--line);background:var(--bg-top)}
|
|
65
63
|
.hdr .sp{flex:1}
|
|
66
64
|
.hname{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink);font-weight:600;margin-right:var(--s2);white-space:nowrap}
|
|
67
|
-
.hdr,.hstrip{
|
|
65
|
+
.hdr,.hstrip{white-space:nowrap}
|
|
68
66
|
.hrun{display:inline-flex;align-items:center;gap:var(--s1);flex:none;margin-left:var(--s2)}
|
|
69
67
|
.hbtn{display:inline-flex;align-items:center;gap:var(--s1);height:22px;padding:0 var(--s2);font-size:11.5px;white-space:nowrap;flex:none}
|
|
70
68
|
.hbtn .hico{width:13px;height:13px}
|
|
@@ -72,40 +70,34 @@ a,a:visited{color:var(--accent)}
|
|
|
72
70
|
.hicon{display:inline-flex;align-items:center;justify-content:center;flex:none;width:24px;height:22px;padding:0;border:0;background:none;color:var(--dim)}
|
|
73
71
|
.hicon:hover{color:var(--accent)}
|
|
74
72
|
.hicon .hico{width:15px;height:15px}
|
|
75
|
-
/* the run state holds its width whatever it says, so a run never moves the buttons beside it */
|
|
76
|
-
.hstatus{max-width:420px;flex:0 1 auto;font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
77
|
-
.hstatus.red{color:var(--red)}
|
|
78
|
-
.hstatus.warn{color:var(--warn)}
|
|
79
|
-
/* its words sit against the run buttons, cut with an ellipsis */
|
|
80
|
-
.hstate{display:inline-flex;gap:var(--s1);align-items:center;width:200px;min-width:200px;flex:none;justify-content:flex-end;font-size:11.5px;color:var(--dim);overflow:hidden}
|
|
81
|
-
.hstate.on{color:var(--ink)}
|
|
82
|
-
.hstate .htext{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
83
|
-
.hstate .spin.idle{visibility:hidden;animation:none}
|
|
84
|
-
/* after .hstate, so a narrow bar wins over its width */
|
|
85
|
-
@container (max-width:600px){.hbtn .lbl{display:none}.hbtn{padding:0 var(--s1)}}
|
|
86
|
-
@container (max-width:520px){.hstate{width:120px;min-width:120px}}
|
|
87
|
-
@container (max-width:420px){.hstate .htext{display:none}.hstate{width:16px;min-width:16px}}
|
|
88
73
|
|
|
89
74
|
/* a row of a (?) legend: one glyph or word, then its meaning */
|
|
90
75
|
.lrow{display:flex;gap:var(--s2);align-items:center;height:29px;flex:none;padding:0 var(--s3)}
|
|
91
76
|
|
|
92
|
-
/* ── the left pane: the
|
|
93
|
-
|
|
77
|
+
/* ── the left pane: the filter, then the tree, the one part that
|
|
78
|
+
scrolls ── */
|
|
94
79
|
aside{border-right:1px solid var(--line);background:var(--panel);min-height:0;display:flex;flex-direction:column;overflow:hidden}
|
|
95
80
|
aside > *{flex:none}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
.
|
|
100
|
-
.
|
|
101
|
-
.
|
|
102
|
-
|
|
103
|
-
.
|
|
104
|
-
|
|
105
|
-
.
|
|
81
|
+
/* the tree is long, so it keeps a scroll indicator, a thin one with no track:
|
|
82
|
+
WebKit (the macOS app) ignores scrollbar-width and draws a wide dark band */
|
|
83
|
+
aside > .tscroll{flex:1;min-height:0;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--line) transparent}
|
|
84
|
+
aside > .tscroll::-webkit-scrollbar{width:6px}
|
|
85
|
+
aside > .tscroll::-webkit-scrollbar-track{background:transparent}
|
|
86
|
+
aside > .tscroll::-webkit-scrollbar-thumb{background:var(--line);border-radius:3px}
|
|
87
|
+
/* a section, the tree's top row: the project list's lines (project-list.css
|
|
88
|
+
.pjpl), a little shorter, the current one lit and marked in the accent.
|
|
89
|
+
The chevron column is the tree's own, so a section's rows line up under it */
|
|
90
|
+
.tsec{display:grid;grid-template-columns:14px 16px minmax(0,1fr) auto auto;align-items:center;gap:var(--s2);height:26px;padding:0 var(--s1) 0 var(--s2);color:var(--dim);font-size:13px;cursor:pointer;white-space:nowrap}
|
|
91
|
+
.tsec:hover{background:var(--panel2);color:var(--ink)}
|
|
92
|
+
.tsec.on{box-shadow:inset 2px 0 0 var(--accent)}
|
|
93
|
+
.tsec.cur{background:var(--sel);color:var(--ink);box-shadow:inset 3px 0 0 var(--accent);font-weight:600}
|
|
94
|
+
.tsec .hico{width:16px;height:16px}
|
|
95
|
+
.tsec:hover .tw{color:var(--ink)}
|
|
96
|
+
.rsecl{min-width:0;overflow:hidden;text-overflow:ellipsis}
|
|
97
|
+
.rsecn{min-width:16px;height:16px;padding:0 var(--s1);border-radius:8px;font-size:10px;font-weight:700;line-height:16px;text-align:center;color:var(--bg)}
|
|
106
98
|
.rsecn.nred{background:var(--red)}
|
|
107
99
|
.rsecn.nwarn{background:var(--warn)}
|
|
108
|
-
.
|
|
100
|
+
.tsec:hover .pjkey,.tsec.cur .pjkey{opacity:1}
|
|
109
101
|
/* segmented severity filter — all · needs · red. A thin border and an
|
|
110
102
|
underline on the live one: no fill, no pill */
|
|
111
103
|
.seg{display:inline-flex;border:1px solid var(--line);border-radius:3px;overflow:hidden;flex:none}
|
|
@@ -115,9 +107,8 @@ aside > .tscroll{flex:1;min-height:0;overflow-y:auto}
|
|
|
115
107
|
.seg button:hover{color:var(--ink)}
|
|
116
108
|
.seg button.on{color:var(--accent);border-bottom-color:var(--accent)}
|
|
117
109
|
.seg button .sk{margin-left:var(--s1);font-size:9px;opacity:.55}
|
|
118
|
-
.swhen{padding:0 var(--s3) var(--s2);font-size:11px;color:var(--dim)}
|
|
119
110
|
/* a narrow pane puts the severity filter under the input, never over it */
|
|
120
|
-
.ssearch{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2);padding:var(--s2) var(--s3)}
|
|
111
|
+
.ssearch{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2);padding:var(--s2) var(--s3);border-bottom:1px solid var(--line)}
|
|
121
112
|
.sfield{position:relative;flex:1 1 96px;min-width:0;display:flex}
|
|
122
113
|
.ssearch input{width:100%;background:var(--bg);border:1px solid var(--line);border-radius:4px;color:var(--ink);
|
|
123
114
|
padding:var(--s0) var(--s4) var(--s0) var(--s2);font-size:12px;outline:none}
|
|
@@ -133,8 +124,8 @@ aside > .tscroll{flex:1;min-height:0;overflow-y:auto}
|
|
|
133
124
|
no prose, no dash separators ── */
|
|
134
125
|
.tree{padding:var(--s1) 0 var(--s4);outline:none}
|
|
135
126
|
.tree:focus-visible{box-shadow:inset 2px 0 0 var(--accent)}
|
|
136
|
-
.trow{display:grid;grid-template-columns:calc(var(--d,0)*
|
|
137
|
-
align-items:center;gap:var(--s1);padding:0 var(--s2);height:20px;line-height:20px;cursor:pointer;white-space:nowrap}
|
|
127
|
+
.trow{display:grid;grid-template-columns:calc(var(--d,0)*10px + 14px) 14px minmax(0,1fr) 12px auto 14px;
|
|
128
|
+
align-items:center;gap:var(--s1);padding:0 var(--s1) 0 var(--s2);height:20px;line-height:20px;cursor:pointer;white-space:nowrap}
|
|
138
129
|
.trow:hover{background:var(--panel2)}
|
|
139
130
|
.trow.on{background:var(--sel);box-shadow:inset 2px 0 0 var(--accent)}
|
|
140
131
|
/* the fold marker: an 11px chevron with the whole first column as its hit
|
|
@@ -168,21 +159,20 @@ aside > .tscroll{flex:1;min-height:0;overflow-y:auto}
|
|
|
168
159
|
.tg.ok{color:var(--ok)}
|
|
169
160
|
.tg.adv{color:var(--warn);opacity:.75}
|
|
170
161
|
.tg.run{color:var(--accent);animation:pulse 1.1s ease-in-out infinite}
|
|
171
|
-
/*
|
|
172
|
-
|
|
173
|
-
.tdur{
|
|
162
|
+
/* the duration: a clock tinted by the tone table (hub-app/dur.ts), the
|
|
163
|
+
figure in its tooltip, so the label keeps the width */
|
|
164
|
+
.tdur{display:flex;align-items:center;justify-content:center;color:var(--dim)}
|
|
165
|
+
.tdur .hico{width:11px;height:11px;color:inherit}
|
|
174
166
|
.tdur.fast,.dnum.fast{color:var(--ok)}
|
|
175
167
|
.tdur.plain,.dnum.plain{color:var(--ink)}
|
|
176
168
|
.tdur.slow,.dnum.slow{color:var(--warn)}
|
|
177
169
|
.tdur.bad,.dnum.bad{color:var(--red)}
|
|
178
170
|
.tnote{font-size:10px;text-align:right;color:var(--dim);overflow:hidden;text-overflow:ellipsis}
|
|
179
171
|
.tnote b{font-weight:600;color:var(--red)}
|
|
180
|
-
.stabs{display:flex;gap:var(--s2);align-items:center;padding:var(--s1) var(--s3);border-bottom:1px solid var(--line)}
|
|
181
|
-
.stabs .sp{flex:1}
|
|
182
|
-
.stabs .cnt{min-width:4ch;text-align:right;font-size:11px;color:var(--dim);font-variant-numeric:tabular-nums}
|
|
183
172
|
.catgen{display:inline-flex;gap:var(--s1)}
|
|
184
173
|
.catgen button{padding:var(--s0) var(--s2);font-size:11px;font-weight:700}
|
|
185
174
|
.tempty{padding:var(--s2) var(--s3);font-size:12px;color:var(--dim);white-space:pre-wrap}
|
|
175
|
+
.thidden{cursor:pointer}.thidden:hover{color:var(--ink)}
|
|
186
176
|
.twait{display:flex;align-items:center;gap:var(--s2)}
|
|
187
177
|
.warnline{color:var(--red)}
|
|
188
178
|
.fold{cursor:pointer;color:var(--dim);font-size:15px;display:inline-block;width:22px;text-align:center;
|
|
@@ -369,7 +359,15 @@ kbd{background:var(--panel2);border:1px solid var(--line);border-radius:4px;padd
|
|
|
369
359
|
.assetrem .rem{color:var(--ok);border-left:2px solid var(--ok);padding-left:var(--s2);font-size:12.5px;margin:0 0 var(--s2);white-space:pre-wrap}
|
|
370
360
|
|
|
371
361
|
/* ── collection item card + status card ── */
|
|
372
|
-
|
|
362
|
+
/* a repo page sits on the page background, never in a box of its own: its
|
|
363
|
+
header and its sentence are unboxed, and each group it holds (the facts,
|
|
364
|
+
a titled section, the command, the admin rows, the runs list) is one
|
|
365
|
+
lighter box, so a box always means "these belong together" */
|
|
366
|
+
.colcard{max-width:760px;width:100%;padding:var(--s2) 0;text-align:left}
|
|
367
|
+
.colcard>.cfacts,.colcard>.csec,.colcard>.ccmd,.colcard>.md:not(.usays),.colcard>.adbox,.tlcard>.rlist{background:var(--panel);border:1px solid var(--line);border-radius:4px;padding:var(--s2) var(--s3)}
|
|
368
|
+
.colcard>.cfacts,.colcard>.ccmd,.colcard>.md:not(.usays),.colcard>.adbox{margin-top:var(--s3)}
|
|
369
|
+
.ccmd>.cf:first-child,.cfacts>.cf:nth-child(-n+2){border-top:0}
|
|
370
|
+
@media (max-width:900px){.cfacts>.cf:nth-child(2){border-top:1px solid var(--line)}}
|
|
373
371
|
.colcard h3{margin:0 0 var(--s2);font-size:15px;display:flex;gap:var(--s2);align-items:baseline}
|
|
374
372
|
.colcard h3 .sp{flex:1}
|
|
375
373
|
.colcard .truth{margin-left:auto;font:11px ui-monospace,Menlo,monospace;color:var(--dim)}
|
|
@@ -560,6 +558,15 @@ a.tok{color:var(--accent)}
|
|
|
560
558
|
.checkcard .dim{color:var(--dim)}
|
|
561
559
|
/* what is not a fact: the output and the slow suites, titled, unboxed */
|
|
562
560
|
.csec{margin-top:var(--s4)}
|
|
561
|
+
/* where it runs: one line per context, the value then its meaning. A context
|
|
562
|
+
that never runs the check stays dim, so the lit lines read down the list */
|
|
563
|
+
.cruns{display:grid;grid-template-columns:auto auto minmax(0,1fr);column-gap:var(--s3);row-gap:var(--s0);align-items:baseline}
|
|
564
|
+
.crun{display:contents;color:var(--dim)}
|
|
565
|
+
.crv{font:11px ui-monospace,Menlo,monospace;white-space:nowrap}
|
|
566
|
+
.crun.on .crv{color:var(--ok)}
|
|
567
|
+
.crun.cache .crv{color:var(--ink)}
|
|
568
|
+
.crm{font-size:11.5px;color:var(--dim);opacity:.8}
|
|
569
|
+
.crun:not(.on) .crm{opacity:.5}
|
|
563
570
|
.cstitle{display:flex;gap:var(--s1);align-items:center;font-size:11px;text-transform:uppercase;letter-spacing:.08em;
|
|
564
571
|
color:var(--dim);margin-bottom:var(--s2)}
|
|
565
572
|
.wline{margin-bottom:var(--s1);word-break:break-word}
|
|
@@ -628,6 +635,7 @@ a.tok{color:var(--accent)}
|
|
|
628
635
|
.colgrid{flex:1;min-height:0;overflow-y:auto;padding:var(--s3) var(--s4)}
|
|
629
636
|
.cghead{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--accent);
|
|
630
637
|
display:flex;gap:var(--s2);align-items:baseline;margin:0 0 var(--s3)}
|
|
638
|
+
.ctlead{font-size:11px;color:var(--dim);margin:calc(-1 * var(--s1)) 0 var(--s2)}
|
|
631
639
|
.cghead .cnt{font-size:11px;color:var(--dim);letter-spacing:0;text-transform:none}
|
|
632
640
|
/* the checks and asks pages: one dense table each (col-tables-view.tsx), on
|
|
633
641
|
the activity table (.actable), a group header per state that folds */
|
|
@@ -745,9 +753,23 @@ pre{background:var(--panel);border:1px solid var(--line);border-radius:3px;paddi
|
|
|
745
753
|
|
|
746
754
|
/* ── the admin page: one row per control, its sentence under its name ── */
|
|
747
755
|
.adrow{display:flex;align-items:center;gap:var(--s4);padding:var(--s3) 0;border-top:1px solid var(--line)}
|
|
756
|
+
.adrow:first-child{border-top:0}
|
|
748
757
|
.adtext{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--s0);font-size:13px}
|
|
749
758
|
.adtext .dim{font-size:12px}
|
|
750
759
|
.adctl{flex:none;display:flex;align-items:center}
|
|
751
760
|
.adflag{display:inline-flex;align-items:center;gap:var(--s1);font-size:12px;color:var(--dim)}
|
|
752
761
|
.adflag input{width:auto;margin:0}
|
|
753
762
|
.adconfirm{display:inline-flex;align-items:center;gap:var(--s2);font-size:12px}
|
|
763
|
+
/* a repo's plan page: one line per planned check */
|
|
764
|
+
.plntable{width:100%;border-collapse:collapse;font-size:12.5px}
|
|
765
|
+
.plntable td{padding:var(--s1) var(--s2);border-top:1px solid var(--line);vertical-align:baseline}
|
|
766
|
+
.plntable tr:first-child td{border-top:0}
|
|
767
|
+
.plnwhen{white-space:nowrap;font-variant-numeric:tabular-nums}
|
|
768
|
+
.plnrun{text-align:right;white-space:nowrap}
|
|
769
|
+
/* a repo's git page */
|
|
770
|
+
.gitfiles,.gitcommits{width:100%;border-collapse:collapse;font-size:12.5px}
|
|
771
|
+
.gitfiles td,.gitcommits td{padding:2px var(--s2);vertical-align:baseline}
|
|
772
|
+
.gitkind{width:1%;white-space:nowrap;color:var(--dim)}
|
|
773
|
+
.gitkind.staged{color:var(--ok)}
|
|
774
|
+
.gitkind.untracked{color:var(--warn)}
|
|
775
|
+
.gitpath{font:12px ui-monospace,Menlo,monospace;overflow-wrap:anywhere}
|
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
export const ICON = {
|
|
6
6
|
shipkit: 'M3 7.5 10 4l7 3.5v5L10 16l-7-3.5zM3 7.5 10 11l7-3.5M10 11v5',
|
|
7
7
|
process: 'M2 10h3.5l2-5 5 10 2-5H18',
|
|
8
|
+
processes: 'M2 10h3.5l2-5 5 10 2-5H18',
|
|
9
|
+
when: 'M10 3a7 7 0 1 0 .01 0M10 6v4l2.5 2',
|
|
10
|
+
plan: 'M5.5 3h9M5.5 17h9M6.5 3c0 4 7 4 7 7s-7 3-7 7M13.5 3c0 4-7 4-7 7s7 3 7 7',
|
|
11
|
+
git: 'M6 5v10M6 3a2 2 0 1 0 0 .01M6 17a2 2 0 1 0 0 .01M14 5a2 2 0 1 0 0 .01M14 7c0 4.5-8 3.5-8 7',
|
|
12
|
+
reflections: 'M4 3.5h12v9H9l-4 3.5v-3.5H4zM7.5 7h5M7.5 9.5h3',
|
|
8
13
|
monitor: 'M3 4h14v9H3zM7 16.5h6M10 13v3.5',
|
|
9
14
|
archived: 'M2.5 4h15v3.5h-15zM4 7.5V16h12V7.5M8 10.5h4',
|
|
10
15
|
home: 'M3 9.5 10 3.5l7 6M5 8v8.5h10V8M8.5 16.5v-4.5h3v4.5',
|
|
@@ -15,7 +20,11 @@ export const ICON = {
|
|
|
15
20
|
fork: 'M6 5v10M6 3a2 2 0 1 0 0 .01M6 17a2 2 0 1 0 0 .01M14 5a2 2 0 1 0 0 .01M14 7c0 4.5-8 3.5-8 7',
|
|
16
21
|
search: 'M8.5 3a5.5 5.5 0 1 0 0 11a5.5 5.5 0 1 0 0-11M12.5 12.5 17 17',
|
|
17
22
|
// a repo page's sections, then its two runs and its editor
|
|
23
|
+
readme: 'M5 2.5h7l3 3v12H5zM12 2.5v3h3M7.5 9h5M7.5 12h5M7.5 15h3',
|
|
18
24
|
checks: 'M4 10.5 7.5 14 16 5.5',
|
|
25
|
+
contracts: 'M5 2.5h10v15H5zM7.5 6h5M7.5 9h5M7.5 12h2.5',
|
|
26
|
+
repo: 'M2.5 5h5l1.5 1.5h8.5v9.5h-15z',
|
|
27
|
+
secrets: 'M7 7a3 3 0 1 0 0 6a3 3 0 1 0 0-6M10 10h7.5M15 10v2.5M17.5 10v2',
|
|
19
28
|
runs: 'M3 5h8M3 10h12M3 15h6',
|
|
20
29
|
asks: 'M3 4h14v9H8l-4 3.5V13H3z',
|
|
21
30
|
tests: 'M8 3h4M8.5 3v5L4 16.5h12L11.5 8V3M6.2 12.5h7.6',
|
|
@@ -30,6 +39,8 @@ export const ICON = {
|
|
|
30
39
|
// a process's own switches on the overview
|
|
31
40
|
play: 'M6.5 4.5v11l9-5.5z',
|
|
32
41
|
stop: 'M5.5 5.5h9v9h-9z',
|
|
42
|
+
link: 'M8.5 11.5a3.5 3.5 0 0 0 5 0l2.5-2.5a3.5 3.5 0 0 0-5-5l-1 1M11.5 8.5a3.5 3.5 0 0 0-5 0L4 11a3.5 3.5 0 0 0 5 5l1-1',
|
|
43
|
+
clock: 'M10 2.5a7.5 7.5 0 1 0 0 15a7.5 7.5 0 1 0 0-15M10 6v4l2.5 2',
|
|
33
44
|
restart: 'M15.5 10a5.5 5.5 0 1 1-1.6-3.9M14.2 3v3.3h-3.3',
|
|
34
45
|
} as const
|
|
35
46
|
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
<link rel="stylesheet" href="./prose-hits.css" />
|
|
10
10
|
<link rel="stylesheet" href="./home-strip.css" />
|
|
11
11
|
<link rel="stylesheet" href="./planned.css" />
|
|
12
|
+
<link rel="stylesheet" href="./when.css" />
|
|
12
13
|
<link rel="stylesheet" href="./activity.css" />
|
|
13
14
|
<link rel="stylesheet" href="./readme.css" />
|
|
14
15
|
<link rel="stylesheet" href="./dock.css" />
|
|
@@ -46,8 +46,11 @@ export function isSearchKey(e: Pick<KeyLike, 'key' | 'metaKey' | 'ctrlKey' | 'al
|
|
|
46
46
|
* plus a letter only types a character inside a text field, where the page
|
|
47
47
|
* leaves the key alone. Read from the physical key (`code`): on a Mac ⌥C
|
|
48
48
|
* types ç, and AZERTY moves the letters */
|
|
49
|
-
export type SectionId = '
|
|
50
|
-
export const SECTION_LETTER: Record<SectionId, string> = {
|
|
49
|
+
export type SectionId = 'readme' | 'contracts' | 'shipkit' | 'repo' | 'secrets' | 'when' | 'runs' | 'plan' | 'processes' | 'git' | 'asks' | 'reflections' | 'tests' | 'media' | 'wikis' | 'admin'
|
|
50
|
+
export const SECTION_LETTER: Record<SectionId, string> = { readme: 'E', contracts: 'K', shipkit: 'S', repo: 'C', secrets: 'X', when: 'O', runs: 'R', plan: 'N', processes: 'P', git: 'G', asks: 'A', reflections: 'F', tests: 'T', media: 'M', wikis: 'W', admin: 'D' }
|
|
51
|
+
/** a section's words on its row, where the id alone would not say it */
|
|
52
|
+
export const SECTION_LABEL: Partial<Record<SectionId, string>> = { shipkit: 'shipkit checks', repo: 'repo checks' }
|
|
53
|
+
export const sectionLabel = (id: SectionId): string => SECTION_LABEL[id] ?? id
|
|
51
54
|
/** the badge at the end of a section's line */
|
|
52
55
|
export const sectionKeyLabel = (id: SectionId): string => `⌥${SECTION_LETTER[id]}`
|
|
53
56
|
|
|
@@ -11,6 +11,7 @@ import type { TimelineInvocation } from '../../ledger/run-timeline.ts'
|
|
|
11
11
|
import { ChartView, type ChartSource } from './chart-view.ts'
|
|
12
12
|
import type { RepoSt } from './repo-st.ts'
|
|
13
13
|
import { chartHeight } from './timeline-geometry.ts'
|
|
14
|
+
import { ownTrouble, reach } from './reach.ts'
|
|
14
15
|
|
|
15
16
|
/** how often the page asks. A strip a second behind the runner is still live */
|
|
16
17
|
export const LIVE_POLL_MS = 1_000
|
|
@@ -107,7 +108,7 @@ export class LiveSvc {
|
|
|
107
108
|
if (this.inFlight) return
|
|
108
109
|
this.inFlight = true
|
|
109
110
|
try {
|
|
110
|
-
const r = await
|
|
111
|
+
const r = await reach(this.st.url('/api/live.json'))
|
|
111
112
|
if (!r.ok) throw new Error(await r.text())
|
|
112
113
|
const v = (await r.json()) as LiveView
|
|
113
114
|
const ended = this.running && v.runs.length === 0
|
|
@@ -125,7 +126,7 @@ export class LiveSvc {
|
|
|
125
126
|
if (this.st.timeline.data != null) void this.st.timeline.load(true)
|
|
126
127
|
}
|
|
127
128
|
} catch (e) {
|
|
128
|
-
runInAction(() => { this.error = `the live runs could not be read: ${
|
|
129
|
+
runInAction(() => { this.error = ownTrouble(e, this.st.home.machine, (m) => `the live runs could not be read: ${m}`) })
|
|
129
130
|
} finally {
|
|
130
131
|
this.inFlight = false
|
|
131
132
|
}
|