Agent Template
Idea Clarifier Chat Canvas
A lightweight chat-style agent template that helps users turn rough, unclear, or fragmented input into a clearer request, summary, or next-step plan.
clarification
brainstorming
chat
writing-assistant
planning
Preview Mode
This is a preview with sample data. The template uses placeholders like
which will be replaced with actual agent data.
About This Template
Idea Clarifier Chat Canvas is a browser-executable AI agent template built on AgentOp. It runs entirely in the browser using Python (via Pyodide) and can be deployed without a server — just download the generated HTML file and open it locally or host it anywhere.
Topics
clarification
brainstorming
chat
writing-assistant
planning
Template Preview
Template Metadata
- Slug
- idea-clarifier-chat-canvas
- Created By
- [email protected]
- Created
- Aug 18, 2026
- Usage Count
- 1
Tags
clarification
brainstorming
chat
writing-assistant
planning
Code Statistics
- HTML Lines
- 92
- CSS Lines
- 231
- JS Lines
- 145
- Python Lines
- 24
Source Code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{ agent_name }}</title>
<style>{{ css_code }}</style>
</head>
<body>
<div class="app-shell">
<aside class="side-panel">
<div class="brand-card">
<div class="brand-emoji">🧭</div>
<div>
<h1>{{ agent_name }}</h1>
<p>{{ description }}</p>
</div>
</div>
<div class="panel-block">
<h2>📝 Input mode</h2>
<label for="mode-select" class="field-label">Choose the kind of help</label>
<select id="mode-select">
<option value="clarify">Clarify my idea</option>
<option value="summarize">Summarize my notes</option>
<option value="rewrite">Rewrite into clean prose</option>
<option value="plan">Turn into next steps</option>
</select>
</div>
<div class="panel-block">
<h2>✨ Example prompts</h2>
<div class="chip-list">
<button type="button" class="chip-btn" data-prompt="Please clarify this rough idea into a clean request: I want some sort of dashboard for tracking team stuff and deadlines.">Clarify a rough request</button>
<button type="button" class="chip-btn" data-prompt="Summarize these scattered notes into 3 bullets: launch timing unclear, website copy needs revision, ask sales for customer objections.">Summarize scattered notes</button>
<button type="button" class="chip-btn" data-prompt="Rewrite this into a cleaner brief: need app maybe for students maybe habit tracker with reminders and progress, simple and motivating.">Rewrite as a brief</button>
<button type="button" class="chip-btn" data-prompt="Turn this into next steps: We need a podcast concept, guest shortlist, publishing plan, and sponsorship ideas.">Create next steps</button>
</div>
</div>
</aside>
<main class="main-panel">
<section class="composer-card">
<div class="section-head">
<h2>💬 Idea workspace</h2>
<p>Paste messy text, fragments, or unclear thoughts. This assistant will organize them into something more useful.</p>
</div>
<div class="field-wrap">
<label for="user-input" class="field-label">Your rough notes or request</label>
<textarea id="user-input" placeholder="Example: I need help turning this half-formed product idea into a short brief with goals, audience, and next steps..."></textarea>
</div>
<div class="actions-row">
<button id="send-btn" type="button">Refine this idea</button>
</div>
<div id="inline-error" class="inline-error hidden" role="alert"></div>
</section>
<section class="results-card">
<div class="section-head">
<h2>📌 Clarified output</h2>
<p>Your refined response will appear below.</p>
</div>
<div id="results-container" class="results-container">
<div class="empty-state" id="empty-state">
<div class="empty-emoji">🪄</div>
<h3>Ready to untangle rough ideas</h3>
<ul>
<li>Turn fragments into a clear request</li>
<li>Extract the key points from messy notes</li>
<li>Rewrite vague drafts into cleaner language</li>
<li>Create practical next steps from ambiguity</li>
</ul>
</div>
</div>
</section>
</main>
</div>
<div id="loading-overlay" class="loading-overlay hidden" aria-hidden="true">
<div class="spinner-card">
<div class="spinner"></div>
<p>Refining your idea...</p>
</div>
</div>
<script>{{ js_code }}</script>
</body>
</html>
:root {
--bg: #f4f7fb;
--panel: #ffffff;
--panel-soft: #eef4ff;
--text: #172033;
--muted: #5f6b85;
--border: #d8e1f0;
--accent: #4f7cff;
--accent-strong: #2f5fe6;
--danger: #c93c3c;
--shadow: 0 18px 45px rgba(24, 39, 75, 0.10);
--radius-xl: 24px;
--radius-lg: 18px;
--radius-md: 12px;
--font: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; background: var(--bg); color: var(--text); font-family: var(--font); }
body { min-height: 100vh; }
.hidden { display: none !important; }
.app-shell {
display: grid;
grid-template-columns: 340px 1fr;
gap: 24px;
max-width: 1400px;
margin: 0 auto;
padding: 24px;
}
.side-panel, .composer-card, .results-card {
background: var(--panel);
border: 1px solid var(--border);
border-radius: var(--radius-xl);
box-shadow: var(--shadow);
}
.side-panel {
padding: 22px;
height: fit-content;
}
.brand-card {
display: flex;
align-items: flex-start;
gap: 14px;
padding-bottom: 18px;
border-bottom: 1px solid var(--border);
}
.brand-emoji, .empty-emoji {
font-size: 32px;
}
.brand-card h1, .section-head h2, .panel-block h2, .empty-state h3 {
margin: 0 0 8px 0;
}
.brand-card p, .section-head p, .panel-block p, .empty-state li {
color: var(--muted);
line-height: 1.55;
}
.panel-block {
margin-top: 22px;
}
.main-panel {
display: grid;
grid-template-rows: auto 1fr;
gap: 24px;
}
.composer-card, .results-card {
padding: 24px;
}
.field-wrap {
margin-top: 18px;
}
.field-label {
display: block;
font-weight: 700;
margin-bottom: 10px;
}
textarea, select {
width: 100%;
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 14px 16px;
font: inherit;
color: var(--text);
background: #fff;
}
textarea {
min-height: 180px;
resize: vertical;
line-height: 1.55;
}
textarea:focus-visible, select:focus-visible, button:focus-visible {
outline: 3px solid rgba(79, 124, 255, 0.25);
outline-offset: 2px;
}
.actions-row {
margin-top: 16px;
display: flex;
gap: 12px;
align-items: center;
}
button {
border: 0;
border-radius: 999px;
padding: 12px 18px;
font: inherit;
font-weight: 700;
cursor: pointer;
transition: transform 0.15s ease, background 0.15s ease, opacity 0.15s ease;
}
#send-btn {
background: var(--accent);
color: white;
}
#send-btn:hover { background: var(--accent-strong); transform: translateY(-1px); }
button:disabled { opacity: 0.6; cursor: not-allowed; transform: none; }
.chip-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.chip-btn {
text-align: left;
background: var(--panel-soft);
color: var(--text);
border: 1px solid var(--border);
border-radius: 14px;
}
.results-container {
margin-top: 18px;
min-height: 320px;
background: linear-gradient(180deg, #fbfcff 0%, #f6f9ff 100%);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 18px;
}
.message {
padding: 14px 16px;
border-radius: 16px;
margin-bottom: 12px;
white-space: pre-wrap;
line-height: 1.6;
}
.message.user {
background: #eef4ff;
border: 1px solid #cfe0ff;
}
.message.assistant {
background: #ffffff;
border: 1px solid var(--border);
}
.inline-error {
margin-top: 14px;
background: #fff2f2;
color: var(--danger);
border: 1px solid #f0c9c9;
border-radius: 12px;
padding: 12px 14px;
}
.empty-state ul {
margin: 10px 0 0 18px;
padding: 0;
}
.loading-overlay {
position: fixed;
inset: 0;
background: rgba(18, 25, 40, 0.38);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.spinner-card {
background: white;
border-radius: 20px;
padding: 24px 28px;
box-shadow: var(--shadow);
display: flex;
align-items: center;
gap: 14px;
}
.spinner {
width: 24px;
height: 24px;
border-radius: 50%;
border: 3px solid #d9e3ff;
border-top-color: var(--accent);
animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (max-width: 1200px) {
.app-shell { grid-template-columns: 1fr; }
}
@media (max-width: 768px) {
.app-shell { padding: 16px; gap: 16px; }
.composer-card, .results-card, .side-panel { padding: 18px; }
#send-btn { width: 100%; }
.actions-row { flex-direction: column; align-items: stretch; }
}
function escapeHtml(str) {
return String(str)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
(function () {
const userInput = document.getElementById('user-input');
const modeSelect = document.getElementById('mode-select');
const sendBtn = document.getElementById('send-btn');
const resultsContainer = document.getElementById('results-container');
const emptyState = document.getElementById('empty-state');
const inlineError = document.getElementById('inline-error');
const loadingOverlay = document.getElementById('loading-overlay');
const chipButtons = Array.from(document.querySelectorAll('.chip-btn'));
let isReady = false;
function setError(message) {
if (!message) {
inlineError.textContent = '';
inlineError.classList.add('hidden');
return;
}
inlineError.textContent = message;
inlineError.classList.remove('hidden');
}
function setLoading(loading) {
sendBtn.disabled = loading;
loadingOverlay.classList.toggle('hidden', !loading);
loadingOverlay.setAttribute('aria-hidden', loading ? 'false' : 'true');
}
function checkReady() {
const pyodideReady = !!window.pyodideReady;
if (!pyodideReady) {
return { ok: false, message: 'The idea clarifier is still initializing. Please wait a moment and try again.' };
}
if (window.PROVIDER === 'local') {
if (!window.agentManager || !window.agentManager.isLoaded) {
return { ok: false, message: 'Please load a local model from the top bar before refining your idea.' };
}
}
return { ok: true, message: '' };
}
window.addMessage = function (type, content) {
if (emptyState) emptyState.classList.add('hidden');
const safeType = type === 'user' ? 'user' : 'assistant';
const existing = resultsContainer.querySelector('.message.streaming');
if (existing && safeType === 'assistant') {
existing.classList.remove('streaming');
existing.classList.add('assistant');
existing.innerHTML = escapeHtml(content);
return;
}
const div = document.createElement('div');
div.className = 'message ' + safeType;
div.innerHTML = escapeHtml(content);
resultsContainer.appendChild(div);
resultsContainer.scrollTop = resultsContainer.scrollHeight;
};
function addStreamingPlaceholder() {
if (emptyState) emptyState.classList.add('hidden');
const div = document.createElement('div');
div.className = 'message assistant streaming';
div.innerHTML = escapeHtml('Working on your clarified version...');
resultsContainer.appendChild(div);
resultsContainer.scrollTop = resultsContainer.scrollHeight;
}
async function handleSubmit() {
setError('');
const readiness = checkReady();
if (!readiness.ok) {
setError(readiness.message);
return;
}
const text = userInput.value.trim();
const mode = modeSelect.value;
if (!text) {
setError('Please paste your rough notes or request so the idea clarifier has something to refine.');
return;
}
const composedQuery = 'Mode: ' + mode + '\n\nUser input:\n' + text;
try {
setLoading(true);
window.addMessage('user', text);
addStreamingPlaceholder();
window.pyodide.globals.set('user_query', composedQuery);
const result = await window.pyodide.runPythonAsync('await process_user_query(user_query)');
const streaming = resultsContainer.querySelector('.message.streaming');
if (streaming) {
streaming.classList.remove('streaming');
streaming.classList.add('assistant');
streaming.innerHTML = escapeHtml(result || 'No response was returned.');
} else {
window.addMessage('assistant', result || 'No response was returned.');
}
} catch (err) {
const streaming = resultsContainer.querySelector('.message.streaming');
if (streaming) streaming.remove();
setError('Something went wrong while refining your idea. Please try again.');
console.error(err);
} finally {
setLoading(false);
}
}
sendBtn.addEventListener('click', handleSubmit);
chipButtons.forEach((btn) => {
btn.addEventListener('click', () => {
userInput.value = btn.getAttribute('data-prompt') || '';
userInput.focus();
setError('');
});
});
modeSelect.addEventListener('change', () => {
setError('');
});
userInput.addEventListener('input', () => {
setError('');
});
document.addEventListener('pyodide-ready', () => {
isReady = true;
window.pyodideReady = true;
});
if (window.pyodideReady) {
isReady = true;
}
})();
async def process_user_query(query: str) -> str:
"""Process a rough user request into a clearer output.
Args:
query: The user's raw request, including selected mode and text.
Returns:
A refined and useful response.
"""
system_prompt = globals().get("TEMPLATE_SYSTEM_PROMPT", "")
if not system_prompt:
system_prompt = (
"You clarify messy user input. "
"Read the request carefully. "
"If the input is vague, still be helpful. "
"Follow the selected mode if present. "
"For clarify mode, produce a cleaner request. "
"For summarize mode, produce a short summary with bullets. "
"For rewrite mode, rewrite into polished prose. "
"For plan mode, produce practical next steps. "
"Be concise, structured, and useful."
)
result = await call_llm(query, system_prompt)
return str(result)
Name your agent
Based on . You'll get your own private copy to try and customize.