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}`; +}; + +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}`).join(''); + + const headers = headerLines + .map(l => `${renderCells(parseTableRow(l), 'th')}`) + .join(''); + const rows = bodyLines + .map(l => `${renderCells(parseTableRow(l), 'td')}`) + .join(''); + + return `${headers}${rows}
    `; +}; + 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 },