stickyflow 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/binding/index.cjs +9 -9
  3. package/dist/binding/index.d.cts +4 -4
  4. package/dist/binding/index.d.ts +4 -4
  5. package/dist/binding/index.js +3 -3
  6. package/dist/{chunk-MFNUJA26.js → chunk-CZW3ARXQ.js} +156 -23
  7. package/dist/chunk-CZW3ARXQ.js.map +1 -0
  8. package/dist/{chunk-WRDVNMFS.js → chunk-EDOPENSE.js} +848 -38
  9. package/dist/chunk-EDOPENSE.js.map +1 -0
  10. package/dist/{chunk-4LENHH6C.cjs → chunk-FQ7GMLCM.cjs} +15 -15
  11. package/dist/{chunk-4LENHH6C.cjs.map → chunk-FQ7GMLCM.cjs.map} +1 -1
  12. package/dist/{chunk-4G2R7PEL.js → chunk-GLZVHUSW.js} +220 -12
  13. package/dist/chunk-GLZVHUSW.js.map +1 -0
  14. package/dist/{chunk-KTNCDNDB.cjs → chunk-IR2T47NW.cjs} +876 -66
  15. package/dist/chunk-IR2T47NW.cjs.map +1 -0
  16. package/dist/{chunk-ULQZ6CZW.js → chunk-LFEGZJWC.js} +3 -3
  17. package/dist/{chunk-ULQZ6CZW.js.map → chunk-LFEGZJWC.js.map} +1 -1
  18. package/dist/{chunk-XJ4W6TW4.cjs → chunk-LNW5VIEA.cjs} +225 -11
  19. package/dist/chunk-LNW5VIEA.cjs.map +1 -0
  20. package/dist/{chunk-FVHVOJS2.js → chunk-MAPNY7MJ.js} +872 -174
  21. package/dist/chunk-MAPNY7MJ.js.map +1 -0
  22. package/dist/{chunk-WQRT5PVT.cjs → chunk-VOYX6JMW.cjs} +1479 -780
  23. package/dist/chunk-VOYX6JMW.cjs.map +1 -0
  24. package/dist/{chunk-JIJMZMGY.cjs → chunk-XPM3E7NV.cjs} +170 -37
  25. package/dist/chunk-XPM3E7NV.cjs.map +1 -0
  26. package/dist/index.cjs +42 -34
  27. package/dist/index.d.cts +34 -6
  28. package/dist/index.d.ts +34 -6
  29. package/dist/index.js +5 -5
  30. package/dist/interaction/index.cjs +266 -235
  31. package/dist/interaction/index.cjs.map +1 -1
  32. package/dist/interaction/index.d.cts +2 -2
  33. package/dist/interaction/index.d.ts +2 -2
  34. package/dist/interaction/index.js +42 -11
  35. package/dist/interaction/index.js.map +1 -1
  36. package/dist/{render-Db0eSz_s.d.cts → render-Bp2FLEzv.d.cts} +14 -1
  37. package/dist/{render-Db0eSz_s.d.ts → render-Bp2FLEzv.d.ts} +14 -1
  38. package/dist/{runtime-n2yG-_j-.d.ts → runtime-CWIGxRcL.d.ts} +2 -2
  39. package/dist/{runtime-BhivvBeL.d.cts → runtime-DIag-XBa.d.cts} +2 -2
  40. package/dist/{runtimeTypes-DnI7mRF5.d.ts → runtimeTypes-BYODuxHk.d.cts} +2 -1
  41. package/dist/{runtimeTypes-CrkMHvVi.d.cts → runtimeTypes-C7sSLfOK.d.ts} +2 -1
  42. package/dist/stickyflow-diagram.global.js +490 -36
  43. package/dist/stickyflow-diagram.global.js.map +1 -1
  44. package/dist/{types-D4cH4f1a.d.ts → types-B9QM_H3R.d.ts} +1 -1
  45. package/dist/{types-BHfUKT4s.d.cts → types-D2w6D_wG.d.cts} +1 -1
  46. package/dist/viewer/index.cjs +9 -9
  47. package/dist/viewer/index.d.cts +5 -4
  48. package/dist/viewer/index.d.ts +5 -4
  49. package/dist/viewer/index.js +4 -4
  50. package/dist/web-component/index.cjs +6 -6
  51. package/dist/web-component/index.d.cts +2 -2
  52. package/dist/web-component/index.d.ts +2 -2
  53. package/dist/web-component/index.js +4 -4
  54. package/package.json +1 -1
  55. package/dist/chunk-4G2R7PEL.js.map +0 -1
  56. package/dist/chunk-FVHVOJS2.js.map +0 -1
  57. package/dist/chunk-JIJMZMGY.cjs.map +0 -1
  58. package/dist/chunk-KTNCDNDB.cjs.map +0 -1
  59. package/dist/chunk-MFNUJA26.js.map +0 -1
  60. package/dist/chunk-WQRT5PVT.cjs.map +0 -1
  61. package/dist/chunk-WRDVNMFS.js.map +0 -1
  62. package/dist/chunk-XJ4W6TW4.cjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -17,6 +17,44 @@ Two things count as breaking here that are easy to overlook:
