claude-remote-cli 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/server/index.js +31 -0
- package/dist/server/sessions.js +9 -4
- package/package.json +1 -1
- package/public/app.js +191 -18
- package/public/index.html +12 -0
- package/public/style.css +30 -0
package/README.md
CHANGED
|
@@ -142,8 +142,10 @@ The PIN hash is stored in config under `pinHash`. To reset:
|
|
|
142
142
|
## Features
|
|
143
143
|
|
|
144
144
|
- **PIN-protected access** with rate limiting
|
|
145
|
-
- **
|
|
146
|
-
- **
|
|
145
|
+
- **Repo sessions** — open Claude directly in any repo root, with fresh or `--continue` mode (one session per repo)
|
|
146
|
+
- **Branch-aware worktrees** — create worktrees from new or existing branches with a type-to-search branch picker
|
|
147
|
+
- **Tabbed sidebar** — switch between Repos and Worktrees views with shared filters and item counts
|
|
148
|
+
- **Worktree isolation** — each worktree session runs in its own git worktree under `.worktrees/`
|
|
147
149
|
- **Resume sessions** — click inactive worktrees to reconnect with `--continue`
|
|
148
150
|
- **Persistent session names** — display names, branch names, and timestamps survive server restarts
|
|
149
151
|
- **Clipboard image paste** — paste screenshots directly into remote terminal sessions (macOS clipboard + xclip on Linux)
|
package/dist/server/index.js
CHANGED
|
@@ -440,6 +440,7 @@ async function main() {
|
|
|
440
440
|
}
|
|
441
441
|
const displayName = branchName || worktreeName;
|
|
442
442
|
const session = sessions.create({
|
|
443
|
+
type: 'worktree',
|
|
443
444
|
repoName: name,
|
|
444
445
|
repoPath: sessionRepoPath,
|
|
445
446
|
cwd,
|
|
@@ -460,6 +461,36 @@ async function main() {
|
|
|
460
461
|
}
|
|
461
462
|
res.status(201).json(session);
|
|
462
463
|
});
|
|
464
|
+
// POST /sessions/repo — start a session in the repo root (no worktree)
|
|
465
|
+
app.post('/sessions/repo', requireAuth, (req, res) => {
|
|
466
|
+
const { repoPath, repoName, continue: continueSession, claudeArgs } = req.body;
|
|
467
|
+
if (!repoPath) {
|
|
468
|
+
res.status(400).json({ error: 'repoPath is required' });
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
// One repo session at a time
|
|
472
|
+
const existing = sessions.findRepoSession(repoPath);
|
|
473
|
+
if (existing) {
|
|
474
|
+
res.status(409).json({ error: 'A session already exists for this repo', sessionId: existing.id });
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
const name = repoName || repoPath.split('/').filter(Boolean).pop() || 'session';
|
|
478
|
+
const baseArgs = [...(config.claudeArgs || []), ...(claudeArgs || [])];
|
|
479
|
+
const args = continueSession ? ['--continue', ...baseArgs] : [...baseArgs];
|
|
480
|
+
const roots = config.rootDirs || [];
|
|
481
|
+
const root = roots.find(function (r) { return repoPath.startsWith(r); }) || '';
|
|
482
|
+
const session = sessions.create({
|
|
483
|
+
type: 'repo',
|
|
484
|
+
repoName: name,
|
|
485
|
+
repoPath,
|
|
486
|
+
cwd: repoPath,
|
|
487
|
+
root,
|
|
488
|
+
displayName: name,
|
|
489
|
+
command: config.claudeCommand,
|
|
490
|
+
args,
|
|
491
|
+
});
|
|
492
|
+
res.status(201).json(session);
|
|
493
|
+
});
|
|
463
494
|
// DELETE /sessions/:id
|
|
464
495
|
app.delete('/sessions/:id', requireAuth, (req, res) => {
|
|
465
496
|
try {
|
package/dist/server/sessions.js
CHANGED
|
@@ -11,7 +11,7 @@ let idleChangeCallback = null;
|
|
|
11
11
|
function onIdleChange(cb) {
|
|
12
12
|
idleChangeCallback = cb;
|
|
13
13
|
}
|
|
14
|
-
function create({ repoName, repoPath, cwd, root, worktreeName, displayName, command, args = [], cols = 80, rows = 24, configPath }) {
|
|
14
|
+
function create({ type, repoName, repoPath, cwd, root, worktreeName, displayName, command, args = [], cols = 80, rows = 24, configPath }) {
|
|
15
15
|
const id = crypto.randomBytes(8).toString('hex');
|
|
16
16
|
const createdAt = new Date().toISOString();
|
|
17
17
|
// Strip CLAUDECODE env var to allow spawning claude inside a claude-managed server
|
|
@@ -30,6 +30,7 @@ function create({ repoName, repoPath, cwd, root, worktreeName, displayName, comm
|
|
|
30
30
|
const MAX_SCROLLBACK = 256 * 1024; // 256KB max
|
|
31
31
|
const session = {
|
|
32
32
|
id,
|
|
33
|
+
type: type || 'worktree',
|
|
33
34
|
root: root || '',
|
|
34
35
|
repoName: repoName || '',
|
|
35
36
|
repoPath,
|
|
@@ -96,15 +97,16 @@ function create({ repoName, repoPath, cwd, root, worktreeName, displayName, comm
|
|
|
96
97
|
const tmpDir = path.join(os.tmpdir(), 'claude-remote-cli', id);
|
|
97
98
|
fs.rm(tmpDir, { recursive: true, force: true }, () => { });
|
|
98
99
|
});
|
|
99
|
-
return { id, root: session.root, repoName: session.repoName, repoPath, worktreeName: session.worktreeName, displayName: session.displayName, pid: ptyProcess.pid, createdAt, lastActivity: createdAt, idle: false };
|
|
100
|
+
return { id, type: session.type, root: session.root, repoName: session.repoName, repoPath, worktreeName: session.worktreeName, displayName: session.displayName, pid: ptyProcess.pid, createdAt, lastActivity: createdAt, idle: false };
|
|
100
101
|
}
|
|
101
102
|
function get(id) {
|
|
102
103
|
return sessions.get(id);
|
|
103
104
|
}
|
|
104
105
|
function list() {
|
|
105
106
|
return Array.from(sessions.values())
|
|
106
|
-
.map(({ id, root, repoName, repoPath, worktreeName, displayName, createdAt, lastActivity, idle }) => ({
|
|
107
|
+
.map(({ id, type, root, repoName, repoPath, worktreeName, displayName, createdAt, lastActivity, idle }) => ({
|
|
107
108
|
id,
|
|
109
|
+
type,
|
|
108
110
|
root,
|
|
109
111
|
repoName,
|
|
110
112
|
repoPath,
|
|
@@ -145,4 +147,7 @@ function write(id, data) {
|
|
|
145
147
|
}
|
|
146
148
|
session.pty.write(data);
|
|
147
149
|
}
|
|
148
|
-
|
|
150
|
+
function findRepoSession(repoPath) {
|
|
151
|
+
return list().find((s) => s.type === 'repo' && s.repoPath === repoPath);
|
|
152
|
+
}
|
|
153
|
+
export { create, get, list, kill, resize, updateDisplayName, write, onIdleChange, findRepoSession };
|
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -39,6 +39,8 @@
|
|
|
39
39
|
var dialogYolo = document.getElementById('dialog-yolo');
|
|
40
40
|
var dialogBranchInput = document.getElementById('dialog-branch-input');
|
|
41
41
|
var dialogBranchList = document.getElementById('dialog-branch-list');
|
|
42
|
+
var dialogContinue = document.getElementById('dialog-continue');
|
|
43
|
+
var dialogContinueField = document.getElementById('dialog-continue-field');
|
|
42
44
|
var contextMenu = document.getElementById('context-menu');
|
|
43
45
|
var ctxResumeYolo = document.getElementById('ctx-resume-yolo');
|
|
44
46
|
var ctxDeleteWorktree = document.getElementById('ctx-delete-worktree');
|
|
@@ -60,6 +62,10 @@
|
|
|
60
62
|
var terminalScrollbarThumb = document.getElementById('terminal-scrollbar-thumb');
|
|
61
63
|
var mobileInput = document.getElementById('mobile-input');
|
|
62
64
|
var mobileHeader = document.getElementById('mobile-header');
|
|
65
|
+
var sidebarTabs = document.querySelectorAll('.sidebar-tab');
|
|
66
|
+
var tabReposCount = document.getElementById('tab-repos-count');
|
|
67
|
+
var tabWorktreesCount = document.getElementById('tab-worktrees-count');
|
|
68
|
+
var activeTab = 'repos';
|
|
63
69
|
var isMobileDevice = 'ontouchstart' in window;
|
|
64
70
|
|
|
65
71
|
// Context menu state
|
|
@@ -92,6 +98,7 @@
|
|
|
92
98
|
var cachedWorktrees = [];
|
|
93
99
|
var allRepos = [];
|
|
94
100
|
var allBranches = [];
|
|
101
|
+
var cachedRepos = [];
|
|
95
102
|
var attentionSessions = {};
|
|
96
103
|
|
|
97
104
|
function loadBranches(repoPath) {
|
|
@@ -523,10 +530,12 @@
|
|
|
523
530
|
Promise.all([
|
|
524
531
|
fetch('/sessions').then(function (res) { return res.json(); }),
|
|
525
532
|
fetch('/worktrees').then(function (res) { return res.json(); }),
|
|
533
|
+
fetch('/repos').then(function (res) { return res.json(); }),
|
|
526
534
|
])
|
|
527
535
|
.then(function (results) {
|
|
528
536
|
cachedSessions = results[0] || [];
|
|
529
537
|
cachedWorktrees = results[1] || [];
|
|
538
|
+
cachedRepos = results[2] || [];
|
|
530
539
|
|
|
531
540
|
// Prune attention flags for sessions that no longer exist
|
|
532
541
|
var activeIds = {};
|
|
@@ -606,6 +615,16 @@
|
|
|
606
615
|
renderUnifiedList();
|
|
607
616
|
});
|
|
608
617
|
|
|
618
|
+
sidebarTabs.forEach(function (tab) {
|
|
619
|
+
tab.addEventListener('click', function () {
|
|
620
|
+
activeTab = tab.dataset.tab;
|
|
621
|
+
sidebarTabs.forEach(function (t) { t.classList.remove('active'); });
|
|
622
|
+
tab.classList.add('active');
|
|
623
|
+
newSessionBtn.textContent = activeTab === 'repos' ? '+ New Session' : '+ New Worktree';
|
|
624
|
+
renderUnifiedList();
|
|
625
|
+
});
|
|
626
|
+
});
|
|
627
|
+
|
|
609
628
|
function rootShortName(path) {
|
|
610
629
|
return path.split('/').filter(Boolean).pop() || path;
|
|
611
630
|
}
|
|
@@ -633,7 +652,42 @@
|
|
|
633
652
|
var repoFilter = sidebarRepoFilter.value;
|
|
634
653
|
var textFilter = sessionFilter.value.toLowerCase();
|
|
635
654
|
|
|
636
|
-
|
|
655
|
+
// Split sessions by type
|
|
656
|
+
var repoSessions = cachedSessions.filter(function (s) { return s.type === 'repo'; });
|
|
657
|
+
var worktreeSessions = cachedSessions.filter(function (s) { return s.type !== 'repo'; });
|
|
658
|
+
|
|
659
|
+
// Filtered repo sessions
|
|
660
|
+
var filteredRepoSessions = repoSessions.filter(function (s) {
|
|
661
|
+
if (rootFilter && s.root !== rootFilter) return false;
|
|
662
|
+
if (repoFilter && s.repoName !== repoFilter) return false;
|
|
663
|
+
if (textFilter) {
|
|
664
|
+
var name = (s.displayName || s.repoName || s.id).toLowerCase();
|
|
665
|
+
if (name.indexOf(textFilter) === -1) return false;
|
|
666
|
+
}
|
|
667
|
+
return true;
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
// Idle repos: all repos without an active repo session
|
|
671
|
+
var activeRepoPathSet = new Set();
|
|
672
|
+
repoSessions.forEach(function (s) { activeRepoPathSet.add(s.repoPath); });
|
|
673
|
+
|
|
674
|
+
var filteredIdleRepos = cachedRepos.filter(function (r) {
|
|
675
|
+
if (activeRepoPathSet.has(r.path)) return false;
|
|
676
|
+
if (rootFilter && r.root !== rootFilter) return false;
|
|
677
|
+
if (repoFilter && r.name !== repoFilter) return false;
|
|
678
|
+
if (textFilter) {
|
|
679
|
+
var name = (r.name || '').toLowerCase();
|
|
680
|
+
if (name.indexOf(textFilter) === -1) return false;
|
|
681
|
+
}
|
|
682
|
+
return true;
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
filteredIdleRepos.sort(function (a, b) {
|
|
686
|
+
return (a.name || '').localeCompare(b.name || '');
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
// Filtered worktree sessions
|
|
690
|
+
var filteredWorktreeSessions = worktreeSessions.filter(function (s) {
|
|
637
691
|
if (rootFilter && s.root !== rootFilter) return false;
|
|
638
692
|
if (repoFilter && s.repoName !== repoFilter) return false;
|
|
639
693
|
if (textFilter) {
|
|
@@ -643,8 +697,9 @@
|
|
|
643
697
|
return true;
|
|
644
698
|
});
|
|
645
699
|
|
|
700
|
+
// Inactive worktrees (deduped against active sessions)
|
|
646
701
|
var activeWorktreePaths = new Set();
|
|
647
|
-
|
|
702
|
+
worktreeSessions.forEach(function (s) {
|
|
648
703
|
if (s.repoPath) activeWorktreePaths.add(s.repoPath);
|
|
649
704
|
});
|
|
650
705
|
|
|
@@ -663,23 +718,35 @@
|
|
|
663
718
|
return (a.name || '').localeCompare(b.name || '');
|
|
664
719
|
});
|
|
665
720
|
|
|
666
|
-
|
|
721
|
+
// Update tab counts
|
|
722
|
+
tabReposCount.textContent = filteredRepoSessions.length + filteredIdleRepos.length;
|
|
723
|
+
tabWorktreesCount.textContent = filteredWorktreeSessions.length + filteredWorktrees.length;
|
|
667
724
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
});
|
|
725
|
+
// Render based on active tab
|
|
726
|
+
sessionList.innerHTML = '';
|
|
671
727
|
|
|
672
|
-
if (
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
728
|
+
if (activeTab === 'repos') {
|
|
729
|
+
filteredRepoSessions.forEach(function (session) {
|
|
730
|
+
sessionList.appendChild(createActiveSessionLi(session));
|
|
731
|
+
});
|
|
732
|
+
if (filteredRepoSessions.length > 0 && filteredIdleRepos.length > 0) {
|
|
733
|
+
sessionList.appendChild(createSectionDivider('Available'));
|
|
734
|
+
}
|
|
735
|
+
filteredIdleRepos.forEach(function (repo) {
|
|
736
|
+
sessionList.appendChild(createIdleRepoLi(repo));
|
|
737
|
+
});
|
|
738
|
+
} else {
|
|
739
|
+
filteredWorktreeSessions.forEach(function (session) {
|
|
740
|
+
sessionList.appendChild(createActiveSessionLi(session));
|
|
741
|
+
});
|
|
742
|
+
if (filteredWorktreeSessions.length > 0 && filteredWorktrees.length > 0) {
|
|
743
|
+
sessionList.appendChild(createSectionDivider('Available'));
|
|
744
|
+
}
|
|
745
|
+
filteredWorktrees.forEach(function (wt) {
|
|
746
|
+
sessionList.appendChild(createInactiveWorktreeLi(wt));
|
|
747
|
+
});
|
|
677
748
|
}
|
|
678
749
|
|
|
679
|
-
filteredWorktrees.forEach(function (wt) {
|
|
680
|
-
sessionList.appendChild(createInactiveWorktreeLi(wt));
|
|
681
|
-
});
|
|
682
|
-
|
|
683
750
|
highlightActiveSession();
|
|
684
751
|
}
|
|
685
752
|
|
|
@@ -824,6 +891,45 @@
|
|
|
824
891
|
return li;
|
|
825
892
|
}
|
|
826
893
|
|
|
894
|
+
function createSectionDivider(label) {
|
|
895
|
+
var divider = document.createElement('li');
|
|
896
|
+
divider.className = 'session-divider';
|
|
897
|
+
divider.textContent = label;
|
|
898
|
+
return divider;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
function createIdleRepoLi(repo) {
|
|
902
|
+
var li = document.createElement('li');
|
|
903
|
+
li.className = 'inactive-worktree';
|
|
904
|
+
li.title = repo.path;
|
|
905
|
+
|
|
906
|
+
var infoDiv = document.createElement('div');
|
|
907
|
+
infoDiv.className = 'session-info';
|
|
908
|
+
|
|
909
|
+
var nameSpan = document.createElement('span');
|
|
910
|
+
nameSpan.className = 'session-name';
|
|
911
|
+
nameSpan.textContent = repo.name;
|
|
912
|
+
nameSpan.title = repo.name;
|
|
913
|
+
|
|
914
|
+
var dot = document.createElement('span');
|
|
915
|
+
dot.className = 'status-dot status-dot--inactive';
|
|
916
|
+
|
|
917
|
+
var subSpan = document.createElement('span');
|
|
918
|
+
subSpan.className = 'session-sub';
|
|
919
|
+
subSpan.textContent = repo.root ? rootShortName(repo.root) : repo.path;
|
|
920
|
+
|
|
921
|
+
infoDiv.appendChild(dot);
|
|
922
|
+
infoDiv.appendChild(nameSpan);
|
|
923
|
+
infoDiv.appendChild(subSpan);
|
|
924
|
+
li.appendChild(infoDiv);
|
|
925
|
+
|
|
926
|
+
li.addEventListener('click', function () {
|
|
927
|
+
openNewSessionDialogForRepo(repo);
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
return li;
|
|
931
|
+
}
|
|
932
|
+
|
|
827
933
|
function startRename(li, session) {
|
|
828
934
|
var nameSpan = li.querySelector('.session-name');
|
|
829
935
|
if (!nameSpan) return;
|
|
@@ -1043,13 +1149,74 @@
|
|
|
1043
1149
|
.catch(function () {});
|
|
1044
1150
|
}
|
|
1045
1151
|
|
|
1046
|
-
|
|
1152
|
+
function resetDialogFields() {
|
|
1047
1153
|
customPath.value = '';
|
|
1048
1154
|
dialogYolo.checked = false;
|
|
1155
|
+
dialogContinue.checked = false;
|
|
1049
1156
|
dialogBranchInput.value = '';
|
|
1050
1157
|
dialogBranchList.hidden = true;
|
|
1051
1158
|
allBranches = [];
|
|
1052
1159
|
populateDialogRootSelect();
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
function showDialogForTab(tab) {
|
|
1163
|
+
var dialogBranchField = dialogBranchInput.closest('.dialog-field');
|
|
1164
|
+
if (tab === 'repos') {
|
|
1165
|
+
dialogBranchField.hidden = true;
|
|
1166
|
+
dialogContinueField.hidden = false;
|
|
1167
|
+
dialogStart.textContent = 'New Session';
|
|
1168
|
+
} else {
|
|
1169
|
+
dialogBranchField.hidden = false;
|
|
1170
|
+
dialogContinueField.hidden = true;
|
|
1171
|
+
dialogStart.textContent = 'New Worktree';
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
function openNewSessionDialogForRepo(repo) {
|
|
1176
|
+
resetDialogFields();
|
|
1177
|
+
|
|
1178
|
+
if (repo.root) {
|
|
1179
|
+
dialogRootSelect.value = repo.root;
|
|
1180
|
+
dialogRootSelect.dispatchEvent(new Event('change'));
|
|
1181
|
+
dialogRepoSelect.value = repo.path;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
showDialogForTab('repos');
|
|
1185
|
+
dialog.showModal();
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
function startRepoSession(repoPath, continueSession, claudeArgs) {
|
|
1189
|
+
var body = { repoPath: repoPath };
|
|
1190
|
+
if (continueSession) body.continue = true;
|
|
1191
|
+
if (claudeArgs) body.claudeArgs = claudeArgs;
|
|
1192
|
+
|
|
1193
|
+
fetch('/sessions/repo', {
|
|
1194
|
+
method: 'POST',
|
|
1195
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1196
|
+
body: JSON.stringify(body),
|
|
1197
|
+
})
|
|
1198
|
+
.then(function (res) {
|
|
1199
|
+
if (res.status === 409) {
|
|
1200
|
+
return res.json().then(function (data) {
|
|
1201
|
+
if (dialog.open) dialog.close();
|
|
1202
|
+
refreshAll();
|
|
1203
|
+
if (data.sessionId) connectToSession(data.sessionId);
|
|
1204
|
+
return null;
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
return res.json();
|
|
1208
|
+
})
|
|
1209
|
+
.then(function (data) {
|
|
1210
|
+
if (!data) return;
|
|
1211
|
+
if (dialog.open) dialog.close();
|
|
1212
|
+
refreshAll();
|
|
1213
|
+
if (data.id) connectToSession(data.id);
|
|
1214
|
+
})
|
|
1215
|
+
.catch(function () {});
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
newSessionBtn.addEventListener('click', function () {
|
|
1219
|
+
resetDialogFields();
|
|
1053
1220
|
|
|
1054
1221
|
var sidebarRoot = sidebarRootFilter.value;
|
|
1055
1222
|
if (sidebarRoot) {
|
|
@@ -1069,6 +1236,7 @@
|
|
|
1069
1236
|
dialogRepoSelect.disabled = true;
|
|
1070
1237
|
}
|
|
1071
1238
|
|
|
1239
|
+
showDialogForTab(activeTab);
|
|
1072
1240
|
dialog.showModal();
|
|
1073
1241
|
});
|
|
1074
1242
|
|
|
@@ -1076,8 +1244,13 @@
|
|
|
1076
1244
|
var repoPathValue = customPath.value.trim() || dialogRepoSelect.value;
|
|
1077
1245
|
if (!repoPathValue) return;
|
|
1078
1246
|
var args = dialogYolo.checked ? ['--dangerously-skip-permissions'] : undefined;
|
|
1079
|
-
|
|
1080
|
-
|
|
1247
|
+
|
|
1248
|
+
if (activeTab === 'repos') {
|
|
1249
|
+
startRepoSession(repoPathValue, dialogContinue.checked, args);
|
|
1250
|
+
} else {
|
|
1251
|
+
var branch = dialogBranchInput.value.trim() || undefined;
|
|
1252
|
+
startSession(repoPathValue, undefined, args, branch);
|
|
1253
|
+
}
|
|
1081
1254
|
});
|
|
1082
1255
|
|
|
1083
1256
|
customPath.addEventListener('blur', function () {
|
package/public/index.html
CHANGED
|
@@ -52,6 +52,10 @@
|
|
|
52
52
|
</select>
|
|
53
53
|
<input type="text" id="session-filter" placeholder="Filter..." />
|
|
54
54
|
</div>
|
|
55
|
+
<div class="sidebar-tabs">
|
|
56
|
+
<button class="sidebar-tab active" data-tab="repos">Repos (<span id="tab-repos-count">0</span>)</button>
|
|
57
|
+
<button class="sidebar-tab" data-tab="worktrees">Worktrees (<span id="tab-worktrees-count">0</span>)</button>
|
|
58
|
+
</div>
|
|
55
59
|
<ul id="session-list"></ul>
|
|
56
60
|
<button id="new-session-btn">+ New Session</button>
|
|
57
61
|
<button id="settings-btn">Settings</button>
|
|
@@ -140,6 +144,14 @@
|
|
|
140
144
|
<span class="dialog-option-hint">Leave empty for auto-generated name</span>
|
|
141
145
|
</div>
|
|
142
146
|
|
|
147
|
+
<div class="dialog-field" id="dialog-continue-field" hidden>
|
|
148
|
+
<label>
|
|
149
|
+
<input type="checkbox" id="dialog-continue" />
|
|
150
|
+
Continue previous conversation
|
|
151
|
+
</label>
|
|
152
|
+
<span class="dialog-option-hint">Resume where you left off (--continue)</span>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
143
155
|
<hr class="dialog-separator" />
|
|
144
156
|
<div class="dialog-custom-path">
|
|
145
157
|
<label for="custom-path-input">Or enter a local path:</label>
|
package/public/style.css
CHANGED
|
@@ -202,6 +202,36 @@ html, body {
|
|
|
202
202
|
border-color: var(--accent);
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
/* Sidebar Tabs */
|
|
206
|
+
.sidebar-tabs {
|
|
207
|
+
display: flex;
|
|
208
|
+
gap: 0;
|
|
209
|
+
padding: 0 8px;
|
|
210
|
+
border-bottom: 1px solid var(--border);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.sidebar-tab {
|
|
214
|
+
flex: 1;
|
|
215
|
+
background: none;
|
|
216
|
+
border: none;
|
|
217
|
+
border-bottom: 2px solid transparent;
|
|
218
|
+
color: var(--text-muted);
|
|
219
|
+
font-size: 0.7rem;
|
|
220
|
+
padding: 6px 4px;
|
|
221
|
+
cursor: pointer;
|
|
222
|
+
transition: color 0.15s, border-color 0.15s;
|
|
223
|
+
text-align: center;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.sidebar-tab:hover {
|
|
227
|
+
color: var(--text);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.sidebar-tab.active {
|
|
231
|
+
color: var(--accent);
|
|
232
|
+
border-bottom-color: var(--accent);
|
|
233
|
+
}
|
|
234
|
+
|
|
205
235
|
#session-list {
|
|
206
236
|
list-style: none;
|
|
207
237
|
flex: 1;
|