summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorhaoyuren <13851610112@163.com>2026-07-28 06:29:04 +0800
committerhaoyuren <13851610112@163.com>2026-07-28 06:29:04 +0800
commitd35a6a0ef366f86aeba28c06f40e44cd29cf1f07 (patch)
tree1f9e08527da6720722dde971636273d859af89d6 /src/main
parent1ab00142191f4e3b94ba36d2a33914697864c892 (diff)
Codex/agent-neutral setup, terminal IME fix, hide own overlay cursor
- Write AGENTS.md (the cross-tool agent-instructions standard) at the project root alongside .claude/CLAUDE.md, generated from one guide; includes an Agent Setup section with the project-specific Codex MCP registration command. AGENTS.md is excluded from Overleaf sync. - MCP server resolves the project directory from its own script location when cwd doesn't contain the state file, so clients that launch MCP servers from a global config (Codex) work without special setup - Terminal: swallow the duplicate compositionend commit when switching input sources mid-composition (xterm finalizes on the keydown and then again on compositionend, sending pinyin text twice) - Don't render our own colored overlay cursor — the native caret already marks our position; overlay cursors are collaborators-only (web parity) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diffstat (limited to 'src/main')
-rw-r--r--src/main/fileSyncBridge.ts8
-rw-r--r--src/main/index.ts41
2 files changed, 38 insertions, 11 deletions
diff --git a/src/main/fileSyncBridge.ts b/src/main/fileSyncBridge.ts
index b7e1979..3072ae6 100644
--- a/src/main/fileSyncBridge.ts
+++ b/src/main/fileSyncBridge.ts
@@ -207,7 +207,7 @@ export class FileSyncBridge {
ignored: [
/(^|[/\\])\../, // dotfiles (also covers .build/ and .claude/)
JUNK_EXT_RE, // pure build noise (aux/log/synctex/…) — never project content
- /(?:^|[/\\])(?:CLAUDE\.md|\.mcp\.json)$/, // App-generated config files
+ /(?:^|[/\\])(?:CLAUDE\.md|AGENTS\.md|\.mcp\.json)$/, // App-generated config files
/(?:^|[/\\])(?:claude-workspace|__MACOSX)(?:[/\\]|$)/ // scratch space + zip junk
]
})
@@ -752,7 +752,7 @@ export class FileSyncBridge {
// Skip app-generated config files and scratch space that should not be synced
const basename = relPath.split('/').pop() || relPath
- if (basename === 'CLAUDE.md' || basename === '.mcp.json') return
+ if (basename === 'CLAUDE.md' || basename === 'AGENTS.md' || basename === '.mcp.json') return
if (relPath.startsWith('claude-workspace/') || relPath === 'claude-workspace') return
// Layer 1: Skip if bridge is currently writing this file
@@ -1412,7 +1412,7 @@ export class FileSyncBridge {
// Skip LaTeX build artifacts, dotfiles, app config files, and scratch space
if (this.isCompileArtifact(relPath)) continue
if (/(^|[/\\])\./.test(relPath)) continue
- if (/(?:^|[/\\])(?:CLAUDE\.md|\.mcp\.json)$/.test(relPath)) continue
+ if (/(?:^|[/\\])(?:CLAUDE\.md|AGENTS\.md|\.mcp\.json)$/.test(relPath)) continue
if (relPath.startsWith('claude-workspace/') || relPath === 'claude-workspace') continue
bridgeLog(`[FileSyncBridge] orphaned file found: ${relPath}`)
@@ -1433,7 +1433,7 @@ export class FileSyncBridge {
// Skip LaTeX build artifacts, dotfiles, app config files, and scratch space
if (this.isCompileArtifact(relPath)) return
if (/(^|[/\\])\./.test(relPath)) return
- if (/(?:^|[/\\])(?:CLAUDE\.md|\.mcp\.json)$/.test(relPath)) return
+ if (/(?:^|[/\\])(?:CLAUDE\.md|AGENTS\.md|\.mcp\.json)$/.test(relPath)) return
if (relPath.startsWith('claude-workspace/') || relPath === 'claude-workspace') return
// Debounce 1s to let the tool finish writing
diff --git a/src/main/index.ts b/src/main/index.ts
index f03f8b6..32be5b3 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -864,9 +864,13 @@ ipcMain.handle('ot:connect', async (_e, projectId: string) => {
// Relay collaborator cursor updates to renderer + track for MCP
overleafSock.on('serverEvent', (name: string, args: unknown[]) => {
if (name === 'clientTracking.clientUpdated') {
- sendToRenderer('cursor:remoteUpdate', args[0])
- // Track online user for MCP
const u = args[0] as { id: string; user_id?: string; name?: string; email?: string }
+ // Skip our own echo — the native caret already marks our position;
+ // colored overlay cursors are for collaborators only (web behavior)
+ if (!u.id || u.id !== overleafSock?.publicId) {
+ sendToRenderer('cursor:remoteUpdate', args[0])
+ }
+ // Track online user for MCP (includes ourselves)
if (u.id) {
mcpOnlineUsers.set(u.id, { name: u.name || u.email?.split('@')[0] || 'User', email: u.email })
writeMcpOnlineUsers()
@@ -928,8 +932,9 @@ ipcMain.handle('ot:connect', async (_e, projectId: string) => {
// Write .mcp.json so Claude Code auto-discovers the MCP server
// Dev: use source file. Packaged: copy bundled server into the project
// temp dir so .mcp.json never contains a stale App Translocation path.
+ let mcpServerPath = ''
try {
- const mcpServerPath = await prepareMcpServerPath(tmpDir)
+ mcpServerPath = await prepareMcpServerPath(tmpDir)
await writeFile(join(tmpDir, '.mcp.json'), JSON.stringify({
mcpServers: {
lattex: {
@@ -964,7 +969,11 @@ ipcMain.handle('ot:connect', async (_e, projectId: string) => {
} catch { /* non-fatal */ }
const ownerName = [projectResult.project.owner.first_name, projectResult.project.owner.last_name].filter(Boolean).join(' ')
- await writeFile(join(tmpDir, '.claude', 'CLAUDE.md'), `# ${projectResult.project.name} — Overleaf Project
+ // One guide, two consumers: .claude/CLAUDE.md (Claude Code's native
+ // location) and AGENTS.md at the project root (the cross-tool standard
+ // read by Codex, Cursor, Gemini CLI, etc.). AGENTS.md is excluded from
+ // Overleaf sync alongside CLAUDE.md/.mcp.json.
+ const agentGuide = `# ${projectResult.project.name} — Overleaf Project
> **IMPORTANT — MANDATORY FIRST STEPS (do this EVERY conversation before ANY edits):**
>
@@ -1054,7 +1063,21 @@ The \`claude-workspace/\` directory is your private scratch space. It is **not s
- **Scripts** — helper scripts for data processing, bibliography management, etc.
**Important**: Always ask the user before running experiments or creating files in \`claude-workspace/\`. This directory persists across sessions for the same project.
-`)
+
+## Agent Setup (MCP)
+
+The tools above come from LatteX's MCP server (standard stdio MCP — works with any MCP-capable agent):
+
+- **Claude Code**: auto-configured. \`.mcp.json\` in this directory registers the \`lattex\` server and \`.claude/settings.json\` pre-approves its tools. Just run \`claude\`.
+- **Codex CLI**: register the server once for this project:
+ \`\`\`
+ codex mcp add lattex -- node "${mcpServerPath}"
+ \`\`\`
+ The path is project-specific — re-run this when switching projects. Approve \`lattex\` tool calls when Codex prompts.
+- **Any other MCP client**: stdio transport, command \`node "${mcpServerPath}"\`.
+`
+ await writeFile(join(tmpDir, '.claude', 'CLAUDE.md'), agentGuide)
+ await writeFile(join(tmpDir, 'AGENTS.md'), agentGuide)
await writeFile(join(tmpDir, '.claude', 'settings.json'), JSON.stringify({
permissions: {
allow: [
@@ -1280,7 +1303,7 @@ ipcMain.handle('cursor:getConnectedUsers', async () => {
if (!overleafSock) return []
try {
const users = await overleafSock.getConnectedUsers()
- // Seed MCP online users map
+ // Seed MCP online users map (includes ourselves)
mcpOnlineUsers.clear()
for (const raw of users) {
const u = raw as { client_id?: string; first_name?: string; last_name?: string; email?: string }
@@ -1290,7 +1313,11 @@ ipcMain.handle('cursor:getConnectedUsers', async () => {
}
}
writeMcpOnlineUsers()
- return users
+ // Exclude our own client — no colored overlay cursor for ourselves
+ return users.filter((raw) => {
+ const u = raw as { client_id?: string }
+ return !u.client_id || u.client_id !== overleafSock?.publicId
+ })
} catch (e) {
console.log('[cursor:getConnectedUsers] error:', e)
return []