@togo-framework/ui-markdown 0.1.0
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.
- package/LICENSE +5 -0
- package/README.md +17 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +352 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
package/LICENSE
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!-- togo-header -->
|
|
2
|
+
# @togo-framework/ui-markdown
|
|
3
|
+
|
|
4
|
+
Markdown renderer (GFM, syntax-highlighted code blocks, mermaid diagrams, table
|
|
5
|
+
export to image) plus a lightweight markdown editor. Part of the togo UI kit.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @togo-framework/ui-markdown
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { MarkdownRenderer, MarkdownEditor } from "@togo-framework/ui-markdown";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Requires `@togo-framework/ui-core` and `@togo-framework/ui-data-table` (tables
|
|
16
|
+
embedded in markdown render via the shared DataTable component).
|
|
17
|
+
<!-- togo-sponsors -->
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
interface MarkdownRendererProps {
|
|
4
|
+
content: string;
|
|
5
|
+
language?: "en" | "ar";
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
/** MarkdownTable — renders a GFM table via the kit's DataTable (sortable, searchable,
|
|
9
|
+
* CSV export). Exported so apps can reuse the markdown-table → DataTable mapping. */
|
|
10
|
+
declare function MarkdownTable({ node, language }: {
|
|
11
|
+
node: any;
|
|
12
|
+
language?: "en" | "ar";
|
|
13
|
+
}): React.JSX.Element;
|
|
14
|
+
/** CodeBlock — a fenced code block with a language label, Copy, and download-as-PNG.
|
|
15
|
+
* Exported so apps can render standalone code with the same chrome. */
|
|
16
|
+
declare function CodeBlock({ lang, children }: {
|
|
17
|
+
lang?: string;
|
|
18
|
+
children?: React.ReactNode;
|
|
19
|
+
}): React.JSX.Element;
|
|
20
|
+
/** MarkdownRenderer — GFM markdown with highlighted code, mermaid diagrams, and
|
|
21
|
+
* token-themed prose (headings/lists/tables/code/quote/links). RTL-aware. */
|
|
22
|
+
declare function MarkdownRenderer({ content, language, className }: MarkdownRendererProps): React.JSX.Element;
|
|
23
|
+
|
|
24
|
+
type MarkdownView = "write" | "preview" | "split";
|
|
25
|
+
interface MarkdownEditorProps {
|
|
26
|
+
value?: string;
|
|
27
|
+
defaultValue?: string;
|
|
28
|
+
onChange?: (value: string) => void;
|
|
29
|
+
view?: MarkdownView;
|
|
30
|
+
defaultView?: MarkdownView;
|
|
31
|
+
onViewChange?: (view: MarkdownView) => void;
|
|
32
|
+
language?: "en" | "ar";
|
|
33
|
+
placeholder?: string;
|
|
34
|
+
minRows?: number;
|
|
35
|
+
className?: string;
|
|
36
|
+
}
|
|
37
|
+
/** MarkdownEditor — toolbar (bold/italic/headings/lists/link/code/quote) + a textarea
|
|
38
|
+
* with a Write / Preview / Split view toggle (Split shows a live MarkdownRenderer).
|
|
39
|
+
* Dependency-light (no CodeMirror/Monaco). Controlled or uncontrolled. RTL + EN/AR. */
|
|
40
|
+
declare function MarkdownEditor({ value, defaultValue, onChange, view, defaultView, onViewChange, language, placeholder, minRows, className, }: MarkdownEditorProps): React.JSX.Element;
|
|
41
|
+
|
|
42
|
+
export { CodeBlock, MarkdownEditor, type MarkdownEditorProps, MarkdownRenderer, type MarkdownRendererProps, MarkdownTable, type MarkdownView };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
// src/components/markdown/MarkdownRenderer.tsx
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import ReactMarkdown from "react-markdown";
|
|
4
|
+
import remarkGfm from "remark-gfm";
|
|
5
|
+
import rehypeHighlight from "rehype-highlight";
|
|
6
|
+
import { Copy, Check, ImageDown } from "lucide-react";
|
|
7
|
+
import { cn } from "@togo-framework/ui-core";
|
|
8
|
+
import { DataTable } from "@togo-framework/ui-data-table";
|
|
9
|
+
import hljs from "highlight.js/lib/core";
|
|
10
|
+
import hlBash from "highlight.js/lib/languages/bash";
|
|
11
|
+
import hlGo from "highlight.js/lib/languages/go";
|
|
12
|
+
import hlTs from "highlight.js/lib/languages/typescript";
|
|
13
|
+
import hlJs from "highlight.js/lib/languages/javascript";
|
|
14
|
+
import hlJson from "highlight.js/lib/languages/json";
|
|
15
|
+
import hlYaml from "highlight.js/lib/languages/yaml";
|
|
16
|
+
import hlSql from "highlight.js/lib/languages/sql";
|
|
17
|
+
import hlGraphql from "highlight.js/lib/languages/graphql";
|
|
18
|
+
import hlPwsh from "highlight.js/lib/languages/powershell";
|
|
19
|
+
import hlDockerfile from "highlight.js/lib/languages/dockerfile";
|
|
20
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
21
|
+
var HL_LANGS = {
|
|
22
|
+
bash: hlBash,
|
|
23
|
+
go: hlGo,
|
|
24
|
+
typescript: hlTs,
|
|
25
|
+
javascript: hlJs,
|
|
26
|
+
json: hlJson,
|
|
27
|
+
yaml: hlYaml,
|
|
28
|
+
sql: hlSql,
|
|
29
|
+
graphql: hlGraphql,
|
|
30
|
+
powershell: hlPwsh,
|
|
31
|
+
dockerfile: hlDockerfile
|
|
32
|
+
};
|
|
33
|
+
for (const [name, def] of Object.entries(HL_LANGS)) {
|
|
34
|
+
try {
|
|
35
|
+
if (!hljs.getLanguage(name)) hljs.registerLanguage(name, def);
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
var HL_ALIAS = {
|
|
40
|
+
sh: "bash",
|
|
41
|
+
shell: "bash",
|
|
42
|
+
zsh: "bash",
|
|
43
|
+
console: "bash",
|
|
44
|
+
ts: "typescript",
|
|
45
|
+
tsx: "typescript",
|
|
46
|
+
js: "javascript",
|
|
47
|
+
jsx: "javascript",
|
|
48
|
+
mjs: "javascript",
|
|
49
|
+
yml: "yaml",
|
|
50
|
+
ps: "powershell",
|
|
51
|
+
ps1: "powershell",
|
|
52
|
+
gql: "graphql",
|
|
53
|
+
docker: "dockerfile"
|
|
54
|
+
};
|
|
55
|
+
function highlightToHtml(code, lang) {
|
|
56
|
+
const key = lang ? HL_ALIAS[lang] ?? lang : void 0;
|
|
57
|
+
try {
|
|
58
|
+
if (key && hljs.getLanguage(key)) return hljs.highlight(code, { language: key }).value;
|
|
59
|
+
} catch {
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
function hastText(node) {
|
|
64
|
+
if (!node) return "";
|
|
65
|
+
if (node.type === "text") return node.value ?? "";
|
|
66
|
+
if (Array.isArray(node.children)) return node.children.map(hastText).join("");
|
|
67
|
+
return "";
|
|
68
|
+
}
|
|
69
|
+
function parseTable(node) {
|
|
70
|
+
const kids = node?.children ?? [];
|
|
71
|
+
const thead = kids.find((c) => c.tagName === "thead");
|
|
72
|
+
const tbody = kids.find((c) => c.tagName === "tbody");
|
|
73
|
+
const hRow = (thead?.children ?? []).find((c) => c.tagName === "tr");
|
|
74
|
+
const headers = (hRow?.children ?? []).filter((c) => c.tagName === "th").map((c) => hastText(c).trim());
|
|
75
|
+
const rows = (tbody?.children ?? []).filter((c) => c.tagName === "tr").map((tr) => (tr.children ?? []).filter((c) => c.tagName === "td").map((c) => hastText(c).trim()));
|
|
76
|
+
return { headers, rows };
|
|
77
|
+
}
|
|
78
|
+
function MarkdownTable({ node, language = "en" }) {
|
|
79
|
+
const { headers, rows } = React.useMemo(() => parseTable(node), [node]);
|
|
80
|
+
if (!headers.length) {
|
|
81
|
+
return /* @__PURE__ */ jsx("div", { className: "my-3 overflow-x-auto rounded-lg border border-border", children: /* @__PURE__ */ jsx("table", { className: "w-full text-start text-sm" }) });
|
|
82
|
+
}
|
|
83
|
+
const columns = headers.map((h, i) => ({
|
|
84
|
+
accessorKey: `c${i}`,
|
|
85
|
+
header: h,
|
|
86
|
+
meta: { header_en: h, header_ar: h }
|
|
87
|
+
}));
|
|
88
|
+
const data = rows.map((r, i) => {
|
|
89
|
+
const o = { _id: String(i) };
|
|
90
|
+
r.forEach((c, j) => {
|
|
91
|
+
o[`c${j}`] = c;
|
|
92
|
+
});
|
|
93
|
+
return o;
|
|
94
|
+
});
|
|
95
|
+
return /* @__PURE__ */ jsx("div", { className: "my-3", children: /* @__PURE__ */ jsx(
|
|
96
|
+
DataTable,
|
|
97
|
+
{
|
|
98
|
+
columns,
|
|
99
|
+
data,
|
|
100
|
+
getRowId: (r) => r._id,
|
|
101
|
+
language,
|
|
102
|
+
showGlobalSearch: rows.length > 8,
|
|
103
|
+
showCsvExport: true,
|
|
104
|
+
showDensityToggle: false,
|
|
105
|
+
pageSize: rows.length > 25 ? 25 : 1e3
|
|
106
|
+
}
|
|
107
|
+
) });
|
|
108
|
+
}
|
|
109
|
+
function CodeBlock({ lang, children }) {
|
|
110
|
+
const boxRef = React.useRef(null);
|
|
111
|
+
const codeRef = React.useRef(null);
|
|
112
|
+
const [copied, setCopied] = React.useState(false);
|
|
113
|
+
const copy = () => {
|
|
114
|
+
const text = codeRef.current?.textContent ?? "";
|
|
115
|
+
navigator.clipboard?.writeText(text).then(() => {
|
|
116
|
+
setCopied(true);
|
|
117
|
+
setTimeout(() => setCopied(false), 1400);
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
const downloadPng = async () => {
|
|
121
|
+
if (!boxRef.current) return;
|
|
122
|
+
const { toPng } = await import("html-to-image");
|
|
123
|
+
const bg = getComputedStyle(boxRef.current).backgroundColor;
|
|
124
|
+
const url = await toPng(boxRef.current, { pixelRatio: 2, backgroundColor: bg });
|
|
125
|
+
const a = document.createElement("a");
|
|
126
|
+
a.href = url;
|
|
127
|
+
a.download = `code.${lang || "txt"}.png`;
|
|
128
|
+
a.click();
|
|
129
|
+
};
|
|
130
|
+
const raw = typeof children === "string" ? children : Array.isArray(children) && children.every((c) => typeof c === "string") ? children.join("") : null;
|
|
131
|
+
const html = raw != null ? highlightToHtml(raw.replace(/\n$/, ""), lang) : null;
|
|
132
|
+
return /* @__PURE__ */ jsxs("div", { className: "tg-code my-3 overflow-hidden rounded-lg border border-border", children: [
|
|
133
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-border bg-muted/60 px-3 py-1.5", children: [
|
|
134
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-xs text-muted-foreground", children: lang || "code" }),
|
|
135
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
|
|
136
|
+
/* @__PURE__ */ jsxs("button", { onClick: copy, className: "flex items-center gap-1 rounded px-1.5 py-0.5 text-xs text-muted-foreground transition hover:bg-accent hover:text-foreground", children: [
|
|
137
|
+
copied ? /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(Copy, { className: "h-3 w-3" }),
|
|
138
|
+
copied ? "Copied" : "Copy"
|
|
139
|
+
] }),
|
|
140
|
+
/* @__PURE__ */ jsxs("button", { onClick: downloadPng, "aria-label": "Download as PNG", className: "flex items-center gap-1 rounded px-1.5 py-0.5 text-xs text-muted-foreground transition hover:bg-accent hover:text-foreground", children: [
|
|
141
|
+
/* @__PURE__ */ jsx(ImageDown, { className: "h-3 w-3" }),
|
|
142
|
+
"PNG"
|
|
143
|
+
] })
|
|
144
|
+
] })
|
|
145
|
+
] }),
|
|
146
|
+
/* @__PURE__ */ jsx("div", { ref: boxRef, className: "bg-muted/40 p-3", children: /* @__PURE__ */ jsx("pre", { dir: "ltr", className: "overflow-x-auto text-[0.8rem] leading-relaxed [&>code]:bg-transparent [&>code]:p-0", children: html != null ? /* @__PURE__ */ jsx("code", { ref: codeRef, className: cn("hljs", lang && `language-${lang}`), dangerouslySetInnerHTML: { __html: html } }) : /* @__PURE__ */ jsx("code", { ref: codeRef, className: cn("hljs", lang && `language-${lang}`), children }) }) })
|
|
147
|
+
] });
|
|
148
|
+
}
|
|
149
|
+
var _mermaidPromise = null;
|
|
150
|
+
function loadMermaid() {
|
|
151
|
+
if (!_mermaidPromise) {
|
|
152
|
+
_mermaidPromise = import("mermaid").then((m) => {
|
|
153
|
+
m.default.initialize({ startOnLoad: false, securityLevel: "strict", theme: "neutral" });
|
|
154
|
+
return m.default;
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return _mermaidPromise;
|
|
158
|
+
}
|
|
159
|
+
var _mermaidSeq = 0;
|
|
160
|
+
function MermaidBlock({ code }) {
|
|
161
|
+
const ref = React.useRef(null);
|
|
162
|
+
const [error, setError] = React.useState(null);
|
|
163
|
+
React.useEffect(() => {
|
|
164
|
+
let cancelled = false;
|
|
165
|
+
loadMermaid().then(async (mermaid) => {
|
|
166
|
+
const id = `tg-mermaid-${++_mermaidSeq}`;
|
|
167
|
+
const { svg } = await mermaid.render(id, code);
|
|
168
|
+
if (!cancelled && ref.current) ref.current.innerHTML = svg;
|
|
169
|
+
}).catch((e) => !cancelled && setError(String(e?.message || e)));
|
|
170
|
+
return () => {
|
|
171
|
+
cancelled = true;
|
|
172
|
+
};
|
|
173
|
+
}, [code]);
|
|
174
|
+
if (error) {
|
|
175
|
+
return /* @__PURE__ */ jsxs("pre", { className: "overflow-auto rounded-lg border border-destructive/40 bg-destructive/10 p-3 text-xs text-destructive", children: [
|
|
176
|
+
"Mermaid error: ",
|
|
177
|
+
error,
|
|
178
|
+
"\n\n",
|
|
179
|
+
code
|
|
180
|
+
] });
|
|
181
|
+
}
|
|
182
|
+
return /* @__PURE__ */ jsx("div", { ref, className: "my-3 flex justify-center overflow-x-auto rounded-lg border border-border bg-card p-3" });
|
|
183
|
+
}
|
|
184
|
+
function MarkdownRenderer({ content, language = "en", className }) {
|
|
185
|
+
const isRTL = language === "ar";
|
|
186
|
+
return /* @__PURE__ */ jsx("div", { dir: isRTL ? "rtl" : "ltr", className: cn("tg-md text-sm leading-relaxed text-foreground", className), children: /* @__PURE__ */ jsx(
|
|
187
|
+
ReactMarkdown,
|
|
188
|
+
{
|
|
189
|
+
remarkPlugins: [remarkGfm],
|
|
190
|
+
rehypePlugins: [[rehypeHighlight, { detect: true, ignoreMissing: true }]],
|
|
191
|
+
components: {
|
|
192
|
+
h1: (p) => /* @__PURE__ */ jsx("h1", { className: "mb-3 mt-5 text-2xl font-bold", ...p }),
|
|
193
|
+
h2: (p) => /* @__PURE__ */ jsx("h2", { className: "mb-2 mt-5 border-b border-border pb-1 text-xl font-semibold", ...p }),
|
|
194
|
+
h3: (p) => /* @__PURE__ */ jsx("h3", { className: "mb-2 mt-4 text-lg font-semibold", ...p }),
|
|
195
|
+
p: (p) => /* @__PURE__ */ jsx("p", { className: "my-2.5", ...p }),
|
|
196
|
+
a: (p) => /* @__PURE__ */ jsx("a", { className: "font-medium text-primary underline-offset-2 hover:underline", ...p }),
|
|
197
|
+
ul: (p) => /* @__PURE__ */ jsx("ul", { className: "my-2.5 list-disc space-y-1 ps-6", ...p }),
|
|
198
|
+
ol: (p) => /* @__PURE__ */ jsx("ol", { className: "my-2.5 list-decimal space-y-1 ps-6", ...p }),
|
|
199
|
+
li: (p) => /* @__PURE__ */ jsx("li", { className: "marker:text-muted-foreground", ...p }),
|
|
200
|
+
blockquote: (p) => /* @__PURE__ */ jsx("blockquote", { className: "my-3 border-s-4 border-primary/40 ps-4 text-muted-foreground", ...p }),
|
|
201
|
+
hr: () => /* @__PURE__ */ jsx("hr", { className: "my-5 border-border" }),
|
|
202
|
+
// GFM tables render through the kit DataTable (sortable + CSV export).
|
|
203
|
+
table: (p) => /* @__PURE__ */ jsx(MarkdownTable, { node: p.node, language }),
|
|
204
|
+
code(props) {
|
|
205
|
+
const { children, className: cls, node, ...rest } = props;
|
|
206
|
+
const text = String(children ?? "");
|
|
207
|
+
const lang = /language-(\w+)/.exec(cls || "")?.[1];
|
|
208
|
+
const inline = !node || node.position?.start.line === node.position?.end.line;
|
|
209
|
+
if (lang === "mermaid") return /* @__PURE__ */ jsx(MermaidBlock, { code: text.replace(/\n$/, "") });
|
|
210
|
+
if (inline && !cls) return /* @__PURE__ */ jsx("code", { className: "rounded bg-muted px-1.5 py-0.5 font-mono text-[0.85em] text-foreground", ...rest, children });
|
|
211
|
+
return /* @__PURE__ */ jsx(CodeBlock, { lang, children });
|
|
212
|
+
},
|
|
213
|
+
pre: (p) => /* @__PURE__ */ jsx(Fragment, { children: p.children })
|
|
214
|
+
},
|
|
215
|
+
children: content
|
|
216
|
+
}
|
|
217
|
+
) });
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// src/components/markdown/MarkdownEditor.tsx
|
|
221
|
+
import * as React2 from "react";
|
|
222
|
+
import { Bold, Italic, Heading1, Heading2, Heading3, List, ListOrdered, Link2, Code, Code2, Quote, Eye, Pencil, Columns2 } from "lucide-react";
|
|
223
|
+
import { cn as cn2 } from "@togo-framework/ui-core";
|
|
224
|
+
import { Button } from "@togo-framework/ui-core";
|
|
225
|
+
import { Textarea } from "@togo-framework/ui-core";
|
|
226
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
227
|
+
var TOOLBAR = [
|
|
228
|
+
{ icon: Bold, title: { en: "Bold", ar: "\u063A\u0627\u0645\u0642" }, act: { wrap: "**" } },
|
|
229
|
+
{ icon: Italic, title: { en: "Italic", ar: "\u0645\u0627\u0626\u0644" }, act: { wrap: "_" } },
|
|
230
|
+
{ icon: Heading1, title: { en: "Heading 1", ar: "\u0639\u0646\u0648\u0627\u0646 1" }, act: { line: "# " } },
|
|
231
|
+
{ icon: Heading2, title: { en: "Heading 2", ar: "\u0639\u0646\u0648\u0627\u0646 2" }, act: { line: "## " } },
|
|
232
|
+
{ icon: Heading3, title: { en: "Heading 3", ar: "\u0639\u0646\u0648\u0627\u0646 3" }, act: { line: "### " } },
|
|
233
|
+
{ icon: List, title: { en: "Bulleted list", ar: "\u0642\u0627\u0626\u0645\u0629" }, act: { line: "- " } },
|
|
234
|
+
{ icon: ListOrdered, title: { en: "Numbered list", ar: "\u0642\u0627\u0626\u0645\u0629 \u0645\u0631\u0642\u0645\u0629" }, act: { line: "1. " } },
|
|
235
|
+
{ icon: Quote, title: { en: "Quote", ar: "\u0627\u0642\u062A\u0628\u0627\u0633" }, act: { line: "> " } },
|
|
236
|
+
{ icon: Link2, title: { en: "Link", ar: "\u0631\u0627\u0628\u0637" }, act: { wrap: "[", end: "](https://)" } },
|
|
237
|
+
{ icon: Code, title: { en: "Inline code", ar: "\u0643\u0648\u062F" }, act: { wrap: "`" } },
|
|
238
|
+
{ icon: Code2, title: { en: "Code block", ar: "\u0643\u062A\u0644\u0629 \u0643\u0648\u062F" }, act: { wrap: "\n```\n", end: "\n```\n" } }
|
|
239
|
+
];
|
|
240
|
+
var VIEWS = [
|
|
241
|
+
{ key: "write", icon: Pencil, en: "Write", ar: "\u0643\u062A\u0627\u0628\u0629" },
|
|
242
|
+
{ key: "preview", icon: Eye, en: "Preview", ar: "\u0645\u0639\u0627\u064A\u0646\u0629" },
|
|
243
|
+
{ key: "split", icon: Columns2, en: "Split", ar: "\u062A\u0642\u0633\u064A\u0645" }
|
|
244
|
+
];
|
|
245
|
+
function MarkdownEditor({
|
|
246
|
+
value,
|
|
247
|
+
defaultValue = "",
|
|
248
|
+
onChange,
|
|
249
|
+
view,
|
|
250
|
+
defaultView = "split",
|
|
251
|
+
onViewChange,
|
|
252
|
+
language = "en",
|
|
253
|
+
placeholder,
|
|
254
|
+
minRows = 10,
|
|
255
|
+
className
|
|
256
|
+
}) {
|
|
257
|
+
const isRTL = language === "ar";
|
|
258
|
+
const ref = React2.useRef(null);
|
|
259
|
+
const [internal, setInternal] = React2.useState(defaultValue);
|
|
260
|
+
const text = value ?? internal;
|
|
261
|
+
const setText = (v) => {
|
|
262
|
+
setInternal(v);
|
|
263
|
+
onChange?.(v);
|
|
264
|
+
};
|
|
265
|
+
const [internalView, setInternalView] = React2.useState(defaultView);
|
|
266
|
+
const activeView = view ?? internalView;
|
|
267
|
+
const setView = (v) => {
|
|
268
|
+
setInternalView(v);
|
|
269
|
+
onViewChange?.(v);
|
|
270
|
+
};
|
|
271
|
+
function apply(act) {
|
|
272
|
+
const el = ref.current;
|
|
273
|
+
if (!el) return;
|
|
274
|
+
const start = el.selectionStart, end = el.selectionEnd;
|
|
275
|
+
const sel = text.slice(start, end);
|
|
276
|
+
let next = text, caret = end;
|
|
277
|
+
if ("wrap" in act) {
|
|
278
|
+
const open = act.wrap, close = "end" in act ? act.end : act.wrap;
|
|
279
|
+
next = text.slice(0, start) + open + sel + close + text.slice(end);
|
|
280
|
+
caret = start + open.length + sel.length + close.length;
|
|
281
|
+
} else if ("line" in act) {
|
|
282
|
+
const lineStart = text.lastIndexOf("\n", start - 1) + 1;
|
|
283
|
+
next = text.slice(0, lineStart) + act.line + text.slice(lineStart);
|
|
284
|
+
caret = end + act.line.length;
|
|
285
|
+
} else if ("insert" in act) {
|
|
286
|
+
next = text.slice(0, start) + act.insert + text.slice(end);
|
|
287
|
+
caret = start + act.insert.length;
|
|
288
|
+
}
|
|
289
|
+
setText(next);
|
|
290
|
+
requestAnimationFrame(() => {
|
|
291
|
+
el.focus();
|
|
292
|
+
el.setSelectionRange(caret, caret);
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
const t = (en, ar) => isRTL ? ar : en;
|
|
296
|
+
return /* @__PURE__ */ jsxs2("div", { dir: isRTL ? "rtl" : "ltr", className: cn2("flex flex-col overflow-hidden rounded-xl border border-border bg-card", className), children: [
|
|
297
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex flex-wrap items-center gap-0.5 border-b border-border p-1.5", children: [
|
|
298
|
+
TOOLBAR.map(({ icon: Icon, title, act }, i) => /* @__PURE__ */ jsx2(
|
|
299
|
+
Button,
|
|
300
|
+
{
|
|
301
|
+
type: "button",
|
|
302
|
+
variant: "ghost",
|
|
303
|
+
size: "sm",
|
|
304
|
+
className: "h-7 w-7 p-0",
|
|
305
|
+
title: t(title.en, title.ar),
|
|
306
|
+
"aria-label": t(title.en, title.ar),
|
|
307
|
+
disabled: activeView === "preview",
|
|
308
|
+
onClick: () => apply(act),
|
|
309
|
+
children: /* @__PURE__ */ jsx2(Icon, { className: "h-4 w-4" })
|
|
310
|
+
},
|
|
311
|
+
i
|
|
312
|
+
)),
|
|
313
|
+
/* @__PURE__ */ jsx2("div", { className: "ms-auto flex items-center gap-0.5", children: VIEWS.map(({ key, icon: Icon, en, ar }) => /* @__PURE__ */ jsxs2(
|
|
314
|
+
Button,
|
|
315
|
+
{
|
|
316
|
+
type: "button",
|
|
317
|
+
size: "sm",
|
|
318
|
+
variant: activeView === key ? "secondary" : "ghost",
|
|
319
|
+
className: "h-7 gap-1.5 px-2",
|
|
320
|
+
onClick: () => setView(key),
|
|
321
|
+
children: [
|
|
322
|
+
/* @__PURE__ */ jsx2(Icon, { className: "h-3.5 w-3.5" }),
|
|
323
|
+
t(en, ar)
|
|
324
|
+
]
|
|
325
|
+
},
|
|
326
|
+
key
|
|
327
|
+
)) })
|
|
328
|
+
] }),
|
|
329
|
+
/* @__PURE__ */ jsxs2("div", { className: cn2("min-h-0 flex-1", activeView === "split" && "grid grid-cols-1 divide-y divide-border md:grid-cols-2 md:divide-x md:divide-y-0 rtl:md:divide-x-reverse"), children: [
|
|
330
|
+
activeView !== "preview" && /* @__PURE__ */ jsx2(
|
|
331
|
+
Textarea,
|
|
332
|
+
{
|
|
333
|
+
ref,
|
|
334
|
+
value: text,
|
|
335
|
+
onChange: (e) => setText(e.target.value),
|
|
336
|
+
placeholder: placeholder ?? t("Write markdown\u2026", "\u0627\u0643\u062A\u0628 \u0645\u0627\u0631\u0643\u062F\u0627\u0648\u0646\u2026"),
|
|
337
|
+
rows: minRows,
|
|
338
|
+
dir: isRTL ? "rtl" : "ltr",
|
|
339
|
+
className: "min-h-[12rem] resize-y rounded-none border-0 font-mono text-sm focus-visible:ring-0"
|
|
340
|
+
}
|
|
341
|
+
),
|
|
342
|
+
activeView !== "write" && /* @__PURE__ */ jsx2("div", { className: "min-h-[12rem] overflow-auto p-4", children: text.trim() ? /* @__PURE__ */ jsx2(MarkdownRenderer, { content: text, language }) : /* @__PURE__ */ jsx2("p", { className: "text-sm text-muted-foreground", children: t("Nothing to preview yet.", "\u0644\u0627 \u0634\u064A\u0621 \u0644\u0644\u0645\u0639\u0627\u064A\u0646\u0629 \u0628\u0639\u062F.") }) })
|
|
343
|
+
] })
|
|
344
|
+
] });
|
|
345
|
+
}
|
|
346
|
+
export {
|
|
347
|
+
CodeBlock,
|
|
348
|
+
MarkdownEditor,
|
|
349
|
+
MarkdownRenderer,
|
|
350
|
+
MarkdownTable
|
|
351
|
+
};
|
|
352
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/markdown/MarkdownRenderer.tsx","../src/components/markdown/MarkdownEditor.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\nimport rehypeHighlight from \"rehype-highlight\";\nimport { Copy, Check, ImageDown } from \"lucide-react\";\nimport type { ColumnDef } from \"@tanstack/react-table\";\nimport { cn } from \"@togo-framework/ui-core\";\nimport { DataTable } from \"@togo-framework/ui-data-table\";\n\n// Standalone syntax highlighting for CodeBlock — registers just the languages we use\n// (keeps the bundle lean; reuses the highlight.js the kit already depends on).\nimport hljs from \"highlight.js/lib/core\";\nimport hlBash from \"highlight.js/lib/languages/bash\";\nimport hlGo from \"highlight.js/lib/languages/go\";\nimport hlTs from \"highlight.js/lib/languages/typescript\";\nimport hlJs from \"highlight.js/lib/languages/javascript\";\nimport hlJson from \"highlight.js/lib/languages/json\";\nimport hlYaml from \"highlight.js/lib/languages/yaml\";\nimport hlSql from \"highlight.js/lib/languages/sql\";\nimport hlGraphql from \"highlight.js/lib/languages/graphql\";\nimport hlPwsh from \"highlight.js/lib/languages/powershell\";\nimport hlDockerfile from \"highlight.js/lib/languages/dockerfile\";\n\nconst HL_LANGS: Record<string, any> = {\n bash: hlBash, go: hlGo, typescript: hlTs, javascript: hlJs, json: hlJson,\n yaml: hlYaml, sql: hlSql, graphql: hlGraphql, powershell: hlPwsh, dockerfile: hlDockerfile,\n};\nfor (const [name, def] of Object.entries(HL_LANGS)) {\n try { if (!hljs.getLanguage(name)) hljs.registerLanguage(name, def); } catch { /* noop */ }\n}\nconst HL_ALIAS: Record<string, string> = {\n sh: \"bash\", shell: \"bash\", zsh: \"bash\", console: \"bash\",\n ts: \"typescript\", tsx: \"typescript\", js: \"javascript\", jsx: \"javascript\", mjs: \"javascript\",\n yml: \"yaml\", ps: \"powershell\", ps1: \"powershell\", gql: \"graphql\", docker: \"dockerfile\",\n};\n/** Highlight a raw code string to hljs HTML; returns null if the language is unknown. */\nfunction highlightToHtml(code: string, lang?: string): string | null {\n const key = lang ? HL_ALIAS[lang] ?? lang : undefined;\n try {\n if (key && hljs.getLanguage(key)) return hljs.highlight(code, { language: key }).value;\n } catch { /* fall through */ }\n return null;\n}\n\nexport interface MarkdownRendererProps {\n content: string;\n language?: \"en\" | \"ar\";\n className?: string;\n}\n\n// ── Markdown table → kit DataTable (sortable + CSV export) ──────────────────────\nfunction hastText(node: any): string {\n if (!node) return \"\";\n if (node.type === \"text\") return node.value ?? \"\";\n if (Array.isArray(node.children)) return node.children.map(hastText).join(\"\");\n return \"\";\n}\nfunction parseTable(node: any): { headers: string[]; rows: string[][] } {\n const kids = node?.children ?? [];\n const thead = kids.find((c: any) => c.tagName === \"thead\");\n const tbody = kids.find((c: any) => c.tagName === \"tbody\");\n const hRow = (thead?.children ?? []).find((c: any) => c.tagName === \"tr\");\n const headers = (hRow?.children ?? []).filter((c: any) => c.tagName === \"th\").map((c: any) => hastText(c).trim());\n const rows = (tbody?.children ?? []).filter((c: any) => c.tagName === \"tr\").map((tr: any) =>\n (tr.children ?? []).filter((c: any) => c.tagName === \"td\").map((c: any) => hastText(c).trim()));\n return { headers, rows };\n}\n\n/** MarkdownTable — renders a GFM table via the kit's DataTable (sortable, searchable,\n * CSV export). Exported so apps can reuse the markdown-table → DataTable mapping. */\nexport function MarkdownTable({ node, language = \"en\" }: { node: any; language?: \"en\" | \"ar\" }) {\n const { headers, rows } = React.useMemo(() => parseTable(node), [node]);\n if (!headers.length) {\n return <div className=\"my-3 overflow-x-auto rounded-lg border border-border\"><table className=\"w-full text-start text-sm\" /></div>;\n }\n const columns: ColumnDef<Record<string, string>>[] = headers.map((h, i) => ({\n accessorKey: `c${i}`,\n header: h,\n meta: { header_en: h, header_ar: h },\n }));\n const data = rows.map((r, i) => {\n const o: Record<string, string> = { _id: String(i) };\n r.forEach((c, j) => { o[`c${j}`] = c; });\n return o;\n });\n return (\n <div className=\"my-3\">\n <DataTable\n columns={columns}\n data={data}\n getRowId={(r) => (r as { _id: string })._id}\n language={language}\n showGlobalSearch={rows.length > 8}\n showCsvExport\n showDensityToggle={false}\n pageSize={rows.length > 25 ? 25 : 1000}\n />\n </div>\n );\n}\n\n// ── Code block — styled header + copy + PNG export ──────────────────────────────\n/** CodeBlock — a fenced code block with a language label, Copy, and download-as-PNG.\n * Exported so apps can render standalone code with the same chrome. */\nexport function CodeBlock({ lang, children }: { lang?: string; children?: React.ReactNode }) {\n const boxRef = React.useRef<HTMLDivElement>(null);\n const codeRef = React.useRef<HTMLElement>(null);\n const [copied, setCopied] = React.useState(false);\n\n const copy = () => {\n const text = codeRef.current?.textContent ?? \"\";\n navigator.clipboard?.writeText(text).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1400); });\n };\n const downloadPng = async () => {\n if (!boxRef.current) return;\n const { toPng } = await import(\"html-to-image\");\n const bg = getComputedStyle(boxRef.current).backgroundColor;\n const url = await toPng(boxRef.current, { pixelRatio: 2, backgroundColor: bg });\n const a = document.createElement(\"a\");\n a.href = url; a.download = `code.${lang || \"txt\"}.png`; a.click();\n };\n\n // Standalone use (e.g. on a landing page) passes a raw string → highlight it here.\n // MarkdownRenderer passes already-highlighted nodes (rehype-highlight) → render as-is.\n const raw =\n typeof children === \"string\" ? children\n : Array.isArray(children) && children.every((c) => typeof c === \"string\") ? children.join(\"\")\n : null;\n const html = raw != null ? highlightToHtml(raw.replace(/\\n$/, \"\"), lang) : null;\n\n return (\n <div className=\"tg-code my-3 overflow-hidden rounded-lg border border-border\">\n <div className=\"flex items-center justify-between border-b border-border bg-muted/60 px-3 py-1.5\">\n <span className=\"font-mono text-xs text-muted-foreground\">{lang || \"code\"}</span>\n <span className=\"flex items-center gap-1\">\n <button onClick={copy} className=\"flex items-center gap-1 rounded px-1.5 py-0.5 text-xs text-muted-foreground transition hover:bg-accent hover:text-foreground\">\n {copied ? <Check className=\"h-3 w-3\" /> : <Copy className=\"h-3 w-3\" />}{copied ? \"Copied\" : \"Copy\"}\n </button>\n <button onClick={downloadPng} aria-label=\"Download as PNG\" className=\"flex items-center gap-1 rounded px-1.5 py-0.5 text-xs text-muted-foreground transition hover:bg-accent hover:text-foreground\">\n <ImageDown className=\"h-3 w-3\" />PNG\n </button>\n </span>\n </div>\n <div ref={boxRef} className=\"bg-muted/40 p-3\">\n <pre dir=\"ltr\" className=\"overflow-x-auto text-[0.8rem] leading-relaxed [&>code]:bg-transparent [&>code]:p-0\">\n {html != null\n ? <code ref={codeRef} className={cn(\"hljs\", lang && `language-${lang}`)} dangerouslySetInnerHTML={{ __html: html }} />\n : <code ref={codeRef} className={cn(\"hljs\", lang && `language-${lang}`)}>{children}</code>}\n </pre>\n </div>\n </div>\n );\n}\n\n// ── Mermaid diagram block (rendered client-side, lazily) ────────────────────────\nlet _mermaidPromise: Promise<typeof import(\"mermaid\").default> | null = null;\nfunction loadMermaid() {\n if (!_mermaidPromise) {\n _mermaidPromise = import(\"mermaid\").then((m) => {\n m.default.initialize({ startOnLoad: false, securityLevel: \"strict\", theme: \"neutral\" });\n return m.default;\n });\n }\n return _mermaidPromise;\n}\n\nlet _mermaidSeq = 0;\nfunction MermaidBlock({ code }: { code: string }) {\n const ref = React.useRef<HTMLDivElement>(null);\n const [error, setError] = React.useState<string | null>(null);\n React.useEffect(() => {\n let cancelled = false;\n loadMermaid()\n .then(async (mermaid) => {\n const id = `tg-mermaid-${++_mermaidSeq}`;\n const { svg } = await mermaid.render(id, code);\n if (!cancelled && ref.current) ref.current.innerHTML = svg;\n })\n .catch((e) => !cancelled && setError(String(e?.message || e)));\n return () => { cancelled = true; };\n }, [code]);\n if (error) {\n return (\n <pre className=\"overflow-auto rounded-lg border border-destructive/40 bg-destructive/10 p-3 text-xs text-destructive\">\n Mermaid error: {error}\n {\"\\n\\n\"}{code}\n </pre>\n );\n }\n return <div ref={ref} className=\"my-3 flex justify-center overflow-x-auto rounded-lg border border-border bg-card p-3\" />;\n}\n\n/** MarkdownRenderer — GFM markdown with highlighted code, mermaid diagrams, and\n * token-themed prose (headings/lists/tables/code/quote/links). RTL-aware. */\nexport function MarkdownRenderer({ content, language = \"en\", className }: MarkdownRendererProps) {\n const isRTL = language === \"ar\";\n return (\n <div dir={isRTL ? \"rtl\" : \"ltr\"} className={cn(\"tg-md text-sm leading-relaxed text-foreground\", className)}>\n <ReactMarkdown\n remarkPlugins={[remarkGfm]}\n rehypePlugins={[[rehypeHighlight, { detect: true, ignoreMissing: true }]]}\n components={{\n h1: (p) => <h1 className=\"mb-3 mt-5 text-2xl font-bold\" {...p} />,\n h2: (p) => <h2 className=\"mb-2 mt-5 border-b border-border pb-1 text-xl font-semibold\" {...p} />,\n h3: (p) => <h3 className=\"mb-2 mt-4 text-lg font-semibold\" {...p} />,\n p: (p) => <p className=\"my-2.5\" {...p} />,\n a: (p) => <a className=\"font-medium text-primary underline-offset-2 hover:underline\" {...p} />,\n ul: (p) => <ul className=\"my-2.5 list-disc space-y-1 ps-6\" {...p} />,\n ol: (p) => <ol className=\"my-2.5 list-decimal space-y-1 ps-6\" {...p} />,\n li: (p) => <li className=\"marker:text-muted-foreground\" {...p} />,\n blockquote: (p) => <blockquote className=\"my-3 border-s-4 border-primary/40 ps-4 text-muted-foreground\" {...p} />,\n hr: () => <hr className=\"my-5 border-border\" />,\n // GFM tables render through the kit DataTable (sortable + CSV export).\n table: (p: any) => <MarkdownTable node={p.node} language={language} />,\n code(props) {\n const { children, className: cls, node, ...rest } = props as any;\n const text = String(children ?? \"\");\n const lang = /language-(\\w+)/.exec(cls || \"\")?.[1];\n const inline = !node || node.position?.start.line === node.position?.end.line;\n if (lang === \"mermaid\") return <MermaidBlock code={text.replace(/\\n$/, \"\")} />;\n if (inline && !cls) return <code className=\"rounded bg-muted px-1.5 py-0.5 font-mono text-[0.85em] text-foreground\" {...rest}>{children}</code>;\n // Block code → styled CodeBlock (copy + PNG export). pre passes through.\n return <CodeBlock lang={lang}>{children}</CodeBlock>;\n },\n pre: (p: any) => <>{p.children}</>,\n }}\n >\n {content}\n </ReactMarkdown>\n </div>\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { Bold, Italic, Heading1, Heading2, Heading3, List, ListOrdered, Link2, Code, Code2, Quote, Eye, Pencil, Columns2 } from \"lucide-react\";\nimport { cn } from \"@togo-framework/ui-core\";\nimport { Button } from \"@togo-framework/ui-core\";\nimport { Textarea } from \"@togo-framework/ui-core\";\nimport { MarkdownRenderer } from \"./MarkdownRenderer\";\n\nexport type MarkdownView = \"write\" | \"preview\" | \"split\";\n\nexport interface MarkdownEditorProps {\n value?: string;\n defaultValue?: string;\n onChange?: (value: string) => void;\n view?: MarkdownView;\n defaultView?: MarkdownView;\n onViewChange?: (view: MarkdownView) => void;\n language?: \"en\" | \"ar\";\n placeholder?: string;\n minRows?: number;\n className?: string;\n}\n\ntype Action =\n | { wrap: string }\n | { wrap: string; end: string }\n | { line: string }\n | { insert: string };\n\nconst TOOLBAR: { icon: React.ElementType; title: { en: string; ar: string }; act: Action }[] = [\n { icon: Bold, title: { en: \"Bold\", ar: \"غامق\" }, act: { wrap: \"**\" } },\n { icon: Italic, title: { en: \"Italic\", ar: \"مائل\" }, act: { wrap: \"_\" } },\n { icon: Heading1, title: { en: \"Heading 1\", ar: \"عنوان 1\" }, act: { line: \"# \" } },\n { icon: Heading2, title: { en: \"Heading 2\", ar: \"عنوان 2\" }, act: { line: \"## \" } },\n { icon: Heading3, title: { en: \"Heading 3\", ar: \"عنوان 3\" }, act: { line: \"### \" } },\n { icon: List, title: { en: \"Bulleted list\", ar: \"قائمة\" }, act: { line: \"- \" } },\n { icon: ListOrdered, title: { en: \"Numbered list\", ar: \"قائمة مرقمة\" }, act: { line: \"1. \" } },\n { icon: Quote, title: { en: \"Quote\", ar: \"اقتباس\" }, act: { line: \"> \" } },\n { icon: Link2, title: { en: \"Link\", ar: \"رابط\" }, act: { wrap: \"[\", end: \"](https://)\" } },\n { icon: Code, title: { en: \"Inline code\", ar: \"كود\" }, act: { wrap: \"`\" } },\n { icon: Code2, title: { en: \"Code block\", ar: \"كتلة كود\" }, act: { wrap: \"\\n```\\n\", end: \"\\n```\\n\" } },\n];\n\nconst VIEWS: { key: MarkdownView; icon: React.ElementType; en: string; ar: string }[] = [\n { key: \"write\", icon: Pencil, en: \"Write\", ar: \"كتابة\" },\n { key: \"preview\", icon: Eye, en: \"Preview\", ar: \"معاينة\" },\n { key: \"split\", icon: Columns2, en: \"Split\", ar: \"تقسيم\" },\n];\n\n/** MarkdownEditor — toolbar (bold/italic/headings/lists/link/code/quote) + a textarea\n * with a Write / Preview / Split view toggle (Split shows a live MarkdownRenderer).\n * Dependency-light (no CodeMirror/Monaco). Controlled or uncontrolled. RTL + EN/AR. */\nexport function MarkdownEditor({\n value, defaultValue = \"\", onChange, view, defaultView = \"split\", onViewChange,\n language = \"en\", placeholder, minRows = 10, className,\n}: MarkdownEditorProps) {\n const isRTL = language === \"ar\";\n const ref = React.useRef<HTMLTextAreaElement>(null);\n const [internal, setInternal] = React.useState(defaultValue);\n const text = value ?? internal;\n const setText = (v: string) => { setInternal(v); onChange?.(v); };\n\n const [internalView, setInternalView] = React.useState<MarkdownView>(defaultView);\n const activeView = view ?? internalView;\n const setView = (v: MarkdownView) => { setInternalView(v); onViewChange?.(v); };\n\n function apply(act: Action) {\n const el = ref.current;\n if (!el) return;\n const start = el.selectionStart, end = el.selectionEnd;\n const sel = text.slice(start, end);\n let next = text, caret = end;\n if (\"wrap\" in act) {\n const open = act.wrap, close = (\"end\" in act ? act.end : act.wrap);\n next = text.slice(0, start) + open + sel + close + text.slice(end);\n caret = start + open.length + sel.length + close.length;\n } else if (\"line\" in act) {\n const lineStart = text.lastIndexOf(\"\\n\", start - 1) + 1;\n next = text.slice(0, lineStart) + act.line + text.slice(lineStart);\n caret = end + act.line.length;\n } else if (\"insert\" in act) {\n next = text.slice(0, start) + act.insert + text.slice(end);\n caret = start + act.insert.length;\n }\n setText(next);\n requestAnimationFrame(() => { el.focus(); el.setSelectionRange(caret, caret); });\n }\n\n const t = (en: string, ar: string) => (isRTL ? ar : en);\n\n return (\n <div dir={isRTL ? \"rtl\" : \"ltr\"} className={cn(\"flex flex-col overflow-hidden rounded-xl border border-border bg-card\", className)}>\n {/* Toolbar */}\n <div className=\"flex flex-wrap items-center gap-0.5 border-b border-border p-1.5\">\n {TOOLBAR.map(({ icon: Icon, title, act }, i) => (\n <Button key={i} type=\"button\" variant=\"ghost\" size=\"sm\" className=\"h-7 w-7 p-0\"\n title={t(title.en, title.ar)} aria-label={t(title.en, title.ar)}\n disabled={activeView === \"preview\"} onClick={() => apply(act)}>\n <Icon className=\"h-4 w-4\" />\n </Button>\n ))}\n <div className=\"ms-auto flex items-center gap-0.5\">\n {VIEWS.map(({ key, icon: Icon, en, ar }) => (\n <Button key={key} type=\"button\" size=\"sm\" variant={activeView === key ? \"secondary\" : \"ghost\"}\n className=\"h-7 gap-1.5 px-2\" onClick={() => setView(key)}>\n <Icon className=\"h-3.5 w-3.5\" />{t(en, ar)}\n </Button>\n ))}\n </div>\n </div>\n\n {/* Body */}\n <div className={cn(\"min-h-0 flex-1\", activeView === \"split\" && \"grid grid-cols-1 divide-y divide-border md:grid-cols-2 md:divide-x md:divide-y-0 rtl:md:divide-x-reverse\")}>\n {activeView !== \"preview\" && (\n <Textarea\n ref={ref}\n value={text}\n onChange={(e) => setText(e.target.value)}\n placeholder={placeholder ?? t(\"Write markdown…\", \"اكتب ماركداون…\")}\n rows={minRows}\n dir={isRTL ? \"rtl\" : \"ltr\"}\n className=\"min-h-[12rem] resize-y rounded-none border-0 font-mono text-sm focus-visible:ring-0\"\n />\n )}\n {activeView !== \"write\" && (\n <div className=\"min-h-[12rem] overflow-auto p-4\">\n {text.trim()\n ? <MarkdownRenderer content={text} language={language} />\n : <p className=\"text-sm text-muted-foreground\">{t(\"Nothing to preview yet.\", \"لا شيء للمعاينة بعد.\")}</p>}\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["React","cn","jsx","jsxs","cn"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@togo-framework/ui-markdown",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "togo UI kit — markdown renderer (GFM, syntax highlighting, mermaid-ready) + editor. Depends on @togo-framework/ui-core and @togo-framework/ui-data-table.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": ["dist"],
|
|
17
|
+
"sideEffects": false,
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"dev": "tsup --watch",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"prepare": "tsup"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"lucide-react": ">=0.400.0",
|
|
26
|
+
"react": ">=18",
|
|
27
|
+
"react-dom": ">=18"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@togo-framework/ui-core": "^0.1.2",
|
|
31
|
+
"@togo-framework/ui-data-table": "^0.1.0",
|
|
32
|
+
"@tanstack/react-table": "^8.21.3",
|
|
33
|
+
"highlight.js": "^11.11.1",
|
|
34
|
+
"html-to-image": "^1.11.13",
|
|
35
|
+
"mermaid": "^11.15.0",
|
|
36
|
+
"react-markdown": "^10.1.0",
|
|
37
|
+
"rehype-highlight": "^7.0.2",
|
|
38
|
+
"remark-gfm": "^4.0.1"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/react": "^19.0.0",
|
|
42
|
+
"@types/react-dom": "^19.0.0",
|
|
43
|
+
"lucide-react": "^0.462.0",
|
|
44
|
+
"react": "^19.0.0",
|
|
45
|
+
"react-dom": "^19.0.0",
|
|
46
|
+
"tsup": "^8.3.0",
|
|
47
|
+
"typescript": "^5.6.0"
|
|
48
|
+
}
|
|
49
|
+
}
|