diff --git a/website_next/index.html b/website_next/index.html
index 8c8aef6c2..48fd4a920 100644
--- a/website_next/index.html
+++ b/website_next/index.html
@@ -116,6 +116,7 @@
+
diff --git a/website_next/wallets/add/index.js b/website_next/wallets/add/index.js
index 52611fbab..f45bfd82d 100644
--- a/website_next/wallets/add/index.js
+++ b/website_next/wallets/add/index.js
@@ -20,8 +20,8 @@ function createSourceInput() {
const input = document.createElement("input");
input.name = "source";
- input.type = redaction.isHidden() ? "password" : "text";
- input.setAttribute("data-wallets-private-input", "");
+ input.type = "text";
+ redaction.setInput(input);
input.autocomplete = "off";
input.placeholder = "xpub or descriptor...";
input.required = true;
@@ -38,10 +38,10 @@ export function createAddForm(options) {
const title = document.createElement("h2");
const name = document.createElement("input");
const source = createSourceInput();
- const actions = document.createElement("div");
+ const actions = document.createElement("footer");
const cancel = document.createElement("button");
const submit = document.createElement("button");
- const status = document.createElement("p");
+ const status = document.createElement("output");
const fields = [
createField("name", name),
createField("xpub or descriptor", source),
@@ -55,8 +55,8 @@ export function createAddForm(options) {
cancel.type = "button";
cancel.append("Cancel");
submit.type = "submit";
+ submit.classList.add("primary");
submit.append("Add");
- status.setAttribute("role", "status");
actions.append(cancel, submit);
form.append(
title,
diff --git a/website_next/wallets/add/style.css b/website_next/wallets/add/style.css
index 4afa740fd..41a8547d9 100644
--- a/website_next/wallets/add/style.css
+++ b/website_next/wallets/add/style.css
@@ -4,17 +4,11 @@ main.wallets {
display: grid;
gap: 0.75rem;
- > div {
+ > footer {
display: flex;
gap: 0.5rem;
justify-content: end;
}
-
- button[type="submit"] {
- border-color: var(--orange);
- color: var(--black);
- background: var(--orange);
- }
}
}
}
diff --git a/website_next/wallets/amount/index.js b/website_next/wallets/amount/index.js
index 9193b9c76..f789b5aa0 100644
--- a/website_next/wallets/amount/index.js
+++ b/website_next/wallets/amount/index.js
@@ -3,10 +3,19 @@ import { redaction } from "../redaction/index.js";
const SATS_PER_BTC = 100_000_000;
const FRACTION_DIGITS = 8;
const FIXED_PRIVATE_TEXT = "*****";
+const amounts = /** @type {BtcAmountRecord[]} */ ([]);
/**
* @typedef {Object} BtcAmountOptions
* @property {boolean} [signed]
+ *
+ * @typedef {Object} BtcAmount
+ * @property {number} sats
+ * @property {boolean} signed
+ *
+ * @typedef {Object} BtcAmountRecord
+ * @property {HTMLElement} element
+ * @property {BtcAmount} amount
*/
/**
@@ -126,30 +135,21 @@ function getBtcParts(sats, options = {}) {
return parts;
}
-/**
- * @param {number} sats
- * @param {BtcAmountOptions} [options]
- */
-export function formatBtc(sats, options = {}) {
- return getBtcParts(sats, options).map((part) => part.text).join("");
-}
-
/**
* @param {HTMLElement} element
- * @param {number} sats
- * @param {BtcAmountOptions} [options]
+ * @param {BtcAmount} amount
*/
-function renderBtcAmount(element, sats, options = {}) {
+function renderBtcAmount(element, amount) {
if (redaction.isHidden()) {
element.textContent = FIXED_PRIVATE_TEXT;
return;
}
- element.replaceChildren(...getBtcParts(sats, options).map((part) => {
+ element.replaceChildren(...getBtcParts(amount.sats, amount).map((part) => {
const span = document.createElement("span");
if (part.muted) {
- span.setAttribute("data-wallets-btc-muted", "");
+ span.classList.add("muted");
}
span.append(part.text);
@@ -165,28 +165,26 @@ function renderBtcAmount(element, sats, options = {}) {
*/
export function createBtcAmount(tag, sats, options = {}) {
const element = document.createElement(tag);
+ const amount = {
+ sats,
+ signed: options.signed === true,
+ };
- element.setAttribute("data-wallets-btc-amount", String(sats));
- element.setAttribute(
- "data-wallets-btc-signed",
- options.signed ? "true" : "false",
- );
- renderBtcAmount(element, sats, options);
+ element.classList.add("wallets__amount");
+ amounts.push({ element, amount });
+ renderBtcAmount(element, amount);
return element;
}
-/**
- * @param {HTMLElement} root
- */
-export function syncBtcAmounts(root) {
- const amounts = root.querySelectorAll("[data-wallets-btc-amount]");
+export function syncBtcAmounts() {
+ for (let index = amounts.length - 1; index >= 0; index -= 1) {
+ const { element, amount } = amounts[index];
- for (const amount of amounts) {
- const element = /** @type {HTMLElement} */ (amount);
- const sats = Number(element.getAttribute("data-wallets-btc-amount"));
- const signed = element.getAttribute("data-wallets-btc-signed") === "true";
-
- renderBtcAmount(element, sats, { signed });
+ if (!element.isConnected) {
+ amounts.splice(index, 1);
+ } else {
+ renderBtcAmount(element, amount);
+ }
}
}
diff --git a/website_next/wallets/amount/style.css b/website_next/wallets/amount/style.css
new file mode 100644
index 000000000..9add21f4f
--- /dev/null
+++ b/website_next/wallets/amount/style.css
@@ -0,0 +1,7 @@
+main.wallets {
+ .wallets__amount {
+ .muted {
+ color: color-mix(in oklch, currentColor 45%, transparent);
+ }
+ }
+}
diff --git a/website_next/wallets/empty/index.js b/website_next/wallets/empty/index.js
index a6d0ed649..8552bb0a9 100644
--- a/website_next/wallets/empty/index.js
+++ b/website_next/wallets/empty/index.js
@@ -15,6 +15,7 @@ export function createEmpty(options) {
text.append("No wallet imported yet");
button.type = "button";
+ button.classList.add("primary");
button.append("Add wallet");
button.addEventListener("click", options.onAdd);
empty.append(text, button);
diff --git a/website_next/wallets/empty/style.css b/website_next/wallets/empty/style.css
index ab1fd0b7b..5ff46b79a 100644
--- a/website_next/wallets/empty/style.css
+++ b/website_next/wallets/empty/style.css
@@ -3,17 +3,11 @@ main.wallets {
display: grid;
gap: 1rem;
place-content: center;
- min-height: 16rem;
+ min-height: calc(100dvh - 2 * var(--offset));
text-align: center;
p {
margin: 0;
}
-
- > button {
- border-color: var(--orange);
- color: var(--black);
- background: var(--orange);
- }
}
}
diff --git a/website_next/wallets/index.js b/website_next/wallets/index.js
index dae28b35a..7687530de 100644
--- a/website_next/wallets/index.js
+++ b/website_next/wallets/index.js
@@ -1,5 +1,4 @@
import { brk } from "../utils/client.js";
-import { createGroupedAddress } from "./wallet/address/index.js";
import {
setStatus,
withBusy,
@@ -81,8 +80,8 @@ export function createWalletsPage() {
}
privacyButton.addEventListener("click", () => {
- redaction.toggle(main, privacyButton, createGroupedAddress);
- syncBtcAmounts(main);
+ redaction.toggle(privacyButton);
+ syncBtcAmounts();
});
lockButton.addEventListener("click", () => {
@@ -209,8 +208,6 @@ export function createWalletsPage() {
const current = vault.current();
const empty = !needsSetup && !locked && !current;
- main.toggleAttribute("data-wallets-page-locked", locked || needsSetup);
- main.toggleAttribute("data-wallets-page-empty", empty);
header.hidden = locked || needsSetup || empty;
selectorElement.hidden = locked || needsSetup || empty;
lockButton.hidden = locked || needsSetup || !vault.hasPassword;
diff --git a/website_next/wallets/layout/index.js b/website_next/wallets/layout/index.js
index 350ee0144..a7d467bfb 100644
--- a/website_next/wallets/layout/index.js
+++ b/website_next/wallets/layout/index.js
@@ -19,19 +19,22 @@ import { createElement } from "../dom.js";
export function createLayout() {
const main = createElement("main", "wallets");
const header = document.createElement("header");
- const actions = document.createElement("div");
+ const actions = document.createElement("menu");
const addButton = document.createElement("button");
const privacyButton = document.createElement("button");
const lockButton = document.createElement("button");
const selector = createElement("section", "wallets__selector");
- const walletList = document.createElement("div");
- const content = document.createElement("section");
+ const walletList = document.createElement("nav");
+ const content = document.createElement("article");
const addDialog = createElement("dialog", "wallets__dialog");
addButton.type = "button";
+ addButton.classList.add("primary");
addButton.append("Add watch-only wallet");
privacyButton.type = "button";
+ privacyButton.classList.add("primary");
lockButton.type = "button";
+ lockButton.classList.add("primary");
lockButton.append("Lock");
content.setAttribute("aria-live", "polite");
walletList.setAttribute("tabindex", "0");
diff --git a/website_next/wallets/layout/style.css b/website_next/wallets/layout/style.css
index 0dd63ff3f..d7427d444 100644
--- a/website_next/wallets/layout/style.css
+++ b/website_next/wallets/layout/style.css
@@ -9,25 +9,22 @@ main.wallets {
justify-content: start;
}
- > div {
+ > menu {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
justify-content: end;
+ margin: 0;
+ padding: 0;
+ list-style: none;
@media (max-width: 34rem) {
justify-content: start;
}
-
- > button {
- border-color: var(--orange);
- color: var(--black);
- background: var(--orange);
- }
}
}
- > section[aria-live] {
+ > article {
display: grid;
gap: 1.5rem;
}
diff --git a/website_next/wallets/lock/index.js b/website_next/wallets/lock/index.js
index 5a4311101..04c28a193 100644
--- a/website_next/wallets/lock/index.js
+++ b/website_next/wallets/lock/index.js
@@ -21,16 +21,16 @@ function bindResetHold(button, onReset) {
clearTimeout(timer);
timer = undefined;
- button.removeAttribute("data-wallets-holding");
+ button.classList.remove("holding");
}
function start() {
if (timer !== undefined) return;
- button.setAttribute("data-wallets-holding", "");
+ button.classList.add("holding");
timer = window.setTimeout(() => {
timer = undefined;
- button.removeAttribute("data-wallets-holding");
+ button.classList.remove("holding");
onReset();
}, RESET_HOLD_MS);
}
@@ -68,7 +68,7 @@ export function createLock(options) {
const password = document.createElement("input");
const button = document.createElement("button");
const reset = document.createElement("button");
- const status = document.createElement("p");
+ const status = document.createElement("output");
title.append("Unlock vault");
password.name = "password";
@@ -78,10 +78,10 @@ export function createLock(options) {
password.placeholder = "Password";
password.required = true;
button.type = "submit";
+ button.classList.add("primary");
button.append("Unlock");
reset.type = "button";
reset.append("Reset vault");
- status.setAttribute("role", "status");
form.append(password, button);
form.addEventListener("submit", (event) => {
event.preventDefault();
diff --git a/website_next/wallets/lock/style.css b/website_next/wallets/lock/style.css
index 84b61ac2e..51651976f 100644
--- a/website_next/wallets/lock/style.css
+++ b/website_next/wallets/lock/style.css
@@ -3,7 +3,9 @@ main.wallets {
display: grid;
gap: 1rem;
place-content: center;
- min-height: 16rem;
+ width: min(100%, 28rem);
+ min-height: calc(100dvh - 2 * var(--offset));
+ margin-inline: auto;
text-align: center;
> h1 {
@@ -15,20 +17,13 @@ main.wallets {
> form {
display: grid;
- grid-template-columns: minmax(12rem, 18rem) auto;
+ grid-template-columns: minmax(0, 1fr) auto;
gap: 0.75rem;
align-items: end;
- justify-content: center;
@media (max-width: 34rem) {
grid-template-columns: 1fr;
}
-
- > button {
- border-color: var(--orange);
- color: var(--black);
- background: var(--orange);
- }
}
> button {
@@ -49,7 +44,7 @@ main.wallets {
transform-origin: left;
}
- &[data-wallets-holding]::before {
+ &.holding::before {
transform: scaleX(1);
transition: transform 2s linear;
}
diff --git a/website_next/wallets/lookup/hash.js b/website_next/wallets/lookup/hash.js
index df3f238f1..8cb007913 100644
--- a/website_next/wallets/lookup/hash.js
+++ b/website_next/wallets/lookup/hash.js
@@ -83,13 +83,6 @@ function rapidHashV3(bytes) {
let a = readU64(bytes, length - 16) ^ BigInt(length);
let b = readU64(bytes, length - 8);
- if (length > 32) {
- seed = rapidMix(
- readU64(bytes, 16) ^ DEFAULT_SECRETS[2],
- readU64(bytes, 24) ^ seed,
- );
- }
-
a ^= DEFAULT_SECRETS[1];
b ^= seed;
diff --git a/website_next/wallets/redaction/index.js b/website_next/wallets/redaction/index.js
index 5b38b3378..a340bd994 100644
--- a/website_next/wallets/redaction/index.js
+++ b/website_next/wallets/redaction/index.js
@@ -1,6 +1,15 @@
const FIXED_PRIVATE_TEXT = "*****";
let hidden = false;
+const effects = /** @type {RedactionEffect[]} */ ([]);
+
+/**
+ * @typedef {"exact" | "fixed"} RedactionMode
+ *
+ * @typedef {Object} RedactionEffect
+ * @property {HTMLElement} element
+ * @property {() => void} sync
+ */
function isHidden() {
return hidden;
@@ -17,23 +26,30 @@ function createText(value) {
/**
* @param {string} value
- * @param {string | null} mode
+ * @param {RedactionMode} mode
*/
function mask(value, mode) {
return mode === "fixed" ? FIXED_PRIVATE_TEXT : createText(value);
}
+/**
+ * @param {HTMLElement} element
+ * @param {() => void} sync
+ */
+function addEffect(element, sync) {
+ effects.push({ element, sync });
+ sync();
+}
+
/**
* @param {HTMLElement} element
* @param {string} value
- * @param {"exact" | "fixed"} [mode]
+ * @param {RedactionMode} [mode]
*/
function setValue(element, value, mode = "exact") {
- element.setAttribute("data-wallets-private-value", value);
- element.setAttribute("data-wallets-private-mode", mode);
- element.textContent = hidden
- ? mask(value, mode)
- : value;
+ addEffect(element, () => {
+ element.textContent = hidden ? mask(value, mode) : value;
+ });
}
/**
@@ -41,15 +57,36 @@ function setValue(element, value, mode = "exact") {
* @param {string} value
*/
function setTitle(element, value) {
- element.setAttribute("data-wallets-private-title", value);
- element.title = hidden ? createText(value) : value;
+ addEffect(element, () => {
+ element.title = hidden ? createText(value) : value;
+ });
+}
+
+/**
+ * @param {HTMLElement} element
+ * @param {string} value
+ * @param {(text: string) => void} render
+ */
+function setAddress(element, value, render) {
+ addEffect(element, () => {
+ render(hidden ? createText(value) : value);
+ });
+}
+
+/**
+ * @param {HTMLInputElement} input
+ */
+function setInput(input) {
+ addEffect(input, () => {
+ input.type = hidden ? "password" : "text";
+ });
}
/**
* @template {keyof HTMLElementTagNameMap} Tag
* @param {Tag} tag
* @param {string} value
- * @param {"exact" | "fixed"} [mode]
+ * @param {RedactionMode} [mode]
*/
function createValue(tag, value, mode = "exact") {
const element = document.createElement(tag);
@@ -59,44 +96,14 @@ function createValue(tag, value, mode = "exact") {
return element;
}
-/**
- * @param {HTMLElement} root
- * @param {(text: string) => HTMLElement} createAddress
- */
-function sync(root, createAddress) {
- const values = root.querySelectorAll("[data-wallets-private-value]");
- const titles = root.querySelectorAll("[data-wallets-private-title]");
- const addresses = root.querySelectorAll("[data-wallets-private-address]");
- const inputs = root.querySelectorAll("[data-wallets-private-input]");
+function sync() {
+ for (let index = effects.length - 1; index >= 0; index -= 1) {
+ const effect = effects[index];
- for (const value of values) {
- const text = value.getAttribute("data-wallets-private-value") ?? "";
- const mode = value.getAttribute("data-wallets-private-mode");
-
- value.textContent = hidden
- ? mask(text, mode)
- : text;
- }
-
- for (const element of titles) {
- const title = /** @type {HTMLElement} */ (element);
- const text = title.getAttribute("data-wallets-private-title") ?? "";
-
- title.title = hidden
- ? createText(text)
- : text;
- }
-
- for (const address of addresses) {
- const text = address.getAttribute("data-wallets-private-address") ?? "";
- const next = hidden ? createText(text) : text;
-
- address.replaceChildren(...createAddress(next).childNodes);
- }
-
- for (const input of inputs) {
- if (input instanceof HTMLInputElement) {
- input.type = hidden ? "password" : "text";
+ if (!effect.element.isConnected) {
+ effects.splice(index, 1);
+ } else {
+ effect.sync();
}
}
}
@@ -110,13 +117,11 @@ function syncButton(button) {
}
/**
- * @param {HTMLElement} root
* @param {HTMLButtonElement} button
- * @param {(text: string) => HTMLElement} createAddress
*/
-function toggle(root, button, createAddress) {
+function toggle(button) {
hidden = !hidden;
- sync(root, createAddress);
+ sync();
syncButton(button);
}
@@ -125,6 +130,8 @@ export const redaction = /** @type {const} */ ({
createText,
setValue,
setTitle,
+ setAddress,
+ setInput,
createValue,
syncButton,
toggle,
diff --git a/website_next/wallets/selector/index.js b/website_next/wallets/selector/index.js
index 9460f4a20..5d847bf43 100644
--- a/website_next/wallets/selector/index.js
+++ b/website_next/wallets/selector/index.js
@@ -8,14 +8,21 @@
* @typedef {Object} WalletSelectorOptions
* @property {() => string} getSelectedId
* @property {(walletId: string) => void} onSelect
+ *
+ * @typedef {Object} WalletSelectorButton
+ * @property {HTMLButtonElement} button
+ * @property {string} id
*/
/**
* @param {HTMLElement} walletList
* @param {StoredWallet[]} wallets
* @param {WalletSelectorOptions} options
+ * @returns {WalletSelectorButton[]}
*/
function renderButtons(walletList, wallets, options) {
+ const buttons = /** @type {WalletSelectorButton[]} */ ([]);
+
walletList.replaceChildren();
for (const wallet of wallets) {
@@ -24,13 +31,15 @@ function renderButtons(walletList, wallets, options) {
button.type = "button";
button.setAttribute("aria-pressed", selected ? "true" : "false");
- button.setAttribute("data-wallet-id", wallet.id);
button.append(wallet.name);
button.addEventListener("click", () => {
options.onSelect(wallet.id);
});
+ buttons.push({ button, id: wallet.id });
walletList.append(button);
}
+
+ return buttons;
}
/**
@@ -38,29 +47,29 @@ function renderButtons(walletList, wallets, options) {
* @param {WalletSelectorOptions} options
*/
export function createSelector(walletList, options) {
- function selectSnappedWallet() {
- const buttons = [...walletList.querySelectorAll("button")];
+ /** @type {WalletSelectorButton[]} */
+ let buttons = [];
+ function selectSnappedWallet() {
if (buttons.length === 0) return;
const listRect = walletList.getBoundingClientRect();
const listCenter = listRect.left + listRect.width / 2;
- const closest = buttons.reduce((best, button) => {
- const rect = button.getBoundingClientRect();
+ const closest = buttons.reduce((best, item) => {
+ const rect = item.button.getBoundingClientRect();
const center = rect.left + rect.width / 2;
const distance = Math.abs(center - listCenter);
return distance < best.distance
- ? { button, distance }
+ ? { item, distance }
: best;
}, {
- button: buttons[0],
+ item: buttons[0],
distance: Number.POSITIVE_INFINITY,
});
- const id = closest.button.getAttribute("data-wallet-id");
- if (id && id !== options.getSelectedId()) {
- options.onSelect(id);
+ if (closest.item.id !== options.getSelectedId()) {
+ options.onSelect(closest.item.id);
}
}
@@ -90,12 +99,13 @@ export function createSelector(walletList, options) {
return {
clear() {
walletList.replaceChildren();
+ buttons = [];
},
/**
* @param {StoredWallet[]} wallets
*/
render(wallets) {
- renderButtons(walletList, wallets, options);
+ buttons = renderButtons(walletList, wallets, options);
},
};
}
diff --git a/website_next/wallets/selector/style.css b/website_next/wallets/selector/style.css
index 88505fdaa..28fcf58ab 100644
--- a/website_next/wallets/selector/style.css
+++ b/website_next/wallets/selector/style.css
@@ -2,7 +2,7 @@ main.wallets {
.wallets__selector {
min-width: 0;
- > div {
+ > nav {
display: flex;
gap: 1rem;
min-width: 0;
diff --git a/website_next/wallets/setup/index.js b/website_next/wallets/setup/index.js
index 73e7db466..17b6f4502 100644
--- a/website_next/wallets/setup/index.js
+++ b/website_next/wallets/setup/index.js
@@ -22,11 +22,11 @@ function createDescriptionText(text) {
export function createSetup(options) {
const section = createElement("section", "wallets__setup");
const title = document.createElement("h1");
- const description = document.createElement("div");
+ const description = document.createElement("article");
const form = document.createElement("form");
const password = document.createElement("input");
const button = document.createElement("button");
- const status = document.createElement("p");
+ const status = document.createElement("output");
title.append("Wallets");
description.append(
@@ -49,8 +49,8 @@ export function createSetup(options) {
password.placeholder = "Set password";
password.required = true;
button.type = "submit";
+ button.classList.add("primary");
button.append("Continue");
- status.setAttribute("role", "status");
form.append(password, button);
form.addEventListener("submit", (event) => {
event.preventDefault();
diff --git a/website_next/wallets/setup/style.css b/website_next/wallets/setup/style.css
index 2460f77e2..2dcd0bd0c 100644
--- a/website_next/wallets/setup/style.css
+++ b/website_next/wallets/setup/style.css
@@ -3,8 +3,8 @@ main.wallets {
display: grid;
gap: 1rem;
place-content: center;
- max-width: 36rem;
- min-height: 16rem;
+ width: min(100%, 36rem);
+ min-height: calc(100dvh - 2 * var(--offset));
margin-inline: auto;
h1 {
@@ -27,7 +27,7 @@ main.wallets {
margin: 0;
}
- > div {
+ > article {
display: grid;
gap: 0.75rem;
color: var(--gray);
@@ -41,18 +41,11 @@ main.wallets {
grid-template-columns: minmax(0, 1fr) auto;
gap: 0.75rem;
align-items: end;
- justify-content: center;
width: 100%;
@media (max-width: 34rem) {
grid-template-columns: 1fr;
}
-
- > button {
- border-color: var(--orange);
- color: var(--black);
- background: var(--orange);
- }
}
}
}
diff --git a/website_next/wallets/style.css b/website_next/wallets/style.css
index 2220c9852..1d7d07128 100644
--- a/website_next/wallets/style.css
+++ b/website_next/wallets/style.css
@@ -11,12 +11,8 @@ main.wallets {
padding: var(--offset) var(--page-x);
scroll-padding-top: var(--offset);
- &:is([data-wallets-page-empty], [data-wallets-page-locked]) {
- min-height: 100dvh;
- align-content: center;
- }
-
- [role="status"] {
+ output {
+ display: block;
min-height: var(--line-height-sm);
margin: 0;
color: var(--gray);
@@ -24,10 +20,6 @@ main.wallets {
line-height: var(--line-height-sm);
}
- [data-wallets-btc-muted] {
- color: color-mix(in oklch, currentColor 45%, transparent);
- }
-
:is(input, select, button) {
min-width: 0;
height: var(--control-height);
@@ -49,6 +41,12 @@ main.wallets {
outline-offset: 2px;
}
+ button.primary {
+ border-color: var(--orange);
+ color: var(--black);
+ background: var(--orange);
+ }
+
input::placeholder {
color: color-mix(in oklch, var(--gray) 70%, transparent);
}
diff --git a/website_next/wallets/wallet/actions/style.css b/website_next/wallets/wallet/actions/style.css
index b1a36da48..132b126a5 100644
--- a/website_next/wallets/wallet/actions/style.css
+++ b/website_next/wallets/wallet/actions/style.css
@@ -9,18 +9,11 @@ main.wallets {
@media (max-width: 34rem) {
justify-content: start;
}
-
- > button {
- border-color: var(--orange);
- color: var(--black);
- background: var(--orange);
-
- &:disabled {
- border-color: color-mix(in oklch, var(--gray) 35%, transparent);
- color: var(--gray);
- background: transparent;
- cursor: default;
- }
+ > button:disabled {
+ border-color: color-mix(in oklch, var(--gray) 35%, transparent);
+ color: var(--gray);
+ background: transparent;
+ cursor: default;
}
}
}
diff --git a/website_next/wallets/wallet/address/index.js b/website_next/wallets/wallet/address/index.js
index 375bfc52a..c447065d1 100644
--- a/website_next/wallets/wallet/address/index.js
+++ b/website_next/wallets/wallet/address/index.js
@@ -17,15 +17,14 @@ export function createGroupedAddress(text) {
const group = document.createElement("span");
for (const character of groups[groupIndex]) {
- const span = document.createElement("span");
+ if (Number.isNaN(Number(character))) {
+ group.append(character);
+ } else {
+ const number = document.createElement("var");
- span.setAttribute(
- "data-wallets-address-character",
- Number.isNaN(Number(character)) ? "letter" : "number",
- );
-
- span.append(character);
- group.append(span);
+ number.append(character);
+ group.append(number);
+ }
}
element.append(group);
@@ -41,12 +40,11 @@ export function createGroupedAddress(text) {
* @param {string} address
*/
function createPrivateAddress(address) {
- const hidden = redaction.createText(address);
- const element = redaction.isHidden()
- ? createGroupedAddress(hidden)
- : createGroupedAddress(address);
+ const element = createGroupedAddress(address);
- element.setAttribute("data-wallets-private-address", address);
+ redaction.setAddress(element, address, (text) => {
+ element.replaceChildren(...createGroupedAddress(text).childNodes);
+ });
return element;
}
@@ -55,10 +53,9 @@ function createPrivateAddress(address) {
* @param {WalletAddress} row
*/
function createAddressBadge(row) {
- const badge = document.createElement("span");
+ const badge = document.createElement("b");
const label = row.branchLabel?.toLowerCase() ?? "address";
- badge.setAttribute("data-wallets-address-branch", label);
badge.append(label, ` #${formatNumber(row.index)}`);
return badge;
@@ -69,7 +66,7 @@ function createAddressBadge(row) {
*/
export function createAddressCellContent(row) {
const element = createElement("div", "wallets__address-cell");
- const anonSet = document.createElement("span");
+ const anonSet = document.createElement("small");
anonSet.append(`anon set: ${formatNumber(row.historyBucketSize)}`);
element.append(
diff --git a/website_next/wallets/wallet/address/style.css b/website_next/wallets/wallet/address/style.css
index fa336fadf..4197d0530 100644
--- a/website_next/wallets/wallet/address/style.css
+++ b/website_next/wallets/wallet/address/style.css
@@ -3,7 +3,7 @@ main.wallets {
display: grid;
gap: 0.25rem;
- > span:first-child {
+ > b {
display: inline-flex;
align-items: center;
justify-self: start;
@@ -12,10 +12,11 @@ main.wallets {
border-radius: 0.25rem;
padding: 0 0.25rem;
color: color-mix(in oklch, var(--white) 76%, var(--gray));
+ font-weight: 400;
line-height: 1;
}
- > span:last-child {
+ > small {
color: var(--gray);
font-size: var(--font-size-xs);
line-height: var(--line-height-xs);
@@ -29,15 +30,13 @@ main.wallets {
max-width: 40rem;
> span {
- white-space: nowrap;
- }
-
- [data-wallets-address-character="letter"] {
color: var(--white);
- }
+ white-space: nowrap;
- [data-wallets-address-character="number"] {
- color: color-mix(in oklch, var(--white) 50%, var(--gray));
+ > var {
+ color: color-mix(in oklch, var(--white) 50%, var(--gray));
+ font-style: normal;
+ }
}
}
}
diff --git a/website_next/wallets/wallet/index.js b/website_next/wallets/wallet/index.js
index 2901a16f0..d1648da3b 100644
--- a/website_next/wallets/wallet/index.js
+++ b/website_next/wallets/wallet/index.js
@@ -22,11 +22,10 @@ import { renderTransactions } from "./transactions/index.js";
export function createWalletPanel() {
const actions = createElement("section", "wallets__wallet-actions");
const summary = createElement("section", "wallets__summary");
- const status = document.createElement("p");
+ const status = document.createElement("output");
const results = createElement("section", "wallets__results");
actions.setAttribute("aria-label", "Wallet actions");
- status.setAttribute("role", "status");
summary.setAttribute("aria-label", "Wallets summary");
results.setAttribute("aria-label", "Wallets results");
diff --git a/website_next/wallets/wallet/receive/index.js b/website_next/wallets/wallet/receive/index.js
index 01ff2ea74..fa220f43e 100644
--- a/website_next/wallets/wallet/receive/index.js
+++ b/website_next/wallets/wallet/receive/index.js
@@ -56,7 +56,7 @@ function createReceiveQr(receiveAddress) {
* @param {ReceiveAddress} receiveAddress
*/
function createReceiveAddress(receiveAddress) {
- const element = document.createElement("div");
+ const element = document.createElement("p");
element.append(createGroupedAddress(receiveAddress.address));
@@ -73,24 +73,28 @@ async function copyReceiveAddress(receiveAddress, copy) {
}
/**
+ * @param {HTMLElement} host
* @param {ReceiveAddress} receiveAddress
*/
-function openReceiveDialog(receiveAddress) {
- const main = document.querySelector("main.wallets") ?? document.body;
+function openReceiveDialog(host, receiveAddress) {
const dialog = createElement(
"dialog",
"wallets__dialog wallets__receive-dialog",
);
- const content = document.createElement("div");
- const actions = document.createElement("div");
+ const content = document.createElement("article");
+ const actions = document.createElement("footer");
const copy = document.createElement("button");
+ const closeForm = document.createElement("form");
const close = document.createElement("button");
copy.type = "button";
+ copy.classList.add("primary");
copy.append("Copy");
- close.type = "button";
+ closeForm.method = "dialog";
+ close.type = "submit";
close.append("Close");
- actions.append(copy, close);
+ closeForm.append(close);
+ actions.append(copy, closeForm);
content.append(
createReceiveTitle(receiveAddress),
createReceiveQr(receiveAddress),
@@ -98,16 +102,13 @@ function openReceiveDialog(receiveAddress) {
actions,
);
dialog.append(content);
- main.append(dialog);
+ host.append(dialog);
copy.addEventListener("click", () => {
void copyReceiveAddress(receiveAddress, copy).catch(() => {
copy.textContent = "Copy failed";
});
});
- close.addEventListener("click", () => {
- dialog.close();
- });
dialog.addEventListener("close", () => {
dialog.remove();
});
@@ -128,10 +129,11 @@ export function renderReceiveButton(element, receiveAddress) {
button.type = "button";
button.disabled = !receiveAddress;
+ button.classList.add("primary");
button.append("Receive");
button.addEventListener("click", () => {
if (receiveAddress) {
- openReceiveDialog(receiveAddress);
+ openReceiveDialog(element, receiveAddress);
}
});
element.append(button);
diff --git a/website_next/wallets/wallet/receive/style.css b/website_next/wallets/wallet/receive/style.css
index 1beb16b43..eb2ad337d 100644
--- a/website_next/wallets/wallet/receive/style.css
+++ b/website_next/wallets/wallet/receive/style.css
@@ -2,11 +2,11 @@ main.wallets {
.wallets__receive-dialog {
width: min(100% - 2rem, 32rem);
- > div {
+ > article {
display: grid;
gap: 1rem;
- h2 {
+ > h2 {
margin: 0;
font-size: var(--font-size-lg);
font-weight: 400;
@@ -22,22 +22,20 @@ main.wallets {
image-rendering: pixelated;
}
- > div:first-of-type {
+ > p {
+ margin: 0;
color: var(--white);
font-size: var(--font-size-sm);
line-height: var(--line-height-sm);
}
- > div:last-of-type {
+ > footer {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
justify-content: end;
-
- > button:first-child {
- border-color: var(--orange);
- color: var(--black);
- background: var(--orange);
+ > form {
+ margin: 0;
}
}
}
diff --git a/website_next/wallets/wallet/transactions/history.js b/website_next/wallets/wallet/transactions/history.js
index 26fb63fe2..6a6c91e5d 100644
--- a/website_next/wallets/wallet/transactions/history.js
+++ b/website_next/wallets/wallet/transactions/history.js
@@ -18,8 +18,6 @@ const historyByBucketKey =
/**
* @typedef {Object} AddressHistory
* @property {unknown[]} transactions
- * @property {number} fetchedAddressCount
- * @property {number} bucketSize
*/
/**
@@ -29,17 +27,6 @@ function createBucketKey(addresses) {
return [...addresses].sort().join("\n");
}
-/**
- * @param {WalletAddress} address
- */
-function assertHistoryIsReasonable(address) {
- if (address.txCount > MAX_SELECTED_ADDRESS_TXS) {
- throw new Error(
- `History disabled for addresses over ${MAX_SELECTED_ADDRESS_TXS} transactions`,
- );
- }
-}
-
/**
* @param {AddressHistoryClient} client
* @param {readonly string[]} addresses
@@ -67,13 +54,12 @@ async function fetchBucketHistory(client, addresses) {
* @returns {Promise}
*/
async function load(client, address) {
- assertHistoryIsReasonable(address);
-
- if (address.historyAddresses.length === 0) {
+ if (
+ address.txCount > MAX_SELECTED_ADDRESS_TXS ||
+ address.historyAddresses.length === 0
+ ) {
return {
transactions: [],
- fetchedAddressCount: 0,
- bucketSize: address.historyBucketSize,
};
}
@@ -94,8 +80,6 @@ async function load(client, address) {
return {
transactions: bucketHistory.get(address.address) ?? [],
- fetchedAddressCount: address.historyAddresses.length,
- bucketSize: address.historyBucketSize,
};
}
diff --git a/website_next/wallets/wallet/transactions/index.js b/website_next/wallets/wallet/transactions/index.js
index 7a22192f5..3fad84b60 100644
--- a/website_next/wallets/wallet/transactions/index.js
+++ b/website_next/wallets/wallet/transactions/index.js
@@ -47,10 +47,10 @@ function appendTransactionDetail(element, transaction) {
* @param {WalletTransaction} transaction
*/
function createTransactionDetails(transaction) {
- const content = document.createElement("div");
+ const content = document.createElement("section");
const txid = document.createElement("code");
const meta = document.createElement("p");
- const list = document.createElement("div");
+ const list = document.createElement("ul");
redaction.setTitle(txid, transaction.txid);
redaction.setValue(txid, transaction.txid);
@@ -62,7 +62,10 @@ function createTransactionDetails(transaction) {
createBtcAmount("span", transaction.fee),
);
for (const address of transaction.addresses) {
- list.append(createAddressCellContent(address.walletAddress));
+ const item = document.createElement("li");
+
+ item.append(createAddressCellContent(address.walletAddress));
+ list.append(item);
}
content.append(txid, meta, list);
@@ -74,7 +77,7 @@ function createTransactionDetails(transaction) {
*/
function createTransactionRow(transaction) {
const row = document.createElement("li");
- const main = document.createElement("div");
+ const main = document.createElement("header");
const label = document.createElement("strong");
const amount = createBtcAmount(
"span",
@@ -87,8 +90,12 @@ function createTransactionRow(transaction) {
const summary = document.createElement("summary");
label.append(typeLabels[transaction.type]);
- amount.dataset.walletsTxAmount =
- transaction.amount >= 0 ? "positive" : "negative";
+ if (transaction.amount > 0) {
+ amount.classList.add("positive");
+ }
+ if (transaction.amount < 0) {
+ amount.classList.add("negative");
+ }
redaction.setTitle(txid, transaction.txid);
redaction.setValue(txid, formatTxid(transaction.txid));
summary.append("Details");
diff --git a/website_next/wallets/wallet/transactions/style.css b/website_next/wallets/wallet/transactions/style.css
index c7a69c87b..ac2970e83 100644
--- a/website_next/wallets/wallet/transactions/style.css
+++ b/website_next/wallets/wallet/transactions/style.css
@@ -55,7 +55,7 @@ main.wallets {
grid-template-columns: 1fr;
}
- > div:first-child {
+ > header {
display: flex;
gap: 1rem;
align-items: baseline;
@@ -71,11 +71,11 @@ main.wallets {
color: var(--white);
white-space: nowrap;
- &[data-wallets-tx-amount="positive"] {
+ &.positive {
color: var(--green);
}
- &[data-wallets-tx-amount="negative"] {
+ &.negative {
color: var(--red);
}
}
@@ -135,7 +135,7 @@ main.wallets {
}
}
- > div {
+ > section {
display: grid;
gap: 1rem;
@@ -145,9 +145,12 @@ main.wallets {
font-family: inherit;
}
- > div {
+ > ul {
display: grid;
gap: 0.75rem;
+ margin: 0;
+ padding: 0;
+ list-style: none;
}
}
}