@tldraw/editor 3.13.0-canary.8e04030e54fe → 3.13.0-canary.8f8ae1660cdb

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.
Files changed (96) hide show
  1. package/dist-cjs/index.d.ts +74 -96
  2. package/dist-cjs/index.js +7 -22
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/editor/Editor.js +3 -5
  5. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  6. package/dist-cjs/lib/editor/managers/SnapManager/HandleSnaps.js.map +2 -2
  7. package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js.map +2 -2
  8. package/dist-cjs/lib/primitives/Box.js +16 -0
  9. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  10. package/dist-cjs/lib/primitives/Mat.js +1 -1
  11. package/dist-cjs/lib/primitives/Mat.js.map +2 -2
  12. package/dist-cjs/lib/primitives/Vec.js +20 -0
  13. package/dist-cjs/lib/primitives/Vec.js.map +2 -2
  14. package/dist-cjs/lib/primitives/geometry/Arc2d.js +2 -2
  15. package/dist-cjs/lib/primitives/geometry/Arc2d.js.map +2 -2
  16. package/dist-cjs/lib/primitives/geometry/Circle2d.js +1 -1
  17. package/dist-cjs/lib/primitives/geometry/Circle2d.js.map +2 -2
  18. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js +1 -1
  19. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
  20. package/dist-cjs/lib/primitives/geometry/CubicSpline2d.js.map +2 -2
  21. package/dist-cjs/lib/primitives/geometry/Edge2d.js +1 -1
  22. package/dist-cjs/lib/primitives/geometry/Edge2d.js.map +2 -2
  23. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js.map +2 -2
  24. package/dist-cjs/lib/primitives/geometry/Geometry2d.js +91 -20
  25. package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +2 -2
  26. package/dist-cjs/lib/primitives/geometry/Group2d.js +55 -2
  27. package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
  28. package/dist-cjs/lib/primitives/geometry/Point2d.js.map +2 -2
  29. package/dist-cjs/lib/primitives/geometry/Polyline2d.js.map +2 -2
  30. package/dist-cjs/lib/primitives/geometry/Stadium2d.js.map +2 -2
  31. package/dist-cjs/lib/utils/debug-flags.js +5 -2
  32. package/dist-cjs/lib/utils/debug-flags.js.map +2 -2
  33. package/dist-cjs/version.js +3 -3
  34. package/dist-cjs/version.js.map +1 -1
  35. package/dist-esm/index.d.mts +74 -96
  36. package/dist-esm/index.mjs +9 -41
  37. package/dist-esm/index.mjs.map +2 -2
  38. package/dist-esm/lib/editor/Editor.mjs +3 -5
  39. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  40. package/dist-esm/lib/editor/managers/SnapManager/HandleSnaps.mjs.map +2 -2
  41. package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs.map +2 -2
  42. package/dist-esm/lib/primitives/Box.mjs +16 -0
  43. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  44. package/dist-esm/lib/primitives/Mat.mjs +1 -1
  45. package/dist-esm/lib/primitives/Mat.mjs.map +2 -2
  46. package/dist-esm/lib/primitives/Vec.mjs +20 -0
  47. package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
  48. package/dist-esm/lib/primitives/geometry/Arc2d.mjs +2 -2
  49. package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
  50. package/dist-esm/lib/primitives/geometry/Circle2d.mjs +1 -1
  51. package/dist-esm/lib/primitives/geometry/Circle2d.mjs.map +2 -2
  52. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +1 -1
  53. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
  54. package/dist-esm/lib/primitives/geometry/CubicSpline2d.mjs.map +2 -2
  55. package/dist-esm/lib/primitives/geometry/Edge2d.mjs +1 -1
  56. package/dist-esm/lib/primitives/geometry/Edge2d.mjs.map +2 -2
  57. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs.map +2 -2
  58. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +92 -21
  59. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
  60. package/dist-esm/lib/primitives/geometry/Group2d.mjs +55 -2
  61. package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
  62. package/dist-esm/lib/primitives/geometry/Point2d.mjs.map +2 -2
  63. package/dist-esm/lib/primitives/geometry/Polyline2d.mjs.map +2 -2
  64. package/dist-esm/lib/primitives/geometry/Stadium2d.mjs.map +2 -2
  65. package/dist-esm/lib/utils/debug-flags.mjs +5 -2
  66. package/dist-esm/lib/utils/debug-flags.mjs.map +2 -2
  67. package/dist-esm/version.mjs +3 -3
  68. package/dist-esm/version.mjs.map +1 -1
  69. package/editor.css +29 -0
  70. package/package.json +7 -7
  71. package/src/index.ts +16 -31
  72. package/src/lib/editor/Editor.test.ts +1 -1
  73. package/src/lib/editor/Editor.ts +3 -5
  74. package/src/lib/editor/managers/SnapManager/HandleSnaps.ts +0 -1
  75. package/src/lib/editor/shapes/shared/getPerfectDashProps.ts +9 -9
  76. package/src/lib/primitives/Box.ts +20 -0
  77. package/src/lib/primitives/Mat.ts +5 -4
  78. package/src/lib/primitives/Vec.ts +23 -0
  79. package/src/lib/primitives/geometry/Arc2d.ts +5 -5
  80. package/src/lib/primitives/geometry/Circle2d.ts +4 -4
  81. package/src/lib/primitives/geometry/CubicBezier2d.ts +4 -4
  82. package/src/lib/primitives/geometry/CubicSpline2d.ts +3 -3
  83. package/src/lib/primitives/geometry/Edge2d.ts +3 -3
  84. package/src/lib/primitives/geometry/Ellipse2d.ts +3 -3
  85. package/src/lib/primitives/geometry/Geometry2d.test.ts +42 -0
  86. package/src/lib/primitives/geometry/Geometry2d.ts +123 -35
  87. package/src/lib/primitives/geometry/Group2d.ts +70 -7
  88. package/src/lib/primitives/geometry/Point2d.ts +2 -2
  89. package/src/lib/primitives/geometry/Polyline2d.ts +3 -3
  90. package/src/lib/primitives/geometry/Stadium2d.ts +3 -3
  91. package/src/lib/test/currentToolIdMask.test.ts +1 -1
  92. package/src/lib/test/user.test.ts +1 -1
  93. package/src/lib/utils/debug-flags.ts +7 -2
  94. package/src/lib/utils/sync/LocalIndexedDb.test.ts +1 -1
  95. package/src/lib/utils/sync/TLLocalSyncClient.test.ts +1 -1
  96. package/src/version.ts +3 -3
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/primitives/geometry/Stadium2d.ts"],
4
- "sourcesContent": ["import { Box } from '../Box'\nimport { Vec } from '../Vec'\nimport { PI } from '../utils'\nimport { Arc2d } from './Arc2d'\nimport { Edge2d } from './Edge2d'\nimport { Geometry2d, Geometry2dOptions } from './Geometry2d'\n\n/** @public */\nexport class Stadium2d extends Geometry2d {\n\tw: number\n\th: number\n\n\ta: Arc2d\n\tb: Edge2d\n\tc: Arc2d\n\td: Edge2d\n\n\tconstructor(\n\t\tpublic config: Omit<Geometry2dOptions, 'isClosed'> & {\n\t\t\twidth: number\n\t\t\theight: number\n\t\t}\n\t) {\n\t\tsuper({ ...config, isClosed: true })\n\t\tconst { width: w, height: h } = config\n\t\tthis.w = w\n\t\tthis.h = h\n\n\t\tif (h > w) {\n\t\t\tconst r = w / 2\n\t\t\tthis.a = new Arc2d({\n\t\t\t\tstart: new Vec(0, r),\n\t\t\t\tend: new Vec(w, r),\n\t\t\t\tcenter: new Vec(w / 2, r),\n\t\t\t\tsweepFlag: 1,\n\t\t\t\tlargeArcFlag: 1,\n\t\t\t})\n\t\t\tthis.b = new Edge2d({ start: new Vec(w, r), end: new Vec(w, h - r) })\n\t\t\tthis.c = new Arc2d({\n\t\t\t\tstart: new Vec(w, h - r),\n\t\t\t\tend: new Vec(0, h - r),\n\t\t\t\tcenter: new Vec(w / 2, h - r),\n\t\t\t\tsweepFlag: 1,\n\t\t\t\tlargeArcFlag: 1,\n\t\t\t})\n\t\t\tthis.d = new Edge2d({ start: new Vec(0, h - r), end: new Vec(0, r) })\n\t\t} else {\n\t\t\tconst r = h / 2\n\t\t\tthis.a = new Arc2d({\n\t\t\t\tstart: new Vec(r, h),\n\t\t\t\tend: new Vec(r, 0),\n\t\t\t\tcenter: new Vec(r, r),\n\t\t\t\tsweepFlag: 1,\n\t\t\t\tlargeArcFlag: 1,\n\t\t\t})\n\t\t\tthis.b = new Edge2d({ start: new Vec(r, 0), end: new Vec(w - r, 0) })\n\t\t\tthis.c = new Arc2d({\n\t\t\t\tstart: new Vec(w - r, 0),\n\t\t\t\tend: new Vec(w - r, h),\n\t\t\t\tcenter: new Vec(w - r, r),\n\t\t\t\tsweepFlag: 1,\n\t\t\t\tlargeArcFlag: 1,\n\t\t\t})\n\t\t\tthis.d = new Edge2d({ start: new Vec(w - r, h), end: new Vec(r, h) })\n\t\t}\n\t}\n\n\tnearestPoint(A: Vec): Vec {\n\t\tlet nearest: Vec | undefined\n\t\tlet dist = Infinity\n\t\tlet _d: number\n\t\tlet p: Vec\n\n\t\tconst { a, b, c, d } = this\n\t\tfor (const part of [a, b, c, d]) {\n\t\t\tp = part.nearestPoint(A)\n\t\t\t_d = Vec.Dist2(p, A)\n\t\t\tif (_d < dist) {\n\t\t\t\tnearest = p\n\t\t\t\tdist = _d\n\t\t\t}\n\t\t}\n\t\tif (!nearest) throw Error('nearest point not found')\n\t\treturn nearest\n\t}\n\n\thitTestLineSegment(A: Vec, B: Vec): boolean {\n\t\tconst { a, b, c, d } = this\n\t\treturn [a, b, c, d].some((edge) => edge.hitTestLineSegment(A, B))\n\t}\n\n\tgetVertices() {\n\t\tconst { a, b, c, d } = this\n\t\treturn [a, b, c, d].reduce<Vec[]>((a, p) => {\n\t\t\ta.push(...p.vertices)\n\t\t\treturn a\n\t\t}, [])\n\t}\n\n\tgetBounds() {\n\t\treturn new Box(0, 0, this.w, this.h)\n\t}\n\n\tgetLength() {\n\t\tconst { h, w } = this\n\t\tif (h > w) return (PI * (w / 2) + (h - w)) * 2\n\t\telse return (PI * (h / 2) + (w - h)) * 2\n\t}\n\n\tgetSvgPathData() {\n\t\tconst { a, b, c, d } = this\n\t\treturn [a, b, c, d].map((p, i) => p.getSvgPathData(i === 0)).join(' ') + ' Z'\n\t}\n}\n"],
5
- "mappings": "AAAA,SAAS,WAAW;AACpB,SAAS,WAAW;AACpB,SAAS,UAAU;AACnB,SAAS,aAAa;AACtB,SAAS,cAAc;AACvB,SAAS,kBAAqC;AAGvC,MAAM,kBAAkB,WAAW;AAAA,EASzC,YACQ,QAIN;AACD,UAAM,EAAE,GAAG,QAAQ,UAAU,KAAK,CAAC;AAL5B;AAMP,UAAM,EAAE,OAAO,GAAG,QAAQ,EAAE,IAAI;AAChC,SAAK,IAAI;AACT,SAAK,IAAI;AAET,QAAI,IAAI,GAAG;AACV,YAAM,IAAI,IAAI;AACd,WAAK,IAAI,IAAI,MAAM;AAAA,QAClB,OAAO,IAAI,IAAI,GAAG,CAAC;AAAA,QACnB,KAAK,IAAI,IAAI,GAAG,CAAC;AAAA,QACjB,QAAQ,IAAI,IAAI,IAAI,GAAG,CAAC;AAAA,QACxB,WAAW;AAAA,QACX,cAAc;AAAA,MACf,CAAC;AACD,WAAK,IAAI,IAAI,OAAO,EAAE,OAAO,IAAI,IAAI,GAAG,CAAC,GAAG,KAAK,IAAI,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;AACpE,WAAK,IAAI,IAAI,MAAM;AAAA,QAClB,OAAO,IAAI,IAAI,GAAG,IAAI,CAAC;AAAA,QACvB,KAAK,IAAI,IAAI,GAAG,IAAI,CAAC;AAAA,QACrB,QAAQ,IAAI,IAAI,IAAI,GAAG,IAAI,CAAC;AAAA,QAC5B,WAAW;AAAA,QACX,cAAc;AAAA,MACf,CAAC;AACD,WAAK,IAAI,IAAI,OAAO,EAAE,OAAO,IAAI,IAAI,GAAG,IAAI,CAAC,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;AAAA,IACrE,OAAO;AACN,YAAM,IAAI,IAAI;AACd,WAAK,IAAI,IAAI,MAAM;AAAA,QAClB,OAAO,IAAI,IAAI,GAAG,CAAC;AAAA,QACnB,KAAK,IAAI,IAAI,GAAG,CAAC;AAAA,QACjB,QAAQ,IAAI,IAAI,GAAG,CAAC;AAAA,QACpB,WAAW;AAAA,QACX,cAAc;AAAA,MACf,CAAC;AACD,WAAK,IAAI,IAAI,OAAO,EAAE,OAAO,IAAI,IAAI,GAAG,CAAC,GAAG,KAAK,IAAI,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;AACpE,WAAK,IAAI,IAAI,MAAM;AAAA,QAClB,OAAO,IAAI,IAAI,IAAI,GAAG,CAAC;AAAA,QACvB,KAAK,IAAI,IAAI,IAAI,GAAG,CAAC;AAAA,QACrB,QAAQ,IAAI,IAAI,IAAI,GAAG,CAAC;AAAA,QACxB,WAAW;AAAA,QACX,cAAc;AAAA,MACf,CAAC;AACD,WAAK,IAAI,IAAI,OAAO,EAAE,OAAO,IAAI,IAAI,IAAI,GAAG,CAAC,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;AAAA,IACrE;AAAA,EACD;AAAA,EAxDA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAoDA,aAAa,GAAa;AACzB,QAAI;AACJ,QAAI,OAAO;AACX,QAAI;AACJ,QAAI;AAEJ,UAAM,EAAE,GAAG,GAAG,GAAG,EAAE,IAAI;AACvB,eAAW,QAAQ,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG;AAChC,UAAI,KAAK,aAAa,CAAC;AACvB,WAAK,IAAI,MAAM,GAAG,CAAC;AACnB,UAAI,KAAK,MAAM;AACd,kBAAU;AACV,eAAO;AAAA,MACR;AAAA,IACD;AACA,QAAI,CAAC,QAAS,OAAM,MAAM,yBAAyB;AACnD,WAAO;AAAA,EACR;AAAA,EAEA,mBAAmB,GAAQ,GAAiB;AAC3C,UAAM,EAAE,GAAG,GAAG,GAAG,EAAE,IAAI;AACvB,WAAO,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,KAAK,CAAC,SAAS,KAAK,mBAAmB,GAAG,CAAC,CAAC;AAAA,EACjE;AAAA,EAEA,cAAc;AACb,UAAM,EAAE,GAAG,GAAG,GAAG,EAAE,IAAI;AACvB,WAAO,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,OAAc,CAACA,IAAG,MAAM;AAC3C,MAAAA,GAAE,KAAK,GAAG,EAAE,QAAQ;AACpB,aAAOA;AAAA,IACR,GAAG,CAAC,CAAC;AAAA,EACN;AAAA,EAEA,YAAY;AACX,WAAO,IAAI,IAAI,GAAG,GAAG,KAAK,GAAG,KAAK,CAAC;AAAA,EACpC;AAAA,EAEA,YAAY;AACX,UAAM,EAAE,GAAG,EAAE,IAAI;AACjB,QAAI,IAAI,EAAG,SAAQ,MAAM,IAAI,MAAM,IAAI,MAAM;AAAA,QACxC,SAAQ,MAAM,IAAI,MAAM,IAAI,MAAM;AAAA,EACxC;AAAA,EAEA,iBAAiB;AAChB,UAAM,EAAE,GAAG,GAAG,GAAG,EAAE,IAAI;AACvB,WAAO,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,MAAM,EAAE,eAAe,MAAM,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI;AAAA,EAC1E;AACD;",
4
+ "sourcesContent": ["import { Box } from '../Box'\nimport { Vec, VecLike } from '../Vec'\nimport { PI } from '../utils'\nimport { Arc2d } from './Arc2d'\nimport { Edge2d } from './Edge2d'\nimport { Geometry2d, Geometry2dOptions } from './Geometry2d'\n\n/** @public */\nexport class Stadium2d extends Geometry2d {\n\tw: number\n\th: number\n\n\ta: Arc2d\n\tb: Edge2d\n\tc: Arc2d\n\td: Edge2d\n\n\tconstructor(\n\t\tpublic config: Omit<Geometry2dOptions, 'isClosed'> & {\n\t\t\twidth: number\n\t\t\theight: number\n\t\t}\n\t) {\n\t\tsuper({ ...config, isClosed: true })\n\t\tconst { width: w, height: h } = config\n\t\tthis.w = w\n\t\tthis.h = h\n\n\t\tif (h > w) {\n\t\t\tconst r = w / 2\n\t\t\tthis.a = new Arc2d({\n\t\t\t\tstart: new Vec(0, r),\n\t\t\t\tend: new Vec(w, r),\n\t\t\t\tcenter: new Vec(w / 2, r),\n\t\t\t\tsweepFlag: 1,\n\t\t\t\tlargeArcFlag: 1,\n\t\t\t})\n\t\t\tthis.b = new Edge2d({ start: new Vec(w, r), end: new Vec(w, h - r) })\n\t\t\tthis.c = new Arc2d({\n\t\t\t\tstart: new Vec(w, h - r),\n\t\t\t\tend: new Vec(0, h - r),\n\t\t\t\tcenter: new Vec(w / 2, h - r),\n\t\t\t\tsweepFlag: 1,\n\t\t\t\tlargeArcFlag: 1,\n\t\t\t})\n\t\t\tthis.d = new Edge2d({ start: new Vec(0, h - r), end: new Vec(0, r) })\n\t\t} else {\n\t\t\tconst r = h / 2\n\t\t\tthis.a = new Arc2d({\n\t\t\t\tstart: new Vec(r, h),\n\t\t\t\tend: new Vec(r, 0),\n\t\t\t\tcenter: new Vec(r, r),\n\t\t\t\tsweepFlag: 1,\n\t\t\t\tlargeArcFlag: 1,\n\t\t\t})\n\t\t\tthis.b = new Edge2d({ start: new Vec(r, 0), end: new Vec(w - r, 0) })\n\t\t\tthis.c = new Arc2d({\n\t\t\t\tstart: new Vec(w - r, 0),\n\t\t\t\tend: new Vec(w - r, h),\n\t\t\t\tcenter: new Vec(w - r, r),\n\t\t\t\tsweepFlag: 1,\n\t\t\t\tlargeArcFlag: 1,\n\t\t\t})\n\t\t\tthis.d = new Edge2d({ start: new Vec(w - r, h), end: new Vec(r, h) })\n\t\t}\n\t}\n\n\tnearestPoint(A: VecLike): Vec {\n\t\tlet nearest: Vec | undefined\n\t\tlet dist = Infinity\n\t\tlet _d: number\n\t\tlet p: Vec\n\n\t\tconst { a, b, c, d } = this\n\t\tfor (const part of [a, b, c, d]) {\n\t\t\tp = part.nearestPoint(A)\n\t\t\t_d = Vec.Dist2(p, A)\n\t\t\tif (_d < dist) {\n\t\t\t\tnearest = p\n\t\t\t\tdist = _d\n\t\t\t}\n\t\t}\n\t\tif (!nearest) throw Error('nearest point not found')\n\t\treturn nearest\n\t}\n\n\thitTestLineSegment(A: VecLike, B: VecLike): boolean {\n\t\tconst { a, b, c, d } = this\n\t\treturn [a, b, c, d].some((edge) => edge.hitTestLineSegment(A, B))\n\t}\n\n\tgetVertices() {\n\t\tconst { a, b, c, d } = this\n\t\treturn [a, b, c, d].reduce<Vec[]>((a, p) => {\n\t\t\ta.push(...p.vertices)\n\t\t\treturn a\n\t\t}, [])\n\t}\n\n\tgetBounds() {\n\t\treturn new Box(0, 0, this.w, this.h)\n\t}\n\n\tgetLength() {\n\t\tconst { h, w } = this\n\t\tif (h > w) return (PI * (w / 2) + (h - w)) * 2\n\t\telse return (PI * (h / 2) + (w - h)) * 2\n\t}\n\n\tgetSvgPathData() {\n\t\tconst { a, b, c, d } = this\n\t\treturn [a, b, c, d].map((p, i) => p.getSvgPathData(i === 0)).join(' ') + ' Z'\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["a"]
7
7
  }
