AI 應用元件
同步打字網格
多格同步打字展示 React 元件:同一句提示詞在 N 個不同外殼的輸入框裡同時逐字打出,內建對話框、終端機、搜尋列、文件編輯器四種外殼,適合比較多個介面或展示「到哪都能用」。
把同一句提示詞同時打進 2–6 個不同外觀的輸入框:所有格子共用一個打字時鐘,逐字同步出現;打完後可以讓每格淡入一段假結果,再清空重打或輪播下一句。內建 chat、terminal、search、editor 四種外殼,也能用 render 傳入自己的殼。適合產品首頁說明「同一個功能在對話、指令列、搜尋、編輯器裡都能用」,或並排比較多個介面的回應格式。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add synced-typing-gridPlayground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
55
0
2
編輯中的呼叫預設屬性
<SyncedTypingGrid/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
安裝
npx webberui@latest add https://webberui.com/r/synced-typing-grid.json或在 components.json 設定 registries 後,改用 @webberui/synced-typing-grid 安裝。
使用
import { SyncedTypingGrid } from "@/components/ui/synced-typing-grid";
<SyncedTypingGrid
text="幫我把上週的客訴整理成三點摘要"
columns={2}
jitter={40}
cells={[
{ id: "chat", label: "對話助理", variant: "chat", result: <p>已整理成三點摘要</p> },
{ id: "terminal", label: "指令列", variant: "terminal" },
{ id: "search", label: "站內搜尋", variant: "search" },
{
id: "custom",
label: "自訂外殼",
render: (typed, done) => (
<div className="rounded-lg border p-3 text-sm">
{typed}
{done ? " ✓" : null}
</div>
),
},
]}
/>Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
text | string | string[] | — | 要同步打出的句子;傳陣列時依序輪播多句 |
cells | SyncedTypingCell[] | — | 各格的介面設定(建議 2–6 格) |
typeSpeed | number | 55 | 每個字的間隔(毫秒) |
startDelay | number | 300 | 每句開始打字前的等待(毫秒) |
pauseDuration | number | 1800 | 打完後停留多久再進入下一句或重打(毫秒) |
loop | boolean | true | 是否循環播放;false 時播完最後一句就停住 |
jitter | number | 0 | 每格確定性的時間偏移上限(±毫秒),製造「幾乎同步」的錯落感;0 為完全同步 |
columns | number | 2 | 大螢幕(lg 以上)的欄數,1–6;手機固定 1 欄、sm 為 2 欄 |
playing | boolean | — | 受控播放狀態;未提供時由元件內部管理 |
defaultPlaying | boolean | true | 非受控模式的初始播放狀態 |
onPlayingChange | (playing: boolean) => void | — | 播放狀態變動時回呼 |
onDone | (sentenceIndex: number) => void | — | 每句全部格子打完時回呼,參數為句子索引 |
showControls | boolean | true | 是否顯示播放/暫停按鈕 |
label | string | "同步打字展示" | 根元素的無障礙名稱 |
pauseLabel | string | "暫停" | 暫停按鈕文字 |
playLabel | string | "播放" | 播放按鈕文字 |
replayLabel | string | "重播" | 播完後的重播按鈕文字 |
className | string | — | 透傳到最外層容器 |
SyncedTypingCell
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
id | string | — | 唯一識別碼,作為 React key |
label | string | — | 格子頂部顯示的介面名稱 |
variant | "chat" | "terminal" | "search" | "editor" | "chat" | 內建外殼樣式;傳了 render 時忽略 |
render | (typed: string, done: boolean) => ReactNode | — | 自訂外殼:取得目前已打出的字串與是否打完,回傳任意節點 |
result | ReactNode | — | 整句打完後淡入的結果節點(例如假回應) |
細節
- 單一共享時鐘:一個計時器推進共用的經過時間,每格據此算出該顯示幾個字,所以所有格子逐字同步;
jitter大於 0 時,每格依索引雜湊得到固定的時間偏移(不用亂數),SSR 與瀏覽器算出的值一致,每次重打的節奏也一樣。 - 切字用
Array.from,以 code point 為單位,中文與 emoji 不會被切成亂碼。 - 傳入陣列時依序輪播;
loop為false時播完最後一句停住,按鈕會變成「重播」。 - 暫停後再播放會從原本的位置接續,不會從頭開始。
- 節奏可用 CSS 變數覆寫:
--wb-synced-typing-caret(游標閃爍週期,預設1s)、--wb-synced-typing-result-duration(結果淡入時長,預設320ms);送出鈕與狀態點的顏色轉換沿用--wb-duration-fast與--wb-ease-out。
可及性
- 根元素為
role="group",以label作為無障礙名稱 - 逐字打字的區域設定
aria-hidden,另放一段sr-only的完整句子,螢幕閱讀器不會逐字播報 - 播放/暫停按鈕為原生
button,文字隨狀態切換(暫停/播放/重播),可用鍵盤操作並有 focus-visible 外框 - 元件不在視口內時(IntersectionObserver)自動暫停,捲回畫面後接續
- 使用者系統開啟「減少動態效果」時,直接顯示完整句子與結果,不打字、游標不閃爍,也不顯示播放按鈕