17
17
 
18
18
  ## [Unreleased]
19
19
 
20
+ ## [0.8.0] - 2026-10-28
21
+
22
+ ### Added
23
+
24
+ - The shader program `glowCard`, the surface of a card: dark glass with a thin edge and colored lights low in the card that curl like smoke, fade out from the side set by `angle` and turn white at that edge, with grit in the lit parts. Parameters are `colorA`, `colorB`, `colorC`, `baseColor`, `glowHeight`, `glowSpread`, `angle`, `core`, `rim`, `grain`, `warp`, `intensity`, `cornerRadius` and `speed`. It draws its own rectangle.
25
+ - The names and the color bar of a heatmap can stand on any side of its grid. `rowLabelSide` is `left` or `right`, `columnLabelSide` is `top` or `bottom`, and `legendSide` is `top`, `bottom`, `left` or `right`. `legendGap` sets the distance between the grid and the color bar, and `axisLabelSpace` sets how much room the names get beside the data. Left out, the room is as much as the longest name needs. The names of a heatmap follow the font, size and color of the axis.
26
+ - `xLabelGap` and `yLabelGap` set how far the labels stand from the graphic, in pixels, along the x axis and along the y axis.
27
+ - `titleAlign` and `axisLabelAlign` align the title over a chart and the names beside the data to `left`, `center` or `right`.
28
+ - `axisLabelAngle` stands the labels of heatmap columns, bar categories and boxplot boxes up, from `0` for flat to `90` for upright.
29
+ - `data-sf-snap-distance` on the svg sets in pixels how close a shape has to come to another before it snaps. Without it the distance stays 20.
30
+ - Round charts (`pie`, `radar`, `radial`, `radialBar` and `polarArea`) keep their proportions while they are resized, and any shape does while Shift is held.
31
+
32
+ ## [0.7.0] - 2026-10-25
33
+
34
+ ### Added
35
+
36
+ - The components `slider`, `progress` and `segmented`. A `slider` takes a value between `min` and `max` from the pointer, in steps of `step` if set. A `progress` shows a value as a bar or, with `variant: "ring"`, as a ring, and can write the share or the value into itself with `display`. A `segmented` shows the labels of `options`, separated by commas, and its value is the number of the chosen one, counted from 0.
37
+ - Three shader programs for backgrounds, which draw a rectangle or a rounded rectangle of their own, as `reactivePanel` does. `ledMatrix` is a wall of small pill-shaped lights with a glow that rises and drifts. `lightBand` is a band of light with a glow that runs through three colors. `silkFolds` is a folded cloth of satin with thin bright edges. Each one has `cornerRadius`, as a share of the shorter side, and `speed`, where 0 gives a still picture. `ledMatrix` and `silkFolds` react to the pointer, and `reaction` sets how strongly. On `ledMatrix` the places the pointer has been dim down behind it, and `trail` is how many seconds that takes. `pointerSize` sets how wide that stripe is. Its light can be moved with `originX` and `originY`, sized with `spread` and put out with 0, and `drift` sets how far it sways.
38
+ - A shader program can read `uWake`, 16 places the pointer has been over its surface, each as x, y and how much of it is left, when it has a number parameter `trail`, the seconds a place takes to fade out.
39
+ - A shader program can read `uPointer`, where the pointer is over its surface from 0 to 1 and counted from the lower left, and `uHover`, which glides to 1 while the pointer is over the surface and back to 0 when it leaves.
40
+ - The component `range`, a slider with two thumbs. `value` is the lower end and `valueEnd` the upper end, and both can be bound with `<->`, each to a field of its own. The nearer thumb follows the pointer, and the thumbs do not pass each other. It takes the `variant`, `direction` and `ticks` of the slider. Its interaction is `range`.
41
+ - `valueEnd` is a bindable property of a component that has a number property of that name. `runtime.slide(id, x, y)` takes the position as fractions of the width and the height.
42
+ - `componentTypes()` is exported from the main entry.
43
+ - `category` on a component descriptor, and `COMPONENT_CATEGORIES`, the list of the shelves a component is filed on: actions, selection, value input, text input, indicators, instruments, navigation and containers. The built-in components name their shelf.
44
+ - The component `knob`, a dial with an arc that fills up to the value. The pointer sets the value by its angle round the dial, between `min` and `max`, in steps of `step`. `start` and `sweep` set where the arc begins and how far it runs, and `display`, `thickness`, `face` and `dot` style it. Its interaction is `turn`.
45
+ - `direction: "vertical"` on a `slider` and on a `progress` bar stands them upright, and they fill from below. `ticks` on a `slider` draws that many marks along it, the two ends included, and `tickColor` colors them.
46
+ - `variant: "field"` on a `slider` draws a field that fills up to the value, with a thin handle at its end, the `label` at the left and the value at the right (`display` is `value`, `percent` or `none`). `surface`, `fill`, `radius`, `textColor` and `fontSize` style it.
47
+ - The component interactions `slide` and `pick`, and `runtime.slide(id, fraction)` on a binding runtime, which turns a position along the width of a component into its value.
48
+ - `animate` on `switch`, `checkbox`, `toggleButton` and `button`, and on the new components. It is the number of milliseconds a change of position, size, color or opacity takes to show. The default is 160 for the first four, 0 for `slider`, 300 for `progress` and 200 for `segmented`. A scene style can carry the same as `transition`, and the viewer patches the elements in place, so the browser does the animation.
49
+ - The shader program `reactivePanel` draws a rounded panel that can send light into the scene and receive it. A panel with `emission` lights the edges of the panels near it on the side that faces it. `reception` sets how much a panel takes in, `emitterFill`, `grain` and `grainScale` light and texture the inside of an emitter, and `glowRadius`, `borderWidth` and `cornerRadius` shape the glow and the border.
50
+ - `surfacePadding` on a `Shader` adds room around the shape for an outer glow, in canvas units. It is not a parameter of the program.
51
+ - A shader on a `rectangle` or a `rounded_rectangle` is drawn when its program draws a rectangle of its own. Today that is `reactivePanel`. Every other program still needs a `circle` or an `oval`.
52
+ - `self.id` in an expression is the id of the element the expression belongs to. In a binding of a `Shader` it is read separately for every shape that uses the shader, so one shader can light the tapped panel and no other. In an element handler it is the element that was pressed, so `onTap: glow.active <- self.id` works for any number of elements.
53
+
54
+ ### Changed
55
+
56
+ - Every shader surface in the SVG carries `data-sf-shader-padding`, `0` when there is no padding.
57
+
20
58
  ## [0.6.0] - 2026-10-22
