@fasl-work/caos-app-shell 0.16.0 → 0.18.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.d.ts CHANGED
@@ -2,8 +2,8 @@ export { A as APP_ROUTE, a as AppShell, b as ArchTab, c as ArchitectureConfig, d
2
2
  import * as react from 'react';
3
3
  import { ReactNode, Component, ErrorInfo } from 'react';
4
4
  import * as zustand from 'zustand';
5
- import { F as FormatOptions } from './tokens-BTOzar-U.js';
6
- export { N as NBSP, a as NOT_AVAILABLE, b as SCIENTIFIC_BELOW, c as SHELL_TOKENS, S as ShellColorToken, d as ShellToken, f as formatNumber, e as formatTicks, r as resolveToken, u as useFormat, g as useThemeTokens } from './tokens-BTOzar-U.js';
5
+ import { F as FormatOptions } from './text-DyQ1A8oo.js';
6
+ export { a as FittedLabel, N as NBSP, b as NOT_AVAILABLE, c as SCIENTIFIC_BELOW, d as SHELL_TOKENS, S as ShellColorToken, e as ShellToken, f as fitLabel, g as fontFamily, h as formatNumber, i as formatTicks, n as niceStep, j as niceTicks, k as resolveToken, t as textWidth, u as useFormat, l as useThemeTokens, w as widestLabel } from './text-DyQ1A8oo.js';
7
7
  import { L as Lang, B as BiText } from './text-BlY47nIt.js';
8
8
  export { p as pick, u as useLangStore, a as useShellLang } from './text-BlY47nIt.js';
9
9
 
@@ -260,8 +260,9 @@ interface CaseSelectorProps {
260
260
  * category structure is preserved either way; only the height changes.
261
261
  */
262
262
  layout?: 'chips' | 'select';
263
- /** A control that sits on the select's row, after it (the workbench's Focus button, shell 0.14.1): the select shrinks to make
264
- * room, so the selector is no taller with it. With `layout: 'chips'` it gets a row of its own, after the chips. */
263
+ /** A control of the product that sits on the select's row, after it: the select shrinks to make room, so the selector is no taller with
264
+ * it (a long case name may then be cut: keep the control narrow). The workbench's own Focus control is not an accessory since 0.17.0:
265
+ * it sits in the group tab row. With `layout: 'chips'` it gets a row of its own, after the chips. */
265
266
  accessory?: ReactNode;
266
267
  }
267
268
  /**
@@ -1117,40 +1118,6 @@ interface BarChartProps {
1117
1118
  */
1118
1119
  declare function BarChart({ title, data, axis, orientation, values, height, onSelect }: BarChartProps): react.JSX.Element;
1119
1120
 
1120
- /**
1121
- * Text that fits the drawing it labels, measured in the font the page renders.
1122
- *
1123
- * Products drew their axes and labels with fixed margins and a fixed width per character. The Linux fonts (DejaVu
1124
- * Sans) are wider than the Windows ones and Spanish labels are longer than English ones, so a column sized for one
1125
- * ran its labels out of the drawing in the other (CAOS_Fragmenta 0.05.000: "Tamaño medio clásico, limitado al bloque
1126
- * in situ" read "dio clásico, limitado al bloque in situ"; its deploy gate failed on Linux). Here a label is measured
1127
- * with the canvas text metrics of the page's own font (`--font-sans`, which the gate's wide-font pass replaces),
1128
- * broken over two lines at a word when it does not fit, and only then shortened with an ellipsis, so the drawing can
1129
- * give the whole label as its title. Ported from CAOS_Fragmenta `viz/text.ts` (0.07.000).
1130
- */
1131
- /** The page's sans-serif stack as the shell token resolves it. */
1132
- declare function fontFamily(): string;
1133
- /** The width of one line of text at a font size, in CSS pixels; outside a browser, an estimate on the wide side. */
1134
- declare function textWidth(text: string, px: number, weight?: number): number;
1135
- /** The widest a set of labels needs, each on one line. */
1136
- declare function widestLabel(labels: readonly string[], px: number, weight?: number): number;
1137
- interface FittedLabel {
1138
- /** One or two lines; the last is shortened with an ellipsis only when the lines allowed are not enough. */
1139
- lines: string[];
1140
- /** Whether the label was shortened, so the drawing gives the whole label as its title. */
1141
- shortened: boolean;
1142
- }
1143
- /** A label in at most `maxLines` lines of `width` pixels: whole when it fits, broken at a word, shortened last. */
1144
- declare function fitLabel(label: string, width: number, px: number, maxLines?: 1 | 2): FittedLabel;
1145
- /** The 1, 2, 2.5 or 5 times a power of ten at or above `x`. */
1146
- declare function niceStep(x: number): number;
1147
- /**
1148
- * Round ticks covering [lo, hi], as many as leave room for labels about `labelSize` pixels along an axis of `room`
1149
- * pixels (at least two, at most `most`). The first tick is at or below `lo` and the last at or above `hi`, so a bar
1150
- * or a point never runs past the last labelled value.
1151
- */
1152
- declare function niceTicks(lo: number, hi: number, room: number, labelSize: number, most?: number): number[];
1153
-
1154
1121
  /** One section of the control rail. A rail whose content does not fit is SPLIT into sections, one shown at a
1155
1122
  * time, never scrolled (ADR-0071 rule 6: a panel must show its own controls). */
1156
1123
  interface RailSection {
@@ -1251,7 +1218,7 @@ interface CaseWorkbenchProps {
1251
1218
  deepLinkView?: boolean | string;
1252
1219
  ariaLabel?: BiText;
1253
1220
  /**
1254
- * Focus mode for the App (ADR-0070, shell 0.14.0): a Focus button in the rail head opens the selected case's open group in
1221
+ * Focus mode for the App (ADR-0070, shell 0.14.0): a Focus control at the end of the group tab row opens the selected case's open group in
1255
1222
  * the full viewport through `FocusShell` (the stage is the view, the rail stays), holds `?focus=1` beside `?case=` and
1256
1223
  * `?view=` in the URL, and returns with Escape or the exit button to the same case, group and sub-view. `true` takes all
1257
1224
  * the defaults. Omit it and the App is exactly what it was.
@@ -1379,4 +1346,4 @@ interface ViewsRowProps {
1379
1346
  */
1380
1347
  declare function ViewsRow({ children, shares }: ViewsRowProps): react.JSX.Element;
1381
1348
 
1382
- export { BREAKPOINTS, BarChart, type BarChartProps, type BarDatum, BiText, Callout, CardDeck, type CardDeckProps, type CaseDef, type CaseFocusConfig, type CaseGroup, type CaseKind, CaseSelector, type CaseSelectorProps, type CaseSelectorText, CaseWorkbench, type CaseWorkbenchProps, ChipGroup, type ChipGroupProps, type ChipOption, type Citation, CitationsProvider, Cite, DECK_TEXT, DOC_AREAS, type DeckCard, DocPage, type DocPageProps, DocReferences, DocRefsScope, DocSection, type DocSectionProps, EXECUTIVE_SCHEMA_VERSION, Equation, type ExecutiveData, type ExecutiveProblem, ExecutiveView, type ExecutiveViewProps, FLIP_LIMITS, FLIP_TEXT, FOCUS_PARAM, FOCUS_TEXT, FOUR_CS, Figure, type FittedLabel, FlipCard, type FlipCardProps, type FocusMode, type FocusModeConfig, type FocusModeOptions, FocusShell, type FocusShellProps, FormatOptions, Gauge, type GaugeProps, type GaugeZone, Glossary, type GlossaryEntry, InlineMath, Knob, type KnobProps, Landing, type Lane, LaneBadge, Lang, LanguageToggle, MAX_PEER_TABS, MAX_WORKBENCH_GROUPS, PanelBoundary, type PausedVizController, PlotCard, type PlotCardProps, type Provenance, Question, type QuestionProps, type RailSection, Readout, type ReadoutItem, type ReadoutProps, ReferenceList, Refs, Stage, type StageSize, Stat, type StatProps, type SubTabDef, SubTabs, type SubTabsProps, type SurfaceFocusConfig, SurfacePage, type SurfacePageProps, type TabDef, type TabGroupDef, TabGroups, type TabGroupsProps, Tabs, type TabsProps, type Theme, ThemeToggle, type Tone, type UsePausedVizOptions, VariantBar, type VariantBarProps, type VariantDef, Verdict, type VerdictProps, type ViewFocusConfig, ViewsRow, type ViewsRowProps, type VizFrame, type VizLoop, type VizLoopDeps, type VizLoopOptions, type WorkbenchGroup, WorkbenchLayout, type WorkbenchLayoutProps, type WorkbenchRoute, WorkbenchShell, type WorkbenchShellProps, type WorkbenchState, WorkbenchStateContext, applyTheme, caseKind, caseTooltip, casesInSource, createVizLoop, deckPosition, deckWindow, firstAppearance, fitLabel, fontFamily, groupByCategory, isStale, makeStateKey, niceStep, niceTicks, perPageFor, readCaseParam, readTheme, removeQueryParam, replaceQueryParam, sourcesPresent, textWidth, useFocusMode, usePausedViz, useStageSize, useThemeStore, useUrlView, useWorkbenchState, validateExecutive, viewParamOf, widestLabel, withCaseParam };
1349
+ export { BREAKPOINTS, BarChart, type BarChartProps, type BarDatum, BiText, Callout, CardDeck, type CardDeckProps, type CaseDef, type CaseFocusConfig, type CaseGroup, type CaseKind, CaseSelector, type CaseSelectorProps, type CaseSelectorText, CaseWorkbench, type CaseWorkbenchProps, ChipGroup, type ChipGroupProps, type ChipOption, type Citation, CitationsProvider, Cite, DECK_TEXT, DOC_AREAS, type DeckCard, DocPage, type DocPageProps, DocReferences, DocRefsScope, DocSection, type DocSectionProps, EXECUTIVE_SCHEMA_VERSION, Equation, type ExecutiveData, type ExecutiveProblem, ExecutiveView, type ExecutiveViewProps, FLIP_LIMITS, FLIP_TEXT, FOCUS_PARAM, FOCUS_TEXT, FOUR_CS, Figure, FlipCard, type FlipCardProps, type FocusMode, type FocusModeConfig, type FocusModeOptions, FocusShell, type FocusShellProps, FormatOptions, Gauge, type GaugeProps, type GaugeZone, Glossary, type GlossaryEntry, InlineMath, Knob, type KnobProps, Landing, type Lane, LaneBadge, Lang, LanguageToggle, MAX_PEER_TABS, MAX_WORKBENCH_GROUPS, PanelBoundary, type PausedVizController, PlotCard, type PlotCardProps, type Provenance, Question, type QuestionProps, type RailSection, Readout, type ReadoutItem, type ReadoutProps, ReferenceList, Refs, Stage, type StageSize, Stat, type StatProps, type SubTabDef, SubTabs, type SubTabsProps, type SurfaceFocusConfig, SurfacePage, type SurfacePageProps, type TabDef, type TabGroupDef, TabGroups, type TabGroupsProps, Tabs, type TabsProps, type Theme, ThemeToggle, type Tone, type UsePausedVizOptions, VariantBar, type VariantBarProps, type VariantDef, Verdict, type VerdictProps, type ViewFocusConfig, ViewsRow, type ViewsRowProps, type VizFrame, type VizLoop, type VizLoopDeps, type VizLoopOptions, type WorkbenchGroup, WorkbenchLayout, type WorkbenchLayoutProps, type WorkbenchRoute, WorkbenchShell, type WorkbenchShellProps, type WorkbenchState, WorkbenchStateContext, applyTheme, caseKind, caseTooltip, casesInSource, createVizLoop, deckPosition, deckWindow, firstAppearance, groupByCategory, isStale, makeStateKey, perPageFor, readCaseParam, readTheme, removeQueryParam, replaceQueryParam, sourcesPresent, useFocusMode, usePausedViz, useStageSize, useThemeStore, useUrlView, useWorkbenchState, validateExecutive, viewParamOf, withCaseParam };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { useThemeStore, useShellLang, useLangStore, SHELL_TOKENS, pick, useStageSize, NOT_AVAILABLE, formatTicks, formatNumber, NBSP } from './chunk-YECZXGVG.js';
2
- export { NBSP, NOT_AVAILABLE, SCIENTIFIC_BELOW, SHELL_TOKENS, Stage, applyTheme, formatNumber, formatTicks, pick, readTheme, resolveToken, useFormat, useLangStore, useShellLang, useStageSize, useThemeStore, useThemeTokens } from './chunk-YECZXGVG.js';
1
+ import { useThemeStore, useShellLang, useLangStore, SHELL_TOKENS, pick, useStageSize, NOT_AVAILABLE, widestLabel, fitLabel, formatTicks, niceTicks, textWidth, formatNumber, NBSP } from './chunk-DBCMAL2G.js';
2
+ export { NBSP, NOT_AVAILABLE, SCIENTIFIC_BELOW, SHELL_TOKENS, Stage, applyTheme, fitLabel, fontFamily, formatNumber, formatTicks, niceStep, niceTicks, pick, readTheme, resolveToken, textWidth, useFormat, useLangStore, useShellLang, useStageSize, useThemeStore, useThemeTokens, widestLabel } from './chunk-DBCMAL2G.js';
3
3
  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, useLayoutEffect, useCallback, useContext, useMemo, Children, isValidElement } from 'react';
@@ -795,17 +795,64 @@ function CaseSelector(props) {
795
795
  const visible = casesInSource(cases, source);
796
796
  const groups = groupByCategory(visible);
797
797
  const modified = cases.find((c) => c.id === modifiedFromId) ?? null;
798
+ const wrapRef = useRef(null);
799
+ const [minHeight, setMinHeight] = useState(null);
800
+ const nameKey = visible.map((c) => c.name).join("");
801
+ useLayoutEffect(() => {
802
+ const wrap = wrapRef.current;
803
+ const display = wrap?.querySelector(".cs-select-display");
804
+ if (!wrap || !display) return void 0;
805
+ const names = nameKey ? nameKey.split("") : [];
806
+ const measure = () => {
807
+ const st = getComputedStyle(display);
808
+ const room = display.clientWidth - parseFloat(st.paddingLeft) - parseFloat(st.paddingRight);
809
+ if (!(room > 0)) return;
810
+ const copy = document.createElement("span");
811
+ copy.className = "cs-select-text";
812
+ copy.setAttribute("aria-hidden", "true");
813
+ copy.setAttribute("style", `position:absolute;visibility:hidden;left:0;top:0;width:${room}px;display:block;overflow:visible;max-height:none;-webkit-line-clamp:unset;line-clamp:unset`);
814
+ display.append(copy);
815
+ let tallest = 0;
816
+ for (const n of names) {
817
+ copy.textContent = n;
818
+ tallest = Math.max(tallest, copy.getBoundingClientRect().height);
819
+ }
820
+ const line = parseFloat(getComputedStyle(copy).lineHeight) || parseFloat(st.fontSize) * 1.25;
821
+ copy.remove();
822
+ const frame = parseFloat(st.paddingTop) + parseFloat(st.paddingBottom) + parseFloat(st.borderTopWidth) + parseFloat(st.borderBottomWidth);
823
+ const next = tallest > line * 1.5 ? Math.ceil((tallest + frame) * 100) / 100 : null;
824
+ setMinHeight((prev) => prev === next ? prev : next);
825
+ };
826
+ measure();
827
+ let width = wrap.getBoundingClientRect().width;
828
+ const ro = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => {
829
+ const w = wrap.getBoundingClientRect().width;
830
+ if (w !== width) {
831
+ width = w;
832
+ measure();
833
+ }
834
+ });
835
+ ro?.observe(wrap);
836
+ void document.fonts?.ready.then(measure);
837
+ return () => ro?.disconnect();
838
+ }, [layout, nameKey]);
839
+ const options = groups.flatMap((g) => g.cases);
840
+ const selectedName = (options.find((c) => c.id === selectedId) ?? options.find((c) => !c.disabled))?.name ?? "";
798
841
  const select = /* @__PURE__ */ jsx(
799
842
  "select",
800
843
  {
801
844
  "data-control": "case",
802
- className: "select cs-select",
845
+ className: "select cs-select cs-select-native",
803
846
  "aria-label": ariaLabel ?? "Case selector",
804
847
  value: selectedId,
805
848
  onChange: (e) => onSelect(e.target.value),
806
849
  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 || "_"))
807
850
  }
808
851
  );
