SumikaNya

SumikaNya

Some modifications to the Shiro theme

Preface#

Some small modifications have been made to the current theme, recording the changes, hoping to help everyone || If there are any mistakes, please feel free to discuss, I'm a newbie ||

Warning

Tip
The modifications are based on personal preference. If you have different ideas, feel free to discuss in the comments.

Modify the Information Display of the Top Navigation Bar#

The relevant file path is src\components\layout\header\config.ts
The display and order of the navigation bar are defined in the headerMenuConfig in the file, you can modify it as needed.
For example, the friend links have been moved out of the secondary level.
image
image

Optimize the Page Effect When Displaying Images with Excessive Height#

If the image height is too long, it will occupy the entire page, which may result in the entire page being an image during browsing. The display effect for excessively tall images is not good. We can limit the height display of the element using max-height in CSS.
Find the following part in src\components\ui\image\ZoomedImage.tsx and add max-height max-h-[70vh] w-auto object-contain to ensure that the maximum height of the image does not exceed 70% (parameters can be adjusted as needed), and the width remains adaptive, allowing the image to be fully displayed without being cropped.

const styles = tv({  
  base: 'rounded-xl overflow-hidden text-center inline-flex items-center justify-center duration-200 max-height max-h-[70vh] w-auto object-contain',  
  variants: {  
    status: {  
      loading: 'hidden opacity-0',  
      loaded: 'opacity-100 block',  
      error: 'hidden opacity-0',  
    },  
  },  
})

::: grid {cols=2,rows=1,gap=4,type=images}
The image occupies the entire page
Added a limit of 70% effect
:::

Enhance the Alert Component to Support Custom Titles#

The markdown syntax extended by Shiro includes GitHub alerts, but it limits the display of titles to only NOTE, IMPORTANT, WARNING, TIP, CAUTION.
Since I use Obsidian to edit Markdown files, the alerts there can support custom title styles, with the syntax as follows:

> [!WARNING] Any title text
> Other text content

On July 8, 2025, Innei updated the handling of blockquotes and alerts, and the original method is now obsolete!
So I used Claude to help modify the file to achieve the same effect.
src\components\ui\markdown\parsers\blockquote.tsx

import React, { Children, cloneElement, useMemo } from 'react';  
import type { FC, ReactNode } from 'react';  
import { GitAlert } from './alert';  
  
const getNodeText = (node: ReactNode): string => {  
  if (node == null) return '';  
  if (typeof node === 'string' || typeof node === 'number') return node.toString();  
  if (Array.isArray(node)) return node.map(getNodeText).join('');  
  if (React.isValidElement<{ children?: ReactNode }>(node)) return getNodeText(node.props.children);  
  return '';  
};  
  
export const MBlockQuote: FC<{  
  className?: string;  
  children: ReactNode;  
  alert?: string;  
}> = ({ className, children, alert }) => {  
  if (alert) return <GitAlert type={alert}>{children}</GitAlert>;  
  
  const { isAlert, alertType, title, content } = useMemo(() => {  
    const childrenArray = Children.toArray(children);  
    const elementChildren = childrenArray.filter(React.isValidElement);  
  
    if (elementChildren.length === 0) return { isAlert: false, alertType: '', title: undefined, content: childrenArray };  
  
    const firstChild = elementChildren[0];  
    const alertMatch = getNodeText(firstChild).trim().match(/^\[!(\w+)\](?:\s(.*))?/);  
  
    if (!alertMatch) return { isAlert: false, alertType: '', title: undefined, content: childrenArray };  
  
    const alertType = alertMatch[1].toUpperCase();  
    const customTitle = alertMatch[2]?.trim() || undefined;  
    const contentNodes: ReactNode[] = [];  
      
    if (React.isValidElement<{ children?: ReactNode }>(firstChild)) {  
      const internalChildren = Children.toArray(firstChild.props.children);  
        
      if (internalChildren.length > 0 && typeof internalChildren[0] === 'string') {  
        const contentOnlyText = internalChildren[0].replace(/^\s*\[!(\w+)\]\s*[^\n]*\n?/, '');  
        const modifiedFirstChild = cloneElement(firstChild, { key: 'first-child-content' }, contentOnlyText, ...internalChildren.slice(1));  
        contentNodes.push(modifiedFirstChild);  
      } else {  
        contentNodes.push(firstChild);  
      }  
    }  
  
    contentNodes.push(...elementChildren.slice(1));  
  
    return { isAlert: true, alertType, title: customTitle, content: contentNodes };  
  }, [children]);  
  
  if (isAlert) {  
    return (  
      <GitAlert type={alertType} title={title}>  
        {content}  
      </GitAlert>  
    );  
  }  
  
  return (  
    <blockquote className={className}>  
      {content}  
    </blockquote>  
  );  
};

src\components\ui\markdown\parsers\alert.tsx

  CAUTION: FluentShieldError20Regular,  
}  
  
