annotation-kit 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/NOTICE +75 -0
- package/README.md +114 -2
- package/README.zh-CN.md +112 -0
- package/dist/annotation-kit.css +157 -0
- package/dist/chunks/references-ukB-tSle.js +1126 -0
- package/dist/chunks/references-ukB-tSle.js.map +1 -0
- package/dist/electron/element-context.cjs +72 -0
- package/dist/electron/guest-preload.cjs +327 -0
- package/dist/electron.js +394 -0
- package/dist/electron.js.map +1 -0
- package/dist/index.js +910 -0
- package/dist/index.js.map +1 -0
- package/dist/react.js +2256 -0
- package/dist/react.js.map +1 -0
- package/dist/styles.d.ts +2 -0
- package/dist/types/address.d.ts +53 -0
- package/dist/types/address.d.ts.map +1 -0
- package/dist/types/client/Workbench.d.ts +11 -0
- package/dist/types/client/Workbench.d.ts.map +1 -0
- package/dist/types/client/composer/PacketPreview.d.ts +12 -0
- package/dist/types/client/composer/PacketPreview.d.ts.map +1 -0
- package/dist/types/client/composer/ReferenceCard.d.ts +12 -0
- package/dist/types/client/composer/ReferenceCard.d.ts.map +1 -0
- package/dist/types/client/composer/attachments.d.ts +10 -0
- package/dist/types/client/composer/attachments.d.ts.map +1 -0
- package/dist/types/client/composer/commit.d.ts +4 -0
- package/dist/types/client/composer/commit.d.ts.map +1 -0
- package/dist/types/client/composer/packets.d.ts +53 -0
- package/dist/types/client/composer/packets.d.ts.map +1 -0
- package/dist/types/client/composer/reference-presentation.d.ts +4 -0
- package/dist/types/client/composer/reference-presentation.d.ts.map +1 -0
- package/dist/types/client/error-copy.d.ts +69 -0
- package/dist/types/client/error-copy.d.ts.map +1 -0
- package/dist/types/client/errors.d.ts +10 -0
- package/dist/types/client/errors.d.ts.map +1 -0
- package/dist/types/client/files/FileSelectionAction.d.ts +7 -0
- package/dist/types/client/files/FileSelectionAction.d.ts.map +1 -0
- package/dist/types/client/files/SelectionCard.d.ts +14 -0
- package/dist/types/client/files/SelectionCard.d.ts.map +1 -0
- package/dist/types/client/files/document-workflow.d.ts +34 -0
- package/dist/types/client/files/document-workflow.d.ts.map +1 -0
- package/dist/types/client/files/pages/DocumentChrome.d.ts +30 -0
- package/dist/types/client/files/pages/DocumentChrome.d.ts.map +1 -0
- package/dist/types/client/files/pages/PageSelectionAction.d.ts +7 -0
- package/dist/types/client/files/pages/PageSelectionAction.d.ts.map +1 -0
- package/dist/types/client/files/pages/batches.d.ts +21 -0
- package/dist/types/client/files/pages/batches.d.ts.map +1 -0
- package/dist/types/client/files/pages/geometry.d.ts +16 -0
- package/dist/types/client/files/pages/geometry.d.ts.map +1 -0
- package/dist/types/client/files/pages/image-store.d.ts +8 -0
- package/dist/types/client/files/pages/image-store.d.ts.map +1 -0
- package/dist/types/client/files/pages/images.d.ts +41 -0
- package/dist/types/client/files/pages/images.d.ts.map +1 -0
- package/dist/types/client/files/pages/interaction.d.ts +55 -0
- package/dist/types/client/files/pages/interaction.d.ts.map +1 -0
- package/dist/types/client/files/pages/selection.d.ts +25 -0
- package/dist/types/client/files/pages/selection.d.ts.map +1 -0
- package/dist/types/client/files/pages/snapshot.d.ts +31 -0
- package/dist/types/client/files/pages/snapshot.d.ts.map +1 -0
- package/dist/types/client/files/references.d.ts +80 -0
- package/dist/types/client/files/references.d.ts.map +1 -0
- package/dist/types/client/files/selection.d.ts +11 -0
- package/dist/types/client/files/selection.d.ts.map +1 -0
- package/dist/types/client/files/sheets/SheetRecovery.d.ts +12 -0
- package/dist/types/client/files/sheets/SheetRecovery.d.ts.map +1 -0
- package/dist/types/client/files/sheets/SheetSurface.d.ts +27 -0
- package/dist/types/client/files/sheets/SheetSurface.d.ts.map +1 -0
- package/dist/types/client/files/sheets/geometry.d.ts +30 -0
- package/dist/types/client/files/sheets/geometry.d.ts.map +1 -0
- package/dist/types/client/files/sheets/navigation.d.ts +4 -0
- package/dist/types/client/files/sheets/navigation.d.ts.map +1 -0
- package/dist/types/client/files/sheets/snapshot.d.ts +45 -0
- package/dist/types/client/files/sheets/snapshot.d.ts.map +1 -0
- package/dist/types/client/locales.d.ts +148 -0
- package/dist/types/client/locales.d.ts.map +1 -0
- package/dist/types/client/messages/SentAnnotations.d.ts +18 -0
- package/dist/types/client/messages/SentAnnotations.d.ts.map +1 -0
- package/dist/types/client/messages/presentation.d.ts +39 -0
- package/dist/types/client/messages/presentation.d.ts.map +1 -0
- package/dist/types/client/model.d.ts +68 -0
- package/dist/types/client/model.d.ts.map +1 -0
- package/dist/types/client/quoted-context.d.ts +3 -0
- package/dist/types/client/quoted-context.d.ts.map +1 -0
- package/dist/types/client/responses/ResponseCard.d.ts +12 -0
- package/dist/types/client/responses/ResponseCard.d.ts.map +1 -0
- package/dist/types/client/responses/ResponseOverlay.d.ts +7 -0
- package/dist/types/client/responses/ResponseOverlay.d.ts.map +1 -0
- package/dist/types/client/responses/composer.d.ts +11 -0
- package/dist/types/client/responses/composer.d.ts.map +1 -0
- package/dist/types/client/responses/controller.d.ts +39 -0
- package/dist/types/client/responses/controller.d.ts.map +1 -0
- package/dist/types/client/responses/references.d.ts +62 -0
- package/dist/types/client/responses/references.d.ts.map +1 -0
- package/dist/types/client/responses/selection.d.ts +43 -0
- package/dist/types/client/responses/selection.d.ts.map +1 -0
- package/dist/types/electron/bridge.d.mts +45 -0
- package/dist/types/electron/bridge.d.mts.map +1 -0
- package/dist/types/electron/build-preload.d.mts +4 -0
- package/dist/types/electron/build-preload.d.mts.map +1 -0
- package/dist/types/electron/element-context.d.cts +7 -0
- package/dist/types/electron/element-context.d.cts.map +1 -0
- package/dist/types/electron/guest-preload.d.cts +2 -0
- package/dist/types/electron/guest-preload.d.cts.map +1 -0
- package/dist/types/electron/index.d.ts +5 -0
- package/dist/types/electron/index.d.ts.map +1 -0
- package/dist/types/host-contracts.d.ts +104 -0
- package/dist/types/host-contracts.d.ts.map +1 -0
- package/dist/types/index.d.ts +32 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/react.d.ts +14 -0
- package/dist/types/react.d.ts.map +1 -0
- package/dist/types/shared/desktop.d.ts +75 -0
- package/dist/types/shared/desktop.d.ts.map +1 -0
- package/dist/types/shared/surface.d.ts +11 -0
- package/dist/types/shared/surface.d.ts.map +1 -0
- package/dist/types/ui/icons.d.ts +11 -0
- package/dist/types/ui/icons.d.ts.map +1 -0
- package/dist/types/ui/regions.d.ts +28 -0
- package/dist/types/ui/regions.d.ts.map +1 -0
- package/package.json +82 -3
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/* Sandboxed webview preload. Only the owning Desktop main process can command it. */
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
const { ipcRenderer } = require('electron')
|
|
5
|
+
const COMMAND = 'aione-desktop:annotation-guest-command'
|
|
6
|
+
const SELECTION = 'aione-desktop:annotation-guest-selection'
|
|
7
|
+
const PAINTED = 'aione-desktop:annotation-guest-painted'
|
|
8
|
+
const reactGrabPrimitives = /* AIONE_REACT_GRAB_PRIMITIVES */ null
|
|
9
|
+
const { elementContext } = /* AIONE_ELEMENT_CONTEXT */ null
|
|
10
|
+
|
|
11
|
+
let mode = 'off'
|
|
12
|
+
let epoch = ''
|
|
13
|
+
let host
|
|
14
|
+
let layer
|
|
15
|
+
let shield
|
|
16
|
+
let dragStart
|
|
17
|
+
let preview
|
|
18
|
+
let currentTarget
|
|
19
|
+
let currentSelection
|
|
20
|
+
let dragCompleted = false
|
|
21
|
+
let frameStyle
|
|
22
|
+
const markers = new Map()
|
|
23
|
+
|
|
24
|
+
function rectOf(rect) {
|
|
25
|
+
const x = Math.max(0, rect.x)
|
|
26
|
+
const y = Math.max(0, rect.y)
|
|
27
|
+
const right = Math.min(window.innerWidth, rect.x + rect.width)
|
|
28
|
+
const bottom = Math.min(window.innerHeight, rect.y + rect.height)
|
|
29
|
+
return { x, y, width: Math.max(0, right - x), height: Math.max(0, bottom - y) }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function cssSelector(element) {
|
|
33
|
+
const parts = []
|
|
34
|
+
let node = element
|
|
35
|
+
while (node && node.nodeType === Node.ELEMENT_NODE && parts.length < 12) {
|
|
36
|
+
const tag = node.tagName.toLowerCase()
|
|
37
|
+
if (node.id) {
|
|
38
|
+
parts.unshift(`${tag}#${CSS.escape(node.id)}`)
|
|
39
|
+
const root = node.getRootNode()
|
|
40
|
+
if (root instanceof ShadowRoot) {
|
|
41
|
+
parts.unshift('>>>')
|
|
42
|
+
node = root.host
|
|
43
|
+
continue
|
|
44
|
+
}
|
|
45
|
+
break
|
|
46
|
+
}
|
|
47
|
+
let index = 1
|
|
48
|
+
for (let previous = node.previousElementSibling; previous; previous = previous.previousElementSibling) {
|
|
49
|
+
if (previous.tagName === node.tagName) index++
|
|
50
|
+
}
|
|
51
|
+
parts.unshift(`${tag}:nth-of-type(${index})`)
|
|
52
|
+
const root = node.getRootNode()
|
|
53
|
+
if (node.parentElement) node = node.parentElement
|
|
54
|
+
else if (root instanceof ShadowRoot) {
|
|
55
|
+
parts.unshift('>>>')
|
|
56
|
+
node = root.host
|
|
57
|
+
} else break
|
|
58
|
+
}
|
|
59
|
+
return parts.join(' > ').replaceAll(' > >>> > ', ' >>> ').slice(0, 2048)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function elementAt(x, y) {
|
|
63
|
+
shield.style.pointerEvents = 'none'
|
|
64
|
+
if (frameStyle?.sheet) frameStyle.sheet.disabled = true
|
|
65
|
+
let element = document.elementFromPoint(x, y)
|
|
66
|
+
try {
|
|
67
|
+
const grabbed = reactGrabPrimitives?.getElementAtPoint(x, y)
|
|
68
|
+
if (grabbed && grabbed.ownerDocument === document && grabbed !== host) element = grabbed
|
|
69
|
+
} catch { /* Sites with unusual DOMs still use native hit testing. */ }
|
|
70
|
+
shield.style.pointerEvents = 'auto'
|
|
71
|
+
if (frameStyle?.sheet) frameStyle.sheet.disabled = false
|
|
72
|
+
let offsetX = 0
|
|
73
|
+
let offsetY = 0
|
|
74
|
+
const frames = []
|
|
75
|
+
for (let depth = 0; depth < 8 && element; depth++) {
|
|
76
|
+
if (element.shadowRoot) {
|
|
77
|
+
const nested = element.shadowRoot.elementFromPoint(x - offsetX, y - offsetY)
|
|
78
|
+
if (nested && nested !== element) { element = nested; continue }
|
|
79
|
+
}
|
|
80
|
+
if (element.tagName === 'IFRAME') {
|
|
81
|
+
let frameDocument
|
|
82
|
+
const frameRect = element.getBoundingClientRect()
|
|
83
|
+
const transformed = getComputedStyle(element).transform !== 'none'
|
|
84
|
+
|| Math.abs(frameRect.width - element.offsetWidth) > 1
|
|
85
|
+
|| Math.abs(frameRect.height - element.offsetHeight) > 1
|
|
86
|
+
try { frameDocument = element.contentDocument } catch { /* Cross-origin frame: select its region. */ }
|
|
87
|
+
if (frameDocument && !transformed) {
|
|
88
|
+
const nested = frameDocument.elementFromPoint(
|
|
89
|
+
x - offsetX - frameRect.x - element.clientLeft,
|
|
90
|
+
y - offsetY - frameRect.y - element.clientTop)
|
|
91
|
+
if (nested) {
|
|
92
|
+
frames.push(cssSelector(element))
|
|
93
|
+
offsetX += frameRect.x + element.clientLeft
|
|
94
|
+
offsetY += frameRect.y + element.clientTop
|
|
95
|
+
element = nested
|
|
96
|
+
continue
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
break
|
|
101
|
+
}
|
|
102
|
+
if (!element || element === host || element === document.documentElement) return undefined
|
|
103
|
+
const bounds = element.getBoundingClientRect()
|
|
104
|
+
const rect = rectOf({ x: bounds.x + offsetX, y: bounds.y + offsetY, width: bounds.width, height: bounds.height })
|
|
105
|
+
if (rect.width < 1 || rect.height < 1) return undefined
|
|
106
|
+
let selectedSelector = cssSelector(element)
|
|
107
|
+
try {
|
|
108
|
+
if (!(element.getRootNode() instanceof ShadowRoot)) {
|
|
109
|
+
selectedSelector = reactGrabPrimitives?.getElementSelector(element) || selectedSelector
|
|
110
|
+
}
|
|
111
|
+
} catch { /* Native selector remains usable. */ }
|
|
112
|
+
return { element, selector: [...frames, selectedSelector].join(' >> ').slice(0, 2048), rect,
|
|
113
|
+
frame: frames.length ? frames.join(' >> ') : 'top document' }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function selection(kind, rect, details = {}) {
|
|
117
|
+
return {
|
|
118
|
+
kind, rect, viewport: { width: window.innerWidth, height: window.innerHeight },
|
|
119
|
+
scroll: { x: window.scrollX, y: window.scrollY },
|
|
120
|
+
url: location.href, title: document.title.slice(0, 2048), ...details,
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function emitSelection(value) {
|
|
125
|
+
currentSelection = value
|
|
126
|
+
ipcRenderer.send(SELECTION, { epoch, selection: value })
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function drawBox(box, number) {
|
|
130
|
+
const outline = document.createElement('div')
|
|
131
|
+
outline.style.cssText = 'position:absolute;box-sizing:border-box;border:2px solid #1677ff;background:rgba(22,119,255,.07);pointer-events:none;'
|
|
132
|
+
outline.style.left = `${box.x}px`
|
|
133
|
+
outline.style.top = `${box.y}px`
|
|
134
|
+
outline.style.width = `${box.width}px`
|
|
135
|
+
outline.style.height = `${box.height}px`
|
|
136
|
+
if (number !== undefined) {
|
|
137
|
+
const badge = document.createElement('span')
|
|
138
|
+
badge.textContent = String(number)
|
|
139
|
+
badge.style.cssText = 'position:absolute;left:0;top:0;padding:1px 6px;border-radius:0 0 4px 0;background:#1677ff;color:#fff;font:700 12px/18px system-ui;'
|
|
140
|
+
outline.append(badge)
|
|
141
|
+
}
|
|
142
|
+
layer.append(outline)
|
|
143
|
+
return outline
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function showPreview(rect) {
|
|
147
|
+
preview?.remove()
|
|
148
|
+
preview = rect && rect.width > 0 && rect.height > 0 ? drawBox(rect) : undefined
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function targetRect(element) {
|
|
152
|
+
if (!element?.isConnected) return undefined
|
|
153
|
+
const bounds = element.getBoundingClientRect()
|
|
154
|
+
let x = bounds.x
|
|
155
|
+
let y = bounds.y
|
|
156
|
+
let frame = element.ownerDocument.defaultView?.frameElement
|
|
157
|
+
for (let depth = 0; frame && depth < 8; depth++) {
|
|
158
|
+
const parent = frame.getBoundingClientRect()
|
|
159
|
+
if (getComputedStyle(frame).transform !== 'none'
|
|
160
|
+
|| Math.abs(parent.width - frame.offsetWidth) > 1
|
|
161
|
+
|| Math.abs(parent.height - frame.offsetHeight) > 1) return undefined
|
|
162
|
+
x += parent.x + frame.clientLeft
|
|
163
|
+
y += parent.y + frame.clientTop
|
|
164
|
+
frame = frame.ownerDocument.defaultView?.frameElement
|
|
165
|
+
}
|
|
166
|
+
return rectOf({ x, y, width: bounds.width, height: bounds.height })
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function stillCurrent(request) {
|
|
170
|
+
const selected = request.selection
|
|
171
|
+
if (!currentSelection || JSON.stringify(selected) !== JSON.stringify(currentSelection)
|
|
172
|
+
|| selected.url !== location.href
|
|
173
|
+
|| selected.viewport.width !== window.innerWidth || selected.viewport.height !== window.innerHeight
|
|
174
|
+
|| selected.scroll.x !== window.scrollX || selected.scroll.y !== window.scrollY) return false
|
|
175
|
+
if (selected.kind === 'region' && !currentTarget) return true
|
|
176
|
+
const now = targetRect(currentTarget)
|
|
177
|
+
if (selected.element && JSON.stringify(elementContext(currentTarget, selected.element.frame)) !== JSON.stringify(selected.element)) return false
|
|
178
|
+
return now && ['x', 'y', 'width', 'height'].every(key => Math.abs(now[key] - selected.rect[key]) <= 1)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function ensureLayer() {
|
|
182
|
+
if (host?.isConnected) return
|
|
183
|
+
host = document.createElement('div')
|
|
184
|
+
host.style.cssText = 'all:initial;position:fixed;inset:0;z-index:2147483647;pointer-events:none;contain:layout style paint;'
|
|
185
|
+
const root = host.attachShadow({ mode: 'closed' })
|
|
186
|
+
layer = document.createElement('div')
|
|
187
|
+
layer.style.cssText = 'position:absolute;inset:0;pointer-events:none;'
|
|
188
|
+
shield = document.createElement('div')
|
|
189
|
+
shield.style.cssText = 'position:absolute;inset:0;pointer-events:none;cursor:crosshair;touch-action:none;'
|
|
190
|
+
root.append(layer, shield)
|
|
191
|
+
document.documentElement.append(host)
|
|
192
|
+
const clearOnViewportChange = () => {
|
|
193
|
+
markers.clear()
|
|
194
|
+
layer.replaceChildren()
|
|
195
|
+
preview = undefined
|
|
196
|
+
dragStart = undefined
|
|
197
|
+
currentTarget = undefined
|
|
198
|
+
currentSelection = undefined
|
|
199
|
+
if (mode !== 'off') ipcRenderer.send(SELECTION, { epoch, selection: null })
|
|
200
|
+
}
|
|
201
|
+
window.addEventListener('scroll', clearOnViewportChange, true)
|
|
202
|
+
let viewportWidth = window.innerWidth, viewportHeight = window.innerHeight
|
|
203
|
+
window.addEventListener('resize', () => {
|
|
204
|
+
// Electron capturePage can emit resize without changing the viewport.
|
|
205
|
+
// Invalidating that notification would cancel the capture it belongs to.
|
|
206
|
+
if (viewportWidth === window.innerWidth && viewportHeight === window.innerHeight) return
|
|
207
|
+
viewportWidth = window.innerWidth
|
|
208
|
+
viewportHeight = window.innerHeight
|
|
209
|
+
clearOnViewportChange()
|
|
210
|
+
})
|
|
211
|
+
window.addEventListener('keydown', event => {
|
|
212
|
+
if (mode === 'off' || !event.isTrusted || event.isComposing || event.key !== 'Escape') return
|
|
213
|
+
event.preventDefault()
|
|
214
|
+
event.stopImmediatePropagation()
|
|
215
|
+
ipcRenderer.send(SELECTION, { epoch, escape: true })
|
|
216
|
+
}, true)
|
|
217
|
+
shield.addEventListener('pointermove', event => {
|
|
218
|
+
if (!event.isTrusted) return
|
|
219
|
+
if (mode === 'element' && !dragStart && !currentSelection) showPreview(elementAt(event.clientX, event.clientY)?.rect)
|
|
220
|
+
if (dragStart && (mode === 'region' || Math.hypot(event.clientX - dragStart.x, event.clientY - dragStart.y) > 5)) {
|
|
221
|
+
showPreview(rectOf({ x: Math.min(dragStart.x, event.clientX), y: Math.min(dragStart.y, event.clientY),
|
|
222
|
+
width: Math.abs(event.clientX - dragStart.x), height: Math.abs(event.clientY - dragStart.y) }))
|
|
223
|
+
}
|
|
224
|
+
})
|
|
225
|
+
shield.addEventListener('click', event => {
|
|
226
|
+
if (mode !== 'element' || !event.isTrusted) return
|
|
227
|
+
event.preventDefault()
|
|
228
|
+
event.stopPropagation()
|
|
229
|
+
if (dragCompleted) { dragCompleted = false; return }
|
|
230
|
+
const result = elementAt(event.clientX, event.clientY)
|
|
231
|
+
if (!result) return
|
|
232
|
+
currentTarget = result.element
|
|
233
|
+
showPreview(result.rect)
|
|
234
|
+
const fallback = result.element.tagName === 'IFRAME' || result.element.tagName === 'CANVAS'
|
|
235
|
+
|| (result.element.tagName.includes('-') && !result.element.shadowRoot)
|
|
236
|
+
if (fallback) {
|
|
237
|
+
emitSelection(selection('region', result.rect, { anchor: { x: event.clientX, y: event.clientY } }))
|
|
238
|
+
return
|
|
239
|
+
}
|
|
240
|
+
emitSelection(selection('element', result.rect, {
|
|
241
|
+
selector: result.selector,
|
|
242
|
+
text: (result.element.innerText ?? result.element.textContent ?? '').trim().slice(0, 2048),
|
|
243
|
+
element: elementContext(result.element, result.frame),
|
|
244
|
+
anchor: { x: event.clientX, y: event.clientY },
|
|
245
|
+
}))
|
|
246
|
+
}, true)
|
|
247
|
+
shield.addEventListener('pointerdown', event => {
|
|
248
|
+
if (mode === 'off' || !event.isTrusted || event.button !== 0) return
|
|
249
|
+
event.preventDefault()
|
|
250
|
+
event.stopPropagation()
|
|
251
|
+
dragStart = { x: event.clientX, y: event.clientY }
|
|
252
|
+
dragCompleted = false
|
|
253
|
+
shield.setPointerCapture(event.pointerId)
|
|
254
|
+
}, true)
|
|
255
|
+
shield.addEventListener('pointerup', event => {
|
|
256
|
+
if (mode === 'off' || !dragStart || !event.isTrusted) return
|
|
257
|
+
event.preventDefault()
|
|
258
|
+
event.stopPropagation()
|
|
259
|
+
const x = Math.min(dragStart.x, event.clientX)
|
|
260
|
+
const y = Math.min(dragStart.y, event.clientY)
|
|
261
|
+
const rect = rectOf({ x, y, width: Math.abs(event.clientX - dragStart.x), height: Math.abs(event.clientY - dragStart.y) })
|
|
262
|
+
dragStart = undefined
|
|
263
|
+
if (mode === 'element' && (rect.width < 6 || rect.height < 6)) return
|
|
264
|
+
dragCompleted = true
|
|
265
|
+
showPreview(rect)
|
|
266
|
+
currentTarget = undefined
|
|
267
|
+
if (rect.width >= 3 && rect.height >= 3) emitSelection(selection('region', rect, { anchor: { x, y } }))
|
|
268
|
+
}, true)
|
|
269
|
+
shield.addEventListener('pointercancel', () => { dragStart = undefined; showPreview(undefined) })
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function setFrameInputBlocked(blocked) {
|
|
273
|
+
if (!blocked) { frameStyle?.remove(); frameStyle = undefined; return }
|
|
274
|
+
if (frameStyle?.isConnected) return
|
|
275
|
+
frameStyle = document.createElement('style')
|
|
276
|
+
frameStyle.textContent = 'iframe { pointer-events: none !important; }'
|
|
277
|
+
document.head.append(frameStyle)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
ipcRenderer.on(COMMAND, (_event, request) => {
|
|
281
|
+
if (!request || typeof request !== 'object') return
|
|
282
|
+
if (request.kind === 'mode' && ['off', 'element', 'region'].includes(request.mode)
|
|
283
|
+
&& typeof request.epoch === 'string') {
|
|
284
|
+
if (epoch !== request.epoch) { markers.clear(); layer?.replaceChildren(); preview = undefined; currentTarget = undefined; currentSelection = undefined }
|
|
285
|
+
epoch = request.epoch
|
|
286
|
+
mode = request.mode
|
|
287
|
+
currentTarget = undefined
|
|
288
|
+
currentSelection = undefined
|
|
289
|
+
showPreview(undefined)
|
|
290
|
+
ensureLayer()
|
|
291
|
+
markers.clear()
|
|
292
|
+
layer.replaceChildren()
|
|
293
|
+
shield.style.pointerEvents = mode === 'off' ? 'none' : 'auto'
|
|
294
|
+
setFrameInputBlocked(mode !== 'off')
|
|
295
|
+
if (mode === 'off') { markers.clear(); layer.replaceChildren(); preview = undefined; currentTarget = undefined; currentSelection = undefined }
|
|
296
|
+
return
|
|
297
|
+
}
|
|
298
|
+
if (request.kind === 'verify' && request.epoch === epoch && typeof request.requestId === 'string') {
|
|
299
|
+
ipcRenderer.send(PAINTED, { requestId: request.requestId, epoch,
|
|
300
|
+
...(!stillCurrent(request) ? { error: 'selection-moved' } : {}) })
|
|
301
|
+
return
|
|
302
|
+
}
|
|
303
|
+
if (request.kind !== 'paint' || request.epoch !== epoch
|
|
304
|
+
|| !Number.isSafeInteger(request.number) || request.number < 1 || request.number > 999
|
|
305
|
+
|| typeof request.requestId !== 'string' || !request.selection?.rect) return
|
|
306
|
+
ensureLayer()
|
|
307
|
+
if (!stillCurrent(request)) {
|
|
308
|
+
ipcRenderer.send(PAINTED, { requestId: request.requestId, epoch, error: 'selection-moved' })
|
|
309
|
+
return
|
|
310
|
+
}
|
|
311
|
+
showPreview(undefined)
|
|
312
|
+
markers.get(request.number)?.remove()
|
|
313
|
+
markers.set(request.number, drawBox(request.selection.rect, request.number))
|
|
314
|
+
let acknowledged = false
|
|
315
|
+
const acknowledge = () => {
|
|
316
|
+
if (acknowledged) return
|
|
317
|
+
acknowledged = true
|
|
318
|
+
clearTimeout(fallback)
|
|
319
|
+
if (request.epoch !== epoch || !host.isConnected) return
|
|
320
|
+
ipcRenderer.send(PAINTED, { requestId: request.requestId, epoch,
|
|
321
|
+
...(!stillCurrent(request) ? { error: 'selection-moved' } : {}) })
|
|
322
|
+
}
|
|
323
|
+
// Chromium can suspend both animation frames while the Desktop window is occluded.
|
|
324
|
+
// A delayed timer still lets capturePage force a composite of the new marker.
|
|
325
|
+
const fallback = setTimeout(acknowledge, 100)
|
|
326
|
+
requestAnimationFrame(() => requestAnimationFrame(acknowledge))
|
|
327
|
+
})
|