@wildwinter/app-shell 0.38.0 → 0.40.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 +62 -0
- package/README.md +8 -0
- package/dist/about.css +17 -38
- package/dist/about.css.map +1 -1
- package/dist/anchored.css +9 -9
- package/dist/anchored.css.map +1 -1
- package/dist/app-store.cjs +15 -0
- package/dist/app-store.cjs.map +1 -1
- package/dist/app-store.d.cts +25 -1
- package/dist/app-store.d.ts +25 -1
- package/dist/app-store.js +14 -1
- package/dist/app-store.js.map +1 -1
- package/dist/chunk-2ECJUTSX.js +97 -0
- package/dist/chunk-2ECJUTSX.js.map +1 -0
- package/dist/chunk-DS4XXAZS.js +56 -0
- package/dist/chunk-DS4XXAZS.js.map +1 -0
- package/dist/chunk-EHR2KBZC.js +82 -0
- package/dist/chunk-EHR2KBZC.js.map +1 -0
- package/dist/chunk-IU7EY6BI.js +26 -0
- package/dist/chunk-IU7EY6BI.js.map +1 -0
- package/dist/chunk-OPZPFOPS.js +66 -0
- package/dist/chunk-OPZPFOPS.js.map +1 -0
- package/dist/chunk-P4G4ERG6.js +60 -0
- package/dist/chunk-P4G4ERG6.js.map +1 -0
- package/dist/chunk-P6WLAEKZ.js +71 -0
- package/dist/chunk-P6WLAEKZ.js.map +1 -0
- package/dist/chunk-R3NXPHRZ.js +534 -0
- package/dist/chunk-R3NXPHRZ.js.map +1 -0
- package/dist/chunk-SDOKZTAR.js +87 -0
- package/dist/chunk-SDOKZTAR.js.map +1 -0
- package/dist/chunk-SEWJLUOD.js +70 -0
- package/dist/chunk-SEWJLUOD.js.map +1 -0
- package/dist/chunk-UG2P777F.js +587 -0
- package/dist/chunk-UG2P777F.js.map +1 -0
- package/dist/chunk-YRFJLJUH.js +55 -0
- package/dist/chunk-YRFJLJUH.js.map +1 -0
- package/dist/chunk-YWMSQFXX.js +146 -0
- package/dist/chunk-YWMSQFXX.js.map +1 -0
- package/dist/comments.css +16 -14
- package/dist/comments.css.map +1 -1
- package/dist/confirm.css +2 -55
- package/dist/confirm.css.map +1 -1
- package/dist/context-menu.cjs +250 -0
- package/dist/context-menu.cjs.map +1 -0
- package/dist/context-menu.css +64 -0
- package/dist/context-menu.css.map +1 -0
- package/dist/context-menu.d.cts +25 -0
- package/dist/context-menu.d.ts +25 -0
- package/dist/context-menu.js +5 -0
- package/dist/context-menu.js.map +1 -0
- package/dist/controls.css +214 -0
- package/dist/controls.css.map +1 -0
- package/dist/dialog.cjs +222 -0
- package/dist/dialog.cjs.map +1 -0
- package/dist/dialog.css +56 -0
- package/dist/dialog.css.map +1 -0
- package/dist/dialog.d.cts +28 -0
- package/dist/dialog.d.ts +28 -0
- package/dist/dialog.js +5 -0
- package/dist/dialog.js.map +1 -0
- package/dist/floating.cjs +83 -0
- package/dist/floating.cjs.map +1 -0
- package/dist/floating.d.cts +25 -0
- package/dist/floating.d.ts +25 -0
- package/dist/floating.js +4 -0
- package/dist/floating.js.map +1 -0
- package/dist/identity.css +15 -50
- package/dist/identity.css.map +1 -1
- package/dist/index.cjs +1553 -260
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +385 -152
- package/dist/index.d.ts +385 -152
- package/dist/index.js +264 -823
- package/dist/index.js.map +1 -1
- package/dist/job.css +24 -1
- package/dist/job.css.map +1 -1
- package/dist/keys.cjs +155 -0
- package/dist/keys.cjs.map +1 -0
- package/dist/keys.css +41 -0
- package/dist/keys.css.map +1 -0
- package/dist/keys.d.cts +57 -0
- package/dist/keys.d.ts +57 -0
- package/dist/keys.js +3 -0
- package/dist/keys.js.map +1 -0
- package/dist/link-status.cjs +430 -0
- package/dist/link-status.cjs.map +1 -0
- package/dist/link-status.css +76 -0
- package/dist/link-status.css.map +1 -0
- package/dist/link-status.d.cts +53 -0
- package/dist/link-status.d.ts +53 -0
- package/dist/link-status.js +5 -0
- package/dist/link-status.js.map +1 -0
- package/dist/menu.cjs +62 -1
- package/dist/menu.cjs.map +1 -1
- package/dist/menu.d.cts +88 -1
- package/dist/menu.d.ts +88 -1
- package/dist/menu.js +1 -1
- package/dist/notes-editor.css +9 -52
- package/dist/notes-editor.css.map +1 -1
- package/dist/pane-shell.css +23 -5
- package/dist/pane-shell.css.map +1 -1
- package/dist/problems.cjs +61 -0
- package/dist/problems.cjs.map +1 -0
- package/dist/problems.d.cts +57 -0
- package/dist/problems.d.ts +57 -0
- package/dist/problems.js +3 -0
- package/dist/problems.js.map +1 -0
- package/dist/property-list-D3VZCJzi.d.cts +162 -0
- package/dist/property-list-D3VZCJzi.d.ts +162 -0
- package/dist/property-list.cjs +784 -0
- package/dist/property-list.cjs.map +1 -0
- package/dist/property-list.d.cts +1 -0
- package/dist/property-list.d.ts +1 -0
- package/dist/property-list.js +4 -0
- package/dist/property-list.js.map +1 -0
- package/dist/save.css +1 -1
- package/dist/save.css.map +1 -1
- package/dist/settings.css +62 -18
- package/dist/settings.css.map +1 -1
- package/dist/stale.css +4 -1
- package/dist/stale.css.map +1 -1
- package/dist/stepper.css +47 -3
- package/dist/stepper.css.map +1 -1
- package/dist/toast.css +5 -5
- package/dist/toast.css.map +1 -1
- package/dist/tokens.css +20 -0
- package/dist/tokens.css.map +1 -1
- package/dist/tool-window.cjs +63 -0
- package/dist/tool-window.cjs.map +1 -1
- package/dist/tool-window.css +8 -4
- package/dist/tool-window.css.map +1 -1
- package/dist/tool-window.d.cts +67 -1
- package/dist/tool-window.d.ts +67 -1
- package/dist/tool-window.js +63 -1
- package/dist/tool-window.js.map +1 -1
- package/dist/tooltip.css +3 -3
- package/dist/tooltip.css.map +1 -1
- package/dist/updater-view.cjs +283 -0
- package/dist/updater-view.cjs.map +1 -0
- package/dist/updater-view.d.cts +23 -0
- package/dist/updater-view.d.ts +23 -0
- package/dist/updater-view.js +6 -0
- package/dist/updater-view.js.map +1 -0
- package/dist/updater.cjs +12 -16
- package/dist/updater.cjs.map +1 -1
- package/dist/updater.css +44 -0
- package/dist/updater.css.map +1 -0
- package/dist/updater.js +12 -16
- package/dist/updater.js.map +1 -1
- package/dist/util.cjs +78 -0
- package/dist/util.cjs.map +1 -0
- package/dist/util.d.cts +37 -0
- package/dist/util.d.ts +37 -0
- package/dist/util.js +3 -0
- package/dist/util.js.map +1 -0
- package/dist/vc.css +4 -3
- package/dist/vc.css.map +1 -1
- package/dist/welcome.cjs +215 -0
- package/dist/welcome.cjs.map +1 -0
- package/dist/welcome.css +112 -0
- package/dist/welcome.css.map +1 -0
- package/dist/welcome.d.cts +55 -0
- package/dist/welcome.d.ts +55 -0
- package/dist/welcome.js +4 -0
- package/dist/welcome.js.map +1 -0
- package/package.json +72 -2
- package/dist/chunk-QDPFU4WX.js +0 -43
- package/dist/chunk-QDPFU4WX.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,241 +1,22 @@
|
|
|
1
|
+
export { mountWelcome } from './chunk-SEWJLUOD.js';
|
|
2
|
+
export { feedUpdaterDownloadProgress, showUpdaterDialog } from './chunk-OPZPFOPS.js';
|
|
3
|
+
import { isKeyCombo, tipWithKey } from './chunk-YWMSQFXX.js';
|
|
4
|
+
export { hintBar, isKeyCombo, keyHint, keyLabel, keyLegends, keyPlatform, setKeyPlatform, tipWithKey } from './chunk-YWMSQFXX.js';
|
|
5
|
+
export { linkAddress, linkStatusClass, linkStatusTip, mountLinkStatus } from './chunk-SDOKZTAR.js';
|
|
6
|
+
import { isEditableTarget } from './chunk-P6WLAEKZ.js';
|
|
7
|
+
export { copyWithFeedback, debounce, formatCount, isEditableTarget, plural, relativeTime } from './chunk-P6WLAEKZ.js';
|
|
8
|
+
export { EDIT_MENU, FILE_MENU, GO_MENU, PANE_MENU, PLAY_MENU, PUBLISH_MENU, REVIEW_MENU, VIEW_MENU } from './chunk-2ECJUTSX.js';
|
|
9
|
+
export { defaultProblemCopy, describeProblem, problemLine, problemName } from './chunk-DS4XXAZS.js';
|
|
10
|
+
export { PROPERTY_NAME_HINT, PROPERTY_TYPES, RESERVED_PROPERTY_NAMES, bindPropertyName, bindPropertyRef, defaultControl, dupGuard, expandableRow, firstIllegalPropertyName, focusNewRow, isCaseOnlyPropertyName, isValidPropertyName, mountPropertyList, mountSettingsDialog, propertyNameProblem, propertyNameify, propertyRefProblem, revalidatePropertyRefs, revealRow, revealRowWhenReady } from './chunk-R3NXPHRZ.js';
|
|
11
|
+
import { dialogFrame } from './chunk-YRFJLJUH.js';
|
|
12
|
+
export { dialogFrame } from './chunk-YRFJLJUH.js';
|
|
13
|
+
export { createFloating } from './chunk-P4G4ERG6.js';
|
|
1
14
|
export { JOB_PROGRESS } from './chunk-MDGOIT3O.js';
|
|
2
|
-
export {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
function tipBold(s) {
|
|
8
|
-
return `${B_OPEN}${s}${B_CLOSE}`;
|
|
9
|
-
}
|
|
10
|
-
function renderTip(host, text) {
|
|
11
|
-
host.textContent = "";
|
|
12
|
-
const re = /\u0002([^\u0003]*)\u0003/g;
|
|
13
|
-
let last = 0;
|
|
14
|
-
let m;
|
|
15
|
-
while ((m = re.exec(text)) !== null) {
|
|
16
|
-
if (m.index > last) host.append(text.slice(last, m.index));
|
|
17
|
-
const strong = document.createElement("strong");
|
|
18
|
-
strong.textContent = m[1] ?? "";
|
|
19
|
-
host.append(strong);
|
|
20
|
-
last = m.index + m[0].length;
|
|
21
|
-
}
|
|
22
|
-
if (last < text.length) host.append(text.slice(last));
|
|
23
|
-
}
|
|
24
|
-
var inited = false;
|
|
25
|
-
var checked = false;
|
|
26
|
-
var suppressed;
|
|
27
|
-
var SHOW_DELAY = 350;
|
|
28
|
-
var EDGE = 6;
|
|
29
|
-
var GAP = 7;
|
|
30
|
-
var tip = null;
|
|
31
|
-
var timer = 0;
|
|
32
|
-
var active = null;
|
|
33
|
-
var activeKey = null;
|
|
34
|
-
function ensureEl() {
|
|
35
|
-
if (tip) return tip;
|
|
36
|
-
const el2 = document.createElement("div");
|
|
37
|
-
el2.className = "tooltip";
|
|
38
|
-
el2.setAttribute("role", "tooltip");
|
|
39
|
-
el2.hidden = true;
|
|
40
|
-
document.body.appendChild(el2);
|
|
41
|
-
tip = el2;
|
|
42
|
-
return el2;
|
|
43
|
-
}
|
|
44
|
-
var rectOf = (anchor) => anchor instanceof HTMLElement ? anchor.getBoundingClientRect() : anchor;
|
|
45
|
-
function place(anchor) {
|
|
46
|
-
const t = ensureEl();
|
|
47
|
-
const r = rectOf(anchor);
|
|
48
|
-
const tr = t.getBoundingClientRect();
|
|
49
|
-
let top = r.top - tr.height - GAP;
|
|
50
|
-
if (top < EDGE) top = r.top + r.height + GAP;
|
|
51
|
-
let left = r.left + r.width / 2 - tr.width / 2;
|
|
52
|
-
left = Math.max(EDGE, Math.min(left, window.innerWidth - tr.width - EDGE));
|
|
53
|
-
t.style.left = `${Math.round(left)}px`;
|
|
54
|
-
t.style.top = `${Math.round(top)}px`;
|
|
55
|
-
}
|
|
56
|
-
function show(anchor) {
|
|
57
|
-
const text = anchor.dataset["tip"];
|
|
58
|
-
if (!text) return;
|
|
59
|
-
paint(anchor, text);
|
|
60
|
-
}
|
|
61
|
-
function paint(anchor, text) {
|
|
62
|
-
if (suppressed?.()) return;
|
|
63
|
-
const t = ensureEl();
|
|
64
|
-
const host = (anchor instanceof HTMLElement ? anchor.closest("dialog[open]") : null) ?? document.body;
|
|
65
|
-
if (t.parentElement !== host) host.appendChild(t);
|
|
66
|
-
active = anchor instanceof HTMLElement ? anchor : null;
|
|
67
|
-
renderTip(t, text);
|
|
68
|
-
t.hidden = false;
|
|
69
|
-
t.classList.remove("show");
|
|
70
|
-
void t.offsetWidth;
|
|
71
|
-
place(anchor);
|
|
72
|
-
t.classList.add("show");
|
|
73
|
-
}
|
|
74
|
-
function hide() {
|
|
75
|
-
if (timer) {
|
|
76
|
-
window.clearTimeout(timer);
|
|
77
|
-
timer = 0;
|
|
78
|
-
}
|
|
79
|
-
active = null;
|
|
80
|
-
activeKey = null;
|
|
81
|
-
if (tip) {
|
|
82
|
-
tip.hidden = true;
|
|
83
|
-
tip.classList.remove("show");
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
function schedule(anchor) {
|
|
87
|
-
if (anchor === active) return;
|
|
88
|
-
if (timer) window.clearTimeout(timer);
|
|
89
|
-
timer = window.setTimeout(() => show(anchor), SHOW_DELAY);
|
|
90
|
-
}
|
|
91
|
-
function tipAt(key, rect, text) {
|
|
92
|
-
if (key === activeKey) {
|
|
93
|
-
if (tip && !tip.hidden) {
|
|
94
|
-
renderTip(tip, text);
|
|
95
|
-
place(rect);
|
|
96
|
-
}
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
hide();
|
|
100
|
-
activeKey = key;
|
|
101
|
-
timer = window.setTimeout(() => {
|
|
102
|
-
timer = 0;
|
|
103
|
-
paint(rect, text);
|
|
104
|
-
}, SHOW_DELAY);
|
|
105
|
-
}
|
|
106
|
-
function hideTip() {
|
|
107
|
-
hide();
|
|
108
|
-
}
|
|
109
|
-
function ensureTooltipHost() {
|
|
110
|
-
initTooltips();
|
|
111
|
-
}
|
|
112
|
-
function checkTooltipHost() {
|
|
113
|
-
if (checked) return;
|
|
114
|
-
checked = true;
|
|
115
|
-
setTimeout(() => {
|
|
116
|
-
if (inited || typeof document === "undefined") return;
|
|
117
|
-
if (document.querySelector("[data-tip]")) {
|
|
118
|
-
console.warn("app-shell: something set `data-tip` but no tooltip host is mounted, so those rollovers will not draw. Call initTooltips() once per window.");
|
|
119
|
-
}
|
|
120
|
-
}, 0);
|
|
121
|
-
}
|
|
122
|
-
var tipAncestor = (n) => n instanceof Element ? n.closest("[data-tip]") : null;
|
|
123
|
-
function initTooltips(opts = {}) {
|
|
124
|
-
if (opts.suppressed !== void 0) suppressed = opts.suppressed;
|
|
125
|
-
if (inited) return;
|
|
126
|
-
inited = true;
|
|
127
|
-
document.addEventListener("pointerover", (e) => {
|
|
128
|
-
const el2 = tipAncestor(e.target);
|
|
129
|
-
if (el2) schedule(el2);
|
|
130
|
-
});
|
|
131
|
-
document.addEventListener("pointerout", (e) => {
|
|
132
|
-
const el2 = tipAncestor(e.target);
|
|
133
|
-
if (!el2) return;
|
|
134
|
-
const to = e.relatedTarget;
|
|
135
|
-
if (to instanceof Node && el2.contains(to)) return;
|
|
136
|
-
hide();
|
|
137
|
-
});
|
|
138
|
-
document.addEventListener("focusin", (e) => {
|
|
139
|
-
const el2 = tipAncestor(e.target);
|
|
140
|
-
if (el2) schedule(el2);
|
|
141
|
-
});
|
|
142
|
-
document.addEventListener("focusout", () => hide());
|
|
143
|
-
document.addEventListener("pointerdown", () => hide(), true);
|
|
144
|
-
document.addEventListener("scroll", () => {
|
|
145
|
-
if (active) hide();
|
|
146
|
-
}, true);
|
|
147
|
-
document.addEventListener("keydown", (e) => {
|
|
148
|
-
if (e.key === "Escape") hide();
|
|
149
|
-
});
|
|
150
|
-
window.addEventListener("blur", () => hide());
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// src/dom.ts
|
|
154
|
-
function el(tag, classNameOrProps, ...children) {
|
|
155
|
-
const node2 = document.createElement(tag);
|
|
156
|
-
const props = typeof classNameOrProps === "string" ? { className: classNameOrProps } : classNameOrProps ?? {};
|
|
157
|
-
if (props.className) node2.className = props.className;
|
|
158
|
-
if (props.text !== void 0 && props.text !== null) node2.textContent = props.text;
|
|
159
|
-
if (props.title !== void 0) node2.title = props.title;
|
|
160
|
-
if (props.tip !== void 0) {
|
|
161
|
-
node2.dataset["tip"] = props.tip;
|
|
162
|
-
node2.setAttribute("aria-label", props.tip);
|
|
163
|
-
ensureTooltipHost();
|
|
164
|
-
}
|
|
165
|
-
if (props.onClick) node2.addEventListener("click", props.onClick);
|
|
166
|
-
for (const child of children) {
|
|
167
|
-
if (child === null || child === void 0) continue;
|
|
168
|
-
node2.append(child);
|
|
169
|
-
}
|
|
170
|
-
checkTooltipHost();
|
|
171
|
-
return node2;
|
|
172
|
-
}
|
|
173
|
-
function iconBtn(glyph, title, onClick, disabled = false, danger = false) {
|
|
174
|
-
const b = el("button", `shell-icon${danger ? " danger" : ""}`);
|
|
175
|
-
b.type = "button";
|
|
176
|
-
b.textContent = glyph;
|
|
177
|
-
b.dataset["tip"] = title;
|
|
178
|
-
b.setAttribute("aria-label", title);
|
|
179
|
-
b.disabled = disabled;
|
|
180
|
-
b.addEventListener("click", onClick);
|
|
181
|
-
return b;
|
|
182
|
-
}
|
|
183
|
-
function labelled(label2, control) {
|
|
184
|
-
const target = control.matches("input, select, textarea") ? control : control.querySelector("input:not([type=button]):not([type=submit]), select, textarea");
|
|
185
|
-
const w = el(target ? "label" : "div", "shell-labelled");
|
|
186
|
-
w.append(el("span", "shell-fieldcap", label2), control);
|
|
187
|
-
if (target) {
|
|
188
|
-
if (!target.id) target.id = `shell-f-${Math.random().toString(36).slice(2, 9)}`;
|
|
189
|
-
w.htmlFor = target.id;
|
|
190
|
-
}
|
|
191
|
-
return w;
|
|
192
|
-
}
|
|
193
|
-
function moveItem(arr, i, delta) {
|
|
194
|
-
const j = i + delta;
|
|
195
|
-
if (j < 0 || j >= arr.length) return false;
|
|
196
|
-
[arr[i], arr[j]] = [arr[j], arr[i]];
|
|
197
|
-
return true;
|
|
198
|
-
}
|
|
199
|
-
function tagChips(holder, onChange) {
|
|
200
|
-
const wrap = el("div", "shell-tags");
|
|
201
|
-
const input = el("input", "shell-tag-input");
|
|
202
|
-
input.type = "text";
|
|
203
|
-
input.placeholder = "Add value";
|
|
204
|
-
input.spellcheck = false;
|
|
205
|
-
const makeChip = (v) => {
|
|
206
|
-
const chip = el("span", "shell-tag", v);
|
|
207
|
-
const x = el("button", "shell-tag-x", "\u2715");
|
|
208
|
-
x.type = "button";
|
|
209
|
-
x.dataset["tip"] = `remove ${v}`;
|
|
210
|
-
x.setAttribute("aria-label", `remove ${v}`);
|
|
211
|
-
x.addEventListener("click", () => {
|
|
212
|
-
holder.values = (holder.values ?? []).filter((o) => o !== v);
|
|
213
|
-
chip.remove();
|
|
214
|
-
onChange?.();
|
|
215
|
-
});
|
|
216
|
-
chip.append(x);
|
|
217
|
-
return chip;
|
|
218
|
-
};
|
|
219
|
-
const commit = () => {
|
|
220
|
-
const v = input.value.trim();
|
|
221
|
-
if (v && !(holder.values ?? []).includes(v)) {
|
|
222
|
-
(holder.values ??= []).push(v);
|
|
223
|
-
wrap.insertBefore(makeChip(v), input);
|
|
224
|
-
onChange?.();
|
|
225
|
-
}
|
|
226
|
-
input.value = "";
|
|
227
|
-
};
|
|
228
|
-
input.addEventListener("keydown", (e) => {
|
|
229
|
-
if (e.key === "Enter" || e.key === ",") {
|
|
230
|
-
e.preventDefault();
|
|
231
|
-
commit();
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
input.addEventListener("blur", commit);
|
|
235
|
-
for (const v of holder.values ?? []) wrap.append(makeChip(v));
|
|
236
|
-
wrap.append(input);
|
|
237
|
-
return wrap;
|
|
238
|
-
}
|
|
15
|
+
export { openContextMenu, openPopover } from './chunk-EHR2KBZC.js';
|
|
16
|
+
import { ensureTooltipHost, el, iconNode, labelled, icon } from './chunk-UG2P777F.js';
|
|
17
|
+
export { ICON_NAMES, ICON_SIZE, breadcrumb, el, hideTip, icon, iconBtn, iconHtml, iconNameOfGlyph, iconNode, iconSvg, initTooltips, isIconName, labelled, metaLine, moveItem, stageChips, tagChips, tipAt, tipBold, wireReorder } from './chunk-UG2P777F.js';
|
|
18
|
+
import { closeWithExit } from './chunk-IU7EY6BI.js';
|
|
19
|
+
export { closeWithExit } from './chunk-IU7EY6BI.js';
|
|
239
20
|
|
|
240
21
|
// src/colour.ts
|
|
241
22
|
var PALETTE_SIZE = 12;
|
|
@@ -257,79 +38,6 @@ function colourFor(name) {
|
|
|
257
38
|
return PALETTE[colourIndex(name)];
|
|
258
39
|
}
|
|
259
40
|
|
|
260
|
-
// src/icons.ts
|
|
261
|
-
var icon = {
|
|
262
|
-
// --- dismissing and revealing ---------------------------------------------
|
|
263
|
-
/** Close a panel, a dialog, a popover. Patterpad's, twelve times over. */
|
|
264
|
-
close: "\u2715",
|
|
265
|
-
/** The overflow menu on a document or a row: "more about this". */
|
|
266
|
-
more: "\u22EF",
|
|
267
|
-
/** Collapsed disclosure, pointing at what would open. */
|
|
268
|
-
collapsed: "\u25B8",
|
|
269
|
-
/** Expanded disclosure. */
|
|
270
|
-
expanded: "\u25BE",
|
|
271
|
-
/** A dropdown's affordance, where it is not a real <select>. */
|
|
272
|
-
dropdown: "\u25BE",
|
|
273
|
-
// --- moving through things ------------------------------------------------
|
|
274
|
-
/** Back / previous, in a breadcrumb or a stepper. */
|
|
275
|
-
back: "\u2039",
|
|
276
|
-
/** Forward / next. */
|
|
277
|
-
forward: "\u203A",
|
|
278
|
-
/** Up one, in a list. */
|
|
279
|
-
up: "\u2191",
|
|
280
|
-
/** Down one. */
|
|
281
|
-
down: "\u2193",
|
|
282
|
-
// --- state ----------------------------------------------------------------
|
|
283
|
-
/** Done, applied, agreed. */
|
|
284
|
-
tick: "\u2713",
|
|
285
|
-
/** Somebody else holds this: the version-control lock. */
|
|
286
|
-
locked: "\u2298",
|
|
287
|
-
/** Read-only on disk with no other holder: the resting state of everything
|
|
288
|
-
* in a lock-based working copy that nobody has checked out yet. */
|
|
289
|
-
readOnly: "\u25CB",
|
|
290
|
-
/** YOU hold this one: checked out / opened by the current user, still yours to
|
|
291
|
-
* edit. Shares its glyph with `note`, which the table permits: an entry is a
|
|
292
|
-
* MEANING, and two meanings may draw the same shape. */
|
|
293
|
-
checkedOut: "\u270E",
|
|
294
|
-
/** Tracked, with local changes not yet committed. */
|
|
295
|
-
modified: "\u25CF",
|
|
296
|
-
/** Not in version control yet: new since the last commit. */
|
|
297
|
-
untracked: "+",
|
|
298
|
-
/** Something needs attention but is not fatal. */
|
|
299
|
-
warning: "\u203C",
|
|
300
|
-
/** A live thing: the running position, an active marker. */
|
|
301
|
-
dot: "\u25CF",
|
|
302
|
-
/** Play, in the sense of "run this". */
|
|
303
|
-
play: "\u25B6",
|
|
304
|
-
/** Start the thing again from the top: a play session, a sweep. Named for the
|
|
305
|
-
* act, not the arrow, which is the table's rule. */
|
|
306
|
-
restart: "\u27F2",
|
|
307
|
-
/** The three ways a collection can be shown. Named by what you get rather than
|
|
308
|
-
* by the glyph, so a second app can pick the same three without copying
|
|
309
|
-
* somebody's taste in symbols. */
|
|
310
|
-
viewCards: "\u25A6",
|
|
311
|
-
viewTable: "\u2630",
|
|
312
|
-
viewNode: "\u25C8",
|
|
313
|
-
// --- handling -------------------------------------------------------------
|
|
314
|
-
/** The drag grip on a reorderable row. */
|
|
315
|
-
grip: "\u283F",
|
|
316
|
-
/** Add one of whatever this list holds. */
|
|
317
|
-
add: "+",
|
|
318
|
-
// --- annotation -----------------------------------------------------------
|
|
319
|
-
/** Documentation notes: the pencil. Both apps had chosen it independently,
|
|
320
|
-
* which is the happiest way for a vocabulary entry to arrive. */
|
|
321
|
-
note: "\u270E",
|
|
322
|
-
/** A comment thread. Speech, not a pencil: documentation states the reason,
|
|
323
|
-
* comments are the conversation, and the two must never look alike. */
|
|
324
|
-
comment: "\u275D"
|
|
325
|
-
};
|
|
326
|
-
var iconSvg = {
|
|
327
|
-
/** A document with lines: notes exist on this thing. */
|
|
328
|
-
noteFilled: '<svg viewBox="0 0 16 16" width="13" height="13" fill="currentColor" fill-rule="evenodd" aria-hidden="true"><path d="M3.4 1.4h5.3L12.6 5.3v8.1a1.2 1.2 0 0 1-1.2 1.2H3.4a1.2 1.2 0 0 1-1.2-1.2V2.6A1.2 1.2 0 0 1 3.4 1.4ZM4.9 6.8h6.2v1.1H4.9Zm0 2.1h6.2v1.1H4.9Zm0 2.1h3.7v1.1H4.9Z"/></svg>',
|
|
329
|
-
/** The same document, empty: nothing written here yet. */
|
|
330
|
-
noteOutline: '<svg viewBox="0 0 16 16" width="13" height="13" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linejoin="round" stroke-linecap="round" aria-hidden="true"><path d="M3.4 2h4.9l3.9 3.9v7.5a1 1 0 0 1-1 1H3.4a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1Z"/><path d="M8.3 2.2v3.8h3.8"/><path d="M4.9 8h6.2M4.9 10h6.2M4.9 12h3.7" stroke-width="0.95"/></svg>'
|
|
331
|
-
};
|
|
332
|
-
|
|
333
41
|
// src/pane-shell.ts
|
|
334
42
|
var DEFAULT_MAX = (w) => Math.min(640, w - 420);
|
|
335
43
|
var openVar = { nav: "--nav-open-w", inspector: "--insp-open-w" };
|
|
@@ -342,6 +50,8 @@ function mountPaneShell(host, opts) {
|
|
|
342
50
|
inspector: offered("inspector") && (opts.initial?.open?.inspector ?? true)
|
|
343
51
|
};
|
|
344
52
|
const width = { ...opts.initial?.width ?? {} };
|
|
53
|
+
const held = { nav: false, inspector: false };
|
|
54
|
+
const showing = (side) => open[side] && !held[side];
|
|
345
55
|
const el2 = (tag, cls) => {
|
|
346
56
|
const n = document.createElement(tag);
|
|
347
57
|
if (cls) n.className = cls;
|
|
@@ -372,18 +82,29 @@ function mountPaneShell(host, opts) {
|
|
|
372
82
|
host.append(root);
|
|
373
83
|
if (!offered("nav")) navToggle.remove();
|
|
374
84
|
if (!offered("inspector")) inspToggle.remove();
|
|
85
|
+
const defaultTip = (side, isOpen) => {
|
|
86
|
+
const text = `${isOpen ? "Hide" : "Show"} ${cfg[side].label ?? side}`;
|
|
87
|
+
const hint = cfg[side].shortcutHint;
|
|
88
|
+
if (!hint) return text;
|
|
89
|
+
return isKeyCombo(hint) ? tipWithKey(text, hint) : `${text} (${hint})`;
|
|
90
|
+
};
|
|
91
|
+
const tipFor = opts.tipFor ?? defaultTip;
|
|
375
92
|
function applyToggleGlyph(side, button) {
|
|
376
93
|
const isOpen = open[side];
|
|
377
|
-
button.
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
button.
|
|
381
|
-
button.setAttribute("aria-label", button.title);
|
|
94
|
+
button.replaceChildren(iconNode(side === "nav" ? isOpen ? "back" : "forward" : isOpen ? "forward" : "back"));
|
|
95
|
+
const tip = tipFor(side, isOpen);
|
|
96
|
+
button.dataset["tip"] = tip;
|
|
97
|
+
button.setAttribute("aria-label", tip);
|
|
382
98
|
button.setAttribute("aria-pressed", String(isOpen));
|
|
383
99
|
}
|
|
100
|
+
ensureTooltipHost();
|
|
384
101
|
function apply() {
|
|
385
|
-
|
|
386
|
-
|
|
102
|
+
for (const side of ["nav", "inspector"]) {
|
|
103
|
+
const collapsed = !showing(side);
|
|
104
|
+
panes.classList.toggle(collapseClass[side], collapsed);
|
|
105
|
+
const also = opts.collapseAlso?.[side];
|
|
106
|
+
if (also) panes.classList.toggle(also, collapsed);
|
|
107
|
+
}
|
|
387
108
|
panes.style.setProperty(openVar.nav, width.nav !== void 0 ? `${width.nav}px` : cfg.nav.defaultWidth);
|
|
388
109
|
panes.style.setProperty(openVar.inspector, width.inspector !== void 0 ? `${width.inspector}px` : cfg.inspector.defaultWidth);
|
|
389
110
|
applyToggleGlyph("nav", navToggle);
|
|
@@ -402,6 +123,11 @@ function mountPaneShell(host, opts) {
|
|
|
402
123
|
function togglePane(side) {
|
|
403
124
|
if (offered(side)) setPaneOpen(side, !open[side]);
|
|
404
125
|
}
|
|
126
|
+
function holdClosed(side, next) {
|
|
127
|
+
if (held[side] === next) return;
|
|
128
|
+
held[side] = next;
|
|
129
|
+
apply();
|
|
130
|
+
}
|
|
405
131
|
function resetWidths() {
|
|
406
132
|
delete width.nav;
|
|
407
133
|
delete width.inspector;
|
|
@@ -411,9 +137,11 @@ function mountPaneShell(host, opts) {
|
|
|
411
137
|
navToggle.addEventListener("click", () => togglePane("nav"));
|
|
412
138
|
inspToggle.addEventListener("click", () => togglePane("inspector"));
|
|
413
139
|
function beginResize(side, startEvent) {
|
|
414
|
-
if (cfg[side].resizable === false || !
|
|
140
|
+
if (cfg[side].resizable === false || !showing(side)) return;
|
|
415
141
|
startEvent.preventDefault();
|
|
416
142
|
const pane = side === "nav" ? navPane : inspPane;
|
|
143
|
+
const seam = side === "nav" ? navResizer : inspResizer;
|
|
144
|
+
seam.classList.add("dragging");
|
|
417
145
|
const min = cfg[side].minWidth ?? 160;
|
|
418
146
|
const max = cfg[side].maxWidth ?? DEFAULT_MAX;
|
|
419
147
|
const startX = startEvent.clientX;
|
|
@@ -429,6 +157,7 @@ function mountPaneShell(host, opts) {
|
|
|
429
157
|
window.removeEventListener("pointermove", onMove);
|
|
430
158
|
window.removeEventListener("pointerup", onUp);
|
|
431
159
|
document.body.classList.remove("pane-resizing");
|
|
160
|
+
seam.classList.remove("dragging");
|
|
432
161
|
width[side] = latest;
|
|
433
162
|
persist();
|
|
434
163
|
};
|
|
@@ -458,7 +187,8 @@ function mountPaneShell(host, opts) {
|
|
|
458
187
|
inspector: inspInner,
|
|
459
188
|
togglePane,
|
|
460
189
|
setPaneOpen,
|
|
461
|
-
isOpen:
|
|
190
|
+
isOpen: showing,
|
|
191
|
+
holdClosed,
|
|
462
192
|
resetWidths,
|
|
463
193
|
state: snapshot,
|
|
464
194
|
destroy: () => {
|
|
@@ -467,189 +197,16 @@ function mountPaneShell(host, opts) {
|
|
|
467
197
|
};
|
|
468
198
|
}
|
|
469
199
|
|
|
470
|
-
// src/settings.ts
|
|
471
|
-
function expandableRow(opts) {
|
|
472
|
-
const row = el("div", "set-row");
|
|
473
|
-
if (opts.name !== void 0) row.dataset.name = opts.name;
|
|
474
|
-
const line = el("div", "set-rowline");
|
|
475
|
-
if (opts.details && opts.details.length) {
|
|
476
|
-
const details = el("div", "set-details");
|
|
477
|
-
details.hidden = true;
|
|
478
|
-
for (const d of opts.details) details.append(d);
|
|
479
|
-
const toggle = el("button", "set-expand");
|
|
480
|
-
toggle.type = "button";
|
|
481
|
-
toggle.dataset.tip = "More";
|
|
482
|
-
toggle.setAttribute("aria-label", "More");
|
|
483
|
-
toggle.setAttribute("aria-expanded", "false");
|
|
484
|
-
toggle.addEventListener("click", () => {
|
|
485
|
-
const open = details.hidden;
|
|
486
|
-
details.hidden = !open;
|
|
487
|
-
toggle.setAttribute("aria-expanded", String(open));
|
|
488
|
-
toggle.classList.toggle("open", open);
|
|
489
|
-
});
|
|
490
|
-
line.append(toggle);
|
|
491
|
-
opts.line.forEach((c) => line.append(c));
|
|
492
|
-
row.append(line, details);
|
|
493
|
-
} else {
|
|
494
|
-
opts.line.forEach((c) => line.append(c));
|
|
495
|
-
row.append(line);
|
|
496
|
-
}
|
|
497
|
-
return row;
|
|
498
|
-
}
|
|
499
|
-
function revealRow(within, name) {
|
|
500
|
-
const row = [...within.querySelectorAll(".set-row")].find((r) => r.dataset.name === name);
|
|
501
|
-
if (!row) return false;
|
|
502
|
-
const toggle = row.querySelector(".set-rowline > .set-expand");
|
|
503
|
-
if (toggle && toggle.getAttribute("aria-expanded") !== "true") toggle.click();
|
|
504
|
-
const still = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
505
|
-
row.scrollIntoView({ block: "center", behavior: still ? "auto" : "smooth" });
|
|
506
|
-
row.classList.remove("landed");
|
|
507
|
-
void row.offsetWidth;
|
|
508
|
-
row.classList.add("landed");
|
|
509
|
-
row.addEventListener("animationend", () => row.classList.remove("landed"), { once: true });
|
|
510
|
-
return true;
|
|
511
|
-
}
|
|
512
|
-
function focusNewRow(listEl) {
|
|
513
|
-
const last = listEl?.lastElementChild;
|
|
514
|
-
if (!(last instanceof HTMLElement)) return;
|
|
515
|
-
last.scrollIntoView({ block: "nearest" });
|
|
516
|
-
const name = last.querySelector("input.set-name") ?? last.querySelector("input");
|
|
517
|
-
name?.focus();
|
|
518
|
-
}
|
|
519
|
-
function dupGuard() {
|
|
520
|
-
const items = [];
|
|
521
|
-
const check = () => {
|
|
522
|
-
const counts = /* @__PURE__ */ new Map();
|
|
523
|
-
for (const it of items) {
|
|
524
|
-
const k = it.key().trim().toLowerCase();
|
|
525
|
-
if (k) counts.set(k, (counts.get(k) ?? 0) + 1);
|
|
526
|
-
}
|
|
527
|
-
let any = false;
|
|
528
|
-
for (const it of items) {
|
|
529
|
-
const k = it.key().trim().toLowerCase();
|
|
530
|
-
const dup = !!k && (counts.get(k) ?? 0) > 1;
|
|
531
|
-
it.input.classList.toggle("invalid", dup);
|
|
532
|
-
if (dup) {
|
|
533
|
-
it.input.title = "Duplicate name. Names must be unique.";
|
|
534
|
-
any = true;
|
|
535
|
-
} else if (it.input.title === "Duplicate name. Names must be unique.") it.input.removeAttribute("title");
|
|
536
|
-
}
|
|
537
|
-
return any;
|
|
538
|
-
};
|
|
539
|
-
return {
|
|
540
|
-
reset() {
|
|
541
|
-
items.length = 0;
|
|
542
|
-
},
|
|
543
|
-
track(input, key = () => input.value, also) {
|
|
544
|
-
items.push({ input, key });
|
|
545
|
-
input.addEventListener("input", check);
|
|
546
|
-
for (const a of also ?? []) a.addEventListener("input", check);
|
|
547
|
-
},
|
|
548
|
-
check,
|
|
549
|
-
firstDuplicate() {
|
|
550
|
-
check();
|
|
551
|
-
return items.find((it) => it.input.classList.contains("invalid"))?.input ?? null;
|
|
552
|
-
}
|
|
553
|
-
};
|
|
554
|
-
}
|
|
555
|
-
function mountSettingsDialog(opts) {
|
|
556
|
-
const dialog = el("dialog", "settings-dialog");
|
|
557
|
-
const form = el("form", "settings-form");
|
|
558
|
-
form.method = "dialog";
|
|
559
|
-
const tabs = el("nav", "settings-tabs");
|
|
560
|
-
const panels = el("div", "settings-panels");
|
|
561
|
-
const error = el("p", "settings-error");
|
|
562
|
-
error.hidden = true;
|
|
563
|
-
const cancel = el("button", "settings-cancel", "Cancel");
|
|
564
|
-
cancel.type = "button";
|
|
565
|
-
cancel.value = "cancel";
|
|
566
|
-
const save = el("button", "settings-save primary", "Save");
|
|
567
|
-
save.value = "save";
|
|
568
|
-
const panelFor = /* @__PURE__ */ new Map();
|
|
569
|
-
const tabFor = /* @__PURE__ */ new Map();
|
|
570
|
-
const handles = /* @__PURE__ */ new Map();
|
|
571
|
-
let active2 = opts.sections[0]?.id ?? "";
|
|
572
|
-
let lastGroup;
|
|
573
|
-
for (const s of opts.sections) {
|
|
574
|
-
if (s.group && s.group !== lastGroup) {
|
|
575
|
-
tabs.append(el("div", "settings-group", s.group));
|
|
576
|
-
lastGroup = s.group;
|
|
577
|
-
}
|
|
578
|
-
const tab = el("button", "settings-tab", s.label);
|
|
579
|
-
tab.type = "button";
|
|
580
|
-
tab.dataset["tab"] = s.id;
|
|
581
|
-
tab.addEventListener("click", () => showTab(s.id));
|
|
582
|
-
tabs.append(tab);
|
|
583
|
-
tabFor.set(s.id, tab);
|
|
584
|
-
const panel = el("section", "settings-panel");
|
|
585
|
-
panel.dataset["panel"] = s.id;
|
|
586
|
-
panel.hidden = true;
|
|
587
|
-
panels.append(panel);
|
|
588
|
-
panelFor.set(s.id, panel);
|
|
589
|
-
}
|
|
590
|
-
function showTab(id) {
|
|
591
|
-
active2 = id;
|
|
592
|
-
for (const [sid, t] of tabFor) t.classList.toggle("active", sid === id);
|
|
593
|
-
for (const [sid, p] of panelFor) p.hidden = sid !== id;
|
|
594
|
-
}
|
|
595
|
-
form.append(
|
|
596
|
-
el("h2", "settings-title", opts.title),
|
|
597
|
-
el("div", "settings-body", tabs, panels),
|
|
598
|
-
el("div", "settings-actions", error, cancel, save)
|
|
599
|
-
);
|
|
600
|
-
dialog.append(form);
|
|
601
|
-
document.body.append(dialog);
|
|
602
|
-
form.addEventListener("keydown", (e) => {
|
|
603
|
-
if (e.key === "Enter" && e.target?.tagName === "INPUT") e.preventDefault();
|
|
604
|
-
});
|
|
605
|
-
cancel.addEventListener("click", () => dialog.close("cancel"));
|
|
606
|
-
save.addEventListener("click", (e) => {
|
|
607
|
-
e.preventDefault();
|
|
608
|
-
error.hidden = true;
|
|
609
|
-
for (const s of opts.sections) {
|
|
610
|
-
const bad = handles.get(s.id)?.firstInvalid?.();
|
|
611
|
-
if (bad) {
|
|
612
|
-
showTab(s.id);
|
|
613
|
-
bad.focus();
|
|
614
|
-
error.textContent = "Fix the highlighted fields first.";
|
|
615
|
-
error.hidden = false;
|
|
616
|
-
return;
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
void (async () => {
|
|
620
|
-
await opts.onSave();
|
|
621
|
-
dialog.close("save");
|
|
622
|
-
})();
|
|
623
|
-
});
|
|
624
|
-
return {
|
|
625
|
-
open(tabId) {
|
|
626
|
-
handles.clear();
|
|
627
|
-
for (const s of opts.sections) {
|
|
628
|
-
const panel = panelFor.get(s.id);
|
|
629
|
-
panel.replaceChildren();
|
|
630
|
-
handles.set(s.id, s.mount(panel));
|
|
631
|
-
}
|
|
632
|
-
error.hidden = true;
|
|
633
|
-
showTab(tabId ?? active2 ?? opts.sections[0]?.id ?? "");
|
|
634
|
-
dialog.showModal();
|
|
635
|
-
},
|
|
636
|
-
destroy() {
|
|
637
|
-
dialog.remove();
|
|
638
|
-
}
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
|
|
642
200
|
// src/tool-window-web.ts
|
|
643
|
-
var PIN_ICON = `<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="12" y1="17" x2="12" y2="22"/><path d="M9 10.8a2 2 0 0 1-1.1 1.8l-1.8.9A2 2 0 0 0 5 15.3V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.7a2 2 0 0 0-1.1-1.8l-1.8-.9A2 2 0 0 1 15 10.8V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z"/></svg>`;
|
|
644
201
|
function pinButton(opts) {
|
|
645
202
|
const b = el("button", "swin-pin");
|
|
646
203
|
b.type = "button";
|
|
647
|
-
b.
|
|
204
|
+
b.append(iconNode("pin"));
|
|
648
205
|
let pinned = opts.pinned;
|
|
649
206
|
const reflect = () => {
|
|
650
207
|
b.classList.toggle("on", pinned);
|
|
651
208
|
b.setAttribute("aria-pressed", String(pinned));
|
|
652
|
-
const label2 = pinned ? "Pinned on top
|
|
209
|
+
const label2 = pinned ? "Pinned on top. Click to unpin." : "Keep on top";
|
|
653
210
|
b.dataset["tip"] = label2;
|
|
654
211
|
b.setAttribute("aria-label", label2);
|
|
655
212
|
};
|
|
@@ -664,66 +221,91 @@ function pinButton(opts) {
|
|
|
664
221
|
reflect();
|
|
665
222
|
} };
|
|
666
223
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
function
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
return;
|
|
224
|
+
var escSlot;
|
|
225
|
+
var escWired = false;
|
|
226
|
+
function wireEsc() {
|
|
227
|
+
if (escWired) return;
|
|
228
|
+
escWired = true;
|
|
229
|
+
window.addEventListener("keydown", (e) => {
|
|
230
|
+
if (e.key !== "Escape" || !escSlot) return;
|
|
231
|
+
if (e.defaultPrevented) return;
|
|
232
|
+
if (!escSlot.fromFields && isEditableTarget(e.target)) return;
|
|
233
|
+
if (escSlot.onEscape?.() === true) return;
|
|
234
|
+
escSlot.onClose();
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
function toolWindowHead(opts) {
|
|
238
|
+
const keep = (parts) => (parts ?? []).filter((p) => p !== null);
|
|
239
|
+
const pin = opts.pin ?? pinButton({ pinned: opts.pinned ?? true, onToggle: opts.onPin ?? (() => {
|
|
240
|
+
}) });
|
|
241
|
+
if (opts.esc !== false) {
|
|
242
|
+
escSlot = { onClose: opts.onClose, onEscape: opts.onEscape, fromFields: opts.esc === "always" };
|
|
243
|
+
wireEsc();
|
|
675
244
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
245
|
+
return el(
|
|
246
|
+
"header",
|
|
247
|
+
{ className: `swin-head${opts.className ? ` ${opts.className}` : ""}` },
|
|
248
|
+
...opts.title !== void 0 ? [el("span", { className: "swin-title", text: opts.title })] : [],
|
|
249
|
+
...opts.tabs ? [opts.tabs] : [],
|
|
250
|
+
...keep(opts.lead),
|
|
251
|
+
el("span", { className: "swin-spacer" }),
|
|
252
|
+
...keep(opts.trail),
|
|
253
|
+
pin.el,
|
|
254
|
+
el("button", { className: "swin-close", tip: "Close (Esc)", onClick: opts.onClose }, iconNode("close"))
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
function followButton(opts) {
|
|
258
|
+
const what = opts.what ?? "line";
|
|
259
|
+
const b = el("button", "btn swin-follow", "Follow in the editor");
|
|
260
|
+
b.type = "button";
|
|
261
|
+
let on = opts.on;
|
|
262
|
+
const reflect = () => {
|
|
263
|
+
b.classList.toggle("on", on);
|
|
264
|
+
b.setAttribute("aria-pressed", String(on));
|
|
265
|
+
const label2 = on ? `The editor opens each ${what} as it plays. Click to stop.` : `Open each ${what} in the editor as it plays`;
|
|
266
|
+
b.dataset["tip"] = label2;
|
|
686
267
|
};
|
|
687
|
-
|
|
688
|
-
|
|
268
|
+
reflect();
|
|
269
|
+
b.addEventListener("click", () => {
|
|
270
|
+
on = !on;
|
|
271
|
+
reflect();
|
|
272
|
+
opts.onToggle(on);
|
|
273
|
+
});
|
|
274
|
+
return { el: b, set: (next) => {
|
|
275
|
+
on = next;
|
|
276
|
+
reflect();
|
|
277
|
+
} };
|
|
689
278
|
}
|
|
690
279
|
|
|
691
280
|
// src/confirm.ts
|
|
692
281
|
function confirmDialog(opts) {
|
|
693
282
|
return new Promise((resolve) => {
|
|
694
|
-
const cancel = el("button", "confirm-btn cancel", "Cancel");
|
|
695
|
-
const danger = el("button", "confirm-btn danger", opts.confirmLabel);
|
|
696
|
-
cancel.type = "button";
|
|
697
|
-
danger.type = "button";
|
|
698
|
-
const dlg = el(
|
|
699
|
-
"dialog",
|
|
700
|
-
"confirm-dialog",
|
|
701
|
-
el("div", "confirm-title", opts.title),
|
|
702
|
-
el("div", "confirm-body", opts.body),
|
|
703
|
-
el("div", "confirm-actions", cancel, danger)
|
|
704
|
-
);
|
|
705
|
-
dlg.setAttribute("aria-label", opts.title);
|
|
706
|
-
document.body.append(dlg);
|
|
707
283
|
let done = false;
|
|
708
284
|
const finish = (ok) => {
|
|
709
285
|
if (done) return;
|
|
710
286
|
done = true;
|
|
711
287
|
resolve(ok);
|
|
712
|
-
|
|
713
|
-
dlg.close();
|
|
714
|
-
dlg.remove();
|
|
715
|
-
});
|
|
288
|
+
frame.close();
|
|
716
289
|
};
|
|
290
|
+
const frame = dialogFrame({
|
|
291
|
+
title: opts.title,
|
|
292
|
+
className: "confirm-dialog",
|
|
293
|
+
onClose: () => finish(false)
|
|
294
|
+
// Esc, or closed by someone else
|
|
295
|
+
});
|
|
296
|
+
const cancel = el("button", "btn confirm-btn cancel", "Cancel");
|
|
297
|
+
const danger = el("button", "btn danger confirm-btn", opts.confirmLabel);
|
|
298
|
+
cancel.type = "button";
|
|
299
|
+
danger.type = "button";
|
|
300
|
+
if (opts.body !== void 0) frame.body.append(el("div", "confirm-body", opts.body));
|
|
301
|
+
if (opts.bodyNode) frame.body.append(opts.bodyNode);
|
|
302
|
+
frame.actions.append(cancel, danger);
|
|
717
303
|
cancel.addEventListener("click", () => finish(false));
|
|
718
304
|
danger.addEventListener("click", () => finish(true));
|
|
719
|
-
|
|
720
|
-
e.
|
|
721
|
-
finish(false);
|
|
722
|
-
});
|
|
723
|
-
dlg.addEventListener("mousedown", (e) => {
|
|
724
|
-
if (e.target === dlg) finish(false);
|
|
305
|
+
frame.dialog.addEventListener("mousedown", (e) => {
|
|
306
|
+
if (e.target === frame.dialog) finish(false);
|
|
725
307
|
});
|
|
726
|
-
|
|
308
|
+
frame.open();
|
|
727
309
|
cancel.focus();
|
|
728
310
|
});
|
|
729
311
|
}
|
|
@@ -731,18 +313,16 @@ function confirmDialog(opts) {
|
|
|
731
313
|
// src/about.ts
|
|
732
314
|
function showAbout(opts) {
|
|
733
315
|
return new Promise((resolve) => {
|
|
734
|
-
const
|
|
316
|
+
const frame = dialogFrame({ title: opts.appName, className: "shell-about", onClose: () => resolve() });
|
|
317
|
+
frame.dialog.setAttribute("aria-label", `About ${opts.appName}`);
|
|
735
318
|
if (opts.wordmark !== void 0) {
|
|
736
319
|
const mark = el("div", "shell-about-mark");
|
|
737
320
|
mark.innerHTML = opts.wordmark;
|
|
738
|
-
dialog.
|
|
321
|
+
frame.dialog.prepend(mark);
|
|
739
322
|
}
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
);
|
|
744
|
-
if (opts.blurb !== void 0) dialog.append(el("p", "shell-about-blurb", opts.blurb));
|
|
745
|
-
if (opts.credits !== void 0) dialog.append(el("p", "shell-about-credits", opts.credits));
|
|
323
|
+
frame.body.append(el("div", "shell-about-version", `Version ${opts.version}`));
|
|
324
|
+
if (opts.blurb !== void 0) frame.body.append(el("p", "shell-about-blurb", opts.blurb));
|
|
325
|
+
if (opts.credits !== void 0) frame.body.append(el("p", "shell-about-credits", opts.credits));
|
|
746
326
|
if (opts.links?.length) {
|
|
747
327
|
const row = el("div", "shell-about-links");
|
|
748
328
|
for (const link of opts.links) {
|
|
@@ -755,24 +335,13 @@ function showAbout(opts) {
|
|
|
755
335
|
a.addEventListener("click", () => opts.onOpenLink?.(link.url));
|
|
756
336
|
row.append(a);
|
|
757
337
|
}
|
|
758
|
-
|
|
338
|
+
frame.body.append(row);
|
|
759
339
|
}
|
|
760
|
-
const close = el("button", "shell-about-close", "Close");
|
|
340
|
+
const close = el("button", "btn shell-about-close", "Close");
|
|
761
341
|
close.type = "button";
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
let done = false;
|
|
766
|
-
const finish = () => {
|
|
767
|
-
if (done) return;
|
|
768
|
-
done = true;
|
|
769
|
-
dialog.close();
|
|
770
|
-
dialog.remove();
|
|
771
|
-
resolve();
|
|
772
|
-
};
|
|
773
|
-
close.addEventListener("click", finish);
|
|
774
|
-
dialog.addEventListener("close", finish);
|
|
775
|
-
dialog.showModal();
|
|
342
|
+
close.addEventListener("click", () => frame.close());
|
|
343
|
+
frame.actions.append(close);
|
|
344
|
+
frame.open();
|
|
776
345
|
close.focus();
|
|
777
346
|
});
|
|
778
347
|
}
|
|
@@ -787,7 +356,7 @@ function openAnchoredPanel(opts) {
|
|
|
787
356
|
if (prev.anchor === opts.anchor) return null;
|
|
788
357
|
}
|
|
789
358
|
const panel = el("div", `shell-anchored ${opts.className ?? ""}`.trim());
|
|
790
|
-
const closeBtn = el("button", "shell-anchored-close", "
|
|
359
|
+
const closeBtn = el("button", "shell-anchored-close", iconNode("close", 12));
|
|
791
360
|
closeBtn.type = "button";
|
|
792
361
|
closeBtn.setAttribute("aria-label", "Close");
|
|
793
362
|
const body = el("div", "shell-anchored-body");
|
|
@@ -795,7 +364,7 @@ function openAnchoredPanel(opts) {
|
|
|
795
364
|
el("div", "shell-anchored-head", el("span", "shell-anchored-title", opts.title), closeBtn),
|
|
796
365
|
body
|
|
797
366
|
);
|
|
798
|
-
document.body.append(panel);
|
|
367
|
+
(opts.host ?? document.body).append(panel);
|
|
799
368
|
placeAnchored(panel, opts.anchor, opts.width, opts.prefer ?? "below", opts.keepClear ?? []);
|
|
800
369
|
let closed = false;
|
|
801
370
|
const close = () => {
|
|
@@ -835,7 +404,7 @@ function closeAnchoredPanel() {
|
|
|
835
404
|
}
|
|
836
405
|
function placeAnchored(panel, anchor, width, prefer, keepClear) {
|
|
837
406
|
const a = anchor.getBoundingClientRect();
|
|
838
|
-
const
|
|
407
|
+
const GAP = 10;
|
|
839
408
|
const CAP = window.innerHeight * 0.7;
|
|
840
409
|
const MIN_H = 140;
|
|
841
410
|
panel.style.width = `${width}px`;
|
|
@@ -843,7 +412,7 @@ function placeAnchored(panel, anchor, width, prefer, keepClear) {
|
|
|
843
412
|
const centred = a.left + a.width / 2 - width / 2;
|
|
844
413
|
panel.style.left = `${Math.round(Math.min(Math.max(8, centred), window.innerWidth - width - 8))}px`;
|
|
845
414
|
} else {
|
|
846
|
-
const leftOf = a.left - width -
|
|
415
|
+
const leftOf = a.left - width - GAP;
|
|
847
416
|
const alignedRight = Math.min(a.right - width, window.innerWidth - width - 8);
|
|
848
417
|
panel.style.left = `${Math.round(prefer === "left" && leftOf >= 8 ? leftOf : Math.max(8, prefer === "left" ? a.left : alignedRight))}px`;
|
|
849
418
|
}
|
|
@@ -864,15 +433,15 @@ function placeAnchored(panel, anchor, width, prefer, keepClear) {
|
|
|
864
433
|
return;
|
|
865
434
|
}
|
|
866
435
|
}
|
|
867
|
-
const roomBelow = bottomLimit - (a.bottom +
|
|
868
|
-
const roomAbove = a.top -
|
|
436
|
+
const roomBelow = bottomLimit - (a.bottom + GAP);
|
|
437
|
+
const roomAbove = a.top - GAP - 8;
|
|
869
438
|
if (roomAbove > roomBelow) {
|
|
870
439
|
panel.style.top = "auto";
|
|
871
|
-
panel.style.bottom = `${Math.round(window.innerHeight - (a.top -
|
|
440
|
+
panel.style.bottom = `${Math.round(window.innerHeight - (a.top - GAP))}px`;
|
|
872
441
|
panel.style.maxHeight = `${Math.round(Math.max(MIN_H, Math.min(CAP, roomAbove)))}px`;
|
|
873
442
|
} else {
|
|
874
443
|
panel.style.bottom = "auto";
|
|
875
|
-
panel.style.top = `${Math.round(Math.max(8, a.bottom +
|
|
444
|
+
panel.style.top = `${Math.round(Math.max(8, a.bottom + GAP))}px`;
|
|
876
445
|
panel.style.maxHeight = `${Math.round(Math.max(MIN_H, Math.min(CAP, roomBelow)))}px`;
|
|
877
446
|
}
|
|
878
447
|
}
|
|
@@ -885,164 +454,6 @@ function isValidGameId(gameId) {
|
|
|
885
454
|
return /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(gameId);
|
|
886
455
|
}
|
|
887
456
|
|
|
888
|
-
// src/property-names.ts
|
|
889
|
-
var RESERVED_PROPERTY_NAMES = ["true", "false", "and", "or", "not"];
|
|
890
|
-
var PROPERTY_NAME_HINT = "Lower case letters, digits and underscores. Cannot start with a digit. No hyphens or spaces.";
|
|
891
|
-
function propertyNameify(text) {
|
|
892
|
-
const trimmed = text.trim();
|
|
893
|
-
const deliberateLeading = trimmed.startsWith("_");
|
|
894
|
-
let out = trimmed.toLowerCase().replace(/['’]/g, "").replace(/[^a-z0-9_]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
895
|
-
if (out === "") return "";
|
|
896
|
-
if (deliberateLeading || /^[0-9]/.test(out)) out = `_${out}`;
|
|
897
|
-
if (RESERVED_PROPERTY_NAMES.includes(out)) out = `${out}_`;
|
|
898
|
-
return out;
|
|
899
|
-
}
|
|
900
|
-
function isValidPropertyName(name) {
|
|
901
|
-
return /^[a-z_][a-z0-9_]*$/.test(name) && !RESERVED_PROPERTY_NAMES.includes(name);
|
|
902
|
-
}
|
|
903
|
-
function isCaseOnlyPropertyName(name) {
|
|
904
|
-
return !isValidPropertyName(name) && isValidPropertyName(name.toLowerCase());
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
// src/property-name-field.ts
|
|
908
|
-
var refSyncs = /* @__PURE__ */ new WeakMap();
|
|
909
|
-
function propertyNameProblem(name) {
|
|
910
|
-
if (isValidPropertyName(name)) return void 0;
|
|
911
|
-
const suggestion = propertyNameify(name);
|
|
912
|
-
const tail = suggestion ? ` Try "${suggestion}", or press Tab.` : "";
|
|
913
|
-
if (RESERVED_PROPERTY_NAMES.includes(name.toLowerCase())) {
|
|
914
|
-
return `Cannot be used: "${name.toLowerCase()}" is a keyword in expressions.${tail}`;
|
|
915
|
-
}
|
|
916
|
-
if (name.includes("-")) return `Cannot be used: a hyphen reads as subtraction in an expression.${tail}`;
|
|
917
|
-
if (/^[0-9]/.test(name)) return `Cannot be used: a name cannot start with a digit.${tail}`;
|
|
918
|
-
if (name !== name.toLowerCase()) {
|
|
919
|
-
return `Cannot be used: expressions fold names, so this would look for "${name.toLowerCase()}".${tail}`;
|
|
920
|
-
}
|
|
921
|
-
return `Cannot be used: only lower case letters, digits and underscores.${tail}`;
|
|
922
|
-
}
|
|
923
|
-
function bindPropertyName(input, onCommit, opts = {}) {
|
|
924
|
-
const hint = opts.hint;
|
|
925
|
-
const sync = () => {
|
|
926
|
-
const v = input.value.trim();
|
|
927
|
-
const problem = v === "" ? void 0 : propertyNameProblem(v);
|
|
928
|
-
input.classList.toggle("illegal", !!problem);
|
|
929
|
-
if (problem) input.title = problem;
|
|
930
|
-
else if (input.title.startsWith("Cannot be used")) {
|
|
931
|
-
if (hint) input.title = hint;
|
|
932
|
-
else input.removeAttribute("title");
|
|
933
|
-
}
|
|
934
|
-
};
|
|
935
|
-
input.addEventListener("input", () => {
|
|
936
|
-
onCommit(input.value);
|
|
937
|
-
sync();
|
|
938
|
-
});
|
|
939
|
-
input.addEventListener("keydown", (e) => {
|
|
940
|
-
if (e.key !== "Tab" || e.shiftKey) return;
|
|
941
|
-
const typed = input.value.trim();
|
|
942
|
-
if (typed === "" || isValidPropertyName(typed)) return;
|
|
943
|
-
const coerced = propertyNameify(typed);
|
|
944
|
-
if (!coerced || coerced === input.value) return;
|
|
945
|
-
e.preventDefault();
|
|
946
|
-
input.value = coerced;
|
|
947
|
-
onCommit(coerced);
|
|
948
|
-
sync();
|
|
949
|
-
});
|
|
950
|
-
sync();
|
|
951
|
-
}
|
|
952
|
-
function firstIllegalPropertyName(host) {
|
|
953
|
-
return host.querySelector("input.illegal");
|
|
954
|
-
}
|
|
955
|
-
function nearest(name, known) {
|
|
956
|
-
const n = name.toLowerCase();
|
|
957
|
-
const byPrefix = known.filter((k) => k.toLowerCase().startsWith(n) || n.startsWith(k.toLowerCase()));
|
|
958
|
-
if (byPrefix.length === 1) return byPrefix[0];
|
|
959
|
-
let best;
|
|
960
|
-
let bestScore = 3;
|
|
961
|
-
for (const k of known) {
|
|
962
|
-
const a = k.toLowerCase();
|
|
963
|
-
if (Math.abs(a.length - n.length) > 2) continue;
|
|
964
|
-
let prev = Array.from({ length: n.length + 1 }, (_, i) => i);
|
|
965
|
-
for (let i = 1; i <= a.length; i++) {
|
|
966
|
-
const row = [i];
|
|
967
|
-
for (let j = 1; j <= n.length; j++) {
|
|
968
|
-
row[j] = Math.min(prev[j] + 1, row[j - 1] + 1, prev[j - 1] + (a[i - 1] === n[j - 1] ? 0 : 1));
|
|
969
|
-
}
|
|
970
|
-
prev = row;
|
|
971
|
-
}
|
|
972
|
-
const d = prev[n.length];
|
|
973
|
-
if (d < bestScore) {
|
|
974
|
-
bestScore = d;
|
|
975
|
-
best = k;
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
return best;
|
|
979
|
-
}
|
|
980
|
-
function propertyRefProblem(name, opts) {
|
|
981
|
-
const scope = opts.scope ? `@${opts.scope}` : "that scope";
|
|
982
|
-
const folded = name.toLowerCase();
|
|
983
|
-
if (!isValidPropertyName(folded)) {
|
|
984
|
-
const suggestion = propertyNameify(name);
|
|
985
|
-
const tail = suggestion ? ` Try "${suggestion}", or press Tab.` : "";
|
|
986
|
-
if (RESERVED_PROPERTY_NAMES.includes(folded)) return `Cannot be used: "${folded}" is a keyword in expressions.${tail}`;
|
|
987
|
-
if (name.includes("-")) return `Cannot be used: a hyphen reads as subtraction in an expression.${tail}`;
|
|
988
|
-
if (/^[0-9]/.test(name)) return `Cannot be used: a name cannot start with a digit.${tail}`;
|
|
989
|
-
return `Cannot be used: only lower case letters, digits and underscores.${tail}`;
|
|
990
|
-
}
|
|
991
|
-
const known = opts.known();
|
|
992
|
-
if (known.some((k) => k.toLowerCase() === folded)) return void 0;
|
|
993
|
-
const near = nearest(folded, known);
|
|
994
|
-
if (near) return `No ${scope} property is called "${name}". Did you mean "${near}"?`;
|
|
995
|
-
if (known.length === 0) return `No ${scope} properties are declared yet. Declare one above first.`;
|
|
996
|
-
const shown = known.slice(0, 3).map((k) => `"${k}"`).join(", ");
|
|
997
|
-
const more = known.length > 3 ? `, and ${known.length - 3} more` : "";
|
|
998
|
-
return `No ${scope} property is called "${name}". Declared: ${shown}${more}.`;
|
|
999
|
-
}
|
|
1000
|
-
function bindPropertyRef(input, onCommit, opts) {
|
|
1001
|
-
const list = document.createElement("datalist");
|
|
1002
|
-
list.id = `shell-propref-${Math.random().toString(36).slice(2, 9)}`;
|
|
1003
|
-
input.setAttribute("list", list.id);
|
|
1004
|
-
const attach = () => {
|
|
1005
|
-
if (!list.isConnected && input.parentNode) input.after(list);
|
|
1006
|
-
};
|
|
1007
|
-
const sync = () => {
|
|
1008
|
-
attach();
|
|
1009
|
-
const known = opts.known();
|
|
1010
|
-
list.replaceChildren(...known.map((k) => {
|
|
1011
|
-
const o = document.createElement("option");
|
|
1012
|
-
o.value = k;
|
|
1013
|
-
return o;
|
|
1014
|
-
}));
|
|
1015
|
-
const v = input.value.trim();
|
|
1016
|
-
const problem = v === "" ? void 0 : propertyRefProblem(v, opts);
|
|
1017
|
-
input.classList.toggle("illegal", !!problem);
|
|
1018
|
-
if (problem) input.title = problem;
|
|
1019
|
-
else if (input.title.startsWith("Cannot be used") || input.title.startsWith("No ")) {
|
|
1020
|
-
if (opts.hint) input.title = opts.hint;
|
|
1021
|
-
else input.removeAttribute("title");
|
|
1022
|
-
}
|
|
1023
|
-
};
|
|
1024
|
-
refSyncs.set(input, sync);
|
|
1025
|
-
input.addEventListener("input", () => {
|
|
1026
|
-
onCommit(input.value);
|
|
1027
|
-
sync();
|
|
1028
|
-
});
|
|
1029
|
-
input.addEventListener("keydown", (e) => {
|
|
1030
|
-
if (e.key !== "Tab" || e.shiftKey) return;
|
|
1031
|
-
const typed = input.value.trim();
|
|
1032
|
-
if (typed === "" || isValidPropertyName(typed.toLowerCase())) return;
|
|
1033
|
-
const coerced = propertyNameify(typed);
|
|
1034
|
-
if (!coerced || coerced === input.value) return;
|
|
1035
|
-
e.preventDefault();
|
|
1036
|
-
input.value = coerced;
|
|
1037
|
-
onCommit(coerced);
|
|
1038
|
-
sync();
|
|
1039
|
-
});
|
|
1040
|
-
sync();
|
|
1041
|
-
}
|
|
1042
|
-
function revalidatePropertyRefs(host) {
|
|
1043
|
-
for (const input of host.querySelectorAll("input")) refSyncs.get(input)?.();
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
457
|
// src/id-editor.ts
|
|
1047
458
|
function openGameIdEditor(opts) {
|
|
1048
459
|
const slugify = opts.slugify ?? gameIdify;
|
|
@@ -1059,9 +470,9 @@ function openGameIdEditor(opts) {
|
|
|
1059
470
|
const input = el("input", "shell-id-input");
|
|
1060
471
|
input.type = "text";
|
|
1061
472
|
input.value = opts.value;
|
|
1062
|
-
input.placeholder = opts.derived || "
|
|
473
|
+
input.placeholder = opts.derived || "game-id";
|
|
1063
474
|
input.spellcheck = false;
|
|
1064
|
-
const hint = el("div", "shell-id-hint", "Lower case, digits, hyphens.
|
|
475
|
+
const hint = el("div", "shell-id-hint", "Lower case letters, digits, and hyphens. Leave it empty to derive one from the name.");
|
|
1065
476
|
const actions = el("div", "shell-id-actions");
|
|
1066
477
|
const reset = el("button", "shell-id-reset", "Reset to auto");
|
|
1067
478
|
const set = el("button", "shell-id-set", "Set");
|
|
@@ -1123,12 +534,13 @@ function foldVc(shards, keys) {
|
|
|
1123
534
|
return out;
|
|
1124
535
|
}
|
|
1125
536
|
function vcBadgeFor(s) {
|
|
1126
|
-
|
|
1127
|
-
if (s?.
|
|
1128
|
-
if (s?.
|
|
1129
|
-
if (s?.
|
|
1130
|
-
if (s?.
|
|
1131
|
-
if (s
|
|
537
|
+
const badge = (name, cls, title) => ({ name, glyph: icon[name] ?? "", cls, title });
|
|
538
|
+
if (s?.lockedBy?.length) return badge("locked", "vc-locked", `Locked by ${s.lockedBy.join(", ")}`);
|
|
539
|
+
if (s?.outOfDate) return badge("down", "vc-stale", "Out of date. A newer version is on the server.");
|
|
540
|
+
if (s?.checkedOutByMe) return badge("checkedOut", "vc-mine", "Checked out by you");
|
|
541
|
+
if (s?.dirty) return badge("modified", "vc-dirty", "Modified. Local changes aren't committed yet.");
|
|
542
|
+
if (s?.untracked) return badge("untracked", "vc-new", "New. Not in version control yet.");
|
|
543
|
+
if (s && !s.writable) return badge("readOnly", "vc-frozen", "Read-only on disk. Saving checks it out.");
|
|
1132
544
|
return null;
|
|
1133
545
|
}
|
|
1134
546
|
function paintVcBadges(root, shards) {
|
|
@@ -1136,7 +548,7 @@ function paintVcBadges(root, shards) {
|
|
|
1136
548
|
host.querySelector(":scope > .vc-badge")?.remove();
|
|
1137
549
|
const badge = vcBadgeFor(foldVc(shards, host.dataset["vc"]));
|
|
1138
550
|
if (!badge) return;
|
|
1139
|
-
const span = el("span", { className: `vc-badge ${badge.cls}`,
|
|
551
|
+
const span = el("span", { className: `vc-badge ${badge.cls}`, tip: badge.title }, iconNode(badge.name, 12));
|
|
1140
552
|
host.append(span);
|
|
1141
553
|
});
|
|
1142
554
|
}
|
|
@@ -1159,8 +571,8 @@ function lockNotice(holders) {
|
|
|
1159
571
|
return el(
|
|
1160
572
|
"div",
|
|
1161
573
|
{ className: "vc-lock" },
|
|
1162
|
-
el("span", { className: "vc-lock-glyph",
|
|
1163
|
-
el("span", { text: `Locked by ${holders.join(", ")} -
|
|
574
|
+
el("span", { className: "vc-lock-glyph" }, iconNode("locked")),
|
|
575
|
+
el("span", { text: `Locked by ${holders.join(", ")}. Read-only until they release it.` })
|
|
1164
576
|
);
|
|
1165
577
|
}
|
|
1166
578
|
|
|
@@ -1168,41 +580,36 @@ function lockNotice(holders) {
|
|
|
1168
580
|
function askIdentity(opts = {}) {
|
|
1169
581
|
return new Promise((resolve) => {
|
|
1170
582
|
const seed = opts.current ?? opts.suggested;
|
|
1171
|
-
const name = el("input", "shell-ident-input");
|
|
583
|
+
const name = el("input", "field shell-ident-input");
|
|
1172
584
|
name.placeholder = "Your name";
|
|
1173
585
|
name.value = seed?.name ?? "";
|
|
1174
|
-
const email = el("input", "shell-ident-input");
|
|
1175
|
-
email.placeholder = "
|
|
586
|
+
const email = el("input", "field shell-ident-input");
|
|
587
|
+
email.placeholder = "Optional";
|
|
1176
588
|
email.type = "email";
|
|
1177
589
|
email.value = seed?.email ?? "";
|
|
1178
|
-
const skip = el("button", "shell-ident-btn", "Skip");
|
|
1179
|
-
const save = el("button", "shell-ident-btn
|
|
590
|
+
const skip = el("button", "btn shell-ident-btn", "Skip");
|
|
591
|
+
const save = el("button", "btn primary shell-ident-btn", opts.mode === "welcome" ? "Continue" : "Save");
|
|
1180
592
|
skip.type = "button";
|
|
1181
593
|
save.type = "button";
|
|
1182
|
-
const dialog = el(
|
|
1183
|
-
"dialog",
|
|
1184
|
-
"shell-ident",
|
|
1185
|
-
el("div", "shell-ident-title", "Who is working here?"),
|
|
1186
|
-
el(
|
|
1187
|
-
"p",
|
|
1188
|
-
"shell-ident-hint",
|
|
1189
|
-
"Stamped on the comments you write. Kept in this app, never in the project, and changeable later."
|
|
1190
|
-
),
|
|
1191
|
-
el("div", "shell-ident-fields", name, email),
|
|
1192
|
-
el("div", "shell-ident-actions", skip, save)
|
|
1193
|
-
);
|
|
1194
|
-
document.body.append(dialog);
|
|
1195
594
|
let done = false;
|
|
1196
595
|
const finish = (keep) => {
|
|
1197
596
|
if (done) return;
|
|
1198
597
|
done = true;
|
|
1199
598
|
const typed = name.value.trim();
|
|
1200
599
|
const value = email.value.trim();
|
|
1201
|
-
const answer = keep && typed !== "" ? value === "" ? { name: typed } : { name: typed, email: value } : void 0;
|
|
1202
|
-
dialog.close();
|
|
1203
|
-
dialog.remove();
|
|
600
|
+
const answer = keep && typed !== "" ? value === "" ? { name: typed } : { name: typed, email: value } : opts.neverAskAgainOnSkip === true ? { name: "" } : void 0;
|
|
1204
601
|
resolve(answer);
|
|
602
|
+
frame.close();
|
|
1205
603
|
};
|
|
604
|
+
const frame = dialogFrame({
|
|
605
|
+
title: titleFor(opts),
|
|
606
|
+
sub: subFor(opts),
|
|
607
|
+
className: "shell-ident",
|
|
608
|
+
onClose: () => finish(false)
|
|
609
|
+
// Escape, or closed by someone else: a skip
|
|
610
|
+
});
|
|
611
|
+
frame.body.append(el("div", "shell-ident-fields", labelled("Name", name), labelled("Email", email)));
|
|
612
|
+
frame.actions.append(skip, save);
|
|
1206
613
|
skip.addEventListener("click", () => finish(false));
|
|
1207
614
|
save.addEventListener("click", () => finish(true));
|
|
1208
615
|
name.addEventListener("keydown", (e) => {
|
|
@@ -1211,11 +618,19 @@ function askIdentity(opts = {}) {
|
|
|
1211
618
|
email.addEventListener("keydown", (e) => {
|
|
1212
619
|
if (e.key === "Enter") finish(true);
|
|
1213
620
|
});
|
|
1214
|
-
|
|
1215
|
-
dialog.showModal();
|
|
621
|
+
frame.open();
|
|
1216
622
|
name.focus();
|
|
1217
623
|
});
|
|
1218
624
|
}
|
|
625
|
+
function titleFor(opts) {
|
|
626
|
+
if (opts.mode === "welcome") return opts.appName ? `Welcome to ${opts.appName}` : "Welcome";
|
|
627
|
+
if (opts.mode === "edit") return "User information";
|
|
628
|
+
return "Who is working here?";
|
|
629
|
+
}
|
|
630
|
+
function subFor(opts) {
|
|
631
|
+
const why = "Your name goes on the comments you write. It's kept in this app, not in the project.";
|
|
632
|
+
return opts.mode === "edit" ? why : `${why} You can change it later.`;
|
|
633
|
+
}
|
|
1219
634
|
|
|
1220
635
|
// src/notes-editor.ts
|
|
1221
636
|
var EDITOR_ONLY = "";
|
|
@@ -1228,10 +643,10 @@ var CLASS_LABEL = {
|
|
|
1228
643
|
};
|
|
1229
644
|
var label = (cls) => cls === EDITOR_ONLY ? "Note (editor-only)" : CLASS_LABEL[cls] ?? cls[0].toUpperCase() + cls.slice(1);
|
|
1230
645
|
var PLACEHOLDER = {
|
|
1231
|
-
vo: "
|
|
1232
|
-
loc: "
|
|
646
|
+
vo: "Direction for the voice actor or director",
|
|
647
|
+
loc: "Context for translators"
|
|
1233
648
|
};
|
|
1234
|
-
var placeholder = (cls) => cls === EDITOR_ONLY ? "
|
|
649
|
+
var placeholder = (cls) => cls === EDITOR_ONLY ? "An internal note, never exported" : PLACEHOLDER[cls] ?? "Why this is here";
|
|
1235
650
|
function openNotesEditor(opts) {
|
|
1236
651
|
const { notes } = opts;
|
|
1237
652
|
const seed = /* @__PURE__ */ new Map();
|
|
@@ -1285,7 +700,7 @@ ${line.text}` : line.text);
|
|
|
1285
700
|
{ className: "shell-notes-class" },
|
|
1286
701
|
el("label", "shell-notes-caption", label(cls))
|
|
1287
702
|
);
|
|
1288
|
-
const area = el("textarea", "shell-notes-text");
|
|
703
|
+
const area = el("textarea", "field shell-notes-text");
|
|
1289
704
|
area.value = areas.get(cls)?.value ?? seed.get(cls) ?? "";
|
|
1290
705
|
area.rows = 4;
|
|
1291
706
|
area.placeholder = placeholder(cls);
|
|
@@ -1311,29 +726,24 @@ ${line.text}` : line.text);
|
|
|
1311
726
|
}
|
|
1312
727
|
};
|
|
1313
728
|
render();
|
|
1314
|
-
const done = el("button", "shell-notes-btn
|
|
1315
|
-
|
|
1316
|
-
"dialog",
|
|
1317
|
-
{ className: "shell-notes" },
|
|
1318
|
-
el("div", { className: "shell-notes-title", text: `Notes: ${opts.subject}` }),
|
|
1319
|
-
body,
|
|
1320
|
-
el("div", { className: "shell-notes-actions" }, done)
|
|
1321
|
-
);
|
|
1322
|
-
dialog.setAttribute("aria-label", `Notes: ${opts.subject}`);
|
|
1323
|
-
document.body.append(dialog);
|
|
729
|
+
const done = el("button", "btn primary shell-notes-btn", "Done");
|
|
730
|
+
done.type = "button";
|
|
1324
731
|
const before = JSON.stringify(notes.own);
|
|
1325
732
|
let closed = false;
|
|
1326
|
-
const
|
|
733
|
+
const commit = () => {
|
|
1327
734
|
if (closed) return;
|
|
1328
735
|
closed = true;
|
|
1329
736
|
const next = value();
|
|
1330
737
|
if (JSON.stringify(next) !== before) opts.save(next);
|
|
1331
|
-
dialog.close();
|
|
1332
|
-
dialog.remove();
|
|
1333
738
|
};
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
739
|
+
const frame = dialogFrame({ title: `Notes: ${opts.subject}`, className: "shell-notes", onClose: commit });
|
|
740
|
+
frame.body.append(body);
|
|
741
|
+
frame.actions.append(done);
|
|
742
|
+
done.addEventListener("click", () => {
|
|
743
|
+
commit();
|
|
744
|
+
frame.close();
|
|
745
|
+
});
|
|
746
|
+
frame.open();
|
|
1337
747
|
areas.get(shown[0] ?? EDITOR_ONLY)?.focus();
|
|
1338
748
|
}
|
|
1339
749
|
|
|
@@ -1405,7 +815,7 @@ function openComments(opts) {
|
|
|
1405
815
|
el("span", { className: "shell-cmt-ts", text: stamp(message.ts) })
|
|
1406
816
|
);
|
|
1407
817
|
if (opts.deleteMessage && message.deleted !== true) {
|
|
1408
|
-
const bin = el("button", { className: "shell-cmt-del",
|
|
818
|
+
const bin = el("button", { className: "shell-cmt-del" }, iconNode("close", 12));
|
|
1409
819
|
bin.dataset.tip = "Delete this comment";
|
|
1410
820
|
bin.setAttribute("aria-label", "Delete this comment");
|
|
1411
821
|
bin.addEventListener("click", () => opts.deleteMessage?.(thread.id, index));
|
|
@@ -1470,37 +880,65 @@ var clock = (ms) => {
|
|
|
1470
880
|
var estimable = (done, total, elapsedMs) => done > 0 && total > 0 && elapsedMs > 1e3 && done / total > 0.1;
|
|
1471
881
|
function mountJobProgress(host, opts) {
|
|
1472
882
|
const units = opts.units ?? "runs";
|
|
883
|
+
const pinned = opts.indeterminate === true;
|
|
884
|
+
const cancellableByDefault = opts.cancellable ?? opts.onCancel !== void 0;
|
|
1473
885
|
const fill = el("i", { className: "job-fill" });
|
|
1474
886
|
const bar = el("div", { className: "job-bar" }, fill);
|
|
887
|
+
bar.setAttribute("role", "progressbar");
|
|
888
|
+
bar.setAttribute("aria-valuemin", "0");
|
|
889
|
+
bar.setAttribute("aria-valuemax", "100");
|
|
1475
890
|
const what = el("span", { className: "job-label" });
|
|
1476
891
|
const count = el("span", { className: "job-count" });
|
|
1477
892
|
const time = el("span", { className: "job-time" });
|
|
1478
893
|
const cancel = el("button", { className: "job-cancel", text: "Cancel" });
|
|
894
|
+
cancel.type = "button";
|
|
1479
895
|
const strip = el("div", { className: "job-strip" }, what, bar, count, time, cancel);
|
|
1480
896
|
strip.hidden = true;
|
|
1481
897
|
host.append(strip);
|
|
898
|
+
let runPinned = pinned;
|
|
899
|
+
let indeterminate = false;
|
|
900
|
+
const setMode = (band) => {
|
|
901
|
+
indeterminate = band;
|
|
902
|
+
strip.classList.toggle("indeterminate", band);
|
|
903
|
+
count.hidden = band;
|
|
904
|
+
if (band) {
|
|
905
|
+
fill.style.width = "";
|
|
906
|
+
bar.removeAttribute("aria-valuenow");
|
|
907
|
+
}
|
|
908
|
+
};
|
|
1482
909
|
const view = {
|
|
1483
910
|
element: strip,
|
|
1484
911
|
get visible() {
|
|
1485
912
|
return !strip.hidden;
|
|
1486
913
|
},
|
|
1487
|
-
|
|
914
|
+
get indeterminate() {
|
|
915
|
+
return indeterminate;
|
|
916
|
+
},
|
|
917
|
+
begin(label2, run) {
|
|
918
|
+
runPinned = run?.indeterminate ?? pinned;
|
|
919
|
+
const cancellable = run?.cancellable ?? cancellableByDefault;
|
|
1488
920
|
what.textContent = label2 ?? "Running\u2026";
|
|
1489
921
|
count.textContent = "";
|
|
1490
922
|
time.textContent = "";
|
|
1491
|
-
|
|
923
|
+
cancel.hidden = !cancellable;
|
|
1492
924
|
cancel.disabled = false;
|
|
1493
925
|
cancel.textContent = "Cancel";
|
|
926
|
+
setMode(true);
|
|
1494
927
|
strip.hidden = false;
|
|
1495
928
|
},
|
|
1496
929
|
update(done, total, elapsedMs) {
|
|
1497
930
|
if (strip.hidden) strip.hidden = false;
|
|
1498
|
-
const
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
931
|
+
const band = runPinned || !(total > 0);
|
|
932
|
+
setMode(band);
|
|
933
|
+
if (!band) {
|
|
934
|
+
const pct = Math.min(100, done / total * 100);
|
|
935
|
+
fill.style.width = `${pct}%`;
|
|
936
|
+
bar.setAttribute("aria-valuenow", String(Math.round(pct)));
|
|
937
|
+
count.textContent = `${done} / ${total} ${units}`.trimEnd();
|
|
938
|
+
}
|
|
939
|
+
if (!band && estimable(done, total, elapsedMs)) {
|
|
1502
940
|
const remaining = elapsedMs / done * (total - done);
|
|
1503
|
-
time.textContent = `${clock(elapsedMs)} elapsed
|
|
941
|
+
time.textContent = `${clock(elapsedMs)} elapsed, about ${clock(remaining)} left`;
|
|
1504
942
|
} else {
|
|
1505
943
|
time.textContent = clock(elapsedMs);
|
|
1506
944
|
}
|
|
@@ -1512,8 +950,10 @@ function mountJobProgress(host, opts) {
|
|
|
1512
950
|
cancel.addEventListener("click", () => {
|
|
1513
951
|
cancel.disabled = true;
|
|
1514
952
|
cancel.textContent = "Stopping\u2026";
|
|
1515
|
-
opts.onCancel();
|
|
953
|
+
opts.onCancel?.();
|
|
1516
954
|
});
|
|
955
|
+
cancel.hidden = !cancellableByDefault;
|
|
956
|
+
setMode(true);
|
|
1517
957
|
return view;
|
|
1518
958
|
}
|
|
1519
959
|
|
|
@@ -1525,7 +965,7 @@ function createSaveController(opts) {
|
|
|
1525
965
|
let auto = true;
|
|
1526
966
|
let dirty = false;
|
|
1527
967
|
let dirtySince = 0;
|
|
1528
|
-
let
|
|
968
|
+
let timer2;
|
|
1529
969
|
let inFlight;
|
|
1530
970
|
const setStatus = (next) => {
|
|
1531
971
|
if (next === status) return;
|
|
@@ -1533,17 +973,17 @@ function createSaveController(opts) {
|
|
|
1533
973
|
opts.onStatus?.(status);
|
|
1534
974
|
};
|
|
1535
975
|
const clearTimer = () => {
|
|
1536
|
-
if (
|
|
1537
|
-
clearTimeout(
|
|
1538
|
-
|
|
976
|
+
if (timer2 === void 0) return;
|
|
977
|
+
clearTimeout(timer2);
|
|
978
|
+
timer2 = void 0;
|
|
1539
979
|
};
|
|
1540
980
|
const arm = () => {
|
|
1541
981
|
clearTimer();
|
|
1542
982
|
if (!auto) return;
|
|
1543
983
|
const deadline = dirtySince + maxWaitMs;
|
|
1544
984
|
const wait = Math.max(0, Math.min(delayMs, deadline - Date.now()));
|
|
1545
|
-
|
|
1546
|
-
|
|
985
|
+
timer2 = setTimeout(() => {
|
|
986
|
+
timer2 = void 0;
|
|
1547
987
|
void run();
|
|
1548
988
|
}, wait);
|
|
1549
989
|
};
|
|
@@ -1636,18 +1076,21 @@ function staleBar(opts) {
|
|
|
1636
1076
|
className: "stale-bar-msg",
|
|
1637
1077
|
text: `${opts.subject} changed in the editor. ${opts.advice ?? "Restart to play the new version."}`
|
|
1638
1078
|
}),
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1079
|
+
// The icon is a node beside the word, not a character inside it; the
|
|
1080
|
+
// button's flex gap sets the space (stale.css).
|
|
1081
|
+
el(
|
|
1082
|
+
"button",
|
|
1083
|
+
{ className: "stale-bar-go", onClick: () => opts.onRestart() },
|
|
1084
|
+
iconNode("restart", 12),
|
|
1085
|
+
label2
|
|
1086
|
+
)
|
|
1644
1087
|
);
|
|
1645
1088
|
}
|
|
1646
1089
|
|
|
1647
1090
|
// src/toast.ts
|
|
1648
1091
|
var DURATION = { info: 4e3, ok: 4e3, error: 7e3 };
|
|
1649
1092
|
var node;
|
|
1650
|
-
var
|
|
1093
|
+
var timer = 0;
|
|
1651
1094
|
var seq = 0;
|
|
1652
1095
|
function mount() {
|
|
1653
1096
|
if (node && node.isConnected) return node;
|
|
@@ -1664,8 +1107,8 @@ function toast(message, kind = "info", opts = {}) {
|
|
|
1664
1107
|
t.textContent = message;
|
|
1665
1108
|
t.className = `shell-toast ${kind}`;
|
|
1666
1109
|
t.hidden = false;
|
|
1667
|
-
window.clearTimeout(
|
|
1668
|
-
|
|
1110
|
+
window.clearTimeout(timer);
|
|
1111
|
+
timer = window.setTimeout(() => {
|
|
1669
1112
|
closeWithExit(t, () => {
|
|
1670
1113
|
if (seq === id) t.hidden = true;
|
|
1671
1114
|
});
|
|
@@ -1673,7 +1116,7 @@ function toast(message, kind = "info", opts = {}) {
|
|
|
1673
1116
|
}
|
|
1674
1117
|
function dismissToast() {
|
|
1675
1118
|
if (!node || node.hidden) return;
|
|
1676
|
-
window.clearTimeout(
|
|
1119
|
+
window.clearTimeout(timer);
|
|
1677
1120
|
const id = seq;
|
|
1678
1121
|
const t = node;
|
|
1679
1122
|
closeWithExit(t, () => {
|
|
@@ -1682,14 +1125,14 @@ function dismissToast() {
|
|
|
1682
1125
|
}
|
|
1683
1126
|
|
|
1684
1127
|
// src/stepper.ts
|
|
1128
|
+
var STEP_ICON = 12;
|
|
1685
1129
|
function renderStepperBar(host, opts) {
|
|
1686
1130
|
const { items } = opts;
|
|
1687
1131
|
const close = opts.onClose === void 0 ? null : el("button", {
|
|
1688
1132
|
className: "stepbar-nav stepbar-close",
|
|
1689
|
-
text: icon.close,
|
|
1690
1133
|
tip: opts.closeTip ?? "Close",
|
|
1691
1134
|
onClick: opts.onClose
|
|
1692
|
-
});
|
|
1135
|
+
}, iconNode("close", STEP_ICON));
|
|
1693
1136
|
const actions = (opts.actions ?? []).filter((a) => a !== null && a !== void 0);
|
|
1694
1137
|
if (items.length === 0) {
|
|
1695
1138
|
if (opts.empty === void 0) {
|
|
@@ -1713,17 +1156,15 @@ function renderStepperBar(host, opts) {
|
|
|
1713
1156
|
const nav = items.length > 1 ? [
|
|
1714
1157
|
el("button", {
|
|
1715
1158
|
className: "stepbar-nav",
|
|
1716
|
-
text: icon.back,
|
|
1717
1159
|
tip: opts.tips?.prev ?? "Previous",
|
|
1718
1160
|
onClick: () => step(-1)
|
|
1719
|
-
}),
|
|
1161
|
+
}, iconNode("back", STEP_ICON)),
|
|
1720
1162
|
el("span", { className: "stepbar-of", text: `${index + 1}/${items.length}` }),
|
|
1721
1163
|
el("button", {
|
|
1722
1164
|
className: "stepbar-nav",
|
|
1723
|
-
text: icon.forward,
|
|
1724
1165
|
tip: opts.tips?.next ?? "Next",
|
|
1725
1166
|
onClick: () => step(1)
|
|
1726
|
-
})
|
|
1167
|
+
}, iconNode("forward", STEP_ICON))
|
|
1727
1168
|
] : [];
|
|
1728
1169
|
host.replaceChildren(
|
|
1729
1170
|
el("span", { className: `stepbar-count ${opts.tone ?? "muted"}`, text: String(items.length) }),
|
|
@@ -1732,7 +1173,7 @@ function renderStepperBar(host, opts) {
|
|
|
1732
1173
|
"button",
|
|
1733
1174
|
{ className: "stepbar-cur", tip: opts.tips?.go ?? "Go to this", onClick: () => opts.onGo(index) },
|
|
1734
1175
|
item.kind === void 0 ? null : el("span", { className: `stepbar-cat${item.kindClass ? ` ${item.kindClass}` : ""}`, text: item.kind }),
|
|
1735
|
-
item.where === void 0 ? null : el("span", { className: "stepbar-where", text: item.where }),
|
|
1176
|
+
item.where === void 0 ? null : typeof item.where === "string" ? el("span", { className: "stepbar-where", text: item.where }) : el("span", "stepbar-where", item.where),
|
|
1736
1177
|
el("span", { className: "stepbar-msg", text: item.text })
|
|
1737
1178
|
),
|
|
1738
1179
|
...actions,
|
|
@@ -1757,11 +1198,11 @@ function createNavHistory(opts) {
|
|
|
1757
1198
|
}
|
|
1758
1199
|
};
|
|
1759
1200
|
return {
|
|
1760
|
-
visit(
|
|
1201
|
+
visit(place) {
|
|
1761
1202
|
ahead = [];
|
|
1762
1203
|
const top = past[past.length - 1];
|
|
1763
|
-
if (top !== void 0 && opts.same(top,
|
|
1764
|
-
else past.push(
|
|
1204
|
+
if (top !== void 0 && opts.same(top, place)) past[past.length - 1] = place;
|
|
1205
|
+
else past.push(place);
|
|
1765
1206
|
if (past.length > cap) past = past.slice(past.length - cap);
|
|
1766
1207
|
},
|
|
1767
1208
|
back(current2) {
|
|
@@ -1779,18 +1220,18 @@ function createNavHistory(opts) {
|
|
|
1779
1220
|
};
|
|
1780
1221
|
}
|
|
1781
1222
|
function historyNav(onBack, onForward) {
|
|
1782
|
-
const btn = (
|
|
1223
|
+
const btn = (name, tip, onClick) => {
|
|
1783
1224
|
const b = document.createElement("button");
|
|
1784
1225
|
b.type = "button";
|
|
1785
1226
|
b.className = "shell-histnav-btn";
|
|
1786
|
-
b.
|
|
1787
|
-
b.dataset["tip"] =
|
|
1788
|
-
b.setAttribute("aria-label",
|
|
1227
|
+
b.append(iconNode(name));
|
|
1228
|
+
b.dataset["tip"] = tip;
|
|
1229
|
+
b.setAttribute("aria-label", tip);
|
|
1789
1230
|
b.addEventListener("click", onClick);
|
|
1790
1231
|
return b;
|
|
1791
1232
|
};
|
|
1792
|
-
const back = btn("
|
|
1793
|
-
const forward = btn("
|
|
1233
|
+
const back = btn("arrowLeft", "Back", onBack);
|
|
1234
|
+
const forward = btn("arrowRight", "Forward", onForward);
|
|
1794
1235
|
const el2 = document.createElement("span");
|
|
1795
1236
|
el2.className = "shell-histnav";
|
|
1796
1237
|
el2.append(back, forward);
|
|
@@ -1803,6 +1244,6 @@ function historyNav(onBack, onForward) {
|
|
|
1803
1244
|
};
|
|
1804
1245
|
}
|
|
1805
1246
|
|
|
1806
|
-
export { PALETTE, PALETTE_SIZE,
|
|
1247
|
+
export { PALETTE, PALETTE_SIZE, askIdentity, closeAnchoredPanel, colourFor, colourIndex, confirmDialog, createNavHistory, createSaveController, dismissToast, foldVc, followButton, gameIdify, historyNav, isValidGameId, lockControls, lockNotice, mountJobProgress, mountPaneShell, openAnchoredPanel, openComments, openGameIdEditor, openNotesEditor, paintVcBadges, pinButton, placeAnchored, renderStepperBar, saveIndicator, showAbout, staleBar, toast, toolWindowHead, vcBadgeFor };
|
|
1807
1248
|
//# sourceMappingURL=index.js.map
|
|
1808
1249
|
//# sourceMappingURL=index.js.map
|