mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
* fix(web): align export and update-check error copy with product (OPEND-2849)
- S26a: a generic export failure now shows the product title + body
("导出失败" / "本次导出未完成,请重新尝试。") through the same Toast
message + details shape as S26c. fileViewer.exportFailed is split into
fileViewer.exportFailedTitle / fileViewer.exportFailedDescription in all
19 locales; the old key is removed.
- S31b: drop the sentence-final period from the title line of
updater.dialogCheckFailed (zh-CN and 17 other locales; th had none).
* fix(web): never surface raw exporter errors on export failure (OPEND-2849)
Product copy S26: an export failure shows the S26a title + body by default;
only an already-localized, user-facing reason may replace it. Raw exporter
strings (HTTP status lines, renderer/daemon errors, internal exceptions)
no longer reach the toast.
- New export-failure-toast module: LocalizedExportError marks errors whose
message comes from an i18n key; exportFailureToast passes only those
through and falls back to S26a for everything else.
- Localized reasons now thrown as LocalizedExportError: PPTX daemon
unreachable / PPTX not available, version image capture failed.
- Image export catch-all uses the same rule instead of echoing err.message.
- The original error object is unchanged, so console.warn and
exportErrorCode analytics still see the raw message.
* fix(web): align disk-write failure and browser-open hint copy with product (OPEND-2849)
- chat.runError.localStorageFailureMessage now matches the product supplement
row 13 verbatim in zh-CN ("…请确认有足够的剩余空间后,再重新尝试。"); the
old "permission to save to the current folder" clause is dropped and all
19 locales are retranslated to the new meaning.
- settings.amrActivationBrowserFailed (S32 browser not opened): half-width
comma replaced by a full-width one in zh-CN and zh-TW; wording unchanged.
* fix(web): align S03/S04/S06/S09a/S15/S24/S28a/S31c/S32a error copy with product (OPEND-2849)
zh-CN copied verbatim from the product error-copy doc; all 19 locales translated.
- S04 pre-send signed out (balance gate dialog): "Open Design 尚未登录" + body.
- S06 pre-send insufficient allowance: owner dialog "可用额度不足" + body (no
balance figure); member dialog "团队额度不足" + body. Benefits list and
buttons unchanged.
- S15 mid-run allowance exhausted: the turn-anchored upgrade card's $0 state
shows title + body, personal vs. member chosen by the billing audience
ProjectView already resolves. Low-balance tier, amount and Upgrade button
unchanged; the pre-send tail card keeps "现在无法开始新任务".
- S24a unfinished todos: "任务尚未完成" + "本次任务仍有 {n} 项内容未完成。" with the
real unfinished count; S24c user stop: "任务已停止" + body. Buttons kept.
- S03 / S28a Home send failures: the optimistic create's rollback toast (the
surface users actually see) and HomeView's inline block now map
AMR_AUTH_REQUIRED to S03 and transport failures to S28a, as title + body.
- S31c update download failed (updater error code download-failed): update
dialog shows "更新下载未完成" + body; Settings status shows the title.
- S32a browser not opened during sign-in: title + body via one shared
component for the three sign-in surfaces; the sign-in page link is kept.
- S09a rate-limit auto retry: run_retry_attempted now carries
failure_category in the SSE contract; a rate_limit retry row reads
"模型服务请求繁忙" + body, other retries keep "正在重试".
46 lines
1.9 KiB
TypeScript
46 lines
1.9 KiB
TypeScript
// OPEND-2849 / product copy S26: which export errors may replace the generic
|
|
// S26a "导出失败 / 本次导出未完成,请重新尝试。" copy. Only errors explicitly
|
|
// marked as carrying a localized, user-facing reason pass through.
|
|
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
import {
|
|
LocalizedExportError,
|
|
exportFailureToast,
|
|
genericExportFailedToast,
|
|
} from '../../src/components/export-failure-toast';
|
|
import { zhCN } from '../../src/i18n/locales/zh-CN';
|
|
import type { Dict } from '../../src/i18n/types';
|
|
|
|
const t = (key: keyof Dict) => zhCN[key];
|
|
const S26A = { message: '导出失败', details: '本次导出未完成,请重新尝试。', tone: 'error' };
|
|
|
|
describe('exportFailureToast', () => {
|
|
it('uses the S26a title + body for the generic case', () => {
|
|
expect(genericExportFailedToast(t)).toEqual(S26A);
|
|
});
|
|
|
|
it.each([
|
|
['an HTTP status line', new Error('html export request failed (500)')],
|
|
['an archive failure', new Error('archive request failed (502)')],
|
|
['an internal exception', new Error('Canvas is not available')],
|
|
['a desktop renderer failure', new Error('desktop PDF export unavailable (404)')],
|
|
['an error without a message', new Error('')],
|
|
['a non-Error rejection', 'boom'],
|
|
['undefined', undefined],
|
|
])('hides %s behind S26a', (_label, err) => {
|
|
expect(exportFailureToast(err, t)).toEqual(S26A);
|
|
});
|
|
|
|
it('passes through an already-localized, user-facing reason', () => {
|
|
const reason = zhCN['fileViewer.exportDaemonUnreachable'];
|
|
expect(exportFailureToast(new LocalizedExportError(reason), t)).toEqual({ message: reason, tone: 'error' });
|
|
});
|
|
|
|
it('keeps the original error object intact for diagnostics', () => {
|
|
const err = new LocalizedExportError(zhCN['fileViewer.exportPptxNa']);
|
|
expect(err).toBeInstanceOf(Error);
|
|
expect(err.message).toBe(zhCN['fileViewer.exportPptxNa']);
|
|
});
|
|
});
|