mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-04-24 20:07:51 +00:00
added seedphrase feature
This commit is contained in:
parent
78e85f9e79
commit
ba7d81dbf8
45
package-lock.json
generated
45
package-lock.json
generated
@ -57,6 +57,7 @@
|
||||
"file-saver": "^2.0.5",
|
||||
"html-to-text": "^9.0.5",
|
||||
"jssha": "3.3.1",
|
||||
"lit": "^3.2.1",
|
||||
"lodash": "^4.17.21",
|
||||
"mime": "^4.0.4",
|
||||
"moment": "^2.30.1",
|
||||
@ -2523,6 +2524,19 @@
|
||||
"resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
|
||||
"integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="
|
||||
},
|
||||
"node_modules/@lit-labs/ssr-dom-shim": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.1.tgz",
|
||||
"integrity": "sha512-wx4aBmgeGvFmOKucFKY+8VFJSYZxs9poN3SDNQFF6lT6NrQUnHiPB2PWz2sc4ieEcAaYYzN+1uWahEeTq2aRIQ=="
|
||||
},
|
||||
"node_modules/@lit/reactive-element": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz",
|
||||
"integrity": "sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==",
|
||||
"dependencies": {
|
||||
"@lit-labs/ssr-dom-shim": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@malept/cross-spawn-promise": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz",
|
||||
@ -4438,8 +4452,7 @@
|
||||
"node_modules/@types/trusted-types": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="
|
||||
},
|
||||
"node_modules/@types/use-sync-external-store": {
|
||||
"version": "0.0.6",
|
||||
@ -10011,6 +10024,34 @@
|
||||
"uc.micro": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lit": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/lit/-/lit-3.2.1.tgz",
|
||||
"integrity": "sha512-1BBa1E/z0O9ye5fZprPtdqnc0BFzxIxTTOO/tQFmyC/hj1O3jL4TfmLBw0WEwjAokdLwpclkvGgDJwTIh0/22w==",
|
||||
"dependencies": {
|
||||
"@lit/reactive-element": "^2.0.4",
|
||||
"lit-element": "^4.1.0",
|
||||
"lit-html": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lit-element": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.1.1.tgz",
|
||||
"integrity": "sha512-HO9Tkkh34QkTeUmEdNYhMT8hzLid7YlMlATSi1q4q17HE5d9mrrEHJ/o8O2D0cMi182zK1F3v7x0PWFjrhXFew==",
|
||||
"dependencies": {
|
||||
"@lit-labs/ssr-dom-shim": "^1.2.0",
|
||||
"@lit/reactive-element": "^2.0.4",
|
||||
"lit-html": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lit-html": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.2.1.tgz",
|
||||
"integrity": "sha512-qI/3lziaPMSKsrwlxH/xMgikhQ0EGOX2ICU73Bi/YHFvz2j/yMCIrw4+puF2IpQ4+upd3EWbvnHM9+PnJn48YA==",
|
||||
"dependencies": {
|
||||
"@types/trusted-types": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/load-json-file": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
|
||||
|
@ -61,6 +61,7 @@
|
||||
"file-saver": "^2.0.5",
|
||||
"html-to-text": "^9.0.5",
|
||||
"jssha": "3.3.1",
|
||||
"lit": "^3.2.1",
|
||||
"lodash": "^4.17.21",
|
||||
"mime": "^4.0.4",
|
||||
"moment": "^2.30.1",
|
||||
|
47
src/App.tsx
47
src/App.tsx
@ -43,11 +43,13 @@ import Return from "./assets/svgs/Return.svg";
|
||||
import Success from "./assets/svgs/Success.svg";
|
||||
import Info from "./assets/svgs/Info.svg";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import './utils/seedPhrase/RandomSentenceGenerator';
|
||||
|
||||
import {
|
||||
createAccount,
|
||||
generateRandomSentence,
|
||||
saveFileToDisk,
|
||||
saveSeedPhraseToDisk,
|
||||
} from "./utils/generateWallet/generateWallet";
|
||||
import { kdf } from "./deps/kdf";
|
||||
import { generateSaveWalletData } from "./utils/generateWallet/storeWallet";
|
||||
@ -123,6 +125,7 @@ import { fileToBase64 } from "./utils/fileReading";
|
||||
import { handleGetFileFromIndexedDB } from "./utils/indexedDB";
|
||||
import { CoreSyncStatus } from "./components/CoreSyncStatus";
|
||||
import { Wallets } from "./Wallets";
|
||||
import { RandomSentenceGenerator } from "./utils/seedPhrase/RandomSentenceGenerator";
|
||||
|
||||
type extStates =
|
||||
| "not-authenticated"
|
||||
@ -210,6 +213,7 @@ const controlAllQueues = (action) => {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export const clearAllQueues = () => {
|
||||
Object.keys(allQueues).forEach((key) => {
|
||||
const val = allQueues[key];
|
||||
@ -384,7 +388,12 @@ function App() {
|
||||
useRecoilState(enabledDevModeAtom);
|
||||
|
||||
const { toggleFullScreen } = useAppFullScreen(setFullScreen);
|
||||
|
||||
const generatorRef = useRef(null)
|
||||
const exportSeedphrase = ()=> {
|
||||
console.log('hello', generatorRef.current.parsedString)
|
||||
const seedPhrase = generatorRef.current.parsedString
|
||||
saveSeedPhraseToDisk(seedPhrase)
|
||||
}
|
||||
useEffect(() => {
|
||||
const isDevModeFromStorage = localStorage.getItem("isEnabledDevMode");
|
||||
if (isDevModeFromStorage) {
|
||||
@ -899,7 +908,7 @@ function App() {
|
||||
res();
|
||||
}, 250);
|
||||
});
|
||||
const res = await createAccount();
|
||||
const res = await createAccount(generatorRef.current.parsedString);
|
||||
const wallet = await res.generateSaveWalletData(
|
||||
walletToBeDownloadedPassword,
|
||||
crypto.kdfThreads,
|
||||
@ -2430,6 +2439,40 @@ function App() {
|
||||
Set up your Qortal account
|
||||
</TextP>
|
||||
<Spacer height="14px" />
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
maxWidth: '100%',
|
||||
justifyContent: 'center',
|
||||
padding: '10px'
|
||||
}}>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
maxWidth: '400px',
|
||||
alignItems: 'center',
|
||||
gap: '10px'
|
||||
}}>
|
||||
<Typography sx={{
|
||||
fontSize: '14px'
|
||||
}}>Your seedphrase</Typography>
|
||||
<Typography sx={{
|
||||
fontSize: '12px'
|
||||
}}>Only shown once! Please copy and keep safe!</Typography>
|
||||
|
||||
<random-sentence-generator
|
||||
ref={generatorRef}
|
||||
template="adverb verb noun adjective noun adverb verb noun adjective noun adjective verbed adjective noun"
|
||||
|
||||
></random-sentence-generator>
|
||||
</Box>
|
||||
</Box>
|
||||
<CustomButton sx={{
|
||||
padding: '7px',
|
||||
fontSize: '12px'
|
||||
}} onClick={exportSeedphrase}>
|
||||
Export Seedphrase
|
||||
</CustomButton>
|
||||
<Spacer height="14px" />
|
||||
<CustomLabel htmlFor="standard-adornment-password">
|
||||
Wallet Password
|
||||
</CustomLabel>
|
||||
|
147
src/Wallets.tsx
147
src/Wallets.tsx
@ -6,13 +6,19 @@ import ListItemText from "@mui/material/ListItemText";
|
||||
import ListItemAvatar from "@mui/material/ListItemAvatar";
|
||||
import Avatar from "@mui/material/Avatar";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { Box, Button, ButtonBase, IconButton, Input } from "@mui/material";
|
||||
import { Box, Button, ButtonBase, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Input } from "@mui/material";
|
||||
import { CustomButton } from "./App-styles";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import EditIcon from "@mui/icons-material/Edit";
|
||||
import { Label } from "./components/Group/AddGroup";
|
||||
import { Spacer } from "./common/Spacer";
|
||||
import { getWallets, storeWallets } from "./background";
|
||||
import { getWallets, storeWallets, walletVersion } from "./background";
|
||||
import { useModal } from "./common/useModal";
|
||||
import PhraseWallet from "./utils/generateWallet/phrase-wallet";
|
||||
import { decryptStoredWalletFromSeedPhrase } from "./utils/decryptWallet";
|
||||
import { crypto } from "./constants/decryptWallet";
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
import { PasswordField } from "./components";
|
||||
|
||||
const parsefilenameQortal = (filename)=> {
|
||||
return filename.startsWith("qortal_backup_") ? filename.slice(14) : filename;
|
||||
@ -21,6 +27,15 @@ const parsefilenameQortal = (filename)=> {
|
||||
export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
||||
const [wallets, setWallets] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [seedValue, setSeedValue] = useState("");
|
||||
const [seedName, setSeedName] = useState("");
|
||||
const [seedError, setSeedError] = useState("");
|
||||
|
||||
const [password, setPassword] = useState("");
|
||||
const [isOpenSeedModal, setIsOpenSeedModal] = useState(false);
|
||||
const [isLoadingEncryptSeed, setIsLoadingEncryptSeed] = useState(false);
|
||||
|
||||
const { isShow, onCancel, onOk, show, } = useModal();
|
||||
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
accept: {
|
||||
@ -81,7 +96,6 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
||||
setWallets((prev) => {
|
||||
let copyPrev = [...prev];
|
||||
if (wallet === null) {
|
||||
console.log("entered");
|
||||
copyPrev.splice(idx, 1); // Use splice to remove the item
|
||||
return copyPrev;
|
||||
} else {
|
||||
@ -91,6 +105,42 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleSetSeedValue = async ()=> {
|
||||
try {
|
||||
setIsOpenSeedModal(true)
|
||||
const {seedValue, seedName, password} = await show({
|
||||
message: "",
|
||||
publishFee: "",
|
||||
});
|
||||
setIsLoadingEncryptSeed(true)
|
||||
const res = await decryptStoredWalletFromSeedPhrase(seedValue)
|
||||
const wallet2 = new PhraseWallet(res, walletVersion);
|
||||
const wallet = await wallet2.generateSaveWalletData(
|
||||
password,
|
||||
crypto.kdfThreads,
|
||||
() => {}
|
||||
);
|
||||
if(wallet?.address0){
|
||||
setWallets([...wallets, {
|
||||
...wallet,
|
||||
name: seedName
|
||||
}]);
|
||||
setIsOpenSeedModal(false)
|
||||
setSeedValue('')
|
||||
setSeedName('')
|
||||
setPassword('')
|
||||
setSeedError('')
|
||||
} else {
|
||||
setSeedError('Could not create wallet.')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
setSeedError(error?.message || 'Could not create wallet.')
|
||||
} finally {
|
||||
setIsLoadingEncryptSeed(false)
|
||||
}
|
||||
}
|
||||
|
||||
const selectedWalletFunc = (wallet) => {
|
||||
setRawWallet(wallet);
|
||||
setExtState("wallet-dropped");
|
||||
@ -179,12 +229,98 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
||||
right: '20px'
|
||||
}}
|
||||
>
|
||||
<CustomButton {...getRootProps()}>
|
||||
<CustomButton onClick={handleSetSeedValue} sx={{
|
||||
padding: '10px'
|
||||
}} >
|
||||
|
||||
Add seed-phrase
|
||||
</CustomButton>
|
||||
<CustomButton sx={{
|
||||
padding: '10px'
|
||||
}} {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
Add wallets
|
||||
</CustomButton>
|
||||
</Box>
|
||||
|
||||
<Dialog
|
||||
open={isOpenSeedModal}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && seedValue && seedName && password) {
|
||||
onOk({seedValue, seedName, password});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
Type or paste in your seed-phrase
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
|
||||
}}
|
||||
>
|
||||
<Label>Name</Label>
|
||||
<Input
|
||||
placeholder="Name"
|
||||
value={seedName}
|
||||
onChange={(e) => setSeedName(e.target.value)}
|
||||
/>
|
||||
<Spacer height="7px" />
|
||||
<Label>Seed-phrase</Label>
|
||||
<Input
|
||||
placeholder="Seed-phrase"
|
||||
value={seedValue}
|
||||
onChange={(e) => setSeedValue(e.target.value)}
|
||||
/>
|
||||
<Spacer height="7px" />
|
||||
|
||||
<Label>Choose new password</Label>
|
||||
<PasswordField
|
||||
id="standard-adornment-password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
|
||||
</Box>
|
||||
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button disabled={isLoadingEncryptSeed} variant="contained" onClick={()=> {
|
||||
setIsOpenSeedModal(false)
|
||||
setSeedValue('')
|
||||
setSeedName('')
|
||||
setPassword('')
|
||||
setSeedError('')
|
||||
}}>
|
||||
Close
|
||||
</Button>
|
||||
<LoadingButton
|
||||
loading={isLoadingEncryptSeed}
|
||||
disabled={!seedValue || !seedName || !password}
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
if(!seedValue || !seedName || !password) return
|
||||
onOk({seedValue, seedName, password});
|
||||
}}
|
||||
autoFocus
|
||||
>
|
||||
Add
|
||||
</LoadingButton>
|
||||
<Typography sx={{
|
||||
fontSize: '14px',
|
||||
visibility: seedError ? 'visible' : 'hidden'
|
||||
}}>{seedError}</Typography>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
</div>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
@ -338,6 +474,9 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -22,3 +22,13 @@ export const decryptStoredWallet = async (password, wallet) => {
|
||||
const decryptedBytes = AES_CBC.decrypt(encryptedSeedBytes, encryptionKey, false, iv)
|
||||
return decryptedBytes
|
||||
}
|
||||
|
||||
export const decryptStoredWalletFromSeedPhrase = async (password) => {
|
||||
console.log('p')
|
||||
const threads = doInitWorkers(crypto.kdfThreads)
|
||||
const salt = new Uint8Array(void 0)
|
||||
|
||||
|
||||
const seed = await kdf(password, salt, threads)
|
||||
return seed
|
||||
}
|
@ -75,8 +75,8 @@ export function generateRandomSentence(template = 'adverb verb noun adjective no
|
||||
return parse(template);
|
||||
}
|
||||
|
||||
export const createAccount = async()=> {
|
||||
const generatedSeedPhrase = generateRandomSentence()
|
||||
export const createAccount = async(generatedSeedPhrase)=> {
|
||||
if(!generatedSeedPhrase) throw new Error('No generated seed-phrase')
|
||||
const threads = doInitWorkers(crypto.kdfThreads)
|
||||
|
||||
const seed = await kdf(generatedSeedPhrase, void 0, threads)
|
||||
@ -94,3 +94,12 @@ export const createAccount = async()=> {
|
||||
await FileSaver.saveAs(blob, fileName);
|
||||
|
||||
}
|
||||
|
||||
export const saveSeedPhraseToDisk = async (data) => {
|
||||
|
||||
const blob = new Blob([data], { type: 'text/plain;charset=utf-8' })
|
||||
const fileName = "qortal_seedphrase.txt"
|
||||
|
||||
await FileSaver.saveAs(blob, fileName);
|
||||
|
||||
}
|
175
src/utils/seedPhrase/RandomSentenceGenerator.ts
Normal file
175
src/utils/seedPhrase/RandomSentenceGenerator.ts
Normal file
@ -0,0 +1,175 @@
|
||||
// Author: irontiga <irontiga@gmail.com>
|
||||
|
||||
import { html, LitElement, css } from 'lit'
|
||||
import * as WORDLISTS from './wordList'
|
||||
|
||||
class RandomSentenceGenerator extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
template: { type: String, attribute: 'template' },
|
||||
parsedString: { type: String },
|
||||
fetchedWordlistCount: { type: Number, value: 0 },
|
||||
capitalize: { type: Boolean },
|
||||
partsOfSpeechMap: { type: Object },
|
||||
templateEntropy: { type: Number, reflect: true, attribute: 'template-entropy' },
|
||||
maxWordLength: { type: Number, attribute: 'max-word-length' }
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.template = 'adjective noun verb adverb.'
|
||||
this.maxWordLength = 0
|
||||
this.parsedString = ''
|
||||
this.fetchedWordlistCount = 0
|
||||
this.capitalize = true
|
||||
this.partsOfSpeechMap = {
|
||||
'noun': 'nouns',
|
||||
'adverb': 'adverbs',
|
||||
'adv': 'adverbs',
|
||||
'verb': 'verbs',
|
||||
'interjection': 'interjections',
|
||||
'adjective': 'adjectives',
|
||||
'adj': 'adjectives',
|
||||
'verbed': 'verbed'
|
||||
}
|
||||
this.partsOfSpeech = Object.keys(this.partsOfSpeechMap)
|
||||
this._wordlists = WORDLISTS
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
div {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
background-color: #1f2023;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div>${this.parsedString}</div>
|
||||
`
|
||||
}
|
||||
|
||||
|
||||
|
||||
firstUpdated() {
|
||||
console.log('this.template', this.template)
|
||||
// ...
|
||||
}
|
||||
|
||||
updated(changedProperties) {
|
||||
let regen = false
|
||||
|
||||
if (changedProperties.has('template')) {
|
||||
regen = true
|
||||
}
|
||||
|
||||
if (changedProperties.has('maxWordLength')) {
|
||||
console.dir(this.maxWordLength)
|
||||
|
||||
if (this.maxWordLength) {
|
||||
const wl = { ...this._wordlists }
|
||||
|
||||
for (const partOfSpeech in this._wordlists) {
|
||||
console.log(this._wordlists[partOfSpeech])
|
||||
if (Array.isArray(this._wordlists[partOfSpeech])) {
|
||||
wl[partOfSpeech] = this._wordlists[partOfSpeech].filter(word => word.length <= this.maxWordLength)
|
||||
}
|
||||
}
|
||||
|
||||
this._wordlists = wl
|
||||
}
|
||||
|
||||
regen = true
|
||||
}
|
||||
|
||||
if (regen) this.generate()
|
||||
}
|
||||
|
||||
_RNG(entropy) {
|
||||
if (entropy > 1074) {
|
||||
throw new Error('Javascript can not handle that much entropy!')
|
||||
}
|
||||
|
||||
let randNum = 0
|
||||
|
||||
const crypto = window.crypto || window.msCrypto
|
||||
|
||||
if (crypto) {
|
||||
const entropy256 = Math.ceil(entropy / 8)
|
||||
|
||||
let buffer = new Uint8Array(entropy256)
|
||||
|
||||
crypto.getRandomValues(buffer)
|
||||
|
||||
randNum = buffer.reduce((num, value) => {
|
||||
return num * value
|
||||
}, 1) / Math.pow(256, entropy256)
|
||||
} else {
|
||||
console.warn('Secure RNG not found. Using Math.random')
|
||||
|
||||
randNum = Math.random()
|
||||
}
|
||||
|
||||
return randNum
|
||||
}
|
||||
|
||||
setRNG(fn) {
|
||||
this._RNG = fn
|
||||
}
|
||||
|
||||
_captitalize(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
}
|
||||
|
||||
getWord(partOfSpeech) {
|
||||
const words = this._wordlists[this.partsOfSpeechMap[partOfSpeech]]
|
||||
const requiredEntropy = Math.log(words.length) / Math.log(2)
|
||||
const index = this._RNG(requiredEntropy) * words.length
|
||||
|
||||
return {
|
||||
word: words[Math.round(index)],
|
||||
entropy: words.length
|
||||
}
|
||||
}
|
||||
|
||||
generate() {
|
||||
this.parsedString = this.parse(this.template)
|
||||
}
|
||||
|
||||
parse(template) {
|
||||
const split = template.split(/[\s]/g)
|
||||
|
||||
let entropy = 1
|
||||
|
||||
const final = split.map(word => {
|
||||
const lower = word.toLowerCase()
|
||||
|
||||
this.partsOfSpeech.some(partOfSpeech => {
|
||||
const partOfSpeechIndex = lower.indexOf(partOfSpeech) // Check it exists
|
||||
const nextChar = word.charAt(partOfSpeech.length)
|
||||
|
||||
if (partOfSpeechIndex === 0 && !(nextChar && (nextChar.match(/[a-zA-Z]/g) != null))) {
|
||||
const replacement = this.getWord(partOfSpeech)
|
||||
word = replacement.word + word.slice(partOfSpeech.length) // Append the rest of the "word" (punctuation)
|
||||
entropy = entropy * replacement.entropy
|
||||
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
return word
|
||||
})
|
||||
|
||||
this.templateEntropy = Math.floor(Math.log(entropy) / Math.log(8))
|
||||
|
||||
return final.join(' ')
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('random-sentence-generator', RandomSentenceGenerator)
|
||||
|
||||
export default RandomSentenceGenerator
|
40
src/utils/seedPhrase/verb-past-tense.ts
Normal file
40
src/utils/seedPhrase/verb-past-tense.ts
Normal file
@ -0,0 +1,40 @@
|
||||
export const EXCEPTIONS = {
|
||||
'are': 'were',
|
||||
'eat': 'ate',
|
||||
'go': 'went',
|
||||
'have': 'had',
|
||||
'inherit': 'inherited',
|
||||
'is': 'was',
|
||||
'run': 'ran',
|
||||
'sit': 'sat',
|
||||
'visit': 'visited'
|
||||
}
|
||||
|
||||
export const getPastTense = (verb, exceptions = EXCEPTIONS) => {
|
||||
if (exceptions[verb]) {
|
||||
return exceptions[verb]
|
||||
}
|
||||
|
||||
if ((/e$/i).test(verb)) {
|
||||
return verb + 'd'
|
||||
}
|
||||
|
||||
if ((/[aeiou]c$/i).test(verb)) {
|
||||
return verb + 'ked'
|
||||
}
|
||||
|
||||
// for american english only
|
||||
if ((/el$/i).test(verb)) {
|
||||
return verb + 'ed'
|
||||
}
|
||||
|
||||
if ((/[aeio][aeiou][dlmnprst]$/).test(verb)) {
|
||||
return verb + 'ed'
|
||||
}
|
||||
|
||||
if ((/[aeiou][bdglmnprst]$/i).test(verb)) {
|
||||
return verb.replace(/(.+[aeiou])([bdglmnprst])/, '$1$2$2ed')
|
||||
}
|
||||
|
||||
return verb + 'ed'
|
||||
}
|
32
src/utils/seedPhrase/wordList.ts
Normal file
32
src/utils/seedPhrase/wordList.ts
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user