refactor(i18n): route remaining Obsidian UI text through the message catalogue

Continues the source-key migration inside the application boundary introduced
in 1.0.0, where LiveSync owns its catalogue and consumes Commonlib as a
published package.

- Replace hardcoded user-visible strings in the Obsidian UI (Setup Wizard
  dialogues, P2P panes, Customisation Sync panes, Global History, the JSON
  conflict pane and the remote-configuration menu) with `$msg` calls, keeping
  the English source string as the key.
- Wire up strings whose translations already existed in the catalogue but were
  still rendered as literals, for example the whole Intro dialogue.
- Add the new entries to `src/common/messagesYAML/en.yaml` and `es.yaml`, then
  regenerate `messagesJson/` and `combinedMessages.prod.ts` through the
  documented `i18n:bake` pipeline.
- No Commonlib gitlink or catalogue is involved; every change is
  LiveSync-owned.

Regenerating the catalogue also normalises three pre-existing entries each in
`ko.json` and `zh.json`, where the committed JSON kept a trailing space before
a newline that YAML cannot represent.

Verified with tsc-check, tsc-check:apps, svelte-check and lint.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Zeedif
2026-07-30 18:59:19 -06:00
co-authored by Claude Opus 5
parent c385bd7ce7
commit b6746be73e
37 changed files with 3624 additions and 499 deletions
@@ -12,6 +12,7 @@
import type { LiveSyncTrysteroReplicator } from "@vrtmrz/livesync-commonlib/compat/replication/trystero/LiveSyncTrysteroReplicator";
import { delay, fireAndForget } from "octagonal-wheels/promises";
import P2PServerStatusCard from "./P2PServerStatusCard.svelte";
import { $msg as translateMessage } from "@/common/translation";
interface Props {
liveSyncReplicator: LiveSyncTrysteroReplicator;
@@ -84,11 +85,11 @@
}
function getAcceptanceStatus(peer: P2PServerInfo["knownAdvertisements"][number]) {
if (peer.isTemporaryAccepted === true) return "ACCEPTED (in session)";
if (peer.isAccepted === true) return "ACCEPTED";
if (peer.isTemporaryAccepted === false) return "DENIED (in session)";
if (peer.isAccepted === false) return "DENIED";
return "NEW";
if (peer.isTemporaryAccepted === true) return translateMessage("ACCEPTED (in session)");
if (peer.isAccepted === true) return translateMessage("ACCEPTED");
if (peer.isTemporaryAccepted === false) return translateMessage("DENIED (in session)");
if (peer.isAccepted === false) return translateMessage("DENIED");
return translateMessage("NEW");
}
function getAcceptanceStatusClass(peer: P2PServerInfo["knownAdvertisements"][number]) {
@@ -102,7 +103,7 @@
<P2PServerStatusCard {getLiveSyncReplicator} showBroadcastToggle={false} />
<div class="peers-section">
<h3>Available Peers</h3>
<h3>{translateMessage("Available Peers")}</h3>
{#if serverInfo && serverInfo.knownAdvertisements.length > 0}
<div class="peers-list">
{#each serverInfo.knownAdvertisements as peer (peer.peerId)}
@@ -126,14 +127,18 @@
disabled={syncingPeerId !== null}
onclick={() => handleSync(peer.peerId)}
>
{syncingPeerId === peer.peerId ? "Syncing..." : "Sync"}
{syncingPeerId === peer.peerId
? translateMessage("Syncing...")
: translateMessage("Sync")}
</button>
<button
class="btn {rebuildMode ? 'btn-primary' : 'btn-secondary'}"
disabled={syncingPeerId !== null}
onclick={() => handleSyncThenClose(peer.peerId)}
>
{syncingPeerId === peer.peerId ? "Syncing..." : "Start Sync & Close"}
{syncingPeerId === peer.peerId
? translateMessage("Syncing...")
: translateMessage("Start Sync & Close")}
</button>
{:else}
<button
@@ -141,7 +146,9 @@
disabled={syncingPeerId !== null}
onclick={() => handleSyncThenClose(peer.peerId)}
>
{syncingPeerId === peer.peerId ? "Syncing..." : "Sync"}
{syncingPeerId === peer.peerId
? translateMessage("Syncing...")
: translateMessage("Sync")}
</button>
{/if}
</div>
@@ -149,16 +156,22 @@
{/each}
</div>
{:else if serverInfo}
<p class="no-peers">No devices available. Waiting for other devices to connect...</p>
<p class="no-peers">
{translateMessage("No devices available. Waiting for other devices to connect...")}
</p>
{/if}
</div>
<div class="footer">
{#if rebuildMode}
<button class="btn btn-cancel" onclick={onClose} disabled={syncingPeerId !== null}>Skip and close</button>
<button class="btn btn-cancel" onclick={onClose} disabled={syncingPeerId !== null}
>{translateMessage("Skip and close")}</button
>
{:else}
<button class="btn btn-cancel" onclick={onClose}>Close</button>
<button class="btn btn-cancel" onclick={onCloseAndDisconnect}>Close & Disconnect</button>
<button class="btn btn-cancel" onclick={onClose}>{translateMessage("Close")}</button>
<button class="btn btn-cancel" onclick={onCloseAndDisconnect}
>{translateMessage("Close & Disconnect")}</button
>
{/if}
</div>
</div>
@@ -263,7 +263,7 @@
</script>
<article>
<h1>Peer to Peer Replicator</h1>
<h1>{_msg("Peer to Peer Replicator")}</h1>
<details bind:open={isNoticeOpened}>
<summary>{_msg("P2P.Note.Summary")}</summary>
<p class="important">{_msg("P2P.Note.important_note")}</p>
@@ -274,23 +274,23 @@
<p>{paragraph}</p>
{/each}
</details>
<h2>Connection Settings</h2>
<h2>{_msg("Connection Settings")}</h2>
{#if isObsidian}
You can configure in the Obsidian Plugin Settings.
{_msg("You can configure in the Obsidian Plugin Settings.")}
{:else}
<details bind:open={isSettingOpened}>
<summary>{eRelay}</summary>
<table class="settings">
<tbody>
<tr>
<th> Enable P2P Replicator </th>
<th>{_msg("Enable P2P Replicator")}</th>
<td>
<label class={{ "is-dirty": isP2PEnabledModified }}>
<input type="checkbox" bind:checked={eP2PEnabled} />
</label>
</td>
</tr><tr>
<th> Relay settings </th>
<th>{_msg("Relay settings")}</th>
<td>
<label class={{ "is-dirty": isRelayModified }}>
<input
@@ -299,12 +299,12 @@
bind:value={eRelay}
autocomplete="off"
/>
<button onclick={() => useDefaultRelay()}> Use vrtmrz's relay </button>
<button onclick={() => useDefaultRelay()}>{_msg("Use vrtmrz's relay")}</button>
</label>
</td>
</tr>
<tr>
<th> Room ID </th>
<th>{_msg("Room ID")}</th>
<td>
<label class={{ "is-dirty": isRoomIdModified }}>
<input
@@ -315,31 +315,32 @@
spellcheck="false"
autocorrect="off"
/>
<button onclick={() => chooseRandom()}> Use Random Number </button>
<button onclick={() => chooseRandom()}>{_msg("Use Random Number")}</button>
</label>
<span>
<small>
This can isolate your connections between devices. Use the same Room ID for the same
devices.</small
<small
>{_msg(
"This can isolate your connections between devices. Use the same Room ID for the same devices."
)}</small
>
</span>
</td>
</tr>
<tr>
<th> Password </th>
<th>{_msg("Password")}</th>
<td>
<label class={{ "is-dirty": isPasswordModified }}>
<input type="password" placeholder="password" bind:value={ePassword} />
</label>
<span>
<small>
This password is used to encrypt the connection. Use something long enough.
{_msg("This password is used to encrypt the connection. Use something long enough.")}
</small>
</span>
</td>
</tr>
<tr>
<th> This device name </th>
<th>{_msg("This device name")}</th>
<td>
<label class={{ "is-dirty": isDeviceNameModified }}>
<input
@@ -351,14 +352,15 @@
</label>
<span>
<small>
Device name to identify the device. Please use shorter one for the stable peer
detection, i.e., "iphone-16" or "macbook-2021".
{_msg(
'Device name to identify the device. Please use shorter one for the stable peer detection, i.e., "iphone-16" or "macbook-2021".'
)}
</small>
</span>
</td>
</tr>
<tr>
<th> Auto Connect </th>
<th>{_msg("Auto Connect")}</th>
<td>
<label class={{ "is-dirty": isAutoStartModified }}>
<input type="checkbox" bind:checked={eAutoStart} />
@@ -366,7 +368,7 @@
</td>
</tr>
<tr>
<th> Start change-broadcasting on Connect </th>
<th>{_msg("Start change-broadcasting on Connect")}</th>
<td>
<label class={{ "is-dirty": isAutoBroadcastModified }}>
<input type="checkbox" bind:checked={eAutoBroadcast} />
@@ -383,39 +385,43 @@
</tr> -->
</tbody>
</table>
<button disabled={!isAnyModified} class="button mod-cta" onclick={saveAndApply}>Save and Apply</button>
<button disabled={!isAnyModified} class="button" onclick={revert}>Revert changes</button>
<button disabled={!isAnyModified} class="button mod-cta" onclick={saveAndApply}
>{_msg("Save and Apply")}</button
>
<button disabled={!isAnyModified} class="button" onclick={revert}>{_msg("Revert changes")}</button>
</details>
{/if}
<div>
<h2>Signaling Server Connection</h2>
<h2>{_msg("Signaling Server Connection")}</h2>
<div>
{#if !isConnected}
<p>No Connection</p>
<p>{_msg("No Connection")}</p>
{:else}
<p>Connected to Signaling Server (as Peer ID: {serverPeerId})</p>
<p>{_msg("Connected to Signaling Server (as Peer ID: ${peerId})", { peerId: serverPeerId })}</p>
{/if}
</div>
<div>
{#if !isConnected}
<button onclick={openServer}>Connect</button>
<button onclick={openServer}>{_msg("Connect")}</button>
{:else}
<button onclick={closeServer}>Disconnect</button>
<button onclick={closeServer}>{_msg("Disconnect")}</button>
{#if replicatorInfo?.isBroadcasting !== undefined}
{#if replicatorInfo?.isBroadcasting}
<button onclick={stopBroadcasting}>Stop Broadcasting</button>
<button onclick={stopBroadcasting}>{_msg("Stop Broadcasting")}</button>
{:else}
<button onclick={startBroadcasting}>Start Broadcasting</button>
<button onclick={startBroadcasting}>{_msg("Start Broadcasting")}</button>
{/if}
{/if}
<details>
<summary>Broadcasting?</summary>
<summary>{_msg("Broadcasting?")}</summary>
<p>
<small>
If you want to use `LiveSync`, you should broadcast changes. All `watching` peers which
detects this will start the replication for fetching. <br />
However, This should not be enabled if you want to increase your secrecy more.
{_msg(
"If you want to use `LiveSync`, you should broadcast changes. All `watching` peers which detects this will start the replication for fetching."
)}
<br />
{_msg("However, This should not be enabled if you want to increase your secrecy more.")}
</small>
</p>
</details>
@@ -424,13 +430,13 @@
</div>
<div>
<h2>Peers</h2>
<h2>{_msg("Peers")}</h2>
<table class="peers">
<thead>
<tr>
<th>Name</th>
<th>Action</th>
<th>Command</th>
<th>{_msg("Name")}</th>
<th>{_msg("Action")}</th>
<th>{_msg("Command")}</th>
</tr>
</thead>
<tbody>
@@ -95,40 +95,40 @@
</script>
<div class="server-status">
<h3>Signalling Status</h3>
<h3>{translateMessage("Signalling Status")}</h3>
<div class="status-item">
<span>Connection:</span>
<span>{translateMessage("Connection:")}</span>
<span class="status-value {isConnected ? 'connected' : 'disconnected'}">
{isConnected ? "🟢 Connected" : "🔴 Disconnected"}
{isConnected ? translateMessage("🟢 Connected") : translateMessage("🔴 Disconnected")}
</span>
</div>
<div class="status-item status-action">
{#if !isConnected}
<button onclick={onOpenConnection}>Open connection</button>
<button onclick={onOpenConnection}>{translateMessage("Open connection")}</button>
{:else}
<button onclick={onDisconnect}>Disconnect</button>
<button onclick={onDisconnect}>{translateMessage("Disconnect")}</button>
{/if}
</div>
{#if serverInfo}
<div class="status-item">
<span>Room ID suffix:</span>
<span class="room-suffix-display" title={roomSuffix || "Not configured"}>
<span>{translateMessage("Room ID suffix:")}</span>
<span class="room-suffix-display" title={roomSuffix || translateMessage("Not configured")}>
{roomSuffix || "-"}
</span>
</div>
<div class="status-item">
<span>Peer ID:</span>
<span>{translateMessage("Peer ID:")}</span>
<span class="peer-id-display" title={serverInfo.serverPeerId}>
{serverInfo.serverPeerId.slice(0, 12)}...
</span>
</div>
<div class="status-item">
<span>Devices:</span>
<span>{translateMessage("Devices:")}</span>
<span>{serverInfo.knownAdvertisements.length}</span>
</div>
{/if}
@@ -146,7 +146,7 @@
? translateMessage("Stop announcing changes")
: translateMessage("Start announcing changes")}
>
{isBroadcasting ? '📡 On' : '📡 Off'}
{isBroadcasting ? translateMessage("📡 On") : translateMessage("📡 Off")}
</button>
</div>
{/if}
@@ -154,39 +154,39 @@
{#if core}
<div class="status-item status-action diag-toggle-row">
<label class="broadcast-label" for="diag-toggle">
🕵️ Diag
{translateMessage("🕵️ Diag")}
</label>
<button
id="diag-toggle"
class="broadcast-button {useDiagRTC ? 'is-on' : 'is-off'}"
onclick={toggleDiagRTC}
title={useDiagRTC
? 'Diagnostic RTCPeerConnection is enabled'
: 'Use Diagnostic RTCPeerConnection for statistics'}
? translateMessage("Diagnostic RTCPeerConnection is enabled")
: translateMessage("Use Diagnostic RTCPeerConnection for statistics")}
>
{useDiagRTC ? 'On' : 'Off'}
{useDiagRTC ? translateMessage("On") : translateMessage("Off")}
</button>
</div>
{/if}
{#if serverInfo}
<div class="diag-section">
<h4>Stats</h4>
<h4>{translateMessage("Stats")}</h4>
<div class="diag-grid">
<div class="diag-item">
<span>Incoming:</span>
<span>{translateMessage("Incoming:")}</span>
<span>{serverInfo.diag.totalNewConnections}</span>
</div>
<div class="diag-item">
<span>Connected:</span>
<span>{translateMessage("Connected:")}</span>
<span>{serverInfo.diag.totalSuccessfulConnections}</span>
</div>
<div class="diag-item">
<span>Failed:</span>
<span>{translateMessage("Failed:")}</span>
<span>{serverInfo.diag.totalFailedConnections}</span>
</div>
<div class="diag-item">
<span>Closed:</span>
<span>{translateMessage("Closed:")}</span>
<span>{serverInfo.diag.totalClosedConnections}</span>
</div>
</div>
@@ -170,11 +170,11 @@
});
function getAcceptanceStatus(peer: P2PServerInfo["knownAdvertisements"][number]) {
if (peer.isTemporaryAccepted === true) return "ACCEPTED (in session)";
if (peer.isAccepted === true) return "ACCEPTED";
if (peer.isTemporaryAccepted === false) return "DENIED (in session)";
if (peer.isAccepted === false) return "DENIED";
return "NEW";
if (peer.isTemporaryAccepted === true) return translateMessage("ACCEPTED (in session)");
if (peer.isAccepted === true) return translateMessage("ACCEPTED");
if (peer.isTemporaryAccepted === false) return translateMessage("DENIED (in session)");
if (peer.isAccepted === false) return translateMessage("DENIED");
return translateMessage("NEW");
}
function getAcceptanceStatusClass(peer: P2PServerInfo["knownAdvertisements"][number]) {
@@ -409,7 +409,7 @@
<div class="p2p-container">
<div class="pane-header">
<h2>P2P Status</h2>
<h2>{translateMessage("P2P Status")}</h2>
<div class="pane-header-actions">
<div class="remote-picker-wrap">
<select
@@ -417,11 +417,11 @@
value={selectedP2PRemoteConfigurationId}
onchange={onP2PRemoteSelected}
disabled={selectingP2PRemote}
aria-label="Select active P2P remote"
title="Select active P2P remote"
aria-label={translateMessage("Select active P2P remote")}
title={translateMessage("Select active P2P remote")}
>
{#if p2pRemoteOptions.length === 0}
<option value="">Select P2P remote...</option>
<option value="">{translateMessage("Select P2P remote...")}</option>
{/if}
{#each p2pRemoteOptions as option}
<option value={option.id}>
@@ -432,8 +432,8 @@
<button
class="icon-button"
onclick={() => createAndSelectP2PRemote()}
title="Create P2P remote"
aria-label="Create P2P remote"
title={translateMessage("Create P2P remote")}
aria-label={translateMessage("Create P2P remote")}
>
+
</button>
@@ -441,8 +441,8 @@
<button
class="icon-button"
onclick={openConnectionSettings}
title="Open P2P Setup..."
aria-label="Open P2P Setup..."
title={translateMessage("Open P2P Setup...")}
aria-label={translateMessage("Open P2P Setup...")}
>
</button>
@@ -450,15 +450,17 @@
</div>
{#if !canEditP2PSettings()}
<p class="warning-line">Please select an active P2P remote configuration to change P2P sync targets.</p>
<p class="warning-line">
{translateMessage("Please select an active P2P remote configuration to change P2P sync targets.")}
</p>
{/if}
<P2PServerStatusCard {getLiveSyncReplicator} {core} />
<div class="peers-section">
<div class="peers-header">
<h3>Detected Peers</h3>
<button class="refresh" onclick={requestServerStatus}>Refresh</button>
<h3>{translateMessage("Detected Peers")}</h3>
<button class="refresh" onclick={requestServerStatus}>{translateMessage("Refresh")}</button>
</div>
{#if serverInfo && serverInfo.knownAdvertisements.length > 0}
@@ -470,7 +472,11 @@
{peer.name} :
<span class="peer-id-mini" title={peer.peerId}>({peer.peerId.slice(0, 8)})</span>
{#if isCommunicating(peer.peerId)}
<span class="comm-icon" title="Communicating" aria-label="Communicating">📡</span>
<span
class="comm-icon"
title={translateMessage("Communicating")}
aria-label={translateMessage("Communicating")}>📡</span
>
{/if}
</div>
<div class="peer-meta">
@@ -486,8 +492,12 @@
<button
class="emoji-button"
disabled={replicatingPeerId !== null}
title={replicatingPeerId === peer.peerId ? "Replicating..." : "Replicate now"}
aria-label={replicatingPeerId === peer.peerId ? "Replicating" : "Replicate now"}
title={replicatingPeerId === peer.peerId
? translateMessage("Replicating...")
: translateMessage("Replicate now")}
aria-label={replicatingPeerId === peer.peerId
? translateMessage("Replicating")
: translateMessage("Replicate now")}
onclick={() => startReplication(peer)}
>
{replicatingPeerId === peer.peerId ? "⏳" : "🔄"}
@@ -497,7 +507,7 @@
disabled={decidingPeerId !== null}
onclick={() => revokeDecision(peer)}
>
Revoke
{translateMessage("Revoke")}
</button>
<button
class="emoji-button"
@@ -534,11 +544,11 @@
</span>
</div>
<div class="decision-row">
<span class="decision-label">PERMANENT</span>
<span class="decision-label">{translateMessage("PERMANENT")}</span>
<button
class="emoji-button"
title="Allow permanently"
aria-label="Allow permanently"
title={translateMessage("Allow permanently")}
aria-label={translateMessage("Allow permanently")}
disabled={decidingPeerId !== null}
onclick={() => makeDecision(peer, true, false)}
>
@@ -546,8 +556,8 @@
</button>
<button
class="emoji-button mod-warning"
title="Deny permanently"
aria-label="Deny permanently"
title={translateMessage("Deny permanently")}
aria-label={translateMessage("Deny permanently")}
disabled={decidingPeerId !== null}
onclick={() => makeDecision(peer, false, false)}
>
@@ -555,11 +565,11 @@
</button>
</div>
<div class="decision-row">
<span class="decision-label">SESSION</span>
<span class="decision-label">{translateMessage("SESSION")}</span>
<button
class="emoji-button"
title="Allow in session"
aria-label="Allow in session"
title={translateMessage("Allow in session")}
aria-label={translateMessage("Allow in session")}
disabled={decidingPeerId !== null}
onclick={() => makeDecision(peer, true, true)}
>
@@ -567,8 +577,8 @@
</button>
<button
class="emoji-button mod-warning"
title="Deny in session"
aria-label="Deny in session"
title={translateMessage("Deny in session")}
aria-label={translateMessage("Deny in session")}
disabled={decidingPeerId !== null}
onclick={() => makeDecision(peer, false, true)}
>
@@ -582,7 +592,7 @@
disabled={decidingPeerId !== null}
onclick={() => revokeDecision(peer)}
>
Revoke
{translateMessage("Revoke")}
</button>
{/if}
</div>
@@ -590,9 +600,11 @@
{/each}
</div>
{:else if serverInfo}
<p class="no-peers">No devices available. Waiting for other devices to connect...</p>
<p class="no-peers">
{translateMessage("No devices available. Waiting for other devices to connect...")}
</p>
{:else}
<p class="no-peers">Fetching status...</p>
<p class="no-peers">{translateMessage("Fetching status...")}</p>
{/if}
</div>
</div>
@@ -1,6 +1,7 @@
<script lang="ts">
import { AcceptedStatus, type PeerStatus } from "@vrtmrz/livesync-commonlib/compat/replication/trystero/P2PReplicatorPaneCommon";
import type { P2PReplicatorHandle } from "./P2PReplicatorPaneHost";
import { $msg as translateMessage } from "@/common/translation";
interface Props {
peerStatus: PeerStatus;
@@ -27,6 +28,11 @@
peer.isSending ? ["SENDING"] : [],
].flat()
);
const chipLabels: Record<string, string> = {
WATCHING: translateMessage("WATCHING"),
FETCHING: translateMessage("FETCHING"),
SENDING: translateMessage("SENDING"),
};
let acceptedStatusChip = $derived.by(() =>
select(
peer.accepted.toString(),
@@ -40,6 +46,13 @@
""
) ?? ""
);
const acceptedStatusLabels: Record<string, string> = {
ACCEPTED: translateMessage("ACCEPTED"),
"ACCEPTED (in session)": translateMessage("ACCEPTED (in session)"),
"DENIED (in session)": translateMessage("DENIED (in session)"),
DENIED: translateMessage("DENIED"),
NEW: translateMessage("NEW"),
};
const classList = {
["SENDING"]: "connected",
["FETCHING"]: "connected",
@@ -75,13 +88,13 @@
const peerAttrLabels = $derived.by(() => {
const attrs = [];
if (peer.syncOnConnect) {
attrs.push("✔ SYNC");
attrs.push(translateMessage("✔ SYNC"));
}
if (peer.watchOnConnect) {
attrs.push("✔ WATCH");
attrs.push(translateMessage("✔ WATCH"));
}
if (peer.syncOnReplicationCommand) {
attrs.push("✔ SELECT");
attrs.push(translateMessage("✔ SELECT"));
}
return attrs;
});
@@ -113,12 +126,14 @@
</div>
<div class="status-chips">
<div class="row">
<span class="chip {select(acceptedStatusChip, classList)}">{acceptedStatusChip}</span>
<span class="chip {select(acceptedStatusChip, classList)}"
>{acceptedStatusLabels[acceptedStatusChip] ?? acceptedStatusChip}</span
>
</div>
{#if isAccepted}
<div class="row">
{#each statusChips as chip}
<span class="chip {select(chip, classList)}">{chip}</span>
<span class="chip {select(chip, classList)}">{chipLabels[chip] ?? chip}</span>
{/each}
</div>
{/if}
@@ -134,15 +149,25 @@
<div class="row">
{#if isNew}
{#if !isAccepted}
<button class="button" onclick={() => makeDecision(true, true)}>Accept in session</button>
<button class="button mod-cta" onclick={() => makeDecision(true, false)}>Accept</button>
<button class="button" onclick={() => makeDecision(true, true)}
>{translateMessage("Accept in session")}</button
>
<button class="button mod-cta" onclick={() => makeDecision(true, false)}
>{translateMessage("Accept")}</button
>
{/if}
{#if !isDenied}
<button class="button" onclick={() => makeDecision(false, true)}>Deny in session</button>
<button class="button mod-warning" onclick={() => makeDecision(false, false)}>Deny</button>
<button class="button" onclick={() => makeDecision(false, true)}
>{translateMessage("Deny in session")}</button
>
<button class="button mod-warning" onclick={() => makeDecision(false, false)}
>{translateMessage("Deny")}</button
>
{/if}
{:else}
<button class="button mod-warning" onclick={() => revokeDecision()}>Revoke</button>
<button class="button mod-warning" onclick={() => revokeDecision()}
>{translateMessage("Revoke")}</button
>
{/if}
</div>
</div>
@@ -155,9 +180,9 @@
<!-- <button class="button" onclick={replicateFrom} disabled={peer.isFetching}>📥</button>
<button class="button" onclick={replicateTo} disabled={peer.isSending}>📤</button> -->
{#if peer.isWatching}
<button class="button" onclick={stopWatching}>Stop ⚡</button>
<button class="button" onclick={stopWatching}>{translateMessage("Stop ⚡")}</button>
{:else}
<button class="button" onclick={startWatching} title="live"></button>
<button class="button" onclick={startWatching} title={translateMessage("live")}></button>
{/if}
{#if showPeerMenu}
<button class="button" onclick={moreMenu}>...</button>