@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.mjs CHANGED
@@ -735,7 +735,7 @@ var ContextMenu = class _ContextMenu {
735
735
  // src/input.ts
736
736
  var LONG_PRESS_MS = 500;
737
737
  var LONG_PRESS_SLOP = 12;
738
- var PINCH_SENSITIVITY = 50;
738
+ var WHEEL_ZOOM_PER_PIXEL = 5e-4;
739
739
  var SUPPRESS_MOUSE_MS = 600;
740
740
  var MouseObserver = class {
741
741
  #clicked;
@@ -833,13 +833,12 @@ var MouseObserver = class {
833
833
  } else if (event.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
834
834
  dy *= this.#ele.clientHeight;
835
835
  }
836
- const amount = -dy / 100;
837
- if (amount === 0) {
836
+ if (dy === 0) {
838
837
  return;
839
838
  }
840
839
  const anchor = this.#mousePos(event);
841
840
  this.#moveCallback(anchor);
842
- this.#zoomCallback(amount, anchor);
841
+ this.#zoomCallback(Math.exp(-dy * WHEEL_ZOOM_PER_PIXEL), anchor);
843
842
  }
844
843
  #move(event) {
845
844
  if (this.#fromTouch()) {
@@ -915,7 +914,7 @@ var MouseObserver = class {
915
914
  const anchor = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
916
915
  if (this.#pinchDist > 0) {
917
916
  this.#moveCallback(anchor);
918
- this.#zoomCallback((dist - this.#pinchDist) / PINCH_SENSITIVITY, anchor);
917
+ this.#zoomCallback(dist / this.#pinchDist, anchor);
919
918
  }
920
919
  this.#pinchDist = dist;
921
920
  return;
@@ -1610,6 +1609,7 @@ var Port = class {
1610
1609
  #dataTypePopupElement;
1611
1610
  #onConnectionAdded;
1612
1611
  #onConnectionRemoved;
1612
+ #widget = null;
1613
1613
  constructor(node, portType, config) {
1614
1614
  this.#node = node;
1615
1615
  this.#connections = new Array();
@@ -1760,14 +1760,31 @@ var Port = class {
1760
1760
  this.#visibilityMayHaveChanged(was);
1761
1761
  }
1762
1762
  #visibilityMayHaveChanged(was) {
1763
- if (was !== this.hidden() || this.#portType === "INPUTARRAY" /* InputArray */) {
1763
+ if (was !== this.hidden() || this.#portType === "INPUTARRAY" /* InputArray */ || this.#widget !== null) {
1764
1764
  this.#node?.invalidateLayout();
1765
1765
  }
1766
1766
  }
1767
+ setWidget(widget) {
1768
+ this.#widget = widget;
1769
+ this.#node?.invalidateLayout();
1770
+ }
1771
+ widget() {
1772
+ return this.#widget;
1773
+ }
1774
+ /**
1775
+ * The widget stands in for a wire, so it leaves once one is connected.
1776
+ */
1777
+ widgetShown() {
1778
+ return this.#widget !== null && this.#portType !== "OUTPUT" /* Output */ && this.#connections.length === 0 && !this.hidden() && !(this.#widget.Hidden?.() ?? false);
1779
+ }
1767
1780
  /**
1768
- * A port asked to hide still shows while anything is connected to it.
1781
+ * A port asked to hide still shows while anything is connected to it,
1782
+ * and while its node is showing its hidden ports.
1769
1783
  */
1770
1784
  hidden() {
1785
+ return this.tucked() && !(this.#node?.hiddenPortsShown() ?? false);
1786
+ }
1787
+ tucked() {
1771
1788
  return this.#hidden && this.#connections.length === 0;
1772
1789
  }
1773
1790
  addConnectionRemovedListener(callback) {
@@ -1869,117 +1886,6 @@ var Port = class {
1869
1886
  }
1870
1887
  };
1871
1888
 
1872
- // src/styles/stroke.ts
1873
- function StrokeStyleWithFallback(input, fallback) {
1874
- return {
1875
- color: input?.color === void 0 ? fallback?.color : input?.color,
1876
- size: input?.size === void 0 ? fallback?.size : input?.size
1877
- };
1878
- }
1879
- var StrokeStyle = class {
1880
- #size;
1881
- #color;
1882
- constructor(config) {
1883
- this.#size = config?.size === void 0 ? 0.5 : config.size;
1884
- this.#color = config?.color === void 0 ? "black" : config.color;
1885
- }
1886
- setupStyle(ctx, scale) {
1887
- ctx.strokeStyle = this.#color;
1888
- ctx.lineWidth = scale * this.#size;
1889
- }
1890
- setColor(newColor) {
1891
- this.#color = newColor;
1892
- }
1893
- setSize(newSize) {
1894
- this.#size = newSize;
1895
- }
1896
- size() {
1897
- return this.#size;
1898
- }
1899
- };
1900
-
1901
- // src/styles/box.ts
1902
- function BoxStyleWithFallback(input, fallback) {
1903
- return {
1904
- radius: input?.radius === void 0 ? fallback?.radius : input?.radius,
1905
- color: input?.color === void 0 ? fallback?.color : input?.color,
1906
- border: StrokeStyleWithFallback(input?.border, fallback?.border)
1907
- };
1908
- }
1909
- var BoxStyle = class {
1910
- #border;
1911
- #color;
1912
- #radius;
1913
- constructor(config) {
1914
- this.#color = config?.color === void 0 ? "#CCCCCC" : config.color;
1915
- this.#border = config?.border === void 0 ? null : new StrokeStyle(config.border);
1916
- this.#radius = config?.radius === void 0 ? 2 : config.radius;
1917
- }
1918
- #box(ctx, box, scale, radius) {
1919
- ctx.fillStyle = this.#color;
1920
- this.#border?.setupStyle(ctx, scale);
1921
- ctx.beginPath();
1922
- ctx.roundRect(
1923
- box.Position.x,
1924
- box.Position.y,
1925
- box.Size.x,
1926
- box.Size.y,
1927
- radius
1928
- );
1929
- ctx.fill();
1930
- }
1931
- #draw(ctx, box, scale, radius) {
1932
- this.#box(ctx, box, scale, radius);
1933
- ctx.stroke();
1934
- }
1935
- Outline(ctx, box, scale, radius) {
1936
- this.#border?.setupStyle(ctx, scale);
1937
- ctx.beginPath();
1938
- ctx.roundRect(
1939
- box.Position.x,
1940
- box.Position.y,
1941
- box.Size.x,
1942
- box.Size.y,
1943
- radius
1944
- );
1945
- ctx.stroke();
1946
- }
1947
- Draw(ctx, box, scale) {
1948
- this.#draw(ctx, box, scale, this.#radius * scale);
1949
- }
1950
- DrawRoundedTopOnly(ctx, box, scale) {
1951
- this.#draw(ctx, box, scale, [this.#radius * scale * 2, this.#radius * scale * 2, 0, 0]);
1952
- }
1953
- DrawUnderline(ctx, box, scale) {
1954
- this.#box(ctx, box, scale, [this.#radius * scale * 2, this.#radius * scale * 2, 0, 0]);
1955
- ctx.beginPath();
1956
- ctx.moveTo(box.Position.x, box.Position.y + box.Size.y);
1957
- ctx.lineTo(box.Position.x + box.Size.x, box.Position.y + box.Size.y);
1958
- ctx.stroke();
1959
- }
1960
- borderSize() {
1961
- if (this.#border === null) {
1962
- return 0;
1963
- }
1964
- return this.#border.size();
1965
- }
1966
- radius() {
1967
- return this.#radius;
1968
- }
1969
- setColor(color) {
1970
- this.#color = color;
1971
- }
1972
- setBorderColor(color) {
1973
- if (this.#border === null) {
1974
- this.#border = new StrokeStyle({
1975
- color
1976
- });
1977
- } else {
1978
- this.#border.setColor(color);
1979
- }
1980
- }
1981
- };
1982
-
1983
1889
  // src/styles/cursor.ts
1984
1890
  var CursorStyle = /* @__PURE__ */ ((CursorStyle2) => {
1985
1891
  CursorStyle2["Default"] = "default";
@@ -2001,97 +1907,6 @@ var CursorStyle = /* @__PURE__ */ ((CursorStyle2) => {
2001
1907
  return CursorStyle2;
2002
1908
  })(CursorStyle || {});
2003
1909
 
2004
- // src/styles/bevel.ts
2005
- function drawBevel(ctx, box, scale, colors, sunken) {
2006
- const width2 = Math.max(1, 2 * scale);
2007
- const half = width2 / 2;
2008
- const left = box.Position.x + half;
2009
- const top = box.Position.y + half;
2010
- const right = box.Position.x + box.Size.x - half;
2011
- const bottom = box.Position.y + box.Size.y - half;
2012
- ctx.save();
2013
- ctx.lineWidth = width2;
2014
- ctx.lineCap = "square";
2015
- ctx.strokeStyle = sunken ? colors.Dark : colors.Light;
2016
- ctx.beginPath();
2017
- ctx.moveTo(left, bottom);
2018
- ctx.lineTo(left, top);
2019
- ctx.lineTo(right, top);
2020
- ctx.stroke();
2021
- ctx.strokeStyle = sunken ? colors.Light : colors.Dark;
2022
- ctx.beginPath();
2023
- ctx.moveTo(right, top);
2024
- ctx.lineTo(right, bottom);
2025
- ctx.lineTo(left, bottom);
2026
- ctx.stroke();
2027
- ctx.restore();
2028
- }
2029
-
2030
- // src/widgets/field.ts
2031
- function widgetFont(scale) {
2032
- return `${Theme.Widget.FontSize * scale}px ${Theme.FontFamily}`;
2033
- }
2034
- function fieldBackground(look) {
2035
- if (look.background !== void 0) {
2036
- return look.background;
2037
- }
2038
- if (look.input) {
2039
- return look.hovered ? Theme.Widget.Input.HoverBackgroundColor : Theme.Widget.Input.BackgroundColor;
2040
- }
2041
- if (look.pressed) {
2042
- return Theme.Widget.Button.Click.BackgroundColor;
2043
- }
2044
- return look.hovered ? Theme.Widget.Hover.BackgroundColor : Theme.Widget.BackgroundColor;
2045
- }
2046
- function drawFieldBox(ctx, box, scale, look) {
2047
- ctx.fillStyle = fieldBackground(look);
2048
- ctx.beginPath();
2049
- ctx.roundRect(box.Position.x, box.Position.y, box.Size.x, box.Size.y, Theme.Widget.Border.Radius * scale);
2050
- ctx.fill();
2051
- if (Theme.Widget.Border.Size > 0) {
2052
- ctx.strokeStyle = look.border ?? Theme.Widget.Border.Color;
2053
- ctx.lineWidth = Theme.Widget.Border.Size * scale;
2054
- ctx.stroke();
2055
- }
2056
- if (Theme.Widget.Bevel !== null) {
2057
- drawBevel(ctx, box, scale, Theme.Widget.Bevel, look.input === true || look.pressed === true);
2058
- }
2059
- }
2060
- function drawField(ctx, box, scale, look) {
2061
- drawFieldBox(ctx, box, scale, look);
2062
- drawFieldText(ctx, box, scale, look);
2063
- }
2064
- function drawFieldText(ctx, box, scale, look) {
2065
- const padding = Theme.Widget.Padding * scale;
2066
- const middle = box.Position.y + box.Size.y / 2;
2067
- const textColor = look.textColor ?? (look.input ? Theme.Widget.Input.FontColor : Theme.Widget.FontColor);
2068
- ctx.font = widgetFont(scale);
2069
- ctx.textBaseline = "middle";
2070
- if (look.label === void 0 || look.label === "") {
2071
- ctx.textAlign = "center";
2072
- ctx.fillStyle = textColor;
2073
- ctx.fillText(
2074
- fitString(ctx, look.value, box.Size.x - padding * 2),
2075
- box.Position.x + box.Size.x / 2,
2076
- middle
2077
- );
2078
- return;
2079
- }
2080
- const room = box.Size.x - padding * 2;
2081
- const label = fitString(ctx, look.label, room / 2);
2082
- const labelWidth = ctx.measureText(label).width;
2083
- ctx.textAlign = "left";
2084
- ctx.fillStyle = look.textColor ?? Theme.Widget.LabelColor;
2085
- ctx.fillText(label, box.Position.x + padding, middle);
2086
- ctx.textAlign = "right";
2087
- ctx.fillStyle = textColor;
2088
- ctx.fillText(
2089
- fitString(ctx, look.value, room - labelWidth - padding),
2090
- box.Position.x + box.Size.x - padding,
2091
- middle
2092
- );
2093
- }
2094
-
2095
1910
  // src/widgets/inplace.ts
2096
1911
  var open = /* @__PURE__ */ new Set();
2097
1912
  function placeEditsDuring(canvas, draw) {
@@ -2312,6 +2127,242 @@ function startingWidgetValue(node, property, configured, fallback) {
2312
2127
  return fallback;
2313
2128
  }
2314
2129
 
2130
+ // src/widgets/hidden_ports.ts
2131
+ var HiddenPortsToggle = class extends WidgetBase {
2132
+ constructor(owner) {
2133
+ super();
2134
+ this.owner = owner;
2135
+ }
2136
+ Size() {
2137
+ return { x: 150, y: 16 };
2138
+ }
2139
+ render(ctx, box, scale) {
2140
+ const label = this.owner.hiddenPortsShown() ? "\u25B4 less" : `\u25BE ${this.owner.hiddenPortCount()} more`;
2141
+ const midY = box.Position.y + box.Size.y / 2;
2142
+ const centerX = box.Position.x + box.Size.x / 2;
2143
+ ctx.font = `${11 * scale}px ${Theme.FontFamily}`;
2144
+ const half = ctx.measureText(label).width / 2 + 6 * scale;
2145
+ ctx.globalAlpha = this.hovered() ? 1 : 0.5;
2146
+ ctx.strokeStyle = Theme.Widget.FontColor;
2147
+ ctx.fillStyle = Theme.Widget.FontColor;
2148
+ ctx.lineWidth = Math.max(1, scale);
2149
+ ctx.beginPath();
2150
+ ctx.moveTo(box.Position.x, midY);
2151
+ ctx.lineTo(centerX - half, midY);
2152
+ ctx.moveTo(centerX + half, midY);
2153
+ ctx.lineTo(box.Position.x + box.Size.x, midY);
2154
+ ctx.stroke();
2155
+ ctx.textAlign = "center";
2156
+ ctx.textBaseline = "middle";
2157
+ ctx.fillText(label, centerX, midY);
2158
+ }
2159
+ onClick() {
2160
+ this.owner.setHiddenPortsShown(!this.owner.hiddenPortsShown());
2161
+ }
2162
+ };
2163
+
2164
+ // src/styles/stroke.ts
2165
+ function StrokeStyleWithFallback(input, fallback) {
2166
+ return {
2167
+ color: input?.color === void 0 ? fallback?.color : input?.color,
2168
+ size: input?.size === void 0 ? fallback?.size : input?.size
2169
+ };
2170
+ }
2171
+ var StrokeStyle = class {
2172
+ #size;
2173
+ #color;
2174
+ constructor(config) {
2175
+ this.#size = config?.size === void 0 ? 0.5 : config.size;
2176
+ this.#color = config?.color === void 0 ? "black" : config.color;
2177
+ }
2178
+ setupStyle(ctx, scale) {
2179
+ ctx.strokeStyle = this.#color;
2180
+ ctx.lineWidth = scale * this.#size;
2181
+ }
2182
+ setColor(newColor) {
2183
+ this.#color = newColor;
2184
+ }
2185
+ setSize(newSize) {
2186
+ this.#size = newSize;
2187
+ }
2188
+ size() {
2189
+ return this.#size;
2190
+ }
2191
+ };
2192
+
2193
+ // src/styles/box.ts
2194
+ function BoxStyleWithFallback(input, fallback) {
2195
+ return {
2196
+ radius: input?.radius === void 0 ? fallback?.radius : input?.radius,
2197
+ color: input?.color === void 0 ? fallback?.color : input?.color,
2198
+ border: StrokeStyleWithFallback(input?.border, fallback?.border)
2199
+ };
2200
+ }
2201
+ var BoxStyle = class {
2202
+ #border;
2203
+ #color;
2204
+ #radius;
2205
+ constructor(config) {
2206
+ this.#color = config?.color === void 0 ? "#CCCCCC" : config.color;
2207
+ this.#border = config?.border === void 0 ? null : new StrokeStyle(config.border);
2208
+ this.#radius = config?.radius === void 0 ? 2 : config.radius;
2209
+ }
2210
+ #box(ctx, box, scale, radius) {
2211
+ ctx.fillStyle = this.#color;
2212
+ this.#border?.setupStyle(ctx, scale);
2213
+ ctx.beginPath();
2214
+ ctx.roundRect(
2215
+ box.Position.x,
2216
+ box.Position.y,
2217
+ box.Size.x,
2218
+ box.Size.y,
2219
+ radius
2220
+ );
2221
+ ctx.fill();
2222
+ }
2223
+ #draw(ctx, box, scale, radius) {
2224
+ this.#box(ctx, box, scale, radius);
2225
+ ctx.stroke();
2226
+ }
2227
+ Outline(ctx, box, scale, radius) {
2228
+ this.#border?.setupStyle(ctx, scale);
2229
+ ctx.beginPath();
2230
+ ctx.roundRect(
2231
+ box.Position.x,
2232
+ box.Position.y,
2233
+ box.Size.x,
2234
+ box.Size.y,
2235
+ radius
2236
+ );
2237
+ ctx.stroke();
2238
+ }
2239
+ Draw(ctx, box, scale) {
2240
+ this.#draw(ctx, box, scale, this.#radius * scale);
2241
+ }
2242
+ DrawRoundedTopOnly(ctx, box, scale) {
2243
+ this.#draw(ctx, box, scale, [this.#radius * scale * 2, this.#radius * scale * 2, 0, 0]);
2244
+ }
2245
+ DrawUnderline(ctx, box, scale) {
2246
+ this.#box(ctx, box, scale, [this.#radius * scale * 2, this.#radius * scale * 2, 0, 0]);
2247
+ ctx.beginPath();
2248
+ ctx.moveTo(box.Position.x, box.Position.y + box.Size.y);
2249
+ ctx.lineTo(box.Position.x + box.Size.x, box.Position.y + box.Size.y);
2250
+ ctx.stroke();
2251
+ }
2252
+ borderSize() {
2253
+ if (this.#border === null) {
2254
+ return 0;
2255
+ }
2256
+ return this.#border.size();
2257
+ }
2258
+ radius() {
2259
+ return this.#radius;
2260
+ }
2261
+ setColor(color) {
2262
+ this.#color = color;
2263
+ }
2264
+ setBorderColor(color) {
2265
+ if (this.#border === null) {
2266
+ this.#border = new StrokeStyle({
2267
+ color
2268
+ });
2269
+ } else {
2270
+ this.#border.setColor(color);
2271
+ }
2272
+ }
2273
+ };
2274
+
2275
+ // src/styles/bevel.ts
2276
+ function drawBevel(ctx, box, scale, colors, sunken) {
2277
+ const width2 = Math.max(1, 2 * scale);
2278
+ const half = width2 / 2;
2279
+ const left = box.Position.x + half;
2280
+ const top = box.Position.y + half;
2281
+ const right = box.Position.x + box.Size.x - half;
2282
+ const bottom = box.Position.y + box.Size.y - half;
2283
+ ctx.save();
2284
+ ctx.lineWidth = width2;
2285
+ ctx.lineCap = "square";
2286
+ ctx.strokeStyle = sunken ? colors.Dark : colors.Light;
2287
+ ctx.beginPath();
2288
+ ctx.moveTo(left, bottom);
2289
+ ctx.lineTo(left, top);
2290
+ ctx.lineTo(right, top);
2291
+ ctx.stroke();
2292
+ ctx.strokeStyle = sunken ? colors.Light : colors.Dark;
2293
+ ctx.beginPath();
2294
+ ctx.moveTo(right, top);
2295
+ ctx.lineTo(right, bottom);
2296
+ ctx.lineTo(left, bottom);
2297
+ ctx.stroke();
2298
+ ctx.restore();
2299
+ }
2300
+
2301
+ // src/widgets/field.ts
2302
+ function widgetFont(scale) {
2303
+ return `${Theme.Widget.FontSize * scale}px ${Theme.FontFamily}`;
2304
+ }
2305
+ function fieldBackground(look) {
2306
+ if (look.background !== void 0) {
2307
+ return look.background;
2308
+ }
2309
+ if (look.input) {
2310
+ return look.hovered ? Theme.Widget.Input.HoverBackgroundColor : Theme.Widget.Input.BackgroundColor;
2311
+ }
2312
+ if (look.pressed) {
2313
+ return Theme.Widget.Button.Click.BackgroundColor;
2314
+ }
2315
+ return look.hovered ? Theme.Widget.Hover.BackgroundColor : Theme.Widget.BackgroundColor;
2316
+ }
2317
+ function drawFieldBox(ctx, box, scale, look) {
2318
+ ctx.fillStyle = fieldBackground(look);
2319
+ ctx.beginPath();
2320
+ ctx.roundRect(box.Position.x, box.Position.y, box.Size.x, box.Size.y, Theme.Widget.Border.Radius * scale);
2321
+ ctx.fill();
2322
+ if (Theme.Widget.Border.Size > 0) {
2323
+ ctx.strokeStyle = look.border ?? Theme.Widget.Border.Color;
2324
+ ctx.lineWidth = Theme.Widget.Border.Size * scale;
2325
+ ctx.stroke();
2326
+ }
2327
+ if (Theme.Widget.Bevel !== null) {
2328
+ drawBevel(ctx, box, scale, Theme.Widget.Bevel, look.input === true || look.pressed === true);
2329
+ }
2330
+ }
2331
+ function drawField(ctx, box, scale, look) {
2332
+ drawFieldBox(ctx, box, scale, look);
2333
+ drawFieldText(ctx, box, scale, look);
2334
+ }
2335
+ function drawFieldText(ctx, box, scale, look) {
2336
+ const padding = Theme.Widget.Padding * scale;
2337
+ const middle = box.Position.y + box.Size.y / 2;
2338
+ const textColor = look.textColor ?? (look.input ? Theme.Widget.Input.FontColor : Theme.Widget.FontColor);
2339
+ ctx.font = widgetFont(scale);
2340
+ ctx.textBaseline = "middle";
2341
+ if (look.label === void 0 || look.label === "") {
2342
+ ctx.textAlign = "center";
2343
+ ctx.fillStyle = textColor;
2344
+ ctx.fillText(
2345
+ fitString(ctx, look.value, box.Size.x - padding * 2),
2346
+ box.Position.x + box.Size.x / 2,
2347
+ middle
2348
+ );
2349
+ return;
2350
+ }
2351
+ const room = box.Size.x - padding * 2;
2352
+ const label = fitString(ctx, look.label, room / 2);
2353
+ const labelWidth = ctx.measureText(label).width;
2354
+ ctx.textAlign = "left";
2355
+ ctx.fillStyle = look.textColor ?? Theme.Widget.LabelColor;
2356
+ ctx.fillText(label, box.Position.x + padding, middle);
2357
+ ctx.textAlign = "right";
2358
+ ctx.fillStyle = textColor;
2359
+ ctx.fillText(
2360
+ fitString(ctx, look.value, room - labelWidth - padding),
2361
+ box.Position.x + box.Size.x - padding,
2362
+ middle
2363
+ );
2364
+ }
2365
+
2315
2366
  // src/widgets/value.ts
2316
2367
  var ValueWidget = class extends WidgetBase {
2317
2368
  #node;
@@ -5079,6 +5130,7 @@ var FlowNode = class {
5079
5130
  #inputPortPositions;
5080
5131
  #outputPortPositions;
5081
5132
  #widgetPositions;
5133
+ #portWidgetPositions = new List();
5082
5134
  #data;
5083
5135
  #registeredAnyPropertyChangeCallbacks;
5084
5136
  #registeredPropertyChangeCallbacks;
@@ -5566,9 +5618,10 @@ var FlowNode = class {
5566
5618
  }
5567
5619
  this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
5568
5620
  size.y += this.#portRowHeight(port, tempMeasurement.y) + this.#elementSpacing;
5569
- size.x = Math.max(size.x, tempMeasurement.x + doublePadding);
5621
+ size.x = Math.max(size.x, this.#portRowWidth(port, tempMeasurement.x) + doublePadding);
5570
5622
  }
5571
- for (const widget of this.#widgets) {
5623
+ const widgets = this.#hasHiddenPortsToggle() ? [this.#hiddenPortsToggle, ...this.#widgets] : this.#widgets;
5624
+ for (const widget of widgets) {
5572
5625
  if (widget.Hidden?.()) {
5573
5626
  continue;
5574
5627
  }
@@ -5586,6 +5639,9 @@ var FlowNode = class {
5586
5639
  }
5587
5640
  addInput(config) {
5588
5641
  const port = new Port(this, config.array ? "INPUTARRAY" /* InputArray */ : "INPUT" /* Input */, config);
5642
+ if (config.widget !== void 0) {
5643
+ port.setWidget(GlobalWidgetFactory.createFromConfig(this, config.widget));
5644
+ }
5589
5645
  this.#input.push(port);
5590
5646
  return port;
5591
5647
  }
@@ -5594,6 +5650,31 @@ var FlowNode = class {
5594
5650
  this.#output.push(port);
5595
5651
  return port;
5596
5652
  }
5653
+ #hiddenPortsShown = false;
5654
+ #hiddenPortsToggle = new HiddenPortsToggle(this);
5655
+ #hiddenPortsTogglePosition = null;
5656
+ hiddenPortsShown() {
5657
+ return this.#hiddenPortsShown;
5658
+ }
5659
+ setHiddenPortsShown(shown) {
5660
+ if (shown === this.#hiddenPortsShown) {
5661
+ return;
5662
+ }
5663
+ this.#hiddenPortsShown = shown;
5664
+ this.invalidateLayout();
5665
+ }
5666
+ hiddenPortCount() {
5667
+ let count = 0;
5668
+ for (const port of [...this.#input, ...this.#output]) {
5669
+ if (port.tucked()) {
5670
+ count++;
5671
+ }
5672
+ }
5673
+ return count;
5674
+ }
5675
+ #hasHiddenPortsToggle() {
5676
+ return this.hiddenPortCount() > 0;
5677
+ }
5597
5678
  addWidget(widget) {
5598
5679
  this.#widgets.push(widget);
5599
5680
  requestRender();
@@ -5672,6 +5753,11 @@ var FlowNode = class {
5672
5753
  intersection.Port = this.#output[i];
5673
5754
  }
5674
5755
  }
5756
+ if (this.#hiddenPortsTogglePosition !== null && InBox(this.#hiddenPortsTogglePosition, position)) {
5757
+ intersection.Node = this;
5758
+ intersection.Widget = this.#hiddenPortsToggle;
5759
+ intersection.WidgetBox = this.#hiddenPortsTogglePosition;
5760
+ }
5675
5761
  for (let i = 0; i < this.#widgetPositions.Count(); i++) {
5676
5762
  const widget = this.#widgets[i];
5677
5763
  if (widget !== void 0 && !widget.Hidden?.() && InBox(this.#widgetPositions.At(i), position)) {
@@ -5681,6 +5767,17 @@ var FlowNode = class {
5681
5767
  intersection.WidgetBox = this.#widgetPositions.At(i);
5682
5768
  }
5683
5769
  }
5770
+ for (let i = 0; i < this.#portWidgetPositions.Count(); i++) {
5771
+ const port = this.#input[i];
5772
+ if (port !== void 0 && port.widgetShown() && InBox(this.#portWidgetPositions.At(i), position)) {
5773
+ intersection.Node = this;
5774
+ intersection.Widget = port.widget();
5775
+ intersection.WidgetBox = this.#portWidgetPositions.At(i);
5776
+ intersection.Port = void 0;
5777
+ intersection.PortIndex = void 0;
5778
+ intersection.PortIsInput = void 0;
5779
+ }
5780
+ }
5684
5781
  return intersection;
5685
5782
  }
5686
5783
  canEditTitle() {
@@ -5795,7 +5892,14 @@ var FlowNode = class {
5795
5892
  }
5796
5893
  #portRowHeight(port, textHeight) {
5797
5894
  const slots = port.slots();
5798
- return slots > 1 ? Math.max(textHeight, slots * ARRAY_SLOT_PITCH) : textHeight;
5895
+ const widget = port.widgetShown() ? port.widget().Size().y : 0;
5896
+ return Math.max(slots > 1 ? slots * ARRAY_SLOT_PITCH : 0, textHeight, widget);
5897
+ }
5898
+ #portRowWidth(port, textWidth) {
5899
+ if (!port.widgetShown()) {
5900
+ return textWidth;
5901
+ }
5902
+ return textWidth + this.#elementSpacing + port.widget().Size().x;
5799
5903
  }
5800
5904
  hasBeenLaidOut() {
5801
5905
  return this.#laidOut;
@@ -5817,6 +5921,7 @@ var FlowNode = class {
5817
5921
  this.#inputPortPositions.Clear();
5818
5922
  this.#outputPortPositions.Clear();
5819
5923
  this.#widgetPositions.Clear();
5924
+ this.#portWidgetPositions.Clear();
5820
5925
  const scaledPadding = this.#padding * camera.zoom;
5821
5926
  const scaledElementSpacing = this.#elementSpacing * camera.zoom;
5822
5927
  const nodeBounds = this.calculateBounds(ctx, camera);
@@ -5911,6 +6016,7 @@ var FlowNode = class {
5911
6016
  const port = this.#input[i];
5912
6017
  if (port.hidden()) {
5913
6018
  this.#inputPortPositions.Push({ Position: { x: nodeBounds.Position.x, y: startY }, Size: { x: 0, y: 0 } });
6019
+ this.#portWidgetPositions.Push({ Position: { x: nodeBounds.Position.x, y: startY }, Size: { x: 0, y: 0 } });
5914
6020
  continue;
5915
6021
  }
5916
6022
  this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
@@ -5920,6 +6026,19 @@ var FlowNode = class {
5920
6026
  position.y = startY + portHeight / 2;
5921
6027
  this.#portTextStyle.setupStyle(ctx, camera.zoom);
5922
6028
  ctx.fillText(port.getDisplayName(), leftSide, position.y);
6029
+ if (port.widgetShown()) {
6030
+ const widget = port.widget();
6031
+ const widgetHeight = widget.Size().y * camera.zoom;
6032
+ const widgetLeft = leftSide + tempMeasurement.x * camera.zoom + scaledElementSpacing;
6033
+ const box = {
6034
+ Position: { x: widgetLeft, y: startY + (portHeight - widgetHeight) / 2 },
6035
+ Size: { x: nodeBounds.Position.x + nodeBounds.Size.x - scaledPadding - widgetLeft, y: widgetHeight }
6036
+ };
6037
+ widget.Draw(ctx, box, camera.zoom, mousePosition);
6038
+ this.#portWidgetPositions.Push(box);
6039
+ } else {
6040
+ this.#portWidgetPositions.Push({ Position: { x: nodeBounds.Position.x, y: startY }, Size: { x: 0, y: 0 } });
6041
+ }
5923
6042
  this.#inputPortPositions.Push(port.render(ctx, position, camera, mousePosition, postProcess));
5924
6043
  startY += portHeight + scaledElementSpacing;
5925
6044
  }
@@ -5941,6 +6060,16 @@ var FlowNode = class {
5941
6060
  this.#outputPortPositions.Push(port.render(ctx, position, camera, mousePosition, postProcess));
5942
6061
  startY += portHeight + scaledElementSpacing;
5943
6062
  }
6063
+ this.#hiddenPortsTogglePosition = null;
6064
+ if (this.#hasHiddenPortsToggle()) {
6065
+ const box = {
6066
+ Position: { x: nodeBounds.Position.x + scaledPadding, y: startY },
6067
+ Size: { x: nodeBounds.Size.x - scaledPadding * 2, y: this.#hiddenPortsToggle.Size().y * camera.zoom }
6068
+ };
6069
+ this.#hiddenPortsToggle.Draw(ctx, box, camera.zoom, mousePosition);
6070
+ this.#hiddenPortsTogglePosition = box;
6071
+ startY += box.Size.y + scaledElementSpacing;
6072
+ }
5944
6073
  for (let i = 0; i < this.#widgets.length; i++) {
5945
6074
  const widget = this.#widgets[i];
5946
6075
  if (widget.Hidden?.()) {
@@ -5995,6 +6124,8 @@ var FlowNode = class {
5995
6124
  return false;
5996
6125
  }
5997
6126
  this.#widgetPositions.Clear();
6127
+ this.#portWidgetPositions.Clear();
6128
+ this.#hiddenPortsTogglePosition = null;
5998
6129
  return true;
5999
6130
  }
6000
6131
  placePorts(bounds, zoom) {
@@ -7655,8 +7786,8 @@ var NodeFlowGraph = class {
7655
7786
  this.#clickEnd.bind(this),
7656
7787
  this.#openContextMenu.bind(this),
7657
7788
  this.#fileDrop.bind(this),
7658
- (amount, anchor) => {
7659
- this.zoom(amount, anchor);
7789
+ (factor, anchor) => {
7790
+ this.#zoomBy(factor, anchor);
7660
7791
  }
7661
7792
  );
7662
7793
  document.addEventListener(
@@ -7784,12 +7915,15 @@ var NodeFlowGraph = class {
7784
7915
  this.#mainNoteSubsystem.addNoteDragStopListener(callback);
7785
7916
  }
7786
7917
  zoom(amount, anchor) {
7918
+ this.#zoomBy(1 + amount * 0.05, anchor);
7919
+ }
7920
+ #zoomBy(factor, anchor) {
7787
7921
  const anchorPos = anchor ?? this.#mousePosition;
7788
7922
  let oldPos = void 0;
7789
7923
  if (anchorPos !== void 0) {
7790
7924
  oldPos = this.#sceenPositionToGraphPosition(anchorPos);
7791
7925
  }
7792
- this.#camera.zoom += amount * this.#camera.zoom * 0.05;
7926
+ this.#camera.zoom *= factor;
7793
7927
  if (!oldPos || anchorPos === void 0) {
7794
7928
  return;
7795
7929
  }