feat: 251125/create_wallet_flow
This commit is contained in:
parent
4da3a8ff7d
commit
df5158b318
@ -15,10 +15,13 @@
|
|||||||
"tauri:build": "tauri build"
|
"tauri:build": "tauri build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@neptune/native": "file:./packages/neptune-native",
|
||||||
|
"@neptune/wasm": "file:./packages/neptune-wasm",
|
||||||
"@tailwindcss/vite": "^4.1.17",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"@tanstack/vue-form": "^1.26.0",
|
"@tanstack/vue-form": "^1.26.0",
|
||||||
|
"@tauri-apps/api": "^2.9.0",
|
||||||
"@vueuse/core": "^14.0.0",
|
"@vueuse/core": "^14.0.0",
|
||||||
"axios": "^1.7.9",
|
"axios": "1.13.2",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-vue-next": "^0.554.0",
|
"lucide-vue-next": "^0.554.0",
|
||||||
|
|||||||
79
packages/neptune-native/index.d.ts
vendored
Normal file
79
packages/neptune-native/index.d.ts
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
/* auto-generated by NAPI-RS */
|
||||||
|
|
||||||
|
export declare function initNativeModule(): string
|
||||||
|
export declare function quickVmTest(): string
|
||||||
|
export declare function getVersion(): string
|
||||||
|
export declare class WalletManager {
|
||||||
|
constructor()
|
||||||
|
/**
|
||||||
|
* Generate spending key from BIP39 seed phrase
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
* * `seed_phrase` - Array of words (12, 15, 18, 21, or 24 words)
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* JSON with spending_key_hex, view_key_hex, receiver_identifier
|
||||||
|
*/
|
||||||
|
generateKeysFromSeed(seedPhrase: Array<string>): string
|
||||||
|
/**
|
||||||
|
* Generate lock_script_and_witness (transaction signature)
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
* * `spending_key_hex` - Hex-encoded spending key from generate_keys_from_seed
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* JSON with lock_script_and_witness in hex format
|
||||||
|
*/
|
||||||
|
createLockScriptAndWitness(spendingKeyHex: string): string
|
||||||
|
/** Derive ViewKey hex (0x-prefixed bincode) from a Generation spending key hex */
|
||||||
|
spendingKeyToViewKeyHex(spendingKeyHex: string): string
|
||||||
|
/** Call wallet_getAdditionRecordsFromViewKey and return JSON as string */
|
||||||
|
getAdditionRecordsFromViewKeyCall(rpcUrl: string, viewKeyHex: string, startBlock: number, endBlock: number | undefined | null, maxSearchDepth: number): Promise<string>
|
||||||
|
/**
|
||||||
|
* Build RPC request to get UTXOs from view key
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
* * `view_key_hex` - Hex-encoded view key from generate_keys_from_seed
|
||||||
|
* * `start_block` - Starting block height (0 for genesis)
|
||||||
|
* * `end_block` - Ending block height (current tip)
|
||||||
|
* * `max_search_depth` - Maximum blocks to search (default: 1000)
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* JSON-RPC request ready to send
|
||||||
|
*
|
||||||
|
* # Note
|
||||||
|
* Method name format: namespace_method (e.g. wallet_getUtxosFromViewKey)
|
||||||
|
*/
|
||||||
|
buildGetUtxosRequest(viewKeyHex: string, startBlock: number, endBlock: number, maxSearchDepth?: number | undefined | null): string
|
||||||
|
/** Build RPC request to test chain height (for connectivity testing) */
|
||||||
|
buildTestRpcRequest(): string
|
||||||
|
/** Build JSON-RPC request to fetch current chain height */
|
||||||
|
buildChainHeightRequest(): string
|
||||||
|
/** Build JSON-RPC request to fetch current chain header (tip) */
|
||||||
|
buildChainHeaderRequest(): string
|
||||||
|
/** Get network information */
|
||||||
|
getNetworkInfo(): string
|
||||||
|
getChainHeightCall(rpcUrl: string): Promise<string>
|
||||||
|
/** Call node_getState to get server state information */
|
||||||
|
getStateCall(rpcUrl: string): Promise<string>
|
||||||
|
/** Call mempool_submitTransaction to broadcast a pre-built transaction */
|
||||||
|
submitTransactionCall(rpcUrl: string, transactionHex: string): Promise<string>
|
||||||
|
getUtxosFromViewKeyCall(rpcUrl: string, viewKeyHex: string, startBlock: number, maxSearchDepth?: number | undefined | null): Promise<string>
|
||||||
|
getArchivalMutatorSet(rpcUrl: string): Promise<string>
|
||||||
|
/**
|
||||||
|
* Build JSON-RPC request to find the canonical block that created a UTXO (by addition_record)
|
||||||
|
* Method: archival_getUtxoCreationBlock
|
||||||
|
*/
|
||||||
|
buildGetUtxoCreationBlockRequest(additionRecordHex: string, maxSearchDepth?: number | undefined | null): string
|
||||||
|
/** Perform JSON-RPC call to find the canonical block that created a UTXO (by addition_record) */
|
||||||
|
getUtxoCreationBlockCall(rpcUrl: string, additionRecordHex: string, maxSearchDepth?: number | undefined | null): Promise<string>
|
||||||
|
/** Call wallet_sendWithSpendingKey to build and broadcast transaction */
|
||||||
|
generateUtxoWithProofCall(rpcUrl: string, utxoHex: string, additionRecordHex: string, senderRandomnessHex: string, receiverPreimageHex: string, maxSearchDepth: string): Promise<string>
|
||||||
|
}
|
||||||
|
export declare class SimpleTransactionBuilder {
|
||||||
|
constructor()
|
||||||
|
buildTransaction(rpcUrl: string, spendingKeyHex: string, inputAdditionRecords: Array<string>, minBlockHeight: number, outputAddresses: Array<string>, outputAmounts: Array<string>, fee: string): Promise<string>
|
||||||
|
}
|
||||||
319
packages/neptune-native/index.js
Normal file
319
packages/neptune-native/index.js
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
|
||||||
|
/* auto-generated by NAPI-RS */
|
||||||
|
|
||||||
|
const { existsSync, readFileSync } = require('fs')
|
||||||
|
const { join } = require('path')
|
||||||
|
|
||||||
|
const { platform, arch } = process
|
||||||
|
|
||||||
|
let nativeBinding = null
|
||||||
|
let localFileExisted = false
|
||||||
|
let loadError = null
|
||||||
|
|
||||||
|
function isMusl() {
|
||||||
|
// For Node 10
|
||||||
|
if (!process.report || typeof process.report.getReport !== 'function') {
|
||||||
|
try {
|
||||||
|
const lddPath = require('child_process').execSync('which ldd').toString().trim()
|
||||||
|
return readFileSync(lddPath, 'utf8').includes('musl')
|
||||||
|
} catch (e) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const { glibcVersionRuntime } = process.report.getReport().header
|
||||||
|
return !glibcVersionRuntime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (platform) {
|
||||||
|
case 'android':
|
||||||
|
switch (arch) {
|
||||||
|
case 'arm64':
|
||||||
|
localFileExisted = existsSync(join(__dirname, 'neptune-native.android-arm64.node'))
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./neptune-native.android-arm64.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('neptune-native-android-arm64')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'arm':
|
||||||
|
localFileExisted = existsSync(join(__dirname, 'neptune-native.android-arm-eabi.node'))
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./neptune-native.android-arm-eabi.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('neptune-native-android-arm-eabi')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported architecture on Android ${arch}`)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'win32':
|
||||||
|
switch (arch) {
|
||||||
|
case 'x64':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'neptune-native.win32-x64-msvc.node')
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./neptune-native.win32-x64-msvc.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('neptune-native-win32-x64-msvc')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'ia32':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'neptune-native.win32-ia32-msvc.node')
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./neptune-native.win32-ia32-msvc.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('neptune-native-win32-ia32-msvc')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'arm64':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'neptune-native.win32-arm64-msvc.node')
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./neptune-native.win32-arm64-msvc.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('neptune-native-win32-arm64-msvc')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported architecture on Windows: ${arch}`)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'darwin':
|
||||||
|
localFileExisted = existsSync(join(__dirname, 'neptune-native.darwin-universal.node'))
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./neptune-native.darwin-universal.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('neptune-native-darwin-universal')
|
||||||
|
}
|
||||||
|
break
|
||||||
|
} catch {}
|
||||||
|
switch (arch) {
|
||||||
|
case 'x64':
|
||||||
|
localFileExisted = existsSync(join(__dirname, 'neptune-native.darwin-x64.node'))
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./neptune-native.darwin-x64.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('neptune-native-darwin-x64')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'arm64':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'neptune-native.darwin-arm64.node')
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./neptune-native.darwin-arm64.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('neptune-native-darwin-arm64')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported architecture on macOS: ${arch}`)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'freebsd':
|
||||||
|
if (arch !== 'x64') {
|
||||||
|
throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
|
||||||
|
}
|
||||||
|
localFileExisted = existsSync(join(__dirname, 'neptune-native.freebsd-x64.node'))
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./neptune-native.freebsd-x64.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('neptune-native-freebsd-x64')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'linux':
|
||||||
|
switch (arch) {
|
||||||
|
case 'x64':
|
||||||
|
if (isMusl()) {
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'neptune-native.linux-x64-musl.node')
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./neptune-native.linux-x64-musl.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('neptune-native-linux-x64-musl')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'neptune-native.linux-x64-gnu.node')
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./neptune-native.linux-x64-gnu.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('neptune-native-linux-x64-gnu')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'arm64':
|
||||||
|
if (isMusl()) {
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'neptune-native.linux-arm64-musl.node')
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./neptune-native.linux-arm64-musl.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('neptune-native-linux-arm64-musl')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'neptune-native.linux-arm64-gnu.node')
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./neptune-native.linux-arm64-gnu.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('neptune-native-linux-arm64-gnu')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'arm':
|
||||||
|
if (isMusl()) {
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'neptune-native.linux-arm-musleabihf.node')
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./neptune-native.linux-arm-musleabihf.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('neptune-native-linux-arm-musleabihf')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'neptune-native.linux-arm-gnueabihf.node')
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./neptune-native.linux-arm-gnueabihf.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('neptune-native-linux-arm-gnueabihf')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'riscv64':
|
||||||
|
if (isMusl()) {
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'neptune-native.linux-riscv64-musl.node')
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./neptune-native.linux-riscv64-musl.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('neptune-native-linux-riscv64-musl')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'neptune-native.linux-riscv64-gnu.node')
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./neptune-native.linux-riscv64-gnu.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('neptune-native-linux-riscv64-gnu')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 's390x':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'neptune-native.linux-s390x-gnu.node')
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./neptune-native.linux-s390x-gnu.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('neptune-native-linux-s390x-gnu')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported architecture on Linux: ${arch}`)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nativeBinding) {
|
||||||
|
if (loadError) {
|
||||||
|
throw loadError
|
||||||
|
}
|
||||||
|
throw new Error(`Failed to load native binding`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { initNativeModule, quickVmTest, getVersion, WalletManager, SimpleTransactionBuilder } = nativeBinding
|
||||||
|
|
||||||
|
module.exports.initNativeModule = initNativeModule
|
||||||
|
module.exports.quickVmTest = quickVmTest
|
||||||
|
module.exports.getVersion = getVersion
|
||||||
|
module.exports.WalletManager = WalletManager
|
||||||
|
module.exports.SimpleTransactionBuilder = SimpleTransactionBuilder
|
||||||
BIN
packages/neptune-native/neptune-native.darwin-arm64.node
Normal file
BIN
packages/neptune-native/neptune-native.darwin-arm64.node
Normal file
Binary file not shown.
BIN
packages/neptune-native/neptune-native.linux-x64-gnu.node
Normal file
BIN
packages/neptune-native/neptune-native.linux-x64-gnu.node
Normal file
Binary file not shown.
BIN
packages/neptune-native/neptune-native.win32-x64-msvc.node
Normal file
BIN
packages/neptune-native/neptune-native.win32-x64-msvc.node
Normal file
Binary file not shown.
45
packages/neptune-native/package.json
Normal file
45
packages/neptune-native/package.json
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"name": "@neptune/native",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Native Node.js addon for Neptune transaction building",
|
||||||
|
"main": "index.js",
|
||||||
|
"files": [
|
||||||
|
"index.js",
|
||||||
|
"index.d.ts",
|
||||||
|
"*.node"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "npx napi build --platform --release",
|
||||||
|
"build:debug": "npx napi build --platform",
|
||||||
|
"prepublishOnly": "napi prepublish -t npm",
|
||||||
|
"test": "cargo test",
|
||||||
|
"universal": "napi universal"
|
||||||
|
},
|
||||||
|
"napi": {
|
||||||
|
"name": "neptune-native",
|
||||||
|
"triples": {
|
||||||
|
"defaults": true,
|
||||||
|
"additional": [
|
||||||
|
"x86_64-unknown-linux-musl",
|
||||||
|
"aarch64-unknown-linux-gnu",
|
||||||
|
"aarch64-apple-darwin",
|
||||||
|
"aarch64-unknown-linux-musl"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@napi-rs/cli": "^2.18.4"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"neptune",
|
||||||
|
"blockchain",
|
||||||
|
"native",
|
||||||
|
"napi",
|
||||||
|
"vm",
|
||||||
|
"proof"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
}
|
||||||
|
}
|
||||||
0
packages/neptune-wasm/.gitignore
vendored
Normal file
0
packages/neptune-wasm/.gitignore
vendored
Normal file
209
packages/neptune-wasm/neptune_wasm.d.ts
vendored
Normal file
209
packages/neptune-wasm/neptune_wasm.d.ts
vendored
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
/**
|
||||||
|
* Generate a new random seed phrase (18 words, 192 bits entropy)
|
||||||
|
* This is completely offline - uses browser's crypto.getRandomValues()
|
||||||
|
*/
|
||||||
|
export function generate_seed(): string;
|
||||||
|
/**
|
||||||
|
* Get view key and address from seed phrase (offline) - COMPATIBLE WITH NEPTUNE-CORE
|
||||||
|
*
|
||||||
|
* This derives the view key from a BIP39 seed phrase using neptune-crypto-core.
|
||||||
|
* The view key can be used with wallet_getUtxosFromViewKey RPC method.
|
||||||
|
*
|
||||||
|
* Input: JSON string with seed_phrase array and network ("mainnet" or "testnet")
|
||||||
|
* Output: JSON string with receiver_identifier, view_key (hex), and address (bech32m)
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```javascript
|
||||||
|
* const result = get_viewkey('["word1", "word2", ...]', "testnet");
|
||||||
|
* const { receiver_identifier, view_key, address } = JSON.parse(result);
|
||||||
|
* console.log('View key:', view_key); // Compatible with neptune-core!
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function get_viewkey(seed_phrase_json: string, network: string): string;
|
||||||
|
/**
|
||||||
|
* Get receiving address from seed phrase (offline)
|
||||||
|
* Input: JSON string with seed_phrase array and network
|
||||||
|
* Output: bech32m encoded address string
|
||||||
|
*/
|
||||||
|
export function address_from_seed(seed_phrase_json: string, network: string): string;
|
||||||
|
/**
|
||||||
|
* Validate a seed phrase (offline)
|
||||||
|
*/
|
||||||
|
export function validate_seed_phrase(seed_phrase_json: string): boolean;
|
||||||
|
/**
|
||||||
|
* Decode view key from hex string (offline)
|
||||||
|
*/
|
||||||
|
export function decode_viewkey(view_key_hex: string): string;
|
||||||
|
/**
|
||||||
|
* Prepare transaction data for server-side signing
|
||||||
|
*
|
||||||
|
* This prepares transaction details but does NOT sign locally.
|
||||||
|
* Instead, it exports the spending key seed so the server can sign.
|
||||||
|
*
|
||||||
|
* Input: JSON string with BuildTxRequest
|
||||||
|
* Output: JSON string with SignedTxData (contains seed for server signing)
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```javascript
|
||||||
|
* const request = {
|
||||||
|
* seed_phrase: ["word1", "word2", ...],
|
||||||
|
* inputs: [{addition_record: "0xabc..."}],
|
||||||
|
* outputs: [{address: "nep1...", amount: "50.0"}],
|
||||||
|
* fee: "0.01",
|
||||||
|
* network: "testnet"
|
||||||
|
* };
|
||||||
|
* const txData = build_and_sign_tx(JSON.stringify(request));
|
||||||
|
* // Now broadcast via JSON-RPC: wallet_broadcastSignedTransaction(txData)
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function build_and_sign_tx(request_json: string): string;
|
||||||
|
/**
|
||||||
|
* Get wallet balance via JSON-RPC (Neptune core ext format)
|
||||||
|
* Uses wallet_balance method from Wallet namespace
|
||||||
|
*/
|
||||||
|
export function get_balance(rpc_url: string): Promise<string>;
|
||||||
|
/**
|
||||||
|
* Send transaction via JSON-RPC
|
||||||
|
* Note: Neptune core ext may not have direct "send" method in public RPC
|
||||||
|
* This is a placeholder - check actual available methods
|
||||||
|
*/
|
||||||
|
export function send_tx_jsonrpc(rpc_url: string, to_address: string, amount: string, fee: string): Promise<string>;
|
||||||
|
/**
|
||||||
|
* Get block height via JSON-RPC (Neptune core ext format)
|
||||||
|
* Uses chain_height method from Chain namespace
|
||||||
|
*/
|
||||||
|
export function get_block_height(rpc_url: string): Promise<bigint>;
|
||||||
|
/**
|
||||||
|
* Get network info via JSON-RPC (Neptune core ext format)
|
||||||
|
* Uses node_network method from Node namespace
|
||||||
|
*/
|
||||||
|
export function get_network_info(rpc_url: string): Promise<string>;
|
||||||
|
/**
|
||||||
|
* Get wallet addresses from Neptune core (PRODUCTION - EXACT Neptune addresses!)
|
||||||
|
* Returns both generation_address and symmetric_address
|
||||||
|
*/
|
||||||
|
export function get_wallet_address(rpc_url: string): Promise<string>;
|
||||||
|
/**
|
||||||
|
* Get view key from Neptune core (RECOMMENDED)
|
||||||
|
* This exports the proper view key format from Neptune core's wallet
|
||||||
|
* Returns a hex-encoded view key that can be used with wallet_getUtxosFromViewKey
|
||||||
|
*/
|
||||||
|
export function get_viewkey_from_neptune(rpc_url: string): Promise<string>;
|
||||||
|
/**
|
||||||
|
* Get UTXOs from view key (scan blockchain)
|
||||||
|
* This calls Neptune core's wallet_getUtxosFromViewKey JSON-RPC method
|
||||||
|
*
|
||||||
|
* Input: view_key (hex string from neptune-core format), start_block, end_block (optional)
|
||||||
|
* Output: JSON string with list of UTXOs
|
||||||
|
*/
|
||||||
|
export function get_utxos_from_viewkey(rpc_url: string, view_key_hex: string, start_block: bigint, end_block?: bigint | null): Promise<string>;
|
||||||
|
/**
|
||||||
|
* Generate viewkey from BIP39 seed phrase
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
* * `phrase` - Space-separated BIP39 seed phrase (12-24 words)
|
||||||
|
* * `key_index` - Key derivation index (default: 0)
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* Hex-encoded viewkey compatible with neptune-core
|
||||||
|
*/
|
||||||
|
export function generate_viewkey_from_phrase(phrase: string, key_index: bigint): string;
|
||||||
|
/**
|
||||||
|
* Generate receiving address from BIP39 seed phrase
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
* * `phrase` - Space-separated BIP39 seed phrase (12-24 words)
|
||||||
|
* * `key_index` - Key derivation index (default: 0)
|
||||||
|
* * `testnet` - Use testnet network (true) or mainnet (false)
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* Bech32m-encoded receiving address
|
||||||
|
*/
|
||||||
|
export function generate_address_from_phrase(phrase: string, key_index: bigint, testnet: boolean): string;
|
||||||
|
/**
|
||||||
|
* Get receiver identifier from viewkey hex
|
||||||
|
*/
|
||||||
|
export function get_receiver_id_from_viewkey(viewkey_hex: string): string;
|
||||||
|
/**
|
||||||
|
* Debug: Get detailed key derivation info
|
||||||
|
*/
|
||||||
|
export function debug_key_derivation(phrase: string, key_index: bigint): string;
|
||||||
|
/**
|
||||||
|
* Sign transaction offline (WASM client-side signing)
|
||||||
|
* This creates lock_script_and_witness WITHOUT exposing spending key to server
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
* * `phrase` - BIP39 seed phrase (18 words)
|
||||||
|
* * `utxos_json` - JSON array of UTXOs from get_utxos (with addition_record)
|
||||||
|
* * `outputs_json` - JSON array of outputs [{address, amount}, ...]
|
||||||
|
* * `fee` - Transaction fee as string
|
||||||
|
* * `key_index` - Key derivation index (usually 0)
|
||||||
|
* * `testnet` - Network selection
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* JSON containing:
|
||||||
|
* - lock_script_and_witness: hex-encoded signature
|
||||||
|
* - view_key: hex-encoded view key
|
||||||
|
* - inputs: array of addition_records
|
||||||
|
* - outputs: array of {address, amount}
|
||||||
|
* - fee: fee string
|
||||||
|
*/
|
||||||
|
export function sign_transaction_offline(phrase: string, utxos_json: string, outputs_json: string, fee: string, key_index: bigint, testnet: boolean): string;
|
||||||
|
|
||||||
|
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
||||||
|
|
||||||
|
export interface InitOutput {
|
||||||
|
readonly memory: WebAssembly.Memory;
|
||||||
|
readonly generate_seed: () => [number, number, number, number];
|
||||||
|
readonly get_viewkey: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
||||||
|
readonly address_from_seed: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
||||||
|
readonly validate_seed_phrase: (a: number, b: number) => [number, number, number];
|
||||||
|
readonly decode_viewkey: (a: number, b: number) => [number, number, number, number];
|
||||||
|
readonly build_and_sign_tx: (a: number, b: number) => [number, number, number, number];
|
||||||
|
readonly get_balance: (a: number, b: number) => any;
|
||||||
|
readonly send_tx_jsonrpc: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => any;
|
||||||
|
readonly get_block_height: (a: number, b: number) => any;
|
||||||
|
readonly get_network_info: (a: number, b: number) => any;
|
||||||
|
readonly get_wallet_address: (a: number, b: number) => any;
|
||||||
|
readonly get_viewkey_from_neptune: (a: number, b: number) => any;
|
||||||
|
readonly get_utxos_from_viewkey: (a: number, b: number, c: number, d: number, e: bigint, f: number, g: bigint) => any;
|
||||||
|
readonly generate_viewkey_from_phrase: (a: number, b: number, c: bigint) => [number, number, number, number];
|
||||||
|
readonly generate_address_from_phrase: (a: number, b: number, c: bigint, d: number) => [number, number, number, number];
|
||||||
|
readonly get_receiver_id_from_viewkey: (a: number, b: number) => [number, number, number, number];
|
||||||
|
readonly debug_key_derivation: (a: number, b: number, c: bigint) => [number, number, number, number];
|
||||||
|
readonly sign_transaction_offline: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: bigint, j: number) => [number, number, number, number];
|
||||||
|
readonly __wbindgen_exn_store: (a: number) => void;
|
||||||
|
readonly __externref_table_alloc: () => number;
|
||||||
|
readonly __wbindgen_export_2: WebAssembly.Table;
|
||||||
|
readonly __wbindgen_export_3: WebAssembly.Table;
|
||||||
|
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
||||||
|
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
||||||
|
readonly __externref_table_dealloc: (a: number) => void;
|
||||||
|
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
|
||||||
|
readonly closure49_externref_shim: (a: number, b: number, c: any) => void;
|
||||||
|
readonly closure268_externref_shim: (a: number, b: number, c: any, d: any) => void;
|
||||||
|
readonly __wbindgen_start: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SyncInitInput = BufferSource | WebAssembly.Module;
|
||||||
|
/**
|
||||||
|
* Instantiates the given `module`, which can either be bytes or
|
||||||
|
* a precompiled `WebAssembly.Module`.
|
||||||
|
*
|
||||||
|
* @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
|
||||||
|
*
|
||||||
|
* @returns {InitOutput}
|
||||||
|
*/
|
||||||
|
export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
|
||||||
|
* for everything else, calls `WebAssembly.instantiate` directly.
|
||||||
|
*
|
||||||
|
* @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
|
||||||
|
*
|
||||||
|
* @returns {Promise<InitOutput>}
|
||||||
|
*/
|
||||||
|
export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;
|
||||||
984
packages/neptune-wasm/neptune_wasm.js
Normal file
984
packages/neptune-wasm/neptune_wasm.js
Normal file
@ -0,0 +1,984 @@
|
|||||||
|
let wasm;
|
||||||
|
|
||||||
|
function addToExternrefTable0(obj) {
|
||||||
|
const idx = wasm.__externref_table_alloc();
|
||||||
|
wasm.__wbindgen_export_2.set(idx, obj);
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleError(f, args) {
|
||||||
|
try {
|
||||||
|
return f.apply(this, args);
|
||||||
|
} catch (e) {
|
||||||
|
const idx = addToExternrefTable0(e);
|
||||||
|
wasm.__wbindgen_exn_store(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cachedUint8ArrayMemory0 = null;
|
||||||
|
|
||||||
|
function getUint8ArrayMemory0() {
|
||||||
|
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
|
||||||
|
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
|
||||||
|
}
|
||||||
|
return cachedUint8ArrayMemory0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getArrayU8FromWasm0(ptr, len) {
|
||||||
|
ptr = ptr >>> 0;
|
||||||
|
return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
|
||||||
|
|
||||||
|
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
|
||||||
|
|
||||||
|
function getStringFromWasm0(ptr, len) {
|
||||||
|
ptr = ptr >>> 0;
|
||||||
|
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLikeNone(x) {
|
||||||
|
return x === undefined || x === null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined')
|
||||||
|
? { register: () => {}, unregister: () => {} }
|
||||||
|
: new FinalizationRegistry(state => {
|
||||||
|
wasm.__wbindgen_export_3.get(state.dtor)(state.a, state.b)
|
||||||
|
});
|
||||||
|
|
||||||
|
function makeMutClosure(arg0, arg1, dtor, f) {
|
||||||
|
const state = { a: arg0, b: arg1, cnt: 1, dtor };
|
||||||
|
const real = (...args) => {
|
||||||
|
// First up with a closure we increment the internal reference
|
||||||
|
// count. This ensures that the Rust closure environment won't
|
||||||
|
// be deallocated while we're invoking it.
|
||||||
|
state.cnt++;
|
||||||
|
const a = state.a;
|
||||||
|
state.a = 0;
|
||||||
|
try {
|
||||||
|
return f(a, state.b, ...args);
|
||||||
|
} finally {
|
||||||
|
if (--state.cnt === 0) {
|
||||||
|
wasm.__wbindgen_export_3.get(state.dtor)(a, state.b);
|
||||||
|
CLOSURE_DTORS.unregister(state);
|
||||||
|
} else {
|
||||||
|
state.a = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
real.original = state;
|
||||||
|
CLOSURE_DTORS.register(real, state, state);
|
||||||
|
return real;
|
||||||
|
}
|
||||||
|
|
||||||
|
function debugString(val) {
|
||||||
|
// primitive types
|
||||||
|
const type = typeof val;
|
||||||
|
if (type == 'number' || type == 'boolean' || val == null) {
|
||||||
|
return `${val}`;
|
||||||
|
}
|
||||||
|
if (type == 'string') {
|
||||||
|
return `"${val}"`;
|
||||||
|
}
|
||||||
|
if (type == 'symbol') {
|
||||||
|
const description = val.description;
|
||||||
|
if (description == null) {
|
||||||
|
return 'Symbol';
|
||||||
|
} else {
|
||||||
|
return `Symbol(${description})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type == 'function') {
|
||||||
|
const name = val.name;
|
||||||
|
if (typeof name == 'string' && name.length > 0) {
|
||||||
|
return `Function(${name})`;
|
||||||
|
} else {
|
||||||
|
return 'Function';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// objects
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
const length = val.length;
|
||||||
|
let debug = '[';
|
||||||
|
if (length > 0) {
|
||||||
|
debug += debugString(val[0]);
|
||||||
|
}
|
||||||
|
for(let i = 1; i < length; i++) {
|
||||||
|
debug += ', ' + debugString(val[i]);
|
||||||
|
}
|
||||||
|
debug += ']';
|
||||||
|
return debug;
|
||||||
|
}
|
||||||
|
// Test for built-in
|
||||||
|
const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
|
||||||
|
let className;
|
||||||
|
if (builtInMatches && builtInMatches.length > 1) {
|
||||||
|
className = builtInMatches[1];
|
||||||
|
} else {
|
||||||
|
// Failed to match the standard '[object ClassName]'
|
||||||
|
return toString.call(val);
|
||||||
|
}
|
||||||
|
if (className == 'Object') {
|
||||||
|
// we're a user defined class or Object
|
||||||
|
// JSON.stringify avoids problems with cycles, and is generally much
|
||||||
|
// easier than looping through ownProperties of `val`.
|
||||||
|
try {
|
||||||
|
return 'Object(' + JSON.stringify(val) + ')';
|
||||||
|
} catch (_) {
|
||||||
|
return 'Object';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// errors
|
||||||
|
if (val instanceof Error) {
|
||||||
|
return `${val.name}: ${val.message}\n${val.stack}`;
|
||||||
|
}
|
||||||
|
// TODO we could test for more things here, like `Set`s and `Map`s.
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
|
||||||
|
let WASM_VECTOR_LEN = 0;
|
||||||
|
|
||||||
|
const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } );
|
||||||
|
|
||||||
|
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
|
||||||
|
? function (arg, view) {
|
||||||
|
return cachedTextEncoder.encodeInto(arg, view);
|
||||||
|
}
|
||||||
|
: function (arg, view) {
|
||||||
|
const buf = cachedTextEncoder.encode(arg);
|
||||||
|
view.set(buf);
|
||||||
|
return {
|
||||||
|
read: arg.length,
|
||||||
|
written: buf.length
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function passStringToWasm0(arg, malloc, realloc) {
|
||||||
|
|
||||||
|
if (realloc === undefined) {
|
||||||
|
const buf = cachedTextEncoder.encode(arg);
|
||||||
|
const ptr = malloc(buf.length, 1) >>> 0;
|
||||||
|
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
|
||||||
|
WASM_VECTOR_LEN = buf.length;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
let len = arg.length;
|
||||||
|
let ptr = malloc(len, 1) >>> 0;
|
||||||
|
|
||||||
|
const mem = getUint8ArrayMemory0();
|
||||||
|
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
|
for (; offset < len; offset++) {
|
||||||
|
const code = arg.charCodeAt(offset);
|
||||||
|
if (code > 0x7F) break;
|
||||||
|
mem[ptr + offset] = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset !== len) {
|
||||||
|
if (offset !== 0) {
|
||||||
|
arg = arg.slice(offset);
|
||||||
|
}
|
||||||
|
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
|
||||||
|
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
|
||||||
|
const ret = encodeString(arg, view);
|
||||||
|
|
||||||
|
offset += ret.written;
|
||||||
|
ptr = realloc(ptr, len, offset, 1) >>> 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
WASM_VECTOR_LEN = offset;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cachedDataViewMemory0 = null;
|
||||||
|
|
||||||
|
function getDataViewMemory0() {
|
||||||
|
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
|
||||||
|
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
|
||||||
|
}
|
||||||
|
return cachedDataViewMemory0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function takeFromExternrefTable0(idx) {
|
||||||
|
const value = wasm.__wbindgen_export_2.get(idx);
|
||||||
|
wasm.__externref_table_dealloc(idx);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Generate a new random seed phrase (18 words, 192 bits entropy)
|
||||||
|
* This is completely offline - uses browser's crypto.getRandomValues()
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function generate_seed() {
|
||||||
|
let deferred2_0;
|
||||||
|
let deferred2_1;
|
||||||
|
try {
|
||||||
|
const ret = wasm.generate_seed();
|
||||||
|
var ptr1 = ret[0];
|
||||||
|
var len1 = ret[1];
|
||||||
|
if (ret[3]) {
|
||||||
|
ptr1 = 0; len1 = 0;
|
||||||
|
throw takeFromExternrefTable0(ret[2]);
|
||||||
|
}
|
||||||
|
deferred2_0 = ptr1;
|
||||||
|
deferred2_1 = len1;
|
||||||
|
return getStringFromWasm0(ptr1, len1);
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get view key and address from seed phrase (offline) - COMPATIBLE WITH NEPTUNE-CORE
|
||||||
|
*
|
||||||
|
* This derives the view key from a BIP39 seed phrase using neptune-crypto-core.
|
||||||
|
* The view key can be used with wallet_getUtxosFromViewKey RPC method.
|
||||||
|
*
|
||||||
|
* Input: JSON string with seed_phrase array and network ("mainnet" or "testnet")
|
||||||
|
* Output: JSON string with receiver_identifier, view_key (hex), and address (bech32m)
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```javascript
|
||||||
|
* const result = get_viewkey('["word1", "word2", ...]', "testnet");
|
||||||
|
* const { receiver_identifier, view_key, address } = JSON.parse(result);
|
||||||
|
* console.log('View key:', view_key); // Compatible with neptune-core!
|
||||||
|
* ```
|
||||||
|
* @param {string} seed_phrase_json
|
||||||
|
* @param {string} network
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function get_viewkey(seed_phrase_json, network) {
|
||||||
|
let deferred4_0;
|
||||||
|
let deferred4_1;
|
||||||
|
try {
|
||||||
|
const ptr0 = passStringToWasm0(seed_phrase_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ptr1 = passStringToWasm0(network, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len1 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.get_viewkey(ptr0, len0, ptr1, len1);
|
||||||
|
var ptr3 = ret[0];
|
||||||
|
var len3 = ret[1];
|
||||||
|
if (ret[3]) {
|
||||||
|
ptr3 = 0; len3 = 0;
|
||||||
|
throw takeFromExternrefTable0(ret[2]);
|
||||||
|
}
|
||||||
|
deferred4_0 = ptr3;
|
||||||
|
deferred4_1 = len3;
|
||||||
|
return getStringFromWasm0(ptr3, len3);
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get receiving address from seed phrase (offline)
|
||||||
|
* Input: JSON string with seed_phrase array and network
|
||||||
|
* Output: bech32m encoded address string
|
||||||
|
* @param {string} seed_phrase_json
|
||||||
|
* @param {string} network
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function address_from_seed(seed_phrase_json, network) {
|
||||||
|
let deferred4_0;
|
||||||
|
let deferred4_1;
|
||||||
|
try {
|
||||||
|
const ptr0 = passStringToWasm0(seed_phrase_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ptr1 = passStringToWasm0(network, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len1 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.address_from_seed(ptr0, len0, ptr1, len1);
|
||||||
|
var ptr3 = ret[0];
|
||||||
|
var len3 = ret[1];
|
||||||
|
if (ret[3]) {
|
||||||
|
ptr3 = 0; len3 = 0;
|
||||||
|
throw takeFromExternrefTable0(ret[2]);
|
||||||
|
}
|
||||||
|
deferred4_0 = ptr3;
|
||||||
|
deferred4_1 = len3;
|
||||||
|
return getStringFromWasm0(ptr3, len3);
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate a seed phrase (offline)
|
||||||
|
* @param {string} seed_phrase_json
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export function validate_seed_phrase(seed_phrase_json) {
|
||||||
|
const ptr0 = passStringToWasm0(seed_phrase_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.validate_seed_phrase(ptr0, len0);
|
||||||
|
if (ret[2]) {
|
||||||
|
throw takeFromExternrefTable0(ret[1]);
|
||||||
|
}
|
||||||
|
return ret[0] !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode view key from hex string (offline)
|
||||||
|
* @param {string} view_key_hex
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function decode_viewkey(view_key_hex) {
|
||||||
|
let deferred3_0;
|
||||||
|
let deferred3_1;
|
||||||
|
try {
|
||||||
|
const ptr0 = passStringToWasm0(view_key_hex, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.decode_viewkey(ptr0, len0);
|
||||||
|
var ptr2 = ret[0];
|
||||||
|
var len2 = ret[1];
|
||||||
|
if (ret[3]) {
|
||||||
|
ptr2 = 0; len2 = 0;
|
||||||
|
throw takeFromExternrefTable0(ret[2]);
|
||||||
|
}
|
||||||
|
deferred3_0 = ptr2;
|
||||||
|
deferred3_1 = len2;
|
||||||
|
return getStringFromWasm0(ptr2, len2);
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare transaction data for server-side signing
|
||||||
|
*
|
||||||
|
* This prepares transaction details but does NOT sign locally.
|
||||||
|
* Instead, it exports the spending key seed so the server can sign.
|
||||||
|
*
|
||||||
|
* Input: JSON string with BuildTxRequest
|
||||||
|
* Output: JSON string with SignedTxData (contains seed for server signing)
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```javascript
|
||||||
|
* const request = {
|
||||||
|
* seed_phrase: ["word1", "word2", ...],
|
||||||
|
* inputs: [{addition_record: "0xabc..."}],
|
||||||
|
* outputs: [{address: "nep1...", amount: "50.0"}],
|
||||||
|
* fee: "0.01",
|
||||||
|
* network: "testnet"
|
||||||
|
* };
|
||||||
|
* const txData = build_and_sign_tx(JSON.stringify(request));
|
||||||
|
* // Now broadcast via JSON-RPC: wallet_broadcastSignedTransaction(txData)
|
||||||
|
* ```
|
||||||
|
* @param {string} request_json
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function build_and_sign_tx(request_json) {
|
||||||
|
let deferred3_0;
|
||||||
|
let deferred3_1;
|
||||||
|
try {
|
||||||
|
const ptr0 = passStringToWasm0(request_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.build_and_sign_tx(ptr0, len0);
|
||||||
|
var ptr2 = ret[0];
|
||||||
|
var len2 = ret[1];
|
||||||
|
if (ret[3]) {
|
||||||
|
ptr2 = 0; len2 = 0;
|
||||||
|
throw takeFromExternrefTable0(ret[2]);
|
||||||
|
}
|
||||||
|
deferred3_0 = ptr2;
|
||||||
|
deferred3_1 = len2;
|
||||||
|
return getStringFromWasm0(ptr2, len2);
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get wallet balance via JSON-RPC (Neptune core ext format)
|
||||||
|
* Uses wallet_balance method from Wallet namespace
|
||||||
|
* @param {string} rpc_url
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
export function get_balance(rpc_url) {
|
||||||
|
const ptr0 = passStringToWasm0(rpc_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.get_balance(ptr0, len0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send transaction via JSON-RPC
|
||||||
|
* Note: Neptune core ext may not have direct "send" method in public RPC
|
||||||
|
* This is a placeholder - check actual available methods
|
||||||
|
* @param {string} rpc_url
|
||||||
|
* @param {string} to_address
|
||||||
|
* @param {string} amount
|
||||||
|
* @param {string} fee
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
export function send_tx_jsonrpc(rpc_url, to_address, amount, fee) {
|
||||||
|
const ptr0 = passStringToWasm0(rpc_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ptr1 = passStringToWasm0(to_address, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len1 = WASM_VECTOR_LEN;
|
||||||
|
const ptr2 = passStringToWasm0(amount, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len2 = WASM_VECTOR_LEN;
|
||||||
|
const ptr3 = passStringToWasm0(fee, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len3 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.send_tx_jsonrpc(ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get block height via JSON-RPC (Neptune core ext format)
|
||||||
|
* Uses chain_height method from Chain namespace
|
||||||
|
* @param {string} rpc_url
|
||||||
|
* @returns {Promise<bigint>}
|
||||||
|
*/
|
||||||
|
export function get_block_height(rpc_url) {
|
||||||
|
const ptr0 = passStringToWasm0(rpc_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.get_block_height(ptr0, len0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get network info via JSON-RPC (Neptune core ext format)
|
||||||
|
* Uses node_network method from Node namespace
|
||||||
|
* @param {string} rpc_url
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
export function get_network_info(rpc_url) {
|
||||||
|
const ptr0 = passStringToWasm0(rpc_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.get_network_info(ptr0, len0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get wallet addresses from Neptune core (PRODUCTION - EXACT Neptune addresses!)
|
||||||
|
* Returns both generation_address and symmetric_address
|
||||||
|
* @param {string} rpc_url
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
export function get_wallet_address(rpc_url) {
|
||||||
|
const ptr0 = passStringToWasm0(rpc_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.get_wallet_address(ptr0, len0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get view key from Neptune core (RECOMMENDED)
|
||||||
|
* This exports the proper view key format from Neptune core's wallet
|
||||||
|
* Returns a hex-encoded view key that can be used with wallet_getUtxosFromViewKey
|
||||||
|
* @param {string} rpc_url
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
export function get_viewkey_from_neptune(rpc_url) {
|
||||||
|
const ptr0 = passStringToWasm0(rpc_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.get_viewkey_from_neptune(ptr0, len0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get UTXOs from view key (scan blockchain)
|
||||||
|
* This calls Neptune core's wallet_getUtxosFromViewKey JSON-RPC method
|
||||||
|
*
|
||||||
|
* Input: view_key (hex string from neptune-core format), start_block, end_block (optional)
|
||||||
|
* Output: JSON string with list of UTXOs
|
||||||
|
* @param {string} rpc_url
|
||||||
|
* @param {string} view_key_hex
|
||||||
|
* @param {bigint} start_block
|
||||||
|
* @param {bigint | null} [end_block]
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
export function get_utxos_from_viewkey(rpc_url, view_key_hex, start_block, end_block) {
|
||||||
|
const ptr0 = passStringToWasm0(rpc_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ptr1 = passStringToWasm0(view_key_hex, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len1 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.get_utxos_from_viewkey(ptr0, len0, ptr1, len1, start_block, !isLikeNone(end_block), isLikeNone(end_block) ? BigInt(0) : end_block);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate viewkey from BIP39 seed phrase
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
* * `phrase` - Space-separated BIP39 seed phrase (12-24 words)
|
||||||
|
* * `key_index` - Key derivation index (default: 0)
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* Hex-encoded viewkey compatible with neptune-core
|
||||||
|
* @param {string} phrase
|
||||||
|
* @param {bigint} key_index
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function generate_viewkey_from_phrase(phrase, key_index) {
|
||||||
|
let deferred3_0;
|
||||||
|
let deferred3_1;
|
||||||
|
try {
|
||||||
|
const ptr0 = passStringToWasm0(phrase, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.generate_viewkey_from_phrase(ptr0, len0, key_index);
|
||||||
|
var ptr2 = ret[0];
|
||||||
|
var len2 = ret[1];
|
||||||
|
if (ret[3]) {
|
||||||
|
ptr2 = 0; len2 = 0;
|
||||||
|
throw takeFromExternrefTable0(ret[2]);
|
||||||
|
}
|
||||||
|
deferred3_0 = ptr2;
|
||||||
|
deferred3_1 = len2;
|
||||||
|
return getStringFromWasm0(ptr2, len2);
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate receiving address from BIP39 seed phrase
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
* * `phrase` - Space-separated BIP39 seed phrase (12-24 words)
|
||||||
|
* * `key_index` - Key derivation index (default: 0)
|
||||||
|
* * `testnet` - Use testnet network (true) or mainnet (false)
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* Bech32m-encoded receiving address
|
||||||
|
* @param {string} phrase
|
||||||
|
* @param {bigint} key_index
|
||||||
|
* @param {boolean} testnet
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function generate_address_from_phrase(phrase, key_index, testnet) {
|
||||||
|
let deferred3_0;
|
||||||
|
let deferred3_1;
|
||||||
|
try {
|
||||||
|
const ptr0 = passStringToWasm0(phrase, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.generate_address_from_phrase(ptr0, len0, key_index, testnet);
|
||||||
|
var ptr2 = ret[0];
|
||||||
|
var len2 = ret[1];
|
||||||
|
if (ret[3]) {
|
||||||
|
ptr2 = 0; len2 = 0;
|
||||||
|
throw takeFromExternrefTable0(ret[2]);
|
||||||
|
}
|
||||||
|
deferred3_0 = ptr2;
|
||||||
|
deferred3_1 = len2;
|
||||||
|
return getStringFromWasm0(ptr2, len2);
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get receiver identifier from viewkey hex
|
||||||
|
* @param {string} viewkey_hex
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function get_receiver_id_from_viewkey(viewkey_hex) {
|
||||||
|
let deferred3_0;
|
||||||
|
let deferred3_1;
|
||||||
|
try {
|
||||||
|
const ptr0 = passStringToWasm0(viewkey_hex, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.get_receiver_id_from_viewkey(ptr0, len0);
|
||||||
|
var ptr2 = ret[0];
|
||||||
|
var len2 = ret[1];
|
||||||
|
if (ret[3]) {
|
||||||
|
ptr2 = 0; len2 = 0;
|
||||||
|
throw takeFromExternrefTable0(ret[2]);
|
||||||
|
}
|
||||||
|
deferred3_0 = ptr2;
|
||||||
|
deferred3_1 = len2;
|
||||||
|
return getStringFromWasm0(ptr2, len2);
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug: Get detailed key derivation info
|
||||||
|
* @param {string} phrase
|
||||||
|
* @param {bigint} key_index
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function debug_key_derivation(phrase, key_index) {
|
||||||
|
let deferred3_0;
|
||||||
|
let deferred3_1;
|
||||||
|
try {
|
||||||
|
const ptr0 = passStringToWasm0(phrase, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.debug_key_derivation(ptr0, len0, key_index);
|
||||||
|
var ptr2 = ret[0];
|
||||||
|
var len2 = ret[1];
|
||||||
|
if (ret[3]) {
|
||||||
|
ptr2 = 0; len2 = 0;
|
||||||
|
throw takeFromExternrefTable0(ret[2]);
|
||||||
|
}
|
||||||
|
deferred3_0 = ptr2;
|
||||||
|
deferred3_1 = len2;
|
||||||
|
return getStringFromWasm0(ptr2, len2);
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign transaction offline (WASM client-side signing)
|
||||||
|
* This creates lock_script_and_witness WITHOUT exposing spending key to server
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
* * `phrase` - BIP39 seed phrase (18 words)
|
||||||
|
* * `utxos_json` - JSON array of UTXOs from get_utxos (with addition_record)
|
||||||
|
* * `outputs_json` - JSON array of outputs [{address, amount}, ...]
|
||||||
|
* * `fee` - Transaction fee as string
|
||||||
|
* * `key_index` - Key derivation index (usually 0)
|
||||||
|
* * `testnet` - Network selection
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
* JSON containing:
|
||||||
|
* - lock_script_and_witness: hex-encoded signature
|
||||||
|
* - view_key: hex-encoded view key
|
||||||
|
* - inputs: array of addition_records
|
||||||
|
* - outputs: array of {address, amount}
|
||||||
|
* - fee: fee string
|
||||||
|
* @param {string} phrase
|
||||||
|
* @param {string} utxos_json
|
||||||
|
* @param {string} outputs_json
|
||||||
|
* @param {string} fee
|
||||||
|
* @param {bigint} key_index
|
||||||
|
* @param {boolean} testnet
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function sign_transaction_offline(phrase, utxos_json, outputs_json, fee, key_index, testnet) {
|
||||||
|
let deferred6_0;
|
||||||
|
let deferred6_1;
|
||||||
|
try {
|
||||||
|
const ptr0 = passStringToWasm0(phrase, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len0 = WASM_VECTOR_LEN;
|
||||||
|
const ptr1 = passStringToWasm0(utxos_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len1 = WASM_VECTOR_LEN;
|
||||||
|
const ptr2 = passStringToWasm0(outputs_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len2 = WASM_VECTOR_LEN;
|
||||||
|
const ptr3 = passStringToWasm0(fee, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len3 = WASM_VECTOR_LEN;
|
||||||
|
const ret = wasm.sign_transaction_offline(ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, key_index, testnet);
|
||||||
|
var ptr5 = ret[0];
|
||||||
|
var len5 = ret[1];
|
||||||
|
if (ret[3]) {
|
||||||
|
ptr5 = 0; len5 = 0;
|
||||||
|
throw takeFromExternrefTable0(ret[2]);
|
||||||
|
}
|
||||||
|
deferred6_0 = ptr5;
|
||||||
|
deferred6_1 = len5;
|
||||||
|
return getStringFromWasm0(ptr5, len5);
|
||||||
|
} finally {
|
||||||
|
wasm.__wbindgen_free(deferred6_0, deferred6_1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function __wbg_adapter_24(arg0, arg1, arg2) {
|
||||||
|
wasm.closure49_externref_shim(arg0, arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function __wbg_adapter_83(arg0, arg1, arg2, arg3) {
|
||||||
|
wasm.closure268_externref_shim(arg0, arg1, arg2, arg3);
|
||||||
|
}
|
||||||
|
|
||||||
|
const __wbindgen_enum_RequestMode = ["same-origin", "no-cors", "cors", "navigate"];
|
||||||
|
|
||||||
|
async function __wbg_load(module, imports) {
|
||||||
|
if (typeof Response === 'function' && module instanceof Response) {
|
||||||
|
if (typeof WebAssembly.instantiateStreaming === 'function') {
|
||||||
|
try {
|
||||||
|
return await WebAssembly.instantiateStreaming(module, imports);
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
if (module.headers.get('Content-Type') != 'application/wasm') {
|
||||||
|
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bytes = await module.arrayBuffer();
|
||||||
|
return await WebAssembly.instantiate(bytes, imports);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const instance = await WebAssembly.instantiate(module, imports);
|
||||||
|
|
||||||
|
if (instance instanceof WebAssembly.Instance) {
|
||||||
|
return { instance, module };
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function __wbg_get_imports() {
|
||||||
|
const imports = {};
|
||||||
|
imports.wbg = {};
|
||||||
|
imports.wbg.__wbg_call_672a4d21634d4a24 = function() { return handleError(function (arg0, arg1) {
|
||||||
|
const ret = arg0.call(arg1);
|
||||||
|
return ret;
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_call_7cccdd69e0791ae2 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||||
|
const ret = arg0.call(arg1, arg2);
|
||||||
|
return ret;
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_error_524f506f44df1645 = function(arg0) {
|
||||||
|
console.error(arg0);
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_fetch_b7bf320f681242d2 = function(arg0, arg1) {
|
||||||
|
const ret = arg0.fetch(arg1);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_getRandomValues_bb689d73e9ab7af6 = function(arg0, arg1) {
|
||||||
|
crypto.getRandomValues(getArrayU8FromWasm0(arg0, arg1));
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_headers_7852a8ea641c1379 = function(arg0) {
|
||||||
|
const ret = arg0.headers;
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_instanceof_Response_f2cc20d9f7dfd644 = function(arg0) {
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
result = arg0 instanceof Response;
|
||||||
|
} catch (_) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
const ret = result;
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_instanceof_Window_def73ea0955fc569 = function(arg0) {
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
result = arg0 instanceof Window;
|
||||||
|
} catch (_) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
const ret = result;
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_log_7fbbb36c3875e666 = function(arg0, arg1) {
|
||||||
|
console.log(getStringFromWasm0(arg0, arg1));
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_log_c222819a41e063d3 = function(arg0) {
|
||||||
|
console.log(arg0);
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_new_23a2665fac83c611 = function(arg0, arg1) {
|
||||||
|
try {
|
||||||
|
var state0 = {a: arg0, b: arg1};
|
||||||
|
var cb0 = (arg0, arg1) => {
|
||||||
|
const a = state0.a;
|
||||||
|
state0.a = 0;
|
||||||
|
try {
|
||||||
|
return __wbg_adapter_83(a, state0.b, arg0, arg1);
|
||||||
|
} finally {
|
||||||
|
state0.a = a;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const ret = new Promise(cb0);
|
||||||
|
return ret;
|
||||||
|
} finally {
|
||||||
|
state0.a = state0.b = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_new_405e22f390576ce2 = function() {
|
||||||
|
const ret = new Object();
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_newnoargs_105ed471475aaf50 = function(arg0, arg1) {
|
||||||
|
const ret = new Function(getStringFromWasm0(arg0, arg1));
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_newwithstrandinit_06c535e0a867c635 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||||
|
const ret = new Request(getStringFromWasm0(arg0, arg1), arg2);
|
||||||
|
return ret;
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_queueMicrotask_97d92b4fcc8a61c5 = function(arg0) {
|
||||||
|
queueMicrotask(arg0);
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_queueMicrotask_d3219def82552485 = function(arg0) {
|
||||||
|
const ret = arg0.queueMicrotask;
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_resolve_4851785c9c5f573d = function(arg0) {
|
||||||
|
const ret = Promise.resolve(arg0);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_set_11cd83f45504cedf = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||||
|
arg0.set(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_setbody_5923b78a95eedf29 = function(arg0, arg1) {
|
||||||
|
arg0.body = arg1;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_setmethod_3c5280fe5d890842 = function(arg0, arg1, arg2) {
|
||||||
|
arg0.method = getStringFromWasm0(arg1, arg2);
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_setmode_5dc300b865044b65 = function(arg0, arg1) {
|
||||||
|
arg0.mode = __wbindgen_enum_RequestMode[arg1];
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_static_accessor_GLOBAL_88a902d13a557d07 = function() {
|
||||||
|
const ret = typeof global === 'undefined' ? null : global;
|
||||||
|
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_static_accessor_GLOBAL_THIS_56578be7e9f832b0 = function() {
|
||||||
|
const ret = typeof globalThis === 'undefined' ? null : globalThis;
|
||||||
|
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_static_accessor_SELF_37c5d418e4bf5819 = function() {
|
||||||
|
const ret = typeof self === 'undefined' ? null : self;
|
||||||
|
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_static_accessor_WINDOW_5de37043a91a9c40 = function() {
|
||||||
|
const ret = typeof window === 'undefined' ? null : window;
|
||||||
|
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_text_7805bea50de2af49 = function() { return handleError(function (arg0) {
|
||||||
|
const ret = arg0.text();
|
||||||
|
return ret;
|
||||||
|
}, arguments) };
|
||||||
|
imports.wbg.__wbg_then_44b73946d2fb3e7d = function(arg0, arg1) {
|
||||||
|
const ret = arg0.then(arg1);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbg_then_48b406749878a531 = function(arg0, arg1, arg2) {
|
||||||
|
const ret = arg0.then(arg1, arg2);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_bigint_from_u64 = function(arg0) {
|
||||||
|
const ret = BigInt.asUintN(64, arg0);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_cb_drop = function(arg0) {
|
||||||
|
const obj = arg0.original;
|
||||||
|
if (obj.cnt-- == 1) {
|
||||||
|
obj.a = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const ret = false;
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_closure_wrapper214 = function(arg0, arg1, arg2) {
|
||||||
|
const ret = makeMutClosure(arg0, arg1, 50, __wbg_adapter_24);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
|
||||||
|
const ret = debugString(arg1);
|
||||||
|
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
const len1 = WASM_VECTOR_LEN;
|
||||||
|
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
||||||
|
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_init_externref_table = function() {
|
||||||
|
const table = wasm.__wbindgen_export_2;
|
||||||
|
const offset = table.grow(4);
|
||||||
|
table.set(0, undefined);
|
||||||
|
table.set(offset + 0, undefined);
|
||||||
|
table.set(offset + 1, null);
|
||||||
|
table.set(offset + 2, true);
|
||||||
|
table.set(offset + 3, false);
|
||||||
|
;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_is_function = function(arg0) {
|
||||||
|
const ret = typeof(arg0) === 'function';
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_is_undefined = function(arg0) {
|
||||||
|
const ret = arg0 === undefined;
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
|
||||||
|
const obj = arg1;
|
||||||
|
const ret = typeof(obj) === 'string' ? obj : undefined;
|
||||||
|
var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||||
|
var len1 = WASM_VECTOR_LEN;
|
||||||
|
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
||||||
|
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
|
||||||
|
const ret = getStringFromWasm0(arg0, arg1);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
imports.wbg.__wbindgen_throw = function(arg0, arg1) {
|
||||||
|
throw new Error(getStringFromWasm0(arg0, arg1));
|
||||||
|
};
|
||||||
|
|
||||||
|
return imports;
|
||||||
|
}
|
||||||
|
|
||||||
|
function __wbg_init_memory(imports, memory) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function __wbg_finalize_init(instance, module) {
|
||||||
|
wasm = instance.exports;
|
||||||
|
__wbg_init.__wbindgen_wasm_module = module;
|
||||||
|
cachedDataViewMemory0 = null;
|
||||||
|
cachedUint8ArrayMemory0 = null;
|
||||||
|
|
||||||
|
|
||||||
|
wasm.__wbindgen_start();
|
||||||
|
return wasm;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initSync(module) {
|
||||||
|
if (wasm !== undefined) return wasm;
|
||||||
|
|
||||||
|
|
||||||
|
if (typeof module !== 'undefined') {
|
||||||
|
if (Object.getPrototypeOf(module) === Object.prototype) {
|
||||||
|
({module} = module)
|
||||||
|
} else {
|
||||||
|
console.warn('using deprecated parameters for `initSync()`; pass a single object instead')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const imports = __wbg_get_imports();
|
||||||
|
|
||||||
|
__wbg_init_memory(imports);
|
||||||
|
|
||||||
|
if (!(module instanceof WebAssembly.Module)) {
|
||||||
|
module = new WebAssembly.Module(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
const instance = new WebAssembly.Instance(module, imports);
|
||||||
|
|
||||||
|
return __wbg_finalize_init(instance, module);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function __wbg_init(module_or_path) {
|
||||||
|
if (wasm !== undefined) return wasm;
|
||||||
|
|
||||||
|
|
||||||
|
if (typeof module_or_path !== 'undefined') {
|
||||||
|
if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
|
||||||
|
({module_or_path} = module_or_path)
|
||||||
|
} else {
|
||||||
|
console.warn('using deprecated parameters for the initialization function; pass a single object instead')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof module_or_path === 'undefined') {
|
||||||
|
module_or_path = new URL('neptune_wasm_bg.wasm', import.meta.url);
|
||||||
|
}
|
||||||
|
const imports = __wbg_get_imports();
|
||||||
|
|
||||||
|
if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {
|
||||||
|
module_or_path = fetch(module_or_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
__wbg_init_memory(imports);
|
||||||
|
|
||||||
|
const { instance, module } = await __wbg_load(await module_or_path, imports);
|
||||||
|
|
||||||
|
return __wbg_finalize_init(instance, module);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { initSync };
|
||||||
|
export default __wbg_init;
|
||||||
BIN
packages/neptune-wasm/neptune_wasm_bg.wasm
Normal file
BIN
packages/neptune-wasm/neptune_wasm_bg.wasm
Normal file
Binary file not shown.
32
packages/neptune-wasm/neptune_wasm_bg.wasm.d.ts
vendored
Normal file
32
packages/neptune-wasm/neptune_wasm_bg.wasm.d.ts
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export const memory: WebAssembly.Memory;
|
||||||
|
export const generate_seed: () => [number, number, number, number];
|
||||||
|
export const get_viewkey: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
||||||
|
export const address_from_seed: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
||||||
|
export const validate_seed_phrase: (a: number, b: number) => [number, number, number];
|
||||||
|
export const decode_viewkey: (a: number, b: number) => [number, number, number, number];
|
||||||
|
export const build_and_sign_tx: (a: number, b: number) => [number, number, number, number];
|
||||||
|
export const get_balance: (a: number, b: number) => any;
|
||||||
|
export const send_tx_jsonrpc: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => any;
|
||||||
|
export const get_block_height: (a: number, b: number) => any;
|
||||||
|
export const get_network_info: (a: number, b: number) => any;
|
||||||
|
export const get_wallet_address: (a: number, b: number) => any;
|
||||||
|
export const get_viewkey_from_neptune: (a: number, b: number) => any;
|
||||||
|
export const get_utxos_from_viewkey: (a: number, b: number, c: number, d: number, e: bigint, f: number, g: bigint) => any;
|
||||||
|
export const generate_viewkey_from_phrase: (a: number, b: number, c: bigint) => [number, number, number, number];
|
||||||
|
export const generate_address_from_phrase: (a: number, b: number, c: bigint, d: number) => [number, number, number, number];
|
||||||
|
export const get_receiver_id_from_viewkey: (a: number, b: number) => [number, number, number, number];
|
||||||
|
export const debug_key_derivation: (a: number, b: number, c: bigint) => [number, number, number, number];
|
||||||
|
export const sign_transaction_offline: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: bigint, j: number) => [number, number, number, number];
|
||||||
|
export const __wbindgen_exn_store: (a: number) => void;
|
||||||
|
export const __externref_table_alloc: () => number;
|
||||||
|
export const __wbindgen_export_2: WebAssembly.Table;
|
||||||
|
export const __wbindgen_export_3: WebAssembly.Table;
|
||||||
|
export const __wbindgen_malloc: (a: number, b: number) => number;
|
||||||
|
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
||||||
|
export const __externref_table_dealloc: (a: number) => void;
|
||||||
|
export const __wbindgen_free: (a: number, b: number, c: number) => void;
|
||||||
|
export const closure49_externref_shim: (a: number, b: number, c: any) => void;
|
||||||
|
export const closure268_externref_shim: (a: number, b: number, c: any, d: any) => void;
|
||||||
|
export const __wbindgen_start: () => void;
|
||||||
16
packages/neptune-wasm/package.json
Normal file
16
packages/neptune-wasm/package.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "@neptune/wasm",
|
||||||
|
"type": "module",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"files": [
|
||||||
|
"neptune_wasm_bg.wasm",
|
||||||
|
"neptune_wasm.js",
|
||||||
|
"neptune_wasm.d.ts"
|
||||||
|
],
|
||||||
|
"main": "neptune_wasm.js",
|
||||||
|
"types": "neptune_wasm.d.ts",
|
||||||
|
"sideEffects": [
|
||||||
|
"./snippets/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
391
pnpm-lock.yaml
generated
391
pnpm-lock.yaml
generated
@ -4,19 +4,31 @@ settings:
|
|||||||
autoInstallPeers: true
|
autoInstallPeers: true
|
||||||
excludeLinksFromLockfile: false
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
overrides:
|
||||||
|
axios: ^1.7.9
|
||||||
|
|
||||||
importers:
|
importers:
|
||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@neptune/native':
|
||||||
|
specifier: file:./packages/neptune-native
|
||||||
|
version: link:packages/neptune-native
|
||||||
|
'@neptune/wasm':
|
||||||
|
specifier: file:./packages/neptune-wasm
|
||||||
|
version: link:packages/neptune-wasm
|
||||||
'@tailwindcss/vite':
|
'@tailwindcss/vite':
|
||||||
specifier: ^4.1.17
|
specifier: ^4.1.17
|
||||||
version: 4.1.17(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2))
|
version: 4.1.17(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2))
|
||||||
'@tanstack/vue-form':
|
'@tanstack/vue-form':
|
||||||
specifier: ^1.26.0
|
specifier: ^1.26.0
|
||||||
version: 1.26.0(vue@3.5.24(typescript@5.9.3))
|
version: 1.26.0(vue@3.5.25(typescript@5.9.3))
|
||||||
|
'@tauri-apps/api':
|
||||||
|
specifier: ^2.9.0
|
||||||
|
version: 2.9.0
|
||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: ^14.0.0
|
specifier: ^14.0.0
|
||||||
version: 14.0.0(vue@3.5.24(typescript@5.9.3))
|
version: 14.0.0(vue@3.5.25(typescript@5.9.3))
|
||||||
axios:
|
axios:
|
||||||
specifier: ^1.7.9
|
specifier: ^1.7.9
|
||||||
version: 1.13.2
|
version: 1.13.2
|
||||||
@ -28,13 +40,13 @@ importers:
|
|||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
lucide-vue-next:
|
lucide-vue-next:
|
||||||
specifier: ^0.554.0
|
specifier: ^0.554.0
|
||||||
version: 0.554.0(vue@3.5.24(typescript@5.9.3))
|
version: 0.554.0(vue@3.5.25(typescript@5.9.3))
|
||||||
pinia:
|
pinia:
|
||||||
specifier: ^2.3.1
|
specifier: ^2.3.1
|
||||||
version: 2.3.1(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3))
|
version: 2.3.1(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3))
|
||||||
reka-ui:
|
reka-ui:
|
||||||
specifier: ^2.6.0
|
specifier: ^2.6.0
|
||||||
version: 2.6.0(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3))
|
version: 2.6.0(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3))
|
||||||
tailwind-merge:
|
tailwind-merge:
|
||||||
specifier: ^3.4.0
|
specifier: ^3.4.0
|
||||||
version: 3.4.0
|
version: 3.4.0
|
||||||
@ -46,13 +58,13 @@ importers:
|
|||||||
version: 1.0.7(tailwindcss@4.1.17)
|
version: 1.0.7(tailwindcss@4.1.17)
|
||||||
vue:
|
vue:
|
||||||
specifier: ^3.5.24
|
specifier: ^3.5.24
|
||||||
version: 3.5.24(typescript@5.9.3)
|
version: 3.5.25(typescript@5.9.3)
|
||||||
vue-i18n:
|
vue-i18n:
|
||||||
specifier: ^10.0.8
|
specifier: ^10.0.8
|
||||||
version: 10.0.8(vue@3.5.24(typescript@5.9.3))
|
version: 10.0.8(vue@3.5.25(typescript@5.9.3))
|
||||||
vue-router:
|
vue-router:
|
||||||
specifier: ^4.5.0
|
specifier: ^4.5.0
|
||||||
version: 4.6.3(vue@3.5.24(typescript@5.9.3))
|
version: 4.6.3(vue@3.5.25(typescript@5.9.3))
|
||||||
vue-sonner:
|
vue-sonner:
|
||||||
specifier: ^2.0.9
|
specifier: ^2.0.9
|
||||||
version: 2.0.9
|
version: 2.0.9
|
||||||
@ -71,13 +83,13 @@ importers:
|
|||||||
version: 24.10.1
|
version: 24.10.1
|
||||||
'@typescript-eslint/eslint-plugin':
|
'@typescript-eslint/eslint-plugin':
|
||||||
specifier: ^8.0.0
|
specifier: ^8.0.0
|
||||||
version: 8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||||
'@typescript-eslint/parser':
|
'@typescript-eslint/parser':
|
||||||
specifier: ^8.0.0
|
specifier: ^8.0.0
|
||||||
version: 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
version: 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||||
'@vitejs/plugin-vue':
|
'@vitejs/plugin-vue':
|
||||||
specifier: ^6.0.1
|
specifier: ^6.0.1
|
||||||
version: 6.0.2(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2))(vue@3.5.24(typescript@5.9.3))
|
version: 6.0.2(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2))(vue@3.5.25(typescript@5.9.3))
|
||||||
'@vue/eslint-config-prettier':
|
'@vue/eslint-config-prettier':
|
||||||
specifier: ^10.0.0
|
specifier: ^10.0.0
|
||||||
version: 10.2.0(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2)
|
version: 10.2.0(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2)
|
||||||
@ -86,7 +98,7 @@ importers:
|
|||||||
version: 14.6.0(eslint-plugin-vue@9.33.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
version: 14.6.0(eslint-plugin-vue@9.33.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||||
'@vue/tsconfig':
|
'@vue/tsconfig':
|
||||||
specifier: ^0.8.1
|
specifier: ^0.8.1
|
||||||
version: 0.8.1(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3))
|
version: 0.8.1(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3))
|
||||||
eslint:
|
eslint:
|
||||||
specifier: ^9.0.0
|
specifier: ^9.0.0
|
||||||
version: 9.39.1(jiti@2.6.1)
|
version: 9.39.1(jiti@2.6.1)
|
||||||
@ -101,10 +113,10 @@ importers:
|
|||||||
version: 5.9.3
|
version: 5.9.3
|
||||||
unplugin-auto-import:
|
unplugin-auto-import:
|
||||||
specifier: ^20.2.0
|
specifier: ^20.2.0
|
||||||
version: 20.2.0(@vueuse/core@14.0.0(vue@3.5.24(typescript@5.9.3)))
|
version: 20.2.0(@vueuse/core@14.0.0(vue@3.5.25(typescript@5.9.3)))
|
||||||
unplugin-vue-components:
|
unplugin-vue-components:
|
||||||
specifier: ^30.0.0
|
specifier: ^30.0.0
|
||||||
version: 30.0.0(@babel/parser@7.28.5)(vue@3.5.24(typescript@5.9.3))
|
version: 30.0.0(@babel/parser@7.28.5)(vue@3.5.25(typescript@5.9.3))
|
||||||
vite:
|
vite:
|
||||||
specifier: ^7.2.4
|
specifier: ^7.2.4
|
||||||
version: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)
|
version: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)
|
||||||
@ -112,6 +124,14 @@ importers:
|
|||||||
specifier: ^3.1.4
|
specifier: ^3.1.4
|
||||||
version: 3.1.5(typescript@5.9.3)
|
version: 3.1.5(typescript@5.9.3)
|
||||||
|
|
||||||
|
packages/neptune-native:
|
||||||
|
devDependencies:
|
||||||
|
'@napi-rs/cli':
|
||||||
|
specifier: ^2.18.4
|
||||||
|
version: 2.18.4
|
||||||
|
|
||||||
|
packages/neptune-wasm: {}
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
'@babel/helper-string-parser@7.27.1':
|
'@babel/helper-string-parser@7.27.1':
|
||||||
@ -387,6 +407,11 @@ packages:
|
|||||||
'@jridgewell/trace-mapping@0.3.31':
|
'@jridgewell/trace-mapping@0.3.31':
|
||||||
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
||||||
|
|
||||||
|
'@napi-rs/cli@2.18.4':
|
||||||
|
resolution: {integrity: sha512-SgJeA4df9DE2iAEpr3M2H0OKl/yjtg1BnRI5/JyowS71tUWhrfSu2LT0V3vlHET+g1hBVlrO60PmEXwUEKp8Mg==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
'@nodelib/fs.scandir@2.1.5':
|
'@nodelib/fs.scandir@2.1.5':
|
||||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@ -648,6 +673,9 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
vue: ^2.7.0 || ^3.0.0
|
vue: ^2.7.0 || ^3.0.0
|
||||||
|
|
||||||
|
'@tauri-apps/api@2.9.0':
|
||||||
|
resolution: {integrity: sha512-qD5tMjh7utwBk9/5PrTA/aGr3i5QaJ/Mlt7p8NilQ45WgbifUNPyKWsA63iQ8YfQq6R8ajMapU+/Q8nMcPRLNw==}
|
||||||
|
|
||||||
'@tauri-apps/cli-darwin-arm64@2.9.4':
|
'@tauri-apps/cli-darwin-arm64@2.9.4':
|
||||||
resolution: {integrity: sha512-9rHkMVtbMhe0AliVbrGpzMahOBg3rwV46JYRELxR9SN6iu1dvPOaMaiC4cP6M/aD1424ziXnnMdYU06RAH8oIw==}
|
resolution: {integrity: sha512-9rHkMVtbMhe0AliVbrGpzMahOBg3rwV46JYRELxR9SN6iu1dvPOaMaiC4cP6M/aD1424ziXnnMdYU06RAH8oIw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
@ -731,63 +759,63 @@ packages:
|
|||||||
'@types/web-bluetooth@0.0.21':
|
'@types/web-bluetooth@0.0.21':
|
||||||
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
|
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
|
||||||
|
|
||||||
'@typescript-eslint/eslint-plugin@8.47.0':
|
'@typescript-eslint/eslint-plugin@8.48.0':
|
||||||
resolution: {integrity: sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA==}
|
resolution: {integrity: sha512-XxXP5tL1txl13YFtrECECQYeZjBZad4fyd3cFV4a19LkAY/bIp9fev3US4S5fDVV2JaYFiKAZ/GRTOLer+mbyQ==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@typescript-eslint/parser': ^8.47.0
|
'@typescript-eslint/parser': ^8.48.0
|
||||||
eslint: ^8.57.0 || ^9.0.0
|
eslint: ^8.57.0 || ^9.0.0
|
||||||
typescript: '>=4.8.4 <6.0.0'
|
typescript: '>=4.8.4 <6.0.0'
|
||||||
|
|
||||||
'@typescript-eslint/parser@8.47.0':
|
'@typescript-eslint/parser@8.48.0':
|
||||||
resolution: {integrity: sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==}
|
resolution: {integrity: sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^8.57.0 || ^9.0.0
|
eslint: ^8.57.0 || ^9.0.0
|
||||||
typescript: '>=4.8.4 <6.0.0'
|
typescript: '>=4.8.4 <6.0.0'
|
||||||
|
|
||||||
'@typescript-eslint/project-service@8.47.0':
|
'@typescript-eslint/project-service@8.48.0':
|
||||||
resolution: {integrity: sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA==}
|
resolution: {integrity: sha512-Ne4CTZyRh1BecBf84siv42wv5vQvVmgtk8AuiEffKTUo3DrBaGYZueJSxxBZ8fjk/N3DrgChH4TOdIOwOwiqqw==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
typescript: '>=4.8.4 <6.0.0'
|
typescript: '>=4.8.4 <6.0.0'
|
||||||
|
|
||||||
'@typescript-eslint/scope-manager@8.47.0':
|
'@typescript-eslint/scope-manager@8.48.0':
|
||||||
resolution: {integrity: sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==}
|
resolution: {integrity: sha512-uGSSsbrtJrLduti0Q1Q9+BF1/iFKaxGoQwjWOIVNJv0o6omrdyR8ct37m4xIl5Zzpkp69Kkmvom7QFTtue89YQ==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@typescript-eslint/tsconfig-utils@8.47.0':
|
'@typescript-eslint/tsconfig-utils@8.48.0':
|
||||||
resolution: {integrity: sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g==}
|
resolution: {integrity: sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
typescript: '>=4.8.4 <6.0.0'
|
typescript: '>=4.8.4 <6.0.0'
|
||||||
|
|
||||||
'@typescript-eslint/type-utils@8.47.0':
|
'@typescript-eslint/type-utils@8.48.0':
|
||||||
resolution: {integrity: sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A==}
|
resolution: {integrity: sha512-zbeVaVqeXhhab6QNEKfK96Xyc7UQuoFWERhEnj3mLVnUWrQnv15cJNseUni7f3g557gm0e46LZ6IJ4NJVOgOpw==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^8.57.0 || ^9.0.0
|
eslint: ^8.57.0 || ^9.0.0
|
||||||
typescript: '>=4.8.4 <6.0.0'
|
typescript: '>=4.8.4 <6.0.0'
|
||||||
|
|
||||||
'@typescript-eslint/types@8.47.0':
|
'@typescript-eslint/types@8.48.0':
|
||||||
resolution: {integrity: sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==}
|
resolution: {integrity: sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@typescript-eslint/typescript-estree@8.47.0':
|
'@typescript-eslint/typescript-estree@8.48.0':
|
||||||
resolution: {integrity: sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg==}
|
resolution: {integrity: sha512-ljHab1CSO4rGrQIAyizUS6UGHHCiAYhbfcIZ1zVJr5nMryxlXMVWS3duFPSKvSUbFPwkXMFk1k0EMIjub4sRRQ==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
typescript: '>=4.8.4 <6.0.0'
|
typescript: '>=4.8.4 <6.0.0'
|
||||||
|
|
||||||
'@typescript-eslint/utils@8.47.0':
|
'@typescript-eslint/utils@8.48.0':
|
||||||
resolution: {integrity: sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ==}
|
resolution: {integrity: sha512-yTJO1XuGxCsSfIVt1+1UrLHtue8xz16V8apzPYI06W0HbEbEWHxHXgZaAgavIkoh+GeV6hKKd5jm0sS6OYxWXQ==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^8.57.0 || ^9.0.0
|
eslint: ^8.57.0 || ^9.0.0
|
||||||
typescript: '>=4.8.4 <6.0.0'
|
typescript: '>=4.8.4 <6.0.0'
|
||||||
|
|
||||||
'@typescript-eslint/visitor-keys@8.47.0':
|
'@typescript-eslint/visitor-keys@8.48.0':
|
||||||
resolution: {integrity: sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==}
|
resolution: {integrity: sha512-T0XJMaRPOH3+LBbAfzR2jalckP1MSG/L9eUtY0DEzUyVaXJ/t6zN0nR7co5kz0Jko/nkSYCBRkz1djvjajVTTg==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@vitejs/plugin-vue@6.0.2':
|
'@vitejs/plugin-vue@6.0.2':
|
||||||
@ -806,17 +834,17 @@ packages:
|
|||||||
'@volar/typescript@2.4.23':
|
'@volar/typescript@2.4.23':
|
||||||
resolution: {integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==}
|
resolution: {integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==}
|
||||||
|
|
||||||
'@vue/compiler-core@3.5.24':
|
'@vue/compiler-core@3.5.25':
|
||||||
resolution: {integrity: sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==}
|
resolution: {integrity: sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==}
|
||||||
|
|
||||||
'@vue/compiler-dom@3.5.24':
|
'@vue/compiler-dom@3.5.25':
|
||||||
resolution: {integrity: sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==}
|
resolution: {integrity: sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==}
|
||||||
|
|
||||||
'@vue/compiler-sfc@3.5.24':
|
'@vue/compiler-sfc@3.5.25':
|
||||||
resolution: {integrity: sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==}
|
resolution: {integrity: sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==}
|
||||||
|
|
||||||
'@vue/compiler-ssr@3.5.24':
|
'@vue/compiler-ssr@3.5.25':
|
||||||
resolution: {integrity: sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==}
|
resolution: {integrity: sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==}
|
||||||
|
|
||||||
'@vue/devtools-api@6.6.4':
|
'@vue/devtools-api@6.6.4':
|
||||||
resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
|
resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
|
||||||
@ -846,22 +874,22 @@ packages:
|
|||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@vue/reactivity@3.5.24':
|
'@vue/reactivity@3.5.25':
|
||||||
resolution: {integrity: sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg==}
|
resolution: {integrity: sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==}
|
||||||
|
|
||||||
'@vue/runtime-core@3.5.24':
|
'@vue/runtime-core@3.5.25':
|
||||||
resolution: {integrity: sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ==}
|
resolution: {integrity: sha512-Z751v203YWwYzy460bzsYQISDfPjHTl+6Zzwo/a3CsAf+0ccEjQ8c+0CdX1WsumRTHeywvyUFtW6KvNukT/smA==}
|
||||||
|
|
||||||
'@vue/runtime-dom@3.5.24':
|
'@vue/runtime-dom@3.5.25':
|
||||||
resolution: {integrity: sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw==}
|
resolution: {integrity: sha512-a4WrkYFbb19i9pjkz38zJBg8wa/rboNERq3+hRRb0dHiJh13c+6kAbgqCPfMaJ2gg4weWD3APZswASOfmKwamA==}
|
||||||
|
|
||||||
'@vue/server-renderer@3.5.24':
|
'@vue/server-renderer@3.5.25':
|
||||||
resolution: {integrity: sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w==}
|
resolution: {integrity: sha512-UJaXR54vMG61i8XNIzTSf2Q7MOqZHpp8+x3XLGtE3+fL+nQd+k7O5+X3D/uWrnQXOdMw5VPih+Uremcw+u1woQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vue: 3.5.24
|
vue: 3.5.25
|
||||||
|
|
||||||
'@vue/shared@3.5.24':
|
'@vue/shared@3.5.25':
|
||||||
resolution: {integrity: sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==}
|
resolution: {integrity: sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==}
|
||||||
|
|
||||||
'@vue/tsconfig@0.8.1':
|
'@vue/tsconfig@0.8.1':
|
||||||
resolution: {integrity: sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g==}
|
resolution: {integrity: sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g==}
|
||||||
@ -909,8 +937,8 @@ packages:
|
|||||||
ajv@6.12.6:
|
ajv@6.12.6:
|
||||||
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
|
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
|
||||||
|
|
||||||
alien-signals@3.1.0:
|
alien-signals@3.1.1:
|
||||||
resolution: {integrity: sha512-yufC6VpSy8tK3I0lO67pjumo5JvDQVQyr38+3OHqe6CHl1t2VZekKZ7EKKZSqk0cRmE7U7tfZbpXiKNzuc+ckg==}
|
resolution: {integrity: sha512-ogkIWbVrLwKtHY6oOAXaYkAxP+cTH7V5FZ5+Tm4NZFd8VDZ6uNMDrfzqctTZ42eTMCSR3ne3otpcxmqSnFfPYA==}
|
||||||
|
|
||||||
ansi-styles@4.3.0:
|
ansi-styles@4.3.0:
|
||||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||||
@ -1657,8 +1685,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
|
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
typescript-eslint@8.47.0:
|
typescript-eslint@8.48.0:
|
||||||
resolution: {integrity: sha512-Lwe8i2XQ3WoMjua/r1PHrCTpkubPYJCAfOurtn+mtTzqB6jNd+14n9UN1bJ4s3F49x9ixAm0FLflB/JzQ57M8Q==}
|
resolution: {integrity: sha512-fcKOvQD9GUn3Xw63EgiDqhvWJ5jsyZUaekl3KVpGsDJnN46WJTe3jWxtQP9lMZm1LJNkFLlTaWAxK2vUQR+cqw==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^8.57.0 || ^9.0.0
|
eslint: ^8.57.0 || ^9.0.0
|
||||||
@ -1815,8 +1843,8 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
typescript: '>=5.0.0'
|
typescript: '>=5.0.0'
|
||||||
|
|
||||||
vue@3.5.24:
|
vue@3.5.25:
|
||||||
resolution: {integrity: sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==}
|
resolution: {integrity: sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
typescript: '*'
|
typescript: '*'
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
@ -1996,11 +2024,11 @@ snapshots:
|
|||||||
|
|
||||||
'@floating-ui/utils@0.2.10': {}
|
'@floating-ui/utils@0.2.10': {}
|
||||||
|
|
||||||
'@floating-ui/vue@1.1.9(vue@3.5.24(typescript@5.9.3))':
|
'@floating-ui/vue@1.1.9(vue@3.5.25(typescript@5.9.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@floating-ui/dom': 1.7.4
|
'@floating-ui/dom': 1.7.4
|
||||||
'@floating-ui/utils': 0.2.10
|
'@floating-ui/utils': 0.2.10
|
||||||
vue-demi: 0.14.10(vue@3.5.24(typescript@5.9.3))
|
vue-demi: 0.14.10(vue@3.5.25(typescript@5.9.3))
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@vue/composition-api'
|
- '@vue/composition-api'
|
||||||
- vue
|
- vue
|
||||||
@ -2055,6 +2083,8 @@ snapshots:
|
|||||||
'@jridgewell/resolve-uri': 3.1.2
|
'@jridgewell/resolve-uri': 3.1.2
|
||||||
'@jridgewell/sourcemap-codec': 1.5.5
|
'@jridgewell/sourcemap-codec': 1.5.5
|
||||||
|
|
||||||
|
'@napi-rs/cli@2.18.4': {}
|
||||||
|
|
||||||
'@nodelib/fs.scandir@2.1.5':
|
'@nodelib/fs.scandir@2.1.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nodelib/fs.stat': 2.0.5
|
'@nodelib/fs.stat': 2.0.5
|
||||||
@ -2228,24 +2258,26 @@ snapshots:
|
|||||||
|
|
||||||
'@tanstack/virtual-core@3.13.12': {}
|
'@tanstack/virtual-core@3.13.12': {}
|
||||||
|
|
||||||
'@tanstack/vue-form@1.26.0(vue@3.5.24(typescript@5.9.3))':
|
'@tanstack/vue-form@1.26.0(vue@3.5.25(typescript@5.9.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tanstack/form-core': 1.26.0
|
'@tanstack/form-core': 1.26.0
|
||||||
'@tanstack/vue-store': 0.7.7(vue@3.5.24(typescript@5.9.3))
|
'@tanstack/vue-store': 0.7.7(vue@3.5.25(typescript@5.9.3))
|
||||||
vue: 3.5.24(typescript@5.9.3)
|
vue: 3.5.25(typescript@5.9.3)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@vue/composition-api'
|
- '@vue/composition-api'
|
||||||
|
|
||||||
'@tanstack/vue-store@0.7.7(vue@3.5.24(typescript@5.9.3))':
|
'@tanstack/vue-store@0.7.7(vue@3.5.25(typescript@5.9.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tanstack/store': 0.7.7
|
'@tanstack/store': 0.7.7
|
||||||
vue: 3.5.24(typescript@5.9.3)
|
vue: 3.5.25(typescript@5.9.3)
|
||||||
vue-demi: 0.14.10(vue@3.5.24(typescript@5.9.3))
|
vue-demi: 0.14.10(vue@3.5.25(typescript@5.9.3))
|
||||||
|
|
||||||
'@tanstack/vue-virtual@3.13.12(vue@3.5.24(typescript@5.9.3))':
|
'@tanstack/vue-virtual@3.13.12(vue@3.5.25(typescript@5.9.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tanstack/virtual-core': 3.13.12
|
'@tanstack/virtual-core': 3.13.12
|
||||||
vue: 3.5.24(typescript@5.9.3)
|
vue: 3.5.25(typescript@5.9.3)
|
||||||
|
|
||||||
|
'@tauri-apps/api@2.9.0': {}
|
||||||
|
|
||||||
'@tauri-apps/cli-darwin-arm64@2.9.4':
|
'@tauri-apps/cli-darwin-arm64@2.9.4':
|
||||||
optional: true
|
optional: true
|
||||||
@ -2304,14 +2336,14 @@ snapshots:
|
|||||||
|
|
||||||
'@types/web-bluetooth@0.0.21': {}
|
'@types/web-bluetooth@0.0.21': {}
|
||||||
|
|
||||||
'@typescript-eslint/eslint-plugin@8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
'@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/regexpp': 4.12.2
|
'@eslint-community/regexpp': 4.12.2
|
||||||
'@typescript-eslint/parser': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
'@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||||
'@typescript-eslint/scope-manager': 8.47.0
|
'@typescript-eslint/scope-manager': 8.48.0
|
||||||
'@typescript-eslint/type-utils': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
'@typescript-eslint/type-utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||||
'@typescript-eslint/utils': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
'@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||||
'@typescript-eslint/visitor-keys': 8.47.0
|
'@typescript-eslint/visitor-keys': 8.48.0
|
||||||
eslint: 9.39.1(jiti@2.6.1)
|
eslint: 9.39.1(jiti@2.6.1)
|
||||||
graphemer: 1.4.0
|
graphemer: 1.4.0
|
||||||
ignore: 7.0.5
|
ignore: 7.0.5
|
||||||
@ -2321,41 +2353,41 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
'@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/scope-manager': 8.47.0
|
'@typescript-eslint/scope-manager': 8.48.0
|
||||||
'@typescript-eslint/types': 8.47.0
|
'@typescript-eslint/types': 8.48.0
|
||||||
'@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3)
|
'@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3)
|
||||||
'@typescript-eslint/visitor-keys': 8.47.0
|
'@typescript-eslint/visitor-keys': 8.48.0
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
eslint: 9.39.1(jiti@2.6.1)
|
eslint: 9.39.1(jiti@2.6.1)
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@typescript-eslint/project-service@8.47.0(typescript@5.9.3)':
|
'@typescript-eslint/project-service@8.48.0(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3)
|
'@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3)
|
||||||
'@typescript-eslint/types': 8.47.0
|
'@typescript-eslint/types': 8.48.0
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@typescript-eslint/scope-manager@8.47.0':
|
'@typescript-eslint/scope-manager@8.48.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 8.47.0
|
'@typescript-eslint/types': 8.48.0
|
||||||
'@typescript-eslint/visitor-keys': 8.47.0
|
'@typescript-eslint/visitor-keys': 8.48.0
|
||||||
|
|
||||||
'@typescript-eslint/tsconfig-utils@8.47.0(typescript@5.9.3)':
|
'@typescript-eslint/tsconfig-utils@8.48.0(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
|
|
||||||
'@typescript-eslint/type-utils@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
'@typescript-eslint/type-utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 8.47.0
|
'@typescript-eslint/types': 8.48.0
|
||||||
'@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3)
|
'@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3)
|
||||||
'@typescript-eslint/utils': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
'@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
eslint: 9.39.1(jiti@2.6.1)
|
eslint: 9.39.1(jiti@2.6.1)
|
||||||
ts-api-utils: 2.1.0(typescript@5.9.3)
|
ts-api-utils: 2.1.0(typescript@5.9.3)
|
||||||
@ -2363,45 +2395,44 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@typescript-eslint/types@8.47.0': {}
|
'@typescript-eslint/types@8.48.0': {}
|
||||||
|
|
||||||
'@typescript-eslint/typescript-estree@8.47.0(typescript@5.9.3)':
|
'@typescript-eslint/typescript-estree@8.48.0(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/project-service': 8.47.0(typescript@5.9.3)
|
'@typescript-eslint/project-service': 8.48.0(typescript@5.9.3)
|
||||||
'@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3)
|
'@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3)
|
||||||
'@typescript-eslint/types': 8.47.0
|
'@typescript-eslint/types': 8.48.0
|
||||||
'@typescript-eslint/visitor-keys': 8.47.0
|
'@typescript-eslint/visitor-keys': 8.48.0
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
fast-glob: 3.3.3
|
|
||||||
is-glob: 4.0.3
|
|
||||||
minimatch: 9.0.5
|
minimatch: 9.0.5
|
||||||
semver: 7.7.3
|
semver: 7.7.3
|
||||||
|
tinyglobby: 0.2.15
|
||||||
ts-api-utils: 2.1.0(typescript@5.9.3)
|
ts-api-utils: 2.1.0(typescript@5.9.3)
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@typescript-eslint/utils@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
'@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1))
|
'@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1))
|
||||||
'@typescript-eslint/scope-manager': 8.47.0
|
'@typescript-eslint/scope-manager': 8.48.0
|
||||||
'@typescript-eslint/types': 8.47.0
|
'@typescript-eslint/types': 8.48.0
|
||||||
'@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3)
|
'@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3)
|
||||||
eslint: 9.39.1(jiti@2.6.1)
|
eslint: 9.39.1(jiti@2.6.1)
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@typescript-eslint/visitor-keys@8.47.0':
|
'@typescript-eslint/visitor-keys@8.48.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 8.47.0
|
'@typescript-eslint/types': 8.48.0
|
||||||
eslint-visitor-keys: 4.2.1
|
eslint-visitor-keys: 4.2.1
|
||||||
|
|
||||||
'@vitejs/plugin-vue@6.0.2(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2))(vue@3.5.24(typescript@5.9.3))':
|
'@vitejs/plugin-vue@6.0.2(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2))(vue@3.5.25(typescript@5.9.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@rolldown/pluginutils': 1.0.0-beta.50
|
'@rolldown/pluginutils': 1.0.0-beta.50
|
||||||
vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)
|
vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)
|
||||||
vue: 3.5.24(typescript@5.9.3)
|
vue: 3.5.25(typescript@5.9.3)
|
||||||
|
|
||||||
'@volar/language-core@2.4.23':
|
'@volar/language-core@2.4.23':
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2415,35 +2446,35 @@ snapshots:
|
|||||||
path-browserify: 1.0.1
|
path-browserify: 1.0.1
|
||||||
vscode-uri: 3.1.0
|
vscode-uri: 3.1.0
|
||||||
|
|
||||||
'@vue/compiler-core@3.5.24':
|
'@vue/compiler-core@3.5.25':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/parser': 7.28.5
|
'@babel/parser': 7.28.5
|
||||||
'@vue/shared': 3.5.24
|
'@vue/shared': 3.5.25
|
||||||
entities: 4.5.0
|
entities: 4.5.0
|
||||||
estree-walker: 2.0.2
|
estree-walker: 2.0.2
|
||||||
source-map-js: 1.2.1
|
source-map-js: 1.2.1
|
||||||
|
|
||||||
'@vue/compiler-dom@3.5.24':
|
'@vue/compiler-dom@3.5.25':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/compiler-core': 3.5.24
|
'@vue/compiler-core': 3.5.25
|
||||||
'@vue/shared': 3.5.24
|
'@vue/shared': 3.5.25
|
||||||
|
|
||||||
'@vue/compiler-sfc@3.5.24':
|
'@vue/compiler-sfc@3.5.25':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/parser': 7.28.5
|
'@babel/parser': 7.28.5
|
||||||
'@vue/compiler-core': 3.5.24
|
'@vue/compiler-core': 3.5.25
|
||||||
'@vue/compiler-dom': 3.5.24
|
'@vue/compiler-dom': 3.5.25
|
||||||
'@vue/compiler-ssr': 3.5.24
|
'@vue/compiler-ssr': 3.5.25
|
||||||
'@vue/shared': 3.5.24
|
'@vue/shared': 3.5.25
|
||||||
estree-walker: 2.0.2
|
estree-walker: 2.0.2
|
||||||
magic-string: 0.30.21
|
magic-string: 0.30.21
|
||||||
postcss: 8.5.6
|
postcss: 8.5.6
|
||||||
source-map-js: 1.2.1
|
source-map-js: 1.2.1
|
||||||
|
|
||||||
'@vue/compiler-ssr@3.5.24':
|
'@vue/compiler-ssr@3.5.25':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/compiler-dom': 3.5.24
|
'@vue/compiler-dom': 3.5.25
|
||||||
'@vue/shared': 3.5.24
|
'@vue/shared': 3.5.25
|
||||||
|
|
||||||
'@vue/devtools-api@6.6.4': {}
|
'@vue/devtools-api@6.6.4': {}
|
||||||
|
|
||||||
@ -2458,11 +2489,11 @@ snapshots:
|
|||||||
|
|
||||||
'@vue/eslint-config-typescript@14.6.0(eslint-plugin-vue@9.33.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
'@vue/eslint-config-typescript@14.6.0(eslint-plugin-vue@9.33.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/utils': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
'@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||||
eslint: 9.39.1(jiti@2.6.1)
|
eslint: 9.39.1(jiti@2.6.1)
|
||||||
eslint-plugin-vue: 9.33.0(eslint@9.39.1(jiti@2.6.1))
|
eslint-plugin-vue: 9.33.0(eslint@9.39.1(jiti@2.6.1))
|
||||||
fast-glob: 3.3.3
|
fast-glob: 3.3.3
|
||||||
typescript-eslint: 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
typescript-eslint: 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||||
vue-eslint-parser: 10.2.0(eslint@9.39.1(jiti@2.6.1))
|
vue-eslint-parser: 10.2.0(eslint@9.39.1(jiti@2.6.1))
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
@ -2472,59 +2503,59 @@ snapshots:
|
|||||||
'@vue/language-core@3.1.5(typescript@5.9.3)':
|
'@vue/language-core@3.1.5(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@volar/language-core': 2.4.23
|
'@volar/language-core': 2.4.23
|
||||||
'@vue/compiler-dom': 3.5.24
|
'@vue/compiler-dom': 3.5.25
|
||||||
'@vue/shared': 3.5.24
|
'@vue/shared': 3.5.25
|
||||||
alien-signals: 3.1.0
|
alien-signals: 3.1.1
|
||||||
muggle-string: 0.4.1
|
muggle-string: 0.4.1
|
||||||
path-browserify: 1.0.1
|
path-browserify: 1.0.1
|
||||||
picomatch: 4.0.3
|
picomatch: 4.0.3
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
|
|
||||||
'@vue/reactivity@3.5.24':
|
'@vue/reactivity@3.5.25':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/shared': 3.5.24
|
'@vue/shared': 3.5.25
|
||||||
|
|
||||||
'@vue/runtime-core@3.5.24':
|
'@vue/runtime-core@3.5.25':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/reactivity': 3.5.24
|
'@vue/reactivity': 3.5.25
|
||||||
'@vue/shared': 3.5.24
|
'@vue/shared': 3.5.25
|
||||||
|
|
||||||
'@vue/runtime-dom@3.5.24':
|
'@vue/runtime-dom@3.5.25':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/reactivity': 3.5.24
|
'@vue/reactivity': 3.5.25
|
||||||
'@vue/runtime-core': 3.5.24
|
'@vue/runtime-core': 3.5.25
|
||||||
'@vue/shared': 3.5.24
|
'@vue/shared': 3.5.25
|
||||||
csstype: 3.2.3
|
csstype: 3.2.3
|
||||||
|
|
||||||
'@vue/server-renderer@3.5.24(vue@3.5.24(typescript@5.9.3))':
|
'@vue/server-renderer@3.5.25(vue@3.5.25(typescript@5.9.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/compiler-ssr': 3.5.24
|
'@vue/compiler-ssr': 3.5.25
|
||||||
'@vue/shared': 3.5.24
|
'@vue/shared': 3.5.25
|
||||||
vue: 3.5.24(typescript@5.9.3)
|
vue: 3.5.25(typescript@5.9.3)
|
||||||
|
|
||||||
'@vue/shared@3.5.24': {}
|
'@vue/shared@3.5.25': {}
|
||||||
|
|
||||||
'@vue/tsconfig@0.8.1(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3))':
|
'@vue/tsconfig@0.8.1(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3))':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
vue: 3.5.24(typescript@5.9.3)
|
vue: 3.5.25(typescript@5.9.3)
|
||||||
|
|
||||||
'@vueuse/core@12.8.2(typescript@5.9.3)':
|
'@vueuse/core@12.8.2(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/web-bluetooth': 0.0.21
|
'@types/web-bluetooth': 0.0.21
|
||||||
'@vueuse/metadata': 12.8.2
|
'@vueuse/metadata': 12.8.2
|
||||||
'@vueuse/shared': 12.8.2(typescript@5.9.3)
|
'@vueuse/shared': 12.8.2(typescript@5.9.3)
|
||||||
vue: 3.5.24(typescript@5.9.3)
|
vue: 3.5.25(typescript@5.9.3)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- typescript
|
- typescript
|
||||||
|
|
||||||
'@vueuse/core@14.0.0(vue@3.5.24(typescript@5.9.3))':
|
'@vueuse/core@14.0.0(vue@3.5.25(typescript@5.9.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/web-bluetooth': 0.0.21
|
'@types/web-bluetooth': 0.0.21
|
||||||
'@vueuse/metadata': 14.0.0
|
'@vueuse/metadata': 14.0.0
|
||||||
'@vueuse/shared': 14.0.0(vue@3.5.24(typescript@5.9.3))
|
'@vueuse/shared': 14.0.0(vue@3.5.25(typescript@5.9.3))
|
||||||
vue: 3.5.24(typescript@5.9.3)
|
vue: 3.5.25(typescript@5.9.3)
|
||||||
|
|
||||||
'@vueuse/metadata@12.8.2': {}
|
'@vueuse/metadata@12.8.2': {}
|
||||||
|
|
||||||
@ -2532,13 +2563,13 @@ snapshots:
|
|||||||
|
|
||||||
'@vueuse/shared@12.8.2(typescript@5.9.3)':
|
'@vueuse/shared@12.8.2(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
vue: 3.5.24(typescript@5.9.3)
|
vue: 3.5.25(typescript@5.9.3)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- typescript
|
- typescript
|
||||||
|
|
||||||
'@vueuse/shared@14.0.0(vue@3.5.24(typescript@5.9.3))':
|
'@vueuse/shared@14.0.0(vue@3.5.25(typescript@5.9.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
vue: 3.5.24(typescript@5.9.3)
|
vue: 3.5.25(typescript@5.9.3)
|
||||||
|
|
||||||
acorn-jsx@5.3.2(acorn@8.15.0):
|
acorn-jsx@5.3.2(acorn@8.15.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2553,7 +2584,7 @@ snapshots:
|
|||||||
json-schema-traverse: 0.4.1
|
json-schema-traverse: 0.4.1
|
||||||
uri-js: 4.4.1
|
uri-js: 4.4.1
|
||||||
|
|
||||||
alien-signals@3.1.0: {}
|
alien-signals@3.1.1: {}
|
||||||
|
|
||||||
ansi-styles@4.3.0:
|
ansi-styles@4.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3043,9 +3074,9 @@ snapshots:
|
|||||||
|
|
||||||
lodash@4.17.21: {}
|
lodash@4.17.21: {}
|
||||||
|
|
||||||
lucide-vue-next@0.554.0(vue@3.5.24(typescript@5.9.3)):
|
lucide-vue-next@0.554.0(vue@3.5.25(typescript@5.9.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
vue: 3.5.24(typescript@5.9.3)
|
vue: 3.5.25(typescript@5.9.3)
|
||||||
|
|
||||||
magic-string@0.30.21:
|
magic-string@0.30.21:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3130,11 +3161,11 @@ snapshots:
|
|||||||
|
|
||||||
picomatch@4.0.3: {}
|
picomatch@4.0.3: {}
|
||||||
|
|
||||||
pinia@2.3.1(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3)):
|
pinia@2.3.1(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/devtools-api': 6.6.4
|
'@vue/devtools-api': 6.6.4
|
||||||
vue: 3.5.24(typescript@5.9.3)
|
vue: 3.5.25(typescript@5.9.3)
|
||||||
vue-demi: 0.14.10(vue@3.5.24(typescript@5.9.3))
|
vue-demi: 0.14.10(vue@3.5.25(typescript@5.9.3))
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@ -3181,19 +3212,19 @@ snapshots:
|
|||||||
|
|
||||||
readdirp@4.1.2: {}
|
readdirp@4.1.2: {}
|
||||||
|
|
||||||
reka-ui@2.6.0(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3)):
|
reka-ui@2.6.0(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@floating-ui/dom': 1.7.4
|
'@floating-ui/dom': 1.7.4
|
||||||
'@floating-ui/vue': 1.1.9(vue@3.5.24(typescript@5.9.3))
|
'@floating-ui/vue': 1.1.9(vue@3.5.25(typescript@5.9.3))
|
||||||
'@internationalized/date': 3.10.0
|
'@internationalized/date': 3.10.0
|
||||||
'@internationalized/number': 3.6.5
|
'@internationalized/number': 3.6.5
|
||||||
'@tanstack/vue-virtual': 3.13.12(vue@3.5.24(typescript@5.9.3))
|
'@tanstack/vue-virtual': 3.13.12(vue@3.5.25(typescript@5.9.3))
|
||||||
'@vueuse/core': 12.8.2(typescript@5.9.3)
|
'@vueuse/core': 12.8.2(typescript@5.9.3)
|
||||||
'@vueuse/shared': 12.8.2(typescript@5.9.3)
|
'@vueuse/shared': 12.8.2(typescript@5.9.3)
|
||||||
aria-hidden: 1.2.6
|
aria-hidden: 1.2.6
|
||||||
defu: 6.1.4
|
defu: 6.1.4
|
||||||
ohash: 2.0.11
|
ohash: 2.0.11
|
||||||
vue: 3.5.24(typescript@5.9.3)
|
vue: 3.5.25(typescript@5.9.3)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@vue/composition-api'
|
- '@vue/composition-api'
|
||||||
- typescript
|
- typescript
|
||||||
@ -3291,12 +3322,12 @@ snapshots:
|
|||||||
|
|
||||||
type-fest@0.20.2: {}
|
type-fest@0.20.2: {}
|
||||||
|
|
||||||
typescript-eslint@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3):
|
typescript-eslint@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/eslint-plugin': 8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
'@typescript-eslint/eslint-plugin': 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||||
'@typescript-eslint/parser': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
'@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||||
'@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3)
|
'@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3)
|
||||||
'@typescript-eslint/utils': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
'@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||||
eslint: 9.39.1(jiti@2.6.1)
|
eslint: 9.39.1(jiti@2.6.1)
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@ -3325,7 +3356,7 @@ snapshots:
|
|||||||
unplugin: 2.3.11
|
unplugin: 2.3.11
|
||||||
unplugin-utils: 0.3.1
|
unplugin-utils: 0.3.1
|
||||||
|
|
||||||
unplugin-auto-import@20.2.0(@vueuse/core@14.0.0(vue@3.5.24(typescript@5.9.3))):
|
unplugin-auto-import@20.2.0(@vueuse/core@14.0.0(vue@3.5.25(typescript@5.9.3))):
|
||||||
dependencies:
|
dependencies:
|
||||||
local-pkg: 1.1.2
|
local-pkg: 1.1.2
|
||||||
magic-string: 0.30.21
|
magic-string: 0.30.21
|
||||||
@ -3334,14 +3365,14 @@ snapshots:
|
|||||||
unplugin: 2.3.11
|
unplugin: 2.3.11
|
||||||
unplugin-utils: 0.3.1
|
unplugin-utils: 0.3.1
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@vueuse/core': 14.0.0(vue@3.5.24(typescript@5.9.3))
|
'@vueuse/core': 14.0.0(vue@3.5.25(typescript@5.9.3))
|
||||||
|
|
||||||
unplugin-utils@0.3.1:
|
unplugin-utils@0.3.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
pathe: 2.0.3
|
pathe: 2.0.3
|
||||||
picomatch: 4.0.3
|
picomatch: 4.0.3
|
||||||
|
|
||||||
unplugin-vue-components@30.0.0(@babel/parser@7.28.5)(vue@3.5.24(typescript@5.9.3)):
|
unplugin-vue-components@30.0.0(@babel/parser@7.28.5)(vue@3.5.25(typescript@5.9.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
chokidar: 4.0.3
|
chokidar: 4.0.3
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
@ -3351,7 +3382,7 @@ snapshots:
|
|||||||
tinyglobby: 0.2.15
|
tinyglobby: 0.2.15
|
||||||
unplugin: 2.3.11
|
unplugin: 2.3.11
|
||||||
unplugin-utils: 0.3.1
|
unplugin-utils: 0.3.1
|
||||||
vue: 3.5.24(typescript@5.9.3)
|
vue: 3.5.25(typescript@5.9.3)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@babel/parser': 7.28.5
|
'@babel/parser': 7.28.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@ -3386,9 +3417,9 @@ snapshots:
|
|||||||
|
|
||||||
vscode-uri@3.1.0: {}
|
vscode-uri@3.1.0: {}
|
||||||
|
|
||||||
vue-demi@0.14.10(vue@3.5.24(typescript@5.9.3)):
|
vue-demi@0.14.10(vue@3.5.25(typescript@5.9.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
vue: 3.5.24(typescript@5.9.3)
|
vue: 3.5.25(typescript@5.9.3)
|
||||||
|
|
||||||
vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@2.6.1)):
|
vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@2.6.1)):
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3415,17 +3446,17 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
vue-i18n@10.0.8(vue@3.5.24(typescript@5.9.3)):
|
vue-i18n@10.0.8(vue@3.5.25(typescript@5.9.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@intlify/core-base': 10.0.8
|
'@intlify/core-base': 10.0.8
|
||||||
'@intlify/shared': 10.0.8
|
'@intlify/shared': 10.0.8
|
||||||
'@vue/devtools-api': 6.6.4
|
'@vue/devtools-api': 6.6.4
|
||||||
vue: 3.5.24(typescript@5.9.3)
|
vue: 3.5.25(typescript@5.9.3)
|
||||||
|
|
||||||
vue-router@4.6.3(vue@3.5.24(typescript@5.9.3)):
|
vue-router@4.6.3(vue@3.5.25(typescript@5.9.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/devtools-api': 6.6.4
|
'@vue/devtools-api': 6.6.4
|
||||||
vue: 3.5.24(typescript@5.9.3)
|
vue: 3.5.25(typescript@5.9.3)
|
||||||
|
|
||||||
vue-sonner@2.0.9: {}
|
vue-sonner@2.0.9: {}
|
||||||
|
|
||||||
@ -3435,13 +3466,13 @@ snapshots:
|
|||||||
'@vue/language-core': 3.1.5(typescript@5.9.3)
|
'@vue/language-core': 3.1.5(typescript@5.9.3)
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
|
|
||||||
vue@3.5.24(typescript@5.9.3):
|
vue@3.5.25(typescript@5.9.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/compiler-dom': 3.5.24
|
'@vue/compiler-dom': 3.5.25
|
||||||
'@vue/compiler-sfc': 3.5.24
|
'@vue/compiler-sfc': 3.5.25
|
||||||
'@vue/runtime-dom': 3.5.24
|
'@vue/runtime-dom': 3.5.25
|
||||||
'@vue/server-renderer': 3.5.24(vue@3.5.24(typescript@5.9.3))
|
'@vue/server-renderer': 3.5.25(vue@3.5.25(typescript@5.9.3))
|
||||||
'@vue/shared': 3.5.24
|
'@vue/shared': 3.5.25
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
|
|
||||||
|
|||||||
2
pnpm-workspace.yaml
Normal file
2
pnpm-workspace.yaml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
packages:
|
||||||
|
- 'packages/*'
|
||||||
@ -1,6 +1,5 @@
|
|||||||
import axios, { type AxiosInstance, type AxiosResponse, type AxiosError } from 'axios'
|
import axios, { type AxiosInstance, type AxiosResponse, type AxiosError } from 'axios'
|
||||||
|
import { STATUS_CODE_SUCCESS } from '@/utils/constants'
|
||||||
const STATUS_CODE_SUCCESS = 200
|
|
||||||
|
|
||||||
export const API_URL = import.meta.env.VITE_APP_API || ''
|
export const API_URL = import.meta.env.VITE_APP_API || ''
|
||||||
|
|
||||||
@ -14,17 +13,6 @@ const instance: AxiosInstance = axios.create({
|
|||||||
withCredentials: false,
|
withCredentials: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Request interceptor
|
|
||||||
instance.interceptors.request.use(
|
|
||||||
config => {
|
|
||||||
// Add request interceptors here (e.g., auth tokens)
|
|
||||||
return config
|
|
||||||
},
|
|
||||||
(error: AxiosError) => {
|
|
||||||
return Promise.reject(error)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Response interceptor
|
// Response interceptor
|
||||||
instance.interceptors.response.use(
|
instance.interceptors.response.use(
|
||||||
(response: AxiosResponse) => {
|
(response: AxiosResponse) => {
|
||||||
@ -41,10 +29,4 @@ instance.interceptors.response.use(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Set locale for API requests
|
|
||||||
export const setLocaleApi = (locale: string) => {
|
|
||||||
instance.defaults.headers.common['lang'] = locale
|
|
||||||
}
|
|
||||||
|
|
||||||
export default instance
|
export default instance
|
||||||
|
|
||||||
|
|||||||
18
src/components/commons/arrow-left/ArrowLeftCommon.vue
Normal file
18
src/components/commons/arrow-left/ArrowLeftCommon.vue
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ArrowLeft } from 'lucide-vue-next'
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
click: []
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
emit('click')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Button variant="ghost" size="icon-lg" @click="handleClick">
|
||||||
|
<ArrowLeft />
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
1
src/components/commons/arrow-left/index.ts
Normal file
1
src/components/commons/arrow-left/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as ArrowLeftCommon } from './ArrowLeftCommon.vue'
|
||||||
4
src/components/commons/index.ts
Normal file
4
src/components/commons/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export * from './layout'
|
||||||
|
export * from './logo'
|
||||||
|
export * from './password-form'
|
||||||
|
|
||||||
@ -6,9 +6,9 @@ const router = useRouter()
|
|||||||
|
|
||||||
// Navigation items for bottom tab bar
|
// Navigation items for bottom tab bar
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{ name: 'Home', icon: Home, route: '/', label: 'Home' },
|
{ name: 'UTXO', icon: Home, route: '/', label: 'UTXO' },
|
||||||
{ name: 'Wallet', icon: Wallet, route: '/wallet', label: 'Wallet' },
|
{ name: 'Wallet', icon: Wallet, route: '/wallet', label: 'Wallet' },
|
||||||
{ name: 'History', icon: History, route: '/history', label: 'History' },
|
{ name: 'History', icon: History, route: '/transaction-history', label: 'History' },
|
||||||
{ name: 'Settings', icon: Settings, route: '/settings', label: 'Settings' },
|
{ name: 'Settings', icon: Settings, route: '/settings', label: 'Settings' },
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ const isActiveRoute = (routePath: string) => {
|
|||||||
alt="Neptune"
|
alt="Neptune"
|
||||||
class="h-8 w-8 rounded-lg object-cover"
|
class="h-8 w-8 rounded-lg object-cover"
|
||||||
/>
|
/>
|
||||||
<span class="text-lg font-semibold text-foreground">Neptune</span>
|
<span class="text-lg font-semibold text-foreground">Neptune Privacy</span>
|
||||||
</div>
|
</div>
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
25
src/components/commons/logo/Logo.vue
Normal file
25
src/components/commons/logo/Logo.vue
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { useColorMode } from '@vueuse/core'
|
||||||
|
|
||||||
|
const mode = useColorMode()
|
||||||
|
const isDark = computed(() => mode.value === 'dark')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="relative">
|
||||||
|
<div
|
||||||
|
v-if="isDark"
|
||||||
|
class="absolute -inset-1 animate-pulse rounded-full bg-linear-to-r from-primary via-accent to-primary opacity-75 blur-xl"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="relative flex h-24 w-24 items-center justify-center rounded-full bg-linear-to-br from-primary to-accent shadow-2xl ring-4 ring-background"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="@/assets/imgs/neptune_logo.jpg"
|
||||||
|
alt="Neptune Privacy"
|
||||||
|
class="h-20 w-20 rounded-full object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
1
src/components/commons/logo/index.ts
Normal file
1
src/components/commons/logo/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as Logo } from './Logo.vue'
|
||||||
209
src/components/commons/password-form/PasswordForm.vue
Normal file
209
src/components/commons/password-form/PasswordForm.vue
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import { useForm } from '@tanstack/vue-form'
|
||||||
|
import { z } from 'zod'
|
||||||
|
import { Eye, EyeOff, Lock, Loader2, ChevronLeft } from 'lucide-vue-next'
|
||||||
|
import { Card, CardContent } from '@/components/ui/card'
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
import { Input } from '@/components/ui/input'
|
||||||
|
import { Label } from '@/components/ui/label'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
label?: string
|
||||||
|
placeholder?: string
|
||||||
|
buttonText?: string
|
||||||
|
hideBackButton?: boolean
|
||||||
|
backButtonText?: string
|
||||||
|
loading?: boolean
|
||||||
|
error?: boolean
|
||||||
|
errorMessage?: string
|
||||||
|
validateFormat?: boolean
|
||||||
|
autocomplete?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
label: 'Password',
|
||||||
|
placeholder: 'Enter your password',
|
||||||
|
buttonText: 'Submit',
|
||||||
|
backButtonText: 'Back',
|
||||||
|
hideBackButton: true,
|
||||||
|
loading: false,
|
||||||
|
error: false,
|
||||||
|
errorMessage: 'Incorrect password. Please try again.',
|
||||||
|
validateFormat: false,
|
||||||
|
autocomplete: 'current-password',
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [password: string]
|
||||||
|
back: []
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const showPassword = ref(false)
|
||||||
|
|
||||||
|
// Password strength calculation
|
||||||
|
const passwordStrength = computed(() => {
|
||||||
|
const password = form.state.values?.password
|
||||||
|
if (!password || !props.validateFormat) return { level: 0, text: '', color: '' }
|
||||||
|
|
||||||
|
let strength = 0
|
||||||
|
const checks = {
|
||||||
|
length: password.length >= 8,
|
||||||
|
uppercase: /[A-Z]/.test(password),
|
||||||
|
lowercase: /[a-z]/.test(password),
|
||||||
|
number: /[0-9]/.test(password),
|
||||||
|
special: /[!@#$%^&*(),.?":{}|<>]/.test(password),
|
||||||
|
}
|
||||||
|
strength = Object.values(checks).filter(Boolean).length
|
||||||
|
|
||||||
|
if (strength <= 2) return { level: 1, text: 'Weak', color: 'hsl(var(--destructive))' }
|
||||||
|
if (strength <= 3) return { level: 2, text: 'Medium', color: 'hsl(48 96% 53%)' } // yellow
|
||||||
|
if (strength <= 4) return { level: 3, text: 'Good', color: 'hsl(221 83% 53%)' } // blue
|
||||||
|
return { level: 4, text: 'Strong', color: 'hsl(142 76% 36%)' } // green
|
||||||
|
})
|
||||||
|
|
||||||
|
// Custom password schema with strength validation
|
||||||
|
const createPasswordSchema = () => {
|
||||||
|
let schema = z.string().min(1, 'Password is required')
|
||||||
|
|
||||||
|
if (props.validateFormat) {
|
||||||
|
schema = schema
|
||||||
|
.min(8, 'Password must be at least 8 characters')
|
||||||
|
.refine((val) => {
|
||||||
|
const checks = {
|
||||||
|
uppercase: /[A-Z]/.test(val),
|
||||||
|
lowercase: /[a-z]/.test(val),
|
||||||
|
number: /[0-9]/.test(val),
|
||||||
|
}
|
||||||
|
const strength = Object.values(checks).filter(Boolean).length
|
||||||
|
return strength >= 2 // Medium level minimum
|
||||||
|
}, 'Password is too weak. Use uppercase, lowercase, and numbers.')
|
||||||
|
}
|
||||||
|
|
||||||
|
return schema
|
||||||
|
}
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
defaultValues: {
|
||||||
|
password: '',
|
||||||
|
},
|
||||||
|
validators: {
|
||||||
|
onChange: z.object({
|
||||||
|
password: createPasswordSchema(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
onSubmit: async ({ value }) => {
|
||||||
|
emit('submit', value.password)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleBack = () => {
|
||||||
|
emit('back')
|
||||||
|
}
|
||||||
|
|
||||||
|
function isInvalid(field: any) {
|
||||||
|
return field.state.meta.isTouched && !field.state.meta.isValid
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form @submit.prevent="form.handleSubmit">
|
||||||
|
<form.Field name="password">
|
||||||
|
<template #default="{ field }">
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="space-y-2">
|
||||||
|
<Label :for="field.name" class="text-base">{{ label }}</Label>
|
||||||
|
<div class="relative">
|
||||||
|
<Lock
|
||||||
|
:size="20"
|
||||||
|
class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
:id="field.name"
|
||||||
|
:name="field.name"
|
||||||
|
v-model="field.state.value"
|
||||||
|
:type="showPassword ? 'text' : 'password'"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
class="h-12 pl-11 pr-11 text-base"
|
||||||
|
:class="{ 'border-destructive': error || isInvalid(field) }"
|
||||||
|
:autocomplete="autocomplete"
|
||||||
|
@blur="field.handleBlur"
|
||||||
|
@input="(e: Event) => {
|
||||||
|
field.handleChange((e.target as HTMLInputElement).value)
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground transition-colors hover:text-foreground"
|
||||||
|
@click="showPassword = !showPassword"
|
||||||
|
>
|
||||||
|
<Eye v-if="!showPassword" :size="20" />
|
||||||
|
<EyeOff v-else :size="20" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Error Message -->
|
||||||
|
<p v-if="error" class="text-sm text-destructive">
|
||||||
|
{{ errorMessage }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Validation Error from Zod -->
|
||||||
|
<p v-else-if="isInvalid(field)" class="text-sm text-destructive">
|
||||||
|
{{ field.state.meta.errors?.[0]?.message }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Password Strength Indicator -->
|
||||||
|
<div v-if="validateFormat && field.state.value" class="space-y-2 pt-1">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div class="h-1 flex-1 overflow-hidden rounded-full bg-muted">
|
||||||
|
<div
|
||||||
|
class="h-full transition-all duration-300 ease-in-out"
|
||||||
|
:style="{
|
||||||
|
width: `${(passwordStrength.level / 4) * 100}%`,
|
||||||
|
backgroundColor: passwordStrength.color,
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="min-w-[60px] text-right text-xs font-semibold"
|
||||||
|
:style="{ color: passwordStrength.color }"
|
||||||
|
>
|
||||||
|
{{ passwordStrength.text }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-xs text-muted-foreground">
|
||||||
|
Use at least 8 characters with uppercase, lowercase, and numbers.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Buttons -->
|
||||||
|
<div class="flex flex-col gap-3">
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
size="lg"
|
||||||
|
class="h-12 w-full text-base font-semibold"
|
||||||
|
:disabled="!field.state.value || loading"
|
||||||
|
>
|
||||||
|
<Loader2 v-if="loading" :size="20" class="mr-2 animate-spin" />
|
||||||
|
{{ buttonText }}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
v-if="!hideBackButton"
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
size="lg"
|
||||||
|
class="h-12 w-full text-base"
|
||||||
|
@click="handleBack"
|
||||||
|
>
|
||||||
|
<ChevronLeft :size="20" class="mr-2" />
|
||||||
|
{{ backButtonText }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</form.Field>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
1
src/components/commons/password-form/index.ts
Normal file
1
src/components/commons/password-form/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as PasswordForm } from './PasswordForm.vue'
|
||||||
15
src/components/ui/dialog/Dialog.vue
Normal file
15
src/components/ui/dialog/Dialog.vue
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { DialogRootEmits, DialogRootProps } from "reka-ui"
|
||||||
|
import { DialogRoot, useForwardPropsEmits } from "reka-ui"
|
||||||
|
|
||||||
|
const props = defineProps<DialogRootProps>()
|
||||||
|
const emits = defineEmits<DialogRootEmits>()
|
||||||
|
|
||||||
|
const forwarded = useForwardPropsEmits(props, emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DialogRoot v-bind="forwarded">
|
||||||
|
<slot />
|
||||||
|
</DialogRoot>
|
||||||
|
</template>
|
||||||
12
src/components/ui/dialog/DialogClose.vue
Normal file
12
src/components/ui/dialog/DialogClose.vue
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { DialogCloseProps } from "reka-ui"
|
||||||
|
import { DialogClose } from "reka-ui"
|
||||||
|
|
||||||
|
const props = defineProps<DialogCloseProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DialogClose v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</DialogClose>
|
||||||
|
</template>
|
||||||
46
src/components/ui/dialog/DialogContent.vue
Normal file
46
src/components/ui/dialog/DialogContent.vue
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { DialogContentEmits, DialogContentProps } from "reka-ui"
|
||||||
|
import type { HTMLAttributes } from "vue"
|
||||||
|
import { reactiveOmit } from "@vueuse/core"
|
||||||
|
import { X } from "lucide-vue-next"
|
||||||
|
import {
|
||||||
|
DialogClose,
|
||||||
|
DialogContent,
|
||||||
|
DialogOverlay,
|
||||||
|
DialogPortal,
|
||||||
|
useForwardPropsEmits,
|
||||||
|
} from "reka-ui"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const props = defineProps<DialogContentProps & { class?: HTMLAttributes["class"] }>()
|
||||||
|
const emits = defineEmits<DialogContentEmits>()
|
||||||
|
|
||||||
|
const delegatedProps = reactiveOmit(props, "class")
|
||||||
|
|
||||||
|
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DialogPortal>
|
||||||
|
<DialogOverlay
|
||||||
|
class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
||||||
|
/>
|
||||||
|
<DialogContent
|
||||||
|
v-bind="forwarded"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
||||||
|
props.class,
|
||||||
|
)"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
|
||||||
|
<DialogClose
|
||||||
|
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
|
||||||
|
>
|
||||||
|
<X class="w-4 h-4" />
|
||||||
|
<span class="sr-only">Close</span>
|
||||||
|
</DialogClose>
|
||||||
|
</DialogContent>
|
||||||
|
</DialogPortal>
|
||||||
|
</template>
|
||||||
22
src/components/ui/dialog/DialogDescription.vue
Normal file
22
src/components/ui/dialog/DialogDescription.vue
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { DialogDescriptionProps } from "reka-ui"
|
||||||
|
import type { HTMLAttributes } from "vue"
|
||||||
|
import { reactiveOmit } from "@vueuse/core"
|
||||||
|
import { DialogDescription, useForwardProps } from "reka-ui"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const props = defineProps<DialogDescriptionProps & { class?: HTMLAttributes["class"] }>()
|
||||||
|
|
||||||
|
const delegatedProps = reactiveOmit(props, "class")
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DialogDescription
|
||||||
|
v-bind="forwardedProps"
|
||||||
|
:class="cn('text-sm text-muted-foreground', props.class)"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DialogDescription>
|
||||||
|
</template>
|
||||||
19
src/components/ui/dialog/DialogFooter.vue
Normal file
19
src/components/ui/dialog/DialogFooter.vue
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { HTMLAttributes } from "vue"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const props = defineProps<{ class?: HTMLAttributes["class"] }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-x-2',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
16
src/components/ui/dialog/DialogHeader.vue
Normal file
16
src/components/ui/dialog/DialogHeader.vue
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { HTMLAttributes } from "vue"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
class?: HTMLAttributes["class"]
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="cn('flex flex-col gap-y-1.5 text-center sm:text-left', props.class)"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
55
src/components/ui/dialog/DialogScrollContent.vue
Normal file
55
src/components/ui/dialog/DialogScrollContent.vue
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { DialogContentEmits, DialogContentProps } from "reka-ui"
|
||||||
|
import type { HTMLAttributes } from "vue"
|
||||||
|
import { reactiveOmit } from "@vueuse/core"
|
||||||
|
import { X } from "lucide-vue-next"
|
||||||
|
import {
|
||||||
|
DialogClose,
|
||||||
|
DialogContent,
|
||||||
|
DialogOverlay,
|
||||||
|
DialogPortal,
|
||||||
|
useForwardPropsEmits,
|
||||||
|
} from "reka-ui"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const props = defineProps<DialogContentProps & { class?: HTMLAttributes["class"] }>()
|
||||||
|
const emits = defineEmits<DialogContentEmits>()
|
||||||
|
|
||||||
|
const delegatedProps = reactiveOmit(props, "class")
|
||||||
|
|
||||||
|
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DialogPortal>
|
||||||
|
<DialogOverlay
|
||||||
|
class="fixed inset-0 z-50 grid place-items-center overflow-y-auto bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
||||||
|
>
|
||||||
|
<DialogContent
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'relative z-50 grid w-full max-w-lg my-8 gap-4 border border-border bg-background p-6 shadow-lg duration-200 sm:rounded-lg md:w-full',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
v-bind="forwarded"
|
||||||
|
@pointer-down-outside="(event) => {
|
||||||
|
const originalEvent = event.detail.originalEvent;
|
||||||
|
const target = originalEvent.target as HTMLElement;
|
||||||
|
if (originalEvent.offsetX > target.clientWidth || originalEvent.offsetY > target.clientHeight) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
|
||||||
|
<DialogClose
|
||||||
|
class="absolute top-4 right-4 p-0.5 transition-colors rounded-md hover:bg-secondary"
|
||||||
|
>
|
||||||
|
<X class="w-4 h-4" />
|
||||||
|
<span class="sr-only">Close</span>
|
||||||
|
</DialogClose>
|
||||||
|
</DialogContent>
|
||||||
|
</DialogOverlay>
|
||||||
|
</DialogPortal>
|
||||||
|
</template>
|
||||||
27
src/components/ui/dialog/DialogTitle.vue
Normal file
27
src/components/ui/dialog/DialogTitle.vue
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { DialogTitleProps } from "reka-ui"
|
||||||
|
import type { HTMLAttributes } from "vue"
|
||||||
|
import { reactiveOmit } from "@vueuse/core"
|
||||||
|
import { DialogTitle, useForwardProps } from "reka-ui"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const props = defineProps<DialogTitleProps & { class?: HTMLAttributes["class"] }>()
|
||||||
|
|
||||||
|
const delegatedProps = reactiveOmit(props, "class")
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DialogTitle
|
||||||
|
v-bind="forwardedProps"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'text-lg font-semibold leading-none tracking-tight',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DialogTitle>
|
||||||
|
</template>
|
||||||
12
src/components/ui/dialog/DialogTrigger.vue
Normal file
12
src/components/ui/dialog/DialogTrigger.vue
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { DialogTriggerProps } from "reka-ui"
|
||||||
|
import { DialogTrigger } from "reka-ui"
|
||||||
|
|
||||||
|
const props = defineProps<DialogTriggerProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DialogTrigger v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</DialogTrigger>
|
||||||
|
</template>
|
||||||
9
src/components/ui/dialog/index.ts
Normal file
9
src/components/ui/dialog/index.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export { default as Dialog } from "./Dialog.vue"
|
||||||
|
export { default as DialogClose } from "./DialogClose.vue"
|
||||||
|
export { default as DialogContent } from "./DialogContent.vue"
|
||||||
|
export { default as DialogDescription } from "./DialogDescription.vue"
|
||||||
|
export { default as DialogFooter } from "./DialogFooter.vue"
|
||||||
|
export { default as DialogHeader } from "./DialogHeader.vue"
|
||||||
|
export { default as DialogScrollContent } from "./DialogScrollContent.vue"
|
||||||
|
export { default as DialogTitle } from "./DialogTitle.vue"
|
||||||
|
export { default as DialogTrigger } from "./DialogTrigger.vue"
|
||||||
@ -22,3 +22,25 @@ const modelValue = useVModel(props, "modelValue", emits, {
|
|||||||
<template>
|
<template>
|
||||||
<input v-model="modelValue" :class="cn('flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50', props.class)">
|
<input v-model="modelValue" :class="cn('flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50', props.class)">
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* Hide default browser password reveal button */
|
||||||
|
input::-ms-reveal,
|
||||||
|
input::-ms-clear {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide password reveal button in Edge */
|
||||||
|
input[type="password"]::-ms-reveal {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide password reveal button in Chrome/Safari */
|
||||||
|
input[type="password"]::-webkit-contacts-auto-fill-button,
|
||||||
|
input[type="password"]::-webkit-credentials-auto-fill-button {
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
24
src/components/ui/textarea/Textarea.vue
Normal file
24
src/components/ui/textarea/Textarea.vue
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { HTMLAttributes } from "vue"
|
||||||
|
import { useVModel } from "@vueuse/core"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
class?: HTMLAttributes["class"]
|
||||||
|
defaultValue?: string | number
|
||||||
|
modelValue?: string | number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emits = defineEmits<{
|
||||||
|
(e: "update:modelValue", payload: string | number): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const modelValue = useVModel(props, "modelValue", emits, {
|
||||||
|
passive: true,
|
||||||
|
defaultValue: props.defaultValue,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<textarea v-model="modelValue" :class="cn('flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50', props.class)" />
|
||||||
|
</template>
|
||||||
1
src/components/ui/textarea/index.ts
Normal file
1
src/components/ui/textarea/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as Textarea } from "./Textarea.vue"
|
||||||
2
src/composables/index.ts
Normal file
2
src/composables/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { useAuthRouting } from './useAuthRouting'
|
||||||
|
export { useMobile } from './useMobile'
|
||||||
23
src/composables/useAuthRouting.ts
Normal file
23
src/composables/useAuthRouting.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { useAuthStore } from '@/stores'
|
||||||
|
|
||||||
|
export function useAuthRouting() {
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
|
const goToCreate = () => {
|
||||||
|
authStore.setState('create')
|
||||||
|
}
|
||||||
|
|
||||||
|
const goToRecover = () => {
|
||||||
|
authStore.setState('recovery')
|
||||||
|
}
|
||||||
|
|
||||||
|
const goBackToWelcome = () => {
|
||||||
|
authStore.setState('welcome')
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
goToCreate,
|
||||||
|
goToRecover,
|
||||||
|
goBackToWelcome,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,69 +6,70 @@ import { useMediaQuery } from '@vueuse/core'
|
|||||||
*/
|
*/
|
||||||
export function useMobile() {
|
export function useMobile() {
|
||||||
const isMobile = useMediaQuery('(max-width: 768px)')
|
const isMobile = useMediaQuery('(max-width: 768px)')
|
||||||
return isMobile
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
const isIOS = computed(() => {
|
||||||
* Detect if device is iOS
|
|
||||||
*/
|
|
||||||
export function useIsIOS() {
|
|
||||||
return computed(() => {
|
|
||||||
if (typeof window === 'undefined') return false
|
if (typeof window === 'undefined') return false
|
||||||
return /iPhone|iPad|iPod/i.test(navigator.userAgent)
|
return /iPhone|iPad|iPod/i.test(navigator.userAgent)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get safe area insets for notched devices
|
* Get safe area insets for notched devices
|
||||||
*/
|
*/
|
||||||
export function useSafeArea() {
|
function useSafeArea() {
|
||||||
|
return {
|
||||||
|
top: computed(() => {
|
||||||
|
if (typeof window === 'undefined') return 0
|
||||||
|
return parseInt(
|
||||||
|
getComputedStyle(document.documentElement).getPropertyValue('env(safe-area-inset-top)') ||
|
||||||
|
'0'
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
bottom: computed(() => {
|
||||||
|
if (typeof window === 'undefined') return 0
|
||||||
|
return parseInt(
|
||||||
|
getComputedStyle(document.documentElement).getPropertyValue(
|
||||||
|
'env(safe-area-inset-bottom)'
|
||||||
|
) || '0'
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent pull-to-refresh on mobile browsers
|
||||||
|
*/
|
||||||
|
function usePreventPullToRefresh() {
|
||||||
|
if (typeof window === 'undefined') return
|
||||||
|
|
||||||
|
let touchStartY = 0
|
||||||
|
|
||||||
|
document.addEventListener(
|
||||||
|
'touchstart',
|
||||||
|
e => {
|
||||||
|
touchStartY = e.touches[0]?.clientY ?? 0
|
||||||
|
},
|
||||||
|
{ passive: false }
|
||||||
|
)
|
||||||
|
|
||||||
|
document.addEventListener(
|
||||||
|
'touchmove',
|
||||||
|
e => {
|
||||||
|
const touchY = e.touches[0]?.clientY ?? 0
|
||||||
|
const touchYDelta = touchY - touchStartY
|
||||||
|
|
||||||
|
// Prevent pull-to-refresh if scrolling up at the top
|
||||||
|
if (touchYDelta > 0 && window.scrollY === 0) {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ passive: false }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
top: computed(() => {
|
isMobile,
|
||||||
if (typeof window === 'undefined') return 0
|
isIOS,
|
||||||
return parseInt(
|
useSafeArea,
|
||||||
getComputedStyle(document.documentElement).getPropertyValue('env(safe-area-inset-top)') ||
|
usePreventPullToRefresh,
|
||||||
'0'
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
bottom: computed(() => {
|
|
||||||
if (typeof window === 'undefined') return 0
|
|
||||||
return parseInt(
|
|
||||||
getComputedStyle(document.documentElement).getPropertyValue(
|
|
||||||
'env(safe-area-inset-bottom)'
|
|
||||||
) || '0'
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Prevent pull-to-refresh on mobile browsers
|
|
||||||
*/
|
|
||||||
export function usePreventPullToRefresh() {
|
|
||||||
if (typeof window === 'undefined') return
|
|
||||||
|
|
||||||
let touchStartY = 0
|
|
||||||
|
|
||||||
document.addEventListener(
|
|
||||||
'touchstart',
|
|
||||||
e => {
|
|
||||||
touchStartY = e.touches[0]?.clientY ?? 0
|
|
||||||
},
|
|
||||||
{ passive: false }
|
|
||||||
)
|
|
||||||
|
|
||||||
document.addEventListener(
|
|
||||||
'touchmove',
|
|
||||||
e => {
|
|
||||||
const touchY = e.touches[0]?.clientY ?? 0
|
|
||||||
const touchYDelta = touchY - touchStartY
|
|
||||||
|
|
||||||
// Prevent pull-to-refresh if scrolling up at the top
|
|
||||||
if (touchYDelta > 0 && window.scrollY === 0) {
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ passive: false }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ const router = createRouter({
|
|||||||
|
|
||||||
// Navigation guards
|
// Navigation guards
|
||||||
router.beforeEach((to, _from, next) => {
|
router.beforeEach((to, _from, next) => {
|
||||||
const hasWallet = false // useNeptuneStore().hasWallet
|
const hasWallet = true // useNeptuneStore().hasWallet
|
||||||
|
|
||||||
if (to.meta.requiresAuth) {
|
if (to.meta.requiresAuth) {
|
||||||
if (!hasWallet) {
|
if (!hasWallet) {
|
||||||
|
|||||||
@ -14,10 +14,14 @@ export const routes: RouteRecordRaw[] = [
|
|||||||
component: Layout,
|
component: Layout,
|
||||||
meta: { requiresAuth: true },
|
meta: { requiresAuth: true },
|
||||||
children: [
|
children: [
|
||||||
// { path: '/home', name: 'home', component: Pages.HomePage },
|
{ path: '/utxo', name: 'utxo', component: Pages.UTXOPage },
|
||||||
{ path: '/wallet', name: 'wallet', component: Pages.WalletPage },
|
{ path: 'wallet', name: 'wallet', component: Pages.WalletPage },
|
||||||
// { path: '/history', name: 'history', component: () => import('@/views/HistoryView.vue') },
|
{
|
||||||
// { path: '/settings', name: 'settings', component: () => import('@/views/SettingsView.vue') },
|
path: '/transaction-history',
|
||||||
|
name: 'transaction-history',
|
||||||
|
component: Pages.TransactionHistoryPage,
|
||||||
|
},
|
||||||
|
{ path: '/settings', name: 'settings', component: Pages.SettingsPage },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,51 +1,22 @@
|
|||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
export type AuthState = 'onboarding' | 'login' | 'create' | 'recovery' | 'confirm' | 'complete'
|
export type AuthState = 'welcome' | 'create' | 'recovery'
|
||||||
|
|
||||||
export const useAuthStore = defineStore('auth', () => {
|
export const useAuthStore = defineStore('auth', () => {
|
||||||
// State
|
// State
|
||||||
const currentState = ref<AuthState>('onboarding')
|
const currentState = ref<AuthState>('welcome')
|
||||||
const previousState = ref<AuthState | null>(null)
|
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
const getCurrentState = computed(() => currentState.value)
|
const getCurrentState = computed(() => currentState.value)
|
||||||
const getPreviousState = computed(() => previousState.value)
|
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
const setState = (state: AuthState) => {
|
const setState = (state: AuthState) => {
|
||||||
previousState.value = currentState.value
|
|
||||||
currentState.value = state
|
currentState.value = state
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToCreate = () => {
|
|
||||||
setState('create')
|
|
||||||
}
|
|
||||||
|
|
||||||
const goToLogin = () => {
|
|
||||||
setState('login')
|
|
||||||
}
|
|
||||||
|
|
||||||
const goToRecover = () => {
|
|
||||||
setState('recovery')
|
|
||||||
}
|
|
||||||
|
|
||||||
const goBack = () => {
|
|
||||||
if (previousState.value) {
|
|
||||||
setState(previousState.value)
|
|
||||||
} else {
|
|
||||||
setState('onboarding')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentState,
|
|
||||||
getCurrentState,
|
getCurrentState,
|
||||||
getPreviousState,
|
|
||||||
setState,
|
setState,
|
||||||
goToCreate,
|
|
||||||
goToLogin,
|
|
||||||
goToRecover,
|
|
||||||
goBack,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,75 +2,54 @@
|
|||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useAuthStore } from '@/stores'
|
import { useAuthStore } from '@/stores'
|
||||||
import { Toaster } from 'vue-sonner'
|
import { Toaster } from 'vue-sonner'
|
||||||
import { CreateWalletFlow, LoginTab, OnboardingTab, RecoverWalletFlow } from './components'
|
import { WelcomeTab, CreateWalletFlow, RecoverWalletFlow } from './components'
|
||||||
|
import { useAuthRouting } from '@/composables'
|
||||||
|
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const { goToCreate, goToRecover, goBackToWelcome } = useAuthRouting()
|
||||||
// const neptuneWallet = useNeptuneWallet()
|
|
||||||
|
|
||||||
const currentState = computed(() => authStore.getCurrentState)
|
const currentState = computed(() => authStore.getCurrentState)
|
||||||
|
|
||||||
const handleGoToCreate = () => {
|
const handleGoToCreate = () => {
|
||||||
authStore.goToCreate()
|
goToCreate()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleGoToRecover = () => {
|
const handleGoToRecover = () => {
|
||||||
authStore.goToRecover()
|
goToRecover()
|
||||||
}
|
|
||||||
|
|
||||||
const handlePasswordSubmit = async (password: string) => {
|
|
||||||
const loginRef = document.querySelector('login-tab') as any
|
|
||||||
try {
|
|
||||||
// TODO: Decrypt keystore with password
|
|
||||||
// await neptuneWallet.decryptKeystore(password)
|
|
||||||
// Mock success for now
|
|
||||||
router.push({ name: 'home' })
|
|
||||||
} catch (err) {
|
|
||||||
if (loginRef) {
|
|
||||||
loginRef.setError(true)
|
|
||||||
loginRef.setLoading(false)
|
|
||||||
}
|
|
||||||
console.error('Failed to unlock wallet:', err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAccessWallet = () => {
|
const handleAccessWallet = () => {
|
||||||
router.push({ name: 'wallet' })
|
router.push({ name: 'wallet' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleGoToWelcome = () => {
|
||||||
authStore.goBack()
|
goBackToWelcome()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="min-h-screen">
|
<div class="min-h-screen">
|
||||||
<!-- Onboarding: Welcome screen -->
|
<!-- Welcome: Welcome screen with login -->
|
||||||
<OnboardingTab
|
<WelcomeTab
|
||||||
v-if="currentState === 'onboarding'"
|
v-if="currentState === 'welcome'"
|
||||||
@go-to-create="handleGoToCreate"
|
@go-to-create="handleGoToCreate"
|
||||||
@go-to-recover="handleGoToRecover"
|
@go-to-recover="handleGoToRecover"
|
||||||
/>
|
@go-to-welcome="handleGoToWelcome"
|
||||||
|
|
||||||
<!-- Login: Unlock existing wallet -->
|
|
||||||
<LoginTab
|
|
||||||
v-else-if="currentState === 'login'"
|
|
||||||
@go-to-create="handleGoToCreate"
|
|
||||||
@submit="handlePasswordSubmit"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Create: New wallet creation flow -->
|
<!-- Create: New wallet creation flow -->
|
||||||
<CreateWalletFlow
|
<CreateWalletFlow
|
||||||
v-else-if="currentState === 'create'"
|
v-else-if="currentState === 'create'"
|
||||||
@navigate-to-recover="handleGoToRecover"
|
@go-to-recover="handleGoToRecover"
|
||||||
@access-wallet="handleAccessWallet"
|
@access-wallet="handleAccessWallet"
|
||||||
|
@go-to-welcome="handleGoToWelcome"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Recovery: Recover wallet from seed phrase -->
|
<!-- Recovery: Recover wallet from seed phrase -->
|
||||||
<RecoverWalletFlow
|
<RecoverWalletFlow
|
||||||
v-else-if="currentState === 'recovery'"
|
v-else-if="currentState === 'recovery'"
|
||||||
@cancel="handleCancel"
|
@cancel="handleGoToWelcome"
|
||||||
@access-wallet="handleAccessWallet"
|
@access-wallet="handleAccessWallet"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@ -87,23 +87,16 @@ const handleNext = () => {
|
|||||||
if (correctCount.value >= totalQuestions) {
|
if (correctCount.value >= totalQuestions) {
|
||||||
emit('next')
|
emit('next')
|
||||||
} else {
|
} else {
|
||||||
setTimeout(() => {
|
// Move to next question
|
||||||
showResult.value = false
|
showResult.value = false
|
||||||
selectedAnswer.value = ''
|
selectedAnswer.value = ''
|
||||||
const newQuiz = generateQuiz()
|
const newQuiz = generateQuiz()
|
||||||
if (newQuiz) {
|
if (newQuiz) quizData.value = newQuiz
|
||||||
quizData.value = newQuiz
|
|
||||||
}
|
|
||||||
}, 800)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
showResult.value = false
|
showResult.value = false
|
||||||
selectedAnswer.value = ''
|
selectedAnswer.value = ''
|
||||||
const newQuiz = generateQuiz()
|
|
||||||
if (newQuiz) {
|
|
||||||
quizData.value = newQuiz
|
|
||||||
}
|
|
||||||
}, 1500)
|
}, 1500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,9 +118,7 @@ onMounted(() => {
|
|||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="space-y-2 text-center">
|
<div class="space-y-2 text-center">
|
||||||
<h1 class="text-2xl font-bold text-foreground">Confirm Recovery Phrase</h1>
|
<h1 class="text-2xl font-bold text-foreground">Confirm Recovery Phrase</h1>
|
||||||
<p class="text-sm text-muted-foreground">
|
<p class="text-sm text-muted-foreground">Select the correct word for each position</p>
|
||||||
Select the correct word for each position
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
@ -159,7 +150,7 @@ onMounted(() => {
|
|||||||
<!-- Quiz Section -->
|
<!-- Quiz Section -->
|
||||||
<div v-if="quizData" class="space-y-5">
|
<div v-if="quizData" class="space-y-5">
|
||||||
<!-- Question Card -->
|
<!-- Question Card -->
|
||||||
<Card class="border-2 border-primary/30 bg-gradient-to-br from-primary/5 to-accent/5">
|
<Card class="border-2 border-primary/30 bg-linear-to-br from-primary/5 to-accent/5">
|
||||||
<CardContent class="py-8">
|
<CardContent class="py-8">
|
||||||
<h2 class="text-center text-xl font-bold text-foreground">
|
<h2 class="text-center text-xl font-bold text-foreground">
|
||||||
Select word
|
Select word
|
||||||
@ -190,10 +181,11 @@ onMounted(() => {
|
|||||||
:class="{
|
:class="{
|
||||||
'border-primary bg-primary/10 shadow-lg': selectedAnswer === option && !showResult,
|
'border-primary bg-primary/10 shadow-lg': selectedAnswer === option && !showResult,
|
||||||
'border-green-500 bg-green-500/10':
|
'border-green-500 bg-green-500/10':
|
||||||
showResult && option === quizData.correctWord,
|
showResult && isCorrect && option === quizData.correctWord,
|
||||||
'border-destructive bg-destructive/10':
|
'border-destructive bg-destructive/10':
|
||||||
showResult && selectedAnswer === option && option !== quizData.correctWord,
|
showResult && selectedAnswer === option && option !== quizData.correctWord,
|
||||||
'border-border hover:border-primary/50 hover:bg-accent': !selectedAnswer || (selectedAnswer !== option && !showResult),
|
'border-border hover:border-primary/50 hover:bg-accent':
|
||||||
|
!selectedAnswer || (selectedAnswer !== option && !showResult),
|
||||||
}"
|
}"
|
||||||
:disabled="showResult"
|
:disabled="showResult"
|
||||||
@click="handleAnswerSelect(option)"
|
@click="handleAnswerSelect(option)"
|
||||||
@ -204,9 +196,12 @@ onMounted(() => {
|
|||||||
class="flex h-8 w-8 items-center justify-center rounded-full text-xs font-bold transition-colors"
|
class="flex h-8 w-8 items-center justify-center rounded-full text-xs font-bold transition-colors"
|
||||||
:class="{
|
:class="{
|
||||||
'bg-primary text-primary-foreground': selectedAnswer === option && !showResult,
|
'bg-primary text-primary-foreground': selectedAnswer === option && !showResult,
|
||||||
'bg-green-500 text-white': showResult && option === quizData.correctWord,
|
'bg-green-500 text-white':
|
||||||
'bg-destructive text-destructive-foreground': showResult && selectedAnswer === option && option !== quizData.correctWord,
|
showResult && isCorrect && option === quizData.correctWord,
|
||||||
'bg-muted text-muted-foreground': !selectedAnswer || (selectedAnswer !== option && !showResult),
|
'bg-destructive text-destructive-foreground':
|
||||||
|
showResult && selectedAnswer === option && option !== quizData.correctWord,
|
||||||
|
'bg-muted text-muted-foreground':
|
||||||
|
!selectedAnswer || (selectedAnswer !== option && !showResult),
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
{{ String.fromCharCode(65 + index) }}
|
{{ String.fromCharCode(65 + index) }}
|
||||||
@ -217,7 +212,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
<!-- Check/X Icon -->
|
<!-- Check/X Icon -->
|
||||||
<CheckCircle2
|
<CheckCircle2
|
||||||
v-if="showResult && option === quizData.correctWord"
|
v-if="showResult && isCorrect && option === quizData.correctWord"
|
||||||
:size="24"
|
:size="24"
|
||||||
class="text-green-500"
|
class="text-green-500"
|
||||||
/>
|
/>
|
||||||
@ -232,22 +227,15 @@ onMounted(() => {
|
|||||||
|
|
||||||
<!-- Result Message -->
|
<!-- Result Message -->
|
||||||
<div v-if="showResult" class="animate-in fade-in slide-in-from-top-4 duration-500">
|
<div v-if="showResult" class="animate-in fade-in slide-in-from-top-4 duration-500">
|
||||||
<Alert
|
<Alert :variant="isCorrect ? 'default' : 'destructive'" class="border-2">
|
||||||
:variant="isCorrect ? 'default' : 'destructive'"
|
|
||||||
class="border-2"
|
|
||||||
>
|
|
||||||
<CheckCircle2 v-if="isCorrect" :size="20" class="text-green-500" />
|
<CheckCircle2 v-if="isCorrect" :size="20" class="text-green-500" />
|
||||||
<XCircle v-else :size="20" class="text-destructive" />
|
<XCircle v-else :size="20" class="text-destructive" />
|
||||||
<AlertDescription class="text-base font-medium">
|
<AlertDescription class="text-base font-medium">
|
||||||
<span v-if="isCorrect && correctCount + 1 >= totalQuestions">
|
<span v-if="isCorrect && correctCount + 1 >= totalQuestions">
|
||||||
Perfect! You've verified your recovery phrase. 🎉
|
Perfect! You've verified your recovery phrase. 🎉
|
||||||
</span>
|
</span>
|
||||||
<span v-else-if="isCorrect">
|
<span v-else-if="isCorrect"> Correct! Moving to next question... </span>
|
||||||
Correct! Moving to next question...
|
<span v-else> That's not correct. Please try again. </span>
|
||||||
</span>
|
|
||||||
<span v-else>
|
|
||||||
That's not correct. Please try again.
|
|
||||||
</span>
|
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
</div>
|
</div>
|
||||||
@ -256,7 +244,7 @@ onMounted(() => {
|
|||||||
<!-- Action Buttons -->
|
<!-- Action Buttons -->
|
||||||
<div class="flex gap-3">
|
<div class="flex gap-3">
|
||||||
<Button
|
<Button
|
||||||
v-if="!showResult || !isCorrect || (isCorrect && correctCount + 1 < totalQuestions)"
|
v-if="!showResult || !isCorrect"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="lg"
|
size="lg"
|
||||||
class="flex-1 gap-2"
|
class="flex-1 gap-2"
|
||||||
@ -266,12 +254,12 @@ onMounted(() => {
|
|||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
v-if="showResult && isCorrect && correctCount + 1 >= totalQuestions"
|
v-if="showResult && isCorrect"
|
||||||
size="lg"
|
size="lg"
|
||||||
class="flex-1 gap-2 text-base font-semibold"
|
class="flex-1 gap-2 text-base font-semibold"
|
||||||
@click="handleNext"
|
@click="handleNext"
|
||||||
>
|
>
|
||||||
Continue
|
{{ correctCount + 1 >= totalQuestions ? 'Continue' : 'Next Question' }}
|
||||||
<Check :size="18" />
|
<Check :size="18" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,18 +1,24 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, h } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { useForm } from '@tanstack/vue-form'
|
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { Eye, EyeOff, Lock, Check, X, ArrowLeft, Shield, KeyRound } from 'lucide-vue-next'
|
import { Eye, EyeOff, Lock, Check, X, Shield, KeyRound } from 'lucide-vue-next'
|
||||||
import { ThemeToggle } from '@/components/ui/theme-toggle'
|
import { ThemeToggle } from '@/components/ui/theme-toggle'
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
next: [password: string]
|
next: [password: string]
|
||||||
navigateToRecover: []
|
goToRecover: []
|
||||||
|
goToWelcome: []
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
// Form state
|
||||||
|
const password = ref('')
|
||||||
|
const confirmPassword = ref('')
|
||||||
const showPassword = ref(false)
|
const showPassword = ref(false)
|
||||||
const showConfirmPassword = ref(false)
|
const showConfirmPassword = ref(false)
|
||||||
|
const passwordTouched = ref(false)
|
||||||
|
const confirmPasswordTouched = ref(false)
|
||||||
|
|
||||||
|
// Validation schema
|
||||||
const passwordSchema = z
|
const passwordSchema = z
|
||||||
.string()
|
.string()
|
||||||
.min(8, 'Password must be at least 8 characters')
|
.min(8, 'Password must be at least 8 characters')
|
||||||
@ -20,35 +26,30 @@ const passwordSchema = z
|
|||||||
.regex(/[a-z]/, 'Must contain at least one lowercase letter')
|
.regex(/[a-z]/, 'Must contain at least one lowercase letter')
|
||||||
.regex(/[0-9]/, 'Must contain at least one number')
|
.regex(/[0-9]/, 'Must contain at least one number')
|
||||||
|
|
||||||
const form = useForm({
|
// Validate password
|
||||||
defaultValues: {
|
const passwordError = computed(() => {
|
||||||
password: '',
|
if (!passwordTouched.value || !password.value) return ''
|
||||||
confirmPassword: '',
|
try {
|
||||||
},
|
passwordSchema.parse(password.value)
|
||||||
validators: {
|
return ''
|
||||||
onChange: z.object({
|
} catch (error) {
|
||||||
password: passwordSchema,
|
if (error instanceof z.ZodError) {
|
||||||
confirmPassword: z.string(),
|
return error.issues[0]?.message || ''
|
||||||
}),
|
|
||||||
},
|
|
||||||
onSubmit: async ({ value }) => {
|
|
||||||
if (value.password === value.confirmPassword) {
|
|
||||||
emit('next', value.password)
|
|
||||||
}
|
}
|
||||||
},
|
return ''
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const passwordStrength = computed(() => {
|
const passwordStrength = computed(() => {
|
||||||
const password = form.state.values.password
|
if (!password.value) return { level: 0, text: '', color: '', width: '0%' }
|
||||||
if (!password) return { level: 0, text: '', color: '', width: '0%' }
|
|
||||||
|
|
||||||
let strength = 0
|
let strength = 0
|
||||||
const checks = {
|
const checks = {
|
||||||
length: password.length >= 8,
|
length: password.value.length >= 8,
|
||||||
uppercase: /[A-Z]/.test(password),
|
uppercase: /[A-Z]/.test(password.value),
|
||||||
lowercase: /[a-z]/.test(password),
|
lowercase: /[a-z]/.test(password.value),
|
||||||
number: /[0-9]/.test(password),
|
number: /[0-9]/.test(password.value),
|
||||||
special: /[!@#$%^&*(),.?":{}|<>]/.test(password),
|
special: /[!@#$%^&*(),.?":{}|<>]/.test(password.value),
|
||||||
}
|
}
|
||||||
|
|
||||||
strength = Object.values(checks).filter(Boolean).length
|
strength = Object.values(checks).filter(Boolean).length
|
||||||
@ -60,32 +61,42 @@ const passwordStrength = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const isPasswordMatch = computed(() => {
|
const isPasswordMatch = computed(() => {
|
||||||
const { password, confirmPassword } = form.state.values
|
if (!confirmPassword.value) return true
|
||||||
if (!confirmPassword) return true
|
return password.value === confirmPassword.value
|
||||||
return password === confirmPassword
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const canProceed = computed(() => {
|
const canProceed = computed(() => {
|
||||||
const { password, confirmPassword } = form.state.values
|
|
||||||
return (
|
return (
|
||||||
password.length >= 8 &&
|
password.value.length >= 8 &&
|
||||||
confirmPassword.length >= 8 &&
|
confirmPassword.value.length >= 8 &&
|
||||||
isPasswordMatch.value &&
|
isPasswordMatch.value &&
|
||||||
passwordStrength.value.level >= 2
|
passwordStrength.value.level >= 2 &&
|
||||||
|
!passwordError.value
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleIHaveWallet = () => {
|
const handleSubmit = () => {
|
||||||
emit('navigateToRecover')
|
passwordTouched.value = true
|
||||||
|
confirmPasswordTouched.value = true
|
||||||
|
|
||||||
|
if (canProceed.value) {
|
||||||
|
emit('next', password.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isInvalid(field: any) {
|
const handleIHaveWallet = () => {
|
||||||
return field.state.meta.isTouched && !field.state.meta.isValid
|
emit('goToRecover')
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleGoToWelcome = () => {
|
||||||
|
emit('goToWelcome')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="relative flex min-h-screen flex-col bg-gradient-to-br from-background via-background to-primary/5 p-4">
|
<div
|
||||||
|
class="relative flex min-h-screen flex-col bg-linear-to-br from-background via-background to-primary/5 p-4"
|
||||||
|
>
|
||||||
<!-- Theme Toggle -->
|
<!-- Theme Toggle -->
|
||||||
<div class="absolute right-4 top-4 z-10">
|
<div class="absolute right-4 top-4 z-10">
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
@ -93,9 +104,7 @@ function isInvalid(field: any) {
|
|||||||
|
|
||||||
<!-- Back Button -->
|
<!-- Back Button -->
|
||||||
<div class="absolute left-4 top-4 z-10">
|
<div class="absolute left-4 top-4 z-10">
|
||||||
<Button variant="ghost" size="icon" @click="handleIHaveWallet">
|
<ArrowLeftCommon @click="handleGoToWelcome" />
|
||||||
<ArrowLeft :size="20" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Content Container -->
|
<!-- Content Container -->
|
||||||
@ -103,130 +112,115 @@ function isInvalid(field: any) {
|
|||||||
<div class="w-full max-w-md space-y-8">
|
<div class="w-full max-w-md space-y-8">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="flex flex-col items-center space-y-4 text-center">
|
<div class="flex flex-col items-center space-y-4 text-center">
|
||||||
<div class="flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-primary to-accent shadow-xl">
|
<Logo />
|
||||||
<Shield :size="32" class="text-primary-foreground" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<h1 class="text-3xl font-bold tracking-tight text-foreground">
|
<h1 class="text-3xl font-bold tracking-tight text-foreground">Create Password</h1>
|
||||||
Create Password
|
<p class="text-base text-muted-foreground">Secure your new wallet</p>
|
||||||
</h1>
|
|
||||||
<p class="text-base text-muted-foreground">
|
|
||||||
Secure your new wallet
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Form -->
|
<!-- Form -->
|
||||||
<Card class="border-2 border-border/50 shadow-xl">
|
<Card class="border-2 border-border/50 shadow-xl">
|
||||||
<CardContent class="p-6">
|
<CardContent class="p-6">
|
||||||
<form id="create-password-form" @submit.prevent="form.handleSubmit">
|
<form id="create-password-form" @submit.prevent="handleSubmit">
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<!-- Password Field -->
|
<!-- Password Field -->
|
||||||
<form.Field name="password">
|
<div class="space-y-2">
|
||||||
<template #default="{ field }">
|
<Label for="password" class="text-base">Password</Label>
|
||||||
<div class="space-y-2">
|
<div class="relative">
|
||||||
<Label :for="field.name" class="text-base">Password</Label>
|
<Lock
|
||||||
<div class="relative">
|
:size="20"
|
||||||
<Lock
|
class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground"
|
||||||
:size="20"
|
/>
|
||||||
class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground"
|
<Input
|
||||||
/>
|
id="password"
|
||||||
<Input
|
v-model="password"
|
||||||
:id="field.name"
|
:type="showPassword ? 'text' : 'password'"
|
||||||
:name="field.name"
|
placeholder="Enter your password"
|
||||||
:model-value="field.state.value"
|
class="h-12 pl-11 pr-11 text-base"
|
||||||
:type="showPassword ? 'text' : 'password'"
|
:class="{ 'border-destructive': passwordError }"
|
||||||
placeholder="Enter your password"
|
@blur="passwordTouched = true"
|
||||||
class="h-12 pl-11 pr-11 text-base"
|
/>
|
||||||
:class="{ 'border-destructive': isInvalid(field) }"
|
<div
|
||||||
autocomplete="new-password"
|
class="absolute right-3 top-1/2 -translate-y-1/2 cursor-pointer text-muted-foreground transition-colors hover:text-foreground"
|
||||||
@blur="field.handleBlur"
|
@click="showPassword = !showPassword"
|
||||||
@input="field.handleChange(($event.target as HTMLInputElement).value)"
|
>
|
||||||
/>
|
<Eye v-if="!showPassword" :size="20" />
|
||||||
<button
|
<EyeOff v-else :size="20" />
|
||||||
type="button"
|
|
||||||
class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground transition-colors hover:text-foreground"
|
|
||||||
@click="showPassword = !showPassword"
|
|
||||||
>
|
|
||||||
<Eye v-if="!showPassword" :size="20" />
|
|
||||||
<EyeOff v-else :size="20" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Password Strength -->
|
|
||||||
<div v-if="field.state.value" class="space-y-2">
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<div class="h-2 flex-1 overflow-hidden rounded-full bg-muted">
|
|
||||||
<div
|
|
||||||
class="h-full transition-all duration-300"
|
|
||||||
:class="passwordStrength.color"
|
|
||||||
:style="{ width: passwordStrength.width }"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span class="text-xs font-semibold" :class="`text-${passwordStrength.color.replace('bg-', '')}`">
|
|
||||||
{{ passwordStrength.text }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Error Message -->
|
|
||||||
<p v-if="isInvalid(field)" class="text-sm text-destructive">
|
|
||||||
{{ field.state.meta.errors[0] }}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</div>
|
||||||
</form.Field>
|
|
||||||
|
<!-- Password Strength -->
|
||||||
|
<div v-if="password" class="space-y-2">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<div class="h-2 flex-1 overflow-hidden rounded-full bg-muted">
|
||||||
|
<div
|
||||||
|
class="h-full transition-all duration-300"
|
||||||
|
:class="passwordStrength.color"
|
||||||
|
:style="{ width: passwordStrength.width }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
class="text-xs font-semibold"
|
||||||
|
:class="`text-${passwordStrength.color.replace('bg-', '')}`"
|
||||||
|
>
|
||||||
|
{{ passwordStrength.text }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Error Message -->
|
||||||
|
<p v-if="passwordError" class="text-sm text-destructive">
|
||||||
|
{{ passwordError }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Confirm Password Field -->
|
<!-- Confirm Password Field -->
|
||||||
<form.Field name="confirmPassword">
|
<div class="space-y-2">
|
||||||
<template #default="{ field }">
|
<Label for="confirmPassword" class="text-base">Confirm Password</Label>
|
||||||
<div class="space-y-2">
|
<div class="relative">
|
||||||
<Label :for="field.name" class="text-base">Confirm Password</Label>
|
<KeyRound
|
||||||
<div class="relative">
|
:size="20"
|
||||||
<KeyRound
|
class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground"
|
||||||
:size="20"
|
/>
|
||||||
class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground"
|
<Input
|
||||||
/>
|
id="confirmPassword"
|
||||||
<Input
|
v-model="confirmPassword"
|
||||||
:id="field.name"
|
:type="showConfirmPassword ? 'text' : 'password'"
|
||||||
:name="field.name"
|
placeholder="Re-enter your password"
|
||||||
:model-value="field.state.value"
|
class="h-12 pl-11 pr-11 text-base"
|
||||||
:type="showConfirmPassword ? 'text' : 'password'"
|
:class="{ 'border-destructive': confirmPassword && !isPasswordMatch }"
|
||||||
placeholder="Re-enter your password"
|
@blur="confirmPasswordTouched = true"
|
||||||
class="h-12 pl-11 pr-11 text-base"
|
/>
|
||||||
:class="{ 'border-destructive': field.state.value && !isPasswordMatch }"
|
<div
|
||||||
autocomplete="new-password"
|
class="absolute right-3 top-1/2 -translate-y-1/2 cursor-pointer text-muted-foreground transition-colors hover:text-foreground"
|
||||||
@blur="field.handleBlur"
|
@click="showConfirmPassword = !showConfirmPassword"
|
||||||
@input="field.handleChange(($event.target as HTMLInputElement).value)"
|
>
|
||||||
/>
|
<Eye v-if="!showConfirmPassword" :size="20" />
|
||||||
<button
|
<EyeOff v-else :size="20" />
|
||||||
type="button"
|
|
||||||
class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground transition-colors hover:text-foreground"
|
|
||||||
@click="showConfirmPassword = !showConfirmPassword"
|
|
||||||
>
|
|
||||||
<Eye v-if="!showConfirmPassword" :size="20" />
|
|
||||||
<EyeOff v-else :size="20" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Password Match Indicator -->
|
|
||||||
<div v-if="field.state.value" class="flex items-center gap-2">
|
|
||||||
<Check v-if="isPasswordMatch" :size="16" class="text-green-500" />
|
|
||||||
<X v-else :size="16" class="text-destructive" />
|
|
||||||
<span class="text-sm" :class="isPasswordMatch ? 'text-green-500' : 'text-destructive'">
|
|
||||||
{{ isPasswordMatch ? 'Passwords match' : 'Passwords do not match' }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</div>
|
||||||
</form.Field>
|
|
||||||
|
<!-- Password Match Indicator -->
|
||||||
|
<div v-if="confirmPassword" class="flex items-center gap-2">
|
||||||
|
<Check v-if="isPasswordMatch" :size="16" class="text-green-500" />
|
||||||
|
<X v-else :size="16" class="text-destructive" />
|
||||||
|
<span
|
||||||
|
class="text-sm"
|
||||||
|
:class="isPasswordMatch ? 'text-green-500' : 'text-destructive'"
|
||||||
|
>
|
||||||
|
{{ isPasswordMatch ? 'Passwords match' : 'Passwords do not match' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Security Info -->
|
<!-- Security Info -->
|
||||||
<Alert>
|
<Alert>
|
||||||
<Shield :size="16" />
|
<Shield :size="16" />
|
||||||
<AlertDescription class="text-xs">
|
<AlertDescription class="text-xs">
|
||||||
Use at least 8 characters with uppercase, lowercase, and numbers for a strong password.
|
Use at least 8 characters with uppercase, lowercase, and numbers for a strong
|
||||||
|
password.
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
@ -248,7 +242,11 @@ function isInvalid(field: any) {
|
|||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<p class="text-sm text-muted-foreground">
|
<p class="text-sm text-muted-foreground">
|
||||||
Already have a wallet?
|
Already have a wallet?
|
||||||
<Button variant="link" class="p-0 text-sm font-semibold text-primary" @click="handleIHaveWallet">
|
<Button
|
||||||
|
variant="link"
|
||||||
|
class="p-0 text-sm font-semibold text-primary"
|
||||||
|
@click="handleIHaveWallet"
|
||||||
|
>
|
||||||
Import wallet
|
Import wallet
|
||||||
</Button>
|
</Button>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -6,8 +6,9 @@ import ConfirmSeedStep from './ConfirmSeedStep.vue'
|
|||||||
import WalletCreatedStep from './WalletCreatedStep.vue'
|
import WalletCreatedStep from './WalletCreatedStep.vue'
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
navigateToRecover: []
|
goToRecover: []
|
||||||
accessWallet: []
|
accessWallet: []
|
||||||
|
goToWelcome: []
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
// TODO: Import useNeptuneWallet composable
|
// TODO: Import useNeptuneWallet composable
|
||||||
@ -18,8 +19,12 @@ const seedPhrase = ref<string[]>([])
|
|||||||
const password = ref('')
|
const password = ref('')
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
|
|
||||||
const handleNavigateToRecover = () => {
|
const handleGoToRecover = () => {
|
||||||
emit('navigateToRecover')
|
emit('goToRecover')
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleGoToWelcome = () => {
|
||||||
|
emit('goToWelcome')
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleNextFromPassword = async (pwd: string) => {
|
const handleNextFromPassword = async (pwd: string) => {
|
||||||
@ -90,14 +95,6 @@ const handleAccessWallet = async () => {
|
|||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCreateAnother = () => {
|
|
||||||
step.value = 1
|
|
||||||
seedPhrase.value = []
|
|
||||||
password.value = ''
|
|
||||||
// TODO: Clear wallet
|
|
||||||
// clearWallet()
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -106,13 +103,14 @@ const handleCreateAnother = () => {
|
|||||||
<CreatePasswordStep
|
<CreatePasswordStep
|
||||||
v-if="step === 1"
|
v-if="step === 1"
|
||||||
@next="handleNextFromPassword"
|
@next="handleNextFromPassword"
|
||||||
@navigate-to-recover="handleNavigateToRecover"
|
@go-to-recover="handleGoToRecover"
|
||||||
|
@go-to-welcome="handleGoToWelcome"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Step 2: Display Seed Phrase -->
|
<!-- Step 2: Display Seed Phrase -->
|
||||||
<div
|
<div
|
||||||
v-else-if="step === 2"
|
v-else-if="step === 2"
|
||||||
class="flex min-h-screen items-center justify-center bg-gradient-to-br from-background via-background to-primary/5 p-4"
|
class="flex min-h-screen items-center justify-center bg-linear-to-br from-background via-background to-primary/5 p-4"
|
||||||
>
|
>
|
||||||
<Card class="w-full max-w-2xl border-2 border-border/50 shadow-xl">
|
<Card class="w-full max-w-2xl border-2 border-border/50 shadow-xl">
|
||||||
<CardContent class="p-6 md:p-8">
|
<CardContent class="p-6 md:p-8">
|
||||||
@ -128,7 +126,7 @@ const handleCreateAnother = () => {
|
|||||||
<!-- Step 3: Confirm Seed Phrase -->
|
<!-- Step 3: Confirm Seed Phrase -->
|
||||||
<div
|
<div
|
||||||
v-else-if="step === 3"
|
v-else-if="step === 3"
|
||||||
class="flex min-h-screen items-center justify-center bg-gradient-to-br from-background via-background to-primary/5 p-4"
|
class="flex min-h-screen items-center justify-center bg-linear-to-br from-background via-background to-primary/5 p-4"
|
||||||
>
|
>
|
||||||
<Card class="w-full max-w-2xl border-2 border-border/50 shadow-xl">
|
<Card class="w-full max-w-2xl border-2 border-border/50 shadow-xl">
|
||||||
<CardContent class="p-6 md:p-8">
|
<CardContent class="p-6 md:p-8">
|
||||||
@ -144,7 +142,7 @@ const handleCreateAnother = () => {
|
|||||||
<!-- Step 4: Wallet Created Success -->
|
<!-- Step 4: Wallet Created Success -->
|
||||||
<div
|
<div
|
||||||
v-else-if="step === 4"
|
v-else-if="step === 4"
|
||||||
class="flex min-h-screen items-center justify-center bg-gradient-to-br from-background via-background to-primary/5 p-4"
|
class="flex min-h-screen items-center justify-center bg-linear-to-br from-background via-background to-primary/5 p-4"
|
||||||
>
|
>
|
||||||
<Card class="w-full max-w-2xl border-2 border-border/50 shadow-xl">
|
<Card class="w-full max-w-2xl border-2 border-border/50 shadow-xl">
|
||||||
<CardContent class="p-6 md:p-8">
|
<CardContent class="p-6 md:p-8">
|
||||||
@ -152,7 +150,6 @@ const handleCreateAnother = () => {
|
|||||||
:seed-phrase="seedPhrase"
|
:seed-phrase="seedPhrase"
|
||||||
:password="password"
|
:password="password"
|
||||||
@access-wallet="handleAccessWallet"
|
@access-wallet="handleAccessWallet"
|
||||||
@create-another="handleCreateAnother"
|
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@ -130,30 +130,6 @@ const handleCopySeed = async () => {
|
|||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
<!-- Info Card -->
|
|
||||||
<Card class="border-primary/20 bg-primary/5">
|
|
||||||
<CardContent class="p-4">
|
|
||||||
<ul class="space-y-2 text-xs text-muted-foreground">
|
|
||||||
<li class="flex items-start gap-2">
|
|
||||||
<span class="mt-0.5 text-primary">✓</span>
|
|
||||||
<span>Write these words down on paper in the exact order</span>
|
|
||||||
</li>
|
|
||||||
<li class="flex items-start gap-2">
|
|
||||||
<span class="mt-0.5 text-primary">✓</span>
|
|
||||||
<span>Store the paper in a secure location</span>
|
|
||||||
</li>
|
|
||||||
<li class="flex items-start gap-2">
|
|
||||||
<span class="mt-0.5 text-primary">✓</span>
|
|
||||||
<span>Never share your recovery phrase with anyone</span>
|
|
||||||
</li>
|
|
||||||
<li class="flex items-start gap-2">
|
|
||||||
<span class="mt-0.5 text-primary">✓</span>
|
|
||||||
<span>Neptune support will never ask for your seed phrase</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<!-- Action Buttons -->
|
<!-- Action Buttons -->
|
||||||
<div class="flex gap-3">
|
<div class="flex gap-3">
|
||||||
<Button variant="outline" size="lg" class="flex-1 gap-2" @click="handleBack">
|
<Button variant="outline" size="lg" class="flex-1 gap-2" @click="handleBack">
|
||||||
|
|||||||
@ -20,10 +20,6 @@ const handleAccessWallet = () => {
|
|||||||
emit('accessWallet')
|
emit('accessWallet')
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCreateAnother = () => {
|
|
||||||
emit('createAnother')
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
showConfetti.value = true
|
showConfetti.value = true
|
||||||
})
|
})
|
||||||
@ -34,9 +30,9 @@ onMounted(() => {
|
|||||||
<!-- Success Animation -->
|
<!-- Success Animation -->
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div class="absolute -inset-4 animate-pulse rounded-full bg-green-500/20 blur-xl"></div>
|
<div class="absolute -inset-4 rounded-full bg-green-500/20 blur-xl"></div>
|
||||||
<div
|
<div
|
||||||
class="relative rounded-full bg-gradient-to-br from-green-400 to-emerald-600 p-6 shadow-2xl"
|
class="relative rounded-full bg-linear-to-br from-green-400 to-emerald-600 p-6 shadow-2xl"
|
||||||
>
|
>
|
||||||
<CheckCircle2 :size="80" class="text-white" />
|
<CheckCircle2 :size="80" class="text-white" />
|
||||||
</div>
|
</div>
|
||||||
@ -55,24 +51,15 @@ onMounted(() => {
|
|||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<!-- Logo -->
|
<!-- Logo -->
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<div class="relative">
|
<Logo />
|
||||||
<div
|
|
||||||
class="absolute -inset-2 animate-pulse rounded-3xl bg-gradient-to-r from-primary via-accent to-primary opacity-30 blur-2xl"
|
|
||||||
></div>
|
|
||||||
<img
|
|
||||||
src="@/assets/imgs/neptune_logo.jpg"
|
|
||||||
alt="Neptune Logo"
|
|
||||||
class="relative h-32 w-32 rounded-3xl object-cover shadow-2xl ring-4 ring-background"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Features Grid -->
|
<!-- Features Grid -->
|
||||||
<div class="grid gap-3">
|
<div class="grid gap-3">
|
||||||
<Card class="border-2 border-primary/20 bg-gradient-to-br from-primary/5 to-transparent">
|
<Card class="border-2 border-green-500/20 bg-linear-to-br from-green-500/5 to-transparent">
|
||||||
<CardContent class="flex items-center gap-4 p-4">
|
<CardContent class="flex items-center gap-4 p-4">
|
||||||
<div class="flex h-12 w-12 items-center justify-center rounded-xl bg-primary/10">
|
<div class="flex h-12 w-12 items-center justify-center rounded-xl bg-primary/10">
|
||||||
<Shield :size="24" class="text-primary" />
|
<Shield />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<h3 class="font-semibold text-foreground">Secure & Private</h3>
|
<h3 class="font-semibold text-foreground">Secure & Private</h3>
|
||||||
@ -82,7 +69,7 @@ onMounted(() => {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
class="border-2 border-green-500/20 bg-gradient-to-br from-green-500/5 to-transparent"
|
class="border-2 border-green-500/20 bg-linear-to-br from-green-500/5 to-transparent"
|
||||||
>
|
>
|
||||||
<CardContent class="flex items-center gap-4 p-4">
|
<CardContent class="flex items-center gap-4 p-4">
|
||||||
<div class="flex h-12 w-12 items-center justify-center rounded-xl bg-green-500/10">
|
<div class="flex h-12 w-12 items-center justify-center rounded-xl bg-green-500/10">
|
||||||
@ -95,14 +82,14 @@ onMounted(() => {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card class="border-2 border-accent/20 bg-gradient-to-br from-accent/5 to-transparent">
|
<Card class="border-2 border-green-500/20 bg-linear-to-br from-green-500/5 to-transparent">
|
||||||
<CardContent class="flex items-center gap-4 p-4">
|
<CardContent class="flex items-center gap-4 p-4">
|
||||||
<div class="flex h-12 w-12 items-center justify-center rounded-xl bg-accent/10">
|
<div class="flex h-12 w-12 items-center justify-center rounded-xl bg-accent/10">
|
||||||
<Sparkles :size="24" class="text-accent-foreground" />
|
<Sparkles :size="24" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<h3 class="font-semibold text-foreground">Ready to Use</h3>
|
<h3 class="font-semibold text-foreground">Ready to Use</h3>
|
||||||
<p class="text-xs text-muted-foreground">Start exploring Web3 now</p>
|
<p class="text-xs text-muted-foreground">Start exploring now</p>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@ -129,15 +116,6 @@ onMounted(() => {
|
|||||||
Open My Wallet
|
Open My Wallet
|
||||||
<ArrowRight :size="20" class="ml-auto" />
|
<ArrowRight :size="20" class="ml-auto" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
class="w-full text-sm text-muted-foreground hover:text-foreground"
|
|
||||||
@click="handleCreateAnother"
|
|
||||||
>
|
|
||||||
Create another wallet instead
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
5
src/views/Auth/components/create/index.ts
Normal file
5
src/views/Auth/components/create/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export { default as CreateWalletFlow } from './CreateWalletFlow.vue'
|
||||||
|
export { default as CreatePasswordStep } from './CreatePasswordStep.vue'
|
||||||
|
export { default as SeedPhraseDisplayStep } from './SeedPhraseDisplayStep.vue'
|
||||||
|
export { default as ConfirmSeedStep } from './ConfirmSeedStep.vue'
|
||||||
|
export { default as WalletCreatedStep } from './WalletCreatedStep.vue'
|
||||||
@ -1,5 +1,5 @@
|
|||||||
export { default as OnboardingTab } from './onboarding/OnboardingTab.vue'
|
export * from './welcome'
|
||||||
export { default as LoginTab } from './login/LoginTab.vue'
|
export * from './create'
|
||||||
export { default as CreateWalletFlow } from './create/CreateWalletFlow.vue'
|
export * from './onboarding'
|
||||||
export { default as RecoverWalletFlow } from './recovery/RecoverWalletFlow.vue'
|
export * from './recovery'
|
||||||
|
export * from './login'
|
||||||
|
|||||||
@ -1,89 +1,46 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useForm } from '@tanstack/vue-form'
|
|
||||||
import { z } from 'zod'
|
|
||||||
import { Eye, EyeOff, Lock, ArrowLeft, Loader2 } from 'lucide-vue-next'
|
|
||||||
import { ThemeToggle } from '@/components/ui/theme-toggle'
|
import { ThemeToggle } from '@/components/ui/theme-toggle'
|
||||||
|
import { PasswordForm } from '@/components/commons'
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
goToCreate: []
|
goToCreate: []
|
||||||
submit: [password: string]
|
submit: [password: string]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const showPassword = ref(false)
|
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
const error = ref(false)
|
const error = ref(false)
|
||||||
|
|
||||||
const passwordSchema = z.string().min(1, 'Password is required')
|
const handleSubmit = async (password: string) => {
|
||||||
|
try {
|
||||||
const form = useForm({
|
isLoading.value = true
|
||||||
defaultValues: {
|
error.value = false
|
||||||
password: '',
|
emit('submit', password)
|
||||||
},
|
} catch (err) {
|
||||||
validators: {
|
error.value = true
|
||||||
onChange: z.object({
|
} finally {
|
||||||
password: passwordSchema,
|
isLoading.value = false
|
||||||
}),
|
}
|
||||||
},
|
}
|
||||||
onSubmit: async ({ value }) => {
|
|
||||||
try {
|
|
||||||
isLoading.value = true
|
|
||||||
error.value = false
|
|
||||||
emit('submit', value.password)
|
|
||||||
} catch (err) {
|
|
||||||
error.value = true
|
|
||||||
} finally {
|
|
||||||
isLoading.value = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleNewWallet = () => {
|
const handleNewWallet = () => {
|
||||||
emit('goToCreate')
|
emit('goToCreate')
|
||||||
}
|
}
|
||||||
|
|
||||||
function isInvalid(field: any) {
|
|
||||||
return field.state.meta.isTouched && !field.state.meta.isValid
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
setError: (val: boolean) => {
|
|
||||||
error.value = val
|
|
||||||
},
|
|
||||||
setLoading: (val: boolean) => {
|
|
||||||
isLoading.value = false
|
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="relative flex min-h-screen flex-col bg-gradient-to-br from-background via-background to-primary/5 p-4">
|
<div class="relative flex min-h-screen flex-col bg-linear-to-br from-background via-background to-primary/5 p-4">
|
||||||
<!-- Theme Toggle -->
|
<!-- Theme Toggle -->
|
||||||
<div class="absolute right-4 top-4 z-10">
|
<div class="absolute right-4 top-4 z-10">
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Back Button -->
|
|
||||||
<div class="absolute left-4 top-4 z-10">
|
|
||||||
<Button variant="ghost" size="icon" @click="handleNewWallet">
|
|
||||||
<ArrowLeft :size="20" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Content Container -->
|
<!-- Content Container -->
|
||||||
<div class="flex flex-1 flex-col items-center justify-center">
|
<div class="flex flex-1 flex-col items-center justify-center">
|
||||||
<div class="w-full max-w-md space-y-8">
|
<div class="w-full max-w-md space-y-8">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="flex flex-col items-center space-y-4 text-center">
|
<div class="flex flex-col items-center space-y-4 text-center">
|
||||||
<div class="relative">
|
<Logo />
|
||||||
<div class="flex h-20 w-20 items-center justify-center rounded-2xl bg-gradient-to-br from-primary to-accent shadow-xl ring-4 ring-background">
|
|
||||||
<img
|
|
||||||
src="@/assets/imgs/neptune_logo.jpg"
|
|
||||||
alt="Neptune"
|
|
||||||
class="h-16 w-16 rounded-xl object-cover"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<h1 class="text-3xl font-bold tracking-tight text-foreground">
|
<h1 class="text-3xl font-bold tracking-tight text-foreground">
|
||||||
@ -98,60 +55,16 @@ defineExpose({
|
|||||||
<!-- Form -->
|
<!-- Form -->
|
||||||
<Card class="border-2 border-border/50 shadow-xl">
|
<Card class="border-2 border-border/50 shadow-xl">
|
||||||
<CardContent class="p-6">
|
<CardContent class="p-6">
|
||||||
<form id="login-form" @submit.prevent="form.handleSubmit">
|
<PasswordForm
|
||||||
<form.Field name="password">
|
label="Password"
|
||||||
<template #default="{ field }">
|
placeholder="Enter your password"
|
||||||
<div class="space-y-4">
|
button-text="Unlock Wallet"
|
||||||
<div class="space-y-2">
|
:loading="isLoading"
|
||||||
<Label :for="field.name" class="text-base">Password</Label>
|
:error="error"
|
||||||
<div class="relative">
|
error-message="Incorrect password. Please try again."
|
||||||
<Lock
|
autocomplete="current-password"
|
||||||
:size="20"
|
@submit="handleSubmit"
|
||||||
class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground"
|
/>
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
:id="field.name"
|
|
||||||
:name="field.name"
|
|
||||||
:model-value="field.state.value"
|
|
||||||
:type="showPassword ? 'text' : 'password'"
|
|
||||||
placeholder="Enter your password"
|
|
||||||
class="h-12 pl-11 pr-11 text-base"
|
|
||||||
:class="{ 'border-destructive': error || isInvalid(field) }"
|
|
||||||
autocomplete="current-password"
|
|
||||||
@blur="field.handleBlur"
|
|
||||||
@input="(e) => {
|
|
||||||
field.handleChange((e.target as HTMLInputElement).value)
|
|
||||||
error = false
|
|
||||||
}"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground transition-colors hover:text-foreground"
|
|
||||||
@click="showPassword = !showPassword"
|
|
||||||
>
|
|
||||||
<Eye v-if="!showPassword" :size="20" />
|
|
||||||
<EyeOff v-else :size="20" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<p v-if="error" class="text-sm text-destructive">
|
|
||||||
Incorrect password. Please try again.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Submit Button -->
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
size="lg"
|
|
||||||
class="h-12 w-full text-base font-semibold"
|
|
||||||
:disabled="!field.state.value || isLoading"
|
|
||||||
>
|
|
||||||
<Loader2 v-if="isLoading" :size="20" class="mr-2 animate-spin" />
|
|
||||||
{{ isLoading ? 'Unlocking...' : 'Unlock Wallet' }}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</form.Field>
|
|
||||||
</form>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|||||||
1
src/views/Auth/components/login/index.ts
Normal file
1
src/views/Auth/components/login/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as LoginTab } from './LoginTab.vue'
|
||||||
@ -17,7 +17,7 @@ const handleRecover = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="relative flex min-h-screen flex-col bg-gradient-to-br from-background via-background to-primary/5 p-4">
|
<div class="relative flex min-h-screen flex-col bg-linear-to-br from-background via-background to-primary/5 p-4">
|
||||||
<!-- Theme Toggle -->
|
<!-- Theme Toggle -->
|
||||||
<div class="absolute right-4 top-4 z-10">
|
<div class="absolute right-4 top-4 z-10">
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
@ -28,31 +28,19 @@ const handleRecover = () => {
|
|||||||
<div class="w-full max-w-md space-y-8">
|
<div class="w-full max-w-md space-y-8">
|
||||||
<!-- Logo and Branding -->
|
<!-- Logo and Branding -->
|
||||||
<div class="flex flex-col items-center space-y-6 text-center">
|
<div class="flex flex-col items-center space-y-6 text-center">
|
||||||
<div class="relative">
|
<Logo />
|
||||||
<div class="absolute -inset-1 animate-pulse rounded-full bg-gradient-to-r from-primary via-accent to-primary opacity-75 blur-xl"></div>
|
|
||||||
<div class="relative flex h-24 w-24 items-center justify-center rounded-full bg-gradient-to-br from-primary to-accent shadow-2xl ring-4 ring-background">
|
|
||||||
<img
|
|
||||||
src="@/assets/imgs/neptune_logo.jpg"
|
|
||||||
alt="Neptune"
|
|
||||||
class="h-20 w-20 rounded-full object-cover"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<h1 class="text-4xl font-bold tracking-tight text-foreground">
|
<h1 class="text-4xl font-bold tracking-tight text-foreground">
|
||||||
Neptune Wallet
|
Neptune Privacy
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-lg text-muted-foreground">
|
|
||||||
Your Gateway to Web3
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Action Cards -->
|
<!-- Action Cards -->
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<!-- Create New Wallet Card -->
|
<!-- Create New Wallet Card -->
|
||||||
<Card class="border-2 border-primary/20 bg-gradient-to-br from-primary/5 to-accent/5 transition-all hover:border-primary/40 hover:shadow-lg">
|
<Card class="border-2 border-primary/20 bg-linear-to-br from-primary/5 to-accent/5 transition-all hover:border-primary/40 hover:shadow-lg">
|
||||||
<CardContent class="p-6">
|
<CardContent class="p-6">
|
||||||
<button
|
<button
|
||||||
class="w-full space-y-3 text-left"
|
class="w-full space-y-3 text-left"
|
||||||
@ -96,7 +84,7 @@ const handleRecover = () => {
|
|||||||
<div class="rounded-xl border border-border/50 bg-muted/30 p-4 backdrop-blur-sm">
|
<div class="rounded-xl border border-border/50 bg-muted/30 p-4 backdrop-blur-sm">
|
||||||
<p class="text-center text-sm text-muted-foreground">
|
<p class="text-center text-sm text-muted-foreground">
|
||||||
<Wallet :size="16" class="mr-2 inline" />
|
<Wallet :size="16" class="mr-2 inline" />
|
||||||
Neptune is a secure, decentralized wallet that puts you in control
|
Neptune Privacy is a secure, decentralized wallet that puts you in control
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
1
src/views/Auth/components/onboarding/index.ts
Normal file
1
src/views/Auth/components/onboarding/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as OnboardingTab } from './OnboardingTab.vue'
|
||||||
@ -1,8 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { useForm } from '@tanstack/vue-form'
|
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { ChevronLeft, Eye, EyeOff, Lock, ArrowLeft, Loader2, KeyRound, Shield } from 'lucide-vue-next'
|
import { ChevronLeft, Eye, EyeOff, Lock, Loader2, KeyRound, Shield } from 'lucide-vue-next'
|
||||||
import { ThemeToggle } from '@/components/ui/theme-toggle'
|
import { ThemeToggle } from '@/components/ui/theme-toggle'
|
||||||
import RecoverSeedInput from './RecoverSeedInput.vue'
|
import RecoverSeedInput from './RecoverSeedInput.vue'
|
||||||
|
|
||||||
@ -22,46 +21,43 @@ const showConfirmPassword = ref(false)
|
|||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
const isSeedPhraseValid = ref(true)
|
const isSeedPhraseValid = ref(true)
|
||||||
|
|
||||||
|
// Form state
|
||||||
|
const password = ref('')
|
||||||
|
const confirmPassword = ref('')
|
||||||
|
const passwordTouched = ref(false)
|
||||||
|
const confirmPasswordTouched = ref(false)
|
||||||
|
|
||||||
|
// Validation schema
|
||||||
const passwordSchema = z
|
const passwordSchema = z
|
||||||
.string()
|
.string()
|
||||||
.min(8, 'Password must be at least 8 characters')
|
.min(8, 'Password must be at least 8 characters')
|
||||||
|
|
||||||
const passwordForm = useForm({
|
// Validate password
|
||||||
defaultValues: {
|
const passwordError = computed(() => {
|
||||||
password: '',
|
if (!passwordTouched.value || !password.value) return ''
|
||||||
confirmPassword: '',
|
try {
|
||||||
},
|
passwordSchema.parse(password.value)
|
||||||
validators: {
|
return ''
|
||||||
onChange: z.object({
|
} catch (error) {
|
||||||
password: passwordSchema,
|
if (error instanceof z.ZodError) {
|
||||||
confirmPassword: z.string(),
|
return error.issues[0]?.message || ''
|
||||||
}),
|
|
||||||
},
|
|
||||||
onSubmit: async ({ value }) => {
|
|
||||||
if (value.password !== value.confirmPassword) {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
try {
|
const isPasswordMatch = computed(() => {
|
||||||
isLoading.value = true
|
if (!confirmPassword.value) return true
|
||||||
// TODO: Recover wallet from seed
|
return password.value === confirmPassword.value
|
||||||
// const result = await recoverWalletFromSeed(seedPhrase.value)
|
})
|
||||||
// if (result.address) {
|
|
||||||
// await createKeystore(seedPhrase.value.join(' '), value.password)
|
|
||||||
// emit('accessWallet')
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Mock success for now
|
const canSubmit = computed(() => {
|
||||||
emit('accessWallet')
|
return (
|
||||||
} catch (err) {
|
password.value.length >= 8 &&
|
||||||
if (err instanceof Error && err.message.includes('Invalid seed phrase')) {
|
confirmPassword.value.length >= 8 &&
|
||||||
isSeedPhraseValid.value = false
|
isPasswordMatch.value &&
|
||||||
}
|
!passwordError.value
|
||||||
console.error('Failed to recover wallet:', err)
|
)
|
||||||
} finally {
|
|
||||||
isLoading.value = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleSeedWordsUpdate = (words: string[]) => {
|
const handleSeedWordsUpdate = (words: string[]) => {
|
||||||
@ -79,35 +75,53 @@ const handleContinue = () => {
|
|||||||
|
|
||||||
const handlePasswordBack = () => {
|
const handlePasswordBack = () => {
|
||||||
showPasswordForm.value = false
|
showPasswordForm.value = false
|
||||||
passwordForm.reset()
|
password.value = ''
|
||||||
|
confirmPassword.value = ''
|
||||||
|
passwordTouched.value = false
|
||||||
|
confirmPasswordTouched.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
emit('cancel')
|
emit('cancel')
|
||||||
showPasswordForm.value = false
|
showPasswordForm.value = false
|
||||||
seedPhrase.value = []
|
seedPhrase.value = []
|
||||||
passwordForm.reset()
|
password.value = ''
|
||||||
|
confirmPassword.value = ''
|
||||||
|
passwordTouched.value = false
|
||||||
|
confirmPasswordTouched.value = false
|
||||||
isSeedPhraseValid.value = true
|
isSeedPhraseValid.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function isInvalid(field: any) {
|
const handleSubmit = async () => {
|
||||||
return field.state.meta.isTouched && !field.state.meta.isValid
|
passwordTouched.value = true
|
||||||
}
|
confirmPasswordTouched.value = true
|
||||||
|
|
||||||
const isPasswordMatch = () => {
|
if (!canSubmit.value) return
|
||||||
const { password, confirmPassword } = passwordForm.state.values
|
|
||||||
if (!confirmPassword) return true
|
|
||||||
return password === confirmPassword
|
|
||||||
}
|
|
||||||
|
|
||||||
const canSubmit = () => {
|
try {
|
||||||
const { password, confirmPassword } = passwordForm.state.values
|
isLoading.value = true
|
||||||
return password.length >= 8 && confirmPassword.length >= 8 && isPasswordMatch()
|
// TODO: Recover wallet from seed
|
||||||
|
// const result = await recoverWalletFromSeed(seedPhrase.value)
|
||||||
|
// if (result.address) {
|
||||||
|
// await createKeystore(seedPhrase.value.join(' '), password.value)
|
||||||
|
// emit('accessWallet')
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Mock success for now
|
||||||
|
emit('accessWallet')
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof Error && err.message.includes('Invalid seed phrase')) {
|
||||||
|
isSeedPhraseValid.value = false
|
||||||
|
}
|
||||||
|
console.error('Failed to recover wallet:', err)
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="relative flex min-h-screen flex-col bg-gradient-to-br from-background via-background to-primary/5 p-4">
|
<div class="relative flex min-h-screen flex-col bg-linear-to-br from-background via-background to-primary/5 p-4">
|
||||||
<!-- Theme Toggle -->
|
<!-- Theme Toggle -->
|
||||||
<div class="absolute right-4 top-4 z-10">
|
<div class="absolute right-4 top-4 z-10">
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
@ -115,9 +129,7 @@ const canSubmit = () => {
|
|||||||
|
|
||||||
<!-- Back Button -->
|
<!-- Back Button -->
|
||||||
<div class="absolute left-4 top-4 z-10">
|
<div class="absolute left-4 top-4 z-10">
|
||||||
<Button variant="ghost" size="icon" @click="handleCancel">
|
<ArrowLeftCommon @click="handleCancel" />
|
||||||
<ArrowLeft :size="20" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Content Container -->
|
<!-- Content Container -->
|
||||||
@ -125,7 +137,7 @@ const canSubmit = () => {
|
|||||||
<div class="w-full max-w-md space-y-8">
|
<div class="w-full max-w-md space-y-8">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="flex flex-col items-center space-y-4 text-center">
|
<div class="flex flex-col items-center space-y-4 text-center">
|
||||||
<div class="flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-primary to-accent shadow-xl">
|
<div class="flex h-16 w-16 items-center justify-center rounded-2xl bg-linear-to-br from-primary to-accent shadow-xl">
|
||||||
<KeyRound :size="32" class="text-primary-foreground" />
|
<KeyRound :size="32" class="text-primary-foreground" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -175,7 +187,7 @@ const canSubmit = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Password Step -->
|
<!-- Password Step -->
|
||||||
<form v-else id="recover-password-form" @submit.prevent="passwordForm.handleSubmit">
|
<form v-else id="recover-password-form" @submit.prevent="handleSubmit">
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<!-- Info Alert -->
|
<!-- Info Alert -->
|
||||||
<Alert class="border-2 border-primary/20 bg-primary/5">
|
<Alert class="border-2 border-primary/20 bg-primary/5">
|
||||||
@ -186,80 +198,66 @@ const canSubmit = () => {
|
|||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
<!-- Password Field -->
|
<!-- Password Field -->
|
||||||
<passwordForm.Field name="password">
|
<div class="space-y-2">
|
||||||
<template #default="{ field }">
|
<Label for="password" class="text-base">Password</Label>
|
||||||
<div class="space-y-2">
|
<div class="relative">
|
||||||
<Label :for="field.name" class="text-base">Password</Label>
|
<Lock
|
||||||
<div class="relative">
|
:size="20"
|
||||||
<Lock
|
class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground"
|
||||||
:size="20"
|
/>
|
||||||
class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground"
|
<Input
|
||||||
/>
|
id="password"
|
||||||
<Input
|
v-model="password"
|
||||||
:id="field.name"
|
:type="showPassword ? 'text' : 'password'"
|
||||||
:name="field.name"
|
placeholder="Create a strong password"
|
||||||
:model-value="field.state.value"
|
class="h-12 pl-11 pr-11 text-base"
|
||||||
:type="showPassword ? 'text' : 'password'"
|
:class="{ 'border-destructive': passwordError }"
|
||||||
placeholder="Create a strong password"
|
@blur="passwordTouched = true"
|
||||||
class="h-12 pl-11 pr-11 text-base"
|
/>
|
||||||
:class="{ 'border-destructive': isInvalid(field) }"
|
<button
|
||||||
autocomplete="new-password"
|
type="button"
|
||||||
@blur="field.handleBlur"
|
class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground transition-colors hover:text-foreground"
|
||||||
@input="field.handleChange(($event.target as HTMLInputElement).value)"
|
@click="showPassword = !showPassword"
|
||||||
/>
|
>
|
||||||
<button
|
<Eye v-if="!showPassword" :size="20" />
|
||||||
type="button"
|
<EyeOff v-else :size="20" />
|
||||||
class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground transition-colors hover:text-foreground"
|
</button>
|
||||||
@click="showPassword = !showPassword"
|
</div>
|
||||||
>
|
<p v-if="passwordError" class="text-sm text-destructive">
|
||||||
<Eye v-if="!showPassword" :size="20" />
|
{{ passwordError }}
|
||||||
<EyeOff v-else :size="20" />
|
</p>
|
||||||
</button>
|
</div>
|
||||||
</div>
|
|
||||||
<p v-if="isInvalid(field)" class="text-sm text-destructive">
|
|
||||||
{{ field.state.meta.errors[0] }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</passwordForm.Field>
|
|
||||||
|
|
||||||
<!-- Confirm Password Field -->
|
<!-- Confirm Password Field -->
|
||||||
<passwordForm.Field name="confirmPassword">
|
<div class="space-y-2">
|
||||||
<template #default="{ field }">
|
<Label for="confirmPassword" class="text-base">Confirm Password</Label>
|
||||||
<div class="space-y-2">
|
<div class="relative">
|
||||||
<Label :for="field.name" class="text-base">Confirm Password</Label>
|
<KeyRound
|
||||||
<div class="relative">
|
:size="20"
|
||||||
<KeyRound
|
class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground"
|
||||||
:size="20"
|
/>
|
||||||
class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground"
|
<Input
|
||||||
/>
|
id="confirmPassword"
|
||||||
<Input
|
v-model="confirmPassword"
|
||||||
:id="field.name"
|
:type="showConfirmPassword ? 'text' : 'password'"
|
||||||
:name="field.name"
|
placeholder="Re-enter your password"
|
||||||
:model-value="field.state.value"
|
class="h-12 pl-11 pr-11 text-base"
|
||||||
:type="showConfirmPassword ? 'text' : 'password'"
|
:class="{ 'border-destructive': confirmPassword && !isPasswordMatch }"
|
||||||
placeholder="Re-enter your password"
|
@blur="confirmPasswordTouched = true"
|
||||||
class="h-12 pl-11 pr-11 text-base"
|
/>
|
||||||
:class="{ 'border-destructive': field.state.value && !isPasswordMatch() }"
|
<button
|
||||||
autocomplete="new-password"
|
type="button"
|
||||||
@blur="field.handleBlur"
|
class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground transition-colors hover:text-foreground"
|
||||||
@input="field.handleChange(($event.target as HTMLInputElement).value)"
|
@click="showConfirmPassword = !showConfirmPassword"
|
||||||
/>
|
>
|
||||||
<button
|
<Eye v-if="!showConfirmPassword" :size="20" />
|
||||||
type="button"
|
<EyeOff v-else :size="20" />
|
||||||
class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground transition-colors hover:text-foreground"
|
</button>
|
||||||
@click="showConfirmPassword = !showConfirmPassword"
|
</div>
|
||||||
>
|
<p v-if="confirmPassword && !isPasswordMatch" class="text-sm text-destructive">
|
||||||
<Eye v-if="!showConfirmPassword" :size="20" />
|
Passwords do not match
|
||||||
<EyeOff v-else :size="20" />
|
</p>
|
||||||
</button>
|
</div>
|
||||||
</div>
|
|
||||||
<p v-if="field.state.value && !isPasswordMatch()" class="text-sm text-destructive">
|
|
||||||
Passwords do not match
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</passwordForm.Field>
|
|
||||||
|
|
||||||
<!-- Action Buttons -->
|
<!-- Action Buttons -->
|
||||||
<div class="flex gap-3">
|
<div class="flex gap-3">
|
||||||
@ -278,7 +276,7 @@ const canSubmit = () => {
|
|||||||
type="submit"
|
type="submit"
|
||||||
size="lg"
|
size="lg"
|
||||||
class="h-12 flex-1 text-base font-semibold"
|
class="h-12 flex-1 text-base font-semibold"
|
||||||
:disabled="!canSubmit() || isLoading"
|
:disabled="!canSubmit || isLoading"
|
||||||
>
|
>
|
||||||
<Loader2 v-if="isLoading" :size="20" class="mr-2 animate-spin" />
|
<Loader2 v-if="isLoading" :size="20" class="mr-2 animate-spin" />
|
||||||
{{ isLoading ? 'Importing...' : 'Import Wallet' }}
|
{{ isLoading ? 'Importing...' : 'Import Wallet' }}
|
||||||
|
|||||||
2
src/views/Auth/components/recovery/index.ts
Normal file
2
src/views/Auth/components/recovery/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { default as RecoverWalletFlow } from './RecoverWalletFlow.vue'
|
||||||
|
export { default as RecoverSeedInput } from './RecoverSeedInput.vue'
|
||||||
45
src/views/Auth/components/welcome/WelcomeTab.vue
Normal file
45
src/views/Auth/components/welcome/WelcomeTab.vue
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { OnboardingTab, LoginTab } from '@/views/Auth/components'
|
||||||
|
import { useNeptuneStore } from '@/stores'
|
||||||
|
|
||||||
|
const neptuneStore = useNeptuneStore()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
goToCreate: []
|
||||||
|
goToRecover: []
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// Internal state to switch between onboarding and login
|
||||||
|
const showLogin = computed(() => neptuneStore.getKeystoreFileName !== null)
|
||||||
|
const handleGoToCreate = () => {
|
||||||
|
emit('goToCreate')
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleGoToRecover = () => {
|
||||||
|
emit('goToRecover')
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = (password: string) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- Onboarding Screen -->
|
||||||
|
<OnboardingTab
|
||||||
|
v-if="!showLogin"
|
||||||
|
@go-to-create="handleGoToCreate"
|
||||||
|
@go-to-recover="handleGoToRecover"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Login Screen -->
|
||||||
|
<LoginTab
|
||||||
|
v-else
|
||||||
|
@go-to-create="handleGoToCreate"
|
||||||
|
@submit="handleSubmit"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
1
src/views/Auth/components/welcome/index.ts
Normal file
1
src/views/Auth/components/welcome/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as WelcomeTab } from './WelcomeTab.vue'
|
||||||
@ -1,60 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const { t } = useI18n()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="container mx-auto px-4 py-6">
|
|
||||||
<!-- Welcome Section -->
|
|
||||||
<div class="mb-6">
|
|
||||||
<h1 class="text-2xl font-bold text-foreground">Welcome back!</h1>
|
|
||||||
<p class="mt-1 text-sm text-muted-foreground">Manage your digital assets securely</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Balance Card -->
|
|
||||||
<div class="mb-6 rounded-xl bg-gradient-to-br from-primary to-accent p-6 text-primary-foreground shadow-lg">
|
|
||||||
<p class="text-sm opacity-90">Total Portfolio Value</p>
|
|
||||||
<p class="mt-2 text-4xl font-bold">$0.00</p>
|
|
||||||
<div class="mt-4 flex gap-2">
|
|
||||||
<Button variant="secondary" size="sm" class="flex-1">Send</Button>
|
|
||||||
<Button variant="secondary" size="sm" class="flex-1">Receive</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Quick Actions -->
|
|
||||||
<div class="mb-6">
|
|
||||||
<h2 class="mb-3 text-lg font-semibold text-foreground">Quick Actions</h2>
|
|
||||||
<div class="grid grid-cols-2 gap-3">
|
|
||||||
<button class="rounded-lg border border-border bg-card p-4 text-left transition-colors hover:bg-accent">
|
|
||||||
<div class="mb-2 text-2xl">💸</div>
|
|
||||||
<p class="font-medium text-foreground">Send</p>
|
|
||||||
<p class="text-xs text-muted-foreground">Transfer assets</p>
|
|
||||||
</button>
|
|
||||||
<button class="rounded-lg border border-border bg-card p-4 text-left transition-colors hover:bg-accent">
|
|
||||||
<div class="mb-2 text-2xl">📥</div>
|
|
||||||
<p class="font-medium text-foreground">Receive</p>
|
|
||||||
<p class="text-xs text-muted-foreground">Get payment</p>
|
|
||||||
</button>
|
|
||||||
<button class="rounded-lg border border-border bg-card p-4 text-left transition-colors hover:bg-accent">
|
|
||||||
<div class="mb-2 text-2xl">🔄</div>
|
|
||||||
<p class="font-medium text-foreground">Swap</p>
|
|
||||||
<p class="text-xs text-muted-foreground">Exchange tokens</p>
|
|
||||||
</button>
|
|
||||||
<button class="rounded-lg border border-border bg-card p-4 text-left transition-colors hover:bg-accent">
|
|
||||||
<div class="mb-2 text-2xl">📊</div>
|
|
||||||
<p class="font-medium text-foreground">Analytics</p>
|
|
||||||
<p class="text-xs text-muted-foreground">View insights</p>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Recent Activity -->
|
|
||||||
<div>
|
|
||||||
<h2 class="mb-3 text-lg font-semibold text-foreground">Recent Activity</h2>
|
|
||||||
<div class="rounded-lg border border-border bg-card p-6 text-center">
|
|
||||||
<p class="text-sm text-muted-foreground">No recent activity</p>
|
|
||||||
<p class="mt-1 text-xs text-muted-foreground">Your transactions will appear here</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
@ -2,7 +2,7 @@
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const goHome = () => {
|
const goHome = () => {
|
||||||
router.push({ name: 'home' })
|
router.push({ name: 'wallet' })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
19
src/views/UTXO/UTXOView.vue
Normal file
19
src/views/UTXO/UTXOView.vue
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const { t } = useI18n()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="container mx-auto px-4 py-6">
|
||||||
|
<h1 class="mb-2 text-2xl font-bold text-foreground">{{ t('wallet.title') }}</h1>
|
||||||
|
<p class="text-muted-foreground">Manage your assets and balances</p>
|
||||||
|
|
||||||
|
<!-- Wallet content will go here -->
|
||||||
|
<div class="mt-6 space-y-4">
|
||||||
|
<div class="rounded-lg border border-border bg-card p-6">
|
||||||
|
<p class="text-sm text-muted-foreground">Total Balance</p>
|
||||||
|
<p class="mt-2 text-3xl font-bold text-foreground">$0.00</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
@ -1,2 +1,6 @@
|
|||||||
export const AuthPage = () => import('@/views/Auth/AuthView.vue')
|
export const AuthPage = () => import('@/views/Auth/AuthView.vue')
|
||||||
export const WalletPage = () => import('@/views/Wallet/WalletView.vue')
|
export const WalletPage = () => import('@/views/Wallet/WalletView.vue')
|
||||||
|
export const UTXOPage = () => import('@/views/UTXO/UTXOView.vue')
|
||||||
|
export const TransactionHistoryPage = () =>
|
||||||
|
import('@/views/TransactionHistory/TransactionHistoryView.vue')
|
||||||
|
export const SettingsPage = () => import('@/views/Setting/SettingsView.vue')
|
||||||
|
|||||||
@ -62,7 +62,6 @@ export default defineConfig({
|
|||||||
build: {
|
build: {
|
||||||
// Tauri uses Chromium on Windows and WebKit on macOS and Linux
|
// Tauri uses Chromium on Windows and WebKit on macOS and Linux
|
||||||
target: process.env.TAURI_ENV_PLATFORM == 'windows' ? 'chrome105' : 'safari13',
|
target: process.env.TAURI_ENV_PLATFORM == 'windows' ? 'chrome105' : 'safari13',
|
||||||
// Don't minify for debug builds
|
|
||||||
minify: !process.env.TAURI_ENV_DEBUG ? 'esbuild' : false,
|
minify: !process.env.TAURI_ENV_DEBUG ? 'esbuild' : false,
|
||||||
// Produce sourcemaps for debug builds
|
// Produce sourcemaps for debug builds
|
||||||
sourcemap: !!process.env.TAURI_ENV_DEBUG,
|
sourcemap: !!process.env.TAURI_ENV_DEBUG,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user