@tomako/tools-runtime 0.1.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.
Files changed (531) hide show
  1. package/README.md +12 -0
  2. package/package.json +629 -0
  3. package/src/assets/icons/brands/social/bluesky-icon.tsx +9 -0
  4. package/src/assets/icons/brands/social/facebook-icon.tsx +9 -0
  5. package/src/assets/icons/brands/social/index.ts +8 -0
  6. package/src/assets/icons/brands/social/instagram-icon.tsx +9 -0
  7. package/src/assets/icons/brands/social/linkedin-icon.tsx +9 -0
  8. package/src/assets/icons/brands/social/threads-icon.tsx +9 -0
  9. package/src/assets/icons/brands/social/tiktok-icon.tsx +10 -0
  10. package/src/assets/icons/brands/social/x-icon.tsx +9 -0
  11. package/src/assets/icons/brands/social/youtube-icon.tsx +9 -0
  12. package/src/assets/icons/social-profile/facebook-icon.tsx +12 -0
  13. package/src/assets/icons/social-profile/index.ts +29 -0
  14. package/src/assets/icons/social-profile/instagram-icon.tsx +18 -0
  15. package/src/assets/icons/social-profile/linkedin-icon.tsx +12 -0
  16. package/src/assets/icons/social-profile/tiktok-icon.tsx +12 -0
  17. package/src/assets/icons/social-profile/x-icon.tsx +13 -0
  18. package/src/assets/icons/social-profile/youtube-icon.tsx +12 -0
  19. package/src/components/tools/engine/form-builder.tsx +219 -0
  20. package/src/components/tools/engine/package-widget-loader.tsx +38 -0
  21. package/src/components/tools/engine/package-widget-runtime.tsx +409 -0
  22. package/src/components/tools/engine/package-widget.tsx +77 -0
  23. package/src/components/tools/engine/result-view.tsx +596 -0
  24. package/src/components/tools/engine/use-legacy-llm-task.ts +64 -0
  25. package/src/components/tools/engine/use-tool-task.ts +195 -0
  26. package/src/components/tools/market-forecast-report.tsx +1150 -0
  27. package/src/components/tools/registry.ts +13 -0
  28. package/src/components/tools/workspace/index.ts +39 -0
  29. package/src/components/tools/workspace/tool-action-bar.tsx +99 -0
  30. package/src/components/tools/workspace/tool-empty-panel.tsx +32 -0
  31. package/src/components/tools/workspace/tool-error-panel.tsx +40 -0
  32. package/src/components/tools/workspace/tool-field.tsx +124 -0
  33. package/src/components/tools/workspace/tool-file-upload.tsx +112 -0
  34. package/src/components/tools/workspace/tool-form-section.tsx +21 -0
  35. package/src/components/tools/workspace/tool-form-shell.tsx +36 -0
  36. package/src/components/tools/workspace/tool-loading-panel.tsx +269 -0
  37. package/src/components/tools/workspace/tool-option-row.tsx +97 -0
  38. package/src/components/tools/workspace/tool-preview-panel.tsx +31 -0
  39. package/src/components/tools/workspace/tool-result-actions.tsx +67 -0
  40. package/src/components/tools/workspace/tool-result-file-actions.ts +31 -0
  41. package/src/components/tools/workspace/tool-result-header.tsx +39 -0
  42. package/src/components/tools/workspace/tool-status.tsx +13 -0
  43. package/src/components/tools/workspace/tool-workspace-layout.tsx +226 -0
  44. package/src/constants/LKS.ts +29 -0
  45. package/src/constants/related-tools.ts +18 -0
  46. package/src/constants/social-profile-kit.ts +284 -0
  47. package/src/constants/tool-assets.ts +7 -0
  48. package/src/env.d.ts +3 -0
  49. package/src/features/tools/_templates/standard-tool.container.tsx +90 -0
  50. package/src/features/tools/app-icon-resizer/app-icon-resizer.container.tsx +11 -0
  51. package/src/features/tools/app-icon-resizer/app-icon-resizer.spec.ts +15 -0
  52. package/src/features/tools/app-icon-resizer/widget/app-icon-resizer.tsx +444 -0
  53. package/src/features/tools/app-icon-resizer/widget/constants.ts +19 -0
  54. package/src/features/tools/app-icon-resizer/widget/helpers.ts +314 -0
  55. package/src/features/tools/app-icon-resizer/widget/index.ts +7 -0
  56. package/src/features/tools/app-icon-resizer/widget/platform-icons.tsx +59 -0
  57. package/src/features/tools/app-icon-resizer/widget/preview-dialogs.tsx +139 -0
  58. package/src/features/tools/app-icon-resizer/widget/result-shell.tsx +165 -0
  59. package/src/features/tools/app-icon-resizer/widget/types.ts +36 -0
  60. package/src/features/tools/app-store-screenshot-generator/app-store-screenshot-generator.container.tsx +108 -0
  61. package/src/features/tools/app-store-screenshot-generator/app-store-screenshot-generator.spec.ts +14 -0
  62. package/src/features/tools/app-store-screenshot-generator/config.ts +3 -0
  63. package/src/features/tools/app-store-screenshot-generator/widget/app-store-screenshot-generator.tsx +1250 -0
  64. package/src/features/tools/app-store-screenshot-generator/widget/asset-slots.ts +76 -0
  65. package/src/features/tools/app-store-screenshot-generator/widget/constants.ts +43 -0
  66. package/src/features/tools/app-store-screenshot-generator/widget/form-controls.tsx +214 -0
  67. package/src/features/tools/app-store-screenshot-generator/widget/generation.ts +528 -0
  68. package/src/features/tools/app-store-screenshot-generator/widget/index.ts +7 -0
  69. package/src/features/tools/app-store-screenshot-generator/widget/input-media.ts +175 -0
  70. package/src/features/tools/app-store-screenshot-generator/widget/types.ts +60 -0
  71. package/src/features/tools/app-store-screenshot-generator/widget/zip.ts +119 -0
  72. package/src/features/tools/build-in-public-story-generator/build-in-public-story-generator.container.tsx +135 -0
  73. package/src/features/tools/build-in-public-story-generator/build-in-public-story-generator.spec.ts +15 -0
  74. package/src/features/tools/build-in-public-story-generator/widget/build-in-public-story-generator.tsx +314 -0
  75. package/src/features/tools/build-in-public-story-generator/widget/constants.ts +3 -0
  76. package/src/features/tools/build-in-public-story-generator/widget/helpers.ts +25 -0
  77. package/src/features/tools/build-in-public-story-generator/widget/index.ts +7 -0
  78. package/src/features/tools/build-in-public-story-generator/widget/preview-panels.tsx +58 -0
  79. package/src/features/tools/build-in-public-story-generator/widget/result-shell.tsx +164 -0
  80. package/src/features/tools/build-in-public-story-generator/widget/types.ts +6 -0
  81. package/src/features/tools/cold-start-channel-selector/cold-start-channel-selector.container.tsx +30 -0
  82. package/src/features/tools/cold-start-channel-selector/cold-start-channel-selector.spec.ts +15 -0
  83. package/src/features/tools/cold-start-channel-selector/widget/cold-start-channel-selector.tsx +417 -0
  84. package/src/features/tools/cold-start-channel-selector/widget/constants.ts +46 -0
  85. package/src/features/tools/cold-start-channel-selector/widget/form-controls.tsx +41 -0
  86. package/src/features/tools/cold-start-channel-selector/widget/helpers.ts +75 -0
  87. package/src/features/tools/cold-start-channel-selector/widget/index.ts +7 -0
  88. package/src/features/tools/cold-start-channel-selector/widget/preview-panels.tsx +250 -0
  89. package/src/features/tools/cold-start-channel-selector/widget/result-shell.tsx +199 -0
  90. package/src/features/tools/cold-start-channel-selector/widget/types.ts +21 -0
  91. package/src/features/tools/commercial-bp-generator/commercial-bp-generator.container.tsx +102 -0
  92. package/src/features/tools/commercial-bp-generator/commercial-bp-generator.spec.ts +14 -0
  93. package/src/features/tools/commercial-bp-generator/widget/commercial-bp-generator.tsx +676 -0
  94. package/src/features/tools/commercial-bp-generator/widget/constants.ts +32 -0
  95. package/src/features/tools/commercial-bp-generator/widget/helpers.ts +147 -0
  96. package/src/features/tools/commercial-bp-generator/widget/index.ts +7 -0
  97. package/src/features/tools/commercial-bp-generator/widget/result-shell.tsx +271 -0
  98. package/src/features/tools/commercial-bp-generator/widget/style-option.tsx +54 -0
  99. package/src/features/tools/commercial-bp-generator/widget/types.ts +33 -0
  100. package/src/features/tools/community-feedback-inbox/community-feedback-inbox.container.tsx +126 -0
  101. package/src/features/tools/community-feedback-inbox/community-feedback-inbox.spec.ts +15 -0
  102. package/src/features/tools/community-feedback-inbox/widget/community-feedback-inbox.tsx +271 -0
  103. package/src/features/tools/community-feedback-inbox/widget/constants.ts +3 -0
  104. package/src/features/tools/community-feedback-inbox/widget/helpers.ts +28 -0
  105. package/src/features/tools/community-feedback-inbox/widget/index.ts +7 -0
  106. package/src/features/tools/community-feedback-inbox/widget/preview-panels.tsx +92 -0
  107. package/src/features/tools/community-feedback-inbox/widget/result-shell.tsx +266 -0
  108. package/src/features/tools/community-feedback-inbox/widget/types.ts +6 -0
  109. package/src/features/tools/community-pain-point-extractor/community-pain-point-extractor.container.tsx +124 -0
  110. package/src/features/tools/community-pain-point-extractor/community-pain-point-extractor.spec.ts +15 -0
  111. package/src/features/tools/community-pain-point-extractor/widget/community-pain-point-extractor.tsx +404 -0
  112. package/src/features/tools/community-pain-point-extractor/widget/constants.ts +41 -0
  113. package/src/features/tools/community-pain-point-extractor/widget/helpers.ts +76 -0
  114. package/src/features/tools/community-pain-point-extractor/widget/index.ts +8 -0
  115. package/src/features/tools/community-pain-point-extractor/widget/progress-panels.tsx +251 -0
  116. package/src/features/tools/community-pain-point-extractor/widget/result-shell.tsx +478 -0
  117. package/src/features/tools/community-pain-point-extractor/widget/types.ts +33 -0
  118. package/src/features/tools/community-post-risk-checker/community-post-risk-checker.package.ts +792 -0
  119. package/src/features/tools/competitor-analysis/competitor-analysis.container.tsx +136 -0
  120. package/src/features/tools/competitor-analysis/competitor-analysis.spec.ts +15 -0
  121. package/src/features/tools/competitor-analysis/widget/competitor-analysis.tsx +241 -0
  122. package/src/features/tools/competitor-analysis/widget/constants.ts +9 -0
  123. package/src/features/tools/competitor-analysis/widget/index.ts +7 -0
  124. package/src/features/tools/competitor-analysis/widget/preview-panels.tsx +60 -0
  125. package/src/features/tools/competitor-analysis/widget/result-shell.tsx +418 -0
  126. package/src/features/tools/competitor-analysis/widget/types.ts +13 -0
  127. package/src/features/tools/disclaimer-generator/disclaimer-generator.package.ts +339 -0
  128. package/src/features/tools/email-template-generator/email-template-generator.container.tsx +224 -0
  129. package/src/features/tools/email-template-generator/email-template-generator.spec.ts +14 -0
  130. package/src/features/tools/email-template-generator/widget/email-template-generator.tsx +358 -0
  131. package/src/features/tools/email-template-generator/widget/helpers.ts +39 -0
  132. package/src/features/tools/email-template-generator/widget/index.ts +7 -0
  133. package/src/features/tools/email-template-generator/widget/preview-panel.tsx +311 -0
  134. package/src/features/tools/email-template-generator/widget/result-text-block.tsx +7 -0
  135. package/src/features/tools/email-template-generator/widget/types.ts +24 -0
  136. package/src/features/tools/gtm-readiness-checklist/gtm-readiness-checklist.container.tsx +75 -0
  137. package/src/features/tools/gtm-readiness-checklist/gtm-readiness-checklist.spec.ts +15 -0
  138. package/src/features/tools/gtm-readiness-checklist/widget/gtm-readiness-checklist.tsx +122 -0
  139. package/src/features/tools/gtm-readiness-checklist/widget/index.ts +7 -0
  140. package/src/features/tools/investor-matching-planner/investor-matching-planner.package.ts +869 -0
  141. package/src/features/tools/koko-codex-assistant/koko-codex-assistant.container.tsx +151 -0
  142. package/src/features/tools/koko-codex-assistant/koko-codex-assistant.spec.ts +14 -0
  143. package/src/features/tools/koko-codex-assistant/widget/index.ts +7 -0
  144. package/src/features/tools/koko-codex-assistant/widget/koko-codex-assistant-download.tsx +192 -0
  145. package/src/features/tools/logo-generator/logo-generator.container.tsx +112 -0
  146. package/src/features/tools/logo-generator/logo-generator.spec.ts +15 -0
  147. package/src/features/tools/logo-generator/widget/index.ts +7 -0
  148. package/src/features/tools/logo-generator/widget/logo-generator.tsx +1244 -0
  149. package/src/features/tools/market-forecast-report/market-forecast-report.container.tsx +73 -0
  150. package/src/features/tools/market-forecast-report/market-forecast-report.spec.ts +15 -0
  151. package/src/features/tools/million-user-growth-test/million-user-growth-test.container.tsx +123 -0
  152. package/src/features/tools/million-user-growth-test/million-user-growth-test.spec.ts +14 -0
  153. package/src/features/tools/million-user-growth-test/widget/index.ts +7 -0
  154. package/src/features/tools/million-user-growth-test/widget/million-user-growth-test.tsx +433 -0
  155. package/src/features/tools/module-loader.ts +107 -0
  156. package/src/features/tools/og-image-generator/og-image-generator.container.tsx +86 -0
  157. package/src/features/tools/og-image-generator/og-image-generator.spec.ts +14 -0
  158. package/src/features/tools/og-image-generator/widget/constants.ts +38 -0
  159. package/src/features/tools/og-image-generator/widget/form-fields.tsx +240 -0
  160. package/src/features/tools/og-image-generator/widget/index.ts +7 -0
  161. package/src/features/tools/og-image-generator/widget/og-image-generator.tsx +742 -0
  162. package/src/features/tools/og-image-generator/widget/preview-panel.tsx +305 -0
  163. package/src/features/tools/og-image-generator/widget/types.ts +1 -0
  164. package/src/features/tools/package/direct-models.ts +20 -0
  165. package/src/features/tools/package/form.ts +38 -0
  166. package/src/features/tools/package/l10n.ts +22 -0
  167. package/src/features/tools/package/package-container.tsx +285 -0
  168. package/src/features/tools/package/package-loaders.ts +22 -0
  169. package/src/features/tools/package/package-registry.ts +36 -0
  170. package/src/features/tools/package/result-markdown.ts +142 -0
  171. package/src/features/tools/package/schema.ts +379 -0
  172. package/src/features/tools/package/to-tool-module.ts +20 -0
  173. package/src/features/tools/package/to-tool-spec.ts +52 -0
  174. package/src/features/tools/package/types.ts +381 -0
  175. package/src/features/tools/pitch-deck-generator/pitch-deck-generator.container.tsx +175 -0
  176. package/src/features/tools/pitch-deck-generator/pitch-deck-generator.spec.ts +14 -0
  177. package/src/features/tools/pitch-deck-generator/pitch-deck-library-data.ts +476 -0
  178. package/src/features/tools/pitch-deck-generator/pitch-deck-library.tsx +159 -0
  179. package/src/features/tools/pitch-deck-generator/widget/ceremony-line.tsx +27 -0
  180. package/src/features/tools/pitch-deck-generator/widget/constants.ts +27 -0
  181. package/src/features/tools/pitch-deck-generator/widget/index.ts +7 -0
  182. package/src/features/tools/pitch-deck-generator/widget/pitch-deck-generator.tsx +763 -0
  183. package/src/features/tools/pitch-deck-generator/widget/result-helpers.ts +102 -0
  184. package/src/features/tools/pitch-deck-generator/widget/result-shell.tsx +263 -0
  185. package/src/features/tools/pitch-deck-generator/widget/slide-frame.tsx +106 -0
  186. package/src/features/tools/pitch-deck-generator/widget/slide-lightbox.tsx +149 -0
  187. package/src/features/tools/pitch-deck-generator/widget/style-option.tsx +54 -0
  188. package/src/features/tools/pitch-deck-generator/widget/types.ts +44 -0
  189. package/src/features/tools/product-agreement-generator/product-agreement-generator.container.tsx +137 -0
  190. package/src/features/tools/product-agreement-generator/product-agreement-generator.spec.ts +14 -0
  191. package/src/features/tools/product-agreement-generator/widget/constants.ts +27 -0
  192. package/src/features/tools/product-agreement-generator/widget/form-controls.tsx +218 -0
  193. package/src/features/tools/product-agreement-generator/widget/helpers.ts +68 -0
  194. package/src/features/tools/product-agreement-generator/widget/index.ts +7 -0
  195. package/src/features/tools/product-agreement-generator/widget/product-agreement-generator.tsx +671 -0
  196. package/src/features/tools/product-agreement-generator/widget/result-shell.tsx +473 -0
  197. package/src/features/tools/product-agreement-generator/widget/types.ts +89 -0
  198. package/src/features/tools/product-hunt-launch-planner/product-hunt-launch-planner.container.tsx +122 -0
  199. package/src/features/tools/product-hunt-launch-planner/product-hunt-launch-planner.spec.ts +15 -0
  200. package/src/features/tools/product-hunt-launch-planner/widget/constants.ts +5 -0
  201. package/src/features/tools/product-hunt-launch-planner/widget/helpers.ts +44 -0
  202. package/src/features/tools/product-hunt-launch-planner/widget/index.ts +7 -0
  203. package/src/features/tools/product-hunt-launch-planner/widget/preview-panel.tsx +143 -0
  204. package/src/features/tools/product-hunt-launch-planner/widget/product-hunt-launch-planner.tsx +504 -0
  205. package/src/features/tools/product-hunt-launch-planner/widget/result-shell.tsx +176 -0
  206. package/src/features/tools/product-hunt-launch-planner/widget/types.ts +20 -0
  207. package/src/features/tools/product-name-generator/product-name-generator.container.tsx +14 -0
  208. package/src/features/tools/product-name-generator/product-name-generator.spec.ts +15 -0
  209. package/src/features/tools/product-name-generator/widget/candidate-card.tsx +273 -0
  210. package/src/features/tools/product-name-generator/widget/constants.ts +21 -0
  211. package/src/features/tools/product-name-generator/widget/form-controls.tsx +136 -0
  212. package/src/features/tools/product-name-generator/widget/helpers.ts +41 -0
  213. package/src/features/tools/product-name-generator/widget/index.ts +7 -0
  214. package/src/features/tools/product-name-generator/widget/product-name-generator.tsx +776 -0
  215. package/src/features/tools/product-name-generator/widget/types.ts +36 -0
  216. package/src/features/tools/product-positioning-analyzer/product-positioning-analyzer.package.ts +801 -0
  217. package/src/features/tools/product-poster-generator/product-poster-generator.container.tsx +14 -0
  218. package/src/features/tools/product-poster-generator/product-poster-generator.spec.ts +15 -0
  219. package/src/features/tools/product-poster-generator/widget/constants.ts +125 -0
  220. package/src/features/tools/product-poster-generator/widget/form-controls.tsx +204 -0
  221. package/src/features/tools/product-poster-generator/widget/helpers.ts +155 -0
  222. package/src/features/tools/product-poster-generator/widget/index.ts +7 -0
  223. package/src/features/tools/product-poster-generator/widget/product-poster-generator.tsx +663 -0
  224. package/src/features/tools/product-poster-generator/widget/result-shell.tsx +12 -0
  225. package/src/features/tools/product-poster-generator/widget/types.ts +83 -0
  226. package/src/features/tools/product-ui-generator/product-ui-generator.container.tsx +116 -0
  227. package/src/features/tools/product-ui-generator/product-ui-generator.spec.ts +14 -0
  228. package/src/features/tools/product-ui-generator/widget/constants.ts +18 -0
  229. package/src/features/tools/product-ui-generator/widget/helpers.ts +11 -0
  230. package/src/features/tools/product-ui-generator/widget/index.ts +7 -0
  231. package/src/features/tools/product-ui-generator/widget/preview-panels.tsx +73 -0
  232. package/src/features/tools/product-ui-generator/widget/product-ui-generator.tsx +304 -0
  233. package/src/features/tools/product-ui-generator/widget/result-shell.tsx +118 -0
  234. package/src/features/tools/product-ui-generator/widget/types.ts +9 -0
  235. package/src/features/tools/reddit-pain-point-finder/reddit-pain-point-finder.container.tsx +113 -0
  236. package/src/features/tools/reddit-pain-point-finder/reddit-pain-point-finder.spec.ts +15 -0
  237. package/src/features/tools/reddit-pain-point-finder/widget/constants.ts +5 -0
  238. package/src/features/tools/reddit-pain-point-finder/widget/index.ts +7 -0
  239. package/src/features/tools/reddit-pain-point-finder/widget/preview-panels.tsx +73 -0
  240. package/src/features/tools/reddit-pain-point-finder/widget/reddit-pain-point-finder.tsx +281 -0
  241. package/src/features/tools/reddit-pain-point-finder/widget/result-shell.tsx +203 -0
  242. package/src/features/tools/reddit-pain-point-finder/widget/types.ts +15 -0
  243. package/src/features/tools/registry.ts +288 -0
  244. package/src/features/tools/runtime-registry.ts +186 -0
  245. package/src/features/tools/shared/create-guide-stack-container.tsx +176 -0
  246. package/src/features/tools/shared/create-standard-landing-container.tsx +163 -0
  247. package/src/features/tools/shared/tool-content-sections.tsx +659 -0
  248. package/src/features/tools/shared/tool-cta.tsx +66 -0
  249. package/src/features/tools/shared/tool-draft-view.tsx +53 -0
  250. package/src/features/tools/shared/tool-guide-section.tsx +191 -0
  251. package/src/features/tools/shared/tool-handoff.tsx +38 -0
  252. package/src/features/tools/shared/tool-mcp-badge.tsx +23 -0
  253. package/src/features/tools/shared/tool-message-translator.ts +37 -0
  254. package/src/features/tools/shared/tool-page-shell.tsx +348 -0
  255. package/src/features/tools/shared/tool-standard-generation-steps.tsx +52 -0
  256. package/src/features/tools/shared/tool-standard-landing-sections.tsx +447 -0
  257. package/src/features/tools/shared/tool-ui-registry.ts +237 -0
  258. package/src/features/tools/shared/tool-widget-slot.tsx +29 -0
  259. package/src/features/tools/slogan-generator/slogan-generator.package.ts +641 -0
  260. package/src/features/tools/social-listening-research-report/social-listening-research-report.container.tsx +124 -0
  261. package/src/features/tools/social-listening-research-report/social-listening-research-report.spec.ts +15 -0
  262. package/src/features/tools/social-listening-research-report/widget/constants.ts +4 -0
  263. package/src/features/tools/social-listening-research-report/widget/index.ts +7 -0
  264. package/src/features/tools/social-listening-research-report/widget/preview-panels.tsx +73 -0
  265. package/src/features/tools/social-listening-research-report/widget/result-shell.tsx +216 -0
  266. package/src/features/tools/social-listening-research-report/widget/social-listening-research-report.tsx +303 -0
  267. package/src/features/tools/social-listening-research-report/widget/types.ts +15 -0
  268. package/src/features/tools/social-platform-asset-adapter/social-platform-asset-adapter.container.tsx +123 -0
  269. package/src/features/tools/social-platform-asset-adapter/social-platform-asset-adapter.spec.ts +15 -0
  270. package/src/features/tools/social-platform-asset-adapter/widget/constants.ts +43 -0
  271. package/src/features/tools/social-platform-asset-adapter/widget/helpers.ts +249 -0
  272. package/src/features/tools/social-platform-asset-adapter/widget/index.ts +7 -0
  273. package/src/features/tools/social-platform-asset-adapter/widget/result-shell.tsx +350 -0
  274. package/src/features/tools/social-platform-asset-adapter/widget/section-heading.tsx +18 -0
  275. package/src/features/tools/social-platform-asset-adapter/widget/social-platform-asset-adapter.tsx +641 -0
  276. package/src/features/tools/social-platform-asset-adapter/widget/types.ts +40 -0
  277. package/src/features/tools/social-profile-kit/social-profile-kit.container.tsx +177 -0
  278. package/src/features/tools/social-profile-kit/social-profile-kit.spec.ts +15 -0
  279. package/src/features/tools/social-profile-kit/widget/context-editor.tsx +171 -0
  280. package/src/features/tools/social-profile-kit/widget/generation-runtime.ts +281 -0
  281. package/src/features/tools/social-profile-kit/widget/index.ts +7 -0
  282. package/src/features/tools/social-profile-kit/widget/input-panel.tsx +294 -0
  283. package/src/features/tools/social-profile-kit/widget/platform-dock.tsx +115 -0
  284. package/src/features/tools/social-profile-kit/widget/profile-canvas.tsx +318 -0
  285. package/src/features/tools/social-profile-kit/widget/profile-preview/bluesky-preview.tsx +188 -0
  286. package/src/features/tools/social-profile-kit/widget/profile-preview/facebook-preview.tsx +168 -0
  287. package/src/features/tools/social-profile-kit/widget/profile-preview/index.ts +1 -0
  288. package/src/features/tools/social-profile-kit/widget/profile-preview/instagram-preview.tsx +278 -0
  289. package/src/features/tools/social-profile-kit/widget/profile-preview/linkedin-preview.tsx +358 -0
  290. package/src/features/tools/social-profile-kit/widget/profile-preview/profile-preview.tsx +348 -0
  291. package/src/features/tools/social-profile-kit/widget/profile-preview/shared.tsx +391 -0
  292. package/src/features/tools/social-profile-kit/widget/profile-preview/threads-preview.tsx +148 -0
  293. package/src/features/tools/social-profile-kit/widget/profile-preview/tiktok-preview.tsx +319 -0
  294. package/src/features/tools/social-profile-kit/widget/profile-preview/x-preview.tsx +341 -0
  295. package/src/features/tools/social-profile-kit/widget/profile-preview/youtube-preview.tsx +297 -0
  296. package/src/features/tools/social-profile-kit/widget/social-profile-kit-generator.tsx +1059 -0
  297. package/src/features/tools/social-profile-kit/widget/types.ts +104 -0
  298. package/src/features/tools/spec-registry.ts +158 -0
  299. package/src/features/tools/startup-idea-research/startup-idea-research.container.tsx +111 -0
  300. package/src/features/tools/startup-idea-research/startup-idea-research.spec.ts +15 -0
  301. package/src/features/tools/startup-idea-research/widget/constants.ts +11 -0
  302. package/src/features/tools/startup-idea-research/widget/index.ts +7 -0
  303. package/src/features/tools/startup-idea-research/widget/preview-panels.tsx +67 -0
  304. package/src/features/tools/startup-idea-research/widget/result-shell.tsx +323 -0
  305. package/src/features/tools/startup-idea-research/widget/startup-idea-research.tsx +272 -0
  306. package/src/features/tools/startup-idea-research/widget/types.ts +21 -0
  307. package/src/features/tools/tarot-reading/tarot-reading.container.tsx +412 -0
  308. package/src/features/tools/tarot-reading/tarot-reading.spec.ts +15 -0
  309. package/src/features/tools/tarot-reading/widget/cards.tsx +152 -0
  310. package/src/features/tools/tarot-reading/widget/constants.ts +69 -0
  311. package/src/features/tools/tarot-reading/widget/deck-view.tsx +86 -0
  312. package/src/features/tools/tarot-reading/widget/deck.ts +86 -0
  313. package/src/features/tools/tarot-reading/widget/index.ts +7 -0
  314. package/src/features/tools/tarot-reading/widget/landing-view.tsx +209 -0
  315. package/src/features/tools/tarot-reading/widget/reading-copy.tsx +138 -0
  316. package/src/features/tools/tarot-reading/widget/reading-view.tsx +189 -0
  317. package/src/features/tools/tarot-reading/widget/reveal-view.tsx +50 -0
  318. package/src/features/tools/tarot-reading/widget/ritual-view.tsx +276 -0
  319. package/src/features/tools/tarot-reading/widget/tarot-reading.tsx +262 -0
  320. package/src/features/tools/tarot-reading/widget/types.ts +22 -0
  321. package/src/features/tools/twitter-gif-downloader/twitter-gif-downloader.container.tsx +114 -0
  322. package/src/features/tools/twitter-gif-downloader/twitter-gif-downloader.spec.ts +14 -0
  323. package/src/features/tools/twitter-gif-downloader/widget/index.ts +7 -0
  324. package/src/features/tools/twitter-gif-downloader/widget/twitter-gif-downloader.tsx +351 -0
  325. package/src/features/tools/types.ts +68 -0
  326. package/src/features/tools/user-interview-guide-generator/user-interview-guide-generator.package.ts +774 -0
  327. package/src/features/tools/user-interview-insight-report-generator/user-interview-insight-report-generator.container.tsx +23 -0
  328. package/src/features/tools/user-interview-insight-report-generator/user-interview-insight-report-generator.spec.ts +14 -0
  329. package/src/features/tools/user-interview-insight-report-generator/widget/constants.ts +8 -0
  330. package/src/features/tools/user-interview-insight-report-generator/widget/helpers.ts +4 -0
  331. package/src/features/tools/user-interview-insight-report-generator/widget/index.ts +7 -0
  332. package/src/features/tools/user-interview-insight-report-generator/widget/preview-panels.tsx +55 -0
  333. package/src/features/tools/user-interview-insight-report-generator/widget/result-shell.tsx +122 -0
  334. package/src/features/tools/user-interview-insight-report-generator/widget/types.ts +16 -0
  335. package/src/features/tools/user-interview-insight-report-generator/widget/user-interview-insight-report-generator.tsx +293 -0
  336. package/src/features/tools/user-interview-record-generator/user-interview-record-generator.container.tsx +18 -0
  337. package/src/features/tools/user-interview-record-generator/user-interview-record-generator.spec.ts +14 -0
  338. package/src/features/tools/user-interview-record-generator/widget/constants.ts +9 -0
  339. package/src/features/tools/user-interview-record-generator/widget/helpers.ts +4 -0
  340. package/src/features/tools/user-interview-record-generator/widget/index.ts +7 -0
  341. package/src/features/tools/user-interview-record-generator/widget/preview-panels.tsx +48 -0
  342. package/src/features/tools/user-interview-record-generator/widget/result-shell.tsx +128 -0
  343. package/src/features/tools/user-interview-record-generator/widget/types.ts +16 -0
  344. package/src/features/tools/user-interview-record-generator/widget/user-interview-record-generator.tsx +305 -0
  345. package/src/features/tools/utm-builder/utm-builder.package.ts +846 -0
  346. package/src/features/tools/widget-registry.ts +73 -0
  347. package/src/generated/mcp-tools-registry.json +17 -0
  348. package/src/host/client-proxy.test.ts +33 -0
  349. package/src/host/client.ts +678 -0
  350. package/src/host/server.ts +119 -0
  351. package/src/i18n/client.ts +11 -0
  352. package/src/i18n/locale-fallback.ts +38 -0
  353. package/src/i18n/locale.ts +18 -0
  354. package/src/i18n/messages/en/page/tools.ts +124 -0
  355. package/src/i18n/messages/en/tools/app-icon-resizer.ts +244 -0
  356. package/src/i18n/messages/en/tools/app-store-screenshot-generator.ts +145 -0
  357. package/src/i18n/messages/en/tools/build-in-public-story-generator.ts +229 -0
  358. package/src/i18n/messages/en/tools/cold-start-channel-selector.ts +423 -0
  359. package/src/i18n/messages/en/tools/commercial-bp-generator.ts +304 -0
  360. package/src/i18n/messages/en/tools/community-feedback-inbox.ts +218 -0
  361. package/src/i18n/messages/en/tools/community-pain-point-extractor.ts +230 -0
  362. package/src/i18n/messages/en/tools/community-post-risk-checker.ts +209 -0
  363. package/src/i18n/messages/en/tools/competitor-analysis.ts +197 -0
  364. package/src/i18n/messages/en/tools/email-template-generator.ts +302 -0
  365. package/src/i18n/messages/en/tools/engine.ts +69 -0
  366. package/src/i18n/messages/en/tools/gtm-readiness-checklist.ts +166 -0
  367. package/src/i18n/messages/en/tools/index.ts +73 -0
  368. package/src/i18n/messages/en/tools/koko-codex-assistant.ts +171 -0
  369. package/src/i18n/messages/en/tools/logo-generator.ts +173 -0
  370. package/src/i18n/messages/en/tools/market-forecast-report.ts +228 -0
  371. package/src/i18n/messages/en/tools/million-user-growth-test.ts +386 -0
  372. package/src/i18n/messages/en/tools/og-image-generator.ts +172 -0
  373. package/src/i18n/messages/en/tools/pitch-deck-generator.ts +358 -0
  374. package/src/i18n/messages/en/tools/product-agreement-generator.ts +411 -0
  375. package/src/i18n/messages/en/tools/product-hunt-launch-planner.ts +272 -0
  376. package/src/i18n/messages/en/tools/product-name-generator.ts +383 -0
  377. package/src/i18n/messages/en/tools/product-poster-generator.ts +294 -0
  378. package/src/i18n/messages/en/tools/product-ui-generator.ts +234 -0
  379. package/src/i18n/messages/en/tools/reddit-pain-point-finder.ts +163 -0
  380. package/src/i18n/messages/en/tools/shared.ts +24 -0
  381. package/src/i18n/messages/en/tools/social-listening-research-report.ts +185 -0
  382. package/src/i18n/messages/en/tools/social-media-profile-generator.ts +22 -0
  383. package/src/i18n/messages/en/tools/social-platform-asset-adapter.ts +287 -0
  384. package/src/i18n/messages/en/tools/social-profile-kit.ts +150 -0
  385. package/src/i18n/messages/en/tools/startup-idea-research.ts +170 -0
  386. package/src/i18n/messages/en/tools/tarot-reading.ts +230 -0
  387. package/src/i18n/messages/en/tools/twitter-gif-downloader.ts +145 -0
  388. package/src/i18n/messages/en/tools/user-interview-insight-report-generator.ts +242 -0
  389. package/src/i18n/messages/en/tools/user-interview-record-generator.ts +244 -0
  390. package/src/i18n/messages/en/tools-ui.ts +704 -0
  391. package/src/i18n/messages/zh/page/tools.ts +124 -0
  392. package/src/i18n/messages/zh/tools/app-icon-resizer.ts +242 -0
  393. package/src/i18n/messages/zh/tools/app-store-screenshot-generator.ts +145 -0
  394. package/src/i18n/messages/zh/tools/build-in-public-story-generator.ts +230 -0
  395. package/src/i18n/messages/zh/tools/cold-start-channel-selector.ts +422 -0
  396. package/src/i18n/messages/zh/tools/commercial-bp-generator.ts +302 -0
  397. package/src/i18n/messages/zh/tools/community-feedback-inbox.ts +217 -0
  398. package/src/i18n/messages/zh/tools/community-pain-point-extractor.ts +230 -0
  399. package/src/i18n/messages/zh/tools/community-post-risk-checker.ts +200 -0
  400. package/src/i18n/messages/zh/tools/competitor-analysis.ts +192 -0
  401. package/src/i18n/messages/zh/tools/email-template-generator.ts +302 -0
  402. package/src/i18n/messages/zh/tools/engine.ts +68 -0
  403. package/src/i18n/messages/zh/tools/gtm-readiness-checklist.ts +166 -0
  404. package/src/i18n/messages/zh/tools/index.ts +73 -0
  405. package/src/i18n/messages/zh/tools/koko-codex-assistant.ts +170 -0
  406. package/src/i18n/messages/zh/tools/logo-generator.ts +172 -0
  407. package/src/i18n/messages/zh/tools/market-forecast-report.ts +228 -0
  408. package/src/i18n/messages/zh/tools/million-user-growth-test.ts +385 -0
  409. package/src/i18n/messages/zh/tools/og-image-generator.ts +173 -0
  410. package/src/i18n/messages/zh/tools/pitch-deck-generator.ts +358 -0
  411. package/src/i18n/messages/zh/tools/product-agreement-generator.ts +401 -0
  412. package/src/i18n/messages/zh/tools/product-hunt-launch-planner.ts +267 -0
  413. package/src/i18n/messages/zh/tools/product-name-generator.ts +383 -0
  414. package/src/i18n/messages/zh/tools/product-poster-generator.ts +282 -0
  415. package/src/i18n/messages/zh/tools/product-ui-generator.ts +234 -0
  416. package/src/i18n/messages/zh/tools/reddit-pain-point-finder.ts +162 -0
  417. package/src/i18n/messages/zh/tools/shared.ts +24 -0
  418. package/src/i18n/messages/zh/tools/social-listening-research-report.ts +181 -0
  419. package/src/i18n/messages/zh/tools/social-media-profile-generator.ts +22 -0
  420. package/src/i18n/messages/zh/tools/social-platform-asset-adapter.ts +284 -0
  421. package/src/i18n/messages/zh/tools/social-profile-kit.ts +150 -0
  422. package/src/i18n/messages/zh/tools/startup-idea-research.ts +165 -0
  423. package/src/i18n/messages/zh/tools/tarot-reading.ts +228 -0
  424. package/src/i18n/messages/zh/tools/twitter-gif-downloader.ts +138 -0
  425. package/src/i18n/messages/zh/tools/user-interview-insight-report-generator.ts +224 -0
  426. package/src/i18n/messages/zh/tools/user-interview-record-generator.ts +226 -0
  427. package/src/i18n/messages/zh/tools-ui.ts +684 -0
  428. package/src/i18n/messages/zh-tw/page/tools.ts +115 -0
  429. package/src/i18n/messages/zh-tw/tools/app-icon-resizer.ts +242 -0
  430. package/src/i18n/messages/zh-tw/tools/app-store-screenshot-generator.ts +145 -0
  431. package/src/i18n/messages/zh-tw/tools/build-in-public-story-generator.ts +230 -0
  432. package/src/i18n/messages/zh-tw/tools/cold-start-channel-selector.ts +440 -0
  433. package/src/i18n/messages/zh-tw/tools/commercial-bp-generator.ts +302 -0
  434. package/src/i18n/messages/zh-tw/tools/community-feedback-inbox.ts +217 -0
  435. package/src/i18n/messages/zh-tw/tools/community-pain-point-extractor.ts +230 -0
  436. package/src/i18n/messages/zh-tw/tools/community-post-risk-checker.ts +200 -0
  437. package/src/i18n/messages/zh-tw/tools/competitor-analysis.ts +192 -0
  438. package/src/i18n/messages/zh-tw/tools/email-template-generator.ts +302 -0
  439. package/src/i18n/messages/zh-tw/tools/engine.ts +68 -0
  440. package/src/i18n/messages/zh-tw/tools/gtm-readiness-checklist.ts +166 -0
  441. package/src/i18n/messages/zh-tw/tools/index.ts +73 -0
  442. package/src/i18n/messages/zh-tw/tools/koko-codex-assistant.ts +170 -0
  443. package/src/i18n/messages/zh-tw/tools/logo-generator.ts +71 -0
  444. package/src/i18n/messages/zh-tw/tools/market-forecast-report.ts +228 -0
  445. package/src/i18n/messages/zh-tw/tools/million-user-growth-test.ts +385 -0
  446. package/src/i18n/messages/zh-tw/tools/og-image-generator.ts +173 -0
  447. package/src/i18n/messages/zh-tw/tools/pitch-deck-generator.ts +358 -0
  448. package/src/i18n/messages/zh-tw/tools/product-agreement-generator.ts +401 -0
  449. package/src/i18n/messages/zh-tw/tools/product-hunt-launch-planner.ts +267 -0
  450. package/src/i18n/messages/zh-tw/tools/product-name-generator.ts +383 -0
  451. package/src/i18n/messages/zh-tw/tools/product-poster-generator.ts +282 -0
  452. package/src/i18n/messages/zh-tw/tools/product-ui-generator.ts +234 -0
  453. package/src/i18n/messages/zh-tw/tools/reddit-pain-point-finder.ts +162 -0
  454. package/src/i18n/messages/zh-tw/tools/shared.ts +24 -0
  455. package/src/i18n/messages/zh-tw/tools/social-listening-research-report.ts +181 -0
  456. package/src/i18n/messages/zh-tw/tools/social-media-profile-generator.ts +22 -0
  457. package/src/i18n/messages/zh-tw/tools/social-platform-asset-adapter.ts +284 -0
  458. package/src/i18n/messages/zh-tw/tools/social-profile-kit.ts +150 -0
  459. package/src/i18n/messages/zh-tw/tools/startup-idea-research.ts +165 -0
  460. package/src/i18n/messages/zh-tw/tools/tarot-reading.ts +228 -0
  461. package/src/i18n/messages/zh-tw/tools/twitter-gif-downloader.ts +138 -0
  462. package/src/i18n/messages/zh-tw/tools/user-interview-insight-report-generator.ts +224 -0
  463. package/src/i18n/messages/zh-tw/tools/user-interview-record-generator.ts +226 -0
  464. package/src/i18n/messages/zh-tw/tools-ui.ts +525 -0
  465. package/src/index.ts +15 -0
  466. package/src/lib/activity-task-handoff.ts +404 -0
  467. package/src/lib/api/errors.ts +39 -0
  468. package/src/lib/app-origin.ts +30 -0
  469. package/src/lib/fact-claim-contract.ts +68 -0
  470. package/src/lib/fake-delay.ts +3 -0
  471. package/src/lib/file/download.ts +10 -0
  472. package/src/lib/routing/locale-paths.ts +27 -0
  473. package/src/lib/seo-meta.ts +3 -0
  474. package/src/lib/tools/app-icon-resizer-schema.ts +191 -0
  475. package/src/lib/tools/app-store-screenshot-generator-schema.ts +185 -0
  476. package/src/lib/tools/build-in-public-story-generator-schema.ts +67 -0
  477. package/src/lib/tools/build-in-public-story-generator.format.ts +507 -0
  478. package/src/lib/tools/cold-start-channel-selector-schema.ts +147 -0
  479. package/src/lib/tools/commercial-bp-generator-schema.ts +222 -0
  480. package/src/lib/tools/community-feedback-inbox-schema.ts +125 -0
  481. package/src/lib/tools/community-feedback-inbox.format.ts +764 -0
  482. package/src/lib/tools/community-pain-point-extractor-schema.ts +166 -0
  483. package/src/lib/tools/community-post-risk-checker-schema.ts +99 -0
  484. package/src/lib/tools/community-post-risk-checker.format.ts +557 -0
  485. package/src/lib/tools/competitor-analysis-schema.ts +144 -0
  486. package/src/lib/tools/draft-preview-access.ts +3 -0
  487. package/src/lib/tools/draft-store.ts +69 -0
  488. package/src/lib/tools/email-template-generator-schema.ts +124 -0
  489. package/src/lib/tools/gtm-checklist-copy.ts +57 -0
  490. package/src/lib/tools/gtm-checklist-structure.ts +37 -0
  491. package/src/lib/tools/image-download.ts +68 -0
  492. package/src/lib/tools/input-quality.ts +97 -0
  493. package/src/lib/tools/logo-generator-schema.ts +224 -0
  494. package/src/lib/tools/market-forecast-report-schema.ts +299 -0
  495. package/src/lib/tools/mcp-registry.ts +16 -0
  496. package/src/lib/tools/mcp-types.ts +18 -0
  497. package/src/lib/tools/million-user-growth-test-schema.ts +49 -0
  498. package/src/lib/tools/million-user-growth-test.ts +181 -0
  499. package/src/lib/tools/og-image-generator-render.ts +448 -0
  500. package/src/lib/tools/og-image-generator-schema.ts +140 -0
  501. package/src/lib/tools/ops-catalog.ts +102 -0
  502. package/src/lib/tools/pitch-deck-generator-schema.ts +182 -0
  503. package/src/lib/tools/pitch-deck-package.ts +0 -0
  504. package/src/lib/tools/product-agreement-generator-schema.ts +168 -0
  505. package/src/lib/tools/product-hunt-launch-planner-schema.ts +136 -0
  506. package/src/lib/tools/product-hunt-launch-planner.format.ts +72 -0
  507. package/src/lib/tools/product-name-generator-schema.ts +563 -0
  508. package/src/lib/tools/product-poster-generator-schema.ts +215 -0
  509. package/src/lib/tools/product-ui-generator-schema.ts +157 -0
  510. package/src/lib/tools/reddit-pain-point-finder-schema.ts +88 -0
  511. package/src/lib/tools/resolve-tool-locale-copy.ts +75 -0
  512. package/src/lib/tools/slug.ts +60 -0
  513. package/src/lib/tools/social-listening-research-report-schema.ts +105 -0
  514. package/src/lib/tools/social-platform-asset-adapter-schema.ts +152 -0
  515. package/src/lib/tools/social-profile-kit.constants.ts +12 -0
  516. package/src/lib/tools/social-profile-kit.generation.ts +257 -0
  517. package/src/lib/tools/social-profile-kit.input.ts +290 -0
  518. package/src/lib/tools/social-profile-kit.manual.ts +481 -0
  519. package/src/lib/tools/social-profile-kit.package.ts +220 -0
  520. package/src/lib/tools/social-profile-kit.schema.ts +328 -0
  521. package/src/lib/tools/startup-idea-research-schema.ts +107 -0
  522. package/src/lib/tools/tarot-imagery.ts +89 -0
  523. package/src/lib/tools/tarot-reading.ts +1044 -0
  524. package/src/lib/tools/tool-message-keys.ts +18 -0
  525. package/src/lib/tools/types.ts +32 -0
  526. package/src/lib/tools/user-interview.format.ts +147 -0
  527. package/src/lib/tools/user-interview.generation.ts +670 -0
  528. package/src/lib/tools/user-interview.schema.ts +192 -0
  529. package/src/lib/zip.ts +106 -0
  530. package/src/services/tools/handlers/gtm-readiness-checklist.ts +71 -0
  531. package/src/services/tools/handlers/types.ts +7 -0
