/** Reader-local comment chrome overlays rather than mutates collaborative prose. */ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; import { ChatCircleIcon } from "@phosphor-icons/react"; import { useCellValue } from "@mdxeditor/gurx"; import { DraftCard, ThreadCard } from "./comments"; import { markerPoints, popoverPoint } from "./comment-geometry"; import { containsHit, passageHits } from "./comment-hits"; import { useCommentSheetReveal } from "./comment-sheet-reveal"; import { $rangeOf } from "./marks"; import { blockElement } from "./scroll"; import { COARSE_POINTER_QUERY, hasCoarsePointer } from "./pointer"; import { useThreads } from "./threads"; import { widgets$ } from "./widget-options"; import type { CSSProperties } from "react"; import type { Point, Rect } from "./comment-geometry"; import type { PassageHit } from "./comment-hits"; import type { ThreadStore, ThreadView } from "./threads"; type PlacedThread = { view: ThreadView; button: Point; hits: PassageHit[]; passages: Rect[]; }; type MeasuredThread = { view: ThreadView; target: Rect; passages: Rect[]; hits: Rect[] }; type PassagePress = { id: string; left: number; pointer: number; top: number; moved: boolean }; function rect(value: DOMRect): Rect { return { top: value.top, right: value.right, bottom: value.bottom, left: value.left, width: value.width, height: value.height, }; } function Preview({ id, onMeasure, style, view, }: { id: string; onMeasure: (element: HTMLDivElement | null) => void; style: CSSProperties; view: ThreadView; }) { let replies = Math.max(0, view.thread.notes.length - 1); return (
{view.quote}
{replies > 0 && {replies} {replies === 1 ? "reply" : "replies"}}