@fieldnotes/svelte 0.0.0-stage → 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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +215 -2
  3. package/dist/CanvasElement.svelte +122 -0
  4. package/dist/CanvasElement.svelte.d.ts +25 -0
  5. package/dist/CanvasElement.svelte.d.ts.map +1 -0
  6. package/dist/FieldNotesCanvas.svelte +161 -0
  7. package/dist/FieldNotesCanvas.svelte.d.ts +42 -0
  8. package/dist/FieldNotesCanvas.svelte.d.ts.map +1 -0
  9. package/dist/Minimap.svelte +85 -0
  10. package/dist/Minimap.svelte.d.ts +25 -0
  11. package/dist/Minimap.svelte.d.ts.map +1 -0
  12. package/dist/ViewportProvider.svelte +13 -0
  13. package/dist/ViewportProvider.svelte.d.ts +10 -0
  14. package/dist/ViewportProvider.svelte.d.ts.map +1 -0
  15. package/dist/context.d.ts +6 -0
  16. package/dist/context.d.ts.map +1 -0
  17. package/dist/context.js +14 -0
  18. package/dist/helpers/from-source.svelte.d.ts +5 -0
  19. package/dist/helpers/from-source.svelte.d.ts.map +1 -0
  20. package/dist/helpers/from-source.svelte.js +25 -0
  21. package/dist/helpers/use-active-tool.svelte.d.ts +4 -0
  22. package/dist/helpers/use-active-tool.svelte.d.ts.map +1 -0
  23. package/dist/helpers/use-active-tool.svelte.js +8 -0
  24. package/dist/helpers/use-camera.svelte.d.ts +5 -0
  25. package/dist/helpers/use-camera.svelte.d.ts.map +1 -0
  26. package/dist/helpers/use-camera.svelte.js +7 -0
  27. package/dist/helpers/use-element-rects.svelte.d.ts +9 -0
  28. package/dist/helpers/use-element-rects.svelte.d.ts.map +1 -0
  29. package/dist/helpers/use-element-rects.svelte.js +17 -0
  30. package/dist/helpers/use-elements.svelte.d.ts +16 -0
  31. package/dist/helpers/use-elements.svelte.d.ts.map +1 -0
  32. package/dist/helpers/use-elements.svelte.js +10 -0
  33. package/dist/helpers/use-history.svelte.d.ts +10 -0
  34. package/dist/helpers/use-history.svelte.d.ts.map +1 -0
  35. package/dist/helpers/use-history.svelte.js +13 -0
  36. package/dist/helpers/use-layers.svelte.d.ts +21 -0
  37. package/dist/helpers/use-layers.svelte.d.ts.map +1 -0
  38. package/dist/helpers/use-layers.svelte.js +25 -0
  39. package/dist/helpers/use-selection-ops.svelte.d.ts +22 -0
  40. package/dist/helpers/use-selection-ops.svelte.d.ts.map +1 -0
  41. package/dist/helpers/use-selection-ops.svelte.js +27 -0
  42. package/dist/helpers/use-selection-style-details.svelte.d.ts +12 -0
  43. package/dist/helpers/use-selection-style-details.svelte.d.ts.map +1 -0
  44. package/dist/helpers/use-selection-style-details.svelte.js +15 -0
  45. package/dist/helpers/use-selection-style.svelte.d.ts +12 -0
  46. package/dist/helpers/use-selection-style.svelte.d.ts.map +1 -0
  47. package/dist/helpers/use-selection-style.svelte.js +15 -0
  48. package/dist/helpers/use-selection.svelte.d.ts +4 -0
  49. package/dist/helpers/use-selection.svelte.d.ts.map +1 -0
  50. package/dist/helpers/use-selection.svelte.js +7 -0
  51. package/dist/helpers/use-tool-options.svelte.d.ts +8 -0
  52. package/dist/helpers/use-tool-options.svelte.d.ts.map +1 -0
  53. package/dist/helpers/use-tool-options.svelte.js +28 -0
  54. package/dist/index.d.ts +23 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +15 -0
  57. package/package.json +58 -3
