Commit graph

4839 commits

Author SHA1 Message Date
Doxa Maps
728970b2bf P2b: a refused tab claim (a hole) keeps the rest of its announcement (studio's 5B note); 53 poisons 2026-10-03 08:00:32 +00:00
Doxa Maps
00f7d349eb Shareable pages P2b: nested shells at any depth have their tab in the address (?subtab= path)
When a shell sits inside another plugin that owns the address (Plugin Studio → the Research Gallery → …), its own
tab had no key. P1 never wrote it from a nested shell, and P2's ownAddress dropped a nested `tab`, so every tab
inside Studio had the same URL.

`subtab` is a PATH, one segment per nested shell below the owner: ?subtab=regions-dashboard at depth 1, and
?subtab=posterverse.upg-100 when a shell sits inside a shell. There are at most 6 segments, each a plain id.
- A nested shell claims its tab with claimView({ tab }, { depth }), where depth comes from its profile-config
  addressDepth (depthOf; its parent passes it down).
- The owner records the claim as that segment: the segments above are kept, the deeper ones and pane/place dropped.
- The same tab again is a no-op (a shell's first-paint claim on a deep link keeps everything below it).
- A segment missing above the claimed depth (a hole) is refused.
- The owner's own tab change drops the whole path.
- subtabAt(search, depth) gives a shell its segment.
Standalone pages and DX Life never see subtab: there the shell is the owner and writes `tab`.

check:view-address H covers depth 1, 2 and 3: first-paint replace, pick push, a composite's pane/place beside
subtab, the same tab claimed again, Back, and the owner's tab change. It has 9 new poisons (52).
2026-10-03 07:50:25 +00:00
Doxa Maps
04057b9177 Merge remote-tracking branch 'origin/development' into regions-plugin/view-address-p2 2026-10-03 07:38:38 +00:00
Doxa Maps
3be82cb23f Shareable pages P2: rebuild the reached bundles from the merged source, then the tree and SRI 2026-10-03 07:34:06 +00:00
Doxa Maps
98e8cace71 Merge origin/development into studio/leaf-escape 2026-10-03 07:25:28 +00:00
Doxa Maps
ef49411405 Merge remote-tracking branch 'origin/development' into regions-plugin/view-address-p2 2026-10-03 07:23:34 +00:00
Doxa Maps
0b066ba1ae Merge the Research Gallery's dev manifest fallback (shareable pages P2 assembly) 2026-10-03 07:19:40 +00:00
Doxa Maps
edbd280d4d Research Gallery dev fallback: the manifest at the public root first, so the dev server serves children as the raw build
Reached as /public/<tree>/, the dev server treats a built bundle as source and
injects an import into its IIFE, so a child loaded as a classic script threw
"Cannot use import statement outside a module" on the staging page. The root
path is tried first; /public/<tree>/ stays last, for a plain static server.
Built trees resolve the manifest before these fallbacks and are unaffected.
2026-10-03 07:19:22 +00:00
Doxa Maps
49b0fff62b Merge Posterverse's dev manifest fallback (shareable pages P2 assembly) 2026-10-03 07:18:40 +00:00
Doxa Maps
09d884ce3a Posterverse: on the dev server, read the built tree from its root, not /public/
On a staging page the manifest fallback was /public/<tree>/manifest.json, which
made /public/<tree>/ the base for every child script. The dev server treats a
/public/… request as source and injects an import into the built IIFE, so the
child failed as a classic script ("Cannot use import statement outside a
module"). The fallback is now /<tree>/manifest.json, where the dev server serves
the built tree unchanged. Built trees are unaffected (they resolve the manifest
before this fallback).
2026-10-03 07:18:00 +00:00
Doxa Maps
1fee1009bb Dev server: the leaf check runs before every landing; the framed reason corrected
check:dev-leaf-escape and its self-test join gates:pre. The comment on the
dev pages' owner rule named the wrong reason for leaving a framed page
unowned: the frame is a partner page that iframes a leaf (the iframe
embed). Plugin Studio mounts its rows directly.
2026-10-03 06:55:10 +00:00
Doxa Maps
5d3218acfd Merge remote-tracking branch 'origin/development' into studio/leaf-escape 2026-10-03 06:53:17 +00:00
Doxa Maps
f6594b77c7 Rebuild the Share button's importers for the Share menu (shareable pages P3); tree and SRI regenerated
The precondition's scope against the pilot (its own step-4 rule): every bundle
that imports ShareButton/MapControlMenu or reads the share catalogue area, plus
the P1 shells. Built one bundle at a time, then bun run tree + fix:sri.
gates:pre 141/0/1, gates:post 99/0/16.
2026-10-03 06:48:36 +00:00
Doxa Maps
dacb9a0762 useViewAddress: unchanged means the same view-key VALUES, not the same string
Measured in a browser on a fresh load: ?owner=x&pane=map came back as ?pane=map&owner=x through one replaceState.
viewSearch puts the view keys first, and writeViewKeys compared strings. The link meant the same thing and no Back
step was added, but it was a needless write, and on a host it would be a needless router.replace. writeViewKeys now
compares the parsed tab/pane/place. check:view-address B covers it, with a poison (44); the real-file revert fails.
2026-10-03 06:45:39 +00:00
Doxa Maps
6f94ff0e30 pane-wrapper: a pending link is also retried by a bounded rAF with an observer present, and by every settled scroll
From the 5B: an observer fires only when the COLUMN resizes. A pane whose height arrives later (styles adopted late
in a shadow root) would have left the link pending, and a pending link blocks the settle's write. check:view-address
covers it: a fresh load whose observer never fires again still opens the link (43 poisons).
2026-10-03 06:42:17 +00:00
Doxa Maps
2b62cc0120 Merge Country Posters' pane address (shareable pages P2) 2026-10-03 06:39:09 +00:00
Doxa Maps
7ad84e5610 pane-wrapper: a fresh load with ?pane= opens that pane once the column has its layout
Measured in a browser on Country Posters and on Regions: a fresh load with ?pane=<second pane> stayed on the first
pane, with the column's scrollTop 0 at 6 s. The column is built in the composite's onMounted, before it has a
layout, so the pane's offsetTop was 0 when the link was shown. And a ResizeObserver's first callback, which comes
after layout, only recorded the height.

Now the link waits until the column has a height and the pane a top, then shows it once. The wait is the observer's
callback, or a bounded rAF without one. A scroll that settles while the link waits does not overwrite it. The
composite's view readout follows from the scroll event it already listens to.

check:view-address F models a column with no layout at build, whose observer first fires after layout. There are 2
new poisons (42 in all), and restoring the old immediate show in the real file fails the gate.
2026-10-03 06:39:09 +00:00
Doxa Maps
4428156bfe Country Posters' panes are in the page address: on its own leaf the open pane is ?pane= (Map; the first pane is the default), and nested it announces to the shell, the same opt-in as Regions and UPG100 2026-10-03 06:36:09 +00:00
Doxa Maps
54fdead5e1 check:view-address: every composite that opts into the address also owns its leaf (a census from the tree, 40 poisons) 2026-10-03 06:32:22 +00:00
Doxa Maps
19bf214650 Merge UPG100's pane address (shareable pages P2) 2026-10-03 06:31:53 +00:00
Doxa Maps
08d236e0df check:view-address: each shell's ownAddress wiring (G), with a poison per shell (38 poisons) 2026-10-03 06:31:40 +00:00
Doxa Maps
ce15aba511 UPG100: the composite's pane in view is part of the page address (?pane=) 2026-10-03 06:31:14 +00:00
Doxa Maps
68842185ea Merge Plugin Studio's ownAddress (shareable pages P2) 2026-10-03 06:30:42 +00:00
Doxa Maps
00577668d6 Merge the Research Gallery shell's ownAddress (shareable pages P2) 2026-10-03 06:30:32 +00:00
Doxa Maps
86a601b3b2 Merge the Posterverse shell's ownAddress (shareable pages P2) 2026-10-03 06:30:18 +00:00
Doxa Maps
e4c31b6583 Research Gallery: a nested composite's pane and place reach the page address (shareable pages P2)
ownAddress on the shell's element, while the page owns its address: a nested
composite's open pane or selected place is written by this shell, the page's
one writer, and a nested element can never switch the shell's tab. Released
in beforeUnmount.
2026-10-03 06:30:10 +00:00
Doxa Maps
5a3194bd08 Plugin Studio: a nested composite's pane and place reach the address
Studio owns the page address and writes ?tab=. A composite nested in a
tab announces its own choice (its open pane, its selected place); Studio
now records that beside ?tab=, while it owns the address, so a reload
or a sent link opens the same pane and place. Only pane and place: the
tab stays Studio's own. The listener is released on unmount, before the
hot-update return.
2026-10-03 06:30:07 +00:00
Doxa Maps
8c05f6207a Posterverse: a nested plugin's view inside a tab reaches the address
The shell owns the address for the views inside its tabs too: a nested
composite's ?pane= / ?place= choice is recorded through the shared helper, only
while this page owns its address, never as a tab switch, and the event stops at
the shell.
2026-10-03 06:29:51 +00:00
Doxa Maps
c963a2a9fa P2: studio's 5B notes. A nested element cannot switch the owner's tab, a kept-alive pane or place is claimed back when its tab is shown again, and scrollTo takes null
- ownAddress records only pane and place. tab is the owner's own key, so a nested announcement cannot switch it.
- A shell keeps Regions (and UPG100) alive when another tab is open. Switching back dropped ?pane= and ?place=
  (the tab change), while the screen still showed them. pane-wrapper and region-dashboards now watch for being
  shown again (a ResizeObserver: no height, then height) and claim their pane / place back with a replace. An
  address that names one wins, because Back to an entry of this tab is applied while the column has no layout.
- scrollTo(id, null) behaves as scrollTo(id).

check:view-address covers each of these (34 poisons, each caught). The slow pane-wrapper arm runs only for
pane-wrapper poisons, so the self-test takes 16 s.
2026-10-03 06:26:53 +00:00
Doxa Maps
c21bcd2e13 Share this page never copies a token; neutral wording in the P3 comments
pageLink() drops ?tk= from the page's address: a leaf page reads a token from
there and leaves it in place, so pressing Share would have sent it. The URL is
rewritten only when tk is present; every other link is the address as it stands.
The comments name the page, not who measured it, and no longer cite a request.
check:map-menu-anchor strips a same-line comment before testing each return, so
a fallback named only in a comment no longer passes.
2026-10-03 06:18:57 +00:00
Doxa Maps
003af1caa8 Shareable pages P2: a composite's open pane and Regions' subject are in the page address
The address gets its second and third keys:
- ?pane= is the open view of a composite;
- ?place= is Regions' subject.
Both keep the ONE-writer rule:
- On its own leaf the composite owns the address and writes.
- Nested in a shell it announces, and the shell writes.
- On a partner's page nothing is written; a link is still read.

useViewAddress:
- claimView() is the one call for a reader's choice: the owner writes it, any other element announces it.
- ownAddress() lets an owner record its nested elements' announcements, and stops them there.
- An announcement carries only the view keys, one event per action.
- pane and place are siblings: a tab change drops both, but a pane change keeps the place, because Regions keeps
  its region while the reader is on the list.

pane-wrapper (buildPaneColumn) takes an opt-in `address`:
- a link opens its pane without writing;
- a click is a push, and a selection made by the same click rides in the same push (one Back step);
- where the reader's scroll settles is a replace;
- Back/Forward scroll and never write;
- the first pane is the default and is never written.

Regions:
- regions-mfe opts its column in and records its panes' announcements on its own leaf. A list click opens the
  dashboard with its place in one write.
- region-dashboards claims ?place= instead of writing its #slug with its own pushState (replace at first paint,
  push on a choice, re-applied on Back). An old #slug link still opens, and on its own leaf it is rewritten to
  ?place=.
- urlSync:false now guards only the tab title.

check:view-address adds:
- E: nested claims and the owner;
- F: pane-wrapper on a simulated column;
- G: the Regions wiring.
The census drops region-dashboards (5 writers now). Self-test: 29 poisons, each caught. Real-source mutations are
caught too.
2026-10-03 06:09:51 +00:00
Doxa Maps
484f07382e Dev server: a leaf URL cannot write markup into the page it opens
The dev leaf route wrote the URL's parts into the page's <title> and the
element's profile-config attribute, refusing only .. and /. A quote or a
tag in a crafted link broke out of the attribute on a publicly reachable
dev server. Each part must now have the shape every bundle, profile and
instance id has, before it reaches either; the title is escaped too.
check:dev-leaf-escape drives the plugin's own middleware: every leaf the
dev manifest advertises is still served in both URL forms, hostile parts
are refused in every position, and the title escapes on its own.
2026-10-03 06:09:09 +00:00
Doxa Maps
ac71afaf55 Share opens on "Share this page"; Embed is its own tab; a header menu opens beside its button (shareable pages P3)
ShareButton: the first tab is "Share this page", the link to exactly what the
reader is looking at. Where the page owns its address (urlOwner 'app' or 'host',
on the map's own profile-config or any element above it across shadow roots)
that is the page's own URL, read at open and at copy; on a partner's page it is
the map's own page, as before. The second tab, "Embed", says in plain words what
embedding is and why it helps, keeps the buttons.shareSteps list, then the paste
hint, the sizes and the code. Copy/sizes labels come from the catalogue.

MapControlMenu: a standalone anchor (a Share button outside any map toolbar)
resolves where its menu lives when it opens, and places it below the button,
toward the side with room, inside the viewport, at the readable width (340 or the
viewport minus its edges), converting into the menu's real containing block. The
render-time area tag that sent header menus far left and narrow is gone. Map
toolbars keep their placement unchanged.

English only for now (the new share.json keys are recorded as translation debt in
check:i18n-parity). check:map-menu-anchor reads a resolution that lives in a named
function, every named-area return must keep its structural fallback.
2026-10-03 06:05:46 +00:00
Doxa Maps
ceb7168bf3 Merge remote-tracking branch 'origin/development' into regions-plugin/view-address
# Conflicts:
#	package.json
2026-10-03 05:43:09 +00:00
Doxa Maps
5c03996dbb Merge the Research Gallery staging page's address ownership (shareable pages P1) 2026-10-03 05:42:23 +00:00
Doxa Maps
bb970b441f Library index: list useViewAddress 2026-10-03 05:41:31 +00:00
Doxa Maps
7285fda93d Merge the dev server's address ownership for Plugin Studio and the leaf pages (shareable pages P1) 2026-10-03 05:41:25 +00:00
Doxa Maps
d65747a300 Merge the Posterverse staging page's address ownership (shareable pages P1) 2026-10-03 05:41:25 +00:00
Doxa Maps
fba3187cd4 Dev server: Plugin Studio and the leaf pages own the address too
The dev pages passed no urlOwner, so on the dev server a shell read ?tab=
but never wrote it: a clicked tab was lost on reload and a copied link
opened the first tab. The root page, every /__leaf/ page and the Studio
staging page now own the address, as the built leaves do. A framed leaf
(Studio iframes its map rows) stays unowned, so it never adds entries
to the top page's Back stack.
2026-10-03 05:39:23 +00:00
Doxa Maps
34073e7c13 Shareable pages P1: rebuild the reached bundles from the merged source, then the tree and SRI 2026-10-03 05:37:18 +00:00
Doxa Maps
dc32000bfe Research Gallery dev page: it owns its address (urlOwner 'app'), so a tab pick writes ?tab=
The dev server serves plugin-designs/doxa-maps-spa/index.html as-is, with no
profile-config, so the shell read ?tab= there but never wrote it. The page is
ours: it now passes urlOwner 'app'. A partner's page still leaves it out.
2026-10-03 05:36:47 +00:00
Doxa Maps
ce1d3b6a46 Posterverse: the dev staging page owns its address
The staging page the dev server serves sets profile-config urlOwner 'app', so on
the dev server the shell writes the open tab to ?tab=: a reload keeps it and a
copied link opens it. Inside the Plugin Studio, Posterverse is nested and still
writes nothing (the Studio owns the address there).
2026-10-03 05:36:19 +00:00
Doxa Maps
789d90eb2f Merge commit '9c381b619e5596e3cc7c1909c242c4985c59712e' into poster-design-plugin/experimental 2026-10-03 05:33:11 +00:00
Doxa Maps
da14272f96 Merge remote-tracking branch 'origin/development' into regions-plugin/view-address 2026-10-03 05:33:02 +00:00
Doxa Maps
b90fb29960 Rebuild bible-posters and my-upg-100-list for their merged catalogue keys; tree and SRI regenerated
The merged posterDesign blocks brought biblePosters.posterDesign.label and
upgMap.posterDesignTerritory, catalogue areas these two bundles ship, so
their committed bytes were stale against the merged source. Rebuilt only;
no source edit.
2026-10-03 05:27:37 +00:00
Doxa Maps
1118219bd5 Merge Plugin Studio's view address and the generated pages' urlOwner (shareable pages P1) 2026-10-03 05:27:34 +00:00
Doxa Maps
7d7ae092b3 Merge the Posterverse shell's view address (shareable pages P1) 2026-10-03 05:27:33 +00:00
Doxa Maps
dfed65c8af Merge the Research Gallery shell's view address (shareable pages P1) 2026-10-03 05:27:33 +00:00
Doxa Maps
8c674c0447 Posterverse: don't overwrite a link to a tab that arrives with the manifest
The first-paint write is skipped when the address names a tab the shell
doesn't know yet (a row that only resolves once the manifest is in). Writing
tab 0 there would replace the link before applyManifest could honour it;
applyManifest's own first-paint replace writes the honoured tab.
2026-10-03 05:26:59 +00:00
Doxa Maps
4241609572 Plugin Studio: the open tab is in the address, so a reload keeps it and a sent link opens it
Plugin Studio read its tab from #<bundle/profile> or from localStorage, and
never wrote it, so a reload worked for one reader but a sent link did not. It
now writes the view address through useViewAddress: ?tab=<bundle>__<profile>
(a row key's "/" is not a plain id; the double underscore keeps a sent link
meaning one row even if another bundle later adopts the profile's name). A
reader's tab click is a push, the first paint a replace, and Back/Forward
re-open the named tab without writing. route() reads ?tab= first (or a bare
profile name while it names exactly one row), then the old #bundle/profile
(rewritten to ?tab= on first paint, its hash dropped), then the remembered
tab, then the first row. The listener is removed before the hot-update return.

generate-tree bakes urlOwner 'app' into our own leaf pages only (never into an
embed snippet), and a framed leaf drops it at runtime, so a page embedded in a
partner's iframe reads its address but never adds entries to the partner's
Back button.
2026-10-03 05:26:13 +00:00