@bongos/core 1.24.78 → 1.24.80
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 +498 -353
- package/.claude/skills/ask-for-help/SKILL.md +1 -1
- package/.claude/skills/builder-sequence/SKILL.md +1 -1
- package/.claude/skills/design/SKILL.md +1 -1
- package/.claude/skills/goal-create/SKILL.md +1 -1
- package/.claude/skills/planning-session/SKILL.md +1 -1
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +8 -0
- package/clients/bongos-client/index.cjs +8 -0
- package/clients/bongos-client/index.d.ts +11 -0
- package/clients/bongos-client/index.mjs +8 -0
- package/docs/adr/0274-one-kernel-three-role-packs.md +2 -2
- package/docs/adr/0399-the-visor-is-glass-over-the-scene-and-owns-no-camera.md +91 -0
- package/docs/adr/0400-a-chain-reserves-work-and-each-sub-quest-is-still-claimed-alone.md +109 -0
- package/docs/adr/0401-one-options-menu-opens-every-settings-screen-in-a-frame.md +53 -0
- package/docs/adr/README.md +3 -0
- package/docs/api/openapi.json +195 -3
- package/docs/api-reference.md +8 -4
- package/docs/architecture.md +7 -3
- package/docs/copy-inventory.md +390 -370
- package/docs/copy-registry.json +719 -518
- package/docs/design/characters/character-sprites-source.webp +0 -0
- package/docs/file-map.md +7 -8
- package/docs/module-api-changelog.md +4 -0
- package/docs/modules-contract.md +5 -0
- package/docs/onboarding/slash-commands.md +1 -1
- package/docs/packs/{engineer.md → artisan.md} +5 -5
- package/docs/packs/artist.md +50 -4
- package/docs/page-inventory.json +15 -4
- package/docs/page-readings.json +3182 -3336
- package/docs/{packs/ideator.md → recipes/ideation-playbook.md} +5 -48
- package/migrations/core_296_crafts_artisan_artist.sql +72 -0
- package/migrations/core_297_builder_character.sql +30 -0
- package/modules/copy-desk/proposals.js +1 -1
- package/modules/economy/credits-by-craft.js +29 -15
- package/modules/hall-ui/public/ai-studio.html +4 -4
- package/modules/hall-ui/public/atlas.html +5 -5
- package/modules/hall-ui/public/atlas.js +3 -1
- package/modules/hall-ui/public/blockers.html +5 -5
- package/modules/hall-ui/public/board-room.html +4 -4
- package/modules/hall-ui/public/characters/artisan.png +0 -0
- package/modules/hall-ui/public/characters/artist.png +0 -0
- package/modules/hall-ui/public/characters/patron.png +0 -0
- package/modules/hall-ui/public/city.css +2 -1
- package/modules/hall-ui/public/city.html +9 -6
- package/modules/hall-ui/public/city.js +7 -1
- package/modules/hall-ui/public/collab.html +4 -4
- package/modules/hall-ui/public/commands.html +4 -4
- package/modules/hall-ui/public/copy-desk.html +4 -4
- package/modules/hall-ui/public/craft-mode.js +258 -64
- package/modules/hall-ui/public/deploy.html +4 -4
- package/modules/hall-ui/public/diagram-viewer.js +2 -1
- package/modules/hall-ui/public/dom-utils.js +1 -1
- package/modules/hall-ui/public/goal-map.html +9 -6
- package/modules/hall-ui/public/goals.html +5 -5
- package/modules/hall-ui/public/government.html +4 -4
- package/modules/hall-ui/public/hall-render.js +3 -1
- package/modules/hall-ui/public/home-city-draw.js +3 -3
- package/modules/hall-ui/public/home-city.css +4 -0
- package/modules/hall-ui/public/home-city.js +17 -9
- package/modules/hall-ui/public/home-gallery.js +8 -1
- package/modules/hall-ui/public/home-idea-box.css +2 -2
- package/modules/hall-ui/public/home-idea-box.js +3 -3
- package/modules/hall-ui/public/idea.html +5 -5
- package/modules/hall-ui/public/ideas.html +5 -5
- package/modules/hall-ui/public/index.html +11 -7
- package/modules/hall-ui/public/market-watch-item.html +4 -4
- package/modules/hall-ui/public/market-watch-rules.html +4 -4
- package/modules/hall-ui/public/market-watch.html +6 -9
- package/modules/hall-ui/public/market-watch.js +22 -45
- package/modules/hall-ui/public/modules.html +4 -4
- package/modules/hall-ui/public/money.html +4 -4
- package/modules/hall-ui/public/my-agents.html +4 -4
- package/modules/hall-ui/public/options-menu.js +409 -0
- package/modules/hall-ui/public/paintbrush.js +3 -3
- package/modules/hall-ui/public/palette.js +11 -13
- package/modules/hall-ui/public/primer.html +5 -5
- package/modules/hall-ui/public/profile-crafts.js +1 -1
- package/modules/hall-ui/public/profile-roles.js +23 -19
- package/modules/hall-ui/public/profile.css +1 -1
- package/modules/hall-ui/public/profile.html +19 -18
- package/modules/hall-ui/public/profile.js +19 -18
- package/modules/hall-ui/public/profile.states.json +4 -4
- package/modules/hall-ui/public/project-settings.html +4 -4
- package/modules/hall-ui/public/roster.html +4 -4
- package/modules/hall-ui/public/roster.js +3 -3
- package/modules/hall-ui/public/settings.css +24 -4
- package/modules/hall-ui/public/settings.html +21 -17
- package/modules/hall-ui/public/settings.js +44 -44
- package/modules/hall-ui/public/shell-game.css +65 -0
- package/modules/hall-ui/public/shell-game.js +283 -3
- package/modules/hall-ui/public/shell.js +7 -26
- package/modules/hall-ui/public/sky-interaction.js +7 -0
- package/modules/hall-ui/public/speciality-tools.js +1 -2
- package/modules/hall-ui/public/street-inside.js +3 -4
- package/modules/hall-ui/public/street.html +4 -4
- package/modules/hall-ui/public/studio.html +5 -5
- package/modules/hall-ui/public/style.css +73 -47
- package/modules/hall-ui/public/task-detail.js +5 -4
- package/modules/hall-ui/public/task.html +5 -5
- package/modules/hall-ui/public/thinking.css +10 -0
- package/modules/hall-ui/public/thinking.html +9 -6
- package/modules/hall-ui/public/tweak-editor.html +5 -5
- package/modules/hall-ui/public/tweak-editor.js +5 -1
- package/modules/hall-ui/public/update-pill.js +91 -2
- package/modules/hall-ui/public/visor.css +80 -0
- package/modules/hall-ui/public/visor.js +316 -0
- package/modules/hall-ui/public/watch-repo-invite.js +159 -0
- package/modules/hall-ui/public/watch.html +6 -5
- package/modules/hall-ui/public/watch.js +17 -2
- package/modules/hall-ui/public/welcome-boot.js +15 -6
- package/modules/hall-ui/public/work.html +5 -5
- package/modules/hall-ui/records/builders-roster.md +2 -2
- package/modules/hall-ui/records/craft-mode.md +13 -11
- package/modules/hall-ui/records/game-mode.md +2 -2
- package/modules/hall-ui/records/governor-street.md +1 -1
- package/modules/hall-ui/records/home-city.md +3 -1
- package/modules/hall-ui/records/market-watch-settings.md +2 -0
- package/modules/hall-ui/records/options-menu.md +15 -0
- package/modules/hall-ui/records/profile-roles.md +11 -11
- package/modules/hall-ui/records/sky.md +3 -3
- package/modules/hall-ui/records/start-screen.md +21 -0
- package/modules/hall-ui/records/tweak-editor.md +1 -1
- package/modules/hall-ui/records/update-pill.md +12 -0
- package/modules/hall-ui/records/visor.md +23 -0
- package/modules/hall-ui/records/welcome-tower.md +5 -5
- package/modules/ideas/ideator-stats.js +3 -1
- package/modules/ideas/inbox.js +2 -2
- package/modules/lifecycle/activity.js +1 -1
- package/modules/lifecycle/cascade.js +4 -4
- package/modules/lifecycle/goal-spec-task.js +1 -1
- package/modules/lifecycle/help-requests.js +19 -15
- package/modules/lifecycle/kickoff-checklist.js +3 -3
- package/modules/lifecycle/kickoff-seed.js +1 -1
- package/modules/lifecycle/migrations/lifecycle_021_crafts_artisan_artist.sql +29 -0
- package/modules/lifecycle/mingle.js +4 -4
- package/modules/lifecycle/page-tweak-write.js +1 -1
- package/modules/lifecycle/routes/task-write-routes.js +3 -1
- package/modules/lifecycle/routes/tasks.js +3 -3
- package/modules/lifecycle/ship-card.js +2 -3
- package/modules/lifecycle/task-classifier.js +39 -29
- package/modules/lifecycle/tests/cascade.mjs +7 -7
- package/modules/onboarding/founding-mode.js +3 -0
- package/modules/onboarding/genesis-home.js +21 -1
- package/modules/onboarding/routes/onboarding.js +2 -0
- package/modules/page-notes/notes.js +3 -2
- package/modules/page-notes/split.js +1 -1
- package/modules/platform-identity/craft-rollup.js +35 -7
- package/modules/platform-identity/migrations/platform_identity_036_crafts_artisan_artist.sql +45 -0
- package/modules/platform-identity/platform-identity.js +2 -2
- package/modules/platform-identity/routes/community-leaderboard.js +2 -1
- package/modules/platform-identity/routes/community-search.js +2 -1
- package/modules/platform-identity/routes/sso.js +2 -1
- package/modules/platform-identity/tests/platform-identity.mjs +6 -6
- package/modules/public-landing/public/community.html +3 -5
- package/modules/public-landing/public/index.html +1 -1
- package/modules/public-landing/public/projects.html +1 -1
- package/modules/specialities/migrations/specialities_004_crafts_artisan_artist.sql +30 -0
- package/modules/specialities/routes/specialities.js +2 -2
- package/modules/status-ui/public/status.js +1 -1
- package/modules/ui-design/kit/fixtures/community-leaderboard.json +10 -10
- package/modules/ui-design/kit/fixtures/community-search.json +2 -2
- package/modules/ui-design/kit/fixtures/scouting-recruiter.json +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +76 -0
- package/scripts/bongos/autobongos-host.js +19 -4
- package/scripts/bongos/autobongos-loop.js +1 -1
- package/scripts/bongos/claude-materialize.js +20 -1
- package/scripts/bongos/discipline-modes.json +7 -12
- package/scripts/bongos/fitness.js +1 -1
- package/scripts/bongos/gen-hall-sitemap.js +7 -7
- package/scripts/bongos/grade-smoke.js +2 -2
- package/scripts/bongos/init.js +2 -2
- package/scripts/bongos/newcomer-floor.config.json +1 -2
- package/scripts/bongos/newcomer-floor.js +4 -3
- package/scripts/bongos/newcomer-floor.templates.json +10 -12
- package/scripts/bongos/newcomer-floor.test.js +47 -38
- package/scripts/bongos/role-pack-guard.js +5 -4
- package/scripts/bongos/seed-tasks.example.json +3 -3
- package/scripts/bongos/setup.js +20 -107
- package/scripts/bongos/start.js +2 -2
- package/scripts/bongos/task.js +5 -4
- package/scripts/bongos/triage.js +1 -1
- package/scripts/bongos/worktree.js +16 -4
- package/scripts/setup-builder.js +6 -46
- package/src/bongos/auth-admission.js +8 -8
- package/src/bongos/craft-mode.js +2 -0
- package/src/bongos/db.js +39 -1
- package/src/bongos/github-repos.js +78 -0
- package/src/bongos/module-scope-map.js +22 -0
- package/src/bongos/role-stats.js +27 -11
- package/src/bongos/routes/github.js +100 -2
- package/src/bongos/routes/me.js +33 -0
- package/src/bongos/serve-internal.js +26 -9
- package/src/crafts.js +66 -0
- package/src/module-api.js +6 -1
- package/src/module-loader/manifest-schema.js +16 -0
- package/src/modules.js +2 -2
- package/src/role-responsibilities.js +6 -5
- package/tests/acclaim_feed_evidence_db.mjs +1 -1
- package/tests/activity_rollup_as_of.mjs +1 -1
- package/tests/api_alias_caller_ratchet.mjs +3 -3
- package/tests/application_lifecycle.mjs +1 -1
- package/tests/artist_review.mjs +1 -1
- package/tests/artist_review_goal.mjs +1 -1
- package/tests/autobongos_host.mjs +19 -1
- package/tests/blocker_hall_live_proof.mjs +1 -1
- package/tests/blocker_tasks_named_db.mjs +2 -2
- package/tests/canonical_profile_url.mjs +1 -1
- package/tests/cap_newcomer_tasks.mjs +13 -13
- package/tests/character_creation.mjs +294 -0
- package/tests/claim_gate_open_blocker.mjs +2 -2
- package/tests/claim_gate_rebase.mjs +6 -6
- package/tests/claude_materialize.mjs +75 -1
- package/tests/clawback_confirmed_backlog.mjs +3 -3
- package/tests/collab_page.mjs +8 -9
- package/tests/collab_self_settling.mjs +2 -2
- package/tests/community_leaderboard_route.mjs +13 -5
- package/tests/community_proof_db.mjs +3 -0
- package/tests/community_search_route.mjs +7 -6
- package/tests/conductor.mjs +1 -1
- package/tests/conductor_main_e2e.mjs +8 -8
- package/tests/copy_desk_flags_db.mjs +1 -1
- package/tests/copy_desk_page_approve.mjs +2 -2
- package/tests/copy_desk_page_claim.mjs +2 -2
- package/tests/copy_desk_page_next.mjs +1 -1
- package/tests/copy_desk_page_release.mjs +1 -1
- package/tests/copy_desk_page_submit.mjs +2 -2
- package/tests/core_258_box_session_claims_db.mjs +1 -1
- package/tests/craft_mode.mjs +48 -34
- package/tests/craft_mode_home.mjs +20 -20
- package/tests/craft_mode_looks.mjs +16 -15
- package/tests/craft_mode_nav.mjs +31 -29
- package/tests/craft_review_mode.mjs +46 -41
- package/tests/crafts_vocabulary.mjs +70 -0
- package/tests/cross_discipline_priority_direction.mjs +5 -5
- package/tests/fitness.mjs +7 -11
- package/tests/funded_project_pays_proof_db.mjs +1 -1
- package/tests/genesis_home.mjs +21 -1
- package/tests/github_chip.mjs +3 -3
- package/tests/github_repo_invite.mjs +379 -0
- package/tests/goal_criterion_confirm_ui.mjs +1 -1
- package/tests/goal_invite_consent.mjs +1 -1
- package/tests/goal_map_page.mjs +2 -2
- package/tests/goal_sort_staleness.mjs +1 -1
- package/tests/goal_spec_task.mjs +1 -1
- package/tests/goal_spec_task_db.mjs +3 -3
- package/tests/grade_engine.mjs +2 -2
- package/tests/grader_no_artifact.mjs +1 -1
- package/tests/grader_prompt_fencing.mjs +1 -1
- package/tests/grader_prompt_window_bound.mjs +1 -1
- package/tests/grader_reader_lens.mjs +2 -2
- package/tests/grader_severity_gate.mjs +1 -1
- package/tests/grader_unbacked_score.mjs +1 -1
- package/tests/hall_approval_queue.mjs +1 -1
- package/tests/hall_board_world.mjs +2 -2
- package/tests/hall_builders_page_pass.mjs +1 -1
- package/tests/hall_city.mjs +1 -1
- package/tests/hall_fullscreen.mjs +27 -3
- package/tests/hall_kit_charts.mjs +1 -1
- package/tests/hall_landing_boot.mjs +1 -1
- package/tests/hall_my_agents.mjs +16 -16
- package/tests/hall_nav.mjs +22 -15
- package/tests/hall_options_menu.mjs +247 -0
- package/tests/hall_oversight_world.mjs +1 -1
- package/tests/hall_palette.mjs +8 -9
- package/tests/hall_profile_world.mjs +3 -3
- package/tests/hall_record_world.mjs +2 -2
- package/tests/hall_settings_world.mjs +2 -1
- package/tests/hall_sitemap.mjs +6 -5
- package/tests/hall_start_screen.mjs +244 -0
- package/tests/hall_street.mjs +2 -2
- package/tests/hall_street_inside.mjs +17 -9
- package/tests/hall_studio_world.mjs +3 -3
- package/tests/hall_visor.mjs +224 -0
- package/tests/help_requests.mjs +47 -15
- package/tests/helpers.mjs +4 -4
- package/tests/home_city.mjs +6 -4
- package/tests/home_city_widget.mjs +33 -14
- package/tests/home_gallery_widget.mjs +4 -4
- package/tests/home_idea_box.mjs +6 -6
- package/tests/hub_community_page.mjs +3 -3
- package/tests/hub_craft_rollup.mjs +75 -36
- package/tests/hub_recent_ships.mjs +3 -2
- package/tests/idea_cascade.mjs +2 -2
- package/tests/idea_credit_streams_db.mjs +1 -1
- package/tests/idea_detail_page.mjs +1 -1
- package/tests/idea_ideate_full.mjs +5 -2
- package/tests/idea_spark_hall.mjs +1 -1
- package/tests/init.mjs +3 -3
- package/tests/instance_manifest.mjs +2 -2
- package/tests/kickoff_checklist.mjs +1 -1
- package/tests/leaderboard_roles.mjs +34 -25
- package/tests/lifecycle_claim_ladder_refs.mjs +55 -0
- package/tests/lightbulb.mjs +14 -10
- package/tests/main_discipline.mjs +8 -8
- package/tests/markdown_line_endings.mjs +1 -1
- package/tests/market_watch_pages.mjs +52 -74
- package/tests/me_streak_leaderboard_queries.mjs +3 -3
- package/tests/migration_allocation.mjs +1 -1
- package/tests/mingle.mjs +36 -21
- package/tests/module-scope-map.mjs +22 -2
- package/tests/module_api.mjs +1 -0
- package/tests/module_contributions.mjs +13 -13
- package/tests/module_manifest.mjs +29 -0
- package/tests/module_skills_hook.mjs +2 -2
- package/tests/modules.mjs +14 -7
- package/tests/nav_permission_atoms.mjs +2 -1
- package/tests/needs_rebase.mjs +1 -1
- package/tests/newcomer_restock.mjs +13 -13
- package/tests/page_notes_panel.mjs +22 -22
- package/tests/page_tweak_no_block.mjs +1 -1
- package/tests/paintbrush.mjs +7 -7
- package/tests/privacy_matrix.mjs +1 -0
- package/tests/profile_activity.mjs +20 -23
- package/tests/profile_earnings.mjs +1 -1
- package/tests/profile_nudge_prefs.mjs +4 -4
- package/tests/profile_role_headline.mjs +73 -46
- package/tests/profile_role_stats.mjs +55 -45
- package/tests/profile_rollup_consent.mjs +19 -6
- package/tests/profile_route.mjs +12 -12
- package/tests/profile_ui_cross_project.mjs +4 -4
- package/tests/project_invite_suggestions.mjs +2 -2
- package/tests/promote_goal_id.mjs +1 -1
- package/tests/publish_manifest.mjs +1 -1
- package/tests/rank_floor_wiring.mjs +1 -1
- package/tests/regrade_actual_minutes.mjs +1 -1
- package/tests/removal_proposal.mjs +2 -2
- package/tests/reward_gate.mjs +4 -4
- package/tests/reward_mode_cost_plus_only.mjs +1 -1
- package/tests/role_responsibilities.mjs +18 -12
- package/tests/role_stats_db.mjs +17 -16
- package/tests/scouting_rollup_fanout.mjs +5 -5
- package/tests/seed_tasks.mjs +5 -2
- package/tests/session_earnings_mirror_db.mjs +1 -1
- package/tests/session_reward_on_land_db.mjs +1 -1
- package/tests/session_reward_reconcile_db.mjs +2 -2
- package/tests/sessions_kit_pager.mjs +1 -1
- package/tests/settings_art_key_absent.mjs +2 -2
- package/tests/settings_main_role.mjs +21 -20
- package/tests/setup_flow.mjs +3 -3
- package/tests/setup_smoke.mjs +20 -0
- package/tests/ship_card.mjs +6 -6
- package/tests/specialities.mjs +16 -4
- package/tests/speciality_db_shape.mjs +6 -6
- package/tests/speciality_injection.mjs +17 -17
- package/tests/speciality_offer_ui.mjs +5 -5
- package/tests/speciality_offered.mjs +6 -5
- package/tests/speciality_session_skills.mjs +7 -7
- package/tests/speciality_skills.mjs +11 -11
- package/tests/speciality_walkthrough_ui.mjs +2 -2
- package/tests/sso_checkin_identity_spoof.mjs +2 -1
- package/tests/task_classifier_vocab.mjs +10 -0
- package/tests/task_cli.mjs +11 -3
- package/tests/task_criteria_unlink_db.mjs +2 -2
- package/tests/task_discipline_required.mjs +38 -16
- package/tests/task_goal_required.mjs +4 -4
- package/tests/task_needs_db.mjs +1 -1
- package/tests/tweak_replaces_artist_review.mjs +1 -1
- package/tests/update_pill.mjs +153 -2
- package/tests/upkeep_home.mjs +1 -1
- package/tests/v3_c1_options_proof.mjs +145 -0
- package/tests/visual_review.mjs +3 -3
- package/tests/watch_applications_queue.mjs +8 -5
- package/tests/watch_sealed_floor.mjs +3 -1
- package/tests/welcome_boot.mjs +12 -9
- package/tests/welcome_boot_browser.mjs +13 -13
- package/tests/worktree_helper.mjs +16 -1
|
@@ -937,14 +937,14 @@
|
|
|
937
937
|
}
|
|
938
938
|
}
|
|
939
939
|
|
|
940
|
-
// ----
|
|
940
|
+
// ---- the options menu's native groups (task 1723 → 2172 → 1005144) ---------
|
|
941
941
|
//
|
|
942
|
-
// The sections sit in
|
|
943
|
-
//
|
|
944
|
-
// shows only the active group
|
|
945
|
-
// [
|
|
946
|
-
//
|
|
947
|
-
// a visible section.
|
|
942
|
+
// The sections sit in groups; the left rail (#settings-rail) is the ONE options
|
|
943
|
+
// menu now (options-menu.js, ADR 0401), which also opens the screens that used
|
|
944
|
+
// to be pages of their own. CSS shows only the active group
|
|
945
|
+
// (.settings-groups[data-active]); a section's own [hidden] attribute still
|
|
946
|
+
// wins, so the per-section reveal logic above is unchanged. Deep links activate
|
|
947
|
+
// their group first, so scrollIntoView lands on a visible section.
|
|
948
948
|
|
|
949
949
|
const RAIL_ITEMS = [
|
|
950
950
|
{ id: 'account', label: 'Account' },
|
|
@@ -955,7 +955,6 @@
|
|
|
955
955
|
{ id: 'update', label: 'Software update' },
|
|
956
956
|
];
|
|
957
957
|
const RAIL_GROUPS = RAIL_ITEMS.map((i) => i.id).concat('extensions');
|
|
958
|
-
let rail = null;
|
|
959
958
|
|
|
960
959
|
// task 2172 — stable per-panel anchors (/settings#cli, /settings#sounds, …)
|
|
961
960
|
// so skills and docs can deep-link a panel without depending on internal
|
|
@@ -984,7 +983,6 @@
|
|
|
984
983
|
const groups = document.getElementById('settings-groups');
|
|
985
984
|
if (!groups) return;
|
|
986
985
|
groups.dataset.active = group;
|
|
987
|
-
rail?.activate(group);
|
|
988
986
|
// A panel whose read is expensive loads when its group opens, not with the page.
|
|
989
987
|
if (typeof CustomEvent === 'function') document.dispatchEvent(new CustomEvent('settings:group', { detail: { group } }));
|
|
990
988
|
if (!opts || opts.scroll !== false) window.scrollTo({ top: 0 });
|
|
@@ -1001,45 +999,52 @@
|
|
|
1001
999
|
return grouped ? { group: grouped.getAttribute('data-group'), el } : null;
|
|
1002
1000
|
}
|
|
1003
1001
|
|
|
1004
|
-
//
|
|
1005
|
-
//
|
|
1006
|
-
|
|
1002
|
+
// One of Settings' own groups, opened by the options menu (options-menu.js); a
|
|
1003
|
+
// panel anchor (#cli) also scrolls its panel into view.
|
|
1004
|
+
function showNative(group, anchor, opts) {
|
|
1005
|
+
const t = anchor ? resolveHash(anchor) : null;
|
|
1006
|
+
activateGroup(t ? t.group : group, { scroll: false });
|
|
1007
|
+
if (t && t.el && (!opts || opts.scroll !== false)) t.el.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
1008
|
+
else if (!t || !t.el) window.scrollTo({ top: 0 });
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// Land the address on its place. The menu reads ?open= and a menu entry's id
|
|
1012
|
+
// (#account, ?open=my-agents#talk); a panel anchor (#cli) is this page's.
|
|
1013
|
+
// Runs on load, after data lands (late-revealed sections), and on hashchange.
|
|
1007
1014
|
function applyHash(opts) {
|
|
1015
|
+
if (leaveIfMoved()) return;
|
|
1016
|
+
const menu = window.OTBOptions;
|
|
1017
|
+
if (menu && menu.openFromUrl()) return;
|
|
1008
1018
|
const t = resolveHash(window.location.hash);
|
|
1009
1019
|
if (!t) return;
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
t.el.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
1013
|
-
} else if (!t.el) {
|
|
1014
|
-
window.scrollTo({ top: 0 });
|
|
1015
|
-
}
|
|
1020
|
+
if (menu) menu.activate(t.group, null, { url: false });
|
|
1021
|
+
showNative(t.group, t.el ? window.location.hash : null, opts);
|
|
1016
1022
|
}
|
|
1017
1023
|
|
|
1018
1024
|
// task 1005044 — a link to an agent setting that moved to My agents
|
|
1019
|
-
// (settings-moved.js)
|
|
1025
|
+
// (settings-moved.js) opens it in the menu, where My agents lives now (task 1005144).
|
|
1020
1026
|
function leaveIfMoved() {
|
|
1021
1027
|
const to = window.OTBSettingsMoved && window.OTBSettingsMoved.movedTarget(window.location.hash);
|
|
1022
|
-
if (to)
|
|
1023
|
-
|
|
1028
|
+
if (!to) return false;
|
|
1029
|
+
const anchor = to.split('#')[1] || null;
|
|
1030
|
+
if (window.OTBOptions) window.OTBOptions.activate('my-agents', anchor);
|
|
1031
|
+
return true;
|
|
1024
1032
|
}
|
|
1025
1033
|
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
// keeps Back for leaving the page, not retracing rail clicks.
|
|
1037
|
-
history.replaceState(null, '', window.location.pathname + window.location.search + `#${key}`);
|
|
1038
|
-
},
|
|
1034
|
+
// The options menu (task 1005144): settings.js hands it the viewer once /me answers.
|
|
1035
|
+
function wireMenu(builder, contributed) {
|
|
1036
|
+
const menu = window.OTBOptions;
|
|
1037
|
+
if (!menu) return;
|
|
1038
|
+
menu.mount({
|
|
1039
|
+
rail: document.getElementById('settings-rail'),
|
|
1040
|
+
groups: document.getElementById('settings-groups'),
|
|
1041
|
+
viewer: { authed: true, rank: builder && builder.rank },
|
|
1042
|
+
contributed,
|
|
1043
|
+
onNative: (id, anchor) => showNative(id, anchor),
|
|
1039
1044
|
});
|
|
1040
|
-
|
|
1045
|
+
if (!menu.openFromUrl() && !resolveHash(window.location.hash)) menu.activate('account', null, { url: false });
|
|
1041
1046
|
applyHash({ scroll: false });
|
|
1042
|
-
window.addEventListener('hashchange', () =>
|
|
1047
|
+
window.addEventListener('hashchange', () => applyHash());
|
|
1043
1048
|
}
|
|
1044
1049
|
|
|
1045
1050
|
// Hide the rail + groups entirely while the not-signed-in gate is showing —
|
|
@@ -1104,13 +1109,10 @@
|
|
|
1104
1109
|
// MODULE PANELS — render any Settings panels modules contributed via the
|
|
1105
1110
|
// seam (BV1.R42). None today; ready for discord/art once they leave core.
|
|
1106
1111
|
mountContributedPanels();
|
|
1107
|
-
//
|
|
1108
|
-
//
|
|
1112
|
+
// THE OPTIONS MENU (task 1005144): the rail is the one menu now. The Extensions
|
|
1113
|
+
// entry shows only when a module actually contributed a panel.
|
|
1109
1114
|
const contributed = $('#contributed-panels');
|
|
1110
|
-
|
|
1111
|
-
rail.refresh([...RAIL_ITEMS, { id: 'extensions', label: 'Extensions' }]);
|
|
1112
|
-
rail.activate($('#settings-groups')?.dataset.active || 'account');
|
|
1113
|
-
}
|
|
1115
|
+
wireMenu(builder, !!(contributed && contributed.children.length > 0));
|
|
1114
1116
|
|
|
1115
1117
|
// Sounds are switched off for now (task 1004554 — .claude/sounds/play.js's
|
|
1116
1118
|
// SOUNDS_ON). The per-sound toggles (#786) and the per-event picker (task 582)
|
|
@@ -1129,7 +1131,5 @@
|
|
|
1129
1131
|
applyHash();
|
|
1130
1132
|
}
|
|
1131
1133
|
|
|
1132
|
-
if (leaveIfMoved()) return;
|
|
1133
|
-
wireRail();
|
|
1134
1134
|
load();
|
|
1135
1135
|
})();
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/* shell-game.css — the game's start screen and load screen (task 1005143; shell-game.js start()).
|
|
2
|
+
Both stand over the welcome's painting, darkened by the hall's scrim. The menu and the load
|
|
3
|
+
screen are each a card of the hall's own tokens, so they follow the hall's theme and the city
|
|
4
|
+
drawn in the load card sits on its own ground. Colours, sizes, corners and spacing are tokens;
|
|
5
|
+
nothing here is typed in. Rules: modules/hall-ui/records/start-screen.md. */
|
|
6
|
+
html.gs-open { overflow: hidden; }
|
|
7
|
+
.gs {
|
|
8
|
+
position: fixed; inset: 0; z-index: var(--ds-z-modal, 160); overflow: auto;
|
|
9
|
+
display: flex; align-items: center; justify-content: center;
|
|
10
|
+
padding: var(--ds-space-lg) var(--ds-space-gutter-narrow);
|
|
11
|
+
background: var(--bg); color: var(--ink); font-family: var(--font-body);
|
|
12
|
+
transition: opacity .5s var(--ds-ease);
|
|
13
|
+
}
|
|
14
|
+
.gs--leaving { opacity: 0; pointer-events: none; }
|
|
15
|
+
|
|
16
|
+
/* the still: the painting, covering the screen, under the scrim so a card reads on any part of it */
|
|
17
|
+
.gs__art { position: absolute; inset: 0; }
|
|
18
|
+
.gs__art::after { content: ""; position: absolute; inset: 0; background: var(--scrim); }
|
|
19
|
+
.gs__img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
|
20
|
+
|
|
21
|
+
/* the two cards */
|
|
22
|
+
.gs__menu, .gs__load {
|
|
23
|
+
position: relative; width: 100%; box-sizing: border-box; padding: var(--ds-space-md);
|
|
24
|
+
background: var(--bg-card); border-radius: var(--ds-radius-panel); box-shadow: var(--ds-shadow-pop);
|
|
25
|
+
}
|
|
26
|
+
.gs__menu[hidden], .gs__load[hidden] { display: none; }
|
|
27
|
+
|
|
28
|
+
/* the start screen: the project's name over five choices, one column */
|
|
29
|
+
.gs__menu { max-width: 24rem; }
|
|
30
|
+
.gs__title {
|
|
31
|
+
margin: var(--ds-space-xs) 0 var(--ds-space-md); text-align: center; text-wrap: balance;
|
|
32
|
+
font-family: var(--font-display); font-size: var(--ds-text-title); font-weight: 700; line-height: 1.1;
|
|
33
|
+
}
|
|
34
|
+
.gs__items { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--ds-space-xs); }
|
|
35
|
+
.gs__items[hidden] { display: none; }
|
|
36
|
+
.gs__items--extras { margin-top: var(--ds-space-sm); flex-direction: row; flex-wrap: wrap; justify-content: center; }
|
|
37
|
+
.gs__item {
|
|
38
|
+
display: flex; align-items: center; justify-content: center; box-sizing: border-box;
|
|
39
|
+
width: 100%; min-height: 48px; padding: var(--ds-space-xs) var(--ds-space-md);
|
|
40
|
+
background: transparent; color: inherit; border: 1px solid var(--rule);
|
|
41
|
+
border-radius: var(--ds-radius-pill); font: inherit; font-size: var(--ds-text-xl); font-weight: 600;
|
|
42
|
+
text-decoration: none; cursor: pointer;
|
|
43
|
+
transition: background-color .15s var(--ds-ease), border-color .15s var(--ds-ease);
|
|
44
|
+
}
|
|
45
|
+
.gs__item:hover { background: var(--bg); border-color: var(--ink-soft); text-decoration: none; }
|
|
46
|
+
.gs__item:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
|
|
47
|
+
/* Start is the one filled choice, as the hall's primary pill is */
|
|
48
|
+
.gs__item[data-gs="start"] { background: var(--accent); color: var(--accent-on); border-color: transparent; }
|
|
49
|
+
.gs__item[data-gs="start"]:hover { background: var(--accent-lift); border-color: transparent; }
|
|
50
|
+
.gs__item--extra { width: auto; min-height: 40px; font-size: var(--ds-text-md); font-weight: 500; }
|
|
51
|
+
.gs__note { margin: var(--ds-space-md) 0 0; text-align: center; font-size: var(--ds-text-md); color: var(--ink-soft); }
|
|
52
|
+
.gs__note[hidden] { display: none; }
|
|
53
|
+
|
|
54
|
+
/* the load screen: the city building up, the week it is showing, and the tip under it */
|
|
55
|
+
.gs__load { max-width: 44rem; display: flex; flex-direction: column; gap: var(--ds-space-sm); text-align: center; }
|
|
56
|
+
.gs__city { display: block; width: 100%; height: auto; max-height: 56vh; }
|
|
57
|
+
.gs__city[hidden], .gs__when[hidden] { display: none; }
|
|
58
|
+
.gs__when { margin: 0; font-size: var(--ds-text-sm); color: var(--ink-soft); font-variant-numeric: tabular-nums; }
|
|
59
|
+
.gs__tip { margin: 0; font-size: var(--ds-text-lg); line-height: 1.45; text-wrap: pretty; }
|
|
60
|
+
.gs__tip b {
|
|
61
|
+
display: inline-block; margin-right: var(--ds-space-xs); padding: 0 var(--ds-space-xs);
|
|
62
|
+
border: 1px solid var(--rule); border-radius: var(--ds-radius-pill);
|
|
63
|
+
font-size: var(--ds-text-2xs); font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
|
|
64
|
+
color: var(--ink-soft); vertical-align: middle;
|
|
65
|
+
}
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
//
|
|
10
10
|
// The mode is one localStorage key (`hall-game`), so it follows the browser across
|
|
11
11
|
// every hall page, and one attribute, html[data-game="on"], for a sheet or script
|
|
12
|
-
// that wants to read it. Leaving full screen in game mode (the button
|
|
12
|
+
// that wants to read it. Leaving full screen in game mode (the corner button)
|
|
13
13
|
// brings the bars back for THAT page load only: the next page opens without them
|
|
14
14
|
// again, and a page's own remembered choice is never overwritten. `?game=on` /
|
|
15
|
-
// `?game=off` on any hall URL sets it by hand
|
|
15
|
+
// `?game=off` on any hall URL sets it by hand; the start screen's Start sets it on (R03, below).
|
|
16
16
|
// Loaded BEFORE shell.js on every hall page; a page without this file is simply
|
|
17
17
|
// never in game mode.
|
|
18
18
|
(function () {
|
|
@@ -41,10 +41,290 @@
|
|
|
41
41
|
write('sessionStorage', HINT_KEY, '1');
|
|
42
42
|
return true;
|
|
43
43
|
}
|
|
44
|
+
|
|
45
|
+
// ---------- the start screen and the load screen (task 1005143, BONGOS-V3 R03) ----------
|
|
46
|
+
// A fresh sign-in opens on the start screen: Start, Options, Achievements, Extras, Quit over a
|
|
47
|
+
// still. Start turns game mode on and passes through the load screen, where the project city
|
|
48
|
+
// builds itself up week by week under a tip, and then the world is there. welcome-boot.js calls
|
|
49
|
+
// start() on Home (where a sign-in lands) and hands in the still; this file draws both screens.
|
|
50
|
+
// Rules are in modules/hall-ui/records/start-screen.md.
|
|
51
|
+
var MENU = [
|
|
52
|
+
{ id: 'start', label: 'Start' },
|
|
53
|
+
{ id: 'options', label: 'Options', href: '/settings' }, // the ONE options menu (task 1005144)
|
|
54
|
+
{ id: 'achievements', label: 'Achievements', href: '/profile' },
|
|
55
|
+
{ id: 'extras', label: 'Extras' },
|
|
56
|
+
{ id: 'quit', label: 'Quit' },
|
|
57
|
+
];
|
|
58
|
+
var EXTRAS = [
|
|
59
|
+
{ label: 'The thinking sky', href: '/thinking' },
|
|
60
|
+
{ label: 'The atlas', href: '/atlas' },
|
|
61
|
+
{ label: 'The docs', href: '/docs' },
|
|
62
|
+
];
|
|
63
|
+
var TIPS = [
|
|
64
|
+
'Escape opens the options menu, where every setting lives.',
|
|
65
|
+
'Every tower is a goal. It gains a floor each time work ships under it.',
|
|
66
|
+
'A crane marks a site someone worked on this week.',
|
|
67
|
+
'A salmon dot on a tower means something there is waiting on you.',
|
|
68
|
+
'Ctrl K, or Cmd K on a Mac, jumps to any page.',
|
|
69
|
+
];
|
|
70
|
+
var TIP_KEY = 'hall-game-tip';
|
|
71
|
+
var CITY_READ = '/api/bongos/home/city?weeks=10';
|
|
72
|
+
var STEPS = 30, STEP_MS = 90; // the history runs in about three seconds
|
|
73
|
+
var HOLD_MS = 1400; // how long the still and the tip stand when there is no city to build
|
|
74
|
+
var LEAVE_MS = 500; // the fade into the world (shell-game.css)
|
|
75
|
+
var NS = 'http://www.w3.org/2000/svg';
|
|
76
|
+
|
|
77
|
+
function tipAt(n) { return TIPS[((Number(n) || 0) % TIPS.length + TIPS.length) % TIPS.length]; }
|
|
78
|
+
// The weeks the load screen stops at, oldest first, ending exactly on now.
|
|
79
|
+
function historySteps(weeks, steps) {
|
|
80
|
+
var last = Math.max(0, (Number(weeks) || 0) - 1), n = Math.max(1, Number(steps) || 1), out = [];
|
|
81
|
+
for (var i = 0; i <= n; i++) out.push(Math.round((last * i / n) * 100) / 100);
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
function whenLabel(weeks, t) {
|
|
85
|
+
var ago = Math.round(Math.max(0, (Number(weeks) || 0) - 1) - t);
|
|
86
|
+
return ago <= 0 ? 'The city, now' : 'The city, ' + ago + (ago === 1 ? ' week ago' : ' weeks ago');
|
|
87
|
+
}
|
|
88
|
+
function esc(v) { return window.OTB.escapeHtml(v); }
|
|
89
|
+
function itemHtml(m) {
|
|
90
|
+
var inner = ' class="gs__item" data-gs="' + m.id + '"';
|
|
91
|
+
if (m.href) return '<li><a' + inner + ' href="' + esc(m.href) + '">' + esc(m.label) + '</a></li>';
|
|
92
|
+
return '<li><button type="button"' + inner + (m.id === 'extras' ? ' aria-expanded="false" aria-controls="gs-extras"' : '') + '>' + esc(m.label) + '</button></li>';
|
|
93
|
+
}
|
|
94
|
+
function screenHtml(o) {
|
|
95
|
+
return '<div class="gs__art" aria-hidden="true">' + (o.art ? '<img class="gs__img" alt="" src="' + esc(o.art) + '" decoding="async">' : '') + '</div>' +
|
|
96
|
+
'<div class="gs__menu">' +
|
|
97
|
+
'<h2 class="gs__title" id="gs-title">' + esc(o.name || 'This project') + '</h2>' +
|
|
98
|
+
'<ul class="gs__items">' + MENU.map(itemHtml).join('') + '</ul>' +
|
|
99
|
+
'<ul class="gs__items gs__items--extras" id="gs-extras" hidden>' + EXTRAS.map(function (x) {
|
|
100
|
+
return '<li><a class="gs__item gs__item--extra" href="' + esc(x.href) + '">' + esc(x.label) + '</a></li>';
|
|
101
|
+
}).join('') + '</ul>' +
|
|
102
|
+
'<p class="gs__note" id="gs-note" role="status" hidden></p>' +
|
|
103
|
+
'</div>' +
|
|
104
|
+
'<div class="gs__load" id="gs-load" role="status" hidden>' +
|
|
105
|
+
'<svg class="gs__city" id="gs-city" role="img" aria-label="The project city building up, week by week" hidden></svg>' +
|
|
106
|
+
'<p class="gs__when" id="gs-when" hidden></p>' +
|
|
107
|
+
'<p class="gs__tip"><b>Tip</b><span id="gs-tip"></span></p>' +
|
|
108
|
+
'</div>';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
var root = null, opts = null, timers = [], phase = null;
|
|
112
|
+
function later(ms, fn) { timers.push(setTimeout(fn, ms)); }
|
|
113
|
+
function stopTimers() { timers.forEach(clearTimeout); timers = []; }
|
|
114
|
+
function still() { return !!(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches); }
|
|
115
|
+
function items() {
|
|
116
|
+
return Array.prototype.filter.call(root.querySelectorAll('.gs__item'), function (b) { return !b.closest('[hidden]'); });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function close() {
|
|
120
|
+
if (!root) return;
|
|
121
|
+
stopTimers();
|
|
122
|
+
document.removeEventListener('keydown', onKey, true);
|
|
123
|
+
if (root.parentNode) root.parentNode.removeChild(root);
|
|
124
|
+
root = null; phase = null;
|
|
125
|
+
document.documentElement.classList.remove('gs-open');
|
|
126
|
+
var done = opts && opts.done;
|
|
127
|
+
opts = null;
|
|
128
|
+
if (typeof done === 'function') done();
|
|
129
|
+
}
|
|
130
|
+
function leave() {
|
|
131
|
+
if (!root || phase === 'leaving') return;
|
|
132
|
+
phase = 'leaving';
|
|
133
|
+
stopTimers();
|
|
134
|
+
if (still()) { close(); return; }
|
|
135
|
+
root.classList.add('gs--leaving');
|
|
136
|
+
later(LEAVE_MS, close);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ---- the load screen: the still, a tip, and the city building up through its history ----
|
|
140
|
+
function build(n) {
|
|
141
|
+
var e = document.createElementNS(NS, n.tag);
|
|
142
|
+
Object.keys(n.attrs).forEach(function (k) { e.setAttribute(k, String(n.attrs[k])); });
|
|
143
|
+
if (n.text != null) e.textContent = n.text;
|
|
144
|
+
n.kids.forEach(function (c) { e.appendChild(build(c)); });
|
|
145
|
+
return e;
|
|
146
|
+
}
|
|
147
|
+
function themeMode() {
|
|
148
|
+
var t = document.documentElement.dataset.theme;
|
|
149
|
+
if (t === 'light' || t === 'dark') return t;
|
|
150
|
+
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
151
|
+
}
|
|
152
|
+
function drawCity(model, t) {
|
|
153
|
+
var Draw = window.OTBHomeCityDraw, svg = root && root.querySelector('#gs-city'), when = root && root.querySelector('#gs-when');
|
|
154
|
+
if (!svg) return;
|
|
155
|
+
var out = Draw.citySvg(model, t, { mode: themeMode(), gov: window.GovCity || null });
|
|
156
|
+
svg.setAttribute('viewBox', out.viewBox);
|
|
157
|
+
while (svg.firstChild) svg.removeChild(svg.firstChild);
|
|
158
|
+
out.nodes.forEach(function (n) { svg.appendChild(build(n)); });
|
|
159
|
+
svg.removeAttribute('hidden');
|
|
160
|
+
when.textContent = whenLabel(model.W, t);
|
|
161
|
+
when.hidden = false;
|
|
162
|
+
}
|
|
163
|
+
// One week after another, as state: each stop is a whole drawing, so nothing here animates.
|
|
164
|
+
function runHistory(model) {
|
|
165
|
+
if (!root || phase !== 'load') return;
|
|
166
|
+
stopTimers();
|
|
167
|
+
var stops = still() ? [model.W - 1] : historySteps(model.W, STEPS), i = 0;
|
|
168
|
+
(function step() {
|
|
169
|
+
if (!root) return;
|
|
170
|
+
drawCity(model, stops[i]);
|
|
171
|
+
i += 1;
|
|
172
|
+
if (i < stops.length) later(STEP_MS, step); else later(still() ? HOLD_MS : 700, leave);
|
|
173
|
+
})();
|
|
174
|
+
}
|
|
175
|
+
function readCity() {
|
|
176
|
+
var Draw = window.OTBHomeCityDraw;
|
|
177
|
+
if (!Draw || typeof fetch !== 'function') return;
|
|
178
|
+
fetch(CITY_READ, { credentials: 'same-origin' }).then(function (r) { return r.ok ? r.json() : null; }).then(function (data) {
|
|
179
|
+
var model = data && Draw.cityModel(data);
|
|
180
|
+
if (model && model.W && model.areas.length) runHistory(model);
|
|
181
|
+
}, function () { /* no city: the still and the tip stand, then the world */ });
|
|
182
|
+
}
|
|
183
|
+
function load() {
|
|
184
|
+
if (!root || phase !== 'menu') return;
|
|
185
|
+
phase = 'load';
|
|
186
|
+
set(true);
|
|
187
|
+
var n = Number(read('localStorage', TIP_KEY)) || 0;
|
|
188
|
+
write('localStorage', TIP_KEY, String(n + 1));
|
|
189
|
+
root.querySelector('#gs-tip').textContent = tipAt(n);
|
|
190
|
+
root.querySelector('.gs__menu').hidden = true;
|
|
191
|
+
root.querySelector('#gs-load').hidden = false;
|
|
192
|
+
root.classList.add('gs--load');
|
|
193
|
+
later(HOLD_MS, leave); // runHistory replaces this once the city answers
|
|
194
|
+
readCity();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Quit closes the tab. A browser only lets a script close a tab a script opened, so when the
|
|
198
|
+
// tab is still here a moment later the screen says so instead of doing nothing.
|
|
199
|
+
function quit() {
|
|
200
|
+
try { window.close(); } catch (_) { /* refused */ }
|
|
201
|
+
later(300, function () {
|
|
202
|
+
var note = root && root.querySelector('#gs-note');
|
|
203
|
+
if (!note) return;
|
|
204
|
+
note.textContent = 'Your browser kept this tab open. Close it to quit.';
|
|
205
|
+
note.hidden = false;
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
function toggleExtras(btn) {
|
|
209
|
+
var box = root.querySelector('#gs-extras'), open = box.hidden;
|
|
210
|
+
box.hidden = !open;
|
|
211
|
+
btn.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
212
|
+
if (open) box.querySelector('a').focus();
|
|
213
|
+
}
|
|
214
|
+
function onClick(e) {
|
|
215
|
+
var el = e.target.closest && e.target.closest('.gs__item');
|
|
216
|
+
if (!el || phase !== 'menu') return;
|
|
217
|
+
var id = el.getAttribute('data-gs');
|
|
218
|
+
if (id === 'start') load();
|
|
219
|
+
else if (id === 'extras') toggleExtras(el);
|
|
220
|
+
else if (id === 'quit') quit();
|
|
221
|
+
}
|
|
222
|
+
// The screen keeps every key while it is up: Escape folds Extras away and nothing else (the
|
|
223
|
+
// shell's Escape must not flip the bars underneath), the arrows and Tab stay inside the menu.
|
|
224
|
+
function onKey(e) {
|
|
225
|
+
if (!root) return;
|
|
226
|
+
if (e.key === 'Escape') {
|
|
227
|
+
e.preventDefault();
|
|
228
|
+
var box = root.querySelector('#gs-extras');
|
|
229
|
+
if (phase === 'menu' && !box.hidden) { var b = root.querySelector('[data-gs="extras"]'); toggleExtras(b); b.focus(); }
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (phase !== 'menu') return;
|
|
233
|
+
var f = items(), at = f.indexOf(document.activeElement), step = 0;
|
|
234
|
+
if (e.key === 'ArrowDown') step = 1;
|
|
235
|
+
else if (e.key === 'ArrowUp') step = -1;
|
|
236
|
+
else if (e.key === 'Tab') step = e.shiftKey ? -1 : 1;
|
|
237
|
+
if (!step || !f.length) return;
|
|
238
|
+
e.preventDefault();
|
|
239
|
+
f[(at + step + f.length) % f.length].focus();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Put the start screen up. `o`: { name, art, done } — the project's name, the still to stand
|
|
243
|
+
// behind both screens, and what to do once the world is showing. True when it is up.
|
|
244
|
+
function start(o) {
|
|
245
|
+
if (root || !document.body) return !!root;
|
|
246
|
+
opts = o || {};
|
|
247
|
+
phase = 'menu';
|
|
248
|
+
root = document.createElement('div');
|
|
249
|
+
root.className = 'gs';
|
|
250
|
+
root.id = 'game-start';
|
|
251
|
+
root.setAttribute('role', 'dialog');
|
|
252
|
+
root.setAttribute('aria-modal', 'true');
|
|
253
|
+
root.setAttribute('aria-labelledby', 'gs-title');
|
|
254
|
+
root.innerHTML = screenHtml(opts);
|
|
255
|
+
root.addEventListener('click', onClick);
|
|
256
|
+
document.addEventListener('keydown', onKey, true);
|
|
257
|
+
document.body.appendChild(root);
|
|
258
|
+
document.documentElement.classList.add('gs-open');
|
|
259
|
+
var first = root.querySelector('.gs__item');
|
|
260
|
+
if (first && first.focus) first.focus();
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
|
|
44
264
|
try {
|
|
45
265
|
var asked = new URLSearchParams(window.location.search || '').get('game');
|
|
46
266
|
if (asked === 'on' || asked === 'off') write('localStorage', KEY, asked);
|
|
47
267
|
} catch (_) { /* old engine: the stored value stands */ }
|
|
48
268
|
stamp();
|
|
49
|
-
|
|
269
|
+
|
|
270
|
+
// ---------- the options menu (task 1005144, BONGOS-V3 R04, ADR 0401) ----------
|
|
271
|
+
// Every setting lives in ONE menu, the Settings page (options-menu.js). A screen
|
|
272
|
+
// that used to have its own page (My agents, Project settings, Modules, …) now
|
|
273
|
+
// opens INSIDE that menu, in a frame, with `?embed=1`. Such a page is EMBEDDED:
|
|
274
|
+
// html[data-embed="on"], no sidebar, top bar, foot or page head (style.css), no
|
|
275
|
+
// full-screen switch, a link to another page opens in the whole window, and an
|
|
276
|
+
// Escape nobody used goes to the menu. Only inside a frame: the same URL opened
|
|
277
|
+
// on its own is the page with its chrome (the server sends a plain visit of the
|
|
278
|
+
// old address to the menu, serve-internal.js).
|
|
279
|
+
var embedded = false;
|
|
280
|
+
try {
|
|
281
|
+
embedded = new URLSearchParams(window.location.search || '').get('embed') === '1'
|
|
282
|
+
&& !!window.parent && window.parent !== window;
|
|
283
|
+
} catch (_) { embedded = false; }
|
|
284
|
+
if (embedded) {
|
|
285
|
+
document.documentElement.dataset.embed = 'on';
|
|
286
|
+
// The shell reads this before it draws the switch, so no button, no Escape-to-leave.
|
|
287
|
+
if (document.body && document.body.setAttribute) document.body.setAttribute('data-fullscreen-switch', 'off');
|
|
288
|
+
}
|
|
289
|
+
// The hall's address for the menu: '/settings' on the builders host, '/builders/settings' on the apex.
|
|
290
|
+
function menuPath() {
|
|
291
|
+
var p = String((window.location && window.location.pathname) || '');
|
|
292
|
+
return (p.indexOf('/builders/') === 0 || p === '/builders' ? '/builders' : '') + '/settings';
|
|
293
|
+
}
|
|
294
|
+
function inField(el) {
|
|
295
|
+
var tag = el && el.tagName ? String(el.tagName).toLowerCase() : '';
|
|
296
|
+
return tag === 'input' || tag === 'textarea' || tag === 'select' || !!(el && el.isContentEditable);
|
|
297
|
+
}
|
|
298
|
+
// ADR 0399 §2.4 row 6: in game mode, the last Escape — one no dialog, picker,
|
|
299
|
+
// palette, sheet or scene pop-up used — is the options menu's. It is judged after
|
|
300
|
+
// the dispatch (a page listener may sit later on the window), and it is marked
|
|
301
|
+
// used before the shell's own check runs, so the bars do not flash back first.
|
|
302
|
+
// On the menu itself it closes the menu; inside a frame it is the menu's too.
|
|
303
|
+
function onEscape(e) {
|
|
304
|
+
if (!e || e.key !== 'Escape' || e.defaultPrevented || inField(e.target)) return;
|
|
305
|
+
if (!embedded && !on()) return;
|
|
306
|
+
window.setTimeout(function () {
|
|
307
|
+
if (e.defaultPrevented) return;
|
|
308
|
+
try { e.preventDefault(); } catch (_) { /* already finished */ }
|
|
309
|
+
var host = embedded ? window.parent : window;
|
|
310
|
+
if (host && host.OTBOptions && typeof host.OTBOptions.escape === 'function') { host.OTBOptions.escape(); return; }
|
|
311
|
+
if (embedded) return;
|
|
312
|
+
var here = String(window.location.pathname || '') + String(window.location.search || '') + String(window.location.hash || '');
|
|
313
|
+
window.location.assign(menuPath() + '?from=' + encodeURIComponent(here));
|
|
314
|
+
}, 0);
|
|
315
|
+
}
|
|
316
|
+
if (window.addEventListener) window.addEventListener('keydown', onEscape);
|
|
317
|
+
// Inside the menu, a link to ANOTHER page leaves the menu for it; a link within the page stays.
|
|
318
|
+
if (embedded && document.addEventListener) {
|
|
319
|
+
document.addEventListener('click', function (e) {
|
|
320
|
+
var a = e.target && e.target.closest ? e.target.closest('a[href]') : null;
|
|
321
|
+
if (!a || a.getAttribute('target')) return;
|
|
322
|
+
try {
|
|
323
|
+
var to = new URL(a.getAttribute('href'), window.location.href);
|
|
324
|
+
if (to.origin !== window.location.origin || to.pathname !== window.location.pathname) a.setAttribute('target', '_top');
|
|
325
|
+
} catch (_) { /* a href the browser cannot read: leave it alone */ }
|
|
326
|
+
}, true);
|
|
327
|
+
}
|
|
328
|
+
window.OTBShellGame = { on: on, set: set, hint: hint, start: start, tipAt: tipAt, historySteps: historySteps, whenLabel: whenLabel, MENU: MENU, TIPS: TIPS,
|
|
329
|
+
embedded: function () { return embedded; }, menuPath: menuPath };
|
|
50
330
|
})();
|
|
@@ -110,8 +110,6 @@
|
|
|
110
110
|
// You
|
|
111
111
|
accomplishments: '<path d="M6 2.5l1.6 4.2M12 2.5l-1.6 4.2"/><circle cx="9" cy="10.8" r="4.2"/><circle cx="9" cy="10.8" r="1.8"/>',
|
|
112
112
|
profile: '<rect x="2.5" y="4" width="13" height="10" rx="1.5"/><circle cx="6.3" cy="8" r="1.6"/><path d="M4.1 12c.3-1.1 1.1-1.7 2.2-1.7s1.9.6 2.2 1.7"/><path d="M10.4 7.5h3M10.4 10.3h3"/>',
|
|
113
|
-
// task 1004685 (prototype): My agents — a helper's head on an antenna, not a person.
|
|
114
|
-
agents: '<rect x="4" y="6" width="10" height="8.5" rx="2.5"/><path d="M9 6V3.6"/><circle cx="9" cy="2.7" r="0.9"/><path d="M7 10.2v0.6M11 10.2v0.6"/><path d="M1.8 9v3M16.2 9v3"/>',
|
|
115
113
|
// Community
|
|
116
114
|
collab: '<path d="M2.5 4a1 1 0 0 1 1-1h7a1 1 0 0 1 1 1v4.5a1 1 0 0 1-1 1H6.5L4 11.5v-2h-.5a1 1 0 0 1-1-1V4Z"/><path d="M11.5 6.5h3a1 1 0 0 1 1 1V12a1 1 0 0 1-1 1H14v2l-2.5-2H8.5a1 1 0 0 1-1-1V9.5"/>',
|
|
117
115
|
roster: '<circle cx="6.5" cy="6.3" r="2.3"/><path d="M2.5 15c.4-2.3 1.9-3.7 4-3.7s3.6 1.4 4 3.7"/><circle cx="12.5" cy="5.8" r="2"/><path d="M11.4 10.5c.4-.1.7-.2 1.1-.2 1.9 0 3.3 1.3 3.7 3.5"/>',
|
|
@@ -119,16 +117,13 @@
|
|
|
119
117
|
primer: '<path d="M9 5C7.5 3.9 5.5 3.5 2.5 3.5v10c3 0 5 .4 6.5 1.5 1.5-1.1 3.5-1.5 6.5-1.5v-10c-3 0-5 .4-6.5 1.5Z"/><path d="M9 5v10"/>',
|
|
120
118
|
commands: '<rect x="2.5" y="3.5" width="13" height="11" rx="1.5"/><path d="M5.5 7.3 7.7 9l-2.2 1.7M9.5 11.2h3"/>',
|
|
121
119
|
atlas: '<circle cx="9" cy="9" r="6.5"/><path d="M2.5 9h13M9 2.5c1.7 1.8 2.6 4 2.6 6.5s-.9 4.7-2.6 6.5c-1.7-1.8-2.6-4-2.6-6.5S7.3 4.3 9 2.5Z"/>',
|
|
122
|
-
modules: '<rect x="2.5" y="2.5" width="5.5" height="5.5" rx="1"/><rect x="10" y="2.5" width="5.5" height="5.5" rx="1"/><rect x="2.5" y="10" width="5.5" height="5.5" rx="1"/><path d="M12.75 10.5v4.5M10.5 12.75h4.5"/>',
|
|
123
120
|
apidocs: '<path d="M6.5 3C5 3 4.5 3.7 4.5 5v2c0 1-.6 2-1.5 2 .9 0 1.5 1 1.5 2v2c0 1.3.5 2 2 2M11.5 3c1.5 0 2 .7 2 2v2c0 1 .6 2 1.5 2-.9 0-1.5 1-1.5 2v2c0 1.3-.5 2-2 2"/>',
|
|
124
121
|
// Government
|
|
125
122
|
watch: '<path d="M1.8 9S4.5 4 9 4s7.2 5 7.2 5-2.7 5-7.2 5S1.8 9 1.8 9Z"/><circle cx="9" cy="9" r="2.2"/>',
|
|
126
123
|
gate: '<path d="M9 2.5 15 5v4c0 3.5-2.4 6-6 7-3.6-1-6-3.5-6-7V5l6-2.5Z"/><path d="M6.5 9l1.8 1.8 3.4-3.6"/>',
|
|
127
124
|
sessions: '<path d="M7 5h8M7 9h8M7 13h8"/><circle cx="3.8" cy="5" r=".7" fill="currentColor"/><circle cx="3.8" cy="9" r=".7" fill="currentColor"/><circle cx="3.8" cy="13" r=".7" fill="currentColor"/>',
|
|
128
|
-
settings: '<circle cx="9" cy="9" r="2.5"/><path d="M9 2.5v2m0 9v2M2.5 9h2m9 0h2M4.4 4.4l1.4 1.4m6.4 6.4 1.4 1.4m0-9.2-1.4 1.4M5.8 12.2l-1.4 1.4"/>',
|
|
129
125
|
deploy: '<path d="M9 12V3M5.5 6.5 9 3l3.5 3.5M3 12.5v2a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1v-2"/>',
|
|
130
126
|
money: '<path d="M4 2.5h10v13l-1.7-1-1.6 1-1.7-1-1.7 1-1.6-1-1.7 1v-13Z"/><path d="M6.5 6h5M6.5 9h5M6.5 12h3"/>',
|
|
131
|
-
permissions: '<circle cx="6" cy="12" r="3.5"/><path d="M8.5 9.5 15 3M12.5 5.5l2 2M10.5 7.5l1.5 1.5"/>',
|
|
132
127
|
// The governor city: a pediment over columns, the Governing mode's own glyph.
|
|
133
128
|
city: '<path d="M2.5 7 9 3l6.5 4M4 7.5v6.5M7 7.5v6.5M11 7.5v6.5M14 7.5v6.5M2.5 15.5h13"/>',
|
|
134
129
|
// the foot
|
|
@@ -302,9 +297,9 @@
|
|
|
302
297
|
// /profile?id=N from the leaderboard); 'signed' just means "show this 'my
|
|
303
298
|
// profile' shortcut once you're signed in".
|
|
304
299
|
{ id: 'profile', label: 'Profile', icon: 'profile', href: '/profile', gate: 'signed' },
|
|
305
|
-
// task
|
|
306
|
-
//
|
|
307
|
-
|
|
300
|
+
// task 1005144 (ADR 0401): My agents, Project settings, Permissions and Modules
|
|
301
|
+
// left the sidebar — they are entries in the ONE options menu (the profile chip),
|
|
302
|
+
// and their old URLs open it (serve-internal.js RETIRED_TO_OPTIONS).
|
|
308
303
|
] },
|
|
309
304
|
// task 1005071: the board's own rooms get a head of their own. Gated on the SAME
|
|
310
305
|
// atom as both items (perm:, which floors below any rank), so the head can never
|
|
@@ -360,9 +355,6 @@
|
|
|
360
355
|
// surface, so this group's gate and audience are unchanged.
|
|
361
356
|
{ id: 'watch', label: 'Watch', icon: 'watch', href: '/watch', gate: 'government', position: true },
|
|
362
357
|
// task 1005049: Sessions and Autobongos (/gate) are tabs on Watch; both URLs redirect there.
|
|
363
|
-
// The project's own fundamentals (task 1003280, goal 1000072). Mirrors the
|
|
364
|
-
// server's requireGovernmentPage floor; cosmetic only, as every item here is.
|
|
365
|
-
{ id: 'project-settings', label: 'Project settings', icon: 'settings', href: '/project-settings', gate: 'government', position: true },
|
|
366
358
|
// task 1005047 (owner decision 2026-11-03, idea 1001312): the owner's one money page —
|
|
367
359
|
// spend and payouts (once on Project settings), the treasury (once the Economy page)
|
|
368
360
|
// and the hosting bills (once 'Fleet costs', /fleet, which now redirects to its
|
|
@@ -379,17 +371,7 @@
|
|
|
379
371
|
// is the platform hall's page alone, and every other hall takes its update from
|
|
380
372
|
// Settings → Software update, whose Update button carries the hub hand-off.
|
|
381
373
|
{ id: 'deploy', label: 'Deploy', icon: 'deploy', href: '/deploy', gate: 'archon', module: 'provisioning render-deploy', position: true },
|
|
382
|
-
//
|
|
383
|
-
// /market-watch now, so the sidebar says "Market Watch" once (owner decision 2026-11-03).
|
|
384
|
-
// BV1.R113: the roles-and-permissions tab (ADR 0151). The one genuinely
|
|
385
|
-
// Archon-only item the 'government' gate comment anticipated — cosmetic
|
|
386
|
-
// only. The wall is the `government.manage` atom checked on each of the
|
|
387
|
-
// tab's nine data routes (route-rank-check.js); the page SHELL itself is
|
|
388
|
-
// metic+, because R16 made /government three rooms and only this one is
|
|
389
|
-
// the archon surface.
|
|
390
|
-
{ id: 'government', label: 'Permissions', icon: 'permissions', href: '/government', gate: 'archon', position: true } // NOT 'Ranks': the ladder is explained in the Primer, and a nav item
|
|
391
|
-
// reading 'Ranks' beside it is the confusion this rename exists to remove. This tab
|
|
392
|
-
// is where a rank's permissions are granted, so it says what it does.,
|
|
374
|
+
// Project settings and Permissions left this group in task 1005144: options menu entries.
|
|
393
375
|
] },
|
|
394
376
|
// task 1005071: 'Docs' → 'Help', and every craft shows it (craft-mode.js ALWAYS_TABS).
|
|
395
377
|
{ group: 'Help', gate: 'signed', items: [
|
|
@@ -409,7 +391,6 @@
|
|
|
409
391
|
// both reads behind it are sign-in gated, so 'signed' is the real floor here.
|
|
410
392
|
{ id: 'commands', label: 'Commands', icon: 'commands', href: '/commands', gate: 'signed' },
|
|
411
393
|
{ id: 'atlas', label: 'Repo Atlas', icon: 'atlas', href: '/atlas', gate: 'member' },
|
|
412
|
-
{ id: 'modules', label: 'Modules', icon: 'modules', href: '/modules', gate: 'member' },
|
|
413
394
|
// The rendered OpenAPI reference lives on the APEX (/docs), not this builders
|
|
414
395
|
// subdomain — external so the rewrite shim doesn't turn it into /builders/docs.
|
|
415
396
|
{ id: 'apidocs', label: 'API docs', icon: 'apidocs', href: '/docs', gate: 'signed', external: true },
|
|
@@ -516,7 +497,7 @@
|
|
|
516
497
|
// task 1831: the profile chip IS the Settings link — one spot, no separate
|
|
517
498
|
// nav-item. It's an <a> so keyboard focus + Enter work for free; visibility
|
|
518
499
|
// stays 'signed'-gated via applyAccess (shown only when authed + filled).
|
|
519
|
-
'<a class="shell-user" id="shell-user" href="/settings" title="
|
|
500
|
+
'<a class="shell-user" id="shell-user" href="/settings" title="Options" aria-label="Options" hidden>' +
|
|
520
501
|
'<span class="shell-user__avatar" id="shell-user-avatar" aria-hidden="true"></span>' +
|
|
521
502
|
'<span class="shell-user__meta"><span class="shell-user__name" id="shell-user-name"></span>' +
|
|
522
503
|
'<span class="shell-user__rank" id="shell-user-rank"></span></span>' +
|
|
@@ -1196,7 +1177,7 @@
|
|
|
1196
1177
|
maybeRenderCoreUpdate();
|
|
1197
1178
|
// task 1004492: a builder with no mode yet this session is asked once — unless Home's welcome (task 1004785, welcome-boot.js) greets the day's first visit instead; it marks the picker asked.
|
|
1198
1179
|
// task 1005076: neither is drawn over the setup screen; setup comes first.
|
|
1199
|
-
if (!holdForSetup(state && state.builder) && !(window.OTBWelcome && window.OTBWelcome.onAccess(access, state && state.builder)) && CRAFT) CRAFT.onAccess(access, state && state.builder);
|
|
1180
|
+
if (!holdForSetup(state && state.builder) && !(CRAFT && CRAFT.characterGate && CRAFT.characterGate(access, state)) && !(window.OTBWelcome && window.OTBWelcome.onAccess(access, state && state.builder)) && CRAFT) CRAFT.onAccess(access, state && state.builder);
|
|
1200
1181
|
var signin = document.getElementById('shell-signin');
|
|
1201
1182
|
if (signin) signin.hidden = access.authed !== false;
|
|
1202
1183
|
var user = document.getElementById('shell-user');
|
|
@@ -1443,7 +1424,7 @@
|
|
|
1443
1424
|
positions: me.positions || null,
|
|
1444
1425
|
// task 1005076: whether setup is complete; incomplete shows setup-gate.js.
|
|
1445
1426
|
setup: (me.onboarding && me.onboarding.setup) || null,
|
|
1446
|
-
builder: me.builder,
|
|
1427
|
+
builder: me.builder, character: me.character, // task 1005147: the character held (null: pick one)
|
|
1447
1428
|
});
|
|
1448
1429
|
}).catch(function () { /* cosmetic only */ });
|
|
1449
1430
|
}
|
|
@@ -147,4 +147,11 @@
|
|
|
147
147
|
].filter(([cls]) => cls !== 'unseen' || S.lenses.includes('all')).map(([cls, name, what]) => `<button type="button" class="obj" title="${esc(what)}"><span class="g"><span class="mk ${cls}"></span></span>${esc(name)}</button>`).join('');
|
|
148
148
|
|
|
149
149
|
Object.assign(SKY, { card, menu, glyphHtml, ideaTitle, sizeWords, showCard, showMenu, hideMenu, pick, setLens, togglePill, updateStats, altText, TAU, hash });
|
|
150
|
+
|
|
151
|
+
// task 1005148: the sky as the visor's scene (ADR 0399 §2.2). The visor owns no camera:
|
|
152
|
+
// "Back to centre" is the camera's own fitAll, and the keys listed are the ones bound above.
|
|
153
|
+
window.__visorScene = { name: 'sky', place: '', // the sky's own heading names the place
|
|
154
|
+
actions: [{ label: 'Back to centre', key: '0', run: () => SKY.fitAll() }],
|
|
155
|
+
keys: [{ label: 'Look around', key: 'drag' }, { label: 'List view', key: 'L' }, { label: 'Search', key: '/' }, { label: 'Take the tour', key: '?' }] };
|
|
156
|
+
if (window.OTBVisor) window.OTBVisor.scene(window.__visorScene);
|
|
150
157
|
})();
|