@autumnsgrove/groveengine 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,41 +1,11 @@
1
1
  <script>
2
2
  import { marked } from "marked";
3
- import mermaid from "mermaid";
4
3
  import { onMount, tick } from "svelte";
5
4
  import { sanitizeMarkdown } from "../../utils/sanitize.js";
6
5
  import "../../styles/content.css";
7
6
  import { Button, Input } from '@groveengine/ui';
8
7
  import Dialog from "../ui/Dialog.svelte";
9
8
 
10
- // Initialize mermaid with grove-themed dark config
11
- mermaid.initialize({
12
- startOnLoad: false,
13
- theme: "dark",
14
- themeVariables: {
15
- primaryColor: "#2d5a2d",
16
- primaryTextColor: "#d4d4d4",
17
- primaryBorderColor: "#4a7c4a",
18
- lineColor: "#8bc48b",
19
- secondaryColor: "#1e3a1e",
20
- tertiaryColor: "#2a2a2a",
21
- background: "#1e1e1e",
22
- mainBkg: "#252526",
23
- secondBkg: "#1e1e1e",
24
- nodeBorder: "#4a7c4a",
25
- clusterBkg: "#1a2a1a",
26
- titleColor: "#8bc48b",
27
- edgeLabelBackground: "#252526",
28
- },
29
- flowchart: {
30
- curve: "basis",
31
- padding: 15,
32
- },
33
- sequence: {
34
- actorMargin: 50,
35
- boxMargin: 10,
36
- },
37
- });
38
-
39
9
  // Props
40
10
  let {
41
11
  content = $bindable(""),
@@ -303,43 +273,9 @@
303
273
  );
304
274
  let charCount = $derived(content.length);
305
275
  let lineCount = $derived(content.split("\n").length);
306
- // Custom marked renderer for mermaid blocks
307
- const renderer = new marked.Renderer();
308
- const originalCodeRenderer = renderer.code.bind(renderer);
309
-
310
- renderer.code = function ({ text, lang }) {
311
- if (lang === "mermaid") {
312
- // Wrap mermaid code in a special container for rendering
313
- const id = `mermaid-${Math.random().toString(36).substr(2, 9)}`;
314
- return `<div class="mermaid-container"><pre class="mermaid" id="${id}">${text}</pre></div>`;
315
- }
316
- return originalCodeRenderer({ text, lang });
317
- };
318
-
319
- marked.use({ renderer });
320
276
 
321
277
  let previewHtml = $derived(content ? sanitizeMarkdown(marked.parse(content)) : "");
322
278
 
323
- // Render mermaid diagrams after preview updates
324
- async function renderMermaidDiagrams() {
325
- await tick();
326
- const mermaidElements = document.querySelectorAll(".preview-content .mermaid, .full-preview-scroll .mermaid");
327
- if (mermaidElements.length > 0) {
328
- try {
329
- await mermaid.run({ nodes: mermaidElements });
330
- } catch (e) {
331
- console.warn("Mermaid rendering error:", e);
332
- }
333
- }
334
- }
335
-
336
- // Trigger mermaid rendering when preview HTML changes
337
- $effect(() => {
338
- if (previewHtml && (showPreview || showFullPreview)) {
339
- renderMermaidDiagrams();
340
- }
341
- });
342
-
343
279
  // Reading time estimate (average 200 words per minute)
