summaryrefslogtreecommitdiff
path: root/src/renderer
diff options
context:
space:
mode:
authorhaoyuren <13851610112@163.com>2026-03-15 18:47:29 -0500
committerhaoyuren <13851610112@163.com>2026-03-15 18:47:29 -0500
commita36b73505754acab6872d23c9add58dfca3cecdd (patch)
tree2c750e7710fa828a277ee7db15e4e5e081747758 /src/renderer
parent4969ea10ffd14e52c83f32e708563bacf910e238 (diff)
Add online users popover, MCP get_online_users, claude-workspace, stronger CLAUDE.mdv0.3.4
- 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 <noreply@anthropic.com>
Diffstat (limited to 'src/renderer')
-rw-r--r--src/renderer/src/App.css64
-rw-r--r--src/renderer/src/components/Toolbar.tsx40
2 files changed, 97 insertions, 7 deletions
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<HTMLDivElement>(null)
+ const usersRef = useRef<HTMLDivElement>(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
</div>
<div className="toolbar-right">
{onlineUsersCount > 0 && (
- <span className="toolbar-users" title={`${onlineUsersCount} user${onlineUsersCount > 1 ? 's' : ''} online`}>
- {onlineUsersCount}
- </span>
+ <div className="toolbar-users-wrap" ref={usersRef}>
+ <button
+ className="toolbar-users"
+ onClick={() => setShowUsersPopover(!showUsersPopover)}
+ title={`${onlineUsersCount} user${onlineUsersCount > 1 ? 's' : ''} online`}
+ >
+ {onlineUsersCount} online
+ </button>
+ {showUsersPopover && (
+ <div className="users-popover">
+ <div className="users-popover-title">Online Users</div>
+ {Array.from(remoteCursors.values()).map((u) => (
+ <div key={u.userId} className="users-popover-item">
+ <span className="users-popover-dot" style={{ background: u.color }} />
+ <span className="users-popover-name">{u.name}</span>
+ </div>
+ ))}
+ {remoteCursors.size === 0 && (
+ <div className="users-popover-empty">No cursor data yet</div>
+ )}
+ </div>
+ )}
+ </div>
)}
<button className={`toolbar-btn ${showChat ? 'active' : ''}`} onClick={toggleChat} title="Toggle chat">
Chat