rvlib-shipkit 1.4.0 → 1.6.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 +114 -0
- package/README.md +17 -17
- package/package.json +3 -2
- package/src/assets/gen/comfyts-gen.ts +12 -8
- package/src/assets/server/studio-server.ts +14 -2
- package/src/check.ts +73 -103
- package/src/checks/SK001-package-json-shape.check.ts +11 -6
- package/src/checks/SK002-contracts-fulfillness.check.ts +4 -0
- package/src/checks/SK003-claude-md-shape.check.ts +7 -3
- 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/checks/SK030-cache-audit.check.ts +34 -0
- package/src/checks/SK031-trace-audit.check.ts +39 -0
- package/src/checks/SK032-ignored-inputs.check.ts +79 -0
- package/src/cli/cmd_Asks.ts +5 -5
- package/src/cli/cmd_Assets.ts +17 -13
- package/src/cli/cmd_Bump.ts +12 -11
- package/src/cli/cmd_CacheAudit.ts +43 -0
- 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 +8 -6
- package/src/cli/cmd_Fleet.ts +9 -7
- package/src/cli/cmd_GlobalProcesses.ts +120 -7
- package/src/cli/cmd_Home.ts +26 -19
- package/src/cli/cmd_Hooks.ts +6 -5
- package/src/cli/cmd_Init.ts +5 -4
- package/src/cli/cmd_Keys.ts +20 -8
- package/src/cli/cmd_Mv.ts +7 -6
- package/src/cli/cmd_Needs.ts +4 -3
- package/src/cli/cmd_Overview.ts +5 -4
- 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 +6 -4
- 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_TraceAudit.ts +62 -0
- 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 +11 -2
- package/src/collections/checks.ts +2 -0
- package/src/collections/collection-types.ts +12 -4
- package/src/collections/providers.ts +5 -3
- package/src/core/app-icon.ts +154 -0
- package/src/core/audit-report.ts +21 -0
- package/src/core/cache-audit.ts +72 -0
- package/src/core/canonical-scripts.ts +2 -2
- package/src/core/check-ctx.ts +14 -5
- package/src/core/check-runner.ts +43 -23
- package/src/core/config-rewrite.ts +37 -24
- package/src/core/discover-checks.ts +23 -2
- package/src/core/floor-resolve.ts +5 -7
- package/src/core/git-root.ts +13 -0
- package/src/core/ignored-block.ts +94 -0
- package/src/core/ignored-inputs.ts +47 -0
- package/src/core/jobs.ts +9 -2
- package/src/core/legacy-shapes.ts +39 -10
- package/src/core/one-lib.ts +59 -4
- 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/read-set.ts +97 -9
- package/src/core/retention.ts +0 -14
- package/src/core/run-check.ts +12 -9
- package/src/core/run-ci.ts +14 -5
- package/src/core/run-fix.ts +30 -23
- package/src/core/run-fixers.ts +48 -5
- package/src/core/runs-matrix.ts +179 -0
- package/src/core/runs.ts +242 -0
- package/src/core/trace-audit.ts +120 -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/gui-app.ts +24 -27
- package/src/home/home-launchd.ts +20 -4
- package/src/home/home-routes.ts +12 -1
- package/src/home/home-start.ts +11 -5
- package/src/home/hubs.ts +5 -5
- package/src/home/open-app.ts +2 -2
- 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/home/via-hub.ts +14 -0
- package/src/hooks/run-hooks.ts +48 -4
- package/src/init-repo.ts +6 -6
- package/src/ledger/db.ts +2 -13
- package/src/ledger/gate-checks.ts +133 -61
- package/src/ledger/git-key.ts +153 -31
- package/src/ledger/git-scan.ts +5 -2
- package/src/ledger/ledger.ts +136 -21
- package/src/ledger/store.ts +14 -0
- package/src/ledger/toolchain-identity.ts +248 -45
- package/src/plan/repo-source.ts +18 -39
- package/src/problems/extract.ts +3 -0
- package/src/release/run-release.ts +5 -0
- package/src/repo-config.ts +46 -54
- 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-svc.ts +36 -3
- package/src/serve/hub-app/admin-view.tsx +46 -12
- 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-card.tsx +1 -44
- package/src/serve/hub-app/col-tables-view.tsx +84 -11
- package/src/serve/hub-app/col-tables.ts +27 -0
- 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 -12
- package/src/serve/hub-app/home-strip.tsx +115 -64
- package/src/serve/hub-app/home.html +1 -0
- package/src/serve/hub-app/hub-app.tsx +32 -8
- package/src/serve/hub-app/hub-types.ts +5 -2
- package/src/serve/hub-app/hub.css +89 -63
- 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 +15 -4
- 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 +45 -0
- package/src/serve/hub-app/project-list.css +4 -1
- package/src/serve/hub-app/project-list.tsx +6 -3
- package/src/serve/hub-app/projects-svc.ts +25 -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 +131 -0
- package/src/serve/hub-app/repo-st.ts +70 -14
- package/src/serve/hub-app/sidebar.tsx +17 -42
- package/src/serve/hub-app/timeline-svc.ts +3 -2
- package/src/serve/hub-app/tree-build.ts +151 -155
- package/src/serve/hub-app/tree-row.tsx +55 -12
- package/src/serve/hub-app/tree-svc.ts +97 -56
- 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 +7 -18
- package/src/serve/project-gutter-route.ts +22 -1
- package/src/serve/run-serve.ts +1 -1
- package/src/serve/serve-hub.ts +4 -14
- package/src/suite/judge-suites.ts +6 -0
- package/src/trace/interpose.c +655 -0
- package/src/trace/read-trace.ts +485 -0
- package/src/trace/tracer.ts +282 -0
- 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(' · ')
|
|
@@ -154,9 +157,10 @@ const ProcessRepo = observer(({ app, name, icon }: { app: HomeSt; name: string;
|
|
|
154
157
|
/** the processes as one table in the activity page's style: shipkit's own
|
|
155
158
|
* first, then a row group per declaring repo in the project list's order,
|
|
156
159
|
* its name in the first column, at the top of its group, then one line per
|
|
157
|
-
* process. The url's header carries the new window button
|
|
158
|
-
|
|
159
|
-
|
|
160
|
+
* process. The url's header carries the new window button. `repo` keeps
|
|
161
|
+
* that one repo's group, the table of its processes page */
|
|
162
|
+
export const ProcessTable = observer(({ app, m, repo }: { app: HomeSt; m: MachineSt; repo?: string }) => {
|
|
163
|
+
const groups = repo == null ? m.processGroups : m.processGroups.filter((g) => g.core !== true && g.repo === repo)
|
|
160
164
|
const rows = groups.flatMap((g) => g.rows)
|
|
161
165
|
// wide enough for the longest set of declared actions, in the button font
|
|
162
166
|
const act = Math.max(0, ...rows.map((r) => r.actions.reduce((n, a) => n + a.label.length + 3, 0)))
|
|
@@ -222,19 +226,6 @@ const Monitor = observer(({ m }: { m: MachineSt }) => {
|
|
|
222
226
|
)
|
|
223
227
|
})
|
|
224
228
|
|
|
225
|
-
/** the two colours of a repo, a picker each: the page draws the dark one */
|
|
226
|
-
export const IdentitySwatches = observer(({ app, name }: { app: HomeSt; name: string }) => {
|
|
227
|
-
const id = app.projects.identityOf(name)
|
|
228
|
-
return (
|
|
229
|
-
<span className="hswatch">
|
|
230
|
-
<input type="color" aria-label="colour on a dark background" title={`${name} on a dark background (this page): ${id.colorDark}, written to repo.config.ts identity.colorDark`}
|
|
231
|
-
value={id.colorDark} onChange={(e) => app.projects.setIdentity(name, { colorDark: e.currentTarget.value })} />
|
|
232
|
-
<input type="color" aria-label="colour on a light background" title={`${name} on a light background: ${id.colorLight}, written to repo.config.ts identity.colorLight`}
|
|
233
|
-
value={id.colorLight} onChange={(e) => app.projects.setIdentity(name, { colorLight: e.currentTarget.value })} />
|
|
234
|
-
</span>
|
|
235
|
-
)
|
|
236
|
-
})
|
|
237
|
-
|
|
238
229
|
/** the shown repo in the strip: its icon, its name in its colour */
|
|
239
230
|
const ShownRepo = observer(({ app, name }: { app: HomeSt; name: string }) => {
|
|
240
231
|
const p = app.projects.list.find((x) => x.name === name)
|
|
@@ -244,23 +235,96 @@ const ShownRepo = observer(({ app, name }: { app: HomeSt; name: string }) => {
|
|
|
244
235
|
{/* the icon's box is there before the project list is, so the name never moves */}
|
|
245
236
|
{p?.icon != null ? <img src={p.icon} alt="" width={18} height={18} /> : <i className="hrico" />}
|
|
246
237
|
<b className="hrname" style={{ color: id.colorDark }}>{name}</b>
|
|
247
|
-
<IdentitySwatches app={app} name={name} />
|
|
248
238
|
</span>
|
|
249
239
|
)
|
|
250
240
|
})
|
|
251
241
|
|
|
252
|
-
/**
|
|
253
|
-
*
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
242
|
+
/** the machine not answering, as ONE card: the web (pm behind it is then
|
|
243
|
+
* unknown, not down), the daemon, pm, or the daemon and pm together */
|
|
244
|
+
const downCard = (m: MachineSt): Card[] => {
|
|
245
|
+
const web = m.down !== '' && m.downWho === 'web'
|
|
246
|
+
const pm = m.pmDown !== '' && !web
|
|
247
|
+
if (m.down === '' && !pm) return []
|
|
248
|
+
const head = m.down === '' ? 'pm down' : pm ? 'daemon and pm down' : `${m.downWho} down`
|
|
249
|
+
return [{ key: 'down', tone: 'red', head, body: m.down === '' ? m.pmDown : pm ? <>{m.down}<br />{m.pmDown}</> : m.down }]
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** what is wrong with the machine itself, one card each: the machine not
|
|
253
|
+
* answering, a crashed child of pm, a crashed global process (its switch
|
|
254
|
+
* starts it again), a failed poll. While pm is silent its last answer says
|
|
255
|
+
* nothing here */
|
|
256
|
+
const machineCards = (m: MachineSt): Card[] => {
|
|
257
|
+
const pm = m.pmDown !== '' ? null : m.pm
|
|
258
|
+
return [
|
|
259
|
+
...downCard(m),
|
|
260
|
+
...(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) ?? '' })),
|
|
261
|
+
...(pm?.resources ?? []).filter((r) => r.status === 'crashed').map((r): Card => ({ key: `res:${r.id}`, tone: 'red', head: 'crashed', body: <ResourceChip m={m} r={r} /> })),
|
|
262
|
+
...(m.error !== '' ? [{ key: 'poll', tone: 'red' as const, body: m.error }] : []),
|
|
263
|
+
]
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** a global process's menu: start or stop, restart, its url opened three
|
|
267
|
+
* ways (each starts it first when it is down), then its declared actions and
|
|
268
|
+
* take over. Every entry is a click on purpose, never the chip itself */
|
|
269
|
+
/** `lastLog: false` where the whole log is drawn under it, so its last line is not said twice */
|
|
270
|
+
export const ProcessMenu = observer(({ m, r, close, lastLog = true }: { m: MachineSt; r: ProcessRow; close: () => void; lastLog?: boolean }) => {
|
|
271
|
+
const on = r.status === 'up' || r.status === 'starting'
|
|
272
|
+
const first = r.status === 'up' ? '' : ', starts it first'
|
|
273
|
+
const act = (a: Parameters<MachineSt['processAct']>[1], action?: string) => () => { close(); void m.processAct(r.id, a, action) }
|
|
274
|
+
const open = (how: OpenHow) => () => { close(); void m.openProcess(r.id, how) }
|
|
275
|
+
const log = lastLog ? r.lastLog : null
|
|
276
|
+
const about = r.description != null && r.description !== '' || r.note != null || log != null
|
|
277
|
+
return (
|
|
278
|
+
<div className="hmenu2">
|
|
279
|
+
<div className="hmenu hmacts">
|
|
280
|
+
<div className="hmhead"><i className={`pdot ${r.status}`} /><b>{r.label}</b><span>{r.status}</span></div>
|
|
281
|
+
{on ? (
|
|
282
|
+
<button className="hmitem" disabled={r.status === 'stopping' || !r.canStop} onClick={act('stop')}>
|
|
283
|
+
<Icon d={ICON.stop} /><span><b>stop</b>{r.canStop ? null : <small>started outside pm, so pm cannot stop it</small>}</span>
|
|
284
|
+
</button>
|
|
285
|
+
) : (
|
|
286
|
+
<button className="hmitem" disabled={r.status === 'unknown'} onClick={act('start')}><Icon d={ICON.play} /><span><b>start</b></span></button>
|
|
287
|
+
)}
|
|
288
|
+
{r.canRestart ? <button className="hmitem" onClick={act('restart')}><Icon d={ICON.restart} /><span><b>restart</b></span></button> : null}
|
|
289
|
+
{r.url == null ? null : (
|
|
290
|
+
<>
|
|
291
|
+
<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>
|
|
292
|
+
<button className="hmitem" onClick={open('chrome')}><Icon d={ICON.browser} /><span><b>open in Chrome</b><small>{r.host}{first}</small></span></button>
|
|
293
|
+
<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>
|
|
294
|
+
</>
|
|
295
|
+
)}
|
|
296
|
+
{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>)}
|
|
297
|
+
{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}
|
|
298
|
+
</div>
|
|
299
|
+
{about ? (
|
|
300
|
+
<div className="hmabout">
|
|
301
|
+
{r.description != null && r.description !== '' ? <div className="md" dangerouslySetInnerHTML={{ __html: renderMd(r.description) }} /> : null}
|
|
302
|
+
{r.note != null ? <small className="hmnote">{r.note}</small> : null}
|
|
303
|
+
{log != null ? <code className="hmlog" title="its last log line">{log}</code> : null}
|
|
304
|
+
</div>
|
|
305
|
+
) : null}
|
|
306
|
+
</div>
|
|
307
|
+
)
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
/** the shown repo's global processes, one chip each, right after its name: its
|
|
311
|
+
* dot and label, and a click opens its menu (ProcessMenu). pm runs them, so
|
|
312
|
+
* they show whether the repo's hub answers or not */
|
|
313
|
+
const RepoProcesses = observer(({ m, name }: { m: MachineSt; name: string }) => {
|
|
314
|
+
const rows = m.processGroups.find((g) => g.core !== true && g.repo === name)?.rows ?? []
|
|
315
|
+
if (rows.length === 0) return null
|
|
316
|
+
return (
|
|
317
|
+
<span className="hprocs hgprocs">
|
|
318
|
+
{rows.map((r) => (
|
|
319
|
+
<span key={r.id} className="hproc">
|
|
320
|
+
<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}</>}>
|
|
321
|
+
{(close) => <ProcessMenu m={m} r={r} close={close} />}
|
|
322
|
+
</Pop>
|
|
323
|
+
</span>
|
|
324
|
+
))}
|
|
325
|
+
</span>
|
|
326
|
+
)
|
|
327
|
+
})
|
|
264
328
|
|
|
265
329
|
export const HomeStrip = observer(({ app }: { app: HomeSt }) => {
|
|
266
330
|
const m = app.machine
|
|
@@ -273,33 +337,17 @@ export const HomeStrip = observer(({ app }: { app: HomeSt }) => {
|
|
|
273
337
|
<>
|
|
274
338
|
<div className="hstrip">
|
|
275
339
|
{name == null ? <span className="hplace">{app.page === 'overview' ? 'overview' : app.page}</span> : <ShownRepo app={app} name={name} />}
|
|
276
|
-
{
|
|
340
|
+
{name == null ? null : <RepoProcesses m={m} name={name} />}
|
|
277
341
|
<span className="sp" />
|
|
278
342
|
{live == null ? null : <RepoTools app={live} />}
|
|
279
|
-
<
|
|
343
|
+
{live == null ? null : <RepoRun app={live} />}
|
|
280
344
|
{live == null ? null : <RepoOpeners app={live} />}
|
|
281
345
|
</div>
|
|
282
|
-
<
|
|
346
|
+
<FloatCards cards={[...(live == null ? [] : hubCards(live)), ...machineCards(m)]} />
|
|
283
347
|
</>
|
|
284
348
|
)
|
|
285
349
|
})
|
|
286
350
|
|
|
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
351
|
/** archive the repo or bring it back: `archived` in its repo.config.ts, and
|
|
304
352
|
* only that key. Archived, it stays listed and nothing runs there */
|
|
305
353
|
export const ArchiveButton = observer(({ app, name }: { app: HomeSt; name: string }) => {
|
|
@@ -326,7 +374,7 @@ export const NotUp = observer(({ app }: { app: HomeSt }) => {
|
|
|
326
374
|
<ArchiveButton app={app} name={name} />
|
|
327
375
|
</div>
|
|
328
376
|
)
|
|
329
|
-
if (w == null) return <div className="hwait"><p className="twait"><span className="spin" />{
|
|
377
|
+
if (w == null) return <div className="hwait"><p className="twait"><span className="spin" />{`looking for ${name}…`}</p></div>
|
|
330
378
|
return (
|
|
331
379
|
<div className="hwait">
|
|
332
380
|
{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 +414,12 @@ export const Overview = observer(({ app }: { app: HomeSt }) => {
|
|
|
366
414
|
const m = app.machine
|
|
367
415
|
if (m == null) return null
|
|
368
416
|
// 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
|
|
417
|
+
// rows never arrive or reorder after it paints. The daemon silent, it
|
|
418
|
+
// draws whatever it last had: the down card says the rest
|
|
370
419
|
const p = app.projects
|
|
371
|
-
|
|
420
|
+
const nothing = m.data == null && m.pm == null && m.widgets == null
|
|
421
|
+
const first = m.data == null || (m.pm == null && m.pmDown === '') || (p.list.length === 0 && !p.fresh && p.error === '')
|
|
422
|
+
if (nothing || (m.down === '' && first)) return <div className="hwait"><p className="twait"><span className="spin" />loading…</p></div>
|
|
372
423
|
return (
|
|
373
424
|
<div className="hall">
|
|
374
425
|
<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,16 +2,17 @@
|
|
|
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'
|
|
11
12
|
import { StrictMode, useEffect, useLayoutEffect, useRef } from 'react'
|
|
12
13
|
import { createRoot } from 'react-dom/client'
|
|
13
|
-
import { ColCard,
|
|
14
|
-
import { AskTable, CheckTable } from './col-tables-view.tsx'
|
|
14
|
+
import { ColCard, StatusCard } from './col-card.tsx'
|
|
15
|
+
import { AskTable, CheckTable, ItemTable, SecretTable } from './col-tables-view.tsx'
|
|
15
16
|
import { WIKIS, WikisView } from './wikis-view.tsx'
|
|
16
17
|
import { ComfyOverlay, ComfyView } from './comfy-view.tsx'
|
|
17
18
|
import type { Filter, RepoSt } from './repo-st.ts'
|
|
@@ -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'
|
|
@@ -88,11 +93,16 @@ const Main = observer(({ app }: { app: RepoSt }) => {
|
|
|
88
93
|
if (app.sel?.t === 'col') return (
|
|
89
94
|
<main>
|
|
90
95
|
{errs}
|
|
91
|
-
{app.sel.id === 'checks' ? <CheckTable app={app} id={app.sel.id} /> : app.sel.id === 'asks' ? <AskTable app={app} id={app.sel.id} /> : <
|
|
96
|
+
{app.sel.id === 'checks' ? <CheckTable app={app} id={app.sel.id} /> : app.sel.id === 'asks' ? <AskTable app={app} id={app.sel.id} /> : app.sel.id === 'creds' ? <SecretTable app={app} id={app.sel.id} /> : <ItemTable app={app} id={app.sel.id} />}
|
|
92
97
|
</main>
|
|
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 (
|
|
@@ -185,13 +195,18 @@ const RepoView = observer(({ app }: { app: RepoSt }) => {
|
|
|
185
195
|
function onKey(e: KeyboardEvent): void {
|
|
186
196
|
// the command keys beat the focus check: they work from anywhere, any page
|
|
187
197
|
const pk = pageKey(e)
|
|
188
|
-
|
|
198
|
+
// in a text field ⌘↑ and ⌘↓ keep moving the caret to its ends
|
|
199
|
+
const typing = (e.target as HTMLElement).tagName === 'INPUT' || (e.target as HTMLElement).tagName === 'TEXTAREA'
|
|
200
|
+
if (pk != null && !(pk.t === 'step' && typing)) {
|
|
189
201
|
e.preventDefault()
|
|
190
202
|
if (pk.t === 'picker') home.palette.toggle()
|
|
191
203
|
else if (pk.t === 'dock') home.dock.cycle()
|
|
192
204
|
else if (pk.t === 'home' || pk.t === 'planned' || pk.t === 'activity') {
|
|
193
205
|
home.palette.close()
|
|
194
206
|
if (home.machine != null) home.navigate(PLACE_PATH[pk.t === 'home' ? 'overview' : pk.t])
|
|
207
|
+
} else if (pk.t === 'step') {
|
|
208
|
+
home.palette.close()
|
|
209
|
+
home.projects.step(pk.by)
|
|
195
210
|
} else {
|
|
196
211
|
const p = home.projects.list[pk.n - 1]
|
|
197
212
|
home.palette.close()
|
|
@@ -225,6 +240,15 @@ function onKey(e: KeyboardEvent): void {
|
|
|
225
240
|
return
|
|
226
241
|
}
|
|
227
242
|
if (e.metaKey || e.ctrlKey || e.altKey) return
|
|
243
|
+
// the tree's keys need no focus: the arrows move it from anywhere on the
|
|
244
|
+
// page, Enter and space only where no button or link claims them
|
|
245
|
+
// the atelier's ←/→ roll, so at its first or last roll they stop there
|
|
246
|
+
const rolls = app.sel?.t === 'asset' && (e.key === 'ArrowLeft' || e.key === 'ArrowRight')
|
|
247
|
+
const own = !rolls && (e.key.startsWith('Arrow') || t.closest('button, a, select, [role="button"]') == null)
|
|
248
|
+
if (own && app.tree.key(e.key)) {
|
|
249
|
+
e.preventDefault()
|
|
250
|
+
return
|
|
251
|
+
}
|
|
228
252
|
if (e.key === FOLLOW_KEY && (!app.live.chart.follow || !home.dock.chart.follow)) {
|
|
229
253
|
e.preventDefault()
|
|
230
254
|
app.live.chart.attach()
|
|
@@ -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
|