Compare commits

...

2 Commits

Author SHA1 Message Date
雨霖铃 fd269348b4 fix: add missing Toaster component to enable toast display
The project was calling toast.success/error/warning from sonner in 7 files,
but the <Toaster> component was never rendered in the React tree. This meant
sonner stored toast data internally but no UI was ever displayed to users.

Changes:
- Create src/components/ui/sonner.tsx with shadcn/ui-style Toaster wrapper
  that integrates with ThemeModeProvider for light/dark theme support
- Add <Toaster /> to AppRoot.tsx so it works across all entry points
  (popup, sidepanel, browser-tab)

fix: always convert dots to underscores in i18n t() key lookup

The t() function only converted dots to underscores when the key contained
a colon (namespace:key format). Keys using dot notation like
'messages.copySuccess' were passed directly to chrome.i18n.getMessage()
without dot-to-underscore conversion, causing lookups to fail silently and
return empty strings - resulting in toast notifications with no text.

Now all separators (both ':' and '.') are consistently converted to '_'
regardless of format, matching the messages.json key convention.

fix: position toast at bottom-center instead of bottom-right
2026-06-05 19:24:03 +08:00
LingandRX 58af2af37b fix: 右键菜单 srcUrl 误判、pre-push 变更文件检测、generating 状态不可见 (#64)
* fix: 修复右键菜单 srcUrl 误判、pre-push 全量扫描、generating 状态不可见问题

- contextMenu: srcUrl 检查限定为 qrCode-image 菜单项,避免其他菜单误返回图片 URL
- pre-push: eslint 改为只检查变更文件(git diff 过滤),tsc 保留全项目检查
- useQrCode: confirmGenerate 用 setTimeout 延迟生成,让 loading 状态先被 React 渲染
- tests: 生成相关测试改为 async + waitFor 适配异步 confirmGenerate

* fix(hooks): pre-push 无 upstream 时回退到 origin/main 对比

* fix(hooks): 用 merge-base 替代 rev-parse 修复 pre-push 变更文件检测
2026-06-05 13:10:44 +08:00
4 changed files with 36 additions and 10 deletions
+4 -2
View File
@@ -4,8 +4,10 @@ npx tsc --noEmit
# ESLint 严格检查(仅检查本次推送的变更文件,不阻塞不相关的旧代码)
# 新分支无 upstream 时,回退到与 origin/main 对比
BASE=$(git rev-parse --verify @{upstream} 2>/dev/null && echo "@{upstream}" || echo "origin/main")
CHANGED_FILES=$(git diff --name-only --diff-filter=d "$BASE"...HEAD -- '*.ts' '*.tsx' '*.js' '*.jsx' '*.mjs')
MERGE_BASE=$(git merge-base HEAD @{upstream} 2>/dev/null || git merge-base HEAD origin/main 2>/dev/null)
if [ -n "$MERGE_BASE" ]; then
CHANGED_FILES=$(git diff --name-only --diff-filter=d "$MERGE_BASE" HEAD -- '*.ts' '*.tsx' '*.js' '*.jsx' '*.mjs')
if [ -n "$CHANGED_FILES" ]; then
echo "$CHANGED_FILES" | xargs npx eslint --max-warnings=0
fi
fi
+26
View File
@@ -0,0 +1,26 @@
import { Toaster as Sonner } from 'sonner';
import { useThemeMode } from '@/providers/ThemeModeProvider';
type ToasterProps = React.ComponentProps<typeof Sonner>;
export function Toaster(props: ToasterProps) {
const { resolvedMode } = useThemeMode();
return (
<Sonner
theme={resolvedMode}
className="toaster group"
position="bottom-center"
toastOptions={{
classNames: {
toast:
'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg',
description: 'group-[.toast]:text-muted-foreground',
actionButton: 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
cancelButton: 'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground',
},
}}
{...props}
/>
);
}
+2
View File
@@ -1,6 +1,7 @@
import React from 'react';
import { ThemeModeProvider } from './ThemeModeProvider';
import { RouterProvider } from './RouterProvider';
import { Toaster } from '@/components/ui/sonner';
interface AppRootProps {
children: React.ReactNode;
@@ -11,6 +12,7 @@ export default function AppRoot({ children }: AppRootProps) {
<React.StrictMode>
<ThemeModeProvider>
<RouterProvider>{children}</RouterProvider>
<Toaster />
</ThemeModeProvider>
</React.StrictMode>
);
+2 -6
View File
@@ -26,12 +26,8 @@ export function useI18n(namespace?: string | string[]) {
const namespaces = Array.isArray(namespace) ? namespace : namespace ? [namespace] : [];
const t = (key: string, options?: Record<string, unknown>): string => {
let msgId = key;
// 处理 namespace:key 格式(兼容原 i18next 用法)
if (key.includes(':')) {
msgId = key.replace(':', '_').replace(/\./g, '_');
}
// 统一将分隔符转换为下划线,兼容 'namespace:key.path' 和 'key.path' 两种写法
const msgId = key.replace(':', '_').replace(/\./g, '_');
// 先尝试直接查找 key
let message = getMessage(msgId);