@jhorst11/wt 2.0.2 → 2.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/src/ui.js DELETED
@@ -1,302 +0,0 @@
1
- import chalk from 'chalk';
2
- import gradient from 'gradient-string';
3
- import figures from 'figures';
4
- import { createRequire } from 'module';
5
-
6
- const require = createRequire(import.meta.url);
7
- const { version } = require('../package.json');
8
-
9
- // Custom gradient for the logo
10
- const wtGradient = gradient(['#00d4ff', '#7c3aed', '#f472b6']);
11
- const successGradient = gradient(['#10b981', '#34d399']);
12
- const warningGradient = gradient(['#f59e0b', '#fbbf24']);
13
-
14
- export const icons = {
15
- tree: 'đŸŒŗ',
16
- branch: 'đŸŒŋ',
17
- rocket: '🚀',
18
- sparkles: '✨',
19
- folder: '📁',
20
- trash: 'đŸ—‘ī¸',
21
- home: '🏠',
22
- check: figures.tick,
23
- cross: figures.cross,
24
- pointer: figures.pointer,
25
- arrowRight: figures.arrowRight,
26
- bullet: figures.bullet,
27
- star: '⭐',
28
- git: 'ķ°Šĸ',
29
- plus: '➕',
30
- warning: 'âš ī¸',
31
- info: 'â„šī¸',
32
- remote: 'â˜ī¸',
33
- local: 'đŸ’ģ',
34
- };
35
-
36
- export const colors = {
37
- primary: chalk.hex('#7c3aed'),
38
- secondary: chalk.hex('#00d4ff'),
39
- success: chalk.hex('#10b981'),
40
- warning: chalk.hex('#f59e0b'),
41
- error: chalk.hex('#ef4444'),
42
- muted: chalk.gray,
43
- highlight: chalk.hex('#f472b6'),
44
- branch: chalk.hex('#34d399'),
45
- path: chalk.hex('#60a5fa'),
46
- };
47
-
48
- export function showLogo() {
49
- const logo = `
50
- ${wtGradient('â•Ļ â•Ļ╔═╗â•Ļ═╗â•Ļ╔═╔â•Ļ╗â•Ļ═╗╔═╗╔═╗')}
51
- ${wtGradient('║║║║ ║╠â•Ļ╝╠╩╗ ║ ╠â•Ļâ•â•‘â•Ŗ â•‘â•Ŗ ')}
52
- ${wtGradient('╚╩╝╚═╝╩╚═╩ ╩ ╩ ╩╚═╚═╝╚═╝')}
53
- ${chalk.gray(' Git Worktree Manager')}
54
- `;
55
- console.log(logo);
56
- }
57
-
58
- export function showMiniLogo(worktreeInfo = null) {
59
- console.log(`\n ${icons.tree} ${wtGradient('worktree')} ${colors.muted(`v${version}`)}`);
60
- if (worktreeInfo) {
61
- const colorDot = colorIndicator(worktreeInfo.color);
62
- console.log(` ${colorDot} ${colors.highlight(worktreeInfo.name)} ${colors.muted(`→ ${worktreeInfo.branch}`)}`);
63
- }
64
- console.log('');
65
- }
66
-
67
- export function success(message) {
68
- console.log(` ${colors.success(icons.check)} ${message}`);
69
- }
70
-
71
- export function error(message) {
72
- console.log(` ${colors.error(icons.cross)} ${message}`);
73
- }
74
-
75
- export function warning(message) {
76
- console.log(` ${colors.warning(icons.warning)} ${message}`);
77
- }
78
-
79
- export function info(message) {
80
- console.log(` ${colors.secondary(icons.info)} ${message}`);
81
- }
82
-
83
- export function heading(text) {
84
- console.log(`\n ${colors.primary.bold(text)}\n`);
85
- }
86
-
87
- export function subheading(text) {
88
- console.log(` ${colors.muted(text)}`);
89
- }
90
-
91
- export function listItem(text, indent = 2) {
92
- const spaces = ' '.repeat(indent);
93
- console.log(`${spaces}${colors.secondary(icons.bullet)} ${text}`);
94
- }
95
-
96
- export function branchItem(name, isCurrent = false, isRemote = false) {
97
- const icon = isRemote ? icons.remote : icons.local;
98
- const prefix = isCurrent ? colors.success(icons.pointer) : ' ';
99
- const branchName = isCurrent ? colors.success.bold(name) : colors.branch(name);
100
- const typeLabel = isRemote ? colors.muted(' (remote)') : '';
101
- console.log(` ${prefix} ${icon} ${branchName}${typeLabel}`);
102
- }
103
-
104
- export function worktreeItem(name, path, isCurrent = false, color = null) {
105
- const prefix = isCurrent ? colors.success(icons.pointer) : ' ';
106
- const nameDisplay = isCurrent ? colors.success.bold(name) : colors.highlight(name);
107
- const colorDot = colorIndicator(color);
108
- const displayName = colorDot ? `${colorDot} ${nameDisplay}` : nameDisplay;
109
- console.log(` ${prefix} ${icons.folder} ${displayName}`);
110
- console.log(` ${colors.muted(path)}`);
111
- }
112
-
113
- export function divider() {
114
- console.log(colors.muted(' ─'.repeat(20)));
115
- }
116
-
117
- export function spacer() {
118
- console.log('');
119
- }
120
-
121
- /**
122
- * Convert hex color to chalk color function.
123
- * Falls back to gray for invalid hex.
124
- * @param {string} hex - Hex color like "#E53935" or "E53935"
125
- * @returns {Function} Chalk color function
126
- */
127
- export function hexToChalk(hex) {
128
- if (!hex) return chalk.gray;
129
- const clean = hex.replace(/^#/, '');
130
- if (!/^[0-9A-Fa-f]{6}$/.test(clean)) return chalk.gray;
131
- return chalk.hex(clean);
132
- }
133
-
134
- /**
135
- * Create a colored circle indicator (●) for visual color display.
136
- * Returns empty string if hex is null/invalid.
137
- * @param {string} hex - Hex color like "#E53935"
138
- * @returns {string} Colored circle character or empty string
139
- */
140
- export function colorIndicator(hex) {
141
- if (!hex) return '';
142
- const color = hexToChalk(hex);
143
- return color('●'); // U+25CF filled circle
144
- }
145
-
146
- /**
147
- * Convert hex to RGB components for terminal sequences.
148
- * @param {string} hex - Hex color like "#E53935"
149
- * @returns {{r: number, g: number, b: number}} RGB components 0-255
150
- */
151
- export function hexToRgb(hex) {
152
- const clean = hex.replace(/^#/, '');
153
- return {
154
- r: parseInt(clean.slice(0, 2), 16),
155
- g: parseInt(clean.slice(2, 4), 16),
156
- b: parseInt(clean.slice(4, 6), 16),
157
- };
158
- }
159
-
160
- /**
161
- * Create a colored divider using the specified hex color.
162
- * Falls back to muted color if hex is invalid.
163
- * @param {string} hex - Hex color like "#E53935"
164
- */
165
- export function coloredDivider(hex) {
166
- const color = hex ? hexToChalk(hex) : colors.muted;
167
- console.log(color(' ─'.repeat(20)));
168
- }
169
-
170
- /**
171
- * Detect terminal type for appropriate color sequences.
172
- * Checks TERM_PROGRAM, TERM, WT_SESSION, and COLORTERM env vars.
173
- * @returns {string} Terminal type: 'iterm2' | 'wezterm' | 'alacritty' |
174
- * 'kitty' | 'ghostty' | 'windows-terminal' | 'vscode' |
175
- * 'osc-generic' | 'unsupported'
176
- */
177
- export function detectTerminal() {
178
- const termProgram = process.env.TERM_PROGRAM || '';
179
- const term = process.env.TERM || '';
180
- const colorTerm = process.env.COLORTERM || '';
181
-
182
- if (termProgram === 'iTerm.app') return 'iterm2';
183
- if (termProgram === 'WezTerm') return 'wezterm';
184
- if (termProgram === 'ghostty') return 'ghostty';
185
- if (termProgram === 'vscode') return 'vscode';
186
- if (process.env.WT_SESSION) return 'windows-terminal';
187
- if (term.includes('kitty')) return 'kitty';
188
- if (termProgram.includes('Alacritty') || term.includes('alacritty')) return 'alacritty';
189
-
190
- // Generic OSC support for truecolor terminals
191
- if (colorTerm === 'truecolor' || colorTerm === '24bit') return 'osc-generic';
192
-
193
- return 'unsupported';
194
- }
195
-
196
- /**
197
- * Set terminal tab color (supports multiple terminal types).
198
- * Works with iTerm2, WezTerm, Kitty, Alacritty, Windows Terminal.
199
- * No-op if stdout is not a TTY or terminal is unsupported.
200
- * @param {string} hex - Hex color like "#E53935" or "E53935"
201
- */
202
- export function setTabColor(hex) {
203
- if (!process.stdout.isTTY || !hex) return;
204
-
205
- const terminal = detectTerminal();
206
- const rgb = hex.replace(/^#/, '');
207
-
208
- if (!/^[0-9A-Fa-f]{6}$/.test(rgb)) return;
209
-
210
- switch (terminal) {
211
- case 'iterm2':
212
- case 'osc-generic':
213
- process.stdout.write(`\x1b]1337;SetColors=tab=${rgb}\x07`);
214
- break;
215
- case 'wezterm':
216
- // WezTerm uses base64-encoded user vars
217
- const b64 = Buffer.from(rgb).toString('base64');
218
- process.stdout.write(`\x1b]1337;SetUserVar=tab_color=${b64}\x07`);
219
- break;
220
- case 'kitty':
221
- process.stdout.write(`\x1b]30001;rgb:${rgb}\x07`);
222
- break;
223
- case 'alacritty':
224
- // Background color as fallback
225
- const r = rgb.slice(0, 2);
226
- const g = rgb.slice(2, 4);
227
- const b = rgb.slice(4, 6);
228
- process.stdout.write(`\x1b]10;rgb:${r}/${g}/${b}\x07`);
229
- break;
230
- case 'windows-terminal':
231
- process.stdout.write(`\x1b]9;4;1;${rgb}\x07`);
232
- break;
233
- // 'ghostty', 'vscode' and 'unsupported' - no-op
234
- }
235
- }
236
-
237
- /**
238
- * Reset terminal tab color to default.
239
- * Gracefully handles multiple terminal types.
240
- */
241
- export function resetTabColor() {
242
- if (!process.stdout.isTTY) return;
243
-
244
- const terminal = detectTerminal();
245
-
246
- switch (terminal) {
247
- case 'iterm2':
248
- case 'osc-generic':
249
- process.stdout.write('\x1b]1337;SetColors=tab=default\x07');
250
- break;
251
- case 'wezterm':
252
- const b64 = Buffer.from('').toString('base64');
253
- process.stdout.write(`\x1b]1337;SetUserVar=tab_color=${b64}\x07`);
254
- break;
255
- case 'kitty':
256
- process.stdout.write('\x1b]30001;rgb:000000\x07');
257
- break;
258
- case 'alacritty':
259
- process.stdout.write('\x1b]10;rgb:00/00/00\x07');
260
- break;
261
- case 'windows-terminal':
262
- process.stdout.write('\x1b]9;4;0\x07');
263
- break;
264
- }
265
- }
266
-
267
- export function formatBranchChoice(branch, type = 'local') {
268
- const icon = type === 'remote' ? icons.remote : icons.local;
269
- const typeLabel = type === 'remote' ? chalk.dim(' (remote)') : '';
270
- return `${icon} ${branch}${typeLabel}`;
271
- }
272
-
273
- export function formatWorktreeChoice(wt, color = null) {
274
- const colorDot = colorIndicator(color);
275
- const prefix = colorDot ? `${colorDot} ` : '';
276
- return `${prefix}${icons.folder} ${colors.highlight(wt.name)} ${colors.muted(`→ ${wt.branch}`)}`;
277
- }
278
-
279
- export function showHelp() {
280
- showLogo();
281
-
282
- console.log(colors.primary.bold(' Commands:\n'));
283
-
284
- const commands = [
285
- ['wt', 'Interactive menu to manage worktrees'],
286
- ['wt new', 'Create a new worktree interactively'],
287
- ['wt list|ls', 'List all worktrees for current repo'],
288
- ['wt go [name]', 'Jump to a worktree (interactive if no name)'],
289
- ['wt merge', 'Merge a worktree branch into another branch'],
290
- ['wt remove|rm', 'Remove a worktree interactively'],
291
- ['wt home', 'Jump back to the main repository'],
292
- ['wt setup', 'Configure shell integration for auto-navigation'],
293
- ];
294
-
295
- commands.forEach(([cmd, desc]) => {
296
- console.log(` ${colors.secondary(cmd.padEnd(18))} ${colors.muted(desc)}`);
297
- });
298
-
299
- spacer();
300
- console.log(colors.muted(' Run any command without arguments for interactive mode'));
301
- spacer();
302
- }