@nodegx/preview 0.2.7 → 0.2.8
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/backend/cli.js +716 -495
- package/dist/browser/noodl-graph-target.js +92 -92
- package/dist/browser/noodl-preview-in-page.js +32 -29
- package/dist/external/deploy/noodl.deploy.js +1 -1
- package/dist/nodegx-deploy.cjs +566 -58
- package/dist/nodegx-deploy.inputs.json +5 -0
- package/dist/noodl-preview.cjs +656 -86
- package/dist/noodl-preview.inputs.json +4 -0
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/nodegx-deploy.cjs
CHANGED
|
@@ -13786,7 +13786,7 @@ var require_common2 = __commonJS({
|
|
|
13786
13786
|
exports2.ownProp = ownProp;
|
|
13787
13787
|
exports2.makeAbs = makeAbs;
|
|
13788
13788
|
exports2.finish = finish3;
|
|
13789
|
-
exports2.mark =
|
|
13789
|
+
exports2.mark = mark2;
|
|
13790
13790
|
exports2.isIgnored = isIgnored;
|
|
13791
13791
|
exports2.childrenIgnored = childrenIgnored;
|
|
13792
13792
|
function ownProp(obj, field2) {
|
|
@@ -13922,7 +13922,7 @@ var require_common2 = __commonJS({
|
|
|
13922
13922
|
});
|
|
13923
13923
|
self2.found = all;
|
|
13924
13924
|
}
|
|
13925
|
-
function
|
|
13925
|
+
function mark2(self2, p) {
|
|
13926
13926
|
var abs = makeAbs(self2, p);
|
|
13927
13927
|
var c = self2.cache[abs];
|
|
13928
13928
|
var m = p;
|
|
@@ -19380,10 +19380,10 @@ var require_nodescope = __commonJS({
|
|
|
19380
19380
|
} else {
|
|
19381
19381
|
const parameters = nodeModel.parameters;
|
|
19382
19382
|
var inputNames = Object.keys(parameters);
|
|
19383
|
-
const
|
|
19383
|
+
const RUN_ON_CHANGE_PREFIX5 = "runOnChange-";
|
|
19384
19384
|
const metadata = this.context.nodeRegister.hasNode(node.name) ? this.context.nodeRegister.getNodeMetadata(node.name) : void 0;
|
|
19385
19385
|
const priorityOf = function(name2) {
|
|
19386
|
-
if (name2.indexOf(
|
|
19386
|
+
if (name2.indexOf(RUN_ON_CHANGE_PREFIX5) === 0) return Number.MAX_SAFE_INTEGER;
|
|
19387
19387
|
const input2 = metadata ? metadata.inputs[name2] : void 0;
|
|
19388
19388
|
return input2 ? input2.inputPriority : 0;
|
|
19389
19389
|
};
|
|
@@ -23328,7 +23328,10 @@ var require_nodedefinition = __commonJS({
|
|
|
23328
23328
|
node.registerOutput(name2, {
|
|
23329
23329
|
getter: function() {
|
|
23330
23330
|
return void 0;
|
|
23331
|
-
}
|
|
23331
|
+
},
|
|
23332
|
+
onFirstConnectionAdded: output.onFirstConnectionAdded,
|
|
23333
|
+
onLastConnectionRemoved: output.onLastConnectionRemoved
|
|
23334
|
+
// a signal keeps its wire hooks (P121-TWO003)
|
|
23332
23335
|
});
|
|
23333
23336
|
} else {
|
|
23334
23337
|
node.registerOutput(name2, output);
|
|
@@ -27291,6 +27294,7 @@ var require_simplejavascript = __commonJS({
|
|
|
27291
27294
|
return names2;
|
|
27292
27295
|
}
|
|
27293
27296
|
function publishHeldWrites(node) {
|
|
27297
|
+
sendHeldChanges(node);
|
|
27294
27298
|
node._internal.heldWrites.forEach((prop) => {
|
|
27295
27299
|
node.registerOutputIfNeeded("out-" + prop);
|
|
27296
27300
|
node.flagOutputDirty("out-" + prop);
|
|
@@ -27334,7 +27338,7 @@ var require_simplejavascript = __commonJS({
|
|
|
27334
27338
|
if (value2 !== this._internal.outputValues[prop]) {
|
|
27335
27339
|
this.registerOutputIfNeeded("out-" + prop);
|
|
27336
27340
|
this._internal.outputValues[prop] = value2;
|
|
27337
|
-
this
|
|
27341
|
+
holdChange(this, prop);
|
|
27338
27342
|
this._internal.heldWrites.delete(prop);
|
|
27339
27343
|
} else {
|
|
27340
27344
|
this._internal.heldWrites.add(prop);
|
|
@@ -27513,7 +27517,7 @@ var require_simplejavascript = __commonJS({
|
|
|
27513
27517
|
});
|
|
27514
27518
|
},
|
|
27515
27519
|
runScript: async function() {
|
|
27516
|
-
const func = this._internal.func;
|
|
27520
|
+
const func = sendingChangesAfterTheSyncPart(this, this._internal.func);
|
|
27517
27521
|
const tokens = this._internal.pendingRunOutcomes || [];
|
|
27518
27522
|
this._internal.pendingRunOutcomes = void 0;
|
|
27519
27523
|
if (func === void 0) {
|
|
@@ -27955,6 +27959,32 @@ var require_simplejavascript = __commonJS({
|
|
|
27955
27959
|
});
|
|
27956
27960
|
}
|
|
27957
27961
|
};
|
|
27962
|
+
function holdChange(node, prop) {
|
|
27963
|
+
const internal2 = node._internal;
|
|
27964
|
+
if (!internal2.heldChanges) internal2.heldChanges = /* @__PURE__ */ new Set();
|
|
27965
|
+
internal2.heldChanges.add(prop);
|
|
27966
|
+
if (internal2.heldChangesQueued) return;
|
|
27967
|
+
internal2.heldChangesQueued = true;
|
|
27968
|
+
Promise.resolve().then(() => sendHeldChanges(node));
|
|
27969
|
+
}
|
|
27970
|
+
function sendHeldChanges(node) {
|
|
27971
|
+
const internal2 = node._internal;
|
|
27972
|
+
internal2.heldChangesQueued = false;
|
|
27973
|
+
const held = internal2.heldChanges;
|
|
27974
|
+
if (!held || held.size === 0 || node._deleted) return;
|
|
27975
|
+
internal2.heldChanges = /* @__PURE__ */ new Set();
|
|
27976
|
+
held.forEach((prop) => node.flagOutputDirty("out-" + prop));
|
|
27977
|
+
}
|
|
27978
|
+
function sendingChangesAfterTheSyncPart(node, func) {
|
|
27979
|
+
if (func === void 0) return void 0;
|
|
27980
|
+
return function(...args) {
|
|
27981
|
+
try {
|
|
27982
|
+
return func.apply(this, args);
|
|
27983
|
+
} finally {
|
|
27984
|
+
sendHeldChanges(node);
|
|
27985
|
+
}
|
|
27986
|
+
};
|
|
27987
|
+
}
|
|
27958
27988
|
module2.exports = SimpleJavascriptNodeModule;
|
|
27959
27989
|
}
|
|
27960
27990
|
});
|
|
@@ -69189,6 +69219,133 @@ var init_tooltips = __esm({
|
|
|
69189
69219
|
}
|
|
69190
69220
|
});
|
|
69191
69221
|
|
|
69222
|
+
// ../noodl-viewer-react/src/keyboard-click.ts
|
|
69223
|
+
function holdsPressable(el) {
|
|
69224
|
+
return el.querySelector(PRESSABLE_INSIDE) !== null;
|
|
69225
|
+
}
|
|
69226
|
+
function clickIsWired(node) {
|
|
69227
|
+
if (!node.hasOutput("onClick")) return false;
|
|
69228
|
+
return reachesSomething(node.getOutput("onClick"), 0);
|
|
69229
|
+
}
|
|
69230
|
+
function reachesSomething(output, depth) {
|
|
69231
|
+
if (!output.hasConnections()) return false;
|
|
69232
|
+
if (!Array.isArray(output.connections)) return true;
|
|
69233
|
+
return output.connections.some((c) => !goesNowhere(c, depth));
|
|
69234
|
+
}
|
|
69235
|
+
function goesNowhere(connection, depth) {
|
|
69236
|
+
const target = connection.node;
|
|
69237
|
+
if (!target || target.name !== "Component Outputs" || typeof connection.inputPortName !== "string") return false;
|
|
69238
|
+
const owner = target.nodeScope?.componentOwner;
|
|
69239
|
+
if (!owner || depth > 16 || owner._internal?.creatorCallbacks) return false;
|
|
69240
|
+
if (typeof owner.hasOutput !== "function" || typeof owner.getOutput !== "function") return false;
|
|
69241
|
+
if (!owner.hasOutput(connection.inputPortName)) return true;
|
|
69242
|
+
return !reachesSomething(owner.getOutput(connection.inputPortName), depth + 1);
|
|
69243
|
+
}
|
|
69244
|
+
function isNativelyInteractive(el) {
|
|
69245
|
+
const tag = el.tagName;
|
|
69246
|
+
if (tag === "BUTTON" || tag === "INPUT" || tag === "SELECT" || tag === "TEXTAREA" || tag === "SUMMARY") return true;
|
|
69247
|
+
if ((tag === "A" || tag === "AREA") && el.hasAttribute("href")) return true;
|
|
69248
|
+
if ((tag === "VIDEO" || tag === "AUDIO") && el.hasAttribute("controls")) return true;
|
|
69249
|
+
return el.isContentEditable === true;
|
|
69250
|
+
}
|
|
69251
|
+
function isSpace(e) {
|
|
69252
|
+
return e.key === " " || e.key === "Spacebar";
|
|
69253
|
+
}
|
|
69254
|
+
function isOwnKey(el, e) {
|
|
69255
|
+
return e.target === el && !e.defaultPrevented && !e.altKey && !e.ctrlKey && !e.metaKey && !e.isComposing;
|
|
69256
|
+
}
|
|
69257
|
+
function mark(el) {
|
|
69258
|
+
let spaceArmed = false;
|
|
69259
|
+
const onKeyDown2 = (e) => {
|
|
69260
|
+
if (!isOwnKey(el, e)) return;
|
|
69261
|
+
if (e.key === "Enter") {
|
|
69262
|
+
e.preventDefault();
|
|
69263
|
+
if (!e.repeat) el.click();
|
|
69264
|
+
} else if (isSpace(e)) {
|
|
69265
|
+
e.preventDefault();
|
|
69266
|
+
if (!e.repeat) spaceArmed = true;
|
|
69267
|
+
}
|
|
69268
|
+
};
|
|
69269
|
+
const onKeyUp = (e) => {
|
|
69270
|
+
if (!isSpace(e)) return;
|
|
69271
|
+
const armed6 = spaceArmed;
|
|
69272
|
+
spaceArmed = false;
|
|
69273
|
+
if (armed6 && e.target === el && !e.defaultPrevented) el.click();
|
|
69274
|
+
};
|
|
69275
|
+
const m = { el, added: [], onKeyDown: onKeyDown2, onKeyUp, observer: null };
|
|
69276
|
+
if (isNativelyInteractive(el)) return m;
|
|
69277
|
+
refresh(m);
|
|
69278
|
+
if (typeof MutationObserver !== "undefined") {
|
|
69279
|
+
m.observer = new MutationObserver(() => refresh(m));
|
|
69280
|
+
m.observer.observe(el, {
|
|
69281
|
+
childList: true,
|
|
69282
|
+
subtree: true,
|
|
69283
|
+
attributes: true,
|
|
69284
|
+
attributeFilter: ["tabindex", "contenteditable", "href", "controls", "type"]
|
|
69285
|
+
});
|
|
69286
|
+
}
|
|
69287
|
+
return m;
|
|
69288
|
+
}
|
|
69289
|
+
function refresh(m) {
|
|
69290
|
+
const { el } = m;
|
|
69291
|
+
const want = !holdsPressable(el);
|
|
69292
|
+
if (want && !m.added.length) {
|
|
69293
|
+
if (!el.hasAttribute("tabindex")) {
|
|
69294
|
+
el.setAttribute("tabindex", "0");
|
|
69295
|
+
m.added.push("tabindex");
|
|
69296
|
+
}
|
|
69297
|
+
if (!el.hasAttribute("role")) {
|
|
69298
|
+
el.setAttribute("role", "button");
|
|
69299
|
+
m.added.push("role");
|
|
69300
|
+
}
|
|
69301
|
+
if (m.added.length) {
|
|
69302
|
+
el.setAttribute(KEYBOARD_CLICK_ATTRIBUTE, "");
|
|
69303
|
+
m.added.push(KEYBOARD_CLICK_ATTRIBUTE);
|
|
69304
|
+
el.addEventListener("keydown", m.onKeyDown);
|
|
69305
|
+
el.addEventListener("keyup", m.onKeyUp);
|
|
69306
|
+
}
|
|
69307
|
+
} else if (!want && m.added.length) {
|
|
69308
|
+
giveBack(m);
|
|
69309
|
+
}
|
|
69310
|
+
}
|
|
69311
|
+
function giveBack(m) {
|
|
69312
|
+
for (const name2 of m.added) m.el.removeAttribute(name2);
|
|
69313
|
+
m.added = [];
|
|
69314
|
+
m.el.removeEventListener("keydown", m.onKeyDown);
|
|
69315
|
+
m.el.removeEventListener("keyup", m.onKeyUp);
|
|
69316
|
+
}
|
|
69317
|
+
function unmark(m) {
|
|
69318
|
+
m.observer?.disconnect();
|
|
69319
|
+
giveBack(m);
|
|
69320
|
+
}
|
|
69321
|
+
function syncKeyboardClick(node) {
|
|
69322
|
+
const el = typeof node.getDOMElement === "function" ? node.getDOMElement() : null;
|
|
69323
|
+
const want = el && clickIsWired(node) ? el : null;
|
|
69324
|
+
const was = marked.get(node);
|
|
69325
|
+
if (was && was.el === want) return;
|
|
69326
|
+
if (was) {
|
|
69327
|
+
unmark(was);
|
|
69328
|
+
marked.delete(node);
|
|
69329
|
+
}
|
|
69330
|
+
if (want) marked.set(node, mark(want));
|
|
69331
|
+
}
|
|
69332
|
+
var KEYBOARD_CLICK_ATTRIBUTE, PRESSABLE_INSIDE, marked, KEYBOARD_CLICK_HOOKS;
|
|
69333
|
+
var init_keyboard_click = __esm({
|
|
69334
|
+
"../noodl-viewer-react/src/keyboard-click.ts"() {
|
|
69335
|
+
KEYBOARD_CLICK_ATTRIBUTE = "data-ndl-keyboard-click";
|
|
69336
|
+
PRESSABLE_INSIDE = 'button, input:not([type="hidden"]), select, textarea, summary, a[href], area[href], video[controls], audio[controls], [contenteditable=""], [contenteditable="true"], [tabindex]:not([tabindex="-1"])';
|
|
69337
|
+
marked = /* @__PURE__ */ new WeakMap();
|
|
69338
|
+
KEYBOARD_CLICK_HOOKS = {
|
|
69339
|
+
onFirstConnectionAdded() {
|
|
69340
|
+
syncKeyboardClick(this);
|
|
69341
|
+
},
|
|
69342
|
+
onLastConnectionRemoved() {
|
|
69343
|
+
syncKeyboardClick(this);
|
|
69344
|
+
}
|
|
69345
|
+
};
|
|
69346
|
+
}
|
|
69347
|
+
});
|
|
69348
|
+
|
|
69192
69349
|
// ../noodl-viewer-react/src/node-shared-port-definitions.ts
|
|
69193
69350
|
function humanCorner(suffix) {
|
|
69194
69351
|
return suffix.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
@@ -69456,6 +69613,7 @@ var init_node_shared_port_definitions = __esm({
|
|
|
69456
69613
|
init_fontloader();
|
|
69457
69614
|
init_media_source();
|
|
69458
69615
|
init_tooltips();
|
|
69616
|
+
init_keyboard_click();
|
|
69459
69617
|
SIZE_MODES = ["explicit", "contentWidth", "contentHeight", "contentSize"];
|
|
69460
69618
|
node_shared_port_definitions_default = {
|
|
69461
69619
|
addInputProps: addInputProps2,
|
|
@@ -69963,7 +70121,9 @@ var init_node_shared_port_definitions = __esm({
|
|
|
69963
70121
|
description: "Fires when this element is clicked or tapped",
|
|
69964
70122
|
group: "Events",
|
|
69965
70123
|
type: "signal",
|
|
69966
|
-
propPath: "pointer"
|
|
70124
|
+
propPath: "pointer",
|
|
70125
|
+
...KEYBOARD_CLICK_HOOKS
|
|
70126
|
+
// P121-TWO003-GROUPKEYBOARD: wired, the element is a button to Tab and a screen reader
|
|
69967
70127
|
},
|
|
69968
70128
|
pointerDown: {
|
|
69969
70129
|
displayName: "Pointer Down",
|
|
@@ -73684,6 +73844,7 @@ function createNodeFromReactComponent(def2) {
|
|
|
73684
73844
|
}
|
|
73685
73845
|
if (element) syncDataAttributesOnElement(this);
|
|
73686
73846
|
if (element) elementChanged(this, element);
|
|
73847
|
+
syncKeyboardClick(this);
|
|
73687
73848
|
},
|
|
73688
73849
|
/**
|
|
73689
73850
|
* NDA-012 (Visual) check A3 — run an imperative action on the inner React component,
|
|
@@ -74110,6 +74271,7 @@ var init_react_component_node = __esm({
|
|
|
74110
74271
|
init_mergedeep();
|
|
74111
74272
|
init_node_shared_port_definitions();
|
|
74112
74273
|
init_node_transitions();
|
|
74274
|
+
init_keyboard_click();
|
|
74113
74275
|
ICON_SOURCE_DIAGNOSTIC = "visual/icon-source-not-an-icon";
|
|
74114
74276
|
DATA_ATTRIBUTE_NAME_PATTERN = "^data-[a-z0-9][a-z0-9._-]*$";
|
|
74115
74277
|
DATA_ATTRIBUTE_NAME = new RegExp(DATA_ATTRIBUTE_NAME_PATTERN);
|
|
@@ -80880,6 +81042,38 @@ function _typeName(t) {
|
|
|
80880
81042
|
if (typeof t === "object") return t.name;
|
|
80881
81043
|
else return t;
|
|
80882
81044
|
}
|
|
81045
|
+
function isInsideRow(node, row) {
|
|
81046
|
+
if (node === row) return true;
|
|
81047
|
+
let owner = (node.parentNodeScope || node.nodeScope)?.componentOwner;
|
|
81048
|
+
for (let depth = 0; owner && depth < 64; depth++) {
|
|
81049
|
+
if (owner === row) return true;
|
|
81050
|
+
const next2 = (owner.parentNodeScope || owner.nodeScope)?.componentOwner;
|
|
81051
|
+
if (next2 === owner) return false;
|
|
81052
|
+
owner = next2;
|
|
81053
|
+
}
|
|
81054
|
+
return false;
|
|
81055
|
+
}
|
|
81056
|
+
function settleRow(context, row) {
|
|
81057
|
+
const self2 = row;
|
|
81058
|
+
for (let pass = 0; pass < 10; pass++) {
|
|
81059
|
+
const dirty = context._dirtyNodes;
|
|
81060
|
+
if (!Array.isArray(dirty)) return;
|
|
81061
|
+
const mine = [];
|
|
81062
|
+
for (let i2 = 0; i2 < dirty.length; i2++) {
|
|
81063
|
+
const node = dirty[i2];
|
|
81064
|
+
if (node && node._dirty && !node._deleted && mine.indexOf(node) === -1 && isInsideRow(node, self2))
|
|
81065
|
+
mine.push(node);
|
|
81066
|
+
}
|
|
81067
|
+
if (mine.length === 0) return;
|
|
81068
|
+
for (let i2 = 0; i2 < mine.length; i2++) {
|
|
81069
|
+
try {
|
|
81070
|
+
mine[i2].update();
|
|
81071
|
+
} catch (e) {
|
|
81072
|
+
console.error(e);
|
|
81073
|
+
}
|
|
81074
|
+
}
|
|
81075
|
+
}
|
|
81076
|
+
}
|
|
80883
81077
|
var import_react16, import_runtime4, import_collection, import_model5, ITEMS_DIAGNOSTIC, defaultDynamicScript, ForEachDefinition, defaultMapCode, ForEachModule, foreach_default;
|
|
80884
81078
|
var init_foreach = __esm({
|
|
80885
81079
|
"../noodl-viewer-react/src/nodes/std-library/data/foreach.tsx"() {
|
|
@@ -81253,6 +81447,7 @@ var init_foreach = __esm({
|
|
|
81253
81447
|
}
|
|
81254
81448
|
}
|
|
81255
81449
|
};
|
|
81450
|
+
settleRow(this.context, itemNode);
|
|
81256
81451
|
const forEachActions = itemNode.nodeScope.getNodesWithType("For Each Actions");
|
|
81257
81452
|
for (let j = 0; j < forEachActions.length; j++) {
|
|
81258
81453
|
forEachActions[j].signalAdded();
|
|
@@ -90070,10 +90265,16 @@ function Text(props) {
|
|
|
90070
90265
|
...pointerProps(props),
|
|
90071
90266
|
style: style2
|
|
90072
90267
|
},
|
|
90073
|
-
|
|
90268
|
+
style2.textOverflow === "ellipsis" ? (
|
|
90269
|
+
// P18-S112-FLEXELLIPSIS — the Text is `display: flex` (its default CSS, which Text Vertical Align's `alignItems`
|
|
90270
|
+
// needs), so its words were an anonymous flex item and `text-overflow` on the element drew no "…": Ellipsis
|
|
90271
|
+
// looked the same as Clip. The words go in a block of their own that may shrink to the box and draws the "…";
|
|
90272
|
+
// the element keeps its flex, so both alignments still place them.
|
|
90273
|
+
/* @__PURE__ */ import_react22.default.createElement("span", { style: { ...ELLIPSIS_LINE, overflow: style2.overflow } }, renderableText(props.text))
|
|
90274
|
+
) : renderableText(props.text)
|
|
90074
90275
|
);
|
|
90075
90276
|
}
|
|
90076
|
-
var import_react22;
|
|
90277
|
+
var import_react22, ELLIPSIS_LINE;
|
|
90077
90278
|
var init_Text = __esm({
|
|
90078
90279
|
"../noodl-viewer-react/src/components/visual/Text/Text.tsx"() {
|
|
90079
90280
|
import_react22 = __toESM(require_react());
|
|
@@ -90081,6 +90282,7 @@ var init_Text = __esm({
|
|
|
90081
90282
|
init_layout();
|
|
90082
90283
|
init_pointerlisteners();
|
|
90083
90284
|
init_noodl_root_ref();
|
|
90285
|
+
ELLIPSIS_LINE = { minWidth: 0, textOverflow: "ellipsis", whiteSpace: "nowrap" };
|
|
90084
90286
|
}
|
|
90085
90287
|
});
|
|
90086
90288
|
|
|
@@ -90454,7 +90656,6 @@ var init_Video = __esm({
|
|
|
90454
90656
|
...videoProps,
|
|
90455
90657
|
playsInline: true,
|
|
90456
90658
|
src,
|
|
90457
|
-
...pointerProps(this.props),
|
|
90458
90659
|
ref: (video) => {
|
|
90459
90660
|
this.video = video;
|
|
90460
90661
|
this.props.innerRef(video);
|
|
@@ -90473,7 +90674,7 @@ var init_Video = __esm({
|
|
|
90473
90674
|
this.canPlay = false;
|
|
90474
90675
|
}
|
|
90475
90676
|
setSourceObject(src) {
|
|
90476
|
-
if (this.video.srcObject !== src) {
|
|
90677
|
+
if (this.video && this.video.srcObject !== src) {
|
|
90477
90678
|
this.video.srcObject = src;
|
|
90478
90679
|
this.canPlay = false;
|
|
90479
90680
|
}
|
|
@@ -90579,6 +90780,7 @@ var init_Video = __esm({
|
|
|
90579
90780
|
CachedVideo,
|
|
90580
90781
|
{
|
|
90581
90782
|
...props.dom,
|
|
90783
|
+
...pointerProps(this.props),
|
|
90582
90784
|
className: withDefaultsClass(props.className, defaultsClass),
|
|
90583
90785
|
style: style2,
|
|
90584
90786
|
innerRef: (video) => {
|
|
@@ -97687,6 +97889,7 @@ var init_parentcomponentobject = __esm({
|
|
|
97687
97889
|
*/
|
|
97688
97890
|
nodeScopeDidInitialize() {
|
|
97689
97891
|
this.context.scheduleAfterUpdate(() => {
|
|
97892
|
+
if (this._deleted) return;
|
|
97690
97893
|
this._internal.resolutionIsLoud = true;
|
|
97691
97894
|
this.onComponentStateNodesChanged();
|
|
97692
97895
|
});
|
|
@@ -102063,7 +102266,7 @@ var init_signinwith = __esm({
|
|
|
102063
102266
|
const tokens = [this.beginOutcome()];
|
|
102064
102267
|
if (state2.succeeded) {
|
|
102065
102268
|
this._internal.notice = state2.notice;
|
|
102066
|
-
this._internal.error =
|
|
102269
|
+
this._internal.error = "";
|
|
102067
102270
|
this.flagOutputDirty("notice");
|
|
102068
102271
|
this.flagOutputDirty("error");
|
|
102069
102272
|
reportOutcomes(this, tokens, "done");
|
|
@@ -164973,7 +165176,7 @@ var node_catalog_default = {
|
|
|
164973
165176
|
"always: the size it holds now, a bare number read in the unit it held; drawn at the next paint"
|
|
164974
165177
|
],
|
|
164975
165178
|
visible: [
|
|
164976
|
-
"always: shown or hidden at
|
|
165179
|
+
"always: shown or hidden at once, its space kept; hidden, a Focus does not reach it"
|
|
164977
165180
|
]
|
|
164978
165181
|
},
|
|
164979
165182
|
warnings: {
|
|
@@ -167158,7 +167361,7 @@ var node_catalog_default = {
|
|
|
167158
167361
|
"always: the page, or the scrolling Group holding it, scrolls until the box can be seen"
|
|
167159
167362
|
],
|
|
167160
167363
|
visible: [
|
|
167161
|
-
"always: shown or hidden at
|
|
167364
|
+
"always: shown or hidden at once, its space kept; hidden, a Focus does not reach it"
|
|
167162
167365
|
]
|
|
167163
167366
|
},
|
|
167164
167367
|
warnings: {
|
|
@@ -169770,7 +169973,7 @@ var node_catalog_default = {
|
|
|
169770
169973
|
"always: the size it holds now, a bare number read in the unit it held; drawn at the next paint"
|
|
169771
169974
|
],
|
|
169772
169975
|
visible: [
|
|
169773
|
-
"always: shown or hidden at
|
|
169976
|
+
"always: shown or hidden at once, its space kept; hidden, a Focus does not reach it"
|
|
169774
169977
|
]
|
|
169775
169978
|
},
|
|
169776
169979
|
warnings: {
|
|
@@ -171952,7 +172155,7 @@ var node_catalog_default = {
|
|
|
171952
172155
|
"always: the page, or the scrolling Group holding it, scrolls until the button can be seen"
|
|
171953
172156
|
],
|
|
171954
172157
|
visible: [
|
|
171955
|
-
"always: shown or hidden at
|
|
172158
|
+
"always: shown or hidden at once, its space kept; hidden, it takes no click and a Focus does not reach it"
|
|
171956
172159
|
]
|
|
171957
172160
|
},
|
|
171958
172161
|
warnings: {
|
|
@@ -174357,7 +174560,7 @@ var node_catalog_default = {
|
|
|
174357
174560
|
"always: the size it holds now, a bare number read in the unit it held; drawn at the next paint"
|
|
174358
174561
|
],
|
|
174359
174562
|
visible: [
|
|
174360
|
-
"always: shown or hidden at
|
|
174563
|
+
"always: shown or hidden at once, its space kept; hidden, a Focus does not reach it"
|
|
174361
174564
|
]
|
|
174362
174565
|
},
|
|
174363
174566
|
warnings: {
|
|
@@ -177227,7 +177430,7 @@ var node_catalog_default = {
|
|
|
177227
177430
|
"always: the size it holds now, a bare number read in the unit it held; drawn at the next paint"
|
|
177228
177431
|
],
|
|
177229
177432
|
visible: [
|
|
177230
|
-
"always: shown or hidden at
|
|
177433
|
+
"always: shown or hidden at once, its space kept; hidden, a Focus does not reach it"
|
|
177231
177434
|
],
|
|
177232
177435
|
opacity: [
|
|
177233
177436
|
"always: how see-through it is; at exactly 0 it takes no pointer, and an act goes to what is behind it"
|
|
@@ -182069,7 +182272,7 @@ var node_catalog_default = {
|
|
|
182069
182272
|
},
|
|
182070
182273
|
isSignal: false,
|
|
182071
182274
|
default: false,
|
|
182072
|
-
description: "Whether a request with no valid session token is allowed to run this function at all"
|
|
182275
|
+
description: "Whether a request with no valid session token is allowed to run this function at all. A run the app starts itself \u2014 a schedule, a row change, a run from Studio \u2014 is always allowed and sees nobody signed in; a webhook is a request like any other"
|
|
182073
182276
|
},
|
|
182074
182277
|
{
|
|
182075
182278
|
name: "params",
|
|
@@ -187095,8 +187298,8 @@ function renderMarkdown(md) {
|
|
|
187095
187298
|
continue;
|
|
187096
187299
|
}
|
|
187097
187300
|
if (lines.every((l) => /^\s*>\s?/.test(l))) {
|
|
187098
|
-
const
|
|
187099
|
-
html.push(`<blockquote>${renderMarkdown(
|
|
187301
|
+
const quoted2 = lines.map((l) => l.replace(/^\s*>\s?/, "")).join("\n");
|
|
187302
|
+
html.push(`<blockquote>${renderMarkdown(quoted2)}</blockquote>`);
|
|
187100
187303
|
continue;
|
|
187101
187304
|
}
|
|
187102
187305
|
if (lines.every((l) => /^\s*[-*]\s+/.test(l))) {
|
|
@@ -199194,7 +199397,10 @@ var invocations = [
|
|
|
199194
199397
|
{ headers: { "X-Parse-Session-Token": "t1", origin: "null", host: "h" } },
|
|
199195
199398
|
{ headers: { "x-parse-session-token": "t1" }, body: '{"a":null}', at: 100 },
|
|
199196
199399
|
{ headers: { host: "internal:8577", "x-forwarded-host": "public.example.com", "x-forwarded-proto": "https, http" }, body: '{"total":"7"}' },
|
|
199197
|
-
{ headers: { origin: ["https://app.example.com/", "https://b.example"], "x-forwarded-proto": "jAvascript", host: "h" }, body: "{}" }
|
|
199400
|
+
{ headers: { origin: ["https://app.example.com/", "https://b.example"], "x-forwarded-proto": "jAvascript", host: "h" }, body: "{}" },
|
|
199401
|
+
{ headers: {}, body: '{"total":3}', startedByApp: true },
|
|
199402
|
+
{ headers: { "x-parse-session-token": "t1" }, body: "{}", startedByApp: true },
|
|
199403
|
+
{ headers: { startedbyapp: "true" }, body: '{"startedByApp":true}' }
|
|
199198
199404
|
];
|
|
199199
199405
|
var CLOUD = { appId: "app", masterKey: "mk", endpoint: "https://be.example" };
|
|
199200
199406
|
var HOSTS3 = [{}];
|
|
@@ -199205,7 +199411,7 @@ for (const invocation of invocations) {
|
|
|
199205
199411
|
HOSTS3.push({ invocation: invocations[0], cloudServices: { ...CLOUD, runId: "run-1" } });
|
|
199206
199412
|
var CloudRequest = defineNode({
|
|
199207
199413
|
type: "noodl.cloud.request",
|
|
199208
|
-
version:
|
|
199414
|
+
version: 4,
|
|
199209
199415
|
changes: [
|
|
199210
199416
|
{
|
|
199211
199417
|
version: 2,
|
|
@@ -199218,6 +199424,12 @@ var CloudRequest = defineNode({
|
|
|
199218
199424
|
says: "User Id is text (it carries the id of the person the session token resolved to). It was declared a flag, so a wire into a Number read the id as NaN and the canvas offered it to a signal input",
|
|
199219
199425
|
row: "P107-S97-USERIDBOOLEAN (the Beta sweep s6)",
|
|
199220
199426
|
migration: { none: "No saved wire stops connecting: measured 106 wires from User Id in 13,839 project files, 70 into script inputs (any type), 35 into text ports, 1 into a record field, none into a signal" }
|
|
199427
|
+
},
|
|
199428
|
+
{
|
|
199429
|
+
version: 4,
|
|
199430
|
+
says: "A run the app starts itself \u2014 a schedule, a row change, a run from Studio \u2014 is not refused when Allow Unauthenticated is off: it runs with nobody signed in. It was refused, so a weekly job could only run a function opened to everyone. A webhook and a direct call are refused as before",
|
|
199431
|
+
row: "P127-BKO006-SCHEDULEAUTH (Richard ruled 2026-10-27: a run the app starts itself counts as the app)",
|
|
199432
|
+
migration: { none: "Nothing a project stores changes: only a run the backend marks as started by the app gets a different answer, and it gets the one its author had to open the function to everyone to get" }
|
|
199221
199433
|
}
|
|
199222
199434
|
],
|
|
199223
199435
|
source: "packages/noodl-viewer-cloud/src/nodes/cloud/request.ts; packages/noodl-viewer-cloud/src/nodes/cloud/requestContract.ts; packages/noodl-viewer-cloud/src/nodes/cloud/requestOrigin.ts; packages/noodl-viewer-cloud/src/services/userservice.js",
|
|
@@ -199244,7 +199456,7 @@ var CloudRequest = defineNode({
|
|
|
199244
199456
|
state: { contract: {}, authenticated: void 0, authUserId: void 0, origin: void 0, requestParameters: {}, waiting: void 0 },
|
|
199245
199457
|
inputs: {
|
|
199246
199458
|
// :154-163 — held raw, read when the request comes and again when its lookup fails
|
|
199247
|
-
allowNoAuth: { type: "boolean", default: false, coerce: "none", displayName: "Allow Unauthenticated", group: "General", description: "Whether a request with no valid session token is allowed to run this function at all", examples: [true, false, "false", 1] },
|
|
199459
|
+
allowNoAuth: { type: "boolean", default: false, coerce: "none", displayName: "Allow Unauthenticated", group: "General", description: "Whether a request with no valid session token is allowed to run this function at all. A run the app starts itself \u2014 a schedule, a row change, a run from Studio \u2014 is always allowed and sees nobody signed in; a webhook is a request like any other", examples: [true, false, "false", 1] },
|
|
199248
199460
|
// :164-172 — held raw
|
|
199249
199461
|
params: { type: "stringlist", coerce: "none", editOnly: true, group: "Parameters", description: "Names to pull out of the request body, each becoming an output \u2014 and a Type, Required and Default row to declare what it is", examples: ["total", "total,a", "a,0", "0,1", ""] }
|
|
199250
199462
|
},
|
|
@@ -199291,7 +199503,7 @@ var CloudRequest = defineNode({
|
|
|
199291
199503
|
}
|
|
199292
199504
|
const waiting = { params, headers: req.headers };
|
|
199293
199505
|
if (!token) {
|
|
199294
|
-
if (!i2.allowNoAuth) return { send: [], answers: [{ op: "refuse", reason: UNAUTHENTICATED }] };
|
|
199506
|
+
if (!i2.allowNoAuth && req.startedByApp !== true) return { send: [], answers: [{ op: "refuse", reason: UNAUTHENTICATED }] };
|
|
199295
199507
|
return proceed(s, params, req.headers, i2, w, { set: {}, send: [] });
|
|
199296
199508
|
}
|
|
199297
199509
|
const cloud = w.hostCloud();
|
|
@@ -203105,6 +203317,18 @@ function growChange(version) {
|
|
|
203105
203317
|
};
|
|
203106
203318
|
}
|
|
203107
203319
|
|
|
203320
|
+
// ../nodegx-node-spec/src/nodes/visible-at-once.ts
|
|
203321
|
+
function visibleAtOnceChange(version) {
|
|
203322
|
+
return {
|
|
203323
|
+
version,
|
|
203324
|
+
says: "No app does anything differently. Visible shows or hides the control at once: a Focus sent in the same moment as Visible going on reaches it (Focused fires), and one sent as Visible goes off does nothing. The version before said Visible waited for the next paint, so it lost the first Focus and kept the second; the runtime never did either",
|
|
203325
|
+
row: "BETA-S7-DROPDOWNFOCUSDIVERGE",
|
|
203326
|
+
migration: {
|
|
203327
|
+
none: "The runtime did not change; only the spec stopped saying Visible waits for the next paint. No stored project changes"
|
|
203328
|
+
}
|
|
203329
|
+
};
|
|
203330
|
+
}
|
|
203331
|
+
|
|
203108
203332
|
// ../nodegx-node-spec/src/nodes/button.ts
|
|
203109
203333
|
var NONE = { send: [] };
|
|
203110
203334
|
var Button3 = defineNode({
|
|
@@ -203113,8 +203337,9 @@ var Button3 = defineNode({
|
|
|
203113
203337
|
// v3 (P109 ISL-031, P107-D30 ruled "Reset and announce"): a control leaving the page (Mounted off) takes Focused, Hover and Pressed with it — each goes false, sent if it was true, with Blurred and Hover End for the ones that were on
|
|
203114
203338
|
// v4 (NSP-019 R35): a Focus that finds the node off the page shows as its warning until a Focus reaches it, and an Icon Source that is not an icon shows as one until an icon or nothing arrives (`diagnostics`)
|
|
203115
203339
|
// v5 (NSP-019 R38): a wired Width in a row, or a wired Height in a column, that is a percent shows as its warning (wired-grow.ts)
|
|
203116
|
-
|
|
203117
|
-
|
|
203340
|
+
// v7 (BETA-S7-DROPDOWNFOCUSDIVERGE, the spec correcting itself): Visible reaches the element at once, so a Focus in the same frame meets it (visible-at-once.ts)
|
|
203341
|
+
version: 7,
|
|
203342
|
+
changes: [focusAndIconSourceChange(4), growChange(5), repeatWhileSavingChange(6, "click"), visibleAtOnceChange(7)],
|
|
203118
203343
|
diagnostics: { ...focusDiagnostics(controlFocusNotMounted("button")), ...iconSourceDiagnostics(), ...wiredGrowDiagnostic },
|
|
203119
203344
|
source: "packages/noodl-viewer-react/src/nodes/controls/button.ts",
|
|
203120
203345
|
needs: ["person"],
|
|
@@ -203300,8 +203525,9 @@ var Button3 = defineNode({
|
|
|
203300
203525
|
// NSP-019 R38 — node.ts :446-458, react-component-node.ts :706-753 (wired-grow.ts)
|
|
203301
203526
|
width: sizeReducer("width"),
|
|
203302
203527
|
height: sizeReducer("height"),
|
|
203303
|
-
// node-shared-port-definitions.ts :
|
|
203304
|
-
|
|
203528
|
+
// node-shared-port-definitions.ts :374-388 — written onto the element at once, so a Focus in the same frame meets it
|
|
203529
|
+
// (visible-at-once.ts)
|
|
203530
|
+
visible: (_s, v) => ({ says: "always: shown or hidden at once, its space kept; hidden, a Focus does not reach it", set: { visible: !!v, pageVisible: !!v }, send: [] })
|
|
203305
203531
|
},
|
|
203306
203532
|
{
|
|
203307
203533
|
// placed on the page with its params set — the first paint (react-component-node.ts :1740-1743 reads `wantsToBeMounted`):
|
|
@@ -203382,8 +203608,9 @@ var Checkbox2 = defineNode({
|
|
|
203382
203608
|
// v3 (P109 ISL-031, P107-C64 ruled "Ignore touches, keep hover"): a finger on a disabled control reports nothing — no Pressed, no Pointer Down or Up — as Enabled promises; the pointer's hover still reports
|
|
203383
203609
|
// v4 (P109 ISL-031, P107-D30 ruled "Reset and announce"): a control leaving the page (Mounted off) takes Focused, Hover and Pressed with it — each goes false, sent if it was true, with Blurred and Hover End for the ones that were on
|
|
203384
203610
|
// v5 (NSP-019 R35): a Focus that finds the node off the page shows as its warning until a Focus reaches it, and an Icon Source that is not an icon shows as one until an icon or nothing arrives (`diagnostics`)
|
|
203385
|
-
|
|
203386
|
-
|
|
203611
|
+
// v6 (BETA-S7-DROPDOWNFOCUSDIVERGE, the spec correcting itself): Visible reaches the element at once, so a Focus in the same frame meets it (visible-at-once.ts)
|
|
203612
|
+
version: 6,
|
|
203613
|
+
changes: [focusAndIconSourceChange(5), visibleAtOnceChange(6)],
|
|
203387
203614
|
diagnostics: { ...focusDiagnostics(controlFocusNotMounted("checkbox")), ...iconSourceDiagnostics() },
|
|
203388
203615
|
source: "packages/noodl-viewer-react/src/nodes/controls/checkbox.ts",
|
|
203389
203616
|
needs: ["person"],
|
|
@@ -203567,8 +203794,9 @@ var Checkbox2 = defineNode({
|
|
|
203567
203794
|
return { says: "it holds the keyboard: it is taken off it, and the Focused state goes false", set: { focused: false, elementFocused: false }, send: ["focusState"], emit: ["onBlur"], outcome: "done" };
|
|
203568
203795
|
},
|
|
203569
203796
|
scrollIntoView: () => ({ says: "always: the page, or the scrolling Group holding it, scrolls until the box can be seen", send: [] }),
|
|
203570
|
-
// node-shared-port-definitions.ts :
|
|
203571
|
-
|
|
203797
|
+
// node-shared-port-definitions.ts :374-388 — written onto the element at once, so a Focus in the same frame meets it
|
|
203798
|
+
// (visible-at-once.ts)
|
|
203799
|
+
visible: (_s, v) => ({ says: "always: shown or hidden at once, its space kept; hidden, a Focus does not reach it", set: { visible: !!v, pageVisible: !!v }, send: [] })
|
|
203572
203800
|
},
|
|
203573
203801
|
{
|
|
203574
203802
|
world: { place: (s) => painted2(s) },
|
|
@@ -203693,8 +203921,9 @@ var TextInput3 = defineNode({
|
|
|
203693
203921
|
// v6 (P109 ISL-031, P107-D30 ruled "Reset and announce"): a control leaving the page (Mounted off) takes Focused, Hover and Pressed with it — each goes false, sent if it was true, with Blurred and Hover End for the ones that were on
|
|
203694
203922
|
// v7 (NSP-019 R35): a Focus that finds the node off the page shows as its warning until a Focus reaches it, and an Icon Source that is not an icon shows as one until an icon or nothing arrives (`diagnostics`)
|
|
203695
203923
|
// v8 (NSP-019 R38): a wired Width in a row, or a wired Height in a column, that is a percent shows as its warning (wired-grow.ts)
|
|
203696
|
-
|
|
203697
|
-
|
|
203924
|
+
// v10 (BETA-S7-DROPDOWNFOCUSDIVERGE, the spec correcting itself): Visible reaches the element at once, so a Focus in the same frame meets it (visible-at-once.ts)
|
|
203925
|
+
version: 10,
|
|
203926
|
+
changes: [focusAndIconSourceChange(7), growChange(8), repeatWhileSavingChange(9, "Enter"), visibleAtOnceChange(10)],
|
|
203698
203927
|
diagnostics: { ...focusDiagnostics(FIELD_FOCUS_NOT_MOUNTED), ...iconSourceDiagnostics(), ...wiredGrowDiagnostic },
|
|
203699
203928
|
source: "packages/noodl-viewer-react/src/nodes/controls/text-input.ts",
|
|
203700
203929
|
needs: ["person"],
|
|
@@ -204015,9 +204244,10 @@ var TextInput3 = defineNode({
|
|
|
204015
204244
|
// NSP-019 R38 — node.ts :446-458, react-component-node.ts :706-753 (wired-grow.ts)
|
|
204016
204245
|
width: sizeReducer("width"),
|
|
204017
204246
|
height: sizeReducer("height"),
|
|
204018
|
-
// node-shared-port-definitions.ts :
|
|
204019
|
-
// Opacity the
|
|
204020
|
-
|
|
204247
|
+
// node-shared-port-definitions.ts :374-388 — written onto the element at once, so a Focus in the same frame meets it
|
|
204248
|
+
// (visible-at-once.ts); Opacity (:320-328) follows at the next paint (TextInput.tsx :118-120 compares the Opacity the style
|
|
204249
|
+
// holds with the number 0)
|
|
204250
|
+
visible: (_s, v) => ({ says: "always: shown or hidden at once, its space kept; hidden, a Focus does not reach it", set: { visible: !!v, pageVisible: !!v }, send: [] }),
|
|
204021
204251
|
opacity: (_s, v) => ({ says: "always: how see-through it is; at exactly 0 it takes no pointer, and an act goes to what is behind it", set: { opacityZero: v === 0 }, send: [] })
|
|
204022
204252
|
},
|
|
204023
204253
|
{
|
|
@@ -204330,7 +204560,8 @@ var Dropdown = defineNode({
|
|
|
204330
204560
|
// v10 (NSP-019 R38): a wired Width in a row, or a wired Height in a column, that is a percent shows as its warning (wired-grow.ts)
|
|
204331
204561
|
// v11 (P121-TWO005-DROPDOWNSTALE): Items that do not offer the placed node's seed ("Option 1") clear it to '' and send Value
|
|
204332
204562
|
// v12 (BETA-S7-DROPDOWNFIRSTPICK): with the value in no option, the element keeps its hidden empty option selected, so every option is a pick
|
|
204333
|
-
|
|
204563
|
+
// v13 (BETA-S7-DROPDOWNFOCUSDIVERGE, the spec correcting itself): Visible reaches the element at once, so a Focus in the same frame meets it (visible-at-once.ts)
|
|
204564
|
+
version: 13,
|
|
204334
204565
|
// NSP-010 — v7 is the spec correcting itself, not the runtime changing
|
|
204335
204566
|
changes: [
|
|
204336
204567
|
{
|
|
@@ -204366,7 +204597,8 @@ var Dropdown = defineNode({
|
|
|
204366
204597
|
migration: {
|
|
204367
204598
|
none: "A pick that did nothing now does what the person asked; nothing an app could rely on, and no stored project changes"
|
|
204368
204599
|
}
|
|
204369
|
-
}
|
|
204600
|
+
},
|
|
204601
|
+
visibleAtOnceChange(13)
|
|
204370
204602
|
],
|
|
204371
204603
|
diagnostics: { ...focusDiagnostics(controlFocusNotMounted("dropdown")), ...iconSourceDiagnostics(), ...wiredGrowDiagnostic },
|
|
204372
204604
|
source: "packages/noodl-viewer-react/src/nodes/controls/options.ts",
|
|
@@ -204648,8 +204880,9 @@ var Dropdown = defineNode({
|
|
|
204648
204880
|
// NSP-019 R38 — node.ts :446-458, react-component-node.ts :706-753 (wired-grow.ts)
|
|
204649
204881
|
width: sizeReducer("width"),
|
|
204650
204882
|
height: sizeReducer("height"),
|
|
204651
|
-
// node-shared-port-definitions.ts :
|
|
204652
|
-
|
|
204883
|
+
// node-shared-port-definitions.ts :374-388 — written onto the element at once, so a Focus in the same frame meets it
|
|
204884
|
+
// (visible-at-once.ts)
|
|
204885
|
+
visible: (_s, v) => ({ says: "always: shown or hidden at once, its space kept; hidden, a Focus does not reach it", set: { visible: !!v, pageVisible: !!v }, send: [] })
|
|
204653
204886
|
},
|
|
204654
204887
|
{
|
|
204655
204888
|
// react-component-node.ts :785-830 `DATA_ATTRIBUTES_RULE` — one string input per name the Data Attributes list names, written
|
|
@@ -204818,7 +205051,8 @@ var Slider2 = defineNode({
|
|
|
204818
205051
|
// v7 (NSP-019 R39, ruled "Yes, warn"): a Max below Min warns, as the Counter's Min above Max does; and a Value that is not a
|
|
204819
205052
|
// number, which the runtime always reported on the error bus, is declared with it (the editor records the `slider/` keys)
|
|
204820
205053
|
// v8 (NSP-019 R38): a wired Width in a row that is a percent shows as its warning (wired-grow.ts)
|
|
204821
|
-
|
|
205054
|
+
// v9 (BETA-S7-DROPDOWNFOCUSDIVERGE, the spec correcting itself): Visible reaches the element at once, so a Focus in the same frame meets it (visible-at-once.ts)
|
|
205055
|
+
version: 9,
|
|
204822
205056
|
changes: [
|
|
204823
205057
|
focusChange(6),
|
|
204824
205058
|
{
|
|
@@ -204827,7 +205061,8 @@ var Slider2 = defineNode({
|
|
|
204827
205061
|
row: "NSP-019 R39 (P107 s104)",
|
|
204828
205062
|
migration: { none: "Only the editor is told. Nothing a wire carries changes, and nothing a person does moves differently" }
|
|
204829
205063
|
},
|
|
204830
|
-
growChange(8)
|
|
205064
|
+
growChange(8),
|
|
205065
|
+
visibleAtOnceChange(9)
|
|
204831
205066
|
],
|
|
204832
205067
|
diagnostics: {
|
|
204833
205068
|
...focusDiagnostics(controlFocusNotMounted("slider")),
|
|
@@ -205061,8 +205296,9 @@ var Slider2 = defineNode({
|
|
|
205061
205296
|
scrollIntoView: () => ({ says: "always: the page, or the scrolling Group holding it, scrolls until the slider can be seen", send: [] }),
|
|
205062
205297
|
// NSP-019 R38 — node.ts :446-458, react-component-node.ts :706-753 (wired-grow.ts)
|
|
205063
205298
|
width: sizeReducer("width", false),
|
|
205064
|
-
// node-shared-port-definitions.ts :
|
|
205065
|
-
|
|
205299
|
+
// node-shared-port-definitions.ts :374-388 — written onto the element at once, so a Focus in the same frame meets it
|
|
205300
|
+
// (visible-at-once.ts)
|
|
205301
|
+
visible: (_s, v) => ({ says: "always: shown or hidden at once, its space kept; hidden, a Focus does not reach it", set: { visible: !!v, pageVisible: !!v }, send: [] })
|
|
205066
205302
|
},
|
|
205067
205303
|
{
|
|
205068
205304
|
// placed on the page with its params set — the first render: Did Mount, and the mount's effect (Slider.tsx :88-90)
|
|
@@ -208042,7 +208278,7 @@ var Video2 = defineNode({
|
|
|
208042
208278
|
autoplay: (_s, v) => ({ says: "always: drawn at the next paint, and read as a load completes \u2014 on, a video that may play (Muted, or a person has acted on the page) and that Pause or Reset has not stopped since the load began starts by itself: On Can Play, then On Play", set: { autoplay: v }, send: [] }),
|
|
208043
208279
|
loop: (_s, v) => ({ says: "always: drawn at the next paint, and read as the video reaches its end \u2014 on, it goes back to the start and plays on; off, it pauses there (On Pause)", set: { loop: v }, send: [] }),
|
|
208044
208280
|
// :35-47 — on the component, or waiting for it with the actions (`sourceObject`: only a `<video>` takes one, and only nothing is
|
|
208045
|
-
// seen to change it;
|
|
208281
|
+
// seen to change it; C99 on the runtime)
|
|
208046
208282
|
srcObject: (s, v) => {
|
|
208047
208283
|
if (!s.onPage) return said("off the page: waits for the element behind the actions already waiting, in order, and is written at the paint that puts the video there (at most 16 wait, and the oldest is pushed out)", toPatch2(waitFor(s, { srcObject: v })));
|
|
208048
208284
|
if (s.kind !== "video" || v !== void 0) return { says: "null (what it already has), a value that is not a stream, or anything on an embedded player: nothing changes \u2014 a live stream itself plays in place of Source, and no scenario holds one", set: {}, send: [], emit: [] };
|
|
@@ -210416,7 +210652,7 @@ function start(w, op) {
|
|
|
210416
210652
|
if (b === MISSING) w.st.hops = HOPS_ADD_MISSING;
|
|
210417
210653
|
else if (b === null) w.st.hops = 2;
|
|
210418
210654
|
else w.st.building = { kind: "add", row: b, index: op.index, at: childIndexFor(w.st.rows, op.index) };
|
|
210419
|
-
} else
|
|
210655
|
+
} else refresh2(w);
|
|
210420
210656
|
}
|
|
210421
210657
|
function compileMapping(code, rethrow = false) {
|
|
210422
210658
|
if (!code) return void 0;
|
|
@@ -210599,7 +210835,7 @@ function report2(w, heard3) {
|
|
|
210599
210835
|
w.pulses.push(`itemOutputSignal-${signal2}`);
|
|
210600
210836
|
}
|
|
210601
210837
|
}
|
|
210602
|
-
function
|
|
210838
|
+
function refresh2(w) {
|
|
210603
210839
|
const st = w.st;
|
|
210604
210840
|
st.refreshScheduled = false;
|
|
210605
210841
|
const tokens = st.tokens;
|
|
@@ -211615,8 +211851,9 @@ var RadioButton3 = defineNode({
|
|
|
211615
211851
|
// v3 (P109 ISL-031, P107-C64 ruled "Ignore touches, keep hover"): a finger on a disabled control reports nothing — no Pressed, no Pointer Down or Up — as Enabled promises; the pointer's hover still reports
|
|
211616
211852
|
// v4 (P109 ISL-031, P107-D30 ruled "Reset and announce"): a control leaving the page (Mounted off) takes Focused, Hover and Pressed with it — each goes false, sent if it was true, with Blurred and Hover End for the ones that were on
|
|
211617
211853
|
// v5 (NSP-019 R35): a Focus that finds the node off the page shows as its warning until a Focus reaches it, and an Icon Source that is not an icon shows as one until an icon or nothing arrives (`diagnostics`)
|
|
211618
|
-
|
|
211619
|
-
|
|
211854
|
+
// v6 (BETA-S7-DROPDOWNFOCUSDIVERGE, the spec correcting itself): Visible reaches the element at once, so a Focus in the same frame meets it (visible-at-once.ts)
|
|
211855
|
+
version: 6,
|
|
211856
|
+
changes: [focusAndIconSourceChange(5), visibleAtOnceChange(6)],
|
|
211620
211857
|
diagnostics: { ...focusDiagnostics(controlFocusNotMounted("radio button")), ...iconSourceDiagnostics() },
|
|
211621
211858
|
source: "packages/noodl-viewer-react/src/nodes/controls/radiobutton.ts",
|
|
211622
211859
|
needs: ["person", "group"],
|
|
@@ -211808,8 +212045,9 @@ var RadioButton3 = defineNode({
|
|
|
211808
212045
|
return { says: "it holds the keyboard: it is taken off it, and the Focused state goes false", set: { focused: false, elementFocused: false }, send: ["focusState"], emit: ["onBlur"], outcome: "done" };
|
|
211809
212046
|
},
|
|
211810
212047
|
scrollIntoView: () => ({ says: "always: the page, or the scrolling Group holding it, scrolls until the button can be seen", send: [] }),
|
|
211811
|
-
// node-shared-port-definitions.ts :
|
|
211812
|
-
|
|
212048
|
+
// node-shared-port-definitions.ts :374-388 — written onto the element at once, so a Focus in the same frame meets it
|
|
212049
|
+
// (visible-at-once.ts)
|
|
212050
|
+
visible: (_s, v) => ({ says: "always: shown or hidden at once, its space kept; hidden, it takes no click and a Focus does not reach it", set: { visible: !!v, pageVisible: !!v }, send: [] })
|
|
211813
212051
|
},
|
|
211814
212052
|
{
|
|
211815
212053
|
world: {
|
|
@@ -215374,7 +215612,7 @@ function applyReturn(s, r) {
|
|
|
215374
215612
|
if (r.inProgress) return { set: { signingIn: true }, send: ["signingIn"] };
|
|
215375
215613
|
if (s.returned) return { send: [] };
|
|
215376
215614
|
if (r.succeeded) {
|
|
215377
|
-
return { set: { signingIn: false, notice: r.notice, error:
|
|
215615
|
+
return { set: { signingIn: false, notice: r.notice, error: "", returned: true }, send: ["signingIn", "notice", "error"], opens: [{ outcome: "done" }] };
|
|
215378
215616
|
}
|
|
215379
215617
|
return { set: { signingIn: false, error: r.error || SIGN_IN_NOT_COMPLETED, returned: true }, send: ["signingIn", "error"], opens: [{ outcome: "failure", error: SIGN_IN_WITH_ERROR_CODE2 }] };
|
|
215380
215618
|
}
|
|
@@ -215384,7 +215622,19 @@ var SignInWith = defineNode({
|
|
|
215384
215622
|
// v1 played the launcher only; no v1 trace moves (no v1 world carried a return)
|
|
215385
215623
|
// v3 (P109 ISL-030, row C50 ruled "Fire it once"): a return's final state is reported once per page load; v2 reported a
|
|
215386
215624
|
// provider's error twice — at mount and at the announcement a tick later.
|
|
215387
|
-
|
|
215625
|
+
// v4 (Beta sweep s19, row P109-S24-SIGNINWITHERRORKEPT ruled "Blank text, like magic link"): a succeeded return clears Error
|
|
215626
|
+
// to blank text and sends it, as Request Magic Link's Done does (D25); v3 cleared it to `undefined`, never sent.
|
|
215627
|
+
version: 4,
|
|
215628
|
+
changes: [
|
|
215629
|
+
{
|
|
215630
|
+
version: 4,
|
|
215631
|
+
says: "A sign-in that comes back succeeded clears Error to blank text and sends it, so a label wired to Error lets go of the last failure (before, the clear was nothing, which never reaches a wire)",
|
|
215632
|
+
row: "P109-S24-SIGNINWITHERRORKEPT",
|
|
215633
|
+
migration: {
|
|
215634
|
+
none: "A page load gets one sign-in back and the node is new on it, so its Error never held an earlier failure: no project can see the before"
|
|
215635
|
+
}
|
|
215636
|
+
}
|
|
215637
|
+
],
|
|
215388
215638
|
source: "packages/noodl-viewer-react/src/nodes/std-library/user/signinwith.ts; userservice.ts; RestAuthAdapter.ts; ParseAuthAdapter.ts",
|
|
215389
215639
|
needs: ["backend", "registry", "clock"],
|
|
215390
215640
|
worldPool: { backends: [...pool("signInWithProvider", null), ...RETURNS] },
|
|
@@ -231392,7 +231642,7 @@ var cloud_node_library_default = {
|
|
|
231392
231642
|
plug: "input",
|
|
231393
231643
|
group: "General",
|
|
231394
231644
|
displayName: "Allow Unauthenticated",
|
|
231395
|
-
description: "Whether a request with no valid session token is allowed to run this function at all",
|
|
231645
|
+
description: "Whether a request with no valid session token is allowed to run this function at all. A run the app starts itself \u2014 a schedule, a row change, a run from Studio \u2014 is always allowed and sees nobody signed in; a webhook is a request like any other",
|
|
231396
231646
|
default: false
|
|
231397
231647
|
},
|
|
231398
231648
|
{
|
|
@@ -235538,7 +235788,11 @@ var AUTHORED_BLOCKING_WARNINGS = /* @__PURE__ */ new Set([
|
|
|
235538
235788
|
// Bug P121-TWO003-SCRIPTCOMPILE — the runtime's own compile fails, so every Run fails. No benign
|
|
235539
235789
|
// reading for a graph an agent just wrote; the write said nothing before, and the first person
|
|
235540
235790
|
// to run the function got "That could not be done."
|
|
235541
|
-
"function-script-not-compiled" /* FunctionScriptNotCompiled
|
|
235791
|
+
"function-script-not-compiled" /* FunctionScriptNotCompiled */,
|
|
235792
|
+
// Bug P127-BKO006-RUNONCHANGEGUESS — a `runOnChange-` name a Function does not have is never read
|
|
235793
|
+
// (2,805 right and 5 wrong over 2,718 Functions, all five agent-written); the message names the
|
|
235794
|
+
// right one.
|
|
235795
|
+
"function-run-on-change-unknown" /* FunctionRunOnChangeUnknown */
|
|
235542
235796
|
]);
|
|
235543
235797
|
|
|
235544
235798
|
// ../noodl-editor/src/editor/src/validation/diagnosticExamples.ts
|
|
@@ -235678,6 +235932,12 @@ var DIAGNOSTIC_EXAMPLES = [
|
|
|
235678
235932
|
code: "function-guards-an-open-collection" /* FunctionGuardsAnOpenCollection */,
|
|
235679
235933
|
examples: ["cloud-members-can-edit-my-records"],
|
|
235680
235934
|
why: `the records only a function creates, with the collection's create rule "nobody": the function runs as the system`
|
|
235935
|
+
},
|
|
235936
|
+
// P112-OWN018-FANOUT (ruled 2026-10-27 "Teach it and warn") — the warning carries the chained shape.
|
|
235937
|
+
{
|
|
235938
|
+
code: "condition-reads-a-siblings-change" /* ConditionReadsASiblingsChange */,
|
|
235939
|
+
examples: ["score-then-move-on"],
|
|
235940
|
+
why: "the press evaluating the gate that reads, and that gate's Done evaluating the gate that moves on, so the score is decided first"
|
|
235681
235941
|
}
|
|
235682
235942
|
];
|
|
235683
235943
|
|
|
@@ -237264,6 +237524,156 @@ var functionNeverReruns = {
|
|
|
237264
237524
|
}
|
|
237265
237525
|
};
|
|
237266
237526
|
|
|
237527
|
+
// ../noodl-editor/src/editor/src/validation/rules/functionRunOnChangeUnknown.ts
|
|
237528
|
+
var RUN_ON_CHANGE_PREFIX4 = "runOnChange-";
|
|
237529
|
+
var FUNCTION_INPUT_PREFIX = "in-";
|
|
237530
|
+
var functionRunOnChangeUnknown = {
|
|
237531
|
+
code: "function-run-on-change-unknown" /* FunctionRunOnChangeUnknown */,
|
|
237532
|
+
description: "A Function has a Run On Value Change setting under a name it does not have (its boxes are runOnChange-in-<input>).",
|
|
237533
|
+
defaultEnabled: true,
|
|
237534
|
+
run(ctx) {
|
|
237535
|
+
const out = [];
|
|
237536
|
+
for (const { component } of ctx.components) {
|
|
237537
|
+
for (const node of component.nodes) {
|
|
237538
|
+
if (node.type !== FUNCTION_NODE_TYPE || !node.parameters) continue;
|
|
237539
|
+
const guessed = Object.keys(node.parameters).filter(
|
|
237540
|
+
(key) => key.startsWith(RUN_ON_CHANGE_PREFIX4) && !key.startsWith(RUN_ON_CHANGE_PREFIX4 + FUNCTION_INPUT_PREFIX)
|
|
237541
|
+
);
|
|
237542
|
+
if (guessed.length === 0) continue;
|
|
237543
|
+
const inputs = scriptPortsForNode(node).filter((p) => p.plug === "input" && p.name.startsWith(FUNCTION_INPUT_PREFIX)).map((p) => p.name.slice(FUNCTION_INPUT_PREFIX.length));
|
|
237544
|
+
const name2 = node.label ? `"${node.label}"` : "This Function";
|
|
237545
|
+
for (const key of guessed) {
|
|
237546
|
+
const wanted = key.slice(RUN_ON_CHANGE_PREFIX4.length);
|
|
237547
|
+
const input2 = inputs.includes(wanted) ? wanted : nearest(wanted, inputs);
|
|
237548
|
+
const right = input2 !== void 0 ? `${RUN_ON_CHANGE_PREFIX4}${FUNCTION_INPUT_PREFIX}${input2}` : void 0;
|
|
237549
|
+
out.push({
|
|
237550
|
+
code: "function-run-on-change-unknown" /* FunctionRunOnChangeUnknown */,
|
|
237551
|
+
severity: "warning",
|
|
237552
|
+
message: `${name2} has no setting "${key}", so nothing reads it` + (input2 === wanted ? ` and a new "${wanted}" still runs it. ` : ". ") + `A Function's Run On Value Change box for an input is "runOnChange-in-<input>"` + (right ? input2 === wanted ? `: write "${right}".` : `; its script reads no "${wanted}" (the nearest input is "${input2}": "${right}").` : `, and its script reads no inputs.`),
|
|
237553
|
+
location: {
|
|
237554
|
+
component: component.name,
|
|
237555
|
+
nodeId: node.id,
|
|
237556
|
+
nodeType: node.type,
|
|
237557
|
+
nodeLabel: node.label,
|
|
237558
|
+
port: key,
|
|
237559
|
+
plug: "input"
|
|
237560
|
+
},
|
|
237561
|
+
...right ? { suggestion: `${right}: ${JSON.stringify(node.parameters[key])}` } : {}
|
|
237562
|
+
});
|
|
237563
|
+
}
|
|
237564
|
+
}
|
|
237565
|
+
}
|
|
237566
|
+
return out;
|
|
237567
|
+
}
|
|
237568
|
+
};
|
|
237569
|
+
|
|
237570
|
+
// ../noodl-editor/src/editor/src/validation/rules/conditionReadsASiblingsChange.ts
|
|
237571
|
+
var CONDITION = "Condition";
|
|
237572
|
+
var conditionReadsASiblingsChange = {
|
|
237573
|
+
code: "condition-reads-a-siblings-change" /* ConditionReadsASiblingsChange */,
|
|
237574
|
+
description: "Two Conditions evaluated by one signal, where one's On True or On False changes what the other reads.",
|
|
237575
|
+
defaultEnabled: true,
|
|
237576
|
+
run(ctx) {
|
|
237577
|
+
const out = [];
|
|
237578
|
+
const { catalog } = ctx;
|
|
237579
|
+
for (const { component, nodeById } of ctx.components) {
|
|
237580
|
+
let exclusiveStates = function(a, b) {
|
|
237581
|
+
const stateOf = (gate) => {
|
|
237582
|
+
const c = (incoming.get(gate) ?? []).find((w) => w.toProperty === "condition");
|
|
237583
|
+
return c && nodeById.get(c.fromId)?.type === "States" && c.fromProperty.startsWith("at-") ? c : void 0;
|
|
237584
|
+
};
|
|
237585
|
+
const sa = stateOf(a);
|
|
237586
|
+
const sb = stateOf(b);
|
|
237587
|
+
return !!sa && !!sb && sa.fromId === sb.fromId && sa.fromProperty !== sb.fromProperty;
|
|
237588
|
+
}, feedingValues = function(gate) {
|
|
237589
|
+
const seen = /* @__PURE__ */ new Set();
|
|
237590
|
+
const stack = (incoming.get(gate) ?? []).filter((c) => c.toProperty === "condition").map((c) => c.fromId);
|
|
237591
|
+
while (stack.length > 0) {
|
|
237592
|
+
const id = stack.pop();
|
|
237593
|
+
if (id === gate || seen.has(id)) continue;
|
|
237594
|
+
seen.add(id);
|
|
237595
|
+
if (!passesThrough(id)) continue;
|
|
237596
|
+
const type = nodeById.get(id)?.type;
|
|
237597
|
+
for (const c of incoming.get(id) ?? []) {
|
|
237598
|
+
if (type && catalog.getPort(type, "input", c.toProperty)?.isSignal) continue;
|
|
237599
|
+
stack.push(c.fromId);
|
|
237600
|
+
}
|
|
237601
|
+
}
|
|
237602
|
+
return seen;
|
|
237603
|
+
}, reaches5 = function(gate, targets) {
|
|
237604
|
+
if (targets.size === 0) return false;
|
|
237605
|
+
const seen = /* @__PURE__ */ new Set([gate]);
|
|
237606
|
+
const stack = (outgoing.get(gate) ?? []).map((c) => c.toId);
|
|
237607
|
+
while (stack.length > 0) {
|
|
237608
|
+
const id = stack.pop();
|
|
237609
|
+
if (targets.has(id)) return true;
|
|
237610
|
+
if (seen.has(id)) continue;
|
|
237611
|
+
seen.add(id);
|
|
237612
|
+
if (!passesThrough(id)) continue;
|
|
237613
|
+
for (const c of outgoing.get(id) ?? []) stack.push(c.toId);
|
|
237614
|
+
}
|
|
237615
|
+
return false;
|
|
237616
|
+
};
|
|
237617
|
+
const outgoing = /* @__PURE__ */ new Map();
|
|
237618
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
237619
|
+
for (const c of component.connections) {
|
|
237620
|
+
(outgoing.get(c.fromId) ?? outgoing.set(c.fromId, []).get(c.fromId)).push(c);
|
|
237621
|
+
(incoming.get(c.toId) ?? incoming.set(c.toId, []).get(c.toId)).push(c);
|
|
237622
|
+
}
|
|
237623
|
+
const isCondition = (id) => nodeById.get(id)?.type === CONDITION;
|
|
237624
|
+
const passesThrough = (id) => {
|
|
237625
|
+
const type = nodeById.get(id)?.type;
|
|
237626
|
+
const known = type ? catalog.getNode(type) : void 0;
|
|
237627
|
+
return !!known && !known.isVisual;
|
|
237628
|
+
};
|
|
237629
|
+
const gatesBySource = /* @__PURE__ */ new Map();
|
|
237630
|
+
for (const c of component.connections) {
|
|
237631
|
+
if (c.toProperty !== "eval" || !isCondition(c.toId)) continue;
|
|
237632
|
+
const key = `${c.fromId}\0${c.fromProperty}`;
|
|
237633
|
+
const gates = gatesBySource.get(key) ?? [];
|
|
237634
|
+
if (!gates.includes(c.toId)) gates.push(c.toId);
|
|
237635
|
+
gatesBySource.set(key, gates);
|
|
237636
|
+
}
|
|
237637
|
+
const reported3 = /* @__PURE__ */ new Set();
|
|
237638
|
+
for (const [key, gates] of gatesBySource) {
|
|
237639
|
+
if (gates.length < 2) continue;
|
|
237640
|
+
const sourceId = key.split("\0")[0];
|
|
237641
|
+
for (const reader of gates) {
|
|
237642
|
+
if (reported3.has(reader)) continue;
|
|
237643
|
+
const feeds = feedingValues(reader);
|
|
237644
|
+
for (const writer of gates) {
|
|
237645
|
+
if (writer === reader) continue;
|
|
237646
|
+
if (exclusiveStates(reader, writer)) continue;
|
|
237647
|
+
if (!reaches5(writer, feeds)) continue;
|
|
237648
|
+
reported3.add(reader);
|
|
237649
|
+
const r = nodeById.get(reader);
|
|
237650
|
+
const w = nodeById.get(writer);
|
|
237651
|
+
const rName = r.label ? `"${r.label}"` : "This Condition";
|
|
237652
|
+
const wName = w.label ? `"${w.label}"` : "another Condition";
|
|
237653
|
+
const sName = nodeById.get(sourceId)?.label ? `"${nodeById.get(sourceId).label}"` : "the same signal";
|
|
237654
|
+
out.push({
|
|
237655
|
+
code: "condition-reads-a-siblings-change" /* ConditionReadsASiblingsChange */,
|
|
237656
|
+
severity: "warning",
|
|
237657
|
+
message: `${rName} and ${wName} are both evaluated by ${sName}, and what ${wName} does changes what ${rName} reads. ${rName} reads its Condition after that change, whichever order the two are wired in. If it should decide on the value from before, chain them: ${rName}'s Done into ${wName}'s Evaluate, instead of both from ${sName}.`,
|
|
237658
|
+
location: {
|
|
237659
|
+
component: component.name,
|
|
237660
|
+
nodeId: r.id,
|
|
237661
|
+
nodeType: r.type,
|
|
237662
|
+
nodeLabel: r.label,
|
|
237663
|
+
port: "eval",
|
|
237664
|
+
plug: "input"
|
|
237665
|
+
},
|
|
237666
|
+
suggestion: `${r.id}.done \u2192 ${w.id}.eval`
|
|
237667
|
+
});
|
|
237668
|
+
break;
|
|
237669
|
+
}
|
|
237670
|
+
}
|
|
237671
|
+
}
|
|
237672
|
+
}
|
|
237673
|
+
return out;
|
|
237674
|
+
}
|
|
237675
|
+
};
|
|
237676
|
+
|
|
237267
237677
|
// ../noodl-editor/src/editor/src/validation/rules/layoutFillTraps.ts
|
|
237268
237678
|
var GROUP = "Group";
|
|
237269
237679
|
var CONTENT_WIDTH = /* @__PURE__ */ new Set(["contentSize", "contentWidth"]);
|
|
@@ -237371,6 +237781,97 @@ var rowOfWordsFillsItsColumn = {
|
|
|
237371
237781
|
}
|
|
237372
237782
|
};
|
|
237373
237783
|
|
|
237784
|
+
// ../noodl-editor/src/editor/src/validation/rules/clickAreaHoldsAControl.ts
|
|
237785
|
+
var CONTROL_TYPES = /* @__PURE__ */ new Map([
|
|
237786
|
+
["net.noodl.controls.button", "Button"],
|
|
237787
|
+
["net.noodl.controls.textinput", "Text Input"],
|
|
237788
|
+
["net.noodl.controls.checkbox", "Checkbox"],
|
|
237789
|
+
["net.noodl.controls.radiobutton", "Radio Button"],
|
|
237790
|
+
["net.noodl.controls.options", "Dropdown"],
|
|
237791
|
+
["net.noodl.controls.range", "Slider"]
|
|
237792
|
+
]);
|
|
237793
|
+
var CLICK = "onClick";
|
|
237794
|
+
var COMPONENT_OUTPUTS2 = "Component Outputs";
|
|
237795
|
+
var clickWired = (component, node) => component.connections.some((c) => c.fromId === node.id && c.fromProperty === CLICK);
|
|
237796
|
+
function reaches4(component, nodeById, node, port5, used) {
|
|
237797
|
+
return component.connections.some((c) => {
|
|
237798
|
+
if (c.fromId !== node.id || c.fromProperty !== port5) return false;
|
|
237799
|
+
if (nodeById.get(c.toId)?.type !== COMPONENT_OUTPUTS2) return true;
|
|
237800
|
+
return used === "all" || used.has(c.toProperty);
|
|
237801
|
+
});
|
|
237802
|
+
}
|
|
237803
|
+
var quoted = (node) => node.label ? ` ("${node.label}")` : "";
|
|
237804
|
+
var clickAreaHoldsAControl = {
|
|
237805
|
+
code: "click-area-holds-a-control" /* ClickAreaHoldsAControl */,
|
|
237806
|
+
description: "An element whose Click is wired holds a control, so the keyboard reaches the control and not the element.",
|
|
237807
|
+
defaultEnabled: true,
|
|
237808
|
+
run(ctx) {
|
|
237809
|
+
const out = [];
|
|
237810
|
+
const byPath = new Map(ctx.components.map((c) => [refToPath(c.component.name), c]));
|
|
237811
|
+
const memo = /* @__PURE__ */ new Map();
|
|
237812
|
+
const componentHolds = (ref, used) => {
|
|
237813
|
+
const path10 = refToPath(ref);
|
|
237814
|
+
const key = `${path10}|${[...used].sort().join(",")}`;
|
|
237815
|
+
if (memo.has(key)) return memo.get(key) ?? null;
|
|
237816
|
+
memo.set(key, null);
|
|
237817
|
+
const entry = byPath.get(path10);
|
|
237818
|
+
let found = null;
|
|
237819
|
+
if (entry) {
|
|
237820
|
+
for (const node of entry.component.nodes) {
|
|
237821
|
+
found = held(node, entry.component, entry.nodeById, used);
|
|
237822
|
+
if (found) break;
|
|
237823
|
+
}
|
|
237824
|
+
}
|
|
237825
|
+
memo.set(key, found);
|
|
237826
|
+
return found;
|
|
237827
|
+
};
|
|
237828
|
+
function held(node, component, nodeById, used) {
|
|
237829
|
+
const control = CONTROL_TYPES.get(node.type);
|
|
237830
|
+
if (control) return `a ${control}${quoted(node)}`;
|
|
237831
|
+
if (reaches4(component, nodeById, node, CLICK, used)) {
|
|
237832
|
+
return `${node.label ? `"${node.label}"` : "an element"}, which answers a click of its own`;
|
|
237833
|
+
}
|
|
237834
|
+
if (isComponentRef(node.type)) {
|
|
237835
|
+
const ports = new Set(component.connections.filter((c) => c.fromId === node.id).map((c) => c.fromProperty));
|
|
237836
|
+
const usedHere = new Set([...ports].filter((port5) => reaches4(component, nodeById, node, port5, used)));
|
|
237837
|
+
const inside = componentHolds(node.type, usedHere);
|
|
237838
|
+
if (inside) return `the component ${node.label ? `"${node.label}"` : `"${refToPath(node.type)}"`}, which holds ${inside}`;
|
|
237839
|
+
}
|
|
237840
|
+
return null;
|
|
237841
|
+
}
|
|
237842
|
+
for (const { component, nodeById } of ctx.components) {
|
|
237843
|
+
for (const area of component.nodes) {
|
|
237844
|
+
if (CONTROL_TYPES.has(area.type) || !clickWired(component, area)) continue;
|
|
237845
|
+
if (!ctx.catalog.getNode(area.type)?.isVisual) continue;
|
|
237846
|
+
let inner = null;
|
|
237847
|
+
const stack = [...area.children ?? []];
|
|
237848
|
+
while (stack.length && !inner) {
|
|
237849
|
+
const node = nodeById.get(stack.shift());
|
|
237850
|
+
if (!node) continue;
|
|
237851
|
+
inner = held(node, component, nodeById, "all");
|
|
237852
|
+
stack.push(...node.children ?? []);
|
|
237853
|
+
}
|
|
237854
|
+
if (!inner) continue;
|
|
237855
|
+
const name2 = area.label ? `"${area.label}"` : `This ${ctx.catalog.getNode(area.type)?.displayName ?? area.type}`;
|
|
237856
|
+
out.push({
|
|
237857
|
+
code: "click-area-holds-a-control" /* ClickAreaHoldsAControl */,
|
|
237858
|
+
severity: "info",
|
|
237859
|
+
message: `${name2} answers a click. It holds ${inner}. The Tab key stops on what it holds instead. A person using a keyboard or a screen reader cannot press ${area.label ? name2 : "it"} itself. If its click does something nothing inside it does, give that its own Button.`,
|
|
237860
|
+
location: {
|
|
237861
|
+
component: component.name,
|
|
237862
|
+
nodeId: area.id,
|
|
237863
|
+
nodeType: area.type,
|
|
237864
|
+
...area.label ? { nodeLabel: area.label } : {},
|
|
237865
|
+
port: CLICK,
|
|
237866
|
+
plug: "output"
|
|
237867
|
+
}
|
|
237868
|
+
});
|
|
237869
|
+
}
|
|
237870
|
+
}
|
|
237871
|
+
return out;
|
|
237872
|
+
}
|
|
237873
|
+
};
|
|
237874
|
+
|
|
237374
237875
|
// ../noodl-editor/src/editor/src/validation/rules/index.ts
|
|
237375
237876
|
var ALL_RULES = [
|
|
237376
237877
|
duplicateNodeId,
|
|
@@ -237427,6 +237928,10 @@ var ALL_RULES = [
|
|
|
237427
237928
|
// arrangement ones.
|
|
237428
237929
|
functionScriptNotCompiled,
|
|
237429
237930
|
functionNeverReruns,
|
|
237931
|
+
functionRunOnChangeUnknown,
|
|
237932
|
+
// P112-OWN018-FANOUT (ruled 2026-10-27 "Teach it and warn") — beside the Function rules: what a graph's signals do in
|
|
237933
|
+
// what order, read from the wires. Two gates off one signal, one changing what the other reads.
|
|
237934
|
+
conditionReadsASiblingsChange,
|
|
237430
237935
|
repeatedSiblingSubtree,
|
|
237431
237936
|
// DEF-025 — beside `repeatedSiblingSubtree` because both read the visual
|
|
237432
237937
|
// tree's arrangement rather than wires: that one asks whether siblings
|
|
@@ -237440,6 +237945,9 @@ var ALL_RULES = [
|
|
|
237440
237945
|
// against the shipped corpus before they were written — this one is 0 hits
|
|
237441
237946
|
// on all ten templates.
|
|
237442
237947
|
dialogWithoutName,
|
|
237948
|
+
// P121-TWO003-NESTEDBUTTON (ruled 2026-10-27) — beside the two above: what a keyboard and a screen reader reach, read
|
|
237949
|
+
// from the arrangement. An info, live, so the Problems panel and an agent's write both say it.
|
|
237950
|
+
clickAreaHoldsAControl,
|
|
237443
237951
|
// P112-OWN020-PILLWRAP / FILLGROW (ruled 2026-10-26: warn and teach) — a node with no size setting fills its parent,
|
|
237444
237952
|
// read from the arrangement like the two above, and live so a person sees it while editing.
|
|
237445
237953
|
contentRowSplitsItsChildren,
|