@invinite-org/chartlang-adapter-kit 1.3.0 → 1.5.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 (155) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +7 -0
  3. package/dist/canvas/index.d.ts +5 -0
  4. package/dist/canvas/index.d.ts.map +1 -0
  5. package/dist/canvas/index.js +5 -0
  6. package/dist/canvas/index.js.map +1 -0
  7. package/dist/canvas/mockContext.d.ts +168 -0
  8. package/dist/canvas/mockContext.d.ts.map +1 -0
  9. package/dist/canvas/mockContext.js +198 -0
  10. package/dist/canvas/mockContext.js.map +1 -0
  11. package/dist/canvas/paintPrimitive.d.ts +35 -0
  12. package/dist/canvas/paintPrimitive.d.ts.map +1 -0
  13. package/dist/canvas/paintPrimitive.js +171 -0
  14. package/dist/canvas/paintPrimitive.js.map +1 -0
  15. package/dist/canvas/renderCtx.d.ts +40 -0
  16. package/dist/canvas/renderCtx.d.ts.map +1 -0
  17. package/dist/canvas/renderCtx.js +4 -0
  18. package/dist/canvas/renderCtx.js.map +1 -0
  19. package/dist/geometry/_lib/arrowhead.d.ts +18 -0
  20. package/dist/geometry/_lib/arrowhead.d.ts.map +1 -0
  21. package/dist/geometry/_lib/arrowhead.js +38 -0
  22. package/dist/geometry/_lib/arrowhead.js.map +1 -0
  23. package/dist/geometry/_lib/bezier.d.ts +57 -0
  24. package/dist/geometry/_lib/bezier.d.ts.map +1 -0
  25. package/dist/geometry/_lib/bezier.js +84 -0
  26. package/dist/geometry/_lib/bezier.js.map +1 -0
  27. package/dist/geometry/_lib/chevron.d.ts +29 -0
  28. package/dist/geometry/_lib/chevron.d.ts.map +1 -0
  29. package/dist/geometry/_lib/chevron.js +37 -0
  30. package/dist/geometry/_lib/chevron.js.map +1 -0
  31. package/dist/geometry/_lib/dash.d.ts +30 -0
  32. package/dist/geometry/_lib/dash.d.ts.map +1 -0
  33. package/dist/geometry/_lib/dash.js +40 -0
  34. package/dist/geometry/_lib/dash.js.map +1 -0
  35. package/dist/geometry/_lib/fibLevels.d.ts +36 -0
  36. package/dist/geometry/_lib/fibLevels.d.ts.map +1 -0
  37. package/dist/geometry/_lib/fibLevels.js +44 -0
  38. package/dist/geometry/_lib/fibLevels.js.map +1 -0
  39. package/dist/geometry/_lib/gannLevels.d.ts +54 -0
  40. package/dist/geometry/_lib/gannLevels.d.ts.map +1 -0
  41. package/dist/geometry/_lib/gannLevels.js +88 -0
  42. package/dist/geometry/_lib/gannLevels.js.map +1 -0
  43. package/dist/geometry/_lib/lineExtend.d.ts +31 -0
  44. package/dist/geometry/_lib/lineExtend.d.ts.map +1 -0
  45. package/dist/geometry/_lib/lineExtend.js +48 -0
  46. package/dist/geometry/_lib/lineExtend.js.map +1 -0
  47. package/dist/geometry/_lib/namedPolyline.d.ts +25 -0
  48. package/dist/geometry/_lib/namedPolyline.d.ts.map +1 -0
  49. package/dist/geometry/_lib/namedPolyline.js +64 -0
  50. package/dist/geometry/_lib/namedPolyline.js.map +1 -0
  51. package/dist/geometry/_lib/pitchforkGeom.d.ts +46 -0
  52. package/dist/geometry/_lib/pitchforkGeom.d.ts.map +1 -0
  53. package/dist/geometry/_lib/pitchforkGeom.js +70 -0
  54. package/dist/geometry/_lib/pitchforkGeom.js.map +1 -0
  55. package/dist/geometry/_lib/shapeStyle.d.ts +21 -0
  56. package/dist/geometry/_lib/shapeStyle.d.ts.map +1 -0
  57. package/dist/geometry/_lib/shapeStyle.js +41 -0
  58. package/dist/geometry/_lib/shapeStyle.js.map +1 -0
  59. package/dist/geometry/_lib/strokeStyle.d.ts +34 -0
  60. package/dist/geometry/_lib/strokeStyle.d.ts.map +1 -0
  61. package/dist/geometry/_lib/strokeStyle.js +26 -0
  62. package/dist/geometry/_lib/strokeStyle.js.map +1 -0
  63. package/dist/geometry/_lib/textStyle.d.ts +70 -0
  64. package/dist/geometry/_lib/textStyle.d.ts.map +1 -0
  65. package/dist/geometry/_lib/textStyle.js +78 -0
  66. package/dist/geometry/_lib/textStyle.js.map +1 -0
  67. package/dist/geometry/decompose.d.ts +28 -0
  68. package/dist/geometry/decompose.d.ts.map +1 -0
  69. package/dist/geometry/decompose.js +176 -0
  70. package/dist/geometry/decompose.js.map +1 -0
  71. package/dist/geometry/index.d.ts +4 -0
  72. package/dist/geometry/index.d.ts.map +1 -0
  73. package/dist/geometry/index.js +5 -0
  74. package/dist/geometry/index.js.map +1 -0
  75. package/dist/geometry/kinds/annotations.d.ts +77 -0
  76. package/dist/geometry/kinds/annotations.d.ts.map +1 -0
  77. package/dist/geometry/kinds/annotations.js +219 -0
  78. package/dist/geometry/kinds/annotations.js.map +1 -0
  79. package/dist/geometry/kinds/boxes.d.ts +116 -0
  80. package/dist/geometry/kinds/boxes.d.ts.map +1 -0
  81. package/dist/geometry/kinds/boxes.js +285 -0
  82. package/dist/geometry/kinds/boxes.js.map +1 -0
  83. package/dist/geometry/kinds/channels.d.ts +72 -0
  84. package/dist/geometry/kinds/channels.d.ts.map +1 -0
  85. package/dist/geometry/kinds/channels.js +148 -0
  86. package/dist/geometry/kinds/channels.js.map +1 -0
  87. package/dist/geometry/kinds/containers.d.ts +54 -0
  88. package/dist/geometry/kinds/containers.d.ts.map +1 -0
  89. package/dist/geometry/kinds/containers.js +268 -0
  90. package/dist/geometry/kinds/containers.js.map +1 -0
  91. package/dist/geometry/kinds/curves.d.ts +53 -0
  92. package/dist/geometry/kinds/curves.d.ts.map +1 -0
  93. package/dist/geometry/kinds/curves.js +110 -0
  94. package/dist/geometry/kinds/curves.js.map +1 -0
  95. package/dist/geometry/kinds/cycles.d.ts +52 -0
  96. package/dist/geometry/kinds/cycles.d.ts.map +1 -0
  97. package/dist/geometry/kinds/cycles.js +158 -0
  98. package/dist/geometry/kinds/cycles.js.map +1 -0
  99. package/dist/geometry/kinds/elliott.d.ts +73 -0
  100. package/dist/geometry/kinds/elliott.d.ts.map +1 -0
  101. package/dist/geometry/kinds/elliott.js +116 -0
  102. package/dist/geometry/kinds/elliott.js.map +1 -0
  103. package/dist/geometry/kinds/fibonacci.d.ts +166 -0
  104. package/dist/geometry/kinds/fibonacci.d.ts.map +1 -0
  105. package/dist/geometry/kinds/fibonacci.js +458 -0
  106. package/dist/geometry/kinds/fibonacci.js.map +1 -0
  107. package/dist/geometry/kinds/freehand.d.ts +53 -0
  108. package/dist/geometry/kinds/freehand.d.ts.map +1 -0
  109. package/dist/geometry/kinds/freehand.js +115 -0
  110. package/dist/geometry/kinds/freehand.js.map +1 -0
  111. package/dist/geometry/kinds/gann.d.ts +63 -0
  112. package/dist/geometry/kinds/gann.d.ts.map +1 -0
  113. package/dist/geometry/kinds/gann.js +153 -0
  114. package/dist/geometry/kinds/gann.js.map +1 -0
  115. package/dist/geometry/kinds/lines.d.ts +90 -0
  116. package/dist/geometry/kinds/lines.d.ts.map +1 -0
  117. package/dist/geometry/kinds/lines.js +201 -0
  118. package/dist/geometry/kinds/lines.js.map +1 -0
  119. package/dist/geometry/kinds/marker.d.ts +21 -0
  120. package/dist/geometry/kinds/marker.d.ts.map +1 -0
  121. package/dist/geometry/kinds/marker.js +47 -0
  122. package/dist/geometry/kinds/marker.js.map +1 -0
  123. package/dist/geometry/kinds/patterns.d.ts +85 -0
  124. package/dist/geometry/kinds/patterns.d.ts.map +1 -0
  125. package/dist/geometry/kinds/patterns.js +133 -0
  126. package/dist/geometry/kinds/patterns.js.map +1 -0
  127. package/dist/geometry/kinds/pitchforks.d.ts +36 -0
  128. package/dist/geometry/kinds/pitchforks.d.ts.map +1 -0
  129. package/dist/geometry/kinds/pitchforks.js +109 -0
  130. package/dist/geometry/kinds/pitchforks.js.map +1 -0
  131. package/dist/geometry/project.d.ts +50 -0
  132. package/dist/geometry/project.d.ts.map +1 -0
  133. package/dist/geometry/project.js +62 -0
  134. package/dist/geometry/project.js.map +1 -0
  135. package/dist/geometry/types.d.ts +146 -0
  136. package/dist/geometry/types.d.ts.map +1 -0
  137. package/dist/geometry/types.js +4 -0
  138. package/dist/geometry/types.js.map +1 -0
  139. package/dist/index.d.ts +4 -0
  140. package/dist/index.d.ts.map +1 -1
  141. package/dist/index.js +2 -0
  142. package/dist/index.js.map +1 -1
  143. package/dist/interaction/domWiring.d.ts +90 -0
  144. package/dist/interaction/domWiring.d.ts.map +1 -0
  145. package/dist/interaction/domWiring.js +113 -0
  146. package/dist/interaction/domWiring.js.map +1 -0
  147. package/dist/interaction/index.d.ts +5 -0
  148. package/dist/interaction/index.d.ts.map +1 -0
  149. package/dist/interaction/index.js +5 -0
  150. package/dist/interaction/index.js.map +1 -0
  151. package/dist/interaction/viewController.d.ts +120 -0
  152. package/dist/interaction/viewController.d.ts.map +1 -0
  153. package/dist/interaction/viewController.js +112 -0
  154. package/dist/interaction/viewController.js.map +1 -0
  155. package/package.json +8 -1
