@wildwinter/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/README.md CHANGED
@@ -64,13 +64,61 @@ event loop (16ms), so a fast inner loop neither floods IPC nor stutters.
64
64
  **A strip, not a modal.** The window that started the job shows it; the rest of
65
65
  the app stays usable.
66
66
 
67
+ ## v0.18 (the bars, and the third semantic colour)
68
+
69
+ Three drawings each app had grown its own copy of, and the token the family kept
70
+ needing and never had.
71
+
72
+ - **`renderStepperBar`** - a bottom bar that walks a list of things to attend to,
73
+ one at a time: count, previous, position, next, the entry as a button, an
74
+ action slot. Four bars across the two apps are this shape (problems, review
75
+ walks). It knows about a list and an index; it knows nothing about what a
76
+ problem or a comment is.
77
+
78
+ ```ts
79
+ import { renderStepperBar } from "@wildwinter/app-shell";
80
+ import "@wildwinter/app-shell/stepper.css";
81
+ renderStepperBar(host, {
82
+ items: problems.map((p) => ({ kind: p.severity, kindClass: `sev-${p.severity}`, where: p.path, text: p.message })),
83
+ at, tone: "danger", onStep: (i) => { at = i; paint(); }, onGo: (i) => jumpTo(problems[i]),
84
+ });
85
+ ```
86
+
87
+ Whether stepping NAVIGATES is the caller's decision, and the bar takes no view:
88
+ an ambient surface moves the view and never the focus; a mode you entered may
89
+ take you somewhere.
90
+
91
+ - **`saveIndicator`** - the six lines that draw what `createSaveController`
92
+ already computes. Three states, always present: a dot that only ever appears
93
+ to worry you leaves "did that get written?" unanswered.
94
+
95
+ ```ts
96
+ const ind = saveIndicator();
97
+ topbar.append(ind.el);
98
+ const saver = createSaveController({ write, onStatus: ind.set });
99
+ ```
100
+
101
+ - **`staleBar`** - a running session that has fallen behind its source. The app
102
+ supplies the noun and the callback; the sentence is shared, because both apps
103
+ had written it independently and landed a word apart.
104
+
105
+ ```ts
106
+ staleBar({ subject: "The project", onRestart: () => void rebuild() });
107
+ ```
108
+
109
+ - **`--ok`** joins `--danger` and `--warn` in `tokens.css`, with a `light-dark()`
110
+ default every palette should override in its own `theme.css`. It is the only
111
+ colour in the grammar layer, and it is there because it kept not being
112
+ anywhere: "all is well" was being met with raw green literals that could not
113
+ theme-shift.
114
+
67
115
  ## Roadmap
68
116
 
69
117
  Later slices lift the rest of the common shell from Patterpad (canonical),
70
- reconciling Storylet Studio's variants: the pane grid + toggles, the save/dirty
71
- controller, the command palette, the anchored popover/panel + toast, and the
72
- detached-window out-of-date / restart pattern. Storylet Studio migrates each
73
- first (proving app-agnosticism); Patterpad follows.
118
+ reconciling Storylet Studio's variants: the pane grid + toggles, the command
119
+ palette, and version control's renderer half. Storylet Studio migrates each
120
+ first (proving app-agnosticism); Patterpad follows - see
121
+ `patterkit/design/from-storylets/patterpad-onto-the-shell.md` for that order.
74
122
 
75
123
  ## Release
76
124
 
package/dist/index.cjs CHANGED
@@ -136,6 +136,15 @@ var icon = {
136
136
  dot: "\u25CF",
137
137
  /** Play, in the sense of "run this". */
138
138
  play: "\u25B6",
139
+ /** Start the thing again from the top: a play session, a sweep. Named for the
140
+ * act, not the arrow, which is the table's rule. */
141
+ restart: "\u27F2",
142
+ /** The three ways a collection can be shown. Named by what you get rather than
143
+ * by the glyph, so a second app can pick the same three without copying
144
+ * somebody's taste in symbols. */
145
+ viewCards: "\u25A6",
146
+ viewTable: "\u2630",
147
+ viewNode: "\u25C8",
139
148
  // --- handling -------------------------------------------------------------
140
149
  /** The drag grip on a reorderable row. */
141
150
  grip: "\u283F",
@@ -1379,6 +1388,99 @@ function createSaveController(opts) {
1379
1388
  };
1380
1389
  }
1381
1390
 
