@sascha384/tic 1.28.0 → 1.30.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.
Files changed (41) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dist/commands.d.ts +6 -0
  3. package/dist/commands.js +16 -0
  4. package/dist/commands.js.map +1 -1
  5. package/dist/components/HelpScreen.js +2 -1
  6. package/dist/components/HelpScreen.js.map +1 -1
  7. package/dist/components/OverlayPanel.d.ts +29 -0
  8. package/dist/components/OverlayPanel.js +120 -0
  9. package/dist/components/OverlayPanel.js.map +1 -0
  10. package/dist/components/Settings.js +28 -30
  11. package/dist/components/Settings.js.map +1 -1
  12. package/dist/components/WorkItemList.js +322 -172
  13. package/dist/components/WorkItemList.js.map +1 -1
  14. package/dist/components/fuzzyMatch.d.ts +1 -0
  15. package/dist/components/fuzzyMatch.js +13 -0
  16. package/dist/components/fuzzyMatch.js.map +1 -1
  17. package/package.json +1 -1
  18. package/dist/components/BulkMenu.d.ts +0 -10
  19. package/dist/components/BulkMenu.js +0 -49
  20. package/dist/components/BulkMenu.js.map +0 -1
  21. package/dist/components/CommandPalette.d.ts +0 -13
  22. package/dist/components/CommandPalette.js +0 -70
  23. package/dist/components/CommandPalette.js.map +0 -1
  24. package/dist/components/DefaultPicker.d.ts +0 -8
  25. package/dist/components/DefaultPicker.js +0 -19
  26. package/dist/components/DefaultPicker.js.map +0 -1
  27. package/dist/components/PriorityPicker.d.ts +0 -6
  28. package/dist/components/PriorityPicker.js +0 -18
  29. package/dist/components/PriorityPicker.js.map +0 -1
  30. package/dist/components/SearchOverlay.d.ts +0 -12
  31. package/dist/components/SearchOverlay.js +0 -64
  32. package/dist/components/SearchOverlay.js.map +0 -1
  33. package/dist/components/StatusPicker.d.ts +0 -7
  34. package/dist/components/StatusPicker.js +0 -16
  35. package/dist/components/StatusPicker.js.map +0 -1
  36. package/dist/components/TemplatePicker.d.ts +0 -8
  37. package/dist/components/TemplatePicker.js +0 -24
  38. package/dist/components/TemplatePicker.js.map +0 -1
  39. package/dist/components/TypePicker.d.ts +0 -7
  40. package/dist/components/TypePicker.js +0 -16
  41. package/dist/components/TypePicker.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useState, useMemo, useEffect, useCallback } from 'react';
3
3
  import { Box, Text, useInput, useApp } from 'ink';
4
4
  import { navigationStore, useNavigationStore, } from '../stores/navigationStore.js';
@@ -14,17 +14,9 @@ import { useBackendDataStore, backendDataStore, } from '../stores/backendDataSto
14
14
  import { useShallow } from 'zustand/shallow';
15
15
  import { SyncQueueStore } from '../sync/queue.js';
16
16
  import { buildTree } from './buildTree.js';
17
- import { SearchOverlay } from './SearchOverlay.js';
18
- import { BulkMenu } from './BulkMenu.js';
19
- import { CommandPalette } from './CommandPalette.js';
20
17
  import { getVisibleCommands, } from '../commands.js';
21
- import { PriorityPicker } from './PriorityPicker.js';
22
- import { TemplatePicker } from './TemplatePicker.js';
23
- import { TypePicker } from './TypePicker.js';
24
- import { StatusPicker } from './StatusPicker.js';
18
+ import { OverlayPanel } from './OverlayPanel.js';
25
19
  import { DetailPanel } from './DetailPanel.js';
26
- import { AutocompleteInput } from './AutocompleteInput.js';
27
- import { MultiAutocompleteInput } from './MultiAutocompleteInput.js';
28
20
  export function getTargetIds(markedIds, cursorItem) {
29
21
  if (markedIds.size > 0) {
30
22
  return [...markedIds];
@@ -88,10 +80,7 @@ export function WorkItemList() {
88
80
  })));
