mirror of
https://github.com/Corfirean/coa-server-manager.git
synced 2026-11-12 20:11:02 +00:00
v0.1.3: interface scales with the window, realmlist line per Friends mode, robust close handler
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0e1016e93c
commit
4eda03ab63
12 files changed
+80
-23
No files matched your search
Generated
+3
-3
@@ -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",
|
||||
|
||||
+1
-1
@@ -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"
|
||||
|
||||
|
||||
+1
-1
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -430,6 +430,9 @@ const de: Partial<Record<keyof typeof en, string>> = {
|
||||
"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;
|
||||
@@ -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;
|
||||
@@ -430,6 +430,9 @@ const es: Partial<Record<keyof typeof en, string>> = {
|
||||
"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;
|
||||
@@ -430,6 +430,9 @@ const fr: Partial<Record<keyof typeof en, string>> = {
|
||||
"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;
|
||||
@@ -430,6 +430,9 @@ const ru: Partial<Record<keyof typeof en, string>> = {
|
||||
"selfupd.now": "Установить сейчас",
|
||||
"about.auto": "Скачивать обновления в фоне и устанавливать их, когда я закрою менеджер",
|
||||
"about.autoHint": "Обновление менеджера никогда не останавливает и не меняет ваши серверы.",
|
||||
|
||||
// Friends line
|
||||
"fr.copyLine": "Скопировать строку",
|
||||
};
|
||||
|
||||
export default ru;
|
||||
@@ -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 {
|
||||
|
||||
+21
-16
@@ -103,22 +103,27 @@ export async function installNow(): Promise<void> {
|
||||
/** 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?.();
|
||||
}
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<div className="mt-2 flex flex-wrap items-center gap-2">
|
||||
<code className="selectable rounded bg-black/40 px-2 py-1 text-sm">{line}</code>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() =>
|
||||
void navigator.clipboard.writeText(line).then(() => {
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
})
|
||||
}
|
||||
>
|
||||
{copied ? t("fr.copied") : t("fr.copyLine")}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function FriendsPage({ serverId }: { serverId: string }) {
|
||||
const t = useT();
|
||||
const human = useHuman();
|
||||
@@ -103,7 +127,12 @@ export function FriendsPage({ serverId }: { serverId: string }) {
|
||||
</h3>
|
||||
<p className="mt-0.5 text-sm text-muted">{t(m.text)}</p>
|
||||
|
||||
{m.id === "lan" && <p className="mt-2 text-sm">{t("fr.yourLan")} <b className="selectable">{st.lan_ip ?? t("fr.unknown")}</b></p>}
|
||||
{m.id === "lan" && (
|
||||
<>
|
||||
<p className="mt-2 text-sm">{t("fr.yourLan")} <b className="selectable">{st.lan_ip ?? t("fr.unknown")}</b></p>
|
||||
{st.lan_ip && cur.mode !== m.id && <RealmlistLine host={st.lan_ip} />}
|
||||
</>
|
||||
)}
|
||||
|
||||
{m.id === "direct" && (
|
||||
<div className="mt-2 text-sm">
|
||||
@@ -119,6 +148,7 @@ export function FriendsPage({ serverId }: { serverId: string }) {
|
||||
{net?.public_ip && net.reachability !== "cgnat" && (
|
||||
<p className="mt-2">{t("fr.publicAddr")} <b className="selectable">{net.public_ip}</b>{!net.router_found && <span className="text-muted">{t("fr.noRouter")}</span>}</p>
|
||||
)}
|
||||
{net?.public_ip && net.reachability !== "cgnat" && cur.mode !== m.id && <RealmlistLine host={net.public_ip} />}
|
||||
{net && !net.public_ip && <p className="mt-2 text-warn">{t("fr.noInternet")}</p>}
|
||||
</div>
|
||||
)}
|
||||
@@ -130,6 +160,7 @@ export function FriendsPage({ serverId }: { serverId: string }) {
|
||||
{st.tailscale.connected && <>{t("fr.privateAddr")} <b className="selectable">{st.tailscale.ip}</b></>}
|
||||
</p>
|
||||
)}
|
||||
{m.id === "private" && st.tailscale.connected && st.tailscale.ip && cur.mode !== m.id && <RealmlistLine host={st.tailscale.ip} />}
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
|
||||
Reference in new issue
Block a user