diff --git a/README.md b/README.md index befb7d3f5..ed1700a29 100644 --- a/README.md +++ b/README.md @@ -427,7 +427,8 @@ bun run tauri:build # builds the app — needs Rust, the Tauri CLI and webvie ## Languages -**Every map speaks 11 languages** — add `?lang=` to a map's page address. Arabic reads right to left. +**Every map speaks 11 languages** — add `?lang=` to a map's page address, or, when a map is embedded in +your own page, set `"locale"` in its `profile-config`. Arabic reads right to left. ![UPG-100 List in Arabic, right to left](documentation/readme-images/lang-arabic-upg100.png) @@ -447,10 +448,18 @@ bun run tauri:build # builds the app — needs Rust, the Tauri CLI and webvie - **Pick a language with the address:** `…/prayer/index.html?lang=fr` shows the map in French. It works on every map's own page and on the Research Gallery. `?locale=` works too. -- **Without it,** a map follows the page's own language (``, which WordPress sets), and +- **Set it from your own page:** when you embed the Research Gallery or a single map with the + JavaScript embed, put the language in its `profile-config` (`"lang"` works too). Set it again + later and the map switches language live. + + ```html + + ``` +- **Without either,** a map follows the page's own language (``, which WordPress sets), and otherwise shows English. -- **Right to left:** in Arabic the whole layout mirrors — the Research Gallery's tab rail moves to - the right. +- **Right to left:** in Arabic the map's layout mirrors — the Research Gallery's tab rail moves to + the right. Set through `profile-config`, only the map's own box mirrors; the rest of your page is + left alone. A `?lang=ar` address mirrors the whole page, which is what a map's own page wants. - **Where the words live:** `library/i18n/locales//`, one folder per language. Adding a language: [documentation/architecture/i18n.md](./documentation/architecture/i18n.md). diff --git a/documentation/readme-images/cap1-single-element.png b/documentation/readme-images/cap1-single-element.png index 7e4f72d93..4dcd4a2c7 100644 Binary files a/documentation/readme-images/cap1-single-element.png and b/documentation/readme-images/cap1-single-element.png differ diff --git a/documentation/readme-images/cap2-adoption.png b/documentation/readme-images/cap2-adoption.png index ee5cb2492..4f52c86aa 100644 Binary files a/documentation/readme-images/cap2-adoption.png and b/documentation/readme-images/cap2-adoption.png differ diff --git a/documentation/readme-images/cap2-engagement.png b/documentation/readme-images/cap2-engagement.png index 6b1a623ae..9e7c73a72 100644 Binary files a/documentation/readme-images/cap2-engagement.png and b/documentation/readme-images/cap2-engagement.png differ diff --git a/documentation/readme-images/cap2-prayer.png b/documentation/readme-images/cap2-prayer.png index 8ddd7694a..5a37fdec7 100644 Binary files a/documentation/readme-images/cap2-prayer.png and b/documentation/readme-images/cap2-prayer.png differ diff --git a/documentation/readme-images/cap3-monolithic-research.png b/documentation/readme-images/cap3-monolithic-research.png index 420463fdf..8ee119f12 100644 Binary files a/documentation/readme-images/cap3-monolithic-research.png and b/documentation/readme-images/cap3-monolithic-research.png differ diff --git a/documentation/readme-images/cap4-upg100-list.png b/documentation/readme-images/cap4-upg100-list.png index a716a374a..2b2088ecb 100644 Binary files a/documentation/readme-images/cap4-upg100-list.png and b/documentation/readme-images/cap4-upg100-list.png differ diff --git a/documentation/readme-images/cap4-upg100-mfe.png b/documentation/readme-images/cap4-upg100-mfe.png index 024915dc9..7a137148a 100644 Binary files a/documentation/readme-images/cap4-upg100-mfe.png and b/documentation/readme-images/cap4-upg100-mfe.png differ diff --git a/documentation/readme-images/cap5-doxa-maps-spa.png b/documentation/readme-images/cap5-doxa-maps-spa.png index 95df30bff..de0c0ab17 100644 Binary files a/documentation/readme-images/cap5-doxa-maps-spa.png and b/documentation/readme-images/cap5-doxa-maps-spa.png differ diff --git a/documentation/readme-images/cap5-plugin-studio.png b/documentation/readme-images/cap5-plugin-studio.png index 3db1400a9..cb04353c1 100644 Binary files a/documentation/readme-images/cap5-plugin-studio.png and b/documentation/readme-images/cap5-plugin-studio.png differ diff --git a/documentation/readme-images/deploy1-iframe.png b/documentation/readme-images/deploy1-iframe.png index 15c3214a1..361363c68 100644 Binary files a/documentation/readme-images/deploy1-iframe.png and b/documentation/readme-images/deploy1-iframe.png differ diff --git a/documentation/readme-images/deploy2-mobile-upg100.png b/documentation/readme-images/deploy2-mobile-upg100.png index db0fb6bd2..68c603c26 100644 Binary files a/documentation/readme-images/deploy2-mobile-upg100.png and b/documentation/readme-images/deploy2-mobile-upg100.png differ diff --git a/documentation/readme-images/exp-prayer-walk-planner.png b/documentation/readme-images/exp-prayer-walk-planner.png index 9a43c9f2e..f40cf8111 100644 Binary files a/documentation/readme-images/exp-prayer-walk-planner.png and b/documentation/readme-images/exp-prayer-walk-planner.png differ diff --git a/documentation/readme-images/exp-wall-of-people-groups.png b/documentation/readme-images/exp-wall-of-people-groups.png index de1b17960..f996cffbf 100644 Binary files a/documentation/readme-images/exp-wall-of-people-groups.png and b/documentation/readme-images/exp-wall-of-people-groups.png differ diff --git a/documentation/readme-images/lang-arabic-upg100.png b/documentation/readme-images/lang-arabic-upg100.png index 6f0dfb35b..c9d0c5ea7 100644 Binary files a/documentation/readme-images/lang-arabic-upg100.png and b/documentation/readme-images/lang-arabic-upg100.png differ diff --git a/documentation/readme-images/prod-big-picture.png b/documentation/readme-images/prod-big-picture.png index 49785ef74..734f32d00 100644 Binary files a/documentation/readme-images/prod-big-picture.png and b/documentation/readme-images/prod-big-picture.png differ diff --git a/documentation/readme-images/prod-cities-internet-phone.png b/documentation/readme-images/prod-cities-internet-phone.png index 721b3f10e..04489065f 100644 Binary files a/documentation/readme-images/prod-cities-internet-phone.png and b/documentation/readme-images/prod-cities-internet-phone.png differ diff --git a/documentation/readme-images/prod-cities-internet.png b/documentation/readme-images/prod-cities-internet.png index 742f77c5e..156113412 100644 Binary files a/documentation/readme-images/prod-cities-internet.png and b/documentation/readme-images/prod-cities-internet.png differ diff --git a/documentation/readme-images/prod-gospel-resources-phone.png b/documentation/readme-images/prod-gospel-resources-phone.png index e4a22e772..d47007dad 100644 Binary files a/documentation/readme-images/prod-gospel-resources-phone.png and b/documentation/readme-images/prod-gospel-resources-phone.png differ diff --git a/documentation/readme-images/prod-gospel-resources.png b/documentation/readme-images/prod-gospel-resources.png index c68e55386..3798e22b6 100644 Binary files a/documentation/readme-images/prod-gospel-resources.png and b/documentation/readme-images/prod-gospel-resources.png differ diff --git a/documentation/readme-images/prod-prayer-phone.png b/documentation/readme-images/prod-prayer-phone.png index 6e4db7f2b..ec2c03cae 100644 Binary files a/documentation/readme-images/prod-prayer-phone.png and b/documentation/readme-images/prod-prayer-phone.png differ diff --git a/documentation/readme-images/prod-regions-phone.png b/documentation/readme-images/prod-regions-phone.png index c4b3b8077..3d44ce9a7 100644 Binary files a/documentation/readme-images/prod-regions-phone.png and b/documentation/readme-images/prod-regions-phone.png differ diff --git a/documentation/readme-images/prod-regions.png b/documentation/readme-images/prod-regions.png index 8ad21e462..35eed9ebe 100644 Binary files a/documentation/readme-images/prod-regions.png and b/documentation/readme-images/prod-regions.png differ diff --git a/documentation/readme-images/prod-research-gallery-phone.png b/documentation/readme-images/prod-research-gallery-phone.png index 6518e86a7..c452ba02d 100644 Binary files a/documentation/readme-images/prod-research-gallery-phone.png and b/documentation/readme-images/prod-research-gallery-phone.png differ diff --git a/documentation/readme-images/prod-simple-research-phone.png b/documentation/readme-images/prod-simple-research-phone.png index 0719de9ca..e6c0dd670 100644 Binary files a/documentation/readme-images/prod-simple-research-phone.png and b/documentation/readme-images/prod-simple-research-phone.png differ diff --git a/documentation/readme-images/prod-simple-research.png b/documentation/readme-images/prod-simple-research.png index 4b9e64b38..00993c0d9 100644 Binary files a/documentation/readme-images/prod-simple-research.png and b/documentation/readme-images/prod-simple-research.png differ diff --git a/documentation/readme-images/shot-bundles.json b/documentation/readme-images/shot-bundles.json index ef2f8813f..0a30b0790 100644 --- a/documentation/readme-images/shot-bundles.json +++ b/documentation/readme-images/shot-bundles.json @@ -1,102 +1,102 @@ { "cap1-single-element": { "bundle": "all-people-groups", - "sha256": "715fa7a854ea0b819f5dbe4ae256d95264984584e6c6933ed321bbb4230b49f7" + "sha256": "1a2ddbee5f7ade4c59be1e729334fbaabb9a4bb52fe91aaae70df0b8a3e817a2" }, "cap2-adoption": { "bundle": "doxa-simple-map", - "sha256": "4f7ecd217d30bcb87a86c3f07f9840b2a500c9637037c4ff64a5e10aba009bf5" + "sha256": "01973724b4f6b656d4f4743cf2167b8d5a984f48401e4738cec83fb3719d289b" }, "cap2-engagement": { "bundle": "doxa-simple-map", - "sha256": "4f7ecd217d30bcb87a86c3f07f9840b2a500c9637037c4ff64a5e10aba009bf5" + "sha256": "01973724b4f6b656d4f4743cf2167b8d5a984f48401e4738cec83fb3719d289b" }, "cap2-prayer": { "bundle": "doxa-simple-map", - "sha256": "4f7ecd217d30bcb87a86c3f07f9840b2a500c9637037c4ff64a5e10aba009bf5" + "sha256": "01973724b4f6b656d4f4743cf2167b8d5a984f48401e4738cec83fb3719d289b" }, "cap3-monolithic-research": { "bundle": "doxa-research-map", - "sha256": "ea9638be007ff6127514e663f1ee79644be5fee4ba7340ef8d3ad6bf18cafc69" + "sha256": "5f73cb71c0a374a5506df426ac4b43648f28d7128b9583da1b358c1ca7519bda" }, "cap4-upg100-list": { "bundle": "my-upg-100-list", - "sha256": "50e6d3caaa49467f9c867e4cc04b5121d965092c96e09a1f3d0a78a7483db480" + "sha256": "237de597f1080ca4893650ed19f6fc3a31b7ea30da498e8fa7d1f74ca88818b2" }, "cap4-upg100-mfe": { "bundle": "my-upg-100-list", - "sha256": "50e6d3caaa49467f9c867e4cc04b5121d965092c96e09a1f3d0a78a7483db480" + "sha256": "237de597f1080ca4893650ed19f6fc3a31b7ea30da498e8fa7d1f74ca88818b2" }, "cap4-upg100-poster-wall": { "bundle": "my-upg-100-list", - "sha256": "50e6d3caaa49467f9c867e4cc04b5121d965092c96e09a1f3d0a78a7483db480" + "sha256": "237de597f1080ca4893650ed19f6fc3a31b7ea30da498e8fa7d1f74ca88818b2" }, "cap5-doxa-maps-spa": { "bundle": "doxa-maps-spa", - "sha256": "3d5467fd47ada47979fb878a5a355329ddc3ad88071ec947bf6c4a61d53df143" + "sha256": "9093c700a38d42a9444650690d99ce3b8b66f0c73e03e00ac49780c3833807c5" }, "cap5-plugin-studio": { "bundle": "plugin-studio", - "sha256": "6b8f3ce6cb17075ec26859f12ec0afe0c78bee5dc02b1f9f81b8d699931dc44a" + "sha256": "0034c901862e25c575966dd48f78d2fa739834db401cf37f1de9653e3034ae05" }, "deploy2-mobile-upg100": { "bundle": "my-upg-100-list", - "sha256": "50e6d3caaa49467f9c867e4cc04b5121d965092c96e09a1f3d0a78a7483db480" + "sha256": "237de597f1080ca4893650ed19f6fc3a31b7ea30da498e8fa7d1f74ca88818b2" }, "exp-prayer-walk-planner": { "bundle": "prayer-walk-planner", - "sha256": "a06c505f4ae24d03f29657065806f65c4dfc36348a1000178d6cb843aaf455e8" + "sha256": "0b695f70da64b8d5c4f0c6ca765571eb621acac08f41e49bc3da57df687ed0a5" }, "exp-wall-of-people-groups": { "bundle": "wall-of-people-groups", - "sha256": "de94f7f53fcfbb757ed8216679e5f563db90cde005ec401104d617fc59c5f105" + "sha256": "2dfc3a3c4918b574862c07271d8594e56b6d1d7ef96553fe67359bbe58c2cad2" }, "lang-arabic-upg100": { "bundle": "my-upg-100-list", - "sha256": "50e6d3caaa49467f9c867e4cc04b5121d965092c96e09a1f3d0a78a7483db480" + "sha256": "237de597f1080ca4893650ed19f6fc3a31b7ea30da498e8fa7d1f74ca88818b2" }, "prod-big-picture": { "bundle": "dashboard-plus-grid", - "sha256": "4ec8da3d155150d391ecbc0ac2f96f60bd2efbb12b3b5e8bdf0a869846dbfa16" + "sha256": "27b9a2e9198b0f3274218faec09796636307d3d6af18c21ed4ba580b2c515457" }, "prod-cities-internet": { "bundle": "cities-internet", - "sha256": "a62739180efc54c2d9e9216eafe0b290921da6ef83827749ead027945f56dbd7" + "sha256": "65e98efcd79016282460e3141ccb2995ad5a11d7039c3303d7c4ae2a7a5ee32d" }, "prod-cities-internet-phone": { "bundle": "cities-internet", - "sha256": "a62739180efc54c2d9e9216eafe0b290921da6ef83827749ead027945f56dbd7" + "sha256": "65e98efcd79016282460e3141ccb2995ad5a11d7039c3303d7c4ae2a7a5ee32d" }, "prod-gospel-resources": { "bundle": "gospel-resources", - "sha256": "8b31fa704a5217be541a804d350551b3f7b7d064eb1096eaecb226ad10e578f4" + "sha256": "00c05aa0437f0304a0403037af640795f6f18797371cf14c2cd50817be1c145c" }, "prod-gospel-resources-phone": { "bundle": "gospel-resources", - "sha256": "8b31fa704a5217be541a804d350551b3f7b7d064eb1096eaecb226ad10e578f4" + "sha256": "00c05aa0437f0304a0403037af640795f6f18797371cf14c2cd50817be1c145c" }, "prod-prayer-phone": { "bundle": "doxa-simple-map", - "sha256": "4f7ecd217d30bcb87a86c3f07f9840b2a500c9637037c4ff64a5e10aba009bf5" + "sha256": "01973724b4f6b656d4f4743cf2167b8d5a984f48401e4738cec83fb3719d289b" }, "prod-regions": { "bundle": "regions-dashboard", - "sha256": "3609a2c914d6af247cb6618585d234e9a2c857df83bfe03c2a699c2615d1828d" + "sha256": "e90b0055e2251c51b5cdc8b419377463ec8919cfbb774725e3c6c47c370433d6" }, "prod-regions-phone": { "bundle": "regions-dashboard", - "sha256": "3609a2c914d6af247cb6618585d234e9a2c857df83bfe03c2a699c2615d1828d" + "sha256": "e90b0055e2251c51b5cdc8b419377463ec8919cfbb774725e3c6c47c370433d6" }, "prod-research-gallery-phone": { "bundle": "doxa-maps-spa", - "sha256": "3d5467fd47ada47979fb878a5a355329ddc3ad88071ec947bf6c4a61d53df143" + "sha256": "9093c700a38d42a9444650690d99ce3b8b66f0c73e03e00ac49780c3833807c5" }, "prod-simple-research": { "bundle": "research-map-plus-grid", - "sha256": "8091ad4c3369928fb322ca69fb9319567fc1351a381e1bffbca518a13e78cb53" + "sha256": "110de278cb89a41a95114fb37ea6a2809b19ae9cd99a79ef2debcd63a130de68" }, "prod-simple-research-phone": { "bundle": "research-map-plus-grid", - "sha256": "8091ad4c3369928fb322ca69fb9319567fc1351a381e1bffbca518a13e78cb53" + "sha256": "110de278cb89a41a95114fb37ea6a2809b19ae9cd99a79ef2debcd63a130de68" } } diff --git a/internal/_serves-this-tree.mjs b/internal/_serves-this-tree.mjs index 8d4bc86e1..897bb7967 100644 --- a/internal/_serves-this-tree.mjs +++ b/internal/_serves-this-tree.mjs @@ -29,9 +29,11 @@ const MANIFEST = `${TREE_NAME}/manifest.json` /** * @param {string} base e.g. http://127.0.0.1:4173 (a trailing /doxa-maps/ is tolerated) * @param {string} repo absolute path to the repo root + * @param {{assets?: string[]}} [opts] data files this gate JUDGES (tree-relative, e.g. + * 'assets/tables/doxa-gospel-access.json'); their served bytes must equal disk too * @returns {Promise<{ok:boolean, why:string}>} */ -export async function servesThisTree(base, repo) { +export async function servesThisTree(base, repo, { assets = [] } = {}) { let local try { local = createHash('sha256').update(readFileSync(resolve(repo, 'public', MANIFEST))).digest('hex') @@ -45,7 +47,25 @@ export async function servesThisTree(base, repo) { if (served !== local) { return { ok: false, why: `${root} serves a DIFFERENT build than public/ — another worktree, or a server that needs restarting` } } - return { ok: true, why: 'same build' } + // ★ THE MANIFEST DESCRIBES BUNDLES, NOT DATA. MEASURED 2026-09-25 (gospel): verify:gospel-map adopted + // another worktree's server on :4173 and reported PASS — the three worktrees' manifests were + // byte-identical because the change under test was a DATA asset (doxa-gospel-access.json), which the + // manifest never mentions; a planted jw.org link went undetected. So a gate whose subject is a data + // file DECLARES it (paths relative to the tree, e.g. 'assets/tables/doxa-gospel-access.json') and its + // served bytes are compared with disk too. Any difference is a different tree for THIS gate. + for (const rel of assets) { + let disk + try { disk = readFileSync(resolve(repo, 'public', TREE_NAME, rel)) } catch { + return { ok: false, why: `no local public/${TREE_NAME}/${rel} to compare against — build the tree first` } + } + const a = await fetch(`${root}/${TREE_NAME}/${rel}`).catch(() => null) + if (!a || !a.ok) return { ok: false, why: `${root} serves a DIFFERENT build — ${rel} is not served there (${a ? a.status : 'no answer'})` } + const got = Buffer.from(await a.arrayBuffer()) + if (!got.equals(disk)) { + return { ok: false, why: `${root} serves a DIFFERENT build — the manifest matches but ${rel} does not (another worktree's data, or a stale server)` } + } + } + return { ok: true, why: assets.length ? `same build, and ${assets.length} declared data file(s) byte-identical` : 'same build' } } @@ -100,7 +120,7 @@ export async function serveThisTree(dir) { * @param {string} repo absolute path to the repo root * @param {string} label the gate's own name, for the message */ -export async function requireThisTree(base, repo, label) { +export async function requireThisTree(base, repo, label, { assets = [] } = {}) { let origin = String(base || '') // A gate that loads file:// URLs is reading THIS checkout by construction — there is no server to // be wrong about, so the guard stays silent rather than inventing a precondition. @@ -116,7 +136,7 @@ export async function requireThisTree(base, repo, label) { if (!/^(localhost|127\.|0\.0\.0\.0$|\[?::1\]?$)/.test(host)) return { ok: true, why: 'remote target, not a worktree port' } } catch { /* unparseable — fall through to the content check */ } try { origin = new URL(origin).origin } catch { /* already an origin, or unusable — pass through */ } - const verdict = await servesThisTree(origin, repo) + const verdict = await servesThisTree(origin, repo, { assets }) if (/DIFFERENT build/.test(verdict.why)) { console.log(`${label}: NOT MEASURED — ${verdict.why}`) console.log(' Point this gate at a server for THIS checkout (bun run serve) and re-run.') diff --git a/internal/browser-slot.sh b/internal/browser-slot.sh index 3f9738ec3..7f47e52f8 100755 --- a/internal/browser-slot.sh +++ b/internal/browser-slot.sh @@ -18,7 +18,8 @@ # Usage: internal/browser-slot.sh [args...] # Env: BROWSER_SLOTS (default 2), BROWSER_SLOT_DIR (default /tmp), # BROWSER_MEM_FLOOR_MB (default 2500), BROWSER_LOCK_WAIT (default 900), -# BROWSER_MEM_WAIT (default 300), BROWSER_MAX_HOLD (default 900 s) +# BROWSER_MEM_WAIT (default 300), BROWSER_MAX_HOLD (default 900 s), +# BROWSER_DISK_FLOOR_MB (default 1500; checked on / and on $TMPDIR or /tmp) set -u SLOTS="${BROWSER_SLOTS:-2}" @@ -33,6 +34,7 @@ MEM_WAIT_S="${BROWSER_MEM_WAIT:-300}" BUSY=75 MAX_HOLD="${BROWSER_MAX_HOLD:-900}" KILL_AFTER="${BROWSER_KILL_AFTER:-15}" +DISK_FLOOR_MB="${BROWSER_DISK_FLOOR_MB:-1500}" # ★ A GATE THAT IS LEGITIMATELY LONG DECLARES ITS OWN LIMIT, in its header: @browser-slot-max-hold 2700 # check:host-locale-contract loads every leaf twice and hit the 900 s default three times on full @@ -68,21 +70,67 @@ fi # the command named so its owner can find it. A long gate that legitimately needs more says so: # BROWSER_MAX_HOLD=1800 internal/browser-slot.sh node internal/.mjs run_held() { - timeout --kill-after="$KILL_AFTER" "$MAX_HOLD" "$@" 9>&- - local rc=$? + # The gate's output still flows to the caller untouched (stdout and stderr kept apart); a COPY goes to + # RAM (/dev/shm, never the disk that may be full) so a failure can be classified afterwards. + local log="" rc + if [ -d /dev/shm ] && [ -w /dev/shm ]; then log=$(mktemp /dev/shm/browser-slot.XXXXXX 2>/dev/null) || log=""; fi + if [ -n "$log" ]; then + { timeout --kill-after="$KILL_AFTER" "$MAX_HOLD" "$@" 9>&- 2> >(tee -a "$log" >&2); } | tee -a "$log" + rc=${PIPESTATUS[0]} + sleep 0.2 # let the stderr tee finish writing its copy + else + timeout --kill-after="$KILL_AFTER" "$MAX_HOLD" "$@" 9>&- + rc=$? + fi if [ "$rc" -eq 124 ] || [ "$rc" -eq 137 ]; then echo "browser-slot: '$*' held a browser slot past BROWSER_MAX_HOLD=${MAX_HOLD}s and was killed (timeout rc $rc) — NOT MEASURED. Give it a timeout of its own, or set BROWSER_MAX_HOLD if it is legitimately that long." >&2 - exit "$BUSY" + rm -f "$log"; exit "$BUSY" fi + # ★ AN INFRASTRUCTURE FAILURE IS NOT A PRODUCT VERDICT (measured 2026-09-25: with the disk at 48 MB free, + # three browser gates went RED with Chromium "Page crashed" — an out-of-disk box wearing a product + # gate's exit code). ENOSPC is always infrastructure. A browser crash is classified as such ONLY when + # the box is below a floor at that moment: a renderer crash on a healthy box can be a real product + # bug (a leak, a runaway loop), and blanket-excusing it would hide exactly that. + if [ "$rc" -ne 0 ] && [ "$rc" -ne "$BUSY" ] && [ -n "$log" ]; then + local infra="" + if grep -qiE 'ENOSPC|No space left on device' "$log"; then + infra="the disk is full (ENOSPC)" + elif grep -qE 'Page crashed|Target (page, context or browser )?has been closed|Target closed|Browser (has been )?closed|browser has disconnected' "$log"; then + local d m; d=$(disk_available_mb); m=$(mem_available_mb) + if [ "$d" -lt "$DISK_FLOOR_MB" ] || [ "$m" -lt "$FLOOR_MB" ]; then + infra="the browser crashed while the box was below a floor (disk ${d}MB / floor ${DISK_FLOOR_MB}MB, memory ${m}MB / floor ${FLOOR_MB}MB)" + fi + fi + if [ -n "$infra" ]; then + echo "browser-slot: '$*' failed from INFRASTRUCTURE, not the product — ${infra}. NOT MEASURED (the command exited $rc)." >&2 + rm -f "$log"; exit "$BUSY" + fi + fi + rm -f "$log" exit "$rc" } slot_path() { [ "$1" = "1" ] && echo "$SLOT_BASE" || echo "$SLOT_BASE$1"; } +# Free disk, the lower of / and the temp dir (they may be different filesystems). BROWSER_FAKE_DISK_FILE is +# a TEST HOOK: when set, the reading comes from that file, so a test can "fill the disk" mid-run. +disk_available_mb() { + if [ -n "${BROWSER_FAKE_DISK_FILE:-}" ] && [ -f "$BROWSER_FAKE_DISK_FILE" ]; then cat "$BROWSER_FAKE_DISK_FILE"; return; fi + local a b; a=$(df --output=avail -m / 2>/dev/null | tail -1 | tr -d ' '); b=$(df --output=avail -m "${TMPDIR:-/tmp}" 2>/dev/null | tail -1 | tr -d ' ') + a=${a:-999999}; b=${b:-999999}; [ "$a" -lt "$b" ] && echo "$a" || echo "$b" +} + mem_available_mb() { awk '/^MemAvailable:/ { printf "%d", $2 / 1024 }' /proc/meminfo 2>/dev/null || echo 999999 } +# ★ REFUSE ON A FULL DISK, before taking a slot: Chromium needs disk for its profile and cache, and a +# measurement started with none ends as a "Page crashed" that reads like a product failure. +if [ "$(disk_available_mb)" -lt "$DISK_FLOOR_MB" ]; then + echo "browser-slot: only $(disk_available_mb)MB free on disk (floor ${DISK_FLOOR_MB}MB) — not measuring" >&2 + exit "$BUSY" +fi + # Hold off while the box is below the floor. Proceeding anyway is not an option here the way it is # for a load average: load makes a measurement slow, memory makes it get killed halfway through. waited=0 diff --git a/internal/check-element-census-vs-live.mjs b/internal/check-element-census-vs-live.mjs index f327de6fa..2aeaeeda8 100644 --- a/internal/check-element-census-vs-live.mjs +++ b/internal/check-element-census-vs-live.mjs @@ -25,7 +25,9 @@ * CENSUS IS BLIND and exits 75. No preset or ad-hoc run is accepted without one. * * PRESETS: internal/element-census/.json — { owner, pages:[{name, leaf, live}], neg:{leaf, text}, - * sanctioned:[{match, flags, what, driver, date, card}] }. An allowlist entry without the Driver's words, + * sanctioned:[{match, flags, what, driver, date, card, theirs?, theirsFlags?}] }. `theirs` makes the entry a + * SANCTIONED PAIR: their string for the same control (e.g. our "Hide Filters" / their "Show Filters"), + * excused as an omission only while our half is on the page. An allowlist entry without the Driver's words, * a date and a card is REFUSED (exit 2): it is evidence, not an ignore list. * * USAGE node internal/check-element-census-vs-live.mjs every preset @@ -64,7 +66,12 @@ function validate(p, from) { for (const k of ['match', 'driver', 'date', 'card']) if (!s[k]) bad(`${from}: sanctioned[${i}] has no "${k}" — every allowance carries the Driver's words, the date and the card`) if (!/^\d{4}-\d{2}-\d{2}$/.test(s.date)) bad(`${from}: sanctioned[${i}].date must be YYYY-MM-DD`) let re; try { re = new RegExp(s.match, s.flags || '') } catch (e) { bad(`${from}: sanctioned[${i}].match is not a regex: ${e.message}`) } - return { ...s, re } + // A SANCTIONED PAIR: `theirs` names THEIR string for the same control in its other state (their + // "Show Filters" vs our sanctioned "Hide Filters") under the same citation, so a sanctioned + // difference does not leave a phantom omission behind. + let theirsRe = null + if (s.theirs !== undefined) { try { theirsRe = new RegExp(s.theirs, s.theirsFlags ?? s.flags ?? '') } catch (e) { bad(`${from}: sanctioned[${i}].theirs is not a regex: ${e.message}`) } } + return { ...s, re, theirsRe } }) return p } @@ -171,6 +178,13 @@ for (const [name, p] of presets) { } const driftShapes = new Set(drift.map(([s]) => shape(s))) theirsOnly = theirsOnly.filter((t) => !driftShapes.has(shape(t))) + // Pairs: THEIR half is excused only when OUR half of the same entry is actually on this page. If + // our control vanished, their string is a real omission again — a pair can never hide a gap. + const pairedTheirs = [] + for (const x of p.sanctioned.filter((x) => x.theirsRe)) { + if (!ours.list.some((t) => x.re.test(t))) continue + theirsOnly = theirsOnly.filter((t) => { if (x.theirsRe.test(t)) { pairedTheirs.push([t, x]); return false } return true }) + } console.log(` OURS-ONLY: ${allowed.length} sanctioned · ${invented.length} unaccounted`) for (const [s, why] of allowed) console.log(` ok "${s.slice(0, 60)}" — ${why.slice(0, 140)}`) for (const s of invented) console.log(` ?? "${s.slice(0, 80)}"`) @@ -179,6 +193,7 @@ for (const [name, p] of presets) { console.log(` DATA DRIFT (same label, different figure — their page is an SSR snapshot, ours is live): ${drift.length}`) for (const [o, t] of drift.slice(0, 10)) console.log(` ~ ours ${JSON.stringify(o.slice(0, 44))} vs theirs ${JSON.stringify(t.slice(0, 44))}`) } + for (const [t, x] of pairedTheirs) console.log(` ok their "${t.slice(0, 50)}" — the other half of a sanctioned pair (${x.what || x.card}, Driver ${x.date})`) console.log(` THEIRS-ONLY (ported nowhere on our side): ${theirsOnly.length}`) for (const s of theirsOnly.slice(0, 25)) console.log(` !! "${s.slice(0, 80)}"`) if (theirsOnly.length > 25) console.log(` … and ${theirsOnly.length - 25} more`) diff --git a/internal/landing-preconditions.mjs b/internal/landing-preconditions.mjs index 4f746fc32..c86fec388 100644 --- a/internal/landing-preconditions.mjs +++ b/internal/landing-preconditions.mjs @@ -22,7 +22,6 @@ * DOXA_SKIP_FRESHNESS=1 skips (4) — for a DRY check only; main-capture scan never sets it. */ import { spawnSync } from 'node:child_process' -import { readdirSync, existsSync } from 'node:fs' import { resolve, dirname } from 'node:path' import { fileURLToPath } from 'node:url' import { TREE_NAME } from './_tree-name.mjs' @@ -42,8 +41,11 @@ if (dirty.stdout.trim()) refuse('the working tree differs from the commit being console.log('LANDING-PRECONDITIONS: 1 clean — the working tree is the commit being landed') // 2. ONE FOLDER, and it is the configured one -const pub = resolve(ROOT, 'public') -const entries = existsSync(pub) ? readdirSync(pub).sort() : [] +// What LANDS is the commit, so read public/ from HEAD, not from the disk: a gitignored, untracked leftover +// (a pre-rename public/assets/ in an older worktree) refused a clean README-only landing (repo, 2026-09-25). +const tree = run('git', ['ls-tree', '--name-only', 'HEAD', 'public/']) +if (tree.status !== 0) refuse('could not read public/ from HEAD') +const entries = tree.stdout.split('\n').filter(Boolean).map((l) => l.replace(/^public\//, '')).sort() const want = ['README.md', TREE_NAME].sort() if (JSON.stringify(entries) !== JSON.stringify(want)) { const extra = entries.filter((e) => !want.includes(e)), missing = want.filter((e) => !entries.includes(e)) @@ -62,7 +64,13 @@ if (process.env.DOXA_SKIP_FRESHNESS === '1') { console.log('LANDING-PRECONDITIONS: 4 fresh — SKIPPED (DOXA_SKIP_FRESHNESS=1; dry check only, never set by main-capture scan)') } else { const fresh = run(process.execPath, ['internal/check-composite-freshness.mjs']) - if (fresh.status !== 0) refuse('a committed composite is not what its source builds — rebuild it (BUNDLE= bun x vite build, bun run tree, bun run fix:sri) and commit', (fresh.stdout + fresh.stderr).trim().split('\n').filter((l) => /✗|STALE|FAIL/.test(l)).slice(0, 8).join('\n')) + if (fresh.status !== 0) refuse( + 'a committed composite is not what its source builds (stale consumers).\n' + + 'README rule 5: ONE agent owns the full build. If your change touches library/ or anything compiled into\n' + + 'other plugins, do not rebuild everyone yourself: commit it on your branch and hand the landing to studio\n' + + '(msg-agent studio "please land @: library change, consumers need rebuilding"). If only YOUR\n' + + 'own plugin is stale, rebuild just it: BUNDLE= bun x vite build && bun run tree && bun run fix:sri.', + (fresh.stdout + fresh.stderr).trim().split('\n').filter((l) => /✗|STALE|FAIL/.test(l)).slice(0, 8).join('\n')) console.log('LANDING-PRECONDITIONS: 4 fresh — every composite rebuilds to its committed bytes') } console.log('LANDING-PRECONDITIONS: PASS — this commit may land.') diff --git a/internal/verify-gospel-map.mjs b/internal/verify-gospel-map.mjs index 4149fb906..dea46da98 100644 --- a/internal/verify-gospel-map.mjs +++ b/internal/verify-gospel-map.mjs @@ -49,6 +49,8 @@ import { resolve, dirname } from 'node:path' import { fileURLToPath } from 'node:url' import { TREE_NAME } from './_tree-name.mjs' +// The data files this gate judges; declared to servesThisTree so their served bytes must equal disk. +const GOSPEL_DATA = ['assets/tables/people-group-pins.json', 'assets/tables/doxa-gospel-access.json'] const BASE = (process.env.BASE_URL || process.argv[2] || 'http://127.0.0.1:4173').replace(/\/+$/, '') const GL = ['--use-gl=swiftshader', '--enable-unsafe-swiftshader', '--ignore-gpu-blocklist', '--enable-webgl', '--no-sandbox'] // The screened publishers. Anything else is a doctrine failure, not a typo. @@ -79,13 +81,22 @@ let server = null const alive = async (b) => { const r = await fetch(`${b}/${TREE_NAME}/gospel-resources/resources-map/index.html`).catch(() => null) if (!(r && r.ok)) return false - const tree = await servesThisTree(b, REPO) + // The tables this gate judges are declared, so a server whose manifest matches but whose DATA does + // not (another worktree) is not adopted — the gate starts its own server for THIS checkout instead. + const tree = await servesThisTree(b, REPO, { assets: GOSPEL_DATA }) if (tree.ok) return true console.log(`GOSPEL-MAP: not adopting ${b} — ${tree.why}`) - return false + return 'different' // reachable, but not THIS tree — distinct from "nothing there" } -if (!(await alive(base))) { +const adopted = await alive(base) +if (adopted === 'different' && EXPLICIT) { + // You NAMED this server and it is another tree: that is NOT MEASURED, never a skip (exit 0 read as + // green while the gate had measured nothing of this checkout). + console.log(`GOSPEL-MAP: NOT MEASURED — ${base} is not serving THIS checkout (see above).`) + process.exit(75) +} +if (adopted !== true) { if (EXPLICIT) { console.log(`GOSPEL-MAP: SKIP — nothing served at ${base} (you named it explicitly, so none was started).`) process.exit(0) @@ -175,22 +186,15 @@ try { * The general weakness belongs to _serves-this-tree.mjs and every gate that relies on it; * this only closes it for the two files this gate actually measures. */ - const sameAsDisk = async (rel) => { - const res = await fetch(`${base}/${TREE_NAME}/${rel}`) - if (!res.ok) return `not served (${res.status})` - const served = await res.text() - const disk = readFileSync(resolve(REPO, `public/${TREE_NAME}/${rel}`), 'utf8') - return served === disk ? null : 'differs from this checkout — the server is another tree' - } - for (const rel of ['assets/tables/people-group-pins.json', 'assets/tables/doxa-gospel-access.json']) { - const why = await sameAsDisk(rel) - if (why) { - console.error(`GOSPEL-MAP: NOT MEASURED — ${rel} ${why}.`) - console.error(` Serving ${base}. Point BASE_URL at a server for THIS checkout, or stop the`) - console.error(' one holding the port; a PASS here would be about somebody else\'s data.') - if (server) server.kill() - process.exit(2) - } + // (The per-gate copy of this check that used to live here is now the shared helper's `assets` + // option — the weakness was in _serves-this-tree.mjs, so the fix is there, for every gate.) + const ident = await servesThisTree(base, REPO, { assets: GOSPEL_DATA }) + if (!ident.ok) { + console.error(`GOSPEL-MAP: NOT MEASURED — ${ident.why}.`) + console.error(` Serving ${base}. Point BASE_URL at a server for THIS checkout, or stop the`) + console.error(' one holding the port; a PASS here would be about somebody else\'s data.') + if (server) server.kill() + process.exit(75) } const idxRes = await fetch(`${base}/${TREE_NAME}/assets/tables/people-group-pins.json`) diff --git a/plugin-designs/plugin-studio/plugin-studio.vue b/plugin-designs/plugin-studio/plugin-studio.vue index 84961b822..49bc0ab38 100644 --- a/plugin-designs/plugin-studio/plugin-studio.vue +++ b/plugin-designs/plugin-studio/plugin-studio.vue @@ -223,6 +223,13 @@ const PS_SHADOW_CSS = ` .ps-logo { display:inline-flex; align-items:center; } .ps-logo svg { display:block; } .ps-count { color:#6d766e; } +/* THE HEADER'S MENUS SIT ABOVE EVERY PLUGIN (Driver, 2026-09-25: "the Mapbox token menu is not on top … + it is underlapping the plugins"). The header row and the pane area are siblings with no stacking context + of their own, so a menu at z-index 30 competed with each plugin's own chrome (a filter bar at 55, a drawer + at 1000) and lost. The header now stacks above the content, and the pane area is ISOLATED: whatever + z-index a plugin uses stays inside its pane. */ +.vtl-top { position:relative; z-index:3; } +.vtl-body { isolation:isolate; } .ps-token { position:relative; display:inline-flex; } .ps-token-dot { width:8px; height:8px; border-radius:50%; background:#b8bdb4; } .ps-token-dot.is-yours, .ps-token-dot.is-url { background:#92b195; } @@ -1058,6 +1065,13 @@ export default defineComponent({ .ps-logo { display: inline-flex; align-items: center; } .ps-logo svg { display: block; } .ps-count { color: #6d766e; } +/* THE HEADER'S MENUS SIT ABOVE EVERY PLUGIN (Driver, 2026-09-25: "the Mapbox token menu is not on top … + it is underlapping the plugins"). The header row and the pane area are siblings with no stacking context + of their own, so a menu at z-index 30 competed with each plugin's own chrome (a filter bar at 55, a drawer + at 1000) and lost. The header now stacks above the content, and the pane area is ISOLATED: whatever + z-index a plugin uses stays inside its pane. */ +.vtl-top { position:relative; z-index:3; } +.vtl-body { isolation:isolate; } .ps-token { position:relative; display:inline-flex; } .ps-token-dot { width:8px; height:8px; border-radius:50%; background:#b8bdb4; } .ps-token-dot.is-yours, .ps-token-dot.is-url { background:#92b195; } diff --git a/public/dx-maps/doxa-maps-spa/manifest.json b/public/dx-maps/doxa-maps-spa/manifest.json index ec1e32458..bc547599d 100644 --- a/public/dx-maps/doxa-maps-spa/manifest.json +++ b/public/dx-maps/doxa-maps-spa/manifest.json @@ -215,7 +215,7 @@ "bundle": "dashboard-plus-grid", "scriptUrl": "./dashboard-plus-grid/dashboard-plus-grid.js", "output": "dx-maps/dashboard-plus-grid/dashboard-plus-grid.js", - "integrity": "sha384-rRF96zozigk0kwJ6SwT1E2hyz91Mdu82TdfNYZP2pMIl86JsFV1byXVeuInovv9Y", + "integrity": "sha384-Mx03ys3XIk0Ud2XgCBsjIUXS3K39uYVoTzyL0aLM4v6fK5Z96qM/1RvPRYLFX+dm", "elements": [ "dashboard-plus-grid" ], @@ -235,7 +235,7 @@ "scriptUrl": "./dashboard-plus-grid/dashboard-plus-grid.js", "embed": { "iframe": "", - "script": "\n\n\n\n" + "script": "\n\n\n\n" }, "instances": [ { @@ -782,7 +782,7 @@ "bundle": "plugin-studio", "scriptUrl": "./plugin-studio/plugin-studio.js", "output": "dx-maps/plugin-studio/plugin-studio.js", - "integrity": "sha384-diiEvybWLwF+XuXh9aAkhzgJCZN9bPezKiAd6ANxLVjR1kHXSg4T+XFI7XHrX0Zb", + "integrity": "sha384-lYyH0iKEqJ0Ps32Bbzbe3XVdfJZbYAZ6SaeP7DURDmt6yVbxMY1bYrJNUdLDcf4d", "elements": [ "plugin-studio" ], @@ -810,7 +810,7 @@ "scriptUrl": "./plugin-studio/plugin-studio.js", "embed": { "iframe": "", - "script": "\n\n" + "script": "\n\n" }, "instances": [ { @@ -945,7 +945,7 @@ "bundle": "research-dashboard", "scriptUrl": "./research-dashboard/research-dashboard.js", "output": "dx-maps/research-dashboard/research-dashboard.js", - "integrity": "sha384-GsBGCy2M1ZnE7Xximx1HpGih2povZWx98661bjzLdaH6pnrTEhZj/PoPDuQ2gfJ5", + "integrity": "sha384-QtpHA6LFYa0Ig1rB2KZtX32+djysHFL251q7PYRCpkuyCts2Zn4JA3KQtqNS2Gak", "elements": [ "research-dashboard-map" ], @@ -961,7 +961,7 @@ "scriptUrl": "./research-dashboard/research-dashboard.js", "embed": { "iframe": "", - "script": "\n\n\n\n" + "script": "\n\n\n\n" }, "bundle": "research-dashboard" } diff --git a/public/dx-maps/index.html b/public/dx-maps/index.html index f50a3e199..979d49c5b 100644 --- a/public/dx-maps/index.html +++ b/public/dx-maps/index.html @@ -14,6 +14,6 @@ - + diff --git a/public/dx-maps/manifest.json b/public/dx-maps/manifest.json index b203af2d5..bc547599d 100644 --- a/public/dx-maps/manifest.json +++ b/public/dx-maps/manifest.json @@ -782,7 +782,7 @@ "bundle": "plugin-studio", "scriptUrl": "./plugin-studio/plugin-studio.js", "output": "dx-maps/plugin-studio/plugin-studio.js", - "integrity": "sha384-diiEvybWLwF+XuXh9aAkhzgJCZN9bPezKiAd6ANxLVjR1kHXSg4T+XFI7XHrX0Zb", + "integrity": "sha384-lYyH0iKEqJ0Ps32Bbzbe3XVdfJZbYAZ6SaeP7DURDmt6yVbxMY1bYrJNUdLDcf4d", "elements": [ "plugin-studio" ], @@ -810,7 +810,7 @@ "scriptUrl": "./plugin-studio/plugin-studio.js", "embed": { "iframe": "", - "script": "\n\n" + "script": "\n\n" }, "instances": [ { diff --git a/public/dx-maps/plugin-studio/plugin-studio.js b/public/dx-maps/plugin-studio/plugin-studio.js index ade14aacf..e28674622 100644 --- a/public/dx-maps/plugin-studio/plugin-studio.js +++ b/public/dx-maps/plugin-studio/plugin-studio.js @@ -274,12 +274,19 @@ .vtl.rail-collapsed:dir(rtl) .vtl-tab { padding-left:12px; padding-right:10px; } .vtl.rail-collapsed:dir(rtl) .vtl-tier { padding-left:8px; padding-right:8px; } } -`,Aa=go({name:"VerticalTabsLayout",props:{active:{type:String,default:""},title:{type:String,default:""},items:{type:Array,default:()=>[]},groups:{type:Array,default:()=>[]},navLabel:{type:String,default:"Sections"},labels:{type:Object,default:()=>({})},initialNavOpen:{type:Boolean,default:!0},header:{type:Boolean,default:!0},initialRailCollapsed:{type:Boolean,default:!1}},emits:["update:active"],setup(){Tn(Ca,"vertical-tabs-layout")},data(){return{navOpen:this.initialNavOpen,railCollapsed:this.initialRailCollapsed,_tabEls:{},folderOverride:{},narrow:!1,_mql:null,_onMedia:null,activatedKeys:{},hostState:{},hostError:{},_hostSlots:{},_hostMounted:{}}},computed:{railShown(){return this.narrow?this.navOpen:!0},allRows(){if(this.groups&&this.groups.length){const e=[];for(const t of this.groups)for(const o of t.rows||[])e.push(o);return e}return this.items},orderedKeys(){return this.allRows.map(e=>e.key)},elementRows(){return this.allRows.filter(e=>e&&e.element)},mountedElementRows(){return this.elementRows.filter(e=>this.activatedKeys[e.key])}},watch:{active:{immediate:!0,handler(e){this.activateElement(e)}}},mounted(){try{window.matchMedia&&(this._mql=window.matchMedia("(max-width: 760px)"),this.narrow=this._mql.matches,this.reconcileForWidth(),this._onMedia=e=>{this.narrow=e.matches,this.reconcileForWidth()},this._mql.addEventListener?this._mql.addEventListener("change",this._onMedia):this._mql.addListener&&this._mql.addListener(this._onMedia))}catch{}this.$nextTick(()=>this.activateElement(this.active))},beforeUnmount(){try{this._mql&&this._onMedia&&(this._mql.removeEventListener?this._mql.removeEventListener("change",this._onMedia):this._mql.removeListener&&this._mql.removeListener(this._onMedia))}catch{}this._mql=null,this._onMedia=null},methods:{registerTab(e,t){t&&(this._tabEls[e]=t)},isNarrow(){return this.narrow},reconcileForWidth(){this.narrow?(this.navOpen=!1,this.railCollapsed=!1):this.navOpen=!0},label(e){const t={toggleList:"Toggle list",openViews:"Open the list of views",expandList:"Expand list",collapseList:"Collapse list",collapse:"Collapse"};return this.labels&&this.labels[e]||t[e]},expandRail(){!this.narrow&&this.railCollapsed&&(this.railCollapsed=!1)},toggleRail(){this.narrow?this.navOpen=!this.navOpen:this.railCollapsed=!this.railCollapsed},setHostSlot(e,t){t&&(this._hostSlots[e]=t,this.$nextTick(()=>this.mountHostElement(e)))},activateElement(e){!e||!this.elementRows.find(o=>o.key===e)||(this.activatedKeys[e]||(this.activatedKeys={...this.activatedKeys,[e]:!0},this.hostState={...this.hostState,[e]:"loading"}),this.$nextTick(()=>this.mountHostElement(e)))},mountHostElement(e){if(this._hostMounted[e])return;const t=this.elementRows.find(n=>n.key===e),o=this._hostSlots[e];!t||!o||(this._hostMounted[e]=!0,this.hostState={...this.hostState,[e]:"loading"},Na(t.src).then(()=>Ma(t.element)).then(()=>{o.innerHTML="";const n=document.createElement(t.element),s=t.attrs||{};for(const l in s){const i=s[l],r=i!=null&&typeof i=="object"?JSON.stringify(i):String(i);try{n[l]=i}catch{}try{n.setAttribute(l,r)}catch{}}n.style.cssText="display:block;position:absolute;inset:0;width:100%;height:100%;",o.appendChild(n),this.hostState={...this.hostState,[e]:"ready"}}).catch(n=>{this._hostMounted[e]=!1,this.hostState={...this.hostState,[e]:"error"},this.hostError={...this.hostError,[e]:n&&n.message||String(n)}}))},iconSvg(e){return e?Ai(e):""},initial(e){const t=(e||"").trim();return t?t.charAt(0).toUpperCase():"•"},isFolderClosed(e){const t=this.folderOverride[e.key];return t!=null?!t:e.open!==!0},toggleFolder(e){const t=this.folderOverride[e],o=(this.groups||[]).find(s=>s.key===e),n=t??(o&&o.open===!0);this.folderOverride={...this.folderOverride,[e]:!n}},pick(e){this.$emit("update:active",e),this.narrow&&(this.navOpen=!1)},onKey(e){const t=this.orderedKeys;if(!t.length)return;const o=t.indexOf(this.active);let n=-1;if(e.key==="ArrowDown"||e.key==="ArrowRight")n=(o+1)%t.length;else if(e.key==="ArrowUp"||e.key==="ArrowLeft")n=(o-1+t.length)%t.length;else if(e.key==="Home")n=0;else if(e.key==="End")n=t.length-1;else return;e.preventDefault();const s=t[n];this.pick(s),this.$nextTick(()=>{const l=this._tabEls[s];l&&l.focus&&l.focus()})}}}),Oa={key:0,class:"vtl-top"},Pa=["aria-expanded","aria-label"],Ra={class:"vtl-brand"},Ia={class:"vtl-title"},Ua={class:"vtl-meta"},Da=["aria-expanded","aria-label"],Ha={class:"vtl-body"},Fa=["aria-label","aria-hidden","role","aria-orientation"],ja=["aria-expanded","aria-label"],Va=["innerHTML"],Ba={class:"vtl-collapse-tx"},za=["aria-expanded","onClick"],Ka={key:0,class:"vtl-cap"},Wa={class:"vtl-tl"},qa={key:1,class:"vtl-td"},Ya=["innerHTML"],Ga={key:1,class:"vtl-tier"},Ja={key:0,class:"vtl-cap"},Xa={class:"vtl-tl"},Za={key:1,class:"vtl-td"},Qa={class:"vtl-rows"},ec=["aria-selected","tabindex","onClick"],tc=["innerHTML"],oc={key:1,class:"vtl-tab-emoji","aria-hidden":"true"},nc={key:2,class:"vtl-tab-fallback","aria-hidden":"true"},sc={class:"vtl-tab-text"},ic={class:"vtl-tab-label"},lc={key:0,class:"vtl-tab-badge"},rc={key:0,class:"vtl-tab-sub"},ac=["innerHTML"],cc=["aria-selected","tabindex","onClick"],pc=["innerHTML"],dc={key:1,class:"vtl-tab-emoji","aria-hidden":"true"},uc={key:2,class:"vtl-tab-fallback","aria-hidden":"true"},fc={class:"vtl-tab-text"},hc={class:"vtl-tab-label"},gc={key:0,class:"vtl-tab-sub"},bc={class:"vtl-main"},mc={class:"vtl-content"},vc=["hidden"],xc={key:0,class:"vtl-host-hint"},kc={key:1,class:"vtl-host-hint err"};function yc(e,t,o,n,s,l){return L(),E("div",{class:he(["vtl",{"nav-open":e.navOpen,"rail-collapsed":e.railCollapsed,"no-header":!e.header}])},[e.header?(L(),E("header",Oa,[x("button",{class:"vtl-menu",type:"button","aria-expanded":String(e.navOpen),onClick:t[0]||(t[0]=i=>e.navOpen=!e.navOpen),"aria-label":e.label("toggleList")},"☰",8,Pa),x("span",Ra,[Mt(e.$slots,"brand")]),x("b",Ia,T(e.title),1),x("span",Ua,[Mt(e.$slots,"meta")])])):J("",!0),e.narrow&&!e.header&&!e.navOpen?(L(),E("button",{key:1,class:"vtl-fab",type:"button","aria-expanded":String(e.navOpen),onClick:t[1]||(t[1]=i=>e.navOpen=!0),"aria-label":e.label("openViews")},"☰",8,Da)):J("",!0),x("div",Ha,[x("nav",{class:"vtl-nav","aria-label":e.navLabel,"aria-hidden":String(!e.railShown),role:e.$slots.rail?null:"tablist","aria-orientation":e.$slots.rail?null:"vertical"},[x("button",{type:"button",class:"vtl-collapse","aria-expanded":String(!e.railCollapsed),"aria-label":e.railCollapsed?e.label("expandList"):e.label("collapseList"),onClick:t[2]||(t[2]=(...i)=>e.toggleRail&&e.toggleRail(...i))},[x("span",{class:"vtl-collapse-ic","aria-hidden":"true",innerHTML:e.iconSvg("chevron")},null,8,Va),x("span",Ba,T(e.label("collapse")),1)],8,ja),e.$slots.rail?Mt(e.$slots,"rail",{collapsed:e.railCollapsed&&!e.narrow,expand:e.expandRail,pick:e.pick},void 0,void 0,0):e.groups&&e.groups.length?(L(!0),E(Y,{key:1},Te(e.groups,i=>(L(),E("div",{class:"vtl-grp",key:i.key},[i.folder?(L(),E("button",{key:0,type:"button",class:"vtl-tier vtl-tier-btn","aria-expanded":String(!e.isFolderClosed(i)),onClick:r=>e.toggleFolder(i.key)},[i.badge?(L(),E("span",Ka,T(i.badge),1)):J("",!0),x("span",Wa,T(i.label),1),i.count!=null?(L(),E("span",qa,T(i.count),1)):J("",!0),x("span",{class:he(["vtl-chev",{open:!e.isFolderClosed(i)}]),"aria-hidden":"true",innerHTML:e.iconSvg("chevron")},null,10,Ya)],8,za)):(L(),E("div",Ga,[i.badge?(L(),E("span",Ja,T(i.badge),1)):J("",!0),x("span",Xa,T(i.label),1),i.count!=null?(L(),E("span",Za,T(i.count),1)):J("",!0)])),ft(x("div",Qa,[(L(!0),E(Y,null,Te(i.rows,r=>(L(),E("button",{key:r.key,type:"button",role:"tab",class:he(["vtl-tab",{active:r.key===e.active}]),"aria-selected":String(r.key===e.active),tabindex:r.key===e.active?0:-1,ref_for:!0,ref:a=>e.registerTab(r.key,a),onClick:a=>e.pick(r.key),onKeydown:t[3]||(t[3]=a=>e.onKey(a))},[r.icon?(L(),E("span",{key:0,class:"vtl-tab-ic","aria-hidden":"true",innerHTML:e.iconSvg(r.icon)},null,8,tc)):r.emoji?(L(),E("span",oc,T(r.emoji),1)):(L(),E("span",nc,T(e.initial(r.label)),1)),x("span",sc,[x("span",ic,[Je(T(r.label),1),r.badge?(L(),E("span",lc,T(r.badge),1)):J("",!0)]),r.sub?(L(),E("span",rc,[x("span",{innerHTML:r.sub},null,8,ac)])):J("",!0)])],42,ec))),128))],512),[[$n,!i.folder||!e.isFolderClosed(i)]])]))),128)):(L(!0),E(Y,{key:2},Te(e.items,i=>(L(),E("button",{key:i.key,type:"button",role:"tab",class:he(["vtl-tab",{active:i.key===e.active}]),"aria-selected":String(i.key===e.active),tabindex:i.key===e.active?0:-1,ref_for:!0,ref:r=>e.registerTab(i.key,r),onClick:r=>e.pick(i.key),onKeydown:t[4]||(t[4]=r=>e.onKey(r))},[i.icon?(L(),E("span",{key:0,class:"vtl-tab-ic","aria-hidden":"true",innerHTML:e.iconSvg(i.icon)},null,8,pc)):i.emoji?(L(),E("span",dc,T(i.emoji),1)):(L(),E("span",uc,T(e.initial(i.label)),1)),x("span",fc,[x("span",hc,T(i.label),1),i.sub?(L(),E("span",gc,T(i.sub),1)):J("",!0)])],42,cc))),128))],8,Fa),x("main",bc,[e.header?Mt(e.$slots,"toolbar",{},void 0,void 0,0):J("",!0),x("div",mc,[(L(!0),E(Y,null,Te(e.mountedElementRows,i=>ft((L(),E("section",{key:i.key,class:"vtl-host",hidden:i.key!==e.active},[x("div",{class:"vtl-host-slot",ref_for:!0,ref:r=>e.setHostSlot(i.key,r)},null,512),e.hostState[i.key]==="loading"?(L(),E("div",xc,"Loading "+T(i.label)+"…",1)):e.hostState[i.key]==="error"?(L(),E("div",kc,[Je(" Could not load "+T(i.label)+".",1),t[5]||(t[5]=x("br",null,null,-1)),x("small",null,T(e.hostError[i.key]),1)])):J("",!0)],8,vc)),[[$n,i.key===e.active]])),128)),Mt(e.$slots,"default")])])])],2)}const $c=Nn(Aa,[["render",yc],["styles",[Ta]]]),wc='.st{display:flex;flex-direction:column;margin:0 -12px}.st-bar{position:sticky;top:0;z-index:3;display:flex;align-items:center;gap:6px;padding:8px 12px;background:#fff}.st-find{flex:1 1 auto;min-width:0;display:flex;align-items:center;gap:6px;height:30px;padding:0 8px;border:1px solid #d1d4cd;border-radius:8px;background:#f3f3f1;color:#6d766e}.st-find:focus-within{border-color:#92b195;background:#fff}.st-find-ic{display:inline-flex}.st-find-ic svg{width:14px;height:14px}.st-find-in{flex:1 1 auto;min-width:0;border:0;outline:0;background:none;font:inherit;font-size:13px;color:#3b463d}.st-tool{flex:0 0 auto;width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;padding:0;border:0;border-radius:8px;background:none;color:#6d766e;cursor:pointer}.st-tool:hover{background:#f3f3f1;color:#3b463d}.st-tool:focus-visible{outline:2px solid #92b195;outline-offset:-2px}.st-tool svg{width:16px;height:16px}.st-tree{display:flex;flex-direction:column;padding:0 0 8px;outline:0}.st-row{position:relative;display:flex;align-items:center;gap:6px;height:28px;box-sizing:border-box;padding-block:0;padding-inline:calc(8px + var(--st-depth) * 14px) 10px;font-size:13px;color:#3b463d;cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;outline:0}.st-row:hover{background:#f3f3f1}.st-row:focus-visible{box-shadow:inset 0 0 0 1px #92b195}.st-row.active{background:#92b1953d}.st-row.active:after{content:"";position:absolute;inset-inline-start:0;top:4px;bottom:4px;width:3px;border-start-end-radius:3px;border-end-end-radius:3px;background:#92b195}.st-row.alias.active{background:#92b1951f}.st-sec{position:sticky;top:46px;z-index:2;height:30px;margin-top:6px;background:#fff;font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:#6d766e;border-top:1px solid #eceee9}.st-sec:first-child{margin-top:0;border-top:0}.st-sec:hover{background:#fff;color:#3b463d}.st-cap{flex:0 0 auto;font:700 10px/1 system-ui,sans-serif;letter-spacing:.3px;padding:3px 6px;border-radius:999px;background:#92b195;color:#1d2a1f}.st-sec-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.st-twisty,.st-twisty-sp{flex:0 0 16px;width:16px;height:16px;display:inline-flex;align-items:center;justify-content:center;color:#6d766e}.st-twisty svg{width:14px;height:14px;transition:transform .12s ease}.st-row[aria-expanded=true]>.st-twisty svg{transform:rotate(90deg)}.st:dir(rtl) .st-twisty svg{transform:scaleX(-1)}.st:dir(rtl) .st-row[aria-expanded=true]>.st-twisty svg{transform:rotate(90deg)}.st-ic{flex:0 0 16px;display:inline-flex;color:#6d766e}.st-ic svg{width:16px;height:16px}.st-row.active .st-ic{color:#5f8a63}.st-label{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.st-desc{flex:1 1 0;min-width:0;overflow:hidden;text-overflow:ellipsis;font:11px/1 ui-monospace,Menlo,monospace;color:#8a928b}.st-part{flex:0 0 auto;font:600 9px/1 system-ui,sans-serif;text-transform:uppercase;letter-spacing:.3px;padding:2px 4px;border:1px solid #92b195;border-radius:4px;color:#5f8a63}.st-count{flex:0 0 auto;margin-inline-start:auto;min-width:18px;text-align:right;font-size:11px;font-weight:500;letter-spacing:0;color:#6d766e}.st-guide{position:absolute;top:0;bottom:0;inset-inline-start:calc(15px + var(--st-g) * 14px);border-inline-start:1px solid #dfe2db;pointer-events:none}.st-row:hover>.st-guide{border-inline-start-color:#c4c8c0}.st-empty{margin:12px;font-size:13px;color:#6d766e}.st-activity{display:flex;flex-direction:column;gap:4px}.st-act{position:relative;display:flex;align-items:center;justify-content:center;width:48px;height:44px;margin-inline-start:172px;padding:0;border:0;border-radius:10px;background:none;color:#6d766e;cursor:pointer}.st-act:hover,.st-act.active{background:#f3f3f1;color:#3b463d}.st-act:focus-visible{outline:2px solid #92b195;outline-offset:-2px}.st-act-ic{display:inline-flex}.st-act-ic svg{width:20px;height:20px}.st-act-n{position:absolute;right:6px;bottom:5px;font:700 9px/1 system-ui,sans-serif;color:#5f8a63}',Sc='.st { display:flex; flex-direction:column; margin:0 -12px; }.st-bar { position:sticky; top:0; z-index:3; display:flex; align-items:center; gap:6px; padding:8px 12px 8px; background:#ffffff; }.st-find { flex:1 1 auto; min-width:0; display:flex; align-items:center; gap:6px; height:30px; padding:0 8px; border:1px solid #d1d4cd; border-radius:8px; background:#f3f3f1; color:#6d766e; }.st-find:focus-within { border-color:#92b195; background:#ffffff; }.st-find-ic { display:inline-flex; } .st-find-ic svg { width:14px; height:14px; }.st-find-in { flex:1 1 auto; min-width:0; border:0; outline:0; background:none; font:inherit; font-size:13px; color:#3b463d; }.st-tool { flex:0 0 auto; width:30px; height:30px; display:inline-flex; align-items:center; justify-content:center; padding:0; border:0; border-radius:8px; background:none; color:#6d766e; cursor:pointer; }.st-tool:hover { background:#f3f3f1; color:#3b463d; } .st-tool:focus-visible { outline:2px solid #92b195; outline-offset:-2px; }.st-tool svg { width:16px; height:16px; }.st-tree { display:flex; flex-direction:column; padding:0 0 8px; outline:0; }.st-row { position:relative; display:flex; align-items:center; gap:6px; height:28px; box-sizing:border-box; padding-block:0; padding-inline:calc(8px + var(--st-depth) * 14px) 10px; font-size:13px; color:#3b463d; cursor:pointer; white-space:nowrap; user-select:none; outline:0; }.st-row:hover { background:#f3f3f1; }.st-row:focus-visible { box-shadow:inset 0 0 0 1px #92b195; }.st-row.active { background:rgba(146,177,149,.24); }.st-row.active::after { content:""; position:absolute; inset-inline-start:0; top:4px; bottom:4px; width:3px; border-start-end-radius:3px; border-end-end-radius:3px; background:#92b195; }.st-row.alias.active { background:rgba(146,177,149,.12); }.st-sec { position:sticky; top:46px; z-index:2; height:30px; margin-top:6px; background:#ffffff; font-size:11px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:#6d766e; border-top:1px solid #eceee9; }.st-sec:first-child { margin-top:0; border-top:0; }.st-sec:hover { background:#ffffff; color:#3b463d; }.st-cap { flex:0 0 auto; font:700 10px/1 system-ui,sans-serif; letter-spacing:.3px; padding:3px 6px; border-radius:999px; background:#92b195; color:#1d2a1f; }.st-sec-label { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; }.st-twisty, .st-twisty-sp { flex:0 0 16px; width:16px; height:16px; display:inline-flex; align-items:center; justify-content:center; color:#6d766e; }.st-twisty svg { width:14px; height:14px; transition:transform .12s ease; }.st-row[aria-expanded="true"] > .st-twisty svg { transform:rotate(90deg); }.st:dir(rtl) .st-twisty svg { transform:scaleX(-1); } .st:dir(rtl) .st-row[aria-expanded="true"] > .st-twisty svg { transform:rotate(90deg); }.st-ic { flex:0 0 16px; display:inline-flex; color:#6d766e; } .st-ic svg { width:16px; height:16px; }.st-row.active .st-ic { color:#5f8a63; }.st-label { flex:0 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; }.st-desc { flex:1 1 0; min-width:0; overflow:hidden; text-overflow:ellipsis; font:11px/1 ui-monospace,Menlo,monospace; color:#8a928b; }.st-part { flex:0 0 auto; font:600 9px/1 system-ui,sans-serif; text-transform:uppercase; letter-spacing:.3px; padding:2px 4px; border:1px solid #92b195; border-radius:4px; color:#5f8a63; }.st-count { flex:0 0 auto; margin-inline-start:auto; min-width:18px; text-align:right; font-size:11px; font-weight:500; letter-spacing:0; color:#6d766e; }.st-guide { position:absolute; top:0; bottom:0; inset-inline-start:calc(15px + var(--st-g) * 14px); border-inline-start:1px solid #dfe2db; pointer-events:none; }.st-row:hover > .st-guide { border-inline-start-color:#c4c8c0; }.st-empty { margin:12px; font-size:13px; color:#6d766e; }.st-activity { display:flex; flex-direction:column; gap:4px; }.st-act { position:relative; display:flex; align-items:center; justify-content:center; width:48px; height:44px; margin-inline-start:172px; padding:0; border:0; border-radius:10px; background:none; color:#6d766e; cursor:pointer; }.st-act:hover, .st-act.active { background:#f3f3f1; color:#3b463d; }.st-act:focus-visible { outline:2px solid #92b195; outline-offset:-2px; }.st-act-ic { display:inline-flex; } .st-act-ic svg { width:20px; height:20px; }.st-act-n { position:absolute; right:6px; bottom:5px; font:700 9px/1 system-ui,sans-serif; color:#5f8a63; }',_c='',Lc={filterPlaceholder:"Filter plugins",collapseAll:"Collapse all",noMatch:"No plugin matches “{query}”.",treeLabel:"Plugins by capability",openSection:"Open {cap}, {label}",part:"part"},Ec='',Tc=go({name:"StudioTree",props:{sections:{type:Array,default:()=>[]},active:{type:String,default:""},collapsed:{type:Boolean,default:!1},pick:{type:Function,default:null},expand:{type:Function,default:null},strings:{type:Object,default:()=>({})}},emits:["select"],setup(){Tn(Sc,"studio-tree")},data(){const e=this.readState();return{query:"",focusId:"",secClosed:e.sec||{},nodeOpen:e.node||{},chev:Ec,collapseAllSvg:_c}},computed:{byId(){const e=new Map;for(const t of this.sections)for(const o of t.nodes)e.set(o.id,o);return e},q(){return this.query.trim().toLowerCase()},kept(){if(!this.q)return null;const e=new Set;for(const t of this.sections)for(const o of t.nodes){if(!(o.label.toLowerCase().includes(this.q)||String(o.desc||"").toLowerCase().includes(this.q)))continue;e.add(o.id);let s=o.parent;for(;s;)e.add(s),s=this.byId.get(s)&&this.byId.get(s).parent}return e},visible(){const e=[];for(const t of this.sections){const o=this.kept?t.nodes.filter(n=>this.kept.has(n.id)):t.nodes;if(!(this.kept&&!o.length)&&(e.push({id:"sec:"+t.id,section:!0,sectionId:t.id,level:1,depth:0,expandable:!0,cap:t.cap,label:t.label,title:t.title||"",count:t.nodes.filter(n=>n.depth===0).length}),!(!this.kept&&this.secClosed[t.id])))for(const n of o)!this.kept&&!this.ancestorsOpen(n)||e.push({...n,sectionId:t.id,level:n.depth+2,expandable:!!n.kids,alias:n.depth>0})}return e},activeSectionId(){for(const e of this.sections)if(e.nodes.some(t=>t.depth===0&&t.target===this.active))return e.id;return""}},watch:{active:{handler(){this.reveal()},immediate:!0},sections(){this.reveal()},query(e){e&&this.visible.length&&(this.focusId=this.visible[0].id)}},methods:{svg(e){return e?Ai(e):""},str(e,t){let o=this.strings[e]||Lc[e]||e;if(t)for(const n in t)o=o.split("{"+n+"}").join(String(t[n]));return o},storageKey(){return"dx-studio-tree:"+(typeof location<"u"&&location.pathname||"")},readState(){try{return JSON.parse(window.localStorage.getItem(this.storageKey())||"{}")||{}}catch{return{}}},saveState(){try{window.localStorage.setItem(this.storageKey(),JSON.stringify({sec:this.secClosed,node:this.nodeOpen}))}catch{}},ancestorsOpen(e){let t=e.parent;for(;t;){if(!this.nodeOpen[t])return!1;t=this.byId.get(t)&&this.byId.get(t).parent}return!0},isOpen(e){return this.kept?!0:e.section?!this.secClosed[e.sectionId]:!!this.nodeOpen[e.id]},setOpen(e,t){e.section?this.secClosed={...this.secClosed,[e.sectionId]:!t}:this.nodeOpen={...this.nodeOpen,[e.id]:t},this.saveState()},toggle(e){e.expandable&&this.setOpen(e,!this.isOpen(e))},collapseAll(){this.nodeOpen={},this.saveState();const e=this.visible.find(t=>!t.section&&t.target===this.active&&t.depth===0);e&&(this.focusId=e.id)},rowClass(e){return{"st-row":!0,"st-sec":!!e.section,active:!e.section&&e.target===this.active,alias:!!e.alias}},setRow(e,t){this._rows||(this._rows={}),t?this._rows[e]=t:delete this._rows[e]},activate(e){if(e.section){this.toggle(e);return}e.expandable&&!this.isOpen(e)&&this.setOpen(e,!0),e.target&&(this.pick?this.pick(e.target):this.$emit("select",e.target))},onRowClick(e){this.focusId=e.id,this.activate(e)},focusRow(e){e&&(this.focusId=e,this.$nextTick(()=>{const t=this._rows&&this._rows[e];t&&(t.focus({preventScroll:!0}),t.scrollIntoView({block:"nearest"}))}))},onTreeKey(e){const t=this.visible;if(!t.length)return;const o=Math.max(0,t.findIndex(l=>l.id===this.focusId)),n=t[o],s=l=>l.section?"":l.parent||"sec:"+l.sectionId;switch(e.key){case"ArrowDown":this.focusRow((t[o+1]||n).id);break;case"ArrowUp":this.focusRow((t[o-1]||n).id);break;case"Home":this.focusRow(t[0].id);break;case"End":this.focusRow(t[t.length-1].id);break;case"ArrowRight":if(!n.expandable)break;this.isOpen(n)?t[o+1]&&t[o+1].level>n.level&&this.focusRow(t[o+1].id):this.setOpen(n,!0);break;case"ArrowLeft":n.expandable&&this.isOpen(n)&&!this.kept?this.setOpen(n,!1):this.focusRow(s(n));break;case"Enter":case" ":this.activate(n);break;default:if(e.key.length===1&&/\S/.test(e.key)&&!e.ctrlKey&&!e.metaKey&&!e.altKey){const l=e.key.toLowerCase(),r=t.slice(o+1).concat(t.slice(0,o+1)).find(a=>String(a.label).toLowerCase().startsWith(l));r&&this.focusRow(r.id);break}return}e.preventDefault()},onFindKey(e){e.key==="Escape"?(this.query="",e.preventDefault()):e.key==="ArrowDown"&&this.visible.length&&(this.focusRow(this.visible[0].id),e.preventDefault())},reveal(){if(!this.active||!this.sections.length)return;let e=null;for(const t of this.sections)if(e=t.nodes.find(o=>o.depth===0&&o.target===this.active),e){this.secClosed[t.id]&&(this.secClosed={...this.secClosed,[t.id]:!1},this.saveState());break}e&&((!this.focusId||!this.byId.has(this.focusId)||this.byId.get(this.focusId).target!==this.active)&&(this.focusId=e.id),this.$nextTick(()=>{const t=this._rows&&this._rows[e.id];t&&t.scrollIntoView&&t.scrollIntoView({block:"nearest"})}))},openSectionFromActivity(e){this.secClosed[e]&&(this.secClosed={...this.secClosed,[e]:!1},this.saveState()),this.expand&&this.expand(),this.$nextTick(()=>this.focusRow("sec:"+e))}}}),Nc={key:0,class:"st-activity"},Mc=["aria-label","onClick"],Cc=["innerHTML"],Ac={class:"st-act-n","aria-hidden":"true"},Oc={key:1,class:"st"},Pc={class:"st-bar"},Rc={class:"st-find"},Ic=["innerHTML"],Uc=["placeholder","aria-label"],Dc=["aria-label","title"],Hc=["innerHTML"],Fc=["aria-label"],jc=["aria-level","aria-expanded","aria-selected","tabindex","title","onClick","onFocus"],Vc=["innerHTML"],Bc={class:"st-cap"},zc={class:"st-sec-label"},Kc={class:"st-count"},Wc=["innerHTML","onClick"],qc={key:1,class:"st-twisty-sp","aria-hidden":"true"},Yc=["innerHTML"],Gc={class:"st-label"},Jc={key:2,class:"st-part"},Xc={class:"st-desc"},Zc={key:3,class:"st-count"},Qc={key:0,class:"st-empty"};function ep(e,t,o,n,s,l){return e.collapsed?(L(),E("div",Nc,[(L(!0),E(Y,null,Te(e.sections,i=>(L(),E("button",{key:i.id,type:"button",class:he(["st-act",{active:i.id===e.activeSectionId}]),"aria-label":e.str("openSection",{cap:"CAP "+i.cap,label:i.label}),onClick:r=>e.openSectionFromActivity(i.id)},[x("span",{class:"st-act-ic","aria-hidden":"true",innerHTML:e.svg(i.icon)},null,8,Cc),x("span",Ac,T(i.cap),1)],10,Mc))),128))])):(L(),E("div",Oc,[x("div",Pc,[x("label",Rc,[x("span",{class:"st-find-ic","aria-hidden":"true",innerHTML:e.svg("search")},null,8,Ic),ft(x("input",{ref:"find",type:"search",class:"st-find-in","onUpdate:modelValue":t[0]||(t[0]=i=>e.query=i),placeholder:e.str("filterPlaceholder"),"aria-label":e.str("filterPlaceholder"),onKeydown:t[1]||(t[1]=(...i)=>e.onFindKey&&e.onFindKey(...i))},null,40,Uc),[[En,e.query]])]),x("button",{type:"button",class:"st-tool","aria-label":e.str("collapseAll"),title:e.str("collapseAll"),onClick:t[2]||(t[2]=(...i)=>e.collapseAll&&e.collapseAll(...i))},[x("span",{"aria-hidden":"true",innerHTML:e.collapseAllSvg},null,8,Hc)],8,Dc)]),x("div",{class:"st-tree",role:"tree","aria-label":e.str("treeLabel"),onKeydown:t[3]||(t[3]=(...i)=>e.onTreeKey&&e.onTreeKey(...i))},[(L(!0),E(Y,null,Te(e.visible,i=>(L(),E("div",{key:i.id,ref_for:!0,ref:r=>e.setRow(i.id,r),role:"treeitem","aria-level":i.level,"aria-expanded":i.expandable?String(e.isOpen(i)):null,"aria-selected":i.section?null:String(i.target===e.active),tabindex:i.id===e.focusId?0:-1,class:he(e.rowClass(i)),style:kt({"--st-depth":i.depth}),title:i.title||null,onClick:r=>e.onRowClick(i),onFocus:r=>e.focusId=i.id},[i.section?(L(),E(Y,{key:0},[x("span",{class:"st-twisty","aria-hidden":"true",innerHTML:e.chev},null,8,Vc),x("span",Bc,"CAP "+T(i.cap),1),x("span",zc,T(i.label),1),x("span",Kc,T(i.count),1)],64)):(L(),E(Y,{key:1},[(L(!0),E(Y,null,Te(i.depth,r=>(L(),E("span",{key:r,class:"st-guide",style:kt({"--st-g":r-1}),"aria-hidden":"true"},null,4))),128)),i.expandable?(L(),E("span",{key:0,class:"st-twisty","aria-hidden":"true",innerHTML:e.chev,onClick:Ei(r=>e.toggle(i),["stop"])},null,8,Wc)):(L(),E("span",qc)),x("span",{class:"st-ic","aria-hidden":"true",innerHTML:e.svg(i.icon)},null,8,Yc),x("span",Gc,T(i.label),1),i.part?(L(),E("span",Jc,T(e.str("part")),1)):J("",!0),x("span",Xc,T(i.desc),1),i.expandable?(L(),E("span",Zc,T(i.kids),1)):J("",!0)],64))],46,jc))),128)),e.query&&!e.visible.length?(L(),E("p",Qc,T(e.str("noMatch",{query:e.query})),1)):J("",!0)],40,Fc)]))}const tp=Nn(Tc,[["render",ep],["styles",[wc]]]),Mn=[{code:"en",name:"English",nativeName:"English",flag:"🇺🇸"},{code:"es",name:"Spanish",nativeName:"Español",flag:"🇪🇸"},{code:"fr",name:"French",nativeName:"Français",flag:"🇫🇷"},{code:"pt",name:"Portuguese",nativeName:"Português",flag:"🇵🇹"},{code:"ar",name:"Arabic",nativeName:"العربية",flag:"🇸🇦",dir:"rtl"},{code:"ru",name:"Russian",nativeName:"Русский",flag:"🇷🇺"},{code:"de",name:"German",nativeName:"Deutsch",flag:"🇩🇪",hideFromSwitcher:!0},{code:"hi",name:"Hindi",nativeName:"हिन्दी",flag:"🇮🇳",hideFromSwitcher:!0}],op=Mn.filter(e=>!e.hideFromSwitcher);function np(e){return Mn.find(t=>t.code===e)}function Cn(e){const t=String(e||"").split("-")[0].toLowerCase();return Mn.some(o=>o.code===t)?t:"en"}const Oi="doxa-maps:locale";function sp(){try{const e=localStorage.getItem(Oi);if(e)return Cn(e)}catch{}return Cn(navigator==null?void 0:navigator.language)}function ip(e){try{localStorage.setItem(Oi,e)}catch{}}const lp=":host{display:block;position:relative;height:var(--plugin-studio-height, 85vh);min-height:480px}:host([hidden]){display:none}.ps-logo{display:inline-flex;align-items:center}.ps-logo svg{display:block}.ps-count{color:#6d766e}.ps-token{position:relative;display:inline-flex}.ps-token-dot{width:8px;height:8px;border-radius:50%;background:#b8bdb4}.ps-token-dot.is-yours,.ps-token-dot.is-url{background:#92b195}.ps-token-dot.is-built{background:#c9b36a}.ps-token-dot.is-none{background:#d27a6a}.ps-token-panel{position:absolute;top:100%;inset-inline-end:0;margin-top:4px;z-index:30;width:min(340px,90vw);display:flex;flex-direction:column;gap:8px;padding:12px;border:1px solid #d1d4cd;border-radius:10px;background:#fff;box-shadow:0 12px 32px #00000040;color:#3b463d;font-size:12px}.ps-token-label{display:flex;flex-direction:column;gap:4px}.ps-token-input{font:inherit;font-size:12px;padding:6px 8px;border:1px solid #d1d4cd;border-radius:7px;background:#f8f8f6;color:#3b463d;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.ps-token-row{display:flex;gap:8px}.ps-token-apply,.ps-token-clear{font:inherit;font-size:12px;padding:5px 12px;border-radius:7px;border:1px solid #d1d4cd;background:#d1d4cd;color:#3b463d;cursor:pointer}.ps-token-apply{background:#92b195;border-color:#92b195;color:#fff}.ps-token-apply:disabled{opacity:.6;cursor:progress}.ps-token-status{margin:0}.ps-token-head{margin:0;font-weight:600;font-size:12px}.ps-token-status.err{color:#b3412f}.ps-token-note{margin:0;color:#6d766e}.ps-locale{position:relative;display:inline-flex}.ps-locale-btn{display:inline-flex;align-items:center;gap:6px;font:inherit;font-size:12px;padding:4px 9px;border:1px solid #d1d4cd;border-radius:8px;background:#d1d4cd;color:#3b463d;cursor:pointer}.ps-locale-btn:hover{background:#d1d4cd}.ps-flag{font-size:13px;line-height:1}.ps-locale-name{max-width:11ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ps-dots{color:#6d766e}.ps-locale-menu{position:absolute;top:100%;right:0;margin-top:4px;z-index:30;min-width:180px;list-style:none;padding:6px;border:1px solid #d1d4cd;border-radius:10px;background:#fff;box-shadow:0 12px 32px #00000080;max-height:320px;overflow:auto}.ps-locale-opt{display:flex;align-items:center;gap:8px;width:100%;text-align:left;font:inherit;font-size:13px;padding:7px 9px;border:0;border-radius:7px;background:none;color:#3b463d;cursor:pointer}.ps-locale-opt:hover,.ps-locale-opt.active{background:#d1d4cd;color:#3b463d}.ps-locale-opt.active .ps-locale-name{color:#92b195}.ps-locale-en{margin-left:auto;font-size:11px;color:#6d766e}.ps-toolbar{display:flex;flex-direction:column;gap:8px;padding:10px 12px 0}.ps-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.ps-bar-t{flex:1 1 auto;min-width:0}.ps-bar-t b{font-size:15px;color:#3b463d}.ps-bar-sub{display:block;font-size:12px;color:#6d766e}.ps-btn{font:inherit;font-size:12px;padding:5px 10px;border:1px solid #d1d4cd;border-radius:8px;background:#d1d4cd;color:#3b463d;cursor:pointer;white-space:nowrap}.ps-btn:hover{background:#d1d4cd}.ps-embed{display:flex;flex-direction:column;gap:6px;padding:8px;border:1px solid #d1d4cd;border-radius:8px;background:#f3f3f1}.ps-embed label{font-size:12px;color:#6d766e;display:flex;justify-content:space-between;align-items:center}.ps-embed textarea{width:100%;height:58px;font:11px/1.35 ui-monospace,Menlo,monospace;border:1px solid #d1d4cd;border-radius:6px;background:#fff;color:#3b463d;padding:6px;resize:vertical}.ps-stage{position:absolute;top:10px;right:12px;bottom:12px;left:12px;border:1px solid #d1d4cd;border-radius:10px;overflow:hidden;background:#f3f3f1}.ps-pane{position:absolute;top:0;right:0;bottom:0;left:0;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;--profile-min-h: 0}.ps-slot{position:relative;min-height:100%}.ps-slot>*{min-height:0;max-height:none}.ps-hint{position:absolute;top:0;right:0;bottom:0;left:0;display:grid;place-items:center;text-align:center;color:#6d766e;font-size:13px;padding:20px}.ps-hint.err{color:#f87171}.ps-hint small{color:#6d766e}",Mo="plugin-studio:mapbox-token",Co="plugin-studio:mapbox-token-url",Pi="plugin-studio:map-provider";function jt(e){try{return typeof window<"u"&&window.localStorage.getItem(e)||""}catch{return""}}function Vt(e,t){try{t?window.localStorage.setItem(e,t):window.localStorage.removeItem(e)}catch{}}const rp=` +`,Aa=go({name:"VerticalTabsLayout",props:{active:{type:String,default:""},title:{type:String,default:""},items:{type:Array,default:()=>[]},groups:{type:Array,default:()=>[]},navLabel:{type:String,default:"Sections"},labels:{type:Object,default:()=>({})},initialNavOpen:{type:Boolean,default:!0},header:{type:Boolean,default:!0},initialRailCollapsed:{type:Boolean,default:!1}},emits:["update:active"],setup(){Tn(Ca,"vertical-tabs-layout")},data(){return{navOpen:this.initialNavOpen,railCollapsed:this.initialRailCollapsed,_tabEls:{},folderOverride:{},narrow:!1,_mql:null,_onMedia:null,activatedKeys:{},hostState:{},hostError:{},_hostSlots:{},_hostMounted:{}}},computed:{railShown(){return this.narrow?this.navOpen:!0},allRows(){if(this.groups&&this.groups.length){const e=[];for(const t of this.groups)for(const o of t.rows||[])e.push(o);return e}return this.items},orderedKeys(){return this.allRows.map(e=>e.key)},elementRows(){return this.allRows.filter(e=>e&&e.element)},mountedElementRows(){return this.elementRows.filter(e=>this.activatedKeys[e.key])}},watch:{active:{immediate:!0,handler(e){this.activateElement(e)}}},mounted(){try{window.matchMedia&&(this._mql=window.matchMedia("(max-width: 760px)"),this.narrow=this._mql.matches,this.reconcileForWidth(),this._onMedia=e=>{this.narrow=e.matches,this.reconcileForWidth()},this._mql.addEventListener?this._mql.addEventListener("change",this._onMedia):this._mql.addListener&&this._mql.addListener(this._onMedia))}catch{}this.$nextTick(()=>this.activateElement(this.active))},beforeUnmount(){try{this._mql&&this._onMedia&&(this._mql.removeEventListener?this._mql.removeEventListener("change",this._onMedia):this._mql.removeListener&&this._mql.removeListener(this._onMedia))}catch{}this._mql=null,this._onMedia=null},methods:{registerTab(e,t){t&&(this._tabEls[e]=t)},isNarrow(){return this.narrow},reconcileForWidth(){this.narrow?(this.navOpen=!1,this.railCollapsed=!1):this.navOpen=!0},label(e){const t={toggleList:"Toggle list",openViews:"Open the list of views",expandList:"Expand list",collapseList:"Collapse list",collapse:"Collapse"};return this.labels&&this.labels[e]||t[e]},expandRail(){!this.narrow&&this.railCollapsed&&(this.railCollapsed=!1)},toggleRail(){this.narrow?this.navOpen=!this.navOpen:this.railCollapsed=!this.railCollapsed},setHostSlot(e,t){t&&(this._hostSlots[e]=t,this.$nextTick(()=>this.mountHostElement(e)))},activateElement(e){!e||!this.elementRows.find(o=>o.key===e)||(this.activatedKeys[e]||(this.activatedKeys={...this.activatedKeys,[e]:!0},this.hostState={...this.hostState,[e]:"loading"}),this.$nextTick(()=>this.mountHostElement(e)))},mountHostElement(e){if(this._hostMounted[e])return;const t=this.elementRows.find(n=>n.key===e),o=this._hostSlots[e];!t||!o||(this._hostMounted[e]=!0,this.hostState={...this.hostState,[e]:"loading"},Na(t.src).then(()=>Ma(t.element)).then(()=>{o.innerHTML="";const n=document.createElement(t.element),s=t.attrs||{};for(const l in s){const i=s[l],r=i!=null&&typeof i=="object"?JSON.stringify(i):String(i);try{n[l]=i}catch{}try{n.setAttribute(l,r)}catch{}}n.style.cssText="display:block;position:absolute;inset:0;width:100%;height:100%;",o.appendChild(n),this.hostState={...this.hostState,[e]:"ready"}}).catch(n=>{this._hostMounted[e]=!1,this.hostState={...this.hostState,[e]:"error"},this.hostError={...this.hostError,[e]:n&&n.message||String(n)}}))},iconSvg(e){return e?Ai(e):""},initial(e){const t=(e||"").trim();return t?t.charAt(0).toUpperCase():"•"},isFolderClosed(e){const t=this.folderOverride[e.key];return t!=null?!t:e.open!==!0},toggleFolder(e){const t=this.folderOverride[e],o=(this.groups||[]).find(s=>s.key===e),n=t??(o&&o.open===!0);this.folderOverride={...this.folderOverride,[e]:!n}},pick(e){this.$emit("update:active",e),this.narrow&&(this.navOpen=!1)},onKey(e){const t=this.orderedKeys;if(!t.length)return;const o=t.indexOf(this.active);let n=-1;if(e.key==="ArrowDown"||e.key==="ArrowRight")n=(o+1)%t.length;else if(e.key==="ArrowUp"||e.key==="ArrowLeft")n=(o-1+t.length)%t.length;else if(e.key==="Home")n=0;else if(e.key==="End")n=t.length-1;else return;e.preventDefault();const s=t[n];this.pick(s),this.$nextTick(()=>{const l=this._tabEls[s];l&&l.focus&&l.focus()})}}}),Oa={key:0,class:"vtl-top"},Pa=["aria-expanded","aria-label"],Ra={class:"vtl-brand"},Ia={class:"vtl-title"},Ua={class:"vtl-meta"},Da=["aria-expanded","aria-label"],Ha={class:"vtl-body"},Fa=["aria-label","aria-hidden","role","aria-orientation"],ja=["aria-expanded","aria-label"],Va=["innerHTML"],Ba={class:"vtl-collapse-tx"},za=["aria-expanded","onClick"],Ka={key:0,class:"vtl-cap"},Wa={class:"vtl-tl"},qa={key:1,class:"vtl-td"},Ya=["innerHTML"],Ga={key:1,class:"vtl-tier"},Ja={key:0,class:"vtl-cap"},Xa={class:"vtl-tl"},Za={key:1,class:"vtl-td"},Qa={class:"vtl-rows"},ec=["aria-selected","tabindex","onClick"],tc=["innerHTML"],oc={key:1,class:"vtl-tab-emoji","aria-hidden":"true"},nc={key:2,class:"vtl-tab-fallback","aria-hidden":"true"},sc={class:"vtl-tab-text"},ic={class:"vtl-tab-label"},lc={key:0,class:"vtl-tab-badge"},rc={key:0,class:"vtl-tab-sub"},ac=["innerHTML"],cc=["aria-selected","tabindex","onClick"],pc=["innerHTML"],dc={key:1,class:"vtl-tab-emoji","aria-hidden":"true"},uc={key:2,class:"vtl-tab-fallback","aria-hidden":"true"},fc={class:"vtl-tab-text"},hc={class:"vtl-tab-label"},gc={key:0,class:"vtl-tab-sub"},bc={class:"vtl-main"},mc={class:"vtl-content"},vc=["hidden"],xc={key:0,class:"vtl-host-hint"},kc={key:1,class:"vtl-host-hint err"};function yc(e,t,o,n,s,l){return L(),E("div",{class:he(["vtl",{"nav-open":e.navOpen,"rail-collapsed":e.railCollapsed,"no-header":!e.header}])},[e.header?(L(),E("header",Oa,[x("button",{class:"vtl-menu",type:"button","aria-expanded":String(e.navOpen),onClick:t[0]||(t[0]=i=>e.navOpen=!e.navOpen),"aria-label":e.label("toggleList")},"☰",8,Pa),x("span",Ra,[Mt(e.$slots,"brand")]),x("b",Ia,T(e.title),1),x("span",Ua,[Mt(e.$slots,"meta")])])):J("",!0),e.narrow&&!e.header&&!e.navOpen?(L(),E("button",{key:1,class:"vtl-fab",type:"button","aria-expanded":String(e.navOpen),onClick:t[1]||(t[1]=i=>e.navOpen=!0),"aria-label":e.label("openViews")},"☰",8,Da)):J("",!0),x("div",Ha,[x("nav",{class:"vtl-nav","aria-label":e.navLabel,"aria-hidden":String(!e.railShown),role:e.$slots.rail?null:"tablist","aria-orientation":e.$slots.rail?null:"vertical"},[x("button",{type:"button",class:"vtl-collapse","aria-expanded":String(!e.railCollapsed),"aria-label":e.railCollapsed?e.label("expandList"):e.label("collapseList"),onClick:t[2]||(t[2]=(...i)=>e.toggleRail&&e.toggleRail(...i))},[x("span",{class:"vtl-collapse-ic","aria-hidden":"true",innerHTML:e.iconSvg("chevron")},null,8,Va),x("span",Ba,T(e.label("collapse")),1)],8,ja),e.$slots.rail?Mt(e.$slots,"rail",{collapsed:e.railCollapsed&&!e.narrow,expand:e.expandRail,pick:e.pick},void 0,void 0,0):e.groups&&e.groups.length?(L(!0),E(Y,{key:1},Te(e.groups,i=>(L(),E("div",{class:"vtl-grp",key:i.key},[i.folder?(L(),E("button",{key:0,type:"button",class:"vtl-tier vtl-tier-btn","aria-expanded":String(!e.isFolderClosed(i)),onClick:r=>e.toggleFolder(i.key)},[i.badge?(L(),E("span",Ka,T(i.badge),1)):J("",!0),x("span",Wa,T(i.label),1),i.count!=null?(L(),E("span",qa,T(i.count),1)):J("",!0),x("span",{class:he(["vtl-chev",{open:!e.isFolderClosed(i)}]),"aria-hidden":"true",innerHTML:e.iconSvg("chevron")},null,10,Ya)],8,za)):(L(),E("div",Ga,[i.badge?(L(),E("span",Ja,T(i.badge),1)):J("",!0),x("span",Xa,T(i.label),1),i.count!=null?(L(),E("span",Za,T(i.count),1)):J("",!0)])),ft(x("div",Qa,[(L(!0),E(Y,null,Te(i.rows,r=>(L(),E("button",{key:r.key,type:"button",role:"tab",class:he(["vtl-tab",{active:r.key===e.active}]),"aria-selected":String(r.key===e.active),tabindex:r.key===e.active?0:-1,ref_for:!0,ref:a=>e.registerTab(r.key,a),onClick:a=>e.pick(r.key),onKeydown:t[3]||(t[3]=a=>e.onKey(a))},[r.icon?(L(),E("span",{key:0,class:"vtl-tab-ic","aria-hidden":"true",innerHTML:e.iconSvg(r.icon)},null,8,tc)):r.emoji?(L(),E("span",oc,T(r.emoji),1)):(L(),E("span",nc,T(e.initial(r.label)),1)),x("span",sc,[x("span",ic,[Je(T(r.label),1),r.badge?(L(),E("span",lc,T(r.badge),1)):J("",!0)]),r.sub?(L(),E("span",rc,[x("span",{innerHTML:r.sub},null,8,ac)])):J("",!0)])],42,ec))),128))],512),[[$n,!i.folder||!e.isFolderClosed(i)]])]))),128)):(L(!0),E(Y,{key:2},Te(e.items,i=>(L(),E("button",{key:i.key,type:"button",role:"tab",class:he(["vtl-tab",{active:i.key===e.active}]),"aria-selected":String(i.key===e.active),tabindex:i.key===e.active?0:-1,ref_for:!0,ref:r=>e.registerTab(i.key,r),onClick:r=>e.pick(i.key),onKeydown:t[4]||(t[4]=r=>e.onKey(r))},[i.icon?(L(),E("span",{key:0,class:"vtl-tab-ic","aria-hidden":"true",innerHTML:e.iconSvg(i.icon)},null,8,pc)):i.emoji?(L(),E("span",dc,T(i.emoji),1)):(L(),E("span",uc,T(e.initial(i.label)),1)),x("span",fc,[x("span",hc,T(i.label),1),i.sub?(L(),E("span",gc,T(i.sub),1)):J("",!0)])],42,cc))),128))],8,Fa),x("main",bc,[e.header?Mt(e.$slots,"toolbar",{},void 0,void 0,0):J("",!0),x("div",mc,[(L(!0),E(Y,null,Te(e.mountedElementRows,i=>ft((L(),E("section",{key:i.key,class:"vtl-host",hidden:i.key!==e.active},[x("div",{class:"vtl-host-slot",ref_for:!0,ref:r=>e.setHostSlot(i.key,r)},null,512),e.hostState[i.key]==="loading"?(L(),E("div",xc,"Loading "+T(i.label)+"…",1)):e.hostState[i.key]==="error"?(L(),E("div",kc,[Je(" Could not load "+T(i.label)+".",1),t[5]||(t[5]=x("br",null,null,-1)),x("small",null,T(e.hostError[i.key]),1)])):J("",!0)],8,vc)),[[$n,i.key===e.active]])),128)),Mt(e.$slots,"default")])])])],2)}const $c=Nn(Aa,[["render",yc],["styles",[Ta]]]),wc='.st{display:flex;flex-direction:column;margin:0 -12px}.st-bar{position:sticky;top:0;z-index:3;display:flex;align-items:center;gap:6px;padding:8px 12px;background:#fff}.st-find{flex:1 1 auto;min-width:0;display:flex;align-items:center;gap:6px;height:30px;padding:0 8px;border:1px solid #d1d4cd;border-radius:8px;background:#f3f3f1;color:#6d766e}.st-find:focus-within{border-color:#92b195;background:#fff}.st-find-ic{display:inline-flex}.st-find-ic svg{width:14px;height:14px}.st-find-in{flex:1 1 auto;min-width:0;border:0;outline:0;background:none;font:inherit;font-size:13px;color:#3b463d}.st-tool{flex:0 0 auto;width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;padding:0;border:0;border-radius:8px;background:none;color:#6d766e;cursor:pointer}.st-tool:hover{background:#f3f3f1;color:#3b463d}.st-tool:focus-visible{outline:2px solid #92b195;outline-offset:-2px}.st-tool svg{width:16px;height:16px}.st-tree{display:flex;flex-direction:column;padding:0 0 8px;outline:0}.st-row{position:relative;display:flex;align-items:center;gap:6px;height:28px;box-sizing:border-box;padding-block:0;padding-inline:calc(8px + var(--st-depth) * 14px) 10px;font-size:13px;color:#3b463d;cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;outline:0}.st-row:hover{background:#f3f3f1}.st-row:focus-visible{box-shadow:inset 0 0 0 1px #92b195}.st-row.active{background:#92b1953d}.st-row.active:after{content:"";position:absolute;inset-inline-start:0;top:4px;bottom:4px;width:3px;border-start-end-radius:3px;border-end-end-radius:3px;background:#92b195}.st-row.alias.active{background:#92b1951f}.st-sec{position:sticky;top:46px;z-index:2;height:30px;margin-top:6px;background:#fff;font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:#6d766e;border-top:1px solid #eceee9}.st-sec:first-child{margin-top:0;border-top:0}.st-sec:hover{background:#fff;color:#3b463d}.st-cap{flex:0 0 auto;font:700 10px/1 system-ui,sans-serif;letter-spacing:.3px;padding:3px 6px;border-radius:999px;background:#92b195;color:#1d2a1f}.st-sec-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.st-twisty,.st-twisty-sp{flex:0 0 16px;width:16px;height:16px;display:inline-flex;align-items:center;justify-content:center;color:#6d766e}.st-twisty svg{width:14px;height:14px;transition:transform .12s ease}.st-row[aria-expanded=true]>.st-twisty svg{transform:rotate(90deg)}.st:dir(rtl) .st-twisty svg{transform:scaleX(-1)}.st:dir(rtl) .st-row[aria-expanded=true]>.st-twisty svg{transform:rotate(90deg)}.st-ic{flex:0 0 16px;display:inline-flex;color:#6d766e}.st-ic svg{width:16px;height:16px}.st-row.active .st-ic{color:#5f8a63}.st-label{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.st-desc{flex:1 1 0;min-width:0;overflow:hidden;text-overflow:ellipsis;font:11px/1 ui-monospace,Menlo,monospace;color:#8a928b}.st-part{flex:0 0 auto;font:600 9px/1 system-ui,sans-serif;text-transform:uppercase;letter-spacing:.3px;padding:2px 4px;border:1px solid #92b195;border-radius:4px;color:#5f8a63}.st-count{flex:0 0 auto;margin-inline-start:auto;min-width:18px;text-align:right;font-size:11px;font-weight:500;letter-spacing:0;color:#6d766e}.st-guide{position:absolute;top:0;bottom:0;inset-inline-start:calc(15px + var(--st-g) * 14px);border-inline-start:1px solid #dfe2db;pointer-events:none}.st-row:hover>.st-guide{border-inline-start-color:#c4c8c0}.st-empty{margin:12px;font-size:13px;color:#6d766e}.st-activity{display:flex;flex-direction:column;gap:4px}.st-act{position:relative;display:flex;align-items:center;justify-content:center;width:48px;height:44px;margin-inline-start:172px;padding:0;border:0;border-radius:10px;background:none;color:#6d766e;cursor:pointer}.st-act:hover,.st-act.active{background:#f3f3f1;color:#3b463d}.st-act:focus-visible{outline:2px solid #92b195;outline-offset:-2px}.st-act-ic{display:inline-flex}.st-act-ic svg{width:20px;height:20px}.st-act-n{position:absolute;right:6px;bottom:5px;font:700 9px/1 system-ui,sans-serif;color:#5f8a63}',Sc='.st { display:flex; flex-direction:column; margin:0 -12px; }.st-bar { position:sticky; top:0; z-index:3; display:flex; align-items:center; gap:6px; padding:8px 12px 8px; background:#ffffff; }.st-find { flex:1 1 auto; min-width:0; display:flex; align-items:center; gap:6px; height:30px; padding:0 8px; border:1px solid #d1d4cd; border-radius:8px; background:#f3f3f1; color:#6d766e; }.st-find:focus-within { border-color:#92b195; background:#ffffff; }.st-find-ic { display:inline-flex; } .st-find-ic svg { width:14px; height:14px; }.st-find-in { flex:1 1 auto; min-width:0; border:0; outline:0; background:none; font:inherit; font-size:13px; color:#3b463d; }.st-tool { flex:0 0 auto; width:30px; height:30px; display:inline-flex; align-items:center; justify-content:center; padding:0; border:0; border-radius:8px; background:none; color:#6d766e; cursor:pointer; }.st-tool:hover { background:#f3f3f1; color:#3b463d; } .st-tool:focus-visible { outline:2px solid #92b195; outline-offset:-2px; }.st-tool svg { width:16px; height:16px; }.st-tree { display:flex; flex-direction:column; padding:0 0 8px; outline:0; }.st-row { position:relative; display:flex; align-items:center; gap:6px; height:28px; box-sizing:border-box; padding-block:0; padding-inline:calc(8px + var(--st-depth) * 14px) 10px; font-size:13px; color:#3b463d; cursor:pointer; white-space:nowrap; user-select:none; outline:0; }.st-row:hover { background:#f3f3f1; }.st-row:focus-visible { box-shadow:inset 0 0 0 1px #92b195; }.st-row.active { background:rgba(146,177,149,.24); }.st-row.active::after { content:""; position:absolute; inset-inline-start:0; top:4px; bottom:4px; width:3px; border-start-end-radius:3px; border-end-end-radius:3px; background:#92b195; }.st-row.alias.active { background:rgba(146,177,149,.12); }.st-sec { position:sticky; top:46px; z-index:2; height:30px; margin-top:6px; background:#ffffff; font-size:11px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:#6d766e; border-top:1px solid #eceee9; }.st-sec:first-child { margin-top:0; border-top:0; }.st-sec:hover { background:#ffffff; color:#3b463d; }.st-cap { flex:0 0 auto; font:700 10px/1 system-ui,sans-serif; letter-spacing:.3px; padding:3px 6px; border-radius:999px; background:#92b195; color:#1d2a1f; }.st-sec-label { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; }.st-twisty, .st-twisty-sp { flex:0 0 16px; width:16px; height:16px; display:inline-flex; align-items:center; justify-content:center; color:#6d766e; }.st-twisty svg { width:14px; height:14px; transition:transform .12s ease; }.st-row[aria-expanded="true"] > .st-twisty svg { transform:rotate(90deg); }.st:dir(rtl) .st-twisty svg { transform:scaleX(-1); } .st:dir(rtl) .st-row[aria-expanded="true"] > .st-twisty svg { transform:rotate(90deg); }.st-ic { flex:0 0 16px; display:inline-flex; color:#6d766e; } .st-ic svg { width:16px; height:16px; }.st-row.active .st-ic { color:#5f8a63; }.st-label { flex:0 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; }.st-desc { flex:1 1 0; min-width:0; overflow:hidden; text-overflow:ellipsis; font:11px/1 ui-monospace,Menlo,monospace; color:#8a928b; }.st-part { flex:0 0 auto; font:600 9px/1 system-ui,sans-serif; text-transform:uppercase; letter-spacing:.3px; padding:2px 4px; border:1px solid #92b195; border-radius:4px; color:#5f8a63; }.st-count { flex:0 0 auto; margin-inline-start:auto; min-width:18px; text-align:right; font-size:11px; font-weight:500; letter-spacing:0; color:#6d766e; }.st-guide { position:absolute; top:0; bottom:0; inset-inline-start:calc(15px + var(--st-g) * 14px); border-inline-start:1px solid #dfe2db; pointer-events:none; }.st-row:hover > .st-guide { border-inline-start-color:#c4c8c0; }.st-empty { margin:12px; font-size:13px; color:#6d766e; }.st-activity { display:flex; flex-direction:column; gap:4px; }.st-act { position:relative; display:flex; align-items:center; justify-content:center; width:48px; height:44px; margin-inline-start:172px; padding:0; border:0; border-radius:10px; background:none; color:#6d766e; cursor:pointer; }.st-act:hover, .st-act.active { background:#f3f3f1; color:#3b463d; }.st-act:focus-visible { outline:2px solid #92b195; outline-offset:-2px; }.st-act-ic { display:inline-flex; } .st-act-ic svg { width:20px; height:20px; }.st-act-n { position:absolute; right:6px; bottom:5px; font:700 9px/1 system-ui,sans-serif; color:#5f8a63; }',_c='',Lc={filterPlaceholder:"Filter plugins",collapseAll:"Collapse all",noMatch:"No plugin matches “{query}”.",treeLabel:"Plugins by capability",openSection:"Open {cap}, {label}",part:"part"},Ec='',Tc=go({name:"StudioTree",props:{sections:{type:Array,default:()=>[]},active:{type:String,default:""},collapsed:{type:Boolean,default:!1},pick:{type:Function,default:null},expand:{type:Function,default:null},strings:{type:Object,default:()=>({})}},emits:["select"],setup(){Tn(Sc,"studio-tree")},data(){const e=this.readState();return{query:"",focusId:"",secClosed:e.sec||{},nodeOpen:e.node||{},chev:Ec,collapseAllSvg:_c}},computed:{byId(){const e=new Map;for(const t of this.sections)for(const o of t.nodes)e.set(o.id,o);return e},q(){return this.query.trim().toLowerCase()},kept(){if(!this.q)return null;const e=new Set;for(const t of this.sections)for(const o of t.nodes){if(!(o.label.toLowerCase().includes(this.q)||String(o.desc||"").toLowerCase().includes(this.q)))continue;e.add(o.id);let s=o.parent;for(;s;)e.add(s),s=this.byId.get(s)&&this.byId.get(s).parent}return e},visible(){const e=[];for(const t of this.sections){const o=this.kept?t.nodes.filter(n=>this.kept.has(n.id)):t.nodes;if(!(this.kept&&!o.length)&&(e.push({id:"sec:"+t.id,section:!0,sectionId:t.id,level:1,depth:0,expandable:!0,cap:t.cap,label:t.label,title:t.title||"",count:t.nodes.filter(n=>n.depth===0).length}),!(!this.kept&&this.secClosed[t.id])))for(const n of o)!this.kept&&!this.ancestorsOpen(n)||e.push({...n,sectionId:t.id,level:n.depth+2,expandable:!!n.kids,alias:n.depth>0})}return e},activeSectionId(){for(const e of this.sections)if(e.nodes.some(t=>t.depth===0&&t.target===this.active))return e.id;return""}},watch:{active:{handler(){this.reveal()},immediate:!0},sections(){this.reveal()},query(e){e&&this.visible.length&&(this.focusId=this.visible[0].id)}},methods:{svg(e){return e?Ai(e):""},str(e,t){let o=this.strings[e]||Lc[e]||e;if(t)for(const n in t)o=o.split("{"+n+"}").join(String(t[n]));return o},storageKey(){return"dx-studio-tree:"+(typeof location<"u"&&location.pathname||"")},readState(){try{return JSON.parse(window.localStorage.getItem(this.storageKey())||"{}")||{}}catch{return{}}},saveState(){try{window.localStorage.setItem(this.storageKey(),JSON.stringify({sec:this.secClosed,node:this.nodeOpen}))}catch{}},ancestorsOpen(e){let t=e.parent;for(;t;){if(!this.nodeOpen[t])return!1;t=this.byId.get(t)&&this.byId.get(t).parent}return!0},isOpen(e){return this.kept?!0:e.section?!this.secClosed[e.sectionId]:!!this.nodeOpen[e.id]},setOpen(e,t){e.section?this.secClosed={...this.secClosed,[e.sectionId]:!t}:this.nodeOpen={...this.nodeOpen,[e.id]:t},this.saveState()},toggle(e){e.expandable&&this.setOpen(e,!this.isOpen(e))},collapseAll(){this.nodeOpen={},this.saveState();const e=this.visible.find(t=>!t.section&&t.target===this.active&&t.depth===0);e&&(this.focusId=e.id)},rowClass(e){return{"st-row":!0,"st-sec":!!e.section,active:!e.section&&e.target===this.active,alias:!!e.alias}},setRow(e,t){this._rows||(this._rows={}),t?this._rows[e]=t:delete this._rows[e]},activate(e){if(e.section){this.toggle(e);return}e.expandable&&!this.isOpen(e)&&this.setOpen(e,!0),e.target&&(this.pick?this.pick(e.target):this.$emit("select",e.target))},onRowClick(e){this.focusId=e.id,this.activate(e)},focusRow(e){e&&(this.focusId=e,this.$nextTick(()=>{const t=this._rows&&this._rows[e];t&&(t.focus({preventScroll:!0}),t.scrollIntoView({block:"nearest"}))}))},onTreeKey(e){const t=this.visible;if(!t.length)return;const o=Math.max(0,t.findIndex(l=>l.id===this.focusId)),n=t[o],s=l=>l.section?"":l.parent||"sec:"+l.sectionId;switch(e.key){case"ArrowDown":this.focusRow((t[o+1]||n).id);break;case"ArrowUp":this.focusRow((t[o-1]||n).id);break;case"Home":this.focusRow(t[0].id);break;case"End":this.focusRow(t[t.length-1].id);break;case"ArrowRight":if(!n.expandable)break;this.isOpen(n)?t[o+1]&&t[o+1].level>n.level&&this.focusRow(t[o+1].id):this.setOpen(n,!0);break;case"ArrowLeft":n.expandable&&this.isOpen(n)&&!this.kept?this.setOpen(n,!1):this.focusRow(s(n));break;case"Enter":case" ":this.activate(n);break;default:if(e.key.length===1&&/\S/.test(e.key)&&!e.ctrlKey&&!e.metaKey&&!e.altKey){const l=e.key.toLowerCase(),r=t.slice(o+1).concat(t.slice(0,o+1)).find(a=>String(a.label).toLowerCase().startsWith(l));r&&this.focusRow(r.id);break}return}e.preventDefault()},onFindKey(e){e.key==="Escape"?(this.query="",e.preventDefault()):e.key==="ArrowDown"&&this.visible.length&&(this.focusRow(this.visible[0].id),e.preventDefault())},reveal(){if(!this.active||!this.sections.length)return;let e=null;for(const t of this.sections)if(e=t.nodes.find(o=>o.depth===0&&o.target===this.active),e){this.secClosed[t.id]&&(this.secClosed={...this.secClosed,[t.id]:!1},this.saveState());break}e&&((!this.focusId||!this.byId.has(this.focusId)||this.byId.get(this.focusId).target!==this.active)&&(this.focusId=e.id),this.$nextTick(()=>{const t=this._rows&&this._rows[e.id];t&&t.scrollIntoView&&t.scrollIntoView({block:"nearest"})}))},openSectionFromActivity(e){this.secClosed[e]&&(this.secClosed={...this.secClosed,[e]:!1},this.saveState()),this.expand&&this.expand(),this.$nextTick(()=>this.focusRow("sec:"+e))}}}),Nc={key:0,class:"st-activity"},Mc=["aria-label","onClick"],Cc=["innerHTML"],Ac={class:"st-act-n","aria-hidden":"true"},Oc={key:1,class:"st"},Pc={class:"st-bar"},Rc={class:"st-find"},Ic=["innerHTML"],Uc=["placeholder","aria-label"],Dc=["aria-label","title"],Hc=["innerHTML"],Fc=["aria-label"],jc=["aria-level","aria-expanded","aria-selected","tabindex","title","onClick","onFocus"],Vc=["innerHTML"],Bc={class:"st-cap"},zc={class:"st-sec-label"},Kc={class:"st-count"},Wc=["innerHTML","onClick"],qc={key:1,class:"st-twisty-sp","aria-hidden":"true"},Yc=["innerHTML"],Gc={class:"st-label"},Jc={key:2,class:"st-part"},Xc={class:"st-desc"},Zc={key:3,class:"st-count"},Qc={key:0,class:"st-empty"};function ep(e,t,o,n,s,l){return e.collapsed?(L(),E("div",Nc,[(L(!0),E(Y,null,Te(e.sections,i=>(L(),E("button",{key:i.id,type:"button",class:he(["st-act",{active:i.id===e.activeSectionId}]),"aria-label":e.str("openSection",{cap:"CAP "+i.cap,label:i.label}),onClick:r=>e.openSectionFromActivity(i.id)},[x("span",{class:"st-act-ic","aria-hidden":"true",innerHTML:e.svg(i.icon)},null,8,Cc),x("span",Ac,T(i.cap),1)],10,Mc))),128))])):(L(),E("div",Oc,[x("div",Pc,[x("label",Rc,[x("span",{class:"st-find-ic","aria-hidden":"true",innerHTML:e.svg("search")},null,8,Ic),ft(x("input",{ref:"find",type:"search",class:"st-find-in","onUpdate:modelValue":t[0]||(t[0]=i=>e.query=i),placeholder:e.str("filterPlaceholder"),"aria-label":e.str("filterPlaceholder"),onKeydown:t[1]||(t[1]=(...i)=>e.onFindKey&&e.onFindKey(...i))},null,40,Uc),[[En,e.query]])]),x("button",{type:"button",class:"st-tool","aria-label":e.str("collapseAll"),title:e.str("collapseAll"),onClick:t[2]||(t[2]=(...i)=>e.collapseAll&&e.collapseAll(...i))},[x("span",{"aria-hidden":"true",innerHTML:e.collapseAllSvg},null,8,Hc)],8,Dc)]),x("div",{class:"st-tree",role:"tree","aria-label":e.str("treeLabel"),onKeydown:t[3]||(t[3]=(...i)=>e.onTreeKey&&e.onTreeKey(...i))},[(L(!0),E(Y,null,Te(e.visible,i=>(L(),E("div",{key:i.id,ref_for:!0,ref:r=>e.setRow(i.id,r),role:"treeitem","aria-level":i.level,"aria-expanded":i.expandable?String(e.isOpen(i)):null,"aria-selected":i.section?null:String(i.target===e.active),tabindex:i.id===e.focusId?0:-1,class:he(e.rowClass(i)),style:kt({"--st-depth":i.depth}),title:i.title||null,onClick:r=>e.onRowClick(i),onFocus:r=>e.focusId=i.id},[i.section?(L(),E(Y,{key:0},[x("span",{class:"st-twisty","aria-hidden":"true",innerHTML:e.chev},null,8,Vc),x("span",Bc,"CAP "+T(i.cap),1),x("span",zc,T(i.label),1),x("span",Kc,T(i.count),1)],64)):(L(),E(Y,{key:1},[(L(!0),E(Y,null,Te(i.depth,r=>(L(),E("span",{key:r,class:"st-guide",style:kt({"--st-g":r-1}),"aria-hidden":"true"},null,4))),128)),i.expandable?(L(),E("span",{key:0,class:"st-twisty","aria-hidden":"true",innerHTML:e.chev,onClick:Ei(r=>e.toggle(i),["stop"])},null,8,Wc)):(L(),E("span",qc)),x("span",{class:"st-ic","aria-hidden":"true",innerHTML:e.svg(i.icon)},null,8,Yc),x("span",Gc,T(i.label),1),i.part?(L(),E("span",Jc,T(e.str("part")),1)):J("",!0),x("span",Xc,T(i.desc),1),i.expandable?(L(),E("span",Zc,T(i.kids),1)):J("",!0)],64))],46,jc))),128)),e.query&&!e.visible.length?(L(),E("p",Qc,T(e.str("noMatch",{query:e.query})),1)):J("",!0)],40,Fc)]))}const tp=Nn(Tc,[["render",ep],["styles",[wc]]]),Mn=[{code:"en",name:"English",nativeName:"English",flag:"🇺🇸"},{code:"es",name:"Spanish",nativeName:"Español",flag:"🇪🇸"},{code:"fr",name:"French",nativeName:"Français",flag:"🇫🇷"},{code:"pt",name:"Portuguese",nativeName:"Português",flag:"🇵🇹"},{code:"ar",name:"Arabic",nativeName:"العربية",flag:"🇸🇦",dir:"rtl"},{code:"ru",name:"Russian",nativeName:"Русский",flag:"🇷🇺"},{code:"de",name:"German",nativeName:"Deutsch",flag:"🇩🇪",hideFromSwitcher:!0},{code:"hi",name:"Hindi",nativeName:"हिन्दी",flag:"🇮🇳",hideFromSwitcher:!0}],op=Mn.filter(e=>!e.hideFromSwitcher);function np(e){return Mn.find(t=>t.code===e)}function Cn(e){const t=String(e||"").split("-")[0].toLowerCase();return Mn.some(o=>o.code===t)?t:"en"}const Oi="doxa-maps:locale";function sp(){try{const e=localStorage.getItem(Oi);if(e)return Cn(e)}catch{}return Cn(navigator==null?void 0:navigator.language)}function ip(e){try{localStorage.setItem(Oi,e)}catch{}}const lp=":host{display:block;position:relative;height:var(--plugin-studio-height, 85vh);min-height:480px}:host([hidden]){display:none}.ps-logo{display:inline-flex;align-items:center}.ps-logo svg{display:block}.ps-count{color:#6d766e}.vtl-top{position:relative;z-index:3}.vtl-body{isolation:isolate}.ps-token{position:relative;display:inline-flex}.ps-token-dot{width:8px;height:8px;border-radius:50%;background:#b8bdb4}.ps-token-dot.is-yours,.ps-token-dot.is-url{background:#92b195}.ps-token-dot.is-built{background:#c9b36a}.ps-token-dot.is-none{background:#d27a6a}.ps-token-panel{position:absolute;top:100%;inset-inline-end:0;margin-top:4px;z-index:30;width:min(340px,90vw);display:flex;flex-direction:column;gap:8px;padding:12px;border:1px solid #d1d4cd;border-radius:10px;background:#fff;box-shadow:0 12px 32px #00000040;color:#3b463d;font-size:12px}.ps-token-label{display:flex;flex-direction:column;gap:4px}.ps-token-input{font:inherit;font-size:12px;padding:6px 8px;border:1px solid #d1d4cd;border-radius:7px;background:#f8f8f6;color:#3b463d;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.ps-token-row{display:flex;gap:8px}.ps-token-apply,.ps-token-clear{font:inherit;font-size:12px;padding:5px 12px;border-radius:7px;border:1px solid #d1d4cd;background:#d1d4cd;color:#3b463d;cursor:pointer}.ps-token-apply{background:#92b195;border-color:#92b195;color:#fff}.ps-token-apply:disabled{opacity:.6;cursor:progress}.ps-token-status{margin:0}.ps-token-head{margin:0;font-weight:600;font-size:12px}.ps-token-status.err{color:#b3412f}.ps-token-note{margin:0;color:#6d766e}.ps-locale{position:relative;display:inline-flex}.ps-locale-btn{display:inline-flex;align-items:center;gap:6px;font:inherit;font-size:12px;padding:4px 9px;border:1px solid #d1d4cd;border-radius:8px;background:#d1d4cd;color:#3b463d;cursor:pointer}.ps-locale-btn:hover{background:#d1d4cd}.ps-flag{font-size:13px;line-height:1}.ps-locale-name{max-width:11ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ps-dots{color:#6d766e}.ps-locale-menu{position:absolute;top:100%;right:0;margin-top:4px;z-index:30;min-width:180px;list-style:none;padding:6px;border:1px solid #d1d4cd;border-radius:10px;background:#fff;box-shadow:0 12px 32px #00000080;max-height:320px;overflow:auto}.ps-locale-opt{display:flex;align-items:center;gap:8px;width:100%;text-align:left;font:inherit;font-size:13px;padding:7px 9px;border:0;border-radius:7px;background:none;color:#3b463d;cursor:pointer}.ps-locale-opt:hover,.ps-locale-opt.active{background:#d1d4cd;color:#3b463d}.ps-locale-opt.active .ps-locale-name{color:#92b195}.ps-locale-en{margin-left:auto;font-size:11px;color:#6d766e}.ps-toolbar{display:flex;flex-direction:column;gap:8px;padding:10px 12px 0}.ps-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.ps-bar-t{flex:1 1 auto;min-width:0}.ps-bar-t b{font-size:15px;color:#3b463d}.ps-bar-sub{display:block;font-size:12px;color:#6d766e}.ps-btn{font:inherit;font-size:12px;padding:5px 10px;border:1px solid #d1d4cd;border-radius:8px;background:#d1d4cd;color:#3b463d;cursor:pointer;white-space:nowrap}.ps-btn:hover{background:#d1d4cd}.ps-embed{display:flex;flex-direction:column;gap:6px;padding:8px;border:1px solid #d1d4cd;border-radius:8px;background:#f3f3f1}.ps-embed label{font-size:12px;color:#6d766e;display:flex;justify-content:space-between;align-items:center}.ps-embed textarea{width:100%;height:58px;font:11px/1.35 ui-monospace,Menlo,monospace;border:1px solid #d1d4cd;border-radius:6px;background:#fff;color:#3b463d;padding:6px;resize:vertical}.ps-stage{position:absolute;top:10px;right:12px;bottom:12px;left:12px;border:1px solid #d1d4cd;border-radius:10px;overflow:hidden;background:#f3f3f1}.ps-pane{position:absolute;top:0;right:0;bottom:0;left:0;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;--profile-min-h: 0}.ps-slot{position:relative;min-height:100%}.ps-slot>*{min-height:0;max-height:none}.ps-hint{position:absolute;top:0;right:0;bottom:0;left:0;display:grid;place-items:center;text-align:center;color:#6d766e;font-size:13px;padding:20px}.ps-hint.err{color:#f87171}.ps-hint small{color:#6d766e}",Mo="plugin-studio:mapbox-token",Co="plugin-studio:mapbox-token-url",Pi="plugin-studio:map-provider";function jt(e){try{return typeof window<"u"&&window.localStorage.getItem(e)||""}catch{return""}}function Vt(e,t){try{t?window.localStorage.setItem(e,t):window.localStorage.removeItem(e)}catch{}}const rp=` :host { display:block; position:relative; height:var(--plugin-studio-height, 85vh); min-height:480px; } :host([hidden]) { display:none; } .ps-logo { display:inline-flex; align-items:center; } .ps-logo svg { display:block; } .ps-count { color:#6d766e; } +/* THE HEADER'S MENUS SIT ABOVE EVERY PLUGIN (Driver, 2026-09-25: "the Mapbox token menu is not on top … + it is underlapping the plugins"). The header row and the pane area are siblings with no stacking context + of their own, so a menu at z-index 30 competed with each plugin's own chrome (a filter bar at 55, a drawer + at 1000) and lost. The header now stacks above the content, and the pane area is ISOLATED: whatever + z-index a plugin uses stays inside its pane. */ +.vtl-top { position:relative; z-index:3; } +.vtl-body { isolation:isolate; } .ps-token { position:relative; display:inline-flex; } .ps-token-dot { width:8px; height:8px; border-radius:50%; background:#b8bdb4; } .ps-token-dot.is-yours, .ps-token-dot.is-url { background:#92b195; } diff --git a/public/dx-maps/plugin-studio/plugin-studio/index.html b/public/dx-maps/plugin-studio/plugin-studio/index.html index 0f8bd5b28..5854900f6 100644 --- a/public/dx-maps/plugin-studio/plugin-studio/index.html +++ b/public/dx-maps/plugin-studio/plugin-studio/index.html @@ -26,7 +26,7 @@ - + - +