@@ -41,7 +41,8 @@ const debugFlags = {
41
41
  debugGeometry: createDebugValue("debugGeometry", { defaults: { all: false } }),
42
42
  hideShapes: createDebugValue("hideShapes", { defaults: { all: false } }),
43
43
  editOnType: createDebugValue("editOnType", { defaults: { all: false } }),
44
- a11y: createDebugValue("a11y", { defaults: { all: false } })
44
+ a11y: createDebugValue("a11y", { defaults: { all: false } }),
45
+ debugElbowArrows: createDebugValue("debugElbowArrows", { defaults: { all: false } })
45
46
  };
46
47
  if (typeof Element !== "undefined") {
47
48
  const nativeElementRemoveChild = Element.prototype.removeChild;
@@ -91,7 +92,9 @@ function createDebugValueBase(def) {
91
92
  configurable: true
92
93
  });
93
94
  }
94
- return Object.assign(valueAtom, def);
95
+ return Object.assign(valueAtom, def, {
96
+ reset: () => valueAtom.set(defaultValue)
97
+ });
95
98
  }
96
99
  function getStoredInitialValue(name) {
97
100
  try {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/utils/debug-flags.ts"],
4
- "sourcesContent": ["import { Atom, atom, react } from '@tldraw/state'\nimport { deleteFromSessionStorage, getFromSessionStorage, setInSessionStorage } from '@tldraw/utils'\n\n// --- 1. DEFINE ---\n//\n// Define your debug values and feature flags here. Use `createDebugValue` to\n// create an arbitrary value with defaults for production, staging, and\n// development. Use `createFeatureFlag` to create a boolean flag which will be\n// `true` by default in development and staging, and `false` in production.\n/** @internal */\nexport const featureFlags: Record<string, DebugFlag<boolean>> = {}\n\n/** @internal */\nexport const pointerCaptureTrackingObject = createDebugValue(\n\t'pointerCaptureTrackingObject',\n\t// ideally we wouldn't store this mutable value in an atom but it's not\n\t// a big deal for debug values\n\t{\n\t\tdefaults: { all: new Map<Element, number>() },\n\t\tshouldStoreForSession: false,\n\t}\n)\n\n/** @internal */\nexport const debugFlags = {\n\t// --- DEBUG VALUES ---\n\tlogPreventDefaults: createDebugValue('logPreventDefaults', {\n\t\tdefaults: { all: false },\n\t}),\n\tlogPointerCaptures: createDebugValue('logPointerCaptures', {\n\t\tdefaults: { all: false },\n\t}),\n\tlogElementRemoves: createDebugValue('logElementRemoves', {\n\t\tdefaults: { all: false },\n\t}),\n\tdebugSvg: createDebugValue('debugSvg', {\n\t\tdefaults: { all: false },\n\t}),\n\tshowFps: createDebugValue('showFps', {\n\t\tdefaults: { all: false },\n\t}),\n\tmeasurePerformance: createDebugValue('measurePerformance', { defaults: { all: false } }),\n\tthrowToBlob: createDebugValue('throwToBlob', {\n\t\tdefaults: { all: false },\n\t}),\n\treconnectOnPing: createDebugValue('reconnectOnPing', {\n\t\tdefaults: { all: false },\n\t}),\n\tdebugCursors: createDebugValue('debugCursors', {\n\t\tdefaults: { all: false },\n\t}),\n\tforceSrgb: createDebugValue('forceSrgbColors', { defaults: { all: false } }),\n\tdebugGeometry: createDebugValue('debugGeometry', { defaults: { all: false } }),\n\thideShapes: createDebugValue('hideShapes', { defaults: { all: false } }),\n\teditOnType: createDebugValue('editOnType', { defaults: { all: false } }),\n\ta11y: createDebugValue('a11y', { defaults: { all: false } }),\n} as const\n\ndeclare global {\n\tinterface Window {\n\t\ttldrawLog(message: any): void\n\t}\n}\n\n// --- 2. USE ---\n// In normal code, read from debug flags directly by calling .value on them:\n// if (debugFlags.preventDefaultLogging.value) { ... }\n//\n// In react, wrap your reads in `useValue` (or your component in `track`)\n// so they react to changes:\n// const shouldLog = useValue(debugFlags.preventDefaultLogging)\n\n// --- 3. GET FUNKY ---\n// If you need to do fun stuff like monkey-patching in response to flag changes,\n// add that here. Make sure you wrap your code in `react` so it runs\n// automatically when values change!\n\nif (typeof Element !== 'undefined') {\n\tconst nativeElementRemoveChild = Element.prototype.removeChild\n\treact('element removal logging', () => {\n\t\tif (debugFlags.logElementRemoves.get()) {\n\t\t\tElement.prototype.removeChild = function <T extends Node>(this: any, child: Node): T {\n\t\t\t\tconsole.warn('[tldraw] removing child:', child)\n\t\t\t\treturn nativeElementRemoveChild.call(this, child) as T\n\t\t\t}\n\t\t} else {\n\t\t\tElement.prototype.removeChild = nativeElementRemoveChild\n\t\t}\n\t})\n}\n\n// --- IMPLEMENTATION ---\n// you probably don't need to read this if you're just using the debug values system\nfunction createDebugValue<T>(\n\tname: string,\n\t{\n\t\tdefaults,\n\t\tshouldStoreForSession = true,\n\t}: { defaults: DebugFlagDefaults<T>; shouldStoreForSession?: boolean }\n) {\n\treturn createDebugValueBase({\n\t\tname,\n\t\tdefaults,\n\t\tshouldStoreForSession,\n\t})\n}\n\n// function createFeatureFlag<T>(\n// \tname: string,\n// \t{\n// \t\tdefaults,\n// \t\tshouldStoreForSession = true,\n// \t}: { defaults: DebugFlagDefaults<T>; shouldStoreForSession?: boolean }\n// ) {\n// \treturn createDebugValueBase({\n// \t\tname,\n// \t\tdefaults,\n// \t\tshouldStoreForSession,\n// \t})\n// }\n\nfunction createDebugValueBase<T>(def: DebugFlagDef<T>): DebugFlag<T> {\n\tconst defaultValue = getDefaultValue(def)\n\tconst storedValue = def.shouldStoreForSession\n\t\t? (getStoredInitialValue(def.name) as T | null)\n\t\t: null\n\tconst valueAtom = atom(`debug:${def.name}`, storedValue ?? defaultValue)\n\n\tif (typeof window !== 'undefined') {\n\t\tif (def.shouldStoreForSession) {\n\t\t\treact(`debug:${def.name}`, () => {\n\t\t\t\tconst currentValue = valueAtom.get()\n\t\t\t\tif (currentValue === defaultValue) {\n\t\t\t\t\tdeleteFromSessionStorage(`tldraw_debug:${def.name}`)\n\t\t\t\t} else {\n\t\t\t\t\tsetInSessionStorage(`tldraw_debug:${def.name}`, JSON.stringify(currentValue))\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\n\t\tObject.defineProperty(window, `tldraw${def.name.replace(/^[a-z]/, (l) => l.toUpperCase())}`, {\n\t\t\tget() {\n\t\t\t\treturn valueAtom.get()\n\t\t\t},\n\t\t\tset(newValue) {\n\t\t\t\tvalueAtom.set(newValue)\n\t\t\t},\n\t\t\tconfigurable: true,\n\t\t})\n\t}\n\n\treturn Object.assign(valueAtom, def)\n}\n\nfunction getStoredInitialValue(name: string) {\n\ttry {\n\t\treturn JSON.parse(getFromSessionStorage(`tldraw_debug:${name}`) ?? 'null')\n\t} catch {\n\t\treturn null\n\t}\n}\n\n// process.env might not be defined, but we can't access it using optional\n// chaining because some bundlers search for `process.env.SOMETHING` as a string\n// and replace it with its value.\nfunction readEnv(fn: () => string | undefined) {\n\ttry {\n\t\treturn fn()\n\t} catch {\n\t\treturn null\n\t}\n}\n\nfunction getDefaultValue<T>(def: DebugFlagDef<T>): T {\n\tconst env =\n\t\treadEnv(() => process.env.TLDRAW_ENV) ??\n\t\treadEnv(() => process.env.VERCEL_PUBLIC_TLDRAW_ENV) ??\n\t\treadEnv(() => process.env.NEXT_PUBLIC_TLDRAW_ENV) ??\n\t\t// default to production because if we don't have one of these, this is probably a library use\n\t\t'production'\n\n\tswitch (env) {\n\t\tcase 'production':\n\t\t\treturn def.defaults.production ?? def.defaults.all\n\t\tcase 'preview':\n\t\tcase 'staging':\n\t\t\treturn def.defaults.staging ?? def.defaults.all\n\t\tdefault:\n\t\t\treturn def.defaults.development ?? def.defaults.all\n\t}\n}\n\n/** @internal */\nexport interface DebugFlagDefaults<T> {\n\tdevelopment?: T\n\tstaging?: T\n\tproduction?: T\n\tall: T\n}\n\n/** @internal */\nexport interface DebugFlagDef<T> {\n\tname: string\n\tdefaults: DebugFlagDefaults<T>\n\tshouldStoreForSession: boolean\n}\n\n/** @internal */\nexport type DebugFlag<T> = DebugFlagDef<T> & Atom<T>\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Atom, atom, react } from '@tldraw/state'\nimport { deleteFromSessionStorage, getFromSessionStorage, setInSessionStorage } from '@tldraw/utils'\n\n// --- 1. DEFINE ---\n//\n// Define your debug values and feature flags here. Use `createDebugValue` to\n// create an arbitrary value with defaults for production, staging, and\n// development. Use `createFeatureFlag` to create a boolean flag which will be\n// `true` by default in development and staging, and `false` in production.\n/** @internal */\nexport const featureFlags: Record<string, DebugFlag<boolean>> = {}\n\n/** @internal */\nexport const pointerCaptureTrackingObject = createDebugValue(\n\t'pointerCaptureTrackingObject',\n\t// ideally we wouldn't store this mutable value in an atom but it's not\n\t// a big deal for debug values\n\t{\n\t\tdefaults: { all: new Map<Element, number>() },\n\t\tshouldStoreForSession: false,\n\t}\n)\n\n/** @internal */\nexport const debugFlags = {\n\t// --- DEBUG VALUES ---\n\tlogPreventDefaults: createDebugValue('logPreventDefaults', {\n\t\tdefaults: { all: false },\n\t}),\n\tlogPointerCaptures: createDebugValue('logPointerCaptures', {\n\t\tdefaults: { all: false },\n\t}),\n\tlogElementRemoves: createDebugValue('logElementRemoves', {\n\t\tdefaults: { all: false },\n\t}),\n\tdebugSvg: createDebugValue('debugSvg', {\n\t\tdefaults: { all: false },\n\t}),\n\tshowFps: createDebugValue('showFps', {\n\t\tdefaults: { all: false },\n\t}),\n\tmeasurePerformance: createDebugValue('measurePerformance', { defaults: { all: false } }),\n\tthrowToBlob: createDebugValue('throwToBlob', {\n\t\tdefaults: { all: false },\n\t}),\n\treconnectOnPing: createDebugValue('reconnectOnPing', {\n\t\tdefaults: { all: false },\n\t}),\n\tdebugCursors: createDebugValue('debugCursors', {\n\t\tdefaults: { all: false },\n\t}),\n\tforceSrgb: createDebugValue('forceSrgbColors', { defaults: { all: false } }),\n\tdebugGeometry: createDebugValue('debugGeometry', { defaults: { all: false } }),\n\thideShapes: createDebugValue('hideShapes', { defaults: { all: false } }),\n\teditOnType: createDebugValue('editOnType', { defaults: { all: false } }),\n\ta11y: createDebugValue('a11y', { defaults: { all: false } }),\n\tdebugElbowArrows: createDebugValue('debugElbowArrows', { defaults: { all: false } }),\n} as const\n\ndeclare global {\n\tinterface Window {\n\t\ttldrawLog(message: any): void\n\t}\n}\n\n// --- 2. USE ---\n// In normal code, read from debug flags directly by calling .value on them:\n// if (debugFlags.preventDefaultLogging.value) { ... }\n//\n// In react, wrap your reads in `useValue` (or your component in `track`)\n// so they react to changes:\n// const shouldLog = useValue(debugFlags.preventDefaultLogging)\n\n// --- 3. GET FUNKY ---\n// If you need to do fun stuff like monkey-patching in response to flag changes,\n// add that here. Make sure you wrap your code in `react` so it runs\n// automatically when values change!\n\nif (typeof Element !== 'undefined') {\n\tconst nativeElementRemoveChild = Element.prototype.removeChild\n\treact('element removal logging', () => {\n\t\tif (debugFlags.logElementRemoves.get()) {\n\t\t\tElement.prototype.removeChild = function <T extends Node>(this: any, child: Node): T {\n\t\t\t\tconsole.warn('[tldraw] removing child:', child)\n\t\t\t\treturn nativeElementRemoveChild.call(this, child) as T\n\t\t\t}\n\t\t} else {\n\t\t\tElement.prototype.removeChild = nativeElementRemoveChild\n\t\t}\n\t})\n}\n\n// --- IMPLEMENTATION ---\n// you probably don't need to read this if you're just using the debug values system\nfunction createDebugValue<T>(\n\tname: string,\n\t{\n\t\tdefaults,\n\t\tshouldStoreForSession = true,\n\t}: { defaults: DebugFlagDefaults<T>; shouldStoreForSession?: boolean }\n) {\n\treturn createDebugValueBase({\n\t\tname,\n\t\tdefaults,\n\t\tshouldStoreForSession,\n\t})\n}\n\n// function createFeatureFlag<T>(\n// \tname: string,\n// \t{\n// \t\tdefaults,\n// \t\tshouldStoreForSession = true,\n// \t}: { defaults: DebugFlagDefaults<T>; shouldStoreForSession?: boolean }\n// ) {\n// \treturn createDebugValueBase({\n// \t\tname,\n// \t\tdefaults,\n// \t\tshouldStoreForSession,\n// \t})\n// }\n\nfunction createDebugValueBase<T>(def: DebugFlagDef<T>): DebugFlag<T> {\n\tconst defaultValue = getDefaultValue(def)\n\tconst storedValue = def.shouldStoreForSession\n\t\t? (getStoredInitialValue(def.name) as T | null)\n\t\t: null\n\tconst valueAtom = atom(`debug:${def.name}`, storedValue ?? defaultValue)\n\n\tif (typeof window !== 'undefined') {\n\t\tif (def.shouldStoreForSession) {\n\t\t\treact(`debug:${def.name}`, () => {\n\t\t\t\tconst currentValue = valueAtom.get()\n\t\t\t\tif (currentValue === defaultValue) {\n\t\t\t\t\tdeleteFromSessionStorage(`tldraw_debug:${def.name}`)\n\t\t\t\t} else {\n\t\t\t\t\tsetInSessionStorage(`tldraw_debug:${def.name}`, JSON.stringify(currentValue))\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\n\t\tObject.defineProperty(window, `tldraw${def.name.replace(/^[a-z]/, (l) => l.toUpperCase())}`, {\n\t\t\tget() {\n\t\t\t\treturn valueAtom.get()\n\t\t\t},\n\t\t\tset(newValue) {\n\t\t\t\tvalueAtom.set(newValue)\n\t\t\t},\n\t\t\tconfigurable: true,\n\t\t})\n\t}\n\n\treturn Object.assign(valueAtom, def, {\n\t\treset: () => valueAtom.set(defaultValue),\n\t})\n}\n\nfunction getStoredInitialValue(name: string) {\n\ttry {\n\t\treturn JSON.parse(getFromSessionStorage(`tldraw_debug:${name}`) ?? 'null')\n\t} catch {\n\t\treturn null\n\t}\n}\n\n// process.env might not be defined, but we can't access it using optional\n// chaining because some bundlers search for `process.env.SOMETHING` as a string\n// and replace it with its value.\nfunction readEnv(fn: () => string | undefined) {\n\ttry {\n\t\treturn fn()\n\t} catch {\n\t\treturn null\n\t}\n}\n\nfunction getDefaultValue<T>(def: DebugFlagDef<T>): T {\n\tconst env =\n\t\treadEnv(() => process.env.TLDRAW_ENV) ??\n\t\treadEnv(() => process.env.VERCEL_PUBLIC_TLDRAW_ENV) ??\n\t\treadEnv(() => process.env.NEXT_PUBLIC_TLDRAW_ENV) ??\n\t\t// default to production because if we don't have one of these, this is probably a library use\n\t\t'production'\n\n\tswitch (env) {\n\t\tcase 'production':\n\t\t\treturn def.defaults.production ?? def.defaults.all\n\t\tcase 'preview':\n\t\tcase 'staging':\n\t\t\treturn def.defaults.staging ?? def.defaults.all\n\t\tdefault:\n\t\t\treturn def.defaults.development ?? def.defaults.all\n\t}\n}\n\n/** @internal */\nexport interface DebugFlagDefaults<T> {\n\tdevelopment?: T\n\tstaging?: T\n\tproduction?: T\n\tall: T\n}\n\n/** @internal */\nexport interface DebugFlagDef<T> {\n\tname: string\n\tdefaults: DebugFlagDefaults<T>\n\tshouldStoreForSession: boolean\n}\n\n/** @internal */\nexport interface DebugFlag<T> extends DebugFlagDef<T>, Atom<T> {\n\treset(): void\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,8 +1,8 @@
1
- const version = "3.13.0-canary.8e04030e54fe";
1
+ const version = "3.13.0-canary.8f8ae1660cdb";
2
2
  const publishDates = {
3
3
  major: "2024-09-13T14:36:29.063Z",
4
- minor: "2025-05-07T09:08:57.011Z",
5
- patch: "2025-05-07T09:08:57.011Z"
4
+ minor: "2025-05-08T11:53:57.810Z",
5
+ patch: "2025-05-08T11:53:57.810Z"
6
6
  };
7
7
  export {
8
8
  publishDates,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/version.ts"],
4
- "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.13.0-canary.8e04030e54fe'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-05-07T09:08:57.011Z',\n\tpatch: '2025-05-07T09:08:57.011Z',\n}\n"],
4
+ "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.13.0-canary.8f8ae1660cdb'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-05-08T11:53:57.810Z',\n\tpatch: '2025-05-08T11:53:57.810Z',\n}\n"],
5
5
  "mappings": "AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
6
6
  "names": []
7
7
  }
package/editor.css CHANGED
@@ -600,6 +600,31 @@ input,
600
600
  }