89
81
  const { setCursor, toggleExpanded, toggleMarked, clearMarked, clampCursor, removeDeletedItem, } = listViewStore.getState();
90
82
  // Local state for inputs and templates
91
- const [parentInput, setParentInput] = useState('');
92
83
  const [allSearchItems, setAllSearchItems] = useState([]);
93
- const [assigneeInput, setAssigneeInput] = useState('');
94
- const [labelsInput, setLabelsInput] = useState('');
95
84
  const [templates, setTemplates] = useState([]);
96
85
  const [showFullDescription, setShowFullDescription] = useState(false);
97
86
  const [descriptionScrollOffset, setDescriptionScrollOffset] = useState(0);
@@ -229,16 +218,6 @@ export function WorkItemList() {
229
218
  chromeLines,
230
219
  linesPerItem: 1,
231
220
  });
232
- // Block 1: Overlay escape handlers for inline inputs
233
- useInput((_input, key) => {
234
- if (key.escape) {
235
- closeOverlay();
236
- }
237
- }, {
238
- isActive: activeOverlay?.type === 'parent-input' ||
239
- activeOverlay?.type === 'assignee-input' ||
240
- activeOverlay?.type === 'labels-input',
241
- });
242
221
  // Block 1.5: Description scroll handler — active when full description is shown
