@svadmin/ui 0.40.0 → 0.40.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,19 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
/* eslint-disable svelte/no-at-html-tags */
|
|
3
|
-
import
|
|
4
|
-
import * as markedHighlightPkg from "marked-highlight";
|
|
5
|
-
import * as hljsPkg from "highlight.js";
|
|
6
|
-
import * as DOMPurifyPkg from "isomorphic-dompurify";
|
|
7
|
-
|
|
8
|
-
const Marked = markedPkg.Marked;
|
|
9
|
-
const markedHighlight = markedHighlightPkg.markedHighlight;
|
|
10
|
-
const hljsModule = hljsPkg.default || hljsPkg;
|
|
11
|
-
const DOMPurify = 'default' in DOMPurifyPkg ? DOMPurifyPkg.default : DOMPurifyPkg;
|
|
12
|
-
|
|
13
|
-
// Only safely import css if hljs exists
|
|
14
|
-
if (hljsModule && Object.keys(hljsModule).length > 0) {
|
|
15
|
-
import("highlight.js/styles/github-dark.css").catch(() => {});
|
|
16
|
-
}
|
|
3
|
+
import { onMount } from "svelte";
|
|
17
4
|
|
|
18
5
|
interface Props {
|
|
19
6
|
/** The raw markdown string to render */
|
|
@@ -26,25 +13,81 @@
|
|
|
26
13
|
|
|
27
14
|
let { content, streaming = false, class: className = "" }: Props = $props();
|
|
28
15
|
|
|
29
|
-
|
|
16
|
+
type MarkedConstructor = typeof import("marked").Marked;
|
|
17
|
+
type MarkedHighlight = typeof import("marked-highlight").markedHighlight;
|
|
18
|
+
type HighlightApi = typeof import("highlight.js").default;
|
|
19
|
+
type Sanitizer = typeof import("isomorphic-dompurify").default;
|
|
20
|
+
|
|
21
|
+
// Lazily loaded optional peer dependencies (marked, marked-highlight,
|
|
22
|
+
// highlight.js, isomorphic-dompurify). They are declared as optional peer
|
|
23
|
+
// deps; statically importing them would crash consumers that have not
|
|
24
|
+
// installed them, even when MarkdownRenderer is never rendered. Load them
|
|
25
|
+
// dynamically so the module graph resolves without them and the component
|
|
26
|
+
// degrades to escaped-text rendering when they are absent.
|
|
27
|
+
let MarkedCtor = $state<MarkedConstructor | null>(null);
|
|
28
|
+
let markedHighlightFn = $state<MarkedHighlight | null>(null);
|
|
29
|
+
let hljs = $state<HighlightApi | null>(null);
|
|
30
|
+
let DOMPurify = $state<Sanitizer | null>(null);
|
|
31
|
+
|
|
32
|
+
onMount(() => {
|
|
33
|
+
let cancelled = false;
|
|
34
|
+
(async () => {
|
|
35
|
+
try {
|
|
36
|
+
const [markedPkg, markedHighlightPkg, hljsPkg, DOMPurifyPkg] = await Promise.all([
|
|
37
|
+
import("marked"),
|
|
38
|
+
import("marked-highlight"),
|
|
39
|
+
import("highlight.js"),
|
|
40
|
+
import("isomorphic-dompurify"),
|
|
41
|
+
]);
|
|
42
|
+
if (cancelled) return;
|
|
43
|
+
MarkedCtor = markedPkg.Marked;
|
|
44
|
+
markedHighlightFn = markedHighlightPkg.markedHighlight;
|
|
45
|
+
hljs = hljsPkg.default;
|
|
46
|
+
DOMPurify = DOMPurifyPkg.default;
|
|
47
|
+
// Only import the theme css when highlight.js is available
|
|
48
|
+
if (hljs && Object.keys(hljs).length > 0) {
|
|
49
|
+
import("highlight.js/styles/github-dark.css").catch(() => {});
|
|
50
|
+
}
|
|
51
|
+
} catch {
|
|
52
|
+
// Optional deps not installed; fall back to escaped-text rendering.
|
|
53
|
+
if (cancelled) return;
|
|
54
|
+
}
|
|
55
|
+
})();
|
|
56
|
+
return () => { cancelled = true; };
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const hasMarkdownDeps = $derived(
|
|
60
|
+
typeof MarkedCtor === "function" &&
|
|
61
|
+
typeof DOMPurify?.sanitize === "function" &&
|
|
62
|
+
typeof markedHighlightFn === "function",
|
|
63
|
+
);
|
|
30
64
|
|
|
31
65
|
// Configure marked with syntax highlighting if available
|
|
32
|
-
const markedObj =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
66
|
+
const markedObj = $derived.by(() => {
|
|
67
|
+
const Constructor = MarkedCtor;
|
|
68
|
+
const highlightExtension = markedHighlightFn;
|
|
69
|
+
if (!Constructor || !highlightExtension) return null;
|
|
70
|
+
|
|
71
|
+
return new Constructor(
|
|
72
|
+
highlightExtension({
|
|
73
|
+
langPrefix: "hljs language-",
|
|
74
|
+
highlight(code: string, lang: string) {
|
|
75
|
+
const language = hljs?.getLanguage && hljs.getLanguage(lang) ? lang : "plaintext";
|
|
76
|
+
return hljs?.highlight ? hljs.highlight(code, { language }).value : code;
|
|
77
|
+
},
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
});
|
|
41
81
|
|
|
42
82
|
// Render HTML safely
|
|
43
|
-
const html = $derived(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
83
|
+
const html = $derived.by(() => {
|
|
84
|
+
const purifier = DOMPurify;
|
|
85
|
+
const parser = markedObj;
|
|
86
|
+
if (hasMarkdownDeps && purifier && parser) {
|
|
87
|
+
return purifier.sanitize(parser.parse(content || ""));
|
|
88
|
+
}
|
|
89
|
+
return `<div style="white-space: pre-wrap">${String(content || "").replace(/</g, "<").replace(/>/g, ">")}</div>`;
|
|
90
|
+
});
|
|
48
91
|
|
|
49
92
|
// Handle copy code blocks
|
|
50
93
|
let copiedBlock = $state<string | null>(null);
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-lg border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 active:translate-y-px aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4 group/button inline-flex shrink-0 items-center justify-center whitespace-nowrap transition-all outline-none select-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
8
8
|
variants: {
|
|
9
9
|
variant: {
|
|
10
|
-
default: "bg-primary text-primary-foreground
|
|
10
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/80",
|
|
11
11
|
outline: "border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground",
|
|
12
12
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
13
13
|
ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
|
|
@@ -3,7 +3,7 @@ import type { HTMLAnchorAttributes, HTMLButtonAttributes } from "svelte/elements
|
|
|
3
3
|
import { type VariantProps } from "tailwind-variants";
|
|
4
4
|
export declare const buttonVariants: import("tailwind-variants").TVReturnType<{
|
|
5
5
|
variant: {
|
|
6
|
-
default: "bg-primary text-primary-foreground
|
|
6
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/80";
|
|
7
7
|
outline: "border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground";
|
|
8
8
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground";
|
|
9
9
|
ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground";
|
|
@@ -22,7 +22,7 @@ export declare const buttonVariants: import("tailwind-variants").TVReturnType<{
|
|
|
22
22
|
};
|
|
23
23
|
}, undefined, "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-lg border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 active:translate-y-px aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4 group/button inline-flex shrink-0 items-center justify-center whitespace-nowrap transition-all outline-none select-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0", {
|
|
24
24
|
variant: {
|
|
25
|
-
default: "bg-primary text-primary-foreground
|
|
25
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/80";
|
|
26
26
|
outline: "border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground";
|
|
27
27
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground";
|
|
28
28
|
ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground";
|
|
@@ -41,7 +41,7 @@ export declare const buttonVariants: import("tailwind-variants").TVReturnType<{
|
|
|
41
41
|
};
|
|
42
42
|
}, undefined, import("tailwind-variants").TVReturnTypeLike<{
|
|
43
43
|
variant: {
|
|
44
|
-
default: "bg-primary text-primary-foreground
|
|
44
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/80";
|
|
45
45
|
outline: "border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground";
|
|
46
46
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground";
|
|
47
47
|
ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svadmin/ui",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.2",
|
|
4
4
|
"description": "Pre-built admin UI components — AdminApp, AutoTable, AutoForm, Sidebar, Layout",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"./components/*": "./dist/components/*"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@svadmin/core": "^0.34.
|
|
42
|
+
"@svadmin/core": "^0.34.1",
|
|
43
43
|
"@tanstack/svelte-query": "^6.1.38",
|
|
44
44
|
"highlight.js": "^11.11.1",
|
|
45
45
|
"isomorphic-dompurify": "^3.20.0",
|