feat: Universal clipboard utility with improved copy functionality#663
feat: Universal clipboard utility with improved copy functionality#663
Conversation
- Add comprehensive clipboard utility (src/utils/clipboard.ts) with: - Modern Clipboard API with automatic fallback to document.execCommand - Cross-browser compatibility and security context handling - Detailed error reporting and debugging capabilities - Support for secure (HTTPS) and insecure (HTTP/localhost) contexts - Update components to use new clipboard utility: - BugReportModal: Enhanced copy functionality with error handling - CodeViewerModal: Improved copy-to-clipboard for code snippets - IDEGlobalRules: Robust clipboard operations for rule copying - McpConfigSection: Enhanced config and command copying - DocumentCard: Reliable ID copying functionality - KnowledgeInspector: Improved content copying - ButtonPlayground: Enhanced CSS style copying - Benefits: - Consistent copy behavior across all browser environments - Better error handling and user feedback - Improved accessibility and security context support - Enhanced debugging capabilities Fixes #662
WalkthroughStandardizes clipboard operations by adding a universal clipboard utility and updating multiple components to use result-based copy handling. Simplifies frontend API URL resolution to prefer relative/proxy paths and exposes API_BASE_URL/API_FULL_URL. Adds ARCHON_HOST and DOCKER_ENV to docker-compose and makes backend MCP host derive from ARCHON_HOST. No public component APIs changed. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant C as UI Component
participant CL as ClipboardUtil
participant N as Navigator.clipboard
participant D as document.execCommand
U->>C: Click "Copy"
C->>CL: copyToClipboard(text)
alt Clipboard API available
CL->>N: writeText(text)
alt Success
N-->>CL: ok
CL-->>C: { success: true, method: 'clipboard-api' }
C->>C: set copied / show success toast
else Failure
N-->>CL: error
CL->>D: execCommand('copy') fallback
alt Fallback success
D-->>CL: true
CL-->>C: { success: true, method: 'execCommand' }
C->>C: set copied / show success toast
else Fallback fail
D-->>CL: false/error
CL-->>C: { success: false, method: 'failed', error }
C->>C: log error / show failure toast
end
end
else No API available
CL->>D: execCommand('copy')
D-->>CL: success/failure
CL-->>C: { success: boolean, method, error? }
C->>C: UI feedback per result
end
sequenceDiagram
autonumber
participant FE as Frontend
participant CFG as config/api.ts
participant S as Server (MCP API)
participant ENV as ARCHON_HOST env
FE->>CFG: getApiBasePath()
alt VITE_API_URL set
CFG-->>FE: "<VITE_API_URL>/api"
else not set
CFG-->>FE: "/api" (relative)
end
FE->>S: GET /api/mcp/config
S->>ENV: read ARCHON_HOST (default "localhost")
ENV-->>S: host
S-->>FE: JSON config with host=<env or default>
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (12)
archon-ui-main/src/utils/clipboard.ts (2)
17-27: Guard against SSR/non‑browser environments in copyToClipboard.Direct navigator/document access throws during SSR. Short‑circuit early with a clear error.
export const copyToClipboard = async (text: string): Promise<ClipboardResult> => { + if (typeof window === 'undefined' || typeof document === 'undefined') { + return { success: false, method: 'failed', error: 'Clipboard unavailable on server' }; + } // Try modern clipboard API first if (navigator.clipboard && navigator.clipboard.writeText) {
20-26: Preserve original error for diagnostics.Capture Clipboard API error to include in the eventual failure message if fallback also fails.
- if (navigator.clipboard && navigator.clipboard.writeText) { + let apiError: unknown | undefined; + if (navigator.clipboard && navigator.clipboard.writeText) { try { await navigator.clipboard.writeText(text); return { success: true, method: 'clipboard-api' }; } catch (error) { - console.warn('Clipboard API failed, trying fallback:', error); + apiError = error; + console.warn('Clipboard API failed, trying fallback:', error); } } @@ - return { + return { success: false, method: 'failed', - error: error instanceof Error ? error.message : 'Unknown error' + error: + (apiError instanceof Error && `API failed: ${apiError.message}`) || + (error instanceof Error && error.message) || + 'Unknown error' };archon-ui-main/src/features/mcp/components/McpConfigSection.tsx (3)
189-199: Surface copy method in toast for debugging.Expose which path succeeded (Clipboard API vs execCommand) to aid user reports.
- if (result.success) { - showToast("Configuration copied to clipboard", "success"); + if (result.success) { + showToast(`Configuration copied (${result.method})`, "success");
212-222: Same enhancement for Claude command.- if (result.success) { - showToast("Command copied to clipboard", "success"); + if (result.success) { + showToast(`Command copied (${result.method})`, "success");
287-291: Optionally disable Copy when unsupported.Consider disabling the button if isClipboardSupported() is false to improve UX in restricted contexts.
archon-ui-main/src/components/code/CodeViewerModal.tsx (1)
106-115: Clean up timeout on unmount.Store the timer id and clear it in cleanup to avoid setting state after unmount.
- const handleCopyCode = async () => { + const copyTimer = React.useRef<number | null>(null); + const handleCopyCode = async () => { if (activeExample) { const result = await copyToClipboard(activeExample.code) if (result.success) { setCopied(true) - setTimeout(() => setCopied(false), 2000) + if (copyTimer.current) window.clearTimeout(copyTimer.current) + copyTimer.current = window.setTimeout(() => setCopied(false), 2000) } else { console.error('Failed to copy to clipboard:', result.error) } } } + useEffect(() => () => { if (copyTimer.current) window.clearTimeout(copyTimer.current) }, [])archon-ui-main/src/features/knowledge/inspector/components/KnowledgeInspector.tsx (1)
83-91: Clear copy timer safely.Avoid dangling timeout if component closes quickly.
- const handleCopy = useCallback(async (text: string, id: string) => { - const result = await copyToClipboard(text); - if (result.success) { - setCopiedId(id); - setTimeout(() => setCopiedId((v) => (v === id ? null : v)), 2000); + const clearTimerRef = useRef<number | null>(null); + const handleCopy = useCallback(async (text: string, id: string) => { + const result = await copyToClipboard(text); + if (result.success) { + setCopiedId(id); + if (clearTimerRef.current) window.clearTimeout(clearTimerRef.current); + clearTimerRef.current = window.setTimeout(() => setCopiedId((v) => (v === id ? null : v)), 2000); } else { console.error("Failed to copy to clipboard:", result.error); } }, []); + useEffect(() => () => { if (clearTimerRef.current) window.clearTimeout(clearTimerRef.current); }, []);archon-ui-main/src/components/settings/ButtonPlayground.tsx (1)
283-291: Clear copy timeout; prevent multiple overlapping timers.- const handleCopyToClipboard = async () => { + const copyTimer = React.useRef<number | null>(null); + const handleCopyToClipboard = async () => { const result = await copyToClipboard(generateCSS()); if (result.success) { setCopied(true); - setTimeout(() => setCopied(false), 2000); + if (copyTimer.current) window.clearTimeout(copyTimer.current); + copyTimer.current = window.setTimeout(() => setCopied(false), 2000); } else { console.error('Failed to copy to clipboard:', result.error); } }; + React.useEffect(() => () => { if (copyTimer.current) window.clearTimeout(copyTimer.current); }, []);archon-ui-main/src/components/settings/IDEGlobalRules.tsx (1)
476-490: Surface method in toast and clean up timer.Provide method detail for debugging and avoid stale timers on unmount.
- const handleCopyToClipboard = async () => { + const copyTimer = React.useRef<number | null>(null); + const handleCopyToClipboard = async () => { const result = await copyToClipboard(currentRules); if (result.success) { setCopied(true); - showToast(`${selectedRuleType === 'claude' ? 'Claude Code' : 'Universal'} rules copied to clipboard!`, 'success'); + showToast(`${selectedRuleType === 'claude' ? 'Claude Code' : 'Universal'} rules copied (${result.method})`, 'success'); // Reset copy icon after 2 seconds - setTimeout(() => { - setCopied(false); - }, 2000); + if (copyTimer.current) window.clearTimeout(copyTimer.current); + copyTimer.current = window.setTimeout(() => setCopied(false), 2000); } else { console.error('Failed to copy text:', result.error); showToast('Failed to copy to clipboard', 'error'); } }; + React.useEffect(() => () => { if (copyTimer.current) window.clearTimeout(copyTimer.current); }, []);archon-ui-main/src/features/projects/documents/components/DocumentCard.tsx (1)
70-78: Log failure path for visibility; keep user feedback consistent.Match other components by logging copy errors when copy fails.
const handleCopyId = useCallback( async (e: React.MouseEvent) => { e.stopPropagation(); const result = await copyToClipboard(document.id); if (result.success) { setIsCopied(true); setTimeout(() => setIsCopied(false), 2000); + } else { + // Keep silent UI but log for diagnostics + console.error("Failed to copy document id:", result.error); } }, [document.id], );archon-ui-main/src/components/bug-report/BugReportModal.tsx (2)
100-117: Differentiate copy failure with context.When clipboard copy fails in fallback, include security context to aid user action.
- const clipboardResult = await copyToClipboard(formattedReport); + const clipboardResult = await copyToClipboard(formattedReport); - if (clipboardResult.success) { + if (clipboardResult.success) { showToast( "Failed to create GitHub issue, but bug report was copied to clipboard. Please paste it in a new GitHub issue.", "warning", 10000, ); } else { - showToast( - "Failed to create GitHub issue and could not copy to clipboard. Please report manually.", - "error", - 10000, - ); + const ctx = (await import("../../utils/clipboard")).getSecurityContext?.() ?? "unknown"; + showToast(`Failed to create GitHub issue and could not copy to clipboard (context: ${ctx}). Please report manually.`, "error", 10000); }
130-141: Inline copy action: show method for support tickets.- const result = await copyToClipboard(formattedReport); - if (result.success) { - showToast("Bug report copied to clipboard", "success"); + const result = await copyToClipboard(formattedReport); + if (result.success) { + showToast(`Bug report copied (${result.method})`, "success"); } else { showToast("Failed to copy to clipboard", "error"); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
archon-ui-main/src/components/bug-report/BugReportModal.tsx(4 hunks)archon-ui-main/src/components/code/CodeViewerModal.tsx(2 hunks)archon-ui-main/src/components/settings/ButtonPlayground.tsx(3 hunks)archon-ui-main/src/components/settings/IDEGlobalRules.tsx(2 hunks)archon-ui-main/src/features/knowledge/inspector/components/KnowledgeInspector.tsx(2 hunks)archon-ui-main/src/features/mcp/components/McpConfigSection.tsx(3 hunks)archon-ui-main/src/features/projects/documents/components/DocumentCard.tsx(2 hunks)archon-ui-main/src/utils/clipboard.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
archon-ui-main/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
archon-ui-main/src/**/*.{ts,tsx}: Use TanStack Query for all data fetching; avoid prop drilling
TypeScript: strict mode with no implicit any in frontend code
State naming: is[Action]ing for loading flags, [resource]Error for errors, selected[Resource] for current selection
Use HTTP polling with ETag caching; do not introduce WebSocket-based updates in the frontend
archon-ui-main/src/**/*.{ts,tsx}: WebSocket event failures (if any) should be logged and not crash the client; continue serving others
Frontend data fetching must use TanStack Query (no prop drilling) with query key factories, smart polling, and optimistic updates with rollback
Use vertical slice architecture: place UI under src/features/[feature]/(components|hooks|services|types)
State naming: use is[Action]ing for loading, [resource]Error for errors, selected[Resource] for selections
Service method names: get[Resource]sByProject(projectId), getResource, create/update/delete patterns
Frontend TypeScript should be strict (no implicit any)
Files:
archon-ui-main/src/utils/clipboard.tsarchon-ui-main/src/features/projects/documents/components/DocumentCard.tsxarchon-ui-main/src/components/code/CodeViewerModal.tsxarchon-ui-main/src/features/knowledge/inspector/components/KnowledgeInspector.tsxarchon-ui-main/src/components/settings/IDEGlobalRules.tsxarchon-ui-main/src/components/settings/ButtonPlayground.tsxarchon-ui-main/src/features/mcp/components/McpConfigSection.tsxarchon-ui-main/src/components/bug-report/BugReportModal.tsx
**/*.{py,ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Never return None/null to indicate failure; raise an exception with details instead
Files:
archon-ui-main/src/utils/clipboard.tsarchon-ui-main/src/features/projects/documents/components/DocumentCard.tsxarchon-ui-main/src/components/code/CodeViewerModal.tsxarchon-ui-main/src/features/knowledge/inspector/components/KnowledgeInspector.tsxarchon-ui-main/src/components/settings/IDEGlobalRules.tsxarchon-ui-main/src/components/settings/ButtonPlayground.tsxarchon-ui-main/src/features/mcp/components/McpConfigSection.tsxarchon-ui-main/src/components/bug-report/BugReportModal.tsx
archon-ui-main/src/features/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
archon-ui-main/src/features/**/*.{ts,tsx}: Follow TanStack Query patterns: query-keys factory, smart polling via useSmartPolling, optimistic updates with rollback
Biome formatting in features: 120-character lines, double quotes, trailing commas
archon-ui-main/src/features/**/*.{ts,tsx}: Use Biome formatting/conventions in /src/features: 120-char lines, double quotes, trailing commas
Use useSmartPolling and polling intervals (1–2s active, 5–10s background) with smart pausing on tab inactivity
Expose progress via dedicated hooks (e.g., useCrawlProgressPolling, useProjectTasks) instead of ad-hoc timers
Do not use prop drilling for data fetching/state; rely on TanStack Query caches/selectors
Files:
archon-ui-main/src/features/projects/documents/components/DocumentCard.tsxarchon-ui-main/src/features/knowledge/inspector/components/KnowledgeInspector.tsxarchon-ui-main/src/features/mcp/components/McpConfigSection.tsx
archon-ui-main/src/components/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Legacy UI code should adhere to standard React ESLint rules
Files:
archon-ui-main/src/components/code/CodeViewerModal.tsxarchon-ui-main/src/components/settings/IDEGlobalRules.tsxarchon-ui-main/src/components/settings/ButtonPlayground.tsxarchon-ui-main/src/components/bug-report/BugReportModal.tsx
archon-ui-main/src/components/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Legacy UI under /components should follow ESLint standard React rules
Files:
archon-ui-main/src/components/code/CodeViewerModal.tsxarchon-ui-main/src/components/settings/IDEGlobalRules.tsxarchon-ui-main/src/components/settings/ButtonPlayground.tsxarchon-ui-main/src/components/bug-report/BugReportModal.tsx
🧬 Code graph analysis (7)
archon-ui-main/src/features/projects/documents/components/DocumentCard.tsx (1)
archon-ui-main/src/utils/clipboard.ts (1)
copyToClipboard(17-63)
archon-ui-main/src/components/code/CodeViewerModal.tsx (1)
archon-ui-main/src/utils/clipboard.ts (1)
copyToClipboard(17-63)
archon-ui-main/src/features/knowledge/inspector/components/KnowledgeInspector.tsx (1)
archon-ui-main/src/utils/clipboard.ts (1)
copyToClipboard(17-63)
archon-ui-main/src/components/settings/IDEGlobalRules.tsx (1)
archon-ui-main/src/utils/clipboard.ts (1)
copyToClipboard(17-63)
archon-ui-main/src/components/settings/ButtonPlayground.tsx (1)
archon-ui-main/src/utils/clipboard.ts (1)
copyToClipboard(17-63)
archon-ui-main/src/features/mcp/components/McpConfigSection.tsx (1)
archon-ui-main/src/utils/clipboard.ts (1)
copyToClipboard(17-63)
archon-ui-main/src/components/bug-report/BugReportModal.tsx (2)
archon-ui-main/src/utils/clipboard.ts (1)
copyToClipboard(17-63)archon-ui-main/src/services/bugReportService.ts (2)
BugReportData(33-42)bugReportService(244-244)
🔇 Additional comments (7)
archon-ui-main/src/features/mcp/components/McpConfigSection.tsx (1)
7-7: LGTM: centralized clipboard util adoption.archon-ui-main/src/components/code/CodeViewerModal.tsx (1)
33-33: LGTM: centralized clipboard util import.Consistent with new utility; reduces duplication.
archon-ui-main/src/components/settings/ButtonPlayground.tsx (1)
6-6: LGTM: reuse shared util instead of inline copy logic.archon-ui-main/src/components/settings/IDEGlobalRules.tsx (1)
6-6: LGTM: unified clipboard flow and error handling.archon-ui-main/src/features/projects/documents/components/DocumentCard.tsx (1)
18-18: LGTM: use shared util in card action.archon-ui-main/src/components/bug-report/BugReportModal.tsx (2)
381-389: LGTM: handler rename avoids shadowing imported util.New name handleCopyToClipboard prevents confusion and improves readability.
14-14: LGTM: imports new util; avoids name shadowing via handler.
…uration Clipboard utility improvements: - Prevent textarea element leak in clipboard fallback with proper cleanup - Add SSR compatibility with typeof guards for navigator/document - Use finally block to ensure cleanup in all error cases Host configuration fixes: - Update MCP API to use ARCHON_HOST environment variable instead of hardcoded localhost - Add ARCHON_HOST to docker-compose environment variables - Ensures MCP configuration shows correct hostname in different deployment environments Addresses CodeRabbit feedback and restores missing host functionality
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
python/src/server/api_routes/mcp_api.py (2)
104-115: Normalize ARCHON_HOST to avoid client-unreachable values (e.g., 0.0.0.0).If ARCHON_HOST is set to 0.0.0.0/:: (common in container configs), clients cannot connect. Sanitize to a reachable host for the browser.
Apply this diff:
@@ - # Configuration for streamable-http mode with actual port - config = { - "host": os.getenv("ARCHON_HOST", "localhost"), - "port": mcp_port, - "transport": "streamable-http", - } + # Configuration for streamable-http mode with actual port + env_host = os.getenv("ARCHON_HOST", "").strip() + host = "localhost" if env_host in {"", "0.0.0.0", "::"} else env_host + config = { + "host": host, + "port": mcp_port, + "transport": "streamable-http", + }Optional: prefer the incoming request’s host (x-forwarded-host or request.url.hostname) as a fallback when ARCHON_HOST is unset. I can provide a patch if you want to take that route.
97-135: Add ETag support to /api/mcp/config for cheap polling and 304s (per server guidelines).Small win for bandwidth and client responsiveness; hash the effective config and return 304 when unchanged.
Apply this diff:
@@ -@router.get("/config") -async def get_mcp_config(): +@router.get("/config") +async def get_mcp_config(request, response): @@ - api_logger.info("MCP configuration (streamable-http mode)") + # Compute a stable ETag over the public config + import hashlib + etag = hashlib.sha256( + f"{config['host']}:{config['port']}:{config.get('model_choice','gpt-4o-mini')}".encode("utf-8") + ).hexdigest()[:16] + if request.headers.get("if-none-match") == etag: + from fastapi import Response + return Response(status_code=304) + response.headers["ETag"] = etag + + api_logger.info("MCP configuration (streamable-http mode)")Add imports outside this hunk:
# at top-level imports from fastapi import APIRouter, HTTPException, Request, Response # add Request, ResponseIf you prefer, I can wrap this with a small helper to avoid repetition across endpoints.
🧹 Nitpick comments (2)
docker-compose.yml (2)
31-31: Avoid ${HOST}; default ARCHON_HOST to ARCHON_PUBLIC_HOST (host.docker.internal)ARCHON_PUBLIC_HOST is not defined in .env.example or .env — add it (e.g. ARCHON_PUBLIC_HOST=host.docker.internal).
File: docker-compose.yml (line ~31) — replace:
- - ARCHON_HOST=${HOST:-localhost} + - ARCHON_HOST=${ARCHON_PUBLIC_HOST:-host.docker.internal}
163-163: Document DOCKER_ENV — consumed by the UI.archon-ui-main/vite.config.ts:18 reads
process.env.DOCKER_ENV === 'true' || existsSync('/.dockerenv'); add DOCKER_ENV to the environment-variable docs (expected value:'true'to force Docker mode; fallback:/.dockerenv).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
archon-ui-main/src/utils/clipboard.ts(1 hunks)docker-compose.yml(2 hunks)python/src/server/api_routes/mcp_api.py(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- archon-ui-main/src/utils/clipboard.ts
🧰 Additional context used
📓 Path-based instructions (5)
python/src/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
python/src/**/*.py: Fail fast on critical conditions: service startup failures, missing configuration/env vars, database connection/auth failures, critical dependencies unavailable
Never accept or store corrupted data (e.g., zero embeddings, null foreign keys, malformed JSON); skip failed items entirely and continue processing
For batch/background operations, continue processing but log detailed per-item failures; for external APIs use retries with exponential backoff and then fail clearly
Error messages must include context, use specific exception types, preserve full stack traces (logging with exc_info=True), include relevant IDs/URLs, and never return None to indicate failure—raise instead; for batch ops report success counts and detailed failures
Backend uses Python 3.12 with a 120-character line length
Avoid introducing WebSocket support in the backend; updates are handled via HTTP polling
Adhere to Ruff lint rules (e.g., no unused imports) and provide type hints to satisfy MyPy
python/src/**/*.py: Fail fast on service startup failures (credentials, DB, service init); crash with clear errors
Treat missing configuration (env vars/invalid settings) as fatal; stop the system
Do not hide database connection failures; bubble up and surface clearly
Authentication/authorization failures must halt the operation and be visible
Never silently accept bad data; let Pydantic validation errors raise
If critical dependencies are unavailable, fail immediately
Reject invalid data that could corrupt state (e.g., zero embeddings, null FKs, malformed JSON)
Batch processing should complete remaining items but log detailed per-item failures
Background tasks (e.g., embedding generation) should finish queues while logging failures
Treat optional features as skippable: log and skip when disabled rather than crashing
External API calls: use retry with exponential backoff; on final failure, raise with clear service/context info
Never accept corrupted data during partial-failure work...
Files:
python/src/server/api_routes/mcp_api.py
python/src/server/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Use specific exception classes and FastAPI exception handlers to produce rich JSON error responses
python/src/server/**/*.py: Provide rich error responses in FastAPI (custom exceptions and handlers that return structured JSON with detail/type)
Implement HTTP polling support and ETag caching on endpoints to enable 304 responses
Files:
python/src/server/api_routes/mcp_api.py
python/src/server/{api_routes,services}/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Follow Service Layer pattern: API routes delegate to services, which handle business logic and call the database layer
Files:
python/src/server/api_routes/mcp_api.py
**/*.{py,ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Never return None/null to indicate failure; raise an exception with details instead
Files:
python/src/server/api_routes/mcp_api.py
python/src/server/api_routes/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Place API route handlers under python/src/server/api_routes/
Files:
python/src/server/api_routes/mcp_api.py
🧠 Learnings (1)
📚 Learning: 2025-09-13T15:53:40.741Z
Learnt from: CR
PR: coleam00/Archon#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-13T15:53:40.741Z
Learning: Applies to .env : Optional environment variables may be set: LOGFIRE_TOKEN, LOG_LEVEL, ARCHON_SERVER_PORT, ARCHON_MCP_PORT, ARCHON_UI_PORT
Applied to files:
docker-compose.yml
- Update getApiUrl() to return empty string when VITE_API_URL is unset - Ensures all API requests use relative paths (/api/...) in development - Prevents bypassing Vite proxy with absolute URLs (host:port) - Maintains existing functionality for explicit VITE_API_URL configuration - Fix TypeScript error by using bracket notation for environment access Addresses CodeRabbit feedback about dev setup relying on Vite proxy
Use proper type assertion to access VITE_API_URL environment variable
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
archon-ui-main/src/config/api.ts (2)
30-31: Normalize to avoid//apiwhenVITE_API_URLends with/.If
VITE_API_URLhas a trailing slash,${apiUrl}/apiyields a double slash. Normalize first.- return `${apiUrl}/api`; + const base = apiUrl.replace(/\/+$/, ''); + return `${base}/api`;
34-36: Avoid module-load evaluation; export a lazy getter.
API_FULL_URL = getApiUrl()executes at import time, which can be brittle in SSR/tests. Prior learning for this file recommends lazy getters over module-load constants.-export const API_FULL_URL = getApiUrl(); +export function getApiFullUrl(): string { + return getApiUrl(); +}Optionally, also prefer
getApiBasePath()at call sites overAPI_BASE_URLto keep behavior encapsulated.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
archon-ui-main/src/config/api.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
archon-ui-main/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
archon-ui-main/src/**/*.{ts,tsx}: Use TanStack Query for all data fetching; avoid prop drilling
TypeScript: strict mode with no implicit any in frontend code
State naming: is[Action]ing for loading flags, [resource]Error for errors, selected[Resource] for current selection
Use HTTP polling with ETag caching; do not introduce WebSocket-based updates in the frontend
archon-ui-main/src/**/*.{ts,tsx}: WebSocket event failures (if any) should be logged and not crash the client; continue serving others
Frontend data fetching must use TanStack Query (no prop drilling) with query key factories, smart polling, and optimistic updates with rollback
Use vertical slice architecture: place UI under src/features/[feature]/(components|hooks|services|types)
State naming: use is[Action]ing for loading, [resource]Error for errors, selected[Resource] for selections
Service method names: get[Resource]sByProject(projectId), getResource, create/update/delete patterns
Frontend TypeScript should be strict (no implicit any)
Files:
archon-ui-main/src/config/api.ts
**/*.{py,ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Never return None/null to indicate failure; raise an exception with details instead
Files:
archon-ui-main/src/config/api.ts
🧠 Learnings (1)
📚 Learning: 2025-09-04T16:30:05.227Z
Learnt from: stevepresley
PR: coleam00/Archon#573
File: archon-ui-main/src/config/api.ts:15-25
Timestamp: 2025-09-04T16:30:05.227Z
Learning: Archon UI API config: Prefer lazy getters getApiFullUrl() and getWsUrl() over module-load constants to avoid SSR/test crashes. Avoid CommonJS exports patterns (Object.defineProperty(exports,…)) in ESM. Add typeof window guards with VITE_API_URL fallback inside getApiUrl()/getWebSocketUrl() when SSR safety is required.
Applied to files:
archon-ui-main/src/config/api.ts
🔇 Additional comments (1)
archon-ui-main/src/config/api.ts (1)
16-18: Empty-string sentinel: verify callers and prefer base-path helper.Returning '' to signal "relative/proxy mode" is fine only if every caller builds paths via getApiBasePath() or API_BASE_URL — verify there are no direct concatenations of getApiUrl() with path strings (risk: malformed URLs or accidental leading "//"). Automated search in the sandbox failed; run these locally and fix any matches to use the base-path helper:
# search all JS/TS files (exclude node_modules) rg -n -S '\bgetApiUrl\s*\(' -g '!**/node_modules/**' || true rg -n -S 'getApiUrl\(\)\s*\+' -g '!**/node_modules/**' || true rg -n -S '\$\{\s*getApiUrl\(\)\s*\}' -g '!**/node_modules/**' || true rg -n -S 'getApiUrl\(\)\s*/' -g '!**/node_modules/**' || true rg -n -S '\bgetApiBasePath\s*\(' -g '!**/node_modules/**' || true rg -n -S '\bAPI_(BASE|FULL)_URL\b' -g '!**/node_modules/**' || true
| // Check if VITE_API_URL is explicitly provided (for absolute URL mode) | ||
| const viteApiUrl = (import.meta.env as any).VITE_API_URL as string | undefined; | ||
| if (viteApiUrl) { | ||
| return viteApiUrl; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Remove any-cast; use typed env and trim.
Casting import.meta.env to any breaks strict TS guarantees and violates our frontend guideline. Type it and avoid any.
Apply this diff:
- const viteApiUrl = (import.meta.env as any).VITE_API_URL as string | undefined;
+ const viteApiUrl = import.meta.env.VITE_API_URL?.trim();Add or update env typings (new file suggested) to keep strict mode:
// env.d.ts
interface ImportMetaEnv {
readonly VITE_API_URL?: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}🤖 Prompt for AI Agents
In archon-ui-main/src/config/api.ts around lines 10 to 13, remove the unsafe
(import.meta.env as any) cast and use a typed env property instead; add a new
env.d.ts (placed in src/ or root) containing the suggested
ImportMetaEnv/ImportMeta interfaces so TypeScript knows VITE_API_URL is
optional, then read const viteApiUrl = import.meta.env.VITE_API_URL; and return
viteApiUrl?.trim() (or undefined if falsy) so the value is typed and
whitespace-trimmed.
| @@ -0,0 +1,135 @@ | |||
| /** | |||
There was a problem hiding this comment.
Following the new architecture this file should be inside the src/features directory
src/features/shared/
| return 'insecure'; | ||
| }; | ||
|
|
||
| /** |
There was a problem hiding this comment.
Is this needed if we have completely replaced the old one?
This would be dead code together with the old api
…ecture - Move clipboard.ts from src/utils/ to src/features/shared/utils/ - Remove copyTextToClipboard backward compatibility function (dead code) - Update all import statements to use new file location - Maintain full clipboard functionality with modern API and fallbacks Addresses: - Review comment r2348420743: Move to new architecture location - Review comment r2348422625: Remove unused backward compatibility function 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
archon-ui-main/src/features/shared/utils/clipboard.ts (3)
31-38: Fallback path should check for document in SSR before touching document.body.Use a typeof guard to avoid throwing when
documentis undefined in SSR.- // Ensure document.body exists before proceeding - if (!document.body) { + // Ensure document exists and document.body is available before proceeding + if (typeof document === "undefined" || !document.body) { return { success: false, method: 'failed', - error: 'document.body is not available' + error: 'document is not available' }; }
28-30: Preserve focus when using the textarea fallback to avoid stealing focus.Capture and restore the previously focused element; this improves UX and a11y.
- let textarea: HTMLTextAreaElement | null = null; + let textarea: HTMLTextAreaElement | null = null; + let prevActive: Element | null = null; @@ - document.body.appendChild(textarea); + prevActive = document.activeElement; + document.body.appendChild(textarea); textarea.select(); textarea.setSelectionRange(0, text.length); @@ - if (textarea && document.body && document.body.contains(textarea)) { + if (textarea && document.body && document.body.contains(textarea)) { try { document.body.removeChild(textarea); } catch (cleanupError) { // Ignore cleanup errors - element may have already been removed console.warn('Failed to cleanup textarea element:', cleanupError); } } + // Restore prior focus if possible + if (prevActive && "focus" in prevActive && typeof (prevActive as any).focus === "function") { + try { + (prevActive as HTMLElement).focus(); + } catch { + /* no-op */ + } + }Also applies to: 50-53, 71-80
118-125: *Recognize IPv6 localhost and .localhost in security context.Treat
::1and subdomains likefoo.localhostas localhost for clearer diagnostics.export const getSecurityContext = (): string => { if (typeof window === 'undefined') return 'server'; if (window.isSecureContext) return 'secure'; if (window.location.protocol === 'https:') return 'https'; - if (window.location.hostname === 'localhost' || - window.location.hostname === '127.0.0.1') return 'localhost'; + const host = window.location.hostname; + if ( + host === 'localhost' || + host === '127.0.0.1' || + host === '::1' || + host.endsWith('.localhost') + ) return 'localhost'; return 'insecure'; };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
archon-ui-main/src/components/bug-report/BugReportModal.tsx(4 hunks)archon-ui-main/src/components/code/CodeViewerModal.tsx(2 hunks)archon-ui-main/src/components/settings/ButtonPlayground.tsx(3 hunks)archon-ui-main/src/components/settings/IDEGlobalRules.tsx(2 hunks)archon-ui-main/src/features/knowledge/inspector/components/KnowledgeInspector.tsx(2 hunks)archon-ui-main/src/features/mcp/components/McpConfigSection.tsx(3 hunks)archon-ui-main/src/features/projects/documents/components/DocumentCard.tsx(2 hunks)archon-ui-main/src/features/shared/utils/clipboard.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
- archon-ui-main/src/features/projects/documents/components/DocumentCard.tsx
- archon-ui-main/src/features/mcp/components/McpConfigSection.tsx
- archon-ui-main/src/components/code/CodeViewerModal.tsx
- archon-ui-main/src/features/knowledge/inspector/components/KnowledgeInspector.tsx
- archon-ui-main/src/components/settings/ButtonPlayground.tsx
- archon-ui-main/src/components/settings/IDEGlobalRules.tsx
- archon-ui-main/src/components/bug-report/BugReportModal.tsx
🧰 Additional context used
📓 Path-based instructions (3)
archon-ui-main/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
archon-ui-main/src/**/*.{ts,tsx}: Use TanStack Query for all data fetching; avoid prop drilling
TypeScript: strict mode with no implicit any in frontend code
State naming: is[Action]ing for loading flags, [resource]Error for errors, selected[Resource] for current selection
Use HTTP polling with ETag caching; do not introduce WebSocket-based updates in the frontend
archon-ui-main/src/**/*.{ts,tsx}: WebSocket event failures (if any) should be logged and not crash the client; continue serving others
Frontend data fetching must use TanStack Query (no prop drilling) with query key factories, smart polling, and optimistic updates with rollback
Use vertical slice architecture: place UI under src/features/[feature]/(components|hooks|services|types)
State naming: use is[Action]ing for loading, [resource]Error for errors, selected[Resource] for selections
Service method names: get[Resource]sByProject(projectId), getResource, create/update/delete patterns
Frontend TypeScript should be strict (no implicit any)
Files:
archon-ui-main/src/features/shared/utils/clipboard.ts
archon-ui-main/src/features/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
archon-ui-main/src/features/**/*.{ts,tsx}: Follow TanStack Query patterns: query-keys factory, smart polling via useSmartPolling, optimistic updates with rollback
Biome formatting in features: 120-character lines, double quotes, trailing commas
archon-ui-main/src/features/**/*.{ts,tsx}: Use Biome formatting/conventions in /src/features: 120-char lines, double quotes, trailing commas
Use useSmartPolling and polling intervals (1–2s active, 5–10s background) with smart pausing on tab inactivity
Expose progress via dedicated hooks (e.g., useCrawlProgressPolling, useProjectTasks) instead of ad-hoc timers
Do not use prop drilling for data fetching/state; rely on TanStack Query caches/selectors
Files:
archon-ui-main/src/features/shared/utils/clipboard.ts
**/*.{py,ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Never return None/null to indicate failure; raise an exception with details instead
Files:
archon-ui-main/src/features/shared/utils/clipboard.ts
🔇 Additional comments (2)
archon-ui-main/src/features/shared/utils/clipboard.ts (2)
82-84: Export alias copyTextToClipboard to preserve API contract (#662)Repo search shows only copyToClipboard is used in-repo; no occurrences of copyTextToClipboard. If this util is part of the public API or used by downstream packages, add the alias at the end of archon-ui-main/src/features/shared/utils/clipboard.ts.
}; +/** Alias to match API contract from #662 */ +export const copyTextToClipboard = copyToClipboard;
1-127: Format archon-ui-main/src/features/shared/utils/clipboard.ts with Biome (double quotes + trailing commas)
Per src/features conventions, convert single quotes to double quotes and add trailing commas; run: npx @biomejs/biome format archon-ui-main/src/features/shared/utils/clipboard.ts
- Add typeof navigator !== 'undefined' guard before accessing navigator.clipboard - Add typeof document !== 'undefined' guard before using document.execCommand fallback - Ensure proper error handling when running in server-side environment - Maintain existing functionality while preventing ReferenceError during SSR/prerender Addresses CodeRabbit feedback: Navigator access needs SSR-safe guards 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…oleam00#663) * feat: Add universal clipboard utility with enhanced copy functionality - Add comprehensive clipboard utility (src/utils/clipboard.ts) with: - Modern Clipboard API with automatic fallback to document.execCommand - Cross-browser compatibility and security context handling - Detailed error reporting and debugging capabilities - Support for secure (HTTPS) and insecure (HTTP/localhost) contexts - Update components to use new clipboard utility: - BugReportModal: Enhanced copy functionality with error handling - CodeViewerModal: Improved copy-to-clipboard for code snippets - IDEGlobalRules: Robust clipboard operations for rule copying - McpConfigSection: Enhanced config and command copying - DocumentCard: Reliable ID copying functionality - KnowledgeInspector: Improved content copying - ButtonPlayground: Enhanced CSS style copying - Benefits: - Consistent copy behavior across all browser environments - Better error handling and user feedback - Improved accessibility and security context support - Enhanced debugging capabilities Fixes coleam00#662 * fix: Improve clipboard utility robustness and add missing host configuration Clipboard utility improvements: - Prevent textarea element leak in clipboard fallback with proper cleanup - Add SSR compatibility with typeof guards for navigator/document - Use finally block to ensure cleanup in all error cases Host configuration fixes: - Update MCP API to use ARCHON_HOST environment variable instead of hardcoded localhost - Add ARCHON_HOST to docker-compose environment variables - Ensures MCP configuration shows correct hostname in different deployment environments Addresses CodeRabbit feedback and restores missing host functionality * fix: Use relative URLs for Vite proxy in development - Update getApiUrl() to return empty string when VITE_API_URL is unset - Ensures all API requests use relative paths (/api/...) in development - Prevents bypassing Vite proxy with absolute URLs (host:port) - Maintains existing functionality for explicit VITE_API_URL configuration - Fix TypeScript error by using bracket notation for environment access Addresses CodeRabbit feedback about dev setup relying on Vite proxy * fix: Resolve TypeScript error in API configuration Use proper type assertion to access VITE_API_URL environment variable * Address PR review comments: Move clipboard utility to features architecture - Move clipboard.ts from src/utils/ to src/features/shared/utils/ - Remove copyTextToClipboard backward compatibility function (dead code) - Update all import statements to use new file location - Maintain full clipboard functionality with modern API and fallbacks Addresses: - Review comment r2348420743: Move to new architecture location - Review comment r2348422625: Remove unused backward compatibility function 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Fix SSR safety issue in clipboard utility - Add typeof navigator !== 'undefined' guard before accessing navigator.clipboard - Add typeof document !== 'undefined' guard before using document.execCommand fallback - Ensure proper error handling when running in server-side environment - Maintain existing functionality while preventing ReferenceError during SSR/prerender Addresses CodeRabbit feedback: Navigator access needs SSR-safe guards 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
feat: add WISC framework context engineering for video demonstration
…eparation feat: add WISC framework context engineering for video demonstration
…eparation feat: add WISC framework context engineering for video demonstration
Pull Request
Summary
Implement a robust, universal clipboard utility that provides consistent copy-to-clipboard functionality across different browser contexts and security environments. This PR consolidates clipboard operations throughout the application with proper error handling and fallback mechanisms.
Changes Made
Add comprehensive clipboard utility (src/utils/clipboard.ts) with:
Update components to use new clipboard utility:
Benefits:
Fixes #662
Type of Change
Affected Services
Testing
Test Evidence
Checklist
Breaking Changes
None. This is a non-breaking change that enhances existing functionality without changing any public APIs.
Additional Notes
Summary by CodeRabbit