@wildwinter/app-shell 0.57.0 → 0.58.1

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 (66) hide show
  1. package/README.md +195 -0
  2. package/dist/chunk-4JQWNC6Z.js +319 -0
  3. package/dist/chunk-4JQWNC6Z.js.map +1 -0
  4. package/dist/chunk-ICYPXUNA.js +8 -0
  5. package/dist/chunk-ICYPXUNA.js.map +1 -0
  6. package/dist/{chunk-XJM733E6.js → chunk-OO6O4LEL.js} +6 -2
  7. package/dist/chunk-OO6O4LEL.js.map +1 -0
  8. package/dist/{chunk-CPNTROWD.js → chunk-OZQ2BYXH.js} +13 -4
  9. package/dist/chunk-OZQ2BYXH.js.map +1 -0
  10. package/dist/{chunk-5BKGDTYD.js → chunk-RMQNBWLZ.js} +3 -3
  11. package/dist/{chunk-5BKGDTYD.js.map → chunk-RMQNBWLZ.js.map} +1 -1
  12. package/dist/index.cjs +334 -3
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +2 -0
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.js +4 -2
  17. package/dist/index.js.map +1 -1
  18. package/dist/property-list.cjs.map +1 -1
  19. package/dist/property-list.js +2 -2
  20. package/dist/tool-window.cjs +11 -2
  21. package/dist/tool-window.cjs.map +1 -1
  22. package/dist/tool-window.d.cts +10 -4
  23. package/dist/tool-window.d.ts +10 -4
  24. package/dist/tool-window.js +1 -1
  25. package/dist/updater.cjs.map +1 -1
  26. package/dist/updater.js +1 -1
  27. package/dist/value-control.cjs +4 -0
  28. package/dist/value-control.cjs.map +1 -1
  29. package/dist/value-control.d.cts +4 -4
  30. package/dist/value-control.d.ts +4 -4
  31. package/dist/value-control.js +1 -1
  32. package/dist/value-rows.cjs +8 -1
  33. package/dist/value-rows.cjs.map +1 -1
  34. package/dist/value-rows.js +5 -2
  35. package/dist/value-rows.js.map +1 -1
  36. package/dist/view-client.cjs +172 -0
  37. package/dist/view-client.cjs.map +1 -0
  38. package/dist/view-client.d.cts +46 -0
  39. package/dist/view-client.d.ts +46 -0
  40. package/dist/view-client.js +165 -0
  41. package/dist/view-client.js.map +1 -0
  42. package/dist/view-host-web.cjs +327 -0
  43. package/dist/view-host-web.cjs.map +1 -0
  44. package/dist/view-host-web.d.cts +61 -0
  45. package/dist/view-host-web.d.ts +61 -0
  46. package/dist/view-host-web.js +4 -0
  47. package/dist/view-host-web.js.map +1 -0
  48. package/dist/view-host.cjs +532 -0
  49. package/dist/view-host.cjs.map +1 -0
  50. package/dist/view-host.d.cts +178 -0
  51. package/dist/view-host.d.ts +178 -0
  52. package/dist/view-host.js +426 -0
  53. package/dist/view-host.js.map +1 -0
  54. package/dist/view-preload.cjs +16 -0
  55. package/dist/view-preload.cjs.map +1 -0
  56. package/dist/view-preload.js +8 -0
  57. package/dist/view-preload.js.map +1 -0
  58. package/dist/view-protocol.cjs +12 -0
  59. package/dist/view-protocol.cjs.map +1 -0
  60. package/dist/view-protocol.d.cts +257 -0
  61. package/dist/view-protocol.d.ts +257 -0
  62. package/dist/view-protocol.js +3 -0
  63. package/dist/view-protocol.js.map +1 -0
  64. package/package.json +25 -4
  65. package/dist/chunk-CPNTROWD.js.map +0 -1
  66. package/dist/chunk-XJM733E6.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -1262,10 +1262,10 @@ function mountSettingsDialog(opts) {
1262
1262
  for (const s of opts.sections) {
1263
1263
  const found = handles.get(s.id)?.firstInvalid?.();
1264
1264
  if (!found) continue;
1265
- const bad = found instanceof HTMLElement ? found : found.el;
1266
- const message = (found instanceof HTMLElement ? void 0 : found.message) ?? bad.dataset["tip"];
1265
+ const bad2 = found instanceof HTMLElement ? found : found.el;
1266
+ const message = (found instanceof HTMLElement ? void 0 : found.message) ?? bad2.dataset["tip"];
1267
1267
  showTab(s.id);
1268
- bad.focus();
1268
+ bad2.focus();
1269
1269
  error.textContent = message || "Fix the highlighted fields first.";
1270
1270
  error.hidden = false;
1271
1271
  return;
@@ -3235,6 +3235,10 @@ function valueHelpText(decl, value) {
3235
3235
  return h === void 0 ? [] : [`${v}: ${h}`];
3236
3236
  }).join("\n");
3237
3237
  }
