1
0
Fork 0

Refactor highlight

This commit is contained in:
Pabloader 2026-03-25 09:36:38 +00:00
parent 6e61ff7194
commit 4b24ce85e0
5 changed files with 86 additions and 65 deletions

View File

@ -0,0 +1,27 @@
.italic {
font-style: italic;
color: var(--italicColor, #AFAFAF);
}
.bold {
font-weight: bold;
}
.quote {
color: var(--quoteColor, #D4E5FF);
}
.codeBlock {
font-family: monospace;
background: var(--codeBg, #49483e);
padding: 0.5em;
display: block;
border-radius: var(--radius, 4px);
}
.inlineCode {
font-family: monospace;
background: var(--codeBg, #49483e);
padding: 0.1em 0.3em;
border-radius: 0.2em;
}

57
src/common/highlight.ts Normal file
View File

@ -0,0 +1,57 @@
import styles from './assets/highlight.module.css';
export const highlight = (message: string, keepMarkup = true): string => {
let resultHTML = '';
const tokenRegex = /(\*\*?|"|```|`)/g;
const stack: string[] = [];
let inCodeBlock = false;
let lastIndex = 0;
let match: RegExpExecArray | null;
while ((match = tokenRegex.exec(message)) !== null) {
resultHTML += message.slice(lastIndex, match.index);
lastIndex = tokenRegex.lastIndex;
const token = match[0];
const isClose = stack.at(-1) === token;
const keepToken = keepMarkup || token === '"';
if (inCodeBlock) {
if (token === '```' && isClose) {
inCodeBlock = false;
stack.pop();
resultHTML += `${keepToken ? token : ''}</span>`;
} else {
resultHTML += token;
}
continue;
}
if (isClose) {
stack.pop();
resultHTML += `${keepToken ? token : ''}</span>`;
} else if (token === '*') {
stack.push(token);
resultHTML += `<span class="${styles.italic}">${keepToken ? token : ''}`;
} else if (token === '**') {
stack.push(token);
resultHTML += `<span class="${styles.bold}">${keepToken ? token : ''}`;
} else if (token === '"') {
stack.push(token);
resultHTML += `<span class="${styles.quote}">"`;
} else if (token === '```') {
stack.push(token);
inCodeBlock = true;
resultHTML += `<span class="${styles.codeBlock}">`;
} else if (token === '`') {
stack.push(token);
resultHTML += `<span class="${styles.inlineCode}">`;
}
}
resultHTML += message.slice(lastIndex);
resultHTML += '</span>'.repeat(stack.length);
return resultHTML;
}

View File

@ -1,10 +1,10 @@
import { useInputState } from "@common/hooks/useInputState"; import { useInputState } from "@common/hooks/useInputState";
import { highlight } from "@common/highlight";
import { Sidebar } from "./sidebar"; import { Sidebar } from "./sidebar";
import { useAppState, type ChatMessage } from "../contexts/state"; import { useAppState, type ChatMessage } from "../contexts/state";
import styles from '../assets/chat-sidebar.module.css'; import styles from '../assets/chat-sidebar.module.css';
import { useState, useRef, useEffect, useMemo, useCallback } from "preact/hooks"; import { useState, useRef, useEffect, useMemo, useCallback } from "preact/hooks";
import LLM from "../utils/llm"; import LLM from "../utils/llm";
import { highlight } from "../utils/highlight";
import Prompt from "../utils/prompt"; import Prompt from "../utils/prompt";
import { Tools } from "../utils/tools"; import { Tools } from "../utils/tools";
import clsx from "clsx"; import clsx from "clsx";

View File

@ -1,7 +1,7 @@
import { ContentEditable } from "@common/components/ContentEditable"; import { ContentEditable } from "@common/components/ContentEditable";
import { highlight } from "@common/highlight";
import { useAppState, type Tab } from "../contexts/state"; import { useAppState, type Tab } from "../contexts/state";
import styles from '../assets/editor.module.css'; import styles from '../assets/editor.module.css';
import { highlight } from "../utils/highlight";
import { useMemo } from "preact/hooks"; import { useMemo } from "preact/hooks";
import { CharacterEditor } from "./character-editor"; import { CharacterEditor } from "./character-editor";
import { LocationEditor } from "./location-editor"; import { LocationEditor } from "./location-editor";

View File

@ -1,63 +0,0 @@
export const highlight = (message: string, keepMarkup = true): string => {
let resultHTML = '';
const replaceRegex = /(\*\*?|"|```|`)/ig;
const splitToken = '___SPLIT_AWOORWA___';
const preparedMessage = message.replace(replaceRegex, `${splitToken}$1${splitToken}`);
const parts = preparedMessage.split(splitToken);
const stack: string[] = [];
let inCodeBlock = false;
for (const part of parts) {
const isClose = stack.at(-1) === part;
const keepPart = keepMarkup || part === '"';
if (inCodeBlock) {
if (part === '```' && isClose) {
inCodeBlock = false;
stack.pop();
resultHTML += `${keepPart ? part : ''}</span>`;
} else {
resultHTML += part;
}
continue;
}
if (isClose) {
stack.pop();
if (part === '*' || part === '**' || part === '"' || part === '`' || part === '```') {
resultHTML += `${keepPart ? part : ''}</span>`;
}
} else {
if (part === '*') {
stack.push(part);
resultHTML += `<span style="font-style:italic;color:var(--italicColor)">${keepPart ? part : ''}`;
} else if (part === '**') {
stack.push(part);
resultHTML += `<span style="font-weight:bold">${keepPart ? part : ''}`;
} else if (part === '"') {
stack.push(part);
resultHTML += `<span style="color:var(--quoteColor)">"`;
} else if (part === '```') {
stack.push(part);
inCodeBlock = true;
resultHTML += `<span style="font-family:monospace;background:var(--codeBg);padding:0.5em;display:block;border-radius:var(--radius)">`;
} else if (part === '`') {
stack.push(part);
resultHTML += `<span style="font-family:monospace;background:var(--codeBg);padding:0.1em 0.3em;border-radius:0.2em">`;
} else {
resultHTML += part;
}
}
}
while (stack.length) {
const part = stack.pop();
if (part === '*' || part === '**' || part === '"' || part === '`' || part === '```') {
resultHTML += `</span>`;
}
}
return resultHTML;
}