@shapesos/clay 0.8.1 → 0.9.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/dist/index.d.cts CHANGED
@@ -5,7 +5,7 @@ export { IconCtrl, KEYBOARD_SHORTCUT_VARIANTS, KeyIcon, KeyboardShortcut, Keyboa
5
5
  export { Lottie, LottieAnimationData, LottieProps, LottieRef } from './lottie.cjs';
6
6
  export { TextArea, TextAreaProps } from './text-area.cjs';
7
7
  export { MarkdownTable, convertTableToCSV, markdownToPlainText, mergeRefs, parseMarkdownTable } from './utils.cjs';
8
- export { BUTTON_SIZE, BUTTON_VARIANT, Button, ButtonProps, ButtonSize, ButtonVariant, ICON_POSITION, IconPosition } from './button.cjs';
8
+ export { BUTTON_INTENT, BUTTON_SIZE, BUTTON_VARIANT, Button, ButtonIntent, ButtonProps, ButtonSize, ButtonVariant, ICON_POSITION, IconPosition } from './button.cjs';
9
9
  import 'react/jsx-runtime';
10
10
  import 'react';
11
11
  import 'lottie-web';
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ export { IconCtrl, KEYBOARD_SHORTCUT_VARIANTS, KeyIcon, KeyboardShortcut, Keyboa
5
5
  export { Lottie, LottieAnimationData, LottieProps, LottieRef } from './lottie.js';
6
6
  export { TextArea, TextAreaProps } from './text-area.js';
7
7
  export { MarkdownTable, convertTableToCSV, markdownToPlainText, mergeRefs, parseMarkdownTable } from './utils.js';
8
- export { BUTTON_SIZE, BUTTON_VARIANT, Button, ButtonProps, ButtonSize, ButtonVariant, ICON_POSITION, IconPosition } from './button.js';
8
+ export { BUTTON_INTENT, BUTTON_SIZE, BUTTON_VARIANT, Button, ButtonIntent, ButtonProps, ButtonSize, ButtonVariant, ICON_POSITION, IconPosition } from './button.js';
9
9
  import 'react/jsx-runtime';
10
10
  import 'react';
11
11
  import 'lottie-web';
package/dist/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import {
2
+ BUTTON_INTENT,
2
3
  BUTTON_SIZE,
3
4
  BUTTON_VARIANT,
4
5
  Button,
5
6
  ICON_POSITION
6
- } from "./chunk-FFABDVB3.js";
7
+ } from "./chunk-EDICS62G.js";
7
8
  import "./chunk-C77QMQNT.js";
8
9
  import {
9
10
  ChatContext,
@@ -11,7 +12,7 @@ import {
11
12
  MESSAGE_ROLE,
12
13
  useChatContext,
13
14
  useCopyToClipboard
14
- } from "./chunk-2EZFFUCL.js";
15
+ } from "./chunk-A77BGJH4.js";
15
16
  import "./chunk-72TJUKMV.js";
16
17
  import {
17
18
  IconButton
@@ -42,15 +43,16 @@ import {
42
43
  import {
43
44
  convertTableToCSV,
44
45
  parseMarkdownTable
45
- } from "./chunk-N4FMZU56.js";
46
+ } from "./chunk-27GJUWVN.js";
46
47
  import {
47
48
  markdownToPlainText
48
- } from "./chunk-QUJYTIGD.js";
49
+ } from "./chunk-NMKKU2UG.js";
49
50
  import {
50
51
  mergeRefs
51
52
  } from "./chunk-KNYB3RL7.js";
52
53
  import "./chunk-5WRI5ZAA.js";
53
54
  export {
55
+ BUTTON_INTENT,
54
56
  BUTTON_SIZE,
55
57
  BUTTON_VARIANT,
56
58
  Button,
package/dist/utils.cjs CHANGED
@@ -31,13 +31,17 @@ module.exports = __toCommonJS(utils_exports);
31
31
  var PLACEHOLDER_PREFIX = "\u200B\u200BIC";
32
32
  var PLACEHOLDER_SUFFIX = "\u200B\u200B";
33
33
  var PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\d+)${PLACEHOLDER_SUFFIX}`, "g");
34
+ function stripInlineMarks(text) {
35
+ return text.replace(/\*{3}(.+?)\*{3}/g, "$1").replace(/\*{2}(.+?)\*{2}/g, "$1").replace(/_{2}(.+?)_{2}/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/_(.+?)_/g, "$1").replace(/~~(.+?)~~/g, "$1");
36
+ }
34
37
  function markdownToPlainText(markdown) {
35
38
  if (!markdown) return "";
36
39
  const inlineCodeSnippets = [];
37
40
  let result = markdown.replace(/^<!--[\s\S]*?--!?>\n?/gm, "").replace(/```[\s\S]*?```/g, "").replace(/`([^`]+)`/g, (_match, code) => {
38
41
  inlineCodeSnippets.push(code);
39
42
  return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;
40
- }).replace(/^#{1,6}\s+/gm, "").replace(/^(?:---|\*\*\*|___)\s*$/gm, "").replace(/\*{3}(.+?)\*{3}/g, "$1").replace(/\*{2}(.+?)\*{2}/g, "$1").replace(/_{2}(.+?)_{2}/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/_(.+?)_/g, "$1").replace(/~~(.+?)~~/g, "$1").replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/^>\s?/gm, "").replace(/^\|[-:\s|]+\|\s*$/gm, "").replace(/^\|\s?/gm, "").replace(/\s?\|$/gm, "").replace(/\n{3,}/g, "\n\n").trim();
43
+ }).replace(/^#{1,6}\s+/gm, "").replace(/^(?:---|\*\*\*|___)\s*$/gm, "");
44
+ result = stripInlineMarks(result).replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/^>\s?/gm, "").replace(/^\|[-:\s|]+\|\s*$/gm, "").replace(/^\|\s?/gm, "").replace(/\s?\|$/gm, "").replace(/\n{3,}/g, "\n\n").trim();
41
45
  result = result.replace(PLACEHOLDER_REGEX, (_match, index) => inlineCodeSnippets[Number(index)]);
42
46
  return result;
43
47
  }
@@ -88,7 +92,7 @@ function parseMarkdownTable(markdown, tableIndex = 0) {
88
92
  continue;
89
93
  }
