@sascha384/tic 1.15.0 → 1.17.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/.claude-plugin/plugin.json +1 -1
- package/dist/app.d.ts +2 -0
- package/dist/app.js +16 -1
- package/dist/app.js.map +1 -1
- package/dist/backends/availability.d.ts +14 -0
- package/dist/backends/availability.js +41 -0
- package/dist/backends/availability.js.map +1 -0
- package/dist/backends/factory.js +9 -5
- package/dist/backends/factory.js.map +1 -1
- package/dist/backends/jira/config.js +5 -1
- package/dist/backends/jira/config.js.map +1 -1
- package/dist/backends/local/config.d.ts +2 -0
- package/dist/backends/local/config.js +1 -0
- package/dist/backends/local/config.js.map +1 -1
- package/dist/backends/local/index.d.ts +0 -2
- package/dist/backends/local/index.js +31 -31
- package/dist/backends/local/index.js.map +1 -1
- package/dist/components/DefaultPicker.d.ts +8 -0
- package/dist/components/DefaultPicker.js +19 -0
- package/dist/components/DefaultPicker.js.map +1 -0
- package/dist/components/Header.js +3 -4
- package/dist/components/Header.js.map +1 -1
- package/dist/components/IterationPicker.js +9 -3
- package/dist/components/IterationPicker.js.map +1 -1
- package/dist/components/Settings.js +174 -71
- package/dist/components/Settings.js.map +1 -1
- package/dist/components/StatusScreen.js +3 -8
- package/dist/components/StatusScreen.js.map +1 -1
- package/dist/components/WorkItemForm.js +10 -2
- package/dist/components/WorkItemForm.js.map +1 -1
- package/dist/components/WorkItemList.js +152 -194
- package/dist/components/WorkItemList.js.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/stores/backendDataStore.d.ts +24 -0
- package/dist/stores/backendDataStore.js +115 -0
- package/dist/stores/backendDataStore.js.map +1 -0
- package/dist/stores/configStore.d.ts +10 -0
- package/dist/stores/configStore.js +80 -0
- package/dist/stores/configStore.js.map +1 -0
- package/dist/stores/uiStore.d.ts +52 -0
- package/dist/stores/uiStore.js +22 -0
- package/dist/stores/uiStore.js.map +1 -0
- package/dist/update-checker.d.ts +6 -0
- package/dist/update-checker.js +27 -0
- package/dist/update-checker.js.map +1 -0
- package/dist/updater.d.ts +6 -0
- package/dist/updater.js +37 -0
- package/dist/updater.js.map +1 -0
- package/package.json +5 -2
- package/dist/hooks/useBackendData.d.ts +0 -17
- package/dist/hooks/useBackendData.js +0 -98
- package/dist/hooks/useBackendData.js.map +0 -1
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useMemo, useEffect } from 'react';
|
|
2
|
+
import { useState, useMemo, useEffect, useCallback } from 'react';
|
|
3
3
|
import { Box, Text, useInput, useApp } from 'ink';
|
|
4
4
|
import Spinner from 'ink-spinner';
|
|
5
|
-
import TextInput from 'ink-text-input';
|
|
6
5
|
import { useAppState } from '../app.js';
|
|
7
6
|
import { isGitRepo } from '../git.js';
|
|
8
7
|
import { beginImplementation } from '../implement.js';
|
|
9
|
-
import {
|
|
8
|
+
import { useConfigStore } from '../stores/configStore.js';
|
|
9
|
+
import { uiStore, useUIStore, getOverlayTargetIds } from '../stores/uiStore.js';
|
|
10
10
|
import { TableLayout } from './TableLayout.js';
|
|
11
11
|
import { CardLayout } from './CardLayout.js';
|
|
12
12
|
import { useTerminalSize } from '../hooks/useTerminalSize.js';
|
|
13
13
|
import { useScrollViewport } from '../hooks/useScrollViewport.js';
|
|
14
|
-
import {
|
|
14
|
+
import { useBackendDataStore, backendDataStore, } from '../stores/backendDataStore.js';
|
|
15
15
|
import { SyncQueueStore } from '../sync/queue.js';
|
|
16
16
|
import { buildTree } from './buildTree.js';
|
|
17
17
|
import { SearchOverlay } from './SearchOverlay.js';
|
|
@@ -22,6 +22,8 @@ import { PriorityPicker } from './PriorityPicker.js';
|
|
|
22
22
|
import { TemplatePicker } from './TemplatePicker.js';
|
|
23
23
|
import { TypePicker } from './TypePicker.js';
|
|
24
24
|
import { StatusPicker } from './StatusPicker.js';
|
|
25
|
+
import { AutocompleteInput } from './AutocompleteInput.js';
|
|
26
|
+
import { MultiAutocompleteInput } from './MultiAutocompleteInput.js';
|
|
25
27
|
export function getTargetIds(markedIds, cursorItem) {
|
|
26
28
|
if (markedIds.size > 0) {
|
|
27
29
|
return [...markedIds];
|
|
@@ -29,46 +31,36 @@ export function getTargetIds(markedIds, cursorItem) {
|
|
|
29
31
|
return cursorItem ? [cursorItem.id] : [];
|
|
30
32
|
}
|
|
31
33
|
export function WorkItemList() {
|
|
32
|
-
const { backend, syncManager, navigate, navigateToHelp, selectWorkItem, activeType, setActiveType, setActiveTemplate, setFormMode, } = useAppState();
|
|
34
|
+
const { backend, syncManager, navigate, navigateToHelp, selectWorkItem, activeType, setActiveType, setActiveTemplate, setFormMode, updateInfo, } = useAppState();
|
|
35
|
+
const defaultType = useConfigStore((s) => s.config.defaultType ?? null);
|
|
36
|
+
const branchMode = useConfigStore((s) => s.config.branchMode ?? 'worktree');
|
|
33
37
|
const { exit } = useApp();
|
|
34
38
|
const [cursor, setCursor] = useState(0);
|
|
35
|
-
const [confirmDelete, setConfirmDelete] = useState(false);
|
|
36
|
-
const [warning, setWarning] = useState('');
|
|
37
|
-
const [settingParent, setSettingParent] = useState(false);
|
|
38
39
|
const [parentInput, setParentInput] = useState('');
|
|
39
|
-
const [isSearching, setIsSearching] = useState(false);
|
|
40
40
|
const [allSearchItems, setAllSearchItems] = useState([]);
|
|
41
41
|
// Marked items state for bulk operations
|
|
42
42
|
const [markedIds, setMarkedIds] = useState(() => new Set());
|
|
43
|
-
const [deleteTargetIds, setDeleteTargetIds] = useState([]);
|
|
44
|
-
const [parentTargetIds, setParentTargetIds] = useState([]);
|
|
45
|
-
const [showBulkMenu, setShowBulkMenu] = useState(false);
|
|
46
|
-
const [showStatusPicker, setShowStatusPicker] = useState(false);
|
|
47
|
-
const [showTypePicker, setShowTypePicker] = useState(false);
|
|
48
|
-
const [showPriorityPicker, setShowPriorityPicker] = useState(false);
|
|
49
|
-
const [settingAssignee, setSettingAssignee] = useState(false);
|
|
50
43
|
const [assigneeInput, setAssigneeInput] = useState('');
|
|
51
|
-
const [settingLabels, setSettingLabels] = useState(false);
|
|
52
44
|
const [labelsInput, setLabelsInput] = useState('');
|
|
53
|
-
const [bulkTargetIds, setBulkTargetIds] = useState([]);
|
|
54
|
-
const [showCommandPalette, setShowCommandPalette] = useState(false);
|
|
55
|
-
const [showTemplatePicker, setShowTemplatePicker] = useState(false);
|
|
56
45
|
const [templates, setTemplates] = useState([]);
|
|
46
|
+
// UI overlay state from store
|
|
47
|
+
const activeOverlay = useUIStore((s) => s.activeOverlay);
|
|
48
|
+
const warning = useUIStore((s) => s.warning);
|
|
49
|
+
const { openOverlay, closeOverlay, setWarning, clearWarning } = uiStore.getState();
|
|
57
50
|
// Marked count for header display
|
|
58
51
|
const markedCount = markedIds.size;
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}, [syncManager, refreshData]);
|
|
52
|
+
const syncStatus = useBackendDataStore((s) => s.syncStatus);
|
|
53
|
+
const capabilities = useBackendDataStore((s) => s.capabilities);
|
|
54
|
+
const types = useBackendDataStore((s) => s.types);
|
|
55
|
+
const statuses = useBackendDataStore((s) => s.statuses);
|
|
56
|
+
const assignees = useBackendDataStore((s) => s.assignees);
|
|
57
|
+
const labelSuggestions = useBackendDataStore((s) => s.labels);
|
|
58
|
+
const iteration = useBackendDataStore((s) => s.currentIteration);
|
|
59
|
+
const allItems = useBackendDataStore((s) => s.items);
|
|
60
|
+
const loading = useBackendDataStore((s) => s.loading);
|
|
61
|
+
const refreshData = useCallback(() => {
|
|
62
|
+
void backendDataStore.getState().refresh();
|
|
63
|
+
}, []);
|
|
72
64
|
useEffect(() => {
|
|
73
65
|
if (capabilities.templates) {
|
|
74
66
|
void backend.listTemplates().then(setTemplates);
|
|
@@ -93,13 +85,14 @@ export function WorkItemList() {
|
|
|
93
85
|
const gitAvailable = useMemo(() => isGitRepo(process.cwd()), []);
|
|
94
86
|
useEffect(() => {
|
|
95
87
|
if (activeType === null && types.length > 0) {
|
|
96
|
-
setActiveType(types[0]);
|
|
88
|
+
setActiveType(defaultType && types.includes(defaultType) ? defaultType : types[0]);
|
|
97
89
|
}
|
|
98
|
-
}, [activeType, types, setActiveType]);
|
|
90
|
+
}, [activeType, types, setActiveType, defaultType]);
|
|
99
91
|
const items = useMemo(() => allItems.filter((item) => item.type === activeType), [allItems, activeType]);
|
|
100
92
|
const fullTree = useMemo(() => capabilities.relationships
|
|
101
93
|
? buildTree(items, allItems, activeType ?? '')
|
|
102
94
|
: buildTree(items, items, activeType ?? ''), [items, allItems, activeType, capabilities.relationships]);
|
|
95
|
+
const parentSuggestions = useMemo(() => allItems.map((item) => `${item.id} - ${item.title}`), [allItems]);
|
|
103
96
|
// Collapse state: set of item IDs that are collapsed (collapsed by default)
|
|
104
97
|
// Track explicitly expanded items (inverse of collapsed).
|
|
105
98
|
// All parents are collapsed by default; expanding removes from this set.
|
|
@@ -134,7 +127,7 @@ export function WorkItemList() {
|
|
|
134
127
|
setCursor((c) => Math.min(c, Math.max(0, treeItems.length - 1)));
|
|
135
128
|
}, [treeItems.length]);
|
|
136
129
|
useEffect(() => {
|
|
137
|
-
if (
|
|
130
|
+
if (activeOverlay?.type !== 'search')
|
|
138
131
|
return;
|
|
139
132
|
let cancelled = false;
|
|
140
133
|
void backend.listWorkItems().then((items) => {
|
|
@@ -144,7 +137,7 @@ export function WorkItemList() {
|
|
|
144
137
|
return () => {
|
|
145
138
|
cancelled = true;
|
|
146
139
|
};
|
|
147
|
-
}, [
|
|
140
|
+
}, [activeOverlay?.type, backend]);
|
|
148
141
|
const isCardMode = terminalWidth < 80;
|
|
149
142
|
const viewport = useScrollViewport({
|
|
150
143
|
totalItems: treeItems.length,
|
|
@@ -152,67 +145,51 @@ export function WorkItemList() {
|
|
|
152
145
|
chromeLines: 6, // title+margin (2) + table header (1) + help bar margin+text (2) + warning (1)
|
|
153
146
|
linesPerItem: isCardMode ? 3 : 1,
|
|
154
147
|
});
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
if (settingLabels) {
|
|
172
|
-
if (key.escape) {
|
|
173
|
-
setSettingLabels(false);
|
|
174
|
-
setBulkTargetIds([]);
|
|
175
|
-
}
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
if (isSearching)
|
|
179
|
-
return;
|
|
180
|
-
if (showCommandPalette)
|
|
181
|
-
return;
|
|
182
|
-
if (showTemplatePicker)
|
|
148
|
+
// Block 1: Overlay escape handlers for inline inputs
|
|
149
|
+
useInput((_input, key) => {
|
|
150
|
+
if (key.escape) {
|
|
151
|
+
closeOverlay();
|
|
152
|
+
}
|
|
153
|
+
}, {
|
|
154
|
+
isActive: activeOverlay?.type === 'parent-input' ||
|
|
155
|
+
activeOverlay?.type === 'assignee-input' ||
|
|
156
|
+
activeOverlay?.type === 'labels-input',
|
|
157
|
+
});
|
|
158
|
+
// Block 2: Delete confirmation handler
|
|
159
|
+
useInput((input) => {
|
|
160
|
+
if (activeOverlay?.type !== 'delete-confirm')
|
|
183
161
|
return;
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
162
|
+
if (input === 'y' || input === 'Y') {
|
|
163
|
+
const targetIds = activeOverlay.targetIds;
|
|
164
|
+
void (async () => {
|
|
165
|
+
for (const id of targetIds) {
|
|
166
|
+
await backend.cachedDeleteWorkItem(id);
|
|
167
|
+
await queueWrite('delete', id);
|
|
168
|
+
}
|
|
169
|
+
closeOverlay();
|
|
170
|
+
setMarkedIds((prev) => {
|
|
171
|
+
const next = new Set(prev);
|
|
172
|
+
for (const id of targetIds) {
|
|
173
|
+
next.delete(id);
|
|
190
174
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
});
|
|
200
|
-
setCursor((c) => Math.max(0, c - 1));
|
|
201
|
-
refreshData();
|
|
202
|
-
})();
|
|
203
|
-
}
|
|
204
|
-
else {
|
|
205
|
-
setConfirmDelete(false);
|
|
206
|
-
setDeleteTargetIds([]);
|
|
207
|
-
}
|
|
208
|
-
return;
|
|
175
|
+
return next;
|
|
176
|
+
});
|
|
177
|
+
setCursor((c) => Math.max(0, c - 1));
|
|
178
|
+
refreshData();
|
|
179
|
+
})();
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
closeOverlay();
|
|
209
183
|
}
|
|
184
|
+
}, { isActive: activeOverlay?.type === 'delete-confirm' });
|
|
185
|
+
// Block 3: Main input handler — only active when no overlay is open
|
|
186
|
+
useInput((input, key) => {
|
|
210
187
|
if (input === '/') {
|
|
211
|
-
|
|
188
|
+
openOverlay({ type: 'search' });
|
|
212
189
|
return;
|
|
213
190
|
}
|
|
214
191
|
if (input === ':') {
|
|
215
|
-
|
|
192
|
+
openOverlay({ type: 'command-palette' });
|
|
216
193
|
return;
|
|
217
194
|
}
|
|
218
195
|
if (input === '?') {
|
|
@@ -221,31 +198,33 @@ export function WorkItemList() {
|
|
|
221
198
|
}
|
|
222
199
|
if (key.upArrow) {
|
|
223
200
|
setCursor((c) => Math.max(0, c - 1));
|
|
224
|
-
|
|
201
|
+
clearWarning();
|
|
225
202
|
}
|
|
226
203
|
if (key.downArrow) {
|
|
227
204
|
setCursor((c) => Math.min(treeItems.length - 1, c + 1));
|
|
228
|
-
|
|
205
|
+
clearWarning();
|
|
229
206
|
}
|
|
230
207
|
if (key.pageUp) {
|
|
231
208
|
setCursor((c) => Math.max(0, c - viewport.maxVisible));
|
|
232
|
-
|
|
209
|
+
clearWarning();
|
|
233
210
|
}
|
|
234
211
|
if (key.pageDown) {
|
|
235
212
|
setCursor((c) => Math.min(treeItems.length - 1, c + viewport.maxVisible));
|
|
236
|
-
|
|
213
|
+
clearWarning();
|
|
237
214
|
}
|
|
238
215
|
if (key.home) {
|
|
239
216
|
setCursor(0);
|
|
240
|
-
|
|
217
|
+
clearWarning();
|
|
241
218
|
}
|
|
242
219
|
if (key.end) {
|
|
243
220
|
setCursor(treeItems.length - 1);
|
|
244
|
-
|
|
221
|
+
clearWarning();
|
|
245
222
|
}
|
|
246
223
|
if (key.rightArrow && treeItems.length > 0) {
|
|
247
224
|
const current = treeItems[cursor];
|
|
248
|
-
if (current &&
|
|
225
|
+
if (current &&
|
|
226
|
+
current.hasChildren &&
|
|
227
|
+
collapsedIds.has(current.item.id)) {
|
|
249
228
|
setExpandedIds((prev) => new Set(prev).add(current.item.id));
|
|
250
229
|
}
|
|
251
230
|
}
|
|
@@ -279,7 +258,7 @@ export function WorkItemList() {
|
|
|
279
258
|
navigate('settings');
|
|
280
259
|
if (input === 'c') {
|
|
281
260
|
if (capabilities.templates && templates.length > 0) {
|
|
282
|
-
|
|
261
|
+
openOverlay({ type: 'template-picker' });
|
|
283
262
|
}
|
|
284
263
|
else {
|
|
285
264
|
setFormMode('item');
|
|
@@ -291,8 +270,7 @@ export function WorkItemList() {
|
|
|
291
270
|
if (input === 'd' && treeItems.length > 0) {
|
|
292
271
|
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
293
272
|
if (targetIds.length > 0) {
|
|
294
|
-
|
|
295
|
-
setConfirmDelete(true);
|
|
273
|
+
openOverlay({ type: 'delete-confirm', targetIds });
|
|
296
274
|
}
|
|
297
275
|
}
|
|
298
276
|
if (input === 'o' && treeItems.length > 0) {
|
|
@@ -304,9 +282,8 @@ export function WorkItemList() {
|
|
|
304
282
|
if (input === 'b' && gitAvailable && treeItems.length > 0) {
|
|
305
283
|
const item = treeItems[cursor].item;
|
|
306
284
|
const comments = item.comments;
|
|
307
|
-
const config = readConfigSync(process.cwd());
|
|
308
285
|
try {
|
|
309
|
-
const result = beginImplementation(item, comments, { branchMode
|
|
286
|
+
const result = beginImplementation(item, comments, { branchMode }, process.cwd());
|
|
310
287
|
setWarning(result.resumed
|
|
311
288
|
? `Resumed work on #${item.id}`
|
|
312
289
|
: `Started work on #${item.id}`);
|
|
@@ -319,14 +296,10 @@ export function WorkItemList() {
|
|
|
319
296
|
if (input === 's') {
|
|
320
297
|
navigate('status');
|
|
321
298
|
}
|
|
322
|
-
if (input === 'p' &&
|
|
323
|
-
capabilities.fields.parent &&
|
|
324
|
-
treeItems.length > 0 &&
|
|
325
|
-
!settingParent) {
|
|
299
|
+
if (input === 'p' && capabilities.fields.parent && treeItems.length > 0) {
|
|
326
300
|
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
327
301
|
if (targetIds.length > 0) {
|
|
328
|
-
|
|
329
|
-
setSettingParent(true);
|
|
302
|
+
openOverlay({ type: 'parent-input', targetIds });
|
|
330
303
|
// For single item, prefill current parent
|
|
331
304
|
if (targetIds.length === 1) {
|
|
332
305
|
const item = treeItems.find((t) => t.item.id === targetIds[0]);
|
|
@@ -342,7 +315,7 @@ export function WorkItemList() {
|
|
|
342
315
|
const nextType = types[(currentIdx + 1) % types.length];
|
|
343
316
|
setActiveType(nextType);
|
|
344
317
|
setCursor(0);
|
|
345
|
-
|
|
318
|
+
clearWarning();
|
|
346
319
|
}
|
|
347
320
|
if (input === 'r' && syncManager) {
|
|
348
321
|
void syncManager.sync().then(() => {
|
|
@@ -366,46 +339,46 @@ export function WorkItemList() {
|
|
|
366
339
|
setMarkedIds(new Set());
|
|
367
340
|
}
|
|
368
341
|
if (input === 'B' && treeItems.length > 0) {
|
|
369
|
-
|
|
342
|
+
openOverlay({ type: 'bulk-menu' });
|
|
370
343
|
}
|
|
371
|
-
if (input === 'P' &&
|
|
344
|
+
if (input === 'P' &&
|
|
345
|
+
capabilities.fields.priority &&
|
|
346
|
+
treeItems.length > 0) {
|
|
372
347
|
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
373
348
|
if (targetIds.length > 0) {
|
|
374
|
-
|
|
375
|
-
setShowPriorityPicker(true);
|
|
349
|
+
openOverlay({ type: 'priority-picker', targetIds });
|
|
376
350
|
}
|
|
377
351
|
}
|
|
378
|
-
if (input === 'a' &&
|
|
352
|
+
if (input === 'a' &&
|
|
353
|
+
capabilities.fields.assignee &&
|
|
354
|
+
treeItems.length > 0) {
|
|
379
355
|
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
380
356
|
if (targetIds.length > 0) {
|
|
381
|
-
|
|
382
|
-
setSettingAssignee(true);
|
|
357
|
+
openOverlay({ type: 'assignee-input', targetIds });
|
|
383
358
|
setAssigneeInput('');
|
|
384
359
|
}
|
|
385
360
|
}
|
|
386
361
|
if (input === 'l' && capabilities.fields.labels && treeItems.length > 0) {
|
|
387
362
|
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
388
363
|
if (targetIds.length > 0) {
|
|
389
|
-
|
|
390
|
-
setSettingLabels(true);
|
|
364
|
+
openOverlay({ type: 'labels-input', targetIds });
|
|
391
365
|
setLabelsInput('');
|
|
392
366
|
}
|
|
393
367
|
}
|
|
394
368
|
if (input === 't' && capabilities.customTypes && treeItems.length > 0) {
|
|
395
369
|
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
396
370
|
if (targetIds.length > 0) {
|
|
397
|
-
|
|
398
|
-
setShowTypePicker(true);
|
|
371
|
+
openOverlay({ type: 'type-picker', targetIds });
|
|
399
372
|
}
|
|
400
373
|
}
|
|
401
|
-
});
|
|
374
|
+
}, { isActive: activeOverlay === null });
|
|
402
375
|
const handleSearchSelect = (item) => {
|
|
403
|
-
|
|
376
|
+
closeOverlay();
|
|
404
377
|
selectWorkItem(item.id);
|
|
405
378
|
navigate('form');
|
|
406
379
|
};
|
|
407
380
|
const handleSearchCancel = () => {
|
|
408
|
-
|
|
381
|
+
closeOverlay();
|
|
409
382
|
};
|
|
410
383
|
const commandContext = {
|
|
411
384
|
screen: 'list',
|
|
@@ -427,7 +400,7 @@ export function WorkItemList() {
|
|
|
427
400
|
gitAvailable,
|
|
428
401
|
]);
|
|
429
402
|
const handleCommandSelect = (command) => {
|
|
430
|
-
|
|
403
|
+
closeOverlay();
|
|
431
404
|
switch (command.id) {
|
|
432
405
|
case 'create':
|
|
433
406
|
selectWorkItem(null);
|
|
@@ -443,8 +416,7 @@ export function WorkItemList() {
|
|
|
443
416
|
if (treeItems.length > 0) {
|
|
444
417
|
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
445
418
|
if (targetIds.length > 0) {
|
|
446
|
-
|
|
447
|
-
setConfirmDelete(true);
|
|
419
|
+
openOverlay({ type: 'delete-confirm', targetIds });
|
|
448
420
|
}
|
|
449
421
|
}
|
|
450
422
|
break;
|
|
@@ -460,9 +432,8 @@ export function WorkItemList() {
|
|
|
460
432
|
if (treeItems[cursor]) {
|
|
461
433
|
const item = treeItems[cursor].item;
|
|
462
434
|
const comments = item.comments;
|
|
463
|
-
const config = readConfigSync(process.cwd());
|
|
464
435
|
try {
|
|
465
|
-
const result = beginImplementation(item, comments, { branchMode
|
|
436
|
+
const result = beginImplementation(item, comments, { branchMode }, process.cwd());
|
|
466
437
|
setWarning(result.resumed
|
|
467
438
|
? `Resumed work on #${item.id}`
|
|
468
439
|
: `Started work on #${item.id}`);
|
|
@@ -510,8 +481,7 @@ export function WorkItemList() {
|
|
|
510
481
|
{
|
|
511
482
|
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
512
483
|
if (targetIds.length > 0) {
|
|
513
|
-
|
|
514
|
-
setShowPriorityPicker(true);
|
|
484
|
+
openOverlay({ type: 'priority-picker', targetIds });
|
|
515
485
|
}
|
|
516
486
|
}
|
|
517
487
|
break;
|
|
@@ -519,8 +489,7 @@ export function WorkItemList() {
|
|
|
519
489
|
{
|
|
520
490
|
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
521
491
|
if (targetIds.length > 0) {
|
|
522
|
-
|
|
523
|
-
setSettingAssignee(true);
|
|
492
|
+
openOverlay({ type: 'assignee-input', targetIds });
|
|
524
493
|
setAssigneeInput('');
|
|
525
494
|
}
|
|
526
495
|
}
|
|
@@ -529,8 +498,7 @@ export function WorkItemList() {
|
|
|
529
498
|
{
|
|
530
499
|
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
531
500
|
if (targetIds.length > 0) {
|
|
532
|
-
|
|
533
|
-
setSettingLabels(true);
|
|
501
|
+
openOverlay({ type: 'labels-input', targetIds });
|
|
534
502
|
setLabelsInput('');
|
|
535
503
|
}
|
|
536
504
|
}
|
|
@@ -539,13 +507,12 @@ export function WorkItemList() {
|
|
|
539
507
|
{
|
|
540
508
|
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
541
509
|
if (targetIds.length > 0) {
|
|
542
|
-
|
|
543
|
-
setShowTypePicker(true);
|
|
510
|
+
openOverlay({ type: 'type-picker', targetIds });
|
|
544
511
|
}
|
|
545
512
|
}
|
|
546
513
|
break;
|
|
547
514
|
case 'bulk-menu':
|
|
548
|
-
|
|
515
|
+
openOverlay({ type: 'bulk-menu' });
|
|
549
516
|
break;
|
|
550
517
|
case 'quit':
|
|
551
518
|
exit();
|
|
@@ -556,7 +523,7 @@ export function WorkItemList() {
|
|
|
556
523
|
const type = command.id.replace('switch-', '');
|
|
557
524
|
setActiveType(type);
|
|
558
525
|
setCursor(0);
|
|
559
|
-
|
|
526
|
+
clearWarning();
|
|
560
527
|
}
|
|
561
528
|
break;
|
|
562
529
|
}
|
|
@@ -565,141 +532,132 @@ export function WorkItemList() {
|
|
|
565
532
|
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
566
533
|
if (targetIds.length === 0)
|
|
567
534
|
return;
|
|
568
|
-
setBulkTargetIds(targetIds);
|
|
569
535
|
switch (action) {
|
|
570
536
|
case 'status':
|
|
571
|
-
|
|
537
|
+
openOverlay({ type: 'status-picker', targetIds });
|
|
572
538
|
break;
|
|
573
539
|
case 'iteration':
|
|
574
540
|
navigate('iteration-picker');
|
|
575
541
|
break;
|
|
576
542
|
case 'parent':
|
|
577
|
-
|
|
578
|
-
setSettingParent(true);
|
|
543
|
+
openOverlay({ type: 'parent-input', targetIds });
|
|
579
544
|
setParentInput('');
|
|
580
545
|
break;
|
|
581
546
|
case 'type':
|
|
582
|
-
|
|
547
|
+
openOverlay({ type: 'type-picker', targetIds });
|
|
583
548
|
break;
|
|
584
549
|
case 'priority':
|
|
585
|
-
|
|
550
|
+
openOverlay({ type: 'priority-picker', targetIds });
|
|
586
551
|
break;
|
|
587
552
|
case 'assignee':
|
|
588
|
-
|
|
553
|
+
openOverlay({ type: 'assignee-input', targetIds });
|
|
589
554
|
setAssigneeInput('');
|
|
590
555
|
break;
|
|
591
556
|
case 'labels':
|
|
592
|
-
|
|
557
|
+
openOverlay({ type: 'labels-input', targetIds });
|
|
593
558
|
setLabelsInput('');
|
|
594
559
|
break;
|
|
595
560
|
case 'delete':
|
|
596
|
-
|
|
597
|
-
setConfirmDelete(true);
|
|
561
|
+
openOverlay({ type: 'delete-confirm', targetIds });
|
|
598
562
|
break;
|
|
599
563
|
}
|
|
600
564
|
};
|
|
601
565
|
const typeLabel = activeType
|
|
602
566
|
? activeType.charAt(0).toUpperCase() + activeType.slice(1) + 's'
|
|
603
567
|
: '';
|
|
604
|
-
const helpText = '↑↓ navigate
|
|
568
|
+
const helpText = '↑↓ navigate ←→ expand/collapse enter edit c create , settings ? help';
|
|
605
569
|
const visibleTreeItems = treeItems.slice(viewport.start, viewport.end);
|
|
606
|
-
return (_jsxs(Box, { flexDirection: "column", children: [
|
|
607
|
-
|
|
570
|
+
return (_jsxs(Box, { flexDirection: "column", children: [activeOverlay?.type === 'search' && (_jsx(SearchOverlay, { items: allSearchItems, currentIteration: iteration, onSelect: handleSearchSelect, onCancel: handleSearchCancel })), activeOverlay?.type !== 'search' && (_jsxs(_Fragment, { children: [activeOverlay?.type === 'bulk-menu' && (_jsx(BulkMenu, { itemCount: markedIds.size > 0 ? markedIds.size : 1, capabilities: capabilities, onSelect: (action) => {
|
|
571
|
+
closeOverlay();
|
|
608
572
|
handleBulkAction(action);
|
|
609
|
-
}, onCancel: () =>
|
|
573
|
+
}, onCancel: () => closeOverlay() })), activeOverlay?.type === 'command-palette' && (_jsx(CommandPalette, { commands: paletteCommands, onSelect: handleCommandSelect, onCancel: () => closeOverlay() })), activeOverlay?.type === 'status-picker' && (_jsx(StatusPicker, { statuses: statuses, onSelect: (status) => {
|
|
574
|
+
const targetIds = getOverlayTargetIds();
|
|
575
|
+
closeOverlay();
|
|
610
576
|
void (async () => {
|
|
611
|
-
|
|
612
|
-
for (const id of bulkTargetIds) {
|
|
577
|
+
for (const id of targetIds) {
|
|
613
578
|
await backend.cachedUpdateWorkItem(id, { status });
|
|
614
579
|
await queueWrite('update', id);
|
|
615
580
|
}
|
|
616
|
-
setBulkTargetIds([]);
|
|
617
581
|
refreshData();
|
|
618
582
|
})();
|
|
619
|
-
}, onCancel: () => {
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
} })), showTypePicker && (_jsx(TypePicker, { types: types, onSelect: (type) => {
|
|
583
|
+
}, onCancel: () => closeOverlay() })), activeOverlay?.type === 'type-picker' && (_jsx(TypePicker, { types: types, onSelect: (type) => {
|
|
584
|
+
const targetIds = getOverlayTargetIds();
|
|
585
|
+
closeOverlay();
|
|
623
586
|
void (async () => {
|
|
624
|
-
|
|
625
|
-
for (const id of bulkTargetIds) {
|
|
587
|
+
for (const id of targetIds) {
|
|
626
588
|
await backend.cachedUpdateWorkItem(id, { type });
|
|
627
589
|
await queueWrite('update', id);
|
|
628
590
|
}
|
|
629
|
-
setBulkTargetIds([]);
|
|
630
591
|
refreshData();
|
|
631
592
|
})();
|
|
632
|
-
}, onCancel: () => {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
} })), showPriorityPicker && (_jsx(PriorityPicker, { onSelect: (priority) => {
|
|
593
|
+
}, onCancel: () => closeOverlay() })), activeOverlay?.type === 'priority-picker' && (_jsx(PriorityPicker, { onSelect: (priority) => {
|
|
594
|
+
const targetIds = getOverlayTargetIds();
|
|
595
|
+
closeOverlay();
|
|
636
596
|
void (async () => {
|
|
637
|
-
|
|
638
|
-
for (const id of bulkTargetIds) {
|
|
597
|
+
for (const id of targetIds) {
|
|
639
598
|
await backend.cachedUpdateWorkItem(id, { priority });
|
|
640
599
|
await queueWrite('update', id);
|
|
641
600
|
}
|
|
642
|
-
setBulkTargetIds([]);
|
|
643
601
|
refreshData();
|
|
644
602
|
})();
|
|
645
|
-
}, onCancel: () => {
|
|
646
|
-
|
|
647
|
-
setBulkTargetIds([]);
|
|
648
|
-
} })), showTemplatePicker && (_jsx(TemplatePicker, { templates: templates, onSelect: (template) => {
|
|
649
|
-
setShowTemplatePicker(false);
|
|
603
|
+
}, onCancel: () => closeOverlay() })), activeOverlay?.type === 'template-picker' && (_jsx(TemplatePicker, { templates: templates, onSelect: (template) => {
|
|
604
|
+
closeOverlay();
|
|
650
605
|
setFormMode('item');
|
|
651
606
|
setActiveTemplate(template);
|
|
652
607
|
selectWorkItem(null);
|
|
653
608
|
navigate('form');
|
|
654
|
-
}, onCancel: () => {
|
|
655
|
-
|
|
656
|
-
} })), _jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { wrap: "truncate", children: [_jsxs(Text, { bold: true, color: "cyan", children: [typeLabel, " \u2014 ", iteration] }), _jsx(Text, { dimColor: true, children: ` (${items.length} items)` })] }), loading && (_jsx(Box, { marginLeft: 1, children: _jsx(Text, { color: "yellow", children: _jsx(Spinner, { type: "dots" }) }) })), syncStatus && syncStatus.state === 'syncing' ? (_jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { color: "yellow", children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { dimColor: true, children: " Syncing..." })] })) : syncStatus && syncStatus.state === 'error' ? (_jsx(Text, { dimColor: true, children: ` ⚠ Sync failed (${syncStatus.errors.length} errors)` })) : syncStatus && syncStatus.pendingCount > 0 ? (_jsx(Text, { dimColor: true, children: ` ↑ ${syncStatus.pendingCount} pending` })) : syncStatus ? (_jsx(Text, { dimColor: true, children: " \u2713 Synced" })) : null, markedCount > 0 && (_jsx(Text, { color: "magenta", children: ` ● ${markedCount} marked` }))] }), terminalWidth >= 80 ? (_jsx(TableLayout, { treeItems: visibleTreeItems, cursor: viewport.visibleCursor, capabilities: capabilities, collapsedIds: collapsedIds, markedIds: markedIds })) : (_jsx(CardLayout, { treeItems: visibleTreeItems, cursor: viewport.visibleCursor, capabilities: capabilities, collapsedIds: collapsedIds, markedIds: markedIds })), treeItems.length === 0 && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["No ", activeType, "s in this iteration."] }) })), _jsx(Box, { marginTop: 1, children: capabilities.fields.parent && settingParent ? (_jsxs(Box, { children: [_jsxs(Text, { color: "cyan", children: ["Set parent for ", parentTargetIds.length, " item", parentTargetIds.length > 1 ? 's' : '', " (empty to clear):", ' '] }), _jsx(TextInput, { value: parentInput, onChange: setParentInput, focus: true, onSubmit: (value) => {
|
|
609
|
+
}, onCancel: () => closeOverlay() })), _jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { wrap: "truncate", children: [_jsxs(Text, { bold: true, color: "cyan", children: [typeLabel, " \u2014 ", iteration] }), _jsx(Text, { dimColor: true, children: ` (${items.length} items)` })] }), loading && (_jsx(Box, { marginLeft: 1, children: _jsx(Text, { color: "yellow", children: _jsx(Spinner, { type: "dots" }) }) })), syncStatus && syncStatus.state === 'syncing' ? (_jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { color: "yellow", children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { dimColor: true, children: " Syncing..." })] })) : syncStatus && syncStatus.state === 'error' ? (_jsx(Text, { dimColor: true, children: ` ⚠ Sync failed (${syncStatus.errors.length} errors)` })) : syncStatus && syncStatus.pendingCount > 0 ? (_jsx(Text, { dimColor: true, children: ` ↑ ${syncStatus.pendingCount} pending` })) : syncStatus ? (_jsx(Text, { dimColor: true, children: " \u2713 Synced" })) : null, markedCount > 0 && (_jsx(Text, { color: "magenta", children: ` ● ${markedCount} marked` }))] }), terminalWidth >= 80 ? (_jsx(TableLayout, { treeItems: visibleTreeItems, cursor: viewport.visibleCursor, capabilities: capabilities, collapsedIds: collapsedIds, markedIds: markedIds })) : (_jsx(CardLayout, { treeItems: visibleTreeItems, cursor: viewport.visibleCursor, capabilities: capabilities, collapsedIds: collapsedIds, markedIds: markedIds })), treeItems.length === 0 && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["No ", activeType, "s in this iteration."] }) })), _jsx(Box, { marginTop: 1, children: activeOverlay?.type === 'parent-input' ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "cyan", children: ["Set parent for ", activeOverlay.targetIds.length, " item", activeOverlay.targetIds.length > 1 ? 's' : '', " (empty to clear):"] }), _jsx(AutocompleteInput, { value: parentInput, onChange: setParentInput, focus: true, suggestions: parentSuggestions, onSubmit: () => {
|
|
610
|
+
const targetIds = getOverlayTargetIds();
|
|
657
611
|
void (async () => {
|
|
658
|
-
const
|
|
612
|
+
const raw = parentInput.trim();
|
|
613
|
+
const newParent = raw === ''
|
|
614
|
+
? null
|
|
615
|
+
: raw.includes(' - ')
|
|
616
|
+
? raw.split(' - ')[0].trim()
|
|
617
|
+
: raw;
|
|
659
618
|
try {
|
|
660
|
-
for (const id of
|
|
619
|
+
for (const id of targetIds) {
|
|
661
620
|
await backend.cachedUpdateWorkItem(id, {
|
|
662
621
|
parent: newParent,
|
|
663
622
|
});
|
|
664
623
|
await queueWrite('update', id);
|
|
665
624
|
}
|
|
666
|
-
|
|
625
|
+
clearWarning();
|
|
667
626
|
}
|
|
668
627
|
catch (e) {
|
|
669
628
|
setWarning(e instanceof Error ? e.message : 'Invalid parent');
|
|
670
629
|
}
|
|
671
|
-
|
|
630
|
+
closeOverlay();
|
|
672
631
|
setParentInput('');
|
|
673
|
-
setParentTargetIds([]);
|
|
674
632
|
refreshData();
|
|
675
633
|
})();
|
|
676
|
-
} })] })) :
|
|
634
|
+
} })] })) : activeOverlay?.type === 'assignee-input' ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "cyan", children: ["Set assignee for ", activeOverlay.targetIds.length, " item", activeOverlay.targetIds.length > 1 ? 's' : '', ":"] }), _jsx(AutocompleteInput, { value: assigneeInput, onChange: setAssigneeInput, focus: true, suggestions: assignees, onSubmit: () => {
|
|
635
|
+
const targetIds = getOverlayTargetIds();
|
|
636
|
+
closeOverlay();
|
|
677
637
|
void (async () => {
|
|
678
|
-
const assignee =
|
|
679
|
-
for (const id of
|
|
638
|
+
const assignee = assigneeInput.trim();
|
|
639
|
+
for (const id of targetIds) {
|
|
680
640
|
await backend.cachedUpdateWorkItem(id, { assignee });
|
|
681
641
|
await queueWrite('update', id);
|
|
682
642
|
}
|
|
683
|
-
setSettingAssignee(false);
|
|
684
643
|
setAssigneeInput('');
|
|
685
|
-
setBulkTargetIds([]);
|
|
686
644
|
refreshData();
|
|
687
645
|
})();
|
|
688
|
-
} })] })) :
|
|
646
|
+
} })] })) : activeOverlay?.type === 'labels-input' ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "cyan", children: ["Set labels for ", activeOverlay.targetIds.length, " item", activeOverlay.targetIds.length > 1 ? 's' : '', ' ', "(comma-separated):"] }), _jsx(MultiAutocompleteInput, { value: labelsInput, onChange: setLabelsInput, focus: true, suggestions: labelSuggestions, onSubmit: () => {
|
|
647
|
+
const targetIds = getOverlayTargetIds();
|
|
648
|
+
closeOverlay();
|
|
689
649
|
void (async () => {
|
|
690
|
-
const labels =
|
|
650
|
+
const labels = labelsInput
|
|
691
651
|
.split(',')
|
|
692
652
|
.map((l) => l.trim())
|
|
693
653
|
.filter(Boolean);
|
|
694
|
-
for (const id of
|
|
654
|
+
for (const id of targetIds) {
|
|
695
655
|
await backend.cachedUpdateWorkItem(id, { labels });
|
|
696
656
|
await queueWrite('update', id);
|
|
697
657
|
}
|
|
698
|
-
setSettingLabels(false);
|
|
699
658
|
setLabelsInput('');
|
|
700
|
-
setBulkTargetIds([]);
|
|
701
659
|
refreshData();
|
|
702
660
|
})();
|
|
703
|
-
} })] })) :
|
|
661
|
+
} })] })) : activeOverlay?.type === 'delete-confirm' ? (_jsxs(Text, { color: "red", children: ["Delete ", activeOverlay.targetIds.length, " item", activeOverlay.targetIds.length > 1 ? 's' : '', "? (y/n)"] })) : (_jsx(Text, { dimColor: true, children: helpText })) }), warning && (_jsx(Box, { children: _jsxs(Text, { color: "yellow", children: ["\u26A0 ", warning] }) })), updateInfo?.updateAvailable && activeOverlay === null && (_jsx(Box, { children: _jsxs(Text, { color: "yellow", children: ["Update available: ", updateInfo.current, " \u2192 ", updateInfo.latest, ' ', "Press , to update in Settings"] }) }))] }))] }));
|
|
704
662
|
}
|
|
705
663
|
//# sourceMappingURL=WorkItemList.js.map
|