feat(preview): make address bar interactive for routing inside iframe
This commit is contained in:
@@ -152,6 +152,7 @@ export default function PreviewTab() {
|
|||||||
|
|
||||||
const deviceMode = usePreviewToolbarStore((s) => s.deviceMode);
|
const deviceMode = usePreviewToolbarStore((s) => s.deviceMode);
|
||||||
const refreshKey = usePreviewToolbarStore((s) => s.refreshKey);
|
const refreshKey = usePreviewToolbarStore((s) => s.refreshKey);
|
||||||
|
const currentPath = usePreviewToolbarStore((s) => s.currentPath);
|
||||||
|
|
||||||
// When the user clicks the manual refresh button in the toolbar, we don't
|
// When the user clicks the manual refresh button in the toolbar, we don't
|
||||||
// just want to reload the iframe — we also want to trigger the same ghost/zombie
|
// just want to reload the iframe — we also want to trigger the same ghost/zombie
|
||||||
@@ -166,8 +167,16 @@ export default function PreviewTab() {
|
|||||||
}, [refreshKey]);
|
}, [refreshKey]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
setIframeSrc(primaryRunning?.url ?? null);
|
if (!primaryRunning?.url) {
|
||||||
}, [primaryRunning?.url]);
|
setIframeSrc(null);
|
||||||
|
} else {
|
||||||
|
const base = primaryRunning.url.replace(/\/$/, "");
|
||||||
|
const path = currentPath.startsWith("/")
|
||||||
|
? currentPath
|
||||||
|
: `/${currentPath}`;
|
||||||
|
setIframeSrc(`${base}${path}`);
|
||||||
|
}
|
||||||
|
}, [primaryRunning?.url, currentPath]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!bridge || !iframeSrc || !iframeDomRef.current) return;
|
if (!bridge || !iframeSrc || !iframeDomRef.current) return;
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ interface PreviewToolbarState {
|
|||||||
setDeviceMode: (mode: "desktop" | "tablet" | "mobile") => void;
|
setDeviceMode: (mode: "desktop" | "tablet" | "mobile") => void;
|
||||||
refreshKey: number;
|
refreshKey: number;
|
||||||
triggerRefresh: () => void;
|
triggerRefresh: () => void;
|
||||||
|
currentPath: string;
|
||||||
|
setCurrentPath: (path: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const usePreviewToolbarStore = create<PreviewToolbarState>((set) => ({
|
export const usePreviewToolbarStore = create<PreviewToolbarState>((set) => ({
|
||||||
@@ -12,4 +14,6 @@ export const usePreviewToolbarStore = create<PreviewToolbarState>((set) => ({
|
|||||||
setDeviceMode: (mode) => set({ deviceMode: mode }),
|
setDeviceMode: (mode) => set({ deviceMode: mode }),
|
||||||
refreshKey: 0,
|
refreshKey: 0,
|
||||||
triggerRefresh: () => set((state) => ({ refreshKey: state.refreshKey + 1 })),
|
triggerRefresh: () => set((state) => ({ refreshKey: state.refreshKey + 1 })),
|
||||||
|
currentPath: "/",
|
||||||
|
setCurrentPath: (path) => set({ currentPath: path }),
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -92,6 +92,8 @@ function PreviewDeviceToggles() {
|
|||||||
const deviceMode = usePreviewToolbarStore((s) => s.deviceMode);
|
const deviceMode = usePreviewToolbarStore((s) => s.deviceMode);
|
||||||
const setDeviceMode = usePreviewToolbarStore((s) => s.setDeviceMode);
|
const setDeviceMode = usePreviewToolbarStore((s) => s.setDeviceMode);
|
||||||
const triggerRefresh = usePreviewToolbarStore((s) => s.triggerRefresh);
|
const triggerRefresh = usePreviewToolbarStore((s) => s.triggerRefresh);
|
||||||
|
const currentPath = usePreviewToolbarStore((s) => s.currentPath);
|
||||||
|
const setCurrentPath = usePreviewToolbarStore((s) => s.setCurrentPath);
|
||||||
|
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const projectId = params?.projectId as string;
|
const projectId = params?.projectId as string;
|
||||||
@@ -157,12 +159,23 @@ function PreviewDeviceToggles() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<span style={{ opacity: 0.6, flexShrink: 0 }}>🌐</span>
|
<span
|
||||||
|
style={{
|
||||||
|
opacity: 0.5,
|
||||||
|
flexShrink: 0,
|
||||||
|
paddingLeft: 4,
|
||||||
|
fontFamily: "var(--font-mono), monospace",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
/
|
||||||
|
</span>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={displayUrl}
|
value={currentPath === "/" ? "" : currentPath.replace(/^\//, "")}
|
||||||
readOnly
|
onChange={(e) =>
|
||||||
placeholder="No dev server running..."
|
setCurrentPath("/" + e.target.value.replace(/^\//, ""))
|
||||||
|
}
|
||||||
|
placeholder="path (e.g. dashboard)"
|
||||||
style={{
|
style={{
|
||||||
background: "transparent",
|
background: "transparent",
|
||||||
border: "none",
|
border: "none",
|
||||||
@@ -172,7 +185,12 @@ function PreviewDeviceToggles() {
|
|||||||
color: "#18181b",
|
color: "#18181b",
|
||||||
fontSize: "0.75rem",
|
fontSize: "0.75rem",
|
||||||
textOverflow: "ellipsis",
|
textOverflow: "ellipsis",
|
||||||
paddingLeft: 4,
|
fontFamily: "var(--font-mono), monospace",
|
||||||
|
}}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
triggerRefresh(); // force reload iframe
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user