@buoy-gg/image-overlay 7.0.17 → 7.0.18

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.
@@ -17,15 +17,20 @@ export function ImageOverlayModal({
17
17
  onMinimize,
18
18
  enableSharedModalDimensions = false,
19
19
  initialModalState,
20
- minimizeTargetPosition
20
+ minimizeTargetPosition,
21
+ inline = false,
22
+ initialView
21
23
  }) {
22
24
  const [view, setView] = useState(() => {
25
+ if (initialView) return initialView;
23
26
  const s = ImageOverlayController.getState();
24
27
  if (s.mode === "free") return "free";
25
28
  if (s.targetRect) return "controls";
26
29
  return "idle";
27
30
  });
28
- const [targets, setTargets] = useState([]);
31
+ // Opening straight onto the target list has to arrive scanned — the list is
32
+ // otherwise only ever filled by the button that navigates to it.
33
+ const [targets, setTargets] = useState(() => initialView === "targets" ? scanForImageTargets() : []);
29
34
  const [state, setState] = useState(() => ImageOverlayController.getState());
30
35
  const [imageUrl, setImageUrl] = useState(persistedImageUrl);
31
36
  const [autoTrack, setAutoTrack] = useState(() => ImageOverlayController.isAutoTracking());
@@ -196,399 +201,440 @@ export function ImageOverlayModal({
196
201
  })]
197
202
  })
198
203
  };
