Mori

DESIGN BOOK · v1.0

Desktop UI

SHELL · COMPONENTS · MOCKUPS

Mori desktop app 的 UI 規範:主視窗(sidebar shell)、chat panel、floating widget、chat bubble、picker、modal。實作位置 src/*.tsx 跟對應 CSS。

Design Reference

原始 UI 設計稿(docs/design/mori-desktop-ui.png)— 含 13 個元件 / mockup 區塊:

Mori desktop UI design system

Main Window Shell

主視窗(label main)固定 880×600 預設,resizable + minWidth 640 minHeight 440。 左側 196px sidebar + 右側內容區。

Sidebar Tabs

Icon 全套 line-art SVG(src/icons.tsx),stroke=currentColor 跟 theme 走。

idiconlabel主內容
chatIconChat對話scrollable thread + bottom input bar(v0.4.0+ topbar 顯示 profile + provider + model)
profilesIconProfilesProfilesvoice / agent profile list + 切換 + 編輯 modal + token 估算 chip(v0.4.3+)+ 加入範本 / 開啟資料夾 按鈕(v0.4.1+)
configIconConfigConfigconfig.json typed form + raw JSON + corrections.md + theme picker
memoryIconMemoryMemory~/.mori/memory/ list + 全文搜尋 + 編輯 modal
annuliIconAnnuliAnnuliannuli vault status / SOUL.md preview / MEMORY sections / events / sleep button(Wave 4 MVP)
skillsIconSkillsSkills當前 profile 啟用 skills 列表 + parameters
depsIconDepsDepsoptional 工具 / 模型 偵測 + 安裝
logsIconHelpLogsv0.4.0+ 事件 log:date / kind / provider filter + 每行可展開 JSON。除錯第一站

Sidebar 底部另有 IconSun / IconMoon theme toggle button(brand-3)。

實作

Chat Panel(對話 tab)

結構由上到下:

  1. top bar — mode chip(IconChat / IconKeyboard / IconSleep)· active profile(v0.4.0+,例 AGENT-01.翻譯助手 / USER-00.純文字輸入,依 mode 自動切)· provider · model + 三個 icon button(IconSleep/IconSun 休眠 toggle · IconRefresh 重置 · IconConfig status)。Profile chip 在 phase=done / profile-switched event 觸發時自動 refresh
  2. thread — scrollable;user 訊息靠右 sand 泡泡(暖色)、Mori 靠左 forest-soft 泡泡(葉脈綠)、IconTool tool chip 顯示 LLM 用了哪個 skill
  3. progress chip — 錄音中(秒數 + 音量條)/ 轉錄中 / 思考中
  4. input bar — 永遠在底部:IconMic / IconStop mic button + textarea(auto-grow)+ 送出 button

實作:src/ChatPanel.tsx + src/chat-panel.css。對應 IPC: get_conversation 拉歷史、submit_text 送文字、toggle 切錄音、 reset_conversation 清歷史。

Listening Mode(Hey Mori 待命,v0.6.0+)

第 3 條獨立 mode 軸:不用按熱鍵,對麥克風喊「Hey Mori」就觸發錄音 + STT + agent。跟 Alt+N(voice dictation Hold)/ Ctrl+Alt+N(agent toggle)完全獨立,可任意切。

進入方式
Tray menu(systray icon 右鍵)→「Hey Mori 待命」項目
離開方式
Tray menu 切回別的 mode / 點托盤圖示再選一次
底層
mori-tauri spawn examples/scripts/mori-wake-listener.py(Python openWakeWord),subprocess 讀麥克風持續偵測。退出時 kill_on_drop 收掉。
偵測命中
先播 wake-ack 應答音(下段)→ 觸發跟 Alt+N Hold-press 等效的 start_recording 流程 → VAD 偵測 user 講完自動 stop → STT → agent
必備檔
~/.mori/wakeword/hey-mori.onnx(v0.6.0+ binary 內嵌 TTS-only generic 版,首次啟動自動解壓)+ ~/.mori/wake-venv/bin/python(Deps tab 一鍵裝)

Wake-ack 應答音

偵測到後播一段 Mori 的回應(「嗯,我在聽」之類),user 不用盯 floating sprite 就知道可以下指令了。先播完才開麥克風,避免從喇叭出來的聲音被 mic 收回污染 STT(沒做 echo cancellation)。

VAD silence-stop(v0.6.0+)

過去 wake event 觸發後固定錄滿 max_record_secs 才停,長指令會被截。v0.6.0 改成即時 VAD:

Settings UI

Config tab → Voice subtab 滑到底兩個 section:

欄位預設說明
threshold0.5偵測門檻,越高越嚴格。光講「Mori」就觸發 → 拉到 0.65
silence_stop_secs1.5VAD 多久靜音算 user 講完
silence_threshold_rms0.012VAD 靜音判定(背景吵就拉 0.02~0.05)
max_record_secs30安全上限(秒)
wake_ack_enabledtrue關掉就完全靜音

自訓 wake-word(Linux only,v0.6.0+)

預設 hey-mori.onnx 是 TTS-only generic 訓練版,英文「Hey Mori」普遍適用但對非英語母語 user 命中率可能偏低。Linux + GPU 進階 user 可:

詳見 examples/scripts/README.wake-train.md。Windows / macOS 暫未支援(piper-phonemize wheel 不齊 + macOS 沒 CUDA)。

Floating Widget(桌面常駐 sprite)

label
floating
大小
160×160 固定,transparent + decorationless + alwaysOnTop + skipTaskbar
內容
sprite + aura ring + ripple + chip overlay
實作
src/FloatingMori.tsx + src/floating.css

狀態 visual

Chat Bubble(獨立視窗)

label
chat_bubble
定位策略
啟動時 visible+off-screen(-10000,-10000),收 chat-bubble-show event 移到 sprite 下方 8px
大小
360 寬 × 依內容 offsetHeight 同步(max 480)
內容
多行 wrap、可滾動、字級 13px、行高 1.55

實作:src/ChatBubble.tsx + src/chat-bubble.css

Picker(Ctrl+Alt+P profile picker)

label
picker
大小
520×280,3-item carousel(prev / current / next)
互動
↑↓ 轉動列表 / Tab 切組 / Enter 確認 / Esc 取消
實作
src/Picker.tsx + src/picker.css

Modal

Profile editor / Memory editor / Status modal 共用 modal 結構:

<div class="mori-modal-backdrop">
  <div class="mori-modal">
    <div class="mori-modal-header">...title + ✕...</div>
    <div class="mori-modal-body">...form / textarea...</div>
    <div class="mori-modal-footer">
      <button class="mori-btn danger">刪除</button>
      <div class="mori-modal-footer-right">
        <button class="mori-btn">還原</button>
        <button class="mori-btn primary">儲存</button>
      </div>
    </div>
  </div>
</div>

Form Components

FormRow

<div class="mori-form-row">
  <div class="mori-form-row-label">
    <span>label</span>
    <span class="mori-form-row-hint">hint</span>
  </div>
  <div class="mori-form-row-input">...input / select / textarea...</div>
</div>

Buttons

class背景用途
.mori-btn白透predefined neutral
.mori-btn.primaryforest主動作 / 儲存
.mori-btn.danger刪除 / 不可逆
.mori-btn.ghost無底close ✕ / 次要
.mori-btn.small小 paddingrow inline

KV Table(API keys / routing.skills)

<div class="mori-kv-table">
  <div class="mori-kv-row">
    <input class="mori-kv-key"   placeholder="..." />
    <input class="mori-kv-value" placeholder="..." />
    <button class="mori-btn small ghost">✕</button>
  </div>
  <button class="mori-btn small">+ 新增</button>
</div>

Window Sizes Summary

windowsizeresizabletransparentskipTaskbar
main880×600
floating160×160
chat_bubble360×56(dyn)
picker520×280

Phase Status

主視窗 UI 完成度(2026-05-13):