@ingcreators/annot-annotator 0.2.0 → 0.5.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 +109 -0
- package/README.md +1 -1
- package/dist/annotator.d.ts +67 -7
- package/dist/dsl/types.d.ts +43 -1
- package/dist/encode/encode.d.ts +37 -0
- package/dist/encode/options.d.ts +19 -0
- package/dist/encode/quantize.d.ts +17 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +949 -313
- package/package.json +7 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,114 @@
|
|
|
1
1
|
# @ingcreators/annot-annotator
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 806badc: Retire the `@ingcreators/annot-imagequant` (GPL-3.0) dynamic-import
|
|
8
|
+
boundary that gated PNG-8 output in the headless annotator and the
|
|
9
|
+
MCP server. `Annotator.toEncoded()`'s smart mode now routes PNG-8
|
|
10
|
+
through the pure-TS Median Cut + Floyd–Steinberg dither at
|
|
11
|
+
`@ingcreators/annot-core/encode/quantize-median-cut` directly.
|
|
12
|
+
|
|
13
|
+
### Removed public API
|
|
14
|
+
- `isImagequantAvailable()` is no longer exported from
|
|
15
|
+
`@ingcreators/annot-annotator`. PNG-8 is now unconditionally
|
|
16
|
+
available — callers that previously gated `format: "smart"` on
|
|
17
|
+
this can drop the check.
|
|
18
|
+
- `Annotator.toEncoded()` no longer emits
|
|
19
|
+
`EncodeResult.reason === "imagequant-missing"`. The
|
|
20
|
+
graceful-PNG-32-fallback path that produced this reason is
|
|
21
|
+
unreachable.
|
|
22
|
+
|
|
23
|
+
### Removed dependency
|
|
24
|
+
- `@ingcreators/annot-imagequant` is dropped from
|
|
25
|
+
`@ingcreators/annot-annotator`'s `dependencies`. Consumers
|
|
26
|
+
that previously installed it as a side-effect of installing
|
|
27
|
+
`annot-annotator` will save the WASM payload from their
|
|
28
|
+
`node_modules`. `annot-mcp` inherits the removal transitively.
|
|
29
|
+
|
|
30
|
+
Phase 3 of `docs/plans/replace-libimagequant-with-median-cut.md`.
|
|
31
|
+
Phase 4 deletes the `@ingcreators/annot-imagequant` workspace
|
|
32
|
+
package and deprecates the published 0.1.0 on npm.
|
|
33
|
+
|
|
34
|
+
- df1a429: **`@ingcreators/annot-annotator` — new `Annotator.toEditablePng()`
|
|
35
|
+
method** that returns a re-editable PNG. The bytes carry the same
|
|
36
|
+
visible pixels as `toPng()` plus the original un-annotated capture +
|
|
37
|
+
the annotations SVG embedded in the PNG's XMP / custom `svGo` chunk.
|
|
38
|
+
Re-opening the file in the Annot editor (or `annot.work/app/`)
|
|
39
|
+
restores the annotations as selectable / movable / restylable
|
|
40
|
+
objects rather than a flat bitmap.
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
const annotator = createAnnotator();
|
|
44
|
+
const editablePng = annotator.toEditablePng({
|
|
45
|
+
originalDataUrl,
|
|
46
|
+
annotationsSvg,
|
|
47
|
+
width,
|
|
48
|
+
height,
|
|
49
|
+
tags: {
|
|
50
|
+
source: "playwright-fixture",
|
|
51
|
+
capturedAt: new Date().toISOString(),
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
await writeFile("shot.png", editablePng);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Image viewers that don't know about the custom chunks display the
|
|
58
|
+
rasterised pixels verbatim — no compatibility loss vs `toPng()`.
|
|
59
|
+
|
|
60
|
+
The existing `toPng()` / `toSvg()` / `toEncoded()` methods are
|
|
61
|
+
unchanged — `toEditablePng()` is purely additive.
|
|
62
|
+
|
|
63
|
+
**`@ingcreators/annot-core` — new `/xmp-bytes` Tier-A subpath**
|
|
64
|
+
exposing the pure-bytes XMP encode / decode primitives that used to
|
|
65
|
+
live (Blob-wrapped) inside `/xmp`:
|
|
66
|
+
- `createEditablePngBytes(opts) -> Uint8Array` — write a re-editable
|
|
67
|
+
PNG. Takes raw PNG bytes for both the rasterised image and the
|
|
68
|
+
original capture; no `Blob` / `FileReader` dependency. The
|
|
69
|
+
function the new `Annotator.toEditablePng()` is built on.
|
|
70
|
+
- `readEditablePngBytes(data) -> AnnotMetadata | null` — PNG-only
|
|
71
|
+
reader.
|
|
72
|
+
- `readEditableImage(data) -> AnnotMetadata | null` — dual PNG /
|
|
73
|
+
JPEG reader (moved here from `/xmp`, also re-exported from `/xmp`
|
|
74
|
+
for source-compat).
|
|
75
|
+
- `WELL_KNOWN_TAG_KEYS` — soft-convention key names for the
|
|
76
|
+
optional `tags` field (`source` / `screen` / `capturedAt` /
|
|
77
|
+
`commit`).
|
|
78
|
+
|
|
79
|
+
Existing `@ingcreators/annot-core/xmp` consumers stay working
|
|
80
|
+
without source changes — `xmp-browser.ts` re-exports the Tier-A
|
|
81
|
+
surface alongside its Blob-wrapped `createEditableImage`.
|
|
82
|
+
|
|
83
|
+
## 0.3.0
|
|
84
|
+
|
|
85
|
+
### Minor Changes
|
|
86
|
+
|
|
87
|
+
- 86d0853: `Annotator.toEncoded(input, encodeOptions?)` — new async method that combines rasterise + smart-encode in one step. Achieves feature parity with Chrome extension's "Save size" + "Format smart" capture options for the manual-creation / agent use cases.
|
|
88
|
+
|
|
89
|
+
**EncodeOptions** (re-exported from `@ingcreators/annot-core/encode/options`):
|
|
90
|
+
- `format: "smart" | "png" | "jpeg"` — Smart picks PNG-8 for UI-heavy content (via libimagequant) and JPEG / PNG-32 for photo-heavy content per `smartFallback`. PNG / JPEG paths skip the smart heuristic.
|
|
91
|
+
- `saveSizePreset: "light" | "standard" | "highQuality" | "original"` — Max-width caps at 1280 / 1920 / 2560 / no-resize. Aspect-preserving; never upscales.
|
|
92
|
+
- `smartFallback: "png" | "jpeg"` — Photo-heavy fallback format.
|
|
93
|
+
- `smartColorThreshold: number` — Unique-colour count threshold (default 15000). Above this the image is treated as photo-heavy.
|
|
94
|
+
- `jpegPercent: number` — JPEG quality 60–100 (default 92).
|
|
95
|
+
|
|
96
|
+
Returns `{ bytes, chosen, reason?, width, height }` so callers can log which format was actually chosen (`"png-8"` / `"photo-fallback-jpeg"` / `"imagequant-missing"` etc.).
|
|
97
|
+
|
|
98
|
+
Standalone `encodeRgba(rgba, width, height, options)` also exported for callers who already have raw RGBA bytes (e.g. from a Playwright screenshot fed through their own canvas).
|
|
99
|
+
|
|
100
|
+
**New runtime dependencies:**
|
|
101
|
+
- `@napi-rs/canvas` (regular `dependencies`) — native canvas binding for PNG / JPEG encoding + resize. ~20 MB platform-matched binary at install time.
|
|
102
|
+
- `@ingcreators/annot-imagequant` (regular `dependencies`) — GPL-3.0 WASM wrapper around libimagequant for PNG-8 quantization. Loaded via dynamic import; consumers who explicitly uninstall the package to avoid the GPL inclusion get a graceful fallback to PNG-32 (`reason: "imagequant-missing"`).
|
|
103
|
+
- `pako` (regular `dependencies`) — pulled in transitively via the PNG-8 encoder; declared explicitly so consumer installs are deterministic.
|
|
104
|
+
|
|
105
|
+
The existing `toPng()` and `toSvg()` methods are unchanged — `toEncoded()` is purely additive.
|
|
106
|
+
|
|
107
|
+
### Patch Changes
|
|
108
|
+
|
|
109
|
+
- Updated dependencies [adae49d]
|
|
110
|
+
- @ingcreators/annot-imagequant@0.1.0
|
|
111
|
+
|
|
3
112
|
## 0.2.0
|
|
4
113
|
|
|
5
114
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -129,7 +129,7 @@ See also:
|
|
|
129
129
|
- [`docs/plans/_done/headless-annotator-spike.md`](../../docs/plans/_done/headless-annotator-spike.md)
|
|
130
130
|
— Phase 0 feasibility report (`@resvg/resvg-js` vs
|
|
131
131
|
`@napi-rs/canvas` trade-offs; documented gaps).
|
|
132
|
-
- [`docs/plans/annot-annotator-package.md`](../../docs/plans/annot-annotator-package.md)
|
|
132
|
+
- [`docs/plans/_done/annot-annotator-package.md`](../../docs/plans/_done/annot-annotator-package.md)
|
|
133
133
|
— Phase 1 design + rationale for the public API shape.
|
|
134
134
|
- [`PRODUCT_DIRECTION.md`](../../PRODUCT_DIRECTION.md) — strategic
|
|
135
135
|
context (Playwright + GitHub vectors).
|
package/dist/annotator.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { EncodeOptions } from '@ingcreators/annot-core/encode/options';
|
|
2
|
+
import { EncodeResult } from './encode/options.js';
|
|
1
3
|
/**
|
|
2
4
|
* Structural shape the annotator accepts. A real `ImageRecord`
|
|
3
5
|
* from `@ingcreators/annot-core/storage` satisfies this — the
|
|
@@ -18,6 +20,22 @@ export interface AnnotatorInput {
|
|
|
18
20
|
/** Output PNG height in pixels — should match the source bitmap. */
|
|
19
21
|
height: number;
|
|
20
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Input for {@link Annotator.toEditablePng}. Same shape as
|
|
25
|
+
* {@link AnnotatorInput} plus optional opaque kv `tags` written into
|
|
26
|
+
* the embedded XMP for provenance / debugging.
|
|
27
|
+
*
|
|
28
|
+
* Soft-convention key names (not validated by the writer; documented
|
|
29
|
+
* in `@ingcreators/annot-core/xmp-bytes`'s `WELL_KNOWN_TAG_KEYS`):
|
|
30
|
+
* - `source` — what produced the PNG (`"docs-tour"`, `"playwright-fixture"`, `"annot-mcp"`).
|
|
31
|
+
* - `screen` — for living-product-docs, the `<Screen id>` value.
|
|
32
|
+
* - `capturedAt` — ISO timestamp.
|
|
33
|
+
* - `commit` — git SHA when applicable.
|
|
34
|
+
*/
|
|
35
|
+
export interface EditableInput extends AnnotatorInput {
|
|
36
|
+
/** Optional opaque kv tags. */
|
|
37
|
+
tags?: Record<string, string>;
|
|
38
|
+
}
|
|
21
39
|
/** Annotator construction options. All optional. */
|
|
22
40
|
export interface AnnotatorOptions {
|
|
23
41
|
/**
|
|
@@ -47,16 +65,27 @@ export interface AnnotatorOptions {
|
|
|
47
65
|
defaultFontFamily?: string;
|
|
48
66
|
}
|
|
49
67
|
/**
|
|
50
|
-
* Annotator instance returned by {@link createAnnotator}.
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
68
|
+
* Annotator instance returned by {@link createAnnotator}.
|
|
69
|
+
*
|
|
70
|
+
* - {@link toPng} synchronous rasterise → PNG-32 bytes.
|
|
71
|
+
* - {@link toSvg} build the rasterise-ready SVG string (no
|
|
72
|
+
* rasterisation).
|
|
73
|
+
* - {@link toEncoded} (since 0.3.0) async rasterise + smart
|
|
74
|
+
* encode pipeline — `saveSizePreset` resize,
|
|
75
|
+
* `format: "smart" | "png" | "jpeg"` decision
|
|
76
|
+
* tree, PNG-8 quantization via the in-tree
|
|
77
|
+
* pure-TS Median Cut + Floyd–Steinberg dither
|
|
78
|
+
* (post-Phase 3 of
|
|
79
|
+
* `docs/plans/_done/replace-libimagequant-with-median-cut.md`;
|
|
80
|
+
* prior versions used the GPL-3.0
|
|
81
|
+
* `@ingcreators/annot-imagequant` WASM).
|
|
54
82
|
*/
|
|
55
83
|
export interface Annotator {
|
|
56
84
|
/**
|
|
57
|
-
* Rasterise the input to a PNG byte array.
|
|
58
|
-
*
|
|
59
|
-
*
|
|
85
|
+
* Rasterise the input to a PNG-32 byte array. Synchronous, no
|
|
86
|
+
* resize, no smart format selection — the most direct path.
|
|
87
|
+
* Use {@link toEncoded} when you need `saveSize` / format
|
|
88
|
+
* decisions.
|
|
60
89
|
*/
|
|
61
90
|
toPng(input: AnnotatorInput): Uint8Array;
|
|
62
91
|
/**
|
|
@@ -65,6 +94,37 @@ export interface Annotator {
|
|
|
65
94
|
* the caller can feed to any other SVG tool.
|
|
66
95
|
*/
|
|
67
96
|
toSvg(input: AnnotatorInput): string;
|
|
97
|
+
/**
|
|
98
|
+
* Rasterise the input to a re-editable PNG. Same visible pixels as
|
|
99
|
+
* {@link toPng}, plus the original un-annotated capture + the
|
|
100
|
+
* annotations SVG are embedded in the PNG's XMP metadata + custom
|
|
101
|
+
* `svGo` chunk. Re-opening the resulting file in the Annot editor
|
|
102
|
+
* (or `annot.work/app/`) restores the annotations as selectable,
|
|
103
|
+
* movable, restylable objects rather than a flat bitmap.
|
|
104
|
+
*
|
|
105
|
+
* Image viewers that don't know about the custom chunks display the
|
|
106
|
+
* rasterised pixels verbatim — no compatibility loss vs `toPng`.
|
|
107
|
+
*
|
|
108
|
+
* Synchronous. Returns PNG bytes as a `Uint8Array`.
|
|
109
|
+
*/
|
|
110
|
+
toEditablePng(input: EditableInput): Uint8Array;
|
|
111
|
+
/**
|
|
112
|
+
* Rasterise + encode in one step. Honours `saveSizePreset`
|
|
113
|
+
* (max-width resize), `format` (`"smart"` / `"png"` /
|
|
114
|
+
* `"jpeg"`), and `jpegPercent`. Returns the encoded bytes plus
|
|
115
|
+
* a metadata record so the caller can log which format was
|
|
116
|
+
* actually picked.
|
|
117
|
+
*
|
|
118
|
+
* Smart mode emits PNG-8 (via the in-tree Median Cut +
|
|
119
|
+
* Floyd–Steinberg dither at
|
|
120
|
+
* `@ingcreators/annot-core/encode/quantize-median-cut`) for
|
|
121
|
+
* UI-heavy content, falling back to PNG-32 / JPEG for
|
|
122
|
+
* photo-heavy content per `smartFallback`. PNG-8 is
|
|
123
|
+
* unconditionally available since Phase 3 of
|
|
124
|
+
* `docs/plans/_done/replace-libimagequant-with-median-cut.md`
|
|
125
|
+
* retired the optional GPL-3.0 imagequant WASM dependency.
|
|
126
|
+
*/
|
|
127
|
+
toEncoded(input: AnnotatorInput, encodeOptions?: EncodeOptions): Promise<EncodeResult>;
|
|
68
128
|
}
|
|
69
129
|
/**
|
|
70
130
|
* Construct a headless annotator. Options are forwarded to the
|
package/dist/dsl/types.d.ts
CHANGED
|
@@ -52,11 +52,53 @@ export type BboxCalloutAnnotation = AnnotationStyle & {
|
|
|
52
52
|
targetBbox: BBox;
|
|
53
53
|
content: string;
|
|
54
54
|
};
|
|
55
|
+
/**
|
|
56
|
+
* Which corner of the target bbox the badge sits at. The badge
|
|
57
|
+
* is centred on the corner — half inside the rect, half outside —
|
|
58
|
+
* so the legend number remains readable against either light or
|
|
59
|
+
* dark target content. `"auto"` (the default) picks the corner
|
|
60
|
+
* that's furthest from the supplied image edge so the badge
|
|
61
|
+
* never clips off the screenshot.
|
|
62
|
+
*/
|
|
63
|
+
export type BadgePlacement = "auto" | "topLeft" | "topRight" | "bottomLeft" | "bottomRight";
|
|
64
|
+
/**
|
|
65
|
+
* Numbered legend badge — target rect outline plus a filled
|
|
66
|
+
* intent-coloured circle at one corner with a bold white number
|
|
67
|
+
* inside. The visual idiom for "this is item N in a step-by-step
|
|
68
|
+
* legend over a screenshot."
|
|
69
|
+
*
|
|
70
|
+
* Differs from `callout` in two ways:
|
|
71
|
+
* 1. No caption arrow — the badge sits ON the target, not next to it.
|
|
72
|
+
* 2. The number renders inside a sized circle, not as bare `<text>`,
|
|
73
|
+
* so it stays readable when the screenshot is scaled down in
|
|
74
|
+
* docs / slides.
|
|
75
|
+
*
|
|
76
|
+
* When `imageWidth` / `imageHeight` are supplied alongside
|
|
77
|
+
* `placement: "auto"`, the renderer picks the corner furthest
|
|
78
|
+
* from the image edge so the badge never clips. Without those,
|
|
79
|
+
* `"auto"` falls back to `"topRight"`.
|
|
80
|
+
*/
|
|
81
|
+
export type BboxNumberedBadgeAnnotation = AnnotationStyle & {
|
|
82
|
+
type: "numberedBadge";
|
|
83
|
+
bbox: BBox;
|
|
84
|
+
number: number;
|
|
85
|
+
/** Override the corner. Default `"auto"`. */
|
|
86
|
+
placement?: BadgePlacement;
|
|
87
|
+
/** Badge diameter in image pixels. Default `40`. */
|
|
88
|
+
badgeSize?: number;
|
|
89
|
+
/**
|
|
90
|
+
* Image dimensions in page pixels — used by `placement: "auto"`
|
|
91
|
+
* to pick the corner furthest from the image edge. When omitted,
|
|
92
|
+
* `"auto"` resolves to `"topRight"`.
|
|
93
|
+
*/
|
|
94
|
+
imageWidth?: number;
|
|
95
|
+
imageHeight?: number;
|
|
96
|
+
};
|
|
55
97
|
export interface RawAnnotation {
|
|
56
98
|
type: "raw";
|
|
57
99
|
svgFragment: string;
|
|
58
100
|
}
|
|
59
|
-
export type BboxAnnotation = BboxRectAnnotation | BboxCircleAnnotation | BboxArrowAnnotation | BboxTextAnnotation | BboxCalloutAnnotation | RawAnnotation;
|
|
101
|
+
export type BboxAnnotation = BboxRectAnnotation | BboxCircleAnnotation | BboxArrowAnnotation | BboxTextAnnotation | BboxCalloutAnnotation | BboxNumberedBadgeAnnotation | RawAnnotation;
|
|
60
102
|
export type RedactStyle = "solid" | "mosaic" | "blur";
|
|
61
103
|
export interface BboxRedactRegion {
|
|
62
104
|
bbox: BBox;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { EncodeOptions } from '@ingcreators/annot-core/encode/options';
|
|
2
|
+
import { EncodeResult } from './options.js';
|
|
3
|
+
/**
|
|
4
|
+
* Encode raw RGBA pixels per the requested {@link EncodeOptions}.
|
|
5
|
+
*
|
|
6
|
+
* Algorithm (mirrors the browser-side pipeline):
|
|
7
|
+
*
|
|
8
|
+
* 1. Resize the source via `saveSizePreset` (aspect-preserving;
|
|
9
|
+
* never upscale).
|
|
10
|
+
* 2. Branch on `format`:
|
|
11
|
+
* - `"png"` → PNG-32 encode (lossless).
|
|
12
|
+
* - `"jpeg"` → JPEG encode at `jpegPercent`.
|
|
13
|
+
* - `"smart"`:
|
|
14
|
+
* - If pixel count > `MAX_SMART_PIXELS`, return PNG-32
|
|
15
|
+
* with `reason: "too-large-for-png8"`.
|
|
16
|
+
* - Sample unique-colour count via {@link isPhotoHeavy}.
|
|
17
|
+
* If photo-heavy, emit `smartFallback` (PNG-32 or
|
|
18
|
+
* JPEG) with reason `"photo-fallback-*"`.
|
|
19
|
+
* - Otherwise quantize to ≤256 colours via the
|
|
20
|
+
* in-tree Median Cut + FS dither and emit PNG-8 with
|
|
21
|
+
* reason `"png-8"`. PNG-8 is unconditionally
|
|
22
|
+
* available post-Phase 3 (no optional WASM gate).
|
|
23
|
+
*/
|
|
24
|
+
export declare function encodeRgba(rgba: Uint8Array, width: number, height: number, options?: EncodeOptions): Promise<EncodeResult>;
|
|
25
|
+
/**
|
|
26
|
+
* Decode a PNG / JPEG byte stream into RGBA and re-encode it
|
|
27
|
+
* through {@link encodeRgba}. Useful for downstream pipelines
|
|
28
|
+
* that emit a PNG first (e.g. the redact-burn output or a
|
|
29
|
+
* Playwright `page.screenshot()` capture) and want to apply
|
|
30
|
+
* `saveSize` / `format` after the fact.
|
|
31
|
+
*
|
|
32
|
+
* The decode goes through `@napi-rs/canvas`'s `loadImage`, so
|
|
33
|
+
* any format the underlying Skia decoder understands (PNG /
|
|
34
|
+
* JPEG / WebP / AVIF on the canvas's supported list) flows
|
|
35
|
+
* through transparently.
|
|
36
|
+
*/
|
|
37
|
+
export declare function decodeAndEncodeImage(imageBytes: Uint8Array, options?: EncodeOptions): Promise<EncodeResult>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type { EncodeFormat, EncodeOptions, EncodeResult as BrowserEncodeResult, SaveSizePreset, } from '@ingcreators/annot-core/encode/options';
|
|
2
|
+
export { computeResizeTarget, DEFAULT_ENCODE_OPTIONS, SAVE_SIZE_LABEL, SAVE_SIZE_MAX_WIDTH, } from '@ingcreators/annot-core/encode/options';
|
|
3
|
+
/**
|
|
4
|
+
* Node-side encode result. Returns raw bytes (vs the browser-side
|
|
5
|
+
* counterpart returning a `data:` URL string) — agents and test
|
|
6
|
+
* runtimes typically want bytes to attach to GitHub issues / test
|
|
7
|
+
* reports / write to disk.
|
|
8
|
+
*/
|
|
9
|
+
export interface EncodeResult {
|
|
10
|
+
/** Encoded image bytes (PNG-8 / PNG-32 / JPEG depending on `chosen`). */
|
|
11
|
+
bytes: Uint8Array;
|
|
12
|
+
/** Actual format chosen (may differ from requested in smart mode). */
|
|
13
|
+
chosen: "png" | "jpeg";
|
|
14
|
+
/** Human-readable note (`"png-8"` / `"photo-fallback-jpeg"` / …). */
|
|
15
|
+
reason?: string;
|
|
16
|
+
/** Final pixel dimensions after `saveSizePreset` resize. */
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quantize RGBA pixels to ≤256 colours via Median Cut + Floyd–
|
|
3
|
+
* Steinberg dither and emit a PNG-8 file.
|
|
4
|
+
*
|
|
5
|
+
* Synchronous, deterministic, GPL-free.
|
|
6
|
+
*/
|
|
7
|
+
export declare function quantizeRgbaToPng8(rgba: Uint8Array, width: number, height: number): Uint8Array;
|
|
8
|
+
/**
|
|
9
|
+
* Heuristic: does this image look photo-heavy?
|
|
10
|
+
*
|
|
11
|
+
* Samples ~50,000 pixels from the RGBA buffer (stride-walks rather
|
|
12
|
+
* than reading every pixel — sufficient for the bimodal "UI vs
|
|
13
|
+
* photo" decision). Returns `true` if the unique-color count
|
|
14
|
+
* exceeds `threshold`. Typical UI screenshots return <5,000 unique
|
|
15
|
+
* colours; pages with rich photography return >20,000.
|
|
16
|
+
*/
|
|
17
|
+
export declare function isPhotoHeavy(rgba: Uint8Array, threshold: number): boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
export { type Annotator, type AnnotatorInput, type AnnotatorOptions, createAnnotator, } from './annotator.js';
|
|
1
|
+
export { type Annotator, type AnnotatorInput, type AnnotatorOptions, createAnnotator, type EditableInput, } from './annotator.js';
|
|
2
|
+
export { decodeAndEncodeImage, encodeRgba } from './encode/encode.js';
|
|
3
|
+
export { type BrowserEncodeResult, computeResizeTarget, DEFAULT_ENCODE_OPTIONS, type EncodeFormat, type EncodeOptions, type EncodeResult, SAVE_SIZE_LABEL, SAVE_SIZE_MAX_WIDTH, type SaveSizePreset, } from './encode/options.js';
|
|
4
|
+
export { isPhotoHeavy } from './encode/quantize.js';
|
|
2
5
|
export { BBOX_ANNOTATION_SCHEMA, BBOX_REDACT_REGION_SCHEMA, SHARED_DEFS, } from './dsl/schema.js';
|
|
3
6
|
export { type ArrowOptions, arrowBetween, type BoundingBox, type RectOptions, rectForBoundingBox, type TextOptions, textAt, } from './dsl/svg-primitives.js';
|
|
4
7
|
export { bboxAnnotationsToSvg } from './dsl/to-svg.js';
|
|
5
|
-
export type { AnnotationStyle, BBox, BboxAnnotation, BboxArrowAnnotation, BboxCalloutAnnotation, BboxCircleAnnotation, BboxRectAnnotation, BboxRedactRegion, BboxTextAnnotation, Intent, Point, RawAnnotation, RedactStyle, } from './dsl/types.js';
|
|
8
|
+
export type { AnnotationStyle, BadgePlacement, BBox, BboxAnnotation, BboxArrowAnnotation, BboxCalloutAnnotation, BboxCircleAnnotation, BboxNumberedBadgeAnnotation, BboxRectAnnotation, BboxRedactRegion, BboxTextAnnotation, Intent, Point, RawAnnotation, RedactStyle, } from './dsl/types.js';
|