Files
moreminimore-vibe/src/contexts/DeepLinkContext.tsx
Will Chen 04b1a36f4a Deep link: add prompt (#1669)
Example:
open
"dyad://add-prompt?data=eyJ0aXRsZSI6IlRlc3QgUHJvbXB0IiwiZGVzY3JpcHRpb24iOiJBIHRlc3QgcHJvbXB0IGZyb20gZGVlcCBsaW5rIiwiY29udGVudCI6IlRoaXMgaXMgdGhlIGNvbnRlbnQgb2YgdGhlIHByb21wdC4ifQ%3D%3D"

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds dyad://add-prompt deep link that navigates to Library and opens a
prefilled Create Prompt dialog from base64 JSON.
> 
> - **Deep Link Handling**
> - Parse `dyad://add-prompt?data=<base64-json>` in `src/main.ts`;
validate with `AddPromptDataSchema` and send `deep-link-received` with
payload.
> - Extend `DeepLinkContext` to navigate to `/library` on `add-prompt`.
> - **Library/Dialogs**
> - Add controlled open state and `prefillData` support to
`CreateOrEditPromptDialog` and `CreatePromptDialog`
(`src/components/CreatePromptDialog.tsx`).
> - In `src/pages/library.tsx`, listen for `add-prompt` deep link,
prefill form, open dialog, and clear deep-link state.
> - **Schemas**
> - Define `AddPromptDataSchema`, `AddPromptPayload`, and
`AddPromptDeepLinkData` in `src/ipc/deep_link_data.ts` and include in
`DeepLinkData` union.
> - **E2E Tests**
> - Add Playwright test `e2e-tests/add_prompt_deep_link.spec.ts` and
ARIA snapshot to verify deep link opens prefilled dialog and saves
prompt.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
1ddb12306cfca195682c8a1b719f60093b858d54. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
2025-10-29 21:26:01 -07:00

56 lines
1.7 KiB
TypeScript

import React, { createContext, useContext, useEffect, useState } from "react";
import { useNavigate } from "@tanstack/react-router";
import { IpcClient } from "../ipc/ipc_client";
import { DeepLinkData } from "../ipc/deep_link_data";
import { useScrollAndNavigateTo } from "@/hooks/useScrollAndNavigateTo";
type DeepLinkContextType = {
lastDeepLink: (DeepLinkData & { timestamp: number }) | null;
clearLastDeepLink: () => void;
};
const DeepLinkContext = createContext<DeepLinkContextType>({
lastDeepLink: null,
clearLastDeepLink: () => {},
});
export function DeepLinkProvider({ children }: { children: React.ReactNode }) {
const [lastDeepLink, setLastDeepLink] = useState<
(DeepLinkData & { timestamp: number }) | null
>(null);
const navigate = useNavigate();
const scrollAndNavigateTo = useScrollAndNavigateTo("/settings", {
behavior: "smooth",
block: "start",
});
useEffect(() => {
const ipcClient = IpcClient.getInstance();
const unsubscribe = ipcClient.onDeepLinkReceived((data) => {
// Update with timestamp to ensure state change even if same type comes twice
setLastDeepLink({ ...data, timestamp: Date.now() });
if (data.type === "add-mcp-server") {
// Navigate to tools-mcp section
scrollAndNavigateTo("tools-mcp");
} else if (data.type === "add-prompt") {
// Navigate to library page
navigate({ to: "/library" });
}
});
return unsubscribe;
}, [navigate, scrollAndNavigateTo]);
return (
<DeepLinkContext.Provider
value={{
lastDeepLink,
clearLastDeepLink: () => setLastDeepLink(null),
}}
>
{children}
</DeepLinkContext.Provider>
);
}
export const useDeepLink = () => useContext(DeepLinkContext);