@ingcreators/annot-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.
Files changed (65) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +201 -0
  3. package/README.md +56 -0
  4. package/dist/auto-capture-options.d.ts +78 -0
  5. package/dist/editor/arrow-markers.d.ts +142 -0
  6. package/dist/editor/bake-translate.d.ts +192 -0
  7. package/dist/editor/font-registry.d.ts +58 -0
  8. package/dist/editor/gradient-utils.d.ts +23 -0
  9. package/dist/editor/history-core.d.ts +48 -0
  10. package/dist/editor/icons/brand-icons.d.ts +53 -0
  11. package/dist/editor/icons/material-symbols.d.ts +105 -0
  12. package/dist/editor/icons/registry.d.ts +179 -0
  13. package/dist/editor/icons/render.d.ts +22 -0
  14. package/dist/editor/icons/sanitize.d.ts +60 -0
  15. package/dist/editor/index.d.ts +13 -0
  16. package/dist/editor/path-utils.d.ts +32 -0
  17. package/dist/editor/property-schema.d.ts +263 -0
  18. package/dist/editor/rich-text-mapper.d.ts +8 -0
  19. package/dist/editor/selection-geometry.d.ts +66 -0
  20. package/dist/editor/shape-utils.d.ts +27 -0
  21. package/dist/editor/svg-format.d.ts +68 -0
  22. package/dist/editor/svg-id-utils.d.ts +37 -0
  23. package/dist/editor/svg-to-annotation-shapes.d.ts +34 -0
  24. package/dist/editor/text-utils.d.ts +212 -0
  25. package/dist/editor/tool-lifecycle.d.ts +56 -0
  26. package/dist/editor/tool-options.d.ts +126 -0
  27. package/dist/editor/tool-panel-adapter.d.ts +105 -0
  28. package/dist/editor/tool-preset-serde.d.ts +43 -0
  29. package/dist/editor/tool-registry.d.ts +320 -0
  30. package/dist/editor/tool-style-reader.d.ts +36 -0
  31. package/dist/editor/tool-style-writer.d.ts +33 -0
  32. package/dist/editor/toolbar-icons.d.ts +84 -0
  33. package/dist/editor/transform-utils.d.ts +127 -0
  34. package/dist/editor/viewport-math.d.ts +69 -0
  35. package/dist/encode/index.d.ts +4 -0
  36. package/dist/encode/options.d.ts +79 -0
  37. package/dist/encode/png8.d.ts +10 -0
  38. package/dist/headless.d.ts +29 -0
  39. package/dist/icons/index.d.ts +15 -0
  40. package/dist/icons/types.d.ts +108 -0
  41. package/dist/index.d.ts +1 -0
  42. package/dist/index.js +3164 -0
  43. package/dist/storage/errors.d.ts +59 -0
  44. package/dist/storage/index.d.ts +6 -0
  45. package/dist/storage/metadata-cache.d.ts +231 -0
  46. package/dist/storage/path.d.ts +49 -0
  47. package/dist/storage/thumbnail-cache.d.ts +110 -0
  48. package/dist/storage/thumbnail.d.ts +31 -0
  49. package/dist/storage/types.d.ts +677 -0
  50. package/dist/utils/assert.d.ts +31 -0
  51. package/dist/utils/constants.d.ts +10 -0
  52. package/dist/utils/dash-utils.d.ts +6 -0
  53. package/dist/utils/desktop-bridge.d.ts +349 -0
  54. package/dist/utils/filename.d.ts +48 -0
  55. package/dist/utils/id.d.ts +21 -0
  56. package/dist/utils/index.d.ts +5 -0
  57. package/dist/utils/types.d.ts +20 -0
  58. package/dist/xmp/xmp-browser.d.ts +39 -0
  59. package/dist/zip/zip-builder.d.ts +8 -0
  60. package/dist/zip/zip-bytes.d.ts +22 -0
  61. package/package.json +58 -0
  62. package/styles/editor.css +1912 -0
  63. package/styles/fonts.css +46 -0
  64. package/styles/property-panel.css +779 -0
  65. package/styles/toolbar.css +673 -0
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Pure assertion helpers shared across `@ingcreators/annot-core`,
3
+ * `@ingcreators/annot-web`, and `@ingcreators/annot-extension`.
4
+ *
5
+ * Phase 6 of `docs/plans/source-audit-cleanup.md` introduced the
6
+ * `assertNonNull` helper as the replacement for the long tail of
7
+ * silent `value!` non-null assertions: when the assertion is
8
+ * actually a load-bearing runtime invariant (and not a "the
9
+ * compiler should have known this" situation), routing through
10
+ * this helper turns a misleading `Cannot read properties of null`
11
+ * crash into a meaningful labelled error.
12
+ *
13
+ * DOM-free — exported from the headless entry point too.
14
+ */
15
+ /**
16
+ * Assert that `v` is neither `null` nor `undefined`. On failure,
17
+ * throw an `Error` whose message is `Assertion failed: <label>`.
18
+ *
19
+ * Prefer this over a bare `value!` whenever:
20
+ * - The non-null guarantee depends on a runtime invariant a
21
+ * reader can't see at the use site (e.g. "by this point in
22
+ * `setupEditor`, `#canvas` has been mounted").
23
+ * - The guarantee is fragile (e.g. depends on a Lit element's
24
+ * render cycle), and a future refactor could break it.
25
+ *
26
+ * Bare `!` stays acceptable for self-evident cases — e.g.
27
+ * `array.find(...)!` where the search predicate guarantees a hit
28
+ * by construction. Add a one-line comment if the guarantee
29
+ * isn't obvious from the surrounding three lines of code.
30
+ */
31
+ export declare function assertNonNull<T>(v: T | null | undefined, label: string): T;
@@ -0,0 +1,10 @@
1
+ export declare const JPEG_QUALITY = 0.9;
2
+ export declare const CAPTURE_DELAY_MS = 300;
3
+ export declare const MAX_CANVAS_DIMENSION = 32767;
4
+ export declare const DEFAULT_STROKE_COLOR = "#ff0000";
5
+ export declare const DEFAULT_FILL_COLOR = "none";
6
+ export declare const DEFAULT_STROKE_WIDTH = 3;
7
+ export declare const DEFAULT_FONT_SIZE = 24;
8
+ export declare const MOSAIC_BLOCK_SIZE = 10;
9
+ export declare const REDACT_BLUR_RADIUS = 12;
10
+ export declare const REDACT_SOLID_COLOR = "#111";
@@ -0,0 +1,6 @@
1
+ /** Dash pattern multipliers (relative to stroke-width), matching Office presets */
2
+ export declare const DASH_MULTIPLIERS: Record<string, number[]>;
3
+ /** Compute SVG stroke-dasharray from a dash key and stroke width */
4
+ export declare function computeDasharray(key: string, strokeWidth: number): string;
5
+ /** Detect dash key from an SVG dasharray string and stroke width */
6
+ export declare function detectDashKey(dasharray: string, strokeWidth: number): string;
@@ -0,0 +1,349 @@
1
+ /**
2
+ * Desktop IPC bridge — the renderer-side seam every desktop-host
3
+ * IPC call goes through.
4
+ *
5
+ * Production transport is Electron's
6
+ * `window.electronAPI.invoke(channel, args)` exposed by the
7
+ * preload script in `packages/desktop/src-electron/preload.ts`.
8
+ *
9
+ * **History**: This file's earlier life as `tauri-bridge.ts`
10
+ * supported a dual Electron / Tauri dispatch during the
11
+ * Phase 1–8 migration. Phase 9 of
12
+ * `docs/plans/_done/desktop-electron-migration.md` removed the
13
+ * Tauri sources + the dual-transport fallback; the bridge now
14
+ * speaks Electron only. The `isTauri` back-compat alias and the
15
+ * dynamic `@tauri-apps/api/core` import are gone in this PR
16
+ * cycle.
17
+ */
18
+ declare const isDesktop: boolean;
19
+ export { isDesktop };
20
+ /** Library root under the desktop host. Used by `bootstrap.ts`
21
+ * to construct `DesktopFs`. */
22
+ export declare function getLibraryRoot(): Promise<string>;
23
+ /** Portable data directory (extension-handoff sweep + legacy-
24
+ * data toast). */
25
+ export declare function getPortableDir(): Promise<string>;
26
+ /**
27
+ * Documentation type for the on-disk tool-preset shape. The
28
+ * canonical schema — which keys belong to which tool — lives in
29
+ * `packages/core/src/editor/tool-registry.ts` (`presetFields`
30
+ * arrays per tool) and `packages/core/src/editor/tool-preset-serde.ts`
31
+ * (`FIELD_TO_SNAKE` table). The fields enumerated here are the
32
+ * union of what every tool's `presetFields` resolves to today,
33
+ * pinned for IDE autocomplete + cross-language schema discoverability.
34
+ *
35
+ * History: this interface used to declare a NARROWER schema mirroring
36
+ * a Rust struct that named only six fields with `#[serde(default = …)]`
37
+ * — but the Rust struct silently dropped all other fields on save,
38
+ * orphaning every variant discriminator (shape_type / arrow_head /
39
+ * shape_kind / draw_style / redact_style / marker_shape /
40
+ * highlight_color), the per-end arrow shape / width / length, and
41
+ * stroke opacity / cap / join. The Rust struct is now schema-
42
+ * transparent (`HashMap<String, serde_yaml::Value>`) and the
43
+ * Phase 2 Electron port (`src-electron/ipc/settings.ts`) carries
44
+ * the same property — all keys round-trip through both hosts; this
45
+ * interface is re-aligned to enumerate the full set toolbar.ts
46
+ * emits, matching reality.
47
+ */
48
+ export interface ToolPreset {
49
+ stroke_color?: string;
50
+ fill_color?: string;
51
+ stroke_width?: number;
52
+ font_size?: number;
53
+ stroke_dasharray?: string;
54
+ fill_opacity?: number;
55
+ /** Subtype for the unified Shape tool (rect / rounded / ellipse). */
56
+ shape_type?: string;
57
+ /** Head variant for the unified Line/Arrow tool (none / end / both). */
58
+ arrow_head?: string;
59
+ /** Shape kind for text-bearing shapes (plain / sticky / callout). */
60
+ shape_kind?: string;
61
+ /** Font family CSS value for the Text tool. */
62
+ font_family?: string;
63
+ /** Style for the unified Draw tool (pen / highlighter). */
64
+ draw_style?: string;
65
+ /** Style for the unified Redact tool (mosaic / solid / blur). */
66
+ redact_style?: string;
67
+ /** Default arrow shapes (per end) — matches OOXML preset types. */
68
+ arrow_head_start?: string;
69
+ arrow_head_end?: string;
70
+ /** Default arrow width / length (per end) — sm / md / lg. */
71
+ arrow_width_start?: string;
72
+ arrow_width_end?: string;
73
+ arrow_length_start?: string;
74
+ arrow_length_end?: string;
75
+ /** Selected highlighter color for the Highlight tool. */
76
+ highlight_color?: string;
77
+ /** Shape for the Counter (Marker) tool — circle / rect / rounded. */
78
+ marker_shape?: string;
79
+ /** Stroke opacity / cap / join. */
80
+ stroke_opacity?: number;
81
+ stroke_linecap?: string;
82
+ stroke_linejoin?: string;
83
+ }
84
+ export interface ToolPresets {
85
+ /** Preset map. Keys are element keys ("shape.rect", "arrow.end")
86
+ * for tools with variants, or bare tool IDs ("crop", "highlight")
87
+ * for tools without variants. Legacy files (pre–per-variant
88
+ * refactor) may have bare tool IDs like "shape" / "arrow" — the
89
+ * loader migrates these to the tool's default variant on read. */
90
+ tools: Record<string, ToolPreset>;
91
+ /** Last-used variant per tool. Used to pick which variant's preset
92
+ * to activate when the user re-selects the tool. Optional — falls
93
+ * back to TOOL_VARIANTS[toolId].fallback when absent. */
94
+ last_variants?: Record<string, string>;
95
+ }
96
+ export declare function loadToolPresets(): Promise<ToolPresets>;
97
+ export declare function saveToolPresets(presets: ToolPresets): Promise<void>;
98
+ export interface AnnotMetadata {
99
+ original_image_b64: string;
100
+ annotations_svg: string;
101
+ width: number;
102
+ height: number;
103
+ }
104
+ export declare function saveWithXmp(renderedImageB64: string, originalImageB64: string, annotationsSvg: string, width: number, height: number, filePath: string): Promise<void>;
105
+ export declare function readXmp(filePath: string): Promise<AnnotMetadata | null>;
106
+ export interface CaptureResult {
107
+ data_url: string;
108
+ width: number;
109
+ height: number;
110
+ }
111
+ export interface WindowInfo {
112
+ /** `desktopCapturer` source id, an opaque string like
113
+ * `"window:1234:5"`. The renderer passes it through to
114
+ * `capture_window`. */
115
+ hwnd: string;
116
+ title: string;
117
+ class: string;
118
+ x: number;
119
+ y: number;
120
+ width: number;
121
+ height: number;
122
+ }
123
+ export declare function captureScreen(): Promise<CaptureResult>;
124
+ export declare function listWindows(): Promise<WindowInfo[]>;
125
+ export declare function captureWindow(hwnd: string): Promise<CaptureResult>;
126
+ export declare function captureRegion(x: number, y: number, width: number, height: number): Promise<CaptureResult>;
127
+ export declare function minimizeMainWindow(): Promise<void>;
128
+ export declare function restoreMainWindow(): Promise<void>;
129
+ /**
130
+ * Unified annotation-shape payload — the input to the shared
131
+ * OOXML DrawingML builder in `@ingcreators/annot-render`. Used by
132
+ * both the PPTX export path
133
+ * (`packages/editor/src/pptx-export.ts`, `ns: "p"`) and the
134
+ * Office-clipboard path
135
+ * (`packages/host-ui/src/toolbar.ts:#copyForOffice`,
136
+ * `ns: "a"`).
137
+ *
138
+ * The Electron-side handler
139
+ * (`packages/desktop/src-electron/ipc/clipboard.ts`) is
140
+ * packaging-only since
141
+ * [`office-paste-shared-drawing-builder` phase 3](../../../../docs/plans/_done/office-paste-shared-drawing-builder.md):
142
+ * the per-shape OOXML is built TS-side and passed to the host as
143
+ * a pre-assembled drawing XML string. The shape taxonomy below is
144
+ * what `svgElementToAnnotationShape` produces:
145
+ *
146
+ * type="rect" Rectangle. Use `corner_radius>0` for rounded
147
+ * variant. Use `redact_style="solid"` for an
148
+ * opaque solid-bar redaction (no outline).
149
+ * type="ellipse" Ellipse.
150
+ * type="line" | "arrow"
151
+ * Line. Use `arrow_shape_start / arrow_shape_end`
152
+ * to describe heads (`"none"` or undefined for
153
+ * no head; `"triangle"` / `"arrow"` / `"stealth"`
154
+ * / `"diamond"` / `"oval"` for the OOXML preset
155
+ * head shapes).
156
+ * type="text" Text-bearing shape. `shape_kind` is the
157
+ * discriminator — auto-bg variants (`plain`
158
+ * / `sticky` / `callout`) plus the text-on-
159
+ * shape kinds (`rect` / `rounded` /
160
+ * `ellipse`, see `isTextOnShape`).
161
+ * `runs[]` holds the per-run
162
+ * text content + per-character formatting
163
+ * (bold / italic / underline / mixed font /
164
+ * size / color); for a uniformly-styled
165
+ * textbox `runs` collapses to one entry per
166
+ * line with no formatting flags.
167
+ * `text_bg_color` carries the bg fill;
168
+ * `tail_x` / `tail_y` set for callout (the
169
+ * OOXML emit then uses `wedgeRoundRectCallout`).
170
+ * type="freehand" Freehand path. The SVG path d-string rides
171
+ * on `path_d`. Use `draw_style` for pen vs
172
+ * highlighter; `stroke_opacity_value` carries
173
+ * the semi-transparent highlighter alpha.
174
+ * type="mosaic_image" Mosaic-redaction PNG, embedded via data URL
175
+ * in `image_data_url`.
176
+ * type="blur_image" Blur-redaction PNG, same shape as mosaic_image.
177
+ * type="marker" Counter marker; `marker_shape` + `label`.
178
+ */
179
+ /** A single styled text run inside a text-bearing shape.
180
+ *
181
+ * On disk each run corresponds to one `<tspan>` child of the
182
+ * shape's `<text>` element. Transitions in any of the formatting
183
+ * fields split the source text into separate runs; runs adjacent
184
+ * within the same paragraph share an `<a:p>` on the OOXML side,
185
+ * while `line_break_after === true` ends the paragraph and starts
186
+ * a new `<a:p>` for the following run.
187
+ *
188
+ * Per-run formatting is OPTIONAL — when omitted, the run inherits
189
+ * the shape-level defaults (`font_size` / `font_family` / `fill`
190
+ * on the parent `AnnotationShape`). A uniformly-styled textbox
191
+ * therefore collapses to one run per line with only `text` +
192
+ * `line_break_after` populated.
193
+ */
194
+ export interface TextRun {
195
+ /** Plain text content of this run. Newlines are NOT permitted —
196
+ * use `line_break_after` to end a paragraph. */
197
+ text: string;
198
+ bold?: boolean;
199
+ italic?: boolean;
200
+ underline?: boolean;
201
+ /** Per-run font size override (px). Omit to inherit the shape-
202
+ * level `font_size`. */
203
+ font_size?: number;
204
+ /** Per-run font family override. */
205
+ font_family?: string;
206
+ /** Per-run text color (`#rrggbb`). Omit to inherit the shape-
207
+ * level `fill`. */
208
+ color?: string;
209
+ /** When true, ends the current paragraph after this run. The
210
+ * next run starts in a fresh `<a:p>` on the OOXML side. */
211
+ line_break_after?: boolean;
212
+ }
213
+ export interface AnnotationShape {
214
+ type: string;
215
+ x?: number;
216
+ y?: number;
217
+ width?: number;
218
+ height?: number;
219
+ x1?: number;
220
+ y1?: number;
221
+ x2?: number;
222
+ y2?: number;
223
+ cx?: number;
224
+ cy?: number;
225
+ rx?: number;
226
+ ry?: number;
227
+ stroke?: string;
228
+ stroke_width?: number;
229
+ stroke_dasharray?: string;
230
+ fill?: string;
231
+ fill_opacity?: number;
232
+ /** Corner radius. 0 (or omitted) = sharp; >0 = rounded rectangle. */
233
+ corner_radius?: number;
234
+ /** Quadratic-Bezier control-point coordinates for a curved
235
+ * arrow, in the same canvas space as `x1/y1/x2/y2`. Both
236
+ * populated together; either being absent renders the arrow
237
+ * as a straight `<a:prstGeom prst="line">` connector. When
238
+ * populated the shared builder swaps to `<a:custGeom>` with a
239
+ * `<a:moveTo>` + `<a:quadBezTo>` path so the curve survives
240
+ * the paste. */
241
+ arrow_curve_cx?: number;
242
+ arrow_curve_cy?: number;
243
+ /** One entry per `<tspan>` in the source SVG. Style transitions
244
+ * split runs; uniformly-styled text collapses to one entry per
245
+ * line with no formatting flags. The OOXML emit walks this
246
+ * array, opening a new `<a:p>` after each run with
247
+ * `line_break_after === true`, otherwise emitting the run as
248
+ * one `<a:r>` inside the current paragraph. */
249
+ runs?: TextRun[];
250
+ font_size?: number;
251
+ font_family?: string;
252
+ /** Discriminator for text-bearing shapes. Phase 1 supports the
253
+ * three text-variant values; Phase 3 adds `rect` / `rounded` /
254
+ * `ellipse` for text-on-shape. */
255
+ shape_kind?: "plain" | "sticky" | "callout" | "rect" | "rounded" | "ellipse";
256
+ /** Sticky / callout background color, in CSS `rgba(...)` or `#rrggbb`
257
+ * form. Populated for `shape_kind === "sticky" | "callout"`;
258
+ * omitted for plain text. */
259
+ text_bg_color?: string;
260
+ /** Horizontal alignment of the run block within the shape. Mirrors
261
+ * the wrapper's `data-text-anchor` attribute and flows through to
262
+ * OOXML as `<a:pPr algn="…">` per paragraph. Omitted = no
263
+ * per-paragraph alignment override (PowerPoint inherits from the
264
+ * paragraph default, i.e. left). */
265
+ text_anchor?: "start" | "middle" | "end";
266
+ /** Vertical alignment of the run block within the shape. Mirrors
267
+ * the wrapper's `data-text-vanchor` attribute and flows through
268
+ * to OOXML as `<a:bodyPr anchor="…">`. Omitted = top. */
269
+ text_vertical_anchor?: "top" | "middle" | "bottom";
270
+ /** Callout tail-tip coordinates (canvas space). */
271
+ tail_x?: number;
272
+ tail_y?: number;
273
+ /** SVG path d-string for freehand strokes. */
274
+ path_d?: string;
275
+ draw_style?: "pen" | "highlighter";
276
+ /** Discriminator for redactions. A type="rect" + redact_style="solid"
277
+ * means an opaque color bar; type="mosaic_image" / "blur_image"
278
+ * carry baked-in PNGs. */
279
+ redact_style?: "solid" | "mosaic" | "blur";
280
+ /** PNG data URL for mosaic / blur redactions (self-contained). */
281
+ image_data_url?: string;
282
+ label?: string;
283
+ /** Counter background shape. `rounded` is a newer variant — older
284
+ * Rust-side consumers that only know `circle`/`rect` will treat
285
+ * unknown values as circle (graceful degradation). */
286
+ marker_shape?: "circle" | "rect" | "rounded";
287
+ /** Rotation in degrees, CW positive, around the shape's bbox center.
288
+ * Omitted (or 0) means no rotation. */
289
+ rotation_deg?: number;
290
+ /** Mirrored along the horizontal axis (left/right swap). */
291
+ flip_h?: boolean;
292
+ /** Mirrored along the vertical axis (top/bottom swap). */
293
+ flip_v?: boolean;
294
+ /** Per-end arrow head shapes — matching OOXML's six preset types
295
+ * one-to-one. */
296
+ arrow_shape_start?: "none" | "arrow" | "triangle" | "stealth" | "diamond" | "oval";
297
+ arrow_shape_end?: "none" | "arrow" | "triangle" | "stealth" | "diamond" | "oval";
298
+ /** Per-dimension arrow widths (perpendicular to stem, OOXML `w`). */
299
+ arrow_width_start?: "sm" | "md" | "lg";
300
+ arrow_width_end?: "sm" | "md" | "lg";
301
+ /** Per-dimension arrow lengths (along stem, OOXML `len`). */
302
+ arrow_length_start?: "sm" | "md" | "lg";
303
+ arrow_length_end?: "sm" | "md" | "lg";
304
+ /** Stroke opacity (0..1). Emitted as `<a:alpha val="..."/>` inside
305
+ * the stroke's solidFill. */
306
+ stroke_opacity_value?: number;
307
+ /** SVG stroke-linecap. Translates to OOXML `<a:ln cap="rnd|sq|flat"/>`. */
308
+ stroke_linecap?: "butt" | "round" | "square";
309
+ /** SVG stroke-linejoin. Translates to `<a:miter/>|<a:round/>|<a:bevel/>`. */
310
+ stroke_linejoin?: "miter" | "round" | "bevel";
311
+ /** Serialized stroke gradient (JSON). Consumer translates into
312
+ * OOXML `<a:gradFill>` inside `<a:ln>`. */
313
+ stroke_gradient?: {
314
+ angle: number;
315
+ stops: Array<{
316
+ color: string;
317
+ offset: number;
318
+ opacity?: number;
319
+ }>;
320
+ };
321
+ /** Serialized fill gradient. */
322
+ fill_gradient?: {
323
+ angle: number;
324
+ stops: Array<{
325
+ color: string;
326
+ offset: number;
327
+ opacity?: number;
328
+ }>;
329
+ };
330
+ }
331
+ /** One mosaic / blur image embedded into the GVML clipboard
332
+ * package. The caller (TS-side `buildDrawingXml` consumer)
333
+ * passes pre-decoded bytes; the host packager writes them at
334
+ * `clipboard/media/{filename}` and binds them via the matching
335
+ * rId in the drawing rels. */
336
+ export interface MosaicMediaPayload {
337
+ filename: string;
338
+ /** Raw image bytes (PNG / JPEG). The IPC adapter JSON-serializes
339
+ * this as an array of numbers — fine for the typical mosaic
340
+ * payload size (a few KB per patch). */
341
+ bytes: Uint8Array;
342
+ }
343
+ /**
344
+ * Office-clipboard copy. The drawing XML + mosaic media list are
345
+ * pre-built on the TS side; the host's job is to ZIP them up
346
+ * alongside the screenshot + theme + content_types and push the
347
+ * result to the OS clipboard.
348
+ */
349
+ export declare function copyAsOffice(drawingXml: string, mosaicMedia: MosaicMediaPayload[], screenshotData?: string, pngDataUrl?: string): Promise<void>;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Default filename helpers.
3
+ *
4
+ * All annot-generated files share a single name shape:
5
+ *
6
+ * annot-YYYYMMDD-HHMMSS-SSS[.annot].<ext>
7
+ *
8
+ * Local-time stamping keeps the filename instantly readable for the
9
+ * user (Drive / GitHub / Finder all show the time the user actually
10
+ * captured the screen). The matching `createdAt` field on
11
+ * `ImageRecord` stores the UTC ms epoch separately, so cross-timezone
12
+ * sync semantics are unaffected.
13
+ *
14
+ * Millisecond precision avoids collisions inside the timed-page
15
+ * capture flow (`pwa-capture` issues frames at 1s+ intervals; even
16
+ * burst capture stays unique). For the cross-device GitHub / Drive
17
+ * sync edge case, the existing `uniquifyFilename(Async)` helper
18
+ * appends a numeric suffix — this format degrades cleanly into
19
+ * `annot-...-001`, etc.
20
+ */
21
+ /** Prefix every annot-native default filename with this token. */
22
+ export declare const ANNOT_FILENAME_PREFIX = "annot";
23
+ /**
24
+ * Format a Date as `YYYYMMDD-HHMMSS-SSS` in local time. Lexicographic
25
+ * order matches chronological order.
26
+ */
27
+ export declare function formatLocalTimestamp(date?: Date): string;
28
+ /**
29
+ * Default filename stem (no extension) for an annot-native capture,
30
+ * e.g. `annot-20260428-143022-123`.
31
+ */
32
+ export declare function defaultAnnotFilenameStem(date?: Date): string;
33
+ /**
34
+ * Default filename for an annot-native image capture, e.g.
35
+ * `annot-20260428-143022-123.annot.png`. The extension is inferred
36
+ * from the data URL prefix (`jpg` for `data:image/jpeg`, otherwise
37
+ * `png`). The `.annot.` infix marks the file as carrying embedded
38
+ * annotation metadata (XMP for PNG, EXIF for JPG) — used by every
39
+ * persistent web storage backend (BrowserStore, DeviceStore,
40
+ * GitHubStore, GoogleDriveStore). The transient extension IDB
41
+ * deliberately omits the infix; pre-annotation captures aren't
42
+ * annot-native yet.
43
+ *
44
+ * PNG-default for unknown inputs is intentional: every production
45
+ * caller passes PNG or JPEG, so the fallback only matters for tests
46
+ * and stray callers — and PNG (lossless) is the safer guess.
47
+ */
48
+ export declare function defaultAnnotImageFilename(originalDataUrl: string, date?: Date): string;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * UUIDv7 (RFC 9562) encoded in Base58 (Bitcoin alphabet).
3
+ *
4
+ * UUIDv7 layout (128 bits):
5
+ * - 48 bits: Unix timestamp in milliseconds (big-endian)
6
+ * - 4 bits : version = 0111
7
+ * - 12 bits: random (rand_a)
8
+ * - 2 bits : variant = 10
9
+ * - 62 bits: random (rand_b)
10
+ *
11
+ * Base58 uses the Bitcoin alphabet (omits 0, O, I, l) so the resulting
12
+ * 22-character string is URL-safe and free of visually ambiguous chars.
13
+ *
14
+ * The result is time-ordered: IDs minted later compare lexicographically
15
+ * greater, which is useful for stable filmstrip ordering.
16
+ */
17
+ /**
18
+ * Generate a new time-ordered unique ID as a ~22-character Base58 string.
19
+ * Uses UUIDv7 underneath.
20
+ */
21
+ export declare function newIdB58(): string;
@@ -0,0 +1,5 @@
1
+ export { assertNonNull } from './assert.js';
2
+ export { DEFAULT_FILL_COLOR, DEFAULT_FONT_SIZE, DEFAULT_STROKE_COLOR, DEFAULT_STROKE_WIDTH, JPEG_QUALITY, MOSAIC_BLOCK_SIZE, REDACT_BLUR_RADIUS, REDACT_SOLID_COLOR, } from './constants.js';
3
+ export { computeDasharray, detectDashKey } from './dash-utils.js';
4
+ export { ANNOT_FILENAME_PREFIX, defaultAnnotFilenameStem, defaultAnnotImageFilename, formatLocalTimestamp, } from './filename.js';
5
+ export { newIdB58 } from './id.js';
@@ -0,0 +1,20 @@
1
+ export interface CaptureRect {
2
+ x: number;
3
+ y: number;
4
+ width: number;
5
+ height: number;
6
+ }
7
+ export interface PageDimensions {
8
+ scrollWidth: number;
9
+ scrollHeight: number;
10
+ viewportWidth: number;
11
+ viewportHeight: number;
12
+ devicePixelRatio: number;
13
+ scrollX: number;
14
+ scrollY: number;
15
+ }
16
+ export interface CaptureSegment {
17
+ dataUrl: string;
18
+ offsetY: number;
19
+ }
20
+ export type CaptureMode = "visible" | "area" | "full";
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Browser-side XMP metadata embedding for re-editable images.
3
+ * Mirrors the Rust implementation in src-tauri/src/commands/xmp.rs
4
+ *
5
+ * JPEG: XMP in APP1 segment, original image in multiple APP2 segments
6
+ * PNG: XMP in iTXt chunk, original image in custom "svGo" chunk
7
+ */
8
+ export interface EditableImageOptions {
9
+ /** Rendered image (screenshot + annotations) as Blob */
10
+ renderedBlob: Blob;
11
+ /** Original capture image data URL (without annotations) */
12
+ originalDataUrl: string;
13
+ /** Annotations-only SVG string */
14
+ annotationsSvg: string;
15
+ /** Image dimensions */
16
+ width: number;
17
+ height: number;
18
+ /** Output format */
19
+ format: "jpg" | "png";
20
+ /** Key-value tags */
21
+ tags?: Record<string, string>;
22
+ }
23
+ /**
24
+ * Create a re-editable image with XMP metadata embedded.
25
+ * Returns a Blob ready for download.
26
+ */
27
+ export declare function createEditableImage(opts: EditableImageOptions): Promise<Blob>;
28
+ export interface AnnotMetadata {
29
+ originalImageDataUrl: string;
30
+ annotationsSvg: string;
31
+ width: number;
32
+ height: number;
33
+ tags: Record<string, string>;
34
+ }
35
+ /**
36
+ * Read XMP metadata from a re-editable image file (JPEG or PNG).
37
+ * Pass the file as an ArrayBuffer or Uint8Array.
38
+ */
39
+ export declare function readEditableImage(data: Uint8Array): AnnotMetadata | null;
@@ -0,0 +1,8 @@
1
+ import { buildZipBytes, ZipEntry } from './zip-bytes.js';
2
+ export type { ZipEntry };
3
+ export { buildZipBytes };
4
+ export declare function buildZip(entries: ZipEntry[]): Blob;
5
+ /** Convert a data URL to Uint8Array binary. */
6
+ export declare function dataUrlToBytes(dataUrl: string): Uint8Array;
7
+ /** Detect file extension from data URL mime type. */
8
+ export declare function dataUrlExt(dataUrl: string): string;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Tier-A ZIP builder — pure-bytes output, no DOM types.
3
+ *
4
+ * Originally lived in `zip-builder.ts` next to a `Blob`-returning
5
+ * companion. Phase 4 of `desktop-electron-migration.md` introduces
6
+ * a Node-side caller (`packages/desktop/src-electron/ipc/clipboard.ts`)
7
+ * whose `tsconfig.json` deliberately omits the DOM lib. Splitting
8
+ * out this module lets the Node-side import stay DOM-free; the
9
+ * browser-side `buildZip` continues to wrap `buildZipBytes` in a
10
+ * `Blob` for callers that want the browser-native shape.
11
+ *
12
+ * The output is a Stored-method (uncompressed) ZIP. Images are
13
+ * already JPEG/PNG-compressed so re-deflating is wasted CPU; the
14
+ * Office clipboard accepts both Stored and Deflated entries.
15
+ */
16
+ export interface ZipEntry {
17
+ /** Filename inside the ZIP (e.g. `"clipboard/drawings/drawing1.xml"`). */
18
+ name: string;
19
+ data: Uint8Array;
20
+ }
21
+ /** Build the ZIP and return raw bytes. */
22
+ export declare function buildZipBytes(entries: ZipEntry[]): Uint8Array;
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@ingcreators/annot-core",
3
+ "version": "0.1.0",
4
+ "description": "Annot SDK — SVG-first screenshot annotation format, storage abstractions, and editor primitives. The foundation shared by the PWA, Chrome extension, Electron desktop, VSCode extension, and the headless annotator.",
5
+ "license": "Apache-2.0",
6
+ "author": "ingcreators",
7
+ "homepage": "https://github.com/ingcreators/annot/tree/main/packages/core#readme",
8
+ "bugs": {
9
+ "url": "https://github.com/ingcreators/annot/issues"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/ingcreators/annot.git",
14
+ "directory": "packages/core"
15
+ },
16
+ "keywords": [
17
+ "annotation",
18
+ "screenshot",
19
+ "svg",
20
+ "editor",
21
+ "storage",
22
+ "headless",
23
+ "annot"
24
+ ],
25
+ "type": "module",
26
+ "main": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "default": "./dist/index.js"
32
+ },
33
+ "./styles/*": "./styles/*"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "styles",
38
+ "README.md",
39
+ "CHANGELOG.md"
40
+ ],
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "devDependencies": {
45
+ "@types/pako": "^2.0.4",
46
+ "typescript": "^6.0.3",
47
+ "vite": "^8.0.13",
48
+ "vite-plugin-dts": "^5.0.0",
49
+ "@ingcreators/annot-imagequant": "0.1.0"
50
+ },
51
+ "dependencies": {
52
+ "pako": "^2.1.0"
53
+ },
54
+ "scripts": {
55
+ "build": "vite build",
56
+ "typecheck": "tsc --noEmit"
57
+ }
58
+ }