@fieldnotes/core 0.1.1 → 0.2.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.
package/dist/index.d.cts CHANGED
@@ -73,6 +73,34 @@ declare function exportState(elements: CanvasElement[], camera: {
73
73
  }): CanvasState;
74
74
  declare function parseState(json: string): CanvasState;
75
75
 
76
+ interface ElementUpdateEvent {
77
+ previous: CanvasElement;
78
+ current: CanvasElement;
79
+ }
80
+ interface ElementStoreEvents {
81
+ add: CanvasElement;
82
+ remove: CanvasElement;
83
+ update: ElementUpdateEvent;
84
+ clear: null;
85
+ }
86
+ declare class ElementStore {
87
+ private elements;
88
+ private bus;
89
+ get count(): number;
90
+ getAll(): CanvasElement[];
91
+ getById(id: string): CanvasElement | undefined;
92
+ getElementsByType<T extends ElementType>(type: T): Extract<CanvasElement, {
93
+ type: T;
94
+ }>[];
95
+ add(element: CanvasElement): void;
96
+ update(id: string, partial: Partial<CanvasElement>): void;
97
+ remove(id: string): void;
98
+ clear(): void;
99
+ snapshot(): CanvasElement[];
100
+ loadSnapshot(elements: CanvasElement[]): void;
101
+ on<K extends keyof ElementStoreEvents>(event: K, listener: (data: ElementStoreEvents[K]) => void): () => void;
102
+ }
103
+
76
104
  interface CameraOptions {
77
105
  minZoom?: number;
78
106
  maxZoom?: number;
@@ -98,6 +126,27 @@ declare class Camera {
98
126
  private notifyChange;
99
127
  }
100
128
 
129
+ interface AutoSaveOptions {
130
+ key?: string;
131
+ debounceMs?: number;
132
+ }
133
+ declare class AutoSave {
134
+ private readonly store;
135
+ private readonly camera;
136
+ private readonly key;
137
+ private readonly debounceMs;
138
+ private timerId;
139
+ private unsubscribers;
140
+ constructor(store: ElementStore, camera: Camera, options?: AutoSaveOptions);
141
+ start(): void;
142
+ stop(): void;
143
+ load(): CanvasState | null;
144
+ clear(): void;
145
+ private scheduleSave;
146
+ private cancelPending;
147
+ private save;
148
+ }
149
+
101
150
  type BackgroundPattern = 'dots' | 'grid' | 'none';
102
151
  interface BackgroundOptions {
103
152
  pattern?: BackgroundPattern;
@@ -114,38 +163,11 @@ declare class Background {
114
163
  private readonly lineWidth;
115
164
  constructor(options?: BackgroundOptions);
116
165
  render(ctx: CanvasRenderingContext2D, camera: Camera): void;
166
+ private adaptSpacing;
117
167
  private renderDots;
118
168
  private renderGrid;
119
169
  }
120
170
 
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
171
  interface ToolContext {
150
172
  camera: Camera;
151
173
  store: ElementStore;
@@ -577,6 +599,6 @@ declare class ImageTool implements Tool {
577
599
  onPointerUp(state: PointerState, ctx: ToolContext): void;
578
600
  }
579
601
 
580
- declare const VERSION = "0.1.0";
602
+ declare const VERSION = "0.1.2";
581
603
 
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 };
604
+ export { AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, 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 };
package/dist/index.d.ts CHANGED
@@ -73,6 +73,34 @@ declare function exportState(elements: CanvasElement[], camera: {
73
73
  }): CanvasState;
74
74
  declare function parseState(json: string): CanvasState;
75
75
 
76
+ interface ElementUpdateEvent {
77
+ previous: CanvasElement;
78
+ current: CanvasElement;
79
+ }
80
+ interface ElementStoreEvents {
81
+ add: CanvasElement;
82
+ remove: CanvasElement;
83
+ update: ElementUpdateEvent;
84
+ clear: null;
85
+ }
86
+ declare class ElementStore {
87
+ private elements;
88
+ private bus;
89
+ get count(): number;
90
+ getAll(): CanvasElement[];
91
+ getById(id: string): CanvasElement | undefined;
92
+ getElementsByType<T extends ElementType>(type: T): Extract<CanvasElement, {
93
+ type: T;
94
+ }>[];
95
+ add(element: CanvasElement): void;
96
+ update(id: string, partial: Partial<CanvasElement>): void;
97
+ remove(id: string): void;
98
+ clear(): void;
99
+ snapshot(): CanvasElement[];
100
+ loadSnapshot(elements: CanvasElement[]): void;
101
+ on<K extends keyof ElementStoreEvents>(event: K, listener: (data: ElementStoreEvents[K]) => void): () => void;
102
+ }
103
+
76
104
  interface CameraOptions {
77
105
  minZoom?: number;
78
106
  maxZoom?: number;
@@ -98,6 +126,27 @@ declare class Camera {
98
126
  private notifyChange;
99
127
  }
100
128
 
129
+ interface AutoSaveOptions {
130
+ key?: string;
131
+ debounceMs?: number;
132
+ }
133
+ declare class AutoSave {
134
+ private readonly store;
135
+ private readonly camera;
136
+ private readonly key;
137
+ private readonly debounceMs;
138
+ private timerId;
139
+ private unsubscribers;
140
+ constructor(store: ElementStore, camera: Camera, options?: AutoSaveOptions);
141
+ start(): void;
142
+ stop(): void;
143
+ load(): CanvasState | null;
144
+ clear(): void;
145
+ private scheduleSave;
146
+ private cancelPending;
147
+ private save;
148
+ }
149
+
101
150
  type BackgroundPattern = 'dots' | 'grid' | 'none';
102
151
  interface BackgroundOptions {
103
152
  pattern?: BackgroundPattern;
@@ -114,38 +163,11 @@ declare class Background {
114
163
  private readonly lineWidth;
115
164
  constructor(options?: BackgroundOptions);
116
165
  render(ctx: CanvasRenderingContext2D, camera: Camera): void;
166
+ private adaptSpacing;
117
167
  private renderDots;
118
168
  private renderGrid;
119
169
  }
120
170
 
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
171
  interface ToolContext {
150
172
  camera: Camera;
151
173
  store: ElementStore;
@@ -577,6 +599,6 @@ declare class ImageTool implements Tool {
577
599
  onPointerUp(state: PointerState, ctx: ToolContext): void;
578
600
  }
579
601
 
580
- declare const VERSION = "0.1.0";
602
+ declare const VERSION = "0.1.2";
581
603
 
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 };
604
+ export { AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, 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 };
package/dist/index.js CHANGED
@@ -91,6 +91,65 @@ function migrateElement(obj) {
91
91
  }
92
92
  }
93
93
 
94
+ // src/core/auto-save.ts
95
+ var DEFAULT_KEY = "fieldnotes-autosave";
96
+ var DEFAULT_DEBOUNCE_MS = 1e3;
97
+ var AutoSave = class {
98
+ constructor(store, camera, options = {}) {
99
+ this.store = store;
100
+ this.camera = camera;
101
+ this.key = options.key ?? DEFAULT_KEY;
102
+ this.debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE_MS;
103
+ }
104
+ key;
105
+ debounceMs;
106
+ timerId = null;
107
+ unsubscribers = [];
108
+ start() {
109
+ const schedule = () => this.scheduleSave();
110
+ this.unsubscribers = [
111
+ this.store.on("add", schedule),
112
+ this.store.on("remove", schedule),
113
+ this.store.on("update", schedule),
114
+ this.camera.onChange(schedule)
115
+ ];
116
+ }
117
+ stop() {
118
+ this.cancelPending();
119
+ this.unsubscribers.forEach((fn) => fn());
120
+ this.unsubscribers = [];
121
+ }
122
+ load() {
123
+ if (typeof localStorage === "undefined") return null;
124
+ const json = localStorage.getItem(this.key);
125
+ if (!json) return null;
126
+ try {
127
+ return parseState(json);
128
+ } catch {
129
+ return null;
130
+ }
131
+ }
132
+ clear() {
133
+ if (typeof localStorage === "undefined") return;
134
+ localStorage.removeItem(this.key);
135
+ }
136
+ scheduleSave() {
137
+ this.cancelPending();
138
+ this.timerId = setTimeout(() => this.save(), this.debounceMs);
139
+ }
140
+ cancelPending() {
141
+ if (this.timerId !== null) {
142
+ clearTimeout(this.timerId);
143
+ this.timerId = null;
144
+ }
145
+ }
146
+ save() {
147
+ if (typeof localStorage === "undefined") return;
148
+ const state = exportState(this.store.snapshot(), this.camera);
149
+ localStorage.setItem(this.key, JSON.stringify(state));
150
+ }
151
+ };
152
+
94
153
  // src/canvas/camera.ts
95
154
  var DEFAULT_MIN_ZOOM = 0.1;
96
155
  var DEFAULT_MAX_ZOOM = 10;
@@ -158,6 +217,7 @@ var Camera = class {
158
217
  };
159
218
 
160
219
  // src/canvas/background.ts
220
+ var MIN_PATTERN_SPACING = 16;
161
221
  var DEFAULTS = {
162
222
  pattern: "dots",
163
223
  spacing: 24,
@@ -193,8 +253,15 @@ var Background = class {
193
253
  }
194
254
  ctx.restore();
195
255
  }
256
+ adaptSpacing(baseSpacing, zoom) {
257
+ let spacing = baseSpacing * zoom;
258
+ while (spacing < MIN_PATTERN_SPACING) {
259
+ spacing *= 2;
260
+ }
261
+ return spacing;
262
+ }
196
263
  renderDots(ctx, camera, width, height) {
197
- const spacing = this.spacing * camera.zoom;
264
+ const spacing = this.adaptSpacing(this.spacing, camera.zoom);
198
265
  const offsetX = camera.position.x % spacing;
199
266
  const offsetY = camera.position.y % spacing;
200
267
  const radius = this.dotRadius * Math.min(camera.zoom, 2);
@@ -209,7 +276,7 @@ var Background = class {
209
276
  ctx.fill();
210
277
  }
211
278
  renderGrid(ctx, camera, width, height) {
212
- const spacing = this.spacing * camera.zoom;
279
+ const spacing = this.adaptSpacing(this.spacing, camera.zoom);
213
280
  const offsetX = camera.position.x % spacing;
214
281
  const offsetY = camera.position.y % spacing;
215
282
  const lineW = this.lineWidth * Math.min(camera.zoom, 2);
@@ -2150,10 +2217,11 @@ var ImageTool = class {
2150
2217
  };
2151
2218
 
2152
2219
  // src/index.ts
2153
- var VERSION = "0.1.0";
2220
+ var VERSION = "0.1.2";
2154
2221
  export {
2155
2222
  AddElementCommand,
2156
2223
  ArrowTool,
2224
+ AutoSave,
2157
2225
  Background,
2158
2226
  BatchCommand,
2159
2227
  Camera,