344
280
  let readingTime = $derived(() => {
345
281
  const minutes = Math.ceil(wordCount / 200);
@@ -533,7 +469,6 @@
533
469
  { id: "heading2", label: "Heading 2", insert: "## " },
534
470
  { id: "heading3", label: "Heading 3", insert: "### " },
535
471
  { id: "code", label: "Code Block", insert: "```\n\n```", cursorOffset: 4 },
536
- { id: "mermaid", label: "Mermaid Diagram", insert: "```mermaid\nflowchart TD\n A[Start] --> B[End]\n```", cursorOffset: 32 },
537
472
  { id: "quote", label: "Quote", insert: "> " },
538
473
  { id: "list", label: "Bullet List", insert: "- " },
539
474
  { id: "numbered", label: "Numbered List", insert: "1. " },
@@ -2619,30 +2554,6 @@
2619
2554
  color: #6a6a6a;
2620
2555
  font-family: "JetBrains Mono", monospace;
2621
2556
  }
2622
- /* Mermaid Diagram Styles */
2623
- :global(.mermaid-container) {
2624
- margin: 1.5rem 0;
2625
- padding: 1rem;
2626
- background: var(--light-bg-primary);
2627
- border: 1px solid var(--light-border-primary);
2628
- border-radius: 8px;
2629
- overflow-x: auto;
2630
- }
2631
- :global(.mermaid) {
2632
- display: flex;
2633
- justify-content: center;
2634
- }
2635
- :global(.mermaid svg) {
2636
- max-width: 100%;
2637
- height: auto;
2638
- }
2639
- /* Mermaid error styling */
2640
- :global(.mermaid-container .error) {
2641
- color: #e07030;
2642
- padding: 0.5rem;
2643
- font-family: monospace;
2644
- font-size: 0.85rem;
2645
- }
2646
2557
  /* Mode Transitions */
2647
2558
  .editor-container {
2648
2559
  transition: border-color 0.3s ease, box-shadow 0.3s ease;
@@ -406,16 +406,42 @@
406
406
  DOMPurify
407
407
  ? DOMPurify.sanitize(processedContent, {
408
408
  ALLOWED_TAGS: [
409
+ // Headings
409
410
  'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
410
- 'p', 'a', 'ul', 'ol', 'li', 'blockquote',
411
- 'code', 'pre', 'strong', 'em', 'img',
412
- 'table', 'thead', 'tbody', 'tr', 'th', 'td',
413
- 'br', 'hr', 'div', 'span', 'sup', 'sub',
414
- 'del', 'ins'
411
+ // Block elements
412
+ 'p', 'blockquote', 'pre', 'hr', 'br', 'div',
413
+ // Lists
414
+ 'ul', 'ol', 'li', 'dl', 'dt', 'dd',
415
+ // Inline elements
416
+ 'a', 'span', 'code', 'strong', 'em', 'b', 'i', 'u',
417
+ 'sup', 'sub', 'del', 'ins', 'mark', 'small', 'abbr',
418
+ 'kbd', 'samp', 'var', 'q', 's',
419
+ // Tables
420
+ 'table', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td', 'caption',
421
+ // Media
422
+ 'img', 'figure', 'figcaption', 'picture', 'source',
423
+ // Forms (for task lists)
424
+ 'input', 'label',
425
+ // Code block copy buttons
426
+ 'button', 'svg', 'path', 'rect', 'g', 'line', 'circle', 'polyline'
415
427
  ],
416
428
  ALLOWED_ATTR: [
417
- 'href', 'src', 'alt', 'title', 'class', 'id',
418
- 'data-anchor', 'data-language', 'data-line-numbers'
429
+ // Links and media
430
+ 'href', 'src', 'alt', 'title', 'target', 'rel',
431
+ // Styling and identification
432
+ 'class', 'id', 'style',
433
+ // Data attributes for custom functionality
434
+ 'data-anchor', 'data-language', 'data-line-numbers', 'data-code',
435
+ // Accessibility
436
+ 'aria-label', 'aria-hidden', 'role',
437
+ // Form elements (for task lists)
438
+ 'type', 'checked', 'disabled',
439
+ // SVG attributes
440
+ 'viewBox', 'fill', 'stroke', 'stroke-width', 'stroke-linecap',
441
+ 'stroke-linejoin', 'd', 'width', 'height', 'x', 'y', 'x1', 'y1',
442
+ 'x2', 'y2', 'r', 'cx', 'cy', 'points', 'xmlns',
443
+ // Tables
444
+ 'colspan', 'rowspan', 'scope'
419
445
  ],
420
446
  ALLOW_DATA_ATTR: true
421
447
  })
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { Toaster } from "sonner";
2
+ import { Toaster } from "svelte-sonner";
3
3
 
4
4
  interface Props {
5
5
  position?:
@@ -1,4 +1,4 @@
1
- import { toast as sonnerToast } from "sonner";
1
+ import { toast as sonnerToast } from "svelte-sonner";
2
2
  /**
3
3
  * Toast notification utility for displaying temporary messages
4
4
  * Wraps sonner toast with consistent defaults and API
@@ -11,17 +11,6 @@ export function extractHeaders(markdown: string): any[];
11
11
  * @returns {string} HTML with anchor markers converted to spans
12
12
  */
13
13
  export function processAnchorTags(html: string): string;
14
- /**
15
- * Process Mermaid diagrams in markdown content
16
- * @param {string} markdown - The markdown content
17
- * @returns {string} Processed markdown with Mermaid diagrams
18
- */
19
- export function processMermaidDiagrams(markdown: string): string;
20
- /**
21
- * Render Mermaid diagrams in the DOM
22
- * This should be called after the content is mounted
23
- */
24
- export function renderMermaidDiagrams(): Promise<void>;
25
14
  /**
26
15
  * Parse markdown content and convert to HTML
27
16
  * @param {string} markdownContent - The raw markdown content (may include frontmatter)
@@ -1,14 +1,6 @@
1
1
  import { marked } from "marked";
2
2
  import matter from "gray-matter";
3
- import mermaid from "mermaid";
4
- import { sanitizeSVG, sanitizeMarkdown } from './sanitize.js';
5
-
6
- // Configure Mermaid
7
- mermaid.initialize({
8
- startOnLoad: false,
9
- theme: "default",
10
- securityLevel: "strict",
11
- });
3
+ import { sanitizeMarkdown } from './sanitize.js';
12
4
 
13
5
  // Configure marked renderer for GitHub-style code blocks
14
6
  const renderer = new marked.Renderer();
@@ -142,42 +134,6 @@ export function processAnchorTags(html) {
142
134
  );
143
135
  }
144
136
 
145
- /**
146
- * Process Mermaid diagrams in markdown content
147
- * @param {string} markdown - The markdown content
148
- * @returns {string} Processed markdown with Mermaid diagrams
149
- */
150
- export function processMermaidDiagrams(markdown) {
151
- // Replace Mermaid code blocks with special divs that will be processed later
152
- return markdown.replace(
153
- /```mermaid\n([\s\S]*?)```/g,
154
- (match, diagramCode) => {
155
- const diagramId = "mermaid-" + Math.random().toString(36).substr(2, 9);
156
- return `<div class="mermaid-container" id="${diagramId}" data-diagram="${encodeURIComponent(diagramCode.trim())}"></div>`;
157
- },
158
- );
159
- }
160
-
161
- /**
162
- * Render Mermaid diagrams in the DOM
163
- * This should be called after the content is mounted
164
- */
165
- export async function renderMermaidDiagrams() {
166
- const containers = document.querySelectorAll(".mermaid-container");
167
-
168
- for (const container of containers) {
169
- try {
170
- const diagramCode = decodeURIComponent(container.dataset.diagram);
171
- const { svg } = await mermaid.render(container.id, diagramCode);
172
- // Sanitize SVG output before injecting into DOM to prevent XSS
173
- container.innerHTML = sanitizeSVG(svg);
174
- } catch (error) {
175
- console.error("Error rendering Mermaid diagram:", error);
176
- container.innerHTML = '<p class="error">Error rendering diagram</p>';
177
- }
178
- }
179
- }
180
-
181
137
  /**
182
138
  * Parse markdown content and convert to HTML
183
139
  * @param {string} markdownContent - The raw markdown content (may include frontmatter)
@@ -186,9 +142,7 @@ export async function renderMermaidDiagrams() {
186
142
  export function parseMarkdownContent(markdownContent) {
187
143
  const { data, content: markdown } = matter(markdownContent);
188
144
 
189
- // Process Mermaid diagrams in the content
190
- const processedContent = processMermaidDiagrams(markdown);
191
- let htmlContent = marked.parse(processedContent);
145
+ let htmlContent = marked.parse(markdown);
192
146
 
193
147
  // Process anchor tags in the HTML content
194
148
  htmlContent = processAnchorTags(htmlContent);
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * Centralized sanitization utilities for XSS prevention
3
- * Uses isomorphic-dompurify for both server-side and client-side sanitization
3
+ * Uses dompurify for client-side sanitization
4
+ * Note: For Cloudflare Workers, DOM APIs are available so we use client-side dompurify
4
5
  */
5
6
 
6
- import DOMPurify from 'isomorphic-dompurify';
7
+ import DOMPurify from 'dompurify';
7
8
 
8
9
  /**
9
10
  * Sanitize HTML content to prevent XSS attacks
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autumnsgrove/groveengine",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Multi-tenant blog engine for Grove Platform. Features gutter annotations, markdown editing, magic code auth, and Cloudflare Workers deployment.",
5
5
  "author": "AutumnsGrove",
6
6
  "license": "MIT",
@@ -86,8 +86,8 @@
86
86
  "test:watch": "vitest watch"
87
87
  },
88
88
  "peerDependencies": {
89
- "svelte": "^5.0.0",
90
89
  "@sveltejs/kit": "^2.0.0",
90
+ "svelte": "^5.0.0",
91
91
  "tailwindcss": "^3.4.0"
92
92
  },
93
93
  "devDependencies": {
@@ -114,17 +114,15 @@
114
114
  "vitest": "^4.0.14"
115
115
  },
116
116
  "dependencies": {
117
- "@groveengine/ui": "^0.3.0",
117
+ "@groveengine/ui": "workspace:*",
118
118
  "@types/dompurify": "^3.0.5",
119
119
  "chart.js": "^4.5.1",
120
120
  "clsx": "^2.1.1",
121
121
  "dompurify": "^3.3.0",
122
122
  "gray-matter": "^4.0.3",
123
- "isomorphic-dompurify": "^2.33.0",
124
123
  "lucide-svelte": "^0.554.0",
125
124
  "marked": "^17.0.1",
126
- "mermaid": "^11.12.1",
127
- "sonner": "^2.0.7",
125
+ "svelte-sonner": "^1.0.7",
128
126
  "tailwind-merge": "^3.4.0"
129
127
  }
130
128
  }