@@ -0,0 +1,846 @@
1
+ import type { AppLocale } from "../../../i18n/locale";
2
+ import { z } from "zod";
3
+
4
+ import type { ToolPackage } from "../package/types";
5
+
6
+ /**
7
+ * UTM Builder(instant 车道首个正式工具)。
8
+ * 证据:docs/seo-keyword-briefs/utm-builder.md(utm builder 12,100/mo LOW + 竞品拆解)。
9
+ * 反超点(相对 ga-dev-tools / utmbuilder.net / dashthis / tilda):
10
+ * 输入自动规范化(空格→连字符、可选小写归一、已有 utm 参数覆盖警告、协议自动补全),
11
+ * 修复项以 warnings 显式输出;URL 纯浏览器本地计算,不上传服务器。
12
+ */
13
+ const resultSchema = z.object({
14
+ finalUrl: z.string().min(1),
15
+ params: z.object({
16
+ source: z.string().min(1),
17
+ medium: z.string().min(1),
18
+ campaign: z.string().min(1),
19
+ term: z.string().optional(),
20
+ content: z.string().optional(),
21
+ }),
22
+ warnings: z.array(z.string()),
23
+ });
24
+
25
+ const UTM_KEYS = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"] as const;
26
+
27
+ type WarnLocale = "en" | "zh" | "zh-tw";
28
+
29
+ const WARN_TEXT: Record<string, Record<WarnLocale, string>> = {
30
+ schemeAdded: {
31
+ en: "The destination URL had no protocol, so https:// was added automatically.",
32
+ zh: "目标 URL 缺少协议,已自动补全 https:// 前缀。",
33
+ "zh-tw": "目標 URL 缺少協定,已自動補全 https:// 前綴。",
34
+ },
35
+ existingUtmReplaced: {
36
+ en: "The URL already contained UTM parameters ({keys}); they were replaced with your new values.",
37
+ zh: "URL 中已存在 UTM 参数({keys}),已用你填写的新值覆盖。",
38
+ "zh-tw": "URL 中已存在 UTM 參數({keys}),已用你填寫的新值覆蓋。",
39
+ },
40
+ spacesReplaced: {
41
+ en: "Spaces in {field} were replaced with hyphens so analytics tools read one clean value.",
42
+ zh: "{field} 中的空格已替换为连字符,避免统计工具把值截断。",
43
+ "zh-tw": "{field} 中的空格已替換為連字號,避免統計工具把值截斷。",
44
+ },
45
+ lowercased: {
46
+ en: "Uppercase letters in {field} were converted to lowercase to keep report values consistent (google ≠ Google in GA4).",
47
+ zh: "{field} 中的大写字母已转为小写,避免报表把 google 和 Google 统计成两个渠道。",
48
+ "zh-tw": "{field} 中的大寫字母已轉為小寫,避免報表把 google 和 Google 統計成兩個管道。",
49
+ },
50
+ fragmentKept: {
51
+ en: "The #fragment was kept at the very end of the URL, after all UTM parameters.",
52
+ zh: "URL 中的 # 锚点已保留在最末尾,UTM 参数插在它之前。",
53
+ "zh-tw": "URL 中的 # 錨點已保留在最末尾,UTM 參數插在它之前。",
54
+ },
55
+ };
56
+
57
+ const FIELD_NAME: Record<string, Record<WarnLocale, string>> = {
58
+ source: { en: "utm_source", zh: "utm_source", "zh-tw": "utm_source" },
59
+ medium: { en: "utm_medium", zh: "utm_medium", "zh-tw": "utm_medium" },
60
+ campaign: { en: "utm_campaign", zh: "utm_campaign", "zh-tw": "utm_campaign" },
61
+ term: { en: "utm_term", zh: "utm_term", "zh-tw": "utm_term" },
62
+ content: { en: "utm_content", zh: "utm_content", "zh-tw": "utm_content" },
63
+ };
64
+
65
+ function toWarnLocale(locale: AppLocale): WarnLocale {
66
+ if (locale === "en") return "en";
67
+ if (locale === "zh-tw") return "zh-tw";
68
+ return "zh";
69
+ }
70
+
71
+ function warn(key: keyof typeof WARN_TEXT, locale: AppLocale, vars?: Record<string, string>): string {
72
+ const l = toWarnLocale(locale);
73
+ let text = WARN_TEXT[key][l];
74
+ if (vars) {
75
+ for (const [name, value] of Object.entries(vars)) {
76
+ text = text.replace(`{${name}}`, value);
77
+ }
78
+ }
79
+ return text;
80
+ }
81
+
82
+ export const utmBuilderPackage: ToolPackage = {
83
+ meta: {
84
+ slug: "utm-builder",
85
+ toolType: "generator",
86
+ keywordEvidence: "docs/seo-keyword-briefs/utm-builder.md",
87
+ seo: {
88
+ updatedAt: "2026-07-04",
89
+ readingTime: "6 min",
90
+ ogImage: "/tools/utm-builder-hero.webp",
91
+ },
92
+ },
93
+ copy: {
94
+ title: { en: "UTM Builder", zh: "UTM 链接生成器", "zh-tw": "UTM 連結生成器" },
95
+ description: {
96
+ en: "Free UTM builder and campaign URL generator: add utm_source, utm_medium, and utm_campaign to any link and get a clean, trackable URL for GA4 — auto-normalized, built entirely in your browser.",
97
+ zh: "免费 UTM 链接生成器:填写目标 URL 和来源、媒介、活动名,立即得到兼容 GA4 的追踪链接。输入自动规范化,全部在浏览器本地完成。",
98
+ "zh-tw": "免費 UTM 連結生成器:填寫目標 URL 和來源、媒介、活動名,立即得到兼容 GA4 的追蹤連結。輸入自動規範化,全部在瀏覽器本地完成。",
99
+ },
100
+ intro: {
101
+ en: "Hand-editing campaign URLs breaks tracking: a second question mark, a stray space, or Google vs google splits your GA4 reports. This UTM generator validates the URL, normalizes every parameter, and tells you exactly what it fixed.",
102
+ zh: "手拼追踪链接很容易翻车:多一个问号、混入空格、大小写不一致,GA4 报表就会把同一渠道拆成两条。这个 UTM 生成器会校验 URL、规范化每个参数,并明确告诉你修复了什么。",
103
+ "zh-tw":
104
+ "手拼追蹤連結很容易出錯:多一個問號、混入空格、大小寫不一致,GA4 報表就會把同一管道拆成兩條。這個 UTM 生成器會校驗 URL、規範化每個參數,並明確告訴你修復了什麼。",
105
+ },
106
+ category: {
107
+ en: "Marketing tools",
108
+ zh: "营销工具",
109
+ "zh-tw": "行銷工具",
110
+ },
111
+ keywords: {
112
+ en: ["utm builder", "utm generator", "utm maker", "campaign url builder", "utm code generator", "utm link builder", "utm url builder", "free utm builder", "google utm builder"],
113
+ zh: ["UTM 链接生成器", "UTM 参数生成", "UTM 制作工具", "营销活动链接生成", "UTM 追踪代码", "UTM 追踪链接", "UTM URL 生成", "免费 UTM 工具", "GA4 UTM"],
114
+ "zh-tw": ["UTM 連結生成器", "UTM 參數生成", "UTM 製作工具", "行銷活動連結生成", "UTM 追蹤代碼", "UTM 追蹤連結", "UTM URL 生成", "免費 UTM 工具", "GA4 UTM"],
115
+ },
116
+ inputSignal: {
117
+ en: "Destination URL + source / medium / campaign",
118
+ zh: "目标 URL + 来源 / 媒介 / 活动名",
119
+ "zh-tw": "目標 URL + 來源 / 媒介 / 活動名",
120
+ },
121
+ outputSignal: {
122
+ en: "Copy-ready campaign URL + auto-fix notes",
123
+ zh: "可直接复制的追踪链接 + 自动修复说明",
124
+ "zh-tw": "可直接複製的追蹤連結 + 自動修復說明",
125
+ },
126
+ boundarySignal: {
127
+ en: "Builds and normalizes links locally; it does not shorten URLs or report clicks",
128
+ zh: "本地生成并规范化链接;不做短链,也不统计点击",
129
+ "zh-tw": "本地生成並規範化連結;不做短鏈,也不統計點擊",
130
+ },
131
+ },
132
+ form: [
133
+ {
134
+ kind: "text",
135
+ id: "websiteUrl",
136
+ required: true,
137
+ minLength: 4,
138
+ fullWidth: true,
139
+ label: { en: "Destination URL", zh: "目标 URL", "zh-tw": "目標 URL" },
140
+ hint: {
141
+ en: "The landing page the ad, email, or post should open",
142
+ zh: "广告、邮件或帖子要打开的落地页",
143
+ "zh-tw": "廣告、郵件或貼文要打開的到達網頁",
144
+ },
145
+ placeholder: { en: "https://tomako.ai/pricing", zh: "https://tomako.ai/pricing", "zh-tw": "https://tomako.ai/pricing" },
146
+ invalidMessage: {
147
+ en: "Paste the full landing page URL, e.g. https://example.com/pricing.",
148
+ zh: "请粘贴完整的落地页 URL,例如 https://example.com/pricing。",
149
+ "zh-tw": "請貼上完整的到達網頁 URL,例如 https://example.com/pricing。",
150
+ },
151
+ },
152
+ {
153
+ kind: "text",
154
+ id: "source",
155
+ required: true,
156
+ label: { en: "Campaign source (utm_source)", zh: "流量来源 utm_source", "zh-tw": "流量來源 utm_source" },
157
+ hint: {
158
+ en: "Where the traffic comes from",
159
+ zh: "流量从哪个平台来",
160
+ "zh-tw": "流量從哪個平台來",
161
+ },
162
+ placeholder: { en: "google / newsletter / twitter", zh: "google / newsletter / twitter", "zh-tw": "google / newsletter / twitter" },
163
+ invalidMessage: {
164
+ en: "Name the traffic source, e.g. google, newsletter, or twitter.",
165
+ zh: "请填写流量来源,例如 google、newsletter 或 twitter。",
166
+ "zh-tw": "請填寫流量來源,例如 google、newsletter 或 twitter。",
167
+ },
168
+ },
169
+ {
170
+ kind: "text",
171
+ id: "medium",
172
+ required: true,
173
+ label: { en: "Campaign medium (utm_medium)", zh: "营销媒介 utm_medium", "zh-tw": "行銷媒介 utm_medium" },
174
+ hint: {
175
+ en: "Use GA4-recognized values so traffic lands in the right channel group: cpc, email, social, referral, display, affiliate",
176
+ zh: "建议用 GA4 渠道分组认可的标准值:cpc、email、social、referral、display、affiliate",
177
+ "zh-tw": "建議用 GA4 管道群組認可的標準值:cpc、email、social、referral、display、affiliate",
178
+ },
179
+ placeholder: { en: "cpc / email / social", zh: "cpc / email / social", "zh-tw": "cpc / email / social" },
180
+ invalidMessage: {
181
+ en: "Name the channel type, e.g. cpc, email, or social.",
182
+ zh: "请填写渠道类型,例如 cpc、email 或 social。",
183
+ "zh-tw": "請填寫管道類型,例如 cpc、email 或 social。",
184
+ },
185
+ },
186
+ {
187
+ kind: "text",
188
+ id: "campaign",
189
+ required: true,
190
+ label: { en: "Campaign name (utm_campaign)", zh: "活动名称 utm_campaign", "zh-tw": "活動名稱 utm_campaign" },
191
+ hint: {
192
+ en: "What this push is called in reports",
193
+ zh: "这次投放在报表里叫什么",
194
+ "zh-tw": "這次投放在報表裡叫什麼",
195
+ },
196
+ placeholder: { en: "summer-sale", zh: "summer-sale", "zh-tw": "summer-sale" },
197
+ invalidMessage: {
198
+ en: "Name the campaign, e.g. summer-sale or launch-week.",
199
+ zh: "请填写活动名称,例如 summer-sale 或 launch-week。",
200
+ "zh-tw": "請填寫活動名稱,例如 summer-sale 或 launch-week。",
201
+ },
202
+ },
203
+ {
204
+ kind: "text",
205
+ id: "term",
206
+ advanced: true,
207
+ label: { en: "Paid keyword (utm_term, optional)", zh: "付费关键词 utm_term(选填)", "zh-tw": "付費關鍵詞 utm_term(選填)" },
208
+ hint: {
209
+ en: "Only for paid search: the keyword you bid on",
210
+ zh: "只在付费搜索时填:你出价的关键词",
211
+ "zh-tw": "只在付費搜尋時填:你出價的關鍵詞",
212
+ },
213
+ placeholder: { en: "running-shoes", zh: "running-shoes", "zh-tw": "running-shoes" },
214
+ },
215
+ {
216
+ kind: "text",
217
+ id: "content",
218
+ advanced: true,
219
+ label: { en: "Ad / link variant (utm_content, optional)", zh: "素材版本 utm_content(选填)", "zh-tw": "素材版本 utm_content(選填)" },
220
+ hint: {
221
+ en: "Tells two creatives or link positions apart in the same campaign",
222
+ zh: "区分同一活动里的不同素材或链接位置",
223
+ "zh-tw": "區分同一活動裡的不同素材或連結位置",
224
+ },
225
+ placeholder: { en: "hero-banner", zh: "hero-banner", "zh-tw": "hero-banner" },
226
+ },
227
+ {
228
+ kind: "toggle",
229
+ id: "lowercase",
230
+ advanced: true,
231
+ defaultValue: true,
232
+ label: { en: "Force lowercase values", zh: "参数统一小写", "zh-tw": "參數統一小寫" },
233
+ hint: {
234
+ en: "Recommended: GA4 treats Google and google as two different sources",
235
+ zh: "建议开启:GA4 会把 Google 和 google 当成两个来源",
236
+ "zh-tw": "建議開啟:GA4 會把 Google 和 google 當成兩個來源",
237
+ },
238
+ onLabel: { en: "On", zh: "开启", "zh-tw": "開啟" },
239
+ offLabel: { en: "Off", zh: "关闭", "zh-tw": "關閉" },
240
+ },
241
+ ],
242
+ runtime: {
243
+ lane: "instant",
244
+ compute: (values, locale) => {
245
+ const warnings: string[] = [];
246
+
247
+ let rawUrl = String(values.websiteUrl ?? "").trim();
248
+ const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(rawUrl);
249
+ if (!hasScheme) {
250
+ rawUrl = `https://${rawUrl}`;
251
+ warnings.push(warn("schemeAdded", locale));
252
+ }
253
+
254
+ let url: URL;
255
+ try {
256
+ url = new URL(rawUrl);
257
+ } catch {
258
+ throw new Error("invalid destination URL");
259
+ }
260
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
261
+ throw new Error("unsupported protocol");
262
+ }
263
+ if (!url.hostname.includes(".") && url.hostname !== "localhost") {
264
+ throw new Error("hostname looks incomplete");
265
+ }
266
+
267
+ const replacedKeys = UTM_KEYS.filter((key) => url.searchParams.has(key));
268
+ if (replacedKeys.length > 0) {
269
+ warnings.push(warn("existingUtmReplaced", locale, { keys: replacedKeys.join(", ") }));
270
+ for (const key of replacedKeys) url.searchParams.delete(key);
271
+ }
272
+
273
+ const forceLowercase = values.lowercase !== false;
274
+ const spacesFixed: string[] = [];
275
+ const caseFixed: string[] = [];
276
+ const fieldLocale = toWarnLocale(locale);
277
+
278
+ const normalize = (field: keyof typeof FIELD_NAME, raw: unknown): string => {
279
+ let value = String(raw ?? "").trim();
280
+ if (!value) return "";
281
+ if (/\s/.test(value)) {
282
+ value = value.replace(/\s+/g, "-");
283
+ spacesFixed.push(FIELD_NAME[field][fieldLocale]);
284
+ }
285
+ if (forceLowercase && /[A-Z]/.test(value)) {
286
+ value = value.toLowerCase();
287
+ caseFixed.push(FIELD_NAME[field][fieldLocale]);
288
+ }
289
+ return value;
290
+ };
291
+
292
+ const params = {
293
+ source: normalize("source", values.source),
294
+ medium: normalize("medium", values.medium),
295
+ campaign: normalize("campaign", values.campaign),
296
+ term: normalize("term", values.term) || undefined,
297
+ content: normalize("content", values.content) || undefined,
298
+ };
299
+
300
+ if (spacesFixed.length > 0) {
301
+ warnings.push(warn("spacesReplaced", locale, { field: spacesFixed.join(", ") }));
302
+ }
303
+ if (caseFixed.length > 0) {
304
+ warnings.push(warn("lowercased", locale, { field: caseFixed.join(", ") }));
305
+ }
306
+
307
+ url.searchParams.set("utm_source", params.source);
308
+ url.searchParams.set("utm_medium", params.medium);
309
+ url.searchParams.set("utm_campaign", params.campaign);
310
+ if (params.term) url.searchParams.set("utm_term", params.term);
311
+ if (params.content) url.searchParams.set("utm_content", params.content);
312
+
313
+ if (url.hash) {
314
+ warnings.push(warn("fragmentKept", locale));
315
+ }
316
+
317
+ return {
318
+ finalUrl: url.toString(),
319
+ params,
320
+ warnings,
321
+ };
322
+ },
323
+ },
324
+ resultSchema,
325
+ resultView: [
326
+ {
327
+ block: "keyValues",
328
+ title: { en: "Your campaign URL", zh: "你的追踪链接", "zh-tw": "你的追蹤連結" },
329
+ items: [
330
+ {
331
+ label: { en: "Tracking link", zh: "UTM 链接", "zh-tw": "UTM 連結" },
332
+ path: "finalUrl",
333
+ copyable: true,
334
+ },
335
+ ],
336
+ },
337
+ {
338
+ block: "keyValues",
339
+ title: { en: "Parameter breakdown", zh: "参数逐项预览", "zh-tw": "參數逐項預覽" },
340
+ items: [
341
+ { label: { en: "utm_source", zh: "utm_source", "zh-tw": "utm_source" }, path: "params.source" },
342
+ { label: { en: "utm_medium", zh: "utm_medium", "zh-tw": "utm_medium" }, path: "params.medium" },
343
+ { label: { en: "utm_campaign", zh: "utm_campaign", "zh-tw": "utm_campaign" }, path: "params.campaign" },
344
+ { label: { en: "utm_term", zh: "utm_term", "zh-tw": "utm_term" }, path: "params.term" },
345
+ { label: { en: "utm_content", zh: "utm_content", "zh-tw": "utm_content" }, path: "params.content" },
346
+ ],
347
+ },
348
+ {
349
+ block: "bulletList",
350
+ title: { en: "Auto-fixes applied", zh: "自动修复说明", "zh-tw": "自動修復說明" },
351
+ path: "warnings",
352
+ tone: "risk",
353
+ },
354
+ ],
355
+ widget: {
356
+ formTitle: { en: "Build a trackable campaign link", zh: "生成可追踪的活动链接", "zh-tw": "生成可追蹤的活動連結" },
357
+ formHint: {
358
+ en: "Fill in the destination URL and where the link will run. The tracking link is generated instantly in your browser — the URL never leaves this page.",
359
+ zh: "填写落地页 URL 和投放位置,追踪链接会在浏览器里即时生成——URL 不会被上传到任何服务器。",
360
+ "zh-tw": "填寫到達網頁 URL 和投放位置,追蹤連結會在瀏覽器裡即時生成——URL 不會被上傳到任何伺服器。",
361
+ },
362
+ submitLabel: { en: "Generate UTM link", zh: "生成 UTM 链接", "zh-tw": "生成 UTM 連結" },
363
+ resultTitle: { en: "Copy your tracking link", zh: "复制你的追踪链接", "zh-tw": "複製你的追蹤連結" },
364
+ errorOverrides: {
365
+ computeFailed: {
366
+ title: { en: "Check the destination URL", zh: "请检查目标 URL", "zh-tw": "請檢查目標 URL" },
367
+ body: {
368
+ en: "The destination URL could not be parsed. Use a full web address like https://example.com/pricing — other protocols such as mailto: or ftp: are not supported.",
369
+ zh: "目标 URL 无法解析。请使用完整的网页地址,例如 https://example.com/pricing;不支持 mailto:、ftp: 等其他协议。",
370
+ "zh-tw": "目標 URL 無法解析。請使用完整的網址,例如 https://example.com/pricing;不支援 mailto:、ftp: 等其他協定。",
371
+ },
372
+ },
373
+ },
374
+ },
375
+ page: {
376
+ workspace: {
377
+ heading: {
378
+ en: "Tag the link before you launch the campaign",
379
+ zh: "投放前先给链接打上追踪参数",
380
+ "zh-tw": "投放前先給連結打上追蹤參數",
381
+ },
382
+ hint: {
383
+ en: "Enter the landing page and name the source, medium, and campaign. You get a copy-ready campaign URL with every parameter validated, normalized, and explained — no sign-up, no waiting.",
384
+ zh: "填写落地页地址,写清来源、媒介和活动名,立即得到可复制的追踪链接;每个参数都会被校验、规范化并说明修复原因。无需注册,无需等待。",
385
+ "zh-tw": "填寫到達網頁的網址,寫清來源、媒介和活動名,立即得到可複製的追蹤連結;每個參數都會被校驗、規範化並說明修復原因。無需註冊,無需等待。",
386
+ },
387
+ badge: { en: "Instant result", zh: "即时生成", "zh-tw": "即時生成" },
388
+ },
389
+ sections: [
390
+ {
391
+ kind: "cards",
392
+ heading: {
393
+ en: "What you get from this UTM generator",
394
+ zh: "这个 UTM 生成器会给你什么",
395
+ "zh-tw": "這個 UTM 生成器會給你什麼",
396
+ },
397
+ items: [
398
+ {
399
+ title: { en: "A copy-ready tracking link", zh: "可直接复制的追踪链接", "zh-tw": "可直接複製的追蹤連結" },
400
+ body: {
401
+ en: "One click copies the final campaign URL, ready to paste into Google Ads, a newsletter, or a social post.",
402
+ zh: "一键复制最终链接,直接粘贴到 Google Ads、邮件或社媒帖子里投放。",
403
+ "zh-tw": "一鍵複製最終連結,直接貼上到 Google Ads、郵件或社群貼文裡投放。",
404
+ },
405
+ },
406
+ {
407
+ title: { en: "Parameter-by-parameter preview", zh: "参数逐项预览", "zh-tw": "參數逐項預覽" },
408
+ body: {
409
+ en: "See the final utm_source, utm_medium, utm_campaign, utm_term, and utm_content values so the team can check naming consistency before launch.",
410
+ zh: "逐项展示 utm_source、utm_medium、utm_campaign、utm_term、utm_content 的最终值,团队投放前核对命名是否一致。",
411
+ "zh-tw": "逐項展示 utm_source、utm_medium、utm_campaign、utm_term、utm_content 的最終值,團隊投放前核對命名是否一致。",
412
+ },
413
+ },
414
+ {
415
+ title: { en: "Auto-fix notes, not silent edits", zh: "显式的自动修复说明", "zh-tw": "顯式的自動修復說明" },
416
+ body: {
417
+ en: "Spaces become hyphens, uppercase becomes lowercase, existing UTM tags get replaced — and every fix is listed so nothing changes behind your back.",
418
+ zh: "空格转连字符、大写转小写、旧 UTM 参数被覆盖——每一处修复都会列出来,不做暗改。",
419
+ "zh-tw": "空格轉連字號、大寫轉小寫、舊 UTM 參數被覆蓋——每一處修復都會列出來,不做暗改。",
420
+ },
421
+ },
422
+ {
423
+ title: { en: "A URL that stays valid", zh: "结构始终正确的 URL", "zh-tw": "結構始終正確的 URL" },
424
+ body: {
425
+ en: "Only one question mark, existing query strings preserved, #fragments kept at the end — the structural mistakes that break hand-built links are handled for you.",
426
+ zh: "只有一个问号、原有查询参数保留、# 锚点保持在末尾——手拼链接最常见的结构错误都被自动处理。",
427
+ "zh-tw": "只有一個問號、原有查詢參數保留、# 錨點保持在末尾——手拼連結最常見的結構錯誤都被自動處理。",
428
+ },
429
+ },
430
+ ],
431
+ },
432
+ {
433
+ kind: "cards",
434
+ heading: {
435
+ en: "What each UTM parameter means",
436
+ zh: "5 个 UTM 参数分别是什么",
437
+ "zh-tw": "5 個 UTM 參數分別是什麼",
438
+ },
439
+ items: [
440
+ {
441
+ title: { en: "utm_source — where", zh: "utm_source——从哪来", "zh-tw": "utm_source——從哪來" },
442
+ body: {
443
+ en: "The platform sending the traffic: google, newsletter, twitter. Required, and the first thing GA4 shows in acquisition reports.",
444
+ zh: "带来流量的平台:google、newsletter、twitter。必填,也是 GA4 流量获取报告里的第一列。",
445
+ "zh-tw": "帶來流量的平台:google、newsletter、twitter。必填,也是 GA4 流量獲取報告裡的第一欄。",
446
+ },
447
+ },
448
+ {
449
+ title: { en: "utm_medium — how", zh: "utm_medium——什么渠道", "zh-tw": "utm_medium——什麼管道" },
450
+ body: {
451
+ en: "The channel type: cpc for paid search, email, social, referral. GA4's default channel grouping matches on these exact values.",
452
+ zh: "渠道类型:付费搜索用 cpc,还有 email、social、referral。GA4 的默认渠道分组按这些标准值精确匹配。",
453
+ "zh-tw": "管道類型:付費搜尋用 cpc,還有 email、social、referral。GA4 的預設管道群組按這些標準值精確匹配。",
454
+ },
455
+ },
456
+ {
457
+ title: { en: "utm_campaign — which push", zh: "utm_campaign——哪次投放", "zh-tw": "utm_campaign——哪次投放" },
458
+ body: {
459
+ en: "The name of this specific promotion: launch-week-2026, summer-sale. Specific enough that you can still find it next quarter.",
460
+ zh: "这次投放的名字:launch-week-2026、summer-sale。要具体到下个季度还能找到它。",
461
+ "zh-tw": "這次投放的名字:launch-week-2026、summer-sale。要具體到下個季度還能找到它。",
462
+ },
463
+ },
464
+ {
465
+ title: { en: "utm_term — which keyword", zh: "utm_term——哪个关键词", "zh-tw": "utm_term——哪個關鍵詞" },
466
+ body: {
467
+ en: "Optional, for paid search only: the keyword you bid on, like running-shoes. Skip it for email and social links.",
468
+ zh: "选填,仅用于付费搜索:你出价的关键词,例如 running-shoes。邮件和社媒链接不需要填。",
469
+ "zh-tw": "選填,僅用於付費搜尋:你出價的關鍵詞,例如 running-shoes。郵件和社群連結不需要填。",
470
+ },
471
+ },
472
+ {
473
+ title: { en: "utm_content — which variant", zh: "utm_content——哪个版本", "zh-tw": "utm_content——哪個版本" },
474
+ body: {
475
+ en: "Optional: tells two creatives or link positions apart in the same campaign, like hero-banner vs footer-link, for A/B comparisons.",
476
+ zh: "选填:区分同一活动里的不同素材或链接位置,例如 hero-banner 和 footer-link,用于 A/B 对比。",
477
+ "zh-tw": "選填:區分同一活動裡的不同素材或連結位置,例如 hero-banner 和 footer-link,用於 A/B 對比。",
478
+ },
479
+ },
480
+ ],
481
+ },
482
+ {
483
+ kind: "bullets",
484
+ heading: {
485
+ en: "When you need a campaign URL builder",
486
+ zh: "什么时候需要活动链接生成器",
487
+ "zh-tw": "什麼時候需要活動連結生成器",
488
+ },
489
+ image: {
490
+ src: "/tools/utm-builder-support.webp",
491
+ alt: {
492
+ en: "Illustration of a marketer tagging links for ads, email, and social channels before a campaign launch.",
493
+ zh: "营销人员在活动上线前为广告、邮件和社媒渠道的链接打追踪参数的插画。",
494
+ "zh-tw": "行銷人員在活動上線前為廣告、郵件和社群管道的連結打追蹤參數的插畫。",
495
+ },
496
+ side: "right",
497
+ wide: true,
498
+ },
499
+ groups: [
500
+ {
501
+ items: {
502
+ en: [
503
+ "You are launching paid ads and need GA4 to attribute traffic to the right source and medium, like google / cpc.",
504
+ "You send newsletters and want email clicks separated from direct traffic instead of disappearing into (direct).",
505
+ "You post the same link on Twitter, LinkedIn, and communities and want to know which channel actually converts.",
506
+ "You run partner or influencer promotions and need each collaboration tracked with its own campaign name.",
507
+ "You print QR codes on offline material and want scans to show up as a measurable campaign.",
508
+ ],
509
+ zh: [
510
+ "你要投付费广告,需要 GA4 把流量正确归因到来源和媒介,比如 google / cpc。",
511
+ "你在发邮件 newsletter,想让邮件点击和直接访问分开,而不是全部混进 (direct)。",
512
+ "同一个链接要发 Twitter、LinkedIn 和社区,你想知道到底哪个渠道真的带来转化。",
513
+ "你在做合作推广或 KOL 投放,每个合作方需要独立的活动名来追踪效果。",
514
+ "你要在线下物料上印二维码,希望扫码流量也能作为可度量的活动出现在报表里。",
515
+ ],
516
+ "zh-tw": [
517
+ "你要投付費廣告,需要 GA4 把流量正確歸因到來源和媒介,比如 google / cpc。",
518
+ "你在發郵件 newsletter,想讓郵件點擊和直接訪問分開,而不是全部混進 (direct)。",
519
+ "同一個連結要發 Twitter、LinkedIn 和社群,你想知道到底哪個管道真的帶來轉化。",
520
+ "你在做合作推廣或 KOL 投放,每個合作方需要獨立的活動名來追蹤效果。",
521
+ "你要在線下物料上印 QR Code,希望掃碼流量也能作為可度量的活動出現在報表裡。",
522
+ ],
523
+ },
524
+ },
525
+ ],
526
+ },
527
+ {
528
+ kind: "bullets",
529
+ heading: {
530
+ en: "What this tool does and does not do",
531
+ zh: "这个工具做什么、不做什么",
532
+ "zh-tw": "這個工具做什麼、不做什麼",
533
+ },
534
+ image: {
535
+ src: "/tools/utm-builder-standard.webp",
536
+ alt: {
537
+ en: "Illustration of a clean tracking link passing checkpoints for structure, casing, and parameter consistency.",
538
+ zh: "追踪链接通过结构、大小写和参数一致性检查点的插画。",
539
+ "zh-tw": "追蹤連結通過結構、大小寫和參數一致性檢查點的插畫。",
540
+ },
541
+ side: "left",
542
+ wide: true,
543
+ },
544
+ groups: [
545
+ {
546
+ title: { en: "Supported", zh: "支持范围", "zh-tw": "支援範圍" },
547
+ items: {
548
+ en: [
549
+ "Any http or https landing page, including URLs that already carry other query parameters or a #fragment.",
550
+ "All five standard UTM parameters recognized by GA4, Google Ads, Mixpanel, Matomo, and most analytics tools.",
551
+ "Auto-normalization: protocol completion, space-to-hyphen, optional lowercase, and replacement of existing UTM tags with a warning.",
552
+ "Everything is computed locally in your browser — the URL is never uploaded, stored, or logged.",
553
+ ],
554
+ zh: [
555
+ "任何 http 或 https 落地页,包括已带其他查询参数或 # 锚点的 URL。",
556
+ "GA4、Google Ads、Mixpanel、Matomo 等主流统计工具都识别的 5 个标准 UTM 参数。",
557
+ "自动规范化:协议补全、空格转连字符、可选小写归一、覆盖旧 UTM 参数并给出警告。",
558
+ "全部在浏览器本地计算——URL 不会被上传、存储或记录。",
559
+ ],
560
+ "zh-tw": [
561
+ "任何 http 或 https 到達網頁,包括已帶其他查詢參數或 # 錨點的 URL。",
562
+ "GA4、Google Ads、Mixpanel、Matomo 等主流統計工具都識別的 5 個標準 UTM 參數。",
563
+ "自動規範化:協定補全、空格轉連字號、可選小寫歸一、覆蓋舊 UTM 參數並給出警告。",
564
+ "全部在瀏覽器本地計算——URL 不會被上傳、儲存或記錄。",
565
+ ],
566
+ },
567
+ },
568
+ {
569
+ title: { en: "Not included", zh: "暂不支持", "zh-tw": "暫不支援" },
570
+ items: {
571
+ en: [
572
+ "It does not shorten links — paste the result into your usual shortener if a campaign needs a short URL.",
573
+ "It does not measure clicks; results show up in GA4 or your analytics tool after the link goes live.",
574
+ "It does not tag internal links — UTM parameters on links inside your own site overwrite the real traffic source.",
575
+ "It builds one link at a time; bulk spreadsheet tagging is a different workflow.",
576
+ ],
577
+ zh: [
578
+ "不做短链——需要短链接时,把结果粘贴到你常用的短链工具即可。",
579
+ "不统计点击数据;链接上线后,效果在 GA4 或你的统计工具里查看。",
580
+ "不要给站内链接打 UTM——站内跳转带 UTM 会覆盖真实的流量来源。",
581
+ "一次生成一条链接;批量表格打参数是另一种工作流。",
582
+ ],
583
+ "zh-tw": [
584
+ "不做短網址——需要短連結時,把結果貼上到你常用的短網址工具即可。",
585
+ "不統計點擊數據;連結上線後,效果在 GA4 或你的統計工具裡查看。",
586
+ "不要給站內連結打 UTM——站內跳轉帶 UTM 會覆蓋真實的流量來源。",
587
+ "一次生成一條連結;批量表格打參數是另一種工作流。",
588
+ ],
589
+ },
590
+ },
591
+ {
592
+ title: { en: "If generation fails", zh: "生成失败时", "zh-tw": "生成失敗時" },
593
+ items: {
594
+ en: [
595
+ "Check that the destination URL is a full web address; https:// is added automatically, but mailto: and ftp: are rejected.",
596
+ "Your form input is kept after an error, so fix the URL and generate again — no retyping.",
597
+ "If a warning says existing UTM tags were replaced, confirm the old values were safe to drop.",
598
+ ],
599
+ zh: [
600
+ "确认目标 URL 是完整网页地址;https:// 会自动补全,但 mailto:、ftp: 会被拒绝。",
601
+ "出错后表单内容会保留,修正 URL 再点一次生成即可,不用重填。",
602
+ "如果警告提示旧 UTM 参数被覆盖,请确认旧值确实可以丢弃。",
603
+ ],
604
+ "zh-tw": [
605
+ "確認目標 URL 是完整網址;https:// 會自動補全,但 mailto:、ftp: 會被拒絕。",
606
+ "出錯後表單內容會保留,修正 URL 再點一次生成即可,不用重填。",
607
+ "如果警告提示舊 UTM 參數被覆蓋,請確認舊值確實可以丟棄。",
608
+ ],
609
+ },
610
+ },
611
+ ],
612
+ },
613
+ {
614
+ kind: "bullets",
615
+ heading: {
616
+ en: "How to use the tracking link after copying",
617
+ zh: "复制链接之后怎么用",
618
+ "zh-tw": "複製連結之後怎麼用",
619
+ },
620
+ image: {
621
+ src: "/tools/utm-builder-result-use.webp",
622
+ alt: {
623
+ en: "Illustration of a tagged campaign link flowing into ads, email, and social posts and reporting back into analytics.",
624
+ zh: "打好参数的活动链接投放到广告、邮件和社媒,并回流到统计报表的插画。",
625
+ "zh-tw": "打好參數的活動連結投放到廣告、郵件和社群,並回流到統計報表的插畫。",
626
+ },
627
+ side: "right",
628
+ wide: true,
629
+ },
630
+ groups: [
631
+ {
632
+ items: {
633
+ en: [
634
+ "Paste the link into the ad platform, email tool, or social post exactly as generated — do not re-edit it by hand.",
635
+ "Open the link once yourself and check the GA4 realtime report to confirm source and medium show up correctly.",
636
+ "Keep one naming convention: always lowercase, always the same channel words (twitter, not tw or Twitter).",
637
+ "Record the campaign name in your team's tracking sheet so the next launch reuses the same values.",
638
+ "Need different ad creatives measured separately? Generate one link per variant using utm_content.",
639
+ ],
640
+ zh: [
641
+ "把链接原样粘贴到广告平台、邮件工具或社媒帖子里,不要再手动改动。",
642
+ "自己先点一次链接,在 GA4 实时报表里确认来源和媒介显示正确。",
643
+ "保持统一命名:始终小写、渠道词固定(用 twitter,不要 tw 或 Twitter 混用)。",
644
+ "把活动名记进团队的投放表格,下次投放沿用同一套命名。",
645
+ "不同广告素材要分开统计时,用 utm_content 为每个版本单独生成链接。",
646
+ ],
647
+ "zh-tw": [
648
+ "把連結原樣貼上到廣告平台、郵件工具或社群貼文裡,不要再手動改動。",
649
+ "自己先點一次連結,在 GA4 即時報表裡確認來源和媒介顯示正確。",
650
+ "保持統一命名:始終小寫、管道詞固定(用 twitter,不要 tw 或 Twitter 混用)。",
651
+ "把活動名記進團隊的投放表格,下次投放沿用同一套命名。",
652
+ "不同廣告素材要分開統計時,用 utm_content 為每個版本單獨生成連結。",
653
+ ],
654
+ },
655
+ },
656
+ ],
657
+ },
658
+ {
659
+ kind: "cards",
660
+ heading: {
661
+ en: "Why this instead of Google's builder or a spreadsheet",
662
+ zh: "为什么不用官方工具或表格手拼",
663
+ "zh-tw": "為什麼不用官方工具或表格手拼",
664
+ },
665
+ items: [
666
+ {
667
+ title: { en: "It fixes input instead of trusting it", zh: "主动修复输入,而不是照单全收", "zh-tw": "主動修復輸入,而不是照單全收" },
668
+ body: {
669
+ en: "Google's Campaign URL Builder and most free UTM makers paste whatever you type. This one normalizes spaces, casing, and duplicate tags, and shows each fix.",
670
+ zh: "Google 官方 Campaign URL Builder 和多数免费工具会把你输入的内容原样拼上去。这里会规范空格、大小写和重复参数,并把每处修复列出来。",
671
+ "zh-tw": "Google 官方 Campaign URL Builder 和多數免費工具會把你輸入的內容原樣拼上去。這裡會規範空格、大小寫和重複參數,並把每處修復列出來。",
672
+ },
673
+ },
674
+ {
675
+ title: { en: "No sign-up, no ads, instant result", zh: "免登录、无广告、即时出结果", "zh-tw": "免登入、無廣告、即時出結果" },
676
+ body: {
677
+ en: "No account required, no upsell banners between you and the link. Fill the form, copy the URL, done in under a minute.",
678
+ zh: "没有注册墙,没有夹在中间的推销横幅。填完表单、复制链接,一分钟内完成。",
679
+ "zh-tw": "沒有註冊牆,沒有夾在中間的推銷橫幅。填完表單、複製連結,一分鐘內完成。",
680
+ },
681
+ },
682
+ {
683
+ title: { en: "Private by design", zh: "默认保护你的链接", "zh-tw": "預設保護你的連結" },
684
+ body: {
685
+ en: "Unlaunched campaign URLs are sensitive. Everything is computed in your browser, so the link never touches a server.",
686
+ zh: "未上线的活动链接本身就是敏感信息。所有计算都在浏览器完成,链接不会经过任何服务器。",
687
+ "zh-tw": "未上線的活動連結本身就是敏感資訊。所有計算都在瀏覽器完成,連結不會經過任何伺服器。",
688
+ },
689
+ },
690
+ {
691
+ title: { en: "Less error-prone than spreadsheets", zh: "比表格手拼更不容易错", "zh-tw": "比表格手拼更不容易錯" },
692
+ body: {
693
+ en: "Spreadsheet formulas silently break URL encoding and produce double question marks. Here the URL structure is parsed and rebuilt properly every time.",
694
+ zh: "表格公式经常悄悄搞坏 URL 编码或拼出两个问号。这里每次都会真正解析并重建 URL 结构。",
695
+ "zh-tw": "表格公式經常悄悄搞壞 URL 編碼或拼出兩個問號。這裡每次都會真正解析並重建 URL 結構。",
696
+ },
697
+ },
698
+ ],
699
+ },
700
+ {
701
+ kind: "bullets",
702
+ heading: {
703
+ en: "What a good UTM link should look like",
704
+ zh: "一条好的 UTM 链接长什么样",
705
+ "zh-tw": "一條好的 UTM 連結長什麼樣",
706
+ },
707
+ image: {
708
+ src: "/tools/utm-builder-value.webp",
709
+ alt: {
710
+ en: "Illustration comparing a messy hand-built link with a clean normalized campaign URL.",
711
+ zh: "手拼的混乱链接与规范化后干净活动链接的前后对比插画。",
712
+ "zh-tw": "手拼的混亂連結與規範化後乾淨活動連結的前後對比插畫。",
713
+ },
714
+ side: "left",
715
+ wide: true,
716
+ },
717
+ groups: [
718
+ {
719
+ items: {
720
+ en: [
721
+ "One question mark, ampersand-separated parameters, and the #fragment at the very end.",
722
+ "Lowercase, hyphen-separated values that read the same in every report: utm_source=google, not utm_source=Google%20Ads.",
723
+ "A campaign name specific enough to find later: launch-week-2026, not test or new.",
724
+ "source answers where, medium answers how, campaign answers which push — if a value answers the wrong question, rename it.",
725
+ "The same value spelled the same way across every channel and teammate.",
726
+ ],
727
+ zh: [
728
+ "只有一个问号,参数用 & 分隔,# 锚点保持在最末尾。",
729
+ "小写、用连字符分隔的参数值,在所有报表里读起来一致:utm_source=google,而不是 utm_source=Google%20Ads。",
730
+ "活动名要具体到之后能找到:launch-week-2026,而不是 test 或 new。",
731
+ "source 回答“从哪来”,medium 回答“什么渠道类型”,campaign 回答“哪次投放”——答错问题的值应该改名。",
732
+ "同一个值在所有渠道、所有同事手里拼写完全一致。",
733
+ ],
734
+ "zh-tw": [
735
+ "只有一個問號,參數用 & 分隔,# 錨點保持在最末尾。",
736
+ "小寫、用連字號分隔的參數值,在所有報表裡讀起來一致:utm_source=google,而不是 utm_source=Google%20Ads。",
737
+ "活動名要具體到之後能找到:launch-week-2026,而不是 test 或 new。",
738
+ "source 回答「從哪來」,medium 回答「什麼管道類型」,campaign 回答「哪次投放」——答錯問題的值應該改名。",
739
+ "同一個值在所有管道、所有同事手裡拼寫完全一致。",
740
+ ],
741
+ },
742
+ },
743
+ ],
744
+ },
745
+ ],
746
+ faq: [
747
+ {
748
+ question: {
749
+ en: "What does UTM stand for?",
750
+ zh: "UTM 是什么的缩写?",
751
+ "zh-tw": "UTM 是什麼的縮寫?",
752
+ },
753
+ answer: {
754
+ en: "Urchin Tracking Module — named after Urchin, the analytics company Google acquired before building Google Analytics. In practice it means the five utm_ query parameters that tell GA4 where a visit came from; a UTM maker like this one appends them to your URL correctly.",
755
+ zh: "Urchin Tracking Module 的缩写——Urchin 是 Google 在打造 Google Analytics 之前收购的统计公司。实际含义就是 5 个 utm_ 查询参数,告诉 GA4 一次访问从哪来;这类 UTM 生成工具做的就是把它们正确拼到你的 URL 上。",
756
+ "zh-tw": "Urchin Tracking Module 的縮寫——Urchin 是 Google 在打造 Google Analytics 之前收購的統計公司。實際含義就是 5 個 utm_ 查詢參數,告訴 GA4 一次訪問從哪來;這類 UTM 生成工具做的就是把它們正確拼到你的 URL 上。",
757
+ },
758
+ },
759
+ {
760
+ question: {
761
+ en: "Why were the UTM parameters already in my URL replaced?",
762
+ zh: "为什么我 URL 里原有的 UTM 参数被覆盖了?",
763
+ "zh-tw": "為什麼我 URL 裡原有的 UTM 參數被覆蓋了?",
764
+ },
765
+ answer: {
766
+ en: "A URL with two sets of UTM tags produces unpredictable attribution, so the builder keeps exactly one set — the values you just entered — and lists the replaced keys in the auto-fix notes so you can double-check.",
767
+ zh: "同一条 URL 带两套 UTM 参数会导致归因混乱,所以工具只保留一套(你刚填写的值),并在自动修复说明里列出被覆盖的参数,方便你确认。",
768
+ "zh-tw": "同一條 URL 帶兩套 UTM 參數會導致歸因混亂,所以工具只保留一套(你剛填寫的值),並在自動修復說明裡列出被覆蓋的參數,方便你確認。",
769
+ },
770
+ },
771
+ {
772
+ question: {
773
+ en: "Do these UTM parameters work with GA4?",
774
+ zh: "生成的 UTM 参数在 GA4 里有效吗?",
775
+ "zh-tw": "生成的 UTM 參數在 GA4 裡有效嗎?",
776
+ },
777
+ answer: {
778
+ en: "Yes. utm_source, utm_medium, utm_campaign, utm_term, and utm_content are the standard parameters GA4 reads for session and traffic acquisition reports, and the same tags are recognized by Google Ads, Mixpanel, Matomo, and most analytics tools.",
779
+ zh: "有效。utm_source、utm_medium、utm_campaign、utm_term、utm_content 就是 GA4 会话与流量获取报告读取的标准参数,Google Ads、Mixpanel、Matomo 等工具也识别同一套参数。",
780
+ "zh-tw": "有效。utm_source、utm_medium、utm_campaign、utm_term、utm_content 就是 GA4 工作階段與流量開發報表讀取的標準參數,Google Ads、Mixpanel、Matomo 等工具也識別同一套參數。",
781
+ },
782
+ },
783
+ {
784
+ question: {
785
+ en: "Should UTM values always be lowercase?",
786
+ zh: "UTM 参数值一定要小写吗?",
787
+ "zh-tw": "UTM 參數值一定要小寫嗎?",
788
+ },
789
+ answer: {
790
+ en: "Strongly recommended. UTM values are case-sensitive in GA4, so Google and google are counted as two different sources. Keeping everything lowercase is the simplest convention — that is why the lowercase toggle is on by default.",
791
+ zh: "强烈建议。GA4 里 UTM 值区分大小写,Google 和 google 会被算成两个来源。全小写是最简单可靠的约定,所以“参数统一小写”默认开启。",
792
+ "zh-tw": "強烈建議。GA4 裡 UTM 值區分大小寫,Google 和 google 會被算成兩個來源。全小寫是最簡單可靠的約定,所以「參數統一小寫」預設開啟。",
793
+ },
794
+ },
795
+ {
796
+ question: {
797
+ en: "Is my URL uploaded to a server?",
798
+ zh: "我的 URL 会被上传到服务器吗?",
799
+ "zh-tw": "我的 URL 會被上傳到伺服器嗎?",
800
+ },
801
+ answer: {
802
+ en: "No. The link is built entirely in your browser with no network request, so unlaunched campaign URLs, internal domains, and staging links stay on your machine.",
803
+ zh: "不会。链接完全在浏览器本地生成,不发任何网络请求;未上线的活动链接、内部域名和测试环境地址都不会离开你的电脑。",
804
+ "zh-tw": "不會。連結完全在瀏覽器本地生成,不發任何網路請求;未上線的活動連結、內部網域和測試環境網址都不會離開你的電腦。",
805
+ },
806
+ },
807
+ {
808
+ question: {
809
+ en: "Where should I not use UTM links?",
810
+ zh: "哪些地方不应该用 UTM 链接?",
811
+ "zh-tw": "哪些地方不應該用 UTM 連結?",
812
+ },
813
+ answer: {
814
+ en: "Never tag links that point from one page of your site to another. Clicking an internal UTM link starts a new attributed session and overwrites the real traffic source, corrupting your reports.",
815
+ zh: "不要给站内互相跳转的链接打 UTM。点击站内 UTM 链接会开启新的归因会话,覆盖真实流量来源,直接污染报表数据。",
816
+ "zh-tw": "不要給站內互相跳轉的連結打 UTM。點擊站內 UTM 連結會開啟新的歸因工作階段,覆蓋真實流量來源,直接污染報表數據。",
817
+ },
818
+ },
819
+ ],
820
+ cta: {
821
+ hint: {
822
+ en: "Link tagged? Put it to work: draft the campaign email that carries it.",
823
+ zh: "链接打好参数了?下一步:写好承载它的营销邮件。",
824
+ "zh-tw": "連結打好參數了?下一步:寫好承載它的行銷郵件。",
825
+ },
826
+ primary: {
827
+ href: "/tools/email-template-generator",
828
+ label: { en: "Draft the campaign email", zh: "生成营销邮件模板", "zh-tw": "生成行銷郵件模板" },
829
+ },
830
+ secondary: {
831
+ href: "/tools",
832
+ label: { en: "Back to all tools", zh: "返回全部工具", "zh-tw": "返回全部工具" },
833
+ },
834
+ },
835
+ },
836
+ assets: {
837
+ hero: {
838
+ src: "/tools/utm-builder-hero.webp",
839
+ alt: {
840
+ en: "Illustration of a campaign link being tagged with UTM parameters and flowing into clean analytics channels.",
841
+ zh: "活动链接被打上 UTM 参数并汇入清晰流量渠道的插画。",
842
+ "zh-tw": "活動連結被打上 UTM 參數並匯入清晰流量管道的插畫。",
843
+ },
844
+ },
845
+ },
846
+ };