@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,783 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anchors Overlay V2
|
|
3
|
+
*
|
|
4
|
+
* Provides a UI for viewing and managing anchors:
|
|
5
|
+
* - View all anchors (global + project-specific)
|
|
6
|
+
* - Add new anchors
|
|
7
|
+
* - Edit existing anchors
|
|
8
|
+
* - Delete anchors
|
|
9
|
+
*
|
|
10
|
+
* Uses TabbedListOverlayV2 for consistent tabbed list behavior.
|
|
11
|
+
*/
|
|
12
|
+
import { TabbedListOverlayV2 } from '../../base/tabbed-list-overlay-v2.js';
|
|
13
|
+
import { BaseScreen, stay, popScreen, closeOverlay, renderBorder, } from '../../base/index.js';
|
|
14
|
+
import { getGlobalAnchorManager, getAnchorManager, } from '../../../anchors/index.js';
|
|
15
|
+
import { getActiveProject } from '../../../tools/project-db.js';
|
|
16
|
+
import * as terminal from '../../terminal.js';
|
|
17
|
+
// =============================================================================
|
|
18
|
+
// Constants
|
|
19
|
+
// =============================================================================
|
|
20
|
+
const PAGE_SIZE = 10;
|
|
21
|
+
const MAX_ANCHOR_LENGTH = 500;
|
|
22
|
+
const PRIORITIES = ['critical', 'safety', 'info'];
|
|
23
|
+
const TABS = [
|
|
24
|
+
{ id: 'all', label: 'All' },
|
|
25
|
+
{ id: 'critical', label: 'Critical' },
|
|
26
|
+
{ id: 'safety', label: 'Safety' },
|
|
27
|
+
{ id: 'info', label: 'Info' },
|
|
28
|
+
{ id: 'global', label: 'Global' },
|
|
29
|
+
{ id: 'project', label: 'Project' },
|
|
30
|
+
];
|
|
31
|
+
// =============================================================================
|
|
32
|
+
// Helper Functions
|
|
33
|
+
// =============================================================================
|
|
34
|
+
function getPriorityBadge(priority, s) {
|
|
35
|
+
switch (priority) {
|
|
36
|
+
case 'critical':
|
|
37
|
+
return s.error('[!]');
|
|
38
|
+
case 'safety':
|
|
39
|
+
return s.warning('[S]');
|
|
40
|
+
case 'info':
|
|
41
|
+
return s.muted('[i]');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function loadAnchors(activeProjectId) {
|
|
45
|
+
const items = [];
|
|
46
|
+
// Global anchors
|
|
47
|
+
const globalManager = getGlobalAnchorManager();
|
|
48
|
+
for (const anchor of globalManager.getAll()) {
|
|
49
|
+
items.push({ anchor, scope: 'global', projectId: null });
|
|
50
|
+
}
|
|
51
|
+
// Project anchors (if active project)
|
|
52
|
+
if (activeProjectId !== null) {
|
|
53
|
+
const projectManager = getAnchorManager(String(activeProjectId));
|
|
54
|
+
for (const anchor of projectManager.getAll()) {
|
|
55
|
+
items.push({ anchor, scope: 'project', projectId: String(activeProjectId) });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Sort by priority (critical first), then by content
|
|
59
|
+
const priorityOrder = { critical: 0, safety: 1, info: 2 };
|
|
60
|
+
items.sort((a, b) => {
|
|
61
|
+
const pa = priorityOrder[a.anchor.priority] ?? 2;
|
|
62
|
+
const pb = priorityOrder[b.anchor.priority] ?? 2;
|
|
63
|
+
if (pa !== pb)
|
|
64
|
+
return pa - pb;
|
|
65
|
+
return a.anchor.content.localeCompare(b.anchor.content);
|
|
66
|
+
});
|
|
67
|
+
return items;
|
|
68
|
+
}
|
|
69
|
+
// =============================================================================
|
|
70
|
+
// Key Detection Helpers
|
|
71
|
+
// =============================================================================
|
|
72
|
+
function isCtrlC(data) {
|
|
73
|
+
return data.length === 1 && data[0] === 0x03;
|
|
74
|
+
}
|
|
75
|
+
function isEscape(data) {
|
|
76
|
+
return data.length === 1 && data[0] === 0x1b;
|
|
77
|
+
}
|
|
78
|
+
function isEnter(data) {
|
|
79
|
+
return data.length === 1 && (data[0] === 0x0d || data[0] === 0x0a);
|
|
80
|
+
}
|
|
81
|
+
function isBackspace(data) {
|
|
82
|
+
return data.length === 1 && (data[0] === 0x7f || data[0] === 0x08);
|
|
83
|
+
}
|
|
84
|
+
function isTab(data) {
|
|
85
|
+
return data.length === 1 && data[0] === 0x09;
|
|
86
|
+
}
|
|
87
|
+
function isShiftTab(data) {
|
|
88
|
+
return data.toString() === '\x1b[Z';
|
|
89
|
+
}
|
|
90
|
+
function isNavigateUp(data) {
|
|
91
|
+
const str = data.toString();
|
|
92
|
+
return str === '\x1b[A' || str === 'k';
|
|
93
|
+
}
|
|
94
|
+
function isNavigateDown(data) {
|
|
95
|
+
const str = data.toString();
|
|
96
|
+
return str === '\x1b[B' || str === 'j';
|
|
97
|
+
}
|
|
98
|
+
function isLeftArrow(data) {
|
|
99
|
+
return data.toString() === '\x1b[D';
|
|
100
|
+
}
|
|
101
|
+
function isRightArrow(data) {
|
|
102
|
+
return data.toString() === '\x1b[C';
|
|
103
|
+
}
|
|
104
|
+
function extractPrintable(data) {
|
|
105
|
+
if (data.length === 1 && data[0] >= 0x20 && data[0] < 0x7f) {
|
|
106
|
+
return String.fromCharCode(data[0]);
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
// =============================================================================
|
|
111
|
+
// Add Anchor Screen
|
|
112
|
+
// =============================================================================
|
|
113
|
+
class AddAnchorScreen extends BaseScreen {
|
|
114
|
+
editState;
|
|
115
|
+
styles;
|
|
116
|
+
hasProject;
|
|
117
|
+
activeProjectId;
|
|
118
|
+
onComplete;
|
|
119
|
+
constructor(editState, styles, hasProject, activeProjectId, onComplete) {
|
|
120
|
+
super();
|
|
121
|
+
this.editState = editState;
|
|
122
|
+
this.styles = styles;
|
|
123
|
+
this.hasProject = hasProject;
|
|
124
|
+
this.activeProjectId = activeProjectId;
|
|
125
|
+
this.onComplete = onComplete;
|
|
126
|
+
}
|
|
127
|
+
render() {
|
|
128
|
+
const s = this.styles;
|
|
129
|
+
const cols = terminal.getTerminalWidth();
|
|
130
|
+
const border = renderBorder(cols, s);
|
|
131
|
+
const lines = [];
|
|
132
|
+
lines.push(border);
|
|
133
|
+
lines.push(` ${s.primaryBold('ADD NEW ANCHOR')}`);
|
|
134
|
+
lines.push('');
|
|
135
|
+
// Content field
|
|
136
|
+
const contentLabel = this.editState.addField === 'content' ? s.primary('Content:') : s.muted('Content:');
|
|
137
|
+
lines.push(` ${contentLabel}`);
|
|
138
|
+
const maxDisplayLen = cols - 6;
|
|
139
|
+
let displayContent = this.editState.addContent;
|
|
140
|
+
if (displayContent.length > maxDisplayLen) {
|
|
141
|
+
displayContent = '…' + displayContent.slice(-(maxDisplayLen - 1));
|
|
142
|
+
}
|
|
143
|
+
lines.push(` ${displayContent}${this.editState.addField === 'content' ? '█' : ''}`);
|
|
144
|
+
lines.push('');
|
|
145
|
+
// Priority field
|
|
146
|
+
const priorityLabel = this.editState.addField === 'priority' ? s.primary('Priority:') : s.muted('Priority:');
|
|
147
|
+
const priorityOptions = PRIORITIES.map(p => p === this.editState.addPriority ? s.inverse(` ${p} `) : s.muted(` ${p} `)).join(' ');
|
|
148
|
+
lines.push(` ${priorityLabel} ${priorityOptions}`);
|
|
149
|
+
// Scope field (only if project is active)
|
|
150
|
+
if (this.hasProject) {
|
|
151
|
+
const scopeLabel = this.editState.addField === 'scope' ? s.primary('Scope:') : s.muted('Scope:');
|
|
152
|
+
const scopeOptions = ['global', 'project'].map(sc => sc === this.editState.addScope ? s.inverse(` ${sc} `) : s.muted(` ${sc} `)).join(' ');
|
|
153
|
+
lines.push(` ${scopeLabel} ${scopeOptions}`);
|
|
154
|
+
}
|
|
155
|
+
lines.push('');
|
|
156
|
+
const remaining = MAX_ANCHOR_LENGTH - this.editState.addContent.length;
|
|
157
|
+
lines.push(s.muted(` ${String(remaining)} chars remaining`));
|
|
158
|
+
// Message
|
|
159
|
+
if (this.editState.message) {
|
|
160
|
+
lines.push('');
|
|
161
|
+
switch (this.editState.messageType) {
|
|
162
|
+
case 'success':
|
|
163
|
+
lines.push(s.success(` ${this.editState.message}`));
|
|
164
|
+
break;
|
|
165
|
+
case 'error':
|
|
166
|
+
lines.push(s.error(` ${this.editState.message}`));
|
|
167
|
+
break;
|
|
168
|
+
default:
|
|
169
|
+
lines.push(s.muted(` ${this.editState.message}`));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
lines.push('');
|
|
173
|
+
lines.push(border);
|
|
174
|
+
lines.push(s.muted(' Tab Switch field · ↑↓/←→ Change value · Enter Save · Esc Cancel'));
|
|
175
|
+
lines.push(border);
|
|
176
|
+
return lines;
|
|
177
|
+
}
|
|
178
|
+
handleKey(data) {
|
|
179
|
+
const char = extractPrintable(data);
|
|
180
|
+
if (isCtrlC(data)) {
|
|
181
|
+
return closeOverlay({ modified: this.editState.modified });
|
|
182
|
+
}
|
|
183
|
+
if (isEscape(data)) {
|
|
184
|
+
this.editState.message = null;
|
|
185
|
+
return popScreen();
|
|
186
|
+
}
|
|
187
|
+
// Tab - switch fields
|
|
188
|
+
if (isTab(data)) {
|
|
189
|
+
if (this.editState.addField === 'content') {
|
|
190
|
+
this.editState.addField = 'priority';
|
|
191
|
+
}
|
|
192
|
+
else if (this.editState.addField === 'priority') {
|
|
193
|
+
this.editState.addField = this.hasProject ? 'scope' : 'content';
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
this.editState.addField = 'content';
|
|
197
|
+
}
|
|
198
|
+
return stay();
|
|
199
|
+
}
|
|
200
|
+
// Shift+Tab - switch fields backward
|
|
201
|
+
if (isShiftTab(data)) {
|
|
202
|
+
if (this.editState.addField === 'content') {
|
|
203
|
+
this.editState.addField = this.hasProject ? 'scope' : 'priority';
|
|
204
|
+
}
|
|
205
|
+
else if (this.editState.addField === 'priority') {
|
|
206
|
+
this.editState.addField = 'content';
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
this.editState.addField = 'priority';
|
|
210
|
+
}
|
|
211
|
+
return stay();
|
|
212
|
+
}
|
|
213
|
+
// Enter - save
|
|
214
|
+
if (isEnter(data)) {
|
|
215
|
+
if (this.editState.addContent.trim()) {
|
|
216
|
+
try {
|
|
217
|
+
const manager = this.editState.addScope === 'global'
|
|
218
|
+
? getGlobalAnchorManager()
|
|
219
|
+
: getAnchorManager(String(this.activeProjectId));
|
|
220
|
+
manager.add({
|
|
221
|
+
content: this.editState.addContent.trim(),
|
|
222
|
+
priority: this.editState.addPriority,
|
|
223
|
+
scope: 'persistent',
|
|
224
|
+
});
|
|
225
|
+
this.editState.modified = true;
|
|
226
|
+
this.onComplete();
|
|
227
|
+
this.editState.message = null;
|
|
228
|
+
return popScreen();
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
this.editState.message = `Error: ${error.message}`;
|
|
232
|
+
this.editState.messageType = 'error';
|
|
233
|
+
return stay();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
this.editState.message = 'Content cannot be empty';
|
|
238
|
+
this.editState.messageType = 'error';
|
|
239
|
+
return stay();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// Field-specific input
|
|
243
|
+
if (this.editState.addField === 'content') {
|
|
244
|
+
if (isBackspace(data)) {
|
|
245
|
+
this.editState.addContent = this.editState.addContent.slice(0, -1);
|
|
246
|
+
return stay();
|
|
247
|
+
}
|
|
248
|
+
if (char && this.editState.addContent.length < MAX_ANCHOR_LENGTH) {
|
|
249
|
+
this.editState.addContent += char;
|
|
250
|
+
return stay();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
else if (this.editState.addField === 'priority') {
|
|
254
|
+
if (isNavigateUp(data) || isLeftArrow(data)) {
|
|
255
|
+
const idx = PRIORITIES.indexOf(this.editState.addPriority);
|
|
256
|
+
this.editState.addPriority = PRIORITIES[(idx - 1 + PRIORITIES.length) % PRIORITIES.length];
|
|
257
|
+
return stay();
|
|
258
|
+
}
|
|
259
|
+
if (isNavigateDown(data) || isRightArrow(data)) {
|
|
260
|
+
const idx = PRIORITIES.indexOf(this.editState.addPriority);
|
|
261
|
+
this.editState.addPriority = PRIORITIES[(idx + 1) % PRIORITIES.length];
|
|
262
|
+
return stay();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
// Scope field
|
|
267
|
+
if (isNavigateUp(data) || isNavigateDown(data) || isLeftArrow(data) || isRightArrow(data)) {
|
|
268
|
+
this.editState.addScope = this.editState.addScope === 'global' ? 'project' : 'global';
|
|
269
|
+
return stay();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return stay(false);
|
|
273
|
+
}
|
|
274
|
+
getMinHeight() {
|
|
275
|
+
return 16;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// =============================================================================
|
|
279
|
+
// Edit Anchor Screen
|
|
280
|
+
// =============================================================================
|
|
281
|
+
class EditAnchorScreen extends BaseScreen {
|
|
282
|
+
editState;
|
|
283
|
+
styles;
|
|
284
|
+
onComplete;
|
|
285
|
+
maxVisibleLines = 6;
|
|
286
|
+
constructor(editState, styles, onComplete) {
|
|
287
|
+
super();
|
|
288
|
+
this.editState = editState;
|
|
289
|
+
this.styles = styles;
|
|
290
|
+
this.onComplete = onComplete;
|
|
291
|
+
}
|
|
292
|
+
wrapText(text, maxWidth) {
|
|
293
|
+
if (text.length <= maxWidth)
|
|
294
|
+
return [text];
|
|
295
|
+
const wrapped = [];
|
|
296
|
+
let remaining = text;
|
|
297
|
+
while (remaining.length > 0) {
|
|
298
|
+
if (remaining.length <= maxWidth) {
|
|
299
|
+
wrapped.push(remaining);
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
let breakPoint = maxWidth;
|
|
303
|
+
const searchStart = Math.max(0, maxWidth - 20);
|
|
304
|
+
for (let i = maxWidth - 1; i >= searchStart; i--) {
|
|
305
|
+
if (remaining[i] === ' ') {
|
|
306
|
+
breakPoint = i + 1;
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
wrapped.push(remaining.slice(0, breakPoint));
|
|
311
|
+
remaining = remaining.slice(breakPoint);
|
|
312
|
+
}
|
|
313
|
+
return wrapped;
|
|
314
|
+
}
|
|
315
|
+
getWrappedCursorPosition(wrappedLines, linearCursor) {
|
|
316
|
+
let pos = 0;
|
|
317
|
+
for (let row = 0; row < wrappedLines.length; row++) {
|
|
318
|
+
const lineLen = wrappedLines[row].length;
|
|
319
|
+
if (pos + lineLen >= linearCursor) {
|
|
320
|
+
return { row, col: linearCursor - pos };
|
|
321
|
+
}
|
|
322
|
+
pos += lineLen;
|
|
323
|
+
}
|
|
324
|
+
const lastRow = wrappedLines.length - 1;
|
|
325
|
+
return { row: lastRow, col: wrappedLines[lastRow]?.length ?? 0 };
|
|
326
|
+
}
|
|
327
|
+
render() {
|
|
328
|
+
const s = this.styles;
|
|
329
|
+
const cols = terminal.getTerminalWidth();
|
|
330
|
+
const border = renderBorder(cols, s);
|
|
331
|
+
const lines = [];
|
|
332
|
+
const item = this.editState.targetItem;
|
|
333
|
+
if (!item) {
|
|
334
|
+
lines.push(border);
|
|
335
|
+
lines.push(s.error(' No anchor selected'));
|
|
336
|
+
lines.push(border);
|
|
337
|
+
return lines;
|
|
338
|
+
}
|
|
339
|
+
lines.push(border);
|
|
340
|
+
lines.push(` ${s.primaryBold('EDIT ANCHOR')}`);
|
|
341
|
+
lines.push('');
|
|
342
|
+
lines.push(s.muted(` ID: ${item.anchor.id}`));
|
|
343
|
+
lines.push(s.muted(` Priority: ${item.anchor.priority}`));
|
|
344
|
+
lines.push(s.muted(` Scope: ${item.scope}`));
|
|
345
|
+
lines.push('');
|
|
346
|
+
lines.push(` ${s.primary('Content:')}`);
|
|
347
|
+
const maxLineWidth = cols - 4;
|
|
348
|
+
const content = this.editState.editContent;
|
|
349
|
+
const cursor = this.editState.editCursor;
|
|
350
|
+
const wrappedLines = this.wrapText(content, maxLineWidth);
|
|
351
|
+
const { row: cursorRow, col: cursorCol } = this.getWrappedCursorPosition(wrappedLines, cursor);
|
|
352
|
+
let startLine = 0;
|
|
353
|
+
if (cursorRow >= this.maxVisibleLines) {
|
|
354
|
+
startLine = cursorRow - this.maxVisibleLines + 1;
|
|
355
|
+
}
|
|
356
|
+
const endLine = Math.min(startLine + this.maxVisibleLines, wrappedLines.length);
|
|
357
|
+
for (let i = startLine; i < endLine; i++) {
|
|
358
|
+
const line = wrappedLines[i];
|
|
359
|
+
const isCursorLine = i === cursorRow;
|
|
360
|
+
if (isCursorLine) {
|
|
361
|
+
if (cursorCol < line.length) {
|
|
362
|
+
const beforeCursor = line.slice(0, cursorCol);
|
|
363
|
+
const cursorChar = line[cursorCol];
|
|
364
|
+
const afterCursor = line.slice(cursorCol + 1);
|
|
365
|
+
lines.push(` ${beforeCursor}${s.inverse(cursorChar)}${afterCursor}`);
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
lines.push(` ${line}${s.inverse(' ')}`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
lines.push(` ${line}`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
for (let i = endLine - startLine; i < this.maxVisibleLines; i++) {
|
|
376
|
+
lines.push('');
|
|
377
|
+
}
|
|
378
|
+
if (wrappedLines.length > this.maxVisibleLines) {
|
|
379
|
+
const scrollInfo = `Lines ${String(startLine + 1)}-${String(endLine)} of ${String(wrappedLines.length)}`;
|
|
380
|
+
lines.push(s.muted(` ${scrollInfo}`));
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
lines.push('');
|
|
384
|
+
}
|
|
385
|
+
const remaining = MAX_ANCHOR_LENGTH - this.editState.editContent.length;
|
|
386
|
+
lines.push(s.muted(` ${String(remaining)} chars remaining`));
|
|
387
|
+
if (this.editState.message) {
|
|
388
|
+
lines.push('');
|
|
389
|
+
switch (this.editState.messageType) {
|
|
390
|
+
case 'success':
|
|
391
|
+
lines.push(s.success(` ${this.editState.message}`));
|
|
392
|
+
break;
|
|
393
|
+
case 'error':
|
|
394
|
+
lines.push(s.error(` ${this.editState.message}`));
|
|
395
|
+
break;
|
|
396
|
+
default:
|
|
397
|
+
lines.push(s.muted(` ${this.editState.message}`));
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
lines.push('');
|
|
401
|
+
lines.push(border);
|
|
402
|
+
lines.push(s.muted(' ←→ Navigate · Enter Save · Esc Cancel'));
|
|
403
|
+
lines.push(border);
|
|
404
|
+
return lines;
|
|
405
|
+
}
|
|
406
|
+
handleKey(data) {
|
|
407
|
+
const char = extractPrintable(data);
|
|
408
|
+
const keyStr = data.toString();
|
|
409
|
+
const item = this.editState.targetItem;
|
|
410
|
+
if (!item) {
|
|
411
|
+
return popScreen();
|
|
412
|
+
}
|
|
413
|
+
if (isCtrlC(data)) {
|
|
414
|
+
return closeOverlay({ modified: this.editState.modified });
|
|
415
|
+
}
|
|
416
|
+
if (isEscape(data)) {
|
|
417
|
+
this.editState.message = null;
|
|
418
|
+
this.editState.targetItem = null;
|
|
419
|
+
return popScreen();
|
|
420
|
+
}
|
|
421
|
+
if (isEnter(data)) {
|
|
422
|
+
return this.saveAndClose(item);
|
|
423
|
+
}
|
|
424
|
+
// Left arrow
|
|
425
|
+
if (isLeftArrow(data)) {
|
|
426
|
+
if (this.editState.editCursor > 0) {
|
|
427
|
+
this.editState.editCursor--;
|
|
428
|
+
return stay();
|
|
429
|
+
}
|
|
430
|
+
return stay(false);
|
|
431
|
+
}
|
|
432
|
+
// Right arrow
|
|
433
|
+
if (isRightArrow(data)) {
|
|
434
|
+
if (this.editState.editCursor < this.editState.editContent.length) {
|
|
435
|
+
this.editState.editCursor++;
|
|
436
|
+
return stay();
|
|
437
|
+
}
|
|
438
|
+
return stay(false);
|
|
439
|
+
}
|
|
440
|
+
// Home/Ctrl+A
|
|
441
|
+
if (keyStr === '\x1b[H' || keyStr === '\x01') {
|
|
442
|
+
this.editState.editCursor = 0;
|
|
443
|
+
return stay();
|
|
444
|
+
}
|
|
445
|
+
// End/Ctrl+E
|
|
446
|
+
if (keyStr === '\x1b[F' || keyStr === '\x05') {
|
|
447
|
+
this.editState.editCursor = this.editState.editContent.length;
|
|
448
|
+
return stay();
|
|
449
|
+
}
|
|
450
|
+
// Backspace
|
|
451
|
+
if (isBackspace(data) && this.editState.editCursor > 0) {
|
|
452
|
+
this.editState.editContent =
|
|
453
|
+
this.editState.editContent.slice(0, this.editState.editCursor - 1) +
|
|
454
|
+
this.editState.editContent.slice(this.editState.editCursor);
|
|
455
|
+
this.editState.editCursor--;
|
|
456
|
+
return stay();
|
|
457
|
+
}
|
|
458
|
+
// Delete key
|
|
459
|
+
if (keyStr === '\x1b[3~' && this.editState.editCursor < this.editState.editContent.length) {
|
|
460
|
+
this.editState.editContent =
|
|
461
|
+
this.editState.editContent.slice(0, this.editState.editCursor) +
|
|
462
|
+
this.editState.editContent.slice(this.editState.editCursor + 1);
|
|
463
|
+
return stay();
|
|
464
|
+
}
|
|
465
|
+
// Printable character
|
|
466
|
+
if (char && this.editState.editContent.length < MAX_ANCHOR_LENGTH) {
|
|
467
|
+
this.editState.editContent =
|
|
468
|
+
this.editState.editContent.slice(0, this.editState.editCursor) +
|
|
469
|
+
char +
|
|
470
|
+
this.editState.editContent.slice(this.editState.editCursor);
|
|
471
|
+
this.editState.editCursor++;
|
|
472
|
+
return stay();
|
|
473
|
+
}
|
|
474
|
+
return stay(false);
|
|
475
|
+
}
|
|
476
|
+
saveAndClose(item) {
|
|
477
|
+
if (this.editState.editContent.trim()) {
|
|
478
|
+
try {
|
|
479
|
+
const manager = item.scope === 'global'
|
|
480
|
+
? getGlobalAnchorManager()
|
|
481
|
+
: getAnchorManager(item.projectId ?? '');
|
|
482
|
+
const oldAnchor = item.anchor;
|
|
483
|
+
manager.remove(oldAnchor.id);
|
|
484
|
+
manager.add({
|
|
485
|
+
id: oldAnchor.id,
|
|
486
|
+
content: this.editState.editContent.trim(),
|
|
487
|
+
priority: oldAnchor.priority,
|
|
488
|
+
scope: oldAnchor.scope,
|
|
489
|
+
tags: oldAnchor.tags,
|
|
490
|
+
metadata: oldAnchor.metadata,
|
|
491
|
+
projectId: oldAnchor.projectId,
|
|
492
|
+
});
|
|
493
|
+
this.editState.modified = true;
|
|
494
|
+
this.onComplete();
|
|
495
|
+
this.editState.message = null;
|
|
496
|
+
this.editState.targetItem = null;
|
|
497
|
+
return popScreen();
|
|
498
|
+
}
|
|
499
|
+
catch (error) {
|
|
500
|
+
this.editState.message = `Error: ${error.message}`;
|
|
501
|
+
this.editState.messageType = 'error';
|
|
502
|
+
return stay();
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
this.editState.message = 'Content cannot be empty';
|
|
507
|
+
this.editState.messageType = 'error';
|
|
508
|
+
return stay();
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
getMinHeight() {
|
|
512
|
+
return 20;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
// =============================================================================
|
|
516
|
+
// Delete Confirm Screen
|
|
517
|
+
// =============================================================================
|
|
518
|
+
class DeleteConfirmScreen extends BaseScreen {
|
|
519
|
+
editState;
|
|
520
|
+
styles;
|
|
521
|
+
onComplete;
|
|
522
|
+
constructor(editState, styles, onComplete) {
|
|
523
|
+
super();
|
|
524
|
+
this.editState = editState;
|
|
525
|
+
this.styles = styles;
|
|
526
|
+
this.onComplete = onComplete;
|
|
527
|
+
}
|
|
528
|
+
render() {
|
|
529
|
+
const s = this.styles;
|
|
530
|
+
const cols = terminal.getTerminalWidth();
|
|
531
|
+
const border = renderBorder(cols, s);
|
|
532
|
+
const lines = [];
|
|
533
|
+
const item = this.editState.targetItem;
|
|
534
|
+
if (!item) {
|
|
535
|
+
lines.push(border);
|
|
536
|
+
lines.push(s.error(' No anchor selected'));
|
|
537
|
+
lines.push(border);
|
|
538
|
+
return lines;
|
|
539
|
+
}
|
|
540
|
+
lines.push(border);
|
|
541
|
+
lines.push(` ${s.warning('DELETE ANCHOR')}`);
|
|
542
|
+
lines.push('');
|
|
543
|
+
lines.push(s.muted(` ID: ${item.anchor.id}`));
|
|
544
|
+
lines.push(s.muted(` Priority: ${item.anchor.priority}`));
|
|
545
|
+
lines.push(s.muted(` Scope: ${item.scope}`));
|
|
546
|
+
lines.push('');
|
|
547
|
+
const maxContentLen = cols - 14;
|
|
548
|
+
let content = item.anchor.content;
|
|
549
|
+
if (content.length > maxContentLen) {
|
|
550
|
+
content = content.slice(0, maxContentLen - 3) + '...';
|
|
551
|
+
}
|
|
552
|
+
lines.push(` Content: ${content}`);
|
|
553
|
+
lines.push('');
|
|
554
|
+
lines.push(s.warning(' Are you sure you want to delete this anchor?'));
|
|
555
|
+
lines.push('');
|
|
556
|
+
lines.push(border);
|
|
557
|
+
lines.push(s.muted(' y Yes · n/Esc No'));
|
|
558
|
+
lines.push(border);
|
|
559
|
+
return lines;
|
|
560
|
+
}
|
|
561
|
+
handleKey(data) {
|
|
562
|
+
const char = extractPrintable(data);
|
|
563
|
+
const item = this.editState.targetItem;
|
|
564
|
+
if (!item) {
|
|
565
|
+
return popScreen();
|
|
566
|
+
}
|
|
567
|
+
if (isCtrlC(data)) {
|
|
568
|
+
return closeOverlay({ modified: this.editState.modified });
|
|
569
|
+
}
|
|
570
|
+
if (char === 'y' || char === 'Y') {
|
|
571
|
+
try {
|
|
572
|
+
const manager = item.scope === 'global'
|
|
573
|
+
? getGlobalAnchorManager()
|
|
574
|
+
: getAnchorManager(item.projectId ?? '');
|
|
575
|
+
manager.remove(item.anchor.id);
|
|
576
|
+
this.editState.modified = true;
|
|
577
|
+
this.onComplete();
|
|
578
|
+
this.editState.targetItem = null;
|
|
579
|
+
return popScreen();
|
|
580
|
+
}
|
|
581
|
+
catch (error) {
|
|
582
|
+
this.editState.message = `Error: ${error.message}`;
|
|
583
|
+
this.editState.messageType = 'error';
|
|
584
|
+
return popScreen();
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
if (char === 'n' || char === 'N' || isEscape(data)) {
|
|
588
|
+
this.editState.targetItem = null;
|
|
589
|
+
return popScreen();
|
|
590
|
+
}
|
|
591
|
+
return stay(false);
|
|
592
|
+
}
|
|
593
|
+
getMinHeight() {
|
|
594
|
+
return 14;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
// =============================================================================
|
|
598
|
+
// Anchors Overlay V2
|
|
599
|
+
// =============================================================================
|
|
600
|
+
export class AnchorsOverlayV2 extends TabbedListOverlayV2 {
|
|
601
|
+
type = 'inline';
|
|
602
|
+
id = 'anchors-overlay-v2';
|
|
603
|
+
editState;
|
|
604
|
+
activeProject;
|
|
605
|
+
/**
|
|
606
|
+
* @param projectId - Optional project ID to view anchors for.
|
|
607
|
+
* If not provided, uses the active project.
|
|
608
|
+
* Pass this when opening from workflow overlay for a specific project.
|
|
609
|
+
*/
|
|
610
|
+
constructor(projectId) {
|
|
611
|
+
// Use provided projectId, or fall back to active project
|
|
612
|
+
const activeProject = getActiveProject();
|
|
613
|
+
const resolvedProjectId = projectId ?? activeProject?.id ?? null;
|
|
614
|
+
const anchors = loadAnchors(resolvedProjectId);
|
|
615
|
+
super({
|
|
616
|
+
title: 'Anchors',
|
|
617
|
+
tabs: TABS,
|
|
618
|
+
items: anchors,
|
|
619
|
+
pageSize: PAGE_SIZE,
|
|
620
|
+
filterByTab: (item, tabId) => {
|
|
621
|
+
if (tabId === 'all')
|
|
622
|
+
return true;
|
|
623
|
+
if (tabId === 'critical' || tabId === 'safety' || tabId === 'info') {
|
|
624
|
+
return item.anchor.priority === tabId;
|
|
625
|
+
}
|
|
626
|
+
if (tabId === 'global')
|
|
627
|
+
return item.scope === 'global';
|
|
628
|
+
if (tabId === 'project')
|
|
629
|
+
return item.scope === 'project';
|
|
630
|
+
return true;
|
|
631
|
+
},
|
|
632
|
+
getSearchText: (item) => `${item.anchor.content} ${item.anchor.priority} ${item.scope}`,
|
|
633
|
+
renderItem: (item, isSelected, s) => {
|
|
634
|
+
const cols = terminal.getTerminalWidth();
|
|
635
|
+
const priorityBadge = getPriorityBadge(item.anchor.priority, s);
|
|
636
|
+
const scopeBadge = item.scope === 'global' ? s.muted('[G]') : s.info('[P]');
|
|
637
|
+
const prefixLen = 15;
|
|
638
|
+
const maxContentLen = Math.max(20, cols - prefixLen - 8);
|
|
639
|
+
let content = item.anchor.content;
|
|
640
|
+
if (content.length > maxContentLen) {
|
|
641
|
+
content = content.slice(0, maxContentLen - 3) + '...';
|
|
642
|
+
}
|
|
643
|
+
const cursor = isSelected ? s.primary('❯ ') : ' ';
|
|
644
|
+
const line = `${priorityBadge} ${scopeBadge} ${content}`;
|
|
645
|
+
if (isSelected) {
|
|
646
|
+
return `${cursor}${s.primary(line)}`;
|
|
647
|
+
}
|
|
648
|
+
return `${cursor}${line}`;
|
|
649
|
+
},
|
|
650
|
+
showCount: true,
|
|
651
|
+
emptyMessage: 'No anchors defined. Press [a] to add one.',
|
|
652
|
+
noResultsMessage: 'No anchors match the filter.',
|
|
653
|
+
footerHints: (searchMode) => {
|
|
654
|
+
if (searchMode) {
|
|
655
|
+
return 'Type to filter · ↑↓/jk Navigate · Enter Edit · Esc Exit search';
|
|
656
|
+
}
|
|
657
|
+
return '↑↓/jk Navigate · ←→/hl Tabs · / Search · a Add · e Edit · d Delete · q/Esc Close';
|
|
658
|
+
},
|
|
659
|
+
});
|
|
660
|
+
// Store resolved project info - uses provided projectId or falls back to active project
|
|
661
|
+
this.activeProject = resolvedProjectId !== null
|
|
662
|
+
? { id: resolvedProjectId, displayName: activeProject?.displayName ?? 'Project' }
|
|
663
|
+
: null;
|
|
664
|
+
this.editState = {
|
|
665
|
+
addContent: '',
|
|
666
|
+
addPriority: 'info',
|
|
667
|
+
addScope: resolvedProjectId !== null ? 'project' : 'global',
|
|
668
|
+
addField: 'content',
|
|
669
|
+
editContent: '',
|
|
670
|
+
editCursor: 0,
|
|
671
|
+
targetItem: null,
|
|
672
|
+
message: null,
|
|
673
|
+
messageType: 'info',
|
|
674
|
+
modified: false,
|
|
675
|
+
};
|
|
676
|
+
this.minHeight = 20;
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Refresh anchors from storage
|
|
680
|
+
*/
|
|
681
|
+
refreshAnchors() {
|
|
682
|
+
const anchors = loadAnchors(this.activeProject?.id ?? null);
|
|
683
|
+
this.state.items = anchors;
|
|
684
|
+
// Re-apply tab filter
|
|
685
|
+
const tabId = TABS[this.state.currentTab]?.id ?? 'all';
|
|
686
|
+
let filtered = anchors.filter((item) => {
|
|
687
|
+
if (tabId === 'all')
|
|
688
|
+
return true;
|
|
689
|
+
if (tabId === 'critical' || tabId === 'safety' || tabId === 'info') {
|
|
690
|
+
return item.anchor.priority === tabId;
|
|
691
|
+
}
|
|
692
|
+
if (tabId === 'global')
|
|
693
|
+
return item.scope === 'global';
|
|
694
|
+
if (tabId === 'project')
|
|
695
|
+
return item.scope === 'project';
|
|
696
|
+
return true;
|
|
697
|
+
});
|
|
698
|
+
// Re-apply search filter
|
|
699
|
+
if (this.state.searchQuery) {
|
|
700
|
+
const query = this.state.searchQuery.toLowerCase();
|
|
701
|
+
filtered = filtered.filter((item) => item.anchor.content.toLowerCase().includes(query) ||
|
|
702
|
+
item.anchor.priority.toLowerCase().includes(query) ||
|
|
703
|
+
item.scope.toLowerCase().includes(query));
|
|
704
|
+
}
|
|
705
|
+
this.state.filteredItems = filtered;
|
|
706
|
+
this.state.selectedIndex = Math.min(this.state.selectedIndex, Math.max(0, filtered.length - 1));
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Override handleKey to add custom behavior (a add, e edit, d delete)
|
|
710
|
+
*/
|
|
711
|
+
handleKey(key) {
|
|
712
|
+
// Ctrl+C always closes
|
|
713
|
+
if (this.isInterruptKey(key)) {
|
|
714
|
+
return this.close({ modified: this.editState.modified });
|
|
715
|
+
}
|
|
716
|
+
// q or Esc closes (only if not in search mode AND on main list)
|
|
717
|
+
// If on a detail screen (screenStack.size() > 1), let super.handleKey() pop the screen
|
|
718
|
+
if (this.isCloseKey(key) && !this.state.searchMode && this.screenStack.size() === 1) {
|
|
719
|
+
return this.close({ modified: this.editState.modified });
|
|
720
|
+
}
|
|
721
|
+
// Only handle custom keys when on main list (screen stack size = 1)
|
|
722
|
+
if (this.screenStack.size() === 1 && !this.state.searchMode) {
|
|
723
|
+
// Enter - edit selected anchor (handle here instead of onItemSelected for proper rerender)
|
|
724
|
+
if (this.isEnterKey(key) && this.state.filteredItems.length > 0) {
|
|
725
|
+
const item = this.state.filteredItems[this.state.selectedIndex];
|
|
726
|
+
this.editState.targetItem = item;
|
|
727
|
+
this.editState.editContent = item.anchor.content;
|
|
728
|
+
this.editState.editCursor = item.anchor.content.length;
|
|
729
|
+
this.editState.message = null;
|
|
730
|
+
this.screenStack.push(new EditAnchorScreen(this.editState, this.getStyles(), () => { this.refreshAnchors(); }));
|
|
731
|
+
return this.rerender();
|
|
732
|
+
}
|
|
733
|
+
// 'a' - add mode
|
|
734
|
+
if (key.char === 'a') {
|
|
735
|
+
this.editState.addContent = '';
|
|
736
|
+
this.editState.addPriority = 'info';
|
|
737
|
+
this.editState.addScope = this.activeProject ? 'project' : 'global';
|
|
738
|
+
this.editState.addField = 'content';
|
|
739
|
+
this.editState.message = null;
|
|
740
|
+
this.screenStack.push(new AddAnchorScreen(this.editState, this.getStyles(), this.activeProject !== null, this.activeProject?.id ?? null, () => { this.refreshAnchors(); }));
|
|
741
|
+
return this.rerender();
|
|
742
|
+
}
|
|
743
|
+
// 'e' - edit mode (when items exist)
|
|
744
|
+
if (key.char === 'e' && this.state.filteredItems.length > 0) {
|
|
745
|
+
const item = this.state.filteredItems[this.state.selectedIndex];
|
|
746
|
+
this.editState.targetItem = item;
|
|
747
|
+
this.editState.editContent = item.anchor.content;
|
|
748
|
+
this.editState.editCursor = item.anchor.content.length;
|
|
749
|
+
this.editState.message = null;
|
|
750
|
+
this.screenStack.push(new EditAnchorScreen(this.editState, this.getStyles(), () => { this.refreshAnchors(); }));
|
|
751
|
+
return this.rerender();
|
|
752
|
+
}
|
|
753
|
+
// 'd' - delete confirm (when items exist)
|
|
754
|
+
if (key.char === 'd' && this.state.filteredItems.length > 0) {
|
|
755
|
+
const item = this.state.filteredItems[this.state.selectedIndex];
|
|
756
|
+
this.editState.targetItem = item;
|
|
757
|
+
this.editState.message = null;
|
|
758
|
+
this.screenStack.push(new DeleteConfirmScreen(this.editState, this.getStyles(), () => { this.refreshAnchors(); }));
|
|
759
|
+
return this.rerender();
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
// Let parent handle everything else
|
|
763
|
+
const result = super.handleKey(key);
|
|
764
|
+
// Convert close result to our result type with modified flag
|
|
765
|
+
if (result.type === 'close') {
|
|
766
|
+
return this.close({ modified: this.editState.modified });
|
|
767
|
+
}
|
|
768
|
+
// Safe to cast other actions (render, none, etc.)
|
|
769
|
+
return result;
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Override to open edit screen on Enter
|
|
773
|
+
*/
|
|
774
|
+
onItemSelected(item) {
|
|
775
|
+
this.editState.targetItem = item;
|
|
776
|
+
this.editState.editContent = item.anchor.content;
|
|
777
|
+
this.editState.editCursor = item.anchor.content.length;
|
|
778
|
+
this.editState.message = null;
|
|
779
|
+
this.screenStack.push(new EditAnchorScreen(this.editState, this.getStyles(), () => { this.refreshAnchors(); }));
|
|
780
|
+
// Return undefined to not close (we pushed a screen instead)
|
|
781
|
+
return undefined;
|
|
782
|
+
}
|
|
783
|
+
}
|