@xulongzhe/clawbench-desktop-linux-x64-payload 0.130.0 → 0.132.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/package.json +1 -1
- package/payload.json +1 -1
- package/resources/annotate.html +335 -0
- package/resources/app.asar +0 -0
- package/resources/app.asar.unpacked/node_modules/ssh2/lib/protocol/crypto/build/Release/sshcrypto.node +0 -0
- package/resources/floating.html +37 -9
- package/resources/input-helper.exe +0 -0
package/package.json
CHANGED
package/payload.json
CHANGED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>ClawBench 标注</title>
|
|
7
|
+
<!--
|
|
8
|
+
Annotation overlay document.
|
|
9
|
+
|
|
10
|
+
Loaded by main/annotateOverlay.ts into a transparent, always-on-top window
|
|
11
|
+
that covers the shared display. It draws on a canvas over a fully
|
|
12
|
+
transparent page, so the desktop underneath stays visible.
|
|
13
|
+
|
|
14
|
+
Every stroke is ALSO reported to the main process. On Windows/macOS the
|
|
15
|
+
window is excluded from the screen capture and those frames are what the
|
|
16
|
+
audience draws from; on Linux the window is part of the picture and the
|
|
17
|
+
main process drops them (forwarding would double every stroke). The page
|
|
18
|
+
does not need to know which — it always reports.
|
|
19
|
+
|
|
20
|
+
`?hideBar=0` (set by main/annotateOverlay.ts where the window is excluded
|
|
21
|
+
from the capture) keeps the toolbar on screen permanently: the audience
|
|
22
|
+
cannot see it there, so fading it out would only hide the tool from the
|
|
23
|
+
person using it. On Linux the bar IS part of the shared picture, so it
|
|
24
|
+
still fades — and any of these brings it back: drawing, moving the pointer
|
|
25
|
+
near the bottom edge, focusing the window, or clicking 标注画面 again in
|
|
26
|
+
the meeting footer (relayed as `annotate:wake`).
|
|
27
|
+
|
|
28
|
+
Self-contained on purpose: no bundler, no fonts, no network. The window
|
|
29
|
+
must paint the instant it is shown.
|
|
30
|
+
-->
|
|
31
|
+
<style>
|
|
32
|
+
:root {
|
|
33
|
+
color-scheme: dark;
|
|
34
|
+
--bar-bg: rgba(22, 24, 28, 0.92);
|
|
35
|
+
--bar-border: rgba(255, 255, 255, 0.16);
|
|
36
|
+
--bar-fg: #f5f6f8;
|
|
37
|
+
}
|
|
38
|
+
html,
|
|
39
|
+
body {
|
|
40
|
+
margin: 0;
|
|
41
|
+
padding: 0;
|
|
42
|
+
width: 100%;
|
|
43
|
+
height: 100%;
|
|
44
|
+
overflow: hidden;
|
|
45
|
+
background: transparent;
|
|
46
|
+
cursor: crosshair;
|
|
47
|
+
user-select: none;
|
|
48
|
+
-webkit-user-select: none;
|
|
49
|
+
font-family: system-ui, -apple-system, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
|
50
|
+
}
|
|
51
|
+
#ink {
|
|
52
|
+
position: fixed;
|
|
53
|
+
inset: 0;
|
|
54
|
+
width: 100%;
|
|
55
|
+
height: 100%;
|
|
56
|
+
touch-action: none;
|
|
57
|
+
}
|
|
58
|
+
/* The toolbar floats at the bottom and fades out while the publisher is
|
|
59
|
+
drawing, so it does not sit in the middle of the shared picture (it is
|
|
60
|
+
visible to the audience on platforms where the window is captured).
|
|
61
|
+
See the `hideBar` query flag above: where it is NOT captured it stays. */
|
|
62
|
+
#bar {
|
|
63
|
+
position: fixed;
|
|
64
|
+
left: 50%;
|
|
65
|
+
bottom: 24px;
|
|
66
|
+
transform: translateX(-50%);
|
|
67
|
+
display: flex;
|
|
68
|
+
align-items: center;
|
|
69
|
+
gap: 6px;
|
|
70
|
+
padding: 6px 10px;
|
|
71
|
+
border-radius: 999px;
|
|
72
|
+
background: var(--bar-bg);
|
|
73
|
+
border: 1px solid var(--bar-border);
|
|
74
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
|
|
75
|
+
transition: opacity 0.18s ease;
|
|
76
|
+
cursor: default;
|
|
77
|
+
}
|
|
78
|
+
#bar.hidden {
|
|
79
|
+
opacity: 0;
|
|
80
|
+
pointer-events: none;
|
|
81
|
+
}
|
|
82
|
+
#bar button {
|
|
83
|
+
display: inline-flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
justify-content: center;
|
|
86
|
+
min-width: 30px;
|
|
87
|
+
height: 30px;
|
|
88
|
+
padding: 0 8px;
|
|
89
|
+
border: none;
|
|
90
|
+
border-radius: 999px;
|
|
91
|
+
background: rgba(255, 255, 255, 0.1);
|
|
92
|
+
color: var(--bar-fg);
|
|
93
|
+
font-size: 12px;
|
|
94
|
+
cursor: pointer;
|
|
95
|
+
}
|
|
96
|
+
#bar button:hover {
|
|
97
|
+
background: rgba(255, 255, 255, 0.2);
|
|
98
|
+
}
|
|
99
|
+
#bar button.on {
|
|
100
|
+
background: rgba(88, 166, 255, 0.85);
|
|
101
|
+
color: #fff;
|
|
102
|
+
}
|
|
103
|
+
.swatch {
|
|
104
|
+
width: 18px;
|
|
105
|
+
height: 18px;
|
|
106
|
+
min-width: 18px;
|
|
107
|
+
padding: 0;
|
|
108
|
+
border-radius: 50%;
|
|
109
|
+
border: 2px solid transparent;
|
|
110
|
+
}
|
|
111
|
+
.swatch.on {
|
|
112
|
+
border-color: #fff;
|
|
113
|
+
}
|
|
114
|
+
.sep {
|
|
115
|
+
width: 1px;
|
|
116
|
+
height: 18px;
|
|
117
|
+
background: var(--bar-border);
|
|
118
|
+
}
|
|
119
|
+
#close {
|
|
120
|
+
background: rgba(207, 34, 46, 0.85);
|
|
121
|
+
}
|
|
122
|
+
</style>
|
|
123
|
+
</head>
|
|
124
|
+
<body>
|
|
125
|
+
<canvas id="ink"></canvas>
|
|
126
|
+
<div id="bar">
|
|
127
|
+
<button id="pen" class="on" title="画笔">✎</button>
|
|
128
|
+
<button id="laser" title="激光笔">◉</button>
|
|
129
|
+
<span class="sep"></span>
|
|
130
|
+
<button class="swatch on" data-color="#ff3b30" style="background: #ff3b30" title="红"></button>
|
|
131
|
+
<button class="swatch" data-color="#ffd60a" style="background: #ffd60a" title="黄"></button>
|
|
132
|
+
<button class="swatch" data-color="#34c759" style="background: #34c759" title="绿"></button>
|
|
133
|
+
<button class="swatch" data-color="#0a84ff" style="background: #0a84ff" title="蓝"></button>
|
|
134
|
+
<span class="sep"></span>
|
|
135
|
+
<button id="undo" title="撤销">↶</button>
|
|
136
|
+
<button id="clear" title="清空">✕</button>
|
|
137
|
+
<button id="close" title="关闭标注">关闭</button>
|
|
138
|
+
</div>
|
|
139
|
+
<script>
|
|
140
|
+
(() => {
|
|
141
|
+
const canvas = document.getElementById('ink')
|
|
142
|
+
const ctx = canvas.getContext('2d')
|
|
143
|
+
const bar = document.getElementById('bar')
|
|
144
|
+
const host = window.AnnotateHost || { ink() {}, close() {} }
|
|
145
|
+
/**
|
|
146
|
+
* Whether the bar may fade out. False when the overlay is excluded from
|
|
147
|
+
* the capture: the audience cannot see the bar there, so hiding it only
|
|
148
|
+
* makes the tool vanish for the host (and a faded bar cannot be
|
|
149
|
+
* clicked — the window is the whole screen, there is nothing else to
|
|
150
|
+
* click).
|
|
151
|
+
*/
|
|
152
|
+
const autoHideBar = new URLSearchParams(location.search).get('hideBar') !== '0'
|
|
153
|
+
|
|
154
|
+
let mode = 'pen'
|
|
155
|
+
let color = '#ff3b30'
|
|
156
|
+
const width = 4
|
|
157
|
+
/** Finished strokes, in normalised 0..1 coordinates. */
|
|
158
|
+
let strokes = []
|
|
159
|
+
let current = null
|
|
160
|
+
/** Laser marker: {x, y, at} or null. */
|
|
161
|
+
let laser = null
|
|
162
|
+
let dpr = 1
|
|
163
|
+
|
|
164
|
+
function resize() {
|
|
165
|
+
dpr = window.devicePixelRatio || 1
|
|
166
|
+
canvas.width = Math.round(window.innerWidth * dpr)
|
|
167
|
+
canvas.height = Math.round(window.innerHeight * dpr)
|
|
168
|
+
redraw()
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function drawStroke(s) {
|
|
172
|
+
if (s.points.length === 0) return
|
|
173
|
+
ctx.strokeStyle = s.color
|
|
174
|
+
ctx.lineWidth = width * dpr
|
|
175
|
+
ctx.lineCap = 'round'
|
|
176
|
+
ctx.lineJoin = 'round'
|
|
177
|
+
ctx.beginPath()
|
|
178
|
+
const p0 = s.points[0]
|
|
179
|
+
ctx.moveTo(p0.x * canvas.width, p0.y * canvas.height)
|
|
180
|
+
for (let i = 1; i < s.points.length; i++) {
|
|
181
|
+
const p = s.points[i]
|
|
182
|
+
ctx.lineTo(p.x * canvas.width, p.y * canvas.height)
|
|
183
|
+
}
|
|
184
|
+
ctx.stroke()
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function redraw() {
|
|
188
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
|
189
|
+
for (const s of strokes) drawStroke(s)
|
|
190
|
+
if (current) drawStroke(current)
|
|
191
|
+
if (laser) {
|
|
192
|
+
ctx.beginPath()
|
|
193
|
+
ctx.fillStyle = 'rgba(255, 59, 48, 0.9)'
|
|
194
|
+
ctx.arc(laser.x * canvas.width, laser.y * canvas.height, 9 * dpr, 0, Math.PI * 2)
|
|
195
|
+
ctx.fill()
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function send(msg) {
|
|
200
|
+
host.ink(JSON.stringify(msg))
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function normalise(ev) {
|
|
204
|
+
return {
|
|
205
|
+
x: Math.min(1, Math.max(0, ev.clientX / window.innerWidth)),
|
|
206
|
+
y: Math.min(1, Math.max(0, ev.clientY / window.innerHeight)),
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function hideBarSoon() {
|
|
211
|
+
if (!autoHideBar) return
|
|
212
|
+
clearTimeout(hideBarSoon.timer)
|
|
213
|
+
hideBarSoon.timer = setTimeout(() => bar.classList.add('hidden'), 2600)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function wake() {
|
|
217
|
+
clearTimeout(hideBarSoon.timer)
|
|
218
|
+
bar.classList.remove('hidden')
|
|
219
|
+
hideBarSoon()
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
canvas.addEventListener('pointerdown', (ev) => {
|
|
223
|
+
canvas.setPointerCapture(ev.pointerId)
|
|
224
|
+
wake()
|
|
225
|
+
const p = normalise(ev)
|
|
226
|
+
if (mode === 'laser') {
|
|
227
|
+
laser = { x: p.x, y: p.y }
|
|
228
|
+
send({ t: 'laser', x: p.x, y: p.y, on: true })
|
|
229
|
+
redraw()
|
|
230
|
+
return
|
|
231
|
+
}
|
|
232
|
+
current = { color, points: [p] }
|
|
233
|
+
send({ t: 'ink', phase: 'start', x: p.x, y: p.y, color, width })
|
|
234
|
+
redraw()
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
canvas.addEventListener('pointermove', (ev) => {
|
|
238
|
+
if (mode === 'laser') {
|
|
239
|
+
if (!laser) return
|
|
240
|
+
const p = normalise(ev)
|
|
241
|
+
laser = { x: p.x, y: p.y }
|
|
242
|
+
send({ t: 'laser', x: p.x, y: p.y, on: true })
|
|
243
|
+
redraw()
|
|
244
|
+
return
|
|
245
|
+
}
|
|
246
|
+
if (!current) return
|
|
247
|
+
const p = normalise(ev)
|
|
248
|
+
current.points.push(p)
|
|
249
|
+
send({ t: 'ink', phase: 'move', x: p.x, y: p.y, color, width })
|
|
250
|
+
redraw()
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
function endStroke(ev) {
|
|
254
|
+
if (mode === 'laser') {
|
|
255
|
+
if (!laser) return
|
|
256
|
+
send({ t: 'laser', x: 0, y: 0, on: false })
|
|
257
|
+
laser = null
|
|
258
|
+
redraw()
|
|
259
|
+
return
|
|
260
|
+
}
|
|
261
|
+
if (!current) return
|
|
262
|
+
const p = ev && ev.clientX !== undefined ? normalise(ev) : current.points[current.points.length - 1]
|
|
263
|
+
send({ t: 'ink', phase: 'end', x: p.x, y: p.y, color, width })
|
|
264
|
+
strokes.push(current)
|
|
265
|
+
current = null
|
|
266
|
+
redraw()
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
canvas.addEventListener('pointerup', endStroke)
|
|
270
|
+
canvas.addEventListener('pointercancel', endStroke)
|
|
271
|
+
|
|
272
|
+
document.getElementById('pen').addEventListener('click', (e) => {
|
|
273
|
+
mode = 'pen'
|
|
274
|
+
e.currentTarget.classList.add('on')
|
|
275
|
+
document.getElementById('laser').classList.remove('on')
|
|
276
|
+
wake()
|
|
277
|
+
})
|
|
278
|
+
document.getElementById('laser').addEventListener('click', (e) => {
|
|
279
|
+
mode = 'laser'
|
|
280
|
+
e.currentTarget.classList.add('on')
|
|
281
|
+
document.getElementById('pen').classList.remove('on')
|
|
282
|
+
wake()
|
|
283
|
+
})
|
|
284
|
+
for (const sw of document.querySelectorAll('.swatch')) {
|
|
285
|
+
sw.addEventListener('click', () => {
|
|
286
|
+
color = sw.dataset.color
|
|
287
|
+
for (const other of document.querySelectorAll('.swatch')) other.classList.remove('on')
|
|
288
|
+
sw.classList.add('on')
|
|
289
|
+
mode = 'pen'
|
|
290
|
+
document.getElementById('pen').classList.add('on')
|
|
291
|
+
document.getElementById('laser').classList.remove('on')
|
|
292
|
+
wake()
|
|
293
|
+
})
|
|
294
|
+
}
|
|
295
|
+
document.getElementById('undo').addEventListener('click', () => {
|
|
296
|
+
strokes.pop()
|
|
297
|
+
send({ t: 'ink-undo' })
|
|
298
|
+
redraw()
|
|
299
|
+
wake()
|
|
300
|
+
})
|
|
301
|
+
document.getElementById('clear').addEventListener('click', () => {
|
|
302
|
+
strokes = []
|
|
303
|
+
current = null
|
|
304
|
+
send({ t: 'ink-clear' })
|
|
305
|
+
redraw()
|
|
306
|
+
wake()
|
|
307
|
+
})
|
|
308
|
+
document.getElementById('close').addEventListener('click', () => host.close())
|
|
309
|
+
|
|
310
|
+
document.addEventListener('keydown', (ev) => {
|
|
311
|
+
if (ev.key === 'Escape') host.close()
|
|
312
|
+
if ((ev.ctrlKey || ev.metaKey) && ev.key.toLowerCase() === 'z') {
|
|
313
|
+
strokes.pop()
|
|
314
|
+
send({ t: 'ink-undo' })
|
|
315
|
+
redraw()
|
|
316
|
+
}
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
// Keep the toolbar reachable without a permanent strip of chrome: any
|
|
320
|
+
// pointer movement near the bottom edge brings it back. The meeting
|
|
321
|
+
// footer's 标注画面 button does the same from outside (onWake), and so
|
|
322
|
+
// does focusing the window — the host may come back from another app.
|
|
323
|
+
document.addEventListener('pointermove', (ev) => {
|
|
324
|
+
if (ev.clientY > window.innerHeight - 120) wake()
|
|
325
|
+
})
|
|
326
|
+
window.addEventListener('focus', wake)
|
|
327
|
+
if (typeof host.onWake === 'function') host.onWake(wake)
|
|
328
|
+
|
|
329
|
+
window.addEventListener('resize', resize)
|
|
330
|
+
resize()
|
|
331
|
+
hideBarSoon()
|
|
332
|
+
})()
|
|
333
|
+
</script>
|
|
334
|
+
</body>
|
|
335
|
+
</html>
|
package/resources/app.asar
CHANGED
|
Binary file
|
|
Binary file
|
package/resources/floating.html
CHANGED
|
@@ -38,11 +38,28 @@
|
|
|
38
38
|
a disc whenever the window is left taller than the content. */
|
|
39
39
|
height: auto;
|
|
40
40
|
padding-top: 5px; padding-bottom: 5px;
|
|
41
|
-
/*
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
/* max-content + no shrink, NOT `fit-content` and NOT `max-width: 100%`.
|
|
42
|
+
|
|
43
|
+
The page reports its natural size so the window can hug the capsule
|
|
44
|
+
(`measure()` reads `scrollWidth`). Both of those older declarations clamp
|
|
45
|
+
the pill to the CURRENT window width, and that makes the measurement
|
|
46
|
+
self-confirming: while the window is too narrow the pill is squeezed, its
|
|
47
|
+
right padding is eaten by the overflowing row, `scrollWidth` then reports
|
|
48
|
+
the squeezed width, and the page asks for exactly the width it already
|
|
49
|
+
has — the capsule can never grow back.
|
|
50
|
+
|
|
51
|
+
Measured (Playwright, the bundled Chromium, "执行中 3 · 未读 2"): growing
|
|
52
|
+
from a 120px window converged on 162px instead of 177px, leaving the last
|
|
53
|
+
glyph 0.4px past the pill's edge — the text visibly touching / spilling
|
|
54
|
+
over the capsule wall. With max-content it requests 177px and keeps the
|
|
55
|
+
documented 15px text gap, and it still shrinks (400 → 88) when the
|
|
56
|
+
counters drop to zero.
|
|
57
|
+
|
|
58
|
+
`flex: 0 0 auto` is the same invariant from the other side: `#pill` is a
|
|
59
|
+
flex item of `#hit`, so without it the flex base size could still be
|
|
60
|
+
shrunk and `max-content` would be advisory only. */
|
|
61
|
+
width: max-content;
|
|
62
|
+
flex: 0 0 auto;
|
|
46
63
|
display: flex; align-items: center; gap: 7px;
|
|
47
64
|
/* Asymmetric on purpose. Do NOT "fix" this back to symmetric.
|
|
48
65
|
|
|
@@ -64,18 +81,29 @@
|
|
|
64
81
|
border-radius: 999px;
|
|
65
82
|
background: var(--bg);
|
|
66
83
|
color: var(--text);
|
|
67
|
-
|
|
84
|
+
/* Inset ring ONLY — no drop shadow, ever.
|
|
85
|
+
|
|
86
|
+
The window is sized to the pill plus a 4px margin (`measure()` reports
|
|
87
|
+
`scrollWidth + 8`), and a transparent window is still rectangular to the
|
|
88
|
+
compositor. A shadow with any blur radius therefore cannot fall away: it
|
|
89
|
+
is clipped at the window edge and paints that 4px ring, which reads as a
|
|
90
|
+
grey semi-transparent box framing the capsule on anything but a dark
|
|
91
|
+
wallpaper. The surround must stay fully transparent — the pill's own
|
|
92
|
+
translucent background is the only thing that should be visible. */
|
|
93
|
+
box-shadow: 0 0 0 1px rgba(255, 255, 255, .07) inset;
|
|
68
94
|
/* Deliberately NOT `-webkit-app-region: drag`: that hands the surface to the
|
|
69
95
|
OS as a title bar, so right-click opens the Windows system menu instead of
|
|
70
96
|
ours and no DOM click reaches the page. Dragging is driven from the main
|
|
71
97
|
process instead (see main/floating.ts). */
|
|
72
98
|
transition: transform .12s ease, box-shadow .12s ease;
|
|
73
99
|
}
|
|
74
|
-
|
|
100
|
+
/* Feedback without a halo: the lift is kept, the ring just brightens. */
|
|
101
|
+
body.hover #pill { transform: translateY(-1px); box-shadow: 0 0 0 1px rgba(255, 255, 255, .16) inset; }
|
|
75
102
|
/* Awaiting approval is the one state worth flagging on the frame itself: a
|
|
76
103
|
blocked session is otherwise indistinguishable from a running one at a
|
|
77
|
-
glance from across the desk.
|
|
78
|
-
|
|
104
|
+
glance from across the desk. The 1px ring follows the pill's own outline —
|
|
105
|
+
unlike a blurred shadow it never paints the margin as a box. */
|
|
106
|
+
body.needs-me #pill { box-shadow: 0 0 0 1px var(--pending); }
|
|
79
107
|
|
|
80
108
|
#logo {
|
|
81
109
|
flex: 0 0 auto;
|
|
Binary file
|