mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
feat(ui): add enable/disable toggle for Ralph Wiggum tracker
Add ability to enable/disable the Ralph tracker: - Per-session toggle in Session Options > Ralph Wiggum tab - Global default toggle in App Settings Changes: - Add "Enable Tracker" toggle to ralph-tab in session options modal - Add "Enable Ralph Wiggum Tracker" toggle to app settings modal - Update /api/sessions/:id/inner-config endpoint to handle `enabled` param - Add CSS for toggle switch component - New sessions inherit global setting from app settings - Setting is saved in innerConfig and synced with backend Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -892,6 +892,9 @@ class ClaudemanApp {
|
||||
}
|
||||
}
|
||||
|
||||
// Get global Ralph tracker setting
|
||||
const ralphEnabled = this.isRalphTrackerEnabledByDefault();
|
||||
|
||||
// Create multiple sessions with unique w-numbers
|
||||
for (let i = 0; i < tabCount; i++) {
|
||||
const sessionName = `w${startNumber + i}-${caseName}`;
|
||||
@@ -905,6 +908,15 @@ class ClaudemanApp {
|
||||
const createData = await createRes.json();
|
||||
if (!createData.success) throw new Error(createData.error);
|
||||
|
||||
// Apply global Ralph tracker setting
|
||||
if (ralphEnabled) {
|
||||
await fetch(`/api/sessions/${createData.session.id}/inner-config`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ enabled: true })
|
||||
});
|
||||
}
|
||||
|
||||
// Start interactive mode
|
||||
await fetch(`/api/sessions/${createData.session.id}/interactive`, {
|
||||
method: 'POST'
|
||||
@@ -1347,6 +1359,7 @@ class ClaudemanApp {
|
||||
// Populate Ralph Wiggum form with current session values
|
||||
const innerState = this.innerStates.get(sessionId);
|
||||
this.populateRalphForm({
|
||||
enabled: innerState?.loop?.enabled ?? session.innerConfig?.enabled ?? false,
|
||||
completionPhrase: innerState?.loop?.completionPhrase || session.innerLoop?.completionPhrase || '',
|
||||
maxIterations: innerState?.loop?.maxIterations || session.innerLoop?.maxIterations || 0,
|
||||
maxTodos: session.innerConfig?.maxTodos || 50,
|
||||
@@ -1529,6 +1542,7 @@ class ClaudemanApp {
|
||||
|
||||
getRalphConfig() {
|
||||
return {
|
||||
enabled: document.getElementById('modalRalphEnabled').checked,
|
||||
completionPhrase: document.getElementById('modalRalphPhrase').value.trim(),
|
||||
maxIterations: parseInt(document.getElementById('modalRalphMaxIterations').value) || 0,
|
||||
maxTodos: parseInt(document.getElementById('modalRalphMaxTodos').value) || 50,
|
||||
@@ -1537,6 +1551,7 @@ class ClaudemanApp {
|
||||
}
|
||||
|
||||
populateRalphForm(config) {
|
||||
document.getElementById('modalRalphEnabled').checked = config?.enabled ?? false;
|
||||
document.getElementById('modalRalphPhrase').value = config?.completionPhrase || '';
|
||||
document.getElementById('modalRalphMaxIterations').value = config?.maxIterations || 0;
|
||||
document.getElementById('modalRalphMaxTodos').value = config?.maxTodos || 50;
|
||||
@@ -1625,6 +1640,7 @@ class ClaudemanApp {
|
||||
const settings = this.loadAppSettingsFromStorage();
|
||||
document.getElementById('appSettingsClaudeMdPath').value = settings.defaultClaudeMdPath || '';
|
||||
document.getElementById('appSettingsDefaultDir').value = settings.defaultWorkingDir || '';
|
||||
document.getElementById('appSettingsRalphEnabled').checked = settings.ralphTrackerEnabled ?? false;
|
||||
document.getElementById('appSettingsModal').classList.add('active');
|
||||
}
|
||||
|
||||
@@ -1636,6 +1652,7 @@ class ClaudemanApp {
|
||||
const settings = {
|
||||
defaultClaudeMdPath: document.getElementById('appSettingsClaudeMdPath').value.trim(),
|
||||
defaultWorkingDir: document.getElementById('appSettingsDefaultDir').value.trim(),
|
||||
ralphTrackerEnabled: document.getElementById('appSettingsRalphEnabled').checked,
|
||||
};
|
||||
|
||||
// Save to localStorage
|
||||
@@ -1657,6 +1674,12 @@ class ClaudemanApp {
|
||||
this.closeAppSettings();
|
||||
}
|
||||
|
||||
// Get the global Ralph tracker enabled setting
|
||||
isRalphTrackerEnabledByDefault() {
|
||||
const settings = this.loadAppSettingsFromStorage();
|
||||
return settings.ralphTrackerEnabled ?? false;
|
||||
}
|
||||
|
||||
loadAppSettingsFromStorage() {
|
||||
try {
|
||||
const saved = localStorage.getItem('claudeman-app-settings');
|
||||
|
||||
@@ -376,6 +376,15 @@
|
||||
|
||||
<!-- Ralph Wiggum Tab Content -->
|
||||
<div class="modal-tab-content hidden" id="ralph-tab">
|
||||
<div class="form-row form-row-switch">
|
||||
<label>Enable Tracker</label>
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="modalRalphEnabled">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
<span class="form-hint">Enable Ralph Wiggum tracker for this session (auto-detects loops and todos)</span>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label>Completion Phrase</label>
|
||||
<input type="text" id="modalRalphPhrase" placeholder="e.g., COMPLETE, DONE, FINISHED">
|
||||
@@ -457,6 +466,14 @@
|
||||
<input type="text" id="appSettingsDefaultDir" placeholder="~/projects">
|
||||
<span class="form-hint">Default directory for new sessions.</span>
|
||||
</div>
|
||||
<div class="form-row form-row-switch">
|
||||
<label>Enable Ralph Wiggum Tracker</label>
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="appSettingsRalphEnabled">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
<span class="form-hint">Enable tracker by default for new sessions. When disabled, tracker auto-enables when Ralph patterns are detected.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button class="btn-toolbar" onclick="app.closeAppSettings()">Cancel</button>
|
||||
|
||||
@@ -1466,6 +1466,77 @@ body {
|
||||
margin-top: 0.35rem;
|
||||
}
|
||||
|
||||
/* Switch toggle for form rows */
|
||||
.form-row-switch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.form-row-switch > label:first-child {
|
||||
margin-bottom: 0;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.form-row-switch .form-hint {
|
||||
flex-basis: 100%;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 36px;
|
||||
height: 20px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.switch .slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: var(--bg-input);
|
||||
border: 1px solid var(--border);
|
||||
transition: 0.2s;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.switch .slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
left: 2px;
|
||||
bottom: 2px;
|
||||
background-color: var(--text-muted);
|
||||
transition: 0.2s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.switch input:checked + .slider {
|
||||
background-color: var(--accent);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.switch input:checked + .slider:before {
|
||||
transform: translateX(16px);
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.switch input:focus + .slider {
|
||||
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
+12
-1
@@ -245,7 +245,8 @@ export class WebServer extends EventEmitter {
|
||||
// Configure inner loop (Ralph Wiggum) settings
|
||||
this.app.post('/api/sessions/:id/inner-config', async (req) => {
|
||||
const { id } = req.params as { id: string };
|
||||
const { completionPhrase, maxIterations, maxTodos, todoExpirationMinutes } = req.body as {
|
||||
const { enabled, completionPhrase, maxIterations, maxTodos, todoExpirationMinutes } = req.body as {
|
||||
enabled?: boolean;
|
||||
completionPhrase?: string;
|
||||
maxIterations?: number;
|
||||
maxTodos?: number;
|
||||
@@ -257,6 +258,15 @@ export class WebServer extends EventEmitter {
|
||||
return { success: false, error: 'Session not found' };
|
||||
}
|
||||
|
||||
// Enable/disable the tracker
|
||||
if (enabled !== undefined) {
|
||||
if (enabled) {
|
||||
session.innerLoopTracker.enable();
|
||||
} else {
|
||||
session.innerLoopTracker.disable();
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the inner loop tracker
|
||||
if (completionPhrase !== undefined) {
|
||||
// Start loop with completion phrase to set it up for watching
|
||||
@@ -271,6 +281,7 @@ export class WebServer extends EventEmitter {
|
||||
|
||||
// Store additional config on session for reference
|
||||
(session as any).innerConfig = {
|
||||
enabled: enabled ?? session.innerLoopTracker.enabled,
|
||||
completionPhrase: completionPhrase || '',
|
||||
maxIterations: maxIterations || 0,
|
||||
maxTodos: maxTodos || 50,
|
||||
|
||||
Reference in New Issue
Block a user