rvlib-shipkit 1.2.0 → 1.3.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/README.md +1 -1
- package/package.json +1 -1
- package/src/assets/run-assets.ts +3 -8
- package/src/check.ts +20 -6
- package/src/checks/SK001-package-json-shape.check.ts +14 -3
- package/src/checks/SK002-contracts-fulfillness.check.ts +3 -3
- package/src/checks/SK004-gitignore-policy.check.ts +11 -3
- package/src/checks/SK005-rvlib-versions.check.ts +13 -1
- package/src/checks/SK006-npm-publish-safety.check.ts +5 -0
- package/src/checks/SK007-github-visibility.check.ts +9 -9
- package/src/checks/SK008-reviewer-agent.check.ts +14 -8
- package/src/checks/SK009-commit-ai-tells.check.ts +29 -5
- package/src/checks/SK013-one-way-in-policy.check.ts +10 -2
- package/src/checks/SK015-nested-repo.check.ts +5 -1
- package/src/checks/SK017-vscode-hub-task.check.ts +7 -0
- package/src/checks/SK022-machine-runs-the-pin.check.ts +6 -2
- package/src/checks/SK024-identity-colors.check.ts +5 -0
- package/src/checks/SK025-release-drift.check.ts +58 -0
- package/src/checks/SK026-asks-autoclose.check.ts +44 -0
- package/src/checks/SK027-agent-wiki-links.check.ts +28 -0
- package/src/checks/SK028-readme.check.ts +79 -0
- package/src/cli/cmd_Assets.ts +1 -1
- package/src/cli/cmd_Ci.ts +5 -0
- package/src/cli/cmd_CrossRepoActivity.ts +116 -0
- package/src/cli/cmd_GlobalProcesses.ts +155 -0
- package/src/cli/cmd_Keys.ts +17 -3
- package/src/cli/cmd_Overview.ts +98 -0
- package/src/cli/cmd_Planned.ts +117 -0
- package/src/cli.ts +9 -1
- package/src/collections/checks.ts +3 -2
- package/src/collections/providers.ts +1 -1
- package/src/collections/test-time.ts +10 -1
- package/src/core/agent-spawn.ts +46 -0
- package/src/core/archived.ts +82 -0
- package/src/core/canonical-scripts.ts +12 -3
- package/src/core/check-runner.ts +40 -12
- package/src/core/discover-checks.ts +2 -0
- package/src/core/file-stamp.ts +20 -0
- package/src/{checks/SK021-github-login.check.ts → core/gh-login.ts} +45 -54
- package/src/core/identity-color.ts +1 -1
- package/src/core/repo-facts.ts +14 -0
- package/src/core/retention.ts +14 -5
- package/src/core/retired-checks.ts +7 -0
- package/src/core/run-check.ts +6 -2
- package/src/core/run-ci.ts +43 -12
- package/src/core/run-fix.ts +7 -2
- package/src/core/run-fixers.ts +10 -6
- package/src/core/wire.ts +11 -0
- package/src/fleet/scan-fleet.ts +2 -1
- package/src/home/activity-shape.ts +135 -0
- package/src/home/activity.ts +249 -0
- package/src/home/global-units.ts +3 -2
- package/src/home/home-routes.ts +32 -5
- package/src/home/pm.ts +37 -4
- package/src/home/process-rows.ts +205 -0
- package/src/home/resources.ts +35 -8
- package/src/home/run-daemon.ts +6 -6
- package/src/home/run-pm.ts +1 -0
- package/src/home/serve-web.ts +6 -3
- package/src/home/supervise.ts +16 -1
- package/src/home/widget-text.ts +119 -0
- package/src/hooks/run-hooks.ts +20 -3
- package/src/ledger/db.ts +1 -1
- package/src/ledger/gate-checks.ts +17 -3
- package/src/ledger/house-runs.ts +43 -0
- package/src/ledger/live-timeline.ts +4 -19
- package/src/ledger/run-log.ts +39 -10
- package/src/ledger/store.ts +19 -2
- package/src/ledger/world-cache.ts +51 -4
- package/src/plan/cadence.ts +21 -5
- package/src/plan/catch-up.ts +37 -0
- package/src/plan/plan-timeline.ts +99 -0
- package/src/plan/planner.ts +138 -13
- package/src/plan/repo-source.ts +120 -8
- package/src/release/run-release.ts +9 -2
- package/src/release/unit-resolve.ts +8 -12
- package/src/repo-config.ts +11 -3
- package/src/serve/house-runs.ts +53 -0
- package/src/serve/hub-app/activity-st.ts +168 -0
- package/src/serve/hub-app/activity-view.tsx +218 -0
- package/src/serve/hub-app/activity.css +78 -0
- package/src/serve/hub-app/admin-svc.ts +41 -0
- package/src/serve/hub-app/admin-view.tsx +42 -0
- package/src/serve/hub-app/chart-view.ts +12 -5
- package/src/serve/hub-app/check-words.ts +2 -0
- package/src/serve/hub-app/controls.css +32 -0
- package/src/serve/hub-app/cycle-button.tsx +36 -0
- package/src/serve/hub-app/dock-anim.ts +25 -0
- package/src/serve/hub-app/dock-st.ts +329 -0
- package/src/serve/hub-app/dock-view.tsx +68 -0
- package/src/serve/hub-app/dock.css +35 -0
- package/src/serve/hub-app/hash-route.ts +7 -1
- package/src/serve/hub-app/header-bar.tsx +79 -104
- package/src/serve/hub-app/home-st.ts +49 -12
- package/src/serve/hub-app/home-strip.css +65 -22
- package/src/serve/hub-app/home-strip.tsx +228 -97
- package/src/serve/hub-app/home.html +4 -0
- package/src/serve/hub-app/hub-app.tsx +96 -160
- package/src/serve/hub-app/hub.css +77 -37
- package/src/serve/hub-app/icons.tsx +23 -3
- package/src/serve/hub-app/index.html +4 -0
- package/src/serve/hub-app/keys.ts +42 -7
- package/src/serve/hub-app/live-svc.ts +24 -64
- package/src/serve/hub-app/machine-st.ts +64 -1
- package/src/serve/hub-app/md.ts +38 -5
- package/src/serve/hub-app/next-run.ts +1 -1
- package/src/serve/hub-app/palette-st.ts +11 -7
- package/src/serve/hub-app/palette-view.tsx +47 -0
- package/src/serve/hub-app/pill-row.tsx +65 -0
- package/src/serve/hub-app/planned-st.ts +21 -85
- package/src/serve/hub-app/planned-view.tsx +12 -7
- package/src/serve/hub-app/planned.css +5 -2
- package/src/serve/hub-app/project-list.css +43 -21
- package/src/serve/hub-app/project-list.tsx +122 -23
- package/src/serve/hub-app/projects-svc.ts +120 -12
- package/src/serve/hub-app/readme-svc.ts +38 -0
- package/src/serve/hub-app/readme-view.tsx +19 -0
- package/src/serve/hub-app/readme.css +11 -0
- package/src/serve/hub-app/repo-sections.tsx +61 -0
- package/src/serve/hub-app/repo-st.ts +25 -6
- package/src/serve/hub-app/runs-tabs.ts +1 -1
- package/src/serve/hub-app/sidebar.tsx +84 -0
- package/src/serve/hub-app/tests-view.tsx +31 -4
- package/src/serve/hub-app/timeline-chart.tsx +31 -27
- package/src/serve/hub-app/timeline-geometry.ts +40 -5
- package/src/serve/hub-app/timeline-svc.ts +2 -2
- package/src/serve/hub-app/timeline-view.tsx +20 -3
- package/src/serve/hub-runtime.ts +17 -9
- package/src/serve/other-repos.ts +8 -45
- package/src/serve/project-gutter-route.ts +100 -20
- package/src/serve/project-gutter.ts +33 -11
- package/src/serve/readme-route.ts +57 -0
- package/src/serve/serve-hub.ts +13 -8
- package/src/serve/tracked-watch.ts +33 -1
- package/src/shipkit-files.ts +20 -4
- package/src/checks/SK023-stores-under-cap.check.ts +0 -49
- package/src/checks/contracts.check.ts +0 -20
|
@@ -1,37 +1,43 @@
|
|
|
1
1
|
// THE page (Bun.serve HTML import — no vite, no build step), one single page
|
|
2
|
-
// app for every page the home serves ('/', '/planned', '/r/<repo>/') and for a
|
|
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
|
-
// resource, by path or by type, coloured by
|
|
7
|
-
//
|
|
8
|
-
// view, an asset row opens the atelier.
|
|
5
|
+
// state, components read and call. A repo's page is its sections over ONE
|
|
6
|
+
// tree of every repo resource (sidebar.tsx), by path or by type, coloured by
|
|
7
|
+
// state in the label itself, over one severity filter persisted at
|
|
8
|
+
// ?f=needs · ?f=red. A row opens its item view, an asset row opens the atelier.
|
|
9
9
|
import { runInAction } from 'mobx'
|
|
10
10
|
import { observer } from 'mobx-react-lite'
|
|
11
|
-
import { StrictMode, useEffect, useRef } from 'react'
|
|
11
|
+
import { StrictMode, useEffect, useLayoutEffect, useRef } from 'react'
|
|
12
12
|
import { createRoot } from 'react-dom/client'
|
|
13
13
|
import { ColCard, ColGrid, StatusCard } from './col-card.tsx'
|
|
14
14
|
import { WIKIS, WikisView } from './wikis-view.tsx'
|
|
15
15
|
import { ComfyOverlay, ComfyView } from './comfy-view.tsx'
|
|
16
16
|
import type { Filter, RepoSt } from './repo-st.ts'
|
|
17
|
-
import { HomeSt } from './home-st.ts'
|
|
17
|
+
import { HomeSt, PLACE_PATH } from './home-st.ts'
|
|
18
18
|
import { TimelineView } from './timeline-view.tsx'
|
|
19
|
-
import { HeaderBar,
|
|
19
|
+
import { HeaderBar, notUp } from './header-bar.tsx'
|
|
20
20
|
import { FOLLOW_KEY } from './timeline-chart.tsx'
|
|
21
21
|
import { CheckCard } from './check-view.tsx'
|
|
22
22
|
import { TestCard } from './test-view.tsx'
|
|
23
23
|
import { TestsPage } from './tests-view.tsx'
|
|
24
24
|
import { AssetStage, CatGrid, InfoChips, MediaTopbar, ToolsBar, VariantsBar } from './media-view.tsx'
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
25
|
+
import { Sidebar } from './sidebar.tsx'
|
|
26
|
+
import { AdminPage } from './admin-view.tsx'
|
|
27
|
+
import { ReadmeView } from './readme-view.tsx'
|
|
28
28
|
import { ProjectList } from './project-list.tsx'
|
|
29
|
-
import { Group, Panel, Separator, type Layout } from 'react-resizable-panels'
|
|
29
|
+
import { Group, Panel, Separator, usePanelRef, type Layout } from 'react-resizable-panels'
|
|
30
30
|
import { LIST, SIDEBAR } from './layout-st.ts'
|
|
31
|
-
import { HomeStrip, NotUp, Overview } from './home-strip.tsx'
|
|
31
|
+
import { ArchivedView, HomeStrip, NotUp, Overview } from './home-strip.tsx'
|
|
32
32
|
import { PlannedView } from './planned-view.tsx'
|
|
33
|
+
import { ActivityView } from './activity-view.tsx'
|
|
34
|
+
import { Palette } from './palette-view.tsx'
|
|
33
35
|
import { SCREEN_WAIT_MS } from './screen-cache.ts'
|
|
34
|
-
import { pageKey } from './keys.ts'
|
|
36
|
+
import { isSearchKey, pageKey, sectionKey } from './keys.ts'
|
|
37
|
+
import { repoSections } from './repo-sections.tsx'
|
|
38
|
+
import { Dock } from './dock-view.tsx'
|
|
39
|
+
import { DOCK } from './dock-st.ts'
|
|
40
|
+
import { animateDock } from './dock-anim.ts'
|
|
35
41
|
|
|
36
42
|
// home.html marks the home's page; index.html is a hub serving on its own
|
|
37
43
|
const home = new HomeSt({ home: document.body.dataset.home != null })
|
|
@@ -39,112 +45,6 @@ const home = new HomeSt({ home: document.body.dataset.home != null })
|
|
|
39
45
|
;(window as unknown as { app: HomeSt }).app = home
|
|
40
46
|
home.boot()
|
|
41
47
|
|
|
42
|
-
// ── the tree: every resource of the repo, one row deep in its path ──────
|
|
43
|
-
// the row itself is tree-row.tsx, so a test renders it headless
|
|
44
|
-
|
|
45
|
-
const Tree = observer(({ app }: { app: RepoSt }) => {
|
|
46
|
-
const t = app.tree
|
|
47
|
-
return (
|
|
48
|
-
// roving focus: the arrows belong to the tree only while it HAS the
|
|
49
|
-
// focus, so the atelier keeps its own keys
|
|
50
|
-
<div className="tree" tabIndex={0} onKeyDown={(e) => { if (t.key(e.key)) e.preventDefault() }}>
|
|
51
|
-
{t.rows.map((r) => <TreeRowView key={r.key} app={app} row={r} />)}
|
|
52
|
-
{!app.cols.loaded && !app.cols.cached ? <div className="tempty twait"><span className="spin" />reading the repo…</div> : null}
|
|
53
|
-
{app.cols.loaded && t.rows.length === 0 ? <div className="tempty">{app.filter === 'red' ? 'no reds — nothing is blocked ✨' : app.filter === 'needs' ? 'nothing needs you ✨' : 'no resources yet'}</div> : null}
|
|
54
|
-
</div>
|
|
55
|
-
)
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
/** what every glyph in the type slot means, behind one `(?)` in the header */
|
|
59
|
-
const TypeLegend = () => (
|
|
60
|
-
<Help text={
|
|
61
|
-
<span className="legend">
|
|
62
|
-
{Object.entries(TYPE_MARK).map(([kind, m]) => (
|
|
63
|
-
<span key={kind} className="lrow"><i className={`ty ${m.hue}`}>{m.glyph}</i>{m.word}</span>
|
|
64
|
-
))}
|
|
65
|
-
</span>
|
|
66
|
-
} />
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
const TreeTabs = observer(({ app }: { app: RepoSt }) => (
|
|
70
|
-
<span className="seg">
|
|
71
|
-
{(['path', 'type'] as const).map((t) => (
|
|
72
|
-
<button key={t} className={app.tree.tab === t ? 'on' : ''} onClick={() => app.tree.setTab(t)}>by {t}</button>
|
|
73
|
-
))}
|
|
74
|
-
</span>
|
|
75
|
-
))
|
|
76
|
-
|
|
77
|
-
// segmented severity filter — all · needs (yellow+red) · red, the address
|
|
78
|
-
// rides at /?f=… so a filtered view is shareable
|
|
79
|
-
// the shortcut letter rides INSIDE its own button, so the binding is
|
|
80
|
-
// discoverable without a legend
|
|
81
|
-
const FILTER_KEY = { all: 'a', needs: 'n', red: 'r' } as const
|
|
82
|
-
const SegFilter = observer(({ app }: { app: RepoSt }) => (
|
|
83
|
-
<span className="seg">
|
|
84
|
-
{(['all', 'needs', 'red'] as const).map((f) => (
|
|
85
|
-
<button key={f} className={app.filter === f ? 'on' : ''} onClick={() => app.setFilter(f)}>
|
|
86
|
-
{f}<span className="sk">{FILTER_KEY[f]}</span>
|
|
87
|
-
</button>
|
|
88
|
-
))}
|
|
89
|
-
</span>
|
|
90
|
-
))
|
|
91
|
-
|
|
92
|
-
const Sidebar = observer(({ app }: { app: RepoSt }) => (
|
|
93
|
-
<aside>
|
|
94
|
-
<div className="shead">
|
|
95
|
-
resources
|
|
96
|
-
<SegFilter app={app} />
|
|
97
|
-
</div>
|
|
98
|
-
<div className="ssearch">
|
|
99
|
-
<input ref={(el) => { app.searchEl = el }} placeholder="filter… (/ to focus, ⌘K to jump)" value={app.query}
|
|
100
|
-
onChange={(e) => app.setQuery(e.target.value)}
|
|
101
|
-
onKeyDown={(e) => { if (e.key === 'Escape') app.setQuery('') }} />
|
|
102
|
-
</div>
|
|
103
|
-
<div className="stabs">
|
|
104
|
-
<TreeTabs app={app} />
|
|
105
|
-
<span className="sp" />
|
|
106
|
-
<TypeLegend />
|
|
107
|
-
<span className="cnt">{app.tree.visible.length}</span>
|
|
108
|
-
</div>
|
|
109
|
-
{/* its line is held while the repo is read, so the tree never drops under it */}
|
|
110
|
-
{app.tree.whenLine === '' && app.cols.loaded ? null : <div className="swhen">{app.tree.whenLine === '' ? '\u00a0' : app.tree.whenLine}</div>}
|
|
111
|
-
{app.media.dead != null ? <div className="tempty"><span className="warnline">media API down: {app.media.dead}</span></div> : null}
|
|
112
|
-
<Tree app={app} />
|
|
113
|
-
</aside>
|
|
114
|
-
))
|
|
115
|
-
|
|
116
|
-
// ── the one picker, cmd+k: home and planned, the shown repo's targets, then
|
|
117
|
-
// every other repo ──────────────────────────────────────────────────────
|
|
118
|
-
const Palette = observer(({ app }: { app: HomeSt }) => {
|
|
119
|
-
const p = app.palette
|
|
120
|
-
if (!p.open) return null
|
|
121
|
-
return (
|
|
122
|
-
<div className="palov" onClick={() => p.close()}>
|
|
123
|
-
<div className="pal" onClick={(e) => e.stopPropagation()}>
|
|
124
|
-
<input autoFocus placeholder="jump to…" value={p.query}
|
|
125
|
-
onChange={(e) => p.setQuery(e.target.value)}
|
|
126
|
-
onKeyDown={(e) => {
|
|
127
|
-
if (e.key === 'ArrowDown') { e.preventDefault(); p.move(1) }
|
|
128
|
-
else if (e.key === 'ArrowUp') { e.preventDefault(); p.move(-1) }
|
|
129
|
-
else if (e.key === 'Enter') p.run()
|
|
130
|
-
else if (e.key === 'Escape') p.close()
|
|
131
|
-
}} />
|
|
132
|
-
<div className="prows">
|
|
133
|
-
{p.results.map((jp, n) => (
|
|
134
|
-
<div key={jp.id} className={`prow${n === p.idx ? ' on' : ''}`}
|
|
135
|
-
onMouseEnter={() => p.setIdx(n)}
|
|
136
|
-
onClick={() => p.run(jp)}>
|
|
137
|
-
<span className="plabel">{jp.label}</span>
|
|
138
|
-
<span className="pchip">{jp.section}</span>
|
|
139
|
-
</div>
|
|
140
|
-
))}
|
|
141
|
-
{p.results.length === 0 ? <div className="prow none">no match</div> : null}
|
|
142
|
-
</div>
|
|
143
|
-
</div>
|
|
144
|
-
</div>
|
|
145
|
-
)
|
|
146
|
-
})
|
|
147
|
-
|
|
148
48
|
// remark box on the ASSET view too: remarks land in .shipkit/assets/notes.md
|
|
149
49
|
// and come back as `remarks` (exchange both ways, media included)
|
|
150
50
|
const AssetRemarks = observer(({ app }: { app: RepoSt }) => {
|
|
@@ -186,6 +86,7 @@ const Main = observer(({ app }: { app: RepoSt }) => {
|
|
|
186
86
|
)
|
|
187
87
|
if (app.sel?.t === 'col') return <main>{errs}<ColGrid app={app} id={app.sel.id} /></main>
|
|
188
88
|
if (app.sel?.t === 'tests') return <main>{errs}<div className="stagewrap"><TestsPage app={app} check={app.sel.check} /></div></main>
|
|
89
|
+
if (app.sel?.t === 'admin') return <main>{errs}<div className="stagewrap"><AdminPage app={app} /></div></main>
|
|
189
90
|
if (app.sel?.t === 'status') return <main>{errs}<div className="stagewrap"><StatusCard app={app} /></div></main>
|
|
190
91
|
if (app.sel?.t === 'cat') return <main>{errs}<CatGrid app={app} /></main>
|
|
191
92
|
if (app.sel?.t === 'asset') return (
|
|
@@ -207,10 +108,10 @@ const Main = observer(({ app }: { app: RepoSt }) => {
|
|
|
207
108
|
return (
|
|
208
109
|
<main>
|
|
209
110
|
{errs}
|
|
210
|
-
<div className="stagewrap"
|
|
211
|
-
{allClear ?
|
|
212
|
-
:
|
|
213
|
-
</div
|
|
111
|
+
<div className="stagewrap">
|
|
112
|
+
{allClear ? <div className="ph">{app.filter === 'red' ? 'no reds — nothing is blocked ✨' : 'all clear — nothing needs you ✨'}</div>
|
|
113
|
+
: <ReadmeView app={app} />}
|
|
114
|
+
</div>
|
|
214
115
|
</main>
|
|
215
116
|
)
|
|
216
117
|
})
|
|
@@ -254,16 +155,6 @@ const Body = observer(({ app }: { app: RepoSt }) => {
|
|
|
254
155
|
)
|
|
255
156
|
})
|
|
256
157
|
|
|
257
|
-
/** the repo's hub has not answered on the home: its reason and its fix show
|
|
258
|
-
* instead of the page. A screen this browser kept shows meanwhile, until
|
|
259
|
-
* the machine says the hub is not up */
|
|
260
|
-
const notUp = (app: RepoSt): boolean => {
|
|
261
|
-
const m = app.home.machine
|
|
262
|
-
if (m == null || app.started) return false
|
|
263
|
-
if (!app.cols.cached) return true
|
|
264
|
-
return m.fresh && m.worker(app.key)?.status !== 'up'
|
|
265
|
-
}
|
|
266
|
-
|
|
267
158
|
/** one repo's page. Every repo visited keeps its region mounted, hidden while
|
|
268
159
|
* another one shows, so its scroll, its frames and its component state are
|
|
269
160
|
* there when it comes back; a hidden repo polls nothing, so it repaints
|
|
@@ -274,8 +165,8 @@ const RepoView = observer(({ app }: { app: RepoSt }) => {
|
|
|
274
165
|
<div className="repo" hidden={!on}>
|
|
275
166
|
{notUp(app) ? <NotUp app={app.home} /> : (
|
|
276
167
|
<>
|
|
277
|
-
|
|
278
|
-
<
|
|
168
|
+
{/* on the home the strip carries the repo's bar */}
|
|
169
|
+
{app.home.machine == null ? <HeaderBar app={app} /> : null}
|
|
279
170
|
<Body app={app} />
|
|
280
171
|
{on ? <ComfyOverlay app={app} /> : null}
|
|
281
172
|
</>
|
|
@@ -291,9 +182,10 @@ function onKey(e: KeyboardEvent): void {
|
|
|
291
182
|
if (pk != null) {
|
|
292
183
|
e.preventDefault()
|
|
293
184
|
if (pk.t === 'picker') home.palette.toggle()
|
|
294
|
-
else if (pk.t === '
|
|
185
|
+
else if (pk.t === 'dock') home.dock.cycle()
|
|
186
|
+
else if (pk.t === 'home' || pk.t === 'planned' || pk.t === 'activity') {
|
|
295
187
|
home.palette.close()
|
|
296
|
-
if (home.machine != null) home.navigate(pk.t === 'home' ? '
|
|
188
|
+
if (home.machine != null) home.navigate(PLACE_PATH[pk.t === 'home' ? 'overview' : pk.t])
|
|
297
189
|
} else {
|
|
298
190
|
const p = home.projects.list[pk.n - 1]
|
|
299
191
|
home.palette.close()
|
|
@@ -312,20 +204,25 @@ function onKey(e: KeyboardEvent): void {
|
|
|
312
204
|
e.preventDefault()
|
|
313
205
|
return
|
|
314
206
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
app.
|
|
207
|
+
const sk = sectionKey(e)
|
|
208
|
+
if (sk != null) {
|
|
209
|
+
const s = repoSections(app).find((x) => x.id === sk)
|
|
210
|
+
if (s != null) {
|
|
211
|
+
e.preventDefault()
|
|
212
|
+
s.go()
|
|
213
|
+
}
|
|
318
214
|
return
|
|
319
215
|
}
|
|
320
|
-
if (e
|
|
321
|
-
if (e.key === LIVE_KEY) {
|
|
216
|
+
if (isSearchKey(e)) {
|
|
322
217
|
e.preventDefault()
|
|
323
|
-
app.
|
|
218
|
+
app.searchEl?.focus()
|
|
324
219
|
return
|
|
325
220
|
}
|
|
326
|
-
if (e.
|
|
221
|
+
if (e.metaKey || e.ctrlKey || e.altKey) return
|
|
222
|
+
if (e.key === FOLLOW_KEY && (!app.live.chart.follow || !home.dock.chart.follow)) {
|
|
327
223
|
e.preventDefault()
|
|
328
224
|
app.live.chart.attach()
|
|
225
|
+
home.dock.chart.attach()
|
|
329
226
|
return
|
|
330
227
|
}
|
|
331
228
|
const f = FILTER_BY_KEY[e.key.toLowerCase()]
|
|
@@ -335,33 +232,72 @@ function onKey(e: KeyboardEvent): void {
|
|
|
335
232
|
}
|
|
336
233
|
}
|
|
337
234
|
|
|
235
|
+
/** a drag of a separator ended: the height of the panel below it, in px */
|
|
236
|
+
const belowPx = (el: HTMLElement | null, layout: Layout, id: string): number | null => {
|
|
237
|
+
const pct = layout[id]
|
|
238
|
+
const h = el?.getBoundingClientRect().height
|
|
239
|
+
return pct == null || h == null || h === 0 ? null : (pct / 100) * h
|
|
240
|
+
}
|
|
241
|
+
|
|
338
242
|
const App = observer(() => {
|
|
339
243
|
useEffect(() => {
|
|
340
244
|
document.addEventListener('keydown', onKey)
|
|
341
245
|
return () => document.removeEventListener('keydown', onKey)
|
|
342
246
|
}, [])
|
|
343
247
|
const l = home.layout
|
|
248
|
+
const d = home.dock
|
|
344
249
|
const el = useRef<HTMLDivElement | null>(null)
|
|
250
|
+
const vel = useRef<HTMLDivElement | null>(null)
|
|
251
|
+
const dockRef = usePanelRef()
|
|
252
|
+
// the dock's size moves the boundary, and only the boundary: its first
|
|
253
|
+
// height is the panel's default, read before the first render. After the
|
|
254
|
+
// render, so the panel already holds the bounds of its new size, and
|
|
255
|
+
// eased (dock-anim.ts), so the boundary slides rather than jumps
|
|
256
|
+
const px = d.px
|
|
257
|
+
useLayoutEffect(() => {
|
|
258
|
+
const p = dockRef.current
|
|
259
|
+
if (p != null && Math.abs(p.getSize().inPixels - px) >= 1) animateDock(vel.current, () => p.resize(px))
|
|
260
|
+
}, [px, dockRef])
|
|
345
261
|
return (
|
|
346
|
-
//
|
|
347
|
-
// and kept per browser, then the page
|
|
348
|
-
//
|
|
262
|
+
// the page over the dock: the project list, its width dragged by the
|
|
263
|
+
// separator and kept per browser, then the page; under both, the
|
|
264
|
+
// dock, on every page. The list, the strip and the dock stay mounted
|
|
265
|
+
// across pages; only the region under the strip changes
|
|
349
266
|
<>
|
|
350
|
-
<Group className="
|
|
267
|
+
<Group className="appv" orientation="vertical" elementRef={vel}
|
|
351
268
|
onLayoutChanged={(layout, meta) => {
|
|
352
|
-
if (!meta.isUserInteraction) return
|
|
353
|
-
const px =
|
|
354
|
-
if (px
|
|
269
|
+
if (!meta.isUserInteraction || d.size !== 'open') return
|
|
270
|
+
const px = belowPx(vel.current, layout, 'dock')
|
|
271
|
+
if (px == null) return
|
|
272
|
+
// a drag past a bound lands on it: the panel's own bounds
|
|
273
|
+
// stay 0 and the max, so the key can take it to any size
|
|
274
|
+
d.setHeight(px)
|
|
275
|
+
if (Math.abs(px - d.height) >= 1) dockRef.current?.resize(d.height)
|
|
355
276
|
}}>
|
|
356
|
-
<Panel id="
|
|
357
|
-
<
|
|
277
|
+
<Panel id="top" minSize={200} className="apptop" style={PANEL}>
|
|
278
|
+
<Group className="app" orientation="horizontal" elementRef={el}
|
|
279
|
+
onLayoutChanged={(layout, meta) => {
|
|
280
|
+
if (!meta.isUserInteraction) return
|
|
281
|
+
const px = leftPx(el.current, layout, 'projects')
|
|
282
|
+
if (px != null) l.setListWidth(px)
|
|
283
|
+
}}>
|
|
284
|
+
<Panel id="projects" defaultSize={l.listWidth} minSize={LIST.min} maxSize={LIST.max} groupResizeBehavior="preserve-pixel-size" className="pjpanel" style={PANEL}>
|
|
285
|
+
<ProjectList app={home} />
|
|
286
|
+
</Panel>
|
|
287
|
+
<Separator className="rsep" />
|
|
288
|
+
<Panel id="page" minSize={320} className="appcol" style={PANEL}>
|
|
289
|
+
{home.machine == null ? null : <HomeStrip app={home} />}
|
|
290
|
+
{home.page === 'overview' ? <Overview app={home} /> : null}
|
|
291
|
+
{home.page === 'planned' ? <PlannedView app={home} /> : null}
|
|
292
|
+
{home.page === 'activity' ? <ActivityView app={home} /> : null}
|
|
293
|
+
{home.page === 'archived' ? <ArchivedView app={home} /> : null}
|
|
294
|
+
{home.visited.map((r) => <RepoView key={r.key} app={r} />)}
|
|
295
|
+
</Panel>
|
|
296
|
+
</Group>
|
|
358
297
|
</Panel>
|
|
359
|
-
<Separator className=
|
|
360
|
-
<Panel id="
|
|
361
|
-
|
|
362
|
-
{home.page === 'overview' ? <Overview app={home} /> : null}
|
|
363
|
-
{home.page === 'planned' ? <PlannedView app={home} /> : null}
|
|
364
|
-
{home.visited.map((r) => <RepoView key={r.key} app={r} />)}
|
|
298
|
+
<Separator className={`rsep hsep${d.size === 'open' ? '' : ' fixed'}`} disabled={d.size !== 'open'} />
|
|
299
|
+
<Panel id="dock" panelRef={dockRef} defaultSize={d.px} minSize={0} maxSize={DOCK.max} groupResizeBehavior="preserve-pixel-size" className="pdock" style={PANEL}>
|
|
300
|
+
<Dock app={home} />
|
|
365
301
|
</Panel>
|
|
366
302
|
</Group>
|
|
367
303
|
<Palette app={home} />
|
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
action, and red/orange/green reserved for STATE. No rule outside this
|
|
5
5
|
block writes a colour, and tests/hub-css-invariants.test.ts fails one
|
|
6
6
|
that does. */
|
|
7
|
-
:root{--bg:#0d0e10;--panel:#141619
|
|
7
|
+
:root{--bg:#0d0e10;--panel:#141619;
|
|
8
|
+
/* the surfaces of the regions, each a small step from the page so the rail,
|
|
9
|
+
the header and the dock read apart at a glance (no light theme: the page
|
|
10
|
+
is dark only, and colorDark is what it draws) */
|
|
11
|
+
--bg-side:#131519;--bg-top:#171a1f;--bg-dock:#10151b;--panel2:#1b1e22;--line:#282c31;--ink:#e2e4e7;--dim:#8d9299;
|
|
8
12
|
--accent:#6b8fb3;--sel:#1a2430;--chk1:#16181b;--chk2:#121417;
|
|
9
13
|
/* state, and nothing else: red broken · orange taste pending · green measured passing */
|
|
10
14
|
--red:#d2605c;--warn:#cf9a4e;--ok:#6faa86;--blue:#6f9bd1;
|
|
@@ -16,16 +20,23 @@
|
|
|
16
20
|
--k1:#8ca8c7;--k2:#a892c9;--k3:#6fb0b8;--k4:#c792b4;--k5:#9198a1;--k6:#8f95cf;
|
|
17
21
|
--wash:rgba(107,143,179,.15);--wash2:rgba(107,143,179,.11);--hair:rgba(107,143,179,.5);
|
|
18
22
|
--errbg:#2a1a1a;--errline:#4a2c2c;--errink:#e3bebe;
|
|
23
|
+
/* a running thing's wash, the green twin of errbg and errline */
|
|
24
|
+
--okbg:#15251c;--okline:#2f5a40;
|
|
19
25
|
/* the ONE spacing scale: every padding, margin and gap in this sheet is a
|
|
20
26
|
value from it or 0, and tests/hub-css-invariants.test.ts fails the rest */
|
|
21
27
|
--s0:2px;--s1:4px;--s2:8px;--s3:12px;--s4:16px}
|
|
22
28
|
*{box-sizing:border-box}html,body,#root{height:100%}
|
|
23
29
|
body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.5 system-ui,sans-serif;overflow:hidden}
|
|
24
|
-
/*
|
|
25
|
-
|
|
26
|
-
.
|
|
30
|
+
/* a link with no class of its own: the browser default is dark blue on this dark page */
|
|
31
|
+
a,a:visited{color:var(--accent)}
|
|
32
|
+
/* the page is the window's height, the dock under it (hub-app.tsx): over
|
|
33
|
+
the dock two panels, the project list, then .appcol, which stacks the
|
|
34
|
+
strip, the header and the body */
|
|
35
|
+
.appv{height:100vh}
|
|
36
|
+
.app{height:100%}
|
|
27
37
|
/* a panel's class lands on the div inside the panel (react-resizable-panels) */
|
|
28
38
|
.pjpanel,.appcol,.pside,.pmain{height:100%;min-width:0}
|
|
39
|
+
.apptop,.pdock{height:100%;min-height:0}
|
|
29
40
|
.pjpanel{display:flex}
|
|
30
41
|
.appcol{display:flex;flex-direction:column}
|
|
31
42
|
/* one region per repo visited, the shown one alone displayed */
|
|
@@ -37,7 +48,11 @@ body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.5 system-ui,sans
|
|
|
37
48
|
.pmain{display:flex;flex-direction:column}
|
|
38
49
|
.pmain > main{flex:1}
|
|
39
50
|
|
|
40
|
-
/* ──
|
|
51
|
+
/* ── a repo's bar: on the home the strip (home-strip.css .hstrip), on a hub
|
|
52
|
+
of its own the header (.hdr). Its name, what runs now and the two runs,
|
|
53
|
+
then its processes, its trouble and its editor icons. One line at any
|
|
54
|
+
width: the container queries shrink the labels to their icons, then the
|
|
55
|
+
run state to its spinner ── */
|
|
41
56
|
/* the repo's processes (serve.processes): a dot, its id, a link once up */
|
|
42
57
|
.hprocs{display:inline-flex;gap:var(--s1);align-items:center}
|
|
43
58
|
.hproc{display:inline-flex;align-items:center;gap:var(--s0)}
|
|
@@ -45,45 +60,51 @@ body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.5 system-ui,sans
|
|
|
45
60
|
.hproc a{color:var(--accent);text-decoration:none;font-size:12px}
|
|
46
61
|
.pdot{display:inline-block;width:7px;height:7px;border-radius:4px;margin-right:var(--s1);background:var(--line)}
|
|
47
62
|
.pdot.up{background:var(--ok)}.pdot.starting,.pdot.stopping{background:var(--warn)}.pdot.crashed{background:var(--red)}
|
|
48
|
-
.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(--
|
|
63
|
+
.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)}
|
|
49
64
|
.hdr .sp{flex:1}
|
|
50
65
|
.hname{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink);font-weight:600;margin-right:var(--s2);white-space:nowrap}
|
|
51
|
-
.
|
|
52
|
-
.
|
|
53
|
-
.
|
|
54
|
-
.
|
|
66
|
+
.hdr,.hstrip{container-type:inline-size;white-space:nowrap}
|
|
67
|
+
.hrun{display:inline-flex;align-items:center;gap:var(--s1);flex:none;margin-left:var(--s2)}
|
|
68
|
+
.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}
|
|
69
|
+
.hbtn .hico{width:13px;height:13px}
|
|
70
|
+
/* the editor icons: bare, dim, lit on hover, as the activity page's Fork icon */
|
|
71
|
+
.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)}
|
|
72
|
+
.hicon:hover{color:var(--accent)}
|
|
73
|
+
.hicon .hico{width:15px;height:15px}
|
|
55
74
|
/* the run state holds its width whatever it says, so a run never moves the buttons beside it */
|
|
56
75
|
.hstatus{max-width:420px;flex:0 1 auto;font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
57
76
|
.hstatus.red{color:var(--red)}
|
|
58
77
|
.hstatus.warn{color:var(--warn)}
|
|
59
|
-
|
|
78
|
+
/* its words sit against the run buttons, cut with an ellipsis */
|
|
79
|
+
.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}
|
|
60
80
|
.hstate.on{color:var(--ink)}
|
|
61
|
-
.hstate .htext{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
62
|
-
.hstate .spin.idle
|
|
81
|
+
.hstate .htext{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
82
|
+
.hstate .spin.idle{visibility:hidden;animation:none}
|
|
83
|
+
/* after .hstate, so a narrow bar wins over its width */
|
|
84
|
+
@container (max-width:600px){.hbtn .lbl{display:none}.hbtn{padding:0 var(--s1)}}
|
|
85
|
+
@container (max-width:520px){.hstate{width:120px;min-width:120px}}
|
|
86
|
+
@container (max-width:420px){.hstate .htext{display:none}.hstate{width:16px;min-width:16px}}
|
|
63
87
|
|
|
64
|
-
/*
|
|
65
|
-
whatever runs; unfolded, the chart at the height its lanes hold, set
|
|
66
|
-
inline from a held value that grows at once and shrinks only after a
|
|
67
|
-
while, so it never jumps every second. Its (?) and the chart tooltip float
|
|
68
|
-
in a portal (floating.tsx), so no box of the strip clips them ── */
|
|
69
|
-
.live{display:flex;flex-direction:column;height:30px;flex:none;border-bottom:1px solid var(--line);background:var(--bg);font-size:11px;color:var(--dim)}
|
|
88
|
+
/* a row of a (?) legend: one glyph or word, then its meaning */
|
|
70
89
|
.lrow{display:flex;gap:var(--s2);align-items:center;height:29px;flex:none;padding:0 var(--s3)}
|
|
71
|
-
.lrow .sp{flex:1}
|
|
72
|
-
.lfold{display:inline-flex;gap:var(--s1);align-items:center;border-color:transparent;color:var(--dim);padding:var(--s0) var(--s1)}
|
|
73
|
-
.lfold .sk{font-size:9px;opacity:.55}
|
|
74
|
-
.lwho{width:180px;flex:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ink)}
|
|
75
|
-
.live.idle .lwho{color:var(--dim)}
|
|
76
|
-
.lwho i{font-style:normal;color:var(--dim);margin-left:var(--s1)}
|
|
77
|
-
.lwho b{font-weight:500;color:var(--accent);margin-left:var(--s1)}
|
|
78
|
-
.lchart{flex:1;min-width:0;height:21px;border:1px solid var(--line);border-radius:3px;overflow:hidden}
|
|
79
|
-
.lopen{flex:1;min-height:0;overflow-y:auto;padding:0 var(--s3);scrollbar-width:thin;scrollbar-color:var(--line) transparent}
|
|
80
|
-
.lnote,.lmore{flex:none;color:var(--warn);white-space:nowrap}
|
|
81
|
-
.lbusy{flex:none;white-space:nowrap}
|
|
82
90
|
|
|
83
|
-
/* ──
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
91
|
+
/* ── the left pane: the sections, the filter, then the tree, the one part
|
|
92
|
+
that scrolls ── */
|
|
93
|
+
aside{border-right:1px solid var(--line);background:var(--panel);min-height:0;display:flex;flex-direction:column;overflow:hidden}
|
|
94
|
+
aside > *{flex:none}
|
|
95
|
+
aside > .tscroll{flex:1;min-height:0;overflow-y:auto}
|
|
96
|
+
/* a repo's sections: the project list's lines (project-list.css .pjpl), a
|
|
97
|
+
little shorter, the current one lit and marked in the accent */
|
|
98
|
+
.rsecs{display:flex;flex-direction:column;padding:var(--s1) 0;border-bottom:1px solid var(--line)}
|
|
99
|
+
.rsec{display:flex;align-items:center;gap:var(--s2);width:100%;height:26px;padding:0 var(--s3);background:transparent;border:0;border-radius:0;text-align:left;color:var(--dim);font-size:13px;cursor:pointer}
|
|
100
|
+
.rsec:hover{background:var(--panel2);color:var(--ink);border-color:transparent}
|
|
101
|
+
.rsec.cur{background:var(--sel);color:var(--ink);box-shadow:inset 3px 0 0 var(--accent);font-weight:600}
|
|
102
|
+
.rsec .hico{flex:none;width:16px;height:16px}
|
|
103
|
+
.rsecl{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
104
|
+
.rsecn{flex:none;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)}
|
|
105
|
+
.rsecn.nred{background:var(--red)}
|
|
106
|
+
.rsecn.nwarn{background:var(--warn)}
|
|
107
|
+
.rsec:hover .pjkey,.rsec.cur .pjkey{opacity:1}
|
|
87
108
|
/* segmented severity filter — all · needs · red. A thin border and an
|
|
88
109
|
underline on the live one: no fill, no pill */
|
|
89
110
|
.seg{display:inline-flex;border:1px solid var(--line);border-radius:3px;overflow:hidden;flex:none}
|
|
@@ -94,10 +115,16 @@ aside{border-right:1px solid var(--line);background:var(--panel);overflow-y:auto
|
|
|
94
115
|
.seg button.on{color:var(--accent);border-bottom-color:var(--accent)}
|
|
95
116
|
.seg button .sk{margin-left:var(--s1);font-size:9px;opacity:.55}
|
|
96
117
|
.swhen{padding:0 var(--s3) var(--s2);font-size:11px;color:var(--dim)}
|
|
97
|
-
|
|
118
|
+
/* a narrow pane puts the severity filter under the input, never over it */
|
|
119
|
+
.ssearch{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2);padding:var(--s2) var(--s3)}
|
|
120
|
+
.sfield{position:relative;flex:1 1 96px;min-width:0;display:flex}
|
|
98
121
|
.ssearch input{width:100%;background:var(--bg);border:1px solid var(--line);border-radius:4px;color:var(--ink);
|
|
99
|
-
padding:var(--
|
|
122
|
+
padding:var(--s0) var(--s4) var(--s0) var(--s2);font-size:12px;outline:none}
|
|
123
|
+
.ssearch input::placeholder{color:var(--dim);opacity:.6;font-size:11.5px}
|
|
100
124
|
.ssearch input:focus{border-color:var(--accent)}
|
|
125
|
+
/* its key, the way the project list prints its keys (.pjkey) */
|
|
126
|
+
.skey{position:absolute;right:var(--s2);top:50%;transform:translateY(-50%);pointer-events:none;font:10px/1 ui-monospace,Menlo,monospace;color:var(--dim);opacity:.6;background:none;border:0;padding:0}
|
|
127
|
+
.sfield input:focus + .skey{opacity:0}
|
|
101
128
|
/* ── the resource tree: ONE row shape, a fixed grid. The indent rides in
|
|
102
129
|
the FIRST column and the label eats the slack, so every right side slot
|
|
103
130
|
lines up down the whole column whatever the depth. Colour is on the
|
|
@@ -390,7 +417,6 @@ kbd{background:var(--panel2);border:1px solid var(--line);border-radius:4px;padd
|
|
|
390
417
|
.tlb.bad,.tlb.red{fill:var(--red)}
|
|
391
418
|
.tlb.rep{fill:var(--line)}
|
|
392
419
|
.tlb.run{fill:var(--accent);animation:pulse 1.6s ease-in-out infinite}
|
|
393
|
-
.tlb.repo{fill:var(--k6)}
|
|
394
420
|
.tlband.run{stroke:var(--accent)}
|
|
395
421
|
.tlconc{fill:var(--hair)}
|
|
396
422
|
.tlconc.over{fill:var(--warn)}
|
|
@@ -439,6 +465,8 @@ kbd{background:var(--panel2);border:1px solid var(--line);border-radius:4px;padd
|
|
|
439
465
|
per test check (the tests page), the failing files with their failure line */
|
|
440
466
|
.rtime{grid-template-columns:minmax(0,1fr) 88px 72px 128px 72px 40px 88px}
|
|
441
467
|
.rcheckt{grid-template-columns:152px 88px 48px 56px minmax(0,1fr) 72px}
|
|
468
|
+
/* every test file: file, check, duration, status, age */
|
|
469
|
+
.rall{grid-template-columns:minmax(0,1fr) 152px 88px 52px 72px}
|
|
442
470
|
.rhead .tleft{text-align:left}
|
|
443
471
|
.tshare{display:grid;grid-template-columns:minmax(0,1fr) 36px;gap:var(--s1);align-items:center}
|
|
444
472
|
.ttrack{display:block;height:6px;border-radius:1px;overflow:hidden;background:var(--panel2)}
|
|
@@ -678,6 +706,9 @@ a.mint:hover{text-decoration:underline}
|
|
|
678
706
|
.prow.on{background:var(--panel2)}
|
|
679
707
|
.prow.none{color:var(--dim);cursor:default}
|
|
680
708
|
.prow .plabel{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
709
|
+
/* the entry's icon: a place's button icon, a repo's own (project-list.tsx Face) */
|
|
710
|
+
.prow .pface{flex:none;display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;color:var(--dim)}
|
|
711
|
+
.prow .pface img,.prow .pface .pgi{width:16px;height:16px;border-radius:4px;object-fit:contain}
|
|
681
712
|
.prow .pchip{font-size:10px;color:var(--dim);background:var(--bg);border:1px solid var(--line);border-radius:5px;
|
|
682
713
|
padding:var(--s0) var(--s1);text-transform:uppercase;letter-spacing:.08em;flex:none}
|
|
683
714
|
|
|
@@ -689,3 +720,12 @@ a.mint:hover{text-decoration:underline}
|
|
|
689
720
|
pre{background:var(--panel);border:1px solid var(--line);border-radius:3px;padding:var(--s4);overflow-x:auto;
|
|
690
721
|
font:12.5px/1.55 ui-monospace,Menlo,monospace;white-space:pre-wrap}
|
|
691
722
|
.ok{color:var(--ok)}.warn{color:var(--warn)}.fail{color:var(--red)}.dim{color:var(--dim)}
|
|
723
|
+
|
|
724
|
+
/* ── the admin page: one row per control, its sentence under its name ── */
|
|
725
|
+
.adrow{display:flex;align-items:center;gap:var(--s4);padding:var(--s3) 0;border-top:1px solid var(--line)}
|
|
726
|
+
.adtext{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--s0);font-size:13px}
|
|
727
|
+
.adtext .dim{font-size:12px}
|
|
728
|
+
.adctl{flex:none;display:flex;align-items:center}
|
|
729
|
+
.adflag{display:inline-flex;align-items:center;gap:var(--s1);font-size:12px;color:var(--dim)}
|
|
730
|
+
.adflag input{width:auto;margin:0}
|
|
731
|
+
.adconfirm{display:inline-flex;align-items:center;gap:var(--s2);font-size:12px}
|
|
@@ -1,12 +1,32 @@
|
|
|
1
|
-
// the few line icons of the home (the overview's section headers, the
|
|
2
|
-
//
|
|
3
|
-
// the page's tokens colour them
|
|
1
|
+
// the few line icons of the home (the overview's section headers, the places
|
|
2
|
+
// on top of the project list, a repo page's sections and its top bar): one 20
|
|
3
|
+
// unit path each, stroked in the text colour, so the page's tokens colour them
|
|
4
|
+
// and nothing else does.
|
|
4
5
|
export const ICON = {
|
|
5
6
|
shipkit: 'M3 7.5 10 4l7 3.5v5L10 16l-7-3.5zM3 7.5 10 11l7-3.5M10 11v5',
|
|
6
7
|
process: 'M2 10h3.5l2-5 5 10 2-5H18',
|
|
7
8
|
monitor: 'M3 4h14v9H3zM7 16.5h6M10 13v3.5',
|
|
9
|
+
archived: 'M2.5 4h15v3.5h-15zM4 7.5V16h12V7.5M8 10.5h4',
|
|
8
10
|
home: 'M3 9.5 10 3.5l7 6M5 8v8.5h10V8M8.5 16.5v-4.5h3v4.5',
|
|
9
11
|
planned: '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',
|
|
12
|
+
browser: 'M10 2.5a7.5 7.5 0 1 0 0 15a7.5 7.5 0 1 0 0-15M2.5 10h15M10 2.5c-3 3-3 12 0 15M10 2.5c3 3 3 12 0 15',
|
|
13
|
+
window: 'M2.5 4h15v12h-15zM2.5 7.5h15M5 5.75h.01M7 5.75h.01',
|
|
14
|
+
activity: 'M6 3v14M6 6.5a2 2 0 1 0 0 .01M6 13.5a2 2 0 1 0 0 .01M10 6.5h7M10 13.5h7M10 10h5',
|
|
15
|
+
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
|
+
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
|
+
// a repo page's sections, then its two runs and its editor
|
|
18
|
+
checks: 'M4 10.5 7.5 14 16 5.5',
|
|
19
|
+
runs: 'M3 5h8M3 10h12M3 15h6',
|
|
20
|
+
asks: 'M3 4h14v9H8l-4 3.5V13H3z',
|
|
21
|
+
tests: 'M8 3h4M8.5 3v5L4 16.5h12L11.5 8V3M6.2 12.5h7.6',
|
|
22
|
+
media: 'M3 4h14v12H3zM3 13l4-4 4 4 2-2 4 4M13 7.5h.01',
|
|
23
|
+
wikis: 'M10 5.5C8.5 4 6 3.5 3 4v11c3-.5 5.5 0 7 1.5c1.5-1.5 4-2 7-1.5V4c-3-.5-5.5 0-7 1.5zM10 5.5v11',
|
|
24
|
+
admin: 'M4 5h12M4 10h12M4 15h12M7 3.5v3M13 8.5v3M9 13.5v3',
|
|
25
|
+
health: 'M2 10h3.5l2-5 5 10 2-5H18',
|
|
26
|
+
gate: 'M10 2.5 16 5v4.5c0 4-2.7 6.5-6 8c-3.3-1.5-6-4-6-8V5zM7.5 10l2 2 3.5-3.5',
|
|
27
|
+
// the VS Code mark, its ribbon drawn as one outline
|
|
28
|
+
vscode: 'M14 2.5l3.5 1.7v11.6L14 17.5zM14 2.5 4.5 12.3 2.5 11V9l2-1.3L14 17.5M14 6.8 10.2 10l3.8 3.2',
|
|
29
|
+
sort: 'M6 3.5v13M3 13.5l3 3 3-3M14 16.5v-13M11 6.5l3-3 3 3',
|
|
10
30
|
} as const
|
|
11
31
|
|
|
12
32
|
export const Icon = ({ d }: { d: string }) => (
|
|
@@ -9,6 +9,10 @@
|
|
|
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="./activity.css" />
|
|
13
|
+
<link rel="stylesheet" href="./readme.css" />
|
|
14
|
+
<link rel="stylesheet" href="./dock.css" />
|
|
15
|
+
<link rel="stylesheet" href="./controls.css" />
|
|
12
16
|
</head>
|
|
13
17
|
<body>
|
|
14
18
|
<div id="root"></div>
|