tmux-weblink 0.3.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 +70 -0
- package/dist/assets/addon-fit-D5N335P2.js +16 -0
- package/dist/assets/terminal-client.js +4 -0
- package/dist/assets/xterm-NI46XXQY.js +51 -0
- package/dist/assets/xterm.css +285 -0
- package/dist/browser/terminal-client.js +4 -0
- package/dist/frontend.js +1 -0
- package/dist/index.js +12 -0
- package/dist/lib/agent-detect.js +3 -0
- package/dist/lib/agents-watch.js +2 -0
- package/dist/lib/atomicWrite.js +1 -0
- package/dist/lib/auditLog.js +1 -0
- package/dist/lib/auth.js +2 -0
- package/dist/lib/cli.js +32 -0
- package/dist/lib/commandbar.js +555 -0
- package/dist/lib/db.js +1 -0
- package/dist/lib/drawer-resize.js +166 -0
- package/dist/lib/drawer-script.js +8 -0
- package/dist/lib/ext-loader.js +1 -0
- package/dist/lib/html.js +1 -0
- package/dist/lib/icons.js +1 -0
- package/dist/lib/image-upload.js +1 -0
- package/dist/lib/load-env.js +6 -0
- package/dist/lib/mobile-toolbar.js +313 -0
- package/dist/lib/notes-db.js +18 -0
- package/dist/lib/notes-drawer.js +210 -0
- package/dist/lib/notes-utils.js +15 -0
- package/dist/lib/pages/agents-index.js +105 -0
- package/dist/lib/pages/history-index.js +85 -0
- package/dist/lib/pages/landing.js +78 -0
- package/dist/lib/pages/login.js +191 -0
- package/dist/lib/pages/notes-index.js +65 -0
- package/dist/lib/pages/notes-page.js +158 -0
- package/dist/lib/pages/quick-commands.js +361 -0
- package/dist/lib/pages/schedule-index.js +392 -0
- package/dist/lib/pages/settings.js +206 -0
- package/dist/lib/pages/terminal.js +364 -0
- package/dist/lib/pinned-views.js +1 -0
- package/dist/lib/plugins.js +3 -0
- package/dist/lib/quick-commands.js +1 -0
- package/dist/lib/rateLimiter.js +2 -0
- package/dist/lib/schedule-delay.js +9 -0
- package/dist/lib/scheduler-drawer.js +379 -0
- package/dist/lib/scheduler.js +1 -0
- package/dist/lib/security-config.js +2 -0
- package/dist/lib/session-access.js +1 -0
- package/dist/lib/session-windows.js +1 -0
- package/dist/lib/sessions-drawer.js +634 -0
- package/dist/lib/sessions-sidebar.js +1 -0
- package/dist/lib/settings.js +2 -0
- package/dist/lib/setup-features.js +1 -0
- package/dist/lib/setup-prompts.js +3 -0
- package/dist/lib/shared-layout.js +412 -0
- package/dist/lib/state-paths.js +1 -0
- package/dist/lib/terminal-config.js +1 -0
- package/dist/lib/theme-store.js +2 -0
- package/dist/lib/theme.js +28 -0
- package/dist/lib/themes/dark-cove.js +1 -0
- package/dist/lib/themes/ghostty.js +1 -0
- package/dist/lib/themes/index.js +1 -0
- package/dist/lib/themes/types.js +0 -0
- package/dist/lib/themes/vscode.js +1 -0
- package/dist/lib/themes/warm-clay.js +1 -0
- package/dist/lib/tmux-capture.js +5 -0
- package/dist/lib/tmux-control.js +2 -0
- package/dist/lib/tmux-panes.js +2 -0
- package/dist/lib/tmux-windows.js +4 -0
- package/dist/lib/watched-panes.js +1 -0
- package/dist/lib/window-history.js +1 -0
- package/dist/lib/window-labels.js +1 -0
- package/dist/lib/windows-drawer.js +364 -0
- package/dist/lib/ws-message.js +1 -0
- package/dist/sessions.js +2 -0
- package/package.json +62 -0
- package/scripts/fix-pty-perms.mjs +16 -0
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
import{icon as c}from"./icons.js";function m(n,s){return n.map(e=>({...e,lastAccessedAt:s.get(e.name)})).sort((e,t)=>{const i=e.lastAccessedAt??0,a=t.lastAccessedAt??0;return i!==a?a-i:e.name.localeCompare(t.name)})}function l(){return`
|
|
2
|
+
.cmdbar-btn {
|
|
3
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
4
|
+
background: none; color: var(--panel-muted);
|
|
5
|
+
border: none; padding: 0;
|
|
6
|
+
cursor: pointer; font-family: var(--font-mono);
|
|
7
|
+
line-height: 1;
|
|
8
|
+
transition: color 0.15s;
|
|
9
|
+
min-width: 44px; min-height: 44px;
|
|
10
|
+
}
|
|
11
|
+
.cmdbar-btn:hover,
|
|
12
|
+
.cmdbar-btn:focus-visible {
|
|
13
|
+
color: var(--panel-accent); outline: none;
|
|
14
|
+
}
|
|
15
|
+
.cmdbar-btn:focus-visible { box-shadow: 0 0 0 2px var(--panel-accent); border-radius: 6px; }
|
|
16
|
+
.cmdbar-shortcut {
|
|
17
|
+
color: inherit; border: 1px solid rgba(148, 163, 184, 0.22);
|
|
18
|
+
border-radius: 4px; padding: 2px 4px; font-size: var(--text-xs);
|
|
19
|
+
}
|
|
20
|
+
.cmdbar-backdrop {
|
|
21
|
+
position: fixed; inset: 0; z-index: 1400; background: rgba(2, 6, 12, 0.62);
|
|
22
|
+
opacity: 0; pointer-events: none; transition: opacity 0.14s ease;
|
|
23
|
+
}
|
|
24
|
+
.cmdbar-backdrop.open { opacity: 1; pointer-events: auto; }
|
|
25
|
+
.cmdbar-panel {
|
|
26
|
+
position: fixed; left: 50%; top: 72px; z-index: 1401;
|
|
27
|
+
width: min(620px, calc(100vw - 28px)); max-height: min(560px, calc(100vh - 96px));
|
|
28
|
+
transform: translate(-50%, -8px) scale(0.985); opacity: 0; pointer-events: none;
|
|
29
|
+
background: var(--panel-bg);
|
|
30
|
+
background: color-mix(in srgb, var(--panel-bg) 94%, black);
|
|
31
|
+
border: 1px solid rgba(125, 211, 252, 0.22); border-radius: 8px;
|
|
32
|
+
box-shadow: 0 24px 80px rgba(0,0,0,0.5);
|
|
33
|
+
overflow: hidden; transition: opacity 0.14s ease, transform 0.14s ease;
|
|
34
|
+
font-family: var(--font-mono);
|
|
35
|
+
}
|
|
36
|
+
.cmdbar-panel.open { opacity: 1; pointer-events: auto; transform: translate(-50%, 0) scale(1); }
|
|
37
|
+
.cmdbar-search {
|
|
38
|
+
display: flex; align-items: center; gap: 10px;
|
|
39
|
+
padding: 14px 16px; border-bottom: 1px solid var(--panel-border);
|
|
40
|
+
}
|
|
41
|
+
.cmdbar-search svg { width: 16px; height: 16px; fill: var(--panel-muted); flex: 0 0 auto; }
|
|
42
|
+
.cmdbar-search input {
|
|
43
|
+
width: 100%; min-width: 0; border: 0; outline: 0; background: transparent;
|
|
44
|
+
color: var(--page-fg); font: inherit; font-size: var(--text-sm);
|
|
45
|
+
}
|
|
46
|
+
.cmdbar-search input::placeholder { color: var(--panel-muted); }
|
|
47
|
+
.cmdbar-list {
|
|
48
|
+
max-height: 428px; overflow: auto; padding: 8px;
|
|
49
|
+
}
|
|
50
|
+
.cmdbar-row {
|
|
51
|
+
display: flex; justify-content: space-between; align-items: center; gap: 14px;
|
|
52
|
+
width: 100%; min-height: 44px; padding: 10px 12px; border: 1px solid transparent; border-radius: 6px;
|
|
53
|
+
background: transparent; color: var(--page-fg); cursor: pointer; text-align: left;
|
|
54
|
+
font: inherit;
|
|
55
|
+
}
|
|
56
|
+
.cmdbar-row:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--panel-accent); }
|
|
57
|
+
.cmdbar-row-action .cmdbar-row-name { color: var(--panel-success); }
|
|
58
|
+
.cmdbar-row-subview .cmdbar-row-meta {
|
|
59
|
+
display: inline-flex; align-items: center; gap: 8px;
|
|
60
|
+
}
|
|
61
|
+
.cmdbar-row-chevron {
|
|
62
|
+
color: var(--panel-accent); font-size: var(--text-base); line-height: 1;
|
|
63
|
+
}
|
|
64
|
+
.cmdbar-row:hover,
|
|
65
|
+
.cmdbar-row.active {
|
|
66
|
+
border-color: color-mix(in srgb, var(--panel-accent) 28%, transparent); background: color-mix(in srgb, var(--panel-accent) 8%, transparent);
|
|
67
|
+
}
|
|
68
|
+
.cmdbar-row-name {
|
|
69
|
+
min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
70
|
+
color: var(--panel-accent); font-size: var(--text-sm);
|
|
71
|
+
}
|
|
72
|
+
.cmdbar-row-meta {
|
|
73
|
+
flex: 0 0 auto; color: var(--panel-muted); font-size: var(--text-xs); text-align: right;
|
|
74
|
+
}
|
|
75
|
+
.cmdbar-row-meta-current { color: var(--panel-success); text-transform: uppercase; letter-spacing: 0.06em; }
|
|
76
|
+
.cmdbar-rename-input {
|
|
77
|
+
width: 100%; min-width: 0; border: 1px solid rgba(125, 211, 252, 0.35);
|
|
78
|
+
border-radius: 4px; padding: 4px 6px; background: rgba(0, 0, 0, 0.25);
|
|
79
|
+
color: var(--page-fg); font: inherit; font-size: var(--text-sm); outline: none;
|
|
80
|
+
}
|
|
81
|
+
.cmdbar-rename-input:focus { border-color: var(--panel-accent); }
|
|
82
|
+
.cmdbar-empty {
|
|
83
|
+
padding: 28px 16px; text-align: center; color: var(--panel-muted); font-size: var(--text-xs);
|
|
84
|
+
}
|
|
85
|
+
.cmdbar-footer {
|
|
86
|
+
display: flex; justify-content: space-between; gap: 12px;
|
|
87
|
+
padding: 8px 12px; border-top: 1px solid var(--panel-border);
|
|
88
|
+
color: var(--panel-muted); font-size: var(--text-xs);
|
|
89
|
+
}
|
|
90
|
+
@media (max-width: 560px) {
|
|
91
|
+
.cmdbar-panel { top: 54px; width: calc(100vw - 16px); max-height: calc(100vh - 70px); }
|
|
92
|
+
.cmdbar-row { align-items: flex-start; flex-direction: column; gap: 4px; }
|
|
93
|
+
.cmdbar-row-meta { text-align: left; }
|
|
94
|
+
}
|
|
95
|
+
@media (prefers-reduced-motion: reduce) {
|
|
96
|
+
.cmdbar-backdrop, .cmdbar-panel { transition-duration: 0.01ms !important; }
|
|
97
|
+
}`}function u(n="Sessions"){return`<button class="cmdbar-btn" id="cmdbar-open" title="${n} (\u2318K)" aria-label="${n} (\u2318K)">
|
|
98
|
+
<span class="cmdbar-shortcut">\u2318K</span>
|
|
99
|
+
</button>`}function p(){return`
|
|
100
|
+
<div class="cmdbar-backdrop" id="cmdbar-backdrop"></div>
|
|
101
|
+
<div class="cmdbar-panel" id="cmdbar-panel" role="dialog" aria-modal="true" aria-label="Switch tmux session">
|
|
102
|
+
<div class="cmdbar-search">
|
|
103
|
+
${c("search")}
|
|
104
|
+
<input id="cmdbar-input" type="search" placeholder="Filter tmux sessions" autocomplete="off" spellcheck="false" />
|
|
105
|
+
</div>
|
|
106
|
+
<div class="cmdbar-list" id="cmdbar-list"></div>
|
|
107
|
+
<div class="cmdbar-footer">
|
|
108
|
+
<span id="cmdbar-count">Recent sessions</span>
|
|
109
|
+
<span id="cmdbar-hint">Enter opens \xB7 Esc closes</span>
|
|
110
|
+
</div>
|
|
111
|
+
</div>`}function f(n,s=[],e={},t=[]){const i=JSON.stringify(n).replace(/</g,"\\u003c"),a=JSON.stringify(s).replace(/</g,"\\u003c"),o=JSON.stringify(e.sessionName??null).replace(/</g,"\\u003c"),r=JSON.stringify(t).replace(/</g,"\\u003c");return`
|
|
112
|
+
(function() {
|
|
113
|
+
const initialSessions = ${i};
|
|
114
|
+
const actions = ${a}.map((action) => ({ ...action, kind: 'action' }));
|
|
115
|
+
const sessionName = ${o};
|
|
116
|
+
const quickCommands = ${r}.map((command) => ({ ...command, kind: 'quickCommand' }));
|
|
117
|
+
let sessions = initialSessions;
|
|
118
|
+
let visible = [];
|
|
119
|
+
let activeIndex = 0;
|
|
120
|
+
let view = 'root';
|
|
121
|
+
let windows = [];
|
|
122
|
+
let renaming = false;
|
|
123
|
+
|
|
124
|
+
const ROOT_PLACEHOLDER = 'Filter tmux sessions';
|
|
125
|
+
const WINDOWS_PLACEHOLDER = 'Filter windows';
|
|
126
|
+
const QUICK_COMMANDS_PLACEHOLDER = 'Filter quick commands';
|
|
127
|
+
|
|
128
|
+
const openBtn = document.getElementById('cmdbar-open');
|
|
129
|
+
const backdrop = document.getElementById('cmdbar-backdrop');
|
|
130
|
+
const panel = document.getElementById('cmdbar-panel');
|
|
131
|
+
const input = document.getElementById('cmdbar-input');
|
|
132
|
+
const list = document.getElementById('cmdbar-list');
|
|
133
|
+
const count = document.getElementById('cmdbar-count');
|
|
134
|
+
const hint = document.getElementById('cmdbar-hint');
|
|
135
|
+
if (!openBtn || !backdrop || !panel || !input || !list || !count || !hint) return;
|
|
136
|
+
|
|
137
|
+
const formatter = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
|
|
138
|
+
|
|
139
|
+
function relativeTime(ts) {
|
|
140
|
+
if (!ts) return '';
|
|
141
|
+
const diffMs = Date.now() - ts;
|
|
142
|
+
if (diffMs < 45000) return 'just now';
|
|
143
|
+
const minutes = Math.floor(diffMs / 60000);
|
|
144
|
+
const hours = Math.floor(minutes / 60);
|
|
145
|
+
const days = Math.floor(hours / 24);
|
|
146
|
+
const weeks = Math.floor(days / 7);
|
|
147
|
+
const months = Math.floor(days / 30);
|
|
148
|
+
if (minutes < 60) return formatter.format(-minutes, 'minute');
|
|
149
|
+
if (hours < 24) return formatter.format(-hours, 'hour');
|
|
150
|
+
if (days < 7) return formatter.format(-days, 'day');
|
|
151
|
+
if (weeks < 5) return formatter.format(-weeks, 'week');
|
|
152
|
+
if (months < 12) return formatter.format(-months, 'month');
|
|
153
|
+
return formatter.format(-Math.floor(days / 365), 'year');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function sessionMeta(session) {
|
|
157
|
+
const parts = [
|
|
158
|
+
session.windows + ' window' + (session.windows === 1 ? '' : 's'),
|
|
159
|
+
];
|
|
160
|
+
if (session.attached) parts.push('attached');
|
|
161
|
+
const recent = relativeTime(session.lastAccessedAt);
|
|
162
|
+
if (recent) parts.push(recent);
|
|
163
|
+
return parts.join(' \xB7 ');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function windowDisplayName(win) {
|
|
167
|
+
return win.label || win.name;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function escapeHtml(value) {
|
|
171
|
+
return String(value)
|
|
172
|
+
.replace(/&/g, '&')
|
|
173
|
+
.replace(/</g, '<')
|
|
174
|
+
.replace(/>/g, '>')
|
|
175
|
+
.replace(/"/g, '"')
|
|
176
|
+
.replace(/'/g, ''');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function updateChrome() {
|
|
180
|
+
if (view === 'windows') {
|
|
181
|
+
input.placeholder = WINDOWS_PLACEHOLDER;
|
|
182
|
+
hint.textContent = 'Enter switches \xB7 r renames \xB7 \u2190 back \xB7 Esc closes';
|
|
183
|
+
} else if (view === 'quickCommands') {
|
|
184
|
+
input.placeholder = QUICK_COMMANDS_PLACEHOLDER;
|
|
185
|
+
hint.textContent = 'Enter pastes \xB7 \u2190 back \xB7 Esc closes';
|
|
186
|
+
} else {
|
|
187
|
+
input.placeholder = ROOT_PLACEHOLDER;
|
|
188
|
+
hint.textContent = 'Enter opens \xB7 Esc closes';
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function selectSession(session) {
|
|
193
|
+
window.location.href = '/s/' + encodeURIComponent(session.name);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function selectItem(item) {
|
|
197
|
+
if (item.kind === 'action') {
|
|
198
|
+
if (item.subView === 'windows' && sessionName) {
|
|
199
|
+
void enterWindowsView();
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (item.subView === 'quickCommands') {
|
|
203
|
+
enterQuickCommandsView();
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
setOpen(false);
|
|
207
|
+
if (item.href) {
|
|
208
|
+
window.location.href = item.href;
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (item.clickTargetId) document.getElementById(item.clickTargetId)?.click();
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
selectSession(item);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function quickCommandMeta(command) {
|
|
218
|
+
return command.description || command.command;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function renderRoot() {
|
|
222
|
+
const query = input.value.trim().toLowerCase();
|
|
223
|
+
const actionMatches = actions.filter((action) => (
|
|
224
|
+
action.label.toLowerCase().includes(query) || action.meta.toLowerCase().includes(query)
|
|
225
|
+
));
|
|
226
|
+
const sessionMatches = query
|
|
227
|
+
? sessions.filter((session) => session.name.toLowerCase().includes(query))
|
|
228
|
+
: sessions.slice(0, 5);
|
|
229
|
+
visible = query ? [...actionMatches, ...sessionMatches] : [...actions, ...sessionMatches];
|
|
230
|
+
activeIndex = Math.min(activeIndex, Math.max(0, visible.length - 1));
|
|
231
|
+
count.textContent = query
|
|
232
|
+
? visible.length + ' result' + (visible.length === 1 ? '' : 's')
|
|
233
|
+
: 'Actions \xB7 Recent sessions';
|
|
234
|
+
|
|
235
|
+
if (!visible.length) {
|
|
236
|
+
list.innerHTML = '<div class="cmdbar-empty">No sessions found</div>';
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
list.innerHTML = visible.map((item, index) => (
|
|
241
|
+
'<button class="cmdbar-row' + (item.kind === 'action' ? ' cmdbar-row-action' : '') + (item.kind === 'action' && item.subView ? ' cmdbar-row-subview' : '') + (index === activeIndex ? ' active' : '') + '" data-index="' + index + '">' +
|
|
242
|
+
'<span class="cmdbar-row-name">' + escapeHtml(item.kind === 'action' ? item.label : item.name) + '</span>' +
|
|
243
|
+
'<span class="cmdbar-row-meta">' + escapeHtml(item.kind === 'action' ? item.meta : sessionMeta(item)) + (item.kind === 'action' && item.subView ? '<span class="cmdbar-row-chevron" aria-hidden="true">›</span>' : '') + '</span>' +
|
|
244
|
+
'</button>'
|
|
245
|
+
)).join('');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function renderWindows() {
|
|
249
|
+
const query = input.value.trim().toLowerCase();
|
|
250
|
+
visible = query
|
|
251
|
+
? windows.filter((win) => {
|
|
252
|
+
const label = (win.label || '').toLowerCase();
|
|
253
|
+
const name = win.name.toLowerCase();
|
|
254
|
+
return label.includes(query) || name.includes(query) || String(win.index).includes(query);
|
|
255
|
+
})
|
|
256
|
+
: windows.slice();
|
|
257
|
+
activeIndex = Math.min(activeIndex, Math.max(0, visible.length - 1));
|
|
258
|
+
count.textContent = query
|
|
259
|
+
? visible.length + ' window' + (visible.length === 1 ? '' : 's')
|
|
260
|
+
: 'Switch window';
|
|
261
|
+
|
|
262
|
+
if (!visible.length) {
|
|
263
|
+
list.innerHTML = '<div class="cmdbar-empty">' + (windows.length ? 'No windows found' : 'No windows in this session') + '</div>';
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
list.innerHTML = visible.map((win, index) => {
|
|
268
|
+
const metaClass = win.active ? ' cmdbar-row-meta-current' : '';
|
|
269
|
+
const meta = win.active ? 'current' : String(win.index);
|
|
270
|
+
return (
|
|
271
|
+
'<button class="cmdbar-row' + (index === activeIndex ? ' active' : '') + '" data-index="' + index + '">' +
|
|
272
|
+
'<span class="cmdbar-row-name">' + escapeHtml(windowDisplayName(win)) + '</span>' +
|
|
273
|
+
'<span class="cmdbar-row-meta' + metaClass + '">' + escapeHtml(meta) + '</span>' +
|
|
274
|
+
'</button>'
|
|
275
|
+
);
|
|
276
|
+
}).join('');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function renderQuickCommands() {
|
|
280
|
+
const query = input.value.trim().toLowerCase();
|
|
281
|
+
visible = query
|
|
282
|
+
? quickCommands.filter((command) => (
|
|
283
|
+
command.title.toLowerCase().includes(query) ||
|
|
284
|
+
command.command.toLowerCase().includes(query) ||
|
|
285
|
+
(command.description || '').toLowerCase().includes(query)
|
|
286
|
+
))
|
|
287
|
+
: quickCommands.slice();
|
|
288
|
+
activeIndex = Math.min(activeIndex, Math.max(0, visible.length - 1));
|
|
289
|
+
count.textContent = query
|
|
290
|
+
? visible.length + ' command' + (visible.length === 1 ? '' : 's')
|
|
291
|
+
: 'Quick commands';
|
|
292
|
+
|
|
293
|
+
if (!visible.length) {
|
|
294
|
+
list.innerHTML = '<div class="cmdbar-empty">' + (
|
|
295
|
+
quickCommands.length
|
|
296
|
+
? 'No quick commands found'
|
|
297
|
+
: 'No quick commands configured. <a href="/quick-commands">Configure them</a>'
|
|
298
|
+
) + '</div>';
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
list.innerHTML = visible.map((command, index) => (
|
|
303
|
+
'<button class="cmdbar-row' + (index === activeIndex ? ' active' : '') + '" data-index="' + index + '">' +
|
|
304
|
+
'<span class="cmdbar-row-name">' + escapeHtml(command.title) + '</span>' +
|
|
305
|
+
'<span class="cmdbar-row-meta">' + escapeHtml(quickCommandMeta(command)) + '</span>' +
|
|
306
|
+
'</button>'
|
|
307
|
+
)).join('');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function render() {
|
|
311
|
+
updateChrome();
|
|
312
|
+
if (view === 'windows') renderWindows();
|
|
313
|
+
else if (view === 'quickCommands') renderQuickCommands();
|
|
314
|
+
else renderRoot();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async function fetchWindows() {
|
|
318
|
+
if (!sessionName) return [];
|
|
319
|
+
try {
|
|
320
|
+
const res = await fetch('/api/session/' + encodeURIComponent(sessionName) + '/windows');
|
|
321
|
+
if (!res.ok) return [];
|
|
322
|
+
return await res.json();
|
|
323
|
+
} catch {
|
|
324
|
+
return [];
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
async function enterWindowsView() {
|
|
329
|
+
if (!sessionName) return;
|
|
330
|
+
windows = await fetchWindows();
|
|
331
|
+
view = 'windows';
|
|
332
|
+
input.value = '';
|
|
333
|
+
activeIndex = 0;
|
|
334
|
+
render();
|
|
335
|
+
setTimeout(() => input.focus(), 0);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function enterQuickCommandsView() {
|
|
339
|
+
view = 'quickCommands';
|
|
340
|
+
input.value = '';
|
|
341
|
+
activeIndex = 0;
|
|
342
|
+
render();
|
|
343
|
+
setTimeout(() => input.focus(), 0);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function exitSubview() {
|
|
347
|
+
view = 'root';
|
|
348
|
+
input.value = '';
|
|
349
|
+
activeIndex = 0;
|
|
350
|
+
render();
|
|
351
|
+
setTimeout(() => input.focus(), 0);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function selectQuickCommand(command) {
|
|
355
|
+
if (!command || typeof command.command !== 'string') return;
|
|
356
|
+
setOpen(false);
|
|
357
|
+
if (window.tmuxWeb && window.tmuxWeb.sendInput) {
|
|
358
|
+
window.tmuxWeb.sendInput(command.command);
|
|
359
|
+
if (window.tmuxWeb.focusTerminal) window.tmuxWeb.focusTerminal();
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function selectWindow(win) {
|
|
364
|
+
if (!sessionName || !win || win.active) return;
|
|
365
|
+
try {
|
|
366
|
+
const res = await fetch(
|
|
367
|
+
'/api/session/' + encodeURIComponent(sessionName) + '/select-window',
|
|
368
|
+
{
|
|
369
|
+
method: 'POST',
|
|
370
|
+
headers: { 'Content-Type': 'application/json' },
|
|
371
|
+
body: JSON.stringify({ windowIndex: win.index }),
|
|
372
|
+
},
|
|
373
|
+
);
|
|
374
|
+
if (!res.ok) return;
|
|
375
|
+
setOpen(false);
|
|
376
|
+
} catch {}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function startWindowRename(win) {
|
|
380
|
+
if (!sessionName || !win || renaming) return;
|
|
381
|
+
renaming = true;
|
|
382
|
+
const row = list.querySelector('.cmdbar-row.active');
|
|
383
|
+
if (!row) {
|
|
384
|
+
renaming = false;
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
const nameEl = row.querySelector('.cmdbar-row-name');
|
|
388
|
+
if (!nameEl) {
|
|
389
|
+
renaming = false;
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const editInput = document.createElement('input');
|
|
394
|
+
editInput.type = 'text';
|
|
395
|
+
editInput.className = 'cmdbar-rename-input';
|
|
396
|
+
editInput.value = win.label || win.name || '';
|
|
397
|
+
editInput.placeholder = win.name || '';
|
|
398
|
+
editInput.addEventListener('click', (event) => event.stopPropagation());
|
|
399
|
+
|
|
400
|
+
let done = false;
|
|
401
|
+
const finish = async (save) => {
|
|
402
|
+
if (done) return;
|
|
403
|
+
done = true;
|
|
404
|
+
renaming = false;
|
|
405
|
+
if (save) {
|
|
406
|
+
try {
|
|
407
|
+
const res = await fetch(
|
|
408
|
+
'/api/session/' + encodeURIComponent(sessionName) + '/window-label',
|
|
409
|
+
{
|
|
410
|
+
method: 'POST',
|
|
411
|
+
headers: { 'Content-Type': 'application/json' },
|
|
412
|
+
body: JSON.stringify({ windowIndex: win.index, label: editInput.value }),
|
|
413
|
+
},
|
|
414
|
+
);
|
|
415
|
+
if (res.ok) {
|
|
416
|
+
const trimmed = editInput.value.trim();
|
|
417
|
+
win.label = trimmed || null;
|
|
418
|
+
const stored = windows.find((entry) => entry.index === win.index);
|
|
419
|
+
if (stored) stored.label = win.label;
|
|
420
|
+
}
|
|
421
|
+
} catch {}
|
|
422
|
+
}
|
|
423
|
+
render();
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
editInput.addEventListener('keydown', (event) => {
|
|
427
|
+
event.stopPropagation();
|
|
428
|
+
if (event.key === 'Enter') { event.preventDefault(); void finish(true); }
|
|
429
|
+
else if (event.key === 'Escape') { event.preventDefault(); void finish(false); }
|
|
430
|
+
});
|
|
431
|
+
editInput.addEventListener('blur', () => { void finish(true); });
|
|
432
|
+
|
|
433
|
+
nameEl.replaceWith(editInput);
|
|
434
|
+
editInput.focus();
|
|
435
|
+
editInput.select();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function setOpen(nextOpen) {
|
|
439
|
+
panel.classList.toggle('open', nextOpen);
|
|
440
|
+
backdrop.classList.toggle('open', nextOpen);
|
|
441
|
+
if (nextOpen) {
|
|
442
|
+
view = 'root';
|
|
443
|
+
renaming = false;
|
|
444
|
+
input.value = '';
|
|
445
|
+
activeIndex = 0;
|
|
446
|
+
render();
|
|
447
|
+
setTimeout(() => input.focus(), 0);
|
|
448
|
+
refreshSessions();
|
|
449
|
+
} else {
|
|
450
|
+
view = 'root';
|
|
451
|
+
renaming = false;
|
|
452
|
+
openBtn.focus();
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async function refreshSessions() {
|
|
457
|
+
try {
|
|
458
|
+
const res = await fetch('/api/sessions', { headers: { accept: 'application/json' } });
|
|
459
|
+
if (!res.ok) return;
|
|
460
|
+
sessions = await res.json();
|
|
461
|
+
render();
|
|
462
|
+
} catch {}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
openBtn.addEventListener('click', () => setOpen(true));
|
|
466
|
+
backdrop.addEventListener('click', () => setOpen(false));
|
|
467
|
+
input.addEventListener('input', () => {
|
|
468
|
+
if (renaming) return;
|
|
469
|
+
activeIndex = 0;
|
|
470
|
+
render();
|
|
471
|
+
});
|
|
472
|
+
list.addEventListener('click', (event) => {
|
|
473
|
+
if (renaming) return;
|
|
474
|
+
const row = event.target instanceof Element ? event.target.closest('.cmdbar-row') : null;
|
|
475
|
+
if (!row) return;
|
|
476
|
+
const item = visible[Number(row.dataset.index)];
|
|
477
|
+
if (!item) return;
|
|
478
|
+
if (view === 'windows') void selectWindow(item);
|
|
479
|
+
else if (view === 'quickCommands') selectQuickCommand(item);
|
|
480
|
+
else selectItem(item);
|
|
481
|
+
});
|
|
482
|
+
list.addEventListener('mousemove', (event) => {
|
|
483
|
+
if (renaming) return;
|
|
484
|
+
const row = event.target instanceof Element ? event.target.closest('.cmdbar-row') : null;
|
|
485
|
+
if (!row) return;
|
|
486
|
+
activeIndex = Number(row.dataset.index);
|
|
487
|
+
render();
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
document.addEventListener('keydown', (event) => {
|
|
491
|
+
const isShortcut = event.metaKey && !event.ctrlKey && !event.altKey && event.key.toLowerCase() === 'k';
|
|
492
|
+
if (isShortcut) {
|
|
493
|
+
event.preventDefault();
|
|
494
|
+
event.stopPropagation();
|
|
495
|
+
setOpen(!panel.classList.contains('open'));
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
if (!panel.classList.contains('open') || renaming) return;
|
|
499
|
+
|
|
500
|
+
if (event.key === 'Escape') {
|
|
501
|
+
event.preventDefault();
|
|
502
|
+
if (view !== 'root') exitSubview();
|
|
503
|
+
else setOpen(false);
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (event.key === 'ArrowLeft') {
|
|
507
|
+
if (view !== 'root') {
|
|
508
|
+
event.preventDefault();
|
|
509
|
+
exitSubview();
|
|
510
|
+
}
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
if (event.key === 'ArrowRight') {
|
|
514
|
+
if (view === 'root') {
|
|
515
|
+
const item = visible[activeIndex];
|
|
516
|
+
if (item && item.kind === 'action' && item.subView === 'windows' && sessionName) {
|
|
517
|
+
event.preventDefault();
|
|
518
|
+
void enterWindowsView();
|
|
519
|
+
} else if (item && item.kind === 'action' && item.subView === 'quickCommands') {
|
|
520
|
+
event.preventDefault();
|
|
521
|
+
enterQuickCommandsView();
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
if (event.key === 'ArrowDown') {
|
|
527
|
+
event.preventDefault();
|
|
528
|
+
activeIndex = Math.min(activeIndex + 1, visible.length - 1);
|
|
529
|
+
render();
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
if (event.key === 'ArrowUp') {
|
|
533
|
+
event.preventDefault();
|
|
534
|
+
activeIndex = Math.max(activeIndex - 1, 0);
|
|
535
|
+
render();
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
if (event.key === 'Enter') {
|
|
539
|
+
event.preventDefault();
|
|
540
|
+
const item = visible[activeIndex];
|
|
541
|
+
if (!item) return;
|
|
542
|
+
if (view === 'windows') void selectWindow(item);
|
|
543
|
+
else if (view === 'quickCommands') selectQuickCommand(item);
|
|
544
|
+
else selectItem(item);
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
if (view === 'windows' && !event.metaKey && !event.ctrlKey && !event.altKey && event.key === 'r') {
|
|
548
|
+
event.preventDefault();
|
|
549
|
+
const item = visible[activeIndex];
|
|
550
|
+
if (item) startWindowRename(item);
|
|
551
|
+
}
|
|
552
|
+
}, true);
|
|
553
|
+
|
|
554
|
+
render();
|
|
555
|
+
}());`}export{m as buildCommandbarSessions,u as commandbarButtonHTML,l as commandbarCSS,p as commandbarHTML,f as commandbarScript};
|
package/dist/lib/db.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Low as s}from"lowdb";import{JSONFile as e}from"lowdb/node";import{mkdirSync as i}from"node:fs";import{join as r}from"node:path";import{getDataRoot as n}from"./state-paths.js";const o=n();i(o,{recursive:!0});const a=new s(new e(r(o,"db.json")),{notes:[],scheduledTasks:[],triggeredTasks:[],sessionAccess:[],pinnedViews:[],watchedPanes:[],windowLabels:[],sessionWindows:[],windowHistory:[],quickCommands:[]});export{a as db};
|