@scrabble-solver/scrabble-solver 2.16.7 → 2.18.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 (370) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +22 -20
  3. package/.next/cache/.previewinfo +1 -1
  4. package/.next/cache/.rscinfo +1 -1
  5. package/.next/cache/.tsbuildinfo +1 -1
  6. package/.next/cache/webpack/client-production/0.pack +0 -0
  7. package/.next/cache/webpack/client-production/index.pack +0 -0
  8. package/.next/cache/webpack/client-production/index.pack.old +0 -0
  9. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  10. package/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
  11. package/.next/cache/webpack/server-production/0.pack +0 -0
  12. package/.next/cache/webpack/server-production/index.pack +0 -0
  13. package/.next/cache/webpack/server-production/index.pack.old +0 -0
  14. package/.next/diagnostics/framework.json +1 -1
  15. package/.next/dynamic-css-manifest.json +8 -1
  16. package/.next/next-minimal-server.js.nft.json +1 -1
  17. package/.next/next-server.js.nft.json +1 -1
  18. package/.next/prerender-manifest.json +4 -4
  19. package/.next/react-loadable-manifest.json +176 -1
  20. package/.next/required-server-files.js +3 -2
  21. package/.next/required-server-files.json +3 -2
  22. package/.next/routes-manifest.json +1 -1
  23. package/.next/server/chunks/30.js +1 -1
  24. package/.next/server/chunks/309.js +1 -0
  25. package/.next/server/chunks/316.js +1 -0
  26. package/.next/server/chunks/344.js +1 -0
  27. package/.next/server/chunks/371.js +1 -0
  28. package/.next/server/chunks/392.js +1 -1
  29. package/.next/server/chunks/465.js +1 -0
  30. package/.next/server/chunks/546.js +1 -0
  31. package/.next/server/chunks/623.js +1 -0
  32. package/.next/server/chunks/624.js +1 -0
  33. package/.next/server/chunks/641.js +1 -0
  34. package/.next/server/chunks/671.js +1 -1
  35. package/.next/server/chunks/692.js +1 -0
  36. package/.next/server/chunks/705.js +1 -0
  37. package/.next/server/chunks/712.js +1 -1
  38. package/.next/server/chunks/846.js +1 -0
  39. package/.next/server/chunks/853.js +15 -0
  40. package/.next/server/chunks/886.js +1 -0
  41. package/.next/server/chunks/916.js +1 -0
  42. package/.next/server/chunks/953.js +1 -0
  43. package/.next/server/chunks/961.js +1 -0
  44. package/.next/server/chunks/968.js +1 -0
  45. package/.next/server/dynamic-css-manifest.js +1 -1
  46. package/.next/server/middleware-build-manifest.js +1 -1
  47. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  48. package/.next/server/pages/404.html +28 -1
  49. package/.next/server/pages/500.html +28 -1
  50. package/.next/server/pages/_app.js +1 -1
  51. package/.next/server/pages/_app.js.nft.json +1 -1
  52. package/.next/server/pages/_document.js +28 -1
  53. package/.next/server/pages/_document.js.nft.json +1 -1
  54. package/.next/server/pages/_error.js +28 -1
  55. package/.next/server/pages/_error.js.nft.json +1 -1
  56. package/.next/server/pages/api/dictionary/[locale]/[word].js +1 -1
  57. package/.next/server/pages/api/dictionary/[locale]/[word].js.nft.json +1 -1
  58. package/.next/server/pages/api/dictionary/[locale].js +1 -1
  59. package/.next/server/pages/api/dictionary/[locale].js.nft.json +1 -1
  60. package/.next/server/pages/api/solve.js +1 -1
  61. package/.next/server/pages/api/solve.js.nft.json +1 -1
  62. package/.next/server/pages/api/verify.js +1 -1
  63. package/.next/server/pages/api/verify.js.nft.json +1 -1
  64. package/.next/server/pages/api/visit.js.nft.json +1 -1
  65. package/.next/server/pages/index.html +28 -1
  66. package/.next/server/pages/index.js +30 -1
  67. package/.next/server/pages/index.js.nft.json +1 -1
  68. package/.next/server/pages/index.json +1 -1
  69. package/.next/server/pages/not-found.html +28 -1
  70. package/.next/server/pages/not-found.js.nft.json +1 -1
  71. package/.next/server/pages-manifest.json +1 -1
  72. package/.next/server/webpack-api-runtime.js +1 -1
  73. package/.next/server/webpack-runtime.js +1 -1
  74. package/.next/static/FL82obwzFHRzvV6z76I-U/_buildManifest.js +1 -0
  75. package/.next/static/chunks/101-829ab8a8d310e7a0.js +3 -0
  76. package/.next/static/chunks/106.e414e88694add27f.js +1 -0
  77. package/.next/static/chunks/127.125d896a19061d71.js +1 -0
  78. package/.next/static/chunks/18.8b91e137d239e15c.js +1 -0
  79. package/.next/static/chunks/277.6785dfaf22fdb0b7.js +1 -0
  80. package/.next/static/chunks/307.67f52f13e3d60d4e.js +1 -0
  81. package/.next/static/chunks/341.79535ba85bf1986f.js +1 -0
  82. package/.next/static/chunks/363.01eed3233f92b6a4.js +1 -0
  83. package/.next/static/chunks/422.1559ae6756912ca5.js +1 -0
  84. package/.next/static/chunks/541.dcd9e8b804f88da5.js +1 -0
  85. package/.next/static/chunks/583.7a46807be14c377e.js +1 -0
  86. package/.next/static/chunks/631.98d42abad0a60af6.js +1 -0
  87. package/.next/static/chunks/663.d2c9ca36b26695b8.js +1 -0
  88. package/.next/static/chunks/711.ba7b8f900dfd40d0.js +1 -0
  89. package/.next/static/chunks/744.b06c9a3de4a1d877.js +1 -0
  90. package/.next/static/chunks/820.97b82f751ca49579.js +1 -0
  91. package/.next/static/chunks/865.a9b563a45848a83c.js +1 -0
  92. package/.next/static/chunks/912.ff8037ba9d4db5c8.js +1 -0
  93. package/.next/static/chunks/939.19b33a3d3c351d73.js +1 -0
  94. package/.next/static/chunks/990.61296e5870b91f11.js +1 -0
  95. package/.next/static/chunks/framework-626cbd48ac5aca57.js +1 -0
  96. package/.next/static/chunks/main-b92e08f3fc96dd8d.js +5 -0
  97. package/.next/static/chunks/pages/_app-ba5d628e81a15461.js +1 -0
  98. package/.next/static/chunks/pages/{_error-afeda7c5231b6c3c.js → _error-f0290a98eb3ed0ec.js} +1 -1
  99. package/.next/static/chunks/pages/index-5f9fa6ac3cd3d97b.js +1 -0
  100. package/.next/static/chunks/pages/not-found-23d61fd3ed791cad.js +1 -0
  101. package/.next/static/chunks/webpack-e34c3ae12275e978.js +1 -0
  102. package/.next/static/css/2d03f0da2c678dfc.css +1 -0
  103. package/.next/static/css/7b6724d2df70f48f.css +1 -0
  104. package/.next/static/css/abc2425d3ba607bc.css +1 -0
  105. package/.next/static/css/b736be3bd02b5d99.css +1 -0
  106. package/.next/static/css/b74cd7de82dabcdd.css +1 -0
  107. package/.next/static/css/c403ce739a9c0d83.css +1 -0
  108. package/.next/static/css/ca311e6c030663d4.css +1 -0
  109. package/.next/static/css/dee878eb64099d92.css +1 -0
  110. package/.next/static/css/fbfdce70731d0d2b.css +1 -0
  111. package/.next/trace +21 -21
  112. package/.next/trace-build +1 -1
  113. package/empty-module.js +2 -0
  114. package/next.config.js +13 -8
  115. package/package.json +35 -16
  116. package/public/llms.txt +11 -0
  117. package/public/robots.txt +4 -0
  118. package/scripts/generate-scss-types.ts +112 -0
  119. package/src/@types/scss.d.ts +6 -9
  120. package/src/@types/svg.d.ts +1 -1
  121. package/src/components/Alert/Alert.module.scss.d.ts +13 -0
  122. package/src/components/Alert/Alert.tsx +4 -1
  123. package/src/components/Badge/Badge.module.scss.d.ts +6 -0
  124. package/src/components/Board/Board.module.scss +10 -0
  125. package/src/components/Board/Board.module.scss.d.ts +17 -0
  126. package/src/components/Board/Board.tsx +14 -26
  127. package/src/components/Board/BoardPure.tsx +54 -49
  128. package/src/components/Board/components/Actions/Actions.module.scss +18 -18
  129. package/src/components/Board/components/Actions/Actions.module.scss.d.ts +7 -0
  130. package/src/components/Board/components/Actions/Actions.tsx +6 -3
  131. package/src/components/Board/components/Actions/lib.ts +3 -1
  132. package/src/components/Board/components/Cell/Cell.module.scss.d.ts +13 -0
  133. package/src/components/Board/components/Cell/Cell.tsx +32 -34
  134. package/src/components/Board/components/InputPrompt/InputPrompt.module.scss.d.ts +10 -0
  135. package/src/components/Board/components/InputPrompt/InputPrompt.tsx +1 -1
  136. package/src/components/Board/components/ToggleDirectionButton/ToggleDirectionButton.module.scss.d.ts +8 -0
  137. package/src/components/Board/components/ToggleDirectionButton/ToggleDirectionButton.tsx +2 -2
  138. package/src/components/Board/getBoardBackground.ts +175 -0
  139. package/src/components/Board/hooks/index.ts +0 -1
  140. package/src/components/Board/hooks/useBoardStyle.ts +10 -20
  141. package/src/components/Board/hooks/useFloatingActions.ts +3 -6
  142. package/src/components/Board/hooks/useGrid.ts +23 -9
  143. package/src/components/Board/selectors.ts +1 -1
  144. package/src/components/Button/Button.module.scss.d.ts +12 -0
  145. package/src/components/Dictionary/Dictionary.module.scss +8 -22
  146. package/src/components/Dictionary/Dictionary.module.scss.d.ts +14 -0
  147. package/src/components/Dictionary/Dictionary.tsx +1 -10
  148. package/src/components/DictionaryInput/DictionaryInput.module.scss.d.ts +7 -0
  149. package/src/components/DictionaryInput/DictionaryInput.tsx +1 -1
  150. package/src/components/EmptyState/EmptyState.module.scss.d.ts +7 -0
  151. package/src/components/EmptyState/EmptyState.tsx +1 -1
  152. package/src/components/IconButton/IconButton.module.scss.d.ts +8 -0
  153. package/src/components/Key/Key.module.scss.d.ts +6 -0
  154. package/src/components/Keys/Arrows/Arrows.module.scss.d.ts +11 -0
  155. package/src/components/Keys/Arrows/Arrows.tsx +4 -1
  156. package/src/components/Keys/index.tsx +1 -1
  157. package/src/components/Loading/Loading.module.scss.d.ts +9 -0
  158. package/src/components/Loading/Loading.tsx +1 -1
  159. package/src/components/Logo/Logo.tsx +4 -9
  160. package/src/components/Logo/LogoBlueprint.tsx +1 -0
  161. package/src/components/Modal/Modal.module.scss.d.ts +15 -0
  162. package/src/components/Modal/Modal.tsx +16 -3
  163. package/src/components/Modal/components/Section/Section.module.scss.d.ts +7 -0
  164. package/src/components/NavButtons/NavButtons.module.scss +20 -0
  165. package/src/components/NavButtons/NavButtons.module.scss.d.ts +13 -0
  166. package/src/components/NavButtons/NavButtons.tsx +29 -45
  167. package/src/components/NotFound/NotFound.module.scss.d.ts +9 -0
  168. package/src/components/NotFound/NotFound.tsx +1 -1
  169. package/src/components/PlainTiles/PlainTiles.module.scss.d.ts +11 -0
  170. package/src/components/PlainTiles/PlainTiles.tsx +0 -1
  171. package/src/components/PlainTiles/Tile.tsx +3 -4
  172. package/src/components/PlainTiles/lib.ts +4 -19
  173. package/src/components/PlainTiles/types.ts +0 -1
  174. package/src/components/Progress/Progress.module.scss +7 -0
  175. package/src/components/Progress/Progress.module.scss.d.ts +6 -0
  176. package/src/components/Progress/Progress.tsx +2 -11
  177. package/src/components/Rack/Rack.module.scss +3 -0
  178. package/src/components/Rack/Rack.module.scss.d.ts +10 -0
  179. package/src/components/Rack/Rack.tsx +8 -11
  180. package/src/components/Rack/components/InputPrompt/InputPrompt.module.scss +2 -0
  181. package/src/components/Rack/components/InputPrompt/InputPrompt.module.scss.d.ts +7 -0
  182. package/src/components/Rack/components/InputPrompt/InputPrompt.tsx +1 -13
  183. package/src/components/Rack/components/InputPrompt/lib.ts +1 -1
  184. package/src/components/Rack/components/RackTile/RackTile.module.scss.d.ts +6 -0
  185. package/src/components/Rack/components/RackTile/RackTile.tsx +5 -4
  186. package/src/components/Rack/selectors.ts +1 -1
  187. package/src/components/Radio/Radio.module.scss.d.ts +12 -0
  188. package/src/components/Results/Cell.tsx +3 -12
  189. package/src/components/Results/Header.tsx +9 -18
  190. package/src/components/Results/HeaderButton.tsx +4 -5
  191. package/src/components/Results/Result.tsx +4 -15
  192. package/src/components/Results/Results.module.scss +4 -7
  193. package/src/components/Results/Results.module.scss.d.ts +27 -0
  194. package/src/components/Results/Results.tsx +14 -5
  195. package/src/components/Results/SolveButton.tsx +1 -1
  196. package/src/components/Results/types.ts +3 -3
  197. package/src/components/ResultsInput/ResultsInput.module.scss.d.ts +7 -0
  198. package/src/components/ResultsInput/ResultsInput.tsx +1 -1
  199. package/src/components/Solver/Solver.module.scss +6 -5
  200. package/src/components/Solver/Solver.module.scss.d.ts +20 -0
  201. package/src/components/Solver/Solver.tsx +23 -24
  202. package/src/components/Solver/components/InsertButton/InsertButton.module.scss.d.ts +7 -0
  203. package/src/components/Solver/components/InsertButton/InsertButton.tsx +1 -1
  204. package/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.module.scss.d.ts +18 -0
  205. package/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.tsx +3 -1
  206. package/src/components/Spinner/Spinner.module.scss.d.ts +10 -0
  207. package/src/components/Tile/Tile.module.scss +3 -0
  208. package/src/components/Tile/Tile.module.scss.d.ts +19 -0
  209. package/src/components/Tile/Tile.tsx +5 -14
  210. package/src/components/Tile/TilePure.tsx +2 -12
  211. package/src/components/Tooltip/Tooltip.module.scss.d.ts +7 -0
  212. package/src/components/Tooltip/Tooltip.tsx +14 -3
  213. package/src/components/Tooltip/useTooltip.ts +2 -1
  214. package/src/hooks/useColumns.ts +4 -7
  215. package/src/hooks/useDeferredRender.ts +20 -0
  216. package/src/hooks/useDirection.ts +4 -3
  217. package/src/hooks/useLanguage.ts +2 -2
  218. package/src/hooks/useLocalStorage.ts +32 -8
  219. package/src/hooks/useMedia.ts +26 -26
  220. package/src/i18n/constants.ts +2 -15
  221. package/src/i18n/i18n.ts +18 -17
  222. package/src/i18n/languages/english.json +0 -1
  223. package/src/i18n/languages/french.json +0 -1
  224. package/src/i18n/languages/german.json +0 -1
  225. package/src/i18n/languages/persian.json +0 -1
  226. package/src/i18n/languages/polish.json +0 -1
  227. package/src/i18n/languages/romanian.json +0 -1
  228. package/src/i18n/languages/spanish.json +0 -1
  229. package/src/i18n/languages/turkish.json +0 -1
  230. package/src/i18n/localeIcons.ts +24 -0
  231. package/src/lib/localeTransliterate.test.ts +5 -0
  232. package/src/lib/localeTransliterate.ts +26 -6
  233. package/src/lib/waitForIdleOrFirstIntent.ts +33 -0
  234. package/src/modals/DictionaryModal/DictionaryModal.module.scss.d.ts +8 -0
  235. package/src/modals/DictionaryModal/DictionaryModal.tsx +3 -1
  236. package/src/modals/KeyMapModal/KeyMapModal.tsx +3 -1
  237. package/src/modals/KeyMapModal/components/Mapping/Mapping.module.scss.d.ts +11 -0
  238. package/src/modals/MenuModal/MenuModal.module.scss +5 -0
  239. package/src/modals/MenuModal/MenuModal.module.scss.d.ts +10 -0
  240. package/src/modals/MenuModal/MenuModal.tsx +16 -5
  241. package/src/modals/RemainingTilesModal/RemainingTilesModal.module.scss +4 -1
  242. package/src/modals/RemainingTilesModal/RemainingTilesModal.module.scss.d.ts +9 -0
  243. package/src/modals/RemainingTilesModal/RemainingTilesModal.tsx +4 -6
  244. package/src/modals/RemainingTilesModal/components/Character/Character.module.scss +1 -0
  245. package/src/modals/RemainingTilesModal/components/Character/Character.module.scss.d.ts +12 -0
  246. package/src/modals/RemainingTilesModal/components/Character/Character.tsx +5 -12
  247. package/src/modals/ResultsModal/ResultsModal.module.scss.d.ts +8 -0
  248. package/src/modals/ResultsModal/ResultsModal.tsx +8 -4
  249. package/src/modals/SettingsModal/SettingsModal.tsx +2 -2
  250. package/src/modals/SettingsModal/components/AutoGroupTilesSetting/AutoGroupTilesSetting.module.scss.d.ts +7 -0
  251. package/src/modals/SettingsModal/components/AutoGroupTilesSetting/AutoGroupTilesSetting.tsx +1 -1
  252. package/src/modals/SettingsModal/components/ConfigSetting/ConfigSetting.module.scss.d.ts +7 -0
  253. package/src/modals/SettingsModal/components/ConfigSetting/ConfigSetting.tsx +1 -1
  254. package/src/modals/SettingsModal/components/HighlightUnreachableCellsSetting/HighlightUnreachableCellsSetting.module.scss.d.ts +7 -0
  255. package/src/modals/SettingsModal/components/HighlightUnreachableCellsSetting/HighlightUnreachableCellsSetting.tsx +1 -1
  256. package/src/modals/SettingsModal/components/InputModeSetting/InputModeSetting.module.scss.d.ts +7 -0
  257. package/src/modals/SettingsModal/components/InputModeSetting/InputModeSetting.tsx +1 -1
  258. package/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.module.scss.d.ts +8 -0
  259. package/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.tsx +24 -21
  260. package/src/modals/SettingsModal/components/RemoveCellFiltersSetting/RemoveCellFiltersSetting.module.scss.d.ts +7 -0
  261. package/src/modals/SettingsModal/components/RemoveCellFiltersSetting/RemoveCellFiltersSetting.tsx +1 -1
  262. package/src/modals/SettingsModal/components/ShowCoordinatesSetting/ShowCoordinatesSetting.module.scss.d.ts +7 -0
  263. package/src/modals/SettingsModal/components/ShowCoordinatesSetting/ShowCoordinatesSetting.tsx +1 -5
  264. package/src/modals/WordsModal/WordsModal.module.scss.d.ts +11 -0
  265. package/src/modals/WordsModal/WordsModal.tsx +4 -2
  266. package/src/modals/preload.ts +24 -0
  267. package/src/pages/_app.tsx +5 -8
  268. package/src/pages/_document.tsx +91 -5
  269. package/src/pages/api/dictionary/[locale]/[word].ts +2 -2
  270. package/src/pages/api/dictionary/[locale]/index.ts +80 -3
  271. package/src/pages/api/solve.ts +3 -3
  272. package/src/pages/api/verify.ts +12 -3
  273. package/src/pages/index.module.scss +1 -0
  274. package/src/pages/index.module.scss.d.ts +11 -0
  275. package/src/pages/index.tsx +98 -38
  276. package/src/pages/not-found.tsx +1 -1
  277. package/src/parameters/index.ts +24 -93
  278. package/src/sdk/solve.ts +13 -7
  279. package/src/sdk/verify.ts +12 -6
  280. package/src/service-worker/appShell.test.ts +86 -0
  281. package/src/service-worker/appShell.ts +39 -0
  282. package/src/service-worker/index.ts +4 -5
  283. package/src/serviceWorkerManager.ts +5 -19
  284. package/src/solver-worker/client.test.ts +151 -0
  285. package/src/solver-worker/client.ts +106 -0
  286. package/src/solver-worker/dictionaries/constants.ts +9 -0
  287. package/src/solver-worker/dictionaries/getDictionary.ts +21 -0
  288. package/src/solver-worker/dictionaries/index.ts +2 -0
  289. package/src/solver-worker/dictionaries/revalidateDictionary.test.ts +199 -0
  290. package/src/solver-worker/dictionaries/revalidateDictionary.ts +86 -0
  291. package/src/solver-worker/dictionaries/revalidatedAt.ts +43 -0
  292. package/src/solver-worker/getGaddag.test.ts +109 -0
  293. package/src/solver-worker/getGaddag.ts +62 -0
  294. package/src/solver-worker/index.ts +1 -0
  295. package/src/solver-worker/index.worker.ts +124 -0
  296. package/src/solver-worker/messages.ts +20 -0
  297. package/src/state/actions.ts +1 -1
  298. package/src/state/app/index.ts +4 -0
  299. package/src/state/app/initialState.ts +6 -0
  300. package/src/state/app/selectors.ts +5 -0
  301. package/src/state/app/slice.ts +20 -0
  302. package/src/state/app/types.ts +4 -0
  303. package/src/state/board/initialState.ts +1 -2
  304. package/src/state/i18n/index.ts +4 -0
  305. package/src/state/i18n/initialState.ts +5 -0
  306. package/src/state/i18n/selectors.ts +3 -0
  307. package/src/state/i18n/slice.ts +16 -0
  308. package/src/state/i18n/types.ts +7 -0
  309. package/src/state/index.ts +3 -0
  310. package/src/state/lib.ts +1 -1
  311. package/src/state/localStorage.ts +86 -29
  312. package/src/state/rack/initialState.ts +1 -2
  313. package/src/state/rack/slice.ts +4 -1
  314. package/src/state/results/lib.ts +5 -5
  315. package/src/state/results/selectors.ts +1 -1
  316. package/src/state/sagas.ts +139 -13
  317. package/src/state/selectors.ts +1 -1
  318. package/src/state/settings/initialState.ts +7 -10
  319. package/src/state/settings/selectors.ts +7 -2
  320. package/src/state/settings/slice.ts +5 -2
  321. package/src/state/store.ts +4 -0
  322. package/src/state/types.ts +4 -0
  323. package/src/state/useTypedStore.ts +6 -0
  324. package/src/styles/_tokens.scss +1 -0
  325. package/src/styles/global.scss +9 -0
  326. package/src/styles/variables.module.scss +0 -21
  327. package/src/styles/variables.module.scss.d.ts +27 -0
  328. package/src/styles/variables.scss +46 -2
  329. package/src/types/api.ts +1 -2
  330. package/src/types/index.ts +1 -2
  331. package/tsconfig.json +4 -0
  332. package/tsconfig.tsbuildinfo +1 -1
  333. package/.next/server/chunks/318.js +0 -3
  334. package/.next/server/chunks/791.js +0 -15
  335. package/.next/static/chunks/framework-3cf15b8041c59dd3.js +0 -1
  336. package/.next/static/chunks/main-852475ca502b4e49.js +0 -5
  337. package/.next/static/chunks/pages/_app-a5bbe02710f13d05.js +0 -7
  338. package/.next/static/chunks/pages/index-ec0f6aeeaf7be227.js +0 -1
  339. package/.next/static/chunks/pages/not-found-9e076427bad69d90.js +0 -1
  340. package/.next/static/chunks/webpack-1a7a166b34ed8d24.js +0 -1
  341. package/.next/static/css/0af35adb4bab56f0.css +0 -2
  342. package/.next/static/css/a78e04fe95452380.css +0 -1
  343. package/.next/static/vYpMmuC_yEurZGcyGVnLf/_buildManifest.js +0 -1
  344. package/src/app-layout/AppLayoutContext.tsx +0 -25
  345. package/src/app-layout/index.ts +0 -1
  346. package/src/app-layout/useAppLayoutValue.ts +0 -112
  347. package/src/components/Board/hooks/useBackgroundImage.tsx +0 -279
  348. package/src/components/index.ts +0 -26
  349. package/src/hooks/index.ts +0 -12
  350. package/src/hooks/useOnWindowResize.ts +0 -11
  351. package/src/hooks/useViewportSize.ts +0 -17
  352. package/src/i18n/index.ts +0 -2
  353. package/src/icons/Flag.svg +0 -4
  354. package/src/icons/Star.svg +0 -4
  355. package/src/icons/index.ts +0 -49
  356. package/src/lib/getTileSizes.ts +0 -13
  357. package/src/lib/index.ts +0 -24
  358. package/src/modals/index.ts +0 -7
  359. package/src/sdk/getDictionary.ts +0 -9
  360. package/src/service-worker/average.ts +0 -7
  361. package/src/service-worker/dictionaries/constants.ts +0 -5
  362. package/src/service-worker/dictionaries/getDictionary.ts +0 -20
  363. package/src/service-worker/dictionaries/index.ts +0 -2
  364. package/src/service-worker/dictionaries/revalidateDictionary.ts +0 -33
  365. package/src/service-worker/getTrie.ts +0 -24
  366. package/src/service-worker/routeSolveRequests.ts +0 -91
  367. package/src/service-worker/routeVerifyRequests.ts +0 -37
  368. /package/.next/static/{vYpMmuC_yEurZGcyGVnLf → FL82obwzFHRzvV6z76I-U}/_ssgManifest.js +0 -0
  369. /package/src/{service-worker → solver-worker}/dictionaries/expirationManager.ts +0 -0
  370. /package/src/{service-worker → solver-worker}/dictionaries/getDictionaryUrl.ts +0 -0
@@ -3,9 +3,9 @@ import classNames from 'classnames';
3
3
  import { type FunctionComponent, useCallback, useEffect } from 'react';
4
4
  import { useDispatch } from 'react-redux';
5
5
 
6
- import { Progress, Tile } from '@/components';
7
- import { LOCALE_FEATURES } from '@/i18n';
8
- import { REMAINING_TILES_TILE_SIZE } from '@/parameters';
6
+ import { Progress } from '@/components/Progress';
7
+ import { Tile } from '@/components/Tile';
8
+ import { LOCALE_FEATURES } from '@/i18n/constants';
9
9
  import {
10
10
  hoveredTileSlice,
11
11
  selectCharacterPoints,
@@ -75,19 +75,12 @@ export const Character: FunctionComponent<Props> = ({ tile }) => {
75
75
  highlighted={isHovered}
76
76
  isBlank={character === BLANK}
77
77
  isValid={remainingCount >= 0}
78
+ locale={locale}
78
79
  points={points}
79
80
  raised
80
- size={REMAINING_TILES_TILE_SIZE}
81
81
  />
82
82
 
83
- <Progress
84
- className={styles.remaining}
85
- max={count}
86
- style={{
87
- width: REMAINING_TILES_TILE_SIZE,
88
- }}
89
- value={remainingCount}
90
- />
83
+ <Progress className={styles.remaining} max={count} value={remainingCount} />
91
84
 
92
85
  <div className={styles.count}>
93
86
  {current.toLocaleString(locale)} / {total.toLocaleString(locale)}
@@ -0,0 +1,8 @@
1
+ // Generated by scripts/generate-scss-types.ts - do not edit.
2
+ declare const styles: {
3
+ readonly content: string;
4
+ readonly dictionary: string;
5
+ readonly results: string;
6
+ };
7
+
8
+ export default styles;
@@ -2,9 +2,13 @@ import { type Result } from '@scrabble-solver/types';
2
2
  import { type FunctionComponent, memo, useEffect, useMemo } from 'react';
3
3
  import { useDispatch } from 'react-redux';
4
4
 
5
- import { useAppLayout } from '@/app-layout';
6
- import { Button, Dictionary, Modal, Results } from '@/components';
7
- import { Check, EyeFill } from '@/icons';
5
+ import { Button } from '@/components/Button';
6
+ import { Dictionary } from '@/components/Dictionary';
7
+ import { Modal } from '@/components/Modal';
8
+ import { Results } from '@/components/Results';
9
+ import { useMediaQuery } from '@/hooks/useMediaQuery';
10
+ import Check from '@/icons/Check.svg';
11
+ import EyeFill from '@/icons/EyeFill.svg';
8
12
  import { resultsSlice, selectProcessedResults, selectResultCandidate, useTranslate, useTypedSelector } from '@/state';
9
13
 
10
14
  import styles from './ResultsModal.module.scss';
@@ -18,7 +22,7 @@ interface Props {
18
22
  const ResultsModalBase: FunctionComponent<Props> = ({ className, isOpen, onClose }) => {
19
23
  const dispatch = useDispatch();
20
24
  const translate = useTranslate();
21
- const { showResultsInModal } = useAppLayout();
25
+ const showResultsInModal = useMediaQuery('<l');
22
26
  const results = useTypedSelector(selectProcessedResults);
23
27
  const resultCandidate = useTypedSelector(selectResultCandidate);
24
28
  const index = results ? results.findIndex((result) => result.id === resultCandidate?.id) : -1;
@@ -1,7 +1,7 @@
1
1
  import { type FunctionComponent, memo } from 'react';
2
2
 
3
- import { Modal } from '@/components';
4
- import { useIsTouchDevice } from '@/hooks';
3
+ import { Modal } from '@/components/Modal';
4
+ import { useIsTouchDevice } from '@/hooks/useIsTouchDevice';
5
5
  import { useTranslate } from '@/state';
6
6
 
7
7
  import {
@@ -0,0 +1,7 @@
1
+ // Generated by scripts/generate-scss-types.ts - do not edit.
2
+ declare const styles: {
3
+ readonly label: string;
4
+ readonly option: string;
5
+ };
6
+
7
+ export default styles;
@@ -1,7 +1,7 @@
1
1
  import { type ChangeEvent, type FunctionComponent } from 'react';
2
2
  import { useDispatch } from 'react-redux';
3
3
 
4
- import { Radio } from '@/components';
4
+ import { Radio } from '@/components/Radio';
5
5
  import { selectAutoGroupTiles, settingsSlice, useTranslate, useTypedSelector } from '@/state';
6
6
 
7
7
  import styles from './AutoGroupTilesSetting.module.scss';
@@ -0,0 +1,7 @@
1
+ // Generated by scripts/generate-scss-types.ts - do not edit.
2
+ declare const styles: {
3
+ readonly label: string;
4
+ readonly option: string;
5
+ };
6
+
7
+ export default styles;
@@ -2,7 +2,7 @@ import { isGame } from '@scrabble-solver/types';
2
2
  import { type ChangeEvent, type FunctionComponent, useMemo } from 'react';
3
3
  import { useDispatch } from 'react-redux';
4
4
 
5
- import { Radio } from '@/components';
5
+ import { Radio } from '@/components/Radio';
6
6
  import { selectGame, selectLocale, settingsSlice, useTypedSelector } from '@/state';
7
7
 
8
8
  import styles from './ConfigSetting.module.scss';
@@ -0,0 +1,7 @@
1
+ // Generated by scripts/generate-scss-types.ts - do not edit.
2
+ declare const styles: {
3
+ readonly label: string;
4
+ readonly option: string;
5
+ };
6
+
7
+ export default styles;
@@ -1,7 +1,7 @@
1
1
  import { type ChangeEvent, type FunctionComponent } from 'react';
2
2
  import { useDispatch } from 'react-redux';
3
3
 
4
- import { Radio } from '@/components';
4
+ import { Radio } from '@/components/Radio';
5
5
  import { selectHighlightUnreachableCells, settingsSlice, useTranslate, useTypedSelector } from '@/state';
6
6
 
7
7
  import styles from './HighlightUnreachableCellsSetting.module.scss';
@@ -0,0 +1,7 @@
1
+ // Generated by scripts/generate-scss-types.ts - do not edit.
2
+ declare const styles: {
3
+ readonly label: string;
4
+ readonly option: string;
5
+ };
6
+
7
+ export default styles;
@@ -1,7 +1,7 @@
1
1
  import { type ChangeEvent, type FunctionComponent } from 'react';
2
2
  import { useDispatch } from 'react-redux';
3
3
 
4
- import { Radio } from '@/components';
4
+ import { Radio } from '@/components/Radio';
5
5
  import { selectInputMode, settingsSlice, useTranslate, useTypedSelector } from '@/state';
6
6
 
7
7
  import styles from './InputModeSetting.module.scss';
@@ -0,0 +1,8 @@
1
+ // Generated by scripts/generate-scss-types.ts - do not edit.
2
+ declare const styles: {
3
+ readonly flag: string;
4
+ readonly label: string;
5
+ readonly option: string;
6
+ };
7
+
8
+ export default styles;
@@ -3,8 +3,9 @@ import classNames from 'classnames';
3
3
  import { type ChangeEvent, type FunctionComponent } from 'react';
4
4
  import { useDispatch } from 'react-redux';
5
5
 
6
- import { Radio } from '@/components';
7
- import { LOCALE_FEATURES } from '@/i18n';
6
+ import { Radio } from '@/components/Radio';
7
+ import { LOCALE_FEATURES } from '@/i18n/constants';
8
+ import { LOCALE_ICONS } from '@/i18n/localeIcons';
8
9
  import { selectLocale, settingsSlice, useTypedSelector } from '@/state';
9
10
 
10
11
  import styles from './LocaleSetting.module.scss';
@@ -27,25 +28,27 @@ export const LocaleSetting: FunctionComponent<Props> = ({ className, disabled })
27
28
 
28
29
  return (
29
30
  <div className={className}>
30
- {OPTIONS.map(({ Icon, ...option }) => (
31
- <Radio
32
- checked={locale === option.locale}
33
- className={classNames(styles.option, className, {
34
- [styles.checked]: locale === option.locale,
35
- })}
36
- disabled={disabled}
37
- key={option.locale}
38
- name="locale"
39
- value={option.locale}
40
- onChange={handleChange}
41
- >
42
- <span className={styles.label}>
43
- <Icon aria-hidden="true" className={styles.flag} role="img" />
44
-
45
- <span>{option.label}</span>
46
- </span>
47
- </Radio>
48
- ))}
31
+ {OPTIONS.map((option) => {
32
+ const Icon = LOCALE_ICONS[option.locale];
33
+
34
+ return (
35
+ <Radio
36
+ checked={locale === option.locale}
37
+ className={classNames(styles.option, className)}
38
+ disabled={disabled}
39
+ key={option.locale}
40
+ name="locale"
41
+ value={option.locale}
42
+ onChange={handleChange}
43
+ >
44
+ <span className={styles.label}>
45
+ <Icon aria-hidden="true" className={styles.flag} role="img" />
46
+
47
+ <span>{option.label}</span>
48
+ </span>
49
+ </Radio>
50
+ );
51
+ })}
49
52
  </div>
50
53
  );
51
54
  };
@@ -0,0 +1,7 @@
1
+ // Generated by scripts/generate-scss-types.ts - do not edit.
2
+ declare const styles: {
3
+ readonly label: string;
4
+ readonly option: string;
5
+ };
6
+
7
+ export default styles;
@@ -1,7 +1,7 @@
1
1
  import { type ChangeEvent, type FunctionComponent } from 'react';
2
2
  import { useDispatch } from 'react-redux';
3
3
 
4
- import { Radio } from '@/components';
4
+ import { Radio } from '@/components/Radio';
5
5
  import { selectRemoveCellFilters, settingsSlice, useTranslate, useTypedSelector } from '@/state';
6
6
 
7
7
  import { parseValue } from './lib';
@@ -0,0 +1,7 @@
1
+ // Generated by scripts/generate-scss-types.ts - do not edit.
2
+ declare const styles: {
3
+ readonly label: string;
4
+ readonly option: string;
5
+ };
6
+
7
+ export default styles;
@@ -2,7 +2,7 @@ import { type ShowCoordinates } from '@scrabble-solver/types';
2
2
  import { type ChangeEvent, type FunctionComponent } from 'react';
3
3
  import { useDispatch } from 'react-redux';
4
4
 
5
- import { Radio } from '@/components';
5
+ import { Radio } from '@/components/Radio';
6
6
  import { selectShowCoordinates, settingsSlice, useTranslate, useTypedSelector } from '@/state';
7
7
 
8
8
  import styles from './ShowCoordinatesSetting.module.scss';
@@ -18,10 +18,6 @@ export const ShowCoordinatesSetting: FunctionComponent<Props> = ({ className, di
18
18
  const value = useTypedSelector(selectShowCoordinates);
19
19
 
20
20
  const options = [
21
- {
22
- label: translate('settings.showCoordinates.hidden'),
23
- value: 'hidden',
24
- },
25
21
  {
26
22
  label: translate('settings.showCoordinates.original'),
27
23
  value: 'original',
@@ -0,0 +1,11 @@
1
+ // Generated by scripts/generate-scss-types.ts - do not edit.
2
+ declare const styles: {
3
+ readonly badge: string;
4
+ readonly icon: string;
5
+ readonly invalid: string;
6
+ readonly title: string;
7
+ readonly valid: string;
8
+ readonly word: string;
9
+ };
10
+
11
+ export default styles;
@@ -1,8 +1,10 @@
1
1
  import classNames from 'classnames';
2
2
  import { type FunctionComponent, memo } from 'react';
3
3
 
4
- import { Badge, Modal } from '@/components';
5
- import { Check, Cross } from '@/icons';
4
+ import { Badge } from '@/components/Badge';
5
+ import { Modal } from '@/components/Modal';
6
+ import Check from '@/icons/Check.svg';
7
+ import Cross from '@/icons/Cross.svg';
6
8
  import { selectInvalidWords, selectLocale, selectValidWords, useTranslate, useTypedSelector } from '@/state';
7
9
 
8
10
  import styles from './WordsModal.module.scss';
@@ -0,0 +1,24 @@
1
+ import { waitForIdleOrFirstIntent } from '@/lib/waitForIdleOrFirstIntent';
2
+
3
+ let isScheduled = false;
4
+
5
+ export function schedulePreloadModals(): void {
6
+ if (isScheduled || typeof window === 'undefined') {
7
+ return;
8
+ }
9
+
10
+ isScheduled = true;
11
+
12
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
13
+ waitForIdleOrFirstIntent().then(() =>
14
+ Promise.all([
15
+ import('./DictionaryModal'),
16
+ import('./KeyMapModal'),
17
+ import('./MenuModal'),
18
+ import('./RemainingTilesModal'),
19
+ import('./ResultsModal'),
20
+ import('./SettingsModal'),
21
+ import('./WordsModal'),
22
+ ]),
23
+ );
24
+ }
@@ -4,8 +4,7 @@ import Head from 'next/head';
4
4
  import { type FunctionComponent } from 'react';
5
5
  import { Provider } from 'react-redux';
6
6
 
7
- import { AppLayoutProvider } from '@/app-layout';
8
- import { SeoMessage } from '@/components';
7
+ import { SeoMessage } from '@/components/SeoMessage';
9
8
  import { store } from '@/state';
10
9
 
11
10
  import '../styles/global.scss';
@@ -77,13 +76,11 @@ const App: FunctionComponent<AppProps> = ({ Component, pageProps }) => (
77
76
  </Head>
78
77
 
79
78
  <Provider store={store}>
80
- <AppLayoutProvider>
81
- <SeoMessage />
79
+ <SeoMessage />
82
80
 
83
- <FloatingDelayGroup delay={0}>
84
- <Component {...pageProps} />
85
- </FloatingDelayGroup>
86
- </AppLayoutProvider>
81
+ <FloatingDelayGroup delay={0}>
82
+ <Component {...pageProps} />
83
+ </FloatingDelayGroup>
87
84
  </Provider>
88
85
  </>
89
86
  );
@@ -1,11 +1,75 @@
1
+ import { games } from '@scrabble-solver/configs';
2
+ import { Game, Locale } from '@scrabble-solver/types';
3
+ import fs from 'fs';
1
4
  import Document, { Head, Html, Main, NextScript } from 'next/document';
5
+ import path from 'path';
2
6
  import { type ReactElement } from 'react';
3
7
 
4
- class MyDocument extends Document {
8
+ import { LOCALE_FEATURES } from '@/i18n/constants';
9
+ import { CONFIG_PENDING_CLASS } from '@/parameters';
10
+
11
+ const GAME_DIMENSIONS = Object.fromEntries(
12
+ Object.values(games).map(({ boardHeight, boardWidth, game, rackSize }) => [
13
+ game,
14
+ [boardWidth, boardHeight, rackSize],
15
+ ]),
16
+ );
17
+
18
+ const LOCALE_DIRECTIONS = Object.fromEntries(
19
+ Object.entries(LOCALE_FEATURES).map(([locale, { direction }]) => [locale, direction]),
20
+ );
21
+
22
+ /**
23
+ * The SSR HTML is always an English Scrabble board. This runs before first paint
24
+ * so a returning user's persisted board dimensions and text direction apply without a flash.
25
+ * The board itself cannot be reshaped pre-hydration, so it is hidden (CONFIG_PENDING_CLASS,
26
+ * see global.scss) until hydration applies the persisted config.
27
+ */
28
+ const preHydrationScript = `(function () {
29
+ try {
30
+ var settings = JSON.parse(localStorage.getItem('scrabble-solver.settings'));
31
+
32
+ if (!settings) {
33
+ return;
34
+ }
35
+
36
+ var dimensions = ${JSON.stringify(GAME_DIMENSIONS)}[settings.game];
37
+ var direction = ${JSON.stringify(LOCALE_DIRECTIONS)}[settings.locale];
38
+ var root = document.documentElement;
39
+
40
+ if (dimensions) {
41
+ root.style.setProperty('--board-cols', dimensions[0]);
42
+ root.style.setProperty('--board-rows', dimensions[1]);
43
+ root.style.setProperty('--rack-size', dimensions[2]);
44
+
45
+ if (settings.game !== ${JSON.stringify(Game.Scrabble)}) {
46
+ root.classList.add(${JSON.stringify(CONFIG_PENDING_CLASS)});
47
+ }
48
+ }
49
+
50
+ if (direction) {
51
+ root.dir = direction;
52
+ root.lang = settings.locale;
53
+ }
54
+ } catch (error) {}
55
+ })()`;
56
+
57
+ export default class MyDocument extends Document {
5
58
  render(): ReactElement {
6
59
  return (
7
- <Html lang="en">
8
- <Head>
60
+ <Html
61
+ /**
62
+ * dir must be present pre-hydration
63
+ */
64
+ dir="ltr"
65
+ /**
66
+ * lang must match the default locale
67
+ */
68
+ lang={Locale.EN_US}
69
+ >
70
+ <InlineCssHead>
71
+ {/* eslint-disable-next-line react/no-danger */}
72
+ <script dangerouslySetInnerHTML={{ __html: preHydrationScript }} />
9
73
  <link rel="apple-touch-icon-precomposed" sizes="57x57" href="icons/apple-touch-icon-57x57.png" />
10
74
  <link rel="apple-touch-icon-precomposed" sizes="114x114" href="icons/apple-touch-icon-114x114.png" />
11
75
  <link rel="apple-touch-icon-precomposed" sizes="72x72" href="icons/apple-touch-icon-72x72.png" />
@@ -27,7 +91,7 @@ class MyDocument extends Document {
27
91
  <meta name="msapplication-square150x150logo" content="icons/mstile-150x150.png" />
28
92
  <meta name="msapplication-wide310x150logo" content="icons/mstile-310x150.png" />
29
93
  <meta name="msapplication-square310x310logo" content="icons/mstile-310x310.png" />
30
- </Head>
94
+ </InlineCssHead>
31
95
 
32
96
  <body>
33
97
  <Main />
@@ -38,4 +102,26 @@ class MyDocument extends Document {
38
102
  }
39
103
  }
40
104
 
41
- export default MyDocument;
105
+ /**
106
+ * Removes render-blocking CSS requests from the critical path.
107
+ */
108
+ class InlineCssHead extends Head {
109
+ getCssLinks(files: Parameters<Head['getCssLinks']>[0]): ReactElement[] | null {
110
+ try {
111
+ const cssFiles = files.allFiles.filter((file) => file.endsWith('.css'));
112
+
113
+ return cssFiles.map((file) => (
114
+ <style
115
+ // eslint-disable-next-line react/no-danger
116
+ dangerouslySetInnerHTML={{
117
+ __html: fs.readFileSync(path.join(process.cwd(), '.next', file), 'utf-8'),
118
+ }}
119
+ data-href={`/_next/${file}`}
120
+ key={file}
121
+ />
122
+ ));
123
+ } catch {
124
+ return super.getCssLinks(files);
125
+ }
126
+ }
127
+ }
@@ -30,8 +30,8 @@ const dictionary = async (request: NextApiRequest, response: NextApiResponse): P
30
30
  },
31
31
  });
32
32
 
33
- const trie = await dictionaries.get(locale);
34
- const results = await Promise.all(words.map((word) => getWordDefinition(locale, word, trie.has(word))));
33
+ const gaddag = await dictionaries.get(locale);
34
+ const results = await Promise.all(words.map((word) => getWordDefinition(locale, word, gaddag.has(word))));
35
35
  response.status(200).send(results.map((result) => result.toJson()));
36
36
  } catch (error) {
37
37
  const message = error instanceof Error ? error.message : 'Unknown error';
@@ -1,7 +1,10 @@
1
+ import { type Gaddag } from '@kamilmielnik/gaddag';
1
2
  import { dictionaries } from '@scrabble-solver/dictionaries';
2
3
  import { logger } from '@scrabble-solver/logger';
3
4
  import { isLocale, type Locale } from '@scrabble-solver/types';
4
5
  import { type NextApiRequest, type NextApiResponse } from 'next';
6
+ import { promisify } from 'util';
7
+ import { gzip } from 'zlib';
5
8
 
6
9
  import { getServerLoggingData } from '@/api';
7
10
 
@@ -22,8 +25,21 @@ const dictionary = async (request: NextApiRequest, response: NextApiResponse): P
22
25
  },
23
26
  });
24
27
 
25
- const trie = await dictionaries.get(locale);
26
- response.status(200).send(trie.serialize());
28
+ const gaddag = await dictionaries.get(locale);
29
+ const isGzip = acceptsGzip(request);
30
+ const body = isGzip ? await getCompressedDictionary(gaddag) : getSerializedDictionary(gaddag);
31
+
32
+ // no-cache makes clients store the payload but revalidate it, so unchanged
33
+ // re-downloads become 304s, answered by Next's built-in ETag handling.
34
+ response.setHeader('Cache-Control', 'no-cache');
35
+ response.setHeader('Content-Type', 'application/octet-stream');
36
+ response.setHeader('Vary', 'Accept-Encoding');
37
+
38
+ if (isGzip) {
39
+ response.setHeader('Content-Encoding', 'gzip');
40
+ }
41
+
42
+ response.status(200).send(body);
27
43
  } catch (error) {
28
44
  const message = error instanceof Error ? error.message : 'Unknown error';
29
45
  logger.error('dictionary - error', { error, meta });
@@ -43,9 +59,70 @@ const parseRequest = (request: NextApiRequest): RequestData => {
43
59
  };
44
60
  };
45
61
 
62
+ const acceptsGzip = (request: NextApiRequest): boolean => {
63
+ const acceptEncoding = request.headers['accept-encoding'];
64
+ const header = Array.isArray(acceptEncoding) ? acceptEncoding.join(',') : acceptEncoding;
65
+
66
+ if (typeof header !== 'string') {
67
+ return false;
68
+ }
69
+
70
+ let wildcardAccepts = false;
71
+
72
+ for (const entry of header.split(',')) {
73
+ const [encoding, ...parameters] = entry.split(';').map((token) => token.trim().toLowerCase());
74
+ const isRefused = parameters.some((parameter) => {
75
+ const [name, quality] = parameter.split('=').map((token) => token.trim());
76
+ return name === 'q' && parseFloat(quality) === 0;
77
+ });
78
+
79
+ if (encoding === 'gzip' || encoding === 'x-gzip') {
80
+ return !isRefused;
81
+ }
82
+
83
+ if (encoding === '*') {
84
+ wildcardAccepts = !isRefused;
85
+ }
86
+ }
87
+
88
+ return wildcardAccepts;
89
+ };
90
+
91
+ // Dictionaries rebuild at most daily, so each response variant is produced
92
+ // once and lives exactly as long as the in-memory dictionary it was made from.
93
+ const serializedCache = new WeakMap<Gaddag, Buffer>();
94
+ const compressedCache = new WeakMap<Gaddag, Promise<Buffer>>();
95
+
96
+ const getSerializedDictionary = (gaddag: Gaddag): Buffer => {
97
+ let serialized = serializedCache.get(gaddag);
98
+
99
+ if (!serialized) {
100
+ // serialize() returns freshly allocated bytes, so the Buffer wraps them without copying.
101
+ const bytes = gaddag.serialize();
102
+ serialized = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
103
+ serializedCache.set(gaddag, serialized);
104
+ }
105
+
106
+ return serialized;
107
+ };
108
+
109
+ const gzipAsync = promisify(gzip);
110
+
111
+ const getCompressedDictionary = (gaddag: Gaddag): Promise<Buffer> => {
112
+ let compressed = compressedCache.get(gaddag);
113
+
114
+ if (!compressed) {
115
+ compressed = gzipAsync(gaddag.serialize());
116
+ compressedCache.set(gaddag, compressed);
117
+ compressed.catch(() => compressedCache.delete(gaddag));
118
+ }
119
+
120
+ return compressed;
121
+ };
122
+
46
123
  export const config = {
47
124
  api: {
48
- responseLimit: '25mb',
125
+ responseLimit: '50mb',
49
126
  },
50
127
  };
51
128
 
@@ -16,7 +16,7 @@ import {
16
16
  import { type NextApiRequest, type NextApiResponse } from 'next';
17
17
 
18
18
  import { getServerLoggingData, isBoardValid, isCharacterValid } from '@/api';
19
- import { isStringArray } from '@/lib';
19
+ import { isStringArray } from '@/lib/isStringArray';
20
20
 
21
21
  interface RequestData {
22
22
  board: Board;
@@ -45,9 +45,9 @@ const solve = async (request: NextApiRequest, response: NextApiResponse): Promis
45
45
  },
46
46
  });
47
47
 
48
- const trie = await dictionaries.get(locale);
48
+ const gaddag = await dictionaries.get(locale);
49
49
  const tiles = characters.map((character) => new Tile({ character, isBlank: character === BLANK }));
50
- const results = solveScrabble(trie, config, board, tiles);
50
+ const results = solveScrabble(gaddag, config, board, tiles);
51
51
  response.status(200).send(results);
52
52
  } catch (error) {
53
53
  const message = error instanceof Error ? error.message : 'Unknown error';
@@ -30,10 +30,19 @@ const verify = async (request: NextApiRequest, response: NextApiResponse): Promi
30
30
  },
31
31
  });
32
32
 
33
- const trie = await dictionaries.get(locale);
33
+ const gaddag = await dictionaries.get(locale);
34
34
  const words = board.getWords().sort((a, b) => a.localeCompare(b, locale));
35
- const invalidWords = words.filter((word) => !trie.has(word));
36
- const validWords = words.filter((word) => trie.has(word));
35
+ const invalidWords: string[] = [];
36
+ const validWords: string[] = [];
37
+
38
+ for (const word of words) {
39
+ if (gaddag.has(word)) {
40
+ validWords.push(word);
41
+ } else {
42
+ invalidWords.push(word);
43
+ }
44
+ }
45
+
37
46
  response.status(200).send({ invalidWords, validWords });
38
47
  } catch (error) {
39
48
  const message = error instanceof Error ? error.message : 'Unknown error';
@@ -22,6 +22,7 @@
22
22
 
23
23
  .logo {
24
24
  height: var(--logo--height);
25
+ width: auto;
25
26
  user-select: none;
26
27
  }
27
28
 
@@ -0,0 +1,11 @@
1
+ // Generated by scripts/generate-scss-types.ts - do not edit.
2
+ declare const styles: {
3
+ readonly logo: string;
4
+ readonly logoContainer: string;
5
+ readonly nav: string;
6
+ readonly navContent: string;
7
+ readonly navLogo: string;
8
+ readonly solver: string;
9
+ };
10
+
11
+ export default styles;