@fasl-work/caos-app-shell 0.7.0 → 0.7.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/README.md +5 -2
- package/chart.css +8 -0
- package/dist/chart.d.ts +5 -3
- package/dist/chart.js +5 -1
- package/dist/{chunk-OV4HK6EQ.js → chunk-LOSSTLPP.js} +3 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -3
- package/dist/{tokens-BCqyHYx2.d.ts → tokens-CSV0MOgR.d.ts} +4 -3
- package/gate/inpage.mjs +53 -13
- package/gate/run.mjs +1 -0
- package/package.json +1 -1
- package/reserved-classes.json +3 -0
- package/styles.css +5 -0
package/README.md
CHANGED
|
@@ -70,8 +70,11 @@ const config: ShellConfig = {
|
|
|
70
70
|
- **Drawing:** put canvas, WebGL and SVG instruments in a `Stage` (or `useStageSize`): nothing draws at zero
|
|
71
71
|
size, and colours come from `useThemeTokens()` (a canvas cannot read CSS variables).
|
|
72
72
|
- **Charts:** `import { UPlotChart } from "@fasl-work/caos-app-shell/chart"`: x and y titles with units, series
|
|
73
|
-
by token colour, `marks` for what the engine detected, a
|
|
74
|
-
|
|
73
|
+
by token colour, `marks` for what the engine detected, a key of every series when there are two or more, a cursor
|
|
74
|
+
readout row, theme and language rebuilds. Keep series labels short: the key wraps under the plot.
|
|
75
|
+
- **Numbers:** `formatNumber(value, lang, opts)` or `useFormat()`; never `toFixed` in a view. A magnitude below 1e-4
|
|
76
|
+
is written in scientific notation (a p-value of 1e-200 is `1E-200`, not two hundred zeros); give `digits`, not
|
|
77
|
+
`decimals`, for p-values.
|
|
75
78
|
- **Documentation routes:** `DocPage` and `DocSection` (each section ends in its own `Refs`, or states why it
|
|
76
79
|
cites nothing), `Equation` with a caption, `CitationsProvider` and `Cite` (every citation has a DOI or URL).
|
|
77
80
|
`Tabs`, `SubTabs` and `TabGroups` are controllable and render only the open panel inside a `PanelBoundary`.
|
package/chart.css
CHANGED
|
@@ -8,4 +8,12 @@
|
|
|
8
8
|
.caos-chart.fill { height: 100%; display: flex; flex-direction: column; min-height: 0; }
|
|
9
9
|
.caos-chart.fill > .caos-chart-plot { flex: 1 1 0; min-height: 0; overflow: hidden; }
|
|
10
10
|
.caos-chart-readout { margin: 0.15rem 0 0; font-family: var(--font-mono); font-size: 0.74rem; line-height: 1.3; color: var(--color-fg-subtle); min-height: 1.3em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
11
|
+
/* The series key (known shell defect 17): every series of a chart with two or more, always in view; it wraps. A line
|
|
12
|
+
swatch in the series' colour, dashed for a dashed series, a dot for a series drawn as points. */
|
|
13
|
+
.caos-chart-legend { list-style: none; margin: 0.25rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.1rem 0.8rem; font-size: 0.74rem; line-height: 1.35; color: var(--color-fg-subtle); }
|
|
14
|
+
.caos-chart.fill > .caos-chart-legend { flex: none; }
|
|
15
|
+
.caos-chart-key { display: inline-flex; align-items: center; gap: 0.3rem; min-width: 0; }
|
|
16
|
+
.caos-chart-swatch { flex: none; width: 16px; height: 0; border-top: 2px solid var(--swatch); }
|
|
17
|
+
.caos-chart-key[data-dash] .caos-chart-swatch { border-top-style: dashed; }
|
|
18
|
+
.caos-chart-key[data-mode="points"] .caos-chart-swatch { width: 7px; height: 7px; border: 0; border-radius: 50%; background: var(--swatch); }
|
|
11
19
|
.caos-chart .u-cursor-x, .caos-chart .u-cursor-y { border-color: var(--color-fg-faint) !important; }
|
package/dist/chart.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { F as FormatOptions, S as ShellToken } from './tokens-
|
|
2
|
+
import { F as FormatOptions, S as ShellToken } from './tokens-CSV0MOgR.js';
|
|
3
3
|
import { B as BiText } from './text-BlY47nIt.js';
|
|
4
4
|
import 'zustand';
|
|
5
5
|
|
|
@@ -48,8 +48,10 @@ interface UPlotChartProps {
|
|
|
48
48
|
* (a canvas cannot read CSS variables); it compares its options by value, never by identity, so a parent re-render
|
|
49
49
|
* does not tear it down; its tick formatters are null-safe and follow the interface language; the y axis is sized to
|
|
50
50
|
* its longest tick label, so no tick is cut; the cursor value is written into a readout row under the plot instead of
|
|
51
|
-
* uPlot's legend, which a sized host clips (known shell defect 3).
|
|
52
|
-
*
|
|
51
|
+
* uPlot's legend, which a sized host clips (known shell defect 3). A chart of two or more series keys every series
|
|
52
|
+
* under the plot, always (its colour, a dashed or a dotted swatch, its label): at rest the readout names none of them
|
|
53
|
+
* (known shell defect 17). The host declares `data-series`, `data-axis-titles`, `data-ticks-cut` and `data-drawn` for
|
|
54
|
+
* the gate.
|
|
53
55
|
*/
|
|
54
56
|
declare function UPlotChart({ x, y, series, marks, height, onCursor }: UPlotChartProps): react.JSX.Element;
|
|
55
57
|
|
package/dist/chart.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useShellLang, useThemeStore, useStageSize, pick, resolveToken, formatNumber } from './chunk-
|
|
1
|
+
import { useShellLang, useThemeStore, useStageSize, pick, resolveToken, formatNumber } from './chunk-LOSSTLPP.js';
|
|
2
2
|
import './chunk-X6Y5CDUD.js';
|
|
3
3
|
import { useRef, useCallback, useState, useMemo, useEffect } from 'react';
|
|
4
4
|
import uPlot from 'uplot';
|
|
@@ -140,6 +140,10 @@ function UPlotChart({ x, y, series, marks, height = 280, onCursor }) {
|
|
|
140
140
|
"data-drawn": width > 0 && h > 0 ? "1" : "0",
|
|
141
141
|
children: [
|
|
142
142
|
/* @__PURE__ */ jsx("div", { ref: setPlot, className: "caos-chart-plot" }),
|
|
143
|
+
series.length > 1 && /* @__PURE__ */ jsx("ul", { className: "caos-chart-legend", "aria-label": lang === "es" ? "Series del gr\xE1fico" : "Series of the chart", children: series.map((s, i) => /* @__PURE__ */ jsxs("li", { className: "caos-chart-key", "data-mode": s.mode ?? "line", "data-dash": s.dash ? "1" : void 0, children: [
|
|
144
|
+
/* @__PURE__ */ jsx("span", { className: "caos-chart-swatch", style: { "--swatch": `var(${s.color ?? ROTATION[i % ROTATION.length]})` }, "aria-hidden": "true" }),
|
|
145
|
+
pick(s.label, lang)
|
|
146
|
+
] }, i)) }),
|
|
143
147
|
/* @__PURE__ */ jsx("p", { className: "caos-chart-readout", "aria-live": "polite", title: read, children: read })
|
|
144
148
|
]
|
|
145
149
|
}
|
|
@@ -112,11 +112,13 @@ function useThemeTokens() {
|
|
|
112
112
|
|
|
113
113
|
// src/lib/format.ts
|
|
114
114
|
var LOCALE = { en: "en-US", es: "es-CL" };
|
|
115
|
+
var SCIENTIFIC_BELOW = 1e-4;
|
|
115
116
|
var NOT_AVAILABLE = { en: "not available", es: "no disponible" };
|
|
116
117
|
function formatNumber(value, lang, opts = {}) {
|
|
117
118
|
if (value === null || value === void 0 || !Number.isFinite(value)) return NOT_AVAILABLE[lang];
|
|
118
119
|
const v = opts.percent ? value * 100 : value;
|
|
119
|
-
const
|
|
120
|
+
const digits = opts.digits ?? 4;
|
|
121
|
+
const nf = opts.decimals !== void 0 ? new Intl.NumberFormat(LOCALE[lang], { minimumFractionDigits: opts.decimals, maximumFractionDigits: opts.decimals }) : v !== 0 && Math.abs(v) < SCIENTIFIC_BELOW ? new Intl.NumberFormat(LOCALE[lang], { notation: "scientific", maximumSignificantDigits: digits }) : Math.abs(v) >= 10 ** (digits - 1) ? new Intl.NumberFormat(LOCALE[lang], { maximumFractionDigits: 0 }) : new Intl.NumberFormat(LOCALE[lang], { maximumSignificantDigits: digits });
|
|
120
122
|
return opts.percent ? `${nf.format(v)} %` : nf.format(v);
|
|
121
123
|
}
|
|
122
124
|
function useFormat() {
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ export { A as AppShell, a as ArchTab, b as ArchitectureConfig, c as Architecture
|
|
|
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-
|
|
6
|
-
export { N as NOT_AVAILABLE, a as SHELL_TOKENS, S as ShellToken, f as formatNumber, r as resolveToken, u as useFormat, b as useThemeTokens } from './tokens-
|
|
5
|
+
import { F as FormatOptions } from './tokens-CSV0MOgR.js';
|
|
6
|
+
export { N as NOT_AVAILABLE, a as SHELL_TOKENS, S as ShellToken, f as formatNumber, r as resolveToken, u as useFormat, b as useThemeTokens } from './tokens-CSV0MOgR.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
|
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useThemeStore, useShellLang, useLangStore, SHELL_TOKENS, pick, formatNumber } from './chunk-
|
|
2
|
-
export { NOT_AVAILABLE, SHELL_TOKENS, Stage, applyTheme, formatNumber, pick, readTheme, resolveToken, useFormat, useLangStore, useShellLang, useStageSize, useThemeStore, useThemeTokens } from './chunk-
|
|
1
|
+
import { useThemeStore, useShellLang, useLangStore, SHELL_TOKENS, pick, formatNumber } from './chunk-LOSSTLPP.js';
|
|
2
|
+
export { NOT_AVAILABLE, SHELL_TOKENS, Stage, applyTheme, formatNumber, pick, readTheme, resolveToken, useFormat, useLangStore, useShellLang, useStageSize, useThemeStore, useThemeTokens } from './chunk-LOSSTLPP.js';
|
|
3
3
|
export { LANG_STORAGE_KEY, STANDARD_ROUTES, THEME_BOOT_SCRIPT, THEME_STORAGE_KEY } from './chunk-X6Y5CDUD.js';
|
|
4
4
|
import { createContext, Component, useState, useRef, useId, useEffect, useCallback, useContext, useMemo } from 'react';
|
|
5
5
|
import { useInRouterContext, NavLink, useLocation } from 'react-router';
|
|
@@ -397,7 +397,6 @@ function Frame({ config, pathname, routed, children }) {
|
|
|
397
397
|
/* @__PURE__ */ jsx("span", { children: config.product.name }),
|
|
398
398
|
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xB7" }),
|
|
399
399
|
/* @__PURE__ */ jsx("span", { children: c.complement }),
|
|
400
|
-
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xB7" }),
|
|
401
400
|
/* @__PURE__ */ jsx("span", { className: "footer-build", "data-version": config.version, "data-build": config.build ?? "", children: /* @__PURE__ */ jsxs("span", { children: [
|
|
402
401
|
c.version,
|
|
403
402
|
config.version,
|
|
@@ -2,9 +2,10 @@ import { L as Lang } from './text-BlY47nIt.js';
|
|
|
2
2
|
|
|
3
3
|
interface FormatOptions {
|
|
4
4
|
/** Significant digits for a value below 10^(digits-1) (default 4); a larger value shows every integer digit, so a
|
|
5
|
-
* count is never rounded (27345 people, not 27,350).
|
|
5
|
+
* count is never rounded (27345 people, not 27,350). A magnitude below 1e-4 is written in scientific notation with
|
|
6
|
+
* these significant digits (6.53E-13). Ignored when `decimals` is set. */
|
|
6
7
|
digits?: number;
|
|
7
|
-
/** Fixed number of decimals. */
|
|
8
|
+
/** Fixed number of decimals. The caller's explicit choice: a value below the last decimal reads as zero. */
|
|
8
9
|
decimals?: number;
|
|
9
10
|
/** Render a fraction as a percentage (0.123 -> 12.3 %). */
|
|
10
11
|
percent?: boolean;
|
|
@@ -13,7 +14,7 @@ interface FormatOptions {
|
|
|
13
14
|
declare const NOT_AVAILABLE: Record<Lang, string>;
|
|
14
15
|
/**
|
|
15
16
|
* A number in the interface language: a decimal point in English and a decimal comma in Spanish
|
|
16
|
-
* (`conventions/languages.md`), significant digits by default. `null`, `undefined` and non-finite values render as
|
|
17
|
+
* (`conventions/languages.md`), significant digits by default, scientific notation below 1e-4. `null`, `undefined` and non-finite values render as
|
|
17
18
|
* "not available", never as `NaN` (failure classes 5 and 23 of the 2026-10-04 history).
|
|
18
19
|
*/
|
|
19
20
|
declare function formatNumber(value: number | null | undefined, lang: Lang, opts?: FormatOptions): string;
|
package/gate/inpage.mjs
CHANGED
|
@@ -101,17 +101,23 @@ export function installLib() {
|
|
|
101
101
|
};
|
|
102
102
|
walk(body, false);
|
|
103
103
|
|
|
104
|
-
// Tab rows: one row each (ADR-0071 rule 4); tops clustered so a 1px active-tab offset is not a second row.
|
|
104
|
+
// Tab rows: one row each (ADR-0071 rule 4); tops clustered so a 1px active-tab offset is not a second row. A row
|
|
105
|
+
// also shows its tabs whole: a row that shrank under a tall panel cut its tabs' height (known shell defect 16).
|
|
105
106
|
const rows = [...document.querySelectorAll('[role="tablist"]')]
|
|
106
107
|
.filter((tl) => visible(tl) && tl.getAttribute('aria-orientation') !== 'vertical')
|
|
107
108
|
.map((tl) => {
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
.map((t) => t.getBoundingClientRect().top)
|
|
111
|
-
.sort((a, b) => a - b);
|
|
109
|
+
const tabs = [...tl.querySelectorAll('[role="tab"]')].filter((t) => t.closest('[role="tablist"]') === tl && visible(t));
|
|
110
|
+
const tops = tabs.map((t) => t.getBoundingClientRect().top).sort((a, b) => a - b);
|
|
112
111
|
let n = tops.length ? 1 : 0;
|
|
113
112
|
for (let i = 1; i < tops.length; i += 1) if (tops[i] - tops[i - 1] > 8) n += 1;
|
|
114
|
-
|
|
113
|
+
// a shrunk row either lets its tabs overflow it or stretches them shorter than their own content
|
|
114
|
+
const lr = tl.getBoundingClientRect();
|
|
115
|
+
let cut = Math.max(0, tl.scrollHeight - tl.clientHeight);
|
|
116
|
+
for (const t of tabs) {
|
|
117
|
+
const r = t.getBoundingClientRect();
|
|
118
|
+
cut = Math.max(cut, t.scrollHeight - t.clientHeight, Math.round(Math.max(0, lr.top - r.top) + Math.max(0, r.bottom - lr.bottom)));
|
|
119
|
+
}
|
|
120
|
+
return { name: tl.getAttribute('aria-label') || describe(tl), rows: n, cut };
|
|
115
121
|
});
|
|
116
122
|
|
|
117
123
|
// The rail: no scroll (rule 6) and every descendant inside its box (G5).
|
|
@@ -312,13 +318,15 @@ export function installLib() {
|
|
|
312
318
|
const s = getComputedStyle(a);
|
|
313
319
|
const ar = a.getBoundingClientRect();
|
|
314
320
|
let r = el.getBoundingClientRect();
|
|
321
|
+
// An element larger than the container is aligned by its start: aligning its end pushed its start out of
|
|
322
|
+
// view, and the probe then landed on whatever covers the container (known shell defect 18).
|
|
315
323
|
if ((s.overflowX === 'auto' || s.overflowX === 'scroll') && a.scrollWidth > a.clientWidth) {
|
|
316
|
-
if (r.left < ar.left) setScroll(a, a.scrollLeft - (ar.left - r.left
|
|
324
|
+
if (r.left < ar.left || r.width > ar.width) setScroll(a, a.scrollLeft - (ar.left - r.left), a.scrollTop);
|
|
317
325
|
else if (r.right > ar.right) setScroll(a, a.scrollLeft + (r.right - ar.right + 8), a.scrollTop);
|
|
318
326
|
}
|
|
319
327
|
r = el.getBoundingClientRect();
|
|
320
328
|
if ((s.overflowY === 'auto' || s.overflowY === 'scroll') && a.scrollHeight > a.clientHeight) {
|
|
321
|
-
if (r.top < ar.top) setScroll(a, a.scrollLeft, a.scrollTop - (ar.top - r.top
|
|
329
|
+
if (r.top < ar.top || r.height > ar.height) setScroll(a, a.scrollLeft, a.scrollTop - (ar.top - r.top));
|
|
322
330
|
else if (r.bottom > ar.bottom) setScroll(a, a.scrollLeft, a.scrollTop + (r.bottom - ar.bottom + 8));
|
|
323
331
|
}
|
|
324
332
|
}
|
|
@@ -334,7 +342,27 @@ export function installLib() {
|
|
|
334
342
|
return Boolean(el);
|
|
335
343
|
};
|
|
336
344
|
|
|
337
|
-
/**
|
|
345
|
+
/** The viewport cut by every ancestor that clips its content (overflow other than visible). */
|
|
346
|
+
const visibleArea = (el) => {
|
|
347
|
+
const area = { left: 0, top: 0, right: window.innerWidth, bottom: window.innerHeight };
|
|
348
|
+
for (let a = el.parentElement; a && a !== document.body && a !== document.documentElement; a = a.parentElement) {
|
|
349
|
+
const s = getComputedStyle(a);
|
|
350
|
+
if (s.overflowX === 'visible' && s.overflowY === 'visible') continue;
|
|
351
|
+
const ar = a.getBoundingClientRect();
|
|
352
|
+
if (s.overflowX !== 'visible') {
|
|
353
|
+
area.left = Math.max(area.left, ar.left);
|
|
354
|
+
area.right = Math.min(area.right, ar.right);
|
|
355
|
+
}
|
|
356
|
+
if (s.overflowY !== 'visible') {
|
|
357
|
+
area.top = Math.max(area.top, ar.top);
|
|
358
|
+
area.bottom = Math.min(area.bottom, ar.bottom);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return area;
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
/** Every visible control can be brought into the viewport and receives the pointer at the centre of its visible
|
|
365
|
+
* part (G5). */
|
|
338
366
|
G.reach = () => {
|
|
339
367
|
const sel = 'a[href], button, select, input:not([type="hidden"]), textarea, [role="tab"], [role="button"], [tabindex]:not([tabindex="-1"])';
|
|
340
368
|
const saved = new Map();
|
|
@@ -352,13 +380,25 @@ export function installLib() {
|
|
|
352
380
|
const r = frags.length ? frags.reduce((a, b) => (b.width * b.height > a.width * a.height ? b : a)) : el.getBoundingClientRect();
|
|
353
381
|
if (r.width < 1 || r.height < 1) continue;
|
|
354
382
|
checked += 1;
|
|
355
|
-
|
|
356
|
-
|
|
383
|
+
// The area the element can show in: the viewport cut by every ancestor that clips (known shell defect 18; an
|
|
384
|
+
// element that fits the window but not its scroll container was reported as never brought into view).
|
|
385
|
+
const area = visibleArea(el);
|
|
386
|
+
const fits = r.width <= area.right - area.left + 1 && r.height <= area.bottom - area.top + 1;
|
|
387
|
+
if (fits && (r.left < area.left - 1 || r.right > area.right + 1 || r.top < area.top - 1 || r.bottom > area.bottom + 1)) {
|
|
388
|
+
out.push(`${describe(el)} cannot be brought into the viewport (at ${Math.round(r.left)},${Math.round(r.top)})`);
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
// the pointer goes to the centre of the part that shows
|
|
392
|
+
const vx0 = Math.max(r.left, area.left);
|
|
393
|
+
const vx1 = Math.min(r.right, area.right);
|
|
394
|
+
const vy0 = Math.max(r.top, area.top);
|
|
395
|
+
const vy1 = Math.min(r.bottom, area.bottom);
|
|
396
|
+
if (vx1 - vx0 < 1 || vy1 - vy0 < 1) {
|
|
357
397
|
out.push(`${describe(el)} cannot be brought into the viewport (at ${Math.round(r.left)},${Math.round(r.top)})`);
|
|
358
398
|
continue;
|
|
359
399
|
}
|
|
360
|
-
const px = Math.min(Math.max(
|
|
361
|
-
const py = Math.min(Math.max(
|
|
400
|
+
const px = Math.min(Math.max((vx0 + vx1) / 2, 0), window.innerWidth - 1);
|
|
401
|
+
const py = Math.min(Math.max((vy0 + vy1) / 2, 0), window.innerHeight - 1);
|
|
362
402
|
const hit = document.elementFromPoint(px, py);
|
|
363
403
|
const ok =
|
|
364
404
|
hit && (hit === el || el.contains(hit) || (el.labels && [...el.labels].some((l) => l.contains(hit))) || (hit.tagName === 'LABEL' && hit.control === el));
|
package/gate/run.mjs
CHANGED
|
@@ -335,6 +335,7 @@ export async function runGate(o) {
|
|
|
335
335
|
if (f.beyondCount) fail('G5', where, `${f.beyondCount} element(s) outside the viewport: ${f.beyond.join('; ')}`);
|
|
336
336
|
if (big && f.docH > f.vh + 1) fail('ADR-0071.1', where, `the document scrolls: ${f.docH}px in a ${f.vh}px viewport (the page is the viewport)`);
|
|
337
337
|
for (const r of f.rows) if (r.rows > 1) fail('ADR-0071.4', where, `tab row "${r.name}" wraps onto ${r.rows} rows`);
|
|
338
|
+
for (const r of f.rows) if (r.cut > 1) fail('G5', where, `tab row "${r.name}" cuts its tabs by ${r.cut}px (a row shrunk under its panel)`);
|
|
338
339
|
if (f.rail) {
|
|
339
340
|
if (big && f.rail.scroll > f.rail.client + 1) fail('ADR-0071.6', where, `the rail scrolls: ${f.rail.scroll}px of content in ${f.rail.client}px`);
|
|
340
341
|
if (f.rail.outsideCount) fail('G5', where, `${f.rail.outsideCount} rail element(s) outside the rail: ${f.rail.outside.join('; ')}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fasl-work/caos-app-shell",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
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",
|
package/reserved-classes.json
CHANGED
package/styles.css
CHANGED
|
@@ -563,6 +563,11 @@ html, body { overflow-x: hidden; }
|
|
|
563
563
|
.caos-wb-rail .caos-variant-note { font-size: 0.8rem; }
|
|
564
564
|
.caos-cw-panel > .subtabs { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; margin-block: 0; }
|
|
565
565
|
.caos-cw-panel > .subtabs > .subtablist { margin-bottom: 0.45rem; }
|
|
566
|
+
/* Known shell defect 16: the group row and the sub-tab row are scroll containers (overflow-x auto), so their
|
|
567
|
+
automatic minimum height is zero, and in this column flex chain they shrank with a panel taller than the
|
|
568
|
+
instrument: the rows were clipped and the panel's top covered the sub-tabs (CAOS_Contraste, 2026-10-05). A tab
|
|
569
|
+
row keeps its height; the panel below it scrolls. */
|
|
570
|
+
.caos-cw > .tabs > .tablist, .caos-cw-panel > .subtabs > .subtablist { flex: none; }
|
|
566
571
|
.page-body.wide.caos-wb { padding-inline: 1rem; }
|
|
567
572
|
.caos-cw-panel > .subtabs > .subtabpanels, .caos-cw-panel > .subtabs > .subtabpanels > .subtabpanel { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
|
|
568
573
|
.caos-cw-panel > .subtabs > .subtabpanels > .subtabpanel { gap: 0.6rem; }
|