getobsrv 0.9.0 → 0.10.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/README.md
CHANGED
|
@@ -73,11 +73,16 @@ the advanced sliders. Enter your own monitor's diagonal in Settings once so the
|
|
|
73
73
|
pane renders at true physical size. Drop a 2x/3x PNG or JPG export to check a design
|
|
74
74
|
before it's built.
|
|
75
75
|
|
|
76
|
-
The target pane opens
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
The target pane opens in **Fit**, a fit-to-pane view of the whole render. Fit
|
|
77
|
+
never enlarges past 1:1, so a render that already fits is shown at true size;
|
|
78
|
+
a larger one is smoothly minified to fit, which is not pixel-exact — the footer
|
|
79
|
+
says so, and names the magnification. Fit is fully interactive: clicks, drags,
|
|
80
|
+
the wheel and the keyboard all reach the page.
|
|
81
|
+
|
|
82
|
+
Switch the toolbar's `1:1 / Fit` control to **1:1** for true magnification,
|
|
83
|
+
which usually overflows the pane: pan with a middle-button drag, Option+drag or
|
|
84
|
+
Option+wheel. From fit, **Option+click** jumps straight to 1:1 with the clicked
|
|
85
|
+
spot centred (plain clicks belong to the page).
|
|
81
86
|
|
|
82
87
|
The `Both / Target` control beside it hides the native pane so the target render
|
|
83
88
|
takes the whole window — useful for a small mobile preset that would otherwise
|
package/out/main/index.js
CHANGED
|
@@ -792,7 +792,7 @@ function registerIpc(ctx) {
|
|
|
792
792
|
settings = s;
|
|
793
793
|
if (s.agentControl !== wasEnabled) applyAgentControl(s.agentControl);
|
|
794
794
|
});
|
|
795
|
-
const uiState = { presetId: "1080p-24", profileId: "reference", viewMode: "
|
|
795
|
+
const uiState = { presetId: "1080p-24", profileId: "reference", viewMode: "fit", panes: "both", mode: "url" };
|
|
796
796
|
let targetBounds = null;
|
|
797
797
|
let canvasBounds = null;
|
|
798
798
|
const MAX_PENDING_APPLIES = 32;
|
|
@@ -12782,7 +12782,12 @@ const useStore = create()((set) => ({
|
|
|
12782
12782
|
update: null,
|
|
12783
12783
|
image: null,
|
|
12784
12784
|
surround: "graphite",
|
|
12785
|
-
|
|
12785
|
+
// Fit, not 1:1: fit never enlarges past 1:1, so a render that already fits
|
|
12786
|
+
// its pane opens at true magnification anyway, while one that does not is
|
|
12787
|
+
// shown whole instead of as its top-left corner. Fit is interactive, so
|
|
12788
|
+
// this costs nothing — and the footer names the actual magnification
|
|
12789
|
+
// whenever fit is minifying.
|
|
12790
|
+
viewMode: "fit",
|
|
12786
12791
|
panes: "both",
|
|
12787
12792
|
fitScale: null,
|
|
12788
12793
|
agentPan: null,
|
|
@@ -14025,7 +14030,6 @@ function TargetCanvas({ onFatal, imageFrame }) {
|
|
|
14025
14030
|
canvas.addEventListener("wheel", onWheel, { passive: false });
|
|
14026
14031
|
const onWindowUp = (e) => {
|
|
14027
14032
|
if (useStore.getState().mode !== "url") return;
|
|
14028
|
-
if (useStore.getState().viewMode !== "1:1") return;
|
|
14029
14033
|
if (panRef.current || e.button === 1) return;
|
|
14030
14034
|
if (e.altKey && !forwardDrag.current) return;
|
|
14031
14035
|
if (e.target === canvas) return;
|
|
@@ -14099,7 +14103,7 @@ function TargetCanvas({ onFatal, imageFrame }) {
|
|
|
14099
14103
|
return body instanceof HTMLElement ? body : null;
|
|
14100
14104
|
};
|
|
14101
14105
|
const startPan = (e) => {
|
|
14102
|
-
if (
|
|
14106
|
+
if (fit) return;
|
|
14103
14107
|
if (!(e.button === 1 || e.button === 0 && e.altKey)) return;
|
|
14104
14108
|
const body = paneBody();
|
|
14105
14109
|
if (!body) return;
|
|
@@ -14148,7 +14152,7 @@ function TargetCanvas({ onFatal, imageFrame }) {
|
|
|
14148
14152
|
pendingJump.current = null;
|
|
14149
14153
|
}, [viewMode]);
|
|
14150
14154
|
const jumpTo1x = (e) => {
|
|
14151
|
-
if (
|
|
14155
|
+
if (!fit || !e.altKey) return;
|
|
14152
14156
|
const r = e.currentTarget.getBoundingClientRect();
|
|
14153
14157
|
pendingJump.current = jumpScroll(
|
|
14154
14158
|
e.clientX - r.left,
|
|
@@ -14189,7 +14193,7 @@ function TargetCanvas({ onFatal, imageFrame }) {
|
|
|
14189
14193
|
}, [agentHighlight, clearAgentHighlight]);
|
|
14190
14194
|
const send = (type) => (e) => {
|
|
14191
14195
|
if (mode !== "url") return;
|
|
14192
|
-
if (
|
|
14196
|
+
if (panRef.current || e.button === 1) return;
|
|
14193
14197
|
if (e.altKey && !forwardDrag.current) return;
|
|
14194
14198
|
const out = mouseEvent(type, e, e.currentTarget.getBoundingClientRect(), scale * dsf / dpr);
|
|
14195
14199
|
if (out) {
|
|
@@ -14220,7 +14224,7 @@ function TargetCanvas({ onFatal, imageFrame }) {
|
|
|
14220
14224
|
"canvas",
|
|
14221
14225
|
{
|
|
14222
14226
|
ref: canvasRef,
|
|
14223
|
-
className: `target-canvas${
|
|
14227
|
+
className: `target-canvas${panning ? " panning" : altHeld ? fit ? " fit" : " pan-ready" : ""}`,
|
|
14224
14228
|
tabIndex: 0,
|
|
14225
14229
|
style: { width: `${cssW}px`, height: `${cssH}px` },
|
|
14226
14230
|
onClick: jumpTo1x,
|
|
@@ -14232,12 +14236,12 @@ function TargetCanvas({ onFatal, imageFrame }) {
|
|
|
14232
14236
|
onMouseUp: send("mouseUp"),
|
|
14233
14237
|
onMouseMove: send("mouseMove"),
|
|
14234
14238
|
onKeyDown: (e) => {
|
|
14235
|
-
if (mode !== "url"
|
|
14239
|
+
if (mode !== "url") return;
|
|
14236
14240
|
if (!e.metaKey && !e.ctrlKey) e.preventDefault();
|
|
14237
14241
|
for (const ev of keyDownEvents(e)) window.obsrv.sendInput(ev);
|
|
14238
14242
|
},
|
|
14239
14243
|
onKeyUp: (e) => {
|
|
14240
|
-
if (mode !== "url"
|
|
14244
|
+
if (mode !== "url") return;
|
|
14241
14245
|
const ev = keyUpEvent(e);
|
|
14242
14246
|
if (ev) window.obsrv.sendInput(ev);
|
|
14243
14247
|
}
|
|
@@ -551,6 +551,27 @@ html, body, #root { margin: 0; height: 100%; background: var(--chrome-0); color:
|
|
|
551
551
|
`left: 0` holds while a wide canvas is scrolled horizontally. The fill is a
|
|
552
552
|
neutral grey: only the rule under it is red, so the one chromatic thing on
|
|
553
553
|
screen is the error itself. */
|
|
554
|
+
/* At 1:1 the render usually overflows and the pane is a scroll box, so it
|
|
555
|
+
starts at the top-left and you pan around it. When it is *smaller* than the
|
|
556
|
+
pane — a small preset, fit mode, or solo target on a wide window — hugging
|
|
557
|
+
the corner leaves it stranded against the chrome with dead field to the
|
|
558
|
+
right. Centre it instead.
|
|
559
|
+
|
|
560
|
+
`safe` is what lets one rule serve both cases: it falls back to start
|
|
561
|
+
alignment the moment the content overflows, so the top-left of an oversized
|
|
562
|
+
render stays reachable by scrolling. Plain `center` would push it out of
|
|
563
|
+
reach. The image pane already centres this way. */
|
|
564
|
+
.target-pane .pane-body {
|
|
565
|
+
display: flex;
|
|
566
|
+
flex-direction: column;
|
|
567
|
+
align-items: safe center;
|
|
568
|
+
justify-content: safe center;
|
|
569
|
+
}
|
|
570
|
+
/* The stall notice spans the pane, not the canvas: it is sticky at `left: 0`
|
|
571
|
+
and must stay pane-width while a wide canvas scrolls under it, so it opts
|
|
572
|
+
out of the centring its sibling wants. */
|
|
573
|
+
.target-pane .pane-body > .stall { align-self: stretch; }
|
|
574
|
+
|
|
554
575
|
/* position: relative anchors the agent highlight to the canvas origin. */
|
|
555
576
|
.target-wrap { min-width: min-content; position: relative; }
|
|
556
577
|
|
|
@@ -598,8 +619,10 @@ html, body, #root { margin: 0; height: 100%; background: var(--chrome-0); color:
|
|
|
598
619
|
and being (0,1,1) and later in the file they beat `.segmented button` and
|
|
599
620
|
split the two groups apart. Nothing about 1:1/Fit is control-specific. */
|
|
600
621
|
|
|
601
|
-
/*
|
|
602
|
-
|
|
622
|
+
/* Option is what a view gesture needs, so Option is what the cursor answers:
|
|
623
|
+
the jump-to-1:1 zoom in fit, the grab affordances for the pan chords
|
|
624
|
+
(middle drag, Option+drag) at 1:1. Unmodified, both views are interactive
|
|
625
|
+
and the page's own cursors show through. Cursors only — no overlay may sit
|
|
603
626
|
on the pixels under inspection. */
|
|
604
627
|
.target-canvas.fit { cursor: zoom-in; }
|
|
605
628
|
.target-canvas.pan-ready { cursor: grab; }
|
package/out/renderer/index.html
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' blob: data:" />
|
|
6
6
|
<title>Obsrv</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
7
|
+
<script type="module" crossorigin src="./assets/index-CQVE5hF3.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="./assets/index-CZUlXhEb.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|