@scrabble-solver/scrabble-solver 2.15.8 → 2.15.9

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 (60) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +7 -7
  3. package/.next/cache/.rscinfo +1 -1
  4. package/.next/cache/.tsbuildinfo +1 -1
  5. package/.next/cache/eslint/.cache_8dgz12 +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/0.pack +0 -0
  10. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  11. package/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
  12. package/.next/cache/webpack/server-production/0.pack +0 -0
  13. package/.next/cache/webpack/server-production/index.pack +0 -0
  14. package/.next/cache/webpack/server-production/index.pack.old +0 -0
  15. package/.next/next-minimal-server.js.nft.json +1 -1
  16. package/.next/next-server.js.nft.json +1 -1
  17. package/.next/prerender-manifest.json +4 -4
  18. package/.next/routes-manifest.json +1 -1
  19. package/.next/server/chunks/60.js +1 -1
  20. package/.next/server/chunks/974.js +1 -1
  21. package/.next/server/middleware-build-manifest.js +1 -1
  22. package/.next/server/pages/404.html +1 -1
  23. package/.next/server/pages/500.html +1 -1
  24. package/.next/server/pages/_app.js +1 -1
  25. package/.next/server/pages/_app.js.nft.json +1 -1
  26. package/.next/server/pages/_error.js +1 -1
  27. package/.next/server/pages/_error.js.nft.json +1 -1
  28. package/.next/server/pages/api/dictionary/[locale]/[word].js +2 -2
  29. package/.next/server/pages/api/dictionary/[locale]/[word].js.nft.json +1 -1
  30. package/.next/server/pages/api/dictionary/[locale].js +1 -1
  31. package/.next/server/pages/api/dictionary/[locale].js.nft.json +1 -1
  32. package/.next/server/pages/api/solve.js +1 -1
  33. package/.next/server/pages/api/solve.js.nft.json +1 -1
  34. package/.next/server/pages/api/verify.js +1 -1
  35. package/.next/server/pages/api/verify.js.nft.json +1 -1
  36. package/.next/server/pages/index.html +1 -1
  37. package/.next/server/pages/index.js +1 -1
  38. package/.next/server/pages/index.js.nft.json +1 -1
  39. package/.next/server/pages/index.json +1 -1
  40. package/.next/server/pages-manifest.json +1 -1
  41. package/.next/static/chunks/pages/_app-735105409cfdb48f.js +1 -0
  42. package/.next/static/chunks/webpack-6224d37324e372cb.js +1 -0
  43. package/.next/trace +23 -23
  44. package/LICENSE +1 -1
  45. package/package.json +10 -9
  46. package/src/components/Board/Board.tsx +1 -1
  47. package/src/components/Board/hooks/useBoardStyle.ts +1 -1
  48. package/src/components/Board/hooks/useGrid.ts +5 -5
  49. package/src/components/EmptyState/EmptyState.tsx +2 -2
  50. package/src/components/Rack/Rack.tsx +7 -7
  51. package/src/components/Rack/components/InputPrompt/InputPrompt.tsx +1 -3
  52. package/src/components/Rack/components/RackTile/RackTile.tsx +6 -4
  53. package/src/hooks/useEffectOnce.ts +9 -1
  54. package/src/lib/extractCharacters.test.ts +2 -1
  55. package/src/lib/extractCharacters.ts +16 -2
  56. package/src/state/sagas.ts +1 -1
  57. package/.next/static/chunks/pages/_app-34618eeeff128581.js +0 -1
  58. package/.next/static/chunks/webpack-4224e970a97a4a86.js +0 -1
  59. /package/.next/static/{IJuVKnkd8P2us9rVuan82 → 3MlxnccTCLyz9bgD30zY2}/_buildManifest.js +0 -0
  60. /package/.next/static/{IJuVKnkd8P2us9rVuan82 → 3MlxnccTCLyz9bgD30zY2}/_ssgManifest.js +0 -0
package/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2024 Kamil Mielnik <kamil@kamilmielnik.com>
1
+ Copyright (c) 2025 Kamil Mielnik <kamil@kamilmielnik.com>
2
2
 
3
3
  Attribution-NonCommercial-NoDerivatives 4.0 International
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scrabble-solver/scrabble-solver",
3
- "version": "2.15.8",
3
+ "version": "2.15.9",
4
4
  "description": "Scrabble Solver 2 - App",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,13 +27,13 @@
27
27
  "@floating-ui/react": "^0.27.5",
28
28
  "@kamilmielnik/trie": "^4.0.0",
29
29
  "@reduxjs/toolkit": "^2.6.1",
30
- "@scrabble-solver/configs": "^2.15.8",
31
- "@scrabble-solver/constants": "^2.15.8",
32
- "@scrabble-solver/dictionaries": "^2.15.8",
33
- "@scrabble-solver/logger": "^2.15.8",
34
- "@scrabble-solver/solver": "^2.15.8",
35
- "@scrabble-solver/types": "^2.15.8",
36
- "@scrabble-solver/word-definitions": "^2.15.8",
30
+ "@scrabble-solver/configs": "^2.15.9",
31
+ "@scrabble-solver/constants": "^2.15.9",
32
+ "@scrabble-solver/dictionaries": "^2.15.9",
33
+ "@scrabble-solver/logger": "^2.15.9",
34
+ "@scrabble-solver/solver": "^2.15.9",
35
+ "@scrabble-solver/types": "^2.15.9",
36
+ "@scrabble-solver/word-definitions": "^2.15.9",
37
37
  "classnames": "^2.5.1",
38
38
  "env-cmd": "^10.1.0",
39
39
  "include-media": "^2.0.0",
@@ -49,6 +49,7 @@
49
49
  "react-window": "^1.8.11",
50
50
  "redux-saga": "^1.3.0",
51
51
  "store2": "^2.14.4",
52
+ "transliteration": "^2.3.5",
52
53
  "use-debounce": "^10.0.4",
53
54
  "workbox-expiration": "^7.3.0",
54
55
  "workbox-precaching": "^7.3.0",
@@ -70,5 +71,5 @@
70
71
  "@types/redux-saga": "^0.10.5",
71
72
  "sass": "^1.86.0"
72
73
  },
73
- "gitHead": "da3ecd2d83f48bcc6aa7d43a6dc937d103e1312d"
74
+ "gitHead": "d0654f37094c58a9982d7984e3fc8722f1fa24f0"
74
75
  }
@@ -116,7 +116,7 @@ const Board: FunctionComponent<Props> = ({ className }) => {
116
116
  dispatch(solveSlice.actions.submit());
117
117
  setHasFocus(false);
118
118
  },
119
- [activeIndex, dispatch, locale],
119
+ [activeIndex, dispatch, insertValue, locale],
120
120
  );
121
121
 
122
122
  const handleToggleBlank = useCallback(() => {
@@ -27,7 +27,7 @@ const useBoardStyle = () => {
27
27
  ? `repeat(${config.boardSize}, 1fr)`
28
28
  : `${coordinatesSize}px repeat(${config.boardSize}, 1fr)`,
29
29
  }),
30
- [backgroundImage, config.boardSize, tileFontSize],
30
+ [backgroundImage, config.boardSize, coordinatesSize, showCoordinates, tileFontSize],
31
31
  );
32
32
 
33
33
  return boardStyle;
@@ -69,7 +69,7 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
69
69
  setActiveIndex({ x, y });
70
70
  inputRefs[y][x].current?.focus();
71
71
  },
72
- [safeActiveIndex, inputRefs],
72
+ [safeActiveIndex, height, inputRefs, width],
73
73
  );
74
74
 
75
75
  const getInputRefPosition = useCallback(
@@ -188,7 +188,7 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
188
188
  moveFocus(Math.abs(position.x - x) + Math.abs(position.y - y));
189
189
  actions.forEach(dispatch);
190
190
  },
191
- [config, directionRef, dispatch, moveFocus, rows],
191
+ [config, directionRef, dispatch, height, moveFocus, rows, width],
192
192
  );
193
193
 
194
194
  const onChange = useCallback(
@@ -219,7 +219,7 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
219
219
 
220
220
  insertValue(position, value);
221
221
  },
222
- [dispatch, insertValue, moveFocus, rows],
222
+ [dispatch, insertValue, moveFocus, rows, getInputRefPosition],
223
223
  );
224
224
 
225
225
  const onDirectionToggle = useCallback(() => setLastDirection(toggleDirection), []);
@@ -347,7 +347,7 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
347
347
  dispatch(boardSlice.actions.toggleCellIsBlank(position));
348
348
  },
349
349
  });
350
- }, [changeActiveIndex, config, direction, dispatch, locale, moveFocus, onDirectionToggle, rows]);
350
+ }, [changeActiveIndex, config, direction, dispatch, getInputRefPosition, locale, moveFocus, onDirectionToggle, rows]);
351
351
 
352
352
  const onPaste = useCallback<ClipboardEventHandler>(
353
353
  (event) => {
@@ -366,7 +366,7 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
366
366
  const value = event.clipboardData.getData('text/plain').toLocaleLowerCase();
367
367
  insertValue(position, value);
368
368
  },
369
- [insertValue],
369
+ [getInputRefPosition, insertValue],
370
370
  );
371
371
 
372
372
  return [
@@ -34,9 +34,9 @@ const EmptyState: FunctionComponent<Props> = ({ children, className, variant })
34
34
  const translate = useTranslate();
35
35
  const locale = useTypedSelector(selectLocale);
36
36
  const { direction } = LOCALE_FEATURES[locale];
37
- const title = useMemo(() => translate(TITLE_KEY_PER_TYPE[variant]), [translate]);
37
+ const title = useMemo(() => translate(TITLE_KEY_PER_TYPE[variant]), [translate, variant]);
38
38
  const message = direction === 'ltr' ? title : title.split('').reverse().join('');
39
- const content = useMemo(() => [message.toUpperCase().split(' ')], [title]);
39
+ const content = useMemo(() => [message.toUpperCase().split(' ')], [message]);
40
40
 
41
41
  return (
42
42
  <div className={classNames(styles.emptyState, className)}>
@@ -67,6 +67,11 @@ const Rack: FunctionComponent<Props> = ({ className, tileSize }) => {
67
67
  const showInputPrompt = inputMode === 'touchscreen' && hasFocus;
68
68
  const ref = useRef<HTMLDivElement>(null);
69
69
 
70
+ const floatingInputPrompt = useFloating({
71
+ placement: 'bottom-start',
72
+ whileElementsMounted: autoUpdate,
73
+ });
74
+
70
75
  useOnclickOutside(() => setHasFocus(false), {
71
76
  ignoreClass: [InputPrompt.styles.form, InputPrompt.styles.input],
72
77
  refs: ref.current ? [ref as RefObject<HTMLDivElement>] : [],
@@ -120,7 +125,7 @@ const Rack: FunctionComponent<Props> = ({ className, tileSize }) => {
120
125
  return character.length > 1 ? character.toLocaleUpperCase(locale) : character;
121
126
  });
122
127
  setInput(uppercasedDigraphs.join(''));
123
- }, [rack, ref]);
128
+ }, [floatingInputPrompt.refs, locale, rack, ref]);
124
129
 
125
130
  const handleKeyDown = useMemo(() => {
126
131
  return createKeyboardNavigation({
@@ -159,12 +164,7 @@ const Rack: FunctionComponent<Props> = ({ className, tileSize }) => {
159
164
  }
160
165
  },
161
166
  });
162
- }, [changeActiveIndex, config, direction]);
163
-
164
- const floatingInputPrompt = useFloating({
165
- placement: 'bottom-start',
166
- whileElementsMounted: autoUpdate,
167
- });
167
+ }, [changeActiveIndex, config, direction, dispatch]);
168
168
 
169
169
  return (
170
170
  <>
@@ -1,5 +1,3 @@
1
- /* eslint-disable max-lines, max-statements */
2
-
3
1
  import classNames from 'classnames';
4
2
  import {
5
3
  CSSProperties,
@@ -45,7 +43,7 @@ const InputPrompt = forwardRef<HTMLFormElement, Props>(
45
43
  dispatch(rackSlice.actions.changeCharacters({ characters, index: 0 }));
46
44
  onSubmit(event);
47
45
  },
48
- [config, value, onSubmit],
46
+ [config, dispatch, value, onSubmit],
49
47
  );
50
48
 
51
49
  useEffect(() => {
@@ -87,7 +87,7 @@ const RackTile: FunctionComponent<Props> = ({
87
87
  dispatch(rackSlice.actions.changeCharacters({ characters, index }));
88
88
  onChange(event);
89
89
  },
90
- [config, index, onChange],
90
+ [config, dispatch, index, onChange],
91
91
  );
92
92
 
93
93
  const handleKeyDown = useMemo(() => {
@@ -101,14 +101,16 @@ const RackTile: FunctionComponent<Props> = ({
101
101
  event.preventDefault();
102
102
  event.stopPropagation();
103
103
  const twoTilesCharacter = config.getTwoCharacterTileByPrefix(event.key);
104
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
105
- dispatch(rackSlice.actions.changeCharacter({ character: twoTilesCharacter!, index }));
104
+
105
+ if (twoTilesCharacter) {
106
+ dispatch(rackSlice.actions.changeCharacter({ character: twoTilesCharacter!, index }));
107
+ }
106
108
  }
107
109
 
108
110
  onKeyDown(event);
109
111
  },
110
112
  });
111
- }, [index, onKeyDown]);
113
+ }, [config, dispatch, index, onKeyDown]);
112
114
 
113
115
  const handleMouseDown: MouseEventHandler<HTMLInputElement> = useCallback(
114
116
  (event) => {
@@ -1,5 +1,13 @@
1
1
  import { EffectCallback, useEffect } from 'react';
2
2
 
3
- const useEffectOnce = (effect: EffectCallback) => useEffect(effect, []);
3
+ import useLatest from './useLatest';
4
+
5
+ const useEffectOnce = (effect: EffectCallback) => {
6
+ const effectRef = useLatest(effect);
7
+
8
+ return useEffect(() => {
9
+ effectRef.current();
10
+ }, [effectRef]);
11
+ };
4
12
 
5
13
  export default useEffectOnce;
@@ -6,7 +6,8 @@ import extractCharacters from './extractCharacters';
6
6
 
7
7
  const tests = [
8
8
  { input: 'ab ', expected: ['a', 'b', BLANK] },
9
- { input: 'śćźa', expected: ['a'] },
9
+ { input: 'śćźa', expected: ['s', 'c', 'z', 'a'] },
10
+ { input: 'bañó', expected: ['b', 'a', 'ñ', 'o'] },
10
11
  { input: 'bueno', expected: ['b', 'u', 'e', 'n', 'o'] },
11
12
  { input: 'bellas', expected: ['b', 'e', 'll', 'a', 's'] },
12
13
  { input: 'BELLAS', expected: ['b', 'e', 'll', 'a', 's'] },
@@ -1,10 +1,24 @@
1
1
  import { BLANK } from '@scrabble-solver/constants';
2
- import { Config } from '@scrabble-solver/types';
2
+ import { Config, Locale } from '@scrabble-solver/types';
3
+ import { transliterate } from 'transliteration';
4
+
5
+ const transliteratePerLocale: Record<Locale, (word: string) => string> = {
6
+ [Locale.DE_DE]: (word) => word,
7
+ [Locale.EN_GB]: (word) => word,
8
+ [Locale.EN_US]: (word) => word,
9
+ [Locale.ES_ES]: (word) => transliterate(word, { ignore: ['ñ'] }),
10
+ [Locale.FA_IR]: (word) => word,
11
+ [Locale.FR_FR]: (word) => transliterate(word),
12
+ [Locale.PL_PL]: (word) => word,
13
+ [Locale.RO_RO]: (word) => transliterate(word),
14
+ [Locale.TR_TR]: (word) => word,
15
+ };
3
16
 
4
17
  const extractCharacters = (config: Config, value: string): string[] => {
5
18
  let index = 0;
6
19
  const characters: string[] = [];
7
- const valueLowercase = value.toLocaleLowerCase(config.locale);
20
+ const localeTransliterate = transliteratePerLocale[config.locale];
21
+ const valueLowercase = localeTransliterate(value.toLocaleLowerCase(config.locale));
8
22
 
9
23
  while (index < valueLowercase.length) {
10
24
  const character = valueLowercase[index];
@@ -1,4 +1,4 @@
1
- /* eslint-disable max-lines */
1
+ /* eslint-disable max-lines, no-implicit-globals */
2
2
 
3
3
  import { PayloadAction } from '@reduxjs/toolkit';
4
4
  import { hasConfig, languages } from '@scrabble-solver/configs';