601
601
  }
602
602
 
603
+ /* --------------------- Arrow Hints -------------------- */
604
+
605
+ .tl-arrow-hint-handle {
606
+ fill: var(--color-selected-contrast);
607
+ stroke: var(--color-selection-stroke);
608
+ stroke-width: calc(1.5px * var(--tl-scale));
609
+ r: calc(4px * var(--tl-scale));
610
+ }
611
+
612
+ .tl-arrow-hint-snap {
613
+ stroke: transparent;
614
+ fill: var(--color-selection-fill);
615
+ r: calc(12px * var(--tl-scale));
616
+ }
617
+
618
+ .tl-arrow-hint-snap__none,
619
+ .tl-arrow-hint-snap__center,
620
+ .tl-arrow-hint-snap__axis {
621
+ display: none;
622
+ }
623
+
624
+ .tl-arrow-hint-snap__edge {
625
+ r: calc(8px * var(--tl-scale));
626
+ }
627
+
603
628
  /* ------------------ Bounds Detail ----------------- */
604
629
 
605
630
  .tl-image,
@@ -969,6 +994,8 @@ input,
969
994
 
970
995
  .tl-rich-text p {
971
996
  margin: 0;
997
+ /* Depending on the extensions, <p> tags can be empty, without a <br />. */
998
+ min-height: 1lh;
972
999
  }
973
1000
 
974
1001
  .tl-rich-text ul,
@@ -976,6 +1003,8 @@ input,
976
1003
  text-align: left;
977
1004
  margin: 0;
978
1005
  padding-left: 3.25ch;
1006
+ /* Some resets, like Tailwind, nix the list styling. */
1007
+ list-style: revert;
979
1008
  }
980
1009
 
981
1010
  .tl-rich-text ol:has(> li:nth-child(10)) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tldraw/editor",
3
3
  "description": "A tiny little drawing app (editor).",
4
- "version": "3.13.0-canary.8e04030e54fe",
4
+ "version": "3.13.0-canary.8f8ae1660cdb",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -48,12 +48,12 @@
48
48
  "@tiptap/core": "^2.9.1",
49
49
  "@tiptap/pm": "^2.9.1",
50
50
  "@tiptap/react": "^2.9.1",
51
- "@tldraw/state": "3.13.0-canary.8e04030e54fe",
52
- "@tldraw/state-react": "3.13.0-canary.8e04030e54fe",
53
- "@tldraw/store": "3.13.0-canary.8e04030e54fe",
54
- "@tldraw/tlschema": "3.13.0-canary.8e04030e54fe",
55
- "@tldraw/utils": "3.13.0-canary.8e04030e54fe",
56
- "@tldraw/validate": "3.13.0-canary.8e04030e54fe",
51
+ "@tldraw/state": "3.13.0-canary.8f8ae1660cdb",
52
+ "@tldraw/state-react": "3.13.0-canary.8f8ae1660cdb",
53
+ "@tldraw/store": "3.13.0-canary.8f8ae1660cdb",
54
+ "@tldraw/tlschema": "3.13.0-canary.8f8ae1660cdb",
55
+ "@tldraw/utils": "3.13.0-canary.8f8ae1660cdb",
56
+ "@tldraw/validate": "3.13.0-canary.8f8ae1660cdb",
57
57
  "@types/core-js": "^2.5.8",
