electron-snapora 1.0.1
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/CHANGELOG.md +21 -0
- package/LICENSE +21 -0
- package/README.es.md +79 -0
- package/README.ja.md +79 -0
- package/README.ko.md +79 -0
- package/README.md +392 -0
- package/README.zh-CN.md +79 -0
- package/dist/core/index.cjs +184 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +97 -0
- package/dist/core/index.d.ts +97 -0
- package/dist/core/index.mjs +150 -0
- package/dist/core/index.mjs.map +1 -0
- package/dist/main/index.cjs +1637 -0
- package/dist/main/index.cjs.map +1 -0
- package/dist/main/index.d.cts +283 -0
- package/dist/main/index.d.ts +283 -0
- package/dist/main/index.mjs +1599 -0
- package/dist/main/index.mjs.map +1 -0
- package/dist/messages-DYY0MpGh.d.ts +76 -0
- package/dist/messages-ifub9bae.d.cts +76 -0
- package/dist/overlay/assets/index-7dVRGn83.css +1 -0
- package/dist/overlay/assets/index-CJf5iC1U.js +2 -0
- package/dist/overlay/assets/index-CJf5iC1U.js.map +1 -0
- package/dist/overlay/index.html +316 -0
- package/dist/overlay/preload.cjs +97 -0
- package/dist/overlay/preload.cjs.map +1 -0
- package/dist/overlay/preload.d.cts +16 -0
- package/dist/overlay/preload.d.ts +16 -0
- package/dist/overlay/preload.mjs +80 -0
- package/dist/overlay/preload.mjs.map +1 -0
- package/dist/preload/auto.cjs +24 -0
- package/dist/preload/auto.cjs.map +1 -0
- package/dist/preload/auto.d.cts +2 -0
- package/dist/preload/auto.d.ts +2 -0
- package/dist/preload/auto.mjs +22 -0
- package/dist/preload/auto.mjs.map +1 -0
- package/dist/preload/index.cjs +46 -0
- package/dist/preload/index.cjs.map +1 -0
- package/dist/preload/index.d.cts +18 -0
- package/dist/preload/index.d.ts +18 -0
- package/dist/preload/index.mjs +19 -0
- package/dist/preload/index.mjs.map +1 -0
- package/dist/types/index.cjs +19 -0
- package/dist/types/index.cjs.map +1 -0
- package/dist/types/index.d.cts +92 -0
- package/dist/types/index.d.ts +92 -0
- package/dist/types/index.mjs +1 -0
- package/dist/types/index.mjs.map +1 -0
- package/package.json +158 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/main.ts","../../src/electron/main/screenshot-manager.ts","../../src/electron/main/diagnostics.ts","../../src/electron/main/electron-capture-adapter.ts","../../src/electron/protocol/limits.ts","../../src/electron/main/errors.ts","../../src/electron/main/electron-output-adapter.ts","../../src/electron/main/overlay-window.ts","../../src/electron/protocol/channels.ts","../../src/electron/main/resource-paths.ts","../../src/electron/protocol/messages.ts","../../src/electron/protocol/validators.ts","../../src/electron/main/output-action-router.ts","../../src/electron/main/screenshot-session.ts","../../src/electron/main/register-host-ipc.ts"],"sourcesContent":["export {\n ScreenshotManager,\n type ScreenshotExecution,\n type ScreenshotBusyPolicy,\n type ScreenshotJobContext,\n type ScreenshotRunner,\n type ScreenshotManagerOptions,\n type ScreenshotManagerIpcMain,\n} from './electron/main/screenshot-manager.js';\nexport {\n ElectronCaptureAdapter,\n type ElectronCaptureAdapterOptions,\n} from './electron/main/electron-capture-adapter.js';\nexport { ScreenshotError } from './electron/main/errors.js';\nexport {\n type ScreenshotDiagnosticContextValue,\n type ScreenshotDiagnosticEvent,\n type ScreenshotDiagnosticListener,\n type ScreenshotDiagnosticPhase,\n type ScreenshotDiagnosticStage,\n} from './electron/main/diagnostics.js';\nexport {\n ElectronOutputAdapter,\n type ElectronOutputAdapterOptions,\n type ScreenshotOutputExecutor,\n} from './electron/main/electron-output-adapter.js';\nexport {\n OverlayWindow,\n type OverlayWindowOptions,\n type ScreenshotOverlayWindow,\n} from './electron/main/overlay-window.js';\nexport {\n resolveHostPreloadPath,\n resolveOverlayResources,\n assertOverlayResources,\n PackagedResourceError,\n type MissingPackagedResource,\n type OverlayResources,\n type PackagedResourceExists,\n} from './electron/main/resource-paths.js';\nexport {\n ScreenshotSession,\n type ScreenshotOverlayFactory,\n type ScreenshotSessionOptions,\n type ScreenshotSessionState,\n} from './electron/main/screenshot-session.js';\nexport {\n registerScreenshotIpc,\n setupElectronSnapora,\n type RegisterScreenshotIpcOptions,\n type SetupElectronSnaporaOptions,\n type SetupElectronSnaporaResult,\n type ValidateScreenshotIpcSender,\n} from './electron/main/register-host-ipc.js';\nexport {\n DEFAULT_HOST_CANCEL_CHANNEL,\n DEFAULT_HOST_CAPTURE_CHANNEL,\n OVERLAY_CHANNELS,\n} from './electron/protocol/channels.js';\nexport { SCREENSHOT_PROTOCOL_VERSION } from './electron/protocol/messages.js';\nexport {\n DEFAULT_SCREENSHOT_RESOURCE_LIMITS,\n HARD_SCREENSHOT_RESOURCE_LIMITS,\n resolveScreenshotResourceLimits,\n type ScreenshotResourceLimitOptions,\n type ScreenshotResourceLimits,\n} from './electron/protocol/limits.js';\nexport type {\n CaptureDisplay,\n CapturedFrame,\n ScreenCaptureAdapter,\n ScreenshotCancelPayload,\n ScreenshotCompletePayload,\n ScreenshotErrorPayload,\n ScreenshotInitializePayload,\n ScreenshotOutputAction,\n ScreenshotOutputPayload,\n ScreenshotOutputResponse,\n ScreenshotReadyPayload,\n} from './electron/protocol/messages.js';\nexport type {\n ScreenshotBounds,\n ScreenshotErrorCode,\n ScreenshotImageResult,\n ScreenshotLocale,\n ScreenshotMessageOverrides,\n ScreenshotMessages,\n ScreenshotOptions,\n ScreenshotOutputMetadata,\n ScreenshotResult,\n ScreenshotTheme,\n ScreenshotTool,\n} from './types.js';\n","import { randomUUID } from 'node:crypto';\n\nimport { ipcMain as electronIpcMain, webContents } from 'electron';\nimport type { IpcMain } from 'electron';\n\nimport type { ScreenshotOptions, ScreenshotResult } from '../../types.js';\nimport {\n emitScreenshotDiagnostic,\n type ScreenshotDiagnosticContextValue,\n type ScreenshotDiagnosticEvent,\n type ScreenshotDiagnosticListener,\n type ScreenshotDiagnosticPhase,\n type ScreenshotDiagnosticStage,\n} from './diagnostics.js';\nimport { ElectronCaptureAdapter } from './electron-capture-adapter.js';\nimport {\n ElectronOutputAdapter,\n type ScreenshotOutputExecutor,\n} from './electron-output-adapter.js';\nimport { toScreenshotFailure } from './errors.js';\nimport { OverlayWindow, type OverlayWindowOptions } from './overlay-window.js';\nimport { getScreenshotOutputRouter } from './output-action-router.js';\nimport {\n ScreenshotSession,\n type ScreenshotOverlayFactory,\n} from './screenshot-session.js';\nimport type { ScreenCaptureAdapter } from '../protocol/messages.js';\nimport {\n resolveScreenshotResourceLimits,\n type ScreenshotResourceLimitOptions,\n} from '../protocol/limits.js';\n\nexport interface ScreenshotJobContext {\n senderWebContentsId?: number;\n}\n\nexport interface ScreenshotExecution {\n result: Promise<ScreenshotResult>;\n cancel(): boolean;\n}\n\nexport type ScreenshotBusyPolicy = 'reject' | 'queue';\n\nexport type ScreenshotRunner = (\n jobId: string,\n options: ScreenshotOptions,\n context: ScreenshotJobContext\n) => Promise<ScreenshotResult> | ScreenshotExecution;\n\nexport type ScreenshotManagerIpcMain = Pick<\n IpcMain,\n 'handle' | 'on' | 'removeListener'\n>;\n\nexport interface ScreenshotManagerOptions {\n /** 完全接管会话执行;设置后其余高层注入项不会参与默认 runner。 */\n runner?: ScreenshotRunner;\n ipcMain?: ScreenshotManagerIpcMain;\n captureAdapter?: ScreenCaptureAdapter;\n outputAdapter?: ScreenshotOutputExecutor;\n createOverlay?: ScreenshotOverlayFactory;\n overlayOptions?: Omit<OverlayWindowOptions, 'display'>;\n overlayReadyTimeoutMs?: number;\n resourceLimits?: ScreenshotResourceLimitOptions;\n /** reject 保持即时失败;queue 仅为其他宿主窗口按 FIFO 排队。 */\n busyPolicy?: ScreenshotBusyPolicy;\n maxQueuedCaptures?: number;\n onDiagnostic?: ScreenshotDiagnosticListener;\n}\n\ninterface QueuedCapture {\n jobId: string;\n queuedAt: number;\n options: ScreenshotOptions;\n context: ScreenshotJobContext;\n resolve(result: ScreenshotResult): void;\n}\n\nconst DEFAULT_MAX_QUEUED_CAPTURES = 8;\n\nfunction createDefaultRunner(\n managerOptions: ScreenshotManagerOptions\n): ScreenshotRunner {\n const ipcMain = managerOptions.ipcMain ?? electronIpcMain;\n const resourceLimits = resolveScreenshotResourceLimits(managerOptions.resourceLimits);\n const captureAdapter =\n managerOptions.captureAdapter ?? new ElectronCaptureAdapter({ resourceLimits });\n const outputAdapter = managerOptions.outputAdapter ?? new ElectronOutputAdapter();\n const outputRouter = getScreenshotOutputRouter(ipcMain);\n const createOverlayWindow =\n managerOptions.createOverlay ??\n ((display) => new OverlayWindow({ ...managerOptions.overlayOptions, display }));\n let previousOverlay: ReturnType<ScreenshotOverlayFactory> | undefined;\n\n return (jobId, captureOptions, context) => {\n // 连续截图前清理仍在展示的复制提示,避免 Toast 被下一次屏幕采集写入图片。\n previousOverlay?.destroy();\n previousOverlay = undefined;\n const reportDiagnostic: ScreenshotDiagnosticListener = (event) => {\n emitScreenshotDiagnostic(managerOptions.onDiagnostic, {\n ...event,\n ...(context.senderWebContentsId === undefined\n ? {}\n : { senderWebContentsId: context.senderWebContentsId }),\n });\n };\n const session = new ScreenshotSession({\n jobId,\n captureOptions,\n captureAdapter,\n ipcMain,\n createOverlay: (display) => {\n const overlay = createOverlayWindow(display);\n previousOverlay = overlay;\n return overlay;\n },\n ...(managerOptions.overlayReadyTimeoutMs === undefined\n ? {}\n : { overlayReadyTimeoutMs: managerOptions.overlayReadyTimeoutMs }),\n resourceLimits,\n onDiagnostic: reportDiagnostic,\n registerOutputHandler: (senderWebContentsId, activeJobId) =>\n outputRouter.register(\n senderWebContentsId,\n activeJobId,\n async (payload, outputContext) => {\n const startedAt = Date.now();\n reportDiagnostic({\n jobId: activeJobId,\n stage: 'output',\n phase: 'start',\n timestamp: startedAt,\n context: {\n action: payload.action,\n outputBytes: payload.result.data.byteLength,\n },\n });\n\n let response;\n try {\n response =\n payload.result.data.byteLength > resourceLimits.maxOutputBytes\n ? {\n status: 'failed' as const,\n code: 'RESOURCE_LIMIT_EXCEEDED' as const,\n message: `Screenshot output exceeds the ${resourceLimits.maxOutputBytes} byte limit.`,\n }\n : await outputAdapter.execute(payload, outputContext);\n } catch (error) {\n const timestamp = Date.now();\n reportDiagnostic({\n jobId: activeJobId,\n stage: 'output',\n phase: 'error',\n timestamp,\n durationMs: Math.max(0, timestamp - startedAt),\n code: 'EXPORT_FAILED',\n message:\n error instanceof Error ? error.message : 'Screenshot output failed.',\n context: { action: payload.action },\n });\n throw error;\n }\n\n const timestamp = Date.now();\n reportDiagnostic({\n jobId: activeJobId,\n stage: 'output',\n phase:\n response.status === 'failed'\n ? 'error'\n : response.status === 'cancelled'\n ? 'cancel'\n : 'complete',\n timestamp,\n durationMs: Math.max(0, timestamp - startedAt),\n ...(response.status === 'failed'\n ? { code: response.code, message: response.message }\n : {}),\n context: { action: payload.action },\n });\n return response;\n }\n ),\n onSettled: () => {\n if (context.senderWebContentsId === undefined) {\n return;\n }\n\n const sender = webContents.fromId(context.senderWebContentsId);\n if (sender && !sender.isDestroyed()) {\n sender.focus();\n }\n },\n });\n\n return {\n result: session.run(),\n cancel: () => session.cancel(),\n };\n };\n}\n\n/**\n * 统一管理截图会话,确保同一时刻只存在一个覆盖层任务。\n * 窗口创建、屏幕采集与导出由 runner 注入,避免核心调度逻辑绑定 Electron 版本细节。\n */\nexport class ScreenshotManager {\n readonly #runner: ScreenshotRunner;\n readonly #busyPolicy: ScreenshotBusyPolicy;\n readonly #maxQueuedCaptures: number;\n readonly #queue: QueuedCapture[] = [];\n readonly #onDiagnostic: ScreenshotDiagnosticListener | undefined;\n #activeJobId: string | undefined;\n #activeSenderWebContentsId: number | undefined;\n #cancelActive: (() => boolean) | undefined;\n\n constructor(options: ScreenshotManagerOptions = {}) {\n if (\n options.busyPolicy !== undefined &&\n !['reject', 'queue'].includes(options.busyPolicy)\n ) {\n throw new TypeError('busyPolicy must be either reject or queue.');\n }\n this.#busyPolicy = options.busyPolicy ?? 'reject';\n this.#maxQueuedCaptures = options.maxQueuedCaptures ?? DEFAULT_MAX_QUEUED_CAPTURES;\n if (\n !Number.isSafeInteger(this.#maxQueuedCaptures) ||\n this.#maxQueuedCaptures < 1 ||\n this.#maxQueuedCaptures > 100\n ) {\n throw new TypeError('maxQueuedCaptures must be an integer between 1 and 100.');\n }\n this.#runner = options.runner ?? createDefaultRunner(options);\n this.#onDiagnostic = options.onDiagnostic;\n }\n\n get activeJobId(): string | undefined {\n return this.#activeJobId;\n }\n\n get queuedCaptureCount(): number {\n return this.#queue.length;\n }\n\n /** 不传 sender ID 时供主进程直接取消;传入时只允许取消该页面创建的任务。 */\n cancel(senderWebContentsId?: number): boolean {\n let cancelled = false;\n if (\n this.#activeJobId &&\n (senderWebContentsId === undefined ||\n this.#activeSenderWebContentsId === senderWebContentsId)\n ) {\n cancelled = this.#cancelActive?.() ?? false;\n }\n\n // Renderer 销毁时同时移除它尚未启动的任务,避免无主 Overlay 稍后被唤起。\n if (senderWebContentsId !== undefined) {\n for (let index = this.#queue.length - 1; index >= 0; index -= 1) {\n const queued = this.#queue[index];\n if (queued?.context.senderWebContentsId !== senderWebContentsId) {\n continue;\n }\n this.#queue.splice(index, 1);\n this.#finishDiagnosticStage(\n queued.jobId,\n 'queue',\n 'cancel',\n queued.queuedAt,\n queued.context,\n undefined,\n { queueDepth: this.#queue.length }\n );\n queued.resolve({ status: 'cancelled' });\n cancelled = true;\n }\n }\n return cancelled;\n }\n\n capture(\n options: ScreenshotOptions = {},\n context: ScreenshotJobContext = {}\n ): Promise<ScreenshotResult> {\n if (this.#activeJobId) {\n return this.#handleBusyCapture(options, context);\n }\n\n return this.#executeCapture(options, context);\n }\n\n #handleBusyCapture(\n options: ScreenshotOptions,\n context: ScreenshotJobContext\n ): Promise<ScreenshotResult> {\n const senderWebContentsId = context.senderWebContentsId;\n const sameSenderAlreadyPending =\n senderWebContentsId !== undefined &&\n (this.#activeSenderWebContentsId === senderWebContentsId ||\n this.#queue.some(\n (queued) => queued.context.senderWebContentsId === senderWebContentsId\n ));\n if (\n this.#busyPolicy === 'reject' ||\n sameSenderAlreadyPending ||\n this.#queue.length >= this.#maxQueuedCaptures\n ) {\n const result: ScreenshotResult = {\n status: 'failed',\n code: 'CAPTURE_BUSY',\n message: sameSenderAlreadyPending\n ? 'This window already has a screenshot task pending.'\n : 'A screenshot task is already running.',\n };\n const timestamp = Date.now();\n this.#emitDiagnostic({\n jobId: randomUUID(),\n stage: 'queue',\n phase: 'error',\n timestamp,\n durationMs: 0,\n ...(senderWebContentsId === undefined ? {} : { senderWebContentsId }),\n code: result.code,\n message: result.message,\n context: {\n queueDepth: this.#queue.length,\n reason: sameSenderAlreadyPending\n ? 'sender-mutex'\n : this.#busyPolicy === 'reject'\n ? 'policy'\n : 'queue-capacity',\n },\n });\n return Promise.resolve(result);\n }\n\n return new Promise((resolve) => {\n const jobId = randomUUID();\n const queuedAt = Date.now();\n this.#queue.push({ jobId, queuedAt, options, context, resolve });\n this.#emitDiagnostic({\n jobId,\n stage: 'queue',\n phase: 'start',\n timestamp: queuedAt,\n ...(senderWebContentsId === undefined ? {} : { senderWebContentsId }),\n context: { queueDepth: this.#queue.length },\n });\n });\n }\n\n async #executeCapture(\n options: ScreenshotOptions,\n context: ScreenshotJobContext,\n jobId: string = randomUUID()\n ): Promise<ScreenshotResult> {\n const startedAt = Date.now();\n this.#activeJobId = jobId;\n this.#activeSenderWebContentsId = context.senderWebContentsId;\n this.#emitDiagnostic({\n jobId,\n stage: 'session',\n phase: 'start',\n timestamp: startedAt,\n ...(context.senderWebContentsId === undefined\n ? {}\n : { senderWebContentsId: context.senderWebContentsId }),\n });\n\n let result: ScreenshotResult;\n try {\n const execution = this.#runner(jobId, options, context);\n if (isScreenshotExecution(execution)) {\n this.#cancelActive = execution.cancel;\n result = await execution.result;\n } else {\n result = await execution;\n }\n } catch (error) {\n result = toScreenshotFailure(error);\n }\n\n this.#finishDiagnosticStage(\n jobId,\n 'session',\n diagnosticPhaseForResult(result),\n startedAt,\n context,\n result,\n { status: result.status }\n );\n if (this.#activeJobId === jobId) {\n this.#activeJobId = undefined;\n this.#activeSenderWebContentsId = undefined;\n this.#cancelActive = undefined;\n this.#startNextQueuedCapture();\n }\n return result;\n }\n\n #startNextQueuedCapture(): void {\n const next = this.#queue.shift();\n if (!next) {\n return;\n }\n this.#finishDiagnosticStage(\n next.jobId,\n 'queue',\n 'complete',\n next.queuedAt,\n next.context,\n undefined,\n { queueDepth: this.#queue.length }\n );\n void this.#executeCapture(next.options, next.context, next.jobId).then(\n next.resolve\n );\n }\n\n #finishDiagnosticStage(\n jobId: string,\n stage: ScreenshotDiagnosticStage,\n phase: Exclude<ScreenshotDiagnosticPhase, 'start'>,\n startedAt: number,\n context: ScreenshotJobContext,\n result?: ScreenshotResult,\n diagnosticContext?: Readonly<Record<string, ScreenshotDiagnosticContextValue>>\n ): void {\n const timestamp = Date.now();\n this.#emitDiagnostic({\n jobId,\n stage,\n phase,\n timestamp,\n durationMs: Math.max(0, timestamp - startedAt),\n ...(context.senderWebContentsId === undefined\n ? {}\n : { senderWebContentsId: context.senderWebContentsId }),\n ...(result?.status === 'failed'\n ? { code: result.code, message: result.message }\n : {}),\n ...(diagnosticContext ? { context: diagnosticContext } : {}),\n });\n }\n\n #emitDiagnostic(event: ScreenshotDiagnosticEvent): void {\n emitScreenshotDiagnostic(this.#onDiagnostic, event);\n }\n}\n\nfunction isScreenshotExecution(\n value: Promise<ScreenshotResult> | ScreenshotExecution\n): value is ScreenshotExecution {\n return 'result' in value && typeof value.cancel === 'function';\n}\n\nfunction diagnosticPhaseForResult(\n result: ScreenshotResult\n): Exclude<ScreenshotDiagnosticPhase, 'start'> {\n return result.status === 'failed'\n ? 'error'\n : result.status === 'cancelled'\n ? 'cancel'\n : 'complete';\n}\n","import type { ScreenshotErrorCode } from '../../types.js';\n\nexport type ScreenshotDiagnosticStage =\n | 'queue'\n | 'session'\n | 'capture'\n | 'overlay-create'\n | 'overlay-load'\n | 'overlay-ready'\n | 'overlay-prepare'\n | 'output';\n\nexport type ScreenshotDiagnosticPhase = 'start' | 'complete' | 'cancel' | 'error';\n\nexport type ScreenshotDiagnosticContextValue =\n string | number | boolean | ReadonlyArray<string | number | boolean>;\n\nexport interface ScreenshotDiagnosticEvent {\n jobId: string;\n stage: ScreenshotDiagnosticStage;\n phase: ScreenshotDiagnosticPhase;\n timestamp: number;\n durationMs?: number;\n senderWebContentsId?: number;\n code?: ScreenshotErrorCode;\n message?: string;\n context?: Readonly<Record<string, ScreenshotDiagnosticContextValue>>;\n}\n\nexport type ScreenshotDiagnosticListener = (\n event: Readonly<ScreenshotDiagnosticEvent>\n) => void;\n\n/** 诊断回调不能影响截图主流程;宿主日志实现抛错时直接隔离。 */\nexport function emitScreenshotDiagnostic(\n listener: ScreenshotDiagnosticListener | undefined,\n event: ScreenshotDiagnosticEvent\n): void {\n try {\n listener?.(event);\n } catch {\n // 日志钩子属于旁路能力,不能让截图任务失败。\n }\n}\n","import { desktopCapturer, screen, systemPreferences } from 'electron';\n\nimport type { ScreenshotOptions } from '../../types.js';\nimport type {\n CaptureDisplay,\n CapturedFrame,\n ScreenCaptureAdapter,\n} from '../protocol/messages.js';\nimport {\n findCapturedFrameLimitViolation,\n resolveScreenshotResourceLimits,\n type ScreenshotResourceLimitOptions,\n type ScreenshotResourceLimits,\n} from '../protocol/limits.js';\nimport { ScreenshotError } from './errors.js';\n\ninterface ElectronDisplayLike {\n id: number;\n bounds: {\n x: number;\n y: number;\n width: number;\n height: number;\n };\n scaleFactor: number;\n}\n\ninterface ScreenApi {\n getAllDisplays(): ElectronDisplayLike[];\n getCursorScreenPoint(): { x: number; y: number };\n getDisplayNearestPoint(point: { x: number; y: number }): ElectronDisplayLike;\n getPrimaryDisplay(): ElectronDisplayLike;\n}\n\ninterface DesktopCaptureSource {\n display_id: string;\n thumbnail: {\n getSize(): { width: number; height: number };\n isEmpty(): boolean;\n toDataURL(): string;\n };\n}\n\ninterface DesktopCapturerApi {\n getSources(options: {\n types: Array<'screen'>;\n thumbnailSize: { width: number; height: number };\n fetchWindowIcons: boolean;\n }): Promise<DesktopCaptureSource[]>;\n}\n\ntype ScreenPermissionStatus =\n 'not-determined' | 'granted' | 'denied' | 'restricted' | 'unknown';\n\nexport interface ElectronCaptureAdapterOptions {\n desktopCapturer?: DesktopCapturerApi;\n screen?: ScreenApi;\n platform?: NodeJS.Platform;\n getScreenPermissionStatus?: () => ScreenPermissionStatus;\n resourceLimits?: ScreenshotResourceLimitOptions;\n}\n\n/**\n * Electron 默认屏幕采集实现。适配器只依赖长期稳定的 Electron API,\n * 采集结果转换为普通数据结构后再交给 Overlay,避免 UI 直接持有 NativeImage。\n */\nexport class ElectronCaptureAdapter implements ScreenCaptureAdapter {\n readonly #desktopCapturer: DesktopCapturerApi;\n readonly #screen: ScreenApi;\n readonly #platform: NodeJS.Platform;\n readonly #getScreenPermissionStatus: () => ScreenPermissionStatus;\n readonly #resourceLimits: ScreenshotResourceLimits;\n\n constructor(options: ElectronCaptureAdapterOptions = {}) {\n this.#desktopCapturer = options.desktopCapturer ?? desktopCapturer;\n this.#screen = options.screen ?? screen;\n this.#platform = options.platform ?? process.platform;\n this.#getScreenPermissionStatus =\n options.getScreenPermissionStatus ??\n (() => systemPreferences.getMediaAccessStatus('screen'));\n this.#resourceLimits = resolveScreenshotResourceLimits(options.resourceLimits);\n }\n\n resolveTargetDisplay(options: ScreenshotOptions = {}): CaptureDisplay {\n this.#assertPermission();\n return this.#toCaptureDisplay(this.#resolveDisplay(options.display ?? 'cursor'));\n }\n\n async capture(\n options: ScreenshotOptions = {},\n targetDisplay?: CaptureDisplay\n ): Promise<CapturedFrame[]> {\n this.#assertPermission();\n\n // Session 预加载 Overlay 后必须继续使用同一个显示器,不能再次按鼠标位置解析。\n const display = targetDisplay\n ? this.#resolveDisplay(targetDisplay.id)\n : this.#resolveDisplay(options.display ?? 'cursor');\n const pixelSize = {\n width: Math.max(1, Math.round(display.bounds.width * display.scaleFactor)),\n height: Math.max(1, Math.round(display.bounds.height * display.scaleFactor)),\n };\n this.#assertRequestedPixelSize(pixelSize);\n\n let sources: DesktopCaptureSource[];\n try {\n sources = await this.#desktopCapturer.getSources({\n types: ['screen'],\n thumbnailSize: pixelSize,\n fetchWindowIcons: false,\n });\n } catch (error) {\n // macOS 首次请求可能先显示系统授权弹窗,失败后状态才变为 denied。\n this.#assertPermission();\n throw new ScreenshotError(\n 'CAPTURE_FAILED',\n 'Electron failed to capture the screen.',\n {\n cause: error,\n }\n );\n }\n\n const source = sources.find(\n (candidate) => candidate.display_id === String(display.id)\n );\n if (!source) {\n this.#assertPermission();\n throw new ScreenshotError(\n 'DISPLAY_NOT_FOUND',\n `No desktop capture source matched display ${display.id}.`\n );\n }\n\n if (source.thumbnail.isEmpty()) {\n this.#assertPermission();\n throw new ScreenshotError(\n 'CAPTURE_FAILED',\n 'Electron returned an empty screen image.'\n );\n }\n\n const actualPixelSize = source.thumbnail.getSize();\n if (actualPixelSize.width <= 0 || actualPixelSize.height <= 0) {\n throw new ScreenshotError(\n 'CAPTURE_FAILED',\n 'Electron returned an invalid screen image size.'\n );\n }\n\n const frame = {\n display: this.#toCaptureDisplay(display),\n dataUrl: source.thumbnail.toDataURL(),\n pixelSize: actualPixelSize,\n };\n const violation = findCapturedFrameLimitViolation(frame, this.#resourceLimits);\n if (violation) {\n throw new ScreenshotError('RESOURCE_LIMIT_EXCEEDED', violation);\n }\n return [frame];\n }\n\n #assertRequestedPixelSize(pixelSize: { width: number; height: number }): void {\n if (pixelSize.width * pixelSize.height > this.#resourceLimits.maxCapturePixels) {\n throw new ScreenshotError(\n 'RESOURCE_LIMIT_EXCEEDED',\n `Requested capture exceeds the ${this.#resourceLimits.maxCapturePixels} pixel limit.`\n );\n }\n }\n\n #assertPermission(): void {\n if (this.#platform !== 'darwin') {\n return;\n }\n\n const status = this.#getScreenPermissionStatus();\n if (status === 'denied' || status === 'restricted') {\n throw new ScreenshotError(\n 'PERMISSION_DENIED',\n 'Screen recording permission is required to capture the display.'\n );\n }\n }\n\n #resolveDisplay(\n requestedDisplay: NonNullable<ScreenshotOptions['display']>\n ): ElectronDisplayLike {\n if (requestedDisplay === 'cursor') {\n return this.#screen.getDisplayNearestPoint(this.#screen.getCursorScreenPoint());\n }\n\n if (requestedDisplay === 'primary') {\n return this.#screen.getPrimaryDisplay();\n }\n\n const display = this.#screen\n .getAllDisplays()\n .find((candidate) => String(candidate.id) === requestedDisplay);\n if (!display) {\n throw new ScreenshotError(\n 'DISPLAY_NOT_FOUND',\n `Display ${requestedDisplay} is not available.`\n );\n }\n\n return display;\n }\n\n #toCaptureDisplay(display: ElectronDisplayLike): CaptureDisplay {\n return {\n id: String(display.id),\n bounds: { ...display.bounds },\n scaleFactor: display.scaleFactor,\n };\n }\n}\n","import type { CapturedFrame } from './messages.js';\n\nexport interface ScreenshotResourceLimits {\n maxCapturePixels: number;\n maxCaptureDataUrlBytes: number;\n maxOutputBytes: number;\n}\n\nexport type ScreenshotResourceLimitOptions = Partial<ScreenshotResourceLimits>;\n\nexport const DEFAULT_SCREENSHOT_RESOURCE_LIMITS: Readonly<ScreenshotResourceLimits> =\n Object.freeze({\n maxCapturePixels: 64 * 1024 * 1024,\n maxCaptureDataUrlBytes: 192 * 1024 * 1024,\n maxOutputBytes: 64 * 1024 * 1024,\n });\n\nexport const HARD_SCREENSHOT_RESOURCE_LIMITS: Readonly<ScreenshotResourceLimits> =\n Object.freeze({\n maxCapturePixels: 128 * 1024 * 1024,\n maxCaptureDataUrlBytes: 256 * 1024 * 1024,\n maxOutputBytes: 256 * 1024 * 1024,\n });\n\nexport function resolveScreenshotResourceLimits(\n options: ScreenshotResourceLimitOptions = {}\n): ScreenshotResourceLimits {\n return {\n maxCapturePixels: resolveLimit('maxCapturePixels', options.maxCapturePixels),\n maxCaptureDataUrlBytes: resolveLimit(\n 'maxCaptureDataUrlBytes',\n options.maxCaptureDataUrlBytes\n ),\n maxOutputBytes: resolveLimit('maxOutputBytes', options.maxOutputBytes),\n };\n}\n\n/** 返回第一项超限原因,由调用层决定映射为捕获失败或导出失败。 */\nexport function findCapturedFrameLimitViolation(\n frame: CapturedFrame,\n limits: ScreenshotResourceLimits\n): string | undefined {\n const { width, height } = frame.pixelSize;\n if (\n !Number.isFinite(width) ||\n !Number.isFinite(height) ||\n width <= 0 ||\n height <= 0 ||\n width * height > limits.maxCapturePixels\n ) {\n return `Captured frame exceeds the ${limits.maxCapturePixels} pixel limit.`;\n }\n if (frame.dataUrl.length > limits.maxCaptureDataUrlBytes) {\n return `Captured frame exceeds the ${limits.maxCaptureDataUrlBytes} byte Data URL limit.`;\n }\n return undefined;\n}\n\nfunction resolveLimit(\n key: keyof ScreenshotResourceLimits,\n configuredValue: number | undefined\n): number {\n const value = configuredValue ?? DEFAULT_SCREENSHOT_RESOURCE_LIMITS[key];\n if (\n !Number.isSafeInteger(value) ||\n value <= 0 ||\n value > HARD_SCREENSHOT_RESOURCE_LIMITS[key]\n ) {\n throw new TypeError(\n `[electron-snapora] ${key} must be a positive safe integer no greater than ${HARD_SCREENSHOT_RESOURCE_LIMITS[key]}.`\n );\n }\n return value;\n}\n","import type { ScreenshotErrorCode, ScreenshotResult } from '../../types.js';\n\nexport class ScreenshotError extends Error {\n readonly code: ScreenshotErrorCode;\n\n constructor(code: ScreenshotErrorCode, message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'ScreenshotError';\n this.code = code;\n }\n}\n\nexport function toScreenshotFailure(\n error: unknown,\n fallbackCode: ScreenshotErrorCode = 'CAPTURE_FAILED'\n): ScreenshotResult {\n if (error instanceof ScreenshotError) {\n return {\n status: 'failed',\n code: error.code,\n message: error.message,\n };\n }\n\n return {\n status: 'failed',\n code: fallbackCode,\n message: error instanceof Error ? error.message : 'Unknown screenshot error.',\n };\n}\n","import { writeFile } from 'node:fs/promises';\n\nimport { BrowserWindow, clipboard, dialog, nativeImage, webContents } from 'electron';\nimport type { SaveDialogOptions } from 'electron';\n\nimport type {\n ScreenshotOutputPayload,\n ScreenshotOutputResponse,\n} from '../protocol/messages.js';\nimport type { ScreenshotOutputContext } from './output-action-router.js';\n\nexport interface ElectronOutputAdapterOptions {\n saveFile?: (\n data: Uint8Array,\n suggestedName: string,\n senderWebContentsId: number\n ) => Promise<string | undefined>;\n copyImage?: (data: Uint8Array) => void;\n createSuggestedName?: () => string;\n}\n\nexport interface ScreenshotOutputExecutor {\n execute(\n payload: ScreenshotOutputPayload,\n context: ScreenshotOutputContext\n ): Promise<ScreenshotOutputResponse>;\n}\n\nexport class ElectronOutputAdapter implements ScreenshotOutputExecutor {\n readonly #saveFile: NonNullable<ElectronOutputAdapterOptions['saveFile']>;\n readonly #copyImage: NonNullable<ElectronOutputAdapterOptions['copyImage']>;\n readonly #createSuggestedName: NonNullable<\n ElectronOutputAdapterOptions['createSuggestedName']\n >;\n\n constructor(options: ElectronOutputAdapterOptions = {}) {\n this.#saveFile = options.saveFile ?? savePngWithDialog;\n this.#copyImage = options.copyImage ?? copyPngToClipboard;\n this.#createSuggestedName = options.createSuggestedName ?? createSuggestedName;\n }\n\n async execute(\n payload: ScreenshotOutputPayload,\n context: ScreenshotOutputContext\n ): Promise<ScreenshotOutputResponse> {\n if (payload.action === 'copy') {\n this.#copyImage(payload.result.data);\n return { status: 'completed', action: 'copy' };\n }\n\n const filePath = await this.#saveFile(\n payload.result.data,\n this.#createSuggestedName(),\n context.senderWebContentsId\n );\n return filePath\n ? { status: 'completed', action: 'save', filePath }\n : { status: 'cancelled' };\n }\n}\n\nasync function savePngWithDialog(\n data: Uint8Array,\n suggestedName: string,\n senderWebContentsId: number\n): Promise<string | undefined> {\n const sender = webContents.fromId(senderWebContentsId);\n const owner = sender ? BrowserWindow.fromWebContents(sender) : null;\n const options: SaveDialogOptions = {\n title: 'Save screenshot',\n defaultPath: suggestedName,\n filters: [{ name: 'PNG image', extensions: ['png'] }],\n properties: ['createDirectory', 'showOverwriteConfirmation'],\n };\n const result = owner\n ? await dialog.showSaveDialog(owner, options)\n : await dialog.showSaveDialog(options);\n if (result.canceled || !result.filePath) {\n return undefined;\n }\n\n await writeFile(result.filePath, data);\n return result.filePath;\n}\n\nfunction copyPngToClipboard(data: Uint8Array): void {\n const image = nativeImage.createFromBuffer(Buffer.from(data));\n if (image.isEmpty()) {\n throw new Error('The exported PNG could not be decoded for the clipboard.');\n }\n clipboard.writeImage(image);\n}\n\nfunction createSuggestedName(): string {\n const timestamp = new Date()\n .toISOString()\n .replaceAll(':', '-')\n .replace(/\\.\\d{3}Z$/, '');\n return `screenshot-${timestamp}.png`;\n}\n","import { BrowserWindow } from 'electron';\nimport type { BrowserWindowConstructorOptions, WebContents } from 'electron';\nimport type { ScreenshotOptions } from '../../types.js';\n\nimport type {\n CaptureDisplay,\n ScreenshotInitializePayload,\n} from '../protocol/messages.js';\nimport { OVERLAY_CHANNELS } from '../protocol/channels.js';\nimport {\n assertOverlayResources,\n resolveOverlayResources,\n type OverlayResources,\n type PackagedResourceExists,\n} from './resource-paths.js';\n\nexport type OverlayBrowserWindow = Pick<\n BrowserWindow,\n | 'destroy'\n | 'focus'\n | 'isDestroyed'\n | 'loadFile'\n | 'moveTop'\n | 'on'\n | 'removeListener'\n | 'setAlwaysOnTop'\n | 'setBounds'\n | 'setIgnoreMouseEvents'\n | 'setOpacity'\n | 'setVisibleOnAllWorkspaces'\n | 'show'\n | 'showInactive'\n> & {\n webContents: Pick<WebContents, 'id' | 'on' | 'removeListener' | 'send'>;\n};\n\nexport type OverlayBrowserWindowFactory = (\n options: BrowserWindowConstructorOptions\n) => OverlayBrowserWindow;\n\nexport interface OverlayWindowOptions {\n display: CaptureDisplay;\n resources?: OverlayResources;\n createWindow?: OverlayBrowserWindowFactory;\n platform?: NodeJS.Platform;\n resourceExists?: PackagedResourceExists;\n}\n\nexport interface ScreenshotOverlayWindow {\n readonly webContentsId: number;\n load(): Promise<void>;\n sendInitialize(payload: ScreenshotInitializePayload): void;\n prime(): void;\n reveal(): void;\n showCopyFeedback?(durationMs: number, options: ScreenshotOptions): void;\n destroy(): void;\n onClosed(listener: () => void): () => void;\n onRendererGone(listener: () => void): () => void;\n}\n\n/** 管理包内截图窗口,窗口始终使用隔离上下文且不向页面开放 Node.js。 */\nexport class OverlayWindow implements ScreenshotOverlayWindow {\n readonly #resources: OverlayResources;\n readonly #window: OverlayBrowserWindow;\n readonly #createWindow: OverlayBrowserWindowFactory;\n readonly #bounds: CaptureDisplay['bounds'];\n readonly #platform: NodeJS.Platform;\n readonly #supportsInvisiblePriming: boolean;\n #primed = false;\n #feedbackWindow: OverlayBrowserWindow | undefined;\n #feedbackTimer: ReturnType<typeof setTimeout> | undefined;\n\n constructor(options: OverlayWindowOptions) {\n this.#resources =\n options.resources ?? resolveOverlayResources(undefined, options.resourceExists);\n assertOverlayResources(this.#resources, options.resourceExists);\n this.#createWindow =\n options.createWindow ?? ((windowOptions) => new BrowserWindow(windowOptions));\n const { bounds } = options.display;\n this.#bounds = bounds;\n this.#platform = options.platform ?? process.platform;\n this.#supportsInvisiblePriming = ['win32', 'darwin'].includes(this.#platform);\n\n this.#window = this.#createWindow({\n x: bounds.x,\n y: bounds.y,\n width: bounds.width,\n height: bounds.height,\n useContentSize: true,\n frame: false,\n hasShadow: false,\n resizable: false,\n movable: false,\n minimizable: false,\n maximizable: false,\n // macOS 简易全屏要求窗口具备全屏能力;无边框窗口没有可供用户切换的系统按钮。\n fullscreenable: this.#platform === 'darwin',\n skipTaskbar: true,\n alwaysOnTop: true,\n transparent: false,\n show: false,\n opacity: this.#supportsInvisiblePriming ? 0 : 1,\n paintWhenInitiallyHidden: true,\n autoHideMenuBar: true,\n backgroundColor: '#000000',\n ...(this.#platform === 'win32'\n ? { roundedCorners: false, thickFrame: false }\n : {}),\n ...(this.#platform === 'darwin'\n ? {\n enableLargerThanScreen: true,\n hiddenInMissionControl: true,\n simpleFullscreen: true,\n }\n : {}),\n webPreferences: {\n preload: this.#resources.preloadPath,\n nodeIntegration: false,\n contextIsolation: true,\n sandbox: true,\n zoomFactor: 1,\n },\n });\n this.#configureMacWorkspaceVisibility(this.#window);\n }\n\n get webContentsId(): number {\n return this.#window.webContents.id;\n }\n\n async load(): Promise<void> {\n await this.#window.loadFile(this.#resources.htmlPath);\n }\n\n sendInitialize(payload: ScreenshotInitializePayload): void {\n this.#window.webContents.send(OVERLAY_CHANNELS.initialize, payload);\n }\n\n /** 先以全透明状态进入桌面合成器,隐藏 Windows/macOS 的窗口出场和大图首帧栅格化。 */\n prime(): void {\n if (!this.#window.isDestroyed() && this.#supportsInvisiblePriming) {\n this.#raiseAboveOtherWindows();\n this.#window.showInactive();\n if (this.#platform !== 'darwin') {\n // Windows 会在首次显示时把无边框窗口压到 workArea;显示后重设 bounds 才能覆盖任务栏。\n this.#window.setBounds(this.#bounds, false);\n }\n this.#window.moveTop();\n this.#primed = true;\n }\n }\n\n reveal(): void {\n if (this.#window.isDestroyed()) {\n return;\n }\n this.#raiseAboveOtherWindows();\n if (this.#primed) {\n this.#window.setOpacity(1);\n this.#window.focus();\n } else {\n this.#window.show();\n }\n this.#window.moveTop();\n }\n\n /** 复制完成后销毁全屏截图层,改用独立的小窗口显示鼠标穿透提示。 */\n showCopyFeedback(durationMs: number, options: ScreenshotOptions): void {\n if (!this.#window.isDestroyed()) {\n this.#window.destroy();\n }\n this.#destroyFeedbackWindow();\n\n const width = Math.min(360, this.#bounds.width);\n const height = Math.min(72, this.#bounds.height);\n const feedbackWindow = this.#createWindow({\n x: this.#bounds.x + Math.round((this.#bounds.width - width) / 2),\n y: this.#bounds.y + Math.min(24, Math.max(0, this.#bounds.height - height)),\n width,\n height,\n useContentSize: true,\n frame: false,\n hasShadow: false,\n resizable: false,\n movable: false,\n minimizable: false,\n maximizable: false,\n fullscreenable: false,\n focusable: false,\n skipTaskbar: true,\n alwaysOnTop: true,\n transparent: true,\n show: false,\n paintWhenInitiallyHidden: true,\n autoHideMenuBar: true,\n backgroundColor: '#00000000',\n webPreferences: {\n preload: this.#resources.preloadPath,\n nodeIntegration: false,\n contextIsolation: true,\n sandbox: true,\n zoomFactor: 1,\n },\n });\n this.#feedbackWindow = feedbackWindow;\n this.#configureMacWorkspaceVisibility(feedbackWindow);\n feedbackWindow.setIgnoreMouseEvents(true);\n\n const handleIpcMessage = (_event: unknown, channel: string): void => {\n if (channel !== OVERLAY_CHANNELS.feedbackReady) {\n return;\n }\n feedbackWindow.webContents.removeListener('ipc-message', handleIpcMessage);\n if (feedbackWindow.isDestroyed() || this.#feedbackWindow !== feedbackWindow) {\n return;\n }\n this.#clearFeedbackTimer();\n this.#raiseAboveOtherWindows(feedbackWindow);\n feedbackWindow.showInactive();\n feedbackWindow.moveTop();\n this.#feedbackTimer = setTimeout(\n () => this.#destroyFeedbackWindow(feedbackWindow),\n durationMs\n );\n this.#feedbackTimer.unref?.();\n };\n feedbackWindow.webContents.on('ipc-message', handleIpcMessage);\n\n // 如果反馈渲染进程异常,隐藏窗口最多保留两秒,避免后台泄漏。\n this.#feedbackTimer = setTimeout(\n () => this.#destroyFeedbackWindow(feedbackWindow),\n 2_000\n );\n this.#feedbackTimer.unref?.();\n void feedbackWindow\n .loadFile(this.#resources.htmlPath)\n .then(() => {\n if (!feedbackWindow.isDestroyed() && this.#feedbackWindow === feedbackWindow) {\n feedbackWindow.webContents.send(OVERLAY_CHANNELS.feedback, {\n kind: 'copy',\n durationMs,\n options,\n });\n }\n })\n .catch(() => this.#destroyFeedbackWindow(feedbackWindow));\n }\n\n /** 截图层必须高于普通置顶窗口;Windows/macOS 使用系统支持的最高标准层级。 */\n #raiseAboveOtherWindows(window = this.#window): void {\n if (this.#platform === 'win32' || this.#platform === 'darwin') {\n window.setAlwaysOnTop(true, 'screen-saver');\n return;\n }\n window.setAlwaysOnTop(true);\n }\n\n /** 让截图层和复制提示在 macOS 全屏 Space 中也保持可见。 */\n #configureMacWorkspaceVisibility(window: OverlayBrowserWindow): void {\n if (this.#platform === 'darwin') {\n window.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });\n }\n }\n\n destroy(): void {\n this.#destroyFeedbackWindow();\n if (!this.#window.isDestroyed()) {\n this.#window.destroy();\n }\n }\n\n #clearFeedbackTimer(): void {\n if (this.#feedbackTimer) {\n clearTimeout(this.#feedbackTimer);\n this.#feedbackTimer = undefined;\n }\n }\n\n #destroyFeedbackWindow(window = this.#feedbackWindow): void {\n this.#clearFeedbackTimer();\n if (window && !window.isDestroyed()) {\n window.destroy();\n }\n if (this.#feedbackWindow === window) {\n this.#feedbackWindow = undefined;\n }\n }\n\n onClosed(listener: () => void): () => void {\n this.#window.on('closed', listener);\n return () => {\n if (!this.#window.isDestroyed()) {\n this.#window.removeListener('closed', listener);\n }\n };\n }\n\n onRendererGone(listener: () => void): () => void {\n const handleGone = () => {\n listener();\n };\n\n this.#window.webContents.on('render-process-gone', handleGone);\n return () => {\n // closed 事件触发时 WebContents 已销毁,不能再访问其 EventEmitter。\n if (!this.#window.isDestroyed()) {\n this.#window.webContents.removeListener('render-process-gone', handleGone);\n }\n };\n }\n}\n","export const DEFAULT_HOST_CAPTURE_CHANNEL = 'electron-snapora:host:capture';\nexport const DEFAULT_HOST_CANCEL_CHANNEL = 'electron-snapora:host:cancel';\n\nexport const OVERLAY_CHANNELS = {\n ready: 'electron-snapora:overlay:ready',\n prepared: 'electron-snapora:overlay:prepared',\n initialize: 'electron-snapora:overlay:initialize',\n feedback: 'electron-snapora:overlay:feedback',\n feedbackReady: 'electron-snapora:overlay:feedback-ready',\n confirm: 'electron-snapora:overlay:confirm',\n cancel: 'electron-snapora:overlay:cancel',\n error: 'electron-snapora:overlay:error',\n output: 'electron-snapora:overlay:output',\n} as const;\n","import { existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nexport interface OverlayResources {\n htmlPath: string;\n preloadPath: string;\n}\n\nexport type PackagedResourceExists = (path: string) => boolean;\n\nexport interface MissingPackagedResource {\n label: string;\n path: string;\n}\n\nexport class PackagedResourceError extends Error {\n readonly missingResources: ReadonlyArray<MissingPackagedResource>;\n\n constructor(missingResources: ReadonlyArray<MissingPackagedResource>) {\n const details = missingResources\n .map((resource) => `${resource.label}: ${resource.path}`)\n .join('; ');\n super(\n `[electron-snapora] Packaged resource missing (${details}). ` +\n 'Keep electron-snapora external in the Electron main-process bundle and include its complete dist directory in the application package.'\n );\n this.name = 'PackagedResourceError';\n this.missingResources = missingResources.map((resource) => ({ ...resource }));\n }\n}\n\n/**\n * main 入口发布在 dist/main,Overlay 页面与内部 Preload 发布在 dist/overlay。\n * 资源定位集中在这里,避免宿主构建工具或 Electron 版本参与路径拼接。\n */\nexport function resolveOverlayResources(\n mainModuleDirectory = __dirname,\n resourceExists: PackagedResourceExists = existsSync\n): OverlayResources {\n const overlayDirectory = resolve(mainModuleDirectory, '..', 'overlay');\n const resources = {\n htmlPath: resolve(overlayDirectory, 'index.html'),\n preloadPath: resolve(overlayDirectory, 'preload.cjs'),\n };\n assertOverlayResources(resources, resourceExists);\n return resources;\n}\n\n/** 返回包内已打包的默认宿主 Preload,适用于不需要合并其他 Preload 的窗口。 */\nexport function resolveHostPreloadPath(\n mainModuleDirectory = __dirname,\n resourceExists: PackagedResourceExists = existsSync\n): string {\n const preloadPath = resolve(mainModuleDirectory, '..', 'preload', 'auto.cjs');\n assertPackagedResources(\n [{ label: 'host preload', path: preloadPath }],\n resourceExists\n );\n return preloadPath;\n}\n\nexport function assertOverlayResources(\n resources: OverlayResources,\n resourceExists: PackagedResourceExists = existsSync\n): void {\n assertPackagedResources(\n [\n { label: 'overlay HTML', path: resources.htmlPath },\n { label: 'overlay preload', path: resources.preloadPath },\n ],\n resourceExists\n );\n}\n\n/** 在 BrowserWindow 创建前失败,避免主进程被打包后只表现为白屏或 ready 超时。 */\nfunction assertPackagedResources(\n resources: Array<{ label: string; path: string }>,\n resourceExists: PackagedResourceExists\n): void {\n const missing = resources.filter((resource) => !resourceExists(resource.path));\n if (missing.length === 0) {\n return;\n }\n\n throw new PackagedResourceError(missing);\n}\n","import type {\n ScreenshotBounds,\n ScreenshotErrorCode,\n ScreenshotImageResult,\n ScreenshotOptions,\n ScreenshotResult,\n} from '../../types.js';\n\nexport const SCREENSHOT_PROTOCOL_VERSION = 1 as const;\n\nexport interface ScreenshotReadyPayload {\n protocolVersion: typeof SCREENSHOT_PROTOCOL_VERSION;\n}\n\nexport interface ScreenshotPreparedPayload extends ScreenshotReadyPayload {\n jobId: string;\n}\n\nexport interface CaptureDisplay {\n id: string;\n bounds: ScreenshotBounds;\n scaleFactor: number;\n}\n\nexport interface CapturedFrame {\n display: CaptureDisplay;\n dataUrl: string;\n pixelSize: {\n width: number;\n height: number;\n };\n}\n\nexport interface ScreenCaptureAdapter {\n /**\n * 可选的同步目标解析,用于让主进程在屏幕采集期间并行加载隐藏 Overlay。\n * 返回值会作为锁定目标传给紧随其后的 capture(),避免鼠标跨屏造成截图与窗口错位。\n */\n resolveTargetDisplay?(options: ScreenshotOptions): CaptureDisplay;\n capture(\n options: ScreenshotOptions,\n targetDisplay?: CaptureDisplay\n ): Promise<CapturedFrame[]>;\n}\n\nexport interface ScreenshotInitializePayload {\n protocolVersion: typeof SCREENSHOT_PROTOCOL_VERSION;\n jobId: string;\n options: ScreenshotOptions;\n frames: CapturedFrame[];\n}\n\nexport interface ScreenshotFeedbackPayload {\n kind: 'copy';\n durationMs: number;\n options: ScreenshotOptions;\n}\n\nexport interface ScreenshotCompletePayload {\n protocolVersion: typeof SCREENSHOT_PROTOCOL_VERSION;\n jobId: string;\n result: ScreenshotResult;\n}\n\nexport interface ScreenshotCancelPayload {\n protocolVersion: typeof SCREENSHOT_PROTOCOL_VERSION;\n jobId: string;\n}\n\nexport interface ScreenshotErrorPayload {\n protocolVersion: typeof SCREENSHOT_PROTOCOL_VERSION;\n jobId: string;\n code: ScreenshotErrorCode;\n message: string;\n}\n\nexport type ScreenshotOutputAction = 'save' | 'copy';\n\nexport interface ScreenshotOutputPayload {\n protocolVersion: typeof SCREENSHOT_PROTOCOL_VERSION;\n jobId: string;\n action: ScreenshotOutputAction;\n result: ScreenshotImageResult;\n}\n\nexport type ScreenshotOutputResponse =\n | { status: 'completed'; action: 'copy' }\n | { status: 'completed'; action: 'save'; filePath: string }\n | { status: 'cancelled' }\n | { status: 'failed'; code: ScreenshotErrorCode; message: string };\n","import type {\n ScreenshotBounds,\n ScreenshotErrorCode,\n ScreenshotImageResult,\n ScreenshotMessageOverrides,\n ScreenshotMessages,\n ScreenshotOptions,\n ScreenshotOutputMetadata,\n ScreenshotResult,\n ScreenshotTheme,\n ScreenshotTool,\n} from '../../types.js';\nimport {\n SCREENSHOT_PROTOCOL_VERSION,\n type ScreenshotCancelPayload,\n type ScreenshotCompletePayload,\n type ScreenshotErrorPayload,\n type ScreenshotOutputPayload,\n type ScreenshotPreparedPayload,\n type ScreenshotReadyPayload,\n} from './messages.js';\nimport { HARD_SCREENSHOT_RESOURCE_LIMITS } from './limits.js';\n\nconst SCREENSHOT_ERROR_CODES = new Set<ScreenshotErrorCode>([\n 'CAPTURE_BUSY',\n 'INVALID_REQUEST',\n 'RESOURCE_LIMIT_EXCEEDED',\n 'PERMISSION_DENIED',\n 'DISPLAY_NOT_FOUND',\n 'CAPTURE_FAILED',\n 'OVERLAY_LOAD_FAILED',\n 'EXPORT_FAILED',\n 'INVALID_RESULT',\n 'UNSUPPORTED_PLATFORM',\n]);\n\nconst SCREENSHOT_TOOLS = new Set<ScreenshotTool>([\n 'rectangle',\n 'ellipse',\n 'arrow',\n 'brush',\n 'text',\n 'mosaic',\n]);\n\nconst SCREENSHOT_OPTION_KEYS = new Set([\n 'display',\n 'tools',\n 'defaultTool',\n 'locale',\n 'messages',\n 'theme',\n]);\n\nconst SCREENSHOT_THEME_COLOR_KEYS = [\n 'accentColor',\n 'accentForegroundColor',\n 'maskColor',\n 'toolbarBackground',\n 'toolbarForeground',\n 'toolbarBorderColor',\n 'toolbarHoverBackground',\n 'tooltipBackground',\n 'tooltipForeground',\n 'destructiveColor',\n 'warningColor',\n 'warningForegroundColor',\n 'selectionHandleColor',\n] as const satisfies ReadonlyArray<Exclude<keyof ScreenshotTheme, 'mode'>>;\n\nconst SCREENSHOT_THEME_KEYS = new Set<string>(['mode', ...SCREENSHOT_THEME_COLOR_KEYS]);\n\nconst SCREENSHOT_MESSAGE_KEYS = [\n 'preparing',\n 'instruction',\n 'exporting',\n 'copied',\n 'saveCancelled',\n 'cancel',\n 'save',\n 'confirm',\n 'select',\n 'rectangle',\n 'ellipse',\n 'arrow',\n 'brush',\n 'text',\n 'mosaic',\n 'undo',\n 'redo',\n 'color',\n 'lineWidth',\n 'fontSize',\n 'annotationCanvas',\n 'selection',\n 'actions',\n 'annotationTools',\n 'history',\n 'annotationStyle',\n 'outputActions',\n 'annotationText',\n] as const satisfies ReadonlyArray<keyof ScreenshotMessages>;\n\nconst SCREENSHOT_MESSAGE_KEY_SET = new Set<string>(SCREENSHOT_MESSAGE_KEYS);\n\nexport type ScreenshotOptionsParseResult =\n { success: true; value: ScreenshotOptions } | { success: false; message: string };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction hasOnlyKeys(\n value: Record<string, unknown>,\n allowedKeys: Set<string>\n): boolean {\n return Object.keys(value).every((key) => allowedKeys.has(key));\n}\n\nfunction isBoundedString(value: unknown, maximumLength: number): value is string {\n return typeof value === 'string' && value.length > 0 && value.length <= maximumLength;\n}\n\n/**\n * Renderer 传入的类型声明不能替代 IPC 边界校验。这里复制白名单字段,\n * 避免畸形枚举、超长字符串或未来未审计字段进入捕获与 Overlay 流程。\n */\nexport function parseScreenshotOptions(value: unknown): ScreenshotOptionsParseResult {\n if (value === undefined) {\n return { success: true, value: {} };\n }\n if (!isRecord(value) || !hasOnlyKeys(value, SCREENSHOT_OPTION_KEYS)) {\n return {\n success: false,\n message: 'Screenshot options must be a plain supported object.',\n };\n }\n\n const parsed: ScreenshotOptions = {};\n if (value.display !== undefined) {\n if (!isBoundedString(value.display, 128)) {\n return {\n success: false,\n message: 'Screenshot display must be a non-empty string.',\n };\n }\n parsed.display = value.display;\n }\n\n if (value.tools !== undefined) {\n if (\n !Array.isArray(value.tools) ||\n value.tools.length > SCREENSHOT_TOOLS.size ||\n !value.tools.every(\n (tool): tool is ScreenshotTool =>\n typeof tool === 'string' && SCREENSHOT_TOOLS.has(tool as ScreenshotTool)\n )\n ) {\n return {\n success: false,\n message: 'Screenshot tools contain an unsupported value.',\n };\n }\n parsed.tools = [...new Set(value.tools)];\n }\n\n if (value.defaultTool !== undefined) {\n if (\n value.defaultTool !== 'select' &&\n (typeof value.defaultTool !== 'string' ||\n !SCREENSHOT_TOOLS.has(value.defaultTool as ScreenshotTool))\n ) {\n return { success: false, message: 'Screenshot defaultTool is unsupported.' };\n }\n if (\n value.defaultTool !== 'select' &&\n parsed.tools &&\n !parsed.tools.includes(value.defaultTool as ScreenshotTool)\n ) {\n return {\n success: false,\n message: 'Screenshot defaultTool must be enabled in tools.',\n };\n }\n parsed.defaultTool = value.defaultTool as 'select' | ScreenshotTool;\n }\n\n if (value.locale !== undefined) {\n if (value.locale !== 'zh-CN' && value.locale !== 'en-US') {\n return { success: false, message: 'Screenshot locale is unsupported.' };\n }\n parsed.locale = value.locale;\n }\n\n if (value.messages !== undefined) {\n const messages = parseScreenshotMessages(value.messages);\n if (!messages) {\n return { success: false, message: 'Screenshot messages are invalid.' };\n }\n parsed.messages = messages;\n }\n\n if (value.theme !== undefined) {\n const theme = parseScreenshotTheme(value.theme);\n if (!theme) {\n return { success: false, message: 'Screenshot theme is invalid.' };\n }\n parsed.theme = theme;\n }\n\n return { success: true, value: parsed };\n}\n\nfunction parseScreenshotTheme(value: unknown): ScreenshotTheme | undefined {\n if (!isRecord(value) || !hasOnlyKeys(value, SCREENSHOT_THEME_KEYS)) {\n return undefined;\n }\n\n const theme: ScreenshotTheme = {};\n if (value.mode !== undefined) {\n if (value.mode !== 'dark' && value.mode !== 'light') {\n return undefined;\n }\n theme.mode = value.mode;\n }\n for (const key of SCREENSHOT_THEME_COLOR_KEYS) {\n const color = value[key];\n if (color === undefined) {\n continue;\n }\n if (!isBoundedString(color, 128)) {\n return undefined;\n }\n theme[key] = color;\n }\n return theme;\n}\n\nfunction parseScreenshotMessages(\n value: unknown\n): ScreenshotMessageOverrides | undefined {\n if (!isRecord(value) || !hasOnlyKeys(value, SCREENSHOT_MESSAGE_KEY_SET)) {\n return undefined;\n }\n\n const messages: ScreenshotMessageOverrides = {};\n for (const key of SCREENSHOT_MESSAGE_KEYS) {\n const message = value[key];\n if (message === undefined) {\n continue;\n }\n if (!isBoundedString(message, 256)) {\n return undefined;\n }\n messages[key] = message;\n }\n return messages;\n}\n\nfunction hasProtocolVersion(value: Record<string, unknown>): boolean {\n return value.protocolVersion === SCREENSHOT_PROTOCOL_VERSION;\n}\n\nfunction hasExpectedJob(\n value: Record<string, unknown>,\n expectedJobId: string\n): boolean {\n return value.jobId === expectedJobId;\n}\n\nfunction isFiniteNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value);\n}\n\nfunction isBounds(value: unknown): value is ScreenshotBounds {\n if (!isRecord(value)) {\n return false;\n }\n\n return (\n isFiniteNumber(value.x) &&\n isFiniteNumber(value.y) &&\n isFiniteNumber(value.width) &&\n isFiniteNumber(value.height) &&\n value.width > 0 &&\n value.height > 0\n );\n}\n\nfunction isImageResult(\n value: unknown,\n maxOutputBytes = HARD_SCREENSHOT_RESOURCE_LIMITS.maxOutputBytes\n): value is ScreenshotImageResult {\n if (!isRecord(value) || value.status !== 'completed') {\n return false;\n }\n\n return (\n value.data instanceof Uint8Array &&\n value.data.byteLength > 0 &&\n value.data.byteLength <= maxOutputBytes &&\n value.mimeType === 'image/png' &&\n isBounds(value.bounds) &&\n typeof value.displayId === 'string' &&\n value.displayId.length > 0\n );\n}\n\nfunction isOutputMetadata(value: unknown): value is ScreenshotOutputMetadata {\n if (!isRecord(value)) {\n return false;\n }\n if (value.action === 'copy') {\n return true;\n }\n return (\n value.action === 'save' &&\n typeof value.filePath === 'string' &&\n value.filePath.length > 0\n );\n}\n\nfunction isCompletedResult(\n value: unknown,\n maxOutputBytes?: number\n): value is Extract<ScreenshotResult, { status: 'completed' }> {\n return (\n isImageResult(value, maxOutputBytes) &&\n isOutputMetadata((value as unknown as Record<string, unknown>).output)\n );\n}\n\nexport function isReadyPayload(value: unknown): value is ScreenshotReadyPayload {\n return isRecord(value) && hasProtocolVersion(value);\n}\n\nexport function isPreparedPayload(\n value: unknown,\n expectedJobId: string\n): value is ScreenshotPreparedPayload {\n return (\n isRecord(value) && hasProtocolVersion(value) && hasExpectedJob(value, expectedJobId)\n );\n}\n\nexport function isCompletePayload(\n value: unknown,\n expectedJobId: string,\n maxOutputBytes?: number\n): value is ScreenshotCompletePayload {\n return (\n isRecord(value) &&\n hasProtocolVersion(value) &&\n hasExpectedJob(value, expectedJobId) &&\n isCompletedResult(value.result, maxOutputBytes)\n );\n}\n\nexport function isCancelPayload(\n value: unknown,\n expectedJobId: string\n): value is ScreenshotCancelPayload {\n return (\n isRecord(value) && hasProtocolVersion(value) && hasExpectedJob(value, expectedJobId)\n );\n}\n\nexport function isErrorPayload(\n value: unknown,\n expectedJobId: string\n): value is ScreenshotErrorPayload {\n return (\n isRecord(value) &&\n hasProtocolVersion(value) &&\n hasExpectedJob(value, expectedJobId) &&\n typeof value.code === 'string' &&\n SCREENSHOT_ERROR_CODES.has(value.code as ScreenshotErrorCode) &&\n typeof value.message === 'string' &&\n value.message.length > 0\n );\n}\n\nexport function isOutputPayload(\n value: unknown,\n maxOutputBytes?: number\n): value is ScreenshotOutputPayload {\n if (!isRecord(value)) {\n return false;\n }\n\n return (\n hasProtocolVersion(value) &&\n typeof value.jobId === 'string' &&\n value.jobId.length > 0 &&\n (value.action === 'save' || value.action === 'copy') &&\n isImageResult(value.result, maxOutputBytes)\n );\n}\n","import type { IpcMain, IpcMainInvokeEvent } from 'electron';\n\nimport { OVERLAY_CHANNELS } from '../protocol/channels.js';\nimport type {\n ScreenshotOutputPayload,\n ScreenshotOutputResponse,\n} from '../protocol/messages.js';\nimport { isOutputPayload } from '../protocol/validators.js';\n\nexport interface ScreenshotOutputContext {\n senderWebContentsId: number;\n}\n\nexport type ScreenshotOutputHandler = (\n payload: ScreenshotOutputPayload,\n context: ScreenshotOutputContext\n) => Promise<ScreenshotOutputResponse>;\n\ntype OutputIpcMain = Pick<IpcMain, 'handle'>;\n\ninterface ActiveOutputHandler {\n jobId: string;\n handler: ScreenshotOutputHandler;\n}\n\n/**\n * ipcMain.handle 是进程级单例注册;Router 按 Overlay WebContents 分发活动任务,\n * 避免多个 ScreenshotManager 重复注册同一 channel。\n */\nexport class ScreenshotOutputRouter {\n readonly #handlers = new Map<number, ActiveOutputHandler>();\n\n constructor(ipcMain: OutputIpcMain) {\n ipcMain.handle(OVERLAY_CHANNELS.output, this.#handleOutput);\n }\n\n register(\n senderWebContentsId: number,\n jobId: string,\n handler: ScreenshotOutputHandler\n ): () => void {\n this.#handlers.set(senderWebContentsId, { jobId, handler });\n return () => {\n const current = this.#handlers.get(senderWebContentsId);\n if (current?.jobId === jobId) {\n this.#handlers.delete(senderWebContentsId);\n }\n };\n }\n\n #handleOutput = async (\n event: IpcMainInvokeEvent,\n payload: unknown\n ): Promise<ScreenshotOutputResponse> => {\n if (!isOutputPayload(payload)) {\n return {\n status: 'failed',\n code: 'INVALID_REQUEST',\n message: 'Invalid or oversized screenshot output request.',\n };\n }\n\n const active = this.#handlers.get(event.sender.id);\n if (!active || active.jobId !== payload.jobId) {\n return {\n status: 'failed',\n code: 'INVALID_REQUEST',\n message: 'Screenshot output task is not active.',\n };\n }\n\n try {\n return await active.handler(payload, {\n senderWebContentsId: event.sender.id,\n });\n } catch (error) {\n return {\n status: 'failed',\n code: 'EXPORT_FAILED',\n message: error instanceof Error ? error.message : 'Screenshot output failed.',\n };\n }\n };\n}\n\nconst routers = new WeakMap<object, ScreenshotOutputRouter>();\n\nexport function getScreenshotOutputRouter(\n ipcMain: OutputIpcMain\n): ScreenshotOutputRouter {\n const key = ipcMain as object;\n const existing = routers.get(key);\n if (existing) {\n return existing;\n }\n\n const router = new ScreenshotOutputRouter(ipcMain);\n routers.set(key, router);\n return router;\n}\n","import type { IpcMain, IpcMainEvent } from 'electron';\n\nimport type { ScreenshotOptions, ScreenshotResult } from '../../types.js';\nimport { OVERLAY_CHANNELS } from '../protocol/channels.js';\nimport {\n SCREENSHOT_PROTOCOL_VERSION,\n type CaptureDisplay,\n type CapturedFrame,\n type ScreenCaptureAdapter,\n} from '../protocol/messages.js';\nimport {\n findCapturedFrameLimitViolation,\n resolveScreenshotResourceLimits,\n type ScreenshotResourceLimitOptions,\n type ScreenshotResourceLimits,\n} from '../protocol/limits.js';\nimport {\n isCancelPayload,\n isCompletePayload,\n isErrorPayload,\n isPreparedPayload,\n isReadyPayload,\n} from '../protocol/validators.js';\nimport {\n emitScreenshotDiagnostic,\n type ScreenshotDiagnosticContextValue,\n type ScreenshotDiagnosticListener,\n type ScreenshotDiagnosticPhase,\n type ScreenshotDiagnosticStage,\n} from './diagnostics.js';\nimport { ScreenshotError, toScreenshotFailure } from './errors.js';\nimport type { ScreenshotOverlayWindow } from './overlay-window.js';\nimport { PackagedResourceError } from './resource-paths.js';\n\nexport type ScreenshotSessionState =\n | 'idle'\n | 'capturing'\n | 'opening-overlay'\n | 'preparing-overlay'\n | 'editing'\n | 'exporting'\n | 'completed'\n | 'cancelled'\n | 'failed';\n\nexport type ScreenshotOverlayFactory = (\n display: CaptureDisplay\n) => ScreenshotOverlayWindow;\n\nexport interface ScreenshotSessionOptions {\n jobId: string;\n captureOptions: ScreenshotOptions;\n captureAdapter: ScreenCaptureAdapter;\n ipcMain: Pick<IpcMain, 'on' | 'removeListener'>;\n createOverlay: ScreenshotOverlayFactory;\n overlayReadyTimeoutMs?: number;\n onSettled?: () => void;\n registerOutputHandler?: (senderWebContentsId: number, jobId: string) => () => void;\n resourceLimits?: ScreenshotResourceLimitOptions;\n onDiagnostic?: ScreenshotDiagnosticListener;\n}\n\ntype SessionResolver = (result: ScreenshotResult) => void;\n\n/**\n * 管理一次截图任务从屏幕采集到 Overlay 结算的完整生命周期。\n * 所有 IPC 都同时校验发送窗口、协议版本和 jobId,并且只允许结算一次。\n */\nexport class ScreenshotSession {\n readonly #options: ScreenshotSessionOptions;\n readonly #resourceLimits: ScreenshotResourceLimits;\n #state: ScreenshotSessionState = 'idle';\n #overlay: ScreenshotOverlayWindow | undefined;\n #frames: CapturedFrame[] = [];\n #overlayLoaded = false;\n #rendererReady = false;\n #resolve: SessionResolver | undefined;\n #settled = false;\n #readyTimer: ReturnType<typeof setTimeout> | undefined;\n #windowCleanups: Array<() => void> = [];\n readonly #diagnosticStageStarts = new Map<ScreenshotDiagnosticStage, number>();\n\n constructor(options: ScreenshotSessionOptions) {\n this.#options = options;\n this.#resourceLimits = resolveScreenshotResourceLimits(options.resourceLimits);\n }\n\n get state(): ScreenshotSessionState {\n return this.#state;\n }\n\n /** 允许主进程在宿主窗口销毁或业务主动终止时结束任意活动阶段。 */\n cancel(): boolean {\n if (this.#settled || this.#state === 'idle') {\n return false;\n }\n this.#settle({ status: 'cancelled' });\n return true;\n }\n\n run(): Promise<ScreenshotResult> {\n if (this.#state !== 'idle') {\n return Promise.resolve({\n status: 'failed',\n code: 'INVALID_RESULT',\n message: 'A screenshot session can only be run once.',\n });\n }\n\n return new Promise((resolve) => {\n this.#resolve = resolve;\n void this.#start();\n });\n }\n\n async #start(): Promise<void> {\n this.#state = 'capturing';\n this.#startDiagnosticStage('capture');\n let targetDisplay: CaptureDisplay | undefined;\n\n // Electron 默认适配器可以同步确定目标屏幕,因此隐藏 Overlay 的加载无需等待截图完成。\n // 窗口直到捕获帧解码、合成完成后才会 prime/reveal,不会进入屏幕截图。\n try {\n targetDisplay = this.#options.captureAdapter.resolveTargetDisplay?.(\n this.#options.captureOptions\n );\n if (targetDisplay) {\n this.#openOverlay(targetDisplay);\n }\n } catch {\n // capture() 会给出规范化的权限或显示器错误;预解析只用于性能优化。\n }\n\n let frames: CapturedFrame[];\n try {\n frames = targetDisplay\n ? await this.#options.captureAdapter.capture(\n this.#options.captureOptions,\n targetDisplay\n )\n : await this.#options.captureAdapter.capture(this.#options.captureOptions);\n if (this.#settled) {\n return;\n }\n if (!frames[0]) {\n throw new ScreenshotError(\n 'CAPTURE_FAILED',\n 'Screen capture returned no frames.'\n );\n }\n if (targetDisplay && !isSameDisplayGeometry(targetDisplay, frames[0].display)) {\n throw new ScreenshotError(\n 'DISPLAY_NOT_FOUND',\n 'The target display changed while the screenshot was starting. Please retry.'\n );\n }\n for (const frame of frames) {\n const violation = findCapturedFrameLimitViolation(frame, this.#resourceLimits);\n if (violation) {\n throw new ScreenshotError('RESOURCE_LIMIT_EXCEEDED', violation);\n }\n }\n this.#finishDiagnosticStage('capture', 'complete', {\n frameCount: frames.length,\n capturePixels: frames.reduce(\n (total, frame) => total + frame.pixelSize.width * frame.pixelSize.height,\n 0\n ),\n });\n } catch (error) {\n const failure = toScreenshotFailure(error);\n this.#finishDiagnosticStage('capture', 'error', diagnosticError(error), failure);\n this.#settle(failure);\n return;\n }\n\n this.#frames = frames;\n this.#state = 'opening-overlay';\n if (!this.#overlay) {\n this.#openOverlay(frames[0].display);\n }\n this.#prepareOverlayWhenReady();\n }\n\n #openOverlay(display: CaptureDisplay): void {\n if (this.#settled || this.#overlay) {\n return;\n }\n\n this.#startDiagnosticStage('overlay-create');\n try {\n this.#overlay = this.#options.createOverlay(display);\n this.#finishDiagnosticStage('overlay-create', 'complete', {\n displayId: display.id,\n overlayWebContentsId: this.#overlay.webContentsId,\n });\n } catch (error) {\n const failure = toScreenshotFailure(error, 'OVERLAY_LOAD_FAILED');\n this.#finishDiagnosticStage(\n 'overlay-create',\n 'error',\n diagnosticError(error),\n failure\n );\n this.#settle(failure);\n return;\n }\n\n this.#registerListeners();\n this.#startReadyTimeout();\n this.#startDiagnosticStage('overlay-load');\n this.#startDiagnosticStage('overlay-ready');\n void this.#loadOverlay();\n }\n\n async #loadOverlay(): Promise<void> {\n try {\n await this.#overlay?.load();\n if (this.#settled) {\n return;\n }\n this.#overlayLoaded = true;\n this.#finishDiagnosticStage('overlay-load', 'complete');\n this.#prepareOverlayWhenReady();\n } catch (error) {\n const failure = toScreenshotFailure(error, 'OVERLAY_LOAD_FAILED');\n this.#finishDiagnosticStage(\n 'overlay-load',\n 'error',\n diagnosticError(error),\n failure\n );\n this.#settle(failure);\n }\n }\n\n #registerListeners(): void {\n this.#options.ipcMain.on(OVERLAY_CHANNELS.ready, this.#handleReady);\n this.#options.ipcMain.on(OVERLAY_CHANNELS.prepared, this.#handlePrepared);\n this.#options.ipcMain.on(OVERLAY_CHANNELS.confirm, this.#handleConfirm);\n this.#options.ipcMain.on(OVERLAY_CHANNELS.cancel, this.#handleCancel);\n this.#options.ipcMain.on(OVERLAY_CHANNELS.error, this.#handleError);\n\n if (this.#overlay) {\n const outputCleanup = this.#options.registerOutputHandler?.(\n this.#overlay.webContentsId,\n this.#options.jobId\n );\n if (outputCleanup) {\n this.#windowCleanups.push(outputCleanup);\n }\n this.#windowCleanups.push(\n this.#overlay.onClosed(() => {\n if (!this.#settled) {\n this.#settle({ status: 'cancelled' });\n }\n }),\n this.#overlay.onRendererGone(() => {\n if (!this.#settled) {\n this.#settle({\n status: 'failed',\n code: 'OVERLAY_LOAD_FAILED',\n message: 'The screenshot overlay renderer exited unexpectedly.',\n });\n }\n })\n );\n }\n }\n\n #handleReady = (event: IpcMainEvent, payload: unknown): void => {\n if (!this.#isExpectedSender(event)) {\n return;\n }\n\n if (!isReadyPayload(payload)) {\n this.#settleInvalidMessage('Invalid overlay ready message.');\n return;\n }\n\n if (\n (this.#state !== 'capturing' && this.#state !== 'opening-overlay') ||\n !this.#overlay ||\n this.#rendererReady\n ) {\n return;\n }\n\n this.#clearReadyTimeout();\n this.#finishDiagnosticStage('overlay-ready', 'complete');\n this.#rendererReady = true;\n this.#prepareOverlayWhenReady();\n };\n\n #prepareOverlayWhenReady(): void {\n if (\n this.#settled ||\n this.#state !== 'opening-overlay' ||\n !this.#overlay ||\n !this.#overlayLoaded ||\n !this.#rendererReady ||\n !this.#frames[0]\n ) {\n return;\n }\n\n this.#state = 'preparing-overlay';\n this.#startDiagnosticStage('overlay-prepare');\n this.#overlay.prime();\n this.#overlay.sendInitialize({\n protocolVersion: SCREENSHOT_PROTOCOL_VERSION,\n jobId: this.#options.jobId,\n options: this.#options.captureOptions,\n frames: this.#frames,\n });\n this.#frames = [];\n this.#startReadyTimeout(\n 'The screenshot overlay did not prepare its captured frame in time.'\n );\n }\n\n #handlePrepared = (event: IpcMainEvent, payload: unknown): void => {\n if (!this.#isExpectedSender(event)) {\n return;\n }\n\n if (!isPreparedPayload(payload, this.#options.jobId)) {\n this.#settleInvalidMessage('Invalid overlay prepared message.');\n return;\n }\n\n if (this.#state !== 'preparing-overlay' || !this.#overlay) {\n return;\n }\n\n this.#clearReadyTimeout();\n this.#finishDiagnosticStage('overlay-prepare', 'complete');\n this.#state = 'editing';\n this.#overlay.reveal();\n };\n\n #handleConfirm = (event: IpcMainEvent, payload: unknown): void => {\n if (!this.#isExpectedSender(event) || this.#state !== 'editing') {\n return;\n }\n\n if (\n !isCompletePayload(\n payload,\n this.#options.jobId,\n this.#resourceLimits.maxOutputBytes\n )\n ) {\n this.#settleInvalidMessage('Invalid screenshot completion message.');\n return;\n }\n\n this.#state = 'exporting';\n this.#settle(payload.result);\n };\n\n #handleCancel = (event: IpcMainEvent, payload: unknown): void => {\n if (\n !this.#isExpectedSender(event) ||\n (this.#state !== 'preparing-overlay' && this.#state !== 'editing')\n ) {\n return;\n }\n\n if (!isCancelPayload(payload, this.#options.jobId)) {\n this.#settleInvalidMessage('Invalid screenshot cancellation message.');\n return;\n }\n\n this.#settle({ status: 'cancelled' });\n };\n\n #handleError = (event: IpcMainEvent, payload: unknown): void => {\n if (\n !this.#isExpectedSender(event) ||\n (this.#state !== 'preparing-overlay' && this.#state !== 'editing')\n ) {\n return;\n }\n\n if (!isErrorPayload(payload, this.#options.jobId)) {\n this.#settleInvalidMessage('Invalid screenshot error message.');\n return;\n }\n\n this.#settle({\n status: 'failed',\n code: payload.code,\n message: payload.message,\n });\n };\n\n #isExpectedSender(event: IpcMainEvent): boolean {\n return (\n this.#overlay !== undefined && event.sender.id === this.#overlay.webContentsId\n );\n }\n\n #settleInvalidMessage(message: string): void {\n this.#settle({\n status: 'failed',\n code: 'INVALID_RESULT',\n message,\n });\n }\n\n #startReadyTimeout(\n message = 'The screenshot overlay did not become ready in time.'\n ): void {\n this.#clearReadyTimeout();\n const timeoutMs = this.#options.overlayReadyTimeoutMs ?? 10_000;\n this.#readyTimer = setTimeout(() => {\n this.#settle({\n status: 'failed',\n code: 'OVERLAY_LOAD_FAILED',\n message,\n });\n }, timeoutMs);\n this.#readyTimer.unref?.();\n }\n\n #clearReadyTimeout(): void {\n if (this.#readyTimer) {\n clearTimeout(this.#readyTimer);\n this.#readyTimer = undefined;\n }\n }\n\n #settle(result: ScreenshotResult): void {\n if (this.#settled) {\n return;\n }\n\n this.#settled = true;\n const phase: ScreenshotDiagnosticPhase =\n result.status === 'cancelled'\n ? 'cancel'\n : result.status === 'failed'\n ? 'error'\n : 'complete';\n for (const stage of [...this.#diagnosticStageStarts.keys()]) {\n this.#finishDiagnosticStage(stage, phase, undefined, result);\n }\n this.#state =\n result.status === 'completed'\n ? 'completed'\n : result.status === 'cancelled'\n ? 'cancelled'\n : 'failed';\n\n this.#clearReadyTimeout();\n this.#removeListeners();\n this.#frames = [];\n if (\n result.status === 'completed' &&\n result.output.action === 'copy' &&\n this.#overlay?.showCopyFeedback\n ) {\n this.#overlay.showCopyFeedback(3_000, this.#options.captureOptions);\n } else {\n this.#overlay?.destroy();\n }\n this.#options.onSettled?.();\n this.#resolve?.(result);\n this.#resolve = undefined;\n }\n\n #startDiagnosticStage(\n stage: ScreenshotDiagnosticStage,\n context?: Readonly<Record<string, ScreenshotDiagnosticContextValue>>\n ): void {\n const timestamp = Date.now();\n this.#diagnosticStageStarts.set(stage, timestamp);\n emitScreenshotDiagnostic(this.#options.onDiagnostic, {\n jobId: this.#options.jobId,\n stage,\n phase: 'start',\n timestamp,\n ...(context ? { context } : {}),\n });\n }\n\n #finishDiagnosticStage(\n stage: ScreenshotDiagnosticStage,\n phase: Exclude<ScreenshotDiagnosticPhase, 'start'>,\n context?: Readonly<Record<string, ScreenshotDiagnosticContextValue>>,\n result?: ScreenshotResult\n ): void {\n const startedAt = this.#diagnosticStageStarts.get(stage);\n if (startedAt === undefined) {\n return;\n }\n this.#diagnosticStageStarts.delete(stage);\n const timestamp = Date.now();\n emitScreenshotDiagnostic(this.#options.onDiagnostic, {\n jobId: this.#options.jobId,\n stage,\n phase,\n timestamp,\n durationMs: Math.max(0, timestamp - startedAt),\n ...(result?.status === 'failed'\n ? { code: result.code, message: result.message }\n : {}),\n ...(context ? { context } : {}),\n });\n }\n\n #removeListeners(): void {\n this.#options.ipcMain.removeListener(OVERLAY_CHANNELS.ready, this.#handleReady);\n this.#options.ipcMain.removeListener(\n OVERLAY_CHANNELS.prepared,\n this.#handlePrepared\n );\n this.#options.ipcMain.removeListener(OVERLAY_CHANNELS.confirm, this.#handleConfirm);\n this.#options.ipcMain.removeListener(OVERLAY_CHANNELS.cancel, this.#handleCancel);\n this.#options.ipcMain.removeListener(OVERLAY_CHANNELS.error, this.#handleError);\n\n for (const cleanup of this.#windowCleanups.splice(0)) {\n cleanup();\n }\n }\n}\n\nfunction diagnosticError(\n error: unknown\n): Readonly<Record<string, ScreenshotDiagnosticContextValue>> {\n const context: Record<string, ScreenshotDiagnosticContextValue> = {\n errorName: error instanceof Error ? error.name : typeof error,\n };\n if (error instanceof PackagedResourceError) {\n context.missingResources = error.missingResources.map(\n (resource) => `${resource.label}: ${resource.path}`\n );\n }\n return context;\n}\n\n/** 预加载窗口的屏幕几何必须与采集帧一致,否则显示会发生缩放或位置错位。 */\nfunction isSameDisplayGeometry(\n expected: CaptureDisplay,\n actual: CaptureDisplay\n): boolean {\n return (\n expected.id === actual.id &&\n expected.scaleFactor === actual.scaleFactor &&\n expected.bounds.x === actual.bounds.x &&\n expected.bounds.y === actual.bounds.y &&\n expected.bounds.width === actual.bounds.width &&\n expected.bounds.height === actual.bounds.height\n );\n}\n","import type { IpcMain, IpcMainInvokeEvent } from 'electron';\n\nimport type { ScreenshotResult } from '../../types.js';\nimport {\n DEFAULT_HOST_CANCEL_CHANNEL,\n DEFAULT_HOST_CAPTURE_CHANNEL,\n} from '../protocol/channels.js';\nimport { parseScreenshotOptions } from '../protocol/validators.js';\nimport { resolveHostPreloadPath } from './resource-paths.js';\nimport {\n ScreenshotManager,\n type ScreenshotManagerOptions,\n} from './screenshot-manager.js';\n\nexport type ValidateScreenshotIpcSender = (event: IpcMainInvokeEvent) => boolean;\n\nexport interface RegisterScreenshotIpcOptions {\n ipcMain: IpcMain;\n manager: ScreenshotManager;\n channel?: string;\n cancelChannel?: string;\n validateSender?: ValidateScreenshotIpcSender;\n}\n\nexport interface SetupElectronSnaporaOptions extends Omit<\n RegisterScreenshotIpcOptions,\n 'manager'\n> {\n managerOptions?: ScreenshotManagerOptions;\n}\n\nexport interface SetupElectronSnaporaResult {\n manager: ScreenshotManager;\n preloadPath: string;\n unregister: () => void;\n}\n\n/**\n * 创建默认截图管理器、注册宿主 IPC,并返回可直接交给 BrowserWindow 的 Preload 路径。\n * 高级宿主仍可分别使用 ScreenshotManager 和 registerScreenshotIpc。\n */\nexport function setupElectronSnapora(\n options: SetupElectronSnaporaOptions\n): SetupElectronSnaporaResult {\n const { managerOptions, ...ipcOptions } = options;\n const manager = new ScreenshotManager(managerOptions);\n\n return {\n manager,\n preloadPath: resolveHostPreloadPath(),\n unregister: registerScreenshotIpc({ ...ipcOptions, manager }),\n };\n}\n\n/** 注册宿主渲染进程调用入口,并返回可用于应用退出或热重载的清理函数。 */\nexport function registerScreenshotIpc(\n options: RegisterScreenshotIpcOptions\n): () => void {\n const channel = options.channel ?? DEFAULT_HOST_CAPTURE_CHANNEL;\n const cancelChannel =\n options.cancelChannel ??\n (options.channel ? `${options.channel}:cancel` : DEFAULT_HOST_CANCEL_CHANNEL);\n\n options.ipcMain.handle(channel, async (event, captureOptions: unknown) => {\n if (!isAuthorizedSender(event, options.validateSender)) {\n return invalidRequest('The screenshot request sender is not authorized.');\n }\n\n const parsed = parseScreenshotOptions(captureOptions);\n if (!parsed.success) {\n return invalidRequest(parsed.message);\n }\n\n const senderWebContentsId = event.sender.id;\n const cancelWhenDestroyed = (): void => {\n options.manager.cancel(senderWebContentsId);\n };\n event.sender.once('destroyed', cancelWhenDestroyed);\n try {\n return await options.manager.capture(parsed.value, { senderWebContentsId });\n } finally {\n event.sender.removeListener('destroyed', cancelWhenDestroyed);\n }\n });\n\n options.ipcMain.handle(cancelChannel, async (event) => {\n if (!isAuthorizedSender(event, options.validateSender)) {\n return false;\n }\n return options.manager.cancel(event.sender.id);\n });\n\n return () => {\n options.ipcMain.removeHandler(channel);\n options.ipcMain.removeHandler(cancelChannel);\n };\n}\n\n/** iframe 永远不能授权;默认仅允许 loadFile() 创建的本地顶层页面。 */\nfunction isAuthorizedSender(\n event: IpcMainInvokeEvent,\n validateSender: ValidateScreenshotIpcSender | undefined\n): boolean {\n if (!event.senderFrame || event.senderFrame !== event.sender.mainFrame) {\n return false;\n }\n\n if (validateSender) {\n try {\n return validateSender(event);\n } catch {\n return false;\n }\n }\n\n try {\n return new URL(event.senderFrame.url).protocol === 'file:';\n } catch {\n return false;\n }\n}\n\nfunction invalidRequest(message: string): ScreenshotResult {\n return { status: 'failed', code: 'INVALID_REQUEST', message };\n}\n"],"mappings":"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","names":["import_electron","import_electron","import_electron","resolve","electronIpcMain","timestamp","resolve"]}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { BrowserWindowConstructorOptions, BrowserWindow, WebContents, IpcMain, IpcMainInvokeEvent } from 'electron';
|
|
2
|
+
import { ScreenshotErrorCode, ScreenshotOptions, ScreenshotResult } from '../types/index.cjs';
|
|
3
|
+
export { ScreenshotBounds, ScreenshotImageResult, ScreenshotLocale, ScreenshotMessageOverrides, ScreenshotMessages, ScreenshotOutputMetadata, ScreenshotTheme, ScreenshotTool } from '../types/index.cjs';
|
|
4
|
+
import { d as ScreenshotOutputPayload, e as ScreenshotOutputResponse, S as ScreenshotInitializePayload, C as CaptureDisplay, f as ScreenCaptureAdapter, g as CapturedFrame } from '../messages-ifub9bae.cjs';
|
|
5
|
+
export { h as SCREENSHOT_PROTOCOL_VERSION, i as ScreenshotCancelPayload, b as ScreenshotCompletePayload, c as ScreenshotErrorPayload, j as ScreenshotOutputAction, k as ScreenshotReadyPayload } from '../messages-ifub9bae.cjs';
|
|
6
|
+
|
|
7
|
+
type ScreenshotDiagnosticStage = 'queue' | 'session' | 'capture' | 'overlay-create' | 'overlay-load' | 'overlay-ready' | 'overlay-prepare' | 'output';
|
|
8
|
+
type ScreenshotDiagnosticPhase = 'start' | 'complete' | 'cancel' | 'error';
|
|
9
|
+
type ScreenshotDiagnosticContextValue = string | number | boolean | ReadonlyArray<string | number | boolean>;
|
|
10
|
+
interface ScreenshotDiagnosticEvent {
|
|
11
|
+
jobId: string;
|
|
12
|
+
stage: ScreenshotDiagnosticStage;
|
|
13
|
+
phase: ScreenshotDiagnosticPhase;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
durationMs?: number;
|
|
16
|
+
senderWebContentsId?: number;
|
|
17
|
+
code?: ScreenshotErrorCode;
|
|
18
|
+
message?: string;
|
|
19
|
+
context?: Readonly<Record<string, ScreenshotDiagnosticContextValue>>;
|
|
20
|
+
}
|
|
21
|
+
type ScreenshotDiagnosticListener = (event: Readonly<ScreenshotDiagnosticEvent>) => void;
|
|
22
|
+
|
|
23
|
+
interface ScreenshotOutputContext {
|
|
24
|
+
senderWebContentsId: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface ElectronOutputAdapterOptions {
|
|
28
|
+
saveFile?: (data: Uint8Array, suggestedName: string, senderWebContentsId: number) => Promise<string | undefined>;
|
|
29
|
+
copyImage?: (data: Uint8Array) => void;
|
|
30
|
+
createSuggestedName?: () => string;
|
|
31
|
+
}
|
|
32
|
+
interface ScreenshotOutputExecutor {
|
|
33
|
+
execute(payload: ScreenshotOutputPayload, context: ScreenshotOutputContext): Promise<ScreenshotOutputResponse>;
|
|
34
|
+
}
|
|
35
|
+
declare class ElectronOutputAdapter implements ScreenshotOutputExecutor {
|
|
36
|
+
#private;
|
|
37
|
+
constructor(options?: ElectronOutputAdapterOptions);
|
|
38
|
+
execute(payload: ScreenshotOutputPayload, context: ScreenshotOutputContext): Promise<ScreenshotOutputResponse>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface OverlayResources {
|
|
42
|
+
htmlPath: string;
|
|
43
|
+
preloadPath: string;
|
|
44
|
+
}
|
|
45
|
+
type PackagedResourceExists = (path: string) => boolean;
|
|
46
|
+
interface MissingPackagedResource {
|
|
47
|
+
label: string;
|
|
48
|
+
path: string;
|
|
49
|
+
}
|
|
50
|
+
declare class PackagedResourceError extends Error {
|
|
51
|
+
readonly missingResources: ReadonlyArray<MissingPackagedResource>;
|
|
52
|
+
constructor(missingResources: ReadonlyArray<MissingPackagedResource>);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* main 入口发布在 dist/main,Overlay 页面与内部 Preload 发布在 dist/overlay。
|
|
56
|
+
* 资源定位集中在这里,避免宿主构建工具或 Electron 版本参与路径拼接。
|
|
57
|
+
*/
|
|
58
|
+
declare function resolveOverlayResources(mainModuleDirectory?: string, resourceExists?: PackagedResourceExists): OverlayResources;
|
|
59
|
+
/** 返回包内已打包的默认宿主 Preload,适用于不需要合并其他 Preload 的窗口。 */
|
|
60
|
+
declare function resolveHostPreloadPath(mainModuleDirectory?: string, resourceExists?: PackagedResourceExists): string;
|
|
61
|
+
declare function assertOverlayResources(resources: OverlayResources, resourceExists?: PackagedResourceExists): void;
|
|
62
|
+
|
|
63
|
+
type OverlayBrowserWindow = Pick<BrowserWindow, 'destroy' | 'focus' | 'isDestroyed' | 'loadFile' | 'moveTop' | 'on' | 'removeListener' | 'setAlwaysOnTop' | 'setBounds' | 'setIgnoreMouseEvents' | 'setOpacity' | 'setVisibleOnAllWorkspaces' | 'show' | 'showInactive'> & {
|
|
64
|
+
webContents: Pick<WebContents, 'id' | 'on' | 'removeListener' | 'send'>;
|
|
65
|
+
};
|
|
66
|
+
type OverlayBrowserWindowFactory = (options: BrowserWindowConstructorOptions) => OverlayBrowserWindow;
|
|
67
|
+
interface OverlayWindowOptions {
|
|
68
|
+
display: CaptureDisplay;
|
|
69
|
+
resources?: OverlayResources;
|
|
70
|
+
createWindow?: OverlayBrowserWindowFactory;
|
|
71
|
+
platform?: NodeJS.Platform;
|
|
72
|
+
resourceExists?: PackagedResourceExists;
|
|
73
|
+
}
|
|
74
|
+
interface ScreenshotOverlayWindow {
|
|
75
|
+
readonly webContentsId: number;
|
|
76
|
+
load(): Promise<void>;
|
|
77
|
+
sendInitialize(payload: ScreenshotInitializePayload): void;
|
|
78
|
+
prime(): void;
|
|
79
|
+
reveal(): void;
|
|
80
|
+
showCopyFeedback?(durationMs: number, options: ScreenshotOptions): void;
|
|
81
|
+
destroy(): void;
|
|
82
|
+
onClosed(listener: () => void): () => void;
|
|
83
|
+
onRendererGone(listener: () => void): () => void;
|
|
84
|
+
}
|
|
85
|
+
/** 管理包内截图窗口,窗口始终使用隔离上下文且不向页面开放 Node.js。 */
|
|
86
|
+
declare class OverlayWindow implements ScreenshotOverlayWindow {
|
|
87
|
+
#private;
|
|
88
|
+
constructor(options: OverlayWindowOptions);
|
|
89
|
+
get webContentsId(): number;
|
|
90
|
+
load(): Promise<void>;
|
|
91
|
+
sendInitialize(payload: ScreenshotInitializePayload): void;
|
|
92
|
+
/** 先以全透明状态进入桌面合成器,隐藏 Windows/macOS 的窗口出场和大图首帧栅格化。 */
|
|
93
|
+
prime(): void;
|
|
94
|
+
reveal(): void;
|
|
95
|
+
/** 复制完成后销毁全屏截图层,改用独立的小窗口显示鼠标穿透提示。 */
|
|
96
|
+
showCopyFeedback(durationMs: number, options: ScreenshotOptions): void;
|
|
97
|
+
destroy(): void;
|
|
98
|
+
onClosed(listener: () => void): () => void;
|
|
99
|
+
onRendererGone(listener: () => void): () => void;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interface ScreenshotResourceLimits {
|
|
103
|
+
maxCapturePixels: number;
|
|
104
|
+
maxCaptureDataUrlBytes: number;
|
|
105
|
+
maxOutputBytes: number;
|
|
106
|
+
}
|
|
107
|
+
type ScreenshotResourceLimitOptions = Partial<ScreenshotResourceLimits>;
|
|
108
|
+
declare const DEFAULT_SCREENSHOT_RESOURCE_LIMITS: Readonly<ScreenshotResourceLimits>;
|
|
109
|
+
declare const HARD_SCREENSHOT_RESOURCE_LIMITS: Readonly<ScreenshotResourceLimits>;
|
|
110
|
+
declare function resolveScreenshotResourceLimits(options?: ScreenshotResourceLimitOptions): ScreenshotResourceLimits;
|
|
111
|
+
|
|
112
|
+
type ScreenshotSessionState = 'idle' | 'capturing' | 'opening-overlay' | 'preparing-overlay' | 'editing' | 'exporting' | 'completed' | 'cancelled' | 'failed';
|
|
113
|
+
type ScreenshotOverlayFactory = (display: CaptureDisplay) => ScreenshotOverlayWindow;
|
|
114
|
+
interface ScreenshotSessionOptions {
|
|
115
|
+
jobId: string;
|
|
116
|
+
captureOptions: ScreenshotOptions;
|
|
117
|
+
captureAdapter: ScreenCaptureAdapter;
|
|
118
|
+
ipcMain: Pick<IpcMain, 'on' | 'removeListener'>;
|
|
119
|
+
createOverlay: ScreenshotOverlayFactory;
|
|
120
|
+
overlayReadyTimeoutMs?: number;
|
|
121
|
+
onSettled?: () => void;
|
|
122
|
+
registerOutputHandler?: (senderWebContentsId: number, jobId: string) => () => void;
|
|
123
|
+
resourceLimits?: ScreenshotResourceLimitOptions;
|
|
124
|
+
onDiagnostic?: ScreenshotDiagnosticListener;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* 管理一次截图任务从屏幕采集到 Overlay 结算的完整生命周期。
|
|
128
|
+
* 所有 IPC 都同时校验发送窗口、协议版本和 jobId,并且只允许结算一次。
|
|
129
|
+
*/
|
|
130
|
+
declare class ScreenshotSession {
|
|
131
|
+
#private;
|
|
132
|
+
constructor(options: ScreenshotSessionOptions);
|
|
133
|
+
get state(): ScreenshotSessionState;
|
|
134
|
+
/** 允许主进程在宿主窗口销毁或业务主动终止时结束任意活动阶段。 */
|
|
135
|
+
cancel(): boolean;
|
|
136
|
+
run(): Promise<ScreenshotResult>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
interface ScreenshotJobContext {
|
|
140
|
+
senderWebContentsId?: number;
|
|
141
|
+
}
|
|
142
|
+
interface ScreenshotExecution {
|
|
143
|
+
result: Promise<ScreenshotResult>;
|
|
144
|
+
cancel(): boolean;
|
|
145
|
+
}
|
|
146
|
+
type ScreenshotBusyPolicy = 'reject' | 'queue';
|
|
147
|
+
type ScreenshotRunner = (jobId: string, options: ScreenshotOptions, context: ScreenshotJobContext) => Promise<ScreenshotResult> | ScreenshotExecution;
|
|
148
|
+
type ScreenshotManagerIpcMain = Pick<IpcMain, 'handle' | 'on' | 'removeListener'>;
|
|
149
|
+
interface ScreenshotManagerOptions {
|
|
150
|
+
/** 完全接管会话执行;设置后其余高层注入项不会参与默认 runner。 */
|
|
151
|
+
runner?: ScreenshotRunner;
|
|
152
|
+
ipcMain?: ScreenshotManagerIpcMain;
|
|
153
|
+
captureAdapter?: ScreenCaptureAdapter;
|
|
154
|
+
outputAdapter?: ScreenshotOutputExecutor;
|
|
155
|
+
createOverlay?: ScreenshotOverlayFactory;
|
|
156
|
+
overlayOptions?: Omit<OverlayWindowOptions, 'display'>;
|
|
157
|
+
overlayReadyTimeoutMs?: number;
|
|
158
|
+
resourceLimits?: ScreenshotResourceLimitOptions;
|
|
159
|
+
/** reject 保持即时失败;queue 仅为其他宿主窗口按 FIFO 排队。 */
|
|
160
|
+
busyPolicy?: ScreenshotBusyPolicy;
|
|
161
|
+
maxQueuedCaptures?: number;
|
|
162
|
+
onDiagnostic?: ScreenshotDiagnosticListener;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* 统一管理截图会话,确保同一时刻只存在一个覆盖层任务。
|
|
166
|
+
* 窗口创建、屏幕采集与导出由 runner 注入,避免核心调度逻辑绑定 Electron 版本细节。
|
|
167
|
+
*/
|
|
168
|
+
declare class ScreenshotManager {
|
|
169
|
+
#private;
|
|
170
|
+
constructor(options?: ScreenshotManagerOptions);
|
|
171
|
+
get activeJobId(): string | undefined;
|
|
172
|
+
get queuedCaptureCount(): number;
|
|
173
|
+
/** 不传 sender ID 时供主进程直接取消;传入时只允许取消该页面创建的任务。 */
|
|
174
|
+
cancel(senderWebContentsId?: number): boolean;
|
|
175
|
+
capture(options?: ScreenshotOptions, context?: ScreenshotJobContext): Promise<ScreenshotResult>;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
interface ElectronDisplayLike {
|
|
179
|
+
id: number;
|
|
180
|
+
bounds: {
|
|
181
|
+
x: number;
|
|
182
|
+
y: number;
|
|
183
|
+
width: number;
|
|
184
|
+
height: number;
|
|
185
|
+
};
|
|
186
|
+
scaleFactor: number;
|
|
187
|
+
}
|
|
188
|
+
interface ScreenApi {
|
|
189
|
+
getAllDisplays(): ElectronDisplayLike[];
|
|
190
|
+
getCursorScreenPoint(): {
|
|
191
|
+
x: number;
|
|
192
|
+
y: number;
|
|
193
|
+
};
|
|
194
|
+
getDisplayNearestPoint(point: {
|
|
195
|
+
x: number;
|
|
196
|
+
y: number;
|
|
197
|
+
}): ElectronDisplayLike;
|
|
198
|
+
getPrimaryDisplay(): ElectronDisplayLike;
|
|
199
|
+
}
|
|
200
|
+
interface DesktopCaptureSource {
|
|
201
|
+
display_id: string;
|
|
202
|
+
thumbnail: {
|
|
203
|
+
getSize(): {
|
|
204
|
+
width: number;
|
|
205
|
+
height: number;
|
|
206
|
+
};
|
|
207
|
+
isEmpty(): boolean;
|
|
208
|
+
toDataURL(): string;
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
interface DesktopCapturerApi {
|
|
212
|
+
getSources(options: {
|
|
213
|
+
types: Array<'screen'>;
|
|
214
|
+
thumbnailSize: {
|
|
215
|
+
width: number;
|
|
216
|
+
height: number;
|
|
217
|
+
};
|
|
218
|
+
fetchWindowIcons: boolean;
|
|
219
|
+
}): Promise<DesktopCaptureSource[]>;
|
|
220
|
+
}
|
|
221
|
+
type ScreenPermissionStatus = 'not-determined' | 'granted' | 'denied' | 'restricted' | 'unknown';
|
|
222
|
+
interface ElectronCaptureAdapterOptions {
|
|
223
|
+
desktopCapturer?: DesktopCapturerApi;
|
|
224
|
+
screen?: ScreenApi;
|
|
225
|
+
platform?: NodeJS.Platform;
|
|
226
|
+
getScreenPermissionStatus?: () => ScreenPermissionStatus;
|
|
227
|
+
resourceLimits?: ScreenshotResourceLimitOptions;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Electron 默认屏幕采集实现。适配器只依赖长期稳定的 Electron API,
|
|
231
|
+
* 采集结果转换为普通数据结构后再交给 Overlay,避免 UI 直接持有 NativeImage。
|
|
232
|
+
*/
|
|
233
|
+
declare class ElectronCaptureAdapter implements ScreenCaptureAdapter {
|
|
234
|
+
#private;
|
|
235
|
+
constructor(options?: ElectronCaptureAdapterOptions);
|
|
236
|
+
resolveTargetDisplay(options?: ScreenshotOptions): CaptureDisplay;
|
|
237
|
+
capture(options?: ScreenshotOptions, targetDisplay?: CaptureDisplay): Promise<CapturedFrame[]>;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
declare class ScreenshotError extends Error {
|
|
241
|
+
readonly code: ScreenshotErrorCode;
|
|
242
|
+
constructor(code: ScreenshotErrorCode, message: string, options?: ErrorOptions);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
type ValidateScreenshotIpcSender = (event: IpcMainInvokeEvent) => boolean;
|
|
246
|
+
interface RegisterScreenshotIpcOptions {
|
|
247
|
+
ipcMain: IpcMain;
|
|
248
|
+
manager: ScreenshotManager;
|
|
249
|
+
channel?: string;
|
|
250
|
+
cancelChannel?: string;
|
|
251
|
+
validateSender?: ValidateScreenshotIpcSender;
|
|
252
|
+
}
|
|
253
|
+
interface SetupElectronSnaporaOptions extends Omit<RegisterScreenshotIpcOptions, 'manager'> {
|
|
254
|
+
managerOptions?: ScreenshotManagerOptions;
|
|
255
|
+
}
|
|
256
|
+
interface SetupElectronSnaporaResult {
|
|
257
|
+
manager: ScreenshotManager;
|
|
258
|
+
preloadPath: string;
|
|
259
|
+
unregister: () => void;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* 创建默认截图管理器、注册宿主 IPC,并返回可直接交给 BrowserWindow 的 Preload 路径。
|
|
263
|
+
* 高级宿主仍可分别使用 ScreenshotManager 和 registerScreenshotIpc。
|
|
264
|
+
*/
|
|
265
|
+
declare function setupElectronSnapora(options: SetupElectronSnaporaOptions): SetupElectronSnaporaResult;
|
|
266
|
+
/** 注册宿主渲染进程调用入口,并返回可用于应用退出或热重载的清理函数。 */
|
|
267
|
+
declare function registerScreenshotIpc(options: RegisterScreenshotIpcOptions): () => void;
|
|
268
|
+
|
|
269
|
+
declare const DEFAULT_HOST_CAPTURE_CHANNEL = "electron-snapora:host:capture";
|
|
270
|
+
declare const DEFAULT_HOST_CANCEL_CHANNEL = "electron-snapora:host:cancel";
|
|
271
|
+
declare const OVERLAY_CHANNELS: {
|
|
272
|
+
readonly ready: "electron-snapora:overlay:ready";
|
|
273
|
+
readonly prepared: "electron-snapora:overlay:prepared";
|
|
274
|
+
readonly initialize: "electron-snapora:overlay:initialize";
|
|
275
|
+
readonly feedback: "electron-snapora:overlay:feedback";
|
|
276
|
+
readonly feedbackReady: "electron-snapora:overlay:feedback-ready";
|
|
277
|
+
readonly confirm: "electron-snapora:overlay:confirm";
|
|
278
|
+
readonly cancel: "electron-snapora:overlay:cancel";
|
|
279
|
+
readonly error: "electron-snapora:overlay:error";
|
|
280
|
+
readonly output: "electron-snapora:overlay:output";
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
export { CaptureDisplay, CapturedFrame, DEFAULT_HOST_CANCEL_CHANNEL, DEFAULT_HOST_CAPTURE_CHANNEL, DEFAULT_SCREENSHOT_RESOURCE_LIMITS, ElectronCaptureAdapter, type ElectronCaptureAdapterOptions, ElectronOutputAdapter, type ElectronOutputAdapterOptions, HARD_SCREENSHOT_RESOURCE_LIMITS, type MissingPackagedResource, OVERLAY_CHANNELS, type OverlayResources, OverlayWindow, type OverlayWindowOptions, PackagedResourceError, type PackagedResourceExists, type RegisterScreenshotIpcOptions, ScreenCaptureAdapter, type ScreenshotBusyPolicy, type ScreenshotDiagnosticContextValue, type ScreenshotDiagnosticEvent, type ScreenshotDiagnosticListener, type ScreenshotDiagnosticPhase, type ScreenshotDiagnosticStage, ScreenshotError, ScreenshotErrorCode, type ScreenshotExecution, ScreenshotInitializePayload, type ScreenshotJobContext, ScreenshotManager, type ScreenshotManagerIpcMain, type ScreenshotManagerOptions, ScreenshotOptions, type ScreenshotOutputExecutor, ScreenshotOutputPayload, ScreenshotOutputResponse, type ScreenshotOverlayFactory, type ScreenshotOverlayWindow, type ScreenshotResourceLimitOptions, type ScreenshotResourceLimits, ScreenshotResult, type ScreenshotRunner, ScreenshotSession, type ScreenshotSessionOptions, type ScreenshotSessionState, type SetupElectronSnaporaOptions, type SetupElectronSnaporaResult, type ValidateScreenshotIpcSender, assertOverlayResources, registerScreenshotIpc, resolveHostPreloadPath, resolveOverlayResources, resolveScreenshotResourceLimits, setupElectronSnapora };
|