diff --git a/dist/index.html b/dist/index.html index 4a8c710..76f7aff 100644 --- a/dist/index.html +++ b/dist/index.html @@ -2326,6 +2326,20 @@ function merge_text_nodes(text) { //#endregion //#region node_modules/svelte/src/internal/client/dom/elements/misc.js /** +* @param {HTMLElement} dom +* @param {boolean} value +* @returns {void} +*/ +function autofocus(dom, value) { + if (value) { + const body = document.body; + dom.autofocus = true; + queue_micro_task(() => { + if (document.activeElement === body) dom.focus(); + }); + } +} +/** * The child of a textarea actually corresponds to the defaultValue property, so we need * to remove it upon hydration to avoid a bug when someone resets the form value. * @param {HTMLTextAreaElement} dom @@ -4817,7 +4831,10 @@ var app$1 = new AppStore(); * @returns {string} */ function generateId() { - return `${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`; + const timestamp = Date.now().toString(36); + const randomBytes = new Uint8Array(4); + crypto.getRandomValues(randomBytes); + return `${timestamp}_${Array.from(randomBytes).map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, 8)}`; } /** * @typedef {Object} CredentialEntry @@ -4955,7 +4972,7 @@ function validateGroup(name) { * * The derived encryption key is kept in memory only — never written to disk. */ -var PBKDF2_ITERATIONS = 1e5; +var PBKDF2_ITERATIONS = 6e5; var SALT_LENGTH = 16; var IV_LENGTH = 12; /** @@ -5088,9 +5105,19 @@ function generatePassword({ length = 16, uppercase = true, lowercase = true, dig charset = charset.split("").filter((c) => !excludeSet.has(c)).join(""); } if (!charset) throw new Error("Password charset is empty — enable at least one character type"); - const randomValues = crypto.getRandomValues(new Uint8Array(length)); + const charsetLength = charset.length; + const maxValid = 256 - 256 % charsetLength; + const randomBytes = new Uint8Array(length * 2); let password = ""; - for (let i = 0; i < length; i++) password += charset[randomValues[i] % charset.length]; + let byteIdx = 0; + while (password.length < length) { + if (byteIdx >= randomBytes.length) { + crypto.getRandomValues(randomBytes); + byteIdx = 0; + } + const byte = randomBytes[byteIdx++]; + if (byte < maxValid) password += charset[byte % charsetLength]; + } return password; } //#endregion @@ -5536,17 +5563,6 @@ async function exportAll() { }; } /** -* Convert a base64 string back to Uint8Array. -* @param {string} base64 -* @returns {Uint8Array} -*/ -function base64ToUint8Array(base64) { - const binary = atob(base64); - const bytes = new Uint8Array(binary.length); - for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i); - return bytes; -} -/** * Import data from a previously exported JSON object. * * Requires the source vault's master password to decrypt entries, then @@ -5682,6 +5698,7 @@ function LockScreen($$anchor, $$props) { var div_3 = child(form); var input = sibling(child(div_3), 2); remove_input_defaults(input); + autofocus(input, true); reset(div_3); var node_1 = sibling(div_3, 2); var consequent_1 = ($$anchor) => { diff --git a/src/components/LockScreen.svelte b/src/components/LockScreen.svelte index 4630bfb..d453c94 100644 --- a/src/components/LockScreen.svelte +++ b/src/components/LockScreen.svelte @@ -95,6 +95,7 @@ bind:value={masterPassword} placeholder="Enter master password" autocomplete="current-password" + autofocus disabled={loading} />