diff --git a/CLAUDE.md b/CLAUDE.md index af68c2dd..423efe40 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,7 +26,7 @@ When user says "COM": 1. Increment version in BOTH `package.json` AND `CLAUDE.md` (verify they match with `grep version package.json && grep Version CLAUDE.md`) 2. Run: `git add -A && git commit -m "chore: bump version to X.XXXX" && git push && npm run build && systemctl --user restart claudeman-web` -**Version**: 0.1482 (must match `package.json` for npm publish) +**Version**: 0.1483 (must match `package.json` for npm publish) ## Project Overview diff --git a/package.json b/package.json index 948a8d26..a3a0b42d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "claudeman", - "version": "0.1482", + "version": "0.1483", "description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence", "type": "module", "main": "dist/index.js", diff --git a/src/web/public/app.js b/src/web/public/app.js index 431cd1dd..ae90e4a8 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -524,17 +524,35 @@ const KeyboardAccessoryBar = { }, /** Read clipboard and send contents as input */ - async pasteFromClipboard() { + /** Show a paste overlay with a textarea for iOS compatibility */ + pasteFromClipboard() { if (typeof app === 'undefined' || !app.activeSessionId) return; - try { - const text = await navigator.clipboard.readText(); - if (text) { - app.sendInput(text); - } - } catch (_err) { - // Clipboard API may fail without secure context or permission - app.showToast?.('Clipboard access denied', 'error'); - } + + // Create overlay + const overlay = document.createElement('div'); + overlay.className = 'paste-overlay'; + overlay.innerHTML = ` +
+ +
+ + +
+
+ `; + + const textarea = overlay.querySelector('.paste-textarea'); + const send = () => { + const text = textarea.value; + overlay.remove(); + if (text) app.sendInput(text); + }; + overlay.querySelector('.paste-cancel').addEventListener('click', () => overlay.remove()); + overlay.querySelector('.paste-send').addEventListener('click', send); + overlay.addEventListener('click', (e) => { if (e.target === overlay) overlay.remove(); }); + + document.body.appendChild(overlay); + textarea.focus(); }, /** Show the accessory bar */ diff --git a/src/web/public/styles.css b/src/web/public/styles.css index 08baf11d..2069c594 100644 --- a/src/web/public/styles.css +++ b/src/web/public/styles.css @@ -8796,6 +8796,73 @@ kbd { /* Position is handled by JS transform along with toolbar */ } + /* Paste overlay for iOS clipboard access */ + .paste-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.6); + z-index: 10000; + display: flex; + align-items: flex-end; + justify-content: center; + padding-bottom: env(safe-area-inset-bottom, 12px); + } + + .paste-dialog { + background: var(--bg-secondary, #1e1e2e); + border: 1px solid var(--border-color, #444); + border-radius: 12px; + padding: 12px; + width: calc(100% - 24px); + max-width: 400px; + margin-bottom: 8px; + } + + .paste-textarea { + width: 100%; + min-height: 100px; + background: var(--bg-primary, #0d0d14); + color: var(--text-primary, #e0e0e0); + border: 1px solid var(--border-color, #444); + border-radius: 8px; + padding: 10px; + font-family: inherit; + font-size: 14px; + resize: vertical; + box-sizing: border-box; + } + + .paste-textarea:focus { + outline: none; + border-color: var(--accent-color, #7aa2f7); + } + + .paste-actions { + display: flex; + justify-content: flex-end; + gap: 8px; + margin-top: 10px; + } + + .paste-cancel, .paste-send { + padding: 8px 18px; + border: none; + border-radius: 8px; + font-size: 14px; + cursor: pointer; + } + + .paste-cancel { + background: var(--bg-tertiary, #333); + color: var(--text-secondary, #aaa); + } + + .paste-send { + background: var(--accent-color, #7aa2f7); + color: #fff; + font-weight: 600; + } + /* LEGACY: Hide old toolbar select (no longer used on mobile) */ .toolbar-select { display: none !important;