translime-plugin-hdr-capture 1.0.1 → 1.0.4
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/dist/bin/index.d.ts +28 -54
- package/dist/bin/index.win32-x64-msvc.node +0 -0
- package/dist/main.cjs.js +126 -56
- package/dist/overlay-preload.cjs.js +9 -0
- package/dist/overlay.css +1 -1
- package/dist/overlay.js +2 -2
- package/dist/ui.esm.js +70 -40
- package/package.json +1 -1
package/dist/bin/index.d.ts
CHANGED
|
@@ -3,24 +3,17 @@
|
|
|
3
3
|
|
|
4
4
|
/* auto-generated by NAPI-RS */
|
|
5
5
|
|
|
6
|
-
/**
|
|
6
|
+
/** 显示器描述信息 */
|
|
7
7
|
export interface DisplayInfo {
|
|
8
|
-
/** 显示器索引 */
|
|
9
8
|
id: number
|
|
10
|
-
/** 显示器名称 */
|
|
11
9
|
name: string
|
|
12
|
-
/** 左边界 */
|
|
13
10
|
x: number
|
|
14
|
-
/** 上边界 */
|
|
15
11
|
y: number
|
|
16
|
-
/** 宽度 */
|
|
17
12
|
width: number
|
|
18
|
-
/** 高度 */
|
|
19
13
|
height: number
|
|
20
|
-
/** 是否主显示器 */
|
|
21
14
|
isPrimary: boolean
|
|
22
15
|
}
|
|
23
|
-
/**
|
|
16
|
+
/** 暴露给 JavaScript 的窗口信息结构 */
|
|
24
17
|
export interface WindowInfo {
|
|
25
18
|
/** 窗口句柄 (HWND) */
|
|
26
19
|
handle: number
|
|
@@ -28,17 +21,11 @@ export interface WindowInfo {
|
|
|
28
21
|
title: string
|
|
29
22
|
/** 窗口类名 */
|
|
30
23
|
className: string
|
|
31
|
-
/** 左边界 */
|
|
32
24
|
left: number
|
|
33
|
-
/** 上边界 */
|
|
34
25
|
top: number
|
|
35
|
-
/** 右边界 */
|
|
36
26
|
right: number
|
|
37
|
-
/** 下边界 */
|
|
38
27
|
bottom: number
|
|
39
|
-
/** 窗口宽度 */
|
|
40
28
|
width: number
|
|
41
|
-
/** 窗口高度 */
|
|
42
29
|
height: number
|
|
43
30
|
}
|
|
44
31
|
/** 矩形区域 */
|
|
@@ -48,71 +35,58 @@ export interface Rect {
|
|
|
48
35
|
width: number
|
|
49
36
|
height: number
|
|
50
37
|
}
|
|
51
|
-
/** Tone Mapping 选项 */
|
|
38
|
+
/** 色调映射 (Tone Mapping) 选项 */
|
|
52
39
|
export interface ToneMappingOptions {
|
|
53
|
-
/**
|
|
40
|
+
/** 曝光度调整值 */
|
|
54
41
|
exposure?: number
|
|
55
|
-
/** 是否保留 HDR
|
|
42
|
+
/** 是否保留 HDR 元数据(某些格式支持) */
|
|
56
43
|
preserveHdrMetadata?: boolean
|
|
57
44
|
}
|
|
58
|
-
/** HDR
|
|
45
|
+
/** HDR 映射专用配置 */
|
|
59
46
|
export interface HdrMappingOptions {
|
|
60
|
-
/**
|
|
47
|
+
/** 是否开启色彩映射 */
|
|
61
48
|
enabled?: boolean
|
|
62
|
-
/** SDR
|
|
49
|
+
/** SDR 白点参考亮度 (nits),默认为 203 */
|
|
63
50
|
sdrWhiteNits?: number
|
|
64
|
-
/** HDR 峰值亮度 (nits)
|
|
51
|
+
/** HDR 峰值亮度 (nits),默认为 1000 */
|
|
65
52
|
hdrMaxNits?: number
|
|
66
|
-
/**
|
|
53
|
+
/** 是否导出原始 HDR 字节流(供后续保存为 EXR 等格式) */
|
|
67
54
|
preserveRaw?: boolean
|
|
68
55
|
}
|
|
69
|
-
/**
|
|
56
|
+
/** 屏幕捕获返回的综合结果 */
|
|
70
57
|
export interface CaptureResult {
|
|
71
|
-
/**
|
|
58
|
+
/** 处理后的图像数据 (RGBA8 缓冲区) */
|
|
72
59
|
buffer: Buffer
|
|
73
|
-
/**
|
|
60
|
+
/** 图像物理宽度 (像素) */
|
|
74
61
|
width: number
|
|
75
|
-
/**
|
|
62
|
+
/** 图像物理高度 (像素) */
|
|
76
63
|
height: number
|
|
77
|
-
/**
|
|
64
|
+
/** 是否来源于 HDR 屏幕 */
|
|
78
65
|
isHdr: boolean
|
|
79
|
-
/**
|
|
80
|
-
* 原始 HDR 数据 (可选,仅当 preserve_raw 为 true 且为 HDR 屏幕时存在)
|
|
81
|
-
* 保存为 RGBA Float16 或 10bit 原始格式的字节流
|
|
82
|
-
*/
|
|
66
|
+
/** 原始 HDR 数据 (F16 格式,仅在 preserve_raw 为开启时存在) */
|
|
83
67
|
rawHdrBuffer?: Buffer
|
|
84
68
|
}
|
|
85
|
-
/**
|
|
69
|
+
/** 获取所有的顶层可见窗口 */
|
|
86
70
|
export declare function getTopLevelWindows(): Array<WindowInfo>
|
|
87
|
-
/**
|
|
71
|
+
/** 获取指定屏幕坐标下的最前端窗口 */
|
|
88
72
|
export declare function getWindowAtPoint(x: number, y: number, ignoreHandle?: number | undefined | null): WindowInfo | null
|
|
89
73
|
/**
|
|
90
|
-
*
|
|
74
|
+
* 捕获指定显示器的当前画面
|
|
91
75
|
*
|
|
92
|
-
*
|
|
76
|
+
* 返回包含图像数据及元数据的结果对象。
|
|
93
77
|
*/
|
|
94
|
-
export declare function captureDisplay(displayId: number, hdrOptions?: HdrMappingOptions | undefined | null): Promise<CaptureResult>
|
|
95
|
-
/**
|
|
78
|
+
export declare function captureDisplay(displayId: number, hdrOptions?: HdrMappingOptions | undefined | null, captureCursor?: boolean | undefined | null): Promise<CaptureResult>
|
|
79
|
+
/** 获取当前连接的所有显示器列表 */
|
|
96
80
|
export declare function getDisplays(): Array<DisplayInfo>
|
|
97
|
-
/**
|
|
81
|
+
/** 根据给定矩形裁剪图像缓冲区 */
|
|
98
82
|
export declare function cropImage(buffer: Buffer, width: number, height: number, rect: Rect): Promise<Buffer>
|
|
99
|
-
/** HDR
|
|
83
|
+
/** 对捕获的 HDR 数据执行后处理色调映射 */
|
|
100
84
|
export declare function toneMap(hdrBuffer: Buffer, width: number, height: number, options?: ToneMappingOptions | undefined | null): Promise<Buffer>
|
|
101
|
-
/**
|
|
85
|
+
/** 将像素数据编码为特定格式(png, jpg, webp) */
|
|
102
86
|
export declare function encodeImage(buffer: Buffer, width: number, height: number, format: string): Promise<Buffer>
|
|
103
|
-
/**
|
|
87
|
+
/** 调整图像大小到指定的分辨率 */
|
|
104
88
|
export declare function resizeImage(buffer: Buffer, width: number, height: number, newWidth: number, newHeight: number): Promise<Buffer>
|
|
105
|
-
/**
|
|
106
|
-
* 将原始 HDR F16 数据编码为 EXR 格式
|
|
107
|
-
*
|
|
108
|
-
* 输入: RGBA F16 格式的原始 HDR 数据 (每像素 8 字节)
|
|
109
|
-
* 输出: OpenEXR 文件字节流
|
|
110
|
-
*/
|
|
89
|
+
/** 将 F16 格式的原始 HDR 数据编码为高性能的 OpenEXR 文件 */
|
|
111
90
|
export declare function encodeHdrToExr(rawBuffer: Buffer, width: number, height: number): Promise<Buffer>
|
|
112
|
-
/**
|
|
113
|
-
* 裁剪 HDR F16 格式的原始数据
|
|
114
|
-
*
|
|
115
|
-
* 输入: RGBA F16 格式的原始 HDR 数据 (每像素 8 字节)
|
|
116
|
-
* 输出: 裁剪后的 RGBA F16 数据
|
|
117
|
-
*/
|
|
91
|
+
/** 在 F16 原始数据层级执行裁剪 */
|
|
118
92
|
export declare function cropHdrF16(rawBuffer: Buffer, width: number, height: number, rect: Rect): Promise<Buffer>
|
|
Binary file
|
package/dist/main.cjs.js
CHANGED
|
@@ -654,7 +654,7 @@ try {
|
|
|
654
654
|
const getTopLevelWindows = () => nativeAddon.getTopLevelWindows();
|
|
655
655
|
const getWindowAtPoint = (x, y, ignoreHandle = null) => nativeAddon.getWindowAtPoint(x, y, ignoreHandle);
|
|
656
656
|
const getDisplays = () => nativeAddon.getDisplays();
|
|
657
|
-
const captureDisplay = async (displayId = 0, hdrOptions = null) => {
|
|
657
|
+
const captureDisplay = async (displayId = 0, hdrOptions = null, captureCursor = false) => {
|
|
658
658
|
try {
|
|
659
659
|
const nativeHdrOptions = hdrOptions ? {
|
|
660
660
|
enabled: hdrOptions.enabled,
|
|
@@ -662,12 +662,13 @@ const captureDisplay = async (displayId = 0, hdrOptions = null) => {
|
|
|
662
662
|
hdrMaxNits: hdrOptions.hdrMaxNits,
|
|
663
663
|
preserveRaw: hdrOptions.preserveRaw
|
|
664
664
|
} : null;
|
|
665
|
-
return await nativeAddon.captureDisplay(displayId, nativeHdrOptions);
|
|
665
|
+
return await nativeAddon.captureDisplay(displayId, nativeHdrOptions, captureCursor);
|
|
666
666
|
} catch (e) {
|
|
667
667
|
logger$1.error(`captureDisplay 失败 (ID=${displayId}):`, e);
|
|
668
668
|
throw e;
|
|
669
669
|
}
|
|
670
670
|
};
|
|
671
|
+
const toneMap = async (hdrBuffer, width, height, options = {}) => nativeAddon.toneMap(hdrBuffer, width, height, options);
|
|
671
672
|
const encodeImage = async (buffer, width, height, format = "png") => nativeAddon.encodeImage(buffer, width, height, format);
|
|
672
673
|
const encodeHdrToExr = async (rawBuffer, width, height) => {
|
|
673
674
|
try {
|
|
@@ -876,6 +877,11 @@ const cropAndSaveScaledFromBuffer = async (sessionData, rect, options = {}) => {
|
|
|
876
877
|
if (savePath) {
|
|
877
878
|
const fs = await import("node:fs/promises");
|
|
878
879
|
const path2 = await import("node:path");
|
|
880
|
+
try {
|
|
881
|
+
await fs.mkdir(savePath, { recursive: true });
|
|
882
|
+
} catch (err) {
|
|
883
|
+
logger$1.error(`无法创建保存目录: ${savePath}`, err);
|
|
884
|
+
}
|
|
879
885
|
const timestamp = dayjs().format("HDR_Capture_YYYY-MM-DD_HH-mm-ss");
|
|
880
886
|
let fileName;
|
|
881
887
|
if (saveFilenameTemplate) {
|
|
@@ -972,6 +978,7 @@ const getPreserveHdr = () => pluginConfig.get("preserveHdr", false);
|
|
|
972
978
|
const getEnableHdrMapping = () => pluginConfig.get("enableHdrMapping", true);
|
|
973
979
|
const getSdrWhiteNits = () => pluginConfig.get("sdrWhiteNits", 203);
|
|
974
980
|
const getHdrMaxNits = () => pluginConfig.get("hdrMaxNits", 1e3);
|
|
981
|
+
const getCaptureCursor = () => pluginConfig.get("captureCursor", false);
|
|
975
982
|
const getAllDisplaysBounds = () => {
|
|
976
983
|
const displays = electron.screen.getAllDisplays();
|
|
977
984
|
let minX = Infinity;
|
|
@@ -994,6 +1001,8 @@ const getAllDisplaysBounds = () => {
|
|
|
994
1001
|
displays,
|
|
995
1002
|
minX,
|
|
996
1003
|
minY,
|
|
1004
|
+
maxX,
|
|
1005
|
+
maxY,
|
|
997
1006
|
width: maxX - minX,
|
|
998
1007
|
height: maxY - minY
|
|
999
1008
|
};
|
|
@@ -1005,16 +1014,28 @@ const updateOverlayBounds = () => {
|
|
|
1005
1014
|
const {
|
|
1006
1015
|
minX,
|
|
1007
1016
|
minY,
|
|
1017
|
+
maxY,
|
|
1008
1018
|
width,
|
|
1009
1019
|
height
|
|
1010
1020
|
} = getAllDisplaysBounds();
|
|
1011
|
-
overlayWindow.
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1021
|
+
const { y } = overlayWindow.getBounds();
|
|
1022
|
+
if (y >= maxY) {
|
|
1023
|
+
overlayWindow.setBounds({
|
|
1024
|
+
width,
|
|
1025
|
+
height,
|
|
1026
|
+
x: 0,
|
|
1027
|
+
y: maxY + 100
|
|
1028
|
+
});
|
|
1029
|
+
logger.info(`屏幕变动,更新离屏位置: (0, ${maxY + 100})`);
|
|
1030
|
+
} else {
|
|
1031
|
+
overlayWindow.setBounds({
|
|
1032
|
+
x: minX,
|
|
1033
|
+
y: minY,
|
|
1034
|
+
width,
|
|
1035
|
+
height
|
|
1036
|
+
});
|
|
1037
|
+
logger.info(`屏幕变动,更新捕获边界: ${width}x${height} at (${minX}, ${minY})`);
|
|
1038
|
+
}
|
|
1018
1039
|
};
|
|
1019
1040
|
const unregisterShortcut = () => {
|
|
1020
1041
|
if (registeredShortcut) {
|
|
@@ -1042,7 +1063,7 @@ const createOverlayWindow = (isDebug = false) => {
|
|
|
1042
1063
|
skipTaskbar: !isDebug,
|
|
1043
1064
|
resizable: isDebug,
|
|
1044
1065
|
movable: isDebug,
|
|
1045
|
-
maximizable:
|
|
1066
|
+
maximizable: false,
|
|
1046
1067
|
fullscreen: false,
|
|
1047
1068
|
thickFrame: false,
|
|
1048
1069
|
hasShadow: false,
|
|
@@ -1073,49 +1094,64 @@ const createOverlayWindow = (isDebug = false) => {
|
|
|
1073
1094
|
});
|
|
1074
1095
|
return overlayWindow;
|
|
1075
1096
|
};
|
|
1076
|
-
const preCaptureAllScreens = async () => {
|
|
1097
|
+
const preCaptureAllScreens = async (startTime = Date.now(), isDebug = false) => {
|
|
1077
1098
|
const electronDisplays = electron.screen.getAllDisplays();
|
|
1078
1099
|
const nativeDisplays = getDisplays();
|
|
1079
|
-
logger.info(
|
|
1100
|
+
logger.info(`[Perf] 开始预捕获 (T+${Date.now() - startTime}ms). 检测到原生显示器数量:`, nativeDisplays.length);
|
|
1080
1101
|
const enableHdrMapping = getEnableHdrMapping();
|
|
1081
1102
|
const sdrWhiteNits = getSdrWhiteNits();
|
|
1082
1103
|
const hdrMaxNits = getHdrMaxNits();
|
|
1083
1104
|
const preserveHdr = getPreserveHdr();
|
|
1105
|
+
const captureCursor = getCaptureCursor();
|
|
1084
1106
|
const hdrOptions = enableHdrMapping ? {
|
|
1085
1107
|
enabled: true,
|
|
1086
1108
|
sdrWhiteNits,
|
|
1087
1109
|
hdrMaxNits,
|
|
1088
|
-
|
|
1110
|
+
preserveHdr,
|
|
1111
|
+
// 注意:传递给 native 的参数名和 config 可能略有不同,这里复用 preserveHdr
|
|
1089
1112
|
preserveRaw: preserveHdr
|
|
1090
1113
|
} : null;
|
|
1091
1114
|
logger.info("HDR 映射配置:", {
|
|
1092
1115
|
enableHdrMapping,
|
|
1093
1116
|
sdrWhiteNits,
|
|
1094
1117
|
hdrMaxNits,
|
|
1095
|
-
preserveHdr
|
|
1118
|
+
preserveHdr,
|
|
1119
|
+
captureCursor
|
|
1096
1120
|
});
|
|
1097
1121
|
const capturePromises = nativeDisplays.map(async (nd) => {
|
|
1098
1122
|
try {
|
|
1099
|
-
|
|
1123
|
+
const t0 = Date.now();
|
|
1124
|
+
logger.info(`[Perf] 正在捕获显示器 ID=${nd.id} (预期 ${nd.width}x${nd.height}) ...`);
|
|
1100
1125
|
const {
|
|
1101
1126
|
buffer,
|
|
1102
1127
|
width,
|
|
1103
1128
|
height,
|
|
1104
1129
|
isHdr,
|
|
1105
1130
|
rawHdrBuffer
|
|
1106
|
-
} = await captureDisplay(nd.id, hdrOptions);
|
|
1131
|
+
} = await captureDisplay(nd.id, hdrOptions, captureCursor);
|
|
1132
|
+
const t1 = Date.now();
|
|
1133
|
+
logger.info(`[Perf] 显示器 ID=${nd.id} 捕获完成, 耗时: ${t1 - t0}ms(T+${t1 - startTime}ms). 实际尺寸 ${width}x${height}, Buffer: ${buffer ? buffer.length : 0}, IS_HDR: ${isHdr}`);
|
|
1134
|
+
if (isDebug && !isHdr && buffer && buffer.length > 0) {
|
|
1135
|
+
try {
|
|
1136
|
+
logger.info("[Perf] (Debug模式) 强制对 SDR 数据执行 Tone Mapping 测试...");
|
|
1137
|
+
const tMap0 = Date.now();
|
|
1138
|
+
await toneMap(buffer, width, height, { exposure: 1 });
|
|
1139
|
+
const tMap1 = Date.now();
|
|
1140
|
+
logger.info(`[Perf] (Debug模式) 强制 Tone Mapping 耗时: ${tMap1 - tMap0}ms(T+${tMap1 - startTime}ms)`);
|
|
1141
|
+
} catch (tmErr) {
|
|
1142
|
+
logger.error("强制 Tone Mapping 测试失败:", tmErr);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1107
1145
|
if (!buffer || buffer.length === 0) {
|
|
1108
1146
|
logger.warn(`显示器 ID=${nd.id} 返回的 Buffer 为空`);
|
|
1109
1147
|
return null;
|
|
1110
1148
|
}
|
|
1111
|
-
logger.info(`显示器 ID=${nd.id} 捕获成功: 实际尺寸 ${width}x${height}, Buffer 长度 ${buffer.length}, IS_HDR: ${isHdr}, 原始 HDR 数据: ${rawHdrBuffer ? rawHdrBuffer.length : "N/A"}`);
|
|
1112
1149
|
const centerX = nd.x + nd.width / 2;
|
|
1113
1150
|
const centerY = nd.y + nd.height / 2;
|
|
1114
1151
|
const ed = electronDisplays.find((d) => {
|
|
1115
1152
|
const b = d.bounds;
|
|
1116
1153
|
return centerX >= b.x && centerX <= b.x + b.width && centerY >= b.y && centerY <= b.y + b.height;
|
|
1117
1154
|
}) || electronDisplays[0];
|
|
1118
|
-
logger.info(`显示器 ID=${nd.id} 匹配到 Electron 显示器: scale=${ed.scaleFactor}`);
|
|
1119
1155
|
return {
|
|
1120
1156
|
displayId: nd.id,
|
|
1121
1157
|
buffer,
|
|
@@ -1134,37 +1170,63 @@ const preCaptureAllScreens = async () => {
|
|
|
1134
1170
|
});
|
|
1135
1171
|
const results = await Promise.allSettled(capturePromises);
|
|
1136
1172
|
const finalResults = results.filter((result) => result.status === "fulfilled" && result.value !== null).map((result) => result.value);
|
|
1137
|
-
logger.info(
|
|
1173
|
+
logger.info(`[Perf] 预捕获全部完成 (T+${Date.now() - startTime}ms), 成功获取到 ${finalResults.length} 张屏幕画面`);
|
|
1138
1174
|
return finalResults;
|
|
1139
1175
|
};
|
|
1140
|
-
const startCapture = async (isDebug = false) => {
|
|
1176
|
+
const startCapture = async (isDebug = false, startTime = Date.now()) => {
|
|
1177
|
+
logger.info(`[Perf] startCapture 开始 (T+${Date.now() - startTime}ms)`);
|
|
1178
|
+
const {
|
|
1179
|
+
displays: allDisplays,
|
|
1180
|
+
minX,
|
|
1181
|
+
minY,
|
|
1182
|
+
maxY,
|
|
1183
|
+
width: totalWidth,
|
|
1184
|
+
height: totalHeight
|
|
1185
|
+
} = getAllDisplaysBounds();
|
|
1186
|
+
if (!overlayWindow || overlayWindow.isDestroyed()) {
|
|
1187
|
+
logger.info(`[Perf] 预创建 Overlay 窗口 (T+${Date.now() - startTime}ms)`);
|
|
1188
|
+
createOverlayWindow(isDebug);
|
|
1189
|
+
}
|
|
1141
1190
|
if (overlayWindow && !overlayWindow.isDestroyed() && overlayWindow.isVisible()) {
|
|
1142
|
-
overlayWindow.
|
|
1143
|
-
|
|
1191
|
+
const { y } = overlayWindow.getBounds();
|
|
1192
|
+
if (y < maxY) {
|
|
1193
|
+
overlayWindow.focus();
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1144
1196
|
}
|
|
1145
1197
|
await new Promise((resolve) => {
|
|
1146
|
-
setTimeout(resolve,
|
|
1198
|
+
setTimeout(resolve, 10);
|
|
1147
1199
|
});
|
|
1200
|
+
logger.info(`[Perf] DWM 稳定等待结束 (T+${Date.now() - startTime}ms)`);
|
|
1148
1201
|
let sessionData = [];
|
|
1149
1202
|
let capturedScreens = [];
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
if (!sessionData || sessionData.length === 0) {
|
|
1153
|
-
logger.error("启动失败: 未能捕获到任何屏幕画面。请检查原生模块加载状态及录屏权限。");
|
|
1154
|
-
return;
|
|
1155
|
-
}
|
|
1203
|
+
sessionData = await preCaptureAllScreens(startTime, isDebug);
|
|
1204
|
+
if (sessionData && sessionData.length > 0) {
|
|
1156
1205
|
currentCaptureSession = sessionData;
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
data
|
|
1161
|
-
|
|
1206
|
+
logger.info(`[Perf] 开始编码预览图 (T+${Date.now() - startTime}ms)...`);
|
|
1207
|
+
capturedScreens = await Promise.all(sessionData.map(async (s) => {
|
|
1208
|
+
const tEncode0 = Date.now();
|
|
1209
|
+
const data = await encodeImage(s.buffer, s.width, s.height, "webp");
|
|
1210
|
+
const tEncode1 = Date.now();
|
|
1211
|
+
logger.info(`[Perf] 编码显示器 ${s.displayId} 为 WebP 耗时: ${tEncode1 - tEncode0}ms`);
|
|
1212
|
+
return {
|
|
1213
|
+
displayId: s.displayId,
|
|
1214
|
+
bounds: s.bounds,
|
|
1215
|
+
data
|
|
1216
|
+
};
|
|
1217
|
+
}));
|
|
1218
|
+
logger.info(`[Perf] 所有预览图编码完成 (T+${Date.now() - startTime}ms)`);
|
|
1219
|
+
} else if (!isDebug) {
|
|
1220
|
+
logger.error("启动失败: 未能捕获到任何屏幕画面。请检查原生模块加载状态及录屏权限。");
|
|
1221
|
+
return;
|
|
1162
1222
|
} else {
|
|
1163
|
-
logger.info("Debug
|
|
1223
|
+
logger.info("Debug 模式:真实截图未返回数据,提供空/Mock数据以启动 UI");
|
|
1224
|
+
capturedScreens = [];
|
|
1164
1225
|
}
|
|
1165
1226
|
const cursorPos = electron.screen.getCursorScreenPoint();
|
|
1227
|
+
const tWin0 = Date.now();
|
|
1166
1228
|
const nativeWindows = getTopLevelWindows();
|
|
1167
|
-
logger.info(
|
|
1229
|
+
logger.info(`[Perf] 获取顶层窗口耗时: ${Date.now() - tWin0}ms, 数量: ${nativeWindows.length}`);
|
|
1168
1230
|
const windows = nativeWindows.map((win) => {
|
|
1169
1231
|
try {
|
|
1170
1232
|
const topLeft = electron.screen.screenToDipPoint({ x: win.left, y: win.top });
|
|
@@ -1183,13 +1245,6 @@ const startCapture = async (isDebug = false) => {
|
|
|
1183
1245
|
return null;
|
|
1184
1246
|
}
|
|
1185
1247
|
}).filter(Boolean);
|
|
1186
|
-
const {
|
|
1187
|
-
displays: allDisplays,
|
|
1188
|
-
minX,
|
|
1189
|
-
minY,
|
|
1190
|
-
width: totalWidth,
|
|
1191
|
-
height: totalHeight
|
|
1192
|
-
} = getAllDisplaysBounds();
|
|
1193
1248
|
allDisplays.forEach((d, idx) => {
|
|
1194
1249
|
windows.push({
|
|
1195
1250
|
handle: 0,
|
|
@@ -1208,11 +1263,6 @@ const startCapture = async (isDebug = false) => {
|
|
|
1208
1263
|
} else {
|
|
1209
1264
|
logger.warn("未能成功转换任何窗口坐标或搜索结果为空");
|
|
1210
1265
|
}
|
|
1211
|
-
if (!overlayWindow || overlayWindow.isDestroyed()) {
|
|
1212
|
-
createOverlayWindow(isDebug);
|
|
1213
|
-
} else {
|
|
1214
|
-
updateOverlayBounds();
|
|
1215
|
-
}
|
|
1216
1266
|
const initData = {
|
|
1217
1267
|
isDebug,
|
|
1218
1268
|
minX,
|
|
@@ -1226,14 +1276,25 @@ const startCapture = async (isDebug = false) => {
|
|
|
1226
1276
|
displays: allDisplays.map((d) => ({
|
|
1227
1277
|
id: d.id,
|
|
1228
1278
|
bounds: d.bounds
|
|
1229
|
-
}))
|
|
1279
|
+
})),
|
|
1280
|
+
// 传递触发时间戳,供 UI 计算总耗时
|
|
1281
|
+
startTime
|
|
1230
1282
|
};
|
|
1231
1283
|
const sendDataAndShow = () => {
|
|
1232
|
-
logger.info(
|
|
1284
|
+
logger.info(`[Perf] 发送初始化数据 (T+${Date.now() - startTime}ms), 截图数量: ${capturedScreens.length}, 窗口数量: ${windows.length}, isDebug: ${isDebug}`);
|
|
1233
1285
|
overlayWindow.webContents.send(`overlay-init@${PLUGIN_ID}`, initData);
|
|
1234
|
-
overlayWindow.
|
|
1286
|
+
overlayWindow.setBounds({
|
|
1287
|
+
x: minX,
|
|
1288
|
+
y: minY,
|
|
1289
|
+
width: totalWidth,
|
|
1290
|
+
height: totalHeight
|
|
1291
|
+
});
|
|
1292
|
+
if (!overlayWindow.isVisible()) {
|
|
1293
|
+
overlayWindow.showInactive();
|
|
1294
|
+
}
|
|
1235
1295
|
overlayWindow.focus();
|
|
1236
1296
|
overlayWindow.setIgnoreMouseEvents(false);
|
|
1297
|
+
logger.info(`[Perf] 窗口已显示并聚焦 (T+${Date.now() - startTime}ms)`);
|
|
1237
1298
|
};
|
|
1238
1299
|
if (overlayWindow.webContents.isLoading()) {
|
|
1239
1300
|
overlayWindow.webContents.once("did-finish-load", sendDataAndShow);
|
|
@@ -1251,11 +1312,15 @@ const registerShortcut = (accelerator) => {
|
|
|
1251
1312
|
if (!lastPart || modifiers.includes(lastPart)) {
|
|
1252
1313
|
return false;
|
|
1253
1314
|
}
|
|
1315
|
+
if (registeredShortcut === finalAccelerator) {
|
|
1316
|
+
return true;
|
|
1317
|
+
}
|
|
1254
1318
|
unregisterShortcut();
|
|
1255
1319
|
try {
|
|
1256
1320
|
const success = electron.globalShortcut.register(finalAccelerator, () => {
|
|
1257
|
-
|
|
1258
|
-
|
|
1321
|
+
const now = Date.now();
|
|
1322
|
+
logger.info(`[Perf] 快捷键触发: ${finalAccelerator} (T+0ms)`);
|
|
1323
|
+
startCapture(false, now).catch((err) => {
|
|
1259
1324
|
logger.error("快捷键触发 startCapture 失败:", err);
|
|
1260
1325
|
});
|
|
1261
1326
|
});
|
|
@@ -1273,10 +1338,15 @@ const registerShortcut = (accelerator) => {
|
|
|
1273
1338
|
};
|
|
1274
1339
|
const closeOverlay = () => {
|
|
1275
1340
|
if (overlayWindow && !overlayWindow.isDestroyed()) {
|
|
1341
|
+
try {
|
|
1342
|
+
overlayWindow.webContents.send(`overlay-reset@${PLUGIN_ID}`);
|
|
1343
|
+
} catch (e) {
|
|
1344
|
+
}
|
|
1276
1345
|
if (getFastResponse()) {
|
|
1277
|
-
|
|
1346
|
+
const { maxY } = getAllDisplaysBounds();
|
|
1278
1347
|
overlayWindow.setIgnoreMouseEvents(true);
|
|
1279
|
-
|
|
1348
|
+
overlayWindow.setPosition(0, maxY + 100);
|
|
1349
|
+
logger.info(`快速响应模式: Overlay 已移至离屏常驻 (0, ${maxY + 100})`);
|
|
1280
1350
|
} else {
|
|
1281
1351
|
overlayWindow.close();
|
|
1282
1352
|
overlayWindow = null;
|
|
@@ -1325,7 +1395,7 @@ const ipcHandlers = [
|
|
|
1325
1395
|
{
|
|
1326
1396
|
type: "start-capture",
|
|
1327
1397
|
handler: () => async ({ isDebug = false }) => {
|
|
1328
|
-
await startCapture(isDebug);
|
|
1398
|
+
await startCapture(isDebug, Date.now());
|
|
1329
1399
|
}
|
|
1330
1400
|
},
|
|
1331
1401
|
{
|
|
@@ -49,6 +49,15 @@ electron.contextBridge.exposeInMainWorld("hdrCapture", {
|
|
|
49
49
|
* @param {function} callback
|
|
50
50
|
*/
|
|
51
51
|
onInit: (callback) => {
|
|
52
|
+
electron.ipcRenderer.removeAllListeners(`overlay-init@${PLUGIN_ID}`);
|
|
52
53
|
electron.ipcRenderer.on(`overlay-init@${PLUGIN_ID}`, (event, data) => callback(data));
|
|
54
|
+
},
|
|
55
|
+
/**
|
|
56
|
+
* 监听重置消息
|
|
57
|
+
* @param {function} callback
|
|
58
|
+
*/
|
|
59
|
+
onReset: (callback) => {
|
|
60
|
+
electron.ipcRenderer.removeAllListeners(`overlay-reset@${PLUGIN_ID}`);
|
|
61
|
+
electron.ipcRenderer.on(`overlay-reset@${PLUGIN_ID}`, () => callback());
|
|
53
62
|
}
|
|
54
63
|
});
|
package/dist/overlay.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.frozen-screens-layer[data-v-0b001d45]{position:absolute;top:0;left:0;width:100%;height:100%;z-index:1;pointer-events:none}.frozen-screen[data-v-0b001d45]{position:absolute}.frozen-screen img[data-v-0b001d45]{width:100%;height:100%;object-fit:fill;display:block}.action-toolbar-container[data-v-2b54cb73]{position:absolute;display:flex;flex-direction:column;align-items:flex-end;z-index:100;pointer-events:none}.action-toolbar-main[data-v-2b54cb73]{display:flex;padding:4px;background:#1e1e1ef2;border-radius:8px;box-shadow:0 4px 20px #0006;backdrop-filter:blur(12px);border:1px solid rgb(255 255 255 / 10%);pointer-events:auto}.btn-group[data-v-2b54cb73]{display:flex;align-items:center}.btn[data-v-2b54cb73]{width:28px;height:28px;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;transition:all .15s ease;background:transparent;border-radius:4px;margin:0 1px}.btn[data-v-2b54cb73]:hover{background:#ffffff26}.btn[data-v-2b54cb73]:active{transform:scale(.95)}.btn.active[data-v-2b54cb73]{background:#ffffff40;color:#adf}.divider[data-v-2b54cb73]{width:1px;height:16px;background:#fff3;margin:0 4px}.btn-save[data-v-2b54cb73]:hover{background:#4caf5099}.btn-copy[data-v-2b54cb73]:hover{background:#2196f399}.btn-cancel[data-v-2b54cb73]:hover{background:#f4433699}.size-settings-bar[data-v-2b54cb73]{margin-top:4px;padding:4px 8px;background:#1e1e1ef2;border-radius:8px;box-shadow:0 4px 20px #0006;backdrop-filter:blur(12px);border:1px solid rgb(255 255 255 / 10%);pointer-events:auto;display:flex;align-items:center;gap:8px;height:36px;box-sizing:border-box}.size-inputs[data-v-2b54cb73]{display:flex;align-items:center;gap:4px;font-family:monospace;font-size:13px;color:#eee}.size-input[data-v-2b54cb73]{background:#0000004d;border:1px solid rgb(255 255 255 / 20%);border-radius:4px;color:#fff;padding:2px 4px;text-align:center;outline:none;font-family:inherit;font-size:inherit;min-width:4ch;height:22px;box-sizing:border-box}.size-input[data-v-2b54cb73]:focus{border-color:#2196f3;background:#0000007f}.size-input[data-v-2b54cb73]::-webkit-outer-spin-button,.size-input[data-v-2b54cb73]::-webkit-inner-spin-button{appearance:none;margin:0}.size-separator[data-v-2b54cb73]{color:#ffffff7f;font-size:12px}.size-unit[data-v-2b54cb73]{color:#ffffff7f;font-size:12px;margin-left:2px}.btn-confirm[data-v-2b54cb73]{border:none;background:#2196f3;color:#fff;border-radius:4px;padding:2px 8px;font-size:12px;cursor:pointer;height:22px;line-height:18px;transition:background .15s;white-space:nowrap}.btn-confirm[data-v-2b54cb73]:hover{background:#42a5f5}.btn-confirm[data-v-2b54cb73]:active{background:#1976d2}.radius-settings[data-v-2b54cb73]{display:flex;align-items:center;gap:8px;width:100%;color:#eee;font-size:13px}.radius-label[data-v-2b54cb73]{font-size:12px;color:#ffffffb3;white-space:nowrap}.radius-slider[data-v-2b54cb73]{flex:1;height:4px;border-radius:2px;background:#ffffff4d;outline:none;cursor:pointer;accent-color:#2196f3;width:100px}.magnifier[data-v-cf910dc4]{position:absolute;border-radius:4px;overflow:hidden;box-shadow:0 4px 12px #0000007f,0 0 0 1px #fff3;pointer-events:none;z-index:9999;background:#000;border:1px solid rgb(255 255 255 / 50%)}@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-black:#000;--color-white:#fff;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--radius-xl:.75rem;--ease-out:cubic-bezier(0,0,.2,1);--blur-xs:4px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:calc(var(--spacing)*0)}.-top-1\.5{top:calc(var(--spacing)*-1.5)}.-top-6{top:calc(var(--spacing)*-6)}.top-1{top:calc(var(--spacing)*1)}.top-1\/2{top:50%}.-right-1\.5{right:calc(var(--spacing)*-1.5)}.-bottom-1\.5{bottom:calc(var(--spacing)*-1.5)}.-left-1\.5{left:calc(var(--spacing)*-1.5)}.left-0{left:calc(var(--spacing)*0)}.left-1{left:calc(var(--spacing)*1)}.left-1\/2{left:50%}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.z-99{z-index:99}.box-border{box-sizing:border-box}.flex{display:flex}.h-3{height:calc(var(--spacing)*3)}.h-full{height:100%}.h-screen{height:100vh}.w-3{width:calc(var(--spacing)*3)}.w-full{width:100%}.w-screen{width:100vw}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.cursor-e-resize{cursor:e-resize}.cursor-move{cursor:move}.cursor-n-resize{cursor:n-resize}.cursor-ne-resize{cursor:ne-resize}.cursor-nw-resize{cursor:nw-resize}.cursor-s-resize{cursor:s-resize}.cursor-se-resize{cursor:se-resize}.cursor-sw-resize{cursor:sw-resize}.cursor-w-resize{cursor:w-resize}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-\[\#2196F3\]{border-color:#2196f3}.border-white\/10{border-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.border-white\/10{border-color:color-mix(in oklab,var(--color-white)10%,transparent)}}.bg-\[\#2196F3\]{background-color:#2196f3}.bg-\[\#2196F3\]\/10{background-color:#2196f31a}.bg-black\/75{background-color:#000000bf}@supports (color:color-mix(in lab,red,red)){.bg-black\/75{background-color:color-mix(in oklab,var(--color-black)75%,transparent)}}.bg-white{background-color:var(--color-white)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-4{padding-inline:calc(var(--spacing)*4)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-2{padding-block:calc(var(--spacing)*2)}.font-mono{font-family:var(--font-mono)}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.whitespace-nowrap{white-space:nowrap}.text-\[\#FF5252\]{color:#ff5252}.text-white{color:var(--color-white)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.backdrop-blur-xs{--tw-backdrop-blur:blur(var(--blur-xs));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-100{--tw-duration:.1s;transition-duration:.1s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.ease-\[cubic-bezier\(0\.23\,1\,0\.32\,1\)\]{--tw-ease:cubic-bezier(.23,1,.32,1);transition-timing-function:cubic-bezier(.23,1,.32,1)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.select-none{-webkit-user-select:none;user-select:none}}html,body{background:0 0;width:100vw;height:100vh;margin:0;padding:0;overflow:hidden}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}
|
|
1
|
+
.frozen-screens-layer[data-v-0b001d45]{position:absolute;top:0;left:0;width:100%;height:100%;z-index:1;pointer-events:none}.frozen-screen[data-v-0b001d45]{position:absolute}.frozen-screen img[data-v-0b001d45]{width:100%;height:100%;object-fit:fill;display:block}.action-toolbar-container[data-v-ebf36e2e]{position:absolute;display:flex;flex-direction:column;align-items:flex-end;z-index:100;pointer-events:none}.action-toolbar-main[data-v-ebf36e2e]{display:flex;padding:4px;background:#1e1e1ef2;border-radius:8px;box-shadow:0 4px 20px #0006;backdrop-filter:blur(12px);border:1px solid rgb(255 255 255 / 10%);pointer-events:auto}.btn-group[data-v-ebf36e2e]{display:flex;align-items:center}.btn[data-v-ebf36e2e]{width:28px;height:28px;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;transition:all .15s ease;background:transparent;border-radius:4px;margin:0 1px}.btn[data-v-ebf36e2e]:hover{background:#ffffff26}.btn[data-v-ebf36e2e]:active{transform:scale(.95)}.btn.active[data-v-ebf36e2e]{background:#ffffff40;color:#adf}.divider[data-v-ebf36e2e]{width:1px;height:16px;background:#fff3;margin:0 4px}.btn-save[data-v-ebf36e2e]:hover{background:#4caf5099}.btn-copy[data-v-ebf36e2e]:hover{background:#2196f399}.btn-cancel[data-v-ebf36e2e]:hover{background:#f4433699}.size-settings-bar[data-v-ebf36e2e]{margin-top:4px;padding:4px 8px;background:#1e1e1ef2;border-radius:8px;box-shadow:0 4px 20px #0006;backdrop-filter:blur(12px);border:1px solid rgb(255 255 255 / 10%);pointer-events:auto;display:flex;align-items:center;gap:8px;height:36px;box-sizing:border-box}.size-inputs[data-v-ebf36e2e]{display:flex;align-items:center;gap:4px;font-family:monospace;font-size:13px;color:#eee}.size-input[data-v-ebf36e2e]{background:#0000004d;border:1px solid rgb(255 255 255 / 20%);border-radius:4px;color:#fff;padding:2px 4px;text-align:center;outline:none;font-family:inherit;font-size:inherit;min-width:4ch;height:22px;box-sizing:border-box}.size-input[data-v-ebf36e2e]:focus{border-color:#2196f3;background:#0000007f}.size-input[data-v-ebf36e2e]::-webkit-outer-spin-button,.size-input[data-v-ebf36e2e]::-webkit-inner-spin-button{appearance:none;margin:0}.size-separator[data-v-ebf36e2e]{color:#ffffff7f;font-size:12px}.size-unit[data-v-ebf36e2e]{color:#ffffff7f;font-size:12px;margin-left:2px}.btn-confirm[data-v-ebf36e2e]{border:none;background:#2196f3;color:#fff;border-radius:4px;padding:2px 8px;font-size:12px;cursor:pointer;height:22px;line-height:18px;transition:background .15s;white-space:nowrap}.btn-confirm[data-v-ebf36e2e]:hover{background:#42a5f5}.btn-confirm[data-v-ebf36e2e]:active{background:#1976d2}.radius-settings[data-v-ebf36e2e]{display:flex;align-items:center;gap:8px;width:100%;color:#eee;font-size:13px}.radius-label[data-v-ebf36e2e]{font-size:12px;color:#ffffffb3;white-space:nowrap}.radius-slider[data-v-ebf36e2e]{flex:1;height:4px;border-radius:2px;background:#ffffff4d;outline:none;cursor:pointer;accent-color:#2196f3;width:100px}.magnifier[data-v-37869409]{position:absolute;border-radius:4px;overflow:hidden;box-shadow:0 4px 12px #0000007f,0 0 0 1px #fff3;pointer-events:none;z-index:9999;background:#000;border:1px solid rgb(255 255 255 / 50%)}@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-black:#000;--color-white:#fff;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--radius-xl:.75rem;--ease-out:cubic-bezier(0,0,.2,1);--blur-xs:4px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:calc(var(--spacing)*0)}.-top-1\.5{top:calc(var(--spacing)*-1.5)}.-top-6{top:calc(var(--spacing)*-6)}.top-1{top:calc(var(--spacing)*1)}.top-1\/2{top:50%}.-right-1\.5{right:calc(var(--spacing)*-1.5)}.-bottom-1\.5{bottom:calc(var(--spacing)*-1.5)}.-left-1\.5{left:calc(var(--spacing)*-1.5)}.left-0{left:calc(var(--spacing)*0)}.left-1{left:calc(var(--spacing)*1)}.left-1\/2{left:50%}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.z-99{z-index:99}.box-border{box-sizing:border-box}.flex{display:flex}.h-3{height:calc(var(--spacing)*3)}.h-full{height:100%}.h-screen{height:100vh}.w-3{width:calc(var(--spacing)*3)}.w-full{width:100%}.w-screen{width:100vw}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.cursor-e-resize{cursor:e-resize}.cursor-move{cursor:move}.cursor-n-resize{cursor:n-resize}.cursor-ne-resize{cursor:ne-resize}.cursor-nw-resize{cursor:nw-resize}.cursor-s-resize{cursor:s-resize}.cursor-se-resize{cursor:se-resize}.cursor-sw-resize{cursor:sw-resize}.cursor-w-resize{cursor:w-resize}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-\[\#2196F3\]{border-color:#2196f3}.border-white\/10{border-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.border-white\/10{border-color:color-mix(in oklab,var(--color-white)10%,transparent)}}.bg-\[\#2196F3\]{background-color:#2196f3}.bg-\[\#2196F3\]\/10{background-color:#2196f31a}.bg-black\/75{background-color:#000000bf}@supports (color:color-mix(in lab,red,red)){.bg-black\/75{background-color:color-mix(in oklab,var(--color-black)75%,transparent)}}.bg-white{background-color:var(--color-white)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-4{padding-inline:calc(var(--spacing)*4)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-2{padding-block:calc(var(--spacing)*2)}.font-mono{font-family:var(--font-mono)}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.whitespace-nowrap{white-space:nowrap}.text-\[\#FF5252\]{color:#ff5252}.text-white{color:var(--color-white)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.backdrop-blur-xs{--tw-backdrop-blur:blur(var(--blur-xs));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-100{--tw-duration:.1s;transition-duration:.1s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.ease-\[cubic-bezier\(0\.23\,1\,0\.32\,1\)\]{--tw-ease:cubic-bezier(.23,1,.32,1);transition-timing-function:cubic-bezier(.23,1,.32,1)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.select-none{-webkit-user-select:none;user-select:none}}html,body{background:0 0;width:100vw;height:100vh;margin:0;padding:0;overflow:hidden}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}
|