Commit 70a7b700 authored by Vũ Hoàng Anh's avatar Vũ Hoàng Anh

feat(ui): redesign prompt manager + cookbook SVG icons + markdown styling

- Rewrite manage_prompt.css: larger fonts (14-16px), premium card layout, better spacing
- Cookbook sidebar: replace emoji with color-coded SVG Lucide icons per group
- Cookbook CSS: add proper pre/code/blockquote/heading styling for markdown content
- Support separator groups (no recipes) in sidebar navigation
- Add mermaid diagram container styles
- Light theme support for code blocks
parent 7aae58aa
...@@ -553,3 +553,189 @@ pre { ...@@ -553,3 +553,189 @@ pre {
gap: 16px; gap: 16px;
} }
} }
/* ═══ Markdown Content Enhancements ═══ */
/* ASCII art / preformatted blocks — preserve box-drawing chars */
.recipe-content pre {
background: #0d1117;
color: #e6edf3;
border: 1px solid var(--cb-border);
border-radius: 10px;
padding: 20px 24px;
font-family: 'JetBrains Mono', 'Consolas', 'Courier New', monospace;
font-size: 13px;
line-height: 1.6;
overflow-x: auto;
white-space: pre;
word-break: normal;
margin: 20px 0;
tab-size: 4;
}
body.light-theme .recipe-content pre {
background: #f6f8fa;
color: #24292f;
border-color: #d0d7de;
}
/* Inline code */
.recipe-content code {
background: rgba(59,130,246,0.08);
color: var(--cb-accent);
padding: 2px 7px;
border-radius: 5px;
font-family: 'JetBrains Mono', 'Consolas', monospace;
font-size: 0.88em;
font-weight: 500;
}
.recipe-content pre code {
background: none;
color: inherit;
padding: 0;
border-radius: 0;
font-size: inherit;
}
/* Blockquotes */
.recipe-content blockquote {
border-left: 4px solid var(--cb-accent);
background: rgba(59,130,246,0.05);
margin: 20px 0;
padding: 16px 20px;
border-radius: 0 10px 10px 0;
color: var(--cb-text);
font-size: 15px;
line-height: 1.7;
}
.recipe-content blockquote p {
margin: 0;
}
/* Headings inside markdown */
.recipe-content .text-block h1 {
font-size: 28px;
font-weight: 800;
margin: 40px 0 16px;
padding-bottom: 10px;
border-bottom: 2px solid var(--cb-border);
}
.recipe-content .text-block h2 {
font-size: 22px;
font-weight: 700;
margin: 36px 0 14px;
padding-bottom: 8px;
border-bottom: 1px solid var(--cb-border);
}
.recipe-content .text-block h3 {
font-size: 18px;
font-weight: 700;
margin: 28px 0 12px;
color: var(--cb-accent);
}
.recipe-content .text-block h4 {
font-size: 16px;
font-weight: 600;
margin: 24px 0 10px;
color: var(--cb-text-muted);
}
/* Lists */
.recipe-content .text-block ul,
.recipe-content .text-block ol {
padding-left: 24px;
margin: 12px 0;
}
.recipe-content .text-block li {
margin-bottom: 6px;
line-height: 1.7;
font-size: 15px;
}
/* Horizontal rules */
.recipe-content hr {
border: none;
border-top: 1px solid var(--cb-border);
margin: 32px 0;
}
/* Images */
.recipe-content .text-block img {
max-width: 100%;
border-radius: 10px;
border: 1px solid var(--cb-border);
margin: 16px 0;
}
/* ═══ Sidebar SVG Group Icons ═══ */
.nav-group-title {
display: flex;
align-items: center;
gap: 8px;
}
.nav-group-title .group-icon {
width: 18px;
height: 18px;
flex-shrink: 0;
opacity: 0.7;
}
.nav-group-title:hover .group-icon {
opacity: 1;
}
/* Separator group styling */
.nav-group-separator {
padding: 18px 20px 6px;
font-size: 10px;
font-weight: 800;
letter-spacing: 0.12em;
color: var(--cb-accent);
border-top: 1px solid var(--cb-border);
margin-top: 8px;
}
.nav-group-separator:first-child {
border-top: none;
margin-top: 0;
}
/* ═══ Mermaid diagram containers ═══ */
.recipe-content .mermaid {
background: rgba(255,255,255,0.03);
border: 1px solid var(--cb-border);
border-radius: 10px;
padding: 20px;
margin: 20px 0;
text-align: center;
overflow-x: auto;
}
body.light-theme .recipe-content .mermaid {
background: #ffffff;
}
/* ═══ Strong / Bold emphasis ═══ */
.recipe-content .text-block strong {
font-weight: 700;
color: var(--cb-text);
}
/* ═══ Links ═══ */
.recipe-content .text-block a {
color: var(--cb-accent);
text-decoration: none;
border-bottom: 1px solid transparent;
transition: border-color 0.2s;
}
.recipe-content .text-block a:hover {
border-bottom-color: var(--cb-accent);
}
...@@ -58,16 +58,61 @@ document.addEventListener('DOMContentLoaded', async () => { ...@@ -58,16 +58,61 @@ document.addEventListener('DOMContentLoaded', async () => {
console.error('Failed to load registry:', err); console.error('Failed to load registry:', err);
} }
// SVG Icon Map for sidebar group titles
const GROUP_ICONS = {
'AI Chatbot': '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#3b82f6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M2 12h20"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>',
'Kiến trúc': '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#8b5cf6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>',
'Tư vấn': '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#f59e0b" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/></svg>',
'Phối đồ': '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#ec4899" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20.38 3.46L16 2 12 5.5 8 2l-4.38 1.46a2 2 0 0 0-1.34 2.23l2.1 14.32A2 2 0 0 0 6.35 21.5h11.3a2 2 0 0 0 1.97-1.49l2.1-14.32a2 2 0 0 0-1.34-2.23z"/></svg>',
'Event': '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#f97316" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/><path d="M8 14h.01"/><path d="M12 14h.01"/><path d="M16 14h.01"/></svg>',
'AI Core': '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#06b6d4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a4 4 0 0 0-4 4v2H6a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2h-2V6a4 4 0 0 0-4-4z"/><circle cx="12" cy="14" r="2"/></svg>',
'Tool': '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#10b981" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>',
'SCALE': '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#3b82f6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>',
'Cá nhân': '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#a855f7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>',
'Voice': '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/></svg>',
'Đo lường': '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/></svg>',
'EXPERIMENTAL': '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#eab308" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 3h6v7l4 8H5l4-8V3z"/><line x1="8" y1="3" x2="16" y2="3"/></svg>',
'Data': '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#6366f1" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg>',
'Social': '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#3b82f6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>',
'Testing': '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#f59e0b" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 3h6v7l4 8H5l4-8V3z"/><line x1="8" y1="3" x2="16" y2="3"/></svg>',
'Hạ tầng': '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>',
'Feedback': '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#f472b6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg>',
};
function getGroupIcon(groupName) {
for (const [key, svg] of Object.entries(GROUP_ICONS)) {
if (groupName.includes(key)) return svg;
}
return '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>';
}
function cleanGroupName(name) {
// Remove emoji and special separator chars
return name.replace(/[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{FE00}-\u{FE0F}\u{1F000}-\u{1FFFF}]/gu, '')
.replace(/━+/g, '').replace(/\s+/g, ' ').trim();
}
// 2. Render Sidebar // 2. Render Sidebar
function renderSidebar(data) { function renderSidebar(data) {
recipeNav.innerHTML = ''; recipeNav.innerHTML = '';
data.groups.forEach(group => { data.groups.forEach(group => {
// Check if this is a separator group (no recipes, just a label)
const cleanName = cleanGroupName(group.name);
if (group.recipes.length === 0) {
const sepEl = document.createElement('div');
sepEl.className = 'nav-group-separator';
sepEl.textContent = cleanName;
recipeNav.appendChild(sepEl);
return;
}
const groupEl = document.createElement('div'); const groupEl = document.createElement('div');
groupEl.className = 'nav-group'; groupEl.className = 'nav-group';
const titleEl = document.createElement('div'); const titleEl = document.createElement('div');
titleEl.className = 'nav-group-title'; titleEl.className = 'nav-group-title';
titleEl.innerHTML = `<span>${group.name}</span> <span class="toggle-icon"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg></span>`; const iconSvg = getGroupIcon(group.name);
titleEl.innerHTML = `<span class="group-icon">${iconSvg}</span><span>${cleanName}</span> <span class="toggle-icon"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg></span>`;
const listEl = document.createElement('ul'); const listEl = document.createElement('ul');
listEl.className = 'recipe-list'; listEl.className = 'recipe-list';
......
$env:PORT=8000; .\ds2api.exe
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment