diff --git a/AppImage/components/onboarding-carousel.tsx b/AppImage/components/onboarding-carousel.tsx index 7c5cc465..4f115e58 100644 --- a/AppImage/components/onboarding-carousel.tsx +++ b/AppImage/components/onboarding-carousel.tsx @@ -96,6 +96,8 @@ const slides: OnboardingSlide[] = [ }, ] +export const ONBOARDING_CLOSED_EVENT = "proxmenux-onboarding-closed" + export function OnboardingCarousel() { const t = useT() const [open, setOpen] = useState(false) @@ -110,6 +112,14 @@ export function OnboardingCarousel() { } }, []) + // The release notes wait for the tour: two identical dialogs on top of each + // other made closing the first one look as if nothing happened. + const [wasOpen, setWasOpen] = useState(false) + useEffect(() => { + if (open) setWasOpen(true) + else if (wasOpen) window.dispatchEvent(new Event(ONBOARDING_CLOSED_EVENT)) + }, [open, wasOpen]) + const handleNext = () => { if (currentSlide < slides.length - 1) { setDirection("next") diff --git a/AppImage/components/proxmox-dashboard.tsx b/AppImage/components/proxmox-dashboard.tsx index d7ec7547..3d8c86df 100644 --- a/AppImage/components/proxmox-dashboard.tsx +++ b/AppImage/components/proxmox-dashboard.tsx @@ -489,7 +489,9 @@ function MonitorDashboard({ product }: { product: Product }) { )}
setShowHealthModal(true)} >
@@ -635,7 +637,7 @@ function MonitorDashboard({ product }: { product: Product }) {
(null) + // Chrome installs a web app only from a secure origin: over plain HTTP it + // can add a shortcut, never the app. Say so instead of steps that fail. + const [secure, setSecure] = useState(true) useEffect(() => { if (typeof window === "undefined") return @@ -80,8 +84,36 @@ export function PwaInstallPrompt() { // localStorage unavailable (private mode etc.) — treat as not dismissed. } + setSecure(window.isSecureContext) setPlatform(isIOS() ? "ios" : "android") - setOpen(true) + + // A dialog of the dashboard (welcome tour, release notes) blocks pointer + // events on everything outside it, so this prompt would show on top but + // could not be tapped. Wait until no dialog is open, and confirm it a + // moment later, since one dialog can open right after another closes. + const dialogOpen = () => document.querySelector('[role="dialog"][data-state="open"]') !== null + let timer: ReturnType | undefined + // Dialogs mount in portals directly under and announce their state + // in data-state; the rest of the page changing does not reset the wait. + const portals = new MutationObserver(() => schedule()) + const states = new MutationObserver(() => schedule()) + const stop = () => { + portals.disconnect() + states.disconnect() + clearTimeout(timer) + } + const schedule = () => { + clearTimeout(timer) + timer = setTimeout(() => { + if (dialogOpen()) return + stop() + setOpen(true) + }, 800) + } + portals.observe(document.body, { childList: true }) + states.observe(document.body, { subtree: true, attributes: true, attributeFilter: ["data-state"] }) + schedule() + return stop }, []) const handleNotNow = useCallback(() => { @@ -120,6 +152,8 @@ export function PwaInstallPrompt() { aria-modal="true" aria-labelledby="pwa-install-title" className="fixed inset-0 z-[100] flex items-end justify-center bg-black/60 backdrop-blur-sm animate-in fade-in duration-200" + // Stays usable even if another dialog opens and disables pointer events on the page. + style={{ pointerEvents: "auto" }} onClick={(e) => { if (e.target === e.currentTarget) handleClose() }} @@ -167,7 +201,9 @@ export function PwaInstallPrompt() {

{platform === "ios" ? t("pwaInstall.iosDescription") - : t("pwaInstall.androidDescription")} + : secure + ? t("pwaInstall.androidDescription") + : t("pwaInstall.insecure.description")}