@@ -0,0 +1,85 @@
1
+ <script module lang="ts">
2
+ /** Props for {@link Minimap}. */
3
+ export interface MinimapProps {
4
+ /** Thumbnail width in CSS pixels. Default `200`. */
5
+ width?: number;
6
+ /** Thumbnail height in CSS pixels. Default `140`. */
7
+ height?: number;
8
+ /** Start collapsed (icon button only). Uncontrolled. Default `false`. */
9
+ defaultCollapsed?: boolean;
10
+ /** Backdrop fill behind the thumbnail. */
11
+ background?: string;
12
+ /** Viewport rectangle stroke color. */
13
+ viewportStroke?: string;
14
+ class?: string;
15
+ style?: string;
16
+ }
17
+
18
+ const toggleStyle =
19
+ 'position: absolute; top: 2px; right: 2px; width: 24px; height: 24px; padding: 0; ' +
20
+ 'border: none; border-radius: 4px; background: rgba(0,0,0,0.35); color: #fff; ' +
21
+ 'font: 14px/1 system-ui, sans-serif; cursor: pointer;';
22
+
23
+ const collapsedStyle =
24
+ 'min-width: 44px; min-height: 44px; border: 1px solid rgba(0,0,0,0.15); border-radius: 8px; ' +
25
+ 'background: rgba(255,255,255,0.85); cursor: pointer; font: 18px/1 system-ui, sans-serif;';
26
+ </script>
27
+
28
+ <!--
29
+ @component
30
+ Collapsible thumbnail overview navigator. Renders inside `<FieldNotesCanvas>`
31
+ (or under `setViewportContext`). Positioning is host-owned — place and
32
+ style the component via `class`/`style`. While collapsed, no controller
33
+ exists: zero listeners, zero rendering.
34
+ -->
35
+ <script lang="ts">
36
+ import { MinimapController } from '@fieldnotes/core';
37
+ import { useViewport } from './context.js';
38
+
39
+ const {
40
+ width = 200,
41
+ height = 140,
42
+ defaultCollapsed = false,
43
+ background,
44
+ viewportStroke,
45
+ class: className,
46
+ style = '',
47
+ }: MinimapProps = $props();
48
+ const viewport = useViewport();
49
+ // Uncontrolled: only the initial value is read.
50
+ // svelte-ignore state_referenced_locally
51
+ let collapsed = $state(defaultCollapsed);
52
+ let canvas = $state<HTMLCanvasElement | null>(null);
53
+
54
+ $effect(() => {
55
+ if (collapsed || !canvas) return;
56
+ const controller = new MinimapController(viewport, canvas, {
57
+ width,
58
+ height,
59
+ background,
60
+ viewportStroke,
61
+ });
62
+ return () => controller.dispose();
63
+ });
64
+ </script>
65
+
66
+ {#if collapsed}
67
+ <button
68
+ type="button"
69
+ aria-label="Expand minimap"
70
+ onclick={() => (collapsed = false)}
71
+ class={className}
72
+ style="{collapsedStyle} {style}">▣</button
73
+ >
74
+ {:else}
75
+ <div class={className} style="position: relative; {style}">
76
+ <canvas bind:this={canvas} style="width: {width}px; height: {height}px; display: block"
77
+ ></canvas>
78
+ <button
79
+ type="button"
80
+ aria-label="Collapse minimap"
81
+ onclick={() => (collapsed = true)}
82
+ style={toggleStyle}>–</button
83
+ >
84
+ </div>
85
+ {/if}
@@ -0,0 +1,25 @@
1
+ /** Props for {@link Minimap}. */
2
+ export interface MinimapProps {
3
+ /** Thumbnail width in CSS pixels. Default `200`. */
4
+ width?: number;
5
+ /** Thumbnail height in CSS pixels. Default `140`. */
6
+ height?: number;
7
+ /** Start collapsed (icon button only). Uncontrolled. Default `false`. */
8
+ defaultCollapsed?: boolean;
9
+ /** Backdrop fill behind the thumbnail. */
10
+ background?: string;
11
+ /** Viewport rectangle stroke color. */
12
+ viewportStroke?: string;
13
+ class?: string;
14
+ style?: string;
15
+ }
16
+ /**
17
+ * Collapsible thumbnail overview navigator. Renders inside `<FieldNotesCanvas>`
18
+ * (or under `setViewportContext`). Positioning is host-owned — place and
19
+ * style the component via `class`/`style`. While collapsed, no controller
20
+ * exists: zero listeners, zero rendering.
21
+ */
22
+ declare const Minimap: import("svelte").Component<MinimapProps, {}, "">;
23
+ type Minimap = ReturnType<typeof Minimap>;
24
+ export default Minimap;
25
+ //# sourceMappingURL=Minimap.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Minimap.svelte.d.ts","sourceRoot":"","sources":["../src/Minimap.svelte.ts"],"names":[],"mappings":"AAGE,iCAAiC;AACjC,MAAM,WAAW,YAAY;IAC3B,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA4DH;;;;;GAKG;AACH,QAAA,MAAM,OAAO,kDAAwC,CAAC;AACtD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAC1C,eAAe,OAAO,CAAC"}
@@ -0,0 +1,13 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { Viewport } from '@fieldnotes/core';
4
+ import { setViewportContext } from './context.js';
5
+
6
+ // Sets the context in its own init so children only render once the viewport exists.
7
+ const props: { viewport: Viewport; children: Snippet } = $props();
8
+ // The canvas mounts this provider once per viewport, so the initial value is the only one.
9
+ // svelte-ignore state_referenced_locally
10
+ setViewportContext(props.viewport);
11
+ </script>
12
+
13
+ {@render props.children()}
@@ -0,0 +1,10 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { Viewport } from '@fieldnotes/core';
3
+ type $$ComponentProps = {
4
+ viewport: Viewport;
5
+ children: Snippet;
6
+ };
7
+ declare const ViewportProvider: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type ViewportProvider = ReturnType<typeof ViewportProvider>;
9
+ export default ViewportProvider;
10
+ //# sourceMappingURL=ViewportProvider.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ViewportProvider.svelte.d.ts","sourceRoot":"","sources":["../src/ViewportProvider.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGhD,KAAK,gBAAgB,GAAI;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC;AAiBpE,QAAA,MAAM,gBAAgB,sDAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Viewport } from '@fieldnotes/core';
2
+ /** Provides `viewport` to descendants' `useViewport()`; call during component init. */
3
+ export declare function setViewportContext(viewport: Viewport): void;
4
+ /** The Viewport from the nearest `<FieldNotesCanvas>`. Throws outside one. */
5
+ export declare function useViewport(): Viewport;
6
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAIjD,uFAAuF;AACvF,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAE3D;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,IAAI,QAAQ,CAMtC"}
@@ -0,0 +1,14 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ const key = Symbol('fieldnotes.viewport');
3
+ /** Provides `viewport` to descendants' `useViewport()`; call during component init. */
4
+ export function setViewportContext(viewport) {
5
+ setContext(key, viewport);
6
+ }
7
+ /** The Viewport from the nearest `<FieldNotesCanvas>`. Throws outside one. */
8
+ export function useViewport() {
9
+ const viewport = getContext(key);
10
+ if (!viewport) {
11
+ throw new Error('useViewport must be used inside <FieldNotesCanvas>');
12
+ }
13
+ return viewport;
14
+ }
@@ -0,0 +1,5 @@
1
+ import type { Readable } from 'svelte/store';
2
+ import type { ViewportSource } from '@fieldnotes/core';
3
+ /** A store over `source`, live while subscribed; each `input` value resubscribes the source. */
4
+ export declare function fromSource<T>(source: ViewportSource<T>, input?: Readable<unknown>): Readable<T>;
5
+ //# sourceMappingURL=from-source.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"from-source.svelte.d.ts","sourceRoot":"","sources":["../../src/helpers/from-source.svelte.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,gGAAgG;AAChG,wBAAgB,UAAU,CAAC,CAAC,EAC1B,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,EACzB,KAAK,GAAE,QAAQ,CAAC,OAAO,CAAkB,GACxC,QAAQ,CAAC,CAAC,CAAC,CAqBb"}
@@ -0,0 +1,25 @@
1
+ import { readable } from 'svelte/store';
2
+ /** A store over `source`, live while subscribed; each `input` value resubscribes the source. */
3
+ export function fromSource(source, input = readable(null)) {
4
+ let current = source.getSnapshot();
5
+ return readable(current, (set) => {
6
+ // Svelte's store equality treats every object as changed, so compare references here.
7
+ const update = () => {
8
+ const next = source.getSnapshot();
9
+ if (next === current)
10
+ return;
11
+ current = next;
12
+ set(next);
13
+ };
14
+ let off;
15
+ const offInput = input.subscribe(() => {
16
+ off?.();
17
+ off = source.subscribe(update);
18
+ update();
19
+ });
20
+ return () => {
21
+ offInput();
22
+ off?.();
23
+ };
24
+ });
25
+ }
@@ -0,0 +1,4 @@
1
+ import type { Readable } from 'svelte/store';
2
+ /** `[activeToolName, setTool]` — the name updates on tool switches; `''` with no active tool. */
3
+ export declare function useActiveTool(): [Readable<string>, (name: string) => void];
4
+ //# sourceMappingURL=use-active-tool.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-active-tool.svelte.d.ts","sourceRoot":"","sources":["../../src/helpers/use-active-tool.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAK7C,iGAAiG;AACjG,wBAAgB,aAAa,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,CAG1E"}
@@ -0,0 +1,8 @@
1
+ import { createActiveToolSource } from '@fieldnotes/core';
2
+ import { useViewport } from '../context.js';
3
+ import { fromSource } from './from-source.svelte.js';
4
+ /** `[activeToolName, setTool]` — the name updates on tool switches; `''` with no active tool. */
5
+ export function useActiveTool() {
6
+ const viewport = useViewport();
7
+ return [fromSource(createActiveToolSource(viewport)), (name) => viewport.setTool(name)];
8
+ }
@@ -0,0 +1,5 @@
1
+ import type { Readable } from 'svelte/store';
2
+ import type { CameraState } from '@fieldnotes/core';
3
+ /** Current camera position and zoom level — updates whenever the camera moves or zoom changes. */
4
+ export declare function useCamera(): Readable<CameraState>;
5
+ //# sourceMappingURL=use-camera.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-camera.svelte.d.ts","sourceRoot":"","sources":["../../src/helpers/use-camera.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAIpD,kGAAkG;AAClG,wBAAgB,SAAS,IAAI,QAAQ,CAAC,WAAW,CAAC,CAEjD"}
@@ -0,0 +1,7 @@
1
+ import { createCameraSource } from '@fieldnotes/core';
2
+ import { useViewport } from '../context.js';
3
+ import { fromSource } from './from-source.svelte.js';
4
+ /** Current camera position and zoom level — updates whenever the camera moves or zoom changes. */
5
+ export function useCamera() {
6
+ return fromSource(createCameraSource(useViewport()));
7
+ }
@@ -0,0 +1,9 @@
1
+ import type { Readable } from 'svelte/store';
2
+ import type { ElementRect, ElementRectMatch } from '@fieldnotes/core';
3
+ /**
4
+ * World rects of the elements a host matcher selects, updating only when a
5
+ * tracked field changes. Camera motion never updates: position content under
6
+ * one camera transform and let the browser composite it. `match` is fixed at init.
7
+ */
8
+ export declare function useElementRects(match: ElementRectMatch): Readable<readonly ElementRect[]>;
9
+ //# sourceMappingURL=use-element-rects.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-element-rects.svelte.d.ts","sourceRoot":"","sources":["../../src/helpers/use-element-rects.svelte.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGtE;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,gBAAgB,GAAG,QAAQ,CAAC,SAAS,WAAW,EAAE,CAAC,CAQzF"}
@@ -0,0 +1,17 @@
1
+ import { readable } from 'svelte/store';
2
+ import { ElementRectTracker } from '@fieldnotes/core';
3
+ import { useViewport } from '../context.js';
4
+ /**
5
+ * World rects of the elements a host matcher selects, updating only when a
6
+ * tracked field changes. Camera motion never updates: position content under
7
+ * one camera transform and let the browser composite it. `match` is fixed at init.
8
+ */
9
+ export function useElementRects(match) {
10
+ const viewport = useViewport();
11
+ return readable([], (set) => {
12
+ const tracker = new ElementRectTracker(viewport, { match });
13
+ set(tracker.getRects());
14
+ tracker.onChange(set);
15
+ return () => tracker.dispose();
16
+ });
17
+ }
@@ -0,0 +1,16 @@
1
+ import type { Readable } from 'svelte/store';
2
+ import type { CanvasElement, ElementType } from '@fieldnotes/core';
3
+ /** All elements; updates on every store mutation (use a selector for hot paths). */
4
+ export declare function useElements(): Readable<CanvasElement[]>;
5
+ /** Elements of one type; updates on every store mutation. */
6
+ export declare function useElements<T extends ElementType>(type: T): Readable<Extract<CanvasElement, {
7
+ type: T;
8
+ }>[]>;
9
+ /**
10
+ * Derived value; updates only when the selected value changes (per `isEqual`,
11
+ * default: one-level shallow compare — arrays by index, plain objects by key, primitives by
12
+ * Object.is). Supply `isEqual` for deeper structures: selectors returning nested fresh objects
13
+ * still need a custom comparator because the default only compares one level deep.
14
+ */
15
+ export declare function useElements<R>(selector: (elements: CanvasElement[]) => R, isEqual?: (a: R, b: R) => boolean): Readable<R>;
16
+ //# sourceMappingURL=use-elements.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-elements.svelte.d.ts","sourceRoot":"","sources":["../../src/helpers/use-elements.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAInE,oFAAoF;AACpF,wBAAgB,WAAW,IAAI,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC;AACzD,6DAA6D;AAC7D,wBAAgB,WAAW,CAAC,CAAC,SAAS,WAAW,EAC/C,IAAI,EAAE,CAAC,GACN,QAAQ,CAAC,OAAO,CAAC,aAAa,EAAE;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAAC,EAAE,CAAC,CAAC;AACnD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,QAAQ,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,CAAC,EAC1C,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO,GAChC,QAAQ,CAAC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { createElementsSelectorSource, createElementsSource } from '@fieldnotes/core';
2
+ import { useViewport } from '../context.js';
3
+ import { fromSource } from './from-source.svelte.js';
4
+ export function useElements(arg, isEqual) {
5
+ const viewport = useViewport();
6
+ if (typeof arg === 'function') {
7
+ return fromSource(createElementsSelectorSource(viewport, () => arg, () => isEqual));
8
+ }
9
+ return fromSource(createElementsSource(viewport, () => arg));
10
+ }
@@ -0,0 +1,10 @@
1
+ import type { Readable } from 'svelte/store';
2
+ import type { HistoryState } from '@fieldnotes/core';
3
+ /** Result of {@link useHistory}. */
4
+ export type UseHistoryResult = Readable<HistoryState> & {
5
+ undo(): void;
6
+ redo(): void;
7
+ };
8
+ /** `$history` is `{ canUndo, canRedo }`; `history.undo()` / `history.redo()` act on the viewport. */
9
+ export declare function useHistory(): UseHistoryResult;
10
+ //# sourceMappingURL=use-history.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-history.svelte.d.ts","sourceRoot":"","sources":["../../src/helpers/use-history.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAIrD,oCAAoC;AACpC,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC,YAAY,CAAC,GAAG;IAAE,IAAI,IAAI,IAAI,CAAC;IAAC,IAAI,IAAI,IAAI,CAAA;CAAE,CAAC;AAEvF,qGAAqG;AACrG,wBAAgB,UAAU,IAAI,gBAAgB,CAQ7C"}
@@ -0,0 +1,13 @@
1
+ import { createHistorySource } from '@fieldnotes/core';
2
+ import { useViewport } from '../context.js';
3
+ import { fromSource } from './from-source.svelte.js';
4
+ /** `$history` is `{ canUndo, canRedo }`; `history.undo()` / `history.redo()` act on the viewport. */
5
+ export function useHistory() {
6
+ const viewport = useViewport();
7
+ const { subscribe } = fromSource(createHistorySource(viewport));
8
+ return {
9
+ subscribe,
10
+ undo: () => viewport.undo(),
11
+ redo: () => viewport.redo(),
12
+ };
13
+ }
@@ -0,0 +1,21 @@
1
+ import type { Readable } from 'svelte/store';
2
+ import type { Layer, LayersState } from '@fieldnotes/core';
3
+ /** Result of {@link useLayers}. */
4
+ export type UseLayersResult = Readable<LayersState> & {
5
+ createLayer: (name?: string) => Layer;
6
+ removeLayer: (id: string) => void;
7
+ renameLayer: (id: string, name: string) => void;
8
+ reorderLayer: (id: string, newOrder: number) => void;
9
+ setVisible: (id: string, visible: boolean) => void;
10
+ setLocked: (id: string, locked: boolean) => void;
11
+ setOpacity: (id: string, opacity: number) => void;
12
+ setActiveLayer: (id: string) => void;
13
+ moveElement: (elementId: string, layerId: string) => void;
14
+ };
15
+ /**
16
+ * Current layer list (`$layers.layers`, `$layers.activeLayerId`) and mutation helpers — updates
17
+ * whenever a layer is added, removed, reordered, renamed, or its visibility/lock/opacity/active
18
+ * state changes.
19
+ */
20
+ export declare function useLayers(): UseLayersResult;
21
+ //# sourceMappingURL=use-layers.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-layers.svelte.d.ts","sourceRoot":"","sources":["../../src/helpers/use-layers.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAI3D,mCAAmC;AACnC,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,WAAW,CAAC,GAAG;IACpD,WAAW,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,KAAK,CAAC;IACtC,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACnD,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACjD,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3D,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,eAAe,CAgB3C"}
@@ -0,0 +1,25 @@
1
+ import { createLayersSource } from '@fieldnotes/core';
2
+ import { useViewport } from '../context.js';
3
+ import { fromSource } from './from-source.svelte.js';
4
+ /**
5
+ * Current layer list (`$layers.layers`, `$layers.activeLayerId`) and mutation helpers — updates
6
+ * whenever a layer is added, removed, reordered, renamed, or its visibility/lock/opacity/active
7
+ * state changes.
8
+ */
9
+ export function useLayers() {
10
+ const viewport = useViewport();
11
+ const layerManager = viewport.layerManager;
12
+ const { subscribe } = fromSource(createLayersSource(viewport));
13
+ return {
14
+ subscribe,
15
+ createLayer: (name) => layerManager.createLayer(name),
16
+ removeLayer: (id) => layerManager.removeLayer(id),
17
+ renameLayer: (id, name) => layerManager.renameLayer(id, name),
18
+ reorderLayer: (id, newOrder) => layerManager.reorderLayer(id, newOrder),
19
+ setVisible: (id, visible) => layerManager.setLayerVisible(id, visible),
20
+ setLocked: (id, locked) => layerManager.setLayerLocked(id, locked),
21
+ setOpacity: (id, opacity) => layerManager.setLayerOpacity(id, opacity),
22
+ setActiveLayer: (id) => layerManager.setActiveLayer(id),
23
+ moveElement: (elementId, layerId) => layerManager.moveElementToLayer(elementId, layerId),
24
+ };
25
+ }
@@ -0,0 +1,22 @@
1
+ import type { Readable } from 'svelte/store';
2
+ import type { AlignEdge, DistributeAxis, SelectionOpsState } from '@fieldnotes/core';
3
+ /** Result of {@link useSelectionOps}. */
4
+ export type UseSelectionOpsResult = Readable<SelectionOpsState & {
5
+ selectedCount: number;
6
+ }> & {
7
+ group: () => void;
8
+ ungroup: () => void;
9
+ toggleLock: () => void;
10
+ align: (edge: AlignEdge) => void;
11
+ distribute: (axis: DistributeAxis) => void;
12
+ rotateCW: () => void;
13
+ rotateCCW: () => void;
14
+ };
15
+ /**
16
+ * Selection operations + derived predicates for the current selection.
17
+ * Predicates (`canGroup`/`canAlign`/…) reflect whether each action is meaningful;
18
+ * `isLocked` is `true`/`false` when the selection is uniformly locked/unlocked,
19
+ * and `null` when empty or mixed. Actions delegate to the core Viewport.
20
+ */
21
+ export declare function useSelectionOps(): UseSelectionOpsResult;
22
+ //# sourceMappingURL=use-selection-ops.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-selection-ops.svelte.d.ts","sourceRoot":"","sources":["../../src/helpers/use-selection-ops.svelte.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAIrF,yCAAyC;AACzC,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC,iBAAiB,GAAG;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG;IAC5F,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACjC,UAAU,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAC3C,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,qBAAqB,CAgBvD"}
@@ -0,0 +1,27 @@
1
+ import { derived } from 'svelte/store';
2
+ import { createSelectionOpsSource } from '@fieldnotes/core';
3
+ import { useViewport } from '../context.js';
4
+ import { fromSource } from './from-source.svelte.js';
5
+ /**
6
+ * Selection operations + derived predicates for the current selection.
7
+ * Predicates (`canGroup`/`canAlign`/…) reflect whether each action is meaningful;
8
+ * `isLocked` is `true`/`false` when the selection is uniformly locked/unlocked,
9
+ * and `null` when empty or mixed. Actions delegate to the core Viewport.
10
+ */
11
+ export function useSelectionOps() {
12
+ const viewport = useViewport();
13
+ const { subscribe } = derived(fromSource(createSelectionOpsSource(viewport)), (state) => ({
14
+ ...state,
15
+ selectedCount: state.selectedIds.length,
16
+ }));
17
+ return {
18
+ subscribe,
19
+ group: () => viewport.groupSelection(),
20
+ ungroup: () => viewport.ungroupSelection(),
21
+ toggleLock: () => viewport.toggleLockSelection(),
22
+ align: (edge) => viewport.alignSelection(edge),
23
+ distribute: (axis) => viewport.distributeSelection(axis),
24
+ rotateCW: () => viewport.rotateSelection('cw'),
25
+ rotateCCW: () => viewport.rotateSelection('ccw'),
26
+ };
27
+ }
@@ -0,0 +1,12 @@
1
+ import type { Readable } from 'svelte/store';
2
+ import type { ElementStyle, SelectionStyleDetails } from '@fieldnotes/core';
3
+ /**
4
+ * Style details of the current selection: per-field applicability and
5
+ * mixed-state, plus the common style. `null` when nothing valid is selected.
6
+ * The apply function delegates to `applyStyleToSelection` (one undo step).
7
+ */
8
+ export declare function useSelectionStyleDetails(): [
9
+ Readable<SelectionStyleDetails | null>,
10
+ (style: ElementStyle) => void
11
+ ];
12
+ //# sourceMappingURL=use-selection-style-details.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-selection-style-details.svelte.d.ts","sourceRoot":"","sources":["../../src/helpers/use-selection-style-details.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAI5E;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI;IAC1C,QAAQ,CAAC,qBAAqB,GAAG,IAAI,CAAC;IACtC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI;CAC9B,CAMA"}
@@ -0,0 +1,15 @@
1
+ import { createSelectionStyleDetailsSource } from '@fieldnotes/core';
2
+ import { useViewport } from '../context.js';
3
+ import { fromSource } from './from-source.svelte.js';
4
+ /**
5
+ * Style details of the current selection: per-field applicability and
6
+ * mixed-state, plus the common style. `null` when nothing valid is selected.
7
+ * The apply function delegates to `applyStyleToSelection` (one undo step).
8
+ */
9
+ export function useSelectionStyleDetails() {
10
+ const viewport = useViewport();
11
+ return [
12
+ fromSource(createSelectionStyleDetailsSource(viewport)),
13
+ (style) => viewport.applyStyleToSelection(style),
14
+ ];
15
+ }
@@ -0,0 +1,12 @@
1
+ import type { Readable } from 'svelte/store';
2
+ import type { ElementStyle } from '@fieldnotes/core';
3
+ /**
4
+ * Normalized style of the current selection.
5
+ * Returns `[style, applyPatch]` — `style` is `null` when nothing is selected.
6
+ * `applyPatch` applies an `ElementStyle` patch to every selected element.
7
+ */
8
+ export declare function useSelectionStyle(): [
9
+ Readable<ElementStyle | null>,
10
+ (style: ElementStyle) => void
11
+ ];
12
+ //# sourceMappingURL=use-selection-style.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-selection-style.svelte.d.ts","sourceRoot":"","sources":["../../src/helpers/use-selection-style.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAIrD;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI;IACnC,QAAQ,CAAC,YAAY,GAAG,IAAI,CAAC;IAC7B,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI;CAC9B,CAMA"}
@@ -0,0 +1,15 @@
1
+ import { createSelectionStyleSource } from '@fieldnotes/core';
2
+ import { useViewport } from '../context.js';
3
+ import { fromSource } from './from-source.svelte.js';
4
+ /**
5
+ * Normalized style of the current selection.
6
+ * Returns `[style, applyPatch]` — `style` is `null` when nothing is selected.
7
+ * `applyPatch` applies an `ElementStyle` patch to every selected element.
8
+ */
9
+ export function useSelectionStyle() {
10
+ const viewport = useViewport();
11
+ return [
12
+ fromSource(createSelectionStyleSource(viewport)),
13
+ (style) => viewport.applyStyleToSelection(style),
14
+ ];
15
+ }
@@ -0,0 +1,4 @@
1
+ import type { Readable } from 'svelte/store';
2
+ /** Selected element ids. Stable array reference — only changes when the selection changes. */
3
+ export declare function useSelection(): Readable<string[]>;
4
+ //# sourceMappingURL=use-selection.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-selection.svelte.d.ts","sourceRoot":"","sources":["../../src/helpers/use-selection.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAK7C,8FAA8F;AAC9F,wBAAgB,YAAY,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC,CAEjD"}
@@ -0,0 +1,7 @@
1
+ import { createSelectionSource } from '@fieldnotes/core';
2
+ import { useViewport } from '../context.js';
3
+ import { fromSource } from './from-source.svelte.js';
4
+ /** Selected element ids. Stable array reference — only changes when the selection changes. */
5
+ export function useSelection() {
6
+ return fromSource(createSelectionSource(useViewport()));
7
+ }
@@ -0,0 +1,8 @@
1
+ import type { Readable } from 'svelte/store';
2
+ /**
3
+ * `[options, setOptions]` for the named tool — updates when the tool's options change.
4
+ * `options` is `null` if the tool is not registered or does not expose options.
5
+ * A getter `toolName` is tracked: the store resubscribes to the new tool.
6
+ */
7
+ export declare function useToolOptions<T extends Record<string, unknown>>(toolName: string | (() => string)): [Readable<T | null>, (partial: Partial<T>) => void];
8
+ //# sourceMappingURL=use-tool-options.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tool-options.svelte.d.ts","sourceRoot":"","sources":["../../src/helpers/use-tool-options.svelte.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAK7C;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9D,QAAQ,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,GAChC,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAiBrD"}
@@ -0,0 +1,28 @@
1
+ import { get, writable } from 'svelte/store';
2
+ import { createToolOptionsSource } from '@fieldnotes/core';
3
+ import { useViewport } from '../context.js';
4
+ import { fromSource } from './from-source.svelte.js';
5
+ /**
6
+ * `[options, setOptions]` for the named tool — updates when the tool's options change.
7
+ * `options` is `null` if the tool is not registered or does not expose options.
8
+ * A getter `toolName` is tracked: the store resubscribes to the new tool.
9
+ */
10
+ export function useToolOptions(toolName) {
11
+ const viewport = useViewport();
12
+ let getName;
13
+ let input;
14
+ if (typeof toolName === 'string') {
15
+ getName = () => toolName;
16
+ }
17
+ else {
18
+ const name = writable(toolName());
19
+ $effect.pre(() => name.set(toolName()));
20
+ getName = () => get(name);
21
+ input = name;
22
+ }
23
+ const options = fromSource(createToolOptionsSource(viewport, getName), input);
24
+ const setOptions = (partial) => {
25
+ viewport.toolManager.getTool(getName())?.setOptions?.(partial);
26
+ };
27
+ return [options, setOptions];
28
+ }
@@ -0,0 +1,23 @@
1
+ export { default as FieldNotesCanvas } from './FieldNotesCanvas.svelte';
2
+ export type { FieldNotesCanvasProps } from './FieldNotesCanvas.svelte';
3
+ export { setViewportContext, useViewport } from './context.js';
4
+ export { default as CanvasElement } from './CanvasElement.svelte';
5
+ export type { CanvasElementProps } from './CanvasElement.svelte';
6
+ export { default as Minimap } from './Minimap.svelte';
7
+ export type { MinimapProps } from './Minimap.svelte';
8
+ export { useCamera } from './helpers/use-camera.svelte.js';
9
+ export { useElements } from './helpers/use-elements.svelte.js';
10
+ export { useSelection } from './helpers/use-selection.svelte.js';
11
+ export { useActiveTool } from './helpers/use-active-tool.svelte.js';
12
+ export { useToolOptions } from './helpers/use-tool-options.svelte.js';
13
+ export { useSelectionStyle } from './helpers/use-selection-style.svelte.js';
14
+ export { useSelectionStyleDetails } from './helpers/use-selection-style-details.svelte.js';
15
+ export { useHistory } from './helpers/use-history.svelte.js';
16
+ export type { UseHistoryResult } from './helpers/use-history.svelte.js';
17
+ export { useLayers } from './helpers/use-layers.svelte.js';
18
+ export type { UseLayersResult } from './helpers/use-layers.svelte.js';
19
+ export { useSelectionOps } from './helpers/use-selection-ops.svelte.js';
20
+ export type { UseSelectionOpsResult } from './helpers/use-selection-ops.svelte.js';
21
+ export { useElementRects } from './helpers/use-element-rects.svelte.js';
22
+ export type { AlignEdge, CameraState, DistributeAxis, ElementRect, ElementStyle, HistoryState, LayersState, SelectionOpsState, SelectionStyleDetails, } from '@fieldnotes/core';
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,YAAY,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,YAAY,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACtD,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAC5E,OAAO,EAAE,wBAAwB,EAAE,MAAM,iDAAiD,CAAC;AAC3F,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAC7D,YAAY,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,YAAY,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACxE,YAAY,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACxE,YAAY,EACV,SAAS,EACT,WAAW,EACX,cAAc,EACd,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC"}