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 -->
56 lines
1.7 KiB
TypeScript
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);
|