mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-02 13:39:41 +02:00
feat(ui): make Ralph Wiggum tracker panel detachable
Add ability to detach the Ralph Wiggum tracker panel as a floating window that can be dragged around the screen. Changes: - Add detach button to the Ralph panel header - Restructure HTML to separate clickable content from controls - Add CSS styles for detached state (floating, draggable, resizable) - Add toggleRalphDetach() and setupRalphDrag() JavaScript methods - Panel auto-expands when detached for better visibility Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1910,6 +1910,95 @@ class ClaudemanApp {
|
||||
this.renderInnerStatePanel();
|
||||
}
|
||||
|
||||
toggleRalphDetach() {
|
||||
const panel = this.$('innerStatePanel');
|
||||
const detachBtn = this.$('ralphDetachBtn');
|
||||
|
||||
if (!panel) return;
|
||||
|
||||
if (panel.classList.contains('detached')) {
|
||||
// Re-attach to original position
|
||||
panel.classList.remove('detached');
|
||||
panel.style.top = '';
|
||||
panel.style.left = '';
|
||||
panel.style.width = '';
|
||||
panel.style.height = '';
|
||||
if (detachBtn) {
|
||||
detachBtn.innerHTML = '↗'; // Detach icon
|
||||
detachBtn.title = 'Detach panel';
|
||||
}
|
||||
} else {
|
||||
// Detach as floating window
|
||||
panel.classList.add('detached');
|
||||
// Expand when detaching for better visibility
|
||||
this.innerStatePanelCollapsed = false;
|
||||
panel.classList.remove('collapsed');
|
||||
if (detachBtn) {
|
||||
detachBtn.innerHTML = '↙'; // Attach icon
|
||||
detachBtn.title = 'Attach panel';
|
||||
}
|
||||
// Setup drag functionality
|
||||
this.setupRalphDrag();
|
||||
}
|
||||
this.renderInnerStatePanel();
|
||||
}
|
||||
|
||||
setupRalphDrag() {
|
||||
const panel = this.$('innerStatePanel');
|
||||
const header = this.$('ralphSummary');
|
||||
|
||||
if (!panel || !header) return;
|
||||
|
||||
let isDragging = false;
|
||||
let startX, startY, startLeft, startTop;
|
||||
|
||||
const onMouseDown = (e) => {
|
||||
// Only drag from header, not from buttons or toggle
|
||||
if (e.target.closest('button') || e.target.closest('.ralph-toggle')) return;
|
||||
if (!panel.classList.contains('detached')) return;
|
||||
|
||||
isDragging = true;
|
||||
startX = e.clientX;
|
||||
startY = e.clientY;
|
||||
const rect = panel.getBoundingClientRect();
|
||||
startLeft = rect.left;
|
||||
startTop = rect.top;
|
||||
|
||||
document.addEventListener('mousemove', onMouseMove);
|
||||
document.addEventListener('mouseup', onMouseUp);
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
const onMouseMove = (e) => {
|
||||
if (!isDragging) return;
|
||||
|
||||
const dx = e.clientX - startX;
|
||||
const dy = e.clientY - startY;
|
||||
|
||||
let newLeft = startLeft + dx;
|
||||
let newTop = startTop + dy;
|
||||
|
||||
// Keep within viewport bounds
|
||||
const rect = panel.getBoundingClientRect();
|
||||
newLeft = Math.max(0, Math.min(window.innerWidth - rect.width, newLeft));
|
||||
newTop = Math.max(0, Math.min(window.innerHeight - rect.height, newTop));
|
||||
|
||||
panel.style.left = newLeft + 'px';
|
||||
panel.style.top = newTop + 'px';
|
||||
};
|
||||
|
||||
const onMouseUp = () => {
|
||||
isDragging = false;
|
||||
document.removeEventListener('mousemove', onMouseMove);
|
||||
document.removeEventListener('mouseup', onMouseUp);
|
||||
};
|
||||
|
||||
// Remove existing listeners before adding new ones
|
||||
header.removeEventListener('mousedown', header._ralphDragHandler);
|
||||
header._ralphDragHandler = onMouseDown;
|
||||
header.addEventListener('mousedown', onMouseDown);
|
||||
}
|
||||
|
||||
renderInnerStatePanel() {
|
||||
const panel = this.$('innerStatePanel');
|
||||
const toggle = this.$('ralphToggle');
|
||||
|
||||
+25
-20
@@ -75,28 +75,33 @@
|
||||
<!-- Enhanced Ralph Wiggum Loop Panel -->
|
||||
<div class="ralph-panel collapsed" id="innerStatePanel" style="display: none;">
|
||||
<!-- Collapsed Summary Bar -->
|
||||
<div class="ralph-summary" onclick="app.toggleInnerStatePanel()">
|
||||
<div class="ralph-ring-mini" id="ralphRingMini">
|
||||
<svg viewBox="0 0 36 36" class="ralph-ring-svg">
|
||||
<circle class="ralph-ring-bg" cx="18" cy="18" r="15.9" />
|
||||
<circle class="ralph-ring-progress" cx="18" cy="18" r="15.9" id="ralphRingMiniProgress" />
|
||||
</svg>
|
||||
<span class="ralph-ring-text" id="ralphRingMiniText">0%</span>
|
||||
<div class="ralph-summary" id="ralphSummary">
|
||||
<div class="ralph-summary-content" onclick="app.toggleInnerStatePanel()">
|
||||
<div class="ralph-ring-mini" id="ralphRingMini">
|
||||
<svg viewBox="0 0 36 36" class="ralph-ring-svg">
|
||||
<circle class="ralph-ring-bg" cx="18" cy="18" r="15.9" />
|
||||
<circle class="ralph-ring-progress" cx="18" cy="18" r="15.9" id="ralphRingMiniProgress" />
|
||||
</svg>
|
||||
<span class="ralph-ring-text" id="ralphRingMiniText">0%</span>
|
||||
</div>
|
||||
<div class="ralph-summary-info">
|
||||
<span class="ralph-title">Ralph Wiggum Tracker</span>
|
||||
<span class="ralph-status-badge" id="ralphStatusBadge">
|
||||
<span class="ralph-status-dot"></span>
|
||||
<span class="ralph-status-text">Idle</span>
|
||||
</span>
|
||||
<span class="ralph-phrase-preview" id="ralphPhrasePreview"></span>
|
||||
</div>
|
||||
<div class="ralph-summary-stats">
|
||||
<span class="ralph-stat"><span class="ralph-stat-icon">⏱</span><span id="ralphStatTime">0m</span></span>
|
||||
<span class="ralph-stat"><span class="ralph-stat-icon">↻</span><span id="ralphStatCycles">0</span></span>
|
||||
<span class="ralph-stat"><span class="ralph-stat-icon">✓</span><span id="ralphStatTasks">0/0</span></span>
|
||||
</div>
|
||||
<span class="ralph-toggle" id="ralphToggle">▶</span>
|
||||
</div>
|
||||
<div class="ralph-summary-info">
|
||||
<span class="ralph-title">Ralph Wiggum Tracker</span>
|
||||
<span class="ralph-status-badge" id="ralphStatusBadge">
|
||||
<span class="ralph-status-dot"></span>
|
||||
<span class="ralph-status-text">Idle</span>
|
||||
</span>
|
||||
<span class="ralph-phrase-preview" id="ralphPhrasePreview"></span>
|
||||
<div class="ralph-controls">
|
||||
<button class="btn-icon-sm" onclick="event.stopPropagation(); app.toggleRalphDetach()" title="Detach/Attach panel" id="ralphDetachBtn">↗</button>
|
||||
</div>
|
||||
<div class="ralph-summary-stats">
|
||||
<span class="ralph-stat"><span class="ralph-stat-icon">⏱</span><span id="ralphStatTime">0m</span></span>
|
||||
<span class="ralph-stat"><span class="ralph-stat-icon">↻</span><span id="ralphStatCycles">0</span></span>
|
||||
<span class="ralph-stat"><span class="ralph-stat-icon">✓</span><span id="ralphStatTasks">0/0</span></span>
|
||||
</div>
|
||||
<span class="ralph-toggle" id="ralphToggle">▶</span>
|
||||
</div>
|
||||
|
||||
<!-- Expanded Detail View -->
|
||||
|
||||
+70
-15
@@ -457,21 +457,6 @@ body {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Collapsed Summary Bar */
|
||||
.ralph-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.ralph-summary:hover {
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
|
||||
/* Mini Progress Ring (in summary) */
|
||||
.ralph-ring-mini {
|
||||
position: relative;
|
||||
@@ -908,6 +893,76 @@ body {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
/* Ralph panel summary layout adjustments */
|
||||
.ralph-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
user-select: none;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.ralph-summary-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ralph-summary-content:hover {
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
|
||||
.ralph-controls {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Detached Ralph Panel */
|
||||
.ralph-panel.detached {
|
||||
position: fixed;
|
||||
top: 100px;
|
||||
left: 100px;
|
||||
width: 400px;
|
||||
min-height: 200px;
|
||||
max-height: 80vh;
|
||||
z-index: 1000;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
|
||||
background: var(--bg-card);
|
||||
overflow: auto;
|
||||
resize: both;
|
||||
}
|
||||
|
||||
.ralph-panel.detached .ralph-summary {
|
||||
cursor: move;
|
||||
background: linear-gradient(180deg, var(--bg-hover) 0%, var(--bg-card) 100%);
|
||||
border-bottom: 1px solid var(--border);
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
.ralph-panel.detached .ralph-summary-content {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.ralph-panel.detached .ralph-summary-content:hover {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.ralph-panel.detached .ralph-detail {
|
||||
max-height: none;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.ralph-panel.detached.collapsed {
|
||||
min-height: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Main - Terminal Area */
|
||||
.main {
|
||||
flex: 1;
|
||||
|
||||
Reference in New Issue
Block a user