mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 16:59:43 +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
|
// Create multiple sessions with unique w-numbers
|
||||||
for (let i = 0; i < tabCount; i++) {
|
for (let i = 0; i < tabCount; i++) {
|
||||||
const sessionName = `w${startNumber + i}-${caseName}`;
|
const sessionName = `w${startNumber + i}-${caseName}`;
|
||||||
@@ -905,6 +908,15 @@ class ClaudemanApp {
|
|||||||
const createData = await createRes.json();
|
const createData = await createRes.json();
|
||||||
if (!createData.success) throw new Error(createData.error);
|
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
|
// Start interactive mode
|
||||||
await fetch(`/api/sessions/${createData.session.id}/interactive`, {
|
await fetch(`/api/sessions/${createData.session.id}/interactive`, {
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
@@ -1347,6 +1359,7 @@ class ClaudemanApp {
|
|||||||
// Populate Ralph Wiggum form with current session values
|
// Populate Ralph Wiggum form with current session values
|
||||||
const innerState = this.innerStates.get(sessionId);
|
const innerState = this.innerStates.get(sessionId);
|
||||||
this.populateRalphForm({
|
this.populateRalphForm({
|
||||||
|
enabled: innerState?.loop?.enabled ?? session.innerConfig?.enabled ?? false,
|
||||||
completionPhrase: innerState?.loop?.completionPhrase || session.innerLoop?.completionPhrase || '',
|
completionPhrase: innerState?.loop?.completionPhrase || session.innerLoop?.completionPhrase || '',
|
||||||
maxIterations: innerState?.loop?.maxIterations || session.innerLoop?.maxIterations || 0,
|
maxIterations: innerState?.loop?.maxIterations || session.innerLoop?.maxIterations || 0,
|
||||||
maxTodos: session.innerConfig?.maxTodos || 50,
|
maxTodos: session.innerConfig?.maxTodos || 50,
|
||||||
@@ -1529,6 +1542,7 @@ class ClaudemanApp {
|
|||||||
|
|
||||||
getRalphConfig() {
|
getRalphConfig() {
|
||||||
return {
|
return {
|
||||||
|
enabled: document.getElementById('modalRalphEnabled').checked,
|
||||||
completionPhrase: document.getElementById('modalRalphPhrase').value.trim(),
|
completionPhrase: document.getElementById('modalRalphPhrase').value.trim(),
|
||||||
maxIterations: parseInt(document.getElementById('modalRalphMaxIterations').value) || 0,
|
maxIterations: parseInt(document.getElementById('modalRalphMaxIterations').value) || 0,
|
||||||
maxTodos: parseInt(document.getElementById('modalRalphMaxTodos').value) || 50,
|
maxTodos: parseInt(document.getElementById('modalRalphMaxTodos').value) || 50,
|
||||||
@@ -1537,6 +1551,7 @@ class ClaudemanApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
populateRalphForm(config) {
|
populateRalphForm(config) {
|
||||||
|
document.getElementById('modalRalphEnabled').checked = config?.enabled ?? false;
|
||||||
document.getElementById('modalRalphPhrase').value = config?.completionPhrase || '';
|
document.getElementById('modalRalphPhrase').value = config?.completionPhrase || '';
|
||||||
document.getElementById('modalRalphMaxIterations').value = config?.maxIterations || 0;
|
document.getElementById('modalRalphMaxIterations').value = config?.maxIterations || 0;
|
||||||
document.getElementById('modalRalphMaxTodos').value = config?.maxTodos || 50;
|
document.getElementById('modalRalphMaxTodos').value = config?.maxTodos || 50;
|
||||||
@@ -1625,6 +1640,7 @@ class ClaudemanApp {
|
|||||||
const settings = this.loadAppSettingsFromStorage();
|
const settings = this.loadAppSettingsFromStorage();
|
||||||
document.getElementById('appSettingsClaudeMdPath').value = settings.defaultClaudeMdPath || '';
|
document.getElementById('appSettingsClaudeMdPath').value = settings.defaultClaudeMdPath || '';
|
||||||
document.getElementById('appSettingsDefaultDir').value = settings.defaultWorkingDir || '';
|
document.getElementById('appSettingsDefaultDir').value = settings.defaultWorkingDir || '';
|
||||||
|
document.getElementById('appSettingsRalphEnabled').checked = settings.ralphTrackerEnabled ?? false;
|
||||||
document.getElementById('appSettingsModal').classList.add('active');
|
document.getElementById('appSettingsModal').classList.add('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1636,6 +1652,7 @@ class ClaudemanApp {
|
|||||||
const settings = {
|
const settings = {
|
||||||
defaultClaudeMdPath: document.getElementById('appSettingsClaudeMdPath').value.trim(),
|
defaultClaudeMdPath: document.getElementById('appSettingsClaudeMdPath').value.trim(),
|
||||||
defaultWorkingDir: document.getElementById('appSettingsDefaultDir').value.trim(),
|
defaultWorkingDir: document.getElementById('appSettingsDefaultDir').value.trim(),
|
||||||
|
ralphTrackerEnabled: document.getElementById('appSettingsRalphEnabled').checked,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Save to localStorage
|
// Save to localStorage
|
||||||
@@ -1657,6 +1674,12 @@ class ClaudemanApp {
|
|||||||
this.closeAppSettings();
|
this.closeAppSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the global Ralph tracker enabled setting
|
||||||
|
isRalphTrackerEnabledByDefault() {
|
||||||
|
const settings = this.loadAppSettingsFromStorage();
|
||||||
|
return settings.ralphTrackerEnabled ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
loadAppSettingsFromStorage() {
|
loadAppSettingsFromStorage() {
|
||||||
try {
|
try {
|
||||||
const saved = localStorage.getItem('claudeman-app-settings');
|
const saved = localStorage.getItem('claudeman-app-settings');
|
||||||
|
|||||||
@@ -376,6 +376,15 @@
|
|||||||
|
|
||||||
<!-- Ralph Wiggum Tab Content -->
|
<!-- Ralph Wiggum Tab Content -->
|
||||||
<div class="modal-tab-content hidden" id="ralph-tab">
|
<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">
|
<div class="form-row">
|
||||||
<label>Completion Phrase</label>
|
<label>Completion Phrase</label>
|
||||||
<input type="text" id="modalRalphPhrase" placeholder="e.g., COMPLETE, DONE, FINISHED">
|
<input type="text" id="modalRalphPhrase" placeholder="e.g., COMPLETE, DONE, FINISHED">
|
||||||
@@ -457,6 +466,14 @@
|
|||||||
<input type="text" id="appSettingsDefaultDir" placeholder="~/projects">
|
<input type="text" id="appSettingsDefaultDir" placeholder="~/projects">
|
||||||
<span class="form-hint">Default directory for new sessions.</span>
|
<span class="form-hint">Default directory for new sessions.</span>
|
||||||
</div>
|
</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>
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button class="btn-toolbar" onclick="app.closeAppSettings()">Cancel</button>
|
<button class="btn-toolbar" onclick="app.closeAppSettings()">Cancel</button>
|
||||||
|
|||||||
@@ -1466,6 +1466,77 @@ body {
|
|||||||
margin-top: 0.35rem;
|
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 {
|
.form-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|||||||
+12
-1
@@ -245,7 +245,8 @@ export class WebServer extends EventEmitter {
|
|||||||
// Configure inner loop (Ralph Wiggum) settings
|
// Configure inner loop (Ralph Wiggum) settings
|
||||||
this.app.post('/api/sessions/:id/inner-config', async (req) => {
|
this.app.post('/api/sessions/:id/inner-config', async (req) => {
|
||||||
const { id } = req.params as { id: string };
|
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;
|
completionPhrase?: string;
|
||||||
maxIterations?: number;
|
maxIterations?: number;
|
||||||
maxTodos?: number;
|
maxTodos?: number;
|
||||||
@@ -257,6 +258,15 @@ export class WebServer extends EventEmitter {
|
|||||||
return { success: false, error: 'Session not found' };
|
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
|
// Configure the inner loop tracker
|
||||||
if (completionPhrase !== undefined) {
|
if (completionPhrase !== undefined) {
|
||||||
// Start loop with completion phrase to set it up for watching
|
// 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
|
// Store additional config on session for reference
|
||||||
(session as any).innerConfig = {
|
(session as any).innerConfig = {
|
||||||
|
enabled: enabled ?? session.innerLoopTracker.enabled,
|
||||||
completionPhrase: completionPhrase || '',
|
completionPhrase: completionPhrase || '',
|
||||||
maxIterations: maxIterations || 0,
|
maxIterations: maxIterations || 0,
|
||||||
maxTodos: maxTodos || 50,
|
maxTodos: maxTodos || 50,
|
||||||
|
|||||||
Reference in New Issue
Block a user