diff --git a/common/types/ui.ts b/common/types/ui.ts index 3c3c8eaf9..602629b90 100644 --- a/common/types/ui.ts +++ b/common/types/ui.ts @@ -42,6 +42,8 @@ export type CustomUI = { chatQuoteColor: string chatQuoteEmphasisColor: string chatQuoteEmphasisWeight: string + chatEmphasisEmphasisColor: string + chatEmphasisEmphasisWeight: string } export type MessageOption = @@ -101,6 +103,8 @@ const customUiGuard = { chatQuoteColor: 'string', chatQuoteEmphasisColor: 'string', chatQuoteEmphasisWeight: 'string', + chatEmphasisEmphasisColor: 'string', + chatEmphasisEmphasisWeight: 'string', } as const export const uiGuard = { @@ -152,6 +156,8 @@ export const defaultUIsettings: UISettings = { chatQuoteColor: '--text-800', chatQuoteEmphasisColor: '--text-800', chatQuoteEmphasisWeight: 'unset', + chatEmphasisEmphasisColor: '--text-800', + chatEmphasisEmphasisWeight: 'unset', }, dark: { @@ -162,6 +168,8 @@ export const defaultUIsettings: UISettings = { chatQuoteColor: '--text-800', chatQuoteEmphasisColor: '--text-800', chatQuoteEmphasisWeight: 'unset', + chatEmphasisEmphasisColor: '--text-800', + chatEmphasisEmphasisWeight: 'unset', }, msgOptsInline: { diff --git a/web/pages/Chat/components/Message.css b/web/pages/Chat/components/Message.css index e0818cae0..ba759559e 100644 --- a/web/pages/Chat/components/Message.css +++ b/web/pages/Chat/components/Message.css @@ -21,6 +21,14 @@ qem { font-weight: var(--text-quote-emphasis-weight); } +eem { + /* emphasis emphasis; *inner* emphasis within tags */ + color: var(--text-800); + color: var(--text-emphasis-emphasis-color); + font-style: italic; + font-weight: var(--text-emphasis-emphasis-weight); +} + .rendered-markdown img { display: inline-block; /* markdown images are expected to be inline-block */ } diff --git a/web/pages/Chat/components/Message.tsx b/web/pages/Chat/components/Message.tsx index b2a7244bc..0f3c163b2 100644 --- a/web/pages/Chat/components/Message.tsx +++ b/web/pages/Chat/components/Message.tsx @@ -1046,70 +1046,109 @@ function retryJsonSchema(original: AppSchema.ChatMessage, split: SplitMessage) { } function renderMessage(ctx: ContextState, text: string, isUser: boolean, adapter?: string) { + // Iam getting pissed of by showdown effing up Asteriks, I just do this myself - @Aldimensch + // Escape single asterisks but not double asterisks + text = text.replace(/(? tags - * and emphasis, which is wrapped in tags. - */ -function wrapWithQuoteElement(str: string) { - // Replace all non-regular double quotes with double regular quotes - // Unicode double quote characters: https://en.wikipedia.org/wiki/Quotation_mark#Unicode_code_point_table - str = str.replace(/[\u201C\u201D\u201E\u201F]/g, '"') - - return str.replace( - /* - Regex magic explained: - <[\s\S]*?> - skip all HTML tags eg. - ```[\s\S]*?``` - skip all code blocks eg.
/``` markdown transform 
 to ```
-    ``[\s\S]*?``    - skip all inline code eg. /`` markdown transform  to `` | this is a non standard markup 
-    `[\s\S]*?`      - skip all inline code eg. /` markdown transform  to `
-
-    (\".+?\")       - capture all regular double quotes, which are not part of HTML tags or code blocks
-    All captured groups are passed to the wrapCaptureGroupQuotes function
-    */
-    /<[\s\S]*?>|```[\s\S]*?```|``[\s\S]*?``|`[\s\S]*?`|(\".+?\")/gm,
-    wrapCaptureGroupQuotes
-  )
+function asterikPairing(text: string) {
+  const lines = text.split(/((?:<\/p>|<\/pre>|||<\/q>))/i);
+  let accum = '';
+  let tempLine = '';
+  for (let line of lines) {
+    if (/(?:<\/p>|<\/pre>|||<\/q>)/i.test(line)) {
+      accum += line;
+    } else {
+      tempLine = outerAsterikPairing(line);
+      accum += innerAsterikPairing(tempLine);
+    }
+  }
+  return accum;
 }
 
-/** Processes capture group from above */
-function wrapCaptureGroupQuotes(match: string, regularQuoted?: string) {
-  if (regularQuoted) {
-    /*If we have a valid string then we are within a quote
-    ([\s\S]*?) - we ignore all characters between  and 
-    a valid capure will look like this: "lets have some fun"
-    we then pass the capture group to wrapCaptureGroupEmphasis function, which will replace  with 
-    */
-    regularQuoted = regularQuoted.replace(/([\s\S]*?)<\/em>/gm, wrapCaptureGroupEmphasis)
-    return '"' + regularQuoted.replace(/\"/g, '') + '"'
+function outerAsterikPairing(line: string) {
+  // Check if the line contains any 
 or  tags, we dont mess with the content of these tags.
+  if (/<(?:pre|code)(?:\s[^>]*)?>/i.test(line)) return line
+
+  const firstAsterisk = line.indexOf('*')
+  const lastAsterisk = line.lastIndexOf('*')
+  if (firstAsterisk !== -1 && lastAsterisk !== -1 && firstAsterisk !== lastAsterisk) {
+    return line.slice(0, firstAsterisk) + '' + line.slice(firstAsterisk + 1, lastAsterisk) + '' + line.slice(lastAsterisk + 1)
   }
-  return match
+  return line
 }
 
-/** Replaces all  tags within a  tag with  tags */
-function wrapCaptureGroupEmphasis(match: string, emphasisQuote?: string) {
-  if (emphasisQuote) {
-    return '' + emphasisQuote.replace(/\"/g, '') + ''
-  }
-  return match
+function innerAsterikPairing(line: string) {
+  // Check if the line contains any 
 or  tags, we dont mess with the content of these tags.
+  if (/<(?:pre|code)(?:\s[^>]*)?>/i.test(line)) return line
+
+  // Count asterisks in the line
+  const asteriskCount = (line.match(/\*/g) || []).length
+  
+  // If odd number of asterisks, we can't properly pair them
+  if (asteriskCount % 2 !== 0) return line
+  
+  // If even number of asterisks, replace all * pairs with  tags
+  return line.replace(/\*(.*?)\*/g, '$1')
+}
+
+/**
+ * Beautifies the markup by handling custom quote and emphasis formatting in a single pass.
+ * This function replaces a series of sequential replacements with a single, more efficient
+ * regular expression. It processes:
+ * - Escaped asterisks inside `` blocks.
+ * - Double-quoted sections `"..."` into `...` tags.
+ * - Asterisk-based emphasis inside quotes into `...` tags.
+ * (Asterisk emphasis is now handled after HTML generation)
+ */
+function makeTextLookNice(str: string) {
+  // First, normalize all Unicode double quotes to standard double quotes.
+  // Unicode double quote characters: https://en.wikipedia.org/wiki/Quotation_mark#Unicode_code_point_table
+  const normalizedStr = str.replace(/[\u201C\u201D\u201E\u201F]/g, '"')
+
+  // This regex handles two cases, in order of priority:
+  // 1. `...` blocks, 2. quoted sections `"..."`
+  return normalizedStr.replace(
+    /([\s\S]*?<\/code>)|(".*?")/gm,
+    (match, codeBlock, quoted) => {
+      // Case 1: A code block was matched.
+      if (codeBlock) {
+        // Un-escape any `\*` back to `*` inside a code block.
+        return codeBlock.replace(/\\\*/g, '*')
+      }
+
+      // Case 2: A quoted string was matched.
+      if (quoted) {
+        // Find any asterisk-wrapped text *inside* the quote and
+        // convert it to a custom emphasis tag ``.
+        const innerContent = quoted.slice(1, -1).replace(/\*(.*?)\*/g, '$1')
+        return `"${innerContent}"`
+      }
+
+      return match
+    }
+  )
 }
 
 function sendAction(_send: MessageProps['sendMessage'], action: AppSchema.ChatAction) {
diff --git a/web/pages/Settings/UISettings.tsx b/web/pages/Settings/UISettings.tsx
index 0083023d2..b122a0818 100644
--- a/web/pages/Settings/UISettings.tsx
+++ b/web/pages/Settings/UISettings.tsx
@@ -447,6 +447,36 @@ const UISettings: Component<{}> = () => {
         value={state.current.chatQuoteEmphasisWeight || 'unset'}
       />
 
+       userStore.saveCustomUI({ chatEmphasisEmphasisColor: 'text-800' })}
+          >
+            Reset to Default
+          
+        }
+        onInput={(color) => tryCustomUI({ chatEmphasisEmphasisColor: color })}
+        onChange={(color) => userStore.saveCustomUI({ chatEmphasisEmphasisColor: color })}
+        value={state.current.chatEmphasisEmphasisColor || '--text-800'}
+      />
+
+