From ce1ad8c99a51beac6552fdb7626f9cbb345630d7 Mon Sep 17 00:00:00 2001 From: Marcus Farrell Date: Wed, 4 Mar 2026 15:21:07 -0800 Subject: [PATCH 1/3] Improving the analytics setup prompts --- apps/web/ui/guides/guide-action-button.tsx | 212 ++++++++++++++++++++- packages/ui/src/icons/anthropic.tsx | 4 +- packages/ui/src/icons/codex.tsx | 20 ++ packages/ui/src/icons/cursor.tsx | 20 ++ packages/ui/src/icons/index.tsx | 2 + packages/ui/src/icons/openai.tsx | 2 +- 6 files changed, 253 insertions(+), 7 deletions(-) create mode 100644 packages/ui/src/icons/codex.tsx create mode 100644 packages/ui/src/icons/cursor.tsx diff --git a/apps/web/ui/guides/guide-action-button.tsx b/apps/web/ui/guides/guide-action-button.tsx index 8b6ea71b8b9..782a4c13576 100644 --- a/apps/web/ui/guides/guide-action-button.tsx +++ b/apps/web/ui/guides/guide-action-button.tsx @@ -3,6 +3,8 @@ import { BookOpen, Button, Check, + Codex, + Cursor, OpenAI, Popover, useCopyToClipboard, @@ -14,6 +16,169 @@ import { useState } from "react"; import { toast } from "sonner"; import { IntegrationGuide } from "./integrations"; +const CONVERSION_TRACKING_PATTERNS = [ + /data-publishable-key=/, + /publishableKey=/, + /s\.setAttribute\("data-publishable-key"/, +]; + +const PUBLISHABLE_KEY_PLACEHOLDER = ""; +const OUTBOUND_DOMAIN_PLACEHOLDER = '["", ""]'; +const REFER_DOMAIN_PLACEHOLDER = ""; + +function getGuideOptionName(guide: IntegrationGuide) { + return guide.description || [guide.title, guide.subtitle].filter(Boolean).join(" "); +} + +function sanitizeClientScriptMarkdown(markdown: string) { + return markdown + .replace(//g, "") + .replace(/dub_pk_[A-Za-z0-9_-]+/g, PUBLISHABLE_KEY_PLACEHOLDER) + .replace( + /data-publishable-key="[^"]*"/g, + `data-publishable-key="${PUBLISHABLE_KEY_PLACEHOLDER}"`, + ) + .replace( + /publishableKey="[^"]*"/g, + `publishableKey="${PUBLISHABLE_KEY_PLACEHOLDER}"`, + ) + .replace( + /s\.setAttribute\("data-publishable-key", "[^"]*"\);/g, + `s.setAttribute("data-publishable-key", "${PUBLISHABLE_KEY_PLACEHOLDER}");`, + ) + .replace( + /"outbound": \["example\.com", "example\.sh"\]/g, + `"outbound": ${OUTBOUND_DOMAIN_PLACEHOLDER}`, + ) + .replace( + /outbound: \["example\.com", "example\.sh"\]/g, + `outbound: ${OUTBOUND_DOMAIN_PLACEHOLDER}`, + ) + .replace( + /"refer":\s*"[^"]*"/g, + `"refer": "${REFER_DOMAIN_PLACEHOLDER}"`, + ) + .replace( + /refer:\s*"[^"]*"/g, + `refer: "${REFER_DOMAIN_PLACEHOLDER}"`, + ) + .trim(); +} + +function sanitizeGuideInstructions(markdown: string) { + return markdown.replace(//g, "").trim(); +} + +function isOutboundDomainTrackingEnabled(markdown: string) { + return ( + /script\.[^"'\s]*outbound-domains[^"'\s]*\.js/.test(markdown) || + /"outbound"\s*:/.test(markdown) || + /\boutbound\s*:/.test(markdown) + ); +} + +function getReactStepOnePrompt(markdown: string, guide: IntegrationGuide) { + const conversionTrackingEnabled = CONVERSION_TRACKING_PATTERNS.some((pattern) => + pattern.test(markdown), + ); + const outboundTrackingEnabled = isOutboundDomainTrackingEnabled(markdown); + + const analyticsProps = [ + conversionTrackingEnabled + ? ` publishableKey="${PUBLISHABLE_KEY_PLACEHOLDER}"` + : null, + outboundTrackingEnabled + ? ` domainsConfig={{\n refer: "${REFER_DOMAIN_PLACEHOLDER}",\n outbound: ${OUTBOUND_DOMAIN_PLACEHOLDER}\n }}` + : null, + ].filter(Boolean); + + const analyticsComponent = + analyticsProps.length > 0 + ? `` + : ""; + + return [ + "I'm using Dub and need to install the client-side script. Help me add the script from the instructions below.", + conversionTrackingEnabled + ? "Ask me for my publishable key before finalizing the conversion tracking setup." + : null, + "Ask me if I want to enable client-side click tracking (Dub Partners). If yes, add `domainsConfig={{ refer: \"\" }}`.", + outboundTrackingEnabled + ? "Ask me which outbound domains I want to track before finalizing the setup." + : null, + "Step 1: Install Dub package to your project", + "```bash\nnpm install @dub/analytics\n```", + "Step 2: Initialize package in your code", + `\`\`\`jsx +import { Analytics as DubAnalytics } from '@dub/analytics/react'; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + {children} + ${analyticsComponent} + + ); +} +\`\`\``, + `Full guide link for reference: ${guide.url}`, + ] + .filter(Boolean) + .join("\n\n"); +} + +function getGuidePrompt(guide: IntegrationGuide, markdown: string) { + if (guide.type === "client-sdk" && guide.key === "shopify") { + return `Read from ${guide.url} so I can ask questions about it.`; + } + + if (guide.type === "client-sdk" && guide.key === "react") { + return getReactStepOnePrompt(markdown, guide); + } + + if (guide.type === "client-sdk") { + const conversionTrackingEnabled = CONVERSION_TRACKING_PATTERNS.some((pattern) => + pattern.test(markdown), + ); + const outboundTrackingEnabled = isOutboundDomainTrackingEnabled(markdown); + const sanitizedMarkdown = sanitizeClientScriptMarkdown(markdown); + + return [ + "I'm using Dub and need to install the client-side script.", + "Help me add the script from the instructions below.", + conversionTrackingEnabled + ? "Ask me for my publishable key before finalizing the conversion tracking setup." + : null, + "Ask me if I want to enable client-side click tracking (Dub Partners). If yes, add `domainsConfig.refer` with my referring domain.", + outboundTrackingEnabled + ? "Ask me which outbound domains I want to track before finalizing the setup." + : null, + "Instructions:", + sanitizedMarkdown, + `Full guide link for reference: ${guide.url}`, + ] + .filter(Boolean) + .join("\n\n"); + } + + const optionName = getGuideOptionName(guide); + const intro = + guide.type === "track-lead" + ? `I'm using Dub and need to track lead events with ${optionName}. Help set that up with these instructions.` + : `I'm using Dub and need to track sale events with ${optionName}. Help set that up with these instructions.`; + + return [ + intro, + "Instructions:", + sanitizeGuideInstructions(markdown), + `Full guide link for reference: ${guide.url}`, + ].join("\n\n"); +} + export const GuideActionButton = ({ guide, markdown, @@ -25,7 +190,9 @@ export const GuideActionButton = ({ const [copied, copyToClipboard] = useCopyToClipboard(); - const prompt = `Read from ${guide.url} so I can ask questions about it.`; + const prompt = getGuidePrompt(guide, markdown); + const cursorUrl = `https://cursor.com/link/prompt?text=${encodeURIComponent(prompt)}`; + const codexUrl = `https://chatgpt.com/codex?prompt=${encodeURIComponent(prompt)}`; return (
@@ -63,7 +230,7 @@ export const GuideActionButton = ({
Copy content - Copy page as Markdown for LLMs + Copy section as Markdown for LLMs
@@ -72,7 +239,6 @@ export const GuideActionButton = ({ className="flex w-full cursor-pointer items-center gap-2 rounded-md p-2 text-sm text-neutral-600 transition-colors hover:bg-neutral-100" onClick={() => { const chatgptUrl = `https://chatgpt.com?hints=search&prompt=${encodeURIComponent(prompt)}`; - console.log("chatgptUrl", chatgptUrl); window.open(chatgptUrl, "_blank", "noopener,noreferrer"); }} > @@ -110,6 +276,46 @@ export const GuideActionButton = ({
+ + + + } align="end" diff --git a/packages/ui/src/icons/anthropic.tsx b/packages/ui/src/icons/anthropic.tsx index ceb91a1dca4..3ffd6fede9f 100644 --- a/packages/ui/src/icons/anthropic.tsx +++ b/packages/ui/src/icons/anthropic.tsx @@ -12,9 +12,7 @@ export function Anthropic({ className, ...props }: SVGProps) { {...props} > diff --git a/packages/ui/src/icons/codex.tsx b/packages/ui/src/icons/codex.tsx new file mode 100644 index 00000000000..af11a972e84 --- /dev/null +++ b/packages/ui/src/icons/codex.tsx @@ -0,0 +1,20 @@ +import { SVGProps } from "react"; + +export function Codex({ className, ...props }: SVGProps) { + return ( + + + + ); +} diff --git a/packages/ui/src/icons/cursor.tsx b/packages/ui/src/icons/cursor.tsx new file mode 100644 index 00000000000..f3c6893f200 --- /dev/null +++ b/packages/ui/src/icons/cursor.tsx @@ -0,0 +1,20 @@ +import { SVGProps } from "react"; + +export function Cursor({ className, ...props }: SVGProps) { + return ( + + + + ); +} diff --git a/packages/ui/src/icons/index.tsx b/packages/ui/src/icons/index.tsx index 9808223e0ca..084c8b2f206 100644 --- a/packages/ui/src/icons/index.tsx +++ b/packages/ui/src/icons/index.tsx @@ -6,7 +6,9 @@ import { ComponentType, SVGProps } from "react"; // custom icons export * from "./arrow-up-right-2"; export * from "./copy"; +export * from "./codex"; export * from "./crown-small"; +export * from "./cursor"; export * from "./dub-analytics"; export * from "./dub-api"; export * from "./dub-crafted-shield"; diff --git a/packages/ui/src/icons/openai.tsx b/packages/ui/src/icons/openai.tsx index de07cf3a003..0485dd7e3d5 100644 --- a/packages/ui/src/icons/openai.tsx +++ b/packages/ui/src/icons/openai.tsx @@ -13,7 +13,7 @@ export function OpenAI({ className, ...props }: SVGProps) { > ); From bdc81edd32c1df2d0e6d3b818e1ebd788f08b935 Mon Sep 17 00:00:00 2001 From: Marcus Farrell Date: Wed, 4 Mar 2026 16:15:07 -0800 Subject: [PATCH 2/3] Copy change --- apps/web/ui/guides/guide-action-button.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/web/ui/guides/guide-action-button.tsx b/apps/web/ui/guides/guide-action-button.tsx index 782a4c13576..c097facbfba 100644 --- a/apps/web/ui/guides/guide-action-button.tsx +++ b/apps/web/ui/guides/guide-action-button.tsx @@ -198,7 +198,7 @@ export const GuideActionButton = ({
@@ -292,7 +292,7 @@ export const GuideActionButton = ({
- Preview prompt and open in Cursor + Ask questions about this step @@ -312,7 +312,7 @@ export const GuideActionButton = ({ - Open the prompt in Codex + Ask questions about this step From d32904a32053b0f15036e90daa17dd77b062a5ee Mon Sep 17 00:00:00 2001 From: Marcus Farrell Date: Wed, 4 Mar 2026 16:23:09 -0800 Subject: [PATCH 3/3] Coderabbit fixes --- apps/web/ui/guides/guide-action-button.tsx | 6 ++++-- packages/ui/src/icons/codex.tsx | 2 +- packages/ui/src/icons/openai.tsx | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/web/ui/guides/guide-action-button.tsx b/apps/web/ui/guides/guide-action-button.tsx index c097facbfba..03bc1492d0a 100644 --- a/apps/web/ui/guides/guide-action-button.tsx +++ b/apps/web/ui/guides/guide-action-button.tsx @@ -119,8 +119,10 @@ export default function RootLayout({ }>) { return ( - {children} - ${analyticsComponent} + + {children} + ${analyticsComponent} + ); } diff --git a/packages/ui/src/icons/codex.tsx b/packages/ui/src/icons/codex.tsx index af11a972e84..508461957d8 100644 --- a/packages/ui/src/icons/codex.tsx +++ b/packages/ui/src/icons/codex.tsx @@ -13,7 +13,7 @@ export function Codex({ className, ...props }: SVGProps) { > ); diff --git a/packages/ui/src/icons/openai.tsx b/packages/ui/src/icons/openai.tsx index 0485dd7e3d5..de07cf3a003 100644 --- a/packages/ui/src/icons/openai.tsx +++ b/packages/ui/src/icons/openai.tsx @@ -13,7 +13,7 @@ export function OpenAI({ className, ...props }: SVGProps) { > );