From b21fe314f9e5e19ebd2ca2d000eb1ea9dd6955d6 Mon Sep 17 00:00:00 2001 From: jk <47693+sectore@users.noreply.github.com> Date: Sat, 20 Jul 2024 17:13:41 +0200 Subject: [PATCH 1/7] Support SLIP-39 - update store - update routes - refactor file structure (needed for new routes) - update header layout slightly - misc. --- README.md | 5 +- bun.lockb | Bin 180586 -> 181333 bytes package.json | 3 +- src/Header.svelte | 85 +++++++++++++-------- src/lib/const.ts | 6 ++ src/lib/store.svelte.ts | 87 ++++++++++++++++------ src/lib/types.ts | 7 +- src/lib/utils.ts | 5 +- src/routes/+layout.svelte | 25 ++++++- src/routes/+page.svelte | 81 ++------------------ src/routes/[slug]/+page.svelte | 79 ++++++++++++++++++++ src/routes/{ => [slug]}/grid/+page.svelte | 0 src/routes/{ => [slug]}/list/+page.svelte | 0 13 files changed, 245 insertions(+), 138 deletions(-) create mode 100644 src/routes/[slug]/+page.svelte rename src/routes/{ => [slug]}/grid/+page.svelte (100%) rename src/routes/{ => [slug]}/list/+page.svelte (100%) diff --git a/README.md b/README.md index caa3e79..448fa39 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# [BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md) | [SLIP-39](https://github.com/satoshilabs/slips/blob/master/slip-0039.md) word lists +# [BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md) | [SLIP-39](https://github.com/satoshilabs/slips/blob/master/slip-0039.md) word list -Explore [BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md) word lists in 10 languages or [SLIP-39](https://github.com/satoshilabs/slips/blob/master/slip-0039.md) in English. Filter words or search for word positions. +Explore [BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md) and [SLIP-39](https://github.com/satoshilabs/slips/blob/master/slip-0039.md) word lists in up to 10 languages. Filter words or search for word positions. ## Live @@ -49,7 +49,6 @@ bun run import:slip39 - [Svelte](https://svelte.dev/) - [daisyUI](https://daisyui.com/) - [Effect](https://effect.website/) -- [bitcoinjs/bip39](https://github.com/bitcoinjs/bip39) - [svelte-typewriter](https://github.com/satohshi/svelte-typewriter) - [lucide-svelte](https://lucide.dev/) diff --git a/bun.lockb b/bun.lockb index 40e794c12625b9b8f0a71efedd2b47f4cd68b1a3..39d733c592f2087378ec18c48a92053a0c3479f9 100755 GIT binary patch delta 25570 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 D61Ml6 diff --git a/package.json b/package.json index 8d7afb1..dafe2bb 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,9 @@ "version": "0.0.1", "devDependencies": { "@effect/platform": "^0.59.2", - "@effect/platform-bun": "^0.39.2", "@effect/platform-browser": "^0.38.3", + "@effect/platform-bun": "^0.39.2", + "@effect/schema": "^0.68.26", "@sveltejs/adapter-auto": "^3.2.2", "@sveltejs/adapter-static": "^3.0.2", "@sveltejs/kit": "^2.5.18", diff --git a/src/Header.svelte b/src/Header.svelte index c953dfd..ee817b2 100644 --- a/src/Header.svelte +++ b/src/Header.svelte @@ -1,61 +1,57 @@ + const isSubPath = (subPath: SUB_PATH) => $page.url.pathname.endsWith(subPath); -{#snippet headline(clazz)} -

- BIP-39 word list -

-{/snippet} + const isBip39 = $derived(store.wordlistType === 'bip39'); + const isSlip39 = $derived(store.wordlistType === 'slip39'); + + const langs = $derived.by(() => (isSlip39 ? ['en'] : languages)); +
-
+ - {@render headline('hidden md:flex')} -
@@ -66,19 +62,44 @@ {/if}
-
- {@render headline('flex md:hidden pt-10')} +
+

+ BIP-39 + + SLIP-39 +

- {languages.length} languages + {langs.length} language{langs.length > 1 ? 's' : ''}

