feat: 251125/create_wallet_flow
This commit is contained in:
parent
4da3a8ff7d
commit
df5158b318
@ -15,10 +15,13 @@
|
||||
"tauri:build": "tauri build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@neptune/native": "file:./packages/neptune-native",
|
||||
"@neptune/wasm": "file:./packages/neptune-wasm",
|
||||
"@tailwindcss/vite": "^4.1.17",
|
||||
"@tanstack/vue-form": "^1.26.0",
|
||||
"@tauri-apps/api": "^2.9.0",
|
||||
"@vueuse/core": "^14.0.0",
|
||||
"axios": "^1.7.9",
|
||||
"axios": "1.13.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"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
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
axios: ^1.7.9
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
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':
|
||||
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))
|
||||
'@tanstack/vue-form':
|
||||
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':
|
||||
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:
|
||||
specifier: ^1.7.9
|
||||
version: 1.13.2
|
||||
@ -28,13 +40,13 @@ importers:
|
||||
version: 2.1.1
|
||||
lucide-vue-next:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
specifier: ^3.4.0
|
||||
version: 3.4.0
|
||||
@ -46,13 +58,13 @@ importers:
|
||||
version: 1.0.7(tailwindcss@4.1.17)
|
||||
vue:
|
||||
specifier: ^3.5.24
|
||||
version: 3.5.24(typescript@5.9.3)
|
||||
version: 3.5.25(typescript@5.9.3)
|
||||
vue-i18n:
|
||||
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:
|
||||
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:
|
||||
specifier: ^2.0.9
|
||||
version: 2.0.9
|
||||
@ -71,13 +83,13 @@ importers:
|
||||
version: 24.10.1
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
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':
|
||||
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':
|
||||
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':
|
||||
specifier: ^10.0.0
|
||||
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)
|
||||
'@vue/tsconfig':
|
||||
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:
|
||||
specifier: ^9.0.0
|
||||
version: 9.39.1(jiti@2.6.1)
|
||||
@ -101,10 +113,10 @@ importers:
|
||||
version: 5.9.3
|
||||
unplugin-auto-import:
|
||||
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:
|
||||
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:
|
||||
specifier: ^7.2.4
|
||||
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
|
||||
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:
|
||||
|
||||
'@babel/helper-string-parser@7.27.1':
|
||||
@ -387,6 +407,11 @@ packages:
|
||||
'@jridgewell/trace-mapping@0.3.31':
|
||||
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':
|
||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||
engines: {node: '>= 8'}
|
||||
@ -648,6 +673,9 @@ packages:
|
||||
peerDependencies:
|
||||
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':
|
||||
resolution: {integrity: sha512-9rHkMVtbMhe0AliVbrGpzMahOBg3rwV46JYRELxR9SN6iu1dvPOaMaiC4cP6M/aD1424ziXnnMdYU06RAH8oIw==}
|
||||
engines: {node: '>= 10'}
|
||||
@ -731,63 +759,63 @@ packages:
|
||||
'@types/web-bluetooth@0.0.21':
|
||||
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.47.0':
|
||||
resolution: {integrity: sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA==}
|
||||
'@typescript-eslint/eslint-plugin@8.48.0':
|
||||
resolution: {integrity: sha512-XxXP5tL1txl13YFtrECECQYeZjBZad4fyd3cFV4a19LkAY/bIp9fev3US4S5fDVV2JaYFiKAZ/GRTOLer+mbyQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
'@typescript-eslint/parser': ^8.47.0
|
||||
'@typescript-eslint/parser': ^8.48.0
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/parser@8.47.0':
|
||||
resolution: {integrity: sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==}
|
||||
'@typescript-eslint/parser@8.48.0':
|
||||
resolution: {integrity: sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/project-service@8.47.0':
|
||||
resolution: {integrity: sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA==}
|
||||
'@typescript-eslint/project-service@8.48.0':
|
||||
resolution: {integrity: sha512-Ne4CTZyRh1BecBf84siv42wv5vQvVmgtk8AuiEffKTUo3DrBaGYZueJSxxBZ8fjk/N3DrgChH4TOdIOwOwiqqw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/scope-manager@8.47.0':
|
||||
resolution: {integrity: sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==}
|
||||
'@typescript-eslint/scope-manager@8.48.0':
|
||||
resolution: {integrity: sha512-uGSSsbrtJrLduti0Q1Q9+BF1/iFKaxGoQwjWOIVNJv0o6omrdyR8ct37m4xIl5Zzpkp69Kkmvom7QFTtue89YQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/tsconfig-utils@8.47.0':
|
||||
resolution: {integrity: sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g==}
|
||||
'@typescript-eslint/tsconfig-utils@8.48.0':
|
||||
resolution: {integrity: sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/type-utils@8.47.0':
|
||||
resolution: {integrity: sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A==}
|
||||
'@typescript-eslint/type-utils@8.48.0':
|
||||
resolution: {integrity: sha512-zbeVaVqeXhhab6QNEKfK96Xyc7UQuoFWERhEnj3mLVnUWrQnv15cJNseUni7f3g557gm0e46LZ6IJ4NJVOgOpw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/types@8.47.0':
|
||||
resolution: {integrity: sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==}
|
||||
'@typescript-eslint/types@8.48.0':
|
||||
resolution: {integrity: sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.47.0':
|
||||
resolution: {integrity: sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg==}
|
||||
'@typescript-eslint/typescript-estree@8.48.0':
|
||||
resolution: {integrity: sha512-ljHab1CSO4rGrQIAyizUS6UGHHCiAYhbfcIZ1zVJr5nMryxlXMVWS3duFPSKvSUbFPwkXMFk1k0EMIjub4sRRQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/utils@8.47.0':
|
||||
resolution: {integrity: sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ==}
|
||||
'@typescript-eslint/utils@8.48.0':
|
||||
resolution: {integrity: sha512-yTJO1XuGxCsSfIVt1+1UrLHtue8xz16V8apzPYI06W0HbEbEWHxHXgZaAgavIkoh+GeV6hKKd5jm0sS6OYxWXQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.47.0':
|
||||
resolution: {integrity: sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==}
|
||||
'@typescript-eslint/visitor-keys@8.48.0':
|
||||
resolution: {integrity: sha512-T0XJMaRPOH3+LBbAfzR2jalckP1MSG/L9eUtY0DEzUyVaXJ/t6zN0nR7co5kz0Jko/nkSYCBRkz1djvjajVTTg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@vitejs/plugin-vue@6.0.2':
|
||||
@ -806,17 +834,17 @@ packages:
|
||||
'@volar/typescript@2.4.23':
|
||||
resolution: {integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==}
|
||||
|
||||
'@vue/compiler-core@3.5.24':
|
||||
resolution: {integrity: sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==}
|
||||
'@vue/compiler-core@3.5.25':
|
||||
resolution: {integrity: sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==}
|
||||
|
||||
'@vue/compiler-dom@3.5.24':
|
||||
resolution: {integrity: sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==}
|
||||
'@vue/compiler-dom@3.5.25':
|
||||
resolution: {integrity: sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==}
|
||||
|
||||
'@vue/compiler-sfc@3.5.24':
|
||||
resolution: {integrity: sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==}
|
||||
'@vue/compiler-sfc@3.5.25':
|
||||
resolution: {integrity: sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==}
|
||||
|
||||
'@vue/compiler-ssr@3.5.24':
|
||||
resolution: {integrity: sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==}
|
||||
'@vue/compiler-ssr@3.5.25':
|
||||
resolution: {integrity: sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==}
|
||||
|
||||
'@vue/devtools-api@6.6.4':
|
||||
resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
|
||||
@ -846,22 +874,22 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
'@vue/reactivity@3.5.24':
|
||||
resolution: {integrity: sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg==}
|
||||
'@vue/reactivity@3.5.25':
|
||||
resolution: {integrity: sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==}
|
||||
|
||||
'@vue/runtime-core@3.5.24':
|
||||
resolution: {integrity: sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ==}
|
||||
'@vue/runtime-core@3.5.25':
|
||||
resolution: {integrity: sha512-Z751v203YWwYzy460bzsYQISDfPjHTl+6Zzwo/a3CsAf+0ccEjQ8c+0CdX1WsumRTHeywvyUFtW6KvNukT/smA==}
|
||||
|
||||
'@vue/runtime-dom@3.5.24':
|
||||
resolution: {integrity: sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw==}
|
||||
'@vue/runtime-dom@3.5.25':
|
||||
resolution: {integrity: sha512-a4WrkYFbb19i9pjkz38zJBg8wa/rboNERq3+hRRb0dHiJh13c+6kAbgqCPfMaJ2gg4weWD3APZswASOfmKwamA==}
|
||||
|
||||
'@vue/server-renderer@3.5.24':
|
||||
resolution: {integrity: sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w==}
|
||||
'@vue/server-renderer@3.5.25':
|
||||
resolution: {integrity: sha512-UJaXR54vMG61i8XNIzTSf2Q7MOqZHpp8+x3XLGtE3+fL+nQd+k7O5+X3D/uWrnQXOdMw5VPih+Uremcw+u1woQ==}
|
||||
peerDependencies:
|
||||
vue: 3.5.24
|
||||
vue: 3.5.25
|
||||
|
||||
'@vue/shared@3.5.24':
|
||||
resolution: {integrity: sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==}
|
||||
'@vue/shared@3.5.25':
|
||||
resolution: {integrity: sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==}
|
||||
|
||||
'@vue/tsconfig@0.8.1':
|
||||
resolution: {integrity: sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g==}
|
||||
@ -909,8 +937,8 @@ packages:
|
||||
ajv@6.12.6:
|
||||
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
|
||||
|
||||
alien-signals@3.1.0:
|
||||
resolution: {integrity: sha512-yufC6VpSy8tK3I0lO67pjumo5JvDQVQyr38+3OHqe6CHl1t2VZekKZ7EKKZSqk0cRmE7U7tfZbpXiKNzuc+ckg==}
|
||||
alien-signals@3.1.1:
|
||||
resolution: {integrity: sha512-ogkIWbVrLwKtHY6oOAXaYkAxP+cTH7V5FZ5+Tm4NZFd8VDZ6uNMDrfzqctTZ42eTMCSR3ne3otpcxmqSnFfPYA==}
|
||||
|
||||
ansi-styles@4.3.0:
|
||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||
@ -1657,8 +1685,8 @@ packages:
|
||||
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
typescript-eslint@8.47.0:
|
||||
resolution: {integrity: sha512-Lwe8i2XQ3WoMjua/r1PHrCTpkubPYJCAfOurtn+mtTzqB6jNd+14n9UN1bJ4s3F49x9ixAm0FLflB/JzQ57M8Q==}
|
||||
typescript-eslint@8.48.0:
|
||||
resolution: {integrity: sha512-fcKOvQD9GUn3Xw63EgiDqhvWJ5jsyZUaekl3KVpGsDJnN46WJTe3jWxtQP9lMZm1LJNkFLlTaWAxK2vUQR+cqw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
@ -1815,8 +1843,8 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '>=5.0.0'
|
||||
|
||||
vue@3.5.24:
|
||||
resolution: {integrity: sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==}
|
||||
vue@3.5.25:
|
||||
resolution: {integrity: sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
@ -1996,11 +2024,11 @@ snapshots:
|
||||
|
||||
'@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:
|
||||
'@floating-ui/dom': 1.7.4
|
||||
'@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:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
@ -2055,6 +2083,8 @@ snapshots:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
'@napi-rs/cli@2.18.4': {}
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
dependencies:
|
||||
'@nodelib/fs.stat': 2.0.5
|
||||
@ -2228,24 +2258,26 @@ snapshots:
|
||||
|
||||
'@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:
|
||||
'@tanstack/form-core': 1.26.0
|
||||
'@tanstack/vue-store': 0.7.7(vue@3.5.24(typescript@5.9.3))
|
||||
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.25(typescript@5.9.3)
|
||||
transitivePeerDependencies:
|
||||
- '@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:
|
||||
'@tanstack/store': 0.7.7
|
||||
vue: 3.5.24(typescript@5.9.3)
|
||||
vue-demi: 0.14.10(vue@3.5.24(typescript@5.9.3))
|
||||
vue: 3.5.25(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:
|
||||
'@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':
|
||||
optional: true
|
||||
@ -2304,14 +2336,14 @@ snapshots:
|
||||
|
||||
'@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:
|
||||
'@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/scope-manager': 8.47.0
|
||||
'@typescript-eslint/type-utils': 8.47.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/visitor-keys': 8.47.0
|
||||
'@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.48.0
|
||||
'@typescript-eslint/type-utils': 8.48.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.48.0
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
graphemer: 1.4.0
|
||||
ignore: 7.0.5
|
||||
@ -2321,41 +2353,41 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- 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:
|
||||
'@typescript-eslint/scope-manager': 8.47.0
|
||||
'@typescript-eslint/types': 8.47.0
|
||||
'@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3)
|
||||
'@typescript-eslint/visitor-keys': 8.47.0
|
||||
'@typescript-eslint/scope-manager': 8.48.0
|
||||
'@typescript-eslint/types': 8.48.0
|
||||
'@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3)
|
||||
'@typescript-eslint/visitor-keys': 8.48.0
|
||||
debug: 4.4.3
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- 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:
|
||||
'@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.47.0
|
||||
'@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.48.0
|
||||
debug: 4.4.3
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/scope-manager@8.47.0':
|
||||
'@typescript-eslint/scope-manager@8.48.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.47.0
|
||||
'@typescript-eslint/visitor-keys': 8.47.0
|
||||
'@typescript-eslint/types': 8.48.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:
|
||||
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:
|
||||
'@typescript-eslint/types': 8.47.0
|
||||
'@typescript-eslint/typescript-estree': 8.47.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/types': 8.48.0
|
||||
'@typescript-eslint/typescript-estree': 8.48.0(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
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
ts-api-utils: 2.1.0(typescript@5.9.3)
|
||||
@ -2363,45 +2395,44 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- 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:
|
||||
'@typescript-eslint/project-service': 8.47.0(typescript@5.9.3)
|
||||
'@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.47.0
|
||||
'@typescript-eslint/visitor-keys': 8.47.0
|
||||
'@typescript-eslint/project-service': 8.48.0(typescript@5.9.3)
|
||||
'@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.48.0
|
||||
'@typescript-eslint/visitor-keys': 8.48.0
|
||||
debug: 4.4.3
|
||||
fast-glob: 3.3.3
|
||||
is-glob: 4.0.3
|
||||
minimatch: 9.0.5
|
||||
semver: 7.7.3
|
||||
tinyglobby: 0.2.15
|
||||
ts-api-utils: 2.1.0(typescript@5.9.3)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- 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:
|
||||
'@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1))
|
||||
'@typescript-eslint/scope-manager': 8.47.0
|
||||
'@typescript-eslint/types': 8.47.0
|
||||
'@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.48.0
|
||||
'@typescript-eslint/types': 8.48.0
|
||||
'@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.47.0':
|
||||
'@typescript-eslint/visitor-keys@8.48.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.47.0
|
||||
'@typescript-eslint/types': 8.48.0
|
||||
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:
|
||||
'@rolldown/pluginutils': 1.0.0-beta.50
|
||||
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':
|
||||
dependencies:
|
||||
@ -2415,35 +2446,35 @@ snapshots:
|
||||
path-browserify: 1.0.1
|
||||
vscode-uri: 3.1.0
|
||||
|
||||
'@vue/compiler-core@3.5.24':
|
||||
'@vue/compiler-core@3.5.25':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.5
|
||||
'@vue/shared': 3.5.24
|
||||
'@vue/shared': 3.5.25
|
||||
entities: 4.5.0
|
||||
estree-walker: 2.0.2
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-dom@3.5.24':
|
||||
'@vue/compiler-dom@3.5.25':
|
||||
dependencies:
|
||||
'@vue/compiler-core': 3.5.24
|
||||
'@vue/shared': 3.5.24
|
||||
'@vue/compiler-core': 3.5.25
|
||||
'@vue/shared': 3.5.25
|
||||
|
||||
'@vue/compiler-sfc@3.5.24':
|
||||
'@vue/compiler-sfc@3.5.25':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.5
|
||||
'@vue/compiler-core': 3.5.24
|
||||
'@vue/compiler-dom': 3.5.24
|
||||
'@vue/compiler-ssr': 3.5.24
|
||||
'@vue/shared': 3.5.24
|
||||
'@vue/compiler-core': 3.5.25
|
||||
'@vue/compiler-dom': 3.5.25
|
||||
'@vue/compiler-ssr': 3.5.25
|
||||
'@vue/shared': 3.5.25
|
||||
estree-walker: 2.0.2
|
||||
magic-string: 0.30.21
|
||||
postcss: 8.5.6
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-ssr@3.5.24':
|
||||
'@vue/compiler-ssr@3.5.25':
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.24
|
||||
'@vue/shared': 3.5.24
|
||||
'@vue/compiler-dom': 3.5.25
|
||||
'@vue/shared': 3.5.25
|
||||
|
||||
'@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)':
|
||||
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-plugin-vue: 9.33.0(eslint@9.39.1(jiti@2.6.1))
|
||||
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))
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
@ -2472,59 +2503,59 @@ snapshots:
|
||||
'@vue/language-core@3.1.5(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@volar/language-core': 2.4.23
|
||||
'@vue/compiler-dom': 3.5.24
|
||||
'@vue/shared': 3.5.24
|
||||
alien-signals: 3.1.0
|
||||
'@vue/compiler-dom': 3.5.25
|
||||
'@vue/shared': 3.5.25
|
||||
alien-signals: 3.1.1
|
||||
muggle-string: 0.4.1
|
||||
path-browserify: 1.0.1
|
||||
picomatch: 4.0.3
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
'@vue/reactivity@3.5.24':
|
||||
'@vue/reactivity@3.5.25':
|
||||
dependencies:
|
||||
'@vue/shared': 3.5.24
|
||||
'@vue/shared': 3.5.25
|
||||
|
||||
'@vue/runtime-core@3.5.24':
|
||||
'@vue/runtime-core@3.5.25':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.5.24
|
||||
'@vue/shared': 3.5.24
|
||||
'@vue/reactivity': 3.5.25
|
||||
'@vue/shared': 3.5.25
|
||||
|
||||
'@vue/runtime-dom@3.5.24':
|
||||
'@vue/runtime-dom@3.5.25':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.5.24
|
||||
'@vue/runtime-core': 3.5.24
|
||||
'@vue/shared': 3.5.24
|
||||
'@vue/reactivity': 3.5.25
|
||||
'@vue/runtime-core': 3.5.25
|
||||
'@vue/shared': 3.5.25
|
||||
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:
|
||||
'@vue/compiler-ssr': 3.5.24
|
||||
'@vue/shared': 3.5.24
|
||||
vue: 3.5.24(typescript@5.9.3)
|
||||
'@vue/compiler-ssr': 3.5.25
|
||||
'@vue/shared': 3.5.25
|
||||
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:
|
||||
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)':
|
||||
dependencies:
|
||||
'@types/web-bluetooth': 0.0.21
|
||||
'@vueuse/metadata': 12.8.2
|
||||
'@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:
|
||||
- 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:
|
||||
'@types/web-bluetooth': 0.0.21
|
||||
'@vueuse/metadata': 14.0.0
|
||||
'@vueuse/shared': 14.0.0(vue@3.5.24(typescript@5.9.3))
|
||||
vue: 3.5.24(typescript@5.9.3)
|
||||
'@vueuse/shared': 14.0.0(vue@3.5.25(typescript@5.9.3))
|
||||
vue: 3.5.25(typescript@5.9.3)
|
||||
|
||||
'@vueuse/metadata@12.8.2': {}
|
||||
|
||||
@ -2532,13 +2563,13 @@ snapshots:
|
||||
|
||||
'@vueuse/shared@12.8.2(typescript@5.9.3)':
|
||||
dependencies:
|
||||
vue: 3.5.24(typescript@5.9.3)
|
||||
vue: 3.5.25(typescript@5.9.3)
|
||||
transitivePeerDependencies:
|
||||
- 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:
|
||||
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):
|
||||
dependencies:
|
||||
@ -2553,7 +2584,7 @@ snapshots:
|
||||
json-schema-traverse: 0.4.1
|
||||
uri-js: 4.4.1
|
||||
|
||||
alien-signals@3.1.0: {}
|
||||
alien-signals@3.1.1: {}
|
||||
|
||||
ansi-styles@4.3.0:
|
||||
dependencies:
|
||||
@ -3043,9 +3074,9 @@ snapshots:
|
||||
|
||||
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:
|
||||
vue: 3.5.24(typescript@5.9.3)
|
||||
vue: 3.5.25(typescript@5.9.3)
|
||||
|
||||
magic-string@0.30.21:
|
||||
dependencies:
|
||||
@ -3130,11 +3161,11 @@ snapshots:
|
||||
|
||||
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:
|
||||
'@vue/devtools-api': 6.6.4
|
||||
vue: 3.5.24(typescript@5.9.3)
|
||||
vue-demi: 0.14.10(vue@3.5.24(typescript@5.9.3))
|
||||
vue: 3.5.25(typescript@5.9.3)
|
||||
vue-demi: 0.14.10(vue@3.5.25(typescript@5.9.3))
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
@ -3181,19 +3212,19 @@ snapshots:
|
||||
|
||||
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:
|
||||
'@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/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/shared': 12.8.2(typescript@5.9.3)
|
||||
aria-hidden: 1.2.6
|
||||
defu: 6.1.4
|
||||
ohash: 2.0.11
|
||||
vue: 3.5.24(typescript@5.9.3)
|
||||
vue: 3.5.25(typescript@5.9.3)
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- typescript
|
||||
@ -3291,12 +3322,12 @@ snapshots:
|
||||
|
||||
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:
|
||||
'@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/parser': 8.47.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/utils': 8.47.0(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.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/typescript-estree': 8.48.0(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)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
@ -3325,7 +3356,7 @@ snapshots:
|
||||
unplugin: 2.3.11
|
||||
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:
|
||||
local-pkg: 1.1.2
|
||||
magic-string: 0.30.21
|
||||
@ -3334,14 +3365,14 @@ snapshots:
|
||||
unplugin: 2.3.11
|
||||
unplugin-utils: 0.3.1
|
||||
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:
|
||||
dependencies:
|
||||
pathe: 2.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:
|
||||
chokidar: 4.0.3
|
||||
debug: 4.4.3
|
||||
@ -3351,7 +3382,7 @@ snapshots:
|
||||
tinyglobby: 0.2.15
|
||||
unplugin: 2.3.11
|
||||
unplugin-utils: 0.3.1
|
||||
vue: 3.5.24(typescript@5.9.3)
|
||||
vue: 3.5.25(typescript@5.9.3)
|
||||
optionalDependencies:
|
||||
'@babel/parser': 7.28.5
|
||||
transitivePeerDependencies:
|
||||
@ -3386,9 +3417,9 @@ snapshots:
|
||||
|
||||
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:
|
||||
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)):
|
||||
dependencies:
|
||||
@ -3415,17 +3446,17 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- 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:
|
||||
'@intlify/core-base': 10.0.8
|
||||
'@intlify/shared': 10.0.8
|
||||
'@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:
|
||||
'@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: {}
|
||||
|
||||
@ -3435,13 +3466,13 @@ snapshots:
|
||||
'@vue/language-core': 3.1.5(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:
|
||||
'@vue/compiler-dom': 3.5.24
|
||||
'@vue/compiler-sfc': 3.5.24
|
||||
'@vue/runtime-dom': 3.5.24
|
||||
'@vue/server-renderer': 3.5.24(vue@3.5.24(typescript@5.9.3))
|
||||
'@vue/shared': 3.5.24
|
||||
'@vue/compiler-dom': 3.5.25
|
||||
'@vue/compiler-sfc': 3.5.25
|
||||
'@vue/runtime-dom': 3.5.25
|
||||
'@vue/server-renderer': 3.5.25(vue@3.5.25(typescript@5.9.3))
|
||||
'@vue/shared': 3.5.25
|
||||
optionalDependencies:
|
||||
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'
|
||||
|
||||
const STATUS_CODE_SUCCESS = 200
|
||||
import { STATUS_CODE_SUCCESS } from '@/utils/constants'
|
||||
|
||||
export const API_URL = import.meta.env.VITE_APP_API || ''
|
||||
|
||||
@ -14,17 +13,6 @@ const instance: AxiosInstance = axios.create({
|
||||
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
|
||||
instance.interceptors.response.use(
|
||||
(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
|
||||
|
||||
|
||||
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
|
||||
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: 'History', icon: History, route: '/history', label: 'History' },
|
||||
{ name: 'History', icon: History, route: '/transaction-history', label: 'History' },
|
||||
{ name: 'Settings', icon: Settings, route: '/settings', label: 'Settings' },
|
||||
]
|
||||
|
||||
@ -28,7 +28,7 @@ const isActiveRoute = (routePath: string) => {
|
||||
alt="Neptune"
|
||||
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>
|
||||
<ThemeToggle />
|
||||
</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>
|
||||
<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>
|
||||
|
||||
<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,23 +6,16 @@ import { useMediaQuery } from '@vueuse/core'
|
||||
*/
|
||||
export function useMobile() {
|
||||
const isMobile = useMediaQuery('(max-width: 768px)')
|
||||
return isMobile
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if device is iOS
|
||||
*/
|
||||
export function useIsIOS() {
|
||||
return computed(() => {
|
||||
const isIOS = computed(() => {
|
||||
if (typeof window === 'undefined') return false
|
||||
return /iPhone|iPad|iPod/i.test(navigator.userAgent)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get safe area insets for notched devices
|
||||
*/
|
||||
export function useSafeArea() {
|
||||
function useSafeArea() {
|
||||
return {
|
||||
top: computed(() => {
|
||||
if (typeof window === 'undefined') return 0
|
||||
@ -45,7 +38,7 @@ export function useSafeArea() {
|
||||
/**
|
||||
* Prevent pull-to-refresh on mobile browsers
|
||||
*/
|
||||
export function usePreventPullToRefresh() {
|
||||
function usePreventPullToRefresh() {
|
||||
if (typeof window === 'undefined') return
|
||||
|
||||
let touchStartY = 0
|
||||
@ -72,3 +65,11 @@ export function usePreventPullToRefresh() {
|
||||
{ passive: false }
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
isMobile,
|
||||
isIOS,
|
||||
useSafeArea,
|
||||
usePreventPullToRefresh,
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ const router = createRouter({
|
||||
|
||||
// Navigation guards
|
||||
router.beforeEach((to, _from, next) => {
|
||||
const hasWallet = false // useNeptuneStore().hasWallet
|
||||
const hasWallet = true // useNeptuneStore().hasWallet
|
||||
|
||||
if (to.meta.requiresAuth) {
|
||||
if (!hasWallet) {
|
||||
|
||||
@ -14,10 +14,14 @@ export const routes: RouteRecordRaw[] = [
|
||||
component: Layout,
|
||||
meta: { requiresAuth: true },
|
||||
children: [
|
||||
// { path: '/home', name: 'home', component: Pages.HomePage },
|
||||
{ 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: '/utxo', name: 'utxo', component: Pages.UTXOPage },
|
||||
{ path: 'wallet', name: 'wallet', component: Pages.WalletPage },
|
||||
{
|
||||
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 { defineStore } from 'pinia'
|
||||
|
||||
export type AuthState = 'onboarding' | 'login' | 'create' | 'recovery' | 'confirm' | 'complete'
|
||||
export type AuthState = 'welcome' | 'create' | 'recovery'
|
||||
|
||||
export const useAuthStore = defineStore('auth', () => {
|
||||
// State
|
||||
const currentState = ref<AuthState>('onboarding')
|
||||
const previousState = ref<AuthState | null>(null)
|
||||
const currentState = ref<AuthState>('welcome')
|
||||
|
||||
// Getters
|
||||
const getCurrentState = computed(() => currentState.value)
|
||||
const getPreviousState = computed(() => previousState.value)
|
||||
|
||||
// Actions
|
||||
const setState = (state: AuthState) => {
|
||||
previousState.value = currentState.value
|
||||
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 {
|
||||
currentState,
|
||||
getCurrentState,
|
||||
getPreviousState,
|
||||
setState,
|
||||
goToCreate,
|
||||
goToLogin,
|
||||
goToRecover,
|
||||
goBack,
|
||||
}
|
||||
})
|
||||
|
||||
@ -2,75 +2,54 @@
|
||||
import { computed } from 'vue'
|
||||
import { useAuthStore } from '@/stores'
|
||||
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 router = useRouter()
|
||||
|
||||
// const neptuneWallet = useNeptuneWallet()
|
||||
const { goToCreate, goToRecover, goBackToWelcome } = useAuthRouting()
|
||||
|
||||
const currentState = computed(() => authStore.getCurrentState)
|
||||
|
||||
const handleGoToCreate = () => {
|
||||
authStore.goToCreate()
|
||||
goToCreate()
|
||||
}
|
||||
|
||||
const handleGoToRecover = () => {
|
||||
authStore.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)
|
||||
}
|
||||
goToRecover()
|
||||
}
|
||||
|
||||
const handleAccessWallet = () => {
|
||||
router.push({ name: 'wallet' })
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
authStore.goBack()
|
||||
const handleGoToWelcome = () => {
|
||||
goBackToWelcome()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-screen">
|
||||
<!-- Onboarding: Welcome screen -->
|
||||
<OnboardingTab
|
||||
v-if="currentState === 'onboarding'"
|
||||
<!-- Welcome: Welcome screen with login -->
|
||||
<WelcomeTab
|
||||
v-if="currentState === 'welcome'"
|
||||
@go-to-create="handleGoToCreate"
|
||||
@go-to-recover="handleGoToRecover"
|
||||
/>
|
||||
|
||||
<!-- Login: Unlock existing wallet -->
|
||||
<LoginTab
|
||||
v-else-if="currentState === 'login'"
|
||||
@go-to-create="handleGoToCreate"
|
||||
@submit="handlePasswordSubmit"
|
||||
@go-to-welcome="handleGoToWelcome"
|
||||
/>
|
||||
|
||||
<!-- Create: New wallet creation flow -->
|
||||
<CreateWalletFlow
|
||||
v-else-if="currentState === 'create'"
|
||||
@navigate-to-recover="handleGoToRecover"
|
||||
@go-to-recover="handleGoToRecover"
|
||||
@access-wallet="handleAccessWallet"
|
||||
@go-to-welcome="handleGoToWelcome"
|
||||
/>
|
||||
|
||||
<!-- Recovery: Recover wallet from seed phrase -->
|
||||
<RecoverWalletFlow
|
||||
v-else-if="currentState === 'recovery'"
|
||||
@cancel="handleCancel"
|
||||
@cancel="handleGoToWelcome"
|
||||
@access-wallet="handleAccessWallet"
|
||||
/>
|
||||
|
||||
|
||||
@ -87,23 +87,16 @@ const handleNext = () => {
|
||||
if (correctCount.value >= totalQuestions) {
|
||||
emit('next')
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
// Move to next question
|
||||
showResult.value = false
|
||||
selectedAnswer.value = ''
|
||||
const newQuiz = generateQuiz()
|
||||
if (newQuiz) {
|
||||
quizData.value = newQuiz
|
||||
}
|
||||
}, 800)
|
||||
if (newQuiz) quizData.value = newQuiz
|
||||
}
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
showResult.value = false
|
||||
selectedAnswer.value = ''
|
||||
const newQuiz = generateQuiz()
|
||||
if (newQuiz) {
|
||||
quizData.value = newQuiz
|
||||
}
|
||||
}, 1500)
|
||||
}
|
||||
}
|
||||
@ -125,9 +118,7 @@ onMounted(() => {
|
||||
<!-- Header -->
|
||||
<div class="space-y-2 text-center">
|
||||
<h1 class="text-2xl font-bold text-foreground">Confirm Recovery Phrase</h1>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Select the correct word for each position
|
||||
</p>
|
||||
<p class="text-sm text-muted-foreground">Select the correct word for each position</p>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
@ -159,7 +150,7 @@ onMounted(() => {
|
||||
<!-- Quiz Section -->
|
||||
<div v-if="quizData" class="space-y-5">
|
||||
<!-- 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">
|
||||
<h2 class="text-center text-xl font-bold text-foreground">
|
||||
Select word
|
||||
@ -190,10 +181,11 @@ onMounted(() => {
|
||||
:class="{
|
||||
'border-primary bg-primary/10 shadow-lg': selectedAnswer === option && !showResult,
|
||||
'border-green-500 bg-green-500/10':
|
||||
showResult && option === quizData.correctWord,
|
||||
showResult && isCorrect && option === quizData.correctWord,
|
||||
'border-destructive bg-destructive/10':
|
||||
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"
|
||||
@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="{
|
||||
'bg-primary text-primary-foreground': selectedAnswer === option && !showResult,
|
||||
'bg-green-500 text-white': showResult && option === quizData.correctWord,
|
||||
'bg-destructive text-destructive-foreground': showResult && selectedAnswer === option && option !== quizData.correctWord,
|
||||
'bg-muted text-muted-foreground': !selectedAnswer || (selectedAnswer !== option && !showResult),
|
||||
'bg-green-500 text-white':
|
||||
showResult && isCorrect && option === quizData.correctWord,
|
||||
'bg-destructive text-destructive-foreground':
|
||||
showResult && selectedAnswer === option && option !== quizData.correctWord,
|
||||
'bg-muted text-muted-foreground':
|
||||
!selectedAnswer || (selectedAnswer !== option && !showResult),
|
||||
}"
|
||||
>
|
||||
{{ String.fromCharCode(65 + index) }}
|
||||
@ -217,7 +212,7 @@ onMounted(() => {
|
||||
|
||||
<!-- Check/X Icon -->
|
||||
<CheckCircle2
|
||||
v-if="showResult && option === quizData.correctWord"
|
||||
v-if="showResult && isCorrect && option === quizData.correctWord"
|
||||
:size="24"
|
||||
class="text-green-500"
|
||||
/>
|
||||
@ -232,22 +227,15 @@ onMounted(() => {
|
||||
|
||||
<!-- Result Message -->
|
||||
<div v-if="showResult" class="animate-in fade-in slide-in-from-top-4 duration-500">
|
||||
<Alert
|
||||
:variant="isCorrect ? 'default' : 'destructive'"
|
||||
class="border-2"
|
||||
>
|
||||
<Alert :variant="isCorrect ? 'default' : 'destructive'" class="border-2">
|
||||
<CheckCircle2 v-if="isCorrect" :size="20" class="text-green-500" />
|
||||
<XCircle v-else :size="20" class="text-destructive" />
|
||||
<AlertDescription class="text-base font-medium">
|
||||
<span v-if="isCorrect && correctCount + 1 >= totalQuestions">
|
||||
Perfect! You've verified your recovery phrase. 🎉
|
||||
</span>
|
||||
<span v-else-if="isCorrect">
|
||||
Correct! Moving to next question...
|
||||
</span>
|
||||
<span v-else>
|
||||
That's not correct. Please try again.
|
||||
</span>
|
||||
<span v-else-if="isCorrect"> Correct! Moving to next question... </span>
|
||||
<span v-else> That's not correct. Please try again. </span>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
</div>
|
||||
@ -256,7 +244,7 @@ onMounted(() => {
|
||||
<!-- Action Buttons -->
|
||||
<div class="flex gap-3">
|
||||
<Button
|
||||
v-if="!showResult || !isCorrect || (isCorrect && correctCount + 1 < totalQuestions)"
|
||||
v-if="!showResult || !isCorrect"
|
||||
variant="outline"
|
||||
size="lg"
|
||||
class="flex-1 gap-2"
|
||||
@ -266,12 +254,12 @@ onMounted(() => {
|
||||
Back
|
||||
</Button>
|
||||
<Button
|
||||
v-if="showResult && isCorrect && correctCount + 1 >= totalQuestions"
|
||||
v-if="showResult && isCorrect"
|
||||
size="lg"
|
||||
class="flex-1 gap-2 text-base font-semibold"
|
||||
@click="handleNext"
|
||||
>
|
||||
Continue
|
||||
{{ correctCount + 1 >= totalQuestions ? 'Continue' : 'Next Question' }}
|
||||
<Check :size="18" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@ -1,18 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, h } from 'vue'
|
||||
import { useForm } from '@tanstack/vue-form'
|
||||
import { ref, computed } from 'vue'
|
||||
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'
|
||||
|
||||
const emit = defineEmits<{
|
||||
next: [password: string]
|
||||
navigateToRecover: []
|
||||
goToRecover: []
|
||||
goToWelcome: []
|
||||
}>()
|
||||
|
||||
// Form state
|
||||
const password = ref('')
|
||||
const confirmPassword = ref('')
|
||||
const showPassword = ref(false)
|
||||
const showConfirmPassword = ref(false)
|
||||
const passwordTouched = ref(false)
|
||||
const confirmPasswordTouched = ref(false)
|
||||
|
||||
// Validation schema
|
||||
const passwordSchema = z
|
||||
.string()
|
||||
.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(/[0-9]/, 'Must contain at least one number')
|
||||
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
},
|
||||
validators: {
|
||||
onChange: z.object({
|
||||
password: passwordSchema,
|
||||
confirmPassword: z.string(),
|
||||
}),
|
||||
},
|
||||
onSubmit: async ({ value }) => {
|
||||
if (value.password === value.confirmPassword) {
|
||||
emit('next', value.password)
|
||||
// Validate password
|
||||
const passwordError = computed(() => {
|
||||
if (!passwordTouched.value || !password.value) return ''
|
||||
try {
|
||||
passwordSchema.parse(password.value)
|
||||
return ''
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
return error.issues[0]?.message || ''
|
||||
}
|
||||
return ''
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const passwordStrength = computed(() => {
|
||||
const password = form.state.values.password
|
||||
if (!password) return { level: 0, text: '', color: '', width: '0%' }
|
||||
if (!password.value) return { level: 0, text: '', color: '', width: '0%' }
|
||||
|
||||
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),
|
||||
length: password.value.length >= 8,
|
||||
uppercase: /[A-Z]/.test(password.value),
|
||||
lowercase: /[a-z]/.test(password.value),
|
||||
number: /[0-9]/.test(password.value),
|
||||
special: /[!@#$%^&*(),.?":{}|<>]/.test(password.value),
|
||||
}
|
||||
|
||||
strength = Object.values(checks).filter(Boolean).length
|
||||
@ -60,32 +61,42 @@ const passwordStrength = computed(() => {
|
||||
})
|
||||
|
||||
const isPasswordMatch = computed(() => {
|
||||
const { password, confirmPassword } = form.state.values
|
||||
if (!confirmPassword) return true
|
||||
return password === confirmPassword
|
||||
if (!confirmPassword.value) return true
|
||||
return password.value === confirmPassword.value
|
||||
})
|
||||
|
||||
const canProceed = computed(() => {
|
||||
const { password, confirmPassword } = form.state.values
|
||||
return (
|
||||
password.length >= 8 &&
|
||||
confirmPassword.length >= 8 &&
|
||||
password.value.length >= 8 &&
|
||||
confirmPassword.value.length >= 8 &&
|
||||
isPasswordMatch.value &&
|
||||
passwordStrength.value.level >= 2
|
||||
passwordStrength.value.level >= 2 &&
|
||||
!passwordError.value
|
||||
)
|
||||
})
|
||||
|
||||
const handleIHaveWallet = () => {
|
||||
emit('navigateToRecover')
|
||||
const handleSubmit = () => {
|
||||
passwordTouched.value = true
|
||||
confirmPasswordTouched.value = true
|
||||
|
||||
if (canProceed.value) {
|
||||
emit('next', password.value)
|
||||
}
|
||||
}
|
||||
|
||||
function isInvalid(field: any) {
|
||||
return field.state.meta.isTouched && !field.state.meta.isValid
|
||||
const handleIHaveWallet = () => {
|
||||
emit('goToRecover')
|
||||
}
|
||||
|
||||
const handleGoToWelcome = () => {
|
||||
emit('goToWelcome')
|
||||
}
|
||||
</script>
|
||||
|
||||
<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 -->
|
||||
<div class="absolute right-4 top-4 z-10">
|
||||
<ThemeToggle />
|
||||
@ -93,9 +104,7 @@ function isInvalid(field: any) {
|
||||
|
||||
<!-- Back Button -->
|
||||
<div class="absolute left-4 top-4 z-10">
|
||||
<Button variant="ghost" size="icon" @click="handleIHaveWallet">
|
||||
<ArrowLeft :size="20" />
|
||||
</Button>
|
||||
<ArrowLeftCommon @click="handleGoToWelcome" />
|
||||
</div>
|
||||
|
||||
<!-- Content Container -->
|
||||
@ -103,59 +112,47 @@ function isInvalid(field: any) {
|
||||
<div class="w-full max-w-md space-y-8">
|
||||
<!-- Header -->
|
||||
<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">
|
||||
<Shield :size="32" class="text-primary-foreground" />
|
||||
</div>
|
||||
<Logo />
|
||||
|
||||
<div class="space-y-2">
|
||||
<h1 class="text-3xl font-bold tracking-tight text-foreground">
|
||||
Create Password
|
||||
</h1>
|
||||
<p class="text-base text-muted-foreground">
|
||||
Secure your new wallet
|
||||
</p>
|
||||
<h1 class="text-3xl font-bold tracking-tight text-foreground">Create Password</h1>
|
||||
<p class="text-base text-muted-foreground">Secure your new wallet</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form -->
|
||||
<Card class="border-2 border-border/50 shadow-xl">
|
||||
<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">
|
||||
<!-- Password Field -->
|
||||
<form.Field name="password">
|
||||
<template #default="{ field }">
|
||||
<div class="space-y-2">
|
||||
<Label :for="field.name" class="text-base">Password</Label>
|
||||
<Label for="password" class="text-base">Password</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"
|
||||
:model-value="field.state.value"
|
||||
id="password"
|
||||
v-model="password"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
placeholder="Enter your password"
|
||||
class="h-12 pl-11 pr-11 text-base"
|
||||
:class="{ 'border-destructive': isInvalid(field) }"
|
||||
autocomplete="new-password"
|
||||
@blur="field.handleBlur"
|
||||
@input="field.handleChange(($event.target as HTMLInputElement).value)"
|
||||
:class="{ 'border-destructive': passwordError }"
|
||||
@blur="passwordTouched = true"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground transition-colors hover:text-foreground"
|
||||
<div
|
||||
class="absolute right-3 top-1/2 -translate-y-1/2 cursor-pointer text-muted-foreground transition-colors hover:text-foreground"
|
||||
@click="showPassword = !showPassword"
|
||||
>
|
||||
<Eye v-if="!showPassword" :size="20" />
|
||||
<EyeOff v-else :size="20" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Password Strength -->
|
||||
<div v-if="field.state.value" class="space-y-2">
|
||||
<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
|
||||
@ -164,69 +161,66 @@ function isInvalid(field: any) {
|
||||
:style="{ width: passwordStrength.width }"
|
||||
/>
|
||||
</div>
|
||||
<span class="text-xs font-semibold" :class="`text-${passwordStrength.color.replace('bg-', '')}`">
|
||||
<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 v-if="passwordError" class="text-sm text-destructive">
|
||||
{{ passwordError }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</form.Field>
|
||||
|
||||
<!-- Confirm Password Field -->
|
||||
<form.Field name="confirmPassword">
|
||||
<template #default="{ field }">
|
||||
<div class="space-y-2">
|
||||
<Label :for="field.name" class="text-base">Confirm Password</Label>
|
||||
<Label for="confirmPassword" class="text-base">Confirm Password</Label>
|
||||
<div class="relative">
|
||||
<KeyRound
|
||||
:size="20"
|
||||
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"
|
||||
id="confirmPassword"
|
||||
v-model="confirmPassword"
|
||||
:type="showConfirmPassword ? 'text' : 'password'"
|
||||
placeholder="Re-enter your password"
|
||||
class="h-12 pl-11 pr-11 text-base"
|
||||
:class="{ 'border-destructive': field.state.value && !isPasswordMatch }"
|
||||
autocomplete="new-password"
|
||||
@blur="field.handleBlur"
|
||||
@input="field.handleChange(($event.target as HTMLInputElement).value)"
|
||||
:class="{ 'border-destructive': confirmPassword && !isPasswordMatch }"
|
||||
@blur="confirmPasswordTouched = true"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground transition-colors hover:text-foreground"
|
||||
<div
|
||||
class="absolute right-3 top-1/2 -translate-y-1/2 cursor-pointer text-muted-foreground transition-colors hover:text-foreground"
|
||||
@click="showConfirmPassword = !showConfirmPassword"
|
||||
>
|
||||
<Eye v-if="!showConfirmPassword" :size="20" />
|
||||
<EyeOff v-else :size="20" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Password Match Indicator -->
|
||||
<div v-if="field.state.value" class="flex items-center gap-2">
|
||||
<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'">
|
||||
<span
|
||||
class="text-sm"
|
||||
:class="isPasswordMatch ? 'text-green-500' : 'text-destructive'"
|
||||
>
|
||||
{{ isPasswordMatch ? 'Passwords match' : 'Passwords do not match' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</form.Field>
|
||||
|
||||
<!-- Security Info -->
|
||||
<Alert>
|
||||
<Shield :size="16" />
|
||||
<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>
|
||||
</Alert>
|
||||
|
||||
@ -248,7 +242,11 @@ function isInvalid(field: any) {
|
||||
<div class="text-center">
|
||||
<p class="text-sm text-muted-foreground">
|
||||
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
|
||||
</Button>
|
||||
</p>
|
||||
|
||||
@ -6,8 +6,9 @@ import ConfirmSeedStep from './ConfirmSeedStep.vue'
|
||||
import WalletCreatedStep from './WalletCreatedStep.vue'
|
||||
|
||||
const emit = defineEmits<{
|
||||
navigateToRecover: []
|
||||
goToRecover: []
|
||||
accessWallet: []
|
||||
goToWelcome: []
|
||||
}>()
|
||||
|
||||
// TODO: Import useNeptuneWallet composable
|
||||
@ -18,8 +19,12 @@ const seedPhrase = ref<string[]>([])
|
||||
const password = ref('')
|
||||
const isLoading = ref(false)
|
||||
|
||||
const handleNavigateToRecover = () => {
|
||||
emit('navigateToRecover')
|
||||
const handleGoToRecover = () => {
|
||||
emit('goToRecover')
|
||||
}
|
||||
|
||||
const handleGoToWelcome = () => {
|
||||
emit('goToWelcome')
|
||||
}
|
||||
|
||||
const handleNextFromPassword = async (pwd: string) => {
|
||||
@ -90,14 +95,6 @@ const handleAccessWallet = async () => {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleCreateAnother = () => {
|
||||
step.value = 1
|
||||
seedPhrase.value = []
|
||||
password.value = ''
|
||||
// TODO: Clear wallet
|
||||
// clearWallet()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -106,13 +103,14 @@ const handleCreateAnother = () => {
|
||||
<CreatePasswordStep
|
||||
v-if="step === 1"
|
||||
@next="handleNextFromPassword"
|
||||
@navigate-to-recover="handleNavigateToRecover"
|
||||
@go-to-recover="handleGoToRecover"
|
||||
@go-to-welcome="handleGoToWelcome"
|
||||
/>
|
||||
|
||||
<!-- Step 2: Display Seed Phrase -->
|
||||
<div
|
||||
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">
|
||||
<CardContent class="p-6 md:p-8">
|
||||
@ -128,7 +126,7 @@ const handleCreateAnother = () => {
|
||||
<!-- Step 3: Confirm Seed Phrase -->
|
||||
<div
|
||||
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">
|
||||
<CardContent class="p-6 md:p-8">
|
||||
@ -144,7 +142,7 @@ const handleCreateAnother = () => {
|
||||
<!-- Step 4: Wallet Created Success -->
|
||||
<div
|
||||
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">
|
||||
<CardContent class="p-6 md:p-8">
|
||||
@ -152,7 +150,6 @@ const handleCreateAnother = () => {
|
||||
:seed-phrase="seedPhrase"
|
||||
:password="password"
|
||||
@access-wallet="handleAccessWallet"
|
||||
@create-another="handleCreateAnother"
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@ -130,30 +130,6 @@ const handleCopySeed = async () => {
|
||||
</AlertDescription>
|
||||
</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 -->
|
||||
<div class="flex gap-3">
|
||||
<Button variant="outline" size="lg" class="flex-1 gap-2" @click="handleBack">
|
||||
|
||||
@ -20,10 +20,6 @@ const handleAccessWallet = () => {
|
||||
emit('accessWallet')
|
||||
}
|
||||
|
||||
const handleCreateAnother = () => {
|
||||
emit('createAnother')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
showConfetti.value = true
|
||||
})
|
||||
@ -34,9 +30,9 @@ onMounted(() => {
|
||||
<!-- Success Animation -->
|
||||
<div class="flex justify-center">
|
||||
<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
|
||||
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" />
|
||||
</div>
|
||||
@ -55,24 +51,15 @@ onMounted(() => {
|
||||
<div class="space-y-6">
|
||||
<!-- Logo -->
|
||||
<div class="flex justify-center">
|
||||
<div class="relative">
|
||||
<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>
|
||||
<Logo />
|
||||
</div>
|
||||
|
||||
<!-- Features Grid -->
|
||||
<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">
|
||||
<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 class="flex-1">
|
||||
<h3 class="font-semibold text-foreground">Secure & Private</h3>
|
||||
@ -82,7 +69,7 @@ onMounted(() => {
|
||||
</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">
|
||||
<div class="flex h-12 w-12 items-center justify-center rounded-xl bg-green-500/10">
|
||||
@ -95,14 +82,14 @@ onMounted(() => {
|
||||
</CardContent>
|
||||
</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">
|
||||
<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 class="flex-1">
|
||||
<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>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@ -129,15 +116,6 @@ onMounted(() => {
|
||||
Open My Wallet
|
||||
<ArrowRight :size="20" class="ml-auto" />
|
||||
</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>
|
||||
</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 { default as LoginTab } from './login/LoginTab.vue'
|
||||
export { default as CreateWalletFlow } from './create/CreateWalletFlow.vue'
|
||||
export { default as RecoverWalletFlow } from './recovery/RecoverWalletFlow.vue'
|
||||
|
||||
export * from './welcome'
|
||||
export * from './create'
|
||||
export * from './onboarding'
|
||||
export * from './recovery'
|
||||
export * from './login'
|
||||
|
||||
@ -1,89 +1,46 @@
|
||||
<script setup lang="ts">
|
||||
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 { PasswordForm } from '@/components/commons'
|
||||
|
||||
const emit = defineEmits<{
|
||||
goToCreate: []
|
||||
submit: [password: string]
|
||||
}>()
|
||||
|
||||
const showPassword = ref(false)
|
||||
const isLoading = ref(false)
|
||||
const error = ref(false)
|
||||
|
||||
const passwordSchema = z.string().min(1, 'Password is required')
|
||||
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
password: '',
|
||||
},
|
||||
validators: {
|
||||
onChange: z.object({
|
||||
password: passwordSchema,
|
||||
}),
|
||||
},
|
||||
onSubmit: async ({ value }) => {
|
||||
const handleSubmit = async (password: string) => {
|
||||
try {
|
||||
isLoading.value = true
|
||||
error.value = false
|
||||
emit('submit', value.password)
|
||||
emit('submit', password)
|
||||
} catch (err) {
|
||||
error.value = true
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const handleNewWallet = () => {
|
||||
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>
|
||||
|
||||
<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 -->
|
||||
<div class="absolute right-4 top-4 z-10">
|
||||
<ThemeToggle />
|
||||
</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 -->
|
||||
<div class="flex flex-1 flex-col items-center justify-center">
|
||||
<div class="w-full max-w-md space-y-8">
|
||||
<!-- Header -->
|
||||
<div class="flex flex-col items-center space-y-4 text-center">
|
||||
<div class="relative">
|
||||
<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>
|
||||
<Logo />
|
||||
|
||||
<div class="space-y-2">
|
||||
<h1 class="text-3xl font-bold tracking-tight text-foreground">
|
||||
@ -98,60 +55,16 @@ defineExpose({
|
||||
<!-- Form -->
|
||||
<Card class="border-2 border-border/50 shadow-xl">
|
||||
<CardContent class="p-6">
|
||||
<form id="login-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">Password</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"
|
||||
:model-value="field.state.value"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
<PasswordForm
|
||||
label="Password"
|
||||
placeholder="Enter your password"
|
||||
class="h-12 pl-11 pr-11 text-base"
|
||||
:class="{ 'border-destructive': error || isInvalid(field) }"
|
||||
button-text="Unlock Wallet"
|
||||
:loading="isLoading"
|
||||
:error="error"
|
||||
error-message="Incorrect password. Please try again."
|
||||
autocomplete="current-password"
|
||||
@blur="field.handleBlur"
|
||||
@input="(e) => {
|
||||
field.handleChange((e.target as HTMLInputElement).value)
|
||||
error = false
|
||||
}"
|
||||
@submit="handleSubmit"
|
||||
/>
|
||||
<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>
|
||||
</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>
|
||||
|
||||
<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 -->
|
||||
<div class="absolute right-4 top-4 z-10">
|
||||
<ThemeToggle />
|
||||
@ -28,31 +28,19 @@ const handleRecover = () => {
|
||||
<div class="w-full max-w-md space-y-8">
|
||||
<!-- Logo and Branding -->
|
||||
<div class="flex flex-col items-center space-y-6 text-center">
|
||||
<div class="relative">
|
||||
<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>
|
||||
<Logo />
|
||||
|
||||
<div class="space-y-2">
|
||||
<h1 class="text-4xl font-bold tracking-tight text-foreground">
|
||||
Neptune Wallet
|
||||
Neptune Privacy
|
||||
</h1>
|
||||
<p class="text-lg text-muted-foreground">
|
||||
Your Gateway to Web3
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Cards -->
|
||||
<div class="space-y-4">
|
||||
<!-- 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">
|
||||
<button
|
||||
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">
|
||||
<p class="text-center text-sm text-muted-foreground">
|
||||
<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>
|
||||
</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">
|
||||
import { ref } from 'vue'
|
||||
import { useForm } from '@tanstack/vue-form'
|
||||
import { ref, computed } from 'vue'
|
||||
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 RecoverSeedInput from './RecoverSeedInput.vue'
|
||||
|
||||
@ -22,46 +21,43 @@ const showConfirmPassword = ref(false)
|
||||
const isLoading = ref(false)
|
||||
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
|
||||
.string()
|
||||
.min(8, 'Password must be at least 8 characters')
|
||||
|
||||
const passwordForm = useForm({
|
||||
defaultValues: {
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
},
|
||||
validators: {
|
||||
onChange: z.object({
|
||||
password: passwordSchema,
|
||||
confirmPassword: z.string(),
|
||||
}),
|
||||
},
|
||||
onSubmit: async ({ value }) => {
|
||||
if (value.password !== value.confirmPassword) {
|
||||
return
|
||||
}
|
||||
|
||||
// Validate password
|
||||
const passwordError = computed(() => {
|
||||
if (!passwordTouched.value || !password.value) return ''
|
||||
try {
|
||||
isLoading.value = true
|
||||
// TODO: Recover wallet from seed
|
||||
// const result = await recoverWalletFromSeed(seedPhrase.value)
|
||||
// if (result.address) {
|
||||
// await createKeystore(seedPhrase.value.join(' '), value.password)
|
||||
// emit('accessWallet')
|
||||
// }
|
||||
passwordSchema.parse(password.value)
|
||||
return ''
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
return error.issues[0]?.message || ''
|
||||
}
|
||||
return ''
|
||||
}
|
||||
})
|
||||
|
||||
// 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
|
||||
}
|
||||
},
|
||||
const isPasswordMatch = computed(() => {
|
||||
if (!confirmPassword.value) return true
|
||||
return password.value === confirmPassword.value
|
||||
})
|
||||
|
||||
const canSubmit = computed(() => {
|
||||
return (
|
||||
password.value.length >= 8 &&
|
||||
confirmPassword.value.length >= 8 &&
|
||||
isPasswordMatch.value &&
|
||||
!passwordError.value
|
||||
)
|
||||
})
|
||||
|
||||
const handleSeedWordsUpdate = (words: string[]) => {
|
||||
@ -79,35 +75,53 @@ const handleContinue = () => {
|
||||
|
||||
const handlePasswordBack = () => {
|
||||
showPasswordForm.value = false
|
||||
passwordForm.reset()
|
||||
password.value = ''
|
||||
confirmPassword.value = ''
|
||||
passwordTouched.value = false
|
||||
confirmPasswordTouched.value = false
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('cancel')
|
||||
showPasswordForm.value = false
|
||||
seedPhrase.value = []
|
||||
passwordForm.reset()
|
||||
password.value = ''
|
||||
confirmPassword.value = ''
|
||||
passwordTouched.value = false
|
||||
confirmPasswordTouched.value = false
|
||||
isSeedPhraseValid.value = true
|
||||
}
|
||||
|
||||
function isInvalid(field: any) {
|
||||
return field.state.meta.isTouched && !field.state.meta.isValid
|
||||
}
|
||||
const handleSubmit = async () => {
|
||||
passwordTouched.value = true
|
||||
confirmPasswordTouched.value = true
|
||||
|
||||
const isPasswordMatch = () => {
|
||||
const { password, confirmPassword } = passwordForm.state.values
|
||||
if (!confirmPassword) return true
|
||||
return password === confirmPassword
|
||||
}
|
||||
if (!canSubmit.value) return
|
||||
|
||||
const canSubmit = () => {
|
||||
const { password, confirmPassword } = passwordForm.state.values
|
||||
return password.length >= 8 && confirmPassword.length >= 8 && isPasswordMatch()
|
||||
try {
|
||||
isLoading.value = true
|
||||
// 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>
|
||||
|
||||
<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 -->
|
||||
<div class="absolute right-4 top-4 z-10">
|
||||
<ThemeToggle />
|
||||
@ -115,9 +129,7 @@ const canSubmit = () => {
|
||||
|
||||
<!-- Back Button -->
|
||||
<div class="absolute left-4 top-4 z-10">
|
||||
<Button variant="ghost" size="icon" @click="handleCancel">
|
||||
<ArrowLeft :size="20" />
|
||||
</Button>
|
||||
<ArrowLeftCommon @click="handleCancel" />
|
||||
</div>
|
||||
|
||||
<!-- Content Container -->
|
||||
@ -125,7 +137,7 @@ const canSubmit = () => {
|
||||
<div class="w-full max-w-md space-y-8">
|
||||
<!-- Header -->
|
||||
<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" />
|
||||
</div>
|
||||
|
||||
@ -175,7 +187,7 @@ const canSubmit = () => {
|
||||
</div>
|
||||
|
||||
<!-- 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">
|
||||
<!-- Info Alert -->
|
||||
<Alert class="border-2 border-primary/20 bg-primary/5">
|
||||
@ -186,26 +198,21 @@ const canSubmit = () => {
|
||||
</Alert>
|
||||
|
||||
<!-- Password Field -->
|
||||
<passwordForm.Field name="password">
|
||||
<template #default="{ field }">
|
||||
<div class="space-y-2">
|
||||
<Label :for="field.name" class="text-base">Password</Label>
|
||||
<Label for="password" class="text-base">Password</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"
|
||||
:model-value="field.state.value"
|
||||
id="password"
|
||||
v-model="password"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
placeholder="Create a strong password"
|
||||
class="h-12 pl-11 pr-11 text-base"
|
||||
:class="{ 'border-destructive': isInvalid(field) }"
|
||||
autocomplete="new-password"
|
||||
@blur="field.handleBlur"
|
||||
@input="field.handleChange(($event.target as HTMLInputElement).value)"
|
||||
:class="{ 'border-destructive': passwordError }"
|
||||
@blur="passwordTouched = true"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
@ -216,34 +223,27 @@ const canSubmit = () => {
|
||||
<EyeOff v-else :size="20" />
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="isInvalid(field)" class="text-sm text-destructive">
|
||||
{{ field.state.meta.errors[0] }}
|
||||
<p v-if="passwordError" class="text-sm text-destructive">
|
||||
{{ passwordError }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</passwordForm.Field>
|
||||
|
||||
<!-- Confirm Password Field -->
|
||||
<passwordForm.Field name="confirmPassword">
|
||||
<template #default="{ field }">
|
||||
<div class="space-y-2">
|
||||
<Label :for="field.name" class="text-base">Confirm Password</Label>
|
||||
<Label for="confirmPassword" class="text-base">Confirm Password</Label>
|
||||
<div class="relative">
|
||||
<KeyRound
|
||||
:size="20"
|
||||
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"
|
||||
id="confirmPassword"
|
||||
v-model="confirmPassword"
|
||||
:type="showConfirmPassword ? 'text' : 'password'"
|
||||
placeholder="Re-enter your password"
|
||||
class="h-12 pl-11 pr-11 text-base"
|
||||
:class="{ 'border-destructive': field.state.value && !isPasswordMatch() }"
|
||||
autocomplete="new-password"
|
||||
@blur="field.handleBlur"
|
||||
@input="field.handleChange(($event.target as HTMLInputElement).value)"
|
||||
:class="{ 'border-destructive': confirmPassword && !isPasswordMatch }"
|
||||
@blur="confirmPasswordTouched = true"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
@ -254,12 +254,10 @@ const canSubmit = () => {
|
||||
<EyeOff v-else :size="20" />
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="field.state.value && !isPasswordMatch()" class="text-sm text-destructive">
|
||||
<p v-if="confirmPassword && !isPasswordMatch" class="text-sm text-destructive">
|
||||
Passwords do not match
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</passwordForm.Field>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="flex gap-3">
|
||||
@ -278,7 +276,7 @@ const canSubmit = () => {
|
||||
type="submit"
|
||||
size="lg"
|
||||
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" />
|
||||
{{ 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 goHome = () => {
|
||||
router.push({ name: 'home' })
|
||||
router.push({ name: 'wallet' })
|
||||
}
|
||||
</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 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: {
|
||||
// Tauri uses Chromium on Windows and WebKit on macOS and Linux
|
||||
target: process.env.TAURI_ENV_PLATFORM == 'windows' ? 'chrome105' : 'safari13',
|
||||
// Don't minify for debug builds
|
||||
minify: !process.env.TAURI_ENV_DEBUG ? 'esbuild' : false,
|
||||
// Produce sourcemaps for debug builds
|
||||
sourcemap: !!process.env.TAURI_ENV_DEBUG,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user