* fix(copy): prevent shorthand copy if source is present in useCopy * refactor(copy): normalized copy usage
		
			
				
	
	
		
			132 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			132 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <script setup lang="ts">
 | |
| import {
 | |
|   chineseSimplifiedWordList,
 | |
|   chineseTraditionalWordList,
 | |
|   czechWordList,
 | |
|   englishWordList,
 | |
|   entropyToMnemonic,
 | |
|   frenchWordList,
 | |
|   generateEntropy,
 | |
|   italianWordList,
 | |
|   japaneseWordList,
 | |
|   koreanWordList,
 | |
|   mnemonicToEntropy,
 | |
|   portugueseWordList,
 | |
|   spanishWordList,
 | |
| } from '@it-tools/bip39';
 | |
| import { Copy, Refresh } from '@vicons/tabler';
 | |
| 
 | |
| import { useCopy } from '@/composable/copy';
 | |
| import { useValidation } from '@/composable/validation';
 | |
| import { isNotThrowing } from '@/utils/boolean';
 | |
| import { withDefaultOnError } from '@/utils/defaults';
 | |
| 
 | |
| const languages = {
 | |
|   'English': englishWordList,
 | |
|   'Chinese simplified': chineseSimplifiedWordList,
 | |
|   'Chinese traditional': chineseTraditionalWordList,
 | |
|   'Czech': czechWordList,
 | |
|   'French': frenchWordList,
 | |
|   'Italian': italianWordList,
 | |
|   'Japanese': japaneseWordList,
 | |
|   'Korean': koreanWordList,
 | |
|   'Portuguese': portugueseWordList,
 | |
|   'Spanish': spanishWordList,
 | |
| };
 | |
| 
 | |
| const entropy = ref(generateEntropy());
 | |
| const passphraseInput = ref('');
 | |
| 
 | |
| const language = ref<keyof typeof languages>('English');
 | |
| const passphrase = computed({
 | |
|   get() {
 | |
|     return withDefaultOnError(() => entropyToMnemonic(entropy.value, languages[language.value]), passphraseInput.value);
 | |
|   },
 | |
|   set(value: string) {
 | |
|     passphraseInput.value = value;
 | |
|     entropy.value = withDefaultOnError(() => mnemonicToEntropy(value, languages[language.value]), '');
 | |
|   },
 | |
| });
 | |
| 
 | |
| const entropyValidation = useValidation({
 | |
|   source: entropy,
 | |
|   rules: [
 | |
|     {
 | |
|       validator: value => value === '' || (value.length <= 32 && value.length >= 16 && value.length % 4 === 0),
 | |
|       message: 'Entropy length should be >= 16, <= 32 and be a multiple of 4',
 | |
|     },
 | |
|     {
 | |
|       validator: value => /^[a-fA-F0-9]*$/.test(value),
 | |
|       message: 'Entropy should be an hexadecimal string',
 | |
|     },
 | |
|   ],
 | |
| });
 | |
| 
 | |
| const mnemonicValidation = useValidation({
 | |
|   source: passphrase,
 | |
|   rules: [
 | |
|     {
 | |
|       validator: value => isNotThrowing(() => mnemonicToEntropy(value, languages[language.value])),
 | |
|       message: 'Invalid mnemonic',
 | |
|     },
 | |
|   ],
 | |
| });
 | |
| 
 | |
| function refreshEntropy() {
 | |
|   entropy.value = generateEntropy();
 | |
| }
 | |
| 
 | |
| const { copy: copyEntropy } = useCopy({ source: entropy, text: 'Entropy copied to the clipboard' });
 | |
| const { copy: copyPassphrase } = useCopy({ source: passphrase, text: 'Passphrase copied to the clipboard' });
 | |
| </script>
 | |
| 
 | |
| <template>
 | |
|   <div>
 | |
|     <n-grid cols="3" x-gap="12">
 | |
|       <n-gi span="1">
 | |
|         <c-select
 | |
|           v-model:value="language"
 | |
|           searchable
 | |
|           label="Language:"
 | |
|           :options="Object.keys(languages)"
 | |
|         />
 | |
|       </n-gi>
 | |
|       <n-gi span="2">
 | |
|         <n-form-item
 | |
|           label="Entropy (seed):"
 | |
|           :feedback="entropyValidation.message"
 | |
|           :validation-status="entropyValidation.status"
 | |
|         >
 | |
|           <n-input-group>
 | |
|             <c-input-text v-model:value="entropy" placeholder="Your string..." />
 | |
| 
 | |
|             <c-button @click="refreshEntropy()">
 | |
|               <n-icon size="22">
 | |
|                 <Refresh />
 | |
|               </n-icon>
 | |
|             </c-button>
 | |
|             <c-button @click="copyEntropy()">
 | |
|               <n-icon size="22">
 | |
|                 <Copy />
 | |
|               </n-icon>
 | |
|             </c-button>
 | |
|           </n-input-group>
 | |
|         </n-form-item>
 | |
|       </n-gi>
 | |
|     </n-grid>
 | |
|     <n-form-item
 | |
|       label="Passphrase (mnemonic):"
 | |
|       :feedback="mnemonicValidation.message"
 | |
|       :validation-status="mnemonicValidation.status"
 | |
|     >
 | |
|       <n-input-group>
 | |
|         <c-input-text v-model:value="passphrase" placeholder="Your mnemonic..." raw-text />
 | |
| 
 | |
|         <c-button @click="copyPassphrase()">
 | |
|           <n-icon size="22" :component="Copy" />
 | |
|         </c-button>
 | |
|       </n-input-group>
 | |
|     </n-form-item>
 | |
|   </div>
 | |
| </template>
 |