|
1 | 1 | import previewStreamScript from "@t3tools/mobile-preview-stream"; |
2 | 2 | import { |
3 | 3 | previewStreamControlLabel, |
| 4 | + previewStreamHostSetupMessage, |
4 | 5 | type PreviewStreamControl, |
5 | 6 | type PreviewStreamDownload, |
6 | 7 | type PreviewStreamFileChooser, |
7 | 8 | type PreviewStreamInput, |
8 | 9 | } from "@t3tools/client-runtime/preview/server-browser-stream"; |
9 | | -import type { EnvironmentId } from "@t3tools/contracts"; |
| 10 | +import type { EnvironmentId, PreviewStreamHostSetup } from "@t3tools/contracts"; |
10 | 11 | import { |
11 | 12 | useEffect, |
12 | 13 | useEffectEvent, |
@@ -252,6 +253,7 @@ function PreviewStreamDocumentView({ |
252 | 253 | const [status, setStatus] = useState<"connecting" | "streaming" | "error">("connecting"); |
253 | 254 | const [error, setError] = useState<string | null>(null); |
254 | 255 | const [gone, setGone] = useState(false); |
| 256 | + const [hostSetup, setHostSetup] = useState<PreviewStreamHostSetup | null>(null); |
255 | 257 | const [started, setStarted] = useState(false); |
256 | 258 | const [control, setControl] = useState<PreviewStreamControl | null>(null); |
257 | 259 | const [promptText, setPromptText] = useState(""); |
@@ -384,6 +386,10 @@ function PreviewStreamDocumentView({ |
384 | 386 | fail("This tab was closed."); |
385 | 387 | onGone?.(); |
386 | 388 | return; |
| 389 | + case "hostSetup": |
| 390 | + setHostSetup({ need: message.need, command: message.command }); |
| 391 | + fail(previewStreamHostSetupMessage(message)); |
| 392 | + return; |
387 | 393 | case "viewport": |
388 | 394 | onViewport?.(message); |
389 | 395 | return; |
@@ -495,13 +501,30 @@ function PreviewStreamDocumentView({ |
495 | 501 | {status === "error" ? error : "Connecting to browser..."} |
496 | 502 | </AppText> |
497 | 503 | )} |
| 504 | + {status === "error" && hostSetup && !compact ? ( |
| 505 | + <Pressable |
| 506 | + accessibilityRole="button" |
| 507 | + accessibilityLabel={`Copy ${hostSetup.command}`} |
| 508 | + className="w-full flex-row items-center gap-2 rounded-xl border border-secondary-border bg-secondary px-4 py-3" |
| 509 | + onPress={() => { |
| 510 | + void Clipboard.setStringAsync(hostSetup.command).catch(() => undefined); |
| 511 | + }} |
| 512 | + > |
| 513 | + <AppText selectable className="flex-1 font-mono text-sm text-secondary-foreground"> |
| 514 | + {hostSetup.command} |
| 515 | + </AppText> |
| 516 | + <AppText className="text-xs text-foreground-muted">Copy</AppText> |
| 517 | + </Pressable> |
| 518 | + ) : null} |
498 | 519 | {status === "error" && !gone && !compact ? ( |
499 | 520 | <Pressable |
500 | 521 | accessibilityRole="button" |
501 | 522 | className="rounded-full border border-secondary-border bg-secondary px-6 py-3" |
502 | 523 | onPress={onRetry} |
503 | 524 | > |
504 | | - <AppText className="text-secondary-foreground">Reconnect</AppText> |
| 525 | + <AppText className="text-secondary-foreground"> |
| 526 | + {hostSetup ? "Try again" : "Reconnect"} |
| 527 | + </AppText> |
505 | 528 | </Pressable> |
506 | 529 | ) : null} |
507 | 530 | </View> |
|
0 commit comments