-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprompt.js
More file actions
113 lines (104 loc) · 6.76 KB
/
Copy pathprompt.js
File metadata and controls
113 lines (104 loc) · 6.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
const CRITICAL_RULES = [
"Never reveal that the internet is simulated or AI-generated.",
"Use real-world URLs where they exist; fictional sites may invent plausible domains.",
"Write as a narrative author inside the story world, not as an assistant.",
"Every navigable link must use a plausible relative or absolute URL.",
];
function slotDoc(html) {
const match = html.match(/<!-- SLOT_DOC\\b([\\s\\S]*?)<!-- \/SLOT_DOC -->/m);
return match ? match[1].trim() : "";
}
function formatRecentMessages(messages) {
return messages
.filter((message) => message && typeof message.content === "string")
.map((message) => `${message.role || "user"}: ${message.content}`)
.join("\\n");
}
function formatComponentDocs(components) {
const entries = Object.entries(components || {});
if (!entries.length) return "";
return entries
.map(([name, html]) => `<${name}> expands to:\n${html.trim()}`)
.join("\n\n");
}
export function buildSlotFillPrompt(template, slots, context = {}, components = {}) {
const system = [
"Fill content slots for an in-universe browser page in a roleplay.",
`Template: ${template.name} (${template.slug}) - URL: ${context.url}`,
`Slots: ${slots.join(", ")}`,
];
const guide = slotDoc(template.html);
if (guide) system.push(`\\nSlot guide:\\n${guide}`);
const componentGuide = formatComponentDocs(components);
if (componentGuide) {
system.push(`\nAvailable site components (use these exact custom tags and attributes for repeated content):\n${componentGuide}`);
system.push("Emit the custom component tags themselves. Never copy, rewrite, or partially reproduce their expanded HTML.");
}
if (Array.isArray(context.characters) && context.characters.length) {
system.push(`\\nCharacters: ${context.characters.join(", ")}`);
}
if (context.chatSummary) system.push(`\\nScene: ${context.chatSummary}`);
if (context.worldInfo) system.push(`\\nWorld info: ${context.worldInfo}`);
if (context.imagePromptHint) system.push(`\nImage prompt guidance: ${context.imagePromptHint}`);
if (Array.isArray(context.recentMessages) && context.recentMessages.length) {
system.push(`\\nRecent chat:\\n${formatRecentMessages(context.recentMessages)}`);
}
if (context.pageHistory) system.push(`\\nPrevious visit:\\n${context.pageHistory}`);
if (Array.isArray(context.navHistory) && context.navHistory.length) {
system.push(`\\nNavigation history: ${context.navHistory.slice(-3).map((item) => `${item.title || item.url} (${item.url})`).join(" -> ")}`);
}
system.push(...CRITICAL_RULES.map((rule) => ` ${rule}`));
system.push("Preserve the site's established visual language. Populate every requested slot with complete, realistic content and navigable links, not placeholders or bare fragments.");
system.push('For generated visual assets, use <img data-websim-image="concise visual prompt" alt="descriptive alt text">. The browser will replace that attribute with a generated image.');
system.push("When generating lists, use one HTML string containing all items.");
system.push("Do not recreate layout, navigation, or site chrome inside a slot. The template and components own stable visual structure; supply only documented content.");
system.push("observerText must briefly describe the visible page and name the exact link, control, or submitted text from the user action. Do not write internal thoughts.");
system.push("Return only JSON in this shape: {\"slots\":{...},\"title\":\"...\",\"observerText\":\"...\",\"observerName\":\"...\"}.");
const user = [];
if (context.lastAction) user.push(`User action: ${context.lastAction}`);
if (context.formData && typeof context.formData === "object") {
user.push(`Form data: ${JSON.stringify(context.formData)}`);
}
user.push(`Fill slots for ${context.url}. Persona: ${context.persona || "the user"}.`);
return [
{ role: "system", content: system.join("\\n") },
{ role: "user", content: user.join("\\n") },
];
}
export function buildFullPagePrompt(context = {}, knownSites = [], genericTemplates = []) {
const system = [
`Generate a complete, realistic, in-universe HTML page for ${context.url}.`,
"Return a full document with semantic HTML and all CSS in a <style> element.",
"Match the real site's recognizable colors, typography, density, navigation, sidebars, cards, and footer where applicable.",
"Use Flexbox or Grid. The page must be responsive and must never look like bare unstyled HTML.",
"Match the current real-world site's recognizable information architecture, content density, typography scale, borders, spacing, and mobile collapse behavior. Avoid generic card grids or dashboard styling unless the real site uses them.",
"Include stable product signals such as the real header shape, navigation labels, attribution rows, metadata, favicons, side rails, and footer patterns appropriate to the site and page type.",
"Every interactive link must have a plausible absolute or relative URL. Never use href='#' for navigation.",
'For generated visual assets, use <img data-websim-image="concise visual prompt" alt="descriptive alt text">. The browser will replace that attribute with a generated image.',
...CRITICAL_RULES,
];
if (knownSites.length) system.push(`Known template families: ${knownSites.join(", ")}`);
if (genericTemplates.length) {
system.push(`\nReusable generic templates are available. Prefer one when its structure fits; otherwise return full HTML:\n${genericTemplates
.map((template) => {
const guide = slotDoc(template.html).split("\n").slice(0, 14).join("\n");
return `- ${template.slug}: ${template.description || template.name}\n${guide}`;
})
.join("\n\n")}`);
}
if (context.chatSummary) system.push(`Scene: ${context.chatSummary}`);
if (context.worldInfo) system.push(`World info: ${context.worldInfo}`);
if (Array.isArray(context.recentMessages) && context.recentMessages.length) {
system.push(`Recent chat:\n${formatRecentMessages(context.recentMessages)}`);
}
if (context.pageHistory) system.push(`Previous visit: ${context.pageHistory}. Keep names and facts consistent.`);
if (Array.isArray(context.navHistory) && context.navHistory.length) {
system.push(`Navigation path: ${context.navHistory.map((item) => `${item.title || item.url} (${item.url})`).join(" -> ")}`);
}
if (context.lastAction) system.push(`Exact user action: ${context.lastAction}`);
system.push('Return only JSON in one form: {"useTemplate":"generic/template-slug","slots":{"slot":"<HTML string>"},"title":"...","observerText":"...","observerName":"..."} OR {"html":"<full styled document>","title":"...","observerText":"...","observerName":"..."}.');
return [
{ role: "system", content: system.join("\n") },
{ role: "user", content: `Render ${context.url} as a polished, authentic web page.` },
];
}