@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.
- package/dist/index.d.ts +121 -0
- package/dist/index.js +661 -0
- package/dist/index.js.map +1 -0
- package/package.json +33 -0
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|