@agent-native/core 0.76.10 → 0.76.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +6 -0
- package/corpus/core/docs/content/locales/ar-SA/actions.md +7 -7
- package/corpus/core/docs/content/locales/ar-SA/template-plan.md +6 -4
- package/corpus/core/docs/content/locales/de-DE/actions.md +9 -9
- package/corpus/core/docs/content/locales/de-DE/template-plan.md +6 -4
- package/corpus/core/docs/content/locales/es-ES/actions.md +9 -9
- package/corpus/core/docs/content/locales/es-ES/template-plan.md +6 -4
- package/corpus/core/docs/content/locales/fr-FR/actions.md +9 -9
- package/corpus/core/docs/content/locales/fr-FR/template-plan.md +6 -4
- package/corpus/core/docs/content/locales/hi-IN/actions.md +9 -9
- package/corpus/core/docs/content/locales/hi-IN/template-plan.md +4 -4
- package/corpus/core/docs/content/locales/ja-JP/actions.md +9 -9
- package/corpus/core/docs/content/locales/ja-JP/template-plan.md +4 -4
- package/corpus/core/docs/content/locales/ko-KR/actions.md +9 -9
- package/corpus/core/docs/content/locales/ko-KR/template-plan.md +4 -4
- package/corpus/core/docs/content/locales/pt-BR/actions.md +9 -9
- package/corpus/core/docs/content/locales/pt-BR/template-plan.md +6 -4
- package/corpus/core/docs/content/locales/zh-CN/actions.md +9 -9
- package/corpus/core/docs/content/locales/zh-CN/template-plan.md +4 -4
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/ErrorBoundary.tsx +151 -10
- package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +51 -31
- package/corpus/templates/analytics/app/components/layout/DashboardHeader.tsx +26 -16
- package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -2
- package/corpus/templates/analytics/app/components/layout/HeaderActions.tsx +8 -1
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +2 -5
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +172 -107
- package/corpus/templates/analytics/app/i18n-data.ts +1076 -5
- package/corpus/templates/analytics/app/pages/Settings.tsx +2 -2
- package/corpus/templates/analytics/changelog/2026-06-24-analytics-navigation-command-palette-and-settings-now-honor-.md +6 -0
- package/corpus/templates/assets/app/components/layout/Header.tsx +0 -2
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +0 -2
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +0 -2
- package/corpus/templates/chat/app/components/layout/Header.tsx +1 -6
- package/corpus/templates/clips/app/components/layout/Header.tsx +1 -6
- package/corpus/templates/clips/app/hooks/use-library.ts +2 -0
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +3 -1
- package/corpus/templates/clips/changelog/2026-06-24-archive-and-spaces-navigation-is-more-resilient-when-sidebar.md +6 -0
- package/corpus/templates/content/app/components/layout/Header.tsx +0 -2
- package/corpus/templates/design/app/components/layout/Header.tsx +1 -6
- package/corpus/templates/forms/app/components/layout/Header.tsx +1 -6
- package/corpus/templates/macros/app/components/layout/Header.tsx +1 -6
- package/corpus/templates/mail/app/components/email/CodeBlockLangPicker.tsx +4 -2
- package/corpus/templates/mail/app/components/email/ComposeBubbleToolbar.tsx +18 -12
- package/corpus/templates/mail/app/components/email/ComposeEditor.tsx +9 -5
- package/corpus/templates/mail/app/components/email/ComposeModal.tsx +36 -19
- package/corpus/templates/mail/app/components/email/EmailList.tsx +59 -55
- package/corpus/templates/mail/app/components/email/EmailListItem.tsx +7 -3
- package/corpus/templates/mail/app/components/email/EmailThread.tsx +25 -16
- package/corpus/templates/mail/app/components/email/InlineReplyComposer.tsx +25 -19
- package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +32 -17
- package/corpus/templates/mail/app/components/email/RecipientInput.tsx +26 -10
- package/corpus/templates/mail/app/components/email/SendLaterButton.tsx +5 -3
- package/corpus/templates/mail/app/components/email/SnoozeModal.tsx +4 -2
- package/corpus/templates/mail/app/components/email/SnoozePopover.tsx +9 -5
- package/corpus/templates/mail/app/components/email/extensions/ComposeImageBlock.tsx +4 -2
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +168 -86
- package/corpus/templates/mail/app/components/layout/SearchBar.tsx +4 -2
- package/corpus/templates/mail/app/i18n/ar-SA.ts +212 -0
- package/corpus/templates/mail/app/i18n/de-DE.ts +212 -0
- package/corpus/templates/mail/app/i18n/en-US.ts +212 -0
- package/corpus/templates/mail/app/i18n/es-ES.ts +212 -0
- package/corpus/templates/mail/app/i18n/fr-FR.ts +212 -0
- package/corpus/templates/mail/app/i18n/hi-IN.ts +212 -0
- package/corpus/templates/mail/app/i18n/ja-JP.ts +212 -0
- package/corpus/templates/mail/app/i18n/ko-KR.ts +212 -0
- package/corpus/templates/mail/app/i18n/pt-BR.ts +212 -0
- package/corpus/templates/mail/app/i18n/zh-CN.ts +211 -0
- package/corpus/templates/mail/changelog/2026-06-24-interface-language-support-now-covers-more-mail-controls-and.md +6 -0
- package/corpus/templates/plan/app/components/layout/Header.tsx +1 -6
- package/corpus/templates/slides/app/components/layout/Header.tsx +1 -6
- package/corpus/templates/videos/app/components/layout/Header.tsx +1 -6
- package/dist/client/ErrorBoundary.d.ts.map +1 -1
- package/dist/client/ErrorBoundary.js +128 -7
- package/dist/client/ErrorBoundary.js.map +1 -1
- package/docs/content/locales/ar-SA/actions.md +7 -7
- package/docs/content/locales/ar-SA/template-plan.md +6 -4
- package/docs/content/locales/de-DE/actions.md +9 -9
- package/docs/content/locales/de-DE/template-plan.md +6 -4
- package/docs/content/locales/es-ES/actions.md +9 -9
- package/docs/content/locales/es-ES/template-plan.md +6 -4
- package/docs/content/locales/fr-FR/actions.md +9 -9
- package/docs/content/locales/fr-FR/template-plan.md +6 -4
- package/docs/content/locales/hi-IN/actions.md +9 -9
- package/docs/content/locales/hi-IN/template-plan.md +4 -4
- package/docs/content/locales/ja-JP/actions.md +9 -9
- package/docs/content/locales/ja-JP/template-plan.md +4 -4
- package/docs/content/locales/ko-KR/actions.md +9 -9
- package/docs/content/locales/ko-KR/template-plan.md +4 -4
- package/docs/content/locales/pt-BR/actions.md +9 -9
- package/docs/content/locales/pt-BR/template-plan.md +6 -4
- package/docs/content/locales/zh-CN/actions.md +9 -9
- package/docs/content/locales/zh-CN/template-plan.md +4 -4
- package/package.json +1 -1
|
@@ -1,4 +1,215 @@
|
|
|
1
1
|
const messages = {
|
|
2
|
+
mail: {
|
|
3
|
+
appName: "邮件",
|
|
4
|
+
pages: {
|
|
5
|
+
team: "团队",
|
|
6
|
+
},
|
|
7
|
+
views: {
|
|
8
|
+
inbox: "收件箱",
|
|
9
|
+
unread: "未读",
|
|
10
|
+
starred: "已加星标",
|
|
11
|
+
snoozed: "已稍后提醒",
|
|
12
|
+
sent: "已发送",
|
|
13
|
+
draftQueue: "草稿队列",
|
|
14
|
+
scheduled: "已计划",
|
|
15
|
+
drafts: "草稿",
|
|
16
|
+
archive: "归档",
|
|
17
|
+
trash: "垃圾箱",
|
|
18
|
+
other: "其他",
|
|
19
|
+
labels: "标签",
|
|
20
|
+
extensions: "扩展",
|
|
21
|
+
},
|
|
22
|
+
toolbar: {
|
|
23
|
+
toggleMenu: "切换菜单",
|
|
24
|
+
menu: "菜单",
|
|
25
|
+
configureTabs: "配置标签页",
|
|
26
|
+
refreshInbox: "刷新收件箱",
|
|
27
|
+
composeEmail: "撰写邮件",
|
|
28
|
+
compose: "撰写",
|
|
29
|
+
composeShortcut: "撰写 (C)",
|
|
30
|
+
accounts: "账号",
|
|
31
|
+
pinSidebar: "固定侧边栏",
|
|
32
|
+
unpinSidebar: "取消固定侧边栏",
|
|
33
|
+
settings: "设置",
|
|
34
|
+
},
|
|
35
|
+
search: {
|
|
36
|
+
label: "搜索",
|
|
37
|
+
placeholder: "搜索...",
|
|
38
|
+
clear: "清除搜索 (Esc)",
|
|
39
|
+
noMatches: "没有匹配项",
|
|
40
|
+
},
|
|
41
|
+
tabSettings: {
|
|
42
|
+
views: "视图",
|
|
43
|
+
categories: "分类",
|
|
44
|
+
rename: "重命名",
|
|
45
|
+
renameTab: "重命名标签页",
|
|
46
|
+
help: "勾选的项目会显示为标签页。标签邮件会从收件箱中分离。",
|
|
47
|
+
},
|
|
48
|
+
accounts: {
|
|
49
|
+
remove: "移除",
|
|
50
|
+
connecting: "正在连接...",
|
|
51
|
+
addAccount: "添加账号",
|
|
52
|
+
},
|
|
53
|
+
actions: {
|
|
54
|
+
undo: "撤销",
|
|
55
|
+
archive: "归档",
|
|
56
|
+
markRead: "标为已读",
|
|
57
|
+
markUnread: "标为未读",
|
|
58
|
+
moveTo: "移动到",
|
|
59
|
+
moveToTrash: "移到垃圾箱",
|
|
60
|
+
},
|
|
61
|
+
compose: {
|
|
62
|
+
addImage: "Add an image",
|
|
63
|
+
agentPromptLabel: "What should the agent write?",
|
|
64
|
+
agentPromptPlaceholder: "e.g. Write a polite follow-up...",
|
|
65
|
+
aiAssist: "AI Assist",
|
|
66
|
+
aiAssistPlaceholder: "e.g. make more formal...",
|
|
67
|
+
apply: "Apply",
|
|
68
|
+
attachFile: "Attach file",
|
|
69
|
+
bold: "Bold",
|
|
70
|
+
cancel: "Cancel",
|
|
71
|
+
code: "Code",
|
|
72
|
+
deleteDraft: "Delete draft",
|
|
73
|
+
discardDraft: "Discard draft",
|
|
74
|
+
enterLinkUrl: "Enter the URL for the link.",
|
|
75
|
+
forward: "Forward",
|
|
76
|
+
fullScreen: "Full screen",
|
|
77
|
+
fullScreenCompose: "Full screen compose",
|
|
78
|
+
generate: "Generate",
|
|
79
|
+
generateShortcut: "Generate ({{shortcut}})",
|
|
80
|
+
generating: "Generating...",
|
|
81
|
+
insertLink: "Insert link",
|
|
82
|
+
italic: "Italic",
|
|
83
|
+
link: "Link",
|
|
84
|
+
minimize: "Minimize",
|
|
85
|
+
minimizeCompose: "Minimize compose",
|
|
86
|
+
newDraft: "New draft",
|
|
87
|
+
pasteLink: "Paste link...",
|
|
88
|
+
plainText: "Plain text",
|
|
89
|
+
popOut: "Pop out to compose window",
|
|
90
|
+
reply: "Reply",
|
|
91
|
+
replyTo: "to {{recipient}}",
|
|
92
|
+
restore: "Restore",
|
|
93
|
+
restoreCompose: "Restore compose",
|
|
94
|
+
restoreComposeSize: "Restore compose size",
|
|
95
|
+
restoreSize: "Restore size",
|
|
96
|
+
send: "Send",
|
|
97
|
+
sending: "Sending...",
|
|
98
|
+
strikethrough: "Strikethrough",
|
|
99
|
+
subject: "Subject",
|
|
100
|
+
toSubmit: "to submit",
|
|
101
|
+
uploading: "Uploading...",
|
|
102
|
+
writeMessagePlaceholder: "Write your message...",
|
|
103
|
+
},
|
|
104
|
+
integrations: {
|
|
105
|
+
addIntegration: "Add integration",
|
|
106
|
+
checking: "Checking...",
|
|
107
|
+
connect: "Connect",
|
|
108
|
+
disconnect: "Disconnect",
|
|
109
|
+
getApiKey: "To get your API key:",
|
|
110
|
+
newIntegration: "New integration",
|
|
111
|
+
newIntegrationPlaceholder:
|
|
112
|
+
"e.g. Salesforce CRM — show deal stage and recent activity for the contact",
|
|
113
|
+
openProviderSettings: "Open {{provider}} Settings",
|
|
114
|
+
save: "Save",
|
|
115
|
+
submit: "Submit",
|
|
116
|
+
submitShortcut: "Submit ({{shortcut}})",
|
|
117
|
+
updateKey: "Update key",
|
|
118
|
+
},
|
|
119
|
+
recipients: {
|
|
120
|
+
aliasName: "Alias name",
|
|
121
|
+
createReusableGroup: "Create a reusable group of {{count}} recipients",
|
|
122
|
+
editAlias: "Edit alias",
|
|
123
|
+
expandToIndividualEmails: "Expand to individual emails",
|
|
124
|
+
peopleCount: "{{count}} people",
|
|
125
|
+
recipientCount: "{{count}} recipients",
|
|
126
|
+
saveAsAlias: "Save as alias",
|
|
127
|
+
saving: "Saving...",
|
|
128
|
+
},
|
|
129
|
+
sendLater: {
|
|
130
|
+
cancelScheduledSend: "Cancel scheduled send",
|
|
131
|
+
pickDateTime: "Pick date & time...",
|
|
132
|
+
scheduleSend: "Schedule send",
|
|
133
|
+
sendNow: "Send now",
|
|
134
|
+
},
|
|
135
|
+
snooze: {
|
|
136
|
+
modalPlaceholder: "Try: 8 am, 3 days, aug 7",
|
|
137
|
+
parsing: "Parsing...",
|
|
138
|
+
snooze: "Snooze",
|
|
139
|
+
snoozeUntil: "Snooze until",
|
|
140
|
+
snoozeUntilDate: "Snooze until {{date}}",
|
|
141
|
+
snoozing: "Snoozing...",
|
|
142
|
+
tryPlaceholder: "or try: friday 5pm, in 2 weeks...",
|
|
143
|
+
},
|
|
144
|
+
thread: {
|
|
145
|
+
alwaysFrom: "Always from {{domain}}",
|
|
146
|
+
downloadAll: "Download all",
|
|
147
|
+
emailContent: "Email content",
|
|
148
|
+
hideQuotedText: "Hide quoted text",
|
|
149
|
+
imagesBlocked: "Images blocked.",
|
|
150
|
+
remoteImagesHidden: "Remote images hidden in this embed.",
|
|
151
|
+
searchConversation: "Search in conversation...",
|
|
152
|
+
showImages: "Show images",
|
|
153
|
+
showQuotedText: "Show quoted text",
|
|
154
|
+
showSignature: "Show signature",
|
|
155
|
+
unsubscribe: "Unsubscribe",
|
|
156
|
+
unsubscribeTooltip: "Unsubscribe from this mailing list",
|
|
157
|
+
unsubscribing: "Unsubscribing...",
|
|
158
|
+
viewPullRequest: "View Pull Request",
|
|
159
|
+
},
|
|
160
|
+
selection: {
|
|
161
|
+
clear: "清除选择",
|
|
162
|
+
actions: "选择操作",
|
|
163
|
+
selected: "已选择 {{count}} 项",
|
|
164
|
+
select: "选择",
|
|
165
|
+
all: "全部",
|
|
166
|
+
none: "无",
|
|
167
|
+
read: "已读",
|
|
168
|
+
unstarred: "未加星标",
|
|
169
|
+
},
|
|
170
|
+
empty: {
|
|
171
|
+
inboxZeroTitle: "你已清空收件箱",
|
|
172
|
+
inboxZeroSubtitle: "所有邮件都处理完了",
|
|
173
|
+
snoozed: "当前没有稍后提醒的邮件。",
|
|
174
|
+
drafts: "保存的草稿会显示在这里。",
|
|
175
|
+
starred: "给邮件加星标后会显示在这里。",
|
|
176
|
+
sent: "你发送的邮件会显示在这里。",
|
|
177
|
+
scheduled: "定时发送的邮件会显示在这里。",
|
|
178
|
+
archive: "归档邮件会显示在这里。",
|
|
179
|
+
trash: "垃圾箱邮件会在这里保留 30 天。",
|
|
180
|
+
spam: "Gmail 标记的垃圾邮件会显示在这里。",
|
|
181
|
+
all: "所有邮件会显示在这里。",
|
|
182
|
+
unread: "你已处理完毕,没有未读邮件。",
|
|
183
|
+
default: "匹配此视图的邮件会显示在这里。",
|
|
184
|
+
noSearchResults: "没有找到“{{query}}”的结果",
|
|
185
|
+
tryDifferentKeywords: "试试其他关键词",
|
|
186
|
+
nothingHere: "这里还没有内容",
|
|
187
|
+
loadingMore: "Loading more...",
|
|
188
|
+
},
|
|
189
|
+
toasts: {
|
|
190
|
+
noEmailSelected: "未选择邮件。",
|
|
191
|
+
reportedSpam: "已举报为垃圾邮件。",
|
|
192
|
+
reportedSpamBlocked: "已举报为垃圾邮件并屏蔽 {{email}}。",
|
|
193
|
+
noThreadSelected: "未选择会话。",
|
|
194
|
+
threadMuted: "会话已静音。",
|
|
195
|
+
archived: "已归档。",
|
|
196
|
+
archivedMany: "已归档 {{count}} 个会话。",
|
|
197
|
+
trashed: "已移到垃圾箱。",
|
|
198
|
+
trashedMany: "已将 {{count}} 个会话移到垃圾箱。",
|
|
199
|
+
scheduledSent: "定时邮件已发送。",
|
|
200
|
+
scheduledSendFailed: "发送定时邮件失败",
|
|
201
|
+
scheduledCancelled: "定时邮件已取消。",
|
|
202
|
+
scheduledCancelFailed: "取消定时邮件失败",
|
|
203
|
+
},
|
|
204
|
+
error: {
|
|
205
|
+
retrying: "正在重试...",
|
|
206
|
+
tryAgainIn: "{{seconds}} 秒后重试",
|
|
207
|
+
tryAgain: "重试",
|
|
208
|
+
rateLimitTitle: "触发 Gmail 速率限制",
|
|
209
|
+
loadTitle: "无法加载邮件",
|
|
210
|
+
rateLimitDescription: "最近对 Google 的请求过多。稍等片刻后会重试。",
|
|
211
|
+
},
|
|
212
|
+
},
|
|
2
213
|
agent: {
|
|
3
214
|
emptyState: "可以问我任何关于邮件的问题",
|
|
4
215
|
suggestionSummarize: "总结我的未读邮件",
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { useLocation } from "react-router";
|
|
2
2
|
import { IconMenu2 } from "@tabler/icons-react";
|
|
3
3
|
import { useHeaderTitle, useHeaderActions } from "./HeaderActions";
|
|
4
|
-
import {
|
|
5
|
-
AgentToggleButton,
|
|
6
|
-
LanguagePicker,
|
|
7
|
-
useT,
|
|
8
|
-
} from "@agent-native/core/client";
|
|
4
|
+
import { AgentToggleButton, useT } from "@agent-native/core/client";
|
|
9
5
|
import { APP_TITLE } from "@/lib/app-config";
|
|
10
6
|
|
|
11
7
|
const pageTitleKeys: Record<string, string> = {
|
|
@@ -54,7 +50,6 @@ export function Header({ onOpenMobileSidebar }: HeaderProps) {
|
|
|
54
50
|
</div>
|
|
55
51
|
<div className="flex items-center gap-2 shrink-0">
|
|
56
52
|
{actions}
|
|
57
|
-
<LanguagePicker variant="icon" />
|
|
58
53
|
<AgentToggleButton />
|
|
59
54
|
</div>
|
|
60
55
|
</header>
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { useLocation } from "react-router";
|
|
2
2
|
import { useDecks } from "@/context/DeckContext";
|
|
3
3
|
import { useHeaderTitle, useHeaderActions } from "./HeaderActions";
|
|
4
|
-
import {
|
|
5
|
-
AgentToggleButton,
|
|
6
|
-
LanguagePicker,
|
|
7
|
-
useT,
|
|
8
|
-
} from "@agent-native/core/client";
|
|
4
|
+
import { AgentToggleButton, useT } from "@agent-native/core/client";
|
|
9
5
|
import { RunsTray } from "@agent-native/core/client/progress";
|
|
10
6
|
|
|
11
7
|
const pageTitleKeys: Record<string, string> = {
|
|
@@ -67,7 +63,6 @@ export function Header() {
|
|
|
67
63
|
</div>
|
|
68
64
|
<div className="flex items-center gap-2 shrink-0">
|
|
69
65
|
{actions}
|
|
70
|
-
<LanguagePicker variant="icon" />
|
|
71
66
|
<RunsTray pollMs={1500} />
|
|
72
67
|
<AgentToggleButton />
|
|
73
68
|
</div>
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { useLocation } from "react-router";
|
|
2
2
|
import { IconMenu2 } from "@tabler/icons-react";
|
|
3
3
|
import { useHeaderTitle, useHeaderActions } from "./HeaderActions";
|
|
4
|
-
import {
|
|
5
|
-
AgentToggleButton,
|
|
6
|
-
LanguagePicker,
|
|
7
|
-
useT,
|
|
8
|
-
} from "@agent-native/core/client";
|
|
4
|
+
import { AgentToggleButton, useT } from "@agent-native/core/client";
|
|
9
5
|
import { compositions } from "@/remotion/registry";
|
|
10
6
|
|
|
11
7
|
const pageTitleKeys: Record<string, string> = {
|
|
@@ -60,7 +56,6 @@ export function Header({ onOpenMobileSidebar }: HeaderProps) {
|
|
|
60
56
|
</div>
|
|
61
57
|
<div className="flex items-center gap-2 shrink-0">
|
|
62
58
|
{actions}
|
|
63
|
-
<LanguagePicker variant="icon" />
|
|
64
59
|
<AgentToggleButton />
|
|
65
60
|
</div>
|
|
66
61
|
</header>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorBoundary.d.ts","sourceRoot":"","sources":["../../src/client/ErrorBoundary.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ErrorBoundary.d.ts","sourceRoot":"","sources":["../../src/client/ErrorBoundary.tsx"],"names":[],"mappings":"AAgSA,wBAAgB,aAAa,4CAI5B"}
|
|
@@ -3,7 +3,126 @@ import { useEffect, useState } from "react";
|
|
|
3
3
|
import { isRouteErrorResponse, useInRouterContext, useRouteError, } from "react-router";
|
|
4
4
|
import { appPath } from "./api-path.js";
|
|
5
5
|
import { isDynamicImportFailureMessage, recoverFromStaleChunkError, } from "./route-chunk-recovery.js";
|
|
6
|
+
import { DEFAULT_LOCALE, LOCALE_HYDRATION_GLOBAL, LOCALE_STORAGE_KEY, normalizeLocaleCode, } from "../localization/shared.js";
|
|
6
7
|
const homeLinkClassName = "mt-6 inline-flex items-center gap-1.5 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm hover:bg-primary/90 cursor-pointer";
|
|
8
|
+
const errorCopy = {
|
|
9
|
+
"en-US": {
|
|
10
|
+
loadingLatest: "Loading the latest version...",
|
|
11
|
+
genericTitle: "Something went wrong",
|
|
12
|
+
genericDetails: "An unexpected error occurred.",
|
|
13
|
+
notFoundTitle: "Page not found",
|
|
14
|
+
notFoundDetails: "We couldn't find this page.",
|
|
15
|
+
statusTitle: (status) => `${status} Error`,
|
|
16
|
+
goHome: "Go home",
|
|
17
|
+
reload: "Reload",
|
|
18
|
+
},
|
|
19
|
+
"zh-CN": {
|
|
20
|
+
loadingLatest: "正在加载最新版本...",
|
|
21
|
+
genericTitle: "出错了",
|
|
22
|
+
genericDetails: "发生了意外错误。",
|
|
23
|
+
notFoundTitle: "页面未找到",
|
|
24
|
+
notFoundDetails: "我们找不到这个页面。",
|
|
25
|
+
statusTitle: (status) => `${status} 错误`,
|
|
26
|
+
goHome: "回到首页",
|
|
27
|
+
reload: "重新加载",
|
|
28
|
+
},
|
|
29
|
+
"es-ES": {
|
|
30
|
+
loadingLatest: "Cargando la versión más reciente...",
|
|
31
|
+
genericTitle: "Algo salió mal",
|
|
32
|
+
genericDetails: "Se produjo un error inesperado.",
|
|
33
|
+
notFoundTitle: "Página no encontrada",
|
|
34
|
+
notFoundDetails: "No pudimos encontrar esta página.",
|
|
35
|
+
statusTitle: (status) => `Error ${status}`,
|
|
36
|
+
goHome: "Ir al inicio",
|
|
37
|
+
reload: "Recargar",
|
|
38
|
+
},
|
|
39
|
+
"fr-FR": {
|
|
40
|
+
loadingLatest: "Chargement de la dernière version...",
|
|
41
|
+
genericTitle: "Un problème est survenu",
|
|
42
|
+
genericDetails: "Une erreur inattendue s'est produite.",
|
|
43
|
+
notFoundTitle: "Page introuvable",
|
|
44
|
+
notFoundDetails: "Nous n'avons pas trouvé cette page.",
|
|
45
|
+
statusTitle: (status) => `Erreur ${status}`,
|
|
46
|
+
goHome: "Accueil",
|
|
47
|
+
reload: "Recharger",
|
|
48
|
+
},
|
|
49
|
+
"de-DE": {
|
|
50
|
+
loadingLatest: "Neueste Version wird geladen...",
|
|
51
|
+
genericTitle: "Etwas ist schiefgelaufen",
|
|
52
|
+
genericDetails: "Ein unerwarteter Fehler ist aufgetreten.",
|
|
53
|
+
notFoundTitle: "Seite nicht gefunden",
|
|
54
|
+
notFoundDetails: "Wir konnten diese Seite nicht finden.",
|
|
55
|
+
statusTitle: (status) => `Fehler ${status}`,
|
|
56
|
+
goHome: "Zur Startseite",
|
|
57
|
+
reload: "Neu laden",
|
|
58
|
+
},
|
|
59
|
+
"ja-JP": {
|
|
60
|
+
loadingLatest: "最新バージョンを読み込み中...",
|
|
61
|
+
genericTitle: "問題が発生しました",
|
|
62
|
+
genericDetails: "予期しないエラーが発生しました。",
|
|
63
|
+
notFoundTitle: "ページが見つかりません",
|
|
64
|
+
notFoundDetails: "このページは見つかりませんでした。",
|
|
65
|
+
statusTitle: (status) => `${status} エラー`,
|
|
66
|
+
goHome: "ホームへ",
|
|
67
|
+
reload: "再読み込み",
|
|
68
|
+
},
|
|
69
|
+
"ko-KR": {
|
|
70
|
+
loadingLatest: "최신 버전을 불러오는 중...",
|
|
71
|
+
genericTitle: "문제가 발생했습니다",
|
|
72
|
+
genericDetails: "예기치 않은 오류가 발생했습니다.",
|
|
73
|
+
notFoundTitle: "페이지를 찾을 수 없음",
|
|
74
|
+
notFoundDetails: "이 페이지를 찾을 수 없습니다.",
|
|
75
|
+
statusTitle: (status) => `${status} 오류`,
|
|
76
|
+
goHome: "홈으로 이동",
|
|
77
|
+
reload: "새로고침",
|
|
78
|
+
},
|
|
79
|
+
"pt-BR": {
|
|
80
|
+
loadingLatest: "Carregando a versão mais recente...",
|
|
81
|
+
genericTitle: "Algo deu errado",
|
|
82
|
+
genericDetails: "Ocorreu um erro inesperado.",
|
|
83
|
+
notFoundTitle: "Página não encontrada",
|
|
84
|
+
notFoundDetails: "Não encontramos esta página.",
|
|
85
|
+
statusTitle: (status) => `Erro ${status}`,
|
|
86
|
+
goHome: "Ir para início",
|
|
87
|
+
reload: "Recarregar",
|
|
88
|
+
},
|
|
89
|
+
"hi-IN": {
|
|
90
|
+
loadingLatest: "नवीनतम संस्करण लोड हो रहा है...",
|
|
91
|
+
genericTitle: "कुछ गलत हो गया",
|
|
92
|
+
genericDetails: "एक अनपेक्षित त्रुटि हुई।",
|
|
93
|
+
notFoundTitle: "पेज नहीं मिला",
|
|
94
|
+
notFoundDetails: "हमें यह पेज नहीं मिला।",
|
|
95
|
+
statusTitle: (status) => `${status} त्रुटि`,
|
|
96
|
+
goHome: "होम पर जाएं",
|
|
97
|
+
reload: "रीलोड करें",
|
|
98
|
+
},
|
|
99
|
+
"ar-SA": {
|
|
100
|
+
loadingLatest: "جار تحميل أحدث إصدار...",
|
|
101
|
+
genericTitle: "حدث خطأ ما",
|
|
102
|
+
genericDetails: "حدث خطأ غير متوقع.",
|
|
103
|
+
notFoundTitle: "الصفحة غير موجودة",
|
|
104
|
+
notFoundDetails: "تعذر العثور على هذه الصفحة.",
|
|
105
|
+
statusTitle: (status) => `خطأ ${status}`,
|
|
106
|
+
goHome: "العودة للرئيسية",
|
|
107
|
+
reload: "إعادة التحميل",
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
function readErrorLocale() {
|
|
111
|
+
if (typeof window === "undefined")
|
|
112
|
+
return DEFAULT_LOCALE;
|
|
113
|
+
const hydrated = window[LOCALE_HYDRATION_GLOBAL]?.locale;
|
|
114
|
+
const stored = window.localStorage?.getItem(LOCALE_STORAGE_KEY);
|
|
115
|
+
return (normalizeLocaleCode(hydrated) ??
|
|
116
|
+
normalizeLocaleCode(stored) ??
|
|
117
|
+
DEFAULT_LOCALE);
|
|
118
|
+
}
|
|
119
|
+
function useErrorCopy() {
|
|
120
|
+
const [locale, setLocale] = useState(readErrorLocale);
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
setLocale(readErrorLocale());
|
|
123
|
+
}, []);
|
|
124
|
+
return errorCopy[locale] ?? errorCopy[DEFAULT_LOCALE];
|
|
125
|
+
}
|
|
7
126
|
function useApplyThemeClass() {
|
|
8
127
|
useEffect(() => {
|
|
9
128
|
const root = document.documentElement;
|
|
@@ -48,9 +167,11 @@ function useStaleChunkRecovery(error) {
|
|
|
48
167
|
return recovering;
|
|
49
168
|
}
|
|
50
169
|
function UpdatingScreen() {
|
|
51
|
-
|
|
170
|
+
const copy = useErrorCopy();
|
|
171
|
+
return (_jsx("main", { className: "flex items-center justify-center min-h-screen p-4 bg-background text-foreground", children: _jsx("p", { className: "text-muted-foreground text-sm", children: copy.loadingLatest }) }));
|
|
52
172
|
}
|
|
53
173
|
function ErrorScreen({ error }) {
|
|
174
|
+
const copy = useErrorCopy();
|
|
54
175
|
const recovering = useStaleChunkRecovery(error);
|
|
55
176
|
// While auto-recovering a stale chunk, show a neutral state and skip the
|
|
56
177
|
// console.error below so the transient, self-healing failure does not get
|
|
@@ -58,17 +179,17 @@ function ErrorScreen({ error }) {
|
|
|
58
179
|
if (recovering)
|
|
59
180
|
return _jsx(UpdatingScreen, {});
|
|
60
181
|
let status = null;
|
|
61
|
-
let title =
|
|
62
|
-
let details =
|
|
182
|
+
let title = copy.genericTitle;
|
|
183
|
+
let details = copy.genericDetails;
|
|
63
184
|
let stack;
|
|
64
185
|
if (isRouteErrorResponse(error)) {
|
|
65
186
|
status = error.status;
|
|
66
187
|
if (error.status === 404) {
|
|
67
|
-
title =
|
|
68
|
-
details =
|
|
188
|
+
title = copy.notFoundTitle;
|
|
189
|
+
details = copy.notFoundDetails;
|
|
69
190
|
}
|
|
70
191
|
else {
|
|
71
|
-
title =
|
|
192
|
+
title = copy.statusTitle(error.status);
|
|
72
193
|
details = error.statusText || details;
|
|
73
194
|
}
|
|
74
195
|
}
|
|
@@ -93,7 +214,7 @@ function ErrorScreen({ error }) {
|
|
|
93
214
|
if (typeof console !== "undefined" && error) {
|
|
94
215
|
console.error("[ErrorBoundary]", error);
|
|
95
216
|
}
|
|
96
|
-
return (_jsx("main", { className: "flex items-center justify-center min-h-screen p-4 bg-background text-foreground", children: _jsxs("div", { className: "flex flex-col items-center text-center max-w-md", children: [status && (_jsx("span", { className: "text-7xl font-bold tracking-tight text-muted-foreground/40", children: status })), _jsx("h1", { className: "mt-3 text-2xl font-semibold", children: title }), _jsx("p", { className: "mt-2 text-muted-foreground text-sm", children: details }), _jsx("a", { href: appPath("/"), className: homeLinkClassName, children:
|
|
217
|
+
return (_jsx("main", { className: "flex items-center justify-center min-h-screen p-4 bg-background text-foreground", children: _jsxs("div", { className: "flex flex-col items-center text-center max-w-md", children: [status && (_jsx("span", { className: "text-7xl font-bold tracking-tight text-muted-foreground/40", children: status })), _jsx("h1", { className: "mt-3 text-2xl font-semibold", children: title }), _jsx("p", { className: "mt-2 text-muted-foreground text-sm", children: details }), _jsx("a", { href: appPath("/"), className: homeLinkClassName, children: copy.goHome }), _jsx("button", { type: "button", onClick: () => window.location.reload(), className: "mt-3 inline-flex cursor-pointer items-center gap-1.5 rounded-md border border-border bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm hover:bg-accent", children: copy.reload }), stack && (_jsx("pre", { className: "mt-6 w-full text-start text-xs overflow-auto p-4 bg-muted rounded", children: _jsx("code", { children: stack }) }))] }) }));
|
|
97
218
|
}
|
|
98
219
|
function RoutedErrorScreen() {
|
|
99
220
|
return _jsx(ErrorScreen, { error: useRouteError() });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorBoundary.js","sourceRoot":"","sources":["../../src/client/ErrorBoundary.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EACL,6BAA6B,EAC7B,0BAA0B,GAC3B,MAAM,2BAA2B,CAAC;AAEnC,MAAM,iBAAiB,GACrB,gKAAgK,CAAC;AAEnK,SAAS,kBAAkB;IACzB,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,IAAI,GAAG,QAAQ,CAAC,eAAe,CAAC;QACtC,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC;YACrE,OAAO;QACT,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAC7C,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;gBACtB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC7B,CAAC;iBAAM,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;gBAC9B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC9B,CAAC;iBAAM,IAAI,MAAM,CAAC,UAAU,CAAC,8BAA8B,CAAC,CAAC,OAAO,EAAE,CAAC;gBACrE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,IAAI,KAAK,YAAY,KAAK;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC;IACjD,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAChD,CAAC;AAED;;;;;GAKG;AACH,SAAS,qBAAqB,CAAC,KAAc;IAC3C,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAChD,6BAA6B,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CACrD,CAAC;IACF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,6BAA6B,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;YAC1D,aAAa,CAAC,KAAK,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,0BAA0B,CAAC,KAAK,CAAC;YAAE,aAAa,CAAC,KAAK,CAAC,CAAC;IAC/D,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IACZ,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,cAAc;IACrB,OAAO,CACL,eAAM,SAAS,EAAC,iFAAiF,YAC/F,YAAG,SAAS,EAAC,+BAA+B,iDAExC,GACC,CACR,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,EAAE,KAAK,EAAsB;IAChD,MAAM,UAAU,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAChD,yEAAyE;IACzE,0EAA0E;IAC1E,4BAA4B;IAC5B,IAAI,UAAU;QAAE,OAAO,KAAC,cAAc,KAAG,CAAC;IAE1C,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,IAAI,KAAK,GAAG,sBAAsB,CAAC;IACnC,IAAI,OAAO,GAAG,+BAA+B,CAAC;IAC9C,IAAI,KAAyB,CAAC;IAE9B,IAAI,oBAAoB,CAAC,KAAK,CAAC,EAAE,CAAC;QAChC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QACtB,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACzB,KAAK,GAAG,gBAAgB,CAAC;YACzB,OAAO,GAAG,6BAA6B,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,KAAK,GAAG,GAAG,KAAK,CAAC,MAAM,QAAQ,CAAC;YAChC,OAAO,GAAG,KAAK,CAAC,UAAU,IAAI,OAAO,CAAC;QACxC,CAAC;IACH,CAAC;SAAM,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAClC,0DAA0D;QAC1D,qEAAqE;QACrE,kEAAkE;QAClE,0BAA0B;QAC1B,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAC1B,CAAC;QACD,IACE,OAAO,OAAO,KAAK,WAAW;YAC9B,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EACrC,CAAC;YACD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QACtB,CAAC;IACH,CAAC;SAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,EAAE,CAAC;QAC9C,OAAO,GAAG,KAAK,CAAC;IAClB,CAAC;IAED,mEAAmE;IACnE,8DAA8D;IAC9D,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,KAAK,EAAE,CAAC;QAC5C,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,CACL,eAAM,SAAS,EAAC,iFAAiF,YAC/F,eAAK,SAAS,EAAC,iDAAiD,aAC7D,MAAM,IAAI,CACT,eAAM,SAAS,EAAC,4DAA4D,YACzE,MAAM,GACF,CACR,EACD,aAAI,SAAS,EAAC,6BAA6B,YAAE,KAAK,GAAM,EACxD,YAAG,SAAS,EAAC,oCAAoC,YAAE,OAAO,GAAK,EAC/D,YAAG,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,iBAAiB,wBAE/C,EACJ,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,EACvC,SAAS,EAAC,4KAA4K,uBAG/K,EACR,KAAK,IAAI,CACR,cAAK,SAAS,EAAC,mEAAmE,YAChF,yBAAO,KAAK,GAAQ,GAChB,CACP,IACG,GACD,CACR,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB;IACxB,OAAO,KAAC,WAAW,IAAC,KAAK,EAAE,aAAa,EAAE,GAAI,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,kBAAkB,EAAE,CAAC;IACrB,IAAI,CAAC,kBAAkB,EAAE;QAAE,OAAO,KAAC,WAAW,IAAC,KAAK,EAAE,SAAS,GAAI,CAAC;IACpE,OAAO,KAAC,iBAAiB,KAAG,CAAC;AAC/B,CAAC","sourcesContent":["import { useEffect, useState } from \"react\";\nimport {\n isRouteErrorResponse,\n useInRouterContext,\n useRouteError,\n} from \"react-router\";\nimport { appPath } from \"./api-path.js\";\nimport {\n isDynamicImportFailureMessage,\n recoverFromStaleChunkError,\n} from \"./route-chunk-recovery.js\";\n\nconst homeLinkClassName =\n \"mt-6 inline-flex items-center gap-1.5 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm hover:bg-primary/90 cursor-pointer\";\n\nfunction useApplyThemeClass() {\n useEffect(() => {\n const root = document.documentElement;\n if (root.classList.contains(\"dark\") || root.classList.contains(\"light\"))\n return;\n try {\n const stored = localStorage.getItem(\"theme\");\n if (stored === \"dark\") {\n root.classList.add(\"dark\");\n } else if (stored === \"light\") {\n root.classList.add(\"light\");\n } else if (window.matchMedia(\"(prefers-color-scheme: dark)\").matches) {\n root.classList.add(\"dark\");\n }\n } catch {}\n }, []);\n}\n\nfunction errorMessageOf(error: unknown): string {\n if (error instanceof Error) return error.message;\n return typeof error === \"string\" ? error : \"\";\n}\n\n/**\n * When a route renders against a stale lazy chunk after a deploy (the chunk's\n * hashed filename no longer exists), the import rejection surfaces here. Reload\n * once to fetch fresh assets instead of stranding the user on an error screen.\n * The reload is loop-guarded; if it cannot recover, fall back to the screen.\n */\nfunction useStaleChunkRecovery(error: unknown): boolean {\n const [recovering, setRecovering] = useState(() =>\n isDynamicImportFailureMessage(errorMessageOf(error)),\n );\n useEffect(() => {\n if (!isDynamicImportFailureMessage(errorMessageOf(error))) {\n setRecovering(false);\n return;\n }\n if (!recoverFromStaleChunkError(error)) setRecovering(false);\n }, [error]);\n return recovering;\n}\n\nfunction UpdatingScreen() {\n return (\n <main className=\"flex items-center justify-center min-h-screen p-4 bg-background text-foreground\">\n <p className=\"text-muted-foreground text-sm\">\n Loading the latest version…\n </p>\n </main>\n );\n}\n\nfunction ErrorScreen({ error }: { error: unknown }) {\n const recovering = useStaleChunkRecovery(error);\n // While auto-recovering a stale chunk, show a neutral state and skip the\n // console.error below so the transient, self-healing failure does not get\n // reported as a hard error.\n if (recovering) return <UpdatingScreen />;\n\n let status: number | null = null;\n let title = \"Something went wrong\";\n let details = \"An unexpected error occurred.\";\n let stack: string | undefined;\n\n if (isRouteErrorResponse(error)) {\n status = error.status;\n if (error.status === 404) {\n title = \"Page not found\";\n details = \"We couldn't find this page.\";\n } else {\n title = `${error.status} Error`;\n details = error.statusText || details;\n }\n } else if (error instanceof Error) {\n // Always surface the underlying error message — a generic\n // \"An unexpected error occurred.\" in production tells users (and us)\n // nothing. The stack trace is still gated to dev so we don't leak\n // internals to end users.\n if (error.message) {\n details = error.message;\n }\n if (\n typeof process !== \"undefined\" &&\n process.env.NODE_ENV !== \"production\"\n ) {\n stack = error.stack;\n }\n } else if (typeof error === \"string\" && error) {\n details = error;\n }\n\n // Log to the console so the underlying failure is recoverable from\n // browser devtools / Sentry even when the UI hides the stack.\n if (typeof console !== \"undefined\" && error) {\n console.error(\"[ErrorBoundary]\", error);\n }\n\n return (\n <main className=\"flex items-center justify-center min-h-screen p-4 bg-background text-foreground\">\n <div className=\"flex flex-col items-center text-center max-w-md\">\n {status && (\n <span className=\"text-7xl font-bold tracking-tight text-muted-foreground/40\">\n {status}\n </span>\n )}\n <h1 className=\"mt-3 text-2xl font-semibold\">{title}</h1>\n <p className=\"mt-2 text-muted-foreground text-sm\">{details}</p>\n <a href={appPath(\"/\")} className={homeLinkClassName}>\n Go home\n </a>\n <button\n type=\"button\"\n onClick={() => window.location.reload()}\n className=\"mt-3 inline-flex cursor-pointer items-center gap-1.5 rounded-md border border-border bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm hover:bg-accent\"\n >\n Reload\n </button>\n {stack && (\n <pre className=\"mt-6 w-full text-start text-xs overflow-auto p-4 bg-muted rounded\">\n <code>{stack}</code>\n </pre>\n )}\n </div>\n </main>\n );\n}\n\nfunction RoutedErrorScreen() {\n return <ErrorScreen error={useRouteError()} />;\n}\n\nexport function ErrorBoundary() {\n useApplyThemeClass();\n if (!useInRouterContext()) return <ErrorScreen error={undefined} />;\n return <RoutedErrorScreen />;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ErrorBoundary.js","sourceRoot":"","sources":["../../src/client/ErrorBoundary.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useState } from \"react\";\nimport {\n isRouteErrorResponse,\n useInRouterContext,\n useRouteError,\n} from \"react-router\";\nimport { appPath } from \"./api-path.js\";\nimport {\n isDynamicImportFailureMessage,\n recoverFromStaleChunkError,\n} from \"./route-chunk-recovery.js\";\nimport {\n DEFAULT_LOCALE,\n LOCALE_HYDRATION_GLOBAL,\n LOCALE_STORAGE_KEY,\n normalizeLocaleCode,\n type LocaleCode,\n} from \"../localization/shared.js\";\n\nconst homeLinkClassName =\n \"mt-6 inline-flex items-center gap-1.5 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm hover:bg-primary/90 cursor-pointer\";\n\nconst errorCopy: Record<\n LocaleCode,\n {\n loadingLatest: string;\n genericTitle: string;\n genericDetails: string;\n notFoundTitle: string;\n notFoundDetails: string;\n statusTitle: (status: number) => string;\n goHome: string;\n reload: string;\n }\n> = {\n \"en-US\": {\n loadingLatest: \"Loading the latest version...\",\n genericTitle: \"Something went wrong\",\n genericDetails: \"An unexpected error occurred.\",\n notFoundTitle: \"Page not found\",\n notFoundDetails: \"We couldn't find this page.\",\n statusTitle: (status) => `${status} Error`,\n goHome: \"Go home\",\n reload: \"Reload\",\n },\n \"zh-CN\": {\n loadingLatest: \"正在加载最新版本...\",\n genericTitle: \"出错了\",\n genericDetails: \"发生了意外错误。\",\n notFoundTitle: \"页面未找到\",\n notFoundDetails: \"我们找不到这个页面。\",\n statusTitle: (status) => `${status} 错误`,\n goHome: \"回到首页\",\n reload: \"重新加载\",\n },\n \"es-ES\": {\n loadingLatest: \"Cargando la versión más reciente...\",\n genericTitle: \"Algo salió mal\",\n genericDetails: \"Se produjo un error inesperado.\",\n notFoundTitle: \"Página no encontrada\",\n notFoundDetails: \"No pudimos encontrar esta página.\",\n statusTitle: (status) => `Error ${status}`,\n goHome: \"Ir al inicio\",\n reload: \"Recargar\",\n },\n \"fr-FR\": {\n loadingLatest: \"Chargement de la dernière version...\",\n genericTitle: \"Un problème est survenu\",\n genericDetails: \"Une erreur inattendue s'est produite.\",\n notFoundTitle: \"Page introuvable\",\n notFoundDetails: \"Nous n'avons pas trouvé cette page.\",\n statusTitle: (status) => `Erreur ${status}`,\n goHome: \"Accueil\",\n reload: \"Recharger\",\n },\n \"de-DE\": {\n loadingLatest: \"Neueste Version wird geladen...\",\n genericTitle: \"Etwas ist schiefgelaufen\",\n genericDetails: \"Ein unerwarteter Fehler ist aufgetreten.\",\n notFoundTitle: \"Seite nicht gefunden\",\n notFoundDetails: \"Wir konnten diese Seite nicht finden.\",\n statusTitle: (status) => `Fehler ${status}`,\n goHome: \"Zur Startseite\",\n reload: \"Neu laden\",\n },\n \"ja-JP\": {\n loadingLatest: \"最新バージョンを読み込み中...\",\n genericTitle: \"問題が発生しました\",\n genericDetails: \"予期しないエラーが発生しました。\",\n notFoundTitle: \"ページが見つかりません\",\n notFoundDetails: \"このページは見つかりませんでした。\",\n statusTitle: (status) => `${status} エラー`,\n goHome: \"ホームへ\",\n reload: \"再読み込み\",\n },\n \"ko-KR\": {\n loadingLatest: \"최신 버전을 불러오는 중...\",\n genericTitle: \"문제가 발생했습니다\",\n genericDetails: \"예기치 않은 오류가 발생했습니다.\",\n notFoundTitle: \"페이지를 찾을 수 없음\",\n notFoundDetails: \"이 페이지를 찾을 수 없습니다.\",\n statusTitle: (status) => `${status} 오류`,\n goHome: \"홈으로 이동\",\n reload: \"새로고침\",\n },\n \"pt-BR\": {\n loadingLatest: \"Carregando a versão mais recente...\",\n genericTitle: \"Algo deu errado\",\n genericDetails: \"Ocorreu um erro inesperado.\",\n notFoundTitle: \"Página não encontrada\",\n notFoundDetails: \"Não encontramos esta página.\",\n statusTitle: (status) => `Erro ${status}`,\n goHome: \"Ir para início\",\n reload: \"Recarregar\",\n },\n \"hi-IN\": {\n loadingLatest: \"नवीनतम संस्करण लोड हो रहा है...\",\n genericTitle: \"कुछ गलत हो गया\",\n genericDetails: \"एक अनपेक्षित त्रुटि हुई।\",\n notFoundTitle: \"पेज नहीं मिला\",\n notFoundDetails: \"हमें यह पेज नहीं मिला।\",\n statusTitle: (status) => `${status} त्रुटि`,\n goHome: \"होम पर जाएं\",\n reload: \"रीलोड करें\",\n },\n \"ar-SA\": {\n loadingLatest: \"جار تحميل أحدث إصدار...\",\n genericTitle: \"حدث خطأ ما\",\n genericDetails: \"حدث خطأ غير متوقع.\",\n notFoundTitle: \"الصفحة غير موجودة\",\n notFoundDetails: \"تعذر العثور على هذه الصفحة.\",\n statusTitle: (status) => `خطأ ${status}`,\n goHome: \"العودة للرئيسية\",\n reload: \"إعادة التحميل\",\n },\n};\n\nfunction readErrorLocale(): LocaleCode {\n if (typeof window === \"undefined\") return DEFAULT_LOCALE;\n const hydrated = (window as any)[LOCALE_HYDRATION_GLOBAL]?.locale;\n const stored = window.localStorage?.getItem(LOCALE_STORAGE_KEY);\n return (\n normalizeLocaleCode(hydrated) ??\n normalizeLocaleCode(stored) ??\n DEFAULT_LOCALE\n );\n}\n\nfunction useErrorCopy() {\n const [locale, setLocale] = useState<LocaleCode>(readErrorLocale);\n useEffect(() => {\n setLocale(readErrorLocale());\n }, []);\n return errorCopy[locale] ?? errorCopy[DEFAULT_LOCALE];\n}\n\nfunction useApplyThemeClass() {\n useEffect(() => {\n const root = document.documentElement;\n if (root.classList.contains(\"dark\") || root.classList.contains(\"light\"))\n return;\n try {\n const stored = localStorage.getItem(\"theme\");\n if (stored === \"dark\") {\n root.classList.add(\"dark\");\n } else if (stored === \"light\") {\n root.classList.add(\"light\");\n } else if (window.matchMedia(\"(prefers-color-scheme: dark)\").matches) {\n root.classList.add(\"dark\");\n }\n } catch {}\n }, []);\n}\n\nfunction errorMessageOf(error: unknown): string {\n if (error instanceof Error) return error.message;\n return typeof error === \"string\" ? error : \"\";\n}\n\n/**\n * When a route renders against a stale lazy chunk after a deploy (the chunk's\n * hashed filename no longer exists), the import rejection surfaces here. Reload\n * once to fetch fresh assets instead of stranding the user on an error screen.\n * The reload is loop-guarded; if it cannot recover, fall back to the screen.\n */\nfunction useStaleChunkRecovery(error: unknown): boolean {\n const [recovering, setRecovering] = useState(() =>\n isDynamicImportFailureMessage(errorMessageOf(error)),\n );\n useEffect(() => {\n if (!isDynamicImportFailureMessage(errorMessageOf(error))) {\n setRecovering(false);\n return;\n }\n if (!recoverFromStaleChunkError(error)) setRecovering(false);\n }, [error]);\n return recovering;\n}\n\nfunction UpdatingScreen() {\n const copy = useErrorCopy();\n return (\n <main className=\"flex items-center justify-center min-h-screen p-4 bg-background text-foreground\">\n <p className=\"text-muted-foreground text-sm\">{copy.loadingLatest}</p>\n </main>\n );\n}\n\nfunction ErrorScreen({ error }: { error: unknown }) {\n const copy = useErrorCopy();\n const recovering = useStaleChunkRecovery(error);\n // While auto-recovering a stale chunk, show a neutral state and skip the\n // console.error below so the transient, self-healing failure does not get\n // reported as a hard error.\n if (recovering) return <UpdatingScreen />;\n\n let status: number | null = null;\n let title = copy.genericTitle;\n let details = copy.genericDetails;\n let stack: string | undefined;\n\n if (isRouteErrorResponse(error)) {\n status = error.status;\n if (error.status === 404) {\n title = copy.notFoundTitle;\n details = copy.notFoundDetails;\n } else {\n title = copy.statusTitle(error.status);\n details = error.statusText || details;\n }\n } else if (error instanceof Error) {\n // Always surface the underlying error message — a generic\n // \"An unexpected error occurred.\" in production tells users (and us)\n // nothing. The stack trace is still gated to dev so we don't leak\n // internals to end users.\n if (error.message) {\n details = error.message;\n }\n if (\n typeof process !== \"undefined\" &&\n process.env.NODE_ENV !== \"production\"\n ) {\n stack = error.stack;\n }\n } else if (typeof error === \"string\" && error) {\n details = error;\n }\n\n // Log to the console so the underlying failure is recoverable from\n // browser devtools / Sentry even when the UI hides the stack.\n if (typeof console !== \"undefined\" && error) {\n console.error(\"[ErrorBoundary]\", error);\n }\n\n return (\n <main className=\"flex items-center justify-center min-h-screen p-4 bg-background text-foreground\">\n <div className=\"flex flex-col items-center text-center max-w-md\">\n {status && (\n <span className=\"text-7xl font-bold tracking-tight text-muted-foreground/40\">\n {status}\n </span>\n )}\n <h1 className=\"mt-3 text-2xl font-semibold\">{title}</h1>\n <p className=\"mt-2 text-muted-foreground text-sm\">{details}</p>\n <a href={appPath(\"/\")} className={homeLinkClassName}>\n {copy.goHome}\n </a>\n <button\n type=\"button\"\n onClick={() => window.location.reload()}\n className=\"mt-3 inline-flex cursor-pointer items-center gap-1.5 rounded-md border border-border bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm hover:bg-accent\"\n >\n {copy.reload}\n </button>\n {stack && (\n <pre className=\"mt-6 w-full text-start text-xs overflow-auto p-4 bg-muted rounded\">\n <code>{stack}</code>\n </pre>\n )}\n </div>\n </main>\n );\n}\n\nfunction RoutedErrorScreen() {\n return <ErrorScreen error={useRouteError()} />;\n}\n\nexport function ErrorBoundary() {\n useApplyThemeClass();\n if (!useInRouterContext()) return <ErrorScreen error={undefined} />;\n return <RoutedErrorScreen />;\n}\n"]}
|
|
@@ -162,19 +162,19 @@ export default defineAction({
|
|
|
162
162
|
|
|
163
163
|
بالنسبة لإجراء `GET`، يتم تمرير `leadId` كمعلمة استعلام: `/_agent-native/actions/get-lead?leadId=abc`.
|
|
164
164
|
|
|
165
|
-
```an-api title="
|
|
165
|
+
```an-api title="نقطة نهاية action المثبتة تلقائيا" method="GET" path="/_agent-native/actions/get-lead"
|
|
166
166
|
{
|
|
167
167
|
"method": "GET",
|
|
168
168
|
"path": "/_agent-native/actions/get-lead",
|
|
169
|
-
"summary": "
|
|
170
|
-
"description": "
|
|
171
|
-
"auth": "
|
|
169
|
+
"summary": "كل action يثبت هنا تلقائيا - اسم الملف هو اسم action.",
|
|
170
|
+
"description": "الافتراضي هو POST؛ `http: { method: \"GET\" }` يجعلها GET. تستدعي React hooks و `callAction` هذا المسار دائما بالاسم، بغض النظر عن أي تجاوز `http.path`.",
|
|
171
|
+
"auth": "ملف تعريف ارتباط للجلسة؛ تحمل استدعاءات الواجهة `X-Agent-Native-Frontend: 1`",
|
|
172
172
|
"params": [
|
|
173
|
-
{ "name": "leadId", "in": "query", "type": "string", "required": true, "description": "
|
|
173
|
+
{ "name": "leadId", "in": "query", "type": "string", "required": true, "description": "تصل وسائط GET كمعاملات query؛ وتصل وسائط POST في جسم JSON." }
|
|
174
174
|
],
|
|
175
175
|
"responses": [
|
|
176
|
-
{ "status": "200", "description": "
|
|
177
|
-
{ "status": "400", "description": "
|
|
176
|
+
{ "status": "200", "description": "قيمة إرجاع action بصيغة JSON." },
|
|
177
|
+
{ "status": "400", "description": "فشل الإدخال في التحقق من schema قبل تشغيل run()." }
|
|
178
178
|
]
|
|
179
179
|
}
|
|
180
180
|
```
|
|
@@ -25,7 +25,7 @@ Codex، أو رمز Claude، أو Markdown، أو خطة التنفيذ المل
|
|
|
25
25
|
|
|
26
26
|
```an-diagram title="أمرين، وسطح مراجعة واحد" summary="يتم نشر كلا الأمرين من خلال موصل Plan MCP المستضاف في نفس سطح التعليق التوضيحي والتعليق."
|
|
27
27
|
{
|
|
28
|
-
"html": "<div class=\"diagram-plan\"><div class=\"diagram-col\"><div class=\"diagram-node\"><span class=\"diagram-pill accent\">/visual-plan</span><small class=\"diagram-muted\"
|
|
28
|
+
"html": "<div class=\"diagram-plan\"><div class=\"diagram-col\"><div class=\"diagram-node\"><span class=\"diagram-pill accent\">/visual-plan</span><small class=\"diagram-muted\">قبل الكود - البنية وواجهة UI وإعادة التنظيم</small></div><div class=\"diagram-node\"><span class=\"diagram-pill\">/visual-recap</span><small class=\"diagram-muted\">بعد الكود - PR و commit و branch و diff</small></div></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-panel center\">موصل Plan MCP<br><small class=\"diagram-muted\">plan.agent-native.com</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">→</div><div class=\"diagram-box\">سطح المراجعة<br><small class=\"diagram-muted\">مخططات · إطارات سلكية · كود مشروح · تعليقات</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">↔</div><div class=\"diagram-node\">وكيل البرمجة<br><small class=\"diagram-muted\">تمت إعادة الملاحظات</small></div></div>",
|
|
29
29
|
"css": ".diagram-plan{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.diagram-plan .diagram-col{display:flex;flex-direction:column;gap:10px}.diagram-plan .diagram-arrow{font-size:22px;line-height:1}.diagram-plan .center{display:flex;flex-direction:column;align-items:center;gap:4px;text-align:center}"
|
|
30
30
|
}
|
|
31
31
|
```
|
|
@@ -33,7 +33,7 @@ Codex، أو رمز Claude، أو Markdown، أو خطة التنفيذ المل
|
|
|
33
33
|
```an-wireframe
|
|
34
34
|
{
|
|
35
35
|
"surface": "desktop",
|
|
36
|
-
"html": "<div style='display:grid;grid-template-columns:1fr 250px;gap:14px;padding:16px;min-height:520px;box-sizing:border-box'><main style='display:flex;flex-direction:column;gap:12px;min-width:0'><div style='display:flex;align-items:center;gap:10px'><h1 style='margin:0'
|
|
36
|
+
"html": "<div style='display:grid;grid-template-columns:1fr 250px;gap:14px;padding:16px;min-height:520px;box-sizing:border-box'><main style='display:flex;flex-direction:column;gap:12px;min-width:0'><div style='display:flex;align-items:center;gap:10px'><h1 style='margin:0'>خطة إعادة تصميم الدفع</h1><div style='flex:1'></div><button>مشاركة</button><button class='primary'>موافقة</button></div><div class='wf-card' style='display:grid;grid-template-columns:1fr 1fr;gap:10px;min-height:150px'><div class='wf-box'>الإطار السلكي الحالي</div><div class='wf-box'>الإطار السلكي المقترح</div></div><div class='wf-card' style='flex:1;display:flex;flex-direction:column;gap:10px'><strong>خطة التنفيذ</strong><div class='wf-box'>قرار: الإبقاء على غلاف الدفع الحالي</div><div class='wf-box'>شرح الكود مع التعليقات</div><div class='wf-box'>أسئلة مفتوحة</div></div></main><aside class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>التعليقات</strong><div class='wf-box'>تثبيت على CTA الأساسي</div><div class='wf-box'>سؤال للوكيل</div><div class='wf-box'>ملاحظة نصية محلولة</div><button class='primary'>إرجاع الملاحظات</button></aside></div>"
|
|
37
37
|
}
|
|
38
38
|
```
|
|
39
39
|
|
|
@@ -570,7 +570,8 @@ const riskCardServerBlock = defineBlock<RiskCardData>({
|
|
|
570
570
|
Read: ServerReadStub,
|
|
571
571
|
placement: ["block"],
|
|
572
572
|
label: "Risk card",
|
|
573
|
-
description:
|
|
573
|
+
description:
|
|
574
|
+
"ملاحظة مخاطر بصيغة markdown بدرجة خطورة منخفضة أو متوسطة أو عالية.",
|
|
574
575
|
});
|
|
575
576
|
|
|
576
577
|
export function registerPlanBlocks(registry: BlockRegistry): void {
|
|
@@ -622,7 +623,8 @@ const riskCardBlock = defineBlock<RiskCardData>({
|
|
|
622
623
|
placement: ["block"],
|
|
623
624
|
editSurface: "panel",
|
|
624
625
|
label: "Risk card",
|
|
625
|
-
description:
|
|
626
|
+
description:
|
|
627
|
+
"ملاحظة مخاطر بصيغة markdown بدرجة خطورة منخفضة أو متوسطة أو عالية.",
|
|
626
628
|
empty: () => ({ severity: "medium", body: "Describe the risk." }),
|
|
627
629
|
});
|
|
628
630
|
|