シンプルなAIアプリ3の続きです。
ブラウザでAIに送信するプロンプトを編集し、AIが返した結果を表示できるようになったので、UIを調整します。
UIの調整
index.htmlを更新する
ブラウザに表示する画面を検討しました。
そして、次のようなイメージにすることを決定しました。
AIアプリ +-------------------------------------------------+ |AIの回答 | AIに送るプロンプト: | |div | +------------------+ | |(id="ai-result") | | textarea | | | | | (id="ai-prompt") | | | | | | | | | | | | | | | | | | | +------------------+ | | | button | | | (id="send-button")| +-------------------------------------------------+
上のイメージに従って、index.html を更新します。
<body>
    <h1>AIアプリ</h1>
    <div id="content">
        <div id="left-panel">
            <h2>AIの回答</h2>
            <div id="ai-result"></div>
        </div>
        <div id="right-panel">
            <label for="ai-prompt">AIに送るプロンプト:</label>
            <textarea id="ai-prompt"></textarea>
            <button id="send-button">送信</button>
        </div>
    </div>
</body>styles.cssの作成
せっかくAIに質問できるプログラムを作成したので、CSS作成をAIに手伝ってもらいましょう。
以下が、AIに送信するプロンプトです。
CSSファイルの作成を手伝ってください。
(実際のプロンプトでは、ここにindex.htmlを添付します。)
上のHTMLに対して、次の条件をすべて満たすCSSを作って欲しいです。
* ai-resultの縦スクロールを有効にしてください。それ以外はすべてスクロールを無効化してください。
* htmlのサイズを100vwと100vhにしてください。
* bodyの幅は80%、高さは95%にしてください。
(など…、他にも細かい指定を記載しましたが、省略します)
以下は、簡単なイメージ図です。
(実際のプロンプトでは、ここにイメージ図を添付します。)というプロンプトを入力して、AIにCSSを作ってもらいました。
ただし、1回で完全なCSSは作成できません。AIが生成したCSSをひな型にして自分で調整するか、細かな指定を追加して何度も生成すると良いでしょう。
以下が、調整や繰り返しによって完成した styles.css です。
html {
    height: 100vh;
    width: 100vw;
    overflow: hidden;
    /* html全体でのスクロールを無効化 */
}
body {
    width: 80%;
    height: 95%;
    margin: auto;
    display: flex;
    flex-direction: column;
    /* body内を縦に配置 */
    overflow: hidden;
    /* body全体でのスクロールを無効化 */
}
#content {
    display: flex;
    flex-direction: row;
    /* content内を横に配置 */
    flex: 1;
    /* contentの高さを残りの高さに合わせる */
    overflow: hidden;
    /* content全体でのスクロールを無効化 */
}
#left-panel {
    width: 66.66%;
    /* 2:1の比率で66.66% */
    height: 100%;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    /* left-panel全体でのスクロールを無効化 */
}
#ai-result {
    width: 100%;
    flex: 1;
    /* ai-resultの高さを残りの高さに合わせる */
    overflow-y: auto;
    /* ai-resultのみ縦スクロールを有効化 */
}
#right-panel {
    width: 33.33%;
    /* 2:1の比率で33.33% */
    height: 100%;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    /* right-panel全体でのスクロールを無効化 */
}
#ai-prompt {
    width: 100%;
    height: 100%;
    /* ai-promptの高さを残りの高さに合わせる */
    resize: none;
    /* サイズ変更不可 */
    flex: 1;
    box-sizing: border-box;
}
#send-button {
    /* ボタンの高さをコンテンツに合わせて調整 */
    height: fit-content;
    padding: 8px 16px;
    background-color: #09F;
    color: #FFF;
    border-radius: 4px;
    border: none;
    margin: 4px 4px 4px auto;
    &:hover {
        background-color: #06A;
    }
}
/* その他のスタイル(任意)*/
body,
h1,
h2,
label,
button {
    font-family: sans-serif;
}
h1 {
    text-align: center;
    margin-bottom: 1em;
}
#left-panel h2 {
    margin-bottom: 0.5em;
}
label {
    margin-bottom: 0.5em;
}実行結果
さっそく使ってみましょう。CSSを変えただけなので、ブラウザの画面を更新するだけです。

