@wildwinter/app-shell 0.30.0 → 0.31.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/dist/index.cjs CHANGED
@@ -892,6 +892,7 @@ function isCaseOnlyPropertyName(name) {
892
892
  }
893
893
 
894
894
  // src/property-name-field.ts
895
+ var refSyncs = /* @__PURE__ */ new WeakMap();
895
896
  function propertyNameProblem(name) {
896
897
  if (isValidPropertyName(name)) return void 0;
897
898
  const suggestion = propertyNameify(name);
@@ -938,6 +939,93 @@ function bindPropertyName(input, onCommit, opts = {}) {
938
939
  function firstIllegalPropertyName(host) {
939
940
  return host.querySelector("input.illegal");
940
941
  }
942
+ function nearest(name, known) {
943
+ const n = name.toLowerCase();
944
+ const byPrefix = known.filter((k) => k.toLowerCase().startsWith(n) || n.startsWith(k.toLowerCase()));
945
+ if (byPrefix.length === 1) return byPrefix[0];
946
+ let best;
947
+ let bestScore = 3;
948
+ for (const k of known) {
949
+ const a = k.toLowerCase();
950
+ if (Math.abs(a.length - n.length) > 2) continue;
951
+ let prev = Array.from({ length: n.length + 1 }, (_, i) => i);
952
+ for (let i = 1; i <= a.length; i++) {
953
+ const row = [i];
954
+ for (let j = 1; j <= n.length; j++) {
955
+ row[j] = Math.min(prev[j] + 1, row[j - 1] + 1, prev[j - 1] + (a[i - 1] === n[j - 1] ? 0 : 1));
956
+ }
957
+ prev = row;
958
+ }
959
+ const d = prev[n.length];
960
+ if (d < bestScore) {
961
+ bestScore = d;
962
+ best = k;
963
+ }
964
+ }
965
+ return best;
966
+ }
967
+ function propertyRefProblem(name, opts) {
968
+ const scope = opts.scope ? `@${opts.scope}` : "that scope";
969
+ const folded = name.toLowerCase();
970
+ if (!isValidPropertyName(folded)) {
971
+ const suggestion = propertyNameify(name);
972
+ const tail = suggestion ? ` Try "${suggestion}", or press Tab.` : "";
973
+ if (RESERVED_PROPERTY_NAMES.includes(folded)) return `Cannot be used: "${folded}" is a keyword in expressions.${tail}`;
974
+ if (name.includes("-")) return `Cannot be used: a hyphen reads as subtraction in an expression.${tail}`;
975
+ if (/^[0-9]/.test(name)) return `Cannot be used: a name cannot start with a digit.${tail}`;
976
+ return `Cannot be used: only lower case letters, digits and underscores.${tail}`;
977
+ }
978
+ const known = opts.known();
979
+ if (known.some((k) => k.toLowerCase() === folded)) return void 0;
980
+ const near = nearest(folded, known);
981
+ if (near) return `No ${scope} property is called "${name}". Did you mean "${near}"?`;
982
+ if (known.length === 0) return `No ${scope} properties are declared yet. Declare one above first.`;
983
+ const shown = known.slice(0, 3).map((k) => `"${k}"`).join(", ");
984
+ const more = known.length > 3 ? `, and ${known.length - 3} more` : "";
985
+ return `No ${scope} property is called "${name}". Declared: ${shown}${more}.`;
986
+ }
987
+ function bindPropertyRef(input, onCommit, opts) {
988
+ const list = document.createElement("datalist");
989
+ list.id = `shell-propref-${Math.random().toString(36).slice(2, 9)}`;
990
+ input.setAttribute("list", list.id);
991
+ input.after(list);
992
+ const sync = () => {
993
+ const known = opts.known();
994
+ list.replaceChildren(...known.map((k) => {
995
+ const o = document.createElement("option");
996
+ o.value = k;
997
+ return o;
998
+ }));
999
+ const v = input.value.trim();
1000
+ const problem = v === "" ? void 0 : propertyRefProblem(v, opts);
1001
+ input.classList.toggle("illegal", !!problem);
1002
+ if (problem) input.title = problem;
1003
+ else if (input.title.startsWith("Cannot be used") || input.title.startsWith("No ")) {
1004
+ if (opts.hint) input.title = opts.hint;
1005
+ else input.removeAttribute("title");
1006
+ }
1007
+ };
1008
+ refSyncs.set(input, sync);
1009
+ input.addEventListener("input", () => {
1010
+ onCommit(input.value);
1011
+ sync();
1012
+ });
1013
+ input.addEventListener("keydown", (e) => {
1014
+ if (e.key !== "Tab" || e.shiftKey) return;
1015
+ const typed = input.value.trim();
1016
+ if (typed === "" || isValidPropertyName(typed.toLowerCase())) return;
1017
+ const coerced = propertyNameify(typed);
1018
+ if (!coerced || coerced === input.value) return;
1019
+ e.preventDefault();
1020
+ input.value = coerced;
1021
+ onCommit(coerced);
1022
+ sync();
1023
+ });
1024
+ sync();
1025
+ }
1026
+ function revalidatePropertyRefs(host) {
1027
+ for (const input of host.querySelectorAll("input")) refSyncs.get(input)?.();
1028
+ }
941
1029
 
942
1030
  // src/id-editor.ts
943
1031
  function openGameIdEditor(opts) {
@@ -1611,6 +1699,7 @@ exports.PROPERTY_NAME_HINT = PROPERTY_NAME_HINT;
1611
1699
  exports.RESERVED_PROPERTY_NAMES = RESERVED_PROPERTY_NAMES;
1612
1700
  exports.askIdentity = askIdentity;
1613
1701
  exports.bindPropertyName = bindPropertyName;
1702
+ exports.bindPropertyRef = bindPropertyRef;
1614
1703
  exports.closeAnchoredPanel = closeAnchoredPanel;
1615
1704
  exports.closeWithExit = closeWithExit;
1616
1705
  exports.colourFor = colourFor;
@@ -1648,7 +1737,9 @@ exports.pinButton = pinButton;
1648
1737
  exports.placeAnchored = placeAnchored;
1649
1738
  exports.propertyNameProblem = propertyNameProblem;
1650
1739
  exports.propertyNameify = propertyNameify;
1740
+ exports.propertyRefProblem = propertyRefProblem;
1651
1741
  exports.renderStepperBar = renderStepperBar;
1742
+ exports.revalidatePropertyRefs = revalidatePropertyRefs;
1652
1743
  exports.saveIndicator = saveIndicator;
1653
1744
  exports.showAbout = showAbout;
1654
1745
  exports.staleBar = staleBar;