蒼時弦也
蒼時弦也
資深軟體工程師
發表於

助手對話介面 - Clean Architecture in TypeScript

這篇文章是 Clean Architecture in TypeScript 系列的一部分,你可以透過 Leanpub 提前閱讀內容。

透過 AI 協力開發,我們可以節省許多以往需要花費力氣的時間。不過開發的流程並不會有太大的變化,這次我們會採用可以馬上確認成果的方式進行開發,因此會從前端的介面開始著手。

初始畫面

我們希望在初期先建構一個可以跟助手(Assistant)對話的介面,基本上會需要訊息區域、對話輸入的區域,這部分我們會直接撰寫程式碼來作為提示。

我們先更新 src/client.tsx 把主畫面 App.tsx 引用進來,可以根據專案習慣使用像是 @/view 這種 Alias 設定,後續的範例都會直接使用這種風格。

1import { render } from "hono/jsx/dom";
2
3import { App } from "@/view/App";
4
5// @ts-ignore - This is a client-side only file
6const root = document.getElementById("root");
7if (root) {
8	render(<App />, root);
9}

接下來將 src/view/App.ts 也建立起來,裡面預計會引用一個 Chat 元件,用於後續我們製作的對話視窗。

 1import { FC } from "hono/jsx/dom";
 2
 3import Chat from "@/view/Chat"
 4import "@/view/style.css"
 5
 6export const App: FC = () => {
 7	return (
 8		<Chat />
 9	);
10};

接著將 TailwindCSS 的基礎設定放到 src/view/style.css 裡面

1@import "tailwindcss";

最後先放一個 Hello World 的訊息到 src/view/Chat.tsx 裡面,確認 Hono JSX 的設定都正確運作,而且我們所需要的功能都按照預期。

 1import { FC } from "hono/jsx/dom";
 2
 3export const Chat: FC = () => {
 4	return (
 5		<div>
 6			<h1>Chat</h1>
 7			<p>Welcome to the chat!</p>
 8		</div>
 9	);
10}

上述的任務可以利用 GitHub Copilot 的程式碼補完協力,雖然不是像代理模式(Agent Mode)那樣完全自動,但這可以幫助我們在過程中反思設計。

樣式設定

因為我們已經有 DESING.md 可以用作樣式的參考,所以可以先將 src/view/style.css 進行更新,利用 AI 來撰寫相應的設定。

受限於 TailwindCSS 4 比較新,我們會需要讓 AI 預先閱讀過 Theme 設定文件Aider 為例子,會給予以下指示。

/read DESIGN.md
/web https://tailwindcss.com/docs/theme

完成之後,給予下面的提示詞,就會自動將 @theme 的內容產生出來。

According to `DESING.md` to add TailwindCSS theme config to `src/view/style.css`

根據使用的模型、編輯器不同結果可能會有差異,但我們預期至少要有類似下面這段 CSS 實作。

 1@theme {
 2  /* 顏色 */
 3  --color-primary: #4F46E5;
 4  --color-secondary: #6B7280;
 5  --color-background: #F9FAFB;
 6  --color-card: #FFFFFF;
 7  --color-text-primary: #1F2937;
 8  --color-text-secondary: #6B7280;
 9  
10  /* 排版 */
11  --font-sans: 'Inter', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', 'Arial', 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
12  --text-base: 1rem;
13  --font-weight-medium: 500;
14  --font-weight-bold: 700;
15  --leading-normal: 1.5;
16  
17  /* 間距 */
18  --spacing-0: 0;
19  --spacing-2: 0.5rem;
20  --spacing-4: 1rem;
21  --spacing-6: 1.5rem;
22  --spacing-8: 2rem;
23  
24  /* 邊框 */
25  --radius-md: 0.375rem;
26  
27  /* 陰影 */
28  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
29}

透過這種方式我們可以在編輯樣式上節省不少時間,必要時可能會需要自己去微調來確保符合預期,不一定要完全依賴使用 AI 編輯,有時候成本會遠大於自己修改一兩個字。

對話介面

接下來就要實際製作對話介面,我們預定要在 Chat 元件中增加 ChatMessageChatInput 兩個元件,此時腦中應該有大致上該有的實作雛形,但是細節我們可以先不考慮。

 1import { FC } from "hono/jsx/dom";
 2
 3export const Chat: FC = () => {
 4	// TODO: Manage Chat Message State
 5
 6	return (
 7		<>
 8			{ /* TODO: Chat Message */ }
 9			{ /* TODO: Chat Input */ }
10		</>
11	);
12}

因為不需要自己實作,所以我們只需要把預期看到的成果用註解的方式記錄下來,在這個階段我們需要的是清楚知道 Chat 元件會保存對話訊息的狀態,而呈現則由 ChatMessageChatInput 兩個元件負責。

完成這個階段的處理後,先 git commit 把實作記錄下來,以免後續 AI 接手處理發生問題時,我們無法退回最後一次正常的狀態。

接下來根據你使用的 AI 工具給指示,目前已經設定 readonly: DESIGN.mdeditable: src/view/Chat.tsx 兩組設定,明確限制我們只會改動到 Chat 元件,而且可以參考 DESIGN.md 的設計規範來套用介面。

Implement `ChatMessage` and `ChatInput` component for `Chat`.

* Create new component file
* Apply style according design token use TailwindCSS
* Use English in UI
* Use conventional commit in English

因為 AI 工具是具有隨機性的,上述的提示詞不一定能很好的運作。以撰寫這個段落時的情況,即使要求 Commit 訊息使用英文最後還是用中文撰寫,這可能是工具的 System Prompt 改變所造成。

但是在 AI 生成後,我們應該預期要能看到如下可操作的介面。

對話介面效果

以往可能需要先學會怎麼管理 React 的狀態,以及實作對應的邏輯在調整樣式才能夠有這樣的效果,但我們可以在短短數分鐘內的幾此嘗試就取得這樣的成果,已經比過去開發更有效率不少。

生成的實作中仍可能會有一些問題,像是使用了 setTimeout 來模擬發送訊息的效果但沒有考慮 React 會重新產生的特性,一方面是因為我們並沒有交代不需要這樣的機制,另一方面也反應出 AI 不一定能判斷這種細節。