@scrawl-board/board 0.1.0-beta.0 → 0.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.d.ts +49 -7
- package/dist/browser.js +250 -99
- package/dist/core.d.ts +96 -25
- package/dist/core.js +63 -23
- package/dist/index.d.ts +118 -30
- package/dist/index.js +335 -111
- package/dist/react.d.ts +95 -7
- package/dist/react.js +326 -110
- package/package.json +1 -1
package/dist/browser.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/core
|
|
1
|
+
//#region src/core/shapes/assets.ts
|
|
2
2
|
var ASSET_REF_PATTERN = /^asset:[a-z0-9](?:[a-z0-9.-]{0,61}[a-z0-9])?:[A-Za-z0-9._~-]+$/;
|
|
3
3
|
function isAssetRef(value) {
|
|
4
4
|
return typeof value === "string" && ASSET_REF_PATTERN.test(value) && new TextEncoder().encode(value).length <= 512;
|
|
@@ -20,12 +20,17 @@ var AssetResolutionError = class extends Error {
|
|
|
20
20
|
var ASSET_CACHE_BYTES_DEFAULT = 256 * 1024 * 1024;
|
|
21
21
|
var ASSET_CACHE_BYTES_MIN = 64 * 1024 * 1024;
|
|
22
22
|
var ASSET_CACHE_BYTES_MAX = 512 * 1024 * 1024;
|
|
23
|
+
var SUPPORTED_ASSET_MEDIA_TYPES = [
|
|
24
|
+
"image/png",
|
|
25
|
+
"image/jpeg",
|
|
26
|
+
"image/webp"
|
|
27
|
+
];
|
|
23
28
|
function clampAssetCacheBytes(value) {
|
|
24
29
|
if (value === void 0 || !Number.isFinite(value)) return ASSET_CACHE_BYTES_DEFAULT;
|
|
25
30
|
return Math.min(ASSET_CACHE_BYTES_MAX, Math.max(ASSET_CACHE_BYTES_MIN, Math.floor(value)));
|
|
26
31
|
}
|
|
27
32
|
//#endregion
|
|
28
|
-
//#region src/core
|
|
33
|
+
//#region src/core/document/identifiers.ts
|
|
29
34
|
function documentId(value) {
|
|
30
35
|
if (!value) throw new TypeError("DocumentId cannot be empty");
|
|
31
36
|
return value;
|
|
@@ -226,7 +231,7 @@ function validation(message, path) {
|
|
|
226
231
|
return new DocumentRecoveryError("DOCUMENT_VALIDATION_FAILED", message, void 0, path);
|
|
227
232
|
}
|
|
228
233
|
//#endregion
|
|
229
|
-
//#region src/core
|
|
234
|
+
//#region src/core/shapes/affine2d.ts
|
|
230
235
|
var IDENTITY = [
|
|
231
236
|
1,
|
|
232
237
|
0,
|
|
@@ -310,7 +315,7 @@ function avgScale(m) {
|
|
|
310
315
|
return (Math.hypot(m[0], m[1]) + Math.hypot(m[2], m[3])) / 2;
|
|
311
316
|
}
|
|
312
317
|
//#endregion
|
|
313
|
-
//#region src/core
|
|
318
|
+
//#region src/core/document/boardSearch.ts
|
|
314
319
|
function hay(s) {
|
|
315
320
|
return s.toLowerCase();
|
|
316
321
|
}
|
|
@@ -375,7 +380,7 @@ function searchBoard(query, board) {
|
|
|
375
380
|
return hits;
|
|
376
381
|
}
|
|
377
382
|
//#endregion
|
|
378
|
-
//#region src/core
|
|
383
|
+
//#region src/core/shapes/types.ts
|
|
379
384
|
/** Erasure level at which a point counts as fully erased. */
|
|
380
385
|
var ERASE_THRESHOLD = .95;
|
|
381
386
|
function cloneStroke(stroke) {
|
|
@@ -442,13 +447,12 @@ function measureTable(table) {
|
|
|
442
447
|
height: table.rowHeights.reduce((sum, h) => sum + h, 0)
|
|
443
448
|
};
|
|
444
449
|
}
|
|
445
|
-
var BOARD_COLOR = "#FFFFFF";
|
|
446
450
|
var FOG_COLOR = "#FFFFFF";
|
|
447
451
|
function cloneImage(img) {
|
|
448
452
|
return { ...img };
|
|
449
453
|
}
|
|
450
454
|
//#endregion
|
|
451
|
-
//#region src/core
|
|
455
|
+
//#region src/core/document/clusterStore.ts
|
|
452
456
|
/** A stroke joins a cluster last written to within this window at full reach. */
|
|
453
457
|
var RECENT_MS = 8e3;
|
|
454
458
|
/** Older clusters are still joinable, at half the gap threshold. */
|
|
@@ -619,7 +623,7 @@ function clamp$2(v, lo, hi) {
|
|
|
619
623
|
return Math.min(hi, Math.max(lo, v));
|
|
620
624
|
}
|
|
621
625
|
//#endregion
|
|
622
|
-
//#region src/core
|
|
626
|
+
//#region src/core/shapes/extensionTypes.ts
|
|
623
627
|
function cloneCustomObject(object) {
|
|
624
628
|
return {
|
|
625
629
|
...object,
|
|
@@ -629,7 +633,7 @@ function cloneCustomObject(object) {
|
|
|
629
633
|
};
|
|
630
634
|
}
|
|
631
635
|
//#endregion
|
|
632
|
-
//#region src/core
|
|
636
|
+
//#region src/core/shapes/kitchenTimer.ts
|
|
633
637
|
var TIMER_DEFAULT_DURATION_MS = 300 * 1e3;
|
|
634
638
|
function cloneTimer(timer) {
|
|
635
639
|
return { ...timer };
|
|
@@ -679,7 +683,7 @@ function formatTimer(ms) {
|
|
|
679
683
|
return `${Math.floor(total / 60)}:${(total % 60).toString().padStart(2, "0")}`;
|
|
680
684
|
}
|
|
681
685
|
//#endregion
|
|
682
|
-
//#region src/core
|
|
686
|
+
//#region src/core/shapes/itemLock.ts
|
|
683
687
|
/**
|
|
684
688
|
* Apply or clear a lock in place. Unlock always drops the holder so a stale
|
|
685
689
|
* name cannot linger on an unlocked item.
|
|
@@ -716,7 +720,7 @@ function serializeLock(item) {
|
|
|
716
720
|
};
|
|
717
721
|
}
|
|
718
722
|
//#endregion
|
|
719
|
-
//#region src/core
|
|
723
|
+
//#region src/core/document/document.ts
|
|
720
724
|
/**
|
|
721
725
|
* The one place a live Stroke becomes its wire representation — in
|
|
722
726
|
* particular, points collapse from {x,y,pressure,erase?} objects into
|
|
@@ -1275,7 +1279,7 @@ function computeBBox(stroke) {
|
|
|
1275
1279
|
return world;
|
|
1276
1280
|
}
|
|
1277
1281
|
//#endregion
|
|
1278
|
-
//#region src/core
|
|
1282
|
+
//#region src/core/commands/commands.ts
|
|
1279
1283
|
/** One drawing action — a marker stroke, or a shape's strokes as one unit. */
|
|
1280
1284
|
var AddStrokesCommand = class {
|
|
1281
1285
|
constructor(strokes) {
|
|
@@ -1617,7 +1621,7 @@ var LockItemsCommand = class {
|
|
|
1617
1621
|
}
|
|
1618
1622
|
};
|
|
1619
1623
|
//#endregion
|
|
1620
|
-
//#region src/core
|
|
1624
|
+
//#region src/core/history/history.ts
|
|
1621
1625
|
var LIMIT = 200;
|
|
1622
1626
|
var History = class {
|
|
1623
1627
|
constructor(doc) {
|
|
@@ -1678,7 +1682,7 @@ var History = class {
|
|
|
1678
1682
|
}
|
|
1679
1683
|
};
|
|
1680
1684
|
//#endregion
|
|
1681
|
-
//#region src/core
|
|
1685
|
+
//#region src/core/events/ops.ts
|
|
1682
1686
|
var ADDED = [
|
|
1683
1687
|
["added", "strokes"],
|
|
1684
1688
|
["notesAdded", "notes"],
|
|
@@ -1732,7 +1736,7 @@ function changeToOps(change, restoring = false) {
|
|
|
1732
1736
|
return ops;
|
|
1733
1737
|
}
|
|
1734
1738
|
//#endregion
|
|
1735
|
-
//#region src/core
|
|
1739
|
+
//#region src/core/shapes/ribbonEdges.ts
|
|
1736
1740
|
var MIN_WIDTH_FACTOR = .35;
|
|
1737
1741
|
var END_TAPER = .55;
|
|
1738
1742
|
function ribbonEdges(points, baseWidth) {
|
|
@@ -1764,7 +1768,7 @@ function ribbonEdges(points, baseWidth) {
|
|
|
1764
1768
|
return out;
|
|
1765
1769
|
}
|
|
1766
1770
|
//#endregion
|
|
1767
|
-
//#region src/core
|
|
1771
|
+
//#region src/core/document/spatialIndex.ts
|
|
1768
1772
|
var CELL = 8;
|
|
1769
1773
|
var SpatialIndex = class {
|
|
1770
1774
|
constructor(doc) {
|
|
@@ -1837,7 +1841,7 @@ function cellsOf(box) {
|
|
|
1837
1841
|
return keys;
|
|
1838
1842
|
}
|
|
1839
1843
|
//#endregion
|
|
1840
|
-
//#region src/core
|
|
1844
|
+
//#region src/core/shapes/stamps.ts
|
|
1841
1845
|
var STAMPS = [
|
|
1842
1846
|
{
|
|
1843
1847
|
kind: "star",
|
|
@@ -1885,7 +1889,7 @@ function isStampKind(value) {
|
|
|
1885
1889
|
return typeof value === "string" && STAMPS.some((s) => s.kind === value);
|
|
1886
1890
|
}
|
|
1887
1891
|
//#endregion
|
|
1888
|
-
//#region src/
|
|
1892
|
+
//#region src/persistence/serialization/svg.ts
|
|
1889
1893
|
/** Matches the key light used by the board shader and note shadows. */
|
|
1890
1894
|
var LIGHT = {
|
|
1891
1895
|
x: -.25,
|
|
@@ -1895,12 +1899,19 @@ var LIGHT = {
|
|
|
1895
1899
|
var MARGIN = 4;
|
|
1896
1900
|
var NOTE_FONT_RATIO = 44 / 512;
|
|
1897
1901
|
var NOTE_PAD_RATIO = 40 / 512;
|
|
1902
|
+
/** Matches the theme system's light-preset `boardSurface` default (theme.ts) — kept in sync manually, since core-internal stays decoupled from the theme layer. */
|
|
1903
|
+
var DEFAULT_BACKGROUND_COLOR = "#f5f5f3";
|
|
1898
1904
|
/**
|
|
1899
1905
|
* `registry` is optional and only enables rendering Custom objects through
|
|
1900
1906
|
* their own `describe()` — without it (or for an object whose extension
|
|
1901
1907
|
* isn't in it), Custom objects still export via the standard fallback
|
|
1902
1908
|
* placeholder (`fallback.bounds`/`label`), never silently dropped.
|
|
1903
1909
|
*
|
|
1910
|
+
* `backgroundColor` should be the Host's actual resolved `boardTheme.surface`
|
|
1911
|
+
* so an export matches what was on screen; defaults to the theme system's
|
|
1912
|
+
* own light-preset default when the caller doesn't have one on hand. The
|
|
1913
|
+
* board's reference grid, if any, is a screen-only aid and never exported.
|
|
1914
|
+
*
|
|
1904
1915
|
* `resolvedAssets` (ticket #23) maps an Asset reference to an already-
|
|
1905
1916
|
* resolved `data:` URI — see `assetExport.ts`'s `exportDocumentSVGWithAssets`,
|
|
1906
1917
|
* which is the only intended caller that ever passes one. Without it, every
|
|
@@ -1908,7 +1919,7 @@ var NOTE_PAD_RATIO = 40 / 512;
|
|
|
1908
1919
|
* placeholder instead of guessing at a URL; a legacy `src`-backed image is
|
|
1909
1920
|
* unaffected either way.
|
|
1910
1921
|
*/
|
|
1911
|
-
function documentToSVG(doc, now = 0, registry, resolvedAssets) {
|
|
1922
|
+
function documentToSVG(doc, now = 0, registry, backgroundColor = DEFAULT_BACKGROUND_COLOR, resolvedAssets) {
|
|
1912
1923
|
const notes = doc.notes ?? [];
|
|
1913
1924
|
const texts = doc.textBlocks ?? [];
|
|
1914
1925
|
const tables = doc.tables ?? [];
|
|
@@ -1921,7 +1932,7 @@ function documentToSVG(doc, now = 0, registry, resolvedAssets) {
|
|
|
1921
1932
|
const highlights = doc.strokes.filter((s) => s.tool === "highlighter");
|
|
1922
1933
|
const ink = doc.strokes.filter((s) => s.tool !== "highlighter");
|
|
1923
1934
|
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${view}">\n<defs>\n${defs}\n</defs>\n${[
|
|
1924
|
-
`<rect x="${fmt(bounds.minX)}" y="${fmt(bounds.minY)}" width="${fmt(bounds.width)}" height="${fmt(bounds.height)}" fill="${
|
|
1935
|
+
`<rect x="${fmt(bounds.minX)}" y="${fmt(bounds.minY)}" width="${fmt(bounds.width)}" height="${fmt(bounds.height)}" fill="${backgroundColor}"/>`,
|
|
1925
1936
|
...images.map((image) => imageToElement(image, resolvedAssets)),
|
|
1926
1937
|
...tables.map(tableToGroup),
|
|
1927
1938
|
...highlights.flatMap(strokeToPaths),
|
|
@@ -2245,7 +2256,7 @@ function fmt(n) {
|
|
|
2245
2256
|
return String(Math.round(n * 100) / 100);
|
|
2246
2257
|
}
|
|
2247
2258
|
//#endregion
|
|
2248
|
-
//#region src/core
|
|
2259
|
+
//#region src/core/shapes/sceneGeometry.ts
|
|
2249
2260
|
var EMPTY_BOUNDS = {
|
|
2250
2261
|
x: 0,
|
|
2251
2262
|
y: 0,
|
|
@@ -2309,7 +2320,7 @@ function textCorners(x, y, text, fontSize, align, m) {
|
|
|
2309
2320
|
return rectCorners(x + (align === "center" ? -width / 2 : align === "end" ? -width : 0), y, width, height, m);
|
|
2310
2321
|
}
|
|
2311
2322
|
//#endregion
|
|
2312
|
-
//#region src/core
|
|
2323
|
+
//#region src/core/shapes/customObjects.ts
|
|
2313
2324
|
var FALLBACK_LABEL_MAX_LENGTH = 80;
|
|
2314
2325
|
function toReadonly(object) {
|
|
2315
2326
|
return object;
|
|
@@ -2438,7 +2449,7 @@ function extensionIdFromType(type) {
|
|
|
2438
2449
|
return slash === -1 ? type : type.slice(0, slash);
|
|
2439
2450
|
}
|
|
2440
2451
|
//#endregion
|
|
2441
|
-
//#region src/core
|
|
2452
|
+
//#region src/core/shapes/extensionRegistry.ts
|
|
2442
2453
|
function buildExtensionRegistry(extensions) {
|
|
2443
2454
|
const list = extensions ?? [];
|
|
2444
2455
|
const diagnostics = [];
|
|
@@ -2535,7 +2546,7 @@ function isWellFormedNamespacedId(id) {
|
|
|
2535
2546
|
return isWellFormedId(id) && id.includes("/") && !id.startsWith("/") && !id.endsWith("/");
|
|
2536
2547
|
}
|
|
2537
2548
|
//#endregion
|
|
2538
|
-
//#region src/
|
|
2549
|
+
//#region src/persistence/adapters/assetValidation.ts
|
|
2539
2550
|
function sniffImageMediaType(bytes) {
|
|
2540
2551
|
if (bytes.length >= 8 && bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71 && bytes[4] === 13 && bytes[5] === 10 && bytes[6] === 26 && bytes[7] === 10) return "image/png";
|
|
2541
2552
|
if (bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) return "image/jpeg";
|
|
@@ -2622,7 +2633,7 @@ function validateDecodedDimensions(width, height) {
|
|
|
2622
2633
|
};
|
|
2623
2634
|
}
|
|
2624
2635
|
//#endregion
|
|
2625
|
-
//#region src/assetController.ts
|
|
2636
|
+
//#region src/persistence/adapters/assetController.ts
|
|
2626
2637
|
var FAILURE_COALESCE_MS = 4e3;
|
|
2627
2638
|
var AssetController = class {
|
|
2628
2639
|
constructor(resolver, options) {
|
|
@@ -2801,7 +2812,7 @@ function toAssetResolutionError(cause, ref) {
|
|
|
2801
2812
|
return new AssetResolutionError("unknown", true, cause instanceof Error ? cause.message : String(cause), ref);
|
|
2802
2813
|
}
|
|
2803
2814
|
//#endregion
|
|
2804
|
-
//#region src/
|
|
2815
|
+
//#region src/persistence/serialization/assetRefCollector.ts
|
|
2805
2816
|
/** Every unique Asset reference in the document, and which object(s) use it. */
|
|
2806
2817
|
function collectDocumentAssetRefs(doc, registry) {
|
|
2807
2818
|
const refs = /* @__PURE__ */ new Map();
|
|
@@ -2828,7 +2839,7 @@ function collectSceneAssetRefs(node, objectId, add) {
|
|
|
2828
2839
|
if (node.kind === "group") for (const child of node.children) collectSceneAssetRefs(child, objectId, add);
|
|
2829
2840
|
}
|
|
2830
2841
|
//#endregion
|
|
2831
|
-
//#region src/assetExport.ts
|
|
2842
|
+
//#region src/persistence/serialization/assetExport.ts
|
|
2832
2843
|
/** Strict mode's rejection — never contains credentials, a fetchable location, or a raw resolver message. */
|
|
2833
2844
|
var SvgAssetExportError = class extends Error {
|
|
2834
2845
|
constructor(missingAssets) {
|
|
@@ -2837,7 +2848,7 @@ var SvgAssetExportError = class extends Error {
|
|
|
2837
2848
|
this.name = "SvgAssetExportError";
|
|
2838
2849
|
}
|
|
2839
2850
|
};
|
|
2840
|
-
async function exportDocumentSVGWithAssets(doc, now, registry, assetController, options) {
|
|
2851
|
+
async function exportDocumentSVGWithAssets(doc, now, registry, assetController, backgroundColor, options) {
|
|
2841
2852
|
const refs = collectDocumentAssetRefs(doc, registry);
|
|
2842
2853
|
const resolvedAssets = /* @__PURE__ */ new Map();
|
|
2843
2854
|
const missingAssets = [];
|
|
@@ -2873,7 +2884,7 @@ async function exportDocumentSVGWithAssets(doc, now, registry, assetController,
|
|
|
2873
2884
|
}
|
|
2874
2885
|
if (missingAssets.length > 0 && options?.missingAssets !== "placeholder") throw new SvgAssetExportError(missingAssets);
|
|
2875
2886
|
return {
|
|
2876
|
-
svg: documentToSVG(doc, now, registry, resolvedAssets),
|
|
2887
|
+
svg: documentToSVG(doc, now, registry, backgroundColor, resolvedAssets),
|
|
2877
2888
|
missingAssets
|
|
2878
2889
|
};
|
|
2879
2890
|
}
|
|
@@ -2884,7 +2895,7 @@ function toDataUri(bytes, mediaType) {
|
|
|
2884
2895
|
return `data:${mediaType};base64,${btoa(binary)}`;
|
|
2885
2896
|
}
|
|
2886
2897
|
//#endregion
|
|
2887
|
-
//#region src/assetIngestion.ts
|
|
2898
|
+
//#region src/persistence/adapters/assetIngestion.ts
|
|
2888
2899
|
async function decodeDimensionsWithImageBitmap(bytes, mediaType) {
|
|
2889
2900
|
const blob = new Blob([bytes], { type: mediaType });
|
|
2890
2901
|
let bitmap;
|
|
@@ -35530,7 +35541,7 @@ var WebGLRenderer = class {
|
|
|
35530
35541
|
}
|
|
35531
35542
|
};
|
|
35532
35543
|
//#endregion
|
|
35533
|
-
//#region
|
|
35544
|
+
//#region src/core/events/auditEvents.ts
|
|
35534
35545
|
var BY_LABEL = {
|
|
35535
35546
|
draw: {
|
|
35536
35547
|
action: "create",
|
|
@@ -35650,7 +35661,7 @@ function lowerFirst(s) {
|
|
|
35650
35661
|
return s.charAt(0).toLowerCase() + s.slice(1);
|
|
35651
35662
|
}
|
|
35652
35663
|
//#endregion
|
|
35653
|
-
//#region
|
|
35664
|
+
//#region src/renderer/shapes/AssetTextureCache.ts
|
|
35654
35665
|
var AssetTextureCache = class {
|
|
35655
35666
|
constructor(assetController) {
|
|
35656
35667
|
this.assetController = assetController;
|
|
@@ -35741,7 +35752,7 @@ function toDecodeError(cause, ref) {
|
|
|
35741
35752
|
return new AssetResolutionError("decode-failed", false, cause instanceof Error ? cause.message : String(cause), ref);
|
|
35742
35753
|
}
|
|
35743
35754
|
//#endregion
|
|
35744
|
-
//#region
|
|
35755
|
+
//#region src/renderer/canvas/raycast.ts
|
|
35745
35756
|
var origin$1 = new Vector3();
|
|
35746
35757
|
var target = new Vector3();
|
|
35747
35758
|
function pointerToBoard(clientX, clientY, canvas, camera) {
|
|
@@ -35772,7 +35783,7 @@ function pointerToPlane(clientX, clientY, canvas, camera, z) {
|
|
|
35772
35783
|
};
|
|
35773
35784
|
}
|
|
35774
35785
|
//#endregion
|
|
35775
|
-
//#region
|
|
35786
|
+
//#region src/renderer/canvas/fog.ts
|
|
35776
35787
|
function createFogUniforms(color) {
|
|
35777
35788
|
return {
|
|
35778
35789
|
uFogColor: { value: new Color(color) },
|
|
@@ -35793,7 +35804,28 @@ var FOG_GLSL = `
|
|
|
35793
35804
|
}
|
|
35794
35805
|
`;
|
|
35795
35806
|
//#endregion
|
|
35796
|
-
//#region
|
|
35807
|
+
//#region src/renderer/canvas/boardSurface.ts
|
|
35808
|
+
var GRID_MODE_CODE = {
|
|
35809
|
+
none: 0,
|
|
35810
|
+
line: 1,
|
|
35811
|
+
dot: 2
|
|
35812
|
+
};
|
|
35813
|
+
var TEXTURE_CODE = {
|
|
35814
|
+
flat: 0,
|
|
35815
|
+
textured: 1
|
|
35816
|
+
};
|
|
35817
|
+
/** Used when a caller doesn't supply one (e.g. ScrawlEngine constructed without a Host theme). */
|
|
35818
|
+
var DEFAULT_BOARD_SURFACE_THEME = {
|
|
35819
|
+
color: "#f5f5f3",
|
|
35820
|
+
gridMode: "none",
|
|
35821
|
+
gridColor: "#deded8",
|
|
35822
|
+
gridSpacing: 5,
|
|
35823
|
+
gridLineWidth: 1,
|
|
35824
|
+
surfaceTexture: "flat"
|
|
35825
|
+
};
|
|
35826
|
+
/** Grid alpha fades out below this on-screen spacing (px) — otherwise it aliases into a solid wash when zoomed out. */
|
|
35827
|
+
var GRID_FADE_MIN_PX = 3;
|
|
35828
|
+
var GRID_FADE_MAX_PX = 8;
|
|
35797
35829
|
var VERTEX$1 = `
|
|
35798
35830
|
varying vec3 vWorldPos;
|
|
35799
35831
|
void main() {
|
|
@@ -35807,6 +35839,13 @@ var FRAGMENT = `
|
|
|
35807
35839
|
uniform vec3 uFogColor;
|
|
35808
35840
|
uniform float uFogNear;
|
|
35809
35841
|
uniform float uFogFar;
|
|
35842
|
+
uniform float uGridMode;
|
|
35843
|
+
uniform vec3 uGridColor;
|
|
35844
|
+
uniform float uGridSpacing;
|
|
35845
|
+
uniform float uWorldPerPixel;
|
|
35846
|
+
uniform float uGridLineWidthPx;
|
|
35847
|
+
uniform float uGridFade;
|
|
35848
|
+
uniform float uSurfaceTexture;
|
|
35810
35849
|
varying vec3 vWorldPos;
|
|
35811
35850
|
|
|
35812
35851
|
float hash(vec2 p) {
|
|
@@ -35825,33 +35864,69 @@ var FRAGMENT = `
|
|
|
35825
35864
|
|
|
35826
35865
|
${FOG_GLSL}
|
|
35827
35866
|
|
|
35867
|
+
// Distance from p's nearest axis-aligned grid line, in world units.
|
|
35868
|
+
// A crisp, ~uGridLineWidthPx-wide line: full alpha to its half-width, a
|
|
35869
|
+
// ~1px anti-aliased falloff beyond that — not a wide, soft-edged band.
|
|
35870
|
+
float gridLineAlpha(vec2 p) {
|
|
35871
|
+
vec2 cell = mod(p, uGridSpacing);
|
|
35872
|
+
vec2 distToLine = min(cell, uGridSpacing - cell);
|
|
35873
|
+
float halfWidth = 0.5 * uGridLineWidthPx * uWorldPerPixel;
|
|
35874
|
+
float aa = uWorldPerPixel;
|
|
35875
|
+
vec2 lineAlpha2 = 1.0 - smoothstep(halfWidth - aa * 0.5, halfWidth + aa * 0.5, distToLine);
|
|
35876
|
+
return max(lineAlpha2.x, lineAlpha2.y);
|
|
35877
|
+
}
|
|
35878
|
+
|
|
35879
|
+
// Distance from p's nearest grid intersection, in world units. Dot radius
|
|
35880
|
+
// matches the configured line width in pixels, same anti-aliasing.
|
|
35881
|
+
float gridDotAlpha(vec2 p) {
|
|
35882
|
+
vec2 cell = mod(p, uGridSpacing) - uGridSpacing * 0.5;
|
|
35883
|
+
float dist = length(cell);
|
|
35884
|
+
float radius = uGridLineWidthPx * uWorldPerPixel;
|
|
35885
|
+
float aa = uWorldPerPixel;
|
|
35886
|
+
return 1.0 - smoothstep(radius - aa * 0.5, radius + aa * 0.5, dist);
|
|
35887
|
+
}
|
|
35888
|
+
|
|
35828
35889
|
void main() {
|
|
35829
35890
|
vec3 col = uColor;
|
|
35830
35891
|
|
|
35831
|
-
|
|
35832
|
-
|
|
35833
|
-
|
|
35892
|
+
if (uSurfaceTexture > 0.5) {
|
|
35893
|
+
// Melamine micro-texture: two octaves of value noise, +-1.75% brightness.
|
|
35894
|
+
float n = vnoise(vWorldPos.xy * 2.3) * 0.6 + vnoise(vWorldPos.xy * 9.7) * 0.4;
|
|
35895
|
+
col *= 1.0 + (n - 0.5) * 0.035;
|
|
35834
35896
|
|
|
35835
|
-
|
|
35836
|
-
|
|
35837
|
-
|
|
35838
|
-
|
|
35839
|
-
|
|
35840
|
-
|
|
35841
|
-
|
|
35842
|
-
|
|
35843
|
-
|
|
35897
|
+
// Satin sheen: Fresnel rim + a specular band from the key light. Both
|
|
35898
|
+
// are ~zero in the straight-on working view and bloom as the camera tilts.
|
|
35899
|
+
vec3 V = normalize(cameraPosition - vWorldPos);
|
|
35900
|
+
vec3 N = vec3(0.0, 0.0, 1.0);
|
|
35901
|
+
float fres = pow(1.0 - max(dot(N, V), 0.0), 3.0);
|
|
35902
|
+
vec3 L = normalize(vec3(-0.25, 0.4, 0.88));
|
|
35903
|
+
vec3 H = normalize(L + V);
|
|
35904
|
+
float spec = pow(max(dot(N, H), 0.0), 90.0);
|
|
35905
|
+
col += (fres * 0.18 + spec * 0.06) * vec3(1.0);
|
|
35906
|
+
}
|
|
35907
|
+
|
|
35908
|
+
if (uGridMode > 0.5 && uGridFade > 0.001) {
|
|
35909
|
+
float gridAlpha = (uGridMode < 1.5 ? gridLineAlpha(vWorldPos.xy) : gridDotAlpha(vWorldPos.xy)) * uGridFade;
|
|
35910
|
+
col = mix(col, uGridColor, gridAlpha);
|
|
35911
|
+
}
|
|
35844
35912
|
|
|
35845
35913
|
col = applyBoardFog(col, vWorldPos);
|
|
35846
35914
|
gl_FragColor = vec4(col, 1.0);
|
|
35847
35915
|
}
|
|
35848
35916
|
`;
|
|
35849
|
-
function createBoardSurface(fog) {
|
|
35917
|
+
function createBoardSurface(fog, theme) {
|
|
35850
35918
|
const material = new ShaderMaterial({
|
|
35851
35919
|
vertexShader: VERTEX$1,
|
|
35852
35920
|
fragmentShader: FRAGMENT,
|
|
35853
35921
|
uniforms: {
|
|
35854
|
-
uColor: { value: new Color(
|
|
35922
|
+
uColor: { value: new Color(theme.color) },
|
|
35923
|
+
uGridMode: { value: GRID_MODE_CODE[theme.gridMode] },
|
|
35924
|
+
uGridColor: { value: new Color(theme.gridColor) },
|
|
35925
|
+
uGridSpacing: { value: Math.max(.001, theme.gridSpacing) },
|
|
35926
|
+
uGridLineWidthPx: { value: Math.max(.1, theme.gridLineWidth) },
|
|
35927
|
+
uWorldPerPixel: { value: .05 },
|
|
35928
|
+
uGridFade: { value: 0 },
|
|
35929
|
+
uSurfaceTexture: { value: TEXTURE_CODE[theme.surfaceTexture] },
|
|
35855
35930
|
...fog
|
|
35856
35931
|
}
|
|
35857
35932
|
});
|
|
@@ -35859,11 +35934,34 @@ function createBoardSurface(fog) {
|
|
|
35859
35934
|
mesh.name = "board-surface";
|
|
35860
35935
|
return mesh;
|
|
35861
35936
|
}
|
|
35862
|
-
/**
|
|
35863
|
-
function
|
|
35937
|
+
/** Applies a theme change (initial or live) — everything except the per-frame fade/pixel-ratio. */
|
|
35938
|
+
function setBoardSurfaceTheme(mesh, theme) {
|
|
35939
|
+
const uniforms = mesh.material.uniforms;
|
|
35940
|
+
uniforms.uColor.value.set(theme.color);
|
|
35941
|
+
uniforms.uGridMode.value = GRID_MODE_CODE[theme.gridMode];
|
|
35942
|
+
uniforms.uGridColor.value.set(theme.gridColor);
|
|
35943
|
+
uniforms.uGridSpacing.value = Math.max(.001, theme.gridSpacing);
|
|
35944
|
+
uniforms.uGridLineWidthPx.value = Math.max(.1, theme.gridLineWidth);
|
|
35945
|
+
uniforms.uSurfaceTexture.value = TEXTURE_CODE[theme.surfaceTexture];
|
|
35946
|
+
}
|
|
35947
|
+
/** Keep the plane centered under the view, larger than the fog horizon, and the grid's on-screen weight/fade current. */
|
|
35948
|
+
function updateBoardSurface(mesh, centerX, centerY, fogFar, worldPerPixel) {
|
|
35864
35949
|
mesh.position.set(centerX, centerY, 0);
|
|
35865
35950
|
const size = fogFar * 2.5;
|
|
35866
35951
|
mesh.scale.set(size, size, 1);
|
|
35952
|
+
const uniforms = mesh.material.uniforms;
|
|
35953
|
+
uniforms.uWorldPerPixel.value = worldPerPixel;
|
|
35954
|
+
const t = (uniforms.uGridSpacing.value / Math.max(1e-6, worldPerPixel) - GRID_FADE_MIN_PX) / (GRID_FADE_MAX_PX - GRID_FADE_MIN_PX);
|
|
35955
|
+
uniforms.uGridFade.value = MathUtils.clamp(t, 0, 1);
|
|
35956
|
+
}
|
|
35957
|
+
/** Temporarily zeroes the grid's contribution (for export snapshots) and returns a restore function. */
|
|
35958
|
+
function suppressBoardSurfaceGrid(mesh) {
|
|
35959
|
+
const uniforms = mesh.material.uniforms;
|
|
35960
|
+
const previous = uniforms.uGridFade.value;
|
|
35961
|
+
uniforms.uGridFade.value = 0;
|
|
35962
|
+
return () => {
|
|
35963
|
+
uniforms.uGridFade.value = previous;
|
|
35964
|
+
};
|
|
35867
35965
|
}
|
|
35868
35966
|
var MIN_HEIGHT = 40;
|
|
35869
35967
|
var MAX_HEIGHT = 1500;
|
|
@@ -36025,9 +36123,9 @@ var CameraRig = class {
|
|
|
36025
36123
|
}
|
|
36026
36124
|
};
|
|
36027
36125
|
//#endregion
|
|
36028
|
-
//#region
|
|
36126
|
+
//#region src/renderer/canvas/scene.ts
|
|
36029
36127
|
var SceneRig = class {
|
|
36030
|
-
constructor(canvas) {
|
|
36128
|
+
constructor(canvas, boardTheme) {
|
|
36031
36129
|
this.renderer = new WebGLRenderer({
|
|
36032
36130
|
canvas,
|
|
36033
36131
|
antialias: true
|
|
@@ -36037,7 +36135,7 @@ var SceneRig = class {
|
|
|
36037
36135
|
this.scene = new Scene();
|
|
36038
36136
|
this.cameraRig = new CameraRig(canvas.clientWidth / Math.max(1, canvas.clientHeight));
|
|
36039
36137
|
this.fog = createFogUniforms(FOG_COLOR);
|
|
36040
|
-
this.board = createBoardSurface(this.fog);
|
|
36138
|
+
this.board = createBoardSurface(this.fog, boardTheme);
|
|
36041
36139
|
this.scene.add(this.board);
|
|
36042
36140
|
this.inkGroup = new Group();
|
|
36043
36141
|
this.inkGroup.name = "ink";
|
|
@@ -36054,9 +36152,17 @@ var SceneRig = class {
|
|
|
36054
36152
|
renderFrame(dtMs) {
|
|
36055
36153
|
this.cameraRig.update(dtMs);
|
|
36056
36154
|
updateFogForCamera(this.fog, this.cameraRig.height);
|
|
36057
|
-
updateBoardSurface(this.board, this.cameraRig.center.x, this.cameraRig.center.y, this.fog.uFogFar.value);
|
|
36155
|
+
updateBoardSurface(this.board, this.cameraRig.center.x, this.cameraRig.center.y, this.fog.uFogFar.value, this.cameraRig.worldPerPixel());
|
|
36058
36156
|
this.renderer.render(this.scene, this.cameraRig.camera);
|
|
36059
36157
|
}
|
|
36158
|
+
/** Live theme update — the board controller's identity stays fixed across theme changes; this is how a new one reaches the render engine. */
|
|
36159
|
+
applyBoardTheme(theme) {
|
|
36160
|
+
setBoardSurfaceTheme(this.board, theme);
|
|
36161
|
+
}
|
|
36162
|
+
/** Zeroes the grid for one render pass (PNG export never bakes it in) and returns a restore function. */
|
|
36163
|
+
suppressGrid() {
|
|
36164
|
+
return suppressBoardSurfaceGrid(this.board);
|
|
36165
|
+
}
|
|
36060
36166
|
dispose() {
|
|
36061
36167
|
this.board.geometry.dispose();
|
|
36062
36168
|
this.board.material.dispose();
|
|
@@ -36064,7 +36170,7 @@ var SceneRig = class {
|
|
|
36064
36170
|
}
|
|
36065
36171
|
};
|
|
36066
36172
|
//#endregion
|
|
36067
|
-
//#region
|
|
36173
|
+
//#region src/renderer/shapes/inkMaterial.ts
|
|
36068
36174
|
var VERTEX = `
|
|
36069
36175
|
attribute float aAlpha;
|
|
36070
36176
|
varying vec2 vUv;
|
|
@@ -36195,7 +36301,7 @@ var InkMaterials = class {
|
|
|
36195
36301
|
}
|
|
36196
36302
|
};
|
|
36197
36303
|
//#endregion
|
|
36198
|
-
//#region
|
|
36304
|
+
//#region src/renderer/shapes/ribbonGeometry.ts
|
|
36199
36305
|
/** Ink floats a hair above the board to avoid z-fighting with the surface. */
|
|
36200
36306
|
var INK_Z = .02;
|
|
36201
36307
|
/** Highlighter always renders under opaque ink (build prompt §4.1). */
|
|
@@ -36246,7 +36352,7 @@ function buildRibbonGeometry(points, baseWidth, z = INK_Z) {
|
|
|
36246
36352
|
return geometry;
|
|
36247
36353
|
}
|
|
36248
36354
|
//#endregion
|
|
36249
|
-
//#region
|
|
36355
|
+
//#region src/renderer/shapes/strokeRenderer.ts
|
|
36250
36356
|
var strokeZ = (stroke) => stroke.tool === "highlighter" ? HIGHLIGHT_Z : INK_Z;
|
|
36251
36357
|
/**
|
|
36252
36358
|
* Derives ink meshes from the document by subscription — the document never
|
|
@@ -36379,7 +36485,7 @@ function syncMatrix(mesh, stroke) {
|
|
|
36379
36485
|
mesh.matrixWorldNeedsUpdate = true;
|
|
36380
36486
|
}
|
|
36381
36487
|
//#endregion
|
|
36382
|
-
//#region
|
|
36488
|
+
//#region src/renderer/shapes/markerProp.ts
|
|
36383
36489
|
var BODY_LENGTH = 11;
|
|
36384
36490
|
var BODY_RADIUS = .85;
|
|
36385
36491
|
var TIP_LENGTH = 1.6;
|
|
@@ -36475,7 +36581,7 @@ var MarkerProp = class {
|
|
|
36475
36581
|
}
|
|
36476
36582
|
};
|
|
36477
36583
|
//#endregion
|
|
36478
|
-
//#region
|
|
36584
|
+
//#region src/renderer/shapes/noteGeometry.ts
|
|
36479
36585
|
/**
|
|
36480
36586
|
* Height of the sheet above the board at (u, v) ∈ [0,1]².
|
|
36481
36587
|
* v = 1 is the adhesive edge (always 0); u = 1 is the peel corner side.
|
|
@@ -36519,7 +36625,7 @@ function applyCurl(geometry, curl, peel) {
|
|
|
36519
36625
|
color.needsUpdate = true;
|
|
36520
36626
|
}
|
|
36521
36627
|
//#endregion
|
|
36522
|
-
//#region
|
|
36628
|
+
//#region src/renderer/shapes/stickyNotes.ts
|
|
36523
36629
|
var TEXTURE_SIZE = 512;
|
|
36524
36630
|
var FOCUS_COLOR$4 = 2450411;
|
|
36525
36631
|
var CURL = .3;
|
|
@@ -43095,7 +43201,7 @@ SYNCABLE_PROPS.forEach((prop) => {
|
|
|
43095
43201
|
new Box3();
|
|
43096
43202
|
new Color();
|
|
43097
43203
|
//#endregion
|
|
43098
|
-
//#region
|
|
43204
|
+
//#region src/renderer/shapes/tables.ts
|
|
43099
43205
|
var FONT_URL$2 = "/fonts/inter-medium.woff";
|
|
43100
43206
|
var TABLE_Z = .012;
|
|
43101
43207
|
var HEADER_Z = .014;
|
|
@@ -43444,7 +43550,7 @@ var TablesRenderer = class {
|
|
|
43444
43550
|
}
|
|
43445
43551
|
};
|
|
43446
43552
|
//#endregion
|
|
43447
|
-
//#region
|
|
43553
|
+
//#region src/renderer/text/textBlocks.ts
|
|
43448
43554
|
var FONT_URL$1 = "/fonts/inter-medium.woff";
|
|
43449
43555
|
var TEXT_Z = .02;
|
|
43450
43556
|
var FOCUS_COLOR$2 = 2450411;
|
|
@@ -43548,7 +43654,7 @@ var TextBlocksRenderer = class {
|
|
|
43548
43654
|
}
|
|
43549
43655
|
};
|
|
43550
43656
|
//#endregion
|
|
43551
|
-
//#region
|
|
43657
|
+
//#region src/renderer/shapes/comments.ts
|
|
43552
43658
|
var PIN_Z = .04;
|
|
43553
43659
|
var PIN_WIDTH = 3.6;
|
|
43554
43660
|
var PIN_HEIGHT = 3.6;
|
|
@@ -43716,7 +43822,7 @@ function drawBubblePath(ctx, x, y, w, h, r, tailX, tailY, tailW) {
|
|
|
43716
43822
|
ctx.closePath();
|
|
43717
43823
|
}
|
|
43718
43824
|
//#endregion
|
|
43719
|
-
//#region
|
|
43825
|
+
//#region src/renderer/shapes/images.ts
|
|
43720
43826
|
var IMAGE_Z = .015;
|
|
43721
43827
|
var FRAME_BORDER = .4;
|
|
43722
43828
|
var FOCUS_COLOR$1 = 2450411;
|
|
@@ -43926,7 +44032,7 @@ function applyPlaceholderTexture(visual, label) {
|
|
|
43926
44032
|
visual.imageMesh.material.needsUpdate = true;
|
|
43927
44033
|
}
|
|
43928
44034
|
//#endregion
|
|
43929
|
-
//#region
|
|
44035
|
+
//#region src/renderer/shapes/kitchenTimers.ts
|
|
43930
44036
|
var FACE_SIZE = 256;
|
|
43931
44037
|
var BODY_COLOR = 14753096;
|
|
43932
44038
|
var KNOB_COLOR = 16448250;
|
|
@@ -44124,7 +44230,7 @@ function renderTimerFace(timer, canvas, now = Date.now()) {
|
|
|
44124
44230
|
return c;
|
|
44125
44231
|
}
|
|
44126
44232
|
//#endregion
|
|
44127
|
-
//#region
|
|
44233
|
+
//#region src/renderer/shapes/customObjects.ts
|
|
44128
44234
|
var FONT_URL = "/fonts/inter-medium.woff";
|
|
44129
44235
|
var CUSTOM_Z = .02;
|
|
44130
44236
|
var FOCUS_COLOR = 2450411;
|
|
@@ -44486,7 +44592,7 @@ function disposeObject3D(obj) {
|
|
|
44486
44592
|
});
|
|
44487
44593
|
}
|
|
44488
44594
|
//#endregion
|
|
44489
|
-
//#region
|
|
44595
|
+
//#region src/interaction/pointer/hitTest.ts
|
|
44490
44596
|
/** Topmost (most recently added) stroke under the point, or null. */
|
|
44491
44597
|
function hitStroke(doc, index, p, slop) {
|
|
44492
44598
|
const candidates = index.query(p.x - slop, p.y - slop, p.x + slop, p.y + slop);
|
|
@@ -44527,7 +44633,7 @@ function distToSegmentSq(p, a, b) {
|
|
|
44527
44633
|
return dx * dx + dy * dy;
|
|
44528
44634
|
}
|
|
44529
44635
|
//#endregion
|
|
44530
|
-
//#region
|
|
44636
|
+
//#region src/interaction/tools/eraserTool.ts
|
|
44531
44637
|
var DECAY_PER_PASS = .4;
|
|
44532
44638
|
/** A point can take another decay pass after this long — rubbing works. */
|
|
44533
44639
|
var REARM_MS = 250;
|
|
@@ -44655,7 +44761,7 @@ function partitionByErasure(points) {
|
|
|
44655
44761
|
return runs;
|
|
44656
44762
|
}
|
|
44657
44763
|
//#endregion
|
|
44658
|
-
//#region
|
|
44764
|
+
//#region src/interaction/tools/noteDrag.ts
|
|
44659
44765
|
/**
|
|
44660
44766
|
* Drag the focused note in X/Y. The grab is resolved on the note's own
|
|
44661
44767
|
* z-plane, not the board, so the note doesn't jump under a tilted ray.
|
|
@@ -44722,7 +44828,7 @@ var NoteDragState = class {
|
|
|
44722
44828
|
}
|
|
44723
44829
|
};
|
|
44724
44830
|
//#endregion
|
|
44725
|
-
//#region
|
|
44831
|
+
//#region src/interaction/tools/imageDrag.ts
|
|
44726
44832
|
/** Drag the focused image in X/Y; one command per drag. */
|
|
44727
44833
|
var ImageDragState = class {
|
|
44728
44834
|
constructor(ctx) {
|
|
@@ -44774,7 +44880,7 @@ var ImageDragState = class {
|
|
|
44774
44880
|
}
|
|
44775
44881
|
};
|
|
44776
44882
|
//#endregion
|
|
44777
|
-
//#region
|
|
44883
|
+
//#region src/interaction/tools/timerDrag.ts
|
|
44778
44884
|
/** Drag the focused timer in X/Y; one command per drag. */
|
|
44779
44885
|
var TimerDragState = class {
|
|
44780
44886
|
constructor(ctx) {
|
|
@@ -44826,7 +44932,7 @@ var TimerDragState = class {
|
|
|
44826
44932
|
}
|
|
44827
44933
|
};
|
|
44828
44934
|
//#endregion
|
|
44829
|
-
//#region
|
|
44935
|
+
//#region src/interaction/tools/shapeGeometry.ts
|
|
44830
44936
|
/** Uniform synthetic pressure; the ink shader supplies the hand-drawn feel. */
|
|
44831
44937
|
var PRESSURE = .7;
|
|
44832
44938
|
var MIN_ELLIPSE_SEGMENTS = 24;
|
|
@@ -45015,7 +45121,7 @@ function snappedEnd(a, b) {
|
|
|
45015
45121
|
};
|
|
45016
45122
|
}
|
|
45017
45123
|
//#endregion
|
|
45018
|
-
//#region
|
|
45124
|
+
//#region src/interaction/tools/shapeTool.ts
|
|
45019
45125
|
var MIN_DRAG_PX$1 = 4;
|
|
45020
45126
|
/** Drag out a shape; live preview through the real ink material. */
|
|
45021
45127
|
var ShapeTool = class {
|
|
@@ -45046,7 +45152,7 @@ var ShapeTool = class {
|
|
|
45046
45152
|
const strokes = this.polylines.map((points) => ({
|
|
45047
45153
|
id: crypto.randomUUID(),
|
|
45048
45154
|
color: this.ctx.inkColor(),
|
|
45049
|
-
baseWidth: this.ctx.
|
|
45155
|
+
baseWidth: this.ctx.shapeStrokeWidth(),
|
|
45050
45156
|
points
|
|
45051
45157
|
}));
|
|
45052
45158
|
this.ctx.clusters.assign(strokes[0]);
|
|
@@ -45080,13 +45186,13 @@ var ShapeTool = class {
|
|
|
45080
45186
|
this.previews[i].visible = polyline !== void 0;
|
|
45081
45187
|
if (polyline) {
|
|
45082
45188
|
this.previews[i].geometry.dispose();
|
|
45083
|
-
this.previews[i].geometry = buildRibbonGeometry(polyline, this.ctx.
|
|
45189
|
+
this.previews[i].geometry = buildRibbonGeometry(polyline, this.ctx.shapeStrokeWidth());
|
|
45084
45190
|
}
|
|
45085
45191
|
}
|
|
45086
45192
|
}
|
|
45087
45193
|
};
|
|
45088
45194
|
//#endregion
|
|
45089
|
-
//#region
|
|
45195
|
+
//#region src/interaction/tools/tableDrag.ts
|
|
45090
45196
|
function getHandleCursor(handle) {
|
|
45091
45197
|
switch (handle) {
|
|
45092
45198
|
case "nw":
|
|
@@ -45206,7 +45312,7 @@ var TableDragState = class {
|
|
|
45206
45312
|
}
|
|
45207
45313
|
};
|
|
45208
45314
|
//#endregion
|
|
45209
|
-
//#region
|
|
45315
|
+
//#region src/interaction/tools/tableTool.ts
|
|
45210
45316
|
var MIN_DRAG_PX = 6;
|
|
45211
45317
|
var TableTool = class {
|
|
45212
45318
|
constructor(ctx) {
|
|
@@ -45337,7 +45443,7 @@ var TableTool = class {
|
|
|
45337
45443
|
}
|
|
45338
45444
|
};
|
|
45339
45445
|
//#endregion
|
|
45340
|
-
//#region
|
|
45446
|
+
//#region src/interaction/tools/commentTool.ts
|
|
45341
45447
|
/** Click on the board to drop a new comment pin or open an existing one. */
|
|
45342
45448
|
var CommentTool = class {
|
|
45343
45449
|
constructor(ctx) {
|
|
@@ -45372,7 +45478,7 @@ var CommentTool = class {
|
|
|
45372
45478
|
}
|
|
45373
45479
|
};
|
|
45374
45480
|
//#endregion
|
|
45375
|
-
//#region
|
|
45481
|
+
//#region src/interaction/tools/textDrag.ts
|
|
45376
45482
|
/** Drag the focused text block in X/Y; one command per drag. */
|
|
45377
45483
|
var TextDragState = class {
|
|
45378
45484
|
constructor(ctx) {
|
|
@@ -45424,7 +45530,7 @@ var TextDragState = class {
|
|
|
45424
45530
|
}
|
|
45425
45531
|
};
|
|
45426
45532
|
//#endregion
|
|
45427
|
-
//#region
|
|
45533
|
+
//#region src/interaction/tools/fsm.ts
|
|
45428
45534
|
var ToolStateMachine = class {
|
|
45429
45535
|
constructor() {
|
|
45430
45536
|
this.states = /* @__PURE__ */ new Map();
|
|
@@ -45626,7 +45732,7 @@ var IdleState = class {
|
|
|
45626
45732
|
}
|
|
45627
45733
|
};
|
|
45628
45734
|
//#endregion
|
|
45629
|
-
//#region
|
|
45735
|
+
//#region src/renderer/selection/gizmo.ts
|
|
45630
45736
|
var GIZMO_Z = .05;
|
|
45631
45737
|
var HANDLE_PX = 9;
|
|
45632
45738
|
var HANDLE_HIT_PX = 12;
|
|
@@ -45857,7 +45963,7 @@ function boxesEqual(a, b) {
|
|
|
45857
45963
|
return a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY;
|
|
45858
45964
|
}
|
|
45859
45965
|
//#endregion
|
|
45860
|
-
//#region
|
|
45966
|
+
//#region src/interaction/tools/pressure.ts
|
|
45861
45967
|
var SIM_MAX = 1;
|
|
45862
45968
|
var SIM_MIN = .3;
|
|
45863
45969
|
var SMOOTHING = .25;
|
|
@@ -45879,7 +45985,7 @@ function clamp(v, lo, hi) {
|
|
|
45879
45985
|
return Math.min(hi, Math.max(lo, v));
|
|
45880
45986
|
}
|
|
45881
45987
|
//#endregion
|
|
45882
|
-
//#region
|
|
45988
|
+
//#region src/interaction/tools/strokeBuilder.ts
|
|
45883
45989
|
var StrokeBuilder = class {
|
|
45884
45990
|
constructor() {
|
|
45885
45991
|
this.points = [];
|
|
@@ -45929,7 +46035,7 @@ function catmullRom(v0, v1, v2, v3, t) {
|
|
|
45929
46035
|
return .5 * (2 * v1 + (-v0 + v2) * t + (2 * v0 - 5 * v1 + 4 * v2 - v3) * t2 + (-v0 + 3 * v1 - 3 * v2 + v3) * t3);
|
|
45930
46036
|
}
|
|
45931
46037
|
//#endregion
|
|
45932
|
-
//#region
|
|
46038
|
+
//#region src/interaction/tools/markerTool.ts
|
|
45933
46039
|
/** Sample spacing while drawing, in screen pixels. */
|
|
45934
46040
|
var MIN_SAMPLE_PX = 1.5;
|
|
45935
46041
|
/** A highlighter is a chisel tip: near-constant width, little pressure play. */
|
|
@@ -46032,7 +46138,7 @@ var MarkerTool = class {
|
|
|
46032
46138
|
}
|
|
46033
46139
|
};
|
|
46034
46140
|
//#endregion
|
|
46035
|
-
//#region
|
|
46141
|
+
//#region src/interaction/tools/orbitTool.ts
|
|
46036
46142
|
/**
|
|
46037
46143
|
* Orbit-preview (build prompt §4.2): alt-drag tilts the camera off-axis to
|
|
46038
46144
|
* read the board's material and depth; releasing eases back to the working
|
|
@@ -46067,7 +46173,7 @@ var OrbitTool = class {
|
|
|
46067
46173
|
}
|
|
46068
46174
|
};
|
|
46069
46175
|
//#endregion
|
|
46070
|
-
//#region
|
|
46176
|
+
//#region src/interaction/tools/panTool.ts
|
|
46071
46177
|
/** Grab-pan: content follows the pointer. */
|
|
46072
46178
|
var PanTool = class {
|
|
46073
46179
|
constructor(ctx) {
|
|
@@ -46097,7 +46203,7 @@ var PanTool = class {
|
|
|
46097
46203
|
}
|
|
46098
46204
|
};
|
|
46099
46205
|
//#endregion
|
|
46100
|
-
//#region
|
|
46206
|
+
//#region src/renderer/selection/selection.ts
|
|
46101
46207
|
/** The set of selected stroke ids. Drops ids the document no longer has. */
|
|
46102
46208
|
var SelectionManager = class {
|
|
46103
46209
|
constructor(doc) {
|
|
@@ -46148,7 +46254,7 @@ var SelectionManager = class {
|
|
|
46148
46254
|
}
|
|
46149
46255
|
};
|
|
46150
46256
|
//#endregion
|
|
46151
|
-
//#region
|
|
46257
|
+
//#region src/interaction/tools/selectTool.ts
|
|
46152
46258
|
var HIT_SLOP_PX = 4;
|
|
46153
46259
|
var CLICK_TOLERANCE_PX = 3;
|
|
46154
46260
|
var MIN_SCALE = .05;
|
|
@@ -46364,7 +46470,7 @@ function safeRatio(numerator, denominator) {
|
|
|
46364
46470
|
return raw;
|
|
46365
46471
|
}
|
|
46366
46472
|
//#endregion
|
|
46367
|
-
//#region
|
|
46473
|
+
//#region src/interaction/tools/customTool.ts
|
|
46368
46474
|
function extensionIdFromToolId(toolId) {
|
|
46369
46475
|
const slash = toolId.indexOf("/");
|
|
46370
46476
|
return slash === -1 ? toolId : toolId.slice(0, slash);
|
|
@@ -46479,12 +46585,19 @@ var ExtensionCommandBatch = class {
|
|
|
46479
46585
|
}
|
|
46480
46586
|
};
|
|
46481
46587
|
//#endregion
|
|
46482
|
-
//#region
|
|
46588
|
+
//#region src/composition/ScrawlEngine.ts
|
|
46483
46589
|
/** Marker stroke width in board units (~1% of the default view height). */
|
|
46484
46590
|
var BASE_WIDTH = .5;
|
|
46485
46591
|
/** Highlighter chisel width — wide enough to cover a line of handwriting. */
|
|
46486
46592
|
var HIGHLIGHT_WIDTH = 2.2;
|
|
46487
46593
|
/**
|
|
46594
|
+
* Shape (rectangle, ellipse, arrow, ...) border width in board units.
|
|
46595
|
+
* Deliberately thinner than BASE_WIDTH: a shape border reads as a crisp,
|
|
46596
|
+
* structural line, not an expressive marker stroke — this is also the
|
|
46597
|
+
* fallback when no Host theme configures ScrawlTheme.shapeStrokeWidth.
|
|
46598
|
+
*/
|
|
46599
|
+
var DEFAULT_SHAPE_STROKE_WIDTH = .15;
|
|
46600
|
+
/**
|
|
46488
46601
|
* Identity of a rendered selection badge: which item, its lock state, and
|
|
46489
46602
|
* where it sits on screen (rounded to the pixel, so sub-pixel camera drift
|
|
46490
46603
|
* does not churn React).
|
|
@@ -46505,9 +46618,11 @@ function emptyExtensionRegistry() {
|
|
|
46505
46618
|
* React never reaches past this class.
|
|
46506
46619
|
*/
|
|
46507
46620
|
var ScrawlEngine = class {
|
|
46508
|
-
constructor(canvas, documentId$1, extensionRegistry = emptyExtensionRegistry(), assetController = new AssetController(void 0)) {
|
|
46621
|
+
constructor(canvas, documentId$1, extensionRegistry = emptyExtensionRegistry(), assetController = new AssetController(void 0), boardTheme = DEFAULT_BOARD_SURFACE_THEME, shapeStrokeWidth = DEFAULT_SHAPE_STROKE_WIDTH) {
|
|
46509
46622
|
this.canvas = canvas;
|
|
46510
46623
|
this.extensionRegistry = extensionRegistry;
|
|
46624
|
+
this.boardTheme = boardTheme;
|
|
46625
|
+
this.shapeStrokeWidth = shapeStrokeWidth;
|
|
46511
46626
|
this.onToolChange = null;
|
|
46512
46627
|
this.onZoomChange = null;
|
|
46513
46628
|
this.onAuditEvent = null;
|
|
@@ -46554,7 +46669,7 @@ var ScrawlEngine = class {
|
|
|
46554
46669
|
this.stampKind = "star";
|
|
46555
46670
|
this.timerDurationMs = TIMER_DEFAULT_DURATION_MS;
|
|
46556
46671
|
this.readOnly = false;
|
|
46557
|
-
this.scene = new SceneRig(canvas);
|
|
46672
|
+
this.scene = new SceneRig(canvas, boardTheme);
|
|
46558
46673
|
this.document = new BoardDocument(documentId(documentId$1));
|
|
46559
46674
|
this.history = new History(this.document);
|
|
46560
46675
|
this.history.onCommand = (command, kind) => {
|
|
@@ -46602,6 +46717,7 @@ var ScrawlEngine = class {
|
|
|
46602
46717
|
setTool: (t) => this.setTool(t),
|
|
46603
46718
|
inkColor: () => this.tool === "highlighter" ? this.highlightColor : this.color,
|
|
46604
46719
|
baseWidth: () => this.tool === "highlighter" ? HIGHLIGHT_WIDTH : BASE_WIDTH,
|
|
46720
|
+
shapeStrokeWidth: () => this.shapeStrokeWidth,
|
|
46605
46721
|
activeTool: () => this.tool,
|
|
46606
46722
|
isSpaceHeld: () => this.spaceHeld,
|
|
46607
46723
|
pointerToBoard: (e) => pointerToBoard(e.clientX, e.clientY, canvas, this.scene.cameraRig.camera),
|
|
@@ -46861,6 +46977,15 @@ var ScrawlEngine = class {
|
|
|
46861
46977
|
height: rig.height
|
|
46862
46978
|
};
|
|
46863
46979
|
}
|
|
46980
|
+
/** Live update of the board surface color/grid — the engine's identity stays fixed across theme changes. */
|
|
46981
|
+
applyBoardTheme(theme) {
|
|
46982
|
+
this.boardTheme = theme;
|
|
46983
|
+
this.scene.applyBoardTheme(theme);
|
|
46984
|
+
}
|
|
46985
|
+
/** Live update of the shape tool's border width (board units). Already-drawn shapes keep the width they were drawn with. */
|
|
46986
|
+
setShapeStrokeWidth(width) {
|
|
46987
|
+
this.shapeStrokeWidth = width;
|
|
46988
|
+
}
|
|
46864
46989
|
/**
|
|
46865
46990
|
* Snap to a peer's camera. No-op while drawing so pointer-to-board
|
|
46866
46991
|
* mapping does not warp the in-progress stroke.
|
|
@@ -46879,7 +47004,7 @@ var ScrawlEngine = class {
|
|
|
46879
47004
|
}
|
|
46880
47005
|
/** The saved document as standalone SVG — a pure walk of the wire format. */
|
|
46881
47006
|
exportSVG() {
|
|
46882
|
-
return documentToSVG(this.document.toJSON(), Date.now(), this.extensionRegistry);
|
|
47007
|
+
return documentToSVG(this.document.toJSON(), Date.now(), this.extensionRegistry, this.boardTheme.color);
|
|
46883
47008
|
}
|
|
46884
47009
|
/**
|
|
46885
47010
|
* Content-framed PNG snapshot. Renders once offscreen-style with UI props
|
|
@@ -46915,6 +47040,7 @@ var ScrawlEngine = class {
|
|
|
46915
47040
|
this.notesRenderer.setFocus(null);
|
|
46916
47041
|
this.textsRenderer.setFocus(null);
|
|
46917
47042
|
this.renderer.setGhostsVisible(false);
|
|
47043
|
+
const restoreGrid = this.scene.suppressGrid();
|
|
46918
47044
|
try {
|
|
46919
47045
|
rig.yaw = 0;
|
|
46920
47046
|
rig.pitch = 0;
|
|
@@ -46925,6 +47051,7 @@ var ScrawlEngine = class {
|
|
|
46925
47051
|
const dataUrl = this.scene.renderer.domElement.toDataURL("image/png");
|
|
46926
47052
|
return await (await fetch(dataUrl)).blob();
|
|
46927
47053
|
} finally {
|
|
47054
|
+
restoreGrid();
|
|
46928
47055
|
rig.center.x = saved.x;
|
|
46929
47056
|
rig.center.y = saved.y;
|
|
46930
47057
|
rig.height = saved.height;
|
|
@@ -47920,7 +48047,7 @@ var ScrawlEngine = class {
|
|
|
47920
48047
|
}
|
|
47921
48048
|
};
|
|
47922
48049
|
//#endregion
|
|
47923
|
-
//#region src/controller-internal.ts
|
|
48050
|
+
//#region src/composition/controller-internal.ts
|
|
47924
48051
|
function createBoardController(options) {
|
|
47925
48052
|
return createBoardControllerWithEngine(options);
|
|
47926
48053
|
}
|
|
@@ -47934,7 +48061,16 @@ function createBoardControllerWithEngine(options, existingEngine) {
|
|
|
47934
48061
|
}
|
|
47935
48062
|
const extensionRegistry = registryResult.registry;
|
|
47936
48063
|
const assetController = new AssetController(options.assetResolver, { cacheBytes: options.assetCacheBytes });
|
|
47937
|
-
|
|
48064
|
+
let currentBoardTheme = {
|
|
48065
|
+
color: options.boardTheme?.surface ?? DEFAULT_BOARD_SURFACE_THEME.color,
|
|
48066
|
+
gridMode: options.boardTheme?.gridMode ?? DEFAULT_BOARD_SURFACE_THEME.gridMode,
|
|
48067
|
+
gridColor: options.boardTheme?.gridColor ?? DEFAULT_BOARD_SURFACE_THEME.gridColor,
|
|
48068
|
+
gridSpacing: options.boardTheme?.gridSpacing ?? DEFAULT_BOARD_SURFACE_THEME.gridSpacing,
|
|
48069
|
+
gridLineWidth: options.boardTheme?.gridLineWidth ?? DEFAULT_BOARD_SURFACE_THEME.gridLineWidth,
|
|
48070
|
+
surfaceTexture: options.boardTheme?.surfaceTexture ?? DEFAULT_BOARD_SURFACE_THEME.surfaceTexture
|
|
48071
|
+
};
|
|
48072
|
+
let currentShapeStrokeWidth = options.boardTheme?.shapeStrokeWidth ?? .15;
|
|
48073
|
+
const engine = existingEngine ?? (options.canvas ? new ScrawlEngine(options.canvas, options.document.id, extensionRegistry, assetController, currentBoardTheme, currentShapeStrokeWidth) : null);
|
|
47938
48074
|
const document = engine?.document ?? new BoardDocument(documentId(options.document.id));
|
|
47939
48075
|
const history = engine?.history ?? new History(document);
|
|
47940
48076
|
const createId = options.createId ?? (() => crypto.randomUUID());
|
|
@@ -48418,6 +48554,21 @@ function createBoardControllerWithEngine(options, existingEngine) {
|
|
|
48418
48554
|
changed();
|
|
48419
48555
|
}
|
|
48420
48556
|
},
|
|
48557
|
+
boardTheme: { set(theme) {
|
|
48558
|
+
currentBoardTheme = {
|
|
48559
|
+
color: theme.surface ?? currentBoardTheme.color,
|
|
48560
|
+
gridMode: theme.gridMode ?? currentBoardTheme.gridMode,
|
|
48561
|
+
gridColor: theme.gridColor ?? currentBoardTheme.gridColor,
|
|
48562
|
+
gridSpacing: theme.gridSpacing ?? currentBoardTheme.gridSpacing,
|
|
48563
|
+
gridLineWidth: theme.gridLineWidth ?? currentBoardTheme.gridLineWidth,
|
|
48564
|
+
surfaceTexture: theme.surfaceTexture ?? currentBoardTheme.surfaceTexture
|
|
48565
|
+
};
|
|
48566
|
+
engine?.applyBoardTheme(currentBoardTheme);
|
|
48567
|
+
if (theme.shapeStrokeWidth !== void 0) {
|
|
48568
|
+
currentShapeStrokeWidth = theme.shapeStrokeWidth;
|
|
48569
|
+
engine?.setShapeStrokeWidth(currentShapeStrokeWidth);
|
|
48570
|
+
}
|
|
48571
|
+
} },
|
|
48421
48572
|
view: {
|
|
48422
48573
|
fit() {
|
|
48423
48574
|
assertActive();
|
|
@@ -48722,10 +48873,10 @@ function createBoardControllerWithEngine(options, existingEngine) {
|
|
|
48722
48873
|
gather: (nextView) => engine?.applyGatherView(nextView) ?? false
|
|
48723
48874
|
},
|
|
48724
48875
|
export: {
|
|
48725
|
-
svg: () => engine ? engine.exportSVG() : documentToSVG(document.toJSON(), Date.now(), extensionRegistry),
|
|
48876
|
+
svg: () => engine ? engine.exportSVG() : documentToSVG(document.toJSON(), Date.now(), extensionRegistry, currentBoardTheme.color),
|
|
48726
48877
|
png: (options) => engine ? engine.exportPNG(options?.maxSide, { awaitAssets: options?.awaitAssets }) : Promise.resolve(null),
|
|
48727
48878
|
json: () => document.toJSON(),
|
|
48728
|
-
svgAsync: (svgOptions) => exportDocumentSVGWithAssets(document.toJSON(), Date.now(), extensionRegistry, assetController, svgOptions)
|
|
48879
|
+
svgAsync: (svgOptions) => exportDocumentSVGWithAssets(document.toJSON(), Date.now(), extensionRegistry, assetController, currentBoardTheme.color, svgOptions)
|
|
48729
48880
|
},
|
|
48730
48881
|
assets: { async ingest(bytes, mediaType, name, signal) {
|
|
48731
48882
|
assertActive();
|
|
@@ -49187,4 +49338,4 @@ function createLocalBoard(options) {
|
|
|
49187
49338
|
};
|
|
49188
49339
|
}
|
|
49189
49340
|
//#endregion
|
|
49190
|
-
export { STAMPS, createBoardController, createLocalBoard, isStampKind, stampDataUrl };
|
|
49341
|
+
export { AssetResolutionError, STAMPS, SUPPORTED_ASSET_MEDIA_TYPES, assetRef, clampAssetCacheBytes, cloneCustomObject, createBoardController, createLocalBoard, isAssetRef, isStampKind, stampDataUrl };
|