numbl 0.4.4 → 0.4.5
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/README.md +5 -1
- package/dist-cli/cli.js +435 -32
- package/dist-lib/graphics/figuresReducer.d.ts +9 -0
- package/dist-lib/graphics/types.d.ts +11 -0
- package/dist-lib/lib.js +339 -3
- package/dist-lib/numbl-core/executeCode.d.ts +10 -0
- package/dist-lib/numbl-core/runtime/plotBuiltinDispatch.d.ts +6 -0
- package/dist-lib/numbl-core/runtime/runtime.d.ts +22 -0
- package/dist-lib/numbl-core/runtime/uihtmlSession.d.ts +40 -0
- package/dist-lib/numbl-core/version.d.ts +1 -1
- package/dist-plot-viewer/assets/{index-Ct51ZiF1.js → index-CPiPZdGN.js} +246 -168
- package/dist-plot-viewer/index.html +1 -1
- package/dist-site-viewer/assets/index-Y_Z9U6zO.js +4826 -0
- package/dist-site-viewer/assets/{numbl-worker-s3tsbJJ2.js → numbl-worker-CdHI6Ort.js} +211 -126
- package/dist-site-viewer/index.html +1 -1
- package/dist-site-viewer/numbl-embed.js +55 -13
- package/package.json +1 -1
- package/dist-site-viewer/assets/index-USrK1-DZ.js +0 -4748
|
@@ -261,6 +261,17 @@ export type AxisLimitSpec = [number | null, number | null] | "auto";
|
|
|
261
261
|
export type PlotInstruction = {
|
|
262
262
|
type: "set_figure_handle";
|
|
263
263
|
handle: number;
|
|
264
|
+
} | {
|
|
265
|
+
/** An HTML UI component (MATLAB `uihtml`): renders self-contained HTML
|
|
266
|
+
* markup in an iframe, bypassing the axes/trace model. `html` is the
|
|
267
|
+
* full HTMLSource string; `id` is a stable per-component key. `data`, when
|
|
268
|
+
* present, is the `Data` property JSON-encoded (via `jsonencode`); the
|
|
269
|
+
* renderer parses it and pushes it to the page's `htmlComponent` so the
|
|
270
|
+
* `setup`/`"DataChanged"` bridge fires (MATLAB `h.Data` → JavaScript). */
|
|
271
|
+
type: "uihtml";
|
|
272
|
+
id: string;
|
|
273
|
+
html: string;
|
|
274
|
+
data?: string;
|
|
264
275
|
} | {
|
|
265
276
|
type: "plot";
|
|
266
277
|
traces: PlotTrace[];
|
package/dist-lib/lib.js
CHANGED
|
@@ -44556,6 +44556,149 @@ for (const { name, min: min2, max: max2 } of INT_RANGES) {
|
|
|
44556
44556
|
]
|
|
44557
44557
|
});
|
|
44558
44558
|
}
|
|
44559
|
+
var TYPECAST_VIEWS = {
|
|
44560
|
+
uint8: (b) => new Uint8Array(b),
|
|
44561
|
+
int8: (b) => new Int8Array(b),
|
|
44562
|
+
uint16: (b) => new Uint16Array(b),
|
|
44563
|
+
int16: (b) => new Int16Array(b),
|
|
44564
|
+
uint32: (b) => new Uint32Array(b),
|
|
44565
|
+
int32: (b) => new Int32Array(b),
|
|
44566
|
+
single: (b) => new Float32Array(b),
|
|
44567
|
+
double: (b) => new Float64Array(b)
|
|
44568
|
+
};
|
|
44569
|
+
defineBuiltin({
|
|
44570
|
+
name: "typecast",
|
|
44571
|
+
help: {
|
|
44572
|
+
signatures: ["B = typecast(X, CLASS)"],
|
|
44573
|
+
description: "Reinterpret the raw bytes of numeric array X as CLASS (e.g. 'uint8', 'single', 'int32'). numbl stores all numerics as double, so X is treated as double-precision; this is mainly for serializing numeric data to bytes, e.g. typecast(double(x), 'uint8')."
|
|
44574
|
+
},
|
|
44575
|
+
cases: [
|
|
44576
|
+
{
|
|
44577
|
+
match: (argTypes) => {
|
|
44578
|
+
if (argTypes.length !== 2) return null;
|
|
44579
|
+
const a = argTypes[0];
|
|
44580
|
+
if (a.kind === "number" || a.kind === "boolean" || a.kind === "tensor" || a.kind === "complex_or_number")
|
|
44581
|
+
return [{ kind: "tensor", isComplex: false }];
|
|
44582
|
+
return null;
|
|
44583
|
+
},
|
|
44584
|
+
apply: (args) => {
|
|
44585
|
+
const X = args[0];
|
|
44586
|
+
const cls = toString(args[1]).toLowerCase();
|
|
44587
|
+
const makeView = TYPECAST_VIEWS[cls];
|
|
44588
|
+
if (!makeView)
|
|
44589
|
+
throw new RuntimeError(`typecast: unsupported class '${cls}'`);
|
|
44590
|
+
let src;
|
|
44591
|
+
if (isRuntimeNumber(X)) src = Float64Array.of(X);
|
|
44592
|
+
else if (isRuntimeLogical(X)) src = Float64Array.of(X ? 1 : 0);
|
|
44593
|
+
else if (isRuntimeTensor(X)) {
|
|
44594
|
+
if (X.imag)
|
|
44595
|
+
throw new RuntimeError("typecast: complex input not supported");
|
|
44596
|
+
src = X.data;
|
|
44597
|
+
} else {
|
|
44598
|
+
throw new RuntimeError("typecast: X must be a numeric array");
|
|
44599
|
+
}
|
|
44600
|
+
const buffer = src.buffer.slice(
|
|
44601
|
+
src.byteOffset,
|
|
44602
|
+
src.byteOffset + src.byteLength
|
|
44603
|
+
);
|
|
44604
|
+
const view = makeView(buffer);
|
|
44605
|
+
const out = allocFloat64Array(view.length);
|
|
44606
|
+
for (let i = 0; i < view.length; i++) out[i] = view[i];
|
|
44607
|
+
return RTV.tensor(out, [1, view.length]);
|
|
44608
|
+
}
|
|
44609
|
+
}
|
|
44610
|
+
]
|
|
44611
|
+
});
|
|
44612
|
+
function jsonEncodeNumber(x) {
|
|
44613
|
+
return Number.isFinite(x) ? String(x) : "null";
|
|
44614
|
+
}
|
|
44615
|
+
function jsonEncodeTensor(v) {
|
|
44616
|
+
if (v.imag && !imagAllZero(v.imag))
|
|
44617
|
+
throw new RuntimeError("jsonencode: complex values are not supported");
|
|
44618
|
+
const data = v.data;
|
|
44619
|
+
const n = data.length;
|
|
44620
|
+
const enc = v._isLogical ? (x) => x ? "true" : "false" : jsonEncodeNumber;
|
|
44621
|
+
const shape = v.shape ?? [n];
|
|
44622
|
+
if (n === 1 && shape.every((s) => s === 1)) return enc(data[0]);
|
|
44623
|
+
if (n === 0) return "[]";
|
|
44624
|
+
const nonSingleton = shape.filter((s) => s > 1).length;
|
|
44625
|
+
if (shape.length <= 1 || nonSingleton <= 1) {
|
|
44626
|
+
const parts2 = [];
|
|
44627
|
+
for (let i = 0; i < n; i++) parts2.push(enc(data[i]));
|
|
44628
|
+
return "[" + parts2.join(",") + "]";
|
|
44629
|
+
}
|
|
44630
|
+
if (shape.length === 2) {
|
|
44631
|
+
const [m, cols] = shape;
|
|
44632
|
+
const rows = [];
|
|
44633
|
+
for (let i = 0; i < m; i++) {
|
|
44634
|
+
const rowParts = [];
|
|
44635
|
+
for (let j = 0; j < cols; j++) rowParts.push(enc(data[j * m + i]));
|
|
44636
|
+
rows.push("[" + rowParts.join(",") + "]");
|
|
44637
|
+
}
|
|
44638
|
+
return "[" + rows.join(",") + "]";
|
|
44639
|
+
}
|
|
44640
|
+
const parts = [];
|
|
44641
|
+
for (let i = 0; i < n; i++) parts.push(enc(data[i]));
|
|
44642
|
+
return "[" + parts.join(",") + "]";
|
|
44643
|
+
}
|
|
44644
|
+
function jsonEncodeCell(v) {
|
|
44645
|
+
if (v.data.length === 0) return "[]";
|
|
44646
|
+
return "[" + v.data.map(jsonEncodeValue).join(",") + "]";
|
|
44647
|
+
}
|
|
44648
|
+
function jsonEncodeStruct(v) {
|
|
44649
|
+
const parts = [];
|
|
44650
|
+
for (const [key, val] of v.fields) {
|
|
44651
|
+
parts.push(JSON.stringify(key) + ":" + jsonEncodeValue(val));
|
|
44652
|
+
}
|
|
44653
|
+
return "{" + parts.join(",") + "}";
|
|
44654
|
+
}
|
|
44655
|
+
function jsonEncodeValue(v) {
|
|
44656
|
+
if (v === void 0 || v === null) return "null";
|
|
44657
|
+
if (isRuntimeNumber(v)) return jsonEncodeNumber(v);
|
|
44658
|
+
if (isRuntimeLogical(v)) return v ? "true" : "false";
|
|
44659
|
+
if (isRuntimeString(v)) return JSON.stringify(v);
|
|
44660
|
+
if (isRuntimeChar(v)) {
|
|
44661
|
+
if (v.shape && v.shape.length === 2 && v.shape[0] > 1) {
|
|
44662
|
+
const rows = v.shape[0];
|
|
44663
|
+
const cols = v.shape[1];
|
|
44664
|
+
const out = [];
|
|
44665
|
+
for (let i = 0; i < rows; i++) {
|
|
44666
|
+
let row = "";
|
|
44667
|
+
for (let j = 0; j < cols; j++) row += v.value[j * rows + i] ?? "";
|
|
44668
|
+
out.push(JSON.stringify(row));
|
|
44669
|
+
}
|
|
44670
|
+
return "[" + out.join(",") + "]";
|
|
44671
|
+
}
|
|
44672
|
+
return JSON.stringify(v.value);
|
|
44673
|
+
}
|
|
44674
|
+
if (isRuntimeComplexNumber(v)) {
|
|
44675
|
+
if (v.im !== 0)
|
|
44676
|
+
throw new RuntimeError("jsonencode: complex values are not supported");
|
|
44677
|
+
return jsonEncodeNumber(v.re);
|
|
44678
|
+
}
|
|
44679
|
+
if (isRuntimeTensor(v)) return jsonEncodeTensor(v);
|
|
44680
|
+
if (isRuntimeCell(v)) return jsonEncodeCell(v);
|
|
44681
|
+
if (isRuntimeStruct(v)) return jsonEncodeStruct(v);
|
|
44682
|
+
if (isRuntimeStructArray(v))
|
|
44683
|
+
return "[" + v.elements.map(jsonEncodeStruct).join(",") + "]";
|
|
44684
|
+
throw new RuntimeError("jsonencode: unsupported value type");
|
|
44685
|
+
}
|
|
44686
|
+
defineBuiltin({
|
|
44687
|
+
name: "jsonencode",
|
|
44688
|
+
help: {
|
|
44689
|
+
signatures: ["txt = jsonencode(V)"],
|
|
44690
|
+
description: "Encode value V (struct, cell, char/string, logical, or numeric array) as a JSON-formatted char row vector."
|
|
44691
|
+
},
|
|
44692
|
+
cases: [
|
|
44693
|
+
{
|
|
44694
|
+
match: (argTypes) => {
|
|
44695
|
+
if (argTypes.length < 1) return null;
|
|
44696
|
+
return [{ kind: "char" }];
|
|
44697
|
+
},
|
|
44698
|
+
apply: (args) => RTV.char(jsonEncodeValue(args[0]))
|
|
44699
|
+
}
|
|
44700
|
+
]
|
|
44701
|
+
});
|
|
44559
44702
|
function idivideMode(args) {
|
|
44560
44703
|
if (args.length < 3) return "fix";
|
|
44561
44704
|
const m = args[2];
|
|
@@ -51289,7 +51432,15 @@ function dispatchPlotBuiltin(name, args, instructions, state) {
|
|
|
51289
51432
|
return true;
|
|
51290
51433
|
// ── Graphics ops: figure / labels / hold / layout ──────────────
|
|
51291
51434
|
case "figure": {
|
|
51292
|
-
|
|
51435
|
+
let handle;
|
|
51436
|
+
if (args.length > 0) {
|
|
51437
|
+
handle = toNumber(args[0]);
|
|
51438
|
+
state.maxFigureHandle = Math.max(state.maxFigureHandle ?? 0, handle);
|
|
51439
|
+
} else {
|
|
51440
|
+
handle = (state.maxFigureHandle ?? 0) + 1;
|
|
51441
|
+
state.maxFigureHandle = handle;
|
|
51442
|
+
}
|
|
51443
|
+
state.currentFigureHandle = handle;
|
|
51293
51444
|
plotInstr(instructions, { type: "set_figure_handle", handle });
|
|
51294
51445
|
return true;
|
|
51295
51446
|
}
|
|
@@ -52236,6 +52387,10 @@ var SPECIAL_BUILTIN_NAMES = [
|
|
|
52236
52387
|
"stream2",
|
|
52237
52388
|
"ishold",
|
|
52238
52389
|
"figure",
|
|
52390
|
+
"drawuihtml",
|
|
52391
|
+
"registeruihtmlcallback",
|
|
52392
|
+
"sendEventToHTMLSource",
|
|
52393
|
+
"uigridlayout",
|
|
52239
52394
|
"subplot",
|
|
52240
52395
|
"tiledlayout",
|
|
52241
52396
|
"nexttile",
|
|
@@ -53585,15 +53740,68 @@ function registerSpecialBuiltins(rt) {
|
|
|
53585
53740
|
return rt.ishold();
|
|
53586
53741
|
});
|
|
53587
53742
|
registerSpecial("figure", (nargout, args) => {
|
|
53588
|
-
const handle = args.length > 0 ? args[0] : 1;
|
|
53589
53743
|
dispatchPlotBuiltin(
|
|
53590
53744
|
"figure",
|
|
53591
53745
|
args.map(ensureRuntimeValue),
|
|
53592
53746
|
rt.plotInstructions,
|
|
53593
53747
|
rt
|
|
53594
53748
|
);
|
|
53595
|
-
return nargout >= 1 ? RTV.num(
|
|
53749
|
+
return nargout >= 1 ? RTV.num(rt.currentFigureHandle) : void 0;
|
|
53750
|
+
});
|
|
53751
|
+
let uihtmlSeq = 0;
|
|
53752
|
+
registerSpecial("drawuihtml", (nargout, args) => {
|
|
53753
|
+
const html = args.length > 0 ? toString(ensureRuntimeValue(args[0])) : "";
|
|
53754
|
+
const data = args.length > 1 ? toString(ensureRuntimeValue(args[1])) : void 0;
|
|
53755
|
+
uihtmlSeq += 1;
|
|
53756
|
+
const id = "uh" + uihtmlSeq;
|
|
53757
|
+
rt.plotInstructions.push({
|
|
53758
|
+
type: "uihtml",
|
|
53759
|
+
id,
|
|
53760
|
+
html,
|
|
53761
|
+
...data !== void 0 ? { data } : {}
|
|
53762
|
+
});
|
|
53763
|
+
return nargout >= 1 ? RTV.char(id) : void 0;
|
|
53764
|
+
});
|
|
53765
|
+
registerSpecial("registeruihtmlcallback", (_nargout, args) => {
|
|
53766
|
+
if (args.length < 3) return void 0;
|
|
53767
|
+
const compId = toString(ensureRuntimeValue(args[0]));
|
|
53768
|
+
const eventType = toString(ensureRuntimeValue(args[1]));
|
|
53769
|
+
const fcn = ensureRuntimeValue(args[2]);
|
|
53770
|
+
if (eventType !== "HTMLEventReceived" && eventType !== "DataChanged") {
|
|
53771
|
+
return void 0;
|
|
53772
|
+
}
|
|
53773
|
+
if (!isRuntimeFunction(fcn)) return void 0;
|
|
53774
|
+
const entry = rt.uihtmlCallbacks.get(compId) ?? {};
|
|
53775
|
+
const prev = entry[eventType];
|
|
53776
|
+
if (prev) decref(rt, prev);
|
|
53777
|
+
incref(fcn);
|
|
53778
|
+
entry[eventType] = fcn;
|
|
53779
|
+
rt.uihtmlCallbacks.set(compId, entry);
|
|
53780
|
+
return void 0;
|
|
53781
|
+
});
|
|
53782
|
+
registerSpecial("sendEventToHTMLSource", (_nargout, args) => {
|
|
53783
|
+
if (args.length < 2) {
|
|
53784
|
+
throw new RuntimeError("sendEventToHTMLSource requires src and name");
|
|
53785
|
+
}
|
|
53786
|
+
const src = ensureRuntimeValue(args[0]);
|
|
53787
|
+
if (!isRuntimeStruct(src) || !src.fields.has("ComponentId")) {
|
|
53788
|
+
throw new RuntimeError(
|
|
53789
|
+
"sendEventToHTMLSource: src must be the component passed to the callback"
|
|
53790
|
+
);
|
|
53791
|
+
}
|
|
53792
|
+
const compId = toString(src.fields.get("ComponentId"));
|
|
53793
|
+
const name = toString(ensureRuntimeValue(args[1]));
|
|
53794
|
+
const dataArg = args.length > 2 ? ensureRuntimeValue(args[2]) : RTV.num(0);
|
|
53795
|
+
const dataJson = toString(
|
|
53796
|
+
ensureRuntimeValue(rt.dispatch("jsonencode", 1, [dataArg]))
|
|
53797
|
+
);
|
|
53798
|
+
rt.onHtmlSourceEvent?.(compId, name, dataJson);
|
|
53799
|
+
return void 0;
|
|
53596
53800
|
});
|
|
53801
|
+
registerSpecial(
|
|
53802
|
+
"uigridlayout",
|
|
53803
|
+
(nargout) => nargout >= 1 ? RTV.num(0) : void 0
|
|
53804
|
+
);
|
|
53597
53805
|
const PLOT_RETURNS_ZERO = ["subplot", "tiledlayout", "nexttile", "legend"];
|
|
53598
53806
|
for (const name of PLOT_RETURNS_ZERO) {
|
|
53599
53807
|
registerSpecial(name, (nargout, args) => {
|
|
@@ -56434,6 +56642,19 @@ var figuresReducer = (state, action) => {
|
|
|
56434
56642
|
switch (action.type) {
|
|
56435
56643
|
case "set_figure_handle":
|
|
56436
56644
|
return { ...state, currentHandle: action.handle };
|
|
56645
|
+
case "uihtml": {
|
|
56646
|
+
const fig = ensureFig(state);
|
|
56647
|
+
return {
|
|
56648
|
+
...state,
|
|
56649
|
+
figs: {
|
|
56650
|
+
...state.figs,
|
|
56651
|
+
[state.currentHandle]: {
|
|
56652
|
+
...fig,
|
|
56653
|
+
uihtml: { id: action.id, html: action.html, data: action.data }
|
|
56654
|
+
}
|
|
56655
|
+
}
|
|
56656
|
+
};
|
|
56657
|
+
}
|
|
56437
56658
|
case "set_hold":
|
|
56438
56659
|
return updateAxes(state, { holdOn: action.value });
|
|
56439
56660
|
case "plot": {
|
|
@@ -56874,6 +57095,7 @@ var Runtime = class _Runtime {
|
|
|
56874
57095
|
this.profilingEnabled = !!options.profile;
|
|
56875
57096
|
this.fileIO = options.fileIO;
|
|
56876
57097
|
this.system = options.system;
|
|
57098
|
+
this.onHtmlSourceEvent = options.onHtmlSourceEvent;
|
|
56877
57099
|
if (options.initialHoldState) {
|
|
56878
57100
|
this.holdState = options.initialHoldState;
|
|
56879
57101
|
}
|
|
@@ -56892,9 +57114,24 @@ var Runtime = class _Runtime {
|
|
|
56892
57114
|
plotInstructions = [];
|
|
56893
57115
|
variableValues = {};
|
|
56894
57116
|
holdState = false;
|
|
57117
|
+
/** Figure-handle tracking for MATLAB-style `figure` allocation: `figure`
|
|
57118
|
+
* with no argument creates a new figure (`maxFigureHandle + 1`). */
|
|
57119
|
+
currentFigureHandle = 0;
|
|
57120
|
+
maxFigureHandle = 0;
|
|
56895
57121
|
/** Monotonic id source for graphics handles whose trace can be live-updated
|
|
56896
57122
|
* via `set` / `update_trace` (e.g. lines returned by `line`). */
|
|
56897
57123
|
graphicsIdCounter = 1;
|
|
57124
|
+
/** Reverse channel for `uihtml` components (HTML → MATLAB). Maps a component
|
|
57125
|
+
* id (the `uihtml` instruction's `id`) to the callback handles registered
|
|
57126
|
+
* for it. When non-empty after a run, the host keeps this runtime alive so
|
|
57127
|
+
* iframe events can re-enter the interpreter and invoke these handles.
|
|
57128
|
+
* Handles are incref'd while stored (see registeruihtmlcallback). */
|
|
57129
|
+
uihtmlCallbacks = /* @__PURE__ */ new Map();
|
|
57130
|
+
/** Hook invoked by `sendEventToHTMLSource(src, name, data)` to push an event
|
|
57131
|
+
* from MATLAB back to a uihtml component's page. `dataJson` is the data
|
|
57132
|
+
* already `jsonencode`d. Set from ExecOptions; the host forwards it to the
|
|
57133
|
+
* iframe. */
|
|
57134
|
+
onHtmlSourceEvent;
|
|
56898
57135
|
// tiledlayout/nexttile state. Reset by tiledlayout, advanced by nexttile.
|
|
56899
57136
|
// mode: "fixed" uses the rows/cols verbatim; "flow"/"vertical"/"horizontal"
|
|
56900
57137
|
// grow the grid as tiles are added.
|
|
@@ -57027,6 +57264,15 @@ var Runtime = class _Runtime {
|
|
|
57027
57264
|
}
|
|
57028
57265
|
}
|
|
57029
57266
|
// ── Builtin initialization ──────────────────────────────────────────
|
|
57267
|
+
/** Register the implicit default figure (handle 1) the first time a graphics
|
|
57268
|
+
* op runs without an explicit `figure`, so a later no-arg `figure` allocates
|
|
57269
|
+
* a new handle instead of reusing 1 (MATLAB semantics). */
|
|
57270
|
+
ensureCurrentFigure() {
|
|
57271
|
+
if (!this.currentFigureHandle) {
|
|
57272
|
+
this.currentFigureHandle = 1;
|
|
57273
|
+
this.maxFigureHandle = Math.max(this.maxFigureHandle, 1);
|
|
57274
|
+
}
|
|
57275
|
+
}
|
|
57030
57276
|
initBuiltins() {
|
|
57031
57277
|
registerSpecialBuiltins(this);
|
|
57032
57278
|
for (const name of PLOT_DISPATCH_NAMES) {
|
|
@@ -57172,6 +57418,22 @@ var Runtime = class _Runtime {
|
|
|
57172
57418
|
this.builtins["stream2"] = (_nargout, args) => {
|
|
57173
57419
|
return stream2Call(args.map(ensureRuntimeValue));
|
|
57174
57420
|
};
|
|
57421
|
+
const NO_AUTO_FIGURE = /* @__PURE__ */ new Set(["figure", "close", "clf", "cla", "axis"]);
|
|
57422
|
+
for (const name of [
|
|
57423
|
+
...PLOT_DISPATCH_NAMES,
|
|
57424
|
+
"fplot",
|
|
57425
|
+
"fplot3",
|
|
57426
|
+
"streamline",
|
|
57427
|
+
"stream2"
|
|
57428
|
+
]) {
|
|
57429
|
+
if (NO_AUTO_FIGURE.has(name)) continue;
|
|
57430
|
+
const orig = this.builtins[name];
|
|
57431
|
+
if (!orig) continue;
|
|
57432
|
+
this.builtins[name] = (n, a) => {
|
|
57433
|
+
this.ensureCurrentFigure();
|
|
57434
|
+
return orig(n, a);
|
|
57435
|
+
};
|
|
57436
|
+
}
|
|
57175
57437
|
}
|
|
57176
57438
|
profileEnter(key) {
|
|
57177
57439
|
if (!this.profilingEnabled) return;
|
|
@@ -63616,6 +63878,10 @@ end
|
|
|
63616
63878
|
{
|
|
63617
63879
|
name: "isIllConditioned.m",
|
|
63618
63880
|
source: "function result = isIllConditioned(dA)\n result = dA.isIllCond;\nend\n"
|
|
63881
|
+
},
|
|
63882
|
+
{
|
|
63883
|
+
name: "uihtml.m",
|
|
63884
|
+
source: "classdef uihtml < handle\n %UIHTML Create an HTML UI component (numbl subset).\n % H = UIHTML('HTMLSource', HTML) renders the self-contained HTML string\n % HTML in the figure pane. An optional leading parent argument (e.g. a\n % figure) is accepted and ignored. Supported name-value options:\n % HTMLSource, Data, Position.\n %\n % H = UIHTML('HTMLSource', HTML, 'Data', X) also sends X into the page.\n % Mirroring MATLAB, X is encoded with jsonencode, parsed in the page with\n % JSON.parse, and set on the JavaScript `htmlComponent.Data` object,\n % firing any \"DataChanged\" listener registered in the page's\n % `function setup(htmlComponent)`. To update the data after construction,\n % set H.Data and call show(H) (numbl re-renders the component):\n %\n % h = uihtml('HTMLSource', html, 'Data', struct('n', 1));\n % h.Data = struct('n', 2);\n % show(h);\n %\n % numbl currently supports HTMLSource given as an HTML markup string (a\n % single self-contained document). HTML file paths and supporting files\n % are not yet supported.\n %\n % The reverse channel (page -> MATLAB) is supported for the IDE:\n % HTMLEventReceivedFcn fires when JS calls\n % htmlComponent.sendEventToMATLAB(name,data); inside the callback use\n % sendEventToHTMLSource(src,name,data) to send back to the page.\n % DataChangedFcn fires when JS sets htmlComponent.Data. Register callbacks\n % at construction (name-value) since numbl renders at construction.\n properties\n HTMLSource = ''\n Data = []\n Position = [100 100 100 100]\n HTMLEventReceivedFcn = []\n DataChangedFcn = []\n end\n methods\n function obj = uihtml(varargin)\n args = varargin;\n % Ignore an optional leading parent argument (anything that is not\n % a name-value name string).\n if numel(args) >= 1 && ~(ischar(args{1}) || isstring(args{1}))\n args = args(2:end);\n end\n for i = 1:2:numel(args) - 1\n name = args{i};\n val = args{i + 1};\n if strcmpi(name, 'HTMLSource')\n obj.HTMLSource = val;\n elseif strcmpi(name, 'Data')\n obj.Data = val;\n elseif strcmpi(name, 'Position')\n obj.Position = val;\n elseif strcmpi(name, 'HTMLEventReceivedFcn')\n obj.HTMLEventReceivedFcn = val;\n elseif strcmpi(name, 'DataChangedFcn')\n obj.DataChangedFcn = val;\n end\n end\n render(obj);\n end\n\n function show(obj)\n render(obj);\n end\n\n function render(obj)\n if isempty(obj.HTMLSource)\n return;\n end\n if isempty(obj.Data)\n id = drawuihtml(obj.HTMLSource);\n else\n id = drawuihtml(obj.HTMLSource, jsonencode(obj.Data));\n end\n if ~isempty(obj.HTMLEventReceivedFcn)\n registeruihtmlcallback(id, 'HTMLEventReceived', ...\n obj.HTMLEventReceivedFcn);\n end\n if ~isempty(obj.DataChangedFcn)\n registeruihtmlcallback(id, 'DataChanged', obj.DataChangedFcn);\n end\n end\n end\nend\n"
|
|
63619
63885
|
}
|
|
63620
63886
|
];
|
|
63621
63887
|
|
|
@@ -91570,6 +91836,64 @@ function registerExecutorsForOpt(registry3, opt) {
|
|
|
91570
91836
|
}
|
|
91571
91837
|
}
|
|
91572
91838
|
|
|
91839
|
+
// src/numbl-core/runtime/uihtmlSession.ts
|
|
91840
|
+
function createUihtmlSession(rt, activeSpecials, onDrawnow) {
|
|
91841
|
+
function invokeHandle(fn, args) {
|
|
91842
|
+
if (fn.jsFn) {
|
|
91843
|
+
if (fn.jsFnExpectsNargout) fn.jsFn(0, ...args);
|
|
91844
|
+
else fn.jsFn(...args);
|
|
91845
|
+
return;
|
|
91846
|
+
}
|
|
91847
|
+
rt.dispatch(fn.name, 0, args);
|
|
91848
|
+
}
|
|
91849
|
+
function dispatchEvent(compId, eventType, payload) {
|
|
91850
|
+
const entry = rt.uihtmlCallbacks.get(compId);
|
|
91851
|
+
const fn = entry?.[eventType];
|
|
91852
|
+
if (!fn || !isRuntimeFunction(fn)) return;
|
|
91853
|
+
const saved = /* @__PURE__ */ new Map();
|
|
91854
|
+
for (const name of SPECIAL_BUILTIN_NAMES) {
|
|
91855
|
+
const ex = getIBuiltin(name);
|
|
91856
|
+
if (ex) saved.set(name, ex);
|
|
91857
|
+
}
|
|
91858
|
+
for (const ib of activeSpecials.values()) registerDynamicIBuiltin(ib);
|
|
91859
|
+
pushCurrentRuntime(rt);
|
|
91860
|
+
const before = rt.plotInstructions.length;
|
|
91861
|
+
try {
|
|
91862
|
+
const src = RTV.struct({ ComponentId: RTV.char(compId) });
|
|
91863
|
+
const dataRV = convertJsonValue(payload.data);
|
|
91864
|
+
const event = eventType === "HTMLEventReceived" ? RTV.struct({
|
|
91865
|
+
HTMLEventName: RTV.char(payload.name ?? ""),
|
|
91866
|
+
HTMLEventData: dataRV,
|
|
91867
|
+
Source: src,
|
|
91868
|
+
EventName: RTV.char("HTMLEventReceived")
|
|
91869
|
+
}) : RTV.struct({
|
|
91870
|
+
Data: dataRV,
|
|
91871
|
+
PreviousData: RTV.tensor(new Float64Array(0), [0, 0]),
|
|
91872
|
+
Source: src,
|
|
91873
|
+
EventName: RTV.char("DataChanged")
|
|
91874
|
+
});
|
|
91875
|
+
invokeHandle(fn, [src, event]);
|
|
91876
|
+
} finally {
|
|
91877
|
+
popCurrentRuntime(rt);
|
|
91878
|
+
for (const ib of saved.values()) registerDynamicIBuiltin(ib);
|
|
91879
|
+
}
|
|
91880
|
+
const newInstrs = rt.plotInstructions.slice(before);
|
|
91881
|
+
if (newInstrs.length && onDrawnow) onDrawnow(newInstrs);
|
|
91882
|
+
}
|
|
91883
|
+
function dispose() {
|
|
91884
|
+
for (const entry of rt.uihtmlCallbacks.values()) {
|
|
91885
|
+
if (entry.HTMLEventReceived) decref(rt, entry.HTMLEventReceived);
|
|
91886
|
+
if (entry.DataChanged) decref(rt, entry.DataChanged);
|
|
91887
|
+
}
|
|
91888
|
+
rt.uihtmlCallbacks.clear();
|
|
91889
|
+
}
|
|
91890
|
+
return {
|
|
91891
|
+
hasCallbacks: () => rt.uihtmlCallbacks.size > 0,
|
|
91892
|
+
dispatchEvent,
|
|
91893
|
+
dispose
|
|
91894
|
+
};
|
|
91895
|
+
}
|
|
91896
|
+
|
|
91573
91897
|
// src/numbl-core/executeCode.ts
|
|
91574
91898
|
globalThis.FloatXArray = Float64Array;
|
|
91575
91899
|
var SHIM_SEARCH_PATH = "__numbl_shims__";
|
|
@@ -92021,6 +92345,18 @@ ${sections.join("\n\n")}` : `// No ${label} generated`;
|
|
|
92021
92345
|
variableValues: interpreter.getVariableValues(),
|
|
92022
92346
|
holdState: rt.holdState
|
|
92023
92347
|
};
|
|
92348
|
+
if (rt.uihtmlCallbacks.size > 0) {
|
|
92349
|
+
const activeSpecials = /* @__PURE__ */ new Map();
|
|
92350
|
+
for (const name of SPECIAL_BUILTIN_NAMES) {
|
|
92351
|
+
const ex = getIBuiltin(name);
|
|
92352
|
+
if (ex) activeSpecials.set(name, ex);
|
|
92353
|
+
}
|
|
92354
|
+
result.uihtmlSession = createUihtmlSession(
|
|
92355
|
+
rt,
|
|
92356
|
+
activeSpecials,
|
|
92357
|
+
options.onDrawnow
|
|
92358
|
+
);
|
|
92359
|
+
}
|
|
92024
92360
|
if (options.profile) {
|
|
92025
92361
|
result.profileData = {
|
|
92026
92362
|
executionTimeMs,
|
|
@@ -10,6 +10,8 @@ import type { FileIOAdapter } from "./fileIOAdapter.js";
|
|
|
10
10
|
import type { SystemAdapter } from "./systemAdapter.js";
|
|
11
11
|
import type { WorkspaceFile } from "../numbl-core/workspace/index.js";
|
|
12
12
|
import type { NativeBridge } from "./workspace/index.js";
|
|
13
|
+
import { type UihtmlSession } from "./runtime/uihtmlSession.js";
|
|
14
|
+
export type { UihtmlSession } from "./runtime/uihtmlSession.js";
|
|
13
15
|
export interface ExecOptions {
|
|
14
16
|
onOutput?: (text: string) => void;
|
|
15
17
|
onDrawnow?: (plotInstructions: PlotInstruction[]) => void;
|
|
@@ -48,6 +50,10 @@ export interface ExecOptions {
|
|
|
48
50
|
implicitCwdPath?: string | null;
|
|
49
51
|
/** SharedArrayBuffer for cooperative cancellation. Int32[0] != 0 means cancelled. */
|
|
50
52
|
cancelSAB?: SharedArrayBuffer;
|
|
53
|
+
/** Hook for `sendEventToHTMLSource(src,name,data)` — pushes an event from a
|
|
54
|
+
* uihtml callback back to the component's page (MATLAB → JS). `dataJson` is
|
|
55
|
+
* the data already `jsonencode`d. The host forwards it to the iframe. */
|
|
56
|
+
onHtmlSourceEvent?: (compId: string, name: string, dataJson: string) => void;
|
|
51
57
|
}
|
|
52
58
|
export interface BuiltinProfileEntry {
|
|
53
59
|
totalTimeMs: number;
|
|
@@ -94,5 +100,9 @@ export interface ExecResult {
|
|
|
94
100
|
workspaceFiles?: WorkspaceFile[];
|
|
95
101
|
/** Final implicit cwd path (for REPL persistence across commands). */
|
|
96
102
|
implicitCwdPath?: string | null;
|
|
103
|
+
/** Present when the run left `uihtml` reverse-channel callbacks registered.
|
|
104
|
+
* The host retains this to dispatch later iframe events into the still-live
|
|
105
|
+
* interpreter (see UihtmlSession). Absent for normal runs (no overhead). */
|
|
106
|
+
uihtmlSession?: UihtmlSession;
|
|
97
107
|
}
|
|
98
108
|
export declare function executeCode(source: string, options?: ExecOptions, workspaceFiles?: WorkspaceFile[], mainFileName?: string, searchPaths?: string[], nativeBridge?: NativeBridge): ExecResult;
|
|
@@ -42,6 +42,12 @@ import type { PlotInstruction } from "../../graphics/types.js";
|
|
|
42
42
|
export interface PlotDispatchState {
|
|
43
43
|
holdState: boolean;
|
|
44
44
|
tiledLayoutState: TiledLayoutState | null;
|
|
45
|
+
/** Current figure handle (0 = none created yet). */
|
|
46
|
+
currentFigureHandle?: number;
|
|
47
|
+
/** Highest figure handle allocated so far. `figure` with no argument
|
|
48
|
+
* creates a NEW figure with handle `maxFigureHandle + 1` (MATLAB
|
|
49
|
+
* semantics), rather than always reusing handle 1. */
|
|
50
|
+
maxFigureHandle?: number;
|
|
45
51
|
}
|
|
46
52
|
/** Active tiled-layout grid. `mode` controls how the grid grows: in
|
|
47
53
|
* `flow` (default), nexttile expands rows/cols to fit; `vertical` and
|
|
@@ -32,9 +32,27 @@ export declare class Runtime {
|
|
|
32
32
|
plotInstructions: PlotInstruction[];
|
|
33
33
|
variableValues: Record<string, RuntimeValue>;
|
|
34
34
|
holdState: boolean;
|
|
35
|
+
/** Figure-handle tracking for MATLAB-style `figure` allocation: `figure`
|
|
36
|
+
* with no argument creates a new figure (`maxFigureHandle + 1`). */
|
|
37
|
+
currentFigureHandle: number;
|
|
38
|
+
maxFigureHandle: number;
|
|
35
39
|
/** Monotonic id source for graphics handles whose trace can be live-updated
|
|
36
40
|
* via `set` / `update_trace` (e.g. lines returned by `line`). */
|
|
37
41
|
graphicsIdCounter: number;
|
|
42
|
+
/** Reverse channel for `uihtml` components (HTML → MATLAB). Maps a component
|
|
43
|
+
* id (the `uihtml` instruction's `id`) to the callback handles registered
|
|
44
|
+
* for it. When non-empty after a run, the host keeps this runtime alive so
|
|
45
|
+
* iframe events can re-enter the interpreter and invoke these handles.
|
|
46
|
+
* Handles are incref'd while stored (see registeruihtmlcallback). */
|
|
47
|
+
uihtmlCallbacks: Map<string, {
|
|
48
|
+
HTMLEventReceived?: RuntimeValue;
|
|
49
|
+
DataChanged?: RuntimeValue;
|
|
50
|
+
}>;
|
|
51
|
+
/** Hook invoked by `sendEventToHTMLSource(src, name, data)` to push an event
|
|
52
|
+
* from MATLAB back to a uihtml component's page. `dataJson` is the data
|
|
53
|
+
* already `jsonencode`d. Set from ExecOptions; the host forwards it to the
|
|
54
|
+
* iframe. */
|
|
55
|
+
onHtmlSourceEvent?: (compId: string, name: string, dataJson: string) => void;
|
|
38
56
|
tiledLayoutState: {
|
|
39
57
|
rows: number;
|
|
40
58
|
cols: number;
|
|
@@ -142,6 +160,10 @@ export declare class Runtime {
|
|
|
142
160
|
* every value adopted into the scope is decref'd. Used by `execStmt`
|
|
143
161
|
* to bound the lifetime of expression transients to one statement. */
|
|
144
162
|
withScope<T>(fn: () => T): T;
|
|
163
|
+
/** Register the implicit default figure (handle 1) the first time a graphics
|
|
164
|
+
* op runs without an explicit `figure`, so a later no-arg `figure` allocates
|
|
165
|
+
* a new handle instead of reusing 1 (MATLAB semantics). */
|
|
166
|
+
ensureCurrentFigure(): void;
|
|
145
167
|
private initBuiltins;
|
|
146
168
|
profileEnter(key: string): void;
|
|
147
169
|
profileLeave(): void;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* uihtml reverse channel (HTML → MATLAB) for a finished run.
|
|
3
|
+
*
|
|
4
|
+
* When a script creates a `uihtml` component with a callback
|
|
5
|
+
* (HTMLEventReceivedFcn / DataChangedFcn), the run leaves entries in
|
|
6
|
+
* `rt.uihtmlCallbacks`. `executeCode` then builds a `UihtmlSession` and returns
|
|
7
|
+
* it so the host (the worker) can keep the interpreter alive and re-enter it
|
|
8
|
+
* when an event arrives from the iframe — mirroring MATLAB, where the script
|
|
9
|
+
* returns but its callbacks keep firing.
|
|
10
|
+
*
|
|
11
|
+
* Dispatch re-activates the run's own special-builtin closures (captured at
|
|
12
|
+
* end-of-run, NOT re-registered — re-registering would reset their counters)
|
|
13
|
+
* and pushes the runtime so `disp`, plotting, and `sendEventToHTMLSource`
|
|
14
|
+
* inside the callback reach this runtime. This mirrors executeCode's own
|
|
15
|
+
* save/restore of SPECIAL_BUILTIN_NAMES.
|
|
16
|
+
*/
|
|
17
|
+
import type { Runtime } from "./runtime.js";
|
|
18
|
+
import type { PlotInstruction } from "../../graphics/types.js";
|
|
19
|
+
import { type IBuiltin } from "../interpreter/builtins/types.js";
|
|
20
|
+
export interface UihtmlSession {
|
|
21
|
+
/** True while at least one component still has a registered callback. */
|
|
22
|
+
hasCallbacks(): boolean;
|
|
23
|
+
/** Dispatch an event from a component's page into MATLAB. `eventType` is
|
|
24
|
+
* "HTMLEventReceived" (JS `sendEventToMATLAB`) or "DataChanged" (JS set
|
|
25
|
+
* `htmlComponent.Data`). `payload.data` is the structured-clone'd JS value.
|
|
26
|
+
* New plot output is flushed via `onDrawnow`; outgoing
|
|
27
|
+
* `sendEventToHTMLSource` calls go through `rt.onHtmlSourceEvent`. */
|
|
28
|
+
dispatchEvent(compId: string, eventType: "HTMLEventReceived" | "DataChanged", payload: {
|
|
29
|
+
name?: string;
|
|
30
|
+
data: unknown;
|
|
31
|
+
}): void;
|
|
32
|
+
/** Release the retained callback handles (and their refs). */
|
|
33
|
+
dispose(): void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Build a session over a runtime that registered uihtml callbacks.
|
|
37
|
+
* `activeSpecials` is a snapshot of this runtime's special-builtin closures,
|
|
38
|
+
* captured at end-of-run while they are still installed globally.
|
|
39
|
+
*/
|
|
40
|
+
export declare function createUihtmlSession(rt: Runtime, activeSpecials: Map<string, IBuiltin>, onDrawnow?: (plotInstructions: PlotInstruction[]) => void): UihtmlSession;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Numbl version, used for JIT disk cache invalidation. */
|
|
2
|
-
export declare const NUMBL_VERSION = "0.4.
|
|
2
|
+
export declare const NUMBL_VERSION = "0.4.5";
|