create-pmos 0.65.0 → 0.65.1
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/package.json +1 -1
- package/payload/KIT-VERSION +1 -1
- package/payload/planning/evals/eval-calibration.md +1 -1
- package/payload/planning/okrs/health-budgets.md +1 -1
- package/payload/state/discoveries.md +1 -1
- package/workspace/guide/docs.md +59 -6
- package/workspace/guide/initiatives-screen.md +8 -0
- package/workspace/lib/doc-library.js +131 -0
- package/workspace/package-lock.json +1 -1
- package/workspace/package.json +1 -1
- package/workspace/public/pm/components.css +307 -0
- package/workspace/public/pm/dest-docs.js +144 -44
- package/workspace/public/pm/doc-graph.js +272 -0
- package/workspace/public/pm/initiative-sheet.js +24 -2
- package/workspace/public/pm/initiatives-copy.js +8 -0
- package/workspace/server.js +21 -0
- package/workspace/test-lists/docs-library-graph.json +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.65.
|
|
3
|
+
"version": "0.65.1",
|
|
4
4
|
"description": "Scaffold the PMOS portable kit — run the PMOS product-management workflow (initiative → lane-fit spec → contract → build → three gates, with work-type lanes for features, research, maintenance, incidents, and content) entirely inside your own repo, with all state as git-versioned files. Includes `workflow-sweep`, which audits the state every board derives from and separates real defects from work you simply have not done. `create-pmos workspace` also installs + launches the local Terminal Workspace GUI. No database, no network, no telemetry.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
package/payload/KIT-VERSION
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Eval calibration log"
|
|
4
4
|
description: "Append-only record of every PM override of an evaluator verdict."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-07T00:
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-07T00:49:12Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Health budgets"
|
|
4
4
|
description: "Closed registry of health-budget anchors (D59), edited only by the PM."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-07T00:
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-07T00:49:12Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Discoveries and friction log"
|
|
4
4
|
description: "Append-only record of what runs discovered and where the process rubbed."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-07T00:
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-07T00:49:12Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
package/workspace/guide/docs.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
type: Guide
|
|
3
|
-
title: "Docs:
|
|
3
|
+
title: "Docs: every repository file by category, the files each one links to, and whether each copy in Google Docs or Confluence is the same"
|
|
4
4
|
timestamp: 2026-10-06
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -8,15 +8,65 @@ timestamp: 2026-10-06
|
|
|
8
8
|
|
|
9
9
|
Open Docs from the top bar, from ⌘K (**Docs**), or press `g d`.
|
|
10
10
|
|
|
11
|
-
Docs lists every
|
|
11
|
+
Docs lists every markdown file in the repository by category. For each file, it shows the files that it links to and the files that link to it. For each document that has a copy in Google Docs or Confluence, it shows whether the repository version (PMOS) or the copy is newer. You can then use one version over the other, one copy at a time.
|
|
12
12
|
|
|
13
13
|
Nothing syncs by itself. A copy changes only when you press a button for that copy.
|
|
14
14
|
|
|
15
15
|
## The list
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Docs has three views:
|
|
18
18
|
|
|
19
|
-
**Needs you** shows the copies that are not the same.
|
|
19
|
+
- **Needs you** shows the copies that are not the same.
|
|
20
|
+
- **With a copy** shows every copy.
|
|
21
|
+
- **All files** shows every markdown file in the repository.
|
|
22
|
+
|
|
23
|
+
The copies are grouped by category.
|
|
24
|
+
|
|
25
|
+
## Categories
|
|
26
|
+
|
|
27
|
+
The category of a file comes from its folder, then from its frontmatter `type`. You do not tag a file.
|
|
28
|
+
|
|
29
|
+
| Category | Files |
|
|
30
|
+
|---|---|
|
|
31
|
+
| Specs | `planning/prd/`, or `type: PRD` |
|
|
32
|
+
| Initiatives | `planning/initiatives/` |
|
|
33
|
+
| Decisions | the ADR folder, `DECISIONS.md`, `pm-decisions.md` |
|
|
34
|
+
| Research & plans | the other `planning/` folders |
|
|
35
|
+
| Explainers | `planning/explainers/` |
|
|
36
|
+
| Evals | `planning/evals/`, or `type: Rubric` |
|
|
37
|
+
| Knowledge | the other `okf/` folders |
|
|
38
|
+
| Guides & rules | all other files |
|
|
39
|
+
| Prototypes | `prototype/` |
|
|
40
|
+
|
|
41
|
+
## All files
|
|
42
|
+
|
|
43
|
+
**All files** opens with one line for each category: its name, its number of files, and "N need you" when a copy in it is not the same. Press a category to open it. It shows its first 20 files: the files with a copy first, then the newest. Press **Show all** to see the rest.
|
|
44
|
+
|
|
45
|
+
A file with no copy is one line: its title, the short name of its initiative (or its path when no initiative owns it), and the date of its last edit. **Add copy** shows when you point at the line, and always on a touch screen.
|
|
46
|
+
|
|
47
|
+
## The links of a file
|
|
48
|
+
|
|
49
|
+
Press a file in **All files** to see its links:
|
|
50
|
+
|
|
51
|
+
- on the left, the files that link to it;
|
|
52
|
+
- in the middle, the file;
|
|
53
|
+
- on the right, the files that it links to.
|
|
54
|
+
|
|
55
|
+
Each side shows at most 7 files. A dashed line is a decision that the file cites. Press a file in the graph to put it in the middle. Press **Back** to go back. **Open file** opens the file in the reader.
|
|
56
|
+
|
|
57
|
+
**List all N links** lists every link in both directions, grouped by kind.
|
|
58
|
+
|
|
59
|
+
A link counts only when a file writes it:
|
|
60
|
+
|
|
61
|
+
- a markdown link to another file;
|
|
62
|
+
- a path in the frontmatter, such as `prd_path`;
|
|
63
|
+
- the `initiative_id` of the file;
|
|
64
|
+
- the anchor (`parent_kr`);
|
|
65
|
+
- a decision number, such as D65.
|
|
66
|
+
|
|
67
|
+
PMOS does not guess a link from similar words. `pmos kg neighbors <path>` shows the same links in the terminal.
|
|
68
|
+
|
|
69
|
+
On a phone the graph is not shown. The list is shown.
|
|
20
70
|
|
|
21
71
|
Each copy is one line:
|
|
22
72
|
|
|
@@ -49,7 +99,8 @@ Click a line to see its details:
|
|
|
49
99
|
- the copy: its address and its last edit;
|
|
50
100
|
- a **Use … version** button for each side;
|
|
51
101
|
- the first difference that the compare found, when it found one;
|
|
52
|
-
- **Compare again** and **Stop tracking this copy
|
|
102
|
+
- **Compare again** and **Stop tracking this copy**;
|
|
103
|
+
- the links of the file, as in **All files**.
|
|
53
104
|
|
|
54
105
|
**Stop tracking this copy** removes the link. The copy is not deleted. Press **Undo** in the message to link it again.
|
|
55
106
|
|
|
@@ -72,7 +123,9 @@ A compare runs only when you press it. There is no schedule. An edit to a reposi
|
|
|
72
123
|
|
|
73
124
|
## Add a copy
|
|
74
125
|
|
|
75
|
-
Type in
|
|
126
|
+
Type in the search box. The search finds every markdown file in the repository by its title, its path or the name of its initiative, also files that have no copy. The results are grouped by category, and the match is marked. Press Esc to clear the search.
|
|
127
|
+
|
|
128
|
+
Press **Add copy** on a file with no copy.
|
|
76
129
|
|
|
77
130
|
Press **Add copy**, paste the address of the Google Doc or Confluence page, and press Enter. A document can have one Google Doc and one Confluence page.
|
|
78
131
|
|
|
@@ -76,6 +76,14 @@ The Files tab lists every file that belongs to the initiative, in three groups:
|
|
|
76
76
|
- **Linked documents.** The documents the library joins to the initiative: a document that declares its `initiative_id`, a document the record names, a file name that starts with the id.
|
|
77
77
|
- **Changed for it in git.** The files that the commits for this initiative changed and that still exist. These are its own branch, trunk commits that name the id, and merged changes that carried its documents. The file opens read-only.
|
|
78
78
|
|
|
79
|
+
**List · Graph** at the top of the tab changes the view. **List** shows the groups above. **Graph** shows the files of the initiative as a network:
|
|
80
|
+
|
|
81
|
+
- a filled dot is a markdown file of the initiative: a file that the record names, or a linked document. Its label is its role, such as PRD, Rubric or Explainer;
|
|
82
|
+
- a hollow dot is a file outside the initiative that one of its files links to, or that links to one of its files. A decision shows its number, such as D65;
|
|
83
|
+
- a line is a link that a file writes. A dashed line is a decision that a file cites.
|
|
84
|
+
|
|
85
|
+
The graph shows at most 14 outside files, the most-linked first. The legend says how many are left out. Press a dot to mark its lines and to see its title, its path and **Open file**. The same files and links always give the same picture. On a phone the list is shown.
|
|
86
|
+
|
|
79
87
|
### A breakdown read from the spec
|
|
80
88
|
|
|
81
89
|
When nobody has planned items for an initiative, the Breakdown tab shows the items of its spec above the empty Breakdown: the user stories, or the entries of the Requirements section of the PRD that the record names. Each item shows the state of the whole initiative: **Done** after you accept it, **Built** when its build is on the trunk, and **Todo** before that. These items are read-only and nothing is written. To track an item, add it to the Breakdown. The card and the row show the same count, for example "0 of 8 · from board-delivery-truth-prd.md".
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/* doc-library.js — every tracked markdown file of a workspace, by category, with its initiative and the links between the
|
|
3
|
+
* files (initiative docs-library-graph, PRD R-L1 to R-L5).
|
|
4
|
+
*
|
|
5
|
+
* The links are the knowledge graph's (lib/kg.js through kg-cli `graphFor`): a markdown link, a path in the header, the
|
|
6
|
+
* initiative, the anchor, a decision number. Nothing is inferred from wording. A category is derived from where a file
|
|
7
|
+
* lives, then from its frontmatter type; it is never written into a file. The result is kept in memory per repository
|
|
8
|
+
* state (git HEAD plus the uncommitted markdown files), and the kg cache stays in the runtime state directory. Nothing
|
|
9
|
+
* here writes into the repository or reads the network.
|
|
10
|
+
*/
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const { execFileSync } = require('child_process');
|
|
14
|
+
const kgCli = require('./kg-cli');
|
|
15
|
+
|
|
16
|
+
/* [key, name] in display order */
|
|
17
|
+
const CATEGORIES = [
|
|
18
|
+
['spec', 'Specs'], ['ini', 'Initiatives'], ['dec', 'Decisions'], ['res', 'Research & plans'], ['exp', 'Explainers'],
|
|
19
|
+
['eval', 'Evals'], ['know', 'Knowledge'], ['guide', 'Guides & rules'], ['proto', 'Prototypes'],
|
|
20
|
+
];
|
|
21
|
+
/* the link kinds, in the order the client numbers them */
|
|
22
|
+
const KINDS = ['link', 'header', 'initiative', 'anchor', 'cites'];
|
|
23
|
+
|
|
24
|
+
const BY_TYPE = { prd: 'spec', initiative: 'ini', adr: 'dec', decision: 'dec', rubric: 'eval', eval: 'eval', verdict: 'eval', explainer: 'exp' };
|
|
25
|
+
|
|
26
|
+
/** The category of one file: where it lives first, then its frontmatter type, then Guides & rules. */
|
|
27
|
+
function categoryOf(rel, type) {
|
|
28
|
+
const p = String(rel || '');
|
|
29
|
+
if (/^prototype\//.test(p)) return 'proto';
|
|
30
|
+
if (/(^|\/)planning\/initiatives\//.test(p)) return 'ini';
|
|
31
|
+
if (/(^|\/)planning\/prd\//.test(p)) return 'spec';
|
|
32
|
+
if (/(^|\/)planning\/evals\//.test(p)) return 'eval';
|
|
33
|
+
if (/(^|\/)planning\/explainers\//.test(p)) return 'exp';
|
|
34
|
+
if (/^okf\/products\/[^/]+\/adr\//.test(p) || /^(DECISIONS|pm-decisions)\.md$/.test(p) || /(^|\/)planning\/decisions-/.test(p)) return 'dec';
|
|
35
|
+
if (/(^|\/)planning\//.test(p)) return 'res';
|
|
36
|
+
if (/^okf\//.test(p)) return 'know';
|
|
37
|
+
const t = String(type || '').trim().toLowerCase();
|
|
38
|
+
return BY_TYPE[t] || 'guide';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** A short name for an initiative title: no "PRD:" prefix, the part before the first dash or colon, at most 44 characters. */
|
|
42
|
+
function shortTitle(title) {
|
|
43
|
+
let t = String(title || '').trim();
|
|
44
|
+
t = t.replace(/^(PRD|Initiative|Chore|Spike|Prototype Brief|Discovery|Incident|Content brief|Feature)\s*(?:[:—–]|\s-\s)\s*/i, '');
|
|
45
|
+
t = t.split(/\s[—–]\s|:\s|\.\s/)[0].trim();
|
|
46
|
+
if (t.length > 44) { const cut = t.slice(0, 43); t = (cut.includes(' ') ? cut.replace(/\s+\S*$/, '') : cut) + '…'; }
|
|
47
|
+
return t ? t[0].toUpperCase() + t.slice(1) : '';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The kind of one kg edge, or null for a kind the library does not show. */
|
|
51
|
+
function kindOf(e) {
|
|
52
|
+
if (e.type === 'link') return 'link';
|
|
53
|
+
if (e.type === 'initiative') return 'initiative';
|
|
54
|
+
if (e.type === 'anchor') return 'anchor';
|
|
55
|
+
if (e.type === 'cites') return e.typed ? 'header' : 'cites';
|
|
56
|
+
if (e.type === 'frontmatter' || e.type === 'supersedes' || e.type === 'amended_by') return 'header';
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const git = (cwd, args) => execFileSync('git', args, { cwd, encoding: 'utf8', maxBuffer: 256 * 1024 * 1024, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
61
|
+
|
|
62
|
+
/** path → YYYY-MM-DD of its last commit (one git log; merge commits are skipped, their changes are in their parents). */
|
|
63
|
+
function commitDates(root) {
|
|
64
|
+
const out = new Map();
|
|
65
|
+
let text = '';
|
|
66
|
+
try { text = git(root, ['log', '--format=%x01%cs', '--name-only', '--', '*.md']); } catch { return out; }
|
|
67
|
+
let day = null;
|
|
68
|
+
for (const line of text.split('\n')) {
|
|
69
|
+
if (line.startsWith('\x01')) { day = line.slice(1); continue; }
|
|
70
|
+
if (line && day && !out.has(line)) out.set(line, day);
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** The uncommitted markdown files: their date is the file's own. */
|
|
76
|
+
function dirtyPaths(root) {
|
|
77
|
+
try {
|
|
78
|
+
return new Set(git(root, ['status', '--porcelain', '-z', '--untracked-files=no']).split('\0').filter(Boolean)
|
|
79
|
+
.map((l) => l.slice(3)).filter((p) => /\.md$/.test(p)));
|
|
80
|
+
} catch { return new Set(); }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const cache = new Map(); // root -> { key, value }
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The library of one repository.
|
|
87
|
+
* owners: () => Map path → { id, title } — the library's own attribution (server.js initiativeIndex), real initiatives
|
|
88
|
+
* only. It is called only when the repository state changed, because the index reads every planning file.
|
|
89
|
+
* Returns { ok, files: [{path,title,category,date,initiative}], initiatives: {id: {title, short}}, kinds, edges: [[from, to, kind]], categories }.
|
|
90
|
+
* `from` and `to` index `files`; `kind` indexes `kinds`.
|
|
91
|
+
*/
|
|
92
|
+
function library(root, { stateDir, owners = () => new Map() } = {}) {
|
|
93
|
+
let key;
|
|
94
|
+
try { key = kgCli.cacheKey(root); } catch (e) { return { ok: false, error: 'this workspace is not a git repository, so there are no tracked files to list' }; }
|
|
95
|
+
const hit = cache.get(root);
|
|
96
|
+
if (hit && hit.key === key) return hit.value;
|
|
97
|
+
const { graph, unreadable } = kgCli.graphFor(root, stateDir ? { stateDir } : {});
|
|
98
|
+
let own = new Map();
|
|
99
|
+
try { own = owners() || new Map(); } catch { /* no attribution: every file has no initiative */ }
|
|
100
|
+
const dates = commitDates(root);
|
|
101
|
+
const dirty = dirtyPaths(root);
|
|
102
|
+
const dayOf = (rel) => {
|
|
103
|
+
if (!dirty.has(rel) && dates.has(rel)) return dates.get(rel);
|
|
104
|
+
try { return fs.statSync(path.join(root, rel)).mtime.toISOString().slice(0, 10); } catch { return dates.get(rel) || null; }
|
|
105
|
+
};
|
|
106
|
+
// An initiative is named by its record's title (planning/initiatives/<id>.md), else by the index's title for it.
|
|
107
|
+
const titleOf = new Map(graph.nodes.map((n) => [n.id, n.title]));
|
|
108
|
+
const named = (o) => String(titleOf.get(`planning/initiatives/${o.id}.md`) || o.title || o.id);
|
|
109
|
+
const initiatives = {};
|
|
110
|
+
const files = graph.nodes.map((n) => {
|
|
111
|
+
const o = own.get(n.id) || null;
|
|
112
|
+
if (o && !initiatives[o.id]) initiatives[o.id] = { title: named(o), short: shortTitle(named(o)) };
|
|
113
|
+
return { path: n.id, title: String(n.title || n.id), category: categoryOf(n.id, n.type), date: dayOf(n.id), initiative: o ? o.id : null };
|
|
114
|
+
});
|
|
115
|
+
const index = new Map(files.map((f, i) => [f.path, i]));
|
|
116
|
+
const seen = new Set();
|
|
117
|
+
const edges = [];
|
|
118
|
+
for (const e of graph.edges) {
|
|
119
|
+
const k = kindOf(e);
|
|
120
|
+
if (!k || e.from === e.to || !index.has(e.from) || !index.has(e.to)) continue;
|
|
121
|
+
const a = index.get(e.from); const b = index.get(e.to); const ki = KINDS.indexOf(k);
|
|
122
|
+
const id = a + '|' + b + '|' + ki;
|
|
123
|
+
if (seen.has(id)) continue;
|
|
124
|
+
seen.add(id); edges.push([a, b, ki]);
|
|
125
|
+
}
|
|
126
|
+
const value = { ok: true, key, files, initiatives, kinds: KINDS, edges, categories: CATEGORIES.map(([k, name]) => ({ key: k, name })), unreadable: unreadable || [] };
|
|
127
|
+
cache.set(root, { key, value });
|
|
128
|
+
return value;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
module.exports = { CATEGORIES, KINDS, categoryOf, shortTitle, kindOf, library, commitDates };
|
package/workspace/package.json
CHANGED
|
@@ -1784,6 +1784,17 @@
|
|
|
1784
1784
|
.in-derived {
|
|
1785
1785
|
margin-bottom: 16px;
|
|
1786
1786
|
}
|
|
1787
|
+
.in-files__bar {
|
|
1788
|
+
display: flex;
|
|
1789
|
+
flex-wrap: wrap;
|
|
1790
|
+
align-items: center;
|
|
1791
|
+
gap: 12px;
|
|
1792
|
+
margin-bottom: 12px;
|
|
1793
|
+
}
|
|
1794
|
+
.in-files__hint {
|
|
1795
|
+
font-size: 12px;
|
|
1796
|
+
color: var(--muted-foreground);
|
|
1797
|
+
}
|
|
1787
1798
|
.in-files__title,
|
|
1788
1799
|
.in-derived__title {
|
|
1789
1800
|
margin: 0;
|
|
@@ -4503,6 +4514,302 @@
|
|
|
4503
4514
|
}
|
|
4504
4515
|
}
|
|
4505
4516
|
|
|
4517
|
+
/* Docs › All files (docs-library-graph): folding categories, one line per file, the match marked, Add copy on hover. */
|
|
4518
|
+
.rp-docs-folds {
|
|
4519
|
+
display: flex;
|
|
4520
|
+
flex-direction: column;
|
|
4521
|
+
}
|
|
4522
|
+
.rp-docs-hint {
|
|
4523
|
+
margin: 0;
|
|
4524
|
+
font-size: 12px;
|
|
4525
|
+
color: var(--muted-foreground);
|
|
4526
|
+
}
|
|
4527
|
+
.rp-docs-mark {
|
|
4528
|
+
background: var(--accent);
|
|
4529
|
+
color: var(--foreground);
|
|
4530
|
+
}
|
|
4531
|
+
.rp-docs-path {
|
|
4532
|
+
font-family: var(--mono);
|
|
4533
|
+
}
|
|
4534
|
+
.rp-docs-showall {
|
|
4535
|
+
align-self: flex-start;
|
|
4536
|
+
margin-top: 4px;
|
|
4537
|
+
}
|
|
4538
|
+
.rp-docs-links {
|
|
4539
|
+
grid-column: 1 / -1;
|
|
4540
|
+
padding: 4px 0 16px 24px;
|
|
4541
|
+
}
|
|
4542
|
+
.rp-docs-detail > .rp-docs-links {
|
|
4543
|
+
padding-left: 0;
|
|
4544
|
+
}
|
|
4545
|
+
/* Add copy shows on hover or focus where the pointer can hover; without hover (a touch screen) it always shows. */
|
|
4546
|
+
@media (hover: hover) and (pointer: fine) {
|
|
4547
|
+
.rp-docs-addcopy {
|
|
4548
|
+
opacity: 0;
|
|
4549
|
+
}
|
|
4550
|
+
.rp-docs-row:hover .rp-docs-addcopy,
|
|
4551
|
+
.rp-docs-row:focus-within .rp-docs-addcopy {
|
|
4552
|
+
opacity: 1;
|
|
4553
|
+
}
|
|
4554
|
+
}
|
|
4555
|
+
|
|
4556
|
+
/* The links of one file: who links here on the left, what it links to on the right, lines behind real buttons. */
|
|
4557
|
+
.rp-graph {
|
|
4558
|
+
display: flex;
|
|
4559
|
+
flex-direction: column;
|
|
4560
|
+
align-items: flex-start;
|
|
4561
|
+
gap: 8px;
|
|
4562
|
+
min-width: 0;
|
|
4563
|
+
}
|
|
4564
|
+
.rp-graph__head {
|
|
4565
|
+
display: flex;
|
|
4566
|
+
flex-wrap: wrap;
|
|
4567
|
+
align-items: center;
|
|
4568
|
+
gap: 8px;
|
|
4569
|
+
width: 100%;
|
|
4570
|
+
}
|
|
4571
|
+
.rp-graph__title {
|
|
4572
|
+
flex: 1 1 240px;
|
|
4573
|
+
min-width: 0;
|
|
4574
|
+
font-size: 14px;
|
|
4575
|
+
}
|
|
4576
|
+
.rp-graph__pic {
|
|
4577
|
+
position: relative;
|
|
4578
|
+
width: 100%;
|
|
4579
|
+
max-width: 820px;
|
|
4580
|
+
aspect-ratio: var(--rp-ar);
|
|
4581
|
+
margin-top: 16px;
|
|
4582
|
+
}
|
|
4583
|
+
.rp-graph__lines {
|
|
4584
|
+
position: absolute;
|
|
4585
|
+
inset: 0;
|
|
4586
|
+
width: 100%;
|
|
4587
|
+
height: 100%;
|
|
4588
|
+
overflow: visible;
|
|
4589
|
+
}
|
|
4590
|
+
.rp-graph__edge {
|
|
4591
|
+
fill: none;
|
|
4592
|
+
stroke: var(--muted-foreground);
|
|
4593
|
+
stroke-width: 1;
|
|
4594
|
+
}
|
|
4595
|
+
.rp-graph__edge--cites {
|
|
4596
|
+
stroke-dasharray: 4 3;
|
|
4597
|
+
}
|
|
4598
|
+
.rp-graph__node,
|
|
4599
|
+
.rp-graph__centre,
|
|
4600
|
+
.rp-graph__more {
|
|
4601
|
+
position: absolute;
|
|
4602
|
+
left: calc(var(--rp-x) * 1%);
|
|
4603
|
+
top: calc(var(--rp-y) * 1%);
|
|
4604
|
+
width: calc(var(--rp-w) * 1%);
|
|
4605
|
+
transform: translateY(-50%);
|
|
4606
|
+
}
|
|
4607
|
+
.rp-graph__node,
|
|
4608
|
+
.rp-graph__item,
|
|
4609
|
+
.rp-net__node {
|
|
4610
|
+
letter-spacing: normal;
|
|
4611
|
+
text-transform: none;
|
|
4612
|
+
}
|
|
4613
|
+
.rp-graph__node {
|
|
4614
|
+
justify-content: flex-start;
|
|
4615
|
+
background: var(--background);
|
|
4616
|
+
font-size: 12px;
|
|
4617
|
+
}
|
|
4618
|
+
.rp-graph__node--in {
|
|
4619
|
+
justify-content: flex-end;
|
|
4620
|
+
}
|
|
4621
|
+
.rp-graph__node--cites {
|
|
4622
|
+
border-style: dashed;
|
|
4623
|
+
}
|
|
4624
|
+
.rp-graph__node:focus-visible {
|
|
4625
|
+
outline: 2px solid color-mix(in oklab, var(--ring) 50%, var(--foreground));
|
|
4626
|
+
outline-offset: 2px;
|
|
4627
|
+
}
|
|
4628
|
+
.rp-graph__centre {
|
|
4629
|
+
padding: 6px 10px;
|
|
4630
|
+
border: 1px solid var(--foreground);
|
|
4631
|
+
background: var(--card);
|
|
4632
|
+
font-size: 12px;
|
|
4633
|
+
font-weight: 600;
|
|
4634
|
+
text-align: center;
|
|
4635
|
+
white-space: nowrap;
|
|
4636
|
+
overflow: hidden;
|
|
4637
|
+
text-overflow: ellipsis;
|
|
4638
|
+
}
|
|
4639
|
+
.rp-graph__more {
|
|
4640
|
+
font-size: 12px;
|
|
4641
|
+
color: var(--muted-foreground);
|
|
4642
|
+
}
|
|
4643
|
+
.rp-graph__more--in {
|
|
4644
|
+
text-align: right;
|
|
4645
|
+
}
|
|
4646
|
+
.rp-graph__cap {
|
|
4647
|
+
position: absolute;
|
|
4648
|
+
top: -20px;
|
|
4649
|
+
font-size: 11px;
|
|
4650
|
+
color: var(--muted-foreground);
|
|
4651
|
+
}
|
|
4652
|
+
.rp-graph__cap--out {
|
|
4653
|
+
right: 0;
|
|
4654
|
+
}
|
|
4655
|
+
.rp-graph__lists {
|
|
4656
|
+
display: grid;
|
|
4657
|
+
grid-template-columns: 1fr 1fr;
|
|
4658
|
+
gap: 16px;
|
|
4659
|
+
width: 100%;
|
|
4660
|
+
}
|
|
4661
|
+
.rp-graph__col,
|
|
4662
|
+
.rp-graph__group {
|
|
4663
|
+
display: flex;
|
|
4664
|
+
flex-direction: column;
|
|
4665
|
+
align-items: flex-start;
|
|
4666
|
+
gap: 2px;
|
|
4667
|
+
min-width: 0;
|
|
4668
|
+
}
|
|
4669
|
+
.rp-graph__col {
|
|
4670
|
+
gap: 12px;
|
|
4671
|
+
}
|
|
4672
|
+
.rp-graph__label {
|
|
4673
|
+
font-size: 12px;
|
|
4674
|
+
color: var(--muted-foreground);
|
|
4675
|
+
}
|
|
4676
|
+
.rp-graph__item {
|
|
4677
|
+
max-width: 100%;
|
|
4678
|
+
justify-content: flex-start;
|
|
4679
|
+
}
|
|
4680
|
+
.rp-graph__hint {
|
|
4681
|
+
margin: 0;
|
|
4682
|
+
font-size: 12px;
|
|
4683
|
+
color: var(--muted-foreground);
|
|
4684
|
+
}
|
|
4685
|
+
@media (max-width: 760px) {
|
|
4686
|
+
.rp-graph__lists {
|
|
4687
|
+
grid-template-columns: 1fr;
|
|
4688
|
+
}
|
|
4689
|
+
}
|
|
4690
|
+
|
|
4691
|
+
/* An initiative's files as a network: filled dots are its files, hollow dots the outside files they link to. */
|
|
4692
|
+
.rp-net {
|
|
4693
|
+
display: flex;
|
|
4694
|
+
flex-direction: column;
|
|
4695
|
+
gap: 8px;
|
|
4696
|
+
}
|
|
4697
|
+
.rp-net__pic {
|
|
4698
|
+
position: relative;
|
|
4699
|
+
width: 100%;
|
|
4700
|
+
aspect-ratio: var(--rp-ar);
|
|
4701
|
+
border: 1px solid var(--border);
|
|
4702
|
+
}
|
|
4703
|
+
.rp-net__lines {
|
|
4704
|
+
position: absolute;
|
|
4705
|
+
inset: 0;
|
|
4706
|
+
width: 100%;
|
|
4707
|
+
height: 100%;
|
|
4708
|
+
}
|
|
4709
|
+
.rp-net__edge {
|
|
4710
|
+
stroke: var(--border);
|
|
4711
|
+
stroke-width: 1;
|
|
4712
|
+
}
|
|
4713
|
+
.rp-net__edge--in {
|
|
4714
|
+
stroke: var(--muted-foreground);
|
|
4715
|
+
}
|
|
4716
|
+
.rp-net__edge--cites {
|
|
4717
|
+
stroke-dasharray: 4 3;
|
|
4718
|
+
}
|
|
4719
|
+
.rp-net__edge--hot {
|
|
4720
|
+
stroke: var(--foreground);
|
|
4721
|
+
stroke-width: 2;
|
|
4722
|
+
}
|
|
4723
|
+
.rp-net__node {
|
|
4724
|
+
position: absolute;
|
|
4725
|
+
left: calc(var(--rp-x) * 1%);
|
|
4726
|
+
top: calc(var(--rp-y) * 1%);
|
|
4727
|
+
height: auto;
|
|
4728
|
+
min-height: 0;
|
|
4729
|
+
padding: 2px 4px 2px 0;
|
|
4730
|
+
transform: translate(-5px, -50%);
|
|
4731
|
+
font-size: 11px;
|
|
4732
|
+
}
|
|
4733
|
+
.rp-net__node--in {
|
|
4734
|
+
transform: translate(-7px, -50%);
|
|
4735
|
+
}
|
|
4736
|
+
.rp-net__node:focus-visible {
|
|
4737
|
+
outline: 2px solid color-mix(in oklab, var(--ring) 50%, var(--foreground));
|
|
4738
|
+
outline-offset: 2px;
|
|
4739
|
+
}
|
|
4740
|
+
.rp-net__node--sel .rp-net__label {
|
|
4741
|
+
font-weight: 600;
|
|
4742
|
+
text-decoration: underline;
|
|
4743
|
+
text-underline-offset: 2px;
|
|
4744
|
+
}
|
|
4745
|
+
.rp-net__label {
|
|
4746
|
+
max-width: 140px;
|
|
4747
|
+
overflow: hidden;
|
|
4748
|
+
text-overflow: ellipsis;
|
|
4749
|
+
white-space: nowrap;
|
|
4750
|
+
color: var(--muted-foreground);
|
|
4751
|
+
}
|
|
4752
|
+
.rp-net__node--sel .rp-net__label,
|
|
4753
|
+
.rp-net__dot--in + .rp-net__label {
|
|
4754
|
+
color: var(--foreground);
|
|
4755
|
+
}
|
|
4756
|
+
.rp-net__dot {
|
|
4757
|
+
display: inline-block;
|
|
4758
|
+
flex: none;
|
|
4759
|
+
margin-right: 4px;
|
|
4760
|
+
vertical-align: middle;
|
|
4761
|
+
width: 10px;
|
|
4762
|
+
height: 10px;
|
|
4763
|
+
border: 1px solid var(--muted-foreground);
|
|
4764
|
+
border-radius: 50%;
|
|
4765
|
+
background: var(--background);
|
|
4766
|
+
}
|
|
4767
|
+
.rp-net__dot--in {
|
|
4768
|
+
width: 14px;
|
|
4769
|
+
height: 14px;
|
|
4770
|
+
border-color: var(--foreground);
|
|
4771
|
+
background: var(--foreground);
|
|
4772
|
+
}
|
|
4773
|
+
.rp-net__dash {
|
|
4774
|
+
display: inline-block;
|
|
4775
|
+
width: 18px;
|
|
4776
|
+
border-top: 1px dashed var(--muted-foreground);
|
|
4777
|
+
}
|
|
4778
|
+
.rp-net__legend {
|
|
4779
|
+
display: flex;
|
|
4780
|
+
flex-wrap: wrap;
|
|
4781
|
+
align-items: center;
|
|
4782
|
+
gap: 16px;
|
|
4783
|
+
font-size: 12px;
|
|
4784
|
+
color: var(--muted-foreground);
|
|
4785
|
+
}
|
|
4786
|
+
.rp-net__legend > span {
|
|
4787
|
+
display: inline-flex;
|
|
4788
|
+
align-items: center;
|
|
4789
|
+
gap: 6px;
|
|
4790
|
+
}
|
|
4791
|
+
.rp-net__sel {
|
|
4792
|
+
display: flex;
|
|
4793
|
+
flex-wrap: wrap;
|
|
4794
|
+
align-items: center;
|
|
4795
|
+
gap: 12px;
|
|
4796
|
+
padding: 8px 12px;
|
|
4797
|
+
border: 1px solid var(--border);
|
|
4798
|
+
}
|
|
4799
|
+
.rp-net__sel-text {
|
|
4800
|
+
display: flex;
|
|
4801
|
+
flex: 1 1 240px;
|
|
4802
|
+
flex-direction: column;
|
|
4803
|
+
min-width: 0;
|
|
4804
|
+
font-size: 14px;
|
|
4805
|
+
}
|
|
4806
|
+
.rp-net__path {
|
|
4807
|
+
font-family: var(--mono);
|
|
4808
|
+
font-size: 12px;
|
|
4809
|
+
color: var(--muted-foreground);
|
|
4810
|
+
overflow-wrap: anywhere;
|
|
4811
|
+
}
|
|
4812
|
+
|
|
4506
4813
|
/* People (people-directory): what waits for the PM, the list beside the chosen person's page. */
|
|
4507
4814
|
.rp-people {
|
|
4508
4815
|
display: flex;
|