diff --git a/src/lib/const.ts b/src/lib/const.ts index 489d805..ea1bff0 100644 --- a/src/lib/const.ts +++ b/src/lib/const.ts @@ -1,5 +1,11 @@ import type { LANG, WordListType } from './types'; +export enum SUB_PATH { + HOME = '/', + LIST = '/list', + GRID = '/grid' +} + const bip39Url = (file: string) => `https://github.com/bitcoin/bips/blob/master/bip-0039/${file}`; type WordListUrls = Record; diff --git a/src/lib/store.svelte.ts b/src/lib/store.svelte.ts index e73fa73..8033059 100644 --- a/src/lib/store.svelte.ts +++ b/src/lib/store.svelte.ts @@ -1,8 +1,9 @@ -import { pipe } from 'effect'; +import { Effect, pipe } from 'effect'; import * as O from 'effect/Option'; import * as A from 'effect/Array'; -import { validPosition } from '$lib/utils'; -import type { LANG, WordList } from './types'; + +import { getLocalStorage, setLocalStorage, validPosition } from '$lib/utils'; +import { WordListTypeSchema, type LANG, type WordList, type WordListType } from './types'; import bip39en from './wordlists/bip39-en'; import bip39cz from './wordlists/bip39-cz'; import bip39zhHans from './wordlists/bip39-zh-Hans'; @@ -13,39 +14,77 @@ import bip39jp from './wordlists/bip39-jp'; import bip39kr from './wordlists/bip39-kr'; import bip39pt from './wordlists/bip39-pt'; import bip39es from './wordlists/bip39-es'; +import slip39es from './wordlists/slip39-en'; -export enum PATHS { - HOME = '/', - LIST = '/list', - GRID = '/grid' -} - -// bip30 wordlists -// https://github.com/bitcoinjs/bip39/tree/master/src/wordlists -const WORDLISTS: Record = { - en: bip39en, - cz: bip39cz, - 'zh-Hans': bip39zhHans, - 'zh-Hant': bip39zhHant, - fr: bip39fr, - it: bip39it, - jp: bip39jp, - kr: bip39kr, - pt: bip39pt, - es: bip39es +const WORD_LIST_MAP: Record> = { + // https://github.com/bitcoinjs/bip39/tree/master/src/wordlists + bip39: { + en: bip39en, + cz: bip39cz, + 'zh-Hans': bip39zhHans, + 'zh-Hant': bip39zhHant, + fr: bip39fr, + it: bip39it, + jp: bip39jp, + kr: bip39kr, + pt: bip39pt, + es: bip39es + }, + slip39: { + en: slip39es, + cz: [''], + 'zh-Hans': [''], + 'zh-Hant': [''], + fr: [''], + it: [''], + jp: [''], + kr: [''], + pt: [''], + es: [''] + } }; +const KEY_WORD_LIST_TYPE = 'type'; +const DEFAULT_WORD_LIST_TYPE: WordListType = 'bip39'; + class Store { selectedLang = $state('en'); + + // @private + #wordlistType = $state(DEFAULT_WORD_LIST_TYPE); + + constructor() { + // check stored `WordlistType` at start + this.checkWordlistType(); + } + + // getter + wordlistType = $derived(this.#wordlistType); + + setWordlistType = (t: WordListType) => + pipe( + setLocalStorage(t, KEY_WORD_LIST_TYPE, WordListTypeSchema), + Effect.tap(() => (this.#wordlistType = t)), + Effect.runSync + ); + + checkWordlistType = () => + pipe( + getLocalStorage(KEY_WORD_LIST_TYPE, WordListTypeSchema), + Effect.orElse(() => Effect.succeed(DEFAULT_WORD_LIST_TYPE)), + Effect.tap((t) => (this.#wordlistType = t)), + Effect.runSync + ); + randomize = $state(false); // @private #wordlist: WordList = $derived.by(() => pipe( - WORDLISTS[this.selectedLang], + WORD_LIST_MAP[this.#wordlistType][this.selectedLang], A.map((word, i) => ({ pos: i + 1, word })) ) ); - // Make wordlist readable only + // getter wordlist = $derived(this.#wordlist); filter = $state>(O.none()); diff --git a/src/lib/types.ts b/src/lib/types.ts index 3e7be31..3e4e498 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,3 +1,5 @@ +import * as S from '@effect/schema/Schema'; + export const languages = [ 'en', 'fr', @@ -13,7 +15,10 @@ export const languages = [ export type LANG = (typeof languages)[number]; -export type WordListType = 'bip39' | 'slip39'; +export const WordListTypeSchema = S.parseJson(S.Literal('bip39', 'slip39')); + +export type WordListType = typeof WordListTypeSchema.Type; +export type WordListTypencoded = typeof WordListTypeSchema.Encoded; export type WordListItem = { pos: number; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index fa99996..67a7510 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -6,7 +6,8 @@ import * as A from 'effect/Array'; import * as SC from '@effect/schema/Schema'; import * as KeyValueStore from '@effect/platform/KeyValueStore'; import { BrowserKeyValueStore } from '@effect/platform-browser'; -import type { WordList, WordListItem } from './types'; +import type { WordList, WordListItem, WordListType } from './types'; +import type { SUB_PATH } from './const'; export const validPosition: (n: string) => O.Option = flow( N.parse, @@ -46,3 +47,5 @@ export const setLocalStorage = (value: A, key: string, schema: SC.Schema `/${type}${subPath}`; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 0995dd7..2c5c261 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,9 +1,32 @@ -
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 26b75b6..66b9dde 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,79 +1,10 @@ - -
-
-
-

