@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.
- package/LICENSES.md +5 -4
- package/dist/{chunk-J6RSONXM.js → chunk-62KWJPRP.js} +4 -4
- package/dist/{chunk-J6RSONXM.js.map → chunk-62KWJPRP.js.map} +1 -1
- package/dist/{chunk-WKPKBAXS.js → chunk-C245WRPF.js} +4 -4
- package/dist/{chunk-WKPKBAXS.js.map → chunk-C245WRPF.js.map} +1 -1
- package/dist/{chunk-R3NXPHRZ.js → chunk-CRUILFAT.js} +28 -6
- package/dist/chunk-CRUILFAT.js.map +1 -0
- package/dist/{chunk-EHR2KBZC.js → chunk-OMG6A5YB.js} +3 -3
- package/dist/{chunk-EHR2KBZC.js.map → chunk-OMG6A5YB.js.map} +1 -1
- package/dist/{chunk-2ECJUTSX.js → chunk-T25WXWPB.js} +3 -2
- package/dist/chunk-T25WXWPB.js.map +1 -0
- package/dist/{chunk-B2TC6Q5J.js → chunk-UQTUCOGV.js} +3 -3
- package/dist/{chunk-B2TC6Q5J.js.map → chunk-UQTUCOGV.js.map} +1 -1
- package/dist/{chunk-YRFJLJUH.js → chunk-VXEFIL43.js} +3 -3
- package/dist/{chunk-YRFJLJUH.js.map → chunk-VXEFIL43.js.map} +1 -1
- package/dist/{chunk-UG2P777F.js → chunk-W4WC4FO5.js} +102 -30
- package/dist/chunk-W4WC4FO5.js.map +1 -0
- package/dist/{chunk-CUFREPJ5.js → chunk-XSS7KM6F.js} +4 -4
- package/dist/{chunk-CUFREPJ5.js.map → chunk-XSS7KM6F.js.map} +1 -1
- package/dist/context-menu.cjs.map +1 -1
- package/dist/context-menu.js +2 -2
- package/dist/dialog.cjs.map +1 -1
- package/dist/dialog.js +2 -2
- package/dist/index.cjs +146 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -14
- package/dist/index.d.ts +97 -14
- package/dist/index.js +31 -17
- package/dist/index.js.map +1 -1
- package/dist/kit-gallery.cjs.map +1 -1
- package/dist/kit-gallery.js +3 -3
- package/dist/link-status.cjs +7 -0
- package/dist/link-status.cjs.map +1 -1
- package/dist/link-status.js +2 -2
- package/dist/menu.cjs +1 -0
- package/dist/menu.cjs.map +1 -1
- package/dist/menu.d.cts +8 -4
- package/dist/menu.d.ts +8 -4
- package/dist/menu.js +1 -1
- package/dist/{property-list-D3VZCJzi.d.cts → property-list-BgZlkcbz.d.cts} +19 -2
- package/dist/{property-list-D3VZCJzi.d.ts → property-list-BgZlkcbz.d.ts} +19 -2
- package/dist/property-list.cjs +93 -27
- package/dist/property-list.cjs.map +1 -1
- package/dist/property-list.d.cts +1 -1
- package/dist/property-list.d.ts +1 -1
- package/dist/property-list.js +2 -2
- package/dist/settings.css +78 -0
- package/dist/settings.css.map +1 -1
- package/dist/updater-view.cjs.map +1 -1
- package/dist/updater-view.js +3 -3
- package/dist/welcome.cjs.map +1 -1
- package/dist/welcome.js +4 -4
- package/package.json +1 -1
- package/dist/chunk-2ECJUTSX.js.map +0 -1
- package/dist/chunk-R3NXPHRZ.js.map +0 -1
- package/dist/chunk-UG2P777F.js.map +0 -1
|
@@ -78,8 +78,14 @@ interface SettingsDialogOptions {
|
|
|
78
78
|
sections: SettingsSection[];
|
|
79
79
|
/** Fired when Save is clicked and every section validates. The app reads its
|
|
80
80
|
* own section state and persists. May be async; the dialog stays open until
|
|
81
|
-
* it resolves, then closes.
|
|
82
|
-
|
|
81
|
+
* it resolves, then closes.
|
|
82
|
+
*
|
|
83
|
+
* Return `false` (or a Promise of it) when the save was REFUSED, a version
|
|
84
|
+
* control lock say, and the dialog stays open with the author's edits in it
|
|
85
|
+
* for another try; saying why is the app's (a toast, usually). A throw, or a
|
|
86
|
+
* rejected Promise, is treated the same way rather than surfacing as an
|
|
87
|
+
* unhandled rejection. Anything else closes the dialog, as it always has. */
|
|
88
|
+
onSave: () => void | boolean | Promise<void | boolean>;
|
|
83
89
|
}
|
|
84
90
|
interface SettingsDialog {
|
|
85
91
|
/** Open (optionally on a given tab id). Sections re-mount from current data. */
|
|
@@ -143,6 +149,17 @@ interface PropertyListOptions<T extends PropertyDeclLike> extends DefaultControl
|
|
|
143
149
|
extraDetails?: (decl: T, index: number) => (HTMLElement | null)[];
|
|
144
150
|
/** Offer the Purpose field. Default true. */
|
|
145
151
|
purpose?: boolean;
|
|
152
|
+
/**
|
|
153
|
+
* A quality's stage was renamed in place (the declaration's own default
|
|
154
|
+
* already follows it). Given, the rename is handed here INSTEAD of
|
|
155
|
+
* `onChange`, so the caller can carry every value that names the stage in
|
|
156
|
+
* the same edit as the declaration; absent, it is an ordinary change.
|
|
157
|
+
*/
|
|
158
|
+
onStageRename?: (decl: T, from: string, to: string) => void;
|
|
159
|
+
/** A quality's stages were reordered (after `onChange`). `note` is a quiet
|
|
160
|
+
* line under the stages for the caller to fill, such as how many gates
|
|
161
|
+
* compare against the ladder. */
|
|
162
|
+
onStageReorder?: (decl: T, note: HTMLElement) => void;
|
|
146
163
|
}
|
|
147
164
|
interface PropertyListHandle extends SettingsSectionHandle {
|
|
148
165
|
/** Both faults, for the Save gate: a duplicate first, then an illegal name. */
|
|
@@ -78,8 +78,14 @@ interface SettingsDialogOptions {
|
|
|
78
78
|
sections: SettingsSection[];
|
|
79
79
|
/** Fired when Save is clicked and every section validates. The app reads its
|
|
80
80
|
* own section state and persists. May be async; the dialog stays open until
|
|
81
|
-
* it resolves, then closes.
|
|
82
|
-
|
|
81
|
+
* it resolves, then closes.
|
|
82
|
+
*
|
|
83
|
+
* Return `false` (or a Promise of it) when the save was REFUSED, a version
|
|
84
|
+
* control lock say, and the dialog stays open with the author's edits in it
|
|
85
|
+
* for another try; saying why is the app's (a toast, usually). A throw, or a
|
|
86
|
+
* rejected Promise, is treated the same way rather than surfacing as an
|
|
87
|
+
* unhandled rejection. Anything else closes the dialog, as it always has. */
|
|
88
|
+
onSave: () => void | boolean | Promise<void | boolean>;
|
|
83
89
|
}
|
|
84
90
|
interface SettingsDialog {
|
|
85
91
|
/** Open (optionally on a given tab id). Sections re-mount from current data. */
|
|
@@ -143,6 +149,17 @@ interface PropertyListOptions<T extends PropertyDeclLike> extends DefaultControl
|
|
|
143
149
|
extraDetails?: (decl: T, index: number) => (HTMLElement | null)[];
|
|
144
150
|
/** Offer the Purpose field. Default true. */
|
|
145
151
|
purpose?: boolean;
|
|
152
|
+
/**
|
|
153
|
+
* A quality's stage was renamed in place (the declaration's own default
|
|
154
|
+
* already follows it). Given, the rename is handed here INSTEAD of
|
|
155
|
+
* `onChange`, so the caller can carry every value that names the stage in
|
|
156
|
+
* the same edit as the declaration; absent, it is an ordinary change.
|
|
157
|
+
*/
|
|
158
|
+
onStageRename?: (decl: T, from: string, to: string) => void;
|
|
159
|
+
/** A quality's stages were reordered (after `onChange`). `note` is a quiet
|
|
160
|
+
* line under the stages for the caller to fill, such as how many gates
|
|
161
|
+
* compare against the ladder. */
|
|
162
|
+
onStageReorder?: (decl: T, note: HTMLElement) => void;
|
|
146
163
|
}
|
|
147
164
|
interface PropertyListHandle extends SettingsSectionHandle {
|
|
148
165
|
/** Both faults, for the Save gate: a duplicate first, then an illegal name. */
|
package/dist/property-list.cjs
CHANGED
|
@@ -260,11 +260,18 @@ var DRAWN = {
|
|
|
260
260
|
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"/>'),
|
|
261
261
|
/** Record: the disc, in whatever colour the button is. (a disc; see DISC_LARGE) */
|
|
262
262
|
record: DISC_LARGE,
|
|
263
|
+
/** Sound: play with audio. (volume-2: the cone and two waves. Patterpad's
|
|
264
|
+
* hand-drawn one had a single wave, which at 14px reads as quiet rather
|
|
265
|
+
* than as sound.) */
|
|
266
|
+
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"/>'),
|
|
263
267
|
// --- handling -------------------------------------------------------------
|
|
264
268
|
/** The drag grip on a reorderable row. (grip-vertical) */
|
|
265
269
|
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"/>'),
|
|
266
270
|
/** Add one of whatever this list holds. (plus) */
|
|
267
271
|
add: stroked('<path d="M5 12h14"/><path d="M12 5v14"/>'),
|
|
272
|
+
/** Copy this to the clipboard. (copy: the front sheet whole, the back
|
|
273
|
+
* sheet showing round two of its sides) */
|
|
274
|
+
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"/>'),
|
|
268
275
|
/** Connect: to a server, a live link, a running game. (plug) */
|
|
269
276
|
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"/>'),
|
|
270
277
|
/** Search. (search) */
|
|
@@ -345,16 +352,17 @@ function iconBtn(name, title, onClick, disabled = false, danger = false) {
|
|
|
345
352
|
b.addEventListener("click", onClick);
|
|
346
353
|
return b;
|
|
347
354
|
}
|
|
348
|
-
function labelled(label, control) {
|
|
355
|
+
function labelled(label, control, hint) {
|
|
349
356
|
const target = control.matches("input, select, textarea") ? control : control.querySelector("input:not([type=button]):not([type=submit]), select, textarea");
|
|
350
357
|
const w = el(target ? "label" : "div", "shell-labelled");
|
|
351
358
|
w.append(el("span", "shell-fieldcap", label), control);
|
|
352
359
|
if (target) {
|
|
353
|
-
if (!target.id) target.id =
|
|
360
|
+
if (!target.id) target.id = uid();
|
|
354
361
|
w.htmlFor = target.id;
|
|
355
362
|
}
|
|
356
363
|
return w;
|
|
357
364
|
}
|
|
365
|
+
var uid = () => `shell-f-${Math.random().toString(36).slice(2, 9)}`;
|
|
358
366
|
function moveItem(arr, i, delta) {
|
|
359
367
|
const j = i + delta;
|
|
360
368
|
if (j < 0 || j >= arr.length) return false;
|
|
@@ -402,7 +410,7 @@ function tagChips(holder, onChange) {
|
|
|
402
410
|
wrap.append(input);
|
|
403
411
|
return wrap;
|
|
404
412
|
}
|
|
405
|
-
function stageChips(holder, onChange) {
|
|
413
|
+
function stageChips(holder, onChange, opts = {}) {
|
|
406
414
|
const wrap = el("div", "shell-tags shell-stages");
|
|
407
415
|
const input = el("input", "shell-tag-input");
|
|
408
416
|
input.type = "text";
|
|
@@ -417,35 +425,79 @@ function stageChips(holder, onChange) {
|
|
|
417
425
|
b.addEventListener("click", onClick);
|
|
418
426
|
return b;
|
|
419
427
|
};
|
|
428
|
+
const nameLabel = (chip, v, i) => {
|
|
429
|
+
const label = el("span", "shell-tag-label", `${i + 1}. ${v}`);
|
|
430
|
+
label.setAttribute("role", "button");
|
|
431
|
+
label.tabIndex = 0;
|
|
432
|
+
label.dataset["tip"] = `Rename ${v}`;
|
|
433
|
+
const edit = () => {
|
|
434
|
+
const field = el("input", "shell-tag-input shell-stage-rename");
|
|
435
|
+
field.type = "text";
|
|
436
|
+
field.value = v;
|
|
437
|
+
field.spellcheck = false;
|
|
438
|
+
field.setAttribute("aria-label", `Rename ${v}`);
|
|
439
|
+
let done = false;
|
|
440
|
+
const finish = (keep) => {
|
|
441
|
+
if (done) return;
|
|
442
|
+
done = true;
|
|
443
|
+
const to = field.value.trim();
|
|
444
|
+
const stages = holder.stages ?? [];
|
|
445
|
+
if (keep && to !== "" && to !== v && !stages.includes(to)) {
|
|
446
|
+
stages[stages.indexOf(v)] = to;
|
|
447
|
+
rebuild();
|
|
448
|
+
if (opts.onRename) opts.onRename(v, to);
|
|
449
|
+
else onChange?.();
|
|
450
|
+
} else {
|
|
451
|
+
rebuild();
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
field.addEventListener("keydown", (e) => {
|
|
455
|
+
if (e.key === "Enter") {
|
|
456
|
+
e.preventDefault();
|
|
457
|
+
finish(true);
|
|
458
|
+
} else if (e.key === "Escape") {
|
|
459
|
+
e.preventDefault();
|
|
460
|
+
e.stopPropagation();
|
|
461
|
+
finish(false);
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
field.addEventListener("blur", () => finish(true));
|
|
465
|
+
label.replaceWith(field);
|
|
466
|
+
chip.classList.add("editing");
|
|
467
|
+
field.focus();
|
|
468
|
+
field.select();
|
|
469
|
+
};
|
|
470
|
+
label.addEventListener("click", (e) => {
|
|
471
|
+
e.preventDefault();
|
|
472
|
+
edit();
|
|
473
|
+
});
|
|
474
|
+
label.addEventListener("keydown", (e) => {
|
|
475
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
476
|
+
e.preventDefault();
|
|
477
|
+
edit();
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
return label;
|
|
481
|
+
};
|
|
482
|
+
const moved = () => {
|
|
483
|
+
rebuild();
|
|
484
|
+
onChange?.();
|
|
485
|
+
opts.onReorder?.();
|
|
486
|
+
};
|
|
420
487
|
const rebuild = () => {
|
|
421
488
|
for (const c of Array.from(wrap.children)) if (c !== input) c.remove();
|
|
422
489
|
const stages = holder.stages ?? [];
|
|
423
490
|
stages.forEach((v, i) => {
|
|
424
|
-
const chip = el("span", "shell-tag"
|
|
491
|
+
const chip = el("span", "shell-tag");
|
|
425
492
|
chip.dataset["stage"] = v;
|
|
426
493
|
chip.append(
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
onChange?.();
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
),
|
|
438
|
-
control(
|
|
439
|
-
"forward",
|
|
440
|
-
`Move ${v} later`,
|
|
441
|
-
i === stages.length - 1,
|
|
442
|
-
() => {
|
|
443
|
-
if (moveItem(stages, i, 1)) {
|
|
444
|
-
rebuild();
|
|
445
|
-
onChange?.();
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
)
|
|
494
|
+
nameLabel(chip, v, i),
|
|
495
|
+
control("back", `Move ${v} earlier`, i === 0, () => {
|
|
496
|
+
if (moveItem(stages, i, -1)) moved();
|
|
497
|
+
}),
|
|
498
|
+
control("forward", `Move ${v} later`, i === stages.length - 1, () => {
|
|
499
|
+
if (moveItem(stages, i, 1)) moved();
|
|
500
|
+
})
|
|
449
501
|
);
|
|
450
502
|
const x = el("button", "shell-tag-x", iconNode("close", CHIP_ICON));
|
|
451
503
|
x.type = "button";
|
|
@@ -736,7 +788,21 @@ function mountPropertyList(host, decls, opts = {}) {
|
|
|
736
788
|
}, false, true);
|
|
737
789
|
const details = keep(opts.extraDetails?.(p, i));
|
|
738
790
|
if (p.type === "enum" || p.type === "flags") details.push(labelled("Values", tagChips(p, refreshDefault)));
|
|
739
|
-
if (p.type === "quality")
|
|
791
|
+
if (p.type === "quality") {
|
|
792
|
+
const note = el("p", "shell-stage-note");
|
|
793
|
+
const stages = stageChips(p, refreshDefault, {
|
|
794
|
+
onRename: (from, to) => {
|
|
795
|
+
if (p.default === from) p.default = to;
|
|
796
|
+
const fresh = defaultControl(p, changed, opts);
|
|
797
|
+
def.replaceWith(fresh);
|
|
798
|
+
def = fresh;
|
|
799
|
+
if (opts.onStageRename) opts.onStageRename(p, from, to);
|
|
800
|
+
else changed();
|
|
801
|
+
},
|
|
802
|
+
onReorder: () => opts.onStageReorder?.(p, note)
|
|
803
|
+
});
|
|
804
|
+
details.push(labelled("Stages (in order)", stages), note);
|
|
805
|
+
}
|
|
740
806
|
if (opts.purpose !== false) {
|
|
741
807
|
const purpose = el("input", "field set-purpose");
|
|
742
808
|
purpose.type = "text";
|