@sascha384/tic 5.15.1 → 5.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/backends/ado/index.js +1 -1
- package/dist/backends/ado/index.js.map +1 -1
- package/dist/backends/ado/mappers.d.ts +2 -2
- package/dist/backends/ado/mappers.js +7 -2
- package/dist/backends/ado/mappers.js.map +1 -1
- package/dist/backends/files/index.d.ts +6 -0
- package/dist/backends/files/index.js +36 -0
- package/dist/backends/files/index.js.map +1 -1
- package/dist/backends/files/sync.d.ts +2 -2
- package/dist/backends/files/sync.js +9 -7
- package/dist/backends/files/sync.js.map +1 -1
- package/dist/backends/github/index.js +3 -3
- package/dist/backends/github/index.js.map +1 -1
- package/dist/backends/github/mappers.js +2 -1
- package/dist/backends/github/mappers.js.map +1 -1
- package/dist/backends/github/pr-mappers.d.ts +1 -1
- package/dist/backends/github/pr-mappers.js +1 -1
- package/dist/backends/github/pr-mappers.js.map +1 -1
- package/dist/backends/gitlab/index.js +13 -6
- package/dist/backends/gitlab/index.js.map +1 -1
- package/dist/backends/gitlab/mappers.js +2 -1
- package/dist/backends/gitlab/mappers.js.map +1 -1
- package/dist/backends/jira/index.js +2 -2
- package/dist/backends/jira/index.js.map +1 -1
- package/dist/backends/jira/mappers.d.ts +1 -1
- package/dist/backends/jira/mappers.js +5 -4
- package/dist/backends/jira/mappers.js.map +1 -1
- package/dist/backends/local/items.js +9 -2
- package/dist/backends/local/items.js.map +1 -1
- package/dist/backends/shared/api-client.js +9 -1
- package/dist/backends/shared/api-client.js.map +1 -1
- package/dist/backends/types.d.ts +2 -3
- package/dist/backends/types.js +8 -2
- package/dist/backends/types.js.map +1 -1
- package/dist/cli/commands/config.js +0 -1
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/item.js +45 -17
- package/dist/cli/commands/item.js.map +1 -1
- package/dist/cli/commands/mcp.js +24 -9
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/pr.js +12 -6
- package/dist/cli/commands/pr.js.map +1 -1
- package/dist/cli/index.js +13 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/components/AuthPrompt.js +1 -1
- package/dist/components/AutocompleteInput.js +1 -1
- package/dist/components/BranchList.js +4 -2
- package/dist/components/BranchList.js.map +1 -1
- package/dist/components/CommandBar.js +8 -7
- package/dist/components/CommandBar.js.map +1 -1
- package/dist/components/DetailPanel.js +2 -2
- package/dist/components/DetailPanel.js.map +1 -1
- package/dist/components/MarkdownEditor.js +9 -5
- package/dist/components/MarkdownEditor.js.map +1 -1
- package/dist/components/MultiAutocompleteInput.js +1 -1
- package/dist/components/MultiSelectInput.js +1 -1
- package/dist/components/OverlayPanel.js +1 -1
- package/dist/components/Settings.js +2 -2
- package/dist/components/Settings.js.map +1 -1
- package/dist/components/StatusScreen.js +2 -2
- package/dist/components/StatusScreen.js.map +1 -1
- package/dist/components/TextInput.d.ts +12 -0
- package/dist/components/TextInput.js +207 -0
- package/dist/components/TextInput.js.map +1 -0
- package/dist/components/WorkItemForm.js +115 -98
- package/dist/components/WorkItemForm.js.map +1 -1
- package/dist/components/WorkItemList.d.ts +5 -3
- package/dist/components/WorkItemList.js +187 -123
- package/dist/components/WorkItemList.js.map +1 -1
- package/dist/components/buildTree.js +15 -13
- package/dist/components/buildTree.js.map +1 -1
- package/dist/components/fuzzyMatch.js +1 -1
- package/dist/components/fuzzyMatch.js.map +1 -1
- package/dist/components/getMarkedDistribution.d.ts +2 -2
- package/dist/components/getMarkedDistribution.js +1 -1
- package/dist/components/getMarkedDistribution.js.map +1 -1
- package/dist/git.d.ts +2 -1
- package/dist/hooks/useFormValidation.js +31 -21
- package/dist/hooks/useFormValidation.js.map +1 -1
- package/dist/hooks/useForwardDelete.d.ts +10 -0
- package/dist/hooks/useForwardDelete.js +34 -0
- package/dist/hooks/useForwardDelete.js.map +1 -0
- package/dist/implement.js +3 -3
- package/dist/implement.js.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/storage/config.d.ts +0 -1
- package/dist/storage/config.js +0 -6
- package/dist/storage/config.js.map +1 -1
- package/dist/storage/index.d.ts +25 -6
- package/dist/storage/index.js +304 -183
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/mappers.d.ts +1 -1
- package/dist/storage/mappers.js +5 -3
- package/dist/storage/mappers.js.map +1 -1
- package/dist/storage/schema.d.ts +83 -380
- package/dist/storage/schema.js +27 -55
- package/dist/storage/schema.js.map +1 -1
- package/dist/storage/syncQueue.d.ts +2 -3
- package/dist/storage/syncQueue.js +10 -17
- package/dist/storage/syncQueue.js.map +1 -1
- package/dist/storage/undo.js +7 -6
- package/dist/storage/undo.js.map +1 -1
- package/dist/stores/backendDataStore.js +3 -1
- package/dist/stores/backendDataStore.js.map +1 -1
- package/dist/stores/formStackStore.d.ts +1 -1
- package/dist/stores/listViewStore.d.ts +6 -6
- package/dist/stores/navigationStore.d.ts +7 -7
- package/dist/stores/undoStore.d.ts +2 -2
- package/dist/sync/SyncManager.d.ts +6 -1
- package/dist/sync/SyncManager.js +80 -76
- package/dist/sync/SyncManager.js.map +1 -1
- package/dist/sync/types.d.ts +6 -7
- package/dist/test-helpers.d.ts +1 -1
- package/dist/test-helpers.js +11 -8
- package/dist/test-helpers.js.map +1 -1
- package/dist/types.d.ts +6 -5
- package/drizzle/0006_dual_id.sql +173 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
|
@@ -20,6 +20,7 @@ import { OverlayPanel } from './OverlayPanel.js';
|
|
|
20
20
|
import { DetailPanel, countWrappedLines } from './DetailPanel.js';
|
|
21
21
|
import { undoStore } from '../stores/undoStore.js';
|
|
22
22
|
import { editorStore } from '../stores/editorStore.js';
|
|
23
|
+
import { formStackStore } from '../stores/formStackStore.js';
|
|
23
24
|
import { CommandBar } from './CommandBar.js';
|
|
24
25
|
import { isSoftDeleteBackend } from '../backends/types.js';
|
|
25
26
|
import { filterStore, useFilterStore } from '../stores/filterStore.js';
|
|
@@ -36,7 +37,7 @@ function buildWorkItemColumns(capabilities, collapsedIds, selectionFg) {
|
|
|
36
37
|
width: 4, // overridden dynamically via useMemo
|
|
37
38
|
required: true,
|
|
38
39
|
sortable: true,
|
|
39
|
-
render: (ti, selected) => (_jsx(Text, { color: selected ? selectionFg : undefined, bold: selected, dimColor: ti.isCrossType && !selected, children: ti.item.id })),
|
|
40
|
+
render: (ti, selected) => (_jsx(Text, { color: selected ? selectionFg : undefined, bold: selected, dimColor: ti.isCrossType && !selected, children: ti.item.id ?? '\u00B7' })),
|
|
40
41
|
});
|
|
41
42
|
// Title column (flex)
|
|
42
43
|
columns.push({
|
|
@@ -48,7 +49,7 @@ function buildWorkItemColumns(capabilities, collapsedIds, selectionFg) {
|
|
|
48
49
|
render: (ti, selected) => {
|
|
49
50
|
const { item, prefix, isCrossType, hasChildren } = ti;
|
|
50
51
|
const collapseIndicator = hasChildren
|
|
51
|
-
? collapsedIds.has(item.
|
|
52
|
+
? collapsedIds.has(item.rowId)
|
|
52
53
|
? '\u25B6 '
|
|
53
54
|
: '\u25BC '
|
|
54
55
|
: ' ';
|
|
@@ -123,11 +124,26 @@ function buildWorkItemColumns(capabilities, collapsedIds, selectionFg) {
|
|
|
123
124
|
}
|
|
124
125
|
return columns;
|
|
125
126
|
}
|
|
126
|
-
|
|
127
|
+
/** Get display IDs (strings) for target items. Used for backend API calls. */
|
|
128
|
+
export function getTargetIds(markedIds, cursorItem, allItems) {
|
|
129
|
+
if (markedIds.size > 0) {
|
|
130
|
+
// Look up display IDs from rowIds
|
|
131
|
+
const ids = [];
|
|
132
|
+
for (const rowId of markedIds) {
|
|
133
|
+
const item = allItems.find((i) => i.rowId === rowId);
|
|
134
|
+
if (item?.id)
|
|
135
|
+
ids.push(item.id);
|
|
136
|
+
}
|
|
137
|
+
return ids;
|
|
138
|
+
}
|
|
139
|
+
return cursorItem?.id ? [cursorItem.id] : [];
|
|
140
|
+
}
|
|
141
|
+
/** Get rowIds for target items. Used for undo entries and store operations. */
|
|
142
|
+
export function getTargetRowIds(markedIds, cursorItem) {
|
|
127
143
|
if (markedIds.size > 0) {
|
|
128
144
|
return [...markedIds];
|
|
129
145
|
}
|
|
130
|
-
return cursorItem ? [cursorItem.
|
|
146
|
+
return cursorItem ? [cursorItem.rowId] : [];
|
|
131
147
|
}
|
|
132
148
|
export function WorkItemList() {
|
|
133
149
|
const { accent, success, error: errorColor, warning: warningColor, marked, mutedDim, selectionBg, } = useThemeStore((s) => s.colors);
|
|
@@ -249,11 +265,16 @@ export function WorkItemList() {
|
|
|
249
265
|
}
|
|
250
266
|
}, [savedViews, defaultView]);
|
|
251
267
|
const queue = useBackendDataStore((s) => s.queue);
|
|
252
|
-
|
|
268
|
+
/** Look up the rowId for a display ID. Returns -1 if not found. */
|
|
269
|
+
const rowIdOf = (displayId) => {
|
|
270
|
+
const item = allItems.find((i) => i.id === displayId);
|
|
271
|
+
return item?.rowId ?? -1;
|
|
272
|
+
};
|
|
273
|
+
const queueWrite = async (action, itemRowId) => {
|
|
253
274
|
if (queue) {
|
|
254
275
|
await queue.append({
|
|
255
276
|
action,
|
|
256
|
-
|
|
277
|
+
itemRowId,
|
|
257
278
|
timestamp: new Date().toISOString(),
|
|
258
279
|
});
|
|
259
280
|
syncManager?.pushPending().catch((err) => {
|
|
@@ -271,7 +292,7 @@ export function WorkItemList() {
|
|
|
271
292
|
type: 'update',
|
|
272
293
|
label,
|
|
273
294
|
itemSnapshots: snapshots,
|
|
274
|
-
|
|
295
|
+
syncItemRowIds: snapshots.map((s) => s.rowId),
|
|
275
296
|
syncAction: 'update',
|
|
276
297
|
});
|
|
277
298
|
};
|
|
@@ -306,14 +327,16 @@ export function WorkItemList() {
|
|
|
306
327
|
capabilities.relationships,
|
|
307
328
|
sortStack,
|
|
308
329
|
]);
|
|
309
|
-
const parentSuggestions = useMemo(() => allItems
|
|
330
|
+
const parentSuggestions = useMemo(() => allItems
|
|
331
|
+
.filter((item) => item.id !== null)
|
|
332
|
+
.map((item) => `${item.id} - ${item.title}`), [allItems]);
|
|
310
333
|
// Collapse state: set of item IDs that are collapsed (collapsed by default)
|
|
311
334
|
// Track explicitly expanded items (inverse of collapsed).
|
|
312
335
|
// All parents are collapsed by default; expanding removes from this set.
|
|
313
336
|
// expandedIds comes from listViewStore (imported above)
|
|
314
337
|
// Derive collapsedIds: all parents minus explicitly expanded ones
|
|
315
338
|
const collapsedIds = useMemo(() => {
|
|
316
|
-
const parentIds = new Set(fullTree.filter((t) => t.hasChildren).map((t) => t.item.
|
|
339
|
+
const parentIds = new Set(fullTree.filter((t) => t.hasChildren).map((t) => t.item.rowId));
|
|
317
340
|
const collapsed = new Set();
|
|
318
341
|
for (const id of parentIds) {
|
|
319
342
|
if (!expandedIds.has(id)) {
|
|
@@ -331,7 +354,7 @@ export function WorkItemList() {
|
|
|
331
354
|
continue;
|
|
332
355
|
skipDepth = null;
|
|
333
356
|
result.push(t);
|
|
334
|
-
if (collapsedIds.has(t.item.
|
|
357
|
+
if (collapsedIds.has(t.item.rowId)) {
|
|
335
358
|
skipDepth = t.depth;
|
|
336
359
|
}
|
|
337
360
|
}
|
|
@@ -387,14 +410,15 @@ export function WorkItemList() {
|
|
|
387
410
|
}
|
|
388
411
|
if (key.return) {
|
|
389
412
|
const item = treeItems[cursor]?.item;
|
|
390
|
-
if (item) {
|
|
413
|
+
if (item && item.id) {
|
|
414
|
+
const displayId = item.id;
|
|
391
415
|
editorStore.getState().init(item.description ?? '', {
|
|
392
416
|
returnScreen: 'list',
|
|
393
417
|
onSave: (content) => {
|
|
394
418
|
const state = backendDataStore.getState();
|
|
395
419
|
if (state.backend) {
|
|
396
420
|
void state.backend
|
|
397
|
-
.cachedUpdateWorkItem(
|
|
421
|
+
.cachedUpdateWorkItem(displayId, { description: content })
|
|
398
422
|
.then(() => backendDataStore.getState().refresh());
|
|
399
423
|
}
|
|
400
424
|
},
|
|
@@ -450,7 +474,7 @@ export function WorkItemList() {
|
|
|
450
474
|
setCursor(newCursor);
|
|
451
475
|
const start = Math.min(anchor, newCursor);
|
|
452
476
|
const end = Math.max(anchor, newCursor);
|
|
453
|
-
setMarkedIds(new Set(treeItems.slice(start, end + 1).map((t) => t.item.
|
|
477
|
+
setMarkedIds(new Set(treeItems.slice(start, end + 1).map((t) => t.item.rowId)));
|
|
454
478
|
clearWarning();
|
|
455
479
|
}
|
|
456
480
|
if (matchesCommand('list-navigate', input, key)) {
|
|
@@ -486,18 +510,18 @@ export function WorkItemList() {
|
|
|
486
510
|
const current = treeItems[cursor];
|
|
487
511
|
if (current &&
|
|
488
512
|
current.hasChildren &&
|
|
489
|
-
collapsedIds.has(current.item.
|
|
490
|
-
toggleExpanded(current.item.
|
|
513
|
+
collapsedIds.has(current.item.rowId)) {
|
|
514
|
+
toggleExpanded(current.item.rowId);
|
|
491
515
|
}
|
|
492
516
|
}
|
|
493
517
|
if (matchesCommand('list-collapse', input, key) && treeItems.length > 0) {
|
|
494
518
|
const current = treeItems[cursor];
|
|
495
519
|
if (current) {
|
|
496
|
-
if (current.hasChildren && !collapsedIds.has(current.item.
|
|
497
|
-
toggleExpanded(current.item.
|
|
520
|
+
if (current.hasChildren && !collapsedIds.has(current.item.rowId)) {
|
|
521
|
+
toggleExpanded(current.item.rowId);
|
|
498
522
|
}
|
|
499
523
|
else if (current.depth > 0 && current.item.parent) {
|
|
500
|
-
const parentIdx = treeItems.findIndex((t) => t.item.
|
|
524
|
+
const parentIdx = treeItems.findIndex((t) => t.item.rowId === current.item.parent);
|
|
501
525
|
if (parentIdx >= 0)
|
|
502
526
|
setCursor(parentIdx);
|
|
503
527
|
}
|
|
@@ -505,7 +529,7 @@ export function WorkItemList() {
|
|
|
505
529
|
}
|
|
506
530
|
if (matchesCommand('edit', input, key) && treeItems.length > 0) {
|
|
507
531
|
setFormMode('item');
|
|
508
|
-
selectWorkItem(treeItems[cursor].item.
|
|
532
|
+
selectWorkItem(treeItems[cursor].item.rowId);
|
|
509
533
|
navigate('form');
|
|
510
534
|
}
|
|
511
535
|
if (matchesCommand('quit', input, key))
|
|
@@ -513,7 +537,7 @@ export function WorkItemList() {
|
|
|
513
537
|
if (matchesCommand('set-iteration', input, key) &&
|
|
514
538
|
capabilities.iterations &&
|
|
515
539
|
treeItems.length > 0) {
|
|
516
|
-
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
540
|
+
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item, allItems);
|
|
517
541
|
if (targetIds.length > 0) {
|
|
518
542
|
openOverlay({ type: 'iteration-picker', targetIds });
|
|
519
543
|
}
|
|
@@ -547,8 +571,20 @@ export function WorkItemList() {
|
|
|
547
571
|
navigate('form');
|
|
548
572
|
}
|
|
549
573
|
}
|
|
574
|
+
if (matchesCommand('create-child', input, key) &&
|
|
575
|
+
treeItems[cursor] &&
|
|
576
|
+
capabilities.fields.parent) {
|
|
577
|
+
formStackStore.getState().clear();
|
|
578
|
+
navigationStore
|
|
579
|
+
.getState()
|
|
580
|
+
.setCreateChildParentId(treeItems[cursor].item.rowId);
|
|
581
|
+
setFormMode('item');
|
|
582
|
+
setActiveTemplate(null);
|
|
583
|
+
selectWorkItem(null);
|
|
584
|
+
navigate('form');
|
|
585
|
+
}
|
|
550
586
|
if (matchesCommand('delete', input, key) && treeItems.length > 0) {
|
|
551
|
-
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
587
|
+
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item, allItems);
|
|
552
588
|
if (targetIds.length > 0) {
|
|
553
589
|
openOverlay({ type: 'delete-confirm', targetIds });
|
|
554
590
|
}
|
|
@@ -561,38 +597,42 @@ export function WorkItemList() {
|
|
|
561
597
|
if (entry.type === 'delete') {
|
|
562
598
|
if (isSoftDeleteBackend(backend)) {
|
|
563
599
|
for (const snap of entry.itemSnapshots) {
|
|
564
|
-
|
|
600
|
+
if (snap.id)
|
|
601
|
+
await backend.restoreWorkItem(snap.id);
|
|
565
602
|
}
|
|
566
603
|
}
|
|
567
604
|
if (queue) {
|
|
568
|
-
await queue.
|
|
605
|
+
await queue.removeByRowIds(entry.syncItemRowIds, 'delete');
|
|
569
606
|
}
|
|
570
607
|
refreshData();
|
|
571
608
|
setToast(entry.itemSnapshots.length === 1
|
|
572
|
-
? `Restored #${entry.itemSnapshots[0].id}`
|
|
609
|
+
? `Restored #${entry.itemSnapshots[0].id ?? entry.itemSnapshots[0].rowId}`
|
|
573
610
|
: `Restored ${entry.itemSnapshots.length} items`);
|
|
574
611
|
}
|
|
575
612
|
else if (entry.type === 'create') {
|
|
576
|
-
for (const
|
|
577
|
-
|
|
613
|
+
for (const rowId of entry.createdRowIds ?? []) {
|
|
614
|
+
const item = allItems.find((i) => i.rowId === rowId);
|
|
615
|
+
if (item?.id)
|
|
616
|
+
await backend.cachedDeleteWorkItem(item.id);
|
|
578
617
|
}
|
|
579
618
|
if (queue) {
|
|
580
|
-
await queue.
|
|
619
|
+
await queue.removeByRowIds(entry.syncItemRowIds, 'create');
|
|
581
620
|
}
|
|
582
621
|
refreshData();
|
|
583
|
-
setToast((entry.
|
|
584
|
-
? `Undid create #${entry.
|
|
585
|
-
: `Undid create of ${entry.
|
|
622
|
+
setToast((entry.createdRowIds?.length ?? 0) === 1
|
|
623
|
+
? `Undid create #${entry.createdRowIds?.[0]}`
|
|
624
|
+
: `Undid create of ${entry.createdRowIds?.length} items`);
|
|
586
625
|
}
|
|
587
626
|
else if (entry.type === 'update') {
|
|
588
627
|
for (const snap of entry.itemSnapshots) {
|
|
589
|
-
|
|
628
|
+
if (snap.id)
|
|
629
|
+
await backend.cachedUpdateWorkItem(snap.id, snap);
|
|
590
630
|
}
|
|
591
631
|
if (queue) {
|
|
592
|
-
await queue.
|
|
632
|
+
await queue.removeByRowIds(entry.syncItemRowIds, 'update');
|
|
593
633
|
}
|
|
594
634
|
for (const snap of entry.itemSnapshots) {
|
|
595
|
-
await queueWrite('update', snap.
|
|
635
|
+
await queueWrite('update', snap.rowId);
|
|
596
636
|
}
|
|
597
637
|
refreshData();
|
|
598
638
|
setToast(`Undid ${entry.label}`);
|
|
@@ -605,39 +645,44 @@ export function WorkItemList() {
|
|
|
605
645
|
}
|
|
606
646
|
if (matchesCommand('open', input, key) && treeItems.length > 0) {
|
|
607
647
|
setFormMode('item');
|
|
608
|
-
selectWorkItem(treeItems[cursor].item.
|
|
648
|
+
selectWorkItem(treeItems[cursor].item.rowId);
|
|
609
649
|
navigate('form');
|
|
610
650
|
}
|
|
611
651
|
if (matchesCommand('branch', input, key) &&
|
|
612
652
|
gitAvailable &&
|
|
613
653
|
treeItems.length > 0) {
|
|
614
654
|
const item = treeItems[cursor].item;
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
const itemUrl = backend?.getItemUrl(item.id) || '';
|
|
618
|
-
// Suspend terminal for interactive child process
|
|
619
|
-
process.stdin.setRawMode?.(false);
|
|
620
|
-
const result = beginImplementation(item, comments, { branchMode, branchCommand, copyToClipboard }, process.cwd(), { itemUrl });
|
|
621
|
-
// Restore terminal after interactive shell
|
|
622
|
-
process.stdin.setRawMode?.(true);
|
|
623
|
-
console.clear();
|
|
624
|
-
let msg = result.resumed
|
|
625
|
-
? `Resumed work on #${item.id}`
|
|
626
|
-
: `Started work on #${item.id}`;
|
|
627
|
-
if (result.commandFailed) {
|
|
628
|
-
msg += ' (branch command failed, fell back to shell)';
|
|
629
|
-
}
|
|
630
|
-
setWarning(msg);
|
|
655
|
+
if (!item.id) {
|
|
656
|
+
setWarning('Cannot create branch for item without display ID');
|
|
631
657
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
658
|
+
else {
|
|
659
|
+
const comments = item.comments;
|
|
660
|
+
try {
|
|
661
|
+
const itemUrl = backend?.getItemUrl(item.id) || '';
|
|
662
|
+
// Suspend terminal for interactive child process
|
|
663
|
+
process.stdin.setRawMode?.(false);
|
|
664
|
+
const result = beginImplementation(item, comments, { branchMode, branchCommand, copyToClipboard }, process.cwd(), { itemUrl });
|
|
665
|
+
// Restore terminal after interactive shell
|
|
666
|
+
process.stdin.setRawMode?.(true);
|
|
667
|
+
console.clear();
|
|
668
|
+
let msg = result.resumed
|
|
669
|
+
? `Resumed work on #${item.id}`
|
|
670
|
+
: `Started work on #${item.id}`;
|
|
671
|
+
if (result.commandFailed) {
|
|
672
|
+
msg += ' (branch command failed, fell back to shell)';
|
|
673
|
+
}
|
|
674
|
+
setWarning(msg);
|
|
675
|
+
}
|
|
676
|
+
catch (e) {
|
|
677
|
+
process.stdin.setRawMode?.(true);
|
|
678
|
+
console.clear();
|
|
679
|
+
setWarning(e instanceof Error ? e.message : 'Failed to start implementation');
|
|
680
|
+
}
|
|
681
|
+
void backendDataStore
|
|
682
|
+
.getState()
|
|
683
|
+
.reloadItem(item.id)
|
|
684
|
+
.catch(() => { });
|
|
636
685
|
}
|
|
637
|
-
void backendDataStore
|
|
638
|
-
.getState()
|
|
639
|
-
.reloadItem(item.id)
|
|
640
|
-
.catch(() => { });
|
|
641
686
|
}
|
|
642
687
|
if (matchesCommand('status', input, key)) {
|
|
643
688
|
navigate('status');
|
|
@@ -656,7 +701,7 @@ export function WorkItemList() {
|
|
|
656
701
|
setToast('Filters cleared');
|
|
657
702
|
}
|
|
658
703
|
if (matchesCommand('list-status', input, key) && treeItems.length > 0) {
|
|
659
|
-
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
704
|
+
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item, allItems);
|
|
660
705
|
if (targetIds.length > 0) {
|
|
661
706
|
openOverlay({ type: 'status-picker', targetIds });
|
|
662
707
|
}
|
|
@@ -677,7 +722,7 @@ export function WorkItemList() {
|
|
|
677
722
|
prCapabilities.create &&
|
|
678
723
|
treeItems.length > 0) {
|
|
679
724
|
const item = treeItems[cursor]?.item;
|
|
680
|
-
if (item) {
|
|
725
|
+
if (item && item.id) {
|
|
681
726
|
const cwd = process.cwd();
|
|
682
727
|
const currentBranch = getCurrentBranch(cwd);
|
|
683
728
|
const expectedBranch = `tic/${slugify(item.id, item.title)}`;
|
|
@@ -687,7 +732,7 @@ export function WorkItemList() {
|
|
|
687
732
|
.createPullRequest({
|
|
688
733
|
title: item.title,
|
|
689
734
|
sourceBranch,
|
|
690
|
-
linkedItems: [item.
|
|
735
|
+
linkedItems: [item.rowId],
|
|
691
736
|
})
|
|
692
737
|
.then((pr) => {
|
|
693
738
|
setToast(`PR #${String(pr.number)} created`);
|
|
@@ -718,12 +763,12 @@ export function WorkItemList() {
|
|
|
718
763
|
}
|
|
719
764
|
if (matchesCommand('mark', input, key) && treeItems.length > 0) {
|
|
720
765
|
setRangeAnchor(null);
|
|
721
|
-
const
|
|
722
|
-
toggleMarked(
|
|
766
|
+
const itemRowId = treeItems[cursor].item.rowId;
|
|
767
|
+
toggleMarked(itemRowId);
|
|
723
768
|
}
|
|
724
769
|
if (matchesCommand('clear-marks', input, key) && treeItems.length > 0) {
|
|
725
770
|
setRangeAnchor(null);
|
|
726
|
-
const visibleIds = treeItems.map((t) => t.item.
|
|
771
|
+
const visibleIds = treeItems.map((t) => t.item.rowId);
|
|
727
772
|
const allMarked = visibleIds.every((id) => markedIds.has(id));
|
|
728
773
|
if (allMarked) {
|
|
729
774
|
clearMarked();
|
|
@@ -738,7 +783,7 @@ export function WorkItemList() {
|
|
|
738
783
|
if (matchesCommand('set-priority', input, key) &&
|
|
739
784
|
capabilities.fields.priority &&
|
|
740
785
|
treeItems.length > 0) {
|
|
741
|
-
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
786
|
+
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item, allItems);
|
|
742
787
|
if (targetIds.length > 0) {
|
|
743
788
|
openOverlay({ type: 'priority-picker', targetIds });
|
|
744
789
|
}
|
|
@@ -746,7 +791,7 @@ export function WorkItemList() {
|
|
|
746
791
|
if (matchesCommand('list-parent', input, key) &&
|
|
747
792
|
capabilities.fields.parent &&
|
|
748
793
|
treeItems.length > 0) {
|
|
749
|
-
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
794
|
+
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item, allItems);
|
|
750
795
|
if (targetIds.length > 0) {
|
|
751
796
|
openOverlay({ type: 'parent-input', targetIds });
|
|
752
797
|
}
|
|
@@ -754,7 +799,7 @@ export function WorkItemList() {
|
|
|
754
799
|
if (matchesCommand('set-assignee', input, key) &&
|
|
755
800
|
capabilities.fields.assignee &&
|
|
756
801
|
treeItems.length > 0) {
|
|
757
|
-
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
802
|
+
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item, allItems);
|
|
758
803
|
if (targetIds.length > 0) {
|
|
759
804
|
openOverlay({ type: 'assignee-input', targetIds });
|
|
760
805
|
}
|
|
@@ -762,7 +807,7 @@ export function WorkItemList() {
|
|
|
762
807
|
if (matchesCommand('set-labels', input, key) &&
|
|
763
808
|
capabilities.fields.labels &&
|
|
764
809
|
treeItems.length > 0) {
|
|
765
|
-
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
810
|
+
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item, allItems);
|
|
766
811
|
if (targetIds.length > 0) {
|
|
767
812
|
openOverlay({ type: 'labels-input', targetIds });
|
|
768
813
|
}
|
|
@@ -770,7 +815,7 @@ export function WorkItemList() {
|
|
|
770
815
|
if (matchesCommand('set-type', input, key) &&
|
|
771
816
|
capabilities.customTypes &&
|
|
772
817
|
treeItems.length > 0) {
|
|
773
|
-
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
818
|
+
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item, allItems);
|
|
774
819
|
if (targetIds.length > 0) {
|
|
775
820
|
openOverlay({ type: 'type-picker', targetIds });
|
|
776
821
|
}
|
|
@@ -949,20 +994,20 @@ export function WorkItemList() {
|
|
|
949
994
|
if (treeItems[cursor]) {
|
|
950
995
|
navigationStore
|
|
951
996
|
.getState()
|
|
952
|
-
.setCreateChildParentId(treeItems[cursor].item.
|
|
997
|
+
.setCreateChildParentId(treeItems[cursor].item.rowId);
|
|
953
998
|
selectWorkItem(null);
|
|
954
999
|
navigate('form');
|
|
955
1000
|
}
|
|
956
1001
|
break;
|
|
957
1002
|
case 'edit':
|
|
958
1003
|
if (treeItems[cursor]) {
|
|
959
|
-
selectWorkItem(treeItems[cursor].item.
|
|
1004
|
+
selectWorkItem(treeItems[cursor].item.rowId);
|
|
960
1005
|
navigate('form');
|
|
961
1006
|
}
|
|
962
1007
|
break;
|
|
963
1008
|
case 'delete':
|
|
964
1009
|
if (treeItems.length > 0) {
|
|
965
|
-
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
1010
|
+
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item, allItems);
|
|
966
1011
|
if (targetIds.length > 0) {
|
|
967
1012
|
openOverlay({ type: 'delete-confirm', targetIds });
|
|
968
1013
|
}
|
|
@@ -971,37 +1016,44 @@ export function WorkItemList() {
|
|
|
971
1016
|
case 'open':
|
|
972
1017
|
if (treeItems[cursor]) {
|
|
973
1018
|
setFormMode('item');
|
|
974
|
-
selectWorkItem(treeItems[cursor].item.
|
|
1019
|
+
selectWorkItem(treeItems[cursor].item.rowId);
|
|
975
1020
|
navigate('form');
|
|
976
1021
|
}
|
|
977
1022
|
break;
|
|
978
1023
|
case 'branch':
|
|
979
1024
|
if (treeItems[cursor]) {
|
|
980
1025
|
const item = treeItems[cursor].item;
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
const itemUrl = backend?.getItemUrl(item.id) || '';
|
|
984
|
-
process.stdin.setRawMode?.(false);
|
|
985
|
-
const result = beginImplementation(item, comments, { branchMode, branchCommand, copyToClipboard }, process.cwd(), { itemUrl });
|
|
986
|
-
process.stdin.setRawMode?.(true);
|
|
987
|
-
console.clear();
|
|
988
|
-
let msg = result.resumed
|
|
989
|
-
? `Resumed work on #${item.id}`
|
|
990
|
-
: `Started work on #${item.id}`;
|
|
991
|
-
if (result.commandFailed) {
|
|
992
|
-
msg += ' (branch command failed, fell back to shell)';
|
|
993
|
-
}
|
|
994
|
-
setWarning(msg);
|
|
1026
|
+
if (!item.id) {
|
|
1027
|
+
setWarning('Cannot create branch for item without display ID');
|
|
995
1028
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1029
|
+
else {
|
|
1030
|
+
const comments = item.comments;
|
|
1031
|
+
try {
|
|
1032
|
+
const itemUrl = backend?.getItemUrl(item.id) || '';
|
|
1033
|
+
process.stdin.setRawMode?.(false);
|
|
1034
|
+
const result = beginImplementation(item, comments, { branchMode, branchCommand, copyToClipboard }, process.cwd(), { itemUrl });
|
|
1035
|
+
process.stdin.setRawMode?.(true);
|
|
1036
|
+
console.clear();
|
|
1037
|
+
let msg = result.resumed
|
|
1038
|
+
? `Resumed work on #${item.id}`
|
|
1039
|
+
: `Started work on #${item.id}`;
|
|
1040
|
+
if (result.commandFailed) {
|
|
1041
|
+
msg += ' (branch command failed, fell back to shell)';
|
|
1042
|
+
}
|
|
1043
|
+
setWarning(msg);
|
|
1044
|
+
}
|
|
1045
|
+
catch (e) {
|
|
1046
|
+
process.stdin.setRawMode?.(true);
|
|
1047
|
+
console.clear();
|
|
1048
|
+
setWarning(e instanceof Error
|
|
1049
|
+
? e.message
|
|
1050
|
+
: 'Failed to start implementation');
|
|
1051
|
+
}
|
|
1052
|
+
void backendDataStore
|
|
1053
|
+
.getState()
|
|
1054
|
+
.reloadItem(item.id)
|
|
1055
|
+
.catch(() => { });
|
|
1000
1056
|
}
|
|
1001
|
-
void backendDataStore
|
|
1002
|
-
.getState()
|
|
1003
|
-
.reloadItem(item.id)
|
|
1004
|
-
.catch(() => { });
|
|
1005
1057
|
}
|
|
1006
1058
|
break;
|
|
1007
1059
|
case 'sync':
|
|
@@ -1016,7 +1068,7 @@ export function WorkItemList() {
|
|
|
1016
1068
|
break;
|
|
1017
1069
|
case 'set-iteration':
|
|
1018
1070
|
if (treeItems.length > 0) {
|
|
1019
|
-
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
1071
|
+
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item, allItems);
|
|
1020
1072
|
if (targetIds.length > 0) {
|
|
1021
1073
|
openOverlay({ type: 'iteration-picker', targetIds });
|
|
1022
1074
|
}
|
|
@@ -1036,8 +1088,8 @@ export function WorkItemList() {
|
|
|
1036
1088
|
break;
|
|
1037
1089
|
case 'mark':
|
|
1038
1090
|
if (treeItems[cursor]) {
|
|
1039
|
-
const
|
|
1040
|
-
toggleMarked(
|
|
1091
|
+
const itemRowId = treeItems[cursor].item.rowId;
|
|
1092
|
+
toggleMarked(itemRowId);
|
|
1041
1093
|
}
|
|
1042
1094
|
break;
|
|
1043
1095
|
case 'clear-marks':
|
|
@@ -1045,7 +1097,7 @@ export function WorkItemList() {
|
|
|
1045
1097
|
break;
|
|
1046
1098
|
case 'set-priority':
|
|
1047
1099
|
{
|
|
1048
|
-
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
1100
|
+
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item, allItems);
|
|
1049
1101
|
if (targetIds.length > 0) {
|
|
1050
1102
|
openOverlay({ type: 'priority-picker', targetIds });
|
|
1051
1103
|
}
|
|
@@ -1053,7 +1105,7 @@ export function WorkItemList() {
|
|
|
1053
1105
|
break;
|
|
1054
1106
|
case 'set-assignee':
|
|
1055
1107
|
{
|
|
1056
|
-
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
1108
|
+
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item, allItems);
|
|
1057
1109
|
if (targetIds.length > 0) {
|
|
1058
1110
|
openOverlay({ type: 'assignee-input', targetIds });
|
|
1059
1111
|
}
|
|
@@ -1061,7 +1113,7 @@ export function WorkItemList() {
|
|
|
1061
1113
|
break;
|
|
1062
1114
|
case 'set-labels':
|
|
1063
1115
|
{
|
|
1064
|
-
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
1116
|
+
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item, allItems);
|
|
1065
1117
|
if (targetIds.length > 0) {
|
|
1066
1118
|
openOverlay({ type: 'labels-input', targetIds });
|
|
1067
1119
|
}
|
|
@@ -1069,7 +1121,7 @@ export function WorkItemList() {
|
|
|
1069
1121
|
break;
|
|
1070
1122
|
case 'set-type':
|
|
1071
1123
|
{
|
|
1072
|
-
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
1124
|
+
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item, allItems);
|
|
1073
1125
|
if (targetIds.length > 0) {
|
|
1074
1126
|
openOverlay({ type: 'type-picker', targetIds });
|
|
1075
1127
|
}
|
|
@@ -1112,7 +1164,7 @@ export function WorkItemList() {
|
|
|
1112
1164
|
}
|
|
1113
1165
|
};
|
|
1114
1166
|
const handleBulkAction = (action) => {
|
|
1115
|
-
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
|
|
1167
|
+
const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item, allItems);
|
|
1116
1168
|
if (targetIds.length === 0)
|
|
1117
1169
|
return;
|
|
1118
1170
|
switch (action) {
|
|
@@ -1147,7 +1199,7 @@ export function WorkItemList() {
|
|
|
1147
1199
|
: '';
|
|
1148
1200
|
const visibleTreeItems = useMemo(() => treeItems.slice(viewport.start, viewport.end), [treeItems, viewport.start, viewport.end]);
|
|
1149
1201
|
const workItemColumns = useMemo(() => {
|
|
1150
|
-
const maxIdLen = visibleTreeItems.reduce((max, { item }) => Math.max(max, item.id.length), 2);
|
|
1202
|
+
const maxIdLen = visibleTreeItems.reduce((max, { item }) => Math.max(max, (item.id ?? '\u00B7').length), 2);
|
|
1151
1203
|
const cols = buildWorkItemColumns(capabilities, collapsedIds, autoFg(selectionBg));
|
|
1152
1204
|
cols[0].width = maxIdLen + 2;
|
|
1153
1205
|
return cols;
|
|
@@ -1161,7 +1213,7 @@ export function WorkItemList() {
|
|
|
1161
1213
|
? formatIterationDates(it.startDate, it.endDate)
|
|
1162
1214
|
: null;
|
|
1163
1215
|
return dates ? ` ${dates}` : '';
|
|
1164
|
-
})()] }), _jsx(Text, { dimColor: mutedDim, children: ` (${filterCount > 0 ? `${items.length}/${unfilteredCount}` : items.length} item${unfilteredCount === 1 ? '' : 's'})` }), markedCount > 0 && (_jsxs(Text, { color: marked, children: [` ● ${markedCount}`, markedDistribution.above > 0 && ` ↑${markedDistribution.above}`, markedDistribution.below > 0 && ` ↓${markedDistribution.below}`] })), filterCount > 0 && (_jsx(Text, { color: warningColor, children: ` [${filterCount} filter${filterCount === 1 ? '' : 's'}${activeViewName ? `: ${activeViewName}` : ''}]` }))] }) }), _jsx(TableLayout, { items: visibleTreeItems, columns: workItemColumns, cursor: viewport.visibleCursor, terminalWidth: terminalWidth, getKey: (ti) => `${ti.item.
|
|
1216
|
+
})()] }), _jsx(Text, { dimColor: mutedDim, children: ` (${filterCount > 0 ? `${items.length}/${unfilteredCount}` : items.length} item${unfilteredCount === 1 ? '' : 's'})` }), markedCount > 0 && (_jsxs(Text, { color: marked, children: [` ● ${markedCount}`, markedDistribution.above > 0 && ` ↑${markedDistribution.above}`, markedDistribution.below > 0 && ` ↓${markedDistribution.below}`] })), filterCount > 0 && (_jsx(Text, { color: warningColor, children: ` [${filterCount} filter${filterCount === 1 ? '' : 's'}${activeViewName ? `: ${activeViewName}` : ''}]` }))] }) }), _jsx(TableLayout, { items: visibleTreeItems, columns: workItemColumns, cursor: viewport.visibleCursor, terminalWidth: terminalWidth, getKey: (ti) => `${ti.item.rowId}-${ti.item.type}`, isMarked: (ti) => markedIds.has(ti.item.rowId), sortStack: sortStack }), treeItems.length === 0 && !loading && initError && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: errorColor, children: "Failed to connect to backend:" }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: errorColor, children: initError }) }), _jsx(Text, { dimColor: mutedDim, children: "Press , for settings or q to quit." })] })), treeItems.length === 0 && !loading && !initError && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: mutedDim, children: ["No ", activeType ?? 'item', "s in this iteration. Press c to create, / to search all."] }) })), loading && treeItems.length === 0 && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: mutedDim, children: "Loading..." }) })), showDetailPanel && treeItems.length > 0 && treeItems[cursor] && (_jsx(Box, { marginTop: 1, children: _jsx(DetailPanel, { item: treeItems[cursor].item, terminalWidth: terminalWidth, showFullDescription: showFullDescription, descriptionScrollOffset: descriptionScrollOffset, maxDescriptionHeight: maxDescriptionHeight }) })), _jsx(Box, { marginTop: 1, children: showFullDescription ? (_jsxs(Box, { children: [_jsx(Text, { dimColor: mutedDim, children: "\u2191\u2193 scroll space/esc close" }), positionText && _jsxs(Text, { dimColor: mutedDim, children: [" ", positionText] })] })) : activeOverlay?.type === 'command-bar' ? (_jsx(CommandBar, { commands: paletteCommands, onCommand: handleCommandSelect, onCancel: closeOverlay })) : activeOverlay?.type === 'bulk-menu' ? ((() => {
|
|
1165
1217
|
const bulkItems = [];
|
|
1166
1218
|
bulkItems.push({
|
|
1167
1219
|
id: 'status',
|
|
@@ -1239,7 +1291,7 @@ export function WorkItemList() {
|
|
|
1239
1291
|
await backend.cachedUpdateWorkItem(id, {
|
|
1240
1292
|
status: item.value,
|
|
1241
1293
|
});
|
|
1242
|
-
await queueWrite('update', id);
|
|
1294
|
+
await queueWrite('update', rowIdOf(id));
|
|
1243
1295
|
}
|
|
1244
1296
|
for (const id of targetIds) {
|
|
1245
1297
|
await backendDataStore.getState().reloadItem(id);
|
|
@@ -1267,7 +1319,7 @@ export function WorkItemList() {
|
|
|
1267
1319
|
await backend.cachedUpdateWorkItem(id, {
|
|
1268
1320
|
type: item.value,
|
|
1269
1321
|
});
|
|
1270
|
-
await queueWrite('update', id);
|
|
1322
|
+
await queueWrite('update', rowIdOf(id));
|
|
1271
1323
|
}
|
|
1272
1324
|
for (const id of targetIds) {
|
|
1273
1325
|
await backendDataStore.getState().reloadItem(id);
|
|
@@ -1295,7 +1347,7 @@ export function WorkItemList() {
|
|
|
1295
1347
|
pushUpdateUndo(targetIds, 'priority change');
|
|
1296
1348
|
for (const id of targetIds) {
|
|
1297
1349
|
await backend.cachedUpdateWorkItem(id, { priority });
|
|
1298
|
-
await queueWrite('update', id);
|
|
1350
|
+
await queueWrite('update', rowIdOf(id));
|
|
1299
1351
|
}
|
|
1300
1352
|
for (const id of targetIds) {
|
|
1301
1353
|
await backendDataStore.getState().reloadItem(id);
|
|
@@ -1337,16 +1389,21 @@ export function WorkItemList() {
|
|
|
1337
1389
|
return;
|
|
1338
1390
|
void (async () => {
|
|
1339
1391
|
const raw = item.value.trim();
|
|
1340
|
-
const
|
|
1392
|
+
const parentDisplayId = raw.includes(' - ')
|
|
1341
1393
|
? raw.split(' - ')[0].trim()
|
|
1342
1394
|
: raw;
|
|
1395
|
+
const newParent = rowIdOf(parentDisplayId);
|
|
1396
|
+
if (newParent === -1) {
|
|
1397
|
+
setWarning(`Parent "${parentDisplayId}" not found`);
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
1343
1400
|
try {
|
|
1344
1401
|
pushUpdateUndo(targetIds, 'parent change');
|
|
1345
1402
|
for (const id of targetIds) {
|
|
1346
1403
|
await backend.cachedUpdateWorkItem(id, {
|
|
1347
1404
|
parent: newParent,
|
|
1348
1405
|
});
|
|
1349
|
-
await queueWrite('update', id);
|
|
1406
|
+
await queueWrite('update', rowIdOf(id));
|
|
1350
1407
|
}
|
|
1351
1408
|
clearWarning();
|
|
1352
1409
|
}
|
|
@@ -1367,18 +1424,24 @@ export function WorkItemList() {
|
|
|
1367
1424
|
return;
|
|
1368
1425
|
void (async () => {
|
|
1369
1426
|
const raw = text.trim();
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1427
|
+
let newParent = null;
|
|
1428
|
+
if (raw !== '') {
|
|
1429
|
+
const parentDisplayId = raw.includes(' - ')
|
|
1373
1430
|
? raw.split(' - ')[0].trim()
|
|
1374
1431
|
: raw;
|
|
1432
|
+
newParent = rowIdOf(parentDisplayId);
|
|
1433
|
+
if (newParent === -1) {
|
|
1434
|
+
setWarning(`Parent "${parentDisplayId}" not found`);
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1375
1438
|
try {
|
|
1376
1439
|
pushUpdateUndo(targetIds, 'parent change');
|
|
1377
1440
|
for (const id of targetIds) {
|
|
1378
1441
|
await backend.cachedUpdateWorkItem(id, {
|
|
1379
1442
|
parent: newParent,
|
|
1380
1443
|
});
|
|
1381
|
-
await queueWrite('update', id);
|
|
1444
|
+
await queueWrite('update', rowIdOf(id));
|
|
1382
1445
|
}
|
|
1383
1446
|
clearWarning();
|
|
1384
1447
|
}
|
|
@@ -1404,7 +1467,7 @@ export function WorkItemList() {
|
|
|
1404
1467
|
await backend.cachedUpdateWorkItem(id, {
|
|
1405
1468
|
assignee: item.value.trim(),
|
|
1406
1469
|
});
|
|
1407
|
-
await queueWrite('update', id);
|
|
1470
|
+
await queueWrite('update', rowIdOf(id));
|
|
1408
1471
|
}
|
|
1409
1472
|
for (const id of targetIds) {
|
|
1410
1473
|
await backendDataStore.getState().reloadItem(id);
|
|
@@ -1428,7 +1491,7 @@ export function WorkItemList() {
|
|
|
1428
1491
|
await backend.cachedUpdateWorkItem(id, {
|
|
1429
1492
|
assignee: text.trim(),
|
|
1430
1493
|
});
|
|
1431
|
-
await queueWrite('update', id);
|
|
1494
|
+
await queueWrite('update', rowIdOf(id));
|
|
1432
1495
|
}
|
|
1433
1496
|
for (const id of targetIds) {
|
|
1434
1497
|
await backendDataStore.getState().reloadItem(id);
|
|
@@ -1455,7 +1518,7 @@ export function WorkItemList() {
|
|
|
1455
1518
|
const labels = selected.map((i) => i.value);
|
|
1456
1519
|
for (const id of targetIds) {
|
|
1457
1520
|
await backend.cachedUpdateWorkItem(id, { labels });
|
|
1458
|
-
await queueWrite('update', id);
|
|
1521
|
+
await queueWrite('update', rowIdOf(id));
|
|
1459
1522
|
}
|
|
1460
1523
|
for (const id of targetIds) {
|
|
1461
1524
|
await backendDataStore.getState().reloadItem(id);
|
|
@@ -1481,7 +1544,7 @@ export function WorkItemList() {
|
|
|
1481
1544
|
.filter(Boolean);
|
|
1482
1545
|
for (const id of targetIds) {
|
|
1483
1546
|
await backend.cachedUpdateWorkItem(id, { labels });
|
|
1484
|
-
await queueWrite('update', id);
|
|
1547
|
+
await queueWrite('update', rowIdOf(id));
|
|
1485
1548
|
}
|
|
1486
1549
|
for (const id of targetIds) {
|
|
1487
1550
|
await backendDataStore.getState().reloadItem(id);
|
|
@@ -1651,7 +1714,7 @@ export function WorkItemList() {
|
|
|
1651
1714
|
await backend.cachedUpdateWorkItem(id, {
|
|
1652
1715
|
iteration: item.value,
|
|
1653
1716
|
});
|
|
1654
|
-
await queueWrite('update', id);
|
|
1717
|
+
await queueWrite('update', rowIdOf(id));
|
|
1655
1718
|
}
|
|
1656
1719
|
for (const id of targetIds) {
|
|
1657
1720
|
await backendDataStore.getState().reloadItem(id);
|
|
@@ -1684,7 +1747,7 @@ export function WorkItemList() {
|
|
|
1684
1747
|
else {
|
|
1685
1748
|
await backend.cachedDeleteWorkItem(id);
|
|
1686
1749
|
}
|
|
1687
|
-
await queueWrite('delete', id);
|
|
1750
|
+
await queueWrite('delete', rowIdOf(id));
|
|
1688
1751
|
}
|
|
1689
1752
|
if (softDelete) {
|
|
1690
1753
|
const evicted = undoStore.getState().pushUndo({
|
|
@@ -1693,18 +1756,19 @@ export function WorkItemList() {
|
|
|
1693
1756
|
? `deleted #${targetIds[0]}`
|
|
1694
1757
|
: `deleted ${targetIds.length} items`,
|
|
1695
1758
|
itemSnapshots: snapshots,
|
|
1696
|
-
|
|
1759
|
+
syncItemRowIds: snapshots.map((s) => s.rowId),
|
|
1697
1760
|
syncAction: 'delete',
|
|
1698
1761
|
});
|
|
1699
1762
|
if (evicted?.type === 'delete') {
|
|
1700
1763
|
for (const snap of evicted.itemSnapshots) {
|
|
1701
|
-
|
|
1764
|
+
if (snap.id)
|
|
1765
|
+
await backend.permanentlyDeleteWorkItem(snap.id);
|
|
1702
1766
|
}
|
|
1703
1767
|
}
|
|
1704
1768
|
}
|
|
1705
1769
|
closeOverlay();
|
|
1706
1770
|
for (const id of targetIds) {
|
|
1707
|
-
removeDeletedItem(id);
|
|
1771
|
+
removeDeletedItem(rowIdOf(id));
|
|
1708
1772
|
}
|
|
1709
1773
|
setCursor(Math.max(0, cursor - 1));
|
|
1710
1774
|
for (const id of targetIds) {
|