Skip to content

Commit 643c9b8

Browse files
committed
refactor(screenshot): dedupe the meaningful-signal predicate and polish png-crop
- Hoist isMeaningfulSignal into @agent-device/contracts/snapshot (next to normalizeType/isMeaningfulLabel) so the ref overlay and the crop rect-projection share one copy instead of each carrying an identical private predicate. Behavior is unchanged. - png-crop: isCropBox was a no-op 'box is Rect' predicate (input already Rect) — make it a plain boolean, and tighten the doc to the contract.
1 parent c5be390 commit 643c9b8

5 files changed

Lines changed: 20 additions & 22 deletions

File tree

packages/capture-kit/src/png-crop.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ import { PNG } from './png.ts';
55
import { decodePngAsync, encodePngAsync } from './png-worker-client.ts';
66

77
/**
8-
* Crops a PNG file in place to `box` (daemon screenshot `--crop-on` path). Decode and encode
9-
* run on the PNG worker thread like the `--scale` resize; the row copy is a straight blit.
10-
* The box is the caller's already-intersected crop region, so a box outside the image is
11-
* a caller bug this refuses rather than clamps.
8+
* Crops `filePath` in place to `box` (positive integer pixels). `box` is the caller's
9+
* already-intersected region, so one outside the image is a caller bug — refused, not clamped.
10+
* Decode and encode run on the PNG worker thread; a full-image box is a no-op.
1211
*/
1312
export async function cropPngFile(filePath: string, box: Rect): Promise<void> {
1413
if (!isCropBox(box)) {
@@ -32,7 +31,7 @@ export async function cropPngFile(filePath: string, box: Rect): Promise<void> {
3231
await fs.writeFile(filePath, await encodePngAsync(cropPngBox(source, box)));
3332
}
3433

35-
function isCropBox(box: Rect): box is Rect {
34+
function isCropBox(box: Rect): boolean {
3635
return (
3736
Number.isInteger(box.x) &&
3837
box.x >= 0 &&

packages/capture-kit/src/snapshot-rect-projection.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { AppError } from '@agent-device/kernel/errors';
22
import { SCREENSHOT_CROP_REASONS } from '@agent-device/contracts/capture';
3-
import { isViewportRootNode, normalizeType } from '@agent-device/contracts/snapshot';
3+
import {
4+
isMeaningfulSignal,
5+
isViewportRootNode,
6+
normalizeType,
7+
} from '@agent-device/contracts/snapshot';
48
import { isPositiveFiniteRect, rectArea } from '@agent-device/kernel/rect';
59
import type { Rect, SnapshotNode } from '@agent-device/kernel/snapshot';
610

@@ -121,14 +125,7 @@ function measureSnapshotBounds(nodes: ReadonlyArray<Pick<SnapshotNode, 'rect'>>)
121125
}
122126

123127
function isSnapshotBoundsOutlier(node: Pick<SnapshotNode, 'type' | 'label'>): boolean {
124-
return normalizeType(node.type ?? '') === 'image' && !isMeaningfulBoundsSignal(node.label);
125-
}
126-
127-
function isMeaningfulBoundsSignal(value: string | undefined): boolean {
128-
if (typeof value !== 'string') return false;
129-
const trimmed = value.trim();
130-
if (!trimmed) return false;
131-
return !/^(true|false)$/i.test(trimmed);
128+
return normalizeType(node.type ?? '') === 'image' && !isMeaningfulSignal(node.label);
132129
}
133130

134131
function roundRect(rect: Rect): Rect {

packages/contracts/src/facades/snapshot.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,6 @@ export {
2626
extractNodeText,
2727
isFillableType,
2828
isMeaningfulLabel,
29+
isMeaningfulSignal,
2930
normalizeType,
3031
} from '../snapshot-text.ts';

packages/contracts/src/snapshot-text.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ export function isMeaningfulLabel(value: string): boolean {
3838
return true;
3939
}
4040

41+
/** A non-empty, non-boolean `label`/`value` is a usable overlay or crop signal. */
42+
export function isMeaningfulSignal(value: string | undefined): boolean {
43+
if (typeof value !== 'string') return false;
44+
const trimmed = value.trim();
45+
if (!trimmed) return false;
46+
return !/^(true|false)$/i.test(trimmed);
47+
}
48+
4149
export function extractNodeText(
4250
node: Pick<RawSnapshotNode, 'label' | 'value' | 'identifier'>,
4351
): string {

src/daemon/screenshot-overlay.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
import { analyzeReactNativeOverlay } from '../core/react-native-overlay.ts';
1515
import {
1616
findNearestAncestor,
17+
isMeaningfulSignal,
1718
isViewportRootNode,
1819
normalizeType,
1920
} from '@agent-device/contracts/snapshot';
@@ -296,14 +297,6 @@ function isUsableOverlayTarget(node: SnapshotNode | null): node is SnapshotNode
296297
return Boolean(node?.rect && hasPositiveRect(node.rect) && !isViewportRootNode(node));
297298
}
298299

299-
function isMeaningfulSignal(value: string | undefined): boolean {
300-
if (typeof value !== 'string') return false;
301-
const trimmed = value.trim();
302-
if (!trimmed) return false;
303-
if (/^(true|false)$/i.test(trimmed)) return false;
304-
return true;
305-
}
306-
307300
function isOverlaySignal(value: string | undefined): boolean {
308301
if (!isMeaningfulSignal(value)) return false;
309302
return !isGenericOverlayLabel(value);

0 commit comments

Comments
 (0)