@bongos/core 1.19.1052 → 1.19.1054
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/.bongos-core.json +33 -33
- package/docs/copy-inventory.md +100 -114
- package/docs/copy-registry.json +163 -401
- package/docs/module-api-changelog.md +4 -0
- package/modules/hall-ui/public/goal-map.html +18 -24
- package/modules/hall-ui/public/goal-map.states.json +2 -2
- package/modules/hall-ui/public/sky-data.js +22 -18
- package/modules/hall-ui/public/sky-interaction.js +23 -22
- package/modules/hall-ui/public/sky-layout.js +5 -5
- package/modules/hall-ui/public/sky-panel.js +3 -2
- package/modules/hall-ui/public/sky-tour.js +37 -26
- package/modules/hall-ui/public/thinking.css +2 -0
- package/modules/hall-ui/public/thinking.html +16 -25
- package/modules/hall-ui/public/thinking.states.json +1 -1
- package/modules/hall-ui/records/sky.md +11 -0
- package/modules/provisioning/control-plane-guard.js +14 -1
- package/modules/provisioning/provisioning.js +5 -4
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/scripts/gds/provision-units.js +38 -2
- package/scripts/gds/provision.js +6 -6
- package/src/module-api.js +1 -1
- package/tests/core_upgrade_door.mjs +16 -2
- package/tests/goal_map_page.mjs +49 -25
- package/tests/hall_sky.mjs +49 -12
- package/tests/helpers.mjs +2 -1
- package/tests/provision_settings_apply.mjs +84 -0
- package/tests/provisioning_settings_apply.mjs +50 -5
|
@@ -2591,5 +2591,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
2591
2591
|
landed since 1.19.1050 with no explicit bump. run 36369343116. (task 1002620)
|
|
2592
2592
|
1.19.1052 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2593
2593
|
landed since 1.19.1051 with no explicit bump. run 36372902384. (task 1002620)
|
|
2594
|
+
1.19.1053 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2595
|
+
landed since 1.19.1052 with no explicit bump. run 36375859084. (task 1002620)
|
|
2596
|
+
1.19.1054 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2597
|
+
landed since 1.19.1053 with no explicit bump. run 36378326588. (task 1002620)
|
|
2594
2598
|
---------------------------------------------------------------------------
|
|
2595
2599
|
```
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<!-- The faces (Manrope, JetBrains Mono) come from the branding pack's font
|
|
12
12
|
links, injected into this head by the server like every hall page's
|
|
13
13
|
(tests/hall_tokens.mjs: no hall page hardcodes a web-font stylesheet). -->
|
|
14
|
-
<link rel="stylesheet" href="/builders/thinking.css?v=2026-09-
|
|
14
|
+
<link rel="stylesheet" href="/builders/thinking.css?v=2026-09-27-split">
|
|
15
15
|
</head>
|
|
16
16
|
<!-- THE PROJECT SKY (task 1004232 / sky part 4 of 5, goal 1000095; ADR 0331,
|
|
17
17
|
ADR 0227 §11). The SAME page as /thinking — docs/design/mocks/sky/
|
|
@@ -25,13 +25,15 @@
|
|
|
25
25
|
URL and took the nav slot the map was denied, under Goals (owner,
|
|
26
26
|
2026-09-24; ADR 0227 §11).
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
THE WHOLE PROJECT ONLY (task 1004328, owner decision 2026-09-27). The
|
|
29
|
+
sheet, the seven sky-*.js files and the drawing are SHARED with /thinking,
|
|
30
|
+
never copied; the lenses and the tools are not. This page has one lens,
|
|
31
|
+
"the whole project" (`data-sky-lens="all"`), so it carries no switcher,
|
|
32
|
+
and it no longer offers "simple" or "your thinking". Its tools are the
|
|
33
|
+
project map's: explore and its start dialog, "my ideas" (your thoughts
|
|
34
|
+
laid over the map), routes, the ledger drawer. The ideator's pills
|
|
35
|
+
(recent, claude layer, my influence) are /thinking's alone, and nothing
|
|
36
|
+
here leaves for that page as a lens. The same rule as
|
|
35
37
|
/thinking on chrome: none — the sky is a full-screen surface with its own
|
|
36
38
|
header, and the hall's doors (the goal, the task, the record) open from
|
|
37
39
|
the panel.
|
|
@@ -58,16 +60,8 @@
|
|
|
58
60
|
<h1>the sky <em id="lensWord">— the whole project</em></h1>
|
|
59
61
|
<div class="stats" id="stats"></div>
|
|
60
62
|
</header>
|
|
61
|
-
<div class="lens" role="group" aria-label="Filter" id="lensGroup">
|
|
62
|
-
<button type="button" id="lensSimple" aria-pressed="false">simple</button>
|
|
63
|
-
<button type="button" id="lensMine" aria-pressed="false">your thinking</button>
|
|
64
|
-
<button type="button" id="lensAll" aria-pressed="true">the whole project</button>
|
|
65
|
-
</div>
|
|
66
63
|
<div class="tools" id="tools">
|
|
67
64
|
<button type="button" class="pill" id="roomBtn" aria-pressed="false" title="The same sky, as a list (L)">list</button>
|
|
68
|
-
<button type="button" class="pill" id="recentBtn" aria-pressed="false" hidden title="Only the thoughts of the last thirty days.">recent</button>
|
|
69
|
-
<button type="button" class="pill" id="claudeBtn" aria-pressed="false" hidden title="Show the thoughts a session filed under your name. Off by default: only what a person filed.">claude layer</button>
|
|
70
|
-
<button type="button" class="pill" id="influenceBtn" aria-pressed="false" hidden title="Only the goals your thoughts touched — everything else fades.">my influence</button>
|
|
71
65
|
<button type="button" class="pill" id="exploreBtn" aria-pressed="true" title="Explore: the sky reveals itself as you follow routes from where you begin.">explore</button>
|
|
72
66
|
<button type="button" class="pill" id="ideasBtn" aria-pressed="false" title="Lay your thoughts over the project map.">my ideas</button>
|
|
73
67
|
<button type="button" class="pill" id="rehearse" aria-pressed="false" hidden title="What if: invents removals, dimming and nods — none has happened in the ledger yet — so you can see how the dead would read. Sizes are never invented.">what if</button>
|
|
@@ -131,13 +125,13 @@
|
|
|
131
125
|
</div>
|
|
132
126
|
</footer>
|
|
133
127
|
|
|
134
|
-
<script src="/builders/idea-objects.js?v=2026-09-
|
|
135
|
-
<script src="/builders/sky-data.js?v=2026-09-
|
|
136
|
-
<script src="/builders/sky-layout.js?v=2026-09-
|
|
137
|
-
<script src="/builders/sky-draw.js?v=2026-09-
|
|
138
|
-
<script src="/builders/sky-interaction.js?v=2026-09-
|
|
139
|
-
<script src="/builders/sky-panel.js?v=2026-09-
|
|
140
|
-
<script src="/builders/sky-composer.js?v=2026-09-
|
|
141
|
-
<script src="/builders/sky-tour.js?v=2026-09-
|
|
128
|
+
<script src="/builders/idea-objects.js?v=2026-09-27-split"></script>
|
|
129
|
+
<script src="/builders/sky-data.js?v=2026-09-27-split"></script>
|
|
130
|
+
<script src="/builders/sky-layout.js?v=2026-09-27-split"></script>
|
|
131
|
+
<script src="/builders/sky-draw.js?v=2026-09-27-split"></script>
|
|
132
|
+
<script src="/builders/sky-interaction.js?v=2026-09-27-split"></script>
|
|
133
|
+
<script src="/builders/sky-panel.js?v=2026-09-27-split"></script>
|
|
134
|
+
<script src="/builders/sky-composer.js?v=2026-09-27-split"></script>
|
|
135
|
+
<script src="/builders/sky-tour.js?v=2026-09-27-split"></script>
|
|
142
136
|
</body>
|
|
143
137
|
</html>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_": "THE PROJECT SKY (task 1004232 / sky part 4 of 5, goal 1000095; ADR 0331, ADR 0227 §11) through modules/ui-design/kit/render.js — the same
|
|
2
|
+
"_": "THE PROJECT SKY (task 1004232 / sky part 4 of 5, goal 1000095; ADR 0331, ADR 0227 §11) through modules/ui-design/kit/render.js — the same renderer as /thinking (thinking.states.json), but its own sky: its only lens is 'the whole project', and it carries no switcher (task 1004328). The page draws GET /sky + GET /sky/mine; against the kit's own stub neither exists, so every state below renders the EMPTY sky — a real state the floors must hold in (the tools, the ledger drawer and the tour all exist with no goal read). To look at the populated sky — the wells sized by credits, the routes, the shuttle menu, explore's 'where do you begin' — run it against the hall-preview harness, whose sky.json + sky__mine.json fixtures carry the wells and the routes:\n\n node scripts/hall-preview/server.js --port 4620 --fixture-me\n node modules/ui-design/kit/render.js --states modules/hall-ui/public/goal-map.states.json --base-in http://localhost:4620 --base-out http://localhost:4620\n\nThe tour opens by itself on a first visit (sky-map-tour-seen — its own key, since the two tours differ), so states that want the bare sky skip it first; explore is on by default and asks where you begin the first time (sky-seen), so the 'sky' state renders that prompt on purpose.",
|
|
3
3
|
"page": "/builders/goal-map",
|
|
4
4
|
"surface": "hall-ui",
|
|
5
5
|
"stub": { "prefix": "/builders/" },
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"sky": {
|
|
18
18
|
"auth": true,
|
|
19
|
-
"_": "The 'the whole project' lens, bare: the brand line,
|
|
19
|
+
"_": "The 'the whole project' lens, bare: the brand line, explore + my ideas + the clock in the tools, the legend — and explore's 'where do you begin' prompt on a first visit.",
|
|
20
20
|
"actions": [["wait", 1200], ["clickIfVisible", "#tourSkip"], ["wait", 300]],
|
|
21
21
|
"expect": { "visible": ["#sky", "#exploreBtn", "#ideasBtn"] }
|
|
22
22
|
},
|
|
@@ -29,23 +29,21 @@
|
|
|
29
29
|
// GET /sky (which thoughts became goals or rings, from the ratify tables).
|
|
30
30
|
// Nothing here guesses either from text.
|
|
31
31
|
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
32
|
+
// TWO SKIES, ONE RENDERER (task 1004328, owner decision 2026-09-27; it
|
|
33
|
+
// replaced part 4's "one page, two doors"). /thinking and /goal-map share
|
|
34
|
+
// these seven files, the sheet and the drawing, and nothing else: each page
|
|
35
|
+
// has its own lenses and its own tools. The body's `data-sky-lens` says which
|
|
36
|
+
// sky this is — 'mine' on /thinking, YOUR thinking only, with the lenses
|
|
37
|
+
// "simple" and "your thinking"; 'all' on /goal-map, the whole project, with
|
|
38
|
+
// the one lens "the whole project" and its features (explore, "my ideas"
|
|
39
|
+
// laid over the map, routes, the ledger of goals). `S.lenses` is the list a
|
|
40
|
+
// page may be in, and setLens never leaves it — not from a button, the tour,
|
|
41
|
+
// a jump or the panel. Neither page links to the other as a lens.
|
|
40
42
|
//
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
// hands back to it after the tour, and sky-interaction.js's switcher leaves
|
|
46
|
-
// for the other page rather than switching when the lens asked for lives
|
|
47
|
-
// there. Every lens still exists inside either page, because the tour walks
|
|
48
|
-
// all three.
|
|
43
|
+
// A control belongs to one page's markup only (the switcher's buttons, the
|
|
44
|
+
// ideator's pills, the project map's pills, explore's start dialog), so the
|
|
45
|
+
// files touch those through hide / press / on below, which do nothing when
|
|
46
|
+
// the page does not carry the element.
|
|
49
47
|
|
|
50
48
|
(() => {
|
|
51
49
|
'use strict';
|
|
@@ -54,6 +52,10 @@
|
|
|
54
52
|
|
|
55
53
|
// ---------------------------------------------------------------- helpers
|
|
56
54
|
const $ = (id) => document.getElementById(id);
|
|
55
|
+
// A control one page carries and the other does not (task 1004328).
|
|
56
|
+
const hide = (id, v) => { const el = $(id); if (el) el.hidden = v; };
|
|
57
|
+
const press = (id, v) => { const el = $(id); if (el) el.setAttribute('aria-pressed', String(v)); };
|
|
58
|
+
const on = (id, ev, fn) => { const el = $(id); if (el) el.addEventListener(ev, fn); };
|
|
57
59
|
const TAU = Math.PI * 2;
|
|
58
60
|
const GOLDEN = 137.50776405003785 * Math.PI / 180;
|
|
59
61
|
const reduce = !!(window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches);
|
|
@@ -114,12 +116,14 @@
|
|
|
114
116
|
// ---------------------------------------------------------------- shared state
|
|
115
117
|
// Every `let` the prototype's closure held, on one object so seven files can
|
|
116
118
|
// read and write the same page. Data-derived fields are filled by buildWorld.
|
|
119
|
+
// Which sky this page is — read once, before the state that depends on it.
|
|
120
|
+
const HOME = document.body.dataset.skyLens === 'all' ? 'all' : 'mine';
|
|
117
121
|
const S = {
|
|
118
122
|
goals: [], goalById: new Map(), taskById: new Map(), edges: [], BANDS: [], ASPECT: 1, BORN: { goals: {}, criteria: {} },
|
|
119
123
|
baseIdeas: [], ideas: [], becameGoal: new Map(), becameCrit: new Map(), T0: NOW - 86400000,
|
|
120
124
|
hasFiledVia: false, canNod: false, template: null, buildingVersionIds: [], loaded: false,
|
|
121
125
|
rehearsal: false, claudeLayer: false, influence: false, ideasOnMap: false, recentOnly: false,
|
|
122
|
-
lens:
|
|
126
|
+
lens: HOME, home: HOME, lenses: HOME === 'all' ? ['all'] : ['simple', 'mine'], tourKey: HOME === 'all' ? 'sky-map-tour-seen' : 'sky-tour-seen', picked: null, hover: null, filter: 'all', gfilter: 'all', dirty: true, gravityGoal: null, flying: null, tourSpot: null,
|
|
123
127
|
clock: NOW, playing: false, clockHidden: store.get('sky-clock') === 'off',
|
|
124
128
|
charted: new Map(), focusSet: null, explore: { on: store.get('sky-explore') !== 'off', seen: new Set(JSON.parse(store.get('sky-seen') || '[]')) },
|
|
125
129
|
cam: { x: 0, y: 0, k: 0.12 }, flight: null, W: 0, H: 0, DPR: 1, t0: 0, shuttles: [],
|
|
@@ -323,7 +327,7 @@
|
|
|
323
327
|
}
|
|
324
328
|
|
|
325
329
|
Object.assign(SKY, {
|
|
326
|
-
S, $, TAU, GOLDEN, reduce, NOW, hash, rng, clamp, lerp, smooth, ease, viewport, esc, fmtDate, fmtDay, ageDays, plain, short, store, tokens, toMs, hallBase, API, api,
|
|
330
|
+
S, $, hide, press, on, TAU, GOLDEN, reduce, NOW, hash, rng, clamp, lerp, smooth, ease, viewport, esc, fmtDate, fmtDay, ageDays, plain, short, store, tokens, toMs, hallBase, API, api,
|
|
327
331
|
OBJ, objects, SESSION_VIAS, viaWord, bornFromSky, goalFromSky, ideaFromMine, bandsFor, wellRadius, ringR, taskAt, buildWorld, edgesOf,
|
|
328
332
|
applyRehearsal, inLayer, shown, diameter, positionWord, brightnessWord, reach, became, temp, placeIdeas, touched, load,
|
|
329
333
|
});
|
|
@@ -5,12 +5,11 @@
|
|
|
5
5
|
//
|
|
6
6
|
// The prototype's "hover card + shuttle menu", "interaction" and "lenses and
|
|
7
7
|
// toggles" sections, verbatim but for the shared state on `SKY.S`. The one
|
|
8
|
-
// behaviour this part CHANGES on purpose: the
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// walks all three. And the "claude layer" pill only appears once part 5's
|
|
8
|
+
// behaviour this part CHANGES on purpose: the two skies are separate (task
|
|
9
|
+
// 1004328, owner decision 2026-09-27). /thinking's switcher is "simple" and
|
|
10
|
+
// "your thinking" and nothing else; /goal-map has no switcher, only "the
|
|
11
|
+
// whole project". setLens stays inside the page's own `S.lenses`, and no
|
|
12
|
+
// button leaves for the other page. And the "claude layer" pill only appears once part 5's
|
|
14
13
|
// `filed_via` exists on at least one thought; until then it is hidden and
|
|
15
14
|
// every thought shows (the README's "must not ship" row).
|
|
16
15
|
//
|
|
@@ -21,7 +20,7 @@
|
|
|
21
20
|
'use strict';
|
|
22
21
|
|
|
23
22
|
const SKY = window.OTBSky; const S = SKY.S;
|
|
24
|
-
const { $, TAU, hash, clamp, esc, fmtDay, short,
|
|
23
|
+
const { $, hide, press, on, TAU, hash, clamp, esc, fmtDay, short, OBJ, diameter, reach, became, inLayer, shown, seen, present, toWorld, bandAt, cv, hit, edgesOf } = SKY;
|
|
25
24
|
|
|
26
25
|
// ---------------------------------------------------------------- hover card + shuttle menu
|
|
27
26
|
const card = $('card'); const menu = $('menu');
|
|
@@ -87,32 +86,33 @@
|
|
|
87
86
|
|
|
88
87
|
// ---------------------------------------------------------------- lenses and toggles
|
|
89
88
|
function setLens(l, noFly) {
|
|
89
|
+
// A lens this page does not have is never entered (task 1004328): the page's own home stands in.
|
|
90
|
+
if (!S.lenses.includes(l)) l = S.home;
|
|
90
91
|
S.lens = l;
|
|
91
|
-
for (const [id, v] of [['lensSimple', 'simple'], ['lensMine', 'mine']
|
|
92
|
+
for (const [id, v] of [['lensSimple', 'simple'], ['lensMine', 'mine']]) press(id, l === v);
|
|
92
93
|
$('lensWord').textContent = l === 'simple' ? '— simply' : l === 'mine' ? '— your thinking' : '— the whole project'; document.querySelector('.brand h1').firstChild.textContent = l === 'all' ? 'the sky ' : 'your sky ';
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
hide('claudeBtn', l !== 'mine' || !S.hasFiledVia); hide('influenceBtn', l !== 'mine'); hide('ideasBtn', l !== 'all'); hide('exploreBtn', l !== 'all'); $('rehearse').hidden = !(l === 'mine' || (l === 'all' && S.ideasOnMap));
|
|
95
|
+
hide('recentBtn', l !== 'simple'); $('roomBtn').hidden = l === 'simple'; $('clockBtn').hidden = l === 'simple'; $('time').hidden = S.clockHidden || l === 'simple'; document.querySelector('.jump').hidden = l === 'simple'; $('legend').hidden = l === 'simple';
|
|
95
96
|
if (l !== 'all') SKY.clearCharts();
|
|
96
97
|
if (l === 'simple') { SKY.toggleRoom(false); pick(null); }
|
|
97
98
|
if (!noFly) { if (l === 'all') SKY.fitAll(); else SKY.fitPresent(); }
|
|
98
99
|
SKY.maybeStart(); SKY.renderRoom(); updateStats(); S.dirty = true;
|
|
99
100
|
}
|
|
100
|
-
// The switcher is
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
$('lensSimple').addEventListener('click', () => (S.home === 'all' ? leave('/thinking') : setLens('simple')));
|
|
106
|
-
$('lensMine').addEventListener('click', () => (S.home === 'all' ? leave('/thinking') : setLens('mine')));
|
|
107
|
-
$('lensAll').addEventListener('click', () => (S.home === 'all' ? setLens('all') : leave('/goal-map')));
|
|
101
|
+
// The switcher is /thinking's alone (task 1004328): "simple" and "your
|
|
102
|
+
// thinking", both switching in place. /goal-map carries no switcher, and no
|
|
103
|
+
// button on either page leaves for the other.
|
|
104
|
+
on('lensSimple', 'click', () => setLens('simple'));
|
|
105
|
+
on('lensMine', 'click', () => setLens('mine'));
|
|
108
106
|
$('fitBtn').addEventListener('click', () => (S.lens === 'all' ? SKY.fitAll() : SKY.fitPresent()));
|
|
109
|
-
|
|
107
|
+
// Each pill is one page's: recent, claude layer and my influence are
|
|
108
|
+
// /thinking's; my ideas and explore are /goal-map's (task 1004328).
|
|
109
|
+
function togglePill(id, get, set) { on(id, 'click', () => { set(!get()); press(id, get()); SKY.renderRoom(); updateStats(); S.dirty = true; }); }
|
|
110
110
|
togglePill('recentBtn', () => S.recentOnly, (v) => { S.recentOnly = v; });
|
|
111
111
|
togglePill('claudeBtn', () => S.claudeLayer, (v) => { S.claudeLayer = v; });
|
|
112
112
|
togglePill('influenceBtn', () => S.influence, (v) => { S.influence = v; });
|
|
113
113
|
togglePill('ideasBtn', () => S.ideasOnMap, (v) => { S.ideasOnMap = v; $('rehearse').hidden = !(S.lens === 'mine' || S.ideasOnMap); });
|
|
114
114
|
togglePill('exploreBtn', () => S.explore.on, (v) => { S.explore.on = v; SKY.saveExplore(); SKY.maybeStart(); });
|
|
115
|
-
|
|
115
|
+
press('exploreBtn', S.explore.on);
|
|
116
116
|
$('rehearse').addEventListener('click', () => { S.rehearsal = !S.rehearsal; $('rehearse').setAttribute('aria-pressed', String(S.rehearsal)); SKY.applyRehearsal(); SKY.placeIdeas(); if (S.picked && S.picked.kind === 'idea') { S.picked = { kind: 'idea', ref: S.ideas.find((i) => i.id === S.picked.ref.id) }; SKY.renderPanel(S.picked); } SKY.renderRoom(); updateStats(); S.dirty = true; });
|
|
117
117
|
|
|
118
118
|
function updateStats() {
|
|
@@ -136,14 +136,15 @@
|
|
|
136
136
|
const is = S.ideas.filter((i) => inLayer(i) && i.title.toLowerCase().includes(q)).slice(0, 8).map((i) => ({ kind: 'idea', ref: i, w: (OBJ[i.st] || { word: i.st }).word }));
|
|
137
137
|
const all = [...gs, ...is]; ul.hidden = false;
|
|
138
138
|
ul.innerHTML = all.length ? all.map((h, n) => `<li><button type="button" data-n="${n}"><span class="lbl">${esc(h.w)}</span><span>${esc(h.ref.title)}</span></button></li>`).join('') : '<li><button type="button" disabled>nothing by that name — or not explored yet</button></li>';
|
|
139
|
-
ul.querySelectorAll('button[data-n]').forEach((b) => b.addEventListener('click', () => { const h = all[+b.dataset.n]; if (h.kind === '
|
|
139
|
+
ul.querySelectorAll('button[data-n]').forEach((b) => b.addEventListener('click', () => { const h = all[+b.dataset.n]; if (h.kind === 'idea' && S.lens === 'all') { S.ideasOnMap = true; press('ideasBtn', true); } pick({ kind: h.kind, ref: h.ref }, true); ul.hidden = true; $('jump').value = ''; }));
|
|
140
140
|
});
|
|
141
141
|
document.addEventListener('click', (e) => { if (!(e.target && e.target.closest && e.target.closest('.jump'))) $('jumpList').hidden = true; });
|
|
142
142
|
|
|
143
143
|
$('legend').innerHTML = [
|
|
144
144
|
['star', 'star', 'burning'], ['protostar', 'protostar', 'waiting'], ['satellite', 'satellite', 'folded in'], ['remnant', 'remnant', 'burned out'], ['cold', 'cold body', 'declined'],
|
|
145
145
|
['well', 'well', 'an open goal'], ['alive', 'alive', 'a well with work in motion'], ['settled', 'settled', 'achieved'], ['collapsed', 'collapsed', 'archived'], ['unseen', 'unexplored', 'not yet reached'], ['task live', 'body', 'a task'], ['born', 'born of a thought', 'a goal or ring an idea became'], ['route', 'route', 'a dependency, charted'],
|
|
146
|
-
|
|
146
|
+
// "unexplored" is explore's word, and explore is the project map's (task 1004328).
|
|
147
|
+
].filter(([cls]) => cls !== 'unseen' || S.lenses.includes('all')).map(([cls, name, what]) => `<button type="button" class="obj" title="${esc(what)}"><span class="g"><span class="mk ${cls}"></span></span>${esc(name)}</button>`).join('');
|
|
147
148
|
|
|
148
149
|
Object.assign(SKY, { card, menu, glyphHtml, ideaTitle, sizeWords, showCard, showMenu, hideMenu, pick, setLens, togglePill, updateStats, altText, TAU, hash });
|
|
149
150
|
})();
|
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
// sky-data.js for the split). The clock draws the sky as it stood on a day —
|
|
7
7
|
// nothing moves position, only what exists changes. Routes are the goal
|
|
8
8
|
// graph's own edges, charted by a shuttle when asked. Explore is the project
|
|
9
|
-
// sky's rule
|
|
10
|
-
//
|
|
9
|
+
// sky's rule, and only /goal-map carries it (task 1004328: its pill and its
|
|
10
|
+
// start dialog are that page's markup alone). The camera is a
|
|
11
11
|
// pan/zoom over world coordinates laid out in sky-data.js.
|
|
12
12
|
|
|
13
13
|
(() => {
|
|
14
14
|
'use strict';
|
|
15
15
|
|
|
16
16
|
const SKY = window.OTBSky; const S = SKY.S;
|
|
17
|
-
const { $, TAU, reduce, NOW, clamp, smooth, ease, esc, fmtDay, short, store } = SKY;
|
|
17
|
+
const { $, hide, press, TAU, reduce, NOW, clamp, smooth, ease, esc, fmtDay, short, store } = SKY;
|
|
18
18
|
|
|
19
19
|
// ---------------------------------------------------------------- the clock
|
|
20
20
|
const slider = $('clock');
|
|
@@ -50,13 +50,13 @@
|
|
|
50
50
|
el.innerHTML = `<button type="button" class="door primary" data-start="mine">where my thoughts are</button>` + wells.map((g) => `<button type="button" class="door" data-start="${esc(g.id)}">${esc(short(g.title))}</button>`).join('') + `<button type="button" class="door" data-start="all">show me everything</button>`;
|
|
51
51
|
el.querySelectorAll('button').forEach((b) => b.addEventListener('click', () => {
|
|
52
52
|
const v = b.dataset.start; $('start').hidden = true;
|
|
53
|
-
if (v === 'all') { explore.on = false;
|
|
53
|
+
if (v === 'all') { explore.on = false; press('exploreBtn', false); saveExplore(); fitAll(); }
|
|
54
54
|
else if (v === 'mine') { const t = S.goals.filter(SKY.touched); (t.length ? t : wells.slice(0, 1)).forEach(reveal); fitPresent(); }
|
|
55
55
|
else { const g = S.goalById.get(v); reveal(g); SKY.pick({ kind: 'goal', ref: g }, true); }
|
|
56
56
|
SKY.renderRoom(); SKY.updateStats(); S.dirty = true;
|
|
57
57
|
}));
|
|
58
58
|
}
|
|
59
|
-
function maybeStart() { const need = S.lens === 'all' && explore.on && explore.seen.size === 0 && $('tour').hidden
|
|
59
|
+
function maybeStart() { const need = S.lens === 'all' && explore.on && explore.seen.size === 0 && $('tour').hidden && !!$('start'); hide('start', !need); if (need) startOptions(); }
|
|
60
60
|
|
|
61
61
|
// ---------------------------------------------------------------- the camera
|
|
62
62
|
const cv = $('sky'); const cam = S.cam;
|
|
@@ -85,10 +85,11 @@
|
|
|
85
85
|
<a class="door" href="${HALL}/goals#/goal/${esc(g.id)}" target="_blank" rel="noopener">the goal</a>
|
|
86
86
|
</div>
|
|
87
87
|
<p class="note">${g.status === 'open' ? 'A well holds work. Its rings are the done-when criteria, thick with the credits they carry, solid once met. It glows while it is alive: a builder holding a task, or work landing this week. Held and claimable tasks drift along their rings; landed work holds still.' : g.status === 'achieved' ? 'Every ring closed. It keeps the full mass of the work; nothing in it is live any more, so it holds no accent — a steady light, not a burning one.' : 'Archived. The mass of what was done stays on the record, drawn at half the radius; nothing here can ignite.'}</p>`;
|
|
88
|
-
|
|
88
|
+
// Descending stays in this page's sky (task 1004328): "simple" does not zoom, so it steps up to "your thinking" — never to the project map.
|
|
89
|
+
$('descendBtn').addEventListener('click', () => { if (S.lens === 'simple') SKY.setLens('mine', true); SKY.fitGoal(g); });
|
|
89
90
|
const cb = $('chartBtn'); if (cb) cb.addEventListener('click', () => SKY.chartAllOf(g));
|
|
90
91
|
el.querySelectorAll('[data-goal]').forEach((a) => a.addEventListener('click', (e) => { e.preventDefault(); SKY.pick({ kind: 'goal', ref: goalById.get(a.dataset.goal) }, true); }));
|
|
91
|
-
el.querySelectorAll('[data-idea]').forEach((a) => a.addEventListener('click', (e) => { e.preventDefault(); const i = S.ideas.find((x) => x.id === a.dataset.idea); if (i) { if (S.lens === 'all') { S.ideasOnMap = true;
|
|
92
|
+
el.querySelectorAll('[data-idea]').forEach((a) => a.addEventListener('click', (e) => { e.preventDefault(); const i = S.ideas.find((x) => x.id === a.dataset.idea); if (i) { if (S.lens === 'all') { S.ideasOnMap = true; SKY.press('ideasBtn', true); } SKY.pick({ kind: 'idea', ref: i }, true); } }));
|
|
92
93
|
} else if (p.kind === 'task' || p.kind === 'ltask') {
|
|
93
94
|
const t = p.ref; const g = t.goal || (t.goalId && goalById.get(t.goalId)); const crit = g && t.ci >= 0 ? g.crit[t.ci] : null;
|
|
94
95
|
el.innerHTML = `
|
|
@@ -5,20 +5,27 @@
|
|
|
5
5
|
// shared state on `SKY.S`, plus the boot the prototype did not need (its data
|
|
6
6
|
// was baked into the page): the five reads (sky-data.js `load`), then the
|
|
7
7
|
// prototype's own start-up line. The thirteen steps and their words are the
|
|
8
|
-
// design of record's; the tour opens once per browser (`
|
|
8
|
+
// design of record's; the tour opens once per browser, per page (`S.tourKey`:
|
|
9
|
+
// `sky-tour-seen` on /thinking, `sky-map-tour-seen` on /goal-map — the two
|
|
10
|
+
// tours differ since task 1004328, so seeing one does not mark the other seen).
|
|
9
11
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
12
|
+
// TWO TOURS FROM ONE LIST (task 1004328, owner decision 2026-09-27). The two
|
|
13
|
+
// skies no longer share lenses, so each page walks only its own: every step
|
|
14
|
+
// says which sky it belongs to (`sky`: 'mine', 'all' or 'both'), and STEPS is
|
|
15
|
+
// this page's slice of the list. /thinking keeps the ideator's steps, the
|
|
16
|
+
// wells and rings they sit in, the clock, the composer and "simple" — nine of
|
|
17
|
+
// the thirteen, in the template's order and words. /goal-map keeps the wells,
|
|
18
|
+
// the rings, life, routes, settled systems, the clock and explore, opened by
|
|
19
|
+
// one step of its own ("This is the whole project.", the one new sentence the
|
|
20
|
+
// split needed — the template's first step is "This is your sky." and
|
|
21
|
+
// belongs to /thinking). When a tour ends it hands back to the page's home
|
|
22
|
+
// lens (`S.home`), and the boot opens in that lens too.
|
|
16
23
|
|
|
17
24
|
(() => {
|
|
18
25
|
'use strict';
|
|
19
26
|
|
|
20
27
|
const SKY = window.OTBSky; const S = SKY.S;
|
|
21
|
-
const { $, NOW, store, edgesOf, inLayer, clamp, lerp, ease } = SKY;
|
|
28
|
+
const { $, hide, press, NOW, store, edgesOf, inLayer, clamp, lerp, ease } = SKY;
|
|
22
29
|
|
|
23
30
|
// ---------------------------------------------------------------- the tour
|
|
24
31
|
const bigWell = () => S.goals.filter((g) => g.status === 'open').sort((a, b) => b.crit.length - a.crit.length || b.mass - a.mass)[0];
|
|
@@ -28,25 +35,29 @@
|
|
|
28
35
|
const aShaped = () => S.ideas.find((i) => i.shaped && inLayer(i)) || S.ideas.find((i) => i.shaped);
|
|
29
36
|
const aRouted = () => S.goals.filter((g) => g.status === 'open' && edgesOf(g).length).sort((a, b) => edgesOf(b).length - edgesOf(a).length)[0];
|
|
30
37
|
const aLive = () => S.goals.filter((g) => g.live > 0).sort((a, b) => b.live - a.live)[0];
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
{ h: '
|
|
35
|
-
{
|
|
36
|
-
{
|
|
37
|
-
{ h: '
|
|
38
|
-
{ h: '
|
|
39
|
-
{
|
|
40
|
-
{
|
|
41
|
-
{ h: '
|
|
42
|
-
{ h: '
|
|
43
|
-
{ h: '
|
|
44
|
-
{
|
|
38
|
+
// Explore off for the tour's length (startTour kept what it was; endTour gives it back).
|
|
39
|
+
const unexplore = () => { S.explore.on = false; hide('start', true); };
|
|
40
|
+
const ALL_STEPS = [
|
|
41
|
+
{ sky: 'all', h: 'This is the whole project.', p: 'Everything in it is a fact from the ledger, drawn instead of listed. Every goal is a gravity well, sized by the credits of its work; the present sits at the centre and shipped versions ring outward as darker ground. Your own thoughts live on Your thinking, and "my ideas" lays them over this map.', go: () => { unexplore(); SKY.setLens('all', true); SKY.fitAll(); spot($('ideasBtn')); } },
|
|
42
|
+
{ sky: 'mine', h: 'This is your sky.', p: 'Everything in it is a fact from the ledger, drawn instead of listed. The present sits at the centre; shipped versions ring outward as darker ground — hover the space between goals to be told which version you are in. Only what a person filed is shown; a session’s filings wait behind "claude layer".', go: () => { SKY.setLens('mine', true); SKY.fitPresent(); spot($('claudeBtn')); } },
|
|
43
|
+
{ sky: 'both', h: 'A goal is a gravity well.', p: 'It holds the work. The rings inside it are its done-when criteria: dashed while open, solid once met, thicker the more credits ride on them. Thoughts caught by the goal sit inside the well.', go: () => { const w = bigWell(); if (w) { S.tourSpot = { kind: 'goal', ref: w }; SKY.flyTo(w.x, w.y, Math.min(S.W, S.H) / (w.r * 2.9), 1100); } } },
|
|
44
|
+
{ sky: 'both', h: 'A ring is a criterion.', p: 'Hover any ring to read it. Come closer and the tasks that gate it resolve out of the dust onto the ring itself. Tasks born of the same thought share an angle, so kin line up radially. Held and claimable tasks drift slowly along their ring; landed work holds still.', go: () => { const w = bigWell(); if (w && w.crit.length) S.tourSpot = { kind: 'ring', goal: w, index: 0 }; } },
|
|
45
|
+
{ sky: 'mine', h: 'A burning thought is a star.', p: 'It caused work that is still alive. Its size is real: the credits the ledger paid this thought’s descent, a slice of every task that fell out of it. It grows as the work grows and never shrinks. Brightness is how much of that work still stands, and that can go out.', go: () => { const s = aStar(); if (s) { S.tourSpot = { kind: 'idea', ref: s }; SKY.flyTo(s.x, s.y, 2.6, 1100); } } },
|
|
46
|
+
{ sky: 'mine', h: 'Some thoughts became the structure.', p: 'A thought ratified into a goal sits at that well’s core with a small accent ring. One ratified into a criterion sits on its ring, and the ring wears an accent notch. When a thought’s descent reaches into other goals, hover it and its reach is drawn: dashed accent lines to the work it caused elsewhere.', go: () => { const s = aShaped(); const w = bigWell(); if (s) { S.tourSpot = { kind: 'idea', ref: s }; SKY.flyTo(s.x, s.y, Math.max(1.4, S.cam.k), 1100); } else if (w) S.tourSpot = { kind: 'goal', ref: w }; } },
|
|
47
|
+
{ sky: 'mine', h: 'Unclaimed thoughts drift.', p: 'A thought nothing caught floats in open space. The longer it stays unclaimed, the further from the centre it drifts and the cooler its light — warm white when fresh, blue-white by half a year.', go: () => { const d = aDrifter(); if (d) { S.tourSpot = { kind: 'idea', ref: d }; SKY.flyTo(d.x, d.y, 1.2, 1100); } } },
|
|
48
|
+
{ sky: 'all', h: 'An alive well glows.', p: 'On the project map the one accent a goal can wear is life: a builder holding a task right now, or work landing this week. Quiet wells stay white. The ledger’s "alive" filter lists them.', go: () => { unexplore(); SKY.setLens('all', true); const g = aLive() || aRouted(); if (g) { S.tourSpot = { kind: 'goal', ref: g }; SKY.flyTo(g.x, g.y, Math.min(S.W, S.H) / (g.r * 4), 1100); } } },
|
|
49
|
+
{ sky: 'all', h: 'Routes run between goals that wait on each other.', p: 'Almost invisible until you ask. Each well wears a small arrow at its rim per route. Click one and the camera pulls back while a shuttle charts the route; double-click charts every route from that goal. Hover the parked shuttle for more: every route from this goal, every route in the sky, only the latest, hide the unrelated.', go: () => { const g = aRouted(); if (g) { S.tourSpot = { kind: 'goal', ref: g }; setTimeout(() => { const e = edgesOf(g)[0]; if (e) SKY.chartRoute(e, g); }, 300); } } },
|
|
50
|
+
{ sky: 'all', h: 'A settled system is a goal that finished.', p: 'It keeps the full mass of its work, measured in credits, and every ring is closed. Nothing in it is live any more, so it carries no accent — a steady white. Archived goals collapse to half their radius and go dark.', go: () => { const g = aSettled(); if (g) { S.tourSpot = { kind: 'goal', ref: g }; SKY.flyTo(g.x, g.y, Math.min(S.W, S.H) / (g.r * 3.2), 1100); } } },
|
|
51
|
+
{ sky: 'both', h: 'The sky has a clock.', p: 'Drag the slider at the bottom and the sky is drawn as it stood that day: goals, tasks and thoughts appear when they were born. Press play to watch it grow from the first day. Nothing moves position — a thought stays where it was caught — only what exists changes.', go: () => { SKY.fitAll(); spot($('time')); } },
|
|
52
|
+
{ sky: 'all', h: 'By default, you explore.', p: 'The project map does not hand you everything. You pick a well to begin in; every route you chart brings the goal at its far end into view, and the ledger, the filters and the search only know what you have reached. Turn "explore" off to see the whole sky at once.', go: () => { spot($('exploreBtn')); } },
|
|
53
|
+
{ sky: 'mine', h: 'Release a thought here.', p: 'One line, and the well that would catch it lights up while you type. Letting it float is a real answer. Switch "full idea" on before the line and five questions follow it, one at a time; skip any. "My influence" fades every goal your thoughts never touched.', go: () => { S.tourSpot = null; SKY.setLens('mine', true); SKY.fitPresent(); spot($('composerWrap')); spot($('influenceBtn'), true); } },
|
|
54
|
+
{ sky: 'mine', h: 'When it is too much, go simple.', p: 'The "simple" view is the first sky: just your thoughts in their wells, recent or all, nothing else to press. Click a band anywhere else to fill the screen with that version. Press L for the list, ? to walk this again.', go: () => { SKY.setLens('simple', true); SKY.fitPresent(); spot($('lensSimple')); spot($('recentBtn'), true); } },
|
|
45
55
|
];
|
|
56
|
+
const STEPS = ALL_STEPS.filter((s) => s.sky === 'both' || s.sky === S.home);
|
|
46
57
|
function spot(el, add) { if (!add) { S.spotted.forEach((e) => e.classList.remove('spot')); S.spotted = []; } if (el) { el.classList.add('spot'); S.spotted.push(el); } }
|
|
47
58
|
function showStep() { const s = STEPS[S.tourI]; if (!s) return endTour(); spot(null); S.tourSpot = null; s.go(); $('tourN').textContent = `how to read the sky · ${S.tourI + 1} of ${STEPS.length}`; $('tourDots').innerHTML = STEPS.map((_, n) => `<i class="${n === S.tourI ? 'on' : ''}"></i>`).join(''); $('tourH').textContent = s.h; $('tourP').textContent = s.p; $('tourNext').textContent = S.tourI === STEPS.length - 1 ? 'start reading' : 'next'; $('tourBack').hidden = S.tourI === 0; $('tour').hidden = false; S.dirty = true; }
|
|
48
|
-
function startTour() { SKY.pick(null); SKY.toggleRoom(false); SKY.clearCharts();
|
|
49
|
-
function endTour() { $('tour').hidden = true; spot(null); S.tourSpot = null; S.tourI = -1; if (S.exploreWas != null) { S.explore.on = S.exploreWas;
|
|
59
|
+
function startTour() { SKY.pick(null); SKY.toggleRoom(false); SKY.clearCharts(); hide('start', true); S.exploreWas = S.explore.on; S.tourI = 0; showStep(); }
|
|
60
|
+
function endTour() { $('tour').hidden = true; spot(null); S.tourSpot = null; S.tourI = -1; if (S.exploreWas != null) { S.explore.on = S.exploreWas; press('exploreBtn', S.explore.on); S.exploreWas = null; } store.set(S.tourKey, '4'); SKY.setLens(S.home, true); SKY.clearCharts(); SKY.maybeStart(); S.dirty = true; }
|
|
50
61
|
$('tourNext').addEventListener('click', () => { S.tourI++; showStep(); }); $('tourBack').addEventListener('click', () => { S.tourI = Math.max(0, S.tourI - 1); showStep(); }); $('tourSkip').addEventListener('click', endTour); $('tourBtn').addEventListener('click', startTour);
|
|
51
62
|
|
|
52
63
|
// ---------------------------------------------------------------- the loop
|
|
@@ -76,9 +87,9 @@
|
|
|
76
87
|
S.cam.k = S.home === 'all' ? SKY.fitRad(S.BANDS[S.BANDS.length - 1].r1 + 100) : SKY.fitRad(S.BANDS[0].r1 + 120);
|
|
77
88
|
document.body.dataset.skyReady = '1';
|
|
78
89
|
requestAnimationFrame(frame);
|
|
79
|
-
if (store.get(
|
|
90
|
+
if (store.get(S.tourKey) !== '4') setTimeout(startTour, 600);
|
|
80
91
|
}
|
|
81
92
|
|
|
82
|
-
Object.assign(SKY, { STEPS, spot, showStep, startTour, endTour, frame, boot });
|
|
93
|
+
Object.assign(SKY, { ALL_STEPS, STEPS, spot, showStep, startTour, endTour, frame, boot });
|
|
83
94
|
boot();
|
|
84
95
|
})();
|
|
@@ -86,6 +86,8 @@ html[data-fullscreen="on"] .sky-stage .top { padding-right: 64px; }
|
|
|
86
86
|
.lens button[aria-pressed="true"] { background: var(--glass-strong); color: var(--ink); }
|
|
87
87
|
.lens button:focus-visible, .pill:focus-visible, .row:focus-visible, .obj:focus-visible, input:focus-visible, .door:focus-visible, .menu button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
88
88
|
.tools { display: inline-flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; max-width: 680px; }
|
|
89
|
+
/* /goal-map carries no switcher (task 1004328), so its tools take the switcher's push to the right. */
|
|
90
|
+
.brand + .tools { margin-left: auto; }
|
|
89
91
|
.pill { background: var(--glass); }
|
|
90
92
|
.pill[aria-pressed="true"] { background: var(--glass-strong); color: var(--ink); }
|
|
91
93
|
.pill[aria-pressed="true"]::before { content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); margin-right: 8px; vertical-align: 1px; }
|