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.
Files changed (50) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE +21 -0
  3. package/README.es.md +79 -0
  4. package/README.ja.md +79 -0
  5. package/README.ko.md +79 -0
  6. package/README.md +392 -0
  7. package/README.zh-CN.md +79 -0
  8. package/dist/core/index.cjs +184 -0
  9. package/dist/core/index.cjs.map +1 -0
  10. package/dist/core/index.d.cts +97 -0
  11. package/dist/core/index.d.ts +97 -0
  12. package/dist/core/index.mjs +150 -0
  13. package/dist/core/index.mjs.map +1 -0
  14. package/dist/main/index.cjs +1637 -0
  15. package/dist/main/index.cjs.map +1 -0
  16. package/dist/main/index.d.cts +283 -0
  17. package/dist/main/index.d.ts +283 -0
  18. package/dist/main/index.mjs +1599 -0
  19. package/dist/main/index.mjs.map +1 -0
  20. package/dist/messages-DYY0MpGh.d.ts +76 -0
  21. package/dist/messages-ifub9bae.d.cts +76 -0
  22. package/dist/overlay/assets/index-7dVRGn83.css +1 -0
  23. package/dist/overlay/assets/index-CJf5iC1U.js +2 -0
  24. package/dist/overlay/assets/index-CJf5iC1U.js.map +1 -0
  25. package/dist/overlay/index.html +316 -0
  26. package/dist/overlay/preload.cjs +97 -0
  27. package/dist/overlay/preload.cjs.map +1 -0
  28. package/dist/overlay/preload.d.cts +16 -0
  29. package/dist/overlay/preload.d.ts +16 -0
  30. package/dist/overlay/preload.mjs +80 -0
  31. package/dist/overlay/preload.mjs.map +1 -0
  32. package/dist/preload/auto.cjs +24 -0
  33. package/dist/preload/auto.cjs.map +1 -0
  34. package/dist/preload/auto.d.cts +2 -0
  35. package/dist/preload/auto.d.ts +2 -0
  36. package/dist/preload/auto.mjs +22 -0
  37. package/dist/preload/auto.mjs.map +1 -0
  38. package/dist/preload/index.cjs +46 -0
  39. package/dist/preload/index.cjs.map +1 -0
  40. package/dist/preload/index.d.cts +18 -0
  41. package/dist/preload/index.d.ts +18 -0
  42. package/dist/preload/index.mjs +19 -0
  43. package/dist/preload/index.mjs.map +1 -0
  44. package/dist/types/index.cjs +19 -0
  45. package/dist/types/index.cjs.map +1 -0
  46. package/dist/types/index.d.cts +92 -0
  47. package/dist/types/index.d.ts +92 -0
  48. package/dist/types/index.mjs +1 -0
  49. package/dist/types/index.mjs.map +1 -0
  50. package/package.json +158 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../node_modules/.pnpm/tsup@8.5.1_jiti@2.7.0_postc_3ddeda64d5f52f6813db2d2873a3e4a1/node_modules/tsup/assets/esm_shims.js","../../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":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\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":["webContents","BrowserWindow","BrowserWindow","resolve","timestamp","webContents","resolve"]}
