From 520cfd96c79d464393d428bcc173ac489ffd24db Mon Sep 17 00:00:00 2001 From: feie9454 Date: Sat, 25 Apr 2026 18:17:58 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=AB=E5=90=8D/=E5=B1=8F=E8=94=BD?= =?UTF-8?q?=E7=A7=BB=E5=88=B0=20.env?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot --- .env.example | 10 +- .../components/ScreenTimeOverviewChart.tsx | 391 +++++++++--------- .../[hostname]/components/ScreenTimeTab.tsx | 91 ++-- .../[hostname]/components/ScreenshotsTab.tsx | 2 +- app/hosts/[hostname]/page.tsx | 2 +- app/hosts/[hostname]/screen-time/route.ts | 24 +- lib/config.ts | 93 +++++ lib/windowFilters.ts | 7 +- 8 files changed, 347 insertions(+), 273 deletions(-) diff --git a/.env.example b/.env.example index eb2fd1f..f5d3ad5 100644 --- a/.env.example +++ b/.env.example @@ -25,4 +25,12 @@ QQ_BOT_TARGET_ID=1234567890 # Generate with: bunx web-push generate-vapid-keys NEXT_PUBLIC_VAPID_PUBLIC_KEY= VAPID_PRIVATE_KEY= -VAPID_SUBJECT=mailto:admin@example.com \ No newline at end of file +VAPID_SUBJECT=mailto:admin@example.com + +# Window filters +# Format: keyword1,keyword2 +IGNORED_WINDOW_KEYWORDS=NVIDIA Overlay,Twinkle Tray Panel + +# Screen Time display name presets +# Format: match=displayName;match=displayName +PROCESS_DISPLAY_NAME_PRESETS=microsoft vs code\code.exe=Visual Studio Code;msedge.exe=Microsoft Edge;windowsTerminal.exe=终端;windowsterminal.exe=终端;blender.exe=Blender;chrome.exe=Google Chrome;steamwebhelper.exe=Steam;steam.exe=Steam;bandizip.exe=Bandizip;explorer.exe=文件资源管理器;devenv.exe=Visual Studio;idea64.exe=IntelliJ IDEA;pycharm64.exe=PyCharm;cursor.exe=Cursor;obsidian.exe=Obsidian;notion.exe=Notion;wechat.exe=微信;qq.exe=QQ;teams.exe=Microsoft Teams;telegram.exe=Telegram \ No newline at end of file diff --git a/app/hosts/[hostname]/components/ScreenTimeOverviewChart.tsx b/app/hosts/[hostname]/components/ScreenTimeOverviewChart.tsx index 54bd092..e1ee288 100644 --- a/app/hosts/[hostname]/components/ScreenTimeOverviewChart.tsx +++ b/app/hosts/[hostname]/components/ScreenTimeOverviewChart.tsx @@ -13,11 +13,7 @@ import { YAxis } from 'recharts'; import { - CalendarDays, - Clock3, Loader2, - Sparkles, - WandSparkles } from 'lucide-react'; import { ScreenTimeResponse } from '../types'; import { formatDuration, getLocalDateInputValue } from '../utils'; @@ -157,19 +153,25 @@ const formatUpdateTime = (value: string | null) => { const OverviewSkeleton = () => { return ( -
-
-
-
-
- {[56, 88, 62, 76, 50, 68, 18].map((height, index) => ( -
-
-
-
+
+
+
+
+
+
+
+ {[0, 1, 2].map((index) => ( +
))}
-
+
+
+
+ {[56, 88, 62, 76, 50, 68, 18].map((height, index) => ( +
+ ))} +
+
{Array.from({ length: 18 }).map((_, index) => (
{formatDuration(datum.totalDurationSeconds)}
-
- {datum.isSelected ? '当前选中日,点击其他日期可跳转' : '点击查看这一天'} -
); } @@ -303,6 +302,8 @@ export default function ScreenTimeOverviewChart({ if (!data) return 0; return Math.max(...data.overview.week.days.map((day) => day.totalDurationSeconds), 0); }, [data]); + const primaryLabel = data ? (unit === 'week' ? '本周' : formatSelectedDateHeadline(data.overview.selectedDay.date)) : ''; + const selectedDayDuration = data?.overview.selectedDay.totalDurationSeconds ?? 0; if (loading && !data) { return ; @@ -311,225 +312,219 @@ export default function ScreenTimeOverviewChart({ if (!data) return null; return ( -
+
{refreshing && ( -
-
+
+
- 正在刷新统计 + 更新中
)} -
-
-
- - {formatSelectedDateHeadline(data.overview.selectedDay.date)} +
+
+
+ {primaryLabel}
-
+
{formatDuration(data.totalDurationSeconds)}
-
- {unit === 'week' ? '本周总屏幕时间' : '当天总屏幕时间'} - - 选中日 {formatDuration(data.overview.selectedDay.totalDurationSeconds)} +
+ {formatUpdateTime(data.lastRecordedAt)}
-
-
-
周平均
-
+
+
+
周平均
+
{formatDuration(data.overview.week.averageDurationSeconds)}
-
-
活跃应用
-
+
+
所选日
+
+ {formatDuration(selectedDayDuration)} +
+
+
+
应用
+
{data.apps.length}
-
-
采样节奏
-
- {formatDuration(data.estimatedSampleSeconds)} -
-
-
-
-
-
周概览
-
- 点击任意一天可以直接跳到该日期 +
+
+
+

周概览

+
+ 平均 {formatDuration(data.overview.week.averageDurationSeconds)}
-
- - 选中日已高亮 -
-
-
- - - - - - - } cursor={{ fill: 'rgba(148, 163, 184, 0.08)' }} /> +
+ + + + + + + } cursor={{ fill: 'rgba(148, 163, 184, 0.08)' }} /> - { - const day = data.overview.week.days[index]; - if (day) { - onSelectDate(day.date, unit === 'week' ? 'day' : undefined); - } - }}> - {weekChartData.map((entry) => ( - - ))} - - {[0, 1, 2].map((index) => ( - { - const day = data.overview.week.days[clickedIndex]; - if (day) { - onSelectDate(day.date, unit === 'week' ? 'day' : undefined); - } - }} - > + { + const day = data.overview.week.days[index]; + if (day) { + onSelectDate(day.date, unit === 'week' ? 'day' : undefined); + } + }}> {weekChartData.map((entry) => ( ))} - ))} - { - const day = data.overview.week.days[index]; - if (day) { - onSelectDate(day.date, unit === 'week' ? 'day' : undefined); - } - }}> - {weekChartData.map((entry) => ( - ( + { + const day = data.overview.week.days[clickedIndex]; + if (day) { + onSelectDate(day.date, unit === 'week' ? 'day' : undefined); + } + }} + > + {weekChartData.map((entry) => ( + + ))} + + ))} + { + const day = data.overview.week.days[index]; + if (day) { + onSelectDate(day.date, unit === 'week' ? 'day' : undefined); + } + }}> + {weekChartData.map((entry) => ( + + ))} + + + +
+ +
+ 总计 {formatDuration(data.overview.week.totalDurationSeconds)} + 最高 {formatDuration(maxWeekDuration)} +
+ + +
+
+

当日分布

+
+ {formatDuration(selectedDayDuration)} +
+
+ +
+ + + + + + } cursor={{ fill: 'rgba(148, 163, 184, 0.08)' }} /> + {[0, 1, 2].map((index) => ( + ))} - - - -
- -
- 周总计 {formatDuration(data.overview.week.totalDurationSeconds)} - 最高 {formatDuration(maxWeekDuration)} -
- -
-
-
当日分布
-
- 24 小时内按主应用堆叠,帮你快速定位高强度时段 -
-
-
- - {formatDuration(data.overview.selectedDay.totalDurationSeconds)} -
-
- -
- - - - - - } cursor={{ fill: 'rgba(148, 163, 184, 0.08)' }} /> - {[0, 1, 2].map((index) => ( - ))} - - - -
+ + +
-
- {data.overview.selectedDay.spotlightApps.length > 0 ? data.overview.selectedDay.spotlightApps.map((app, index) => ( - - )) : ( -
- 当前日期没有足够的活跃应用数据来展示聚焦图例。 -
- )} -
-
- -
- {formatUpdateTime(data.lastRecordedAt)} - 周柱状图可点击跳转,Focus 卡片可直接展开对应应用 + + )) : ( +
+ 暂无应用数据 +
+ )} +
+
); diff --git a/app/hosts/[hostname]/components/ScreenTimeTab.tsx b/app/hosts/[hostname]/components/ScreenTimeTab.tsx index 317aaf8..ca15164 100644 --- a/app/hosts/[hostname]/components/ScreenTimeTab.tsx +++ b/app/hosts/[hostname]/components/ScreenTimeTab.tsx @@ -131,23 +131,23 @@ export default function ScreenTimeTab({ hostname }: ScreenTimeTabProps) { }; return ( -
-
-
-
-
- Screen Time +
+
+
+
+
+ 屏幕使用时间
-

+

{periodLabel}

-
-
+
+
-
)} -
-
+
+
-

