@playcraft/cli 0.0.53 → 0.0.56

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.
Files changed (242) hide show
  1. package/README.md +21 -0
  2. package/REMIX.md +103 -19
  3. package/dist/atom-tree/fusion-hints.js +8 -3
  4. package/dist/atom-tree/fusion-intent.js +62 -0
  5. package/dist/atom-tree/selection.js +3 -1
  6. package/dist/commands/create.js +439 -159
  7. package/dist/commands/image.js +79 -69
  8. package/dist/commands/levels.js +29 -0
  9. package/dist/commands/patterns.js +75 -0
  10. package/dist/commands/remix.js +47 -0
  11. package/dist/commands/skills.js +48 -17
  12. package/dist/index.js +13 -6
  13. package/dist/remix/clone-source.js +208 -0
  14. package/dist/remix/clone.js +255 -0
  15. package/dist/scaffold/templates.js +35 -0
  16. package/dist/utils/agent-api-client.js +10 -3
  17. package/dist/utils/remix-image-attachments.js +4 -32
  18. package/dist/utils/sandbox-path.js +20 -0
  19. package/package.json +3 -3
  20. package/project-template/.claude/agents/creative-director.md +114 -0
  21. package/project-template/.claude/agents/developer.md +5 -2
  22. package/project-template/.claude/agents/game-designer.md +41 -20
  23. package/project-template/.claude/agents/refs/README.md +8 -0
  24. package/project-template/.claude/agents/refs/creative-director-guide.md +35 -0
  25. package/project-template/.claude/agents/refs/creative-director-kickoff-guide.md +52 -0
  26. package/project-template/.claude/agents/refs/creative-discovery-guide.md +76 -0
  27. package/project-template/.claude/agents/refs/creative-north-star-examples.md +82 -0
  28. package/project-template/.claude/agents/refs/developer-dev-handoff.md +1 -1
  29. package/project-template/.claude/agents/refs/game-designer-fusion-depth-playbook.md +28 -0
  30. package/project-template/.claude/agents/refs/game-designer-gameplay-sufficiency.md +4 -4
  31. package/project-template/.claude/agents/reviewer.md +11 -0
  32. package/project-template/.claude/hooks/README.md +12 -12
  33. package/project-template/.claude/hooks/block-developer-preflight.mjs +49 -13
  34. package/project-template/.claude/hooks/block-ring1-tasklist.mjs +1 -1
  35. package/project-template/.claude/hooks/user-prompt.mjs +3 -13
  36. package/project-template/.claude/hooks/validate-workflow-stop.mjs +209 -12
  37. package/project-template/.claude/settings.json +2 -2
  38. package/project-template/.claude/skills/playcraft-playable-optimization/SKILL.md +20 -20
  39. package/project-template/.claude/skills/session-analyzer/scripts/resolve-session.mjs +0 -1
  40. package/project-template/.claude/snapshot.mjs +0 -1
  41. package/project-template/.codebuddy/settings.json +1 -1
  42. package/project-template/.cursor/rules/playcraft-orchestrator.mdc +1 -1
  43. package/project-template/CLAUDE.md +9 -9
  44. package/project-template/docs/project-state.md +1 -1
  45. package/project-template/docs/team/agent-conduct.md +18 -18
  46. package/project-template/docs/team/collaboration.md +19 -7
  47. package/project-template/docs/team/workflow-changelog.md +15 -0
  48. package/project-template/fusion-patterns/catalog.json +77 -0
  49. package/project-template/package.json +2 -0
  50. package/project-template/skills/_shared/scripts/create-playable.mjs +31 -11
  51. package/project-template/skills/_shared/scripts/dev-start.mjs +3 -3
  52. package/project-template/skills/_shared/scripts/disclosure-config.mjs +65 -39
  53. package/project-template/skills/_shared/scripts/harness-smoke.mjs +3 -1
  54. package/project-template/skills/_shared/scripts/lib/fusion-intent.mjs +88 -0
  55. package/project-template/skills/_shared/scripts/lib/skill-fidelity.mjs +185 -0
  56. package/project-template/skills/_shared/scripts/slice-set.mjs +29 -2
  57. package/project-template/skills/_shared/scripts/state-get.mjs +124 -7
  58. package/project-template/skills/_shared/scripts/state-store.mjs +25 -2
  59. package/project-template/skills/_shared/scripts/validate-level-sketch.mjs +444 -0
  60. package/project-template/skills/_shared/scripts/verify-design-consistency.mjs +728 -0
  61. package/project-template/skills/playable-record/scripts/lib/dev-server.mjs +46 -11
  62. package/project-template/skills/playable-record/scripts/record-playable.mjs +2 -1
  63. package/project-template/skills/playable-record/scripts/verify-contract.mjs +4 -4
  64. package/project-template/skills/playable-record/scripts/verify-firstwin.mjs +3 -3
  65. package/project-template/skills/playable-record/scripts/verify-fusion.mjs +37 -2
  66. package/project-template/skills/playable-record/scripts/verify-slice.mjs +2 -2
  67. package/project-template/templates/design-brief.template.md +34 -0
  68. package/project-template/templates/design-methodology.md +1 -2
  69. package/project-template/templates/design.template.md +65 -0
  70. package/project-template/templates/developer-log.template.md +12 -1
  71. package/project-template/templates/five-axis-framework.md +12 -0
  72. package/project-template/templates/game-designer-creative-process.template.md +99 -0
  73. package/project-template/templates/level-sketch.template.json +56 -0
  74. package/project-template/templates/north-star.template.md +43 -0
  75. package/project-template/templates/project-state.template.md +1 -1
  76. package/project-template/vite.config.ts +4 -5
  77. package/project-templates/phaser/.claude/agents/creative-director.md +114 -0
  78. package/project-templates/phaser/.claude/agents/game-designer.md +41 -20
  79. package/project-templates/phaser/.claude/agents/refs/creative-director-guide.md +35 -0
  80. package/project-templates/phaser/.claude/agents/refs/creative-director-kickoff-guide.md +52 -0
  81. package/project-templates/phaser/.claude/agents/refs/creative-discovery-guide.md +76 -0
  82. package/project-templates/phaser/.claude/agents/refs/creative-north-star-examples.md +82 -0
  83. package/project-templates/phaser/.claude/agents/refs/developer-dev-handoff.md +1 -1
  84. package/project-templates/phaser/.claude/agents/refs/game-designer-fusion-depth-playbook.md +28 -0
  85. package/project-templates/phaser/.claude/agents/reviewer.md +11 -0
  86. package/project-templates/phaser/.claude/hooks/block-ring1-tasklist.mjs +2 -2
  87. package/project-templates/phaser/.claude/hooks/user-prompt.mjs +17 -18
  88. package/project-templates/phaser/.claude/hooks/validate-workflow-stop.mjs +209 -12
  89. package/project-templates/phaser/.claude/skills/playcraft-playable-optimization/SKILL.md +20 -20
  90. package/project-templates/phaser/.cursor/rules/playcraft-orchestrator.mdc +1 -1
  91. package/project-templates/phaser/CLAUDE.md +88 -54
  92. package/project-templates/phaser/docs/DESIGN.md +25 -0
  93. package/project-templates/phaser/docs/project-state.md +1 -1
  94. package/project-templates/phaser/docs/team/collaboration.md +19 -7
  95. package/project-templates/phaser/docs/team/workflow-changelog.md +14 -0
  96. package/project-templates/phaser/fusion-patterns/catalog.json +77 -0
  97. package/project-templates/phaser/package.json +3 -1
  98. package/project-templates/phaser/skills/_shared/scripts/dev-start.mjs +3 -3
  99. package/project-templates/phaser/skills/_shared/scripts/disclosure-config.mjs +65 -39
  100. package/project-templates/phaser/skills/_shared/scripts/harness-smoke.mjs +3 -1
  101. package/project-templates/phaser/skills/_shared/scripts/lib/fusion-intent.mjs +88 -0
  102. package/project-templates/phaser/skills/_shared/scripts/lib/skill-fidelity.mjs +185 -0
  103. package/project-templates/phaser/skills/_shared/scripts/state-get.mjs +124 -7
  104. package/project-templates/phaser/skills/_shared/scripts/state-store.mjs +25 -2
  105. package/project-templates/phaser/skills/_shared/scripts/validate-level-sketch.mjs +444 -0
  106. package/project-templates/phaser/skills/_shared/scripts/verify-design-consistency.mjs +728 -0
  107. package/project-templates/phaser/skills/playable-record/scripts/lib/dev-server.mjs +46 -11
  108. package/project-templates/phaser/skills/playable-record/scripts/record-playable.mjs +2 -1
  109. package/project-templates/phaser/skills/playable-record/scripts/verify-contract.mjs +4 -4
  110. package/project-templates/phaser/skills/playable-record/scripts/verify-firstwin.mjs +3 -3
  111. package/project-templates/phaser/skills/playable-record/scripts/verify-fusion.mjs +37 -2
  112. package/project-templates/phaser/skills/playable-record/scripts/verify-slice.mjs +2 -2
  113. package/project-templates/phaser/templates/design-brief.template.md +42 -11
  114. package/project-templates/phaser/templates/design-methodology.md +1 -2
  115. package/project-templates/phaser/templates/design.template.md +77 -0
  116. package/project-templates/phaser/templates/five-axis-framework.md +50 -1
  117. package/project-templates/phaser/templates/game-designer-creative-process.template.md +99 -0
  118. package/project-templates/phaser/templates/level-sketch.template.json +56 -0
  119. package/project-templates/phaser/templates/north-star.template.md +43 -0
  120. package/project-templates/phaser/templates/project-state.template.md +1 -1
  121. package/project-templates/phaser/vite.config.ts +4 -5
  122. package/project-templates/playable-template-phaser/.playcraftignore +10 -0
  123. package/project-templates/playable-template-phaser/CLAUDE.md +178 -0
  124. package/project-templates/playable-template-phaser/PLAYCRAFT-MIGRATION.md +328 -0
  125. package/project-templates/playable-template-phaser/README.md +381 -0
  126. package/project-templates/playable-template-phaser/assets/brain.png +0 -0
  127. package/project-templates/playable-template-phaser/assets/button.png +0 -0
  128. package/project-templates/playable-template-phaser/assets/interact.png +0 -0
  129. package/project-templates/playable-template-phaser/assets/logo.webp +0 -0
  130. package/project-templates/playable-template-phaser/assets/warn_brain.png +0 -0
  131. package/project-templates/playable-template-phaser/builds.config.js +12 -0
  132. package/project-templates/playable-template-phaser/globals.d.ts +2 -0
  133. package/project-templates/playable-template-phaser/index.html +12 -0
  134. package/project-templates/playable-template-phaser/package.json +45 -0
  135. package/project-templates/playable-template-phaser/scripts/base64-to-mp3.js +340 -0
  136. package/project-templates/playable-template-phaser/scripts/convert-to-webp.js +370 -0
  137. package/project-templates/playable-template-phaser/src/Game.ts +154 -0
  138. package/project-templates/playable-template-phaser/src/config/LoadingConfig.ts +40 -0
  139. package/project-templates/playable-template-phaser/src/config/ThemeConfig.ts +81 -0
  140. package/project-templates/playable-template-phaser/src/index.css +18 -0
  141. package/project-templates/playable-template-phaser/src/index.html +9 -0
  142. package/project-templates/playable-template-phaser/src/index.ts +30 -0
  143. package/project-templates/playable-template-phaser/src/json5.d.ts +4 -0
  144. package/project-templates/playable-template-phaser/src/managers/AudioManager.ts +559 -0
  145. package/project-templates/playable-template-phaser/src/scenes/MainScene.ts +301 -0
  146. package/project-templates/playable-template-phaser/src/scenes/PreloadScene.ts +256 -0
  147. package/project-templates/playable-template-phaser/src/theme/README.md +194 -0
  148. package/project-templates/playable-template-phaser/src/theme/blue-theme/assets/.gitkeep +24 -0
  149. package/project-templates/playable-template-phaser/src/theme/blue-theme/index.ts +27 -0
  150. package/project-templates/playable-template-phaser/src/theme/blue-theme/theme.data.json5 +19 -0
  151. package/project-templates/playable-template-phaser/src/theme/green-theme/assets/.gitkeep +3 -0
  152. package/project-templates/playable-template-phaser/src/theme/green-theme/index.ts +20 -0
  153. package/project-templates/playable-template-phaser/src/theme/green-theme/theme.data.json5 +19 -0
  154. package/project-templates/playable-template-phaser/src/theme/index.ts +1 -0
  155. package/project-templates/playable-template-phaser/src/theme/red-theme/assets/.gitkeep +3 -0
  156. package/project-templates/playable-template-phaser/src/theme/red-theme/index.ts +20 -0
  157. package/project-templates/playable-template-phaser/src/theme/red-theme/theme.data.json5 +19 -0
  158. package/project-templates/playable-template-phaser/src/theme/theme.metadata.md +60 -0
  159. package/project-templates/playable-template-phaser/src/theme/theme.schema.json5 +149 -0
  160. package/project-templates/playable-template-phaser/src/ui/BrainAgeUI.ts +681 -0
  161. package/project-templates/playable-template-phaser/src/ui/CountdownUI.ts +565 -0
  162. package/project-templates/playable-template-phaser/src/ui/PlayButton.ts +51 -0
  163. package/project-templates/playable-template-phaser/src/ui/ScoreUI.ts +207 -0
  164. package/project-templates/playable-template-phaser/src/ui/SuccessUI.ts +43 -0
  165. package/project-templates/playable-template-phaser/src/ui/index.ts +15 -0
  166. package/project-templates/playable-template-phaser/tsconfig.json +26 -0
  167. package/project-templates/playable-template-phaser/vite.config.ts +64 -0
  168. package/project-templates/playable-template-phaser//346/226/260/346/211/213/345/274/200/345/217/221/346/214/207/345/215/227.md +209 -0
  169. package/project-templates/playable-template-three/README.md +795 -0
  170. package/project-templates/playable-template-three/assets/Stork.glb +0 -0
  171. package/project-templates/playable-template-three/assets/button.png +0 -0
  172. package/project-templates/playable-template-three/assets/logo.webp +0 -0
  173. package/project-templates/playable-template-three/build.json +7 -0
  174. package/project-templates/playable-template-three/builds.config.example.js +151 -0
  175. package/project-templates/playable-template-three/builds.config.js +158 -0
  176. package/project-templates/playable-template-three/package.json +36 -0
  177. package/project-templates/playable-template-three/scripts/convert-to-webp.js +370 -0
  178. package/project-templates/playable-template-three/scripts/theme-builds.js +514 -0
  179. package/project-templates/playable-template-three/src/Game.ts +215 -0
  180. package/project-templates/playable-template-three/src/config/LoadingConfig.ts +27 -0
  181. package/project-templates/playable-template-three/src/config/ThemeConfig.ts +36 -0
  182. package/project-templates/playable-template-three/src/global.d.ts +108 -0
  183. package/project-templates/playable-template-three/src/index.css +18 -0
  184. package/project-templates/playable-template-three/src/index.html +9 -0
  185. package/project-templates/playable-template-three/src/index.ts +20 -0
  186. package/project-templates/playable-template-three/src/managers/AudioManager.ts +538 -0
  187. package/project-templates/playable-template-three/src/scenes/PreloadScene.ts +305 -0
  188. package/project-templates/playable-template-three/src/theme/README.md +225 -0
  189. package/project-templates/playable-template-three/src/theme/blue-theme/assets/.gitkeep +28 -0
  190. package/project-templates/playable-template-three/src/theme/blue-theme/index.ts +45 -0
  191. package/project-templates/playable-template-three/src/theme/current.ts +12 -0
  192. package/project-templates/playable-template-three/src/theme/green-theme/assets/.gitkeep +3 -0
  193. package/project-templates/playable-template-three/src/theme/green-theme/index.ts +34 -0
  194. package/project-templates/playable-template-three/src/theme/red-theme/assets/.gitkeep +3 -0
  195. package/project-templates/playable-template-three/src/theme/red-theme/index.ts +34 -0
  196. package/project-templates/playable-template-three/tsconfig.json +24 -0
  197. package/project-templates/threejs/.claude/agents/creative-director.md +114 -0
  198. package/project-templates/threejs/.claude/agents/game-designer.md +41 -20
  199. package/project-templates/threejs/.claude/agents/refs/creative-director-guide.md +35 -0
  200. package/project-templates/threejs/.claude/agents/refs/creative-director-kickoff-guide.md +52 -0
  201. package/project-templates/threejs/.claude/agents/refs/creative-discovery-guide.md +76 -0
  202. package/project-templates/threejs/.claude/agents/refs/creative-north-star-examples.md +82 -0
  203. package/project-templates/threejs/.claude/agents/refs/developer-dev-handoff.md +1 -1
  204. package/project-templates/threejs/.claude/agents/refs/game-designer-fusion-depth-playbook.md +28 -0
  205. package/project-templates/threejs/.claude/agents/reviewer.md +11 -0
  206. package/project-templates/threejs/.claude/hooks/block-ring1-tasklist.mjs +2 -2
  207. package/project-templates/threejs/.claude/hooks/user-prompt.mjs +17 -18
  208. package/project-templates/threejs/.claude/hooks/validate-workflow-stop.mjs +209 -12
  209. package/project-templates/threejs/.claude/skills/playcraft-playable-optimization/SKILL.md +20 -20
  210. package/project-templates/threejs/.cursor/rules/playcraft-orchestrator.mdc +1 -1
  211. package/project-templates/threejs/CLAUDE.md +88 -54
  212. package/project-templates/threejs/docs/DESIGN.md +25 -0
  213. package/project-templates/threejs/docs/project-state.md +1 -1
  214. package/project-templates/threejs/docs/team/collaboration.md +19 -7
  215. package/project-templates/threejs/docs/team/workflow-changelog.md +14 -0
  216. package/project-templates/threejs/fusion-patterns/catalog.json +77 -0
  217. package/project-templates/threejs/package.json +3 -1
  218. package/project-templates/threejs/skills/_shared/scripts/dev-start.mjs +3 -3
  219. package/project-templates/threejs/skills/_shared/scripts/disclosure-config.mjs +65 -39
  220. package/project-templates/threejs/skills/_shared/scripts/harness-smoke.mjs +3 -1
  221. package/project-templates/threejs/skills/_shared/scripts/lib/fusion-intent.mjs +88 -0
  222. package/project-templates/threejs/skills/_shared/scripts/lib/skill-fidelity.mjs +185 -0
  223. package/project-templates/threejs/skills/_shared/scripts/state-get.mjs +124 -7
  224. package/project-templates/threejs/skills/_shared/scripts/state-store.mjs +25 -2
  225. package/project-templates/threejs/skills/_shared/scripts/validate-level-sketch.mjs +444 -0
  226. package/project-templates/threejs/skills/_shared/scripts/verify-design-consistency.mjs +728 -0
  227. package/project-templates/threejs/skills/playable-record/scripts/lib/dev-server.mjs +46 -11
  228. package/project-templates/threejs/skills/playable-record/scripts/record-playable.mjs +2 -1
  229. package/project-templates/threejs/skills/playable-record/scripts/verify-contract.mjs +4 -4
  230. package/project-templates/threejs/skills/playable-record/scripts/verify-firstwin.mjs +3 -3
  231. package/project-templates/threejs/skills/playable-record/scripts/verify-fusion.mjs +37 -2
  232. package/project-templates/threejs/skills/playable-record/scripts/verify-slice.mjs +2 -2
  233. package/project-templates/threejs/templates/design-brief.template.md +42 -11
  234. package/project-templates/threejs/templates/design-methodology.md +1 -2
  235. package/project-templates/threejs/templates/design.template.md +77 -0
  236. package/project-templates/threejs/templates/five-axis-framework.md +50 -1
  237. package/project-templates/threejs/templates/game-designer-creative-process.template.md +99 -0
  238. package/project-templates/threejs/templates/level-sketch.template.json +56 -0
  239. package/project-templates/threejs/templates/north-star.template.md +43 -0
  240. package/project-templates/threejs/templates/project-state.template.md +1 -1
  241. package/project-templates/threejs/vite.config.ts +4 -5
  242. package/project-template/docs/DESIGN.md +0 -65
