rvlib-shipkit 1.1.0 → 1.2.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 +67 -0
- package/package.json +3 -1
- package/src/check.ts +24 -0
- package/src/checks/SK022-machine-runs-the-pin.check.ts +43 -0
- package/src/checks/SK023-stores-under-cap.check.ts +49 -0
- package/src/checks/SK024-identity-colors.check.ts +49 -0
- package/src/cli/cmd_Check.ts +26 -1
- package/src/cli/cmd_Ci.ts +18 -5
- package/src/cli/cmd_Home.ts +170 -0
- package/src/cli.ts +22 -2
- package/src/comfy/comfy-panel.ts +1 -16
- package/src/core/attach.ts +14 -9
- package/src/core/check-file-names.ts +8 -0
- package/src/core/check-runner.ts +40 -18
- package/src/core/discover-checks.ts +36 -18
- package/src/core/identity-color.ts +203 -0
- package/src/core/identity.ts +91 -0
- package/src/core/job-follow.ts +102 -0
- package/src/core/job-run.ts +27 -0
- package/src/core/jobs.ts +87 -27
- package/src/core/legacy-shapes.ts +1 -14
- package/src/core/machine-db.ts +83 -0
- package/src/core/one-lib.ts +85 -0
- package/src/core/port-holders.ts +43 -0
- package/src/core/retention.ts +213 -0
- package/src/core/run-check.ts +6 -2
- package/src/core/run-ci.ts +3 -0
- package/src/core/slots.ts +5 -0
- package/src/core/statement-end.ts +17 -0
- package/src/home/attach.ts +95 -0
- package/src/home/autostart.ts +108 -0
- package/src/home/global-units.ts +86 -0
- package/src/home/gui-app/ShipkitGui.swift +159 -0
- package/src/home/gui-app.ts +99 -0
- package/src/home/handoff.ts +51 -0
- package/src/home/home-launchd.ts +104 -0
- package/src/home/home-paths.ts +88 -0
- package/src/home/home-routes.ts +208 -0
- package/src/home/home-start.ts +105 -0
- package/src/home/home-state.ts +43 -0
- package/src/home/home-words.ts +12 -0
- package/src/home/hubs.ts +286 -0
- package/src/home/lib-watch.ts +139 -0
- package/src/home/machine-stores.ts +115 -0
- package/src/home/open-app.ts +16 -0
- package/src/home/pm-config.ts +37 -0
- package/src/home/pm.ts +141 -0
- package/src/home/resources.ts +338 -0
- package/src/home/run-daemon.ts +137 -0
- package/src/home/run-pm.ts +149 -0
- package/src/home/run-unit-action.ts +25 -0
- package/src/home/run-web.ts +37 -0
- package/src/home/serve-daemon.ts +31 -0
- package/src/home/serve-web.ts +172 -0
- package/src/home/supervise.ts +346 -0
- package/src/home/via-hub.ts +142 -0
- package/src/home/widgets.ts +102 -0
- package/src/ledger/check-stats.ts +19 -45
- package/src/ledger/db.ts +69 -17
- package/src/ledger/gate-checks.ts +5 -0
- package/src/ledger/git-scan.ts +31 -7
- package/src/ledger/ledger.ts +40 -14
- package/src/ledger/run-log.ts +58 -23
- package/src/ledger/store.ts +228 -103
- package/src/ledger/world-cache.ts +15 -13
- package/src/plan/cadence.ts +237 -0
- package/src/plan/planner.ts +303 -0
- package/src/plan/repo-source.ts +100 -0
- package/src/problems/problem-line.ts +1 -1
- package/src/repo-config.ts +51 -2
- package/src/serve/hub-app/check-view.tsx +9 -9
- package/src/serve/hub-app/check-words.ts +1 -1
- package/src/serve/hub-app/checks-svc.ts +12 -8
- package/src/serve/hub-app/col-card.tsx +4 -4
- package/src/serve/hub-app/cols-svc.ts +92 -13
- package/src/serve/hub-app/comfy-svc.ts +12 -6
- package/src/serve/hub-app/comfy-view.tsx +3 -3
- package/src/serve/hub-app/floating.tsx +49 -0
- package/src/serve/hub-app/geometry-view.tsx +4 -4
- package/src/serve/hub-app/hash-route.ts +1 -1
- package/src/serve/hub-app/header-bar.tsx +44 -12
- package/src/serve/hub-app/health-svc.ts +3 -3
- package/src/serve/hub-app/help.tsx +10 -7
- package/src/serve/hub-app/home-st.ts +241 -0
- package/src/serve/hub-app/home-strip.css +70 -0
- package/src/serve/hub-app/home-strip.tsx +245 -0
- package/src/serve/hub-app/home.html +17 -0
- package/src/serve/hub-app/hub-app.tsx +201 -93
- package/src/serve/hub-app/hub-types.ts +0 -3
- package/src/serve/hub-app/hub.css +43 -23
- package/src/serve/hub-app/icons.tsx +14 -0
- package/src/serve/hub-app/index.html +3 -1
- package/src/serve/hub-app/keys.ts +25 -0
- package/src/serve/hub-app/layout-st.ts +55 -0
- package/src/serve/hub-app/live-svc.ts +12 -9
- package/src/serve/hub-app/machine-st.ts +270 -0
- package/src/serve/hub-app/media-svc.ts +63 -44
- package/src/serve/hub-app/media-view.tsx +20 -20
- package/src/serve/hub-app/next-run.ts +24 -30
- package/src/serve/hub-app/palette-st.ts +88 -0
- package/src/serve/hub-app/planned-st.ts +171 -0
- package/src/serve/hub-app/planned-view.tsx +109 -0
- package/src/serve/hub-app/planned.css +39 -0
- package/src/serve/hub-app/processes-svc.ts +63 -0
- package/src/serve/hub-app/project-list.css +97 -0
- package/src/serve/hub-app/project-list.tsx +167 -0
- package/src/serve/hub-app/projects-svc.ts +139 -17
- package/src/serve/hub-app/prose-hits-st.ts +3 -3
- package/src/serve/hub-app/prose-hits-view.tsx +6 -6
- package/src/serve/hub-app/{hub-st.ts → repo-st.ts} +155 -84
- package/src/serve/hub-app/runs-tabs.ts +1 -1
- package/src/serve/hub-app/screen-cache.ts +74 -0
- package/src/serve/hub-app/test-view.tsx +4 -4
- package/src/serve/hub-app/tests-svc.ts +3 -3
- package/src/serve/hub-app/tests-view.tsx +10 -10
- package/src/serve/hub-app/timeline-chart.tsx +24 -12
- package/src/serve/hub-app/timeline-svc.ts +5 -4
- package/src/serve/hub-app/timeline-view.tsx +13 -13
- package/src/serve/hub-app/tree-row.tsx +2 -2
- package/src/serve/hub-app/tree-svc.ts +2 -2
- package/src/serve/hub-app/wikis-view.tsx +10 -6
- package/src/serve/hub-runtime.ts +542 -0
- package/src/serve/project-gutter-route.ts +21 -0
- package/src/serve/project-gutter.ts +3 -2
- package/src/serve/repo-processes.ts +89 -0
- package/src/serve/run-serve.ts +110 -402
- package/src/serve/serve-hub.ts +142 -44
- package/src/serve/stale-restart.ts +5 -0
- package/src/serve/{gate-trigger.ts → tracked-watch.ts} +4 -64
- package/src/serve/warm-cell.ts +95 -0
- package/src/shipkit-files.ts +114 -0
- package/src/shipkit.ts +2 -1
- package/src/suite/kept-timings.ts +1 -1
- package/src/wiki/graph-positions.ts +1 -1
- package/src/serve/health-schedule.ts +0 -102
- package/src/serve/hub-app/project-gutter.css +0 -65
- package/src/serve/hub-app/project-gutter.tsx +0 -128
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
// the home's strip over every page it serves: one quiet line. On a repo page
|
|
2
|
+
// the shown repo (its icon, its name in its own colour, the two swatches that
|
|
3
|
+
// set those colours); on the overview and the plan, the page's name. On the
|
|
4
|
+
// right, only machine wide trouble, and only while it lasts (pm or the daemon
|
|
5
|
+
// down, a child or a global process crashed). The places themselves are the
|
|
6
|
+
// two buttons at the top of the project list (⌘J, ⌘P) and the first two
|
|
7
|
+
// entries of the picker.
|
|
8
|
+
// What belongs to one repo (its hub state, restart, the active flag, VS
|
|
9
|
+
// Code, modernize) is RepoControls, in that repo's own header. NotUp stands
|
|
10
|
+
// in for the hub page while its hub cannot answer, with the fix. Every link
|
|
11
|
+
// of the home is a page of the same app: a plain click moves it in place.
|
|
12
|
+
import { observer } from 'mobx-react-lite'
|
|
13
|
+
import type { ReactNode } from 'react'
|
|
14
|
+
import type { ResourceView } from '../../home/resources.ts'
|
|
15
|
+
import type { WorkerView } from '../../home/hubs.ts'
|
|
16
|
+
import { NOT_INSTALLED } from '../../home/home-words.ts'
|
|
17
|
+
import type { MachineSt } from './machine-st.ts'
|
|
18
|
+
import { plainClick, type HomeSt } from './home-st.ts'
|
|
19
|
+
import { Icon, ICON } from './icons.tsx'
|
|
20
|
+
import { Tip } from './floating.tsx'
|
|
21
|
+
|
|
22
|
+
const STATUS_WORD: Record<WorkerView['status'], string> = { up: 'up', starting: 'starting', crashed: 'crashed', down: 'down', stopping: 'stopping' }
|
|
23
|
+
|
|
24
|
+
export const Dot = ({ s }: { s: WorkerView['status'] }) => <i className={`hdot ${s}`} />
|
|
25
|
+
|
|
26
|
+
/** a link to a page of the home: a plain click shows it in place, any other
|
|
27
|
+
* click keeps the browser's own behaviour */
|
|
28
|
+
export const HomeLink = ({ app, to, className, title, children }: { app: HomeSt; to: string; className?: string; title?: string; children: ReactNode }) => (
|
|
29
|
+
<a className={className} href={to} title={title} onClick={(e) => {
|
|
30
|
+
if (!plainClick(e)) return
|
|
31
|
+
e.preventDefault()
|
|
32
|
+
app.navigate(to)
|
|
33
|
+
}}>{children}</a>
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
const Log = ({ lines }: { lines: string[] }) => (lines.length === 0 ? null : <pre className="hlog">{lines.join('\n')}</pre>)
|
|
37
|
+
|
|
38
|
+
/** one switch per global process; its log floats under it on hover */
|
|
39
|
+
export const ResourceChip = observer(({ m, r }: { m: MachineSt; r: ResourceView }) => {
|
|
40
|
+
const on = r.status === 'up' || r.status === 'starting'
|
|
41
|
+
const title = [r.description, r.where != null ? `on ${r.where}` : null, r.note].filter((x) => x != null).join(' · ')
|
|
42
|
+
const log = r.tail.length > 0 ? `${title !== '' ? `${title}\n\n` : ''}${r.tail.join('\n')}` : null
|
|
43
|
+
return (
|
|
44
|
+
<Tip className="hproc" tipClass="hpop" placement="bottom-start" interactive content={log} data-log={log == null ? undefined : 'yes'}>
|
|
45
|
+
<button className={`hres ${r.status}`} title={log != null ? undefined : title}
|
|
46
|
+
disabled={r.status === 'stopping' || (on && !r.canStop)}
|
|
47
|
+
onClick={() => void m.processAct(r.id, on ? 'stop' : 'start')}>
|
|
48
|
+
<Dot s={r.status} />{r.label}
|
|
49
|
+
</button>
|
|
50
|
+
</Tip>
|
|
51
|
+
)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
/** a global process on the overview, as one card: its switch (its log
|
|
55
|
+
* floats on hover), its state in words, the repo and file that declare it,
|
|
56
|
+
* its url, then its buttons: restart, take over when a copy started
|
|
57
|
+
* elsewhere runs, open in the browser, open as an app window (app: true
|
|
58
|
+
* only), its declared actions */
|
|
59
|
+
const ProcessCard = observer(({ m, r }: { m: MachineSt; r: ResourceView }) => {
|
|
60
|
+
const up = r.status === 'up'
|
|
61
|
+
return (
|
|
62
|
+
<section className="hcard">
|
|
63
|
+
<div className="hchead">
|
|
64
|
+
<ResourceChip m={m} r={r} />
|
|
65
|
+
<span className={`hcstate ${r.status}`}>{STATUS_WORD[r.status]}</span>
|
|
66
|
+
</div>
|
|
67
|
+
<div className="hdim hcfile">{r.repo}/{r.file}</div>
|
|
68
|
+
{r.url != null ? (
|
|
69
|
+
<a className="hcurl" href={r.url} title={`open ${r.url} in the default browser`} onClick={(e) => {
|
|
70
|
+
// the default browser, through pm, never inside the shipkit window
|
|
71
|
+
e.preventDefault()
|
|
72
|
+
void m.processAct(r.id, 'open')
|
|
73
|
+
}}>{r.url.replace(/^https?:\/\//, '')}</a>
|
|
74
|
+
) : null}
|
|
75
|
+
{r.description != null || r.where != null ? <div className="hdim">{[r.description, r.where != null ? `on ${r.where}` : null].filter((x) => x != null).join(' · ')}</div> : null}
|
|
76
|
+
<div className="hcbtns">
|
|
77
|
+
{up && r.canStop ? <button className="ghost" onClick={() => void m.processAct(r.id, 'restart')} title={`stop ${r.label} and start it again`}>restart</button> : null}
|
|
78
|
+
{r.takeOver?.ok === true ? <button className="ghost" onClick={() => void m.processAct(r.id, 'takeover')} title={`stop the copy of ${r.label} started elsewhere (the process group listening on its port) and start one pm supervises, in one go`}>take over</button> : null}
|
|
79
|
+
{r.url != null ? <button className="ghost" onClick={() => void m.processAct(r.id, 'open')} title={up ? `open ${r.url} in the default browser` : `start ${r.label}, then open ${r.url} in the default browser`}>{up ? 'open' : 'start and open'}</button> : null}
|
|
80
|
+
{r.app && r.url != null ? <button className="ghost" onClick={() => void m.processAct(r.id, 'app')} title={up ? `open ${r.url} as a shipkit app window` : `start ${r.label}, then open ${r.url} as a shipkit app window`}>open as app</button> : null}
|
|
81
|
+
{r.actions.map((a) => (
|
|
82
|
+
<button key={a.id} className="ghost" disabled={r.running != null} onClick={() => void m.processAct(r.id, 'action', a.id)}>{r.running === a.id ? <span className="spin" /> : null}{a.label}</button>
|
|
83
|
+
))}
|
|
84
|
+
</div>
|
|
85
|
+
{r.takeOver?.ok === false ? <div className="hnote">cannot take over: {r.takeOver.why}</div> : null}
|
|
86
|
+
{r.note != null ? <div className="hnote">{r.note}</div> : null}
|
|
87
|
+
</section>
|
|
88
|
+
)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
/** every house repo's widgets: the html the daemon last rendered, each under its title */
|
|
92
|
+
const Monitor = observer(({ m }: { m: MachineSt }) => {
|
|
93
|
+
const w = m.widgets
|
|
94
|
+
if (w == null || (w.widgets.length === 0 && w.errors.length === 0)) return null
|
|
95
|
+
return (
|
|
96
|
+
<section className="hsec">
|
|
97
|
+
<h2><Icon d={ICON.monitor} />monitor</h2>
|
|
98
|
+
<div className="hmon">
|
|
99
|
+
{w.widgets.map((x) => (
|
|
100
|
+
<section key={`${x.repo}/${x.id}`} className="hwidget" title={`${x.repo}/${x.file}`}>
|
|
101
|
+
<h3>{x.title}<span className="hdim"> · {x.repo}</span></h3>
|
|
102
|
+
{x.error != null ? <p className="hnote">{x.error}</p> : null}
|
|
103
|
+
{x.html == null ? <p className="twait"><span className="spin" /></p> : <div dangerouslySetInnerHTML={{ __html: x.html }} />}
|
|
104
|
+
</section>
|
|
105
|
+
))}
|
|
106
|
+
</div>
|
|
107
|
+
{w.errors.map((e) => <p key={e} className="hnote">{e}</p>)}
|
|
108
|
+
</section>
|
|
109
|
+
)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
/** the two colours of a repo, a picker each: the page draws the dark one */
|
|
113
|
+
export const IdentitySwatches = observer(({ app, name }: { app: HomeSt; name: string }) => {
|
|
114
|
+
const id = app.projects.identityOf(name)
|
|
115
|
+
return (
|
|
116
|
+
<span className="hswatch">
|
|
117
|
+
<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`}
|
|
118
|
+
value={id.colorDark} onChange={(e) => app.projects.setIdentity(name, { colorDark: e.currentTarget.value })} />
|
|
119
|
+
<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`}
|
|
120
|
+
value={id.colorLight} onChange={(e) => app.projects.setIdentity(name, { colorLight: e.currentTarget.value })} />
|
|
121
|
+
</span>
|
|
122
|
+
)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
/** the shown repo in the strip: its icon, its name in its colour */
|
|
126
|
+
const ShownRepo = observer(({ app, name }: { app: HomeSt; name: string }) => {
|
|
127
|
+
const p = app.projects.list.find((x) => x.name === name)
|
|
128
|
+
const id = app.projects.identityOf(name)
|
|
129
|
+
return (
|
|
130
|
+
<span className="hrepo">
|
|
131
|
+
{/* the icon's box is there before the project list is, so the name never moves */}
|
|
132
|
+
{p?.icon != null ? <img src={p.icon} alt="" width={18} height={18} /> : <i className="hrico" />}
|
|
133
|
+
<b className="hrname" style={{ color: id.colorDark }}>{name}</b>
|
|
134
|
+
<IdentitySwatches app={app} name={name} />
|
|
135
|
+
</span>
|
|
136
|
+
)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
/** what is wrong with the machine itself, one chip each. Nothing when all is
|
|
140
|
+
* well: the strip states trouble, never health */
|
|
141
|
+
const MachineAlerts = observer(({ m }: { m: MachineSt }) => (
|
|
142
|
+
<>
|
|
143
|
+
{(m.pm?.children ?? []).filter((c) => c.status === 'crashed' || c.status === 'down').map((c) => (
|
|
144
|
+
<span key={c.id} className="halert" title={c.note ?? c.tail.at(-1) ?? ''}><Dot s={c.status} />{c.id} {STATUS_WORD[c.status]}</span>
|
|
145
|
+
))}
|
|
146
|
+
{(m.pm?.resources ?? []).filter((r) => r.status === 'crashed').map((r) => <ResourceChip key={r.id} m={m} r={r} />)}
|
|
147
|
+
{m.error !== '' ? <span className="herr" title={m.error}>{m.error}</span> : null}
|
|
148
|
+
</>
|
|
149
|
+
))
|
|
150
|
+
|
|
151
|
+
export const HomeStrip = observer(({ app }: { app: HomeSt }) => {
|
|
152
|
+
const m = app.machine
|
|
153
|
+
if (m == null) return null
|
|
154
|
+
const name = m.repo
|
|
155
|
+
return (
|
|
156
|
+
<>
|
|
157
|
+
{m.down !== '' ? <div className="hdown" role="alert"><b>daemon down</b> {m.down}</div> : null}
|
|
158
|
+
{m.pmDown !== '' ? <div className="hdown" role="alert"><b>pm down</b> {m.pmDown}</div> : null}
|
|
159
|
+
<div className="hstrip">
|
|
160
|
+
{name == null ? <span className="hplace">{app.page === 'planned' ? 'planned' : 'overview'}</span> : <ShownRepo app={app} name={name} />}
|
|
161
|
+
<span className="sp" />
|
|
162
|
+
<MachineAlerts m={m} />
|
|
163
|
+
</div>
|
|
164
|
+
</>
|
|
165
|
+
)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
/** one repo's own controls on the home, in that repo's header: its hub
|
|
169
|
+
* state, restart, the active flag, VS Code, and modernize when it is behind */
|
|
170
|
+
export const RepoControls = observer(({ app, name }: { app: HomeSt; name: string }) => {
|
|
171
|
+
const m = app.machine
|
|
172
|
+
if (m == null) return null
|
|
173
|
+
const w = m.worker(name)
|
|
174
|
+
const behind = m.data?.behind[name]
|
|
175
|
+
return (
|
|
176
|
+
<span className="hctl">
|
|
177
|
+
{w == null || w.status === 'up' ? null : <span className="hstat"><Dot s={w.status} />{STATUS_WORD[w.status]}</span>}
|
|
178
|
+
<button className="ghost" onClick={() => void m.hub(name, 'restart')} title="load this repo's hub again, its config read fresh">restart</button>
|
|
179
|
+
<label className="hflag" title="an active repo runs the checks the plan holds for it, and gets its pings, even with no agent in it">
|
|
180
|
+
<input type="checkbox" checked={m.isActive(name)} onChange={(e) => void m.setActive(name, e.currentTarget.checked)} />active
|
|
181
|
+
</label>
|
|
182
|
+
<button className="ghost" onClick={() => m.openInCode(name)} title="open the folder in VS Code (or double click its icon)">VS Code</button>
|
|
183
|
+
{behind == null ? null : <button className="primary" onClick={() => void m.modernize(name)} title={`this repo runs shipkit ${behind}, the home runs ${m.data?.version}`}>modernize {behind} → {m.data?.version}</button>}
|
|
184
|
+
</span>
|
|
185
|
+
)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
/** why the shown repo's hub does not answer, and what to do about it */
|
|
189
|
+
export const NotUp = observer(({ app }: { app: HomeSt }) => {
|
|
190
|
+
const m = app.machine
|
|
191
|
+
const name = m?.repo
|
|
192
|
+
if (m == null || name == null) return null
|
|
193
|
+
const w = m.shown
|
|
194
|
+
const mod = m.data?.modernize[name]
|
|
195
|
+
if (w == null) return <div className="hwait"><p className="twait"><span className="spin" />{m.down !== '' ? `waiting for the daemon to load ${name}…` : `looking for ${name}…`}</p></div>
|
|
196
|
+
return (
|
|
197
|
+
<div className="hwait">
|
|
198
|
+
{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>}
|
|
199
|
+
{w.blocked == null && (w.status === 'down' || w.status === 'crashed') ? <button className="primary" onClick={() => void m.hub(name, 'restart')}>start it</button> : null}
|
|
200
|
+
{w.blocked != null && mod?.status !== 'running' ? <button className="primary" onClick={() => void m.modernize(name)}>{w.blocked === NOT_INSTALLED ? 'bun install' : 'modernize'}</button> : null}
|
|
201
|
+
{mod == null ? null : <><p>modernize: {mod.status}{mod.code != null ? `, exit ${mod.code}` : ''}</p><Log lines={mod.tail} /></>}
|
|
202
|
+
<Log lines={w.tail} />
|
|
203
|
+
</div>
|
|
204
|
+
)
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
/** the overview at `/`: shipkit's own processes, a card per global process,
|
|
208
|
+
* then the widgets. The repos are the project list's, not a section here */
|
|
209
|
+
export const Overview = observer(({ app }: { app: HomeSt }) => {
|
|
210
|
+
const m = app.machine
|
|
211
|
+
if (m == null) return null
|
|
212
|
+
if (m.data == null) return <div className="hwait"><p className="twait"><span className="spin" />{m.down !== '' ? 'the daemon does not answer yet' : 'loading…'}</p></div>
|
|
213
|
+
const procs = m.pm?.resources ?? []
|
|
214
|
+
return (
|
|
215
|
+
<div className="hall">
|
|
216
|
+
<div className="hstack">
|
|
217
|
+
<section className="hsec">
|
|
218
|
+
<h2><Icon d={ICON.shipkit} />shipkit<span className="sp" /><button className="ghost" title="open one more window on this page" onClick={() => m.newWindow()}>new window</button></h2>
|
|
219
|
+
{m.pm == null ? <p className="hdim">pm does not answer, so its children and the global processes are unknown</p> : (
|
|
220
|
+
<div className="hcards"><section className="hcard">
|
|
221
|
+
<div className="hrow"><span className="hlink"><Dot s="up" />pm</span><span className="hdim">pid {m.pm.pid} · shipkit {m.pm.version}</span></div>
|
|
222
|
+
{m.pm.children.map((c) => (
|
|
223
|
+
<div key={c.id} className="hrow">
|
|
224
|
+
<span className="hlink"><Dot s={c.status} />{c.id}</span>
|
|
225
|
+
<span className="hdim">{STATUS_WORD[c.status]}{c.pid != null ? ` · pid ${c.pid}` : ''}{c.restarts > 0 ? ` · ${c.restarts} restart(s)` : ''}</span>
|
|
226
|
+
{c.note != null ? <span className="hnote">{c.note}</span> : null}
|
|
227
|
+
<span className="sp" />
|
|
228
|
+
<button className="ghost" onClick={() => void m.restartChild(c.id as 'daemon' | 'web')} title={`stop the ${c.id} and start it again, through pm`}>restart</button>
|
|
229
|
+
{c.status === 'crashed' ? <Log lines={c.tail} /> : null}
|
|
230
|
+
</div>
|
|
231
|
+
))}
|
|
232
|
+
</section></div>
|
|
233
|
+
)}
|
|
234
|
+
</section>
|
|
235
|
+
<section className="hsec">
|
|
236
|
+
<h2><Icon d={ICON.process} />global processes</h2>
|
|
237
|
+
{procs.length === 0 ? <p className="hdim">{m.pm == null ? 'unknown while pm does not answer' : 'none declared: a repo declares one in a <name>.shipkit.ts file, as export default globalProcess({...})'}</p> : null}
|
|
238
|
+
<div className="hcards">{procs.map((r) => <ProcessCard key={r.id} m={m} r={r} />)}</div>
|
|
239
|
+
{(m.pm?.unitErrors ?? []).map((e) => <p key={e} className="hnote">{e}</p>)}
|
|
240
|
+
</section>
|
|
241
|
+
<Monitor m={m} />
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
)
|
|
245
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>home</title>
|
|
7
|
+
<link rel="stylesheet" href="./hub.css" />
|
|
8
|
+
<link rel="stylesheet" href="./project-list.css" />
|
|
9
|
+
<link rel="stylesheet" href="./prose-hits.css" />
|
|
10
|
+
<link rel="stylesheet" href="./home-strip.css" />
|
|
11
|
+
<link rel="stylesheet" href="./planned.css" />
|
|
12
|
+
</head>
|
|
13
|
+
<body data-home>
|
|
14
|
+
<div id="root"></div>
|
|
15
|
+
<script type="module" src="./hub-app.tsx"></script>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|