@elicdavis/node-flow 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -805,7 +805,7 @@ var ContextMenu = class _ContextMenu {
805
805
  // src/input.ts
806
806
  var LONG_PRESS_MS = 500;
807
807
  var LONG_PRESS_SLOP = 12;
808
- var PINCH_SENSITIVITY = 50;
808
+ var WHEEL_ZOOM_PER_PIXEL = 5e-4;
809
809
  var SUPPRESS_MOUSE_MS = 600;
810
810
  var MouseObserver = class {
811
811
  #clicked;
@@ -903,13 +903,12 @@ var MouseObserver = class {
903
903
  } else if (event.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
904
904
  dy *= this.#ele.clientHeight;
905
905
  }
906
- const amount = -dy / 100;
907
- if (amount === 0) {
906
+ if (dy === 0) {
908
907
  return;
909
908
  }
910
909
  const anchor = this.#mousePos(event);
911
910
  this.#moveCallback(anchor);
912
- this.#zoomCallback(amount, anchor);
911
+ this.#zoomCallback(Math.exp(-dy * WHEEL_ZOOM_PER_PIXEL), anchor);
913
912
  }
914
913
  #move(event) {
915
914
  if (this.#fromTouch()) {
@@ -985,7 +984,7 @@ var MouseObserver = class {
985
984
  const anchor = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
986
985
  if (this.#pinchDist > 0) {
987
986
  this.#moveCallback(anchor);
988
- this.#zoomCallback((dist - this.#pinchDist) / PINCH_SENSITIVITY, anchor);
987
+ this.#zoomCallback(dist / this.#pinchDist, anchor);
989
988
  }
990
989
  this.#pinchDist = dist;
991
990
  return;
@@ -1680,6 +1679,7 @@ var Port = class {
1680
1679
  #dataTypePopupElement;
1681
1680
  #onConnectionAdded;
1682
1681
  #onConnectionRemoved;
1682
+ #widget = null;
1683
1683
  constructor(node, portType, config) {
1684
1684
  this.#node = node;
1685
1685
  this.#connections = new Array();
@@ -1830,14 +1830,31 @@ var Port = class {
1830
1830
  this.#visibilityMayHaveChanged(was);
1831
1831
  }
1832
1832
  #visibilityMayHaveChanged(was) {
1833
- if (was !== this.hidden() || this.#portType === "INPUTARRAY" /* InputArray */) {
1833
+ if (was !== this.hidden() || this.#portType === "INPUTARRAY" /* InputArray */ || this.#widget !== null) {
1834
1834
  this.#node?.invalidateLayout();
1835
1835
  }
1836
1836
  }
1837
+ setWidget(widget) {
1838
+ this.#widget = widget;
1839
+ this.#node?.invalidateLayout();
1840
+ }
1841
+ widget() {
1842
+ return this.#widget;
1843
+ }
1844
+ /**
1845
+ * The widget stands in for a wire, so it leaves once one is connected.
1846
+ */
1847
+ widgetShown() {
1848
+ return this.#widget !== null && this.#portType !== "OUTPUT" /* Output */ && this.#connections.length === 0 && !this.hidden() && !(this.#widget.Hidden?.() ?? false);
1849
+ }
1837
1850
  /**
1838
- * A port asked to hide still shows while anything is connected to it.
1851
+ * A port asked to hide still shows while anything is connected to it,
1852
+ * and while its node is showing its hidden ports.
1839
1853
  */
1840
1854
  hidden() {
1855
+ return this.tucked() && !(this.#node?.hiddenPortsShown() ?? false);
1856
+ }
1857
+ tucked() {
1841
1858
  return this.#hidden && this.#connections.length === 0;
1842
1859
  }
1843
1860
  addConnectionRemovedListener(callback) {
@@ -1939,117 +1956,6 @@ var Port = class {
1939
1956
  }
1940
1957
  };
1941
1958
 
1942
- // src/styles/stroke.ts
1943
- function StrokeStyleWithFallback(input, fallback) {
1944
- return {
1945
- color: input?.color === void 0 ? fallback?.color : input?.color,
1946
- size: input?.size === void 0 ? fallback?.size : input?.size
1947
- };
1948
- }
1949
- var StrokeStyle = class {
1950
- #size;
1951
- #color;
1952
- constructor(config) {
1953
- this.#size = config?.size === void 0 ? 0.5 : config.size;
1954
- this.#color = config?.color === void 0 ? "black" : config.color;
1955
- }
1956
- setupStyle(ctx, scale) {
1957
- ctx.strokeStyle = this.#color;
1958
- ctx.lineWidth = scale * this.#size;
1959
- }
1960
- setColor(newColor) {
1961
- this.#color = newColor;
1962
- }
1963
- setSize(newSize) {
1964
- this.#size = newSize;
1965
- }
1966
- size() {
1967
- return this.#size;
1968
- }
1969
- };
1970
-
1971
- // src/styles/box.ts
1972
- function BoxStyleWithFallback(input, fallback) {
1973
- return {
1974
- radius: input?.radius === void 0 ? fallback?.radius : input?.radius,
1975
- color: input?.color === void 0 ? fallback?.color : input?.color,
1976
- border: StrokeStyleWithFallback(input?.border, fallback?.border)
1977
- };
1978
- }
1979
- var BoxStyle = class {
1980
- #border;
1981
- #color;
1982
- #radius;
1983
- constructor(config) {
1984
- this.#color = config?.color === void 0 ? "#CCCCCC" : config.color;
1985
- this.#border = config?.border === void 0 ? null : new StrokeStyle(config.border);
1986
- this.#radius = config?.radius === void 0 ? 2 : config.radius;
1987
- }
1988
- #box(ctx, box, scale, radius) {
1989
- ctx.fillStyle = this.#color;
1990
- this.#border?.setupStyle(ctx, scale);
1991
- ctx.beginPath();
1992
- ctx.roundRect(
1993
- box.Position.x,
1994
- box.Position.y,
1995
- box.Size.x,
1996
- box.Size.y,
1997
- radius
1998
- );
1999
- ctx.fill();
2000
- }
2001
- #draw(ctx, box, scale, radius) {
2002
- this.#box(ctx, box, scale, radius);
2003
- ctx.stroke();
2004
- }
2005
- Outline(ctx, box, scale, radius) {
2006
- this.#border?.setupStyle(ctx, scale);
2007
- ctx.beginPath();
2008
- ctx.roundRect(
2009
- box.Position.x,
2010
- box.Position.y,
2011
- box.Size.x,
2012
- box.Size.y,
2013
- radius
2014
- );
2015
- ctx.stroke();
2016
- }
2017
- Draw(ctx, box, scale) {
2018
- this.#draw(ctx, box, scale, this.#radius * scale);
2019
- }
2020
- DrawRoundedTopOnly(ctx, box, scale) {
2021
- this.#draw(ctx, box, scale, [this.#radius * scale * 2, this.#radius * scale * 2, 0, 0]);
2022
- }
2023
- DrawUnderline(ctx, box, scale) {
2024
- this.#box(ctx, box, scale, [this.#radius * scale * 2, this.#radius * scale * 2, 0, 0]);
2025
- ctx.beginPath();
2026
- ctx.moveTo(box.Position.x, box.Position.y + box.Size.y);
2027
- ctx.lineTo(box.Position.x + box.Size.x, box.Position.y + box.Size.y);
2028
- ctx.stroke();
2029
- }
2030
- borderSize() {
2031
- if (this.#border === null) {
2032
- return 0;
2033
- }
2034
- return this.#border.size();
2035
- }
2036
- radius() {
2037
- return this.#radius;
2038
- }
2039
- setColor(color) {
2040
- this.#color = color;
2041
- }
2042
- setBorderColor(color) {
2043
- if (this.#border === null) {
2044
- this.#border = new StrokeStyle({
2045
- color
2046
- });
2047
- } else {
2048
- this.#border.setColor(color);
2049
- }
2050
- }
2051
- };
2052
-
2053
1959
  // src/styles/cursor.ts
2054
1960
  var CursorStyle = /* @__PURE__ */ ((CursorStyle2) => {
2055
1961
  CursorStyle2["Default"] = "default";
@@ -2071,97 +1977,6 @@ var CursorStyle = /* @__PURE__ */ ((CursorStyle2) => {
2071
1977
  return CursorStyle2;
2072
1978
  })(CursorStyle || {});
2073
1979
 
2074
- // src/styles/bevel.ts
2075
- function drawBevel(ctx, box, scale, colors, sunken) {
2076
- const width2 = Math.max(1, 2 * scale);
2077
- const half = width2 / 2;
2078
- const left = box.Position.x + half;
2079
- const top = box.Position.y + half;
2080
- const right = box.Position.x + box.Size.x - half;
2081
- const bottom = box.Position.y + box.Size.y - half;
2082
- ctx.save();
2083
- ctx.lineWidth = width2;
2084
- ctx.lineCap = "square";
2085
- ctx.strokeStyle = sunken ? colors.Dark : colors.Light;
2086
- ctx.beginPath();
2087
- ctx.moveTo(left, bottom);
2088
- ctx.lineTo(left, top);
2089
- ctx.lineTo(right, top);
2090
- ctx.stroke();
2091
- ctx.strokeStyle = sunken ? colors.Light : colors.Dark;
2092
- ctx.beginPath();
2093
- ctx.moveTo(right, top);
2094
- ctx.lineTo(right, bottom);
2095
- ctx.lineTo(left, bottom);
2096
- ctx.stroke();
2097
- ctx.restore();
2098
- }
2099
-
2100
- // src/widgets/field.ts
2101
- function widgetFont(scale) {
2102
- return `${Theme.Widget.FontSize * scale}px ${Theme.FontFamily}`;
2103
- }
2104
- function fieldBackground(look) {
2105
- if (look.background !== void 0) {
2106
- return look.background;
2107
- }
2108
- if (look.input) {
2109
- return look.hovered ? Theme.Widget.Input.HoverBackgroundColor : Theme.Widget.Input.BackgroundColor;
2110
- }
2111
- if (look.pressed) {
2112
- return Theme.Widget.Button.Click.BackgroundColor;
2113
- }
2114
- return look.hovered ? Theme.Widget.Hover.BackgroundColor : Theme.Widget.BackgroundColor;
2115
- }
2116
- function drawFieldBox(ctx, box, scale, look) {
2117
- ctx.fillStyle = fieldBackground(look);
2118
- ctx.beginPath();
2119
- ctx.roundRect(box.Position.x, box.Position.y, box.Size.x, box.Size.y, Theme.Widget.Border.Radius * scale);
2120
- ctx.fill();
2121
- if (Theme.Widget.Border.Size > 0) {
2122
- ctx.strokeStyle = look.border ?? Theme.Widget.Border.Color;
2123
- ctx.lineWidth = Theme.Widget.Border.Size * scale;
2124
- ctx.stroke();
2125
- }
2126
- if (Theme.Widget.Bevel !== null) {
2127
- drawBevel(ctx, box, scale, Theme.Widget.Bevel, look.input === true || look.pressed === true);
2128
- }
2129
- }
2130
- function drawField(ctx, box, scale, look) {
2131
- drawFieldBox(ctx, box, scale, look);
2132
- drawFieldText(ctx, box, scale, look);
2133
- }
2134
- function drawFieldText(ctx, box, scale, look) {
2135
- const padding = Theme.Widget.Padding * scale;
2136
- const middle = box.Position.y + box.Size.y / 2;
2137
- const textColor = look.textColor ?? (look.input ? Theme.Widget.Input.FontColor : Theme.Widget.FontColor);
2138
- ctx.font = widgetFont(scale);
2139
- ctx.textBaseline = "middle";
2140
- if (look.label === void 0 || look.label === "") {
2141
- ctx.textAlign = "center";
2142
- ctx.fillStyle = textColor;
2143
- ctx.fillText(
2144
- fitString(ctx, look.value, box.Size.x - padding * 2),
2145
- box.Position.x + box.Size.x / 2,
2146
- middle
2147
- );
2148
- return;
2149
- }
2150
- const room = box.Size.x - padding * 2;
2151
- const label = fitString(ctx, look.label, room / 2);
2152
- const labelWidth = ctx.measureText(label).width;
2153
- ctx.textAlign = "left";
2154
- ctx.fillStyle = look.textColor ?? Theme.Widget.LabelColor;
2155
- ctx.fillText(label, box.Position.x + padding, middle);
2156
- ctx.textAlign = "right";
2157
- ctx.fillStyle = textColor;
2158
- ctx.fillText(
2159
- fitString(ctx, look.value, room - labelWidth - padding),
2160
- box.Position.x + box.Size.x - padding,
2161
- middle
2162
- );
2163
- }
2164
-
2165
1980
  // src/widgets/inplace.ts
2166
1981
  var open = /* @__PURE__ */ new Set();
2167
1982
  function placeEditsDuring(canvas, draw) {
@@ -2382,6 +2197,242 @@ function startingWidgetValue(node, property, configured, fallback) {
2382
2197
  return fallback;
2383
2198
  }
2384
2199
 
2200
+ // src/widgets/hidden_ports.ts
2201
+ var HiddenPortsToggle = class extends WidgetBase {
2202
+ constructor(owner) {
2203
+ super();
2204
+ this.owner = owner;
2205
+ }
2206
+ Size() {
2207
+ return { x: 150, y: 16 };
2208
+ }
2209
+ render(ctx, box, scale) {
2210
+ const label = this.owner.hiddenPortsShown() ? "\u25B4 less" : `\u25BE ${this.owner.hiddenPortCount()} more`;
2211
+ const midY = box.Position.y + box.Size.y / 2;
2212
+ const centerX = box.Position.x + box.Size.x / 2;
2213
+ ctx.font = `${11 * scale}px ${Theme.FontFamily}`;
2214
+ const half = ctx.measureText(label).width / 2 + 6 * scale;
2215
+ ctx.globalAlpha = this.hovered() ? 1 : 0.5;
2216
+ ctx.strokeStyle = Theme.Widget.FontColor;
2217
+ ctx.fillStyle = Theme.Widget.FontColor;
2218
+ ctx.lineWidth = Math.max(1, scale);
2219
+ ctx.beginPath();
2220
+ ctx.moveTo(box.Position.x, midY);
2221
+ ctx.lineTo(centerX - half, midY);
2222
+ ctx.moveTo(centerX + half, midY);
2223
+ ctx.lineTo(box.Position.x + box.Size.x, midY);
2224
+ ctx.stroke();
2225
+ ctx.textAlign = "center";
2226
+ ctx.textBaseline = "middle";
2227
+ ctx.fillText(label, centerX, midY);
2228
+ }
2229
+ onClick() {
2230
+ this.owner.setHiddenPortsShown(!this.owner.hiddenPortsShown());
2231
+ }
2232
+ };
2233
+
2234
+ // src/styles/stroke.ts
2235
+ function StrokeStyleWithFallback(input, fallback) {
2236
+ return {
2237
+ color: input?.color === void 0 ? fallback?.color : input?.color,
2238
+ size: input?.size === void 0 ? fallback?.size : input?.size
2239
+ };
2240
+ }
2241
+ var StrokeStyle = class {
2242
+ #size;
2243
+ #color;
2244
+ constructor(config) {
2245
+ this.#size = config?.size === void 0 ? 0.5 : config.size;
2246
+ this.#color = config?.color === void 0 ? "black" : config.color;
2247
+ }
2248
+ setupStyle(ctx, scale) {
2249
+ ctx.strokeStyle = this.#color;
2250
+ ctx.lineWidth = scale * this.#size;
2251
+ }
2252
+ setColor(newColor) {
2253
+ this.#color = newColor;
2254
+ }
2255
+ setSize(newSize) {
2256
+ this.#size = newSize;
2257
+ }
2258
+ size() {
2259
+ return this.#size;
2260
+ }
2261
+ };
2262
+
2263
+ // src/styles/box.ts
2264
+ function BoxStyleWithFallback(input, fallback) {
2265
+ return {
2266
+ radius: input?.radius === void 0 ? fallback?.radius : input?.radius,
2267
+ color: input?.color === void 0 ? fallback?.color : input?.color,
2268
+ border: StrokeStyleWithFallback(input?.border, fallback?.border)
2269
+ };
2270
+ }
2271
+ var BoxStyle = class {
2272
+ #border;
2273
+ #color;
2274
+ #radius;
2275
+ constructor(config) {
2276
+ this.#color = config?.color === void 0 ? "#CCCCCC" : config.color;
2277
+ this.#border = config?.border === void 0 ? null : new StrokeStyle(config.border);
2278
+ this.#radius = config?.radius === void 0 ? 2 : config.radius;
2279
+ }
2280
+ #box(ctx, box, scale, radius) {
2281
+ ctx.fillStyle = this.#color;
2282
+ this.#border?.setupStyle(ctx, scale);
2283
+ ctx.beginPath();
2284
+ ctx.roundRect(
2285
+ box.Position.x,
2286
+ box.Position.y,
2287
+ box.Size.x,
2288
+ box.Size.y,
2289
+ radius
2290
+ );
2291
+ ctx.fill();
2292
+ }
2293
+ #draw(ctx, box, scale, radius) {
2294
+ this.#box(ctx, box, scale, radius);
2295
+ ctx.stroke();
2296
+ }
2297
+ Outline(ctx, box, scale, radius) {
2298
+ this.#border?.setupStyle(ctx, scale);
2299
+ ctx.beginPath();
2300
+ ctx.roundRect(
2301
+ box.Position.x,
2302
+ box.Position.y,
2303
+ box.Size.x,
2304
+ box.Size.y,
2305
+ radius
2306
+ );
2307
+ ctx.stroke();
2308
+ }
2309
+ Draw(ctx, box, scale) {
2310
+ this.#draw(ctx, box, scale, this.#radius * scale);
2311
+ }
2312
+ DrawRoundedTopOnly(ctx, box, scale) {
2313
+ this.#draw(ctx, box, scale, [this.#radius * scale * 2, this.#radius * scale * 2, 0, 0]);
2314
+ }
2315
+ DrawUnderline(ctx, box, scale) {
2316
+ this.#box(ctx, box, scale, [this.#radius * scale * 2, this.#radius * scale * 2, 0, 0]);
2317
+ ctx.beginPath();
2318
+ ctx.moveTo(box.Position.x, box.Position.y + box.Size.y);
2319
+ ctx.lineTo(box.Position.x + box.Size.x, box.Position.y + box.Size.y);
2320
+ ctx.stroke();
2321
+ }
2322
+ borderSize() {
2323
+ if (this.#border === null) {
2324
+ return 0;
2325
+ }
2326
+ return this.#border.size();
2327
+ }
2328
+ radius() {
2329
+ return this.#radius;
2330
+ }
2331
+ setColor(color) {
2332
+ this.#color = color;
2333
+ }
2334
+ setBorderColor(color) {
2335
+ if (this.#border === null) {
2336
+ this.#border = new StrokeStyle({
2337
+ color
2338
+ });
2339
+ } else {
2340
+ this.#border.setColor(color);
2341
+ }
2342
+ }
2343
+ };
2344
+
2345
+ // src/styles/bevel.ts
2346
+ function drawBevel(ctx, box, scale, colors, sunken) {
2347
+ const width2 = Math.max(1, 2 * scale);
2348
+ const half = width2 / 2;
2349
+ const left = box.Position.x + half;
2350
+ const top = box.Position.y + half;
2351
+ const right = box.Position.x + box.Size.x - half;
2352
+ const bottom = box.Position.y + box.Size.y - half;
2353
+ ctx.save();
2354
+ ctx.lineWidth = width2;
2355
+ ctx.lineCap = "square";
2356
+ ctx.strokeStyle = sunken ? colors.Dark : colors.Light;
2357
+ ctx.beginPath();
2358
+ ctx.moveTo(left, bottom);
2359
+ ctx.lineTo(left, top);
2360
+ ctx.lineTo(right, top);
2361
+ ctx.stroke();
2362
+ ctx.strokeStyle = sunken ? colors.Light : colors.Dark;
2363
+ ctx.beginPath();
2364
+ ctx.moveTo(right, top);
2365
+ ctx.lineTo(right, bottom);
2366
+ ctx.lineTo(left, bottom);
2367
+ ctx.stroke();
2368
+ ctx.restore();
2369
+ }
2370
+
2371
+ // src/widgets/field.ts
2372
+ function widgetFont(scale) {
2373
+ return `${Theme.Widget.FontSize * scale}px ${Theme.FontFamily}`;
2374
+ }
2375
+ function fieldBackground(look) {
2376
+ if (look.background !== void 0) {
2377
+ return look.background;
2378
+ }
2379
+ if (look.input) {
2380
+ return look.hovered ? Theme.Widget.Input.HoverBackgroundColor : Theme.Widget.Input.BackgroundColor;
2381
+ }
2382
+ if (look.pressed) {
2383
+ return Theme.Widget.Button.Click.BackgroundColor;
2384
+ }
2385
+ return look.hovered ? Theme.Widget.Hover.BackgroundColor : Theme.Widget.BackgroundColor;
2386
+ }
2387
+ function drawFieldBox(ctx, box, scale, look) {
2388
+ ctx.fillStyle = fieldBackground(look);
2389
+ ctx.beginPath();
2390
+ ctx.roundRect(box.Position.x, box.Position.y, box.Size.x, box.Size.y, Theme.Widget.Border.Radius * scale);
2391
+ ctx.fill();
2392
+ if (Theme.Widget.Border.Size > 0) {
2393
+ ctx.strokeStyle = look.border ?? Theme.Widget.Border.Color;
2394
+ ctx.lineWidth = Theme.Widget.Border.Size * scale;
2395
+ ctx.stroke();
2396
+ }
2397
+ if (Theme.Widget.Bevel !== null) {
2398
+ drawBevel(ctx, box, scale, Theme.Widget.Bevel, look.input === true || look.pressed === true);
2399
+ }
2400
+ }
2401
+ function drawField(ctx, box, scale, look) {
2402
+ drawFieldBox(ctx, box, scale, look);
2403
+ drawFieldText(ctx, box, scale, look);
2404
+ }
2405
+ function drawFieldText(ctx, box, scale, look) {
2406
+ const padding = Theme.Widget.Padding * scale;
2407
+ const middle = box.Position.y + box.Size.y / 2;
2408
+ const textColor = look.textColor ?? (look.input ? Theme.Widget.Input.FontColor : Theme.Widget.FontColor);
2409
+ ctx.font = widgetFont(scale);
2410
+ ctx.textBaseline = "middle";
2411
+ if (look.label === void 0 || look.label === "") {
2412
+ ctx.textAlign = "center";
2413
+ ctx.fillStyle = textColor;
2414
+ ctx.fillText(
2415
+ fitString(ctx, look.value, box.Size.x - padding * 2),
2416
+ box.Position.x + box.Size.x / 2,
2417
+ middle
2418
+ );
2419
+ return;
2420
+ }
2421
+ const room = box.Size.x - padding * 2;
2422
+ const label = fitString(ctx, look.label, room / 2);
2423
+ const labelWidth = ctx.measureText(label).width;
2424
+ ctx.textAlign = "left";
2425
+ ctx.fillStyle = look.textColor ?? Theme.Widget.LabelColor;
2426
+ ctx.fillText(label, box.Position.x + padding, middle);
2427
+ ctx.textAlign = "right";
2428
+ ctx.fillStyle = textColor;
2429
+ ctx.fillText(
2430
+ fitString(ctx, look.value, room - labelWidth - padding),
2431
+ box.Position.x + box.Size.x - padding,
2432
+ middle
2433
+ );
2434
+ }
2435
+
2385
2436
  // src/widgets/value.ts
2386
2437
  var ValueWidget = class extends WidgetBase {
2387
2438
  #node;
@@ -5149,6 +5200,7 @@ var FlowNode = class {
5149
5200
  #inputPortPositions;
5150
5201
  #outputPortPositions;
5151
5202
  #widgetPositions;
5203
+ #portWidgetPositions = new List();
5152
5204
  #data;
5153
5205
  #registeredAnyPropertyChangeCallbacks;
5154
5206
  #registeredPropertyChangeCallbacks;
@@ -5636,9 +5688,10 @@ var FlowNode = class {
5636
5688
  }
5637
5689
  this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
5638
5690
  size.y += this.#portRowHeight(port, tempMeasurement.y) + this.#elementSpacing;
5639
- size.x = Math.max(size.x, tempMeasurement.x + doublePadding);
5691
+ size.x = Math.max(size.x, this.#portRowWidth(port, tempMeasurement.x) + doublePadding);
5640
5692
  }
5641
- for (const widget of this.#widgets) {
5693
+ const widgets = this.#hasHiddenPortsToggle() ? [this.#hiddenPortsToggle, ...this.#widgets] : this.#widgets;
5694
+ for (const widget of widgets) {
5642
5695
  if (widget.Hidden?.()) {
5643
5696
  continue;
5644
5697
  }
@@ -5656,6 +5709,9 @@ var FlowNode = class {
5656
5709
  }
5657
5710
  addInput(config) {
5658
5711
  const port = new Port(this, config.array ? "INPUTARRAY" /* InputArray */ : "INPUT" /* Input */, config);
5712
+ if (config.widget !== void 0) {
5713
+ port.setWidget(GlobalWidgetFactory.createFromConfig(this, config.widget));
5714
+ }
5659
5715
  this.#input.push(port);
5660
5716
  return port;
5661
5717
  }
@@ -5664,6 +5720,31 @@ var FlowNode = class {
5664
5720
  this.#output.push(port);
5665
5721
  return port;
5666
5722
  }
5723
+ #hiddenPortsShown = false;
5724
+ #hiddenPortsToggle = new HiddenPortsToggle(this);
5725
+ #hiddenPortsTogglePosition = null;
5726
+ hiddenPortsShown() {
5727
+ return this.#hiddenPortsShown;
5728
+ }
5729
+ setHiddenPortsShown(shown) {
5730
+ if (shown === this.#hiddenPortsShown) {
5731
+ return;
5732
+ }
5733
+ this.#hiddenPortsShown = shown;
5734
+ this.invalidateLayout();
5735
+ }
5736
+ hiddenPortCount() {
5737
+ let count = 0;
5738
+ for (const port of [...this.#input, ...this.#output]) {
5739
+ if (port.tucked()) {
5740
+ count++;
5741
+ }
5742
+ }
5743
+ return count;
5744
+ }
5745
+ #hasHiddenPortsToggle() {
5746
+ return this.hiddenPortCount() > 0;
5747
+ }
5667
5748
  addWidget(widget) {
5668
5749
  this.#widgets.push(widget);
5669
5750
  requestRender();
@@ -5742,6 +5823,11 @@ var FlowNode = class {
5742
5823
  intersection.Port = this.#output[i];
5743
5824
  }
5744
5825
  }
5826
+ if (this.#hiddenPortsTogglePosition !== null && InBox(this.#hiddenPortsTogglePosition, position)) {
5827
+ intersection.Node = this;
5828
+ intersection.Widget = this.#hiddenPortsToggle;
5829
+ intersection.WidgetBox = this.#hiddenPortsTogglePosition;
5830
+ }
5745
5831
  for (let i = 0; i < this.#widgetPositions.Count(); i++) {
5746
5832
  const widget = this.#widgets[i];
5747
5833
  if (widget !== void 0 && !widget.Hidden?.() && InBox(this.#widgetPositions.At(i), position)) {
@@ -5751,6 +5837,17 @@ var FlowNode = class {
5751
5837
  intersection.WidgetBox = this.#widgetPositions.At(i);
5752
5838
  }
5753
5839
  }
5840
+ for (let i = 0; i < this.#portWidgetPositions.Count(); i++) {
5841
+ const port = this.#input[i];
5842
+ if (port !== void 0 && port.widgetShown() && InBox(this.#portWidgetPositions.At(i), position)) {
5843
+ intersection.Node = this;
5844
+ intersection.Widget = port.widget();
5845
+ intersection.WidgetBox = this.#portWidgetPositions.At(i);
5846
+ intersection.Port = void 0;
5847
+ intersection.PortIndex = void 0;
5848
+ intersection.PortIsInput = void 0;
5849
+ }
5850
+ }
5754
5851
  return intersection;
5755
5852
  }
5756
5853
  canEditTitle() {
@@ -5865,7 +5962,14 @@ var FlowNode = class {
5865
5962
  }
5866
5963
  #portRowHeight(port, textHeight) {
5867
5964
  const slots = port.slots();
5868
- return slots > 1 ? Math.max(textHeight, slots * ARRAY_SLOT_PITCH) : textHeight;
5965
+ const widget = port.widgetShown() ? port.widget().Size().y : 0;
5966
+ return Math.max(slots > 1 ? slots * ARRAY_SLOT_PITCH : 0, textHeight, widget);
5967
+ }
5968
+ #portRowWidth(port, textWidth) {
5969
+ if (!port.widgetShown()) {
5970
+ return textWidth;
5971
+ }
5972
+ return textWidth + this.#elementSpacing + port.widget().Size().x;
5869
5973
  }
5870
5974
  hasBeenLaidOut() {
5871
5975
  return this.#laidOut;
@@ -5887,6 +5991,7 @@ var FlowNode = class {
5887
5991
  this.#inputPortPositions.Clear();
5888
5992
  this.#outputPortPositions.Clear();
5889
5993
  this.#widgetPositions.Clear();
5994
+ this.#portWidgetPositions.Clear();
5890
5995
  const scaledPadding = this.#padding * camera.zoom;
5891
5996
  const scaledElementSpacing = this.#elementSpacing * camera.zoom;
5892
5997
  const nodeBounds = this.calculateBounds(ctx, camera);
@@ -5981,6 +6086,7 @@ var FlowNode = class {
5981
6086
  const port = this.#input[i];
5982
6087
  if (port.hidden()) {
5983
6088
  this.#inputPortPositions.Push({ Position: { x: nodeBounds.Position.x, y: startY }, Size: { x: 0, y: 0 } });
6089
+ this.#portWidgetPositions.Push({ Position: { x: nodeBounds.Position.x, y: startY }, Size: { x: 0, y: 0 } });
5984
6090
  continue;
5985
6091
  }
5986
6092
  this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
@@ -5990,6 +6096,19 @@ var FlowNode = class {
5990
6096
  position.y = startY + portHeight / 2;
5991
6097
  this.#portTextStyle.setupStyle(ctx, camera.zoom);
5992
6098
  ctx.fillText(port.getDisplayName(), leftSide, position.y);
6099
+ if (port.widgetShown()) {
6100
+ const widget = port.widget();
6101
+ const widgetHeight = widget.Size().y * camera.zoom;
6102
+ const widgetLeft = leftSide + tempMeasurement.x * camera.zoom + scaledElementSpacing;
6103
+ const box = {
6104
+ Position: { x: widgetLeft, y: startY + (portHeight - widgetHeight) / 2 },
6105
+ Size: { x: nodeBounds.Position.x + nodeBounds.Size.x - scaledPadding - widgetLeft, y: widgetHeight }
6106
+ };
6107
+ widget.Draw(ctx, box, camera.zoom, mousePosition);
6108
+ this.#portWidgetPositions.Push(box);
6109
+ } else {
6110
+ this.#portWidgetPositions.Push({ Position: { x: nodeBounds.Position.x, y: startY }, Size: { x: 0, y: 0 } });
6111
+ }
5993
6112
  this.#inputPortPositions.Push(port.render(ctx, position, camera, mousePosition, postProcess));
5994
6113
  startY += portHeight + scaledElementSpacing;
5995
6114
  }
@@ -6011,6 +6130,16 @@ var FlowNode = class {
6011
6130
  this.#outputPortPositions.Push(port.render(ctx, position, camera, mousePosition, postProcess));
6012
6131
  startY += portHeight + scaledElementSpacing;
6013
6132
  }
6133
+ this.#hiddenPortsTogglePosition = null;
6134
+ if (this.#hasHiddenPortsToggle()) {
6135
+ const box = {
6136
+ Position: { x: nodeBounds.Position.x + scaledPadding, y: startY },
6137
+ Size: { x: nodeBounds.Size.x - scaledPadding * 2, y: this.#hiddenPortsToggle.Size().y * camera.zoom }
6138
+ };
6139
+ this.#hiddenPortsToggle.Draw(ctx, box, camera.zoom, mousePosition);
6140
+ this.#hiddenPortsTogglePosition = box;
6141
+ startY += box.Size.y + scaledElementSpacing;
6142
+ }
6014
6143
  for (let i = 0; i < this.#widgets.length; i++) {
6015
6144
  const widget = this.#widgets[i];
6016
6145
  if (widget.Hidden?.()) {
@@ -6065,6 +6194,8 @@ var FlowNode = class {
6065
6194
  return false;
6066
6195
  }
6067
6196
  this.#widgetPositions.Clear();
6197
+ this.#portWidgetPositions.Clear();
6198
+ this.#hiddenPortsTogglePosition = null;
6068
6199
  return true;
6069
6200
  }
6070
6201
  placePorts(bounds, zoom) {
@@ -7725,8 +7856,8 @@ var NodeFlowGraph = class {
7725
7856
  this.#clickEnd.bind(this),
7726
7857
  this.#openContextMenu.bind(this),
7727
7858
  this.#fileDrop.bind(this),
7728
- (amount, anchor) => {
7729
- this.zoom(amount, anchor);
7859
+ (factor, anchor) => {
7860
+ this.#zoomBy(factor, anchor);
7730
7861
  }
7731
7862
  );
7732
7863
  document.addEventListener(
@@ -7854,12 +7985,15 @@ var NodeFlowGraph = class {
7854
7985
  this.#mainNoteSubsystem.addNoteDragStopListener(callback);
7855
7986
  }
7856
7987
  zoom(amount, anchor) {
7988
+ this.#zoomBy(1 + amount * 0.05, anchor);
7989
+ }
7990
+ #zoomBy(factor, anchor) {
7857
7991
  const anchorPos = anchor ?? this.#mousePosition;
7858
7992
  let oldPos = void 0;
7859
7993
  if (anchorPos !== void 0) {
7860
7994
  oldPos = this.#sceenPositionToGraphPosition(anchorPos);
7861
7995
  }
7862
- this.#camera.zoom += amount * this.#camera.zoom * 0.05;
7996
+ this.#camera.zoom *= factor;
7863
7997
  if (!oldPos || anchorPos === void 0) {
7864
7998
  return;
7865
7999
  }