3238
+ if (decl.type === "pick") {
3239
+ const held = typeof value === "string" && value !== "" ? value : typeof decl.default === "string" ? decl.default : "";
3240
+ return held === "" ? "" : said(held) ?? "";
3241
+ }
3238
3242
  return "";
3239
3243
  }
3240
3244
  var fallbackFlags = (decl) => Array.isArray(decl.default) ? decl.default.filter((v) => typeof v === "string") : [];
@@ -3838,6 +3842,325 @@ function historyNav(onBack, onForward) {
3838
3842
  };
3839
3843
  }
3840
3844
 
3845
+ // src/view-protocol.ts
3846
+ var VIEW_API = 1;
3847
+ var VIEW_PORT_CHANNEL = "wildwinter-view:port";
3848
+ var VIEW_PORT_MESSAGE = "wildwinter-view:port";
3849
+
3850
+ // src/view-host-web.ts
3851
+ var PALETTE_TOKENS = [
3852
+ "--bg",
3853
+ "--surface",
3854
+ "--card",
3855
+ "--ink",
3856
+ "--muted",
3857
+ "--line",
3858
+ "--line-soft",
3859
+ "--accent",
3860
+ "--accent-soft",
3861
+ "--on-accent",
3862
+ "--danger",
3863
+ "--warn",
3864
+ "--ok",
3865
+ "--font-ui",
3866
+ "--font-read",
3867
+ "--font-mono",
3868
+ ...Array.from({ length: 12 }, (_, i) => `--char-${i}`)
3869
+ ];
3870
+ var GRAMMAR_TOKENS = [
3871
+ "--color-scheme",
3872
+ "--chrome-size",
3873
+ "--scrim",
3874
+ "--shadow-1",
3875
+ "--shadow-2",
3876
+ "--shadow-3",
3877
+ "--ease-standard",
3878
+ "--dur-quick",
3879
+ "--dur-base",
3880
+ "--dur-settle",
3881
+ "--dur-slow",
3882
+ "--dur-panel",
3883
+ "--dur-exit",
3884
+ "--radius-sm",
3885
+ "--radius-md",
3886
+ "--radius-lg",
3887
+ "--radius-pill",
3888
+ "--scroll-thumb",
3889
+ "--scroll-thumb-hover"
3890
+ ];
3891
+ var VIEW_THEME_TOKENS = [...PALETTE_TOKENS, ...GRAMMAR_TOKENS];
3892
+ function readTheme(root = document.documentElement, tokens = VIEW_THEME_TOKENS) {
3893
+ const style = getComputedStyle(root);
3894
+ const out = {};
3895
+ for (const name of tokens) {
3896
+ const value = style.getPropertyValue(name).trim();
3897
+ if (value !== "") out[name] = value;
3898
+ }
3899
+ const declared = (style.getPropertyValue("color-scheme") || style.colorScheme || "").trim();
3900
+ const words = declared.split(/\s+/);
3901
+ let scheme = "light";
3902
+ if (declared === "dark") scheme = "dark";
3903
+ else if (words.includes("light") && words.includes("dark")) {
3904
+ scheme = typeof matchMedia === "function" && matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
3905
+ }
3906
+ return { scheme, tokens: out };
3907
+ }
3908
+ function onViewPort(fn, target = window, closed) {
3909
+ const listener = (e) => {
3910
+ if (e.source !== target) return;
3911
+ const data = e.data;
3912
+ if (!data || typeof data !== "object" || data.type !== VIEW_PORT_MESSAGE) return;
3913
+ const port = e.ports?.[0];
3914
+ const meta = data.meta;
3915
+ if (!meta || typeof meta.window !== "number" || typeof meta.extension !== "string" || typeof meta.view !== "string") return;
3916
+ if (meta.closed === true) {
3917
+ closed?.(meta);
3918
+ return;
3919
+ }
3920
+ if (!port) return;
3921
+ fn(port, meta);
3922
+ };
3923
+ target.addEventListener("message", listener);
3924
+ return () => target.removeEventListener("message", listener);
3925
+ }
3926
+ var NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
3927
+ var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
3928
+ var isId = (v) => typeof v === "string" && v !== "";
3929
+ function isJson(v, depth = 0) {
3930
+ if (depth > 64) return false;
3931
+ if (v === null || typeof v === "string" || typeof v === "boolean") return true;
3932
+ if (typeof v === "number") return Number.isFinite(v);
3933
+ if (Array.isArray(v)) return v.every((x) => isJson(x, depth + 1));
3934
+ if (isObj(v)) return Object.getPrototypeOf(v) === Object.prototype && Object.values(v).every((x) => isJson(x, depth + 1));
3935
+ return false;
3936
+ }
3937
+ var bad = (message) => ({ code: "bad-request", message });
3938
+ function argsProblem(op, args) {
3939
+ switch (op) {
3940
+ case "project":
3941
+ case "selection":
3942
+ return null;
3943
+ case "node":
3944
+ if (!isId(args["id"])) return "A node request names the node by its id.";
3945
+ if (args["children"] !== void 0 && typeof args["children"] !== "boolean") return "A node request's children is true or false.";
3946
+ return null;
3947
+ case "media":
3948
+ if (!Array.isArray(args["ids"]) || !args["ids"].every(isId)) return "A media request lists the lines' ids.";
3949
+ return null;
3950
+ case "select":
3951
+ return isId(args["id"]) ? null : "A select request names the node by its id.";
3952
+ case "write": {
3953
+ if (typeof args["label"] !== "string" || args["label"].trim() === "") return "A write has a label, for the undo step.";
3954
+ const edits = args["edits"];
3955
+ if (!Array.isArray(edits)) return "A write lists its edits.";
3956
+ for (const e of edits) {
3957
+ if (!isObj(e) || !isId(e["node"])) return "Each edit names its node by id.";
3958
+ if (typeof e["field"] !== "string" || !NAME.test(e["field"])) return "Each edit names one of the extension's fields, never a path inside one.";
3959
+ if ("value" in e && e["value"] !== void 0 && !isJson(e["value"])) return `The value for ${e["field"]} isn't plain JSON.`;
3960
+ }
3961
+ return null;
3962
+ }
3963
+ }
3964
+ }
3965
+ var OPS = ["project", "node", "selection", "media", "write", "select"];
3966
+ function hostView(port, adapter, opts) {
3967
+ const { meta } = opts;
3968
+ const ext = meta.extension;
3969
+ const allowed = /* @__PURE__ */ new Set([ext, ...opts.reads]);
3970
+ const theme = opts.theme ?? (() => readTheme());
3971
+ const debounceMs = opts.debounceMs ?? 100;
3972
+ let welcomed = false;
3973
+ let closed = false;
3974
+ let pending = null;
3975
+ let timer3;
3976
+ const post = (msg) => {
3977
+ if (!closed) port.postMessage(msg);
3978
+ };
3979
+ const emit = (name, value) => {
3980
+ if (welcomed) post({ t: "event", name, value });
3981
+ };
3982
+ const filterNode = (node2) => {
3983
+ const keptExt = {};
3984
+ for (const [id, data] of Object.entries(node2.ext ?? {})) if (allowed.has(id)) keptExt[id] = data;
3985
+ const base = { id: node2.id, kind: node2.kind, parent: node2.parent, label: node2.label, host: node2.host ?? {}, ext: keptExt };
3986
+ const out = typeof node2.held === "string" ? { ...base, held: node2.held } : base;
3987
+ return node2.children ? { ...out, children: node2.children.map(filterNode) } : out;
3988
+ };
3989
+ const answer = async (op, args) => {
3990
+ switch (op) {
3991
+ case "project":
3992
+ return { value: filterNode(await adapter.project()) };
3993
+ case "node": {
3994
+ const node2 = await adapter.node(args["id"], args["children"] === true);
3995
+ return { value: node2 ? filterNode(node2) : null };
3996
+ }
3997
+ case "selection":
3998
+ return { value: await adapter.selection() };
3999
+ case "media":
4000
+ if (!adapter.media) return { refusal: { code: "unsupported", message: "This app has no recordings or timings for lines." } };
4001
+ return { value: await adapter.media(args["ids"]) };
4002
+ case "select":
4003
+ return { value: await adapter.select(args["id"]) };
4004
+ case "write": {
4005
+ const writable = adapter.writable(ext);
4006
+ if (writable.readOnly) return { refusal: { code: "read-only", message: writable.why ?? "This project is read-only just now." } };
4007
+ const edits = args["edits"].map((e) => "value" in e && e.value !== void 0 ? { node: e.node, field: e.field, value: e.value } : { node: e.node, field: e.field });
4008
+ if (edits.length === 0) return { value: { written: 0 } };
4009
+ if (edits.length > 1) {
4010
+ const projectId = (await adapter.project()).id;
4011
+ const onProject = edits.filter((e) => e.node === projectId).length;
4012
+ if (onProject > 0 && onProject < edits.length) {
4013
+ return { refusal: bad("A write changes the project or other nodes, not both: they undo separately. Make two writes.") };
4014
+ }
4015
+ }
4016
+ const refused = await adapter.write(ext, args["label"], edits);
4017
+ if (refused) return { refusal: refused };
4018
+ return { value: { written: edits.length } };
4019
+ }
4020
+ }
4021
+ };
4022
+ const request = async (msg) => {
4023
+ const id = msg["id"];
4024
+ if (typeof id !== "number" || !Number.isInteger(id)) return;
4025
+ const reply = (r) => post("refusal" in r ? { t: "reply", id, ok: false, refusal: r.refusal } : { t: "reply", id, ok: true, value: r.value });
4026
+ const op = msg["op"];
4027
+ if (typeof op !== "string" || !OPS.includes(op)) {
4028
+ reply({ refusal: bad(`There's no request called ${JSON.stringify(op)}.`) });
4029
+ return;
4030
+ }
4031
+ const args = msg["args"] ?? {};
4032
+ if (!isObj(args)) {
4033
+ reply({ refusal: bad("A request's args are an object.") });
4034
+ return;
4035
+ }
4036
+ const problem = argsProblem(op, args);
4037
+ if (problem) {
4038
+ reply({ refusal: bad(problem) });
4039
+ return;
4040
+ }
4041
+ try {
4042
+ reply(await answer(op, args));
4043
+ } catch (e) {
4044
+ reply({ refusal: { code: "failed", message: `The app couldn't do that: ${e instanceof Error ? e.message : String(e)}` } });
4045
+ }
4046
+ };
4047
+ const hello = (msg) => {
4048
+ const api = msg["api"];
4049
+ if (api !== VIEW_API) {
4050
+ const who = `${opts.names?.extension ?? ext}'s ${opts.names?.view ?? meta.view}`;
4051
+ const message = typeof api === "number" ? `${who} was built for view API ${api}; this app serves ${VIEW_API}.` : `${who} didn't say which view API it was built for; this app serves ${VIEW_API}.`;
4052
+ post({ t: "refused", message });
4053
+ close(false);
4054
+ return;
4055
+ }
4056
+ const welcome = {
4057
+ t: "welcome",
4058
+ api: VIEW_API,
4059
+ host: adapter.host,
4060
+ extension: { id: ext, version: meta.version, ...meta.pinned !== void 0 ? { pinned: meta.pinned } : {} },
4061
+ view: meta.view,
4062
+ settings: adapter.settings(ext),
4063
+ theme: theme(),
4064
+ writable: adapter.writable(ext)
4065
+ };
4066
+ welcomed = true;
4067
+ post(welcome);
4068
+ };
4069
+ const onMessage = (e) => {
4070
+ const msg = e.data;
4071
+ if (closed || !isObj(msg)) return;
4072
+ if (!welcomed) {
4073
+ if (msg["t"] === "hello") hello(msg);
4074
+ return;
4075
+ }
4076
+ if (msg["t"] === "request") void request(msg);
4077
+ else if (msg["t"] === "key" && typeof msg["combo"] === "string") adapter.key?.(msg["combo"]);
4078
+ };
4079
+ port.addEventListener("message", onMessage);
4080
+ port.start();
4081
+ const flush = () => {
4082
+ timer3 = void 0;
4083
+ const nodes = pending;
4084
+ pending = null;
4085
+ if (nodes === null) return;
4086
+ emit("changed", { nodes: nodes === "all" ? "all" : [...nodes] });
4087
+ };
4088
+ function close(sayClosing = true) {
4089
+ if (closed) return;
4090
+ if (sayClosing) emit("closing", {});
4091
+ clearTimeout(timer3);
4092
+ closed = true;
4093
+ port.removeEventListener("message", onMessage);
4094
+ port.close();
4095
+ }
4096
+ return {
4097
+ changed(nodes) {
4098
+ if (closed || !welcomed) return;
4099
+ if (nodes === "all" || pending === "all") pending = "all";
4100
+ else {
4101
+ pending ??= /* @__PURE__ */ new Set();
4102
+ for (const n of nodes) pending.add(n);
4103
+ }
4104
+ if (timer3 === void 0) timer3 = setTimeout(flush, debounceMs);
4105
+ },
4106
+ selection() {
4107
+ if (closed || !welcomed) return;
4108
+ void Promise.resolve().then(() => adapter.selection()).then((s) => emit("selection", s), () => {
4109
+ });
4110
+ },
4111
+ theme() {
4112
+ if (!closed) emit("theme", theme());
4113
+ },
4114
+ writable() {
4115
+ if (!closed) emit("writable", adapter.writable(ext));
4116
+ },
4117
+ close: () => close(true)
4118
+ };
4119
+ }
4120
+ function hostViews(setup, opts = {}) {
4121
+ const handles = /* @__PURE__ */ new Map();
4122
+ const drop = (meta) => {
4123
+ handles.get(meta.window)?.close();
4124
+ handles.delete(meta.window);
4125
+ };
4126
+ const stop = onViewPort((port, meta) => {
4127
+ drop(meta);
4128
+ const s = setup(meta);
4129
+ if (!s) {
4130
+ port.close();
4131
+ return;
4132
+ }
4133
+ handles.set(meta.window, hostView(port, s.adapter, {
4134
+ meta,
4135
+ reads: s.reads,
4136
+ ...s.names !== void 0 ? { names: s.names } : {},
4137
+ ...opts.theme !== void 0 ? { theme: opts.theme } : {},
4138
+ ...opts.debounceMs !== void 0 ? { debounceMs: opts.debounceMs } : {}
4139
+ }));
4140
+ }, opts.target ?? window, drop);
4141
+ const each = (f) => {
4142
+ for (const h of handles.values()) f(h);
4143
+ };
4144
+ return {
4145
+ get size() {
4146
+ return handles.size;
4147
+ },
4148
+ changed: (nodes) => each((h) => h.changed(nodes)),
4149
+ selection: () => each((h) => h.selection()),
4150
+ theme: () => each((h) => h.theme()),
4151
+ writable: () => each((h) => h.writable()),
4152
+ close: () => {
4153
+ each((h) => h.close());
4154
+ handles.clear();
4155
+ },
4156
+ dispose: () => {
4157
+ stop();
4158
+ each((h) => h.close());
4159
+ handles.clear();
4160
+ }
4161
+ };
4162
+ }
4163
+
3841
4164
  exports.EDIT_MENU = EDIT_MENU;