- #{no} -

- of {wordlistTypeable.length} words -
-
- { - noVisible = true; - currentWord = wordlistTypeable[i]; - }} - ondeleteend={() => { - noVisible = false; - }} - /> -
-
- -
-
-
diff --git a/src/routes/[slug]/+page.svelte b/src/routes/[slug]/+page.svelte new file mode 100644 index 0000000..26b75b6 --- /dev/null +++ b/src/routes/[slug]/+page.svelte @@ -0,0 +1,79 @@ + + +
+
+
+

+ #{no} +

+ of {wordlistTypeable.length} words +
+
+ { + noVisible = true; + currentWord = wordlistTypeable[i]; + }} + ondeleteend={() => { + noVisible = false; + }} + /> +
+
+ +
+
+
diff --git a/src/routes/grid/+page.svelte b/src/routes/[slug]/grid/+page.svelte similarity index 100% rename from src/routes/grid/+page.svelte rename to src/routes/[slug]/grid/+page.svelte diff --git a/src/routes/list/+page.svelte b/src/routes/[slug]/list/+page.svelte similarity index 100% rename from src/routes/list/+page.svelte rename to src/routes/[slug]/list/+page.svelte From 112ecc79df9c0c7d5e5ece348b67e5fe3f247fca Mon Sep 17 00:00:00 2001 From: jk <47693+sectore@users.noreply.github.com> Date: Sun, 21 Jul 2024 20:23:34 +0200 Subject: [PATCH 2/7] optional languages --- src/Header.svelte | 18 ++++++------- src/lib/store.svelte.ts | 57 ++++++++++++++++++++++++--------------- src/lib/types.ts | 3 +++ src/routes/+layout.svelte | 29 +++++++++----------- 4 files changed, 59 insertions(+), 48 deletions(-) diff --git a/src/Header.svelte b/src/Header.svelte index ee817b2..a2375ae 100644 --- a/src/Header.svelte +++ b/src/Header.svelte @@ -3,7 +3,7 @@ import { AlignJustify, LayoutGrid, Moon, Repeat2, Sun } from 'lucide-svelte'; import { SUB_PATH } from '$lib/const'; import store from '$lib/store.svelte'; - import { languages, type LANG } from '$lib/types'; + import { type LANG } from '$lib/types'; import { getFullPath } from '$lib/utils'; import T from '$lib/theme.svelte'; @@ -12,7 +12,10 @@ const isBip39 = $derived(store.wordlistType === 'bip39'); const isSlip39 = $derived(store.wordlistType === 'slip39'); - const langs = $derived.by(() => (isSlip39 ? ['en'] : languages)); + const langLabel = $derived.by(() => { + const l = store.languages.length; + return `${l} language${l > 1 ? 's' : ''}`; + });
@@ -87,19 +90,16 @@ >

- {langs.length} language{langs.length > 1 ? 's' : ''} + {langLabel}

