Surface short copy/paste operator prompts derived from canonical workflow
files with workflow path and SHA-256 citations. Adds /prompts, /prompts/{id},
and /api/prompts with one-click copy.
Closes #428
87 lines
2.6 KiB
Python
87 lines
2.6 KiB
Python
"""HTML views for the prompt library (#428)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import html
|
|
|
|
from webui.layout import render_page
|
|
from webui.prompt_library import PromptEntry, load_prompt_library
|
|
|
|
|
|
def _escape(text: str) -> str:
|
|
return html.escape(text, quote=True)
|
|
|
|
|
|
def _render_prompt_card(entry: PromptEntry) -> str:
|
|
hash_short = (
|
|
f"<code>{_escape(entry.workflow_hash[:12])}</code>"
|
|
if entry.workflow_hash
|
|
else "<span class=\"muted\">n/a</span>"
|
|
)
|
|
task_mode = (
|
|
f"<code>{_escape(entry.task_mode)}</code>"
|
|
if entry.task_mode
|
|
else "<span class=\"muted\">n/a</span>"
|
|
)
|
|
note = (
|
|
f'<p class="meta">{_escape(entry.source_note)}</p>'
|
|
if entry.source_note
|
|
else ""
|
|
)
|
|
prompt_id = f"prompt-{entry.slug}"
|
|
return (
|
|
f'<section class="prompt-card" id="{_escape(entry.slug)}">'
|
|
f"<h3>{_escape(entry.label)}</h3>"
|
|
f'<p class="meta">Workflow: <code>{_escape(entry.workflow_path)}</code> · '
|
|
f"task_mode: {task_mode} · sha256: {hash_short}</p>"
|
|
f'<pre class="prompt-text" id="{prompt_id}">{_escape(entry.prompt_text)}</pre>'
|
|
f'<button type="button" class="copy-btn" data-copy-target="{prompt_id}">'
|
|
"Copy prompt</button>"
|
|
f"{note}"
|
|
"</section>"
|
|
)
|
|
|
|
|
|
PROMPT_PAGE_SCRIPT = """
|
|
<script>
|
|
document.querySelectorAll('.copy-btn').forEach((btn) => {
|
|
btn.addEventListener('click', async () => {
|
|
const targetId = btn.getAttribute('data-copy-target');
|
|
const node = document.getElementById(targetId);
|
|
if (!node) return;
|
|
const text = node.textContent || '';
|
|
try {
|
|
await navigator.clipboard.writeText(text);
|
|
const prior = btn.textContent;
|
|
btn.textContent = 'Copied';
|
|
setTimeout(() => { btn.textContent = prior; }, 1200);
|
|
} catch (_err) {
|
|
btn.textContent = 'Copy failed';
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
"""
|
|
|
|
|
|
def render_prompts_page() -> str:
|
|
entries = load_prompt_library()
|
|
cards = "".join(_render_prompt_card(entry) for entry in entries)
|
|
body = (
|
|
"<h2>Prompt library</h2>"
|
|
"<p>Short copy/paste task prompts derived from canonical workflows. "
|
|
"Full policy remains in the cited workflow files — not duplicated here.</p>"
|
|
f"{cards}"
|
|
"<p><a href=\"/api/prompts\">JSON API</a></p>"
|
|
f"{PROMPT_PAGE_SCRIPT}"
|
|
)
|
|
return render_page(title="Prompts", body_html=body)
|
|
|
|
|
|
def render_prompt_detail(entry: PromptEntry) -> str:
|
|
body = (
|
|
f"<p><a href=\"/prompts\">← All prompts</a></p>"
|
|
f"{_render_prompt_card(entry)}"
|
|
f"{PROMPT_PAGE_SCRIPT}"
|
|
)
|
|
return render_page(title=entry.label, body_html=body) |