@@ -3,14 +3,15 @@ import type { ComponentType, VNode, h as hType } from 'preact';
3
3
// biome-ignore lint: needed for preact
4
4
import { h } from 'preact' ; // eslint-disable-line @typescript-eslint/no-unused-vars
5
5
import { useCallback , useEffect , useMemo , useRef , useState } from 'preact/hooks' ;
6
- import { DOCUMENT , WINDOW } from '../../constants' ;
6
+ import { CROP_COLOR , DOCUMENT , WINDOW } from '../../constants' ;
7
7
import type { Dialog } from '../../types' ;
8
8
import { createScreenshotInputStyles } from './ScreenshotInput.css' ;
9
9
import { useTakeScreenshot } from './useTakeScreenshot' ;
10
10
11
11
const CROP_BUTTON_SIZE = 30 ;
12
12
const CROP_BUTTON_BORDER = 3 ;
13
13
const CROP_BUTTON_OFFSET = CROP_BUTTON_SIZE + CROP_BUTTON_BORDER ;
14
+ const DPI = WINDOW . devicePixelRatio ;
14
15
15
16
interface FactoryParams {
16
17
h : typeof hType ;
@@ -79,8 +80,14 @@ export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: Factor
79
80
const cropper = croppingRef . current ;
80
81
const imageDimensions = constructRect ( getContainedSize ( imageBuffer ) ) ;
81
82
if ( cropper ) {
82
- cropper . width = imageDimensions . width ;
83
- cropper . height = imageDimensions . height ;
83
+ cropper . width = imageDimensions . width * DPI ;
84
+ cropper . height = imageDimensions . height * DPI ;
85
+ cropper . style . width = `${ imageDimensions . width } px` ;
86
+ cropper . style . height = `${ imageDimensions . height } px` ;
87
+ const ctx = cropper . getContext ( '2d' ) ;
88
+ if ( ctx ) {
89
+ ctx . scale ( DPI , DPI ) ;
90
+ }
84
91
}
85
92
86
93
const cropButton = cropContainerRef . current ;
@@ -104,9 +111,9 @@ export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: Factor
104
111
if ( ! ctx ) {
105
112
return ;
106
113
}
114
+
107
115
const imageDimensions = constructRect ( getContainedSize ( imageBuffer ) ) ;
108
116
const croppingBox = constructRect ( croppingRect ) ;
109
-
110
117
ctx . clearRect ( 0 , 0 , imageDimensions . width , imageDimensions . height ) ;
111
118
112
119
// draw gray overlay around the selection
@@ -115,9 +122,12 @@ export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: Factor
115
122
ctx . clearRect ( croppingBox . x , croppingBox . y , croppingBox . width , croppingBox . height ) ;
116
123
117
124
// draw selection border
118
- ctx . strokeStyle = 'purple' ;
125
+ ctx . strokeStyle = CROP_COLOR ;
119
126
ctx . lineWidth = 3 ;
120
- ctx . strokeRect ( croppingBox . x , croppingBox . y , croppingBox . width , croppingBox . height ) ;
127
+ ctx . strokeRect ( croppingBox . x + 1 , croppingBox . y + 1 , croppingBox . width - 2 , croppingBox . height - 2 ) ;
128
+ ctx . strokeStyle = '#000000' ;
129
+ ctx . lineWidth = 1 ;
130
+ ctx . strokeRect ( croppingBox . x + 3 , croppingBox . y + 3 , croppingBox . width - 6 , croppingBox . height - 6 ) ;
121
131
} , [ croppingRect ] ) ;
122
132
123
133
function onGrabButton ( e : Event , corner : string ) : void {
@@ -143,32 +153,32 @@ export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: Factor
143
153
const mouseX = e . clientX - cropBoundingRect . x ;
144
154
const mouseY = e . clientY - cropBoundingRect . y ;
145
155
switch ( corner ) {
146
- case 'topleft ' :
156
+ case 'top-left ' :
147
157
setCroppingRect ( prev => ( {
148
158
...prev ,
149
159
startX : Math . min ( Math . max ( 0 , mouseX ) , prev . endX - CROP_BUTTON_OFFSET ) ,
150
160
startY : Math . min ( Math . max ( 0 , mouseY ) , prev . endY - CROP_BUTTON_OFFSET ) ,
151
161
} ) ) ;
152
162
break ;
153
- case 'topright ' :
163
+ case 'top-right ' :
154
164
setCroppingRect ( prev => ( {
155
165
...prev ,
156
- endX : Math . max ( Math . min ( mouseX , cropCanvas . width ) , prev . startX + CROP_BUTTON_OFFSET ) ,
166
+ endX : Math . max ( Math . min ( mouseX , cropCanvas . width / DPI ) , prev . startX + CROP_BUTTON_OFFSET ) ,
157
167
startY : Math . min ( Math . max ( 0 , mouseY ) , prev . endY - CROP_BUTTON_OFFSET ) ,
158
168
} ) ) ;
159
169
break ;
160
- case 'bottomleft ' :
170
+ case 'bottom-left ' :
161
171
setCroppingRect ( prev => ( {
162
172
...prev ,
163
173
startX : Math . min ( Math . max ( 0 , mouseX ) , prev . endX - CROP_BUTTON_OFFSET ) ,
164
- endY : Math . max ( Math . min ( mouseY , cropCanvas . height ) , prev . startY + CROP_BUTTON_OFFSET ) ,
174
+ endY : Math . max ( Math . min ( mouseY , cropCanvas . height / DPI ) , prev . startY + CROP_BUTTON_OFFSET ) ,
165
175
} ) ) ;
166
176
break ;
167
- case 'bottomright ' :
177
+ case 'bottom-right ' :
168
178
setCroppingRect ( prev => ( {
169
179
...prev ,
170
- endX : Math . max ( Math . min ( mouseX , cropCanvas . width ) , prev . startX + CROP_BUTTON_OFFSET ) ,
171
- endY : Math . max ( Math . min ( mouseY , cropCanvas . height ) , prev . startY + CROP_BUTTON_OFFSET ) ,
180
+ endX : Math . max ( Math . min ( mouseX , cropCanvas . width / DPI ) , prev . startX + CROP_BUTTON_OFFSET ) ,
181
+ endY : Math . max ( Math . min ( mouseY , cropCanvas . height / DPI ) , prev . startY + CROP_BUTTON_OFFSET ) ,
172
182
} ) ) ;
173
183
break ;
174
184
}
@@ -238,40 +248,40 @@ export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: Factor
238
248
return (
239
249
< div class = "editor" >
240
250
< style dangerouslySetInnerHTML = { styles } />
241
- < div class = "canvasContainer " ref = { canvasContainerRef } >
242
- < div class = "cropButtonContainer " style = { { position : 'absolute' } } ref = { cropContainerRef } >
251
+ < div class = "editor__canvas-container " ref = { canvasContainerRef } >
252
+ < div class = "editor__crop-container " style = { { position : 'absolute' } } ref = { cropContainerRef } >
243
253
< canvas style = { { position : 'absolute' } } ref = { croppingRef } > </ canvas >
244
254
< CropCorner
245
- left = { croppingRect . startX }
246
- top = { croppingRect . startY }
255
+ left = { croppingRect . startX - CROP_BUTTON_BORDER }
256
+ top = { croppingRect . startY - CROP_BUTTON_BORDER }
247
257
onGrabButton = { onGrabButton }
248
- corner = "topleft "
258
+ corner = "top-left "
249
259
> </ CropCorner >
250
260
< CropCorner
251
- left = { croppingRect . endX - CROP_BUTTON_SIZE }
252
- top = { croppingRect . startY }
261
+ left = { croppingRect . endX - CROP_BUTTON_SIZE + CROP_BUTTON_BORDER }
262
+ top = { croppingRect . startY - CROP_BUTTON_BORDER }
253
263
onGrabButton = { onGrabButton }
254
- corner = "topright "
264
+ corner = "top-right "
255
265
> </ CropCorner >
256
266
< CropCorner
257
- left = { croppingRect . startX }
258
- top = { croppingRect . endY - CROP_BUTTON_SIZE }
267
+ left = { croppingRect . startX - CROP_BUTTON_BORDER }
268
+ top = { croppingRect . endY - CROP_BUTTON_SIZE + CROP_BUTTON_BORDER }
259
269
onGrabButton = { onGrabButton }
260
- corner = "bottomleft "
270
+ corner = "bottom-left "
261
271
> </ CropCorner >
262
272
< CropCorner
263
- left = { croppingRect . endX - CROP_BUTTON_SIZE }
264
- top = { croppingRect . endY - CROP_BUTTON_SIZE }
273
+ left = { croppingRect . endX - CROP_BUTTON_SIZE + CROP_BUTTON_BORDER }
274
+ top = { croppingRect . endY - CROP_BUTTON_SIZE + CROP_BUTTON_BORDER }
265
275
onGrabButton = { onGrabButton }
266
- corner = "bottomright "
276
+ corner = "bottom-right "
267
277
> </ CropCorner >
268
278
< div
269
279
style = { {
270
280
left : Math . max ( 0 , croppingRect . endX - 191 ) ,
271
281
top : Math . max ( 0 , croppingRect . endY + 8 ) ,
272
282
display : confirmCrop ? 'flex' : 'none' ,
273
283
} }
274
- class = "crop -btn-group"
284
+ class = "editor__crop -btn-group"
275
285
>
276
286
< button
277
287
onClick = { e => {
@@ -280,8 +290,8 @@ export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: Factor
280
290
setCroppingRect ( {
281
291
startX : 0 ,
282
292
startY : 0 ,
283
- endX : croppingRef . current . width ,
284
- endY : croppingRef . current . height ,
293
+ endX : croppingRef . current . width / DPI ,
294
+ endY : croppingRef . current . height / DPI ,
285
295
} ) ;
286
296
}
287
297
setConfirmCrop ( false ) ;
@@ -321,16 +331,10 @@ function CropCorner({
321
331
} ) : VNode {
322
332
return (
323
333
< button
324
- class = "crop-btn"
334
+ class = { `editor__crop-corner editor__crop-corner-- ${ corner } ` }
325
335
style = { {
326
336
top : top ,
327
337
left : left ,
328
- borderTop : corner === 'topleft' || corner === 'topright' ? 'solid purple' : 'none' ,
329
- borderLeft : corner === 'topleft' || corner === 'bottomleft' ? 'solid purple' : 'none' ,
330
- borderRight : corner === 'topright' || corner === 'bottomright' ? 'solid purple' : 'none' ,
331
- borderBottom : corner === 'bottomleft' || corner === 'bottomright' ? 'solid purple' : 'none' ,
332
- borderWidth : `${ CROP_BUTTON_BORDER } px` ,
333
- cursor : corner === 'topleft' || corner === 'bottomright' ? 'nwse-resize' : 'nesw-resize' ,
334
338
} }
335
339
onMouseDown = { e => {
336
340
e . preventDefault ( ) ;
0 commit comments