viveworker 0.7.0-beta.3 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/web/app.css CHANGED
@@ -33,10 +33,16 @@
33
33
  }
34
34
 
35
35
  html {
36
+ width: 100%;
37
+ max-width: 100%;
38
+ overflow-x: hidden;
36
39
  background: var(--bg);
37
40
  }
38
41
 
39
42
  body {
43
+ width: 100%;
44
+ max-width: 100%;
45
+ min-width: 0;
40
46
  margin: 0;
41
47
  min-height: 100vh;
42
48
  overflow-x: hidden;
@@ -63,8 +69,17 @@ code {
63
69
  background: rgba(156, 181, 197, 0.12);
64
70
  }
65
71
 
72
+ #app {
73
+ width: 100%;
74
+ max-width: 100%;
75
+ min-width: 0;
76
+ }
77
+
66
78
  .app-shell {
79
+ width: 100%;
80
+ max-width: 100%;
67
81
  min-height: 100vh;
82
+ overflow-x: clip;
68
83
  padding:
69
84
  max(1rem, env(safe-area-inset-top))
70
85
  1rem
@@ -389,6 +404,8 @@ code {
389
404
  }
390
405
 
391
406
  .app-main {
407
+ width: 100%;
408
+ max-width: 100%;
392
409
  display: grid;
393
410
  gap: 1rem;
394
411
  min-width: 0;
@@ -403,6 +420,16 @@ code {
403
420
  .choice-stack {
404
421
  display: grid;
405
422
  gap: 1rem;
423
+ min-width: 0;
424
+ max-width: 100%;
425
+ }
426
+
427
+ .timeline-shell,
428
+ .timeline-list,
429
+ .timeline-thread-filter,
430
+ .timeline-thread-filter__row {
431
+ min-width: 0;
432
+ max-width: 100%;
406
433
  }
407
434
 
408
435
  .detail-shell,
@@ -892,12 +919,37 @@ code {
892
919
  gap: 0.18rem;
893
920
  }
894
921
 
922
+ .settings-row__title-line {
923
+ min-width: 0;
924
+ display: inline-flex;
925
+ align-items: center;
926
+ gap: 0.42rem;
927
+ }
928
+
895
929
  .settings-row__title,
896
930
  .settings-info-row__label {
931
+ min-width: 0;
897
932
  font-size: 1rem;
898
933
  line-height: 1.3;
899
934
  }
900
935
 
936
+ .settings-row__badge {
937
+ flex: 0 0 auto;
938
+ display: inline-flex;
939
+ align-items: center;
940
+ min-height: 1.18rem;
941
+ padding: 0.08rem 0.42rem 0.1rem;
942
+ border-radius: 999px;
943
+ border: 1px solid rgba(240, 180, 40, 0.26);
944
+ background: rgba(240, 180, 40, 0.11);
945
+ color: rgba(255, 220, 140, 0.96);
946
+ font-size: 0.66rem;
947
+ font-weight: 800;
948
+ letter-spacing: 0.04em;
949
+ line-height: 1;
950
+ text-transform: uppercase;
951
+ }
952
+
901
953
  .settings-row__subtitle {
902
954
  color: var(--muted);
903
955
  font-size: 0.84rem;
@@ -1237,6 +1289,18 @@ code {
1237
1289
  background: rgba(53, 26, 29, 0.96);
1238
1290
  }
1239
1291
 
1292
+ .wallet-beta-notice {
1293
+ border-color: rgba(240, 180, 40, 0.18);
1294
+ background: rgba(42, 34, 22, 0.78);
1295
+ }
1296
+
1297
+ .wallet-beta-notice p {
1298
+ margin: 0;
1299
+ color: rgba(255, 220, 140, 0.9);
1300
+ font-size: 0.84rem;
1301
+ line-height: 1.5;
1302
+ }
1303
+
1240
1304
  .settings-update-notice {
1241
1305
  margin-top: 0.85rem;
1242
1306
  border-color: rgba(121, 196, 255, 0.22);
@@ -1321,7 +1385,8 @@ code {
1321
1385
  border-color: rgba(112, 202, 157, 0.16);
1322
1386
  }
1323
1387
 
1324
- .wallet-step-card--locked {
1388
+ .wallet-step-card--locked,
1389
+ .wallet-step-card--comingSoon {
1325
1390
  opacity: 0.78;
1326
1391
  }
1327
1392
 
@@ -1408,7 +1473,8 @@ code {
1408
1473
  color: rgba(205, 220, 231, 0.6);
1409
1474
  }
1410
1475
 
1411
- .wallet-step-card__status--optional {
1476
+ .wallet-step-card__status--optional,
1477
+ .wallet-step-card__status--comingSoon {
1412
1478
  border-color: rgba(240, 180, 40, 0.22);
1413
1479
  background: rgba(240, 180, 40, 0.12);
1414
1480
  color: rgba(255, 220, 140, 0.96);
@@ -1735,12 +1801,17 @@ code {
1735
1801
  }
1736
1802
 
1737
1803
  .card-list {
1804
+ width: 100%;
1805
+ max-width: 100%;
1806
+ min-width: 0;
1738
1807
  display: grid;
1739
1808
  gap: 0.85rem;
1740
1809
  }
1741
1810
 
1742
1811
  .item-card {
1743
1812
  width: 100%;
1813
+ min-width: 0;
1814
+ overflow: hidden;
1744
1815
  display: grid;
1745
1816
  gap: 0.8rem;
1746
1817
  padding: 0.95rem;
@@ -1754,6 +1825,7 @@ code {
1754
1825
  }
1755
1826
 
1756
1827
  .item-card__header {
1828
+ min-width: 0;
1757
1829
  display: flex;
1758
1830
  justify-content: space-between;
1759
1831
  gap: 0.8rem;
@@ -1761,11 +1833,12 @@ code {
1761
1833
  }
1762
1834
 
1763
1835
  .item-card__header-right {
1836
+ flex: 0 0 auto;
1764
1837
  display: inline-flex;
1765
1838
  align-items: center;
1766
1839
  justify-content: flex-end;
1767
1840
  gap: 0.55rem;
1768
- min-width: fit-content;
1841
+ min-width: 0;
1769
1842
  }
1770
1843
 
1771
1844
  .item-card__header--completion-readonly {
@@ -1775,18 +1848,21 @@ code {
1775
1848
 
1776
1849
  .item-card__meta,
1777
1850
  .detail-pills {
1851
+ min-width: 0;
1778
1852
  display: flex;
1779
1853
  flex-wrap: wrap;
1780
1854
  gap: 0.55rem;
1781
1855
  }
1782
1856
 
1783
1857
  .item-card__content {
1858
+ min-width: 0;
1784
1859
  display: grid;
1785
1860
  gap: 0.38rem;
1786
1861
  }
1787
1862
 
1788
1863
  .item-card__intent,
1789
1864
  .detail-intent {
1865
+ min-width: 0;
1790
1866
  margin: 0;
1791
1867
  display: inline-flex;
1792
1868
  align-items: center;
@@ -1794,6 +1870,7 @@ code {
1794
1870
  color: rgba(236, 248, 255, 0.86);
1795
1871
  font-size: 0.84rem;
1796
1872
  line-height: 1.35;
1873
+ overflow-wrap: anywhere;
1797
1874
  }
1798
1875
 
1799
1876
  .item-card__intent {
@@ -1810,6 +1887,7 @@ code {
1810
1887
  }
1811
1888
 
1812
1889
  .item-card__title {
1890
+ min-width: 0;
1813
1891
  margin: 0;
1814
1892
  font-size: 1.08rem;
1815
1893
  line-height: 1.35;
@@ -1817,6 +1895,7 @@ code {
1817
1895
  }
1818
1896
 
1819
1897
  .item-card__summary {
1898
+ min-width: 0;
1820
1899
  margin: 0;
1821
1900
  color: var(--muted);
1822
1901
  line-height: 1.45;
@@ -1828,6 +1907,7 @@ code {
1828
1907
  }
1829
1908
 
1830
1909
  .item-card__thread {
1910
+ min-width: 0;
1831
1911
  margin: 0;
1832
1912
  color: var(--muted);
1833
1913
  font-size: 0.82rem;
@@ -2101,6 +2181,8 @@ code {
2101
2181
 
2102
2182
  .timeline-entry {
2103
2183
  width: 100%;
2184
+ min-width: 0;
2185
+ overflow: hidden;
2104
2186
  display: grid;
2105
2187
  gap: 0.55rem;
2106
2188
  padding: 0.92rem;
@@ -2288,6 +2370,7 @@ code {
2288
2370
  }
2289
2371
 
2290
2372
  .timeline-entry__meta {
2373
+ min-width: 0;
2291
2374
  display: grid;
2292
2375
  grid-template-columns: minmax(0, 1fr) auto;
2293
2376
  gap: 0.75rem;
@@ -2305,6 +2388,7 @@ code {
2305
2388
  min-width: 0;
2306
2389
  display: inline-flex;
2307
2390
  align-items: center;
2391
+ flex-wrap: wrap;
2308
2392
  gap: 0.42rem;
2309
2393
  color: #edf8ff;
2310
2394
  font-size: 0.8rem;
@@ -2338,6 +2422,7 @@ code {
2338
2422
  }
2339
2423
 
2340
2424
  .timeline-entry__body {
2425
+ min-width: 0;
2341
2426
  display: grid;
2342
2427
  gap: 0.34rem;
2343
2428
  }
@@ -2408,6 +2493,7 @@ code {
2408
2493
 
2409
2494
  .timeline-entry__title,
2410
2495
  .timeline-entry__summary {
2496
+ min-width: 0;
2411
2497
  margin: 0;
2412
2498
  overflow-wrap: anywhere;
2413
2499
  }
@@ -4721,7 +4807,9 @@ button[aria-busy="true"]:not(.is-loading) {
4721
4807
  }
4722
4808
 
4723
4809
  .provider-filter {
4724
- display: inline-flex;
4810
+ width: 100%;
4811
+ min-width: 0;
4812
+ display: flex;
4725
4813
  align-items: center;
4726
4814
  gap: 0.25rem;
4727
4815
  padding: 0.2rem;
@@ -4732,8 +4820,13 @@ button[aria-busy="true"]:not(.is-loading) {
4732
4820
  max-width: 100%;
4733
4821
  overflow-x: auto;
4734
4822
  -webkit-overflow-scrolling: touch;
4823
+ scrollbar-width: none;
4824
+ }
4825
+ .provider-filter::-webkit-scrollbar {
4826
+ display: none;
4735
4827
  }
4736
4828
  .provider-filter__button {
4829
+ min-width: 0;
4737
4830
  appearance: none;
4738
4831
  background: transparent;
4739
4832
  color: var(--text-muted, #9aa6b2);
package/web/app.js CHANGED
@@ -132,6 +132,23 @@ function hazbasePasskeyHostSupport() {
132
132
  }
133
133
 
134
134
  const app = document.querySelector("#app");
135
+ let bootSplashDismissed = false;
136
+
137
+ function dismissBootSplash() {
138
+ if (bootSplashDismissed || typeof document === "undefined") {
139
+ return;
140
+ }
141
+ bootSplashDismissed = true;
142
+ const splash = document.querySelector("#boot-splash");
143
+ document.body?.classList.add("viveworker-ready");
144
+ if (!splash) {
145
+ return;
146
+ }
147
+ splash.setAttribute("aria-hidden", "true");
148
+ window.setTimeout(() => {
149
+ splash.remove();
150
+ }, 280);
151
+ }
135
152
  const params = new URLSearchParams(window.location.search);
136
153
  const initialItem = params.get("item") || "";
137
154
  const initialTargetTab = params.get("tab") || "";
@@ -146,6 +163,7 @@ boot().catch((error) => {
146
163
  const hint = /Load failed|Failed to fetch|NetworkError|fetch/i.test(message)
147
164
  ? `<p class="muted">${escapeHtml(L("error.networkHint"))}</p>`
148
165
  : "";
166
+ dismissBootSplash();
149
167
  app.innerHTML = `
150
168
  <main class="onboarding-shell">
151
169
  <section class="onboarding-card">
@@ -1171,6 +1189,7 @@ function renderPair() {
1171
1189
  });
1172
1190
 
1173
1191
  bindSharedUi(renderPair);
1192
+ requestAnimationFrame(dismissBootSplash);
1174
1193
  }
1175
1194
 
1176
1195
  async function pair(payload) {
@@ -1279,6 +1298,7 @@ async function renderShell() {
1279
1298
  applyPendingListScrollRestore();
1280
1299
  applyPendingSettingsSubpageScrollReset();
1281
1300
  applyPendingSettingsScrollRestore();
1301
+ requestAnimationFrame(dismissBootSplash);
1282
1302
  }
1283
1303
 
1284
1304
  function applyPendingDetailScrollReset() {
@@ -1899,7 +1919,11 @@ function buildActionOutcomeDetail({ kind, title, message }) {
1899
1919
 
1900
1920
  function approvalOutcomeMessage(actionUrl, provider) {
1901
1921
  const vars = { provider: providerDisplayName(provider) };
1902
- return /\/accept$/u.test(String(actionUrl || ""))
1922
+ const normalizedUrl = String(actionUrl || "");
1923
+ if (/\/api\/payments\/x402\/hazbase-wallet\/[^/]+\/pay$/u.test(normalizedUrl)) {
1924
+ return L("server.message.paymentSubmitted");
1925
+ }
1926
+ return /\/accept$/u.test(normalizedUrl)
1903
1927
  ? L("server.message.approvalAccepted", vars)
1904
1928
  : L("server.message.approvalRejected", vars);
1905
1929
  }
@@ -2617,6 +2641,8 @@ function timelineEntryStatusLabel(item, isMessageLike) {
2617
2641
  return L("timeline.status.approved");
2618
2642
  case "rejected":
2619
2643
  return L("timeline.status.rejected");
2644
+ case "failed":
2645
+ return L("timeline.status.failed");
2620
2646
  case "implemented":
2621
2647
  return L("timeline.status.implemented");
2622
2648
  case "dismissed":
@@ -3518,6 +3544,7 @@ function renderSettingsRoot(context, { mobile }) {
3518
3544
  page: "wallet",
3519
3545
  icon: "coin",
3520
3546
  title: L("settings.wallet.title"),
3547
+ badge: "beta",
3521
3548
  subtitle: L("settings.wallet.subtitle"),
3522
3549
  value: context.hazbase?.sessionInvalid
3523
3550
  ? L("settings.hazbase.status.sessionExpired")
@@ -3780,12 +3807,15 @@ function renderSettingsGroup(title, rows, options = {}) {
3780
3807
  `;
3781
3808
  }
3782
3809
 
3783
- function renderSettingsNavRow({ page, icon, title, subtitle, value }) {
3810
+ function renderSettingsNavRow({ page, icon, title, badge, subtitle, value }) {
3784
3811
  return `
3785
3812
  <button class="settings-nav-row" type="button" data-settings-subpage="${escapeHtml(page)}">
3786
3813
  <span class="settings-row__icon" aria-hidden="true">${renderIcon(icon)}</span>
3787
3814
  <span class="settings-row__body">
3788
- <span class="settings-row__title">${escapeHtml(title)}</span>
3815
+ <span class="settings-row__title-line">
3816
+ <span class="settings-row__title">${escapeHtml(title)}</span>
3817
+ ${badge ? `<span class="settings-row__badge">${escapeHtml(badge)}</span>` : ""}
3818
+ </span>
3789
3819
  ${subtitle ? `<span class="settings-row__subtitle">${escapeHtml(subtitle)}</span>` : ""}
3790
3820
  </span>
3791
3821
  <span class="settings-row__value">${escapeHtml(value || "")}</span>
@@ -4565,6 +4595,7 @@ function renderSettingsWalletPage(context) {
4565
4595
  // longer duplicated.
4566
4596
  const guideRows = [
4567
4597
  renderHazbaseWalletBanner(flow),
4598
+ renderHazbaseWalletBetaNotice(),
4568
4599
  state.hazbaseNotice
4569
4600
  ? `<div class="settings-copy-block settings-copy-block--compact wallet-flow-message wallet-flow-message--notice"><p>${escapeHtml(state.hazbaseNotice)}</p></div>`
4570
4601
  : "",
@@ -4616,15 +4647,10 @@ function renderSettingsWalletPage(context) {
4616
4647
  function renderHazbaseWalletStepList(flow) {
4617
4648
  const rendered = [];
4618
4649
  for (const step of flow.steps) {
4619
- // Step 4 (Base mainnet) is hidden while hazbase's closed beta keeps
4620
- // chainId 8453 out of `WALLET_GATEWAY_CHAINS_JSON` calling bootstrap
4621
- // against mainnet currently returns `unsupported_chain`, so there's no
4622
- // path for the user to complete the step. Step 3 (Base Sepolia) is the
4623
- // true completion boundary; the ready banner already keys off
4624
- // `coreReady = signedIn && hasPasskey && hasBaseSepolia`. Drop this
4625
- // guard (and restore the opt-in reveal) once mainnet is enabled
4626
- // server-side.
4627
- if (step.number === 4) {
4650
+ // Keep the Base mainnet roadmap visible only after the usable Base Sepolia
4651
+ // wallet is ready. Showing Step 4 during email/passkey setup makes the
4652
+ // sequential flow feel like there is another action competing for focus.
4653
+ if (step.number === 4 && step.status === "comingSoon" && !flow.coreReady) {
4628
4654
  continue;
4629
4655
  }
4630
4656
 
@@ -4764,16 +4790,10 @@ function deriveHazbaseWalletFlow(hazbase) {
4764
4790
  icon: "coin",
4765
4791
  title: L("settings.wallet.step.base.title"),
4766
4792
  copy: L("settings.wallet.step.base.copy"),
4767
- detail: baseMainnet?.smartAccountAddress || L("settings.hazbase.wallet.missing"),
4793
+ detail: baseMainnet?.smartAccountAddress || L("settings.wallet.step.base.comingSoonDetail"),
4768
4794
  monoDetail: Boolean(baseMainnet?.smartAccountAddress),
4769
- status: hasBaseMainnet ? "complete" : coreReady ? "optional" : "locked",
4770
- actions: hasBaseMainnet
4771
- ? []
4772
- : [
4773
- actionButton("settings.hazbase.action.bootstrapBase", "bootstrap-base", {
4774
- disabled: !coreReady,
4775
- }),
4776
- ],
4795
+ status: hasBaseMainnet ? "complete" : "comingSoon",
4796
+ actions: [],
4777
4797
  },
4778
4798
  ],
4779
4799
  };
@@ -4825,14 +4845,29 @@ function renderHazbaseWalletBanner(flow) {
4825
4845
  `;
4826
4846
  }
4827
4847
 
4848
+ function renderHazbaseWalletBetaNotice() {
4849
+ return `
4850
+ <div class="settings-copy-block settings-copy-block--compact wallet-beta-notice">
4851
+ <p>${escapeHtml(L("settings.wallet.betaNotice"))}</p>
4852
+ </div>
4853
+ `;
4854
+ }
4855
+
4828
4856
  function renderHazbaseWalletStepCard(step, { mode = "full" } = {}) {
4829
4857
  const statusMeta = {
4830
4858
  complete: { label: L("settings.wallet.status.complete"), icon: "completed" },
4831
4859
  current: { label: L("settings.wallet.status.current"), icon: "pending" },
4832
4860
  locked: { label: L("settings.wallet.status.locked"), icon: "lock" },
4833
4861
  optional: { label: L("settings.wallet.status.optional"), icon: "coin" },
4862
+ comingSoon: { label: L("settings.wallet.status.comingSoon"), icon: "coin" },
4834
4863
  pending: { label: L("settings.wallet.status.pending"), icon: "pending" },
4835
4864
  }[step.status] || { label: L("settings.wallet.status.pending"), icon: "pending" };
4865
+ const statusChipHtml = step.status === "current"
4866
+ ? ""
4867
+ : `<span class="wallet-step-card__status wallet-step-card__status--${escapeHtml(step.status)}">
4868
+ <span class="wallet-step-card__status-icon" aria-hidden="true">${renderIcon(statusMeta.icon)}</span>
4869
+ <span>${escapeHtml(statusMeta.label)}</span>
4870
+ </span>`;
4836
4871
 
4837
4872
  if (mode === "compact") {
4838
4873
  // Compact row for finished steps. Keeps the check icon + title + one-line
@@ -4863,10 +4898,7 @@ function renderHazbaseWalletStepCard(step, { mode = "full" } = {}) {
4863
4898
  <h3 class="wallet-step-card__title">${escapeHtml(step.title)}</h3>
4864
4899
  </div>
4865
4900
  </div>
4866
- <span class="wallet-step-card__status wallet-step-card__status--${escapeHtml(step.status)}">
4867
- <span class="wallet-step-card__status-icon" aria-hidden="true">${renderIcon(statusMeta.icon)}</span>
4868
- <span>${escapeHtml(statusMeta.label)}</span>
4869
- </span>
4901
+ ${statusChipHtml}
4870
4902
  </div>
4871
4903
  <p class="wallet-step-card__copy">${escapeHtml(step.copy)}</p>
4872
4904
  <p class="wallet-step-card__detail ${step.monoDetail ? "wallet-step-card__detail--mono" : ""}">${escapeHtml(step.detail)}</p>
@@ -6496,20 +6528,7 @@ function renderLogoutConfirmModal() {
6496
6528
  <strong id="logout-confirm-title">${escapeHtml(L("logout.confirm.title"))}</strong>
6497
6529
  <p class="muted">${escapeHtml(L("logout.confirm.copy"))}</p>
6498
6530
  </div>
6499
- <div class="logout-option">
6500
- <div class="logout-option__copy">
6501
- <strong>${escapeHtml(L("logout.confirm.keepTrustedTitle"))}</strong>
6502
- <p class="muted">${escapeHtml(L("logout.confirm.keepTrustedCopy"))}</p>
6503
- </div>
6504
- <button class="primary primary--wide" type="button" data-logout-mode="session">${escapeHtml(L("logout.action.keepTrusted"))}</button>
6505
- </div>
6506
- <div class="logout-option logout-option--danger">
6507
- <div class="logout-option__copy">
6508
- <strong>${escapeHtml(L("logout.confirm.removeTitle"))}</strong>
6509
- <p class="muted">${escapeHtml(L("logout.confirm.removeCopy"))}</p>
6510
- </div>
6511
- <button class="secondary secondary--wide" type="button" data-logout-mode="revoke">${escapeHtml(L("logout.action.removeDevice"))}</button>
6512
- </div>
6531
+ <button class="secondary secondary--wide" type="button" data-logout-mode="session">${escapeHtml(L("logout.action.keepTrusted"))}</button>
6513
6532
  <button class="ghost ghost--wide" type="button" data-close-logout-confirm>${escapeHtml(L("common.cancel"))}</button>
6514
6533
  </section>
6515
6534
  </div>
@@ -6870,18 +6889,33 @@ function bindShellInteractions() {
6870
6889
  state.pendingActionUrls.delete(actionUrl);
6871
6890
  } catch (error) {
6872
6891
  state.pendingActionUrls.delete(actionUrl);
6892
+ const approvalFinalized = Boolean(error?.payload?.approvalFinalized);
6873
6893
  if (error?.errorKey === "hazbase-session-expired") {
6874
6894
  await fetchHazbaseStatus();
6875
6895
  }
6876
- // Restore buttons on failure so the user can retry
6877
- for (const sibling of siblingButtons) {
6878
- if (originalLabels.has(sibling)) {
6879
- sibling.innerHTML = originalLabels.get(sibling);
6896
+ if (approvalFinalized) {
6897
+ await refreshAuthenticatedState();
6898
+ if (keepDetailOpen && activeItem?.kind === "approval") {
6899
+ pinActionOutcomeDetail(
6900
+ activeItem,
6901
+ buildActionOutcomeDetail({
6902
+ kind: "approval",
6903
+ title: state.currentDetail?.title,
6904
+ message: L("server.message.paymentFailed", { reason: error.message || String(error) }),
6905
+ })
6906
+ );
6880
6907
  }
6881
- sibling.disabled = false;
6882
- sibling.removeAttribute("aria-busy");
6908
+ } else {
6909
+ // Restore buttons on recoverable failure so the user can retry the same action.
6910
+ for (const sibling of siblingButtons) {
6911
+ if (originalLabels.has(sibling)) {
6912
+ sibling.innerHTML = originalLabels.get(sibling);
6913
+ }
6914
+ sibling.disabled = false;
6915
+ sibling.removeAttribute("aria-busy");
6916
+ }
6917
+ button.classList.remove("is-loading");
6883
6918
  }
6884
- button.classList.remove("is-loading");
6885
6919
  state.pushError = error.message || String(error);
6886
6920
  await renderShell();
6887
6921
  }
package/web/i18n.js CHANGED
@@ -250,6 +250,7 @@ const translations = {
250
250
  "timeline.filterLabel": "Thread filter",
251
251
  "timeline.status.approved": "Approved",
252
252
  "timeline.status.rejected": "Rejected",
253
+ "timeline.status.failed": "Failed",
253
254
  "timeline.status.implemented": "Started",
254
255
  "timeline.status.dismissed": "Dismissed",
255
256
  "timeline.status.submitted": "Sent",
@@ -520,6 +521,7 @@ const translations = {
520
521
  "settings.wallet.flow.title": "Recommended order",
521
522
  "settings.wallet.flow.banner.title": "Let's set up your wallet",
522
523
  "settings.wallet.flow.copy": "You need to activate your wallet to exchange task rewards over A2A.",
524
+ "settings.wallet.betaNotice": "Wallet is in beta. During this test period, wallet payments and payouts are available on testnets only.",
523
525
  "settings.wallet.sessionExpired.title": "Refresh your wallet session",
524
526
  "settings.wallet.sessionExpired.copy": "Your wallet session expired. Sign in again with a one-time password; your passkey and Base Sepolia address stay linked.",
525
527
  "settings.wallet.ready.title": "Ready to use",
@@ -533,6 +535,7 @@ const translations = {
533
535
  "settings.wallet.status.pending": "Pending",
534
536
  "settings.wallet.status.locked": "Waiting",
535
537
  "settings.wallet.status.optional": "Optional",
538
+ "settings.wallet.status.comingSoon": "Coming soon",
536
539
  "settings.wallet.step.signIn.title": "Sign in with email",
537
540
  "settings.wallet.step.signIn.copy": "Send a one-time password to your email, then enter it here to sign in to your wallet.",
538
541
  "settings.wallet.step.refreshSession.title": "Refresh session",
@@ -542,7 +545,8 @@ const translations = {
542
545
  "settings.wallet.step.baseSepolia.title": "Issue a Base Sepolia wallet",
543
546
  "settings.wallet.step.baseSepolia.copy": "Create a testing wallet for the current beta flow. This is the main wallet we recommend issuing first.",
544
547
  "settings.wallet.step.base.title": "Issue a Base wallet",
545
- "settings.wallet.step.base.copy": "Create a Base mainnet wallet when you are ready for production payouts. This step is optional for now.",
548
+ "settings.wallet.step.base.copy": "Base mainnet wallet issuance is planned for production payouts. It is not active yet.",
549
+ "settings.wallet.step.base.comingSoonDetail": "Coming soon",
546
550
  "settings.wallet.mainnet.optIn": "Also set up Base mainnet",
547
551
  "settings.wallet.mainnet.optInHint": "Optional — required only for production payouts.",
548
552
  "settings.hazbase.title": "Wallet",
@@ -707,6 +711,8 @@ const translations = {
707
711
  "claudeAway.notifyOnly.notice": "Sync mode is OFF — please respond on the desktop. {provider} is waiting for input there.",
708
712
  "server.message.approvalAccepted": "Approved. {provider} will continue.",
709
713
  "server.message.approvalRejected": "Rejected. {provider} was told not to continue this action.",
714
+ "server.message.paymentSubmitted": "Payment submitted. Waiting for settlement to complete.",
715
+ "server.message.paymentFailed": "Payment failed: {reason}",
710
716
  "server.message.planImplemented": "{provider} was asked to start implementing from this plan.",
711
717
  "server.message.planDismissed": "This plan prompt was closed and future reminders were suppressed.",
712
718
  "server.confirm.planImplement": "Implementation will start from this plan.",
@@ -828,8 +834,8 @@ const translations = {
828
834
  "Open the HTTP URL in a mobile browser only if you intentionally allowed insecure LAN access. This mode is not recommended on shared Wi-Fi.",
829
835
  "cli.setup.instructions.localOnlyHttp":
830
836
  "This setup is loopback-only HTTP for local development. Use the default HTTPS setup or --allow-insecure-http-lan if you explicitly need LAN access.",
831
- "logout.confirm.title": "Choose how to log out",
832
- "logout.confirm.copy": "You can end just this session, or remove trust for this device so pairing is required next time.",
837
+ "logout.confirm.title": "Log out of viveworker?",
838
+ "logout.confirm.copy": "This ends the current session. This device stays trusted, so you can come back without pairing again.",
833
839
  "logout.confirm.keepTrustedTitle": "Log out only",
834
840
  "logout.confirm.keepTrustedCopy": "End this session now, but keep this device trusted for the next time you open viveworker.",
835
841
  "logout.confirm.removeTitle": "Log out and remove this device",
@@ -1167,6 +1173,7 @@ const translations = {
1167
1173
  "timeline.filterLabel": "スレッドフィルター",
1168
1174
  "timeline.status.approved": "承認済み",
1169
1175
  "timeline.status.rejected": "拒否済み",
1176
+ "timeline.status.failed": "失敗",
1170
1177
  "timeline.status.implemented": "実装開始",
1171
1178
  "timeline.status.dismissed": "見送り",
1172
1179
  "timeline.status.submitted": "送信済み",
@@ -1293,14 +1300,14 @@ const translations = {
1293
1300
  "settings.install.title": "ホーム画面に追加",
1294
1301
  "settings.install.copy":
1295
1302
  "いちばん使いやすい体験にするには、viveworker をホーム画面に追加して、インストール版を開いてください。",
1296
- "settings.device.title": "Devices",
1303
+ "settings.device.title": "信頼済み端末",
1297
1304
  "settings.device.copy": "この LAN 上で viveworker を開ける信頼済み端末を管理します。",
1298
1305
  "settings.device.count": ({ count }) => `${count} 台の端末`,
1299
1306
  "settings.device.section.current": "この端末",
1300
1307
  "settings.device.section.other": "他の信頼済み端末",
1301
1308
  "settings.device.thisDevice": "この端末",
1302
1309
  "settings.device.emptyCurrent": "この端末は現在の有効な信頼済み端末一覧には入っていません。以前の信頼期限が切れたか、削除された可能性があります。必要なら Mac で `npx viveworker pair` を実行して再ペアリングしてください。",
1303
- "settings.device.emptyOther": "他に有効な信頼済み端末はありません。期限切れまたは削除済みの端末はここには表示されません。必要なら Mac で `npx viveworker pair` を実行して再ペアリングしてください。",
1310
+ "settings.device.emptyOther": "他に有効な信頼済み端末はありません。",
1304
1311
  "settings.device.addAnother.title": "端末を追加する",
1305
1312
  "settings.device.addAnother.heading": "新しい端末 / ブラウザを追加",
1306
1313
  "settings.device.addAnother.copy":
@@ -1437,6 +1444,7 @@ const translations = {
1437
1444
  "settings.wallet.flow.title": "おすすめの順番",
1438
1445
  "settings.wallet.flow.banner.title": "まずは、セットアップしましょう",
1439
1446
  "settings.wallet.flow.copy": "A2A でタスクの報酬をやりとりするには、ウォレットを有効化する必要があります。",
1447
+ "settings.wallet.betaNotice": "ウォレット機能はベータテスト中です。この期間はテストネットでのみ支払い・受け取りを利用できます。",
1440
1448
  "settings.wallet.sessionExpired.title": "ウォレットセッションを更新しましょう",
1441
1449
  "settings.wallet.sessionExpired.copy": "ウォレットのセッションが切れています。ワンタイムパスワードで再サインインすれば、Passkey と Base Sepolia アドレスはそのまま使えます。",
1442
1450
  "settings.wallet.ready.title": "利用準備ができました",
@@ -1450,6 +1458,7 @@ const translations = {
1450
1458
  "settings.wallet.status.pending": "保留",
1451
1459
  "settings.wallet.status.locked": "前の step 待ち",
1452
1460
  "settings.wallet.status.optional": "任意",
1461
+ "settings.wallet.status.comingSoon": "Coming soon",
1453
1462
  "settings.wallet.step.signIn.title": "メールでサインイン",
1454
1463
  "settings.wallet.step.signIn.copy": "メールにワンタイムパスワードを送り、届いた値を入力してウォレットにサインインします。",
1455
1464
  "settings.wallet.step.refreshSession.title": "セッションを更新",
@@ -1459,7 +1468,8 @@ const translations = {
1459
1468
  "settings.wallet.step.baseSepolia.title": "Base Sepolia ウォレットを発行",
1460
1469
  "settings.wallet.step.baseSepolia.copy": "現在の beta フローで使うテスト用ウォレットを発行します。まずはこれを用意するのがおすすめです。",
1461
1470
  "settings.wallet.step.base.title": "Base ウォレットを発行",
1462
- "settings.wallet.step.base.copy": "本番の payout が必要になったら Base mainnet ウォレットを発行します。現時点では任意です。",
1471
+ "settings.wallet.step.base.copy": "本番 payout 向けの Base mainnet ウォレット発行は準備中です。まだ有効化されていません。",
1472
+ "settings.wallet.step.base.comingSoonDetail": "Coming soon",
1463
1473
  "settings.wallet.mainnet.optIn": "Base mainnet も設定する",
1464
1474
  "settings.wallet.mainnet.optInHint": "任意 — 本番 payout が必要な場合だけ。",
1465
1475
  "settings.hazbase.title": "ウォレット",
@@ -1624,6 +1634,8 @@ const translations = {
1624
1634
  "claudeAway.notifyOnly.notice": "同期モードは OFF です。PC で操作してください。{provider} は PC からの入力を待っています。",
1625
1635
  "server.message.approvalAccepted": "承認しました。{provider} を続行します。",
1626
1636
  "server.message.approvalRejected": "拒否しました。{provider} にこの操作を続けないよう伝えました。",
1637
+ "server.message.paymentSubmitted": "支払いを送信しました。決済完了を待っています。",
1638
+ "server.message.paymentFailed": "支払いに失敗しました: {reason}",
1627
1639
  "server.message.planImplemented": "この plan から実装を開始するよう {provider} に伝えました。",
1628
1640
  "server.message.planDismissed": "この Plan 確認を閉じ、再通知を抑止しました。",
1629
1641
  "server.confirm.planImplement": "この plan から実装を開始します。",
@@ -1745,8 +1757,8 @@ const translations = {
1745
1757
  "HTTP URL は、LAN 上の平文アクセスを明示的に許可した場合だけブラウザで開いてください。共有 Wi-Fi では非推奨です。",
1746
1758
  "cli.setup.instructions.localOnlyHttp":
1747
1759
  "この setup はローカル開発用の loopback-only HTTP です。別の端末から使うなら既定の HTTPS setup か --allow-insecure-http-lan を使ってください。",
1748
- "logout.confirm.title": "ログアウト方法を選んでください",
1749
- "logout.confirm.copy": "session だけ終えることも、この端末の信頼ごと削除して次回 pairing 必須にすることもできます。",
1760
+ "logout.confirm.title": "viveworker からログアウトしますか?",
1761
+ "logout.confirm.copy": "現在の session を終了します。この端末の信頼は維持されるので、次回は再 pairing なしで戻れます。",
1750
1762
  "logout.confirm.keepTrustedTitle": "ログアウトのみ",
1751
1763
  "logout.confirm.keepTrustedCopy": "今の session だけ終了し、この端末の信頼は残します。",
1752
1764
  "logout.confirm.removeTitle": "ログアウトしてこの端末を削除",