tlc-claude-code 1.2.26 → 1.2.28
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/dashboard/dist/components/ActivityFeed.d.ts +17 -0
- package/dashboard/dist/components/ActivityFeed.js +42 -0
- package/dashboard/dist/components/ActivityFeed.test.d.ts +1 -0
- package/dashboard/dist/components/ActivityFeed.test.js +162 -0
- package/dashboard/dist/components/BranchSelector.d.ts +16 -0
- package/dashboard/dist/components/BranchSelector.js +49 -0
- package/dashboard/dist/components/BranchSelector.test.d.ts +1 -0
- package/dashboard/dist/components/BranchSelector.test.js +166 -0
- package/dashboard/dist/components/CommandPalette.d.ts +17 -0
- package/dashboard/dist/components/CommandPalette.js +118 -0
- package/dashboard/dist/components/CommandPalette.test.d.ts +1 -0
- package/dashboard/dist/components/CommandPalette.test.js +181 -0
- package/dashboard/dist/components/ConnectionStatus.d.ts +16 -0
- package/dashboard/dist/components/ConnectionStatus.js +27 -0
- package/dashboard/dist/components/ConnectionStatus.test.d.ts +1 -0
- package/dashboard/dist/components/ConnectionStatus.test.js +121 -0
- package/dashboard/dist/components/DeviceFrame.d.ts +19 -0
- package/dashboard/dist/components/DeviceFrame.js +52 -0
- package/dashboard/dist/components/DeviceFrame.test.d.ts +1 -0
- package/dashboard/dist/components/DeviceFrame.test.js +118 -0
- package/dashboard/dist/components/EnvironmentBadge.d.ts +11 -0
- package/dashboard/dist/components/EnvironmentBadge.js +16 -0
- package/dashboard/dist/components/EnvironmentBadge.test.d.ts +1 -0
- package/dashboard/dist/components/EnvironmentBadge.test.js +102 -0
- package/dashboard/dist/components/FocusIndicator.d.ts +19 -0
- package/dashboard/dist/components/FocusIndicator.js +47 -0
- package/dashboard/dist/components/FocusIndicator.test.d.ts +1 -0
- package/dashboard/dist/components/FocusIndicator.test.js +117 -0
- package/dashboard/dist/components/KeyboardHelp.d.ts +15 -0
- package/dashboard/dist/components/KeyboardHelp.js +61 -0
- package/dashboard/dist/components/KeyboardHelp.test.d.ts +1 -0
- package/dashboard/dist/components/KeyboardHelp.test.js +131 -0
- package/dashboard/dist/components/LogSearch.d.ts +13 -0
- package/dashboard/dist/components/LogSearch.js +43 -0
- package/dashboard/dist/components/LogSearch.test.d.ts +1 -0
- package/dashboard/dist/components/LogSearch.test.js +100 -0
- package/dashboard/dist/components/LogStream.d.ts +21 -0
- package/dashboard/dist/components/LogStream.js +123 -0
- package/dashboard/dist/components/LogStream.test.d.ts +1 -0
- package/dashboard/dist/components/LogStream.test.js +159 -0
- package/dashboard/dist/components/PlanView.d.ts +7 -0
- package/dashboard/dist/components/PlanView.js +74 -2
- package/dashboard/dist/components/PlanView.test.js +70 -1
- package/dashboard/dist/components/PreviewPanel.d.ts +18 -0
- package/dashboard/dist/components/PreviewPanel.js +73 -0
- package/dashboard/dist/components/PreviewPanel.test.d.ts +1 -0
- package/dashboard/dist/components/PreviewPanel.test.js +124 -0
- package/dashboard/dist/components/ProjectCard.d.ts +18 -0
- package/dashboard/dist/components/ProjectCard.js +19 -0
- package/dashboard/dist/components/ProjectCard.test.d.ts +1 -0
- package/dashboard/dist/components/ProjectCard.test.js +53 -0
- package/dashboard/dist/components/ProjectDetail.d.ts +44 -0
- package/dashboard/dist/components/ProjectDetail.js +65 -0
- package/dashboard/dist/components/ProjectDetail.test.d.ts +1 -0
- package/dashboard/dist/components/ProjectDetail.test.js +196 -0
- package/dashboard/dist/components/ProjectList.d.ts +11 -0
- package/dashboard/dist/components/ProjectList.js +62 -0
- package/dashboard/dist/components/ProjectList.test.d.ts +1 -0
- package/dashboard/dist/components/ProjectList.test.js +93 -0
- package/dashboard/dist/components/SettingsPanel.d.ts +32 -0
- package/dashboard/dist/components/SettingsPanel.js +154 -0
- package/dashboard/dist/components/SettingsPanel.test.d.ts +1 -0
- package/dashboard/dist/components/SettingsPanel.test.js +196 -0
- package/dashboard/dist/components/StatusBar.d.ts +16 -0
- package/dashboard/dist/components/StatusBar.js +47 -0
- package/dashboard/dist/components/StatusBar.test.d.ts +1 -0
- package/dashboard/dist/components/StatusBar.test.js +123 -0
- package/dashboard/dist/components/TaskBoard.d.ts +22 -0
- package/dashboard/dist/components/TaskBoard.js +102 -0
- package/dashboard/dist/components/TaskBoard.test.d.ts +1 -0
- package/dashboard/dist/components/TaskBoard.test.js +113 -0
- package/dashboard/dist/components/TaskCard.d.ts +17 -0
- package/dashboard/dist/components/TaskCard.js +29 -0
- package/dashboard/dist/components/TaskCard.test.d.ts +1 -0
- package/dashboard/dist/components/TaskCard.test.js +109 -0
- package/dashboard/dist/components/TaskDetail.d.ts +36 -0
- package/dashboard/dist/components/TaskDetail.js +41 -0
- package/dashboard/dist/components/TaskDetail.test.d.ts +1 -0
- package/dashboard/dist/components/TaskDetail.test.js +164 -0
- package/dashboard/dist/components/TaskFilter.d.ts +12 -0
- package/dashboard/dist/components/TaskFilter.js +138 -0
- package/dashboard/dist/components/TaskFilter.test.d.ts +1 -0
- package/dashboard/dist/components/TaskFilter.test.js +109 -0
- package/dashboard/dist/components/TeamPanel.d.ts +15 -0
- package/dashboard/dist/components/TeamPanel.js +24 -0
- package/dashboard/dist/components/TeamPanel.test.d.ts +1 -0
- package/dashboard/dist/components/TeamPanel.test.js +109 -0
- package/dashboard/dist/components/TeamPresence.d.ts +14 -0
- package/dashboard/dist/components/TeamPresence.js +31 -0
- package/dashboard/dist/components/TeamPresence.test.d.ts +1 -0
- package/dashboard/dist/components/TeamPresence.test.js +144 -0
- package/dashboard/dist/components/layout/Header.d.ts +9 -0
- package/dashboard/dist/components/layout/Header.js +11 -0
- package/dashboard/dist/components/layout/Header.test.d.ts +1 -0
- package/dashboard/dist/components/layout/Header.test.js +35 -0
- package/dashboard/dist/components/layout/Shell.d.ts +10 -0
- package/dashboard/dist/components/layout/Shell.js +5 -0
- package/dashboard/dist/components/layout/Shell.test.d.ts +1 -0
- package/dashboard/dist/components/layout/Shell.test.js +34 -0
- package/dashboard/dist/components/layout/Sidebar.d.ts +14 -0
- package/dashboard/dist/components/layout/Sidebar.js +8 -0
- package/dashboard/dist/components/layout/Sidebar.test.d.ts +1 -0
- package/dashboard/dist/components/layout/Sidebar.test.js +40 -0
- package/dashboard/dist/components/ui/Badge.d.ts +9 -0
- package/dashboard/dist/components/ui/Badge.js +13 -0
- package/dashboard/dist/components/ui/Badge.test.d.ts +1 -0
- package/dashboard/dist/components/ui/Badge.test.js +69 -0
- package/dashboard/dist/components/ui/Button.d.ts +12 -0
- package/dashboard/dist/components/ui/Button.js +14 -0
- package/dashboard/dist/components/ui/Button.test.d.ts +1 -0
- package/dashboard/dist/components/ui/Button.test.js +81 -0
- package/dashboard/dist/components/ui/Card.d.ts +21 -0
- package/dashboard/dist/components/ui/Card.js +20 -0
- package/dashboard/dist/components/ui/Card.test.d.ts +1 -0
- package/dashboard/dist/components/ui/Card.test.js +82 -0
- package/dashboard/dist/components/ui/Input.d.ts +13 -0
- package/dashboard/dist/components/ui/Input.js +8 -0
- package/dashboard/dist/components/ui/Input.test.d.ts +1 -0
- package/dashboard/dist/components/ui/Input.test.js +68 -0
- package/dashboard/dist/styles/tokens.d.ts +150 -0
- package/dashboard/dist/styles/tokens.js +184 -0
- package/dashboard/dist/styles/tokens.test.d.ts +1 -0
- package/dashboard/dist/styles/tokens.test.js +95 -0
- package/dashboard/dist/test/setup.d.ts +1 -0
- package/dashboard/dist/test/setup.js +1 -0
- package/dashboard/package.json +3 -0
- package/package.json +1 -1
- package/server/dashboard/index.html +157 -2
- package/server/index.js +38 -21
- package/server/lib/adapters/base-adapter.js +114 -0
- package/server/lib/adapters/base-adapter.test.js +90 -0
- package/server/lib/adapters/claude-adapter.js +141 -0
- package/server/lib/adapters/claude-adapter.test.js +180 -0
- package/server/lib/adapters/deepseek-adapter.js +153 -0
- package/server/lib/adapters/deepseek-adapter.test.js +193 -0
- package/server/lib/adapters/openai-adapter.js +190 -0
- package/server/lib/adapters/openai-adapter.test.js +231 -0
- package/server/lib/budget-tracker.js +169 -0
- package/server/lib/budget-tracker.test.js +165 -0
- package/server/lib/claude-injector.js +85 -0
- package/server/lib/claude-injector.test.js +161 -0
- package/server/lib/consensus-engine.js +135 -0
- package/server/lib/consensus-engine.test.js +152 -0
- package/server/lib/context-builder.js +112 -0
- package/server/lib/context-builder.test.js +120 -0
- package/server/lib/file-collector.js +322 -0
- package/server/lib/file-collector.test.js +307 -0
- package/server/lib/memory-classifier.js +175 -0
- package/server/lib/memory-classifier.test.js +169 -0
- package/server/lib/memory-committer.js +138 -0
- package/server/lib/memory-committer.test.js +136 -0
- package/server/lib/memory-hooks.js +127 -0
- package/server/lib/memory-hooks.test.js +136 -0
- package/server/lib/memory-init.js +104 -0
- package/server/lib/memory-init.test.js +119 -0
- package/server/lib/memory-observer.js +149 -0
- package/server/lib/memory-observer.test.js +158 -0
- package/server/lib/memory-reader.js +243 -0
- package/server/lib/memory-reader.test.js +216 -0
- package/server/lib/memory-storage.js +120 -0
- package/server/lib/memory-storage.test.js +136 -0
- package/server/lib/memory-writer.js +176 -0
- package/server/lib/memory-writer.test.js +231 -0
- package/server/lib/overdrive-command.js +30 -6
- package/server/lib/overdrive-command.test.js +8 -1
- package/server/lib/pattern-detector.js +216 -0
- package/server/lib/pattern-detector.test.js +241 -0
- package/server/lib/relevance-scorer.js +175 -0
- package/server/lib/relevance-scorer.test.js +107 -0
- package/server/lib/review-command.js +238 -0
- package/server/lib/review-command.test.js +245 -0
- package/server/lib/review-orchestrator.js +273 -0
- package/server/lib/review-orchestrator.test.js +300 -0
- package/server/lib/review-reporter.js +288 -0
- package/server/lib/review-reporter.test.js +240 -0
- package/server/lib/session-summary.js +90 -0
- package/server/lib/session-summary.test.js +156 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type ActivityType = 'commit' | 'claim' | 'complete' | 'review' | 'comment' | 'release';
|
|
2
|
+
export interface Activity {
|
|
3
|
+
id: string;
|
|
4
|
+
type: ActivityType;
|
|
5
|
+
user: string;
|
|
6
|
+
message: string;
|
|
7
|
+
timestamp: string;
|
|
8
|
+
ref?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ActivityFeedProps {
|
|
11
|
+
activities: Activity[];
|
|
12
|
+
filterUser?: string;
|
|
13
|
+
filterType?: ActivityType;
|
|
14
|
+
limit?: number;
|
|
15
|
+
compact?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare function ActivityFeed({ activities, filterUser, filterType, limit, compact, }: ActivityFeedProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
const typeIcons = {
|
|
5
|
+
commit: { icon: '⊕', color: 'green' },
|
|
6
|
+
claim: { icon: '◉', color: 'cyan' },
|
|
7
|
+
complete: { icon: '✓', color: 'green' },
|
|
8
|
+
review: { icon: '⬡', color: 'magenta' },
|
|
9
|
+
comment: { icon: '◇', color: 'yellow' },
|
|
10
|
+
release: { icon: '○', color: 'gray' },
|
|
11
|
+
};
|
|
12
|
+
export function ActivityFeed({ activities, filterUser, filterType, limit, compact = false, }) {
|
|
13
|
+
// Filter activities
|
|
14
|
+
const filteredActivities = useMemo(() => {
|
|
15
|
+
let result = activities;
|
|
16
|
+
if (filterUser) {
|
|
17
|
+
result = result.filter((a) => a.user === filterUser);
|
|
18
|
+
}
|
|
19
|
+
if (filterType) {
|
|
20
|
+
result = result.filter((a) => a.type === filterType);
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}, [activities, filterUser, filterType]);
|
|
24
|
+
// Apply limit
|
|
25
|
+
const displayActivities = limit
|
|
26
|
+
? filteredActivities.slice(0, limit)
|
|
27
|
+
: filteredActivities;
|
|
28
|
+
const hasMore = limit && filteredActivities.length > limit;
|
|
29
|
+
const remainingCount = filteredActivities.length - (limit || 0);
|
|
30
|
+
// Empty state
|
|
31
|
+
if (activities.length === 0) {
|
|
32
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, children: "Activity" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "No activity yet - it's quiet here" }) })] }));
|
|
33
|
+
}
|
|
34
|
+
// No matches
|
|
35
|
+
if (filteredActivities.length === 0) {
|
|
36
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, children: "Activity" }), filterUser && _jsxs(Text, { dimColor: true, children: [" - @", filterUser] }), filterType && _jsxs(Text, { dimColor: true, children: [" - ", filterType] })] }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: "No matching activity" }) })] }));
|
|
37
|
+
}
|
|
38
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, children: "Activity " }), _jsxs(Text, { dimColor: true, children: ["(", filteredActivities.length, ")"] }), filterUser && _jsxs(Text, { color: "cyan", children: [" @", filterUser] }), filterType && _jsxs(Text, { color: "magenta", children: [" [", filterType, "]"] })] }), displayActivities.map((activity) => {
|
|
39
|
+
const { icon, color } = typeIcons[activity.type];
|
|
40
|
+
return (_jsxs(Box, { marginBottom: compact ? 0 : 1, flexDirection: compact ? 'row' : 'column', children: [_jsxs(Box, { children: [_jsxs(Text, { color: color, children: [icon, " "] }), _jsxs(Text, { color: "cyan", children: ["@", activity.user] }), _jsxs(Text, { children: [' ', compact ? activity.message.slice(0, 40) : activity.message, compact && activity.message.length > 40 && '...'] })] }), !compact && (_jsxs(Box, { marginLeft: 2, children: [_jsx(Text, { dimColor: true, children: activity.timestamp }), activity.ref && (_jsxs(Text, { dimColor: true, children: [" \u2022 ", activity.ref] }))] })), compact && (_jsxs(Text, { dimColor: true, children: [" (", activity.timestamp, ")"] }))] }, activity.id));
|
|
41
|
+
}), hasMore && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["+", remainingCount, " more activities..."] }) }))] }));
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import { render } from 'ink-testing-library';
|
|
4
|
+
import { ActivityFeed } from './ActivityFeed.js';
|
|
5
|
+
const sampleActivities = [
|
|
6
|
+
{
|
|
7
|
+
id: 'a1',
|
|
8
|
+
type: 'commit',
|
|
9
|
+
user: 'alice',
|
|
10
|
+
message: 'Added user authentication',
|
|
11
|
+
timestamp: '2 min ago',
|
|
12
|
+
ref: 'abc1234',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: 'a2',
|
|
16
|
+
type: 'claim',
|
|
17
|
+
user: 'bob',
|
|
18
|
+
message: 'Claimed Task 5: Add validation',
|
|
19
|
+
timestamp: '5 min ago',
|
|
20
|
+
ref: 'task-5',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'a3',
|
|
24
|
+
type: 'complete',
|
|
25
|
+
user: 'carol',
|
|
26
|
+
message: 'Completed Task 3: Create schema',
|
|
27
|
+
timestamp: '10 min ago',
|
|
28
|
+
ref: 'task-3',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 'a4',
|
|
32
|
+
type: 'review',
|
|
33
|
+
user: 'alice',
|
|
34
|
+
message: 'Requested review on PR #42',
|
|
35
|
+
timestamp: '15 min ago',
|
|
36
|
+
ref: 'pr-42',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'a5',
|
|
40
|
+
type: 'comment',
|
|
41
|
+
user: 'dave',
|
|
42
|
+
message: 'Commented on Task 3',
|
|
43
|
+
timestamp: '20 min ago',
|
|
44
|
+
ref: 'task-3',
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
describe('ActivityFeed', () => {
|
|
48
|
+
describe('Activity Display', () => {
|
|
49
|
+
it('shows recent activities', () => {
|
|
50
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities }));
|
|
51
|
+
expect(lastFrame()).toContain('Added user authentication');
|
|
52
|
+
expect(lastFrame()).toContain('Claimed Task 5');
|
|
53
|
+
});
|
|
54
|
+
it('shows activity user', () => {
|
|
55
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities }));
|
|
56
|
+
expect(lastFrame()).toContain('alice');
|
|
57
|
+
expect(lastFrame()).toContain('bob');
|
|
58
|
+
});
|
|
59
|
+
it('shows activity timestamp', () => {
|
|
60
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities }));
|
|
61
|
+
expect(lastFrame()).toContain('2 min ago');
|
|
62
|
+
expect(lastFrame()).toContain('5 min ago');
|
|
63
|
+
});
|
|
64
|
+
it('shows activity type icon', () => {
|
|
65
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities }));
|
|
66
|
+
// Should have icons for different types
|
|
67
|
+
expect(lastFrame()).toBeDefined();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('Activity Types', () => {
|
|
71
|
+
it('shows commit activities', () => {
|
|
72
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities }));
|
|
73
|
+
expect(lastFrame()).toContain('Added user authentication');
|
|
74
|
+
});
|
|
75
|
+
it('shows claim activities', () => {
|
|
76
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities }));
|
|
77
|
+
expect(lastFrame()).toContain('Claimed Task 5');
|
|
78
|
+
});
|
|
79
|
+
it('shows complete activities', () => {
|
|
80
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities }));
|
|
81
|
+
expect(lastFrame()).toContain('Completed Task 3');
|
|
82
|
+
});
|
|
83
|
+
it('shows review activities', () => {
|
|
84
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities }));
|
|
85
|
+
expect(lastFrame()).toContain('PR #42');
|
|
86
|
+
});
|
|
87
|
+
it('shows comment activities', () => {
|
|
88
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities }));
|
|
89
|
+
expect(lastFrame()).toContain('Commented');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
describe('User Filter', () => {
|
|
93
|
+
it('filters by user', () => {
|
|
94
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities, filterUser: "alice" }));
|
|
95
|
+
expect(lastFrame()).toContain('alice');
|
|
96
|
+
expect(lastFrame()).not.toContain('bob');
|
|
97
|
+
});
|
|
98
|
+
it('shows filter indicator', () => {
|
|
99
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities, filterUser: "alice" }));
|
|
100
|
+
expect(lastFrame()).toMatch(/filter|alice/i);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
describe('Type Filter', () => {
|
|
104
|
+
it('filters by activity type', () => {
|
|
105
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities, filterType: "commit" }));
|
|
106
|
+
expect(lastFrame()).toContain('Added user authentication');
|
|
107
|
+
expect(lastFrame()).not.toContain('Claimed Task 5');
|
|
108
|
+
});
|
|
109
|
+
it('shows type filter indicator', () => {
|
|
110
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities, filterType: "commit" }));
|
|
111
|
+
expect(lastFrame()).toMatch(/commit|filter/i);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
describe('Combined Filters', () => {
|
|
115
|
+
it('filters by user and type', () => {
|
|
116
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities, filterUser: "alice", filterType: "commit" }));
|
|
117
|
+
expect(lastFrame()).toContain('Added user authentication');
|
|
118
|
+
expect(lastFrame()).not.toContain('PR #42');
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
describe('References', () => {
|
|
122
|
+
it('shows reference links', () => {
|
|
123
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities }));
|
|
124
|
+
expect(lastFrame()).toMatch(/abc1234|task-5|pr-42/);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
describe('Empty State', () => {
|
|
128
|
+
it('shows message when no activities', () => {
|
|
129
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: [] }));
|
|
130
|
+
expect(lastFrame()).toMatch(/no.*activity|quiet|empty/i);
|
|
131
|
+
});
|
|
132
|
+
it('shows message when filter matches nothing', () => {
|
|
133
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities, filterUser: "nonexistent" }));
|
|
134
|
+
expect(lastFrame()).toMatch(/no.*activity|no.*match/i);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
describe('Limit', () => {
|
|
138
|
+
it('limits number of activities shown', () => {
|
|
139
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities, limit: 2 }));
|
|
140
|
+
const output = lastFrame() || '';
|
|
141
|
+
// Should only show first 2 activities
|
|
142
|
+
expect(output).toContain('Added user authentication');
|
|
143
|
+
expect(output).toContain('Claimed Task 5');
|
|
144
|
+
});
|
|
145
|
+
it('shows "more" indicator when limited', () => {
|
|
146
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities, limit: 2 }));
|
|
147
|
+
expect(lastFrame()).toMatch(/more|\+\d|\.\.\.|\d+.*remaining/i);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
describe('Compact Mode', () => {
|
|
151
|
+
it('supports compact display', () => {
|
|
152
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities, compact: true }));
|
|
153
|
+
expect(lastFrame()).toContain('alice');
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
describe('Header', () => {
|
|
157
|
+
it('shows activity count', () => {
|
|
158
|
+
const { lastFrame } = render(_jsx(ActivityFeed, { activities: sampleActivities }));
|
|
159
|
+
expect(lastFrame()).toContain('5');
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface Branch {
|
|
2
|
+
name: string;
|
|
3
|
+
isCurrent?: boolean;
|
|
4
|
+
ahead?: number;
|
|
5
|
+
behind?: number;
|
|
6
|
+
lastCommit?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface BranchSelectorProps {
|
|
9
|
+
branches: Branch[];
|
|
10
|
+
currentBranch?: string;
|
|
11
|
+
initialSelected?: number;
|
|
12
|
+
filter?: string;
|
|
13
|
+
compact?: boolean;
|
|
14
|
+
onSelect?: (branch: Branch) => void;
|
|
15
|
+
}
|
|
16
|
+
export declare function BranchSelector({ branches, currentBranch, initialSelected, filter, compact, onSelect, }: BranchSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useMemo } from 'react';
|
|
3
|
+
import { Box, Text, useInput } from 'ink';
|
|
4
|
+
export function BranchSelector({ branches, currentBranch, initialSelected = 0, filter = '', compact = false, onSelect, }) {
|
|
5
|
+
const [selectedIndex, setSelectedIndex] = useState(initialSelected);
|
|
6
|
+
// Filter branches
|
|
7
|
+
const filteredBranches = useMemo(() => {
|
|
8
|
+
if (!filter)
|
|
9
|
+
return branches;
|
|
10
|
+
const lowerFilter = filter.toLowerCase();
|
|
11
|
+
return branches.filter((b) => b.name.toLowerCase().includes(lowerFilter));
|
|
12
|
+
}, [branches, filter]);
|
|
13
|
+
// Determine current branch
|
|
14
|
+
const current = useMemo(() => {
|
|
15
|
+
const fromProp = branches.find((b) => b.name === currentBranch);
|
|
16
|
+
if (fromProp)
|
|
17
|
+
return fromProp.name;
|
|
18
|
+
const fromFlag = branches.find((b) => b.isCurrent);
|
|
19
|
+
return fromFlag?.name;
|
|
20
|
+
}, [branches, currentBranch]);
|
|
21
|
+
// Handle keyboard navigation
|
|
22
|
+
useInput((input, key) => {
|
|
23
|
+
if (filteredBranches.length === 0)
|
|
24
|
+
return;
|
|
25
|
+
if (key.downArrow || input === 'j') {
|
|
26
|
+
setSelectedIndex((prev) => Math.min(prev + 1, filteredBranches.length - 1));
|
|
27
|
+
}
|
|
28
|
+
else if (key.upArrow || input === 'k') {
|
|
29
|
+
setSelectedIndex((prev) => Math.max(prev - 1, 0));
|
|
30
|
+
}
|
|
31
|
+
else if (key.return && onSelect) {
|
|
32
|
+
onSelect(filteredBranches[selectedIndex]);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
// Empty state
|
|
36
|
+
if (branches.length === 0) {
|
|
37
|
+
return (_jsx(Box, { flexDirection: "column", padding: 1, children: _jsx(Text, { dimColor: true, children: "No branches found" }) }));
|
|
38
|
+
}
|
|
39
|
+
// No matches for filter
|
|
40
|
+
if (filteredBranches.length === 0) {
|
|
41
|
+
return (_jsx(Box, { flexDirection: "column", padding: 1, children: _jsxs(Text, { dimColor: true, children: ["No branches matching \"", filter, "\""] }) }));
|
|
42
|
+
}
|
|
43
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { dimColor: true, children: [filteredBranches.length, " branch", filteredBranches.length !== 1 ? 'es' : '', filter && ` matching "${filter}"`] }) }), filteredBranches.map((branch, index) => {
|
|
44
|
+
const isSelected = index === selectedIndex;
|
|
45
|
+
const isCurrent = branch.name === current || branch.isCurrent;
|
|
46
|
+
const isSynced = (branch.ahead || 0) === 0 && (branch.behind || 0) === 0;
|
|
47
|
+
return (_jsxs(Box, { marginBottom: compact ? 0 : 1, children: [_jsx(Text, { color: isSelected ? 'cyan' : undefined, children: isSelected ? '▶ ' : ' ' }), _jsx(Text, { color: "green", children: isCurrent ? '* ' : ' ' }), _jsx(Text, { bold: isSelected || isCurrent, color: isSelected ? 'cyan' : isCurrent ? 'green' : 'white', children: branch.name }), _jsxs(Box, { marginLeft: 1, children: [branch.ahead !== undefined && branch.ahead > 0 && (_jsxs(Text, { color: "green", children: ["\u2191", branch.ahead] })), branch.behind !== undefined && branch.behind > 0 && (_jsxs(Text, { color: "yellow", children: [" \u2193", branch.behind] })), isSynced && isCurrent && (_jsx(Text, { color: "green", children: " \u2713" }))] }), !compact && branch.lastCommit && (_jsx(Box, { marginLeft: 1, children: _jsxs(Text, { dimColor: true, children: ["(", branch.lastCommit, ")"] }) }))] }, branch.name));
|
|
48
|
+
}), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "\u2191/k \u2193/j navigate \u2022 Enter switch" }) })] }));
|
|
49
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
+
import { render } from 'ink-testing-library';
|
|
4
|
+
import { BranchSelector } from './BranchSelector.js';
|
|
5
|
+
const sampleBranches = [
|
|
6
|
+
{
|
|
7
|
+
name: 'main',
|
|
8
|
+
isCurrent: true,
|
|
9
|
+
ahead: 0,
|
|
10
|
+
behind: 0,
|
|
11
|
+
lastCommit: '2 hours ago',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: 'feature/auth',
|
|
15
|
+
isCurrent: false,
|
|
16
|
+
ahead: 3,
|
|
17
|
+
behind: 1,
|
|
18
|
+
lastCommit: '30 min ago',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'feature/dashboard',
|
|
22
|
+
isCurrent: false,
|
|
23
|
+
ahead: 5,
|
|
24
|
+
behind: 0,
|
|
25
|
+
lastCommit: '1 hour ago',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'bugfix/login',
|
|
29
|
+
isCurrent: false,
|
|
30
|
+
ahead: 1,
|
|
31
|
+
behind: 2,
|
|
32
|
+
lastCommit: '3 hours ago',
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
describe('BranchSelector', () => {
|
|
36
|
+
describe('Current Branch', () => {
|
|
37
|
+
it('shows current branch name', () => {
|
|
38
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: sampleBranches }));
|
|
39
|
+
expect(lastFrame()).toContain('main');
|
|
40
|
+
});
|
|
41
|
+
it('highlights current branch', () => {
|
|
42
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: sampleBranches }));
|
|
43
|
+
// Current branch should have indicator
|
|
44
|
+
expect(lastFrame()).toContain('*');
|
|
45
|
+
});
|
|
46
|
+
it('shows current branch when no branches marked current', () => {
|
|
47
|
+
const branches = sampleBranches.map(b => ({ ...b, isCurrent: false }));
|
|
48
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: branches, currentBranch: "main" }));
|
|
49
|
+
expect(lastFrame()).toContain('main');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe('Branch List', () => {
|
|
53
|
+
it('shows all branches', () => {
|
|
54
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: sampleBranches }));
|
|
55
|
+
expect(lastFrame()).toContain('main');
|
|
56
|
+
expect(lastFrame()).toContain('feature/auth');
|
|
57
|
+
expect(lastFrame()).toContain('feature/dashboard');
|
|
58
|
+
expect(lastFrame()).toContain('bugfix/login');
|
|
59
|
+
});
|
|
60
|
+
it('shows last commit time', () => {
|
|
61
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: sampleBranches }));
|
|
62
|
+
expect(lastFrame()).toContain('2 hours ago');
|
|
63
|
+
expect(lastFrame()).toContain('30 min ago');
|
|
64
|
+
});
|
|
65
|
+
it('shows branch count', () => {
|
|
66
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: sampleBranches }));
|
|
67
|
+
expect(lastFrame()).toContain('4');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('Ahead/Behind Status', () => {
|
|
71
|
+
it('shows ahead count', () => {
|
|
72
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: sampleBranches }));
|
|
73
|
+
expect(lastFrame()).toContain('↑3');
|
|
74
|
+
});
|
|
75
|
+
it('shows behind count', () => {
|
|
76
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: sampleBranches }));
|
|
77
|
+
expect(lastFrame()).toContain('↓1');
|
|
78
|
+
});
|
|
79
|
+
it('shows both ahead and behind', () => {
|
|
80
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: sampleBranches }));
|
|
81
|
+
// feature/auth has ahead: 3, behind: 1
|
|
82
|
+
const output = lastFrame() || '';
|
|
83
|
+
expect(output).toContain('↑3');
|
|
84
|
+
expect(output).toContain('↓1');
|
|
85
|
+
});
|
|
86
|
+
it('hides zero ahead/behind', () => {
|
|
87
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: sampleBranches }));
|
|
88
|
+
// main has ahead: 0, behind: 0 - should not show ↑0 ↓0
|
|
89
|
+
const output = lastFrame() || '';
|
|
90
|
+
// Count occurrences - main shouldn't add ↑0 or ↓0
|
|
91
|
+
expect(output).not.toContain('↑0');
|
|
92
|
+
expect(output).not.toContain('↓0');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
describe('Selection', () => {
|
|
96
|
+
it('first branch is selected by default', () => {
|
|
97
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: sampleBranches }));
|
|
98
|
+
expect(lastFrame()).toContain('▶');
|
|
99
|
+
});
|
|
100
|
+
it('accepts initialSelected prop', () => {
|
|
101
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: sampleBranches, initialSelected: 2 }));
|
|
102
|
+
// Third branch (feature/dashboard) should be selected
|
|
103
|
+
expect(lastFrame()).toContain('▶');
|
|
104
|
+
});
|
|
105
|
+
it('calls onSelect when branch selected', () => {
|
|
106
|
+
const onSelect = vi.fn();
|
|
107
|
+
render(_jsx(BranchSelector, { branches: sampleBranches, onSelect: onSelect }));
|
|
108
|
+
// Selection happens on Enter key - tested via stdin
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
describe('Navigation', () => {
|
|
112
|
+
it('shows navigation hint', () => {
|
|
113
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: sampleBranches }));
|
|
114
|
+
expect(lastFrame()).toContain('↑/k');
|
|
115
|
+
expect(lastFrame()).toContain('↓/j');
|
|
116
|
+
});
|
|
117
|
+
it('shows switch hint', () => {
|
|
118
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: sampleBranches }));
|
|
119
|
+
expect(lastFrame()).toContain('Enter');
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe('Empty State', () => {
|
|
123
|
+
it('shows message when no branches', () => {
|
|
124
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: [] }));
|
|
125
|
+
expect(lastFrame()).toContain('No branches');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
describe('Filter', () => {
|
|
129
|
+
it('filters branches by name', () => {
|
|
130
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: sampleBranches, filter: "feature" }));
|
|
131
|
+
expect(lastFrame()).toContain('feature/auth');
|
|
132
|
+
expect(lastFrame()).toContain('feature/dashboard');
|
|
133
|
+
expect(lastFrame()).not.toContain('bugfix/login');
|
|
134
|
+
});
|
|
135
|
+
it('shows filter hint when filtering', () => {
|
|
136
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: sampleBranches, filter: "feature" }));
|
|
137
|
+
expect(lastFrame()).toContain('feature');
|
|
138
|
+
});
|
|
139
|
+
it('shows no results when filter matches nothing', () => {
|
|
140
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: sampleBranches, filter: "nonexistent" }));
|
|
141
|
+
expect(lastFrame()).toContain('No branches matching');
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
describe('Compact Mode', () => {
|
|
145
|
+
it('supports compact display', () => {
|
|
146
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: sampleBranches, compact: true }));
|
|
147
|
+
expect(lastFrame()).toContain('main');
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
describe('Status Indicators', () => {
|
|
151
|
+
it('shows clean status for synced branch', () => {
|
|
152
|
+
const syncedBranches = [
|
|
153
|
+
{ name: 'main', isCurrent: true, ahead: 0, behind: 0 },
|
|
154
|
+
];
|
|
155
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: syncedBranches }));
|
|
156
|
+
expect(lastFrame()).toContain('✓');
|
|
157
|
+
});
|
|
158
|
+
it('shows warning for behind branch', () => {
|
|
159
|
+
const behindBranches = [
|
|
160
|
+
{ name: 'old-branch', isCurrent: true, ahead: 0, behind: 5 },
|
|
161
|
+
];
|
|
162
|
+
const { lastFrame } = render(_jsx(BranchSelector, { branches: behindBranches }));
|
|
163
|
+
expect(lastFrame()).toContain('↓5');
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface Command {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
shortcut?: string;
|
|
6
|
+
category?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface CommandPaletteProps {
|
|
9
|
+
commands: Command[];
|
|
10
|
+
query?: string;
|
|
11
|
+
recentIds?: string[];
|
|
12
|
+
isActive?: boolean;
|
|
13
|
+
onSelect: (command: Command) => void;
|
|
14
|
+
onQueryChange?: (query: string) => void;
|
|
15
|
+
onClose?: () => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function CommandPalette({ commands, query, recentIds, isActive, onSelect, onQueryChange, onClose, }: CommandPaletteProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useMemo } from 'react';
|
|
3
|
+
import { Box, Text, useInput } from 'ink';
|
|
4
|
+
function fuzzyMatch(text, query) {
|
|
5
|
+
const lowerText = text.toLowerCase();
|
|
6
|
+
const lowerQuery = query.toLowerCase();
|
|
7
|
+
return lowerText.includes(lowerQuery);
|
|
8
|
+
}
|
|
9
|
+
function groupByCategory(commands) {
|
|
10
|
+
const groups = {};
|
|
11
|
+
for (const cmd of commands) {
|
|
12
|
+
const category = cmd.category || 'general';
|
|
13
|
+
if (!groups[category]) {
|
|
14
|
+
groups[category] = [];
|
|
15
|
+
}
|
|
16
|
+
groups[category].push(cmd);
|
|
17
|
+
}
|
|
18
|
+
return Object.entries(groups).map(([category, commands]) => ({
|
|
19
|
+
category,
|
|
20
|
+
commands,
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
export function CommandPalette({ commands, query = '', recentIds = [], isActive = true, onSelect, onQueryChange, onClose, }) {
|
|
24
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
25
|
+
const [internalQuery, setInternalQuery] = useState(query);
|
|
26
|
+
const effectiveQuery = query || internalQuery;
|
|
27
|
+
// Filter commands by query
|
|
28
|
+
const filteredCommands = useMemo(() => {
|
|
29
|
+
if (!effectiveQuery)
|
|
30
|
+
return commands;
|
|
31
|
+
return commands.filter((cmd) => fuzzyMatch(cmd.name, effectiveQuery) ||
|
|
32
|
+
fuzzyMatch(cmd.description, effectiveQuery) ||
|
|
33
|
+
fuzzyMatch(cmd.id, effectiveQuery));
|
|
34
|
+
}, [commands, effectiveQuery]);
|
|
35
|
+
// Sort with recent commands first
|
|
36
|
+
const sortedCommands = useMemo(() => {
|
|
37
|
+
if (recentIds.length === 0)
|
|
38
|
+
return filteredCommands;
|
|
39
|
+
const recent = [];
|
|
40
|
+
const others = [];
|
|
41
|
+
for (const cmd of filteredCommands) {
|
|
42
|
+
if (recentIds.includes(cmd.id)) {
|
|
43
|
+
recent.push(cmd);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
others.push(cmd);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return [...recent, ...others];
|
|
50
|
+
}, [filteredCommands, recentIds]);
|
|
51
|
+
// Group commands
|
|
52
|
+
const groupedCommands = useMemo(() => {
|
|
53
|
+
if (recentIds.length > 0 && !effectiveQuery) {
|
|
54
|
+
// Show recent section separately
|
|
55
|
+
const recent = sortedCommands.filter((c) => recentIds.includes(c.id));
|
|
56
|
+
const others = sortedCommands.filter((c) => !recentIds.includes(c.id));
|
|
57
|
+
const groups = [];
|
|
58
|
+
if (recent.length > 0) {
|
|
59
|
+
groups.push({ category: 'recent', commands: recent });
|
|
60
|
+
}
|
|
61
|
+
groups.push(...groupByCategory(others));
|
|
62
|
+
return groups;
|
|
63
|
+
}
|
|
64
|
+
return groupByCategory(sortedCommands);
|
|
65
|
+
}, [sortedCommands, recentIds, effectiveQuery]);
|
|
66
|
+
// Flatten for navigation
|
|
67
|
+
const flatCommands = sortedCommands;
|
|
68
|
+
useInput((input, key) => {
|
|
69
|
+
if (!isActive)
|
|
70
|
+
return;
|
|
71
|
+
// Close on Escape
|
|
72
|
+
if (key.escape) {
|
|
73
|
+
onClose?.();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// Execute on Enter
|
|
77
|
+
if (key.return && flatCommands[selectedIndex]) {
|
|
78
|
+
onSelect(flatCommands[selectedIndex]);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Navigation
|
|
82
|
+
if (key.downArrow || input === 'j') {
|
|
83
|
+
setSelectedIndex((prev) => Math.min(prev + 1, flatCommands.length - 1));
|
|
84
|
+
}
|
|
85
|
+
else if (key.upArrow || input === 'k') {
|
|
86
|
+
setSelectedIndex((prev) => Math.max(prev - 1, 0));
|
|
87
|
+
}
|
|
88
|
+
// Backspace
|
|
89
|
+
else if (key.backspace || key.delete) {
|
|
90
|
+
const newQuery = internalQuery.slice(0, -1);
|
|
91
|
+
setInternalQuery(newQuery);
|
|
92
|
+
onQueryChange?.(newQuery);
|
|
93
|
+
setSelectedIndex(0);
|
|
94
|
+
}
|
|
95
|
+
// Regular character input
|
|
96
|
+
else if (input && !key.ctrl && !key.meta && input.length === 1) {
|
|
97
|
+
const newQuery = internalQuery + input;
|
|
98
|
+
setInternalQuery(newQuery);
|
|
99
|
+
onQueryChange?.(newQuery);
|
|
100
|
+
setSelectedIndex(0);
|
|
101
|
+
}
|
|
102
|
+
}, { isActive });
|
|
103
|
+
// Empty commands
|
|
104
|
+
if (commands.length === 0) {
|
|
105
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "Command Palette" }) }), _jsx(Text, { dimColor: true, children: "No commands available" })] }));
|
|
106
|
+
}
|
|
107
|
+
// No matches
|
|
108
|
+
if (filteredCommands.length === 0) {
|
|
109
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { color: "cyan", children: "> " }), _jsx(Text, { children: effectiveQuery }), _jsx(Text, { color: "cyan", children: "\u258F" })] }), _jsxs(Text, { color: "yellow", children: ["No commands matching \"", effectiveQuery, "\""] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Esc close" }) })] }));
|
|
110
|
+
}
|
|
111
|
+
let commandIndex = 0;
|
|
112
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { color: "cyan", children: "> " }), _jsx(Text, { children: effectiveQuery }), _jsx(Text, { color: "cyan", children: "\u258F" }), effectiveQuery && (_jsxs(Text, { dimColor: true, children: [" (", filteredCommands.length, " commands)"] }))] }), groupedCommands.map((group) => (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "gray", children: group.category.toUpperCase() }) }), group.commands.map((cmd) => {
|
|
113
|
+
const isSelected = commandIndex === selectedIndex;
|
|
114
|
+
const currentIndex = commandIndex;
|
|
115
|
+
commandIndex++;
|
|
116
|
+
return (_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { color: isSelected ? 'cyan' : undefined, children: isSelected ? '▶ ' : ' ' }), cmd.shortcut && (_jsxs(Text, { color: "yellow", children: ["[", cmd.shortcut, "] "] })), _jsx(Text, { bold: isSelected, color: isSelected ? 'cyan' : 'white', children: cmd.name }), _jsxs(Text, { dimColor: true, children: [" - ", cmd.description] })] }, cmd.id));
|
|
117
|
+
})] }, group.category))), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "\u2191/k \u2193/j navigate \u2022 Enter execute \u2022 Esc close" }) })] }));
|
|
118
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|