diff --git a/src/common/assets/highlight.module.css b/src/common/assets/highlight.module.css
index ff1d8af..5445676 100644
--- a/src/common/assets/highlight.module.css
+++ b/src/common/assets/highlight.module.css
@@ -42,4 +42,33 @@
.header3 {
font-size: 1em;
+}
+
+.table {
+ border-collapse: collapse;
+ width: 100%;
+ margin: 0.5em 0;
+}
+
+.table th,
+.table td {
+ border: 1px solid var(--tableBorder, #555);
+ padding: 0.3em 0.6em;
+ text-align: left;
+}
+
+.table th {
+ background: var(--tableHeaderBg, #3a3a3a);
+ font-weight: bold;
+}
+
+.list {
+ margin: 0.4em 0;
+ padding-left: 1.5em;
+}
+
+.hr {
+ border: none;
+ border-top: 1px solid var(--hrColor, #555);
+ margin: 0.5em 0;
}
\ No newline at end of file
diff --git a/src/common/highlight.ts b/src/common/highlight.ts
index 41ac074..47959d7 100644
--- a/src/common/highlight.ts
+++ b/src/common/highlight.ts
@@ -1,6 +1,44 @@
import clsx from 'clsx';
import styles from './assets/highlight.module.css';
+const parseTableRow = (line: string): string[] =>
+ line.split('|').slice(1, -1).map(cell => cell.trim());
+
+const isSeparatorRow = (line: string): boolean =>
+ /^\|[\s|:\-]+\|$/.test(line.trim());
+
+export const parseList = (block: string, ordered: boolean): string => {
+ const marker = ordered ? /^\d+\. / : /^[-+] /;
+ const items = block.trim().split('\n')
+ .map(l => `
${l.trim().replace(marker, '')}`)
+ .join('');
+ const tag = ordered ? 'ol' : 'ul';
+ return `<${tag} class="${styles.list}">${items}${tag}>`;
+};
+
+export const parseTable = (table: string): string => {
+ const lines = table.trim().split('\n').map(l => l.trim()).filter(l => l.startsWith('|'));
+ if (lines.length < 2) return table;
+
+ const sepIndex = lines.findIndex(isSeparatorRow);
+ if (sepIndex === -1) return table;
+
+ const headerLines = lines.slice(0, sepIndex);
+ const bodyLines = lines.slice(sepIndex + 1);
+
+ const renderCells = (cells: string[], tag: 'th' | 'td') =>
+ cells.map(cell => `<${tag}>${cell}${tag}>`).join('');
+
+ const headers = headerLines
+ .map(l => `${renderCells(parseTableRow(l), 'th')}
`)
+ .join('');
+ const rows = bodyLines
+ .map(l => `${renderCells(parseTableRow(l), 'td')}
`)
+ .join('');
+
+ return ``;
+};
+
export const highlight = (message: string, keepMarkup = true): string => {
let resultHTML = '';
const tokenRegex = /(\*\*?|"|```|`|(?:^|\n)#{1,3} |\n)/g;
@@ -90,5 +128,12 @@ export const highlight = (message: string, keepMarkup = true): string => {
if (inHeader) resultHTML += '';
resultHTML += ''.repeat(stack.length);
+ if (!keepMarkup) {
+ resultHTML = resultHTML.replace(/((?:(?:^|\n)\|.+)+)/g, match => parseTable(match));
+ resultHTML = resultHTML.replace(/(^|\n)---(\n|$)/g, (_, pre, post) => `${pre}
${post}`);
+ resultHTML = resultHTML.replace(/((?:(?:^|\n)[-+] .+)+)/g, match => parseList(match, false));
+ resultHTML = resultHTML.replace(/((?:(?:^|\n)\d+\. .+)+)/g, match => parseList(match, true));
+ }
+
return resultHTML;
}
diff --git a/src/games/storywriter/utils/tools.ts b/src/games/storywriter/utils/tools.ts
index 4362f1e..c1d3efa 100644
--- a/src/games/storywriter/utils/tools.ts
+++ b/src/games/storywriter/utils/tools.ts
@@ -394,6 +394,9 @@ export namespace Tools {
{ name: `character:${c.name}`, content: c.name },
{ name: `character:${c.name}`, content: c.shortDescription },
{ name: `character:${c.name}`, content: c.description || '' },
+ ...c.relations.map(rel => (
+ { name: `character:${c.name}`, content: `relation: ${rel.name} (${rel.relation})` }
+ )),
]),
...appState.currentStory.locations.flatMap(l => [
{ name: `location:${l.name}`, content: l.name },