arvid-e 8 месяцев назад
Родитель
Сommit
b22503fa2c

+ 56 - 0
apps/app/src/services/renderer/rehype-plugins/add-inline-code-attribute.ts

@@ -0,0 +1,56 @@
+// apps/app/src/services/renderer/rehype-plugins/add-inline-code-attribute.ts
+
+import type { Element, Root } from 'hast';
+import type { Plugin } from 'unified';
+import type { Node, Parent } from 'unist'; // Import Node and Parent for 'parent' property
+import { visit, type Test } from 'unist-util-visit';
+
+
+// WORKAROUND: Define a local type that explicitly includes the 'parent' property.
+// This is necessary because the 'parent' property is optional or not always
+// directly typed on 'Node' in some versions of unist.
+interface NodeWithParent extends Node {
+  parent?: Parent | undefined;
+}
+
+/**
+ * A Rehype plugin to ensure 'inline="true"' attribute is correctly applied to <code> tags.
+ *
+ * It aims to:
+ * 1. Ensure <code> tags wrapped directly inside <pre> tags (block code) DO NOT have 'inline="true"'.
+ * 2. Ensure <code> tags NOT wrapped directly inside <pre> tags (true inline code,
+ * whether from Markdown backticks or raw HTML `<code>`) DO have 'inline="true"'.
+ */
+export const rehypePlugin: Plugin<[], Root> = () => {
+  const isCodeElement: Test = { tagName: 'code', type: 'element' };
+
+  return (tree) => {
+    visit(tree, isCodeElement, (node: Element) => {
+      const typedNode = node as NodeWithParent; // Cast to access 'parent' property
+
+      // Determine if the <code> tag is directly inside a <pre> tag
+      const isInsidePre = typedNode.parent
+                          && typedNode.parent.type === 'element'
+                          && (typedNode.parent as Element).tagName === 'pre';
+
+      // --- Decision Logic ---
+      if (isInsidePre) {
+        // If it's inside a <pre> tag, it's a block code.
+        // Ensure 'inline' property is removed if present.
+        if (node.properties?.inline) {
+          delete (node.properties as Record<string, any>).inline;
+          // Clean up properties object if it becomes empty
+          if (Object.keys(node.properties).length === 0) {
+            node.properties = {};
+          }
+        }
+      }
+      else {
+        // If it's NOT inside a <pre> tag, it should be treated as inline code.
+        // Ensure 'inline="true"' is set.
+        node.properties = node.properties || {};
+        node.properties.inline = true;
+      }
+    });
+  };
+};

+ 4 - 51
apps/app/src/services/renderer/remark-plugins/codeblock.ts

@@ -1,64 +1,17 @@
-import type { Properties } from 'hast';
 import type { Schema as SanitizeOption } from 'hast-util-sanitize';
-import type { InlineCode as MdastInlineCode, Html, Text } from 'mdast';
+import type { InlineCode } from 'mdast';
 import type { Plugin } from 'unified';
-import type { Node, Parent, Point } from 'unist';
 import { visit } from 'unist-util-visit';
 
 
-type InlineCode = MdastInlineCode & {
-  data?: {
-    hProperties?: Properties;
-    [key: string]: unknown;
-  };
-};
-
 const SUPPORTED_CODE = ['inline'];
 
 export const remarkPlugin: Plugin = () => {
-
-  return (tree: Node) => {
-    const defaultPoint: Point = { line: 1, column: 1, offset: 0 };
-
-    visit(tree, 'html', (node: Html, index: number | undefined, parent: Parent | undefined) => {
-      // Find <code> tag
-      if (typeof node.value === 'string' && node.value.toLowerCase() === '<code>') {
-        if (parent && parent.children && index !== undefined) {
-          const contentNode = parent.children[index + 1] as Text | undefined;
-          const closingTagNode = parent.children[index + 2] as Html | undefined;
-
-          // Find closing tag
-          if (contentNode && contentNode.type === 'text'
-            && closingTagNode && closingTagNode.type === 'html'
-            && typeof closingTagNode.value === 'string' && closingTagNode.value.toLowerCase() === '</code>') {
-
-            // Create InlineCode node
-            const newInlineCodeNode: InlineCode = {
-              type: 'inlineCode',
-              value: contentNode.value,
-              position: {
-                start: contentNode.position?.start || node.position?.start || defaultPoint,
-                end: contentNode.position?.end || closingTagNode.position?.end || defaultPoint,
-              },
-              data: {
-                hProperties: { inline: 'true' },
-              },
-            };
-
-            parent.children.splice(index, 3, newInlineCodeNode);
-          }
-        }
-      }
-    }, true);
-
-    // Ensure all inlineCode nodes (including those from backticks) have the 'inline' property.
+  return (tree) => {
     visit(tree, 'inlineCode', (node: InlineCode) => {
-      node.data = node.data || {};
-      node.data.hProperties = (node.data.hProperties || {}) as Properties;
-      node.data.hProperties.inline = 'true';
+      const data = node.data || (node.data = {});
+      data.hProperties = { inline: 'true' }; // set 'true' explicitly because the empty string is evaluated as false for `if (inline) { ... }`
     });
-
-    return tree;
   };
 };
 

+ 2 - 1
apps/app/src/services/renderer/renderer.tsx

@@ -22,6 +22,7 @@ import loggerFactory from '~/utils/logger';
 
 import { tagNames as recommendedTagNames, attributes as recommendedAttributes } from './recommended-whitelist';
 import * as addClass from './rehype-plugins/add-class';
+import * as addInlineCodeAttribute from './rehype-plugins/add-inline-code-attribute';
 import { relativeLinks } from './rehype-plugins/relative-links';
 import { relativeLinksByPukiwikiLikeLinker } from './rehype-plugins/relative-links-by-pukiwiki-like-linker';
 import * as codeBlock from './remark-plugins/codeblock';
@@ -30,7 +31,6 @@ import * as emoji from './remark-plugins/emoji';
 import { pukiwikiLikeLinker } from './remark-plugins/pukiwiki-like-linker';
 import * as xsvToTable from './remark-plugins/xsv-to-table';
 
-
 // import EasyGrid from './PreProcessor/EasyGrid';
 
 
@@ -115,6 +115,7 @@ export const generateCommonOptions = (pagePath: string|undefined): RendererOptio
       [addClass.rehypePlugin, {
         table: 'table table-bordered',
       }],
+      addInlineCodeAttribute.rehypePlugin,
     ],
     components: {
       a: NextLink,