@blario/react-native 0.1.3 → 0.1.5
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 +16 -2
- package/dist/actions/scrolling.d.ts +4 -0
- package/dist/actions/select.d.ts +16 -0
- package/dist/actions/types.d.ts +3 -0
- package/dist/backend/app-info.d.ts +1 -0
- package/dist/fiber/text.d.ts +1 -0
- package/dist/snapshot/collect.d.ts +1 -0
- package/dist/snapshot/scroll.d.ts +2 -0
- package/dist/snapshot/view.d.ts +3 -0
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/actions/click.ts +3 -1
- package/src/actions/executor.ts +3 -0
- package/src/actions/landing.ts +2 -2
- package/src/actions/parse.ts +2 -0
- package/src/actions/scrolling.ts +8 -3
- package/src/actions/select.ts +162 -0
- package/src/actions/types.ts +1 -0
- package/src/backend/app-info.ts +3 -0
- package/src/chat/widget-bundle.generated.ts +2 -2
- package/src/fiber/text.ts +2 -0
- package/src/snapshot/capture.ts +1 -0
- package/src/snapshot/collect.ts +15 -2
- package/src/snapshot/render.ts +3 -1
- package/src/snapshot/scroll.ts +12 -0
- package/src/snapshot/view.ts +34 -0
- package/src/snapshot/wrappers.ts +2 -1
- package/src/version.ts +1 -1
package/src/fiber/text.ts
CHANGED
|
@@ -3,6 +3,8 @@ import type { Fiber } from './types.ts';
|
|
|
3
3
|
|
|
4
4
|
const PRIVATE_USE_GLYPHS = /[-\u{F0000}-\u{10FFFF}]/gu;
|
|
5
5
|
|
|
6
|
+
export const TEXT_HOST_TYPE = 'RCTText';
|
|
7
|
+
|
|
6
8
|
export function normalizeText(value: string): string {
|
|
7
9
|
return value.replace(PRIVATE_USE_GLYPHS, '').replace(/\s+/g, ' ').trim();
|
|
8
10
|
}
|
package/src/snapshot/capture.ts
CHANGED
|
@@ -124,6 +124,7 @@ export class SnapshotCapturer {
|
|
|
124
124
|
rect,
|
|
125
125
|
scroll: candidate.kind === 'scroll' ? describeScrollDirections(geometries.get(candidate.fiber) ?? null) : null,
|
|
126
126
|
offscreen,
|
|
127
|
+
wheelOption: candidate.wheelOption,
|
|
127
128
|
}),
|
|
128
129
|
);
|
|
129
130
|
if (mode === 'register') {
|
package/src/snapshot/collect.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { childrenOf, componentName, firstHostDescendant, isHostComponent, propsOf } from '../fiber/reader.ts';
|
|
2
|
-
import { collectText } from '../fiber/text.ts';
|
|
2
|
+
import { collectText, TEXT_HOST_TYPE } from '../fiber/text.ts';
|
|
3
3
|
import type { Fiber } from '../fiber/types.ts';
|
|
4
4
|
import { classify } from './classify.ts';
|
|
5
5
|
import type { ElementKind } from './kinds.ts';
|
|
6
6
|
import { isFocusedScreen, type ScreenRef, screenOf } from './navigation.ts';
|
|
7
7
|
import { roleOf } from './properties.ts';
|
|
8
|
+
import { isWheel } from './scroll.ts';
|
|
8
9
|
|
|
9
10
|
export const AGENT_OVERLAY_TEST_ID = 'blar-agent-overlay';
|
|
10
11
|
|
|
@@ -14,10 +15,11 @@ export interface Candidate {
|
|
|
14
15
|
host: Fiber;
|
|
15
16
|
ancestors: Fiber[];
|
|
16
17
|
scrollContainers: Fiber[];
|
|
18
|
+
wheelOption?: boolean;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
const MODAL_COMPONENT_NAME = 'Modal';
|
|
20
|
-
const
|
|
22
|
+
const WHEEL_OPTION_LIMIT = 24;
|
|
21
23
|
const TEXT_BLOCK_LIMIT = 800;
|
|
22
24
|
const HEADING_ROLES: readonly string[] = ['header', 'heading'];
|
|
23
25
|
|
|
@@ -75,6 +77,9 @@ export function collect(root: Fiber): Collected {
|
|
|
75
77
|
const block = readTextBlock(fiber, ancestors, scrollContainers);
|
|
76
78
|
if (block !== null) {
|
|
77
79
|
textBlocks.push(block);
|
|
80
|
+
if (isWheelOption(block)) {
|
|
81
|
+
candidates.push({ kind: 'button', fiber, host: fiber, ancestors: block.ancestors, scrollContainers: block.scrollContainers, wheelOption: true });
|
|
82
|
+
}
|
|
78
83
|
}
|
|
79
84
|
ancestors.push(fiber);
|
|
80
85
|
if (kind === 'scroll') {
|
|
@@ -118,6 +123,14 @@ function readTextBlock(fiber: Fiber, ancestors: Fiber[], scrollContainers: Fiber
|
|
|
118
123
|
};
|
|
119
124
|
}
|
|
120
125
|
|
|
126
|
+
function isWheelOption(block: TextBlock): boolean {
|
|
127
|
+
const wheel = block.scrollContainers[block.scrollContainers.length - 1];
|
|
128
|
+
if (wheel === undefined || !isWheel(wheel) || block.text.length > WHEEL_OPTION_LIMIT) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
return !block.ancestors.slice(block.ancestors.lastIndexOf(wheel)).some((ancestor) => classify(ancestor) === 'button');
|
|
132
|
+
}
|
|
133
|
+
|
|
121
134
|
export function isOpenModal(fiber: Fiber): boolean {
|
|
122
135
|
return componentName(fiber) === MODAL_COMPONENT_NAME && fiber.child !== null && propsOf(fiber).visible !== false;
|
|
123
136
|
}
|
package/src/snapshot/render.ts
CHANGED
|
@@ -51,6 +51,7 @@ function renderButton(view: ElementView, twins: number): string {
|
|
|
51
51
|
const tag = openTag('button', [
|
|
52
52
|
['role', view.role],
|
|
53
53
|
['testID', view.testID],
|
|
54
|
+
['list', view.list],
|
|
54
55
|
['icon', view.icon],
|
|
55
56
|
['offscreen', view.offscreen],
|
|
56
57
|
...commonStateAttributes(view),
|
|
@@ -94,6 +95,7 @@ function renderSwitch(view: ElementView, twins: number): string {
|
|
|
94
95
|
function renderScroll(view: ElementView): string {
|
|
95
96
|
const tag = openTag('scroll', [
|
|
96
97
|
['testID', view.testID],
|
|
98
|
+
['list', view.list],
|
|
97
99
|
['scroll', view.scroll],
|
|
98
100
|
]);
|
|
99
101
|
return `${tag} />`;
|
|
@@ -113,7 +115,7 @@ function renderBody(view: ElementView, twins: number): string {
|
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
export function twinSignature(view: ElementView): string {
|
|
116
|
-
return [view.kind, view.label, view.icon ?? '', view.role ?? ''].join('|');
|
|
118
|
+
return [view.kind, view.label, view.icon ?? '', view.role ?? '', view.list ?? ''].join('|');
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
export function countTwins(views: ElementView[]): Map<string, number> {
|
package/src/snapshot/scroll.ts
CHANGED
|
@@ -43,6 +43,12 @@ export function isHorizontal(fiber: Fiber): boolean {
|
|
|
43
43
|
return propsOf(fiber).horizontal === true;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
// A wheel picker: a snapping list whose rows are plain text, where scrolling a row into the center is what selects it.
|
|
47
|
+
export function isWheel(fiber: Fiber): boolean {
|
|
48
|
+
const { snapToInterval, snapToOffsets } = propsOf(fiber);
|
|
49
|
+
return typeof snapToInterval === 'number' || Array.isArray(snapToOffsets);
|
|
50
|
+
}
|
|
51
|
+
|
|
46
52
|
export async function measureScrollGeometry(scrollFiber: Fiber): Promise<ScrollGeometry | null> {
|
|
47
53
|
const scrollHost = findScrollHost(scrollFiber);
|
|
48
54
|
if (scrollHost === null) {
|
|
@@ -73,6 +79,12 @@ export function viewportExtentOf(geometry: ScrollGeometry): number {
|
|
|
73
79
|
return geometry.horizontal ? geometry.viewport.width : geometry.viewport.height;
|
|
74
80
|
}
|
|
75
81
|
|
|
82
|
+
export function offsetCentering(rect: Rect, geometry: ScrollGeometry): number {
|
|
83
|
+
const { viewport, horizontal } = geometry;
|
|
84
|
+
const delta = horizontal ? rect.x + rect.width / 2 - (viewport.x + viewport.width / 2) : rect.y + rect.height / 2 - (viewport.y + viewport.height / 2);
|
|
85
|
+
return Math.min(Math.max(scrollOffsetOf(geometry) + delta, 0), maxScrollOffsetOf(geometry));
|
|
86
|
+
}
|
|
87
|
+
|
|
76
88
|
export function scrollDirectionsOf(geometry: ScrollGeometry): ScrollDirection[] {
|
|
77
89
|
const offset = scrollOffsetOf(geometry);
|
|
78
90
|
const canBackward = offset > SCROLL_TOLERANCE;
|
package/src/snapshot/view.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { collectText, precedingText } from '../fiber/text.ts';
|
|
|
2
2
|
import { propsOf } from '../fiber/reader.ts';
|
|
3
3
|
import type { Fiber } from '../fiber/types.ts';
|
|
4
4
|
import type { Rect } from '../host/rect.ts';
|
|
5
|
+
import { scrollInstanceOf } from './classify.ts';
|
|
5
6
|
import { iconNameIn } from './icon.ts';
|
|
6
7
|
import type { Offscreen } from './visibility.ts';
|
|
7
8
|
import type { ElementKind } from './kinds.ts';
|
|
@@ -33,9 +34,24 @@ export interface ElementView {
|
|
|
33
34
|
selected: boolean | null;
|
|
34
35
|
scroll: string | null;
|
|
35
36
|
offscreen: Offscreen | null;
|
|
37
|
+
list: string | null;
|
|
36
38
|
rect: Rect;
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
const LIST_NAME_DEPTH = 8;
|
|
42
|
+
|
|
43
|
+
// What a list is called: hosts name the view around it, as AgendaPro does with Hours and Minutes.
|
|
44
|
+
export function listNameOf(list: Fiber, ancestors: Fiber[]): string | null {
|
|
45
|
+
for (const fiber of [list, ...ancestors.slice(-LIST_NAME_DEPTH).reverse()]) {
|
|
46
|
+
const props = propsOf(fiber);
|
|
47
|
+
const name = explicitLabelOf(props) ?? testIdOf(props);
|
|
48
|
+
if (name !== null) {
|
|
49
|
+
return name;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
39
55
|
function labelFor(kind: ElementKind, fiber: Fiber, ancestors: Fiber[]): string {
|
|
40
56
|
const explicit = explicitLabelOf(propsOf(fiber));
|
|
41
57
|
if (explicit !== null) {
|
|
@@ -57,6 +73,23 @@ export interface ViewSource {
|
|
|
57
73
|
rect: Rect;
|
|
58
74
|
scroll: string | null;
|
|
59
75
|
offscreen?: Offscreen | null;
|
|
76
|
+
wheelOption?: boolean;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function listOfView({ kind, fiber, ancestors, wheelOption }: ViewSource): string | null {
|
|
80
|
+
if (kind === 'scroll') {
|
|
81
|
+
return listNameOf(fiber, ancestors);
|
|
82
|
+
}
|
|
83
|
+
if (wheelOption !== true) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
for (let index = ancestors.length - 1; index >= 0; index -= 1) {
|
|
87
|
+
const ancestor = ancestors[index];
|
|
88
|
+
if (ancestor !== undefined && scrollInstanceOf(ancestor) !== null) {
|
|
89
|
+
return listNameOf(ancestor, ancestors.slice(0, index));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
60
93
|
}
|
|
61
94
|
|
|
62
95
|
export function buildView(source: ViewSource): ElementView {
|
|
@@ -82,6 +115,7 @@ export function buildView(source: ViewSource): ElementView {
|
|
|
82
115
|
selected: flags.selected,
|
|
83
116
|
scroll,
|
|
84
117
|
offscreen,
|
|
118
|
+
list: listOfView(source),
|
|
85
119
|
rect,
|
|
86
120
|
};
|
|
87
121
|
}
|
package/src/snapshot/wrappers.ts
CHANGED
|
@@ -60,7 +60,8 @@ export function dropWrapperDuplicates(measured: MeasuredCandidate[]): MeasuredCa
|
|
|
60
60
|
const OFFSCREEN_LIMIT = 30;
|
|
61
61
|
|
|
62
62
|
export function limitOffscreen(measured: MeasuredCandidate[]): MeasuredCandidate[] {
|
|
63
|
-
|
|
63
|
+
// Every option of a wheel stays listed: one dropped here could never be selected.
|
|
64
|
+
const hidden = measured.filter((entry) => entry.offscreen !== null && entry.candidate.kind !== 'scroll' && entry.candidate.wheelOption !== true);
|
|
64
65
|
if (hidden.length <= OFFSCREEN_LIMIT) {
|
|
65
66
|
return measured;
|
|
66
67
|
}
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const SDK_VERSION = '0.1.
|
|
1
|
+
export const SDK_VERSION = '0.1.5';
|