作成している自作Webアプリで文字の表示方法を調整する機会がありましたので、記録に残しておきたいと思います。
自作Webアプリの内容は、IT技術に関する4拓問題を出題して回答するアプリです。
修正前
問題文で、日本語の文章とコードブロックの文章の見た目が変わらず、見づらくなってしまっていました。
修正後
コードブロックの背景が黒背景になって、見やすくなりました!
どのように解決したのか
解決したと偉そうに書いていますが、解決は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)
});
}
この部分がテキストを分割する核心部分です。テキスト全体をスキャンして:
- 普通のテキスト部分は「text」タイプとして保存
- トリプルバッククォートで囲まれた部分は「triple-code」タイプとして保存(バッククォート自体は削除)
- シングルバッククォートで囲まれた部分は「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>;
}
});
この部分では、分割したパーツをそれぞれ適切なスタイルでレンダリングします:
- 通常テキスト: 普通の要素として表示
- シングルコード: 浅い灰色背景の
要素として表示(インラインコード用)
- トリプルコード: 黒背景の
要素として表示(ブロックコード用)
- もし最初の行が言語名(例:javascript)なら、その言語をデータ属性に設定し、表示はしない
まとめ
まだまだ自作Webアプリは改良中ですが、このような見えやすさの部分も改良していくことで、より使いやすいアプリを目指していきたいと思います!