d4c29a1bf4
* fix(ci): fix release workflow artifact upload issue - Split artifact upload into 3 separate steps (Chrome, Firefox, Source) - Update download steps to match new artifact names - Add if-no-files-found: error for better error handling - Fix Node.js 20 compatibility issue with upload-artifact@v4 glob pattern * docs: 更新 AGENTS.md,添加测试工具和翻译文件结构说明 * docs: 更新 AGENTS.md,完善 CI 步骤和存储键名格式说明 * fix: remove unnecessary animations from StorageCleaner page - Remove entrance animations (animate-in, fade-in, zoom-in) from all components - Remove scale effect (active:scale-[0.99]) from clean button - Remove transition effects (transition-all, transition-colors) from OptionItem, StorageOptionsGrid, AutoRefreshToggle - Remove bouncing animation from error icon - Remove pulsing animation from warning banner - Keep animate-spin on button loading spinner as functional indicator * fix: remove unnecessary animations from RightClickRestorer page * fix: remove all unnecessary animations from all pages - Remove dead code animations (animate-in, fade-in, slide-in, zoom-in, shake) from tailwindcss-animate plugin (not installed) - Remove decorative transition effects (transition-all, transition-colors) from all page components - Remove scale effects (active:scale-95) from buttons - Remove bounce animations (animate-bounce) from icons - Keep functional animate-spin on loading spinners as they provide essential loading feedback Affected pages: Dashboard, Timestamp, Jwt, JsonTools, Base64Converter, HtmlToMarkdown, MarkdownToHtml, QrCode, TextStatistics * docs: 添加项目文档(copilot-instructions、编码规范、各目录 README) - 新增 .github/copilot-instructions.md:Copilot 指令文件,涵盖构建命令、CI 流水线、项目架构、关键规范 - 新增 .github/CODING_STANDARDS.md:完整的代码编写规范文档(TypeScript、React、样式、错误处理、命名、测试、Hook、i18n、存储等 12 个章节) - 新增 11 个目录的 README.md:components、components/ui、config、entrypoints、i18n、lib、pages、providers、public、src、types、utils - 更新 AGENTS.md:补充页面组件模式说明和 components/ui 目录 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(CopyButton): use shadcn buttonVariants instead of duplicated variant classes Remove hand-written variantClasses/sizeClasses that duplicated shadcn's buttonVariants. Now extends ButtonProps and imports buttonVariants from components/ui/button for consistent styling. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(ErrorBoundary): use shadcn destructive tokens instead of hardcoded red colors Replace hardcoded red-200/red-50/red-600 with border-destructive, bg-destructive/5, text-destructive. Use variant='destructive' on Button. Align error log styling with PageErrorBoundary. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(TextInputArea): replace hardcoded Chinese strings with i18n t() calls Use existing translation keys for clear success, copy success, and copy error messages instead of hardcoded Chinese text. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(QrCodePreview): use shadcn Button instead of manual button styles Replace hand-written button class strings with shadcn Button component using outline and default variants. Removes ~15 lines of duplicated Tailwind classes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs(GlobalSnackbar): clarify relationship with sonner toast Add note explaining when to use GlobalSnackbar (provider context, severity levels, custom positioning) vs sonner toast (simple one-off messages). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(ImageUploader): wrap handleClearFile with useCallback Prevents unnecessary re-renders by memoizing the callback, consistent with handleFileChange which already uses useCallback. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(SwitchButtonGroup): remove redundant containerPadding conditional Both branches of the ternary returned 'p-1'. Inline the constant directly into the className string. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(TopBar): add missing openExtensionPage import The function was called in handleOpenInTab but never imported from utils/chromeTabs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(TopBar): add selectedIndex boundary protection in keyboard nav Guard against out-of-bounds access when search results change during keyboard navigation. Check selectedIndex < totalItems before accessing arrays. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(RouterContainer): remove unnecessary empty-dep useMemo getEntryPointType() is a simple config getter. Replace useMemo(() => ..., []) with a direct call — the empty dep array made the memo pointless. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(i18n): add missing translation keys for error boundaries and copy messages Add keys for errorBoundary, pageErrorBoundary, router, and messages.copyEmpty in both zh and en locales. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(CopyButton): replace hardcoded Chinese strings with i18n t() calls Use useTranslation('common') for tooltip, copy empty/success/error toast messages. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(ErrorBoundary): replace hardcoded Chinese with i18n via withTranslation HOC Use react-i18next withTranslation HOC for class component to translate title, description, and refresh button text. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(PageErrorBoundary): replace hardcoded Chinese with i18n via withTranslation HOC Use react-i18next withTranslation HOC for class component to translate title, description, and retry button text. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(RouterContainer): replace hardcoded Chinese with i18n t() calls Use useTranslation('common') for 404 page title and description with entryPointType interpolation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(pages): 修复 TypeScript 错误、硬编码字符串,移除死代码 - 修复 LiveClock.tsx CopyButton size 属性类型错误 (small → sm) - 修复 ToolCard.tsx 紫色 RGB 值拼写错误 (147,51,2 purple → 147,51,232) - 替换 4 处硬编码中文为 i18n 调用 (StorageCleaner, JsonTools) - MarkdownToHtml 预览链接色改为 CSS 变量以支持暗黑模式 - 移除 Base64Converter 中已被 Base64ConverterSection 替代的死代码 (FileMode.tsx, ImageMode.tsx 及其测试文件) - 更新 README.md 移除对已删除文件的引用 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(CopyButton, LiveClock): 修改按钮大小为 'icon',移除硬编码颜色配置 * refactor(TextInputArea): 复用 CopyButton 组件替换内联复制逻辑 * fix(test): 修复 CopyButton mock 未捕获 clipboard writeText 异常 --------- Co-authored-by: Ubuntu <ubuntu@localhost.localdomain> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
100 lines
3.8 KiB
TypeScript
100 lines
3.8 KiB
TypeScript
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
import { Clock } from 'lucide-react';
|
|
import CopyButton from '@/components/CopyButton';
|
|
import { useSnackbar } from '@/components/GlobalSnackbar';
|
|
import type { UnitType } from './constants';
|
|
import { useLazyTranslation } from '@/utils/useLazyTranslation';
|
|
import { cn } from '@/lib/utils'; // 引入标准的 shadcn 工具函数
|
|
|
|
interface LiveClockProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
unit: UnitType;
|
|
onUseNow: (val: number) => void;
|
|
}
|
|
|
|
const LiveClock = React.memo(({ unit, onUseNow, className, ...props }: LiveClockProps) => {
|
|
const { t } = useLazyTranslation('timestamp');
|
|
const { showMessage } = useSnackbar();
|
|
const onUseNowRef = useRef(onUseNow);
|
|
|
|
// 1. 采用毫秒/秒的双态原子计数,避免无意义的重绘
|
|
const [currentDisplay, setCurrentDisplay] = useState(() => {
|
|
const initNow = Date.now();
|
|
return {
|
|
rawTime: initNow,
|
|
text: String(Math.floor(initNow / (unit === 'ms' ? 1 : 1000))),
|
|
};
|
|
});
|
|
|
|
// 始终保持外部回调指针最新
|
|
useEffect(() => {
|
|
onUseNowRef.current = onUseNow;
|
|
}, [onUseNow]);
|
|
|
|
// 2. 高频高灵敏度计时器 (200ms 刷新率)
|
|
useEffect(() => {
|
|
const tick = () => {
|
|
const rightNow = Date.now();
|
|
const nextText = String(Math.floor(rightNow / (unit === 'ms' ? 1 : 1000)));
|
|
|
|
// 性能核心:只有当生成的文本内容发生变化时,才触发 React 的 State 更新。
|
|
// 在“秒(s)”单位下,这可以让组件的渲染频率暴跌 90%,做到极度省电和高性能。
|
|
setCurrentDisplay((prev) => {
|
|
if (prev.text === nextText) return prev;
|
|
return { rawTime: rightNow, text: nextText };
|
|
});
|
|
};
|
|
|
|
// 200ms 的高速低延迟轮询,比 1000ms 更具响应灵敏度,且因为上面有过滤,完全不用担心引发性能损耗
|
|
const tickId = setInterval(tick, 200);
|
|
return () => clearInterval(tickId);
|
|
}, [unit]);
|
|
|
|
const handleUseNow = useCallback(() => {
|
|
// 捕获真实极其精准的绝对时间戳
|
|
onUseNowRef.current(currentDisplay.rawTime);
|
|
showMessage?.(t('timestamp:usedSuccess'), { severity: 'success' });
|
|
}, [currentDisplay.rawTime, showMessage, t]);
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
// 3. 完美适配 shadcn 暗黑模式:
|
|
// 不再写死 bg-primary/10,改用更高级的 bg-secondary/50 和中性边框,
|
|
// 在任何主题色下都能表现得低调且极具质感。
|
|
'flex items-center gap-3 px-3 h-10 rounded-lg border border-border/80 bg-secondary/50',
|
|
className,
|
|
)}
|
|
{...props}
|
|
>
|
|
<span className="text-muted-foreground font-bold text-[10px] uppercase tracking-wider whitespace-nowrap shrink-0 selection:bg-transparent select-none">
|
|
{t('timestamp:currentTs')}
|
|
</span>
|
|
|
|
{/* 4. tabular-nums 强制使用等宽数字布局,彻底消灭数字跳动时字符宽度不同带来的抖动颤噪感 */}
|
|
<span className="flex-1 font-mono font-bold text-foreground text-sm tracking-tight leading-none truncate tabular-nums">
|
|
{currentDisplay.text}
|
|
</span>
|
|
|
|
{/* 5. 按钮重构成精巧的 shadcn 原子微动效风格 */}
|
|
<button
|
|
type="button"
|
|
onClick={handleUseNow}
|
|
title={t('timestamp:useNowTooltip')}
|
|
className="flex h-7 w-7 items-center justify-center rounded-md border border-input bg-background text-muted-foreground shadow-sm hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
>
|
|
<Clock className="w-3.5 h-3.5" />
|
|
</button>
|
|
|
|
<CopyButton
|
|
text={currentDisplay.text}
|
|
tooltip={t('timestamp:copyTsTooltip')}
|
|
className="h-7 w-7 rounded-md border"
|
|
/>
|
|
</div>
|
|
);
|
|
});
|
|
|
|
LiveClock.displayName = 'LiveClock';
|
|
|
|
export default LiveClock;
|