@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.
Files changed (121) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dist/backends/ado/index.js +1 -1
  3. package/dist/backends/ado/index.js.map +1 -1
  4. package/dist/backends/ado/mappers.d.ts +2 -2
  5. package/dist/backends/ado/mappers.js +7 -2
  6. package/dist/backends/ado/mappers.js.map +1 -1
  7. package/dist/backends/files/index.d.ts +6 -0
  8. package/dist/backends/files/index.js +36 -0
  9. package/dist/backends/files/index.js.map +1 -1
  10. package/dist/backends/files/sync.d.ts +2 -2
  11. package/dist/backends/files/sync.js +9 -7
  12. package/dist/backends/files/sync.js.map +1 -1
  13. package/dist/backends/github/index.js +3 -3
  14. package/dist/backends/github/index.js.map +1 -1
  15. package/dist/backends/github/mappers.js +2 -1
  16. package/dist/backends/github/mappers.js.map +1 -1
  17. package/dist/backends/github/pr-mappers.d.ts +1 -1
  18. package/dist/backends/github/pr-mappers.js +1 -1
  19. package/dist/backends/github/pr-mappers.js.map +1 -1
  20. package/dist/backends/gitlab/index.js +13 -6
  21. package/dist/backends/gitlab/index.js.map +1 -1
  22. package/dist/backends/gitlab/mappers.js +2 -1
  23. package/dist/backends/gitlab/mappers.js.map +1 -1
  24. package/dist/backends/jira/index.js +2 -2
  25. package/dist/backends/jira/index.js.map +1 -1
  26. package/dist/backends/jira/mappers.d.ts +1 -1
  27. package/dist/backends/jira/mappers.js +5 -4
  28. package/dist/backends/jira/mappers.js.map +1 -1
  29. package/dist/backends/local/items.js +9 -2
  30. package/dist/backends/local/items.js.map +1 -1
  31. package/dist/backends/shared/api-client.js +9 -1
  32. package/dist/backends/shared/api-client.js.map +1 -1
  33. package/dist/backends/types.d.ts +2 -3
  34. package/dist/backends/types.js +8 -2
  35. package/dist/backends/types.js.map +1 -1
  36. package/dist/cli/commands/config.js +0 -1
  37. package/dist/cli/commands/config.js.map +1 -1
  38. package/dist/cli/commands/item.js +45 -17
  39. package/dist/cli/commands/item.js.map +1 -1
  40. package/dist/cli/commands/mcp.js +24 -9
  41. package/dist/cli/commands/mcp.js.map +1 -1
  42. package/dist/cli/commands/pr.js +12 -6
  43. package/dist/cli/commands/pr.js.map +1 -1
  44. package/dist/cli/index.js +13 -6
  45. package/dist/cli/index.js.map +1 -1
  46. package/dist/components/AuthPrompt.js +1 -1
  47. package/dist/components/AutocompleteInput.js +1 -1
  48. package/dist/components/BranchList.js +4 -2
  49. package/dist/components/BranchList.js.map +1 -1
  50. package/dist/components/CommandBar.js +8 -7
  51. package/dist/components/CommandBar.js.map +1 -1
  52. package/dist/components/DetailPanel.js +2 -2
  53. package/dist/components/DetailPanel.js.map +1 -1
  54. package/dist/components/MarkdownEditor.js +9 -5
  55. package/dist/components/MarkdownEditor.js.map +1 -1
  56. package/dist/components/MultiAutocompleteInput.js +1 -1
  57. package/dist/components/MultiSelectInput.js +1 -1
  58. package/dist/components/OverlayPanel.js +1 -1
  59. package/dist/components/Settings.js +2 -2
  60. package/dist/components/Settings.js.map +1 -1
  61. package/dist/components/StatusScreen.js +2 -2
  62. package/dist/components/StatusScreen.js.map +1 -1
  63. package/dist/components/TextInput.d.ts +12 -0
  64. package/dist/components/TextInput.js +207 -0
  65. package/dist/components/TextInput.js.map +1 -0
  66. package/dist/components/WorkItemForm.js +115 -98
  67. package/dist/components/WorkItemForm.js.map +1 -1
  68. package/dist/components/WorkItemList.d.ts +5 -3
  69. package/dist/components/WorkItemList.js +187 -123
  70. package/dist/components/WorkItemList.js.map +1 -1
  71. package/dist/components/buildTree.js +15 -13
  72. package/dist/components/buildTree.js.map +1 -1
  73. package/dist/components/fuzzyMatch.js +1 -1
  74. package/dist/components/fuzzyMatch.js.map +1 -1
  75. package/dist/components/getMarkedDistribution.d.ts +2 -2
  76. package/dist/components/getMarkedDistribution.js +1 -1
  77. package/dist/components/getMarkedDistribution.js.map +1 -1
  78. package/dist/git.d.ts +2 -1
  79. package/dist/hooks/useFormValidation.js +31 -21
  80. package/dist/hooks/useFormValidation.js.map +1 -1
  81. package/dist/hooks/useForwardDelete.d.ts +10 -0
  82. package/dist/hooks/useForwardDelete.js +34 -0
  83. package/dist/hooks/useForwardDelete.js.map +1 -0
  84. package/dist/implement.js +3 -3
  85. package/dist/implement.js.map +1 -1
  86. package/dist/index.js +3 -1
  87. package/dist/index.js.map +1 -1
  88. package/dist/storage/config.d.ts +0 -1
  89. package/dist/storage/config.js +0 -6
  90. package/dist/storage/config.js.map +1 -1
  91. package/dist/storage/index.d.ts +25 -6
  92. package/dist/storage/index.js +304 -183
  93. package/dist/storage/index.js.map +1 -1
  94. package/dist/storage/mappers.d.ts +1 -1
  95. package/dist/storage/mappers.js +5 -3
  96. package/dist/storage/mappers.js.map +1 -1
  97. package/dist/storage/schema.d.ts +83 -380
  98. package/dist/storage/schema.js +27 -55
  99. package/dist/storage/schema.js.map +1 -1
  100. package/dist/storage/syncQueue.d.ts +2 -3
  101. package/dist/storage/syncQueue.js +10 -17
  102. package/dist/storage/syncQueue.js.map +1 -1
  103. package/dist/storage/undo.js +7 -6
  104. package/dist/storage/undo.js.map +1 -1
  105. package/dist/stores/backendDataStore.js +3 -1
  106. package/dist/stores/backendDataStore.js.map +1 -1
  107. package/dist/stores/formStackStore.d.ts +1 -1
  108. package/dist/stores/listViewStore.d.ts +6 -6
  109. package/dist/stores/navigationStore.d.ts +7 -7
  110. package/dist/stores/undoStore.d.ts +2 -2
  111. package/dist/sync/SyncManager.d.ts +6 -1
  112. package/dist/sync/SyncManager.js +80 -76
  113. package/dist/sync/SyncManager.js.map +1 -1
  114. package/dist/sync/types.d.ts +6 -7
  115. package/dist/test-helpers.d.ts +1 -1
  116. package/dist/test-helpers.js +11 -8
  117. package/dist/test-helpers.js.map +1 -1
  118. package/dist/types.d.ts +6 -5
  119. package/drizzle/0006_dual_id.sql +173 -0
  120. package/drizzle/meta/_journal.json +7 -0
  121. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TextInput.js","sourceRoot":"","sources":["../../src/components/TextInput.tsx"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState, useMemo, useEffect } from 'react';
