@bongos/core 1.24.79 → 1.24.81
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 +554 -394
- 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/0402-helm-is-the-halls-look-and-the-hall-is-dark-only.md +142 -0
- package/docs/adr/README.md +4 -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 +393 -378
- package/docs/copy-registry.json +722 -578
- 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 +16 -7
- package/docs/page-readings.json +3184 -3338
- 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 +6 -4
- package/modules/hall-ui/public/ai-studio.states.json +1 -0
- package/modules/hall-ui/public/atlas.html +5 -5
- package/modules/hall-ui/public/atlas.js +3 -1
- package/modules/hall-ui/public/atlas.states.json +1 -0
- package/modules/hall-ui/public/blockers.html +5 -5
- package/modules/hall-ui/public/board-room.html +4 -4
- package/modules/hall-ui/public/board-room.states.json +1 -0
- 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 +3 -4
- package/modules/hall-ui/public/city.html +9 -9
- package/modules/hall-ui/public/city.js +17 -56
- package/modules/hall-ui/public/city.states.json +7 -32
- package/modules/hall-ui/public/collab.html +4 -4
- package/modules/hall-ui/public/collab.states.json +1 -0
- package/modules/hall-ui/public/commands.html +4 -4
- package/modules/hall-ui/public/commands.states.json +1 -0
- package/modules/hall-ui/public/controls.css +63 -11
- package/modules/hall-ui/public/copy-desk.html +4 -4
- package/modules/hall-ui/public/copy-desk.states.json +1 -0
- package/modules/hall-ui/public/craft-mode.js +258 -64
- package/modules/hall-ui/public/deploy.html +4 -4
- package/modules/hall-ui/public/deploy.states.json +1 -0
- 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/goals.states.json +1 -0
- package/modules/hall-ui/public/government.html +4 -4
- package/modules/hall-ui/public/government.states.json +1 -0
- 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/idea.states.json +1 -0
- package/modules/hall-ui/public/ideas.html +5 -5
- package/modules/hall-ui/public/ideas.states.json +1 -0
- package/modules/hall-ui/public/index-founding.states.json +1 -0
- package/modules/hall-ui/public/index-genesis-decide.states.json +1 -0
- package/modules/hall-ui/public/index-genesis-demo.states.json +1 -0
- package/modules/hall-ui/public/index-genesis.states.json +1 -0
- package/modules/hall-ui/public/index.html +11 -7
- package/modules/hall-ui/public/index.states.json +1 -0
- package/modules/hall-ui/public/market-watch-item.html +4 -4
- package/modules/hall-ui/public/market-watch-item.states.json +1 -0
- package/modules/hall-ui/public/market-watch-rules.html +4 -4
- package/modules/hall-ui/public/market-watch-rules.states.json +1 -0
- 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/market-watch.states.json +1 -0
- package/modules/hall-ui/public/modules.html +4 -4
- package/modules/hall-ui/public/modules.states.json +1 -0
- package/modules/hall-ui/public/money.html +4 -4
- package/modules/hall-ui/public/money.states.json +1 -0
- package/modules/hall-ui/public/my-agents.html +4 -4
- package/modules/hall-ui/public/my-agents.states.json +1 -0
- 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/primer.states.json +1 -0
- 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 +5 -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/roster.states.json +1 -0
- 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/settings.states.json +1 -0
- package/modules/hall-ui/public/shell-game.css +110 -0
- package/modules/hall-ui/public/shell-game.js +316 -3
- package/modules/hall-ui/public/shell.js +15 -66
- 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-card.states.json +1 -0
- package/modules/hall-ui/public/street-inside.js +3 -4
- package/modules/hall-ui/public/street.css +1 -5
- package/modules/hall-ui/public/street.html +6 -10
- package/modules/hall-ui/public/street.js +8 -59
- package/modules/hall-ui/public/street.states.json +7 -32
- package/modules/hall-ui/public/studio.html +5 -5
- package/modules/hall-ui/public/studio.states.json +1 -0
- package/modules/hall-ui/public/style.css +197 -112
- package/modules/hall-ui/public/task-detail.js +5 -4
- package/modules/hall-ui/public/task.html +5 -5
- package/modules/hall-ui/public/task.states.json +1 -0
- package/modules/hall-ui/public/thinking.css +10 -0
- package/modules/hall-ui/public/thinking.html +9 -6
- package/modules/hall-ui/public/thinking.states.json +1 -0
- 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/tweak-editor.states.json +1 -0
- 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/watch.states.json +1 -0
- package/modules/hall-ui/public/welcome-boot.js +15 -6
- package/modules/hall-ui/public/work-founding.states.json +1 -0
- package/modules/hall-ui/public/work.html +5 -5
- package/modules/hall-ui/public/work.states.json +1 -0
- 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 +25 -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/hall-ui/specimens/components.states.json +2 -1
- 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/modules/ui-design/public/brand.html +2 -2
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +76 -0
- package/scripts/bongos/autobongos-loop.js +1 -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/hall-preview/README.md +2 -3
- 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 +33 -11
- package/src/crafts.js +66 -0
- package/src/hall-helm.js +79 -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/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 +1 -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/design_reference.mjs +1 -1
- 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 +3 -2
- package/tests/hall_city.mjs +16 -30
- package/tests/hall_controls.mjs +52 -4
- package/tests/hall_fullscreen.mjs +27 -3
- package/tests/hall_helm.mjs +187 -0
- 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 +6 -4
- package/tests/hall_settings_world.mjs +2 -1
- package/tests/hall_sitemap.mjs +6 -5
- package/tests/hall_start_screen.mjs +307 -0
- package/tests/hall_street.mjs +15 -28
- package/tests/hall_street_inside.mjs +17 -9
- package/tests/hall_studio_world.mjs +3 -3
- package/tests/hall_topbar_320.mjs +4 -2
- 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
|
@@ -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,323 @@
|
|
|
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
|
+
// The project's own logo where it has one (ADR 0358), else the shared mark, as the sidebar draws it.
|
|
95
|
+
function markHtml() {
|
|
96
|
+
var theme = (window.__BRANDING__ && window.__BRANDING__.theme) || {};
|
|
97
|
+
if (theme.logo) return '<img class="gs__mark" src="' + esc(theme.logo) + '" alt="" decoding="async">';
|
|
98
|
+
return '<svg class="gs__mark" viewBox="0 0 200 200" aria-hidden="true"><use href="/modules/ui-design/public/brand/mark.svg#mark"/></svg>';
|
|
99
|
+
}
|
|
100
|
+
// The Helm panel (task 1005192, ADR 0402): the mark and the name, the choices, one line at the foot.
|
|
101
|
+
function screenHtml(o) {
|
|
102
|
+
return '<div class="gs__art" aria-hidden="true">' + (o.art ? '<img class="gs__img" alt="" src="' + esc(o.art) + '" decoding="async">' : '') + '</div>' +
|
|
103
|
+
'<div class="gs__menu">' +
|
|
104
|
+
'<div class="gs__top">' +
|
|
105
|
+
'<div class="gs__head">' + markHtml() +
|
|
106
|
+
'<h2 class="gs__title" id="gs-title">' + esc(o.name || 'This project') + '</h2>' +
|
|
107
|
+
'</div>' +
|
|
108
|
+
'<div class="gs__list">' +
|
|
109
|
+
'<ul class="gs__items">' + MENU.map(itemHtml).join('') + '</ul>' +
|
|
110
|
+
'<ul class="gs__items gs__items--extras" id="gs-extras" hidden>' + EXTRAS.map(function (x) {
|
|
111
|
+
return '<li><a class="gs__item gs__item--extra" href="' + esc(x.href) + '">' + esc(x.label) + '</a></li>';
|
|
112
|
+
}).join('') + '</ul>' +
|
|
113
|
+
'</div>' +
|
|
114
|
+
'</div>' +
|
|
115
|
+
'<div class="gs__foot">' +
|
|
116
|
+
'<p class="gs__line" id="gs-line">' + esc(tipAt(read('localStorage', TIP_KEY))) + '</p>' +
|
|
117
|
+
'<p class="gs__note" id="gs-note" role="status" hidden></p>' +
|
|
118
|
+
'</div>' +
|
|
119
|
+
'</div>' +
|
|
120
|
+
'<div class="gs__load" id="gs-load" role="status" hidden>' +
|
|
121
|
+
'<p class="gs__when" id="gs-when" hidden></p>' +
|
|
122
|
+
'<ol class="gs__segs segs" id="gs-segs" aria-hidden="true" hidden></ol>' +
|
|
123
|
+
'<svg class="gs__city" id="gs-city" role="img" aria-label="The project city building up, week by week" hidden></svg>' +
|
|
124
|
+
'<p class="gs__tip"><b>Tip</b><span id="gs-tip"></span></p>' +
|
|
125
|
+
'</div>';
|
|
126
|
+
}
|
|
127
|
+
// One segment per week of the history: filled up to the week being shown. The words beside
|
|
128
|
+
// them (#gs-when) say the same thing, so the row is hidden from a screen reader.
|
|
129
|
+
// Counted the way whenLabel words it, so the row and the sentence never disagree.
|
|
130
|
+
function segsDone(weeks, t) {
|
|
131
|
+
var n = Math.max(0, Math.round(Number(weeks) || 0));
|
|
132
|
+
return Math.max(0, n - Math.round(Math.max(0, n - 1 - (Number(t) || 0))));
|
|
133
|
+
}
|
|
134
|
+
function drawSegs(weeks, t) {
|
|
135
|
+
var box = root && root.querySelector('#gs-segs'), n = Math.max(0, Math.round(Number(weeks) || 0));
|
|
136
|
+
if (!box || n < 2) return;
|
|
137
|
+
while (box.children.length < n) box.appendChild(document.createElement('li'));
|
|
138
|
+
var done = segsDone(n, t);
|
|
139
|
+
for (var i = 0; i < box.children.length; i++) box.children[i].className = i < done ? 'seg seg--done' : 'seg';
|
|
140
|
+
box.hidden = false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
var root = null, opts = null, timers = [], phase = null;
|
|
144
|
+
function later(ms, fn) { timers.push(setTimeout(fn, ms)); }
|
|
145
|
+
function stopTimers() { timers.forEach(clearTimeout); timers = []; }
|
|
146
|
+
function still() { return !!(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches); }
|
|
147
|
+
function items() {
|
|
148
|
+
return Array.prototype.filter.call(root.querySelectorAll('.gs__item'), function (b) { return !b.closest('[hidden]'); });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function close() {
|
|
152
|
+
if (!root) return;
|
|
153
|
+
stopTimers();
|
|
154
|
+
document.removeEventListener('keydown', onKey, true);
|
|
155
|
+
if (root.parentNode) root.parentNode.removeChild(root);
|
|
156
|
+
root = null; phase = null;
|
|
157
|
+
document.documentElement.classList.remove('gs-open');
|
|
158
|
+
var done = opts && opts.done;
|
|
159
|
+
opts = null;
|
|
160
|
+
if (typeof done === 'function') done();
|
|
161
|
+
}
|
|
162
|
+
function leave() {
|
|
163
|
+
if (!root || phase === 'leaving') return;
|
|
164
|
+
phase = 'leaving';
|
|
165
|
+
stopTimers();
|
|
166
|
+
if (still()) { close(); return; }
|
|
167
|
+
root.classList.add('gs--leaving');
|
|
168
|
+
later(LEAVE_MS, close);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ---- the load screen: the still, a tip, and the city building up through its history ----
|
|
172
|
+
function build(n) {
|
|
173
|
+
var e = document.createElementNS(NS, n.tag);
|
|
174
|
+
Object.keys(n.attrs).forEach(function (k) { e.setAttribute(k, String(n.attrs[k])); });
|
|
175
|
+
if (n.text != null) e.textContent = n.text;
|
|
176
|
+
n.kids.forEach(function (c) { e.appendChild(build(c)); });
|
|
177
|
+
return e;
|
|
178
|
+
}
|
|
179
|
+
function themeMode() {
|
|
180
|
+
var t = document.documentElement.dataset.theme;
|
|
181
|
+
if (t === 'light' || t === 'dark') return t;
|
|
182
|
+
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
183
|
+
}
|
|
184
|
+
function drawCity(model, t) {
|
|
185
|
+
var Draw = window.OTBHomeCityDraw, svg = root && root.querySelector('#gs-city'), when = root && root.querySelector('#gs-when');
|
|
186
|
+
if (!svg) return;
|
|
187
|
+
var out = Draw.citySvg(model, t, { mode: themeMode(), gov: window.GovCity || null });
|
|
188
|
+
svg.setAttribute('viewBox', out.viewBox);
|
|
189
|
+
while (svg.firstChild) svg.removeChild(svg.firstChild);
|
|
190
|
+
out.nodes.forEach(function (n) { svg.appendChild(build(n)); });
|
|
191
|
+
svg.removeAttribute('hidden');
|
|
192
|
+
when.textContent = whenLabel(model.W, t);
|
|
193
|
+
when.hidden = false;
|
|
194
|
+
drawSegs(model.W, t);
|
|
195
|
+
}
|
|
196
|
+
// One week after another, as state: each stop is a whole drawing, so nothing here animates.
|
|
197
|
+
function runHistory(model) {
|
|
198
|
+
if (!root || phase !== 'load') return;
|
|
199
|
+
stopTimers();
|
|
200
|
+
var stops = still() ? [model.W - 1] : historySteps(model.W, STEPS), i = 0;
|
|
201
|
+
(function step() {
|
|
202
|
+
if (!root) return;
|
|
203
|
+
drawCity(model, stops[i]);
|
|
204
|
+
i += 1;
|
|
205
|
+
if (i < stops.length) later(STEP_MS, step); else later(still() ? HOLD_MS : 700, leave);
|
|
206
|
+
})();
|
|
207
|
+
}
|
|
208
|
+
function readCity() {
|
|
209
|
+
var Draw = window.OTBHomeCityDraw;
|
|
210
|
+
if (!Draw || typeof fetch !== 'function') return;
|
|
211
|
+
fetch(CITY_READ, { credentials: 'same-origin' }).then(function (r) { return r.ok ? r.json() : null; }).then(function (data) {
|
|
212
|
+
var model = data && Draw.cityModel(data);
|
|
213
|
+
if (model && model.W && model.areas.length) runHistory(model);
|
|
214
|
+
}, function () { /* no city: the still and the tip stand, then the world */ });
|
|
215
|
+
}
|
|
216
|
+
function load() {
|
|
217
|
+
if (!root || phase !== 'menu') return;
|
|
218
|
+
phase = 'load';
|
|
219
|
+
set(true);
|
|
220
|
+
var n = Number(read('localStorage', TIP_KEY)) || 0;
|
|
221
|
+
write('localStorage', TIP_KEY, String(n + 1));
|
|
222
|
+
root.querySelector('#gs-tip').textContent = tipAt(n);
|
|
223
|
+
root.querySelector('.gs__menu').hidden = true;
|
|
224
|
+
root.querySelector('#gs-load').hidden = false;
|
|
225
|
+
root.classList.add('gs--load');
|
|
226
|
+
later(HOLD_MS, leave); // runHistory replaces this once the city answers
|
|
227
|
+
readCity();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Quit closes the tab. A browser only lets a script close a tab a script opened, so when the
|
|
231
|
+
// tab is still here a moment later the screen says so instead of doing nothing.
|
|
232
|
+
function quit() {
|
|
233
|
+
try { window.close(); } catch (_) { /* refused */ }
|
|
234
|
+
later(300, function () {
|
|
235
|
+
var note = root && root.querySelector('#gs-note');
|
|
236
|
+
if (!note) return;
|
|
237
|
+
note.textContent = 'Your browser kept this tab open. Close it to quit.';
|
|
238
|
+
note.hidden = false;
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
function toggleExtras(btn) {
|
|
242
|
+
var box = root.querySelector('#gs-extras'), open = box.hidden;
|
|
243
|
+
box.hidden = !open;
|
|
244
|
+
btn.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
245
|
+
if (open) box.querySelector('a').focus();
|
|
246
|
+
}
|
|
247
|
+
function onClick(e) {
|
|
248
|
+
var el = e.target.closest && e.target.closest('.gs__item');
|
|
249
|
+
if (!el || phase !== 'menu') return;
|
|
250
|
+
var id = el.getAttribute('data-gs');
|
|
251
|
+
if (id === 'start') load();
|
|
252
|
+
else if (id === 'extras') toggleExtras(el);
|
|
253
|
+
else if (id === 'quit') quit();
|
|
254
|
+
}
|
|
255
|
+
// The screen keeps every key while it is up: Escape folds Extras away and nothing else (the
|
|
256
|
+
// shell's Escape must not flip the bars underneath), the arrows and Tab stay inside the menu.
|
|
257
|
+
function onKey(e) {
|
|
258
|
+
if (!root) return;
|
|
259
|
+
if (e.key === 'Escape') {
|
|
260
|
+
e.preventDefault();
|
|
261
|
+
var box = root.querySelector('#gs-extras');
|
|
262
|
+
if (phase === 'menu' && !box.hidden) { var b = root.querySelector('[data-gs="extras"]'); toggleExtras(b); b.focus(); }
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (phase !== 'menu') return;
|
|
266
|
+
var f = items(), at = f.indexOf(document.activeElement), step = 0;
|
|
267
|
+
if (e.key === 'ArrowDown') step = 1;
|
|
268
|
+
else if (e.key === 'ArrowUp') step = -1;
|
|
269
|
+
else if (e.key === 'Tab') step = e.shiftKey ? -1 : 1;
|
|
270
|
+
if (!step || !f.length) return;
|
|
271
|
+
e.preventDefault();
|
|
272
|
+
f[(at + step + f.length) % f.length].focus();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Put the start screen up. `o`: { name, art, done } — the project's name, the still to stand
|
|
276
|
+
// behind both screens, and what to do once the world is showing. True when it is up.
|
|
277
|
+
function start(o) {
|
|
278
|
+
if (root || !document.body) return !!root;
|
|
279
|
+
opts = o || {};
|
|
280
|
+
phase = 'menu';
|
|
281
|
+
root = document.createElement('div');
|
|
282
|
+
root.className = 'gs';
|
|
283
|
+
root.id = 'game-start';
|
|
284
|
+
root.setAttribute('role', 'dialog');
|
|
285
|
+
root.setAttribute('aria-modal', 'true');
|
|
286
|
+
root.setAttribute('aria-labelledby', 'gs-title');
|
|
287
|
+
root.innerHTML = screenHtml(opts);
|
|
288
|
+
root.addEventListener('click', onClick);
|
|
289
|
+
document.addEventListener('keydown', onKey, true);
|
|
290
|
+
document.body.appendChild(root);
|
|
291
|
+
document.documentElement.classList.add('gs-open');
|
|
292
|
+
var first = root.querySelector('.gs__item');
|
|
293
|
+
if (first && first.focus) first.focus();
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
|
|
44
297
|
try {
|
|
45
298
|
var asked = new URLSearchParams(window.location.search || '').get('game');
|
|
46
299
|
if (asked === 'on' || asked === 'off') write('localStorage', KEY, asked);
|
|
47
300
|
} catch (_) { /* old engine: the stored value stands */ }
|
|
48
301
|
stamp();
|
|
49
|
-
|
|
302
|
+
|
|
303
|
+
// ---------- the options menu (task 1005144, BONGOS-V3 R04, ADR 0401) ----------
|
|
304
|
+
// Every setting lives in ONE menu, the Settings page (options-menu.js). A screen
|
|
305
|
+
// that used to have its own page (My agents, Project settings, Modules, …) now
|
|
306
|
+
// opens INSIDE that menu, in a frame, with `?embed=1`. Such a page is EMBEDDED:
|
|
307
|
+
// html[data-embed="on"], no sidebar, top bar, foot or page head (style.css), no
|
|
308
|
+
// full-screen switch, a link to another page opens in the whole window, and an
|
|
309
|
+
// Escape nobody used goes to the menu. Only inside a frame: the same URL opened
|
|
310
|
+
// on its own is the page with its chrome (the server sends a plain visit of the
|
|
311
|
+
// old address to the menu, serve-internal.js).
|
|
312
|
+
var embedded = false;
|
|
313
|
+
try {
|
|
314
|
+
embedded = new URLSearchParams(window.location.search || '').get('embed') === '1'
|
|
315
|
+
&& !!window.parent && window.parent !== window;
|
|
316
|
+
} catch (_) { embedded = false; }
|
|
317
|
+
if (embedded) {
|
|
318
|
+
document.documentElement.dataset.embed = 'on';
|
|
319
|
+
// The shell reads this before it draws the switch, so no button, no Escape-to-leave.
|
|
320
|
+
if (document.body && document.body.setAttribute) document.body.setAttribute('data-fullscreen-switch', 'off');
|
|
321
|
+
}
|
|
322
|
+
// The hall's address for the menu: '/settings' on the builders host, '/builders/settings' on the apex.
|
|
323
|
+
function menuPath() {
|
|
324
|
+
var p = String((window.location && window.location.pathname) || '');
|
|
325
|
+
return (p.indexOf('/builders/') === 0 || p === '/builders' ? '/builders' : '') + '/settings';
|
|
326
|
+
}
|
|
327
|
+
function inField(el) {
|
|
328
|
+
var tag = el && el.tagName ? String(el.tagName).toLowerCase() : '';
|
|
329
|
+
return tag === 'input' || tag === 'textarea' || tag === 'select' || !!(el && el.isContentEditable);
|
|
330
|
+
}
|
|
331
|
+
// ADR 0399 §2.4 row 6: in game mode, the last Escape — one no dialog, picker,
|
|
332
|
+
// palette, sheet or scene pop-up used — is the options menu's. It is judged after
|
|
333
|
+
// the dispatch (a page listener may sit later on the window), and it is marked
|
|
334
|
+
// used before the shell's own check runs, so the bars do not flash back first.
|
|
335
|
+
// On the menu itself it closes the menu; inside a frame it is the menu's too.
|
|
336
|
+
function onEscape(e) {
|
|
337
|
+
if (!e || e.key !== 'Escape' || e.defaultPrevented || inField(e.target)) return;
|
|
338
|
+
if (!embedded && !on()) return;
|
|
339
|
+
window.setTimeout(function () {
|
|
340
|
+
if (e.defaultPrevented) return;
|
|
341
|
+
try { e.preventDefault(); } catch (_) { /* already finished */ }
|
|
342
|
+
var host = embedded ? window.parent : window;
|
|
343
|
+
if (host && host.OTBOptions && typeof host.OTBOptions.escape === 'function') { host.OTBOptions.escape(); return; }
|
|
344
|
+
if (embedded) return;
|
|
345
|
+
var here = String(window.location.pathname || '') + String(window.location.search || '') + String(window.location.hash || '');
|
|
346
|
+
window.location.assign(menuPath() + '?from=' + encodeURIComponent(here));
|
|
347
|
+
}, 0);
|
|
348
|
+
}
|
|
349
|
+
if (window.addEventListener) window.addEventListener('keydown', onEscape);
|
|
350
|
+
// Inside the menu, a link to ANOTHER page leaves the menu for it; a link within the page stays.
|
|
351
|
+
if (embedded && document.addEventListener) {
|
|
352
|
+
document.addEventListener('click', function (e) {
|
|
353
|
+
var a = e.target && e.target.closest ? e.target.closest('a[href]') : null;
|
|
354
|
+
if (!a || a.getAttribute('target')) return;
|
|
355
|
+
try {
|
|
356
|
+
var to = new URL(a.getAttribute('href'), window.location.href);
|
|
357
|
+
if (to.origin !== window.location.origin || to.pathname !== window.location.pathname) a.setAttribute('target', '_top');
|
|
358
|
+
} catch (_) { /* a href the browser cannot read: leave it alone */ }
|
|
359
|
+
}, true);
|
|
360
|
+
}
|
|
361
|
+
window.OTBShellGame = { on: on, set: set, hint: hint, start: start, tipAt: tipAt, historySteps: historySteps, whenLabel: whenLabel, segsDone: segsDone, MENU: MENU, TIPS: TIPS,
|
|
362
|
+
embedded: function () { return embedded; }, menuPath: menuPath };
|
|
50
363
|
})();
|
|
@@ -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>' +
|
|
@@ -557,7 +538,6 @@
|
|
|
557
538
|
'</button>' +
|
|
558
539
|
(CRAFT ? CRAFT.switchHtml() : '') +
|
|
559
540
|
'<button class="zoom-toggle" id="zoom-toggle" type="button" aria-label="Text size"></button>' +
|
|
560
|
-
'<button class="theme-toggle" id="theme-toggle" type="button" aria-label="Switch theme"></button>' +
|
|
561
541
|
// task 1004327: the full-screen switch, LAST so it sits in the corner the
|
|
562
542
|
// collapsed bar keeps in full screen — the one control that stays put.
|
|
563
543
|
// task 1004430: on every page now, unless the page opts out.
|
|
@@ -665,44 +645,21 @@
|
|
|
665
645
|
window.setTimeout(function () { if (!e.defaultPrevented && isFullscreen()) setFullscreen(false); }, 0);
|
|
666
646
|
}
|
|
667
647
|
|
|
668
|
-
// ---------- theme (
|
|
669
|
-
//
|
|
670
|
-
//
|
|
671
|
-
//
|
|
672
|
-
//
|
|
673
|
-
|
|
674
|
-
var themeMedia = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
|
|
675
|
-
|
|
676
|
-
function storedTheme() {
|
|
677
|
-
try { var v = window.localStorage.getItem(THEME_KEY); return v === 'dark' || v === 'light' ? v : null; } catch (_) { return null; }
|
|
678
|
-
}
|
|
679
|
-
function effectiveTheme() {
|
|
680
|
-
return storedTheme() || (themeMedia && themeMedia.matches ? 'dark' : 'light');
|
|
681
|
-
}
|
|
648
|
+
// ---------- theme: the hall is dark only (task 1005192, ADR 0402) ----------
|
|
649
|
+
// Helm is the hall's one look, so there is no light/dark switch and no stored
|
|
650
|
+
// choice: the server stamps html[data-theme="dark"] on every hall page
|
|
651
|
+
// (src/hall-helm.js) and this sets it again, for a page read without that stamp.
|
|
652
|
+
// The old 'hall-theme' preference is never read. Scripts that only READ the
|
|
653
|
+
// attribute (the city, the sky, the atlas) keep working: they always read dark.
|
|
682
654
|
function applyTheme() {
|
|
683
|
-
|
|
684
|
-
document.documentElement.dataset.theme = t;
|
|
685
|
-
var btn = document.getElementById('theme-toggle');
|
|
686
|
-
if (btn) {
|
|
687
|
-
var toDark = t !== 'dark';
|
|
688
|
-
btn.setAttribute('aria-label', toDark ? 'Switch to dark theme' : 'Switch to light theme');
|
|
689
|
-
btn.title = btn.getAttribute('aria-label');
|
|
690
|
-
btn.innerHTML = toDark
|
|
691
|
-
? '<svg viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M14.5 10.8A6 6 0 0 1 7.2 3.5a6 6 0 1 0 7.3 7.3Z"/></svg>'
|
|
692
|
-
: '<svg viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="9" cy="9" r="3.5"/><path d="M9 1.5v2m0 11v2M1.5 9h2m11 0h2M3.7 3.7l1.4 1.4m7.8 7.8 1.4 1.4m0-10.6-1.4 1.4M5.1 12.9l-1.4 1.4"/></svg>';
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
function toggleTheme() {
|
|
696
|
-
var next = effectiveTheme() === 'dark' ? 'light' : 'dark';
|
|
697
|
-
try { window.localStorage.setItem(THEME_KEY, next); } catch (_) { /* private mode */ }
|
|
698
|
-
applyTheme();
|
|
655
|
+
document.documentElement.dataset.theme = 'dark';
|
|
699
656
|
}
|
|
700
657
|
|
|
701
658
|
// ---------- zoom (accessibility: enlarge the whole page) ----------
|
|
702
659
|
// task 1475 (idea 280): a one-click "make everything bigger" control for
|
|
703
660
|
// readability. Cycles 100% -> 125% -> 150% -> 100% via CSS `zoom` on <html>,
|
|
704
661
|
// which enlarges px AND rem sizes uniformly (a font-size bump would miss the
|
|
705
|
-
// hall's many px values). Persisted per-browser
|
|
662
|
+
// hall's many px values). Persisted per-browser.
|
|
706
663
|
var ZOOM_KEY = 'hall-zoom';
|
|
707
664
|
var ZOOM_STEPS = [1, 1.25, 1.5];
|
|
708
665
|
function storedZoom() {
|
|
@@ -1196,7 +1153,7 @@
|
|
|
1196
1153
|
maybeRenderCoreUpdate();
|
|
1197
1154
|
// 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
1155
|
// 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);
|
|
1156
|
+
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
1157
|
var signin = document.getElementById('shell-signin');
|
|
1201
1158
|
if (signin) signin.hidden = access.authed !== false;
|
|
1202
1159
|
var user = document.getElementById('shell-user');
|
|
@@ -1345,8 +1302,6 @@
|
|
|
1345
1302
|
if (document.documentElement.classList.contains('drawer-open')) closeDrawer();
|
|
1346
1303
|
else openDrawer();
|
|
1347
1304
|
});
|
|
1348
|
-
var theme = document.getElementById('theme-toggle');
|
|
1349
|
-
if (theme) theme.addEventListener('click', toggleTheme);
|
|
1350
1305
|
var zoom = document.getElementById('zoom-toggle');
|
|
1351
1306
|
if (zoom) zoom.addEventListener('click', cycleZoom);
|
|
1352
1307
|
var full = document.getElementById('fullscreen-toggle');
|
|
@@ -1354,9 +1309,6 @@
|
|
|
1354
1309
|
full.addEventListener('click', function () { setFullscreen(!isFullscreen()); });
|
|
1355
1310
|
window.addEventListener('keydown', onFullscreenKey);
|
|
1356
1311
|
}
|
|
1357
|
-
if (themeMedia && themeMedia.addEventListener) {
|
|
1358
|
-
themeMedia.addEventListener('change', function () { if (!storedTheme()) applyTheme(); });
|
|
1359
|
-
}
|
|
1360
1312
|
var scrim = document.getElementById('app-scrim');
|
|
1361
1313
|
if (scrim) scrim.addEventListener('click', closeDrawer);
|
|
1362
1314
|
var sidebar = document.getElementById('app-sidebar');
|
|
@@ -1443,7 +1395,7 @@
|
|
|
1443
1395
|
positions: me.positions || null,
|
|
1444
1396
|
// task 1005076: whether setup is complete; incomplete shows setup-gate.js.
|
|
1445
1397
|
setup: (me.onboarding && me.onboarding.setup) || null,
|
|
1446
|
-
builder: me.builder,
|
|
1398
|
+
builder: me.builder, character: me.character, // task 1005147: the character held (null: pick one)
|
|
1447
1399
|
});
|
|
1448
1400
|
}).catch(function () { /* cosmetic only */ });
|
|
1449
1401
|
}
|
|
@@ -1488,9 +1440,6 @@
|
|
|
1488
1440
|
closeDrawer: closeDrawer,
|
|
1489
1441
|
isFullscreen: isFullscreen,
|
|
1490
1442
|
setFullscreen: setFullscreen,
|
|
1491
|
-
// task 1004544: a page with its own light/dark switch (the governor city
|
|
1492
|
-
// and street) repaints the top bar's after writing 'hall-theme'.
|
|
1493
|
-
applyTheme: applyTheme,
|
|
1494
1443
|
// task 1004477: a page that renders an exit after boot asks the shell to fill it.
|
|
1495
1444
|
fillExits: TRAIL.fillExits,
|
|
1496
1445
|
positions: function () { return access.positions; }, // task 1004713: seats held (null while unknown)
|
|
@@ -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
|
})();
|
|
@@ -621,8 +621,7 @@
|
|
|
621
621
|
<span>Role it sits under</span>
|
|
622
622
|
<select name="discipline" required>
|
|
623
623
|
<option value="">Pick one…</option>
|
|
624
|
-
<option value="
|
|
625
|
-
<option value="ideator">Ideator</option>
|
|
624
|
+
<option value="artisan">Artisan</option>
|
|
626
625
|
<option value="artist">Artist</option>
|
|
627
626
|
<option value="ui">UI design</option>
|
|
628
627
|
</select>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_": "THE MATTER CARD THAT ACTS (task 1004528, goal 1000125) on the street page, through modules/ui-design/kit/render.js. Kept beside street.states.json rather than inside it, so the street and its interiors (task 1004526) and the card each own their states. The populated card needs the hall-preview harness, whose government__docket.json puts the review-seat amendment on the board source (board:47) and whose government__board__items.json carries its ballot (item 47), with a canned answer for a vote:\n\n node scripts/hall-preview/server.js --port 4625 --fixture-me\n node modules/ui-design/kit/render.js --states modules/hall-ui/public/street-card.states.json --base-in http://localhost:4625 --base-out http://localhost:4625\n\nTask 1004566 adds a card per kind the docket carries (blocker, red-team report, core update, module re-check, a Full Idea and the review queue), each compact and with \"more\" open; the harness fixture carries them. Against the kit stub there is no docket and no pin, so these states are for the harness.",
|
|
3
3
|
"page": "/builders/street",
|
|
4
|
+
"modes": ["dark"],
|
|
4
5
|
"_storage": "The hall asks a first-visit question (the session mode picker, craft-mode.js) over every page; seeding \"already asked\" renders the page a returning builder sees (task 1005080).",
|
|
5
6
|
"storage": { "session": { "hall-craft-asked": "1" } },
|
|
6
7
|
"surface": "hall-ui",
|
|
@@ -81,7 +81,6 @@
|
|
|
81
81
|
let top = h;
|
|
82
82
|
if (craft === 'artist') { const bh = Math.round(h * 0.8); S.box(out, T, x, 0, w, bh, T.work, { d: 6, win: true, lit: lit }); S.sawtooth(out, x, -bh, w, 3, 12, T.roof, T.glass); top = bh + 12; }
|
|
83
83
|
else if (craft === 'ui') { S.box(out, T, x, 0, w, h, T.glass, { d: 6, grid: 3 }); S.box(out, T, x - 2, 0, w + 4, 4, T.roof, { d: 6, z: h }); top = h + 4; }
|
|
84
|
-
else if (craft === 'ideator') { S.box(out, T, x, 0, w, 22, T.work, { d: 6, door: true }); S.gable(out, x, -22, w, 14, T.roof); top = 36; }
|
|
85
84
|
else S.box(out, T, x, 0, w, h, T.work, { d: 6, win: true, lit: lit });
|
|
86
85
|
if (!lit) {
|
|
87
86
|
const fx = x + w / 2;
|
|
@@ -410,7 +409,7 @@
|
|
|
410
409
|
stageEdge: p.draw === 'awaiting a tester' ? 'rgba(251,149,105,0.6)' : T.ruleStrong,
|
|
411
410
|
})),
|
|
412
411
|
legend: [
|
|
413
|
-
{ key: 'a', text: 'Shape is the craft: tower for
|
|
412
|
+
{ key: 'a', text: 'Shape is the craft: tower for artisan, studio roof for artist, glass front for UI.' },
|
|
414
413
|
{ key: 'b', text: 'Stage is the status: staked plot, slab, scaffold, built, then lit once shipped. A flag means it waits on a check.' },
|
|
415
414
|
],
|
|
416
415
|
};
|
|
@@ -670,7 +669,7 @@
|
|
|
670
669
|
}
|
|
671
670
|
|
|
672
671
|
// ---------- the real districts ----------
|
|
673
|
-
const CRAFTS = Object.freeze(['
|
|
672
|
+
const CRAFTS = Object.freeze(['artisan', 'artist', 'ui']);
|
|
674
673
|
// A task's status as the prototype's construction stage: [drawing stage, pill words].
|
|
675
674
|
const STAGES = Object.freeze({
|
|
676
675
|
shipped: ['shipped', 'shipped'], confirmed: ['graded · landing', 'graded · landing'], completed: ['being graded', 'being graded'],
|
|
@@ -699,7 +698,7 @@
|
|
|
699
698
|
const shown = mine.slice().sort((a, b) => rank(a) - rank(b) || recent(a) - recent(b) || Number(a.id) - Number(b.id)).slice(0, MAX_ITEMS)
|
|
700
699
|
.sort((a, b) => PIPELINE.indexOf(a.status) - PIPELINE.indexOf(b.status) || Number(a.id) - Number(b.id));
|
|
701
700
|
const items = shown.map((t) => {
|
|
702
|
-
const craft = CRAFTS.indexOf(t.discipline) >= 0 ? t.discipline : '
|
|
701
|
+
const craft = CRAFTS.indexOf(t.discipline) >= 0 ? t.discipline : 'artisan';
|
|
703
702
|
const st = STAGES[t.status];
|
|
704
703
|
const built = ['ready', 'backlog'].indexOf(st[0]) < 0;
|
|
705
704
|
return [String(t.title || 'task ' + t.id), String(t.kind || 'task'), craft, st[0], built ? taskHeight(t) : 0, st[1], '/builders/task/' + encodeURIComponent(t.id)];
|
|
@@ -128,8 +128,6 @@ html[data-fullscreen="on"] .street-top { padding-right: 184px; }
|
|
|
128
128
|
.street-hud__dot--hot { background: var(--accent); }
|
|
129
129
|
.street-hud__dot--moving { width: 6px; height: 6px; border: 0; background: var(--ink); }
|
|
130
130
|
.street-hud__n { font-size: 24px; font-weight: 300; font-variant-numeric: tabular-nums; }
|
|
131
|
-
.street-mode { margin-left: 36px; flex: none; background: none; border: 1px solid var(--street-rule-strong); border-radius: 999px; height: 36px; padding: 0 14px; display: flex; align-items: center; gap: 8px; cursor: pointer; color: var(--ink); font-family: var(--font-body); font-size: 13px; text-transform: lowercase; }
|
|
132
|
-
.street-mode svg { display: block; stroke-width: 1.4; }
|
|
133
131
|
|
|
134
132
|
/* the street band: the prototype's 1440-wide artboard, scaled by street.js */
|
|
135
133
|
/* The living street's parallax (task 1004685) is Keith Clark's, sideways and
|
|
@@ -309,20 +307,18 @@ html[data-fullscreen="on"] .street-top { padding-right: 184px; }
|
|
|
309
307
|
.street-card__lines li { padding: 8px 0; border-top: 1px solid var(--street-rule); font-size: 14px; line-height: 1.4; }
|
|
310
308
|
.street-card__result { margin: 0; font-size: 15px; line-height: 1.45; }
|
|
311
309
|
|
|
312
|
-
.street-mark:focus-visible, .street-
|
|
310
|
+
.street-mark:focus-visible, .street-pin:focus-visible, .street-door:focus-visible, .street-sign:focus-visible, .street-card__close:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
|
|
313
311
|
|
|
314
312
|
/* narrower: the strip tightens, then wraps; the readings scroll sideways */
|
|
315
313
|
@media (max-width: 1180px) {
|
|
316
314
|
.street-hud { gap: 20px; }
|
|
317
315
|
.street-hud__n { font-size: 20px; }
|
|
318
|
-
.street-mode { margin-left: 20px; }
|
|
319
316
|
}
|
|
320
317
|
/* narrow: two rows inside the 80 px top-bar floor, the wordmark and the switch
|
|
321
318
|
on the first, the three figures on the second as their own sideways strip */
|
|
322
319
|
@media (max-width: 900px) {
|
|
323
320
|
.street-top { height: auto; max-height: 80px; display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "brand mode" "hud hud"; row-gap: 2px; padding: 5px 16px; }
|
|
324
321
|
.street-top__brand { grid-area: brand; }
|
|
325
|
-
.street-mode { grid-area: mode; margin-left: 0; height: 34px; }
|
|
326
322
|
.street-hud { grid-area: hud; margin-left: 0; gap: 18px; min-width: 0; overflow-x: auto; overflow-y: hidden; white-space: nowrap; height: 30px; }
|
|
327
323
|
.street-hud__item { gap: 6px; flex: none; }
|
|
328
324
|
.street-hud .street-label { font-size: 11px; }
|