Переглянути джерело

fix(ui): preserve code spans adjacent to tildes (#35835)

Luke Parker 1 місяць тому
батько
коміт
9a51765bd2

+ 4 - 2
bun.lock

@@ -1133,7 +1133,7 @@
     "hono": "4.10.7",
     "hono-openapi": "1.1.2",
     "luxon": "3.6.1",
-    "marked": "17.0.1",
+    "marked": "17.0.6",
     "marked-shiki": "1.2.1",
     "opentui-spinner": "0.0.7",
     "remeda": "2.26.0",
@@ -4313,7 +4313,7 @@
 
     "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
 
-    "marked": ["marked@17.0.1", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg=="],
+    "marked": ["marked@17.0.6", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA=="],
 
     "marked-katex-extension": ["marked-katex-extension@5.1.6", "", { "peerDependencies": { "katex": ">=0.16 <0.17", "marked": ">=4 <18" } }, "sha512-vYpLXwmlIDKILIhJtiRTgdyZRn5sEYdFBuTmbpjD7lbCIzg0/DWyK3HXIntN3Tp8zV6hvOUgpZNLWRCgWVc24A=="],
 
@@ -6051,6 +6051,8 @@
 
     "@opentui/core/diff": ["diff@9.0.0", "", {}, "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw=="],
 
+    "@opentui/core/marked": ["marked@17.0.1", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg=="],
+
     "@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="],
 
     "@oslojs/jwt/@oslojs/encoding": ["@oslojs/encoding@0.4.1", "", {}, "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q=="],

+ 1 - 1
package.json

@@ -70,7 +70,7 @@
       "hono-openapi": "1.1.2",
       "fuzzysort": "3.1.0",
       "luxon": "3.6.1",
-      "marked": "17.0.1",
+      "marked": "17.0.6",
       "marked-shiki": "1.2.1",
       "remend": "1.3.0",
       "@playwright/test": "1.59.1",

+ 15 - 0
packages/ui/src/context/marked-code-span.test.ts

@@ -0,0 +1,15 @@
+import { expect, test } from "bun:test"
+import { Marked } from "marked"
+import { markedCodeSpanBoundary } from "./marked-code-span"
+
+test("preserves code spans adjacent to tildes", async () => {
+  const marked = new Marked(markedCodeSpanBoundary)
+
+  expect(await marked.parse("~`0.1576` to measurement-window-only `0.00092`")).toBe(
+    "<p>~<code>0.1576</code> to measurement-window-only <code>0.00092</code></p>\n",
+  )
+  expect(await marked.parse("`before`~`after`")).toBe(
+    "<p><code>before</code>~<code>after</code></p>\n",
+  )
+  expect(await marked.parse("~~`deleted code`~~")).toBe("<p><del><code>deleted code</code></del></p>\n")
+})

+ 17 - 0
packages/ui/src/context/marked-code-span.ts

@@ -0,0 +1,17 @@
+import type { MarkedExtension } from "marked"
+
+// Keep adjacent tilde and backtick runs separate until markedjs/marked#4011 is released.
+export const markedCodeSpanBoundary = {
+  tokenizer: {
+    inlineText(src) {
+      const match = /^(`+(?=~)|~+(?=`))/.exec(src)
+      if (!match) return false
+      return {
+        type: "text",
+        raw: match[0],
+        text: match[0],
+        escaped: this.lexer.state.inRawBlock,
+      }
+    },
+  },
+} satisfies MarkedExtension

+ 2 - 0
packages/ui/src/context/marked.tsx

@@ -3,6 +3,7 @@ import markedShiki from "marked-shiki"
 import katex from "katex"
 import { bundledLanguages, type BundledLanguage } from "shiki"
 import { createSimpleContext } from "./helper"
+import { markedCodeSpanBoundary } from "./marked-code-span"
 import { getSharedHighlighter, registerCustomTheme, ThemeRegistrationResolved } from "@pierre/diffs"
 
 export const OpenCodeTheme = {
@@ -521,6 +522,7 @@ export const { use: useMarked, provider: MarkedProvider } = createSimpleContext(
   name: "Marked",
   init: (props: { nativeParser?: NativeMarkdownParser }) => {
     const jsParser = marked.use(
+      markedCodeSpanBoundary,
       {
         renderer: {
           link({ href, title, text }) {