90
94
  if (tableCount === tableIndex) {
91
- const columns = parseCells(headerLine);
95
+ const columns = parseCells(headerLine).map(stripInlineMarks);
92
96
  const rows = [];
93
97
  const title = i > 0 ? extractTableTitle(lines[i - 1]) : void 0;
94
98
  for (let j = i + 2; j < lines.length; j++) {
@@ -99,7 +103,7 @@ function parseMarkdownTable(markdown, tableIndex = 0) {
99
103
  if (trimmedLine === "") {
100
104
  break;
101
105
  }
102
- rows.push(parseCells(lines[j]));
106
+ rows.push(parseCells(lines[j]).map(stripInlineMarks));
103
107
  }
104
108
  return { columns, rows, ...title ? { title } : {} };
105
109
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/index.ts","../src/utils/markdown-to-plain-text.ts","../src/utils/merge-refs.ts","../src/utils/parse-markdown-table.ts","../src/utils/convert-table-to-csv.ts"],"sourcesContent":["export { markdownToPlainText } from \"./markdown-to-plain-text\";\nexport { mergeRefs } from \"./merge-refs\";\nexport { parseMarkdownTable } from \"./parse-markdown-table\";\nexport type { MarkdownTable } from \"./parse-markdown-table\";\nexport { convertTableToCSV } from \"./convert-table-to-csv\";\n","const PLACEHOLDER_PREFIX = \"\\u200B\\u200BIC\";\nconst PLACEHOLDER_SUFFIX = \"\\u200B\\u200B\";\nconst PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\\\d+)${PLACEHOLDER_SUFFIX}`, \"g\");\n\n/**\n * Converts a Markdown string to plain text by stripping all formatting syntax.\n *\n * Handles headings, bold/italic/strikethrough, links, images, blockquotes, code blocks,\n * inline code, tables, and horizontal rules. Inline code content is preserved literally\n * (formatting inside backticks is not stripped).\n *\n * @param markdown - The Markdown source string.\n * @returns A plain-text string with all Markdown syntax removed.\n */\nexport function markdownToPlainText(markdown: string): string {\n if (!markdown) return \"\";\n\n // Protect inline code content from bold/italic stripping by replacing with placeholders\n const inlineCodeSnippets: string[] = [];\n let result = markdown\n // HTML comments (e.g. <!-- table-title: ... -->), including multiline and --!> variants\n .replace(/^<!--[\\s\\S]*?--!?>\\n?/gm, \"\")\n // Code blocks (must come first)\n .replace(/```[\\s\\S]*?```/g, \"\")\n // Inline code → placeholder\n .replace(/`([^`]+)`/g, (_match, code: string) => {\n inlineCodeSnippets.push(code);\n return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;\n })\n // Remove headings markers, keep text\n .replace(/^#{1,6}\\s+/gm, \"\")\n // Remove horizontal rules\n .replace(/^(?:---|\\*\\*\\*|___)\\s*$/gm, \"\")\n // Bold + italic\n .replace(/\\*{3}(.+?)\\*{3}/g, \"$1\")\n // Bold\n .replace(/\\*{2}(.+?)\\*{2}/g, \"$1\")\n .replace(/_{2}(.+?)_{2}/g, \"$1\")\n // Italic\n .replace(/\\*(.+?)\\*/g, \"$1\")\n .replace(/_(.+?)_/g, \"$1\")\n // Strikethrough\n .replace(/~~(.+?)~~/g, \"$1\")\n // Images: ![alt](url) → alt (must come before links)\n .replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, \"$1\")\n // Links: [text](url) → text\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\n // Blockquote markers\n .replace(/^>\\s?/gm, \"\")\n // Table separator rows (|---|---|)\n .replace(/^\\|[-:\\s|]+\\|\\s*$/gm, \"\")\n // Table rows: | a | b | → a | b\n .replace(/^\\|\\s?/gm, \"\")\n .replace(/\\s?\\|$/gm, \"\")\n // Collapse multiple blank lines\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n\n // Restore inline code content\n result = result.replace(PLACEHOLDER_REGEX, (_match, index: string) => inlineCodeSnippets[Number(index)]);\n\n return result;\n}\n","import type { Ref } from \"react\";\n\n/**\n * Combines multiple React refs into a single callback ref.\n * Useful when a component needs to forward its ref while also keeping a local ref.\n *\n * @param refs - Any number of React refs (callback refs, RefObjects, or `undefined`).\n * @returns A callback ref that updates all provided refs when the DOM node changes.\n */\nexport function mergeRefs<T>(...refs: (Ref<T> | undefined)[]): (node: T | null) => void {\n return (node) => {\n for (const ref of refs) {\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n (ref as React.MutableRefObject<T | null>).current = node;\n }\n }\n };\n}\n","/** Structured representation of a parsed markdown table. */\nexport interface MarkdownTable {\n /** Column header labels. */\n columns: string[];\n /** Data rows, each an array of cell values matching the column order. */\n rows: string[][];\n /** Optional title extracted from a `<!-- table-title: ... -->` comment above the table. */\n title?: string;\n}\n\n/**\n * Parses cells from a markdown table row, trimming whitespace and stripping leading/trailing pipes.\n * @param line - A single line of a markdown table.\n * @returns An array of trimmed cell values.\n */\nfunction parseCells(line: string): string[] {\n const trimmed = line.trim();\n const stripped = trimmed.startsWith(\"|\") ? trimmed.slice(1) : trimmed;\n const withoutTrailing = stripped.endsWith(\"|\") ? stripped.slice(0, -1) : stripped;\n return withoutTrailing.split(\"|\").map((cell) => cell.trim());\n}\n\n/**\n * Tests whether a line is a GFM table separator row (e.g. `| --- | --- |`).\n * @param line - A single line to test.\n * @returns `true` if the line matches the separator pattern.\n */\nfunction isSeparatorRow(line: string): boolean {\n const cells = parseCells(line);\n return cells.length > 0 && cells.every((cell) => /^:?-{1,}:?$/.test(cell));\n}\n\n/**\n * Tests whether a line looks like a markdown table row (contains at least one pipe).\n * @param line - A single line to test.\n * @returns `true` if the line contains a pipe character.\n */\nfunction isTableRow(line: string): boolean {\n return line.includes(\"|\");\n}\n\nconst TABLE_TITLE_PREFIX = \"<!-- table-title:\";\nconst TABLE_TITLE_SUFFIX = \"-->\";\n\n/**\n * Extracts a table title from a `<!-- table-title: ... -->` HTML comment.\n * Uses string methods instead of regex to avoid ReDoS on untrusted input.\n * @param line - A single line to test.\n * @returns The title string, or `undefined` if the line is not a title comment.\n */\nfunction extractTableTitle(line: string): string | undefined {\n const trimmed = line.trim();\n if (!trimmed.startsWith(TABLE_TITLE_PREFIX) || !trimmed.endsWith(TABLE_TITLE_SUFFIX)) {\n return undefined;\n }\n return trimmed.slice(TABLE_TITLE_PREFIX.length, -TABLE_TITLE_SUFFIX.length).trim() || undefined;\n}\n\n/**\n * Extracts the Nth GFM markdown table from a string.\n * @param markdown - The markdown content to parse.\n * @param tableIndex - Zero-based index of the table to extract.\n * @returns The parsed table, or `null` if no table exists at the given index.\n */\nexport function parseMarkdownTable(markdown: string, tableIndex = 0): MarkdownTable | null {\n const lines = markdown.split(\"\\n\");\n let tableCount = 0;\n\n for (let i = 0; i < lines.length - 1; i++) {\n const headerLine = lines[i];\n const separatorLine = lines[i + 1];\n\n if (!isTableRow(headerLine) || !isSeparatorRow(separatorLine)) {\n continue;\n }\n\n if (tableCount === tableIndex) {\n const columns = parseCells(headerLine);\n const rows: string[][] = [];\n const title = i > 0 ? extractTableTitle(lines[i - 1]) : undefined;\n\n for (let j = i + 2; j < lines.length; j++) {\n if (!isTableRow(lines[j]) || isSeparatorRow(lines[j])) {\n break;\n }\n const trimmedLine = lines[j].trim();\n if (trimmedLine === \"\") {\n break;\n }\n rows.push(parseCells(lines[j]));\n }\n\n return { columns, rows, ...(title ? { title } : {}) };\n }\n\n tableCount++;\n }\n\n return null;\n}\n","import type { MarkdownTable } from \"./parse-markdown-table\";\n\n/**\n * Escapes and optionally quotes a CSV field per RFC 4180.\n * Fields containing commas, double quotes, or newlines are wrapped in double quotes.\n * Existing double quotes are escaped by doubling them.\n * @param field - The raw field value.\n * @returns The escaped/quoted field string.\n */\nfunction escapeField(field: string): string {\n if (field.includes(\",\") || field.includes('\"') || field.includes(\"\\n\") || field.includes(\"\\r\")) {\n return `\"${field.replace(/\"/g, '\"\"')}\"`;\n }\n return field;\n}\n\n/**\n * Converts a parsed markdown table to a CSV string (RFC 4180).\n * Fields containing commas, double quotes, or newlines are quoted.\n * @param table - The structured table data to convert.\n * @returns A CSV-formatted string with CRLF line endings.\n */\nexport function convertTableToCSV(table: MarkdownTable): string {\n const headerRow = table.columns.map(escapeField).join(\",\");\n const dataRows = table.rows.map((row) => row.map(escapeField).join(\",\"));\n return [headerRow, ...dataRows].join(\"\\r\\n\");\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/utils/index.ts","../src/utils/markdown-to-plain-text.ts","../src/utils/merge-refs.ts","../src/utils/parse-markdown-table.ts","../src/utils/convert-table-to-csv.ts"],"sourcesContent":["export { markdownToPlainText } from \"./markdown-to-plain-text\";\nexport { mergeRefs } from \"./merge-refs\";\nexport { parseMarkdownTable } from \"./parse-markdown-table\";\nexport type { MarkdownTable } from \"./parse-markdown-table\";\nexport { convertTableToCSV } from \"./convert-table-to-csv\";\n","const PLACEHOLDER_PREFIX = \"\\u200B\\u200BIC\";\nconst PLACEHOLDER_SUFFIX = \"\\u200B\\u200B\";\nconst PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\\\d+)${PLACEHOLDER_SUFFIX}`, \"g\");\n\n/**\n * Strips only inline markdown marks (bold, italic, strikethrough) from a string.\n * Does not touch block-level syntax (headings, blockquotes, horizontal rules, links, images)\n * so values like `> 100`, `# items`, or `---` are preserved as-is.\n * @param text - The text to strip inline marks from.\n * @returns The text with inline marks removed.\n */\nexport function stripInlineMarks(text: string): string {\n return (\n text\n // Bold + italic\n .replace(/\\*{3}(.+?)\\*{3}/g, \"$1\")\n // Bold\n .replace(/\\*{2}(.+?)\\*{2}/g, \"$1\")\n .replace(/_{2}(.+?)_{2}/g, \"$1\")\n // Italic\n .replace(/\\*(.+?)\\*/g, \"$1\")\n .replace(/_(.+?)_/g, \"$1\")\n // Strikethrough\n .replace(/~~(.+?)~~/g, \"$1\")\n );\n}\n\n/**\n * Converts a Markdown string to plain text by stripping all formatting syntax.\n *\n * Handles headings, bold/italic/strikethrough, links, images, blockquotes, code blocks,\n * inline code, tables, and horizontal rules. Inline code content is preserved literally\n * (formatting inside backticks is not stripped).\n *\n * @param markdown - The Markdown source string.\n * @returns A plain-text string with all Markdown syntax removed.\n */\nexport function markdownToPlainText(markdown: string): string {\n if (!markdown) return \"\";\n\n // Protect inline code content from bold/italic stripping by replacing with placeholders\n const inlineCodeSnippets: string[] = [];\n let result = markdown\n // HTML comments (e.g. <!-- table-title: ... -->), including multiline and --!> variants\n .replace(/^<!--[\\s\\S]*?--!?>\\n?/gm, \"\")\n // Code blocks (must come first)\n .replace(/```[\\s\\S]*?```/g, \"\")\n // Inline code → placeholder\n .replace(/`([^`]+)`/g, (_match, code: string) => {\n inlineCodeSnippets.push(code);\n return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;\n })\n // Remove headings markers, keep text\n .replace(/^#{1,6}\\s+/gm, \"\")\n // Remove horizontal rules\n .replace(/^(?:---|\\*\\*\\*|___)\\s*$/gm, \"\");\n result = stripInlineMarks(result)\n // Images: ![alt](url) → alt (must come before links)\n .replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, \"$1\")\n // Links: [text](url) → text\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\n // Blockquote markers\n .replace(/^>\\s?/gm, \"\")\n // Table separator rows (|---|---|)\n .replace(/^\\|[-:\\s|]+\\|\\s*$/gm, \"\")\n // Table rows: | a | b | → a | b\n .replace(/^\\|\\s?/gm, \"\")\n .replace(/\\s?\\|$/gm, \"\")\n // Collapse multiple blank lines\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n\n // Restore inline code content\n result = result.replace(PLACEHOLDER_REGEX, (_match, index: string) => inlineCodeSnippets[Number(index)]);\n\n return result;\n}\n","import type { Ref } from \"react\";\n\n/**\n * Combines multiple React refs into a single callback ref.\n * Useful when a component needs to forward its ref while also keeping a local ref.\n *\n * @param refs - Any number of React refs (callback refs, RefObjects, or `undefined`).\n * @returns A callback ref that updates all provided refs when the DOM node changes.\n */\nexport function mergeRefs<T>(...refs: (Ref<T> | undefined)[]): (node: T | null) => void {\n return (node) => {\n for (const ref of refs) {\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n (ref as React.MutableRefObject<T | null>).current = node;\n }\n }\n };\n}\n","import { stripInlineMarks } from \"./markdown-to-plain-text\";\n\n/** Structured representation of a parsed markdown table. */\nexport interface MarkdownTable {\n /** Column header labels. */\n columns: string[];\n /** Data rows, each an array of cell values matching the column order. */\n rows: string[][];\n /** Optional title extracted from a `<!-- table-title: ... -->` comment above the table. */\n title?: string;\n}\n\n/**\n * Parses cells from a markdown table row, trimming whitespace and stripping leading/trailing pipes.\n * @param line - A single line of a markdown table.\n * @returns An array of trimmed cell values.\n */\nfunction parseCells(line: string): string[] {\n const trimmed = line.trim();\n const stripped = trimmed.startsWith(\"|\") ? trimmed.slice(1) : trimmed;\n const withoutTrailing = stripped.endsWith(\"|\") ? stripped.slice(0, -1) : stripped;\n return withoutTrailing.split(\"|\").map((cell) => cell.trim());\n}\n\n/**\n * Tests whether a line is a GFM table separator row (e.g. `| --- | --- |`).\n * @param line - A single line to test.\n * @returns `true` if the line matches the separator pattern.\n */\nfunction isSeparatorRow(line: string): boolean {\n const cells = parseCells(line);\n return cells.length > 0 && cells.every((cell) => /^:?-{1,}:?$/.test(cell));\n}\n\n/**\n * Tests whether a line looks like a markdown table row (contains at least one pipe).\n * @param line - A single line to test.\n * @returns `true` if the line contains a pipe character.\n */\nfunction isTableRow(line: string): boolean {\n return line.includes(\"|\");\n}\n\nconst TABLE_TITLE_PREFIX = \"<!-- table-title:\";\nconst TABLE_TITLE_SUFFIX = \"-->\";\n\n/**\n * Extracts a table title from a `<!-- table-title: ... -->` HTML comment.\n * Uses string methods instead of regex to avoid ReDoS on untrusted input.\n * @param line - A single line to test.\n * @returns The title string, or `undefined` if the line is not a title comment.\n */\nfunction extractTableTitle(line: string): string | undefined {\n const trimmed = line.trim();\n if (!trimmed.startsWith(TABLE_TITLE_PREFIX) || !trimmed.endsWith(TABLE_TITLE_SUFFIX)) {\n return undefined;\n }\n return trimmed.slice(TABLE_TITLE_PREFIX.length, -TABLE_TITLE_SUFFIX.length).trim() || undefined;\n}\n\n/**\n * Extracts the Nth GFM markdown table from a string.\n * @param markdown - The markdown content to parse.\n * @param tableIndex - Zero-based index of the table to extract.\n * @returns The parsed table, or `null` if no table exists at the given index.\n */\nexport function parseMarkdownTable(markdown: string, tableIndex = 0): MarkdownTable | null {\n const lines = markdown.split(\"\\n\");\n let tableCount = 0;\n\n for (let i = 0; i < lines.length - 1; i++) {\n const headerLine = lines[i];\n const separatorLine = lines[i + 1];\n\n if (!isTableRow(headerLine) || !isSeparatorRow(separatorLine)) {\n continue;\n }\n\n if (tableCount === tableIndex) {\n const columns = parseCells(headerLine).map(stripInlineMarks);\n const rows: string[][] = [];\n const title = i > 0 ? extractTableTitle(lines[i - 1]) : undefined;\n\n for (let j = i + 2; j < lines.length; j++) {\n if (!isTableRow(lines[j]) || isSeparatorRow(lines[j])) {\n break;\n }\n const trimmedLine = lines[j].trim();\n if (trimmedLine === \"\") {\n break;\n }\n rows.push(parseCells(lines[j]).map(stripInlineMarks));\n }\n\n return { columns, rows, ...(title ? { title } : {}) };\n }\n\n tableCount++;\n }\n\n return null;\n}\n","import type { MarkdownTable } from \"./parse-markdown-table\";\n\n/**\n * Escapes and optionally quotes a CSV field per RFC 4180.\n * Fields containing commas, double quotes, or newlines are wrapped in double quotes.\n * Existing double quotes are escaped by doubling them.\n * @param field - The raw field value.\n * @returns The escaped/quoted field string.\n */\nfunction escapeField(field: string): string {\n if (field.includes(\",\") || field.includes('\"') || field.includes(\"\\n\") || field.includes(\"\\r\")) {\n return `\"${field.replace(/\"/g, '\"\"')}\"`;\n }\n return field;\n}\n\n/**\n * Converts a parsed markdown table to a CSV string (RFC 4180).\n * Fields containing commas, double quotes, or newlines are quoted.\n * @param table - The structured table data to convert.\n * @returns A CSV-formatted string with CRLF line endings.\n */\nexport function convertTableToCSV(table: MarkdownTable): string {\n const headerRow = table.columns.map(escapeField).join(\",\");\n const dataRows = table.rows.map((row) => row.map(escapeField).join(\",\"));\n return [headerRow, ...dataRows].join(\"\\r\\n\");\n}\n"],"mappings":"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","names":[]}
package/dist/utils.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  convertTableToCSV,
3
3
  parseMarkdownTable
