mirror of
https://github.com/supabase/supabase.git
synced 2026-09-07 02:20:52 +08:00
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Docs enhancement: Agent-ready prompt blocks on all 18 framework quickstart pages. ## What is the current behavior? Framework quickstarts do not surface a copyable AI prompt. Readers have to assemble context themselves when asking an AI coding assistant to follow the guide. ## What is the new behavior? - Partials at `apps/docs/content/_partials/ai/quickstart_prompt_{framework}.mdx` contain `<AiPrompt prompt={...} />` (Prettier multiline single-quoted JS string with `\n` escapes). - Each quickstart includes `<$Partial path="ai/quickstart_prompt_{framework}.mdx" />`. - Runtime: `AiPrompt` → `PromptPanel` (Copy AI Prompt, expandable). - Markdown export: `apps/docs/internals/markdown-schema/AiPrompt.ts` decodes Prettier single-quoted prompt expressions so exported markdown includes an **AI Prompt** section without quote leak. - Shared `$Partial` helpers live in `lib/partials.utils.ts`. - Closes DOCS-1144. ### Example before/after | | Production | Preview | | --- | --- | --- | | Next.js quickstart | [production](https://supabase.com/docs/guides/getting-started/quickstarts/nextjs) | [preview](https://docs-git-nikrichers-docs-1144-add-ai-prompt-blo-5af4d8-supabase.vercel.app/docs/guides/getting-started/quickstarts/nextjs) | **Light** | Before | After | | --- | --- | |  |  | **Dark** | Before | After | | --- | --- | |  |  | ### Test plan - [x] Preview renders AI Prompt panel with copy - [x] Spot-check Next.js, Flutter, Expo, Vue - [x] `test-quickstart-prompts` structural - [x] Markdown export includes **AI Prompt** without quote leak - [x] Format CI green after prettier/single-quote decode fix ## Additional context - Worktree: `~/GitHub/supabase/supabase-worktrees/nikrichers/docs-1144-add-ai-prompt-blocks-to-all-18-framework-quickstarts` - Skills: `generate-quickstart-prompts` / `test-quickstart-prompts`; librarian update https://github.com/supabase/docs-agent-skills/pull/21 - `PromptPanel` replaced the older GlassPanel experiment for the expandable copy UI --------- Co-authored-by: Nik Richers <nik@validmind.ai> Co-authored-by: jeremenichelli <jeremenichelli@users.noreply.github.com>
71 lines
2.2 KiB
TypeScript
71 lines
2.2 KiB
TypeScript
/**
|
|
* Shared $Partial variable substitution for the MDX runtime pipeline and the
|
|
* markdown export pipeline.
|
|
*/
|
|
|
|
/**
|
|
* Substitutes provided variables into partial content. Variable substitution is
|
|
* optional: any variable referenced in the content but not provided is replaced
|
|
* with an empty string, and any variable provided but not referenced is ignored.
|
|
* A leading `\` escape (`\{{ .var }}`) opts a placeholder out of substitution.
|
|
*/
|
|
export function substitutePartialVars(
|
|
content: string,
|
|
vars: Record<string, string> | undefined
|
|
): string {
|
|
for (const [key, value] of Object.entries(vars ?? {})) {
|
|
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
content = content.replace(
|
|
new RegExp(`(?<!\\\\)\\{\\{\\s*\\.${escapedKey}\\s*\\}\\}`, 'g'),
|
|
() => value
|
|
)
|
|
}
|
|
|
|
content = content.replace(/(?<!\\)\{\{\s*\.[\w-]+\s*\}\}/g, '')
|
|
return content
|
|
}
|
|
|
|
function assertStringValues(parsed: unknown): asserts parsed is Record<string, string> {
|
|
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
throw new Error('Invalid $Partial variables: must be valid JSON containing only string values')
|
|
}
|
|
|
|
for (const value of Object.values(parsed)) {
|
|
if (typeof value !== 'string') {
|
|
throw new Error(
|
|
'Invalid $Partial variables: must be valid JSON containing only string values'
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parses the `variables` prop from a `$Partial` node. Accepts a JSON object
|
|
* string (from MDX attribute expressions) or an already-parsed object.
|
|
*/
|
|
export function parsePartialVariables(raw: unknown): Record<string, string> | undefined {
|
|
if (raw === undefined || raw === null || raw === true) {
|
|
return undefined
|
|
}
|
|
|
|
if (typeof raw === 'object') {
|
|
assertStringValues(raw)
|
|
return raw
|
|
}
|
|
|
|
if (typeof raw !== 'string') {
|
|
throw new Error('Invalid $Partial variables: must be valid JSON containing only string values')
|
|
}
|
|
|
|
try {
|
|
const parsed: unknown = JSON.parse(raw)
|
|
assertStringValues(parsed)
|
|
return parsed
|
|
} catch (error) {
|
|
if (error instanceof Error && error.message.startsWith('Invalid $Partial variables')) {
|
|
throw error
|
|
}
|
|
throw new Error('Invalid $Partial variables: must be valid JSON containing only string values')
|
|
}
|
|
}
|