| undefined>(
+ undefined,
+ );
+
+ useEffect(
+ () => () => {
+ clearTimeout(copiedTimer.current);
+ },
+ [],
+ );
+
+ async function copy() {
+ await navigator.clipboard.writeText(value);
+ setCopied(true);
+ clearTimeout(copiedTimer.current);
+ copiedTimer.current = setTimeout(() => setCopied(false), 1200);
+ }
+
+ const code = (
+ void copy()}
+ onKeyDown={(event) => {
+ if (event.key !== "Enter" && event.key !== " ") return;
+ event.preventDefault();
+ void copy();
+ }}
+ >
+ {value}
+
+ );
+
+ if (block) {
+ return (
+
+ );
+ }
+
+ return (
+ <>
+ {code}
+
+ >
+ );
+}
+
+/**
+ * Executes parseInlineNodes.
+ * @param input Parameter input.
+ * @returns InlineNode[].
+ */
+export function parseInlineNodes(input: string): InlineNode[] {
+ const nodes: InlineNode[] = [];
+
+ let cursor = 0;
+ let match = INLINE_TOKEN_REGEX.exec(input);
+
+ while (match) {
+ const index = match.index;
+ const raw = match[0];
+
+ if (index > cursor) {
+ nodes.push(...parseEmojiText(input.slice(cursor, index)));
+ }
+
+ if (match[1] !== undefined && match[2] !== undefined) {
+ nodes.push({ type: "image", alt: match[1], src: normalizeUrl(match[2]) });
+ } else if (match[3] !== undefined && match[4] !== undefined) {
+ nodes.push({
+ type: "link",
+ label: match[3],
+ href: normalizeUrl(match[4]),
+ });
+ } else if (match[5] !== undefined) {
+ nodes.push({ type: "code", value: match[5] });
+ } else if (match[6] !== undefined) {
+ nodes.push({ type: "del", value: match[6] });
+ } else if (match[7] !== undefined || match[8] !== undefined) {
+ nodes.push({ type: "strong", value: match[7] ?? match[8] ?? "" });
+ } else if (match[9] !== undefined || match[10] !== undefined) {
+ nodes.push({ type: "em", value: match[9] ?? match[10] ?? "" });
+ } else {
+ nodes.push(...parseEmojiText(raw));
+ }
+
+ cursor = index + raw.length;
+ match = INLINE_TOKEN_REGEX.exec(input);
+ }
+
+ if (cursor < input.length) {
+ nodes.push(...parseEmojiText(input.slice(cursor)));
+ }
+
+ INLINE_TOKEN_REGEX.lastIndex = 0;
+ return nodes;
+}
+
+export function parseEmojiText(input: string): InlineNode[] {
+ const nodes: InlineNode[] = [];
+ let cursor = 0;
+
+ for (const match of findEmojiShortcodes(input)) {
+ if (match.from > cursor) {
+ nodes.push({ type: "text", value: input.slice(cursor, match.from) });
+ }
+
+ nodes.push({ type: "emoji", shortcode: match.emoji.shortcode });
+ cursor = match.to;
+ }
+
+ if (cursor < input.length) {
+ nodes.push({ type: "text", value: input.slice(cursor) });
+ }
+
+ return nodes;
+}
+
+/**
+ * Executes collectInlineRanges.
+ * @param input Parameter input.
+ * @param offset Parameter offset.
+ * @returns {
+ styleRanges: InlineDecorationRange[];
+ tokenRanges: InlineTokenRange[];
+}.
+ */
+export function collectInlineRanges(
+ input: string,
+ offset = 0,
+): {
+ styleRanges: InlineDecorationRange[];
+ tokenRanges: InlineTokenRange[];
+} {
+ const styleRanges: InlineDecorationRange[] = [];
+ const tokenRanges: InlineTokenRange[] = [];
+
+ let match = INLINE_TOKEN_REGEX.exec(input);
+
+ while (match) {
+ const raw = match[0];
+ const start = offset + match.index;
+ const end = start + raw.length;
+
+ if (match[1] !== undefined && match[2] !== undefined) {
+ const openLength = 2;
+ const closeLength = raw.endsWith(")") ? 1 : 0;
+
+ const imageEnd = start + openLength + match[1].length;
+ tokenRanges.push({ from: start, to: start + openLength });
+ tokenRanges.push({ from: imageEnd, to: imageEnd + 1 });
+
+ const srcStart = imageEnd + 1;
+ const srcEnd = end - closeLength;
+ tokenRanges.push({ from: srcStart, to: srcStart + 1 });
+ tokenRanges.push({ from: srcEnd, to: srcEnd + closeLength });
+ } else if (match[3] !== undefined && match[4] !== undefined) {
+ const label = match[3];
+ const labelStart = start + 1;
+ const labelEnd = labelStart + label.length;
+
+ tokenRanges.push({ from: start, to: start + 1 });
+ tokenRanges.push({ from: labelEnd, to: labelEnd + 1 });
+ tokenRanges.push({ from: labelEnd + 1, to: labelEnd + 2 });
+ tokenRanges.push({ from: end - 1, to: end });
+
+ styleRanges.push({
+ from: labelStart,
+ to: labelEnd,
+ className: "tm-md-link",
+ });
+ } else if (match[5] !== undefined) {
+ const codeStart = start + 1;
+ const codeEnd = end - 1;
+
+ tokenRanges.push({ from: start, to: start + 1 });
+ tokenRanges.push({ from: end - 1, to: end });
+ styleRanges.push({
+ from: codeStart,
+ to: codeEnd,
+ className: "tm-md-code",
+ });
+ } else if (match[6] !== undefined) {
+ const contentStart = start + 2;
+ const contentEnd = end - 2;
+
+ tokenRanges.push({ from: start, to: start + 2 });
+ tokenRanges.push({ from: end - 2, to: end });
+ styleRanges.push({
+ from: contentStart,
+ to: contentEnd,
+ className: "tm-md-del",
+ });
+ } else if (match[7] !== undefined || match[8] !== undefined) {
+ const contentStart = start + 2;
+ const contentEnd = end - 2;
+
+ tokenRanges.push({ from: start, to: start + 2 });
+ tokenRanges.push({ from: end - 2, to: end });
+ styleRanges.push({
+ from: contentStart,
+ to: contentEnd,
+ className: "tm-md-strong",
+ });
+ } else if (match[9] !== undefined || match[10] !== undefined) {
+ const contentStart = start + 1;
+ const contentEnd = end - 1;
+
+ tokenRanges.push({ from: start, to: start + 1 });
+ tokenRanges.push({ from: end - 1, to: end });
+ styleRanges.push({
+ from: contentStart,
+ to: contentEnd,
+ className: "tm-md-em",
+ });
+ }
+
+ match = INLINE_TOKEN_REGEX.exec(input);
+ }
+
+ INLINE_TOKEN_REGEX.lastIndex = 0;
+ return { styleRanges, tokenRanges };
+}
+
+/**
+ * Executes parseMarkdownBlocks.
+ * @param markdown Parameter markdown.
+ * @returns MarkdownBlock[].
+ */
+export function parseMarkdownBlocks(markdown: string): MarkdownBlock[] {
+ const lines = markdown.replace(/\r\n/g, "\n").split("\n");
+ const blocks: MarkdownBlock[] = [];
+
+ let index = 0;
+
+ while (index < lines.length) {
+ const line = lines[index];
+
+ if (!line.trim()) {
+ index += 1;
+ continue;
+ }
+
+ const codeFence = line.match(/^```\s*([^`]*)$/);
+ if (codeFence) {
+ const language = (codeFence[1] ?? "").trim();
+ const codeLines: string[] = [];
+ index += 1;
+
+ while (index < lines.length && !/^```\s*$/.test(lines[index])) {
+ codeLines.push(lines[index]);
+ index += 1;
+ }
+
+ if (index < lines.length) {
+ index += 1;
+ }
+
+ blocks.push({ type: "code", language, code: codeLines.join("\n") });
+ continue;
+ }
+
+ if (/^(?:\*\s*){3,}$|^(?:-\s*){3,}$|^(?:_\s*){3,}$/.test(line.trim())) {
+ blocks.push({ type: "hr" });
+ index += 1;
+ continue;
+ }
+
+ const heading = line.match(/^(#{1,6})\s+(.+)$/);
+ if (heading) {
+ blocks.push({
+ type: "heading",
+ level: heading[1].length,
+ text: heading[2],
+ });
+ index += 1;
+ continue;
+ }
+
+ const quote = line.match(/^>\s?(.*)$/);
+ if (quote) {
+ const quoteLines: string[] = [quote[1]];
+ index += 1;
+
+ while (index < lines.length) {
+ const next = lines[index].match(/^>\s?(.*)$/);
+ if (!next) break;
+ quoteLines.push(next[1]);
+ index += 1;
+ }
+
+ blocks.push({ type: "blockquote", text: quoteLines.join("\n") });
+ continue;
+ }
+
+ const tableCandidate = readTable(lines, index);
+ if (tableCandidate) {
+ blocks.push(tableCandidate.block);
+ index = tableCandidate.nextIndex;
+ continue;
+ }
+
+ const unordered = line.match(/^\s*[-*+]\s+(.*)$/);
+ const ordered = line.match(/^\s*\d+\.\s+(.*)$/);
+ if (unordered || ordered) {
+ const orderedList = Boolean(ordered);
+ const items: ListItem[] = [];
+
+ while (index < lines.length) {
+ const current = lines[index];
+ const match = orderedList
+ ? current.match(/^\s*\d+\.\s+(.*)$/)
+ : current.match(/^\s*[-*+]\s+(.*)$/);
+
+ if (!match) break;
+
+ const task = match[1].match(/^\[( |x|X)\]\s+(.*)$/);
+ if (task) {
+ items.push({
+ text: task[2],
+ checked: task[1].toLowerCase() === "x",
+ });
+ } else {
+ items.push({ text: match[1], checked: null });
+ }
+
+ index += 1;
+ }
+
+ blocks.push({ type: "list", ordered: orderedList, items });
+ continue;
+ }
+
+ const paragraphLines = [line];
+ index += 1;
+
+ while (
+ index < lines.length &&
+ lines[index].trim() &&
+ !/^(#{1,6})\s+/.test(lines[index]) &&
+ !/^```\s*/.test(lines[index]) &&
+ !/^>\s?/.test(lines[index]) &&
+ !/^\s*[-*+]\s+/.test(lines[index]) &&
+ !/^\s*\d+\.\s+/.test(lines[index]) &&
+ !/^(?:\*\s*){3,}$|^(?:-\s*){3,}$|^(?:_\s*){3,}$/.test(lines[index].trim())
+ ) {
+ paragraphLines.push(lines[index]);
+ index += 1;
+ }
+
+ blocks.push({ type: "paragraph", text: paragraphLines.join("\n") });
+ }
+
+ return blocks;
+}
+
+/**
+ * Executes renderInline.
+ * @param nodes Parameter nodes.
+ * @returns React.ReactNode[].
+ */
+function renderInline(nodes: InlineNode[]): ReactNode[] {
+ return nodes.map((node, index) => {
+ if (node.type === "text") {
+ return node.value;
+ }
+
+ if (node.type === "emoji") {
+ return (
+
+ );
+ }
+
+ if (node.type === "strong") {
+ return (
+
+ {renderInline(parseEmojiText(node.value))}
+
+ );
+ }
+
+ if (node.type === "em") {
+ return (
+
+ {renderInline(parseEmojiText(node.value))}
+
+ );
+ }
+
+ if (node.type === "del") {
+ return (
+
+ {renderInline(parseEmojiText(node.value))}
+
+ );
+ }
+
+ if (node.type === "code") {
+ return ;
+ }
+
+ if (node.type === "link") {
+ return (
+
+ {renderInline(parseEmojiText(node.label))}
+
+ );
+ }
+
+ return (
+
+ );
+ });
+}
+
+/**
+ * Executes renderBlocks.
+ * @param blocks Parameter blocks.
+ * @returns React.ReactElement.
+ */
+export function renderBlocks(blocks: MarkdownBlock[]): ReactElement {
+ return (
+ <>
+ {blocks.map((block, blockIndex) => {
+ if (block.type === "heading") {
+ const className = `tm-md-heading tm-md-h${String(block.level)}`;
+ if (block.level === 1)
+ return (
+
+ {renderInline(parseInlineNodes(block.text))}
+
+ );
+ if (block.level === 2)
+ return (
+
+ {renderInline(parseInlineNodes(block.text))}
+
+ );
+ if (block.level === 3)
+ return (
+
+ {renderInline(parseInlineNodes(block.text))}
+
+ );
+ if (block.level === 4)
+ return (
+
+ {renderInline(parseInlineNodes(block.text))}
+
+ );
+ if (block.level === 5)
+ return (
+
+ {renderInline(parseInlineNodes(block.text))}
+
+ );
+ return (
+
+ {renderInline(parseInlineNodes(block.text))}
+
+ );
+ }
+
+ if (block.type === "blockquote") {
+ return (
+
+ {block.text.split("\n").map((line, lineIndex) => (
+ {renderInline(parseInlineNodes(line))}
+ ))}
+
+ );
+ }
+
+ if (block.type === "code") {
+ return (
+
+ );
+ }
+
+ if (block.type === "list") {
+ const Tag = block.ordered ? "ol" : "ul";
+ return (
+
+ {block.items.map((item, itemIndex) => (
+
+ {item.checked !== null ? (
+
+ ) : null}
+ {renderInline(parseInlineNodes(item.text))}
+
+ ))}
+
+ );
+ }
+
+ if (block.type === "table") {
+ return (
+
+
+
+
+ {block.headers.map((header, headerIndex) => (
+ |
+ {renderInline(parseInlineNodes(header))}
+ |
+ ))}
+
+
+
+ {block.rows.map((row, rowIndex) => (
+
+ {row.map((cell, cellIndex) => (
+ |
+ {renderInline(parseInlineNodes(cell))}
+ |
+ ))}
+
+ ))}
+
+
+
+ );
+ }
+
+ if (block.type === "hr") {
+ return
;
+ }
+
+ return (
+
+ {block.text.split("\n").map((line, lineIndex) => (
+
+ {lineIndex > 0 ?
: null}
+ {renderInline(parseInlineNodes(line))}
+
+ ))}
+
+ );
+ })}
+ >
+ );
+}
+
+/**
+ * Executes normalizeUrl.
+ * @param input Parameter input.
+ * @returns string.
+ */
+function normalizeUrl(input: string): string {
+ const value = input.trim();
+ if (/^(https?:|mailto:|tel:|\/)/i.test(value)) {
+ return value;
+ }
+
+ return "#";
+}
+
+/**
+ * Executes splitTableRow.
+ * @param row Parameter row.
+ * @returns string[].
+ */
+function splitTableRow(row: string): string[] {
+ const cleaned = row.trim().replace(/^\|/, "").replace(/\|$/, "");
+ return cleaned.split("|").map((cell) => cell.trim());
+}
+
+/**
+ * Executes readTable.
+ * @param lines Parameter lines.
+ * @param index Parameter index.
+ * @returns { block: TableBlock; nextIndex: number } | null.
+ */
+function readTable(
+ lines: string[],
+ index: number,
+): { block: TableBlock; nextIndex: number } | null {
+ const header = lines[index] ?? "";
+ const separator = lines[index + 1] ?? "";
+
+ if (!header.includes("|") || !separator.includes("|")) {
+ return null;
+ }
+
+ const separatorCells = splitTableRow(separator);
+ const isSeparator = separatorCells.every((cell) => /^:?-{3,}:?$/.test(cell));
+ if (!isSeparator) {
+ return null;
+ }
+
+ const headers = splitTableRow(header);
+ const rows: string[][] = [];
+ let cursor = index + 2;
+
+ while (cursor < lines.length && lines[cursor].includes("|")) {
+ rows.push(splitTableRow(lines[cursor]));
+ cursor += 1;
+ }
+
+ return {
+ block: { type: "table", headers, rows },
+ nextIndex: cursor,
+ };
+}
+
+const markdownStyles = `
+.tm-md-root { color: var(--foreground); line-height: 1.65; font-size: 1rem; }
+.tm-md-heading { margin: 0.2rem 0 0.35rem; font-weight: 700; line-height: 1.25; }
+.tm-md-h1 { font-size: 1.65rem; }
+.tm-md-h2 { font-size: 1.45rem; }
+.tm-md-h3 { font-size: 1.25rem; }
+.tm-md-h4 { font-size: 1.1rem; }
+.tm-md-h5 { font-size: 1rem; }
+.tm-md-h6 { font-size: 0.95rem; opacity: 0.9; }
+.tm-md-blockquote { margin: 0.45rem 0; padding-left: 0.75rem; opacity: 0.95; }
+.tm-md-blockquote p { margin: 0.2rem 0; }
+.tm-md-pre { margin: 0.45rem 0; padding: 0.65rem 0.75rem; border-radius: 0.5rem; background: var(--muted); overflow-x: auto; }
+.tm-md-code, .tm-md-codeblock { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 0.87em; cursor: pointer; }
+.tm-md-code { padding: 0.08rem 0.32rem; border: 1px solid var(--border); border-radius: 0.28rem; background: var(--muted); }
+.tm-md-codeblock { display: block; }
+.tm-md-code:focus-visible, .tm-md-codeblock:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
+.tm-md-strong { font-weight: 700; }
+.tm-md-em { font-style: italic; }
+.tm-md-del { text-decoration: line-through; }
+.tm-md-link { color: var(--primary); text-decoration: underline; text-underline-offset: 0.14rem; }
+.tm-md-image { display: block; max-width: 100%; border-radius: 0.4rem; margin: 0.5rem 0; }
+.tm-md-emoji { display: inline-block; width: 1.15em; height: 1.15em; vertical-align: -0.18em; }
+.tm-md-ul, .tm-md-ol { margin: 0.3rem 0 0.35rem 1.2rem; padding: 0; }
+.tm-md-li { margin: 0.2rem 0; }
+.tm-md-checkbox { margin-right: 0.5rem; vertical-align: middle; }
+.tm-md-table-wrap { overflow-x: auto; margin: 0.45rem 0; }
+.tm-md-table { border-collapse: collapse; width: 100%; min-width: 16rem; }
+.tm-md-table th, .tm-md-table td { padding: 0.4rem 0.5rem; text-align: left; }
+.tm-md-table th { background: var(--muted); font-weight: 600; }
+.tm-md-hr { margin: 0.55rem 0; }
+
+.cm-editor.tm-md-editor { border-radius: inherit; background: transparent; caret-color: var(--foreground); }
+.cm-editor.tm-md-editor.cm-focused { outline: none; box-shadow: none; }
+.cm-editor.tm-md-editor .cm-scroller { font-family: inherit; line-height: 1.55; max-height: 30vh; overflow-y: auto; overflow-x: hidden; }
+.cm-editor.tm-md-editor .cm-content { caret-color: var(--foreground); }
+.cm-editor.tm-md-editor .cm-content { padding: var(--tm-md-content-padding, 0.25rem 0.625rem); min-height: 2rem; }
+.cm-editor.tm-md-editor .cm-line { padding: 0; color: var(--foreground); }
+.cm-editor.tm-md-editor .tm-md-editor-emoji { display: inline-block; width: 1.15em; height: 1.15em; vertical-align: -0.18em; object-fit: contain; pointer-events: none; }
+.cm-editor.tm-md-editor .tm-md-hidden-token { color: transparent; opacity: 0; font-size: inherit; }
+.cm-editor.tm-md-editor .tm-md-code-line { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; background: var(--muted); border-radius: 0.3rem; }
+.cm-tooltip.cm-tooltip-autocomplete { min-width: 18rem; max-width: min(26rem, calc(100vw - 1rem)); overflow: hidden; border: 1px solid var(--border); border-radius: var(--radius); background: var(--popover); color: var(--popover-foreground); box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); font-family: "Public Sans Variable", sans-serif; font-size: 0.875rem; }
+.cm-editor.tm-md-editor .cm-tooltip.cm-tooltip-autocomplete > ul { max-height: min(20rem, 45vh); padding: 0.25rem; font-family: "Public Sans Variable", sans-serif; scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
+.cm-tooltip.cm-tooltip-autocomplete > ul::-webkit-scrollbar { width: 6px; height: 6px; }
+.cm-tooltip.cm-tooltip-autocomplete > ul::-webkit-scrollbar-track { background: transparent; }
+.cm-tooltip.cm-tooltip-autocomplete > ul::-webkit-scrollbar-thumb { border-radius: 9999px; background: var(--border); }
+.cm-tooltip.cm-tooltip-autocomplete > ul > li { display: flex; min-height: 2.25rem; align-items: center; border-radius: calc(var(--radius) * 0.8); padding: 0.3rem 0.5rem; color: var(--popover-foreground); }
+.cm-tooltip.cm-tooltip-autocomplete > ul > li:hover,
+.cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected] { background: var(--accent); color: var(--accent-foreground); }
+.cm-tooltip.cm-tooltip-autocomplete .cm-completionIcon { display: none; }
+.cm-tooltip.cm-tooltip-autocomplete .cm-completionLabel { overflow: hidden; text-overflow: ellipsis; }
+.cm-tooltip.cm-tooltip-autocomplete .cm-completionMatchedText { color: inherit; text-decoration: none; font-weight: 600; }
+.cm-tooltip-autocomplete .tm-md-completion-emoji { display: inline-block; width: 1.35rem; height: 1.35rem; flex: 0 0 auto; margin-right: 0.5rem; vertical-align: middle; }
+`;
+
+/**
+ * Executes ensureMarkdownStyles.
+ * @param none This function has no parameters.
+ * @returns void.
+ */
+export function ensureMarkdownStyles(): void {
+ if (typeof document === "undefined") return;
+
+ const styleId = "tensamin-markdown-styles";
+ let style = document.getElementById(styleId) as HTMLStyleElement | null;
+
+ if (!style) {
+ style = document.createElement("style");
+ style.id = styleId;
+ document.head.appendChild(style);
+ }
+
+ if (style.textContent !== markdownStyles) {
+ style.textContent = markdownStyles;
+ }
+}
diff --git a/packages/markdown/src/text.tsx b/packages/markdown/src/text.tsx
new file mode 100644
index 0000000..ca65fe0
--- /dev/null
+++ b/packages/markdown/src/text.tsx
@@ -0,0 +1,30 @@
+import { useMemo, type CSSProperties } from "react";
+
+import {
+ ensureMarkdownStyles,
+ parseMarkdownBlocks,
+ renderBlocks,
+} from "./markdown";
+
+export type TextProps = {
+ value: string;
+ fontSize?: CSSProperties["fontSize"];
+};
+
+/**
+ * Executes Text.
+ * @param props Parameter props.
+ * @returns unknown.
+ */
+export default function Text(props: TextProps) {
+ ensureMarkdownStyles();
+
+ const blocks = useMemo(() => parseMarkdownBlocks(props.value), [props.value]);
+ const renderedBlocks = useMemo(() => renderBlocks(blocks), [blocks]);
+
+ return (
+
+ {renderedBlocks}
+
+ );
+}
diff --git a/packages/markdown/todo.md b/packages/markdown/todo.md
new file mode 100644
index 0000000..b59da98
--- /dev/null
+++ b/packages/markdown/todo.md
@@ -0,0 +1 @@
+- Improve the Input box
diff --git a/packages/markdown/tsconfig.json b/packages/markdown/tsconfig.json
new file mode 100644
index 0000000..9714625
--- /dev/null
+++ b/packages/markdown/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "jsx": "react-jsx",
+ "strict": true,
+ "skipLibCheck": true,
+ "noEmit": true
+ },
+ "include": ["src"]
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0cbdd6e..107149b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -459,6 +459,45 @@ importers:
specifier: ^8.2.1
version: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.50.0)(yaml@2.9.0)
+ packages/markdown:
+ dependencies:
+ '@codemirror/autocomplete':
+ specifier: ^6.20.3
+ version: 6.20.3
+ '@codemirror/commands':
+ specifier: ^6.10.4
+ version: 6.11.0
+ '@codemirror/lang-markdown':
+ specifier: ^6.5.2
+ version: 6.5.2
+ '@codemirror/language':
+ specifier: ^6.12.4
+ version: 6.12.4
+ '@codemirror/state':
+ specifier: ^6.7.1
+ version: 6.7.1
+ '@codemirror/view':
+ specifier: ^6.43.8
+ version: 6.43.9
+ '@methanium/ui':
+ specifier: https://git.methanium.net/methanium/ui/releases/download/0.0.29/methanium-ui.tgz
+ version: https://git.methanium.net/methanium/ui/releases/download/0.0.29/methanium-ui.tgz(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(emojibase@17.0.0)(react-dom@19.2.8(react@19.2.8))(react-is@19.2.8)(react@19.2.8)(redux@5.0.1)(supports-color@7.2.0)(typescript@6.0.3)
+ '@twemoji/api':
+ specifier: ^17.0.3
+ version: 17.0.3
+ emojibase-data:
+ specifier: ^17.0.0
+ version: 17.0.0(emojibase@17.0.0)
+ lucide-react:
+ specifier: ^1.30.0
+ version: 1.32.0(react@19.2.8)
+ react:
+ specifier: ^19.2.8
+ version: 19.2.8
+ react-dom:
+ specifier: ^19.2.8
+ version: 19.2.8(react@19.2.8)
+
packages/mtp:
dependencies:
'@methanium/ui':
@@ -2540,6 +2579,12 @@ packages:
'@ts-morph/common@0.27.0':
resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==}
+ '@twemoji/api@17.0.3':
+ resolution: {integrity: sha512-iwERjxY0QgPGVwT6b1OKG0Oa9nIfHhJw+Ij1TapTBMKTvVCU6qdXPXX/XKwxKx5QZIJW5GwELUCtw8wlaIQ2ug==}
+
+ '@twemoji/parser@17.0.2':
+ resolution: {integrity: sha512-X/P7pHsGOxnrupQYUVetIeuxBGgffFu8CLwoPMMjH9CWmQvlXiCpbTW/BXxMOCWXQojgHdmgdvm6IsCqAQ5nxA==}
+
'@types/cacheable-request@6.0.3':
resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==}
@@ -4528,6 +4573,9 @@ packages:
jsonfile@4.0.0:
resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
+ jsonfile@5.0.0:
+ resolution: {integrity: sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==}
+
jsonfile@6.2.1:
resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==}
@@ -8431,6 +8479,15 @@ snapshots:
minimatch: 10.2.6
path-browserify: 1.0.1
+ '@twemoji/api@17.0.3':
+ dependencies:
+ '@twemoji/parser': 17.0.2
+ fs-extra: 8.1.0
+ jsonfile: 5.0.0
+ universalify: 0.1.2
+
+ '@twemoji/parser@17.0.2': {}
+
'@types/cacheable-request@6.0.3':
dependencies:
'@types/http-cache-semantics': 4.2.0
@@ -10619,6 +10676,12 @@ snapshots:
optionalDependencies:
graceful-fs: 4.2.11
+ jsonfile@5.0.0:
+ dependencies:
+ universalify: 0.1.2
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
jsonfile@6.2.1:
dependencies:
universalify: 2.0.1