@bongos/core 1.24.81 → 1.24.82
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 +85 -70
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +4 -0
- package/clients/bongos-client/index.cjs +4 -0
- package/clients/bongos-client/index.d.ts +5 -0
- package/clients/bongos-client/index.mjs +4 -0
- package/docs/api/openapi.json +86 -2
- package/docs/api-reference.md +4 -2
- package/docs/copy-inventory.md +38 -32
- package/docs/copy-registry.json +107 -52
- package/docs/module-api-changelog.md +2 -0
- package/docs/page-inventory.json +1 -0
- package/docs/page-readings.json +202 -196
- package/modules/builder-settings/hall-view-prefs.js +50 -0
- package/modules/builder-settings/routes/settings.js +32 -0
- package/modules/hall-ui/public/ai-studio.html +3 -3
- package/modules/hall-ui/public/atlas.html +3 -3
- package/modules/hall-ui/public/blockers.html +3 -3
- package/modules/hall-ui/public/board-room.html +3 -3
- package/modules/hall-ui/public/city.html +4 -4
- package/modules/hall-ui/public/collab.html +3 -3
- package/modules/hall-ui/public/commands.html +3 -3
- package/modules/hall-ui/public/copy-desk.html +3 -3
- package/modules/hall-ui/public/deploy.html +3 -3
- package/modules/hall-ui/public/goal-map.html +4 -4
- package/modules/hall-ui/public/goals.html +3 -3
- package/modules/hall-ui/public/government.html +3 -3
- package/modules/hall-ui/public/idea.html +3 -3
- package/modules/hall-ui/public/ideas.html +3 -3
- package/modules/hall-ui/public/index.html +5 -5
- package/modules/hall-ui/public/market-watch-item.html +3 -3
- package/modules/hall-ui/public/market-watch-rules.html +3 -3
- package/modules/hall-ui/public/market-watch.html +3 -3
- package/modules/hall-ui/public/modules.html +3 -3
- package/modules/hall-ui/public/money.html +3 -3
- package/modules/hall-ui/public/my-agents.html +3 -3
- package/modules/hall-ui/public/options-menu.js +2 -1
- package/modules/hall-ui/public/primer.html +3 -3
- package/modules/hall-ui/public/profile.html +3 -3
- package/modules/hall-ui/public/project-settings.html +3 -3
- package/modules/hall-ui/public/roster.html +3 -3
- package/modules/hall-ui/public/settings-view.js +75 -0
- package/modules/hall-ui/public/settings.html +17 -4
- package/modules/hall-ui/public/shell-game.css +10 -0
- package/modules/hall-ui/public/shell-game.js +98 -33
- package/modules/hall-ui/public/shell.js +4 -3
- package/modules/hall-ui/public/street.html +3 -3
- package/modules/hall-ui/public/studio.html +3 -3
- package/modules/hall-ui/public/style.css +2 -0
- package/modules/hall-ui/public/task.html +3 -3
- package/modules/hall-ui/public/thinking.html +4 -4
- package/modules/hall-ui/public/tweak-editor.html +3 -3
- package/modules/hall-ui/public/visor.js +7 -1
- package/modules/hall-ui/public/watch.html +3 -3
- package/modules/hall-ui/public/work.html +3 -3
- package/modules/hall-ui/records/game-mode.md +5 -4
- package/modules/hall-ui/records/start-screen.md +4 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/scripts/hall-preview/pref-writes.js +8 -1
- package/src/module-api.js +1 -1
- package/tests/hall_fullscreen.mjs +42 -21
- package/tests/hall_start_screen.mjs +38 -3
- package/tests/hall_view_prefs.mjs +42 -0
- package/tests/hall_visor.mjs +3 -2
- package/tests/nav_permission_atoms.mjs +1 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<link rel="icon" href="/modules/ui-design/public/brand/favicon.svg" type="image/svg+xml">
|
|
11
11
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
12
12
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
13
|
-
<link rel="stylesheet" href="/builders/style.css?v=2026-11-11-
|
|
13
|
+
<link rel="stylesheet" href="/builders/style.css?v=2026-11-11-view2">
|
|
14
14
|
<link rel="stylesheet" href="/builders/tweak-editor.css?v=2026-09-29-tw27-aqfix">
|
|
15
15
|
</head>
|
|
16
16
|
<!-- THE TWEAK EDITOR (task 1004321 / BV2.TW10, goal 1000095; ADR 0341 D4 and
|
|
@@ -143,10 +143,10 @@
|
|
|
143
143
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
144
144
|
|
|
145
145
|
<script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
|
|
146
|
-
<script src="/builders/shell-game.js?v=2026-11-11-
|
|
146
|
+
<script src="/builders/shell-game.js?v=2026-11-11-view2"></script>
|
|
147
147
|
<script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
|
|
148
148
|
<script src="/builders/craft-mode.js?v=2026-11-10-visor"></script>
|
|
149
|
-
<script src="/builders/shell.js?v=2026-11-11-
|
|
149
|
+
<script src="/builders/shell.js?v=2026-11-11-view2"></script>
|
|
150
150
|
<script src="/builders/hall-dialog.js"></script>
|
|
151
151
|
<script src="/builders/palette.js" defer></script>
|
|
152
152
|
<script src="/builders/brand-holes.js?v=2026-09-29-aqfix"></script>
|
|
@@ -69,7 +69,13 @@
|
|
|
69
69
|
var mode = typeof C.current === 'function' ? C.current() : null;
|
|
70
70
|
return ACTIONS[mode] ? mode : null;
|
|
71
71
|
}
|
|
72
|
-
|
|
72
|
+
// Game mode reaches only the game's places (task 1005200): an action that opens a classic
|
|
73
|
+
// page is off the glass until that page has a game version. A tool is always this page's.
|
|
74
|
+
function inGame(a) {
|
|
75
|
+
var G = window.OTBShellGame;
|
|
76
|
+
return !a.href || !G || typeof G.isPlace !== 'function' || G.isPlace(a.href);
|
|
77
|
+
}
|
|
78
|
+
function actionsFor(id) { return ACTIONS[id] ? ACTIONS[id].filter(inGame) : []; }
|
|
73
79
|
function credits() {
|
|
74
80
|
var C = craft();
|
|
75
81
|
var me = C && typeof C.me === 'function' ? C.me() : null;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<link rel="icon" href="/modules/ui-design/public/brand/favicon.svg" type="image/svg+xml">
|
|
12
12
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
13
13
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
14
|
-
<link rel="stylesheet" href="/builders/style.css?v=2026-11-11-
|
|
14
|
+
<link rel="stylesheet" href="/builders/style.css?v=2026-11-11-view2">
|
|
15
15
|
<!-- The oversight family's sheet (task 1003469): the figure band, the group
|
|
16
16
|
head, the row, the form. Then the page's own: severities, the report
|
|
17
17
|
reader, the grade trend. The attention tiles + tabs come from style.css's
|
|
@@ -269,10 +269,10 @@
|
|
|
269
269
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
270
270
|
chrome exists before the page script runs. -->
|
|
271
271
|
<script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
|
|
272
|
-
<script src="/builders/shell-game.js?v=2026-11-11-
|
|
272
|
+
<script src="/builders/shell-game.js?v=2026-11-11-view2"></script>
|
|
273
273
|
<script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
|
|
274
274
|
<script src="/builders/craft-mode.js?v=2026-11-10-visor"></script>
|
|
275
|
-
<script src="/builders/shell.js?v=2026-11-11-
|
|
275
|
+
<script src="/builders/shell.js?v=2026-11-11-view2"></script>
|
|
276
276
|
<script src="/builders/hall-dialog.js"></script>
|
|
277
277
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
278
278
|
<script src="/builders/palette.js" defer></script>
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="icon" href="/modules/ui-design/public/brand/favicon.svg" type="image/svg+xml">
|
|
9
9
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
10
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
|
-
<link rel="stylesheet" href="/builders/style.css?v=2026-11-11-
|
|
11
|
+
<link rel="stylesheet" href="/builders/style.css?v=2026-11-11-view2">
|
|
12
12
|
<!-- Page-local styles (task 2170) — extracted from the old inline <style>
|
|
13
13
|
block per the hall placement rules; shared pager/count-pill styles now
|
|
14
14
|
live in style.css's hall-kit section. -->
|
|
@@ -172,10 +172,10 @@
|
|
|
172
172
|
<!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
|
|
173
173
|
time so the chrome exists before the deferred page scripts execute. -->
|
|
174
174
|
<script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
|
|
175
|
-
<script src="/builders/shell-game.js?v=2026-11-11-
|
|
175
|
+
<script src="/builders/shell-game.js?v=2026-11-11-view2"></script>
|
|
176
176
|
<script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
|
|
177
177
|
<script src="/builders/craft-mode.js?v=2026-11-10-visor"></script>
|
|
178
|
-
<script src="/builders/shell.js?v=2026-11-11-
|
|
178
|
+
<script src="/builders/shell.js?v=2026-11-11-view2"></script>
|
|
179
179
|
<script src="/builders/hall-dialog.js"></script>
|
|
180
180
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
181
181
|
<script src="/builders/palette.js" defer></script>
|
|
@@ -7,14 +7,15 @@ Covers: every shelled hall page (`shell-game.js`, `shell.js`). Spec: `docs/specs
|
|
|
7
7
|
- **Game mode is the existing full-screen switch, made hall-wide. There is no second full-screen mode.** The switch was already on every page (task 1004430) but remembered per page, so a reader had to turn it on 33 times. Game mode is one `localStorage` key, `hall-game`; while it is `on`, `shell.js` `storedFullscreen()` answers yes for every page, whatever that page remembers. What hides, the corner button and the Escape hand-off are the shell's, unchanged.
|
|
8
8
|
- **`shell-game.js` holds the mode and nothing else**: `window.OTBShellGame` = `on()`, `set(bool)`, `hint()`. It stamps `html[data-game="on"]` for a sheet or script that wants to read the mode. `shell.js` reads it through a fallback object, so a page without the file is never in game mode. It is its own file because `shell.js` sits on the 1,500-line ratchet; the start screen and options menu (R03, R04) are expected to grow here, not in `shell.js`.
|
|
9
9
|
- **Load order**: `shell-game.js` before `shell-trail.js` / `craft-mode.js` / `shell.js`, loaded the same way (deferred or not) as the shell on that page. Test-pinned.
|
|
10
|
-
- **
|
|
10
|
+
- **Game or classic, never both (task 1005200, owner 2026-11-11).** Inside game mode there is no way back to the bars: the corner button is hidden (`style.css`) and does nothing if pressed, Escape opens the options menu, and the "how to leave" line is never shown. The only switch is Options → Preferences → View (`settings-view.js`), plus the chooser at every sign-in (`records/start-screen.md`). A page's own `hall-fullscreen:<page>` memory is still never overwritten, so switching to classic hands each page back what it remembered.
|
|
11
|
+
- **Game mode reaches only the game's places** (`shell-game.js` `PLACES`: home, city, goal-map, thinking, atlas, settings, profile). Any other hall page `location.replace`s to the city on load, and so does switching to game on one. A screen embedded in the options menu is exempt. The visor drops an action whose `href` is not a place (`visor.js` `inGame`). A page joins `PLACES` when it has a game version.
|
|
12
|
+
- **The choice is kept on the account**: `GET/PATCH /api/bongos/me/hall-view` (`modules/builder-settings/hall-view-prefs.js`, key `hall_view` in `behavior_prefs`, null = never chosen). `hall-game` in localStorage is only the copy a page paints from; `sync()` reads the account on every page load and switches if another browser changed it.
|
|
11
13
|
- **No page may opt out.** `<body data-fullscreen-switch="off">` would keep that page's sidebar in game mode; the test fails on one. A new hall page needs `#app-topbar` and the `shell-game.js` tag, like the rest.
|
|
12
|
-
-
|
|
13
|
-
- **How it is turned on today**: `?game=on` / `?game=off` on any hall URL, or `OTBShellGame.set()`. Nothing in the hall's interface sets it yet; the start screen (R03) is where that lands.
|
|
14
|
+
- **`?game=on` / `?game=off` are gone** (task 1005200): the address no longer switches the view.
|
|
14
15
|
|
|
15
16
|
## For the tasks after this one
|
|
16
17
|
|
|
17
18
|
- A HUD or options menu that wants Escape must call `preventDefault()` on the press. `shell.js` `onFullscreenKey` waits one tick before leaving so a listener added after it still wins.
|
|
18
|
-
- Since R04 (task 1005144, ADR 0401) the last Escape in game mode opens the options menu (`shell-game.js` `onEscape`), and on the menu it closes it again.
|
|
19
|
+
- Since R04 (task 1005144, ADR 0401) the last Escape in game mode opens the options menu (`shell-game.js` `onEscape`), and on the menu it closes it again. Since task 1005200 there is no way back to the bars inside game mode.
|
|
19
20
|
|
|
20
21
|
Tests: `tests/hall_fullscreen.mjs` (the eight `game mode:` cases run the real `shell-game.js` + `shell.js`), and the script lists in `tests/hall_city.mjs`, `tests/hall_street.mjs`, `tests/hall_my_agents.mjs`, `tests/goal_map_page.mjs`.
|
|
@@ -23,3 +23,7 @@ Covers: Home (`shell-game.js` `start()`, `shell-game.css`, the hand-off in `welc
|
|
|
23
23
|
- New art for the start screen (the city in the clouds) is task 1005196; `welcome-boot.js` still hands in the welcome's painting.
|
|
24
24
|
|
|
25
25
|
Tests: `tests/hall_start_screen.mjs` (the hand-off and pure parts in a vm; both screens, the panel's place at desk and phone widths, the segments, Quit, the no-city and reduced-motion paths in a real browser).
|
|
26
|
+
|
|
27
|
+
## The view chooser (task 1005200, owner 2026-11-11)
|
|
28
|
+
|
|
29
|
+
Every sign-in now opens on a chooser before the start screen: **Game** or **Classic**, each with one line saying what it is. The view chosen last time (the browser's `hall-game` copy) is marked "Last time", lit with the accent on its edge, and focused, so Enter keeps it. Game goes on to the start screen as before; Classic closes the screen and the hall is as it always was. Either choice is saved to the account (`PATCH /api/bongos/me/hall-view`). The Extras list lost The docs: it is not a game place. Tests: `tests/hall_start_screen.mjs`.
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.82",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.24.
|
|
9
|
+
"version": "1.24.82",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.82",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -11589,5 +11589,11 @@
|
|
|
11589
11589
|
"id": "1005192",
|
|
11590
11590
|
"text": "The hall now wears the new Helm look: deep night panels, ice-blue text, wide capital titles and cut-corner buttons, all from one shared design system. The start and load screens are rebuilt to match, and the hall is dark only."
|
|
11591
11591
|
}
|
|
11592
|
+
],
|
|
11593
|
+
"1.24.82": [
|
|
11594
|
+
{
|
|
11595
|
+
"id": "1005200",
|
|
11596
|
+
"text": "The hall now has two separate views, Game and Classic, and never mixes them. You choose one each time you sign in and can switch only in Options. Game mode stays full screen and only opens the game's places."
|
|
11597
|
+
}
|
|
11592
11598
|
]
|
|
11593
11599
|
}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
//
|
|
15
15
|
// What it is NOT: storage. Nothing is written to disk and a restart forgets
|
|
16
16
|
// everything; each recorded write is logged so the harness console shows what the
|
|
17
|
-
// page sent. Only these
|
|
17
|
+
// page sent. Only these routes are recorded — every other write is refused
|
|
18
18
|
// exactly as before.
|
|
19
19
|
'use strict';
|
|
20
20
|
|
|
@@ -27,6 +27,7 @@ const wandering = lib('wandering-prefs');
|
|
|
27
27
|
const models = lib('model-alloc-prefs');
|
|
28
28
|
const render = lib('render-prefs');
|
|
29
29
|
const skills = lib('skill-prefs');
|
|
30
|
+
const hallView = lib('hall-view-prefs'); // task 1005200: game or classic
|
|
30
31
|
|
|
31
32
|
// apiPath → the /me slice it answers for (null: the skill prefs, not on /me)
|
|
32
33
|
const ROUTES = {
|
|
@@ -35,6 +36,7 @@ const ROUTES = {
|
|
|
35
36
|
'me/model-allocation': 'model_allocation',
|
|
36
37
|
'me/render': 'render',
|
|
37
38
|
'me/skill-prefs': null,
|
|
39
|
+
'me/hall-view': 'hall_view',
|
|
38
40
|
};
|
|
39
41
|
|
|
40
42
|
function createPrefRecorder({ log = console.log } = {}) {
|
|
@@ -63,6 +65,7 @@ function createPrefRecorder({ log = console.log } = {}) {
|
|
|
63
65
|
if (slice === 'wandering') return wandering.describeForApi(behavior, rank);
|
|
64
66
|
if (slice === 'model_allocation') return models.describeForApi(behavior, instanceModels);
|
|
65
67
|
if (slice === 'render') return render.describeForApi(behavior);
|
|
68
|
+
if (slice === 'hall_view') return hallView.describeForApi(behavior);
|
|
66
69
|
return {
|
|
67
70
|
prefs: skillPrefs, effective: skills.listEffective(skillPrefs), defaults: skills.SKILL_DEFAULTS,
|
|
68
71
|
known_skills: skills.KNOWN_SKILLS, allowed_models: skills.ALLOWED_MODELS,
|
|
@@ -96,6 +99,10 @@ function createPrefRecorder({ log = console.log } = {}) {
|
|
|
96
99
|
v = render.validatePatch(b);
|
|
97
100
|
if (!v.ok) return invalid(v.errors);
|
|
98
101
|
behavior = render.applyPatch(behavior, v.patch);
|
|
102
|
+
} else if (slice === 'hall_view') {
|
|
103
|
+
v = hallView.validatePatch(b);
|
|
104
|
+
if (!v.ok) return invalid(v.errors);
|
|
105
|
+
behavior = hallView.applyView(behavior, v.view);
|
|
99
106
|
} else {
|
|
100
107
|
v = skills.validatePatch(b);
|
|
101
108
|
if (!v.ok) return invalid(v.errors);
|
package/src/module-api.js
CHANGED
|
@@ -81,7 +81,7 @@ const { sleep } = require('./sleep');
|
|
|
81
81
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
82
82
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
83
83
|
|
|
84
|
-
const CORE_VERSION = '1.24.
|
|
84
|
+
const CORE_VERSION = '1.24.82'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
85
85
|
|
|
86
86
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
87
87
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
|
@@ -94,7 +94,8 @@ function boot({ page = 'thinking', optIn = true, store = {}, storageThrows = fal
|
|
|
94
94
|
get cookie() { return ''; }, set cookie(_v) {},
|
|
95
95
|
};
|
|
96
96
|
const assigned = []; // task 1005144: where the page sent the browser (game mode's Escape opens the menu)
|
|
97
|
-
const
|
|
97
|
+
const replaced = []; // task 1005200: a classic page in game mode sends the browser to the city
|
|
98
|
+
const location = { hostname: 'h', origin: 'https://h', href: 'https://h/' + page + search, pathname: path || '/' + page, hash, search, assign(u) { assigned.push(u); }, replace(u) { replaced.push(u); } };
|
|
98
99
|
// `session` is the TAB's sessionStorage: pass one object to several boots and
|
|
99
100
|
// they are page loads in one tab (the trail, task 1004476).
|
|
100
101
|
const sessionStorage = {
|
|
@@ -135,6 +136,7 @@ function boot({ page = 'thinking', optIn = true, store = {}, storageThrows = fal
|
|
|
135
136
|
return {
|
|
136
137
|
store,
|
|
137
138
|
assigned,
|
|
139
|
+
replaced,
|
|
138
140
|
game: () => windowObj.OTBShellGame,
|
|
139
141
|
gameStamp: () => root.dataset.game === 'on',
|
|
140
142
|
// The "how to leave" line: showing = the shell created it and it is not hidden.
|
|
@@ -517,17 +519,36 @@ test('game mode: every hall page carries the switch — none opts out, and each
|
|
|
517
519
|
}
|
|
518
520
|
});
|
|
519
521
|
|
|
520
|
-
|
|
522
|
+
// task 1005200 (owner, 2026-11-11): game OR classic, never both. Inside game mode there is
|
|
523
|
+
// no way back to the bars — the corner button is hidden (style.css) and, pressed
|
|
524
|
+
// anyway, does nothing; Escape is the options menu's, where the view is switched.
|
|
525
|
+
test('game mode: there is no way back to the bars inside it, and a page\'s own memory is never rewritten', () => {
|
|
521
526
|
const store = { 'hall-game': 'on', 'hall-fullscreen:thinking': 'on' };
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
assert.equal(
|
|
525
|
-
assert.equal(store['hall-fullscreen:
|
|
526
|
-
assert.equal(boot({ page: 'goals', optIn: false, store }).on(), true, 'the next load opens without bars again');
|
|
527
|
+
const profile = boot({ page: 'profile', optIn: false, store });
|
|
528
|
+
profile.button().click();
|
|
529
|
+
assert.equal(profile.on(), true, 'the corner button does not leave game mode');
|
|
530
|
+
assert.equal(store['hall-fullscreen:profile'], undefined, 'nothing remembered for the page');
|
|
527
531
|
const sky = boot({ page: 'thinking', store });
|
|
528
532
|
sky.button().click();
|
|
529
|
-
|
|
533
|
+
sky.press('Escape');
|
|
534
|
+
assert.equal(sky.on(), true, 'nor does Escape');
|
|
530
535
|
assert.equal(store['hall-fullscreen:thinking'], 'on', 'the sky\'s own choice survives game mode');
|
|
536
|
+
assert.match(read('style.css'), /html\[data-game="on"\] \.fullscreen-toggle \{ display: none; \}/, 'and the button is not drawn');
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
test('game mode: only the game\'s places are there — a classic page sends the browser to the city', () => {
|
|
540
|
+
const store = { 'hall-game': 'on' };
|
|
541
|
+
assert.deepEqual(boot({ page: 'goals', optIn: false, store }).replaced, ['/'], 'Goals is a classic page');
|
|
542
|
+
assert.deepEqual(boot({ page: 'work', optIn: false, store, path: '/builders/work' }).replaced, ['/builders/'], 'on the apex too');
|
|
543
|
+
for (const page of ['home', 'city', 'goal-map', 'thinking', 'atlas', 'settings', 'profile']) {
|
|
544
|
+
assert.deepEqual(boot({ page, optIn: false, store }).replaced, [], `${page} is a game place`);
|
|
545
|
+
}
|
|
546
|
+
assert.deepEqual(boot({ page: 'goals', optIn: false, store: {} }).replaced, [], 'classic reaches every page');
|
|
547
|
+
const G = boot({ page: 'city', store }).game();
|
|
548
|
+
assert.equal(G.isPlace('/city'), true);
|
|
549
|
+
assert.equal(G.isPlace('/builders/'), true);
|
|
550
|
+
assert.equal(G.isPlace('/work'), false);
|
|
551
|
+
assert.equal(G.isPlace('/blockers?x=1'), false);
|
|
531
552
|
});
|
|
532
553
|
|
|
533
554
|
// task 1005144 (BONGOS-V3 R04, ADR 0399 §2.4 row 6): in game mode the last Escape is the
|
|
@@ -562,35 +583,35 @@ test('game mode: the Escape hand-off is unchanged — a press the page used, or
|
|
|
562
583
|
|
|
563
584
|
test('game mode: set() switches the page live, and turning it off hands each page back its own memory', () => {
|
|
564
585
|
const store = { 'hall-fullscreen:thinking': 'on' };
|
|
565
|
-
const goals = boot({ page: '
|
|
586
|
+
const goals = boot({ page: 'profile', optIn: false, store });
|
|
566
587
|
goals.game().set(true);
|
|
567
588
|
assert.equal(store['hall-game'], 'on');
|
|
568
589
|
assert.equal(goals.on(), true, 'on at once, no reload');
|
|
569
590
|
assert.ok(goals.resizes() >= 1, 'a page that sizes to its box is told to re-measure');
|
|
570
591
|
goals.game().set(false);
|
|
571
|
-
assert.equal(goals.on(), false, '
|
|
592
|
+
assert.equal(goals.on(), false, 'the profile remembered nothing, so its bars return');
|
|
572
593
|
assert.equal(goals.gameStamp(), false);
|
|
573
|
-
assert.equal(store['hall-fullscreen:
|
|
594
|
+
assert.equal(store['hall-fullscreen:profile'], undefined, 'and switching the mode wrote nothing for the page');
|
|
595
|
+
const work = boot({ page: 'work', optIn: false, store: {} });
|
|
596
|
+
work.game().set(true);
|
|
597
|
+
assert.deepEqual(work.replaced, ['/'], 'switched to game on a classic page: to the city');
|
|
574
598
|
const sky = boot({ page: 'thinking', store: Object.assign(store, { 'hall-game': 'on' }) });
|
|
575
599
|
sky.game().set(false);
|
|
576
600
|
assert.equal(sky.on(), true, 'the sky had chosen full screen itself, and keeps it');
|
|
577
601
|
});
|
|
578
602
|
|
|
579
|
-
test('game mode:
|
|
603
|
+
test('game mode: the address does not switch it — Options and the sign-in chooser do (task 1005200)', () => {
|
|
580
604
|
const store = {};
|
|
581
|
-
assert.equal(boot({ page: '
|
|
582
|
-
assert.equal(store['hall-game'], '
|
|
583
|
-
assert.
|
|
584
|
-
assert.equal(boot({ page: 'work', optIn: false, store, search: '?game=off' }).on(), false);
|
|
585
|
-
boot({ page: 'work', optIn: false, store, search: '?game=%22%3E%3Cimg%3E' });
|
|
586
|
-
assert.equal(store['hall-game'], 'off', 'a value that is not on/off is not stored');
|
|
605
|
+
assert.equal(boot({ page: 'profile', optIn: false, store, search: '?game=on' }).on(), false);
|
|
606
|
+
assert.equal(store['hall-game'], undefined, 'nothing stored from the URL');
|
|
607
|
+
assert.doesNotMatch(read('shell-game.js'), /get\('game'\)/, 'the ?game= reader is gone');
|
|
587
608
|
});
|
|
588
609
|
|
|
589
|
-
test('game mode
|
|
610
|
+
test('game mode never says how to leave full screen (there is no leaving); outside it the shell\'s rule stands', () => {
|
|
590
611
|
const store = { 'hall-game': 'on' };
|
|
591
612
|
const session = {};
|
|
592
|
-
assert.equal(boot({ page: '
|
|
593
|
-
assert.equal(boot({ page: '
|
|
613
|
+
assert.equal(boot({ page: 'profile', optIn: false, store, session }).hintShowing(), false, 'no line in game mode');
|
|
614
|
+
assert.equal(boot({ page: 'city', store, session }).hintShowing(), false);
|
|
594
615
|
const own = { 'hall-fullscreen:thinking': 'on' };
|
|
595
616
|
assert.equal(boot({ page: 'thinking', store: own, session: {} }).hintShowing(), true);
|
|
596
617
|
assert.equal(boot({ page: 'thinking', store: own, session: {} }).hintShowing(), true, 'a remembered page says it on every open, as before');
|
|
@@ -188,12 +188,21 @@ test('a sign-in passes through the start screen and the load screen before the w
|
|
|
188
188
|
const { page, errors } = await signedIn(ctx);
|
|
189
189
|
await page.waitForSelector('#game-start');
|
|
190
190
|
assert.deepEqual(await state(page), { start: true, welcome: false, picker: false, game: null, full: null },
|
|
191
|
-
'the start screen, not the welcome and not the picker; game mode is not on until
|
|
191
|
+
'the start screen, not the welcome and not the picker; game mode is not on until it is chosen');
|
|
192
|
+
// task 1005200: first the chooser — game or classic — then, for game, the start screen.
|
|
193
|
+
const views = await page.evaluate(() => ({
|
|
194
|
+
items: Array.from(document.querySelectorAll('.gs__items:not([hidden]) .gs__item')).map((e) => e.textContent),
|
|
195
|
+
focus: document.activeElement.textContent,
|
|
196
|
+
}));
|
|
197
|
+
assert.deepEqual(views, { items: ['GameThe city, full screen', 'ClassicThe hall with its sidebar'], focus: 'GameThe city, full screen' });
|
|
198
|
+
await page.keyboard.press('Enter');
|
|
199
|
+
await page.waitForSelector('#gs-items:not([hidden])');
|
|
200
|
+
assert.equal((await state(page)).game, 'on', 'choosing Game turns game mode on');
|
|
192
201
|
const menu = await page.evaluate(() => ({
|
|
193
202
|
title: document.getElementById('gs-title').textContent,
|
|
194
|
-
items: Array.from(document.querySelectorAll('.gs__items:not(.gs__items--extras) .gs__item')).map((e) => e.textContent),
|
|
203
|
+
items: Array.from(document.querySelectorAll('.gs__items:not(.gs__items--extras):not([hidden]) .gs__item')).map((e) => e.textContent),
|
|
195
204
|
focus: document.activeElement.textContent,
|
|
196
|
-
small: Array.from(document.querySelectorAll('.gs__items:not(.gs__items--extras) .gs__item')).filter((e) => e.getBoundingClientRect().height < 46).length,
|
|
205
|
+
small: Array.from(document.querySelectorAll('.gs__items:not(.gs__items--extras):not([hidden]) .gs__item')).filter((e) => e.getBoundingClientRect().height < 46).length,
|
|
197
206
|
}));
|
|
198
207
|
assert.deepEqual(menu, { title: 'Harbor', items: ['Start', 'Options', 'Achievements', 'Extras', 'Quit'], focus: 'Start', small: 0 });
|
|
199
208
|
|
|
@@ -253,6 +262,29 @@ test('a sign-in passes through the start screen and the load screen before the w
|
|
|
253
262
|
await ctx.close();
|
|
254
263
|
});
|
|
255
264
|
|
|
265
|
+
// task 1005200: Classic at the chooser — the screen goes, game mode is off, and the account
|
|
266
|
+
// is told. The view chosen last time is the one marked and focused.
|
|
267
|
+
test('choosing Classic at sign-in closes the screen, leaves game mode off, and saves it to the account', { skip }, async () => {
|
|
268
|
+
const ctx = await hall();
|
|
269
|
+
const saved = [];
|
|
270
|
+
await ctx.route(ORIGIN + '/api/bongos/me/hall-view', (route) => {
|
|
271
|
+
if (route.request().method() === 'PATCH') saved.push(JSON.parse(route.request().postData()));
|
|
272
|
+
return route.fulfill({ contentType: 'application/json', body: JSON.stringify({ view: null }) });
|
|
273
|
+
});
|
|
274
|
+
const page = await ctx.newPage();
|
|
275
|
+
await page.addInitScript(() => { try { localStorage.setItem('hall-welcome-signin', '1'); localStorage.setItem('hall-game', 'off'); } catch (_) { /* no storage */ } });
|
|
276
|
+
await page.goto(ORIGIN + '/');
|
|
277
|
+
await page.waitForFunction(() => !!window.OTBShell && !!window.OTBWelcome && !!window.OTBShellGame);
|
|
278
|
+
await page.evaluate(() => { const me = { authed: true, rank: 'metic', builder: { id: 7, display_name: 'Ada', github_login: 'ada' } }; window.OTBShell.applyAccess(me); window.OTBShell.applyAccess(me); });
|
|
279
|
+
await page.waitForSelector('#game-start');
|
|
280
|
+
assert.equal(await page.evaluate(() => document.activeElement.textContent), 'ClassicLast timeThe hall with its sidebar', 'the view chosen last time is focused and marked');
|
|
281
|
+
await page.keyboard.press('Enter');
|
|
282
|
+
await page.waitForSelector('#game-start', { state: 'detached' });
|
|
283
|
+
assert.equal((await state(page)).game, null);
|
|
284
|
+
assert.deepEqual(saved, [{ view: 'classic' }]);
|
|
285
|
+
await ctx.close();
|
|
286
|
+
});
|
|
287
|
+
|
|
256
288
|
test('on a phone the panel is the whole width, nothing scrolls sideways and every choice clears the floor (task 1005192)', { skip }, async () => {
|
|
257
289
|
for (const [w, h] of [[390, 844], [320, 700]]) {
|
|
258
290
|
const ctx = await hall({ viewport: { width: w, height: h } });
|
|
@@ -266,6 +298,7 @@ test('on a phone the panel is the whole width, nothing scrolls sideways and ever
|
|
|
266
298
|
};
|
|
267
299
|
});
|
|
268
300
|
assert.deepEqual(m, { x: 0, w, sideways: 0, small: 0 }, `at ${w}px`);
|
|
301
|
+
await page.click('[data-gs="view-game"]');
|
|
269
302
|
await page.click('[data-gs="start"]');
|
|
270
303
|
await page.waitForSelector('#gs-load:not([hidden])');
|
|
271
304
|
assert.equal(await page.evaluate(() => { const gs = document.getElementById('game-start'); return gs.scrollWidth - gs.clientWidth; }), 0, `the load screen fits ${w}px`);
|
|
@@ -279,6 +312,7 @@ test('Quit closes the tab, and says so when the browser keeps it open', { skip }
|
|
|
279
312
|
const { page } = await signedIn(ctx);
|
|
280
313
|
await page.waitForSelector('#game-start');
|
|
281
314
|
await page.evaluate(() => { window.__closed = 0; window.close = () => { window.__closed += 1; }; });
|
|
315
|
+
await page.click('[data-gs="view-game"]');
|
|
282
316
|
await page.click('[data-gs="quit"]');
|
|
283
317
|
await page.waitForSelector('#gs-note:not([hidden])');
|
|
284
318
|
assert.equal(await page.evaluate(() => window.__closed), 1);
|
|
@@ -291,6 +325,7 @@ test('with no city to draw, or with reduced motion, the still and the tip stand
|
|
|
291
325
|
const ctx = await hall(opts);
|
|
292
326
|
const { page, errors } = await signedIn(ctx);
|
|
293
327
|
await page.waitForSelector('#game-start');
|
|
328
|
+
await page.click('[data-gs="view-game"]');
|
|
294
329
|
await page.click('[data-gs="start"]');
|
|
295
330
|
await page.waitForSelector('#gs-load:not([hidden])');
|
|
296
331
|
assert.match(await page.textContent('#gs-tip'), /\.$/);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// tests/hall_view_prefs.mjs — the game-or-classic choice kept on the account
|
|
2
|
+
// (task 1005200, modules/builder-settings/hall-view-prefs.js), and the two routes
|
|
3
|
+
// that read and write it.
|
|
4
|
+
import { test } from 'node:test';
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { createRequire } from 'node:module';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
|
|
11
|
+
const require = createRequire(import.meta.url);
|
|
12
|
+
const ROOT = fileURLToPath(new URL('..', import.meta.url));
|
|
13
|
+
const prefs = require('../modules/builder-settings/hall-view-prefs.js');
|
|
14
|
+
|
|
15
|
+
test('never chosen is null, not a default view: the sign-in chooser asks', () => {
|
|
16
|
+
assert.equal(prefs.storedView({}), null);
|
|
17
|
+
assert.equal(prefs.storedView(null), null);
|
|
18
|
+
assert.equal(prefs.storedView({ hall_view: 'fullscreen' }), null, 'an unknown value is not a choice');
|
|
19
|
+
assert.deepEqual(prefs.describeForApi({ hall_view: 'game' }), { view: 'game', views: ['game', 'classic'] });
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('a PATCH takes exactly { view: game | classic }', () => {
|
|
23
|
+
assert.deepEqual(prefs.validatePatch({ view: 'classic' }), { ok: true, view: 'classic' });
|
|
24
|
+
assert.equal(prefs.validatePatch({}).ok, false, 'no view');
|
|
25
|
+
assert.equal(prefs.validatePatch({ view: 'both' }).ok, false);
|
|
26
|
+
assert.equal(prefs.validatePatch({ view: 'game', extra: 1 }).ok, false, 'unknown keys refused');
|
|
27
|
+
assert.equal(prefs.validatePatch([]).ok, false);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('saving the view keeps every other behaviour preference', () => {
|
|
31
|
+
const before = { wandering: 2, widgets: 'rich', interaction: { verbosity: 'concise' } };
|
|
32
|
+
const after = prefs.applyView(before, 'game');
|
|
33
|
+
assert.deepEqual(after, { ...before, hall_view: 'game' });
|
|
34
|
+
assert.equal(before.hall_view, undefined, 'pure: the input is not changed');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('the routes: own builder only, GET and PATCH on /me/hall-view', () => {
|
|
38
|
+
const src = fs.readFileSync(path.join(ROOT, 'modules/builder-settings/routes/settings.js'), 'utf8');
|
|
39
|
+
assert.match(src, /router\.get\('\/me\/hall-view', api\.requireBuilder,/);
|
|
40
|
+
assert.match(src, /router\.patch\('\/me\/hall-view', api\.requireBuilder,/);
|
|
41
|
+
assert.match(src, /hallViewPrefs\.applyView\(existing, v\.view\)/, 'writes through the pure shaper');
|
|
42
|
+
});
|
package/tests/hall_visor.mjs
CHANGED
|
@@ -151,7 +151,7 @@ test('in game mode the glass shows the place, the main action, the sprite and th
|
|
|
151
151
|
}));
|
|
152
152
|
assert.equal(g.shown, 'block');
|
|
153
153
|
assert.equal(g.place, 'Test city');
|
|
154
|
-
assert.match(g.main, /^Claim a task
|
|
154
|
+
assert.match(g.main, /^Goal sky/, 'Claim a task opens a classic page, so it is off the glass in game mode (task 1005200)');
|
|
155
155
|
assert.equal(g.who, 'Artisan');
|
|
156
156
|
assert.equal(g.credits, '56,021 credits');
|
|
157
157
|
assert.equal(g.sprite, '/builders/characters/artisan.png');
|
|
@@ -189,7 +189,8 @@ test('the sheet lists the character\'s actions, then this place, and runs a scen
|
|
|
189
189
|
const { ctx, page } = await open();
|
|
190
190
|
await page.click('[data-visor-sheet]');
|
|
191
191
|
const rows = await page.$$eval('.visor__sheet .visor__row', (r) => r.map((x) => [...x.children].map((c) => c.textContent).join(' ')));
|
|
192
|
-
|
|
192
|
+
// task 1005200: Claim a task and Blockers open classic pages, so they are off the glass.
|
|
193
|
+
assert.deepEqual(rows, ['Goal sky G', 'Your thinking T', 'Back to centre 0', 'Look around drag', 'Options Esc']);
|
|
193
194
|
await page.click('[data-visor-scene="0"]');
|
|
194
195
|
assert.deepEqual(await page.evaluate(() => ({ centred: window.centred, open: OTBVisor.isOpen() })), { centred: true, open: false });
|
|
195
196
|
await ctx.close();
|
|
@@ -103,7 +103,7 @@ test('the hall-preview fixture carries every atom /me resolves', () => {
|
|
|
103
103
|
// task 1005070: the remembered nav (the rail paints before /me).
|
|
104
104
|
// task 1005088: the sidebar mark is drawn by reference to the one brand mark.
|
|
105
105
|
// task 1005144: My agents, Project settings, Permissions and Modules left for the options menu.
|
|
106
|
-
const SHELL_SHA = '
|
|
106
|
+
const SHELL_SHA = '89f6f166ed22a8ee';
|
|
107
107
|
|
|
108
108
|
test('shell.js has not changed without its cache-bust being bumped', () => {
|
|
109
109
|
const actual = crypto.createHash('sha256').update(SHELL).digest('hex').slice(0, 16);
|