3842
4165
  exports.FILE_MENU = FILE_MENU;
3843
4166
  exports.GO_MENU = GO_MENU;
@@ -3853,7 +4176,11 @@ exports.PROPERTY_TYPES = PROPERTY_TYPES;
3853
4176
  exports.PUBLISH_MENU = PUBLISH_MENU;
3854
4177
  exports.RESERVED_PROPERTY_NAMES = RESERVED_PROPERTY_NAMES;
3855
4178
  exports.REVIEW_MENU = REVIEW_MENU;
4179
+ exports.VIEW_API = VIEW_API;
3856
4180
  exports.VIEW_MENU = VIEW_MENU;
4181
+ exports.VIEW_PORT_CHANNEL = VIEW_PORT_CHANNEL;
4182
+ exports.VIEW_PORT_MESSAGE = VIEW_PORT_MESSAGE;
4183
+ exports.VIEW_THEME_TOKENS = VIEW_THEME_TOKENS;
3857
4184
  exports.askIdentity = askIdentity;
3858
4185
  exports.bindPropertyName = bindPropertyName;
3859
4186
  exports.bindPropertyRef = bindPropertyRef;
@@ -3891,6 +4218,8 @@ exports.gameIdify = gameIdify;
3891
4218
  exports.hideTip = hideTip;
