ccmanager 0.1.14 → 0.2.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/dist/cli.js +3 -0
- package/dist/components/Configuration.js +34 -6
- package/dist/components/Menu.js +87 -25
- package/dist/hooks/useGitStatus.d.ts +2 -0
- package/dist/hooks/useGitStatus.js +52 -0
- package/dist/hooks/useGitStatus.test.d.ts +1 -0
- package/dist/hooks/useGitStatus.test.js +186 -0
- package/dist/services/worktreeConfigManager.d.ts +10 -0
- package/dist/services/worktreeConfigManager.js +27 -0
- package/dist/services/worktreeService.js +8 -0
- package/dist/services/worktreeService.test.js +8 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/utils/concurrencyLimit.d.ts +4 -0
- package/dist/utils/concurrencyLimit.js +30 -0
- package/dist/utils/concurrencyLimit.test.d.ts +1 -0
- package/dist/utils/concurrencyLimit.test.js +63 -0
- package/dist/utils/gitStatus.d.ts +19 -0
- package/dist/utils/gitStatus.js +146 -0
- package/dist/utils/gitStatus.test.d.ts +1 -0
- package/dist/utils/gitStatus.test.js +141 -0
- package/dist/utils/worktreeConfig.d.ts +3 -0
- package/dist/utils/worktreeConfig.js +43 -0
- package/dist/utils/worktreeUtils.d.ts +37 -0
- package/dist/utils/worktreeUtils.js +114 -0
- package/dist/utils/worktreeUtils.test.js +105 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { generateWorktreeDirectory, extractBranchParts, } from './worktreeUtils.js';
|
|
2
|
+
import { generateWorktreeDirectory, extractBranchParts, truncateString, prepareWorktreeItems, calculateColumnPositions, assembleWorktreeLabel, } from './worktreeUtils.js';
|
|
3
3
|
describe('generateWorktreeDirectory', () => {
|
|
4
4
|
describe('with default pattern', () => {
|
|
5
5
|
it('should generate directory with sanitized branch name', () => {
|
|
@@ -72,3 +72,107 @@ describe('extractBranchParts', () => {
|
|
|
72
72
|
});
|
|
73
73
|
});
|
|
74
74
|
});
|
|
75
|
+
describe('truncateString', () => {
|
|
76
|
+
it('should return original string if shorter than max length', () => {
|
|
77
|
+
expect(truncateString('hello', 10)).toBe('hello');
|
|
78
|
+
expect(truncateString('test', 4)).toBe('test');
|
|
79
|
+
});
|
|
80
|
+
it('should truncate and add ellipsis if longer than max length', () => {
|
|
81
|
+
expect(truncateString('hello world', 8)).toBe('hello...');
|
|
82
|
+
expect(truncateString('this is a long string', 10)).toBe('this is...');
|
|
83
|
+
});
|
|
84
|
+
it('should handle edge cases', () => {
|
|
85
|
+
expect(truncateString('', 5)).toBe('');
|
|
86
|
+
expect(truncateString('abc', 3)).toBe('abc');
|
|
87
|
+
expect(truncateString('abcd', 3)).toBe('...');
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
describe('prepareWorktreeItems', () => {
|
|
91
|
+
const mockWorktree = {
|
|
92
|
+
path: '/path/to/worktree',
|
|
93
|
+
branch: 'feature/test-branch',
|
|
94
|
+
isMainWorktree: false,
|
|
95
|
+
hasSession: false,
|
|
96
|
+
};
|
|
97
|
+
// Simplified mock
|
|
98
|
+
const mockSession = {
|
|
99
|
+
id: 'test-session',
|
|
100
|
+
worktreePath: '/path/to/worktree',
|
|
101
|
+
state: 'idle',
|
|
102
|
+
process: {},
|
|
103
|
+
output: [],
|
|
104
|
+
outputHistory: [],
|
|
105
|
+
lastActivity: new Date(),
|
|
106
|
+
isActive: true,
|
|
107
|
+
terminal: {},
|
|
108
|
+
};
|
|
109
|
+
it('should prepare basic worktree without git status', () => {
|
|
110
|
+
const items = prepareWorktreeItems([mockWorktree], []);
|
|
111
|
+
expect(items).toHaveLength(1);
|
|
112
|
+
expect(items[0]?.baseLabel).toBe('feature/test-branch');
|
|
113
|
+
});
|
|
114
|
+
it('should include session status in label', () => {
|
|
115
|
+
const items = prepareWorktreeItems([mockWorktree], [mockSession]);
|
|
116
|
+
expect(items[0]?.baseLabel).toContain('[○ Idle]');
|
|
117
|
+
});
|
|
118
|
+
it('should mark main worktree', () => {
|
|
119
|
+
const mainWorktree = { ...mockWorktree, isMainWorktree: true };
|
|
120
|
+
const items = prepareWorktreeItems([mainWorktree], []);
|
|
121
|
+
expect(items[0]?.baseLabel).toContain('(main)');
|
|
122
|
+
});
|
|
123
|
+
it('should truncate long branch names', () => {
|
|
124
|
+
const longBranch = {
|
|
125
|
+
...mockWorktree,
|
|
126
|
+
branch: 'feature/this-is-a-very-long-branch-name-that-should-be-truncated',
|
|
127
|
+
};
|
|
128
|
+
const items = prepareWorktreeItems([longBranch], []);
|
|
129
|
+
expect(items[0]?.baseLabel.length).toBeLessThanOrEqual(50); // 40 + status + default
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
describe('column alignment', () => {
|
|
133
|
+
const mockItems = [
|
|
134
|
+
{
|
|
135
|
+
worktree: {},
|
|
136
|
+
baseLabel: 'feature/test-branch',
|
|
137
|
+
fileChanges: '\x1b[32m+10\x1b[0m \x1b[31m-5\x1b[0m',
|
|
138
|
+
aheadBehind: '\x1b[33m↑2 ↓3\x1b[0m',
|
|
139
|
+
parentBranch: '',
|
|
140
|
+
lengths: {
|
|
141
|
+
base: 19, // 'feature/test-branch'.length
|
|
142
|
+
fileChanges: 6, // '+10 -5'.length
|
|
143
|
+
aheadBehind: 5, // '↑2 ↓3'.length
|
|
144
|
+
parentBranch: 0,
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
worktree: {},
|
|
149
|
+
baseLabel: 'main',
|
|
150
|
+
fileChanges: '\x1b[32m+2\x1b[0m \x1b[31m-1\x1b[0m',
|
|
151
|
+
aheadBehind: '\x1b[33m↑1\x1b[0m',
|
|
152
|
+
parentBranch: '',
|
|
153
|
+
lengths: {
|
|
154
|
+
base: 4, // 'main'.length
|
|
155
|
+
fileChanges: 5, // '+2 -1'.length
|
|
156
|
+
aheadBehind: 2, // '↑1'.length
|
|
157
|
+
parentBranch: 0,
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
];
|
|
161
|
+
it('should calculate column positions from items', () => {
|
|
162
|
+
const positions = calculateColumnPositions(mockItems);
|
|
163
|
+
expect(positions.fileChanges).toBe(21); // 19 + 2 padding
|
|
164
|
+
expect(positions.aheadBehind).toBeGreaterThan(positions.fileChanges);
|
|
165
|
+
expect(positions.parentBranch).toBeGreaterThan(positions.aheadBehind);
|
|
166
|
+
});
|
|
167
|
+
it('should assemble label with proper alignment', () => {
|
|
168
|
+
const item = mockItems[0];
|
|
169
|
+
const columns = calculateColumnPositions(mockItems);
|
|
170
|
+
const result = assembleWorktreeLabel(item, columns);
|
|
171
|
+
expect(result).toContain('feature/test-branch');
|
|
172
|
+
expect(result).toContain('\x1b[32m+10\x1b[0m');
|
|
173
|
+
expect(result).toContain('\x1b[33m↑2 ↓3\x1b[0m');
|
|
174
|
+
// Check alignment by stripping ANSI codes
|
|
175
|
+
const plain = result.replace(/\x1b\[[0-9;]*m/g, '');
|
|
176
|
+
expect(plain.indexOf('+10 -5')).toBe(21); // Should start at column 21
|
|
177
|
+
});
|
|
178
|
+
});
|