@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,86 @@
1
+ "use client";
2
+
3
+ import { useTranslations } from "../../../../i18n/client";
4
+ import { cn } from "@tomako/ui/cn";
5
+ import { motion } from "framer-motion";
6
+ import type { CSSProperties } from "react";
7
+
8
+ import { useDeckLayout } from "./deck";
9
+
10
+ export function DeckView({
11
+ isShuffling,
12
+ isCollecting,
13
+ selectedDeckIds,
14
+ onSelectCard,
15
+ }: {
16
+ isShuffling: boolean;
17
+ isCollecting: boolean;
18
+ selectedDeckIds: readonly number[];
19
+ onSelectCard: (id: number) => void;
20
+ }) {
21
+ const t = useTranslations("toolsPages.tarotReading.widget");
22
+ const deckLayout = useDeckLayout();
23
+ const { cards: activeDeckCards, isCompact, spreadWidth } = deckLayout;
24
+ const deckFieldWidth = isCompact
25
+ ? Math.max(420, spreadWidth + 120)
26
+ : Math.min(1600, spreadWidth + 180);
27
+
28
+ return (
29
+ <motion.div
30
+ key="deck"
31
+ className="mx-auto -mt-8 w-full max-w-[1680px] px-4 md:-mt-10"
32
+ initial={{ opacity: 0, scale: 0.98 }}
33
+ animate={{ opacity: 1, scale: 1 }}
34
+ exit={{ opacity: 0 }}
35
+ transition={{ duration: isCollecting ? 0.2 : 0.45 }}
36
+ >
37
+ <div className="text-center">
38
+ <p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-[#D7A84F]">
39
+ {isShuffling ? t("ritual.shuffling") : t("ritual.choose")}
40
+ </p>
41
+ <h2 className="mt-3 text-3xl font-light text-[#FFF8EA] md:text-4xl">
42
+ {isShuffling ? t("ritual.shufflingTitle") : t("ritual.chooseTitle")}
43
+ </h2>
44
+ </div>
45
+
46
+ <div
47
+ className="tarot-deck-field relative mx-auto mt-12 h-[430px] w-full max-w-[1600px] overflow-visible md:mt-14 md:h-[560px]"
48
+ style={{ maxWidth: deckFieldWidth }}
49
+ >
50
+ <div className="absolute left-1/2 top-[54%] h-48 w-48 -translate-x-1/2 -translate-y-1/2 rounded-full bg-[radial-gradient(circle,rgba(215,168,79,0.14),transparent_68%)]" />
51
+ {activeDeckCards.map((deckCard, index) => {
52
+ const selectedIndex = selectedDeckIds.indexOf(deckCard.id);
53
+ const selected = selectedIndex >= 0;
54
+
55
+ return (
56
+ <button
57
+ key={deckCard.id}
58
+ type="button"
59
+ aria-label={`${t("ritual.choose")} ${index + 1}`}
60
+ disabled={isShuffling || isCollecting || selected || selectedDeckIds.length >= 3}
61
+ onClick={() => onSelectCard(deckCard.id)}
62
+ className={cn(
63
+ "tarot-deck-card absolute left-1/2 top-1/2 h-28 w-[74px] overflow-visible rounded-[0.95rem] outline-none focus-visible:ring-[3px] focus-visible:ring-[#D7A84F]/32 sm:h-32 sm:w-[86px] md:h-40 md:w-[104px]",
64
+ selected ? "cursor-default" : "cursor-pointer",
65
+ selected ? "tarot-deck-card-selected" : "",
66
+ isCollecting && selected ? "tarot-deck-card-collect-selected" : "",
67
+ isCollecting && !selected ? "tarot-deck-card-collect-dismiss" : "",
68
+ )}
69
+ style={
70
+ {
71
+ "--deck-x": `${deckCard.x}px`,
72
+ "--deck-y": `${deckCard.y}px`,
73
+ "--deck-rotate": `${deckCard.rotate}deg`,
74
+ "--deck-selected-lift": `${isCompact ? -54 : -72}px`,
75
+ "--deck-collect-x": `${(selectedIndex - 1) * (isCompact ? 96 : 150)}px`,
76
+ "--deck-collect-y": `${isCompact ? -34 : -48}px`,
77
+ zIndex: index,
78
+ } as CSSProperties
79
+ }
80
+ />
81
+ );
82
+ })}
83
+ </div>
84
+ </motion.div>
85
+ );
86
+ }
@@ -0,0 +1,86 @@
1
+ "use client";
2
+
3
+ import { useEffect, useMemo, useState } from "react";
4
+
5
+ import { weakQuestionPattern } from "./constants";
6
+ import type { DeckCard, DeckLayout } from "./types";
7
+
8
+ export function createDeckSpread(
9
+ count: number,
10
+ width: number,
11
+ arcDepth: number,
12
+ rotateRange: number,
13
+ yOffset = -28,
14
+ ): DeckCard[] {
15
+ return Array.from({ length: count }, (_, index) => {
16
+ const progress = count === 1 ? 0.5 : index / (count - 1);
17
+ const centered = progress - 0.5;
18
+
19
+ return {
20
+ id: index,
21
+ x: -width / 2 + progress * width,
22
+ y: Math.pow(centered * 2, 2) * arcDepth + yOffset,
23
+ rotate: centered * rotateRange,
24
+ };
25
+ });
26
+ }
27
+
28
+ export function createResponsiveDeckLayout(viewportWidth: number): DeckLayout {
29
+ if (viewportWidth < 768) {
30
+ const spreadWidth = Math.min(360, Math.max(300, viewportWidth - 70));
31
+
32
+ return {
33
+ cards: createDeckSpread(78, spreadWidth, 62, 30, -22),
34
+ isCompact: true,
35
+ spreadWidth,
36
+ };
37
+ }
38
+
39
+ if (viewportWidth < 1100) {
40
+ const spreadWidth = Math.max(640, viewportWidth - 220);
41
+
42
+ return {
43
+ cards: createDeckSpread(78, spreadWidth, 92, 36, -42),
44
+ isCompact: false,
45
+ spreadWidth,
46
+ };
47
+ }
48
+
49
+ const spreadWidth = Math.min(1500, Math.max(1080, viewportWidth - 360));
50
+ const arcDepth = Math.min(178, Math.max(118, spreadWidth * 0.11));
51
+
52
+ return {
53
+ cards: createDeckSpread(78, spreadWidth, arcDepth, 46, -68),
54
+ isCompact: false,
55
+ spreadWidth,
56
+ };
57
+ }
58
+
59
+ export function useDeckLayout() {
60
+ const [viewportWidth, setViewportWidth] = useState(() =>
61
+ typeof window === "undefined" ? 1440 : window.innerWidth,
62
+ );
63
+
64
+ useEffect(() => {
65
+ let frame = 0;
66
+ const handleResize = () => {
67
+ window.cancelAnimationFrame(frame);
68
+ frame = window.requestAnimationFrame(() => {
69
+ setViewportWidth(window.innerWidth);
70
+ });
71
+ };
72
+
73
+ window.addEventListener("resize", handleResize);
74
+ return () => {
75
+ window.cancelAnimationFrame(frame);
76
+ window.removeEventListener("resize", handleResize);
77
+ };
78
+ }, []);
79
+
80
+ return useMemo(() => createResponsiveDeckLayout(viewportWidth), [viewportWidth]);
81
+ }
82
+
83
+ export function isWeakQuestion(question: string): boolean {
84
+ const normalized = question.trim();
85
+ return normalized.length < 6 || weakQuestionPattern.test(normalized);
86
+ }
@@ -0,0 +1,7 @@
1
+ "use client";
2
+
3
+ import dynamic from "next/dynamic";
4
+
5
+ export const TarotReadingWidget = dynamic(() =>
6
+ import("./tarot-reading").then((module) => module.TarotReadingWidget),
7
+ );
@@ -0,0 +1,209 @@
1
+ "use client";
2
+
3
+ import { Button } from "@tomako/ui/button";
4
+ import { useTranslations } from "../../../../i18n/client";
5
+ import { cn } from "@tomako/ui/cn";
6
+ import { motion } from "framer-motion";
7
+ import { Shuffle } from "lucide-react";
8
+ import {
9
+ useLayoutEffect,
10
+ useRef,
11
+ useState,
12
+ type KeyboardEvent,
13
+ } from "react";
14
+
15
+ import { maxQuestionLength } from "./constants";
16
+
17
+ export function LandingView({
18
+ question,
19
+ error,
20
+ onQuestionChange,
21
+ onSubmit,
22
+ }: {
23
+ question: string;
24
+ error: string | null;
25
+ onQuestionChange: (value: string) => void;
26
+ onSubmit: (questionOverride?: string) => void;
27
+ }) {
28
+ const t = useTranslations("toolsPages.tarotReading.widget");
29
+ const [focused, setFocused] = useState(false);
30
+ const [questionPage, setQuestionPage] = useState(0);
31
+ const [visibleQuestionRows, setVisibleQuestionRows] = useState<number[][]>([
32
+ [0, 1, 2],
33
+ [3, 4, 5],
34
+ ]);
35
+ const quickQuestionsRef = useRef<HTMLDivElement>(null);
36
+ const quickMeasureRefs = useRef<Array<HTMLSpanElement | null>>([]);
37
+ const quickQuestions = t.raw("quickQuestions") as string[];
38
+ const visibleQuestions = Array.from({ length: 6 }, (_, index) =>
39
+ quickQuestions[(questionPage * 6 + index) % quickQuestions.length],
40
+ );
41
+ const visibleQuestionKey = visibleQuestions.join("\u0000");
42
+
43
+ useLayoutEffect(() => {
44
+ const container = quickQuestionsRef.current;
45
+ if (!container) return;
46
+
47
+ function updateVisibleRows() {
48
+ if (!quickQuestionsRef.current) return;
49
+ const availableWidth = quickQuestionsRef.current.clientWidth;
50
+ const gap = 8;
51
+ const nextRows = [
52
+ [0, 1, 2],
53
+ [3, 4, 5],
54
+ ].map((candidateIndexes) => {
55
+ let usedWidth = 0;
56
+ return candidateIndexes.filter((index) => {
57
+ const measurement = quickMeasureRefs.current[index];
58
+ if (!measurement) return false;
59
+ const nextWidth =
60
+ usedWidth === 0
61
+ ? measurement.offsetWidth
62
+ : usedWidth + gap + measurement.offsetWidth;
63
+ if (nextWidth > availableWidth) return false;
64
+ usedWidth = nextWidth;
65
+ return true;
66
+ });
67
+ });
68
+
69
+ setVisibleQuestionRows((current) =>
70
+ current.length === nextRows.length &&
71
+ current.every(
72
+ (row, rowIndex) =>
73
+ row.length === nextRows[rowIndex].length &&
74
+ row.every((value, index) => value === nextRows[rowIndex][index]),
75
+ )
76
+ ? current
77
+ : nextRows,
78
+ );
79
+ }
80
+
81
+ updateVisibleRows();
82
+ const observer = new ResizeObserver(updateVisibleRows);
83
+ observer.observe(container);
84
+ return () => observer.disconnect();
85
+ }, [visibleQuestionKey]);
86
+
87
+ function handleQuestionKeyDown(event: KeyboardEvent<HTMLTextAreaElement>) {
88
+ if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
89
+ event.preventDefault();
90
+ submitQuestion();
91
+ }
92
+ }
93
+
94
+ function submitQuestion() {
95
+ onSubmit();
96
+ }
97
+
98
+ return (
99
+ <motion.div
100
+ key="landing"
101
+ className="mx-auto w-full max-w-[772px]"
102
+ initial={{ opacity: 1, y: 14 }}
103
+ animate={{ opacity: 1, y: 0 }}
104
+ exit={{ opacity: 0, y: -22 }}
105
+ transition={{ duration: 0.48, ease: [0.2, 0.8, 0.2, 1] }}
106
+ >
107
+ <div
108
+ className={cn(
109
+ "tarot-magic-border tarot-glass-panel relative overflow-hidden rounded-[0.45rem] border transition-[border-color,box-shadow] duration-300",
110
+ focused ? "tarot-glass-panel-focus" : "",
111
+ )}
112
+ >
113
+ <label className="block">
114
+ <span className="sr-only">{t("questionLabel")}</span>
115
+ <textarea
116
+ rows={4}
117
+ maxLength={maxQuestionLength}
118
+ value={question}
119
+ onChange={(event) => onQuestionChange(event.target.value)}
120
+ onKeyDown={handleQuestionKeyDown}
121
+ onFocus={() => setFocused(true)}
122
+ onBlur={() => setFocused(false)}
123
+ placeholder={focused ? "" : t("questionPlaceholder")}
124
+ aria-invalid={error ? true : undefined}
125
+ suppressHydrationWarning
126
+ className="h-28 w-full resize-none bg-transparent px-6 py-5 text-[15px] leading-6 text-white outline-none transition placeholder:text-[#6A6864] md:h-32 md:px-7"
127
+ />
128
+ </label>
129
+
130
+ <div
131
+ className="flex flex-col justify-between gap-3 border-t px-4 py-2.5 sm:flex-row sm:items-center md:px-6"
132
+ style={{ borderColor: "rgba(248, 241, 224, 0.12)" }}
133
+ >
134
+ <div className="min-w-0 flex-1">
135
+ {error ? (
136
+ <p className="text-sm font-medium text-[#F0A88F]">{error}</p>
137
+ ) : (
138
+ <p className="sr-only">{t("privacyHint")}</p>
139
+ )}
140
+ </div>
141
+
142
+ <div className="flex shrink-0 flex-col gap-1 text-right text-[11px] font-semibold tracking-[0.18em] text-[#5F5B56]">
143
+ <span>{t("questionLabel")} · {question.length} / {maxQuestionLength}</span>
144
+ </div>
145
+ </div>
146
+ </div>
147
+
148
+ <div className="mt-5">
149
+ <div className="mb-3 flex items-center justify-between gap-4">
150
+ <p className="text-xs font-semibold tracking-[0.12em] text-[#8D887F]">
151
+ {t("quickTitle")}
152
+ </p>
153
+ <button
154
+ type="button"
155
+ className="inline-flex items-center gap-1.5 text-xs text-[#77726A] transition hover:text-[#D7A84F]"
156
+ onClick={() => setQuestionPage((current) => current + 1)}
157
+ >
158
+ <Shuffle className="size-3.5" />
159
+ {t("quickRefresh")}
160
+ </button>
161
+ </div>
162
+ <div ref={quickQuestionsRef} className="relative flex flex-col gap-2 overflow-hidden">
163
+ <div aria-hidden className="pointer-events-none absolute invisible">
164
+ {visibleQuestions.map((item, index) => (
165
+ <span
166
+ key={`${item}-measure`}
167
+ ref={(element) => {
168
+ quickMeasureRefs.current[index] = element;
169
+ }}
170
+ className="inline-flex whitespace-nowrap px-3.5 py-2 text-[15px] leading-5"
171
+ >
172
+ {item}
173
+ </span>
174
+ ))}
175
+ </div>
176
+ {visibleQuestionRows.map((row, rowIndex) => (
177
+ <div
178
+ key={rowIndex}
179
+ className="flex min-h-9 items-center gap-2 overflow-hidden"
180
+ >
181
+ {row.map((index) => {
182
+ const item = visibleQuestions[index];
183
+ return (
184
+ <button
185
+ key={item}
186
+ type="button"
187
+ className="tarot-quick-question shrink-0 whitespace-nowrap rounded-full border-0 bg-[#FFF8EA]/[0.035] px-3.5 py-2 text-left text-[15px] leading-5 text-[#AAA49A] transition hover:bg-[#D7A84F]/8 hover:text-[#F8F1E0]"
188
+ onClick={() => onQuestionChange(item)}
189
+ >
190
+ {item}
191
+ </button>
192
+ );
193
+ })}
194
+ </div>
195
+ ))}
196
+ </div>
197
+ </div>
198
+
199
+ <Button
200
+ type="button"
201
+ size="lg"
202
+ className="tarot-magic-button mx-auto mt-8 flex h-12 min-w-32 justify-center rounded-[0.35rem] border border-[#F8F1E0] bg-[#F8F1E0] px-8 text-sm font-semibold tracking-[0.08em] text-[#080808] shadow-[0_18px_48px_rgba(248,241,224,0.18)] hover:bg-white"
203
+ onClick={submitQuestion}
204
+ >
205
+ {t("drawButton")}
206
+ </Button>
207
+ </motion.div>
208
+ );
209
+ }
@@ -0,0 +1,138 @@
1
+ "use client";
2
+
3
+ import { isZhRouteLocale } from "../../../../i18n/locale-fallback";
4
+ import type { AppLocale } from "../../../../i18n/locale";
5
+ import {
6
+ getTarotPositionLabel,
7
+ type TarotReading,
8
+ } from "../../../../lib/tools/tarot-reading";
9
+ import type { TarotAiReading } from "../../../../host/client";
10
+ import { motion } from "framer-motion";
11
+
12
+ export function ReadingTextBlock({
13
+ children,
14
+ delay = 0,
15
+ }: {
16
+ children: string;
17
+ delay?: number;
18
+ }) {
19
+ return (
20
+ <motion.div
21
+ className="border border-[#2C2F31] bg-[#101112]/82 px-7 py-6 text-base leading-9 text-[#A7A4A0] shadow-[0_0_0_1px_rgba(255,255,255,0.012)] md:px-9 md:text-lg md:leading-10"
22
+ initial={{ opacity: 0, y: 16 }}
23
+ animate={{ opacity: 1, y: 0 }}
24
+ transition={{ duration: 0.56, delay, ease: [0.2, 0.8, 0.2, 1] }}
25
+ >
26
+ {children}
27
+ </motion.div>
28
+ );
29
+ }
30
+
31
+ export function buildReadingBlocks(reading: TarotReading, locale: AppLocale): string[] {
32
+ const [past, present, future] = reading.cards;
33
+ const shortTheme = (value: string) => value.split(/[::]/, 1)[0];
34
+ const questionLens = {
35
+ general: "我会把判断落回这件事有没有出现新的事实、行为和结果,而不只看当下的感觉。",
36
+ love: "关系里真正要核对的是对方有没有回应、沟通是否清楚、边界是否被尊重,而不只是彼此还有没有情绪。",
37
+ career: "事业或工作上,我会具体看职责有没有变化、资源是否到位、推进是否产生了可见结果。",
38
+ money: "说到赚钱,我会具体看收入从哪里来、有没有成交或回款、成本是否可控,而不把忙碌本身当成收益。",
39
+ decision: "这个选择最终要看哪一条路更符合你的标准,也更能承担实际成本和后果。",
40
+ self: "这次真正值得观察的是,你的情绪、行为和边界有没有出现可以被自己确认的变化。",
41
+ }[reading.questionType];
42
+
43
+ if (isZhRouteLocale(locale)) {
44
+ return [
45
+ reading.overallStory,
46
+ `把这条时间线连起来看,我会这样理解:过去的${past.name}${past.orientation === "upright" ? "正位" : "逆位"}留下了“${shortTheme(past.theme)}”这层影响,所以事情走到今天并不突然。中间的${present.name}${present.orientation === "upright" ? "正位" : "逆位"}把焦点拉回你现在的状态——${present.meaning} 回到你问的“${reading.question}”,${questionLens}`,
47
+ `再往后看,${future.name}${future.orientation === "upright" ? "正位" : "逆位"}让我想到的是“${shortTheme(future.theme)}”。它描述的是这件事按现有方式发展时更容易出现的局面,但我不会把它看成已经写好的结局。对“${reading.question}”来说,真正的转折要从${present.advice}开始;随后再用现实中的具体变化验证方向,而不是只等一个答案。`,
48
+ reading.adjustment,
49
+ ];
50
+ }
51
+
52
+ return [
53
+ reading.overallStory,
54
+ `Past into present: ${past.name} ${past.orientation}, themed around “${shortTheme(past.theme)},” sets the background. ${past.positionLens} With ${present.name} ${present.orientation}, the focus shifts from why this happened to what is workable now. ${present.positionLens} ${present.advice}`,
55
+ `Present into future: ${future.name} ${future.orientation} is not a fixed prediction but an extension of the current pattern. ${future.positionLens} Adjusting the core named by ${present.name} can soften the difficult expression and strengthen the useful one; leaving it unchanged makes the shown trajectory more likely.`,
56
+ reading.adjustment,
57
+ ];
58
+ }
59
+
60
+ /** Three self-contained synthesis paragraphs for the collapsed result view. */
61
+ export function buildSummaryBlocks(reading: TarotReading, locale: AppLocale): string[] {
62
+ const [past, present, future] = reading.cards;
63
+ const orientation = (value: typeof past) =>
64
+ value.orientation === "upright"
65
+ ? (isZhRouteLocale(locale) ? "正位" : "upright")
66
+ : (isZhRouteLocale(locale) ? "逆位" : "reversed");
67
+
68
+ if (isZhRouteLocale(locale)) {
69
+ return [
70
+ `围绕“${reading.question}”,${past.name}${orientation(past)}、${present.name}${orientation(present)}与${future.name}${orientation(future)}形成的主线,是从“${past.theme}”走到“${present.theme}”,再进入“${future.theme}”。这组牌真正强调的不是单一结果,而是你如何把过去形成的条件,转化为现在能够主动调整的选择。`,
71
+ `当前的转折点落在${present.name}${orientation(present)}:${present.positionLens} 如果继续沿用现在的方式,${future.name}${orientation(future)}所提示的趋势会逐渐明显;如果先回应当下牌指出的核心问题,未来就仍有调整空间,而不是固定结局。`,
72
+ `行动上,先落实${present.name}给出的提醒:${present.advice} 随后再用${future.name}校准下一步:${future.advice} 先做一个现实中可以观察结果的小动作,再根据真实反馈决定是否继续推进。`,
73
+ ];
74
+ }
75
+
76
+ return [
77
+ `For “${reading.question},” ${past.name} ${orientation(past)}, ${present.name} ${orientation(present)}, and ${future.name} ${orientation(future)} move from “${past.theme}” through “${present.theme}” toward “${future.theme}.” The spread is less about one fixed outcome than about turning past conditions into a choice you can make now.`,
78
+ `The hinge is ${present.name} ${orientation(present)}: ${present.positionLens} Continuing the same pattern makes the trajectory shown by ${future.name} more likely; responding to the present card keeps that future adjustable rather than fixed.`,
79
+ `Begin with the present card's cue: ${present.advice} Then use the future card to calibrate the next step: ${future.advice} Make one observable move and let real feedback—not prediction alone—guide what follows.`,
80
+ ];
81
+ }
82
+
83
+ export function buildShareText(
84
+ reading: TarotReading,
85
+ locale: AppLocale,
86
+ aiReading?: TarotAiReading | null,
87
+ ): string {
88
+ const readingBlocks = aiReading
89
+ ? [
90
+ aiReading.overview,
91
+ aiReading.narrative,
92
+ aiReading.turningPoint,
93
+ aiReading.action,
94
+ ]
95
+ : buildReadingBlocks(reading, locale);
96
+
97
+ if (isZhRouteLocale(locale)) {
98
+ return [
99
+ `问题:${reading.question}`,
100
+ "",
101
+ "牌阵:",
102
+ ...reading.cards.map(
103
+ (card) =>
104
+ `${getTarotPositionLabel(locale, card.position)}|${card.name}|${card.orientation === "upright" ? "正位" : "逆位"}`,
105
+ ),
106
+ "",
107
+ "完整解读:",
108
+ ...readingBlocks.flatMap((block) => [block, ""]),
109
+ ...(aiReading
110
+ ? [
111
+ "行动建议:",
112
+ aiReading.recommendedAction,
113
+ aiReading.recommendationReason,
114
+ ]
115
+ : []),
116
+ ].join("\n");
117
+ }
118
+
119
+ return [
120
+ `Question: ${reading.question}`,
121
+ "",
122
+ "Spread:",
123
+ ...reading.cards.map(
124
+ (card) =>
125
+ `${getTarotPositionLabel(locale, card.position)} | ${card.name} | ${card.orientation}`,
126
+ ),
127
+ "",
128
+ "Full reading:",
129
+ ...readingBlocks.flatMap((block) => [block, ""]),
130
+ ...(aiReading
131
+ ? [
132
+ "Recommended action:",
133
+ aiReading.recommendedAction,
134
+ aiReading.recommendationReason,
135
+ ]
136
+ : []),
137
+ ].join("\n");
138
+ }