Both shells declared a `title` prop. defineCustomElement maps every prop to an
attribute, and `title` already exists on every HTMLElement as the tooltip, so Vue warned
on every single mount that it would overwrite the native property. Harmless in effect —
nothing here wants a tooltip — but a warning repeated on every mount is where a real
error goes to hide, and this repo has already paid for that once when a thrown setup()
silently removed three buttons behind noise that looked ignorable.
Renaming the prop was safe because nothing outside ever set it: no host passes title=,
no profile-config reads it, and the value is only forwarded to the shared layout, which
is an ordinary Vue component rather than a custom element and so has no collision of its
own. Both shells carried the identical declaration — the same duplicate-implementation
pattern that earlier turned one resolver assumption into two separate outages — so both
were fixed together.
The studio smoke check now samples several tabs instead of only the first, and measures
node depth inside the mounted element rather than counting the slot's children. The old
check read one child for every pane whether the element rendered a full dashboard or
nothing at all, which is the "mounts is not works" trap its own header warns about. It
polls for content rather than sleeping a fixed interval, because a fixed wait either runs
too slow to use on every edit or marks a slow tab broken, and a gate that cries wolf gets
switched off.
Measured after: zero Vue warnings in the browser console.
A capability-5 shell mounted inside the studio carries its own tab list and resolves
it against its own tree base, which it derives from its script URL when nobody supplies
one. Nested here that guess was empty, so the resolver took its parent-directory
fallback and requested paths above the leaf at the server root — three bundles 404'd
that way. The requests were non-fatal, because each tab still rendered from its
fallback list, which is exactly why they survived: a silent 404 behind a working
screen. The studio already knows the correct base, since it is the one it resolved
against, so it now passes it down through the existing profile-config path rather than
letting the child re-derive it.
EVERY TAB FAILED TO LOAD. Introducing tree-base for the dev server prefixed it onto
URLs that were already rooted, producing /__plugin-gallery//plugin-designs/<b>/index.js
and a 404 on all 29 tabs. Both capability-5 shells had byte-identical resolve()
implementations, so both carried the same defect; both now return an already-absolute
ref untouched and join through exactly one slash. The built tree hands relative refs
and keeps its base, so this is one rule for both trees rather than a dev-only branch.
THE STUDIO NO LONGER FRAMES ANYTHING. Map elements were nested in iframes pointing at
their standalone leaf pages, to dodge a one-shot customElements.define collision. That
collision cannot occur here: 19 bundles claim 19 distinct tags, none shared, and two map
elements were proven to coexist in one document with the first unaffected by the second.
Every pane now mounts the plugin's real custom element, which is what makes a row both
reviewable and nestable.
Panes keep two lifetimes, and the difference is the WebGL context budget rather than
taste. Light rows stay mounted so switching back is instant and state survives. Map rows
are destroyed on deactivate, because a hidden map holds its GPU context: the browser
caps live contexts, then silently reclaims the OLDEST, so a map visited ten tabs ago
would go blank with no error anywhere. getContext never returns null and contextlost
never fires on the new canvas, which is what makes that failure so hard to attribute.
The dev server serves a leaf route for one element with no chrome, and passes the tree
base to nested shells so they discover the live tree instead of a baked fallback.
More than half the catalog had an empty description in manifest.json, so the
plugin-studio — the surface a Doxa inheritor browses to learn what exists —
showed flagship plugins as a bare "bundle - <tag>" with no explanation, while
a demo bundle explained itself fully.
Cause: the generator reads a JSDoc @description TAG, and these files carried
only prose. Every description added here is a summary of what that file's own
header already documented, not new invention. Claims were checked against the
artifacts: doxa-simple-map's "three presets" was verified against
simple-map.instances.json (prayer, engagement, adoption).
The two cap-5 shells needed a second pass. Their headers are HTML comments
with no leading asterisk, and the real TAG_RE requires `* @tag value`, so the
first attempt extracted nothing.
Result: 26/26 profiles described, 0 empty; the built gallery went from 15
empty / 11 populated to 0 / 26. Verified in a browser, not just in the
manifest. Embed suite 40/40.