@tldraw/editor 5.2.0-next.b91d4a4551c9 → 5.2.0-next.cd4a35fc06d5

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 (58) hide show
  1. package/README.md +1 -1
  2. package/dist-cjs/index.d.ts +7 -43
  3. package/dist-cjs/index.js +3 -4
  4. package/dist-cjs/index.js.map +2 -2
  5. package/dist-cjs/lib/editor/Editor.js +55 -16
  6. package/dist-cjs/lib/editor/Editor.js.map +3 -3
  7. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js +8 -58
  8. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +2 -2
  9. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js +1 -1
  10. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js.map +2 -2
  11. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js +1 -2
  12. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +2 -2
  13. package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js +79 -0
  14. package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js.map +7 -0
  15. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  16. package/dist-cjs/lib/editor/types/event-types.js +0 -2
  17. package/dist-cjs/lib/editor/types/event-types.js.map +2 -2
  18. package/dist-cjs/version.js +3 -3
  19. package/dist-cjs/version.js.map +1 -1
  20. package/dist-esm/index.d.mts +7 -43
  21. package/dist-esm/index.mjs +2 -6
  22. package/dist-esm/index.mjs.map +2 -2
  23. package/dist-esm/lib/editor/Editor.mjs +55 -16
  24. package/dist-esm/lib/editor/Editor.mjs.map +3 -3
  25. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs +8 -58
  26. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +2 -2
  27. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs +1 -1
  28. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs.map +2 -2
  29. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs +1 -2
  30. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +2 -2
  31. package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs +59 -0
  32. package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs.map +7 -0
  33. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  34. package/dist-esm/lib/editor/types/event-types.mjs +0 -2
  35. package/dist-esm/lib/editor/types/event-types.mjs.map +2 -2
  36. package/dist-esm/version.mjs +3 -3
  37. package/dist-esm/version.mjs.map +1 -1
  38. package/editor.css +2 -0
  39. package/package.json +8 -8
  40. package/src/index.ts +1 -5
  41. package/src/lib/editor/Editor.ts +87 -24
  42. package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +54 -74
  43. package/src/lib/editor/managers/ClickManager/ClickManager.ts +15 -65
  44. package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.test.ts +14 -0
  45. package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.ts +6 -3
  46. package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +4 -4
  47. package/src/lib/editor/managers/FocusManager/FocusManager.ts +1 -2
  48. package/src/lib/editor/managers/FontManager/FontManager.test.ts +13 -9
  49. package/src/lib/editor/managers/TextManager/TextManager.test.ts +16 -14
  50. package/src/lib/editor/overlays/strokeShapeIndicators.ts +86 -0
  51. package/src/lib/editor/tools/StateNode.ts +0 -2
  52. package/src/lib/editor/types/event-types.ts +2 -6
  53. package/src/version.ts +3 -3
  54. package/dist-cjs/lib/editor/overlays/ShapeIndicatorOverlayUtil.js +0 -161
  55. package/dist-cjs/lib/editor/overlays/ShapeIndicatorOverlayUtil.js.map +0 -7
  56. package/dist-esm/lib/editor/overlays/ShapeIndicatorOverlayUtil.mjs +0 -141
  57. package/dist-esm/lib/editor/overlays/ShapeIndicatorOverlayUtil.mjs.map +0 -7
  58. package/src/lib/editor/overlays/ShapeIndicatorOverlayUtil.ts +0 -216
@@ -15,12 +15,14 @@ import { FontManager } from './FontManager'
15
15
  vi.mock('../../Editor')
16
16
 
17
17
  // Mock globals
18
- global.FontFace = vi.fn().mockImplementation((family, src, descriptors) => ({
19
- family,
20
- src,
21
- ...descriptors,
22
- load: vi.fn(() => Promise.resolve()),
23
- }))
18
+ global.FontFace = vi.fn().mockImplementation(function (family: any, src: any, descriptors: any) {
19
+ return {
20
+ family,
21
+ src,
22
+ ...descriptors,
23
+ load: vi.fn(() => Promise.resolve()),
24
+ }
25
+ })
24
26
 
25
27
  Object.defineProperty(global.document, 'fonts', {
26
28
  value: {
@@ -200,9 +202,11 @@ describe('FontManager', () => {
200
202
  const font = createMockFont()
201
203
  const error = new Error('Font load failed')
202
204
 
203
- ;(global.FontFace as Mock).mockReturnValue({
204
- family: font.family,
205
- load: vi.fn(() => Promise.reject(error)),
205
+ ;(global.FontFace as Mock).mockImplementationOnce(function () {
206
+ return {
207
+ family: font.family,
208
+ load: vi.fn(() => Promise.reject(error)),
209
+ }
206
210
  })
207
211
 
208
212
  const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
@@ -82,20 +82,22 @@ const mockEditor = {
82
82
  getContainerDocument: vi.fn(() => mockDocument),
83
83
  } as unknown as Editor
84
84
 
85
- global.Range = vi.fn(() => ({
86
- setStart: vi.fn(),
87
- setEnd: vi.fn(),
88
- getClientRects: vi.fn(() => [
89
- {
90
- width: 10,
91
- height: 16,
92
- left: 0,
93
- top: 0,
94
- right: 10,
95
- bottom: 16,
96
- },
97
- ]),
98
- })) as any
85
+ global.Range = vi.fn(function () {
86
+ return {
87
+ setStart: vi.fn(),
88
+ setEnd: vi.fn(),
89
+ getClientRects: vi.fn(() => [
90
+ {
91
+ width: 10,
92
+ height: 16,
93
+ left: 0,
94
+ top: 0,
95
+ right: 10,
96
+ bottom: 16,
97
+ },
98
+ ]),
99
+ }
100
+ }) as any
99
101
 
100
102
  describe('TextManager', () => {
101
103
  let textManager: TextManager
@@ -0,0 +1,86 @@
1
+ import { createComputedCache } from '@tldraw/store'
2
+ import { TLShape, TLShapeId } from '@tldraw/tlschema'
3
+ import type { Editor } from '../Editor'
4
+
5
+ const indicatorPathCache = createComputedCache(
6
+ 'shapeIndicatorPath',
7
+ (editor: Editor, shape: TLShape) => {
8
+ const util = editor.getShapeUtil(shape)
9
+ return util.getIndicatorPath(shape)
10
+ },
11
+ {
12
+ areRecordsEqual(a, b) {
13
+ return a.props === b.props
14
+ },
15
+ }
16
+ )
17
+
18
+ /**
19
+ * Combine every batchable shape indicator into a single page-space `Path2D` and
20
+ * emit one stroke call. Shapes whose indicator needs an evenodd clip (e.g.
21
+ * arrows with labels or complex arrowheads) can't be batched — they still
22
+ * stroke individually inside a save/restore with `ctx.clip` applied.
23
+ *
24
+ * Shared by any overlay util that paints shape indicators (e.g. collaborator
25
+ * selections).
26
+ *
27
+ * @public
28
+ */
29
+ export function strokeShapeIndicators(
30
+ editor: Editor,
31
+ ctx: CanvasRenderingContext2D,
32
+ shapeIds: TLShapeId[]
33
+ ): void {
34
+ if (shapeIds.length === 0) return
35
+
36
+ const batched = new Path2D()
37
+
38
+ for (const shapeId of shapeIds) {
39
+ const shape = editor.getShape(shapeId)
40
+ if (!shape || shape.isLocked) continue
41
+
42
+ const pageTransform = editor.getShapePageTransform(shape)
43
+ if (!pageTransform) continue
44
+
45
+ const indicatorPath = indicatorPathCache.get(editor, shape.id)
46
+ if (!indicatorPath) continue
47
+
48
+ if (indicatorPath instanceof Path2D) {
49
+ batched.addPath(indicatorPath, pageTransform)
50
+ continue
51
+ }
52
+
53
+ const { path, clipPath, additionalPaths } = indicatorPath
54
+
55
+ if (!clipPath) {
56
+ batched.addPath(path, pageTransform)
57
+ if (additionalPaths) {
58
+ for (const p of additionalPaths) batched.addPath(p, pageTransform)
59
+ }
60
+ continue
61
+ }
62
+
63
+ // Clipped case: fall back to an individual stroke. Rare (arrows with
64
+ // labels / complex arrowheads), so the extra save/restore/stroke
65
+ // pair per such shape isn't worth batching away.
66
+ ctx.save()
67
+ ctx.transform(
68
+ pageTransform.a,
69
+ pageTransform.b,
70
+ pageTransform.c,
71
+ pageTransform.d,
72
+ pageTransform.e,
73
+ pageTransform.f
74
+ )
75
+ ctx.save()
76
+ ctx.clip(clipPath, 'evenodd')
77
+ ctx.stroke(path)
78
+ ctx.restore()
79
+ if (additionalPaths) {
80
+ for (const p of additionalPaths) ctx.stroke(p)
81
+ }
82
+ ctx.restore()
83
+ }
84
+
85
+ ctx.stroke(batched)
86
+ }
@@ -268,8 +268,6 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
268
268
  onLongPress?(info: TLPointerEventInfo): void
269
269
  onPointerUp?(info: TLPointerEventInfo): void
270
270
  onDoubleClick?(info: TLClickEventInfo): void
271
- onTripleClick?(info: TLClickEventInfo): void
272
- onQuadrupleClick?(info: TLClickEventInfo): void
273
271
  onRightClick?(info: TLPointerEventInfo): void
274
272
  onMiddleClick?(info: TLPointerEventInfo): void
275
273
  onKeyDown?(info: TLKeyboardEventInfo): void
@@ -24,7 +24,7 @@ export type TLPointerEventName =
24
24
  | 'middle_click'
25
25
 
26
26
  /** @public */
27
- export type TLCLickEventName = 'double_click' | 'triple_click' | 'quadruple_click'
27
+ export type TLCLickEventName = 'double_click'
28
28
 
29
29
  /** @public */
30
30
  export type TLPinchEventName = 'pinch_start' | 'pinch' | 'pinch_end'
@@ -72,7 +72,7 @@ export type TLClickEventInfo = TLBaseEventInfo & {
72
72
  point: VecLike
73
73
  pointerId: number
74
74
  button: number
75
- phase: 'down' | 'up' | 'settle'
75
+ phase: 'down' | 'up' | 'settle-down' | 'settle-up'
76
76
  } & TLPointerEventTarget
77
77
 
78
78
  /** @public */
@@ -173,8 +173,6 @@ export interface TLEventHandlers {
173
173
  onLongPress: TLPointerEvent
174
174
  onRightClick: TLPointerEvent
175
175
  onDoubleClick: TLClickEvent
176
- onTripleClick: TLClickEvent
177
- onQuadrupleClick: TLClickEvent
178
176
  onMiddleClick: TLPointerEvent
179
177
  onPointerUp: TLPointerEvent
180
178
  onKeyDown: TLKeyboardEvent
@@ -206,7 +204,5 @@ export const EVENT_NAME_MAP: Record<
206
204
  complete: 'onComplete',
207
205
  interrupt: 'onInterrupt',
208
206
  double_click: 'onDoubleClick',
209
- triple_click: 'onTripleClick',
210
- quadruple_click: 'onQuadrupleClick',
211
207
  tick: 'onTick',
212
208
  }
package/src/version.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  // This file is automatically generated by internal/scripts/refresh-assets.ts.
2
2
  // Do not edit manually. Or do, I'm a comment, not a cop.
3
3
 
4
- export const version = '5.2.0-next.b91d4a4551c9'
4
+ export const version = '5.2.0-next.cd4a35fc06d5'
5
5
  export const publishDates = {
6
6
  major: '2026-05-06T16:28:18.473Z',
7
- minor: '2026-06-03T10:38:24.758Z',
8
- patch: '2026-06-03T10:38:24.758Z',
7
+ minor: '2026-06-12T08:51:03.446Z',
8
+ patch: '2026-06-12T08:51:03.446Z',
9
9
  }
@@ -1,161 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
- var ShapeIndicatorOverlayUtil_exports = {};
20
- __export(ShapeIndicatorOverlayUtil_exports, {
21
- ShapeIndicatorOverlayUtil: () => ShapeIndicatorOverlayUtil,
22
- strokeShapeIndicators: () => strokeShapeIndicators
23
- });
24
- module.exports = __toCommonJS(ShapeIndicatorOverlayUtil_exports);
25
- var import_state = require("@tldraw/state");
26
- var import_store = require("@tldraw/store");
27
- var import_OverlayUtil = require("./OverlayUtil");
28
- const indicatorPathCache = (0, import_store.createComputedCache)(
29
- "shapeIndicatorPath",
30
- (editor, shape) => {
31
- const util = editor.getShapeUtil(shape);
32
- return util.getIndicatorPath(shape);
33
- },
34
- {
35
- areRecordsEqual(a, b) {
36
- return a.props === b.props;
37
- }
38
- }
39
- );
40
- function strokeShapeIndicators(editor, ctx, shapeIds) {
41
- if (shapeIds.length === 0) return;
42
- const batched = new Path2D();
43
- for (const shapeId of shapeIds) {
44
- const shape = editor.getShape(shapeId);
45
- if (!shape || shape.isLocked) continue;
46
- const pageTransform = editor.getShapePageTransform(shape);
47
- if (!pageTransform) continue;
48
- const indicatorPath = indicatorPathCache.get(editor, shape.id);
49
- if (!indicatorPath) continue;
50
- if (indicatorPath instanceof Path2D) {
51
- batched.addPath(indicatorPath, pageTransform);
52
- continue;
53
- }
54
- const { path, clipPath, additionalPaths } = indicatorPath;
55
- if (!clipPath) {
56
- batched.addPath(path, pageTransform);
57
- if (additionalPaths) {
58
- for (const p of additionalPaths) batched.addPath(p, pageTransform);
59
- }
60
- continue;
61
- }
62
- ctx.save();
63
- ctx.transform(
64
- pageTransform.a,
65
- pageTransform.b,
66
- pageTransform.c,
67
- pageTransform.d,
68
- pageTransform.e,
69
- pageTransform.f
70
- );
71
- ctx.save();
72
- ctx.clip(clipPath, "evenodd");
73
- ctx.stroke(path);
74
- ctx.restore();
75
- if (additionalPaths) {
76
- for (const p of additionalPaths) ctx.stroke(p);
77
- }
78
- ctx.restore();
79
- }
80
- ctx.stroke(batched);
81
- }
82
- class ShapeIndicatorOverlayUtil extends import_OverlayUtil.OverlayUtil {
83
- static type = "shape_indicator";
84
- options = { zIndex: 50, lineWidth: 1.5, hintedLineWidth: 2.5 };
85
- // Narrow projection of instance state. Reading the full record would
86
- // re-fire getOverlays on every cursor move / brush update; gating on these
87
- // three booleans means we only re-fire when one of them actually flips.
88
- _instanceFlags$ = (0, import_state.computed)(
89
- "shape indicator instance flags",
90
- () => {
91
- const i = this.editor.getInstanceState();
92
- return {
93
- isChangingStyle: i.isChangingStyle,
94
- isHoveringCanvas: i.isHoveringCanvas,
95
- isCoarsePointer: i.isCoarsePointer
96
- };
97
- },
98
- {
99
- isEqual: (a, b) => a.isChangingStyle === b.isChangingStyle && a.isHoveringCanvas === b.isHoveringCanvas && a.isCoarsePointer === b.isCoarsePointer
100
- }
101
- );
102
- isActive() {
103
- return true;
104
- }
105
- getOverlays() {
106
- const editor = this.editor;
107
- const renderingShapeIds = new Set(editor.getRenderingShapes().map((s) => s.id));
108
- const idsToDisplay = [];
109
- const { isChangingStyle, isHoveringCanvas, isCoarsePointer } = this._instanceFlags$.get();
110
- const isIdleOrEditing = editor.isInAny("select.idle", "select.editing_shape");
111
- const isInSelectState = editor.isInAny(
112
- "select.brushing",
113
- "select.scribble_brushing",
114
- "select.pointing_shape",
115
- "select.pointing_selection",
116
- "select.pointing_handle"
117
- );
118
- if (!isChangingStyle && (isIdleOrEditing || isInSelectState)) {
119
- for (const id of editor.getSelectedShapeIds()) {
120
- if (renderingShapeIds.has(id)) idsToDisplay.push(id);
121
- }
122
- if (isIdleOrEditing && isHoveringCanvas && !isCoarsePointer) {
123
- const hovered = editor.getHoveredShapeId();
124
- if (hovered && renderingShapeIds.has(hovered) && !idsToDisplay.includes(hovered)) {
125
- idsToDisplay.push(hovered);
126
- }
127
- }
128
- }
129
- const hintingShapeIds = [];
130
- for (const id of editor.getHintingShapeIds()) {
131
- if (renderingShapeIds.has(id)) hintingShapeIds.push(id);
132
- }
133
- if (idsToDisplay.length === 0 && hintingShapeIds.length === 0) {
134
- return [];
135
- }
136
- return [
137
- {
138
- id: "shape_indicator",
139
- type: "shape_indicator",
140
- props: { idsToDisplay, hintingShapeIds }
141
- }
142
- ];
143
- }
144
- render(ctx, overlays) {
145
- const overlay = overlays[0];
146
- if (!overlay) return;
147
- const editor = this.editor;
148
- const zoom = editor.getZoomLevel();
149
- const { idsToDisplay, hintingShapeIds } = overlay.props;
150
- ctx.lineCap = "round";
151
- ctx.lineJoin = "round";
152
- ctx.strokeStyle = editor.getCurrentTheme().colors[editor.getColorMode()].selectionStroke;
153
- ctx.lineWidth = this.options.lineWidth / zoom;
154
- strokeShapeIndicators(editor, ctx, idsToDisplay);
155
- if (hintingShapeIds.length > 0) {
156
- ctx.lineWidth = this.options.hintedLineWidth / zoom;
157
- strokeShapeIndicators(editor, ctx, hintingShapeIds);
158
- }
159
- }
160
- }
161
- //# sourceMappingURL=ShapeIndicatorOverlayUtil.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../src/lib/editor/overlays/ShapeIndicatorOverlayUtil.ts"],
4
- "sourcesContent": ["import { computed } from '@tldraw/state'\nimport { createComputedCache } from '@tldraw/store'\nimport { TLShape, TLShapeId } from '@tldraw/tlschema'\nimport type { Editor } from '../Editor'\nimport { OverlayUtil, TLOverlay } from './OverlayUtil'\n\ninterface RelevantInstanceFlags {\n\tisChangingStyle: boolean\n\tisHoveringCanvas: boolean | null\n\tisCoarsePointer: boolean\n}\n\n/** @public */\nexport interface TLShapeIndicatorOverlay extends TLOverlay {\n\tprops: {\n\t\tidsToDisplay: TLShapeId[]\n\t\thintingShapeIds: TLShapeId[]\n\t}\n}\n\nconst indicatorPathCache = createComputedCache(\n\t'shapeIndicatorPath',\n\t(editor: Editor, shape: TLShape) => {\n\t\tconst util = editor.getShapeUtil(shape)\n\t\treturn util.getIndicatorPath(shape)\n\t},\n\t{\n\t\tareRecordsEqual(a, b) {\n\t\t\treturn a.props === b.props\n\t\t},\n\t}\n)\n\n/**\n * Combine every batchable shape indicator into a single page-space `Path2D` and\n * emit one stroke call. Shapes whose indicator needs an evenodd clip (e.g.\n * arrows with labels or complex arrowheads) can't be batched \u2014 they still\n * stroke individually inside a save/restore with `ctx.clip` applied.\n *\n * Shared by {@link ShapeIndicatorOverlayUtil} and any overlay util that paints\n * shape indicators (e.g. collaborator selections).\n *\n * @public\n */\nexport function strokeShapeIndicators(\n\teditor: Editor,\n\tctx: CanvasRenderingContext2D,\n\tshapeIds: TLShapeId[]\n): void {\n\tif (shapeIds.length === 0) return\n\n\tconst batched = new Path2D()\n\n\tfor (const shapeId of shapeIds) {\n\t\tconst shape = editor.getShape(shapeId)\n\t\tif (!shape || shape.isLocked) continue\n\n\t\tconst pageTransform = editor.getShapePageTransform(shape)\n\t\tif (!pageTransform) continue\n\n\t\tconst indicatorPath = indicatorPathCache.get(editor, shape.id)\n\t\tif (!indicatorPath) continue\n\n\t\tif (indicatorPath instanceof Path2D) {\n\t\t\tbatched.addPath(indicatorPath, pageTransform)\n\t\t\tcontinue\n\t\t}\n\n\t\tconst { path, clipPath, additionalPaths } = indicatorPath\n\n\t\tif (!clipPath) {\n\t\t\tbatched.addPath(path, pageTransform)\n\t\t\tif (additionalPaths) {\n\t\t\t\tfor (const p of additionalPaths) batched.addPath(p, pageTransform)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// Clipped case: fall back to an individual stroke. Rare (arrows with\n\t\t// labels / complex arrowheads), so the extra save/restore/stroke\n\t\t// pair per such shape isn't worth batching away.\n\t\tctx.save()\n\t\tctx.transform(\n\t\t\tpageTransform.a,\n\t\t\tpageTransform.b,\n\t\t\tpageTransform.c,\n\t\t\tpageTransform.d,\n\t\t\tpageTransform.e,\n\t\t\tpageTransform.f\n\t\t)\n\t\tctx.save()\n\t\tctx.clip(clipPath, 'evenodd')\n\t\tctx.stroke(path)\n\t\tctx.restore()\n\t\tif (additionalPaths) {\n\t\t\tfor (const p of additionalPaths) ctx.stroke(p)\n\t\t}\n\t\tctx.restore()\n\t}\n\n\tctx.stroke(batched)\n}\n\n/**\n * Overlay util for shape indicators \u2014 the selection / hover / hint outlines drawn\n * under the selection foreground. Paints local indicators in the theme's\n * selection color.\n *\n * Remote collaborator selection indicators are drawn by a separate overlay util\n * (e.g. `CollaboratorShapeIndicatorOverlayUtil` from `tldraw`) that runs at a\n * lower z-index so peer selections appear under the local indicators.\n *\n * Non-interactive: contributes no hit-test geometry.\n *\n * @public\n */\nexport class ShapeIndicatorOverlayUtil extends OverlayUtil<TLShapeIndicatorOverlay> {\n\tstatic override type = 'shape_indicator'\n\toverride options = { zIndex: 50, lineWidth: 1.5, hintedLineWidth: 2.5 }\n\n\t// Narrow projection of instance state. Reading the full record would\n\t// re-fire getOverlays on every cursor move / brush update; gating on these\n\t// three booleans means we only re-fire when one of them actually flips.\n\tprivate _instanceFlags$ = computed<RelevantInstanceFlags>(\n\t\t'shape indicator instance flags',\n\t\t() => {\n\t\t\tconst i = this.editor.getInstanceState()\n\t\t\treturn {\n\t\t\t\tisChangingStyle: i.isChangingStyle,\n\t\t\t\tisHoveringCanvas: i.isHoveringCanvas,\n\t\t\t\tisCoarsePointer: i.isCoarsePointer,\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tisEqual: (a, b) =>\n\t\t\t\ta.isChangingStyle === b.isChangingStyle &&\n\t\t\t\ta.isHoveringCanvas === b.isHoveringCanvas &&\n\t\t\t\ta.isCoarsePointer === b.isCoarsePointer,\n\t\t}\n\t)\n\n\toverride isActive(): boolean {\n\t\treturn true\n\t}\n\n\toverride getOverlays(): TLShapeIndicatorOverlay[] {\n\t\tconst editor = this.editor\n\t\tconst renderingShapeIds = new Set(editor.getRenderingShapes().map((s) => s.id))\n\n\t\t// Local selected / hovered indicators.\n\t\tconst idsToDisplay: TLShapeId[] = []\n\t\tconst { isChangingStyle, isHoveringCanvas, isCoarsePointer } = this._instanceFlags$.get()\n\t\tconst isIdleOrEditing = editor.isInAny('select.idle', 'select.editing_shape')\n\t\tconst isInSelectState = editor.isInAny(\n\t\t\t'select.brushing',\n\t\t\t'select.scribble_brushing',\n\t\t\t'select.pointing_shape',\n\t\t\t'select.pointing_selection',\n\t\t\t'select.pointing_handle'\n\t\t)\n\n\t\tif (!isChangingStyle && (isIdleOrEditing || isInSelectState)) {\n\t\t\tfor (const id of editor.getSelectedShapeIds()) {\n\t\t\t\tif (renderingShapeIds.has(id)) idsToDisplay.push(id)\n\t\t\t}\n\t\t\tif (isIdleOrEditing && isHoveringCanvas && !isCoarsePointer) {\n\t\t\t\tconst hovered = editor.getHoveredShapeId()\n\t\t\t\tif (hovered && renderingShapeIds.has(hovered) && !idsToDisplay.includes(hovered)) {\n\t\t\t\t\tidsToDisplay.push(hovered)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Hinted shapes (drawn thicker). Already deduped at write time in\n\t\t// `updateHintingShapeIds`, so no need to dedupe again here.\n\t\tconst hintingShapeIds: TLShapeId[] = []\n\t\tfor (const id of editor.getHintingShapeIds()) {\n\t\t\tif (renderingShapeIds.has(id)) hintingShapeIds.push(id)\n\t\t}\n\n\t\tif (idsToDisplay.length === 0 && hintingShapeIds.length === 0) {\n\t\t\treturn []\n\t\t}\n\n\t\treturn [\n\t\t\t{\n\t\t\t\tid: 'shape_indicator',\n\t\t\t\ttype: 'shape_indicator',\n\t\t\t\tprops: { idsToDisplay, hintingShapeIds },\n\t\t\t},\n\t\t]\n\t}\n\n\toverride render(ctx: CanvasRenderingContext2D, overlays: TLShapeIndicatorOverlay[]): void {\n\t\tconst overlay = overlays[0]\n\t\tif (!overlay) return\n\n\t\tconst editor = this.editor\n\t\tconst zoom = editor.getZoomLevel()\n\t\tconst { idsToDisplay, hintingShapeIds } = overlay.props\n\n\t\tctx.lineCap = 'round'\n\t\tctx.lineJoin = 'round'\n\n\t\t// Local selected / hovered indicators \u2014 one stroke call for the whole batch.\n\t\tctx.strokeStyle = editor.getCurrentTheme().colors[editor.getColorMode()].selectionStroke\n\t\tctx.lineWidth = this.options.lineWidth / zoom\n\t\tstrokeShapeIndicators(editor, ctx, idsToDisplay)\n\n\t\t// Hinted shapes \u2014 thicker stroke, one call for the whole batch.\n\t\tif (hintingShapeIds.length > 0) {\n\t\t\tctx.lineWidth = this.options.hintedLineWidth / zoom\n\t\t\tstrokeShapeIndicators(editor, ctx, hintingShapeIds)\n\t\t}\n\t}\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,141 +0,0 @@
1
- import { computed } from "@tldraw/state";
2
- import { createComputedCache } from "@tldraw/store";
3
- import { OverlayUtil } from "./OverlayUtil.mjs";
4
- const indicatorPathCache = createComputedCache(
5
- "shapeIndicatorPath",
6
- (editor, shape) => {
7
- const util = editor.getShapeUtil(shape);
8
- return util.getIndicatorPath(shape);
9
- },
10
- {
11
- areRecordsEqual(a, b) {
12
- return a.props === b.props;
13
- }
14
- }
15
- );
16
- function strokeShapeIndicators(editor, ctx, shapeIds) {
17
- if (shapeIds.length === 0) return;
18
- const batched = new Path2D();
19
- for (const shapeId of shapeIds) {
20
- const shape = editor.getShape(shapeId);
21
- if (!shape || shape.isLocked) continue;
22
- const pageTransform = editor.getShapePageTransform(shape);
23
- if (!pageTransform) continue;
24
- const indicatorPath = indicatorPathCache.get(editor, shape.id);
25
- if (!indicatorPath) continue;
26
- if (indicatorPath instanceof Path2D) {
27
- batched.addPath(indicatorPath, pageTransform);
28
- continue;
29
- }
30
- const { path, clipPath, additionalPaths } = indicatorPath;
31
- if (!clipPath) {
32
- batched.addPath(path, pageTransform);
33
- if (additionalPaths) {
34
- for (const p of additionalPaths) batched.addPath(p, pageTransform);
35
- }
36
- continue;
37
- }
38
- ctx.save();
39
- ctx.transform(
40
- pageTransform.a,
41
- pageTransform.b,
42
- pageTransform.c,
43
- pageTransform.d,
44
- pageTransform.e,
45
- pageTransform.f
46
- );
47
- ctx.save();
48
- ctx.clip(clipPath, "evenodd");
49
- ctx.stroke(path);
50
- ctx.restore();
51
- if (additionalPaths) {
52
- for (const p of additionalPaths) ctx.stroke(p);
53
- }
54
- ctx.restore();
55
- }
56
- ctx.stroke(batched);
57
- }
58
- class ShapeIndicatorOverlayUtil extends OverlayUtil {
59
- static type = "shape_indicator";
60
- options = { zIndex: 50, lineWidth: 1.5, hintedLineWidth: 2.5 };
61
- // Narrow projection of instance state. Reading the full record would
62
- // re-fire getOverlays on every cursor move / brush update; gating on these
63
- // three booleans means we only re-fire when one of them actually flips.
64
- _instanceFlags$ = computed(
65
- "shape indicator instance flags",
66
- () => {
67
- const i = this.editor.getInstanceState();
68
- return {
69
- isChangingStyle: i.isChangingStyle,
70
- isHoveringCanvas: i.isHoveringCanvas,
71
- isCoarsePointer: i.isCoarsePointer
72
- };
73
- },
74
- {
75
- isEqual: (a, b) => a.isChangingStyle === b.isChangingStyle && a.isHoveringCanvas === b.isHoveringCanvas && a.isCoarsePointer === b.isCoarsePointer
76
- }
77
- );
78
- isActive() {
79
- return true;
80
- }
81
- getOverlays() {
82
- const editor = this.editor;
83
- const renderingShapeIds = new Set(editor.getRenderingShapes().map((s) => s.id));
84
- const idsToDisplay = [];
85
- const { isChangingStyle, isHoveringCanvas, isCoarsePointer } = this._instanceFlags$.get();
86
- const isIdleOrEditing = editor.isInAny("select.idle", "select.editing_shape");
87
- const isInSelectState = editor.isInAny(
88
- "select.brushing",
89
- "select.scribble_brushing",
90
- "select.pointing_shape",
91
- "select.pointing_selection",
92
- "select.pointing_handle"
93
- );
94
- if (!isChangingStyle && (isIdleOrEditing || isInSelectState)) {
95
- for (const id of editor.getSelectedShapeIds()) {
96
- if (renderingShapeIds.has(id)) idsToDisplay.push(id);
97
- }
98
- if (isIdleOrEditing && isHoveringCanvas && !isCoarsePointer) {
99
- const hovered = editor.getHoveredShapeId();
100
- if (hovered && renderingShapeIds.has(hovered) && !idsToDisplay.includes(hovered)) {
101
- idsToDisplay.push(hovered);
102
- }
103
- }
104
- }
105
- const hintingShapeIds = [];
106
- for (const id of editor.getHintingShapeIds()) {
107
- if (renderingShapeIds.has(id)) hintingShapeIds.push(id);
108
- }
109
- if (idsToDisplay.length === 0 && hintingShapeIds.length === 0) {
110
- return [];
111
- }
112
- return [
113
- {
114
- id: "shape_indicator",
115
- type: "shape_indicator",
116
- props: { idsToDisplay, hintingShapeIds }
117
- }
118
- ];
119
- }
120
- render(ctx, overlays) {
121
- const overlay = overlays[0];
122
- if (!overlay) return;
123
- const editor = this.editor;
124
- const zoom = editor.getZoomLevel();
125
- const { idsToDisplay, hintingShapeIds } = overlay.props;
126
- ctx.lineCap = "round";
127
- ctx.lineJoin = "round";
128
- ctx.strokeStyle = editor.getCurrentTheme().colors[editor.getColorMode()].selectionStroke;
129
- ctx.lineWidth = this.options.lineWidth / zoom;
130
- strokeShapeIndicators(editor, ctx, idsToDisplay);
131
- if (hintingShapeIds.length > 0) {
132
- ctx.lineWidth = this.options.hintedLineWidth / zoom;
133
- strokeShapeIndicators(editor, ctx, hintingShapeIds);
134
- }
135
- }
136
- }
137
- export {
138
- ShapeIndicatorOverlayUtil,
139
- strokeShapeIndicators
140
- };
141
- //# sourceMappingURL=ShapeIndicatorOverlayUtil.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../src/lib/editor/overlays/ShapeIndicatorOverlayUtil.ts"],
4
- "sourcesContent": ["import { computed } from '@tldraw/state'\nimport { createComputedCache } from '@tldraw/store'\nimport { TLShape, TLShapeId } from '@tldraw/tlschema'\nimport type { Editor } from '../Editor'\nimport { OverlayUtil, TLOverlay } from './OverlayUtil'\n\ninterface RelevantInstanceFlags {\n\tisChangingStyle: boolean\n\tisHoveringCanvas: boolean | null\n\tisCoarsePointer: boolean\n}\n\n/** @public */\nexport interface TLShapeIndicatorOverlay extends TLOverlay {\n\tprops: {\n\t\tidsToDisplay: TLShapeId[]\n\t\thintingShapeIds: TLShapeId[]\n\t}\n}\n\nconst indicatorPathCache = createComputedCache(\n\t'shapeIndicatorPath',\n\t(editor: Editor, shape: TLShape) => {\n\t\tconst util = editor.getShapeUtil(shape)\n\t\treturn util.getIndicatorPath(shape)\n\t},\n\t{\n\t\tareRecordsEqual(a, b) {\n\t\t\treturn a.props === b.props\n\t\t},\n\t}\n)\n\n/**\n * Combine every batchable shape indicator into a single page-space `Path2D` and\n * emit one stroke call. Shapes whose indicator needs an evenodd clip (e.g.\n * arrows with labels or complex arrowheads) can't be batched \u2014 they still\n * stroke individually inside a save/restore with `ctx.clip` applied.\n *\n * Shared by {@link ShapeIndicatorOverlayUtil} and any overlay util that paints\n * shape indicators (e.g. collaborator selections).\n *\n * @public\n */\nexport function strokeShapeIndicators(\n\teditor: Editor,\n\tctx: CanvasRenderingContext2D,\n\tshapeIds: TLShapeId[]\n): void {\n\tif (shapeIds.length === 0) return\n\n\tconst batched = new Path2D()\n\n\tfor (const shapeId of shapeIds) {\n\t\tconst shape = editor.getShape(shapeId)\n\t\tif (!shape || shape.isLocked) continue\n\n\t\tconst pageTransform = editor.getShapePageTransform(shape)\n\t\tif (!pageTransform) continue\n\n\t\tconst indicatorPath = indicatorPathCache.get(editor, shape.id)\n\t\tif (!indicatorPath) continue\n\n\t\tif (indicatorPath instanceof Path2D) {\n\t\t\tbatched.addPath(indicatorPath, pageTransform)\n\t\t\tcontinue\n\t\t}\n\n\t\tconst { path, clipPath, additionalPaths } = indicatorPath\n\n\t\tif (!clipPath) {\n\t\t\tbatched.addPath(path, pageTransform)\n\t\t\tif (additionalPaths) {\n\t\t\t\tfor (const p of additionalPaths) batched.addPath(p, pageTransform)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// Clipped case: fall back to an individual stroke. Rare (arrows with\n\t\t// labels / complex arrowheads), so the extra save/restore/stroke\n\t\t// pair per such shape isn't worth batching away.\n\t\tctx.save()\n\t\tctx.transform(\n\t\t\tpageTransform.a,\n\t\t\tpageTransform.b,\n\t\t\tpageTransform.c,\n\t\t\tpageTransform.d,\n\t\t\tpageTransform.e,\n\t\t\tpageTransform.f\n\t\t)\n\t\tctx.save()\n\t\tctx.clip(clipPath, 'evenodd')\n\t\tctx.stroke(path)\n\t\tctx.restore()\n\t\tif (additionalPaths) {\n\t\t\tfor (const p of additionalPaths) ctx.stroke(p)\n\t\t}\n\t\tctx.restore()\n\t}\n\n\tctx.stroke(batched)\n}\n\n/**\n * Overlay util for shape indicators \u2014 the selection / hover / hint outlines drawn\n * under the selection foreground. Paints local indicators in the theme's\n * selection color.\n *\n * Remote collaborator selection indicators are drawn by a separate overlay util\n * (e.g. `CollaboratorShapeIndicatorOverlayUtil` from `tldraw`) that runs at a\n * lower z-index so peer selections appear under the local indicators.\n *\n * Non-interactive: contributes no hit-test geometry.\n *\n * @public\n */\nexport class ShapeIndicatorOverlayUtil extends OverlayUtil<TLShapeIndicatorOverlay> {\n\tstatic override type = 'shape_indicator'\n\toverride options = { zIndex: 50, lineWidth: 1.5, hintedLineWidth: 2.5 }\n\n\t// Narrow projection of instance state. Reading the full record would\n\t// re-fire getOverlays on every cursor move / brush update; gating on these\n\t// three booleans means we only re-fire when one of them actually flips.\n\tprivate _instanceFlags$ = computed<RelevantInstanceFlags>(\n\t\t'shape indicator instance flags',\n\t\t() => {\n\t\t\tconst i = this.editor.getInstanceState()\n\t\t\treturn {\n\t\t\t\tisChangingStyle: i.isChangingStyle,\n\t\t\t\tisHoveringCanvas: i.isHoveringCanvas,\n\t\t\t\tisCoarsePointer: i.isCoarsePointer,\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tisEqual: (a, b) =>\n\t\t\t\ta.isChangingStyle === b.isChangingStyle &&\n\t\t\t\ta.isHoveringCanvas === b.isHoveringCanvas &&\n\t\t\t\ta.isCoarsePointer === b.isCoarsePointer,\n\t\t}\n\t)\n\n\toverride isActive(): boolean {\n\t\treturn true\n\t}\n\n\toverride getOverlays(): TLShapeIndicatorOverlay[] {\n\t\tconst editor = this.editor\n\t\tconst renderingShapeIds = new Set(editor.getRenderingShapes().map((s) => s.id))\n\n\t\t// Local selected / hovered indicators.\n\t\tconst idsToDisplay: TLShapeId[] = []\n\t\tconst { isChangingStyle, isHoveringCanvas, isCoarsePointer } = this._instanceFlags$.get()\n\t\tconst isIdleOrEditing = editor.isInAny('select.idle', 'select.editing_shape')\n\t\tconst isInSelectState = editor.isInAny(\n\t\t\t'select.brushing',\n\t\t\t'select.scribble_brushing',\n\t\t\t'select.pointing_shape',\n\t\t\t'select.pointing_selection',\n\t\t\t'select.pointing_handle'\n\t\t)\n\n\t\tif (!isChangingStyle && (isIdleOrEditing || isInSelectState)) {\n\t\t\tfor (const id of editor.getSelectedShapeIds()) {\n\t\t\t\tif (renderingShapeIds.has(id)) idsToDisplay.push(id)\n\t\t\t}\n\t\t\tif (isIdleOrEditing && isHoveringCanvas && !isCoarsePointer) {\n\t\t\t\tconst hovered = editor.getHoveredShapeId()\n\t\t\t\tif (hovered && renderingShapeIds.has(hovered) && !idsToDisplay.includes(hovered)) {\n\t\t\t\t\tidsToDisplay.push(hovered)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Hinted shapes (drawn thicker). Already deduped at write time in\n\t\t// `updateHintingShapeIds`, so no need to dedupe again here.\n\t\tconst hintingShapeIds: TLShapeId[] = []\n\t\tfor (const id of editor.getHintingShapeIds()) {\n\t\t\tif (renderingShapeIds.has(id)) hintingShapeIds.push(id)\n\t\t}\n\n\t\tif (idsToDisplay.length === 0 && hintingShapeIds.length === 0) {\n\t\t\treturn []\n\t\t}\n\n\t\treturn [\n\t\t\t{\n\t\t\t\tid: 'shape_indicator',\n\t\t\t\ttype: 'shape_indicator',\n\t\t\t\tprops: { idsToDisplay, hintingShapeIds },\n\t\t\t},\n\t\t]\n\t}\n\n\toverride render(ctx: CanvasRenderingContext2D, overlays: TLShapeIndicatorOverlay[]): void {\n\t\tconst overlay = overlays[0]\n\t\tif (!overlay) return\n\n\t\tconst editor = this.editor\n\t\tconst zoom = editor.getZoomLevel()\n\t\tconst { idsToDisplay, hintingShapeIds } = overlay.props\n\n\t\tctx.lineCap = 'round'\n\t\tctx.lineJoin = 'round'\n\n\t\t// Local selected / hovered indicators \u2014 one stroke call for the whole batch.\n\t\tctx.strokeStyle = editor.getCurrentTheme().colors[editor.getColorMode()].selectionStroke\n\t\tctx.lineWidth = this.options.lineWidth / zoom\n\t\tstrokeShapeIndicators(editor, ctx, idsToDisplay)\n\n\t\t// Hinted shapes \u2014 thicker stroke, one call for the whole batch.\n\t\tif (hintingShapeIds.length > 0) {\n\t\t\tctx.lineWidth = this.options.hintedLineWidth / zoom\n\t\t\tstrokeShapeIndicators(editor, ctx, hintingShapeIds)\n\t\t}\n\t}\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }