Clarify desktop daemon wording and helper text

This commit is contained in:
Mohamed Boudra
2026-03-08 16:05:28 +07:00
parent 6f7b3db4fa
commit 1e5e0f625d
5 changed files with 51 additions and 52 deletions
@@ -65,7 +65,7 @@ export function AddHostMethodModal({
<Link2 size={18} color={theme.colors.foreground} /> <Link2 size={18} color={theme.colors.foreground} />
<View style={styles.optionBody}> <View style={styles.optionBody}>
<Text style={styles.optionText}>Direct connection</Text> <Text style={styles.optionText}>Direct connection</Text>
<Text style={styles.optionSubtext}>Local network or Tailscale (unencrypted).</Text> <Text style={styles.optionSubtext}>Local network or VPN.</Text>
</View> </View>
</Pressable> </Pressable>
@@ -74,7 +74,7 @@ export function AddHostMethodModal({
<QrCode size={18} color={theme.colors.foreground} /> <QrCode size={18} color={theme.colors.foreground} />
<View style={styles.optionBody}> <View style={styles.optionBody}>
<Text style={styles.optionText}>Scan QR code</Text> <Text style={styles.optionText}>Scan QR code</Text>
<Text style={styles.optionSubtext}>Relay pairing (E2EE).</Text> <Text style={styles.optionSubtext}>Encrypted relay connection.</Text>
</View> </View>
</Pressable> </Pressable>
) : null} ) : null}
@@ -83,7 +83,7 @@ export function AddHostMethodModal({
<ClipboardPaste size={18} color={theme.colors.foreground} /> <ClipboardPaste size={18} color={theme.colors.foreground} />
<View style={styles.optionBody}> <View style={styles.optionBody}>
<Text style={styles.optionText}>Paste pairing link</Text> <Text style={styles.optionText}>Paste pairing link</Text>
<Text style={styles.optionSubtext}>Relay pairing (E2EE).</Text> <Text style={styles.optionSubtext}>Encrypted relay connection.</Text>
</View> </View>
</Pressable> </Pressable>
</AdaptiveModalSheet> </AdaptiveModalSheet>
@@ -103,13 +103,13 @@ function buildConnectionFailureCopy(endpoint: string, error: unknown): { title:
rawLower.includes("connection refused") || rawLower.includes("connection refused") ||
rawLower.includes("err_connection_refused") rawLower.includes("err_connection_refused")
) { ) {
detail = "Connection was refused. Is the daemon running on that host and port?"; detail = "Connection refused. Is the server running at this address?";
} else if (rawLower.includes("enotfound") || rawLower.includes("not found")) { } else if (rawLower.includes("enotfound") || rawLower.includes("not found")) {
detail = "Host not found. Check the hostname and try again."; detail = "Host not found. Check the hostname and try again.";
} else if (rawLower.includes("ehostunreach") || rawLower.includes("host is unreachable")) { } else if (rawLower.includes("ehostunreach") || rawLower.includes("host is unreachable")) {
detail = "Host is unreachable. Check your network and firewall."; detail = "Host is unreachable. Check your network and firewall.";
} else if (rawLower.includes("certificate") || rawLower.includes("tls") || rawLower.includes("ssl")) { } else if (rawLower.includes("certificate") || rawLower.includes("tls") || rawLower.includes("ssl")) {
detail = "TLS/certificate error. This app expects a daemon reachable over the local network or via relay."; detail = "TLS error. Direct connections use an unencrypted local connection. Use relay for remote access.";
} else if (raw) { } else if (raw) {
detail = "Unable to connect. Check the host/port and that the daemon is reachable."; detail = "Unable to connect. Check the host/port and that the daemon is reachable.";
} else { } else {
@@ -221,7 +221,7 @@ export function AddHostModal({ visible, onClose, onCancel, onSaved, targetServer
return ( return (
<AdaptiveModalSheet title="Direct connection" visible={visible} onClose={handleClose} testID="add-host-modal"> <AdaptiveModalSheet title="Direct connection" visible={visible} onClose={handleClose} testID="add-host-modal">
<Text style={styles.helper}>Connect to a daemon by entering host:port.</Text> <Text style={styles.helper}>Enter the address of a Paseo server.</Text>
<View style={styles.field}> <View style={styles.field}>
<Text style={styles.label}>Host</Text> <Text style={styles.label}>Host</Text>
@@ -229,7 +229,7 @@ export function AddHostModal({ visible, onClose, onCancel, onSaved, targetServer
ref={hostInputRef} ref={hostInputRef}
value={endpointRaw} value={endpointRaw}
onChangeText={setEndpointRaw} onChangeText={setEndpointRaw}
placeholder="host:6767" placeholder="hostname:port"
placeholderTextColor={theme.colors.foregroundMuted} placeholderTextColor={theme.colors.foregroundMuted}
style={styles.input} style={styles.input}
autoCapitalize="none" autoCapitalize="none"
@@ -149,7 +149,7 @@ export function PairLinkModal({ visible, onClose, onCancel, onSaved, targetServe
return ( return (
<AdaptiveModalSheet title="Paste pairing link" visible={visible} onClose={handleClose} testID="pair-link-modal"> <AdaptiveModalSheet title="Paste pairing link" visible={visible} onClose={handleClose} testID="pair-link-modal">
<Text style={styles.helper}>Paste the daemon’s pairing link.</Text> <Text style={styles.helper}>Paste the pairing link from your server.</Text>
<View style={styles.field}> <View style={styles.field}>
<Text style={styles.label}>Pairing link</Text> <Text style={styles.label}>Pairing link</Text>
@@ -88,9 +88,9 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
statusError ?? statusError ??
(managedStatus?.daemonRunning (managedStatus?.daemonRunning
? managedStatus.transportType === "tcp" ? managedStatus.transportType === "tcp"
? `Managed daemon running on explicit TCP ${managedStatus.transportPath}.` ? `Running on ${managedStatus.transportPath}.`
: `Managed daemon running on private ${managedStatus.transportType}.` : "Running."
: "Managed daemon is currently stopped."); : "Not running.");
const handleUpdateLocalDaemon = useCallback(() => { const handleUpdateLocalDaemon = useCallback(() => {
if (!showSection) { if (!showSection) {
@@ -101,10 +101,10 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
} }
void confirmDialog({ void confirmDialog({
title: "Restart managed daemon", title: "Restart daemon",
message: message:
"This restarts the desktop-managed daemon using its private managed home and socket.", "This will restart the built-in daemon. The app will reconnect automatically.",
confirmLabel: "Restart daemon", confirmLabel: "Restart",
cancelLabel: "Cancel", cancelLabel: "Cancel",
}) })
.then((confirmed) => { .then((confirmed) => {
@@ -118,13 +118,13 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
void restartManagedDaemon() void restartManagedDaemon()
.then((status) => { .then((status) => {
setManagedStatus(status); setManagedStatus(status);
setStatusMessage("Managed daemon restarted."); setStatusMessage("Daemon restarted.");
return loadManagedStatus(); return loadManagedStatus();
}) })
.catch((error) => { .catch((error) => {
console.error("[Settings] Failed to restart managed daemon", error); console.error("[Settings] Failed to restart managed daemon", error);
const message = error instanceof Error ? error.message : String(error); const message = error instanceof Error ? error.message : String(error);
setStatusMessage(`Managed daemon restart failed: ${message}`); setStatusMessage(`Restart failed: ${message}`);
}) })
.finally(() => { .finally(() => {
setIsRestartingDaemon(false); setIsRestartingDaemon(false);
@@ -132,7 +132,7 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
}) })
.catch((error) => { .catch((error) => {
console.error("[Settings] Failed to open managed daemon restart confirmation", error); console.error("[Settings] Failed to open managed daemon restart confirmation", error);
Alert.alert("Error", "Unable to open the managed daemon restart confirmation dialog."); Alert.alert("Error", "Unable to open the restart confirmation dialog.");
}); });
}, [isRestartingDaemon, loadManagedStatus, showSection]); }, [isRestartingDaemon, loadManagedStatus, showSection]);
@@ -162,7 +162,7 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
}) })
.catch((error) => { .catch((error) => {
const message = error instanceof Error ? error.message : String(error); const message = error instanceof Error ? error.message : String(error);
setCliStatusMessage(`CLI shim action failed: ${message}`); setCliStatusMessage(`CLI install failed: ${message}`);
}) })
.finally(() => { .finally(() => {
setIsInstallingCli(false); setIsInstallingCli(false);
@@ -191,10 +191,10 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
void Clipboard.setStringAsync(logPath) void Clipboard.setStringAsync(logPath)
.then(() => { .then(() => {
Alert.alert("Copied", "Managed daemon log path copied."); Alert.alert("Copied", "Log path copied.");
}) })
.catch((error) => { .catch((error) => {
console.error("[Settings] Failed to copy managed daemon log path", error); console.error("[Settings] Failed to copy log path", error);
Alert.alert("Error", "Unable to copy log path."); Alert.alert("Error", "Unable to copy log path.");
}); });
}, [managedLogs?.logPath, managedStatus?.logPath]); }, [managedLogs?.logPath, managedStatus?.logPath]);
@@ -219,7 +219,7 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
.then((pairing) => { .then((pairing) => {
setPairingOffer(pairing); setPairingOffer(pairing);
if (!pairing.relayEnabled || !pairing.url) { if (!pairing.relayEnabled || !pairing.url) {
setPairingStatusMessage("Relay pairing is disabled for this managed daemon."); setPairingStatusMessage("Relay pairing is not available.");
} }
}) })
.catch((error) => { .catch((error) => {
@@ -269,12 +269,12 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
}) })
.then((status) => { .then((status) => {
setManagedStatus(status); setManagedStatus(status);
setStatusMessage("Managed TCP exposure disabled."); setStatusMessage("Network access disabled.");
return loadManagedStatus(); return loadManagedStatus();
}) })
.catch((error) => { .catch((error) => {
const message = error instanceof Error ? error.message : String(error); const message = error instanceof Error ? error.message : String(error);
setStatusMessage(`Managed TCP update failed: ${message}`); setStatusMessage(`Failed to update: ${message}`);
}) })
.finally(() => { .finally(() => {
setIsSavingTcpSettings(false); setIsSavingTcpSettings(false);
@@ -296,15 +296,15 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
return; return;
} }
if (port === 6767) { if (port === 6767) {
Alert.alert("Port unavailable", "Managed TCP mode must not claim 127.0.0.1:6767."); Alert.alert("Port unavailable", "Port 6767 is reserved. Choose a different port.");
return; return;
} }
void confirmDialog({ void confirmDialog({
title: "Enable managed TCP", title: "Enable network access",
message: message:
"This exposes the managed daemon on a TCP listener. Relay remains available, but network exposure is no longer private to the desktop app.", "This makes the daemon reachable on your local network. Relay connections will continue to work.",
confirmLabel: "Enable TCP", confirmLabel: "Enable",
cancelLabel: "Cancel", cancelLabel: "Cancel",
}) })
.then((confirmed) => { .then((confirmed) => {
@@ -317,20 +317,20 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
.then((status) => { .then((status) => {
setManagedStatus(status); setManagedStatus(status);
setIsTcpModalOpen(false); setIsTcpModalOpen(false);
setStatusMessage(`Managed TCP exposure enabled on ${host}:${port}.`); setStatusMessage(`Network access enabled on ${host}:${port}.`);
return loadManagedStatus(); return loadManagedStatus();
}) })
.catch((error) => { .catch((error) => {
const message = error instanceof Error ? error.message : String(error); const message = error instanceof Error ? error.message : String(error);
setStatusMessage(`Managed TCP update failed: ${message}`); setStatusMessage(`Failed to update: ${message}`);
}) })
.finally(() => { .finally(() => {
setIsSavingTcpSettings(false); setIsSavingTcpSettings(false);
}); });
}) })
.catch((error) => { .catch((error) => {
console.error("[Settings] Failed to open managed TCP confirmation", error); console.error("[Settings] Failed to open TCP confirmation", error);
Alert.alert("Error", "Unable to open the managed TCP confirmation dialog."); Alert.alert("Error", "Unable to open the confirmation dialog.");
}); });
}, [isSavingTcpSettings, loadManagedStatus, tcpHostInput, tcpPortInput]); }, [isSavingTcpSettings, loadManagedStatus, tcpHostInput, tcpPortInput]);
@@ -340,7 +340,7 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
return ( return (
<View style={styles.section}> <View style={styles.section}>
<Text style={styles.sectionTitle}>Managed daemon</Text> <Text style={styles.sectionTitle}>Built-in daemon</Text>
<View style={styles.card}> <View style={styles.card}>
<View style={styles.row}> <View style={styles.row}>
<View style={styles.rowContent}> <View style={styles.rowContent}>
@@ -353,7 +353,7 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
<View style={styles.rowContent}> <View style={styles.rowContent}>
<Text style={styles.rowTitle}>Restart daemon</Text> <Text style={styles.rowTitle}>Restart daemon</Text>
<Text style={styles.hintText}> <Text style={styles.hintText}>
Restarts the desktop-managed daemon without touching `~/.paseo` or `127.0.0.1:6767`. Restarts the built-in daemon. Your data and external connections are not affected.
</Text> </Text>
{statusMessage ? ( {statusMessage ? (
<Text style={styles.statusText}>{statusMessage}</Text> <Text style={styles.statusText}>{statusMessage}</Text>
@@ -370,9 +370,9 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
</View> </View>
<View style={[styles.row, styles.rowBorder]}> <View style={[styles.row, styles.rowBorder]}>
<View style={styles.rowContent}> <View style={styles.rowContent}>
<Text style={styles.rowTitle}>CLI shim</Text> <Text style={styles.rowTitle}>Command line (CLI)</Text>
<Text style={styles.hintText}> <Text style={styles.hintText}>
Installs a global `paseo` launcher that forwards into the desktop-managed runtime. Adds the `paseo` command to your terminal.
</Text> </Text>
{cliStatusMessage ? <Text style={styles.statusText}>{cliStatusMessage}</Text> : null} {cliStatusMessage ? <Text style={styles.statusText}>{cliStatusMessage}</Text> : null}
</View> </View>
@@ -395,7 +395,7 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
<Text style={styles.hintText}> <Text style={styles.hintText}>
{managedLogs?.logPath ?? {managedLogs?.logPath ??
managedStatus?.logPath ?? managedStatus?.logPath ??
"Managed daemon log path unavailable."} "Log path unavailable."}
</Text> </Text>
</View> </View>
<View style={styles.actionGroup}> <View style={styles.actionGroup}>
@@ -418,7 +418,7 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
<View style={styles.rowContent}> <View style={styles.rowContent}>
<Text style={styles.rowTitle}>Pair device</Text> <Text style={styles.rowTitle}>Pair device</Text>
<Text style={styles.hintText}> <Text style={styles.hintText}>
Show the managed daemon QR code and a copyable pairing link for the mobile app. Connect your phone to this computer.
</Text> </Text>
</View> </View>
<Button variant="secondary" size="sm" onPress={handleOpenPairingModal}> <Button variant="secondary" size="sm" onPress={handleOpenPairingModal}>
@@ -427,10 +427,9 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
</View> </View>
<View style={[styles.row, styles.rowBorder]}> <View style={[styles.row, styles.rowBorder]}>
<View style={styles.rowContent}> <View style={styles.rowContent}>
<Text style={styles.rowTitle}>Advanced TCP</Text> <Text style={styles.rowTitle}>Network access</Text>
<Text style={styles.hintText}> <Text style={styles.hintText}>
Default off. Use only if you explicitly want a network listener instead of the Allow other apps or devices on your network to connect directly.
private managed transport.
</Text> </Text>
<Text style={styles.statusText}> <Text style={styles.statusText}>
{managedStatus?.tcpEnabled {managedStatus?.tcpEnabled
@@ -464,8 +463,8 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
{daemonVersionMismatch ? ( {daemonVersionMismatch ? (
<View style={styles.warningCard}> <View style={styles.warningCard}>
<Text style={styles.warningText}> <Text style={styles.warningText}>
Desktop app and managed daemon versions differ. Keep both on the same version to avoid App and daemon versions don't match. Update both to the same version for the best
stability issues or breaking changes. experience.
</Text> </Text>
</View> </View>
) : null} ) : null}
@@ -473,12 +472,12 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
<AdaptiveModalSheet <AdaptiveModalSheet
visible={isTcpModalOpen} visible={isTcpModalOpen}
onClose={() => setIsTcpModalOpen(false)} onClose={() => setIsTcpModalOpen(false)}
title="Managed TCP settings" title="Network access"
> >
<View style={styles.modalBody}> <View style={styles.modalBody}>
<Text style={styles.hintText}> <Text style={styles.hintText}>
TCP is advanced opt-in only. It must stay off unless you explicitly need direct network Expose the daemon on a specific address and port so other apps can connect directly.
access, and it must never use port 6767. Port 6767 is reserved and cannot be used here.
</Text> </Text>
<AdaptiveTextInput <AdaptiveTextInput
value={tcpHostInput} value={tcpHostInput}
@@ -553,7 +552,7 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
<AdaptiveModalSheet <AdaptiveModalSheet
visible={isLogsModalOpen} visible={isLogsModalOpen}
onClose={() => setIsLogsModalOpen(false)} onClose={() => setIsLogsModalOpen(false)}
title="Managed daemon logs" title="Daemon logs"
testID="managed-daemon-logs-dialog" testID="managed-daemon-logs-dialog"
snapPoints={["70%", "92%"]} snapPoints={["70%", "92%"]}
> >
@@ -561,7 +560,7 @@ export function LocalDaemonSection({ appVersion }: LocalDaemonSectionProps) {
<Text style={styles.hintText}> <Text style={styles.hintText}>
{managedLogs?.logPath ?? {managedLogs?.logPath ??
managedStatus?.logPath ?? managedStatus?.logPath ??
"Managed daemon log path unavailable."} "Log path unavailable."}
</Text> </Text>
<Text style={styles.logOutput} selectable> <Text style={styles.logOutput} selectable>
{managedLogs?.contents.length ? managedLogs.contents : "(log file is empty)"} {managedLogs?.contents.length ? managedLogs.contents : "(log file is empty)"}
+5 -5
View File
@@ -56,10 +56,10 @@ function formatHostConnectionLabel(connection: HostConnection): string {
return `Relay (${connection.relayEndpoint})`; return `Relay (${connection.relayEndpoint})`;
} }
if (connection.type === "directSocket") { if (connection.type === "directSocket") {
return `Socket (${connection.path})`; return `Local (${connection.path})`;
} }
if (connection.type === "directPipe") { if (connection.type === "directPipe") {
return `Pipe (${connection.path})`; return `Local (${connection.path})`;
} }
return `TCP (${connection.endpoint})`; return `TCP (${connection.endpoint})`;
} }
@@ -76,10 +76,10 @@ function formatActiveConnectionBadge(input: {
return { icon: <Globe size={theme.iconSize.xs} color={theme.colors.foregroundMuted} />, text: "Relay" }; return { icon: <Globe size={theme.iconSize.xs} color={theme.colors.foregroundMuted} />, text: "Relay" };
} }
if (activeConnection.type === "directSocket") { if (activeConnection.type === "directSocket") {
return { icon: <Monitor size={theme.iconSize.xs} color={theme.colors.foregroundMuted} />, text: "Socket" }; return { icon: <Monitor size={theme.iconSize.xs} color={theme.colors.foregroundMuted} />, text: "Local" };
} }
if (activeConnection.type === "directPipe") { if (activeConnection.type === "directPipe") {
return { icon: <Monitor size={theme.iconSize.xs} color={theme.colors.foregroundMuted} />, text: "Pipe" }; return { icon: <Monitor size={theme.iconSize.xs} color={theme.colors.foregroundMuted} />, text: "Local" };
} }
return { return {
icon: <Monitor size={theme.iconSize.xs} color={theme.colors.foregroundMuted} />, icon: <Monitor size={theme.iconSize.xs} color={theme.colors.foregroundMuted} />,
@@ -705,7 +705,7 @@ export default function SettingsScreen() {
); );
const restartConfirmationMessage = const restartConfirmationMessage =
"This will immediately stop the Paseo daemon process. The app will disconnect until it restarts."; "This will restart the daemon. The app will reconnect automatically.";
if (isLoading) { if (isLoading) {
return ( return (