@fieldnotes/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,582 @@
1
+ type Listener<T> = (data: T) => void;
2
+ declare class EventBus<TEvents extends {
3
+ [K in keyof TEvents]: TEvents[K];
4
+ }> {
5
+ private listeners;
6
+ on<K extends keyof TEvents>(event: K, listener: Listener<TEvents[K]>): () => void;
7
+ off<K extends keyof TEvents>(event: K, listener: Listener<TEvents[K]>): void;
8
+ emit<K extends keyof TEvents>(event: K, data: TEvents[K]): void;
9
+ clear(): void;
10
+ }
11
+
12
+ interface Point {
13
+ x: number;
14
+ y: number;
15
+ }
16
+ interface Size {
17
+ w: number;
18
+ h: number;
19
+ }
20
+ type Bounds = Point & Size;
21
+
22
+ interface BaseElement {
23
+ id: string;
24
+ type: string;
25
+ position: Point;
26
+ zIndex: number;
27
+ locked: boolean;
28
+ }
29
+ interface StrokeElement extends BaseElement {
30
+ type: 'stroke';
31
+ points: Point[];
32
+ color: string;
33
+ width: number;
34
+ opacity: number;
35
+ }
36
+ interface NoteElement extends BaseElement {
37
+ type: 'note';
38
+ size: Size;
39
+ text: string;
40
+ backgroundColor: string;
41
+ }
42
+ interface ArrowElement extends BaseElement {
43
+ type: 'arrow';
44
+ from: Point;
45
+ to: Point;
46
+ bend: number;
47
+ color: string;
48
+ width: number;
49
+ }
50
+ interface ImageElement extends BaseElement {
51
+ type: 'image';
52
+ size: Size;
53
+ src: string;
54
+ }
55
+ interface HtmlElement extends BaseElement {
56
+ type: 'html';
57
+ size: Size;
58
+ }
59
+ type CanvasElement = StrokeElement | NoteElement | ArrowElement | ImageElement | HtmlElement;
60
+ type ElementType = CanvasElement['type'];
61
+
62
+ interface CanvasState {
63
+ version: number;
64
+ camera: {
65
+ position: Point;
66
+ zoom: number;
67
+ };
68
+ elements: CanvasElement[];
69
+ }
70
+ declare function exportState(elements: CanvasElement[], camera: {
71
+ position: Point;
72
+ zoom: number;
73
+ }): CanvasState;
74
+ declare function parseState(json: string): CanvasState;
75
+
76
+ interface CameraOptions {
77
+ minZoom?: number;
78
+ maxZoom?: number;
79
+ }
80
+ declare class Camera {
81
+ private x;
82
+ private y;
83
+ private z;
84
+ private readonly minZoom;
85
+ private readonly maxZoom;
86
+ private changeListeners;
87
+ constructor(options?: CameraOptions);
88
+ get position(): Point;
89
+ get zoom(): number;
90
+ pan(dx: number, dy: number): void;
91
+ moveTo(x: number, y: number): void;
92
+ setZoom(level: number): void;
93
+ zoomAt(level: number, screenPoint: Point): void;
94
+ screenToWorld(screen: Point): Point;
95
+ worldToScreen(world: Point): Point;
96
+ toCSSTransform(): string;
97
+ onChange(listener: () => void): () => void;
98
+ private notifyChange;
99
+ }
100
+
101
+ type BackgroundPattern = 'dots' | 'grid' | 'none';
102
+ interface BackgroundOptions {
103
+ pattern?: BackgroundPattern;
104
+ spacing?: number;
105
+ color?: string;
106
+ dotRadius?: number;
107
+ lineWidth?: number;
108
+ }
109
+ declare class Background {
110
+ private readonly pattern;
111
+ private readonly spacing;
112
+ private readonly color;
113
+ private readonly dotRadius;
114
+ private readonly lineWidth;
115
+ constructor(options?: BackgroundOptions);
116
+ render(ctx: CanvasRenderingContext2D, camera: Camera): void;
117
+ private renderDots;
118
+ private renderGrid;
119
+ }
120
+
121
+ interface ElementUpdateEvent {
122
+ previous: CanvasElement;
123
+ current: CanvasElement;
124
+ }
125
+ interface ElementStoreEvents {
126
+ add: CanvasElement;
127
+ remove: CanvasElement;
128
+ update: ElementUpdateEvent;
129
+ clear: null;
130
+ }
131
+ declare class ElementStore {
132
+ private elements;
133
+ private bus;
134
+ get count(): number;
135
+ getAll(): CanvasElement[];
136
+ getById(id: string): CanvasElement | undefined;
137
+ getElementsByType<T extends ElementType>(type: T): Extract<CanvasElement, {
138
+ type: T;
139
+ }>[];
140
+ add(element: CanvasElement): void;
141
+ update(id: string, partial: Partial<CanvasElement>): void;
142
+ remove(id: string): void;
143
+ clear(): void;
144
+ snapshot(): CanvasElement[];
145
+ loadSnapshot(elements: CanvasElement[]): void;
146
+ on<K extends keyof ElementStoreEvents>(event: K, listener: (data: ElementStoreEvents[K]) => void): () => void;
147
+ }
148
+
149
+ interface ToolContext {
150
+ camera: Camera;
151
+ store: ElementStore;
152
+ requestRender: () => void;
153
+ switchTool?: (name: string) => void;
154
+ editElement?: (id: string) => void;
155
+ setCursor?: (cursor: string) => void;
156
+ }
157
+ interface PointerState {
158
+ x: number;
159
+ y: number;
160
+ pressure: number;
161
+ }
162
+ interface Tool {
163
+ readonly name: string;
164
+ onPointerDown(state: PointerState, ctx: ToolContext): void;
165
+ onPointerMove(state: PointerState, ctx: ToolContext): void;
166
+ onPointerUp(state: PointerState, ctx: ToolContext): void;
167
+ onHover?(state: PointerState, ctx: ToolContext): void;
168
+ onActivate?(ctx: ToolContext): void;
169
+ onDeactivate?(ctx: ToolContext): void;
170
+ renderOverlay?(ctx: CanvasRenderingContext2D): void;
171
+ }
172
+ type ToolName = 'hand' | 'select' | 'pencil' | 'eraser' | 'arrow' | 'note' | 'image';
173
+
174
+ declare class ToolManager {
175
+ private tools;
176
+ private current;
177
+ private changeListeners;
178
+ get activeTool(): Tool | null;
179
+ get toolNames(): string[];
180
+ register(tool: Tool): void;
181
+ setTool(name: string, ctx: ToolContext): void;
182
+ handlePointerDown(state: PointerState, ctx: ToolContext): void;
183
+ handlePointerMove(state: PointerState, ctx: ToolContext): void;
184
+ handlePointerUp(state: PointerState, ctx: ToolContext): void;
185
+ onChange(listener: (name: string) => void): () => void;
186
+ }
187
+
188
+ interface Command {
189
+ execute(store: ElementStore): void;
190
+ undo(store: ElementStore): void;
191
+ }
192
+
193
+ interface HistoryStackOptions {
194
+ maxSize?: number;
195
+ }
196
+ declare class HistoryStack {
197
+ private undoStack;
198
+ private redoStack;
199
+ private readonly maxSize;
200
+ private changeListeners;
201
+ constructor(options?: HistoryStackOptions);
202
+ get canUndo(): boolean;
203
+ get canRedo(): boolean;
204
+ get undoCount(): number;
205
+ get redoCount(): number;
206
+ push(command: Command): void;
207
+ undo(store: ElementStore): boolean;
208
+ redo(store: ElementStore): boolean;
209
+ clear(): void;
210
+ onChange(listener: () => void): () => void;
211
+ private notifyChange;
212
+ }
213
+
214
+ declare class HistoryRecorder {
215
+ private readonly store;
216
+ private readonly stack;
217
+ private recording;
218
+ private transaction;
219
+ private updateSnapshots;
220
+ private unsubscribers;
221
+ constructor(store: ElementStore, stack: HistoryStack);
222
+ pause(): void;
223
+ resume(): void;
224
+ begin(): void;
225
+ commit(): void;
226
+ rollback(): void;
227
+ destroy(): void;
228
+ private record;
229
+ private onAdd;
230
+ private onRemove;
231
+ private onUpdate;
232
+ private flushUpdateSnapshots;
233
+ }
234
+
235
+ interface InputHandlerOptions {
236
+ toolManager?: ToolManager;
237
+ toolContext?: ToolContext;
238
+ historyRecorder?: HistoryRecorder;
239
+ historyStack?: HistoryStack;
240
+ }
241
+ declare class InputHandler {
242
+ private readonly element;
243
+ private readonly camera;
244
+ private isPanning;
245
+ private lastPointer;
246
+ private spaceHeld;
247
+ private activePointers;
248
+ private lastPinchDistance;
249
+ private lastPinchCenter;
250
+ private toolManager;
251
+ private toolContext;
252
+ private historyRecorder;
253
+ private historyStack;
254
+ private isToolActive;
255
+ private readonly abortController;
256
+ constructor(element: HTMLElement, camera: Camera, options?: InputHandlerOptions);
257
+ setToolManager(toolManager: ToolManager, toolContext: ToolContext): void;
258
+ destroy(): void;
259
+ private bind;
260
+ private onWheel;
261
+ private isInteractiveHtmlContent;
262
+ private onPointerDown;
263
+ private onPointerMove;
264
+ private onPointerUp;
265
+ private onKeyDown;
266
+ private onKeyUp;
267
+ private startPinch;
268
+ private handlePinchMove;
269
+ private getPinchPoints;
270
+ private distance;
271
+ private midpoint;
272
+ private toPointerState;
273
+ private dispatchToolDown;
274
+ private dispatchToolMove;
275
+ private dispatchToolHover;
276
+ private dispatchToolUp;
277
+ private deleteSelected;
278
+ private handleUndo;
279
+ private handleRedo;
280
+ private cancelToolIfActive;
281
+ }
282
+
283
+ interface ViewportOptions {
284
+ camera?: CameraOptions;
285
+ background?: BackgroundOptions;
286
+ }
287
+ declare class Viewport {
288
+ private readonly container;
289
+ readonly camera: Camera;
290
+ readonly store: ElementStore;
291
+ readonly toolManager: ToolManager;
292
+ readonly history: HistoryStack;
293
+ readonly domLayer: HTMLDivElement;
294
+ private readonly canvasEl;
295
+ private readonly wrapper;
296
+ private readonly unsubCamera;
297
+ private readonly unsubStore;
298
+ private readonly inputHandler;
299
+ private readonly background;
300
+ private readonly renderer;
301
+ private readonly noteEditor;
302
+ private readonly historyRecorder;
303
+ readonly toolContext: ToolContext;
304
+ private resizeObserver;
305
+ private animFrameId;
306
+ private needsRender;
307
+ private domNodes;
308
+ private htmlContent;
309
+ constructor(container: HTMLElement, options?: ViewportOptions);
310
+ get ctx(): CanvasRenderingContext2D | null;
311
+ requestRender(): void;
312
+ exportState(): CanvasState;
313
+ exportJSON(): string;
314
+ loadState(state: CanvasState): void;
315
+ loadJSON(json: string): void;
316
+ undo(): boolean;
317
+ redo(): boolean;
318
+ addImage(src: string, position: {
319
+ x: number;
320
+ y: number;
321
+ }, size?: {
322
+ w: number;
323
+ h: number;
324
+ }): void;
325
+ addHtmlElement(dom: HTMLElement, position: {
326
+ x: number;
327
+ y: number;
328
+ }, size?: {
329
+ w: number;
330
+ h: number;
331
+ }): string;
332
+ destroy(): void;
333
+ private startRenderLoop;
334
+ private render;
335
+ private startEditingNote;
336
+ private onDblClick;
337
+ private onDragOver;
338
+ private onDrop;
339
+ private syncDomNode;
340
+ private renderDomContent;
341
+ private removeDomNode;
342
+ private clearDomNodes;
343
+ private createWrapper;
344
+ private createCanvas;
345
+ private createDomLayer;
346
+ private applyCameraTransform;
347
+ private syncCanvasSize;
348
+ private observeResize;
349
+ }
350
+
351
+ declare class ElementRenderer {
352
+ isDomElement(element: CanvasElement): boolean;
353
+ renderCanvasElement(ctx: CanvasRenderingContext2D, element: CanvasElement): void;
354
+ private renderStroke;
355
+ private renderArrow;
356
+ private renderArrowhead;
357
+ }
358
+
359
+ declare class NoteEditor {
360
+ private editingId;
361
+ private editingNode;
362
+ private blurHandler;
363
+ private keyHandler;
364
+ private pointerHandler;
365
+ private pendingEditId;
366
+ get isEditing(): boolean;
367
+ get editingElementId(): string | null;
368
+ startEditing(node: HTMLDivElement, elementId: string, store: ElementStore): void;
369
+ stopEditing(store: ElementStore): void;
370
+ destroy(store: ElementStore): void;
371
+ private activateEditing;
372
+ }
373
+
374
+ declare function createId(prefix: string): string;
375
+
376
+ interface BaseDefaults {
377
+ position?: Point;
378
+ zIndex?: number;
379
+ locked?: boolean;
380
+ }
381
+ interface StrokeInput extends BaseDefaults {
382
+ points: Point[];
383
+ color?: string;
384
+ width?: number;
385
+ opacity?: number;
386
+ }
387
+ interface NoteInput extends BaseDefaults {
388
+ position: Point;
389
+ size?: Size;
390
+ text?: string;
391
+ backgroundColor?: string;
392
+ }
393
+ interface ArrowInput extends BaseDefaults {
394
+ from: Point;
395
+ to: Point;
396
+ bend?: number;
397
+ color?: string;
398
+ width?: number;
399
+ }
400
+ interface ImageInput extends BaseDefaults {
401
+ position: Point;
402
+ size: Size;
403
+ src: string;
404
+ }
405
+ interface HtmlInput extends BaseDefaults {
406
+ position: Point;
407
+ size: Size;
408
+ }
409
+ declare function createStroke(input: StrokeInput): StrokeElement;
410
+ declare function createNote(input: NoteInput): NoteElement;
411
+ declare function createArrow(input: ArrowInput): ArrowElement;
412
+ declare function createImage(input: ImageInput): ImageElement;
413
+ declare function createHtmlElement(input: HtmlInput): HtmlElement;
414
+
415
+ interface Rect {
416
+ x: number;
417
+ y: number;
418
+ w: number;
419
+ h: number;
420
+ }
421
+ declare function getArrowControlPoint(from: Point, to: Point, bend: number): Point;
422
+ declare function getArrowMidpoint(from: Point, to: Point, bend: number): Point;
423
+ declare function getBendFromPoint(from: Point, to: Point, dragPoint: Point): number;
424
+ declare function getArrowTangentAngle(from: Point, to: Point, bend: number, t: number): number;
425
+ declare function isNearBezier(point: Point, from: Point, to: Point, bend: number, threshold: number): boolean;
426
+ declare function getArrowBounds(from: Point, to: Point, bend: number): Rect;
427
+
428
+ declare class AddElementCommand implements Command {
429
+ private readonly element;
430
+ constructor(element: CanvasElement);
431
+ execute(store: ElementStore): void;
432
+ undo(store: ElementStore): void;
433
+ }
434
+ declare class RemoveElementCommand implements Command {
435
+ private readonly element;
436
+ constructor(element: CanvasElement);
437
+ execute(store: ElementStore): void;
438
+ undo(store: ElementStore): void;
439
+ }
440
+ declare class UpdateElementCommand implements Command {
441
+ private readonly id;
442
+ private readonly previous;
443
+ private readonly current;
444
+ constructor(id: string, previous: CanvasElement, current: CanvasElement);
445
+ execute(store: ElementStore): void;
446
+ undo(store: ElementStore): void;
447
+ }
448
+ declare class BatchCommand implements Command {
449
+ readonly commands: readonly Command[];
450
+ constructor(commands: Command[]);
451
+ execute(store: ElementStore): void;
452
+ undo(store: ElementStore): void;
453
+ }
454
+
455
+ declare class HandTool implements Tool {
456
+ readonly name = "hand";
457
+ private panning;
458
+ private lastScreen;
459
+ onActivate(ctx: ToolContext): void;
460
+ onDeactivate(ctx: ToolContext): void;
461
+ onPointerDown(state: PointerState, ctx: ToolContext): void;
462
+ onPointerMove(state: PointerState, ctx: ToolContext): void;
463
+ onPointerUp(_state: PointerState, ctx: ToolContext): void;
464
+ }
465
+
466
+ interface PencilToolOptions {
467
+ color?: string;
468
+ width?: number;
469
+ }
470
+ declare class PencilTool implements Tool {
471
+ readonly name = "pencil";
472
+ private drawing;
473
+ private points;
474
+ private color;
475
+ private width;
476
+ constructor(options?: PencilToolOptions);
477
+ onActivate(ctx: ToolContext): void;
478
+ onDeactivate(ctx: ToolContext): void;
479
+ setOptions(options: PencilToolOptions): void;
480
+ onPointerDown(state: PointerState, ctx: ToolContext): void;
481
+ onPointerMove(state: PointerState, ctx: ToolContext): void;
482
+ onPointerUp(_state: PointerState, ctx: ToolContext): void;
483
+ renderOverlay(ctx: CanvasRenderingContext2D): void;
484
+ }
485
+
486
+ interface EraserToolOptions {
487
+ radius?: number;
488
+ }
489
+ declare class EraserTool implements Tool {
490
+ readonly name = "eraser";
491
+ private erasing;
492
+ private readonly radius;
493
+ private readonly cursor;
494
+ constructor(options?: EraserToolOptions);
495
+ onActivate(ctx: ToolContext): void;
496
+ onDeactivate(ctx: ToolContext): void;
497
+ onPointerDown(state: PointerState, ctx: ToolContext): void;
498
+ onPointerMove(state: PointerState, ctx: ToolContext): void;
499
+ onPointerUp(_state: PointerState, _ctx: ToolContext): void;
500
+ private eraseAt;
501
+ private strokeIntersects;
502
+ }
503
+
504
+ declare class SelectTool implements Tool {
505
+ readonly name = "select";
506
+ private _selectedIds;
507
+ private mode;
508
+ private lastWorld;
509
+ private currentWorld;
510
+ private ctx;
511
+ get selectedIds(): string[];
512
+ get isMarqueeActive(): boolean;
513
+ onActivate(ctx: ToolContext): void;
514
+ onDeactivate(ctx: ToolContext): void;
515
+ onPointerDown(state: PointerState, ctx: ToolContext): void;
516
+ onPointerMove(state: PointerState, ctx: ToolContext): void;
517
+ onPointerUp(_state: PointerState, ctx: ToolContext): void;
518
+ onHover(state: PointerState, ctx: ToolContext): void;
519
+ renderOverlay(canvasCtx: CanvasRenderingContext2D): void;
520
+ private updateHoverCursor;
521
+ private handleResize;
522
+ private hitTestResizeHandle;
523
+ private getHandlePositions;
524
+ private renderMarquee;
525
+ private renderSelectionBoxes;
526
+ private getMarqueeRect;
527
+ private findElementsInRect;
528
+ private rectsOverlap;
529
+ private getElementBounds;
530
+ private hitTest;
531
+ private isInsideBounds;
532
+ }
533
+
534
+ interface ArrowToolOptions {
535
+ color?: string;
536
+ width?: number;
537
+ }
538
+ declare class ArrowTool implements Tool {
539
+ readonly name = "arrow";
540
+ private drawing;
541
+ private start;
542
+ private end;
543
+ private color;
544
+ private width;
545
+ constructor(options?: ArrowToolOptions);
546
+ onPointerDown(state: PointerState, ctx: ToolContext): void;
547
+ onPointerMove(state: PointerState, ctx: ToolContext): void;
548
+ onPointerUp(_state: PointerState, ctx: ToolContext): void;
549
+ renderOverlay(ctx: CanvasRenderingContext2D): void;
550
+ }
551
+
552
+ interface NoteToolOptions {
553
+ backgroundColor?: string;
554
+ size?: Size;
555
+ }
556
+ declare class NoteTool implements Tool {
557
+ readonly name = "note";
558
+ private backgroundColor;
559
+ private size;
560
+ constructor(options?: NoteToolOptions);
561
+ onPointerDown(_state: PointerState, _ctx: ToolContext): void;
562
+ onPointerMove(_state: PointerState, _ctx: ToolContext): void;
563
+ onPointerUp(state: PointerState, ctx: ToolContext): void;
564
+ }
565
+
566
+ interface ImageToolOptions {
567
+ size?: Size;
568
+ }
569
+ declare class ImageTool implements Tool {
570
+ readonly name = "image";
571
+ private size;
572
+ private src;
573
+ constructor(options?: ImageToolOptions);
574
+ setSrc(src: string): void;
575
+ onPointerDown(_state: PointerState, _ctx: ToolContext): void;
576
+ onPointerMove(_state: PointerState, _ctx: ToolContext): void;
577
+ onPointerUp(state: PointerState, ctx: ToolContext): void;
578
+ }
579
+
580
+ declare const VERSION = "0.1.0";
581
+
582
+ export { AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Bounds, Camera, type CameraOptions, type CanvasElement, type CanvasState, type Command, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, HandTool, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputHandler, NoteEditor, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, RemoveElementCommand, SelectTool, type Size, type StrokeElement, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, VERSION, Viewport, type ViewportOptions, createArrow, createHtmlElement, createId, createImage, createNote, createStroke, exportState, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, isNearBezier, parseState };