diff --git a/Cargo.lock b/Cargo.lock
index 5c7f2da..96e64f4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -347,7 +347,7 @@ dependencies = [
[[package]]
name = "coa-core"
-version = "0.1.2"
+version = "0.1.3"
dependencies = [
"base64 0.22.1",
"chrono",
@@ -375,7 +375,7 @@ dependencies = [
[[package]]
name = "coa-release"
-version = "0.1.2"
+version = "0.1.3"
dependencies = [
"chrono",
"coa-core",
@@ -384,7 +384,7 @@ dependencies = [
[[package]]
name = "coa-server-manager"
-version = "0.1.2"
+version = "0.1.3"
dependencies = [
"coa-core",
"serde",
diff --git a/Cargo.toml b/Cargo.toml
index 85d3932..864976f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,7 +3,7 @@ resolver = "2"
members = ["crates/coa-core", "crates/coa-release", "src-tauri"]
[workspace.package]
-version = "0.1.2"
+version = "0.1.3"
edition = "2021"
license = "AGPL-3.0-only"
diff --git a/package.json b/package.json
index 27427c1..28a21ef 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "coa-server-manager",
"private": true,
- "version": "0.1.2",
+ "version": "0.1.3",
"license": "AGPL-3.0-only",
"type": "module",
"scripts": {
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index 415b386..7a7fe33 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "CoA Server Manager",
- "version": "0.1.2",
+ "version": "0.1.3",
"identifier": "com.coa.servermanager",
"build": {
"beforeDevCommand": "npm run dev",
diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts
index f99bd68..f492b08 100644
--- a/src/i18n/locales/de.ts
+++ b/src/i18n/locales/de.ts
@@ -430,6 +430,9 @@ const de: Partial> = {
"selfupd.now": "Jetzt installieren",
"about.auto": "Updates im Hintergrund laden und beim Schließen des Managers installieren",
"about.autoHint": "Ein Manager-Update stoppt oder verändert deine Server nie.",
+
+ // Friends line
+ "fr.copyLine": "Zeile kopieren",
};
export default de;
diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts
index 00d3c29..3d221d9 100644
--- a/src/i18n/locales/en.ts
+++ b/src/i18n/locales/en.ts
@@ -430,6 +430,9 @@ const en = {
"selfupd.now": "Install now",
"about.auto": "Download updates in the background and install them when I close the Manager",
"about.autoHint": "Your servers are never stopped or changed by a Manager update.",
+
+ // Friends line
+ "fr.copyLine": "Copy line",
} as const;
export default en;
diff --git a/src/i18n/locales/es.ts b/src/i18n/locales/es.ts
index a8caa4e..d487e18 100644
--- a/src/i18n/locales/es.ts
+++ b/src/i18n/locales/es.ts
@@ -430,6 +430,9 @@ const es: Partial> = {
"selfupd.now": "Instalar ahora",
"about.auto": "Descargar las actualizaciones en segundo plano e instalarlas al cerrar el Manager",
"about.autoHint": "Una actualización del Manager nunca detiene ni cambia tus servidores.",
+
+ // Friends line
+ "fr.copyLine": "Copiar línea",
};
export default es;
diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts
index df005b4..61aeb1e 100644
--- a/src/i18n/locales/fr.ts
+++ b/src/i18n/locales/fr.ts
@@ -430,6 +430,9 @@ const fr: Partial> = {
"selfupd.now": "Installer maintenant",
"about.auto": "Télécharger les mises à jour en arrière-plan et les installer à la fermeture du Manager",
"about.autoHint": "Une mise à jour du Manager n’arrête ni ne modifie jamais vos serveurs.",
+
+ // Friends line
+ "fr.copyLine": "Copier la ligne",
};
export default fr;
diff --git a/src/i18n/locales/ru.ts b/src/i18n/locales/ru.ts
index 7889ffb..c75e753 100644
--- a/src/i18n/locales/ru.ts
+++ b/src/i18n/locales/ru.ts
@@ -430,6 +430,9 @@ const ru: Partial> = {
"selfupd.now": "Установить сейчас",
"about.auto": "Скачивать обновления в фоне и устанавливать их, когда я закрою менеджер",
"about.autoHint": "Обновление менеджера никогда не останавливает и не меняет ваши серверы.",
+
+ // Friends line
+ "fr.copyLine": "Скопировать строку",
};
export default ru;
diff --git a/src/index.css b/src/index.css
index e6f1ac5..d441f36 100644
--- a/src/index.css
+++ b/src/index.css
@@ -16,6 +16,12 @@
--font-sans: "Segoe UI Variable Text", "Segoe UI", system-ui, sans-serif;
}
+/* Everything is sized in rem, so scaling the root font grows the whole interface with the window
+ (16px up to about 1280px wide, then gradually larger; capped at 26px). */
+html {
+ font-size: clamp(16px, calc(0.75vw + 6.4px), 26px);
+}
+
html,
body,
#root {
diff --git a/src/lib/selfUpdate.ts b/src/lib/selfUpdate.ts
index 47c498c..e73bca6 100644
--- a/src/lib/selfUpdate.ts
+++ b/src/lib/selfUpdate.ts
@@ -103,22 +103,27 @@ export async function installNow(): Promise {
/** Install a downloaded update when the user closes the window. Call once at start-up. */
export function installOnClose(): () => void {
let stop: (() => void) | undefined;
- void getCurrentWindow()
- .onCloseRequested(async (event) => {
- if (state.phase !== "ready" || !update || !autoUpdateEnabled()) return;
- event.preventDefault();
- try {
- set({ phase: "installing" });
- await update.install();
- } catch (e) {
- set({ phase: "error", error: String(e) });
- await getCurrentWindow().destroy();
- }
- })
- .then((un) => {
- stop = un;
- })
- .catch(() => {});
+ try {
+ const win = getCurrentWindow();
+ void win
+ .onCloseRequested(async (event) => {
+ if (state.phase !== "ready" || !update || !autoUpdateEnabled()) return;
+ event.preventDefault();
+ try {
+ set({ phase: "installing" });
+ await update.install();
+ } catch (e) {
+ set({ phase: "error", error: String(e) });
+ await win.destroy();
+ }
+ })
+ .then((un) => {
+ stop = un;
+ })
+ .catch(() => {});
+ } catch {
+ /* not running inside the desktop shell (browser preview) */
+ }
return () => stop?.();
}
diff --git a/src/screens/FriendsPage.tsx b/src/screens/FriendsPage.tsx
index 3b2cc0e..e723f20 100644
--- a/src/screens/FriendsPage.tsx
+++ b/src/screens/FriendsPage.tsx
@@ -22,6 +22,30 @@ const MODES: { id: FriendsMode; title: Key; text: Key }[] = [
{ id: "private", title: "fr.mode.private.title", text: "fr.mode.private.text" },
];
+/** A ready-to-paste realmlist line for the friend, shown as soon as the address is known. */
+function RealmlistLine({ host }: { host: string }) {
+ const t = useT();
+ const [copied, setCopied] = useState(false);
+ const line = `set realmlist ${host}`;
+ return (
+
+ {line}
+
+
+ );
+}
+
export function FriendsPage({ serverId }: { serverId: string }) {
const t = useT();
const human = useHuman();
@@ -103,7 +127,12 @@ export function FriendsPage({ serverId }: { serverId: string }) {
{t(m.text)}
- {m.id === "lan" && {t("fr.yourLan")} {st.lan_ip ?? t("fr.unknown")}
}
+ {m.id === "lan" && (
+ <>
+ {t("fr.yourLan")} {st.lan_ip ?? t("fr.unknown")}
+ {st.lan_ip && cur.mode !== m.id && }
+ >
+ )}
{m.id === "direct" && (
@@ -119,6 +148,7 @@ export function FriendsPage({ serverId }: { serverId: string }) {
{net?.public_ip && net.reachability !== "cgnat" && (
{t("fr.publicAddr")} {net.public_ip}{!net.router_found && {t("fr.noRouter")}}
)}
+ {net?.public_ip && net.reachability !== "cgnat" && cur.mode !== m.id &&
}
{net && !net.public_ip &&
{t("fr.noInternet")}
}
)}
@@ -130,6 +160,7 @@ export function FriendsPage({ serverId }: { serverId: string }) {
{st.tailscale.connected && <>{t("fr.privateAddr")} {st.tailscale.ip}>}
)}
+ {m.id === "private" && st.tailscale.connected && st.tailscale.ip && cur.mode !== m.id && }