@scrawl-board/board 0.1.0-beta.1 → 0.1.0-beta.3
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 +32 -16
- package/dist/browser.js +163 -118
- package/dist/core.d.ts +16 -3
- package/dist/core.js +31 -23
- package/dist/index.d.ts +110 -20
- package/dist/index.js +216 -136
- package/dist/react.d.ts +101 -19
- package/dist/react.js +215 -135
- package/dist/styles.css +2 -1
- 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;
|
|
@@ -30,7 +30,7 @@ function clampAssetCacheBytes(value) {
|
|
|
30
30
|
return Math.min(ASSET_CACHE_BYTES_MAX, Math.max(ASSET_CACHE_BYTES_MIN, Math.floor(value)));
|
|
31
31
|
}
|
|
32
32
|
//#endregion
|
|
33
|
-
//#region src/core
|
|
33
|
+
//#region src/core/document/identifiers.ts
|
|
34
34
|
function documentId(value) {
|
|
35
35
|
if (!value) throw new TypeError("DocumentId cannot be empty");
|
|
36
36
|
return value;
|
|
@@ -231,7 +231,7 @@ function validation(message, path) {
|
|
|
231
231
|
return new DocumentRecoveryError("DOCUMENT_VALIDATION_FAILED", message, void 0, path);
|
|
232
232
|
}
|
|
233
233
|
//#endregion
|
|
234
|
-
//#region src/core
|
|
234
|
+
//#region src/core/shapes/affine2d.ts
|
|
235
235
|
var IDENTITY = [
|
|
236
236
|
1,
|
|
237
237
|
0,
|
|
@@ -315,7 +315,7 @@ function avgScale(m) {
|
|
|
315
315
|
return (Math.hypot(m[0], m[1]) + Math.hypot(m[2], m[3])) / 2;
|
|
316
316
|
}
|
|
317
317
|
//#endregion
|
|
318
|
-
//#region src/core
|
|
318
|
+
//#region src/core/document/boardSearch.ts
|
|
319
319
|
function hay(s) {
|
|
320
320
|
return s.toLowerCase();
|
|
321
321
|
}
|
|
@@ -380,7 +380,7 @@ function searchBoard(query, board) {
|
|
|
380
380
|
return hits;
|
|
381
381
|
}
|
|
382
382
|
//#endregion
|
|
383
|
-
//#region src/core
|
|
383
|
+
//#region src/core/shapes/types.ts
|
|
384
384
|
/** Erasure level at which a point counts as fully erased. */
|
|
385
385
|
var ERASE_THRESHOLD = .95;
|
|
386
386
|
function cloneStroke(stroke) {
|
|
@@ -452,7 +452,7 @@ function cloneImage(img) {
|
|
|
452
452
|
return { ...img };
|
|
453
453
|
}
|
|
454
454
|
//#endregion
|
|
455
|
-
//#region src/core
|
|
455
|
+
//#region src/core/document/clusterStore.ts
|
|
456
456
|
/** A stroke joins a cluster last written to within this window at full reach. */
|
|
457
457
|
var RECENT_MS = 8e3;
|
|
458
458
|
/** Older clusters are still joinable, at half the gap threshold. */
|
|
@@ -623,7 +623,7 @@ function clamp$2(v, lo, hi) {
|
|
|
623
623
|
return Math.min(hi, Math.max(lo, v));
|
|
624
624
|
}
|
|
625
625
|
//#endregion
|
|
626
|
-
//#region src/core
|
|
626
|
+
//#region src/core/shapes/extensionTypes.ts
|
|
627
627
|
function cloneCustomObject(object) {
|
|
628
628
|
return {
|
|
629
629
|
...object,
|
|
@@ -633,7 +633,7 @@ function cloneCustomObject(object) {
|
|
|
633
633
|
};
|
|
634
634
|
}
|
|
635
635
|
//#endregion
|
|
636
|
-
//#region src/core
|
|
636
|
+
//#region src/core/shapes/kitchenTimer.ts
|
|
637
637
|
var TIMER_DEFAULT_DURATION_MS = 300 * 1e3;
|
|
638
638
|
function cloneTimer(timer) {
|
|
639
639
|
return { ...timer };
|
|
@@ -683,7 +683,7 @@ function formatTimer(ms) {
|
|
|
683
683
|
return `${Math.floor(total / 60)}:${(total % 60).toString().padStart(2, "0")}`;
|
|
684
684
|
}
|
|
685
685
|
//#endregion
|
|
686
|
-
//#region src/core
|
|
686
|
+
//#region src/core/shapes/itemLock.ts
|
|
687
687
|
/**
|
|
688
688
|
* Apply or clear a lock in place. Unlock always drops the holder so a stale
|
|
689
689
|
* name cannot linger on an unlocked item.
|
|
@@ -720,7 +720,7 @@ function serializeLock(item) {
|
|
|
720
720
|
};
|
|
721
721
|
}
|
|
722
722
|
//#endregion
|
|
723
|
-
//#region src/core
|
|
723
|
+
//#region src/core/document/document.ts
|
|
724
724
|
/**
|
|
725
725
|
* The one place a live Stroke becomes its wire representation — in
|
|
726
726
|
* particular, points collapse from {x,y,pressure,erase?} objects into
|
|
@@ -1279,7 +1279,7 @@ function computeBBox(stroke) {
|
|
|
1279
1279
|
return world;
|
|
1280
1280
|
}
|
|
1281
1281
|
//#endregion
|
|
1282
|
-
//#region src/core
|
|
1282
|
+
//#region src/core/commands/commands.ts
|
|
1283
1283
|
/** One drawing action — a marker stroke, or a shape's strokes as one unit. */
|
|
1284
1284
|
var AddStrokesCommand = class {
|
|
1285
1285
|
constructor(strokes) {
|
|
@@ -1621,7 +1621,7 @@ var LockItemsCommand = class {
|
|
|
1621
1621
|
}
|
|
1622
1622
|
};
|
|
1623
1623
|
//#endregion
|
|
1624
|
-
//#region src/core
|
|
1624
|
+
//#region src/core/history/history.ts
|
|
1625
1625
|
var LIMIT = 200;
|
|
1626
1626
|
var History = class {
|
|
1627
1627
|
constructor(doc) {
|
|
@@ -1682,7 +1682,7 @@ var History = class {
|
|
|
1682
1682
|
}
|
|
1683
1683
|
};
|
|
1684
1684
|
//#endregion
|
|
1685
|
-
//#region src/core
|
|
1685
|
+
//#region src/core/events/ops.ts
|
|
1686
1686
|
var ADDED = [
|
|
1687
1687
|
["added", "strokes"],
|
|
1688
1688
|
["notesAdded", "notes"],
|
|
@@ -1736,10 +1736,18 @@ function changeToOps(change, restoring = false) {
|
|
|
1736
1736
|
return ops;
|
|
1737
1737
|
}
|
|
1738
1738
|
//#endregion
|
|
1739
|
-
//#region src/core
|
|
1739
|
+
//#region src/core/shapes/ribbonEdges.ts
|
|
1740
1740
|
var MIN_WIDTH_FACTOR = .35;
|
|
1741
1741
|
var END_TAPER = .55;
|
|
1742
|
-
|
|
1742
|
+
/**
|
|
1743
|
+
* @param handDrawn Default `true`: ink's organic feel — width follows
|
|
1744
|
+
* per-point pressure, and both ends taper. Pass `false` for a geometric
|
|
1745
|
+
* shape outline, which has no real pressure signal and isn't a pen stroke
|
|
1746
|
+
* with a lift-off: it renders at the exact `baseWidth`, uniformly, with no
|
|
1747
|
+
* taper at its start/end (which, for a closed shape, is the same point —
|
|
1748
|
+
* tapering it would visibly pinch just that one corner).
|
|
1749
|
+
*/
|
|
1750
|
+
function ribbonEdges(points, baseWidth, handDrawn = true) {
|
|
1743
1751
|
const pts = points.length === 1 ? [points[0], {
|
|
1744
1752
|
...points[0],
|
|
1745
1753
|
x: points[0].x + baseWidth * .05
|
|
@@ -1754,8 +1762,8 @@ function ribbonEdges(points, baseWidth) {
|
|
|
1754
1762
|
const len = Math.hypot(dx, dy) || 1;
|
|
1755
1763
|
dx /= len;
|
|
1756
1764
|
dy /= len;
|
|
1757
|
-
let width = baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * pts[i].pressure);
|
|
1758
|
-
if (i === 0 || i === n - 1) width *= END_TAPER;
|
|
1765
|
+
let width = handDrawn ? baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * pts[i].pressure) : baseWidth;
|
|
1766
|
+
if (handDrawn && (i === 0 || i === n - 1)) width *= END_TAPER;
|
|
1759
1767
|
const hw = width / 2;
|
|
1760
1768
|
out[i] = {
|
|
1761
1769
|
lx: pts[i].x - dy * hw,
|
|
@@ -1768,7 +1776,7 @@ function ribbonEdges(points, baseWidth) {
|
|
|
1768
1776
|
return out;
|
|
1769
1777
|
}
|
|
1770
1778
|
//#endregion
|
|
1771
|
-
//#region src/core
|
|
1779
|
+
//#region src/core/document/spatialIndex.ts
|
|
1772
1780
|
var CELL = 8;
|
|
1773
1781
|
var SpatialIndex = class {
|
|
1774
1782
|
constructor(doc) {
|
|
@@ -1841,7 +1849,7 @@ function cellsOf(box) {
|
|
|
1841
1849
|
return keys;
|
|
1842
1850
|
}
|
|
1843
1851
|
//#endregion
|
|
1844
|
-
//#region src/core
|
|
1852
|
+
//#region src/core/shapes/stamps.ts
|
|
1845
1853
|
var STAMPS = [
|
|
1846
1854
|
{
|
|
1847
1855
|
kind: "star",
|
|
@@ -1889,7 +1897,7 @@ function isStampKind(value) {
|
|
|
1889
1897
|
return typeof value === "string" && STAMPS.some((s) => s.kind === value);
|
|
1890
1898
|
}
|
|
1891
1899
|
//#endregion
|
|
1892
|
-
//#region src/
|
|
1900
|
+
//#region src/persistence/serialization/svg.ts
|
|
1893
1901
|
/** Matches the key light used by the board shader and note shadows. */
|
|
1894
1902
|
var LIGHT = {
|
|
1895
1903
|
x: -.25,
|
|
@@ -2031,7 +2039,7 @@ function strokeToPaths(stroke) {
|
|
|
2031
2039
|
y,
|
|
2032
2040
|
pressure,
|
|
2033
2041
|
...erase > 0 ? { erase } : {}
|
|
2034
|
-
})), stroke.baseWidth);
|
|
2042
|
+
})), stroke.baseWidth, stroke.tool !== "shape");
|
|
2035
2043
|
const transform = stroke.matrix ? ` transform="matrix(${fmt(stroke.matrix[0])} ${fmt(-stroke.matrix[1])} ${fmt(-stroke.matrix[2])} ${fmt(stroke.matrix[3])} ${fmt(stroke.matrix[4])} ${fmt(-stroke.matrix[5])})"` : "";
|
|
2036
2044
|
const baseOpacity = stroke.tool === "highlighter" ? .4 : 1;
|
|
2037
2045
|
const paths = [];
|
|
@@ -2256,7 +2264,7 @@ function fmt(n) {
|
|
|
2256
2264
|
return String(Math.round(n * 100) / 100);
|
|
2257
2265
|
}
|
|
2258
2266
|
//#endregion
|
|
2259
|
-
//#region src/core
|
|
2267
|
+
//#region src/core/shapes/sceneGeometry.ts
|
|
2260
2268
|
var EMPTY_BOUNDS = {
|
|
2261
2269
|
x: 0,
|
|
2262
2270
|
y: 0,
|
|
@@ -2320,7 +2328,7 @@ function textCorners(x, y, text, fontSize, align, m) {
|
|
|
2320
2328
|
return rectCorners(x + (align === "center" ? -width / 2 : align === "end" ? -width : 0), y, width, height, m);
|
|
2321
2329
|
}
|
|
2322
2330
|
//#endregion
|
|
2323
|
-
//#region src/core
|
|
2331
|
+
//#region src/core/shapes/customObjects.ts
|
|
2324
2332
|
var FALLBACK_LABEL_MAX_LENGTH = 80;
|
|
2325
2333
|
function toReadonly(object) {
|
|
2326
2334
|
return object;
|
|
@@ -2449,7 +2457,7 @@ function extensionIdFromType(type) {
|
|
|
2449
2457
|
return slash === -1 ? type : type.slice(0, slash);
|
|
2450
2458
|
}
|
|
2451
2459
|
//#endregion
|
|
2452
|
-
//#region src/core
|
|
2460
|
+
//#region src/core/shapes/extensionRegistry.ts
|
|
2453
2461
|
function buildExtensionRegistry(extensions) {
|
|
2454
2462
|
const list = extensions ?? [];
|
|
2455
2463
|
const diagnostics = [];
|
|
@@ -2546,7 +2554,7 @@ function isWellFormedNamespacedId(id) {
|
|
|
2546
2554
|
return isWellFormedId(id) && id.includes("/") && !id.startsWith("/") && !id.endsWith("/");
|
|
2547
2555
|
}
|
|
2548
2556
|
//#endregion
|
|
2549
|
-
//#region src/
|
|
2557
|
+
//#region src/persistence/adapters/assetValidation.ts
|
|
2550
2558
|
function sniffImageMediaType(bytes) {
|
|
2551
2559
|
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";
|
|
2552
2560
|
if (bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) return "image/jpeg";
|
|
@@ -2633,7 +2641,7 @@ function validateDecodedDimensions(width, height) {
|
|
|
2633
2641
|
};
|
|
2634
2642
|
}
|
|
2635
2643
|
//#endregion
|
|
2636
|
-
//#region src/assetController.ts
|
|
2644
|
+
//#region src/persistence/adapters/assetController.ts
|
|
2637
2645
|
var FAILURE_COALESCE_MS = 4e3;
|
|
2638
2646
|
var AssetController = class {
|
|
2639
2647
|
constructor(resolver, options) {
|
|
@@ -2812,7 +2820,7 @@ function toAssetResolutionError(cause, ref) {
|
|
|
2812
2820
|
return new AssetResolutionError("unknown", true, cause instanceof Error ? cause.message : String(cause), ref);
|
|
2813
2821
|
}
|
|
2814
2822
|
//#endregion
|
|
2815
|
-
//#region src/
|
|
2823
|
+
//#region src/persistence/serialization/assetRefCollector.ts
|
|
2816
2824
|
/** Every unique Asset reference in the document, and which object(s) use it. */
|
|
2817
2825
|
function collectDocumentAssetRefs(doc, registry) {
|
|
2818
2826
|
const refs = /* @__PURE__ */ new Map();
|
|
@@ -2839,7 +2847,7 @@ function collectSceneAssetRefs(node, objectId, add) {
|
|
|
2839
2847
|
if (node.kind === "group") for (const child of node.children) collectSceneAssetRefs(child, objectId, add);
|
|
2840
2848
|
}
|
|
2841
2849
|
//#endregion
|
|
2842
|
-
//#region src/assetExport.ts
|
|
2850
|
+
//#region src/persistence/serialization/assetExport.ts
|
|
2843
2851
|
/** Strict mode's rejection — never contains credentials, a fetchable location, or a raw resolver message. */
|
|
2844
2852
|
var SvgAssetExportError = class extends Error {
|
|
2845
2853
|
constructor(missingAssets) {
|
|
@@ -2895,7 +2903,7 @@ function toDataUri(bytes, mediaType) {
|
|
|
2895
2903
|
return `data:${mediaType};base64,${btoa(binary)}`;
|
|
2896
2904
|
}
|
|
2897
2905
|
//#endregion
|
|
2898
|
-
//#region src/assetIngestion.ts
|
|
2906
|
+
//#region src/persistence/adapters/assetIngestion.ts
|
|
2899
2907
|
async function decodeDimensionsWithImageBitmap(bytes, mediaType) {
|
|
2900
2908
|
const blob = new Blob([bytes], { type: mediaType });
|
|
2901
2909
|
let bitmap;
|
|
@@ -35541,7 +35549,7 @@ var WebGLRenderer = class {
|
|
|
35541
35549
|
}
|
|
35542
35550
|
};
|
|
35543
35551
|
//#endregion
|
|
35544
|
-
//#region
|
|
35552
|
+
//#region src/core/events/auditEvents.ts
|
|
35545
35553
|
var BY_LABEL = {
|
|
35546
35554
|
draw: {
|
|
35547
35555
|
action: "create",
|
|
@@ -35661,7 +35669,7 @@ function lowerFirst(s) {
|
|
|
35661
35669
|
return s.charAt(0).toLowerCase() + s.slice(1);
|
|
35662
35670
|
}
|
|
35663
35671
|
//#endregion
|
|
35664
|
-
//#region
|
|
35672
|
+
//#region src/renderer/shapes/AssetTextureCache.ts
|
|
35665
35673
|
var AssetTextureCache = class {
|
|
35666
35674
|
constructor(assetController) {
|
|
35667
35675
|
this.assetController = assetController;
|
|
@@ -35752,7 +35760,7 @@ function toDecodeError(cause, ref) {
|
|
|
35752
35760
|
return new AssetResolutionError("decode-failed", false, cause instanceof Error ? cause.message : String(cause), ref);
|
|
35753
35761
|
}
|
|
35754
35762
|
//#endregion
|
|
35755
|
-
//#region
|
|
35763
|
+
//#region src/renderer/canvas/raycast.ts
|
|
35756
35764
|
var origin$1 = new Vector3();
|
|
35757
35765
|
var target = new Vector3();
|
|
35758
35766
|
function pointerToBoard(clientX, clientY, canvas, camera) {
|
|
@@ -35783,7 +35791,7 @@ function pointerToPlane(clientX, clientY, canvas, camera, z) {
|
|
|
35783
35791
|
};
|
|
35784
35792
|
}
|
|
35785
35793
|
//#endregion
|
|
35786
|
-
//#region
|
|
35794
|
+
//#region src/renderer/canvas/fog.ts
|
|
35787
35795
|
function createFogUniforms(color) {
|
|
35788
35796
|
return {
|
|
35789
35797
|
uFogColor: { value: new Color(color) },
|
|
@@ -35804,18 +35812,24 @@ var FOG_GLSL = `
|
|
|
35804
35812
|
}
|
|
35805
35813
|
`;
|
|
35806
35814
|
//#endregion
|
|
35807
|
-
//#region
|
|
35815
|
+
//#region src/renderer/canvas/boardSurface.ts
|
|
35808
35816
|
var GRID_MODE_CODE = {
|
|
35809
35817
|
none: 0,
|
|
35810
35818
|
line: 1,
|
|
35811
35819
|
dot: 2
|
|
35812
35820
|
};
|
|
35821
|
+
var TEXTURE_CODE = {
|
|
35822
|
+
flat: 0,
|
|
35823
|
+
textured: 1
|
|
35824
|
+
};
|
|
35813
35825
|
/** Used when a caller doesn't supply one (e.g. ScrawlEngine constructed without a Host theme). */
|
|
35814
35826
|
var DEFAULT_BOARD_SURFACE_THEME = {
|
|
35815
35827
|
color: "#f5f5f3",
|
|
35816
35828
|
gridMode: "none",
|
|
35817
35829
|
gridColor: "#deded8",
|
|
35818
|
-
gridSpacing: 5
|
|
35830
|
+
gridSpacing: 5,
|
|
35831
|
+
gridLineWidth: 1,
|
|
35832
|
+
surfaceTexture: "flat"
|
|
35819
35833
|
};
|
|
35820
35834
|
/** Grid alpha fades out below this on-screen spacing (px) — otherwise it aliases into a solid wash when zoomed out. */
|
|
35821
35835
|
var GRID_FADE_MIN_PX = 3;
|
|
@@ -35836,8 +35850,10 @@ var FRAGMENT = `
|
|
|
35836
35850
|
uniform float uGridMode;
|
|
35837
35851
|
uniform vec3 uGridColor;
|
|
35838
35852
|
uniform float uGridSpacing;
|
|
35839
|
-
uniform float
|
|
35853
|
+
uniform float uWorldPerPixel;
|
|
35854
|
+
uniform float uGridLineWidthPx;
|
|
35840
35855
|
uniform float uGridFade;
|
|
35856
|
+
uniform float uSurfaceTexture;
|
|
35841
35857
|
varying vec3 vWorldPos;
|
|
35842
35858
|
|
|
35843
35859
|
float hash(vec2 p) {
|
|
@@ -35857,37 +35873,45 @@ var FRAGMENT = `
|
|
|
35857
35873
|
${FOG_GLSL}
|
|
35858
35874
|
|
|
35859
35875
|
// Distance from p's nearest axis-aligned grid line, in world units.
|
|
35876
|
+
// A crisp, ~uGridLineWidthPx-wide line: full alpha to its half-width, a
|
|
35877
|
+
// ~1px anti-aliased falloff beyond that — not a wide, soft-edged band.
|
|
35860
35878
|
float gridLineAlpha(vec2 p) {
|
|
35861
35879
|
vec2 cell = mod(p, uGridSpacing);
|
|
35862
35880
|
vec2 distToLine = min(cell, uGridSpacing - cell);
|
|
35863
|
-
|
|
35881
|
+
float halfWidth = 0.5 * uGridLineWidthPx * uWorldPerPixel;
|
|
35882
|
+
float aa = uWorldPerPixel;
|
|
35883
|
+
vec2 lineAlpha2 = 1.0 - smoothstep(halfWidth - aa * 0.5, halfWidth + aa * 0.5, distToLine);
|
|
35864
35884
|
return max(lineAlpha2.x, lineAlpha2.y);
|
|
35865
35885
|
}
|
|
35866
35886
|
|
|
35867
|
-
// Distance from p's nearest grid intersection, in world units.
|
|
35887
|
+
// Distance from p's nearest grid intersection, in world units. Dot radius
|
|
35888
|
+
// matches the configured line width in pixels, same anti-aliasing.
|
|
35868
35889
|
float gridDotAlpha(vec2 p) {
|
|
35869
35890
|
vec2 cell = mod(p, uGridSpacing) - uGridSpacing * 0.5;
|
|
35870
35891
|
float dist = length(cell);
|
|
35871
|
-
float radius =
|
|
35872
|
-
|
|
35892
|
+
float radius = uGridLineWidthPx * uWorldPerPixel;
|
|
35893
|
+
float aa = uWorldPerPixel;
|
|
35894
|
+
return 1.0 - smoothstep(radius - aa * 0.5, radius + aa * 0.5, dist);
|
|
35873
35895
|
}
|
|
35874
35896
|
|
|
35875
35897
|
void main() {
|
|
35876
35898
|
vec3 col = uColor;
|
|
35877
35899
|
|
|
35878
|
-
|
|
35879
|
-
|
|
35880
|
-
|
|
35900
|
+
if (uSurfaceTexture > 0.5) {
|
|
35901
|
+
// Melamine micro-texture: two octaves of value noise, +-1.75% brightness.
|
|
35902
|
+
float n = vnoise(vWorldPos.xy * 2.3) * 0.6 + vnoise(vWorldPos.xy * 9.7) * 0.4;
|
|
35903
|
+
col *= 1.0 + (n - 0.5) * 0.035;
|
|
35881
35904
|
|
|
35882
|
-
|
|
35883
|
-
|
|
35884
|
-
|
|
35885
|
-
|
|
35886
|
-
|
|
35887
|
-
|
|
35888
|
-
|
|
35889
|
-
|
|
35890
|
-
|
|
35905
|
+
// Satin sheen: Fresnel rim + a specular band from the key light. Both
|
|
35906
|
+
// are ~zero in the straight-on working view and bloom as the camera tilts.
|
|
35907
|
+
vec3 V = normalize(cameraPosition - vWorldPos);
|
|
35908
|
+
vec3 N = vec3(0.0, 0.0, 1.0);
|
|
35909
|
+
float fres = pow(1.0 - max(dot(N, V), 0.0), 3.0);
|
|
35910
|
+
vec3 L = normalize(vec3(-0.25, 0.4, 0.88));
|
|
35911
|
+
vec3 H = normalize(L + V);
|
|
35912
|
+
float spec = pow(max(dot(N, H), 0.0), 90.0);
|
|
35913
|
+
col += (fres * 0.18 + spec * 0.06) * vec3(1.0);
|
|
35914
|
+
}
|
|
35891
35915
|
|
|
35892
35916
|
if (uGridMode > 0.5 && uGridFade > 0.001) {
|
|
35893
35917
|
float gridAlpha = (uGridMode < 1.5 ? gridLineAlpha(vWorldPos.xy) : gridDotAlpha(vWorldPos.xy)) * uGridFade;
|
|
@@ -35907,8 +35931,10 @@ function createBoardSurface(fog, theme) {
|
|
|
35907
35931
|
uGridMode: { value: GRID_MODE_CODE[theme.gridMode] },
|
|
35908
35932
|
uGridColor: { value: new Color(theme.gridColor) },
|
|
35909
35933
|
uGridSpacing: { value: Math.max(.001, theme.gridSpacing) },
|
|
35910
|
-
|
|
35934
|
+
uGridLineWidthPx: { value: Math.max(.1, theme.gridLineWidth) },
|
|
35935
|
+
uWorldPerPixel: { value: .05 },
|
|
35911
35936
|
uGridFade: { value: 0 },
|
|
35937
|
+
uSurfaceTexture: { value: TEXTURE_CODE[theme.surfaceTexture] },
|
|
35912
35938
|
...fog
|
|
35913
35939
|
}
|
|
35914
35940
|
});
|
|
@@ -35916,13 +35942,15 @@ function createBoardSurface(fog, theme) {
|
|
|
35916
35942
|
mesh.name = "board-surface";
|
|
35917
35943
|
return mesh;
|
|
35918
35944
|
}
|
|
35919
|
-
/** Applies a theme change (initial or live) — everything except the per-frame fade/
|
|
35945
|
+
/** Applies a theme change (initial or live) — everything except the per-frame fade/pixel-ratio. */
|
|
35920
35946
|
function setBoardSurfaceTheme(mesh, theme) {
|
|
35921
35947
|
const uniforms = mesh.material.uniforms;
|
|
35922
35948
|
uniforms.uColor.value.set(theme.color);
|
|
35923
35949
|
uniforms.uGridMode.value = GRID_MODE_CODE[theme.gridMode];
|
|
35924
35950
|
uniforms.uGridColor.value.set(theme.gridColor);
|
|
35925
35951
|
uniforms.uGridSpacing.value = Math.max(.001, theme.gridSpacing);
|
|
35952
|
+
uniforms.uGridLineWidthPx.value = Math.max(.1, theme.gridLineWidth);
|
|
35953
|
+
uniforms.uSurfaceTexture.value = TEXTURE_CODE[theme.surfaceTexture];
|
|
35926
35954
|
}
|
|
35927
35955
|
/** Keep the plane centered under the view, larger than the fog horizon, and the grid's on-screen weight/fade current. */
|
|
35928
35956
|
function updateBoardSurface(mesh, centerX, centerY, fogFar, worldPerPixel) {
|
|
@@ -35930,7 +35958,7 @@ function updateBoardSurface(mesh, centerX, centerY, fogFar, worldPerPixel) {
|
|
|
35930
35958
|
const size = fogFar * 2.5;
|
|
35931
35959
|
mesh.scale.set(size, size, 1);
|
|
35932
35960
|
const uniforms = mesh.material.uniforms;
|
|
35933
|
-
uniforms.
|
|
35961
|
+
uniforms.uWorldPerPixel.value = worldPerPixel;
|
|
35934
35962
|
const t = (uniforms.uGridSpacing.value / Math.max(1e-6, worldPerPixel) - GRID_FADE_MIN_PX) / (GRID_FADE_MAX_PX - GRID_FADE_MIN_PX);
|
|
35935
35963
|
uniforms.uGridFade.value = MathUtils.clamp(t, 0, 1);
|
|
35936
35964
|
}
|
|
@@ -36103,7 +36131,7 @@ var CameraRig = class {
|
|
|
36103
36131
|
}
|
|
36104
36132
|
};
|
|
36105
36133
|
//#endregion
|
|
36106
|
-
//#region
|
|
36134
|
+
//#region src/renderer/canvas/scene.ts
|
|
36107
36135
|
var SceneRig = class {
|
|
36108
36136
|
constructor(canvas, boardTheme) {
|
|
36109
36137
|
this.renderer = new WebGLRenderer({
|
|
@@ -36150,7 +36178,7 @@ var SceneRig = class {
|
|
|
36150
36178
|
}
|
|
36151
36179
|
};
|
|
36152
36180
|
//#endregion
|
|
36153
|
-
//#region
|
|
36181
|
+
//#region src/renderer/shapes/inkMaterial.ts
|
|
36154
36182
|
var VERTEX = `
|
|
36155
36183
|
attribute float aAlpha;
|
|
36156
36184
|
varying vec2 vUv;
|
|
@@ -36281,15 +36309,15 @@ var InkMaterials = class {
|
|
|
36281
36309
|
}
|
|
36282
36310
|
};
|
|
36283
36311
|
//#endregion
|
|
36284
|
-
//#region
|
|
36312
|
+
//#region src/renderer/shapes/ribbonGeometry.ts
|
|
36285
36313
|
/** Ink floats a hair above the board to avoid z-fighting with the surface. */
|
|
36286
36314
|
var INK_Z = .02;
|
|
36287
36315
|
/** Highlighter always renders under opaque ink (build prompt §4.1). */
|
|
36288
36316
|
var HIGHLIGHT_Z = .016;
|
|
36289
36317
|
/** Ghosts sit under everything so live strokes always read on top. */
|
|
36290
36318
|
var GHOST_Z = .012;
|
|
36291
|
-
function buildRibbonGeometry(points, baseWidth, z = INK_Z) {
|
|
36292
|
-
const edges = ribbonEdges(points, baseWidth);
|
|
36319
|
+
function buildRibbonGeometry(points, baseWidth, z = INK_Z, handDrawn = true) {
|
|
36320
|
+
const edges = ribbonEdges(points, baseWidth, handDrawn);
|
|
36293
36321
|
const n = edges.length;
|
|
36294
36322
|
const positions = new Float32Array(n * 2 * 3);
|
|
36295
36323
|
const uvs = new Float32Array(n * 2 * 2);
|
|
@@ -36332,8 +36360,10 @@ function buildRibbonGeometry(points, baseWidth, z = INK_Z) {
|
|
|
36332
36360
|
return geometry;
|
|
36333
36361
|
}
|
|
36334
36362
|
//#endregion
|
|
36335
|
-
//#region
|
|
36363
|
+
//#region src/renderer/shapes/strokeRenderer.ts
|
|
36336
36364
|
var strokeZ = (stroke) => stroke.tool === "highlighter" ? HIGHLIGHT_Z : INK_Z;
|
|
36365
|
+
/** Shapes (rect/ellipse/line/arrow/...) are geometric outlines, not pressure-sensitive ink. */
|
|
36366
|
+
var handDrawn = (stroke) => stroke.tool !== "shape";
|
|
36337
36367
|
/**
|
|
36338
36368
|
* Derives ink meshes from the document by subscription — the document never
|
|
36339
36369
|
* knows the renderer exists. Also hosts session-only ghosts (decision Q7)
|
|
@@ -36358,12 +36388,12 @@ var StrokeRenderer = class {
|
|
|
36358
36388
|
this.ghostGroup.visible = visible;
|
|
36359
36389
|
}
|
|
36360
36390
|
/** Leave a smeared residue of an erased span (never persisted). */
|
|
36361
|
-
addGhost(points, color, baseWidth) {
|
|
36391
|
+
addGhost(points, color, baseWidth, handDrawn = true) {
|
|
36362
36392
|
if (points.length < 2) return;
|
|
36363
36393
|
const mesh = new Mesh(buildRibbonGeometry(points.map((p) => ({
|
|
36364
36394
|
...p,
|
|
36365
36395
|
erase: 0
|
|
36366
|
-
})), baseWidth * 1.7, GHOST_Z), this.materials.ghost(color));
|
|
36396
|
+
})), baseWidth * 1.7, GHOST_Z, handDrawn), this.materials.ghost(color));
|
|
36367
36397
|
this.ghostGroup.add(mesh);
|
|
36368
36398
|
}
|
|
36369
36399
|
/**
|
|
@@ -36375,7 +36405,7 @@ var StrokeRenderer = class {
|
|
|
36375
36405
|
if (this.meshes.has(stroke.id) || this.retiredLive.has(stroke.id) || stroke.points.length < 2) return;
|
|
36376
36406
|
this.liveUser.set(stroke.id, userId);
|
|
36377
36407
|
const existing = this.liveMeshes.get(stroke.id);
|
|
36378
|
-
const geometry = buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke));
|
|
36408
|
+
const geometry = buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke), handDrawn(stroke));
|
|
36379
36409
|
if (existing) {
|
|
36380
36410
|
existing.geometry.dispose();
|
|
36381
36411
|
existing.geometry = geometry;
|
|
@@ -36435,7 +36465,7 @@ var StrokeRenderer = class {
|
|
|
36435
36465
|
const mesh = this.meshes.get(stroke.id);
|
|
36436
36466
|
if (mesh) {
|
|
36437
36467
|
mesh.geometry.dispose();
|
|
36438
|
-
mesh.geometry = buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke));
|
|
36468
|
+
mesh.geometry = buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke), handDrawn(stroke));
|
|
36439
36469
|
}
|
|
36440
36470
|
}
|
|
36441
36471
|
for (const stroke of change.transformed) {
|
|
@@ -36444,7 +36474,7 @@ var StrokeRenderer = class {
|
|
|
36444
36474
|
}
|
|
36445
36475
|
}
|
|
36446
36476
|
create(stroke) {
|
|
36447
|
-
const mesh = new Mesh(buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke)), stroke.tool === "highlighter" ? this.materials.highlight(stroke.color) : this.materials.get(stroke.color));
|
|
36477
|
+
const mesh = new Mesh(buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke), handDrawn(stroke)), stroke.tool === "highlighter" ? this.materials.highlight(stroke.color) : this.materials.get(stroke.color));
|
|
36448
36478
|
mesh.matrixAutoUpdate = false;
|
|
36449
36479
|
syncMatrix(mesh, stroke);
|
|
36450
36480
|
this.meshes.set(stroke.id, mesh);
|
|
@@ -36465,7 +36495,7 @@ function syncMatrix(mesh, stroke) {
|
|
|
36465
36495
|
mesh.matrixWorldNeedsUpdate = true;
|
|
36466
36496
|
}
|
|
36467
36497
|
//#endregion
|
|
36468
|
-
//#region
|
|
36498
|
+
//#region src/renderer/shapes/markerProp.ts
|
|
36469
36499
|
var BODY_LENGTH = 11;
|
|
36470
36500
|
var BODY_RADIUS = .85;
|
|
36471
36501
|
var TIP_LENGTH = 1.6;
|
|
@@ -36561,7 +36591,7 @@ var MarkerProp = class {
|
|
|
36561
36591
|
}
|
|
36562
36592
|
};
|
|
36563
36593
|
//#endregion
|
|
36564
|
-
//#region
|
|
36594
|
+
//#region src/renderer/shapes/noteGeometry.ts
|
|
36565
36595
|
/**
|
|
36566
36596
|
* Height of the sheet above the board at (u, v) ∈ [0,1]².
|
|
36567
36597
|
* v = 1 is the adhesive edge (always 0); u = 1 is the peel corner side.
|
|
@@ -36605,7 +36635,7 @@ function applyCurl(geometry, curl, peel) {
|
|
|
36605
36635
|
color.needsUpdate = true;
|
|
36606
36636
|
}
|
|
36607
36637
|
//#endregion
|
|
36608
|
-
//#region
|
|
36638
|
+
//#region src/renderer/shapes/stickyNotes.ts
|
|
36609
36639
|
var TEXTURE_SIZE = 512;
|
|
36610
36640
|
var FOCUS_COLOR$4 = 2450411;
|
|
36611
36641
|
var CURL = .3;
|
|
@@ -43181,7 +43211,7 @@ SYNCABLE_PROPS.forEach((prop) => {
|
|
|
43181
43211
|
new Box3();
|
|
43182
43212
|
new Color();
|
|
43183
43213
|
//#endregion
|
|
43184
|
-
//#region
|
|
43214
|
+
//#region src/renderer/shapes/tables.ts
|
|
43185
43215
|
var FONT_URL$2 = "/fonts/inter-medium.woff";
|
|
43186
43216
|
var TABLE_Z = .012;
|
|
43187
43217
|
var HEADER_Z = .014;
|
|
@@ -43530,7 +43560,7 @@ var TablesRenderer = class {
|
|
|
43530
43560
|
}
|
|
43531
43561
|
};
|
|
43532
43562
|
//#endregion
|
|
43533
|
-
//#region
|
|
43563
|
+
//#region src/renderer/text/textBlocks.ts
|
|
43534
43564
|
var FONT_URL$1 = "/fonts/inter-medium.woff";
|
|
43535
43565
|
var TEXT_Z = .02;
|
|
43536
43566
|
var FOCUS_COLOR$2 = 2450411;
|
|
@@ -43634,7 +43664,7 @@ var TextBlocksRenderer = class {
|
|
|
43634
43664
|
}
|
|
43635
43665
|
};
|
|
43636
43666
|
//#endregion
|
|
43637
|
-
//#region
|
|
43667
|
+
//#region src/renderer/shapes/comments.ts
|
|
43638
43668
|
var PIN_Z = .04;
|
|
43639
43669
|
var PIN_WIDTH = 3.6;
|
|
43640
43670
|
var PIN_HEIGHT = 3.6;
|
|
@@ -43802,7 +43832,7 @@ function drawBubblePath(ctx, x, y, w, h, r, tailX, tailY, tailW) {
|
|
|
43802
43832
|
ctx.closePath();
|
|
43803
43833
|
}
|
|
43804
43834
|
//#endregion
|
|
43805
|
-
//#region
|
|
43835
|
+
//#region src/renderer/shapes/images.ts
|
|
43806
43836
|
var IMAGE_Z = .015;
|
|
43807
43837
|
var FRAME_BORDER = .4;
|
|
43808
43838
|
var FOCUS_COLOR$1 = 2450411;
|
|
@@ -44012,7 +44042,7 @@ function applyPlaceholderTexture(visual, label) {
|
|
|
44012
44042
|
visual.imageMesh.material.needsUpdate = true;
|
|
44013
44043
|
}
|
|
44014
44044
|
//#endregion
|
|
44015
|
-
//#region
|
|
44045
|
+
//#region src/renderer/shapes/kitchenTimers.ts
|
|
44016
44046
|
var FACE_SIZE = 256;
|
|
44017
44047
|
var BODY_COLOR = 14753096;
|
|
44018
44048
|
var KNOB_COLOR = 16448250;
|
|
@@ -44210,7 +44240,7 @@ function renderTimerFace(timer, canvas, now = Date.now()) {
|
|
|
44210
44240
|
return c;
|
|
44211
44241
|
}
|
|
44212
44242
|
//#endregion
|
|
44213
|
-
//#region
|
|
44243
|
+
//#region src/renderer/shapes/customObjects.ts
|
|
44214
44244
|
var FONT_URL = "/fonts/inter-medium.woff";
|
|
44215
44245
|
var CUSTOM_Z = .02;
|
|
44216
44246
|
var FOCUS_COLOR = 2450411;
|
|
@@ -44572,7 +44602,7 @@ function disposeObject3D(obj) {
|
|
|
44572
44602
|
});
|
|
44573
44603
|
}
|
|
44574
44604
|
//#endregion
|
|
44575
|
-
//#region
|
|
44605
|
+
//#region src/interaction/pointer/hitTest.ts
|
|
44576
44606
|
/** Topmost (most recently added) stroke under the point, or null. */
|
|
44577
44607
|
function hitStroke(doc, index, p, slop) {
|
|
44578
44608
|
const candidates = index.query(p.x - slop, p.y - slop, p.x + slop, p.y + slop);
|
|
@@ -44583,9 +44613,10 @@ function hitStroke(doc, index, p, slop) {
|
|
|
44583
44613
|
const scale = m ? avgScale(m) : 1;
|
|
44584
44614
|
let prev = null;
|
|
44585
44615
|
let prevWidth = 0;
|
|
44616
|
+
const handDrawn = stroke.tool !== "shape";
|
|
44586
44617
|
for (const point of stroke.points) {
|
|
44587
44618
|
const world = m ? apply(m, point) : point;
|
|
44588
|
-
const width = stroke.baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * point.pressure) * scale;
|
|
44619
|
+
const width = (handDrawn ? stroke.baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * point.pressure) : stroke.baseWidth) * scale;
|
|
44589
44620
|
if (prev) {
|
|
44590
44621
|
const reach = Math.max(width, prevWidth) / 2 + slop;
|
|
44591
44622
|
if (distToSegmentSq(p, prev, world) <= reach * reach) {
|
|
@@ -44613,7 +44644,7 @@ function distToSegmentSq(p, a, b) {
|
|
|
44613
44644
|
return dx * dx + dy * dy;
|
|
44614
44645
|
}
|
|
44615
44646
|
//#endregion
|
|
44616
|
-
//#region
|
|
44647
|
+
//#region src/interaction/tools/eraserTool.ts
|
|
44617
44648
|
var DECAY_PER_PASS = .4;
|
|
44618
44649
|
/** A point can take another decay pass after this long — rubbing works. */
|
|
44619
44650
|
var REARM_MS = 250;
|
|
@@ -44719,7 +44750,7 @@ var EraserTool = class {
|
|
|
44719
44750
|
...p,
|
|
44720
44751
|
...apply(m, p)
|
|
44721
44752
|
})) : run.points;
|
|
44722
|
-
this.ctx.renderer.addGhost(world, current.color, current.baseWidth * (m ? avgScale(m) : 1));
|
|
44753
|
+
this.ctx.renderer.addGhost(world, current.color, current.baseWidth * (m ? avgScale(m) : 1), current.tool !== "shape");
|
|
44723
44754
|
}
|
|
44724
44755
|
}
|
|
44725
44756
|
doc.removeStrokes(before.map((s) => s.id));
|
|
@@ -44741,7 +44772,7 @@ function partitionByErasure(points) {
|
|
|
44741
44772
|
return runs;
|
|
44742
44773
|
}
|
|
44743
44774
|
//#endregion
|
|
44744
|
-
//#region
|
|
44775
|
+
//#region src/interaction/tools/noteDrag.ts
|
|
44745
44776
|
/**
|
|
44746
44777
|
* Drag the focused note in X/Y. The grab is resolved on the note's own
|
|
44747
44778
|
* z-plane, not the board, so the note doesn't jump under a tilted ray.
|
|
@@ -44808,7 +44839,7 @@ var NoteDragState = class {
|
|
|
44808
44839
|
}
|
|
44809
44840
|
};
|
|
44810
44841
|
//#endregion
|
|
44811
|
-
//#region
|
|
44842
|
+
//#region src/interaction/tools/imageDrag.ts
|
|
44812
44843
|
/** Drag the focused image in X/Y; one command per drag. */
|
|
44813
44844
|
var ImageDragState = class {
|
|
44814
44845
|
constructor(ctx) {
|
|
@@ -44860,7 +44891,7 @@ var ImageDragState = class {
|
|
|
44860
44891
|
}
|
|
44861
44892
|
};
|
|
44862
44893
|
//#endregion
|
|
44863
|
-
//#region
|
|
44894
|
+
//#region src/interaction/tools/timerDrag.ts
|
|
44864
44895
|
/** Drag the focused timer in X/Y; one command per drag. */
|
|
44865
44896
|
var TimerDragState = class {
|
|
44866
44897
|
constructor(ctx) {
|
|
@@ -44912,7 +44943,7 @@ var TimerDragState = class {
|
|
|
44912
44943
|
}
|
|
44913
44944
|
};
|
|
44914
44945
|
//#endregion
|
|
44915
|
-
//#region
|
|
44946
|
+
//#region src/interaction/tools/shapeGeometry.ts
|
|
44916
44947
|
/** Uniform synthetic pressure; the ink shader supplies the hand-drawn feel. */
|
|
44917
44948
|
var PRESSURE = .7;
|
|
44918
44949
|
var MIN_ELLIPSE_SEGMENTS = 24;
|
|
@@ -44952,10 +44983,7 @@ function rect(a, b, step) {
|
|
|
44952
44983
|
a
|
|
44953
44984
|
];
|
|
44954
44985
|
const out = [];
|
|
44955
|
-
for (let i = 0; i < corners.length - 1; i++)
|
|
44956
|
-
const edge = sampleSegment(corners[i], corners[i + 1], step);
|
|
44957
|
-
out.push(...i === 0 ? edge : edge.slice(1));
|
|
44958
|
-
}
|
|
44986
|
+
for (let i = 0; i < corners.length - 1; i++) out.push(...sampleSegment(corners[i], corners[i + 1], step));
|
|
44959
44987
|
return out;
|
|
44960
44988
|
}
|
|
44961
44989
|
function ellipse(a, b, step) {
|
|
@@ -45026,10 +45054,7 @@ function polygon(sides, a, b, step, startAngle = -Math.PI / 2) {
|
|
|
45026
45054
|
}
|
|
45027
45055
|
vertices.push(vertices[0]);
|
|
45028
45056
|
const out = [];
|
|
45029
|
-
for (let i = 0; i < vertices.length - 1; i++)
|
|
45030
|
-
const edge = sampleSegment(vertices[i], vertices[i + 1], step);
|
|
45031
|
-
out.push(...i === 0 ? edge : edge.slice(1));
|
|
45032
|
-
}
|
|
45057
|
+
for (let i = 0; i < vertices.length - 1; i++) out.push(...sampleSegment(vertices[i], vertices[i + 1], step));
|
|
45033
45058
|
return out;
|
|
45034
45059
|
}
|
|
45035
45060
|
function star(points, a, b, step) {
|
|
@@ -45053,10 +45078,7 @@ function star(points, a, b, step) {
|
|
|
45053
45078
|
}
|
|
45054
45079
|
vertices.push(vertices[0]);
|
|
45055
45080
|
const out = [];
|
|
45056
|
-
for (let i = 0; i < vertices.length - 1; i++)
|
|
45057
|
-
const edge = sampleSegment(vertices[i], vertices[i + 1], step);
|
|
45058
|
-
out.push(...i === 0 ? edge : edge.slice(1));
|
|
45059
|
-
}
|
|
45081
|
+
for (let i = 0; i < vertices.length - 1; i++) out.push(...sampleSegment(vertices[i], vertices[i + 1], step));
|
|
45060
45082
|
return out;
|
|
45061
45083
|
}
|
|
45062
45084
|
function heart(a, b, step) {
|
|
@@ -45101,7 +45123,7 @@ function snappedEnd(a, b) {
|
|
|
45101
45123
|
};
|
|
45102
45124
|
}
|
|
45103
45125
|
//#endregion
|
|
45104
|
-
//#region
|
|
45126
|
+
//#region src/interaction/tools/shapeTool.ts
|
|
45105
45127
|
var MIN_DRAG_PX$1 = 4;
|
|
45106
45128
|
/** Drag out a shape; live preview through the real ink material. */
|
|
45107
45129
|
var ShapeTool = class {
|
|
@@ -45132,7 +45154,8 @@ var ShapeTool = class {
|
|
|
45132
45154
|
const strokes = this.polylines.map((points) => ({
|
|
45133
45155
|
id: crypto.randomUUID(),
|
|
45134
45156
|
color: this.ctx.inkColor(),
|
|
45135
|
-
baseWidth: this.ctx.
|
|
45157
|
+
baseWidth: this.ctx.shapeStrokeWidth(),
|
|
45158
|
+
tool: "shape",
|
|
45136
45159
|
points
|
|
45137
45160
|
}));
|
|
45138
45161
|
this.ctx.clusters.assign(strokes[0]);
|
|
@@ -45166,13 +45189,13 @@ var ShapeTool = class {
|
|
|
45166
45189
|
this.previews[i].visible = polyline !== void 0;
|
|
45167
45190
|
if (polyline) {
|
|
45168
45191
|
this.previews[i].geometry.dispose();
|
|
45169
|
-
this.previews[i].geometry = buildRibbonGeometry(polyline, this.ctx.
|
|
45192
|
+
this.previews[i].geometry = buildRibbonGeometry(polyline, this.ctx.shapeStrokeWidth(), void 0, false);
|
|
45170
45193
|
}
|
|
45171
45194
|
}
|
|
45172
45195
|
}
|
|
45173
45196
|
};
|
|
45174
45197
|
//#endregion
|
|
45175
|
-
//#region
|
|
45198
|
+
//#region src/interaction/tools/tableDrag.ts
|
|
45176
45199
|
function getHandleCursor(handle) {
|
|
45177
45200
|
switch (handle) {
|
|
45178
45201
|
case "nw":
|
|
@@ -45292,7 +45315,7 @@ var TableDragState = class {
|
|
|
45292
45315
|
}
|
|
45293
45316
|
};
|
|
45294
45317
|
//#endregion
|
|
45295
|
-
//#region
|
|
45318
|
+
//#region src/interaction/tools/tableTool.ts
|
|
45296
45319
|
var MIN_DRAG_PX = 6;
|
|
45297
45320
|
var TableTool = class {
|
|
45298
45321
|
constructor(ctx) {
|
|
@@ -45423,7 +45446,7 @@ var TableTool = class {
|
|
|
45423
45446
|
}
|
|
45424
45447
|
};
|
|
45425
45448
|
//#endregion
|
|
45426
|
-
//#region
|
|
45449
|
+
//#region src/interaction/tools/commentTool.ts
|
|
45427
45450
|
/** Click on the board to drop a new comment pin or open an existing one. */
|
|
45428
45451
|
var CommentTool = class {
|
|
45429
45452
|
constructor(ctx) {
|
|
@@ -45458,7 +45481,7 @@ var CommentTool = class {
|
|
|
45458
45481
|
}
|
|
45459
45482
|
};
|
|
45460
45483
|
//#endregion
|
|
45461
|
-
//#region
|
|
45484
|
+
//#region src/interaction/tools/textDrag.ts
|
|
45462
45485
|
/** Drag the focused text block in X/Y; one command per drag. */
|
|
45463
45486
|
var TextDragState = class {
|
|
45464
45487
|
constructor(ctx) {
|
|
@@ -45510,7 +45533,7 @@ var TextDragState = class {
|
|
|
45510
45533
|
}
|
|
45511
45534
|
};
|
|
45512
45535
|
//#endregion
|
|
45513
|
-
//#region
|
|
45536
|
+
//#region src/interaction/tools/fsm.ts
|
|
45514
45537
|
var ToolStateMachine = class {
|
|
45515
45538
|
constructor() {
|
|
45516
45539
|
this.states = /* @__PURE__ */ new Map();
|
|
@@ -45712,7 +45735,7 @@ var IdleState = class {
|
|
|
45712
45735
|
}
|
|
45713
45736
|
};
|
|
45714
45737
|
//#endregion
|
|
45715
|
-
//#region
|
|
45738
|
+
//#region src/renderer/selection/gizmo.ts
|
|
45716
45739
|
var GIZMO_Z = .05;
|
|
45717
45740
|
var HANDLE_PX = 9;
|
|
45718
45741
|
var HANDLE_HIT_PX = 12;
|
|
@@ -45943,7 +45966,7 @@ function boxesEqual(a, b) {
|
|
|
45943
45966
|
return a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY;
|
|
45944
45967
|
}
|
|
45945
45968
|
//#endregion
|
|
45946
|
-
//#region
|
|
45969
|
+
//#region src/interaction/tools/pressure.ts
|
|
45947
45970
|
var SIM_MAX = 1;
|
|
45948
45971
|
var SIM_MIN = .3;
|
|
45949
45972
|
var SMOOTHING = .25;
|
|
@@ -45965,7 +45988,7 @@ function clamp(v, lo, hi) {
|
|
|
45965
45988
|
return Math.min(hi, Math.max(lo, v));
|
|
45966
45989
|
}
|
|
45967
45990
|
//#endregion
|
|
45968
|
-
//#region
|
|
45991
|
+
//#region src/interaction/tools/strokeBuilder.ts
|
|
45969
45992
|
var StrokeBuilder = class {
|
|
45970
45993
|
constructor() {
|
|
45971
45994
|
this.points = [];
|
|
@@ -46015,7 +46038,7 @@ function catmullRom(v0, v1, v2, v3, t) {
|
|
|
46015
46038
|
return .5 * (2 * v1 + (-v0 + v2) * t + (2 * v0 - 5 * v1 + 4 * v2 - v3) * t2 + (-v0 + 3 * v1 - 3 * v2 + v3) * t3);
|
|
46016
46039
|
}
|
|
46017
46040
|
//#endregion
|
|
46018
|
-
//#region
|
|
46041
|
+
//#region src/interaction/tools/markerTool.ts
|
|
46019
46042
|
/** Sample spacing while drawing, in screen pixels. */
|
|
46020
46043
|
var MIN_SAMPLE_PX = 1.5;
|
|
46021
46044
|
/** A highlighter is a chisel tip: near-constant width, little pressure play. */
|
|
@@ -46118,7 +46141,7 @@ var MarkerTool = class {
|
|
|
46118
46141
|
}
|
|
46119
46142
|
};
|
|
46120
46143
|
//#endregion
|
|
46121
|
-
//#region
|
|
46144
|
+
//#region src/interaction/tools/orbitTool.ts
|
|
46122
46145
|
/**
|
|
46123
46146
|
* Orbit-preview (build prompt §4.2): alt-drag tilts the camera off-axis to
|
|
46124
46147
|
* read the board's material and depth; releasing eases back to the working
|
|
@@ -46153,7 +46176,7 @@ var OrbitTool = class {
|
|
|
46153
46176
|
}
|
|
46154
46177
|
};
|
|
46155
46178
|
//#endregion
|
|
46156
|
-
//#region
|
|
46179
|
+
//#region src/interaction/tools/panTool.ts
|
|
46157
46180
|
/** Grab-pan: content follows the pointer. */
|
|
46158
46181
|
var PanTool = class {
|
|
46159
46182
|
constructor(ctx) {
|
|
@@ -46183,7 +46206,7 @@ var PanTool = class {
|
|
|
46183
46206
|
}
|
|
46184
46207
|
};
|
|
46185
46208
|
//#endregion
|
|
46186
|
-
//#region
|
|
46209
|
+
//#region src/renderer/selection/selection.ts
|
|
46187
46210
|
/** The set of selected stroke ids. Drops ids the document no longer has. */
|
|
46188
46211
|
var SelectionManager = class {
|
|
46189
46212
|
constructor(doc) {
|
|
@@ -46234,7 +46257,7 @@ var SelectionManager = class {
|
|
|
46234
46257
|
}
|
|
46235
46258
|
};
|
|
46236
46259
|
//#endregion
|
|
46237
|
-
//#region
|
|
46260
|
+
//#region src/interaction/tools/selectTool.ts
|
|
46238
46261
|
var HIT_SLOP_PX = 4;
|
|
46239
46262
|
var CLICK_TOLERANCE_PX = 3;
|
|
46240
46263
|
var MIN_SCALE = .05;
|
|
@@ -46450,7 +46473,7 @@ function safeRatio(numerator, denominator) {
|
|
|
46450
46473
|
return raw;
|
|
46451
46474
|
}
|
|
46452
46475
|
//#endregion
|
|
46453
|
-
//#region
|
|
46476
|
+
//#region src/interaction/tools/customTool.ts
|
|
46454
46477
|
function extensionIdFromToolId(toolId) {
|
|
46455
46478
|
const slash = toolId.indexOf("/");
|
|
46456
46479
|
return slash === -1 ? toolId : toolId.slice(0, slash);
|
|
@@ -46565,12 +46588,19 @@ var ExtensionCommandBatch = class {
|
|
|
46565
46588
|
}
|
|
46566
46589
|
};
|
|
46567
46590
|
//#endregion
|
|
46568
|
-
//#region
|
|
46591
|
+
//#region src/composition/ScrawlEngine.ts
|
|
46569
46592
|
/** Marker stroke width in board units (~1% of the default view height). */
|
|
46570
46593
|
var BASE_WIDTH = .5;
|
|
46571
46594
|
/** Highlighter chisel width — wide enough to cover a line of handwriting. */
|
|
46572
46595
|
var HIGHLIGHT_WIDTH = 2.2;
|
|
46573
46596
|
/**
|
|
46597
|
+
* Shape (rectangle, ellipse, arrow, ...) border width in board units.
|
|
46598
|
+
* Deliberately thinner than BASE_WIDTH: a shape border reads as a crisp,
|
|
46599
|
+
* structural line, not an expressive marker stroke — this is also the
|
|
46600
|
+
* fallback when no Host theme configures ScrawlTheme.shapeStrokeWidth.
|
|
46601
|
+
*/
|
|
46602
|
+
var DEFAULT_SHAPE_STROKE_WIDTH = .15;
|
|
46603
|
+
/**
|
|
46574
46604
|
* Identity of a rendered selection badge: which item, its lock state, and
|
|
46575
46605
|
* where it sits on screen (rounded to the pixel, so sub-pixel camera drift
|
|
46576
46606
|
* does not churn React).
|
|
@@ -46591,10 +46621,11 @@ function emptyExtensionRegistry() {
|
|
|
46591
46621
|
* React never reaches past this class.
|
|
46592
46622
|
*/
|
|
46593
46623
|
var ScrawlEngine = class {
|
|
46594
|
-
constructor(canvas, documentId$1, extensionRegistry = emptyExtensionRegistry(), assetController = new AssetController(void 0), boardTheme = DEFAULT_BOARD_SURFACE_THEME) {
|
|
46624
|
+
constructor(canvas, documentId$1, extensionRegistry = emptyExtensionRegistry(), assetController = new AssetController(void 0), boardTheme = DEFAULT_BOARD_SURFACE_THEME, shapeStrokeWidth = DEFAULT_SHAPE_STROKE_WIDTH) {
|
|
46595
46625
|
this.canvas = canvas;
|
|
46596
46626
|
this.extensionRegistry = extensionRegistry;
|
|
46597
46627
|
this.boardTheme = boardTheme;
|
|
46628
|
+
this.shapeStrokeWidth = shapeStrokeWidth;
|
|
46598
46629
|
this.onToolChange = null;
|
|
46599
46630
|
this.onZoomChange = null;
|
|
46600
46631
|
this.onAuditEvent = null;
|
|
@@ -46689,6 +46720,7 @@ var ScrawlEngine = class {
|
|
|
46689
46720
|
setTool: (t) => this.setTool(t),
|
|
46690
46721
|
inkColor: () => this.tool === "highlighter" ? this.highlightColor : this.color,
|
|
46691
46722
|
baseWidth: () => this.tool === "highlighter" ? HIGHLIGHT_WIDTH : BASE_WIDTH,
|
|
46723
|
+
shapeStrokeWidth: () => this.shapeStrokeWidth,
|
|
46692
46724
|
activeTool: () => this.tool,
|
|
46693
46725
|
isSpaceHeld: () => this.spaceHeld,
|
|
46694
46726
|
pointerToBoard: (e) => pointerToBoard(e.clientX, e.clientY, canvas, this.scene.cameraRig.camera),
|
|
@@ -46953,6 +46985,10 @@ var ScrawlEngine = class {
|
|
|
46953
46985
|
this.boardTheme = theme;
|
|
46954
46986
|
this.scene.applyBoardTheme(theme);
|
|
46955
46987
|
}
|
|
46988
|
+
/** Live update of the shape tool's border width (board units). Already-drawn shapes keep the width they were drawn with. */
|
|
46989
|
+
setShapeStrokeWidth(width) {
|
|
46990
|
+
this.shapeStrokeWidth = width;
|
|
46991
|
+
}
|
|
46956
46992
|
/**
|
|
46957
46993
|
* Snap to a peer's camera. No-op while drawing so pointer-to-board
|
|
46958
46994
|
* mapping does not warp the in-progress stroke.
|
|
@@ -48014,7 +48050,7 @@ var ScrawlEngine = class {
|
|
|
48014
48050
|
}
|
|
48015
48051
|
};
|
|
48016
48052
|
//#endregion
|
|
48017
|
-
//#region src/controller-internal.ts
|
|
48053
|
+
//#region src/composition/controller-internal.ts
|
|
48018
48054
|
function createBoardController(options) {
|
|
48019
48055
|
return createBoardControllerWithEngine(options);
|
|
48020
48056
|
}
|
|
@@ -48032,9 +48068,12 @@ function createBoardControllerWithEngine(options, existingEngine) {
|
|
|
48032
48068
|
color: options.boardTheme?.surface ?? DEFAULT_BOARD_SURFACE_THEME.color,
|
|
48033
48069
|
gridMode: options.boardTheme?.gridMode ?? DEFAULT_BOARD_SURFACE_THEME.gridMode,
|
|
48034
48070
|
gridColor: options.boardTheme?.gridColor ?? DEFAULT_BOARD_SURFACE_THEME.gridColor,
|
|
48035
|
-
gridSpacing: options.boardTheme?.gridSpacing ?? DEFAULT_BOARD_SURFACE_THEME.gridSpacing
|
|
48071
|
+
gridSpacing: options.boardTheme?.gridSpacing ?? DEFAULT_BOARD_SURFACE_THEME.gridSpacing,
|
|
48072
|
+
gridLineWidth: options.boardTheme?.gridLineWidth ?? DEFAULT_BOARD_SURFACE_THEME.gridLineWidth,
|
|
48073
|
+
surfaceTexture: options.boardTheme?.surfaceTexture ?? DEFAULT_BOARD_SURFACE_THEME.surfaceTexture
|
|
48036
48074
|
};
|
|
48037
|
-
|
|
48075
|
+
let currentShapeStrokeWidth = options.boardTheme?.shapeStrokeWidth ?? .15;
|
|
48076
|
+
const engine = existingEngine ?? (options.canvas ? new ScrawlEngine(options.canvas, options.document.id, extensionRegistry, assetController, currentBoardTheme, currentShapeStrokeWidth) : null);
|
|
48038
48077
|
const document = engine?.document ?? new BoardDocument(documentId(options.document.id));
|
|
48039
48078
|
const history = engine?.history ?? new History(document);
|
|
48040
48079
|
const createId = options.createId ?? (() => crypto.randomUUID());
|
|
@@ -48523,9 +48562,15 @@ function createBoardControllerWithEngine(options, existingEngine) {
|
|
|
48523
48562
|
color: theme.surface ?? currentBoardTheme.color,
|
|
48524
48563
|
gridMode: theme.gridMode ?? currentBoardTheme.gridMode,
|
|
48525
48564
|
gridColor: theme.gridColor ?? currentBoardTheme.gridColor,
|
|
48526
|
-
gridSpacing: theme.gridSpacing ?? currentBoardTheme.gridSpacing
|
|
48565
|
+
gridSpacing: theme.gridSpacing ?? currentBoardTheme.gridSpacing,
|
|
48566
|
+
gridLineWidth: theme.gridLineWidth ?? currentBoardTheme.gridLineWidth,
|
|
48567
|
+
surfaceTexture: theme.surfaceTexture ?? currentBoardTheme.surfaceTexture
|
|
48527
48568
|
};
|
|
48528
48569
|
engine?.applyBoardTheme(currentBoardTheme);
|
|
48570
|
+
if (theme.shapeStrokeWidth !== void 0) {
|
|
48571
|
+
currentShapeStrokeWidth = theme.shapeStrokeWidth;
|
|
48572
|
+
engine?.setShapeStrokeWidth(currentShapeStrokeWidth);
|
|
48573
|
+
}
|
|
48529
48574
|
} },
|
|
48530
48575
|
view: {
|
|
48531
48576
|
fit() {
|