@compilr-dev/cli 0.4.0 → 0.5.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/README.md +30 -12
- package/dist/agent.d.ts +74 -1
- package/dist/agent.js +259 -76
- package/dist/anchors/index.d.ts +9 -0
- package/dist/anchors/index.js +9 -0
- package/dist/anchors/project-anchors.d.ts +79 -0
- package/dist/anchors/project-anchors.js +202 -0
- package/dist/commands/handler-types.d.ts +68 -0
- package/dist/commands/handler-types.js +8 -0
- package/dist/commands/handlers/agent-commands.d.ts +13 -0
- package/dist/commands/handlers/agent-commands.js +305 -0
- package/dist/commands/handlers/design-commands.d.ts +15 -0
- package/dist/commands/handlers/design-commands.js +334 -0
- package/dist/commands/handlers/index.d.ts +20 -0
- package/dist/commands/handlers/index.js +43 -0
- package/dist/commands/handlers/overlay-commands.d.ts +21 -0
- package/dist/commands/handlers/overlay-commands.js +287 -0
- package/dist/commands/handlers/project-commands.d.ts +11 -0
- package/dist/commands/handlers/project-commands.js +167 -0
- package/dist/commands/handlers/simple-commands.d.ts +19 -0
- package/dist/commands/handlers/simple-commands.js +144 -0
- package/dist/commands/index.d.ts +2 -1
- package/dist/commands/registry.d.ts +50 -0
- package/dist/commands/registry.js +75 -0
- package/dist/commands-v2/handlers/context.d.ts +13 -0
- package/dist/commands-v2/handlers/context.js +348 -0
- package/dist/commands-v2/handlers/core.d.ts +13 -0
- package/dist/commands-v2/handlers/core.js +165 -0
- package/dist/commands-v2/handlers/debug.d.ts +11 -0
- package/dist/commands-v2/handlers/debug.js +159 -0
- package/dist/commands-v2/handlers/index.d.ts +12 -0
- package/dist/commands-v2/handlers/index.js +24 -0
- package/dist/commands-v2/handlers/project.d.ts +22 -0
- package/dist/commands-v2/handlers/project.js +814 -0
- package/dist/commands-v2/handlers/settings.d.ts +15 -0
- package/dist/commands-v2/handlers/settings.js +235 -0
- package/dist/commands-v2/index.d.ts +13 -0
- package/dist/commands-v2/index.js +15 -0
- package/dist/commands-v2/registry.d.ts +37 -0
- package/dist/commands-v2/registry.js +80 -0
- package/dist/commands-v2/types.d.ts +75 -0
- package/dist/commands-v2/types.js +7 -0
- package/dist/commands.js +110 -7
- package/dist/index.js +288 -29
- package/dist/input-handlers/index.d.ts +7 -0
- package/dist/input-handlers/index.js +7 -0
- package/dist/input-handlers/memory-handler.d.ts +26 -0
- package/dist/input-handlers/memory-handler.js +68 -0
- package/dist/repl-helpers.d.ts +63 -0
- package/dist/repl-helpers.js +318 -0
- package/dist/repl-v2.d.ts +155 -0
- package/dist/repl-v2.js +774 -0
- package/dist/repl.d.ts +32 -4
- package/dist/repl.js +250 -977
- package/dist/settings/index.d.ts +23 -0
- package/dist/settings/index.js +48 -0
- package/dist/settings/paths.d.ts +110 -0
- package/dist/settings/paths.js +264 -0
- package/dist/templates/compilr-md.js +7 -4
- package/dist/templates/index.js +3 -4
- package/dist/themes/colors.js +3 -1
- package/dist/themes/registry.d.ts +5 -36
- package/dist/themes/registry.js +11 -95
- package/dist/themes/types.d.ts +3 -38
- package/dist/themes/types.js +2 -2
- package/dist/tools/anchor-tools.d.ts +31 -0
- package/dist/tools/anchor-tools.js +255 -0
- package/dist/tools/backlog-wrappers.d.ts +54 -0
- package/dist/tools/backlog-wrappers.js +338 -0
- package/dist/tools/backlog.js +1 -1
- package/dist/tools/db-tools.d.ts +65 -0
- package/dist/tools/db-tools.js +19 -0
- package/dist/tools/document-db.d.ts +43 -0
- package/dist/tools/document-db.js +220 -0
- package/dist/tools/project-db.d.ts +102 -0
- package/dist/tools/project-db.js +370 -0
- package/dist/tools/workitem-db.d.ts +103 -0
- package/dist/tools/workitem-db.js +549 -0
- package/dist/tools.js +13 -3
- package/dist/ui/agents-overlay-v2.d.ts +43 -0
- package/dist/ui/agents-overlay-v2.js +809 -0
- package/dist/ui/agents-overlay.d.ts +5 -5
- package/dist/ui/agents-overlay.js +782 -420
- package/dist/ui/anchors-overlay.d.ts +12 -0
- package/dist/ui/anchors-overlay.js +775 -0
- package/dist/ui/arch-type-overlay.d.ts +1 -6
- package/dist/ui/arch-type-overlay.js +175 -203
- package/dist/ui/ask-user-overlay-v2.d.ts +26 -0
- package/dist/ui/ask-user-overlay-v2.js +555 -0
- package/dist/ui/ask-user-overlay.d.ts +2 -2
- package/dist/ui/ask-user-overlay.js +443 -535
- package/dist/ui/ask-user-simple-overlay-v2.d.ts +25 -0
- package/dist/ui/ask-user-simple-overlay-v2.js +215 -0
- package/dist/ui/ask-user-simple-overlay.d.ts +2 -2
- package/dist/ui/ask-user-simple-overlay.js +182 -209
- package/dist/ui/backlog-overlay.d.ts +16 -1
- package/dist/ui/backlog-overlay.js +525 -659
- package/dist/ui/base/index.d.ts +26 -0
- package/dist/ui/base/index.js +33 -0
- package/dist/ui/base/inline-overlay-utils.d.ts +217 -0
- package/dist/ui/base/inline-overlay-utils.js +320 -0
- package/dist/ui/base/inline-overlay.d.ts +159 -0
- package/dist/ui/base/inline-overlay.js +257 -0
- package/dist/ui/base/key-utils.d.ts +15 -0
- package/dist/ui/base/key-utils.js +30 -0
- package/dist/ui/base/overlay-base-v2.d.ts +193 -0
- package/dist/ui/base/overlay-base-v2.js +246 -0
- package/dist/ui/base/overlay-base.d.ts +156 -0
- package/dist/ui/base/overlay-base.js +238 -0
- package/dist/ui/base/overlay-lifecycle.d.ts +65 -0
- package/dist/ui/base/overlay-lifecycle.js +159 -0
- package/dist/ui/base/overlay-types.d.ts +185 -0
- package/dist/ui/base/overlay-types.js +7 -0
- package/dist/ui/base/render-utils.d.ts +8 -0
- package/dist/ui/base/render-utils.js +11 -0
- package/dist/ui/base/screen-stack.d.ts +148 -0
- package/dist/ui/base/screen-stack.js +184 -0
- package/dist/ui/base/tabbed-list-overlay-v2.d.ts +103 -0
- package/dist/ui/base/tabbed-list-overlay-v2.js +317 -0
- package/dist/ui/base/tabbed-list-overlay.d.ts +153 -0
- package/dist/ui/base/tabbed-list-overlay.js +369 -0
- package/dist/ui/commands-overlay-v2.d.ts +33 -0
- package/dist/ui/commands-overlay-v2.js +441 -0
- package/dist/ui/commands-overlay.d.ts +7 -2
- package/dist/ui/commands-overlay.js +384 -355
- package/dist/ui/config-overlay.d.ts +5 -4
- package/dist/ui/config-overlay.js +243 -513
- package/dist/ui/conversation.d.ts +75 -4
- package/dist/ui/conversation.js +374 -161
- package/dist/ui/docs-overlay.d.ts +17 -0
- package/dist/ui/docs-overlay.js +303 -0
- package/dist/ui/ephemeral.d.ts +1 -1
- package/dist/ui/ephemeral.js +1 -1
- package/dist/ui/features/index.d.ts +34 -0
- package/dist/ui/features/index.js +34 -0
- package/dist/ui/features/input-feature.d.ts +85 -0
- package/dist/ui/features/input-feature.js +238 -0
- package/dist/ui/features/list-feature.d.ts +155 -0
- package/dist/ui/features/list-feature.js +244 -0
- package/dist/ui/features/pagination-feature.d.ts +154 -0
- package/dist/ui/features/pagination-feature.js +238 -0
- package/dist/ui/features/search-feature.d.ts +148 -0
- package/dist/ui/features/search-feature.js +185 -0
- package/dist/ui/features/tab-feature.d.ts +194 -0
- package/dist/ui/features/tab-feature.js +307 -0
- package/dist/ui/footer-v2.d.ts +222 -0
- package/dist/ui/footer-v2.js +1349 -0
- package/dist/ui/footer.d.ts +107 -0
- package/dist/ui/footer.js +359 -67
- package/dist/ui/guardrail-overlay.d.ts +29 -0
- package/dist/ui/guardrail-overlay.js +145 -0
- package/dist/ui/help-overlay-v2.d.ts +34 -0
- package/dist/ui/help-overlay-v2.js +309 -0
- package/dist/ui/help-overlay.d.ts +16 -0
- package/dist/ui/help-overlay.js +316 -0
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +1 -3
- package/dist/ui/init-overlay-v2.d.ts +34 -0
- package/dist/ui/init-overlay-v2.js +600 -0
- package/dist/ui/init-overlay.d.ts +12 -2
- package/dist/ui/init-overlay.js +349 -270
- package/dist/ui/input-prompt-v2.d.ts +1 -0
- package/dist/ui/input-prompt-v2.js +14 -6
- package/dist/ui/input-prompt.d.ts +116 -33
- package/dist/ui/input-prompt.js +536 -337
- package/dist/ui/iteration-limit-overlay-v2.d.ts +21 -0
- package/dist/ui/iteration-limit-overlay-v2.js +114 -0
- package/dist/ui/iteration-limit-overlay.d.ts +2 -2
- package/dist/ui/iteration-limit-overlay.js +92 -128
- package/dist/ui/keys-overlay-v2.d.ts +41 -0
- package/dist/ui/keys-overlay-v2.js +248 -0
- package/dist/ui/keys-overlay.d.ts +1 -0
- package/dist/ui/keys-overlay.js +203 -141
- package/dist/ui/line-utils.d.ts +88 -0
- package/dist/ui/line-utils.js +150 -0
- package/dist/ui/live-region.d.ts +161 -0
- package/dist/ui/live-region.js +387 -0
- package/dist/ui/mascot/expressions.d.ts +32 -0
- package/dist/ui/mascot/expressions.js +213 -0
- package/dist/ui/mascot/index.d.ts +8 -0
- package/dist/ui/mascot/index.js +8 -0
- package/dist/ui/mascot/renderer.d.ts +19 -0
- package/dist/ui/mascot/renderer.js +97 -0
- package/dist/ui/mascot-overlay-v2.d.ts +41 -0
- package/dist/ui/mascot-overlay-v2.js +138 -0
- package/dist/ui/mascot-overlay.d.ts +21 -0
- package/dist/ui/mascot-overlay.js +146 -0
- package/dist/ui/model-overlay-v2.d.ts +49 -0
- package/dist/ui/model-overlay-v2.js +118 -0
- package/dist/ui/model-overlay.d.ts +27 -0
- package/dist/ui/model-overlay.js +221 -0
- package/dist/ui/model-warning-overlay.js +3 -5
- package/dist/ui/new-overlay.d.ts +34 -0
- package/dist/ui/new-overlay.js +604 -0
- package/dist/ui/overlay/impl/agents-overlay-v2.d.ts +45 -0
- package/dist/ui/overlay/impl/agents-overlay-v2.js +825 -0
- package/dist/ui/overlay/impl/anchors-overlay-v2.d.ts +47 -0
- package/dist/ui/overlay/impl/anchors-overlay-v2.js +783 -0
- package/dist/ui/overlay/impl/arch-type-overlay-v2.d.ts +37 -0
- package/dist/ui/overlay/impl/arch-type-overlay-v2.js +240 -0
- package/dist/ui/overlay/impl/ask-user-overlay-v2.d.ts +72 -0
- package/dist/ui/overlay/impl/ask-user-overlay-v2.js +584 -0
- package/dist/ui/overlay/impl/ask-user-simple-overlay-v2.d.ts +46 -0
- package/dist/ui/overlay/impl/ask-user-simple-overlay-v2.js +204 -0
- package/dist/ui/overlay/impl/backlog-overlay-v2.d.ts +49 -0
- package/dist/ui/overlay/impl/backlog-overlay-v2.js +642 -0
- package/dist/ui/overlay/impl/commands-overlay-v2.d.ts +33 -0
- package/dist/ui/overlay/impl/commands-overlay-v2.js +441 -0
- package/dist/ui/overlay/impl/config-overlay-v2.d.ts +100 -0
- package/dist/ui/overlay/impl/config-overlay-v2.js +654 -0
- package/dist/ui/overlay/impl/dashboard-overlay-v2.d.ts +55 -0
- package/dist/ui/overlay/impl/dashboard-overlay-v2.js +359 -0
- package/dist/ui/overlay/impl/docs-overlay-v2.d.ts +45 -0
- package/dist/ui/overlay/impl/docs-overlay-v2.js +114 -0
- package/dist/ui/overlay/impl/document-detail-overlay-v2.d.ts +77 -0
- package/dist/ui/overlay/impl/document-detail-overlay-v2.js +1071 -0
- package/dist/ui/overlay/impl/guardrail-overlay-v2.d.ts +43 -0
- package/dist/ui/overlay/impl/guardrail-overlay-v2.js +114 -0
- package/dist/ui/overlay/impl/help-overlay-v2.d.ts +34 -0
- package/dist/ui/overlay/impl/help-overlay-v2.js +309 -0
- package/dist/ui/overlay/impl/init-overlay-v2.d.ts +77 -0
- package/dist/ui/overlay/impl/init-overlay-v2.js +593 -0
- package/dist/ui/overlay/impl/init-setup-overlay-v2.d.ts +25 -0
- package/dist/ui/overlay/impl/init-setup-overlay-v2.js +97 -0
- package/dist/ui/overlay/impl/iteration-limit-overlay-v2.d.ts +35 -0
- package/dist/ui/overlay/impl/iteration-limit-overlay-v2.js +105 -0
- package/dist/ui/overlay/impl/keys-overlay-v2.d.ts +41 -0
- package/dist/ui/overlay/impl/keys-overlay-v2.js +248 -0
- package/dist/ui/overlay/impl/mascot-overlay-v2.d.ts +41 -0
- package/dist/ui/overlay/impl/mascot-overlay-v2.js +138 -0
- package/dist/ui/overlay/impl/model-overlay-v2.d.ts +49 -0
- package/dist/ui/overlay/impl/model-overlay-v2.js +118 -0
- package/dist/ui/overlay/impl/model-warning-overlay-v2.d.ts +46 -0
- package/dist/ui/overlay/impl/model-warning-overlay-v2.js +132 -0
- package/dist/ui/overlay/impl/new-overlay-v2.d.ts +77 -0
- package/dist/ui/overlay/impl/new-overlay-v2.js +593 -0
- package/dist/ui/overlay/impl/permission-overlay-v2.d.ts +36 -0
- package/dist/ui/overlay/impl/permission-overlay-v2.js +380 -0
- package/dist/ui/overlay/impl/projects-overlay-v2.d.ts +36 -0
- package/dist/ui/overlay/impl/projects-overlay-v2.js +499 -0
- package/dist/ui/overlay/impl/theme-overlay-v2.d.ts +42 -0
- package/dist/ui/overlay/impl/theme-overlay-v2.js +135 -0
- package/dist/ui/overlay/impl/tools-overlay-v2.d.ts +47 -0
- package/dist/ui/overlay/impl/tools-overlay-v2.js +218 -0
- package/dist/ui/overlay/impl/tutorial-overlay-v2.d.ts +31 -0
- package/dist/ui/overlay/impl/tutorial-overlay-v2.js +1035 -0
- package/dist/ui/overlay/impl/workflow-overlay-v2.d.ts +80 -0
- package/dist/ui/overlay/impl/workflow-overlay-v2.js +637 -0
- package/dist/ui/overlay/index.d.ts +33 -0
- package/dist/ui/overlay/index.js +35 -0
- package/dist/ui/overlay/key-utils.d.ts +6 -0
- package/dist/ui/overlay/key-utils.js +6 -0
- package/dist/ui/overlay/overlay-types.d.ts +128 -0
- package/dist/ui/overlay/overlay-types.js +22 -0
- package/dist/ui/overlay/types.d.ts +135 -0
- package/dist/ui/overlay/types.js +22 -0
- package/dist/ui/overlays/help-overlay-v2.d.ts +28 -0
- package/dist/ui/overlays/help-overlay-v2.js +198 -0
- package/dist/ui/overlays/index.d.ts +11 -0
- package/dist/ui/overlays/index.js +11 -0
- package/dist/ui/overlays.d.ts +0 -4
- package/dist/ui/overlays.js +0 -444
- package/dist/ui/permission-overlay-v2.d.ts +36 -0
- package/dist/ui/permission-overlay-v2.js +380 -0
- package/dist/ui/permission-overlay.d.ts +1 -1
- package/dist/ui/permission-overlay.js +186 -298
- package/dist/ui/projects-overlay.d.ts +19 -0
- package/dist/ui/projects-overlay.js +484 -0
- package/dist/ui/providers/types.d.ts +178 -0
- package/dist/ui/providers/types.js +9 -0
- package/dist/ui/render-modes.d.ts +36 -0
- package/dist/ui/render-modes.js +44 -0
- package/dist/ui/startup-menu.d.ts +36 -0
- package/dist/ui/startup-menu.js +236 -0
- package/dist/ui/subagent-renderer.d.ts +117 -0
- package/dist/ui/subagent-renderer.js +334 -0
- package/dist/ui/terminal-codes.d.ts +94 -0
- package/dist/ui/terminal-codes.js +124 -0
- package/dist/ui/terminal-renderer.d.ts +221 -0
- package/dist/ui/terminal-renderer.js +751 -0
- package/dist/ui/terminal-ui.d.ts +463 -0
- package/dist/ui/terminal-ui.js +2296 -0
- package/dist/ui/terminal.d.ts +20 -0
- package/dist/ui/terminal.js +72 -0
- package/dist/ui/theme-overlay-v2.d.ts +42 -0
- package/dist/ui/theme-overlay-v2.js +135 -0
- package/dist/ui/theme-overlay.d.ts +24 -0
- package/dist/ui/theme-overlay.js +127 -0
- package/dist/ui/todo-zone.js +53 -25
- package/dist/ui/tool-formatters.d.ts +16 -0
- package/dist/ui/tool-formatters.js +516 -0
- package/dist/ui/tools-overlay-v2.d.ts +47 -0
- package/dist/ui/tools-overlay-v2.js +218 -0
- package/dist/ui/tools-overlay.d.ts +10 -2
- package/dist/ui/tools-overlay.js +172 -220
- package/dist/ui/tutorial-overlay-v2.d.ts +31 -0
- package/dist/ui/tutorial-overlay-v2.js +1035 -0
- package/dist/ui/tutorial-overlay.d.ts +1 -0
- package/dist/ui/tutorial-overlay.js +400 -302
- package/dist/ui/workflow-overlay.d.ts +22 -0
- package/dist/ui/workflow-overlay.js +636 -0
- package/dist/utils/debug-log.d.ts +28 -0
- package/dist/utils/debug-log.js +57 -0
- package/dist/utils/model-tiers.js +1 -1
- package/dist/utils/path-safety.d.ts +56 -0
- package/dist/utils/path-safety.js +239 -0
- package/dist/workflow/guided-mode-injector.d.ts +42 -0
- package/dist/workflow/guided-mode-injector.js +191 -0
- package/dist/workflow/index.d.ts +8 -0
- package/dist/workflow/index.js +8 -0
- package/dist/workflow/step-criteria.d.ts +62 -0
- package/dist/workflow/step-criteria.js +150 -0
- package/dist/workflow/step-tracker.d.ts +92 -0
- package/dist/workflow/step-tracker.js +141 -0
- package/package.json +12 -5
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backlog Overlay V2
|
|
3
|
+
*
|
|
4
|
+
* Modal overlay for viewing and managing the project backlog.
|
|
5
|
+
* Uses TabbedListOverlayV2 for consistent list/tab/search behavior.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Search by title/description
|
|
9
|
+
* - Filter tabs by type (All/Feature/Bug/Tech-Debt/Chore)
|
|
10
|
+
* - Pagination for large backlogs
|
|
11
|
+
* - Quick status toggle with Space
|
|
12
|
+
* - Add new items with wizard
|
|
13
|
+
* - Database-backed with project isolation
|
|
14
|
+
*/
|
|
15
|
+
import chalk from 'chalk';
|
|
16
|
+
import * as terminal from '../../terminal.js';
|
|
17
|
+
import { TabbedListOverlayV2, BaseScreen, stay, popScreen, closeOverlay, isEscape, isCtrlC, isEnter, isSpace, isNavigateUp, isNavigateDown, isLeftArrow, isRightArrow, isVimLeft, isVimRight, isBackspace, extractPrintable, renderBorder, wrapText, } from '../../base/index.js';
|
|
18
|
+
import { workItemRepository } from '../../../db/repositories/work-item-repository.js';
|
|
19
|
+
import { getActiveProject } from '../../../tools/project-db.js';
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// Constants
|
|
22
|
+
// =============================================================================
|
|
23
|
+
const PAGE_SIZE = 8;
|
|
24
|
+
const TYPE_OPTIONS = ['feature', 'bug', 'tech-debt', 'chore'];
|
|
25
|
+
const STATUS_OPTIONS = ['📋', '🚧', '✅'];
|
|
26
|
+
const PRIORITY_OPTIONS = ['critical', 'high', 'medium', 'low'];
|
|
27
|
+
const TYPE_LABELS = {
|
|
28
|
+
'feature': 'Feature',
|
|
29
|
+
'bug': 'Bug',
|
|
30
|
+
'tech-debt': 'Tech Debt',
|
|
31
|
+
'chore': 'Chore',
|
|
32
|
+
};
|
|
33
|
+
const PRIORITY_LABELS = {
|
|
34
|
+
'critical': 'Critical',
|
|
35
|
+
'high': 'High',
|
|
36
|
+
'medium': 'Medium',
|
|
37
|
+
'low': 'Low',
|
|
38
|
+
};
|
|
39
|
+
const TABS = [
|
|
40
|
+
{ id: 'all', label: 'All' },
|
|
41
|
+
{ id: 'active', label: 'Active' },
|
|
42
|
+
{ id: 'feature', label: 'Feature' },
|
|
43
|
+
{ id: 'bug', label: 'Bug' },
|
|
44
|
+
{ id: 'tech-debt', label: 'Tech' },
|
|
45
|
+
{ id: 'chore', label: 'Chore' },
|
|
46
|
+
];
|
|
47
|
+
// =============================================================================
|
|
48
|
+
// Status Mapping (Database ↔ Display)
|
|
49
|
+
// =============================================================================
|
|
50
|
+
/**
|
|
51
|
+
* Map database status to display emoji
|
|
52
|
+
*/
|
|
53
|
+
function dbStatusToEmoji(status) {
|
|
54
|
+
switch (status) {
|
|
55
|
+
case 'backlog': return '📋';
|
|
56
|
+
case 'in_progress': return '🚧';
|
|
57
|
+
case 'completed': return '✅';
|
|
58
|
+
case 'skipped': return '✅'; // Show skipped as completed for simplicity
|
|
59
|
+
default: return '📋';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Map display emoji to database status
|
|
64
|
+
*/
|
|
65
|
+
function emojiToDbStatus(emoji) {
|
|
66
|
+
switch (emoji) {
|
|
67
|
+
case '📋': return 'backlog';
|
|
68
|
+
case '🚧': return 'in_progress';
|
|
69
|
+
case '✅': return 'completed';
|
|
70
|
+
default: return 'backlog';
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Convert database WorkItem to display BacklogItem
|
|
75
|
+
*/
|
|
76
|
+
function workItemToBacklogItem(item) {
|
|
77
|
+
return {
|
|
78
|
+
id: item.itemId,
|
|
79
|
+
dbId: item.id,
|
|
80
|
+
type: item.type,
|
|
81
|
+
status: dbStatusToEmoji(item.status),
|
|
82
|
+
priority: item.priority,
|
|
83
|
+
title: item.title,
|
|
84
|
+
description: item.description ?? '',
|
|
85
|
+
commit: item.commitHash ?? undefined,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
// =============================================================================
|
|
89
|
+
// Database Operations
|
|
90
|
+
// =============================================================================
|
|
91
|
+
/**
|
|
92
|
+
* Load items from database for a project
|
|
93
|
+
*/
|
|
94
|
+
function loadItemsFromDb(projectId) {
|
|
95
|
+
const result = workItemRepository.query({
|
|
96
|
+
project_id: projectId,
|
|
97
|
+
limit: 1000, // Get all items
|
|
98
|
+
});
|
|
99
|
+
return result.items.map(workItemToBacklogItem);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Update an item's status in the database
|
|
103
|
+
*/
|
|
104
|
+
function updateItemStatus(dbId, status) {
|
|
105
|
+
workItemRepository.update(dbId, {
|
|
106
|
+
status: emojiToDbStatus(status),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Update an item's type in the database
|
|
111
|
+
*/
|
|
112
|
+
function updateItemType(dbId, type) {
|
|
113
|
+
workItemRepository.update(dbId, { type });
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Update an item's priority in the database
|
|
117
|
+
*/
|
|
118
|
+
function updateItemPriority(dbId, priority) {
|
|
119
|
+
workItemRepository.update(dbId, { priority });
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Create a new item in the database
|
|
123
|
+
*/
|
|
124
|
+
function createItem(projectId, type, priority, title, description) {
|
|
125
|
+
const item = workItemRepository.create({
|
|
126
|
+
project_id: projectId,
|
|
127
|
+
type,
|
|
128
|
+
priority,
|
|
129
|
+
title,
|
|
130
|
+
description,
|
|
131
|
+
});
|
|
132
|
+
return workItemToBacklogItem(item);
|
|
133
|
+
}
|
|
134
|
+
// =============================================================================
|
|
135
|
+
// Helper Functions
|
|
136
|
+
// =============================================================================
|
|
137
|
+
function getStatusLabel(status) {
|
|
138
|
+
switch (status) {
|
|
139
|
+
case '📋': return 'Backlog';
|
|
140
|
+
case '🚧': return 'In Progress';
|
|
141
|
+
case '✅': return 'Completed';
|
|
142
|
+
default: return '';
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// =============================================================================
|
|
146
|
+
// Detail Screen (In-Place Editing)
|
|
147
|
+
// =============================================================================
|
|
148
|
+
/**
|
|
149
|
+
* Screen for editing item type, status, and priority.
|
|
150
|
+
* Renders in-place over the list.
|
|
151
|
+
*/
|
|
152
|
+
class DetailScreen extends BaseScreen {
|
|
153
|
+
item;
|
|
154
|
+
editState;
|
|
155
|
+
styles;
|
|
156
|
+
selectedField = 0; // 0=type, 1=status, 2=priority
|
|
157
|
+
constructor(item, editState, styles) {
|
|
158
|
+
super();
|
|
159
|
+
this.item = item;
|
|
160
|
+
this.editState = editState;
|
|
161
|
+
this.styles = styles;
|
|
162
|
+
}
|
|
163
|
+
render() {
|
|
164
|
+
const s = this.styles;
|
|
165
|
+
const cols = terminal.getTerminalWidth();
|
|
166
|
+
const border = renderBorder(cols, s);
|
|
167
|
+
const lines = [];
|
|
168
|
+
// Header
|
|
169
|
+
lines.push(border);
|
|
170
|
+
lines.push(` ${s.primaryBold('Edit Item')} ${s.muted(this.item.id)}`);
|
|
171
|
+
lines.push('');
|
|
172
|
+
// Title
|
|
173
|
+
lines.push(` ${chalk.bold(this.item.title)}`);
|
|
174
|
+
lines.push('');
|
|
175
|
+
// Editable fields with selection indicator
|
|
176
|
+
const typePrefix = this.selectedField === 0 ? s.primary('❯ Type: ') : s.muted(' Type: ');
|
|
177
|
+
const typeValue = this.selectedField === 0 ? s.primary(TYPE_LABELS[this.item.type]) : TYPE_LABELS[this.item.type];
|
|
178
|
+
lines.push(` ${typePrefix}${typeValue}`);
|
|
179
|
+
const statusLabel = this.item.status + ' ' + getStatusLabel(this.item.status);
|
|
180
|
+
const statusPrefix = this.selectedField === 1 ? s.primary('❯ Status: ') : s.muted(' Status: ');
|
|
181
|
+
const statusValue = this.selectedField === 1 ? s.primary(statusLabel) : statusLabel;
|
|
182
|
+
lines.push(` ${statusPrefix}${statusValue}`);
|
|
183
|
+
const priPrefix = this.selectedField === 2 ? s.primary('❯ Priority: ') : s.muted(' Priority: ');
|
|
184
|
+
const priValue = this.selectedField === 2 ? s.primary(PRIORITY_LABELS[this.item.priority]) : PRIORITY_LABELS[this.item.priority];
|
|
185
|
+
lines.push(` ${priPrefix}${priValue}`);
|
|
186
|
+
lines.push('');
|
|
187
|
+
// Description
|
|
188
|
+
lines.push(` ${s.muted('Description:')}`);
|
|
189
|
+
const descLines = wrapText(this.item.description, cols - 6);
|
|
190
|
+
for (const line of descLines) {
|
|
191
|
+
lines.push(` ${line}`);
|
|
192
|
+
}
|
|
193
|
+
// Commit info
|
|
194
|
+
if (this.item.commit) {
|
|
195
|
+
lines.push('');
|
|
196
|
+
lines.push(` ${s.muted('Commit:')} ${this.item.commit}`);
|
|
197
|
+
}
|
|
198
|
+
lines.push('');
|
|
199
|
+
lines.push(border);
|
|
200
|
+
lines.push(` ${s.muted('↑↓/jk Select field · ←→/hl Change value · Esc Back')}`);
|
|
201
|
+
lines.push(border);
|
|
202
|
+
return lines;
|
|
203
|
+
}
|
|
204
|
+
handleKey(data) {
|
|
205
|
+
// Ctrl+C closes everything
|
|
206
|
+
if (isCtrlC(data)) {
|
|
207
|
+
return closeOverlay({ modified: this.editState.modified });
|
|
208
|
+
}
|
|
209
|
+
// Escape - back to list
|
|
210
|
+
if (isEscape(data)) {
|
|
211
|
+
return popScreen();
|
|
212
|
+
}
|
|
213
|
+
// Up/Down or k/j - navigate fields
|
|
214
|
+
if (isNavigateUp(data) && this.selectedField > 0) {
|
|
215
|
+
this.selectedField = (this.selectedField - 1);
|
|
216
|
+
return stay();
|
|
217
|
+
}
|
|
218
|
+
if (isNavigateDown(data) && this.selectedField < 2) {
|
|
219
|
+
this.selectedField = (this.selectedField + 1);
|
|
220
|
+
return stay();
|
|
221
|
+
}
|
|
222
|
+
// Left/Right or h/l - change value of selected field
|
|
223
|
+
const isLeft = isLeftArrow(data) || isVimLeft(data);
|
|
224
|
+
const isRight = isRightArrow(data) || isVimRight(data);
|
|
225
|
+
if (isLeft || isRight) {
|
|
226
|
+
const direction = isRight ? 1 : -1;
|
|
227
|
+
if (this.selectedField === 0) {
|
|
228
|
+
// Type
|
|
229
|
+
const currentIdx = TYPE_OPTIONS.indexOf(this.item.type);
|
|
230
|
+
const newIdx = (currentIdx + direction + TYPE_OPTIONS.length) % TYPE_OPTIONS.length;
|
|
231
|
+
const newType = TYPE_OPTIONS[newIdx];
|
|
232
|
+
updateItemType(this.item.dbId, newType);
|
|
233
|
+
this.item.type = newType;
|
|
234
|
+
}
|
|
235
|
+
else if (this.selectedField === 1) {
|
|
236
|
+
// Status
|
|
237
|
+
const currentIdx = STATUS_OPTIONS.indexOf(this.item.status);
|
|
238
|
+
const newIdx = (currentIdx + direction + STATUS_OPTIONS.length) % STATUS_OPTIONS.length;
|
|
239
|
+
const newStatus = STATUS_OPTIONS[newIdx];
|
|
240
|
+
updateItemStatus(this.item.dbId, newStatus);
|
|
241
|
+
this.item.status = newStatus;
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
// Priority
|
|
245
|
+
const currentIdx = PRIORITY_OPTIONS.indexOf(this.item.priority);
|
|
246
|
+
const newIdx = (currentIdx + direction + PRIORITY_OPTIONS.length) % PRIORITY_OPTIONS.length;
|
|
247
|
+
const newPriority = PRIORITY_OPTIONS[newIdx];
|
|
248
|
+
updateItemPriority(this.item.dbId, newPriority);
|
|
249
|
+
this.item.priority = newPriority;
|
|
250
|
+
}
|
|
251
|
+
this.editState.modified = true;
|
|
252
|
+
this.editState.reloadItems();
|
|
253
|
+
return stay();
|
|
254
|
+
}
|
|
255
|
+
return stay(false);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Minimum height for stable rendering.
|
|
259
|
+
*/
|
|
260
|
+
getMinHeight() {
|
|
261
|
+
return 20;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// =============================================================================
|
|
265
|
+
// Add Screen (Multi-Step Wizard)
|
|
266
|
+
// =============================================================================
|
|
267
|
+
/**
|
|
268
|
+
* Screen for adding a new backlog item.
|
|
269
|
+
* 5 steps: type, priority, title, description, confirm
|
|
270
|
+
*/
|
|
271
|
+
class AddScreen extends BaseScreen {
|
|
272
|
+
editState;
|
|
273
|
+
styles;
|
|
274
|
+
step = 0;
|
|
275
|
+
selectedIndex = 0;
|
|
276
|
+
inputBuffer = '';
|
|
277
|
+
error = null;
|
|
278
|
+
newItem = { type: null, priority: null, title: '', description: '' };
|
|
279
|
+
constructor(editState, styles) {
|
|
280
|
+
super();
|
|
281
|
+
this.editState = editState;
|
|
282
|
+
this.styles = styles;
|
|
283
|
+
}
|
|
284
|
+
render() {
|
|
285
|
+
const s = this.styles;
|
|
286
|
+
const cols = terminal.getTerminalWidth();
|
|
287
|
+
const border = renderBorder(cols, s);
|
|
288
|
+
const lines = [];
|
|
289
|
+
// Header
|
|
290
|
+
lines.push(border);
|
|
291
|
+
lines.push(` ${s.primaryBold('Add Item')}`);
|
|
292
|
+
lines.push('');
|
|
293
|
+
const stepLabels = ['Type', 'Priority', 'Title', 'Description', 'Confirm'];
|
|
294
|
+
lines.push(` ${s.muted(`Step ${String(this.step + 1)}/5: ${stepLabels[this.step]}`)}`);
|
|
295
|
+
lines.push('');
|
|
296
|
+
switch (this.step) {
|
|
297
|
+
case 0: // Type
|
|
298
|
+
lines.push(` ${chalk.bold('What type of item?')}`);
|
|
299
|
+
lines.push('');
|
|
300
|
+
for (let i = 0; i < TYPE_OPTIONS.length; i++) {
|
|
301
|
+
const isSelected = this.selectedIndex === i;
|
|
302
|
+
const prefix = isSelected ? s.primary('❯ ') : ' ';
|
|
303
|
+
const label = `${String(i + 1)}. ${TYPE_LABELS[TYPE_OPTIONS[i]]}`;
|
|
304
|
+
lines.push(` ${prefix}${isSelected ? s.primary(label) : s.muted(label)}`);
|
|
305
|
+
}
|
|
306
|
+
break;
|
|
307
|
+
case 1: // Priority
|
|
308
|
+
lines.push(` ${chalk.bold('Priority level?')}`);
|
|
309
|
+
lines.push('');
|
|
310
|
+
for (let i = 0; i < PRIORITY_OPTIONS.length; i++) {
|
|
311
|
+
const isSelected = this.selectedIndex === i;
|
|
312
|
+
const prefix = isSelected ? s.primary('❯ ') : ' ';
|
|
313
|
+
const label = `${String(i + 1)}. ${PRIORITY_LABELS[PRIORITY_OPTIONS[i]]}`;
|
|
314
|
+
lines.push(` ${prefix}${isSelected ? s.primary(label) : s.muted(label)}`);
|
|
315
|
+
}
|
|
316
|
+
break;
|
|
317
|
+
case 2: // Title
|
|
318
|
+
lines.push(` ${chalk.bold('Title (short description)')}`);
|
|
319
|
+
lines.push('');
|
|
320
|
+
lines.push(` > ${this.inputBuffer}█`);
|
|
321
|
+
lines.push('');
|
|
322
|
+
if (this.error) {
|
|
323
|
+
lines.push(` ${s.error(this.error)}`);
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
lines.push(` ${s.muted('Keep it concise (2-100 characters)')}`);
|
|
327
|
+
}
|
|
328
|
+
break;
|
|
329
|
+
case 3: // Description
|
|
330
|
+
lines.push(` ${chalk.bold('Description (details)')}`);
|
|
331
|
+
lines.push('');
|
|
332
|
+
lines.push(` > ${this.inputBuffer}█`);
|
|
333
|
+
lines.push('');
|
|
334
|
+
if (this.error) {
|
|
335
|
+
lines.push(` ${s.error(this.error)}`);
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
lines.push(` ${s.muted('Explain the requirement or issue')}`);
|
|
339
|
+
}
|
|
340
|
+
break;
|
|
341
|
+
case 4: // Confirm
|
|
342
|
+
lines.push(` ${chalk.bold('Confirm new item:')}`);
|
|
343
|
+
lines.push('');
|
|
344
|
+
lines.push(` ${s.muted('Type:')} ${this.newItem.type ? TYPE_LABELS[this.newItem.type] : 'N/A'}`);
|
|
345
|
+
lines.push(` ${s.muted('Priority:')} ${this.newItem.priority ? PRIORITY_LABELS[this.newItem.priority] : 'N/A'}`);
|
|
346
|
+
lines.push(` ${s.muted('Title:')} ${this.newItem.title}`);
|
|
347
|
+
lines.push(` ${s.muted('Description:')} ${this.newItem.description}`);
|
|
348
|
+
lines.push('');
|
|
349
|
+
for (let i = 0; i < 2; i++) {
|
|
350
|
+
const isSelected = this.selectedIndex === i;
|
|
351
|
+
const prefix = isSelected ? s.primary('❯ ') : ' ';
|
|
352
|
+
const label = i === 0 ? '1. Create item' : '2. Cancel';
|
|
353
|
+
lines.push(` ${prefix}${isSelected ? s.primary(label) : s.muted(label)}`);
|
|
354
|
+
}
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
lines.push('');
|
|
358
|
+
lines.push(border);
|
|
359
|
+
if (this.step === 2 || this.step === 3) {
|
|
360
|
+
lines.push(` ${s.muted('Enter Confirm · Esc Back')}`);
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
lines.push(` ${s.muted('↑↓/jk Navigate · Enter Select · Esc Back')}`);
|
|
364
|
+
}
|
|
365
|
+
lines.push(border);
|
|
366
|
+
return lines;
|
|
367
|
+
}
|
|
368
|
+
handleKey(data) {
|
|
369
|
+
const char = extractPrintable(data);
|
|
370
|
+
// Ctrl+C closes everything
|
|
371
|
+
if (isCtrlC(data)) {
|
|
372
|
+
return closeOverlay({ modified: this.editState.modified });
|
|
373
|
+
}
|
|
374
|
+
// Text input steps (title and description)
|
|
375
|
+
if (this.step === 2 || this.step === 3) {
|
|
376
|
+
if (isEscape(data)) {
|
|
377
|
+
if (this.step === 2) {
|
|
378
|
+
this.step = 1;
|
|
379
|
+
this.selectedIndex = 0;
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
this.step = 2;
|
|
383
|
+
this.inputBuffer = this.newItem.title;
|
|
384
|
+
}
|
|
385
|
+
this.error = null;
|
|
386
|
+
return stay();
|
|
387
|
+
}
|
|
388
|
+
if (isEnter(data)) {
|
|
389
|
+
const text = this.inputBuffer.trim();
|
|
390
|
+
if (this.step === 2) {
|
|
391
|
+
if (text.length < 2) {
|
|
392
|
+
this.error = 'Title too short (min 2 characters)';
|
|
393
|
+
}
|
|
394
|
+
else if (text.length > 100) {
|
|
395
|
+
this.error = 'Title too long (max 100 characters)';
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
this.newItem.title = text;
|
|
399
|
+
this.step = 3;
|
|
400
|
+
this.inputBuffer = '';
|
|
401
|
+
this.error = null;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
if (text.length < 5) {
|
|
406
|
+
this.error = 'Description too short (min 5 characters)';
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
this.newItem.description = text;
|
|
410
|
+
this.step = 4;
|
|
411
|
+
this.selectedIndex = 0;
|
|
412
|
+
this.error = null;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return stay();
|
|
416
|
+
}
|
|
417
|
+
if (isBackspace(data)) {
|
|
418
|
+
this.inputBuffer = this.inputBuffer.slice(0, -1);
|
|
419
|
+
return stay();
|
|
420
|
+
}
|
|
421
|
+
if (char) {
|
|
422
|
+
this.inputBuffer += char;
|
|
423
|
+
return stay();
|
|
424
|
+
}
|
|
425
|
+
return stay(false);
|
|
426
|
+
}
|
|
427
|
+
// Selection steps (type, priority, confirm)
|
|
428
|
+
if (isEscape(data)) {
|
|
429
|
+
if (this.step === 0) {
|
|
430
|
+
return popScreen();
|
|
431
|
+
}
|
|
432
|
+
else if (this.step === 4) {
|
|
433
|
+
this.step = 3;
|
|
434
|
+
this.inputBuffer = this.newItem.description;
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
this.step = (this.step - 1);
|
|
438
|
+
this.selectedIndex = 0;
|
|
439
|
+
}
|
|
440
|
+
return stay();
|
|
441
|
+
}
|
|
442
|
+
if (isNavigateUp(data)) {
|
|
443
|
+
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
|
|
444
|
+
return stay();
|
|
445
|
+
}
|
|
446
|
+
if (isNavigateDown(data)) {
|
|
447
|
+
const maxIdx = this.getMaxIndexForStep();
|
|
448
|
+
this.selectedIndex = Math.min(maxIdx, this.selectedIndex + 1);
|
|
449
|
+
return stay();
|
|
450
|
+
}
|
|
451
|
+
if (isEnter(data)) {
|
|
452
|
+
switch (this.step) {
|
|
453
|
+
case 0:
|
|
454
|
+
this.newItem.type = TYPE_OPTIONS[this.selectedIndex];
|
|
455
|
+
this.step = 1;
|
|
456
|
+
this.selectedIndex = 0;
|
|
457
|
+
break;
|
|
458
|
+
case 1:
|
|
459
|
+
this.newItem.priority = PRIORITY_OPTIONS[this.selectedIndex];
|
|
460
|
+
this.step = 2;
|
|
461
|
+
this.inputBuffer = '';
|
|
462
|
+
break;
|
|
463
|
+
case 4:
|
|
464
|
+
if (this.newItem.type && this.newItem.priority && this.editState.projectId) {
|
|
465
|
+
if (this.selectedIndex === 0) {
|
|
466
|
+
// Create item in database
|
|
467
|
+
createItem(this.editState.projectId, this.newItem.type, this.newItem.priority, this.newItem.title, this.newItem.description);
|
|
468
|
+
this.editState.modified = true;
|
|
469
|
+
this.editState.reloadItems();
|
|
470
|
+
}
|
|
471
|
+
return popScreen();
|
|
472
|
+
}
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
return stay();
|
|
476
|
+
}
|
|
477
|
+
// Number keys for quick selection
|
|
478
|
+
if (char && char >= '1' && char <= '9') {
|
|
479
|
+
const numIdx = parseInt(char, 10) - 1;
|
|
480
|
+
const maxIdx = this.getMaxIndexForStep();
|
|
481
|
+
if (numIdx <= maxIdx) {
|
|
482
|
+
this.selectedIndex = numIdx;
|
|
483
|
+
return stay();
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return stay(false);
|
|
487
|
+
}
|
|
488
|
+
getMaxIndexForStep() {
|
|
489
|
+
switch (this.step) {
|
|
490
|
+
case 0: return TYPE_OPTIONS.length - 1;
|
|
491
|
+
case 1: return PRIORITY_OPTIONS.length - 1;
|
|
492
|
+
case 4: return 1;
|
|
493
|
+
default: return 0;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Minimum height for stable rendering.
|
|
498
|
+
*/
|
|
499
|
+
getMinHeight() {
|
|
500
|
+
return 18;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
// =============================================================================
|
|
504
|
+
// Backlog Overlay V2 Class
|
|
505
|
+
// =============================================================================
|
|
506
|
+
export class BacklogOverlayV2 extends TabbedListOverlayV2 {
|
|
507
|
+
type = 'inline';
|
|
508
|
+
id = 'backlog-overlay-v2';
|
|
509
|
+
editState;
|
|
510
|
+
/**
|
|
511
|
+
* @param projectId - Optional project ID to view backlog for.
|
|
512
|
+
* If not provided, uses the active project.
|
|
513
|
+
* Pass this when opening from workflow overlay for a specific project.
|
|
514
|
+
*/
|
|
515
|
+
constructor(projectId) {
|
|
516
|
+
// Use provided projectId, or fall back to active project
|
|
517
|
+
const activeProject = getActiveProject();
|
|
518
|
+
const resolvedProjectId = projectId ?? activeProject?.id ?? null;
|
|
519
|
+
// Load items from database (empty if no project)
|
|
520
|
+
const items = resolvedProjectId ? loadItemsFromDb(resolvedProjectId) : [];
|
|
521
|
+
super({
|
|
522
|
+
title: 'Backlog',
|
|
523
|
+
tabs: TABS,
|
|
524
|
+
items,
|
|
525
|
+
pageSize: PAGE_SIZE,
|
|
526
|
+
filterByTab: (item, tabId) => {
|
|
527
|
+
if (tabId === 'all')
|
|
528
|
+
return true;
|
|
529
|
+
if (tabId === 'active') {
|
|
530
|
+
return item.status === '📋' || item.status === '🚧';
|
|
531
|
+
}
|
|
532
|
+
return item.type === tabId;
|
|
533
|
+
},
|
|
534
|
+
getSearchText: (item) => `${item.id} ${item.title} ${item.description}`,
|
|
535
|
+
renderItem: (item, isSelected, styles) => {
|
|
536
|
+
const cols = terminal.getTerminalWidth();
|
|
537
|
+
const prefix = isSelected ? styles.primary('❯ ') : ' ';
|
|
538
|
+
const idW = 10;
|
|
539
|
+
const typeW = 10;
|
|
540
|
+
const statusW = 4;
|
|
541
|
+
const priW = 8;
|
|
542
|
+
const titleW = Math.max(20, cols - idW - typeW - statusW - priW - 10);
|
|
543
|
+
const row = `${item.id.padEnd(idW)}${item.type.padEnd(typeW)}${item.status.padEnd(statusW)}${item.priority.padEnd(priW)}${item.title.slice(0, titleW - 2)}`;
|
|
544
|
+
if (isSelected) {
|
|
545
|
+
return `${prefix}${styles.primary(row)}`;
|
|
546
|
+
}
|
|
547
|
+
return `${prefix}${styles.muted(row)}`;
|
|
548
|
+
},
|
|
549
|
+
showCount: true,
|
|
550
|
+
emptyMessage: resolvedProjectId === null
|
|
551
|
+
? 'No active project. Use /projects to select one.'
|
|
552
|
+
: 'No items found. Use [a] to add or /design to create initial backlog.',
|
|
553
|
+
noResultsMessage: 'No items match the search.',
|
|
554
|
+
renderSelectedPreview: (item, styles) => {
|
|
555
|
+
const cols = terminal.getTerminalWidth();
|
|
556
|
+
const descPrefix = ' Desc: ';
|
|
557
|
+
const maxDescLen = cols - descPrefix.length - 2;
|
|
558
|
+
const truncatedDesc = item.description.length > maxDescLen
|
|
559
|
+
? item.description.slice(0, maxDescLen - 3) + '...'
|
|
560
|
+
: item.description;
|
|
561
|
+
const lines = [styles.muted(descPrefix) + truncatedDesc];
|
|
562
|
+
if (item.commit) {
|
|
563
|
+
lines.push(styles.muted(' Commit: ') + item.commit);
|
|
564
|
+
}
|
|
565
|
+
return lines;
|
|
566
|
+
},
|
|
567
|
+
footerHints: (searchMode) => {
|
|
568
|
+
if (searchMode) {
|
|
569
|
+
return 'Type to search · Enter/Esc Exit search · Backspace Delete';
|
|
570
|
+
}
|
|
571
|
+
return '/ Search · ↑↓/jk Navigate · ←→/hl Pages · Tab Filter · Space Toggle · a Add · q/Esc Close';
|
|
572
|
+
},
|
|
573
|
+
});
|
|
574
|
+
// Create edit state that screens can share
|
|
575
|
+
this.editState = {
|
|
576
|
+
modified: false,
|
|
577
|
+
projectId: resolvedProjectId,
|
|
578
|
+
items,
|
|
579
|
+
reloadItems: () => {
|
|
580
|
+
if (this.editState.projectId) {
|
|
581
|
+
this.editState.items = loadItemsFromDb(this.editState.projectId);
|
|
582
|
+
// Update the overlay's internal state
|
|
583
|
+
this.state.items = this.editState.items;
|
|
584
|
+
// Re-apply filters
|
|
585
|
+
const currentTabId = this.listConfig.tabs[this.state.currentTab]?.id ?? 'all';
|
|
586
|
+
let filtered = this.state.items.filter((item) => this.listConfig.filterByTab(item, currentTabId));
|
|
587
|
+
if (this.state.searchQuery) {
|
|
588
|
+
const query = this.state.searchQuery.toLowerCase();
|
|
589
|
+
filtered = filtered.filter((item) => this.listConfig.getSearchText(item).toLowerCase().includes(query));
|
|
590
|
+
}
|
|
591
|
+
this.state.filteredItems = filtered;
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
createDetailScreen(item) {
|
|
597
|
+
return new DetailScreen(item, this.editState, this.getStyles());
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Override handleKey to add custom behavior (Space toggle, 'a' add).
|
|
601
|
+
*/
|
|
602
|
+
handleKey(key) {
|
|
603
|
+
const data = key.raw;
|
|
604
|
+
// Only intercept when on the main list screen
|
|
605
|
+
if (this.screenStack.size() === 1) {
|
|
606
|
+
// Ctrl+C always closes
|
|
607
|
+
if (isCtrlC(data)) {
|
|
608
|
+
return this.close({ modified: this.editState.modified });
|
|
609
|
+
}
|
|
610
|
+
// Only handle custom keys when not in search mode
|
|
611
|
+
if (!this.state.searchMode) {
|
|
612
|
+
const char = extractPrintable(data);
|
|
613
|
+
// Space - toggle status
|
|
614
|
+
if (isSpace(data) && this.state.filteredItems.length > 0) {
|
|
615
|
+
const item = this.state.filteredItems[this.state.selectedIndex];
|
|
616
|
+
// Cycle through statuses: 📋 → 🚧 → ✅ → 📋
|
|
617
|
+
const statusIdx = STATUS_OPTIONS.indexOf(item.status);
|
|
618
|
+
const newStatus = STATUS_OPTIONS[(statusIdx + 1) % STATUS_OPTIONS.length];
|
|
619
|
+
updateItemStatus(item.dbId, newStatus);
|
|
620
|
+
this.editState.modified = true;
|
|
621
|
+
this.editState.reloadItems();
|
|
622
|
+
return this.rerender();
|
|
623
|
+
}
|
|
624
|
+
// 'a' - add mode
|
|
625
|
+
if (char === 'a' || char === 'A') {
|
|
626
|
+
if (this.editState.projectId) {
|
|
627
|
+
const addScreen = new AddScreen(this.editState, this.getStyles());
|
|
628
|
+
this.screenStack.push(addScreen);
|
|
629
|
+
return this.rerender();
|
|
630
|
+
}
|
|
631
|
+
return this.noAction();
|
|
632
|
+
}
|
|
633
|
+
// 'q' or Esc closes with result
|
|
634
|
+
if (char === 'q' || isEscape(data)) {
|
|
635
|
+
return this.close({ modified: this.editState.modified });
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
// Let parent handle other keys (tabs, search, pagination, Enter for detail)
|
|
640
|
+
return super.handleKey(key);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commands Overlay V2
|
|
3
|
+
*
|
|
4
|
+
* Modal overlay for viewing and managing custom command definitions.
|
|
5
|
+
* Uses TabbedListOverlayV2 for consistent navigation patterns.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Tabs: All, Project, Personal
|
|
9
|
+
* - Search with / toggle
|
|
10
|
+
* - Detail view for command information
|
|
11
|
+
* - Wizard for creating new commands
|
|
12
|
+
*/
|
|
13
|
+
import { type CustomCommand } from '../../../commands/index.js';
|
|
14
|
+
import { TabbedListOverlayV2, BaseScreen } from '../../base/index.js';
|
|
15
|
+
export interface CommandsOverlayV2Result {
|
|
16
|
+
/** Command that was created (if any) */
|
|
17
|
+
created?: string;
|
|
18
|
+
}
|
|
19
|
+
type CommandItem = CustomCommand | {
|
|
20
|
+
type: 'create';
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Commands overlay using TabbedListOverlayV2.
|
|
24
|
+
* Displays custom commands in a searchable, tabbed list.
|
|
25
|
+
*/
|
|
26
|
+
export declare class CommandsOverlayV2 extends TabbedListOverlayV2<CommandItem, CommandsOverlayV2Result> {
|
|
27
|
+
readonly type: "inline";
|
|
28
|
+
readonly id = "commands-overlay-v2";
|
|
29
|
+
private readonly registry;
|
|
30
|
+
constructor();
|
|
31
|
+
protected createDetailScreen(item: CommandItem): BaseScreen | null;
|
|
32
|
+
}
|
|
33
|
+
export {};
|