@yivan-lab/pretty-please 1.2.0 → 1.3.1

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.
@@ -1,6 +1,8 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
+ // 主题目录路径
5
+ const THEMES_DIR = path.join(os.homedir(), '.please', 'themes');
4
6
  // 深色主题(原默认主题)
5
7
  const darkTheme = {
6
8
  primary: '#00D9FF',
@@ -54,12 +56,219 @@ const lightTheme = {
54
56
  comment: '#4B5563',
55
57
  },
56
58
  };
57
- // 所有主题
58
- export const themes = {
59
- dark: darkTheme,
60
- light: lightTheme,
59
+ // Nord 主题 - 冷色调护眼,适合长时间使用
60
+ const nordTheme = {
61
+ primary: '#88C0D0', // Nord8 - 冰蓝色
62
+ secondary: '#B48EAD', // Nord15 - 紫色
63
+ accent: '#EBCB8B', // Nord13 - 黄色
64
+ success: '#A3BE8C', // Nord14 - 绿色
65
+ error: '#BF616A', // Nord11 - 红色
66
+ warning: '#D08770', // Nord12 - 橙色
67
+ info: '#81A1C1', // Nord9 - 蓝色
68
+ text: {
69
+ primary: '#ECEFF4', // Nord6 - 雪白色
70
+ secondary: '#D8DEE9', // Nord4 - 浅灰色
71
+ muted: '#A3BE8C', // Nord14 - 柔和绿
72
+ dim: '#5E81AC', // Nord10 - 暗蓝色
73
+ },
74
+ border: '#4C566A', // Nord3
75
+ divider: '#3B4252', // Nord1
76
+ code: {
77
+ background: '#2E3440', // Nord0 - 深灰背景
78
+ text: '#ECEFF4', // Nord6
79
+ keyword: '#81A1C1', // Nord9 - 蓝色
80
+ string: '#A3BE8C', // Nord14 - 绿色
81
+ function: '#88C0D0', // Nord8 - 青色
82
+ comment: '#616E88', // Nord3 变体
83
+ },
84
+ };
85
+ // Dracula 主题 - 暗色系流行配色
86
+ const draculaTheme = {
87
+ primary: '#8BE9FD', // 青色
88
+ secondary: '#BD93F9', // 紫色
89
+ accent: '#FF79C6', // 粉色
90
+ success: '#50FA7B', // 绿色
91
+ error: '#FF5555', // 红色
92
+ warning: '#FFB86C', // 橙色
93
+ info: '#8BE9FD', // 青色
94
+ text: {
95
+ primary: '#F8F8F2', // 几乎白色
96
+ secondary: '#BFBFBF', // 灰色
97
+ muted: '#6272A4', // 暗灰蓝
98
+ dim: '#44475A', // 深灰
99
+ },
100
+ border: '#6272A4', // 评论色
101
+ divider: '#44475A', // 深灰
102
+ code: {
103
+ background: '#282A36', // 背景色
104
+ text: '#F8F8F2', // 前景色
105
+ keyword: '#FF79C6', // 粉色关键字
106
+ string: '#F1FA8C', // 黄色字符串
107
+ function: '#50FA7B', // 绿色函数
108
+ comment: '#6272A4', // 评论色
109
+ },
110
+ };
111
+ // Retro Green 主题 - 经典终端荧光绿
112
+ const retroTheme = {
113
+ primary: '#00FF00', // 荧光绿
114
+ secondary: '#00CC00', // 暗绿
115
+ accent: '#00FFAA', // 青绿色
116
+ success: '#00FF00', // 荧光绿
117
+ error: '#FF0000', // 纯红
118
+ warning: '#FFFF00', // 纯黄
119
+ info: '#00FFFF', // 青色
120
+ text: {
121
+ primary: '#00FF00', // 荧光绿
122
+ secondary: '#00DD00', // 稍暗绿
123
+ muted: '#00AA00', // 中绿
124
+ dim: '#008800', // 暗绿
125
+ },
126
+ border: '#00AA00', // 中绿边框
127
+ divider: '#006600', // 暗绿分割线
128
+ code: {
129
+ background: '#000000', // 纯黑背景
130
+ text: '#00FF00', // 荧光绿
131
+ keyword: '#00FFFF', // 青色关键字
132
+ string: '#00FF00', // 绿色字符串
133
+ function: '#00FFAA', // 青绿函数
134
+ comment: '#008800', // 暗绿注释
135
+ },
136
+ };
137
+ // High Contrast 主题 - 极高对比度,辅助功能
138
+ const contrastTheme = {
139
+ primary: '#FFFFFF', // 纯白
140
+ secondary: '#AAAAAA', // 灰色
141
+ accent: '#00FFFF', // 青色
142
+ success: '#00FF00', // 纯绿
143
+ error: '#FF0000', // 纯红
144
+ warning: '#FFFF00', // 纯黄
145
+ info: '#00FFFF', // 青色
146
+ text: {
147
+ primary: '#FFFFFF', // 纯白
148
+ secondary: '#CCCCCC', // 浅灰
149
+ muted: '#999999', // 中灰
150
+ dim: '#666666', // 暗灰
151
+ },
152
+ border: '#FFFFFF', // 白色边框
153
+ divider: '#888888', // 灰色分割线
154
+ code: {
155
+ background: '#000000', // 纯黑背景
156
+ text: '#FFFFFF', // 纯白文字
157
+ keyword: '#00FFFF', // 青色关键字
158
+ string: '#00FF00', // 绿色字符串
159
+ function: '#FFFF00', // 黄色函数
160
+ comment: '#888888', // 灰色注释
161
+ },
162
+ };
163
+ // Monokai 主题 - 经典编辑器配色
164
+ const monokaiTheme = {
165
+ primary: '#66D9EF', // 青色
166
+ secondary: '#AE81FF', // 紫色
167
+ accent: '#F92672', // 粉红
168
+ success: '#A6E22E', // 绿色
169
+ error: '#F92672', // 粉红
170
+ warning: '#E6DB74', // 黄色
171
+ info: '#66D9EF', // 青色
172
+ text: {
173
+ primary: '#F8F8F2', // 几乎白色
174
+ secondary: '#CFCFC2', // 浅灰
175
+ muted: '#75715E', // 暗灰
176
+ dim: '#49483E', // 深灰
177
+ },
178
+ border: '#75715E', // 注释色
179
+ divider: '#49483E', // 深灰
180
+ code: {
181
+ background: '#272822', // 深灰背景
182
+ text: '#F8F8F2', // 前景色
183
+ keyword: '#F92672', // 粉红关键字
184
+ string: '#E6DB74', // 黄色字符串
185
+ function: '#A6E22E', // 绿色函数
186
+ comment: '#75715E', // 注释色
187
+ },
188
+ };
189
+ // 所有主题定义(颜色 + 元数据)
190
+ export const themeDefinitions = {
191
+ dark: {
192
+ metadata: {
193
+ name: 'dark',
194
+ displayName: '深色',
195
+ description: '默认深色主题,明亮的颜色适合深色终端背景',
196
+ category: 'dark',
197
+ previewColor: '#00D9FF',
198
+ author: 'built-in',
199
+ },
200
+ colors: darkTheme,
201
+ },
202
+ light: {
203
+ metadata: {
204
+ name: 'light',
205
+ displayName: '浅色',
206
+ description: '浅色主题,较深的颜色适合浅色终端背景',
207
+ category: 'light',
208
+ previewColor: '#0369A1',
209
+ author: 'built-in',
210
+ },
211
+ colors: lightTheme,
212
+ },
213
+ nord: {
214
+ metadata: {
215
+ name: 'nord',
216
+ displayName: '北欧冷色',
217
+ description: '冷色调护眼主题,适合长时间使用',
218
+ category: 'dark',
219
+ previewColor: '#88C0D0',
220
+ author: 'built-in',
221
+ },
222
+ colors: nordTheme,
223
+ },
224
+ dracula: {
225
+ metadata: {
226
+ name: 'dracula',
227
+ displayName: '德古拉暗色',
228
+ description: '高对比暗色主题,色彩丰富但不刺眼',
229
+ category: 'dark',
230
+ previewColor: '#BD93F9',
231
+ author: 'built-in',
232
+ },
233
+ colors: draculaTheme,
234
+ },
235
+ retro: {
236
+ metadata: {
237
+ name: 'retro',
238
+ displayName: '复古终端绿',
239
+ description: '经典终端荧光绿,致敬老派 hacker 风格',
240
+ category: 'dark',
241
+ previewColor: '#00FF00',
242
+ author: 'built-in',
243
+ },
244
+ colors: retroTheme,
245
+ },
246
+ contrast: {
247
+ metadata: {
248
+ name: 'contrast',
249
+ displayName: '高对比度',
250
+ description: '极高对比度,适合视力辅助和强光环境',
251
+ category: 'dark',
252
+ previewColor: '#FFFFFF',
253
+ author: 'built-in',
254
+ },
255
+ colors: contrastTheme,
256
+ },
257
+ monokai: {
258
+ metadata: {
259
+ name: 'monokai',
260
+ displayName: '经典编辑器',
261
+ description: 'Monokai 经典配色,开发者熟悉的选择',
262
+ category: 'dark',
263
+ previewColor: '#66D9EF',
264
+ author: 'built-in',
265
+ },
266
+ colors: monokaiTheme,
267
+ },
61
268
  };
62
- // 获取当前主题
269
+ // 向后兼容:导出纯颜色主题对象
270
+ export const themes = Object.fromEntries(Object.entries(themeDefinitions).map(([name, def]) => [name, def.colors]));
271
+ // 获取当前主题(按需加载策略)
63
272
  export function getCurrentTheme() {
64
273
  // 直接读取配置文件,避免循环依赖
65
274
  try {
@@ -67,15 +276,278 @@ export function getCurrentTheme() {
67
276
  if (fs.existsSync(configPath)) {
68
277
  const content = fs.readFileSync(configPath, 'utf-8');
69
278
  const config = JSON.parse(content);
70
- if (config.theme && themes[config.theme]) {
71
- return themes[config.theme];
279
+ const themeName = config.theme || 'dark';
280
+ // 1. 先检查是否为内置主题
281
+ if (themeName in themeDefinitions) {
282
+ return themeDefinitions[themeName].colors;
72
283
  }
284
+ // 2. 如果不是内置主题,加载自定义主题
285
+ const customTheme = loadCustomTheme(themeName);
286
+ if (customTheme) {
287
+ return customTheme.colors;
288
+ }
289
+ // 3. 加载失败,回退到默认主题
290
+ console.warn(`⚠ 主题 "${themeName}" 不存在,已回退到 dark 主题`);
73
291
  }
74
292
  }
75
293
  catch {
76
294
  // 忽略错误,返回默认主题
77
295
  }
78
- return themes.dark;
296
+ return themeDefinitions.dark.colors;
297
+ }
298
+ // 获取单个主题的元数据(支持自定义主题)
299
+ export function getThemeMetadata(name) {
300
+ // 先检查内置主题
301
+ if (name in themeDefinitions) {
302
+ return themeDefinitions[name].metadata;
303
+ }
304
+ // 加载自定义主题
305
+ const customTheme = loadCustomTheme(name);
306
+ return customTheme?.metadata || undefined;
307
+ }
308
+ // 获取所有主题的元数据列表(扫描所有主题)
309
+ export function getAllThemeMetadata() {
310
+ const customThemes = loadAllCustomThemes();
311
+ const allThemes = { ...themeDefinitions, ...customThemes };
312
+ return Object.values(allThemes).map((def) => def.metadata);
313
+ }
314
+ // 获取完整的主题定义(颜色 + 元数据)
315
+ export function getThemeDefinition(name) {
316
+ // 先检查内置主题
317
+ if (name in themeDefinitions) {
318
+ return themeDefinitions[name];
319
+ }
320
+ // 加载自定义主题
321
+ return loadCustomTheme(name) || undefined;
322
+ }
323
+ // 检查主题是否存在(支持自定义主题)
324
+ export function isValidTheme(name) {
325
+ // 先检查内置主题
326
+ if (name in themeDefinitions) {
327
+ return true;
328
+ }
329
+ // 检查自定义主题文件是否存在
330
+ const themePath = path.join(THEMES_DIR, `${name}.json`);
331
+ return fs.existsSync(themePath);
332
+ }
333
+ // 检查是否为内置主题
334
+ export function isBuiltinTheme(name) {
335
+ return name in themeDefinitions;
336
+ }
337
+ // ========== 自定义主题加载逻辑 ==========
338
+ /**
339
+ * 加载单个自定义主题(按需加载)
340
+ */
341
+ function loadCustomTheme(name) {
342
+ try {
343
+ const themePath = path.join(THEMES_DIR, `${name}.json`);
344
+ if (!fs.existsSync(themePath)) {
345
+ return null;
346
+ }
347
+ const content = fs.readFileSync(themePath, 'utf-8');
348
+ const theme = JSON.parse(content);
349
+ // 验证主题格式
350
+ if (!validateTheme(theme)) {
351
+ console.warn(`⚠ 主题 "${name}" 格式不正确,已跳过`);
352
+ return null;
353
+ }
354
+ return theme;
355
+ }
356
+ catch (error) {
357
+ console.warn(`⚠ 加载主题 "${name}" 失败: ${error.message}`);
358
+ return null;
359
+ }
360
+ }
361
+ /**
362
+ * 加载所有自定义主题(完整扫描)
363
+ */
364
+ function loadAllCustomThemes() {
365
+ const themes = {};
366
+ try {
367
+ // 确保主题目录存在
368
+ if (!fs.existsSync(THEMES_DIR)) {
369
+ return themes;
370
+ }
371
+ // 扫描所有 .json 文件
372
+ const files = fs.readdirSync(THEMES_DIR).filter((f) => f.endsWith('.json'));
373
+ for (const file of files) {
374
+ try {
375
+ const themePath = path.join(THEMES_DIR, file);
376
+ const content = fs.readFileSync(themePath, 'utf-8');
377
+ const theme = JSON.parse(content);
378
+ // 验证主题格式
379
+ if (validateTheme(theme)) {
380
+ themes[theme.metadata.name] = theme;
381
+ }
382
+ }
383
+ catch (error) {
384
+ // 单个主题加载失败不影响其他主题
385
+ continue;
386
+ }
387
+ }
388
+ }
389
+ catch (error) {
390
+ // 目录读取失败,返回空对象
391
+ }
392
+ return themes;
393
+ }
394
+ /**
395
+ * 验证主题格式是否正确
396
+ */
397
+ export function validateTheme(theme) {
398
+ try {
399
+ // 检查基本结构
400
+ if (!theme || typeof theme !== 'object') {
401
+ return false;
402
+ }
403
+ // 检查 metadata 必填字段
404
+ if (!theme.metadata ||
405
+ typeof theme.metadata !== 'object' ||
406
+ !theme.metadata.name ||
407
+ !theme.metadata.displayName ||
408
+ !theme.metadata.category) {
409
+ return false;
410
+ }
411
+ // 检查 category 值
412
+ if (theme.metadata.category !== 'dark' && theme.metadata.category !== 'light') {
413
+ return false;
414
+ }
415
+ // 检查 colors 必填字段
416
+ if (!theme.colors ||
417
+ typeof theme.colors !== 'object' ||
418
+ !theme.colors.primary ||
419
+ !theme.colors.success ||
420
+ !theme.colors.error ||
421
+ !theme.colors.text ||
422
+ !theme.colors.text.primary) {
423
+ return false;
424
+ }
425
+ // 验证颜色格式(十六进制)
426
+ const hexRegex = /^#[0-9A-Fa-f]{6}$/;
427
+ const requiredColors = [
428
+ theme.colors.primary,
429
+ theme.colors.secondary,
430
+ theme.colors.accent,
431
+ theme.colors.success,
432
+ theme.colors.error,
433
+ theme.colors.warning,
434
+ theme.colors.info,
435
+ theme.colors.text.primary,
436
+ theme.colors.border,
437
+ ];
438
+ for (const color of requiredColors) {
439
+ if (!color || !hexRegex.test(color)) {
440
+ return false;
441
+ }
442
+ }
443
+ return true;
444
+ }
445
+ catch {
446
+ return false;
447
+ }
448
+ }
449
+ /**
450
+ * 获取主题验证的详细错误信息
451
+ */
452
+ export function validateThemeWithDetails(theme) {
453
+ const errors = [];
454
+ // 检查基本结构
455
+ if (!theme || typeof theme !== 'object') {
456
+ errors.push('主题必须是一个 JSON 对象');
457
+ return { valid: false, errors };
458
+ }
459
+ // 检查 metadata
460
+ if (!theme.metadata || typeof theme.metadata !== 'object') {
461
+ errors.push('缺少 metadata 字段');
462
+ }
463
+ else {
464
+ if (!theme.metadata.name)
465
+ errors.push('缺少 metadata.name');
466
+ if (!theme.metadata.displayName)
467
+ errors.push('缺少 metadata.displayName');
468
+ if (!theme.metadata.category) {
469
+ errors.push('缺少 metadata.category');
470
+ }
471
+ else if (theme.metadata.category !== 'dark' && theme.metadata.category !== 'light') {
472
+ errors.push('metadata.category 必须是 "dark" 或 "light"');
473
+ }
474
+ }
475
+ // 检查 colors
476
+ if (!theme.colors || typeof theme.colors !== 'object') {
477
+ errors.push('缺少 colors 字段');
478
+ }
479
+ else {
480
+ const hexRegex = /^#[0-9A-Fa-f]{6}$/;
481
+ // 检查必填颜色字段
482
+ const colorFields = [
483
+ 'primary',
484
+ 'secondary',
485
+ 'accent',
486
+ 'success',
487
+ 'error',
488
+ 'warning',
489
+ 'info',
490
+ 'border',
491
+ 'divider',
492
+ ];
493
+ for (const field of colorFields) {
494
+ if (!theme.colors[field]) {
495
+ errors.push(`缺少 colors.${field}`);
496
+ }
497
+ else if (!hexRegex.test(theme.colors[field])) {
498
+ errors.push(`colors.${field} 格式错误(应为 #RRGGBB 格式)`);
499
+ }
500
+ }
501
+ // 检查 text 字段
502
+ if (!theme.colors.text || typeof theme.colors.text !== 'object') {
503
+ errors.push('缺少 colors.text');
504
+ }
505
+ else {
506
+ const textFields = ['primary', 'secondary', 'muted', 'dim'];
507
+ for (const field of textFields) {
508
+ if (!theme.colors.text[field]) {
509
+ errors.push(`缺少 colors.text.${field}`);
510
+ }
511
+ else if (!hexRegex.test(theme.colors.text[field])) {
512
+ errors.push(`colors.text.${field} 格式错误(应为 #RRGGBB 格式)`);
513
+ }
514
+ }
515
+ }
516
+ // 检查 code 字段
517
+ if (!theme.colors.code || typeof theme.colors.code !== 'object') {
518
+ errors.push('缺少 colors.code');
519
+ }
520
+ else {
521
+ const codeFields = ['background', 'text', 'keyword', 'string', 'function', 'comment'];
522
+ for (const field of codeFields) {
523
+ if (!theme.colors.code[field]) {
524
+ errors.push(`缺少 colors.code.${field}`);
525
+ }
526
+ else if (!hexRegex.test(theme.colors.code[field])) {
527
+ errors.push(`colors.code.${field} 格式错误(应为 #RRGGBB 格式)`);
528
+ }
529
+ }
530
+ }
531
+ }
532
+ return { valid: errors.length === 0, errors };
533
+ }
534
+ /**
535
+ * 创建主题模板
536
+ */
537
+ export function createThemeTemplate(name, displayName, category) {
538
+ // 根据类别选择基础配色
539
+ const baseTheme = category === 'dark' ? darkTheme : lightTheme;
540
+ return {
541
+ metadata: {
542
+ name,
543
+ displayName,
544
+ description: '自定义主题',
545
+ category,
546
+ previewColor: baseTheme.primary,
547
+ author: os.userInfo().username || 'user',
548
+ },
549
+ colors: baseTheme,
550
+ };
79
551
  }
80
552
  // 向后兼容:导出默认主题
81
553
  export const theme = darkTheme;
@@ -1,7 +1,16 @@
1
+ /**
2
+ * 原生控制台输出工具函数
3
+ * 用于不需要 Ink 的场景,避免清屏和性能问题
4
+ */
5
+ export declare const MIN_COMMAND_BOX_WIDTH = 20;
1
6
  /**
2
7
  * 计算字符串的显示宽度(中文占2个宽度)
3
8
  */
4
9
  export declare function getDisplayWidth(str: string): number;
10
+ /**
11
+ * 将长文本按指定宽度自动换行(智能分词)
12
+ */
13
+ export declare function wrapText(text: string, maxWidth: number): string[];
5
14
  /**
6
15
  * 绘制命令框(原生版本)
7
16
  */
@@ -4,6 +4,8 @@ import { getCurrentTheme } from '../ui/theme.js';
4
4
  * 原生控制台输出工具函数
5
5
  * 用于不需要 Ink 的场景,避免清屏和性能问题
6
6
  */
7
+ // 命令框最小宽度(统一配置)
8
+ export const MIN_COMMAND_BOX_WIDTH = 20;
7
9
  // 获取当前主题颜色
8
10
  function getColors() {
9
11
  const theme = getCurrentTheme();
@@ -33,22 +35,83 @@ export function getDisplayWidth(str) {
33
35
  }
34
36
  return width;
35
37
  }
38
+ /**
39
+ * 将长文本按指定宽度自动换行(智能分词)
40
+ */
41
+ export function wrapText(text, maxWidth) {
42
+ if (getDisplayWidth(text) <= maxWidth) {
43
+ return [text];
44
+ }
45
+ const lines = [];
46
+ const words = text.split(' ');
47
+ let currentLine = '';
48
+ for (const word of words) {
49
+ const testLine = currentLine ? `${currentLine} ${word}` : word;
50
+ const testWidth = getDisplayWidth(testLine);
51
+ if (testWidth <= maxWidth) {
52
+ currentLine = testLine;
53
+ }
54
+ else {
55
+ // 如果当前行有内容,先保存
56
+ if (currentLine) {
57
+ lines.push(currentLine);
58
+ currentLine = word;
59
+ }
60
+ else {
61
+ // 单个单词太长,强制拆分
62
+ let remaining = word;
63
+ while (remaining.length > 0) {
64
+ let chunk = '';
65
+ for (const char of remaining) {
66
+ if (getDisplayWidth(chunk + char) <= maxWidth) {
67
+ chunk += char;
68
+ }
69
+ else {
70
+ break;
71
+ }
72
+ }
73
+ if (chunk.length === 0) {
74
+ // 防止死循环,至少取一个字符
75
+ chunk = remaining[0];
76
+ }
77
+ lines.push(chunk);
78
+ remaining = remaining.slice(chunk.length);
79
+ }
80
+ }
81
+ }
82
+ }
83
+ // 添加最后一行
84
+ if (currentLine) {
85
+ lines.push(currentLine);
86
+ }
87
+ return lines;
88
+ }
36
89
  /**
37
90
  * 绘制命令框(原生版本)
38
91
  */
39
92
  export function drawCommandBox(command, title = '生成命令') {
40
93
  const colors = getColors();
41
- const lines = command.split('\n');
94
+ // 获取终端宽度,限制最大宽度
95
+ const termWidth = process.stdout.columns || 80;
42
96
  const titleWidth = getDisplayWidth(title);
43
- const maxContentWidth = Math.max(...lines.map((l) => getDisplayWidth(l)));
44
- const boxWidth = Math.max(maxContentWidth + 4, titleWidth + 6, 20);
97
+ // 计算最大内容宽度(终端宽度 - 边框和内边距)
98
+ const maxContentWidth = termWidth - 6; // 减去 '│ ' 和 ' │' 以及一些余量
99
+ // 处理命令换行
100
+ const originalLines = command.split('\n');
101
+ const wrappedLines = [];
102
+ for (const line of originalLines) {
103
+ wrappedLines.push(...wrapText(line, maxContentWidth));
104
+ }
105
+ // 计算实际使用的宽度
106
+ const actualMaxWidth = Math.max(...wrappedLines.map((l) => getDisplayWidth(l)), titleWidth);
107
+ const boxWidth = Math.max(MIN_COMMAND_BOX_WIDTH, Math.min(actualMaxWidth + 4, termWidth - 2));
45
108
  const topPadding = boxWidth - titleWidth - 5;
46
- const topBorder = '┌─ ' + title + ' ' + '─'.repeat(topPadding) + '┐';
109
+ const topBorder = '┌─ ' + title + ' ' + '─'.repeat(Math.max(0, topPadding)) + '┐';
47
110
  const bottomBorder = '└' + '─'.repeat(boxWidth - 2) + '┘';
48
111
  console.log(chalk.hex(colors.warning)(topBorder));
49
- for (const line of lines) {
112
+ for (const line of wrappedLines) {
50
113
  const lineWidth = getDisplayWidth(line);
51
- const padding = ' '.repeat(boxWidth - lineWidth - 4);
114
+ const padding = ' '.repeat(Math.max(0, boxWidth - lineWidth - 4));
52
115
  console.log(chalk.hex(colors.warning)('│ ') + chalk.hex(colors.primary)(line) + padding + chalk.hex(colors.warning)(' │'));
53
116
  }
54
117
  console.log(chalk.hex(colors.warning)(bottomBorder));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yivan-lab/pretty-please",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "AI 驱动的命令行工具,将自然语言转换为可执行的 Shell 命令",
5
5
  "type": "module",
6
6
  "bin": {