@bongos/core 1.24.26 → 1.24.28
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 +164 -109
- package/docs/adr/0307-autobongos-a-deterministic-supervisor-and-agentic-workers.md +12 -0
- package/docs/copy-inventory.md +242 -223
- package/docs/copy-registry.json +559 -358
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +18 -6
- package/docs/page-readings.json +4399 -4315
- package/docs/recipes/ui-look-before-you-ship.md +1 -0
- package/modules/autonomy/fence.js +16 -1
- package/modules/government/protected-surfaces.json +2 -0
- package/modules/hall-ui/public/ai-studio.html +1 -1
- 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/city.html +1 -1
- package/modules/hall-ui/public/collab.html +1 -1
- package/modules/hall-ui/public/commands.html +1 -1
- package/modules/hall-ui/public/copy-desk.html +1 -1
- package/modules/hall-ui/public/core-move-notice.js +9 -6
- package/modules/hall-ui/public/deploy-handoff.js +203 -0
- package/modules/hall-ui/public/deploy.html +4 -2
- package/modules/hall-ui/public/deploy.js +7 -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/goal-map.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 +1 -1
- package/modules/hall-ui/public/ideas.html +1 -1
- package/modules/hall-ui/public/index.html +1 -1
- package/modules/hall-ui/public/market-watch-item.html +1 -1
- package/modules/hall-ui/public/market-watch-rules.html +1 -1
- package/modules/hall-ui/public/market-watch-settings.html +1 -1
- package/modules/hall-ui/public/market-watch.html +1 -1
- package/modules/hall-ui/public/modules.html +1 -1
- package/modules/hall-ui/public/my-agents.css +72 -1
- package/modules/hall-ui/public/my-agents.html +31 -7
- package/modules/hall-ui/public/my-agents.js +63 -54
- package/modules/hall-ui/public/my-agents.states.json +18 -0
- 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-software-update.js +22 -8
- package/modules/hall-ui/public/settings.css +2 -132
- package/modules/hall-ui/public/settings.html +15 -53
- package/modules/hall-ui/public/shell.js +25 -73
- package/modules/hall-ui/public/speciality-tools.css +140 -0
- package/modules/hall-ui/public/{settings-specialities.js → speciality-tools.js} +83 -6
- package/modules/hall-ui/public/street.html +1 -1
- package/modules/hall-ui/public/studio.html +1 -1
- package/modules/hall-ui/public/style.css +119 -39
- 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/update-pill.js +623 -0
- package/modules/hall-ui/public/watch.html +1 -1
- package/modules/hall-ui/public/work.html +1 -1
- package/modules/hall-ui/records/my-agents.md +4 -2
- package/modules/hall-ui/records/panel-family.md +2 -0
- package/modules/hall-ui/records/update-pill.md +12 -0
- package/modules/lifecycle/github-push.js +1 -0
- package/modules/lifecycle/routes/tasks.js +2 -2
- package/modules/lifecycle/runner-self-guard.js +122 -0
- package/modules/lifecycle/runner-self.js +154 -0
- package/modules/public-landing/public/account.html +3 -3
- package/modules/public-landing/public/assets/cosmos.css +45 -0
- package/modules/public-landing/public/community.html +4 -4
- package/modules/public-landing/public/connections.html +1 -1
- package/modules/public-landing/public/guild.html +1 -1
- package/modules/public-landing/public/invitations.html +4 -4
- package/modules/public-landing/public/projects.html +26 -26
- package/modules/status-ui/public/index.html +2 -2
- package/modules/status-ui/public/index.states.json +12 -0
- package/modules/status-ui/public/status.js +10 -2
- package/modules/status-ui/public/style.css +57 -0
- package/modules/ui-design/kit/README.md +1 -1
- package/modules/ui-design/kit/serve.js +8 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +20 -0
- package/scripts/bongos/autobongos-loop.js +4 -0
- package/scripts/bongos/autobongos-run.js +21 -4
- package/scripts/bongos/autobongos-tweak-lane.js +5 -3
- package/scripts/bongos/ship-flow.js +20 -0
- package/scripts/hall-preview/server.js +4 -0
- package/src/module-api.js +1 -1
- package/tests/autobongos_host_access.mjs +5 -3
- package/tests/autobongos_self_modification.mjs +420 -0
- package/tests/core_update_banner.mjs +6 -92
- package/tests/core_upgrade_owner_door.mjs +4 -56
- package/tests/deploy_handoff.mjs +206 -0
- package/tests/government_protected_surfaces.mjs +15 -0
- package/tests/hall_loading.mjs +3 -1
- package/tests/hall_my_agents.mjs +104 -2
- package/tests/helpers.mjs +161 -0
- package/tests/hub_connections_page.mjs +1 -1
- package/tests/hub_invitations_page.mjs +1 -1
- package/tests/loading_mark.mjs +203 -0
- package/tests/nav_permission_atoms.mjs +3 -2
- package/tests/software_update.mjs +6 -2
- package/tests/speciality_offer_ui.mjs +182 -149
- package/tests/speciality_walkthrough_ui.mjs +180 -161
- package/tests/update_pill.mjs +431 -0
|
@@ -164,9 +164,12 @@ html[data-theme="dark"] body[data-page="my-agents"] {
|
|
|
164
164
|
.ma-group { margin-top: var(--sp-md); }
|
|
165
165
|
.ma-group:first-child { margin-top: 4px; }
|
|
166
166
|
.ma-rows { list-style: none; margin: 0; padding: 0; }
|
|
167
|
+
/* the fact keeps a floor (task 1005052): a row with more chips than fit beside it
|
|
168
|
+
— session cards, with its way back to the profile — wraps its chips instead of
|
|
169
|
+
squeezing the fact to a word a line */
|
|
167
170
|
.ma-row {
|
|
168
171
|
display: grid;
|
|
169
|
-
grid-template-columns: minmax(118px, 160px) minmax(
|
|
172
|
+
grid-template-columns: minmax(118px, 160px) minmax(132px, 1fr) auto;
|
|
170
173
|
gap: 6px 18px;
|
|
171
174
|
align-items: center;
|
|
172
175
|
min-height: 48px;
|
|
@@ -241,6 +244,74 @@ html[data-theme="dark"] body[data-page="my-agents"] {
|
|
|
241
244
|
.ma-row--sub { min-height: 40px; padding: 4px 0; }
|
|
242
245
|
.ma-row--sub .ma-row__fact { font-size: 13px; }
|
|
243
246
|
|
|
247
|
+
/* ---- what it knows: the speciality tools (task 1005052) ----
|
|
248
|
+
The SAME component Settings mounts (speciality-tools.js; its structure in
|
|
249
|
+
speciality-tools.css, linked before this sheet). Its markup is named in the
|
|
250
|
+
panel family's words (.prow, .btn, .pbtn) and this page has no panel.css, so
|
|
251
|
+
this skin draws those names in this page's own look: a hairline row like the
|
|
252
|
+
desk's, ghost pills, a chosen walkthrough answer as ink on glass. No accent —
|
|
253
|
+
on this page salmon would mean "needs you", and nothing here does. */
|
|
254
|
+
.ma-col { display: flex; flex-direction: column; gap: var(--sp-md); min-width: 0; }
|
|
255
|
+
.ma-know .prows { list-style: none; margin: 4px 0 0; padding: 0; }
|
|
256
|
+
.ma-know .prow { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 8px 18px; padding: 12px 0; border-top: var(--line) solid var(--rule-soft); }
|
|
257
|
+
.ma-know .prows > .prow:first-child { border-top: 0; }
|
|
258
|
+
.ma-know .prow__main { flex: 1 1 280px; min-width: 0; }
|
|
259
|
+
.ma-know .prow__label { display: flex; flex-wrap: wrap; align-items: baseline; margin: 0; font-size: 14.5px; font-weight: 500; letter-spacing: -0.01em; color: var(--ink); }
|
|
260
|
+
.ma-know .prow__hint { margin: 4px 0 0; max-width: var(--measure); font-size: 13px; line-height: 1.5; color: var(--ink-soft); }
|
|
261
|
+
.ma-know .prow__actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 6px; }
|
|
262
|
+
.ma-know .speciality-create__h { margin: 4px 0 0; font-size: 14px; font-weight: 500; color: var(--ink); }
|
|
263
|
+
.ma-know .btn,
|
|
264
|
+
.ma-know .pbtn {
|
|
265
|
+
display: inline-flex;
|
|
266
|
+
align-items: center;
|
|
267
|
+
justify-content: center;
|
|
268
|
+
min-height: 30px;
|
|
269
|
+
min-width: 30px;
|
|
270
|
+
padding: 0 14px;
|
|
271
|
+
border: var(--line) solid var(--rule);
|
|
272
|
+
border-radius: var(--radius-pill);
|
|
273
|
+
background: none;
|
|
274
|
+
color: var(--ink-soft);
|
|
275
|
+
font: inherit;
|
|
276
|
+
font-size: 13px;
|
|
277
|
+
font-weight: 400;
|
|
278
|
+
text-transform: lowercase;
|
|
279
|
+
white-space: nowrap;
|
|
280
|
+
cursor: pointer;
|
|
281
|
+
}
|
|
282
|
+
.ma-know .btn:hover:not(:disabled),
|
|
283
|
+
.ma-know .pbtn:hover:not(:disabled) { background: none; color: var(--ink); border-color: var(--ink-faint); }
|
|
284
|
+
.ma-know .btn:disabled,
|
|
285
|
+
.ma-know .pbtn:disabled { background: none; color: var(--ink-faint); border-color: var(--rule-soft); cursor: default; }
|
|
286
|
+
/* the walkthrough's answer you gave, and its one commit, read as a chosen chip */
|
|
287
|
+
.ma-know .pbtn[aria-pressed="true"],
|
|
288
|
+
.ma-know .pbtn--save:not(:disabled) { background: var(--glass-strong); color: var(--ink); border-color: var(--ink-soft); }
|
|
289
|
+
/* the 24px floor, drawn in ink: the create form's "for sale" box and the skill picker */
|
|
290
|
+
.ma-know input[type="checkbox"] { width: 24px; height: 24px; margin: 0; flex: none; accent-color: var(--ink); cursor: pointer; }
|
|
291
|
+
.ma-know .speciality-field > span { color: var(--ink-soft); }
|
|
292
|
+
/* "create a speciality": the form folds away until asked for, so the section opens on what you have */
|
|
293
|
+
.ma-know__new { margin-top: 10px; padding-top: 12px; border-top: var(--line) solid var(--rule-soft); }
|
|
294
|
+
.ma-know__new > summary {
|
|
295
|
+
display: inline-flex;
|
|
296
|
+
align-items: center;
|
|
297
|
+
min-height: 30px;
|
|
298
|
+
padding: 0 14px;
|
|
299
|
+
border: var(--line) solid var(--rule);
|
|
300
|
+
border-radius: var(--radius-pill);
|
|
301
|
+
font-size: 13px;
|
|
302
|
+
color: var(--ink-soft);
|
|
303
|
+
list-style: none;
|
|
304
|
+
cursor: pointer;
|
|
305
|
+
}
|
|
306
|
+
.ma-know__new > summary::-webkit-details-marker { display: none; }
|
|
307
|
+
.ma-know__new > summary:hover { color: var(--ink); border-color: var(--ink-faint); }
|
|
308
|
+
.ma-know__new[open] > summary { color: var(--ink); background: var(--glass-strong); border-color: var(--ink-soft); }
|
|
309
|
+
.ma-know__new .speciality-create { margin-top: 4px; padding-top: 0; border-top: 0; }
|
|
310
|
+
/* the fold's own label already names it, so the form's "Create one" would say it twice */
|
|
311
|
+
.ma-know__new .speciality-create > .speciality-create__h:first-child { display: none; }
|
|
312
|
+
.ma-know__status { margin: 8px 0 0; font-size: 13px; color: var(--ink-soft); }
|
|
313
|
+
.ma-know__status:empty { display: none; }
|
|
314
|
+
|
|
244
315
|
/* ---- what the agent reads first ---- */
|
|
245
316
|
.ma-read { margin: 0 0 12px; }
|
|
246
317
|
.ma-read--project { padding: 12px 14px; background: var(--ma-law-wash); border-radius: var(--radius-sm); }
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
group head and the foot line (records/oversight-family.md). This page's
|
|
14
14
|
repeated thing is that row at the desk's one-line density. -->
|
|
15
15
|
<link rel="stylesheet" href="/builders/oversight.css">
|
|
16
|
+
<!-- the speciality tools' structural sheet, shared with Settings (task 1005052);
|
|
17
|
+
before this page's own, whose .ma-know skin draws them in this page's look -->
|
|
18
|
+
<link rel="stylesheet" href="/builders/speciality-tools.css">
|
|
16
19
|
<link rel="stylesheet" href="/builders/my-agents.css">
|
|
17
20
|
</head>
|
|
18
21
|
<body data-page="my-agents" data-page-title="My agents">
|
|
@@ -38,11 +41,30 @@
|
|
|
38
41
|
<section class="ma-strict" id="ma-strict" aria-labelledby="ma-strict-h" hidden></section>
|
|
39
42
|
|
|
40
43
|
<div class="ma-grid">
|
|
41
|
-
<
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
<div class="ma-col">
|
|
45
|
+
<section class="scroll ma-desk" id="ma-desk" aria-labelledby="ma-desk-h">
|
|
46
|
+
<h2 class="scroll__h" id="ma-desk-h">Your agents</h2>
|
|
47
|
+
<p class="scroll__lede" id="ma-desk-lede">One line each. Tap to change; it saves straight away.</p>
|
|
48
|
+
<div id="ma-rows" aria-live="polite"><p class="hall-loading" role="status">Reading your settings…</p></div>
|
|
49
|
+
</section>
|
|
50
|
+
|
|
51
|
+
<!-- task 1005052: WHAT IT KNOWS — every speciality tool Settings has, from
|
|
52
|
+
the SAME component (speciality-tools.js fills these hosts by id). A
|
|
53
|
+
section of its own, not desk rows: a desk row holds at most two
|
|
54
|
+
buttons, and my-agents.js redraws the desk on every change. Ships
|
|
55
|
+
hidden; the component reveals it once its reads answer. -->
|
|
56
|
+
<section class="scroll ma-know" id="specialities-scroll" aria-labelledby="specialities-h" hidden>
|
|
57
|
+
<h2 class="scroll__h" id="specialities-h">What it knows</h2>
|
|
58
|
+
<p class="scroll__lede">A speciality is what you work on, and how you want to be asked about it. The one you use reaches every session where you hold work in its role.</p>
|
|
59
|
+
<div id="speciality-suggestions"></div>
|
|
60
|
+
<ul class="prows" id="specialities-rows"></ul>
|
|
61
|
+
<details class="ma-know__new">
|
|
62
|
+
<summary>create a speciality</summary>
|
|
63
|
+
<form id="speciality-create" class="speciality-create"></form>
|
|
64
|
+
</details>
|
|
65
|
+
<p class="ma-know__status" id="specialities-status" role="status" aria-live="polite"></p>
|
|
66
|
+
</section>
|
|
67
|
+
</div>
|
|
46
68
|
|
|
47
69
|
<aside class="scroll ma-reads" id="ma-reads" aria-labelledby="ma-reads-h">
|
|
48
70
|
<h2 class="scroll__h" id="ma-reads-h">What your agent reads first</h2>
|
|
@@ -62,11 +84,13 @@
|
|
|
62
84
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
63
85
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
64
86
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
65
|
-
<script src="/builders/shell.js?v=2026-
|
|
87
|
+
<script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
|
|
66
88
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
67
89
|
<script src="/builders/palette.js" defer></script>
|
|
68
90
|
<script src="/builders/gov-living.js?v=2026-10-10-real1"></script>
|
|
69
91
|
<script src="/builders/gov-positions.js?v=2026-10-09-positions1"></script>
|
|
70
|
-
<script src="/builders/my-agents.js?v=2026-
|
|
92
|
+
<script src="/builders/my-agents.js?v=2026-11-03-defaults"></script>
|
|
93
|
+
<!-- the speciality tools (task 1005052): the one component Settings mounts too -->
|
|
94
|
+
<script src="/builders/speciality-tools.js?v=2026-11-03-shared"></script>
|
|
71
95
|
</body>
|
|
72
96
|
</html>
|
|
@@ -7,12 +7,19 @@
|
|
|
7
7
|
// yours. Every row is one of your agent settings, and a project law can LOCK it.
|
|
8
8
|
// Every part of it is a real read (task 1004717: the prototype overlay is gone):
|
|
9
9
|
// real EVERY ROW (task 1004714). GET /me's interaction profile, wandering,
|
|
10
|
-
// model allocation and session cards; GET /me/skill-prefs;
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// /
|
|
15
|
-
//
|
|
10
|
+
// model allocation and session cards; GET /me/skill-prefs; the sounds
|
|
11
|
+
// switch (off by the owner's call, .claude/sounds/play.js). Writes go to
|
|
12
|
+
// the real routes (PATCH /me/interaction|wandering|model-allocation|
|
|
13
|
+
// render|skill-prefs), so in the preview harness they reach its recorder
|
|
14
|
+
// (scripts/hall-preview/pref-writes.js). Session cards and each skill
|
|
15
|
+
// voice can go BACK to the default (task 1005052): the first chip clears
|
|
16
|
+
// your own pick (PATCH ... null), as Settings' "default" choices do.
|
|
17
|
+
// real WHAT IT KNOWS (task 1005052): every speciality tool Settings has —
|
|
18
|
+
// create, train, choose what it offers, adopt (its instructions read in
|
|
19
|
+
// full first), the skills walkthrough, stop using. Not this file: the
|
|
20
|
+
// SAME component Settings mounts (speciality-tools.js) fills its own
|
|
21
|
+
// section; this page only re-reads /me when it says a speciality changed,
|
|
22
|
+
// so "What your agent reads first" stays true.
|
|
16
23
|
// real the project's laws and the locks they put on your settings (task
|
|
17
24
|
// 1004707): GET /me's `laws` block — the very words every session reads —
|
|
18
25
|
// and `laws.locks`, what each lock did to you, enforced by the server. A
|
|
@@ -57,7 +64,7 @@
|
|
|
57
64
|
'model.main': { name: 'Main session' },
|
|
58
65
|
'model.subagentDefault': { name: 'Helpers' },
|
|
59
66
|
'model.subagentRoutine': { name: 'Routine helpers' },
|
|
60
|
-
render: { name: 'Session cards', words: { minimal: 'one line', text: 'plain text', standard: 'rich card', rich: 'rich, open' } },
|
|
67
|
+
render: { name: 'Session cards', words: { '': 'follow my profile', minimal: 'one line', text: 'plain text', standard: 'rich card', rich: 'rich, open' } },
|
|
61
68
|
};
|
|
62
69
|
// The law a habit would become, in the ask-format voice: a short plain sentence.
|
|
63
70
|
const LAW_LINES = {
|
|
@@ -70,7 +77,9 @@
|
|
|
70
77
|
{ key: 'talk', title: 'How it talks to you', keys: ['technical_level', 'toolchain_familiarity', 'verbosity', 'machinery', 'render', 'sounds'] },
|
|
71
78
|
{ key: 'work', title: 'How your agents work', keys: ['wandering'] },
|
|
72
79
|
{ key: 'models', title: 'Which models run', keys: ['model.main', 'model.subagentDefault', 'model.subagentRoutine', 'skills'] },
|
|
73
|
-
|
|
80
|
+
// 'What it knows' (the specialities) is a section of its own since task
|
|
81
|
+
// 1005052: its tools are more than a desk row's two buttons, and they are the
|
|
82
|
+
// shared speciality-tools.js component, which this desk's redraw would wipe.
|
|
74
83
|
];
|
|
75
84
|
// the permission atom that may file a laws vote (POST /government/board/laws)
|
|
76
85
|
const PROPOSE_ATOM = 'board.item.open';
|
|
@@ -86,8 +95,8 @@
|
|
|
86
95
|
const nameFor = (key) => (SETTING[key] ? SETTING[key].name : key);
|
|
87
96
|
|
|
88
97
|
// ---------- the pure view ----------
|
|
89
|
-
// buildView({ me, constitution, presets, skills,
|
|
90
|
-
// proposed, switched,
|
|
98
|
+
// buildView({ me, constitution, presets, skills, preview,
|
|
99
|
+
// proposed, switched, as, project, now }, { GovLiving, GovPositions })
|
|
91
100
|
// → { project, positions, strict, groups[], habits[], canPropose, reads, counts }
|
|
92
101
|
// `preview` is a strictness preset key to look at instead of the project's own;
|
|
93
102
|
// `presets` is GET /government/presets' body (task 1004715); `switched` maps a
|
|
@@ -132,9 +141,16 @@
|
|
|
132
141
|
if (!r) return null;
|
|
133
142
|
const levels = arr(r.levels).map((l) => l.value);
|
|
134
143
|
const level = str(r.level || r.derived_level);
|
|
144
|
+
// task 1005052: your own pick, or none — then the cards follow the level
|
|
145
|
+
// your interaction profile derives, and "follow my profile" (value '',
|
|
146
|
+
// written as null) is the chip pressed. A pinned level survives a later
|
|
147
|
+
// profile change; following does not pin today's.
|
|
148
|
+
const own = r.override_level == null ? null : str(r.override_level);
|
|
135
149
|
return {
|
|
136
|
-
key, name: nameFor(key), fact: wordFor(key, level),
|
|
137
|
-
|
|
150
|
+
key, name: nameFor(key), fact: wordFor(key, level), note: own === null ? 'follows your profile' : null,
|
|
151
|
+
locked: false, lock: null, cap: null, control: 'chips', value: own === null ? '' : own, effective: level,
|
|
152
|
+
options: [{ value: '', label: wordFor(key, ''), pressed: own === null }]
|
|
153
|
+
.concat(levels.map((v) => ({ value: v, label: wordFor(key, v), pressed: v === own }))),
|
|
138
154
|
};
|
|
139
155
|
}
|
|
140
156
|
if (key === 'sounds') {
|
|
@@ -149,19 +165,17 @@
|
|
|
149
165
|
return {
|
|
150
166
|
key, name: 'Per skill', fact: eff.length + ' skills' + (changed ? ' · ' + changed + ' yours' : ' · all default'),
|
|
151
167
|
locked: false, lock: null, cap: null, control: 'expand', options: [],
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
key, name: 'Specialities', fact: adopted.length ? adopted.join(', ') : 'none yet', locked: false, lock: null, cap: null, control: 'adopt',
|
|
164
|
-
options: offer.map((x) => ({ value: String(x.id), label: str(x.name).toLowerCase(), pressed: adopted.indexOf(str(x.name)) >= 0 })),
|
|
168
|
+
// task 1005052: "default" (value '', written as null) clears your own
|
|
169
|
+
// pick, as Settings' Default does; a model is pressed only if you chose it.
|
|
170
|
+
skills: eff.map((e) => {
|
|
171
|
+
const own = e.override_model == null ? null : str(e.override_model);
|
|
172
|
+
return {
|
|
173
|
+
key: 'skill.' + e.skill, skill: e.skill, value: e.effective_model,
|
|
174
|
+
note: own === null ? null : 'default: ' + str(e.default_model),
|
|
175
|
+
options: [{ value: '', label: 'default', pressed: own === null }]
|
|
176
|
+
.concat(arr(sk.allowed_models).map((v) => ({ value: v, label: v, pressed: v === own }))),
|
|
177
|
+
};
|
|
178
|
+
}),
|
|
165
179
|
};
|
|
166
180
|
}
|
|
167
181
|
return null;
|
|
@@ -170,7 +184,7 @@
|
|
|
170
184
|
const rows = g.keys.map((k) => settingRow(k) || extraRow(k)).filter(Boolean);
|
|
171
185
|
return { key: g.key, title: g.title, rows, source: 'real', locked: rows.filter((r) => r.locked).length };
|
|
172
186
|
}).filter((g) => g.rows.length);
|
|
173
|
-
const lockableKeys = groups.reduce((a, g) => a.concat(g.rows.filter((r) => r.key !== 'sounds'
|
|
187
|
+
const lockableKeys = groups.reduce((a, g) => a.concat(g.rows.filter((r) => r.key !== 'sounds').map((r) => r.key)), []);
|
|
174
188
|
const total = lockableKeys.length;
|
|
175
189
|
const lockedNow = groups.reduce((n, g) => n + g.locked, 0);
|
|
176
190
|
|
|
@@ -273,8 +287,9 @@
|
|
|
273
287
|
return n < 0 ? null : { method: 'PATCH', path: '/me/wandering', body: { wandering: n }, slice: 'wandering' };
|
|
274
288
|
}
|
|
275
289
|
if (key.indexOf('model.') === 0) return { method: 'PATCH', path: '/me/model-allocation', body: { [key.slice(6)]: value }, slice: 'model_allocation' };
|
|
276
|
-
|
|
277
|
-
if (key
|
|
290
|
+
// '' is the way back to the default: null clears your own pick (task 1005052)
|
|
291
|
+
if (key === 'render') return { method: 'PATCH', path: '/me/render', body: { widgets: value === '' ? null : value }, slice: 'render' };
|
|
292
|
+
if (key.indexOf('skill.') === 0) return { method: 'PATCH', path: '/me/skill-prefs', body: { [key.slice(6)]: value === '' ? null : value }, slice: 'skills' };
|
|
278
293
|
return null;
|
|
279
294
|
}
|
|
280
295
|
|
|
@@ -322,7 +337,7 @@
|
|
|
322
337
|
const LOCK = '<svg class="ma-lock__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3.5" y="7" width="9" height="6.5" rx="1.5"/><path d="M5.5 7V5.2a2.5 2.5 0 0 1 5 0V7"/></svg>';
|
|
323
338
|
const TICK = '<svg class="ma-tick" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3.5 8.5l3 3 6-7"/></svg>';
|
|
324
339
|
|
|
325
|
-
const S = { me: null, constitution: null, presets: null, skills: null,
|
|
340
|
+
const S = { me: null, constitution: null, presets: null, skills: null, preview: null, proposed: {}, pending: {}, switched: {}, skillsOpen: false, readAll: false, view: null };
|
|
326
341
|
|
|
327
342
|
function asNow() {
|
|
328
343
|
try { return root.sessionStorage.getItem('hall-as') === 'nobody' || /[?&]as=nobody\b/.test(root.location.search) ? 'nobody' : null; }
|
|
@@ -330,8 +345,8 @@
|
|
|
330
345
|
}
|
|
331
346
|
function view() {
|
|
332
347
|
S.view = buildView({
|
|
333
|
-
me: S.me, constitution: S.constitution, presets: S.presets, skills: S.skills,
|
|
334
|
-
preview: S.preview, proposed: S.proposed, pending: S.pending, switched: S.switched,
|
|
348
|
+
me: S.me, constitution: S.constitution, presets: S.presets, skills: S.skills,
|
|
349
|
+
preview: S.preview, proposed: S.proposed, pending: S.pending, switched: S.switched, as: asNow(), project: PROJECT, now: Date.now(),
|
|
335
350
|
}, { GovLiving: root.GovLiving, GovPositions: root.GovPositions });
|
|
336
351
|
return S.view;
|
|
337
352
|
}
|
|
@@ -345,10 +360,6 @@
|
|
|
345
360
|
// every lock is a real law, enforced by the server (task 1004714)
|
|
346
361
|
if (row.locked) return '<span class="ma-lock">' + LOCK + '<span>' + esc(row.lock.text) + '</span></span>';
|
|
347
362
|
if (row.control === 'chips') return chipsHtml(row, row.name);
|
|
348
|
-
if (row.control === 'adopt') {
|
|
349
|
-
return row.options.length ? '<div class="ma-chips" role="group" aria-label="Adopt a speciality">' + row.options.map((op) =>
|
|
350
|
-
'<button type="button" class="ma-chip" data-adopt="' + esc(op.value) + '" aria-pressed="' + (op.pressed ? 'true' : 'false') + '"' + (op.pressed ? ' disabled' : '') + '>' + esc(op.pressed ? op.label : '+ ' + op.label) + '</button>').join('') + '</div>' : '';
|
|
351
|
-
}
|
|
352
363
|
if (row.control === 'expand') {
|
|
353
364
|
return '<button type="button" class="btn-ghost ma-more" data-expand="skills" aria-expanded="' + (S.skillsOpen ? 'true' : 'false') + '" aria-controls="ma-skills">' + (S.skillsOpen ? 'hide' : 'show') + '</button>';
|
|
354
365
|
}
|
|
@@ -357,14 +368,15 @@
|
|
|
357
368
|
function rowHtml(row) {
|
|
358
369
|
// a cap (task 1004714): the chips offer the values under it, and the line says which law
|
|
359
370
|
const sub = (row.cap ? '<span class="ma-row__yours ma-row__cap">' + LOCK + 'capped by ' + esc(row.cap.text) + '</span>' : '') +
|
|
360
|
-
(row.yours ? '<span class="ma-row__yours">yours: ' + esc(row.yours) + '</span>' : '')
|
|
371
|
+
(row.yours ? '<span class="ma-row__yours">yours: ' + esc(row.yours) + '</span>' : '') +
|
|
372
|
+
(row.note ? '<span class="ma-row__yours">' + esc(row.note) + '</span>' : '');
|
|
361
373
|
let html = '<li class="ma-row' + (row.locked ? ' ma-row--locked' : '') + '" data-row="' + esc(row.key) + '">' +
|
|
362
374
|
'<span class="ma-row__name">' + esc(row.name) + TICK + '</span>' +
|
|
363
375
|
'<span class="ma-row__fact"><span>' + esc(row.fact) + '</span>' + sub + '</span>' +
|
|
364
376
|
'<span class="ma-row__act">' + controlHtml(row) + '</span></li>';
|
|
365
377
|
if (row.control === 'expand' && S.skillsOpen && !row.locked) {
|
|
366
378
|
html += '<li class="ma-sub" id="ma-skills"><ul class="ma-rows ma-rows--sub">' + row.skills.map((s) =>
|
|
367
|
-
'<li class="ma-row ma-row--sub" data-row="' + esc(s.key) + '"><span class="ma-row__name ma-mono">' + esc(s.skill) + TICK + '</span><span class="ma-row__fact"><span>' + esc(s.value) + '</span
|
|
379
|
+
'<li class="ma-row ma-row--sub" data-row="' + esc(s.key) + '"><span class="ma-row__name ma-mono">' + esc(s.skill) + TICK + '</span><span class="ma-row__fact"><span>' + esc(s.value) + '</span>' + (s.note ? '<span class="ma-row__yours">' + esc(s.note) + '</span>' : '') + '</span>' +
|
|
368
380
|
'<span class="ma-row__act">' + chipsHtml({ key: s.key, options: s.options }, s.skill) + '</span></li>').join('') + '</ul></li>';
|
|
369
381
|
}
|
|
370
382
|
return html;
|
|
@@ -502,7 +514,9 @@
|
|
|
502
514
|
const row = findRow(key);
|
|
503
515
|
if (!row || row.locked) return;
|
|
504
516
|
const w = writeFor(key, value, S);
|
|
505
|
-
const
|
|
517
|
+
const skill = key.indexOf('skill.') === 0 ? key.slice(6) : null;
|
|
518
|
+
const said = value === '' ? (skill ? skill + ' on its default model' : nameFor(key).toLowerCase() + ' follow your profile')
|
|
519
|
+
: (skill || nameFor(key).toLowerCase()) + ' ' + wordFor(key, value);
|
|
506
520
|
if (!w) return;
|
|
507
521
|
api.request(w.method, API + w.path, { body: w.body }).then((r) => {
|
|
508
522
|
if (r && r.ok && r.data) {
|
|
@@ -525,8 +539,8 @@
|
|
|
525
539
|
if (w.slice === 'interaction' && S.me.interaction) { S.me.interaction.effective = Object.assign({}, S.me.interaction.effective, { [key]: value }); S.me.interaction.stored = Object.assign({}, S.me.interaction.stored, { [key]: value }); }
|
|
526
540
|
if (w.slice === 'wandering' && S.me.wandering) { S.me.wandering.effective_key = value; S.me.wandering.override_level = w.body.wandering; }
|
|
527
541
|
if (w.slice === 'model_allocation' && S.me.model_allocation) S.me.model_allocation.effective = Object.assign({}, S.me.model_allocation.effective, w.body);
|
|
528
|
-
if (w.slice === 'render' && S.me.render) S.me.render.level
|
|
529
|
-
if (w.slice === 'skills' && S.skills) arr(S.skills.effective).forEach((e) => { if (e.skill === key.slice(6)) { e.effective_model = value; e.override_model = value; } });
|
|
542
|
+
if (w.slice === 'render' && S.me.render) Object.assign(S.me.render, value === '' ? { level: S.me.render.derived_level, override_level: null } : { level: value, override_level: value });
|
|
543
|
+
if (w.slice === 'skills' && S.skills) arr(S.skills.effective).forEach((e) => { if (e.skill === key.slice(6)) { e.effective_model = value === '' ? e.default_model : value; e.override_model = value === '' ? null : value; } });
|
|
530
544
|
}
|
|
531
545
|
function findRow(key) {
|
|
532
546
|
const v = S.view;
|
|
@@ -539,16 +553,6 @@
|
|
|
539
553
|
}
|
|
540
554
|
return null;
|
|
541
555
|
}
|
|
542
|
-
function adopt(id) {
|
|
543
|
-
const sp = arr(S.specialities && S.specialities.specialities).find((x) => String(x.id) === String(id));
|
|
544
|
-
if (!sp) return;
|
|
545
|
-
api.request('POST', API + '/specialities/' + encodeURIComponent(id) + '/adopt', { body: {} }).then((r) => {
|
|
546
|
-
if (r && r.ok) {
|
|
547
|
-
S.adopted.push(str(sp.name)); render(); lit('specialities');
|
|
548
|
-
toast(sp.name + ' adopted. Its skills stay off until you turn them on.');
|
|
549
|
-
} else toast('Not adopted' + (r && r.status ? ' (' + r.status + ')' : '') + '. Nothing changed.');
|
|
550
|
-
}, () => toast('Not adopted: no answer.'));
|
|
551
|
-
}
|
|
552
556
|
// A habit proposed as a law: a real laws vote (POST /government/board/laws,
|
|
553
557
|
// task 1004706), sat under the constitution in force like any amendment.
|
|
554
558
|
function propose(key) {
|
|
@@ -604,9 +608,15 @@
|
|
|
604
608
|
if (b.hasAttribute('data-set')) { const row = findRow(b.getAttribute('data-set')); const raw = b.getAttribute('data-value'); const opt = row && row.options.find((x) => String(x.value) === raw); if (opt && !opt.pressed) choose(row.key, opt.value); return; }
|
|
605
609
|
if (b.hasAttribute('data-expand')) { S.skillsOpen = !S.skillsOpen; render(); return; }
|
|
606
610
|
if (b.hasAttribute('data-readall')) { S.readAll = !S.readAll; render(); return; }
|
|
607
|
-
if (b.hasAttribute('data-adopt')) { adopt(b.getAttribute('data-adopt')); return; }
|
|
608
611
|
if (b.hasAttribute('data-propose')) { propose(b.getAttribute('data-propose')); }
|
|
609
612
|
});
|
|
613
|
+
// The speciality tools (speciality-tools.js, their own section) say when what
|
|
614
|
+
// a session is sent moved: an adopt, a stop, a skills save. /me carries it,
|
|
615
|
+
// so read it again and "What your agent reads first" stays true (task 1005052).
|
|
616
|
+
document.addEventListener('otb:specialities-changed', async () => {
|
|
617
|
+
const me = await getJSON('/me');
|
|
618
|
+
if (me && me.builder) { S.me = me; render(); }
|
|
619
|
+
});
|
|
610
620
|
}
|
|
611
621
|
|
|
612
622
|
async function boot() {
|
|
@@ -618,15 +628,14 @@
|
|
|
618
628
|
return;
|
|
619
629
|
}
|
|
620
630
|
S.me = me;
|
|
621
|
-
const [constitution, presets, skills,
|
|
622
|
-
getJSON('/government/constitution'), getJSON('/government/presets'), getJSON('/me/skill-prefs'),
|
|
631
|
+
const [constitution, presets, skills, room] = await Promise.all([
|
|
632
|
+
getJSON('/government/constitution'), getJSON('/government/presets'), getJSON('/me/skill-prefs'),
|
|
623
633
|
getJSON('/government/board/items'),
|
|
624
634
|
]);
|
|
625
635
|
S.pending = pendingLaws(room);
|
|
626
636
|
S.presets = presets || null;
|
|
627
637
|
S.constitution = constitution;
|
|
628
638
|
S.skills = skills || null;
|
|
629
|
-
S.specialities = specialities || null;
|
|
630
639
|
render();
|
|
631
640
|
}
|
|
632
641
|
boot();
|
|
@@ -25,6 +25,24 @@
|
|
|
25
25
|
"actions": [["wait", 900], ["clickIfVisible", "[data-craft-close]"], ["wait", 300], ["click", "button[data-set=\"verbosity\"][aria-pressed=\"false\"]"], ["wait", 400]],
|
|
26
26
|
"expect": { "visible": [".ma-row[data-saved] .ma-tick"] }
|
|
27
27
|
},
|
|
28
|
+
"what-it-knows": {
|
|
29
|
+
"auth": true,
|
|
30
|
+
"_": "Task 1005052: the speciality tools, the same component Settings mounts. Someone else's speciality (Economics) shows its full instructions open beside its one adopt control; your own (Pricing, in use) carries choose its skills, train, offered skills and stop using. Session cards above follow the profile.",
|
|
31
|
+
"actions": [["wait", 900], ["clickIfVisible", "[data-craft-close]"], ["wait", 300], ["focus", "#specialities-rows button[data-act=\"adopt\"]"], ["wait", 300]],
|
|
32
|
+
"expect": { "visible": ["#specialities-scroll", ".speciality-contract[open]", "#specialities-rows button[data-act=\"abandon\"]", "#specialities-rows button[data-act=\"walk\"]", "button[data-set=\"render\"][data-value=\"\"][aria-pressed=\"true\"]"] }
|
|
33
|
+
},
|
|
34
|
+
"speciality-walk": {
|
|
35
|
+
"auth": true,
|
|
36
|
+
"_": "Adopting Economics opens the skills walkthrough in its card: one skill per step, leave it off or turn it on at equal weight.",
|
|
37
|
+
"actions": [["wait", 900], ["clickIfVisible", "[data-craft-close]"], ["wait", 300], ["click", "#specialities-rows button[data-act=\"adopt\"]"], ["wait", 700]],
|
|
38
|
+
"expect": { "visible": [".skill-walk", ".skill-walk__facts", "button[data-walk=\"on\"]", "button[data-walk=\"off\"]"] }
|
|
39
|
+
},
|
|
40
|
+
"speciality-forms": {
|
|
41
|
+
"auth": true,
|
|
42
|
+
"_": "The owner's tools open in place: Train on Pricing, and the folded create form with its skills picker.",
|
|
43
|
+
"actions": [["wait", 900], ["clickIfVisible", "[data-craft-close]"], ["wait", 300], ["click", "#specialities-rows button[data-act=\"train\"]"], ["wait", 300], ["click", ".ma-know__new > summary"], ["wait", 300], ["click", "#speciality-create-offer > summary"], ["wait", 900]],
|
|
44
|
+
"expect": { "visible": ["form.speciality-train", "#speciality-create input[name=\"name\"]", "#speciality-create-skills .speciality-offer__list"] }
|
|
45
|
+
},
|
|
28
46
|
"nobody": {
|
|
29
47
|
"auth": true,
|
|
30
48
|
"url": "/my-agents?as=nobody",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
114
114
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
115
115
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
116
|
-
<script src="/builders/shell.js?v=2026-
|
|
116
|
+
<script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
|
|
117
117
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
118
118
|
<script src="/builders/palette.js" defer></script>
|
|
119
119
|
<!-- the shared markdown reader (window.OTBMdReader) — primer.js renders through
|
|
@@ -208,7 +208,7 @@
|
|
|
208
208
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
209
209
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
210
210
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
211
|
-
<script src="/builders/shell.js?v=2026-
|
|
211
|
+
<script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
|
|
212
212
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
213
213
|
<script src="/builders/palette.js" defer></script>
|
|
214
214
|
<!-- task 1004727: the ONE karma vote control every task/idea list renders -->
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
88
88
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
89
89
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
90
|
-
<script src="/builders/shell.js?v=2026-
|
|
90
|
+
<script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
|
|
91
91
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
92
92
|
<script src="/builders/palette.js" defer></script>
|
|
93
93
|
<script src="/builders/hall-kit.js"></script>
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
75
75
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
76
76
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
77
|
-
<script src="/builders/shell.js?v=2026-
|
|
77
|
+
<script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
|
|
78
78
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
79
79
|
<script src="/builders/palette.js" defer></script>
|
|
80
80
|
<script src="/builders/founding-mark.js?v=2026-09-30-founding"></script>
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
45
45
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
46
46
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
47
|
-
<script src="/builders/shell.js?v=2026-
|
|
47
|
+
<script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
|
|
48
48
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
49
49
|
<script src="/builders/palette.js" defer></script>
|
|
50
50
|
<!-- The chart kit and the timeline's series builders (task 1003447). Both MUST
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
57
57
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
58
58
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
59
|
-
<script src="/builders/shell.js?v=2026-
|
|
59
|
+
<script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
|
|
60
60
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
61
61
|
<script src="/builders/palette.js" defer></script>
|
|
62
62
|
<!-- the branding pack's currency label, loaded BEFORE roster.js. -->
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
115
115
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
116
116
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
117
|
-
<script src="/builders/shell.js?v=2026-
|
|
117
|
+
<script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
|
|
118
118
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
119
119
|
<script src="/builders/palette.js" defer></script>
|
|
120
120
|
<script src="/builders/task-detail.js?v=2026-07-03-taskdetail"></script>
|
|
@@ -7,11 +7,12 @@
|
|
|
7
7
|
// update channel lets it take; what each version in between brings — and, for the people
|
|
8
8
|
// the upgrade door lets through, an Update button.
|
|
9
9
|
//
|
|
10
|
-
// THE BUTTON
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
10
|
+
// THE BUTTON RUNS THE PILL'S FLOW; IT IS NOT A SECOND DOOR. Taking an update is the deploy
|
|
11
|
+
// door's act, with its health check and auto-rollback (ADR 0293), and on a hosted project that
|
|
12
|
+
// door is on the hub (ADR 0339 D3). Since task 1005041 the button hands this panel's reading to
|
|
13
|
+
// update-pill.js, which takes the update in place (this page names the version; the door moves
|
|
14
|
+
// it), so there is one copy of that flow, not two. The server still names the door (`door`),
|
|
15
|
+
// and the button's href is that door, for a page whose pill script did not load.
|
|
15
16
|
//
|
|
16
17
|
// THE PAGE KNOWS NOTHING (deploy.js's rule, kept). Every version here is the server's reading
|
|
17
18
|
// with the time it was taken. "Up to date" is drawn only from an ok reading with nothing
|
|
@@ -125,11 +126,14 @@
|
|
|
125
126
|
if (!sameOrigin && !/^https?:\/\/[^\s"'<>]+$/i.test(url)) return '';
|
|
126
127
|
let where = '';
|
|
127
128
|
if (!sameOrigin) {
|
|
128
|
-
try { where =
|
|
129
|
+
try { where = new URL(url).host; } catch (e) { return ''; }
|
|
129
130
|
}
|
|
131
|
+
// IN PLACE (task 1005041): the button runs the Update pill's flow (update-pill.js) and the
|
|
132
|
+
// page stays put; the pill in the top bar shows the progress. The href stays the door's,
|
|
133
|
+
// so a page whose pill script did not load still reaches the update the old way.
|
|
130
134
|
return `<div class="row-end su-door">
|
|
131
|
-
<a class="pbtn pbtn--save" id="su-update" href="${escapeHtml(url)}">Update to ${escapeHtml(offered)}
|
|
132
|
-
<span class="panel-hint">
|
|
135
|
+
<a class="pbtn pbtn--save" id="su-update" href="${escapeHtml(url)}">Update to ${escapeHtml(offered)}</a>
|
|
136
|
+
<span class="panel-hint">Takes a few minutes. The project restarts once, checks itself, and goes back to the version it runs now if it does not come back healthy.${where ? ` A small window from ${escapeHtml(where)} opens for a moment to start it.` : ''}</span>
|
|
133
137
|
</div>${door.kind === 'hub' && ask.available ? askHtml(ask, offered, { lead: false }) : ''}`;
|
|
134
138
|
}
|
|
135
139
|
|
|
@@ -236,6 +240,16 @@
|
|
|
236
240
|
const askBtn = byId('su-ask');
|
|
237
241
|
if (askBtn) askBtn.addEventListener('click', () => askForUpgrade(data, askBtn));
|
|
238
242
|
|
|
243
|
+
// The same press as the pill's Update now; only when it took the update in place is the
|
|
244
|
+
// link's own navigation stopped.
|
|
245
|
+
const updateBtn = byId('su-update');
|
|
246
|
+
if (updateBtn) {
|
|
247
|
+
updateBtn.addEventListener('click', (e) => {
|
|
248
|
+
const pill = window.OTBUpdatePill;
|
|
249
|
+
if (pill && pill.updateNow(data, { api })) e.preventDefault();
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
239
253
|
const more = byId('su-more');
|
|
240
254
|
if (more) {
|
|
241
255
|
more.addEventListener('click', () => {
|