Repository navigation
Open Perpetuum wiki (static site, Zola) - #1
Merged
Merged
Conversation
- Features: 17 gameplay system pages + client UI guide - Content: items catalog (4,514 item pages), ores, plants, deployables, robots, extensions, tech tree, missions, recipes, shop (per-vendor + per-item 'Where to buy') - Zones: generation mechanics, zone index, 3 worked examples, zone map (all 82 zones on the x/y grid with 171 TP connections) - Formats: zone file + field reference pages - Generator output committed (regenerated by Perpetuum.WikiGenerate) - Templates (Zola), sci-fi dark/light theme, search index, mermaid
- Move the content generator (Perpetuum.WikiGenerate) into generator/ - Makefile: build/serve run Zola in the pinned Docker container (ghcr.io/getzola/zola:v0.23.6); generate runs the C# generator (WIKI_DB / WIKI_PLANTRULES / WIKI_GBF, gbf default ../Perpetuum.gbf) - README: build + regeneration instructions - GitHub Pages workflow for this repo (triggers on main) - gitignore: build output, generator bin/obj Verified: make build and make generate from this layout reproduce the committed pages byte-identically.
search.js loads from <head>, so its init ran before the body existed and silently bailed (no input listener ever attached). Wait for DOMContentLoaded when the document is still loading. Enter now navigates to the top result (the input is not in a form, so previously it was a no-op). Verified with jsdom on a deep page and the home page: typing shows the result dropdown, Enter navigates, Escape clears.
Sources: perpetuum.miraheze.org (fetched 2026-09-25; all 149 wikitext pages kept as provenance in official-backup/, noted in the developer comments of every adapted page). New feature pages: - faq (tutorial, faction choice), abbreviations (EP/NIC/CT/TAP/...), npcs (ranks + faction names, damage weaknesses, spawn types, TAPs, tactics, open-PvP hunting perks), sparks, exploration (artifacts/relics), outposts (stability, the four SAP types, decay, relics/guardians/invaders), lore (Nia, the Nians, the Syndicate), client-setup (Linux, multi-boxing rules, UI scaling), modules (families + fitting rules of thumb) Enriched existing pages: - combat (target lock, optimal/falloff, surface size, damage types, detection/interference/speed/demobilization) - getting-started (tutorial, survival rules), groups (corp creation, CEO takeover, logo editor), production (item lifecycle: decoders -> reverse engineering -> mill -> prototype), missions (relations -> facility ratio, mission flavors, private transport collateral), gathering (solid vs liquid ore behavior), robots (community class-name crosswalk: light robots=runners, assault robots=crawlers, heavy gliders=walkers, destroyers=starter & special) Terminology: rewritten to this wiki's terms (CPRG, protected/open-PvP zones, current client zone names). Skipped: the source's Cargo stat tables (our generator is current by construction), per-robot bios, WIP/unverified formulas, retail-client-only guides. Home page and features index updated with the new pages. (Home-page video cards land in the follow-up UI commit with their CSS.)
- Branding: OPP project logo (from the community wiki) as the favicon (32px) and the brand icon left of the title (150px) - Accent palettes: swatch button next to the theme toggle offering cyan (default) / violet / phosphor / amber; each palette tints the whole site in both dark and light modes (neon accents, body glows, grid overlay, background families, table-header bands); persisted in localStorage, applied pre-paint - Search keyboard: Ctrl+F / Cmd+F focuses the wiki search bar, Esc unfocuses + clears; placeholder hints at the shortcut - Home page: click-to-play video grid (official OPP YouTube videos) above 'New here' - auto-fit grid, several per row on wide windows, iframes only loaded on demand - Footer: GitHub + X @OpenPerpetuum icon links, and attribution for the logo and adapted community-wiki content
- 34 robot model icons from the OPP community wiki in static/img/robots/ (Scarab's icon is missing on their CDN; all others resolved) - RobotsPage emits the icon per model (display-name -> icon map) so it survives regeneration - Footer attribution: logo, robot icons and adapted content credited to the Open Perpetuum community wiki
- Content background is now a 2-stop top->bottom gradient with per-palette --bg-top/--bg-bottom (dark + light) - Colorblind modes in the settings menu (default/deuteranopia/protanopia/ tritanopia/grayscale): Okabe-Ito-based retint of the zone-map family colors and the accent; map colors moved from inline hex to CSS classes (ZonesMapPage emits mapfam-* classes, style.css owns the colors); persisted as wiki-cb, applied pre-paint - Screen-reader text: video cards get role=button + aria-label, the zone map SVG gains a <title>, decorative spans aria-hidden; audited: 0 images without alt text site-wide - Footer aligned to the content column (margin-left 240px, max-width 1050px, panel border) so it no longer slides under the sticky sidenav
- 18 community-wiki images into static/img/: the four SAP icons + intrusion + relic (outposts), reactor plasma (npcs), spark (sparks), planet + three megacorp logos (lore), fitting/coreactor/evasive/ drainer/neutralizer/interference-emitter (modules) - Scarab model icon extracted from the client archive (GBF entry icon_scarab, per-chunk XOR) — its wiki 404s; the only robot icon not under a licensed source (noted in RobotsPage.cs) - Items catalog now lists tier 0-5 ascending within every sub-category (untyped items last), name as the secondary key
- Layout: sidenav + (main + footer) in a flex row; the sticky sidenav now releases at the content column's bottom, so at full scroll its bottom aligns with the footer's - Home menu: outline-only SVG group icons (transparent fill, accent stroke, shapes echoing the community wiki's category icons: radar disc, robotic arm, calibration panel, compass star, twin robots, crystal) - Footer: YouTube channel link - i18n seam: default_language in config, ALL chrome strings moved to [extra.ui] (templates read config.extra.ui; JS receives them via a #wiki-ui-strings data-attribute element -> window.WIKI_UI, since Zola 0.23 has no JSON filter); language button (globe) left of the palette with the current 'English' entry - search.js reads its copy from WIKI_UI - Makefile: document the root-owned public/ case; clean falls back to a container rm (root cause was a leftover make serve container rebuilding public/ as root)
- Shop: shop.md is now an index (description, vendors, category reference table); each catalog category is a dedicated generated page (ShopPage.BuildCategoryPage -> shop/<slug>.md, 11 pages); the left menu Item shop entry expands (native <details>) to list the categories - Sidenav: full-height flex panel (no sticky) — the top scrolls with the content and the bottom meets the footer's in every viewport, incl. fullscreen; group headings carry the outline icons (SVG sprite, 36px), home menu icons doubled to 36px via the same sprite - FAQ and Client & PC setup moved under the New here group - Current-page selection: bold + neon outline (with contrast) in the left menu; the bottom menu additionally wraps the selected entry in parentheses, e.g. (Features); Formats (dev) removed from the bottom menu - Fix: a stray </script> after the WIKI_UI collector split the body script block, rendering the JS as a visible wall of text at the page bottom
…ages - Sidenav: sticky below the top bar with its bottom at the screen edge while scrolling; on short pages it stretches to the content height instead of overflowing (align-items: stretch + max-height: calc(100vh - 70px)) - Bottom menu selection keeps the bold + neon outline but no longer wraps the entry in parentheses - Sprite symbols: add fill="none" stroke="currentColor" + line caps — the 2x icons rendered as solid black fills instead of outlines - Language button: the menu now actually opens (it had no toggle handler) and lists a single entry, 'EN - English'; selection persists (wiki-lang) - Main-menu categories: all 8 sidenav group headings are now links to new category overview pages (/menu/<slug>/, content/menu/*.md) that describe the category and link to each sub-page; group headings highlight when their overview page is open - Sub-menus: <details> open state is remembered per browser (localStorage wiki-nav-open:<key>) and force-opens while the current page is inside the sub-menu's section
- All images sourced from the Open Perpetuum community wiki are out of the repo: 18 feature-page images, 35 robot model icons (incl. the GBF-extracted Scarab), the project logo and favicon. The official-backup snapshot had already been stripped from the git history and now lives outside the repo. - static/img/placeholder.svg: outline robot glyph (stroke=currentColor) used everywhere an icon is needed but not defined — robot family pages (all 50 families for now, via the RobotsPage placeholder fallback), the top-bar brand and the favicon. RobotIcons stays as the (empty) hook for licensed art. - Feature pages: dropped the inline community-wiki images; dev notes no longer point at the old official-backup/ path. - Footer attribution: logo/icon wording removed.
… unlimited vendor qty Numbers (Md.Num): - All generated tables now use engineering notation: 1000 -> 1k, 1001 -> 1.001k (max 3 decimals, trailing zeros trimmed, k/M/G). Applied to item stats, shop prices, mission rewards/fees, tech tree point prices and ore amounts; values below 1000 are unchanged. Mermaid transport diagrams: - The finished product is now always the right-hand node (blue), the capsule the left one (green): capsule pages read 'capsule --yields/carries--> item', item pages read 'capsule --> item'. The right node links to the item page. Tech tree (was one flat table, now a page tree): - /content/techtree/ index with the 7 research categories - one page per category (techtree/groups/<name>/) with a clickable mermaid tree of its chains (658 nodes total) and a node table - one detail page per node (techtree/nodes/<item-slug>/): category, what it unlocks, parent, what it unlocks next, enabler extension, point prices - robot nodes link to the robot catalog; node slugs match item slugs Item shop: - Vendor qty now shows the stock situation: '∞' when the vendor has no global limit (itemshop.globallimit NULL, per ItemShopEntry.CheckGlobalLimit), '240 · stock 600' when limited; explained on the shop index and category pages. All current stock is unlimited, so every row shows ∞ for now. Icon visibility: - SVG icons loaded via <img> cannot inherit page CSS colors, so the placeholder renders as a CSS alpha-mask (currentColor) in the top bar, robot pages and (explicit cyan) the favicon. The language button now has an explicit neon color in both themes. Navigation: - All 8 left-menu groups are collapsible (<details>) with full-width summary rows for a larger toggle target; open state persists per group (wiki-nav-open:<group>) and a group force-opens when the current page is one of its entries (longest-link match, so /features/research/ opens Industry & progress, not Data reference via the /features/ index link). - Zone Map is a top-level entry, second in the menu (and in Data reference).
What an item stat value means and how big it is: - Units: each documented stat field gets a unit (RP, CU, m, m/s, s, hp, %) and a one-line player-facing meaning. Only fields whose meaning is certain are documented; modifier and internal fields stay unitless (Formats lists every field). - Scale: per stat, item count, smallest/median/largest value in the whole catalog, and a real item to open at each end. - Powergrid/CPU context: the robot chassis capacity table (powergrid_max / core_max / cpu_max per frame, test/NPC/police frames excluded) so a module usage value can be read against the robot that carries it, with a worked percentage example (usage 214 on a 100 RP Ikarus vs a 6.6k RP Onyx). - Capsule stats (e.g. landmine damage) link to the payload item, not the capsule entry. - Linked from the item catalog index.
Client names: - ClientNames.cs: static snapshot of the official client's English string dictionary, reduced to the 4,266 definition/zone keys whose display name differs from the derived name (of 4,338 resolved keys). Official client development is finished, so the strings are final. - Verified byte-identical output: a full regeneration against the committed (archive-generated) content produced 0 differing files. - Removed: Gbf.cs (900 MB archive parser), --gbf argument, Makefile WIKI_GBF, README/Makefile layout assumptions about ../Perpetuum.gbf. - The generator now only needs the database and the plant rule files. Stale page cleanup (pre-existing bug, found while diffing): - The generator overwrote pages in place but never deleted ones that fell out of the catalog: 1,329 ghost item pages from the first implementation (fluxore, gammaterial, ... inventory pages) had been sitting in git and in the search index. make generate now removes its owned artifacts (items/, shop/, techtree/, the top-level generated .md files) before writing. - Search index drops from 6,580 to 5,251 entries. Link integrity: - Stat reference and tech tree node pages now apply the catalog exclusion rules (NPC unit fits, ores, deployable structures) before linking, so deployable PBS foundation capsules render as plain text instead of dead links. 0 missing links / 0 bad anchors across 5,252 pages.
The wiki backlog entries (IMPROVEMENT-034/035/036) used to live in the PerpetuumServer2 repository's docs/backlog/ as uncommitted working-tree changes; per the standalone-repo rule the wiki's own backlog now lives here. Reconstructed from the original entries and the work log; all three are complete.
The 8 menu groups are back to flat, always-visible headings with their entries listed beneath — no <details>, no persisted open state, no force-open script. The item-shop sub-categories are an always-open indented list again. The Zone Map top-level entry stays where it is.
docs/category-tree.md defines the full target menu/section tree with every node marked existing vs planned. Key decisions: - New 'How it works' (mechanics) group; 'Knowledge' becomes 'World & lore'. - Robots become a per-robot generated catalog; modules get per-family pages. - Stat reference joins the Data reference menu. - No pages move until planned content exists (no URL churn). - No ingestion from the community wiki backup: planned pages are written originally so the wiki stays eligible for a permissive license. - IMPROVEMENT-037 tracks the work.
- The current-page highlight was exact-match only, so generated sub-pages (item pages, tech tree nodes, shop categories) highlighted nothing. It now takes the longest matching menu entry — the page itself or the deepest ancestor entry — so an item page highlights 'Content', a shop category highlights its own row, etc. - The sidenav scroll position is kept per tab (sessionStorage) and restored on load, and after restore the highlighted entry is scrolled into view if it is not visible, so navigation can never hide the current category/sub-category.
The Zone map is already a top-level entry (second in the menu); the repeat inside the Data reference group at the bottom of the menu is removed.
Styled as a nav-group (icon + uppercase neon label) in the same position — second in the menu — instead of a plain entry.
All formerly adapted feature pages are now original or backend-verified prose, so the copyleft attribution footer is replaced by an Apache-2.0 statement. Adds LICENSE/, updates the footer in config.toml, cleans stale community-wiki CSS rules and updates the reuse-policy docs.
…extension tree; misc fixes - ZonePages.cs: generate a dedicated page for every zone (82 total incl. the three hand-written worked examples), with facts, ore config, steady-state stock pie, TP connections, exit gates and back-links - ZoneMapSvg.cs: per-zone teleport map (static/zonemaps/*.svg) plotting TP columns at real tile coordinates, landing spots from other zones and stronghold/arena exit gates; disabled columns drawn dimmed - Zones map renamed to World (menu label + page title + back-links) - Extensions tree: interactive SVG (static/extensions-tree.svg) of all active extensions by rank/category with prerequisite edges, using the shared zoom/pan wrapper (map.js now handles .zonemap-wrap and .map-zoom-wrap, plus touch pinch); new Extension tree menu group - map.js: wheel zoom was already keyless; added pinch zoom, MutationObserver re-scan for late-rendered wrappers - base.html: section hash only jumps on a fresh menu click (back button now restores scroll position); history.scrollRestoration=auto; mermaid render waits for the deferred script instead of dropping diagrams - Steam: replaced the retired iframeguesturl embed with a storefront card (Steam CDN header image + store link) - protection.md, sidenav, style: world map references and tree icon
Ore type rows on the ores index used to be in-page anchors (#titan, ...). Each of the 19 ore types now has its own page under /content/ores/<slug>/: - extraction facts (yield, solid/liquid, enabler effect, geo-scan document) - link to the material's item page when it exists in the item catalog (ores with the CfOre category flag are excluded from the item catalog, so the link is emitted only when the catalog page actually exists) - Where it spawns: every zone that regenerates the ore with its node configuration, each zone linked to its dedicated page; ores without a zone config (plants) say so explicitly - Made from it: catalog items that take the ore as a recipe component, with amount per production and research level (non-catalog definitions such as transport capsules and robot fits appear by name only) OresPage.cs reworked into Load/IndexBuild/OrePagesBuild; the ores index keeps its per-zone generation tables but all ore references (jump line, table, per-zone rows) now point at the dedicated pages. Zone pages and the hand-written worked examples link to /content/ores/<slug>/ instead of row anchors. Generator cleans ores/ before writing, like items/shop/techtree.
The wiki repo no longer needs to sit inside the server repo. A submodule 'server/' pins a PerpetuumServer2 commit and provides: - the plant rule files: WIKI_PLANTRULES now defaults to server/src/Perpetuum.ServerService2/data/plantrules, so 'git submodule update --init server' + 'make generate WIKI_DB=...' is all that is needed (WIKI_PLANTRULES still overridable for a live GameRoot) - the server source and docs/db_structure for verifying generated claims without a second checkout The site build itself (make build, CI) already needed no database or parent repo; only 'make generate' used WIKI_PLANTRULES from outside the repo. Client display names remain a static snapshot (ClientNames.cs).
Content was stale relative to the database:
- named{1,2,3}_sentry_turret / _gun / _unit_cprg and
standard_engineering_remote_controller(_cprg) definitions are now
disabled in entitydefaults; their item and tech-tree pages are
removed, and sentry_turret(_unit)(_cprg) + the
named/standard engineering remote controller pages are added
(definitions renamed upstream)
- extensions.md, items index, zone map SVG and search index updated
accordingly (5,356 search entries)
Generated via 'make generate' with WIKI_PLANTRULES from the new
server/ submodule (identical output to the previous GameRoot path).
The workflow only triggered on push to main, so PRs (including the feature/implementation merge) had no automated check. It now also runs on pull_request; the Pages deploy job is guarded to main pushes only, so PR runs are build-only and never touch the Pages environment.
The GitHub runner is non-root, so 'tar xzC /usr/local' failed with permission denied. Extract the release tarball into $GITHUB_WORKSPACE/tools and prepend it to $GITHUB_PATH instead.
$GITHUB_PATH only takes effect from the next step, so the 'zola --version' sanity check in the same step still saw command-not-found. Export PATH in-step; keep the GITHUB_PATH line so the Build step also finds zola.
The teleport maps now show each zone's actual terrain instead of a
procedural stand-in. tools/gen_zone_teleport_maps.py reads the zone's
altitude layer (2 bytes/tile little-endian; the game's own layer files,
custom-layers/*.bin with the .gbf archives as fallback — decoded with
the server's extract_gbf.py) and renders two 128x128 modes per zone:
height hillshaded monochrome heightmap (real island shapes)
color altitude color ramp + the accurate island coastline (the
Island flag of the blocks layer)
The SVG carries both as <image> layers; zone-map.js adds a Height/Color/
Plain switcher and remembers the choice (localStorage) so it applies to
every other zone map too. Zones without layer data fall back to the old
embedded procedural heightmap. make zonemaps mounts the sibling
PerpetuumServer2 checkout read-only; all 80 maps currently have real
terrain (5.6 MB of PNGs, the SVGs themselves shrank to 0.18 MB).
map.js now attaches the same wheel-zoom/drag-pan/pinch controls to mermaid diagrams as to the maps: per-wrap state survives theme-toggle re-renders (the svg is swapped in place), a reset button is created for each diagram box, and the 25% area-budget clamp applies. Pie charts are excluded — small, dense, and their wheel-zoom was just in the way, so the wheel keeps scrolling the page over them.
The <800px rule gave main an extra 1rem of side margin on top of the .layout inset, so the panel sat 16px right of the footer below it. Drop the extra margin — .layout and the footer already share the same 1.2rem inset, so the panel edges now line up top to bottom.
* roads: the zone's Highway tiles (control layer, ushort/tile, the
Highway flag from the server's TerrainControlFlags) are now drawn in
both display modes. The earlier 'roads in the water' was a data
misread — every road tile is on land; the coastline is now derived
from the altitude band instead of the blocks Island flag, which
proved too sparse to use as a land mask.
* resolution: 128 → 512 per mode (tiny 256×256 zones keep native
size), so terrain, roads and the island edge read crisply.
* points of interest: teleport columns / landing spots / exit gates
get a 1.5× radius and a 1.25× stroke; labels grow 1.3×, lift to
match, and get a dark halo (paint-order stroke) so they stay
readable on any terrain. A data-zm-v2 marker keeps the script
idempotent now that the sizing is multiplicative.
* modes: Plain is gone — Height and Color only (the stored preference
still normalises old 'plain' values to 'height').
* the generator now runs one zone per process (multiprocessing; the
per-tile loops are Python bytecode, so threads would not help):
80 zones in ~7s on 32 cores instead of ~100s.
The 160 generated PNGs (~51 MB) leave git; the repo keeps only the small SVGs plus derived PNGs for the 20 zones whose layer data has no public source yet (it ships only in the game client's Perpetuum.gbf, which needs a Steam account). tools/fetch_zone_layers.sh pulls what is publicly available — the same sources the PerpetuumServer2 CI uses: the original zones' .bin layers from the Dedicated Server installer (Steam app 693060, anonymous) and the gamma/custom zones from the public Drive archive. Both parts are verified working. CI (test + build jobs) caches .assets and runs the fetch + generator before zola build. Locally, make zonemaps prefers a PerpetuumServer2 checkout and skips the download when one exists; the Docker wrapper is gone (pure python3). The generator keeps the committed fallback PNGs for zones without layer data instead of downgrading to the procedural placeholder.
The old check looked for a 'pie' class on the rendered svg — this mermaid build leaves the svg root unclassed, so the exclusion never matched. Detect pies from the pie-specific inner elements (or from the diagram source before the render), and strip the zoom affordances if a pie is only recognized after the handlers attached (mermaid inserts the svg shell before its content). Also fixes the reset button vanishing after a mermaid re-render (theme toggle): the re-render wipes the button, so it is re-created in the re-render path. Covers a regression: an injected pie chart must keep the wheel scrolling the page, ignore drags, and show no reset button.
…, extension categories - Character: new /features/character/ page (character window: extensions tab and sparks); Extensions moves under it in the nav (as in the game) and the loose 'Extension tree' entry leaves the Reference group - Recipes: the flat component table becomes category sections of reward-style cards, each linked to the item's own page (tools/ gen_recipes_cards.py, mirrored in RecipesPage.cs) - Item pages: 'Production' mermaid section showing what can be built with the item (capped at 8 products, rest to the recipes page; tools/ gen_production_pages.py, mirrored in ItemsPage.cs) - Extensions page: 'Main categories' section with a new overview SVG — the 15 skill categories as boxes (count, entry points, rank range) with an arrow for every cross-category prerequisite (tools/ gen_extension_categories.py + ExtensionsCategories.cs) - Zone maps: teleport links were missing the trailing slash (regex group stopped before the table link's '/'); regenerated all 80 SVGs - Mobile drawer: the backdrop-click listener closed the drawer the instant the hamburger opened it (the button sits outside #sidenav); the test now taps the backdrop with a raw mouse click (it is a body::before) - tools/regen_search_index.py moved out of /tmp; search index refreshed - README: document the no-database content tools
…s, spark tree, nav rework - Extensions page: per-category card sections (rank, price, bonus, prerequisites) replace the 251-row table; Research link removed; sparks referenced from the intro. - Item pages: reverse production tree (components -> item, with amounts and research level) plus forward 'Used in production' sample; CT-capsule and ore pages untouched; disabled/hidden definitions no longer produce 404 links. - Recipes page stays the index (card layout); each recipe's data now also lives on the produced item's page. - New spark connection tree (static/sparks-tree.svg, 47 sparks -> 21 bundles) embedded on the sparks page; categories overview now uses rank bands (left = starting at rank 1) and leaves sparks out. - Nav: Sparks under Character; Research becomes a sub-group holding the Tech tree; Lore moves to the top of the World section. Menu pages follow. - Generator now writes tools/recipes_data.json (components + itemresearchlevels + display names) so the Python one-shot tools stay byte-identical with the C# output; plants/robots mermaid fences fixed to column 0.
…ap, in-zone TP hover lines, clickable category boxes - zone pages: teleport map moved to right under the title (generated + 3 hand-written pages); caption explains the hover lines when present - zone maps: hidden dashed lines per local (in-zone) teleport pair, lit while the cursor is near one of the pair's columns (hysteresis, screen px so it tracks zoom); endpoint circles carry the data-ltp tokens - world map: every zone drawn with its real terrain (world.png, 128px color-mode downscale of the zone map) over the family-color outline; in-zone teleports carry endpoint dots + hover lines; sub-sections are zone cards (coastline thumb.png, name, type/size) - gen_zone_teleport_maps.py: emits world.png + thumb.png per zone (real data, fallback downscale, or fBm) — same altitude grid, so the shape matches the full maps - extensions: the detail tree section is gone; the category overview is embedded inline and its boxes link to the per-category sections below; card values (rank, credits, bonus) are bolded and colored - gen_extension_categories.py rewritten byte-identical to the C# generator (verified C#-then-Python produces no diff) - WriteRecipesData carries over the items/ores sections maintained by gen_production_pages.py (previously blanked) - tests: ltp hover, world terrain + cards, extension box links + emphasis (64 passed / 54 skipped)
… TP lines, spark family graph, always-visible in-zone TPs, internal link checker - Rename the 'Starter islands' map section to 'Alpha' (group id, nav label/key, protection table, zone-page back-links). - World map: drop the teleport-connections and exit-gates tables (the map itself carries the data); islands now use the index-card design (coastline thumb.png over the dark family-outlined plate); inter-zone TPs are dashed lines gradient-colored source-family → destination- family (per-line userSpaceOnUse gradients); no more in-zone TP hover lines on the world map. - Zone maps: in-zone teleport dashed lines are always visible (hover only brightens them); captions updated. - Anchor jumps (category/family boxes) now land below the two-row sticky topbar (scroll-margin-top). - Sparks page is now fully generated (SparksPage.cs + SparksFamilySvg.cs): hand-written prose + a compact family overview graph (6 clickable boxes linking to the per-family sections) + 47 per-spark cards (unlock cost / standing, switch cost, bundled extension levels) + the connection tree further down. tools/gen_spark_families.py is the byte-identical offline mirror (C# generator remains the source of truth). - OresPage.ItemLink: only catalog items get item-page links (robot fits and PBS capsules have no item page — 97 broken links on the plants page, caught by the new link checker). - gen_zone_teleport_maps.py: emit world.png/thumb.png for world-map zones without a teleport SVG (the strongholds). - tests/links.spec.js: exhaustive internal link + anchor sweep over all 5,300+ built pages (Node-side; a browser crawl is too slow and gets paused by Playwright's watchdog). tests/sparks.spec.js: family boxes, card counts, emphasis, click-to-scroll. - 70 passed / 54 skipped / 0 failed.
The family cards (unlock cost, switch cost, bundled extension levels) replace it — the 47-row bipartite SVG was hard to scan and duplicated what the cards now state per spark. - SparksPage.cs: drop the '## Spark connection tree' section and the #tree references (family intro sentence, card prereq link) - Program.cs + SparksTree.cs + static/sparks-tree.svg deleted - gen_spark_families.py: generated block now ends at <a id='switching-sparks'>; intro line updated (byte-identical to C#, re-verified) - sparks.spec.js: asserts the tree is gone
…line
The raw tierlevel number ('Tier | 3') meant nothing to someone who has
never seen the tier system. Now:
- Md.Tier renders the tier as T1–T5 (the client's convention —
def_elitet4_* is tierlevel 4, def_elitet2_* is 2, so display = level:
standard = T1, named1 = T2, named2 = T3, named3 = T4) with series
markers: 'T3 (prototype)' for prototype variants, 'T4 (special)' for
elite/artifact/faction lines.
- Item pages of tiered items gain a 'Tier line' row: the whole
standard → named1 → named2 → named3 chain as links with tier badges,
current item bolded (1,306 pages affected, incl. the _pr prototype
variants of each tier).
- The items index legend now explains T1–T5/prototype/special (it used
to describe the internal tiertype codes, which confused things).
- production.md: new 'Tier progression' section stating the rules
explicitly — each tier is its own tech-tree node (tiers must be
unlocked in order, no making a higher tier without research), each
higher tier consumes one specimen of the previous tier (a T1 can
never be turned in for a T3; the specimen is consumed, not
converted) — with the small miner module line as the worked example.
- tests/tiers.spec.js: T-row format, tier-line chain/links/bold on the
T1 and T3 pages, and the rules section on the production page.
The committed (batch 3) copy was the Python json.dump(indent=1) form; make generate rewrites recipes/names in the generator's compact layout and carries over the items/ores sections. Content is byte-identical modulo formatting — all four sections verified equal by key. The Python tool's json.dump rewrite keeps producing the same data (it only re-reads what the pages carry), so this is purely the file's resting format between regenerations. Also re-ran make zonemaps (the previous commit had captured the raw pre-zonemaps SVGs — the last make generate overwrote them and the commit ran before the zonemaps step). Zone-map tests re-verified green.
…cards, deployables pages B5 batch, all 14 items: - zone map local TPs already family-colored (verified) - probes mermaid: drop the junk-node parenthetical - tech tree: per-root-line mermaid graphs behind a chip index (like the missions page); chips are raw <a> HTML because Zola does not parse markdown inside HTML blocks - spark family SVG: 3 columns x 2 rows (was 6 across) - sparks page: Switching sparks now above the family catalog - abbreviations: EP/CT/Proto links fixed, robots terminology link fixed - nav: Recipes under Production, Missions data page under Play/Missions - Named1/2/3/4 tokens in derived names -> T2/T3/T4/T5 (matches the tierlevel the client shows; 314 display names changed, slugs untouched) - item pages: 'Produced from' mermaid tree -> component cards (icon + name left, required amount in body); 'Used in production' keeps its mermaid; C# and the Python tool stay byte-identical - ores index: per-zone node generation section removed (data stays on each ore page's Zone parameters table) - items catalog index removed (Zola render:false keeps it a 404); '[All items]' footers and stale links cleaned up - deployables: card index by category + one page per deployable (220 pages) with capsule<->object twin links - zone pages: 'Connections' section removed (zone map + world map already show the TPs) Also: gen_production_pages.py rewrites recipes_data.json in the C# line-per-entry shape (was a 90k-line indent=1 dump), and the production section now appends at EOF (the [All items] anchor is gone).
… pages
- gen_zone_teleport_maps.py: ATTR regex allowed digits (x1/y1/x2/y2 were
dropped, ltp lines degenerated to points) — regression test added
- ZoneMapSvg.cs: ltp endpoint labels exclude the zone's own name
- ZonesMapPage.cs: TP lines drawn OVER the islands with endpoints at the
real column/landing positions (AVG(x/y) within each zone); gate links
anchored at the gate position; FamilyPages() generates
content/zones/{alpha,beta,gamma}.md (gamma with T0-T4 sections); the
map page keeps only the map + legend + links to the family pages
- Md.Header(): extra front-matter param (nested under extra:, as Zola
0.23.6 requires — top-level custom keys never reach page.extra)
- ZonePages.cs + hand-written zone pages: extra.family (alpha/beta/gamma)
- base.html: World group reordered (Lore, Training, Protection, Alpha,
Beta, Gamma with T0-T4 sub-list); body[data-zone-family] + nav
highlight for the matching family-listing entry
- map.js: attachTpLines() — inter-zone TP lines are nearly invisible at
rest on desktop pointers and fade in near the cursor (point-to-segment
distance in viewBox coords, works through zoom/pan; touch layouts keep
the plain 0.65 opacity)
- tests: nav/map/zone-map updated for the new structure; new tests for
family highlighting, family-listing cards, TP-line proximity, TP line
anchoring/on-top order. 82 passed / 0 failed / 60 skipped
… cards - ZonesMapPage.cs: the world map flipped the y axis, but the game grid grows downward (zone teleport maps plot DB coords directly on the terrain PNGs and align) — gamma is at the TOP of the map, the starter islands at the bottom, like in the client. Zones without any inter-zone teleport connection (strongholds, PvP arena, dead-end gate islands) are no longer plotted; gate links follow their source islands. - World map TP lines: rest opacity 0.07 -> 0.25 on desktop (faded but visible); hover-reveal unchanged. - Home page: the "Get the game" and "Watch first" sections moved from getting-started.md (first content after the intro paragraph); new home-card grid of high-level topics (Movement, Gathering, Missions, Combat, PBS, Intrusion, Production, Research, Market, Robots, Sparks) with high-contrast names; the World Map card takes a full-width line with a low-res render of the real map (static/world-map-thumb.png, shot from the generated map SVG) and the label centered over it. - Mobile: the hamburger now floats bottom-left (fixed) instead of taking a topbar slot; dimmed while the drawer is open. - tools/shot.js: screenshot helper (url, out, --width/--height/--scale, --full, --clip, --wait, --idle, --mobile, --eval). - tests: map.spec.js updated (59 plotted nodes, rest opacity 0.25, hover test scrolls the line into view — the map is taller than the viewport); zone-map failures were stale SVGs (pipeline order: generate -> zonemaps). Full suite: 82 passed / 0 failed / 60 skipped.
…olumn - Desktop: the topbar is a single row — brand left, then the chrome icons (language/palette/theme) and the search bar at the right end. Mobile keeps the two rows (row 2 = search). - Wide screens: the content column (main + footer, shared 1050px width) is the centered element; the sidenav sits to its left and is not part of the centered block (.content-col centers a 1050px grid column). - Sidenav sticky offsets and anchor scroll-margins follow the shorter one-row topbar (mobile keeps the old two-row values). - Mobile: the three chrome buttons are hidden while the sidenav drawer is open (the drawer covers that corner of the topbar); the hamburger goes back to its topbar slot (the floating bottom-left variant will come back with the work needed to make it right). - tests/sparks.spec.js: the anchor-scroll assertion measured the topbar height instead of hardcoding the old two-row height.
…rawer
- Progressive width test (2560 -> 320px, 22 breakpoints): the search bar
must be visible and non-overlapping at every size. Found and fixed:
- the desktop-only .topbar-search display rule hid the search entirely
on mobile (width 0) — it is a row-2 element on mobile again
- 801-900px: the search (basis 560px, shrink 1) overlapped the chrome
icons; now .topbar-main flexes (shrink 1) and the search shrinks
first (shrink 3, min-width 140px), so only the brand ellipsizes,
never an overlap
- Mobile drawer: instead of hiding the three chrome buttons (language /
palette / theme) while the drawer is open, they reposition inside the
drawer just below the Home quick-link (fixed at the measured 4.2rem /
z 46 above the drawer).
- Full suite: 82 passed / 0 failed / 60 skipped.
- Topbar brand renamed to "OP Wiki" (config.extra.ui.brand; the full site name stays the page <title>). - World map opens pre-zoomed (initContentZoom): the bounding box of the plotted islands is scaled to ~88% of the view and centered. Verified browser mapping: screen = (rc - t) + s*P with rc the element-box center — the box center lands within 0.0px of the wrap center. The wheel caps at s=3 until the user pans (INITIAL_MAX), so the whole canvas stays reachable; reset returns to the pre-zoomed resting state. - Zone labels: moved ABOVE each island (centered), display names instead of definition names (zone_gamma_tc_z106 -> "Gamma TC Z106"; the DB has no client display names, so they are derived), and the font is scaled per zone so a label never overhangs the island's own width — the map reads clean at rest, zoom in for the small islands. - Home page world-map card thumbnail: re-rendered at the card's own aspect (tools/gen_world_thumb.js), zoomed onto the alpha starter zones centered with the beta edge on top, no zone names. - "Get the game" and "Watch first" (incl. the click-to-play script) moved from the home page to Getting started, ahead of the character steps.
- Wheel zoom now truly anchors on the mouse: the old zoomAt computed the fixed point as (C - C0)/s instead of (C - C0 - t)/s, so once a pan had moved the map (t != 0) the wheel zoom drifted toward the center. Re-derived the transform math (screen = C0 + t + s*(p - C0), C0 = rect center - t) and added a regression test that verifies the content point under the cursor stays under it across successive zooms. Applies to the world map, zone maps and mermaid diagrams. - Zone maps: exit teleports are now drawn as dashed lines from the column to the map edge, pointing the way the destination zone lies on the WORLD MAP (world-grid delta, y-down like the zone map), in the destination's family color — so a glance tells you the TP leaves the zone, which family it is, and roughly where. - Color-mode contrast: local-TP dashed lines and the landing-spot circles get a dark casing underneath, so the dashes no longer blend into the bright terrain in color display mode. - Nav: the Ores and Plants content pages move under Gathering (Play), out of the Reference Content list.
- Remove the Reference group: abbreviations merged into FAQ (page deleted), zone generation moved to World > How it works, zone index to World as All zones, deployables under Play > Bases & sites, server/formats into the new Dev group; /menu/reference/ deleted - New sidenav tree: Start(4), World(6), Play(6 sub-groups), Systems(6 sub-groups); shop categories become a 3rd-level sub-list under Market - Dev mode: Play/Dev knob in the top bar (persisted in localStorage, no-flash early script) toggles html[data-mode=dev] - Dev mode = matrix theme (near-black, glowing bright green text/borders) + the Dev sidenav group (hidden in play mode): Developer home, Server & reference, Formats - New /features/architecture/ dev home: high-level architecture overview (client/server/DB/zone files) from a dev/DevOps point of view - Update config.toml nav strings, docs/category-tree.md, search index; nav tests rewritten for the new tree + dev-mode coverage Tests: 87 passed / 0 failed / 65 skipped
Multi-destination teleport endpoints (zone-map columns, world-map islands) no longer guess a destination on click: - C#: circles carry data-dests (external dests) + data-eid; exit lines carry data-tps (source eid) + data-tpd (dest). World islands carry data-dests + data-zn; TP lines data-tps + data-tpd. - Python: multi-dest columns are left UNLINKED (no <a>); single-dest keeps its plain link; LTP-only endpoints carry no data-dests at all. - map.js: attachTpSelector() opens a destination popover on hover (desktop) / tap (touch). While open, the item's dashed lines are emphasised; hovering one entry emphasises only that line (scoped by source, so shared destinations of other columns stay dim); clicking an entry navigates to the destination's zone page. - Zone pages (generated + 3 hand-written): a small horizontal world-map card above the zone map linking to /zones/map/. - Nav: How it works keeps only Zone generation (protection levels is the head link); PBS sub-list gains the Power base stations category; Deployables sub-list lists its 10 main categories as anchor links; Groups renamed to Alliance / Corporation. Tests: +5 (zone selector, single-dest links, LTP no-action, world selector, nav structure); 92 passed / 0 failed / 70 skipped.
… color-mode line contrast - Zone index: family card grid (the gamma family-page design) instead of the stat table, listing only the 59 zones with at least one teleport point to another zone (the world map's connection set); strongholds, the arena and dead-end gate islands are dropped. - Zone meta for the map tool: the index no longer carries the table tools/gen_zone_teleport_maps.py parsed, so the generator now writes static/zonemaps/meta.tsv (name, display name, slug, id, size) and the Python tool reads it (table parsing kept as fallback). - Gamma family page: Training no longer listed — the tier loop matched every id starting with "t", which includes "training". - TP selector: entries now carry the destination DISPLAY names (the same strings the column labels show; data-dests is ;-separated display|zone pairs); the selector also opens when hovering the column label. The world map keeps its plain island links — no selector there. - Deployables index: the Power base stations category is no longer listed (the PBS page documents those; per-deployable PBS pages are still generated); nav: PBS is a plain entry again, Missions renamed to Mission list. - Nav: a gamma zone page highlights its tier entry (T1-T4, computed from the zone number — kept in sync with ZonesMapPage.GammaTier), not the Gamma head. - Color render mode: the TP lines (ltp + exit) are darkened (CSS filter on the wrap's zm-color class) and drawn 1.7x thicker (zone-map.js restores the original widths on exit) for contrast on bright terrain. Tests: zone-index/cards, selector display names + label hover, tier highlight, deployables nav, sub-count 16; world-map selector test removed. 93 passed / 0 failed.
…orld-map ones The zone cards (family pages) reference /zonemaps/<slug>/thumb.png for every zone of the family, but gen_zone_teleport_maps.py only created world.png/thumb.png for the slugs referenced in content/zones/map.md (the world map). Since TP-less zones (strongholds 71/72, dead-end gate islands, the arena) no longer appear on the world map, their thumbnails were never generated on a clean checkout -> 404 in CI's link test. Locally the stale files from an earlier run masked the break. The fallback set now comes from static/zonemaps/meta.tsv (the full zone list the generator writes), minus the zones that have their own teleport SVG; the map.md scan is kept only as a legacy fallback. Verified with a full clean rebuild (no public/, no generated zonemap assets): generate -> zonemaps -> build -> 93 passed / 0 failed. Also re-encodes static/search_index.json as written by make generate (C# SearchIndex) instead of the Python re-emitter — byte encoding of embedded quotes differs, parsed content is identical.
In dev mode the Home quick link and all play-mode groups (Start, World, Play, Systems) are hidden — the sidenav contains just the Dev group. Play mode is unchanged. Also fixes a latent template bug this exposed: the Systems group's </ul></li> was missing, so the Dev group's <li> was actually nested inside the Systems item list (the browser auto-closed both at </nav>). In dev mode the Dev group used to render at the bottom of the Systems group; it is now a proper top-level sibling.
clouths
marked this pull request as ready for review
October 4, 2026 22:33
clouths
added a commit
that referenced
this pull request
Oct 5, 2026
CodeQL alert #1: DOM text from data-dests flows into window.location.href via hrefOf() without sanitization. The fix strips any character not in [a-z0-9-] so a tampered value can never produce a non-zone path or a scheme switch (e.g. javascript:). Adds a Playwright test that opens a multi-dest TP selector, clicks a destination, and asserts the navigated URL matches /zones/<safe-slug>/.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
A complete player-facing + developer-facing wiki for the Open Perpetuum server, built with Zola (~5,400 pages):
/features/): 20+ hand-written pages (combat, production, PBS, transport, research, …) with mermaid diagrams, written from and verified against the server source/content/): items (one page per item), ores (one page per ore), plants, deployables, robots, extensions + interactive extension tree, missions, shop (per-category pages), recipes, tech tree (658 node pages), stat reference/zones/): one page per zone (82) with ore configuration and per-zone teleport maps (SVG), the interactive world map, zone index, protection levelsGenerated markdown is committed to git, so the site build (and CI) needs no database — only a Zola binary.
CI
wiki.ymlruns the Zola build on every PR (build-only) and deploys to GitHub Pages onmain. GitHub Pages needs to be enabled on the repo (Settings → Pages → Source: GitHub Actions) before the deploy job can do anything.How to run locally
Prerequisites
git clone --recurse-submodules https://github.com/OpenPerpetuum/Wiki.git cd WikiBuild the site (no DB needed)
Open http://localhost:8085 — that is the whole site.
make cleanremoves the build output.Regenerate the data pages (DB needed)
The generated pages (items, ores, zones, tech tree, …) are committed; regenerate them only when the database content changes:
The generator issues SELECTs only (read-only).
WIKI_PLANTRULESdefaults to the submodule'sserver/src/Perpetuum.ServerService2/data/plantrulesand can be pointed at any GameRootplantrules/directory instead.Rebuild afterwards with
make build. Seegenerator/README.mdfor the page ↔ table mapping.What the PR check does
The CI check is exactly
zola buildwith the pinnedv0.23.6release binary — equivalent tomake buildwithout Docker. A green check means the site compiles and all templates render.