4
- } from "./chunk-N4FMZU56.js";
4
+ } from "./chunk-27GJUWVN.js";
5
5
  import {
6
6
  markdownToPlainText
7
- } from "./chunk-QUJYTIGD.js";
7
+ } from "./chunk-NMKKU2UG.js";
8
8
  import {
9
9
  mergeRefs
10
10
  } from "./chunk-KNYB3RL7.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapesos/clay",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "main": "./dist/index.cjs",
5
5
  "module": "./dist/index.js",
6
6
  "devDependencies": {
@@ -1,149 +0,0 @@
1
- import {
2
- typographyMixin,
3
- typographyTypes
4
- } from "./chunk-SV24ONND.js";
5
- import {
6
- Icon
7
- } from "./chunk-OLJIJYB5.js";
8
- import {
9
- colors
10
- } from "./chunk-JF3P66JF.js";
11
-
12
- // src/components/button/button.tsx
13
- import { forwardRef } from "react";
14
-
15
- // src/components/button/button-styles.ts
16
- import styled, { css } from "styled-components";
17
- var sizeStyles = {
18
- xs: css`
19
- padding: 6px 10px;
20
- border-radius: 8px;
21
- gap: 4px;
22
- `,
23
- s: css`
24
- padding: 8px 12px;
25
- border-radius: 10px;
26
- gap: 6px;
27
- `,
28
- m: css`
29
- padding: 10px 14px;
30
- border-radius: 10px;
31
- gap: 6px;
32
- `
33
- };
34
- var variantStyles = {
35
- solid: css`
36
- background: ${colors["brown-100"]};
37
- color: ${colors.white};
38
- border: none;
39
-
40
- &:hover:not(:disabled) {
41
- background: ${colors["brown-90"]};
42
- }
43
-
44
- &:disabled {
45
- background: ${colors["brown-20"]};
46
- color: ${colors["brown-50"]};
47
- }
48
- `,
49
- outline: css`
50
- background: ${colors.white};
51
- color: ${colors["brown-100"]};
52
- border: 1px solid ${colors["brown-40"]};
53
-
54
- &:hover:not(:disabled) {
55
- background: ${colors["brown-20"]};
56
- }
57
-
58
- &:disabled {
59
- border-color: ${colors["brown-20"]};
60
- color: ${colors["brown-50"]};
61
- }
62
- `,
63
- ghost: css`
64
- background: transparent;
65
- color: ${colors["brown-100"]};
66
- border: none;
67
-
68
- &:hover:not(:disabled) {
69
- background: ${colors["brown-20"]};
70
- }
71
-
72
- &:disabled {
73
- color: ${colors["brown-50"]};
74
- }
75
- `
76
- };
77
- var StyledButton = styled.button`
78
- display: inline-flex;
79
- align-items: center;
80
- justify-content: center;
81
- cursor: pointer;
82
- ${typographyMixin(typographyTypes.GEIST_BODY_XS_MEDIUM)};
83
- transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease;
84
-
85
- &:disabled {
86
- cursor: not-allowed;
87
- }
88
-
89
- ${({ $size }) => sizeStyles[$size]}
90
- ${({ $variant }) => variantStyles[$variant]}
91
- `;
92
-
93
- // src/components/button/constants.ts
94
- var BUTTON_VARIANT = {
95
- SOLID: "solid",
96
- OUTLINE: "outline",
97
- GHOST: "ghost"
98
- };
99
- var BUTTON_SIZE = {
100
- XS: "xs",
101
- S: "s",
102
- M: "m"
103
- };
104
- var ICON_POSITION = {
105
- LEADING: "leading",
106
- TRAILING: "trailing"
107
- };
108
-
109
- // src/components/button/button.tsx
110
- import { jsx, jsxs } from "react/jsx-runtime";
111
- var Button = forwardRef(function Button2({
112
- children,
113
- variant = BUTTON_VARIANT.SOLID,
114
- size = BUTTON_SIZE.M,
115
- icon,
116
- iconPosition = ICON_POSITION.LEADING,
117
- disabled = false,
118
- onClick,
119
- className,
120
- "aria-label": ariaLabel
121
- }, ref) {
122
- const iconElement = icon ? /* @__PURE__ */ jsx(Icon, { icon, size: 16 }) : null;
123
- return /* @__PURE__ */ jsxs(
124
- StyledButton,
125
- {
126
- ref,
127
- type: "button",
128
- $variant: variant,
129
- $size: size,
130
- disabled,
131
- onClick: disabled ? void 0 : onClick,
132
- className,
133
- "aria-label": ariaLabel,
134
- children: [
135
- iconPosition === ICON_POSITION.LEADING && iconElement,
136
- /* @__PURE__ */ jsx("span", { children }),
137
- iconPosition === ICON_POSITION.TRAILING && iconElement
138
- ]
139
- }
140
- );
141
- });
142
-
143
- export {
144
- BUTTON_VARIANT,
145
- BUTTON_SIZE,
146
- ICON_POSITION,
147
- Button
148
- };
149
- //# sourceMappingURL=chunk-FFABDVB3.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/button/button.tsx","../src/components/button/button-styles.ts","../src/components/button/constants.ts"],"sourcesContent":["import { forwardRef } from \"react\";\nimport { Icon } from \"../icon/icon\";\nimport { StyledButton } from \"./button-styles\";\nimport { BUTTON_SIZE, BUTTON_VARIANT, ICON_POSITION } from \"./constants\";\nimport type { ButtonProps } from \"./types\";\n\n/** A versatile button component with multiple variants and sizes. */\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n {\n children,\n variant = BUTTON_VARIANT.SOLID,\n size = BUTTON_SIZE.M,\n icon,\n iconPosition = ICON_POSITION.LEADING,\n disabled = false,\n onClick,\n className,\n \"aria-label\": ariaLabel,\n },\n ref\n) {\n const iconElement = icon ? <Icon icon={icon} size={16} /> : null;\n\n return (\n <StyledButton\n ref={ref}\n type=\"button\"\n $variant={variant}\n $size={size}\n disabled={disabled}\n onClick={disabled ? undefined : onClick}\n className={className}\n aria-label={ariaLabel}\n >\n {iconPosition === ICON_POSITION.LEADING && iconElement}\n <span>{children}</span>\n {iconPosition === ICON_POSITION.TRAILING && iconElement}\n </StyledButton>\n );\n});\n","import styled, { css } from \"styled-components\";\nimport { colors } from \"../../tokens/colors\";\nimport { typographyMixin, typographyTypes } from \"../../tokens/typography\";\nimport type { ButtonSize, ButtonVariant } from \"./types\";\n\nconst sizeStyles: Record<ButtonSize, ReturnType<typeof css>> = {\n xs: css`\n padding: 6px 10px;\n border-radius: 8px;\n gap: 4px;\n `,\n s: css`\n padding: 8px 12px;\n border-radius: 10px;\n gap: 6px;\n `,\n m: css`\n padding: 10px 14px;\n border-radius: 10px;\n gap: 6px;\n `,\n};\n\nconst variantStyles: Record<ButtonVariant, ReturnType<typeof css>> = {\n solid: css`\n background: ${colors[\"brown-100\"]};\n color: ${colors.white};\n border: none;\n\n &:hover:not(:disabled) {\n background: ${colors[\"brown-90\"]};\n }\n\n &:disabled {\n background: ${colors[\"brown-20\"]};\n color: ${colors[\"brown-50\"]};\n }\n `,\n outline: css`\n background: ${colors.white};\n color: ${colors[\"brown-100\"]};\n border: 1px solid ${colors[\"brown-40\"]};\n\n &:hover:not(:disabled) {\n background: ${colors[\"brown-20\"]};\n }\n\n &:disabled {\n border-color: ${colors[\"brown-20\"]};\n color: ${colors[\"brown-50\"]};\n }\n `,\n ghost: css`\n background: transparent;\n color: ${colors[\"brown-100\"]};\n border: none;\n\n &:hover:not(:disabled) {\n background: ${colors[\"brown-20\"]};\n }\n\n &:disabled {\n color: ${colors[\"brown-50\"]};\n }\n `,\n};\n\n/** Styled button element with variant and size transient props. */\nexport const StyledButton = styled.button<{ $variant: ButtonVariant; $size: ButtonSize }>`\n display: inline-flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n ${typographyMixin(typographyTypes.GEIST_BODY_XS_MEDIUM)};\n transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease;\n\n &:disabled {\n cursor: not-allowed;\n }\n\n ${({ $size }) => sizeStyles[$size]}\n ${({ $variant }) => variantStyles[$variant]}\n`;\n","/** Available visual style variants for the Button component. */\nexport const BUTTON_VARIANT = {\n SOLID: \"solid\",\n OUTLINE: \"outline\",\n GHOST: \"ghost\",\n} as const;\n\n/** Available size presets for the Button component. */\nexport const BUTTON_SIZE = {\n XS: \"xs\",\n S: \"s\",\n M: \"m\",\n} as const;\n\n/** Available icon positions relative to the label text. */\nexport const ICON_POSITION = {\n LEADING: \"leading\",\n TRAILING: \"trailing\",\n} as const;\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,kBAAkB;;;ACA3B,OAAO,UAAU,WAAW;AAK5B,IAAM,aAAyD;AAAA,EAC7D,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,EAKJ,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA,EAKH,GAAG;AAAA;AAAA;AAAA;AAAA;AAKL;AAEA,IAAM,gBAA+D;AAAA,EACnE,OAAO;AAAA,kBACS,OAAO,WAAW,CAAC;AAAA,aACxB,OAAO,KAAK;AAAA;AAAA;AAAA;AAAA,oBAIL,OAAO,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA,oBAIlB,OAAO,UAAU,CAAC;AAAA,eACvB,OAAO,UAAU,CAAC;AAAA;AAAA;AAAA,EAG/B,SAAS;AAAA,kBACO,OAAO,KAAK;AAAA,aACjB,OAAO,WAAW,CAAC;AAAA,wBACR,OAAO,UAAU,CAAC;AAAA;AAAA;AAAA,oBAGtB,OAAO,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA,sBAIhB,OAAO,UAAU,CAAC;AAAA,eACzB,OAAO,UAAU,CAAC;AAAA;AAAA;AAAA,EAG/B,OAAO;AAAA;AAAA,aAEI,OAAO,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA,oBAIZ,OAAO,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA,eAIvB,OAAO,UAAU,CAAC;AAAA;AAAA;AAGjC;AAGO,IAAM,eAAe,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,IAK/B,gBAAgB,gBAAgB,oBAAoB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOrD,CAAC,EAAE,MAAM,MAAM,WAAW,KAAK,CAAC;AAAA,IAChC,CAAC,EAAE,SAAS,MAAM,cAAc,QAAQ,CAAC;AAAA;;;AChFtC,IAAM,iBAAiB;AAAA,EAC5B,OAAO;AAAA,EACP,SAAS;AAAA,EACT,OAAO;AACT;AAGO,IAAM,cAAc;AAAA,EACzB,IAAI;AAAA,EACJ,GAAG;AAAA,EACH,GAAG;AACL;AAGO,IAAM,gBAAgB;AAAA,EAC3B,SAAS;AAAA,EACT,UAAU;AACZ;;;AFG6B,cAGzB,YAHyB;AAdtB,IAAM,SAAS,WAA2C,SAASA,QACxE;AAAA,EACE;AAAA,EACA,UAAU,eAAe;AAAA,EACzB,OAAO,YAAY;AAAA,EACnB;AAAA,EACA,eAAe,cAAc;AAAA,EAC7B,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA,cAAc;AAChB,GACA,KACA;AACA,QAAM,cAAc,OAAO,oBAAC,QAAK,MAAY,MAAM,IAAI,IAAK;AAE5D,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,MAAK;AAAA,MACL,UAAU;AAAA,MACV,OAAO;AAAA,MACP;AAAA,MACA,SAAS,WAAW,SAAY;AAAA,MAChC;AAAA,MACA,cAAY;AAAA,MAEX;AAAA,yBAAiB,cAAc,WAAW;AAAA,QAC3C,oBAAC,UAAM,UAAS;AAAA,QACf,iBAAiB,cAAc,YAAY;AAAA;AAAA;AAAA,EAC9C;AAEJ,CAAC;","names":["Button"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/parse-markdown-table.ts","../src/utils/convert-table-to-csv.ts"],"sourcesContent":["/** Structured representation of a parsed markdown table. */\nexport interface MarkdownTable {\n /** Column header labels. */\n columns: string[];\n /** Data rows, each an array of cell values matching the column order. */\n rows: string[][];\n /** Optional title extracted from a `<!-- table-title: ... -->` comment above the table. */\n title?: string;\n}\n\n/**\n * Parses cells from a markdown table row, trimming whitespace and stripping leading/trailing pipes.\n * @param line - A single line of a markdown table.\n * @returns An array of trimmed cell values.\n */\nfunction parseCells(line: string): string[] {\n const trimmed = line.trim();\n const stripped = trimmed.startsWith(\"|\") ? trimmed.slice(1) : trimmed;\n const withoutTrailing = stripped.endsWith(\"|\") ? stripped.slice(0, -1) : stripped;\n return withoutTrailing.split(\"|\").map((cell) => cell.trim());\n}\n\n/**\n * Tests whether a line is a GFM table separator row (e.g. `| --- | --- |`).\n * @param line - A single line to test.\n * @returns `true` if the line matches the separator pattern.\n */\nfunction isSeparatorRow(line: string): boolean {\n const cells = parseCells(line);\n return cells.length > 0 && cells.every((cell) => /^:?-{1,}:?$/.test(cell));\n}\n\n/**\n * Tests whether a line looks like a markdown table row (contains at least one pipe).\n * @param line - A single line to test.\n * @returns `true` if the line contains a pipe character.\n */\nfunction isTableRow(line: string): boolean {\n return line.includes(\"|\");\n}\n\nconst TABLE_TITLE_PREFIX = \"<!-- table-title:\";\nconst TABLE_TITLE_SUFFIX = \"-->\";\n\n/**\n * Extracts a table title from a `<!-- table-title: ... -->` HTML comment.\n * Uses string methods instead of regex to avoid ReDoS on untrusted input.\n * @param line - A single line to test.\n * @returns The title string, or `undefined` if the line is not a title comment.\n */\nfunction extractTableTitle(line: string): string | undefined {\n const trimmed = line.trim();\n if (!trimmed.startsWith(TABLE_TITLE_PREFIX) || !trimmed.endsWith(TABLE_TITLE_SUFFIX)) {\n return undefined;\n }\n return trimmed.slice(TABLE_TITLE_PREFIX.length, -TABLE_TITLE_SUFFIX.length).trim() || undefined;\n}\n\n/**\n * Extracts the Nth GFM markdown table from a string.\n * @param markdown - The markdown content to parse.\n * @param tableIndex - Zero-based index of the table to extract.\n * @returns The parsed table, or `null` if no table exists at the given index.\n */\nexport function parseMarkdownTable(markdown: string, tableIndex = 0): MarkdownTable | null {\n const lines = markdown.split(\"\\n\");\n let tableCount = 0;\n\n for (let i = 0; i < lines.length - 1; i++) {\n const headerLine = lines[i];\n const separatorLine = lines[i + 1];\n\n if (!isTableRow(headerLine) || !isSeparatorRow(separatorLine)) {\n continue;\n }\n\n if (tableCount === tableIndex) {\n const columns = parseCells(headerLine);\n const rows: string[][] = [];\n const title = i > 0 ? extractTableTitle(lines[i - 1]) : undefined;\n\n for (let j = i + 2; j < lines.length; j++) {\n if (!isTableRow(lines[j]) || isSeparatorRow(lines[j])) {\n break;\n }\n const trimmedLine = lines[j].trim();\n if (trimmedLine === \"\") {\n break;\n }\n rows.push(parseCells(lines[j]));\n }\n\n return { columns, rows, ...(title ? { title } : {}) };\n }\n\n tableCount++;\n }\n\n return null;\n}\n","import type { MarkdownTable } from \"./parse-markdown-table\";\n\n/**\n * Escapes and optionally quotes a CSV field per RFC 4180.\n * Fields containing commas, double quotes, or newlines are wrapped in double quotes.\n * Existing double quotes are escaped by doubling them.\n * @param field - The raw field value.\n * @returns The escaped/quoted field string.\n */\nfunction escapeField(field: string): string {\n if (field.includes(\",\") || field.includes('\"') || field.includes(\"\\n\") || field.includes(\"\\r\")) {\n return `\"${field.replace(/\"/g, '\"\"')}\"`;\n }\n return field;\n}\n\n/**\n * Converts a parsed markdown table to a CSV string (RFC 4180).\n * Fields containing commas, double quotes, or newlines are quoted.\n * @param table - The structured table data to convert.\n * @returns A CSV-formatted string with CRLF line endings.\n */\nexport function convertTableToCSV(table: MarkdownTable): string {\n const headerRow = table.columns.map(escapeField).join(\",\");\n const dataRows = table.rows.map((row) => row.map(escapeField).join(\",\"));\n return [headerRow, ...dataRows].join(\"\\r\\n\");\n}\n"],"mappings":";AAeA,SAAS,WAAW,MAAwB;AAC1C,QAAM,UAAU,KAAK,KAAK;AAC1B,QAAM,WAAW,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;AAC9D,QAAM,kBAAkB,SAAS,SAAS,GAAG,IAAI,SAAS,MAAM,GAAG,EAAE,IAAI;AACzE,SAAO,gBAAgB,MAAM,GAAG,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC;AAC7D;AAOA,SAAS,eAAe,MAAuB;AAC7C,QAAM,QAAQ,WAAW,IAAI;AAC7B,SAAO,MAAM,SAAS,KAAK,MAAM,MAAM,CAAC,SAAS,cAAc,KAAK,IAAI,CAAC;AAC3E;AAOA,SAAS,WAAW,MAAuB;AACzC,SAAO,KAAK,SAAS,GAAG;AAC1B;AAEA,IAAM,qBAAqB;AAC3B,IAAM,qBAAqB;AAQ3B,SAAS,kBAAkB,MAAkC;AAC3D,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,CAAC,QAAQ,WAAW,kBAAkB,KAAK,CAAC,QAAQ,SAAS,kBAAkB,GAAG;AACpF,WAAO;AAAA,EACT;AACA,SAAO,QAAQ,MAAM,mBAAmB,QAAQ,CAAC,mBAAmB,MAAM,EAAE,KAAK,KAAK;AACxF;AAQO,SAAS,mBAAmB,UAAkB,aAAa,GAAyB;AACzF,QAAM,QAAQ,SAAS,MAAM,IAAI;AACjC,MAAI,aAAa;AAEjB,WAAS,IAAI,GAAG,IAAI,MAAM,SAAS,GAAG,KAAK;AACzC,UAAM,aAAa,MAAM,CAAC;AAC1B,UAAM,gBAAgB,MAAM,IAAI,CAAC;AAEjC,QAAI,CAAC,WAAW,UAAU,KAAK,CAAC,eAAe,aAAa,GAAG;AAC7D;AAAA,IACF;AAEA,QAAI,eAAe,YAAY;AAC7B,YAAM,UAAU,WAAW,UAAU;AACrC,YAAM,OAAmB,CAAC;AAC1B,YAAM,QAAQ,IAAI,IAAI,kBAAkB,MAAM,IAAI,CAAC,CAAC,IAAI;AAExD,eAAS,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACzC,YAAI,CAAC,WAAW,MAAM,CAAC,CAAC,KAAK,eAAe,MAAM,CAAC,CAAC,GAAG;AACrD;AAAA,QACF;AACA,cAAM,cAAc,MAAM,CAAC,EAAE,KAAK;AAClC,YAAI,gBAAgB,IAAI;AACtB;AAAA,QACF;AACA,aAAK,KAAK,WAAW,MAAM,CAAC,CAAC,CAAC;AAAA,MAChC;AAEA,aAAO,EAAE,SAAS,MAAM,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC,EAAG;AAAA,IACtD;AAEA;AAAA,EACF;AAEA,SAAO;AACT;;;AC1FA,SAAS,YAAY,OAAuB;AAC1C,MAAI,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,IAAI,KAAK,MAAM,SAAS,IAAI,GAAG;AAC9F,WAAO,IAAI,MAAM,QAAQ,MAAM,IAAI,CAAC;AAAA,EACtC;AACA,SAAO;AACT;AAQO,SAAS,kBAAkB,OAA8B;AAC9D,QAAM,YAAY,MAAM,QAAQ,IAAI,WAAW,EAAE,KAAK,GAAG;AACzD,QAAM,WAAW,MAAM,KAAK,IAAI,CAAC,QAAQ,IAAI,IAAI,WAAW,EAAE,KAAK,GAAG,CAAC;AACvE,SAAO,CAAC,WAAW,GAAG,QAAQ,EAAE,KAAK,MAAM;AAC7C;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/markdown-to-plain-text.ts"],"sourcesContent":["const PLACEHOLDER_PREFIX = \"\\u200B\\u200BIC\";\nconst PLACEHOLDER_SUFFIX = \"\\u200B\\u200B\";\nconst PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\\\d+)${PLACEHOLDER_SUFFIX}`, \"g\");\n\n/**\n * Converts a Markdown string to plain text by stripping all formatting syntax.\n *\n * Handles headings, bold/italic/strikethrough, links, images, blockquotes, code blocks,\n * inline code, tables, and horizontal rules. Inline code content is preserved literally\n * (formatting inside backticks is not stripped).\n *\n * @param markdown - The Markdown source string.\n * @returns A plain-text string with all Markdown syntax removed.\n */\nexport function markdownToPlainText(markdown: string): string {\n if (!markdown) return \"\";\n\n // Protect inline code content from bold/italic stripping by replacing with placeholders\n const inlineCodeSnippets: string[] = [];\n let result = markdown\n // HTML comments (e.g. <!-- table-title: ... -->), including multiline and --!> variants\n .replace(/^<!--[\\s\\S]*?--!?>\\n?/gm, \"\")\n // Code blocks (must come first)\n .replace(/```[\\s\\S]*?```/g, \"\")\n // Inline code → placeholder\n .replace(/`([^`]+)`/g, (_match, code: string) => {\n inlineCodeSnippets.push(code);\n return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;\n })\n // Remove headings markers, keep text\n .replace(/^#{1,6}\\s+/gm, \"\")\n // Remove horizontal rules\n .replace(/^(?:---|\\*\\*\\*|___)\\s*$/gm, \"\")\n // Bold + italic\n .replace(/\\*{3}(.+?)\\*{3}/g, \"$1\")\n // Bold\n .replace(/\\*{2}(.+?)\\*{2}/g, \"$1\")\n .replace(/_{2}(.+?)_{2}/g, \"$1\")\n // Italic\n .replace(/\\*(.+?)\\*/g, \"$1\")\n .replace(/_(.+?)_/g, \"$1\")\n // Strikethrough\n .replace(/~~(.+?)~~/g, \"$1\")\n // Images: ![alt](url) → alt (must come before links)\n .replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, \"$1\")\n // Links: [text](url) → text\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\n // Blockquote markers\n .replace(/^>\\s?/gm, \"\")\n // Table separator rows (|---|---|)\n .replace(/^\\|[-:\\s|]+\\|\\s*$/gm, \"\")\n // Table rows: | a | b | → a | b\n .replace(/^\\|\\s?/gm, \"\")\n .replace(/\\s?\\|$/gm, \"\")\n // Collapse multiple blank lines\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n\n // Restore inline code content\n result = result.replace(PLACEHOLDER_REGEX, (_match, index: string) => inlineCodeSnippets[Number(index)]);\n\n return result;\n}\n"],"mappings":";AAAA,IAAM,qBAAqB;AAC3B,IAAM,qBAAqB;AAC3B,IAAM,oBAAoB,IAAI,OAAO,GAAG,kBAAkB,SAAS,kBAAkB,IAAI,GAAG;AAYrF,SAAS,oBAAoB,UAA0B;AAC5D,MAAI,CAAC,SAAU,QAAO;AAGtB,QAAM,qBAA+B,CAAC;AACtC,MAAI,SAAS,SAEV,QAAQ,2BAA2B,EAAE,EAErC,QAAQ,mBAAmB,EAAE,EAE7B,QAAQ,cAAc,CAAC,QAAQ,SAAiB;AAC/C,uBAAmB,KAAK,IAAI;AAC5B,WAAO,GAAG,kBAAkB,GAAG,mBAAmB,SAAS,CAAC,GAAG,kBAAkB;AAAA,EACnF,CAAC,EAEA,QAAQ,gBAAgB,EAAE,EAE1B,QAAQ,6BAA6B,EAAE,EAEvC,QAAQ,oBAAoB,IAAI,EAEhC,QAAQ,oBAAoB,IAAI,EAChC,QAAQ,kBAAkB,IAAI,EAE9B,QAAQ,cAAc,IAAI,EAC1B,QAAQ,YAAY,IAAI,EAExB,QAAQ,cAAc,IAAI,EAE1B,QAAQ,2BAA2B,IAAI,EAEvC,QAAQ,0BAA0B,IAAI,EAEtC,QAAQ,WAAW,EAAE,EAErB,QAAQ,uBAAuB,EAAE,EAEjC,QAAQ,YAAY,EAAE,EACtB,QAAQ,YAAY,EAAE,EAEtB,QAAQ,WAAW,MAAM,EACzB,KAAK;AAGR,WAAS,OAAO,QAAQ,mBAAmB,CAAC,QAAQ,UAAkB,mBAAmB,OAAO,KAAK,CAAC,CAAC;AAEvG,SAAO;AACT;","names":[]}