58
58
  "@use-gesture/react": "^10.3.1",
59
59
  "classnames": "^2.5.1",
package/src/index.ts CHANGED
@@ -4,37 +4,11 @@ import 'core-js/stable/array/flat-map.js'
4
4
  import 'core-js/stable/array/flat.js'
5
5
  import 'core-js/stable/string/at.js'
6
6
  import 'core-js/stable/string/replace-all.js'
7
- export {
8
- EMPTY_ARRAY,
9
- EffectScheduler,
10
- atom,
11
- computed,
12
- react,
13
- transact,
14
- transaction,
15
- whyAmIRunning,
16
- type Atom,
17
- type Signal,
18
- } from '@tldraw/state'
19
- export {
20
- track,
21
- useAtom,
22
- useComputed,
23
- useQuickReactor,
24
- useReactor,
25
- useStateTracking,
26
- useValue,
27
- } from '@tldraw/state-react'
28
- export { resizeScaled } from './lib/editor/shapes/shared/resizeScaled'
29
- export {
30
- getFontsFromRichText,
31
- type RichTextFontVisitor,
32
- type RichTextFontVisitorState,
33
- type TLTextOptions,
34
- type TiptapEditor,
35
- type TiptapNode,
36
- } from './lib/utils/richText'
37
- export { LocalIndexedDb, Table, type StoreName } from './lib/utils/sync/LocalIndexedDb'
7
+
8
+ // eslint-disable-next-line local/no-export-star
9
+ export * from '@tldraw/state'
10
+ // eslint-disable-next-line local/no-export-star
11
+ export * from '@tldraw/state-react'
38
12
  // eslint-disable-next-line local/no-export-star
