feat(phase2): localization EN/RU + additional widget types
- Add svelte-i18n with 224 translation keys (English + Russian) - Language switcher in header (EN/RU toggle, persists to localStorage) - Extract all hardcoded strings from 37 component/page files - Add 4 new widget types: Bookmark, Note (markdown), Embed (iframe), Status - WidgetRenderer dispatches by type, WidgetGrid supports full-width widgets - Type-specific config forms in board editor - Install marked for markdown rendering
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
<script lang="ts">
|
||||
import { marked } from 'marked';
|
||||
|
||||
interface NoteConfig {
|
||||
content: string;
|
||||
format: 'markdown' | 'text';
|
||||
}
|
||||
|
||||
interface Props {
|
||||
config: NoteConfig;
|
||||
}
|
||||
|
||||
let { config }: Props = $props();
|
||||
|
||||
// Configure marked for security
|
||||
marked.setOptions({
|
||||
breaks: true,
|
||||
gfm: true
|
||||
});
|
||||
|
||||
const renderedContent = $derived.by(() => {
|
||||
if (config.format === 'text') {
|
||||
return config.content
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/\n/g, '<br>');
|
||||
}
|
||||
// Sanitize by stripping script tags and event handlers from markdown output
|
||||
const raw = marked.parse(config.content, { async: false }) as string;
|
||||
return raw
|
||||
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
|
||||
.replace(/\s*on\w+\s*=\s*"[^"]*"/gi, '')
|
||||
.replace(/\s*on\w+\s*=\s*'[^']*'/gi, '');
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex h-full flex-col rounded-xl border border-border bg-card p-4">
|
||||
<div class="prose prose-sm prose-invert max-w-none flex-1 overflow-auto text-foreground">
|
||||
{@html renderedContent}
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user