3892
4219
  exports.hintBar = hintBar;
3893
4220
  exports.historyNav = historyNav;
4221
+ exports.hostView = hostView;
4222
+ exports.hostViews = hostViews;
3894
4223
  exports.icon = icon;
3895
4224
  exports.iconBtn = iconBtn;
3896
4225
  exports.iconHtml = iconHtml;
@@ -3925,6 +4254,7 @@ exports.mountPropertyList = mountPropertyList;
3925
4254
  exports.mountSettingsDialog = mountSettingsDialog;
3926
4255
  exports.mountWelcome = mountWelcome;
3927
4256
  exports.moveItem = moveItem;
4257
+ exports.onViewPort = onViewPort;
3928
4258
  exports.openAnchoredPanel = openAnchoredPanel;
3929
4259
  exports.openComments = openComments;
3930
4260
  exports.openContextMenu = openContextMenu;
@@ -3941,6 +4271,7 @@ exports.problemName = problemName;
3941
4271
  exports.propertyNameProblem = propertyNameProblem;
3942
4272
  exports.propertyNameify = propertyNameify;
3943
4273
  exports.propertyRefProblem = propertyRefProblem;
4274
+ exports.readTheme = readTheme;
3944
4275
  exports.relativeTime = relativeTime;
3945
4276
  exports.renderStepperBar = renderStepperBar;
3946
4277
  exports.revalidatePropertyRefs = revalidatePropertyRefs;