@ashley-shrok/viewmodel-shell 0.4.5 → 0.4.7
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/tui.js +39 -6
- package/package.json +1 -1
package/dist/tui.js
CHANGED
|
@@ -12,6 +12,7 @@ const NO_CTX = {
|
|
|
12
12
|
copiedKey: null,
|
|
13
13
|
focusKey: () => undefined,
|
|
14
14
|
interactive: false,
|
|
15
|
+
fill: false,
|
|
15
16
|
draft: () => undefined,
|
|
16
17
|
};
|
|
17
18
|
/** OSC 52 clipboard write — the terminal-native analog of the clipboard API.
|
|
@@ -987,6 +988,8 @@ function App(props) {
|
|
|
987
988
|
copiedKey,
|
|
988
989
|
focusKey: (o) => map.get(o),
|
|
989
990
|
interactive,
|
|
991
|
+
fill: fillViewport,
|
|
992
|
+
fillCols: fillViewport ? vp.cols : undefined,
|
|
990
993
|
draft: (k) => draftFor(k),
|
|
991
994
|
onFieldChange: (k, v) => setDraft((s) => ({ ...s, [k]: v })),
|
|
992
995
|
onFieldSubmit,
|
|
@@ -1242,25 +1245,55 @@ export class TuiAdapter {
|
|
|
1242
1245
|
}
|
|
1243
1246
|
}
|
|
1244
1247
|
/** page & section share the 4 layout presets. Information-honest, not pixel. */
|
|
1245
|
-
layoutContainer(layout, children, density, rctx) {
|
|
1248
|
+
layoutContainer(layout, children, density, rctx, topLevel = false) {
|
|
1246
1249
|
const sp = this.spacing(density);
|
|
1247
1250
|
const kids = (children ?? []).map((c, i) => this.renderNode(c, this.keyOf(c, i), density, undefined, rctx));
|
|
1251
|
+
// 0.4.7 — numeric width ONLY at the page's TOP layout container (it IS
|
|
1252
|
+
// the full terminal width). Everything below fills via Yoga align-stretch
|
|
1253
|
+
// from this numeric anchor (proven on the real-adapter oracle). NESTED
|
|
1254
|
+
// layoutContainer calls (a section's own children) pass topLevel=false ⇒
|
|
1255
|
+
// no width ⇒ they get the correct PANE-relative width via stretch (a
|
|
1256
|
+
// global numeric cols here would overflow a narrower pane). `cards` never
|
|
1257
|
+
// gets it (uniform small-tile grid by design). Not top-level / not
|
|
1258
|
+
// filling ⇒ {} ⇒ byte-identical.
|
|
1259
|
+
const fillW = topLevel && rctx.fillCols ? { width: rctx.fillCols } : {};
|
|
1248
1260
|
switch (layout) {
|
|
1249
1261
|
case "split":
|
|
1250
|
-
return (_jsx(Box, { flexDirection: "row", gap: sp.gap || 1, children: kids.map((el, i) => (_jsx(Box, { flexGrow: 1, flexShrink: 1, flexBasis: 0, children: el }, i))) }));
|
|
1262
|
+
return (_jsx(Box, { flexDirection: "row", gap: sp.gap || 1, ...fillW, children: kids.map((el, i) => (_jsx(Box, { flexGrow: 1, flexShrink: 1, flexBasis: 0, children: el }, i))) }));
|
|
1251
1263
|
case "cards":
|
|
1252
1264
|
return (_jsx(Box, { flexDirection: "row", flexWrap: "wrap", gap: sp.gap || 1, children: kids.map((el, i) => (_jsx(Box, { flexGrow: 0, flexShrink: 1, flexBasis: 28, minWidth: 20, children: el }, i))) }));
|
|
1253
1265
|
case "sidebar": {
|
|
1254
1266
|
if (kids.length === 0)
|
|
1255
1267
|
return _jsx(Box, {});
|
|
1256
1268
|
const [rail, ...rest] = kids;
|
|
1269
|
+
// Deterministic NUMERIC split when filling. `flexGrow` does NOT
|
|
1270
|
+
// distribute reliably past a flexShrink:0 fixed-basis rail (real-
|
|
1271
|
+
// adapter oracle: sidebar stayed 34 at cols 80 AND 160, while `split`
|
|
1272
|
+
// — symmetric flexGrow — scaled). So when filling: fixed numeric rail
|
|
1273
|
+
// + the exact remaining width on the main pane (it then carries the
|
|
1274
|
+
// terminal width to its section via align-stretch). Not filling ⇒ the
|
|
1275
|
+
// ORIGINAL flex props, byte-identical.
|
|
1276
|
+
const cols = topLevel && rctx.fillCols ? rctx.fillCols : 0;
|
|
1277
|
+
const filling = cols > 0;
|
|
1278
|
+
const RAIL = 24;
|
|
1279
|
+
const g = sp.gap || 1;
|
|
1280
|
+
const mw = Math.max(1, cols - RAIL - g);
|
|
1257
1281
|
if (rest.length === 0) {
|
|
1258
|
-
return
|
|
1282
|
+
return filling ? (
|
|
1283
|
+
// flexDirection:column ⇒ the lone section align-stretches to the
|
|
1284
|
+
// numeric width (a default-row box would not width-stretch it).
|
|
1285
|
+
_jsx(Box, { flexDirection: "column", flexShrink: 0, ...fillW, children: rail })) : (_jsx(Box, { flexShrink: 0, flexBasis: 24, minWidth: 18, children: rail }));
|
|
1259
1286
|
}
|
|
1260
|
-
return (_jsxs(Box, { flexDirection: "row", gap:
|
|
1287
|
+
return (_jsxs(Box, { flexDirection: "row", gap: g, ...fillW, children: [filling ? (_jsx(Box, { flexShrink: 0, width: RAIL, children: rail })) : (_jsx(Box, { flexShrink: 0, flexBasis: 24, minWidth: 18, children: rail })), filling ? (
|
|
1288
|
+
// Single numeric-width column directly holding the detail
|
|
1289
|
+
// sections — exactly the `stack` shape the oracle proves scales
|
|
1290
|
+
// (a card section align-stretches to a numeric-width parent).
|
|
1291
|
+
// The extra auto-width wrapper that used to sit here broke that
|
|
1292
|
+
// align-stretch chain (oracle: card detail stuck at 38).
|
|
1293
|
+
_jsx(Box, { flexDirection: "column", gap: sp.gap, flexShrink: 1, width: mw, children: rest })) : (_jsx(Box, { flexGrow: 1, flexShrink: 1, flexBasis: 0, children: _jsx(Box, { flexDirection: "column", gap: sp.gap, children: rest }) }))] }));
|
|
1261
1294
|
}
|
|
1262
1295
|
default: // "stack" / undefined
|
|
1263
|
-
return (_jsx(Box, { flexDirection: "column", gap: sp.gap, children: kids }));
|
|
1296
|
+
return (_jsx(Box, { flexDirection: "column", gap: sp.gap, ...fillW, children: kids }));
|
|
1264
1297
|
}
|
|
1265
1298
|
}
|
|
1266
1299
|
// ── the node switch ──────────────────────────────────────────────────────
|
|
@@ -1269,7 +1302,7 @@ export class TuiAdapter {
|
|
|
1269
1302
|
case "page": {
|
|
1270
1303
|
const d = node.density === "compact" ? "compact" : "comfortable";
|
|
1271
1304
|
const sp = this.spacing(d);
|
|
1272
|
-
return (_jsxs(Box, { flexDirection: "column", gap: sp.gap, children: [node.title ? (_jsx(Box, { children: _jsx(Text, { bold: true, underline: true, children: node.title }) })) : null, this.layoutContainer(node.layout, node.children ?? [], d, rctx)] }, key));
|
|
1305
|
+
return (_jsxs(Box, { flexDirection: "column", gap: sp.gap, ...(rctx.fillCols ? { width: rctx.fillCols, flexGrow: 1 } : {}), children: [node.title ? (_jsx(Box, { children: _jsx(Text, { bold: true, underline: true, children: node.title }) })) : null, this.layoutContainer(node.layout, node.children ?? [], d, rctx, true)] }, key));
|
|
1273
1306
|
}
|
|
1274
1307
|
case "section": {
|
|
1275
1308
|
const sp = this.spacing(density);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ashley-shrok/viewmodel-shell",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.7",
|
|
4
4
|
"description": "A server-driven UI framework where the wire format is structured enough that agents can build full-stack apps without ever opening a browser and all UI tests are pure unit tests with no browser runtime. Server returns a JSON tree of typed nodes; a thin TypeScript adapter renders it to DOM. Backend-agnostic — a .NET reference backend ships with the repo, but any language can produce the JSON contract.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|