21
59
 
22
60
  ### Added
@@ -235,4 +273,5 @@ First versioned release. Not yet published to npm.
235
273
  across machines. Now a locale-independent code-point compare.
236
274
 
237
275
  [Unreleased]: https://github.com/D-Queck/stickyflow.js/compare/v0.1.0...HEAD
276
+ [0.7.0]: https://github.com/D-Queck/stickyflow.js/releases/tag/v0.7.0
238
277
  [0.1.0]: https://github.com/D-Queck/stickyflow.js/releases/tag/v0.1.0
@@ -1,34 +1,34 @@
1
1
  'use strict';
2
2
 
3
- var chunkJIJMZMGY_cjs = require('../chunk-JIJMZMGY.cjs');
4
- require('../chunk-WQRT5PVT.cjs');
5
- require('../chunk-XJ4W6TW4.cjs');
3
+ var chunkXPM3E7NV_cjs = require('../chunk-XPM3E7NV.cjs');
4
+ require('../chunk-VOYX6JMW.cjs');
5
+ require('../chunk-LNW5VIEA.cjs');
6
6
 
7
7
 
8
8
 
9
9
  Object.defineProperty(exports, "buildBindingGraph", {
10
10
  enumerable: true,
11
- get: function () { return chunkJIJMZMGY_cjs.buildBindingGraph; }
11
+ get: function () { return chunkXPM3E7NV_cjs.buildBindingGraph; }
12
12
  });
13
13
  Object.defineProperty(exports, "collectContracts", {
14
14
  enumerable: true,
15
- get: function () { return chunkJIJMZMGY_cjs.collectContracts; }
15
+ get: function () { return chunkXPM3E7NV_cjs.collectContracts; }
16
16
  });
17
17
  Object.defineProperty(exports, "createBindingRuntime", {
18
18
  enumerable: true,
19
- get: function () { return chunkJIJMZMGY_cjs.createBindingRuntime; }
19
+ get: function () { return chunkXPM3E7NV_cjs.createBindingRuntime; }
20
20
  });
21
21
  Object.defineProperty(exports, "createDataSource", {
22
22
  enumerable: true,
23
- get: function () { return chunkJIJMZMGY_cjs.createDataSource; }
23
+ get: function () { return chunkXPM3E7NV_cjs.createDataSource; }
24
24
  });
25
25
  Object.defineProperty(exports, "interactiveElements", {
26
26
  enumerable: true,
27
- get: function () { return chunkJIJMZMGY_cjs.interactiveElements; }
27
+ get: function () { return chunkXPM3E7NV_cjs.interactiveElements; }
28
28
  });
