@fasl-work/caos-app-shell 0.13.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { APP_ROUTE, LANDING_ROUTE } from './chunk-33WTUQ7Q.js';
4
4
  export { APP_ROUTE, LANDING_ROUTE, LANG_STORAGE_KEY, STANDARD_ROUTES, THEME_BOOT_SCRIPT, THEME_STORAGE_KEY } from './chunk-33WTUQ7Q.js';
5
5
  import { createContext, Component, useState, useRef, useId, useEffect, useCallback, useContext, useMemo, useLayoutEffect, Children } from 'react';
6
6
  import { useInRouterContext, NavLink, useLocation, Navigate, Link } from 'react-router';
7
- import { Sun, Moon, Languages, Boxes, CodeXml, Globe, Briefcase, Info } from 'lucide-react';
7
+ import { Sun, Moon, Languages, Boxes, CodeXml, Globe, Briefcase, Info, ChevronLeft, ChevronRight, Maximize2, RotateCw } from 'lucide-react';
8
8
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
9
  import katex from 'katex';
10
10
 
@@ -483,8 +483,9 @@ function WorkbenchShell({
483
483
  overlays
484
484
  ] });
485
485
  }
486
- function FocusShell({ stage, rail, title, description, hud, onExit, exitLabel, stageLabel }) {
487
- return /* @__PURE__ */ jsxs("main", { className: "caos-focus-shell", children: [
486
+ function FocusShell({ stage, rail, title, description, hud, onExit, exitLabel, stageLabel, landmark = true }) {
487
+ const Root = landmark ? "main" : "div";
488
+ return /* @__PURE__ */ jsxs(Root, { className: "caos-focus-shell", "data-focus-shell": "", children: [
488
489
  /* @__PURE__ */ jsxs("section", { className: "caos-focus-stage", "aria-label": stageLabel, children: [
489
490
  /* @__PURE__ */ jsx("div", { className: "caos-focus-instrument", children: stage }),
490
491
  /* @__PURE__ */ jsxs("div", { className: "caos-focus-label", children: [
@@ -498,10 +499,10 @@ function FocusShell({ stage, rail, title, description, hud, onExit, exitLabel, s
498
499
  /* @__PURE__ */ jsxs("div", { className: "caos-focus-actions", children: [
499
500
  /* @__PURE__ */ jsx(LanguageToggle, {}),
500
501
  /* @__PURE__ */ jsx(ThemeToggle, {}),
501
- /* @__PURE__ */ jsx("button", { type: "button", onClick: onExit, children: exitLabel })
502
+ /* @__PURE__ */ jsx("button", { type: "button", "data-focus-exit": "", onClick: onExit, children: exitLabel })
502
503
  ] })
503
504
  ] }),
504
- /* @__PURE__ */ jsx("aside", { className: "caos-focus-rail", children: rail })
505
+ /* @__PURE__ */ jsx("aside", { className: "caos-focus-rail", "data-rail": "", children: rail })
505
506
  ] });
506
507
  }
507
508
 
@@ -805,6 +806,8 @@ function CaseSelector(props) {
805
806
  function caseKindOf(c) {
806
807
  return c.kind ?? "synthetic";
807
808
  }
809
+ var ViewMemoryContext = createContext(null);
810
+ var memoryKey = (kind, ariaLabel, ids) => `${kind}|${ariaLabel ?? ""}|${ids.join(",")}`;
808
811
  var CitationsContext = createContext({ list: [], byId: {} });
809
812
  function CitationsProvider({ items, children }) {
810
813
  const byId = {};
@@ -938,7 +941,12 @@ var MAX_PEER_TABS = 6;
938
941
  function Tabs({ tabs, initial, value, onChange, ariaLabel }) {
939
942
  const baseId = useId();
940
943
  const first = tabs[0]?.id ?? "";
941
- const [own, setOwn] = useState(initial ?? first);
944
+ const memory = useContext(ViewMemoryContext);
945
+ const mkey = memoryKey("tabs", ariaLabel, tabs.map((t2) => t2.id));
946
+ const [own, setOwn] = useState(() => {
947
+ const kept = memory?.get(mkey);
948
+ return kept && tabs.some((t2) => t2.id === kept) ? kept : initial ?? first;
949
+ });
942
950
  const active = value ?? own;
943
951
  const rowRef = useRef(null);
944
952
  useOverflowFade(rowRef, ".tab.active", [active, tabs.length]);
@@ -947,6 +955,7 @@ function Tabs({ tabs, initial, value, onChange, ariaLabel }) {
947
955
  }
948
956
  function select(id) {
949
957
  if (value === void 0) setOwn(id);
958
+ memory?.set(mkey, id);
950
959
  onChange?.(id);
951
960
  }
952
961
  function onKeyDown(e, idx) {
@@ -1003,7 +1012,12 @@ function Tabs({ tabs, initial, value, onChange, ariaLabel }) {
1003
1012
  function SubTabs({ tabs, initial, value, onChange, ariaLabel, orientation = "horizontal" }) {
1004
1013
  const baseId = useId();
1005
1014
  const first = tabs[0]?.id ?? "";
1006
- const [own, setOwn] = useState(initial ?? first);
1015
+ const memory = useContext(ViewMemoryContext);
1016
+ const mkey = memoryKey("subtabs", ariaLabel, tabs.map((t2) => t2.id));
1017
+ const [own, setOwn] = useState(() => {
1018
+ const kept = memory?.get(mkey);
1019
+ return kept && tabs.some((t2) => t2.id === kept) ? kept : initial ?? first;
1020
+ });
1007
1021
  const active = value ?? own;
1008
1022
  const vertical = orientation === "vertical";
1009
1023
  const rowRef = useRef(null);
@@ -1013,6 +1027,7 @@ function SubTabs({ tabs, initial, value, onChange, ariaLabel, orientation = "hor
1013
1027
  }
1014
1028
  function select(id) {
1015
1029
  if (value === void 0) setOwn(id);
1030
+ memory?.set(mkey, id);
1016
1031
  onChange?.(id);
1017
1032
  }
1018
1033
  function onKeyDown(e, idx) {
@@ -2125,6 +2140,216 @@ var ONWARD_LINKS = {
2125
2140
  technical: { en: "How the work explores this problem", es: "C\xF3mo explora el trabajo este problema" },
2126
2141
  app: { en: "Open the App", es: "Abrir la App" }
2127
2142
  };
2143
+ var useIsoLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
2144
+ var FLIP_LIMITS = {
2145
+ title: { en: 8, es: 10 },
2146
+ back: { en: 40, es: 50 }
2147
+ };
2148
+ var FLIP_TEXT = {
2149
+ show: { en: "Show details", es: "Mostrar detalles" },
2150
+ hide: { en: "Hide details", es: "Ocultar detalles" }
2151
+ };
2152
+ function FlipCard({ title, label, children, id, defaultOpen = false, open: controlled, onOpenChange, className, lang: forced }) {
2153
+ const stored = useShellLang();
2154
+ const lang = forced ?? stored;
2155
+ const auto = useId().replace(/:/g, "");
2156
+ const base = id ?? `flip-${auto}`;
2157
+ const backId = `${base}-back`;
2158
+ const [own, setOwn] = useState(defaultOpen);
2159
+ const open = controlled ?? own;
2160
+ const front = useRef(null);
2161
+ const back = useRef(null);
2162
+ const byUser = useRef(false);
2163
+ const frontFace = useRef(null);
2164
+ const backFace = useRef(null);
2165
+ useIsoLayoutEffect(() => {
2166
+ frontFace.current?.toggleAttribute("inert", open);
2167
+ backFace.current?.toggleAttribute("inert", !open);
2168
+ }, [open]);
2169
+ useEffect(() => {
2170
+ if (!byUser.current) return;
2171
+ (open ? back : front).current?.focus({ preventScroll: true });
2172
+ }, [open]);
2173
+ function turn(next) {
2174
+ byUser.current = true;
2175
+ if (controlled === void 0) setOwn(next);
2176
+ onOpenChange?.(next);
2177
+ }
2178
+ function onKeyDown(e) {
2179
+ if (e.key === "Escape" && open) {
2180
+ e.preventDefault();
2181
+ e.stopPropagation();
2182
+ turn(false);
2183
+ }
2184
+ }
2185
+ const icon = /* @__PURE__ */ jsx(RotateCw, { size: 14, "aria-hidden": "true", className: "caos-flip-icon" });
2186
+ return /* @__PURE__ */ jsx("div", { className: ["caos-flip", className].filter(Boolean).join(" "), "data-flip-card": "", "data-flip-open": open ? "true" : "false", onKeyDown, children: /* @__PURE__ */ jsxs("div", { className: "caos-flip-inner", children: [
2187
+ /* @__PURE__ */ jsx("div", { className: "caos-flip-face caos-flip-front", "aria-hidden": open ? true : void 0, ref: frontFace, children: /* @__PURE__ */ jsxs(
2188
+ "button",
2189
+ {
2190
+ type: "button",
2191
+ className: "caos-flip-toggle",
2192
+ "aria-expanded": open,
2193
+ "aria-controls": backId,
2194
+ "data-flip-toggle": "front",
2195
+ ref: front,
2196
+ onClick: () => turn(true),
2197
+ children: [
2198
+ /* @__PURE__ */ jsx("span", { className: "caos-flip-title", "data-flip-title": "", children: title }),
2199
+ icon,
2200
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: FLIP_TEXT.show[lang] })
2201
+ ]
2202
+ }
2203
+ ) }),
2204
+ /* @__PURE__ */ jsxs("div", { className: "caos-flip-face caos-flip-back", id: backId, "data-flip-back": "", "aria-hidden": open ? void 0 : true, ref: backFace, children: [
2205
+ /* @__PURE__ */ jsxs(
2206
+ "button",
2207
+ {
2208
+ type: "button",
2209
+ className: "caos-flip-toggle caos-flip-return",
2210
+ "aria-expanded": open,
2211
+ "aria-controls": backId,
2212
+ "data-flip-toggle": "back",
2213
+ ref: back,
2214
+ onClick: () => turn(false),
2215
+ children: [
2216
+ label ? /* @__PURE__ */ jsx("span", { className: "caos-flip-return-title", children: label }) : /* @__PURE__ */ jsx("span", {}),
2217
+ icon,
2218
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: FLIP_TEXT.hide[lang] })
2219
+ ]
2220
+ }
2221
+ ),
2222
+ /* @__PURE__ */ jsx("div", { className: "caos-flip-content", "data-flip-content": "", children })
2223
+ ] })
2224
+ ] }) });
2225
+ }
2226
+ var DECK_TEXT = {
2227
+ previous: { en: "Previous", es: "Anterior" },
2228
+ next: { en: "Next", es: "Siguiente" },
2229
+ of: { en: "of", es: "de" },
2230
+ to: { en: "to", es: "a" }
2231
+ };
2232
+ var MIN_CARD_REM = 15;
2233
+ var GAP_REM = 0.9;
2234
+ var SWIPE_PX = 50;
2235
+ function perPageFor(width, minCard, gap) {
2236
+ if (!(width > 0)) return 1;
2237
+ return Math.max(1, Math.floor((width + gap) / (minCard + gap)));
2238
+ }
2239
+ function deckWindow(n, per, start) {
2240
+ const p = Math.max(1, Math.floor(per));
2241
+ if (p >= n) return { start: 0, end: n };
2242
+ const s = Math.min(Math.max(0, start), n - p);
2243
+ return { start: s, end: s + p };
2244
+ }
2245
+ function deckPosition(start, shown, n, lang) {
2246
+ const o = DECK_TEXT.of[lang];
2247
+ if (shown <= 1) return `${start + 1} ${o} ${n}`;
2248
+ return `${start + 1} ${DECK_TEXT.to[lang]} ${start + shown} ${o} ${n}`;
2249
+ }
2250
+ var useIsoLayoutEffect2 = typeof window === "undefined" ? useEffect : useLayoutEffect;
2251
+ function CardDeck({ cards, flip = false, carouselAbove = 4, ariaLabel, className, lang: forced }) {
2252
+ const stored = useShellLang();
2253
+ const lang = forced ?? stored;
2254
+ const n = cards.length;
2255
+ const carousel = n > carouselAbove;
2256
+ const rootRef = useRef(null);
2257
+ const [per, setPer] = useState(1);
2258
+ const [start, setStart] = useState(0);
2259
+ const [moved, setMoved] = useState(false);
2260
+ const touch = useRef(null);
2261
+ useIsoLayoutEffect2(() => {
2262
+ const root = rootRef.current;
2263
+ if (!carousel || !root || typeof ResizeObserver === "undefined") return void 0;
2264
+ const measure = () => {
2265
+ const rem = parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
2266
+ setPer(perPageFor(root.clientWidth, MIN_CARD_REM * rem, GAP_REM * rem));
2267
+ };
2268
+ measure();
2269
+ const ro = new ResizeObserver(measure);
2270
+ ro.observe(root);
2271
+ return () => ro.disconnect();
2272
+ }, [carousel, n]);
2273
+ const win = deckWindow(n, carousel ? per : n, start);
2274
+ const shown = win.end - win.start;
2275
+ const controls = carousel && shown < n;
2276
+ const atStart = win.start <= 0;
2277
+ const atEnd = win.end >= n;
2278
+ function go(to, step = false) {
2279
+ if (!controls) return;
2280
+ setMoved(true);
2281
+ setStart(deckWindow(n, per, step ? win.start + to : to).start);
2282
+ }
2283
+ const page = () => Math.max(1, per);
2284
+ useEffect(() => {
2285
+ const root = rootRef.current;
2286
+ const a = document.activeElement;
2287
+ if (!root || !a || !root.contains(a) || a.checkVisibility?.({ checkVisibilityCSS: true }) !== false) return;
2288
+ (root.querySelector('[data-deck-card]:not([hidden]) [data-flip-card][data-flip-open="false"] [data-flip-toggle="front"], [data-deck-card]:not([hidden]) [data-flip-card][data-flip-open="true"] [data-flip-toggle="back"]') ?? root.querySelector("[data-deck-next]"))?.focus({ preventScroll: true });
2289
+ }, [win.start, win.end]);
2290
+ function onKeyDown(e) {
2291
+ if (!controls || e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return;
2292
+ const t2 = e.target;
2293
+ if (t2.closest('input, select, textarea, [contenteditable="true"]')) return;
2294
+ if (e.key === "ArrowRight") go(1, true);
2295
+ else if (e.key === "ArrowLeft") go(-1, true);
2296
+ else if (e.key === "Home") go(0);
2297
+ else if (e.key === "End") go(n);
2298
+ else return;
2299
+ e.preventDefault();
2300
+ }
2301
+ function onTouchStart(e) {
2302
+ const t2 = e.touches[0];
2303
+ touch.current = t2 ? { x: t2.clientX, y: t2.clientY } : null;
2304
+ }
2305
+ function onTouchEnd(e) {
2306
+ const from = touch.current;
2307
+ const t2 = e.changedTouches[0];
2308
+ touch.current = null;
2309
+ if (!from || !t2 || !controls) return;
2310
+ const dx = t2.clientX - from.x;
2311
+ const dy = t2.clientY - from.y;
2312
+ if (Math.abs(dx) >= SWIPE_PX && Math.abs(dx) > 2 * Math.abs(dy)) go(dx < 0 ? page() : -page(), true);
2313
+ }
2314
+ return /* @__PURE__ */ jsxs(
2315
+ "div",
2316
+ {
2317
+ ref: rootRef,
2318
+ className: "caos-deck",
2319
+ "data-card-deck": "",
2320
+ "data-deck-mode": carousel ? "carousel" : "row",
2321
+ "data-deck-per": carousel ? Math.min(per, n) : n,
2322
+ "data-deck-flip": flip ? "" : void 0,
2323
+ "data-deck-moved": moved ? "" : void 0,
2324
+ role: carousel ? "group" : void 0,
2325
+ "aria-roledescription": carousel ? "carousel" : void 0,
2326
+ "aria-label": carousel ? ariaLabel : void 0,
2327
+ onKeyDown: carousel ? onKeyDown : void 0,
2328
+ children: [
2329
+ /* @__PURE__ */ jsx(
2330
+ "ul",
2331
+ {
2332
+ className: ["caos-deck-track", className].filter(Boolean).join(" "),
2333
+ "data-deck-track": "",
2334
+ "aria-label": carousel ? void 0 : ariaLabel,
2335
+ style: carousel ? { "--deck-per": Math.min(per, n) } : void 0,
2336
+ onTouchStart: carousel ? onTouchStart : void 0,
2337
+ onTouchEnd: carousel ? onTouchEnd : void 0,
2338
+ children: cards.map((c, i) => /* @__PURE__ */ jsx("li", { className: "caos-deck-card", "data-deck-card": "", hidden: carousel && (i < win.start || i >= win.end), ...c.attrs, children: flip ? /* @__PURE__ */ jsx(FlipCard, { title: c.title, label: c.label, lang, children: c.body }) : /* @__PURE__ */ jsxs("div", { className: "caos-card", children: [
2339
+ /* @__PURE__ */ jsx("strong", { className: "caos-card-title", children: c.title }),
2340
+ c.body
2341
+ ] }) }, c.id ?? i))
2342
+ }
2343
+ ),
2344
+ controls && /* @__PURE__ */ jsxs("div", { className: "caos-deck-bar", children: [
2345
+ /* @__PURE__ */ jsx("button", { type: "button", className: "caos-deck-btn", "data-deck-prev": "", "aria-label": DECK_TEXT.previous[lang], "aria-disabled": atStart, onClick: () => !atStart && go(-page(), true), children: /* @__PURE__ */ jsx(ChevronLeft, { size: 18, "aria-hidden": "true" }) }),
2346
+ /* @__PURE__ */ jsx("span", { className: "caos-deck-position", "data-deck-position": "", "aria-live": "polite", children: deckPosition(win.start, shown, n, lang) }),
2347
+ /* @__PURE__ */ jsx("button", { type: "button", className: "caos-deck-btn", "data-deck-next": "", "aria-label": DECK_TEXT.next[lang], "aria-disabled": atEnd, onClick: () => !atEnd && go(page(), true), children: /* @__PURE__ */ jsx(ChevronRight, { size: 18, "aria-hidden": "true" }) })
2348
+ ] })
2349
+ ]
2350
+ }
2351
+ );
2352
+ }
2128
2353
  var UI = {
2129
2354
  bearers: { en: "Who bears it", es: "Qui\xE9n lo soporta" },
2130
2355
  cost: { en: "Cost", es: "Costo" },
@@ -2188,6 +2413,9 @@ function cleanSvg(text) {
2188
2413
  const root = doc.documentElement;
2189
2414
  if (!root || root.localName !== "svg" || doc.querySelector("parsererror")) return null;
2190
2415
  for (const bad of Array.from(doc.querySelectorAll("script, foreignObject"))) bad.remove();
2416
+ for (const anim of Array.from(doc.querySelectorAll("animate, set"))) {
2417
+ if (/^(xlink:)?href$|^on/i.test(anim.getAttribute("attributeName") ?? "")) anim.remove();
2418
+ }
2191
2419
  for (const el of Array.from(doc.querySelectorAll("*"))) {
2192
2420
  for (const attr of Array.from(el.attributes)) {
2193
2421
  if (/^on/i.test(attr.name) || /^(xlink:)?href$/i.test(attr.name) && /^\s*javascript:/i.test(attr.value)) el.removeAttribute(attr.name);
@@ -2199,10 +2427,41 @@ function cleanSvg(text) {
2199
2427
  if (!root.getAttribute("role")) root.setAttribute("role", "img");
2200
2428
  return new XMLSerializer().serializeToString(root);
2201
2429
  }
2430
+ function smilEnd(svg) {
2431
+ const secs = (v) => {
2432
+ const m = /^\s*(\d*\.?\d+)\s*(ms|s)?\s*$/.exec(v ?? "");
2433
+ return m ? Number(m[1]) / (m[2] === "ms" ? 1e3 : 1) : 0;
2434
+ };
2435
+ let end = 0;
2436
+ for (const a of Array.from(svg.querySelectorAll("animate, animateTransform, animateMotion, set"))) {
2437
+ end = Math.max(end, secs(a.getAttribute("begin")) + secs(a.getAttribute("dur")));
2438
+ }
2439
+ return end;
2440
+ }
2441
+ function useStillSmil(host, svg) {
2442
+ useEffect(() => {
2443
+ const el = host.current?.querySelector("svg");
2444
+ if (!svg || !el || typeof matchMedia === "undefined" || !el.querySelector("animate, animateTransform, animateMotion, set")) return void 0;
2445
+ const mq = matchMedia("(prefers-reduced-motion: reduce)");
2446
+ const apply = () => {
2447
+ if (mq.matches) {
2448
+ el.setCurrentTime(smilEnd(el));
2449
+ el.pauseAnimations();
2450
+ } else {
2451
+ el.unpauseAnimations();
2452
+ }
2453
+ };
2454
+ apply();
2455
+ mq.addEventListener("change", apply);
2456
+ return () => mq.removeEventListener("change", apply);
2457
+ }, [host, svg]);
2458
+ }
2202
2459
  function Illustration({ name, spec, base, lang }) {
2203
2460
  const file = typeof spec.file === "string" ? spec.file : spec.file[lang];
2204
2461
  const url = /^([a-z]+:)?\/\//i.test(file) || file.startsWith("/") ? file : `${base}${file}`;
2205
2462
  const [loaded, setLoaded] = useState({ status: "loading" });
2463
+ const art = useRef(null);
2464
+ useStillSmil(art, loaded.status === "ready" ? loaded.svg : null);
2206
2465
  useEffect(() => {
2207
2466
  let live = true;
2208
2467
  setLoaded({ status: "loading" });
@@ -2229,7 +2488,7 @@ function Illustration({ name, spec, base, lang }) {
2229
2488
  "data-exec-file": file,
2230
2489
  "data-state": loaded.status === "ready" ? void 0 : loaded.status,
2231
2490
  children: [
2232
- loaded.status === "ready" ? /* @__PURE__ */ jsx("div", { className: "caos-exec-art", dangerouslySetInnerHTML: { __html: loaded.svg } }) : loaded.status === "error" ? /* @__PURE__ */ jsxs("p", { className: "caos-exec-fig-error", role: "alert", children: [
2491
+ loaded.status === "ready" ? /* @__PURE__ */ jsx("div", { className: "caos-exec-art", ref: art, dangerouslySetInnerHTML: { __html: loaded.svg } }) : loaded.status === "error" ? /* @__PURE__ */ jsxs("p", { className: "caos-exec-fig-error", role: "alert", children: [
2233
2492
  t(UI.missing, lang),
2234
2493
  ": ",
2235
2494
  /* @__PURE__ */ jsx("code", { children: file }),
@@ -2311,19 +2570,32 @@ function ExecutiveViewBody({ data, base = "/", lang: forced, open = 1, onOpenTec
2311
2570
  art("pain"),
2312
2571
  /* @__PURE__ */ jsx("ul", { className: "caos-exec-figures", children: d.pain.figures.map((f, i) => /* @__PURE__ */ jsx(Figure2, { f, path: `pain.figures[${i}]`, kind: "pain.figures" }, i)) }),
2313
2572
  /* @__PURE__ */ jsx("h3", { className: "caos-exec-sub", children: t(UI.bearers, lang) }),
2314
- /* @__PURE__ */ jsx("ul", { className: "caos-exec-cards", children: d.pain.bearers.map((b, i) => /* @__PURE__ */ jsxs("li", { className: "caos-exec-card", "data-exec-item": "pain.bearers", children: [
2315
- /* @__PURE__ */ jsx("strong", { children: field(`pain.bearers[${i}].who`, b.who) }),
2316
- /* @__PURE__ */ jsxs("div", { children: [
2317
- /* @__PURE__ */ jsx(Label, { children: t(UI.cost, lang) }),
2318
- " ",
2319
- field(`pain.bearers[${i}].cost`, b.cost)
2320
- ] }),
2321
- /* @__PURE__ */ jsxs("div", { children: [
2322
- /* @__PURE__ */ jsx(Label, { children: t(UI.measure, lang) }),
2323
- " ",
2324
- field(`pain.bearers[${i}].measure`, b.measure)
2325
- ] })
2326
- ] }, i)) }),
2573
+ /* @__PURE__ */ jsx(
2574
+ CardDeck,
2575
+ {
2576
+ flip: true,
2577
+ ariaLabel: t(UI.bearers, lang),
2578
+ className: "caos-exec-cards",
2579
+ cards: d.pain.bearers.map((b, i) => ({
2580
+ id: i,
2581
+ title: field(`pain.bearers[${i}].who`, b.who),
2582
+ label: t(b.who, lang),
2583
+ attrs: { "data-exec-item": "pain.bearers" },
2584
+ body: /* @__PURE__ */ jsxs(Fragment, { children: [
2585
+ /* @__PURE__ */ jsxs("div", { children: [
2586
+ /* @__PURE__ */ jsx(Label, { children: t(UI.cost, lang) }),
2587
+ " ",
2588
+ field(`pain.bearers[${i}].cost`, b.cost)
2589
+ ] }),
2590
+ /* @__PURE__ */ jsxs("div", { children: [
2591
+ /* @__PURE__ */ jsx(Label, { children: t(UI.measure, lang) }),
2592
+ " ",
2593
+ field(`pain.bearers[${i}].measure`, b.measure)
2594
+ ] })
2595
+ ] })
2596
+ }))
2597
+ }
2598
+ ),
2327
2599
  /* @__PURE__ */ jsx("h3", { className: "caos-exec-sub", children: t(UI.causes, lang) }),
2328
2600
  /* @__PURE__ */ jsx("ul", { className: "caos-exec-causes", children: d.pain.causes.map((c, i) => /* @__PURE__ */ jsxs("li", { className: "caos-exec-cause", "data-exec-item": "pain.causes", "data-exec-weight": c.weight, children: [
2329
2601
  /* @__PURE__ */ jsxs("div", { className: "caos-exec-cause-head", children: [
@@ -2359,38 +2631,66 @@ function ExecutiveViewBody({ data, base = "/", lang: forced, open = 1, onOpenTec
2359
2631
  // 3. the current situation
2360
2632
  /* @__PURE__ */ jsxs(Section, { n: 3, lang, keyMessage: d.situation.key_message, keyPath: "situation.key_message", children: [
2361
2633
  /* @__PURE__ */ jsx("h3", { className: "caos-exec-sub", children: t(UI.roles, lang) }),
2362
- /* @__PURE__ */ jsx("ul", { className: "caos-exec-cards", children: d.situation.roles.map((r, i) => /* @__PURE__ */ jsxs("li", { className: "caos-exec-card", "data-exec-item": "situation.roles", children: [
2363
- /* @__PURE__ */ jsx("strong", { children: field(`situation.roles[${i}].role`, r.role) }),
2364
- /* @__PURE__ */ jsxs("div", { children: [
2365
- /* @__PURE__ */ jsx(Label, { children: t(UI.decides, lang) }),
2366
- " ",
2367
- field(`situation.roles[${i}].decides`, r.decides)
2368
- ] }),
2369
- /* @__PURE__ */ jsxs("div", { children: [
2370
- /* @__PURE__ */ jsx(Label, { children: t(UI.needs, lang) }),
2371
- " ",
2372
- field(`situation.roles[${i}].needs`, r.needs)
2373
- ] })
2374
- ] }, i)) }),
2634
+ /* @__PURE__ */ jsx(
2635
+ CardDeck,
2636
+ {
2637
+ flip: true,
2638
+ ariaLabel: t(UI.roles, lang),
2639
+ className: "caos-exec-cards",
2640
+ cards: d.situation.roles.map((r, i) => ({
2641
+ id: i,
2642
+ title: field(`situation.roles[${i}].role`, r.role),
2643
+ label: t(r.role, lang),
2644
+ attrs: { "data-exec-item": "situation.roles" },
2645
+ body: /* @__PURE__ */ jsxs(Fragment, { children: [
2646
+ /* @__PURE__ */ jsxs("div", { children: [
2647
+ /* @__PURE__ */ jsx(Label, { children: t(UI.decides, lang) }),
2648
+ " ",
2649
+ field(`situation.roles[${i}].decides`, r.decides)
2650
+ ] }),
2651
+ /* @__PURE__ */ jsxs("div", { children: [
2652
+ /* @__PURE__ */ jsx(Label, { children: t(UI.needs, lang) }),
2653
+ " ",
2654
+ field(`situation.roles[${i}].needs`, r.needs)
2655
+ ] })
2656
+ ] })
2657
+ }))
2658
+ }
2659
+ ),
2375
2660
  /* @__PURE__ */ jsx("p", { className: "caos-exec-interaction", children: field("situation.interaction", d.situation.interaction) }),
2376
2661
  art("situation"),
2377
2662
  /* @__PURE__ */ jsx("h3", { className: "caos-exec-sub", children: t(UI.sota, lang) }),
2378
- /* @__PURE__ */ jsx("ul", { className: "caos-exec-cards", children: d.situation.state_of_the_art.map((a, i) => /* @__PURE__ */ jsxs("li", { className: "caos-exec-card", "data-exec-item": "situation.state_of_the_art", children: [
2379
- /* @__PURE__ */ jsxs("strong", { children: [
2380
- field(`situation.state_of_the_art[${i}].approach`, a.approach),
2381
- /* @__PURE__ */ jsx(Cite2, { id: a.source })
2382
- ] }),
2383
- /* @__PURE__ */ jsxs("div", { children: [
2384
- /* @__PURE__ */ jsx(Label, { children: t(UI.achieves, lang) }),
2385
- " ",
2386
- field(`situation.state_of_the_art[${i}].achieves`, a.achieves)
2387
- ] }),
2388
- /* @__PURE__ */ jsxs("div", { children: [
2389
- /* @__PURE__ */ jsx(Label, { children: t(UI.fallsShort, lang) }),
2390
- " ",
2391
- field(`situation.state_of_the_art[${i}].falls_short`, a.falls_short)
2392
- ] })
2393
- ] }, i)) }),
2663
+ /* @__PURE__ */ jsx(
2664
+ CardDeck,
2665
+ {
2666
+ flip: true,
2667
+ ariaLabel: t(UI.sota, lang),
2668
+ className: "caos-exec-cards",
2669
+ cards: d.situation.state_of_the_art.map((a, i) => ({
2670
+ id: i,
2671
+ title: field(`situation.state_of_the_art[${i}].approach`, a.approach),
2672
+ label: t(a.approach, lang),
2673
+ attrs: { "data-exec-item": "situation.state_of_the_art" },
2674
+ body: /* @__PURE__ */ jsxs(Fragment, { children: [
2675
+ /* @__PURE__ */ jsxs("div", { children: [
2676
+ /* @__PURE__ */ jsx(Label, { children: t(UI.achieves, lang) }),
2677
+ " ",
2678
+ field(`situation.state_of_the_art[${i}].achieves`, a.achieves)
2679
+ ] }),
2680
+ /* @__PURE__ */ jsxs("div", { children: [
2681
+ /* @__PURE__ */ jsx(Label, { children: t(UI.fallsShort, lang) }),
2682
+ " ",
2683
+ field(`situation.state_of_the_art[${i}].falls_short`, a.falls_short)
2684
+ ] }),
2685
+ /* @__PURE__ */ jsxs("div", { children: [
2686
+ /* @__PURE__ */ jsx(Label, { children: t(UI.source, lang) }),
2687
+ " ",
2688
+ /* @__PURE__ */ jsx(Cite2, { id: a.source })
2689
+ ] })
2690
+ ] })
2691
+ }))
2692
+ }
2693
+ ),
2394
2694
  /* @__PURE__ */ jsxs("p", { className: "caos-exec-status", "data-exec-item": "situation.status", children: [
2395
2695
  /* @__PURE__ */ jsx(Label, { children: field("situation.status.heading", d.situation.status.heading) }),
2396
2696
  " ",
@@ -2434,11 +2734,24 @@ function ExecutiveViewBody({ data, base = "/", lang: forced, open = 1, onOpenTec
2434
2734
  /* @__PURE__ */ jsx(Paragraphs, { list: d.proposal.paragraphs, path: "proposal.paragraphs" }),
2435
2735
  art("proposal"),
2436
2736
  /* @__PURE__ */ jsx("h3", { className: "caos-exec-sub", children: t(UI.fourCs, lang) }),
2437
- /* @__PURE__ */ jsx("ul", { className: "caos-exec-cs", children: d.proposal.four_cs.map((r, i) => /* @__PURE__ */ jsxs("li", { className: "caos-exec-card", "data-exec-c": i, "data-exec-item": "proposal.four_cs", children: [
2438
- /* @__PURE__ */ jsx("strong", { "data-exec-field": `proposal.four_cs[${i}].c`, "data-exec-cname": "", children: t(FOUR_CS[i].name, lang) }),
2439
- /* @__PURE__ */ jsx("small", { className: "caos-exec-cdef", "data-exec-cdef": "", children: t(FOUR_CS[i].definition, lang) }),
2440
- /* @__PURE__ */ jsx("span", { children: field(`proposal.four_cs[${i}].applies`, r.applies) })
2441
- ] }, i)) }),
2737
+ /* @__PURE__ */ jsx(
2738
+ CardDeck,
2739
+ {
2740
+ flip: true,
2741
+ ariaLabel: t(UI.fourCs, lang),
2742
+ className: "caos-exec-cs",
2743
+ cards: d.proposal.four_cs.map((r, i) => ({
2744
+ id: i,
2745
+ title: /* @__PURE__ */ jsx("span", { "data-exec-field": `proposal.four_cs[${i}].c`, "data-exec-cname": "", children: t(FOUR_CS[i].name, lang) }),
2746
+ label: t(FOUR_CS[i].name, lang),
2747
+ attrs: { "data-exec-c": i, "data-exec-item": "proposal.four_cs" },
2748
+ body: /* @__PURE__ */ jsxs(Fragment, { children: [
2749
+ /* @__PURE__ */ jsx("small", { className: "caos-exec-cdef", "data-exec-cdef": "", children: t(FOUR_CS[i].definition, lang) }),
2750
+ /* @__PURE__ */ jsx("span", { children: field(`proposal.four_cs[${i}].applies`, r.applies) })
2751
+ ] })
2752
+ }))
2753
+ }
2754
+ ),
2442
2755
  /* @__PURE__ */ jsx("h3", { className: "caos-exec-sub", children: t(UI.actions, lang) }),
2443
2756
  /* @__PURE__ */ jsx("ol", { className: "caos-exec-actions", children: d.proposal.actions.map((a, i) => /* @__PURE__ */ jsxs("li", { className: "caos-exec-card", "data-exec-item": "proposal.actions", children: [
2444
2757
  /* @__PURE__ */ jsx("strong", { children: field(`proposal.actions[${i}].action`, a.action) }),
@@ -2523,6 +2836,13 @@ function replaceQueryParam(param, value) {
2523
2836
  const next = withCaseParam(window.location.search, value, param);
2524
2837
  window.history.replaceState(window.history.state, "", `${window.location.pathname}${next}${window.location.hash}`);
2525
2838
  }
2839
+ function removeQueryParam(param) {
2840
+ if (typeof window === "undefined") return;
2841
+ const p = new URLSearchParams(window.location.search);
2842
+ p.delete(param);
2843
+ const q = p.toString();
2844
+ window.history.replaceState(window.history.state, "", `${window.location.pathname}${q ? `?${q}` : ""}${window.location.hash}`);
2845
+ }
2526
2846
  function useUrlView(ids, param, value, onChange) {
2527
2847
  const [own, setOwn] = useState(
2528
2848
  () => param && typeof window !== "undefined" ? readCaseParam(window.location.search, param) : null
@@ -2787,7 +3107,7 @@ function BarChart({ title, data, axis, orientation = "horizontal", values = true
2787
3107
  function isSections(rail) {
2788
3108
  return Array.isArray(rail) && rail.every((s) => typeof s === "object" && s !== null && "id" in s && "content" in s);
2789
3109
  }
2790
- function WorkbenchLayout({ rail, railHead, children, railLabel, className }) {
3110
+ function RailBody({ rail, railHead, railLabel }) {
2791
3111
  const lang = useShellLang();
2792
3112
  const sections = isSections(rail) ? rail : null;
2793
3113
  const [active, setActive] = useState(sections?.[0]?.id ?? "");
@@ -2807,38 +3127,43 @@ function WorkbenchLayout({ rail, railHead, children, railLabel, className }) {
2807
3127
  document.getElementById(`${baseId}-rs-${target.id}`)?.focus();
2808
3128
  }
2809
3129
  }
3130
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3131
+ railHead && /* @__PURE__ */ jsx("div", { className: "caos-wb-rail-head", children: railHead }),
3132
+ sections ? /* @__PURE__ */ jsxs(Fragment, { children: [
3133
+ /* @__PURE__ */ jsx("div", { className: "caos-wb-rail-sections", role: "tablist", "aria-label": label, ref: rowRef, children: sections.map((s, idx) => /* @__PURE__ */ jsx(
3134
+ "button",
3135
+ {
3136
+ type: "button",
3137
+ role: "tab",
3138
+ id: `${baseId}-rs-${s.id}`,
3139
+ "aria-selected": s.id === current?.id,
3140
+ "aria-controls": `${baseId}-rp-${s.id}`,
3141
+ tabIndex: s.id === current?.id ? 0 : -1,
3142
+ className: s.id === current?.id ? "chip on" : "chip",
3143
+ onClick: () => setActive(s.id),
3144
+ onKeyDown: (e) => onKeyDown(e, idx),
3145
+ children: pick(s.label, lang)
3146
+ },
3147
+ s.id
3148
+ )) }),
3149
+ current && /* @__PURE__ */ jsx(
3150
+ "div",
3151
+ {
3152
+ className: "caos-wb-rail-panel",
3153
+ role: "tabpanel",
3154
+ id: `${baseId}-rp-${current.id}`,
3155
+ "aria-labelledby": `${baseId}-rs-${current.id}`,
3156
+ children: current.content
3157
+ }
3158
+ )
3159
+ ] }) : /* @__PURE__ */ jsx("div", { className: "caos-wb-rail-panel", children: rail })
3160
+ ] });
3161
+ }
3162
+ function WorkbenchLayout({ rail, railHead, children, railLabel, className }) {
3163
+ const lang = useShellLang();
3164
+ const label = pick(railLabel ?? { en: "Controls", es: "Controles" }, lang);
2810
3165
  return /* @__PURE__ */ jsxs("div", { className: ["page-body", "wide", "caos-wb", className].filter(Boolean).join(" "), "data-workbench": "", children: [
2811
- /* @__PURE__ */ jsxs("aside", { className: "caos-wb-rail", "aria-label": label, "data-rail": "", children: [
2812
- railHead && /* @__PURE__ */ jsx("div", { className: "caos-wb-rail-head", children: railHead }),
2813
- sections ? /* @__PURE__ */ jsxs(Fragment, { children: [
2814
- /* @__PURE__ */ jsx("div", { className: "caos-wb-rail-sections", role: "tablist", "aria-label": label, ref: rowRef, children: sections.map((s, idx) => /* @__PURE__ */ jsx(
2815
- "button",
2816
- {
2817
- type: "button",
2818
- role: "tab",
2819
- id: `${baseId}-rs-${s.id}`,
2820
- "aria-selected": s.id === current?.id,
2821
- "aria-controls": `${baseId}-rp-${s.id}`,
2822
- tabIndex: s.id === current?.id ? 0 : -1,
2823
- className: s.id === current?.id ? "chip on" : "chip",
2824
- onClick: () => setActive(s.id),
2825
- onKeyDown: (e) => onKeyDown(e, idx),
2826
- children: pick(s.label, lang)
2827
- },
2828
- s.id
2829
- )) }),
2830
- current && /* @__PURE__ */ jsx(
2831
- "div",
2832
- {
2833
- className: "caos-wb-rail-panel",
2834
- role: "tabpanel",
2835
- id: `${baseId}-rp-${current.id}`,
2836
- "aria-labelledby": `${baseId}-rs-${current.id}`,
2837
- children: current.content
2838
- }
2839
- )
2840
- ] }) : /* @__PURE__ */ jsx("div", { className: "caos-wb-rail-panel", children: rail })
2841
- ] }),
3166
+ /* @__PURE__ */ jsx("aside", { className: "caos-wb-rail", "aria-label": label, "data-rail": "", children: /* @__PURE__ */ jsx(RailBody, { rail, railHead, railLabel }) }),
2842
3167
  /* @__PURE__ */ jsx("section", { className: "caos-wb-main", "data-instrument": "", children })
2843
3168
  ] });
2844
3169
  }
@@ -2913,13 +3238,73 @@ function VariantBar({ variants, activeId, onSelect, title, lane }) {
2913
3238
  active?.note && /* @__PURE__ */ jsx("p", { className: "caos-variant-note", children: pick(active.note, lang) })
2914
3239
  ] });
2915
3240
  }
3241
+
3242
+ // src/workbench/focus.ts
3243
+ var FOCUS_TEXT = {
3244
+ enter: { en: "Focus", es: "Enfocar" },
3245
+ enterLabel: { en: "Open this case in focus mode", es: "Abrir este caso en modo enfoque" },
3246
+ exit: { en: "Back to the App", es: "Volver a la App" },
3247
+ stage: { en: "Focus stage", es: "Escenario de enfoque" }
3248
+ };
3249
+ var FOCUS_PARAM = "focus";
2916
3250
  var MAX_WORKBENCH_GROUPS = MAX_PEER_TABS;
2917
3251
  function panel(id, lane, provenance, content) {
2918
3252
  return /* @__PURE__ */ jsx("div", { className: "caos-cw-panel", "data-group": id, "data-lane": lane, "data-provenance": provenance, children: content });
2919
3253
  }
2920
3254
  function CaseWorkbench(props) {
2921
- const { caseId, cases, source, controls, replayOnly, variants, rail, railLabel, groups, compare, context: context2, group, onGroupChange, deepLinkView = true, ariaLabel } = props;
3255
+ const { caseId, cases, source, controls, replayOnly, variants, rail, railLabel, groups, compare, context: context2, group, onGroupChange, deepLinkView = true, ariaLabel, focus } = props;
2922
3256
  const lang = useShellLang();
3257
+ const focusCfg = focus === true ? {} : focus || null;
3258
+ const focusParam = focusCfg?.param ?? FOCUS_PARAM;
3259
+ const [focusOn, setFocusOn] = useState(() => focusCfg !== null && typeof window !== "undefined" && readCaseParam(window.location.search, focusParam) === "1");
3260
+ const enterRef = useRef(null);
3261
+ const memory = useRef(/* @__PURE__ */ new Map());
3262
+ const turned = useRef(null);
3263
+ const onChangeRef = useRef(focusCfg?.onChange);
3264
+ onChangeRef.current = focusCfg?.onChange;
3265
+ const setFocus = useCallback(
3266
+ (on) => {
3267
+ turned.current = on ? "in" : "out";
3268
+ setFocusOn(on);
3269
+ if (on) replaceQueryParam(focusParam, "1");
3270
+ else removeQueryParam(focusParam);
3271
+ onChangeRef.current?.(on);
3272
+ },
3273
+ [focusParam]
3274
+ );
3275
+ const showFocus = focusCfg !== null && focusOn;
3276
+ useEffect(() => {
3277
+ if (!showFocus) return void 0;
3278
+ const onKey = (e) => {
3279
+ if (e.key !== "Escape" || e.defaultPrevented) return;
3280
+ e.preventDefault();
3281
+ setFocus(false);
3282
+ };
3283
+ document.addEventListener("keydown", onKey);
3284
+ return () => document.removeEventListener("keydown", onKey);
3285
+ }, [showFocus, setFocus]);
3286
+ useEffect(() => {
3287
+ if (!showFocus) return void 0;
3288
+ const frame = document.querySelector("[data-focus-shell]");
3289
+ if (!frame) return void 0;
3290
+ const made = [];
3291
+ for (let node = frame; node && node !== document.body; node = node.parentElement) {
3292
+ for (const sib of Array.from(node.parentElement?.children ?? [])) {
3293
+ if (sib !== node && !sib.hasAttribute("inert") && sib.tagName !== "SCRIPT" && sib.tagName !== "STYLE") {
3294
+ sib.setAttribute("inert", "");
3295
+ made.push(sib);
3296
+ }
3297
+ }
3298
+ }
3299
+ return () => {
3300
+ for (const el of made) el.removeAttribute("inert");
3301
+ };
3302
+ }, [showFocus]);
3303
+ useEffect(() => {
3304
+ if (turned.current === "in" && showFocus) document.querySelector("[data-focus-exit]")?.focus();
3305
+ else if (turned.current === "out" && !showFocus) enterRef.current?.focus();
3306
+ turned.current = null;
3307
+ }, [showFocus]);
2923
3308
  const variantId = variants?.activeId;
2924
3309
  const stateKey = useMemo(
2925
3310
  () => makeStateKey({ caseId, variantId: variantId ?? null, source: source ?? null, controls: controls ?? {} }),
@@ -2944,32 +3329,66 @@ function CaseWorkbench(props) {
2944
3329
  );
2945
3330
  }
2946
3331
  const [openGroup, selectGroup] = useUrlView(tabs.map((t2) => t2.id), viewParamOf(deepLinkView), group, onGroupChange);
2947
- const railHead = cases || variants ? /* @__PURE__ */ jsxs(Fragment, { children: [
3332
+ const pickers = cases || variants ? /* @__PURE__ */ jsxs(Fragment, { children: [
2948
3333
  cases && /* @__PURE__ */ jsx(CaseSelector, { lang, ...cases }),
2949
3334
  variants && /* @__PURE__ */ jsx(VariantBar, { ...variants })
2950
3335
  ] }) : null;
2951
- return /* @__PURE__ */ jsx(WorkbenchStateContext.Provider, { value: { stateKey, caseId, variantId, source }, children: /* @__PURE__ */ jsx(WorkbenchLayout, { rail: rail ?? null, railHead, railLabel, children: /* @__PURE__ */ jsx(
2952
- "div",
3336
+ const enter = focusCfg && /* @__PURE__ */ jsxs(
3337
+ "button",
2953
3338
  {
2954
- className: "caos-cw",
2955
- "data-case-workbench": "",
2956
- "data-group-count": tabs.length,
2957
- "data-case": caseId,
2958
- "data-variant": variantId ?? "",
2959
- "data-source": source ?? "",
2960
- "data-state-key": stateKey,
2961
- "data-replay-only": replayOnly ? "1" : void 0,
2962
- children: /* @__PURE__ */ jsx(
2963
- Tabs,
2964
- {
2965
- tabs,
2966
- value: openGroup,
2967
- onChange: selectGroup,
2968
- ariaLabel: pick(ariaLabel ?? { en: "Case views", es: "Vistas del caso" }, lang)
2969
- }
2970
- )
3339
+ ref: enterRef,
3340
+ type: "button",
3341
+ className: "btn caos-focus-enter",
3342
+ "data-focus-enter": "",
3343
+ "aria-label": FOCUS_TEXT.enterLabel[lang],
3344
+ title: FOCUS_TEXT.enterLabel[lang],
3345
+ onClick: () => setFocus(true),
3346
+ children: [
3347
+ /* @__PURE__ */ jsx(Maximize2, { size: 14, "aria-hidden": "true" }),
3348
+ /* @__PURE__ */ jsx("span", { children: FOCUS_TEXT.enter[lang] })
3349
+ ]
3350
+ }
3351
+ );
3352
+ const railHead = pickers || enter ? /* @__PURE__ */ jsxs(Fragment, { children: [
3353
+ pickers,
3354
+ enter
3355
+ ] }) : null;
3356
+ const instrumentData = {
3357
+ "data-case-workbench": "",
3358
+ "data-group-count": tabs.length,
3359
+ "data-case": caseId,
3360
+ "data-variant": variantId ?? "",
3361
+ "data-source": source ?? "",
3362
+ "data-state-key": stateKey,
3363
+ "data-replay-only": replayOnly ? "1" : void 0
3364
+ };
3365
+ if (showFocus && focusCfg) {
3366
+ const open = tabs.find((t2) => t2.id === openGroup) ?? tabs[0];
3367
+ const caseName = cases?.cases.find((c) => c.id === caseId)?.name ?? caseId;
3368
+ return /* @__PURE__ */ jsx(ViewMemoryContext.Provider, { value: memory.current, children: /* @__PURE__ */ jsx(WorkbenchStateContext.Provider, { value: { stateKey, caseId, variantId, source }, children: /* @__PURE__ */ jsx(
3369
+ FocusShell,
3370
+ {
3371
+ title: focusCfg.title !== void 0 ? pick(focusCfg.title, lang) : caseName,
3372
+ description: focusCfg.description !== void 0 ? pick(focusCfg.description, lang) : open?.label ?? "",
3373
+ hud: (focusCfg.hud ?? []).map((h) => ({ label: pick(h.label, lang), value: h.value })),
3374
+ onExit: () => setFocus(false),
3375
+ exitLabel: FOCUS_TEXT.exit[lang],
3376
+ stageLabel: FOCUS_TEXT.stage[lang],
3377
+ landmark: false,
3378
+ stage: /* @__PURE__ */ jsx("div", { className: "caos-cw caos-cw-focus", ...instrumentData, "data-focus-group": open?.id, "data-instrument": "", children: /* @__PURE__ */ jsx("div", { className: "tabs", children: open && /* @__PURE__ */ jsx("div", { className: "tabpanel", role: "tabpanel", tabIndex: 0, "aria-label": open.label, "data-panel": open.id, children: /* @__PURE__ */ jsx(PanelBoundary, { panel: open.id, children: /* @__PURE__ */ jsx(DocRefsScope, { children: open.content }) }) }) }) }),
3379
+ rail: /* @__PURE__ */ jsx("div", { className: "caos-focus-rail-body", children: /* @__PURE__ */ jsx(RailBody, { rail: rail ?? null, railHead: pickers, railLabel }) })
3380
+ }
3381
+ ) }) });
3382
+ }
3383
+ return /* @__PURE__ */ jsx(ViewMemoryContext.Provider, { value: memory.current, children: /* @__PURE__ */ jsx(WorkbenchStateContext.Provider, { value: { stateKey, caseId, variantId, source }, children: /* @__PURE__ */ jsx(WorkbenchLayout, { rail: rail ?? null, railHead, railLabel, children: /* @__PURE__ */ jsx("div", { className: "caos-cw", ...instrumentData, children: /* @__PURE__ */ jsx(
3384
+ Tabs,
3385
+ {
3386
+ tabs,
3387
+ value: openGroup,
3388
+ onChange: selectGroup,
3389
+ ariaLabel: pick(ariaLabel ?? { en: "Case views", es: "Vistas del caso" }, lang)
2971
3390
  }
2972
- ) }) });
3391
+ ) }) }) }) });
2973
3392
  }
2974
3393
  function toneOf(it) {
2975
3394
  if (it.tone) return it.tone;
@@ -3171,4 +3590,4 @@ function ViewsRow({ children, shares }) {
3171
3590
  }) });
3172
3591
  }
3173
3592
 
3174
- export { AppShell, ArchitectureModal, BREAKPOINTS, BarChart, Callout, CaseSelector, CaseWorkbench, ChipGroup, CitationsProvider, Cite, DOC_AREAS, DocPage, DocReferences, DocRefsScope, DocSection, EXECUTIVE_SCHEMA_VERSION, Equation, ExecutiveView, FOUR_CS, Figure, FocusShell, Gauge, Glossary, InlineMath, Knob, Landing, LaneBadge, LanguageToggle, MAX_PEER_TABS, MAX_WORKBENCH_GROUPS, PanelBoundary, PlotCard, Question, Readout, ReferenceList, Refs, Stat, SubTabs, SurfacePage, TabGroups, Tabs, ThemeToggle, VariantBar, Verdict, ViewsRow, WorkbenchLayout, WorkbenchShell, WorkbenchStateContext, caseKind, caseTooltip, casesInSource, createVizLoop, firstAppearance, fitLabel, fontFamily, groupByCategory, isStale, makeStateKey, niceStep, niceTicks, readCaseParam, replaceQueryParam, sourcesPresent, textWidth, usePausedViz, useUrlView, useWorkbenchState, validateArchitectureConfig, validateExecutive, viewParamOf, widestLabel, withCaseParam };
3593
+ export { AppShell, ArchitectureModal, BREAKPOINTS, BarChart, Callout, CardDeck, CaseSelector, CaseWorkbench, ChipGroup, CitationsProvider, Cite, DECK_TEXT, DOC_AREAS, DocPage, DocReferences, DocRefsScope, DocSection, EXECUTIVE_SCHEMA_VERSION, Equation, ExecutiveView, FLIP_LIMITS, FLIP_TEXT, FOCUS_PARAM, FOCUS_TEXT, FOUR_CS, Figure, FlipCard, FocusShell, Gauge, Glossary, InlineMath, Knob, Landing, LaneBadge, LanguageToggle, MAX_PEER_TABS, MAX_WORKBENCH_GROUPS, PanelBoundary, PlotCard, Question, Readout, ReferenceList, Refs, Stat, SubTabs, SurfacePage, TabGroups, Tabs, ThemeToggle, VariantBar, Verdict, ViewsRow, WorkbenchLayout, WorkbenchShell, WorkbenchStateContext, caseKind, caseTooltip, casesInSource, createVizLoop, deckPosition, deckWindow, firstAppearance, fitLabel, fontFamily, groupByCategory, isStale, makeStateKey, niceStep, niceTicks, perPageFor, readCaseParam, removeQueryParam, replaceQueryParam, sourcesPresent, textWidth, usePausedViz, useUrlView, useWorkbenchState, validateArchitectureConfig, validateExecutive, viewParamOf, widestLabel, withCaseParam };