@@ -0,0 +1,76 @@
1
+ import { ScreenshotOptions, ScreenshotBounds, ScreenshotResult, ScreenshotErrorCode, ScreenshotImageResult } from './types/index.js';
2
+
3
+ declare const SCREENSHOT_PROTOCOL_VERSION: 1;
4
+ interface ScreenshotReadyPayload {
5
+ protocolVersion: typeof SCREENSHOT_PROTOCOL_VERSION;
6
+ }
7
+ interface CaptureDisplay {
8
+ id: string;
9
+ bounds: ScreenshotBounds;
10
+ scaleFactor: number;
11
+ }
12
+ interface CapturedFrame {
13
+ display: CaptureDisplay;
14
+ dataUrl: string;
15
+ pixelSize: {
16
+ width: number;
17
+ height: number;
18
+ };
19
+ }
20
+ interface ScreenCaptureAdapter {
21
+ /**
22
+ * 可选的同步目标解析,用于让主进程在屏幕采集期间并行加载隐藏 Overlay。
23
+ * 返回值会作为锁定目标传给紧随其后的 capture(),避免鼠标跨屏造成截图与窗口错位。
24
+ */
25
+ resolveTargetDisplay?(options: ScreenshotOptions): CaptureDisplay;
26
+ capture(options: ScreenshotOptions, targetDisplay?: CaptureDisplay): Promise<CapturedFrame[]>;
27
+ }
28
+ interface ScreenshotInitializePayload {
29
+ protocolVersion: typeof SCREENSHOT_PROTOCOL_VERSION;
30
+ jobId: string;
31
+ options: ScreenshotOptions;
32
+ frames: CapturedFrame[];
33
+ }
34
+ interface ScreenshotFeedbackPayload {
35
+ kind: 'copy';
36
+ durationMs: number;
37
+ options: ScreenshotOptions;
38
+ }
39
+ interface ScreenshotCompletePayload {
40
+ protocolVersion: typeof SCREENSHOT_PROTOCOL_VERSION;
41
+ jobId: string;
42
+ result: ScreenshotResult;
43
+ }
44
+ interface ScreenshotCancelPayload {
45
+ protocolVersion: typeof SCREENSHOT_PROTOCOL_VERSION;
46
+ jobId: string;
47
+ }
48
+ interface ScreenshotErrorPayload {
49
+ protocolVersion: typeof SCREENSHOT_PROTOCOL_VERSION;
50
+ jobId: string;
51
+ code: ScreenshotErrorCode;
52
+ message: string;
53
+ }
54
+ type ScreenshotOutputAction = 'save' | 'copy';
55
+ interface ScreenshotOutputPayload {
56
+ protocolVersion: typeof SCREENSHOT_PROTOCOL_VERSION;
57
+ jobId: string;
58
+ action: ScreenshotOutputAction;
59
+ result: ScreenshotImageResult;
60
+ }
61
+ type ScreenshotOutputResponse = {
62
+ status: 'completed';
63
+ action: 'copy';
64
+ } | {
65
+ status: 'completed';
66
+ action: 'save';
67
+ filePath: string;
68
+ } | {
69
+ status: 'cancelled';
70
+ } | {
71
+ status: 'failed';
72
+ code: ScreenshotErrorCode;
73
+ message: string;
74
+ };
75
+
76
+ export { type CaptureDisplay as C, type ScreenshotInitializePayload as S, type ScreenshotFeedbackPayload as a, type ScreenshotCompletePayload as b, type ScreenshotErrorPayload as c, type ScreenshotOutputPayload as d, type ScreenshotOutputResponse as e, type ScreenCaptureAdapter as f, type CapturedFrame as g, SCREENSHOT_PROTOCOL_VERSION as h, type ScreenshotCancelPayload as i, type ScreenshotOutputAction as j, type ScreenshotReadyPayload as k };
@@ -0,0 +1,76 @@
1
+ import { ScreenshotOptions, ScreenshotBounds, ScreenshotResult, ScreenshotErrorCode, ScreenshotImageResult } from './types/index.cjs';
2
+
3
+ declare const SCREENSHOT_PROTOCOL_VERSION: 1;
4
+ interface ScreenshotReadyPayload {
5
+ protocolVersion: typeof SCREENSHOT_PROTOCOL_VERSION;
6
+ }
7
+ interface CaptureDisplay {
8
+ id: string;
9
+ bounds: ScreenshotBounds;
10
+ scaleFactor: number;
11
+ }
12
+ interface CapturedFrame {
13
+ display: CaptureDisplay;
14
+ dataUrl: string;
15
+ pixelSize: {
16
+ width: number;
17
+ height: number;
18
+ };
19
+ }
20
+ interface ScreenCaptureAdapter {
21
+ /**
22
+ * 可选的同步目标解析,用于让主进程在屏幕采集期间并行加载隐藏 Overlay。
23
+ * 返回值会作为锁定目标传给紧随其后的 capture(),避免鼠标跨屏造成截图与窗口错位。
24
+ */
25
+ resolveTargetDisplay?(options: ScreenshotOptions): CaptureDisplay;
26
+ capture(options: ScreenshotOptions, targetDisplay?: CaptureDisplay): Promise<CapturedFrame[]>;
27
+ }
28
+ interface ScreenshotInitializePayload {
29
+ protocolVersion: typeof SCREENSHOT_PROTOCOL_VERSION;
30
+ jobId: string;
31
+ options: ScreenshotOptions;
32
+ frames: CapturedFrame[];
33
+ }
34
+ interface ScreenshotFeedbackPayload {
35
+ kind: 'copy';
36
+ durationMs: number;
37
+ options: ScreenshotOptions;
38
+ }
39
+ interface ScreenshotCompletePayload {
40
+ protocolVersion: typeof SCREENSHOT_PROTOCOL_VERSION;
41
+ jobId: string;
42
+ result: ScreenshotResult;
43
+ }
44
+ interface ScreenshotCancelPayload {
45
+ protocolVersion: typeof SCREENSHOT_PROTOCOL_VERSION;
46
+ jobId: string;
47
+ }
48
+ interface ScreenshotErrorPayload {
49
+ protocolVersion: typeof SCREENSHOT_PROTOCOL_VERSION;
50
+ jobId: string;
51
+ code: ScreenshotErrorCode;
52
+ message: string;
53
+ }
54
+ type ScreenshotOutputAction = 'save' | 'copy';
55
+ interface ScreenshotOutputPayload {
56
+ protocolVersion: typeof SCREENSHOT_PROTOCOL_VERSION;
57
+ jobId: string;
58
+ action: ScreenshotOutputAction;
59
+ result: ScreenshotImageResult;
60
+ }
61
+ type ScreenshotOutputResponse = {
62
+ status: 'completed';
63
+ action: 'copy';
64
+ } | {
65
+ status: 'completed';
66
+ action: 'save';
67
+ filePath: string;
68
+ } | {
69
+ status: 'cancelled';
70
+ } | {
71
+ status: 'failed';
72
+ code: ScreenshotErrorCode;
73
+ message: string;
74
+ };
75
+
76
+ export { type CaptureDisplay as C, type ScreenshotInitializePayload as S, type ScreenshotFeedbackPayload as a, type ScreenshotCompletePayload as b, type ScreenshotErrorPayload as c, type ScreenshotOutputPayload as d, type ScreenshotOutputResponse as e, type ScreenCaptureAdapter as f, type CapturedFrame as g, SCREENSHOT_PROTOCOL_VERSION as h, type ScreenshotCancelPayload as i, type ScreenshotOutputAction as j, type ScreenshotReadyPayload as k };
@@ -0,0 +1 @@
1
+ :root{--snapora-blue-500: #0a84ff;--snapora-red-500: #ff453a;--snapora-amber-400: #f6bd46;--snapora-neutral-0: #fff;--snapora-neutral-900: #202226;--snapora-neutral-950: #191b1f;--snapora-color-accent: var(--snapora-blue-500);--snapora-color-on-accent: var(--snapora-neutral-0);--snapora-color-mask: rgb(0 0 0 / 48%);--snapora-color-surface: rgb(61 63 68 / 96%);--snapora-color-on-surface: rgb(255 255 255 / 92%);--snapora-color-border: rgb(255 255 255 / 12%);--snapora-color-hover: rgb(255 255 255 / 11%);--snapora-color-tooltip: rgb(25 27 31 / 98%);--snapora-color-on-tooltip: rgb(255 255 255 / 92%);--snapora-color-danger: rgb(255 69 58 / 76%);--snapora-color-warning: var(--snapora-amber-400);--snapora-color-on-warning: #211800;--snapora-color-handle: var(--snapora-neutral-0);--snapora-accent: var(--snapora-color-accent);--snapora-accent-foreground: var(--snapora-color-on-accent);--snapora-mask: var(--snapora-color-mask);--snapora-toolbar: var(--snapora-color-surface);--snapora-toolbar-foreground: var(--snapora-color-on-surface);--snapora-toolbar-hover: var(--snapora-color-hover);--snapora-toolbar-border: var(--snapora-color-border);--snapora-tooltip: var(--snapora-color-tooltip);--snapora-tooltip-foreground: var(--snapora-color-on-tooltip);--snapora-danger: var(--snapora-color-danger);--snapora-warning: var(--snapora-color-warning);--snapora-warning-foreground: var(--snapora-color-on-warning);--snapora-handle: var(--snapora-color-handle);--snapora-text: var(--snapora-toolbar-foreground);--snapora-toolbar-item-gap: 4px;--snapora-toolbar-group-padding: 3px;color-scheme:dark;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;background:transparent}:root[data-snapora-theme=light]{--snapora-color-surface: rgb(250 250 250 / 96%);--snapora-color-on-surface: rgb(24 24 27 / 88%);--snapora-color-border: rgb(24 24 27 / 14%);--snapora-color-hover: rgb(24 24 27 / 9%);--snapora-color-tooltip: rgb(39 39 42 / 98%);--snapora-color-on-tooltip: rgb(255 255 255 / 94%);--snapora-color-danger: rgb(220 38 38 / 82%);color-scheme:light}*{box-sizing:border-box}html,body,#app{width:100%;height:100%;margin:0;overflow:hidden}body{background:#000;-webkit-user-select:none;user-select:none}button,select,input{font:inherit}.capture-surface{position:relative;width:100%;height:100%;cursor:crosshair;touch-action:none}.screen-frame{display:block;width:100%;height:100%;object-fit:fill;pointer-events:none}.annotation-canvas{position:absolute;inset:0;z-index:2;width:100%;height:100%;cursor:crosshair}.capture-surface[data-tool=select] .annotation-canvas{cursor:default}.capture-surface[data-tool=text] .annotation-canvas{cursor:text}.capture-surface[data-state=ready] .annotation-canvas,.capture-surface[data-state=creating] .annotation-canvas{cursor:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='24'%20height='24'%20viewBox='0%200%2024%2024'%3e%3cg%20fill='none'%20stroke-linecap='round'%3e%3cpath%20d='M12%201.5v6M12%2016.5v6M1.5%2012h6M16.5%2012h6'%20stroke='%23111318'%20stroke-width='3.2'%20opacity='.78'%20/%3e%3cpath%20d='M12%201.5v6M12%2016.5v6M1.5%2012h6M16.5%2012h6'%20stroke='%23fff'%20stroke-width='1.3'%20/%3e%3ccircle%20cx='12'%20cy='12'%20r='4'%20fill='%23111318'%20fill-opacity='.72'%20stroke='%23fff'%20stroke-width='1.2'%20/%3e%3ccircle%20cx='12'%20cy='12'%20r='1.65'%20fill='%230a84ff'%20stroke='%23fff'%20stroke-width='.8'%20/%3e%3c/g%3e%3c/svg%3e") 12 12,crosshair}.screen-mask{position:absolute;inset:0;z-index:1;background:var(--snapora-mask);pointer-events:none}.selection{position:absolute;top:0;left:0;z-index:3;border:1px solid var(--snapora-accent);background:transparent;box-shadow:0 0 0 100vmax var(--snapora-mask),inset 0 0 0 1px #ffffff42;pointer-events:none}.selection[hidden],.selection-toolbar[hidden],.screen-mask[hidden],.status[hidden],.copy-feedback[hidden]{display:none}:root[data-snapora-feedback=copy] body{background:transparent}:root[data-snapora-feedback=copy] .capture-surface{cursor:default;pointer-events:none}:root[data-snapora-feedback=copy] .capture-surface>:not(.copy-feedback){display:none}.size-hint{position:absolute;top:-27px;left:-1px;min-width:max-content;padding:4px 7px;border:1px solid rgb(255 255 255 / 12%);border-radius:5px;color:var(--snapora-text);background:#202226eb;box-shadow:0 3px 10px #0000003d;font:500 11px/1.2 ui-monospace,Cascadia Code,monospace;font-variant-numeric:tabular-nums;pointer-events:none}.resize-handle{position:absolute;width:8px;height:8px;padding:0;border:2px solid var(--snapora-accent);border-radius:50%;background:var(--snapora-handle);box-shadow:0 1px 4px #00000070;pointer-events:auto}.capture-surface:not([data-tool=select]) .resize-handle{display:none}.handle-nw,.handle-n,.handle-ne{top:-4px}.handle-sw,.handle-s,.handle-se{bottom:-4px}.handle-nw,.handle-w,.handle-sw{left:-4px}.handle-ne,.handle-e,.handle-se{right:-4px}.handle-n,.handle-s{left:calc(50% - 4px)}.handle-e,.handle-w{top:calc(50% - 4px)}.handle-nw,.handle-se{cursor:nwse-resize}.handle-ne,.handle-sw{cursor:nesw-resize}.handle-n,.handle-s{cursor:ns-resize}.handle-e,.handle-w{cursor:ew-resize}.selection-toolbar{position:absolute;top:0;left:0;z-index:4;display:flex;flex-wrap:nowrap;align-items:stretch;gap:8px;max-width:calc(100vw - 16px);padding:0;border:0;background:transparent}.toolbar-panel{position:relative;display:flex;height:44px;padding:2px;overflow:visible;border:1px solid var(--snapora-toolbar-border);border-radius:8px;background:var(--snapora-toolbar);box-shadow:0 8px 24px #00000057,0 1px 2px #00000042;-webkit-backdrop-filter:blur(18px) saturate(120%);backdrop-filter:blur(18px) saturate(120%)}.toolbar-group{display:flex;align-items:center;gap:0}.history-group>.icon-button+.icon-button{border-left:1px solid rgb(255 255 255 / 7%)}.tool-group,.action-group{gap:var(--snapora-toolbar-item-gap);padding-right:var(--snapora-toolbar-group-padding);padding-left:var(--snapora-toolbar-group-padding)}.toolbar-divider{align-self:stretch;width:1px;margin:8px 3px;background:var(--snapora-toolbar-border)}.icon-button{position:relative;display:grid;width:38px;height:38px;padding:0;border:0;border-radius:6px;color:var(--snapora-toolbar-foreground);background:transparent;cursor:pointer;place-items:center;transition:color .1s ease,background-color .1s ease,transform 80ms ease}.toolbar-icon{width:20px;height:20px;overflow:visible;fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.8;pointer-events:none}.icon-mosaic{fill:currentColor;stroke:none}.icon-button:hover:not(:disabled){color:var(--snapora-toolbar-foreground);background:var(--snapora-toolbar-hover)}.icon-button:active:not(:disabled){transform:scale(.95)}.tool-button[data-active=true],.confirm-button{color:var(--snapora-accent-foreground);background:var(--snapora-accent)}.tool-button[data-active=true]:hover,.confirm-button:hover{background:var(--snapora-accent);filter:brightness(1.08)}.cancel-button:hover:not(:disabled){color:var(--snapora-accent-foreground);background:var(--snapora-danger)}.icon-button:disabled{cursor:default;opacity:.28}.icon-button:focus-visible,.color-input:focus-visible,.selection-toolbar select:focus-visible{outline:2px solid var(--snapora-toolbar-foreground);outline-offset:-3px}.style-group{gap:2px;padding:0 3px}.tool-group .color-control,.style-group label{position:relative;display:grid;width:36px;height:38px;cursor:pointer;place-items:center}.tool-group .color-control{width:38px}.font-control{display:none!important}.capture-surface[data-tool=text] .line-control{display:none}.capture-surface[data-tool=text] .font-control{display:grid!important}.capture-surface[data-tool=mosaic] .style-group,.capture-surface[data-tool=mosaic] .settings-panel>.toolbar-divider,.capture-surface[data-selected-type=mosaic] .style-group,.capture-surface[data-selected-type=mosaic] .settings-panel>.toolbar-divider{display:none}.color-input{width:27px;height:27px;padding:0;overflow:hidden;border:2px solid var(--snapora-toolbar-foreground);border-radius:50%;background:transparent;cursor:pointer}.color-input::-webkit-color-swatch-wrapper{padding:0}.color-input::-webkit-color-swatch{border:0;border-radius:50%}.selection-toolbar select{width:34px;height:28px;padding:0;appearance:none;border:1px solid rgb(255 255 255 / 13%);border-radius:6px;color:var(--snapora-text);background:#0000002e;font-size:11px;font-variant-numeric:tabular-nums;text-align:center;cursor:pointer}.selection-toolbar option{color:#fff;background:#313338}[data-tooltip]:before,[data-tooltip]:after{position:absolute;left:50%;z-index:20;opacity:0;pointer-events:none;transform:translate(-50%,4px);transition:opacity .1s ease,transform .1s ease;transition-delay:0ms}[data-tooltip]:before{bottom:calc(100% + 4px);width:0;height:0;border:5px solid transparent;border-top-color:var(--snapora-tooltip);content:""}[data-tooltip]:after{bottom:calc(100% + 14px);min-width:max-content;padding:7px 9px;border:1px solid rgb(255 255 255 / 10%);border-radius:7px;color:var(--snapora-tooltip-foreground);background:var(--snapora-tooltip);box-shadow:0 6px 18px #00000057;content:attr(data-tooltip);font-size:12px;font-weight:500;line-height:1;white-space:nowrap}[data-tooltip][data-shortcut]:after{content:attr(data-tooltip) " · " attr(data-shortcut)}[data-tooltip]:hover:before,[data-tooltip]:hover:after,[data-tooltip]:focus-visible:before,[data-tooltip]:focus-visible:after,[data-tooltip]:focus-within:before,[data-tooltip]:focus-within:after{opacity:1;transform:translate(-50%);transition-delay:.35s}[data-tooltip]:disabled:hover:before,[data-tooltip]:disabled:hover:after{opacity:0}.selection-toolbar[data-placement=above] [data-tooltip]:before,.selection-toolbar[data-placement=above] [data-tooltip]:after{top:auto;bottom:auto;transform:translate(-50%,-4px)}.selection-toolbar[data-placement=above] [data-tooltip]:before{top:calc(100% + 4px);border-top-color:transparent;border-bottom-color:var(--snapora-tooltip)}.selection-toolbar[data-placement=above] [data-tooltip]:after{top:calc(100% + 14px)}.selection-toolbar[data-placement=above] [data-tooltip]:hover:before,.selection-toolbar[data-placement=above] [data-tooltip]:hover:after,.selection-toolbar[data-placement=above] [data-tooltip]:focus-visible:before,.selection-toolbar[data-placement=above] [data-tooltip]:focus-visible:after,.selection-toolbar[data-placement=above] [data-tooltip]:focus-within:before,.selection-toolbar[data-placement=above] [data-tooltip]:focus-within:after{transform:translate(-50%)}.text-editor{position:absolute;top:0;left:0;z-index:5;width:260px;min-height:42px;padding:8px 10px;overflow:hidden;resize:none;border:1px solid rgb(255 255 255 / 90%);border-radius:6px;outline:none;color:#fff;background:transparent;box-shadow:0 1px 3px #00000073;font-family:system-ui,sans-serif;font-weight:600;line-height:1.3;caret-color:#fff;-webkit-user-select:text;user-select:text}.text-editor[hidden]{display:none}.status{position:absolute;top:18px;left:50%;display:flex;align-items:center;gap:8px;margin:0;padding:8px 11px;border:1px solid rgb(255 255 255 / 12%);border-radius:8px;color:#ffffffd1;background:#202226e5;box-shadow:0 8px 24px #00000047;-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);font-size:12px;line-height:1;transform:translate(-50%)}.capture-surface[data-state=waiting] .status{top:50%;transform:translate(-50%,-50%)}.capture-surface[data-state=exporting] .status:before{width:11px;height:11px;border:2px solid rgb(255 255 255 / 28%);border-top-color:#fff;border-radius:50%;content:"";animation:snapora-spin .7s linear infinite}.copy-feedback{position:fixed;top:8px;left:50%;z-index:20;display:flex;width:max-content;max-width:calc(100vw - 16px);min-height:48px;align-items:center;gap:10px;padding:10px 16px 10px 10px;border:1px solid var(--snapora-warning);border-radius:12px;color:var(--snapora-text);background:var(--snapora-toolbar);box-shadow:0 12px 36px #00000057;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);font-size:14px;font-weight:600;line-height:20px;white-space:nowrap;transform:translate(-50%);animation:snapora-feedback-in .18s ease-out;pointer-events:none}.copy-feedback-text{white-space:nowrap}.copy-feedback-icon{display:grid;width:22px;height:22px;flex:0 0 22px;place-items:center;color:var(--snapora-warning);background:transparent}.copy-feedback-check{width:20px;height:20px;fill:currentColor;stroke:none}@keyframes snapora-feedback-in{0%{opacity:0;transform:translate(-50%,-8px) scale(.98)}to{opacity:1;transform:translate(-50%)}}.icon-sprite{position:fixed;width:0;height:0;overflow:hidden}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@keyframes snapora-spin{to{transform:rotate(360deg)}}@media(max-width:800px){.selection-toolbar{gap:5px}.toolbar-panel{height:40px;padding:1px}.icon-button{width:35px;height:36px}.toolbar-icon{width:18px;height:18px}.style-group label{width:33px;height:36px}.tool-group .color-control{width:35px;height:36px}}@media(prefers-reduced-motion:reduce){.icon-button,[data-tooltip]:before,[data-tooltip]:after{transition:none}.capture-surface[data-state=exporting] .status:before{animation:none}.copy-feedback{animation:none}}
@@ -0,0 +1,2 @@
1
+ (function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))o(i);new MutationObserver(i=>{for(const r of i)if(r.type==="childList")for(const a of r.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&o(a)}).observe(document,{childList:!0,subtree:!0});function n(i){const r={};return i.integrity&&(r.integrity=i.integrity),i.referrerPolicy&&(r.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?r.credentials="include":i.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function o(i){if(i.ep)return;i.ep=!0;const r=n(i);fetch(i.href,r)}})();function Wt(t,e){return{x:Math.min(t.x,e.x),y:Math.min(t.y,e.y),width:Math.abs(e.x-t.x),height:Math.abs(e.y-t.y)}}function st(t,e=1){return t.width>=e&&t.height>=e}function zt(t,e,n){if(e.width<=0||e.height<=0)throw new RangeError("Viewport dimensions must be greater than zero.");return{x:t.x*(n.width/e.width),y:t.y*(n.height/e.height)}}const Z=1.3;function Rt(t,e,n,o){const i=e.ascent+e.descent,r=Math.max(0,o-i)/2;return{x:t.x+n.x,y:t.y+n.y+r+e.ascent}}function Ft(t,e){const n=t.font;t.font=Q(e);try{const o=t.measureText("Mg国");return{ascent:ct(o.fontBoundingBoxAscent,o.actualBoundingBoxAscent,e*.8),descent:ct(o.fontBoundingBoxDescent,o.actualBoundingBoxDescent,e*.2)}}finally{t.font=n}}function ct(...t){return t.find(e=>Number.isFinite(e)&&e>0)??0}function J(t){return t.split(/\r?\n/)}function Q(t){return`600 ${t}px system-ui, sans-serif`}function Ut(t,e,n){const o=t.font;t.font=Q(n);try{const i=J(e).map(a=>t.measureText(a)),r=t.measureText("Mg国");return{width:Math.max(1,...i.map(a=>a.width)),ascent:Math.max(n*.8,r.actualBoundingBoxAscent,...i.map(a=>a.actualBoundingBoxAscent)),descent:Math.max(n*.2,r.actualBoundingBoxDescent,...i.map(a=>a.actualBoundingBoxDescent))}}finally{t.font=o}}function Ht(t,e,n,o){const i={...o,color:n.color};switch(t){case"rectangle":return{...i,type:"rectangle",bounds:{...e,width:0,height:0},lineWidth:n.lineWidth};case"ellipse":return{...i,type:"ellipse",bounds:{...e,width:0,height:0},lineWidth:n.lineWidth};case"arrow":return{...i,type:"arrow",start:e,end:e,lineWidth:n.lineWidth};case"brush":return{...i,type:"brush",points:[e],lineWidth:n.lineWidth};case"mosaic":return{...i,type:"mosaic",bounds:{...e,width:0,height:0}}}}function Nt(t,e,n){switch(t.type){case"rectangle":case"ellipse":case"mosaic":return{...t,bounds:Wt(e,n)};case"arrow":return{...t,end:n};case"brush":return Xt(t.points,n)?{...t,points:[...t.points,n]}:t}}function _t(t){switch(t.type){case"rectangle":case"ellipse":case"mosaic":return t.bounds.width>=2&&t.bounds.height>=2;case"arrow":return C(t.start,t.end)>=2;case"brush":return t.points.length>=2;case"text":return t.value.trim().length>0}}function E(t){switch(t.type){case"rectangle":case"ellipse":case"mosaic":return t.bounds;case"arrow":return lt([t.start,t.end],t.lineWidth*2);case"brush":return lt(t.points,t.lineWidth);case"text":{const e=J(t.value);return{x:t.position.x,y:t.position.y-t.metrics.ascent,width:t.metrics.width,height:t.metrics.ascent+t.metrics.descent+t.fontSize*Z*Math.max(0,e.length-1)}}}}function jt(t,e){const n=e.color??t.color;switch(t.type){case"rectangle":case"ellipse":case"arrow":case"brush":{const o=e.lineWidth??t.lineWidth;return n===t.color&&o===t.lineWidth?t:{...t,color:n,lineWidth:o}}case"text":{const o=e.fontSize??t.fontSize,i=o/t.fontSize;return n===t.color&&o===t.fontSize?t:{...t,color:n,fontSize:o,metrics:ft(t.metrics,i)}}case"mosaic":return t}}function $t(t,e,n){return[...t].sort((o,i)=>i.zIndex-o.zIndex).find(o=>Vt(o,e,n))}function Gt(t,e,n){const o=ht(E(t));return Object.entries(o).find(([,i])=>C(i,e)<=n)?.[0]}function ht(t){const e=t.x+t.width/2,n=t.y+t.height/2,o=t.x+t.width,i=t.y+t.height;return{nw:{x:t.x,y:t.y},n:{x:e,y:t.y},ne:{x:o,y:t.y},e:{x:o,y:n},se:{x:o,y:i},s:{x:e,y:i},sw:{x:t.x,y:i},w:{x:t.x,y:n}}}function Kt(t,e,n){const o=E(t),i=n.x-o.x,r=n.x+n.width-(o.x+o.width),a=n.y-o.y,s=n.y+n.height-(o.y+o.height),l={x:Math.min(Math.max(e.x,i),r),y:Math.min(Math.max(e.y,a),s)};switch(t.type){case"rectangle":case"ellipse":case"mosaic":return{...t,bounds:{...t.bounds,x:t.bounds.x+l.x,y:t.bounds.y+l.y}};case"arrow":return{...t,start:R(t.start,l),end:R(t.end,l)};case"brush":return{...t,points:t.points.map(d=>R(d,l))};case"text":return{...t,position:R(t.position,l)}}}function qt(t,e){const n=E(t),o=i=>({x:e.x+(i.x-n.x)/Math.max(n.width,1)*e.width,y:e.y+(i.y-n.y)/Math.max(n.height,1)*e.height});switch(t.type){case"rectangle":case"ellipse":case"mosaic":return{...t,bounds:e};case"arrow":return{...t,start:o(t.start),end:o(t.end)};case"brush":return{...t,points:t.points.map(o)};case"text":{const i=e.height/Math.max(n.height,1),r=Math.max(8,t.fontSize*i),a=r/t.fontSize;return{...t,position:o(t.position),fontSize:r,metrics:ft(t.metrics,a)}}}}function ft(t,e){return{width:t.width*e,ascent:t.ascent*e,descent:t.descent*e}}function Vt(t,e,n){if(t.type==="arrow"||t.type==="brush"){const i=t.type==="arrow"?[t.start,t.end]:t.points;return i.some((r,a)=>{const s=i[a+1];return s?Yt(e,r,s)<=n:C(e,r)<=n})}const o=E(t);return e.x>=o.x-n&&e.x<=o.x+o.width+n&&e.y>=o.y-n&&e.y<=o.y+o.height+n}function lt(t,e){const n=t.map(l=>l.x),o=t.map(l=>l.y),i=Math.min(...n)-e,r=Math.min(...o)-e,a=Math.max(...n)+e,s=Math.max(...o)+e;return{x:i,y:r,width:a-i,height:s-r}}function Xt(t,e){const n=t.at(-1);return!n||C(n,e)>=.75}function C(t,e){return Math.hypot(t.x-e.x,t.y-e.y)}function Yt(t,e,n){const o=(n.x-e.x)**2+(n.y-e.y)**2;if(o===0)return C(t,e);const i=Math.max(0,Math.min(1,((t.x-e.x)*(n.x-e.x)+(t.y-e.y)*(n.y-e.y))/o));return C(t,{x:e.x+i*(n.x-e.x),y:e.y+i*(n.y-e.y)})}function R(t,e){return{x:t.x+e.x,y:t.y+e.y}}const x=8;function pt(t,e,n){t.save(),n.clipBounds&&(t.beginPath(),t.rect(n.clipBounds.x,n.clipBounds.y,n.clipBounds.width,n.clipBounds.height),t.clip());for(const r of[...e].sort((a,s)=>a.zIndex-s.zIndex))Zt(t,r,n);t.restore();const o=e.find(r=>r.id===n.draftElementId);o?.type==="mosaic"&&ee(t,E(o),n.selectionHandleSize??8);const i=e.find(r=>r.id===n.selectedElementId);i&&ne(t,E(i),n.selectionHandleSize??8)}function Zt(t,e,n){switch(t.save(),t.strokeStyle=e.color,t.fillStyle=e.color,t.lineCap="round",t.lineJoin="round",e.type){case"rectangle":t.lineWidth=e.lineWidth,t.strokeRect(e.bounds.x,e.bounds.y,e.bounds.width,e.bounds.height);break;case"ellipse":t.lineWidth=e.lineWidth,t.beginPath(),t.ellipse(e.bounds.x+e.bounds.width/2,e.bounds.y+e.bounds.height/2,e.bounds.width/2,e.bounds.height/2,0,0,Math.PI*2),t.stroke();break;case"arrow":Jt(t,e.start.x,e.start.y,e.end.x,e.end.y,e.lineWidth);break;case"brush":t.lineWidth=e.lineWidth,Qt(t,e.points);break;case"text":t.font=Q(e.fontSize),t.textBaseline="alphabetic",J(e.value).forEach((o,i)=>{t.fillText(o,e.position.x,e.position.y+i*e.fontSize*Z)});break;case"mosaic":n.mosaicSource&&te(t,e,n.mosaicSource,n.imageSize);break}t.restore()}function Jt(t,e,n,o,i,r){const a=Math.atan2(i-n,o-e),s=Math.max(12,r*4);t.lineWidth=r,t.beginPath(),t.moveTo(e,n),t.lineTo(o,i),t.lineTo(o-s*Math.cos(a-Math.PI/6),i-s*Math.sin(a-Math.PI/6)),t.moveTo(o,i),t.lineTo(o-s*Math.cos(a+Math.PI/6),i-s*Math.sin(a+Math.PI/6)),t.stroke()}function Qt(t,e){const n=e[0];if(n){t.beginPath(),t.moveTo(n.x,n.y);for(const o of e.slice(1))t.lineTo(o.x,o.y);t.stroke()}}function te(t,e,n,o){const i=Math.max(0,Math.floor(e.bounds.x/x)*x),r=Math.max(0,Math.floor(e.bounds.y/x)*x),a=Math.min(o.width,Math.ceil((e.bounds.x+e.bounds.width)/x)*x),s=Math.min(o.height,Math.ceil((e.bounds.y+e.bounds.height)/x)*x),l=a-i,d=s-r;if(l<=0||d<=0)return;const m=Math.max(1,Math.ceil(l/x)),w=Math.max(1,Math.ceil(d/x)),at=oe(m,w),z=at.getContext("2d");z&&(z.imageSmoothingEnabled=!0,z.imageSmoothingQuality="high",z.drawImage(n,i,r,l,d,0,0,m,w),t.save(),t.beginPath(),t.rect(e.bounds.x,e.bounds.y,e.bounds.width,e.bounds.height),t.clip(),t.imageSmoothingEnabled=!1,t.drawImage(at,0,0,m,w,i,r,l,d),t.restore())}function ee(t,e,n){t.save(),t.fillStyle="rgba(10, 132, 255, 0.24)",t.fillRect(e.x,e.y,e.width,e.height),t.strokeStyle="rgba(0, 0, 0, 0.78)",t.lineWidth=Math.max(3.5,n/2),t.setLineDash([]),t.strokeRect(e.x,e.y,e.width,e.height),t.strokeStyle="#53b5ff",t.lineWidth=Math.max(1.5,n/5),t.setLineDash([n*.75,n*.5]),t.strokeRect(e.x,e.y,e.width,e.height),t.setLineDash([]),t.restore()}function ne(t,e,n){t.save(),t.strokeStyle="#ffffff",t.fillStyle="#35a7ff",t.lineWidth=Math.max(1,n/8),t.setLineDash([n,n/2]),t.strokeRect(e.x,e.y,e.width,e.height),t.setLineDash([]);for(const o of Object.values(ht(e)))t.fillRect(o.x-n/2,o.y-n/2,n,n),t.strokeRect(o.x-n/2,o.y-n/2,n,n);t.restore()}function oe(t,e){if(typeof OffscreenCanvas<"u")return new OffscreenCanvas(t,e);const n=document.createElement("canvas");return n.width=t,n.height=e,n}class ie{#t=[];#e=[];execute(e,n){const o=e.execute(n);return this.#t.push(e),this.#e.length=0,o}undo(e){const n=this.#t.pop();if(!n)return e;const o=n.undo(e);return this.#e.push(n),o}redo(e){const n=this.#e.pop();if(!n)return e;const o=n.execute(e);return this.#t.push(n),o}clear(){this.#t.length=0,this.#e.length=0}get canUndo(){return this.#t.length>0}get canRedo(){return this.#e.length>0}}class re{#t;constructor(e){this.#t=e}execute(e){return e.elements.some(n=>n.id===this.#t.id)?e:{...e,elements:[...e.elements,this.#t]}}undo(e){return{...e,elements:e.elements.filter(n=>n.id!==this.#t.id)}}}class ae{#t;#e;constructor(e,n){if(e.id!==n.id)throw new Error("An annotation update must preserve the element id.");this.#t=e,this.#e=n}execute(e){return dt(e,this.#e)}undo(e){return dt(e,this.#t)}}class se{#t;#e;constructor(e,n){this.#t=e,this.#e=n}execute(e){return{...e,elements:e.elements.filter(n=>n.id!==this.#t.id)}}undo(e){if(e.elements.some(o=>o.id===this.#t.id))return e;const n=[...e.elements];return n.splice(Math.min(this.#e,n.length),0,this.#t),{...e,elements:n}}}function dt(t,e){let n=!1;const o=t.elements.map(i=>i.id!==e.id?i:(n=!0,e));return n?{...t,elements:o}:t}const ce={color:"#ff3b30",lineWidth:4,fontSize:24};function le(){const t=new ie,e=new Set;let n={document:null,activeTool:"select",selectedElementId:null,draft:null,preview:null,style:ce,canUndo:!1,canRedo:!1};const o=r=>{n={...n,...r},e.forEach(a=>a(n))},i=r=>{o({document:r,canUndo:t.canUndo,canRedo:t.canRedo})};return{getState:()=>n,initialize(r,a="select"){t.clear(),o({document:{selection:r,elements:[]},activeTool:a,selectedElementId:null,draft:null,preview:null,canUndo:!1,canRedo:!1})},setSelection(r){n.document&&o({document:{...n.document,selection:r}})},setTool(r){o({activeTool:r,selectedElementId:null,draft:null,preview:null})},setStyle(r){o({style:{...n.style,...r}})},setDraft(r){o({draft:r})},commitDraft(r=!0){if(!n.document||!n.draft)return;const a=n.draft;i(t.execute(new re(a),n.document)),o({draft:null,selectedElementId:r?a.id:null})},select(r){o({selectedElementId:r,preview:null})},preview(r){o({preview:r})},commitUpdate(r,a){n.document&&(i(t.execute(new ae(r,a),n.document)),o({preview:null,selectedElementId:a.id}))},deleteSelected(){if(!n.document||!n.selectedElementId)return;const r=n.document.elements.findIndex(s=>s.id===n.selectedElementId),a=n.document.elements[r];a&&(i(t.execute(new se(a,r),n.document)),o({selectedElementId:null,preview:null}))},undo(){n.document&&(i(t.undo(n.document)),o({selectedElementId:null,preview:null,draft:null}))},redo(){n.document&&(i(t.redo(n.document)),o({selectedElementId:null,preview:null,draft:null}))},subscribe(r){return e.add(r),()=>e.delete(r)}}}function yt(t){if(!t.document)return t.draft?[t.draft]:[];const e=t.preview?t.document.elements.map(n=>n.id===t.preview?.id?t.preview:n):t.document.elements;return t.draft?[...e,t.draft]:e}async function de(t,e,n=[],o=e){const i=Math.round(e.width),r=Math.round(e.height);if(i<=0||r<=0)throw new RangeError("The exported screenshot selection must not be empty.");if(typeof OffscreenCanvas<"u"){const d=new OffscreenCanvas(i,r),m=d.getContext("2d");if(!m)throw new Error("Unable to create a 2D canvas context.");ut(m,t,e,i,r,n,o);const w=await d.convertToBlob({type:"image/png"});return new Uint8Array(await w.arrayBuffer())}const a=document.createElement("canvas");a.width=i,a.height=r;const s=a.getContext("2d");if(!s)throw new Error("Unable to create a 2D canvas context.");ut(s,t,e,i,r,n,o);const l=await new Promise((d,m)=>{a.toBlob(w=>{w?d(w):m(new Error("Unable to encode the screenshot selection as PNG."))},"image/png")});return new Uint8Array(await l.arrayBuffer())}function ut(t,e,n,o,i,r,a){t.drawImage(e,n.x,n.y,n.width,n.height,0,0,o,i),r.length>0&&(t.save(),t.translate(-n.x,-n.y),pt(t,r,{clipBounds:n,imageSize:a,mosaicSource:e}),t.restore())}const ue="en-US",mt={preparing:"Preparing screenshot…",instruction:"Drag to select an area · Esc to cancel",exporting:"Exporting screenshot…",copied:"Copied to clipboard",saveCancelled:"Save cancelled",cancel:"Cancel",save:"Save",confirm:"Copy & Done",select:"Select",rectangle:"Rectangle",ellipse:"Ellipse",arrow:"Arrow",brush:"Brush",text:"Text",mosaic:"Mosaic",undo:"Undo",redo:"Redo",color:"Color",lineWidth:"Line width",fontSize:"Font size",annotationCanvas:"Screenshot annotations",selection:"Screenshot selection",actions:"Screenshot actions",annotationTools:"Annotation tools",history:"History",annotationStyle:"Annotation style",outputActions:"Output actions",annotationText:"Annotation text"},he={"en-US":mt,"zh-CN":{preparing:"正在准备截图…",instruction:"拖动选择截图区域 · Esc 取消",exporting:"正在生成截图…",copied:"已复制到剪贴板",saveCancelled:"已取消保存",cancel:"取消",save:"保存",confirm:"复制并完成",select:"选择",rectangle:"矩形",ellipse:"椭圆",arrow:"箭头",brush:"画笔",text:"文字",mosaic:"马赛克",undo:"撤销",redo:"重做",color:"颜色",lineWidth:"线宽",fontSize:"字号",annotationCanvas:"截图标注画布",selection:"截图选区",actions:"截图操作",annotationTools:"标注工具",history:"操作历史",annotationStyle:"标注样式",outputActions:"输出操作",annotationText:"标注文字"}},fe={accentColor:"--snapora-color-accent",accentForegroundColor:"--snapora-color-on-accent",maskColor:"--snapora-color-mask",toolbarBackground:"--snapora-color-surface",toolbarForeground:"--snapora-color-on-surface",toolbarBorderColor:"--snapora-color-border",toolbarHoverBackground:"--snapora-color-hover",tooltipBackground:"--snapora-color-tooltip",tooltipForeground:"--snapora-color-on-tooltip",destructiveColor:"--snapora-color-danger",warningColor:"--snapora-color-warning",warningForegroundColor:"--snapora-color-on-warning",selectionHandleColor:"--snapora-color-handle"};function pe(t){const e={};for(const[n,o]of Object.entries(fe)){const i=t?.[n];i&&(e[o]=i)}return{mode:t?.mode??"dark",tokens:e}}function gt(t=ue,e={}){return{...mt,...he[t],...e}}function U(t,e){return{x:Math.min(Math.max(t.x,e.x),e.x+e.width),y:Math.min(Math.max(t.y,e.y),e.y+e.height)}}function wt(t,e,n){const o=U(t,n),i=U(e,n);return{x:Math.min(o.x,i.x),y:Math.min(o.y,i.y),width:Math.abs(i.x-o.x),height:Math.abs(i.y-o.y)}}function ye(t,e,n){const o=n.x+Math.max(0,n.width-t.width),i=n.y+Math.max(0,n.height-t.height);return{...t,x:Math.min(Math.max(t.x+e.x,n.x),o),y:Math.min(Math.max(t.y+e.y,n.y),i)}}function xt(t,e,n,o,i){const r=U(n,o);let a=t.x,s=t.y,l=t.x+t.width,d=t.y+t.height;return e.includes("w")&&(a=Math.min(r.x,l-i),a=Math.max(a,o.x)),e.includes("e")&&(l=Math.max(r.x,a+i),l=Math.min(l,o.x+o.width)),e.includes("n")&&(s=Math.min(r.y,d-i),s=Math.max(s,o.y)),e.includes("s")&&(d=Math.max(r.y,s+i),d=Math.min(d,o.y+o.height)),{x:a,y:s,width:l-a,height:d-s}}function tt(t,e,n){K(e,"Viewport"),K(n,"Image");const o=n.width/e.width,i=n.height/e.height,r=Math.max(0,Math.floor(t.x*o)),a=Math.max(0,Math.floor(t.y*i)),s=Math.min(n.width,Math.ceil((t.x+t.width)*o)),l=Math.min(n.height,Math.ceil((t.y+t.height)*i));return{x:r,y:a,width:Math.max(0,s-r),height:Math.max(0,l-a)}}function me(t,e,n){K(e,"Viewport");const o=n.width/e.width,i=n.height/e.height;return{x:n.x+t.x*o,y:n.y+t.y*i,width:t.width*o,height:t.height*i}}function ge(t,e,n,o=10,i=8){const r=Math.max(i,e.width-n.width-i),a=Math.min(Math.max(t.x+t.width-n.width,i),r),s=t.y+t.height+o;if(s+n.height<=e.height-i)return{x:a,y:s,placement:"below"};const l=t.y-n.height-o;return l>=i?{x:a,y:l,placement:"above"}:{x:a,y:Math.min(Math.max(t.y+o,i),Math.max(i,e.height-n.height-i)),placement:"inside"}}function K(t,e){if(t.width<=0||t.height<=0)throw new RangeError(`${e} dimensions must be greater than zero.`)}const q=4,we={phase:"waiting",payload:null,selection:null,interaction:null};function xe(t=we){let e=t;const n=new Set;return{getState:()=>e,dispatch(o){const i=be(e,o);i!==e&&(e=i,n.forEach(r=>r(e)))},subscribe(o){return n.add(o),()=>n.delete(o)}}}function be(t,e){switch(e.type){case"initialize":return{phase:"waiting",payload:e.payload,selection:null,interaction:null};case"image-ready":return t.payload?{...t,phase:"ready"}:t;case"begin-create":return!t.payload||t.phase==="exporting"?t:{...t,phase:"creating",selection:wt(e.point,e.point,e.bounds),interaction:{kind:"create",pointerId:e.pointerId,origin:e.point}};case"begin-move":return!t.selection||t.phase==="exporting"?t:{...t,phase:"moving",interaction:{kind:"move",pointerId:e.pointerId,origin:e.point,initialSelection:t.selection}};case"begin-resize":return!t.selection||t.phase==="exporting"?t:{...t,phase:"resizing",interaction:{kind:"resize",pointerId:e.pointerId,handle:e.handle,initialSelection:t.selection}};case"pointer-move":return Se(t,e.pointerId,e.point,e.bounds);case"end-interaction":return!t.interaction||t.interaction.pointerId!==e.pointerId?t:{...t,phase:t.selection&&st(t.selection,q)?"selected":"ready",selection:t.selection&&st(t.selection,q)?t.selection:null,interaction:null};case"begin-export":return t.selection&&t.phase==="selected"?{...t,phase:"exporting"}:t;case"export-failed":return t.selection?{...t,phase:"selected"}:t}}function Se(t,e,n,o){const i=t.interaction;return!i||i.pointerId!==e?t:i.kind==="create"?{...t,selection:wt(i.origin,n,o)}:i.kind==="move"?{...t,selection:ye(i.initialSelection,{x:n.x-i.origin.x,y:n.y-i.origin.y},o)}:{...t,selection:xt(i.initialSelection,i.handle,n,o,q)}}function u(t){const e=document.querySelector(t);if(!e)throw new Error(`Electron Snapora overlay element is missing: ${t}`);return e}function Ee(t){const e=t.getContext("2d");if(!e)throw new Error("Electron Snapora could not create the annotation canvas.");return e}const y=u(".capture-surface"),k=u(".status"),ve=u(".copy-feedback"),Me=u(".copy-feedback-text"),M=u(".screen-frame"),Ie=u(".screen-mask"),p=u(".annotation-canvas"),H=Ee(p),N=u(".selection"),Ce=u(".size-hint"),S=u(".selection-toolbar"),f=u(".text-editor"),bt=u(".cancel-button"),St=u(".save-button"),Et=u(".confirm-button"),et=u(".undo-button"),nt=u(".redo-button"),O=u(".color-input"),V=u(".line-width-select"),X=u(".font-size-select"),Te=u(".color-control"),ke=u(".line-control"),Pe=u(".font-control"),Le=u(".tool-group"),Ae=u(".history-group"),De=u(".style-group"),Be=u(".action-group"),W=Array.from(document.querySelectorAll("[data-tool]")),h=xe(),c=le();let g=null,_=!0,vt="select",F=null,A=null,ot=!1,D,Y=gt();const Oe=180;function We(t){const e=t.frames[0];if(!e){window.snaporaOverlay.reportError({jobId:t.jobId,code:"CAPTURE_FAILED",message:"The screenshot overlay received no captured frame."});return}vt=t.options.defaultTool??"select",h.dispatch({type:"initialize",payload:t}),Bt(t.options),Ot(t.options),qe(t.options.tools),M.onload=async()=>{p.width=e.pixelSize.width,p.height=e.pixelSize.height,h.dispatch({type:"image-ready"}),M.getBoundingClientRect(),await Lt(),window.snaporaOverlay.prepared(t.jobId)},M.onerror=()=>{window.snaporaOverlay.reportError({jobId:t.jobId,code:"CAPTURE_FAILED",message:"The captured screen image could not be loaded."})},M.src=e.dataUrl}window.snaporaOverlay.onInitialize(We);window.snaporaOverlay.onFeedback(t=>{t.kind==="copy"&&(L(),Bt(t.options),Ot(t.options),document.documentElement.dataset.snaporaFeedback="copy",y.dataset.state="copied",Me.textContent=B("copied"),ve.hidden=!1,Lt().then(()=>window.snaporaOverlay.feedbackReady()))});h.subscribe($);c.subscribe($);p.addEventListener("pointerdown",ze);p.addEventListener("pointermove",Re);p.addEventListener("pointerup",Mt);p.addEventListener("pointercancel",Mt);p.addEventListener("dblclick",Fe);N.addEventListener("pointerdown",t=>{if(t.button!==0)return;const e=t.target.dataset.handle;!e||c.getState().activeTool!=="select"||(t.stopPropagation(),y.setPointerCapture(t.pointerId),g={kind:"selection",pointerId:t.pointerId},h.dispatch({type:"begin-resize",pointerId:t.pointerId,handle:e}))});y.addEventListener("pointermove",t=>{g?.kind==="selection"&&h.dispatch({type:"pointer-move",pointerId:t.pointerId,point:G(t),bounds:it()})});y.addEventListener("pointerup",It);y.addEventListener("pointercancel",It);for(const t of W)t.addEventListener("click",()=>{const e=t.dataset.tool;c.setTool(e)});et.addEventListener("click",()=>c.undo());nt.addEventListener("click",()=>c.redo());O.addEventListener("input",()=>{c.setStyle({color:O.value})});O.addEventListener("change",()=>{rt({color:O.value})});V.addEventListener("change",()=>{const t=Number(V.value);c.setStyle({lineWidth:t});const e=T();rt({lineWidth:t*e})});X.addEventListener("change",()=>{const t=Number(X.value);c.setStyle({fontSize:t}),rt({fontSize:t*T()})});bt.addEventListener("click",kt);Et.addEventListener("click",()=>{I()});St.addEventListener("click",()=>{I("save")});f.addEventListener("keydown",t=>{t.key==="Escape"?(t.stopPropagation(),j(!1)):t.key==="Enter"&&!t.shiftKey&&(t.preventDefault(),t.stopPropagation(),j(!0))});f.addEventListener("blur",()=>j(!0));f.addEventListener("input",Tt);window.addEventListener("keydown",t=>{if(!f.hidden)return;const e=t.ctrlKey||t.metaKey;if(e&&t.key.toLowerCase()==="z"){t.preventDefault(),t.shiftKey?c.redo():c.undo();return}if(e&&t.key.toLowerCase()==="y"){t.preventDefault(),c.redo();return}if(e&&t.key.toLowerCase()==="c"){t.preventDefault(),I("copy");return}if(e&&t.key.toLowerCase()==="s"){t.preventDefault(),I("save");return}if(!e&&!t.altKey){const o={v:"select",r:"rectangle",o:"ellipse",a:"arrow",p:"brush",t:"text",m:"mosaic"}[t.key.toLowerCase()],i=W.find(r=>r.dataset.tool===o&&!r.hidden);if(o&&i){t.preventDefault(),c.setTool(o);return}}if(t.key==="Delete"||t.key==="Backspace"){t.preventDefault(),c.deleteSelected();return}t.key==="Escape"?kt():t.key==="Enter"&&(t.preventDefault(),I())});window.snaporaOverlay.ready();function ze(t){if(t.button!==0||h.getState().phase==="waiting")return;f.hidden||j(!0),t.stopPropagation(),p.setPointerCapture(t.pointerId);const e=G(t),n=h.getState().selection;if(!n||!At(n,e)){Ue(t.pointerId,e);return}const o=Pt(e),i=c.getState();i.activeTool==="select"?He(t.pointerId,o,e):i.activeTool==="text"?(t.preventDefault(),p.releasePointerCapture(t.pointerId),_e(e,o)):Ne(t.pointerId,o,i.activeTool)}function Re(t){const e=g;if(!e||e.pointerId!==t.pointerId)return;const n=G(t);if(e.kind==="selection"){h.dispatch({type:"pointer-move",pointerId:t.pointerId,point:n,bounds:it()});return}const o=c.getState().document;if(!o)return;const i=U(Pt(n),o.selection);if(e.kind==="draw"){const r=c.getState().draft;c.setDraft(Nt(r&&r.type!=="text"?r:e.element,e.origin,i))}else if(e.kind==="move-element")c.preview(Kt(e.before,{x:i.x-e.origin.x,y:i.y-e.origin.y},o.selection));else{const r=xt(E(e.before),e.handle,i,o.selection,8);c.preview(qt(e.before,r))}}function Mt(t){const e=g;if(!(!e||e.pointerId!==t.pointerId)){if(e.kind==="selection")Ct(t.pointerId);else if(e.kind==="draw"){const n=c.getState().draft;n&&_t(n)?c.commitDraft(n.type!=="mosaic"&&n.type!=="brush"):c.setDraft(null)}else{const n=c.getState().preview;n&&c.commitUpdate(e.before,n)}g=null,p.hasPointerCapture(t.pointerId)&&p.releasePointerCapture(t.pointerId)}}function Fe(t){if(t.button!==0)return;const e=h.getState();e.phase!=="selected"||!e.selection||!At(e.selection,G(t))||(t.preventDefault(),t.stopPropagation(),I("copy"))}function Ue(t,e){_=!0,g={kind:"selection",pointerId:t},h.dispatch({type:"begin-create",pointerId:t,point:e,bounds:it()})}function He(t,e,n){const o=c.getState(),i=yt(o),r=i.find(d=>d.id===o.selectedElementId),a=T()*8,s=r?Gt(r,e,a):void 0;if(r&&s){g={kind:"resize-element",pointerId:t,before:r,handle:s};return}const l=$t(i,e,a);if(l){c.select(l.id),g={kind:"move-element",pointerId:t,origin:e,before:l};return}c.select(null),h.dispatch({type:"begin-move",pointerId:t,point:n}),g={kind:"selection",pointerId:t},_=!1}function Ne(t,e,n){if(n==="text")return;const o=c.getState(),i=T(),r=Ht(n,e,{...o.style,lineWidth:o.style.lineWidth*i,fontSize:o.style.fontSize*i},{id:crypto.randomUUID(),zIndex:Dt(),createdAt:Date.now()});c.select(null),c.setDraft(r),g={kind:"draw",pointerId:t,origin:e,element:r}}function It(t){g?.kind==="selection"&&(Ct(t.pointerId),g=null,y.hasPointerCapture(t.pointerId)&&y.releasePointerCapture(t.pointerId))}function Ct(t){h.dispatch({type:"end-interaction",pointerId:t});const e=h.getState().selection,n=h.getState().payload?.frames[0];if(!e||!n)return;const o=tt(e,v(),n.pixelSize);_||!c.getState().document?c.initialize(o,vt):c.setSelection(o),_=!1}function _e(t,e){F=e,f.value="",f.hidden=!1,f.style.transform=`translate(${t.x}px, ${t.y}px)`,f.style.color=c.getState().style.color,f.style.fontSize=`${Math.max(14,c.getState().style.fontSize)}px`,Tt(),f.focus()}function Tt(){f.style.height="auto";const t=f.offsetHeight-f.clientHeight;f.style.height=`${Math.max(f.scrollHeight+t,42)}px`}function j(t){if(f.hidden)return;const e=f.value.trim();if(t&&e&&F){const n=c.getState(),o=T(),i=n.style.fontSize*o,r=Ut(H,e,i),a=Ft(H,i),s=window.getComputedStyle(f),l={x:(P(s.borderLeftWidth)+P(s.paddingLeft))*o,y:(P(s.borderTopWidth)+P(s.paddingTop))*o},d=P(s.lineHeight)*o,m=Rt(F,a,l,d||i*Z),w={id:crypto.randomUUID(),type:"text",zIndex:Dt(),createdAt:Date.now(),color:n.style.color,position:m,value:e,fontSize:i,metrics:r};c.setDraft(w),c.commitDraft(!1)}F=null,f.hidden=!0,f.value=""}function P(t){const e=Number.parseFloat(t);return Number.isFinite(e)?e:0}function kt(){const t=h.getState().payload?.jobId;t&&window.snaporaOverlay.cancel(t)}async function I(t="copy"){const e=h.getState(),n=e.payload?.frames[0];if(!(!e.payload||!n||!e.selection||e.phase!=="selected")){A=null,L(),h.dispatch({type:"begin-export"}),je();try{const o=v(),i=tt(e.selection,o,n.pixelSize),r=me(e.selection,o,n.display.bounds),a=c.getState().document?.elements??[],l={status:"completed",data:await de(M,i,a,n.pixelSize),mimeType:"image/png",bounds:r,displayId:n.display.id},d=await window.snaporaOverlay.output({jobId:e.payload.jobId,action:t,result:l});if(d.status!=="completed"){L(),h.dispatch({type:"export-failed"}),A=d.status==="cancelled"?B("saveCancelled"):d.message,$();return}const m=d.action==="save"?{action:"save",filePath:d.filePath}:{action:"copy"};L(),window.snaporaOverlay.confirm({jobId:e.payload.jobId,result:{...l,output:m}})}catch(o){L(),h.dispatch({type:"export-failed"}),window.snaporaOverlay.reportError({jobId:e.payload.jobId,code:"EXPORT_FAILED",message:o instanceof Error?o.message:"Screenshot export failed."})}}}function je(){D=window.setTimeout(()=>{D=void 0,h.getState().phase==="exporting"&&(ot=!0,$())},Oe)}function L(){D!==void 0&&(window.clearTimeout(D),D=void 0),ot=!1}function $(){const t=h.getState(),e=c.getState(),n=t.selection;y.dataset.state=t.phase,y.dataset.tool=e.activeTool;const o=e.document?.elements.find(r=>r.id===e.selectedElementId);o?y.dataset.selectedType=o.type:delete y.dataset.selectedType,Ie.hidden=n!==null,N.hidden=n===null,S.hidden=t.phase!=="selected";const i=t.phase==="waiting"||t.phase==="ready"||t.phase==="exporting"&&ot;if(k.hidden=!i&&!A,t.phase==="waiting"?k.textContent=B("preparing"):t.phase==="ready"?k.textContent=B("instruction"):t.phase==="exporting"?k.textContent=B("exporting"):A&&(k.textContent=A),n){Ke(N,n);const r=t.payload?.frames[0],a=r?tt(n,v(),r.pixelSize):n;Ce.value=`${a.width} × ${a.height}`,S.hidden||Ge(n)}for(const r of W){const a=r.dataset.tool===e.activeTool;r.dataset.active=String(a),r.setAttribute("aria-pressed",String(a))}et.disabled=!e.canUndo,nt.disabled=!e.canRedo,$e()}function $e(){H.clearRect(0,0,p.width,p.height);const t=h.getState().payload?.frames[0],e=c.getState();!t||!e.document||pt(H,yt(e),{clipBounds:e.document.selection,imageSize:t.pixelSize,mosaicSource:M,draftElementId:e.draft?.id??null,selectedElementId:e.selectedElementId,selectionHandleSize:T()*8})}function Ge(t){const e={width:S.offsetWidth,height:S.offsetHeight},n=ge(t,v(),e);S.style.transform=`translate(${n.x}px, ${n.y}px)`,S.dataset.placement=n.placement}function Ke(t,e){t.style.transform=`translate(${e.x}px, ${e.y}px)`,t.style.width=`${e.width}px`,t.style.height=`${e.height}px`}function G(t){const e=y.getBoundingClientRect();return{x:t.clientX-e.left,y:t.clientY-e.top}}function Pt(t){const e=h.getState().payload?.frames[0];return e?zt(t,v(),e.pixelSize):t}function T(){const t=h.getState().payload?.frames[0];return t?t.pixelSize.width/Math.max(v().width,1):1}function it(){return{x:0,y:0,...v()}}function v(){return{width:y.clientWidth,height:y.clientHeight}}function Lt(t=2,e=48){return new Promise(n=>{let o=!1,i=t;const r=()=>{o||(o=!0,window.clearTimeout(s),n())},a=()=>{if(i-=1,i<=0){r();return}requestAnimationFrame(a)},s=window.setTimeout(r,e);requestAnimationFrame(a)})}function At(t,e){return e.x>=t.x&&e.x<=t.x+t.width&&e.y>=t.y&&e.y<=t.y+t.height}function Dt(){return(c.getState().document?.elements??[]).reduce((e,n)=>Math.max(e,n.zIndex),-1)+1}function rt(t){const e=c.getState(),n=e.document?.elements.find(i=>i.id===e.selectedElementId);if(!n)return;const o=jt(n,t);o!==n&&c.commitUpdate(n,o)}function qe(t){const e=new Set(t??["rectangle","ellipse","arrow","brush","text","mosaic"]);for(const n of W){const o=n.dataset.tool;n.hidden=o!=="select"&&!e.has(o)}}function Bt(t){const e=pe(t.theme);document.documentElement.dataset.snaporaTheme=e.mode;for(const[n,o]of Object.entries(e.tokens))Ve(n,o)}function Ve(t,e){e&&CSS.supports("color",e)&&document.documentElement.style.setProperty(t,e)}function Ot(t){const e=t.locale??"en-US";Y=gt(e,t.messages);const n=Y,o={select:n.select,rectangle:n.rectangle,ellipse:n.ellipse,arrow:n.arrow,brush:n.brush,text:n.text,mosaic:n.mosaic};document.documentElement.lang=e;for(const i of W)b(i,o[i.dataset.tool]);b(et,n.undo),b(nt,n.redo),b(St,n.save),b(bt,n.cancel),b(Et,n.confirm),b(Te,n.color),b(ke,n.lineWidth),b(Pe,n.fontSize),p.setAttribute("aria-label",n.annotationCanvas),N.setAttribute("aria-label",n.selection),S.setAttribute("aria-label",n.actions),Le.setAttribute("aria-label",n.annotationTools),Ae.setAttribute("aria-label",n.history),De.setAttribute("aria-label",n.annotationStyle),Be.setAttribute("aria-label",n.outputActions),f.setAttribute("aria-label",n.annotationText),O.setAttribute("aria-label",n.color),V.setAttribute("aria-label",n.lineWidth),X.setAttribute("aria-label",n.fontSize)}function b(t,e){t.dataset.tooltip=e,t.setAttribute("aria-label",e)}function B(t){return Y[t]}
2
+ //# sourceMappingURL=index-CJf5iC1U.js.map