From 31101a69e7e2aa37e429664ddf3ae8aabb7c4c8e Mon Sep 17 00:00:00 2001 From: chauhan_s Date: Tue, 17 Mar 2026 20:58:16 +0530 Subject: [PATCH 1/4] fix: Refine verify email dialog for `verify` and `change` email flows (#14671) ### Summary This PR improves the verify email dialog by giving the verify-email and change-email flows distinct messaging instead of reusing the same generic copy. ### What changed * Use flow-specific body copy in the verify email dialog * Keep the existing action-specific subtitle behavior for: * Verify email * Change email * Update the English i18n strings so each flow explains the correct intent: * Verify email focuses on confirming email ownership * Change email focuses on securely starting the email-change process ### Why The previous dialog message was shared across both flows, which made the change-email experience feel ambiguous. This update makes the intent clearer for users and better matches the action they are taking. https://www.loom.com/share/c64c20570a8242358bd178a2ac50e413 ## Summary by CodeRabbit * **Bug Fixes** * Improved clarity in email verification and email change dialog messages to better explain the confirmation process and link purpose. * Enhanced distinction between email verification and email change workflows with context-specific messaging. --- .../core/src/desktop/dialogs/verify-email/index.tsx | 12 ++++++++++-- packages/frontend/i18n/src/resources/en.json | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/frontend/core/src/desktop/dialogs/verify-email/index.tsx b/packages/frontend/core/src/desktop/dialogs/verify-email/index.tsx index a95db4456c916..a079aaca1055b 100644 --- a/packages/frontend/core/src/desktop/dialogs/verify-email/index.tsx +++ b/packages/frontend/core/src/desktop/dialogs/verify-email/index.tsx @@ -108,10 +108,18 @@ export const VerifyEmailDialog = ({ > -

{t['com.affine.auth.verify.email.message']({ email })}

+

+ {changeEmail + ? t['com.affine.auth.change.email.message']({ email }) + : t['com.affine.auth.verify.email.message']({ email })} +

Download the AFFiNE Client for the latest features and Performance.", "com.affine.banner.local-warning": "Your local data is stored in the browser and may be lost. Don't risk it - enable cloud now!", From cc2f23339ea425f6295cbd94f11c556a92a41705 Mon Sep 17 00:00:00 2001 From: steffenrapp <88974099+steffenrapp@users.noreply.github.com> Date: Tue, 17 Mar 2026 16:28:36 +0100 Subject: [PATCH 2/4] feat(i18n): update German translation (#14674) ## Summary by CodeRabbit * **Documentation** * Enhanced German language support with new translations for Obsidian import, MCP server integration, and Copilot features. Improved error message translations for better clarity and consistency. --- packages/frontend/i18n/src/resources/de.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/frontend/i18n/src/resources/de.json b/packages/frontend/i18n/src/resources/de.json index f9e7b6a773efb..9d61986376525 100644 --- a/packages/frontend/i18n/src/resources/de.json +++ b/packages/frontend/i18n/src/resources/de.json @@ -622,6 +622,8 @@ "com.affine.import.modal.tip": "Wenn du Unterstützung für zusätzliche Dateitypen anfordern möchtest, lass es uns gerne wissen auf", "com.affine.import.notion": "Notion", "com.affine.import.notion.tooltip": "Importiere deine Notion-Daten. Unterstützte Importformate: HTML mit Unterseiten.", + "com.affine.import.obsidian": "Obsidian-Vault", + "com.affine.import.obsidian.tooltip": "Importiere einen Obsidian-Vault. Wähle einen Ordner aus, um alle Notizen, Bilder und Assets mit aufgelösten Wiki-Links zu importieren.", "com.affine.import.snapshot": "Snapshot", "com.affine.import.snapshot.tooltip": "Importiere deine AFFiNE-Workspace- und Seiten-Snapshot-Datei.", "com.affine.import.dotaffinefile": ".affine-Datei", @@ -2129,6 +2131,7 @@ "com.affine.integration.calendar.no-calendar": "Noch keine abonnierten Kalender.", "com.affine.integration.mcp-server.name": "MCP-Server", "com.affine.integration.mcp-server.desc": "Anderen MCP-Clients ermöglichen, die Seite von AFFiNE zu suchen und zu lesen.", + "com.affine.integration.mcp-server.copy-json.disabled-hint": "Der MCP-Token wird nur einmal angezeigt. Lösche ihn und erstelle ihn neu, um die JSON-Konfiguration zu kopieren.", "com.affine.audio.notes": "Notizen", "com.affine.audio.transcribing": "Transkription läuft", "com.affine.audio.transcribe.non-owner.confirm.title": "KI-Ergebnisse für andere können nicht abgerufen werden", @@ -2176,6 +2179,7 @@ "error.SSRF_BLOCKED_ERROR": "Ungültige URL", "error.RESPONSE_TOO_LARGE_ERROR": "Antwort zu groß ({{receivedBytes}} Bytes), Limit beträgt {{limitBytes}} Bytes", "error.EMAIL_SERVICE_NOT_CONFIGURED": "E-Mail-Dienst ist nicht konfiguriert.", + "error.IMAGE_FORMAT_NOT_SUPPORTED": "Bildformat nicht unterstützt: {{format}}", "error.QUERY_TOO_LONG": "Abfrage ist zu lang, die maximale Länge beträgt {{max}}.", "error.VALIDATION_ERROR": "Validierungsfehler, Fehler: {{errors}}", "error.USER_NOT_FOUND": "Benutzer nicht gefunden.", @@ -2291,7 +2295,7 @@ "error.CANNOT_DELETE_ACCOUNT_WITH_OWNED_TEAM_WORKSPACE": "Konto kann nicht gelöscht werden. Du bist Besitzer eines oder mehrerer Team-Workspaces. Bitte übertrage den Besitz oder lösche die Workspaces zuerst.", "error.CAPTCHA_VERIFICATION_FAILED": "Captcha-Überprüfung fehlgeschlagen.", "error.INVALID_LICENSE_SESSION_ID": "Ungültige Sitzungs-ID zur Erstellung des Lizenzschlüssels.", - "error.LICENSE_REVEALED": "Der Lizenzschlüssel wurde aufgedeckt. Bitte überprüfe dein Postfach, das du beim Bezahlvorgang angegeben hast.", + "error.LICENSE_REVEALED": "Der Lizenzschlüssel wurde angezeigt. Bitte überprüfe dein Postfach, das du beim Bezahlvorgang angegeben hast.", "error.WORKSPACE_LICENSE_ALREADY_EXISTS": "Workspace hat bereits eine Lizenz angewendet.", "error.LICENSE_NOT_FOUND": "Lizenz nicht gefunden.", "error.INVALID_LICENSE_TO_ACTIVATE": "Ungültige Lizenz zum Aktivieren. {{reason}}", From bbcb7e69fec7746ed2f0568d9f6e4a039fe4adaf Mon Sep 17 00:00:00 2001 From: chauhan_s Date: Tue, 17 Mar 2026 20:59:28 +0530 Subject: [PATCH 3/4] fix: correct "has accept" to "has accepted" (#14669) fixes #14407 ## Summary by CodeRabbit * **Bug Fixes** * Corrected grammar in the notification message displayed when an invitation is accepted. --- packages/frontend/i18n/src/resources/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index 10b02617a4bb3..73b10ed627707 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -1994,7 +1994,7 @@ "com.affine.notification.empty": "No new notifications", "com.affine.notification.loading-more": "Loading more...", "com.affine.notification.empty.description": "You'll be notified here for @mentions and workspace invites.", - "com.affine.notification.invitation-accepted": "<1>{{username}} has accept your invitation", + "com.affine.notification.invitation-accepted": "<1>{{username}} has accepted your invitation", "com.affine.notification.invitation-review-request": "<1>{{username}} has requested to join <2>{{workspaceName}}", "com.affine.notification.invitation-review-declined": "<1>{{username}} has declined your request to join <2>{{workspaceName}}", "com.affine.notification.invitation-review-approved": "<1>{{username}} has approved your request to join <2>{{workspaceName}}", From 1112a066237d88b257a7470c6815296d7145dd0b Mon Sep 17 00:00:00 2001 From: DarkSky Date: Tue, 17 Mar 2026 23:32:57 +0800 Subject: [PATCH 4/4] fix: ci --- .../src/plugins/copilot/providers/fal.ts | 18 ++++++++++++------ .../src/plugins/copilot/providers/openai.ts | 12 ++++++++---- packages/frontend/i18n/src/i18n.gen.ts | 6 +++--- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/packages/backend/server/src/plugins/copilot/providers/fal.ts b/packages/backend/server/src/plugins/copilot/providers/fal.ts index b6927a141d404..d875ac8bf2388 100644 --- a/packages/backend/server/src/plugins/copilot/providers/fal.ts +++ b/packages/backend/server/src/plugins/copilot/providers/fal.ts @@ -258,7 +258,7 @@ export class FalProvider extends CopilotProvider { const model = this.selectModel(cond); try { - metrics.ai.counter('chat_text_calls').add(1, { model: model.id }); + metrics.ai.counter('chat_text_calls').add(1, this.metricLabels(model.id)); // by default, image prompt assumes there is only one message const prompt = this.extractPrompt(messages[messages.length - 1]); @@ -283,7 +283,9 @@ export class FalProvider extends CopilotProvider { } return data.output; } catch (e: any) { - metrics.ai.counter('chat_text_errors').add(1, { model: model.id }); + metrics.ai + .counter('chat_text_errors') + .add(1, this.metricLabels(model.id)); throw this.handleError(e); } } @@ -296,12 +298,16 @@ export class FalProvider extends CopilotProvider { const model = this.selectModel(cond); try { - metrics.ai.counter('chat_text_stream_calls').add(1, { model: model.id }); + metrics.ai + .counter('chat_text_stream_calls') + .add(1, this.metricLabels(model.id)); const result = await this.text(cond, messages, options); yield result; } catch (e) { - metrics.ai.counter('chat_text_stream_errors').add(1, { model: model.id }); + metrics.ai + .counter('chat_text_stream_errors') + .add(1, this.metricLabels(model.id)); throw e; } } @@ -319,7 +325,7 @@ export class FalProvider extends CopilotProvider { try { metrics.ai .counter('generate_images_stream_calls') - .add(1, { model: model.id }); + .add(1, this.metricLabels(model.id)); // by default, image prompt assumes there is only one message const prompt = this.extractPrompt( @@ -376,7 +382,7 @@ export class FalProvider extends CopilotProvider { } catch (e) { metrics.ai .counter('generate_images_stream_errors') - .add(1, { model: model.id }); + .add(1, this.metricLabels(model.id)); throw this.handleError(e); } } diff --git a/packages/backend/server/src/plugins/copilot/providers/openai.ts b/packages/backend/server/src/plugins/copilot/providers/openai.ts index 318d0845e4b75..1c77626cfacce 100644 --- a/packages/backend/server/src/plugins/copilot/providers/openai.ts +++ b/packages/backend/server/src/plugins/copilot/providers/openai.ts @@ -664,7 +664,7 @@ export class OpenAIProvider extends CopilotProvider { const model = this.selectModel(normalizedCond); try { - metrics.ai.counter('chat_text_calls').add(1, { model: model.id }); + metrics.ai.counter('chat_text_calls').add(1, this.metricLabels(model.id)); const backendConfig = this.createNativeConfig(); const middleware = this.getActiveProviderMiddleware(); const cap = this.getAttachCapability(model, ModelOutputType.Structured); @@ -687,7 +687,9 @@ export class OpenAIProvider extends CopilotProvider { const validated = schema.parse(parsed); return JSON.stringify(validated); } catch (e: any) { - metrics.ai.counter('chat_text_errors').add(1, { model: model.id }); + metrics.ai + .counter('chat_text_errors') + .add(1, this.metricLabels(model.id)); throw this.handleError(e); } } @@ -983,7 +985,7 @@ export class OpenAIProvider extends CopilotProvider { metrics.ai .counter('generate_images_stream_calls') - .add(1, { model: model.id }); + .add(1, this.metricLabels(model.id)); const { content: prompt, attachments } = [...messages].pop() || {}; if (!prompt) throw new CopilotPromptInvalid('Prompt is required'); @@ -1021,7 +1023,9 @@ export class OpenAIProvider extends CopilotProvider { } return; } catch (e: any) { - metrics.ai.counter('generate_images_errors').add(1, { model: model.id }); + metrics.ai + .counter('generate_images_errors') + .add(1, this.metricLabels(model.id)); throw this.handleError(e); } } diff --git a/packages/frontend/i18n/src/i18n.gen.ts b/packages/frontend/i18n/src/i18n.gen.ts index ad38936bcca34..91278a4f99c97 100644 --- a/packages/frontend/i18n/src/i18n.gen.ts +++ b/packages/frontend/i18n/src/i18n.gen.ts @@ -1095,7 +1095,7 @@ export function useAFFiNEI18N(): { */ ["com.affine.appearanceSettings.showLinkedDocInSidebar.description"](): string; /** - * `Your current email is {{email}}. We'll send a temporary verification link to this email.` + * `Your current email is {{email}}. We'll send a confirmation link there first so you can securely switch to a new email address.` */ ["com.affine.auth.change.email.message"](options: { readonly email: string; @@ -1427,7 +1427,7 @@ export function useAFFiNEI18N(): { */ ["com.affine.auth.toast.title.signed-in"](): string; /** - * `Your current email is {{email}}. We'll send a temporary verification link to this email.` + * `Your current email is {{email}}. We'll send a verification link to this email so you can confirm it belongs to you.` */ ["com.affine.auth.verify.email.message"](options: { readonly email: string; @@ -9897,7 +9897,7 @@ export const TypedTrans: { ["2"]: JSX.Element; }>>; /** - * `<1>{{username}} has accept your invitation` + * `<1>{{username}} has accepted your invitation` */ ["com.affine.notification.invitation-accepted"]: ComponentType