1391
+ // src/save-view.ts
1392
+ var WORDS = {
1393
+ saved: "Saved",
1394
+ saving: "Saving\u2026",
1395
+ unsaved: "Unsaved"
1396
+ };
1397
+ function saveIndicator(initial = "saved") {
1398
+ const node = el("span");
1399
+ const set = (status) => {
1400
+ node.className = `savestat ${status}`;
1401
+ node.textContent = WORDS[status];
1402
+ };
1403
+ set(initial);
1404
+ return { el: node, set };
1405
+ }
1406
+
1407
+ // src/stale.ts
1408
+ function staleBar(opts) {
1409
+ const label2 = opts.restartLabel ?? "Restart";
1410
+ return el(
1411
+ "div",
1412
+ { className: "stale-bar" },
1413
+ el("span", {
1414
+ className: "stale-bar-msg",
1415
+ text: `${opts.subject} changed in the editor. ${opts.advice ?? "Restart to play the new version."}`
1416
+ }),
1417
+ el("button", {
1418
+ className: "stale-bar-go",
1419
+ text: `${icon.restart} ${label2}`,
1420
+ onClick: () => opts.onRestart()
1421
+ })
1422
+ );
1423
+ }
1424
+
1425
+ // src/stepper.ts
1426
+ function renderStepperBar(host, opts) {
1427
+ const { items } = opts;
1428
+ const close = opts.onClose === void 0 ? null : el("button", {
1429
+ className: "stepbar-nav stepbar-close",
1430
+ text: icon.close,
1431
+ tip: opts.closeTip ?? "Close",
1432
+ onClick: opts.onClose
1433
+ });
1434
+ const actions = (opts.actions ?? []).filter((a) => a !== null && a !== void 0);
1435
+ if (items.length === 0) {
1436
+ if (opts.empty === void 0) {
1437
+ host.hidden = true;
1438
+ host.replaceChildren();
1439
+ return;
1440
+ }
1441
+ host.hidden = false;
1442
+ host.replaceChildren(
1443
+ el("span", { className: "stepbar-count muted", text: "0" }),
1444
+ el("span", { className: "stepbar-empty", text: opts.empty }),
1445
+ ...actions,
1446
+ ...close ? [close] : []
1447
+ );
1448
+ return;
1449
+ }
1450
+ host.hidden = false;
1451
+ const index = Math.min(Math.max(opts.at, 0), items.length - 1);
1452
+ const item = items[index];
1453
+ const step = (delta) => opts.onStep((index + delta + items.length) % items.length);
1454
+ const nav = items.length > 1 ? [
1455
+ el("button", {
1456
+ className: "stepbar-nav",
1457
+ text: icon.back,
1458
+ tip: opts.tips?.prev ?? "Previous",
1459
+ onClick: () => step(-1)
1460
+ }),
1461
+ el("span", { className: "stepbar-of", text: `${index + 1}/${items.length}` }),
1462
+ el("button", {
1463
+ className: "stepbar-nav",
1464
+ text: icon.forward,
1465
+ tip: opts.tips?.next ?? "Next",
1466
+ onClick: () => step(1)
1467
+ })
1468
+ ] : [];
1469
+ host.replaceChildren(
1470
+ el("span", { className: `stepbar-count ${opts.tone ?? "muted"}`, text: String(items.length) }),
1471
+ ...nav,
1472
+ el(
1473
+ "button",
1474
+ { className: "stepbar-cur", tip: opts.tips?.go ?? "Go to this", onClick: () => opts.onGo(index) },
1475
+ item.kind === void 0 ? null : el("span", { className: `stepbar-cat${item.kindClass ? ` ${item.kindClass}` : ""}`, text: item.kind }),
1476
+ item.where === void 0 ? null : el("span", { className: "stepbar-where", text: item.where }),
1477
+ el("span", { className: "stepbar-msg", text: item.text })
1478
+ ),
1479
+ ...actions,
1480
+ ...close ? [close] : []
1481
+ );
1482
+ }
1483
+
1382
1484
  exports.EDIT_MENU = EDIT_MENU;
1383
1485
  exports.JOB_PROGRESS = JOB_PROGRESS;
1384
1486
  exports.PALETTE = PALETTE;
@@ -1416,7 +1518,10 @@ exports.openNotesEditor = openNotesEditor;
1416
1518
  exports.paintVcBadges = paintVcBadges;
1417
1519
  exports.pinButton = pinButton;
1418
1520
  exports.placeAnchored = placeAnchored;
1521
+ exports.renderStepperBar = renderStepperBar;
1522
+ exports.saveIndicator = saveIndicator;
1419
1523
  exports.showAbout = showAbout;
1524
+ exports.staleBar = staleBar;
1420
1525
  exports.tagChips = tagChips;
1421
1526
  exports.tipAt = tipAt;
1422
1527
  exports.tipBold = tipBold;