mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 16:59:43 +02:00
feat(ui): add 20-tab support and Claude CLI settings
- Increase max tab count from 10 to 20 - Fix header to properly expand for multiple tab rows (up to 4 rows) - Add scrollbar for tabs area when many tabs exist - Add Claude CLI startup mode setting (skip permissions/normal/allowed tools) - Reorganize settings modal into sections (CLI, Paths, Features) - Fix case select dropdown to fit content width - Create optimization todos document for future work Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,149 @@
|
|||||||
|
# Claudeman Code Optimization TODO List
|
||||||
|
|
||||||
|
> Generated: 2026-01-21
|
||||||
|
> Purpose: Comprehensive optimization roadmap for future sessions
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document contains prioritized optimization tasks identified through codebase analysis.
|
||||||
|
Each item has specific file:line references and expected impact estimates.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## HIGH PRIORITY OPTIMIZATIONS
|
||||||
|
|
||||||
|
### 1. String Concatenation in Hot Paths
|
||||||
|
- **Files**: `src/session.ts:460, 677-686`, `src/respawn-controller.ts:469, 826`, `src/inner-loop-tracker.ts:408`
|
||||||
|
- **Issue**: String `+=` in terminal buffer accumulation creates GC pressure
|
||||||
|
- **Fix**: Use array-based accumulation with periodic joins
|
||||||
|
- **Impact**: 10-20% faster terminal streaming
|
||||||
|
|
||||||
|
### 2. Regex Pattern lastIndex Reset
|
||||||
|
- **Files**: `src/inner-loop-tracker.ts:464-479`, `src/screen-manager.ts:264`
|
||||||
|
- **Issue**: Global regex without lastIndex reset causes skip-every-other-match bug
|
||||||
|
- **Fix**: Always reset `pattern.lastIndex = 0` before use
|
||||||
|
- **Impact**: Correct pattern matching, eliminate intermittent bugs
|
||||||
|
|
||||||
|
### 3. Unbounded Buffer Growth
|
||||||
|
- **Files**: `src/respawn-controller.ts:268-269`, `src/inner-loop-tracker.ts:257-258`
|
||||||
|
- **Issue**: Buffers lack proper size limits or have loose limits
|
||||||
|
- **Fix**: Add hard limits with Ring Buffer pattern
|
||||||
|
- **Impact**: Bounded memory, prevent OOM in long sessions
|
||||||
|
|
||||||
|
### 4. Event Listener Management
|
||||||
|
- **Files**: `src/respawn-controller.ts:446-455`, `src/session.ts:481-501`, `src/web/server.ts:987-1084`
|
||||||
|
- **Issue**: Listeners created repeatedly without cleanup tracking
|
||||||
|
- **Fix**: Use debounce/throttle utilities, consolidate timeout management
|
||||||
|
- **Impact**: Fewer memory leaks, reduced GC sweeps
|
||||||
|
|
||||||
|
### 5. Task Lookup Optimization
|
||||||
|
- **Files**: `src/task-tracker.ts:214-220, 231-234`
|
||||||
|
- **Issue**: O(n) sequential iteration for task lookups
|
||||||
|
- **Fix**: Add secondary indices `runningTasksByType: Map<string, Set<string>>`
|
||||||
|
- **Impact**: O(1) lookups, faster with 1000+ tasks
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MEDIUM PRIORITY OPTIMIZATIONS
|
||||||
|
|
||||||
|
### 6. Pattern Test Result Caching
|
||||||
|
- **Files**: `src/inner-loop-tracker.ts:529-543`
|
||||||
|
- **Issue**: Multiple pattern tests on same data without caching
|
||||||
|
- **Fix**: Use `.exec()` once and reuse result
|
||||||
|
- **Impact**: 2-3x faster todo detection
|
||||||
|
|
||||||
|
### 7. Dynamic Terminal Batching
|
||||||
|
- **Files**: `src/web/server.ts:46, 1350-1360`
|
||||||
|
- **Issue**: 16ms flush interval is too long for interactive sessions
|
||||||
|
- **Fix**: Flush immediately if batch > 1KB OR timeout 16ms
|
||||||
|
- **Impact**: Snappier UI feel
|
||||||
|
|
||||||
|
### 8. Buffer Pagination
|
||||||
|
- **Files**: `src/session.ts:314-365`, `src/web/server.ts:219-224`
|
||||||
|
- **Issue**: Full buffer sent on every state update
|
||||||
|
- **Fix**: Add `/api/sessions/:id/history?offset=X&limit=Y`
|
||||||
|
- **Impact**: 10-100x faster reconnects
|
||||||
|
|
||||||
|
### 9. Remove Unused Regex Patterns
|
||||||
|
- **Files**: `src/respawn-controller.ts:52-59`
|
||||||
|
- **Issue**: ANSI_ESCAPE_PATTERN, WHITESPACE_PATTERN marked @deprecated but still compiled
|
||||||
|
- **Fix**: Remove unused patterns
|
||||||
|
- **Impact**: Cleaner code, slightly faster startup
|
||||||
|
|
||||||
|
### 10. State Serialization
|
||||||
|
- **Files**: `src/state-store.ts:80, 196`, `src/web/server.ts:1343`
|
||||||
|
- **Issue**: Full JSON.stringify on every save
|
||||||
|
- **Fix**: Incremental or selective serialization
|
||||||
|
- **Impact**: 5-10x faster state saves
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## LOW PRIORITY OPTIMIZATIONS
|
||||||
|
|
||||||
|
### 11. Error Handling Consistency
|
||||||
|
- **Files**: `src/screen-manager.ts:101-104`, `src/session.ts:726-729`, `src/web/server.ts:838-845`
|
||||||
|
- **Issue**: Mix of try-catch and error codes
|
||||||
|
- **Fix**: Create ErrorRegistry, standardize responses
|
||||||
|
- **Impact**: Better debugging
|
||||||
|
|
||||||
|
### 12. TypeScript Strict Typing
|
||||||
|
- **Files**: `src/task-tracker.ts:179`, `src/web/server.ts:138, 174`
|
||||||
|
- **Issue**: Some implicit `any` types
|
||||||
|
- **Fix**: Strong typing for all messages and payloads
|
||||||
|
- **Impact**: Better IDE support, fewer runtime errors
|
||||||
|
|
||||||
|
### 13. Dead Code Removal
|
||||||
|
- **Files**: `src/task.ts`, `src/task-queue.ts`, `src/ralph-loop.ts`
|
||||||
|
- **Issue**: Potentially unused files
|
||||||
|
- **Fix**: Verify imports, remove unused
|
||||||
|
- **Impact**: Cleaner codebase
|
||||||
|
|
||||||
|
### 14. Console Logging
|
||||||
|
- **Files**: Throughout `src/session.ts`, `src/respawn-controller.ts`, `src/screen-manager.ts`
|
||||||
|
- **Issue**: Debug logs not disabled in production
|
||||||
|
- **Fix**: Use logger abstraction with levels
|
||||||
|
- **Impact**: Cleaner logs
|
||||||
|
|
||||||
|
### 15. Magic Numbers
|
||||||
|
- **Files**: `src/session.ts:397, 428`, `src/respawn-controller.ts:614, 619`
|
||||||
|
- **Issue**: Hardcoded timeout values
|
||||||
|
- **Fix**: Extract to named constants
|
||||||
|
- **Impact**: Easier tuning
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Phases
|
||||||
|
|
||||||
|
### Phase 1 (Critical) - Items #1-5
|
||||||
|
- Time: 8-12 hours
|
||||||
|
- Expected Gain: 30% performance
|
||||||
|
|
||||||
|
### Phase 2 (Important) - Items #6-10
|
||||||
|
- Time: 6-8 hours
|
||||||
|
- Expected Gain: 15% performance
|
||||||
|
|
||||||
|
### Phase 3 (Polish) - Items #11-15
|
||||||
|
- Time: 4-6 hours
|
||||||
|
- Expected Gain: 5% + code quality
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Progress Tracking
|
||||||
|
|
||||||
|
| Item | Status | Completed By | Notes |
|
||||||
|
|------|--------|--------------|-------|
|
||||||
|
| 1 | ☐ Pending | | |
|
||||||
|
| 2 | ☐ Pending | | |
|
||||||
|
| 3 | ☐ Pending | | |
|
||||||
|
| 4 | ☐ Pending | | |
|
||||||
|
| 5 | ☐ Pending | | |
|
||||||
|
| 6 | ☐ Pending | | |
|
||||||
|
| 7 | ☐ Pending | | |
|
||||||
|
| 8 | ☐ Pending | | |
|
||||||
|
| 9 | ☐ Pending | | |
|
||||||
|
| 10 | ☐ Pending | | |
|
||||||
|
| 11 | ☐ Pending | | |
|
||||||
|
| 12 | ☐ Pending | | |
|
||||||
|
| 13 | ☐ Pending | | |
|
||||||
|
| 14 | ☐ Pending | | |
|
||||||
|
| 15 | ☐ Pending | | |
|
||||||
+41
-43
@@ -283,12 +283,32 @@ class ClaudemanApp {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.eventSource.addEventListener('session:clearTerminal', (e) => {
|
this.eventSource.addEventListener('session:clearTerminal', async (e) => {
|
||||||
const data = JSON.parse(e.data);
|
const data = JSON.parse(e.data);
|
||||||
if (data.id === this.activeSessionId) {
|
if (data.id === this.activeSessionId) {
|
||||||
// Clear terminal after screen attach to remove initialization blank space
|
// Fetch buffer, clear terminal, write buffer, resize (no Ctrl+L needed)
|
||||||
this.terminal.clear();
|
try {
|
||||||
this.terminal.reset();
|
const res = await fetch(`/api/sessions/${data.id}/terminal`);
|
||||||
|
const termData = await res.json();
|
||||||
|
|
||||||
|
this.terminal.clear();
|
||||||
|
this.terminal.reset();
|
||||||
|
if (termData.terminalBuffer) {
|
||||||
|
this.terminal.write(termData.terminalBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send resize to ensure proper dimensions
|
||||||
|
const dims = this.fitAddon.proposeDimensions();
|
||||||
|
if (dims) {
|
||||||
|
await fetch(`/api/sessions/${data.id}/resize`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ cols: dims.cols, rows: dims.rows })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('clearTerminal refresh failed:', err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -669,15 +689,6 @@ class ClaudemanApp {
|
|||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ cols: dims.cols, rows: dims.rows })
|
body: JSON.stringify({ cols: dims.cols, rows: dims.rows })
|
||||||
});
|
});
|
||||||
// Send Ctrl+L to fix squished display after tab switch
|
|
||||||
const session = this.sessions.get(sessionId);
|
|
||||||
if (session && session.mode !== 'shell') {
|
|
||||||
fetch(`/api/sessions/${sessionId}/input`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ input: '\x0c' })
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update respawn banner
|
// Update respawn banner
|
||||||
@@ -858,7 +869,7 @@ class ClaudemanApp {
|
|||||||
incrementTabCount() {
|
incrementTabCount() {
|
||||||
const input = document.getElementById('tabCount');
|
const input = document.getElementById('tabCount');
|
||||||
const current = parseInt(input.value) || 1;
|
const current = parseInt(input.value) || 1;
|
||||||
input.value = Math.min(10, current + 1);
|
input.value = Math.min(20, current + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
decrementTabCount() {
|
decrementTabCount() {
|
||||||
@@ -869,7 +880,7 @@ class ClaudemanApp {
|
|||||||
|
|
||||||
async runClaude() {
|
async runClaude() {
|
||||||
const caseName = document.getElementById('quickStartCase').value || 'testcase';
|
const caseName = document.getElementById('quickStartCase').value || 'testcase';
|
||||||
const tabCount = Math.min(10, Math.max(1, parseInt(document.getElementById('tabCount').value) || 1));
|
const tabCount = Math.min(20, Math.max(1, parseInt(document.getElementById('tabCount').value) || 1));
|
||||||
|
|
||||||
this.terminal.clear();
|
this.terminal.clear();
|
||||||
this.terminal.writeln(`\x1b[1;32m Starting ${tabCount} Claude session(s) in ${caseName}...\x1b[0m`);
|
this.terminal.writeln(`\x1b[1;32m Starting ${tabCount} Claude session(s) in ${caseName}...\x1b[0m`);
|
||||||
@@ -947,35 +958,9 @@ class ClaudemanApp {
|
|||||||
this.terminal.writeln(`\x1b[90m Created session ${i}/${tabCount}: ${sessionName}\x1b[0m`);
|
this.terminal.writeln(`\x1b[90m Created session ${i}/${tabCount}: ${sessionName}\x1b[0m`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-switch to the new session
|
// Auto-switch to the new session using selectSession (does proper refresh)
|
||||||
if (firstSessionId) {
|
if (firstSessionId) {
|
||||||
// IMPORTANT: Clear terminal BEFORE setting activeSessionId
|
await this.selectSession(firstSessionId);
|
||||||
// This prevents SSE events from writing during the transition
|
|
||||||
this.terminal.clear();
|
|
||||||
this.terminal.reset();
|
|
||||||
|
|
||||||
// Small delay to ensure terminal is fully reset before SSE events can write
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 50));
|
|
||||||
|
|
||||||
// NOW set the active session so SSE events start populating
|
|
||||||
this.activeSessionId = firstSessionId;
|
|
||||||
this.renderSessionTabs();
|
|
||||||
this.renderInnerStatePanel(); // Render Ralph panel if enabled globally
|
|
||||||
|
|
||||||
// Send resize to the new session
|
|
||||||
const dims = this.fitAddon.proposeDimensions();
|
|
||||||
if (dims) {
|
|
||||||
await fetch(`/api/sessions/${firstSessionId}/resize`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ cols: dims.cols, rows: dims.rows })
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Mark this session as needing Ctrl+L fix once Claude is running
|
|
||||||
this.pendingCtrlL = this.pendingCtrlL || new Set();
|
|
||||||
this.pendingCtrlL.add(firstSessionId);
|
|
||||||
console.log('[DEBUG] Marked session for pending Ctrl+L:', firstSessionId);
|
|
||||||
|
|
||||||
this.loadQuickStartCases();
|
this.loadQuickStartCases();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1664,6 +1649,16 @@ class ClaudemanApp {
|
|||||||
document.getElementById('appSettingsShowSystemStats').checked = settings.showSystemStats ?? true;
|
document.getElementById('appSettingsShowSystemStats').checked = settings.showSystemStats ?? true;
|
||||||
document.getElementById('appSettingsShowTokenCount').checked = settings.showTokenCount ?? true;
|
document.getElementById('appSettingsShowTokenCount').checked = settings.showTokenCount ?? true;
|
||||||
document.getElementById('appSettingsShowMonitor').checked = settings.showMonitor ?? true;
|
document.getElementById('appSettingsShowMonitor').checked = settings.showMonitor ?? true;
|
||||||
|
// Claude CLI settings
|
||||||
|
const claudeModeSelect = document.getElementById('appSettingsClaudeMode');
|
||||||
|
const allowedToolsRow = document.getElementById('allowedToolsRow');
|
||||||
|
claudeModeSelect.value = settings.claudeMode || 'dangerously-skip-permissions';
|
||||||
|
document.getElementById('appSettingsAllowedTools').value = settings.allowedTools || '';
|
||||||
|
allowedToolsRow.style.display = claudeModeSelect.value === 'allowedTools' ? '' : 'none';
|
||||||
|
// Toggle allowed tools row visibility based on mode selection
|
||||||
|
claudeModeSelect.onchange = () => {
|
||||||
|
allowedToolsRow.style.display = claudeModeSelect.value === 'allowedTools' ? '' : 'none';
|
||||||
|
};
|
||||||
document.getElementById('appSettingsModal').classList.add('active');
|
document.getElementById('appSettingsModal').classList.add('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1681,6 +1676,9 @@ class ClaudemanApp {
|
|||||||
showSystemStats: document.getElementById('appSettingsShowSystemStats').checked,
|
showSystemStats: document.getElementById('appSettingsShowSystemStats').checked,
|
||||||
showTokenCount: document.getElementById('appSettingsShowTokenCount').checked,
|
showTokenCount: document.getElementById('appSettingsShowTokenCount').checked,
|
||||||
showMonitor: document.getElementById('appSettingsShowMonitor').checked,
|
showMonitor: document.getElementById('appSettingsShowMonitor').checked,
|
||||||
|
// Claude CLI settings
|
||||||
|
claudeMode: document.getElementById('appSettingsClaudeMode').value,
|
||||||
|
allowedTools: document.getElementById('appSettingsAllowedTools').value.trim(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Save to localStorage
|
// Save to localStorage
|
||||||
|
|||||||
@@ -167,7 +167,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<div class="tab-count-group" title="Number of tabs to open (1-10)">
|
<div class="tab-count-group" title="Number of tabs to open (1-10)">
|
||||||
<button class="tab-count-btn" onclick="app.decrementTabCount()">−</button>
|
<button class="tab-count-btn" onclick="app.decrementTabCount()">−</button>
|
||||||
<input type="number" id="tabCount" class="tab-count-input" value="1" min="1" max="10" readonly>
|
<input type="number" id="tabCount" class="tab-count-input" value="1" min="1" max="20" readonly>
|
||||||
<button class="tab-count-btn" onclick="app.incrementTabCount()">+</button>
|
<button class="tab-count-btn" onclick="app.incrementTabCount()">+</button>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn-toolbar btn-shell" onclick="app.runShell()" title="Run Shell">
|
<button class="btn-toolbar btn-shell" onclick="app.runShell()" title="Run Shell">
|
||||||
@@ -479,7 +479,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-section">
|
<div class="settings-section">
|
||||||
<div class="settings-section-title">General</div>
|
<div class="settings-section-title">Claude CLI</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Startup Mode</label>
|
||||||
|
<select id="appSettingsClaudeMode" class="form-select">
|
||||||
|
<option value="dangerously-skip-permissions">Skip Permissions (default)</option>
|
||||||
|
<option value="normal">Normal (with prompts)</option>
|
||||||
|
<option value="allowedTools">Allowed Tools Only</option>
|
||||||
|
</select>
|
||||||
|
<span class="form-hint">How Claude CLI is started in screen sessions</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-row" id="allowedToolsRow" style="display: none;">
|
||||||
|
<label>Allowed Tools</label>
|
||||||
|
<input type="text" id="appSettingsAllowedTools" placeholder="Read,Glob,Grep,Bash">
|
||||||
|
<span class="form-hint">Comma-separated list of tools to allow (e.g., Read,Glob,Grep,Bash)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="settings-section">
|
||||||
|
<div class="settings-section-title">Paths & Defaults</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label>Default CLAUDE.md Template Path</label>
|
<label>Default CLAUDE.md Template Path</label>
|
||||||
<input type="text" id="appSettingsClaudeMdPath" placeholder="/path/to/CLAUDE.md template">
|
<input type="text" id="appSettingsClaudeMdPath" placeholder="/path/to/CLAUDE.md template">
|
||||||
@@ -490,6 +507,9 @@
|
|||||||
<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>
|
||||||
|
<div class="settings-section">
|
||||||
|
<div class="settings-section-title">Features</div>
|
||||||
<div class="form-row form-row-switch">
|
<div class="form-row form-row-switch">
|
||||||
<label>Enable Ralph Wiggum Tracker</label>
|
<label>Enable Ralph Wiggum Tracker</label>
|
||||||
<label class="switch">
|
<label class="switch">
|
||||||
|
|||||||
+30
-10
@@ -42,9 +42,9 @@ body {
|
|||||||
/* Compact Header */
|
/* Compact Header */
|
||||||
.header {
|
.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
height: var(--header-height);
|
min-height: var(--header-height);
|
||||||
padding: 0 0.75rem;
|
padding: 0.35rem 0.75rem;
|
||||||
background: var(--bg-card);
|
background: var(--bg-card);
|
||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--border);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@@ -56,6 +56,8 @@ body {
|
|||||||
padding-right: 0.75rem;
|
padding-right: 0.75rem;
|
||||||
border-right: 1px solid var(--border);
|
border-right: 1px solid var(--border);
|
||||||
margin-right: 0.5rem;
|
margin-right: 0.5rem;
|
||||||
|
height: var(--header-height);
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
@@ -68,12 +70,30 @@ body {
|
|||||||
/* Session Tabs */
|
/* Session Tabs */
|
||||||
.session-tabs {
|
.session-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 2px;
|
gap: 4px 2px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-x: auto;
|
overflow-y: auto;
|
||||||
padding: 0 0.25rem;
|
max-height: 120px;
|
||||||
|
padding: 0.15rem 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-tabs::-webkit-scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-tabs::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-tabs::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--border);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-tabs::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--border-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.session-tabs::-webkit-scrollbar {
|
.session-tabs::-webkit-scrollbar {
|
||||||
@@ -1225,9 +1245,9 @@ body {
|
|||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: right 0.4rem center;
|
background-position: right 0.4rem center;
|
||||||
width: auto;
|
width: fit-content;
|
||||||
min-width: 60px;
|
min-width: unset;
|
||||||
max-width: 150px;
|
max-width: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-select:hover {
|
.toolbar-select:hover {
|
||||||
|
|||||||
Reference in New Issue
Block a user