0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

自作Webアプリで文字の表示方法調整

Posted at

作成している自作Webアプリで文字の表示方法を調整する機会がありましたので、記録に残しておきたいと思います。

自作Webアプリの内容は、IT技術に関する4拓問題を出題して回答するアプリです。

修正前

スクリーンショット 2025-05-11 131148.png

問題文で、日本語の文章とコードブロックの文章の見た目が変わらず、見づらくなってしまっていました。

修正後

スクリーンショット 2025-05-11 131701.png

コードブロックの背景が黒背景になって、見やすくなりました!

どのように解決したのか

解決したと偉そうに書いていますが、解決はClaude3.7にお任せしました。
Claude3.7はformatTextWithCodeBlocks関数というのを作成してくれました。

後追いになりますが、どのような関数を作ってくれたのか紐解いてみます。

formatTextWithCodeBlocks関数の概要

この関数は、テキスト内にある「コードブロック」を見つけて、それらを特別なスタイルで表示するためのものです。
コードブロックには2種類あります:

  • トリプルバッククォート(```)で囲まれたブロック: 複数行のコードを表示
  • シングルバッククォート(`)で囲まれたブロック: 文章内の単語や短いコードを強調表示

ステップ1: 初期準備

// テキストが空なら何もしない
if (!text) return null;
  
// 結果を格納するための配列を作成
const parts = [];

ステップ2: コードブロックを検出するための正規表現

const regex = /(```[\s\S]*?```)|(`[^`]*?`)/g;
  • ([\s\S]*?): トリプルバッククォート(```)で囲まれた任意の文字列を検出

  • [\s\S]*?: 任意の文字(改行を含む)を「最小限」マッチさせる

  • |: または

  • ([^]*?): シングルバッククォート()で囲まれた任意の文字列を検出

  • [^]*?`: バッククォート以外の任意の文字を「最小限」マッチさせる

  • g: グローバルフラグ(テキスト全体を検索する)

ステップ3: テキストを順番に処理

let lastIndex = 0; // 最後に処理した位置
let match;
  
// パターンにマッチする部分をすべて検出
while ((match = regex.exec(text)) !== null) {
    // コードブロックの前にある普通のテキスト部分を保存
    if (match.index > lastIndex) {
      parts.push({
        type: 'text',
        content: text.substring(lastIndex, match.index)
      });
    }
    
    // トリプルバッククォートの場合
    if (match[1]) {
      // バッククォート自体を取り除く(前後の3文字ずつ)
      const codeContent = match[1].substring(3, match[1].length - 3);
      parts.push({
        type: 'triple-code',
        content: codeContent
      });
    } 
    // シングルバッククォートの場合
    else if (match[2]) {
      // バッククォート自体を取り除く(前後の1文字ずつ)
      const codeContent = match[2].substring(1, match[2].length - 1);
      parts.push({
        type: 'single-code',
        content: codeContent
      });
    }
    
    // 次の検索開始位置を更新
    lastIndex = match.index + match[0].length;
}

// テキストの最後まで処理したら、残りの部分も普通のテキストとして保存
if (lastIndex < text.length) {
  parts.push({
    type: 'text',
    content: text.substring(lastIndex)
  });
}

この部分がテキストを分割する核心部分です。テキスト全体をスキャンして:

  1. 普通のテキスト部分は「text」タイプとして保存
  2. トリプルバッククォートで囲まれた部分は「triple-code」タイプとして保存(バッククォート自体は削除)
  3. シングルバッククォートで囲まれた部分は「single-code」タイプとして保存(バッククォート自体は削除)

ステップ4: 分割したパーツをスタイル付きでレンダリング

// 分割したパーツを適切なスタイルでレンダリング
return parts.map((part, index) => {
  if (part.type === 'single-code') {
    // インラインコード用のスタイル
    return (
      <code 
        key={index} 
        className="px-1.5 py-0.5 mx-0.5 bg-gray-100 dark:bg-gray-700 ..." 
      >
        {part.content}
      </code>
    );
  } else if (part.type === 'triple-code') {
    // ブロックコード用のスタイル
    let language = '';
    let codeContent = part.content;
    
    // 言語名が指定されていたら抽出
    const firstLineMatch = part.content.match(/^(\w+)\s*\n([\s\S]*)$/);
    if (firstLineMatch) {
      language = firstLineMatch[1]; // 言語名
      codeContent = firstLineMatch[2]; // 残りのコード
    }
    
    return (
      <pre className="p-4 my-3 bg-gray-900 text-gray-100 ...">
        <code>{codeContent}</code>
      </pre>
    );
  } else {
    // 普通のテキスト
    return <span key={index}>{part.content}</span>;
  }
});

この部分では、分割したパーツをそれぞれ適切なスタイルでレンダリングします:

  1. 通常テキスト: 普通の要素として表示
  2. シングルコード: 浅い灰色背景の要素として表示(インラインコード用)
  3. トリプルコード: 黒背景の
    要素として表示(ブロックコード用)
  • もし最初の行が言語名(例:javascript)なら、その言語をデータ属性に設定し、表示はしない

まとめ

まだまだ自作Webアプリは改良中ですが、このような見えやすさの部分も改良していくことで、より使いやすいアプリを目指していきたいと思います!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?