852
+ const selectBox = /* @__PURE__ */ jsxs("div", { className: "cs-select-wrap", ref: wrapRef, children: [
853
+ /* @__PURE__ */ jsx("div", { className: "cs-select-display", style: minHeight === null ? void 0 : { minHeight }, "aria-hidden": "true", children: /* @__PURE__ */ jsx("span", { className: "cs-select-text", children: selectedName }) }),
854
+ select
855
+ ] });
809
856
  return /* @__PURE__ */ jsxs("div", { className: `cs${className ? ` ${className}` : ""}`, role: "group", "aria-label": ariaLabel ?? "Case selector", children: [
810
857
  source && lanes.length > 1 && /* @__PURE__ */ jsxs("div", { className: "cs-source", children: [
811
858
  /* @__PURE__ */ jsx("span", { className: "cs-source-label", children: t2.source }),
@@ -824,9 +871,9 @@ function CaseSelector(props) {
824
871
  ] }),
825
872
  source && source !== "synthetic" && /* @__PURE__ */ jsx("p", { className: "cs-locked banner", children: lockedNote ?? t2.lockedNote }),
826
873
  layout === "select" && (props.accessory ? /* @__PURE__ */ jsxs("div", { className: "cs-select-row", children: [
827
- select,
874
+ selectBox,
828
875
  props.accessory
829
- ] }) : select),
876
+ ] }) : selectBox),
830
877
  layout === "chips" && groups.map((g) => /* @__PURE__ */ jsxs("div", { className: "cs-group", role: "group", "aria-label": g.category || void 0, "data-control": "case", children: [
831
878
  g.category && /* @__PURE__ */ jsx("span", { className: "cs-group-label", children: g.category }),
832
879
  /* @__PURE__ */ jsx("div", { className: "cs-chips", children: g.cases.map((c) => {
@@ -2045,7 +2092,7 @@ function executiveTextFields(d) {
2045
2092
  add("title", d.title, 1);
2046
2093
  add("key_statement", d.key_statement, 2);
2047
2094
  d.headline_figures.forEach((f, i) => figure(`headline_figures[${i}]`, f));
2048
- const { pain, context: context2, situation, trends, proposal } = d;
2095
+ const { pain, context, situation, trends, proposal } = d;
2049
2096
  add("pain.key_message", pain.key_message, 1);
2050
2097
  pain.paragraphs.forEach((p, i) => add(`pain.paragraphs[${i}]`, p, void 0, EXEC_LIMITS.minParagraphWords));
2051
2098
  pain.bearers.forEach((b, i) => {
@@ -2055,11 +2102,11 @@ function executiveTextFields(d) {
2055
2102
  });
2056
2103
  pain.figures.forEach((f, i) => figure(`pain.figures[${i}]`, f));
2057
2104
  pain.causes.forEach((c, i) => add(`pain.causes[${i}].cause`, c.cause));
2058
- add("context.key_message", context2.key_message, 1);
2059
- context2.paragraphs.forEach((p, i) => add(`context.paragraphs[${i}]`, p, void 0, EXEC_LIMITS.minParagraphWords));
2060
- add("context.setting", context2.setting);
2061
- context2.forces.forEach((f, i) => add(`context.forces[${i}].force`, f.force));
2062
- context2.evidence.forEach((e, i) => add(`context.evidence[${i}].statement`, e.statement, 1));
2105
+ add("context.key_message", context.key_message, 1);
2106
+ context.paragraphs.forEach((p, i) => add(`context.paragraphs[${i}]`, p, void 0, EXEC_LIMITS.minParagraphWords));
2107
+ add("context.setting", context.setting);
2108
+ context.forces.forEach((f, i) => add(`context.forces[${i}].force`, f.force));
2109
+ context.evidence.forEach((e, i) => add(`context.evidence[${i}].statement`, e.statement, 1));
2063
2110
  add("situation.key_message", situation.key_message, 1);
2064
2111
  situation.roles.forEach((r, i) => {
2065
2112
  add(`situation.roles[${i}].role`, r.role);
@@ -3086,74 +3133,6 @@ var BREAKPOINTS = {
3086
3133
  /** The gate's large screen: the viewport rules of ADR-0071 are measured from here up. */
3087
3134
  large: 1280
3088
3135
  };
3089
-
3090
- // src/chart/text.ts
3091
- var context;
3092
- function fontFamily() {
3093
- if (typeof document === "undefined") return "sans-serif";
3094
- return getComputedStyle(document.documentElement).getPropertyValue("--font-sans").trim() || "sans-serif";
3095
- }
3096
- function textWidth(text, px, weight = 400) {
3097
- if (context === void 0) {
3098
- try {
3099
- context = typeof document === "undefined" ? null : document.createElement("canvas").getContext("2d");
3100
- } catch {
3101
- context = null;
3102
- }
3103
- }
3104
- if (!context) return text.length * px * 0.62;
3105
- context.font = `${weight} ${px}px ${fontFamily()}`;
3106
- return context.measureText(text).width;
3107
- }
3108
- function widestLabel(labels, px, weight = 400) {
3109
- return Math.max(0, ...labels.map((label) => textWidth(label, px, weight)));
3110
- }
3111
- function shorten(line, width, px) {
3112
- if (textWidth(line, px) <= width) return line;
3113
- let cut = line;
3114
- while (cut.length > 1 && textWidth(`${cut}\u2026`, px) > width) cut = cut.slice(0, -1);
3115
- return `${cut.trimEnd()}\u2026`;
3116
- }
3117
- function fitLabel(label, width, px, maxLines = 2) {
3118
- if (textWidth(label, px) <= width) return { lines: [label], shortened: false };
3119
- const words = label.split(" ");
3120
- if (maxLines === 2 && words.length > 1) {
3121
- let best = null;
3122
- let bestWidth = Infinity;
3123
- for (let k = 1; k < words.length; k += 1) {
3124
- const first = words.slice(0, k).join(" ");
3125
- const second = words.slice(k).join(" ");
3126
- const widest = Math.max(textWidth(first, px), textWidth(second, px));
3127
- if (widest < bestWidth) {
3128
- bestWidth = widest;
3129
- best = [first, second];
3130
- }
3131
- }
3132
- if (best && bestWidth <= width) return { lines: best, shortened: false };
3133
- if (best) return { lines: [shorten(best[0], width, px), shorten(best[1], width, px)], shortened: true };
3134
- }
3135
- return { lines: [shorten(label, width, px)], shortened: true };
3136
- }
3137
- function niceStep(x) {
3138
- if (!(x > 0) || !Number.isFinite(x)) return 1;
3139
- const magnitude = 10 ** Math.floor(Math.log10(x));
3140
- return ([1, 2, 2.5, 5, 10].find((m) => m * magnitude >= x * (1 - 1e-12)) ?? 10) * magnitude;
3141
- }
3142
- function niceTicks(lo, hi, room, labelSize, most = 6) {
3143
- if (!Number.isFinite(lo) || !Number.isFinite(hi)) return [0];
3144
- if (hi < lo) [lo, hi] = [hi, lo];
3145
- if (hi === lo) hi = lo === 0 ? 1 : lo + Math.abs(lo);
3146
- const count = Math.max(2, Math.min(most, Math.floor(room / (labelSize + 16)) + 1));
3147
- const step = niceStep((hi - lo) / (count - 1));
3148
- const first = Math.floor(lo / step + 1e-9) * step;
3149
- const ticks = [];
3150
- for (let k = 0; first + k * step <= hi + step * 1e-9 || ticks.length < 2; k += 1) {
3151
- ticks.push(Number((first + k * step).toPrecision(12)));
3152
- if (ticks.length > 50) break;
3153
- }
3154
- if (ticks[ticks.length - 1] < hi - step * 1e-9) ticks.push(Number((ticks[ticks.length - 1] + step).toPrecision(12)));
3155
- return ticks;
3156
- }
3157
3136
  var PX = 11;
3158
3137
  var LINE = 13;
3159
3138
  var TONE = {
@@ -3442,7 +3421,7 @@ function panel(id, lane, provenance, content) {
3442
3421
  return /* @__PURE__ */ jsx("div", { className: "caos-cw-panel", "data-group": id, "data-lane": lane, "data-provenance": provenance, children: content });
3443
3422
  }
3444
3423
  function CaseWorkbench(props) {
3445
- const { caseId, cases, source, controls, replayOnly, variants, rail, railLabel, groups, compare, context: context2, group, onGroupChange, deepLinkView = true, ariaLabel, focus } = props;
3424
+ const { caseId, cases, source, controls, replayOnly, variants, rail, railLabel, groups, compare, context, group, onGroupChange, deepLinkView = true, ariaLabel, focus } = props;
3446
3425
  const lang = useShellLang();
3447
3426
  const focusCfg = focus === true ? {} : focus || null;
3448
3427
  const { show: showFocus, setFocus, enterRef, memory } = useFocusMode(focusCfg);
@@ -3462,8 +3441,8 @@ function CaseWorkbench(props) {
3462
3441
  }
3463
3442
  tabs.push({
3464
3443
  id: "context",
3465
- label: pick(context2.label ?? { en: "Context", es: "Contexto" }, lang),
3466
- content: /* @__PURE__ */ jsx("div", { className: "prose caos-cw-context", children: context2.content })
3444
+ label: pick(context.label ?? { en: "Context", es: "Contexto" }, lang),
3445
+ content: /* @__PURE__ */ jsx("div", { className: "prose caos-cw-context", children: context.content })
3467
3446
  });
3468
3447
  if (tabs.length > MAX_WORKBENCH_GROUPS) {
3469
3448
  throw new Error(
@@ -3487,19 +3466,12 @@ function CaseWorkbench(props) {
3487
3466
  ]
3488
3467
  }
3489
3468
  );
3490
- const besideSelect = Boolean(cases && (cases.layout ?? "chips") === "select");
3491
3469
  const pickers = cases || variants ? /* @__PURE__ */ jsxs(Fragment, { children: [
3492
- cases && /* @__PURE__ */ jsx(CaseSelector, { lang, ...cases, accessory: besideSelect ? enter : cases.accessory }),
3493
- variants && /* @__PURE__ */ jsx(VariantBar, { ...variants })
3494
- ] }) : null;
3495
- const focusPickers = cases || variants ? /* @__PURE__ */ jsxs(Fragment, { children: [
3496
- cases && /* @__PURE__ */ jsx(CaseSelector, { lang, ...cases, accessory: cases.accessory }),
3470
+ cases && /* @__PURE__ */ jsx(CaseSelector, { lang, ...cases }),
3497
3471
  variants && /* @__PURE__ */ jsx(VariantBar, { ...variants })
3498
3472
  ] }) : null;
3499
- const railHead = pickers || enter ? /* @__PURE__ */ jsxs(Fragment, { children: [
3500
- pickers,
3501
- besideSelect ? null : enter
3502
- ] }) : null;
3473
+ const railHead = pickers;
3474
+ const focusPickers = pickers;
3503
3475
  const instrumentData = {
3504
3476
  "data-case-workbench": "",
3505
3477
  "data-group-count": tabs.length,
@@ -3533,7 +3505,8 @@ function CaseWorkbench(props) {
3533
3505
  tabs,
3534
3506
  value: openGroup,
3535
3507
  onChange: selectGroup,
3536
- ariaLabel: pick(ariaLabel ?? { en: "Case views", es: "Vistas del caso" }, lang)
3508
+ ariaLabel: pick(ariaLabel ?? { en: "Case views", es: "Vistas del caso" }, lang),
3509
+ trailing: enter || void 0
3537
3510
  }
3538
3511
  ) }) }) }) });
3539
3512
  }
@@ -3737,4 +3710,4 @@ function ViewsRow({ children, shares }) {
3737
3710
  }) });
3738
3711
  }
3739
3712
 
3740
- 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, useFocusMode, usePausedViz, useUrlView, useWorkbenchState, validateArchitectureConfig, validateExecutive, viewParamOf, widestLabel, withCaseParam };
3713
+ 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, groupByCategory, isStale, makeStateKey, perPageFor, readCaseParam, removeQueryParam, replaceQueryParam, sourcesPresent, useFocusMode, usePausedViz, useUrlView, useWorkbenchState, validateArchitectureConfig, validateExecutive, viewParamOf, withCaseParam };
@@ -58,4 +58,42 @@ declare function resolveToken(token: string, fallback?: string): string;
58
58
  */
