projectkeeper 0.0.0-stage → 1.0.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 +56 -0
- package/LICENSE +21 -0
- package/README.md +283 -2
- package/README.zh-CN.md +285 -0
- package/THIRD_PARTY_NOTICES.md +53 -0
- package/bin/pk.mjs +6 -0
- package/bin/postinstall.mjs +5 -0
- package/bin/strip-types.mjs +28 -0
- package/package.json +68 -4
- package/patches/README.md +111 -0
- package/patches/pi-ai-0.85.0.ts +122 -0
- package/patches/pi-ai-0.87.1.ts +122 -0
- package/scripts/apply-pi-streaming-patch.ts +86 -0
- package/skills/clerk/coverage/SKILL.md +35 -0
- package/skills/clerk/cross-check/SKILL.md +118 -0
- package/skills/clerk/deepen-dispatch/SKILL.md +69 -0
- package/skills/clerk/lane-follow-up/SKILL.md +94 -0
- package/skills/clerk/lane-plan/SKILL.md +107 -0
- package/skills/clerk/lane-slot/SKILL.md +113 -0
- package/skills/clerk/lane-topic/SKILL.md +116 -0
- package/skills/clerk/orientation/SKILL.md +160 -0
- package/skills/clerk/reconcile/SKILL.md +77 -0
- package/skills/clerk/skeleton-dispatch/SKILL.md +43 -0
- package/skills/clerk/spot-check/SKILL.md +52 -0
- package/skills/clerk/synthesis/SKILL.md +65 -0
- package/skills/table-follow-up/SKILL.md +42 -0
- package/src/cli.ts +319 -0
- package/src/codemap/README.md +13 -0
- package/src/codemap/engines.ts +102 -0
- package/src/codemap/facts.ts +504 -0
- package/src/context/agent-entry.ts +232 -0
- package/src/context/ask.ts +97 -0
- package/src/context/assemble.ts +1058 -0
- package/src/context/code-source.ts +103 -0
- package/src/context/k-briefs.ts +114 -0
- package/src/context/lineage.ts +100 -0
- package/src/context/object-brief.ts +300 -0
- package/src/context/owner-words.ts +191 -0
- package/src/context/pack-fixture.ts +342 -0
- package/src/context/project-rules.ts +160 -0
- package/src/intake/intake.ts +392 -0
- package/src/intake/material-rules.ts +166 -0
- package/src/intake/removal.ts +316 -0
- package/src/intake/skipped.ts +72 -0
- package/src/keeper/adjustment.ts +565 -0
- package/src/keeper/bounds/boundary.ts +509 -0
- package/src/keeper/bounds/command.ts +1665 -0
- package/src/keeper/bounds/paths.ts +164 -0
- package/src/keeper/bounds/reads.ts +718 -0
- package/src/keeper/bounds/shell-write-guard.ts +521 -0
- package/src/keeper/capabilities.ts +136 -0
- package/src/keeper/clerk-steps.ts +151 -0
- package/src/keeper/clerk-tools.ts +1424 -0
- package/src/keeper/conversation.ts +253 -0
- package/src/keeper/evidence.ts +662 -0
- package/src/keeper/fake-provider.ts +132 -0
- package/src/keeper/held.ts +15 -0
- package/src/keeper/history-tools.ts +163 -0
- package/src/keeper/jobs/answering.ts +53 -0
- package/src/keeper/key-check.ts +105 -0
- package/src/keeper/ledger-tools.ts +295 -0
- package/src/keeper/link-check.ts +185 -0
- package/src/keeper/merge.ts +260 -0
- package/src/keeper/note-origin.ts +56 -0
- package/src/keeper/numbers-check.ts +102 -0
- package/src/keeper/open-in-pi.ts +57 -0
- package/src/keeper/organize/absence.ts +266 -0
- package/src/keeper/organize/carried-on.ts +214 -0
- package/src/keeper/organize/clerk-coverage.ts +293 -0
- package/src/keeper/organize/clerk-prompts.ts +132 -0
- package/src/keeper/organize/clerk.ts +1395 -0
- package/src/keeper/organize/coverage-gate.ts +19 -0
- package/src/keeper/organize/coverage-tools.ts +765 -0
- package/src/keeper/organize/entries.ts +153 -0
- package/src/keeper/organize/entry-gate.ts +289 -0
- package/src/keeper/organize/fill-tools.ts +718 -0
- package/src/keeper/organize/follow-up.ts +280 -0
- package/src/keeper/organize/generation-check.ts +504 -0
- package/src/keeper/organize/graph.ts +230 -0
- package/src/keeper/organize/lane-tools.ts +289 -0
- package/src/keeper/organize/looked-tools.ts +54 -0
- package/src/keeper/organize/materials.ts +243 -0
- package/src/keeper/organize/owner-lines.ts +493 -0
- package/src/keeper/organize/place-tools.ts +216 -0
- package/src/keeper/organize/placement-inference.ts +1045 -0
- package/src/keeper/organize/placement-time.ts +320 -0
- package/src/keeper/organize/placing.ts +821 -0
- package/src/keeper/organize/positioning.ts +33 -0
- package/src/keeper/organize/readiness.ts +47 -0
- package/src/keeper/organize/reading.ts +401 -0
- package/src/keeper/organize/round-blocks.ts +65 -0
- package/src/keeper/organize/round-news.ts +164 -0
- package/src/keeper/organize/round-open.ts +586 -0
- package/src/keeper/organize/schedule-state.ts +39 -0
- package/src/keeper/organize/schedule.ts +137 -0
- package/src/keeper/organize/service.ts +409 -0
- package/src/keeper/organize/skills.ts +116 -0
- package/src/keeper/organize/slots.ts +85 -0
- package/src/keeper/organize/stage-gate.ts +175 -0
- package/src/keeper/organize/stage-tools.ts +471 -0
- package/src/keeper/organize/standing-notes.ts +141 -0
- package/src/keeper/organize/table-follow-up.ts +330 -0
- package/src/keeper/organize/takeover-clerk.ts +832 -0
- package/src/keeper/organize/takeover-state.ts +103 -0
- package/src/keeper/organize/takeover.ts +216 -0
- package/src/keeper/organize/update-pending.ts +535 -0
- package/src/keeper/organize/workbench-placement.ts +92 -0
- package/src/keeper/own-keys.ts +114 -0
- package/src/keeper/owner-text.ts +165 -0
- package/src/keeper/project-folder.ts +334 -0
- package/src/keeper/prompts.ts +101 -0
- package/src/keeper/references.ts +177 -0
- package/src/keeper/relook/compose.ts +120 -0
- package/src/keeper/relook/prompts.ts +36 -0
- package/src/keeper/roles.ts +259 -0
- package/src/keeper/round-tools.ts +227 -0
- package/src/keeper/route.ts +148 -0
- package/src/keeper/rules.ts +97 -0
- package/src/keeper/runtime.ts +1858 -0
- package/src/keeper/scope-tools.ts +194 -0
- package/src/keeper/sendback-text.ts +83 -0
- package/src/keeper/step-timing.ts +207 -0
- package/src/keeper/testing/clerk-script.ts +212 -0
- package/src/keeper/testing/streaming-arguments.ts +57 -0
- package/src/keeper/tools.ts +2340 -0
- package/src/keeper/utterance-tools.ts +64 -0
- package/src/keeper/verbatim.ts +397 -0
- package/src/ledger/adapters.ts +186 -0
- package/src/ledger/arrangements.ts +257 -0
- package/src/ledger/blame.ts +58 -0
- package/src/ledger/code-engine-child.ts +69 -0
- package/src/ledger/code-engine.ts +548 -0
- package/src/ledger/code.ts +607 -0
- package/src/ledger/docs.ts +294 -0
- package/src/ledger/git-read.ts +321 -0
- package/src/ledger/index.ts +1850 -0
- package/src/ledger/item-range.ts +16 -0
- package/src/ledger/lines.ts +328 -0
- package/src/ledger/numbering.ts +269 -0
- package/src/ledger/ranges.ts +74 -0
- package/src/ledger/rebuild-worker.ts +15 -0
- package/src/ledger/rebuild.ts +387 -0
- package/src/ledger/repo-scan.ts +181 -0
- package/src/ledger/schema.ts +450 -0
- package/src/ledger/sessions.ts +134 -0
- package/src/ledger/text-scan.ts +466 -0
- package/src/ledger/time.ts +185 -0
- package/src/ledger/ts-symbols.ts +180 -0
- package/src/ledger/views.ts +377 -0
- package/src/mcp/server.ts +127 -0
- package/src/mcp/tools.ts +187 -0
- package/src/model/ids.ts +23 -0
- package/src/model/k-types.ts +912 -0
- package/src/model/time.ts +85 -0
- package/src/model/types.ts +1382 -0
- package/src/model/views-k.ts +707 -0
- package/src/model/vocab.ts +269 -0
- package/src/process/analysis.ts +79 -0
- package/src/process/breakpoint-candidates.ts +383 -0
- package/src/process/breakpoints.ts +746 -0
- package/src/process/claim-check.ts +295 -0
- package/src/process/delivery-links.ts +52 -0
- package/src/process/facts.ts +487 -0
- package/src/process/generations.ts +138 -0
- package/src/process/index.ts +243 -0
- package/src/process/integration.ts +128 -0
- package/src/process/placement.ts +107 -0
- package/src/process/plan.ts +276 -0
- package/src/process/sendbacks.ts +244 -0
- package/src/process/text.ts +185 -0
- package/src/process/units.ts +543 -0
- package/src/process/unnumbered.ts +431 -0
- package/src/process/work.ts +884 -0
- package/src/scope/candidates.ts +86 -0
- package/src/scope/decisions.ts +229 -0
- package/src/scope/discover.ts +741 -0
- package/src/scope/ignore.ts +126 -0
- package/src/scope/ignored-place.ts +93 -0
- package/src/scope/skip.ts +161 -0
- package/src/scope/toolchain.ts +180 -0
- package/src/scope/worktree.ts +187 -0
- package/src/server/api.ts +549 -0
- package/src/server/app.ts +595 -0
- package/src/server/drafts-api.ts +170 -0
- package/src/server/folders-api.ts +243 -0
- package/src/server/graph-view.ts +639 -0
- package/src/server/http.ts +224 -0
- package/src/server/k-agent-api.ts +56 -0
- package/src/server/k-rounds-d99.ts +91 -0
- package/src/server/k-views.ts +405 -0
- package/src/server/keeper-page.ts +446 -0
- package/src/server/keys-api.ts +239 -0
- package/src/server/project-folder-api.ts +104 -0
- package/src/server/vendor.ts +14 -0
- package/src/server/workbench-content.ts +561 -0
- package/src/sources/anchor.ts +129 -0
- package/src/sources/files.ts +267 -0
- package/src/sources/gitobs.ts +41 -0
- package/src/sources/history.ts +231 -0
- package/src/sources/scan-worker.ts +10 -0
- package/src/sources/sessions/locate.ts +292 -0
- package/src/sources/sessions/read.ts +346 -0
- package/src/sources/sessions/scope.ts +42 -0
- package/src/sources/sessions/utterances.ts +223 -0
- package/src/sources/watch.ts +215 -0
- package/src/store/json-file.ts +50 -0
- package/src/store/paths.ts +39 -0
- package/src/store/project-store.ts +295 -0
- package/src/store/versions.ts +172 -0
- package/src/store/workspace.ts +216 -0
- package/src/table/check.ts +333 -0
- package/src/table/follow-up-tools.ts +30 -0
- package/src/table/follow-up.ts +344 -0
- package/src/table/import.ts +610 -0
- package/src/table/project-home.ts +61 -0
- package/src/table/sessions.ts +168 -0
- package/src/table/takeover.ts +483 -0
- package/src/util/git-check.ts +29 -0
- package/src/util/git.ts +411 -0
- package/src/util/own-ts.ts +15 -0
- package/src/util/paths.ts +225 -0
- package/template/FILLING-IN.md +148 -0
- package/template/examples/papertrail.json +747 -0
- package/template/projectkeeper.schema.json +1001 -0
- package/ui/app.js +1135 -0
- package/ui/failures.js +82 -0
- package/ui/flyout.js +120 -0
- package/ui/folder-chooser.js +264 -0
- package/ui/glossary.js +156 -0
- package/ui/graph-fit.js +157 -0
- package/ui/graph-material.js +61 -0
- package/ui/graph-palette.js +109 -0
- package/ui/graph-tools.js +62 -0
- package/ui/graph.js +1712 -0
- package/ui/index.html +31 -0
- package/ui/k/code-view.js +230 -0
- package/ui/k/draft-view.js +93 -0
- package/ui/k/k-views.css +224 -0
- package/ui/k/round-tree.js +450 -0
- package/ui/k/scope-k.js +159 -0
- package/ui/k-data.js +118 -0
- package/ui/k-fold.js +106 -0
- package/ui/k-process.css +302 -0
- package/ui/k-process.js +934 -0
- package/ui/keeper-activity.js +175 -0
- package/ui/keeper-view.js +801 -0
- package/ui/keeper.css +101 -0
- package/ui/key-order.js +62 -0
- package/ui/list-fold.js +67 -0
- package/ui/markdown.d.ts +21 -0
- package/ui/markdown.js +472 -0
- package/ui/placement.d.ts +92 -0
- package/ui/placement.js +445 -0
- package/ui/popover-place.d.ts +16 -0
- package/ui/popover-place.js +89 -0
- package/ui/popover.js +198 -0
- package/ui/project-folder.js +37 -0
- package/ui/shell-state.js +40 -0
- package/ui/strip-rows.js +171 -0
- package/ui/styles.css +492 -0
- package/ui/theme-pick.js +36 -0
- package/ui/theme.js +74 -0
- package/ui/themes/LICENSE.md +13 -0
- package/ui/themes/README.md +44 -0
- package/ui/themes/TOKENS.md +291 -0
- package/ui/themes/a1/board.css +1557 -0
- package/ui/themes/a1/pk.css +392 -0
- package/ui/themes/b1/board.css +1163 -0
- package/ui/themes/b1/pk.css +218 -0
- package/ui/themes/d2/board.css +518 -0
- package/ui/themes/d2/pk.css +243 -0
- package/ui/themes/guard.css +73 -0
- package/ui/themes/i1/board.css +776 -0
- package/ui/themes/i1/pk.css +223 -0
- package/ui/themes/manifest.json +13 -0
- package/ui/views.js +2796 -0
package/ui/theme.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Themes on the page (Spec §6.16; CKC-09 AC-40): the two <link>s in index.html — the frozen board.css of a theme and
|
|
2
|
+
// ProjectKeeper's own layer for it — take their hrefs from the choice kept in this browser. Changing the theme swaps the
|
|
3
|
+
// two hrefs and nothing else: no data is fetched again, nothing is rebuilt; the graph alone repaints its pictures in the
|
|
4
|
+
// new colours once the sheets have loaded (graph.js repaint, through the `pk:theme` event). A theme whose files do not
|
|
5
|
+
// load falls back to the factory look for this session. The choices themselves are pure (theme-pick.js).
|
|
6
|
+
import { FACTORY, THEME_KEY, resolveTheme, themeHrefs, themeOptions, themeName } from './theme-pick.js';
|
|
7
|
+
import { h, toast } from './app.js';
|
|
8
|
+
|
|
9
|
+
let manifest = null;
|
|
10
|
+
let current = FACTORY;
|
|
11
|
+
let latest = 0; // the last request; a slower earlier one is not allowed to finish after it
|
|
12
|
+
|
|
13
|
+
/** The theme in force: a manifest id, or the factory look (''). */
|
|
14
|
+
export const currentTheme = () => current;
|
|
15
|
+
export const themeList = () => themeOptions(manifest);
|
|
16
|
+
|
|
17
|
+
/** Read the manifest and open the remembered theme (or the default); resolves once its sheets are in. */
|
|
18
|
+
export async function initTheme() {
|
|
19
|
+
try { manifest = await (await fetch('/themes/manifest.json')).json(); } catch { manifest = null; }
|
|
20
|
+
let saved = null;
|
|
21
|
+
try { saved = localStorage.getItem(THEME_KEY); } catch { /* nothing remembered here */ }
|
|
22
|
+
await applyTheme(resolveTheme(saved, manifest), { remember: false, announce: false });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Put a theme on the page. `remember` keeps it in this browser (a personal preference, never a project asset). When a
|
|
27
|
+
* sheet fails to load the factory look is shown instead and the owner is told; the remembered choice is left as it
|
|
28
|
+
* is, so a theme that is back next time comes back by itself.
|
|
29
|
+
*/
|
|
30
|
+
export async function applyTheme(id, { remember = true, announce = true } = {}) {
|
|
31
|
+
const board = document.getElementById('theme-board'), pk = document.getElementById('theme-pk');
|
|
32
|
+
if (!board || !pk) return;
|
|
33
|
+
const mine = ++latest;
|
|
34
|
+
if (remember) { try { localStorage.setItem(THEME_KEY, id); } catch { /* a convenience only */ } }
|
|
35
|
+
const hrefs = themeHrefs(id);
|
|
36
|
+
const ok = await Promise.all([swap(board, hrefs.board), swap(pk, hrefs.pk)]).then((r) => r.every(Boolean));
|
|
37
|
+
if (mine !== latest) return; // superseded
|
|
38
|
+
if (ok || id === FACTORY) current = id;
|
|
39
|
+
else {
|
|
40
|
+
current = FACTORY;
|
|
41
|
+
await Promise.all([swap(board, ''), swap(pk, '')]);
|
|
42
|
+
if (mine !== latest) return;
|
|
43
|
+
toast(`Theme ${themeName(id, manifest)} could not be loaded; showing the factory look`);
|
|
44
|
+
}
|
|
45
|
+
syncPickers();
|
|
46
|
+
document.dispatchEvent(new CustomEvent('pk:theme', { detail: { id: current, announce } }));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Set a <link>'s href and wait for the sheet: true once it is in, false when it could not be loaded. */
|
|
50
|
+
function swap(link, href) {
|
|
51
|
+
if ((link.getAttribute('href') ?? '') === href) return Promise.resolve(true);
|
|
52
|
+
if (!href) { link.setAttribute('href', ''); return Promise.resolve(true); }
|
|
53
|
+
return new Promise((resolve) => {
|
|
54
|
+
let done = false;
|
|
55
|
+
const finish = (ok) => { if (done) return; done = true; clearTimeout(timer); link.removeEventListener('load', onLoad); link.removeEventListener('error', onError); resolve(ok); };
|
|
56
|
+
const onLoad = () => finish(true);
|
|
57
|
+
const onError = () => finish(false);
|
|
58
|
+
// A sheet that has not answered in this long is treated as missing: the factory look is always ready.
|
|
59
|
+
const timer = setTimeout(() => finish(false), 8000);
|
|
60
|
+
link.addEventListener('load', onLoad);
|
|
61
|
+
link.addEventListener('error', onError);
|
|
62
|
+
link.setAttribute('href', href);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The picker in the top bar: the factory look and the four themes by name; a change applies at once. */
|
|
67
|
+
export function themePicker() {
|
|
68
|
+
const select = h('select', { class: 'theme-select', 'aria-label': 'Theme', title: 'Theme: how the workbench looks. Kept in this browser.', onChange: (e) => { void applyTheme(e.target.value); } },
|
|
69
|
+
...themeOptions(manifest).map((o) => h('option', { value: o.id, selected: o.id === current }, o.name)));
|
|
70
|
+
return h('label', { class: 'theme-pick' }, h('span', {}, 'Theme'), select);
|
|
71
|
+
}
|
|
72
|
+
function syncPickers() {
|
|
73
|
+
document.querySelectorAll('.theme-pick select').forEach((s) => { if (s.value !== current) s.value = current; });
|
|
74
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Licence of the themes
|
|
2
|
+
|
|
3
|
+
The files in this folder — the four themes `a1` (Walnut Workshop), `b1` (Peacock & Brass), `d2` (Warm Clay) and `i1`
|
|
4
|
+
(Task Console), each a `board.css` and a `pk.css`, with `guard.css`, `manifest.json`, `README.md` and `TOKENS.md` — are
|
|
5
|
+
**not covered by the MIT licence** that covers the rest of ProjectKeeper.
|
|
6
|
+
|
|
7
|
+
All rights in them are reserved by their author. They are published here so that ProjectKeeper looks as it was made to
|
|
8
|
+
look, and you may use them with ProjectKeeper as shipped: run it with them, and keep them in a copy or fork of
|
|
9
|
+
ProjectKeeper. You may not take them into another product, or redistribute them apart from ProjectKeeper, without the
|
|
10
|
+
author's permission.
|
|
11
|
+
|
|
12
|
+
ProjectKeeper works without them: with no theme chosen it shows its own built-in dark look, which is part of the
|
|
13
|
+
MIT-licensed code (`ui/styles.css`).
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Themes
|
|
2
|
+
|
|
3
|
+
The workbench borrows WorkflowKeeper's four themes, by the owner's decision. The owner reviewed and hand-edited
|
|
4
|
+
each of them there; what was reviewed is frozen.
|
|
5
|
+
|
|
6
|
+
The themes are not covered by ProjectKeeper's MIT licence: see [LICENSE.md](LICENSE.md).
|
|
7
|
+
|
|
8
|
+
| id | name | character |
|
|
9
|
+
| --- | --- | --- |
|
|
10
|
+
| `a1` | Walnut Workshop | dark walnut, cast brass plates, parchment cards; the default (owner 2026-09-22) |
|
|
11
|
+
| `b1` | Peacock & Brass | four steps of green-black, brass only on edges and on what can be clicked |
|
|
12
|
+
| `d2` | Warm Clay | warm clay paper, terracotta accent; the one light theme |
|
|
13
|
+
| `i1` | Task Console | near-black machined panels, amber warning parts |
|
|
14
|
+
|
|
15
|
+
The workbench's own dark look stays as the factory theme and is what shows when no theme is chosen or a theme fails to
|
|
16
|
+
load.
|
|
17
|
+
|
|
18
|
+
## What is frozen
|
|
19
|
+
|
|
20
|
+
`<id>/board.css` is a **byte-for-byte copy** of WorkflowKeeper's `app/themes/<id>/board.css`. `manifest.json` records
|
|
21
|
+
where each came from, the WorkflowKeeper commit, the day it was copied, its size and its SHA-256.
|
|
22
|
+
|
|
23
|
+
- Never edit a `board.css` here, not even whitespace. Only the owner changes what is frozen, and they change it in
|
|
24
|
+
WorkflowKeeper. `.gitattributes` marks these files `-text` so git never rewrites their line endings.
|
|
25
|
+
- `npm test` checks every copy against the manifest (`src/server/themes.test.ts`).
|
|
26
|
+
- When WorkflowKeeper's theme has changed, that is a decision for the owner: copy the new file over, update the
|
|
27
|
+
manifest, and look at the workbench in that theme again.
|
|
28
|
+
- WorkflowKeeper's covers (`cover.html`) are not copied: the workbench has no start screen.
|
|
29
|
+
|
|
30
|
+
## What is ProjectKeeper's own
|
|
31
|
+
|
|
32
|
+
Everything the workbench adds for a theme goes in its own layer beside the frozen file, never into it: `<id>/pk.css`.
|
|
33
|
+
That layer uses only values the theme already has (its colours, radii, shadows and fonts) and can be removed as a
|
|
34
|
+
whole, leaving the factory look.
|
|
35
|
+
|
|
36
|
+
- `ui/styles.css` says every colour, shadow, radius and font as `var(--pk-…, <factory literal>)`; `pk.css` fills the
|
|
37
|
+
tokens on `:root` and adds the parts the board never had. The tokens are the contract in [TOKENS.md](TOKENS.md).
|
|
38
|
+
- The page loads `styles.css` → `<id>/board.css` → `<id>/pk.css` → `guard.css` (always last: the shell's layout, no
|
|
39
|
+
colour). The picker in the top bar swaps the two theme hrefs and nothing else; the choice is this browser's
|
|
40
|
+
(`localStorage`, `pk.theme`), `a1` by default (`manifest.json`). The graph reads its colours from the `--pk-graph-*`
|
|
41
|
+
tokens and repaints in place (`ui/graph-palette.js`).
|
|
42
|
+
- All four carry a complete layer (`pk.css`); `i1` was the first, the model for the other three.
|
|
43
|
+
- `scripts/ui-theme-check.mjs` measures a running fixture workbench: contrast, the graph's four colours through
|
|
44
|
+
`palette-check.py`, what a switch leaves untouched, the fallback, the guard.
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# The `--pk-*` tokens: what a theme's `pk.css` fills in
|
|
2
|
+
|
|
3
|
+
The contract between `ui/styles.css` and each theme's own layer (`ui/themes/<id>/pk.css`). Read this, look at
|
|
4
|
+
`i1/pk.css` (the finished example), and fill your theme without reading `styles.css`.
|
|
5
|
+
|
|
6
|
+
## How it works
|
|
7
|
+
|
|
8
|
+
- `styles.css` says every colour, shadow, radius and font as `var(--pk-…, <factory literal>)`. The factory skin defines no
|
|
9
|
+
token, so with no theme every fallback applies and the factory look is exactly what it was (a test guards this:
|
|
10
|
+
`src/ui/styles-tokens.test.ts`).
|
|
11
|
+
- A theme is two files, loaded after `styles.css` in this order: the owner's frozen `board.css` (never edited, checked
|
|
12
|
+
byte for byte), then `pk.css`. `themes/guard.css` is always last.
|
|
13
|
+
- `pk.css` defines the tokens on `:root` **using only values the board already has**: its own custom properties
|
|
14
|
+
(`var(--card)`), or literals that appear in its `board.css`. No new colour, shadow or radius. Then it adds
|
|
15
|
+
the workbench parts the board never had (a segmented control, a conversation, a graph) in the same language.
|
|
16
|
+
- **Custom properties inherit.** A token may take another value on another surface: `a1` gives the parchment cards
|
|
17
|
+
their own text and line tokens by redefining them on `.popover, .flyout, dialog, .note-card, .keeper-panel,
|
|
18
|
+
.strip-row.open, .graph-tip, .keeper-entry`, so everything standing on a card takes the ink.
|
|
19
|
+
- **Names.** Only `--pk-*` custom properties exist in `styles.css` — the boards define `--bg`, `--line`, `--amber`,
|
|
20
|
+
`--surface`, `--raise` with meanings of their own, so the workbench never reads an unprefixed one.
|
|
21
|
+
- **Chains.** Many tokens fall back to a more general one (`--pk-card-bg` → `--pk-surface` → the literal). A theme that
|
|
22
|
+
sets only the general tokens gets a coherent whole and refines from there. The graph's palette is read the same way
|
|
23
|
+
(`ui/graph-palette.js` `PALETTE_VARS`: the graph token first, then the general token, then the factory value).
|
|
24
|
+
- **Switching a theme swaps the two `<link>` hrefs and nothing else.** No data is fetched, nothing is rebuilt; the
|
|
25
|
+
graph alone reads its palette again and repaints its pictures (`graph.js repaint`). So a theme must not depend on any
|
|
26
|
+
script running: everything is CSS, and the graph's colours are CSS variables.
|
|
27
|
+
- **Every theme passes the same checks** (`scripts/ui-theme-check.mjs`): body text 4.5:1 or better on its surfaces,
|
|
28
|
+
labels and counts readable, the graph's four colours checked with `scripts/palette-check.py`
|
|
29
|
+
(`Plan` / `Work item` under protan and deutan not below the factory's 25.2 / 21.1), the readability mark unchanged by
|
|
30
|
+
the theme, no new `/api/` request on a switch, zoom / pan / scroll unchanged.
|
|
31
|
+
|
|
32
|
+
## The tokens
|
|
33
|
+
|
|
34
|
+
Factory value = the literal `styles.css` falls back to. Where a token is read with more than one fallback, the theme's
|
|
35
|
+
one value replaces all of them (that is the point). "Chain" says what stands in when the token is not set.
|
|
36
|
+
|
|
37
|
+
### Surfaces and lines
|
|
38
|
+
|
|
39
|
+
| Token | What it is | Where | Factory |
|
|
40
|
+
| --- | --- | --- | --- |
|
|
41
|
+
| `--pk-ground` | the page behind everything; the ring around timeline dots | `body`, `.tl-event:before` | `#141413` |
|
|
42
|
+
| `--pk-rail` | the band surfaces: the rail, the bottom strip, the control row, the legend, the compare bar, the band under the pill | `.rail`, `.strip`, `.graph-tools`, `.legend`, `.compare-bar`, `.body::after` | `#191918` |
|
|
43
|
+
| `--pk-surface` | sections, cards and controls when their own tokens are not set; legend chips | chains; `.lg-mark`, `.lg-count` | `#1c1c1a` |
|
|
44
|
+
| `--pk-raised` | what lifts off a surface: hover, tags, chips, the toast, the Markdown table head, a note card inside the popover | `.btn:hover`, `.tag`, `.nav-item:hover`, `#toast`, `.md th`, … | `#242420` (toast `#303027`) |
|
|
45
|
+
| `--pk-line` | the stronger line: borders of tags of the strip's marks, blockquotes, table heads, keyboard hints, the timeline | 19 places | `#35352f` |
|
|
46
|
+
| `--pk-line-soft` | the quiet line: section borders, table rows, the rail's right edge, the top bar's bottom | 15 places | `#2b2b26` |
|
|
47
|
+
| `--pk-line-strong` | the line of what floats or is picked out: the scrollbar thumb, the toast's border, the legend count chip | `main` scrollbar, `#toast`, `.lg-count` | `#4b4b42` / `#505044` / `#4a4a43` |
|
|
48
|
+
| `--pk-scrim` | what dims the page under the popover and a dialog | `.popover-scrim`, `dialog::backdrop` | `#0009` |
|
|
49
|
+
|
|
50
|
+
### Text (four steps)
|
|
51
|
+
|
|
52
|
+
| Token | What it is | Where | Factory |
|
|
53
|
+
| --- | --- | --- | --- |
|
|
54
|
+
| `--pk-text-strong` | the highest-contrast text the theme has: the Keeper's answers and the owner's messages | `.msg.keeper`, `.msg.user`; the graph's node text by chain | `#e7e7df` |
|
|
55
|
+
| `--pk-text` | body text | `body`, 23 places | `#e7e7df` |
|
|
56
|
+
| `--pk-text-muted` | secondary text: `small`, `.muted`, tags, section subtitles, the object column of a strip row | 42 places | `#a5a59b` |
|
|
57
|
+
| `--pk-text-faint` | tertiary: rail section labels, `dt`, times, counts, hints, line numbers | 32 places | `#85857b` (`#75756c`) |
|
|
58
|
+
|
|
59
|
+
Body text must read 4.5:1 or better on `--pk-surface`, `--pk-rail` and `--pk-card-bg`; muted and faint are used at
|
|
60
|
+
10.5–12px, so keep them at 4.5:1 too where you can (i1: `--tx1` 9.8, `#a89a83` 6.4, `--tx2` 5.4 on the card).
|
|
61
|
+
|
|
62
|
+
### The accent and the families
|
|
63
|
+
|
|
64
|
+
| Token | What it is | Where | Factory |
|
|
65
|
+
| --- | --- | --- | --- |
|
|
66
|
+
| `--pk-accent` | the one signal colour: focus rings, the active state, the selected object, the pill's edge, the popover's top edge, the readability alert | 39 places | `#e3b76f` |
|
|
67
|
+
| `--pk-accent-bg` | the tinted face under accent text: `.tag.amber`, `.btn.active`, `.read-mark.alert`, the active nav on a narrow window | 7 places | `#322c20` |
|
|
68
|
+
| `--pk-accent-hi` | the accent lit further: the primary button's hover | `.btn.primary:hover` | `#edc98f` |
|
|
69
|
+
| `--pk-on-accent` | text on a solid accent face | `.btn.primary`, `.mk-decide` | `#211e16` / `#141413` |
|
|
70
|
+
| `--pk-accent-soft` | the quiet accent face of a selected row: the active view in the rail, a selected strip row | `.nav-item.active`, `.strip .item.sel` | `#302b22` |
|
|
71
|
+
| `--pk-flash` | where the List's flash starts when an object is arrived at | `@keyframes pk-flash` | `#4a3f25` |
|
|
72
|
+
| `--pk-star` | the ★ in the List and the legend | `.star`, `.star-legend` | `#ffd54a` |
|
|
73
|
+
| `--pk-green`, `--pk-green-bg`, `--pk-green-ln` | good / done: `.tag.green`, `.dot.green`, `.mk-good`, the diff's new side | | `#a3c5ac`, `#253129` (`#202820`), `#374237` |
|
|
74
|
+
| `--pk-blue`, `--pk-blue-bg` | what can be pressed: links, `.text-btn`, `.tag.blue`, `.mk-discuss` | | `#9fbdd5`, `#252e36` |
|
|
75
|
+
| `--pk-red`, `--pk-red-bg`, `--pk-red-ln` | wrong / removed: `.tag.red`, `.btn.danger`, `.mk-bad`, the diff's old side (`-ln` for struck text) | | `#dca6a0`, `#3a2a2a` (`#23201e`), `#b8a59d` |
|
|
76
|
+
| `--pk-purple`, `--pk-purple-bg` | `.tag.purple` | | `#c4b0dc`, `#2e2838` |
|
|
77
|
+
|
|
78
|
+
### Selection
|
|
79
|
+
|
|
80
|
+
| Token | What it is | Where | Factory |
|
|
81
|
+
| --- | --- | --- | --- |
|
|
82
|
+
| `--pk-sel-bg` | a selected table row | `table.list tr.sel td` | `#1f1f1b` |
|
|
83
|
+
| `--pk-control-on` | the segment that is on | `.segmented button.active` | `#34332d` |
|
|
84
|
+
| `--pk-control-on-text`, `--pk-control-on-shadow` | its text and its shadow (pressed, lit) | same | `--pk-text`, `none` |
|
|
85
|
+
|
|
86
|
+
### Fonts
|
|
87
|
+
|
|
88
|
+
| Token | What it is | Factory |
|
|
89
|
+
| --- | --- | --- |
|
|
90
|
+
| `--pk-font-body` | the whole `font` shorthand of `body`: give the board's own size and stack (i1 `12.5px/1.55 "Segoe UI",…`) | `13px/1.55 var(--pk-font-ui)` |
|
|
91
|
+
| `--pk-font-ui` | the interface family, where it is set apart from `body` (`kbd`, quoted sources, cite chips) | `'Segoe UI Variable Text','Segoe UI','Microsoft YaHei',sans-serif` |
|
|
92
|
+
| `--pk-font-mono` | identifiers, counts, paths, code | `'Cascadia Code',Consolas,monospace` |
|
|
93
|
+
| `--pk-font-display` | the π mark of the Keeper | `Georgia,serif` |
|
|
94
|
+
|
|
95
|
+
The heading sizes are the workbench's, pinned by the guard: `h1` 22px, `h2` 16px, `h3` 13px, `h4` 12px. A theme colours
|
|
96
|
+
them; it does not resize them (hierarchy by size difference, kept the same in every theme).
|
|
97
|
+
|
|
98
|
+
### Radii
|
|
99
|
+
|
|
100
|
+
| Token | Where | Factory |
|
|
101
|
+
| --- | --- | --- |
|
|
102
|
+
| `--pk-radius` | buttons, cards, the Markdown `pre`, the walkthrough boxes, the toast | `6px` (`7px`) |
|
|
103
|
+
| `--pk-radius-sm` | tags, inputs, rows, nav items, the strip's marks, folds, diff boxes | `4px` / `5px` |
|
|
104
|
+
| `--pk-radius-xs` | code, `kbd`, legend swatches, segment buttons | `3px` / `2px` |
|
|
105
|
+
| `--pk-radius-panel` | the dialog and the Keeper's window (the popover and flyout go through `--pk-card-radius`) | `9px` / `10px` |
|
|
106
|
+
| `--pk-radius-pill` | pills: the Keeper's dock, the graph's corner buttons, index chips, the agent chip, the legend count | `22px` … `8px` |
|
|
107
|
+
|
|
108
|
+
Circles (`.dot`, `.lg-mark`) stay `50%`; they are shapes, not radii.
|
|
109
|
+
|
|
110
|
+
### Shadows
|
|
111
|
+
|
|
112
|
+
| Token | Where | Factory |
|
|
113
|
+
| --- | --- | --- |
|
|
114
|
+
| `--pk-shadow-float` | the docked Keeper over the main view on a narrow window; the popover and flyout by chain | `0 14px 40px #0009` |
|
|
115
|
+
| `--pk-shadow-window` | the Keeper's popped-out window (and while it is dragged), the dialog | `0 28px 80px #000b, 0 1px 0 #ffffff0d inset` … |
|
|
116
|
+
| `--pk-shadow-raised` | the pill, the toast, the graph tooltip | `0 4px 16px #0006` … |
|
|
117
|
+
|
|
118
|
+
### The one card
|
|
119
|
+
|
|
120
|
+
The popover, the docked Keeper panel and its window, the dialog, note cards, the row of the bottom strip that opens,
|
|
121
|
+
the Keeper entry in the rail and the graph tooltip all read these, so one set of values makes them one card.
|
|
122
|
+
|
|
123
|
+
| Token | Chain | What it is | Factory |
|
|
124
|
+
| --- | --- | --- | --- |
|
|
125
|
+
| `--pk-card-bg` | `--pk-surface` | the face | `#1c1c1a` (`#191917`, `#1e1e1b`, `#10100f`, `#191918`) |
|
|
126
|
+
| `--pk-card-texture` | | `background-image` over the face (a chamfer, a brush, a grain); `none` for flat | `none` |
|
|
127
|
+
| `--pk-card-border` | `--pk-line` / `--pk-line-soft` | the frame | |
|
|
128
|
+
| `--pk-card-shadow` | `--pk-shadow-float` for the floating ones | the card's own shadow (i1: `--bezel-chunk`) | `none` |
|
|
129
|
+
| `--pk-card-radius` | `--pk-radius` / `--pk-radius-panel` | | `6px` / `8px` |
|
|
130
|
+
| `--pk-card-text` | `--pk-text` | text on the card (a1: the ink) | |
|
|
131
|
+
| `--pk-card-line` | `--pk-line-soft` / `--pk-line` | dividers inside a card: heads, feet, the composer's top | |
|
|
132
|
+
| `--pk-card-hover-border` | `--pk-line` | a note card pointed at | |
|
|
133
|
+
| `--pk-card-foot-bg` | `--pk-rail` | the popover's foot (i1: the card's port strip) | |
|
|
134
|
+
| `--pk-card-head-bg` | `--pk-raised` | the line of a strip row that is open | |
|
|
135
|
+
|
|
136
|
+
### A section and its head (the bin header)
|
|
137
|
+
|
|
138
|
+
`.section` blocks (Project scope, Keeper, the Change log…) and `details.fold`; the head is `.section > header` and the
|
|
139
|
+
column head of the bottom strip (`.strip header`).
|
|
140
|
+
|
|
141
|
+
| Token | Chain | Factory |
|
|
142
|
+
| --- | --- | --- |
|
|
143
|
+
| `--pk-section-bg` | `--pk-surface` | |
|
|
144
|
+
| `--pk-section-border` | `--pk-line-soft` | |
|
|
145
|
+
| `--pk-section-radius` | `--pk-radius` / `--pk-radius-sm` | |
|
|
146
|
+
| `--pk-section-shadow` | | `none` |
|
|
147
|
+
| `--pk-head-bg` | | `transparent` |
|
|
148
|
+
| `--pk-head-texture` | | `none` |
|
|
149
|
+
| `--pk-head-border` | `--pk-line-soft` | the head's bottom line |
|
|
150
|
+
| `--pk-head-shadow` | | `none` — i1 draws its amber top edge here as `inset 0 2px 0 …`, so no rectangle changes |
|
|
151
|
+
| `--pk-head-text` | `--pk-text` / `--pk-text-muted` | |
|
|
152
|
+
|
|
153
|
+
### Controls (the toolbar's parts)
|
|
154
|
+
|
|
155
|
+
`.btn`, `.segmented`, the pill, the coverage pill, the search trigger, the theme picker, the graph's corner group,
|
|
156
|
+
`.popover-close`, `.outline-modes`, `.read-mark`.
|
|
157
|
+
|
|
158
|
+
| Token | Chain | Factory |
|
|
159
|
+
| --- | --- | --- |
|
|
160
|
+
| `--pk-control-bg` | `--pk-surface` / `--pk-rail` | |
|
|
161
|
+
| `--pk-control-texture` | | `none` |
|
|
162
|
+
| `--pk-control-border` | `--pk-line` | |
|
|
163
|
+
| `--pk-control-shadow` | | `none` |
|
|
164
|
+
| `--pk-control-radius` | `--pk-radius` / `--pk-radius-sm` | |
|
|
165
|
+
| `--pk-control-hover-bg` | `--pk-raised` | |
|
|
166
|
+
| `--pk-control-hover-border` | `--pk-control-border` | |
|
|
167
|
+
|
|
168
|
+
### The sunken well
|
|
169
|
+
|
|
170
|
+
Inputs, the composer, quoted sources, Markdown code and `pre`, the rule rows of Project scope.
|
|
171
|
+
|
|
172
|
+
| Token | Chain | Factory |
|
|
173
|
+
| --- | --- | --- |
|
|
174
|
+
| `--pk-well-bg` | `--pk-surface` / `--pk-ground` / `--pk-rail` | `#22221e`, `#171715` … |
|
|
175
|
+
| `--pk-well-border` | `--pk-line` / `--pk-line-soft` | |
|
|
176
|
+
| `--pk-well-shadow` | | `none` |
|
|
177
|
+
| `--pk-code-bg` | `--pk-raised` | bare `code` (the Markdown one is a well) |
|
|
178
|
+
|
|
179
|
+
### The conversation
|
|
180
|
+
|
|
181
|
+
| Token | Chain | What it is |
|
|
182
|
+
| --- | --- | --- |
|
|
183
|
+
| `--pk-msg-user-bg`, `--pk-msg-user-border` | `--pk-raised`, `--pk-line` | the owner's message bubble (WorkflowKeeper puts it on the accent face) |
|
|
184
|
+
|
|
185
|
+
### The graph
|
|
186
|
+
|
|
187
|
+
Read by `ui/graph-palette.js` into cytoscape and the objects' SVG pictures. Each entry reads the graph token first,
|
|
188
|
+
then the general token in the chain, then the factory value. The kinds' four colours **must** be checked with
|
|
189
|
+
`scripts/palette-check.py` on `--pk-graph-node-bg`.
|
|
190
|
+
|
|
191
|
+
**Write these as literal colours, not `var()` chains into the board.** The palette reads them with
|
|
192
|
+
`getComputedStyle(document.documentElement).getPropertyValue(...)`, which returns a custom property's value *unresolved*:
|
|
193
|
+
a chain that ends in one of `d2`'s `light-dark(light, dark)` tokens (or any `var()`) arrives as that text, is not a colour
|
|
194
|
+
the canvas can use, and falls back to the factory value without a word. `a1` and `d2` found this; their `pk.css` write
|
|
195
|
+
the board's light literals for every `--pk-graph-*` token. The CSS on the page is unaffected (the browser resolves the
|
|
196
|
+
chain there); only what JavaScript reads for the canvas is.
|
|
197
|
+
|
|
198
|
+
| Token | Chain | What it is | Factory |
|
|
199
|
+
| --- | --- | --- | --- |
|
|
200
|
+
| `--pk-graph-bg` | `--pk-ground` | the canvas (i1: `transparent`, the ground's grid shows) | `#141413` |
|
|
201
|
+
| `--pk-graph-intent` / `-plan` / `-work` / `-reality` | `--pk-blue` / `--pk-purple` / `--pk-green` / `--pk-accent` | the four kinds: the object's border, its top band, the legend icon | `#9fbdd5` `#c4b0dc` `#a3c5ac` `#e0a98e` |
|
|
202
|
+
| `--pk-graph-node-bg` | `--pk-card-bg` → `--pk-surface` | the object's face | `#232320` |
|
|
203
|
+
| `--pk-graph-node-text` | `--pk-text-strong` → `--pk-text` | the name | `#e7e7df` |
|
|
204
|
+
| `--pk-graph-node-kind` | (none) | the kind line; unset, it is written in the object's own colour (the factory way); set, in this colour (i1: `--tx1`, "the words in the body colour, the colour on the edges") | unset |
|
|
205
|
+
| `--pk-graph-node-muted` / `-faint` | `--pk-text-muted` / `--pk-text-faint` | progress, struck names, the folder's empty line | `#a5a59b` / `#85857b` |
|
|
206
|
+
| `--pk-graph-node-chamfer`, `-shade`, `-brush`, `-lip`, `-shadow` | (none) | the card's material lent to the objects: a highlight band under the top edge, a shade along the bottom, a hairline texture, a hard lip 3px below, a soft shadow (`dy 6, blur 5`). Any left unset is not drawn; all unset (the factory) means no material and the picture drawn as before. Use the theme's own shadow parts (i1: the parts of `--bezel-chunk` and `--card-brush`) | unset |
|
|
207
|
+
| `--pk-graph-edge` / `-edge-other` / `-edge-related` / `-edge-lit` / `-edge-selected` | `--pk-line-strong` / `--pk-text-faint` / `--pk-text-muted` / `--pk-text-strong` / `--pk-accent-hi` | the lines: structure; other links and dotted inferred; on the focused path; pointed at; selected | `#6e6e66` `#8c8c84` `#cfcfc6` `#e7e7df` `#f0d7a3` |
|
|
208
|
+
| `--pk-graph-questioned` | `--pk-accent` | a `Questioned` relation, always drawn, always loud | `#e3b76f` |
|
|
209
|
+
| `--pk-graph-label-bg`, `-chip-bg`, `-chip-border` | `--pk-ground`, `--pk-surface`, `--pk-line-strong` | the label on a lit line; the link-count chip | `#10100f` `#1c1c1a` `#4a4a43` |
|
|
210
|
+
| `--pk-graph-selection` | (none) | the selected object's border and halo — never one of the kinds (i1: `--tx0`) | `#ffffff` |
|
|
211
|
+
| `--pk-graph-focus` | `--pk-star` | the ring on the focused object | `#ffd54a` |
|
|
212
|
+
| `--pk-graph-diff-added` / `-changed` / `-removed` | `--pk-green` / `--pk-accent` / `--pk-red` | Compare | |
|
|
213
|
+
| `--pk-graph-replaced`, `--pk-graph-danger` | `--pk-line-strong`, `--pk-red` | a replaced object's border; `No established link` | `#5a5a50`, `#dca6a0` |
|
|
214
|
+
| `--pk-graph-group-bg`, `-border`, `-back`, `-back-border`, `-chip` | `--pk-raised`, `--pk-line-strong`, `--pk-surface`, `--pk-line`, `--pk-raised` | a folded group's stacked cards and its count chip | |
|
|
215
|
+
| `--pk-graph-mark-star` / `-flag` / `-note` / `-ask` / `-pending` | `--pk-text-strong` / `--pk-accent` / `--pk-text` / `--pk-accent` / `--pk-blue` | the corner marks ★ ⚑ ✎ ✎ ⟳ — on the objects, in the legend and in the List (`.mark-<key>`); keep them loud | `#f3ead2` `#e3b76f` `#e7e7df` `#e3b76f` `#9fbdd5` |
|
|
216
|
+
|
|
217
|
+
### The List's layers (owner 2026-09-30, from a design study of hers)
|
|
218
|
+
|
|
219
|
+
The List by module has three layers of its own (`ui/k-process.css`): the top — the owner's words, the product, the
|
|
220
|
+
goals — is WorkflowLens's **Sign in card** (a framed plate with an inlaid hairline and four rivets); a module's head is
|
|
221
|
+
its **lane card** (corner brackets, a small caps kicker, the title, the one sentence of its effect); a module's body is a
|
|
222
|
+
**panel** of its own (a1: the dark striped wood). Unset, every token falls back to the section, the raised face and the
|
|
223
|
+
accent, so a theme that sets none of them still gets the three layers in its own colours. `a1` sets all of them from
|
|
224
|
+
WorkflowKeeper's own recipes (cover.css `.loginplate`, `.signin`, `.flab`, `.paper`, `.strip`; board.css `.laneLabel`
|
|
225
|
+
r4/r5) — the one place its layer takes values from the cover rather than the board, at the owner's request.
|
|
226
|
+
|
|
227
|
+
| Token | Chain | What it is | Factory |
|
|
228
|
+
| --- | --- | --- | --- |
|
|
229
|
+
| `--pk-plate-bg`, `--pk-plate-texture` | `--pk-section-bg` → `--pk-surface`; none | the plate's face and its material | `#1c1c1a`, `none` |
|
|
230
|
+
| `--pk-plate-border`, `--pk-plate-border-width`, `--pk-plate-radius`, `--pk-plate-shadow` | `--pk-section-border`; `1px`; `--pk-radius`; `--pk-section-shadow` | its frame and drop | |
|
|
231
|
+
| `--pk-plate-inlay` | `--pk-line-soft` | the hairline inlaid 8px inside the frame | `#2b2b26` |
|
|
232
|
+
| `--pk-plate-rivet`, `--pk-plate-rivet-shadow` | `--pk-line-strong`; none | the four rivets (a colour or a gradient) | `#4b4b42` |
|
|
233
|
+
| `--pk-plate-kicker` | `--pk-accent` | the engraved title (the project's name) and the small caps labels | `#e3b76f` |
|
|
234
|
+
| `--pk-slot-bg`, `--pk-slot-text`, `--pk-slot-border`, `--pk-slot-shadow` | `--pk-well-*`, `--pk-text` | the product's slot on the plate (a1: the vellum `.paper`) | |
|
|
235
|
+
| `--pk-lane-bg`, `--pk-lane-texture` | `--pk-raised`; none | the lane card's face and material | `#242420` |
|
|
236
|
+
| `--pk-lane-border`, `--pk-lane-radius`, `--pk-lane-shadow` | `--pk-line`; `--pk-radius-sm`; none | its frame | `#35352f` |
|
|
237
|
+
| `--pk-lane-corner` | `--pk-accent` | the L corner brackets | `#e3b76f` |
|
|
238
|
+
| `--pk-lane-kicker`, `--pk-lane-title`, `--pk-lane-sub`, `--pk-lane-link` | `--pk-accent`, `--pk-text-strong`, `--pk-text-muted`, `--pk-blue` | the code and counts, the name, the effect, `Intent & basis` | |
|
|
239
|
+
| `--pk-module-bg`, `--pk-module-texture`, `--pk-module-border`, `--pk-module-shadow` | `--pk-section-bg`; none; `--pk-section-border`; none | a module's body | |
|
|
240
|
+
|
|
241
|
+
The sizes are the workbench's (the lane title 15px, the kicker and labels 10.5px): a theme colours and textures the
|
|
242
|
+
layers and may set their font family; it does not resize them.
|
|
243
|
+
|
|
244
|
+
## What the guard does, and what your `pk.css` must do because of it
|
|
245
|
+
|
|
246
|
+
`themes/guard.css` is last and says no colour. It restates the shell's layout (full-height grid, the main view scrolls,
|
|
247
|
+
the fixed layers) and puts the workbench's sizes back where a board's **element** or **class** rule would land on the
|
|
248
|
+
workbench's markup. What it puts back is layout only; the colours those rules carry are yours to restate in `pk.css`.
|
|
249
|
+
|
|
250
|
+
Rules every board has, and what happens to them:
|
|
251
|
+
|
|
252
|
+
| Board rule | Lands on | Guard | Your `pk.css` |
|
|
253
|
+
| --- | --- | --- | --- |
|
|
254
|
+
| `header { position: sticky; top: 0; z-index: 30–40; padding; margin }` | `.section > header`, `.strip header`, the heads of the popover, Keeper and dialog | `position: static; z-index: auto; margin: 0`, the workbench's paddings | the head's colours through `--pk-head-*`; a `border-top` (b1's brass line) would add height — draw it as `inset 0 2px 0` in `--pk-head-shadow` |
|
|
255
|
+
| `h1 { font-size: 14–18px; font-family; color; text-shadow }` | page titles (`.page-head h1`) | `h1` 22px / 600 / `-.4px`; `h2`–`h4` too | colour and text-shadow if you want them (i1: `--tx0`, `0 1px 0 rgba(0,0,0,.5)`) |
|
|
256
|
+
| `body { font; color; background; font-variant-numeric }` | everything | nothing (the guard leaves fonts and colours to the theme) | restate `body { color; font; background }` from your tokens so `pk.css` is the one source; `--pk-font-body` carries the board's size and stack |
|
|
257
|
+
| `code { font-size 10.5–11px; padding; border; background; box-shadow }` | bare `code` (rare; `.mono` is used instead) | nothing | a free look; `--pk-code-bg` if you care |
|
|
258
|
+
| `* { box-sizing }`, `[hidden] { display: none !important }` | the same as the workbench | nothing | nothing |
|
|
259
|
+
| `.legend { padding: 6px 20px; flex-wrap: wrap; gap: 16px; align-items: baseline; background; border-bottom }` | the graph's legend | `flex-wrap: nowrap; align-items: center; gap: 10px; white-space: nowrap; padding: 6px 12px; border-bottom: 0` | background through `--pk-rail` |
|
|
260
|
+
| `.mk { font-size: 12–13px; line-height }` | the marks of a strip row | `font-size: 10.5px; line-height: 1; 16×16` | colours through `--pk-line`, `--pk-text-muted` |
|
|
261
|
+
| `.empty { padding: 36–40px; color; font-style }` | empty states | `padding: 44px 20px; text-align: center; max-width: 64ch; margin: 0 auto` | **`.empty { color: var(--pk-text-muted) }`** — the board's colour is for its own page and may be unreadable on yours |
|
|
262
|
+
| `.fold > summary { margin: 0 -8px; padding; border-radius; display: block }` and `::before { margin-right; color }` | `details.fold` | `margin: 0`; `::before { margin-right: 0 }` (the workbench's `details.fold > summary` outranks the rest) | a free look: the open chevron's colour and the hover face are the board's |
|
|
263
|
+
| `.sub { font-size; color; font-style; opacity }` (a1: serif italic) | nothing — the workbench only has `.page-head .sub`, which outranks it | nothing | a free look |
|
|
264
|
+
| `.toolbar`, `.toolbar label` | nothing — no workbench element carries the class | nothing | nothing |
|
|
265
|
+
| `.mono { font-family }` (i1 only) | `.mono` text | nothing | the same stack as `--pk-font-mono` |
|
|
266
|
+
| `.amber`, `.red`, `.none`, `.open` | nothing — the boards only use them compounded (`.dc.amber`, `.slot.decl.open`) | nothing | nothing |
|
|
267
|
+
| `::-webkit-scrollbar*` (a1, b1), `::selection` (b1) | every scrollbar, the selection | nothing | a free look |
|
|
268
|
+
|
|
269
|
+
Per theme, what was seen (2026-09-21):
|
|
270
|
+
|
|
271
|
+
| Theme | Leaks that hit the workbench (before the guard) | Kept as a free look |
|
|
272
|
+
| --- | --- | --- |
|
|
273
|
+
| `i1` | sticky `header` with panel face and shadow; `h1` 14px; `body` 12.5px + grid; `.legend` wrapping and padded; `.mk` 12px; `.empty` in `--tx3` (3.3:1) | the grid ground (kept on purpose, also under the graph); `.fold` chevron amber when open, hover face `--well`; `.mono` in `ui-monospace` |
|
|
274
|
+
| `b1` | sticky `header` with a 2px brass `border-top` (adds height) and shadow; `h1` 15px; `.legend`; `.mk` 13px; `.empty` in `--fg-3` | `::selection` brass; scrollbars in the board's greens |
|
|
275
|
+
| `d2` | sticky `header`; `h1` 14px; `body` 12.5px; `.legend`; `.mk`; `.empty` in `--fg3`; `color-scheme: light` (wanted: the native controls follow) | `code` with `--elev-groove` |
|
|
276
|
+
| `a1` | sticky `header` with wood gradient, 2px brass `border-bottom`, z-index 40; `h1` 18px serif with text-shadow; `body` 12.5px with `background-attachment: fixed`; `.legend` with brass bottom line; `.mk`; `.empty` in `--ink-sepia` italic (unreadable on walnut) | `.sub` italic serif (only `.page-head .sub`, which outranks it); scrollbars in brass |
|
|
277
|
+
|
|
278
|
+
The class clashes (`amber empty fold legend mk none open red sub toolbar`, `mono` in i1) are the same in all four
|
|
279
|
+
boards; a board updated in WorkflowKeeper is checked again with the script in `themes.test.ts` and this table.
|
|
280
|
+
|
|
281
|
+
## Filling a theme: the order that worked for i1
|
|
282
|
+
|
|
283
|
+
1. Surfaces and text first (`--pk-ground` … `--pk-text-faint`), then measure the contrast of body, muted and faint on
|
|
284
|
+
the card, the panel and the ground (`scripts/ui-theme-check.mjs` prints them).
|
|
285
|
+
2. The accent and the families, the card, the section head, the control, the well: every one from the board's own
|
|
286
|
+
card / bin header / tray tile (as in WorkflowKeeper: each part after the theme's card).
|
|
287
|
+
3. `body`, `.empty`, and the segmented control (`.segmented button`, `.active`) as the board's tray tiles.
|
|
288
|
+
4. The graph: the four kinds from the numbers file, `--pk-graph-node-bg` the card, the material from the card's shadow
|
|
289
|
+
parts, the marks loud, the selection never one of the kinds. Run `palette-check.py` on your four with `--bg` your
|
|
290
|
+
node face, and the theme check for the readability mark.
|
|
291
|
+
5. Screenshots at 1280×800: default view, popover + docked Keeper, List, a dialog, Project scope.
|