Commit b5bc27a

HPCesia <me@hpcesia.com>
2025-02-11 15:32:46
feat: inline component
1 parent 46d0fa3
src/content/posts/Markdown-Extensions.md
@@ -111,3 +111,28 @@ If no title, will automatically use `Tab [index]` as title.
 ```
 
 ::::
+
+### Inline
+
+:::tabs
+::tab[Component Syntax]
+
+```md
+:inline[content]
+```
+
+::tab[Parameter Description]
+
+- `content`: The content of the inline component, will add `inline` class to the rendered content.
+
+::tab[Component Examples]{active}
+
+This is an inline img example: :inline[![Inline Image](/favicon/favicon-192x192.png)].
+
+::tab[Code of Examples]
+
+```md
+This is an inline img example: :inline[![Inline Image](/favicon/favicon-192x192.png)].
+```
+
+:::
src/plugins/components/inline.mjs
@@ -0,0 +1,41 @@
+/// <reference types="mdast" />
+import { h } from 'hastscript';
+
+/**
+ * Create a Tabs component.
+ *
+ * @param {Object} props - The properties of the component.
+ * @param {import('mdast').RootContent[]} children - The children elements of the component.
+ * @returns {import('mdast').Parent} The created Tabs component.
+ */
+export function componentInline(props, children) {
+  if (!Array.isArray(children)) {
+    return h(
+      'span',
+      { class: 'hidden' },
+      'Invalid directive. ("inline" directive must be a text directive with child.)'
+    );
+  }
+  if (children.length !== 1) {
+    return h(
+      'span',
+      { class: 'hidden' },
+      'Invalid directive. ("inline" directive must be a text directive with child.)'
+    );
+  }
+  const child = children[0];
+
+  if (child.tagName === 'img') {
+    delete child.properties['data-zoom'];
+  }
+
+  const classes = [
+    ...new Set([
+      ...('class' in child.properties ? child.properties.class : '').split(' '),
+      'inline',
+    ]),
+  ].join(' ');
+  child.properties.class = classes;
+
+  return h(child.tagName, child.properties, child.children);
+}
src/plugins/remark-image-process.mjs
@@ -10,6 +10,10 @@ export function remarkImageProcess() {
       node.data = node.data || {};
       node.data.hProperties = node.data.hProperties || {};
       node.data.hProperties['data-zoom'] = '';
+      // lazyload
+      node.data.hProperties.loading = 'lazy';
+      // async decode
+      node.data.hProperties.decoding = 'async';
     });
   };
 }
src/styles/markdown.css
@@ -68,12 +68,20 @@ article {
 
   /* 媒体元素 */
   img {
-    position: relative;
-    margin: 1rem auto;
-    max-width: 75%;
-    max-height: 40rem;
+    &:not(.inline) {
+      position: relative;
+      margin: 1rem auto;
+      max-width: 75%;
+      max-height: 40rem;
+
+      @apply rounded-md;
+    }
 
-    @apply rounded-md;
+    &:is(.inline) {
+      max-height: 1.5em;
+      vertical-align: middle;
+      margin: 0 0.25em;
+    }
   }
 
   hr {
astro.config.mjs
@@ -1,5 +1,6 @@
 // @ts-check
 import { CDN } from './src/constants/cdn.mjs';
+import { componentInline } from './src/plugins/components/inline.mjs';
 import { componentTabs } from './src/plugins/components/tabs.mjs';
 import { rehypeWrapTables } from './src/plugins/rehype-wrap-tables.mjs';
 import { remarkExcerpt } from './src/plugins/remark-excerpt';
@@ -63,7 +64,7 @@ export default defineConfig({
       [
         rehypeComponents,
         {
-          components: { tabs: componentTabs },
+          components: { tabs: componentTabs, inline: componentInline },
         },
       ],
       [