@@ -208,12 +244,30 @@ export function PwaInstallPrompt() { - ) : ( + ) : secure ? (
{t("pwaInstall.android.stepOpenMenu")} ⋮ →{" "} + {t("pwaInstall.installApp")} {t("pwaInstall.android.or")}{" "} {t("pwaInstall.addToHomeScreen")} → {t("pwaInstall.android.stepConfirm")}{" "} {t("pwaInstall.android.install")}.
+ ) : ( +
+

{t("pwaInstall.insecure.howTo")}

+ + {t("pwaInstall.insecure.docs")} + +

+ {t("pwaInstall.insecure.shortcut")} ⋮ →{" "} + {t("pwaInstall.addToHomeScreen")}. +

+
)}
diff --git a/AppImage/components/release-notes-modal.tsx b/AppImage/components/release-notes-modal.tsx index 89355910..c0e60747 100644 --- a/AppImage/components/release-notes-modal.tsx +++ b/AppImage/components/release-notes-modal.tsx @@ -7,6 +7,7 @@ import { X, Sparkles, Thermometer, Activity, HardDrive, Shield, Globe, Cpu, Zap, import { Checkbox } from "./ui/checkbox" import { useT } from "../lib/i18n/provider" import { APP_VERSION } from "../lib/version" +import { ONBOARDING_CLOSED_EVENT } from "./onboarding-carousel" interface ReleaseNote { date: string @@ -533,11 +534,15 @@ export function useVersionCheck() { const [showReleaseNotes, setShowReleaseNotes] = useState(false) useEffect(() => { - const lastSeenVersion = localStorage.getItem("proxmenux-last-seen-version") - - if (lastSeenVersion !== APP_VERSION) { - setShowReleaseNotes(true) + if (localStorage.getItem("proxmenux-last-seen-version") === APP_VERSION) return + const show = () => setShowReleaseNotes(true) + if (localStorage.getItem("proxmenux-onboarding-seen")) { + show() + return } + // The welcome tour opens first; the notes follow once it is closed. + window.addEventListener(ONBOARDING_CLOSED_EVENT, show, { once: true }) + return () => window.removeEventListener(ONBOARDING_CLOSED_EVENT, show) }, []) return { showReleaseNotes, setShowReleaseNotes } diff --git a/AppImage/components/terminal-panel.tsx b/AppImage/components/terminal-panel.tsx index d65ac01c..148cc287 100644 --- a/AppImage/components/terminal-panel.tsx +++ b/AppImage/components/terminal-panel.tsx @@ -19,6 +19,7 @@ import { ChevronDown, Copy, Clipboard, + RotateCw, } from "lucide-react" import { copyTerminalSelection, pasteFromClipboard } from "@/lib/terminal-clipboard" import { @@ -97,6 +98,10 @@ const proxmoxCommands = [ "scp file user@host:/path", "tail -f /var/log/syslog", "history", "clear", ] +const ROTATE_HINT_KEY = "proxmenux-terminal-rotate-hint-dismissed" +// Toolbar above the terminal and key bar below it, in CSS pixels. +const LANDSCAPE_CHROME_PX = 120 + export const TerminalPanel: React.FC = ({ websocketUrl, onClose }) => { const t = useT() const localizedCommands = proxmoxCommands.map((cmd, index) => ({ @@ -109,6 +114,17 @@ export const TerminalPanel: React.FC = ({ websocketUrl, onCl const [isMobile, setIsMobile] = useState(false) const [isTablet, setIsTablet] = useState(false) const [terminalHeight, setTerminalHeight] = useState(500) // altura por defecto en px + const [viewportHeight, setViewportHeight] = useState(0) + const [portrait, setPortrait] = useState(false) + // ProxMenux menus need about 80 columns; a phone held upright gives ~50. + const [rotateHintHidden, setRotateHintHidden] = useState(true) + const panelRef = useRef(null) + // A phone turned sideways uses the tablet layout with little height: keep the + // terminal, its toolbar and its key bar inside the screen. + const fittedHeight = isTablet && viewportHeight + ? Math.min(terminalHeight, Math.max(200, viewportHeight - LANDSCAPE_CHROME_PX)) + : terminalHeight + const [searchModalOpen, setSearchModalOpen] = useState(false) const [searchQuery, setSearchQuery] = useState("") const [filteredCommands, setFilteredCommands] = useState>(localizedCommands) @@ -129,6 +145,8 @@ export const TerminalPanel: React.FC = ({ websocketUrl, onCl setIsMobile(width < 768) setIsTablet(isTouchDevice && isTabletSize) + setViewportHeight(window.innerHeight) + setPortrait(window.innerHeight > width) } updateDeviceType() @@ -139,6 +157,11 @@ export const TerminalPanel: React.FC = ({ websocketUrl, onCl if (savedHeight) { setTerminalHeight(Number.parseInt(savedHeight, 10)) } + try { + setRotateHintHidden(localStorage.getItem(ROTATE_HINT_KEY) === "1") + } catch { + setRotateHintHidden(false) + } return () => { window.removeEventListener("resize", handleResize) @@ -417,12 +440,25 @@ export const TerminalPanel: React.FC = ({ websocketUrl, onCl const container = containerRefs.current[terminal.id] if (!terminal.term && container) { initializeTerminal(terminal, container) + } else if (terminal.term?.element && container && terminal.term.element.parentElement !== container) { + // Turning the device switches between the phone and tablet layouts, + // which render new containers: move the open terminal into its new + // place instead of leaving it in the one React removed. + container.appendChild(terminal.term.element) + try { + terminal.fitAddon?.fit() + if (terminal.ws?.readyState === WebSocket.OPEN) { + terminal.ws.send(JSON.stringify({ type: "resize", cols: terminal.term.cols, rows: terminal.term.rows })) + } + } catch (err) { + console.warn("[Terminal] resize after layout change failed:", err) + } } }) }, 50) return () => clearTimeout(timer) - }, [terminals, isMobile]) + }, [terminals, isMobile, isTablet]) useEffect(() => { if (window.innerWidth < 640) return @@ -449,7 +485,15 @@ export const TerminalPanel: React.FC = ({ websocketUrl, onCl } } }) - }, [terminalHeight, layout, terminals, isMobile]) + }, [terminalHeight, fittedHeight, layout, terminals, isMobile]) + + useEffect(() => { + // Turned sideways, the dashboard header fills most of a phone screen: + // scroll so the whole terminal is in view. + if (isTablet && !portrait && viewportHeight && viewportHeight < terminalHeight + LANDSCAPE_CHROME_PX) { + panelRef.current?.scrollIntoView({ block: "start", behavior: "smooth" }) + } + }, [isTablet, portrait]) const initializeTerminal = async (terminal: TerminalInstance, container: HTMLDivElement) => { const [TerminalClass, FitAddonClass] = await Promise.all([ @@ -766,8 +810,17 @@ const handleClose = () => { const activeTerminal = terminals.find((t) => t.id === activeTerminalId) + const dismissRotateHint = () => { + setRotateHintHidden(true) + try { + localStorage.setItem(ROTATE_HINT_KEY, "1") + } catch { + // Best effort: the hint shows again on the next visit. + } + } + return ( -
+
@@ -843,13 +896,30 @@ const handleClose = () => {
+ {isMobile && portrait && !rotateHintHidden && ( +
+
+ )} +
{ containerRefs.current["main"] = el }} - className={`overflow-hidden flex flex-col ${isMobile ? "flex-1 h-[60vh]" : "overflow-hidden"} w-full max-w-full`} - style={!isMobile || isTablet ? { height: `${terminalHeight}px`, flexShrink: 0 } : undefined} + // In a column flex parent, flex-1 (basis 0) would override the height + // and the terminal would shrink to xterm's default 24 rows. + className={`overflow-hidden flex flex-col ${isMobile ? `${portrait && !rotateHintHidden ? "h-[calc(60dvh-36px)]" : "h-[60dvh]"} shrink-0` : "overflow-hidden"} w-full max-w-full`} + style={!isMobile || isTablet ? { height: `${fittedHeight}px`, flexShrink: 0 } : undefined} > {isMobile ? ( @@ -876,7 +946,8 @@ const handleClose = () => { key={terminal.id} value={terminal.id} forceMount - className={`flex-1 h-full mt-0 ${activeTerminalId === terminal.id ? "block" : "hidden"}`} + // Below the tab list: h-full would take the whole container and push the tabs out. + className={`flex-1 min-h-0 mt-0 ${activeTerminalId === terminal.id ? "block" : "hidden"}`} >
(containerRefs.current[terminal.id] = el)} diff --git a/AppImage/messages/de/common.json b/AppImage/messages/de/common.json index 97cdced1..a538e80f 100644 --- a/AppImage/messages/de/common.json +++ b/AppImage/messages/de/common.json @@ -5516,6 +5516,7 @@ "iosDescription": "Fügen Sie den Monitor für einen schnellen Zugriff zu Ihrem Startbildschirm hinzu.", "androidDescription": "Fügen Sie den Monitor als App hinzu, damit er wie eine normale Anwendung geöffnet wird.", "addToHomeScreen": "Zum Startbildschirm hinzufügen", + "installApp": "App installieren", "ios": { "stepShareBefore": "Tippen Sie auf", "share": "Aktie", @@ -5528,10 +5529,17 @@ "android": { "stepOpenMenu": "Öffnen Sie das Browsermenü", "stepConfirm": "durch Antippen bestätigen", - "install": "Installieren" + "install": "Installieren", + "or": "oder" }, "notNow": "Nicht jetzt", - "neverAgain": "Nicht mehr anzeigen" + "neverAgain": "Nicht mehr anzeigen", + "insecure": { + "description": "Über unverschlüsseltes HTTP kann Chrome nur eine Verknüpfung auf dem Startbildschirm hinzufügen.", + "howTo": "Um den Monitor als App zu installieren, öffnen Sie ihn über HTTPS mit einem gültigen Zertifikat: ein ACME-Zertifikat von Proxmox unter Admin → Sicherheit, ein Reverse-Proxy oder Tailscale.", + "docs": "HTTPS einrichten", + "shortcut": "Bis dahin können Sie eine Verknüpfung hinzufügen:" + } }, "vmMetrics": { "title": "Metriken – {name}", @@ -5681,6 +5689,7 @@ } }, "terminal": { + "rotateHint": "Drehen Sie das Telefon quer, um die ProxMenux-Menüs vollständig zu sehen.", "connected": "Verbunden", "disconnected": "Getrennt", "terminalCount": "{count} / 4 Terminals", diff --git a/AppImage/messages/en/common.json b/AppImage/messages/en/common.json index 8858146f..c9e342ab 100644 --- a/AppImage/messages/en/common.json +++ b/AppImage/messages/en/common.json @@ -5643,6 +5643,7 @@ "iosDescription": "Add the Monitor to your home screen for quick access.", "androidDescription": "Add the Monitor as an app so it opens like a normal application.", "addToHomeScreen": "Add to Home Screen", + "installApp": "Install app", "ios": { "stepShareBefore": "Tap the", "share": "Share", @@ -5655,10 +5656,17 @@ "android": { "stepOpenMenu": "Open the browser menu", "stepConfirm": "confirm by tapping", - "install": "Install" + "install": "Install", + "or": "or" }, "notNow": "Not now", - "neverAgain": "Don’t show again" + "neverAgain": "Don’t show again", + "insecure": { + "description": "Over plain HTTP, Chrome can only add a shortcut to the home screen.", + "howTo": "To install the Monitor as an app, open it over HTTPS with a valid certificate: a Proxmox ACME certificate in Admin → Security, a reverse proxy, or Tailscale.", + "docs": "How to set up HTTPS", + "shortcut": "Meanwhile, you can add a shortcut:" + } }, "vmMetrics": { "title": "Metrics — {name}", @@ -5808,6 +5816,7 @@ } }, "terminal": { + "rotateHint": "Turn your phone sideways to see the ProxMenux menus in full.", "connected": "Connected", "disconnected": "Disconnected", "terminalCount": "{count} / 4 terminals", diff --git a/AppImage/messages/es/common.json b/AppImage/messages/es/common.json index acdec7dc..cf1140a0 100644 --- a/AppImage/messages/es/common.json +++ b/AppImage/messages/es/common.json @@ -5669,6 +5669,7 @@ "iosDescription": "Añade el monitor a tu pantalla de inicio para un acceso rápido.", "androidDescription": "Añade el Monitor como una aplicación para que se abra como una aplicación normal.", "addToHomeScreen": "Añadir a la pantalla de inicio", + "installApp": "Instalar aplicación", "ios": { "stepShareBefore": "Toca el", "share": "Compartir", @@ -5681,10 +5682,17 @@ "android": { "stepOpenMenu": "Abre el menú del navegador", "stepConfirm": "confirmar tocando", - "install": "Instalar" + "install": "Instalar", + "or": "o" }, "notNow": "Ahora no", - "neverAgain": "No volver a mostrar" + "neverAgain": "No volver a mostrar", + "insecure": { + "description": "Por HTTP sin cifrar, Chrome solo puede añadir un acceso directo a la pantalla de inicio.", + "howTo": "Para instalar el Monitor como aplicación, ábrelo por HTTPS con un certificado válido: un certificado ACME de Proxmox en Administración → Seguridad, un proxy inverso o Tailscale.", + "docs": "Cómo configurar HTTPS", + "shortcut": "Mientras tanto, puedes añadir un acceso directo:" + } }, "vmMetrics": { "title": "Métricas: {name}", @@ -5834,6 +5842,7 @@ } }, "terminal": { + "rotateHint": "Gira el móvil para ver completos los menús de ProxMenux.", "connected": "Conectado", "disconnected": "Desconectado", "terminalCount": "{count} / 4 terminales", diff --git a/AppImage/messages/fr/common.json b/AppImage/messages/fr/common.json index b037dbe1..24e6186f 100644 --- a/AppImage/messages/fr/common.json +++ b/AppImage/messages/fr/common.json @@ -5516,6 +5516,7 @@ "iosDescription": "Ajoutez le moniteur à votre écran d'accueil pour un accès rapide.", "androidDescription": "Ajoutez le moniteur en tant qu'application pour qu'il s'ouvre comme une application normale.", "addToHomeScreen": "Ajouter à l'écran d'accueil", + "installApp": "Installer l'application", "ios": { "stepShareBefore": "Appuyez sur le", "share": "Partager", @@ -5528,10 +5529,17 @@ "android": { "stepOpenMenu": "Ouvrez le menu du navigateur", "stepConfirm": "confirmer en appuyant sur", - "install": "Installer" + "install": "Installer", + "or": "ou" }, "notNow": "Pas maintenant", - "neverAgain": "Ne plus montrer" + "neverAgain": "Ne plus montrer", + "insecure": { + "description": "En HTTP non chiffré, Chrome peut seulement ajouter un raccourci à l'écran d'accueil.", + "howTo": "Pour installer le moniteur comme application, ouvrez-le en HTTPS avec un certificat valide : un certificat ACME de Proxmox dans Administrateur → Sécurité, un proxy inverse ou Tailscale.", + "docs": "Configurer HTTPS", + "shortcut": "En attendant, vous pouvez ajouter un raccourci :" + } }, "vmMetrics": { "title": "Métriques — {name}", @@ -5681,6 +5689,7 @@ } }, "terminal": { + "rotateHint": "Tournez le téléphone à l'horizontale pour voir les menus de ProxMenux en entier.", "connected": "Connecté", "disconnected": "Déconnecté", "terminalCount": "{count} / 4 bornes", diff --git a/AppImage/messages/it/common.json b/AppImage/messages/it/common.json index d4bb755b..0d08798a 100644 --- a/AppImage/messages/it/common.json +++ b/AppImage/messages/it/common.json @@ -5516,6 +5516,7 @@ "iosDescription": "Aggiungi il Monitor alla schermata iniziale per un accesso rapido.", "androidDescription": "Aggiungi il Monitor come app in modo che si apra come una normale applicazione.", "addToHomeScreen": "Aggiungi alla schermata Home", + "installApp": "Installa app", "ios": { "stepShareBefore": "Tocca il pulsante", "share": "Condividi", @@ -5528,10 +5529,17 @@ "android": { "stepOpenMenu": "Apri il menu del browser", "stepConfirm": "conferma toccando", - "install": "Installa" + "install": "Installa", + "or": "oppure" }, "notNow": "Non adesso", - "neverAgain": "Non mostrare più" + "neverAgain": "Non mostrare più", + "insecure": { + "description": "Tramite HTTP non cifrato, Chrome può solo aggiungere una scorciatoia alla schermata Home.", + "howTo": "Per installare il Monitor come app, aprilo tramite HTTPS con un certificato valido: un certificato ACME di Proxmox in Amministratore → Sicurezza, un reverse proxy o Tailscale.", + "docs": "Come configurare HTTPS", + "shortcut": "Nel frattempo, puoi aggiungere una scorciatoia:" + } }, "vmMetrics": { "title": "Metriche — {name}", @@ -5681,6 +5689,7 @@ } }, "terminal": { + "rotateHint": "Ruota il telefono in orizzontale per vedere per intero i menu di ProxMenux.", "connected": "Collegato", "disconnected": "Disconnesso", "terminalCount": "{count} / 4 terminali", diff --git a/AppImage/messages/pt/common.json b/AppImage/messages/pt/common.json index fc952055..15003692 100644 --- a/AppImage/messages/pt/common.json +++ b/AppImage/messages/pt/common.json @@ -5516,6 +5516,7 @@ "iosDescription": "Adicione o monitor à sua tela inicial para acesso rápido.", "androidDescription": "Adicione o Monitor como um aplicativo para que ele abra como um aplicativo normal.", "addToHomeScreen": "Adicionar à tela inicial", + "installApp": "Instalar app", "ios": { "stepShareBefore": "Toque no", "share": "Compartilhar", @@ -5528,10 +5529,17 @@ "android": { "stepOpenMenu": "Abra o menu do navegador", "stepConfirm": "confirme tocando", - "install": "Instalar" + "install": "Instalar", + "or": "ou" }, "notNow": "Agora não", - "neverAgain": "Não mostre novamente" + "neverAgain": "Não mostre novamente", + "insecure": { + "description": "Por HTTP sem criptografia, o Chrome só pode adicionar um atalho à tela inicial.", + "howTo": "Para instalar o Monitor como aplicativo, abra-o por HTTPS com um certificado válido: um certificado ACME do Proxmox em Administrador → Segurança, um proxy reverso ou o Tailscale.", + "docs": "Como configurar o HTTPS", + "shortcut": "Enquanto isso, você pode adicionar um atalho:" + } }, "vmMetrics": { "title": "Métricas — {name}", @@ -5681,6 +5689,7 @@ } }, "terminal": { + "rotateHint": "Gire o celular na horizontal para ver os menus do ProxMenux por completo.", "connected": "Conectado", "disconnected": "Desconectado", "terminalCount": "{count} / 4 terminais", diff --git a/AppImage/messages/sk/common.json b/AppImage/messages/sk/common.json index db089903..3522d998 100644 --- a/AppImage/messages/sk/common.json +++ b/AppImage/messages/sk/common.json @@ -5515,6 +5515,7 @@ "iosDescription": "Pridajte Monitor na plochu, aby bol rýchlo poruke.", "androidDescription": "Pridajte Monitor ako aplikáciu, aby sa otváral ako bežná appka.", "addToHomeScreen": "Pridať na plochu", + "installApp": "Inštalovať aplikáciu", "ios": { "stepShareBefore": "Ťuknite na", "share": "Zdieľať", @@ -5527,10 +5528,17 @@ "android": { "stepOpenMenu": "Otvorte menu prehliadača", "stepConfirm": "potom potvrďte ťuknutím na", - "install": "Inštalovať" + "install": "Inštalovať", + "or": "alebo" }, "notNow": "Teraz nie", - "neverAgain": "Už nezobrazovať" + "neverAgain": "Už nezobrazovať", + "insecure": { + "description": "Cez nešifrované HTTP môže Chrome pridať na plochu iba odkaz.", + "howTo": "Ak chcete Monitor nainštalovať ako aplikáciu, otvorte ho cez HTTPS s platným certifikátom: certifikát ACME z Proxmoxu v Správa → Bezpečnosť, reverzný proxy alebo Tailscale.", + "docs": "Ako nastaviť HTTPS", + "shortcut": "Zatiaľ môžete pridať odkaz:" + } }, "vmMetrics": { "title": "Metriky — {name}", @@ -5680,6 +5688,7 @@ } }, "terminal": { + "rotateHint": "Otočte telefón na šírku, aby ste videli celé ponuky ProxMenux.", "connected": "Pripojené", "disconnected": "Odpojené", "terminalCount": "{count} zo 4 terminálov", diff --git a/AppImage/messages/sv/common.json b/AppImage/messages/sv/common.json index ae9c8557..e8e74e1b 100644 --- a/AppImage/messages/sv/common.json +++ b/AppImage/messages/sv/common.json @@ -5516,6 +5516,7 @@ "iosDescription": "Lägg till monitorn på din startskärm för snabb åtkomst.", "androidDescription": "Lägg till Monitor som en app så att den öppnas som en vanlig applikation.", "addToHomeScreen": "Lägg till på startskärmen", + "installApp": "Installera app", "ios": { "stepShareBefore": "Tryck på", "share": "Dela", @@ -5528,10 +5529,17 @@ "android": { "stepOpenMenu": "Öppna webbläsarmenyn", "stepConfirm": "bekräfta genom att trycka på", - "install": "Installera" + "install": "Installera", + "or": "eller" }, "notNow": "Inte nu", - "neverAgain": "Visa inte igen" + "neverAgain": "Visa inte igen", + "insecure": { + "description": "Över okrypterad HTTP kan Chrome bara lägga till en genväg på startskärmen.", + "howTo": "För att installera Monitor som en app, öppna den över HTTPS med ett giltigt certifikat: ett ACME-certifikat från Proxmox under Administration → Säkerhet, en omvänd proxy eller Tailscale.", + "docs": "Så konfigurerar du HTTPS", + "shortcut": "Under tiden kan du lägga till en genväg:" + } }, "vmMetrics": { "title": "Mätvärden — {name}", @@ -5681,6 +5689,7 @@ } }, "terminal": { + "rotateHint": "Vrid telefonen till liggande läge för att se ProxMenux-menyerna i sin helhet.", "connected": "Ansluten", "disconnected": "Frånkopplad", "terminalCount": "{count} / 4 terminaler", diff --git a/web/app/[locale]/docs/monitor/access-auth/page.tsx b/web/app/[locale]/docs/monitor/access-auth/page.tsx index 427bd543..1c039072 100644 --- a/web/app/[locale]/docs/monitor/access-auth/page.tsx +++ b/web/app/[locale]/docs/monitor/access-auth/page.tsx @@ -545,7 +545,7 @@ curl -X DELETE http://:8008/api/auth/api-tokens/ \\ {t.rich("apiTokens.outro", { apiLink, intLink })}

-

{t("https.heading")}

+

{t("https.heading")}

{t("https.intro")}

    {httpsItems.map((_, idx) => (