29
29
  Object.defineProperty(exports, "renderBound", {
30
30
  enumerable: true,
31
- get: function () { return chunkJIJMZMGY_cjs.renderBound; }
31
+ get: function () { return chunkXPM3E7NV_cjs.renderBound; }
32
32
  });
33
33
  //# sourceMappingURL=index.cjs.map
34
34
  //# sourceMappingURL=index.cjs.map
@@ -1,7 +1,7 @@
1
- import { S as SflDocument, D as DataSourceContract, B as BindingDiagnostic } from '../runtimeTypes-CrkMHvVi.cjs';
2
- export { a as BindingGraph, b as BindingRuntime, c as BindingRuntimeOptions, d as BindingTarget, e as BindingType, f as BindingValue, g as BoundAssignment, h as BoundHandler, i as BoundProperty, j as DataSource, E as ElementInfo, k as ElementState, I as InteractiveElements, M as MutableDataSource, l as buildBindingGraph, m as createDataSource, n as interactiveElements } from '../runtimeTypes-CrkMHvVi.cjs';
3
- export { R as RenderBoundOptions, c as createBindingRuntime, r as renderBound } from '../runtime-BhivvBeL.cjs';
4
- import '../render-Db0eSz_s.cjs';
1
+ import { S as SflDocument, D as DataSourceContract, B as BindingDiagnostic } from '../runtimeTypes-BYODuxHk.cjs';
2
+ export { a as BindingGraph, b as BindingRuntime, c as BindingRuntimeOptions, d as BindingTarget, e as BindingType, f as BindingValue, g as BoundAssignment, h as BoundHandler, i as BoundProperty, j as DataSource, E as ElementInfo, k as ElementState, I as InteractiveElements, M as MutableDataSource, l as buildBindingGraph, m as createDataSource, n as interactiveElements } from '../runtimeTypes-BYODuxHk.cjs';
3
+ export { R as RenderBoundOptions, c as createBindingRuntime, r as renderBound } from '../runtime-DIag-XBa.cjs';
4
+ import '../render-Bp2FLEzv.cjs';
5
5
 