应用使用排行

-

- 进程默认折叠,展开后可查看时长最长的前 5 个窗口标题。 -

+

最常使用

+ {screenTime && ( +
+ {screenTime.apps.length} 个应用 · {formatDuration(screenTime.totalDurationSeconds)} +
+ )}
{isRefreshing && ( @@ -231,14 +233,14 @@ export default function ScreenTimeTab({ hostname }: ScreenTimeTabProps) {
{loading && !screenTime && ( -
+
正在加载屏幕使用时间数据...
)} {!loading && screenTime && screenTime.apps.length === 0 && !error && ( -
+
当前时间段内没有可统计的活跃窗口记录。
)} @@ -254,7 +256,7 @@ export default function ScreenTimeTab({ hostname }: ScreenTimeTabProps) { return (
toggleExpanded(app.processId)} className="w-full text-left" > -
-
+
+
{String(index + 1).padStart(2, '0')}
{isExpanded ? ( - + ) : ( - + )}
{app.processName}
{highlightedProcessId === app.processId && ( - - Focus + + 聚焦 )} - + {app.titleCount} 个标题
-
- {formatDuration(app.durationSeconds)} +
{app.percentage}% + + + {app.processPath || '路径未知'} +
-
- 路径: {app.processPath || '路径未知'} -
- -
+
{isExpanded && ( -
-
+
+
{app.titles.map((title) => (
-
+
{title.title}
-
- 占该进程 {title.percentage}% +
+ {title.percentage}%
@@ -331,7 +332,7 @@ export default function ScreenTimeTab({ hostname }: ScreenTimeTabProps) {
-
+
{app.titleCount > app.titles.length && ( -
- 其余 {app.titleCount - app.titles.length} 个标题未展示。 +
+ 还有 {app.titleCount - app.titles.length} 个标题
)}
diff --git a/app/hosts/[hostname]/components/ScreenshotsTab.tsx b/app/hosts/[hostname]/components/ScreenshotsTab.tsx index 77687a3..14c9cce 100644 --- a/app/hosts/[hostname]/components/ScreenshotsTab.tsx +++ b/app/hosts/[hostname]/components/ScreenshotsTab.tsx @@ -633,7 +633,7 @@ export default function ScreenshotsTab({ { !(navigator as any).connection?.saveData && records.map(record => record.screenshots).flat() - .filter((_, index) => Math.abs(index - currentFrameIndex) <= 20) + .filter((_, index) => Math.abs(index - currentFrameIndex) <= 5) .map(screenshot => ) } {/* 图片预览区域 */} diff --git a/app/hosts/[hostname]/page.tsx b/app/hosts/[hostname]/page.tsx index e76e950..80216f7 100644 --- a/app/hosts/[hostname]/page.tsx +++ b/app/hosts/[hostname]/page.tsx @@ -77,7 +77,7 @@ export default function HostDetail() { }`} > - 屏幕使用时间 + 屏幕使用