Prompt gallery (#957)

- [x] show prompt instead of app in autocomplete
- [x] use proper array/list for db (tags)
- [x] don't do <dyad-prompt> - replace inline
This commit is contained in:
Will Chen
2025-08-18 13:25:11 -07:00
committed by GitHub
parent a547735714
commit 573642ae5f
26 changed files with 1540 additions and 42 deletions

View File

@@ -61,6 +61,9 @@ import { FileUploadsState } from "../utils/file_uploads_state";
import { OpenAIResponsesProviderOptions } from "@ai-sdk/openai";
import { extractMentionedAppsCodebases } from "../utils/mention_apps";
import { parseAppMentions } from "@/shared/parse_mention_apps";
import { prompts as promptsTable } from "../../db/schema";
import { inArray } from "drizzle-orm";
import { replacePromptReference } from "../utils/replacePromptReference";
type AsyncIterableStream<T> = AsyncIterable<T> & ReadableStream<T>;
@@ -274,6 +277,26 @@ export function registerChatStreamHandlers() {
// Add user message to database with attachment info
let userPrompt = req.prompt + (attachmentInfo ? attachmentInfo : "");
// Inline referenced prompt contents for mentions like @prompt:<id>
try {
const matches = Array.from(userPrompt.matchAll(/@prompt:(\d+)/g));
if (matches.length > 0) {
const ids = Array.from(new Set(matches.map((m) => Number(m[1]))));
const referenced = await db
.select()
.from(promptsTable)
.where(inArray(promptsTable.id, ids));
if (referenced.length > 0) {
const promptsMap: Record<number, string> = {};
for (const p of referenced) {
promptsMap[p.id] = p.content;
}
userPrompt = replacePromptReference(userPrompt, promptsMap);
}
}
} catch (e) {
logger.error("Failed to inline referenced prompts:", e);
}
if (req.selectedComponent) {
let componentSnippet = "[component snippet not available]";
try {

View File

@@ -0,0 +1,91 @@
import { IpcMainInvokeEvent } from "electron";
import log from "electron-log";
import { createLoggedHandler } from "./safe_handle";
import { db } from "@/db";
import { prompts } from "@/db/schema";
import { eq } from "drizzle-orm";
import {
CreatePromptParamsDto,
PromptDto,
UpdatePromptParamsDto,
} from "../ipc_types";
const logger = log.scope("prompt_handlers");
const handle = createLoggedHandler(logger);
export function registerPromptHandlers() {
handle("prompts:list", async (): Promise<PromptDto[]> => {
const rows = db.select().from(prompts).all();
return rows.map((r) => ({
id: r.id!,
title: r.title,
description: r.description ?? null,
content: r.content,
createdAt: r.createdAt,
updatedAt: r.updatedAt,
}));
});
handle(
"prompts:create",
async (
_e: IpcMainInvokeEvent,
params: CreatePromptParamsDto,
): Promise<PromptDto> => {
const { title, description, content } = params;
if (!title || !content) {
throw new Error("Title and content are required");
}
const result = db
.insert(prompts)
.values({
title,
description: description ?? null,
content,
})
.run();
const id = Number(result.lastInsertRowid);
const row = db.select().from(prompts).where(eq(prompts.id, id)).get();
if (!row) throw new Error("Failed to fetch created prompt");
return {
id: row.id!,
title: row.title,
description: row.description ?? null,
content: row.content,
createdAt: row.createdAt,
updatedAt: row.updatedAt,
};
},
);
handle(
"prompts:update",
async (
_e: IpcMainInvokeEvent,
params: UpdatePromptParamsDto,
): Promise<void> => {
const { id, title, description, content } = params;
if (!id) throw new Error("Prompt id is required");
if (!title || !content) throw new Error("Title and content are required");
const now = new Date();
db.update(prompts)
.set({
title,
description: description ?? null,
content,
updatedAt: now,
})
.where(eq(prompts.id, id))
.run();
},
);
handle(
"prompts:delete",
async (_e: IpcMainInvokeEvent, id: number): Promise<void> => {
if (!id) throw new Error("Prompt id is required");
db.delete(prompts).where(eq(prompts.id, id)).run();
},
);
}