const AlertIcon: FC<{  
  type: keyof typeof typedIconMap  
  title?: string  
}> = ({ type, title }) => {  
  const finalType = type || 'NOTE'  
  const Icon = typedIconMap[finalType] || typedIconMap.NOTE  
  const typePrefix = title || (finalType[0] + finalType.toLowerCase().slice(1));  
  
  return (  
    <span  
@@ -57,9 +58,8 @@ export const AlertIcon: FC<{  
  )  
}  
  
export const GitAlert: FC<{ type: string; title?: string; children: React.ReactNode }> = (props) => {  
  const { type, title, children } = props  
  
  const upperType = type.toUpperCase() as keyof typeof borderColorMap  
  return (  
    <blockquote  
@@ -71,10 +71,10 @@ export const GitAlert = (props: { type: string; text: string }) => {  
        'not-italic',  
      )}  
    >  
      <AlertIcon type={upperType as any} title={title} />  
      <br />  
  
      {children}  
    </blockquote>  
  )  
}

The following is the historical processing.

Thus, the regex and parsing for this part have been modified to enhance the display of custom titles. The file path is src\components\ui\markdown\parsers\alert.tsx.

import clsx from 'clsx'
import type { MarkdownToJSX } from 'markdown-to-jsx'
import { blockRegex, Priority } from 'markdown-to-jsx'
import type { FC } from 'react'

import {
  FluentShieldError20Regular,
  FluentWarning28Regular,
  IonInformation,
} from '~/components/icons/status'

import { Markdown } from '../Markdown'

const textColorMap = {
  NOTE: 'text-blue-500 dark:text-blue-400',
  IMPORTANT: 'text-accent',
  WARNING: 'text-amber-500 dark:text-amber-400',
  TIP: 'text-green-500 dark:text-green-400',
  CAUTION: 'text-red-500 dark:text-red-400',
} as any

const borderColorMap = {
  NOTE: 'before:bg-blue-500 before:bg-blue-400',
  IMPORTANT: 'before:bg-accent',
  WARNING: 'before:bg-amber-500 dark:before:bg-amber-400',
  TIP: 'before:bg-green-500 dark:before:bg-green-400',
  CAUTION: 'before:bg-red-500 dark:before:bg-red-400',
} as any

const typedIconMap = {
  NOTE: IonInformation,
  IMPORTANT: FluentWarning28Regular,
  WARNING: FluentShieldError20Regular,
  TIP: IonInformation,
  CAUTION: FluentShieldError20Regular,
}

export const AlertIcon: FC<{
  type: keyof typeof typedIconMap
  title?: string // Define title
}> = ({ type, title }) => { // Add title
  const finalType = type || 'NOTE'
  const Icon = typedIconMap[finalType] || typedIconMap.NOTE
  const typePrefix = title || (finalType[0] + finalType.toLowerCase().slice(1)) // If title has value, get title, otherwise keep it the same as before

  return (
    <span
      className={clsx('mb-1 inline-flex items-center', textColorMap[finalType])}
    >
      <Icon
        className={clsx(
          `shrink-0 text-3xl md:mr-2 md:self-start md:text-left`,
          typedIconMap[finalType] || typedIconMap.NOTE,
        )}
      />

      {typePrefix}
    </span>
  )
}

/**
 *
 * > [!NOTE]
 * > Highlights information that users should take into account, even when skimming.
 */
const ALERT_BLOCKQUOTE_R =
  /^(> \[!(?<type>NOTE|IMPORTANT|WARNING|TIP|CAUTION)\](?<title>.*?))(?<body>(?:\n *>.*)*)(?=\n{2,}|$)/ // Modify regex to include title retrieval

export const AlertsRule: MarkdownToJSX.Rule = {
  match: blockRegex(ALERT_BLOCKQUOTE_R),
  order: Priority.HIGH,
  parse(capture) {
    return {
      raw: capture[0],
      parsed: {
        ...capture.groups,
      },
    } as any
  },
  react(node, output, state) {
    const { type, body, title } = node.parsed // Add title
    let first = true // Control the replacement logic for replace, the first line removes >, the rest replace with <br>
    const bodyClean = body.replace(/^> */gm, (match: string) => first ? (first = false, '') : '<br>').trim() // Modify replacement logic

    return (
      <blockquote
        className={clsx(borderColorMap[type], 'not-italic')}
        key={state.key}
      >
        <!-- Add title parsing, adjust line break logic -->
        <AlertIcon type={type as any} title={title} /> 
        

        <Markdown
          allowsScript
          className="not-prose w-full [&>p:first-child]:mt-0"
        >
          {bodyClean}
        </Markdown>
      </blockquote>
    )
  },
}

After the modification, the following effect can be achieved. If there are multiple alerts together, remember to separate them with <div />.

> [!WARNING] Any title text
> Other text content

> [!NOTE] Any title text
> Other text content

Warning

Any title text
Other text content

Note

Any title text
Other text content

This article is synchronized and updated to xLog by Mix Space.
The original link is https://blog.lolita.best/posts/blog/ShiroModifySumika


Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.