Markdownへの対応
テストのためにいろいろなプロンプトを入力してみると、次のようなフォーマットでAIが回答してくることが多いことが分かりました。
「赤い」といえば、色々なものが思い浮かびます。いくつか例を挙げると:
* **色:** これは最も直接的な答えですね。トマト、リンゴ、血、バラ、夕焼けなど、赤い色のもの全てです。
* **感情・状態:** 怒り、興奮、危険、熱、炎症など、赤い色に関連付けられる感情や状態を表す言葉も考えられます。
(以下省略)これは、Markdown記法 というフォーマットです。(※ 私はまったく知らなかったのですが、今回、AIに質問することで知りました。)
Gemini の文章生成は、 Markdown記法を生成しやすいようです。
そこで、AIから返ってきたMarkdown記法の結果を、適切に処理して、画面表示してみましょう。
script.tsの修正
ブラウザへの表示部分なので、src/client/script.ts を改良します。
以下が、改良した src/client/script.ts です。変更されていないGetPrompt()関数のコードは省略しています。
import { marked } from 'https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js';
import { IClientToServer, IServerToClient } from "../common/interface.ts";
// 送信ボタンのクリックイベントを登録する
const sendButton = document.getElementById("send-button");
if (sendButton instanceof HTMLButtonElement) {
    sendButton.addEventListener("click", async () => {
        try {
            // id="ai-prompt"のtextareaからプロンプトを得る。
            const prompt = GetPrompt();
            // プロンプトが空なら、それを表示して終了する。
            if (!prompt) {
                UpdateAiResult("プロンプトがありません");
                return;
            }
            // プロンプトをサーバーの"ai-api"に送信する。
            const sendData: IClientToServer = { prompt: prompt };
            const response = await fetch("ai-api", {
                method: "POST",
                body: JSON.stringify(sendData)
            });
            // サーバーから結果が返ってきたら、それを表示する。
            const json = await response.json() as IServerToClient;
            UpdateAiResult(marked.parse(json.result), 'HTML');
        } catch (error) {
            // エラーがあった場合は、それを表示する。
            console.error('Error:', error);
            UpdateAiResult(`エラーが発生しました: ${(error as Error).message}`);
        }
    });
}
/**
 * id="ai-result"の内部をresultに変更する。
 * @param result テキストまたはHTML
 * @param type resultをテキストとして扱うか、HTMLとして扱うか
 */
function UpdateAiResult(result: string, type: 'text' | 'HTML' = 'text') {
    const aiResult = document.getElementById("ai-result");
    if (aiResult) {
        if (type === 'text') {
            aiResult.innerText = result;
        }
        else {
            aiResult.innerHTML = result;
        }
    }
}import で Markdown 記法を HTML に変換するライブラリをインポートしています。
marked.parse()関数で AIの返したテキストを処理することで、Markdown 記法が HTML になります。
UpdateAiResult()関数は、従来通りテキストを受け取るケースと、Markdownで処理されたHTMLを受け取るケースに対応する修正を行いました。
なお、importで変換ライブラリをインポートするために、index.htmlのscript.jsを読み込むところで、以下のようにtype="module"を追加する必要があります。
<script src="script.js" type="module"></script>実行結果

Markdown記法の、**色:** のような表記がHTMLの <strong>色:</strong> に変換され、適切に表示されるようになりました。
今回までの作業状況
以下に最新のディレクトリ構成と状況を記載します。
/project-root
|-- deno.json
|-- /public
|   |-- index.html(Update:UI変更とMarkdown対応)
|   |-- styles.css(New)
|   |-- script.js(Update:script.tsから生成される)
|-- /src
|   |-- /common
|   |   |-- interface.ts
|   |-- /client
|   |   |-- script.ts(Update:Markdown対応)
|   |-- /server
|       |-- main.ts 
  
  
  
  
コメント