@@ -0,0 +1,112 @@
1
+ // Copyright (c) 2026 Invinite. Licensed under the MIT License.
2
+ // See the LICENSE file in the repo root for full license text.
3
+ const DEFAULT_MIN_SPAN = 1;
4
+ const DEFAULT_MAX_SPAN_FACTOR = 1;
5
+ // Clamp a candidate window: bound its span to [minSpan, maxSpan] (maxSpan =
6
+ // dataSpan * maxSpanFactor), then shift it to sit inside [dataMin, dataMax]
7
+ // when it fits, or pin it to the full data range when it is at least as wide
8
+ // as the data. The single clamp shared by zoom / pan / resolve.
9
+ function clampWindow(win, dataMin, dataMax, minSpan, maxSpanFactor) {
10
+ const dataSpan = Math.max(0, dataMax - dataMin);
11
+ const maxSpan = dataSpan * maxSpanFactor;
12
+ let span = win.xMax - win.xMin;
13
+ if (maxSpan > 0 && span > maxSpan)
14
+ span = maxSpan;
15
+ if (span < minSpan)
16
+ span = minSpan;
17
+ const center = (win.xMin + win.xMax) / 2;
18
+ let xMin = center - span / 2;
19
+ let xMax = center + span / 2;
20
+ if (span <= dataSpan) {
21
+ if (xMin < dataMin) {
22
+ xMax += dataMin - xMin;
23
+ xMin = dataMin;
24
+ }
25
+ if (xMax > dataMax) {
26
+ xMin -= xMax - dataMax;
27
+ xMax = dataMax;
28
+ }
29
+ return { xMin, xMax };
30
+ }
31
+ return { xMin: dataMin, xMax: dataMax };
32
+ }
33
+ /**
34
+ * Build a {@link ViewController}. Pass `opts` to tune the zoom-in floor
35
+ * (`minSpan`) and zoom-out ceiling (`maxSpanFactor`); both default to a
36
+ * "1 ms floor, all-data ceiling" policy.
37
+ *
38
+ * @since 1.6
39
+ * @stable
40
+ * @example
41
+ * const view = createViewController({ minSpan: 2, maxSpanFactor: 1 });
42
+ * view.zoomAt(50, 0.5, 0, 100); // zoom in 2× about x=50
43
+ * void view.resolveXWindow(0, 100);
44
+ */
45
+ export function createViewController(opts) {
46
+ const minSpan = opts?.minSpan ?? DEFAULT_MIN_SPAN;
47
+ const maxSpanFactor = opts?.maxSpanFactor ?? DEFAULT_MAX_SPAN_FACTOR;
48
+ let held;
49
+ let interacted = false;
50
+ const base = (dataMin, dataMax) => held ?? { xMin: dataMin, xMax: dataMax };
51
+ return {
52
+ get userInteracted() {
53
+ return interacted;
54
+ },
55
+ resolveXWindow(dataMin, dataMax) {
56
+ if (!interacted || held === undefined)
57
+ return { xMin: dataMin, xMax: dataMax };
58
+ return clampWindow(held, dataMin, dataMax, minSpan, maxSpanFactor);
59
+ },
60
+ zoomAt(pivotX, factor, dataMin, dataMax) {
61
+ interacted = true;
62
+ const b = base(dataMin, dataMax);
63
+ const span = b.xMax - b.xMin;
64
+ const frac = span === 0 ? 0.5 : (pivotX - b.xMin) / span;
65
+ const newSpan = span * factor;
66
+ const xMin = pivotX - frac * newSpan;
67
+ held = clampWindow({ xMin, xMax: xMin + newSpan }, dataMin, dataMax, minSpan, maxSpanFactor);
68
+ },
69
+ panBy(deltaWorldX, dataMin, dataMax) {
70
+ interacted = true;
71
+ const b = base(dataMin, dataMax);
72
+ held = clampWindow({ xMin: b.xMin + deltaWorldX, xMax: b.xMax + deltaWorldX }, dataMin, dataMax, minSpan, maxSpanFactor);
73
+ },
74
+ reset() {
75
+ held = undefined;
76
+ interacted = false;
77
+ },
78
+ };
79
+ }
80
+ /**
81
+ * Fold the y range of every candidate whose `x` falls inside `win` — the
82
+ * shared "auto-fit the price scale to the visible window" helper (matching
83
+ * lightweight-charts' auto price scale). Non-finite `lo`/`hi` rows are
84
+ * skipped. Returns `undefined` when no finite in-window candidate is seen,
85
+ * so the caller keeps its own degenerate-range fallback. Horizontal lines
86
+ * (no `x`) are folded in by the caller, not here.
87
+ *
88
+ * @since 1.6
89
+ * @stable
90
+ * @example
91
+ * const r = yRangeInWindow([{ x: 5, lo: 1, hi: 3 }], { xMin: 0, xMax: 10 });
92
+ * // r === { yMin: 1, yMax: 3 }
93
+ * void r;
94
+ */
95
+ export function yRangeInWindow(candidates, win) {
96
+ let yMin = Number.POSITIVE_INFINITY;
97
+ let yMax = Number.NEGATIVE_INFINITY;
98
+ for (const c of candidates) {
99
+ if (c.x < win.xMin || c.x > win.xMax)
100
+ continue;
101
+ if (!Number.isFinite(c.lo) || !Number.isFinite(c.hi))
102
+ continue;
103
+ if (c.lo < yMin)
104
+ yMin = c.lo;
105
+ if (c.hi > yMax)
106
+ yMax = c.hi;
107
+ }
108
+ if (!Number.isFinite(yMin) || !Number.isFinite(yMax))
109
+ return undefined;
110
+ return { yMin, yMax };
111
+ }
112
+ //# sourceMappingURL=viewController.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewController.js","sourceRoot":"","sources":["../../src/interaction/viewController.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAwE/D,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAC3B,MAAM,uBAAuB,GAAG,CAAC,CAAC;AAElC,4EAA4E;AAC5E,4EAA4E;AAC5E,6EAA6E;AAC7E,gEAAgE;AAChE,SAAS,WAAW,CAChB,GAAY,EACZ,OAAe,EACf,OAAe,EACf,OAAe,EACf,aAAqB;IAErB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,QAAQ,GAAG,aAAa,CAAC;IACzC,IAAI,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IAC/B,IAAI,OAAO,GAAG,CAAC,IAAI,IAAI,GAAG,OAAO;QAAE,IAAI,GAAG,OAAO,CAAC;IAClD,IAAI,IAAI,GAAG,OAAO;QAAE,IAAI,GAAG,OAAO,CAAC;IACnC,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC;IAC7B,IAAI,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC;IAC7B,IAAI,IAAI,IAAI,QAAQ,EAAE,CAAC;QACnB,IAAI,IAAI,GAAG,OAAO,EAAE,CAAC;YACjB,IAAI,IAAI,OAAO,GAAG,IAAI,CAAC;YACvB,IAAI,GAAG,OAAO,CAAC;QACnB,CAAC;QACD,IAAI,IAAI,GAAG,OAAO,EAAE,CAAC;YACjB,IAAI,IAAI,IAAI,GAAG,OAAO,CAAC;YACvB,IAAI,GAAG,OAAO,CAAC;QACnB,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC1B,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC5C,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAyB;IAC1D,MAAM,OAAO,GAAG,IAAI,EAAE,OAAO,IAAI,gBAAgB,CAAC;IAClD,MAAM,aAAa,GAAG,IAAI,EAAE,aAAa,IAAI,uBAAuB,CAAC;IACrE,IAAI,IAAyB,CAAC;IAC9B,IAAI,UAAU,GAAG,KAAK,CAAC;IAEvB,MAAM,IAAI,GAAG,CAAC,OAAe,EAAE,OAAe,EAAW,EAAE,CACvD,IAAI,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAE7C,OAAO;QACH,IAAI,cAAc;YACd,OAAO,UAAU,CAAC;QACtB,CAAC;QACD,cAAc,CAAC,OAAe,EAAE,OAAe;YAC3C,IAAI,CAAC,UAAU,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;YAC/E,OAAO,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;QACvE,CAAC;QACD,MAAM,CAAC,MAAc,EAAE,MAAc,EAAE,OAAe,EAAE,OAAe;YACnE,UAAU,GAAG,IAAI,CAAC;YAClB,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACjC,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;YAC7B,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;YACzD,MAAM,OAAO,GAAG,IAAI,GAAG,MAAM,CAAC;YAC9B,MAAM,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC;YACrC,IAAI,GAAG,WAAW,CACd,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,EAAE,EAC9B,OAAO,EACP,OAAO,EACP,OAAO,EACP,aAAa,CAChB,CAAC;QACN,CAAC;QACD,KAAK,CAAC,WAAmB,EAAE,OAAe,EAAE,OAAe;YACvD,UAAU,GAAG,IAAI,CAAC;YAClB,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACjC,IAAI,GAAG,WAAW,CACd,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,GAAG,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,GAAG,WAAW,EAAE,EAC1D,OAAO,EACP,OAAO,EACP,OAAO,EACP,aAAa,CAChB,CAAC;QACN,CAAC;QACD,KAAK;YACD,IAAI,GAAG,SAAS,CAAC;YACjB,UAAU,GAAG,KAAK,CAAC;QACvB,CAAC;KACJ,CAAC;AACN,CAAC;AAgBD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAC1B,UAAkC,EAClC,GAAY;IAEZ,IAAI,IAAI,GAAG,MAAM,CAAC,iBAAiB,CAAC;IACpC,IAAI,IAAI,GAAG,MAAM,CAAC,iBAAiB,CAAC;IACpC,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI;YAAE,SAAS;QAC/C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;YAAE,SAAS;QAC/D,IAAI,CAAC,CAAC,EAAE,GAAG,IAAI;YAAE,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,EAAE,GAAG,IAAI;YAAE,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC;IACjC,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACvE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAC1B,CAAC","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n\n/**\n * A visible window into the world x axis (bar times in UTC ms). The two\n * self-scaled adapters (canvas2d, konva) resolve one per frame from a\n * {@link ViewController} and feed `xMin`/`xMax` into their `Viewport`.\n *\n * @since 1.6\n * @stable\n * @example\n * const win: XWindow = { xMin: 0, xMax: 100 };\n * void win;\n */\nexport type XWindow = { readonly xMin: number; readonly xMax: number };\n\n/**\n * Construction options for {@link createViewController}. `minSpan` is the\n * smallest world-x span a zoom-in can reach (so the window never collapses\n * to a point); `maxSpanFactor` is the largest span a zoom-out can reach as\n * a multiple of the current data span (default `1` ⇒ cannot zoom out past\n * \"all data visible\").\n *\n * @since 1.6\n * @stable\n * @example\n * const opts: ViewControllerOpts = { minSpan: 1, maxSpanFactor: 1 };\n * void opts;\n */\nexport type ViewControllerOpts = {\n readonly minSpan?: number;\n readonly maxSpanFactor?: number;\n};\n\n/**\n * Stateful, library-agnostic pan/zoom controller for an adapter that\n * computes its own x scale every frame. It holds a user x-window plus a\n * `userInteracted` flag: until the user wheels or drags, {@link\n * ViewController.resolveXWindow} returns the full data range (auto-follow\n * live bars); after the first interaction it returns the held window\n * (re-clamped as data grows), so live frames stop snapping the view back.\n *\n * All transforms are pure functions of the current state + the supplied\n * data bounds — there is no DOM or library coupling. The example adapters\n * wire DOM events to {@link ViewController.zoomAt} / {@link\n * ViewController.panBy} / {@link ViewController.reset} via {@link\n * attachInteraction}.\n *\n * @since 1.6\n * @stable\n * @example\n * const view = createViewController();\n * view.resolveXWindow(0, 100); // { xMin: 0, xMax: 100 } (auto-follow)\n * view.panBy(10, 0, 100);\n * view.userInteracted; // true\n */\nexport type ViewController = {\n /** `true` once the user has zoomed or panned (auto-follow is paused). */\n readonly userInteracted: boolean;\n /**\n * The x-window to render this frame: `[dataXMin, dataXMax]` while not\n * interacted (auto-follow), else the held window clamped into the\n * current data bounds.\n */\n resolveXWindow(dataXMin: number, dataXMax: number): XWindow;\n /** Zoom by `factor` (`<1` in, `>1` out) about a world-x pivot. */\n zoomAt(pivotX: number, factor: number, dataXMin: number, dataXMax: number): void;\n /** Pan the window by a signed world-x delta. */\n panBy(deltaWorldX: number, dataXMin: number, dataXMax: number): void;\n /** Clear the held window + flag so the view auto-follows again. */\n reset(): void;\n};\n\nconst DEFAULT_MIN_SPAN = 1;\nconst DEFAULT_MAX_SPAN_FACTOR = 1;\n\n// Clamp a candidate window: bound its span to [minSpan, maxSpan] (maxSpan =\n// dataSpan * maxSpanFactor), then shift it to sit inside [dataMin, dataMax]\n// when it fits, or pin it to the full data range when it is at least as wide\n// as the data. The single clamp shared by zoom / pan / resolve.\nfunction clampWindow(\n win: XWindow,\n dataMin: number,\n dataMax: number,\n minSpan: number,\n maxSpanFactor: number,\n): XWindow {\n const dataSpan = Math.max(0, dataMax - dataMin);\n const maxSpan = dataSpan * maxSpanFactor;\n let span = win.xMax - win.xMin;\n if (maxSpan > 0 && span > maxSpan) span = maxSpan;\n if (span < minSpan) span = minSpan;\n const center = (win.xMin + win.xMax) / 2;\n let xMin = center - span / 2;\n let xMax = center + span / 2;\n if (span <= dataSpan) {\n if (xMin < dataMin) {\n xMax += dataMin - xMin;\n xMin = dataMin;\n }\n if (xMax > dataMax) {\n xMin -= xMax - dataMax;\n xMax = dataMax;\n }\n return { xMin, xMax };\n }\n return { xMin: dataMin, xMax: dataMax };\n}\n\n/**\n * Build a {@link ViewController}. Pass `opts` to tune the zoom-in floor\n * (`minSpan`) and zoom-out ceiling (`maxSpanFactor`); both default to a\n * \"1 ms floor, all-data ceiling\" policy.\n *\n * @since 1.6\n * @stable\n * @example\n * const view = createViewController({ minSpan: 2, maxSpanFactor: 1 });\n * view.zoomAt(50, 0.5, 0, 100); // zoom in 2× about x=50\n * void view.resolveXWindow(0, 100);\n */\nexport function createViewController(opts?: ViewControllerOpts): ViewController {\n const minSpan = opts?.minSpan ?? DEFAULT_MIN_SPAN;\n const maxSpanFactor = opts?.maxSpanFactor ?? DEFAULT_MAX_SPAN_FACTOR;\n let held: XWindow | undefined;\n let interacted = false;\n\n const base = (dataMin: number, dataMax: number): XWindow =>\n held ?? { xMin: dataMin, xMax: dataMax };\n\n return {\n get userInteracted(): boolean {\n return interacted;\n },\n resolveXWindow(dataMin: number, dataMax: number): XWindow {\n if (!interacted || held === undefined) return { xMin: dataMin, xMax: dataMax };\n return clampWindow(held, dataMin, dataMax, minSpan, maxSpanFactor);\n },\n zoomAt(pivotX: number, factor: number, dataMin: number, dataMax: number): void {\n interacted = true;\n const b = base(dataMin, dataMax);\n const span = b.xMax - b.xMin;\n const frac = span === 0 ? 0.5 : (pivotX - b.xMin) / span;\n const newSpan = span * factor;\n const xMin = pivotX - frac * newSpan;\n held = clampWindow(\n { xMin, xMax: xMin + newSpan },\n dataMin,\n dataMax,\n minSpan,\n maxSpanFactor,\n );\n },\n panBy(deltaWorldX: number, dataMin: number, dataMax: number): void {\n interacted = true;\n const b = base(dataMin, dataMax);\n held = clampWindow(\n { xMin: b.xMin + deltaWorldX, xMax: b.xMax + deltaWorldX },\n dataMin,\n dataMax,\n minSpan,\n maxSpanFactor,\n );\n },\n reset(): void {\n held = undefined;\n interacted = false;\n },\n };\n}\n\n/**\n * One candidate row for {@link yRangeInWindow}: a world `x` (bar time) plus\n * the low / high values to fold into the y range when `x` is inside the\n * window. Bars pass `{ x: time, lo: low, hi: high }`; a scalar series point\n * passes `lo === hi === value`.\n *\n * @since 1.6\n * @stable\n * @example\n * const c: WindowYInput = { x: 10, lo: 99, hi: 101 };\n * void c;\n */\nexport type WindowYInput = { readonly x: number; readonly lo: number; readonly hi: number };\n\n/**\n * Fold the y range of every candidate whose `x` falls inside `win` — the\n * shared \"auto-fit the price scale to the visible window\" helper (matching\n * lightweight-charts' auto price scale). Non-finite `lo`/`hi` rows are\n * skipped. Returns `undefined` when no finite in-window candidate is seen,\n * so the caller keeps its own degenerate-range fallback. Horizontal lines\n * (no `x`) are folded in by the caller, not here.\n *\n * @since 1.6\n * @stable\n * @example\n * const r = yRangeInWindow([{ x: 5, lo: 1, hi: 3 }], { xMin: 0, xMax: 10 });\n * // r === { yMin: 1, yMax: 3 }\n * void r;\n */\nexport function yRangeInWindow(\n candidates: Iterable<WindowYInput>,\n win: XWindow,\n): { readonly yMin: number; readonly yMax: number } | undefined {\n let yMin = Number.POSITIVE_INFINITY;\n let yMax = Number.NEGATIVE_INFINITY;\n for (const c of candidates) {\n if (c.x < win.xMin || c.x > win.xMax) continue;\n if (!Number.isFinite(c.lo) || !Number.isFinite(c.hi)) continue;\n if (c.lo < yMin) yMin = c.lo;\n if (c.hi > yMax) yMax = c.hi;\n }\n if (!Number.isFinite(yMin) || !Number.isFinite(yMax)) return undefined;\n return { yMin, yMax };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invinite-org/chartlang-adapter-kit",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "SDK for writing chartlang adapters in consumer repos",
@@ -10,6 +10,10 @@
10
10
  ".": {
11
11
  "types": "./dist/index.d.ts",
12
12
  "import": "./dist/index.js"
13
+ },
14
+ "./canvas": {
15
+ "types": "./dist/canvas/index.d.ts",
16
+ "import": "./dist/canvas/index.js"
13
17
  }
14
18
  },
15
19
  "files": [
@@ -21,6 +25,9 @@
21
25
  "dependencies": {
22
26
  "@invinite-org/chartlang-core": "^1.2.0"
23
27
  },
28
+ "devDependencies": {
29
+ "@types/node": "^20.0.0"
30
+ },
24
31
  "publishConfig": {
25
32
  "access": "public",
26
33
  "provenance": true