59
59
  declare function useThemeTokens(): Record<ShellToken, string>;
60
60
 
61
- export { type FormatOptions as F, NBSP as N, type ShellColorToken as S, NOT_AVAILABLE as a, SCIENTIFIC_BELOW as b, SHELL_TOKENS as c, type ShellToken as d, formatTicks as e, formatNumber as f, useThemeTokens as g, resolveToken as r, useFormat as u };
61
+ /**
62
+ * Text that fits the drawing it labels, measured in the font the page renders.
63
+ *
64
+ * Products drew their axes and labels with fixed margins and a fixed width per character. The Linux fonts (DejaVu
65
+ * Sans) are wider than the Windows ones and Spanish labels are longer than English ones, so a column sized for one
66
+ * ran its labels out of the drawing in the other (CAOS_Fragmenta 0.05.000: "Tamaño medio clásico, limitado al bloque
67
+ * in situ" read "dio clásico, limitado al bloque in situ"; its deploy gate failed on Linux). Here a label is measured
68
+ * with the canvas text metrics of the page's own font (`--font-sans`, which the gate's wide-font pass replaces),
69
+ * broken over two lines at a word when it does not fit, and only then shortened with an ellipsis, so the drawing can
70
+ * give the whole label as its title. Ported from CAOS_Fragmenta `viz/text.ts` (0.07.000).
71
+ */
72
+
73
+ /** The name of a reference line that has no label, for the handle a reader drags (a screen reader says it): numbered when
74
+ * several unlabelled lines of one chart can be dragged, so each handle has a name of its own. */
75
+ declare function referenceLineName(lang: Lang, n: number, of: number): string;
76
+ /** The page's sans-serif stack as the shell token resolves it. */
77
+ declare function fontFamily(): string;
78
+ /** The width of one line of text at a font size, in CSS pixels; outside a browser, an estimate on the wide side. */
79
+ declare function textWidth(text: string, px: number, weight?: number): number;
80
+ /** The widest a set of labels needs, each on one line. */
81
+ declare function widestLabel(labels: readonly string[], px: number, weight?: number): number;
82
+ interface FittedLabel {
83
+ /** One or two lines; the last is shortened with an ellipsis only when the lines allowed are not enough. */
84
+ lines: string[];
85
+ /** Whether the label was shortened, so the drawing gives the whole label as its title. */
86
+ shortened: boolean;
87
+ }
88
+ /** A label in at most `maxLines` lines of `width` pixels: whole when it fits, broken at a word, shortened last. */
89
+ declare function fitLabel(label: string, width: number, px: number, maxLines?: 1 | 2): FittedLabel;
90
+ /** The 1, 2, 2.5 or 5 times a power of ten at or above `x`. */
91
+ declare function niceStep(x: number): number;
92
+ /**
93
+ * Round ticks covering [lo, hi], as many as leave room for labels about `labelSize` pixels along an axis of `room`
94
+ * pixels (at least two, at most `most`). The first tick is at or below `lo` and the last at or above `hi`, so a bar
95
+ * or a point never runs past the last labelled value.
96
+ */
97
+ declare function niceTicks(lo: number, hi: number, room: number, labelSize: number, most?: number): number[];
98
+
99
+ export { type FormatOptions as F, NBSP as N, type ShellColorToken as S, type FittedLabel as a, NOT_AVAILABLE as b, SCIENTIFIC_BELOW as c, SHELL_TOKENS as d, type ShellToken as e, fitLabel as f, fontFamily as g, formatNumber as h, formatTicks as i, niceTicks as j, resolveToken as k, useThemeTokens as l, niceStep as n, referenceLineName as r, textWidth as t, useFormat as u, widestLabel as w };
package/gate/inpage.mjs CHANGED
@@ -181,6 +181,67 @@ export function installLib() {
181
181
  };