@@ -0,0 +1,370 @@
1
+ /**
2
+ * 图片转 WebP 格式脚本
3
+ *
4
+ * 使用方法:
5
+ * node scripts/convert-to-webp.js // 默认质量 100,保留原图
6
+ * node scripts/convert-to-webp.js 90 // 质量 90
7
+ * node scripts/convert-to-webp.js --quality=80 // 质量 80
8
+ * node scripts/convert-to-webp.js -q 90 --delete-original // 质量 90,删除原图
9
+ * node scripts/convert-to-webp.js --dry-run // 预览模式
10
+ * node scripts/convert-to-webp.js --force // 强制覆盖
11
+ * node scripts/convert-to-webp.js --dir=./public/images // 指定目录
12
+ * node scripts/convert-to-webp.js --help // 显示帮助
13
+ */
14
+
15
+ const webp = require('webp-converter');
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const readline = require('readline');
19
+
20
+ // 授予权限
21
+ webp.grant_permission();
22
+
23
+ // 默认配置
24
+ const DEFAULT_CONFIG = {
25
+ dir: './src/assets',
26
+ quality: 100,
27
+ deleteOriginal: false,
28
+ force: false,
29
+ isDryRun: false
30
+ };
31
+
32
+ const SUPPORTED_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.jfif'];
33
+
34
+ /**
35
+ * 解析命令行参数
36
+ */
37
+ function parseArgs() {
38
+ const args = process.argv.slice(2);
39
+ const config = { ...DEFAULT_CONFIG };
40
+
41
+ // 第一个参数如果是数字,视为质量值
42
+ if (args[0] && !args[0].startsWith('-') && !isNaN(args[0])) {
43
+ config.quality = parseInt(args[0]);
44
+ }
45
+
46
+ args.forEach((arg, index) => {
47
+ if (arg === '--dry-run') {
48
+ config.isDryRun = true;
49
+ } else if (arg === '--delete-original' || arg === '-d') {
50
+ config.deleteOriginal = true;
51
+ } else if (arg === '--force' || arg === '-f') {
52
+ config.force = true;
53
+ } else if (arg.startsWith('--quality=')) {
54
+ config.quality = parseInt(arg.split('=')[1]) || DEFAULT_CONFIG.quality;
55
+ } else if (arg === '-q' || arg === '--quality') {
56
+ const nextArg = args[index + 1];
57
+ if (nextArg && !isNaN(nextArg)) {
58
+ config.quality = parseInt(nextArg);
59
+ }
60
+ } else if (arg.startsWith('--dir=')) {
61
+ config.dir = arg.split('=')[1];
62
+ }
63
+ });
64
+
65
+ // 质量范围检查
66
+ if (config.quality < 0 || config.quality > 100) {
67
+ console.error('❌ 质量参数必须在 0-100 之间');
68
+ process.exit(1);
69
+ }
70
+
71
+ return config;
72
+ }
73
+
74
+ // 统计信息
75
+ let stats = {
76
+ total: 0,
77
+ success: 0,
78
+ failed: 0,
79
+ skipped: 0,
80
+ deleted: 0,
81
+ originalSize: 0,
82
+ convertedSize: 0
83
+ };
84
+
85
+ /**
86
+ * 递归获取目录下所有图片文件
87
+ */
88
+ function getAllImageFiles(dir, fileList = []) {
89
+ if (!fs.existsSync(dir)) {
90
+ return fileList;
91
+ }
92
+
93
+ const files = fs.readdirSync(dir);
94
+
95
+ files.forEach(file => {
96
+ const filePath = path.join(dir, file);
97
+ const stat = fs.statSync(filePath);
98
+
99
+ if (stat.isDirectory()) {
100
+ getAllImageFiles(filePath, fileList);
101
+ } else {
102
+ const ext = path.extname(file).toLowerCase();
103
+ if (SUPPORTED_EXTENSIONS.includes(ext)) {
104
+ fileList.push(filePath);
105
+ }
106
+ }
107
+ });
108
+
109
+ return fileList;
110
+ }
111
+
112
+ /**
113
+ * 格式化文件大小
114
+ */
115
+ function formatSize(bytes) {
116
+ if (bytes === 0) return '0 B';
117
+ const k = 1024;
118
+ const sizes = ['B', 'KB', 'MB', 'GB'];
119
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
120
+ return (bytes / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i];
121
+ }
122
+
123
+ /**
124
+ * 询问用户确认
125
+ */
126
+ function askConfirmation(question) {
127
+ const rl = readline.createInterface({
128
+ input: process.stdin,
129
+ output: process.stdout
130
+ });
131
+
132
+ return new Promise(resolve => {
133
+ rl.question(question, answer => {
134
+ rl.close();
135
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
136
+ });
137
+ });
138
+ }
139
+
140
+ /**
141
+ * 转换单个图片
142
+ */
143
+ async function convertImage(inputPath, config) {
144
+ const outputPath = inputPath.replace(/\.(png|jpg|jpeg|jfif)$/i, '.webp');
145
+
146
+ // 如果已经是 webp 格式,跳过
147
+ if (inputPath.endsWith('.webp')) {
148
+ console.log(`⏭️ 跳过: ${inputPath} (已是 WebP 格式)`);
149
+ stats.skipped++;
150
+ return;
151
+ }
152
+
153
+ // 如果输出文件已存在,直接覆盖
154
+ if (fs.existsSync(outputPath)) {
155
+ if (config.isDryRun) {
156
+ console.log(`🔍 [预览] 覆盖: ${outputPath}`);
157
+ } else {
158
+ console.log(`⚠️ 覆盖: ${outputPath}`);
159
+ }
160
+ }
161
+
162
+ try {
163
+ const originalSize = fs.statSync(inputPath).size;
164
+ stats.originalSize += originalSize;
165
+
166
+ if (config.isDryRun) {
167
+ console.log(`🔍 [预览] ${inputPath} -> ${outputPath}`);
168
+ console.log(` 原始大小: ${formatSize(originalSize)}`);
169
+ if (config.deleteOriginal) {
170
+ console.log(` ⚠️ 将删除原图`);
171
+ }
172
+ stats.total++;
173
+ return;
174
+ }
175
+
176
+ console.log(`🔄 转换中: ${inputPath}`);
177
+
178
+ // 转换图片
179
+ const result = await webp.cwebp(
180
+ inputPath,
181
+ outputPath,
182
+ `-q ${config.quality}`,
183
+ '-v'
184
+ );
185
+
186
+ if (result) {
187
+ const convertedSize = fs.statSync(outputPath).size;
188
+ stats.convertedSize += convertedSize;
189
+
190
+ const ratio = ((1 - convertedSize / originalSize) * 100).toFixed(2);
191
+ const sizeChange = ratio > 0 ? '压缩' : '增加';
192
+ console.log(`✅ 成功: ${outputPath}`);
193
+ console.log(` ${formatSize(originalSize)} -> ${formatSize(convertedSize)} (${sizeChange} ${Math.abs(ratio)}%)`);
194
+
195
+ // 删除原图
196
+ if (config.deleteOriginal) {
197
+ try {
198
+ fs.unlinkSync(inputPath);
199
+ console.log(`🗑️ 已删除原图: ${inputPath}`);
200
+ stats.deleted++;
201
+ } catch (deleteError) {
202
+ console.error(`❌ 删除失败: ${inputPath}`);
203
+ console.error(` 错误: ${deleteError.message}`);
204
+ }
205
+ }
206
+
207
+ stats.success++;
208
+ }
209
+ } catch (error) {
210
+ console.error(`❌ 失败: ${inputPath}`);
211
+ console.error(` 错误: ${error.message}`);
212
+ stats.failed++;
213
+ }
214
+
215
+ stats.total++;
216
+ }
217
+
218
+ /**
219
+ * 显示帮助信息
220
+ */
221
+ function showHelp() {
222
+ console.log(`
223
+ 📸 图片转 WebP 格式工具
224
+
225
+ 使用方法:
226
+ node scripts/convert-to-webp.js [质量值] [选项]
227
+
228
+ 参数:
229
+ 质量值 设置转换质量 0-100 (默认: 100)
230
+ 示例: node scripts/convert-to-webp.js 90
231
+
232
+ 选项:
233
+ --quality=<0-100>, -q <0-100> 设置转换质量 (默认: 100)
234
+ --delete-original, -d 转换后删除原图 ⚠️
235
+ --force, -f 强制覆盖已存在的文件,不询问
236
+ --dry-run 预览模式,不实际转换
237
+ --dir=<路径> 指定源目录 (默认: ./src/assets)
238
+ --help, -h 显示此帮助信息
239
+
240
+ 使用示例:
241
+ # 基础用法
242
+ node scripts/convert-to-webp.js # 质量 100
243
+ node scripts/convert-to-webp.js 90 # 质量 90
244
+ node scripts/convert-to-webp.js --quality=80 # 质量 80
245
+
246
+ # 预览模式(推荐首次使用)
247
+ node scripts/convert-to-webp.js --dry-run # 预览
248
+ node scripts/convert-to-webp.js 90 --dry-run # 质量 90 预览
249
+
250
+ # 删除原图(谨慎使用)
251
+ node scripts/convert-to-webp.js 90 --delete-original
252
+ node scripts/convert-to-webp.js -q 85 -d
253
+
254
+ # 强制覆盖
255
+ node scripts/convert-to-webp.js --force # 不询问直接覆盖
256
+ node scripts/convert-to-webp.js 80 -f -d # 组合使用
257
+
258
+ # 指定目录
259
+ node scripts/convert-to-webp.js --dir=./public/images
260
+ node scripts/convert-to-webp.js --dir=./assets -q 90
261
+
262
+ NPM 快捷命令:
263
+ npm run webp # 质量 100
264
+ npm run webp:preview # 预览模式
265
+ npm run webp:80 # 质量 80
266
+ npm run webp:90 # 质量 90
267
+
268
+ 质量推荐:
269
+ 100 最高质量,文件较大 - 适合重要图标、UI 元素
270
+ 90 极佳质量,推荐使用 - 适合游戏角色、场景图
271
+ 80 优秀质量,文件较小 - 适合背景图、装饰图
272
+ 70 良好质量,大幅压缩 - 适合缩略图、临时图片
273
+
274
+ 注意事项:
275
+ ⚠️ 使用 --delete-original 前请确保备份
276
+ 💡 首次使用建议先运行 --dry-run 预览
277
+ 📦 支持格式: .png, .jpg, .jpeg, .jfif
278
+ `);
279
+ }
280
+
281
+ /**
282
+ * 主函数
283
+ */
284
+ async function main() {
285
+ // 显示帮助
286
+ if (process.argv.includes('--help') || process.argv.includes('-h')) {
287
+ showHelp();
288
+ process.exit(0);
289
+ }
290
+
291
+ const config = parseArgs();
292
+
293
+ console.log('='.repeat(60));
294
+ console.log('📸 图片转 WebP 格式工具');
295
+ console.log('='.repeat(60));
296
+ console.log(`📁 源目录: ${config.dir}`);
297
+ console.log(`🎨 质量: ${config.quality}%`);
298
+ console.log(`🔧 模式: ${config.isDryRun ? '预览模式(不会实际转换)' : '转换模式'}`);
299
+ console.log(`🗑️ 删除原图: ${config.deleteOriginal ? '是 ⚠️' : '否'}`);
300
+ console.log(`⚡ 强制覆盖: ${config.force ? '是' : '否'}`);
301
+ console.log('='.repeat(60));
302
+ console.log('');
303
+
304
+ // 检查源目录是否存在
305
+ if (!fs.existsSync(config.dir)) {
306
+ console.error(`❌ 错误: 目录 ${config.dir} 不存在`);
307
+ process.exit(1);
308
+ }
309
+
310
+ // 获取所有图片文件
311
+ console.log('🔍 扫描图片文件...');
312
+ const imageFiles = getAllImageFiles(config.dir);
313
+
314
+ if (imageFiles.length === 0) {
315
+ console.log('⚠️ 未找到支持的图片文件');
316
+ console.log(` 支持格式: ${SUPPORTED_EXTENSIONS.join(', ')}`);
317
+ process.exit(0);
318
+ }
319
+
320
+ console.log(`📝 找到 ${imageFiles.length} 个图片文件\n`);
321
+
322
+ // 如果要删除原图且不是预览模式,要求确认
323
+ if (config.deleteOriginal && !config.isDryRun && !config.force) {
324
+ console.log('⚠️ 警告: 您选择了删除原图选项!');
325
+ const confirmed = await askConfirmation('确定要继续吗? (y/n): ');
326
+ if (!confirmed) {
327
+ console.log('❌ 已取消操作');
328
+ process.exit(0);
329
+ }
330
+ console.log('');
331
+ }
332
+
333
+ // 逐个转换
334
+ for (const imagePath of imageFiles) {
335
+ await convertImage(imagePath, config);
336
+ console.log('');
337
+ }
338
+
339
+ // 输出统计信息
340
+ console.log('='.repeat(60));
341
+ console.log('📊 转换统计');
342
+ console.log('='.repeat(60));
343
+ console.log(`总计: ${stats.total} 个文件`);
344
+ console.log(`成功: ${stats.success} 个`);
345
+ console.log(`失败: ${stats.failed} 个`);
346
+ console.log(`跳过: ${stats.skipped} 个`);
347
+
348
+ if (config.deleteOriginal && !config.isDryRun) {
349
+ console.log(`删除: ${stats.deleted} 个原图`);
350
+ }
351
+
352
+ if (!config.isDryRun && stats.success > 0) {
353
+ console.log('');
354
+ console.log(`原始总大小: ${formatSize(stats.originalSize)}`);
355
+ console.log(`转换后总大小: ${formatSize(stats.convertedSize)}`);
356
+ const totalRatio = ((1 - stats.convertedSize / stats.originalSize) * 100).toFixed(2);
357
+ console.log(`总体压缩率: ${totalRatio}%`);
358
+ console.log(`节省空间: ${formatSize(stats.originalSize - stats.convertedSize)}`);
359
+ }
360
+
361
+ console.log('='.repeat(60));
362
+ console.log(config.isDryRun ? '🔍 预览完成!' : '✨ 转换完成!');
363
+ console.log('='.repeat(60));
364
+ }
365
+
366
+ // 运行
367
+ main().catch(error => {
368
+ console.error('❌ 发生错误:', error);
369
+ process.exit(1);
370
+ });