Skip to content

Commit bff1889

Browse files
committed
feat: update frontend movement and pickers
1 parent 933373a commit bff1889

19 files changed

Lines changed: 519 additions & 175 deletions

File tree

CHANGELOG.md

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
All notable changes to TimeLens are documented here.
44
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5-
65
---
76

87
## [2.3.0] - 2026-08-28
@@ -20,6 +19,16 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
2019
- **Update flow tests** — added coverage for update checking, download confirmation, download progress, install confirmation, and unavailable updater states.
2120
- **Scoped Local API credentials** — API credentials now have a nickname and independently configured data-access and operation permissions, with revocation and one-time secret reveal/copy behavior.
2221
- **Local API permission picker** — Settings now provides localized multi-select controls for the Local API's supported read, subscription, and write scopes.
22+
- **Animation & Motion Control Center** — Added user-selectable motion preset controls (`Full Animations`, `Reduced Motion`, `Disable All Animations`) in Settings.
23+
- **Granular Animation Controls List** — Added independent toggle options for 6 specific motion categories:
24+
- Page & Tab Transitions
25+
- Card Hover & Micro-interactions
26+
- Modal & Dialog Animations
27+
- Widget Window Motion
28+
- Chart & Data Animations
29+
- Pulse & Status Effects
30+
- **Multi-language Support (i18n)** — Added full translation strings for all animation modes and granular toggle items across English (`en`), Simplified Chinese (`zh-CN`), and Traditional Chinese (`zh-TW`).
31+
2332

2433
### Changed
2534

@@ -36,6 +45,11 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
3645
- **Extension Bridge compatibility** — legacy extension bridge keys remain fully authorized for existing integrations but are no longer exposed in Settings.
3746
- **Settings presentation** — neutral texture options include an aurora gradient, and Settings surfaces use lighter, transparent grouping backgrounds so the active skin remains visible.
3847
- **Themed switches** — Settings toggles use the active palette's accent color, including the default blue palette, instead of native white controls.
48+
- **UI & UX Design Polish** — Refined card, button, and settings control designs with improved glassmorphism backdrop blur and top-border highlights (`inset 0 1px 0 rgba(255, 255, 255, 0.1)`).
49+
- **Smoother Apple-Style Easing** — Applied modern cubic-bezier easing (`cubic-bezier(0.16, 1, 0.3, 1)`) for micro-interactions and page entry transitions.
50+
- **High-Contrast Segmented Controls & Tab Buttons** — Replaced low-contrast white-on-light selection states with high-contrast `bg-accent-blue/15 text-accent-blue border border-accent-blue/30` styling across Widget Center, Browser Usage, Dashboard Insights, Quick Capture, and Timer widgets.
51+
- **Unified Hover Micro-Interactions** — Standardized hover states across sidebars, search buttons, settings items, and tab options to use consistent soft blue tinting (`bg-accent-blue/10 text-accent-blue`).
52+
- **Collapsible Widget Center Layout** — Compacted top "Layout Presets" and "Widget Health Center" cards into collapsible sections (collapsed by default) to maximize space for the main widget list.
3953

4054
### Fixed
4155

@@ -50,21 +64,17 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5064
- **CodeQL Rust extraction** — changed the Rust CodeQL job to `manual` build mode and moved the Cargo build after CodeQL initialization.
5165
- **Cryptographic value detection** — changed Argon2 output-buffer initialization so CodeQL no longer reports the derived-key buffer as a hard-coded cryptographic value.
5266
- **Manual update notification** — fixed Settings > About so a manually detected update opens the same confirmation dialog used by automatic checks.
67+
- **kbd Shortcut Contrast** — Replaced dark purple keycap badge backgrounds in search inputs with high-contrast card borders (`bg-surface-card border border-surface-border text-text-secondary`) for clear visibility in both light and dark modes.
68+
- **Sidebar Hover CSS Specificity** — Removed CSS `!important` overrides that interfered with hover state specificity on navigation items.
69+
- **Settings Card Background Noise** — Removed dark container card background wrappers around motion controls, making animation options render seamlessly as standard SettingsRows.
70+
- **Removed Motion Bounce & Translate Y Shifts** — Eliminated unwanted button/card jump, scale, and translateY bouncing on click and setting toggle events.
5371