182
182
  };
183
183
 
184
+ /**
185
+ * Selects whose selected option does not fit the room it leaves, or a case select that shows another name than the one it holds (BL-074, shell
186
+ * 0.17.0; the reviews of 0.17.0). What is measured is what the reader sees:
187
+ * - a select with the shell's display (`.cs-select-wrap`, the case select since the trial of 0.17.0) shows the name whole at any length in the
188
+ * display's real `.cs-select-text` element: the name is cut when that element overflows its box (`scrollHeight` or `scrollWidth` past its
189
+ * `clientHeight` or `clientWidth` by more than 1px, e.g. a product stylesheet that clamps it), measured on the element itself, never on a probe
190
+ * copy, and when the display's own box is smaller than its content (a product stylesheet that fixes its height); the display is stale when the name it shows, whitespace normalized, differs from the text of the option the select holds;
191
+ * - any other select shows its text on one line: the room is its width less its own CSS padding and less the native arrow, which is
192
+ * measured, not assumed (a probe select of the same class holding one character, sized to its content; 0 with `appearance: none`).
193
+ * Plain text is measured with the select's own font on one canvas. Runs in the App and inside Focus (its rail holds a case select too).
194
+ */
195
+ const selectCuts = () => {
196
+ const cuts = [];
197
+ const mctx = document.createElement('canvas').getContext('2d');
198
+ const norm = (t) => (t ?? '').trim().replace(/\s+/g, ' ');
199
+ for (const sel of document.querySelectorAll('select')) {
200
+ if (!mctx || !visible(sel)) continue;
201
+ const ss = getComputedStyle(sel);
202
+ const held = norm(sel.options[sel.selectedIndex]?.textContent);
203
+ const display = sel.closest('.cs-select-wrap')?.querySelector('.cs-select-display');
204
+ if (display) {
205
+ const t = display.querySelector('.cs-select-text') ?? display;
206
+ const shown = norm(t.textContent);
207
+ if (shown !== held) {
208
+ cuts.push(`${describe(sel)} shows "${shown}" while the select holds "${held}" (a stale display)`);
209
+ continue;
210
+ }
211
+ const overV = t.scrollHeight > t.clientHeight + 1;
212
+ const overH = t.scrollWidth > t.clientWidth + 1;
213
+ if (overV || overH) {
214
+ const line = Number.parseFloat(getComputedStyle(t).lineHeight) || 1;
215
+ cuts.push(`${describe(sel)} cuts the case name "${shown}" (the name needs ${Math.round(t.scrollHeight / line)} lines, ${Math.round(t.scrollHeight)}px high and ${Math.round(t.scrollWidth)}px wide, and its box holds ${Math.round(t.clientHeight)}px by ${Math.round(t.clientWidth)}px; held option "${held}")`);
216
+ } else if (display.scrollHeight > display.clientHeight + 1 || display.scrollWidth > display.clientWidth + 1) {
217
+ // the text fits its own box but the display's box is smaller (a product stylesheet that fixes the display's height)
218
+ cuts.push(`${describe(sel)} cuts the case name "${shown}" in its display (the name needs ${Math.round(display.scrollHeight)}px by ${Math.round(display.scrollWidth)}px, the display holds ${Math.round(display.clientHeight)}px by ${Math.round(display.clientWidth)}px; held option "${held}")`);
219
+ }
220
+ continue;
221
+ }
222
+ const text = held;
223
+ if (!text) continue;
224
+ mctx.font = `${ss.fontStyle} ${ss.fontWeight} ${ss.fontSize} ${ss.fontFamily}`;
225
+ const pad = Number.parseFloat(ss.paddingLeft) + Number.parseFloat(ss.paddingRight);
226
+ let arrow = 0;
227
+ const host = sel.parentElement;
228
+ if (host) {
229
+ const probe = sel.cloneNode(false);
230
+ for (const a of [...probe.attributes]) if (a.name !== 'class') probe.removeAttribute(a.name);
231
+ probe.append(new Option('x'));
232
+ probe.setAttribute('style', 'position:absolute;visibility:hidden;width:auto;min-width:0;max-width:none;flex:none;left:0;top:0');
233
+ host.append(probe);
234
+ const pv = getComputedStyle(probe);
235
+ arrow = Math.max(0, probe.clientWidth - Number.parseFloat(pv.paddingLeft) - Number.parseFloat(pv.paddingRight) - mctx.measureText('x').width);
236
+ probe.remove();
237
+ }
238
+ const need = mctx.measureText(text).width;
239
+ const has = sel.clientWidth - pad - arrow;
240
+ if (need > has + 1) cuts.push(`${describe(sel)} cuts "${text}" (${Math.round(need)}px of text in ${Math.round(has)}px of room, the ${Math.round(arrow)}px arrow taken out)`);
241
+ }
242
+ return cuts;
243
+ };
244
+
184
245
  /** The static layout of the current view (G5, G6 inputs, ADR-0071 and ADR-0017 measures). */