6
6
  declare function collectContracts(sflDocument: SflDocument): {
7
7
  contracts: Map<string, DataSourceContract>;
@@ -1,7 +1,7 @@
1
- import { S as SflDocument, D as DataSourceContract, B as BindingDiagnostic } from '../runtimeTypes-DnI7mRF5.js';
2
- export { a as BindingGraph, b as BindingRuntime, c as BindingRuntimeOptions, d as BindingTarget, e as BindingType, f as BindingValue, g as BoundAssignment, h as BoundHandler, i as BoundProperty, j as DataSource, E as ElementInfo, k as ElementState, I as InteractiveElements, M as MutableDataSource, l as buildBindingGraph, m as createDataSource, n as interactiveElements } from '../runtimeTypes-DnI7mRF5.js';
3
- export { R as RenderBoundOptions, c as createBindingRuntime, r as renderBound } from '../runtime-n2yG-_j-.js';
4
- import '../render-Db0eSz_s.js';
1
+ import { S as SflDocument, D as DataSourceContract, B as BindingDiagnostic } from '../runtimeTypes-C7sSLfOK.js';
2
+ export { a as BindingGraph, b as BindingRuntime, c as BindingRuntimeOptions, d as BindingTarget, e as BindingType, f as BindingValue, g as BoundAssignment, h as BoundHandler, i as BoundProperty, j as DataSource, E as ElementInfo, k as ElementState, I as InteractiveElements, M as MutableDataSource, l as buildBindingGraph, m as createDataSource, n as interactiveElements } from '../runtimeTypes-C7sSLfOK.js';
3
+ export { R as RenderBoundOptions, c as createBindingRuntime, r as renderBound } from '../runtime-CWIGxRcL.js';
4
+ import '../render-Bp2FLEzv.js';
5
5
 
6
6
  declare function collectContracts(sflDocument: SflDocument): {
7
7
  contracts: Map<string, DataSourceContract>;
@@ -1,5 +1,5 @@
1
- export { buildBindingGraph, collectContracts, createBindingRuntime, createDataSource, interactiveElements, renderBound } from '../chunk-MFNUJA26.js';
2
- import '../chunk-FVHVOJS2.js';
3
- import '../chunk-4G2R7PEL.js';
1
+ export { buildBindingGraph, collectContracts, createBindingRuntime, createDataSource, interactiveElements, renderBound } from '../chunk-CZW3ARXQ.js';
2
+ import '../chunk-MAPNY7MJ.js';
3
+ import '../chunk-GLZVHUSW.js';
4
4
  //# sourceMappingURL=index.js.map
5
5
  //# sourceMappingURL=index.js.map
@@ -1,5 +1,5 @@
1
- import { renderCanvas, sflToRenderInput } from './chunk-FVHVOJS2.js';
2
- import { getComponentDescriptor, parseSource, isGenericComponent, isBuiltInComponentType } from './chunk-4G2R7PEL.js';
1
+ import { renderCanvas, sflToRenderInput, sliderReach } from './chunk-MAPNY7MJ.js';
2
+ import { getComponentDescriptor, parseSource, isGenericComponent, isBuiltInComponentType } from './chunk-GLZVHUSW.js';
3
3
 
4
4
  // src/binding/types.ts
5
5
  var DECLARED = {
@@ -98,7 +98,7 @@ function createDataSource(initial) {
98
98
  }
99
99
 
100
100
  // src/binding/componentBinding.ts
101
- var BINDABLE_COMPONENT_PROPERTIES = ["value", "disabled"];
101
+ var BINDABLE_COMPONENT_PROPERTIES = ["value", "valueEnd", "disabled"];
102
102
  function acceptsUserChange(componentType) {
103
103
  return getComponentDescriptor(componentType)?.value?.writable === true;
104
104
  }
@@ -116,6 +116,9 @@ function componentPropertyType(componentType, property) {
116
116
  if (property === "disabled") {
117
117
  return "boolean";
118
118
  }
119
+ if (property === "valueEnd") {
120
+ return getComponentDescriptor(componentType)?.properties.valueEnd?.type === "number" ? "number" : void 0;
121
+ }
119
122
  return property === "value" ? getComponentDescriptor(componentType)?.value?.type : void 0;
120
123
  }
121
124
  function twoWayTargetProblem(key, expr, contracts) {
@@ -260,7 +263,7 @@ function inferType(expr, scope, report) {
260
263
  const name = expr.path.join(".");
261
264
  report(
262
265
  "unknown-reference",
263
- name === "time.delta" ? "'time.delta' is only available in an onTick handler." : `Unknown reference '${name}'. Use <DataSource>.<field>, or <element>.pressed / hovered / pointer.`
266
+ name === "time.delta" ? "'time.delta' is only available in an onTick handler." : `Unknown reference '${name}'. Use <DataSource>.<field>, self.id, or <element>.pressed / hovered / pointer.`
264
267
  );
265
268
  }
266
269
  return type;
@@ -429,12 +432,15 @@ function elementStateType(element, state) {
429
432
  }
430
433
  return element.kind === "Component" ? componentStateType(element.componentType, state) : void 0;
431
434
  }
432
- function createScope(contracts, elements, ticking = false) {
435
+ function createScope(contracts, elements, ticking = false, selfAvailable = false) {
433
436
  return {
434
437
  resolve(path) {
435
438
  if (path.length !== 2) {
436
439
  return void 0;
437
440
  }
441
+ if (selfAvailable && path[0] === "self" && path[1] === "id") {
442
+ return "string";
443
+ }
438
444
  const contract = contracts.get(path[0]);
439
445
  if (contract) {
440
446
  return fieldType(contract, path[1]);
@@ -542,7 +548,7 @@ function bindHandler(site, contracts, elements, diagnostics) {
542
548
  report("tick-needs-record", `DataSource '${name}' has to be a record source to change its fields on every tick.`);
543
549
  return void 0;
544
550
  }
545
- const scope = createScope(contracts, elements, ticking);
551
+ const scope = createScope(contracts, elements, ticking, !ticking);
546
552
  const assignments = handler.assignments.flatMap(
547
553
  (assignment) => bindAssignment(assignment, name, own, contracts, scope, report) ?? []
548
554
  );
@@ -581,7 +587,7 @@ function expectedType(site, elements) {
581
587
  }
582
588
  function canBindTwoWay(site, elements) {
583
589
  const { kind, path, binding } = site;
584
- return kind === "Component" && binding.key === "value" && acceptsUserChange(elements.get(path[0])?.componentType);
590
+ return kind === "Component" && (binding.key === "value" || binding.key === "valueEnd") && acceptsUserChange(elements.get(path[0])?.componentType);
585
591
  }
586
592
  function rejectedOperator(site, elements, contracts) {
587
593
  const { binding } = site;
@@ -648,7 +654,7 @@ function buildBindingGraph(sflDocument) {
648
654
  const { contracts, diagnostics } = collectContracts(sflDocument);
649
655
  const elements = collectElements(sflDocument);
650
656
  warnNameCollisions(contracts, elements, diagnostics);
651
- const scope = createScope(contracts, elements);
657
+ const scope = createScope(contracts, elements, false, true);
652
658
  const sites = [];
653
659
  collectSites(sflDocument.blocks, [], sites);
654
660
  const bindings = sites.flatMap((site) => bindProperty(site, scope, elements, contracts, diagnostics) ?? []);
@@ -659,7 +665,7 @@ function buildBindingGraph(sflDocument) {
659
665
  }
660
666
 
661
667
  // src/binding/interactive.ts
662
- var OPERATED = /* @__PURE__ */ new Set(["toggle", "press"]);
668
+ var OPERATED = /* @__PURE__ */ new Set(["toggle", "press", "slide", "pick", "turn", "range"]);
663
669
  function interactiveElements(graph) {
664
670
  const pressable = /* @__PURE__ */ new Set();
665
671
  const scales = /* @__PURE__ */ new Set();
@@ -733,6 +739,12 @@ function writeComponentValue(input, id, value) {
733
739
  }
734
740
  component.value = isGenericComponent(component) ? value : String(value);
735
741
  }
742
+ function writeComponentValueEnd(input, id, value) {
743
+ const component = findComponent(input, id);
744
+ if (component && isGenericComponent(component)) {
745
+ component.properties.valueEnd = value;
746
+ }
747
+ }
736
748
  function writeComponentDisabled(input, id, disabled) {
737
749
  const component = findComponent(input, id);
738
750
  if (component && isGenericComponent(component)) {
@@ -775,13 +787,17 @@ var APPLY = {
775
787
  "Shape.textColor": (input, path, value) => setShape(input, path, (shape) => shape.textColor = value),
776
788
  "Shape.opacity": (input, path, value) => setShape(input, path, (shape) => shape.opacity = value),
777
789
  "Component.value": (input, path, value) => writeComponentValue(input, path[0], value),
790
+ "Component.valueEnd": (input, path, value) => writeComponentValueEnd(input, path[0], value),
778
791
  "Component.disabled": (input, path, value) => writeComponentDisabled(input, path[0], value === true)
779
792
  };
780
- function applyShader(input, binding, value) {
793
+ function applyShader(input, binding, value, ownerId) {
781
794
  const owners = [];
782
795
  for (const shape of input.shapes) {
783
- if (shape.shader?.name === binding.ownerPath[0]) {
784
- shape.shader.parameters = { ...shape.shader.parameters, [binding.property]: value };
796
+ if (shape.shader?.name === binding.ownerPath[0] && (ownerId === void 0 || shape.id === ownerId)) {
797
+ shape.shader = {
798
+ ...shape.shader,
799
+ parameters: { ...shape.shader.parameters, [binding.property]: value }
800
+ };
785
801
  owners.push(shape.id);
786
802
  }
787
803
  }
@@ -846,7 +862,7 @@ function readValue(state, path) {
846
862
  return Array.isArray(data) ? data.map((row) => row[field]) : data[field];
847
863
  }
848
864
  function missingSource(state, binding) {
849
- return binding.dependencies.map((dependency) => dependency.split(".")[0]).find((name) => !state.sources.has(name) && !state.elementState.has(name));
865
+ return binding.dependencies.map((dependency) => dependency.split(".")[0]).find((name) => name !== "self" && !state.sources.has(name) && !state.elementState.has(name));
850
866
  }
851
867
 
852
868
  // src/binding/evaluation.ts
@@ -888,7 +904,32 @@ function evaluateBinding(state, binding) {
888
904
  warnMissingSource(state, binding, missing);
889
905
  return false;
890
906
  }
891
- const value = evaluate(binding.expr, { read: (path) => readValue(state, path) });
907
+ const readsSelf = binding.dependencies.includes("self.id");
908
+ if (binding.ownerKind === "Shader" && readsSelf) {
909
+ const owners = state.input.shapes.filter((shape) => shape.shader?.name === binding.ownerPath[0]);
910
+ for (const owner of owners) {
911
+ const value2 = evaluate(binding.expr, {
912
+ read: (path) => path[0] === "self" && path[1] === "id" ? owner.id : readValue(state, path)
913
+ });
914
+ const appliedId = `${binding.id}.${owner.id}`;
915
+ const key2 = JSON.stringify(value2);
916
+ if (state.applied.get(appliedId) === key2) {
917
+ continue;
918
+ }
919
+ state.applied.set(appliedId, key2);
920
+ applyShader(state.input, binding, value2, owner.id);
921
+ announce(state, appliedId, {
922
+ kind: "shader",
923
+ owners: [owner.id],
924
+ parameters: { [binding.property]: value2 }
925
+ });
926
+ }
927
+ return false;
928
+ }
929
+ const selfId = binding.ownerKind === "Shader" ? void 0 : binding.ownerPath[0];
930
+ const value = evaluate(binding.expr, {
931
+ read: (path) => path[0] === "self" && path[1] === "id" && selfId ? selfId : readValue(state, path)
932
+ });
892
933
  const key = JSON.stringify(value);
893
934
  if (state.applied.get(binding.id) === key) {
894
935
  return false;
@@ -946,14 +987,22 @@ function markDirty(state, name) {
946
987
  }
947
988
 
948
989
  // src/binding/handlers.ts
949
- function runHandler(state, handler, delta) {
990
+ function runHandler(state, handler, delta, selfId) {
950
991
  const env = {
951
- read: (path) => path[0] === "time" && path[1] === "delta" ? delta ?? 0 : readValue(state, path)
992
+ read: (path) => {
993
+ if (path[0] === "time" && path[1] === "delta") {
994
+ return delta ?? 0;
995
+ }
996
+ if (path[0] === "self" && path[1] === "id" && selfId) {
997
+ return selfId;
998
+ }
999
+ return readValue(state, path);
1000
+ }
952
1001
  };
953
1002
  for (const assignment of handler.assignments) {
954
1003
  const target = state.sources.get(assignment.source);
955
1004
  const readable = collectRefs(assignment.expr).every(
956
- ([name]) => name === "time" || state.sources.has(name) || state.elementState.has(name)
1005
+ ([name]) => name === "time" || name === "self" && Boolean(selfId) || state.sources.has(name) || state.elementState.has(name)
957
1006
  );
958
1007
  if (!target || !("set" in target) || !readable) {
959
1008
  continue;
@@ -974,7 +1023,7 @@ function runHandler(state, handler, delta) {
974
1023
  function dispatch(state, id, event) {
975
1024
  for (const handler of state.graph.handlers) {
976
1025
  if (handler.event === event && handler.ownerKind !== "DataSource" && handler.ownerPath[0] === id) {
977
- runHandler(state, handler);
1026
+ runHandler(state, handler, void 0, id);
978
1027
  }
979
1028
  }
980
1029
  }
@@ -1000,9 +1049,9 @@ function descriptorOf(state, id) {
1000
1049
  const info = state.graph.elements.get(id);
1001
1050
  return info?.kind === "Component" ? getComponentDescriptor(info.componentType) : void 0;
1002
1051
  }
1003
- function valueBinding(state, id) {
1052
+ function valueBinding(state, id, property = "value") {
1004
1053
  return state.graph.bindings.find(
1005
- (binding) => binding.ownerKind === "Component" && binding.ownerPath[0] === id && binding.property === "value"
1054
+ (binding) => binding.ownerKind === "Component" && binding.ownerPath[0] === id && binding.property === property
1006
1055
  );
1007
1056
  }
1008
1057
  function writeBack(state, binding, value) {
@@ -1016,7 +1065,30 @@ function writeBack(state, binding, value) {
1016
1065
  source.set({ ...row, [target[1]]: value });
1017
1066
  }
1018
1067
  }
1019
- function changeComponentValue(state, id, next) {
1068
+ function changeValueEnd(state, id, next) {
1069
+ const component = findComponent(state.input, id);
1070
+ if (!component || !isGenericComponent(component) || component.properties.valueEnd === next || isComponentDisabled(state.input, id)) {
1071
+ return false;
1072
+ }
1073
+ const binding = valueBinding(state, id, "valueEnd");
1074
+ if (binding && binding.operator !== "<->") {
1075
+ return false;
1076
+ }
1077
+ writeComponentValueEnd(state.input, id, next);
1078
+ state.renderNeeded = true;
1079
+ if (binding) {
1080
+ state.applied.set(binding.id, JSON.stringify(next));
1081
+ writeBack(state, binding, next);
1082
+ }
1083
+ markDirty(state, id);
1084
+ dispatch(state, id, CHANGE_EVENT);
1085
+ schedule(state);
1086
+ return true;
1087
+ }
1088
+ function changeComponentValue(state, id, next, property = "value") {
1089
+ if (property === "valueEnd") {
1090
+ return typeof next === "number" && changeValueEnd(state, id, next);
1091
+ }
1020
1092
  const spec = descriptorOf(state, id)?.value;
1021
1093
  const element = state.elementState.get(id);
1022
1094
  if (!spec?.writable || !element || typeof next !== spec.type || element.value === next) {
@@ -1041,6 +1113,64 @@ function changeComponentValue(state, id, next) {
1041
1113
  schedule(state);
1042
1114
  return true;
1043
1115
  }
1116
+ var TAU = Math.PI * 2;
1117
+ var grabbed = /* @__PURE__ */ new WeakMap();
1118
+ var within = (value) => Math.min(1, Math.max(0, value));
1119
+ function slideAlong(properties, width, height, x, y) {
1120
+ const vertical = properties.direction === "vertical";
1121
+ const length = vertical ? height : width;
1122
+ const reach = sliderReach(properties.variant, vertical ? width : height);
1123
+ const position = vertical ? 1 - y : x;
1124
+ return length > reach * 2 ? within((position * length - reach) / (length - reach * 2)) : position;
1125
+ }
1126
+ function turnAlong(properties, width, height, x, y) {
1127
+ const start = Number(properties.start ?? 225) * Math.PI / 180;
1128
+ const sweep = Number(properties.sweep ?? 270) * Math.PI / 180;
1129
+ const gap = TAU - sweep;
1130
+ const angle = Math.atan2(-(y - 0.5) * height, (x - 0.5) * width);
1131
+ const shifted = start - angle + gap / 2;
1132
+ return within(((shifted % TAU + TAU) % TAU - gap / 2) / sweep);
1133
+ }
1134
+ function slideElement(state, id, x, y = 0) {
1135
+ const component = findComponent(state.input, id);
1136
+ if (!component || !isGenericComponent(component)) {
1137
+ return;
1138
+ }
1139
+ const kind = descriptorOf(state, id)?.interaction;
1140
+ const { properties } = component;
1141
+ if (kind === "pick") {
1142
+ const count = String(properties.options ?? "").split(",").filter((option) => option.trim()).length;
1143
+ if (count > 0) {
1144
+ changeComponentValue(state, id, Math.min(count - 1, Math.floor(x * count)));
1145
+ }
1146
+ return;
1147
+ }
1148
+ const along = kind === "turn" ? turnAlong(properties, component.width, component.height, x, y) : slideAlong(properties, component.width, component.height, x, y);
1149
+ const min = Number(properties.min ?? 0);
1150
+ const max = Number(properties.max ?? 100);
1151
+ const step = Number(properties.step ?? 0);
1152
+ let value = min + along * (max - min);
1153
+ if (step > 0) {
1154
+ value = min + Math.round((value - min) / step) * step;
1155
+ }
1156
+ const taken = Number(Math.min(max, Math.max(min, value)).toFixed(6));
1157
+ if (kind !== "range") {
1158
+ changeComponentValue(state, id, taken);
1159
+ return;
1160
+ }
1161
+ const low = Number(component.value);
1162
+ const high = Number(properties.valueEnd ?? max);
1163
+ const holds = grabbed.get(state) ?? /* @__PURE__ */ new Map();
1164
+ grabbed.set(state, holds);
1165
+ if (!holds.has(id)) {
1166
+ holds.set(id, taken >= high || taken > low && taken - low > high - taken ? "end" : "start");
1167
+ }
1168
+ if (holds.get(id) === "start") {
1169
+ changeComponentValue(state, id, Math.min(taken, high));
1170
+ } else {
1171
+ changeComponentValue(state, id, Math.max(taken, low), "valueEnd");
1172
+ }
1173
+ }
1044
1174
  function reactToActivation(state, id) {
1045
1175
  const element = state.elementState.get(id);
1046
1176
  const descriptor = descriptorOf(state, id);
@@ -1049,6 +1179,7 @@ function reactToActivation(state, id) {
1049
1179
  }
1050
1180
  }
1051
1181
  function pressElement(state, id) {
1182
+ grabbed.get(state)?.delete(id);
1052
1183
  if (isComponentDisabled(state.input, id)) {
1053
1184
  return;
1054
1185
  }
@@ -1056,6 +1187,7 @@ function pressElement(state, id) {
1056
1187
  dispatch(state, id, "onPress");
1057
1188
  }
1058
1189
  function releaseElement(state, id, tapped) {
1190
+ grabbed.get(state)?.delete(id);
1059
1191
  setElementState(state, id, { pressed: false });
1060
1192
  if (isComponentDisabled(state.input, id)) {
1061
1193
  return;
@@ -1152,6 +1284,7 @@ function createBindingRuntime(sfl, options = {}) {
1152
1284
  release: (id, tapped) => releaseElement(state, id, tapped),
1153
1285
  activate: (id) => activateElement(state, id),
1154
1286
  change: (id, value) => changeComponentValue(state, id, value),
1287
+ slide: (id, x, y) => slideElement(state, id, x, y),
1155
1288
  drag(id, pointer) {
1156
1289
  if (pointer !== void 0) {
1157
1290
  setElementState(state, id, { pointer });
@@ -1178,5 +1311,5 @@ function renderBound(sfl, options = {}) {
1178
1311
  }
1179
1312
 
1180
1313
  export { buildBindingGraph, collectContracts, createBindingRuntime, createDataSource, interactiveElements, renderBound };
1181
- //# sourceMappingURL=chunk-MFNUJA26.js.map
1182
- //# sourceMappingURL=chunk-MFNUJA26.js.map
1314
+ //# sourceMappingURL=chunk-CZW3ARXQ.js.map
1315
+ //# sourceMappingURL=chunk-CZW3ARXQ.js.map