5472
### Security
5573

5674
- **Managed local resources** — image imports enforce allowed extensions, file signatures, size limits, managed-directory boundaries, and traversal protection.
5775
- **Gateway request governance** — network, media, local API, and notification requests continue through permission checks, audit logging, timeouts, response limits, and normalized errors.
5876
- **Dependency audit**`npm audit --audit-level=high` reports zero vulnerabilities for the v2.3.0 dependency tree.
5977

60-
### Tests
61-
62-
- Frontend tests: **71 passed**.
63-
- TypeScript typecheck: passed.
64-
- ESLint: 0 errors; 8 existing warnings remain.
65-
- Rust `cargo check`: passed.
66-
- Rust unit tests compile successfully, but execution is blocked on the current Windows environment by `0xc0000139 STATUS_ENTRYPOINT_NOT_FOUND` during test-process startup.
67-
- `git diff --check`: passed.
6878

6979
---
7080

src/App.tsx

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ export default function App() {
3131
const [activePalette, setActivePalette] = useState(skinPalette);
3232
const reducedMotion = useSettingsStore((s) => s.reducedMotion);
3333
const compactWidgets = useSettingsStore((s) => s.compactWidgets);
34+
const animationMode = useSettingsStore((s) => s.animationMode) || (reducedMotion ? "reduced" : "full");
35+
36+
const pageTransitions = useSettingsStore((s) => s.animationConfig?.pageTransitions ?? true);
37+
const cardHover = useSettingsStore((s) => s.animationConfig?.cardHover ?? true);
38+
const modalAnimations = useSettingsStore((s) => s.animationConfig?.modalAnimations ?? true);
39+
const widgetAnimations = useSettingsStore((s) => s.animationConfig?.widgetAnimations ?? true);
40+
const chartAnimations = useSettingsStore((s) => s.animationConfig?.chartAnimations ?? true);
41+
const pulseEffects = useSettingsStore((s) => s.animationConfig?.pulseEffects ?? true);
42+
3443
const [skin, setSkin] = useState({
3544
app: appBackgroundImage,
3645
widget: widgetBackgroundImage,
@@ -56,9 +65,22 @@ export default function App() {
5665

5766
useEffect(() => {
5867
const root = document.documentElement;
59-
root.classList.toggle("reduce-motion", reducedMotion);
68+
const isReduced = animationMode === "reduced" || reducedMotion;
69+
const isDisabled = animationMode === "disabled";
70+
71+
root.classList.toggle("reduce-motion", isReduced || isDisabled);
72+
root.classList.toggle("animation-mode-disabled", isDisabled);
73+
root.classList.toggle("animation-mode-reduced", animationMode === "reduced");
74+
root.classList.toggle("animation-mode-full", animationMode === "full");
6075
root.classList.toggle("compact-widgets", compactWidgets);
61-
}, [compactWidgets, reducedMotion]);
76+
77+
root.classList.toggle("no-page-transitions", isDisabled || !pageTransitions);
78+
root.classList.toggle("no-card-hover", isDisabled || !cardHover);
79+
root.classList.toggle("no-modal-animations", isDisabled || !modalAnimations);
80+
root.classList.toggle("no-widget-animations", isDisabled || !widgetAnimations);
81+
root.classList.toggle("no-chart-animations", isDisabled || !chartAnimations);
82+
root.classList.toggle("no-pulse-effects", isDisabled || !pulseEffects);
83+
}, [animationMode, cardHover, chartAnimations, compactWidgets, modalAnimations, pageTransitions, pulseEffects, reducedMotion, widgetAnimations]);
6284

6385
useEffect(() => {
6486
const root = document.documentElement;

src/components/ExePickerInput.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export default function ExePickerInput({
7878

7979
return (
8080
<div ref={wrapRef} className={clsx("relative z-[100]", className)}>
81-
<div className="flex gap-1">
81+
<div className="flex gap-1.5">
8282
<input
8383
type="text"
8484
className="ui-field flex-1"
@@ -95,34 +95,34 @@ export default function ExePickerInput({
9595
onClick={browseFile}
9696
title={t("browseExecutable")}
9797
aria-label={t("browseExecutable")}
98-
className="px-2.5 rounded-lg border border-surface-border text-text-muted hover:text-text-primary hover:bg-surface-hover transition-colors flex-shrink-0"
98+
className="px-3 rounded-xl border border-surface-border text-text-muted hover:text-text-primary hover:bg-accent-blue/10 hover:border-accent-blue/40 transition-colors flex-shrink-0 flex items-center justify-center"
9999
>
100100
<FolderOpen size={15} />
101101
</button>
102102
</div>
103103

104104
{open && query && filtered.length > 0 && (
105-
<div className="absolute z-[110] left-0 right-0 mt-1 max-h-60 overflow-y-auto rounded-xl border border-surface-border bg-surface-card shadow-xl divide-y divide-surface-border">
105+
<div className="absolute z-[110] left-0 right-0 mt-1.5 max-h-60 overflow-y-auto rounded-xl border border-surface-border/80 bg-surface-card/95 backdrop-blur-md shadow-2xl divide-y divide-surface-border/40">
106106
{filtered.slice(0, 25).map((row) => (
107107
<button
108108
key={row.exe_path}
109109
type="button"
110110
onMouseDown={() => pick(row)}
111111
className={clsx(
112-
"w-full flex flex-col items-start px-3 py-2 text-xs hover:bg-surface-hover transition-colors text-left"
112+
"w-full flex flex-col items-start px-3.5 py-2 text-xs hover:bg-surface-hover/80 active:bg-accent-blue/15 transition-colors text-left"
113113
)}
114114
>
115115
<span className="text-text-primary font-medium">{row.app_name}</span>
116-
<span className="text-text-muted truncate max-w-full" title={row.exe_path}>
116+
<span className="text-text-muted truncate max-w-full text-[11px]" title={row.exe_path}>
117117
{row.exe_path}
118118
</span>
119119
</button>
120120
))}
121121
</div>
122122
)}
123123
{open && query && filtered.length === 0 && (
124-
<div className="absolute z-[110] left-0 right-0 mt-1 rounded-xl border border-surface-border bg-surface-card shadow-xl">
125-
<p className="px-3 py-3 text-xs text-text-muted">{t("noAppsFoundBrowse")}</p>
124+
<div className="absolute z-[110] left-0 right-0 mt-1.5 rounded-xl border border-surface-border/80 bg-surface-card/95 backdrop-blur-md shadow-2xl">
125+
<p className="px-3.5 py-3 text-xs text-text-muted">{t("noAppsFoundBrowse")}</p>
126126
</div>
127127
)}
128128
</div>

src/components/GlobalSearch.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,14 @@ export default function GlobalSearch({ open, onClose }: Props) {
224224
role="dialog"
225225
aria-modal="true"
226226
aria-label={t("common:search")}
227-
className="fixed inset-0 z-50 flex items-start justify-center pt-20 bg-black/50 backdrop-blur-sm"
227+
data-modal-overlay
228+
className="fixed inset-0 z-50 flex items-start justify-center pt-20 bg-black/60 backdrop-blur-md transition-opacity duration-200"
228229
onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}
229230
>
230-
<div className="w-full max-w-xl rounded-2xl bg-surface-light shadow-2xl overflow-hidden border border-surface-border">
231+
<div
232+
data-modal-content
233+
className="w-full max-w-xl rounded-2xl bg-surface-light shadow-2xl overflow-hidden border border-surface-border/80 transition-all duration-200 transform scale-100"
234+
>
231235
{/* Input */}
232236
<div className="flex items-center gap-3 px-4 py-3 border-b border-surface-border bg-surface-light">
233237
<Search size={16} className="text-text-muted flex-shrink-0" />

src/components/layout/Sidebar.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,13 @@ export default function Sidebar({ onOpenSearch }: Props) {
5858
<button
5959
onClick={onOpenSearch}
6060
className="w-full flex items-center gap-2 px-3 py-2 rounded-xl text-xs text-text-muted
61-
border border-surface-border hover:bg-surface-hover hover:text-text-secondary transition-colors"
61+
border border-surface-border hover:bg-accent-blue/10 hover:text-accent-blue transition-colors"
6262
title={t("common:openGlobalSearch")}
6363
aria-label={t("common:openGlobalSearch")}
6464
>
6565
<Search size={13} />
6666
<span className="flex-1 text-left">{t("common:search")}</span>
67-
<kbd className="text-[10px] px-1 py-0.5 rounded bg-surface-hover">Ctrl K</kbd>
67+
<kbd className="text-[10px] px-1.5 py-0.5 rounded bg-surface-card border border-surface-border text-text-secondary">Ctrl K</kbd>
6868
</button>
6969
</div>
7070

@@ -79,7 +79,7 @@ export default function Sidebar({ onOpenSearch }: Props) {
7979
"flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm transition-colors",
8080
isActive
8181
? "bg-accent-blue/20 text-accent-blue font-medium"
82-
: "text-text-secondary hover:text-text-primary hover:bg-surface-hover"
82+
: "text-text-secondary hover:text-accent-blue hover:bg-accent-blue/10"
8383
)
8484
}
8585
>

src/i18n/locales/en/settings.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"searchNoResult": "No matching setting found",
66
"generalDesc": "Language and region preferences",
77
"appearanceDesc": "Theme and visual style",
8+
"animationDesc": "Animation presets and fine-grained motion control",
89
"accessibility": { "reducedMotion": "Reduce motion", "compactWidgets": "Compact widget display" },
910
"trayIconDesc": "Tray or taskbar icon appearance",
1011
"trackingDesc": "Monitoring, sampling, and idle settings",
@@ -379,6 +380,28 @@
379380
"openEncryption": "Open Encryption",
380381
"refresh": "Refresh all panels"
381382
},
383+
"animation": {
384+
"title": "Animation & Motion Controls",
385+
"description": "Configure visual motion, animation presets, and fine-grained interface controls",
386+
"mode": "Motion Preset",
387+
"modeFull": "Full Animations",
388+
"modeReduced": "Reduced Motion",
389+
"modeDisabled": "Disable All Animations",
390+
"optionsTitle": "Animation Controls List",
391+
"optionsDesc": "Selectively enable or disable specific interface animations",
392+
"pageTransitions": "Page & Tab Transitions",
393+
"pageTransitionsHint": "Smooth fading and sliding when switching pages or tabs",
394+
"cardHover": "Card Hover & Micro-interactions",
395+
"cardHoverHint": "Elevation, subtle scaling, and glow effects on cards",
396+
"modalAnimations": "Modal & Dialog Animations",
397+
"modalAnimationsHint": "Scale and backdrop blur transitions for dialog windows",
398+
"widgetAnimations": "Widget Window Motion",
399+
"widgetAnimationsHint": "Fade-in and focus state transitions for desktop widgets",
400+
"chartAnimations": "Chart & Data Animations",
401+
"chartAnimationsHint": "Animated bar and line transitions in analytics views",
402+
"pulseEffects": "Pulse & Status Effects",
403+
"pulseEffectsHint": "Pulsing indicator lights and live activity breathing badges"
404+
},
382405
"about": {
383406
"title": "About",
384407
"version": "Version",

src/i18n/locales/zh-CN/settings.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"searchNoResult": "未找到匹配的设置项",
66
"generalDesc": "语言与区域偏好",
77
"appearanceDesc": "主题与视觉风格",
8+
"animationDesc": "动画预设与界面动效细分控制",
89
"accessibility": { "reducedMotion": "减少动画", "compactWidgets": "紧凑小组件显示" },
910
"trayIconDesc": "托盘或任务栏图标外观",
1011
"trackingDesc": "监控、采样与空闲设置",
@@ -379,6 +380,28 @@
379380
"openProfiles": "打开配置",
380381
"openEncryption": "打开加密"
381382
},
383+
"animation": {
384+
"title": "动画与动效控制",
385+
"description": "配置或关闭界面视觉动画与过渡效果",
386+
"mode": "预设动效模式",
387+
"modeFull": "完整动效",
388+
"modeReduced": "减弱动效",
389+
"modeDisabled": "关闭所有动画",
390+
"optionsTitle": "动画细分关闭列表",
391+
"optionsDesc": "允许自由选择并开启/关闭指定的界面动画效果",
392+
"pageTransitions": "页面与标签过渡",
393+
"pageTransitionsHint": "切换页面或标签卡时的平滑淡入与平移动画",
394+
"cardHover": "卡片悬浮与微交互",
395+
"cardHoverHint": "卡片 Hover 浮起、微缩放与边框光影反馈",
396+
"modalAnimations": "对话框与弹窗动画",
397+
"modalAnimationsHint": "弹窗打开与关闭时的绽放及背景模糊过渡",
398+
"widgetAnimations": "桌面小组件过渡",
399+
"widgetAnimationsHint": "小组件窗口淡入淡出及失焦透明度过渡",
400+
"chartAnimations": "图表与数据渲染动画",
401+
"chartAnimationsHint": "分析图表柱状与折线渲染时的动态过渡",
402+
"pulseEffects": "呼吸灯与状态脉冲",
403+
"pulseEffectsHint": "监控状态指示灯的呼吸脉冲与实时提示动效"
404+
},
382405
"about": {
383406
"title": "关于",
384407
"version": "版本",

src/i18n/locales/zh-TW/settings.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"searchNoResult": "未找到匹配的設定項",
66
"generalDesc": "語言與區域偏好",
77
"appearanceDesc": "主題與視覺風格",
8+
"animationDesc": "動畫預設與介面動效細分控制",
89
"accessibility": { "reducedMotion": "減少動畫", "compactWidgets": "精簡小元件顯示" },
910
"trayIconDesc": "托盤或工作列圖示外觀",
1011
"trackingDesc": "監控、取樣與閒置設定",
@@ -379,6 +380,28 @@
379380
"openProfiles": "開啟設定檔",
380381
"openEncryption": "開啟加密"
381382
},
383+
"animation": {
384+
"title": "動畫與動效控制",
385+
"description": "設定或關閉介面視覺動畫與過渡效果",
386+
"mode": "預設動效模式",
387+
"modeFull": "完整動效",
388+
"modeReduced": "減弱動效",
389+
"modeDisabled": "關閉所有動畫",
390+
"optionsTitle": "動畫細分關閉列表",
391+
"optionsDesc": "允許自由選擇並開啟/關閉指定的介面動畫效果",
392+
"pageTransitions": "頁面與標籤過渡",
393+
"pageTransitionsHint": "切換頁面或標籤卡時的平滑淡入與平移動畫",
394+
"cardHover": "卡片懸浮與微互動",
395+
"cardHoverHint": "卡片 Hover 浮起、微縮放與邊框光影回饋",
396+
"modalAnimations": "對話框與彈窗動畫",
397+
"modalAnimationsHint": "彈窗開啟與關閉時的縮放及背景模糊過渡",
398+
"widgetAnimations": "桌面小元件過渡",
399+
"widgetAnimationsHint": "小元件視窗淡入淡出及失焦透明度過渡",
400+
"chartAnimations": "圖表與資料呈現動畫",
401+
"chartAnimationsHint": "分析圖表柱狀與折線呈現時的動態過渡",
402+
"pulseEffects": "呼吸燈與狀態脈衝",
403+
"pulseEffectsHint": "監控狀態指示燈的呼吸脈衝與實時提示動效"
404+
},
382405
"about": {
383406
"title": "關於",
384407
"version": "版本",

src/pages/BrowserUsage/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -503,16 +503,16 @@ export default function BrowserUsage() {
503503
</a>
504504

505505
{/* Date preset selector */}
506-
<div className="flex gap-1.5 bg-surface-hover rounded-xl p-1">
506+
<div className="flex gap-1.5 bg-surface-card border border-surface-border rounded-xl p-1 shadow-xs">
507507
{PRESETS.map((p) => (
508508
<button
509509
key={p}
510510
onClick={() => handlePresetChange(p)}
511511
className={clsx(
512-
"px-3 py-1.5 rounded-lg text-xs font-medium transition-colors",
512+
"px-3 py-1.5 rounded-lg text-xs font-semibold transition-all duration-150",
513513
preset === p
514-
? "bg-accent-blue text-white shadow"
515-
: "text-text-secondary hover:text-text-primary"
514+
? "bg-accent-blue/15 text-accent-blue border border-accent-blue/30 shadow-xs"
515+
: "text-text-secondary border border-transparent hover:text-accent-blue hover:bg-accent-blue/10"
516516
)}
517517
>
518518
{t(`browserUsage:${p}`)}

0 commit comments

Comments
 (0)