@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.
- package/CHANGELOG.md +7 -0
- package/LICENSE +201 -0
- package/README.md +56 -0
- package/dist/auto-capture-options.d.ts +78 -0
- package/dist/editor/arrow-markers.d.ts +142 -0
- package/dist/editor/bake-translate.d.ts +192 -0
- package/dist/editor/font-registry.d.ts +58 -0
- package/dist/editor/gradient-utils.d.ts +23 -0
- package/dist/editor/history-core.d.ts +48 -0
- package/dist/editor/icons/brand-icons.d.ts +53 -0
- package/dist/editor/icons/material-symbols.d.ts +105 -0
- package/dist/editor/icons/registry.d.ts +179 -0
- package/dist/editor/icons/render.d.ts +22 -0
- package/dist/editor/icons/sanitize.d.ts +60 -0
- package/dist/editor/index.d.ts +13 -0
- package/dist/editor/path-utils.d.ts +32 -0
- package/dist/editor/property-schema.d.ts +263 -0
- package/dist/editor/rich-text-mapper.d.ts +8 -0
- package/dist/editor/selection-geometry.d.ts +66 -0
- package/dist/editor/shape-utils.d.ts +27 -0
- package/dist/editor/svg-format.d.ts +68 -0
- package/dist/editor/svg-id-utils.d.ts +37 -0
- package/dist/editor/svg-to-annotation-shapes.d.ts +34 -0
- package/dist/editor/text-utils.d.ts +212 -0
- package/dist/editor/tool-lifecycle.d.ts +56 -0
- package/dist/editor/tool-options.d.ts +126 -0
- package/dist/editor/tool-panel-adapter.d.ts +105 -0
- package/dist/editor/tool-preset-serde.d.ts +43 -0
- package/dist/editor/tool-registry.d.ts +320 -0
- package/dist/editor/tool-style-reader.d.ts +36 -0
- package/dist/editor/tool-style-writer.d.ts +33 -0
- package/dist/editor/toolbar-icons.d.ts +84 -0
- package/dist/editor/transform-utils.d.ts +127 -0
- package/dist/editor/viewport-math.d.ts +69 -0
- package/dist/encode/index.d.ts +4 -0
- package/dist/encode/options.d.ts +79 -0
- package/dist/encode/png8.d.ts +10 -0
- package/dist/headless.d.ts +29 -0
- package/dist/icons/index.d.ts +15 -0
- package/dist/icons/types.d.ts +108 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3164 -0
- package/dist/storage/errors.d.ts +59 -0
- package/dist/storage/index.d.ts +6 -0
- package/dist/storage/metadata-cache.d.ts +231 -0
- package/dist/storage/path.d.ts +49 -0
- package/dist/storage/thumbnail-cache.d.ts +110 -0
- package/dist/storage/thumbnail.d.ts +31 -0
- package/dist/storage/types.d.ts +677 -0
- package/dist/utils/assert.d.ts +31 -0
- package/dist/utils/constants.d.ts +10 -0
- package/dist/utils/dash-utils.d.ts +6 -0
- package/dist/utils/desktop-bridge.d.ts +349 -0
- package/dist/utils/filename.d.ts +48 -0
- package/dist/utils/id.d.ts +21 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/types.d.ts +20 -0
- package/dist/xmp/xmp-browser.d.ts +39 -0
- package/dist/zip/zip-builder.d.ts +8 -0
- package/dist/zip/zip-bytes.d.ts +22 -0
- package/package.json +58 -0
- package/styles/editor.css +1912 -0
- package/styles/fonts.css +46 -0
- package/styles/property-panel.css +779 -0
- 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
|
+
}
|