243
222
  useInput((_input, key) => {
244
223
  if (_input === ' ' || key.escape) {
@@ -254,34 +233,6 @@ export function WorkItemList() {
254
233
  setDescriptionScrollOffset((o) => Math.min(maxScroll, o + 1));
255
234
  }
256
235
  }, { isActive: showFullDescription && activeOverlay === null });
257
- // Block 2: Delete confirmation handler
258
- useInput((input) => {
259
- if (activeOverlay?.type !== 'delete-confirm')
260
- return;
261
- if (input === 'y' || input === 'Y') {
262
- const targetIds = activeOverlay.targetIds;
263
- if (!backend)
264
- return;
265
- void (async () => {
266
- for (const id of targetIds) {
267
- await backend.cachedDeleteWorkItem(id);
268
- await queueWrite('delete', id);
269
- }
270
- closeOverlay();
271
- for (const id of targetIds) {
272
- removeDeletedItem(id);
273
- }
274
- setCursor(Math.max(0, cursor - 1));
275
- refreshData();
276
- setToast(targetIds.length === 1
277
- ? `Item #${targetIds[0]} deleted`
278
- : `${targetIds.length} items deleted`);
279
- })();
280
- }
281
- else {
282
- closeOverlay();
283
- }
284
- }, { isActive: activeOverlay?.type === 'delete-confirm' });
285
236
  // Block 3: Main input handler — only active when no overlay is open
286
237
  useInput((input, key) => {
287
238
  if (input === '/') {
@@ -393,9 +344,15 @@ export function WorkItemList() {
393
344
  }
394
345
  refreshData();
395
346
  }
396
- if (input === 's') {
347
+ if (input === 'S') {
397
348
  navigate('status');
398
349
  }
350
+ if (input === 's' && treeItems.length > 0) {
351
+ const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
352
+ if (targetIds.length > 0) {
353
+ openOverlay({ type: 'status-picker', targetIds });
354
+ }
355
+ }
399
356
  if (input === 'v') {
400
357
  void configStore
401
358
  .getState()
@@ -409,14 +366,6 @@ export function WorkItemList() {
409
366
  const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
410
367
  if (targetIds.length > 0) {
411
368
  openOverlay({ type: 'parent-input', targetIds });
412
- // For single item, prefill current parent
413
- if (targetIds.length === 1) {
414
- const item = treeItems.find((t) => t.item.id === targetIds[0]);
415
- setParentInput(item?.item.parent ?? '');
416
- }
417
- else {
418
- setParentInput('');
419
- }
420
369
  }
421
370
  }
422
371
  if (key.tab && capabilities.customTypes && types.length > 0) {
@@ -455,14 +404,12 @@ export function WorkItemList() {
455
404
  const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
456
405
  if (targetIds.length > 0) {
457
406
  openOverlay({ type: 'assignee-input', targetIds });
458
- setAssigneeInput('');
459
407
  }
460
408
  }
461
409
  if (input === 'l' && capabilities.fields.labels && treeItems.length > 0) {
462
410
  const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
463
411
  if (targetIds.length > 0) {
464
412
  openOverlay({ type: 'labels-input', targetIds });
465
- setLabelsInput('');
466
413
  }
467
414
  }
468
415
  if (input === 't' && capabilities.customTypes && treeItems.length > 0) {
@@ -583,7 +530,6 @@ export function WorkItemList() {
583
530
  const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
584
531
  if (targetIds.length > 0) {
585
532
  openOverlay({ type: 'assignee-input', targetIds });
586
- setAssigneeInput('');
587
533
  }
588
534
  }
589
535
  break;
@@ -592,7 +538,6 @@ export function WorkItemList() {
592
538
  const targetIds = getTargetIds(markedIds, treeItems[cursor]?.item);
593
539
  if (targetIds.length > 0) {
594
540
  openOverlay({ type: 'labels-input', targetIds });
595
- setLabelsInput('');
596
541
  }
597
542
  }
598
543
  break;
@@ -634,7 +579,6 @@ export function WorkItemList() {
634
579
  break;
635
580
  case 'parent':
636
581
  openOverlay({ type: 'parent-input', targetIds });
637
- setParentInput('');
638
582
  break;
639
583
  case 'type':
640
584
  openOverlay({ type: 'type-picker', targetIds });
@@ -644,11 +588,9 @@ export function WorkItemList() {
644
588
  break;
645
589
  case 'assignee':
646
590
  openOverlay({ type: 'assignee-input', targetIds });
647
- setAssigneeInput('');
648
591
  break;
649
592
  case 'labels':
650
593
  openOverlay({ type: 'labels-input', targetIds });
651
- setLabelsInput('');
652
594
  break;
653
595
  case 'delete':
654
596
  openOverlay({ type: 'delete-confirm', targetIds });
@@ -662,128 +604,336 @@ export function WorkItemList() {
662
604
  const positionText = treeItems.length > viewport.maxVisible
663
605
  ? `${cursor + 1}/${treeItems.length}`
664
606
  : '';
665
- 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) => {
666
- closeOverlay();
667
- handleBulkAction(action);
668
- }, onCancel: () => closeOverlay() })), activeOverlay?.type === 'command-palette' && (_jsx(CommandPalette, { commands: paletteCommands, onSelect: handleCommandSelect, onCancel: () => closeOverlay() })), activeOverlay?.type === 'status-picker' && (_jsx(StatusPicker, { statuses: statuses, onSelect: (status) => {
669
- const targetIds = getOverlayTargetIds();
607
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(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)` }), markedCount > 0 && (_jsx(Text, { color: "magenta", children: ` ● ${markedCount} marked` }))] }) }), _jsx(TableLayout, { treeItems: visibleTreeItems, cursor: viewport.visibleCursor, capabilities: capabilities, collapsedIds: collapsedIds, markedIds: markedIds, terminalWidth: terminalWidth }), treeItems.length === 0 && !loading && initError && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "red", children: "Failed to connect to backend:" }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "red", children: initError }) }), _jsx(Text, { dimColor: true, children: "Press , for settings or q to quit." })] })), treeItems.length === 0 && !loading && !initError && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, 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: true, children: "Loading..." }) })), showDetailPanel && treeItems.length > 0 && treeItems[cursor] && (_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: true, children: "\u2191\u2193 scroll space/esc close" }), positionText && _jsxs(Text, { dimColor: true, children: [" ", positionText] })] })) : activeOverlay?.type === 'search' ? ((() => {
608
+ const searchItems = allSearchItems.map((item) => ({
609
+ id: item.id,
610
+ label: `#${item.id} ${item.title}`,
611
+ value: item.id,
612
+ hint: item.type,
613
+ category: item.iteration === iteration
614
+ ? 'Current iteration'
615
+ : (item.iteration ?? 'No iteration'),
616
+ }));
617
+ return (_jsx(OverlayPanel, { title: "Search", items: searchItems, placeholder: "Type to search...", onSelect: (selected) => {
618
+ const item = allSearchItems.find((i) => i.id === selected.value);
619
+ if (item)
620
+ handleSearchSelect(item);
621
+ }, onCancel: handleSearchCancel }));
622
+ })()) : activeOverlay?.type === 'bulk-menu' ? ((() => {
623
+ const bulkItems = [];
624
+ bulkItems.push({
625
+ id: 'status',
626
+ label: 'Set status...',
627
+ value: 'status',
628
+ hint: 's',
629
+ });
630
+ if (capabilities.iterations) {
631
+ bulkItems.push({
632
+ id: 'iteration',
633
+ label: 'Set iteration...',
634
+ value: 'iteration',
635
+ hint: 'i',
636
+ });
637
+ }
638
+ if (capabilities.fields.parent) {
639
+ bulkItems.push({
640
+ id: 'parent',
641
+ label: 'Set parent...',
642
+ value: 'parent',
643
+ hint: 'p',
644
+ });
645
+ }
646
+ if (capabilities.customTypes) {
647
+ bulkItems.push({
648
+ id: 'type',
649
+ label: 'Set type...',
650
+ value: 'type',
651
+ hint: 't',
652
+ });
653
+ }
654
+ if (capabilities.fields.priority) {
655
+ bulkItems.push({
656
+ id: 'priority',
657
+ label: 'Set priority...',
658
+ value: 'priority',
659
+ hint: 'P',
660
+ });
661
+ }
662
+ if (capabilities.fields.assignee) {
663
+ bulkItems.push({
664
+ id: 'assignee',
665
+ label: 'Set assignee...',
666
+ value: 'assignee',
667
+ hint: 'a',
668
+ });
669
+ }
670
+ if (capabilities.fields.labels) {
671
+ bulkItems.push({
672
+ id: 'labels',
673
+ label: 'Set labels...',
674
+ value: 'labels',
675
+ hint: 'l',
676
+ });
677
+ }
678
+ bulkItems.push({
679
+ id: 'delete',
680
+ label: 'Delete',
681
+ value: 'delete',
682
+ hint: 'd',
683
+ });
684
+ const count = markedIds.size > 0 ? markedIds.size : 1;
685
+ return (_jsx(OverlayPanel, { title: `Bulk Actions (${count} ${count === 1 ? 'item' : 'items'})`, items: bulkItems, onSelect: (item) => {
670
686
  closeOverlay();
671
- if (!backend)
672
- return;
673
- void (async () => {
687
+ handleBulkAction(item.value);
688
+ }, onCancel: () => closeOverlay() }));
689
+ })()) : activeOverlay?.type === 'command-palette' ? (_jsx(OverlayPanel, { title: "Commands", items: paletteCommands.map((cmd) => ({
690
+ id: cmd.id,
691
+ label: cmd.label,
692
+ value: cmd.id,
693
+ hint: cmd.shortcut,
694
+ category: cmd.category,
695
+ })), placeholder: "Type a command...", onSelect: (item) => {
696
+ const cmd = paletteCommands.find((c) => c.id === item.value);
697
+ if (cmd)
698
+ handleCommandSelect(cmd);
699
+ }, onCancel: () => closeOverlay() })) : activeOverlay?.type === 'status-picker' ? (_jsx(OverlayPanel, { title: "Set Status", items: statuses.map((s) => ({ id: s, label: s, value: s })), onSelect: (item) => {
700
+ const targetIds = getOverlayTargetIds();
701
+ closeOverlay();
702
+ if (!backend)
703
+ return;
704
+ void (async () => {
705
+ for (const id of targetIds) {
706
+ await backend.cachedUpdateWorkItem(id, {
707
+ status: item.value,
708
+ });
709
+ await queueWrite('update', id);
710
+ }
711
+ refreshData();
712
+ setToast(targetIds.length === 1
713
+ ? 'Status updated'
714
+ : `${targetIds.length} items updated`);
715
+ })();
716
+ }, onCancel: () => closeOverlay() })) : activeOverlay?.type === 'type-picker' ? (_jsx(OverlayPanel, { title: "Set Type", items: types.map((t) => ({
717
+ id: t,
718
+ label: t.charAt(0).toUpperCase() + t.slice(1),
719
+ value: t,
720
+ })), onSelect: (item) => {
721
+ const targetIds = getOverlayTargetIds();
722
+ closeOverlay();
723
+ if (!backend)
724
+ return;
725
+ void (async () => {
726
+ for (const id of targetIds) {
727
+ await backend.cachedUpdateWorkItem(id, {
728
+ type: item.value,
729
+ });
730
+ await queueWrite('update', id);
731
+ }
732
+ refreshData();
733
+ setToast(targetIds.length === 1
734
+ ? 'Type updated'
735
+ : `${targetIds.length} items updated`);
736
+ })();
737
+ }, onCancel: () => closeOverlay() })) : activeOverlay?.type === 'priority-picker' ? (_jsx(OverlayPanel, { title: "Set Priority", items: [
738
+ { id: 'critical', label: 'Critical', value: 'critical' },
739
+ { id: 'high', label: 'High', value: 'high' },
740
+ { id: 'medium', label: 'Medium', value: 'medium' },
741
+ { id: 'low', label: 'Low', value: 'low' },
742
+ ], onSelect: (item) => {
743
+ const targetIds = getOverlayTargetIds();
744
+ closeOverlay();
745
+ if (!backend)
746
+ return;
747
+ const priority = item.value;
748
+ void (async () => {
749
+ for (const id of targetIds) {
750
+ await backend.cachedUpdateWorkItem(id, { priority });
751
+ await queueWrite('update', id);
752
+ }
753
+ refreshData();
754
+ setToast(targetIds.length === 1
755
+ ? 'Priority updated'
756
+ : `${targetIds.length} items updated`);
757
+ })();
758
+ }, onCancel: () => closeOverlay() })) : activeOverlay?.type === 'template-picker' ? (_jsx(OverlayPanel, { title: "Select Template", items: [
759
+ { id: '__none__', label: 'No template', value: '__none__' },
760
+ ...templates.map((t) => ({
761
+ id: t.slug,
762
+ label: t.name,
763
+ value: t.slug,
764
+ })),
765
+ ], onSelect: (item) => {
766
+ closeOverlay();
767
+ setFormMode('item');
768
+ if (item.value === '__none__') {
769
+ setActiveTemplate(null);
770
+ }
771
+ else {
772
+ const template = templates.find((t) => t.slug === item.value);
773
+ setActiveTemplate(template ?? null);
774
+ }
775
+ selectWorkItem(null);
776
+ navigate('form');
777
+ }, onCancel: () => closeOverlay() })) : activeOverlay?.type === 'parent-input' ? (_jsx(OverlayPanel, { title: `Set Parent (${activeOverlay.targetIds.length} item${activeOverlay.targetIds.length > 1 ? 's' : ''})`, items: parentSuggestions.map((s) => ({
778
+ id: s,
779
+ label: s,
780
+ value: s,
781
+ })), allowFreeform: true, onSelect: (item) => {
782
+ const targetIds = getOverlayTargetIds();
783
+ if (!backend)
784
+ return;
785
+ void (async () => {
786
+ const raw = item.value.trim();
787
+ const newParent = raw.includes(' - ')
788
+ ? raw.split(' - ')[0].trim()
789
+ : raw;
790
+ try {
674
791
  for (const id of targetIds) {
675
- await backend.cachedUpdateWorkItem(id, { status });
792
+ await backend.cachedUpdateWorkItem(id, {
793
+ parent: newParent,
794
+ });
676
795
  await queueWrite('update', id);
677
796
  }
678
- refreshData();
679
- setToast(targetIds.length === 1
680
- ? 'Status updated'
681
- : `${targetIds.length} items updated`);
682
- })();
683
- }, onCancel: () => closeOverlay() })), activeOverlay?.type === 'type-picker' && (_jsx(TypePicker, { types: types, onSelect: (type) => {
684
- const targetIds = getOverlayTargetIds();
797
+ clearWarning();
798
+ }
799
+ catch (e) {
800
+ setWarning(e instanceof Error ? e.message : 'Invalid parent');
801
+ }
685
802
  closeOverlay();
686
- if (!backend)
687
- return;
688
- void (async () => {
803
+ refreshData();
804
+ setToast(targetIds.length === 1
805
+ ? 'Parent updated'
806
+ : `${targetIds.length} items updated`);
807
+ })();
808
+ }, onSubmitFreeform: (text) => {
809
+ const targetIds = getOverlayTargetIds();
810
+ if (!backend)
811
+ return;
812
+ void (async () => {
813
+ const raw = text.trim();
814
+ const newParent = raw === ''
815
+ ? null
816
+ : raw.includes(' - ')
817
+ ? raw.split(' - ')[0].trim()
818
+ : raw;
819
+ try {
689
820
  for (const id of targetIds) {
690
- await backend.cachedUpdateWorkItem(id, { type });
821
+ await backend.cachedUpdateWorkItem(id, {
822
+ parent: newParent,
823
+ });
691
824
  await queueWrite('update', id);
692
825
  }
693
- refreshData();
694
- setToast(targetIds.length === 1
695
- ? 'Type updated'
696
- : `${targetIds.length} items updated`);
697
- })();
698
- }, onCancel: () => closeOverlay() })), activeOverlay?.type === 'priority-picker' && (_jsx(PriorityPicker, { onSelect: (priority) => {
699
- const targetIds = getOverlayTargetIds();
826
+ clearWarning();
827
+ }
828
+ catch (e) {
829
+ setWarning(e instanceof Error ? e.message : 'Invalid parent');
830
+ }
700
831
  closeOverlay();
832
+ refreshData();
833
+ setToast(targetIds.length === 1
834
+ ? 'Parent updated'
835
+ : `${targetIds.length} items updated`);
836
+ })();
837
+ }, onCancel: () => closeOverlay(), placeholder: "Type parent ID or title...", emptyMessage: "Type a parent ID (empty to clear)" })) : activeOverlay?.type === 'assignee-input' ? (_jsx(OverlayPanel, { title: `Set Assignee (${activeOverlay.targetIds.length} item${activeOverlay.targetIds.length > 1 ? 's' : ''})`, items: assignees.map((a) => ({ id: a, label: a, value: a })), allowFreeform: true, onSelect: (item) => {
838
+ const targetIds = getOverlayTargetIds();
839
+ closeOverlay();
840
+ if (!backend)
841
+ return;
842
+ void (async () => {
843
+ for (const id of targetIds) {
844
+ await backend.cachedUpdateWorkItem(id, {
845
+ assignee: item.value.trim(),
846
+ });
847
+ await queueWrite('update', id);
848
+ }
849
+ refreshData();
850
+ setToast(targetIds.length === 1
851
+ ? 'Assignee updated'
852
+ : `${targetIds.length} items updated`);
853
+ })();
854
+ }, onSubmitFreeform: (text) => {
855
+ const targetIds = getOverlayTargetIds();
856
+ closeOverlay();
857
+ if (!backend)
858
+ return;
859
+ void (async () => {
860
+ for (const id of targetIds) {
861
+ await backend.cachedUpdateWorkItem(id, {
862
+ assignee: text.trim(),
863
+ });
864
+ await queueWrite('update', id);
865
+ }
866
+ refreshData();
867
+ setToast(targetIds.length === 1
868
+ ? 'Assignee updated'
869
+ : `${targetIds.length} items updated`);
870
+ })();
871
+ }, onCancel: () => closeOverlay(), placeholder: "Type assignee name..." })) : activeOverlay?.type === 'labels-input' ? (_jsx(OverlayPanel, { title: `Set Labels (${activeOverlay.targetIds.length} item${activeOverlay.targetIds.length > 1 ? 's' : ''})`, items: labelSuggestions.map((l) => ({
872
+ id: l,
873
+ label: l,
874
+ value: l,
875
+ })), multiSelect: true, allowFreeform: true, onSelect: () => { }, onConfirm: (selected) => {
876
+ const targetIds = getOverlayTargetIds();
877
+ closeOverlay();
878
+ if (!backend)
879
+ return;
880
+ void (async () => {
881
+ const labels = selected.map((i) => i.value);
882
+ for (const id of targetIds) {
883
+ await backend.cachedUpdateWorkItem(id, { labels });
884
+ await queueWrite('update', id);
885
+ }
886
+ refreshData();
887
+ setToast(targetIds.length === 1
888
+ ? 'Labels updated'
889
+ : `${targetIds.length} items updated`);
890
+ })();
891
+ }, onSubmitFreeform: (text) => {
892
+ const targetIds = getOverlayTargetIds();
893
+ closeOverlay();
894
+ if (!backend)
895
+ return;
896
+ void (async () => {
897
+ const labels = text
898
+ .split(',')
899
+ .map((l) => l.trim())
900
+ .filter(Boolean);
901
+ for (const id of targetIds) {
902
+ await backend.cachedUpdateWorkItem(id, { labels });
903
+ await queueWrite('update', id);
904
+ }
905
+ refreshData();
906
+ setToast(targetIds.length === 1
907
+ ? 'Labels updated'
908
+ : `${targetIds.length} items updated`);
909
+ })();
910
+ }, onCancel: () => closeOverlay(), placeholder: "Type to filter labels..." })) : activeOverlay?.type === 'delete-confirm' ? (_jsx(OverlayPanel, { title: `Delete ${activeOverlay.targetIds.length} item${activeOverlay.targetIds.length > 1 ? 's' : ''}?`, items: [
911
+ { id: 'yes', label: 'Yes, delete', value: 'yes' },
912
+ { id: 'no', label: 'Cancel', value: 'no' },
913
+ ], onSelect: (item) => {
914
+ if (item.value === 'yes') {
915
+ const targetIds = activeOverlay.targetIds;
701
916
  if (!backend)
702
917
  return;
703
918
  void (async () => {
704
919
  for (const id of targetIds) {
705
- await backend.cachedUpdateWorkItem(id, { priority });
706
- await queueWrite('update', id);
920
+ await backend.cachedDeleteWorkItem(id);
921
+ await queueWrite('delete', id);
922
+ }
923
+ closeOverlay();
924
+ for (const id of targetIds) {
925
+ removeDeletedItem(id);
707
926
  }
927
+ setCursor(Math.max(0, cursor - 1));
708
928
  refreshData();
709
929
  setToast(targetIds.length === 1
710
- ? 'Priority updated'
711
- : `${targetIds.length} items updated`);
930
+ ? `Item #${targetIds[0]} deleted`
931
+ : `${targetIds.length} items deleted`);
712
932
  })();
713
- }, onCancel: () => closeOverlay() })), activeOverlay?.type === 'template-picker' && (_jsx(TemplatePicker, { templates: templates, onSelect: (template) => {
933
+ }
934
+ else {
714
935
  closeOverlay();
715
- setFormMode('item');
716
- setActiveTemplate(template);
717
- selectWorkItem(null);
718
- navigate('form');
719
- }, onCancel: () => closeOverlay() })), _jsx(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)` }), markedCount > 0 && (_jsx(Text, { color: "magenta", children: ` ● ${markedCount} marked` }))] }) }), _jsx(TableLayout, { treeItems: visibleTreeItems, cursor: viewport.visibleCursor, capabilities: capabilities, collapsedIds: collapsedIds, markedIds: markedIds, terminalWidth: terminalWidth }), treeItems.length === 0 && !loading && initError && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "red", children: "Failed to connect to backend:" }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "red", children: initError }) }), _jsx(Text, { dimColor: true, children: "Press , for settings or q to quit." })] })), treeItems.length === 0 && !loading && !initError && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, 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: true, children: "Loading..." }) })), showDetailPanel && treeItems.length > 0 && treeItems[cursor] && (_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: true, children: "\u2191\u2193 scroll space/esc close" }), positionText && _jsxs(Text, { dimColor: true, children: [" ", positionText] })] })) : 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: () => {
720
- const targetIds = getOverlayTargetIds();
721
- if (!backend)
722
- return;
723
- void (async () => {
724
- const raw = parentInput.trim();
725
- const newParent = raw === ''
726
- ? null
727
- : raw.includes(' - ')
728
- ? raw.split(' - ')[0].trim()
729
- : raw;
730
- try {
731
- for (const id of targetIds) {
732
- await backend.cachedUpdateWorkItem(id, {
733
- parent: newParent,
734
- });
735
- await queueWrite('update', id);
736
- }
737
- clearWarning();
738
- }
739
- catch (e) {
740
- setWarning(e instanceof Error ? e.message : 'Invalid parent');
741
- }
742
- closeOverlay();
743
- setParentInput('');
744
- refreshData();
745
- setToast(targetIds.length === 1
746
- ? 'Parent updated'
747
- : `${targetIds.length} items updated`);
748
- })();
749
- } })] })) : 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: () => {
750
- const targetIds = getOverlayTargetIds();
751
- closeOverlay();
752
- if (!backend)
753
- return;
754
- void (async () => {
755
- const assignee = assigneeInput.trim();
756
- for (const id of targetIds) {
757
- await backend.cachedUpdateWorkItem(id, { assignee });
758
- await queueWrite('update', id);
759
- }
760
- setAssigneeInput('');
761
- refreshData();
762
- setToast(targetIds.length === 1
763
- ? 'Assignee updated'
764
- : `${targetIds.length} items updated`);
765
- })();
766
- } })] })) : 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: () => {
767
- const targetIds = getOverlayTargetIds();
768
- closeOverlay();
769
- if (!backend)
770
- return;
771
- void (async () => {
772
- const labels = labelsInput
773
- .split(',')
774
- .map((l) => l.trim())
775
- .filter(Boolean);
776
- for (const id of targetIds) {
777
- await backend.cachedUpdateWorkItem(id, { labels });
778
- await queueWrite('update', id);
779
- }
780
- setLabelsInput('');
781
- refreshData();
782
- setToast(targetIds.length === 1
783
- ? 'Labels updated'
784
- : `${targetIds.length} items updated`);
785
- })();
786
- } })] })) : activeOverlay?.type === 'delete-confirm' ? (_jsxs(Text, { color: "red", children: ["Delete ", activeOverlay.targetIds.length, " item", activeOverlay.targetIds.length > 1 ? 's' : '', "? (y/n)"] })) : toast ? (_jsxs(Box, { children: [_jsx(Text, { color: "green", children: toast.message }), positionText && _jsxs(Text, { dimColor: true, children: [" ", positionText] })] })) : (_jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: buildHelpText(terminalWidth -
787
- (positionText ? positionText.length + 2 : 0)) }), positionText && _jsxs(Text, { dimColor: true, children: [" ", positionText] })] })) }), 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"] }) }))] }))] }));
936
+ }
937
+ }, onCancel: () => closeOverlay() })) : toast ? (_jsxs(Box, { children: [_jsx(Text, { color: "green", children: toast.message }), positionText && _jsxs(Text, { dimColor: true, children: [" ", positionText] })] })) : (_jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: buildHelpText(terminalWidth - (positionText ? positionText.length + 2 : 0)) }), positionText && _jsxs(Text, { dimColor: true, children: [" ", positionText] })] })) }), 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"] }) }))] }));
788
938
  }
789
939
  //# sourceMappingURL=WorkItemList.js.map