diff --git a/e2e/coach-layout.spec.ts b/e2e/coach-layout.spec.ts index ae6bfbaec..b4cdc2871 100644 --- a/e2e/coach-layout.spec.ts +++ b/e2e/coach-layout.spec.ts @@ -807,12 +807,14 @@ test.describe("Coach page frame", () => { const popover = page.locator('[data-slot="coach-settings-popover"]'); await expect(popover).toBeVisible(); await expect(popover).toHaveAttribute("role", "dialog"); + // The model is chosen in Settings > AI; the overlay shows what is in + // effect and links there. await expect( popover.locator('[data-slot="coach-quick-provider"]'), - ).toHaveValue("local"); + ).toHaveAttribute("data-value", "local"); await expect( popover.locator('[data-slot="coach-quick-model"]'), - ).toHaveValue("qwen2.5"); + ).toHaveAttribute("data-value", "qwen2.5"); await expect( popover.locator('[data-slot="coach-sources-window"]'), ).toBeVisible(); diff --git a/messages/de.json b/messages/de.json index b8afe311c..8b03cdf9b 100644 --- a/messages/de.json +++ b/messages/de.json @@ -3264,12 +3264,9 @@ "provider": "Antwortet mit", "modelManaged": "Das Modell für diesen Anbieter legst du in den KI-Einstellungen fest.", "noProvider": "Kein Anbieter verfügbar", - "saving": "Wird gespeichert…", - "saveFailed": "Die Änderung ließ sich nicht speichern.", "allSettings": "Alle KI-Einstellungen", - "providerNotActive": "{provider} konnte nicht übernehmen: Es fehlt eine funktionierende Anmeldung oder ein Schlüssel. Richte das in den KI-Einstellungen ein.", "operatorCodex": "ChatGPT (vom Betreiber bereitgestellt)", - "operatorManaged": "Diese Verbindung verwaltet der Betreiber deines Servers. Sobald einer deiner eigenen Anbieter eingerichtet ist, antwortet er zuerst." + "changeModel": "In Einstellungen > KI ändern" }, "answer": { "scrollToBottom": "Zur neuesten Nachricht", @@ -5289,12 +5286,17 @@ "groups": { "account": "Konto", "yourData": "Deine Daten", - "display": "Anzeige", "healthProfile": "Gesundheitsprofil", "ai": "KI", "connectivity": "Konnektivität", - "system": "System" - } + "system": "System", + "tracking": "Tracking", + "arrangement": "Anordnung", + "notifications": "Benachrichtigungen", + "connections": "Verbindungen", + "data": "Daten" + }, + "allSections": "Alle Bereiche" }, "sharedRecord": { "managedSettings": { @@ -5428,11 +5430,25 @@ "remindersHeading": "Erinnerungen", "matrixLink": "Benachrichtigungen je Kanal", "channelsLink": "Zustellkanäle", - "displayHeading": "Auf dem Handy" + "displayHeading": "Auf dem Handy", + "elsewhere": { + "title": "Dort eingestellt, wo sie hingehören", + "description": "Diese Erinnerungen stellst du direkt bei dem ein, worum es geht.", + "doses": { + "title": "Medikamenten-Einnahmen", + "description": "Einnahmezeiten und Erinnerungen stellst du beim jeweiligen Medikament ein.", + "link": "Medikamente öffnen" + }, + "cycle": { + "title": "Zyklus", + "description": "Perioden- und Fruchtbarkeits-Erinnerungen schaltest du je Kanal in der Benachrichtigungsübersicht.", + "link": "Benachrichtigungsübersicht öffnen" + } + } }, "layout": { - "title": "Layout", - "backToHub": "Zurück zu Layout", + "title": "Anordnung", + "backToHub": "Zurück zu Anordnung", "dashboard": { "title": "Dashboard", "description": "Kachel-Layout und Reihenfolge." @@ -5441,7 +5457,7 @@ "title": "Insights", "description": "Reihenfolge der Übersicht und der Navigations-Pills." }, - "subtitle": "Lege fest, was Dashboard, Auswertungen und Tracking-Module zeigen und in welcher Reihenfolge.", + "subtitle": "Lege Reihenfolge und Ansicht von Dashboard, Insights und Tracking-Listen fest.", "medications": { "title": "Medikamente", "description": "Listenansicht und manuelle Reihenfolge." @@ -5458,10 +5474,6 @@ "title": "Vorsorge", "description": "Listenansicht und Reihenfolge der Erinnerungen." }, - "mood": { - "title": "Stimmungs-Tags", - "description": "Gruppen, Tags und archivierte Tags der Stimmungs-Auswahl." - }, "documents": { "title": "Dokumente", "description": "Ansicht und Anordnung der Monate." @@ -5852,7 +5864,28 @@ }, "manageIn": "In {section} verwalten", "operatorDisabled": "Serverweit deaktiviert", - "subtitle": "Lege fest, welche Tracking-Bereiche HealthLog überall anzeigt." + "subtitle": "Lege fest, welche Tracking-Bereiche HealthLog überall anzeigt.", + "backToHub": "Zurück zu Module", + "openModuleSettings": "Moduleinstellungen", + "pages": { + "medications": { + "title": "Medikamente", + "description": "Injektionsstellen, die die Rotation auslässt." + }, + "mood": { + "title": "Stimmungs-Tags", + "description": "Gruppen, eigene Tags und archivierte Tags." + }, + "labs": { + "title": "Labor", + "description": "Biomarker und Texterkennung auf dem Gerät für Laborberichte." + }, + "arrangementTitle": "Reihenfolge und Ansicht", + "arrangementDescription": "Wie diese Liste sortiert und angezeigt wird, legst du unter Anordnung fest.", + "openArrangement": "In Anordnung öffnen", + "moduleSettingsTitle": "Moduleinstellungen", + "moduleSettingsDescription": "Alles außer Reihenfolge und Ansicht findest du auf der Seite des Moduls unter Module." + } }, "gesundheitsakte": { "title": "Gesundheitsakte", @@ -5995,6 +6028,10 @@ "access": { "title": "Geteilter Zugriff", "subtitle": "Wer deine Gesundheitsakte öffnen kann und wessen Akte du öffnen kannst." + }, + "preview": { + "title": "Vorschau-Funktionen", + "subtitle": "Probier aus, was noch nicht fertig ist. Alles hier bleibt aus, bis du es einschaltest." } }, "profile": "Profil", @@ -7006,21 +7043,19 @@ }, "integrationSetupGuide": "Einrichtungsanleitung", "coach": { - "activate": { - "title": "Coach aktivieren", - "description": "Zeigt den Coach-Button und das Coach-Panel.", - "toggleAria": "Coach-Button und Coach-Panel aktivieren", - "savedOn": "Coach ist aktiviert.", - "savedOff": "Coach ist ausgeblendet.", - "saveError": "Speichern fehlgeschlagen. Bitte erneut versuchen." - }, "memory": { "edit": "Bearbeiten", "source": { "user": "von dir", "coach": "vom Coach" } - } + }, + "state": { + "title": "Coach", + "description": "Ob der Coach-Button und das Coach-Panel erscheinen. Der Schalter liegt unter Module." + }, + "nudgesMoved": "Stellst du mit deinen anderen Erinnerungen unter Benachrichtigungen ein.", + "openNudges": "In Benachrichtigungen öffnen" }, "integrationRedirectGuide": { "intro": "Die Verbindung läuft über deine eigene {provider}-Entwickler-App. Die Einrichtung dauert etwa 5 Minuten:", @@ -7163,12 +7198,6 @@ "delivery": "Benachrichtigungsprotokoll: {days} Tage", "unavailable": "Aufbewahrungsdetails sind derzeit nicht verfügbar." }, - "export": { - "title": "Meine Daten herunterladen", - "description": "Deine Datensätze als CSV oder JSON oder eine vollständige Akte für die Praxis.", - "openExport": "Export & Backup", - "openHealthRecord": "Gesundheitsakte" - }, "delete": { "title": "Löschen oder zurücksetzen", "description": "Lösche dein Konto vollständig oder lösche deine Gesundheitsdaten und behalte das Konto.", @@ -7184,7 +7213,6 @@ "mcp": { "enableTitle": "Remote-MCP-Endpunkt", "enableDescription": "Erlaube einem MCP-fähigen Assistenten (Claude.ai, ChatGPT), deine Gesundheitsdaten über ein begrenztes Token zu lesen.", - "enableToggleLabel": "MCP-Endpunkt aktivieren", "endpointLabel": "Connector-URL", "connectDescription": "Füge diese URL in die Einrichtung des eigenen Connectors deines Assistenten ein und autorisiere dich mit dem Token unten oder melde dich bei Aufforderung an.", "tokensTitle": "Connector-Tokens", @@ -7203,7 +7231,8 @@ "connectionRevokeFailed": "Assistent konnte nicht getrennt werden.", "tokensDetail": "Das Token wird nur einmal angezeigt.", "connectionsDetail": "Das Widerrufen einer Verbindung beendet den Zugriff sofort, einschließlich der Möglichkeit zur Erneuerung.", - "tokenNamePlaceholder": "Name, z. B. Claude Desktop" + "tokenNamePlaceholder": "Name, z. B. Claude Desktop", + "offKeepsTokens": "Ausschalten pausiert den Endpunkt. Deine Tokens und Verbindungen bleiben und funktionieren wieder, sobald er an ist." }, "strava": "Strava", "stravaDescription": "Verbinde dein Strava-Konto, um deine Aktivitäten in deinen Trainingsverlauf zu übernehmen.", @@ -7465,7 +7494,25 @@ "passkeyAddedToast": "Passkey hinzugefügt.", "passkeyRemovedToast": "Passkey entfernt.", "integrationDisconnected": "{provider} getrennt.", - "integrationDisconnectFailed": "{provider} konnte nicht getrennt werden." + "integrationDisconnectFailed": "{provider} konnte nicht getrennt werden.", + "preview": { + "topics": { + "title": "Themen (Beta)", + "description": "Fügt einen eigenen Eintrag Themen hinzu, mit Übersichtsseiten über den einzelnen Insights-Seiten.", + "detail": "Deine Insights-Seiten, die Navigation und alle Links bleiben genau wie sie sind. Die Themenseiten kommen mit einem späteren Update; dieser Schalter legt fest, ob du sie siehst.", + "savedOn": "Themen-Vorschau eingeschaltet.", + "savedOff": "Themen-Vorschau ausgeschaltet." + }, + "loadError": "Deine Vorschau-Einstellungen konnten nicht geladen werden.", + "feedbackLink": "Feedback in den GitHub-Diskussionen geben" + }, + "timezoneSaved": "Zeitzone gespeichert.", + "moduleState": { + "on": "An", + "off": "Aus", + "change": "In Module ändern" + }, + "timezoneUseDevice": "Zeitzone dieses Geräts übernehmen ({zone})" }, "admin": { "title": "Administration", @@ -8855,6 +8902,10 @@ "detailedToast": "Benachrichtigungen zeigen jetzt Gesundheitswerte.", "genericToast": "Benachrichtigungen zeigen keine Gesundheitswerte mehr.", "saveError": "Speichern fehlgeschlagen. Bitte später erneut versuchen." + }, + "checkupReminders": { + "title": "Vorsorge-Erinnerungen", + "description": "Schalte einzelne Vorsorge-Erinnerungen ein oder aus." } }, "i18n": { @@ -9631,8 +9682,6 @@ "description": "Wert eintragen; die Erinnerung wird als erledigt markiert." }, "manage": { - "title": "Erinnerungen verwalten", - "description": "Einzelne Erinnerungen aktivieren oder deaktivieren.", "empty": "Noch keine Erinnerungen zum Anpassen." }, "dashboard": { diff --git a/messages/en.json b/messages/en.json index 8ec8c0e61..1a795ba9f 100644 --- a/messages/en.json +++ b/messages/en.json @@ -3264,12 +3264,9 @@ "provider": "Answers with", "modelManaged": "The model for this provider is set in AI settings.", "noProvider": "No provider available", - "saving": "Saving…", - "saveFailed": "That change could not be saved.", "allSettings": "All AI settings", - "providerNotActive": "{provider} could not take over: it has no working sign-in or key. Set it up in AI settings.", "operatorCodex": "ChatGPT (shared by the operator)", - "operatorManaged": "Your server's operator manages this connection. Once one of your own providers is set up, it answers first." + "changeModel": "Change in Settings > AI" }, "answer": { "scrollToBottom": "Jump to the latest message", @@ -5289,12 +5286,17 @@ "groups": { "account": "Account", "yourData": "Your data", - "display": "Display", "healthProfile": "Health profile", "ai": "AI", "connectivity": "Connectivity", - "system": "System" - } + "system": "System", + "tracking": "Tracking", + "arrangement": "Arrangement", + "notifications": "Notifications", + "connections": "Connections", + "data": "Data" + }, + "allSections": "All sections" }, "sharedRecord": { "managedSettings": { @@ -5428,11 +5430,25 @@ "remindersHeading": "Reminders", "matrixLink": "Per-channel notifications", "channelsLink": "Delivery channels", - "displayHeading": "On your phone" + "displayHeading": "On your phone", + "elsewhere": { + "title": "Set where they belong", + "description": "These reminders are set next to what they are about.", + "doses": { + "title": "Medication doses", + "description": "Each medication's dose times and reminders are set on the medication.", + "link": "Open medications" + }, + "cycle": { + "title": "Cycle", + "description": "Period and fertile-window reminders are switched per channel in the notification matrix.", + "link": "Open the notification matrix" + } + } }, "layout": { - "title": "Layout", - "backToHub": "Back to Layout", + "title": "Arrangement", + "backToHub": "Back to Arrangement", "dashboard": { "title": "Dashboard", "description": "Tile layout and order." @@ -5441,7 +5457,7 @@ "title": "Insights", "description": "Overview sections and navigation-pill order." }, - "subtitle": "Choose what your dashboard, insights and tracking modules show, and in which order.", + "subtitle": "Choose the order and view of your dashboard, insights and tracking lists.", "medications": { "title": "Medications", "description": "List view and manual order." @@ -5458,10 +5474,6 @@ "title": "Checkups", "description": "List view and reminder order." }, - "mood": { - "title": "Mood tags", - "description": "Groups, tags, and archived tags for the mood picker." - }, "documents": { "title": "Documents", "description": "View and month arrangement." @@ -5852,7 +5864,28 @@ }, "manageIn": "Manage in {section}", "operatorDisabled": "Disabled server-wide", - "subtitle": "Choose which tracking areas HealthLog shows across the app." + "subtitle": "Choose which tracking areas HealthLog shows across the app.", + "backToHub": "Back to Modules", + "openModuleSettings": "Module settings", + "pages": { + "medications": { + "title": "Medications", + "description": "Injection sites the rotation leaves out." + }, + "mood": { + "title": "Mood tags", + "description": "Groups, your own tags and archived tags." + }, + "labs": { + "title": "Labs", + "description": "Biomarkers and on-device text recognition for lab reports." + }, + "arrangementTitle": "Order and view", + "arrangementDescription": "How this list is sorted and shown lives under Arrangement.", + "openArrangement": "Open in Arrangement", + "moduleSettingsTitle": "Module settings", + "moduleSettingsDescription": "Everything beyond order and view lives on the module's page under Modules." + } }, "gesundheitsakte": { "title": "Health record", @@ -5995,6 +6028,10 @@ "access": { "title": "Shared access", "subtitle": "Who can open your health record, and whose you can open." + }, + "preview": { + "title": "Preview features", + "subtitle": "Try what is not finished yet. Everything here stays off until you switch it on." } }, "profile": "Profile", @@ -7006,21 +7043,19 @@ }, "integrationSetupGuide": "Setup guide", "coach": { - "activate": { - "title": "Activate Coach", - "description": "Show the Coach button and drawer.", - "toggleAria": "Activate the Coach button and drawer", - "savedOn": "Coach activated.", - "savedOff": "Coach hidden.", - "saveError": "Could not save the change. Please try again." - }, "memory": { "edit": "Edit", "source": { "user": "from you", "coach": "from the Coach" } - } + }, + "state": { + "title": "Coach", + "description": "Whether the Coach button and drawer show. The switch is in Modules." + }, + "nudgesMoved": "Set with your other reminders under Notifications.", + "openNudges": "Open in Notifications" }, "integrationRedirectGuide": { "intro": "This connects through your own {provider} developer app, about 5 minutes to set up:", @@ -7163,12 +7198,6 @@ "delivery": "Notification delivery log: kept {days} days", "unavailable": "Retention details are unavailable right now." }, - "export": { - "title": "Download my data", - "description": "Your records as CSV or JSON, or a complete health record for your doctor.", - "openExport": "Export & backup", - "openHealthRecord": "Health record" - }, "delete": { "title": "Delete or reset", "description": "Remove your account entirely, or wipe your health records while keeping the account.", @@ -7184,7 +7213,6 @@ "mcp": { "enableTitle": "Remote MCP endpoint", "enableDescription": "Let an MCP-capable assistant (Claude.ai, ChatGPT) read your health data over a scoped token.", - "enableToggleLabel": "Enable the MCP endpoint", "endpointLabel": "Connector URL", "connectDescription": "Paste this URL into your assistant's custom-connector setup, then authorize with the token below or sign in when prompted.", "tokensTitle": "Connector tokens", @@ -7203,7 +7231,8 @@ "connectionRevokeFailed": "Could not disconnect the assistant.", "connectionsDetail": "Revoking a connection ends its access immediately, including its ability to refresh.", "tokensDetail": "The token is shown once.", - "tokenNamePlaceholder": "Name, e.g. Claude Desktop" + "tokenNamePlaceholder": "Name, e.g. Claude Desktop", + "offKeepsTokens": "Switching it off pauses the endpoint. Your tokens and connections stay and work again once it is back on." }, "strava": "Strava", "stravaDescription": "Connect your Strava account to sync your activities into your workout history.", @@ -7465,7 +7494,25 @@ "passkeyAddedToast": "Passkey added.", "passkeyRemovedToast": "Passkey removed.", "integrationDisconnected": "{provider} disconnected.", - "integrationDisconnectFailed": "Could not disconnect {provider}." + "integrationDisconnectFailed": "Could not disconnect {provider}.", + "preview": { + "topics": { + "title": "Topics (beta)", + "description": "Adds a separate Topics entry with overview pages above the single-metric Insights pages.", + "detail": "Your Insights pages, navigation and links stay exactly as they are. The topic pages arrive in a later update; this switch decides whether you see them.", + "savedOn": "Topics preview switched on.", + "savedOff": "Topics preview switched off." + }, + "loadError": "Could not load your preview settings.", + "feedbackLink": "Share feedback in the GitHub discussions" + }, + "timezoneSaved": "Timezone saved.", + "moduleState": { + "on": "On", + "off": "Off", + "change": "Change in Modules" + }, + "timezoneUseDevice": "Use this device's timezone ({zone})" }, "admin": { "title": "Administration", @@ -8855,6 +8902,10 @@ "detailedToast": "Notifications now include health figures.", "genericToast": "Notifications no longer include health figures.", "saveError": "Could not save. Please try again later." + }, + "checkupReminders": { + "title": "Checkup reminders", + "description": "Switch individual checkup reminders on or off." } }, "i18n": { @@ -9631,8 +9682,6 @@ "description": "Enter the reading; the reminder is marked done." }, "manage": { - "title": "Manage reminders", - "description": "Enable or disable individual reminders.", "empty": "No reminders to customize yet." }, "dashboard": { diff --git a/messages/es.json b/messages/es.json index 80928dc2d..0289ee7a0 100644 --- a/messages/es.json +++ b/messages/es.json @@ -3264,12 +3264,9 @@ "provider": "Responde con", "modelManaged": "El modelo de este proveedor se configura en los ajustes de IA.", "noProvider": "Ningún proveedor disponible", - "saving": "Guardando…", - "saveFailed": "No se pudo guardar el cambio.", "allSettings": "Todos los ajustes de IA", - "providerNotActive": "{provider} no pudo encargarse: no tiene un inicio de sesión ni una clave que funcionen. Configúralo en los ajustes de IA.", "operatorCodex": "ChatGPT (compartido por el operador)", - "operatorManaged": "El operador de tu servidor gestiona esta conexión. Cuando configures uno de tus propios proveedores, responderá primero." + "changeModel": "Cambiar en Ajustes > IA" }, "answer": { "scrollToBottom": "Ir al último mensaje", @@ -5289,12 +5286,17 @@ "groups": { "account": "Cuenta", "yourData": "Tus datos", - "display": "Visualización", "healthProfile": "Perfil de salud", "ai": "IA", "connectivity": "Conectividad", - "system": "Sistema" - } + "system": "Sistema", + "tracking": "Seguimiento", + "arrangement": "Disposición", + "notifications": "Notificaciones", + "connections": "Conexiones", + "data": "Datos" + }, + "allSections": "Todas las secciones" }, "sharedRecord": { "managedSettings": { @@ -5428,11 +5430,25 @@ "remindersHeading": "Recordatorios", "matrixLink": "Notificaciones por canal", "channelsLink": "Canales de entrega", - "displayHeading": "En tu teléfono" + "displayHeading": "En tu teléfono", + "elsewhere": { + "title": "Se ajustan donde corresponden", + "description": "Estos recordatorios se ajustan junto a aquello a lo que se refieren.", + "doses": { + "title": "Tomas de medicación", + "description": "Las horas de toma y los recordatorios se ajustan en cada medicamento.", + "link": "Abrir medicamentos" + }, + "cycle": { + "title": "Ciclo", + "description": "Los recordatorios del periodo y de la ventana fértil se activan por canal en la matriz de notificaciones.", + "link": "Abrir la matriz de notificaciones" + } + } }, "layout": { - "title": "Diseño", - "backToHub": "Volver a Diseño", + "title": "Disposición", + "backToHub": "Volver a Disposición", "dashboard": { "title": "Panel", "description": "Diseño y orden de los mosaicos." @@ -5441,7 +5457,7 @@ "title": "Análisis", "description": "Orden de la vista general y de las pestañas de navegación." }, - "subtitle": "Elige qué muestran tu panel, los análisis y los módulos de seguimiento, y en qué orden.", + "subtitle": "Elige el orden y la vista de tu panel, tus análisis y tus listas de seguimiento.", "medications": { "title": "Medicamentos", "description": "Vista de lista y orden manual." @@ -5458,10 +5474,6 @@ "title": "Revisiones", "description": "Vista de lista y orden de recordatorios." }, - "mood": { - "title": "Etiquetas de ánimo", - "description": "Grupos, etiquetas y etiquetas archivadas del selector de ánimo." - }, "documents": { "title": "Documentos", "description": "Vista y disposición de los meses." @@ -5852,7 +5864,28 @@ }, "manageIn": "Gestionar en {section}", "operatorDisabled": "Desactivado en todo el servidor", - "subtitle": "Elige qué áreas de seguimiento muestra HealthLog en la app." + "subtitle": "Elige qué áreas de seguimiento muestra HealthLog en la app.", + "backToHub": "Volver a Módulos", + "openModuleSettings": "Ajustes del módulo", + "pages": { + "medications": { + "title": "Medicamentos", + "description": "Zonas de inyección que la rotación omite." + }, + "mood": { + "title": "Etiquetas de ánimo", + "description": "Grupos, tus propias etiquetas y las archivadas." + }, + "labs": { + "title": "Laboratorio", + "description": "Biomarcadores y reconocimiento de texto en el dispositivo para informes de laboratorio." + }, + "arrangementTitle": "Orden y vista", + "arrangementDescription": "Cómo se ordena y muestra esta lista se ajusta en Disposición.", + "openArrangement": "Abrir en Disposición", + "moduleSettingsTitle": "Ajustes del módulo", + "moduleSettingsDescription": "Todo lo demás está en la página del módulo, en Módulos." + } }, "gesundheitsakte": { "title": "Historial médico", @@ -5995,6 +6028,10 @@ "access": { "title": "Acceso compartido", "subtitle": "Quién puede abrir tu historial de salud y cuál puedes abrir tú." + }, + "preview": { + "title": "Funciones en vista previa", + "subtitle": "Prueba lo que aún no está terminado. Todo aquí sigue desactivado hasta que lo actives." } }, "profile": "Perfil", @@ -7006,21 +7043,19 @@ }, "integrationSetupGuide": "Guía de configuración", "coach": { - "activate": { - "title": "Activar el Coach", - "description": "Muestra el botón y el panel del Coach.", - "toggleAria": "Activar el botón y el panel del Coach", - "savedOn": "Coach activado.", - "savedOff": "Coach oculto.", - "saveError": "No se pudo guardar el cambio. Inténtalo de nuevo." - }, "memory": { "edit": "Editar", "source": { "user": "de ti", "coach": "del Coach" } - } + }, + "state": { + "title": "Coach", + "description": "Si se muestran el botón y el panel del Coach. El interruptor está en Módulos." + }, + "nudgesMoved": "Se ajustan con tus otros recordatorios en Notificaciones.", + "openNudges": "Abrir en Notificaciones" }, "integrationRedirectGuide": { "intro": "Esto se conecta mediante tu propia app de desarrollador de {provider}, con unos 5 minutos de configuración:", @@ -7163,12 +7198,6 @@ "delivery": "Registro de envío de notificaciones: {days} días", "unavailable": "Los detalles de retención no están disponibles en este momento." }, - "export": { - "title": "Descargar mis datos", - "description": "Tus registros en CSV o JSON, o un historial completo para tu médico.", - "openExport": "Exportar y copia de seguridad", - "openHealthRecord": "Historial médico" - }, "delete": { "title": "Eliminar o restablecer", "description": "Elimina tu cuenta por completo o borra tus registros de salud conservando la cuenta.", @@ -7184,7 +7213,6 @@ "mcp": { "enableTitle": "Endpoint MCP remoto", "enableDescription": "Permite que un asistente compatible con MCP (Claude.ai, ChatGPT) lea tus datos de salud mediante un token limitado.", - "enableToggleLabel": "Activar el endpoint MCP", "endpointLabel": "URL del conector", "connectDescription": "Pega esta URL en la configuración del conector personalizado de tu asistente y autoriza con el token de abajo o inicia sesión cuando se te solicite.", "tokensTitle": "Tokens del conector", @@ -7203,7 +7231,8 @@ "connectionRevokeFailed": "No se pudo desconectar el asistente.", "tokensDetail": "El token se muestra una sola vez.", "connectionsDetail": "Revocar una conexión finaliza su acceso de inmediato, incluida su capacidad de renovación.", - "tokenNamePlaceholder": "Nombre, p. ej. Claude Desktop" + "tokenNamePlaceholder": "Nombre, p. ej. Claude Desktop", + "offKeepsTokens": "Desactivarlo pausa el punto de acceso. Tus tokens y conexiones se mantienen y vuelven a funcionar al activarlo." }, "strava": "Strava", "stravaDescription": "Conecta tu cuenta de Strava para sincronizar tus actividades con tu historial de entrenamientos.", @@ -7465,7 +7494,25 @@ "passkeyAddedToast": "Passkey añadida.", "passkeyRemovedToast": "Passkey eliminada.", "integrationDisconnected": "{provider} desconectado.", - "integrationDisconnectFailed": "No se pudo desconectar {provider}." + "integrationDisconnectFailed": "No se pudo desconectar {provider}.", + "preview": { + "topics": { + "title": "Temas (beta)", + "description": "Añade una entrada aparte, Temas, con páginas de resumen por encima de las páginas de cada métrica.", + "detail": "Tus páginas de análisis, la navegación y los enlaces se quedan exactamente como están. Las páginas de temas llegan en una actualización posterior; este interruptor decide si las ves.", + "savedOn": "Vista previa de Temas activada.", + "savedOff": "Vista previa de Temas desactivada." + }, + "loadError": "No se pudieron cargar tus ajustes de vista previa.", + "feedbackLink": "Comparte tu opinión en las discusiones de GitHub" + }, + "timezoneSaved": "Zona horaria guardada.", + "moduleState": { + "on": "Activado", + "off": "Desactivado", + "change": "Cambiar en Módulos" + }, + "timezoneUseDevice": "Usar la zona horaria de este dispositivo ({zone})" }, "admin": { "title": "Administration", @@ -8855,6 +8902,10 @@ "detailedToast": "Las notificaciones ahora incluyen valores de salud.", "genericToast": "Las notificaciones ya no incluyen valores de salud.", "saveError": "No se pudo guardar. Inténtalo de nuevo más tarde." + }, + "checkupReminders": { + "title": "Recordatorios de chequeos", + "description": "Activa o desactiva recordatorios de chequeos concretos." } }, "i18n": { @@ -9631,8 +9682,6 @@ "description": "Introduce el valor; el recordatorio se marca como hecho." }, "manage": { - "title": "Gestionar recordatorios", - "description": "Activa o desactiva recordatorios individuales.", "empty": "Aún no hay recordatorios para personalizar." }, "dashboard": { diff --git a/messages/fr.json b/messages/fr.json index 4f7058a57..a08d7f9dd 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -3264,12 +3264,9 @@ "provider": "Répond avec", "modelManaged": "Le modèle de ce fournisseur se règle dans les paramètres IA.", "noProvider": "Aucun fournisseur disponible", - "saving": "Enregistrement…", - "saveFailed": "Impossible d’enregistrer cette modification.", "allSettings": "Tous les paramètres IA", - "providerNotActive": "{provider} n'a pas pu prendre le relais : aucune connexion ni clé valide. Configurez-le dans les réglages IA.", "operatorCodex": "ChatGPT (partagé par l'opérateur)", - "operatorManaged": "L'opérateur de votre serveur gère cette connexion. Dès qu'un de vos propres fournisseurs est configuré, c'est lui qui répond en premier." + "changeModel": "Modifier dans Réglages > IA" }, "answer": { "scrollToBottom": "Aller au dernier message", @@ -5289,12 +5286,17 @@ "groups": { "account": "Compte", "yourData": "Vos données", - "display": "Affichage", "healthProfile": "Profil de santé", "ai": "IA", "connectivity": "Connectivité", - "system": "Système" - } + "system": "Système", + "tracking": "Suivi", + "arrangement": "Disposition", + "notifications": "Notifications", + "connections": "Connexions", + "data": "Données" + }, + "allSections": "Toutes les sections" }, "sharedRecord": { "managedSettings": { @@ -5428,7 +5430,21 @@ "remindersHeading": "Rappels", "matrixLink": "Notifications par canal", "channelsLink": "Canaux de diffusion", - "displayHeading": "Sur ton téléphone" + "displayHeading": "Sur ton téléphone", + "elsewhere": { + "title": "Réglés là où ils se rattachent", + "description": "Ces rappels se règlent à côté de ce qu’ils concernent.", + "doses": { + "title": "Prises de médicaments", + "description": "Les heures de prise et les rappels se règlent sur chaque médicament.", + "link": "Ouvrir les médicaments" + }, + "cycle": { + "title": "Cycle", + "description": "Les rappels de règles et de fenêtre fertile s’activent par canal dans la grille des notifications.", + "link": "Ouvrir la grille des notifications" + } + } }, "layout": { "title": "Disposition", @@ -5441,7 +5457,7 @@ "title": "Analyses", "description": "Ordre de la vue d'ensemble et des onglets de navigation." }, - "subtitle": "Choisissez ce qu'affichent le tableau de bord, les analyses et les modules de suivi, et dans quel ordre.", + "subtitle": "Choisissez l’ordre et l’affichage du tableau de bord, des analyses et des listes de suivi.", "medications": { "title": "Médicaments", "description": "Vue en liste et ordre manuel." @@ -5458,10 +5474,6 @@ "title": "Examens", "description": "Vue en liste et ordre des rappels." }, - "mood": { - "title": "Étiquettes d’humeur", - "description": "Groupes, étiquettes et étiquettes archivées du sélecteur d’humeur." - }, "documents": { "title": "Documents", "description": "Affichage et disposition des mois." @@ -5852,7 +5864,28 @@ }, "manageIn": "Gérer dans {section}", "operatorDisabled": "Désactivé sur tout le serveur", - "subtitle": "Choisissez les domaines de suivi affichés par HealthLog." + "subtitle": "Choisissez les domaines de suivi affichés par HealthLog.", + "backToHub": "Retour à Modules", + "openModuleSettings": "Réglages du module", + "pages": { + "medications": { + "title": "Médicaments", + "description": "Sites d’injection que la rotation laisse de côté." + }, + "mood": { + "title": "Étiquettes d’humeur", + "description": "Groupes, vos propres étiquettes et les étiquettes archivées." + }, + "labs": { + "title": "Analyses", + "description": "Biomarqueurs et reconnaissance de texte sur l’appareil pour les comptes rendus d’analyses." + }, + "arrangementTitle": "Ordre et affichage", + "arrangementDescription": "Le tri et l’affichage de cette liste se règlent dans Disposition.", + "openArrangement": "Ouvrir dans Disposition", + "moduleSettingsTitle": "Réglages du module", + "moduleSettingsDescription": "Tout le reste se trouve sur la page du module, dans Modules." + } }, "gesundheitsakte": { "title": "Dossier médical", @@ -5995,6 +6028,10 @@ "access": { "title": "Accès partagé", "subtitle": "Qui peut ouvrir votre dossier de santé, et lequel vous pouvez ouvrir." + }, + "preview": { + "title": "Fonctions en aperçu", + "subtitle": "Essayez ce qui n’est pas encore terminé. Tout reste désactivé ici tant que vous ne l’activez pas." } }, "profile": "Profil", @@ -7006,21 +7043,19 @@ }, "integrationSetupGuide": "Guide de configuration", "coach": { - "activate": { - "title": "Activer le Coach", - "description": "Affiche le bouton et le panneau du Coach.", - "toggleAria": "Activer le bouton et le panneau du Coach", - "savedOn": "Coach activé.", - "savedOff": "Coach masqué.", - "saveError": "Impossible d enregistrer la modification. Veuillez réessayer." - }, "memory": { "edit": "Modifier", "source": { "user": "de toi", "coach": "du Coach" } - } + }, + "state": { + "title": "Coach", + "description": "Si le bouton et le panneau du Coach s’affichent. L’interrupteur se trouve dans Modules." + }, + "nudgesMoved": "Se règlent avec vos autres rappels dans Notifications.", + "openNudges": "Ouvrir dans Notifications" }, "integrationRedirectGuide": { "intro": "Cette connexion passe par votre propre application développeur {provider}, environ 5 minutes de configuration :", @@ -7163,12 +7198,6 @@ "delivery": "Journal d'envoi des notifications : {days} jours", "unavailable": "Les détails de conservation sont indisponibles pour le moment." }, - "export": { - "title": "Télécharger mes données", - "description": "Vos enregistrements en CSV ou JSON, ou un dossier complet pour votre médecin.", - "openExport": "Export et sauvegarde", - "openHealthRecord": "Dossier médical" - }, "delete": { "title": "Supprimer ou réinitialiser", "description": "Supprimez complètement votre compte, ou effacez vos données de santé tout en conservant le compte.", @@ -7184,7 +7213,6 @@ "mcp": { "enableTitle": "Point de terminaison MCP distant", "enableDescription": "Autorisez un assistant compatible MCP (Claude.ai, ChatGPT) à lire vos données de santé via un jeton restreint.", - "enableToggleLabel": "Activer le point de terminaison MCP", "endpointLabel": "URL du connecteur", "connectDescription": "Collez cette URL dans la configuration du connecteur personnalisé de votre assistant, puis autorisez avec le jeton ci-dessous ou connectez-vous lorsque vous y êtes invité.", "tokensTitle": "Jetons du connecteur", @@ -7203,7 +7231,8 @@ "connectionRevokeFailed": "Impossible de déconnecter l'assistant.", "tokensDetail": "Le jeton n'est affiché qu'une seule fois.", "connectionsDetail": "Révoquer une connexion met fin à son accès immédiatement, y compris sa capacité à se renouveler.", - "tokenNamePlaceholder": "Nom, p. ex. Claude Desktop" + "tokenNamePlaceholder": "Nom, p. ex. Claude Desktop", + "offKeepsTokens": "Le désactiver met le point d’accès en pause. Vos jetons et connexions restent et fonctionnent de nouveau dès sa réactivation." }, "strava": "Strava", "stravaDescription": "Connecte ton compte Strava pour synchroniser tes activités avec ton historique d'entraînement.", @@ -7465,7 +7494,25 @@ "passkeyAddedToast": "Passkey ajoutée.", "passkeyRemovedToast": "Passkey supprimée.", "integrationDisconnected": "{provider} déconnecté.", - "integrationDisconnectFailed": "Impossible de déconnecter {provider}." + "integrationDisconnectFailed": "Impossible de déconnecter {provider}.", + "preview": { + "topics": { + "title": "Thèmes (bêta)", + "description": "Ajoute une entrée Thèmes distincte, avec des pages de synthèse au-dessus des pages par mesure.", + "detail": "Vos pages d’analyse, la navigation et les liens restent exactement tels quels. Les pages de thèmes arrivent dans une mise à jour ultérieure ; cet interrupteur décide si vous les voyez.", + "savedOn": "Aperçu des thèmes activé.", + "savedOff": "Aperçu des thèmes désactivé." + }, + "loadError": "Impossible de charger vos réglages d’aperçu.", + "feedbackLink": "Donner votre avis dans les discussions GitHub" + }, + "timezoneSaved": "Fuseau horaire enregistré.", + "moduleState": { + "on": "Activé", + "off": "Désactivé", + "change": "Modifier dans Modules" + }, + "timezoneUseDevice": "Utiliser le fuseau horaire de cet appareil ({zone})" }, "admin": { "title": "Administration", @@ -8855,6 +8902,10 @@ "detailedToast": "Les notifications incluent désormais des valeurs de santé.", "genericToast": "Les notifications n'incluent plus de valeurs de santé.", "saveError": "Enregistrement impossible. Réessaie plus tard." + }, + "checkupReminders": { + "title": "Rappels de bilans", + "description": "Activez ou désactivez chaque rappel de bilan." } }, "i18n": { @@ -9631,8 +9682,6 @@ "description": "Saisissez la valeur ; le rappel est marqué comme fait." }, "manage": { - "title": "Gérer les rappels", - "description": "Activer ou désactiver des rappels individuels.", "empty": "Aucun rappel à personnaliser pour l'instant." }, "dashboard": { diff --git a/messages/it.json b/messages/it.json index a1dc08274..b633ccaa0 100644 --- a/messages/it.json +++ b/messages/it.json @@ -3264,12 +3264,9 @@ "provider": "Risponde con", "modelManaged": "Il modello di questo fornitore si imposta nelle impostazioni IA.", "noProvider": "Nessun fornitore disponibile", - "saving": "Salvataggio…", - "saveFailed": "Impossibile salvare la modifica.", "allSettings": "Tutte le impostazioni IA", - "providerNotActive": "{provider} non è subentrato: manca un accesso o una chiave funzionante. Configuralo nelle impostazioni IA.", "operatorCodex": "ChatGPT (condiviso dall'operatore)", - "operatorManaged": "Questa connessione è gestita dall'operatore del tuo server. Quando configuri uno dei tuoi fornitori, risponde per primo." + "changeModel": "Cambia in Impostazioni > IA" }, "answer": { "scrollToBottom": "Vai all'ultimo messaggio", @@ -5289,12 +5286,17 @@ "groups": { "account": "Account", "yourData": "I tuoi dati", - "display": "Visualizzazione", "healthProfile": "Profilo sanitario", "ai": "IA", "connectivity": "Connettività", - "system": "Sistema" - } + "system": "Sistema", + "tracking": "Monitoraggio", + "arrangement": "Disposizione", + "notifications": "Notifiche", + "connections": "Connessioni", + "data": "Dati" + }, + "allSections": "Tutte le sezioni" }, "sharedRecord": { "managedSettings": { @@ -5428,11 +5430,25 @@ "remindersHeading": "Promemoria", "matrixLink": "Notifiche per canale", "channelsLink": "Canali di recapito", - "displayHeading": "Sul telefono" + "displayHeading": "Sul telefono", + "elsewhere": { + "title": "Impostati dove appartengono", + "description": "Questi promemoria si impostano accanto a ciò che riguardano.", + "doses": { + "title": "Assunzioni di farmaci", + "description": "Orari e promemoria si impostano su ciascun farmaco.", + "link": "Apri i farmaci" + }, + "cycle": { + "title": "Ciclo", + "description": "I promemoria del ciclo e della finestra fertile si attivano per canale nella griglia delle notifiche.", + "link": "Apri la griglia delle notifiche" + } + } }, "layout": { - "title": "Layout", - "backToHub": "Torna a Layout", + "title": "Disposizione", + "backToHub": "Torna a Disposizione", "dashboard": { "title": "Cruscotto", "description": "Layout e ordine dei riquadri." @@ -5441,7 +5457,7 @@ "title": "Approfondimenti", "description": "Ordine della panoramica e delle schede di navigazione." }, - "subtitle": "Scegli cosa mostrano dashboard, approfondimenti e moduli di monitoraggio, e in quale ordine.", + "subtitle": "Scegli ordine e vista della dashboard, delle analisi e degli elenchi di monitoraggio.", "medications": { "title": "Farmaci", "description": "Vista elenco e ordine manuale." @@ -5458,10 +5474,6 @@ "title": "Controlli", "description": "Vista elenco e ordine dei promemoria." }, - "mood": { - "title": "Tag dell’umore", - "description": "Gruppi, tag e tag archiviati del selettore dell’umore." - }, "documents": { "title": "Documenti", "description": "Visualizzazione e disposizione dei mesi." @@ -5852,7 +5864,28 @@ }, "manageIn": "Gestisci in {section}", "operatorDisabled": "Disattivato a livello di server", - "subtitle": "Scegli quali aree di monitoraggio HealthLog mostra nell'app." + "subtitle": "Scegli quali aree di monitoraggio HealthLog mostra nell'app.", + "backToHub": "Torna a Moduli", + "openModuleSettings": "Impostazioni del modulo", + "pages": { + "medications": { + "title": "Farmaci", + "description": "Siti di iniezione esclusi dalla rotazione." + }, + "mood": { + "title": "Tag dell’umore", + "description": "Gruppi, i tuoi tag e i tag archiviati." + }, + "labs": { + "title": "Esami", + "description": "Biomarcatori e riconoscimento del testo sul dispositivo per i referti." + }, + "arrangementTitle": "Ordine e vista", + "arrangementDescription": "Ordinamento e vista di questo elenco si impostano in Disposizione.", + "openArrangement": "Apri in Disposizione", + "moduleSettingsTitle": "Impostazioni del modulo", + "moduleSettingsDescription": "Tutto il resto è nella pagina del modulo, in Moduli." + } }, "gesundheitsakte": { "title": "Cartella clinica", @@ -5995,6 +6028,10 @@ "access": { "title": "Accesso condiviso", "subtitle": "Chi può aprire la tua cartella sanitaria e quale puoi aprire tu." + }, + "preview": { + "title": "Funzioni in anteprima", + "subtitle": "Prova ciò che non è ancora finito. Qui tutto resta spento finché non lo attivi." } }, "profile": "Profilo", @@ -7006,21 +7043,19 @@ }, "integrationSetupGuide": "Guida alla configurazione", "coach": { - "activate": { - "title": "Attiva il Coach", - "description": "Mostra il pulsante e il pannello del Coach.", - "toggleAria": "Attiva il pulsante e il pannello del Coach", - "savedOn": "Coach attivato.", - "savedOff": "Coach nascosto.", - "saveError": "Impossibile salvare la modifica. Riprova." - }, "memory": { "edit": "Modifica", "source": { "user": "da te", "coach": "dal Coach" } - } + }, + "state": { + "title": "Coach", + "description": "Se il pulsante e il pannello del Coach sono visibili. L’interruttore è in Moduli." + }, + "nudgesMoved": "Si impostano con gli altri promemoria in Notifiche.", + "openNudges": "Apri in Notifiche" }, "integrationRedirectGuide": { "intro": "Questa connessione passa dalla tua app sviluppatore {provider} (circa 5 minuti di configurazione):", @@ -7163,12 +7198,6 @@ "delivery": "Registro di invio delle notifiche: {days} giorni", "unavailable": "I dettagli di conservazione non sono disponibili al momento." }, - "export": { - "title": "Scarica i miei dati", - "description": "I tuoi record in CSV o JSON, o una cartella completa per il medico.", - "openExport": "Esportazione e backup", - "openHealthRecord": "Fascicolo sanitario" - }, "delete": { "title": "Elimina o reimposta", "description": "Elimina completamente il tuo account oppure cancella i tuoi dati sanitari mantenendo l'account.", @@ -7184,7 +7213,6 @@ "mcp": { "enableTitle": "Endpoint MCP remoto", "enableDescription": "Consenti a un assistente compatibile con MCP (Claude.ai, ChatGPT) di leggere i tuoi dati sulla salute tramite un token limitato.", - "enableToggleLabel": "Attiva l'endpoint MCP", "endpointLabel": "URL del connettore", "connectDescription": "Incolla questo URL nella configurazione del connettore personalizzato del tuo assistente e autorizza con il token qui sotto o accedi quando richiesto.", "tokensTitle": "Token del connettore", @@ -7203,7 +7231,8 @@ "connectionRevokeFailed": "Impossibile disconnettere l'assistente.", "tokensDetail": "Il token viene mostrato una sola volta.", "connectionsDetail": "Revocare una connessione ne termina subito l'accesso, inclusa la capacità di rinnovarsi.", - "tokenNamePlaceholder": "Nome, ad es. Claude Desktop" + "tokenNamePlaceholder": "Nome, ad es. Claude Desktop", + "offKeepsTokens": "Spegnerlo mette in pausa l’endpoint. Token e connessioni restano e tornano a funzionare quando lo riaccendi." }, "strava": "Strava", "stravaDescription": "Collega il tuo account Strava per sincronizzare le tue attività con la cronologia degli allenamenti.", @@ -7465,7 +7494,25 @@ "passkeyAddedToast": "Passkey aggiunta.", "passkeyRemovedToast": "Passkey rimossa.", "integrationDisconnected": "{provider} disconnesso.", - "integrationDisconnectFailed": "Impossibile disconnettere {provider}." + "integrationDisconnectFailed": "Impossibile disconnettere {provider}.", + "preview": { + "topics": { + "title": "Temi (beta)", + "description": "Aggiunge una voce Temi separata, con pagine di panoramica sopra le pagine delle singole metriche.", + "detail": "Le pagine delle analisi, la navigazione e i link restano esattamente come sono. Le pagine dei temi arrivano con un aggiornamento successivo; questo interruttore decide se le vedi.", + "savedOn": "Anteprima dei temi attivata.", + "savedOff": "Anteprima dei temi disattivata." + }, + "loadError": "Impossibile caricare le impostazioni di anteprima.", + "feedbackLink": "Lascia un parere nelle discussioni su GitHub" + }, + "timezoneSaved": "Fuso orario salvato.", + "moduleState": { + "on": "Attivo", + "off": "Disattivo", + "change": "Cambia in Moduli" + }, + "timezoneUseDevice": "Usa il fuso orario di questo dispositivo ({zone})" }, "admin": { "title": "Administration", @@ -8855,6 +8902,10 @@ "detailedToast": "Le notifiche ora includono valori di salute.", "genericToast": "Le notifiche non includono più valori di salute.", "saveError": "Impossibile salvare. Riprova più tardi." + }, + "checkupReminders": { + "title": "Promemoria dei controlli", + "description": "Attiva o disattiva i singoli promemoria dei controlli." } }, "i18n": { @@ -9631,8 +9682,6 @@ "description": "Inserisci il valore; il promemoria viene segnato come fatto." }, "manage": { - "title": "Gestisci promemoria", - "description": "Attiva o disattiva singoli promemoria.", "empty": "Nessun promemoria da personalizzare." }, "dashboard": { diff --git a/messages/ko.json b/messages/ko.json index 8712f3f1a..efa6ad902 100644 --- a/messages/ko.json +++ b/messages/ko.json @@ -3264,12 +3264,9 @@ "provider": "응답 제공자", "modelManaged": "이 제공자의 모델은 AI 설정에서 정해요.", "noProvider": "사용할 수 있는 제공자가 없어요", - "saving": "저장 중…", - "saveFailed": "변경 사항을 저장하지 못했어요.", "allSettings": "모든 AI 설정", - "providerNotActive": "{provider}(으)로 전환하지 못했습니다. 작동하는 로그인이나 키가 없습니다. AI 설정에서 설정하세요.", "operatorCodex": "ChatGPT (운영자 공유)", - "operatorManaged": "이 연결은 서버 운영자가 관리합니다. 내 공급자를 하나라도 설정하면 그 공급자가 먼저 답합니다." + "changeModel": "설정 > AI에서 변경" }, "answer": { "scrollToBottom": "최신 메시지로 이동", @@ -5289,12 +5286,17 @@ "groups": { "account": "계정", "yourData": "내 데이터", - "display": "화면", "healthProfile": "건강 프로필", "ai": "AI", "connectivity": "연결", - "system": "시스템" - } + "system": "시스템", + "tracking": "기록", + "arrangement": "배치", + "notifications": "알림", + "connections": "연결", + "data": "데이터" + }, + "allSections": "모든 항목" }, "sharedRecord": { "managedSettings": { @@ -5428,11 +5430,25 @@ "remindersHeading": "알림", "matrixLink": "채널별 알림", "channelsLink": "전송 채널", - "displayHeading": "휴대폰에서" + "displayHeading": "휴대폰에서", + "elsewhere": { + "title": "해당 위치에서 설정", + "description": "이 리마인더는 관련 항목 옆에서 설정합니다.", + "doses": { + "title": "약 복용", + "description": "복용 시간과 리마인더는 각 약에서 설정합니다.", + "link": "약 열기" + }, + "cycle": { + "title": "주기", + "description": "생리와 가임기 리마인더는 알림 표에서 채널별로 켭니다.", + "link": "알림 표 열기" + } + } }, "layout": { - "title": "화면 구성", - "backToHub": "화면 구성으로 돌아가기", + "title": "배치", + "backToHub": "배치로 돌아가기", "dashboard": { "title": "대시보드", "description": "타일 배치와 순서." @@ -5441,7 +5457,7 @@ "title": "인사이트", "description": "개요 섹션과 내비게이션 칩 순서." }, - "subtitle": "대시보드, 인사이트, 기록 모듈에 무엇을 어떤 순서로 보여줄지 정해요.", + "subtitle": "대시보드, 인사이트, 기록 목록의 순서와 보기를 정하세요.", "medications": { "title": "복약", "description": "목록 보기와 수동 순서." @@ -5458,10 +5474,6 @@ "title": "건강검진", "description": "목록 보기와 알림 순서." }, - "mood": { - "title": "기분 태그", - "description": "기분 선택기의 그룹, 태그, 보관된 태그." - }, "documents": { "title": "문서", "description": "보기 방식과 월 배치." @@ -5852,7 +5864,28 @@ }, "manageIn": "{section}에서 관리", "operatorDisabled": "서버 전체에서 꺼짐", - "subtitle": "HealthLog가 앱 전체에서 보여 줄 기록 영역을 골라요." + "subtitle": "HealthLog가 앱 전체에서 보여 줄 기록 영역을 골라요.", + "backToHub": "모듈로 돌아가기", + "openModuleSettings": "모듈 설정", + "pages": { + "medications": { + "title": "약", + "description": "순환에서 제외할 주사 부위입니다." + }, + "mood": { + "title": "기분 태그", + "description": "그룹, 직접 만든 태그, 보관된 태그입니다." + }, + "labs": { + "title": "검사", + "description": "바이오마커와 검사 보고서용 기기 내 텍스트 인식입니다." + }, + "arrangementTitle": "순서와 보기", + "arrangementDescription": "이 목록의 정렬과 보기는 배치에서 설정합니다.", + "openArrangement": "배치에서 열기", + "moduleSettingsTitle": "모듈 설정", + "moduleSettingsDescription": "순서와 보기 외의 설정은 모듈의 페이지에 있습니다." + } }, "gesundheitsakte": { "title": "건강 기록", @@ -5995,6 +6028,10 @@ "access": { "title": "공유 접근", "subtitle": "내 건강 기록을 누가 열 수 있고, 내가 누구의 기록을 열 수 있는지예요." + }, + "preview": { + "title": "미리 보기 기능", + "subtitle": "아직 완성되지 않은 기능을 써 보세요. 직접 켜기 전까지는 모두 꺼져 있습니다." } }, "profile": "프로필", @@ -7006,21 +7043,19 @@ }, "integrationSetupGuide": "설정 가이드", "coach": { - "activate": { - "title": "코치 활성화", - "description": "코치 버튼과 패널을 표시해요.", - "toggleAria": "코치 버튼과 패널 활성화", - "savedOn": "코치를 켰어요.", - "savedOff": "코치를 숨겼어요.", - "saveError": "변경 사항을 저장하지 못했어요. 다시 시도해 주세요." - }, "memory": { "edit": "편집", "source": { "user": "내가 알려 줌", "coach": "코치가 기억함" } - } + }, + "state": { + "title": "코치", + "description": "코치 버튼과 패널 표시 여부입니다. 스위치는 모듈에 있습니다." + }, + "nudgesMoved": "알림에서 다른 리마인더와 함께 설정합니다.", + "openNudges": "알림에서 열기" }, "integrationRedirectGuide": { "intro": "직접 만든 {provider} 개발자 앱으로 연결해요. 설정에 5분쯤 걸려요:", @@ -7163,12 +7198,6 @@ "delivery": "알림 발송 기록: {days}일 보관", "unavailable": "지금은 보관 기간 정보를 볼 수 없어요." }, - "export": { - "title": "내 데이터 내려받기", - "description": "기록을 CSV나 JSON으로, 또는 진료용 전체 건강 기록으로 받을 수 있어요.", - "openExport": "내보내기·백업", - "openHealthRecord": "건강 기록" - }, "delete": { "title": "삭제 또는 초기화", "description": "계정을 완전히 없애거나, 계정은 두고 건강 기록만 지워요.", @@ -7184,7 +7213,6 @@ "mcp": { "enableTitle": "원격 MCP 엔드포인트", "enableDescription": "MCP를 지원하는 어시스턴트(Claude.ai, ChatGPT)가 범위가 제한된 토큰으로 건강 데이터를 읽게 해요.", - "enableToggleLabel": "MCP 엔드포인트 켜기", "endpointLabel": "커넥터 URL", "connectDescription": "어시스턴트의 커스텀 커넥터 설정에 이 URL을 붙여 넣고, 아래 토큰으로 인증하거나 안내가 나오면 로그인하세요.", "tokensTitle": "커넥터 토큰", @@ -7203,7 +7231,8 @@ "connectionRevokeFailed": "어시스턴트 연결을 해제하지 못했어요.", "connectionsDetail": "연결을 해지하면 갱신 권한까지 포함해 접근이 즉시 끊겨요.", "tokensDetail": "토큰은 한 번만 보여줘요.", - "tokenNamePlaceholder": "이름 (예: Claude Desktop)" + "tokenNamePlaceholder": "이름 (예: Claude Desktop)", + "offKeepsTokens": "끄면 엔드포인트가 일시 중지됩니다. 토큰과 연결은 그대로 남고 다시 켜면 다시 작동합니다." }, "strava": "Strava", "stravaDescription": "Strava 계정을 연결해 활동을 운동 기록으로 동기화해요.", @@ -7465,7 +7494,25 @@ "passkeyAddedToast": "패스키를 추가했어요.", "passkeyRemovedToast": "패스키를 삭제했어요.", "integrationDisconnected": "{provider} 연결을 해제했어요.", - "integrationDisconnectFailed": "{provider} 연결을 해제하지 못했어요." + "integrationDisconnectFailed": "{provider} 연결을 해제하지 못했어요.", + "preview": { + "topics": { + "title": "주제 (베타)", + "description": "개별 지표 페이지 위에 개요 페이지가 있는 별도의 주제 항목을 추가합니다.", + "detail": "인사이트 페이지, 메뉴, 링크는 지금 그대로 유지됩니다. 주제 페이지는 이후 업데이트에서 제공되며, 이 스위치로 표시 여부를 정합니다.", + "savedOn": "주제 미리 보기를 켰습니다.", + "savedOff": "주제 미리 보기를 껐습니다." + }, + "loadError": "미리 보기 설정을 불러오지 못했습니다.", + "feedbackLink": "GitHub 토론에서 의견 남기기" + }, + "timezoneSaved": "시간대를 저장했습니다.", + "moduleState": { + "on": "켜짐", + "off": "꺼짐", + "change": "모듈에서 변경" + }, + "timezoneUseDevice": "이 기기의 시간대 사용 ({zone})" }, "admin": { "title": "관리자", @@ -8855,6 +8902,10 @@ "detailedToast": "이제 알림에 건강 수치가 포함됩니다.", "genericToast": "이제 알림에 건강 수치가 포함되지 않습니다.", "saveError": "저장하지 못했어요. 나중에 다시 시도해 주세요." + }, + "checkupReminders": { + "title": "검진 리마인더", + "description": "검진 리마인더를 개별로 켜거나 끕니다." } }, "i18n": { @@ -9631,8 +9682,6 @@ "description": "측정값을 입력하면 알림이 완료로 표시돼요." }, "manage": { - "title": "알림 관리", - "description": "알림을 하나씩 켜거나 끌 수 있어요.", "empty": "아직 설정할 알림이 없어요." }, "dashboard": { diff --git a/messages/nl.json b/messages/nl.json index dc0c5e5f0..03d9ac271 100644 --- a/messages/nl.json +++ b/messages/nl.json @@ -3264,12 +3264,9 @@ "provider": "Antwoordt met", "modelManaged": "Het model voor deze provider stel je in bij de AI-instellingen.", "noProvider": "Geen provider beschikbaar", - "saving": "Opslaan…", - "saveFailed": "Die wijziging kon niet worden opgeslagen.", "allSettings": "Alle AI-instellingen", - "providerNotActive": "{provider} kon het niet overnemen: er is geen werkende aanmelding of sleutel. Stel deze provider in bij de AI-instellingen.", "operatorCodex": "ChatGPT (gedeeld door de serverbeheerder)", - "operatorManaged": "De beheerder van je server beheert deze verbinding. Zodra een van je eigen providers is ingesteld, antwoordt die als eerste." + "changeModel": "Wijzigen in Instellingen > AI" }, "answer": { "scrollToBottom": "Naar het nieuwste bericht", @@ -5289,12 +5286,17 @@ "groups": { "account": "Account", "yourData": "Je gegevens", - "display": "Weergave", "healthProfile": "Gezondheidsprofiel", "ai": "AI", "connectivity": "Verbindingen", - "system": "Systeem" - } + "system": "Systeem", + "tracking": "Bijhouden", + "arrangement": "Indeling", + "notifications": "Meldingen", + "connections": "Verbindingen", + "data": "Gegevens" + }, + "allSections": "Alle onderdelen" }, "sharedRecord": { "managedSettings": { @@ -5428,7 +5430,21 @@ "remindersHeading": "Herinneringen", "matrixLink": "Meldingen per kanaal", "channelsLink": "Bezorgkanalen", - "displayHeading": "Op je telefoon" + "displayHeading": "Op je telefoon", + "elsewhere": { + "title": "Ingesteld waar ze thuishoren", + "description": "Deze herinneringen stel je in bij waar ze over gaan.", + "doses": { + "title": "Medicatie-innames", + "description": "Innametijden en herinneringen stel je per medicijn in.", + "link": "Medicatie openen" + }, + "cycle": { + "title": "Cyclus", + "description": "Herinneringen voor menstruatie en vruchtbare dagen zet je per kanaal aan in het meldingenoverzicht.", + "link": "Meldingenoverzicht openen" + } + } }, "layout": { "title": "Indeling", @@ -5441,7 +5457,7 @@ "title": "Inzichten", "description": "Overzichtsonderdelen en volgorde van de navigatieknoppen." }, - "subtitle": "Kies wat je dashboard, inzichten en registratiemodules laten zien, en in welke volgorde.", + "subtitle": "Kies de volgorde en weergave van je dashboard, inzichten en lijsten.", "medications": { "title": "Medicatie", "description": "Lijstweergave en handmatige volgorde." @@ -5458,10 +5474,6 @@ "title": "Preventieve controles", "description": "Lijstweergave en volgorde van herinneringen." }, - "mood": { - "title": "Stemmingstags", - "description": "Groepen, tags en gearchiveerde tags voor de stemmingskiezer." - }, "documents": { "title": "Documenten", "description": "Weergave en indeling per maand." @@ -5852,7 +5864,28 @@ }, "manageIn": "Beheren in {section}", "operatorDisabled": "Op de hele server uitgeschakeld", - "subtitle": "Kies welke onderdelen HealthLog in de hele app toont." + "subtitle": "Kies welke onderdelen HealthLog in de hele app toont.", + "backToHub": "Terug naar Modules", + "openModuleSettings": "Module-instellingen", + "pages": { + "medications": { + "title": "Medicatie", + "description": "Injectieplaatsen die de rotatie overslaat." + }, + "mood": { + "title": "Stemmingstags", + "description": "Groepen, je eigen tags en gearchiveerde tags." + }, + "labs": { + "title": "Labwaarden", + "description": "Biomarkers en tekstherkenning op het apparaat voor labuitslagen." + }, + "arrangementTitle": "Volgorde en weergave", + "arrangementDescription": "Hoe deze lijst wordt gesorteerd en getoond, stel je in bij Indeling.", + "openArrangement": "Openen in Indeling", + "moduleSettingsTitle": "Module-instellingen", + "moduleSettingsDescription": "Al het andere staat op de pagina van de module onder Modules." + } }, "gesundheitsakte": { "title": "Gezondheidsdossier", @@ -5995,6 +6028,10 @@ "access": { "title": "Gedeelde toegang", "subtitle": "Wie je gezondheidsdossier kan openen, en wiens dossier jij kunt openen." + }, + "preview": { + "title": "Previewfuncties", + "subtitle": "Probeer wat nog niet af is. Alles hier blijft uit tot je het aanzet." } }, "profile": "Profiel", @@ -7006,21 +7043,19 @@ }, "integrationSetupGuide": "Installatiehandleiding", "coach": { - "activate": { - "title": "Coach activeren", - "description": "De Coach-knop en het Coach-paneel tonen.", - "toggleAria": "De Coach-knop en het Coach-paneel activeren", - "savedOn": "Coach geactiveerd.", - "savedOff": "Coach verborgen.", - "saveError": "De wijziging kon niet worden opgeslagen. Probeer het opnieuw." - }, "memory": { "edit": "Bewerken", "source": { "user": "van jou", "coach": "van de Coach" } - } + }, + "state": { + "title": "Coach", + "description": "Of de Coach-knop en het Coach-paneel zichtbaar zijn. De schakelaar staat bij Modules." + }, + "nudgesMoved": "Stel je in bij je andere herinneringen onder Meldingen.", + "openNudges": "Openen in Meldingen" }, "integrationRedirectGuide": { "intro": "Dit koppelt via je eigen {provider}-ontwikkelaarsapp; het instellen duurt ongeveer 5 minuten:", @@ -7163,12 +7198,6 @@ "delivery": "Logboek van bezorgde meldingen: {days} dagen bewaard", "unavailable": "Details over de bewaartermijn zijn op dit moment niet beschikbaar." }, - "export": { - "title": "Mijn gegevens downloaden", - "description": "Je gegevens als CSV of JSON, of een volledig medisch dossier voor je arts.", - "openExport": "Export en back-up", - "openHealthRecord": "Medisch dossier" - }, "delete": { "title": "Verwijderen of wissen", "description": "Verwijder je account volledig, of wis je gezondheidsgegevens en behoud je account.", @@ -7184,7 +7213,6 @@ "mcp": { "enableTitle": "Extern MCP-endpoint", "enableDescription": "Laat een assistent met MCP-ondersteuning (Claude.ai, ChatGPT) je gezondheidsgegevens lezen via een token met beperkte rechten.", - "enableToggleLabel": "Het MCP-endpoint inschakelen", "endpointLabel": "Connector-URL", "connectDescription": "Plak deze URL in de instellingen voor aangepaste connectors van je assistent en autoriseer daarna met het token hieronder, of log in wanneer daarom wordt gevraagd.", "tokensTitle": "Connectortokens", @@ -7203,7 +7231,8 @@ "connectionRevokeFailed": "De assistent kon niet worden ontkoppeld.", "connectionsDetail": "Als je een koppeling intrekt, eindigt de toegang direct, inclusief de mogelijkheid om die te vernieuwen.", "tokensDetail": "Het token wordt één keer getoond.", - "tokenNamePlaceholder": "Naam, bijv. Claude Desktop" + "tokenNamePlaceholder": "Naam, bijv. Claude Desktop", + "offKeepsTokens": "Uitzetten pauzeert het eindpunt. Je tokens en verbindingen blijven en werken weer zodra het aan staat." }, "strava": "Strava", "stravaDescription": "Koppel je Strava-account om je activiteiten naar je trainingsgeschiedenis te synchroniseren.", @@ -7465,7 +7494,25 @@ "passkeyAddedToast": "Passkey toegevoegd.", "passkeyRemovedToast": "Passkey verwijderd.", "integrationDisconnected": "{provider} ontkoppeld.", - "integrationDisconnectFailed": "{provider} kon niet worden ontkoppeld." + "integrationDisconnectFailed": "{provider} kon niet worden ontkoppeld.", + "preview": { + "topics": { + "title": "Onderwerpen (bèta)", + "description": "Voegt een apart item Onderwerpen toe, met overzichtspagina’s boven de pagina’s per meting.", + "detail": "Je inzichtpagina’s, navigatie en links blijven precies zoals ze zijn. De onderwerppagina’s komen met een latere update; deze schakelaar bepaalt of je ze ziet.", + "savedOn": "Preview van onderwerpen ingeschakeld.", + "savedOff": "Preview van onderwerpen uitgeschakeld." + }, + "loadError": "Je preview-instellingen konden niet worden geladen.", + "feedbackLink": "Geef feedback in de GitHub-discussies" + }, + "timezoneSaved": "Tijdzone opgeslagen.", + "moduleState": { + "on": "Aan", + "off": "Uit", + "change": "Wijzigen in Modules" + }, + "timezoneUseDevice": "Tijdzone van dit apparaat gebruiken ({zone})" }, "admin": { "title": "Beheer", @@ -8855,6 +8902,10 @@ "detailedToast": "Meldingen bevatten nu gezondheidscijfers.", "genericToast": "Meldingen bevatten geen gezondheidscijfers meer.", "saveError": "Opslaan mislukt. Probeer het later opnieuw." + }, + "checkupReminders": { + "title": "Herinneringen voor controles", + "description": "Zet afzonderlijke herinneringen voor controles aan of uit." } }, "i18n": { @@ -9631,8 +9682,6 @@ "description": "Voer de meetwaarde in; de herinnering wordt als gedaan gemarkeerd." }, "manage": { - "title": "Herinneringen beheren", - "description": "Zet afzonderlijke herinneringen aan of uit.", "empty": "Nog geen herinneringen om aan te passen." }, "dashboard": { diff --git a/messages/pl.json b/messages/pl.json index 625abe2a4..8f09be7a1 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -3264,12 +3264,9 @@ "provider": "Odpowiada przez", "modelManaged": "Model tego dostawcy ustawisz w ustawieniach AI.", "noProvider": "Brak dostępnego dostawcy", - "saving": "Zapisywanie…", - "saveFailed": "Nie udało się zapisać zmiany.", "allSettings": "Wszystkie ustawienia AI", - "providerNotActive": "{provider} nie mógł przejąć odpowiedzi: brak działającego logowania lub klucza. Skonfiguruj go w ustawieniach AI.", "operatorCodex": "ChatGPT (udostępniony przez operatora)", - "operatorManaged": "Tym połączeniem zarządza operator Twojego serwera. Gdy skonfigurujesz własnego dostawcę, to on odpowie jako pierwszy." + "changeModel": "Zmień w Ustawieniach > AI" }, "answer": { "scrollToBottom": "Przejdź do najnowszej wiadomości", @@ -5289,12 +5286,17 @@ "groups": { "account": "Konto", "yourData": "Twoje dane", - "display": "Wyświetlanie", "healthProfile": "Profil zdrowotny", "ai": "AI", "connectivity": "Łączność", - "system": "System" - } + "system": "System", + "tracking": "Śledzenie", + "arrangement": "Układ", + "notifications": "Powiadomienia", + "connections": "Połączenia", + "data": "Dane" + }, + "allSections": "Wszystkie sekcje" }, "sharedRecord": { "managedSettings": { @@ -5428,11 +5430,25 @@ "remindersHeading": "Przypomnienia", "matrixLink": "Powiadomienia wg kanału", "channelsLink": "Kanały dostarczania", - "displayHeading": "Na telefonie" + "displayHeading": "Na telefonie", + "elsewhere": { + "title": "Ustawiane tam, gdzie należą", + "description": "Te przypomnienia ustawiasz obok tego, czego dotyczą.", + "doses": { + "title": "Dawki leków", + "description": "Godziny przyjmowania i przypomnienia ustawiasz przy każdym leku.", + "link": "Otwórz leki" + }, + "cycle": { + "title": "Cykl", + "description": "Przypomnienia o miesiączce i dniach płodnych włączasz dla każdego kanału w tabeli powiadomień.", + "link": "Otwórz tabelę powiadomień" + } + } }, "layout": { "title": "Układ", - "backToHub": "Powrót do układu", + "backToHub": "Wróć do Układu", "dashboard": { "title": "Pulpit", "description": "Układ i kolejność kafelków." @@ -5441,7 +5457,7 @@ "title": "Analizy", "description": "Kolejność przeglądu i pigułek nawigacyjnych." }, - "subtitle": "Wybierz, co pokazują pulpit, analizy i moduły śledzenia oraz w jakiej kolejności.", + "subtitle": "Wybierz kolejność i widok pulpitu, analiz i list śledzenia.", "medications": { "title": "Leki", "description": "Widok listy i ręczna kolejność." @@ -5458,10 +5474,6 @@ "title": "Badania kontrolne", "description": "Widok listy i kolejność przypomnień." }, - "mood": { - "title": "Tagi nastroju", - "description": "Grupy, tagi i zarchiwizowane tagi w wyborze nastroju." - }, "documents": { "title": "Dokumenty", "description": "Widok i układ miesięcy." @@ -5852,7 +5864,28 @@ }, "manageIn": "Zarządzaj w {section}", "operatorDisabled": "Wyłączone na całym serwerze", - "subtitle": "Wybierz, które obszary śledzenia pokazuje HealthLog w aplikacji." + "subtitle": "Wybierz, które obszary śledzenia pokazuje HealthLog w aplikacji.", + "backToHub": "Wróć do Modułów", + "openModuleSettings": "Ustawienia modułu", + "pages": { + "medications": { + "title": "Leki", + "description": "Miejsca wstrzyknięć pomijane w rotacji." + }, + "mood": { + "title": "Tagi nastroju", + "description": "Grupy, własne tagi i tagi zarchiwizowane." + }, + "labs": { + "title": "Badania", + "description": "Biomarkery i rozpoznawanie tekstu na urządzeniu dla wyników badań." + }, + "arrangementTitle": "Kolejność i widok", + "arrangementDescription": "Sortowanie i widok tej listy ustawiasz w Układzie.", + "openArrangement": "Otwórz w Układzie", + "moduleSettingsTitle": "Ustawienia modułu", + "moduleSettingsDescription": "Wszystko poza kolejnością i widokiem jest na stronie modułu w Modułach." + } }, "gesundheitsakte": { "title": "Dokumentacja zdrowotna", @@ -5995,6 +6028,10 @@ "access": { "title": "Udostępniony dostęp", "subtitle": "Kto może otworzyć twoją kartę zdrowia i czyją kartę możesz otworzyć ty." + }, + "preview": { + "title": "Funkcje w podglądzie", + "subtitle": "Wypróbuj to, co nie jest jeszcze gotowe. Wszystko tutaj pozostaje wyłączone, dopóki tego nie włączysz." } }, "profile": "Profil", @@ -7006,21 +7043,19 @@ }, "integrationSetupGuide": "Przewodnik konfiguracji", "coach": { - "activate": { - "title": "Włącz Coacha", - "description": "Pokazuje przycisk i panel Coacha.", - "toggleAria": "Włącz przycisk i panel Coacha", - "savedOn": "Coach włączony.", - "savedOff": "Coach ukryty.", - "saveError": "Nie udało się zapisać zmiany. Spróbuj ponownie." - }, "memory": { "edit": "Edytuj", "source": { "user": "od ciebie", "coach": "od Coacha" } - } + }, + "state": { + "title": "Coach", + "description": "Czy przycisk i panel Coacha są widoczne. Przełącznik jest w Modułach." + }, + "nudgesMoved": "Ustawiasz je razem z innymi przypomnieniami w Powiadomieniach.", + "openNudges": "Otwórz w Powiadomieniach" }, "integrationRedirectGuide": { "intro": "To połączenie działa przez Twoją własną aplikację deweloperską {provider}, około 5 minut konfiguracji:", @@ -7163,12 +7198,6 @@ "delivery": "Dziennik wysyłki powiadomień: {days} dni", "unavailable": "Szczegóły przechowywania są obecnie niedostępne." }, - "export": { - "title": "Pobierz moje dane", - "description": "Twoje rekordy jako CSV lub JSON albo pełna dokumentacja dla lekarza.", - "openExport": "Eksport i kopia zapasowa", - "openHealthRecord": "Dokumentacja medyczna" - }, "delete": { "title": "Usuń lub zresetuj", "description": "Usuń całe konto albo wyczyść dane zdrowotne, zachowując konto.", @@ -7184,7 +7213,6 @@ "mcp": { "enableTitle": "Zdalny punkt końcowy MCP", "enableDescription": "Zezwól asystentowi obsługującemu MCP (Claude.ai, ChatGPT) na odczyt danych zdrowotnych za pomocą ograniczonego tokenu.", - "enableToggleLabel": "Włącz punkt końcowy MCP", "endpointLabel": "Adres URL łącznika", "connectDescription": "Wklej ten adres URL w konfiguracji własnego łącznika asystenta, a następnie autoryzuj za pomocą poniższego tokenu lub zaloguj się po wyświetleniu monitu.", "tokensTitle": "Tokeny łącznika", @@ -7203,7 +7231,8 @@ "connectionRevokeFailed": "Nie udało się rozłączyć asystenta.", "tokensDetail": "Token jest wyświetlany tylko raz.", "connectionsDetail": "Cofnięcie połączenia natychmiast kończy dostęp, w tym możliwość odnawiania.", - "tokenNamePlaceholder": "Nazwa, np. Claude Desktop" + "tokenNamePlaceholder": "Nazwa, np. Claude Desktop", + "offKeepsTokens": "Wyłączenie wstrzymuje punkt końcowy. Tokeny i połączenia zostają i działają ponownie po włączeniu." }, "strava": "Strava", "stravaDescription": "Połącz konto Strava, aby synchronizować aktywności z historią treningów.", @@ -7465,7 +7494,25 @@ "passkeyAddedToast": "Dodano passkey.", "passkeyRemovedToast": "Usunięto passkey.", "integrationDisconnected": "Rozłączono {provider}.", - "integrationDisconnectFailed": "Nie udało się rozłączyć {provider}." + "integrationDisconnectFailed": "Nie udało się rozłączyć {provider}.", + "preview": { + "topics": { + "title": "Tematy (beta)", + "description": "Dodaje osobną pozycję Tematy ze stronami przeglądowymi nad stronami poszczególnych pomiarów.", + "detail": "Strony analiz, nawigacja i linki pozostają dokładnie takie, jakie są. Strony tematów pojawią się w późniejszej aktualizacji; ten przełącznik decyduje, czy je zobaczysz.", + "savedOn": "Podgląd tematów włączony.", + "savedOff": "Podgląd tematów wyłączony." + }, + "loadError": "Nie udało się wczytać ustawień podglądu.", + "feedbackLink": "Podziel się opinią w dyskusjach na GitHubie" + }, + "timezoneSaved": "Strefa czasowa zapisana.", + "moduleState": { + "on": "Włączone", + "off": "Wyłączone", + "change": "Zmień w Modułach" + }, + "timezoneUseDevice": "Użyj strefy czasowej tego urządzenia ({zone})" }, "admin": { "title": "Administracja", @@ -8855,6 +8902,10 @@ "detailedToast": "Powiadomienia zawierają teraz wartości zdrowotne.", "genericToast": "Powiadomienia nie zawierają już wartości zdrowotnych.", "saveError": "Nie udało się zapisać. Spróbuj ponownie później." + }, + "checkupReminders": { + "title": "Przypomnienia o badaniach kontrolnych", + "description": "Włączaj lub wyłączaj pojedyncze przypomnienia o badaniach." } }, "i18n": { @@ -9631,8 +9682,6 @@ "description": "Wpisz wartość; przypomnienie zostanie oznaczone jako wykonane." }, "manage": { - "title": "Zarządzaj przypomnieniami", - "description": "Włącz lub wyłącz poszczególne przypomnienia.", "empty": "Brak przypomnień do dostosowania." }, "dashboard": { diff --git a/next.config.ts b/next.config.ts index 0af3eb7aa..a844e2081 100644 --- a/next.config.ts +++ b/next.config.ts @@ -196,9 +196,16 @@ const nextConfig: NextConfig = { destination: "/settings/layout/medications", permanent: true, }, + // v1.44 — the mood page was tag management, which lives under + // Settings > Modules > Mood now; both older addresses go straight there. { source: "/settings/mood", - destination: "/settings/layout/mood", + destination: "/settings/modules/mood", + permanent: true, + }, + { + source: "/settings/layout/mood", + destination: "/settings/modules/mood", permanent: true, }, { diff --git a/src/__tests__/ai-input-invalidation-guard.test.ts b/src/__tests__/ai-input-invalidation-guard.test.ts index 6b890ceed..322dedbb3 100644 --- a/src/__tests__/ai-input-invalidation-guard.test.ts +++ b/src/__tests__/ai-input-invalidation-guard.test.ts @@ -104,8 +104,12 @@ describe("AI-input writes evict the capability answer", () => { it("finds the write sites it is meant to hold (an empty sweep is a broken matcher)", () => { const total = sites.reduce((n, s) => n + s.writes.length, 0); - expect(sites.length).toBeGreaterThanOrEqual(21); - expect(total).toBeGreaterThanOrEqual(22); + // v1.44 — three write sites left on purpose: the Coach's second switch + // and the MCP page's second switch (the one switch is in Modules), and + // the Coach overlay's copy of the provider and model selects (chosen in + // Settings > AI only). + expect(sites.length).toBeGreaterThanOrEqual(19); + expect(total).toBeGreaterThanOrEqual(19); }); it.each(sites.map((s) => [s.rel, s] as const))( diff --git a/src/__tests__/customize-shortcut-guard.test.ts b/src/__tests__/customize-shortcut-guard.test.ts index 9e4f93341..4fe9624ea 100644 --- a/src/__tests__/customize-shortcut-guard.test.ts +++ b/src/__tests__/customize-shortcut-guard.test.ts @@ -39,8 +39,15 @@ import { describe, expect, it } from "vitest"; const SRC = join(process.cwd(), "src"); -/** The prefix every customise shortcut points at. */ -const LAYOUT_PREFIX = "/settings/layout/"; +/** + * The prefixes a customise shortcut points at: a module's order and view + * under Arrangement, or (since v1.44) its own page under Modules. + */ +const SHORTCUT_PREFIXES = ["/settings/layout/", "/settings/modules/"]; + +function isShortcut(text: string): boolean { + return SHORTCUT_PREFIXES.some((prefix) => text.startsWith(prefix)); +} /** The answer a shared-record surface withholds these on: `!inSharedRecord`. */ const GUARD = "inSharedRecord"; @@ -62,6 +69,8 @@ const NOT_A_SHARED_DESTINATION: Record = { "The Insights page itself. Same flag, same reason.", "components/insights/insights-edit-mode.tsx": "The Insights arrange mode, reachable only from the page above.", + "components/settings/settings-anchor-forward.tsx": + "Not a shortcut: the table of old Settings anchors and where they moved, read on a Settings page, which the record gate already answers for.", }; function allSourceFiles(): string[] { @@ -127,7 +136,7 @@ function unguardedShortcuts(source: string, fileName: string): string[] { const visit = (node: ts.Node): void => { if ( ts.isStringLiteralLike(node) && - node.text.startsWith(LAYOUT_PREFIX) && + isShortcut(node.text) && !guardedByOwnRecord(node) ) { found.push(node.text); @@ -140,7 +149,9 @@ function unguardedShortcuts(source: string, fileName: string): string[] { describe("the customise shortcut is withheld inside somebody else's record", () => { const carrying = allSourceFiles().filter((rel) => - readFileSync(join(SRC, rel), "utf8").includes(LAYOUT_PREFIX), + SHORTCUT_PREFIXES.some((prefix) => + readFileSync(join(SRC, rel), "utf8").includes(prefix), + ), ); it("finds the surfaces that carry one", () => { diff --git a/src/__tests__/destructive-control-guard.test.ts b/src/__tests__/destructive-control-guard.test.ts index 75ed78be4..b5d72fd0d 100644 --- a/src/__tests__/destructive-control-guard.test.ts +++ b/src/__tests__/destructive-control-guard.test.ts @@ -162,7 +162,7 @@ const REGISTRY: DestructiveEntry[] = [ file: "components/settings/mcp-section.tsx", destroys: "an MCP client grant or an MCP token", recovery: "permanent", - confirm: ["AlertDialog"], + confirm: ["ConfirmButton"], }, { file: "components/settings/sharing-section.tsx", diff --git a/src/__tests__/managed-record-navigation.test.tsx b/src/__tests__/managed-record-navigation.test.tsx index 8653150cf..d6c1f14be 100644 --- a/src/__tests__/managed-record-navigation.test.tsx +++ b/src/__tests__/managed-record-navigation.test.tsx @@ -14,6 +14,7 @@ const recordRef: { value: { kind: "managed" | "shared"; level: string } } = { vi.mock("next/navigation", () => ({ usePathname: () => pathnameRef.value, + useRouter: () => ({ replace: () => {}, push: () => {} }), })); vi.mock("@/hooks/use-auth", () => ({ @@ -112,7 +113,9 @@ describe("managed record navigation", () => { expect( html.match(new RegExp(`href="/settings/${section.slug}"`, "g")) ?.length ?? 0, - ).toBe(2); + // The sidebar carries the link; the phone's section sheet mounts its + // list only when opened (v1.44). + ).toBe(1); } for (const section of unavailableDestinations) { diff --git a/src/__tests__/resolved-field-consumer-guard.test.ts b/src/__tests__/resolved-field-consumer-guard.test.ts index 397a6af7f..bd75538a8 100644 --- a/src/__tests__/resolved-field-consumer-guard.test.ts +++ b/src/__tests__/resolved-field-consumer-guard.test.ts @@ -142,8 +142,6 @@ const RULES: ResolvedFieldRule[] = [ 'import { resolveAiCapability } from "@/lib/ai/capabilities/resolve";', ], allow: { - "src/components/settings/ai/disable-coach-card.tsx": - "the Activate-Coach switch: it edits the stored opt-out, so its checked state is that value", "src/hooks/use-module-toggle.ts": "the module switch for the Coach (Settings hub and command palette): its checked state is the stored opt-out it writes", "src/components/settings/managed-record-settings-section.tsx": diff --git a/src/__tests__/settings-legacy-routes.test.ts b/src/__tests__/settings-legacy-routes.test.ts new file mode 100644 index 000000000..ffba71623 --- /dev/null +++ b/src/__tests__/settings-legacy-routes.test.ts @@ -0,0 +1,191 @@ +/** + * Every Settings address a person ever had keeps working. + * + * Settings has been regrouped many times (v1.4 split, v1.17 layout hub, + * v1.25 Darstellung, v1.44 nine groups), and every bookmark, cross-link in + * an older notification and docs page still points at one of those shapes. + * This walks the full list of historical `/settings/*` addresses through the + * redirects in `next.config.ts` and asserts each one lands on a route that + * renders: a section slug, an Arrangement sub-page or a module page. It does + * the same for the anchors a page forwards client-side when a card moved + * while its page stayed (`settings-anchor-forward.tsx`). + * + * A route removed without a redirect, a redirect pointing at a page that no + * longer exists, or a forward whose target 404s fails here by name. + */ +import { describe, expect, it } from "vitest"; + +import nextConfig from "../../next.config"; +import { SETTINGS_SECTION_SLUGS } from "@/components/settings/section-slugs"; +import { LAYOUT_GROUP_IDS } from "@/components/settings/layout-groups"; +import { MODULE_SETTINGS_PAGE_IDS } from "@/components/settings/module-settings-page-ids"; +import { + SETTINGS_ANCHOR_FORWARDS, + forwardedAnchorTarget, +} from "@/components/settings/settings-anchor-forward"; + +/** Every Settings path that was ever reachable, newest shapes included. */ +const HISTORICAL_SETTINGS_PATHS: readonly string[] = [ + "/settings", + // Section slugs of every release since the v1.4 split. + "/settings/account", + "/settings/security", + "/settings/access", + "/settings/modules", + "/settings/integrations", + "/settings/sources", + "/settings/channels", + "/settings/notifications", + "/settings/reminders", + "/settings/layout", + "/settings/environment", + "/settings/anamnesis", + "/settings/score", + "/settings/thresholds", + "/settings/ai", + "/settings/coach", + "/settings/api", + "/settings/mcp", + "/settings/gesundheitsakte", + "/settings/sharing", + "/settings/export", + "/settings/advanced", + "/settings/privacy", + "/settings/about", + // The per-module personalisation slugs before the layout hub. + "/settings/dashboard", + "/settings/insights", + "/settings/medications", + "/settings/mood", + "/settings/labs", + "/settings/illness", + "/settings/vorsorge", + // Layout hub sub-pages since v1.25.11. + "/settings/layout/dashboard", + "/settings/layout/insights", + "/settings/layout/medications", + "/settings/layout/mood", + "/settings/layout/labs", + "/settings/layout/illness", + "/settings/layout/documents", + "/settings/layout/vorsorge", +]; + +interface Redirect { + source: string; + destination: string; +} + +async function settingsRedirects(): Promise { + const all = (await nextConfig.redirects?.()) ?? []; + return all + .filter((r) => r.source.startsWith("/settings")) + .map((r) => ({ source: r.source, destination: r.destination })); +} + +/** Follow exact-match redirects until none applies; the hash is dropped. */ +function resolve(path: string, redirects: readonly Redirect[]): string { + let current = path; + for (let hop = 0; hop < 5; hop += 1) { + const next = redirects.find((r) => r.source === current); + if (!next) return current; + current = next.destination.split("#")[0]!; + } + throw new Error(`redirect loop from ${path}`); +} + +/** Does a path (no hash) render a Settings page? */ +function renders(path: string): boolean { + if (path === "/settings") return true; // the page redirects to account + const parts = path.split("/").filter(Boolean); + if (parts[0] !== "settings") return false; + if (parts.length === 2) { + return (SETTINGS_SECTION_SLUGS as readonly string[]).includes(parts[1]!); + } + if (parts.length === 3 && parts[1] === "layout") { + return LAYOUT_GROUP_IDS.includes(parts[2]!); + } + if (parts.length === 3 && parts[1] === "modules") { + return (MODULE_SETTINGS_PAGE_IDS as readonly string[]).includes(parts[2]!); + } + return false; +} + +describe("historical Settings addresses", () => { + it("reads the redirects from next.config.ts (an empty read is a broken matcher)", async () => { + const redirects = await settingsRedirects(); + expect(redirects.length).toBeGreaterThanOrEqual(10); + }); + + it.each(HISTORICAL_SETTINGS_PATHS)( + "%s still lands on a page", + async (path) => { + const redirects = await settingsRedirects(); + const landed = resolve(path, redirects); + expect(renders(landed), `${path} → ${landed}`).toBe(true); + }, + ); + + it("every redirect into Settings points at a page that renders", async () => { + for (const r of await settingsRedirects()) { + const target = r.destination.split("#")[0]!; + expect(renders(target), `${r.source} → ${r.destination}`).toBe(true); + } + }); + + it("every routable section, sub-page and module page is on the list", () => { + const routable = [ + ...SETTINGS_SECTION_SLUGS.map((s) => `/settings/${s}`), + ...LAYOUT_GROUP_IDS.map((id) => `/settings/layout/${id}`), + ...MODULE_SETTINGS_PAGE_IDS.map((id) => `/settings/modules/${id}`), + ]; + // New routes join the list when they ship, so a later move cannot drop + // one silently. Module pages are new in v1.44 and are allowed here. + const listed = new Set(HISTORICAL_SETTINGS_PATHS); + const unlisted = routable.filter( + (path) => + !listed.has(path) && + !path.startsWith("/settings/modules/") && + path !== "/settings/preview", + ); + expect(unlisted).toEqual([]); + }); + + it("keeps the v1.44 moves addressable", async () => { + const redirects = await settingsRedirects(); + expect(resolve("/settings/layout/mood", redirects)).toBe( + "/settings/modules/mood", + ); + expect(resolve("/settings/mood", redirects)).toBe("/settings/modules/mood"); + }); +}); + +describe("anchors forwarded after a card moved", () => { + const forwards = Object.entries(SETTINGS_ANCHOR_FORWARDS).flatMap( + ([page, anchors]) => + Object.entries(anchors).map(([anchor, target]) => ({ + page, + anchor, + target, + })), + ); + + it("has forwards to check", () => { + expect(forwards.length).toBeGreaterThanOrEqual(8); + }); + + it.each(forwards)( + "$page#$anchor forwards to a page that renders", + ({ page, anchor, target }) => { + expect(renders(page), page).toBe(true); + expect(forwardedAnchorTarget(page, `#${anchor}`)).toBe(target); + expect(renders(target.split("#")[0]!), target).toBe(true); + }, + ); + + it("leaves an anchor that did not move alone", () => { + expect(forwardedAnchorTarget("/settings/privacy", "#retention")).toBeNull(); + expect(forwardedAnchorTarget("/settings/security", "#sessions")).toBeNull(); + expect(forwardedAnchorTarget("/settings/privacy", "")).toBeNull(); + }); +}); diff --git a/src/__tests__/success-affordance-guard.test.ts b/src/__tests__/success-affordance-guard.test.ts index 2b78f8702..858a950ab 100644 --- a/src/__tests__/success-affordance-guard.test.ts +++ b/src/__tests__/success-affordance-guard.test.ts @@ -382,7 +382,7 @@ const PINNED_AFFORDANCES: Record< }, "src/components/settings/mcp-section.tsx": { "text-success": 4, - "toast.success": 2, + "toast.success": 1, }, "src/components/settings/notification-status-card.tsx": { CheckCircle2: 2, diff --git a/src/app/labs/page.tsx b/src/app/labs/page.tsx index 2fde494b0..74316b483 100644 --- a/src/app/labs/page.tsx +++ b/src/app/labs/page.tsx @@ -153,7 +153,7 @@ export default function LabsPage() { className="min-h-11 min-w-11 sm:min-h-9 sm:min-w-9" > diff --git a/src/app/mood/page-client.tsx b/src/app/mood/page-client.tsx index a5cf60065..33f62f98c 100644 --- a/src/app/mood/page-client.tsx +++ b/src/app/mood/page-client.tsx @@ -150,7 +150,7 @@ export default function MoodPageClient() { className="min-h-11 min-w-11 sm:min-h-9 sm:min-w-9" > diff --git a/src/app/settings/[section]/page.tsx b/src/app/settings/[section]/page.tsx index e4834c44b..c58be6aeb 100644 --- a/src/app/settings/[section]/page.tsx +++ b/src/app/settings/[section]/page.tsx @@ -44,7 +44,7 @@ export default async function SettingsSectionPage({ params }: PageProps) { } // Each section is its own chunk (see `SettingsSectionLoader`), so the route - // ships the one it shows rather than all twenty-one. Integrations is the + // ships the one it shows rather than all of them. Integrations is the // one section that needs a server-resolved value: the OAuth callback URL // each provider registers, read from the runtime env here, per request, // because a `NEXT_PUBLIC_*` read inside a client module is inlined at build diff --git a/src/app/settings/layout/[module]/page.tsx b/src/app/settings/layout/[module]/page.tsx index b0e82d825..a220b1265 100644 --- a/src/app/settings/layout/[module]/page.tsx +++ b/src/app/settings/layout/[module]/page.tsx @@ -9,6 +9,8 @@ import { isLayoutGroupId, } from "@/components/settings/layout-groups"; import { SettingsShell } from "@/components/settings/settings-shell"; +import { ModuleSettingsLinkCard } from "@/components/settings/module-settings-pages"; +import { isModuleSettingsPageId } from "@/components/settings/module-settings-page-ids"; import { SettingsHubBackLink } from "@/components/settings/settings-hub-back-link"; import { resolveServerLocale } from "@/lib/i18n/server-locale"; import { getServerTranslator } from "@/lib/i18n/server-translator"; @@ -86,6 +88,11 @@ export default async function SettingsLayoutModulePage({ params }: PageProps) {
+ {/* v1.44 — what the module needs beyond order and view lives + on its page under Modules; say where. */} + {isModuleSettingsPageId(group.id) ? ( + + ) : null}
diff --git a/src/app/settings/modules/[module]/page.tsx b/src/app/settings/modules/[module]/page.tsx new file mode 100644 index 000000000..9eb38daa9 --- /dev/null +++ b/src/app/settings/modules/[module]/page.tsx @@ -0,0 +1,72 @@ +import { notFound } from "next/navigation"; + +import { LayoutModuleGate } from "@/components/settings/layout-module-gate"; +import { RecordSettingsSectionGate } from "@/components/settings/record-settings-section-gate"; +import { ModuleSettingsPageBody } from "@/components/settings/module-settings-pages"; +import { + MODULE_SETTINGS_PAGE_IDS, + isModuleSettingsPageId, + moduleSettingsPageKeys, + moduleSettingsPageModule, +} from "@/components/settings/module-settings-page-ids"; +import { SettingsShell } from "@/components/settings/settings-shell"; +import { SettingsHubBackLink } from "@/components/settings/settings-hub-back-link"; +import { resolveServerLocale } from "@/lib/i18n/server-locale"; +import { getServerTranslator } from "@/lib/i18n/server-translator"; + +/** + * A module's own settings: `/settings/modules/`. + * + * What a module needs beyond order and view (the labs biomarker catalog and + * on-device text recognition, the mood tags, the injection sites) lives here, + * under Settings > Modules, with a back link to the Modules list. Pre-rendered + * for the known ids; any other segment 404s at routing time. + */ + +export const dynamicParams = false; + +export function generateStaticParams() { + return MODULE_SETTINGS_PAGE_IDS.map((module) => ({ module })); +} + +interface PageProps { + params: Promise<{ module: string }>; +} + +export default async function SettingsModulePage({ params }: PageProps) { + const { module } = await params; + if (!isModuleSettingsPageId(module)) notFound(); + + const locale = await resolveServerLocale(); + const { t } = getServerTranslator(locale); + const { titleKey, descriptionKey } = moduleSettingsPageKeys(module); + const headingId = `settings-section-modules-${module}-title`; + + return ( + + ), + }} + > + {/* The cards here lived under Arrangement until v1.44 and keep its + posture inside somebody else's record, so the gate is asked about + `layout`, not about the Modules switches. */} + +
+ + + +
+
+
+ ); +} diff --git a/src/components/insights/coach-panel/__tests__/coach-model-picker.test.tsx b/src/components/insights/coach-panel/__tests__/coach-model-picker.test.tsx index d24b0fca0..be9b6a4f5 100644 --- a/src/components/insights/coach-panel/__tests__/coach-model-picker.test.tsx +++ b/src/components/insights/coach-panel/__tests__/coach-model-picker.test.tsx @@ -1,24 +1,8 @@ import { describe, expect, it } from "vitest"; -import type { - ProviderChainData, - UserAIProvider, -} from "@/components/settings/ai/shared"; +import type { UserAIProvider } from "@/components/settings/ai/shared"; -import { - chainWithFirst, - modelControlFor, - selectableProviders, - switchDidNotTake, -} from "../coach-model-picker"; - -const CHAIN: ProviderChainData["configuredChain"] = [ - { providerType: "codex", enabled: true, available: false }, - { providerType: "openai", enabled: true, available: true }, - { providerType: "anthropic", enabled: false, available: true }, - { providerType: "local", enabled: true, available: true }, - { providerType: "admin-openai", enabled: true, available: true }, -]; +import { modelControlFor } from "../coach-model-picker"; function userProvider(over: Partial = {}): UserAIProvider { return { @@ -40,55 +24,6 @@ function userProvider(over: Partial = {}): UserAIProvider { }; } -describe("chainWithFirst", () => { - it("moves the chosen provider to the front and keeps the rest in order", () => { - expect(chainWithFirst(CHAIN, "local")).toEqual([ - { providerType: "local", priority: 1, enabled: true }, - { providerType: "codex", priority: 2, enabled: true }, - { providerType: "openai", priority: 3, enabled: true }, - { providerType: "anthropic", priority: 4, enabled: false }, - { providerType: "admin-openai", priority: 5, enabled: true }, - ]); - }); - - it("never sends a reasoning setting, so the server keeps the stored one", () => { - for (const entry of chainWithFirst(CHAIN, "openai")) { - expect(Object.keys(entry).sort()).toEqual([ - "enabled", - "priority", - "providerType", - ]); - } - }); -}); - -describe("selectableProviders", () => { - it("offers only enabled providers that can answer", () => { - expect(selectableProviders(CHAIN)).toEqual([ - "openai", - "local", - "admin-openai", - ]); - }); -}); - -describe("switchDidNotTake", () => { - it("is quiet when the chosen provider leads the chain the server resolves", () => { - expect(switchDidNotTake("openai", { activeProvider: "openai" })).toBe( - false, - ); - }); - - it("speaks up when the server still answers with another provider", () => { - // A provider without a working key was saved to the front of the chain; - // the resolver skipped it and the select would jump back silently. - expect(switchDidNotTake("anthropic", { activeProvider: "openai" })).toBe( - true, - ); - expect(switchDidNotTake("anthropic", { activeProvider: null })).toBe(true); - }); -}); - describe("modelControlFor", () => { it("offers the presets for the provider whose key the person saved", () => { const control = modelControlFor("openai", userProvider()); diff --git a/src/components/insights/coach-panel/__tests__/coach-settings-overlay.test.tsx b/src/components/insights/coach-panel/__tests__/coach-settings-overlay.test.tsx index 726f4b5d3..dfd13f529 100644 --- a/src/components/insights/coach-panel/__tests__/coach-settings-overlay.test.tsx +++ b/src/components/insights/coach-panel/__tests__/coach-settings-overlay.test.tsx @@ -70,41 +70,32 @@ describe("", () => { expect(html).toMatch(/href="\/settings\/ai"[^>]*>All AI settings { + it("shows the provider and model in effect and links to the one place they are chosen", () => { const html = render(); - const select = html.match( - /]*data-slot="coach-quick-provider"[\s\S]*?<\/select>/, - )?.[0]; - expect(select).toBeDefined(); - expect(select).toContain('value="local"'); - expect(select).toContain('value="admin-openai"'); - expect(select).not.toContain('value="codex"'); + expect(html).toMatch( + /data-slot="coach-quick-provider"[^>]*data-value="local"/, + ); + expect(html).toMatch( + /data-slot="coach-quick-model"[^>]*data-value="qwen2.5"/, + ); + expect(html).toContain('href="/settings/ai#ai-provider"'); + // v1.44 — no second copy of the selects here. + expect(html).not.toMatch(/]*data-slot="coach-quick-provider"/); + expect(html).not.toMatch(/]*data-slot="coach-quick-model"/); + expect(html).toContain('id="coach-quick-reasoning"'); + // Keys and base URLs are not here. + expect(html).not.toContain("http://localhost:11434/v1"); + expect(html).not.toMatch(/type="password"/); }); - it("shows the operator's central Codex as the active provider when it answers", () => { - // `admin-codex` is never a chain entry, so no option matched it and the - // select showed its first option as the one answering. + it("names the operator's central Codex when it answers", () => { const html = render(, { ...CHAIN, activeProvider: "admin-codex" as ProviderChainData["activeProvider"], }); - const select = html.match( - /]*data-slot="coach-quick-provider"[\s\S]*?<\/select>/, - )?.[0]; - expect(select).toMatch( - /