Skip to content

Commit 390d559

Browse files
committed
feat: draw docgen integration — Excalidraw + Mermaid whiteboard pipeline
- Added {{Draw:}} tag rendering pipeline (transformDrawMarkdown + bindDrawPreviewActions) - Added 🎨 Draw toolbar button with Excalidraw/Mermaid insert action - Added excalidraw.com to CSP frame-src for iframe embedding - Added draw-docgen.css (309 lines) — card UI, tool pills, Mermaid editor, dark mode - Added draw-docgen.js lazy-loaded module (Phase 3j) - Expanded DOMPurify allowlist with data-draw-index, data-draw-tool, data-tool, data-skill
1 parent 5d0ddff commit 390d559

6 files changed

Lines changed: 381 additions & 4 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
| **Desktop** | Native app via Neutralino.js with system tray and offline support |
3737
| **Code Execution** | 7 languages in-browser: Bash ([just-bash](https://justbash.dev/)), Math (Nerdamer), LaTeX (MathJax + Nerdamer evaluation), Python ([Pyodide](https://pyodide.org/)), HTML (sandboxed iframe, `html-autorun` for widgets/quizzes), JavaScript (sandboxed iframe), SQL ([sql.js](https://sql.js.org/) SQLite) · 25+ compiled languages via [Judge0 CE](https://ce.judge0.com): C, C++, Rust, Go, Java, TypeScript, Kotlin, Scala, Ruby, Swift, Haskell, Dart, C#, and more · **▶ Run All** notebook engine — one-click sequential execution with preflight dialog (block table with model/status), pre-execution model loading (AI + TTS auto-loaded before blocks run), progress bar, abort, per-block status badges, detailed console logging, and SQLite shared context store |
3838
| **Security** | Content Security Policy (CSP), SRI integrity hashes, XSS sanitization (DOMPurify), ReDoS protection, Firestore write-token ownership, API keys via HTTP headers, postMessage origin validation, 8-char passphrase minimum, sandboxed code execution |
39-
| **AI Document Tags** | `{{@AI:}}` text generation (`@think: Yes` for deep reasoning), `{{@Image:}}` image generation (Gemini Imagen), `{{@OCR:}}` image-to-text extraction (Text/Math/Table modes via Granite Docling 258M or Florence-2 230M, PDF page rendering via pdf.js), `{{@TTS:}}` text-to-speech playback (Kokoro TTS per card, language selector, ▶ Play / ⬇ Save WAV), `{{@STT:}}` speech-to-text dictation (engine selector: Whisper/Voxtral/Web Speech API, 11 languages, Record/Stop/Insert/Clear), `{{@Translate:}}` translation (target language selector, integrated TTS pronunciation, cloud model routing), `{{@Game:}}` game builder (AI-generated or pre-built, Canvas 2D/Three.js/P5.js, import/export HTML) — `@` prefix syntax on all tag types + metadata fields (`@name`, `@use`, `@think`, `@search`, `@prompt`, `@step`, `@upload`, `@model`, `@engine`, `@lang`, `@prebuilt`); `@model:` field persists selected model per card with intelligent defaults (OCR→`granite-docling`, TTS→`kokoro-tts`, STT→`voxtral-stt`, Image→`imagen-ultra`); editable `@prompt:` textarea and `@step:` inputs in preview cards; description/prompt separation (bare text = label, `@prompt:` = AI instruction); 📎 image/PDF upload for multimodal vision analysis; per-card model selector with document-portable model persistence, concurrent block operations |
39+
| **AI Document Tags** | `{{@AI:}}` text generation (`@think: Yes` for deep reasoning), `{{@Image:}}` image generation (Gemini Imagen), `{{@OCR:}}` image-to-text extraction (Text/Math/Table modes via Granite Docling 258M or Florence-2 230M, PDF page rendering via pdf.js), `{{@TTS:}}` text-to-speech playback (Kokoro TTS per card, language selector, ▶ Play / ⬇ Save WAV), `{{@STT:}}` speech-to-text dictation (engine selector: Whisper/Voxtral/Web Speech API, 11 languages, Record/Stop/Insert/Clear), `{{@Translate:}}` translation (target language selector, integrated TTS pronunciation, cloud model routing), `{{@Game:}}` game builder (AI-generated or pre-built, Canvas 2D/Three.js/P5.js, import/export HTML), `{{@Draw:}}` whiteboard (Excalidraw + Mermaid, Insert/PNG/SVG export) — `@` prefix syntax on all tag types + metadata fields (`@name`, `@use`, `@think`, `@search`, `@prompt`, `@step`, `@upload`, `@model`, `@engine`, `@lang`, `@prebuilt`); `@model:` field persists selected model per card with intelligent defaults (OCR→`granite-docling`, TTS→`kokoro-tts`, STT→`voxtral-stt`, Image→`imagen-ultra`); editable `@prompt:` textarea and `@step:` inputs in preview cards; description/prompt separation (bare text = label, `@prompt:` = AI instruction); 📎 image/PDF upload for multimodal vision analysis; per-card model selector with document-portable model persistence, concurrent block operations |
4040
| **🔌 API Calls** | `{{API:}}` REST API integration — GET/POST/PUT/DELETE methods, custom headers, JSON body, response stored in `$(api_varName)` variables; inline review panel; toolbar GET/POST buttons |
4141
| **🔗 Agent Flow** | `{{Agent:}}` multi-step pipeline — define Step 1/2/3, chain outputs, per-card model + search provider selector, live step status indicators (⏳/✅/❌), review combined output |
4242
| **🔍 Web Search** | Toggle web search for AI — 7 providers: DuckDuckGo (free), Brave Search, Serper.dev, Tavily (AI-optimized), Google CSE, Wikipedia, Wikidata; search results injected into LLM context; source citations in responses; per-agent-card search provider selector |
@@ -459,6 +459,7 @@ TextAgent has undergone significant evolution since its inception. What started
459459

460460
| Date | Commits | Feature / Update |
461461
|------|---------|-----------------:|
462+
| **2026-03-18** | | 🎨 **Draw DocGen Integration** — full `{{Draw:}}` tag pipeline: `transformDrawMarkdown` + `bindDrawPreviewActions` in renderer, 🎨 Draw toolbar button, `excalidraw.com` added to CSP `frame-src`, `draw-docgen.css` (309-line standalone stylesheet with card UI, tool pills, Mermaid editor, dark mode), `draw-docgen.js` lazy-loaded as Phase 3j; DOMPurify allowlist expanded with `data-draw-index`, `data-draw-tool`, `data-tool`, `data-skill` |
462463
| **2026-03-17** | | 🎨 **Excalidraw Export Fix** — fixed broken Insert/PNG/SVG export buttons (`excalidrawAPI` was `null` due to Excalidraw 0.17+ using `excalidrawAPI` prop instead of `ref`); replaced canvas-scraping with native `exportToBlob`/`exportToSvg` APIs; Insert now replaces `{{Draw:}}` tag with image and closes whiteboard; inserted images use compact `gen-img:` registry (no raw base64 in editor) |
463464
| **2026-03-17** | | 🔒 **View-Locked Sharing & Shared Versions** — sharers can lock recipients to PPT or Preview mode via new pill selector in share modal; view lock stored server-side in Firestore (tamper-proof — stripping `&view=ppt` from URL doesn't bypass); `setViewMode()` guard blocks mode switching; non-matching view buttons visually disabled; "Previously Shared" section in share modal shows past shares per document with timestamps, view-mode badges (PPT/Preview), secure badge, and Copy/Delete buttons; shared versions tracked in localStorage keyed by parent cloud doc ID |
464465
| **2026-03-17** | | 🎨 **Three-Level Header & Read-Only Pill** — new three-level header visibility toggle (Full → Compact QAB → fully Hidden with floating "TextAgent" restore pill at top-center, 35% opacity, hover to reveal); header level persists via `localStorage`; "Read-only" shared-view pill repositioned from top-right to bottom-right corner with upward slide-in animation |
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Draw DocGen Integration — Excalidraw + Mermaid Whiteboard
2+
3+
- Added `{{Draw:}}` tag rendering pipeline to `renderer.js` via `transformDrawMarkdown` and `bindDrawPreviewActions`
4+
- Added 🎨 Draw toolbar button in `index.html` with Excalidraw/Mermaid insert action
5+
- Added `excalidraw.com` to CSP `frame-src` directive for iframe embedding
6+
- Added `draw-docgen.css` (309 lines) — full card UI, tool pills, Mermaid editor, dark mode
7+
- Added `draw-docgen.js` lazy-loaded module in `main.js` (Phase 3j)
8+
- Added DOMPurify attribute allowlist entries: `data-draw-index`, `data-draw-tool`, `data-tool`, `data-skill`, `spellcheck`, `rows`
9+
10+
---
11+
12+
## Summary
13+
Integrates the Draw DocGen component (`{{Draw:}}` tag) into the rendering pipeline, toolbar, and module loader — completing the Excalidraw + Mermaid whiteboard feature end-to-end.
14+
15+
---
16+
17+
## 1. CSP Update
18+
**Files:** `index.html`
19+
**What:** Added `https://excalidraw.com` to the `frame-src` CSP directive so Excalidraw iframes load without being blocked.
20+
**Impact:** Excalidraw whiteboard renders inside `{{Draw:}}` cards without CSP violations.
21+
22+
## 2. Draw Toolbar Button
23+
**Files:** `index.html`
24+
**What:** Added a new "Draw" toolbar group with a 🎨 Draw button (`data-action="draw-tag"`) between Git and Coding groups, with purple-accented label.
25+
**Impact:** Users can insert `{{Draw:}}` tags from the formatting toolbar with one click.
26+
27+
## 3. Renderer Pipeline Integration
28+
**Files:** `js/renderer.js`
29+
**What:** Chained `transformDrawMarkdown` after `transformGitMarkdown` in the rendering pipeline; added `bindDrawPreviewActions` call after preview render; expanded DOMPurify `ADD_ATTR` with draw-specific data attributes (`data-draw-index`, `data-draw-tool`, `data-tool`, `data-skill`, `spellcheck`, `rows`).
30+
**Impact:** `{{Draw:}}` tags are parsed into interactive preview cards and their actions (open Excalidraw, render Mermaid) are bound after render.
31+
32+
## 4. Draw DocGen Stylesheet
33+
**Files:** `css/draw-docgen.css` [NEW]
34+
**What:** 309-line standalone stylesheet with card container, header, buttons, Excalidraw iframe preview, tool pills (Excalidraw/Mermaid toggle), inline Mermaid editor with live preview, fullscreen support, and complete dark mode coverage.
35+
**Impact:** Draw cards have polished purple-accented UI matching the existing DocGen card design language.
36+
37+
## 5. Module Import
38+
**Files:** `src/main.js`
39+
**What:** Added `import '../css/draw-docgen.css'` for styling and `await import('../js/draw-docgen.js')` as Phase 3j in the lazy module loader.
40+
**Impact:** Draw module loads after Git module, keeping it standalone and removable.
41+
42+
---
43+
44+
## Files Changed (4 total)
45+
46+
| File | Lines Changed | Type |
47+
|------|:---:|------|
48+
| `index.html` | +7 −1 | CSP `frame-src` + toolbar Draw button |
49+
| `js/renderer.js` | +8 −3 | Draw pipeline + DOMPurify attrs |
50+
| `src/main.js` | +4 | CSS import + JS module import |
51+
| `css/draw-docgen.css` | +309 | New standalone stylesheet |

css/draw-docgen.css

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
/* ============================================
2+
draw-docgen.css — {{Draw:}} Tag Styles
3+
Excalidraw whiteboard integration
4+
Standalone — remove this file to disable
5+
============================================ */
6+
7+
/* Card container */
8+
.draw-docgen-card {
9+
border: 2px solid #6965db;
10+
border-radius: 12px;
11+
background: linear-gradient(135deg, rgba(105, 101, 219, 0.08) 0%, rgba(99, 102, 241, 0.05) 100%);
12+
margin: 12px 0;
13+
overflow: hidden;
14+
transition: border-color 0.2s, box-shadow 0.2s;
15+
}
16+
17+
.draw-docgen-card:hover {
18+
border-color: #7c78e8;
19+
box-shadow: 0 4px 20px rgba(105, 101, 219, 0.15);
20+
}
21+
22+
/* Header */
23+
.draw-docgen-header {
24+
display: flex;
25+
align-items: center;
26+
gap: 8px;
27+
padding: 10px 14px;
28+
background: linear-gradient(135deg, rgba(105, 101, 219, 0.12) 0%, rgba(99, 102, 241, 0.08) 100%);
29+
border-bottom: 1px solid rgba(105, 101, 219, 0.2);
30+
flex-wrap: wrap;
31+
}
32+
33+
.draw-docgen-icon {
34+
font-size: 1.2em;
35+
}
36+
37+
.draw-docgen-label {
38+
font-weight: 600;
39+
font-size: 0.85rem;
40+
color: #6965db;
41+
}
42+
43+
.draw-docgen-name {
44+
font-size: 0.7rem;
45+
padding: 2px 8px;
46+
border-radius: 10px;
47+
background: rgba(105, 101, 219, 0.15);
48+
color: #6965db;
49+
font-weight: 600;
50+
letter-spacing: 0.3px;
51+
max-width: 200px;
52+
overflow: hidden;
53+
text-overflow: ellipsis;
54+
white-space: nowrap;
55+
}
56+
57+
.draw-docgen-actions {
58+
display: flex;
59+
align-items: center;
60+
gap: 6px;
61+
margin-left: auto;
62+
flex-wrap: wrap;
63+
}
64+
65+
/* Buttons */
66+
.draw-docgen-btn {
67+
border: none;
68+
background: rgba(105, 101, 219, 0.1);
69+
color: #6965db;
70+
padding: 4px 10px;
71+
border-radius: 6px;
72+
font-size: 0.78rem;
73+
cursor: pointer;
74+
transition: all 0.15s;
75+
white-space: nowrap;
76+
font-weight: 500;
77+
}
78+
79+
.draw-docgen-btn:hover {
80+
background: rgba(105, 101, 219, 0.2);
81+
color: #5753c9;
82+
}
83+
84+
.draw-docgen-open {
85+
background: #6965db;
86+
color: #fff;
87+
}
88+
89+
.draw-docgen-open:hover {
90+
background: #5753c9;
91+
color: #fff;
92+
}
93+
94+
/* Excalidraw iframe preview */
95+
.draw-docgen-preview {
96+
padding: 0;
97+
position: relative;
98+
background: #fff;
99+
border-radius: 0 0 10px 10px;
100+
overflow: hidden;
101+
}
102+
103+
.draw-docgen-iframe {
104+
width: 100%;
105+
height: 500px;
106+
border: none;
107+
display: block;
108+
background: #fff;
109+
}
110+
111+
/* Fullscreen iframe */
112+
.draw-docgen-iframe:fullscreen {
113+
width: 100vw;
114+
height: 100vh;
115+
}
116+
117+
/* Info hint */
118+
.draw-docgen-info {
119+
padding: 8px 14px;
120+
font-size: 0.78rem;
121+
color: #6b7280;
122+
}
123+
124+
/* Status indicator */
125+
.draw-docgen-status {
126+
font-size: 0.72rem;
127+
padding: 2px 8px;
128+
border-radius: 10px;
129+
background: rgba(16, 185, 129, 0.15);
130+
color: #059669;
131+
font-weight: 500;
132+
}
133+
134+
/* Toolbar button */
135+
.fmt-draw-btn {
136+
color: #6965db !important;
137+
font-weight: 600;
138+
}
139+
140+
/* ─── Dark Mode ─── */
141+
142+
[data-theme="dark"] .draw-docgen-card,
143+
.dark-mode .draw-docgen-card {
144+
border-color: #5753c9;
145+
background: linear-gradient(135deg, rgba(105, 101, 219, 0.12) 0%, rgba(99, 102, 241, 0.08) 100%);
146+
}
147+
148+
[data-theme="dark"] .draw-docgen-header,
149+
.dark-mode .draw-docgen-header {
150+
background: linear-gradient(135deg, rgba(105, 101, 219, 0.18) 0%, rgba(99, 102, 241, 0.1) 100%);
151+
border-bottom-color: rgba(105, 101, 219, 0.3);
152+
}
153+
154+
[data-theme="dark"] .draw-docgen-label,
155+
.dark-mode .draw-docgen-label {
156+
color: #a5a2f1;
157+
}
158+
159+
[data-theme="dark"] .draw-docgen-name,
160+
.dark-mode .draw-docgen-name {
161+
background: rgba(165, 162, 241, 0.2);
162+
color: #a5a2f1;
163+
}
164+
165+
[data-theme="dark"] .draw-docgen-btn,
166+
.dark-mode .draw-docgen-btn {
167+
background: rgba(165, 162, 241, 0.15);
168+
color: #a5a2f1;
169+
}
170+
171+
[data-theme="dark"] .draw-docgen-preview,
172+
.dark-mode .draw-docgen-preview {
173+
background: #121212;
174+
}
175+
176+
[data-theme="dark"] .draw-docgen-info,
177+
.dark-mode .draw-docgen-info {
178+
color: #9ca3af;
179+
}
180+
181+
[data-theme="dark"] .draw-docgen-status,
182+
.dark-mode .draw-docgen-status {
183+
background: rgba(52, 211, 153, 0.2);
184+
color: #34d399;
185+
}
186+
187+
/* ─── Tool Pills (Excalidraw / Mermaid) ─── */
188+
189+
.draw-tool-pills {
190+
display: flex;
191+
gap: 4px;
192+
padding: 8px 14px;
193+
background: rgba(105, 101, 219, 0.04);
194+
border-bottom: 1px solid rgba(105, 101, 219, 0.1);
195+
}
196+
197+
.draw-tool-pill {
198+
border: 1px solid rgba(105, 101, 219, 0.2);
199+
background: transparent;
200+
color: #6b7280;
201+
padding: 4px 12px;
202+
border-radius: 16px;
203+
font-size: 0.72rem;
204+
cursor: pointer;
205+
transition: all 0.15s;
206+
font-weight: 500;
207+
}
208+
209+
.draw-tool-pill:hover {
210+
border-color: #6965db;
211+
color: #6965db;
212+
}
213+
214+
.draw-tool-pill.active {
215+
background: #6965db;
216+
color: #fff;
217+
border-color: #6965db;
218+
}
219+
220+
/* ─── Mermaid Editor ─── */
221+
222+
.draw-mermaid-editor {
223+
padding: 10px 14px;
224+
border-top: 1px solid rgba(105, 101, 219, 0.08);
225+
}
226+
227+
.draw-mermaid-input {
228+
width: 100%;
229+
border: 1px solid rgba(105, 101, 219, 0.2);
230+
border-radius: 8px;
231+
padding: 10px 12px;
232+
font-size: 0.82rem;
233+
font-family: 'SF Mono', 'Menlo', 'Consolas', monospace;
234+
line-height: 1.5;
235+
resize: vertical;
236+
background: rgba(255, 255, 255, 0.6);
237+
color: inherit;
238+
transition: border-color 0.2s;
239+
min-height: 80px;
240+
tab-size: 2;
241+
}
242+
243+
.draw-mermaid-input:focus {
244+
outline: none;
245+
border-color: #6965db;
246+
box-shadow: 0 0 0 3px rgba(105, 101, 219, 0.1);
247+
}
248+
249+
.draw-mermaid-actions {
250+
display: flex;
251+
gap: 6px;
252+
margin-top: 8px;
253+
}
254+
255+
.draw-mermaid-preview {
256+
margin-top: 10px;
257+
border-radius: 8px;
258+
overflow: hidden;
259+
}
260+
261+
.draw-mermaid-preview .mermaid {
262+
display: flex;
263+
justify-content: center;
264+
padding: 12px;
265+
background: rgba(105, 101, 219, 0.03);
266+
border: 1px solid rgba(105, 101, 219, 0.1);
267+
border-radius: 8px;
268+
}
269+
270+
.draw-mermaid-preview .mermaid svg {
271+
max-width: 100%;
272+
height: auto;
273+
}
274+
275+
/* ─── Dark Mode: Tool Pills ─── */
276+
277+
[data-theme="dark"] .draw-tool-pills,
278+
.dark-mode .draw-tool-pills {
279+
background: rgba(105, 101, 219, 0.06);
280+
border-bottom-color: rgba(105, 101, 219, 0.15);
281+
}
282+
283+
[data-theme="dark"] .draw-tool-pill,
284+
.dark-mode .draw-tool-pill {
285+
border-color: rgba(165, 162, 241, 0.3);
286+
color: #9ca3af;
287+
}
288+
289+
[data-theme="dark"] .draw-tool-pill.active,
290+
.dark-mode .draw-tool-pill.active {
291+
background: #6965db;
292+
color: #fff;
293+
border-color: #6965db;
294+
}
295+
296+
/* ─── Dark Mode: Mermaid Editor ─── */
297+
298+
[data-theme="dark"] .draw-mermaid-input,
299+
.dark-mode .draw-mermaid-input {
300+
background: rgba(0, 0, 0, 0.3);
301+
border-color: rgba(165, 162, 241, 0.3);
302+
}
303+
304+
[data-theme="dark"] .draw-mermaid-preview .mermaid,
305+
.dark-mode .draw-mermaid-preview .mermaid {
306+
background: rgba(105, 101, 219, 0.06);
307+
border-color: rgba(105, 101, 219, 0.15);
308+
}

index.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
media-src 'self' blob: https: http:;
2929
worker-src 'self' blob:;
3030
child-src 'self' blob:;
31-
frame-src 'self' blob: https://webvm.io https://s.tradingview.com https://www.youtube-nocookie.com https://www.youtube.com https://player.vimeo.com;
31+
frame-src 'self' blob: https://webvm.io https://s.tradingview.com https://www.youtube-nocookie.com https://www.youtube.com https://player.vimeo.com https://excalidraw.com;
3232
object-src 'none';
3333
base-uri 'self';
3434
">
@@ -692,6 +692,12 @@ <h5>Menu</h5>
692692
Git</button>
693693
</div>
694694
<span class="fmt-separator"></span>
695+
<div class="fmt-linux-group" title="Whiteboard & Diagrams">
696+
<span class="fmt-api-group-label" style="color:#6965db">Draw</span>
697+
<button class="fmt-btn fmt-draw-btn" data-action="draw-tag" title="Insert Excalidraw / Mermaid whiteboard">🎨
698+
Draw</button>
699+
</div>
700+
<span class="fmt-separator"></span>
695701
<div class="fmt-coding-group" title="Executable Code Blocks">
696702
<span class="fmt-coding-group-label">Coding</span>
697703
<button class="fmt-btn fmt-coding-btn fmt-coding-bash" data-action="coding-bash"

0 commit comments

Comments
 (0)