@buoy-gg/image-overlay 7.0.17 → 7.0.19

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