@bongos/core 1.19.1067 → 1.19.1069
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 +73 -58
- package/docs/copy-inventory.md +74 -53
- package/docs/copy-registry.json +310 -101
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +10 -2
- package/docs/page-readings.json +566 -536
- package/modules/hall-ui/public/atlas.html +1 -1
- package/modules/hall-ui/public/blockers.html +1 -1
- package/modules/hall-ui/public/board-room.html +1 -1
- package/modules/hall-ui/public/collab.html +1 -1
- package/modules/hall-ui/public/copy-desk.html +1 -1
- package/modules/hall-ui/public/deploy.html +1 -1
- package/modules/hall-ui/public/diagrams.html +1 -1
- package/modules/hall-ui/public/drachmae.html +1 -1
- package/modules/hall-ui/public/fleet.html +1 -1
- package/modules/hall-ui/public/gate.html +1 -1
- package/modules/hall-ui/public/goals.html +1 -1
- package/modules/hall-ui/public/government.html +1 -1
- package/modules/hall-ui/public/idea.html +3 -3
- package/modules/hall-ui/public/ideas.html +1 -1
- package/modules/hall-ui/public/index.html +1 -1
- package/modules/hall-ui/public/modules.html +1 -1
- package/modules/hall-ui/public/primer.html +1 -1
- package/modules/hall-ui/public/profile.html +1 -1
- package/modules/hall-ui/public/project-settings.html +1 -1
- package/modules/hall-ui/public/ranks.html +1 -1
- package/modules/hall-ui/public/roadmap.html +1 -1
- package/modules/hall-ui/public/roster.html +1 -1
- package/modules/hall-ui/public/sessions.html +1 -1
- package/modules/hall-ui/public/settings.html +1 -1
- package/modules/hall-ui/public/shell.js +23 -32
- package/modules/hall-ui/public/studio-lib.js +207 -0
- package/modules/hall-ui/public/studio.css +559 -170
- package/modules/hall-ui/public/studio.html +143 -49
- package/modules/hall-ui/public/studio.js +442 -33
- package/modules/hall-ui/public/studio.states.json +164 -4
- package/modules/hall-ui/public/task.html +1 -1
- package/modules/hall-ui/public/thinking.html +1 -1
- package/modules/hall-ui/public/tweak-editor.html +1 -1
- package/modules/hall-ui/public/watch.html +1 -1
- package/modules/hall-ui/public/work.html +1 -1
- package/modules/hall-ui/records/studio.md +47 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/scripts/hall-preview/server.js +5 -0
- package/src/module-api.js +1 -1
- package/tests/hall_audit.mjs +4 -0
- package/tests/hall_nav.mjs +25 -1
- package/tests/hall_page_gate_map.mjs +2 -0
- package/tests/hall_studio_home.mjs +429 -0
- package/tests/hall_studio_world.mjs +7 -1
- package/tests/hall_tweak_editor.mjs +1 -1
- package/tests/nav_permission_atoms.mjs +1 -1
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
|
|
92
92
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
93
93
|
|
|
94
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
94
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
95
95
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
96
96
|
<script src="/builders/palette.js" defer></script>
|
|
97
97
|
<script src="/builders/atlas.js?v=2026-09-02-index" defer></script>
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
|
|
51
51
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
52
52
|
chrome exists before the page script runs. -->
|
|
53
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
53
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
54
54
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
55
55
|
<script src="/builders/palette.js" defer></script>
|
|
56
56
|
<!-- shared frontend toolkit (window.OTB — rank labels, number formats) + the
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
|
|
61
61
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
62
62
|
|
|
63
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
63
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
64
64
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
65
65
|
<script src="/builders/palette.js" defer></script>
|
|
66
66
|
<script src="/builders/dom-utils.js?v=2026-06-16-board"></script>
|
|
@@ -148,7 +148,7 @@
|
|
|
148
148
|
|
|
149
149
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
150
150
|
|
|
151
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
151
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
152
152
|
<script src="/builders/palette.js" defer></script>
|
|
153
153
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
154
154
|
<script src="/builders/collab-lib.js?v=2026-09-24-archive"></script>
|
|
@@ -177,7 +177,7 @@
|
|
|
177
177
|
|
|
178
178
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
179
179
|
|
|
180
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
180
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
181
181
|
<script src="/builders/palette.js" defer></script>
|
|
182
182
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
183
183
|
<script src="/builders/hall-kit.js"></script>
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
|
|
70
70
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
71
71
|
|
|
72
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
72
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
73
73
|
<script src="/builders/palette.js" defer></script>
|
|
74
74
|
<script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
|
|
75
75
|
<script src="/builders/hall-kit.js"></script>
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
|
|
98
98
|
<!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
|
|
99
99
|
time so the chrome exists before the deferred page scripts execute. -->
|
|
100
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
100
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
101
101
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
102
102
|
<script src="/builders/palette.js" defer></script>
|
|
103
103
|
<script src="/builders/diagram-viewer.js?v=2026-06-14-diagviewer-1" defer></script>
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
|
|
99
99
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
100
100
|
|
|
101
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
101
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
102
102
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
103
103
|
<script src="/builders/palette.js" defer></script>
|
|
104
104
|
<script src="/builders/md-reader.js?v=2026-09-02-mdreader-2"></script>
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
|
|
55
55
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
56
56
|
|
|
57
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
57
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
58
58
|
<script src="/builders/palette.js" defer></script>
|
|
59
59
|
<script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
|
|
60
60
|
<script src="/builders/hall-kit.js"></script>
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
<div id="gate-modal-root"></div>
|
|
77
77
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
78
78
|
|
|
79
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
79
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
80
80
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
81
81
|
<script src="/builders/palette.js" defer></script>
|
|
82
82
|
<script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
|
|
@@ -235,7 +235,7 @@
|
|
|
235
235
|
|
|
236
236
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
237
237
|
|
|
238
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
238
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
239
239
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
240
240
|
<script src="/builders/palette.js" defer></script>
|
|
241
241
|
<script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
|
|
@@ -134,7 +134,7 @@
|
|
|
134
134
|
|
|
135
135
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
136
136
|
|
|
137
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
137
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
138
138
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
139
139
|
<script src="/builders/palette.js" defer></script>
|
|
140
140
|
<script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
link), so a deep-linked visitor needs a way back before the body loads
|
|
28
28
|
— the task.html precedent. -->
|
|
29
29
|
<div class="rec-crumb">
|
|
30
|
-
<a class="rec-back" href="/
|
|
31
|
-
<svg class="rec-back__ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M9.4 3.6 5 8l4.4 4.4"/><path d="M5 8h7.2"/></svg>Back to
|
|
30
|
+
<a class="rec-back" href="/thinking">
|
|
31
|
+
<svg class="rec-back__ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M9.4 3.6 5 8l4.4 4.4"/><path d="M5 8h7.2"/></svg>Back to your thinking</a>
|
|
32
32
|
<p class="rec-sub" id="idea-sub">Loading idea</p>
|
|
33
33
|
</div>
|
|
34
34
|
</div>
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
|
|
53
53
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
54
54
|
|
|
55
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
55
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
56
56
|
<script src="/builders/palette.js" defer></script>
|
|
57
57
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib" defer></script>
|
|
58
58
|
<!-- task-detail.js still owns the #/idea/N and #/blocker/N overlay, so a ref
|
|
@@ -225,7 +225,7 @@
|
|
|
225
225
|
|
|
226
226
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
227
227
|
|
|
228
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
228
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
229
229
|
<script src="/builders/palette.js" defer></script>
|
|
230
230
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
231
231
|
<!-- task 1003069 (R08) — the quick-filing form's routed result links a task
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
<script src="/builders/brand-apply.js?v=2026-06-27-r75" defer></script>
|
|
103
103
|
<!-- The app shell (sidebar + top bar + view router). Dependency-free; loaded
|
|
104
104
|
BEFORE builders.js so the chrome ids exist when events are wired. -->
|
|
105
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
105
|
+
<script src="/builders/shell.js?v=2026-09-28-archive" defer></script>
|
|
106
106
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
107
107
|
<script src="/builders/palette.js" defer></script>
|
|
108
108
|
<!-- task 2166: the shared component kit (window.OTBKit) — pager/ledger/rail.
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
|
|
81
81
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
82
82
|
|
|
83
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
83
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
84
84
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
85
85
|
<script src="/builders/palette.js" defer></script>
|
|
86
86
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
|
|
116
116
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
117
117
|
|
|
118
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
118
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
119
119
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
120
120
|
<script src="/builders/palette.js" defer></script>
|
|
121
121
|
<!-- the shared markdown reader (window.OTBMdReader) — primer.js renders through
|
|
@@ -187,7 +187,7 @@
|
|
|
187
187
|
|
|
188
188
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
189
189
|
chrome exists before the page script runs. -->
|
|
190
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
190
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
191
191
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
192
192
|
<script src="/builders/palette.js" defer></script>
|
|
193
193
|
<!-- shared frontend toolkit (window.OTB) — loaded before profile.js, which pulls from it. -->
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
|
|
59
59
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
60
60
|
|
|
61
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
61
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
62
62
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
63
63
|
<script src="/builders/palette.js" defer></script>
|
|
64
64
|
<script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
|
|
69
69
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
70
70
|
chrome exists before the page script runs. -->
|
|
71
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
71
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
72
72
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
73
73
|
<script src="/builders/palette.js" defer></script>
|
|
74
74
|
<!-- shared frontend toolkit (window.OTB) — loaded before ranks.js, which pulls from it. -->
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
43
43
|
|
|
44
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
44
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
45
45
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
46
46
|
<script src="/builders/palette.js" defer></script>
|
|
47
47
|
<script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
|
|
51
51
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
52
52
|
chrome exists before the page script runs. -->
|
|
53
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
53
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
54
54
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
55
55
|
<script src="/builders/palette.js" defer></script>
|
|
56
56
|
<!-- shared frontend toolkit (window.OTB — rank labels, number formats) + the
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
|
|
100
100
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
101
101
|
chrome exists before the page script runs. -->
|
|
102
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
102
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
103
103
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
104
104
|
<script src="/builders/palette.js" defer></script>
|
|
105
105
|
<!-- shared frontend toolkit (window.OTB) — loaded BEFORE sessions.js so it can pull from it. -->
|
|
@@ -436,7 +436,7 @@
|
|
|
436
436
|
|
|
437
437
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
438
438
|
chrome exists before the page scripts run. -->
|
|
439
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
439
|
+
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
440
440
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
441
441
|
<script src="/builders/palette.js" defer></script>
|
|
442
442
|
<!-- shared frontend toolkit (window.OTB) — loaded before consuming IIFEs. -->
|
|
@@ -210,43 +210,34 @@
|
|
|
210
210
|
// so the badge counts ballots this viewer can actually cast, never "items
|
|
211
211
|
// open somewhere".
|
|
212
212
|
{ id: 'board-room', label: 'Board Room', icon: 'admin', href: '/board-room', gate: 'perm:board.vote.cast', count: 'board_votes' },
|
|
213
|
-
// task
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
//
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
//
|
|
223
|
-
// not one of "your numbers". gate 'signed' and NOT a craft or rank gate, on
|
|
224
|
-
// the goal's own settled decision: there is no permission gate on Full
|
|
225
|
-
// Ideas, completeness scoring and triage do the filtering. Hiding the page
|
|
226
|
-
// behind the ideator craft would hide the ideator's own surface from
|
|
227
|
-
// everyone else who has a thought, which is most of the value.
|
|
228
|
-
{ id: 'ideas', label: 'Ideas', icon: 'goals', href: '/ideas', gate: 'signed' },
|
|
213
|
+
// task 1004326 (BV2.TW15 of goal 1000095): the Ideas board (/ideas, task
|
|
214
|
+
// 1002928) and the Copy desk (/copy-desk, task 1003113) are ARCHIVED — off
|
|
215
|
+
// the nav on the owner's call of 2026-09-27, NOT deleted. Both pages and
|
|
216
|
+
// their routes stay reachable by URL, so old bookmarks, the studio's
|
|
217
|
+
// old-reviews dialog and idea.js's 'Develop it' deep link still land, and
|
|
218
|
+
// no data moves (page asks keep copy_desk_flags, ADR 0341 D6). Ideas are
|
|
219
|
+
// filed from Your thinking, whose form covers quick and Full Ideas; artist
|
|
220
|
+
// work lives in the Studio. Do not re-add either item without a new owner
|
|
221
|
+
// call: tests/hall_nav.mjs pins the group without them.
|
|
222
|
+
//
|
|
229
223
|
// task 1004098 (BV2.R03 of goal 1000095): THE ROOM — what you filed and
|
|
230
|
-
// what became of it
|
|
231
|
-
//
|
|
232
|
-
//
|
|
233
|
-
//
|
|
234
|
-
//
|
|
235
|
-
//
|
|
236
|
-
//
|
|
237
|
-
//
|
|
238
|
-
//
|
|
224
|
+
// what became of it, and since TW15 also where you file: the daily loop
|
|
225
|
+
// (file, then find out) on one page, and the whole criterion
|
|
226
|
+
// (`ideator-nothing-is-lost`) is that the second half must be reachable
|
|
227
|
+
// without asking anyone. NOT in the 'You' group beside Standing and
|
|
228
|
+
// Profile, even though it is your own view: that group is your numbers,
|
|
229
|
+
// and this page's subject is the project's work, seen from the thought
|
|
230
|
+
// that caused it. gate 'signed' — there is no permission gate on having a
|
|
231
|
+
// thought, and the route reads the VIEWER's own trail, so there is
|
|
232
|
+
// nothing here a rank could protect.
|
|
239
233
|
{ id: 'thinking', label: 'Your thinking', icon: 'goals', href: '/thinking', gate: 'signed' },
|
|
240
|
-
{ id: 'copy-desk', label: 'Copy desk', icon: 'docs', href: '/copy-desk', gate: 'signed', module: 'copy-desk' },
|
|
241
234
|
// task 1003117 (R06 of goal 1000074): the Studio — artist mode, "a place an
|
|
242
|
-
// artist wants to be, not a form".
|
|
243
|
-
//
|
|
244
|
-
// ACT, not reference) or You (its subject is the product, not a person).
|
|
235
|
+
// artist wants to be, not a form". Here and not in Docs (a place you ACT,
|
|
236
|
+
// not reference) or You (its subject is the product, not a person).
|
|
245
237
|
// gate 'signed' and NOT craft-gated: an artist is a craft, not a rank, so
|
|
246
238
|
// hiding the artist's own room behind the artist craft would hide it from
|
|
247
|
-
// the artist
|
|
248
|
-
//
|
|
249
|
-
// rather than vanishing.
|
|
239
|
+
// the artist. NO module gate: it degrades to reviews alone on an instance
|
|
240
|
+
// without copy-desk, rather than vanishing.
|
|
250
241
|
{ id: 'studio', label: 'Studio', icon: 'studio', href: '/studio', gate: 'signed' },
|
|
251
242
|
] },
|
|
252
243
|
// Your own numbers. task 2174 folded Achievements into Standing and this task
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// studio-lib.js — the studio home's pure half (task 1003971 / BV2.TW16, goal
|
|
2
|
+
// 1000095; ADR 0341 D2, D5, D8 and D11; docs/design/mocks/tweak-mode/Main.dc.html).
|
|
3
|
+
//
|
|
4
|
+
// The studio is the artist's lofi home: the night room, a greeting whose words
|
|
5
|
+
// are editable where they stand, the tally in the corner, Resume, and three
|
|
6
|
+
// quick actions that open glass panels in place. Every word it says about the
|
|
7
|
+
// ledger is a pure function of the four reads it makes, so a test imports this
|
|
8
|
+
// file and runs the real rules:
|
|
9
|
+
//
|
|
10
|
+
// tallyParts(tally) the corner pill: credits, pages tweaked, this week
|
|
11
|
+
// resumeDetail(resume, c) "Home · landing · 12 of 38 lines"
|
|
12
|
+
// subParts(resume, c) the sub-line's parts ("Home", "landing", "Twelve", …)
|
|
13
|
+
// flowDetail(next) "next up: Projects — 2 people asked for it"
|
|
14
|
+
// statusDetail(pages) "6 of 37 pages tweaked"
|
|
15
|
+
// surfaceRows(pages, host) Artist Review Status: one row per surface
|
|
16
|
+
// landingHost(origin, host) the landing surface's address for that list
|
|
17
|
+
// askedLine(n) "4 pages have been asked for. …"
|
|
18
|
+
// pickLabel(candidate) a "pick another" chip: "Work · builders hall"
|
|
19
|
+
// matchLine(text, lines) which line of the studio's own reading a word on
|
|
20
|
+
// the page is, so an in-place edit names its key
|
|
21
|
+
// claimWords(code, detail) a refused claim, in words
|
|
22
|
+
//
|
|
23
|
+
// Node-require-safe (the tweak-editor-lib.js pattern): assignment is the only
|
|
24
|
+
// eval-time side effect, and only when a window exists.
|
|
25
|
+
(() => {
|
|
26
|
+
'use strict';
|
|
27
|
+
|
|
28
|
+
const editorLib = (typeof window !== 'undefined' && window.OTBTweakEditor)
|
|
29
|
+
|| (typeof require === 'function' ? require('./tweak-editor-lib.js') : null);
|
|
30
|
+
|
|
31
|
+
// The studio's own page id (docs/page-inventory.json). Its words are a tweak
|
|
32
|
+
// of this page like any other (ADR 0341 D2), never a live edit.
|
|
33
|
+
const STUDIO_PAGE = 'builders:studio';
|
|
34
|
+
const PAGE_ID_RE = /^[a-z0-9-]+:[a-z0-9-]+$/;
|
|
35
|
+
|
|
36
|
+
// A surface as the artboard says it in a line ("Home · landing · …").
|
|
37
|
+
const SURFACE_WORDS = Object.freeze({ landing: 'landing', builders: 'builders hall', status: 'status' });
|
|
38
|
+
function surfaceWord(id) { return SURFACE_WORDS[id] || String(id || ''); }
|
|
39
|
+
|
|
40
|
+
// What each surface is, under its name in Artist Review Status (the
|
|
41
|
+
// artboard's words). The landing surface is the instance's own address.
|
|
42
|
+
const SURFACE_WHERE = Object.freeze({ builders: 'where the work happens', status: 'the public dashboard' });
|
|
43
|
+
|
|
44
|
+
function titleOf(page) {
|
|
45
|
+
if (editorLib && typeof editorLib.displayTitle === 'function') return editorLib.displayTitle(page);
|
|
46
|
+
return String((page && (page.title || page.page_id)) || '');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function num(n) { const v = Number(n); return Number.isFinite(v) ? v : 0; }
|
|
50
|
+
|
|
51
|
+
// The artboard writes its counts in words ("Twelve of its thirty-eight lines").
|
|
52
|
+
const ONES = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten',
|
|
53
|
+
'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'];
|
|
54
|
+
const TENS = ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'];
|
|
55
|
+
function numberWords(n, { capital = false, zero = 'none' } = {}) {
|
|
56
|
+
const v = Math.floor(num(n));
|
|
57
|
+
let out;
|
|
58
|
+
if (v === 0) out = zero;
|
|
59
|
+
else if (v < 0 || v > 999) out = String(v);
|
|
60
|
+
else if (v < 20) out = ONES[v];
|
|
61
|
+
else if (v < 100) out = TENS[Math.floor(v / 10)] + (v % 10 ? `-${ONES[v % 10]}` : '');
|
|
62
|
+
else {
|
|
63
|
+
const rest = v % 100;
|
|
64
|
+
out = `${ONES[Math.floor(v / 100)]} hundred${rest ? ` and ${numberWords(rest)}` : ''}`;
|
|
65
|
+
}
|
|
66
|
+
return capital ? out.charAt(0).toUpperCase() + out.slice(1) : out;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// The corner pill. "+38" this week, the artboard's sign; a quiet week is 0.
|
|
70
|
+
function tallyParts(tally) {
|
|
71
|
+
if (!tally) return null;
|
|
72
|
+
const week = num(tally.this_week);
|
|
73
|
+
return { credits: String(num(tally.credits)), pages: String(num(tally.pages_tweaked)), week: week > 0 ? `+${week}` : String(week) };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Resume's second line. `counter` is the editor read's { changed, total }.
|
|
77
|
+
// Each sentence is ONE template literal, so the copy registry places the
|
|
78
|
+
// whole line the page shows (and an in-place tweak of it names one string).
|
|
79
|
+
function resumeDetail(resume, counter) {
|
|
80
|
+
if (!resume) return '';
|
|
81
|
+
const title = titleOf(resume);
|
|
82
|
+
const surface = surfaceWord(resume.surface);
|
|
83
|
+
if (!counter || !num(counter.total)) return `${title} · ${surface}`;
|
|
84
|
+
return `${title} · ${surface} · ${num(counter.changed)} of ${num(counter.total)} lines`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// The greeting's sub-line, when the artist holds a page: its parts, in the
|
|
88
|
+
// artboard's words ("Twelve", "thirty-eight"). Null otherwise: the page's own
|
|
89
|
+
// sentence (studio.html) stands. The SENTENCE is written where studio.js
|
|
90
|
+
// assigns it, because the sub-line is one of the studio's own editable lines,
|
|
91
|
+
// and the copy registry places a line at its textContent assignment: a
|
|
92
|
+
// sentence built in here would read as unplaced, and an in-place tweak of it
|
|
93
|
+
// could not name its string.
|
|
94
|
+
function subParts(resume, counter) {
|
|
95
|
+
if (!resume) return null;
|
|
96
|
+
const counted = !!counter && num(counter.total) > 0;
|
|
97
|
+
return {
|
|
98
|
+
title: titleOf(resume),
|
|
99
|
+
surface: surfaceWord(resume.surface),
|
|
100
|
+
counted,
|
|
101
|
+
changed: counted ? numberWords(counter.changed, { capital: true }) : '',
|
|
102
|
+
total: counted ? numberWords(counter.total) : '',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function flowDetail(next) {
|
|
107
|
+
const rec = next && next.recommended;
|
|
108
|
+
if (rec) return `next up: ${titleOf(rec)} — ${rec.why}`;
|
|
109
|
+
if (next) return 'every page is taken for now';
|
|
110
|
+
return 'the next page to tweak';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function statusDetail(pages) {
|
|
114
|
+
const t = pages && pages.totals;
|
|
115
|
+
return t && t.line ? t.line : 'how far each surface has come';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// The instance's own address, from the hall's host (tweak-editor-lib liveUrl's
|
|
119
|
+
// rule: the hall answers at builders.<apex>).
|
|
120
|
+
function apexHost(host) {
|
|
121
|
+
return String(host || '').replace(/^(builders|status)\./, '');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// The landing surface's address, as Artist Review Status names it
|
|
125
|
+
// ("cloudbongos.com"): the instance's own public origin, which the server
|
|
126
|
+
// stamps into the page from the branding pack, else the hall's host without
|
|
127
|
+
// its builders. prefix. An unstamped token ({{…}}) is no address.
|
|
128
|
+
function landingHost(stampedOrigin, host) {
|
|
129
|
+
const o = String(stampedOrigin || '').trim();
|
|
130
|
+
if (o && !/\{\{/.test(o)) return o.replace(/^[a-z]+:\/\//i, '').replace(/\/+$/, '');
|
|
131
|
+
return apexHost(host);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Artist Review Status: landing, builders hall, status, each "N of M tweaked"
|
|
135
|
+
// (ADR 0341 D8). The bar is one value against that surface's page count.
|
|
136
|
+
function surfaceRows(pages, host) {
|
|
137
|
+
return ((pages && pages.surfaces) || []).map((s) => {
|
|
138
|
+
const total = num(s.total);
|
|
139
|
+
const done = num(s.tweaked);
|
|
140
|
+
return {
|
|
141
|
+
id: s.id,
|
|
142
|
+
name: s.label || s.id,
|
|
143
|
+
where: s.id === 'landing' ? (host || 'the public site') : (SURFACE_WHERE[s.id] || ''),
|
|
144
|
+
count: s.line || `${done} of ${total} tweaked`,
|
|
145
|
+
pct: total > 0 ? Math.max(3, Math.min(100, Math.round((100 * done) / total))) : 3,
|
|
146
|
+
};
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function askedLine(n) {
|
|
151
|
+
const v = num(n);
|
|
152
|
+
if (v <= 0) return '';
|
|
153
|
+
return v === 1
|
|
154
|
+
? '1 page has been asked for. It comes up first in the Tweak/CopyWrite Flow.'
|
|
155
|
+
: `${v} pages have been asked for. They come up first in the Tweak/CopyWrite Flow.`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// A "pick another" chip names the page and its surface (the artboard's label).
|
|
159
|
+
function pickLabel(c) {
|
|
160
|
+
return `${titleOf(c)} · ${surfaceWord(c && c.surface)}`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Which reading line a word on the page IS. A reading line's text carries
|
|
164
|
+
// {…} for each hole the page fills in (the registry's convention), so each
|
|
165
|
+
// hole matches any run of words. The most specific match wins (the most
|
|
166
|
+
// literal characters), and a line that is only holes never matches anything.
|
|
167
|
+
const HOLE = '{…}';
|
|
168
|
+
function tidy(s) { return String(s == null ? '' : s).replace(/\s+/g, ' ').trim(); }
|
|
169
|
+
function escapeRe(s) { return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }
|
|
170
|
+
function matchLine(text, lines) {
|
|
171
|
+
const want = tidy(text);
|
|
172
|
+
if (!want) return null;
|
|
173
|
+
let best = null;
|
|
174
|
+
let bestLit = 0;
|
|
175
|
+
for (const l of lines || []) {
|
|
176
|
+
if (!l || typeof l.text !== 'string' || l.placement === 'shared') continue;
|
|
177
|
+
const parts = tidy(l.text).split(HOLE);
|
|
178
|
+
const literal = parts.join('').trim().length;
|
|
179
|
+
// A line mostly made of holes ("{…} of {…}") would match almost any
|
|
180
|
+
// sentence; its own words must be at least half of what the page shows.
|
|
181
|
+
if (!literal || literal * 2 < want.length) continue;
|
|
182
|
+
const re = new RegExp(`^${parts.map(escapeRe).join('.+?')}$`);
|
|
183
|
+
if (re.test(want) && literal > bestLit) { best = l; bestLit = literal; }
|
|
184
|
+
}
|
|
185
|
+
return best;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// A refused claim, in the artist's words. The server's own sentence
|
|
189
|
+
// (what_this_means) wins where it has one.
|
|
190
|
+
function claimWords(code, detail) {
|
|
191
|
+
const d = detail || {};
|
|
192
|
+
const holder = d.holder && (d.holder.login || d.holder.name);
|
|
193
|
+
if (code === 'page_held') return `${holder || 'Another artist'} is tweaking this page right now.`;
|
|
194
|
+
if (code === 'not_an_artist') return 'Only an artist can take a page. Your craft is set in Settings.';
|
|
195
|
+
if (d.what_this_means) return d.what_this_means;
|
|
196
|
+
if (code === 'page_in_flight') return 'This page is on its way to the site. It comes back to you to approve.';
|
|
197
|
+
if (code === 'no_open_version') return 'No version is being built right now, so there is nowhere for a page to land.';
|
|
198
|
+
return 'That page could not be taken just now. Nothing changed.';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const api = {
|
|
202
|
+
STUDIO_PAGE, PAGE_ID_RE, SURFACE_WORDS, surfaceWord, titleOf, numberWords, tallyParts, resumeDetail,
|
|
203
|
+
subParts, flowDetail, statusDetail, apexHost, landingHost, surfaceRows, askedLine, pickLabel, matchLine, claimWords,
|
|
204
|
+
};
|
|
205
|
+
if (typeof window !== 'undefined') window.OTBStudioLib = api;
|
|
206
|
+
if (typeof module !== 'undefined' && module.exports) module.exports = api;
|
|
207
|
+
})();
|