@invinite-org/chartlang-adapter-kit 1.5.0 → 1.6.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 (63) hide show
  1. package/CHANGELOG.md +142 -0
  2. package/dist/base/bufferingAdapter.d.ts +1 -0
  3. package/dist/base/bufferingAdapter.d.ts.map +1 -1
  4. package/dist/base/bufferingAdapter.js +1 -0
  5. package/dist/base/bufferingAdapter.js.map +1 -1
  6. package/dist/base/passThroughAdapter.d.ts +1 -0
  7. package/dist/base/passThroughAdapter.d.ts.map +1 -1
  8. package/dist/base/passThroughAdapter.js +1 -0
  9. package/dist/base/passThroughAdapter.js.map +1 -1
  10. package/dist/canvas/glyphs.d.ts +209 -0
  11. package/dist/canvas/glyphs.d.ts.map +1 -0
  12. package/dist/canvas/glyphs.js +219 -0
  13. package/dist/canvas/glyphs.js.map +1 -0
  14. package/dist/canvas/index.d.ts +2 -0
  15. package/dist/canvas/index.d.ts.map +1 -1
  16. package/dist/canvas/index.js +1 -0
  17. package/dist/canvas/index.js.map +1 -1
  18. package/dist/canvas/mockContext.d.ts +42 -0
  19. package/dist/canvas/mockContext.d.ts.map +1 -1
  20. package/dist/canvas/mockContext.js +50 -0
  21. package/dist/canvas/mockContext.js.map +1 -1
  22. package/dist/canvas/renderCtx.d.ts +6 -0
  23. package/dist/canvas/renderCtx.d.ts.map +1 -1
  24. package/dist/canvas/renderCtx.js.map +1 -1
  25. package/dist/capabilities/capabilities.d.ts +16 -0
  26. package/dist/capabilities/capabilities.d.ts.map +1 -1
  27. package/dist/capabilities/capabilities.js +16 -0
  28. package/dist/capabilities/capabilities.js.map +1 -1
  29. package/dist/defineAdapter.d.ts +2 -0
  30. package/dist/defineAdapter.d.ts.map +1 -1
  31. package/dist/defineAdapter.js +1 -0
  32. package/dist/defineAdapter.js.map +1 -1
  33. package/dist/geometry/index.d.ts +3 -0
  34. package/dist/geometry/index.d.ts.map +1 -1
  35. package/dist/geometry/index.js +2 -0
  36. package/dist/geometry/index.js.map +1 -1
  37. package/dist/geometry/renderOrder.d.ts +52 -0
  38. package/dist/geometry/renderOrder.d.ts.map +1 -0
  39. package/dist/geometry/renderOrder.js +35 -0
  40. package/dist/geometry/renderOrder.js.map +1 -0
  41. package/dist/geometry/shift.d.ts +117 -0
  42. package/dist/geometry/shift.d.ts.map +1 -0
  43. package/dist/geometry/shift.js +141 -0
  44. package/dist/geometry/shift.js.map +1 -0
  45. package/dist/index.d.ts +3 -2
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +2 -1
  48. package/dist/index.js.map +1 -1
  49. package/dist/interaction/viewController.d.ts +20 -8
  50. package/dist/interaction/viewController.d.ts.map +1 -1
  51. package/dist/interaction/viewController.js +26 -5
  52. package/dist/interaction/viewController.js.map +1 -1
  53. package/dist/mocks/mockCandleSource.d.ts +12 -0
  54. package/dist/mocks/mockCandleSource.d.ts.map +1 -1
  55. package/dist/mocks/mockCandleSource.js +13 -4
  56. package/dist/mocks/mockCandleSource.js.map +1 -1
  57. package/dist/types.d.ts +91 -8
  58. package/dist/types.d.ts.map +1 -1
  59. package/dist/types.js.map +1 -1
  60. package/dist/validation/validateEmission.d.ts.map +1 -1
  61. package/dist/validation/validateEmission.js +10 -0
  62. package/dist/validation/validateEmission.js.map +1 -1
  63. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,147 @@
1
1
  # @invinite-org/chartlang-adapter-kit
2
2
 
