@coderyo/drawings 1.0.0-rc.2

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.
@@ -0,0 +1,121 @@
1
+ interface DrawingRecord {
2
+ id: string;
3
+ type: string;
4
+ symbol: string;
5
+ interval: string;
6
+ points: Array<{
7
+ t: number;
8
+ price: number;
9
+ }>;
10
+ meta?: Record<string, unknown>;
11
+ }
12
+ interface DrawingStore {
13
+ version: number;
14
+ drawings: DrawingRecord[];
15
+ }
16
+ declare function storageKey(chartId: string, symbol: string, interval: string): string;
17
+ declare function loadDrawings(key: string): DrawingStore;
18
+ declare function saveDrawings(key: string, store: DrawingStore): void;
19
+
20
+ interface DrawingStyleMeta {
21
+ color?: string;
22
+ lineWidth?: number;
23
+ locked?: boolean;
24
+ text?: string;
25
+ }
26
+ declare const DEFAULT_DRAWING_STYLE: Required<Pick<DrawingStyleMeta, 'color' | 'lineWidth'>>;
27
+ declare function getDrawingStyle(d: DrawingRecord): DrawingStyleMeta & {
28
+ color: string;
29
+ lineWidth: number;
30
+ locked: boolean;
31
+ };
32
+ declare function setDrawingStyle(d: DrawingRecord, patch: DrawingStyleMeta): void;
33
+
34
+ type DrawingTool = 'cursor' | 'trendline' | 'hline' | 'vline' | 'rectangle' | 'fibonacci' | 'text';
35
+ interface DrawingManagerOptions {
36
+ canvas: HTMLCanvasElement;
37
+ /** Main chart pane (overlay parent); cursor-mode hit tests use capture here while overlay is pass-through. */
38
+ interactionHost?: HTMLElement;
39
+ chartId: string;
40
+ symbol: string;
41
+ interval: string;
42
+ priceToY: (price: number) => number | null;
43
+ timeToX: (tMs: number) => number | null;
44
+ xToTime?: (x: number) => number | null;
45
+ yToPrice?: (y: number) => number | null;
46
+ returnToCursorAfterDraw?: boolean;
47
+ onSelectionChange?: (id: string | null, record: DrawingRecord | null) => void;
48
+ onRequestCursorTool?: () => void;
49
+ onContextMenu?: (payload: {
50
+ clientX: number;
51
+ clientY: number;
52
+ drawing: DrawingRecord | null;
53
+ }) => void;
54
+ }
55
+ declare class DrawingManager {
56
+ private readonly opts;
57
+ private tool;
58
+ private drawings;
59
+ private draft;
60
+ private selectedId;
61
+ private drag;
62
+ private activePointerId;
63
+ private key;
64
+ private readonly ctx;
65
+ private readonly handleRadius;
66
+ private labelDragIndex;
67
+ private readonly interactionHost;
68
+ private hostListenersAttached;
69
+ private moveListenerTarget;
70
+ private layerVisible;
71
+ private persistenceEnabled;
72
+ constructor(opts: DrawingManagerOptions);
73
+ getTool(): DrawingTool;
74
+ setLayerVisible(visible: boolean): void;
75
+ setPersistence(enabled: boolean): void;
76
+ setTool(tool: DrawingTool): void;
77
+ getSelectedId(): string | null;
78
+ deleteSelected(): boolean;
79
+ deselect(): void;
80
+ getSelected(): DrawingRecord | null;
81
+ copySelected(): DrawingRecord | null;
82
+ toggleLockSelected(): boolean;
83
+ updateSelectedStyle(patch: DrawingStyleMeta): void;
84
+ setReturnToCursorAfterDraw(v: boolean): void;
85
+ private applyPointerMode;
86
+ private setMoveListeners;
87
+ setContext(symbol: string, interval: string): void;
88
+ redraw(): void;
89
+ private paintAnchorLabel;
90
+ destroy(): void;
91
+ private emitSelection;
92
+ private onContextMenu;
93
+ private onHostContextMenu;
94
+ private openDrawingContextMenu;
95
+ private onHostPointerDown;
96
+ private paint;
97
+ private paintHandles;
98
+ private paintHLine;
99
+ private paintVLine;
100
+ private paintTrendline;
101
+ private paintRectangle;
102
+ private paintFibonacci;
103
+ private paintText;
104
+ private persist;
105
+ private onKeyDown;
106
+ private handleCursorPointerDown;
107
+ private onDown;
108
+ private onMove;
109
+ private onUp;
110
+ private capturePointer;
111
+ private releasePointer;
112
+ private getDrawing;
113
+ private hitPoint;
114
+ private hitPointFromClient;
115
+ private hitTestAnchorAny;
116
+ private hitTestDrawing;
117
+ private distanceToDrawing;
118
+ private distToSegment;
119
+ }
120
+
121
+ export { DEFAULT_DRAWING_STYLE, DrawingManager, type DrawingManagerOptions, type DrawingRecord, type DrawingStore, type DrawingStyleMeta, type DrawingTool, getDrawingStyle, loadDrawings, saveDrawings, setDrawingStyle, storageKey };
package/dist/index.js ADDED
@@ -0,0 +1,661 @@
1
+ // src/storage.ts
2
+ var STORAGE_VERSION = 1;
3
+ function storageKey(chartId, symbol, interval) {
4
+ return `tradview:drawings:v${STORAGE_VERSION}:${chartId}:${symbol}:${interval}`;
5
+ }
6
+ function loadDrawings(key) {
7
+ try {
8
+ const raw = localStorage.getItem(key);
9
+ if (!raw) return { version: STORAGE_VERSION, drawings: [] };
10
+ const parsed = JSON.parse(raw);
11
+ if (parsed.version !== STORAGE_VERSION) return { version: STORAGE_VERSION, drawings: [] };
12
+ return parsed;
13
+ } catch {
14
+ return { version: STORAGE_VERSION, drawings: [] };
15
+ }
16
+ }
17
+ function saveDrawings(key, store) {
18
+ localStorage.setItem(key, JSON.stringify(store));
19
+ }
20
+
21
+ // src/drawing-style.ts
22
+ var DEFAULT_DRAWING_STYLE = {
23
+ color: "#58a6ff",
24
+ lineWidth: 2
25
+ };
26
+ function getDrawingStyle(d) {
27
+ const m = d.meta ?? {};
28
+ return {
29
+ color: m.color ?? DEFAULT_DRAWING_STYLE.color,
30
+ lineWidth: m.lineWidth ?? DEFAULT_DRAWING_STYLE.lineWidth,
31
+ locked: Boolean(m.locked),
32
+ text: m.text
33
+ };
34
+ }
35
+ function setDrawingStyle(d, patch) {
36
+ d.meta = { ...d.meta ?? {}, ...patch };
37
+ }
38
+
39
+ // src/drawing-manager.ts
40
+ var FIB_LEVELS = [0, 0.236, 0.382, 0.5, 0.618, 1];
41
+ var DrawingManager = class {
42
+ constructor(opts) {
43
+ this.opts = opts;
44
+ this.interactionHost = opts.interactionHost ?? opts.canvas.parentElement;
45
+ this.ctx = {
46
+ chartId: opts.chartId,
47
+ symbol: opts.symbol,
48
+ interval: opts.interval
49
+ };
50
+ this.key = storageKey(this.ctx.chartId, this.ctx.symbol, this.ctx.interval);
51
+ this.drawings = loadDrawings(this.key).drawings;
52
+ this.handleRadius = 6 * devicePixelRatio;
53
+ opts.canvas.addEventListener("pointerdown", this.onDown);
54
+ window.addEventListener("keydown", this.onKeyDown);
55
+ opts.canvas.addEventListener("contextmenu", this.onContextMenu);
56
+ opts.canvas.style.touchAction = "none";
57
+ if (this.interactionHost) this.interactionHost.style.touchAction = "none";
58
+ this.applyPointerMode();
59
+ }
60
+ opts;
61
+ tool = "cursor";
62
+ drawings = [];
63
+ draft = null;
64
+ selectedId = null;
65
+ drag = null;
66
+ activePointerId = null;
67
+ key;
68
+ ctx;
69
+ handleRadius;
70
+ labelDragIndex = null;
71
+ interactionHost;
72
+ hostListenersAttached = false;
73
+ moveListenerTarget = null;
74
+ layerVisible = true;
75
+ persistenceEnabled = true;
76
+ getTool() {
77
+ return this.tool;
78
+ }
79
+ setLayerVisible(visible) {
80
+ this.layerVisible = visible;
81
+ this.opts.canvas.style.visibility = visible ? "visible" : "hidden";
82
+ if (!visible) {
83
+ this.opts.canvas.style.pointerEvents = "none";
84
+ } else {
85
+ this.applyPointerMode();
86
+ }
87
+ }
88
+ setPersistence(enabled) {
89
+ this.persistenceEnabled = enabled;
90
+ }
91
+ setTool(tool) {
92
+ this.tool = tool;
93
+ if (tool !== "cursor") {
94
+ this.selectedId = null;
95
+ this.drag = null;
96
+ }
97
+ this.applyPointerMode();
98
+ this.redraw();
99
+ }
100
+ getSelectedId() {
101
+ return this.selectedId;
102
+ }
103
+ deleteSelected() {
104
+ if (!this.selectedId) return false;
105
+ this.drawings = this.drawings.filter((d) => d.id !== this.selectedId);
106
+ this.selectedId = null;
107
+ this.drag = null;
108
+ this.persist();
109
+ this.redraw();
110
+ this.emitSelection();
111
+ return true;
112
+ }
113
+ deselect() {
114
+ this.selectedId = null;
115
+ this.drag = null;
116
+ this.redraw();
117
+ this.emitSelection();
118
+ }
119
+ getSelected() {
120
+ return this.selectedId ? this.getDrawing(this.selectedId) ?? null : null;
121
+ }
122
+ copySelected() {
123
+ const src = this.getSelected();
124
+ if (!src) return null;
125
+ const intervalMs = 36e5;
126
+ const copy = {
127
+ ...src,
128
+ id: `d-${Date.now()}`,
129
+ points: src.points.map((p) => ({ t: p.t + intervalMs * 0.02, price: p.price * 1.001 })),
130
+ meta: { ...src.meta ?? {} }
131
+ };
132
+ this.drawings.push(copy);
133
+ this.selectedId = copy.id;
134
+ this.persist();
135
+ this.redraw();
136
+ this.emitSelection();
137
+ return copy;
138
+ }
139
+ toggleLockSelected() {
140
+ const d = this.getSelected();
141
+ if (!d) return false;
142
+ const style = getDrawingStyle(d);
143
+ setDrawingStyle(d, { locked: !style.locked });
144
+ this.persist();
145
+ this.redraw();
146
+ this.emitSelection();
147
+ return !style.locked;
148
+ }
149
+ updateSelectedStyle(patch) {
150
+ const d = this.getSelected();
151
+ if (!d) return;
152
+ setDrawingStyle(d, patch);
153
+ this.persist();
154
+ this.redraw();
155
+ this.emitSelection();
156
+ }
157
+ setReturnToCursorAfterDraw(v) {
158
+ this.opts.returnToCursorAfterDraw = v;
159
+ }
160
+ applyPointerMode() {
161
+ const cursor = this.tool === "cursor";
162
+ this.opts.canvas.style.pointerEvents = cursor ? "none" : "auto";
163
+ const moveTarget = cursor && this.interactionHost ? this.interactionHost : this.opts.canvas;
164
+ this.setMoveListeners(moveTarget);
165
+ if (!this.interactionHost) return;
166
+ if (cursor && !this.hostListenersAttached) {
167
+ this.interactionHost.addEventListener("pointerdown", this.onHostPointerDown, true);
168
+ this.interactionHost.addEventListener("contextmenu", this.onHostContextMenu, true);
169
+ this.hostListenersAttached = true;
170
+ } else if (!cursor && this.hostListenersAttached) {
171
+ this.interactionHost.removeEventListener("pointerdown", this.onHostPointerDown, true);
172
+ this.interactionHost.removeEventListener("contextmenu", this.onHostContextMenu, true);
173
+ this.hostListenersAttached = false;
174
+ }
175
+ }
176
+ setMoveListeners(target) {
177
+ if (this.moveListenerTarget === target) return;
178
+ if (this.moveListenerTarget) {
179
+ this.moveListenerTarget.removeEventListener("pointermove", this.onMove);
180
+ this.moveListenerTarget.removeEventListener("pointerup", this.onUp);
181
+ this.moveListenerTarget.removeEventListener("pointercancel", this.onUp);
182
+ }
183
+ this.moveListenerTarget = target;
184
+ target.addEventListener("pointermove", this.onMove);
185
+ target.addEventListener("pointerup", this.onUp);
186
+ target.addEventListener("pointercancel", this.onUp);
187
+ }
188
+ setContext(symbol, interval) {
189
+ this.ctx.symbol = symbol;
190
+ this.ctx.interval = interval;
191
+ this.key = storageKey(this.ctx.chartId, symbol, interval);
192
+ this.drawings = loadDrawings(this.key).drawings;
193
+ this.selectedId = null;
194
+ this.draft = null;
195
+ this.drag = null;
196
+ this.redraw();
197
+ }
198
+ redraw() {
199
+ const canvas = this.opts.canvas;
200
+ const ctx = canvas.getContext("2d");
201
+ if (!ctx) return;
202
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
203
+ for (const d of [...this.drawings, ...this.draft ? [this.draft] : []]) {
204
+ const selected = d.id === this.selectedId;
205
+ const style = getDrawingStyle(d);
206
+ ctx.strokeStyle = selected ? "#f78166" : style.color;
207
+ ctx.fillStyle = selected ? "#f7816688" : `${style.color}44`;
208
+ ctx.lineWidth = (selected ? style.lineWidth + 1 : style.lineWidth) * devicePixelRatio;
209
+ ctx.font = `${12 * devicePixelRatio}px sans-serif`;
210
+ this.paint(ctx, d);
211
+ if (selected && !this.draft && this.tool === "cursor") this.paintHandles(ctx, d);
212
+ }
213
+ if (this.labelDragIndex != null && this.selectedId && this.drag?.kind === "anchor") {
214
+ const d = this.getDrawing(this.selectedId);
215
+ const p = d?.points[this.labelDragIndex];
216
+ if (d && p) this.paintAnchorLabel(ctx, p.t, p.price);
217
+ }
218
+ }
219
+ paintAnchorLabel(ctx, t, price) {
220
+ const x = this.opts.timeToX(t);
221
+ const y = this.opts.priceToY(price);
222
+ if (x == null || y == null) return;
223
+ const label = `${new Date(t).toLocaleString()} ${price.toLocaleString(void 0, { maximumFractionDigits: 4 })}`;
224
+ ctx.font = `${11 * devicePixelRatio}px sans-serif`;
225
+ ctx.fillStyle = "#0d1117";
226
+ const pad = 4 * devicePixelRatio;
227
+ const tw = ctx.measureText(label).width;
228
+ ctx.fillRect(x + 8, y - 20, tw + pad * 2, 16 * devicePixelRatio);
229
+ ctx.fillStyle = "#e6edf3";
230
+ ctx.fillText(label, x + 8 + pad, y - 8);
231
+ }
232
+ destroy() {
233
+ this.opts.canvas.removeEventListener("pointerdown", this.onDown);
234
+ if (this.moveListenerTarget) {
235
+ this.moveListenerTarget.removeEventListener("pointermove", this.onMove);
236
+ this.moveListenerTarget.removeEventListener("pointerup", this.onUp);
237
+ this.moveListenerTarget.removeEventListener("pointercancel", this.onUp);
238
+ this.moveListenerTarget = null;
239
+ }
240
+ this.opts.canvas.removeEventListener("contextmenu", this.onContextMenu);
241
+ if (this.interactionHost && this.hostListenersAttached) {
242
+ this.interactionHost.removeEventListener("pointerdown", this.onHostPointerDown, true);
243
+ this.interactionHost.removeEventListener("contextmenu", this.onHostContextMenu, true);
244
+ this.hostListenersAttached = false;
245
+ }
246
+ window.removeEventListener("keydown", this.onKeyDown);
247
+ }
248
+ emitSelection() {
249
+ this.opts.onSelectionChange?.(this.selectedId, this.getSelected());
250
+ }
251
+ onContextMenu = (e) => {
252
+ if (this.tool !== "cursor") return;
253
+ this.openDrawingContextMenu(e);
254
+ };
255
+ onHostContextMenu = (e) => {
256
+ if (this.tool !== "cursor") return;
257
+ const pt = this.hitPointFromClient(e.clientX, e.clientY);
258
+ if (!pt) return;
259
+ const onDrawing = this.hitTestAnchorAny(pt.x, pt.y) != null || this.hitTestDrawing(pt.x, pt.y) != null;
260
+ if (!onDrawing) return;
261
+ e.preventDefault();
262
+ e.stopPropagation();
263
+ this.openDrawingContextMenu(e);
264
+ };
265
+ openDrawingContextMenu(e) {
266
+ e.preventDefault();
267
+ const pt = this.hitPointFromClient(e.clientX, e.clientY);
268
+ if (pt) {
269
+ const hit = this.hitTestDrawing(pt.x, pt.y);
270
+ if (hit) this.selectedId = hit;
271
+ this.redraw();
272
+ this.emitSelection();
273
+ }
274
+ this.opts.onContextMenu?.({
275
+ clientX: e.clientX,
276
+ clientY: e.clientY,
277
+ drawing: this.getSelected()
278
+ });
279
+ }
280
+ onHostPointerDown = (e) => {
281
+ if (!this.layerVisible || this.tool !== "cursor") return;
282
+ if (this.handleCursorPointerDown(e)) {
283
+ e.stopPropagation();
284
+ e.preventDefault();
285
+ }
286
+ };
287
+ paint(ctx, d) {
288
+ switch (d.type) {
289
+ case "hline":
290
+ this.paintHLine(ctx, d);
291
+ break;
292
+ case "vline":
293
+ this.paintVLine(ctx, d);
294
+ break;
295
+ case "trendline":
296
+ this.paintTrendline(ctx, d);
297
+ break;
298
+ case "rectangle":
299
+ this.paintRectangle(ctx, d);
300
+ break;
301
+ case "fibonacci":
302
+ this.paintFibonacci(ctx, d);
303
+ break;
304
+ case "text":
305
+ this.paintText(ctx, d);
306
+ break;
307
+ }
308
+ }
309
+ paintHandles(ctx, d) {
310
+ for (const p of d.points) {
311
+ const x = this.opts.timeToX(p.t);
312
+ const y = this.opts.priceToY(p.price);
313
+ if (x == null || y == null) continue;
314
+ ctx.beginPath();
315
+ ctx.fillStyle = "#f78166";
316
+ ctx.strokeStyle = "#fff";
317
+ ctx.lineWidth = 2;
318
+ ctx.arc(x, y, this.handleRadius, 0, Math.PI * 2);
319
+ ctx.fill();
320
+ ctx.stroke();
321
+ }
322
+ }
323
+ paintHLine(ctx, d) {
324
+ const p = d.points[0];
325
+ if (!p) return;
326
+ const y = this.opts.priceToY(p.price);
327
+ if (y == null) return;
328
+ ctx.beginPath();
329
+ ctx.moveTo(0, y);
330
+ ctx.lineTo(this.opts.canvas.width, y);
331
+ ctx.stroke();
332
+ }
333
+ paintVLine(ctx, d) {
334
+ const p = d.points[0];
335
+ if (!p) return;
336
+ const x = this.opts.timeToX(p.t);
337
+ if (x == null) return;
338
+ ctx.beginPath();
339
+ ctx.moveTo(x, 0);
340
+ ctx.lineTo(x, this.opts.canvas.height);
341
+ ctx.stroke();
342
+ }
343
+ paintTrendline(ctx, d) {
344
+ if (d.points.length < 2) return;
345
+ const x1 = this.opts.timeToX(d.points[0].t);
346
+ const y1 = this.opts.priceToY(d.points[0].price);
347
+ const x2 = this.opts.timeToX(d.points[1].t);
348
+ const y2 = this.opts.priceToY(d.points[1].price);
349
+ if (x1 == null || y1 == null || x2 == null || y2 == null) return;
350
+ ctx.beginPath();
351
+ ctx.moveTo(x1, y1);
352
+ ctx.lineTo(x2, y2);
353
+ ctx.stroke();
354
+ }
355
+ paintRectangle(ctx, d) {
356
+ if (d.points.length < 2) return;
357
+ const x1 = this.opts.timeToX(d.points[0].t);
358
+ const y1 = this.opts.priceToY(d.points[0].price);
359
+ const x2 = this.opts.timeToX(d.points[1].t);
360
+ const y2 = this.opts.priceToY(d.points[1].price);
361
+ if (x1 == null || y1 == null || x2 == null || y2 == null) return;
362
+ const left = Math.min(x1, x2);
363
+ const top = Math.min(y1, y2);
364
+ const w = Math.abs(x2 - x1);
365
+ const h = Math.abs(y2 - y1);
366
+ ctx.fillRect(left, top, w, h);
367
+ ctx.strokeRect(left, top, w, h);
368
+ }
369
+ paintFibonacci(ctx, d) {
370
+ if (d.points.length < 2) return;
371
+ const p0 = d.points[0];
372
+ const p1 = d.points[1];
373
+ const y0 = this.opts.priceToY(p0.price);
374
+ const y1 = this.opts.priceToY(p1.price);
375
+ if (y0 == null || y1 == null) return;
376
+ const minY = Math.min(y0, y1);
377
+ const maxY = Math.max(y0, y1);
378
+ const range = maxY - minY;
379
+ ctx.setLineDash([4 * devicePixelRatio, 4 * devicePixelRatio]);
380
+ for (const lvl of FIB_LEVELS) {
381
+ const y = maxY - range * lvl;
382
+ ctx.beginPath();
383
+ ctx.moveTo(0, y);
384
+ ctx.lineTo(this.opts.canvas.width, y);
385
+ ctx.stroke();
386
+ ctx.fillStyle = "#8b949e";
387
+ ctx.fillText(`${(lvl * 100).toFixed(1)}%`, 4, y - 2);
388
+ ctx.fillStyle = "#58a6ff44";
389
+ }
390
+ ctx.setLineDash([]);
391
+ }
392
+ paintText(ctx, d) {
393
+ const p = d.points[0];
394
+ if (!p) return;
395
+ const x = this.opts.timeToX(p.t);
396
+ const y = this.opts.priceToY(p.price);
397
+ if (x == null || y == null) return;
398
+ const label = String(d.meta?.text ?? "Text");
399
+ ctx.fillStyle = "#e6edf3";
400
+ ctx.fillText(label, x + 4, y - 4);
401
+ }
402
+ persist() {
403
+ if (!this.persistenceEnabled) return;
404
+ saveDrawings(this.key, { version: 1, drawings: this.drawings });
405
+ }
406
+ onKeyDown = (e) => {
407
+ const tag = e.target?.tagName;
408
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
409
+ if (this.tool !== "cursor" || !this.selectedId) return;
410
+ if (e.key === "Delete" || e.key === "Backspace") {
411
+ this.deleteSelected();
412
+ e.preventDefault();
413
+ }
414
+ };
415
+ handleCursorPointerDown(e) {
416
+ const pt = this.hitPoint(e);
417
+ if (!pt) return false;
418
+ const anchor = this.hitTestAnchorAny(pt.x, pt.y);
419
+ if (anchor) {
420
+ const d = this.getDrawing(anchor.drawingId);
421
+ if (d && getDrawingStyle(d).locked) return true;
422
+ this.selectedId = anchor.drawingId;
423
+ this.drag = { kind: "anchor", drawingId: anchor.drawingId, pointIndex: anchor.pointIndex };
424
+ this.labelDragIndex = anchor.pointIndex;
425
+ this.capturePointer(e);
426
+ this.redraw();
427
+ this.emitSelection();
428
+ return true;
429
+ }
430
+ const id = this.hitTestDrawing(pt.x, pt.y);
431
+ this.selectedId = id;
432
+ if (id) {
433
+ const d = this.getDrawing(id);
434
+ if (d && !getDrawingStyle(d).locked) {
435
+ this.drag = {
436
+ kind: "body",
437
+ drawingId: id,
438
+ startPoints: d.points.map((p) => ({ ...p })),
439
+ startT: pt.t,
440
+ startPrice: pt.price
441
+ };
442
+ this.capturePointer(e);
443
+ }
444
+ this.redraw();
445
+ this.emitSelection();
446
+ return true;
447
+ }
448
+ if (this.selectedId) {
449
+ this.selectedId = null;
450
+ this.drag = null;
451
+ this.redraw();
452
+ this.emitSelection();
453
+ }
454
+ return false;
455
+ }
456
+ onDown = (e) => {
457
+ if (!this.layerVisible || this.tool === "cursor") return;
458
+ const pt = this.hitPoint(e);
459
+ if (!pt) return;
460
+ const type = this.tool === "hline" ? "hline" : this.tool;
461
+ this.draft = {
462
+ id: `d-${Date.now()}`,
463
+ type,
464
+ symbol: this.ctx.symbol,
465
+ interval: this.ctx.interval,
466
+ points: [pt],
467
+ meta: type === "text" ? { text: "Note" } : void 0
468
+ };
469
+ this.capturePointer(e);
470
+ };
471
+ onMove = (e) => {
472
+ if (this.drag && this.activePointerId === e.pointerId) {
473
+ const pt2 = this.hitPoint(e);
474
+ if (!pt2) return;
475
+ const d = this.getDrawing(this.drag.drawingId);
476
+ if (!d || getDrawingStyle(d).locked) return;
477
+ if (this.drag.kind === "anchor") {
478
+ this.labelDragIndex = this.drag.pointIndex;
479
+ d.points[this.drag.pointIndex] = { t: pt2.t, price: pt2.price };
480
+ } else {
481
+ const dt = pt2.t - this.drag.startT;
482
+ const dp = pt2.price - this.drag.startPrice;
483
+ d.points = this.drag.startPoints.map((p) => ({
484
+ t: p.t + dt,
485
+ price: p.price + dp
486
+ }));
487
+ }
488
+ this.redraw();
489
+ return;
490
+ }
491
+ if (!this.draft) return;
492
+ const pt = this.hitPoint(e);
493
+ if (!pt) return;
494
+ if (this.draft.type === "hline") {
495
+ this.draft.points = [{ t: pt.t, price: pt.price }];
496
+ } else if (this.draft.type === "vline" || this.draft.type === "text") {
497
+ this.draft.points = [{ t: pt.t, price: pt.price }];
498
+ } else if (this.draft.points.length === 1) {
499
+ this.draft.points = [this.draft.points[0], pt];
500
+ } else {
501
+ this.draft.points[1] = pt;
502
+ }
503
+ this.redraw();
504
+ };
505
+ onUp = (e) => {
506
+ if (this.activePointerId === e.pointerId) {
507
+ this.releasePointer(e);
508
+ }
509
+ if (this.drag) {
510
+ const changed = this.drag.kind === "anchor" || this.drag.kind === "body";
511
+ this.drag = null;
512
+ this.labelDragIndex = null;
513
+ if (changed) this.persist();
514
+ this.redraw();
515
+ return;
516
+ }
517
+ if (!this.draft) return;
518
+ const needsTwo = ["trendline", "rectangle", "fibonacci"].includes(this.draft.type);
519
+ if (needsTwo && this.draft.points.length < 2) {
520
+ this.draft = null;
521
+ return;
522
+ }
523
+ const finishedTool = this.tool;
524
+ this.drawings.push(this.draft);
525
+ this.selectedId = this.draft.id;
526
+ this.draft = null;
527
+ this.persist();
528
+ this.redraw();
529
+ this.emitSelection();
530
+ if (this.opts.returnToCursorAfterDraw && finishedTool !== "cursor") {
531
+ this.setTool("cursor");
532
+ this.opts.onRequestCursorTool?.();
533
+ }
534
+ };
535
+ capturePointer(e) {
536
+ this.activePointerId = e.pointerId;
537
+ const target = this.tool === "cursor" && this.interactionHost ? this.interactionHost : this.opts.canvas;
538
+ try {
539
+ target.setPointerCapture(e.pointerId);
540
+ } catch {
541
+ }
542
+ }
543
+ releasePointer(e) {
544
+ this.activePointerId = null;
545
+ const target = this.tool === "cursor" && this.interactionHost ? this.interactionHost : this.opts.canvas;
546
+ try {
547
+ if (target.hasPointerCapture(e.pointerId)) {
548
+ target.releasePointerCapture(e.pointerId);
549
+ }
550
+ } catch {
551
+ }
552
+ }
553
+ getDrawing(id) {
554
+ return this.drawings.find((d) => d.id === id);
555
+ }
556
+ hitPoint(e) {
557
+ return this.hitPointFromClient(e.clientX, e.clientY);
558
+ }
559
+ hitPointFromClient(clientX, clientY) {
560
+ const rect = this.opts.canvas.getBoundingClientRect();
561
+ if (rect.width <= 0 || rect.height <= 0) return null;
562
+ const x = (clientX - rect.left) / rect.width * this.opts.canvas.width;
563
+ const y = (clientY - rect.top) / rect.height * this.opts.canvas.height;
564
+ const t = this.opts.xToTime?.(x) ?? null;
565
+ const price = this.opts.yToPrice?.(y) ?? null;
566
+ if (t == null || price == null) return null;
567
+ return { t, price, x, y };
568
+ }
569
+ hitTestAnchorAny(x, y) {
570
+ const r = this.handleRadius + 4 * devicePixelRatio;
571
+ let best = null;
572
+ for (const d of this.drawings) {
573
+ for (let i = 0; i < d.points.length; i++) {
574
+ const p = d.points[i];
575
+ const px = this.opts.timeToX(p.t);
576
+ const py = this.opts.priceToY(p.price);
577
+ if (px == null || py == null) continue;
578
+ const dist = Math.hypot(x - px, y - py);
579
+ if (dist > r) continue;
580
+ if (!best || dist < best.dist) {
581
+ best = { drawingId: d.id, pointIndex: i, dist };
582
+ }
583
+ }
584
+ }
585
+ return best ? { drawingId: best.drawingId, pointIndex: best.pointIndex } : null;
586
+ }
587
+ hitTestDrawing(x, y) {
588
+ const tol = 10 * devicePixelRatio;
589
+ let best = null;
590
+ for (const d of this.drawings) {
591
+ const dist = this.distanceToDrawing(d, x, y);
592
+ if (dist == null || dist > tol) continue;
593
+ if (!best || dist < best.dist) best = { id: d.id, dist };
594
+ }
595
+ return best?.id ?? null;
596
+ }
597
+ distanceToDrawing(d, x, y) {
598
+ if (d.type === "hline" && d.points[0]) {
599
+ const py = this.opts.priceToY(d.points[0].price);
600
+ return py == null ? null : Math.abs(y - py);
601
+ }
602
+ if (d.type === "vline" && d.points[0]) {
603
+ const px = this.opts.timeToX(d.points[0].t);
604
+ return px == null ? null : Math.abs(x - px);
605
+ }
606
+ if (d.type === "trendline" && d.points.length >= 2) {
607
+ return this.distToSegment(d, x, y);
608
+ }
609
+ if ((d.type === "rectangle" || d.type === "fibonacci") && d.points.length >= 2) {
610
+ const x1 = this.opts.timeToX(d.points[0].t);
611
+ const y1 = this.opts.priceToY(d.points[0].price);
612
+ const x2 = this.opts.timeToX(d.points[1].t);
613
+ const y2 = this.opts.priceToY(d.points[1].price);
614
+ if (x1 == null || y1 == null || x2 == null || y2 == null) return null;
615
+ const left = Math.min(x1, x2);
616
+ const right = Math.max(x1, x2);
617
+ const top = Math.min(y1, y2);
618
+ const bottom = Math.max(y1, y2);
619
+ const inside = x >= left && x <= right && y >= top && y <= bottom;
620
+ const edge = Math.min(
621
+ Math.abs(x - left),
622
+ Math.abs(x - right),
623
+ Math.abs(y - top),
624
+ Math.abs(y - bottom)
625
+ );
626
+ return inside ? 0 : edge;
627
+ }
628
+ if (d.type === "text" && d.points[0]) {
629
+ const px = this.opts.timeToX(d.points[0].t);
630
+ const py = this.opts.priceToY(d.points[0].price);
631
+ if (px == null || py == null) return null;
632
+ return Math.hypot(x - px, y - py);
633
+ }
634
+ return null;
635
+ }
636
+ distToSegment(d, x, y) {
637
+ const x1 = this.opts.timeToX(d.points[0].t);
638
+ const y1 = this.opts.priceToY(d.points[0].price);
639
+ const x2 = this.opts.timeToX(d.points[1].t);
640
+ const y2 = this.opts.priceToY(d.points[1].price);
641
+ if (x1 == null || y1 == null || x2 == null || y2 == null) return null;
642
+ const dx = x2 - x1;
643
+ const dy = y2 - y1;
644
+ const len2 = dx * dx + dy * dy;
645
+ if (len2 === 0) return Math.hypot(x - x1, y - y1);
646
+ const t = Math.max(0, Math.min(1, ((x - x1) * dx + (y - y1) * dy) / len2));
647
+ const px = x1 + t * dx;
648
+ const py = y1 + t * dy;
649
+ return Math.hypot(x - px, y - py);
650
+ }
651
+ };
652
+ export {
653
+ DEFAULT_DRAWING_STYLE,
654
+ DrawingManager,
655
+ getDrawingStyle,
656
+ loadDrawings,
657
+ saveDrawings,
658
+ setDrawingStyle,
659
+ storageKey
660
+ };
661
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/storage.ts","../src/drawing-style.ts","../src/drawing-manager.ts"],"sourcesContent":["const STORAGE_VERSION = 1;\n\nexport interface DrawingRecord {\n id: string;\n type: string;\n symbol: string;\n interval: string;\n points: Array<{ t: number; price: number }>;\n meta?: Record<string, unknown>;\n}\n\nexport interface DrawingStore {\n version: number;\n drawings: DrawingRecord[];\n}\n\nexport function storageKey(chartId: string, symbol: string, interval: string): string {\n return `tradview:drawings:v${STORAGE_VERSION}:${chartId}:${symbol}:${interval}`;\n}\n\nexport function loadDrawings(key: string): DrawingStore {\n try {\n const raw = localStorage.getItem(key);\n if (!raw) return { version: STORAGE_VERSION, drawings: [] };\n const parsed = JSON.parse(raw) as DrawingStore;\n if (parsed.version !== STORAGE_VERSION) return { version: STORAGE_VERSION, drawings: [] };\n return parsed;\n } catch {\n return { version: STORAGE_VERSION, drawings: [] };\n }\n}\n\nexport function saveDrawings(key: string, store: DrawingStore): void {\n localStorage.setItem(key, JSON.stringify(store));\n}","import type { DrawingRecord } from './storage.js';\n\nexport interface DrawingStyleMeta {\n color?: string;\n lineWidth?: number;\n locked?: boolean;\n text?: string;\n}\n\nexport const DEFAULT_DRAWING_STYLE: Required<Pick<DrawingStyleMeta, 'color' | 'lineWidth'>> = {\n color: '#58a6ff',\n lineWidth: 2,\n};\n\nexport function getDrawingStyle(d: DrawingRecord): DrawingStyleMeta & {\n color: string;\n lineWidth: number;\n locked: boolean;\n} {\n const m = (d.meta ?? {}) as DrawingStyleMeta;\n return {\n color: m.color ?? DEFAULT_DRAWING_STYLE.color,\n lineWidth: m.lineWidth ?? DEFAULT_DRAWING_STYLE.lineWidth,\n locked: Boolean(m.locked),\n text: m.text,\n };\n}\n\nexport function setDrawingStyle(d: DrawingRecord, patch: DrawingStyleMeta): void {\n d.meta = { ...(d.meta ?? {}), ...patch };\n}","import { getDrawingStyle, setDrawingStyle, type DrawingStyleMeta } from './drawing-style.js';\nimport type { DrawingRecord } from './storage.js';\nimport { loadDrawings, saveDrawings, storageKey } from './storage.js';\n\nexport type { DrawingRecord, DrawingStyleMeta };\n\nexport type DrawingTool =\n | 'cursor'\n | 'trendline'\n | 'hline'\n | 'vline'\n | 'rectangle'\n | 'fibonacci'\n | 'text';\n\nconst FIB_LEVELS = [0, 0.236, 0.382, 0.5, 0.618, 1];\n\nexport interface DrawingManagerOptions {\n canvas: HTMLCanvasElement;\n /** Main chart pane (overlay parent); cursor-mode hit tests use capture here while overlay is pass-through. */\n interactionHost?: HTMLElement;\n chartId: string;\n symbol: string;\n interval: string;\n priceToY: (price: number) => number | null;\n timeToX: (tMs: number) => number | null;\n xToTime?: (x: number) => number | null;\n yToPrice?: (y: number) => number | null;\n returnToCursorAfterDraw?: boolean;\n onSelectionChange?: (id: string | null, record: DrawingRecord | null) => void;\n onRequestCursorTool?: () => void;\n onContextMenu?: (payload: {\n clientX: number;\n clientY: number;\n drawing: DrawingRecord | null;\n }) => void;\n}\n\ninterface DrawingContext {\n chartId: string;\n symbol: string;\n interval: string;\n}\n\ntype DragState =\n | { kind: 'anchor'; drawingId: string; pointIndex: number }\n | {\n kind: 'body';\n drawingId: string;\n startPoints: Array<{ t: number; price: number }>;\n startT: number;\n startPrice: number;\n };\n\nexport class DrawingManager {\n private tool: DrawingTool = 'cursor';\n private drawings: DrawingRecord[] = [];\n private draft: DrawingRecord | null = null;\n private selectedId: string | null = null;\n private drag: DragState | null = null;\n private activePointerId: number | null = null;\n private key: string;\n private readonly ctx: DrawingContext;\n private readonly handleRadius: number;\n private labelDragIndex: number | null = null;\n private readonly interactionHost: HTMLElement | null;\n private hostListenersAttached = false;\n private moveListenerTarget: HTMLElement | null = null;\n private layerVisible = true;\n private persistenceEnabled = true;\n\n constructor(private readonly opts: DrawingManagerOptions) {\n this.interactionHost = opts.interactionHost ?? opts.canvas.parentElement;\n this.ctx = {\n chartId: opts.chartId,\n symbol: opts.symbol,\n interval: opts.interval,\n };\n this.key = storageKey(this.ctx.chartId, this.ctx.symbol, this.ctx.interval);\n this.drawings = loadDrawings(this.key).drawings;\n this.handleRadius = 6 * devicePixelRatio;\n opts.canvas.addEventListener('pointerdown', this.onDown);\n window.addEventListener('keydown', this.onKeyDown);\n opts.canvas.addEventListener('contextmenu', this.onContextMenu);\n opts.canvas.style.touchAction = 'none';\n if (this.interactionHost) this.interactionHost.style.touchAction = 'none';\n this.applyPointerMode();\n }\n\n getTool(): DrawingTool {\n return this.tool;\n }\n\n setLayerVisible(visible: boolean): void {\n this.layerVisible = visible;\n this.opts.canvas.style.visibility = visible ? 'visible' : 'hidden';\n if (!visible) {\n this.opts.canvas.style.pointerEvents = 'none';\n } else {\n this.applyPointerMode();\n }\n }\n\n setPersistence(enabled: boolean): void {\n this.persistenceEnabled = enabled;\n }\n\n setTool(tool: DrawingTool): void {\n this.tool = tool;\n if (tool !== 'cursor') {\n this.selectedId = null;\n this.drag = null;\n }\n this.applyPointerMode();\n this.redraw();\n }\n\n getSelectedId(): string | null {\n return this.selectedId;\n }\n\n deleteSelected(): boolean {\n if (!this.selectedId) return false;\n this.drawings = this.drawings.filter((d) => d.id !== this.selectedId);\n this.selectedId = null;\n this.drag = null;\n this.persist();\n this.redraw();\n this.emitSelection();\n return true;\n }\n\n deselect(): void {\n this.selectedId = null;\n this.drag = null;\n this.redraw();\n this.emitSelection();\n }\n\n getSelected(): DrawingRecord | null {\n return this.selectedId ? (this.getDrawing(this.selectedId) ?? null) : null;\n }\n\n copySelected(): DrawingRecord | null {\n const src = this.getSelected();\n if (!src) return null;\n const intervalMs = 3_600_000;\n const copy: DrawingRecord = {\n ...src,\n id: `d-${Date.now()}`,\n points: src.points.map((p) => ({ t: p.t + intervalMs * 0.02, price: p.price * 1.001 })),\n meta: { ...(src.meta ?? {}) },\n };\n this.drawings.push(copy);\n this.selectedId = copy.id;\n this.persist();\n this.redraw();\n this.emitSelection();\n return copy;\n }\n\n toggleLockSelected(): boolean {\n const d = this.getSelected();\n if (!d) return false;\n const style = getDrawingStyle(d);\n setDrawingStyle(d, { locked: !style.locked });\n this.persist();\n this.redraw();\n this.emitSelection();\n return !style.locked;\n }\n\n updateSelectedStyle(patch: DrawingStyleMeta): void {\n const d = this.getSelected();\n if (!d) return;\n setDrawingStyle(d, patch);\n this.persist();\n this.redraw();\n this.emitSelection();\n }\n\n setReturnToCursorAfterDraw(v: boolean): void {\n (this.opts as { returnToCursorAfterDraw?: boolean }).returnToCursorAfterDraw = v;\n }\n\n private applyPointerMode(): void {\n const cursor = this.tool === 'cursor';\n this.opts.canvas.style.pointerEvents = cursor ? 'none' : 'auto';\n const moveTarget = cursor && this.interactionHost ? this.interactionHost : this.opts.canvas;\n this.setMoveListeners(moveTarget);\n\n if (!this.interactionHost) return;\n if (cursor && !this.hostListenersAttached) {\n this.interactionHost.addEventListener('pointerdown', this.onHostPointerDown, true);\n this.interactionHost.addEventListener('contextmenu', this.onHostContextMenu, true);\n this.hostListenersAttached = true;\n } else if (!cursor && this.hostListenersAttached) {\n this.interactionHost.removeEventListener('pointerdown', this.onHostPointerDown, true);\n this.interactionHost.removeEventListener('contextmenu', this.onHostContextMenu, true);\n this.hostListenersAttached = false;\n }\n }\n\n private setMoveListeners(target: HTMLElement): void {\n if (this.moveListenerTarget === target) return;\n if (this.moveListenerTarget) {\n this.moveListenerTarget.removeEventListener('pointermove', this.onMove);\n this.moveListenerTarget.removeEventListener('pointerup', this.onUp);\n this.moveListenerTarget.removeEventListener('pointercancel', this.onUp);\n }\n this.moveListenerTarget = target;\n target.addEventListener('pointermove', this.onMove);\n target.addEventListener('pointerup', this.onUp);\n target.addEventListener('pointercancel', this.onUp);\n }\n\n setContext(symbol: string, interval: string): void {\n this.ctx.symbol = symbol;\n this.ctx.interval = interval;\n this.key = storageKey(this.ctx.chartId, symbol, interval);\n this.drawings = loadDrawings(this.key).drawings;\n this.selectedId = null;\n this.draft = null;\n this.drag = null;\n this.redraw();\n }\n\n redraw(): void {\n const canvas = this.opts.canvas;\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n for (const d of [...this.drawings, ...(this.draft ? [this.draft] : [])]) {\n const selected = d.id === this.selectedId;\n const style = getDrawingStyle(d);\n ctx.strokeStyle = selected ? '#f78166' : style.color;\n ctx.fillStyle = selected ? '#f7816688' : `${style.color}44`;\n ctx.lineWidth = (selected ? style.lineWidth + 1 : style.lineWidth) * devicePixelRatio;\n ctx.font = `${12 * devicePixelRatio}px sans-serif`;\n this.paint(ctx, d);\n if (selected && !this.draft && this.tool === 'cursor') this.paintHandles(ctx, d);\n }\n\n if (\n this.labelDragIndex != null &&\n this.selectedId &&\n this.drag?.kind === 'anchor'\n ) {\n const d = this.getDrawing(this.selectedId);\n const p = d?.points[this.labelDragIndex];\n if (d && p) this.paintAnchorLabel(ctx, p.t, p.price);\n }\n }\n\n private paintAnchorLabel(ctx: CanvasRenderingContext2D, t: number, price: number): void {\n const x = this.opts.timeToX(t);\n const y = this.opts.priceToY(price);\n if (x == null || y == null) return;\n const label = `${new Date(t).toLocaleString()} ${price.toLocaleString(undefined, { maximumFractionDigits: 4 })}`;\n ctx.font = `${11 * devicePixelRatio}px sans-serif`;\n ctx.fillStyle = '#0d1117';\n const pad = 4 * devicePixelRatio;\n const tw = ctx.measureText(label).width;\n ctx.fillRect(x + 8, y - 20, tw + pad * 2, 16 * devicePixelRatio);\n ctx.fillStyle = '#e6edf3';\n ctx.fillText(label, x + 8 + pad, y - 8);\n }\n\n destroy(): void {\n this.opts.canvas.removeEventListener('pointerdown', this.onDown);\n if (this.moveListenerTarget) {\n this.moveListenerTarget.removeEventListener('pointermove', this.onMove);\n this.moveListenerTarget.removeEventListener('pointerup', this.onUp);\n this.moveListenerTarget.removeEventListener('pointercancel', this.onUp);\n this.moveListenerTarget = null;\n }\n this.opts.canvas.removeEventListener('contextmenu', this.onContextMenu);\n if (this.interactionHost && this.hostListenersAttached) {\n this.interactionHost.removeEventListener('pointerdown', this.onHostPointerDown, true);\n this.interactionHost.removeEventListener('contextmenu', this.onHostContextMenu, true);\n this.hostListenersAttached = false;\n }\n window.removeEventListener('keydown', this.onKeyDown);\n }\n\n private emitSelection(): void {\n this.opts.onSelectionChange?.(this.selectedId, this.getSelected());\n }\n\n private onContextMenu = (e: MouseEvent) => {\n if (this.tool !== 'cursor') return;\n this.openDrawingContextMenu(e);\n };\n\n private onHostContextMenu = (e: MouseEvent) => {\n if (this.tool !== 'cursor') return;\n const pt = this.hitPointFromClient(e.clientX, e.clientY);\n if (!pt) return;\n const onDrawing =\n this.hitTestAnchorAny(pt.x, pt.y) != null || this.hitTestDrawing(pt.x, pt.y) != null;\n if (!onDrawing) return;\n e.preventDefault();\n e.stopPropagation();\n this.openDrawingContextMenu(e);\n };\n\n private openDrawingContextMenu(e: MouseEvent): void {\n e.preventDefault();\n const pt = this.hitPointFromClient(e.clientX, e.clientY);\n if (pt) {\n const hit = this.hitTestDrawing(pt.x, pt.y);\n if (hit) this.selectedId = hit;\n this.redraw();\n this.emitSelection();\n }\n this.opts.onContextMenu?.({\n clientX: e.clientX,\n clientY: e.clientY,\n drawing: this.getSelected(),\n });\n };\n\n private onHostPointerDown = (e: PointerEvent) => {\n if (!this.layerVisible || this.tool !== 'cursor') return;\n if (this.handleCursorPointerDown(e)) {\n e.stopPropagation();\n e.preventDefault();\n }\n };\n\n private paint(ctx: CanvasRenderingContext2D, d: DrawingRecord): void {\n switch (d.type) {\n case 'hline':\n this.paintHLine(ctx, d);\n break;\n case 'vline':\n this.paintVLine(ctx, d);\n break;\n case 'trendline':\n this.paintTrendline(ctx, d);\n break;\n case 'rectangle':\n this.paintRectangle(ctx, d);\n break;\n case 'fibonacci':\n this.paintFibonacci(ctx, d);\n break;\n case 'text':\n this.paintText(ctx, d);\n break;\n }\n }\n\n private paintHandles(ctx: CanvasRenderingContext2D, d: DrawingRecord): void {\n for (const p of d.points) {\n const x = this.opts.timeToX(p.t);\n const y = this.opts.priceToY(p.price);\n if (x == null || y == null) continue;\n ctx.beginPath();\n ctx.fillStyle = '#f78166';\n ctx.strokeStyle = '#fff';\n ctx.lineWidth = 2;\n ctx.arc(x, y, this.handleRadius, 0, Math.PI * 2);\n ctx.fill();\n ctx.stroke();\n }\n }\n\n private paintHLine(ctx: CanvasRenderingContext2D, d: DrawingRecord): void {\n const p = d.points[0];\n if (!p) return;\n const y = this.opts.priceToY(p.price);\n if (y == null) return;\n ctx.beginPath();\n ctx.moveTo(0, y);\n ctx.lineTo(this.opts.canvas.width, y);\n ctx.stroke();\n }\n\n private paintVLine(ctx: CanvasRenderingContext2D, d: DrawingRecord): void {\n const p = d.points[0];\n if (!p) return;\n const x = this.opts.timeToX(p.t);\n if (x == null) return;\n ctx.beginPath();\n ctx.moveTo(x, 0);\n ctx.lineTo(x, this.opts.canvas.height);\n ctx.stroke();\n }\n\n private paintTrendline(ctx: CanvasRenderingContext2D, d: DrawingRecord): void {\n if (d.points.length < 2) return;\n const x1 = this.opts.timeToX(d.points[0]!.t);\n const y1 = this.opts.priceToY(d.points[0]!.price);\n const x2 = this.opts.timeToX(d.points[1]!.t);\n const y2 = this.opts.priceToY(d.points[1]!.price);\n if (x1 == null || y1 == null || x2 == null || y2 == null) return;\n ctx.beginPath();\n ctx.moveTo(x1, y1);\n ctx.lineTo(x2, y2);\n ctx.stroke();\n }\n\n private paintRectangle(ctx: CanvasRenderingContext2D, d: DrawingRecord): void {\n if (d.points.length < 2) return;\n const x1 = this.opts.timeToX(d.points[0]!.t);\n const y1 = this.opts.priceToY(d.points[0]!.price);\n const x2 = this.opts.timeToX(d.points[1]!.t);\n const y2 = this.opts.priceToY(d.points[1]!.price);\n if (x1 == null || y1 == null || x2 == null || y2 == null) return;\n const left = Math.min(x1, x2);\n const top = Math.min(y1, y2);\n const w = Math.abs(x2 - x1);\n const h = Math.abs(y2 - y1);\n ctx.fillRect(left, top, w, h);\n ctx.strokeRect(left, top, w, h);\n }\n\n private paintFibonacci(ctx: CanvasRenderingContext2D, d: DrawingRecord): void {\n if (d.points.length < 2) return;\n const p0 = d.points[0]!;\n const p1 = d.points[1]!;\n const y0 = this.opts.priceToY(p0.price);\n const y1 = this.opts.priceToY(p1.price);\n if (y0 == null || y1 == null) return;\n const minY = Math.min(y0, y1);\n const maxY = Math.max(y0, y1);\n const range = maxY - minY;\n ctx.setLineDash([4 * devicePixelRatio, 4 * devicePixelRatio]);\n for (const lvl of FIB_LEVELS) {\n const y = maxY - range * lvl;\n ctx.beginPath();\n ctx.moveTo(0, y);\n ctx.lineTo(this.opts.canvas.width, y);\n ctx.stroke();\n ctx.fillStyle = '#8b949e';\n ctx.fillText(`${(lvl * 100).toFixed(1)}%`, 4, y - 2);\n ctx.fillStyle = '#58a6ff44';\n }\n ctx.setLineDash([]);\n }\n\n private paintText(ctx: CanvasRenderingContext2D, d: DrawingRecord): void {\n const p = d.points[0];\n if (!p) return;\n const x = this.opts.timeToX(p.t);\n const y = this.opts.priceToY(p.price);\n if (x == null || y == null) return;\n const label = String(d.meta?.text ?? 'Text');\n ctx.fillStyle = '#e6edf3';\n ctx.fillText(label, x + 4, y - 4);\n }\n\n private persist(): void {\n if (!this.persistenceEnabled) return;\n saveDrawings(this.key, { version: 1, drawings: this.drawings });\n }\n\n private onKeyDown = (e: KeyboardEvent) => {\n const tag = (e.target as HTMLElement)?.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;\n if (this.tool !== 'cursor' || !this.selectedId) return;\n if (e.key === 'Delete' || e.key === 'Backspace') {\n this.deleteSelected();\n e.preventDefault();\n }\n };\n\n private handleCursorPointerDown(e: PointerEvent): boolean {\n const pt = this.hitPoint(e);\n if (!pt) return false;\n\n const anchor = this.hitTestAnchorAny(pt.x, pt.y);\n if (anchor) {\n const d = this.getDrawing(anchor.drawingId);\n if (d && getDrawingStyle(d).locked) return true;\n this.selectedId = anchor.drawingId;\n this.drag = { kind: 'anchor', drawingId: anchor.drawingId, pointIndex: anchor.pointIndex };\n this.labelDragIndex = anchor.pointIndex;\n this.capturePointer(e);\n this.redraw();\n this.emitSelection();\n return true;\n }\n\n const id = this.hitTestDrawing(pt.x, pt.y);\n this.selectedId = id;\n if (id) {\n const d = this.getDrawing(id);\n if (d && !getDrawingStyle(d).locked) {\n this.drag = {\n kind: 'body',\n drawingId: id,\n startPoints: d.points.map((p) => ({ ...p })),\n startT: pt.t,\n startPrice: pt.price,\n };\n this.capturePointer(e);\n }\n this.redraw();\n this.emitSelection();\n return true;\n }\n\n if (this.selectedId) {\n this.selectedId = null;\n this.drag = null;\n this.redraw();\n this.emitSelection();\n }\n return false;\n }\n\n private onDown = (e: PointerEvent) => {\n if (!this.layerVisible || this.tool === 'cursor') return;\n\n const pt = this.hitPoint(e);\n if (!pt) return;\n\n const type = this.tool === 'hline' ? 'hline' : this.tool;\n this.draft = {\n id: `d-${Date.now()}`,\n type,\n symbol: this.ctx.symbol,\n interval: this.ctx.interval,\n points: [pt],\n meta: type === 'text' ? { text: 'Note' } : undefined,\n };\n this.capturePointer(e);\n };\n\n private onMove = (e: PointerEvent) => {\n if (this.drag && this.activePointerId === e.pointerId) {\n const pt = this.hitPoint(e);\n if (!pt) return;\n const d = this.getDrawing(this.drag.drawingId);\n if (!d || getDrawingStyle(d).locked) return;\n\n if (this.drag.kind === 'anchor') {\n this.labelDragIndex = this.drag.pointIndex;\n d.points[this.drag.pointIndex] = { t: pt.t, price: pt.price };\n } else {\n const dt = pt.t - this.drag.startT;\n const dp = pt.price - this.drag.startPrice;\n d.points = this.drag.startPoints.map((p) => ({\n t: p.t + dt,\n price: p.price + dp,\n }));\n }\n this.redraw();\n return;\n }\n\n if (!this.draft) return;\n const pt = this.hitPoint(e);\n if (!pt) return;\n\n if (this.draft.type === 'hline') {\n this.draft.points = [{ t: pt.t, price: pt.price }];\n } else if (this.draft.type === 'vline' || this.draft.type === 'text') {\n this.draft.points = [{ t: pt.t, price: pt.price }];\n } else if (this.draft.points.length === 1) {\n this.draft.points = [this.draft.points[0]!, pt];\n } else {\n this.draft.points[1] = pt;\n }\n this.redraw();\n };\n\n private onUp = (e: PointerEvent) => {\n if (this.activePointerId === e.pointerId) {\n this.releasePointer(e);\n }\n\n if (this.drag) {\n const changed = this.drag.kind === 'anchor' || this.drag.kind === 'body';\n this.drag = null;\n this.labelDragIndex = null;\n if (changed) this.persist();\n this.redraw();\n return;\n }\n\n if (!this.draft) return;\n const needsTwo = ['trendline', 'rectangle', 'fibonacci'].includes(this.draft.type);\n if (needsTwo && this.draft.points.length < 2) {\n this.draft = null;\n return;\n }\n const finishedTool = this.tool;\n this.drawings.push(this.draft);\n this.selectedId = this.draft.id;\n this.draft = null;\n this.persist();\n this.redraw();\n this.emitSelection();\n if (this.opts.returnToCursorAfterDraw && finishedTool !== 'cursor') {\n this.setTool('cursor');\n this.opts.onRequestCursorTool?.();\n }\n };\n\n private capturePointer(e: PointerEvent): void {\n this.activePointerId = e.pointerId;\n const target =\n this.tool === 'cursor' && this.interactionHost ? this.interactionHost : this.opts.canvas;\n try {\n target.setPointerCapture(e.pointerId);\n } catch {\n /* ignore */\n }\n }\n\n private releasePointer(e: PointerEvent): void {\n this.activePointerId = null;\n const target =\n this.tool === 'cursor' && this.interactionHost ? this.interactionHost : this.opts.canvas;\n try {\n if (target.hasPointerCapture(e.pointerId)) {\n target.releasePointerCapture(e.pointerId);\n }\n } catch {\n /* ignore */\n }\n }\n\n private getDrawing(id: string): DrawingRecord | undefined {\n return this.drawings.find((d) => d.id === id);\n }\n\n private hitPoint(e: PointerEvent): { t: number; price: number; x: number; y: number } | null {\n return this.hitPointFromClient(e.clientX, e.clientY);\n }\n\n private hitPointFromClient(\n clientX: number,\n clientY: number,\n ): { t: number; price: number; x: number; y: number } | null {\n const rect = this.opts.canvas.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return null;\n const x = ((clientX - rect.left) / rect.width) * this.opts.canvas.width;\n const y = ((clientY - rect.top) / rect.height) * this.opts.canvas.height;\n const t = this.opts.xToTime?.(x) ?? null;\n const price = this.opts.yToPrice?.(y) ?? null;\n if (t == null || price == null) return null;\n return { t, price, x, y };\n }\n\n private hitTestAnchorAny(x: number, y: number): { drawingId: string; pointIndex: number } | null {\n const r = this.handleRadius + 4 * devicePixelRatio;\n let best: { drawingId: string; pointIndex: number; dist: number } | null = null;\n\n for (const d of this.drawings) {\n for (let i = 0; i < d.points.length; i++) {\n const p = d.points[i]!;\n const px = this.opts.timeToX(p.t);\n const py = this.opts.priceToY(p.price);\n if (px == null || py == null) continue;\n const dist = Math.hypot(x - px, y - py);\n if (dist > r) continue;\n if (!best || dist < best.dist) {\n best = { drawingId: d.id, pointIndex: i, dist };\n }\n }\n }\n return best ? { drawingId: best.drawingId, pointIndex: best.pointIndex } : null;\n }\n\n private hitTestDrawing(x: number, y: number): string | null {\n const tol = 10 * devicePixelRatio;\n let best: { id: string; dist: number } | null = null;\n\n for (const d of this.drawings) {\n const dist = this.distanceToDrawing(d, x, y);\n if (dist == null || dist > tol) continue;\n if (!best || dist < best.dist) best = { id: d.id, dist };\n }\n return best?.id ?? null;\n }\n\n private distanceToDrawing(d: DrawingRecord, x: number, y: number): number | null {\n if (d.type === 'hline' && d.points[0]) {\n const py = this.opts.priceToY(d.points[0].price);\n return py == null ? null : Math.abs(y - py);\n }\n if (d.type === 'vline' && d.points[0]) {\n const px = this.opts.timeToX(d.points[0].t);\n return px == null ? null : Math.abs(x - px);\n }\n if (d.type === 'trendline' && d.points.length >= 2) {\n return this.distToSegment(d, x, y);\n }\n if ((d.type === 'rectangle' || d.type === 'fibonacci') && d.points.length >= 2) {\n const x1 = this.opts.timeToX(d.points[0]!.t);\n const y1 = this.opts.priceToY(d.points[0]!.price);\n const x2 = this.opts.timeToX(d.points[1]!.t);\n const y2 = this.opts.priceToY(d.points[1]!.price);\n if (x1 == null || y1 == null || x2 == null || y2 == null) return null;\n const left = Math.min(x1, x2);\n const right = Math.max(x1, x2);\n const top = Math.min(y1, y2);\n const bottom = Math.max(y1, y2);\n const inside = x >= left && x <= right && y >= top && y <= bottom;\n const edge = Math.min(\n Math.abs(x - left),\n Math.abs(x - right),\n Math.abs(y - top),\n Math.abs(y - bottom),\n );\n return inside ? 0 : edge;\n }\n if (d.type === 'text' && d.points[0]) {\n const px = this.opts.timeToX(d.points[0].t);\n const py = this.opts.priceToY(d.points[0].price);\n if (px == null || py == null) return null;\n return Math.hypot(x - px, y - py);\n }\n return null;\n }\n\n private distToSegment(d: DrawingRecord, x: number, y: number): number | null {\n const x1 = this.opts.timeToX(d.points[0]!.t);\n const y1 = this.opts.priceToY(d.points[0]!.price);\n const x2 = this.opts.timeToX(d.points[1]!.t);\n const y2 = this.opts.priceToY(d.points[1]!.price);\n if (x1 == null || y1 == null || x2 == null || y2 == null) return null;\n const dx = x2 - x1;\n const dy = y2 - y1;\n const len2 = dx * dx + dy * dy;\n if (len2 === 0) return Math.hypot(x - x1, y - y1);\n const t = Math.max(0, Math.min(1, ((x - x1) * dx + (y - y1) * dy) / len2));\n const px = x1 + t * dx;\n const py = y1 + t * dy;\n return Math.hypot(x - px, y - py);\n }\n}"],"mappings":"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","names":["pt"]}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@coderyo/drawings",
3
+ "version": "1.0.0-rc.2",
4
+ "license": "UNLICENSED",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js"
10
+ }
11
+ },
12
+ "devDependencies": {
13
+ "tsup": "^8.5.0",
14
+ "typescript": "^5.8.3",
15
+ "vitest": "^3.2.4",
16
+ "@coderyo/eslint-config": "0.0.0",
17
+ "@coderyo/tsconfig": "0.0.0"
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/CodeRyoStudio/tradview.git",
25
+ "directory": "packages/drawings"
26
+ },
27
+ "scripts": {
28
+ "build": "tsup",
29
+ "test": "vitest run --passWithNoTests",
30
+ "lint": "eslint src",
31
+ "typecheck": "tsc --noEmit"
32
+ }
33
+ }