From a36b73505754acab6872d23c9add58dfca3cecdd Mon Sep 17 00:00:00 2001 From: haoyuren <13851610112@163.com> Date: Sun, 15 Mar 2026 18:47:29 -0500 Subject: Add online users popover, MCP get_online_users, claude-workspace, stronger CLAUDE.md - Online users: click "N online" badge to see user names with cursor colors - MCP get_online_users tool: main process tracks users in .lattex-online-users.json - CLAUDE.md: show current user's Overleaf name (fetched from /user/settings) - CLAUDE.md: mandatory first steps with MUST/NEVER language at top of file - claude-workspace/ directory: untracked scratch space for Claude Code experiments - chokidar + bridge skip claude-workspace/ to prevent syncing to Overleaf Co-Authored-By: Claude Opus 4.6 --- src/renderer/src/App.css | 64 +++++++++++++++++++++++++++++++++ src/renderer/src/components/Toolbar.tsx | 40 +++++++++++++++++---- 2 files changed, 97 insertions(+), 7 deletions(-) (limited to 'src/renderer') diff --git a/src/renderer/src/App.css b/src/renderer/src/App.css index 7f7b24c..906bf36 100644 --- a/src/renderer/src/App.css +++ b/src/renderer/src/App.css @@ -2229,6 +2229,10 @@ html, body, #root { /* ── Toolbar Users Count ─────────────────────────────────────── */ +.toolbar-users-wrap { + position: relative; +} + .toolbar-users { font-size: 11px; color: var(--text-muted); @@ -2236,6 +2240,66 @@ html, body, #root { padding: 2px 8px; border-radius: 10px; font-weight: 500; + border: none; + cursor: pointer; + font-family: var(--font-sans); +} + +.toolbar-users:hover { + background: var(--bg-active); +} + +.users-popover { + position: absolute; + top: calc(100% + 6px); + right: 0; + background: var(--bg-secondary); + border: 1px solid var(--border); + border-radius: 6px; + padding: 6px 0; + min-width: 180px; + z-index: 100; + box-shadow: 0 4px 12px rgba(0,0,0,0.15); +} + +.users-popover-title { + font-size: 10px; + font-weight: 600; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.5px; + padding: 4px 12px 6px; + border-bottom: 1px solid var(--border); + margin-bottom: 2px; +} + +.users-popover-item { + display: flex; + align-items: center; + gap: 8px; + padding: 5px 12px; + font-size: 12px; + color: var(--text-primary); +} + +.users-popover-dot { + width: 8px; + height: 8px; + border-radius: 50%; + flex-shrink: 0; +} + +.users-popover-name { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.users-popover-empty { + padding: 5px 12px; + font-size: 11px; + color: var(--text-muted); + font-style: italic; } /* ── Chat Panel ──────────────────────────────────────────────── */ diff --git a/src/renderer/src/components/Toolbar.tsx b/src/renderer/src/components/Toolbar.tsx index 0cee7e1..b89385a 100644 --- a/src/renderer/src/components/Toolbar.tsx +++ b/src/renderer/src/components/Toolbar.tsx @@ -3,6 +3,7 @@ import { useState, useRef, useEffect } from 'react' import { useAppStore } from '../stores/appStore' +import { remoteCursors } from '../App' interface ToolbarProps { onCompile: () => void @@ -19,19 +20,24 @@ export default function Toolbar({ onCompile, onLocalCompile, onBack }: ToolbarPr } = useAppStore() const [showCompileMenu, setShowCompileMenu] = useState(false) + const [showUsersPopover, setShowUsersPopover] = useState(false) const menuRef = useRef(null) + const usersRef = useRef(null) - // Close menu on outside click + // Close menus on outside click useEffect(() => { - if (!showCompileMenu) return + if (!showCompileMenu && !showUsersPopover) return const handler = (e: MouseEvent) => { - if (menuRef.current && !menuRef.current.contains(e.target as Node)) { + if (showCompileMenu && menuRef.current && !menuRef.current.contains(e.target as Node)) { setShowCompileMenu(false) } + if (showUsersPopover && usersRef.current && !usersRef.current.contains(e.target as Node)) { + setShowUsersPopover(false) + } } document.addEventListener('mousedown', handler) return () => document.removeEventListener('mousedown', handler) - }, [showCompileMenu]) + }, [showCompileMenu, showUsersPopover]) const projectName = overleafProject?.name || 'Project' @@ -91,9 +97,29 @@ export default function Toolbar({ onCompile, onLocalCompile, onBack }: ToolbarPr
{onlineUsersCount > 0 && ( - 1 ? 's' : ''} online`}> - {onlineUsersCount} - +
+ + {showUsersPopover && ( +
+
Online Users
+ {Array.from(remoteCursors.values()).map((u) => ( +
+ + {u.name} +
+ ))} + {remoteCursors.size === 0 && ( +
No cursor data yet
+ )} +
+ )} +
)}