@farmslot/adapter-rn 0.28.0 → 0.29.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/CHANGELOG.md +4 -0
- package/package.json +4 -4
- package/react-native/RecipeHud.tsx +113 -36
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,10 @@ All notable changes to `@farmslot/adapter-rn` (published as `@farmslot/expo-reci
|
|
|
6
6
|
|
|
7
7
|
- Active-development baseline; add user-facing changes here before release or package publication.
|
|
8
8
|
|
|
9
|
+
## 0.29.0 - 2026-10-20
|
|
10
|
+
|
|
11
|
+
- The native recipe HUD drops the default RUN badge so the two-line intent gets the full width at 13px/17px, and adds a tiny muted row with the step that just finished (`✓`/`✗`) and `N/M`, within the unchanged 360 by 88 panel. A caller-supplied `text.badge` still renders.
|
|
12
|
+
|
|
9
13
|
## 0.28.0 - 2026-10-19
|
|
10
14
|
|
|
11
15
|
- Align with `@farmslot/protocol` 0.59.0, `@farmslot/recipe-runner` 0.45.0 and `@farmslot/agent-runtime` 0.35.0.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farmslot/adapter-rn",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -46,9 +46,9 @@
|
|
|
46
46
|
"prepublishOnly": "node ../../scripts/quality/check-farmslot-package-readiness.mjs --publish --packages @farmslot/adapter-rn"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@farmslot/agent-runtime": "0.
|
|
50
|
-
"@farmslot/protocol": "0.
|
|
51
|
-
"@farmslot/recipe-runner": "0.
|
|
49
|
+
"@farmslot/agent-runtime": "0.36.0",
|
|
50
|
+
"@farmslot/protocol": "0.60.0",
|
|
51
|
+
"@farmslot/recipe-runner": "0.46.0",
|
|
52
52
|
"pngjs": "^7.0.0",
|
|
53
53
|
"ws": "^8.18.0"
|
|
54
54
|
},
|
|
@@ -11,7 +11,16 @@ import {
|
|
|
11
11
|
import { initialWindowMetrics, SafeAreaInsetsContext } from 'react-native-safe-area-context';
|
|
12
12
|
|
|
13
13
|
import {
|
|
14
|
+
HUD_COLORS,
|
|
15
|
+
HUD_NATIVE_FINISHED_FONT_SIZE,
|
|
16
|
+
HUD_NATIVE_FINISHED_LINE_HEIGHT,
|
|
17
|
+
HUD_NATIVE_INTENT_LINE_HEIGHT,
|
|
18
|
+
HUD_NATIVE_INTENT_MAX_LINES,
|
|
19
|
+
HUD_PANEL_BOUNDS,
|
|
20
|
+
hudFinishedLine,
|
|
14
21
|
hudIntentDisplay,
|
|
22
|
+
type HudStepResult,
|
|
23
|
+
nextHudFinishedStep,
|
|
15
24
|
type RecipeHudPlacement,
|
|
16
25
|
waitForHudPaint,
|
|
17
26
|
} from '@farmslot/protocol/recipe';
|
|
@@ -25,6 +34,8 @@ export interface FarmslotRecipeHudState {
|
|
|
25
34
|
totalSteps?: number;
|
|
26
35
|
error?: string;
|
|
27
36
|
position?: RecipeHudPlacement;
|
|
37
|
+
/** The step that just finished; useRecipeHudState keeps it across updates. */
|
|
38
|
+
finished?: Pick<HudStepResult, 'intent' | 'status'>;
|
|
28
39
|
}
|
|
29
40
|
|
|
30
41
|
export type RecipeHudStateController = readonly [
|
|
@@ -37,18 +48,28 @@ interface HudAcknowledgement {
|
|
|
37
48
|
reject(error: unknown): void;
|
|
38
49
|
}
|
|
39
50
|
|
|
40
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* Bridge acknowledgement follows the committed HUD rather than the state setter.
|
|
53
|
+
* Updates carry the step that just finished, reset at each new run.
|
|
54
|
+
*/
|
|
41
55
|
export function useRecipeHudState(): RecipeHudStateController {
|
|
42
56
|
const [state, setState] = useState<FarmslotRecipeHudState | null>(null);
|
|
43
|
-
const
|
|
44
|
-
|
|
57
|
+
const received = useRef<FarmslotRecipeHudState | null>(null);
|
|
58
|
+
const lastResult = useRef<HudStepResult | null>(null);
|
|
45
59
|
const pending = useRef<HudAcknowledgement[]>([]);
|
|
46
60
|
const lifecycle = useRef<AbortController | null>(null);
|
|
47
61
|
const update = useCallback((next: FarmslotRecipeHudState | null) => {
|
|
48
|
-
if (next ===
|
|
62
|
+
if (next === received.current) return Promise.resolve();
|
|
63
|
+
received.current = next;
|
|
64
|
+
const step = nextHudFinishedStep(lastResult.current, next);
|
|
65
|
+
lastResult.current = step.last;
|
|
66
|
+
const shown =
|
|
67
|
+
next && next.finished === undefined && step.finished
|
|
68
|
+
? { ...next, finished: { intent: step.finished.intent, status: step.finished.status } }
|
|
69
|
+
: next;
|
|
49
70
|
return new Promise<void>((resolve, reject) => {
|
|
50
71
|
pending.current.push({ resolve, reject });
|
|
51
|
-
setState(
|
|
72
|
+
setState(shown);
|
|
52
73
|
});
|
|
53
74
|
}, []);
|
|
54
75
|
useEffect(() => {
|
|
@@ -108,6 +129,17 @@ export interface RecipeHudProps extends RecipeHudOptions {
|
|
|
108
129
|
state: FarmslotRecipeHudState | null;
|
|
109
130
|
}
|
|
110
131
|
|
|
132
|
+
const PADDING_VERTICAL = 3;
|
|
133
|
+
const ERROR_LINE_HEIGHT = 16;
|
|
134
|
+
// The native panel never grows: the finished row shows only while two intent
|
|
135
|
+
// lines, that row and an error line still fit its height.
|
|
136
|
+
const FINISHED_ROW_FITS =
|
|
137
|
+
PADDING_VERTICAL * 2 +
|
|
138
|
+
HUD_NATIVE_INTENT_LINE_HEIGHT * HUD_NATIVE_INTENT_MAX_LINES +
|
|
139
|
+
HUD_NATIVE_FINISHED_LINE_HEIGHT +
|
|
140
|
+
ERROR_LINE_HEIGHT <=
|
|
141
|
+
HUD_PANEL_BOUNDS.native.maxHeight;
|
|
142
|
+
|
|
111
143
|
export function RecipeHud({
|
|
112
144
|
state,
|
|
113
145
|
styles: customStyles,
|
|
@@ -117,10 +149,15 @@ export function RecipeHud({
|
|
|
117
149
|
}: Readonly<RecipeHudProps>): React.ReactElement | null {
|
|
118
150
|
const insets = useContext(SafeAreaInsetsContext) ?? initialWindowMetrics?.insets;
|
|
119
151
|
if (!state || state.status === 'idle') return null;
|
|
120
|
-
const badge = text?.badge?.(state)
|
|
152
|
+
const badge = text?.badge?.(state);
|
|
121
153
|
const intent = hudIntentDisplay(text?.intent?.(state) ?? state.intent, 'native');
|
|
122
154
|
const error = text?.error?.(state) ?? state.error;
|
|
123
155
|
const badgeTone = badgeToneForStatus(state.status);
|
|
156
|
+
const finished =
|
|
157
|
+
FINISHED_ROW_FITS && state.finished
|
|
158
|
+
? hudFinishedLine({ nodeId: 'finished', ...state.finished })
|
|
159
|
+
: null;
|
|
160
|
+
const progress = FINISHED_ROW_FITS ? formatProgress(state.currentStep, state.totalSteps) : '';
|
|
124
161
|
const overlay = (
|
|
125
162
|
<View
|
|
126
163
|
onLayout={onLayout}
|
|
@@ -141,39 +178,67 @@ export function RecipeHud({
|
|
|
141
178
|
{ overflow: 'hidden', flexDirection: 'row' },
|
|
142
179
|
]}
|
|
143
180
|
>
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
181
|
+
{badge ? (
|
|
182
|
+
<Text
|
|
183
|
+
numberOfLines={1}
|
|
184
|
+
allowFontScaling={false}
|
|
185
|
+
style={[
|
|
186
|
+
styles.badgeText,
|
|
187
|
+
badgeTone === 'fail'
|
|
188
|
+
? styles.badgeTextFail
|
|
189
|
+
: badgeTone === 'pass'
|
|
190
|
+
? styles.badgeTextPass
|
|
191
|
+
: styles.badgeTextRunning,
|
|
192
|
+
customStyles?.badgeText,
|
|
193
|
+
badgeTone === 'fail'
|
|
194
|
+
? customStyles?.badgeTextFail
|
|
195
|
+
: badgeTone === 'pass'
|
|
196
|
+
? customStyles?.badgeTextPass
|
|
197
|
+
: customStyles?.badgeTextRunning,
|
|
198
|
+
{ maxWidth: 80, marginRight: 8 },
|
|
199
|
+
]}
|
|
200
|
+
>
|
|
201
|
+
{badge}
|
|
202
|
+
</Text>
|
|
203
|
+
) : null}
|
|
165
204
|
<View style={{ flex: 1, minWidth: 0 }}>
|
|
166
205
|
<Text
|
|
167
206
|
{...intent.nativeProps}
|
|
168
|
-
style={[
|
|
207
|
+
style={[
|
|
208
|
+
styles.line,
|
|
209
|
+
state.status === 'fail' ? styles.failed : undefined,
|
|
210
|
+
customStyles?.line,
|
|
211
|
+
customStyles?.intent,
|
|
212
|
+
intent.nativeStyle,
|
|
213
|
+
]}
|
|
169
214
|
>
|
|
170
215
|
{intent.text}
|
|
171
216
|
</Text>
|
|
217
|
+
{finished || progress ? (
|
|
218
|
+
<View style={styles.finishedRow}>
|
|
219
|
+
<Text
|
|
220
|
+
numberOfLines={1}
|
|
221
|
+
allowFontScaling={false}
|
|
222
|
+
style={[styles.finished, finished?.tone === 'fail' ? styles.failed : undefined]}
|
|
223
|
+
>
|
|
224
|
+
{finished?.text}
|
|
225
|
+
</Text>
|
|
226
|
+
{progress ? (
|
|
227
|
+
<Text numberOfLines={1} allowFontScaling={false} style={styles.progress}>
|
|
228
|
+
{progress}
|
|
229
|
+
</Text>
|
|
230
|
+
) : null}
|
|
231
|
+
</View>
|
|
232
|
+
) : null}
|
|
172
233
|
{error ? (
|
|
173
234
|
<Text
|
|
174
235
|
numberOfLines={1}
|
|
175
236
|
allowFontScaling={false}
|
|
176
|
-
style={[
|
|
237
|
+
style={[
|
|
238
|
+
styles.error,
|
|
239
|
+
customStyles?.error,
|
|
240
|
+
{ lineHeight: ERROR_LINE_HEIGHT, maxHeight: ERROR_LINE_HEIGHT },
|
|
241
|
+
]}
|
|
177
242
|
>
|
|
178
243
|
{error}
|
|
179
244
|
</Text>
|
|
@@ -188,12 +253,6 @@ export function RecipeHud({
|
|
|
188
253
|
);
|
|
189
254
|
}
|
|
190
255
|
|
|
191
|
-
function formatBadge(state: FarmslotRecipeHudState): string {
|
|
192
|
-
const status = state.status === 'running' ? 'run' : state.status;
|
|
193
|
-
const progress = formatProgress(state.currentStep, state.totalSteps);
|
|
194
|
-
return [status, progress].filter(Boolean).join(' ').toUpperCase();
|
|
195
|
-
}
|
|
196
|
-
|
|
197
256
|
function formatProgress(currentStep?: number, totalSteps?: number): string {
|
|
198
257
|
if (!currentStep || !totalSteps) return '';
|
|
199
258
|
return `${currentStep}/${totalSteps}`;
|
|
@@ -214,7 +273,7 @@ const styles = StyleSheet.create({
|
|
|
214
273
|
zIndex: 9999,
|
|
215
274
|
backgroundColor: 'rgba(0, 0, 0, 0.58)',
|
|
216
275
|
paddingHorizontal: 10,
|
|
217
|
-
paddingVertical:
|
|
276
|
+
paddingVertical: PADDING_VERTICAL,
|
|
218
277
|
},
|
|
219
278
|
line: {
|
|
220
279
|
color: '#ffffff',
|
|
@@ -235,6 +294,24 @@ const styles = StyleSheet.create({
|
|
|
235
294
|
badgeTextFail: {
|
|
236
295
|
color: '#ff4d4f',
|
|
237
296
|
},
|
|
297
|
+
finishedRow: {
|
|
298
|
+
flexDirection: 'row',
|
|
299
|
+
},
|
|
300
|
+
finished: {
|
|
301
|
+
flex: 1,
|
|
302
|
+
color: HUD_COLORS.muted,
|
|
303
|
+
fontSize: HUD_NATIVE_FINISHED_FONT_SIZE,
|
|
304
|
+
lineHeight: HUD_NATIVE_FINISHED_LINE_HEIGHT,
|
|
305
|
+
},
|
|
306
|
+
failed: {
|
|
307
|
+
color: HUD_COLORS.fail,
|
|
308
|
+
},
|
|
309
|
+
progress: {
|
|
310
|
+
marginLeft: 6,
|
|
311
|
+
color: HUD_COLORS.muted,
|
|
312
|
+
fontSize: HUD_NATIVE_FINISHED_FONT_SIZE,
|
|
313
|
+
lineHeight: HUD_NATIVE_FINISHED_LINE_HEIGHT,
|
|
314
|
+
},
|
|
238
315
|
error: {
|
|
239
316
|
color: '#e6e6e6',
|
|
240
317
|
fontSize: 10,
|