185
246
  G.facts = () => {
186
247
  const vw = window.innerWidth;
@@ -272,6 +333,8 @@ export function installLib() {
272
333
  if (!named) truncated.push(describe(el));
273
334
  }
274
335
 
336
+ const cutSelects = selectCuts();
337
+
275
338
  // Containers that cut their content: overflow hidden or clip with more content than box (S1, G5).
276
339
  const clipped = [];
277
340
  const containers = new Set([de, body, document.getElementById('root'), ...document.querySelectorAll('main, .page, .page-body, [data-instrument], [data-rail], .tabpanel, .caos-cw, [data-plot]')]);
@@ -415,6 +478,8 @@ export function installLib() {
415
478
  rail,
416
479
  truncated: truncated.slice(0, 5),
417
480
  truncatedCount: truncated.length,
481
+ cutSelects: cutSelects.slice(0, 5),
482
+ cutSelectCount: cutSelects.length,
418
483
  clipped,
419
484
  instrument,
420
485
  surfaces,
@@ -433,7 +498,7 @@ export function installLib() {
433
498
  // charts whose axis gives a tick the label of the tick before it (UPlotChart declares data-ticks-repeat)
434
499
  repeatedTicks: [...document.querySelectorAll('[data-ticks-repeat]')]
435
500
  .filter((el) => visible(el) && Number(el.getAttribute('data-ticks-repeat')) > 0)
436
- .map((el) => ({ label: describe(el.closest('[data-plot]') || el), n: Number(el.getAttribute('data-ticks-repeat')) })),
501
+ .map((el) => ({ label: describe(el.closest('[data-plot]') || el), n: Number(el.getAttribute('data-ticks-repeat')), names: el.getAttribute('data-ticks-repeated') || '' })),
437
502
  // the drawing a filling card keeps under its note (G6 stage floor): the body of every visible `PlotCard fill`
438
503
  fillStages: [...document.querySelectorAll('.caos-plot.fill')]
439
504
  .filter(visible)
@@ -1063,6 +1128,7 @@ export function installLib() {
1063
1128
  return {
1064
1129
  focus: Boolean(shell),
1065
1130
  covered,
1131
+ cutSelects: (() => { const c = selectCuts(); return { count: c.length, list: c.slice(0, 5) }; })(),
1066
1132
  enterInside: [...document.querySelectorAll('[data-focus-shell] [data-focus-enter]')].filter(visible).length,
1067
1133
  app: Boolean(document.querySelector('[data-workbench], [data-surface]')),
1068
1134
  caseId: stage ? stage.getAttribute('data-case') : null,
package/gate/run.mjs CHANGED
@@ -8,7 +8,7 @@
8
8
  // G4 deep links every route loaded directly (and with a trailing slash), 200 and rendered; artifacts
9
9
  // are JSON, not the HTML fallback; a missing asset answers 404; doc text clears a floor
10
10
  // G5 scroll and reach boxes inside the viewport, the rail and their scrollers; tall views scroll; nothing is
11
- // cut; truncated text carries its full text; every control reachable by the pointer
11
+ // cut; truncated text carries its full text; a select shows its whole selected text (0.17.0); every control reachable by the pointer
12
12
  // G6 painted area drawing surfaces have painted pixels, fill their stage, and cover at least half the
13
13
  // viewport on the App route, measured on the screenshot, never on a host box
14
14
  // G7 walk everything every route, tab, sub-tab and case, at five sizes, both themes, both languages,
@@ -269,6 +269,15 @@ export async function runGate(o) {
269
269
  fail('G18', { route: appRoute, mode: 'routes' }, `the App route ${appRoute} is not among the measured routes (${routes.join(', ') || 'none'}): with --workbench '' and --require-focus the gate measures the App as a surface and must walk it; add it to --routes or name it with --app-route`);
270
270
  }
271
271
 
272
+ // ---- G18 on a workbench App (shell 0.17.0): under --require-focus every workbench route must be among the measured routes, or no Focus would be measured
273
+ if (opt.requireFocus) {
274
+ for (const wr of workbenchRoutes) {
275
+ if (!routes.includes(wr)) {
276
+ fail('G18', { route: wr, mode: 'routes' }, `the workbench route ${wr} is not among the measured routes (${routes.join(', ') || 'none'}): with --require-focus the gate must walk the App to measure its Focus mode; add it to --routes or drop it from --workbench`);
277
+ }
278
+ }
279
+ }
280
+
272
281
  // ---- The landing (shell 0.13.0): the root opens the Introduction on its executive view; an old App link with a query string reaches the App.
273
282
  {
274
283
  const ctx = await openContext(primarySize, themes[0], langs[0]);
@@ -460,6 +469,7 @@ export async function runGate(o) {
460
469
  if (f.rail.outsideCount) fail('G5', where, `${f.rail.outsideCount} rail element(s) outside the rail: ${f.rail.outside.join('; ')}`);
461
470
  }
462
471
  if (f.truncatedCount) fail('G5', where, `${f.truncatedCount} truncated text(s) without the full text in a title: ${f.truncated.join('; ')}`);
472
+ if (f.cutSelectCount) fail('G5', where, `${f.cutSelectCount} select(s) cut the selected text (a case name cut in its box, or a display that names another case than the one the select holds, or a plain select with a text wider than its room): ${f.cutSelects.join('; ')}`);
463
473
  for (const c of f.clipped) fail('G5', where, c);
464
474
  if (f.offCentre !== null && f.offCentre > 2) fail('ADR-0017.1', where, `the prose page is off-centre by ${f.offCentre.toFixed(1)}px`);
465
475
  if (f.referenceLists) fail('ADR-0017.4', where, `${f.referenceLists} bibliography outside the end-of-page references block (a .reference-list); give each DocSection its refs and let the page list them once`);
@@ -471,7 +481,9 @@ export async function runGate(o) {
471
481
  if (d) s.walkState.doc.push(d);
472
482
  }
473
483
  for (const r of f.repeatedTicks ?? []) {
474
- fail('G6', where, `${r.label}: ${r.n} axis tick label(s) repeat the label before them (give the axis fixed decimals)`);
484
+ // named x ticks (UPlotChart declares the repeated names): a name used twice, wherever the two ticks sit on the axis
485
+ if (r.names) fail('G6', where, `${r.label}: ${r.n} named x tick label(s) repeat another tick's label (${r.names.split('|').map((n) => `"${n}"`).join(', ')}); give every named tick a label of its own`);
486
+ else fail('G6', where, `${r.label}: ${r.n} axis tick label(s) repeat the label before them (give the axis fixed decimals)`);
475
487
  }
476
488
  // a filling card whose note leaves its drawing under the floor (CAOS_APP_SHELL#95); below 1280 px the document scrolls
477
489
  if (big) for (const c of f.fillStages ?? []) if (c.h < opt.stageFloor) fail('G6', where, `the drawing of ${c.label} keeps ${Math.round(c.h)} px under its note (floor ${opt.stageFloor} px)`);
@@ -913,6 +925,7 @@ export async function runGate(o) {
913
925
  if (big && f.share < opt.instrumentMin) fail('G18', at, `${label}: the stage covers ${(100 * f.share).toFixed(1)}% of the viewport (floor ${(100 * opt.instrumentMin).toFixed(0)}%)`);
914
926
  if (f.exitInside === false) fail('G18', at, `${label}: the exit control is outside the viewport`);
915
927
  for (const c of f.covered) fail('G18', at, `${label}: ${c.chrome} covers the control "${c.control}" (the stage must clear the Focus chrome)`);
928
+ if (f.cutSelects.count) fail('G5', at, `${label}: ${f.cutSelects.count} select(s) cut the selected text (a case name cut in its box, or a display that names another case than the one the select holds, or a plain select with a text wider than its room): ${f.cutSelects.list.join('; ')}`);
916
929
  if (f.enterInside > 0) fail('G18', at, `${label}: the Focus control shows inside Focus mode (it opens the mode the reader is already in)`);
917
930
  for (const m of await page.evaluate(() => window.__caosGate.drawingText())) fail('G10', at, m);
918
931
  for (const m of await page.evaluate(() => window.__caosGate.brokenValues())) fail('G19', at, m);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fasl-work/caos-app-shell",
3
- "version": "0.16.0",
3
+ "version": "0.18.0",
4
4
  "description": "Shared web-app shell, workbench primitives, content primitives, design system and measured gate for the CAOS / Faena public apps (ADR-0016, ADR-0017, ADR-0071).",
5
5
  "license": "MIT",
6
6
  "author": "Felipe Santibáñez-Leal",
@@ -56,7 +56,7 @@
56
56
  "check:sdd": "python scripts/check_sdd.py ."
57
57
  },
58
58
  "peerDependencies": {
59
- "katex": "^0.16.0",
59
+ "katex": "^0.16.0 || ^0.17.0 || ^0.18.0 || ^0.19.0",
60
60
  "lucide-react": ">=0.400.0",
61
61
  "react": "^18 || ^19",
62
62
  "react-dom": "^18 || ^19",
@@ -67,11 +67,10 @@
67
67
  },
68
68
  "devDependencies": {
69
69
  "@playwright/test": "1.63.0",
70
- "@types/katex": "^0.16.7",
71
70
  "@types/react": "^19.0.0",
72
71
  "@types/react-dom": "^19.0.0",
73
72
  "esbuild": "0.28.1",
74
- "katex": "^0.16.11",
73
+ "katex": "0.19.0",
75
74
  "lucide-react": "1.43.0",
76
75
  "react": "^19.0.0",
77
76
  "react-dom": "^19.0.0",
@@ -25,6 +25,7 @@
25
25
  "caos-card",
26
26
  "caos-chart",
27
27
  "caos-chart-key",
28
+ "caos-chart-key-button",
28
29
  "caos-chart-legend",
29
30
  "caos-chart-plot",
30
31
  "caos-chart-readout",
@@ -198,8 +199,11 @@
198
199
  "cs-locked",
199
200
  "cs-modified",
200
201
  "cs-reset",
201
- "cs-select",
202
+ "cs-select-display",
203
+ "cs-select-native",
202
204
  "cs-select-row",
205
+ "cs-select-text",
206
+ "cs-select-wrap",
203
207
  "cs-source",
204
208
  "cs-source-label",
205
209
  "cs-source-tabs",
@@ -335,6 +339,7 @@
335
339
  "caos-card",
336
340
  "caos-chart",
337
341
  "caos-chart-key",
342
+ "caos-chart-key-button",
338
343
  "caos-chart-legend",
339
344
  "caos-chart-plot",
340
345
  "caos-chart-readout",
@@ -512,8 +517,11 @@
512
517
  "cs-locked",
513
518
  "cs-modified",
514
519
  "cs-reset",
515
- "cs-select",
520
+ "cs-select-display",
521
+ "cs-select-native",
516
522
  "cs-select-row",
523
+ "cs-select-text",
524
+ "cs-select-wrap",
517
525
  "cs-source",
518
526
  "cs-source-label",
519
527
  "cs-source-tabs",