mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-08 16:39:42 +02:00
chore: bump version to 0.1636
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -35,7 +35,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`)
|
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`
|
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.1635 (must match `package.json`)
|
**Version**: 0.1636 (must match `package.json`)
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "claudeman",
|
"name": "claudeman",
|
||||||
"version": "0.1635",
|
"version": "0.1636",
|
||||||
"description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence",
|
"description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ After=network.target
|
|||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
WorkingDirectory=/home/arkon/default/claudeman
|
WorkingDirectory=/home/arkon/default/claudeman
|
||||||
ExecStart=/usr/bin/node dist/index.js web
|
ExecStart=/usr/bin/node dist/index.js web --https
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
KillMode=process
|
KillMode=process
|
||||||
|
|||||||
+31
-26
@@ -610,6 +610,13 @@ const VoiceInput = {
|
|||||||
_accumulatedFinal: '',
|
_accumulatedFinal: '',
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
this._initRecognition();
|
||||||
|
// Always show buttons — if unsupported, toggle() shows a toast
|
||||||
|
this._showButtons();
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Try to create a SpeechRecognition instance */
|
||||||
|
_initRecognition() {
|
||||||
const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
|
const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
|
||||||
this.supported = !!SR;
|
this.supported = !!SR;
|
||||||
if (!this.supported) return;
|
if (!this.supported) return;
|
||||||
@@ -623,9 +630,6 @@ const VoiceInput = {
|
|||||||
this.recognition.onresult = (e) => this._onResult(e);
|
this.recognition.onresult = (e) => this._onResult(e);
|
||||||
this.recognition.onerror = (e) => this._onError(e);
|
this.recognition.onerror = (e) => this._onError(e);
|
||||||
this.recognition.onend = () => this._onEnd();
|
this.recognition.onend = () => this._onEnd();
|
||||||
|
|
||||||
// Show voice buttons now that we know it's supported
|
|
||||||
this._showButtons();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
toggle() {
|
toggle() {
|
||||||
@@ -637,7 +641,13 @@ const VoiceInput = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
if (!this.supported || this.isRecording) return;
|
if (this.isRecording) return;
|
||||||
|
// Lazy-init: retry if recognition was cleaned up or not available at page load
|
||||||
|
if (!this.recognition) this._initRecognition();
|
||||||
|
if (!this.supported) {
|
||||||
|
app.showToast('Voice input not supported in this browser', 'warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!app.activeSessionId) {
|
if (!app.activeSessionId) {
|
||||||
app.showToast('No active session', 'warning');
|
app.showToast('No active session', 'warning');
|
||||||
return;
|
return;
|
||||||
@@ -798,20 +808,20 @@ const VoiceInput = {
|
|||||||
desktopBtn.setAttribute('aria-label', isRecording ? 'Stop voice input' : 'Start voice input');
|
desktopBtn.setAttribute('aria-label', isRecording ? 'Stop voice input' : 'Start voice input');
|
||||||
desktopBtn.title = isRecording ? 'Stop voice input (Ctrl+Shift+V)' : 'Voice input (Ctrl+Shift+V)';
|
desktopBtn.title = isRecording ? 'Stop voice input (Ctrl+Shift+V)' : 'Voice input (Ctrl+Shift+V)';
|
||||||
}
|
}
|
||||||
// Mobile button (inside accessory bar)
|
// Mobile toolbar button (always visible on mobile)
|
||||||
const mobileBtn = document.querySelector('[data-action="voice"]');
|
const mobileToolbarBtn = document.getElementById('voiceInputBtnMobile');
|
||||||
if (mobileBtn) {
|
if (mobileToolbarBtn) {
|
||||||
mobileBtn.classList.toggle('recording', isRecording);
|
mobileToolbarBtn.classList.toggle('recording', isRecording);
|
||||||
mobileBtn.setAttribute('aria-pressed', String(isRecording));
|
mobileToolbarBtn.setAttribute('aria-pressed', String(isRecording));
|
||||||
mobileBtn.setAttribute('aria-label', isRecording ? 'Stop voice input' : 'Start voice input');
|
mobileToolbarBtn.setAttribute('aria-label', isRecording ? 'Stop voice input' : 'Start voice input');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_showButtons() {
|
_showButtons() {
|
||||||
const desktopBtn = document.getElementById('voiceInputBtn');
|
const desktopBtn = document.getElementById('voiceInputBtn');
|
||||||
if (desktopBtn) desktopBtn.style.display = '';
|
if (desktopBtn) desktopBtn.style.display = '';
|
||||||
const mobileBtn = document.querySelector('[data-action="voice"]');
|
const mobileToolbarBtn = document.getElementById('voiceInputBtnMobile');
|
||||||
if (mobileBtn) mobileBtn.style.display = '';
|
if (mobileToolbarBtn) mobileToolbarBtn.style.display = '';
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Cleanup on SSE reconnect or page unload */
|
/** Cleanup on SSE reconnect or page unload */
|
||||||
@@ -863,16 +873,6 @@ const KeyboardAccessoryBar = {
|
|||||||
<button class="accessory-btn" data-action="init" title="/init">/init</button>
|
<button class="accessory-btn" data-action="init" title="/init">/init</button>
|
||||||
<button class="accessory-btn" data-action="clear" title="/clear">/clear</button>
|
<button class="accessory-btn" data-action="clear" title="/clear">/clear</button>
|
||||||
<button class="accessory-btn" data-action="compact" title="/compact">/compact</button>
|
<button class="accessory-btn" data-action="compact" title="/compact">/compact</button>
|
||||||
<button class="accessory-btn accessory-btn-voice" data-action="voice" title="Voice input"
|
|
||||||
aria-label="Start voice input" aria-pressed="false"
|
|
||||||
style="${!VoiceInput.supported ? 'display:none' : ''}">
|
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
|
|
||||||
<path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
|
|
||||||
<line x1="12" y1="19" x2="12" y2="23"/>
|
|
||||||
<line x1="8" y1="23" x2="16" y2="23"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button class="accessory-btn" data-action="paste" title="Paste from clipboard">
|
<button class="accessory-btn" data-action="paste" title="Paste from clipboard">
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/>
|
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/>
|
||||||
@@ -941,9 +941,6 @@ const KeyboardAccessoryBar = {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'voice':
|
|
||||||
VoiceInput.toggle();
|
|
||||||
break;
|
|
||||||
case 'paste':
|
case 'paste':
|
||||||
this.pasteFromClipboard();
|
this.pasteFromClipboard();
|
||||||
break;
|
break;
|
||||||
@@ -1418,6 +1415,10 @@ class ZerolagInputAddon {
|
|||||||
}
|
}
|
||||||
_detectBufferText() {
|
_detectBufferText() {
|
||||||
if (this._bufferDetectDone || !this._terminal) return null;
|
if (this._bufferDetectDone || !this._terminal) return null;
|
||||||
|
// Mark as done regardless of result — prevents repeated scans from picking
|
||||||
|
// up PTY-echoed text or Ink redraws that appear between keystrokes.
|
||||||
|
// Explicit resetBufferDetection() is needed to re-enable (e.g., Tab completion).
|
||||||
|
this._bufferDetectDone = true;
|
||||||
try {
|
try {
|
||||||
const prompt = this.findPrompt();
|
const prompt = this.findPrompt();
|
||||||
if (!prompt) return null;
|
if (!prompt) return null;
|
||||||
@@ -1425,7 +1426,7 @@ class ZerolagInputAddon {
|
|||||||
const afterPrompt = _zl_readTextAfterPrompt(this._terminal, prompt, offset);
|
const afterPrompt = _zl_readTextAfterPrompt(this._terminal, prompt, offset);
|
||||||
if (afterPrompt.length > 0) {
|
if (afterPrompt.length > 0) {
|
||||||
this._flushedOffset = afterPrompt.length; this._flushedText = afterPrompt;
|
this._flushedOffset = afterPrompt.length; this._flushedText = afterPrompt;
|
||||||
this._lastPromptPos = prompt; this._bufferDetectDone = true;
|
this._lastPromptPos = prompt;
|
||||||
return afterPrompt;
|
return afterPrompt;
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
@@ -2565,6 +2566,8 @@ class ClaudemanApp {
|
|||||||
// Enter: send full buffered text + \r to PTY in one shot
|
// Enter: send full buffered text + \r to PTY in one shot
|
||||||
const text = this._localEchoOverlay?.pendingText || '';
|
const text = this._localEchoOverlay?.pendingText || '';
|
||||||
this._localEchoOverlay?.clear();
|
this._localEchoOverlay?.clear();
|
||||||
|
// Suppress detection so PTY-echoed text isn't re-detected as user input
|
||||||
|
this._localEchoOverlay?.suppressBufferDetection();
|
||||||
// Clear flushed offset and text — Enter commits all text
|
// Clear flushed offset and text — Enter commits all text
|
||||||
this._flushedOffsets?.delete(this.activeSessionId);
|
this._flushedOffsets?.delete(this.activeSessionId);
|
||||||
this._flushedTexts?.delete(this.activeSessionId);
|
this._flushedTexts?.delete(this.activeSessionId);
|
||||||
@@ -2671,6 +2674,8 @@ class ClaudemanApp {
|
|||||||
// Control chars (Ctrl+C, single ESC): send buffered text + control char immediately
|
// Control chars (Ctrl+C, single ESC): send buffered text + control char immediately
|
||||||
const text = this._localEchoOverlay?.pendingText || '';
|
const text = this._localEchoOverlay?.pendingText || '';
|
||||||
this._localEchoOverlay?.clear();
|
this._localEchoOverlay?.clear();
|
||||||
|
// Suppress detection so PTY-echoed text isn't re-detected as user input
|
||||||
|
this._localEchoOverlay?.suppressBufferDetection();
|
||||||
// Clear flushed offset and text — control chars (Ctrl+C, Escape) change
|
// Clear flushed offset and text — control chars (Ctrl+C, Escape) change
|
||||||
// cursor position or abort readline, making flushed text tracking invalid.
|
// cursor position or abort readline, making flushed text tracking invalid.
|
||||||
this._flushedOffsets?.delete(this.activeSessionId);
|
this._flushedOffsets?.delete(this.activeSessionId);
|
||||||
|
|||||||
@@ -332,6 +332,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Mobile-only case button + gear -->
|
<!-- Mobile-only case button + gear -->
|
||||||
|
<button class="btn-voice-mobile" id="voiceInputBtnMobile" onclick="VoiceInput.toggle()"
|
||||||
|
title="Voice input" aria-label="Start voice input" aria-pressed="false">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
|
||||||
|
<path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
|
||||||
|
<line x1="12" y1="19" x2="12" y2="23"/>
|
||||||
|
<line x1="8" y1="23" x2="16" y2="23"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
<button class="btn-settings-mobile" onclick="app.openAppSettings()" title="App Settings">⚙</button>
|
<button class="btn-settings-mobile" onclick="app.openAppSettings()" title="App Settings">⚙</button>
|
||||||
<button class="btn-toolbar btn-case-mobile" onclick="app.showMobileCasePicker()" title="Select case">
|
<button class="btn-toolbar btn-case-mobile" onclick="app.showMobileCasePicker()" title="Select case">
|
||||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
|
||||||
@@ -362,8 +371,7 @@
|
|||||||
|
|
||||||
<div class="toolbar-right">
|
<div class="toolbar-right">
|
||||||
<button class="btn-toolbar btn-sm btn-voice" id="voiceInputBtn" onclick="VoiceInput.toggle()"
|
<button class="btn-toolbar btn-sm btn-voice" id="voiceInputBtn" onclick="VoiceInput.toggle()"
|
||||||
title="Voice input (Ctrl+Shift+V)" aria-label="Start voice input" aria-pressed="false"
|
title="Voice input (Ctrl+Shift+V)" aria-label="Start voice input" aria-pressed="false">
|
||||||
style="display: none;">
|
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
|
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
|
||||||
<path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
|
<path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
|
||||||
|
|||||||
+34
-19
@@ -314,6 +314,39 @@ html.mobile-init .file-browser-panel {
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Mobile voice input button in toolbar — initially hidden via inline style,
|
||||||
|
shown by VoiceInput._showButtons() clearing the inline display */
|
||||||
|
.btn-voice-mobile {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
background: #1a2a3f;
|
||||||
|
border: 1px solid rgba(59, 130, 246, 0.3);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #93c5fd;
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
order: 5; /* Between Run Shell and settings gear */
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-voice-mobile svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-voice-mobile:active {
|
||||||
|
background: #2a3a5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-voice-mobile.recording {
|
||||||
|
background: rgba(239, 68, 68, 0.2);
|
||||||
|
border-color: rgba(239, 68, 68, 0.5);
|
||||||
|
color: #ef4444;
|
||||||
|
animation: voice-pulse 1.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
/* Mobile app settings gear in toolbar - far right */
|
/* Mobile app settings gear in toolbar - far right */
|
||||||
.btn-settings-mobile {
|
.btn-settings-mobile {
|
||||||
display: inline-flex !important;
|
display: inline-flex !important;
|
||||||
@@ -328,7 +361,7 @@ html.mobile-init .file-browser-panel {
|
|||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
order: 5; /* Far right */
|
order: 6; /* Far right */
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -746,24 +779,6 @@ html.mobile-init .file-browser-panel {
|
|||||||
background: #3a3a3a;
|
background: #3a3a3a;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Voice input button */
|
|
||||||
.accessory-btn-voice {
|
|
||||||
background: #1a2a3f;
|
|
||||||
border-color: rgba(59, 130, 246, 0.25);
|
|
||||||
color: #93c5fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.accessory-btn-voice:active {
|
|
||||||
background: #2a3a5f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.accessory-btn-voice.recording {
|
|
||||||
background: rgba(239, 68, 68, 0.2);
|
|
||||||
border-color: rgba(239, 68, 68, 0.5);
|
|
||||||
color: #ef4444;
|
|
||||||
animation: voice-pulse 1.5s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Voice preview — positioned above accessory bar on mobile */
|
/* Voice preview — positioned above accessory bar on mobile */
|
||||||
.voice-preview {
|
.voice-preview {
|
||||||
bottom: calc(var(--safe-area-bottom) + 94px);
|
bottom: calc(var(--safe-area-bottom) + 94px);
|
||||||
|
|||||||
@@ -1936,6 +1936,11 @@ body {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Mobile voice input button - hidden on desktop */
|
||||||
|
.btn-voice-mobile {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Mobile case settings gear - hidden on desktop */
|
/* Mobile case settings gear - hidden on desktop */
|
||||||
.btn-case-settings-mobile {
|
.btn-case-settings-mobile {
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
Reference in New Issue
Block a user