39
13
  export * from '@tldraw/store'
40
14
  // eslint-disable-next-line local/no-export-star
@@ -43,6 +17,7 @@ export * from '@tldraw/tlschema'
43
17
  export * from '@tldraw/utils'
44
18
  // eslint-disable-next-line local/no-export-star
45
19
  export * from '@tldraw/validate'
20
+
46
21
  export {
47
22
  ErrorScreen,
48
23
  LoadingScreen,
@@ -212,6 +187,7 @@ export {
212
187
  export { GroupShapeUtil } from './lib/editor/shapes/group/GroupShapeUtil'
213
188
  export { getPerfectDashProps } from './lib/editor/shapes/shared/getPerfectDashProps'
214
189
  export { resizeBox, type ResizeBoxOptions } from './lib/editor/shapes/shared/resizeBox'
190
+ export { resizeScaled } from './lib/editor/shapes/shared/resizeScaled'
215
191
  export { BaseBoxShapeTool } from './lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool'
216
192
  export { maybeSnapToGrid } from './lib/editor/tools/BaseBoxShapeTool/children/Pointing'
217
193
  export { StateNode, type TLStateNodeConstructor } from './lib/editor/tools/StateNode'
@@ -459,12 +435,21 @@ export { hardResetEditor } from './lib/utils/hardResetEditor'
459
435
  export { isAccelKey } from './lib/utils/keyboard'
460
436
  export { normalizeWheel } from './lib/utils/normalizeWheel'
461
437
  export { refreshPage } from './lib/utils/refreshPage'
438
+ export {
439
+ getFontsFromRichText,
440
+ type RichTextFontVisitor,
441
+ type RichTextFontVisitorState,
442
+ type TLTextOptions,
443
+ type TiptapEditor,
444
+ type TiptapNode,
445
+ } from './lib/utils/richText'
462
446
  export {
463
447
  applyRotationToSnapshotShapes,
464
448
  getRotationSnapshot,
465
449
  type TLRotationSnapshot,
466
450
  } from './lib/utils/rotation'
467
451
  export { runtime, setRuntimeOverrides } from './lib/utils/runtime'
452
+ export { LocalIndexedDb, Table, type StoreName } from './lib/utils/sync/LocalIndexedDb'
468
453
  export { type TLStoreWithStatus } from './lib/utils/sync/StoreWithStatus'
469
454
  export { hardReset } from './lib/utils/sync/hardReset'
470
455
  export { uniq } from './lib/utils/uniq'
@@ -52,7 +52,7 @@ beforeEach(() => {
52
52
  shapeUtils: [CustomShape],
53
53
  bindingUtils: [],
54
54
  tools: [],
55
- store: createTLStore({ shapeUtils: [CustomShape] }),
55
+ store: createTLStore({ shapeUtils: [CustomShape], bindingUtils: [] }),
56
56
  getContainer: () => document.body,
57
57
  })
58
58
  editor.setCameraOptions({ isLocked: true })
@@ -10343,12 +10343,10 @@ export class Editor extends EventEmitter<TLEventMap> {
10343
10343
  if (this.inputs.isPanning && this.inputs.isPointing) {
10344
10344
  // Handle spacebar / middle mouse button panning
10345
10345
  const { currentScreenPoint, previousScreenPoint } = this.inputs
10346
- const { panSpeed } = cameraOptions
10347
10346
  const offset = Vec.Sub(currentScreenPoint, previousScreenPoint)
10348
- this.setCamera(
10349
- new Vec(cx + (offset.x * panSpeed) / cz, cy + (offset.y * panSpeed) / cz, cz),
10350
- { immediate: true }
10351
- )
10347
+ this.setCamera(new Vec(cx + offset.x / cz, cy + offset.y / cz, cz), {
10348
+ immediate: true,
10349
+ })
10352
10350
  this.maybeTrackPerformance('Panning')
10353
10351
  return
10354
10352
  }
@@ -45,7 +45,6 @@ export interface HandleSnapGeometry {
45
45
 
46
46
  const defaultGetSelfSnapOutline = () => null
47
47
  const defaultGetSelfSnapPoints = () => []
48
-
49
48
  /** @public */
50
49
  export class HandleSnaps {
51
50
  readonly editor: Editor
@@ -4,15 +4,15 @@ import { TLDefaultDashStyle } from '@tldraw/tlschema'
4
4
  export function getPerfectDashProps(
5
5
  totalLength: number,
6
6
  strokeWidth: number,
7
- opts = {} as Partial<{
8
- style: TLDefaultDashStyle
9
- snap: number
10
- end: 'skip' | 'outset' | 'none'
11
- start: 'skip' | 'outset' | 'none'
12
- lengthRatio: number
13
- closed: boolean
14
- forceSolid: boolean
15
- }>
7
+ opts: {
8
+ style?: TLDefaultDashStyle
9
+ snap?: number
10
+ end?: 'skip' | 'outset' | 'none'
11
+ start?: 'skip' | 'outset' | 'none'
12
+ lengthRatio?: number
13
+ closed?: boolean
14
+ forceSolid?: boolean
15
+ } = {}
16
16
  ): {
17
17
  strokeDasharray: string
18
18
  strokeDashoffset: string
@@ -57,6 +57,11 @@ export class Box {
57
57
  this.x = n
58
58
  }
59
59
 
60
+ // eslint-disable-next-line no-restricted-syntax
61
+ get left() {
62
+ return this.x
63
+ }
64
+
60
65
  // eslint-disable-next-line no-restricted-syntax
61
66
  get midX() {
62
67
  return this.x + this.w / 2
@@ -67,6 +72,11 @@ export class Box {
67
72
  return this.x + this.w
68
73
  }
69
74
 
75
+ // eslint-disable-next-line no-restricted-syntax
76
+ get right() {
77
+ return this.x + this.w
78
+ }
79
+
70
80
  // eslint-disable-next-line no-restricted-syntax
71
81
  get minY() {
72
82
  return this.y
@@ -77,6 +87,11 @@ export class Box {
77
87
  this.y = n
78
88
  }
79
89
 
90
+ // eslint-disable-next-line no-restricted-syntax
91
+ get top() {
92
+ return this.y
93
+ }
94
+
80
95
  // eslint-disable-next-line no-restricted-syntax
81
96
  get midY() {
82
97
  return this.y + this.h / 2
@@ -87,6 +102,11 @@ export class Box {
87
102
  return this.y + this.h
88
103
  }
89
104
 
105
+ // eslint-disable-next-line no-restricted-syntax
106
+ get bottom() {
107
+ return this.y + this.h
108
+ }
109
+
90
110
  // eslint-disable-next-line no-restricted-syntax
91
111
  get width() {
92
112
  return this.w
@@ -157,12 +157,13 @@ export class Mat {
157
157
  return Mat.Compose(Mat.Translate(cx, cy!), rotationMatrix, Mat.Translate(-cx, -cy!))
158
158
  }
159
159
 
160
- static Scale(x: number, y: number): MatModel
161
- static Scale(x: number, y: number, cx: number, cy: number): MatModel
162
- static Scale(x: number, y: number, cx?: number, cy?: number): MatModel {
160
+ static Scale(x: number, y: number): Mat
161
+ static Scale(x: number, y: number, cx: number, cy: number): Mat
162
+ static Scale(x: number, y: number, cx?: number, cy?: number): Mat {
163
163
  const scaleMatrix = new Mat(x, 0, 0, y, 0, 0)
164
164
  if (cx === undefined) return scaleMatrix
165
- return Mat.Compose(Mat.Translate(cx, cy!), scaleMatrix, Mat.Translate(-cx, -cy!))
165
+
166
+ return Mat.Translate(cx, cy!).multiply(scaleMatrix).translate(-cx, -cy!)
166
167
  }
167
168
  static Multiply(m1: MatModel, m2: MatModel): MatModel {
168
169
  return {
@@ -319,6 +319,11 @@ export class Vec {
319
319
  return ((A.y - B.y) ** 2 + (A.x - B.x) ** 2) ** 0.5
320
320
  }
321
321
 
322
+ // Get the Manhattan distance between two points.
323
+ static ManhattanDist(A: VecLike, B: VecLike): number {
324
+ return Math.abs(A.x - B.x) + Math.abs(A.y - B.y)
325
+ }
326
+
322
327
  // Get whether a distance between two points is less than a number. This is faster to calulate than using `Vec.Dist(a, b) < n`.
323
328
  static DistMin(A: VecLike, B: VecLike, n: number): boolean {
324
329
  return (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) < n ** 2
@@ -465,10 +470,28 @@ export class Vec {
465
470
  return isNaN(A.x) || isNaN(A.y)
466
471
  }
467
472
 
473
+ /**
474
+ * Get the angle from position A to position B.
475
+ */
468
476
  static Angle(A: VecLike, B: VecLike): number {
469
477
  return Math.atan2(B.y - A.y, B.x - A.x)
470
478
  }
471
479
 
480
+ /**
481
+ * Get the angle between vector A and vector B. This will return the smallest angle between the
482
+ * two vectors, between -π and π. The sign indicates direction of angle.
483
+ */
484
+ static AngleBetween(A: VecLike, B: VecLike): number {
485
+ const p = A.x * B.x + A.y * B.y
486
+ const n = Math.sqrt(
487
+ (Math.pow(A.x, 2) + Math.pow(A.y, 2)) * (Math.pow(B.x, 2) + Math.pow(B.y, 2))
488
+ )
489
+ const sign = A.x * B.y - A.y * B.x < 0 ? -1 : 1
490
+ const angle = sign * Math.acos(p / n)
491
+
492
+ return angle
493
+ }
494
+
472
495
  /**
473
496
  * Linearly interpolate between two points.
474
497
  * @param A - The first point.
@@ -1,4 +1,4 @@
1
- import { Vec } from '../Vec'
1
+ import { Vec, VecLike } from '../Vec'
2
2
  import { intersectLineSegmentCircle } from '../intersect'
3
3
  import { getArcMeasure, getPointInArcT, getPointOnCircle } from '../utils'
4
4
  import { Geometry2d, Geometry2dOptions } from './Geometry2d'
@@ -44,14 +44,14 @@ export class Arc2d extends Geometry2d {
44
44
  this._center = center
45
45
  }
46
46
 
47
- nearestPoint(point: Vec): Vec {
47
+ nearestPoint(point: VecLike): Vec {
48
48
  const { _center, measure, radius, angleEnd, angleStart, start: A, end: B } = this
49
49
  const t = getPointInArcT(measure, angleStart, angleEnd, _center.angle(point))
50
50
  if (t <= 0) return A
51
51
  if (t >= 1) return B
52
52
 
53
53
  // Get the point (P) on the arc, then pick the nearest of A, B, and P
54
- const P = _center.clone().add(point.clone().sub(_center).uni().mul(radius))
54
+ const P = Vec.Sub(point, _center).uni().mul(radius).add(_center)
55
55
 
56
56
  let nearest: Vec | undefined
57
57
  let dist = Infinity
@@ -67,7 +67,7 @@ export class Arc2d extends Geometry2d {
67
67
  return nearest
68
68
  }
69
69
 
70
- hitTestLineSegment(A: Vec, B: Vec): boolean {
70
+ hitTestLineSegment(A: VecLike, B: VecLike): boolean {
71
71
  const { _center, radius, measure, angleStart, angleEnd } = this
72
72
  const intersection = intersectLineSegmentCircle(A, B, _center, radius)
73
73
  if (intersection === null) return false
@@ -95,6 +95,6 @@ export class Arc2d extends Geometry2d {
95
95
  }
96
96
 
97
97
  override getLength() {
98
- return this.measure * this.radius
98
+ return Math.abs(this.measure * this.radius)
99
99
  }
100
100
  }
@@ -1,5 +1,5 @@
1
1
  import { Box } from '../Box'
2
- import { Vec } from '../Vec'
2
+ import { Vec, VecLike } from '../Vec'
3
3
  import { intersectLineSegmentCircle } from '../intersect'
4
4
  import { PI2, getPointOnCircle } from '../utils'
5
5
  import { Geometry2d, Geometry2dOptions } from './Geometry2d'
@@ -43,13 +43,13 @@ export class Circle2d extends Geometry2d {
43
43
  return vertices
44
44
  }
45
45
 
46
- nearestPoint(point: Vec): Vec {
46
+ nearestPoint(point: VecLike): Vec {
47
47
  const { _center, radius } = this
48
48
  if (_center.equals(point)) return Vec.AddXY(_center, radius, 0)
49
- return _center.clone().add(point.clone().sub(_center).uni().mul(radius))
49
+ return Vec.Sub(point, _center).uni().mul(radius).add(_center)
50
50
  }
51
51
 
52
- hitTestLineSegment(A: Vec, B: Vec, distance = 0): boolean {
52
+ hitTestLineSegment(A: VecLike, B: VecLike, distance = 0): boolean {
53
53
  const { _center, radius } = this
54
54
  return intersectLineSegmentCircle(A, B, _center, radius + distance) !== null
55
55
  }
@@ -1,5 +1,5 @@
1
- import { Vec } from '../Vec'
2
- import { Geometry2dOptions } from './Geometry2d'
1
+ import { Vec, VecLike } from '../Vec'
2
+ import { Geometry2dFilters, Geometry2dOptions } from './Geometry2d'
3
3
  import { Polyline2d } from './Polyline2d'
4
4
 
5
5
  /** @public */
@@ -52,7 +52,7 @@ export class CubicBezier2d extends Polyline2d {
52
52
  return CubicBezier2d.GetAtT(this, 0.5)
53
53
  }
54
54
 
55
- nearestPoint(A: Vec): Vec {
55
+ nearestPoint(A: VecLike): Vec {
56
56
  let nearest: Vec | undefined
57
57
  let dist = Infinity
58
58
  let d: number
@@ -89,7 +89,7 @@ export class CubicBezier2d extends Polyline2d {
89
89
  )
90
90
  }
91
91
 
92
- override getLength(precision = 32) {
92
+ override getLength(filters?: Geometry2dFilters, precision = 32) {
93
93
  let n1: Vec,
94
94
  p1 = this.a,
95
95
  length = 0
@@ -1,4 +1,4 @@
1
- import { Vec } from '../Vec'
1
+ import { Vec, VecLike } from '../Vec'
2
2
  import { CubicBezier2d } from './CubicBezier2d'
3
3
  import { Geometry2d, Geometry2dOptions } from './Geometry2d'
4
4
 
@@ -58,7 +58,7 @@ export class CubicSpline2d extends Geometry2d {
58
58
  return vertices
59
59
  }
60
60
 
61
- nearestPoint(A: Vec): Vec {
61
+ nearestPoint(A: VecLike): Vec {
62
62
  let nearest: Vec | undefined
63
63
  let dist = Infinity
64
64
  let d: number
@@ -75,7 +75,7 @@ export class CubicSpline2d extends Geometry2d {
75
75
  return nearest
76
76
  }
77
77
 
78
- hitTestLineSegment(A: Vec, B: Vec): boolean {
78
+ hitTestLineSegment(A: VecLike, B: VecLike): boolean {
79
79
  return this.segments.some((segment) => segment.hitTestLineSegment(A, B))
80
80
  }
81
81
 
@@ -1,5 +1,5 @@
1
- import { Vec } from '../Vec'
2
1
  import { linesIntersect } from '../intersect'
2
+ import { Vec, VecLike } from '../Vec'
3
3
  import { Geometry2d } from './Geometry2d'
4
4
 
5
5
  /** @public */
@@ -34,7 +34,7 @@ export class Edge2d extends Geometry2d {
34
34
  return [this.start, this.end]
35
35
  }
36
36
 
37
- override nearestPoint(point: Vec): Vec {
37
+ override nearestPoint(point: VecLike): Vec {
38
38
  const { start, end, d, u, ul: l } = this
39
39
  if (d.len() === 0) return start // start and end are the same
40
40
  if (l === 0) return start // no length in the unit vector
@@ -48,7 +48,7 @@ export class Edge2d extends Geometry2d {
48
48
  return new Vec(cx, cy)
49
49
  }
50
50
 
51
- override hitTestLineSegment(A: Vec, B: Vec, distance = 0): boolean {
51
+ override hitTestLineSegment(A: VecLike, B: VecLike, distance = 0): boolean {
52
52
  return (
53
53
  linesIntersect(A, B, this.start, this.end) || this.distanceToLineSegment(A, B) <= distance
54
54
  )
@@ -1,5 +1,5 @@
1
1
  import { Box } from '../Box'
2
- import { Vec } from '../Vec'
2
+ import { Vec, VecLike } from '../Vec'
3
3
  import { PI, PI2, perimeterOfEllipse } from '../utils'
4
4
  import { Edge2d } from './Edge2d'
5
5
  import { Geometry2d, Geometry2dOptions } from './Geometry2d'
@@ -73,7 +73,7 @@ export class Ellipse2d extends Geometry2d {
73
73
  return vertices
74
74
  }
75
75
 
76
- nearestPoint(A: Vec): Vec {
76
+ nearestPoint(A: VecLike): Vec {
77
77
  let nearest: Vec | undefined
78
78
  let dist = Infinity
79
79
  let d: number
@@ -90,7 +90,7 @@ export class Ellipse2d extends Geometry2d {
90
90
  return nearest
91
91
  }
92
92
 
93
- hitTestLineSegment(A: Vec, B: Vec): boolean {
93
+ hitTestLineSegment(A: VecLike, B: VecLike): boolean {
94
94
  return this.edges.some((edge) => edge.hitTestLineSegment(A, B))
95
95
  }
96
96