199
- return /*#__PURE__*/_jsx(JsModal, {
200
- visible: visible,
201
- onClose: onClose,
202
- onMinimize: onMinimize,
203
- minimizeTargetPosition: minimizeTargetPosition,
204
- initialModalState: initialModalState,
205
- header: headerContent,
206
- initialMode: "bottomSheet",
207
- persistenceKey: "buoy-image-overlay-modal",
208
- enablePersistence: true,
209
- children: /*#__PURE__*/_jsxs(View, {
210
- style: styles.container,
211
- children: [view === "idle" && /*#__PURE__*/_jsxs(View, {
212
- style: styles.homeContainer,
204
+ const body = /*#__PURE__*/_jsxs(View, {
205
+ style: styles.container,
206
+ children: [view === "idle" && /*#__PURE__*/_jsxs(View, {
207
+ style: styles.homeContainer,
208
+ children: [/*#__PURE__*/_jsx(Text, {
209
+ style: styles.homeSubtitle,
210
+ children: "Choose a mode"
211
+ }), /*#__PURE__*/_jsxs(View, {
212
+ style: styles.modeGrid,
213
+ children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
214
+ style: styles.modePanel,
215
+ onPress: handleScan,
216
+ activeOpacity: 0.7,
217
+ children: [/*#__PURE__*/_jsx(View, {
218
+ style: [styles.modeIconCircle, {
219
+ backgroundColor: macOSColors.semantic.success + "18"
220
+ }],
221
+ children: /*#__PURE__*/_jsx(SquareDashed, {
222
+ size: 24,
223
+ color: macOSColors.semantic.success
224
+ })
225
+ }), /*#__PURE__*/_jsxs(Text, {
226
+ style: styles.modePanelTitle,
227
+ children: ["Component", "\n", "Match"]
228
+ }), /*#__PURE__*/_jsx(Text, {
229
+ style: styles.modePanelDesc,
230
+ children: "Overlay on a tagged component"
231
+ })]
232
+ }), /*#__PURE__*/_jsxs(TouchableOpacity, {
233
+ style: styles.modePanel,
234
+ onPress: () => setView("free"),
235
+ activeOpacity: 0.7,
236
+ children: [/*#__PURE__*/_jsx(View, {
237
+ style: [styles.modeIconCircle, {
238
+ backgroundColor: buoyColors.primary + "18"
239
+ }],
240
+ children: /*#__PURE__*/_jsx(Image, {
241
+ size: 24,
242
+ color: buoyColors.primary
243
+ })
244
+ }), /*#__PURE__*/_jsxs(Text, {
245
+ style: styles.modePanelTitle,
246
+ children: ["Free", "\n", "Placement"]
247
+ }), /*#__PURE__*/_jsx(Text, {
248
+ style: styles.modePanelDesc,
249
+ children: "Drag and resize anywhere"
250
+ })]
251
+ })]
252
+ })]
253
+ }), view === "targets" && /*#__PURE__*/_jsx(TargetList, {
254
+ targets: targets,
255
+ onSelect: handleSelectTarget
256
+ }), view === "controls" && /*#__PURE__*/_jsxs(View, {
257
+ style: styles.controlsContainer,
258
+ children: [state.targetRect && /*#__PURE__*/_jsxs(View, {
259
+ style: styles.infoCard,
260
+ children: [/*#__PURE__*/_jsxs(View, {
261
+ style: styles.infoRow,
262
+ children: [/*#__PURE__*/_jsx(Text, {
263
+ style: styles.infoLabel,
264
+ children: "Component"
265
+ }), /*#__PURE__*/_jsx(Text, {
266
+ style: styles.infoValue,
267
+ children: state.targetLabel
268
+ })]
269
+ }), /*#__PURE__*/_jsxs(View, {
270
+ style: styles.infoRow,
271
+ children: [/*#__PURE__*/_jsx(Text, {
272
+ style: styles.infoLabel,
273
+ children: "Size"
274
+ }), /*#__PURE__*/_jsxs(Text, {
275
+ style: styles.infoValueMono,
276
+ children: [Math.round(state.targetRect.width), " x ", Math.round(state.targetRect.height)]
277
+ })]
278
+ }), /*#__PURE__*/_jsxs(View, {
279
+ style: styles.infoRow,
280
+ children: [/*#__PURE__*/_jsx(Text, {
281
+ style: styles.infoLabel,
282
+ children: "Position"
283
+ }), /*#__PURE__*/_jsxs(Text, {
284
+ style: styles.infoValueMono,
285
+ children: [Math.round(state.targetRect.x), ", ", Math.round(state.targetRect.y)]
286
+ })]
287
+ }), state.imageWidth != null && state.imageHeight != null && /*#__PURE__*/_jsxs(View, {
288
+ style: styles.infoRow,
289
+ children: [/*#__PURE__*/_jsx(Text, {
290
+ style: styles.infoLabel,
291
+ children: "Image"
292
+ }), /*#__PURE__*/_jsxs(Text, {
293
+ style: styles.infoValueMono,
294
+ children: [state.imageWidth, " x ", state.imageHeight]
295
+ })]
296
+ })]
297
+ }), /*#__PURE__*/_jsxs(View, {
298
+ style: styles.inputSection,
213
299
  children: [/*#__PURE__*/_jsx(Text, {
214
- style: styles.homeSubtitle,
215
- children: "Choose a mode"
300
+ style: styles.sectionLabel,
301
+ children: "Image"
302
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
303
+ style: styles.pasteButton,
304
+ onPress: handlePasteImage,
305
+ activeOpacity: 0.7,
306
+ children: /*#__PURE__*/_jsx(Text, {
307
+ style: styles.pasteButtonText,
308
+ children: "Paste from Clipboard"
309
+ })
216
310
  }), /*#__PURE__*/_jsxs(View, {
217
- style: styles.modeGrid,
218
- children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
219
- style: styles.modePanel,
220
- onPress: handleScan,
221
- activeOpacity: 0.7,
222
- children: [/*#__PURE__*/_jsx(View, {
223
- style: [styles.modeIconCircle, {
224
- backgroundColor: macOSColors.semantic.success + "18"
225
- }],
226
- children: /*#__PURE__*/_jsx(SquareDashed, {
227
- size: 24,
228
- color: macOSColors.semantic.success
229
- })
230
- }), /*#__PURE__*/_jsxs(Text, {
231
- style: styles.modePanelTitle,
232
- children: ["Component", "\n", "Match"]
233
- }), /*#__PURE__*/_jsx(Text, {
234
- style: styles.modePanelDesc,
235
- children: "Overlay on a tagged component"
236
- })]
237
- }), /*#__PURE__*/_jsxs(TouchableOpacity, {
238
- style: styles.modePanel,
239
- onPress: () => setView("free"),
311
+ style: styles.urlRow,
312
+ children: [/*#__PURE__*/_jsx(TextInput, {
313
+ style: styles.urlInput,
314
+ value: imageUrl,
315
+ onChangeText: setImageUrl,
316
+ placeholder: "or enter URL...",
317
+ placeholderTextColor: macOSColors.text.muted,
318
+ autoCapitalize: "none",
319
+ autoCorrect: false
320
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
321
+ style: [styles.loadButton, !imageUrl.trim() && styles.loadButtonDisabled],
322
+ onPress: handleLoadImage,
323
+ disabled: !imageUrl.trim(),
240
324
  activeOpacity: 0.7,
241
- children: [/*#__PURE__*/_jsx(View, {
242
- style: [styles.modeIconCircle, {
243
- backgroundColor: buoyColors.primary + "18"
244
- }],
245
- children: /*#__PURE__*/_jsx(Image, {
246
- size: 24,
247
- color: buoyColors.primary
248
- })
249
- }), /*#__PURE__*/_jsxs(Text, {
250
- style: styles.modePanelTitle,
251
- children: ["Free", "\n", "Placement"]
252
- }), /*#__PURE__*/_jsx(Text, {
253
- style: styles.modePanelDesc,
254
- children: "Drag and resize anywhere"
255
- })]
325
+ children: /*#__PURE__*/_jsx(Text, {
326
+ style: styles.loadButtonText,
327
+ children: "Load"
328
+ })
256
329
  })]
257
330
  })]
258
- }), view === "targets" && /*#__PURE__*/_jsx(TargetList, {
259
- targets: targets,
260
- onSelect: handleSelectTarget
261
- }), view === "controls" && /*#__PURE__*/_jsxs(View, {
262
- style: styles.controlsContainer,
263
- children: [state.targetRect && /*#__PURE__*/_jsxs(View, {
264
- style: styles.infoCard,
331
+ }), /*#__PURE__*/_jsxs(View, {
332
+ style: styles.toggleSection,
333
+ children: [/*#__PURE__*/_jsxs(View, {
334
+ style: styles.toggleRow,
335
+ children: [/*#__PURE__*/_jsx(Text, {
336
+ style: styles.toggleLabel,
337
+ children: "Show Outline"
338
+ }), /*#__PURE__*/_jsx(Switch, {
339
+ value: state.showOutline,
340
+ onValueChange: v => ImageOverlayController.setShowOutline(v),
341
+ trackColor: {
342
+ false: macOSColors.background.hover,
343
+ true: buoyColors.primary
344
+ }
345
+ })]
346
+ }), /*#__PURE__*/_jsxs(View, {
347
+ style: styles.toggleRow,
348
+ children: [/*#__PURE__*/_jsx(Text, {
349
+ style: styles.toggleLabel,
350
+ children: "Auto Track"
351
+ }), /*#__PURE__*/_jsx(Switch, {
352
+ value: autoTrack,
353
+ onValueChange: setAutoTrack,
354
+ trackColor: {
355
+ false: macOSColors.background.hover,
356
+ true: macOSColors.semantic.success
357
+ }
358
+ })]
359
+ }), state.imageUri && /*#__PURE__*/_jsxs(_Fragment, {
265
360
  children: [/*#__PURE__*/_jsxs(View, {
266
- style: styles.infoRow,
361
+ style: styles.blendRow,
267
362
  children: [/*#__PURE__*/_jsx(Text, {
268
- style: styles.infoLabel,
269
- children: "Component"
270
- }), /*#__PURE__*/_jsx(Text, {
271
- style: styles.infoValue,
272
- children: state.targetLabel
363
+ style: styles.toggleLabel,
364
+ children: "Opacity"
365
+ }), /*#__PURE__*/_jsx(View, {
366
+ style: styles.blendButtons,
367
+ children: [0, 25, 50, 75, 100].map(pct => /*#__PURE__*/_jsx(TouchableOpacity, {
368
+ style: [styles.blendButton, Math.round(state.opacity * 100) === pct && styles.blendButtonActive],
369
+ onPress: () => ImageOverlayController.setOpacity(pct / 100),
370
+ activeOpacity: 0.7,
371
+ children: /*#__PURE__*/_jsxs(Text, {
372
+ style: [styles.blendButtonText, Math.round(state.opacity * 100) === pct && styles.blendButtonTextActive],
373
+ children: [pct, "%"]
374
+ })
375
+ }, pct))
273
376
  })]
274
377
  }), /*#__PURE__*/_jsxs(View, {
378
+ style: styles.blendRow,
379
+ children: [/*#__PURE__*/_jsx(Text, {
380
+ style: styles.toggleLabel,
381
+ children: "Flip"
382
+ }), /*#__PURE__*/_jsxs(View, {
383
+ style: styles.blendButtons,
384
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
385
+ style: [styles.blendButton, state.invertX && styles.blendButtonActive],
386
+ onPress: () => ImageOverlayController.toggleInvertX(),
387
+ activeOpacity: 0.7,
388
+ children: /*#__PURE__*/_jsx(Text, {
389
+ style: [styles.blendButtonText, state.invertX && styles.blendButtonTextActive],
390
+ children: "Horizontal"
391
+ })
392
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
393
+ style: [styles.blendButton, state.invertY && styles.blendButtonActive],
394
+ onPress: () => ImageOverlayController.toggleInvertY(),
395
+ activeOpacity: 0.7,
396
+ children: /*#__PURE__*/_jsx(Text, {
397
+ style: [styles.blendButtonText, state.invertY && styles.blendButtonTextActive],
398
+ children: "Vertical"
399
+ })
400
+ })]
401
+ })]
402
+ })]
403
+ })]
404
+ }), state.imageUri && /*#__PURE__*/_jsx(OverlayControls, {
405
+ opacity: state.opacity,
406
+ scale: state.scale,
407
+ offsetX: state.offsetX,
408
+ offsetY: state.offsetY,
409
+ onOpacityChange: v => ImageOverlayController.setOpacity(v),
410
+ onScaleChange: v => ImageOverlayController.setScale(v),
411
+ onOffsetChange: (x, y) => ImageOverlayController.setOffset(x, y)
412
+ }), /*#__PURE__*/_jsxs(View, {
413
+ style: styles.actions,
414
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
415
+ style: styles.actionButton,
416
+ onPress: () => ImageOverlayController.resetSettings(),
417
+ activeOpacity: 0.7,
418
+ children: /*#__PURE__*/_jsx(Text, {
419
+ style: styles.actionButtonText,
420
+ children: "Reset Settings"
421
+ })
422
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
423
+ style: styles.actionButton,
424
+ onPress: () => ImageOverlayController.remeasure(),
425
+ activeOpacity: 0.7,
426
+ children: /*#__PURE__*/_jsx(Text, {
427
+ style: styles.actionButtonText,
428
+ children: "Remeasure"
429
+ })
430
+ })]
431
+ })]
432
+ }), view === "free" && /*#__PURE__*/_jsxs(View, {
433
+ style: styles.controlsContainer,
434
+ children: [/*#__PURE__*/_jsxs(View, {
435
+ style: styles.inputSection,
436
+ children: [/*#__PURE__*/_jsx(Text, {
437
+ style: styles.sectionLabel,
438
+ children: "Image"
439
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
440
+ style: styles.pasteButton,
441
+ onPress: handlePasteImage,
442
+ activeOpacity: 0.7,
443
+ children: /*#__PURE__*/_jsx(Text, {
444
+ style: styles.pasteButtonText,
445
+ children: "Paste from Clipboard"
446
+ })
447
+ }), /*#__PURE__*/_jsxs(View, {
448
+ style: styles.urlRow,
449
+ children: [/*#__PURE__*/_jsx(TextInput, {
450
+ style: styles.urlInput,
451
+ value: imageUrl,
452
+ onChangeText: setImageUrl,
453
+ placeholder: "or enter URL...",
454
+ placeholderTextColor: macOSColors.text.muted,
455
+ autoCapitalize: "none",
456
+ autoCorrect: false
457
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
458
+ style: [styles.loadButton, !imageUrl.trim() && styles.loadButtonDisabled],
459
+ onPress: handleLoadImage,
460
+ disabled: !imageUrl.trim(),
461
+ activeOpacity: 0.7,
462
+ children: /*#__PURE__*/_jsx(Text, {
463
+ style: styles.loadButtonText,
464
+ children: "Load"
465
+ })
466
+ })]
467
+ })]
468
+ }), state.imageUri && state.mode === "free" && /*#__PURE__*/_jsxs(_Fragment, {
469
+ children: [/*#__PURE__*/_jsxs(View, {
470
+ style: styles.infoCard,
471
+ children: [/*#__PURE__*/_jsxs(View, {
275
472
  style: styles.infoRow,
276
473
  children: [/*#__PURE__*/_jsx(Text, {
277
474
  style: styles.infoLabel,
278
- children: "Size"
475
+ children: "Position"
279
476
  }), /*#__PURE__*/_jsxs(Text, {
280
477
  style: styles.infoValueMono,
281
- children: [Math.round(state.targetRect.width), " x ", Math.round(state.targetRect.height)]
478
+ children: [Math.round(state.freeX), ", ", Math.round(state.freeY)]
282
479
  })]
283
480
  }), /*#__PURE__*/_jsxs(View, {
284
481
  style: styles.infoRow,
285
482
  children: [/*#__PURE__*/_jsx(Text, {
286
483
  style: styles.infoLabel,
287
- children: "Position"
484
+ children: "Size"
288
485
  }), /*#__PURE__*/_jsxs(Text, {
289
486
  style: styles.infoValueMono,
290
- children: [Math.round(state.targetRect.x), ", ", Math.round(state.targetRect.y)]
487
+ children: [Math.round(state.freeWidth), " x ", Math.round(state.freeHeight)]
291
488
  })]
292
489
  }), state.imageWidth != null && state.imageHeight != null && /*#__PURE__*/_jsxs(View, {
293
490
  style: styles.infoRow,
294
491
  children: [/*#__PURE__*/_jsx(Text, {
295
492
  style: styles.infoLabel,
296
- children: "Image"
493
+ children: "Original"
297
494
  }), /*#__PURE__*/_jsxs(Text, {
298
495
  style: styles.infoValueMono,
299
496
  children: [state.imageWidth, " x ", state.imageHeight]
300
497
  })]
301
498
  })]
302
- }), /*#__PURE__*/_jsxs(View, {
499
+ }), /*#__PURE__*/_jsx(View, {
303
500
  style: styles.inputSection,
304
- children: [/*#__PURE__*/_jsx(Text, {
305
- style: styles.sectionLabel,
306
- children: "Image"
307
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
308
- style: styles.pasteButton,
309
- onPress: handlePasteImage,
310
- activeOpacity: 0.7,
311
- children: /*#__PURE__*/_jsx(Text, {
312
- style: styles.pasteButtonText,
313
- children: "Paste from Clipboard"
314
- })
315
- }), /*#__PURE__*/_jsxs(View, {
316
- style: styles.urlRow,
317
- children: [/*#__PURE__*/_jsx(TextInput, {
318
- style: styles.urlInput,
319
- value: imageUrl,
320
- onChangeText: setImageUrl,
321
- placeholder: "or enter URL...",
322
- placeholderTextColor: macOSColors.text.muted,
323
- autoCapitalize: "none",
324
- autoCorrect: false
325
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
326
- style: [styles.loadButton, !imageUrl.trim() && styles.loadButtonDisabled],
327
- onPress: handleLoadImage,
328
- disabled: !imageUrl.trim(),
329
- activeOpacity: 0.7,
330
- children: /*#__PURE__*/_jsx(Text, {
331
- style: styles.loadButtonText,
332
- children: "Load"
333
- })
334
- })]
335
- })]
501
+ children: /*#__PURE__*/_jsx(OverlayControls, {
502
+ opacity: state.opacity,
503
+ scale: 1,
504
+ offsetX: 0,
505
+ offsetY: 0,
506
+ onOpacityChange: v => ImageOverlayController.setOpacity(v),
507
+ onScaleChange: () => {},
508
+ onOffsetChange: () => {},
509
+ opacityOnly: true
510
+ })
336
511
  }), /*#__PURE__*/_jsxs(View, {
337
512
  style: styles.toggleSection,
338
513
  children: [/*#__PURE__*/_jsxs(View, {
339
514
  style: styles.toggleRow,
340
515
  children: [/*#__PURE__*/_jsx(Text, {
341
516
  style: styles.toggleLabel,
342
- children: "Show Outline"
517
+ children: "Lock Position"
343
518
  }), /*#__PURE__*/_jsx(Switch, {
344
- value: state.showOutline,
345
- onValueChange: v => ImageOverlayController.setShowOutline(v),
519
+ value: state.locked,
520
+ onValueChange: v => ImageOverlayController.setLocked(v),
346
521
  trackColor: {
347
522
  false: macOSColors.background.hover,
348
- true: buoyColors.primary
523
+ true: "#f59e0b"
349
524
  }
350
525
  })]
351
526
  }), /*#__PURE__*/_jsxs(View, {
352
- style: styles.toggleRow,
527
+ style: styles.blendRow,
353
528
  children: [/*#__PURE__*/_jsx(Text, {
354
529
  style: styles.toggleLabel,
355
- children: "Auto Track"
356
- }), /*#__PURE__*/_jsx(Switch, {
357
- value: autoTrack,
358
- onValueChange: setAutoTrack,
359
- trackColor: {
360
- false: macOSColors.background.hover,
361
- true: macOSColors.semantic.success
362
- }
530
+ children: "Opacity"
531
+ }), /*#__PURE__*/_jsx(View, {
532
+ style: styles.blendButtons,
533
+ children: [0, 25, 50, 75, 100].map(pct => /*#__PURE__*/_jsx(TouchableOpacity, {
534
+ style: [styles.blendButton, Math.round(state.opacity * 100) === pct && styles.blendButtonActive],
535
+ onPress: () => ImageOverlayController.setOpacity(pct / 100),
536
+ activeOpacity: 0.7,
537
+ children: /*#__PURE__*/_jsxs(Text, {
538
+ style: [styles.blendButtonText, Math.round(state.opacity * 100) === pct && styles.blendButtonTextActive],
539
+ children: [pct, "%"]
540
+ })
541
+ }, pct))
363
542
  })]
364
- }), state.imageUri && /*#__PURE__*/_jsxs(_Fragment, {
365
- children: [/*#__PURE__*/_jsxs(View, {
366
- style: styles.blendRow,
367
- children: [/*#__PURE__*/_jsx(Text, {
368
- style: styles.toggleLabel,
369
- children: "Opacity"
370
- }), /*#__PURE__*/_jsx(View, {
371
- style: styles.blendButtons,
372
- children: [0, 25, 50, 75, 100].map(pct => /*#__PURE__*/_jsx(TouchableOpacity, {
373
- style: [styles.blendButton, Math.round(state.opacity * 100) === pct && styles.blendButtonActive],
374
- onPress: () => ImageOverlayController.setOpacity(pct / 100),
375
- activeOpacity: 0.7,
376
- children: /*#__PURE__*/_jsxs(Text, {
377
- style: [styles.blendButtonText, Math.round(state.opacity * 100) === pct && styles.blendButtonTextActive],
378
- children: [pct, "%"]
379
- })
380
- }, pct))
381
- })]
543
+ }), /*#__PURE__*/_jsxs(View, {
544
+ style: styles.blendRow,
545
+ children: [/*#__PURE__*/_jsx(Text, {
546
+ style: styles.toggleLabel,
547
+ children: "Flip"
382
548
  }), /*#__PURE__*/_jsxs(View, {
383
- style: styles.blendRow,
384
- children: [/*#__PURE__*/_jsx(Text, {
385
- style: styles.toggleLabel,
386
- children: "Flip"
387
- }), /*#__PURE__*/_jsxs(View, {
388
- style: styles.blendButtons,
389
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
390
- style: [styles.blendButton, state.invertX && styles.blendButtonActive],
391
- onPress: () => ImageOverlayController.toggleInvertX(),
392
- activeOpacity: 0.7,
393
- children: /*#__PURE__*/_jsx(Text, {
394
- style: [styles.blendButtonText, state.invertX && styles.blendButtonTextActive],
395
- children: "Horizontal"
396
- })
397
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
398
- style: [styles.blendButton, state.invertY && styles.blendButtonActive],
399
- onPress: () => ImageOverlayController.toggleInvertY(),
400
- activeOpacity: 0.7,
401
- children: /*#__PURE__*/_jsx(Text, {
402
- style: [styles.blendButtonText, state.invertY && styles.blendButtonTextActive],
403
- children: "Vertical"
404
- })
405
- })]
549
+ style: styles.blendButtons,
550
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
551
+ style: [styles.blendButton, state.invertX && styles.blendButtonActive],
552
+ onPress: () => ImageOverlayController.toggleInvertX(),
553
+ activeOpacity: 0.7,
554
+ children: /*#__PURE__*/_jsx(Text, {
555
+ style: [styles.blendButtonText, state.invertX && styles.blendButtonTextActive],
556
+ children: "Horizontal"
557
+ })
558
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
559
+ style: [styles.blendButton, state.invertY && styles.blendButtonActive],
560
+ onPress: () => ImageOverlayController.toggleInvertY(),
561
+ activeOpacity: 0.7,
562
+ children: /*#__PURE__*/_jsx(Text, {
563
+ style: [styles.blendButtonText, state.invertY && styles.blendButtonTextActive],
564
+ children: "Vertical"
565
+ })
406
566
  })]
407
567
  })]
408
568
  })]
409
- }), state.imageUri && /*#__PURE__*/_jsx(OverlayControls, {
410
- opacity: state.opacity,
411
- scale: state.scale,
412
- offsetX: state.offsetX,
413
- offsetY: state.offsetY,
414
- onOpacityChange: v => ImageOverlayController.setOpacity(v),
415
- onScaleChange: v => ImageOverlayController.setScale(v),
416
- onOffsetChange: (x, y) => ImageOverlayController.setOffset(x, y)
417
- }), /*#__PURE__*/_jsxs(View, {
569
+ }), /*#__PURE__*/_jsx(View, {
418
570
  style: styles.actions,
419
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
571
+ children: /*#__PURE__*/_jsx(TouchableOpacity, {
420
572
  style: styles.actionButton,
421
573
  onPress: () => ImageOverlayController.resetSettings(),
422
574
  activeOpacity: 0.7,
423
575
  children: /*#__PURE__*/_jsx(Text, {
424
576
  style: styles.actionButtonText,
425
- children: "Reset Settings"
426
- })
427
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
428
- style: styles.actionButton,
429
- onPress: () => ImageOverlayController.remeasure(),
430
- activeOpacity: 0.7,
431
- children: /*#__PURE__*/_jsx(Text, {
432
- style: styles.actionButtonText,
433
- children: "Remeasure"
434
- })
435
- })]
436
- })]
437
- }), view === "free" && /*#__PURE__*/_jsxs(View, {
438
- style: styles.controlsContainer,
439
- children: [/*#__PURE__*/_jsxs(View, {
440
- style: styles.inputSection,
441
- children: [/*#__PURE__*/_jsx(Text, {
442
- style: styles.sectionLabel,
443
- children: "Image"
444
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
445
- style: styles.pasteButton,
446
- onPress: handlePasteImage,
447
- activeOpacity: 0.7,
448
- children: /*#__PURE__*/_jsx(Text, {
449
- style: styles.pasteButtonText,
450
- children: "Paste from Clipboard"
451
- })
452
- }), /*#__PURE__*/_jsxs(View, {
453
- style: styles.urlRow,
454
- children: [/*#__PURE__*/_jsx(TextInput, {
455
- style: styles.urlInput,
456
- value: imageUrl,
457
- onChangeText: setImageUrl,
458
- placeholder: "or enter URL...",
459
- placeholderTextColor: macOSColors.text.muted,
460
- autoCapitalize: "none",
461
- autoCorrect: false
462
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
463
- style: [styles.loadButton, !imageUrl.trim() && styles.loadButtonDisabled],
464
- onPress: handleLoadImage,
465
- disabled: !imageUrl.trim(),
466
- activeOpacity: 0.7,
467
- children: /*#__PURE__*/_jsx(Text, {
468
- style: styles.loadButtonText,
469
- children: "Load"
470
- })
471
- })]
472
- })]
473
- }), state.imageUri && state.mode === "free" && /*#__PURE__*/_jsxs(_Fragment, {
474
- children: [/*#__PURE__*/_jsxs(View, {
475
- style: styles.infoCard,
476
- children: [/*#__PURE__*/_jsxs(View, {
477
- style: styles.infoRow,
478
- children: [/*#__PURE__*/_jsx(Text, {
479
- style: styles.infoLabel,
480
- children: "Position"
481
- }), /*#__PURE__*/_jsxs(Text, {
482
- style: styles.infoValueMono,
483
- children: [Math.round(state.freeX), ", ", Math.round(state.freeY)]
484
- })]
485
- }), /*#__PURE__*/_jsxs(View, {
486
- style: styles.infoRow,
487
- children: [/*#__PURE__*/_jsx(Text, {
488
- style: styles.infoLabel,
489
- children: "Size"
490
- }), /*#__PURE__*/_jsxs(Text, {
491
- style: styles.infoValueMono,
492
- children: [Math.round(state.freeWidth), " x ", Math.round(state.freeHeight)]
493
- })]
494
- }), state.imageWidth != null && state.imageHeight != null && /*#__PURE__*/_jsxs(View, {
495
- style: styles.infoRow,
496
- children: [/*#__PURE__*/_jsx(Text, {
497
- style: styles.infoLabel,
498
- children: "Original"
499
- }), /*#__PURE__*/_jsxs(Text, {
500
- style: styles.infoValueMono,
501
- children: [state.imageWidth, " x ", state.imageHeight]
502
- })]
503
- })]
504
- }), /*#__PURE__*/_jsx(View, {
505
- style: styles.inputSection,
506
- children: /*#__PURE__*/_jsx(OverlayControls, {
507
- opacity: state.opacity,
508
- scale: 1,
509
- offsetX: 0,
510
- offsetY: 0,
511
- onOpacityChange: v => ImageOverlayController.setOpacity(v),
512
- onScaleChange: () => {},
513
- onOffsetChange: () => {},
514
- opacityOnly: true
577
+ children: "Reset Position"
515
578
  })
516
- }), /*#__PURE__*/_jsxs(View, {
517
- style: styles.toggleSection,
518
- children: [/*#__PURE__*/_jsxs(View, {
519
- style: styles.toggleRow,
520
- children: [/*#__PURE__*/_jsx(Text, {
521
- style: styles.toggleLabel,
522
- children: "Lock Position"
523
- }), /*#__PURE__*/_jsx(Switch, {
524
- value: state.locked,
525
- onValueChange: v => ImageOverlayController.setLocked(v),
526
- trackColor: {
527
- false: macOSColors.background.hover,
528
- true: "#f59e0b"
529
- }
530
- })]
531
- }), /*#__PURE__*/_jsxs(View, {
532
- style: styles.blendRow,
533
- children: [/*#__PURE__*/_jsx(Text, {
534
- style: styles.toggleLabel,
535
- children: "Opacity"
536
- }), /*#__PURE__*/_jsx(View, {
537
- style: styles.blendButtons,
538
- children: [0, 25, 50, 75, 100].map(pct => /*#__PURE__*/_jsx(TouchableOpacity, {
539
- style: [styles.blendButton, Math.round(state.opacity * 100) === pct && styles.blendButtonActive],
540
- onPress: () => ImageOverlayController.setOpacity(pct / 100),
541
- activeOpacity: 0.7,
542
- children: /*#__PURE__*/_jsxs(Text, {
543
- style: [styles.blendButtonText, Math.round(state.opacity * 100) === pct && styles.blendButtonTextActive],
544
- children: [pct, "%"]
545
- })
546
- }, pct))
547
- })]
548
- }), /*#__PURE__*/_jsxs(View, {
549
- style: styles.blendRow,
550
- children: [/*#__PURE__*/_jsx(Text, {
551
- style: styles.toggleLabel,
552
- children: "Flip"
553
- }), /*#__PURE__*/_jsxs(View, {
554
- style: styles.blendButtons,
555
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
556
- style: [styles.blendButton, state.invertX && styles.blendButtonActive],
557
- onPress: () => ImageOverlayController.toggleInvertX(),
558
- activeOpacity: 0.7,
559
- children: /*#__PURE__*/_jsx(Text, {
560
- style: [styles.blendButtonText, state.invertX && styles.blendButtonTextActive],
561
- children: "Horizontal"
562
- })
563
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
564
- style: [styles.blendButton, state.invertY && styles.blendButtonActive],
565
- onPress: () => ImageOverlayController.toggleInvertY(),
566
- activeOpacity: 0.7,
567
- children: /*#__PURE__*/_jsx(Text, {
568
- style: [styles.blendButtonText, state.invertY && styles.blendButtonTextActive],
569
- children: "Vertical"
570
- })
571
- })]
572
- })]
573
- })]
574
- }), /*#__PURE__*/_jsx(View, {
575
- style: styles.actions,
576
- children: /*#__PURE__*/_jsx(TouchableOpacity, {
577
- style: styles.actionButton,
578
- onPress: () => ImageOverlayController.resetSettings(),
579
- activeOpacity: 0.7,
580
- children: /*#__PURE__*/_jsx(Text, {
581
- style: styles.actionButtonText,
582
- children: "Reset Position"
583
- })
584
- })
585
- })]
579
+ })
586
580
  })]
587
581
  })]
588
- })
582
+ })]
583
+ });
584
+
585
+ // The ONLY difference between the phone and an embedded host is which shell
586
+ // the header and body go in. Everything above this is shared.
587
+ if (inline) {
588
+ return /*#__PURE__*/_jsxs(View, {
589
+ style: styles.inlineRoot,
590
+ children: [/*#__PURE__*/_jsx(View, {
591
+ style: styles.inlineHeader,
592
+ children: headerContent.customContent
593
+ }), body]
594
+ });
595
+ }
596
+ return /*#__PURE__*/_jsx(JsModal, {
597
+ visible: visible,
598
+ onClose: onClose,
599
+ onMinimize: onMinimize,
600
+ minimizeTargetPosition: minimizeTargetPosition,
601
+ initialModalState: initialModalState,
602
+ header: headerContent,
603
+ initialMode: "bottomSheet",
604
+ persistenceKey: "buoy-image-overlay-modal",
605
+ enablePersistence: true,
606
+ children: body
607
+ });
608
+ }
609
+
610
+ /**
611
+ * The same Image Overlay tool as `ImageOverlayModal`, rendered INLINE for hosts
612
+ * that place tools in a panel rather than a bottom sheet (the docs-site live
613
+ * demo). Exists so there is exactly one implementation of the tool's header,
614
+ * mode chooser, target list and controls.
615
+ *
616
+ * `initialView` seeds which screen it opens on — the view is otherwise internal
617
+ * state reachable only by tapping.
618
+ */
619
+ export function ImageOverlayToolView(props = {}) {
620
+ return /*#__PURE__*/_jsx(ImageOverlayModal, {
621
+ ...props,
622
+ inline: true,
623
+ visible: true,
624
+ onClose: () => {}
589
625
  });
590
626
  }
591
627
  const styles = StyleSheet.create({
628
+ inlineRoot: {
629
+ flex: 1,
630
+ minHeight: 0,
631
+ backgroundColor: macOSColors.background.base
632
+ },
633
+ inlineHeader: {
634
+ borderBottomWidth: 1,
635
+ borderBottomColor: macOSColors.border.default,
636
+ backgroundColor: macOSColors.background.card
637
+ },
592
638
  container: {
593
639
  flex: 1
594
640
  },