@wildwinter/app-shell 0.46.5 → 0.48.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.
Files changed (56) hide show
  1. package/LICENSES.md +5 -4
  2. package/dist/{chunk-J6RSONXM.js → chunk-62KWJPRP.js} +4 -4
  3. package/dist/{chunk-J6RSONXM.js.map → chunk-62KWJPRP.js.map} +1 -1
  4. package/dist/{chunk-WKPKBAXS.js → chunk-C245WRPF.js} +4 -4
  5. package/dist/{chunk-WKPKBAXS.js.map → chunk-C245WRPF.js.map} +1 -1
  6. package/dist/{chunk-R3NXPHRZ.js → chunk-CRUILFAT.js} +28 -6
  7. package/dist/chunk-CRUILFAT.js.map +1 -0
  8. package/dist/{chunk-EHR2KBZC.js → chunk-OMG6A5YB.js} +3 -3
  9. package/dist/{chunk-EHR2KBZC.js.map → chunk-OMG6A5YB.js.map} +1 -1
  10. package/dist/{chunk-2ECJUTSX.js → chunk-T25WXWPB.js} +3 -2
  11. package/dist/chunk-T25WXWPB.js.map +1 -0
  12. package/dist/{chunk-B2TC6Q5J.js → chunk-UQTUCOGV.js} +3 -3
  13. package/dist/{chunk-B2TC6Q5J.js.map → chunk-UQTUCOGV.js.map} +1 -1
  14. package/dist/{chunk-YRFJLJUH.js → chunk-VXEFIL43.js} +3 -3
  15. package/dist/{chunk-YRFJLJUH.js.map → chunk-VXEFIL43.js.map} +1 -1
  16. package/dist/{chunk-UG2P777F.js → chunk-W4WC4FO5.js} +102 -30
  17. package/dist/chunk-W4WC4FO5.js.map +1 -0
  18. package/dist/{chunk-CUFREPJ5.js → chunk-XSS7KM6F.js} +4 -4
  19. package/dist/{chunk-CUFREPJ5.js.map → chunk-XSS7KM6F.js.map} +1 -1
  20. package/dist/context-menu.cjs.map +1 -1
  21. package/dist/context-menu.js +2 -2
  22. package/dist/dialog.cjs.map +1 -1
  23. package/dist/dialog.js +2 -2
  24. package/dist/index.cjs +146 -36
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +97 -14
  27. package/dist/index.d.ts +97 -14
  28. package/dist/index.js +31 -17
  29. package/dist/index.js.map +1 -1
  30. package/dist/kit-gallery.cjs.map +1 -1
  31. package/dist/kit-gallery.js +3 -3
  32. package/dist/link-status.cjs +7 -0
  33. package/dist/link-status.cjs.map +1 -1
  34. package/dist/link-status.js +2 -2
  35. package/dist/menu.cjs +1 -0
  36. package/dist/menu.cjs.map +1 -1
  37. package/dist/menu.d.cts +8 -4
  38. package/dist/menu.d.ts +8 -4
  39. package/dist/menu.js +1 -1
  40. package/dist/{property-list-D3VZCJzi.d.cts → property-list-BgZlkcbz.d.cts} +19 -2
  41. package/dist/{property-list-D3VZCJzi.d.ts → property-list-BgZlkcbz.d.ts} +19 -2
  42. package/dist/property-list.cjs +93 -27
  43. package/dist/property-list.cjs.map +1 -1
  44. package/dist/property-list.d.cts +1 -1
  45. package/dist/property-list.d.ts +1 -1
  46. package/dist/property-list.js +2 -2
  47. package/dist/settings.css +78 -0
  48. package/dist/settings.css.map +1 -1
  49. package/dist/updater-view.cjs.map +1 -1
  50. package/dist/updater-view.js +3 -3
  51. package/dist/welcome.cjs.map +1 -1
  52. package/dist/welcome.js +4 -4
  53. package/package.json +1 -1
  54. package/dist/chunk-2ECJUTSX.js.map +0 -1
  55. package/dist/chunk-R3NXPHRZ.js.map +0 -1
  56. package/dist/chunk-UG2P777F.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -285,11 +285,18 @@ var DRAWN = {
285
285
  viewNode: stroked('<path d="m10.586 5.414-5.172 5.172"/><path d="m18.586 13.414-5.172 5.172"/><path d="M6 12h12"/><circle cx="12" cy="20" r="2"/><circle cx="12" cy="4" r="2"/><circle cx="20" cy="12" r="2"/><circle cx="4" cy="12" r="2"/>'),
286
286
  /** Record: the disc, in whatever colour the button is. (a disc; see DISC_LARGE) */
287
287
  record: DISC_LARGE,
288
+ /** Sound: play with audio. (volume-2: the cone and two waves. Patterpad's
289
+ * hand-drawn one had a single wave, which at 14px reads as quiet rather
290
+ * than as sound.) */
291
+ speaker: stroked('<path d="M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z"/><path d="M16 9a5 5 0 0 1 0 6"/><path d="M19.364 18.364a9 9 0 0 0 0-12.728"/>'),
288
292
  // --- handling -------------------------------------------------------------
289
293
  /** The drag grip on a reorderable row. (grip-vertical) */
290
294
  grip: stroked('<circle cx="9" cy="12" r="1"/><circle cx="9" cy="5" r="1"/><circle cx="9" cy="19" r="1"/><circle cx="15" cy="12" r="1"/><circle cx="15" cy="5" r="1"/><circle cx="15" cy="19" r="1"/>'),
291
295
  /** Add one of whatever this list holds. (plus) */
292
296
  add: stroked('<path d="M5 12h14"/><path d="M12 5v14"/>'),
297
+ /** Copy this to the clipboard. (copy: the front sheet whole, the back
298
+ * sheet showing round two of its sides) */
299
+ copy: stroked('<rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>'),
293
300
  /** Connect: to a server, a live link, a running game. (plug) */
294
301
  connect: stroked('<path d="M12 22v-5"/><path d="M15 8V2"/><path d="M17 8a1 1 0 0 1 1 1v4a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1z"/><path d="M9 8V2"/>'),
295
302
  /** Search. (search) */
@@ -379,15 +386,36 @@ function iconBtn(name, title, onClick, disabled = false, danger = false) {
379
386
  b.addEventListener("click", onClick);
380
387
  return b;
381
388
  }
382
- function labelled(label2, control) {
389
+ function labelled(label2, control, hint) {
383
390
  const target = control.matches("input, select, textarea") ? control : control.querySelector("input:not([type=button]):not([type=submit]), select, textarea");
384
391
  const w = el(target ? "label" : "div", "shell-labelled");
385
392
  w.append(el("span", "shell-fieldcap", label2), control);
386
393
  if (target) {
387
- if (!target.id) target.id = `shell-f-${Math.random().toString(36).slice(2, 9)}`;
394
+ if (!target.id) target.id = uid();
388
395
  w.htmlFor = target.id;
389
396
  }
390
- return w;
397
+ if (hint === void 0) return w;
398
+ const note = el("p", "shell-fieldhint");
399
+ note.append(...Array.isArray(hint) ? hint : [hint]);
400
+ note.id = uid();
401
+ if (target) {
402
+ const by = target.getAttribute("aria-describedby");
403
+ target.setAttribute("aria-describedby", by ? `${by} ${note.id}` : note.id);
404
+ }
405
+ return el("div", "shell-field", w, note);
406
+ }
407
+ var uid = () => `shell-f-${Math.random().toString(36).slice(2, 9)}`;
408
+ function labelledToggle(label2, opts = {}) {
409
+ const input = el("input", "shell-toggle-input");
410
+ input.type = "checkbox";
411
+ input.checked = opts.checked ?? false;
412
+ input.disabled = opts.disabled ?? false;
413
+ const { onChange } = opts;
414
+ if (onChange) input.addEventListener("change", () => onChange(input.checked));
415
+ const row = labelled(label2, input, opts.hint);
416
+ (row.matches(".shell-labelled") ? row : row.querySelector(".shell-labelled"))?.classList.add("shell-toggle");
417
+ if (opts.sub) row.classList.add("shell-suboption");
418
+ return { row, input };
391
419
  }
392
420
  function moveItem(arr, i, delta) {
393
421
  const j = i + delta;
@@ -436,7 +464,7 @@ function tagChips(holder, onChange) {
436
464
  wrap.append(input);
437
465
  return wrap;
438
466
  }
439
- function stageChips(holder, onChange) {
467
+ function stageChips(holder, onChange, opts = {}) {
440
468
  const wrap = el("div", "shell-tags shell-stages");
441
469
  const input = el("input", "shell-tag-input");
442
470
  input.type = "text";
@@ -451,35 +479,79 @@ function stageChips(holder, onChange) {
451
479
  b.addEventListener("click", onClick);
452
480
  return b;
453
481
  };
482
+ const nameLabel = (chip2, v, i) => {
483
+ const label2 = el("span", "shell-tag-label", `${i + 1}. ${v}`);
484
+ label2.setAttribute("role", "button");
485
+ label2.tabIndex = 0;
486
+ label2.dataset["tip"] = `Rename ${v}`;
487
+ const edit = () => {
488
+ const field = el("input", "shell-tag-input shell-stage-rename");
489
+ field.type = "text";
490
+ field.value = v;
491
+ field.spellcheck = false;
492
+ field.setAttribute("aria-label", `Rename ${v}`);
493
+ let done = false;
494
+ const finish = (keep) => {
495
+ if (done) return;
496
+ done = true;
497
+ const to = field.value.trim();
498
+ const stages = holder.stages ?? [];
499
+ if (keep && to !== "" && to !== v && !stages.includes(to)) {
500
+ stages[stages.indexOf(v)] = to;
501
+ rebuild();
502
+ if (opts.onRename) opts.onRename(v, to);
503
+ else onChange?.();
504
+ } else {
505
+ rebuild();
506
+ }
507
+ };
508
+ field.addEventListener("keydown", (e) => {
509
+ if (e.key === "Enter") {
510
+ e.preventDefault();
511
+ finish(true);
512
+ } else if (e.key === "Escape") {
513
+ e.preventDefault();
514
+ e.stopPropagation();
515
+ finish(false);
516
+ }
517
+ });
518
+ field.addEventListener("blur", () => finish(true));
519
+ label2.replaceWith(field);
520
+ chip2.classList.add("editing");
521
+ field.focus();
522
+ field.select();
523
+ };
524
+ label2.addEventListener("click", (e) => {
525
+ e.preventDefault();
526
+ edit();
527
+ });
528
+ label2.addEventListener("keydown", (e) => {
529
+ if (e.key === "Enter" || e.key === " ") {
530
+ e.preventDefault();
531
+ edit();
532
+ }
533
+ });
534
+ return label2;
535
+ };
536
+ const moved = () => {
537
+ rebuild();
538
+ onChange?.();
539
+ opts.onReorder?.();
540
+ };
454
541
  const rebuild = () => {
455
542
  for (const c of Array.from(wrap.children)) if (c !== input) c.remove();
456
543
  const stages = holder.stages ?? [];
457
544
  stages.forEach((v, i) => {
458
- const chip2 = el("span", "shell-tag", `${i + 1}. ${v}`);
545
+ const chip2 = el("span", "shell-tag");
459
546
  chip2.dataset["stage"] = v;
460
547
  chip2.append(
461
- control(
462
- "back",
463
- `Move ${v} earlier`,
464
- i === 0,
465
- () => {
466
- if (moveItem(stages, i, -1)) {
467
- rebuild();
468
- onChange?.();
469
- }
470
- }
471
- ),
472
- control(
473
- "forward",
474
- `Move ${v} later`,
475
- i === stages.length - 1,
476
- () => {
477
- if (moveItem(stages, i, 1)) {
478
- rebuild();
479
- onChange?.();
480
- }
481
- }
482
- )
548
+ nameLabel(chip2, v, i),
549
+ control("back", `Move ${v} earlier`, i === 0, () => {
550
+ if (moveItem(stages, i, -1)) moved();
551
+ }),
552
+ control("forward", `Move ${v} later`, i === stages.length - 1, () => {
553
+ if (moveItem(stages, i, 1)) moved();
554
+ })
483
555
  );
484
556
  const x = el("button", "shell-tag-x", iconNode("close", CHIP_ICON));
485
557
  x.type = "button";
@@ -935,6 +1007,7 @@ var FILE_MENU = {
935
1007
  projectSettings: { label: "Project Settings\u2026", accelerator: "CmdOrCtrl+," }
936
1008
  };
937
1009
  var PLAY_MENU = {
1010
+ play: { accelerator: "CmdOrCtrl+P" },
938
1011
  liveLink: { label: "Live Link" }
939
1012
  };
940
1013
  var REVIEW_MENU = {
@@ -1197,9 +1270,17 @@ function mountSettingsDialog(opts) {
1197
1270
  error.hidden = false;
1198
1271
  return;
1199
1272
  }
1273
+ save.disabled = true;
1200
1274
  void (async () => {
1201
- await opts.onSave();
1202
- dialog.close("save");
1275
+ let saved;
1276
+ try {
1277
+ saved = await opts.onSave();
1278
+ } catch (err) {
1279
+ saved = false;
1280
+ console.error("Settings save failed:", err);
1281
+ }
1282
+ save.disabled = false;
1283
+ if (saved !== false) dialog.close("save");
1203
1284
  })();
1204
1285
  });
1205
1286
  return {
@@ -2104,16 +2185,19 @@ var CLASS_LABEL = {
2104
2185
  everyone: "Everyone",
2105
2186
  writing: "Writing",
2106
2187
  vo: "Voice (VO)",
2107
- loc: "Localisers"
2188
+ loc: "Localisers",
2189
+ editor: "Editors"
2108
2190
  };
2109
2191
  var label = (cls) => cls === EDITOR_ONLY ? "Note (editor-only)" : CLASS_LABEL[cls] ?? cls[0].toUpperCase() + cls.slice(1);
2110
2192
  var PLACEHOLDER = {
2111
2193
  vo: "Direction for the voice actor or director",
2112
- loc: "Context for translators"
2194
+ loc: "Context for translators",
2195
+ editor: "Context for an outside editor"
2113
2196
  };
2114
2197
  var placeholder = (cls) => cls === EDITOR_ONLY ? "An internal note, never exported" : PLACEHOLDER[cls] ?? "Why this is here";
2115
2198
  function openNotesEditor(opts) {
2116
2199
  const { notes } = opts;
2200
+ const inherited = notes.inherited ?? [];
2117
2201
  const seed = /* @__PURE__ */ new Map();
2118
2202
  for (const line of notes.own) {
2119
2203
  const key = line.type ?? EDITOR_ONLY;
@@ -2121,6 +2205,8 @@ function openNotesEditor(opts) {
2121
2205
  ${line.text}` : line.text);
2122
2206
  }
2123
2207
  const shown = [...seed.keys()];
2208
+ const focus = opts.focus !== void 0 && (opts.focus === EDITOR_ONLY || notes.classes.includes(opts.focus) || seed.has(opts.focus)) ? opts.focus : void 0;
2209
+ if (focus !== void 0 && !shown.includes(focus)) shown.push(focus);
2124
2210
  if (shown.length === 0) shown.push(notes.classes[0] ?? EDITOR_ONLY);
2125
2211
  const areas = /* @__PURE__ */ new Map();
2126
2212
  const value = () => {
@@ -2138,13 +2224,13 @@ ${line.text}` : line.text);
2138
2224
  const body = el("div", "shell-notes-body");
2139
2225
  const render = () => {
2140
2226
  body.replaceChildren();
2141
- if (notes.inherited.length > 0) {
2227
+ if (inherited.length > 0) {
2142
2228
  const from = el(
2143
2229
  "div",
2144
2230
  { className: "shell-notes-inherited" },
2145
2231
  el("div", "shell-notes-caption", "Inherited")
2146
2232
  );
2147
- for (const level of notes.inherited) {
2233
+ for (const level of inherited) {
2148
2234
  from.append(el(
2149
2235
  "div",
2150
2236
  { className: "shell-notes-level" },
@@ -2201,7 +2287,12 @@ ${line.text}` : line.text);
2201
2287
  const next = value();
2202
2288
  if (JSON.stringify(next) !== before) opts.save(next);
2203
2289
  };
2204
- const frame = dialogFrame({ title: `Notes: ${opts.subject}`, className: "shell-notes", onClose: commit });
2290
+ const frame = dialogFrame({
2291
+ title: opts.subject !== void 0 ? `Notes: ${opts.subject}` : "Notes",
2292
+ ...opts.sub !== void 0 ? { sub: opts.sub } : {},
2293
+ className: "shell-notes",
2294
+ onClose: commit
2295
+ });
2205
2296
  frame.body.append(body);
2206
2297
  frame.actions.append(done);
2207
2298
  done.addEventListener("click", () => {
@@ -2209,7 +2300,7 @@ ${line.text}` : line.text);
2209
2300
  frame.close();
2210
2301
  });
2211
2302
  frame.open();
2212
- areas.get(shown[0] ?? EDITOR_ONLY)?.focus();
2303
+ areas.get(focus ?? shown[0] ?? EDITOR_ONLY)?.focus();
2213
2304
  }
2214
2305
 
2215
2306
  // src/comments.ts
@@ -3146,7 +3237,21 @@ function mountPropertyList(host, decls, opts = {}) {
3146
3237
  }, false, true);
3147
3238
  const details = keep(opts.extraDetails?.(p, i));
3148
3239
  if (p.type === "enum" || p.type === "flags") details.push(labelled("Values", tagChips(p, refreshDefault)));
3149
- if (p.type === "quality") details.push(labelled("Stages (in order)", stageChips(p, refreshDefault)));
3240
+ if (p.type === "quality") {
3241
+ const note = el("p", "shell-stage-note");
3242
+ const stages = stageChips(p, refreshDefault, {
3243
+ onRename: (from, to) => {
3244
+ if (p.default === from) p.default = to;
3245
+ const fresh = defaultControl(p, changed, opts);
3246
+ def.replaceWith(fresh);
3247
+ def = fresh;
3248
+ if (opts.onStageRename) opts.onStageRename(p, from, to);
3249
+ else changed();
3250
+ },
3251
+ onReorder: () => opts.onStageReorder?.(p, note)
3252
+ });
3253
+ details.push(labelled("Stages (in order)", stages), note);
3254
+ }
3150
3255
  if (opts.purpose !== false) {
3151
3256
  const purpose = el("input", "field set-purpose");
3152
3257
  purpose.type = "text";
@@ -3222,6 +3327,10 @@ function createNavHistory(opts) {
3222
3327
  },
3223
3328
  canForward() {
3224
3329
  return ahead.length > 0;
3330
+ },
3331
+ clear() {
3332
+ past = [];
3333
+ ahead = [];
3225
3334
  }
3226
3335
  };
3227
3336
  }
@@ -3318,6 +3427,7 @@ exports.keyLegends = keyLegends;
3318
3427
  exports.keyPlatform = keyPlatform;
3319
3428
  exports.kitTile = kitTile;
3320
3429
  exports.labelled = labelled;
3430
+ exports.labelledToggle = labelledToggle;
3321
3431
  exports.linkAddress = linkAddress;
3322
3432
  exports.linkStatusClass = linkStatusClass;
3323
3433
  exports.linkStatusTip = linkStatusTip;