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

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
 
@@ -739,6 +740,17 @@ function CaseSelector(props) {
739
740
  const visible = casesInSource(cases, source);
740
741
  const groups = groupByCategory(visible);
741
742
  const modified = cases.find((c) => c.id === modifiedFromId) ?? null;
743
+ const select = /* @__PURE__ */ jsx(
744
+ "select",
745
+ {
746
+ "data-control": "case",
747
+ className: "select cs-select",
748
+ "aria-label": ariaLabel ?? "Case selector",
749
+ value: selectedId,
750
+ onChange: (e) => onSelect(e.target.value),
751
+ children: groups.map((g) => /* @__PURE__ */ jsx("optgroup", { label: g.category || " ", children: g.cases.map((c) => /* @__PURE__ */ jsx("option", { value: c.id, disabled: c.disabled, title: caseTooltip(c) || void 0, children: c.name }, c.id)) }, g.category || "_"))
752
+ }
753
+ );
742
754
  return /* @__PURE__ */ jsxs("div", { className: `cs${className ? ` ${className}` : ""}`, role: "group", "aria-label": ariaLabel ?? "Case selector", children: [
743
755
  source && lanes.length > 1 && /* @__PURE__ */ jsxs("div", { className: "cs-source", children: [
744
756
  /* @__PURE__ */ jsx("span", { className: "cs-source-label", children: t2.source }),
@@ -756,17 +768,10 @@ function CaseSelector(props) {
756
768
  )) })
757
769
  ] }),
758
770
  source && source !== "synthetic" && /* @__PURE__ */ jsx("p", { className: "cs-locked banner", children: lockedNote ?? t2.lockedNote }),
759
- layout === "select" && /* @__PURE__ */ jsx(
760
- "select",
761
- {
762
- "data-control": "case",
763
- className: "select cs-select",
764
- "aria-label": ariaLabel ?? "Case selector",
765
- value: selectedId,
766
- onChange: (e) => onSelect(e.target.value),
767
- children: groups.map((g) => /* @__PURE__ */ jsx("optgroup", { label: g.category || " ", children: g.cases.map((c) => /* @__PURE__ */ jsx("option", { value: c.id, disabled: c.disabled, title: caseTooltip(c) || void 0, children: c.name }, c.id)) }, g.category || "_"))
768
- }
769
- ),
771
+ layout === "select" && (props.accessory ? /* @__PURE__ */ jsxs("div", { className: "cs-select-row", children: [
772
+ select,
773
+ props.accessory
774
+ ] }) : select),
770
775
  layout === "chips" && groups.map((g) => /* @__PURE__ */ jsxs("div", { className: "cs-group", role: "group", "aria-label": g.category || void 0, "data-control": "case", children: [
771
776
  g.category && /* @__PURE__ */ jsx("span", { className: "cs-group-label", children: g.category }),
772
777
  /* @__PURE__ */ jsx("div", { className: "cs-chips", children: g.cases.map((c) => {
@@ -792,6 +797,7 @@ function CaseSelector(props) {
792
797
  );
793
798
  }) })
794
799
  ] }, g.category || "_")),
800
+ layout === "chips" && props.accessory,
795
801
  modified && /* @__PURE__ */ jsxs("div", { className: "cs-modified", children: [
796
802
  /* @__PURE__ */ jsxs("span", { className: "badge accent", children: [
797
803
  t2.modifiedPrefix,
@@ -805,6 +811,8 @@ function CaseSelector(props) {
805
811
  function caseKindOf(c) {
806
812
  return c.kind ?? "synthetic";
807
813
  }
814
+ var ViewMemoryContext = createContext(null);
815
+ var memoryKey = (kind, ariaLabel, ids) => `${kind}|${ariaLabel ?? ""}|${ids.join(",")}`;
808
816
  var CitationsContext = createContext({ list: [], byId: {} });
809
817
  function CitationsProvider({ items, children }) {
810
818
  const byId = {};
@@ -938,7 +946,12 @@ var MAX_PEER_TABS = 6;
938
946
  function Tabs({ tabs, initial, value, onChange, ariaLabel }) {
939
947
  const baseId = useId();
940
948
  const first = tabs[0]?.id ?? "";
941
- const [own, setOwn] = useState(initial ?? first);
949
+ const memory = useContext(ViewMemoryContext);
950
+ const mkey = memoryKey("tabs", ariaLabel, tabs.map((t2) => t2.id));
951
+ const [own, setOwn] = useState(() => {
952
+ const kept = memory?.get(mkey);
953
+ return kept && tabs.some((t2) => t2.id === kept) ? kept : initial ?? first;
954
+ });
942
955
  const active = value ?? own;
943
956
  const rowRef = useRef(null);
944
957
  useOverflowFade(rowRef, ".tab.active", [active, tabs.length]);
@@ -947,6 +960,7 @@ function Tabs({ tabs, initial, value, onChange, ariaLabel }) {
947
960
  }
948
961
  function select(id) {
949
962
  if (value === void 0) setOwn(id);
963
+ memory?.set(mkey, id);
950
964
  onChange?.(id);
951
965
  }
952
966
  function onKeyDown(e, idx) {
@@ -1003,7 +1017,12 @@ function Tabs({ tabs, initial, value, onChange, ariaLabel }) {
1003
1017
  function SubTabs({ tabs, initial, value, onChange, ariaLabel, orientation = "horizontal" }) {
1004
1018
  const baseId = useId();
1005
1019
  const first = tabs[0]?.id ?? "";
1006
- const [own, setOwn] = useState(initial ?? first);
1020
+ const memory = useContext(ViewMemoryContext);
1021
+ const mkey = memoryKey("subtabs", ariaLabel, tabs.map((t2) => t2.id));
1022
+ const [own, setOwn] = useState(() => {
1023
+ const kept = memory?.get(mkey);
1024
+ return kept && tabs.some((t2) => t2.id === kept) ? kept : initial ?? first;
1025
+ });
1007
1026
  const active = value ?? own;
1008
1027
  const vertical = orientation === "vertical";
1009
1028
  const rowRef = useRef(null);
@@ -1013,6 +1032,7 @@ function SubTabs({ tabs, initial, value, onChange, ariaLabel, orientation = "hor
1013
1032
  }
1014
1033
  function select(id) {
1015
1034
  if (value === void 0) setOwn(id);
1035
+ memory?.set(mkey, id);
1016
1036
  onChange?.(id);
1017
1037
  }
1018
1038
  function onKeyDown(e, idx) {
@@ -2125,6 +2145,216 @@ var ONWARD_LINKS = {
2125
2145
  technical: { en: "How the work explores this problem", es: "C\xF3mo explora el trabajo este problema" },
2126
2146
  app: { en: "Open the App", es: "Abrir la App" }
2127
2147
  };
2148
+ var useIsoLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
2149
+ var FLIP_LIMITS = {
2150
+ title: { en: 8, es: 10 },
2151
+ back: { en: 40, es: 50 }
2152
+ };
2153
+ var FLIP_TEXT = {
2154
+ show: { en: "Show details", es: "Mostrar detalles" },
2155
+ hide: { en: "Hide details", es: "Ocultar detalles" }
2156
+ };
2157
+ function FlipCard({ title, label, children, id, defaultOpen = false, open: controlled, onOpenChange, className, lang: forced }) {
2158
+ const stored = useShellLang();
2159
+ const lang = forced ?? stored;
2160
+ const auto = useId().replace(/:/g, "");
2161
+ const base = id ?? `flip-${auto}`;
2162
+ const backId = `${base}-back`;
2163
+ const [own, setOwn] = useState(defaultOpen);
2164
+ const open = controlled ?? own;
2165
+ const front = useRef(null);
2166
+ const back = useRef(null);
2167
+ const byUser = useRef(false);
2168
+ const frontFace = useRef(null);
2169
+ const backFace = useRef(null);
2170
+ useIsoLayoutEffect(() => {
2171
+ frontFace.current?.toggleAttribute("inert", open);
2172
+ backFace.current?.toggleAttribute("inert", !open);
2173
+ }, [open]);
2174
+ useEffect(() => {
2175
+ if (!byUser.current) return;
2176
+ (open ? back : front).current?.focus({ preventScroll: true });
2177
+ }, [open]);
2178
+ function turn(next) {
2179
+ byUser.current = true;
2180
+ if (controlled === void 0) setOwn(next);
2181
+ onOpenChange?.(next);
2182
+ }
2183
+ function onKeyDown(e) {
2184
+ if (e.key === "Escape" && open) {
2185
+ e.preventDefault();
2186
+ e.stopPropagation();
2187
+ turn(false);
2188
+ }
2189
+ }
2190
+ const icon = /* @__PURE__ */ jsx(RotateCw, { size: 14, "aria-hidden": "true", className: "caos-flip-icon" });
2191
+ 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: [
2192
+ /* @__PURE__ */ jsx("div", { className: "caos-flip-face caos-flip-front", "aria-hidden": open ? true : void 0, ref: frontFace, children: /* @__PURE__ */ jsxs(
2193
+ "button",
2194
+ {
2195
+ type: "button",
2196
+ className: "caos-flip-toggle",
2197
+ "aria-expanded": open,
2198
+ "aria-controls": backId,
2199
+ "data-flip-toggle": "front",
2200
+ ref: front,
2201
+ onClick: () => turn(true),
2202
+ children: [
2203
+ /* @__PURE__ */ jsx("span", { className: "caos-flip-title", "data-flip-title": "", children: title }),
2204
+ icon,
2205
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: FLIP_TEXT.show[lang] })
2206
+ ]
2207
+ }
2208
+ ) }),
2209
+ /* @__PURE__ */ jsxs("div", { className: "caos-flip-face caos-flip-back", id: backId, "data-flip-back": "", "aria-hidden": open ? void 0 : true, ref: backFace, children: [
2210
+ /* @__PURE__ */ jsxs(
2211
+ "button",
2212
+ {
2213
+ type: "button",
2214
+ className: "caos-flip-toggle caos-flip-return",
2215
+ "aria-expanded": open,
2216
+ "aria-controls": backId,
2217
+ "data-flip-toggle": "back",
2218
+ ref: back,
2219
+ onClick: () => turn(false),
2220
+ children: [
2221
+ label ? /* @__PURE__ */ jsx("span", { className: "caos-flip-return-title", children: label }) : /* @__PURE__ */ jsx("span", {}),
2222
+ icon,
2223
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: FLIP_TEXT.hide[lang] })
2224
+ ]
2225
+ }
2226
+ ),
2227
+ /* @__PURE__ */ jsx("div", { className: "caos-flip-content", "data-flip-content": "", children })
2228
+ ] })
2229
+ ] }) });
2230
+ }
2231
+ var DECK_TEXT = {
2232
+ previous: { en: "Previous", es: "Anterior" },
2233
+ next: { en: "Next", es: "Siguiente" },
2234
+ of: { en: "of", es: "de" },
2235
+ to: { en: "to", es: "a" }
2236
+ };
2237
+ var MIN_CARD_REM = 15;
2238
+ var GAP_REM = 0.9;
2239
+ var SWIPE_PX = 50;
2240
+ function perPageFor(width, minCard, gap) {
2241
+ if (!(width > 0)) return 1;
2242
+ return Math.max(1, Math.floor((width + gap) / (minCard + gap)));
2243
+ }
2244
+ function deckWindow(n, per, start) {
2245
+ const p = Math.max(1, Math.floor(per));
2246
+ if (p >= n) return { start: 0, end: n };
2247
+ const s = Math.min(Math.max(0, start), n - p);
2248
+ return { start: s, end: s + p };
2249
+ }
2250
+ function deckPosition(start, shown, n, lang) {
2251
+ const o = DECK_TEXT.of[lang];
2252
+ if (shown <= 1) return `${start + 1} ${o} ${n}`;
2253
+ return `${start + 1} ${DECK_TEXT.to[lang]} ${start + shown} ${o} ${n}`;
2254
+ }
2255
+ var useIsoLayoutEffect2 = typeof window === "undefined" ? useEffect : useLayoutEffect;
2256
+ function CardDeck({ cards, flip = false, carouselAbove = 4, ariaLabel, className, lang: forced }) {
2257
+ const stored = useShellLang();
2258
+ const lang = forced ?? stored;
2259
+ const n = cards.length;
2260
+ const carousel = n > carouselAbove;
2261
+ const rootRef = useRef(null);
2262
+ const [per, setPer] = useState(1);
2263
+ const [start, setStart] = useState(0);
2264
+ const [moved, setMoved] = useState(false);
2265
+ const touch = useRef(null);
2266
+ useIsoLayoutEffect2(() => {
2267
+ const root = rootRef.current;
2268
+ if (!carousel || !root || typeof ResizeObserver === "undefined") return void 0;
2269
+ const measure = () => {
2270
+ const rem = parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
2271
+ setPer(perPageFor(root.clientWidth, MIN_CARD_REM * rem, GAP_REM * rem));
2272
+ };
2273
+ measure();
2274
+ const ro = new ResizeObserver(measure);
2275
+ ro.observe(root);
2276
+ return () => ro.disconnect();
2277
+ }, [carousel, n]);
2278
+ const win = deckWindow(n, carousel ? per : n, start);
2279
+ const shown = win.end - win.start;
2280
+ const controls = carousel && shown < n;
2281
+ const atStart = win.start <= 0;
2282
+ const atEnd = win.end >= n;
2283
+ function go(to, step = false) {
2284
+ if (!controls) return;
2285
+ setMoved(true);
2286
+ setStart(deckWindow(n, per, step ? win.start + to : to).start);
2287
+ }
2288
+ const page = () => Math.max(1, per);
2289
+ useEffect(() => {
2290
+ const root = rootRef.current;
2291
+ const a = document.activeElement;
2292
+ if (!root || !a || !root.contains(a) || a.checkVisibility?.({ checkVisibilityCSS: true }) !== false) return;
2293
+ (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 });
2294
+ }, [win.start, win.end]);
2295
+ function onKeyDown(e) {
2296
+ if (!controls || e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return;
2297
+ const t2 = e.target;
2298
+ if (t2.closest('input, select, textarea, [contenteditable="true"]')) return;
2299
+ if (e.key === "ArrowRight") go(1, true);
2300
+ else if (e.key === "ArrowLeft") go(-1, true);
2301
+ else if (e.key === "Home") go(0);
2302
+ else if (e.key === "End") go(n);
2303
+ else return;
2304
+ e.preventDefault();
2305
+ }
2306
+ function onTouchStart(e) {
2307
+ const t2 = e.touches[0];
2308
+ touch.current = t2 ? { x: t2.clientX, y: t2.clientY } : null;
2309
+ }
2310
+ function onTouchEnd(e) {
2311
+ const from = touch.current;
2312
+ const t2 = e.changedTouches[0];
2313
+ touch.current = null;
2314
+ if (!from || !t2 || !controls) return;
2315
+ const dx = t2.clientX - from.x;
2316
+ const dy = t2.clientY - from.y;
2317
+ if (Math.abs(dx) >= SWIPE_PX && Math.abs(dx) > 2 * Math.abs(dy)) go(dx < 0 ? page() : -page(), true);
2318
+ }
2319
+ return /* @__PURE__ */ jsxs(
2320
+ "div",
2321
+ {
2322
+ ref: rootRef,
2323
+ className: "caos-deck",
2324
+ "data-card-deck": "",
2325
+ "data-deck-mode": carousel ? "carousel" : "row",
2326
+ "data-deck-per": carousel ? Math.min(per, n) : n,
2327
+ "data-deck-flip": flip ? "" : void 0,
2328
+ "data-deck-moved": moved ? "" : void 0,
2329
+ role: carousel ? "group" : void 0,
2330
+ "aria-roledescription": carousel ? "carousel" : void 0,
2331
+ "aria-label": carousel ? ariaLabel : void 0,
2332
+ onKeyDown: carousel ? onKeyDown : void 0,
2333
+ children: [
2334
+ /* @__PURE__ */ jsx(
2335
+ "ul",
2336
+ {
2337
+ className: ["caos-deck-track", className].filter(Boolean).join(" "),
2338
+ "data-deck-track": "",
2339
+ "aria-label": carousel ? void 0 : ariaLabel,
2340
+ style: carousel ? { "--deck-per": Math.min(per, n) } : void 0,
2341
+ onTouchStart: carousel ? onTouchStart : void 0,
2342
+ onTouchEnd: carousel ? onTouchEnd : void 0,
2343
+ 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: [
2344
+ /* @__PURE__ */ jsx("strong", { className: "caos-card-title", children: c.title }),
2345
+ c.body
2346
+ ] }) }, c.id ?? i))
2347
+ }
2348
+ ),
2349
+ controls && /* @__PURE__ */ jsxs("div", { className: "caos-deck-bar", children: [
2350
+ /* @__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" }) }),
2351
+ /* @__PURE__ */ jsx("span", { className: "caos-deck-position", "data-deck-position": "", "aria-live": "polite", children: deckPosition(win.start, shown, n, lang) }),
2352
+ /* @__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" }) })
2353
+ ] })
2354
+ ]
2355
+ }
2356
+ );
2357
+ }
2128
2358
  var UI = {
2129
2359
  bearers: { en: "Who bears it", es: "Qui\xE9n lo soporta" },
2130
2360
  cost: { en: "Cost", es: "Costo" },
@@ -2188,6 +2418,9 @@ function cleanSvg(text) {
2188
2418
  const root = doc.documentElement;
2189
2419
  if (!root || root.localName !== "svg" || doc.querySelector("parsererror")) return null;
2190
2420
  for (const bad of Array.from(doc.querySelectorAll("script, foreignObject"))) bad.remove();
2421
+ for (const anim of Array.from(doc.querySelectorAll("animate, set"))) {
2422
+ if (/^(xlink:)?href$|^on/i.test(anim.getAttribute("attributeName") ?? "")) anim.remove();
2423
+ }
2191
2424
  for (const el of Array.from(doc.querySelectorAll("*"))) {
2192
2425
  for (const attr of Array.from(el.attributes)) {
2193
2426
  if (/^on/i.test(attr.name) || /^(xlink:)?href$/i.test(attr.name) && /^\s*javascript:/i.test(attr.value)) el.removeAttribute(attr.name);
@@ -2199,10 +2432,41 @@ function cleanSvg(text) {
2199
2432
  if (!root.getAttribute("role")) root.setAttribute("role", "img");
2200
2433
  return new XMLSerializer().serializeToString(root);
2201
2434
  }
2435
+ function smilEnd(svg) {
2436
+ const secs = (v) => {
2437
+ const m = /^\s*(\d*\.?\d+)\s*(ms|s)?\s*$/.exec(v ?? "");
2438
+ return m ? Number(m[1]) / (m[2] === "ms" ? 1e3 : 1) : 0;
2439
+ };
2440
+ let end = 0;
2441
+ for (const a of Array.from(svg.querySelectorAll("animate, animateTransform, animateMotion, set"))) {
2442
+ end = Math.max(end, secs(a.getAttribute("begin")) + secs(a.getAttribute("dur")));
2443
+ }
2444
+ return end;
2445
+ }
2446
+ function useStillSmil(host, svg) {
2447
+ useEffect(() => {
2448
+ const el = host.current?.querySelector("svg");
2449
+ if (!svg || !el || typeof matchMedia === "undefined" || !el.querySelector("animate, animateTransform, animateMotion, set")) return void 0;
2450
+ const mq = matchMedia("(prefers-reduced-motion: reduce)");
2451
+ const apply = () => {
2452
+ if (mq.matches) {
2453
+ el.setCurrentTime(smilEnd(el));
2454
+ el.pauseAnimations();
2455
+ } else {
2456
+ el.unpauseAnimations();
2457
+ }
2458
+ };
2459
+ apply();
2460
+ mq.addEventListener("change", apply);
2461
+ return () => mq.removeEventListener("change", apply);
2462
+ }, [host, svg]);
2463
+ }
2202
2464
  function Illustration({ name, spec, base, lang }) {
2203
2465
  const file = typeof spec.file === "string" ? spec.file : spec.file[lang];
2204
2466
  const url = /^([a-z]+:)?\/\//i.test(file) || file.startsWith("/") ? file : `${base}${file}`;
2205
2467
  const [loaded, setLoaded] = useState({ status: "loading" });
2468
+ const art = useRef(null);
2469
+ useStillSmil(art, loaded.status === "ready" ? loaded.svg : null);
2206
2470
  useEffect(() => {
2207
2471
  let live = true;
2208
2472
  setLoaded({ status: "loading" });
@@ -2229,7 +2493,7 @@ function Illustration({ name, spec, base, lang }) {
2229
2493
  "data-exec-file": file,
2230
2494
  "data-state": loaded.status === "ready" ? void 0 : loaded.status,
2231
2495
  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: [
2496
+ 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
2497
  t(UI.missing, lang),
2234
2498
  ": ",
2235
2499
  /* @__PURE__ */ jsx("code", { children: file }),
@@ -2311,19 +2575,32 @@ function ExecutiveViewBody({ data, base = "/", lang: forced, open = 1, onOpenTec
2311
2575
  art("pain"),
2312
2576
  /* @__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
2577
  /* @__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)) }),
2578
+ /* @__PURE__ */ jsx(
2579
+ CardDeck,
2580
+ {
2581
+ flip: true,
2582
+ ariaLabel: t(UI.bearers, lang),
2583
+ className: "caos-exec-cards",
2584
+ cards: d.pain.bearers.map((b, i) => ({
2585
+ id: i,
2586
+ title: field(`pain.bearers[${i}].who`, b.who),
2587
+ label: t(b.who, lang),
2588
+ attrs: { "data-exec-item": "pain.bearers" },
2589
+ body: /* @__PURE__ */ jsxs(Fragment, { children: [
2590
+ /* @__PURE__ */ jsxs("div", { children: [
2591
+ /* @__PURE__ */ jsx(Label, { children: t(UI.cost, lang) }),
2592
+ " ",
2593
+ field(`pain.bearers[${i}].cost`, b.cost)
2594
+ ] }),
2595
+ /* @__PURE__ */ jsxs("div", { children: [
2596
+ /* @__PURE__ */ jsx(Label, { children: t(UI.measure, lang) }),
2597
+ " ",
2598
+ field(`pain.bearers[${i}].measure`, b.measure)
2599
+ ] })
2600
+ ] })
2601
+ }))
2602
+ }
2603
+ ),
2327
2604
  /* @__PURE__ */ jsx("h3", { className: "caos-exec-sub", children: t(UI.causes, lang) }),
2328
2605
  /* @__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
2606
  /* @__PURE__ */ jsxs("div", { className: "caos-exec-cause-head", children: [
@@ -2359,38 +2636,66 @@ function ExecutiveViewBody({ data, base = "/", lang: forced, open = 1, onOpenTec
2359
2636
  // 3. the current situation
2360
2637
  /* @__PURE__ */ jsxs(Section, { n: 3, lang, keyMessage: d.situation.key_message, keyPath: "situation.key_message", children: [
2361
2638
  /* @__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)) }),
2639
+ /* @__PURE__ */ jsx(
2640
+ CardDeck,
2641
+ {
2642
+ flip: true,
2643
+ ariaLabel: t(UI.roles, lang),
2644
+ className: "caos-exec-cards",
2645
+ cards: d.situation.roles.map((r, i) => ({
2646
+ id: i,
2647
+ title: field(`situation.roles[${i}].role`, r.role),
2648
+ label: t(r.role, lang),
2649
+ attrs: { "data-exec-item": "situation.roles" },
2650
+ body: /* @__PURE__ */ jsxs(Fragment, { children: [
2651
+ /* @__PURE__ */ jsxs("div", { children: [
2652
+ /* @__PURE__ */ jsx(Label, { children: t(UI.decides, lang) }),
2653
+ " ",
2654
+ field(`situation.roles[${i}].decides`, r.decides)
2655
+ ] }),
2656
+ /* @__PURE__ */ jsxs("div", { children: [
2657
+ /* @__PURE__ */ jsx(Label, { children: t(UI.needs, lang) }),
2658
+ " ",
2659
+ field(`situation.roles[${i}].needs`, r.needs)
2660
+ ] })
2661
+ ] })
2662
+ }))
2663
+ }
2664
+ ),
2375
2665
  /* @__PURE__ */ jsx("p", { className: "caos-exec-interaction", children: field("situation.interaction", d.situation.interaction) }),
2376
2666
  art("situation"),
2377
2667
  /* @__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)) }),
2668
+ /* @__PURE__ */ jsx(
2669
+ CardDeck,
2670
+ {
2671
+ flip: true,
2672
+ ariaLabel: t(UI.sota, lang),
2673
+ className: "caos-exec-cards",
2674
+ cards: d.situation.state_of_the_art.map((a, i) => ({
2675
+ id: i,
2676
+ title: field(`situation.state_of_the_art[${i}].approach`, a.approach),
2677
+ label: t(a.approach, lang),
2678
+ attrs: { "data-exec-item": "situation.state_of_the_art" },
2679
+ body: /* @__PURE__ */ jsxs(Fragment, { children: [
2680
+ /* @__PURE__ */ jsxs("div", { children: [
2681
+ /* @__PURE__ */ jsx(Label, { children: t(UI.achieves, lang) }),
2682
+ " ",
2683
+ field(`situation.state_of_the_art[${i}].achieves`, a.achieves)
2684
+ ] }),
2685
+ /* @__PURE__ */ jsxs("div", { children: [
2686
+ /* @__PURE__ */ jsx(Label, { children: t(UI.fallsShort, lang) }),
2687
+ " ",
2688
+ field(`situation.state_of_the_art[${i}].falls_short`, a.falls_short)
2689
+ ] }),
2690
+ /* @__PURE__ */ jsxs("div", { children: [
2691
+ /* @__PURE__ */ jsx(Label, { children: t(UI.source, lang) }),
2692
+ " ",
2693
+ /* @__PURE__ */ jsx(Cite2, { id: a.source })
2694
+ ] })
2695
+ ] })
2696
+ }))
2697
+ }
2698
+ ),
2394
2699
  /* @__PURE__ */ jsxs("p", { className: "caos-exec-status", "data-exec-item": "situation.status", children: [
2395
2700
  /* @__PURE__ */ jsx(Label, { children: field("situation.status.heading", d.situation.status.heading) }),
2396
2701
  " ",
@@ -2434,11 +2739,24 @@ function ExecutiveViewBody({ data, base = "/", lang: forced, open = 1, onOpenTec
2434
2739
  /* @__PURE__ */ jsx(Paragraphs, { list: d.proposal.paragraphs, path: "proposal.paragraphs" }),
2435
2740
  art("proposal"),
2436
2741
  /* @__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)) }),
2742
+ /* @__PURE__ */ jsx(
2743
+ CardDeck,
2744
+ {
2745
+ flip: true,
2746
+ ariaLabel: t(UI.fourCs, lang),
2747
+ className: "caos-exec-cs",
2748
+ cards: d.proposal.four_cs.map((r, i) => ({
2749
+ id: i,
2750
+ title: /* @__PURE__ */ jsx("span", { "data-exec-field": `proposal.four_cs[${i}].c`, "data-exec-cname": "", children: t(FOUR_CS[i].name, lang) }),
2751
+ label: t(FOUR_CS[i].name, lang),
2752
+ attrs: { "data-exec-c": i, "data-exec-item": "proposal.four_cs" },
2753
+ body: /* @__PURE__ */ jsxs(Fragment, { children: [
2754
+ /* @__PURE__ */ jsx("small", { className: "caos-exec-cdef", "data-exec-cdef": "", children: t(FOUR_CS[i].definition, lang) }),
2755
+ /* @__PURE__ */ jsx("span", { children: field(`proposal.four_cs[${i}].applies`, r.applies) })
2756
+ ] })
2757
+ }))
2758
+ }
2759
+ ),
2442
2760
  /* @__PURE__ */ jsx("h3", { className: "caos-exec-sub", children: t(UI.actions, lang) }),
2443
2761
  /* @__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
2762
  /* @__PURE__ */ jsx("strong", { children: field(`proposal.actions[${i}].action`, a.action) }),
@@ -2523,6 +2841,13 @@ function replaceQueryParam(param, value) {
2523
2841
  const next = withCaseParam(window.location.search, value, param);
2524
2842
  window.history.replaceState(window.history.state, "", `${window.location.pathname}${next}${window.location.hash}`);
2525
2843
  }
2844
+ function removeQueryParam(param) {
2845
+ if (typeof window === "undefined") return;
2846
+ const p = new URLSearchParams(window.location.search);
2847
+ p.delete(param);
2848
+ const q = p.toString();
2849
+ window.history.replaceState(window.history.state, "", `${window.location.pathname}${q ? `?${q}` : ""}${window.location.hash}`);
2850
+ }
2526
2851
  function useUrlView(ids, param, value, onChange) {
2527
2852
  const [own, setOwn] = useState(
2528
2853
  () => param && typeof window !== "undefined" ? readCaseParam(window.location.search, param) : null
@@ -2787,7 +3112,7 @@ function BarChart({ title, data, axis, orientation = "horizontal", values = true
2787
3112
  function isSections(rail) {
2788
3113
  return Array.isArray(rail) && rail.every((s) => typeof s === "object" && s !== null && "id" in s && "content" in s);
2789
3114
  }
2790
- function WorkbenchLayout({ rail, railHead, children, railLabel, className }) {
3115
+ function RailBody({ rail, railHead, railLabel }) {
2791
3116
  const lang = useShellLang();
2792
3117
  const sections = isSections(rail) ? rail : null;
2793
3118
  const [active, setActive] = useState(sections?.[0]?.id ?? "");
@@ -2807,38 +3132,43 @@ function WorkbenchLayout({ rail, railHead, children, railLabel, className }) {
2807
3132
  document.getElementById(`${baseId}-rs-${target.id}`)?.focus();
2808
3133
  }
2809
3134
  }
3135
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3136
+ railHead && /* @__PURE__ */ jsx("div", { className: "caos-wb-rail-head", children: railHead }),
3137
+ sections ? /* @__PURE__ */ jsxs(Fragment, { children: [
3138
+ /* @__PURE__ */ jsx("div", { className: "caos-wb-rail-sections", role: "tablist", "aria-label": label, ref: rowRef, children: sections.map((s, idx) => /* @__PURE__ */ jsx(
3139
+ "button",
3140
+ {
3141
+ type: "button",
3142
+ role: "tab",
3143
+ id: `${baseId}-rs-${s.id}`,
3144
+ "aria-selected": s.id === current?.id,
3145
+ "aria-controls": `${baseId}-rp-${s.id}`,
3146
+ tabIndex: s.id === current?.id ? 0 : -1,
3147
+ className: s.id === current?.id ? "chip on" : "chip",
3148
+ onClick: () => setActive(s.id),
3149
+ onKeyDown: (e) => onKeyDown(e, idx),
3150
+ children: pick(s.label, lang)
3151
+ },
3152
+ s.id
3153
+ )) }),
3154
+ current && /* @__PURE__ */ jsx(
3155
+ "div",
3156
+ {
3157
+ className: "caos-wb-rail-panel",
3158
+ role: "tabpanel",
3159
+ id: `${baseId}-rp-${current.id}`,
3160
+ "aria-labelledby": `${baseId}-rs-${current.id}`,
3161
+ children: current.content
3162
+ }
3163
+ )
3164
+ ] }) : /* @__PURE__ */ jsx("div", { className: "caos-wb-rail-panel", children: rail })
3165
+ ] });
3166
+ }
3167
+ function WorkbenchLayout({ rail, railHead, children, railLabel, className }) {
3168
+ const lang = useShellLang();
3169
+ const label = pick(railLabel ?? { en: "Controls", es: "Controles" }, lang);
2810
3170
  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
- ] }),
3171
+ /* @__PURE__ */ jsx("aside", { className: "caos-wb-rail", "aria-label": label, "data-rail": "", children: /* @__PURE__ */ jsx(RailBody, { rail, railHead, railLabel }) }),
2842
3172
  /* @__PURE__ */ jsx("section", { className: "caos-wb-main", "data-instrument": "", children })
2843
3173
  ] });
2844
3174
  }
@@ -2913,13 +3243,73 @@ function VariantBar({ variants, activeId, onSelect, title, lane }) {
2913
3243
  active?.note && /* @__PURE__ */ jsx("p", { className: "caos-variant-note", children: pick(active.note, lang) })
2914
3244
  ] });
2915
3245
  }
3246
+
3247
+ // src/workbench/focus.ts
3248
+ var FOCUS_TEXT = {
3249
+ enter: { en: "Focus", es: "Enfocar" },
3250
+ enterLabel: { en: "Open this case in focus mode", es: "Abrir este caso en modo enfoque" },
3251
+ exit: { en: "Back to the App", es: "Volver a la App" },
3252
+ stage: { en: "Focus stage", es: "Escenario de enfoque" }
3253
+ };
3254
+ var FOCUS_PARAM = "focus";
2916
3255
  var MAX_WORKBENCH_GROUPS = MAX_PEER_TABS;
2917
3256
  function panel(id, lane, provenance, content) {
2918
3257
  return /* @__PURE__ */ jsx("div", { className: "caos-cw-panel", "data-group": id, "data-lane": lane, "data-provenance": provenance, children: content });
2919
3258
  }
2920
3259
  function CaseWorkbench(props) {
2921
- const { caseId, cases, source, controls, replayOnly, variants, rail, railLabel, groups, compare, context: context2, group, onGroupChange, deepLinkView = true, ariaLabel } = props;
3260
+ const { caseId, cases, source, controls, replayOnly, variants, rail, railLabel, groups, compare, context: context2, group, onGroupChange, deepLinkView = true, ariaLabel, focus } = props;
2922
3261
  const lang = useShellLang();
3262
+ const focusCfg = focus === true ? {} : focus || null;
3263
+ const focusParam = focusCfg?.param ?? FOCUS_PARAM;
3264
+ const [focusOn, setFocusOn] = useState(() => focusCfg !== null && typeof window !== "undefined" && readCaseParam(window.location.search, focusParam) === "1");
3265
+ const enterRef = useRef(null);
3266
+ const memory = useRef(/* @__PURE__ */ new Map());
3267
+ const turned = useRef(null);
3268
+ const onChangeRef = useRef(focusCfg?.onChange);
3269
+ onChangeRef.current = focusCfg?.onChange;
3270
+ const setFocus = useCallback(
3271
+ (on) => {
3272
+ turned.current = on ? "in" : "out";
3273
+ setFocusOn(on);
3274
+ if (on) replaceQueryParam(focusParam, "1");
3275
+ else removeQueryParam(focusParam);
3276
+ onChangeRef.current?.(on);
3277
+ },
3278
+ [focusParam]
3279
+ );
3280
+ const showFocus = focusCfg !== null && focusOn;
3281
+ useEffect(() => {
3282
+ if (!showFocus) return void 0;
3283
+ const onKey = (e) => {
3284
+ if (e.key !== "Escape" || e.defaultPrevented) return;
3285
+ e.preventDefault();
3286
+ setFocus(false);
3287
+ };
3288
+ document.addEventListener("keydown", onKey);
3289
+ return () => document.removeEventListener("keydown", onKey);
3290
+ }, [showFocus, setFocus]);
3291
+ useEffect(() => {
3292
+ if (!showFocus) return void 0;
3293
+ const frame = document.querySelector("[data-focus-shell]");
3294
+ if (!frame) return void 0;
3295
+ const made = [];
3296
+ for (let node = frame; node && node !== document.body; node = node.parentElement) {
3297
+ for (const sib of Array.from(node.parentElement?.children ?? [])) {
3298
+ if (sib !== node && !sib.hasAttribute("inert") && sib.tagName !== "SCRIPT" && sib.tagName !== "STYLE") {
3299
+ sib.setAttribute("inert", "");
3300
+ made.push(sib);
3301
+ }
3302
+ }
3303
+ }
3304
+ return () => {
3305
+ for (const el of made) el.removeAttribute("inert");
3306
+ };
3307
+ }, [showFocus]);
3308
+ useEffect(() => {
3309
+ if (turned.current === "in" && showFocus) document.querySelector("[data-focus-exit]")?.focus();
3310
+ else if (turned.current === "out" && !showFocus) enterRef.current?.focus();
3311
+ turned.current = null;
3312
+ }, [showFocus]);
2923
3313
  const variantId = variants?.activeId;
2924
3314
  const stateKey = useMemo(
2925
3315
  () => makeStateKey({ caseId, variantId: variantId ?? null, source: source ?? null, controls: controls ?? {} }),
@@ -2944,32 +3334,67 @@ function CaseWorkbench(props) {
2944
3334
  );
2945
3335
  }
2946
3336
  const [openGroup, selectGroup] = useUrlView(tabs.map((t2) => t2.id), viewParamOf(deepLinkView), group, onGroupChange);
2947
- const railHead = cases || variants ? /* @__PURE__ */ jsxs(Fragment, { children: [
2948
- cases && /* @__PURE__ */ jsx(CaseSelector, { lang, ...cases }),
3337
+ const enter = focusCfg && /* @__PURE__ */ jsxs(
3338
+ "button",
3339
+ {
3340
+ ref: enterRef,
3341
+ type: "button",
3342
+ className: "btn caos-focus-enter",
3343
+ "data-focus-enter": "",
3344
+ "aria-label": FOCUS_TEXT.enterLabel[lang],
3345
+ title: FOCUS_TEXT.enterLabel[lang],
3346
+ onClick: () => setFocus(true),
3347
+ children: [
3348
+ /* @__PURE__ */ jsx(Maximize2, { size: 14, "aria-hidden": "true" }),
3349
+ /* @__PURE__ */ jsx("span", { children: FOCUS_TEXT.enter[lang] })
3350
+ ]
3351
+ }
3352
+ );
3353
+ const besideSelect = Boolean(cases && (cases.layout ?? "chips") === "select");
3354
+ const pickers = cases || variants ? /* @__PURE__ */ jsxs(Fragment, { children: [
3355
+ cases && /* @__PURE__ */ jsx(CaseSelector, { lang, ...cases, accessory: besideSelect ? enter : cases.accessory }),
2949
3356
  variants && /* @__PURE__ */ jsx(VariantBar, { ...variants })
2950
3357
  ] }) : 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",
3358
+ const railHead = pickers || enter ? /* @__PURE__ */ jsxs(Fragment, { children: [
3359
+ pickers,
3360
+ besideSelect ? null : enter
3361
+ ] }) : null;
3362
+ const instrumentData = {
3363
+ "data-case-workbench": "",
3364
+ "data-group-count": tabs.length,
3365
+ "data-case": caseId,
3366
+ "data-variant": variantId ?? "",
3367
+ "data-source": source ?? "",
3368
+ "data-state-key": stateKey,
3369
+ "data-replay-only": replayOnly ? "1" : void 0
3370
+ };
3371
+ if (showFocus && focusCfg) {
3372
+ const open = tabs.find((t2) => t2.id === openGroup) ?? tabs[0];
3373
+ const caseName = cases?.cases.find((c) => c.id === caseId)?.name ?? caseId;
3374
+ return /* @__PURE__ */ jsx(ViewMemoryContext.Provider, { value: memory.current, children: /* @__PURE__ */ jsx(WorkbenchStateContext.Provider, { value: { stateKey, caseId, variantId, source }, children: /* @__PURE__ */ jsx(
3375
+ FocusShell,
3376
+ {
3377
+ title: focusCfg.title !== void 0 ? pick(focusCfg.title, lang) : caseName,
3378
+ description: focusCfg.description !== void 0 ? pick(focusCfg.description, lang) : open?.label ?? "",
3379
+ hud: (focusCfg.hud ?? []).map((h) => ({ label: pick(h.label, lang), value: h.value })),
3380
+ onExit: () => setFocus(false),
3381
+ exitLabel: FOCUS_TEXT.exit[lang],
3382
+ stageLabel: FOCUS_TEXT.stage[lang],
3383
+ landmark: false,
3384
+ 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 }) }) }) }) }),
3385
+ rail: /* @__PURE__ */ jsx("div", { className: "caos-focus-rail-body", children: /* @__PURE__ */ jsx(RailBody, { rail: rail ?? null, railHead: pickers, railLabel }) })
3386
+ }
3387
+ ) }) });
3388
+ }
3389
+ 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(
3390
+ Tabs,
2953
3391
  {
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
- )
3392
+ tabs,
3393
+ value: openGroup,
3394
+ onChange: selectGroup,
3395
+ ariaLabel: pick(ariaLabel ?? { en: "Case views", es: "Vistas del caso" }, lang)
2971
3396
  }
2972
- ) }) });
3397
+ ) }) }) }) });
2973
3398
  }
2974
3399
  function toneOf(it) {
2975
3400
  if (it.tone) return it.tone;
@@ -3171,4 +3596,4 @@ function ViewsRow({ children, shares }) {
3171
3596
  }) });
3172
3597
  }
3173
3598
 
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 };
3599
+ 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 };