3
3
  import { Box, Text, useInput } from 'ink';
4
- import TextInput from 'ink-text-input';
4
+ import TextInput from './TextInput.js';
5
5
  import SelectInput from 'ink-select-input';
6
6
  import { AutocompleteInput } from './AutocompleteInput.js';
7
7
  import { MultiSelectInput } from './MultiSelectInput.js';
@@ -54,11 +54,21 @@ export function WorkItemForm() {
54
54
  popWorkItem: s.popWorkItem,
55
55
  })));
56
56
  const queue = useBackendDataStore((s) => s.queue);
57
- const queueWrite = async (action, itemId, extra) => {
57
+ /** Look up the rowId for a display ID. Returns -1 if not found. */
58
+ const rowIdOf = (displayId) => {
59
+ const item = allItems.find((i) => i.id === displayId);
60
+ return item?.rowId ?? -1;
61
+ };
62
+ /** Look up the display ID for a rowId. Returns null if not found. */
63
+ const displayIdOf = (rowId) => {
64
+ const item = allItems.find((i) => i.rowId === rowId);
65
+ return item?.id ?? null;
66
+ };
67
+ const queueWrite = async (action, itemRowId, extra) => {
58
68
  if (queue) {
59
69
  await queue.append({
60
70
  action,
61
- itemId,
71
+ itemRowId,
62
72
  timestamp: new Date().toISOString(),
63
73
  ...(extra?.commentData ? { commentData: extra.commentData } : {}),
64
74
  ...(extra?.templateSlug ? { templateSlug: extra.templateSlug } : {}),
@@ -97,16 +107,24 @@ export function WorkItemForm() {
97
107
  }
98
108
  let cancelled = false;
99
109
  setItemLoading(true);
110
+ const displayId = displayIdOf(selectedWorkItemId);
111
+ if (!displayId) {
112
+ setItemLoading(false);
113
+ return;
114
+ }
100
115
  void (async () => {
101
116
  try {
102
- const item = await backend.getWorkItem(selectedWorkItemId);
117
+ const item = await backend.getWorkItem(displayId);
103
118
  const [ch, dep] = capabilities.relationships
104
119
  ? await Promise.all([
105
- backend.getChildren(selectedWorkItemId),
106
- backend.getDependents(selectedWorkItemId),
120
+ backend.getChildren(displayId),
121
+ backend.getDependents(displayId),
107
122
  ])
108
123
  : [[], []];
109
- const pi = item.parent ? await backend.getWorkItem(item.parent) : null;
124
+ const parentDisplayId = item.parent !== null ? displayIdOf(item.parent) : null;
125
+ const pi = parentDisplayId
126
+ ? await backend.getWorkItem(parentDisplayId)
127
+ : null;
110
128
  if (cancelled)
111
129
  return;
112
130
  setExistingItem(item);
@@ -180,10 +198,10 @@ export function WorkItemForm() {
180
198
  all.push('rel-parent');
181
199
  }
182
200
  for (const child of children) {
183
- all.push(`rel-child-${child.id}`);
201
+ all.push(`rel-child-${child.rowId}`);
184
202
  }
185
203
  for (const dep of dependents) {
186
- all.push(`rel-dependent-${dep.id}`);
204
+ all.push(`rel-dependent-${dep.rowId}`);
187
205
  }
188
206
  }
189
207
  return all;
@@ -205,7 +223,9 @@ export function WorkItemForm() {
205
223
  return required;
206
224
  }, [selectedWorkItemId, capabilities.customTypes]);
207
225
  const requiredFieldsList = useMemo(() => capabilities.requiredFields ?? [...requiredFields], [capabilities.requiredFields, requiredFields]);
208
- const { errors: validationErrors, validate, clearError, } = useFormValidation(allItems, selectedWorkItemId, requiredFieldsList);
226
+ // Convert selectedWorkItemId (rowId) to display ID for validation
227
+ const selectedDisplayId = selectedWorkItemId !== null ? displayIdOf(selectedWorkItemId) : null;
228
+ const { errors: validationErrors, validate, clearError, } = useFormValidation(allItems, selectedDisplayId, requiredFieldsList);
209
229
  const [comments, setComments] = useState([]);
210
230
  // Derive field values from current draft
211
231
  const title = currentDraft?.fields.title ?? '';
@@ -248,6 +268,15 @@ export function WorkItemForm() {
248
268
  useEffect(() => {
249
269
  if (formStackStore.getState().stack.length > 0)
250
270
  return; // Already has a draft
271
+ // Check for create-child prefill
272
+ const { createChildParentId, setCreateChildParentId } = navigationStore.getState();
273
+ let parentId = '';
274
+ if (selectedWorkItemId === null && createChildParentId) {
275
+ const parent = allItems.find((i) => i.rowId === createChildParentId);
276
+ const displayId = parent?.id ?? String(createChildParentId);
277
+ parentId = parent ? `#${displayId} - ${parent.title}` : `#${displayId}`;
278
+ setCreateChildParentId(null);
279
+ }
251
280
  const initialFields = {
252
281
  title: '',
253
282
  type: activeType ?? types[0] ?? '',
@@ -257,46 +286,21 @@ export function WorkItemForm() {
257
286
  assignee: '',
258
287
  labels: '',
259
288
  description: '',
260
- parentId: '',
289
+ parentId,
261
290
  dependsOn: '',
262
291
  newComment: '',
263
292
  };
264
293
  const draft = {
265
294
  itemId: selectedWorkItemId,
266
- itemTitle: selectedWorkItemId ? `#${selectedWorkItemId}` : '(new)',
295
+ itemTitle: selectedWorkItemId
296
+ ? `#${selectedDisplayId ?? selectedWorkItemId}`
297
+ : '(new)',
267
298
  fields: initialFields,
268
299
  initialSnapshot: { ...initialFields },
269
300
  focusedField: 0,
270
301
  };
271
302
  pushDraft(draft);
272
303
  }, []); // Only on mount
273
- // Prefill parent for create-child (create mode only)
274
- useEffect(() => {
275
- if (selectedWorkItemId !== null)
276
- return;
277
- const { createChildParentId, setCreateChildParentId } = navigationStore.getState();
278
- if (!createChildParentId)
279
- return;
280
- const parentItem = allItems.find((i) => i.id === createChildParentId);
281
- const parentDisplay = parentItem
282
- ? `#${createChildParentId} - ${parentItem.title}`
283
- : `#${createChildParentId}`;
284
- updateFields({ parentId: parentDisplay });
285
- // Update initialSnapshot so parent doesn't count as dirty
286
- formStackStore.setState((state) => {
287
- if (state.stack.length === 0)
288
- return state;
289
- const updated = [...state.stack];
290
- const current = updated[updated.length - 1];
291
- updated[updated.length - 1] = {
292
- ...current,
293
- initialSnapshot: { ...current.fields, parentId: parentDisplay },
294
- };
295
- return { stack: updated };
296
- });
297
- // Clear after use
298
- setCreateChildParentId(null);
299
- }, [selectedWorkItemId, allItems]);
300
304
  // Sync form fields when the existing item finishes loading
301
305
  useEffect(() => {
302
306
  if (!existingItem)
@@ -308,16 +312,20 @@ export function WorkItemForm() {
308
312
  // Build field values
309
313
  const parentIdValue = existingItem.parent !== null && existingItem.parent !== undefined
310
314
  ? (() => {
311
- const pi = allItems.find((i) => i.id === existingItem.parent);
315
+ const pi = allItems.find((i) => i.rowId === existingItem.parent);
316
+ const parentDisplayId = pi?.id ?? String(existingItem.parent);
312
317
  return pi
313
- ? `#${existingItem.parent} - ${pi.title}`
318
+ ? `#${parentDisplayId} - ${pi.title}`
314
319
  : String(existingItem.parent);
315
320
  })()
316
321
  : '';
317
322
  const dependsOnValue = existingItem.dependsOn
318
- ?.map((depId) => {
319
- const depItem = allItems.find((i) => i.id === depId);
320
- return depItem ? `#${depId} - ${depItem.title}` : depId;
323
+ ?.map((depRowId) => {
324
+ const depItem = allItems.find((i) => i.rowId === depRowId);
325
+ const depDisplayId = depItem?.id ?? String(depRowId);
326
+ return depItem
327
+ ? `#${depDisplayId} - ${depItem.title}`
328
+ : String(depRowId);
321
329
  })
322
330
  .join(', ') ?? '';
323
331
  const newFields = {
@@ -420,7 +428,7 @@ export function WorkItemForm() {
420
428
  }, [formMode, editingTemplateSlug, backend]);
421
429
  const parentSuggestions = useMemo(() => {
422
430
  return allItems
423
- .filter((item) => item.id !== selectedWorkItemId)
431
+ .filter((item) => item.rowId !== selectedWorkItemId && item.id !== null)
424
432
  .map((item) => `#${item.id} - ${item.title}`);
425
433
  }, [allItems, selectedWorkItemId]);
426
434
  const [editing, setEditing] = useState(false);
@@ -448,23 +456,26 @@ export function WorkItemForm() {
448
456
  .split(',')
449
457
  .map((l) => l.trim())
450
458
  .filter((l) => l.length > 0);
459
+ // Parse display IDs from form fields and resolve to rowIds
451
460
  const parsedParent = (() => {
452
461
  const trimmed = parentId.trim();
453
462
  if (!trimmed)
454
463
  return null;
455
464
  const match = trimmed.match(/^#(\S+)\s*-\s/);
456
- return match ? match[1] : trimmed;
465
+ const displayId = match ? match[1] : trimmed;
466
+ return rowIdOf(displayId);
457
467
  })();
458
468
  const parsedDependsOn = dependsOn
459
469
  .split(',')
460
470
  .map((s) => {
461
471
  const trimmed = s.trim();
462
472
  if (!trimmed)
463
- return '';
473
+ return -1;
464
474
  const match = trimmed.match(/^#(\S+)\s*-\s/);
465
- return match ? match[1] : trimmed;
475
+ const displayId = match ? match[1] : trimmed;
476
+ return rowIdOf(displayId);
466
477
  })
467
- .filter((s) => s.length > 0);
478
+ .filter((rowId) => rowId !== -1);
468
479
  if (formMode === 'template') {
469
480
  const template = {
470
481
  slug: editingTemplateSlug ?? slugifyTemplateName(title),
@@ -484,19 +495,23 @@ export function WorkItemForm() {
484
495
  template.iteration = iteration;
485
496
  if (description)
486
497
  template.description = description;
487
- if (parsedParent)
488
- template.parent = parsedParent;
489
- if (parsedDependsOn.length > 0)
490
- template.dependsOn = parsedDependsOn;
498
+ if (parsedParent !== null) {
499
+ template.parent = displayIdOf(parsedParent);
500
+ }
501
+ if (parsedDependsOn.length > 0) {
502
+ template.dependsOn = parsedDependsOn
503
+ .map((rid) => displayIdOf(rid))
504
+ .filter((id) => id !== null);
505
+ }
491
506
  if (editingTemplateSlug) {
492
507
  await backend.updateTemplate(editingTemplateSlug, template);
493
- await queueWrite('template-update', template.slug, {
508
+ await queueWrite('template-update', 0, {
494
509
  templateSlug: template.slug,
495
510
  });
496
511
  }
497
512
  else {
498
513
  await backend.createTemplate(template);
499
- await queueWrite('template-create', template.slug, {
514
+ await queueWrite('template-create', 0, {
500
515
  templateSlug: template.slug,
501
516
  });
502
517
  }
@@ -505,9 +520,9 @@ export function WorkItemForm() {
505
520
  setEditingTemplateSlug(null);
506
521
  return;
507
522
  }
508
- if (selectedWorkItemId !== null) {
509
- const snapshot = await backend.getWorkItem(selectedWorkItemId);
510
- await backend.cachedUpdateWorkItem(selectedWorkItemId, {
523
+ if (selectedWorkItemId !== null && selectedDisplayId) {
524
+ const snapshot = await backend.getWorkItem(selectedDisplayId);
525
+ await backend.cachedUpdateWorkItem(selectedDisplayId, {
511
526
  title,
512
527
  type,
513
528
  status,
@@ -522,13 +537,13 @@ export function WorkItemForm() {
522
537
  await queueWrite('update', selectedWorkItemId);
523
538
  undoStore.getState().pushUndo({
524
539
  type: 'update',
525
- label: `edited #${selectedWorkItemId}`,
540
+ label: `edited #${selectedDisplayId}`,
526
541
  itemSnapshots: [snapshot],
527
- syncItemIds: [selectedWorkItemId],
542
+ syncItemRowIds: [selectedWorkItemId],
528
543
  syncAction: 'update',
529
544
  });
530
545
  if (capabilities.comments && newComment.trim().length > 0) {
531
- const added = await backend.addComment(selectedWorkItemId, {
546
+ const added = await backend.addComment(selectedDisplayId, {
532
547
  author: 'me',
533
548
  body: newComment.trim(),
534
549
  });
@@ -538,10 +553,10 @@ export function WorkItemForm() {
538
553
  setComments((prev) => [...prev, added]);
539
554
  setNewComment('');
540
555
  }
541
- await backendDataStore.getState().reloadItem(selectedWorkItemId);
556
+ await backendDataStore.getState().reloadItem(selectedDisplayId);
542
557
  uiStore
543
558
  .getState()
544
- .setToast(`Item #${selectedWorkItemId} updated — press u to undo`);
559
+ .setToast(`Item #${selectedDisplayId} updated — press u to undo`);
545
560
  }
546
561
  else {
547
562
  const created = await backend.cachedCreateWorkItem({
@@ -556,28 +571,30 @@ export function WorkItemForm() {
556
571
  parent: parsedParent,
557
572
  dependsOn: parsedDependsOn,
558
573
  });
559
- await queueWrite('create', created.id);
574
+ await queueWrite('create', created.rowId);
560
575
  undoStore.getState().pushUndo({
561
576
  type: 'create',
562
- label: `created #${created.id}`,
577
+ label: `created #${created.id ?? created.rowId}`,
563
578
  itemSnapshots: [],
564
- syncItemIds: [created.id],
579
+ syncItemRowIds: [created.rowId],
565
580
  syncAction: 'create',
566
- createdIds: [created.id],
581
+ createdRowIds: [created.rowId],
567
582
  });
568
- if (capabilities.comments && newComment.trim().length > 0) {
583
+ if (capabilities.comments && newComment.trim().length > 0 && created.id) {
569
584
  await backend.addComment(created.id, {
570
585
  author: 'me',
571
586
  body: newComment.trim(),
572
587
  });
573
- await queueWrite('comment', created.id, {
588
+ await queueWrite('comment', created.rowId, {
574
589
  commentData: { author: 'me', body: newComment.trim() },
575
590
  });
576
591
  }
577
- await backendDataStore.getState().reloadItem(created.id);
592
+ if (created.id) {
593
+ await backendDataStore.getState().reloadItem(created.id);
594
+ }
578
595
  uiStore
579
596
  .getState()
580
- .setToast(`Item #${created.id} created — press u to undo`);
597
+ .setToast(`Item #${created.id ?? created.rowId} created — press u to undo`);
581
598
  setActiveTemplate(null);
582
599
  }
583
600
  // Update the initialSnapshot after saving so isDirty becomes false
@@ -609,7 +626,7 @@ export function WorkItemForm() {
609
626
  await save();
610
627
  if (pendingRelNav) {
611
628
  // Push a new draft for the target item
612
- const targetItem = allItems.find((i) => i.id === pendingRelNav);
629
+ const targetItem = allItems.find((i) => i.rowId === pendingRelNav);
613
630
  const defaultFields = {
614
631
  title: '',
615
632
  type: activeType ?? types[0] ?? '',
@@ -625,7 +642,7 @@ export function WorkItemForm() {
625
642
  };
626
643
  const newDraft = {
627
644
  itemId: pendingRelNav,
628
- itemTitle: targetItem?.title ?? `#${pendingRelNav}`,
645
+ itemTitle: targetItem?.title ?? `#${targetItem?.id ?? pendingRelNav}`,
629
646
  fields: defaultFields,
630
647
  initialSnapshot: { ...defaultFields },
631
648
  focusedField: 0,
@@ -659,7 +676,7 @@ export function WorkItemForm() {
659
676
  if (pendingRelNav) {
660
677
  // Push a new draft for the target item (discarding current)
661
678
  formStackStore.getState().pop();
662
- const targetItem = allItems.find((i) => i.id === pendingRelNav);
679
+ const targetItem = allItems.find((i) => i.rowId === pendingRelNav);
663
680
  const defaultFields = {
664
681
  title: '',
665
682
  type: activeType ?? types[0] ?? '',
@@ -675,7 +692,7 @@ export function WorkItemForm() {
675
692
  };
676
693
  const newDraft = {
677
694
  itemId: pendingRelNav,
678
- itemTitle: targetItem?.title ?? `#${pendingRelNav}`,
695
+ itemTitle: targetItem?.title ?? `#${targetItem?.id ?? pendingRelNav}`,
679
696
  fields: defaultFields,
680
697
  initialSnapshot: { ...defaultFields },
681
698
  focusedField: 0,
@@ -807,24 +824,24 @@ export function WorkItemForm() {
807
824
  }
808
825
  if (matchesCommand('form-edit', _input, key)) {
809
826
  if (isRelationshipField) {
810
- let targetId = null;
827
+ let targetRowId = null;
811
828
  if (currentField === 'rel-parent' && existingItem?.parent) {
812
- targetId = existingItem.parent;
829
+ targetRowId = existingItem.parent;
813
830
  }
814
831
  else if (currentField.startsWith('rel-child-')) {
815
- targetId = currentField.slice('rel-child-'.length);
832
+ targetRowId = Number(currentField.slice('rel-child-'.length));
816
833
  }
817
834
  else if (currentField.startsWith('rel-dependent-')) {
818
- targetId = currentField.slice('rel-dependent-'.length);
835
+ targetRowId = Number(currentField.slice('rel-dependent-'.length));
819
836
  }
820
- if (targetId) {
837
+ if (targetRowId) {
821
838
  if (isDirty) {
822
- setPendingRelNav(targetId);
839
+ setPendingRelNav(targetRowId);
823
840
  setShowDirtyPrompt(true);
824
841
  }
825
842
  else {
826
843
  // Create new draft for target item before navigating
827
- const targetItem = allItems.find((i) => i.id === targetId);
844
+ const targetItem = allItems.find((i) => i.rowId === targetRowId);
828
845
  const defaultFields = {
829
846
  title: '',
830
847
  type: activeType ?? types[0] ?? '',
@@ -839,14 +856,14 @@ export function WorkItemForm() {
839
856
  newComment: '',
840
857
  };
841
858
  const newDraft = {
842
- itemId: targetId,
843
- itemTitle: targetItem?.title ?? `#${targetId}`,
859
+ itemId: targetRowId,
860
+ itemTitle: targetItem?.title ?? `#${targetItem?.id ?? targetRowId}`,
844
861
  fields: defaultFields,
845
862
  initialSnapshot: { ...defaultFields },
846
863
  focusedField: 0,
847
864
  };
848
865
  pushDraft(newDraft);
849
- pushWorkItem(targetId);
866
+ pushWorkItem(targetRowId);
850
867
  }
851
868
  }
852
869
  }
@@ -1106,24 +1123,24 @@ export function WorkItemForm() {
1106
1123
  function renderRelationshipField(field, index) {
1107
1124
  const focused = index === focusedField;
1108
1125
  const cursor = focused ? '>' : ' ';
1109
- const id = field.startsWith('rel-child-')
1110
- ? field.slice('rel-child-'.length)
1126
+ const rowId = field.startsWith('rel-child-')
1127
+ ? Number(field.slice('rel-child-'.length))
1111
1128
  : field.startsWith('rel-dependent-')
1112
- ? field.slice('rel-dependent-'.length)
1129
+ ? Number(field.slice('rel-dependent-'.length))
1113
1130
  : null;
1114
- let item = null;
1131
+ let relItem = null;
1115
1132
  if (field === 'rel-parent' && parentItem) {
1116
- item = { id: parentItem.id, title: parentItem.title };
1133
+ relItem = parentItem;
1117
1134
  }
1118
- else if (id) {
1119
- const child = children.find((c) => c.id === id);
1120
- const dep = dependents.find((d) => d.id === id);
1121
- const relItem = child ?? dep;
1122
- item = relItem ? { id: relItem.id, title: relItem.title } : null;
1135
+ else if (rowId !== null) {
1136
+ relItem =
1137
+ children.find((c) => c.rowId === rowId) ??
1138
+ dependents.find((d) => d.rowId === rowId) ??
1139
+ null;
1123
1140
  }
1124
- if (!item)
1141
+ if (!relItem)
1125
1142
  return null;
1126
- return (_jsxs(Box, { children: [_jsxs(Text, { color: focused ? accent : undefined, children: [cursor, " "] }), _jsxs(Text, { bold: focused, color: focused ? accent : undefined, children: ["#", item.id, " (", item.title, ")"] })] }, field));
1143
+ return (_jsxs(Box, { children: [_jsxs(Text, { color: focused ? accent : undefined, children: [cursor, " "] }), _jsxs(Text, { bold: focused, color: focused ? accent : undefined, children: ["#", relItem.id ?? relItem.rowId, " (", relItem.title, ")"] })] }, field));
1127
1144
  }
1128
1145
  const formStackLength = useFormStackStore((s) => s.stack.length);
1129
1146
  const chromeLines = useMemo(() => {
@@ -1168,7 +1185,7 @@ export function WorkItemForm() {
1168
1185
  const typeLabel = formMode === 'template' ? '' : type.charAt(0).toUpperCase() + type.slice(1);
1169
1186
  const isFieldVisible = (index) => index >= viewport.start && index < viewport.end;
1170
1187
  return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { bold: true, color: accent, children: [mode, typeLabel ? ` ${typeLabel}` : '', formMode !== 'template' && selectedWorkItemId !== null
1171
- ? ` #${selectedWorkItemId}`
1188
+ ? ` #${selectedDisplayId ?? selectedWorkItemId}`
1172
1189
  : ''] }) }), _jsx(Breadcrumbs, {}), fields.map((field, index) => {
1173
1190
  if (field === 'rel-parent' ||
1174
1191
  field.startsWith('rel-child-') ||