diff --git a/docs/p2p_sync_updates_2026.md b/docs/p2p_sync_updates_2026.md index 34dc9de..b6f3903 100644 --- a/docs/p2p_sync_updates_2026.md +++ b/docs/p2p_sync_updates_2026.md @@ -20,15 +20,21 @@ Once you have saved the settings, return to the **P2P Status Pane** and click th ## 3. Real-time Control The status pane in the right sidebar provides granular control over your synchronisation: +- **Active P2P Remote (new):** P2P now has its own active remote selection, separate from the normal active remote for database replication. Use the combo box next to the cog icon to choose which P2P remote configuration is active for P2P features. +- **Create P2P Remote (new):** Use the **+** button to open the P2P setup dialogue and create a dedicated P2P remote configuration. This is recommended when no P2P active remote has been selected yet. +- **Selection required (new):** If no P2P active remote is selected, the pane asks for selection before P2P target-related changes are saved. + - **Signalling Status:** Shows if you are connected to the relay (🟢 Online). - **Live-push (Broadcast):** Toggle "Broadcast changes" to notify other peers whenever you make an edit. -- **Watch:** Enable "Watch" on specific peers to automatically pull changes when they broadcast. This creates a "LiveSync-like" experience. -- **Sync (🔄/🔁):** Mark specific peers as **sync targets**. Peers marked here will be included when you run the **"P2P: Sync with targets"** command (see section 5). Click the button next to a peer to toggle it on (🔄, highlighted) or off (🔁). This setting is persisted in your configuration. +- **Replicate now (🔄):** Start immediate bidirectional replication with a visible peer (Pull, then Push). +- **Watch (🔔/🔕):** Enable "Watch" on specific peers to automatically pull changes when they broadcast. This creates a "LiveSync-like" experience. +- **Sync target (🔗/⛓️‍💥):** Mark specific peers as **sync targets**. Peers marked here will be included when you run the **"P2P: Sync with targets"** command (see section 5). Click the button next to a peer to toggle it on (🔗, highlighted) or off (⛓️‍💥). This setting is persisted in your configuration. ## 4. Replication Dialogue If you want to synchronise with a specific peer manually, use the **Replication** command or button. This opens the **Replication Dialogue** listing available devices. Inside the dialogue, the **Server Status** card at the top confirms you are still connected while performing the sync. +The status card now shows a stable **Room ID suffix** above **Peer ID**. The Room ID suffix is better for identifying your P2P group, while Peer ID may change between connections. Two actions are available per peer: diff --git a/src/features/P2PSync/P2PReplicator/P2PReplicatorPane.svelte b/src/features/P2PSync/P2PReplicator/P2PReplicatorPane.svelte index 9dd7c8e..948a20e 100644 --- a/src/features/P2PSync/P2PReplicator/P2PReplicatorPane.svelte +++ b/src/features/P2PSync/P2PReplicator/P2PReplicatorPane.svelte @@ -5,20 +5,21 @@ AcceptedStatus, ConnectionStatus, type PeerStatus, - } from "../../../lib/src/replication/trystero/P2PReplicatorPaneCommon"; - import type { LiveSyncTrysteroReplicator } from "../../../lib/src/replication/trystero/LiveSyncTrysteroReplicator"; + } from "@lib/replication/trystero/P2PReplicatorPaneCommon"; + import type { LiveSyncTrysteroReplicator } from "@lib/replication/trystero/LiveSyncTrysteroReplicator"; import PeerStatusRow from "../P2PReplicator/PeerStatusRow.svelte"; - import { EVENT_LAYOUT_READY, eventHub } from "../../../common/events"; + import { EVENT_LAYOUT_READY, eventHub } from "@/common/events"; import { type PeerInfo, type P2PServerInfo, EVENT_SERVER_STATUS, EVENT_REQUEST_STATUS, EVENT_P2P_REPLICATOR_STATUS, - } from "../../../lib/src/replication/trystero/TrysteroReplicatorP2PServer"; - import { type P2PReplicatorStatus } from "../../../lib/src/replication/trystero/TrysteroReplicator"; - import { $msg as _msg } from "../../../lib/src/common/i18n"; - import { SETTING_KEY_P2P_DEVICE_NAME } from "../../../lib/src/common/types"; + } from "@lib/replication/trystero/TrysteroReplicatorP2PServer"; + import { type P2PReplicatorStatus } from "@lib/replication/trystero/TrysteroReplicator"; + import { $msg as _msg } from "@lib/common/i18n"; + import { SETTING_KEY_P2P_DEVICE_NAME } from "@lib/common/types"; + import { generateP2PRoomId } from "@lib/common/utils"; import type { LiveSyncBaseCore } from "@/LiveSyncBaseCore"; interface Props { @@ -148,6 +149,7 @@ eventHub.emitEvent(EVENT_REQUEST_STATUS); return () => { r(); + rx(); r2(); r3(); }; @@ -216,18 +218,8 @@ function useDefaultRelay() { eRelay = DEFAULT_SETTINGS.P2P_relays; } - function _generateRandom() { - return (Math.floor(Math.random() * 1000) + 1000).toString().substring(1); - } - function generateRandom(length: number) { - let buf = ""; - while (buf.length < length) { - buf += "-" + _generateRandom(); - } - return buf.substring(1, length); - } function chooseRandom() { - eRoomId = generateRandom(12) + "-" + Math.random().toString(36).substring(2, 5); + eRoomId = generateP2PRoomId(); } async function openServer() { @@ -251,7 +243,7 @@ setting?: boolean; }; return initialDialogStatus; - } catch (e) { + } catch { return {}; } }; diff --git a/src/features/P2PSync/P2PReplicator/P2PServerStatusCard.svelte b/src/features/P2PSync/P2PReplicator/P2PServerStatusCard.svelte index 980aed4..4d53651 100644 --- a/src/features/P2PSync/P2PReplicator/P2PServerStatusCard.svelte +++ b/src/features/P2PSync/P2PReplicator/P2PServerStatusCard.svelte @@ -8,17 +8,22 @@ EVENT_REQUEST_STATUS, EVENT_P2P_REPLICATOR_STATUS, } from "@lib/replication/trystero/TrysteroReplicatorP2PServer"; + import { EVENT_SETTING_SAVED } from "@lib/events/coreEvents"; import type { LiveSyncTrysteroReplicator } from "@/lib/src/replication/trystero/LiveSyncTrysteroReplicator"; import type { P2PReplicatorStatus } from "@/lib/src/replication/trystero/TrysteroReplicator"; + import { extractP2PRoomSuffix } from "@/lib/src/common/utils"; + import type { LiveSyncBaseCore } from "@/LiveSyncBaseCore"; interface Props { liveSyncReplicator: LiveSyncTrysteroReplicator; showBroadcastToggle?: boolean; + core?: LiveSyncBaseCore; } - let { liveSyncReplicator, showBroadcastToggle = true }: Props = $props(); + let { liveSyncReplicator, showBroadcastToggle = true, core }: Props = $props(); let serverInfo = $state(undefined); let replicatorStatus = $state(undefined); + let roomSuffix = $state(extractP2PRoomSuffix(core?.services.setting.currentSettings()?.P2P_roomID ?? "")); async function requestServerStatus() { await Promise.resolve(liveSyncReplicator.requestStatus()); @@ -46,10 +51,14 @@ onMount(() => { const unsubscribe = eventHub.onEvent(EVENT_SERVER_STATUS, (status) => { serverInfo = status; + roomSuffix = extractP2PRoomSuffix(status?.roomId ?? ""); }); const unsubscribeStatus = eventHub.onEvent(EVENT_P2P_REPLICATOR_STATUS, (status) => { replicatorStatus = status; }); + const unsubscribeSettings = eventHub.onEvent(EVENT_SETTING_SAVED, (settings) => { + roomSuffix = extractP2PRoomSuffix(settings?.P2P_roomID ?? ""); + }); fireAndForget(async () => { await delay(100); @@ -59,6 +68,7 @@ return () => { unsubscribe(); unsubscribeStatus(); + unsubscribeSettings(); }; }); @@ -85,6 +95,13 @@ {#if serverInfo} +
+ Room ID suffix: + + {roomSuffix || "-"} + +
+
Peer ID: @@ -162,6 +179,12 @@ text-overflow: ellipsis; } + .room-suffix-display { + font-family: monospace; + font-size: 0.85rem; + font-weight: 600; + } + .broadcast-row { align-items: center; margin-top: 0.25rem; diff --git a/src/features/P2PSync/P2PReplicator/P2PServerStatusPane.svelte b/src/features/P2PSync/P2PReplicator/P2PServerStatusPane.svelte index fc148f8..aeeee48 100644 --- a/src/features/P2PSync/P2PReplicator/P2PServerStatusPane.svelte +++ b/src/features/P2PSync/P2PReplicator/P2PServerStatusPane.svelte @@ -1,6 +1,6 @@

P2P Status

- +
+
+ + +
+ +
- + {#if !canEditP2PSettings()} +

Please select an active P2P remote configuration to change P2P sync targets.

+ {/if} + +
@@ -225,6 +457,15 @@ {getAcceptanceStatus(peer)} +
SYNC @@ -249,9 +491,10 @@ class="emoji-button {isSyncTarget(peer.name) ? 'is-watching' : ''}" title={isSyncTarget(peer.name) ? 'Sync target \u2014 click to remove' : 'Set as sync target'} aria-label={isSyncTarget(peer.name) ? 'Remove sync target' : 'Set sync target'} + disabled={!canEditP2PSettings()} onclick={() => toggleSyncTarget(peer)} > - {isSyncTarget(peer.name) ? '🔄' : '🔁'} + {isSyncTarget(peer.name) ? '🔗' : '⛓️‍💥'}
{:else}
@@ -345,6 +588,37 @@ gap: 0.5rem; } + .pane-header-actions { + display: flex; + align-items: center; + gap: 0.4rem; + min-width: 0; + } + + .remote-picker-wrap { + display: inline-flex; + gap: 0.3rem; + align-items: center; + min-width: 0; + } + + .remote-picker { + max-width: 14rem; + min-width: 8rem; + height: 1.9rem; + border: 1px solid var(--divider-color); + border-radius: 0.4rem; + background-color: var(--interactive-normal); + color: var(--text-normal); + padding: 0 0.45rem; + } + + .warning-line { + margin: -0.2rem 0 0; + font-size: 0.82rem; + color: var(--text-warning); + } + .pane-header h2 { margin: 0; font-size: 1.1rem; @@ -511,7 +785,7 @@ } .accepted-row { - grid-template-columns: 1fr auto; + grid-template-columns: 1fr auto auto; } .decision-label { diff --git a/src/lib b/src/lib index 07e287c..f2b910a 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit 07e287c53122a09300f916b5679826e2d1d75b5a +Subproject commit f2b910aa4e9c4217f62d4d51ea5e25855cb3e62b diff --git a/src/modules/features/SetupWizard/dialogs/SetupRemoteP2P.svelte b/src/modules/features/SetupWizard/dialogs/SetupRemoteP2P.svelte index 64a794f..cd2020e 100644 --- a/src/modules/features/SetupWizard/dialogs/SetupRemoteP2P.svelte +++ b/src/modules/features/SetupWizard/dialogs/SetupRemoteP2P.svelte @@ -1,13 +1,13 @@