rvlib-shipkit 1.2.0 → 1.4.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 +104 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/asks/asks-queue.ts +4 -2
- package/src/assets/kits/identity-kit.ts +15 -13
- package/src/assets/run-assets.ts +15 -10
- package/src/check.ts +26 -10
- package/src/checks/SK001-package-json-shape.check.ts +34 -5
- 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 +103 -61
- package/src/checks/SK013-one-way-in-policy.check.ts +10 -2
- package/src/checks/SK014-prose-ste.check.ts +16 -6
- package/src/checks/SK015-nested-repo.check.ts +5 -1
- package/src/checks/SK016-legacy-check-shapes.check.ts +11 -5
- 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 +50 -0
- package/src/checks/SK028-readme.check.ts +83 -0
- package/src/cli/cmd_Assets.ts +1 -1
- package/src/cli/cmd_Ci.ts +5 -0
- package/src/cli/cmd_CrossRepoActivity.ts +119 -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/collection-types.ts +15 -0
- package/src/collections/providers.ts +7 -3
- package/src/collections/test-time.ts +10 -1
- package/src/contracts/apply-contracts.ts +9 -8
- package/src/contracts/contract-types.ts +8 -2
- package/src/contracts/contracts-engine.ts +34 -13
- package/src/contracts/manifests/identity.contract.ts +6 -5
- package/src/contracts/manifests/social.contract.ts +4 -2
- package/src/contracts/manifests/steam.contract.ts +7 -3
- package/src/contracts/web-surface.ts +59 -0
- package/src/core/agent-spawn.ts +46 -0
- package/src/core/archived.ts +82 -0
- package/src/core/budget.ts +19 -9
- package/src/core/canonical-scripts.ts +12 -3
- package/src/core/check-runner.ts +66 -17
- package/src/core/discover-checks.ts +10 -5
- 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/layout.ts +2 -0
- package/src/core/motherhome.ts +88 -66
- 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 +8 -7
- package/src/core/run-ci.ts +46 -15
- package/src/core/run-fix.ts +7 -2
- package/src/core/run-fixers.ts +10 -6
- package/src/core/wire.ts +49 -9
- package/src/fleet/scan-fleet.ts +2 -1
- package/src/home/activity-shape.ts +138 -0
- package/src/home/activity.ts +256 -0
- package/src/home/global-units.ts +3 -2
- package/src/home/gui-app/ShipkitGui.swift +144 -25
- package/src/home/gui-app.ts +93 -30
- package/src/home/home-launchd.ts +10 -2
- package/src/home/home-routes.ts +34 -6
- package/src/home/home-start.ts +33 -5
- package/src/home/machine-stores.ts +4 -2
- package/src/home/open-app.ts +23 -10
- package/src/home/pm-config.ts +15 -11
- package/src/home/pm-inputs.ts +76 -0
- package/src/home/pm.ts +40 -7
- package/src/home/process-rows.ts +222 -0
- package/src/home/resources.ts +99 -20
- package/src/home/run-daemon.ts +12 -7
- package/src/home/run-pm.ts +30 -5
- 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/home/widgets.ts +32 -3
- package/src/hooks/commit-message.ts +2 -3
- package/src/hooks/run-hooks.ts +32 -5
- package/src/ledger/db.ts +1 -1
- package/src/ledger/gate-checks.ts +20 -6
- 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 +30 -2
- package/src/ledger/world-cache.ts +63 -14
- 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 +17 -4
- 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/checks-svc.ts +2 -1
- package/src/serve/hub-app/col-tables-view.tsx +124 -0
- package/src/serve/hub-app/col-tables.ts +115 -0
- package/src/serve/hub-app/cols-svc.ts +28 -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 +50 -13
- package/src/serve/hub-app/home-strip.css +81 -23
- package/src/serve/hub-app/home-strip.tsx +243 -102
- package/src/serve/hub-app/home.html +4 -0
- package/src/serve/hub-app/hub-app.tsx +103 -161
- package/src/serve/hub-app/hub-types.ts +6 -3
- package/src/serve/hub-app/hub.css +101 -39
- package/src/serve/hub-app/icons.tsx +27 -3
- package/src/serve/hub-app/index.html +4 -0
- package/src/serve/hub-app/keys.ts +44 -9
- package/src/serve/hub-app/live-svc.ts +24 -64
- package/src/serve/hub-app/machine-st.ts +65 -13
- 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 +13 -9
- 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 +26 -7
- 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 +29 -9
- 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 +34 -13
- 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/serve/warm-cell.ts +49 -16
- package/src/shipkit-files.ts +20 -4
- package/src/suite/judge-suites.ts +42 -17
- package/src/suite/suite-cli.ts +5 -8
- package/src/wiki/lint-md.ts +2 -0
- package/src/wiki/scan-md.ts +37 -10
- package/src/checks/SK023-stores-under-cap.check.ts +0 -49
- package/src/checks/contracts.check.ts +0 -20
|
@@ -1,37 +1,44 @@
|
|
|
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
|
+
import { AskTable, CheckTable } from './col-tables-view.tsx'
|
|
14
15
|
import { WIKIS, WikisView } from './wikis-view.tsx'
|
|
15
16
|
import { ComfyOverlay, ComfyView } from './comfy-view.tsx'
|
|
16
17
|
import type { Filter, RepoSt } from './repo-st.ts'
|
|
17
|
-
import { HomeSt } from './home-st.ts'
|
|
18
|
+
import { HomeSt, PLACE_PATH } from './home-st.ts'
|
|
18
19
|
import { TimelineView } from './timeline-view.tsx'
|
|
19
|
-
import { HeaderBar,
|
|
20
|
+
import { HeaderBar, notUp } from './header-bar.tsx'
|
|
20
21
|
import { FOLLOW_KEY } from './timeline-chart.tsx'
|
|
21
22
|
import { CheckCard } from './check-view.tsx'
|
|
22
23
|
import { TestCard } from './test-view.tsx'
|
|
23
24
|
import { TestsPage } from './tests-view.tsx'
|
|
24
25
|
import { AssetStage, CatGrid, InfoChips, MediaTopbar, ToolsBar, VariantsBar } from './media-view.tsx'
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
26
|
+
import { Sidebar } from './sidebar.tsx'
|
|
27
|
+
import { AdminPage } from './admin-view.tsx'
|
|
28
|
+
import { ReadmeView } from './readme-view.tsx'
|
|
28
29
|
import { ProjectList } from './project-list.tsx'
|
|
29
|
-
import { Group, Panel, Separator, type Layout } from 'react-resizable-panels'
|
|
30
|
+
import { Group, Panel, Separator, usePanelRef, type Layout } from 'react-resizable-panels'
|
|
30
31
|
import { LIST, SIDEBAR } from './layout-st.ts'
|
|
31
|
-
import { HomeStrip, NotUp, Overview } from './home-strip.tsx'
|
|
32
|
+
import { ArchivedView, HomeStrip, NotUp, Overview } from './home-strip.tsx'
|
|
32
33
|
import { PlannedView } from './planned-view.tsx'
|
|
34
|
+
import { ActivityView } from './activity-view.tsx'
|
|
35
|
+
import { Palette } from './palette-view.tsx'
|
|
33
36
|
import { SCREEN_WAIT_MS } from './screen-cache.ts'
|
|
34
|
-
import { pageKey } from './keys.ts'
|
|
37
|
+
import { isSearchKey, pageKey, sectionKey } from './keys.ts'
|
|
38
|
+
import { repoSections } from './repo-sections.tsx'
|
|
39
|
+
import { Dock } from './dock-view.tsx'
|
|
40
|
+
import { DOCK } from './dock-st.ts'
|
|
41
|
+
import { animateDock } from './dock-anim.ts'
|
|
35
42
|
|
|
36
43
|
// home.html marks the home's page; index.html is a hub serving on its own
|
|
37
44
|
const home = new HomeSt({ home: document.body.dataset.home != null })
|
|
@@ -39,112 +46,6 @@ const home = new HomeSt({ home: document.body.dataset.home != null })
|
|
|
39
46
|
;(window as unknown as { app: HomeSt }).app = home
|
|
40
47
|
home.boot()
|
|
41
48
|
|
|
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
49
|
// remark box on the ASSET view too: remarks land in .shipkit/assets/notes.md
|
|
149
50
|
// and come back as `remarks` (exchange both ways, media included)
|
|
150
51
|
const AssetRemarks = observer(({ app }: { app: RepoSt }) => {
|
|
@@ -184,8 +85,14 @@ const Main = observer(({ app }: { app: RepoSt }) => {
|
|
|
184
85
|
: <div className="stagewrap"><ColCard app={app} col={app.sel.col} id={app.sel.id} /></div>}
|
|
185
86
|
</main>
|
|
186
87
|
)
|
|
187
|
-
if (app.sel?.t === 'col') return
|
|
88
|
+
if (app.sel?.t === 'col') return (
|
|
89
|
+
<main>
|
|
90
|
+
{errs}
|
|
91
|
+
{app.sel.id === 'checks' ? <CheckTable app={app} id={app.sel.id} /> : app.sel.id === 'asks' ? <AskTable app={app} id={app.sel.id} /> : <ColGrid app={app} id={app.sel.id} />}
|
|
92
|
+
</main>
|
|
93
|
+
)
|
|
188
94
|
if (app.sel?.t === 'tests') return <main>{errs}<div className="stagewrap"><TestsPage app={app} check={app.sel.check} /></div></main>
|
|
95
|
+
if (app.sel?.t === 'admin') return <main>{errs}<div className="stagewrap"><AdminPage app={app} /></div></main>
|
|
189
96
|
if (app.sel?.t === 'status') return <main>{errs}<div className="stagewrap"><StatusCard app={app} /></div></main>
|
|
190
97
|
if (app.sel?.t === 'cat') return <main>{errs}<CatGrid app={app} /></main>
|
|
191
98
|
if (app.sel?.t === 'asset') return (
|
|
@@ -207,10 +114,10 @@ const Main = observer(({ app }: { app: RepoSt }) => {
|
|
|
207
114
|
return (
|
|
208
115
|
<main>
|
|
209
116
|
{errs}
|
|
210
|
-
<div className="stagewrap"
|
|
211
|
-
{allClear ?
|
|
212
|
-
:
|
|
213
|
-
</div
|
|
117
|
+
<div className="stagewrap">
|
|
118
|
+
{allClear ? <div className="ph">{app.filter === 'red' ? 'no reds — nothing is blocked ✨' : 'all clear — nothing needs you ✨'}</div>
|
|
119
|
+
: <ReadmeView app={app} />}
|
|
120
|
+
</div>
|
|
214
121
|
</main>
|
|
215
122
|
)
|
|
216
123
|
})
|
|
@@ -254,16 +161,6 @@ const Body = observer(({ app }: { app: RepoSt }) => {
|
|
|
254
161
|
)
|
|
255
162
|
})
|
|
256
163
|
|
|
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
164
|
/** one repo's page. Every repo visited keeps its region mounted, hidden while
|
|
268
165
|
* another one shows, so its scroll, its frames and its component state are
|
|
269
166
|
* there when it comes back; a hidden repo polls nothing, so it repaints
|
|
@@ -274,8 +171,8 @@ const RepoView = observer(({ app }: { app: RepoSt }) => {
|
|
|
274
171
|
<div className="repo" hidden={!on}>
|
|
275
172
|
{notUp(app) ? <NotUp app={app.home} /> : (
|
|
276
173
|
<>
|
|
277
|
-
|
|
278
|
-
<
|
|
174
|
+
{/* on the home the strip carries the repo's bar */}
|
|
175
|
+
{app.home.machine == null ? <HeaderBar app={app} /> : null}
|
|
279
176
|
<Body app={app} />
|
|
280
177
|
{on ? <ComfyOverlay app={app} /> : null}
|
|
281
178
|
</>
|
|
@@ -291,9 +188,10 @@ function onKey(e: KeyboardEvent): void {
|
|
|
291
188
|
if (pk != null) {
|
|
292
189
|
e.preventDefault()
|
|
293
190
|
if (pk.t === 'picker') home.palette.toggle()
|
|
294
|
-
else if (pk.t === '
|
|
191
|
+
else if (pk.t === 'dock') home.dock.cycle()
|
|
192
|
+
else if (pk.t === 'home' || pk.t === 'planned' || pk.t === 'activity') {
|
|
295
193
|
home.palette.close()
|
|
296
|
-
if (home.machine != null) home.navigate(pk.t === 'home' ? '
|
|
194
|
+
if (home.machine != null) home.navigate(PLACE_PATH[pk.t === 'home' ? 'overview' : pk.t])
|
|
297
195
|
} else {
|
|
298
196
|
const p = home.projects.list[pk.n - 1]
|
|
299
197
|
home.palette.close()
|
|
@@ -312,20 +210,25 @@ function onKey(e: KeyboardEvent): void {
|
|
|
312
210
|
e.preventDefault()
|
|
313
211
|
return
|
|
314
212
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
app.
|
|
213
|
+
const sk = sectionKey(e)
|
|
214
|
+
if (sk != null) {
|
|
215
|
+
const s = repoSections(app).find((x) => x.id === sk)
|
|
216
|
+
if (s != null) {
|
|
217
|
+
e.preventDefault()
|
|
218
|
+
s.go()
|
|
219
|
+
}
|
|
318
220
|
return
|
|
319
221
|
}
|
|
320
|
-
if (e
|
|
321
|
-
if (e.key === LIVE_KEY) {
|
|
222
|
+
if (isSearchKey(e)) {
|
|
322
223
|
e.preventDefault()
|
|
323
|
-
app.
|
|
224
|
+
app.searchEl?.focus()
|
|
324
225
|
return
|
|
325
226
|
}
|
|
326
|
-
if (e.
|
|
227
|
+
if (e.metaKey || e.ctrlKey || e.altKey) return
|
|
228
|
+
if (e.key === FOLLOW_KEY && (!app.live.chart.follow || !home.dock.chart.follow)) {
|
|
327
229
|
e.preventDefault()
|
|
328
230
|
app.live.chart.attach()
|
|
231
|
+
home.dock.chart.attach()
|
|
329
232
|
return
|
|
330
233
|
}
|
|
331
234
|
const f = FILTER_BY_KEY[e.key.toLowerCase()]
|
|
@@ -335,33 +238,72 @@ function onKey(e: KeyboardEvent): void {
|
|
|
335
238
|
}
|
|
336
239
|
}
|
|
337
240
|
|
|
241
|
+
/** a drag of a separator ended: the height of the panel below it, in px */
|
|
242
|
+
const belowPx = (el: HTMLElement | null, layout: Layout, id: string): number | null => {
|
|
243
|
+
const pct = layout[id]
|
|
244
|
+
const h = el?.getBoundingClientRect().height
|
|
245
|
+
return pct == null || h == null || h === 0 ? null : (pct / 100) * h
|
|
246
|
+
}
|
|
247
|
+
|
|
338
248
|
const App = observer(() => {
|
|
339
249
|
useEffect(() => {
|
|
340
250
|
document.addEventListener('keydown', onKey)
|
|
341
251
|
return () => document.removeEventListener('keydown', onKey)
|
|
342
252
|
}, [])
|
|
343
253
|
const l = home.layout
|
|
254
|
+
const d = home.dock
|
|
344
255
|
const el = useRef<HTMLDivElement | null>(null)
|
|
256
|
+
const vel = useRef<HTMLDivElement | null>(null)
|
|
257
|
+
const dockRef = usePanelRef()
|
|
258
|
+
// the dock's size moves the boundary, and only the boundary: its first
|
|
259
|
+
// height is the panel's default, read before the first render. After the
|
|
260
|
+
// render, so the panel already holds the bounds of its new size, and
|
|
261
|
+
// eased (dock-anim.ts), so the boundary slides rather than jumps
|
|
262
|
+
const px = d.px
|
|
263
|
+
useLayoutEffect(() => {
|
|
264
|
+
const p = dockRef.current
|
|
265
|
+
if (p != null && Math.abs(p.getSize().inPixels - px) >= 1) animateDock(vel.current, () => p.resize(px))
|
|
266
|
+
}, [px, dockRef])
|
|
345
267
|
return (
|
|
346
|
-
//
|
|
347
|
-
// and kept per browser, then the page
|
|
348
|
-
//
|
|
268
|
+
// the page over the dock: the project list, its width dragged by the
|
|
269
|
+
// separator and kept per browser, then the page; under both, the
|
|
270
|
+
// dock, on every page. The list, the strip and the dock stay mounted
|
|
271
|
+
// across pages; only the region under the strip changes
|
|
349
272
|
<>
|
|
350
|
-
<Group className="
|
|
273
|
+
<Group className="appv" orientation="vertical" elementRef={vel}
|
|
351
274
|
onLayoutChanged={(layout, meta) => {
|
|
352
|
-
if (!meta.isUserInteraction) return
|
|
353
|
-
const px =
|
|
354
|
-
if (px
|
|
275
|
+
if (!meta.isUserInteraction || d.size !== 'open') return
|
|
276
|
+
const px = belowPx(vel.current, layout, 'dock')
|
|
277
|
+
if (px == null) return
|
|
278
|
+
// a drag past a bound lands on it: the panel's own bounds
|
|
279
|
+
// stay 0 and the max, so the key can take it to any size
|
|
280
|
+
d.setHeight(px)
|
|
281
|
+
if (Math.abs(px - d.height) >= 1) dockRef.current?.resize(d.height)
|
|
355
282
|
}}>
|
|
356
|
-
<Panel id="
|
|
357
|
-
<
|
|
283
|
+
<Panel id="top" minSize={200} className="apptop" style={PANEL}>
|
|
284
|
+
<Group className="app" orientation="horizontal" elementRef={el}
|
|
285
|
+
onLayoutChanged={(layout, meta) => {
|
|
286
|
+
if (!meta.isUserInteraction) return
|
|
287
|
+
const px = leftPx(el.current, layout, 'projects')
|
|
288
|
+
if (px != null) l.setListWidth(px)
|
|
289
|
+
}}>
|
|
290
|
+
<Panel id="projects" defaultSize={l.listWidth} minSize={LIST.min} maxSize={LIST.max} groupResizeBehavior="preserve-pixel-size" className="pjpanel" style={PANEL}>
|
|
291
|
+
<ProjectList app={home} />
|
|
292
|
+
</Panel>
|
|
293
|
+
<Separator className="rsep" />
|
|
294
|
+
<Panel id="page" minSize={320} className="appcol" style={PANEL}>
|
|
295
|
+
{home.machine == null ? null : <HomeStrip app={home} />}
|
|
296
|
+
{home.page === 'overview' ? <Overview app={home} /> : null}
|
|
297
|
+
{home.page === 'planned' ? <PlannedView app={home} /> : null}
|
|
298
|
+
{home.page === 'activity' ? <ActivityView app={home} /> : null}
|
|
299
|
+
{home.page === 'archived' ? <ArchivedView app={home} /> : null}
|
|
300
|
+
{home.visited.map((r) => <RepoView key={r.key} app={r} />)}
|
|
301
|
+
</Panel>
|
|
302
|
+
</Group>
|
|
358
303
|
</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} />)}
|
|
304
|
+
<Separator className={`rsep hsep${d.size === 'open' ? '' : ' fixed'}`} disabled={d.size !== 'open'} />
|
|
305
|
+
<Panel id="dock" panelRef={dockRef} defaultSize={d.px} minSize={0} maxSize={DOCK.max} groupResizeBehavior="preserve-pixel-size" className="pdock" style={PANEL}>
|
|
306
|
+
<Dock app={home} />
|
|
365
307
|
</Panel>
|
|
366
308
|
</Group>
|
|
367
309
|
<Palette app={home} />
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Wire types the hub app reads — mirrors of the server shapes
|
|
2
2
|
// (studio-server.ts, collection-types.ts). Kept structural on purpose: the
|
|
3
3
|
// page renders what the API sends, the file is the truth.
|
|
4
|
-
import type { ResourceType, SuiteFacts, TestFacts, TestRun } from '../../collections/collection-types.ts'
|
|
4
|
+
import type { AskFacts, ResourceType, SuiteFacts, TestFacts, TestRun } from '../../collections/collection-types.ts'
|
|
5
5
|
|
|
6
6
|
export type SideAsset = {
|
|
7
7
|
id: string
|
|
@@ -62,7 +62,7 @@ export type Artifact = { copy?: string; url?: string; steps?: string[] }
|
|
|
62
62
|
* The list lives where the providers that emit it live, so a kind can never
|
|
63
63
|
* be known on one side of the wire only. Type only, so nothing server side
|
|
64
64
|
* reaches the bundle */
|
|
65
|
-
export type { ResourceType, SuiteFacts, TestFacts, TestRun }
|
|
65
|
+
export type { AskFacts, ResourceType, SuiteFacts, TestFacts, TestRun }
|
|
66
66
|
|
|
67
67
|
export type CheckStatus = 'pass' | 'warn' | 'fail' | 'skip'
|
|
68
68
|
|
|
@@ -73,6 +73,7 @@ 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 { Line } from '../../core/wire.ts'
|
|
76
77
|
import type { CheckFrom } from '../../check.ts'
|
|
77
78
|
export type { CheckFrom, WhenAt, WhenSource }
|
|
78
79
|
|
|
@@ -154,7 +155,7 @@ export type CheckEvent = {
|
|
|
154
155
|
replayed: boolean
|
|
155
156
|
blocking: boolean | null
|
|
156
157
|
advisory: boolean
|
|
157
|
-
lines:
|
|
158
|
+
lines: Line[]
|
|
158
159
|
}
|
|
159
160
|
/** the colour of the tree label: red broken · warn taste pending · ok
|
|
160
161
|
* passing on a check that ran · idle the neutral default · quiet archived or out of play */
|
|
@@ -186,6 +187,8 @@ export type Resource = {
|
|
|
186
187
|
check?: CheckFacts
|
|
187
188
|
/** test files only: where it runs and what it last cost */
|
|
188
189
|
test?: TestFacts
|
|
190
|
+
/** asks only: kind, dates, what it blocks, its probe */
|
|
191
|
+
ask?: AskFacts
|
|
189
192
|
}
|
|
190
193
|
export type ColMeta = { id: string; title: string; media: string }
|
|
191
194
|
export type Col = ColMeta & { instances: Resource[] }
|
|
@@ -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,18 +20,26 @@
|
|
|
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
|
+
/* the box the machine toasts float in (home-strip.css .htoasts) */
|
|
42
|
+
.appcol{position:relative;display:flex;flex-direction:column}
|
|
31
43
|
/* one region per repo visited, the shown one alone displayed */
|
|
32
44
|
.repo{flex:1;min-height:0;display:flex;flex-direction:column}
|
|
33
45
|
.repo[hidden]{display:none}
|
|
@@ -37,7 +49,11 @@ body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.5 system-ui,sans
|
|
|
37
49
|
.pmain{display:flex;flex-direction:column}
|
|
38
50
|
.pmain > main{flex:1}
|
|
39
51
|
|
|
40
|
-
/* ──
|
|
52
|
+
/* ── a repo's bar: on the home the strip (home-strip.css .hstrip), on a hub
|
|
53
|
+
of its own the header (.hdr). Its name, what runs now and the two runs,
|
|
54
|
+
then its processes, its trouble and its editor icons. One line at any
|
|
55
|
+
width: the container queries shrink the labels to their icons, then the
|
|
56
|
+
run state to its spinner ── */
|
|
41
57
|
/* the repo's processes (serve.processes): a dot, its id, a link once up */
|
|
42
58
|
.hprocs{display:inline-flex;gap:var(--s1);align-items:center}
|
|
43
59
|
.hproc{display:inline-flex;align-items:center;gap:var(--s0)}
|
|
@@ -45,45 +61,51 @@ body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.5 system-ui,sans
|
|
|
45
61
|
.hproc a{color:var(--accent);text-decoration:none;font-size:12px}
|
|
46
62
|
.pdot{display:inline-block;width:7px;height:7px;border-radius:4px;margin-right:var(--s1);background:var(--line)}
|
|
47
63
|
.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(--
|
|
64
|
+
.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
65
|
.hdr .sp{flex:1}
|
|
50
66
|
.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
|
-
.
|
|
67
|
+
.hdr,.hstrip{container-type:inline-size;white-space:nowrap}
|
|
68
|
+
.hrun{display:inline-flex;align-items:center;gap:var(--s1);flex:none;margin-left:var(--s2)}
|
|
69
|
+
.hbtn{display:inline-flex;align-items:center;gap:var(--s1);height:22px;padding:0 var(--s2);font-size:11.5px;white-space:nowrap;flex:none}
|
|
70
|
+
.hbtn .hico{width:13px;height:13px}
|
|
71
|
+
/* the editor icons: bare, dim, lit on hover, as the activity page's Fork icon */
|
|
72
|
+
.hicon{display:inline-flex;align-items:center;justify-content:center;flex:none;width:24px;height:22px;padding:0;border:0;background:none;color:var(--dim)}
|
|
73
|
+
.hicon:hover{color:var(--accent)}
|
|
74
|
+
.hicon .hico{width:15px;height:15px}
|
|
55
75
|
/* the run state holds its width whatever it says, so a run never moves the buttons beside it */
|
|
56
76
|
.hstatus{max-width:420px;flex:0 1 auto;font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
57
77
|
.hstatus.red{color:var(--red)}
|
|
58
78
|
.hstatus.warn{color:var(--warn)}
|
|
59
|
-
|
|
79
|
+
/* its words sit against the run buttons, cut with an ellipsis */
|
|
80
|
+
.hstate{display:inline-flex;gap:var(--s1);align-items:center;width:200px;min-width:200px;flex:none;justify-content:flex-end;font-size:11.5px;color:var(--dim);overflow:hidden}
|
|
60
81
|
.hstate.on{color:var(--ink)}
|
|
61
|
-
.hstate .htext{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
62
|
-
.hstate .spin.idle
|
|
82
|
+
.hstate .htext{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
83
|
+
.hstate .spin.idle{visibility:hidden;animation:none}
|
|
84
|
+
/* after .hstate, so a narrow bar wins over its width */
|
|
85
|
+
@container (max-width:600px){.hbtn .lbl{display:none}.hbtn{padding:0 var(--s1)}}
|
|
86
|
+
@container (max-width:520px){.hstate{width:120px;min-width:120px}}
|
|
87
|
+
@container (max-width:420px){.hstate .htext{display:none}.hstate{width:16px;min-width:16px}}
|
|
63
88
|
|
|
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)}
|
|
89
|
+
/* a row of a (?) legend: one glyph or word, then its meaning */
|
|
70
90
|
.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
91
|
|
|
83
|
-
/* ──
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
/* ── the left pane: the sections, the filter, then the tree, the one part
|
|
93
|
+
that scrolls ── */
|
|
94
|
+
aside{border-right:1px solid var(--line);background:var(--panel);min-height:0;display:flex;flex-direction:column;overflow:hidden}
|
|
95
|
+
aside > *{flex:none}
|
|
96
|
+
aside > .tscroll{flex:1;min-height:0;overflow-y:auto}
|
|
97
|
+
/* a repo's sections: the project list's lines (project-list.css .pjpl), a
|
|
98
|
+
little shorter, the current one lit and marked in the accent */
|
|
99
|
+
.rsecs{display:flex;flex-direction:column;padding:var(--s1) 0;border-bottom:1px solid var(--line)}
|
|
100
|
+
.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}
|
|
101
|
+
.rsec:hover{background:var(--panel2);color:var(--ink);border-color:transparent}
|
|
102
|
+
.rsec.cur{background:var(--sel);color:var(--ink);box-shadow:inset 3px 0 0 var(--accent);font-weight:600}
|
|
103
|
+
.rsec .hico{flex:none;width:16px;height:16px}
|
|
104
|
+
.rsecl{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
105
|
+
.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)}
|
|
106
|
+
.rsecn.nred{background:var(--red)}
|
|
107
|
+
.rsecn.nwarn{background:var(--warn)}
|
|
108
|
+
.rsec:hover .pjkey,.rsec.cur .pjkey{opacity:1}
|
|
87
109
|
/* segmented severity filter — all · needs · red. A thin border and an
|
|
88
110
|
underline on the live one: no fill, no pill */
|
|
89
111
|
.seg{display:inline-flex;border:1px solid var(--line);border-radius:3px;overflow:hidden;flex:none}
|
|
@@ -94,10 +116,16 @@ aside{border-right:1px solid var(--line);background:var(--panel);overflow-y:auto
|
|
|
94
116
|
.seg button.on{color:var(--accent);border-bottom-color:var(--accent)}
|
|
95
117
|
.seg button .sk{margin-left:var(--s1);font-size:9px;opacity:.55}
|
|
96
118
|
.swhen{padding:0 var(--s3) var(--s2);font-size:11px;color:var(--dim)}
|
|
97
|
-
|
|
119
|
+
/* a narrow pane puts the severity filter under the input, never over it */
|
|
120
|
+
.ssearch{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2);padding:var(--s2) var(--s3)}
|
|
121
|
+
.sfield{position:relative;flex:1 1 96px;min-width:0;display:flex}
|
|
98
122
|
.ssearch input{width:100%;background:var(--bg);border:1px solid var(--line);border-radius:4px;color:var(--ink);
|
|
99
|
-
padding:var(--
|
|
123
|
+
padding:var(--s0) var(--s4) var(--s0) var(--s2);font-size:12px;outline:none}
|
|
124
|
+
.ssearch input::placeholder{color:var(--dim);opacity:.6;font-size:11.5px}
|
|
100
125
|
.ssearch input:focus{border-color:var(--accent)}
|
|
126
|
+
/* its key, the way the project list prints its keys (.pjkey) */
|
|
127
|
+
.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}
|
|
128
|
+
.sfield input:focus + .skey{opacity:0}
|
|
101
129
|
/* ── the resource tree: ONE row shape, a fixed grid. The indent rides in
|
|
102
130
|
the FIRST column and the label eats the slack, so every right side slot
|
|
103
131
|
lines up down the whole column whatever the depth. Colour is on the
|
|
@@ -390,7 +418,6 @@ kbd{background:var(--panel2);border:1px solid var(--line);border-radius:4px;padd
|
|
|
390
418
|
.tlb.bad,.tlb.red{fill:var(--red)}
|
|
391
419
|
.tlb.rep{fill:var(--line)}
|
|
392
420
|
.tlb.run{fill:var(--accent);animation:pulse 1.6s ease-in-out infinite}
|
|
393
|
-
.tlb.repo{fill:var(--k6)}
|
|
394
421
|
.tlband.run{stroke:var(--accent)}
|
|
395
422
|
.tlconc{fill:var(--hair)}
|
|
396
423
|
.tlconc.over{fill:var(--warn)}
|
|
@@ -439,6 +466,8 @@ kbd{background:var(--panel2);border:1px solid var(--line);border-radius:4px;padd
|
|
|
439
466
|
per test check (the tests page), the failing files with their failure line */
|
|
440
467
|
.rtime{grid-template-columns:minmax(0,1fr) 88px 72px 128px 72px 40px 88px}
|
|
441
468
|
.rcheckt{grid-template-columns:152px 88px 48px 56px minmax(0,1fr) 72px}
|
|
469
|
+
/* every test file: file, check, duration, status, age */
|
|
470
|
+
.rall{grid-template-columns:minmax(0,1fr) 152px 88px 52px 72px}
|
|
442
471
|
.rhead .tleft{text-align:left}
|
|
443
472
|
.tshare{display:grid;grid-template-columns:minmax(0,1fr) 36px;gap:var(--s1);align-items:center}
|
|
444
473
|
.ttrack{display:block;height:6px;border-radius:1px;overflow:hidden;background:var(--panel2)}
|
|
@@ -600,6 +629,27 @@ a.tok{color:var(--accent)}
|
|
|
600
629
|
.cghead{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--accent);
|
|
601
630
|
display:flex;gap:var(--s2);align-items:baseline;margin:0 0 var(--s3)}
|
|
602
631
|
.cghead .cnt{font-size:11px;color:var(--dim);letter-spacing:0;text-transform:none}
|
|
632
|
+
/* the checks and asks pages: one dense table each (col-tables-view.tsx), on
|
|
633
|
+
the activity table (.actable), a group header per state that folds */
|
|
634
|
+
.ctpage{flex:1;min-height:0;overflow-y:auto;padding:var(--s3) var(--s4);font-size:12.5px;container-type:inline-size}
|
|
635
|
+
.cttable{font-variant-numeric:tabular-nums}
|
|
636
|
+
.cttable td{padding:var(--s0) var(--s2);vertical-align:baseline}
|
|
637
|
+
.ctgrp td{padding:var(--s2) var(--s2) var(--s1);color:var(--dim);font-size:11.5px;border-bottom:1px solid var(--line);cursor:pointer;user-select:none}
|
|
638
|
+
.ctgrp:hover td{color:var(--ink)}
|
|
639
|
+
.ctgrp.g-fail td{color:var(--red)}
|
|
640
|
+
.ctgrp.g-warn td{color:var(--warn)}
|
|
641
|
+
.ctcaret{display:inline-block;width:14px}
|
|
642
|
+
.ctrow{cursor:pointer}
|
|
643
|
+
.ctrow:hover{background:var(--panel2)}
|
|
644
|
+
.ctdot{font-size:10px}
|
|
645
|
+
.ctid,.ctkind{font:11.5px ui-monospace,Menlo,monospace}
|
|
646
|
+
.ctfind{font:11.5px ui-monospace,Menlo,monospace;color:var(--dim)}
|
|
647
|
+
.ctdur{text-align:right;font:11.5px ui-monospace,Menlo,monospace}
|
|
648
|
+
.ctbudget,.ctplan,.ctblocks{color:var(--dim)}
|
|
649
|
+
.ctprobe{font-size:11.5px}
|
|
650
|
+
.cttable .c-dot{width:22px}.cttable .c-id{width:140px}.cttable .c-title{width:16%}.cttable .c-age{width:72px}.cttable .c-dur{width:152px}.cttable .c-plan{width:88px}
|
|
651
|
+
.cttable .c-kind{width:92px}.cttable .c-blocks{width:32%}.cttable .c-probe{width:84px}
|
|
652
|
+
@container (max-width:760px){.cttable .c-title{width:0}.cttable .cttitle{display:none}.cttable .c-plan{width:0}.cttable .ctplan{display:none}}
|
|
603
653
|
.cgcards{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:var(--s3)}
|
|
604
654
|
.cgcard{background:var(--panel);border:1px solid var(--line);border-radius:5px;padding:var(--s2) var(--s3);cursor:pointer;min-width:0}
|
|
605
655
|
.cgcard:hover{border-color:var(--accent)}
|
|
@@ -666,7 +716,7 @@ a.mint{color:var(--accent);font-size:12px;text-decoration:none}
|
|
|
666
716
|
a.mint:hover{text-decoration:underline}
|
|
667
717
|
.colcard h3 .stchip,.colcard h3 a.mint{margin-left:var(--s2)}
|
|
668
718
|
|
|
669
|
-
/* ── the cmd+
|
|
719
|
+
/* ── the cmd+j picker ── */
|
|
670
720
|
.palov{position:fixed;inset:0;background:var(--scrim);z-index:100;display:flex;justify-content:center;align-items:flex-start;
|
|
671
721
|
padding-top:14vh}
|
|
672
722
|
.pal{width:min(560px,90vw);background:var(--panel);border:1px solid var(--line);border-radius:3px;
|
|
@@ -678,6 +728,9 @@ a.mint:hover{text-decoration:underline}
|
|
|
678
728
|
.prow.on{background:var(--panel2)}
|
|
679
729
|
.prow.none{color:var(--dim);cursor:default}
|
|
680
730
|
.prow .plabel{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
731
|
+
/* the entry's icon: a place's button icon, a repo's own (project-list.tsx Face) */
|
|
732
|
+
.prow .pface{flex:none;display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;color:var(--dim)}
|
|
733
|
+
.prow .pface img,.prow .pface .pgi{width:16px;height:16px;border-radius:4px;object-fit:contain}
|
|
681
734
|
.prow .pchip{font-size:10px;color:var(--dim);background:var(--bg);border:1px solid var(--line);border-radius:5px;
|
|
682
735
|
padding:var(--s0) var(--s1);text-transform:uppercase;letter-spacing:.08em;flex:none}
|
|
683
736
|
|
|
@@ -689,3 +742,12 @@ a.mint:hover{text-decoration:underline}
|
|
|
689
742
|
pre{background:var(--panel);border:1px solid var(--line);border-radius:3px;padding:var(--s4);overflow-x:auto;
|
|
690
743
|
font:12.5px/1.55 ui-monospace,Menlo,monospace;white-space:pre-wrap}
|
|
691
744
|
.ok{color:var(--ok)}.warn{color:var(--warn)}.fail{color:var(--red)}.dim{color:var(--dim)}
|
|
745
|
+
|
|
746
|
+
/* ── the admin page: one row per control, its sentence under its name ── */
|
|
747
|
+
.adrow{display:flex;align-items:center;gap:var(--s4);padding:var(--s3) 0;border-top:1px solid var(--line)}
|
|
748
|
+
.adtext{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--s0);font-size:13px}
|
|
749
|
+
.adtext .dim{font-size:12px}
|
|
750
|
+
.adctl{flex:none;display:flex;align-items:center}
|
|
751
|
+
.adflag{display:inline-flex;align-items:center;gap:var(--s1);font-size:12px;color:var(--dim)}
|
|
752
|
+
.adflag input{width:auto;margin:0}
|
|
753
|
+
.adconfirm{display:inline-flex;align-items:center;gap:var(--s2);font-size:12px}
|