diff --git a/src/lib/store.svelte.ts b/src/lib/store.svelte.ts index 8033059..7a4dfb9 100644 --- a/src/lib/store.svelte.ts +++ b/src/lib/store.svelte.ts @@ -1,6 +1,7 @@ import { Effect, pipe } from 'effect'; import * as O from 'effect/Option'; import * as A from 'effect/Array'; +import * as R from 'effect/Record'; import { getLocalStorage, setLocalStorage, validPosition } from '$lib/utils'; import { WordListTypeSchema, type LANG, type WordList, type WordListType } from './types'; @@ -16,31 +17,31 @@ import bip39pt from './wordlists/bip39-pt'; import bip39es from './wordlists/bip39-es'; import slip39es from './wordlists/slip39-en'; -const WORD_LIST_MAP: Record> = { +const WORD_LIST_MAP: Record>> = { // https://github.com/bitcoinjs/bip39/tree/master/src/wordlists bip39: { - en: bip39en, - cz: bip39cz, - 'zh-Hans': bip39zhHans, - 'zh-Hant': bip39zhHant, - fr: bip39fr, - it: bip39it, - jp: bip39jp, - kr: bip39kr, - pt: bip39pt, - es: bip39es + en: O.some(bip39en), + cz: O.some(bip39cz), + 'zh-Hans': O.some(bip39zhHans), + 'zh-Hant': O.some(bip39zhHant), + fr: O.some(bip39fr), + it: O.some(bip39it), + jp: O.some(bip39jp), + kr: O.some(bip39kr), + pt: O.some(bip39pt), + es: O.some(bip39es) }, slip39: { - en: slip39es, - cz: [''], - 'zh-Hans': [''], - 'zh-Hant': [''], - fr: [''], - it: [''], - jp: [''], - kr: [''], - pt: [''], - es: [''] + en: O.some(slip39es), + cz: O.none(), + 'zh-Hans': O.none(), + 'zh-Hant': O.none(), + fr: O.none(), + it: O.none(), + jp: O.none(), + kr: O.none(), + pt: O.none(), + es: O.none() } }; @@ -81,12 +82,24 @@ class Store { #wordlist: WordList = $derived.by(() => pipe( WORD_LIST_MAP[this.#wordlistType][this.selectedLang], - A.map((word, i) => ({ pos: i + 1, word })) + // transform `string` -> `WordListItem` + O.map(A.map((word, i) => ({ pos: i + 1, word }))), + O.getOrElse(() => []) ) ); // getter wordlist = $derived(this.#wordlist); + languages = $derived.by(() => + pipe( + WORD_LIST_MAP[this.#wordlistType], + // filter out `none` values + R.getSomes, + // get languages (keys) + R.keys + ) + ); + filter = $state>(O.none()); wordlistFiltered = $derived.by(() => pipe( diff --git a/src/lib/types.ts b/src/lib/types.ts index 3e4e498..685787c 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -15,6 +15,9 @@ export const languages = [ export type LANG = (typeof languages)[number]; +// Schema for routes (`string`) +export const WordListTypeLiteralSchema = S.Literal('bip39', 'slip39'); +// Schema for LocalStorage (JSON) export const WordListTypeSchema = S.parseJson(S.Literal('bip39', 'slip39')); export type WordListType = typeof WordListTypeSchema.Type; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 2c5c261..b9f8267 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,30 +1,25 @@ From 2f6c0becbe854af29ac0b39844cd5b25331ccfdb Mon Sep 17 00:00:00 2001 From: jk <47693+sectore@users.noreply.github.com> Date: Sun, 21 Jul 2024 20:26:17 +0200 Subject: [PATCH 3/7] rename app to `wordlist` --- README.md | 4 ++-- flake.nix | 2 +- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 448fa39..fdbd6a1 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # [BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md) | [SLIP-39](https://github.com/satoshilabs/slips/blob/master/slip-0039.md) word list -Explore [BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md) and [SLIP-39](https://github.com/satoshilabs/slips/blob/master/slip-0039.md) word lists in up to 10 languages. Filter words or search for word positions. +Explore [BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md) and [SLIP-39](https://github.com/satoshilabs/slips/blob/master/slip-0039.md) word list in up to 10 languages. Filter words or search for word positions. ## Live -https://bip39.bitcoinbeachtravemuende.de +https://wordlist.bitcoinbeachtravemuende.de ## Preview diff --git a/flake.nix b/flake.nix index fcf390a..6f3d0eb 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,5 @@ { - description = "bip39 word list"; + description = "word list"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; diff --git a/package.json b/package.json index dafe2bb..7332a8c 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "bip39-slip39-wordlist", + "name": "wordlist", "version": "0.0.1", "devDependencies": { "@effect/platform": "^0.59.2", From ec599ce4bd1e0b636084245833780ece582c3908 Mon Sep 17 00:00:00 2001 From: jk <47693+sectore@users.noreply.github.com> Date: Sun, 21 Jul 2024 20:26:35 +0200 Subject: [PATCH 4/7] `wordlist` -> `word-list` --- README.md | 2 +- package.json | 2 +- src/Footer.svelte | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fdbd6a1..2c8235f 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ https://wordlist.bitcoinbeachtravemuende.de ## Preview -https://github.com/BitcoinBeachTravemuende/bip39-word-list/assets/47693/d03fa943-cb90-4064-8b3e-9577f4d0b2e1 +https://github.com/BitcoinBeachTravemuende/word-list/assets/47693/d03fa943-cb90-4064-8b3e-9577f4d0b2e1 ## Develop (locally) diff --git a/package.json b/package.json index 7332a8c..8f1d1cc 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "wordlist", + "name": "word-list", "version": "0.0.1", "devDependencies": { "@effect/platform": "^0.59.2", diff --git a/src/Footer.svelte b/src/Footer.svelte index 142c6ab..51f2eaa 100644 --- a/src/Footer.svelte +++ b/src/Footer.svelte @@ -6,7 +6,7 @@