3
+ ## 1.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 189493a: Add `bezierCurveTo` to the canvas sink's `RenderCtx` (and the shared
8
+ `MockCanvasContext` / `RecordedCall` / `canonicalise`). A self-scaled canvas
9
+ adapter uses it to stroke a smooth curve through a plot series' points instead
10
+ of straight segments. Production `CanvasRenderingContext2D` /
11
+ `OffscreenCanvasRenderingContext2D` already satisfy the new member, so this is
12
+ additive — every existing canvas-family caller keeps compiling, and an adapter
13
+ that never calls it paints byte-for-byte as before (the method is absent from
14
+ all existing `hashCallLog` pins).
15
+
16
+ This backs default plot-line smoothing in the reference adapters: plain `line`
17
+ plots now render as a smooth curve (monotone-cubic in the canvas2d reference;
18
+ each library adapter uses its native smoothing — konva `tension`, echarts
19
+ `smooth`, uPlot spline paths, lightweight-charts `lineType: Curved`) so a
20
+ moving-average line reads as a curve rather than a faceted polyline at dense bar
21
+ spacing. Step-lines and area edges stay straight.
22
+
23
+ - 8bc628e: Promote the canvas glyph geometry into the shared `./canvas` sink so the
24
+ canvas-family adapters (uplot draw-hook, lightweight-charts overlay) consume one
25
+ source instead of hand-porting it (the bug class the `shift.ts` /
26
+ `renderOrder.ts` promotions exist to kill). New `./canvas` exports:
27
+ `drawShape` / `drawCharacter` / `drawArrow` / `drawMarker` / `drawLabel`
28
+ (`shape` / `character` / `arrow` / `marker` / `label` geometry on a
29
+ `RenderCtx`) plus their arg + enum types (`ShapeArgs` / `ShapeGlyph` /
30
+ `CharacterArgs` / `ArrowArgs` / `MarkerArgs` / `MarkerShape` / `LabelArgs` /
31
+ `LabelPosition` / `GlyphLocation`). Each helper is model-free — it draws onto a
32
+ `RenderCtx` and takes a plain `fallbackColor: string` (the null-color default),
33
+ so it carries no palette / library / model types. The five filled-marker `shape`
34
+ glyphs delegate to `drawMarker`; `cross` / `xcross` / `flag` stroke directly.
35
+ Promoted out of the canvas2d reference adapter (which keeps its own
36
+ `Palette`-taking local renderers — re-consume deferred). Pure, fully covered.
37
+ - ab8b218: Add `rect` + `clip` to the canvas sink's `RenderCtx` (and the shared
38
+ `MockCanvasContext` / `RecordedCall` / `hashCallLog`). Together they compose the
39
+ standard `beginPath()` → `rect()` → `clip()` idiom an adapter uses to confine a
40
+ hand-rolled `ctx` draw pass to its plotting-area box. Production
41
+ `CanvasRenderingContext2D` / `OffscreenCanvasRenderingContext2D` already satisfy
42
+ the two new members, so this is additive — every existing canvas-family caller
43
+ keeps compiling, and an adapter that never calls them paints byte-for-byte as
44
+ before (the methods are absent from all existing `hashCallLog` pins). The uPlot
45
+ reference adapter is the first consumer: it clips its candle/band/hline/drawing
46
+ overlay so off-window marks stop spilling into the axis gutters.
47
+ - 8bc628e: Add the shared z-order render comparator (`geometry/renderOrder.ts`) so every
48
+ adapter sorts its paint pass identically instead of hand-porting the math. New
49
+ public exports on the root barrel: `sortByRenderOrder<T extends RenderOrderKey>`
50
+ (the model-agnostic `a.z - b.z || a.band - b.band || a.seq - b.seq` total order,
51
+ sorted in place and the same array returned), `RENDER_BAND` (`{ series, glyph,
52
+ hline, drawing }`, the pre-`z` phase order), and the `RenderOrderKey` structural
53
+ key (`{ z, band, seq }`). Promoted out of the canvas2d reference adapter
54
+ (which now re-exports the comparator and aliases `BAND = RENDER_BAND`),
55
+ mirroring the earlier `shift.ts` promotion — the z-comparator is identical
56
+ across rendering models, so one generic helper replaces what would otherwise be
57
+ five divergent ports as Tasks 4/6/10/12 add `z` to the other adapters. The
58
+ comparator is generic over the mark payload, so each adapter keeps its own
59
+ mark union local. Pure, fully covered, behaviour-preserving (the canvas2d
60
+ integration hash has no drawings, so the comparator path is unchanged).
61
+ - ab8b218: Add the shared bar-shift projection contract (`geometry/shift.ts`) so every
62
+ adapter honours the universal plot `offset` (`PlotEmission.xShift`) identically.
63
+ New public exports: `medianBarSpacing`, `shiftedBarTime`, `projectShiftedX`
64
+ (promoted out of the canvas2d reference adapter), plus `maxShiftedTime` (widen a
65
+ self-scaled adapter's `xMax` for a `+k` future-projected point) and
66
+ `shiftedBarIndex` (the category/index analogue for declarative adapters). The
67
+ three rendering models — self-scaled time (canvas2d, konva), category/index
68
+ (echarts), and aligned/native-time (uplot, lightweight-charts) — now share one
69
+ pure, fully-covered implementation instead of four divergent ports, which is
70
+ what let four of the five reference adapters silently drop the offset and
71
+ collapse multi-plot/offset scripts onto a single x-position. An omitted / `0`
72
+ `xShift` reproduces the unshifted projection byte-for-byte, so no rendering
73
+ goldens change from this addition.
74
+ - 189493a: Two rendering fixes for the self-scaled adapters.
75
+
76
+ **Viewport no longer snaps to fit-all on the first interaction.** The shared
77
+ `createViewController` now seeds the held window from the window last returned by
78
+ `resolveXWindow` (what the user is currently looking at — the framed
79
+ `initialVisibleBars` view) on the first `zoomAt`/`panBy`, instead of from the full
80
+ data range. Previously the first wheel/drag discarded the framed window and
81
+ snapped the chart back to all bars; now leaving auto-follow zooms smoothly from
82
+ the current view. It falls back to the data bounds only when nothing has rendered
83
+ yet, so the interact-before-first-render path is unchanged.
84
+
85
+ **Add `setTransform` to the canvas sink's `RenderCtx`** (and the shared
86
+ `MockCanvasContext` / `RecordedCall` / `canonicalise`). This lets a self-scaled
87
+ canvas adapter apply an ambient `setTransform(dpr, 0, 0, dpr, 0, 0)` and draw in
88
+ CSS-pixel space, so absolute sizes (line widths, fonts) render at their intended
89
+ thickness on a HiDPI backing store instead of a half-thick, edgy hairline.
90
+ Production `CanvasRenderingContext2D` / `OffscreenCanvasRenderingContext2D`
91
+ already satisfy the new member, so this is additive — every existing
92
+ canvas-family caller keeps compiling, and an adapter that never calls it (e.g.
93
+ at `dpr === 1`) paints byte-for-byte as before (the method is absent from all
94
+ existing `hashCallLog` pins). The canvas2d reference adapter is the first
95
+ consumer.
96
+
97
+ - e620ba8: Add `bgcolor(color, opts?)` and `barcolor(color, opts?)` — Pine-ergonomic
98
+ top-level aliases for the `bg-color` / `bar-color` plot styles. One call
99
+ (`bgcolor(close > open ? "#16a34a" : "#dc2626", { transp: 80 })`) replaces
100
+ the verbose `plot(NaN, { style: { kind: "bg-color", … } })`. Surfaced in the
101
+ generated primitive reference and taught in the chartlang-coding skill.
102
+
103
+ Deliverable 2 (per-bar dynamic color): `PlotEmission` gains an optional
104
+ `colorValue: Color | null` channel; the runtime resolves the `bgcolor` /
105
+ `barcolor` per-bar color into it (omitted on the static `plot` path → wire
106
+ byte-identical, every pinned `plot-hash` untouched), validates it
107
+ (non-empty color string or `null`), and dedups it last-write-wins per
108
+ `(slotId, bar)` like `value`. Adapters prefer `colorValue` over the static
109
+ `style.color` at render time — this precedence is now the normative
110
+ adapter-kit contract (`PlotEmission.colorValue` JSDoc) and is implemented in
111
+ the canvas2d reference renderer (`null` ⇒ paint-nothing gap; omitted ⇒ static
112
+ fallback). The Pine converter emits the real per-bar dynamic color
113
+ (`bgcolor(close > open ? "#16a34a" : "#dc2626")`) instead of a static
114
+ `plot(NaN, …)`, so `bgcolor`/`barcolor` round-trip with per-bar semantics
115
+ intact.
116
+
117
+ - 08cba38: Add `time.*` calendar accessors (`time.year/month/dayofmonth/dayofweek/hour/
118
+ minute/second/timestamp`), a `time.timeClose(t, tz?)` bar-close accessor
119
+ (Pine's `time_close()` = bar start + interval), a `session.isOpen(t, spec, tz?)`
120
+ helper, and an `input.session` kind. Calendar fields are derived from a `Time`
121
+ epoch via the host (authors stay sandboxed — `Date`/`Intl` remain banned). v1
122
+ is UTC + fixed-offset only; exchange-tz/DST is a scoped follow-up. The Pine
123
+ converter lowers `dayofweek` / `time()` / `time_close()` / `input.session`.
124
+ - 1efb49c: Add multi-symbol support to `request.security`. `request.security({ symbol,
125
+ interval })` now reads a **different instrument** (not just a higher
126
+ timeframe), e.g. `request.security({ symbol: "AMEX:SPY", interval: "1D" })`.
127
+ `symbol` is optional (defaults to the chart symbol) and must be a compile-time
128
+ literal (`input.symbol` / `input.enum` resolved). A new `multiSymbol` adapter
129
+ capability gates non-chart-symbol requests: a different-symbol request against
130
+ an adapter declaring `multiSymbol: false` degrades to an all-NaN
131
+ bar/series with a single deduped `multi-symbol-not-supported` diagnostic,
132
+ mirroring `multi-timeframe-not-supported` (the symbol gate precedes the
133
+ timeframe gate, so a both-different request emits only the symbol diagnostic).
134
+ The Pine converter now lowers `request.security("OTHER", tf, expr)`, and the
135
+ `chartlang scaffold-adapter` template advertises `multiSymbol`.
136
+
137
+ ### Patch Changes
138
+
139
+ - Updated dependencies [e620ba8]
140
+ - Updated dependencies [08cba38]
141
+ - Updated dependencies [1efb49c]
142
+ - Updated dependencies [1efb49c]
143
+ - @invinite-org/chartlang-core@1.3.0
144
+
3
145
  ## 1.5.0
4
146
 
5
147
  ### Minor Changes
@@ -25,6 +25,7 @@ import type { Adapter, CandleEvent, Capabilities, RunnerEmissions } from "../typ
25
25
  * inputs: new Set(),
26
26
  * intervals: [],
27
27
  * multiTimeframe: false,
28
+ * multiSymbol: false,
28
29
  * subPanes: 0,
29
30
  * symInfoFields: new Set(),
30
31
  * maxDrawingsPerScript: {
@@ -1 +1 @@
1
- {"version":3,"file":"bufferingAdapter.d.ts","sourceRoot":"","sources":["../../src/base/bufferingAdapter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEvF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,qBAAa,gBAAiB,YAAW,OAAO;aAIxB,EAAE,EAAE,MAAM;aACV,IAAI,EAAE,MAAM;aACZ,YAAY,EAAE,YAAY;IAC1C,OAAO,CAAC,QAAQ,CAAC,MAAM;IAN3B,OAAO,CAAC,QAAQ,CAAyB;gBAGrB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,YAAY,EACzB,MAAM,EAAE,aAAa,CAAC,WAAW,CAAC;IAGvD,OAAO,IAAI,aAAa,CAAC,WAAW,CAAC;IAIrC,WAAW,CAAC,SAAS,EAAE,eAAe,GAAG,IAAI;IAI7C,KAAK,IAAI,aAAa,CAAC,eAAe,CAAC;IAMvC,OAAO,IAAI,IAAI;CAGlB"}
1
+ {"version":3,"file":"bufferingAdapter.d.ts","sourceRoot":"","sources":["../../src/base/bufferingAdapter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEvF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,qBAAa,gBAAiB,YAAW,OAAO;aAIxB,EAAE,EAAE,MAAM;aACV,IAAI,EAAE,MAAM;aACZ,YAAY,EAAE,YAAY;IAC1C,OAAO,CAAC,QAAQ,CAAC,MAAM;IAN3B,OAAO,CAAC,QAAQ,CAAyB;gBAGrB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,YAAY,EACzB,MAAM,EAAE,aAAa,CAAC,WAAW,CAAC;IAGvD,OAAO,IAAI,aAAa,CAAC,WAAW,CAAC;IAIrC,WAAW,CAAC,SAAS,EAAE,eAAe,GAAG,IAAI;IAI7C,KAAK,IAAI,aAAa,CAAC,eAAe,CAAC;IAMvC,OAAO,IAAI,IAAI;CAGlB"}
@@ -26,6 +26,7 @@
26
26
  * inputs: new Set(),
27
27
  * intervals: [],
28
28
  * multiTimeframe: false,
29
+ * multiSymbol: false,
29
30
  * subPanes: 0,
30
31
  * symInfoFields: new Set(),
31
32
  * maxDrawingsPerScript: {
@@ -1 +1 @@
1
- {"version":3,"file":"bufferingAdapter.js","sourceRoot":"","sources":["../../src/base/bufferingAdapter.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAI/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,OAAO,gBAAgB;IAIL;IACA;IACA;IACC;IANb,QAAQ,GAAsB,EAAE,CAAC;IAEzC,YACoB,EAAU,EACV,IAAY,EACZ,YAA0B,EACzB,MAAkC;QAHnC,OAAE,GAAF,EAAE,CAAQ;QACV,SAAI,GAAJ,IAAI,CAAQ;QACZ,iBAAY,GAAZ,YAAY,CAAc;QACzB,WAAM,GAAN,MAAM,CAA4B;IACpD,CAAC;IAEJ,OAAO;QACH,OAAO,IAAI,CAAC,MAAM,CAAC;IACvB,CAAC;IAED,WAAW,CAAC,SAA0B;QAClC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IAED,KAAK;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QAClC,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;QACnB,OAAO,GAAG,CAAC;IACf,CAAC;IAED,OAAO;QACH,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;IACvB,CAAC;CACJ","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n\nimport type { Adapter, CandleEvent, Capabilities, RunnerEmissions } from \"../types.js\";\n\n/**\n * `Adapter` that records every `onEmissions` batch and exposes a\n * `drain()` method to retrieve them all at once. Used by the\n * conformance suite (Task 11) to collect emissions across a full\n * fixture playback before asserting against golden bars.\n *\n * @since 0.1\n * @stable\n * @example\n * import {\n * BufferingAdapter,\n * capabilities,\n * mockCandleSource,\n * } from \"@invinite-org/chartlang-adapter-kit\";\n *\n * const a = new BufferingAdapter(\n * \"b\", \"Buffering\",\n * {\n * plots: capabilities.allLines(),\n * drawings: new Set(),\n * alerts: new Set(),\n * alertConditions: false,\n * logs: false,\n * inputs: new Set(),\n * intervals: [],\n * multiTimeframe: false,\n * subPanes: 0,\n * symInfoFields: new Set(),\n * maxDrawingsPerScript: {\n * lines: 0, labels: 0, boxes: 0, polylines: 0, other: 0,\n * },\n * maxLookback: 5000,\n * maxTickHz: 10,\n * },\n * mockCandleSource([]),\n * );\n * a.drain();\n */\nexport class BufferingAdapter implements Adapter {\n private buffered: RunnerEmissions[] = [];\n\n constructor(\n public readonly id: string,\n public readonly name: string,\n public readonly capabilities: Capabilities,\n private readonly source: AsyncIterable<CandleEvent>,\n ) {}\n\n candles(): AsyncIterable<CandleEvent> {\n return this.source;\n }\n\n onEmissions(emissions: RunnerEmissions): void {\n this.buffered.push(emissions);\n }\n\n drain(): ReadonlyArray<RunnerEmissions> {\n const out = this.buffered.slice();\n this.buffered = [];\n return out;\n }\n\n dispose(): void {\n this.buffered = [];\n }\n}\n"]}
1
+ {"version":3,"file":"bufferingAdapter.js","sourceRoot":"","sources":["../../src/base/bufferingAdapter.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAI/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,OAAO,gBAAgB;IAIL;IACA;IACA;IACC;IANb,QAAQ,GAAsB,EAAE,CAAC;IAEzC,YACoB,EAAU,EACV,IAAY,EACZ,YAA0B,EACzB,MAAkC;QAHnC,OAAE,GAAF,EAAE,CAAQ;QACV,SAAI,GAAJ,IAAI,CAAQ;QACZ,iBAAY,GAAZ,YAAY,CAAc;QACzB,WAAM,GAAN,MAAM,CAA4B;IACpD,CAAC;IAEJ,OAAO;QACH,OAAO,IAAI,CAAC,MAAM,CAAC;IACvB,CAAC;IAED,WAAW,CAAC,SAA0B;QAClC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IAED,KAAK;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QAClC,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;QACnB,OAAO,GAAG,CAAC;IACf,CAAC;IAED,OAAO;QACH,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;IACvB,CAAC;CACJ","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n\nimport type { Adapter, CandleEvent, Capabilities, RunnerEmissions } from \"../types.js\";\n\n/**\n * `Adapter` that records every `onEmissions` batch and exposes a\n * `drain()` method to retrieve them all at once. Used by the\n * conformance suite (Task 11) to collect emissions across a full\n * fixture playback before asserting against golden bars.\n *\n * @since 0.1\n * @stable\n * @example\n * import {\n * BufferingAdapter,\n * capabilities,\n * mockCandleSource,\n * } from \"@invinite-org/chartlang-adapter-kit\";\n *\n * const a = new BufferingAdapter(\n * \"b\", \"Buffering\",\n * {\n * plots: capabilities.allLines(),\n * drawings: new Set(),\n * alerts: new Set(),\n * alertConditions: false,\n * logs: false,\n * inputs: new Set(),\n * intervals: [],\n * multiTimeframe: false,\n * multiSymbol: false,\n * subPanes: 0,\n * symInfoFields: new Set(),\n * maxDrawingsPerScript: {\n * lines: 0, labels: 0, boxes: 0, polylines: 0, other: 0,\n * },\n * maxLookback: 5000,\n * maxTickHz: 10,\n * },\n * mockCandleSource([]),\n * );\n * a.drain();\n */\nexport class BufferingAdapter implements Adapter {\n private buffered: RunnerEmissions[] = [];\n\n constructor(\n public readonly id: string,\n public readonly name: string,\n public readonly capabilities: Capabilities,\n private readonly source: AsyncIterable<CandleEvent>,\n ) {}\n\n candles(): AsyncIterable<CandleEvent> {\n return this.source;\n }\n\n onEmissions(emissions: RunnerEmissions): void {\n this.buffered.push(emissions);\n }\n\n drain(): ReadonlyArray<RunnerEmissions> {\n const out = this.buffered.slice();\n this.buffered = [];\n return out;\n }\n\n dispose(): void {\n this.buffered = [];\n }\n}\n"]}
@@ -24,6 +24,7 @@ import type { Adapter, CandleEvent, Capabilities, RunnerEmissions } from "../typ
24
24
  * inputs: new Set(),
25
25
  * intervals: [],
26
26
  * multiTimeframe: false,
27
+ * multiSymbol: false,
27
28
  * subPanes: 0,
28
29
  * symInfoFields: new Set(),
29
30
  * maxDrawingsPerScript: {
@@ -1 +1 @@
1
- {"version":3,"file":"passThroughAdapter.d.ts","sourceRoot":"","sources":["../../src/base/passThroughAdapter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEvF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,qBAAa,kBAAmB,YAAW,OAAO;aAE1B,EAAE,EAAE,MAAM;aACV,IAAI,EAAE,MAAM;aACZ,YAAY,EAAE,YAAY;IAC1C,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAHP,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,YAAY,EACzB,MAAM,EAAE,aAAa,CAAC,WAAW,CAAC;IAGvD,OAAO,IAAI,aAAa,CAAC,WAAW,CAAC;IAIrC,WAAW,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI;IAI9C,OAAO,IAAI,IAAI;CAGlB"}
1
+ {"version":3,"file":"passThroughAdapter.d.ts","sourceRoot":"","sources":["../../src/base/passThroughAdapter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEvF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,qBAAa,kBAAmB,YAAW,OAAO;aAE1B,EAAE,EAAE,MAAM;aACV,IAAI,EAAE,MAAM;aACZ,YAAY,EAAE,YAAY;IAC1C,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAHP,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,YAAY,EACzB,MAAM,EAAE,aAAa,CAAC,WAAW,CAAC;IAGvD,OAAO,IAAI,aAAa,CAAC,WAAW,CAAC;IAIrC,WAAW,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI;IAI9C,OAAO,IAAI,IAAI;CAGlB"}
@@ -25,6 +25,7 @@
25
25
  * inputs: new Set(),
26
26
  * intervals: [],
27
27
  * multiTimeframe: false,
28
+ * multiSymbol: false,
28
29
  * subPanes: 0,
29
30
  * symInfoFields: new Set(),
30
31
  * maxDrawingsPerScript: {
@@ -1 +1 @@
1
- {"version":3,"file":"passThroughAdapter.js","sourceRoot":"","sources":["../../src/base/passThroughAdapter.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAI/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,OAAO,kBAAkB;IAEP;IACA;IACA;IACC;IAJrB,YACoB,EAAU,EACV,IAAY,EACZ,YAA0B,EACzB,MAAkC;QAHnC,OAAE,GAAF,EAAE,CAAQ;QACV,SAAI,GAAJ,IAAI,CAAQ;QACZ,iBAAY,GAAZ,YAAY,CAAc;QACzB,WAAM,GAAN,MAAM,CAA4B;IACpD,CAAC;IAEJ,OAAO;QACH,OAAO,IAAI,CAAC,MAAM,CAAC;IACvB,CAAC;IAED,WAAW,CAAC,UAA2B;QACnC,oBAAoB;IACxB,CAAC;IAED,OAAO;QACH,oBAAoB;IACxB,CAAC;CACJ","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n\nimport type { Adapter, CandleEvent, Capabilities, RunnerEmissions } from \"../types.js\";\n\n/**\n * Minimal `Adapter` that forwards a supplied `AsyncIterable<CandleEvent>`\n * and drops every emission. Useful as a runtime test fixture when the\n * caller doesn't need to observe outputs.\n *\n * @since 0.1\n * @stable\n * @example\n * import {\n * PassThroughAdapter,\n * capabilities,\n * mockCandleSource,\n * } from \"@invinite-org/chartlang-adapter-kit\";\n *\n * const a = new PassThroughAdapter(\n * \"p\", \"Passthrough\",\n * {\n * plots: capabilities.allLines(),\n * drawings: new Set(),\n * alerts: new Set(),\n * alertConditions: false,\n * logs: false,\n * inputs: new Set(),\n * intervals: [],\n * multiTimeframe: false,\n * subPanes: 0,\n * symInfoFields: new Set(),\n * maxDrawingsPerScript: {\n * lines: 0, labels: 0, boxes: 0, polylines: 0, other: 0,\n * },\n * maxLookback: 5000,\n * maxTickHz: 10,\n * },\n * mockCandleSource([]),\n * );\n * void a;\n */\nexport class PassThroughAdapter implements Adapter {\n constructor(\n public readonly id: string,\n public readonly name: string,\n public readonly capabilities: Capabilities,\n private readonly source: AsyncIterable<CandleEvent>,\n ) {}\n\n candles(): AsyncIterable<CandleEvent> {\n return this.source;\n }\n\n onEmissions(_emissions: RunnerEmissions): void {\n // intentional no-op\n }\n\n dispose(): void {\n // intentional no-op\n }\n}\n"]}
1
+ {"version":3,"file":"passThroughAdapter.js","sourceRoot":"","sources":["../../src/base/passThroughAdapter.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAI/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,OAAO,kBAAkB;IAEP;IACA;IACA;IACC;IAJrB,YACoB,EAAU,EACV,IAAY,EACZ,YAA0B,EACzB,MAAkC;QAHnC,OAAE,GAAF,EAAE,CAAQ;QACV,SAAI,GAAJ,IAAI,CAAQ;QACZ,iBAAY,GAAZ,YAAY,CAAc;QACzB,WAAM,GAAN,MAAM,CAA4B;IACpD,CAAC;IAEJ,OAAO;QACH,OAAO,IAAI,CAAC,MAAM,CAAC;IACvB,CAAC;IAED,WAAW,CAAC,UAA2B;QACnC,oBAAoB;IACxB,CAAC;IAED,OAAO;QACH,oBAAoB;IACxB,CAAC;CACJ","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n\nimport type { Adapter, CandleEvent, Capabilities, RunnerEmissions } from \"../types.js\";\n\n/**\n * Minimal `Adapter` that forwards a supplied `AsyncIterable<CandleEvent>`\n * and drops every emission. Useful as a runtime test fixture when the\n * caller doesn't need to observe outputs.\n *\n * @since 0.1\n * @stable\n * @example\n * import {\n * PassThroughAdapter,\n * capabilities,\n * mockCandleSource,\n * } from \"@invinite-org/chartlang-adapter-kit\";\n *\n * const a = new PassThroughAdapter(\n * \"p\", \"Passthrough\",\n * {\n * plots: capabilities.allLines(),\n * drawings: new Set(),\n * alerts: new Set(),\n * alertConditions: false,\n * logs: false,\n * inputs: new Set(),\n * intervals: [],\n * multiTimeframe: false,\n * multiSymbol: false,\n * subPanes: 0,\n * symInfoFields: new Set(),\n * maxDrawingsPerScript: {\n * lines: 0, labels: 0, boxes: 0, polylines: 0, other: 0,\n * },\n * maxLookback: 5000,\n * maxTickHz: 10,\n * },\n * mockCandleSource([]),\n * );\n * void a;\n */\nexport class PassThroughAdapter implements Adapter {\n constructor(\n public readonly id: string,\n public readonly name: string,\n public readonly capabilities: Capabilities,\n private readonly source: AsyncIterable<CandleEvent>,\n ) {}\n\n candles(): AsyncIterable<CandleEvent> {\n return this.source;\n }\n\n onEmissions(_emissions: RunnerEmissions): void {\n // intentional no-op\n }\n\n dispose(): void {\n // intentional no-op\n }\n}\n"]}
@@ -0,0 +1,209 @@
1
+ import type { RenderCtx } from "./renderCtx.js";
2
+ /**
3
+ * Vertical anchoring mode shared by the `shape` / `character` glyphs.
4
+ * `above` / `below` offset the glyph relative to the plot value; `absolute`
5
+ * pins it at the value.
6
+ *
7
+ * @since 1.7
8
+ * @stable
9
+ * @example
10
+ * const location: GlyphLocation = "below";
11
+ * void location;
12
+ */
13
+ export type GlyphLocation = "above" | "below" | "absolute";
14
+ /**
15
+ * Inventory of `shape` glyphs (Pine `plotshape`). The first five reuse the
16
+ * filled {@link drawMarker} geometry; `cross` / `xcross` / `flag` are
17
+ * stroke-only.
18
+ *
19
+ * @since 1.7
20
+ * @stable
21
+ * @example
22
+ * const glyph: ShapeGlyph = "cross";
23
+ * void glyph;
24
+ */
25
+ export type ShapeGlyph = "circle" | "triangle-up" | "triangle-down" | "square" | "diamond" | "cross" | "xcross" | "flag";
26
+ /**
27
+ * Inputs for {@link drawShape}. World-space `x` / `y` are pixels the caller
28
+ * derives via `timeToX` / `priceToY`; `size` is the glyph bounding-box edge
29
+ * length in pixels.
30
+ *
31
+ * @since 1.7
32
+ * @stable
33
+ * @example
34
+ * const args: ShapeArgs = { x: 10, y: 20, shape: "cross", size: 8, color: null };
35
+ * void args;
36
+ */
37
+ export type ShapeArgs = {
38
+ readonly x: number;
39
+ readonly y: number;
40
+ readonly shape: ShapeGlyph;
41
+ readonly size: number;
42
+ readonly location?: GlyphLocation;
43
+ readonly color: string | null;
44
+ };
45
+ /**
46
+ * Discrete marker glyph. Matches the `marker` variant of `PlotStyle.shape`.
47
+ *
48
+ * @since 1.7
49
+ * @stable
50
+ * @example
51
+ * const s: MarkerShape = "triangle-up";
52
+ * void s;
53
+ */
54
+ export type MarkerShape = "circle" | "triangle-up" | "triangle-down" | "square" | "diamond";
55
+ /**
56
+ * Inputs for {@link drawMarker}. World-space `x` / `y` are pixels the caller
57
+ * derives via `timeToX` / `priceToY`; `size` is the glyph's bounding-box edge
58
+ * length in pixels (the circle uses `size` as its diameter).
59
+ *
60
+ * @since 1.7
61
+ * @stable
62
+ * @example
63
+ * const args: MarkerArgs = { x: 100, y: 50, shape: "triangle-up", size: 8, color: "#26a69a" };
64
+ * void args;
65
+ */
66
+ export type MarkerArgs = {
67
+ readonly x: number;
68
+ readonly y: number;
69
+ readonly shape: MarkerShape;
70
+ readonly size: number;
71
+ readonly color: string | null;
72
+ };
73
+ /**
74
+ * Inputs for {@link drawCharacter}. World-space `x` / `y` are pixels the
75
+ * caller derives via `timeToX` / `priceToY`; `size` is the font px size.
76
+ *
77
+ * @since 1.7
78
+ * @stable
79
+ * @example
80
+ * const args: CharacterArgs = { x: 10, y: 20, char: "A", size: 12, color: null };
81
+ * void args;
82
+ */
83
+ export type CharacterArgs = {
84
+ readonly x: number;
85
+ readonly y: number;
86
+ readonly char: string;
87
+ readonly size: number;
88
+ readonly location?: GlyphLocation;
89
+ readonly color: string | null;
90
+ };
91
+ /**
92
+ * Inputs for {@link drawArrow}. World-space `x` / `y` are pixels the caller
93
+ * derives via `timeToX` / `priceToY`; `size` is the triangle bounding-box edge
94
+ * length in pixels.
95
+ *
96
+ * @since 1.7
97
+ * @stable
98
+ * @example
99
+ * const args: ArrowArgs = { x: 10, y: 20, direction: "up", size: 10, color: null };
100
+ * void args;
101
+ */
102
+ export type ArrowArgs = {
103
+ readonly x: number;
104
+ readonly y: number;
105
+ readonly direction: "up" | "down";
106
+ readonly size: number;
107
+ readonly color: string | null;
108
+ };
109
+ /**
110
+ * Position the label sits in relative to the (`x`, `y`) anchor.
111
+ *
112
+ * - `above` → text sits above the anchor (`textBaseline = "bottom"`).
113
+ * - `below` → text sits below the anchor (`textBaseline = "top"`).
114
+ * - `anchor` → text is vertically centred on the anchor (`textBaseline =
115
+ * "middle"`).
116
+ *
117
+ * @since 1.7
118
+ * @stable
119
+ * @example
120
+ * const p: LabelPosition = "above";
121
+ * void p;
122
+ */
123
+ export type LabelPosition = "above" | "below" | "anchor";
124
+ /**
125
+ * Inputs for {@link drawLabel}. World-space `x` / `y` are pixels the caller
126
+ * derives via `timeToX` / `priceToY`. `font` defaults to `"10px sans-serif"`
127
+ * when omitted.
128
+ *
129
+ * @since 1.7
130
+ * @stable
131
+ * @example
132
+ * const args: LabelArgs = { x: 100, y: 50, text: "PEAK", position: "above", color: "#26a69a" };
133
+ * void args;
134
+ */
135
+ export type LabelArgs = {
136
+ readonly x: number;
137
+ readonly y: number;
138
+ readonly text: string;
139
+ readonly position: LabelPosition;
140
+ readonly color: string | null;
141
+ readonly font?: string;
142
+ };
143
+ /**
144
+ * Render a `shape` glyph at a plot anchor. The five filled-marker shapes
145
+ * (`circle` / `triangle-up` / `triangle-down` / `square` / `diamond`)
146
+ * delegate to {@link drawMarker}; `cross` / `xcross` / `flag` stroke their
147
+ * geometry directly. A null `color` falls back to `fallbackColor`.
148
+ *
149
+ * @since 1.7
150
+ * @stable
151
+ * @example
152
+ * declare const ctx: RenderCtx;
153
+ * drawShape(ctx, { x: 10, y: 20, shape: "cross", size: 8, color: null }, "#90caf9");
154
+ */
155
+ export declare function drawShape(ctx: RenderCtx, args: ShapeArgs, fallbackColor: string): void;
156
+ /**
157
+ * Render a discrete marker glyph at (`x`, `y`). The renderer sets `fillStyle`
158
+ * once, then dispatches on `shape`:
159
+ *
160
+ * - `circle` → `arc` → `closePath` → `fill`.
161
+ * - `square` → single `fillRect` (`size x size`, centred on the anchor).
162
+ * - `triangle-up` / `triangle-down` / `diamond` → polygon via `beginPath` +
163
+ * `moveTo` + `lineTo`s + `closePath` + `fill`.
164
+ *
165
+ * A null `color` falls back to `fallbackColor`.
166
+ *
167
+ * @since 1.7
168
+ * @stable
169
+ * @example
170
+ * declare const ctx: RenderCtx;
171
+ * drawMarker(ctx, { x: 100, y: 50, shape: "circle", size: 6, color: "#26a69a" }, "#90caf9");
172
+ */
173
+ export declare function drawMarker(ctx: RenderCtx, args: MarkerArgs, fallbackColor: string): void;
174
+ /**
175
+ * Render a `character` glyph (Pine `plotchar`) as centred canvas text. A null
176
+ * `color` falls back to `fallbackColor`.
177
+ *
178
+ * @since 1.7
179
+ * @stable
180
+ * @example
181
+ * declare const ctx: RenderCtx;
182
+ * drawCharacter(ctx, { x: 10, y: 20, char: "A", size: 12, color: null }, "#90caf9");
183
+ */
184
+ export declare function drawCharacter(ctx: RenderCtx, args: CharacterArgs, fallbackColor: string): void;
185
+ /**
186
+ * Render an `arrow` glyph (Pine `plotarrow`) as a filled directional triangle.
187
+ * A null `color` falls back to `fallbackColor`.
188
+ *
189
+ * @since 1.7
190
+ * @stable
191
+ * @example
192
+ * declare const ctx: RenderCtx;
193
+ * drawArrow(ctx, { x: 10, y: 20, direction: "up", size: 10, color: null }, "#90caf9");
194
+ */
195
+ export declare function drawArrow(ctx: RenderCtx, args: ArrowArgs, fallbackColor: string): void;
196
+ /**
197
+ * Render a single text annotation at (`x`, `y`). The renderer sets `fillStyle`,
198
+ * `font`, `textAlign = "center"`, and a `position`-dependent `textBaseline`,
199
+ * then calls `fillText`. A null `color` falls back to `fallbackColor`; an
200
+ * omitted `font` falls back to `"10px sans-serif"`.
201
+ *
202
+ * @since 1.7
203
+ * @stable
204
+ * @example
205
+ * declare const ctx: RenderCtx;
206
+ * drawLabel(ctx, { x: 100, y: 50, text: "PEAK", position: "above", color: "#26a69a" }, "#90caf9");
207
+ */
208
+ export declare function drawLabel(ctx: RenderCtx, args: LabelArgs, fallbackColor: string): void;
209
+ //# sourceMappingURL=glyphs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glyphs.d.ts","sourceRoot":"","sources":["../../src/canvas/glyphs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,219 @@
1
+ // Copyright (c) 2026 Invinite. Licensed under the MIT License.
2
+ // See the LICENSE file in the repo root for full license text.
3
+ // Shared, model-free glyph geometry for the canvas-family adapters (uplot
4
+ // draw-hook, lightweight-charts overlay). Promoted out of the canvas2d
5
+ // reference adapter's `render/{shape,character,arrow,marker,label}.ts` so the
6
+ // `shape` / `character` / `arrow` / `marker` / `label` geometry is written ONCE
7
+ // instead of hand-ported per consumer (the bug class the `shift.ts` /
8
+ // `renderOrder.ts` promotions exist to kill). Each helper draws onto a
9
+ // `RenderCtx` and takes a plain `fallbackColor: string` (the null-color
10
+ // default), so it carries no palette / library / model types — the move is a
11
+ // pure RenderCtx-based promotion. canvas2d keeps its own `Palette`-taking
12
+ // local renderers (re-consume deferred).
13
+ const OFFSET_RATIO = 1.25;
14
+ const TWO_PI = Math.PI * 2;
15
+ const DEFAULT_LABEL_FONT = "10px sans-serif";
16
+ function anchoredShapeY(args) {
17
+ switch (args.location ?? "absolute") {
18
+ case "above":
19
+ return args.y - args.size * OFFSET_RATIO;
20
+ case "below":
21
+ return args.y + args.size * OFFSET_RATIO;
22
+ case "absolute":
23
+ return args.y;
24
+ }
25
+ }
26
+ /**
27
+ * Render a `shape` glyph at a plot anchor. The five filled-marker shapes
28
+ * (`circle` / `triangle-up` / `triangle-down` / `square` / `diamond`)
29
+ * delegate to {@link drawMarker}; `cross` / `xcross` / `flag` stroke their
30
+ * geometry directly. A null `color` falls back to `fallbackColor`.
31
+ *
32
+ * @since 1.7
33
+ * @stable
34
+ * @example
35
+ * declare const ctx: RenderCtx;
36
+ * drawShape(ctx, { x: 10, y: 20, shape: "cross", size: 8, color: null }, "#90caf9");
37
+ */
38
+ export function drawShape(ctx, args, fallbackColor) {
39
+ const y = anchoredShapeY(args);
40
+ if (args.shape === "circle" ||
41
+ args.shape === "triangle-up" ||
42
+ args.shape === "triangle-down" ||
43
+ args.shape === "square" ||
44
+ args.shape === "diamond") {
45
+ drawMarker(ctx, { ...args, y, shape: args.shape }, fallbackColor);
46
+ return;
47
+ }
48
+ ctx.strokeStyle = args.color ?? fallbackColor;
49
+ ctx.lineWidth = 1;
50
+ const half = args.size / 2;
51
+ switch (args.shape) {
52
+ case "cross":
53
+ ctx.beginPath();
54
+ ctx.moveTo(args.x - half, y);
55
+ ctx.lineTo(args.x + half, y);
56
+ ctx.moveTo(args.x, y - half);
57
+ ctx.lineTo(args.x, y + half);
58
+ ctx.stroke();
59
+ return;
60
+ case "xcross":
61
+ ctx.beginPath();
62
+ ctx.moveTo(args.x - half, y - half);
63
+ ctx.lineTo(args.x + half, y + half);
64
+ ctx.moveTo(args.x + half, y - half);
65
+ ctx.lineTo(args.x - half, y + half);
66
+ ctx.stroke();
67
+ return;
68
+ case "flag":
69
+ ctx.beginPath();
70
+ ctx.moveTo(args.x - half, y + half);
71
+ ctx.lineTo(args.x - half, y - half);
72
+ ctx.lineTo(args.x + half, y - half / 2);
73
+ ctx.lineTo(args.x - half, y);
74
+ ctx.stroke();
75
+ return;
76
+ }
77
+ }
78
+ /**
79
+ * Render a discrete marker glyph at (`x`, `y`). The renderer sets `fillStyle`
80
+ * once, then dispatches on `shape`:
81
+ *
82
+ * - `circle` → `arc` → `closePath` → `fill`.
83
+ * - `square` → single `fillRect` (`size x size`, centred on the anchor).
84
+ * - `triangle-up` / `triangle-down` / `diamond` → polygon via `beginPath` +
85
+ * `moveTo` + `lineTo`s + `closePath` + `fill`.
86
+ *
87
+ * A null `color` falls back to `fallbackColor`.
88
+ *
89
+ * @since 1.7
90
+ * @stable
91
+ * @example
92
+ * declare const ctx: RenderCtx;
93
+ * drawMarker(ctx, { x: 100, y: 50, shape: "circle", size: 6, color: "#26a69a" }, "#90caf9");
94
+ */
95
+ export function drawMarker(ctx, args, fallbackColor) {
96
+ ctx.fillStyle = args.color ?? fallbackColor;
97
+ const half = args.size / 2;
98
+ switch (args.shape) {
99
+ case "circle":
100
+ ctx.beginPath();
101
+ ctx.arc(args.x, args.y, half, 0, TWO_PI);
102
+ ctx.closePath();
103
+ ctx.fill();
104
+ return;
105
+ case "square":
106
+ ctx.fillRect(args.x - half, args.y - half, args.size, args.size);
107
+ return;
108
+ case "triangle-up":
109
+ ctx.beginPath();
110
+ ctx.moveTo(args.x, args.y - half);
111
+ ctx.lineTo(args.x + half, args.y + half);
112
+ ctx.lineTo(args.x - half, args.y + half);
113
+ ctx.closePath();
114
+ ctx.fill();
115
+ return;
116
+ case "triangle-down":
117
+ ctx.beginPath();
118
+ ctx.moveTo(args.x, args.y + half);
119
+ ctx.lineTo(args.x + half, args.y - half);
120
+ ctx.lineTo(args.x - half, args.y - half);
121
+ ctx.closePath();
122
+ ctx.fill();
123
+ return;
124
+ case "diamond":
125
+ ctx.beginPath();
126
+ ctx.moveTo(args.x, args.y - half);
127
+ ctx.lineTo(args.x + half, args.y);
128
+ ctx.lineTo(args.x, args.y + half);
129
+ ctx.lineTo(args.x - half, args.y);
130
+ ctx.closePath();
131
+ ctx.fill();
132
+ return;
133
+ }
134
+ }
135
+ function characterAnchor(args) {
136
+ switch (args.location ?? "absolute") {
137
+ case "above":
138
+ return { y: args.y - args.size, baseline: "bottom" };
139
+ case "below":
140
+ return { y: args.y + args.size, baseline: "top" };
141
+ case "absolute":
142
+ return { y: args.y, baseline: "middle" };
143
+ }
144
+ }
145
+ /**
146
+ * Render a `character` glyph (Pine `plotchar`) as centred canvas text. A null
147
+ * `color` falls back to `fallbackColor`.
148
+ *
149
+ * @since 1.7
150
+ * @stable
151
+ * @example
152
+ * declare const ctx: RenderCtx;
153
+ * drawCharacter(ctx, { x: 10, y: 20, char: "A", size: 12, color: null }, "#90caf9");
154
+ */
155
+ export function drawCharacter(ctx, args, fallbackColor) {
156
+ const resolved = characterAnchor(args);
157
+ ctx.fillStyle = args.color ?? fallbackColor;
158
+ ctx.font = `${args.size}px sans-serif`;
159
+ ctx.textAlign = "center";
160
+ ctx.textBaseline = resolved.baseline;
161
+ ctx.fillText(args.char, args.x, resolved.y);
162
+ }
163
+ /**
164
+ * Render an `arrow` glyph (Pine `plotarrow`) as a filled directional triangle.
165
+ * A null `color` falls back to `fallbackColor`.
166
+ *
167
+ * @since 1.7
168
+ * @stable
169
+ * @example
170
+ * declare const ctx: RenderCtx;
171
+ * drawArrow(ctx, { x: 10, y: 20, direction: "up", size: 10, color: null }, "#90caf9");
172
+ */
173
+ export function drawArrow(ctx, args, fallbackColor) {
174
+ const half = args.size / 2;
175
+ ctx.fillStyle = args.color ?? fallbackColor;
176
+ ctx.beginPath();
177
+ if (args.direction === "up") {
178
+ ctx.moveTo(args.x, args.y - half);
179
+ ctx.lineTo(args.x + half, args.y + half);
180
+ ctx.lineTo(args.x - half, args.y + half);
181
+ }
182
+ else {
183
+ ctx.moveTo(args.x, args.y + half);
184
+ ctx.lineTo(args.x + half, args.y - half);
185
+ ctx.lineTo(args.x - half, args.y - half);
186
+ }
187
+ ctx.closePath();
188
+ ctx.fill();
189
+ }
190
+ function labelBaseline(position) {
191
+ switch (position) {
192
+ case "above":
193
+ return "bottom";
194
+ case "below":
195
+ return "top";
196
+ case "anchor":
197
+ return "middle";
198
+ }
199
+ }
200
+ /**
201
+ * Render a single text annotation at (`x`, `y`). The renderer sets `fillStyle`,
202
+ * `font`, `textAlign = "center"`, and a `position`-dependent `textBaseline`,
203
+ * then calls `fillText`. A null `color` falls back to `fallbackColor`; an
204
+ * omitted `font` falls back to `"10px sans-serif"`.
205
+ *
206
+ * @since 1.7
207
+ * @stable
208
+ * @example
209
+ * declare const ctx: RenderCtx;
210
+ * drawLabel(ctx, { x: 100, y: 50, text: "PEAK", position: "above", color: "#26a69a" }, "#90caf9");
211
+ */
212
+ export function drawLabel(ctx, args, fallbackColor) {
213
+ ctx.fillStyle = args.color ?? fallbackColor;
214
+ ctx.font = args.font ?? DEFAULT_LABEL_FONT;
215
+ ctx.textAlign = "center";
216
+ ctx.textBaseline = labelBaseline(args.position);
217
+ ctx.fillText(args.text, args.x, args.y);
218
+ }
219
+ //# sourceMappingURL=glyphs.js.map