letmecook 0.0.12 → 0.0.14
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/package.json +1 -1
- package/src/flows/add-repos.ts +52 -16
- package/src/flows/edit-session.ts +104 -46
- package/src/flows/new-session.ts +81 -76
- package/src/ui/add-repos.ts +184 -180
- package/src/ui/common/command-runner.ts +249 -0
- package/src/ui/common/footer.ts +105 -0
- package/src/ui/common/keyboard.ts +95 -0
- package/src/ui/confirm-delete.ts +10 -9
- package/src/ui/conflict.ts +10 -1
- package/src/ui/exit.ts +79 -47
- package/src/ui/list.ts +23 -14
- package/src/ui/main-menu.ts +25 -46
- package/src/ui/reclone-prompt.ts +11 -9
- package/src/ui/renderer.ts +16 -4
- package/src/ui/session-details.ts +43 -18
- package/src/ui/session-settings.ts +91 -71
- package/src/ui/skills.ts +44 -100
package/src/ui/add-repos.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { type CliRenderer, TextRenderable, InputRenderable, type KeyEvent } from
|
|
|
2
2
|
import { createBaseLayout, clearLayout } from "./renderer";
|
|
3
3
|
import { parseRepoSpec, type RepoSpec } from "../types";
|
|
4
4
|
import { listRepoHistory } from "../repo-history";
|
|
5
|
+
import { showFooter, hideFooter } from "./common/footer";
|
|
6
|
+
import { isEnter, isEscape, isArrowUp, isArrowDown } from "./common/keyboard";
|
|
5
7
|
|
|
6
8
|
export interface AddReposResult {
|
|
7
9
|
repos: RepoSpec[];
|
|
@@ -11,7 +13,6 @@ export interface AddReposResult {
|
|
|
11
13
|
export async function showAddReposPrompt(renderer: CliRenderer): Promise<AddReposResult> {
|
|
12
14
|
const history = await listRepoHistory();
|
|
13
15
|
const historySpecs = history.map((item) => item.spec);
|
|
14
|
-
let historyIndex = historySpecs.length;
|
|
15
16
|
const maxMatches = 6;
|
|
16
17
|
|
|
17
18
|
return new Promise((resolve) => {
|
|
@@ -21,12 +22,12 @@ export async function showAddReposPrompt(renderer: CliRenderer): Promise<AddRepo
|
|
|
21
22
|
|
|
22
23
|
const repos: RepoSpec[] = [];
|
|
23
24
|
let currentInput = "";
|
|
24
|
-
let
|
|
25
|
-
let
|
|
26
|
-
let
|
|
27
|
-
let
|
|
28
|
-
let
|
|
29
|
-
let
|
|
25
|
+
let currentReadOnly = false;
|
|
26
|
+
let currentLatest = false;
|
|
27
|
+
let currentValidRepo: RepoSpec | null = null;
|
|
28
|
+
let selectedMatchIndex = -1; // -1 means no match selected, user is typing freely
|
|
29
|
+
let lastQuery = ""; // Track the query that generated current matches
|
|
30
|
+
let isNavigating = false; // Flag to prevent input handler from resetting when navigating
|
|
30
31
|
|
|
31
32
|
// Repository input
|
|
32
33
|
const repoLabel = new TextRenderable(renderer, {
|
|
@@ -67,10 +68,10 @@ export async function showAddReposPrompt(renderer: CliRenderer): Promise<AddRepo
|
|
|
67
68
|
});
|
|
68
69
|
content.add(matchesList);
|
|
69
70
|
|
|
70
|
-
//
|
|
71
|
+
// Inline toggles (always visible when repo is valid)
|
|
71
72
|
const detailsLabel = new TextRenderable(renderer, {
|
|
72
73
|
id: "details-label",
|
|
73
|
-
content: "
|
|
74
|
+
content: "",
|
|
74
75
|
fg: "#e2e8f0",
|
|
75
76
|
marginTop: 1,
|
|
76
77
|
});
|
|
@@ -78,7 +79,7 @@ export async function showAddReposPrompt(renderer: CliRenderer): Promise<AddRepo
|
|
|
78
79
|
|
|
79
80
|
const detailsReadOnly = new TextRenderable(renderer, {
|
|
80
81
|
id: "details-readonly",
|
|
81
|
-
content: "
|
|
82
|
+
content: "",
|
|
82
83
|
fg: "#94a3b8",
|
|
83
84
|
marginTop: 0,
|
|
84
85
|
});
|
|
@@ -86,7 +87,7 @@ export async function showAddReposPrompt(renderer: CliRenderer): Promise<AddRepo
|
|
|
86
87
|
|
|
87
88
|
const detailsLatest = new TextRenderable(renderer, {
|
|
88
89
|
id: "details-latest",
|
|
89
|
-
content: "
|
|
90
|
+
content: "",
|
|
90
91
|
fg: "#94a3b8",
|
|
91
92
|
marginTop: 0,
|
|
92
93
|
});
|
|
@@ -98,10 +99,20 @@ export async function showAddReposPrompt(renderer: CliRenderer): Promise<AddRepo
|
|
|
98
99
|
repoInput.insertText(text);
|
|
99
100
|
currentInput = repoInput.value;
|
|
100
101
|
if (currentInput.trim()) {
|
|
101
|
-
|
|
102
|
+
const repo = validateRepo(currentInput.trim());
|
|
103
|
+
currentValidRepo = repo;
|
|
104
|
+
if (repo) {
|
|
105
|
+
currentReadOnly = false;
|
|
106
|
+
currentLatest = false;
|
|
107
|
+
}
|
|
102
108
|
} else {
|
|
103
109
|
statusText.content = "";
|
|
110
|
+
currentValidRepo = null;
|
|
104
111
|
}
|
|
112
|
+
selectedMatchIndex = -1;
|
|
113
|
+
lastQuery = currentInput;
|
|
114
|
+
updateMatches();
|
|
115
|
+
updateDetails();
|
|
105
116
|
event.preventDefault();
|
|
106
117
|
};
|
|
107
118
|
|
|
@@ -114,7 +125,7 @@ export async function showAddReposPrompt(renderer: CliRenderer): Promise<AddRepo
|
|
|
114
125
|
});
|
|
115
126
|
content.add(statusText);
|
|
116
127
|
|
|
117
|
-
// Repos list
|
|
128
|
+
// Repos list with counter
|
|
118
129
|
const reposLabel = new TextRenderable(renderer, {
|
|
119
130
|
id: "repos-label",
|
|
120
131
|
content: "\nAdded repositories:",
|
|
@@ -131,15 +142,6 @@ export async function showAddReposPrompt(renderer: CliRenderer): Promise<AddRepo
|
|
|
131
142
|
});
|
|
132
143
|
content.add(reposList);
|
|
133
144
|
|
|
134
|
-
// Instructions
|
|
135
|
-
const instructions = new TextRenderable(renderer, {
|
|
136
|
-
id: "instructions",
|
|
137
|
-
content: "\n[Enter] Next [Ctrl+D] Continue [↑/↓] History [Tab] Complete [Esc] Exit",
|
|
138
|
-
fg: "#64748b",
|
|
139
|
-
marginTop: 2,
|
|
140
|
-
});
|
|
141
|
-
content.add(instructions);
|
|
142
|
-
|
|
143
145
|
repoInput.focus();
|
|
144
146
|
|
|
145
147
|
function updateReposList() {
|
|
@@ -159,43 +161,37 @@ export async function showAddReposPrompt(renderer: CliRenderer): Promise<AddRepo
|
|
|
159
161
|
}
|
|
160
162
|
|
|
161
163
|
function updateDetails() {
|
|
162
|
-
if (
|
|
164
|
+
if (currentValidRepo) {
|
|
163
165
|
detailsLabel.content = "\nRepository details:";
|
|
164
166
|
detailsLabel.fg = "#e2e8f0";
|
|
165
|
-
detailsReadOnly.content = ` Read-only: ${
|
|
166
|
-
detailsReadOnly.fg =
|
|
167
|
-
detailsLatest.content = ` Latest: ${
|
|
168
|
-
detailsLatest.fg =
|
|
169
|
-
instructions.content =
|
|
170
|
-
"\n[Enter] Add [r] Toggle read-only [l] Toggle latest [Esc] Back";
|
|
167
|
+
detailsReadOnly.content = ` Read-only: ${currentReadOnly ? "Yes [r]" : "No [r]"}`;
|
|
168
|
+
detailsReadOnly.fg = currentReadOnly ? "#f59e0b" : "#94a3b8";
|
|
169
|
+
detailsLatest.content = ` Latest: ${currentLatest ? "Yes [l]" : "No [l]"}`;
|
|
170
|
+
detailsLatest.fg = currentLatest ? "#22d3ee" : "#94a3b8";
|
|
171
171
|
} else {
|
|
172
|
-
detailsLabel.content = "
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
detailsReadOnly.fg = "#475569";
|
|
176
|
-
detailsLatest.content = " Latest: No";
|
|
177
|
-
detailsLatest.fg = "#475569";
|
|
178
|
-
instructions.content =
|
|
179
|
-
"\n[Enter] Next [Ctrl+D] Continue [↑/↓] History [Tab] Complete [Esc] Cancel";
|
|
172
|
+
detailsLabel.content = "";
|
|
173
|
+
detailsReadOnly.content = "";
|
|
174
|
+
detailsLatest.content = "";
|
|
180
175
|
}
|
|
181
176
|
}
|
|
182
177
|
|
|
183
|
-
function
|
|
184
|
-
const
|
|
185
|
-
if (!
|
|
186
|
-
matchesList.content = "(no matches)";
|
|
187
|
-
matchesList.fg = "#64748b";
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
178
|
+
function getMatchesForQuery(query: string): string[] {
|
|
179
|
+
const trimmed = query.trim();
|
|
180
|
+
if (!trimmed) return [];
|
|
190
181
|
|
|
191
|
-
const lowerQuery =
|
|
192
|
-
|
|
182
|
+
const lowerQuery = trimmed.toLowerCase();
|
|
183
|
+
return historySpecs
|
|
193
184
|
.filter((spec) => spec.toLowerCase().startsWith(lowerQuery))
|
|
194
185
|
.toSorted((a, b) => {
|
|
195
186
|
if (a.length !== b.length) return a.length - b.length;
|
|
196
187
|
return a.localeCompare(b);
|
|
197
188
|
})
|
|
198
189
|
.slice(0, maxMatches);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function updateMatches() {
|
|
193
|
+
const query = lastQuery; // Use the last query, not currentInput (which may be a selected match)
|
|
194
|
+
const matches = getMatchesForQuery(query);
|
|
199
195
|
|
|
200
196
|
if (matches.length === 0) {
|
|
201
197
|
matchesList.content = "(no matches)";
|
|
@@ -205,192 +201,200 @@ export async function showAddReposPrompt(renderer: CliRenderer): Promise<AddRepo
|
|
|
205
201
|
|
|
206
202
|
matchesList.content = matches
|
|
207
203
|
.map((spec, index) => {
|
|
208
|
-
const isSelected =
|
|
204
|
+
const isSelected = selectedMatchIndex === index;
|
|
209
205
|
return `${isSelected ? "▶" : " "} ${spec}`;
|
|
210
206
|
})
|
|
211
207
|
.join("\n");
|
|
212
208
|
matchesList.fg = "#94a3b8";
|
|
213
209
|
}
|
|
214
210
|
|
|
215
|
-
function
|
|
216
|
-
const query = value.trim();
|
|
217
|
-
if (!query) return [];
|
|
218
|
-
const lowerQuery = query.toLowerCase();
|
|
219
|
-
return historySpecs
|
|
220
|
-
.filter((spec) => spec.toLowerCase().startsWith(lowerQuery))
|
|
221
|
-
.toSorted((a, b) => {
|
|
222
|
-
if (a.length !== b.length) return a.length - b.length;
|
|
223
|
-
return a.localeCompare(b);
|
|
224
|
-
})
|
|
225
|
-
.slice(0, maxMatches);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function validateAndAddRepo(spec: string): boolean {
|
|
211
|
+
function validateRepo(spec: string): RepoSpec | null {
|
|
229
212
|
try {
|
|
230
|
-
parseRepoSpec(spec);
|
|
213
|
+
const repo = parseRepoSpec(spec);
|
|
231
214
|
statusText.content = "✓ Valid format";
|
|
232
215
|
statusText.fg = "#10b981";
|
|
233
|
-
return
|
|
216
|
+
return repo;
|
|
234
217
|
} catch (error) {
|
|
235
218
|
statusText.content = `✗ ${error instanceof Error ? error.message : "Invalid format"}`;
|
|
236
219
|
statusText.fg = "#ef4444";
|
|
237
|
-
return
|
|
220
|
+
return null;
|
|
238
221
|
}
|
|
239
222
|
}
|
|
240
223
|
|
|
241
|
-
function
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
224
|
+
function selectMatch(matchIndex: number) {
|
|
225
|
+
const matches = getMatchesForQuery(lastQuery);
|
|
226
|
+
if (matchIndex < 0 || matchIndex >= matches.length) return;
|
|
227
|
+
|
|
228
|
+
const selectedMatch = matches[matchIndex];
|
|
229
|
+
if (!selectedMatch) return;
|
|
230
|
+
|
|
231
|
+
selectedMatchIndex = matchIndex;
|
|
232
|
+
isNavigating = true; // Set flag to prevent input handler from resetting
|
|
233
|
+
|
|
234
|
+
// Update input with selected match
|
|
235
|
+
repoInput.value = selectedMatch;
|
|
236
|
+
repoInput.cursorPosition = selectedMatch.length; // Set cursor to end
|
|
237
|
+
currentInput = selectedMatch;
|
|
238
|
+
|
|
239
|
+
// Validate and update details
|
|
240
|
+
const repo = validateRepo(selectedMatch.trim());
|
|
241
|
+
currentValidRepo = repo;
|
|
242
|
+
if (repo) {
|
|
243
|
+
currentReadOnly = false;
|
|
244
|
+
currentLatest = false;
|
|
248
245
|
}
|
|
249
|
-
|
|
246
|
+
|
|
247
|
+
updateMatches(); // Refresh display with new selection (matches stay the same, just highlight changes)
|
|
248
|
+
updateDetails();
|
|
249
|
+
|
|
250
|
+
isNavigating = false; // Reset flag
|
|
250
251
|
}
|
|
251
252
|
|
|
252
|
-
function
|
|
253
|
-
|
|
254
|
-
if (!spec) return;
|
|
255
|
-
|
|
256
|
-
if (validateAndAddRepo(spec)) {
|
|
257
|
-
// Check if already added
|
|
258
|
-
if (repos.some((r) => r.spec === spec)) {
|
|
259
|
-
statusText.content = "⚠️ Repository already added";
|
|
260
|
-
statusText.fg = "#f59e0b";
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
253
|
+
function addCurrentRepo() {
|
|
254
|
+
if (!currentValidRepo) return;
|
|
263
255
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
256
|
+
const spec = currentInput.trim();
|
|
257
|
+
// Check if already added
|
|
258
|
+
if (repos.some((r) => r.spec === spec)) {
|
|
259
|
+
statusText.content = "⚠️ Repository already added";
|
|
260
|
+
statusText.fg = "#f59e0b";
|
|
261
|
+
return;
|
|
270
262
|
}
|
|
271
|
-
}
|
|
272
263
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
repos.push(pendingRepo);
|
|
264
|
+
const repoToAdd = { ...currentValidRepo };
|
|
265
|
+
repoToAdd.readOnly = currentReadOnly;
|
|
266
|
+
repoToAdd.latest = currentLatest;
|
|
267
|
+
repos.push(repoToAdd);
|
|
278
268
|
|
|
279
|
-
pendingRepo = null;
|
|
280
|
-
pendingReadOnly = false;
|
|
281
|
-
pendingLatest = false;
|
|
282
|
-
mode = "spec";
|
|
283
269
|
currentInput = "";
|
|
284
270
|
repoInput.value = "";
|
|
285
|
-
|
|
271
|
+
currentValidRepo = null;
|
|
272
|
+
currentReadOnly = false;
|
|
273
|
+
currentLatest = false;
|
|
286
274
|
updateReposList();
|
|
287
|
-
|
|
275
|
+
lastQuery = "";
|
|
276
|
+
selectedMatchIndex = -1;
|
|
277
|
+
updateMatches();
|
|
288
278
|
updateDetails();
|
|
289
|
-
lastMatchIndex = -1;
|
|
290
|
-
lastMatchQuery = "";
|
|
291
|
-
|
|
292
|
-
statusText.content = "✓ Added successfully";
|
|
293
|
-
statusText.fg = "#10b981";
|
|
294
279
|
|
|
295
|
-
|
|
296
|
-
if (statusText.content?.toString() === "✓ Added successfully") {
|
|
297
|
-
statusText.content = "";
|
|
298
|
-
}
|
|
299
|
-
}, 2000);
|
|
280
|
+
statusText.content = "";
|
|
300
281
|
}
|
|
301
282
|
|
|
302
283
|
const handleKeypress = (key: KeyEvent) => {
|
|
303
|
-
if (
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}
|
|
315
|
-
updateDetails();
|
|
316
|
-
} else if (key.name === "escape" || key.name === "backspace") {
|
|
317
|
-
mode = "spec";
|
|
318
|
-
pendingRepo = null;
|
|
319
|
-
pendingReadOnly = false;
|
|
320
|
-
pendingLatest = false;
|
|
321
|
-
repoInput.focus();
|
|
322
|
-
updateDetails();
|
|
323
|
-
} else if (key.name === "return" || key.name === "enter") {
|
|
324
|
-
confirmAddRepo();
|
|
284
|
+
if (isEscape(key)) {
|
|
285
|
+
cleanup();
|
|
286
|
+
resolve({ repos, cancelled: true });
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Toggle read-only
|
|
291
|
+
if (key.name === "r" && currentValidRepo) {
|
|
292
|
+
currentReadOnly = !currentReadOnly;
|
|
293
|
+
if (!currentReadOnly) {
|
|
294
|
+
currentLatest = false;
|
|
325
295
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
const match = matches[nextIndex];
|
|
348
|
-
if (!match) return;
|
|
349
|
-
repoInput.value = match;
|
|
350
|
-
repoInput.cursorPosition = match.length;
|
|
351
|
-
currentInput = match;
|
|
352
|
-
lastMatchIndex = nextIndex;
|
|
353
|
-
lastMatchQuery = query.toLowerCase();
|
|
354
|
-
validateAndAddRepo(match.trim());
|
|
355
|
-
updateMatches(currentInput, match);
|
|
356
|
-
} else if (key.name === "up") {
|
|
357
|
-
if (historySpecs.length === 0) return;
|
|
358
|
-
historyIndex = Math.max(0, historyIndex - 1);
|
|
359
|
-
const spec = historySpecs[historyIndex];
|
|
360
|
-
if (spec) applyHistorySpec(spec);
|
|
361
|
-
} else if (key.name === "down") {
|
|
362
|
-
if (historySpecs.length === 0) return;
|
|
363
|
-
historyIndex = Math.min(historySpecs.length - 1, historyIndex + 1);
|
|
364
|
-
const spec = historySpecs[historyIndex];
|
|
365
|
-
if (spec) applyHistorySpec(spec);
|
|
366
|
-
} else if (key.name === "d" && key.ctrl) {
|
|
367
|
-
// Ctrl+D to finish
|
|
296
|
+
updateDetails();
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Toggle latest
|
|
301
|
+
if (key.name === "l" && currentValidRepo) {
|
|
302
|
+
currentLatest = !currentLatest;
|
|
303
|
+
if (currentLatest) {
|
|
304
|
+
currentReadOnly = true;
|
|
305
|
+
}
|
|
306
|
+
updateDetails();
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Enter to add repo or continue
|
|
311
|
+
if (isEnter(key)) {
|
|
312
|
+
if (currentInput.trim() && currentValidRepo) {
|
|
313
|
+
addCurrentRepo();
|
|
314
|
+
} else if (!currentInput.trim() && repos.length > 0) {
|
|
315
|
+
// Empty input + repos added = continue
|
|
368
316
|
cleanup();
|
|
369
317
|
resolve({ repos, cancelled: false });
|
|
370
318
|
}
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Arrow keys for navigating matches
|
|
323
|
+
if (isArrowUp(key)) {
|
|
324
|
+
const matches = getMatchesForQuery(lastQuery);
|
|
325
|
+
if (matches.length === 0) return;
|
|
326
|
+
|
|
327
|
+
if (selectedMatchIndex < 0) {
|
|
328
|
+
// Start from last match
|
|
329
|
+
selectedMatchIndex = matches.length - 1;
|
|
330
|
+
} else {
|
|
331
|
+
// Move up
|
|
332
|
+
selectedMatchIndex = Math.max(0, selectedMatchIndex - 1);
|
|
333
|
+
}
|
|
334
|
+
selectMatch(selectedMatchIndex);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (isArrowDown(key)) {
|
|
339
|
+
const matches = getMatchesForQuery(lastQuery);
|
|
340
|
+
if (matches.length === 0) return;
|
|
341
|
+
|
|
342
|
+
if (selectedMatchIndex < 0) {
|
|
343
|
+
// Start from first match
|
|
344
|
+
selectedMatchIndex = 0;
|
|
345
|
+
} else {
|
|
346
|
+
// Move down
|
|
347
|
+
selectedMatchIndex = Math.min(matches.length - 1, selectedMatchIndex + 1);
|
|
348
|
+
}
|
|
349
|
+
selectMatch(selectedMatchIndex);
|
|
350
|
+
return;
|
|
371
351
|
}
|
|
372
352
|
};
|
|
373
353
|
|
|
374
354
|
repoInput.on("input", (value: string) => {
|
|
375
355
|
currentInput = value;
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
356
|
+
|
|
357
|
+
// If we're navigating (programmatically setting value), don't reset selection
|
|
358
|
+
if (isNavigating) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// User is typing - reset selected match index and update query
|
|
363
|
+
selectedMatchIndex = -1;
|
|
364
|
+
lastQuery = value; // Update the query that generates matches
|
|
365
|
+
|
|
379
366
|
if (value.trim()) {
|
|
380
|
-
|
|
367
|
+
const repo = validateRepo(value.trim());
|
|
368
|
+
currentValidRepo = repo;
|
|
369
|
+
if (repo) {
|
|
370
|
+
currentReadOnly = false;
|
|
371
|
+
currentLatest = false;
|
|
372
|
+
}
|
|
381
373
|
} else {
|
|
382
374
|
statusText.content = "";
|
|
375
|
+
currentValidRepo = null;
|
|
383
376
|
}
|
|
384
|
-
updateMatches(
|
|
377
|
+
updateMatches(); // Update matches based on new lastQuery
|
|
378
|
+
updateDetails();
|
|
385
379
|
});
|
|
386
380
|
|
|
387
381
|
const cleanup = () => {
|
|
388
382
|
renderer.keyInput.off("keypress", handleKeypress);
|
|
389
383
|
repoInput.blur();
|
|
384
|
+
hideFooter(renderer);
|
|
390
385
|
clearLayout(renderer);
|
|
391
386
|
};
|
|
392
387
|
|
|
388
|
+
// Show footer with context-aware actions
|
|
389
|
+
showFooter(renderer, content, {
|
|
390
|
+
navigate: true,
|
|
391
|
+
select: true,
|
|
392
|
+
back: true,
|
|
393
|
+
custom: currentValidRepo ? ["r Toggle RO", "l Toggle Latest"] : ["↑↓ Navigate matches"],
|
|
394
|
+
});
|
|
395
|
+
|
|
393
396
|
renderer.keyInput.on("keypress", handleKeypress);
|
|
394
397
|
updateDetails();
|
|
398
|
+
updateReposList();
|
|
395
399
|
});
|
|
396
400
|
}
|