@mocanvas/editor 4.5.0 → 4.5.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/index.d.ts CHANGED
@@ -8241,7 +8241,20 @@ interface EditorConfig extends TldrawOptions {
8241
8241
  zoomMax: number;
8242
8242
  /** The zoom levels the zoom-in / zoom-out steps land on. */
8243
8243
  zoomSteps: number[];
8244
- /** The renderer's clear colour, as premultiplied linear RGBA. */
8244
+ /**
8245
+ * The page colour behind the canvas, as RGBA in 0–1.
8246
+ *
8247
+ * @deprecated The canvas background is a *theme* colour — the theme carries
8248
+ * one for each colour mode and publishes it as `--mocanvas-background`,
8249
+ * which is what the container paints. This knob is honoured when it is set
8250
+ * to anything other than its default, so an app that configured it keeps the
8251
+ * colour it configured; a new app should set the theme, or pass
8252
+ * `style={{ background }}` to `<Canvas>`, either of which follows dark mode.
8253
+ *
8254
+ * It is no longer the renderer's clear colour: the GPU layer clears
8255
+ * transparent, because anything it painted covered the grid and the
8256
+ * `Background` slot beneath it.
8257
+ */
8245
8258
  backgroundColor: [number, number, number, number];
8246
8259
  /**
8247
8260
  * How far the zoom must change, in octaves, before `getDebouncedZoomLevel()`
@@ -13125,14 +13138,25 @@ declare function DefaultCanvas(): react.JSX.Element;
13125
13138
  */
13126
13139
  declare function DefaultBackground(): react.JSX.Element;
13127
13140
  /**
13128
- * The grid, drawn in page space with an SVG pattern.
13129
- *
13130
- * Two patterns rather than one: a fine cell at the document's grid step and a
13131
- * heavier one every fifth cell, so the eye can count without the fine grid
13132
- * having to be dark enough to read on its own. Both fade out as the camera
13133
- * zooms away, because a grid finer than a few screen pixels is noise.
13134
- */
13135
- declare function DefaultGrid({ x, y, z, size }: TLGridProps): react.JSX.Element;
13141
+ * The grid: one lattice of dots on the document's own grid step, drawn with an
13142
+ * SVG pattern in page space.
13143
+ *
13144
+ * **The dot scales with the world, and the lattice fades as a whole.** Those
13145
+ * are the two rules, and each fixes a direction the old grid got backwards. A
13146
+ * fixed 1px dot vanishes as you zoom in — the cell grows to 80 screen pixels
13147
+ * and what is left is a speck every 80px, which reads as no grid at all — so
13148
+ * the radius is a fraction of the cell, floored and capped so it never
13149
+ * disappears and never becomes a blob. And a second, heavier lattice every
13150
+ * fifth cell at a fixed opacity is exactly the dense sheen a zoomed-out canvas
13151
+ * must not have: one lattice, one opacity, gone below {@link GRID_GONE_PX}.
13152
+ *
13153
+ * **No re-levelling**, deliberately: the spacing is always the document's step
13154
+ * rather than a power of it chosen per zoom. Dots are then magnets — a corner
13155
+ * on a multiple of the step sits on a dot at *every* zoom — and re-levelling
13156
+ * breaks that on the intermediate steps, which is worse than a grid that
13157
+ * bows out when it gets too dense to mean anything.
13158
+ */
13159
+ declare function DefaultGrid({ x, y, z, size }: TLGridProps): react.JSX.Element | null;
13136
13160
  /**
13137
13161
  * Drawn in place of one shape whose body threw.
13138
13162
  *
package/dist/index.js CHANGED
@@ -276,16 +276,16 @@ var Vec = class _Vec {
276
276
  * The point on the segment `A`→`B` closest to `P`. With `clamp` false the
277
277
  * segment is treated as the infinite line through `A` and `B`.
278
278
  */
279
- static NearestPointOnLineSegment(A, B, P, clamp3 = true) {
279
+ static NearestPointOnLineSegment(A, B, P, clamp4 = true) {
280
280
  const ab = _Vec.Sub(B, A);
281
281
  const l2 = _Vec.Dpr(ab, ab);
282
282
  if (l2 === 0) return _Vec.From(A);
283
283
  let t = _Vec.Dpr(_Vec.Sub(P, A), ab) / l2;
284
- if (clamp3) t = Math.max(0, Math.min(1, t));
284
+ if (clamp4) t = Math.max(0, Math.min(1, t));
285
285
  return _Vec.Add(A, _Vec.Mul(ab, t));
286
286
  }
287
- static DistanceToLineSegment(A, B, P, clamp3 = true) {
288
- return _Vec.Dist(P, _Vec.NearestPointOnLineSegment(A, B, P, clamp3));
287
+ static DistanceToLineSegment(A, B, P, clamp4 = true) {
288
+ return _Vec.Dist(P, _Vec.NearestPointOnLineSegment(A, B, P, clamp4));
289
289
  }
290
290
  /** The point closest to `P` on the infinite line through `A` in direction `u`. */
291
291
  static NearestPointOnLineThroughPoint(A, u, P) {
@@ -7840,6 +7840,7 @@ var DEFAULT_EDITOR_CONFIG = {
7840
7840
  var RAD_PER_DEG = Math.PI / 180;
7841
7841
  var WHEEL_ZOOM_DELTA_CAP = 50;
7842
7842
  var MIDDLE_BUTTON = 1;
7843
+ var TRANSPARENT_CLEAR = [0, 0, 0, 0];
7843
7844
  var COLLABORATOR_INACTIVE_TIMEOUT = 6e4;
7844
7845
  var editorSequence = 0;
7845
7846
  var warnedKeys = /* @__PURE__ */ new Set();
@@ -10261,7 +10262,7 @@ var Editor = class extends EventEmitter {
10261
10262
  const vp = this.getViewportScreenBounds();
10262
10263
  const camState = { x: cam.x, y: cam.y, z: cam.z };
10263
10264
  const frame = this.engine.frame(camState, vp.w, vp.h);
10264
- backend.draw(frame, camState, { background: this.options.backgroundColor });
10265
+ backend.draw(frame, camState, { background: TRANSPARENT_CLEAR });
10265
10266
  const overlay = EngineBridge.readOverlay(frame.overlay);
10266
10267
  const ids = [];
10267
10268
  const clips = [];
@@ -14457,19 +14458,24 @@ function DefaultCanvas() {
14457
14458
  function DefaultBackground() {
14458
14459
  return /* @__PURE__ */ jsx("div", { className: "mocanvas-background", style: { position: "absolute", inset: 0, background: "var(--mocanvas-canvas, transparent)" } });
14459
14460
  }
14461
+ function clamp3(v, min, max) {
14462
+ return Math.max(min, Math.min(max, v));
14463
+ }
14464
+ var GRID_SOLID_PX = 12;
14465
+ var GRID_GONE_PX = 6;
14466
+ var GRID_DOT_RATIO = 16;
14467
+ var GRID_DOT_MIN = 0.75;
14468
+ var GRID_DOT_MAX = 5;
14460
14469
  function DefaultGrid({ x, y, z, size }) {
14461
14470
  const id = useSharedSafeId("grid");
14462
- const step = size * z;
14463
- const fineOpacity = Math.min(1, Math.max(0, (step - 4) / 12));
14464
- const FINE = 0.7;
14465
- const MAJOR = 0.9;
14471
+ const cell = size * z;
14472
+ const opacity = clamp3((cell - GRID_GONE_PX) / (GRID_SOLID_PX - GRID_GONE_PX), 0, 1);
14473
+ if (opacity <= 0) return null;
14474
+ const r = clamp3(cell / GRID_DOT_RATIO, GRID_DOT_MIN, GRID_DOT_MAX);
14475
+ const half = cell / 2;
14466
14476
  return /* @__PURE__ */ jsxs("svg", { className: "mocanvas-grid", style: { position: "absolute", inset: 0, width: "100%", height: "100%", pointerEvents: "none" }, "aria-hidden": "true", children: [
14467
- /* @__PURE__ */ jsxs("defs", { children: [
14468
- /* @__PURE__ */ jsx("pattern", { id, width: step, height: step, patternUnits: "userSpaceOnUse", patternTransform: `translate(${x * z} ${y * z})`, children: /* @__PURE__ */ jsx("circle", { cx: 0, cy: 0, r: 1, fill: "var(--mocanvas-grid, currentColor)", opacity: fineOpacity * FINE }) }),
14469
- /* @__PURE__ */ jsx("pattern", { id: `${id}_major`, width: step * 5, height: step * 5, patternUnits: "userSpaceOnUse", patternTransform: `translate(${x * z} ${y * z})`, children: /* @__PURE__ */ jsx("circle", { cx: 0, cy: 0, r: 1.5, fill: "var(--mocanvas-grid, currentColor)", opacity: MAJOR }) })
14470
- ] }),
14471
- /* @__PURE__ */ jsx("rect", { width: "100%", height: "100%", fill: `url(#${id})` }),
14472
- /* @__PURE__ */ jsx("rect", { width: "100%", height: "100%", fill: `url(#${id}_major)` })
14477
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("pattern", { id, width: cell, height: cell, patternUnits: "userSpaceOnUse", patternTransform: `translate(${x * z - half} ${y * z - half})`, children: /* @__PURE__ */ jsx("circle", { cx: half, cy: half, r, fill: "var(--mocanvas-grid, currentColor)" }) }) }),
14478
+ /* @__PURE__ */ jsx("rect", { width: "100%", height: "100%", fill: `url(#${id})`, opacity })
14473
14479
  ] });
14474
14480
  }
14475
14481
  function DefaultShapeErrorFallback({ error }) {
@@ -14909,6 +14915,13 @@ function useErrorSlot(key, fallback) {
14909
14915
  const slot = local?.[key] !== void 0 ? local[key] : provided[key];
14910
14916
  return slot === void 0 ? fallback : slot;
14911
14917
  }
14918
+ function backgroundFor(editor) {
14919
+ const set = editor.options.backgroundColor;
14920
+ const dflt = DEFAULT_EDITOR_CONFIG.backgroundColor;
14921
+ if (set.every((v, i) => v === dflt[i])) return "var(--mocanvas-background, #f9fafb)";
14922
+ const [r, g, b, a] = set;
14923
+ return `rgba(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(b * 255)}, ${a})`;
14924
+ }
14912
14925
  var containerStyle = {
14913
14926
  position: "relative",
14914
14927
  width: "100%",
@@ -15049,7 +15062,7 @@ function CanvasBody({ editor, className, style, children, components, indicatorO
15049
15062
  {
15050
15063
  ref: containerRef,
15051
15064
  className: className ? `mocanvas ${className}` : "mocanvas",
15052
- style: { ...containerStyle, ...themeVars, ...cameraVars, ...style, cursor },
15065
+ style: { ...containerStyle, background: backgroundFor(editor), ...themeVars, ...cameraVars, ...style, cursor },
15053
15066
  tabIndex: 0,
15054
15067
  onPointerDown: events.onPointerDown,
15055
15068
  onPointerMove: events.onPointerMove,