Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 112 additions & 24 deletions desktop-app/resources/js/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -6596,7 +6596,8 @@ document.addEventListener("DOMContentLoaded", async function () {
WaveDrom: 'wavedrom',
Markmap: 'markmap',
Mermaid: 'mermaid',
'ABC Notation': 'abc'
'ABC Notation': 'abc',
'STL (3D)': 'stl'
};
return engines[category] || null;
}
Expand Down Expand Up @@ -6698,7 +6699,7 @@ document.addEventListener("DOMContentLoaded", async function () {
const cleanCode = getCleanCode(template.code);
const engine = getDiagramEngineForCategory(template.category);
try {
if (template.category === 'ABC Notation') {
if (template.category === 'ABC Notation' || template.category === 'STL (3D)') {
callback(null);
return;
}
Expand Down Expand Up @@ -6758,16 +6759,45 @@ document.addEventListener("DOMContentLoaded", async function () {
if (previewCode) previewCode.value = '';
confirmBtn.disabled = true;

const categories = [
'Mermaid',
'PlantUML',
'Graphviz',
'D2',
'Vega-Lite',
'ABC Notation',
'WaveDrom',
'Markmap'
const categoryGroups = [
{
label: 'Diagrams',
icon: 'bi-diagram-3',
categories: ['Mermaid', 'PlantUML', 'Graphviz', 'D2']
},
{
label: 'Mind Maps',
icon: 'bi-diagram-2',
categories: ['Markmap']
},
{
label: 'Data Visualization',
icon: 'bi-bar-chart-line',
categories: ['Vega-Lite']
},
{
label: 'Technical Notation',
icon: 'bi-code-slash',
categories: ['WaveDrom', 'ABC Notation']
},
{
label: '3D Models',
icon: 'bi-box',
categories: ['STL (3D)']
}
];

const categoryIcons = {
Mermaid: 'bi-water',
PlantUML: 'bi-leaf',
Graphviz: 'bi-bezier2',
D2: 'bi-badge-2d',
Markmap: 'bi-diagram-2',
'Vega-Lite': 'bi-bar-chart-line',
WaveDrom: 'bi-activity',
'ABC Notation': 'bi-music-note-beamed',
'STL (3D)': 'bi-box'
};

const svgFlowchart = `<svg viewBox="0 0 160 120" width="100%" height="100%"><rect x="45" y="15" width="70" height="26" fill="#f4f5f7" stroke="#673ab7" stroke-width="1.5" rx="3"/><text x="80" y="31" font-size="9" text-anchor="middle" font-family="sans-serif" fill="#333" font-weight="bold">Start</text><path d="M 80 41 L 80 75" stroke="#333" stroke-width="1.2" marker-end="url(#arrow-f)"/><rect x="45" y="75" width="70" height="26" fill="#f4f5f7" stroke="#673ab7" stroke-width="1.5" rx="3"/><text x="80" y="91" font-size="9" text-anchor="middle" font-family="sans-serif" fill="#333" font-weight="bold">End</text><defs><marker id="arrow-f" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="5" markerHeight="5" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#333"/></marker></defs></svg>`;
const svgMermaidFlowchartLR = `<svg viewBox="0 0 160 120" width="100%" height="100%"><rect x="10" y="47" width="40" height="26" fill="#e8f5e9" stroke="#2e7d32" stroke-width="1.5" rx="3"/><text x="30" y="63" font-size="8" text-anchor="middle" font-family="sans-serif" fill="#1b5e20" font-weight="bold">Left</text><path d="M 50 60 L 110 60" stroke="#2e7d32" stroke-width="1.2" marker-end="url(#arrow-flr)"/><rect x="110" y="47" width="40" height="26" fill="#e8f5e9" stroke="#2e7d32" stroke-width="1.5" rx="3"/><text x="130" y="63" font-size="8" text-anchor="middle" font-family="sans-serif" fill="#1b5e20" font-weight="bold">Right</text><defs><marker id="arrow-flr" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="5" markerHeight="5" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#2e7d32"/></marker></defs></svg>`;
Expand Down Expand Up @@ -6879,6 +6909,10 @@ document.addEventListener("DOMContentLoaded", async function () {
const svgMarkmapChecklist = `<svg viewBox="0 0 160 120" width="100%" height="100%"><rect x="10" y="50" width="35" height="18" rx="2" fill="#ede7f6" stroke="#5e35b1" stroke-width="1.5"/><text x="27" y="61" font-size="7" text-anchor="middle" font-family="sans-serif" fill="#5e35b1" font-weight="bold">Tasks</text><path d="M 45 59 C 65 59, 70 30, 90 30" fill="none" stroke="#5e35b1" stroke-width="1.5"/><path d="M 45 59 C 65 59, 70 90, 90 90" fill="none" stroke="#5e35b1" stroke-width="1.5"/><circle cx="90" cy="30" r="3" fill="#5e35b1"/><circle cx="90" cy="90" r="3" fill="#5e35b1"/><text x="96" y="33" font-size="7" font-family="sans-serif">☒ Todo A</text><text x="96" y="93" font-size="7" font-family="sans-serif">☑ Todo B</text></svg>`;
const svgMarkmapCode = `<svg viewBox="0 0 160 120" width="100%" height="100%"><rect x="10" y="50" width="35" height="18" rx="2" fill="#eceff1" stroke="#455a64" stroke-width="1.5"/><text x="27" y="61" font-size="7" text-anchor="middle" font-family="sans-serif" fill="#455a64" font-weight="bold">Project</text><path d="M 45 59 C 65 59, 70 30, 90 30" fill="none" stroke="#455a64" stroke-width="1.5"/><path d="M 45 59 C 65 59, 70 90, 90 90" fill="none" stroke="#455a64" stroke-width="1.5"/><circle cx="90" cy="30" r="3" fill="#455a64"/><circle cx="90" cy="90" r="3" fill="#455a64"/><text x="96" y="33" font-size="7" font-family="monospace">code()</text><text x="96" y="93" font-size="7" font-family="monospace">test()</text></svg>`;

// STL (3D) local previews
const svgStlTetrahedron = `<svg viewBox="0 0 160 120" width="100%" height="100%"><polygon points="80,18 34,90 126,90" fill="#dbeafe" stroke="#2563eb" stroke-width="2"/><polygon points="80,18 34,90 80,70" fill="#93c5fd" opacity="0.85"/><polygon points="80,18 126,90 80,70" fill="#60a5fa" opacity="0.85"/><polygon points="34,90 126,90 80,70" fill="#1d4ed8" opacity="0.75"/><line x1="80" y1="18" x2="80" y2="70" stroke="#1e40af" stroke-width="1.5"/><text x="80" y="108" text-anchor="middle" font-size="9" font-family="monospace" fill="#1e3a8a" font-weight="bold">STL</text></svg>`;
const svgStlCube = `<svg viewBox="0 0 160 120" width="100%" height="100%"><polygon points="58,24 118,44 88,62 28,42" fill="#dbeafe" stroke="#2563eb" stroke-width="1.8"/><polygon points="28,42 88,62 88,100 28,80" fill="#93c5fd" stroke="#2563eb" stroke-width="1.8"/><polygon points="88,62 118,44 118,82 88,100" fill="#60a5fa" stroke="#2563eb" stroke-width="1.8"/><line x1="58" y1="24" x2="58" y2="62" stroke="#1d4ed8" stroke-width="1.4" stroke-dasharray="3 3"/><line x1="58" y1="62" x2="28" y2="80" stroke="#1d4ed8" stroke-width="1.4" stroke-dasharray="3 3"/><line x1="58" y1="62" x2="118" y2="82" stroke="#1d4ed8" stroke-width="1.4" stroke-dasharray="3 3"/><text x="80" y="16" text-anchor="middle" font-size="8" font-family="monospace" fill="#1e3a8a" font-weight="bold">20 mm cube</text></svg>`;

const templates = [
// Mermaid
{
Expand Down Expand Up @@ -7462,6 +7496,24 @@ document.addEventListener("DOMContentLoaded", async function () {
label: 'Inline Code Blocks',
svg: svgMarkmapCode,
code: '```markmap\n# Development\n## Languages\n- `JavaScript`\n- `Python`\n## Functions\n- `main()`\n- `helper_func()`\n```\n'
},

// STL (3D)
{
id: 'stl-tetrahedron',
category: 'STL (3D)',
title: 'Tetrahedron Mesh',
label: 'Tetrahedron Mesh',
svg: svgStlTetrahedron,
code: '```stl\nsolid tetrahedron\n facet normal 0 0 1\n outer loop\n vertex 0 0 0\n vertex 1 0 0\n vertex 0 1 0\n endloop\n endfacet\n facet normal 0 1 0\n outer loop\n vertex 0 0 0\n vertex 0 0 1\n vertex 1 0 0\n endloop\n endfacet\n facet normal 1 0 0\n outer loop\n vertex 0 0 0\n vertex 0 1 0\n vertex 0 0 1\n endloop\n endfacet\n facet normal 1 1 1\n outer loop\n vertex 1 0 0\n vertex 0 0 1\n vertex 0 1 0\n endloop\n endfacet\nendsolid tetrahedron\n```\n'
},
{
id: 'stl-calibration-cube',
category: 'STL (3D)',
title: 'Calibration Cube',
label: 'Calibration Cube',
svg: svgStlCube,
code: '```stl\nsolid calibration_cube_20mm\n facet normal 0 0 -1\n outer loop\n vertex 0 0 0\n vertex 20 0 0\n vertex 20 20 0\n endloop\n endfacet\n facet normal 0 0 -1\n outer loop\n vertex 0 0 0\n vertex 20 20 0\n vertex 0 20 0\n endloop\n endfacet\n facet normal 0 0 1\n outer loop\n vertex 0 0 20\n vertex 20 20 20\n vertex 20 0 20\n endloop\n endfacet\n facet normal 0 0 1\n outer loop\n vertex 0 0 20\n vertex 0 20 20\n vertex 20 20 20\n endloop\n endfacet\n facet normal 0 -1 0\n outer loop\n vertex 0 0 0\n vertex 0 0 20\n vertex 20 0 20\n endloop\n endfacet\n facet normal 0 -1 0\n outer loop\n vertex 0 0 0\n vertex 20 0 20\n vertex 20 0 0\n endloop\n endfacet\n facet normal 1 0 0\n outer loop\n vertex 20 0 0\n vertex 20 0 20\n vertex 20 20 20\n endloop\n endfacet\n facet normal 1 0 0\n outer loop\n vertex 20 0 0\n vertex 20 20 20\n vertex 20 20 0\n endloop\n endfacet\n facet normal 0 1 0\n outer loop\n vertex 20 20 0\n vertex 20 20 20\n vertex 0 20 20\n endloop\n endfacet\n facet normal 0 1 0\n outer loop\n vertex 20 20 0\n vertex 0 20 20\n vertex 0 20 0\n endloop\n endfacet\n facet normal -1 0 0\n outer loop\n vertex 0 20 0\n vertex 0 20 20\n vertex 0 0 20\n endloop\n endfacet\n facet normal -1 0 0\n outer loop\n vertex 0 20 0\n vertex 0 0 20\n vertex 0 0 0\n endloop\n endfacet\nendsolid calibration_cube_20mm\n```\n'
}
];

Expand All @@ -7470,18 +7522,54 @@ document.addEventListener("DOMContentLoaded", async function () {

function renderSidebar() {
sidebar.textContent = '';
categories.forEach(cat => {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'diagram-sidebar-btn';
if (cat === activeCategory) btn.classList.add('is-active');
btn.textContent = cat;
btn.addEventListener('click', () => {
activeCategory = cat;
renderSidebar();
renderGrid();
categoryGroups.forEach(group => {
const isExpanded = group.categories.includes(activeCategory);
const groupEl = document.createElement('div');
groupEl.className = 'diagram-sidebar-group';
if (isExpanded) groupEl.classList.add('is-expanded');

const heading = document.createElement('button');
heading.type = 'button';
heading.className = 'diagram-sidebar-group-header';
heading.setAttribute('aria-expanded', String(isExpanded));
heading.innerHTML = `
<i class="bi ${group.icon} diagram-sidebar-group-icon" aria-hidden="true"></i>
<span>${group.label}</span>
<i class="bi ${isExpanded ? 'bi-chevron-up' : 'bi-chevron-right'} diagram-sidebar-chevron" aria-hidden="true"></i>
`;
heading.addEventListener('click', () => {
if (!isExpanded) {
activeCategory = group.categories[0];
selectedTemplate = null;
renderSidebar();
renderGrid();
}
});
groupEl.appendChild(heading);

const itemsEl = document.createElement('div');
itemsEl.className = 'diagram-sidebar-items';

group.categories.forEach(cat => {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'diagram-sidebar-btn';
if (cat === activeCategory) btn.classList.add('is-active');
btn.innerHTML = `
<i class="bi ${categoryIcons[cat] || 'bi-circle'} diagram-sidebar-item-icon" aria-hidden="true"></i>
<span>${cat}</span>
`;
btn.addEventListener('click', () => {
activeCategory = cat;
renderSidebar();
renderGrid();
});
itemsEl.appendChild(btn);
});
sidebar.appendChild(btn);

groupEl.appendChild(itemsEl);

sidebar.appendChild(groupEl);
});
}

Expand Down Expand Up @@ -7514,7 +7602,7 @@ document.addEventListener("DOMContentLoaded", async function () {
const previewDiv = document.createElement('div');
previewDiv.className = 'diagram-card-preview';

const catClass = t.category.toLowerCase().replace(/\s+/g, '');
const catClass = t.category.toLowerCase().replace(/[^a-z0-9]+/g, '');

previewDiv.innerHTML = `
<div style="display:flex; align-items:center; justify-content:center; width:100%; height:100%;">
Expand Down Expand Up @@ -7552,7 +7640,7 @@ document.addEventListener("DOMContentLoaded", async function () {
if (previewCode) previewCode.value = t.code.trim();
confirmBtn.disabled = false;

const catClass = t.category.toLowerCase().replace(/\s+/g, '');
const catClass = t.category.toLowerCase().replace(/[^a-z0-9]+/g, '');
// Render bottom preview container with API image & fallback
previewContainer.innerHTML = `
<div class="diagram-svg-container diagram-svg-${catClass}" style="width:100%; height:100%; display:flex; align-items:center; justify-content:center; overflow:hidden;">
Expand Down
131 changes: 115 additions & 16 deletions desktop-app/resources/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -5366,39 +5366,114 @@ html[data-theme="dark"] .d2-diagram > svg:not([data-diagram-native-dark="true"])
}

.diagram-modal-sidebar {
width: 220px;
border-right: 1px solid var(--border-color);
padding: 16px 8px;
width: 260px;
margin: 16px 0 16px 16px;
border: 1px solid var(--border-color);
border-radius: 8px;
background: var(--bg-color);
padding: 0;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 4px;
gap: 0;
flex-shrink: 0;
}

.diagram-sidebar-btn {
.diagram-sidebar-group {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 14px;
flex-direction: column;
border-top: 1px solid var(--border-color);
}

.diagram-sidebar-group:first-child {
border-top: none;
}

.diagram-sidebar-group-header,
.diagram-sidebar-btn {
width: 100%;
border: none;
background: transparent;
color: var(--text-color);
text-align: left;
border-radius: 6px;
cursor: pointer;
font-size: 0.9rem;
font-weight: 500;
font-size: 0.95rem;
text-align: left;
transition: background-color 0.15s ease, color 0.15s ease;
}

.diagram-sidebar-group-header {
display: grid;
grid-template-columns: 24px 1fr 18px;
align-items: center;
gap: 12px;
min-height: 62px;
padding: 0 18px;
font-weight: 500;
}

.diagram-sidebar-group-header:hover {
background-color: var(--button-hover-bg, rgba(120, 120, 120, 0.06));
}

.diagram-sidebar-group-icon,
.diagram-sidebar-item-icon,
.diagram-sidebar-chevron {
display: inline-flex;
align-items: center;
justify-content: center;
line-height: 1;
}

.diagram-sidebar-group-icon {
color: var(--text-color);
font-size: 1.1rem;
}

.diagram-sidebar-chevron {
justify-self: end;
color: var(--text-muted, #6b7280);
font-size: 0.85rem;
}

.diagram-sidebar-items {
display: none;
flex-direction: column;
gap: 4px;
padding: 0 12px 12px;
}

.diagram-sidebar-group.is-expanded .diagram-sidebar-items {
display: flex;
}

.diagram-sidebar-btn {
display: flex;
align-items: center;
gap: 14px;
min-height: 50px;
padding: 0 16px 0 20px;
border-radius: 7px;
font-weight: 500;
}

.diagram-sidebar-btn:hover {
background-color: var(--button-hover-bg, rgba(120, 120, 120, 0.1));
background-color: var(--button-hover-bg, rgba(120, 120, 120, 0.06));
}

.diagram-sidebar-btn.is-active {
background-color: var(--accent-color, #0076ff);
color: #fff;
background-color: color-mix(in srgb, var(--accent-color, #0076ff) 13%, transparent);
color: var(--accent-color, #0076ff);
font-weight: 650;
}

.diagram-sidebar-btn.is-active .diagram-sidebar-item-icon {
color: var(--accent-color, #0076ff);
}

.diagram-sidebar-item-icon {
width: 22px;
color: var(--text-muted, #6b7280);
font-size: 1.05rem;
}

.diagram-modal-content {
Expand Down Expand Up @@ -5646,18 +5721,42 @@ html[data-theme="dark"] .diagram-preview-container {
display: none !important;
}

@media (max-width: 768px) {
@media (max-width: 640px) {
.diagram-modal-body {
flex-direction: column;
}
.diagram-modal-sidebar {
width: 100%;
height: 60px;
margin: 0;
flex-direction: row;
border-right: none;
border-bottom: 1px solid var(--border-color);
border-left: none;
border-radius: 0;
padding: 8px;
gap: 6px;
}

.diagram-sidebar-group-header {
display: none;
}

.diagram-sidebar-group {
display: contents;
}

.diagram-sidebar-items {
display: contents;
}

.diagram-sidebar-btn {
width: auto;
min-height: 36px;
flex-shrink: 0;
padding: 0 12px;
}

.diagram-modal-box {
height: 95vh;
}
Expand Down
Loading
Loading