{
  "version": 3,
  "sources": ["../../src/canvas/thread-state.ts"],
  "sourcesContent": ["import {\n\tBoxModel,\n\tEditor,\n\tMat,\n\tTLCommentAnchor,\n\tTLCommentThread,\n\tTLShape,\n\tTLShapeId,\n\tVec,\n\tVecLike,\n} from 'tldraw'\nimport { getCommentingOptions } from './options'\nimport { commentsSidebarOpen, openThreadId } from './state'\nimport { POPOVER_OFFSET } from './thread-view'\n\n/** How far an imprecise shape pin steps inside the shape from its anchor spot, in screen px \u2014\n *  most of the marker sits within the shape, with a small overhang past the corner. */\nexport const IMPRECISE_PIN_INSET_PX = 20\n\n/** Screen-pixel margin by which the viewport is inflated when culling canvas markers (thread pins\n *  and cluster badges), so a marker just off-screen is already mounted when a pan brings it in. */\nconst MARKER_CULL_MARGIN_PX = 120\n\n/**\n * Whether a viewport-space point sits within the viewport inflated by\n * {@link MARKER_CULL_MARGIN_PX}. The cull test for screen-fixed markers: off-screen markers\n * return null from their position signal and unmount instead of tracking every camera frame.\n * @internal\n */\nexport function isInInflatedViewport(editor: Editor, point: VecLike): boolean {\n\tconst viewport = editor.getViewportScreenBounds()\n\tconst margin = MARKER_CULL_MARGIN_PX\n\treturn (\n\t\tpoint.x >= -margin &&\n\t\tpoint.y >= -margin &&\n\t\tpoint.x <= viewport.w + margin &&\n\t\tpoint.y <= viewport.h + margin\n\t)\n}\n\n/**\n * Whether any part of a page-space box overlaps the viewport inflated by\n * {@link MARKER_CULL_MARGIN_PX}. The cull test for region-anchored threads, whose dashed box can\n * be on screen while the pin corner itself is not.\n * @internal\n */\nexport function isBoxInInflatedViewport(editor: Editor, box: BoxModel): boolean {\n\tconst viewport = editor.getViewportScreenBounds()\n\tconst margin = MARKER_CULL_MARGIN_PX\n\t// Zoom is positive, so the page box's corners keep their order through the transform.\n\tconst min = editor.pageToViewport({ x: box.x, y: box.y })\n\tconst max = editor.pageToViewport({ x: box.x + box.w, y: box.y + box.h })\n\treturn (\n\t\tmax.x >= -margin &&\n\t\tmax.y >= -margin &&\n\t\tmin.x <= viewport.w + margin &&\n\t\tmin.y <= viewport.h + margin\n\t)\n}\n\n/** Imprecise shape pins tuck inside the shape rather than hanging off its edge: the marker\n *  extends up-right of its anchor point, so step it toward the shape's centre. Screen px \u2014 the\n *  pin is screen-fixed while the shape scales with zoom. Null for anchors that need no inset. */\nexport function impreciseShapePinInset(\n\teditor: Editor,\n\tanchor: TLCommentThread['anchor']\n): { x: number; y: number } | null {\n\tif (anchor.type !== 'shape' || anchor.isPrecise) return null\n\tconst spot = getCommentingOptions(editor).impreciseShapeAnchor\n\tconst inset = {\n\t\tx: Math.sign(0.5 - spot.x) * IMPRECISE_PIN_INSET_PX,\n\t\ty: Math.sign(0.5 - spot.y) * IMPRECISE_PIN_INSET_PX,\n\t}\n\t// The spot is a corner of the shape's own bounds, so which way \"toward the centre\" points turns\n\t// with the shape: without this a rotated shape's pin would step out of the shape, not into it.\n\tconst rotation = editor.getShapePageTransform(anchor.shapeId as TLShapeId)?.rotation() ?? 0\n\treturn rotation === 0 ? inset : Vec.Rot(inset, rotation)\n}\n\n/** The corner a region's pin and composer sit on, as a normalized 0\u20131 offset (bottom-right). Pin\n *  position, composer placement, region move, and which corner has no resize handle all derive\n *  from it. */\nexport const REGION_PIN_CORNER: VecLike = { x: 1, y: 1 }\n\n/** A region anchor's pin corner: the corner its creating drag released on, when recorded, else\n *  {@link REGION_PIN_CORNER}. @internal */\nexport function regionAnchorPinCorner(\n\tanchor: Extract<TLCommentAnchor, { type: 'region' }>\n): VecLike {\n\tif (anchor.pinX !== undefined && anchor.pinY !== undefined) {\n\t\treturn { x: anchor.pinX, y: anchor.pinY }\n\t}\n\treturn REGION_PIN_CORNER\n}\n\n/** The page point of a region's pin corner. */\nexport function regionPinPoint(region: BoxModel, corner: VecLike = REGION_PIN_CORNER): VecLike {\n\treturn {\n\t\tx: region.x + corner.x * region.w,\n\t\ty: region.y + corner.y * region.h,\n\t}\n}\n\n/**\n * Where a thread's pin sits on the page, for each anchor kind. Null hides the pin. Imprecise shape\n * anchors use {@link CommentingOptions.impreciseShapeAnchor} rather than the stored `x`/`y`.\n *\n * A shape anchor's `x`/`y` are normalized within the shape's bounds and resolved through its page\n * transform, so the pin rides rotation instead of being left behind in the bounding box.\n * @public\n */\nexport function anchorPagePoint(\n\teditor: Editor,\n\tanchor: TLCommentAnchor\n): { x: number; y: number } | null {\n\tswitch (anchor.type) {\n\t\tcase 'shape': {\n\t\t\tconst shape = editor.getShape(anchor.shapeId as TLShapeId)\n\t\t\tif (!shape) return null\n\t\t\tconst transform = editor.getShapePageTransform(shape)\n\t\t\tif (!transform) return null\n\t\t\tconst { point, size } = editor.getShapeGeometry(shape).bounds\n\t\t\t// Precise pins sit at their stored x/y; imprecise ones at the editor's configured spot.\n\t\t\tconst spot = anchor.isPrecise ? anchor : getCommentingOptions(editor).impreciseShapeAnchor\n\t\t\treturn Mat.applyToPoint(transform, Vec.Add(point, Vec.MulV(spot, size)))\n\t\t}\n\t\tcase 'point':\n\t\t\treturn { x: anchor.x, y: anchor.y }\n\t\tcase 'region':\n\t\t\treturn regionPinPoint(anchor, regionAnchorPinCorner(anchor))\n\t\tcase 'page':\n\t\t\treturn null\n\t}\n}\n\n/**\n * The shape a comment placed at a page point should anchor to, or undefined for empty canvas.\n *\n * Uses the editor's hit-test margin, the same slack select and hover use \u2014 without it, open-path\n * shapes (arrows, lines, draw strokes) are unhittable in practice.\n *\n * `hitFrameInside` lets a click inside a frame's body anchor to the frame, which it otherwise\n * wouldn't (the select-tool convention hits only the edge/label). A child shape under the pointer\n * still wins, so only a frame's empty interior anchors the frame.\n *\n * @internal\n */\nexport function commentTargetShapeAt(editor: Editor, page: VecLike): TLShape | undefined {\n\treturn editor.getShapeAtPoint(page, {\n\t\thitInside: true,\n\t\thitFrameInside: true,\n\t\tmargin: editor.getHitTestMargin(),\n\t})\n}\n\n/**\n * A shape anchor for a page point. `x`/`y` are the point's normalized (0\u20131) offset within the\n * shape's own bounds, taken in the shape's own space, so a pin on a rotated shape records the spot\n * it was dropped on. Remembered either way: when `precise` the pin sits at exactly `x`/`y`,\n * otherwise at the consumer's imprecise default.\n * @public\n */\nexport function shapeAnchorAt(\n\teditor: Editor,\n\tshapeId: TLShapeId,\n\tpage: { x: number; y: number },\n\tprecise: boolean\n): TLCommentAnchor {\n\tconst shape = editor.getShape(shapeId)\n\tconst bounds = shape && editor.getShapeGeometry(shape).bounds\n\tif (!shape || !bounds || bounds.w === 0 || bounds.h === 0) {\n\t\treturn { type: 'shape', shapeId, x: 0.5, y: 0.5, isPrecise: precise }\n\t}\n\tconst local = editor.getPointInShapeSpace(shape, page)\n\treturn {\n\t\ttype: 'shape',\n\t\tshapeId,\n\t\tx: (local.x - bounds.minX) / bounds.w,\n\t\ty: (local.y - bounds.minY) / bounds.h,\n\t\tisPrecise: precise,\n\t}\n}\n\n/** Open a thread and bring it into view \u2014 switch to its page if needed, then center its pin. @public */\nexport function focusThread(editor: Editor, thread: TLCommentThread): void {\n\tif (thread.pageId !== editor.getCurrentPageId()) {\n\t\teditor.setCurrentPage(thread.pageId as any)\n\t}\n\topenThreadId.set(editor, thread.id)\n\tconst point = anchorPagePoint(editor, thread.anchor)\n\tif (!point) return\n\tconst offset = commentCenterScreenOffset(editor) / editor.getZoomLevel()\n\teditor.centerOnPoint({ x: point.x + offset, y: point.y }, { animation: { duration: 200 } })\n}\n\n/** The left inset a nudged pin never crosses, and the gap kept between thread UI and sidebar. */\nconst CENTER_MARGIN_PX = 8\n\n/** How far the open thread UI reaches right of its pin, in screen px: the popover's offset plus\n *  the thread panel's fixed width (300px, `.tlui-cmt-thread`). */\nconst THREAD_UI_EXTENT_PX = POPOVER_OFFSET.thread.x + 300\n\n/**\n * How far right of a centered-on pin the true viewport center should sit, in screen px. While the\n * comments sidebar covers the viewport's right edge, dead-centering a pin would put its thread\n * popover under the sidebar \u2014 so centering aims at the middle of the uncovered area instead, never\n * past the viewport's left edge. Zero when the sidebar is closed or not on screen.\n * @internal\n */\nexport function commentCenterScreenOffset(editor: Editor): number {\n\tif (!commentsSidebarOpen.get(editor)) return 0\n\tconst sidebar = editor.getContainer().querySelector('.tlui-cmt-canvas-sidebar')\n\tif (!sidebar) return 0\n\tconst viewport = editor.getViewportScreenBounds()\n\t// Viewport screen bounds are the container's client rect, so this is container-local.\n\tconst sidebarLeft = sidebar.getBoundingClientRect().left - viewport.x\n\tif (sidebarLeft >= viewport.w) return 0\n\t// Aim the pin at the middle of the uncovered area, nudged left until the thread UI clears the\n\t// sidebar \u2014 but never past the left edge (the edge wins when there's no room for both).\n\tconst pinX = Math.max(\n\t\tMath.min(sidebarLeft / 2, sidebarLeft - CENTER_MARGIN_PX - THREAD_UI_EXTENT_PX),\n\t\tCENTER_MARGIN_PX\n\t)\n\treturn viewport.w / 2 - pinX\n}\n"],
  "mappings": "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",
  "names": []
}
