scrabble-solver 2.18.0 → 2.18.1
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.
- package/.nx/workspace-data/d/server-process.json +2 -2
- package/.nx/workspace-data/file-map.json +1481 -1485
- package/.nx/workspace-data/lockfile-dependencies.hash +1 -1
- package/.nx/workspace-data/lockfile-nodes.hash +1 -1
- package/.nx/workspace-data/nx_files.nxt +0 -0
- package/.nx/workspace-data/project-graph.json +71 -28
- package/.nx/workspace-data/source-maps.json +172 -0
- package/CLAUDE.md +1 -1
- package/bun.lock +29 -29
- package/e2e/app.spec.ts +1 -1
- package/e2e/features/solver-paths.spec.ts +2 -2
- package/lerna.json +1 -1
- package/package.json +18 -37
- package/packages/configs/package.json +13 -4
- package/packages/constants/package.json +6 -2
- package/packages/dictionaries/package.json +12 -5
- package/packages/logger/package.json +7 -2
- package/packages/scrabble-solver/.next/BUILD_ID +1 -1
- package/packages/scrabble-solver/.next/build-manifest.json +10 -10
- package/packages/scrabble-solver/.next/cache/.previewinfo +1 -1
- package/packages/scrabble-solver/.next/cache/.rscinfo +1 -1
- package/packages/scrabble-solver/.next/cache/.tsbuildinfo +1 -1
- package/packages/scrabble-solver/.next/cache/webpack/client-production/0.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/server-production/0.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/server-production/index.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/server-production/index.pack.old +0 -0
- package/packages/scrabble-solver/.next/prerender-manifest.json +4 -4
- package/packages/scrabble-solver/.next/routes-manifest.json +1 -1
- package/packages/scrabble-solver/.next/server/chunks/712.js +1 -1
- package/packages/scrabble-solver/.next/server/middleware-build-manifest.js +1 -1
- package/packages/scrabble-solver/.next/server/pages/404.html +3 -2
- package/packages/scrabble-solver/.next/server/pages/500.html +3 -2
- package/packages/scrabble-solver/.next/server/pages/_document.js +1 -1
- package/packages/scrabble-solver/.next/server/pages/_error.js +2 -2
- package/packages/scrabble-solver/.next/server/pages/index.html +3 -2
- package/packages/scrabble-solver/.next/server/pages/index.js +3 -3
- package/packages/scrabble-solver/.next/server/pages/index.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/not-found.html +3 -2
- package/packages/scrabble-solver/.next/server/pages-manifest.json +1 -1
- package/packages/scrabble-solver/.next/static/{FL82obwzFHRzvV6z76I-U → 5KOWzYqfY1Z2rjrzptr8D}/_buildManifest.js +1 -1
- package/packages/scrabble-solver/.next/static/chunks/pages/_app-2e607dcd4135f340.js +1 -0
- package/packages/scrabble-solver/.next/static/chunks/pages/index-e0087247c995328d.js +1 -0
- package/packages/scrabble-solver/.next/static/chunks/pages/not-found-a59f9deeeff63d17.js +1 -0
- package/packages/scrabble-solver/.next/static/chunks/{webpack-e34c3ae12275e978.js → webpack-cfb34d175e081981.js} +1 -1
- package/packages/scrabble-solver/.next/static/css/{abc2425d3ba607bc.css → 4dbcacc1122ac6b9.css} +1 -0
- package/packages/scrabble-solver/.next/trace +21 -21
- package/packages/scrabble-solver/.next/trace-build +1 -1
- package/packages/scrabble-solver/package.json +17 -10
- package/packages/scrabble-solver/public/robots.txt +1 -0
- package/packages/scrabble-solver/public/service-worker.js +1 -1
- package/packages/scrabble-solver/public/site.webmanifest +14 -0
- package/packages/scrabble-solver/src/lib/waitForIdleOrFirstIntent.ts +19 -8
- package/packages/scrabble-solver/src/pages/_app.tsx +40 -76
- package/packages/scrabble-solver/src/pages/_document.tsx +21 -19
- package/packages/scrabble-solver/src/pages/index.tsx +30 -3
- package/packages/scrabble-solver/src/pages/not-found.tsx +13 -2
- package/packages/scrabble-solver/src/parameters/index.ts +7 -0
- package/packages/scrabble-solver/src/state/sagas.ts +4 -4
- package/packages/scrabble-solver/tsconfig.tsbuildinfo +1 -1
- package/packages/solver/package.json +14 -6
- package/packages/types/package.json +10 -3
- package/packages/word-definitions/package.json +9 -3
- package/packages/word-lists/package.json +11 -3
- package/playwright-report/index.html +1 -1
- package/packages/scrabble-solver/.next/static/chunks/pages/_app-ba5d628e81a15461.js +0 -1
- package/packages/scrabble-solver/.next/static/chunks/pages/index-5f9fa6ac3cd3d97b.js +0 -1
- package/packages/scrabble-solver/.next/static/chunks/pages/not-found-23d61fd3ed791cad.js +0 -1
- package/packages/scrabble-solver/src/components/SeoMessage/SeoMessage.tsx +0 -18
- package/packages/scrabble-solver/src/components/SeoMessage/index.ts +0 -1
- /package/packages/scrabble-solver/.next/static/{FL82obwzFHRzvV6z76I-U → 5KOWzYqfY1Z2rjrzptr8D}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
[{"name":"run-typescript","duration":
|
|
1
|
+
[{"name":"run-typescript","duration":167919,"timestamp":15741743485,"id":13,"parentId":1,"tags":{},"startTime":1786879882182,"traceId":"09ff88835bed4614"},{"name":"run-webpack","duration":6553601,"timestamp":15741913668,"id":15,"parentId":1,"tags":{},"startTime":1786879882352,"traceId":"09ff88835bed4614"},{"name":"static-check","duration":184131,"timestamp":15748541724,"id":2019,"parentId":1,"tags":{},"startTime":1786879888980,"traceId":"09ff88835bed4614"},{"name":"static-generation","duration":249410,"timestamp":15748736408,"id":2037,"parentId":1,"tags":{},"startTime":1786879889175,"traceId":"09ff88835bed4614"},{"name":"collect-build-traces","duration":4410264,"timestamp":15748726538,"id":2034,"parentId":1,"tags":{},"startTime":1786879889165,"traceId":"09ff88835bed4614"},{"name":"telemetry-flush","duration":21,"timestamp":15753138117,"id":2044,"parentId":1,"tags":{},"startTime":1786879893576,"traceId":"09ff88835bed4614"},{"name":"next-build","duration":11637473,"timestamp":15741500668,"id":1,"tags":{"buildMode":"default","version":"16.3.1","bundler":"webpack","has-custom-webpack-config":"true","use-build-worker":"false"},"startTime":1786879881939,"traceId":"09ff88835bed4614"}]
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scrabble-solver/scrabble-solver",
|
|
3
|
-
"version": "2.18.
|
|
4
|
-
"description": "Scrabble Solver
|
|
3
|
+
"version": "2.18.1",
|
|
4
|
+
"description": "Scrabble Solver web app: free word finder and board solver for Scrabble, Super Scrabble, Literaki, and more.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"scrabble",
|
|
7
|
+
"word-finder",
|
|
8
|
+
"solver",
|
|
9
|
+
"nextjs",
|
|
10
|
+
"react"
|
|
11
|
+
],
|
|
5
12
|
"repository": {
|
|
6
13
|
"type": "git",
|
|
7
14
|
"url": "git+https://github.com/kamilmielnik/scrabble-solver.git"
|
|
@@ -11,7 +18,7 @@
|
|
|
11
18
|
"email": "kamil@kamilmielnik.com",
|
|
12
19
|
"url": "https://kamilmielnik.com"
|
|
13
20
|
},
|
|
14
|
-
"license": "CC
|
|
21
|
+
"license": "CC-BY-NC-ND-4.0",
|
|
15
22
|
"bugs": {
|
|
16
23
|
"url": "https://github.com/kamilmielnik/scrabble-solver/issues"
|
|
17
24
|
},
|
|
@@ -48,13 +55,13 @@
|
|
|
48
55
|
"@floating-ui/react": "^0.27.20",
|
|
49
56
|
"@kamilmielnik/gaddag": "^2.0.0",
|
|
50
57
|
"@reduxjs/toolkit": "^2.12.0",
|
|
51
|
-
"@scrabble-solver/configs": "^2.18.
|
|
52
|
-
"@scrabble-solver/constants": "^2.18.
|
|
53
|
-
"@scrabble-solver/dictionaries": "^2.18.
|
|
54
|
-
"@scrabble-solver/logger": "^2.18.
|
|
55
|
-
"@scrabble-solver/solver": "^2.18.
|
|
56
|
-
"@scrabble-solver/types": "^2.18.
|
|
57
|
-
"@scrabble-solver/word-definitions": "^2.18.
|
|
58
|
+
"@scrabble-solver/configs": "^2.18.1",
|
|
59
|
+
"@scrabble-solver/constants": "^2.18.1",
|
|
60
|
+
"@scrabble-solver/dictionaries": "^2.18.1",
|
|
61
|
+
"@scrabble-solver/logger": "^2.18.1",
|
|
62
|
+
"@scrabble-solver/solver": "^2.18.1",
|
|
63
|
+
"@scrabble-solver/types": "^2.18.1",
|
|
64
|
+
"@scrabble-solver/word-definitions": "^2.18.1",
|
|
58
65
|
"classnames": "^2.5.1",
|
|
59
66
|
"env-cmd": "^11.0.0",
|
|
60
67
|
"include-media": "^2.0.0",
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
(()=>{"use strict";let e,t,s;var a,r={303:()=>{try{self["workbox:strategies:7.4.0"]&&_()}catch(e){}},448:()=>{try{self["workbox:routing:7.4.0"]&&_()}catch(e){}},693:()=>{try{self["workbox:core:7.4.0"]&&_()}catch(e){}},802:()=>{try{self["workbox:precaching:7.4.0"]&&_()}catch(e){}}},i={};function n(e){var t=i[e];if(void 0!==t)return t.exports;var s=i[e]={exports:{}},a=!0;try{r[e](s,s.exports,n),a=!1}finally{a&&delete i[e]}return s.exports}n(693);class l extends Error{constructor(e,t){super(((e,...t)=>{let s=e;return t.length>0&&(s+=` :: ${JSON.stringify(t)}`),s})(e,t)),this.name=e,this.details=t}}let c={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"u">typeof registration?registration.scope:""},h=e=>[c.prefix,e,c.suffix].filter(e=>e&&e.length>0).join("-"),o=e=>e||h(c.precache);function u(e,t){let s=t();return e.waitUntil(s),s}n(802);class f{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:s})=>{if("install"===e.type&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;s?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return s}}}class d{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let s=(null==t?void 0:t.cacheKey)||this._precacheController.getCacheKeyForURL(e.url);return s?new Request(s,{headers:e.headers}):e},this._precacheController=e}}async function p(t,s){let a=null;if(t.url&&(a=new URL(t.url).origin),a!==self.location.origin)throw new l("cross-origin-copy-response",{origin:a});let r=t.clone(),i={headers:new Headers(r.headers),status:r.status,statusText:r.statusText},n=s?s(i):i,c=!function(){if(void 0===e){let t=new Response("");if("body"in t)try{new Response(t.body),e=!0}catch(t){e=!1}e=!1}return e}()?await r.blob():r.body;return new Response(c,n)}function w(e,t){let s=new URL(e);for(let e of t)s.searchParams.delete(e);return s.href}async function y(e,t,s,a){let r=w(t.url,s);if(t.url===r)return e.match(t,a);let i=Object.assign(Object.assign({},a),{ignoreSearch:!0});for(let n of(await e.keys(t,i)))if(r===w(n.url,s))return e.match(n,a)}class g{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}}let m=new Set;async function R(){for(let e of m)await e()}function C(e){return"string"==typeof e?new Request(e):e}n(303);class b{constructor(e,t){for(const s of(this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new g,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map,this._plugins))this._pluginStateMap.set(s,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:t}=this,s=C(e);if("navigate"===s.mode&&t instanceof FetchEvent&&t.preloadResponse){let e=await t.preloadResponse;if(e)return e}let a=this.hasCallback("fetchDidFail")?s.clone():null;try{for(let e of this.iterateCallbacks("requestWillFetch"))s=await e({request:s.clone(),event:t})}catch(e){if(e instanceof Error)throw new l("plugin-error-request-will-fetch",{thrownErrorMessage:e.message})}let r=s.clone();try{let e;for(let a of(e=await fetch(s,"navigate"===s.mode?void 0:this._strategy.fetchOptions),this.iterateCallbacks("fetchDidSucceed")))e=await a({event:t,request:r,response:e});return e}catch(e){throw a&&await this.runCallbacks("fetchDidFail",{error:e,event:t,originalRequest:a.clone(),request:r.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),s=t.clone();return this.waitUntil(this.cachePut(e,s)),t}async cacheMatch(e){let t,s=C(e),{cacheName:a,matchOptions:r}=this._strategy,i=await this.getCacheKey(s,"read"),n=Object.assign(Object.assign({},r),{cacheName:a});for(let e of(t=await caches.match(i,n),this.iterateCallbacks("cachedResponseWillBeUsed")))t=await e({cacheName:a,matchOptions:r,cachedResponse:t,request:i,event:this.event})||void 0;return t}async cachePut(e,t){let s=C(e);await new Promise(e=>setTimeout(e,0));let a=await this.getCacheKey(s,"write");if(!t)throw new l("cache-put-with-no-response",{url:new URL(String(a.url),location.href).href.replace(RegExp(`^${location.origin}`),"")});let r=await this._ensureResponseSafeToCache(t);if(!r)return!1;let{cacheName:i,matchOptions:n}=this._strategy,c=await self.caches.open(i),h=this.hasCallback("cacheDidUpdate"),o=h?await y(c,a.clone(),["__WB_REVISION__"],n):null;try{await c.put(a,h?r.clone():r)}catch(e){if(e instanceof Error)throw"QuotaExceededError"===e.name&&await R(),e}for(let e of this.iterateCallbacks("cacheDidUpdate"))await e({cacheName:i,oldResponse:o,newResponse:r.clone(),request:a,event:this.event});return!0}async getCacheKey(e,t){let s=`${e.url} | ${t}`;if(!this._cacheKeys[s]){let a=e;for(let e of this.iterateCallbacks("cacheKeyWillBeUsed"))a=C(await e({mode:t,request:a,event:this.event,params:this.params}));this._cacheKeys[s]=a}return this._cacheKeys[s]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let s of this.iterateCallbacks(e))await s(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if("function"==typeof t[e]){let s=this._pluginStateMap.get(t),a=a=>{let r=Object.assign(Object.assign({},a),{state:s});return t[e](r)};yield a}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>"rejected"===e.status);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,s=!1;for(let e of this.iterateCallbacks("cacheWillUpdate"))if(t=await e({request:this.request,response:t,event:this.event})||void 0,s=!0,!t)break;return!s&&t&&200!==t.status&&(t=void 0),t}}class v{constructor(e={}){this.cacheName=(e=>e||h(c.runtime))(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,s="string"==typeof e.request?new Request(e.request):e.request,a=new b(this,{event:t,request:s,params:"params"in e?e.params:void 0}),r=this._getResponse(a,s,t),i=this._awaitComplete(r,a,s,t);return[r,i]}async _getResponse(e,t,s){let a;await e.runCallbacks("handlerWillStart",{event:s,request:t});try{if(!(a=await this._handle(t,e))||"error"===a.type)throw new l("no-response",{url:t.url})}catch(r){if(r instanceof Error){for(let i of e.iterateCallbacks("handlerDidError"))if(a=await i({error:r,event:s,request:t}))break}if(a);else throw r}for(let r of e.iterateCallbacks("handlerWillRespond"))a=await r({event:s,request:t,response:a});return a}async _awaitComplete(e,t,s,a){let r,i;try{r=await e}catch(e){}try{await t.runCallbacks("handlerDidRespond",{event:a,request:s,response:r}),await t.doneWaiting()}catch(e){e instanceof Error&&(i=e)}if(await t.runCallbacks("handlerDidComplete",{event:a,request:s,response:r,error:i}),t.destroy(),i)throw i}}class L extends v{constructor(e={}){e.cacheName=o(e.cacheName),super(e),this._fallbackToNetwork=!1!==e.fallbackToNetwork,this.plugins.push(L.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){let s=await t.cacheMatch(e);return s||(t.event&&"install"===t.event.type?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,t){let s,a=t.params||{};if(this._fallbackToNetwork){let r=a.integrity,i=e.integrity,n=!i||i===r;s=await t.fetch(new Request(e,{integrity:"no-cors"!==e.mode?i||r:void 0})),r&&n&&"no-cors"!==e.mode&&(this._useDefaultCacheabilityPluginIfNeeded(),await t.cachePut(e,s.clone()))}else throw new l("missing-precache-entry",{cacheName:this.cacheName,url:e.url});return s}async _handleInstall(e,t){this._useDefaultCacheabilityPluginIfNeeded();let s=await t.fetch(e);if(!await t.cachePut(e,s.clone()))throw new l("bad-precaching-response",{url:e.url,status:s.status});return s}_useDefaultCacheabilityPluginIfNeeded(){let e=null,t=0;for(let[s,a]of this.plugins.entries())a!==L.copyRedirectedCacheableResponsesPlugin&&(a===L.defaultPrecacheCacheabilityPlugin&&(e=s),a.cacheWillUpdate&&t++);0===t?this.plugins.push(L.defaultPrecacheCacheabilityPlugin):t>1&&null!==e&&this.plugins.splice(e,1)}}L.defaultPrecacheCacheabilityPlugin={cacheWillUpdate:async({response:e})=>!e||e.status>=400?null:e},L.copyRedirectedCacheableResponsesPlugin={cacheWillUpdate:async({response:e})=>e.redirected?await p(e):e};class U{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:s=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new L({cacheName:o(e),plugins:[...t,new d({precacheController:this})],fallbackToNetwork:s}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||(self.addEventListener("install",this.install),self.addEventListener("activate",this.activate),this._installAndActiveListenersAdded=!0)}addToCacheList(e){let t=[];for(let s of e){"string"==typeof s?t.push(s):s&&void 0===s.revision&&t.push(s.url);let{cacheKey:e,url:a}=function(e){if(!e)throw new l("add-to-cache-list-unexpected-type",{entry:e});if("string"==typeof e){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:t,url:s}=e;if(!s)throw new l("add-to-cache-list-unexpected-type",{entry:e});if(!t){let e=new URL(s,location.href);return{cacheKey:e.href,url:e.href}}let a=new URL(s,location.href),r=new URL(s,location.href);return a.searchParams.set("__WB_REVISION__",t),{cacheKey:a.href,url:r.href}}(s),r="string"!=typeof s&&s.revision?"reload":"default";if(this._urlsToCacheKeys.has(a)&&this._urlsToCacheKeys.get(a)!==e)throw new l("add-to-cache-list-conflicting-entries",{firstEntry:this._urlsToCacheKeys.get(a),secondEntry:e});if("string"!=typeof s&&s.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==s.integrity)throw new l("add-to-cache-list-conflicting-integrities",{url:a});this._cacheKeysToIntegrities.set(e,s.integrity)}this._urlsToCacheKeys.set(a,e),this._urlsToCacheModes.set(a,r),t.length>0&&console.warn(`Workbox is precaching URLs without revision info: ${t.join(", ")}
|
|
2
|
-
This is generally NOT safe. Learn more at https://bit.ly/wb-precache`)}}install(e){return u(e,async()=>{let t=new f;for(let[s,a]of(this.strategy.plugins.push(t),this._urlsToCacheKeys)){let t=this._cacheKeysToIntegrities.get(a),r=this._urlsToCacheModes.get(s),i=new Request(s,{integrity:t,cache:r,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:a},request:i,event:e}))}let{updatedURLs:s,notUpdatedURLs:a}=t;return{updatedURLs:s,notUpdatedURLs:a}})}activate(e){return u(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),s=new Set(this._urlsToCacheKeys.values()),a=[];for(let r of t)s.has(r.url)||(await e.delete(r),a.push(r.url));return{deletedURLs:a}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,s=this.getCacheKeyForURL(t);if(s)return(await self.caches.open(this.strategy.cacheName)).match(s)}createHandlerBoundToURL(e){let t=this.getCacheKeyForURL(e);if(!t)throw new l("non-precached-url",{url:e});return s=>(s.request=new Request(e),s.params=Object.assign({cacheKey:t},s.params),this.strategy.handle(s))}}let k=()=>(t||(t=new U),t);n(448);let T=e=>e&&"object"==typeof e?e:{handle:e};class K{constructor(e,t,s="GET"){this.handler=T(t),this.match=e,this.method=s}setCatchHandler(e){this.catchHandler=T(e)}}class x extends K{constructor(e,t,s){super(({url:t})=>{let s=e.exec(t.href);if(s)return t.origin!==location.origin&&0!==s.index?void 0:s.slice(1)},t,s)}}class P{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener("fetch",e=>{let{request:t}=e,s=this.handleRequest({request:t,event:e});s&&e.respondWith(s)})}addCacheListener(){self.addEventListener("message",e=>{if(e.data&&"CACHE_URLS"===e.data.type){let{payload:t}=e.data,s=Promise.all(t.urlsToCache.map(t=>{"string"==typeof t&&(t=[t]);let s=new Request(...t);return this.handleRequest({request:s,event:e})}));e.waitUntil(s),e.ports&&e.ports[0]&&s.then(()=>e.ports[0].postMessage(!0))}})}handleRequest({request:e,event:t}){let s,a=new URL(e.url,location.href);if(!a.protocol.startsWith("http"))return;let r=a.origin===location.origin,{params:i,route:n}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:a}),l=n&&n.handler,c=e.method;if(!l&&this._defaultHandlerMap.has(c)&&(l=this._defaultHandlerMap.get(c)),!l)return;try{s=l.handle({url:a,request:e,event:t,params:i})}catch(e){s=Promise.reject(e)}let h=n&&n.catchHandler;return s instanceof Promise&&(this._catchHandler||h)&&(s=s.catch(async s=>{if(h)try{return await h.handle({url:a,request:e,event:t,params:i})}catch(e){e instanceof Error&&(s=e)}if(this._catchHandler)return this._catchHandler.handle({url:a,request:e,event:t});throw s})),s}findMatchingRoute({url:e,sameOrigin:t,request:s,event:a}){for(let r of this._routes.get(s.method)||[]){let i,n=r.match({url:e,sameOrigin:t,request:s,event:a});if(n)return Array.isArray(i=n)&&0===i.length||n.constructor===Object&&0===Object.keys(n).length?i=void 0:"boolean"==typeof n&&(i=void 0),{route:r,params:i}}return{}}setDefaultHandler(e,t="GET"){this._defaultHandlerMap.set(t,T(e))}setCatchHandler(e){this._catchHandler=T(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new l("unregister-route-but-not-found-with-method",{method:e.method});let t=this._routes.get(e.method).indexOf(e);if(t>-1)this._routes.get(e.method).splice(t,1);else throw new l("unregister-route-route-not-registered")}}function q(e,t,a){let r;if("string"==typeof e){let s=new URL(e,location.href);r=new K(({url:e})=>e.href===s.href,t,a)}else if(e instanceof RegExp)r=new x(e,t,a);else if("function"==typeof e)r=new K(e,t,a);else if(e instanceof K)r=e;else throw new l("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});return(!s&&((s=new P).addFetchListener(),s.addCacheListener()),s).registerRoute(r),r}class E extends K{constructor(e,t){super(({request:s})=>{let a=e.getURLsToCacheKeys();for(let r of function*(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:s="index.html",cleanURLs:a=!0,urlManipulation:r}={}){let i=new URL(e,location.href);i.hash="",yield i.href;let n=function(e,t=[]){for(let s of[...e.searchParams.keys()])t.some(e=>e.test(s))&&e.searchParams.delete(s);return e}(i,t);if(yield n.href,s&&n.pathname.endsWith("/")){let e=new URL(n.href);e.pathname+=s,yield e.href}if(a){let e=new URL(n.href);e.pathname+=".html",yield e.href}if(r)for(let e of r({url:i}))yield e.href}(s.url,t)){let t=a.get(r);if(t){let s=e.getIntegrityForCacheKey(t);return{cacheKey:t,integrity:s}}}},e.strategy)}}let M="-precache-",N=async(e,t=M)=>{let s=(await self.caches.keys()).filter(s=>s.includes(t)&&s.includes(self.registration.scope)&&s!==e);return await Promise.all(s.map(e=>self.caches.delete(e))),s};class W extends K{constructor(e,{allowlist:t=[/./],denylist:s=[]}={}){super(e=>this._match(e),e),this._allowlist=t,this._denylist=s}_match({url:e,request:t}){if(t&&"navigate"!==t.mode)return!1;let s=e.pathname+e.search;for(let e of this._denylist)if(e.test(s))return!1;return!!this._allowlist.some(e=>e.test(s))}}let O="app-shell";async function I(){let e=await caches.open(O);await e.add("/")}async function S(e){let{request:t}=e;try{return await fetch(t)}catch(s){let e=await caches.open(O),t=await e.match("/");if(!t)throw s;return t}}self.addEventListener("install",e=>{e.waitUntil(I()),self.skipWaiting()}),self.addEventListener("activate",()=>{self.clients.claim()}),self.addEventListener("activate",e=>{let t=o();e.waitUntil(N(t).then(e=>{}))}),a=[{'revision':'
|
|
2
|
+
This is generally NOT safe. Learn more at https://bit.ly/wb-precache`)}}install(e){return u(e,async()=>{let t=new f;for(let[s,a]of(this.strategy.plugins.push(t),this._urlsToCacheKeys)){let t=this._cacheKeysToIntegrities.get(a),r=this._urlsToCacheModes.get(s),i=new Request(s,{integrity:t,cache:r,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:a},request:i,event:e}))}let{updatedURLs:s,notUpdatedURLs:a}=t;return{updatedURLs:s,notUpdatedURLs:a}})}activate(e){return u(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),s=new Set(this._urlsToCacheKeys.values()),a=[];for(let r of t)s.has(r.url)||(await e.delete(r),a.push(r.url));return{deletedURLs:a}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,s=this.getCacheKeyForURL(t);if(s)return(await self.caches.open(this.strategy.cacheName)).match(s)}createHandlerBoundToURL(e){let t=this.getCacheKeyForURL(e);if(!t)throw new l("non-precached-url",{url:e});return s=>(s.request=new Request(e),s.params=Object.assign({cacheKey:t},s.params),this.strategy.handle(s))}}let k=()=>(t||(t=new U),t);n(448);let T=e=>e&&"object"==typeof e?e:{handle:e};class K{constructor(e,t,s="GET"){this.handler=T(t),this.match=e,this.method=s}setCatchHandler(e){this.catchHandler=T(e)}}class x extends K{constructor(e,t,s){super(({url:t})=>{let s=e.exec(t.href);if(s)return t.origin!==location.origin&&0!==s.index?void 0:s.slice(1)},t,s)}}class P{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener("fetch",e=>{let{request:t}=e,s=this.handleRequest({request:t,event:e});s&&e.respondWith(s)})}addCacheListener(){self.addEventListener("message",e=>{if(e.data&&"CACHE_URLS"===e.data.type){let{payload:t}=e.data,s=Promise.all(t.urlsToCache.map(t=>{"string"==typeof t&&(t=[t]);let s=new Request(...t);return this.handleRequest({request:s,event:e})}));e.waitUntil(s),e.ports&&e.ports[0]&&s.then(()=>e.ports[0].postMessage(!0))}})}handleRequest({request:e,event:t}){let s,a=new URL(e.url,location.href);if(!a.protocol.startsWith("http"))return;let r=a.origin===location.origin,{params:i,route:n}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:a}),l=n&&n.handler,c=e.method;if(!l&&this._defaultHandlerMap.has(c)&&(l=this._defaultHandlerMap.get(c)),!l)return;try{s=l.handle({url:a,request:e,event:t,params:i})}catch(e){s=Promise.reject(e)}let h=n&&n.catchHandler;return s instanceof Promise&&(this._catchHandler||h)&&(s=s.catch(async s=>{if(h)try{return await h.handle({url:a,request:e,event:t,params:i})}catch(e){e instanceof Error&&(s=e)}if(this._catchHandler)return this._catchHandler.handle({url:a,request:e,event:t});throw s})),s}findMatchingRoute({url:e,sameOrigin:t,request:s,event:a}){for(let r of this._routes.get(s.method)||[]){let i,n=r.match({url:e,sameOrigin:t,request:s,event:a});if(n)return Array.isArray(i=n)&&0===i.length||n.constructor===Object&&0===Object.keys(n).length?i=void 0:"boolean"==typeof n&&(i=void 0),{route:r,params:i}}return{}}setDefaultHandler(e,t="GET"){this._defaultHandlerMap.set(t,T(e))}setCatchHandler(e){this._catchHandler=T(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new l("unregister-route-but-not-found-with-method",{method:e.method});let t=this._routes.get(e.method).indexOf(e);if(t>-1)this._routes.get(e.method).splice(t,1);else throw new l("unregister-route-route-not-registered")}}function q(e,t,a){let r;if("string"==typeof e){let s=new URL(e,location.href);r=new K(({url:e})=>e.href===s.href,t,a)}else if(e instanceof RegExp)r=new x(e,t,a);else if("function"==typeof e)r=new K(e,t,a);else if(e instanceof K)r=e;else throw new l("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});return(!s&&((s=new P).addFetchListener(),s.addCacheListener()),s).registerRoute(r),r}class E extends K{constructor(e,t){super(({request:s})=>{let a=e.getURLsToCacheKeys();for(let r of function*(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:s="index.html",cleanURLs:a=!0,urlManipulation:r}={}){let i=new URL(e,location.href);i.hash="",yield i.href;let n=function(e,t=[]){for(let s of[...e.searchParams.keys()])t.some(e=>e.test(s))&&e.searchParams.delete(s);return e}(i,t);if(yield n.href,s&&n.pathname.endsWith("/")){let e=new URL(n.href);e.pathname+=s,yield e.href}if(a){let e=new URL(n.href);e.pathname+=".html",yield e.href}if(r)for(let e of r({url:i}))yield e.href}(s.url,t)){let t=a.get(r);if(t){let s=e.getIntegrityForCacheKey(t);return{cacheKey:t,integrity:s}}}},e.strategy)}}let M="-precache-",N=async(e,t=M)=>{let s=(await self.caches.keys()).filter(s=>s.includes(t)&&s.includes(self.registration.scope)&&s!==e);return await Promise.all(s.map(e=>self.caches.delete(e))),s};class W extends K{constructor(e,{allowlist:t=[/./],denylist:s=[]}={}){super(e=>this._match(e),e),this._allowlist=t,this._denylist=s}_match({url:e,request:t}){if(t&&"navigate"!==t.mode)return!1;let s=e.pathname+e.search;for(let e of this._denylist)if(e.test(s))return!1;return!!this._allowlist.some(e=>e.test(s))}}let O="app-shell";async function I(){let e=await caches.open(O);await e.add("/")}async function S(e){let{request:t}=e;try{return await fetch(t)}catch(s){let e=await caches.open(O),t=await e.match("/");if(!t)throw s;return t}}self.addEventListener("install",e=>{e.waitUntil(I()),self.skipWaiting()}),self.addEventListener("activate",()=>{self.clients.claim()}),self.addEventListener("activate",e=>{let t=o();e.waitUntil(N(t).then(e=>{}))}),a=[{'revision':'8486f920bd3e2ac450039aafcb1d8f76','url':'/_next/build-manifest.json'},{'revision':'82cbcbcce8a0a8cb21c5e4efa1de5e4c','url':'/_next/dynamic-css-manifest.json'},{'revision':'1da1766c9299366bb973f535ec69ff90','url':'/_next/react-loadable-manifest.json'},{'revision':'271476b8fdb65f6d540f92faa6bca0be','url':'/_next/server/dynamic-css-manifest.js'},{'revision':'260f621e04297bcecd94427e45d7405b','url':'/_next/server/middleware-build-manifest.js'},{'revision':'72c6f757460ce938d861f3c2673b3bab','url':'/_next/server/middleware-react-loadable-manifest.js'},{'revision':'9fce7989bff5d35b01e177447faca50d','url':'/_next/server/next-font-manifest.js'},{'revision':'d51420cd4aa5d37d6719849cf36d0d6f','url':'/_next/server/next-font-manifest.json'},{'revision':'d09f2baf8f9ee346bc1af7cbc794463f','url':'/_next/static/5KOWzYqfY1Z2rjrzptr8D/_buildManifest.js'},{'revision':'b6652df95db52feb4daf4eca35380933','url':'/_next/static/5KOWzYqfY1Z2rjrzptr8D/_ssgManifest.js'},{'revision':null,'url':'/_next/static/chunks/101-829ab8a8d310e7a0.js'},{'revision':null,'url':'/_next/static/chunks/106.e414e88694add27f.js'},{'revision':null,'url':'/_next/static/chunks/127.125d896a19061d71.js'},{'revision':null,'url':'/_next/static/chunks/18.8b91e137d239e15c.js'},{'revision':null,'url':'/_next/static/chunks/277.6785dfaf22fdb0b7.js'},{'revision':null,'url':'/_next/static/chunks/307.67f52f13e3d60d4e.js'},{'revision':null,'url':'/_next/static/chunks/341.79535ba85bf1986f.js'},{'revision':null,'url':'/_next/static/chunks/363.01eed3233f92b6a4.js'},{'revision':null,'url':'/_next/static/chunks/422.1559ae6756912ca5.js'},{'revision':null,'url':'/_next/static/chunks/541.dcd9e8b804f88da5.js'},{'revision':null,'url':'/_next/static/chunks/583.7a46807be14c377e.js'},{'revision':null,'url':'/_next/static/chunks/631.98d42abad0a60af6.js'},{'revision':null,'url':'/_next/static/chunks/663.d2c9ca36b26695b8.js'},{'revision':null,'url':'/_next/static/chunks/711.ba7b8f900dfd40d0.js'},{'revision':null,'url':'/_next/static/chunks/744.b06c9a3de4a1d877.js'},{'revision':null,'url':'/_next/static/chunks/820.97b82f751ca49579.js'},{'revision':null,'url':'/_next/static/chunks/865.a9b563a45848a83c.js'},{'revision':null,'url':'/_next/static/chunks/912.ff8037ba9d4db5c8.js'},{'revision':null,'url':'/_next/static/chunks/939.19b33a3d3c351d73.js'},{'revision':null,'url':'/_next/static/chunks/990.61296e5870b91f11.js'},{'revision':null,'url':'/_next/static/chunks/framework-626cbd48ac5aca57.js'},{'revision':null,'url':'/_next/static/chunks/main-b92e08f3fc96dd8d.js'},{'revision':null,'url':'/_next/static/chunks/pages/_app-2e607dcd4135f340.js'},{'revision':null,'url':'/_next/static/chunks/pages/_error-f0290a98eb3ed0ec.js'},{'revision':null,'url':'/_next/static/chunks/pages/index-e0087247c995328d.js'},{'revision':null,'url':'/_next/static/chunks/pages/not-found-a59f9deeeff63d17.js'},{'revision':'846118c33b2c0e922d7b3a7676f81f6f','url':'/_next/static/chunks/polyfills-42372ed130431b0a.js'},{'revision':null,'url':'/_next/static/chunks/webpack-cfb34d175e081981.js'}],k().precache(a),q(new E(k(),void 0)),q(new W(S,{denylist:[/^\/api\//]}))})();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Scrabble Solver",
|
|
3
|
+
"short_name": "Scrabble Solver",
|
|
4
|
+
"description": "Free Scrabble word finder and board solver.",
|
|
5
|
+
"start_url": "/",
|
|
6
|
+
"display": "standalone",
|
|
7
|
+
"background_color": "#f4f4f4",
|
|
8
|
+
"theme_color": "#efe3ae",
|
|
9
|
+
"icons": [
|
|
10
|
+
{ "src": "/icons/favicon-196x196.png", "sizes": "196x196", "type": "image/png" },
|
|
11
|
+
{ "src": "/icons/favicon-128.png", "sizes": "128x128", "type": "image/png" },
|
|
12
|
+
{ "src": "/icons/favicon.svg", "sizes": "any", "type": "image/svg+xml" }
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -1,21 +1,32 @@
|
|
|
1
1
|
export function waitForIdleOrFirstIntent(): Promise<void> {
|
|
2
2
|
return new Promise((resolve) => {
|
|
3
|
-
const settle = ()
|
|
4
|
-
window.removeEventListener('pointerdown', settle);
|
|
5
|
-
window.removeEventListener('keydown', settle);
|
|
6
|
-
resolve();
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
window.addEventListener('pointerdown', settle, { once: true });
|
|
10
|
-
window.addEventListener('keydown', settle, { once: true });
|
|
3
|
+
const settle = onFirstIntent(resolve);
|
|
11
4
|
whenIdle(settle);
|
|
12
5
|
});
|
|
13
6
|
}
|
|
14
7
|
|
|
8
|
+
export function waitForFirstIntent(): Promise<void> {
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
onFirstIntent(resolve);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
15
14
|
export function waitForIdle(): Promise<void> {
|
|
16
15
|
return new Promise((resolve) => whenIdle(resolve));
|
|
17
16
|
}
|
|
18
17
|
|
|
18
|
+
function onFirstIntent(callback: () => void): () => void {
|
|
19
|
+
const settle = () => {
|
|
20
|
+
window.removeEventListener('pointerdown', settle);
|
|
21
|
+
window.removeEventListener('keydown', settle);
|
|
22
|
+
callback();
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
window.addEventListener('pointerdown', settle, { once: true });
|
|
26
|
+
window.addEventListener('keydown', settle, { once: true });
|
|
27
|
+
return settle;
|
|
28
|
+
}
|
|
29
|
+
|
|
19
30
|
function whenIdle(callback: () => void): void {
|
|
20
31
|
const requestIdle = () => {
|
|
21
32
|
if ('requestIdleCallback' in window) {
|
|
@@ -4,85 +4,49 @@ import Head from 'next/head';
|
|
|
4
4
|
import { type FunctionComponent } from 'react';
|
|
5
5
|
import { Provider } from 'react-redux';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { SITE_DESCRIPTION, SITE_URL } from '@/parameters';
|
|
8
8
|
import { store } from '@/state';
|
|
9
9
|
|
|
10
10
|
import '../styles/global.scss';
|
|
11
11
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
'SJP',
|
|
52
|
-
'FISE-2017',
|
|
53
|
-
'FISE-2',
|
|
54
|
-
'CNRTL',
|
|
55
|
-
'Kamil Mielnik',
|
|
56
|
-
].join(',');
|
|
57
|
-
|
|
58
|
-
const App: FunctionComponent<AppProps> = ({ Component, pageProps }) => (
|
|
59
|
-
<>
|
|
60
|
-
<Head>
|
|
61
|
-
<title>Scrabble Solver 2</title>
|
|
62
|
-
<meta charSet="utf-8" />
|
|
63
|
-
<meta name="author" content="Kamil Mielnik" />
|
|
64
|
-
<meta name="description" content={DESCRIPTION} />
|
|
65
|
-
<meta name="keywords" content={KEYWORDS} />
|
|
66
|
-
<meta name="robots" content="index, follow, notranslate, noimageindex" />
|
|
67
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
68
|
-
<meta property="og:title" content={DESCRIPTION} />
|
|
69
|
-
<meta property="og:type" content="website" />
|
|
70
|
-
<meta property="og:url" content="https://scrabble-solver.org" />
|
|
71
|
-
<meta property="og:image" content="https://scrabble-solver.org/og.png" />
|
|
72
|
-
<meta property="og:image:type" content="image/png" />
|
|
73
|
-
<meta property="og:image:width" content="1200" />
|
|
74
|
-
<meta property="og:image:height" content="630" />
|
|
75
|
-
<meta property="og:image:alt" content={DESCRIPTION} />
|
|
76
|
-
</Head>
|
|
77
|
-
|
|
78
|
-
<Provider store={store}>
|
|
79
|
-
<SeoMessage />
|
|
80
|
-
|
|
81
|
-
<FloatingDelayGroup delay={0}>
|
|
82
|
-
<Component {...pageProps} />
|
|
83
|
-
</FloatingDelayGroup>
|
|
84
|
-
</Provider>
|
|
85
|
-
</>
|
|
86
|
-
);
|
|
12
|
+
const TITLE = 'Scrabble Solver 2 - Board Solver & Word Finder';
|
|
13
|
+
|
|
14
|
+
const App: FunctionComponent<AppProps> = ({ Component, pageProps, router }) => {
|
|
15
|
+
const isIndexable = router.pathname === '/';
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<Head>
|
|
20
|
+
<title>{TITLE}</title>
|
|
21
|
+
<meta charSet="utf-8" />
|
|
22
|
+
<meta name="author" content="Kamil Mielnik" />
|
|
23
|
+
<meta name="description" content={SITE_DESCRIPTION} />
|
|
24
|
+
{isIndexable ? <link rel="canonical" href={`${SITE_URL}/`} /> : <meta name="robots" content="noindex" />}
|
|
25
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
26
|
+
<meta property="og:site_name" content="Scrabble Solver" />
|
|
27
|
+
<meta property="og:title" content={TITLE} />
|
|
28
|
+
<meta property="og:description" content={SITE_DESCRIPTION} />
|
|
29
|
+
<meta property="og:type" content="website" />
|
|
30
|
+
<meta property="og:url" content={`${SITE_URL}/`} />
|
|
31
|
+
<meta property="og:locale" content="en_US" />
|
|
32
|
+
<meta property="og:image" content={`${SITE_URL}/og.png`} />
|
|
33
|
+
<meta property="og:image:type" content="image/png" />
|
|
34
|
+
<meta property="og:image:width" content="1200" />
|
|
35
|
+
<meta property="og:image:height" content="630" />
|
|
36
|
+
<meta property="og:image:alt" content="Scrabble tiles spelling out Scrabble Solver 2" />
|
|
37
|
+
<meta name="twitter:card" content="summary_large_image" />
|
|
38
|
+
<meta name="twitter:title" content={TITLE} />
|
|
39
|
+
<meta name="twitter:description" content={SITE_DESCRIPTION} />
|
|
40
|
+
<meta name="twitter:image" content={`${SITE_URL}/og.png`} />
|
|
41
|
+
</Head>
|
|
42
|
+
|
|
43
|
+
<Provider store={store}>
|
|
44
|
+
<FloatingDelayGroup delay={0}>
|
|
45
|
+
<Component {...pageProps} />
|
|
46
|
+
</FloatingDelayGroup>
|
|
47
|
+
</Provider>
|
|
48
|
+
</>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
87
51
|
|
|
88
52
|
export default App;
|
|
@@ -70,27 +70,29 @@ export default class MyDocument extends Document {
|
|
|
70
70
|
<InlineCssHead>
|
|
71
71
|
{/* eslint-disable-next-line react/no-danger */}
|
|
72
72
|
<script dangerouslySetInnerHTML={{ __html: preHydrationScript }} />
|
|
73
|
-
<link rel="apple-touch-icon-precomposed" sizes="57x57" href="icons/apple-touch-icon-57x57.png" />
|
|
74
|
-
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="icons/apple-touch-icon-114x114.png" />
|
|
75
|
-
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="icons/apple-touch-icon-72x72.png" />
|
|
76
|
-
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="icons/apple-touch-icon-144x144.png" />
|
|
77
|
-
<link rel="apple-touch-icon-precomposed" sizes="60x60" href="icons/apple-touch-icon-60x60.png" />
|
|
78
|
-
<link rel="apple-touch-icon-precomposed" sizes="120x120" href="icons/apple-touch-icon-120x120.png" />
|
|
79
|
-
<link rel="apple-touch-icon-precomposed" sizes="76x76" href="icons/apple-touch-icon-76x76.png" />
|
|
80
|
-
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="icons/apple-touch-icon-152x152.png" />
|
|
81
|
-
<link rel="icon" type="image/png" href="icons/favicon-196x196.png" sizes="196x196" />
|
|
82
|
-
<link rel="icon" type="image/png" href="icons/favicon-96x96.png" sizes="96x96" />
|
|
83
|
-
<link rel="icon" type="image/png" href="icons/favicon-32x32.png" sizes="32x32" />
|
|
84
|
-
<link rel="icon" type="image/png" href="icons/favicon-16x16.png" sizes="16x16" />
|
|
85
|
-
<link rel="icon" type="image/png" href="icons/favicon-128.png" sizes="128x128" />
|
|
86
|
-
<link rel="icon" href="icons/favicon.svg" />
|
|
73
|
+
<link rel="apple-touch-icon-precomposed" sizes="57x57" href="/icons/apple-touch-icon-57x57.png" />
|
|
74
|
+
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="/icons/apple-touch-icon-114x114.png" />
|
|
75
|
+
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="/icons/apple-touch-icon-72x72.png" />
|
|
76
|
+
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="/icons/apple-touch-icon-144x144.png" />
|
|
77
|
+
<link rel="apple-touch-icon-precomposed" sizes="60x60" href="/icons/apple-touch-icon-60x60.png" />
|
|
78
|
+
<link rel="apple-touch-icon-precomposed" sizes="120x120" href="/icons/apple-touch-icon-120x120.png" />
|
|
79
|
+
<link rel="apple-touch-icon-precomposed" sizes="76x76" href="/icons/apple-touch-icon-76x76.png" />
|
|
80
|
+
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="/icons/apple-touch-icon-152x152.png" />
|
|
81
|
+
<link rel="icon" type="image/png" href="/icons/favicon-196x196.png" sizes="196x196" />
|
|
82
|
+
<link rel="icon" type="image/png" href="/icons/favicon-96x96.png" sizes="96x96" />
|
|
83
|
+
<link rel="icon" type="image/png" href="/icons/favicon-32x32.png" sizes="32x32" />
|
|
84
|
+
<link rel="icon" type="image/png" href="/icons/favicon-16x16.png" sizes="16x16" />
|
|
85
|
+
<link rel="icon" type="image/png" href="/icons/favicon-128.png" sizes="128x128" />
|
|
86
|
+
<link rel="icon" href="/icons/favicon.svg" />
|
|
87
|
+
<link rel="manifest" href="/site.webmanifest" />
|
|
87
88
|
<meta name="application-name" content="Scrabble Solver" />
|
|
89
|
+
<meta name="theme-color" content="#EFE3AE" />
|
|
88
90
|
<meta name="msapplication-TileColor" content="#EFE3AE" />
|
|
89
|
-
<meta name="msapplication-TileImage" content="icons/mstile-144x144.png" />
|
|
90
|
-
<meta name="msapplication-square70x70logo" content="icons/mstile-70x70.png" />
|
|
91
|
-
<meta name="msapplication-square150x150logo" content="icons/mstile-150x150.png" />
|
|
92
|
-
<meta name="msapplication-wide310x150logo" content="icons/mstile-310x150.png" />
|
|
93
|
-
<meta name="msapplication-square310x310logo" content="icons/mstile-310x310.png" />
|
|
91
|
+
<meta name="msapplication-TileImage" content="/icons/mstile-144x144.png" />
|
|
92
|
+
<meta name="msapplication-square70x70logo" content="/icons/mstile-70x70.png" />
|
|
93
|
+
<meta name="msapplication-square150x150logo" content="/icons/mstile-150x150.png" />
|
|
94
|
+
<meta name="msapplication-wide310x150logo" content="/icons/mstile-310x150.png" />
|
|
95
|
+
<meta name="msapplication-square310x310logo" content="/icons/mstile-310x310.png" />
|
|
94
96
|
</InlineCssHead>
|
|
95
97
|
|
|
96
98
|
<body>
|
|
@@ -18,7 +18,7 @@ import { useLanguage } from '@/hooks/useLanguage';
|
|
|
18
18
|
import { useLocalStorage } from '@/hooks/useLocalStorage';
|
|
19
19
|
import { LOCALE_FEATURES } from '@/i18n/constants';
|
|
20
20
|
import { schedulePreloadModals } from '@/modals/preload';
|
|
21
|
-
import { CONFIG_PENDING_CLASS } from '@/parameters';
|
|
21
|
+
import { CONFIG_PENDING_CLASS, GITHUB_PROJECT_URL, NPM_PACKAGE_URL, SITE_DESCRIPTION, SITE_URL } from '@/parameters';
|
|
22
22
|
import { registerServiceWorker } from '@/serviceWorkerManager';
|
|
23
23
|
import { initialize, reset, selectConfig, selectIsHydrated, selectLocale, useTypedSelector } from '@/state';
|
|
24
24
|
|
|
@@ -49,6 +49,30 @@ const WordsModal = dynamic(() => import('@/modals/WordsModal').then((module) =>
|
|
|
49
49
|
ssr: false,
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
+
const WEB_APPLICATION_JSON_LD = JSON.stringify({
|
|
53
|
+
'@context': 'https://schema.org',
|
|
54
|
+
'@type': 'WebApplication',
|
|
55
|
+
name: 'Scrabble Solver',
|
|
56
|
+
alternateName: 'Scrabble Solver 2',
|
|
57
|
+
url: `${SITE_URL}/`,
|
|
58
|
+
description: SITE_DESCRIPTION,
|
|
59
|
+
applicationCategory: 'GameApplication',
|
|
60
|
+
operatingSystem: 'Any',
|
|
61
|
+
browserRequirements: 'Requires JavaScript',
|
|
62
|
+
image: `${SITE_URL}/og.png`,
|
|
63
|
+
offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' },
|
|
64
|
+
inLanguage: ['en-US', 'en-GB', 'de-DE', 'es-ES', 'fa-IR', 'fr-FR', 'pl-PL', 'ro-RO', 'tr-TR'],
|
|
65
|
+
featureList: [
|
|
66
|
+
'Finds the highest-scoring words for a given board and rack',
|
|
67
|
+
'Supports Scrabble, Super Scrabble, Scrabble Duel, Letter League, Crossplay, Literaki, and Kelimelik',
|
|
68
|
+
'Dictionaries and word definitions in 8 languages',
|
|
69
|
+
'Works offline once a dictionary is cached',
|
|
70
|
+
'Free, open source, no ads, no sign-up',
|
|
71
|
+
],
|
|
72
|
+
sameAs: [GITHUB_PROJECT_URL, NPM_PACKAGE_URL],
|
|
73
|
+
author: { '@type': 'Person', name: 'Kamil Mielnik', url: 'https://kamilmielnik.com' },
|
|
74
|
+
});
|
|
75
|
+
|
|
52
76
|
interface Props {
|
|
53
77
|
version: string;
|
|
54
78
|
}
|
|
@@ -149,11 +173,11 @@ const Index: FunctionComponent<Props> = ({ version }) => {
|
|
|
149
173
|
<>
|
|
150
174
|
<nav className={styles.nav}>
|
|
151
175
|
<div className={styles.navContent}>
|
|
152
|
-
<
|
|
176
|
+
<h1 className={styles.navLogo}>
|
|
153
177
|
<a className={styles.logoContainer} href="/" title={version}>
|
|
154
178
|
<Logo className={styles.logo} />
|
|
155
179
|
</a>
|
|
156
|
-
</
|
|
180
|
+
</h1>
|
|
157
181
|
|
|
158
182
|
<NavButtons
|
|
159
183
|
onClear={handleClear}
|
|
@@ -170,6 +194,9 @@ const Index: FunctionComponent<Props> = ({ version }) => {
|
|
|
170
194
|
<Solver className={styles.solver} onShowResults={handleShowResults} />
|
|
171
195
|
</main>
|
|
172
196
|
|
|
197
|
+
{/* eslint-disable-next-line react/no-danger */}
|
|
198
|
+
<script dangerouslySetInnerHTML={{ __html: WEB_APPLICATION_JSON_LD }} type="application/ld+json" />
|
|
199
|
+
|
|
173
200
|
{mountedModals.menu && (
|
|
174
201
|
<MenuModal
|
|
175
202
|
isOpen={modals.menu}
|
|
@@ -1,5 +1,16 @@
|
|
|
1
|
-
'
|
|
1
|
+
import Head from 'next/head';
|
|
2
|
+
import { type FunctionComponent } from 'react';
|
|
2
3
|
|
|
3
4
|
import { NotFound } from '@/components/NotFound';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
const NotFoundPage: FunctionComponent = () => (
|
|
7
|
+
<>
|
|
8
|
+
<Head>
|
|
9
|
+
<title>Page not found - Scrabble Solver</title>
|
|
10
|
+
</Head>
|
|
11
|
+
|
|
12
|
+
<NotFound />
|
|
13
|
+
</>
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
export default NotFoundPage;
|
|
@@ -10,6 +10,13 @@ export const BREAKPOINTS = {
|
|
|
10
10
|
|
|
11
11
|
export const GITHUB_PROJECT_URL = 'https://github.com/kamilmielnik/scrabble-solver';
|
|
12
12
|
|
|
13
|
+
export const NPM_PACKAGE_URL = 'https://www.npmjs.com/package/scrabble-solver';
|
|
14
|
+
|
|
15
|
+
export const SITE_URL = 'https://scrabble-solver.org';
|
|
16
|
+
|
|
17
|
+
export const SITE_DESCRIPTION =
|
|
18
|
+
'Free, open-source board solver and word finder for Scrabble, Letter League, and Literaki. Enter your letters and board to get the highest-scoring words.';
|
|
19
|
+
|
|
13
20
|
// Must match the class name in global.scss
|
|
14
21
|
export const CONFIG_PENDING_CLASS = 'config-pending';
|
|
15
22
|
|
|
@@ -11,7 +11,7 @@ import { call, delay, put, select, spawn, takeEvery, takeLatest } from 'redux-sa
|
|
|
11
11
|
import { LOCALE_FEATURES } from '@/i18n/constants';
|
|
12
12
|
import { loadTranslations } from '@/i18n/i18n';
|
|
13
13
|
import { memoize } from '@/lib/memoize';
|
|
14
|
-
import { waitForIdleOrFirstIntent } from '@/lib/waitForIdleOrFirstIntent';
|
|
14
|
+
import { waitForFirstIntent, waitForIdleOrFirstIntent } from '@/lib/waitForIdleOrFirstIntent';
|
|
15
15
|
import { findWordDefinitions, solve, verify, visit } from '@/sdk';
|
|
16
16
|
import { prefetchDictionary } from '@/solver-worker';
|
|
17
17
|
|
|
@@ -139,7 +139,7 @@ function* onInitialize({ payload }: PayloadAction<{ version: string }>): AnyGene
|
|
|
139
139
|
const board = yield select(selectBoard);
|
|
140
140
|
const locale = yield select(selectLocale);
|
|
141
141
|
|
|
142
|
-
yield spawn(
|
|
142
|
+
yield spawn(prefetchDictionaryOnFirstIntent);
|
|
143
143
|
yield spawn(loadLocaleTranslations, locale);
|
|
144
144
|
yield spawn(preloadTranslationsWhenIdle);
|
|
145
145
|
yield spawn(visitWhenIdle);
|
|
@@ -202,8 +202,8 @@ function* visitWhenIdle(): AnyGenerator {
|
|
|
202
202
|
yield call(visit);
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
-
function*
|
|
206
|
-
yield call(
|
|
205
|
+
function* prefetchDictionaryOnFirstIntent(): AnyGenerator {
|
|
206
|
+
yield call(waitForFirstIntent);
|
|
207
207
|
const locale = yield select(selectLocale);
|
|
208
208
|
yield call(prefetchDictionary, locale);
|
|
209
209
|
}
|