{"version":3,"sources":["../src/collab/react.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * `@ai-matrx/realtime/collab/react` — what a collab surface draws.\n *  - RemoteCursorsLayer: one caret box per remote peer, positioned by the host editor's `mapPosition`.\n *  - RemotePeerChips: the \"who is here\" strip (moved from matrx-frontend `lib/collab/RemoteCursorsLayer`).\n * Inline styles only: the package ships no stylesheet.\n */\nimport { useEffect, useMemo, useState, type CSSProperties, type ReactElement } from \"react\";\n\nimport type { CollabSession } from \"./session\";\nimport type { AwarenessState } from \"./types\";\n\ntype PeerState = { uid: string; name: string; color: string; cursor?: unknown };\n\nfunction useAwarenessStates(awareness: CollabSession[\"awareness\"]): Map<number, Record<string, unknown>> {\n  const [states, setStates] = useState(() => new Map(awareness.getStates() as Map<number, Record<string, unknown>>));\n  useEffect(() => {\n    const sync = (): void => setStates(new Map(awareness.getStates() as Map<number, Record<string, unknown>>));\n    awareness.on(\"change\", sync);\n    sync();\n    return () => awareness.off(\"change\", sync);\n  }, [awareness]);\n  return states;\n}\n\nexport function RemoteCursorsLayer(props: {\n  session: CollabSession;\n  /** Editor position payload (`awareness.cursor`) to a rect in this layer's coordinate space; null hides it. */\n  mapPosition: (p: unknown) => DOMRect | null;\n}): ReactElement {\n  const states = useAwarenessStates(props.session.awareness);\n  const self = props.session.awareness.clientID;\n  const peers: Array<{ key: number; rect: DOMRect; s: PeerState }> = [];\n  states.forEach((raw, key) => {\n    if (key === self || typeof raw.uid !== \"string\" || raw.cursor == null) return;\n    const rect = props.mapPosition(raw.cursor);\n    if (rect) peers.push({ key, rect, s: raw as PeerState });\n  });\n  return (\n    <div style={{ position: \"absolute\", inset: 0, pointerEvents: \"none\" }} aria-hidden=\"true\">\n      {peers.map(({ key, rect, s }) => (\n        <div\n          key={key}\n          style={{\n            position: \"absolute\",\n            left: rect.left,\n            top: rect.top,\n            width: Math.max(rect.width, 2),\n            height: rect.height,\n            border: `2px solid ${s.color}`,\n            borderRadius: 2,\n          }}\n        >\n          <span\n            style={{\n              position: \"absolute\",\n              bottom: \"100%\",\n              left: -2,\n              padding: \"0 4px\",\n              fontSize: 10,\n              lineHeight: \"14px\",\n              color: \"#fff\",\n              background: s.color,\n              whiteSpace: \"nowrap\",\n              borderRadius: \"2px 2px 2px 0\",\n            }}\n          >\n            {s.name}\n          </span>\n        </div>\n      ))}\n    </div>\n  );\n}\n\nconst STALE_AFTER_MS = 30_000;\n\nfunction columnLetter(col: number): string {\n  let out = \"\";\n  let n = col;\n  while (n >= 0) {\n    out = String.fromCharCode(65 + (n % 26)) + out;\n    n = Math.floor(n / 26) - 1;\n  }\n  return out || \"A\";\n}\n\nfunction chipTitle(s: AwarenessState): string {\n  return s.row !== null && s.col !== null ? `${s.name} is at ${columnLetter(s.col)}${s.row + 1}` : s.name;\n}\n\nconst stripStyle: CSSProperties = { display: \"flex\", alignItems: \"center\", gap: 8, fontSize: 12 };\n\nexport function RemotePeerChips({ states, selfUid }: { states: Map<number, AwarenessState>; selfUid: string }): ReactElement | null {\n  const peers = useMemo(() => {\n    const now = Date.now();\n    const out: AwarenessState[] = [];\n    states.forEach((s) => {\n      if (s.uid === selfUid) return;\n      if (now - s.ts > STALE_AFTER_MS) return;\n      out.push(s);\n    });\n    out.sort((a, b) => a.name.localeCompare(b.name));\n    return out;\n  }, [states, selfUid]);\n  if (peers.length === 0) return null;\n  return (\n    <div style={stripStyle}>\n      <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" style={{ opacity: 0.6 }} aria-hidden=\"true\">\n        <path d=\"M18 21a8 8 0 0 0-16 0\" />\n        <circle cx=\"10\" cy=\"8\" r=\"5\" />\n        <path d=\"M22 20c0-3.37-2-6.5-4-8a5 5 0 0 0-.45-8.3\" />\n      </svg>\n      <div style={{ display: \"flex\", flexWrap: \"wrap\", gap: 4 }}>\n        {peers.map((p) => (\n          <span\n            key={p.uid}\n            title={chipTitle(p)}\n            style={{\n              display: \"inline-flex\",\n              alignItems: \"center\",\n              gap: 4,\n              borderRadius: 9999,\n              border: `1px solid ${p.color}`,\n              backgroundColor: `${p.color}1a`,\n              color: p.color,\n              padding: \"2px 8px\",\n            }}\n          >\n            <span style={{ width: 6, height: 6, borderRadius: 9999, backgroundColor: p.color }} />\n            <span style={{ fontWeight: 500 }}>{p.name}</span>\n            {p.row !== null && p.col !== null && (\n              <span style={{ opacity: 0.7 }}>\n                {columnLetter(p.col)}\n                {p.row + 1}\n              </span>\n            )}\n          </span>\n        ))}\n      </div>\n    </div>\n  );\n}\n"],"mappings":"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","names":[]}