npm package: @krystalnaroo/ai-chat-vue3
Description: Vue 3 UI widget for AI Chat.
Latest version: 0.1.17

# @krystalnaroo/ai-chat-vue3

## 安裝

```sh
yarn add @krystalnaroo/ai-chat-vue3
```

## Live2D 資源

Live2D 模型與 `pixiDisplay.min.js` 需要被放到「使用者專案」的 `public/Live2D` 才能透過 URL 取得。

套件安裝時會自動複製到 `public/Live2D`。在 monorepo 環境下，若偵測到 `apps/vue3/public`，會優先複製到該位置。你也可以手動執行：

```sh
npx ai-chat-vue3-copy-live2d
```

如果你的 `public` 不在專案根目錄，可用 `--dest` 指定：

```sh
npx ai-chat-vue3-copy-live2d --dest /path/to/your/project
```

也可以用環境變數控制：

```sh
AI_CHAT_VUE3_ASSETS_DEST=/path/to/your/project npx ai-chat-vue3-copy-live2d
AI_CHAT_VUE3_ASSETS_DISABLE=1 npx ai-chat-vue3-copy-live2d
```

## Vite 設定（常見 CJS/ESM 相容問題）

若在使用 Vite 專案出現類似 `does not provide an export named 'default'` 的錯誤，請加入以下設定：

```js
// vite.config.(js|mjs)
export default defineConfig({
  optimizeDeps: {
    include: ["object-assign", "url", "eventemitter3", "earcut"]
  },
  ssr: {
    noExternal: ["@krystalnaroo/ai-chat-vue3"]
  }
});
```

## Live2D Core 引入

請在專案的 `index.html` 加上：

```html
<script src="/Live2D/pixiDisplay.min.js"></script>
```

### 1. 基本用法（懸浮按鈕）

直接在頁面引入，它會自己固定在右下角，可以拖來拖去吸附在瀏覽器邊。

```vue
<template>
  <AiChatWidget url="wss://你的後端WebSocket網址/ws/audio" />
</template>

<script setup>
import { AiChatWidget } from "@krystalnaroo/ai-chat-vue3";
</script>
```

### 2. 獨立頁面模式（全螢幕）

如果你想要點擊按鈕後，不是打開一個小視窗，而是跳轉到一個獨立的聊天頁面

**第一步：在首頁放按鈕**
設定 `openMode="page"`，並告訴它要跳轉去哪 (`pageUrl`)。

```vue
<!-- App.vue -->
<AiChatWidget url="wss://..." openMode="page" pageUrl="/ai-chat" />
```

**第二步：建立聊天頁面**
在你路由設定的頁面裡（例如 `views/AiChat.vue`），加上 `layout="fullscreen"`。

```vue
<!-- views/AiChat.vue -->
<template>
  <AiChatWidget url="wss://..."  layout="fullscreen" />
</template>
```

----

## Props

| 參數                      | 預設值                           | 說明                                                          |
|:------------------------|:------------------------------|:------------------------------------------------------------|
| `url`                   | -                             | 完整的 WebSocket 網址，例如 `wss://api.example.com/ws`。             |
| `basePath`              | `/ws`                         | 如果沒給完整 url，就會用目前的網域 + 這個路徑。                                 |
| `openMode`              | `panel`                       | `panel` (展開小面板) 或 `page` (開新分頁)。                            |
| `pageUrl`               | `/ai-chat`                    | 如果是用 `page` 模式，要跳轉去哪裡。                                      |
| `layout`                | `floating`                    | `floating` (懸浮按鈕) 或 `fullscreen` (全螢幕)。                     |
| `mergeAgentResponses`   | `true`                        | 是否將回應內容全部合併成一句話(包含語音)                                       |
| `maxRecordSeconds`      | `60`                          | 錄音最長可以錄幾秒。                                                  |
| `showClearHistory`      | `true`                        | 是否顯示清空歷史紀錄按鈕                                                |
| `enableHistoryStorage`  | `true`                        | 是否啟用 IndexedDB 歷史紀錄儲存                                       |
| `initialPosition`       | `{ bottom: 200, right: 1 }`   | 懸浮按鈕初始位置，支援 `top`/`bottom`/`left`/`right` 四個方向的組合（單位：px，距離視窗邊緣）。詳見下方說明。 |
| `fabBounce`             | `true`                        | 懸浮按鈕是否啟用上下抖動動畫（面板開啟或拖曳時自動暫停）。 |
| `autoReconnect`         | `true`                        | 是否啟用斷線自動重連（僅在面板開啟時觸發）。 |
| `maxReconnectAttempts`  | `10`                          | 最大重連嘗試次數，超過後停止重連。 |
| `reconnectInterval`     | `3000`                        | 每次重連的間隔時間（毫秒）。 |
| `sessionTimeout`        | `600`                         | 單次會話最大存活時間（秒），到期後自動斷線。預設 10 分鐘。 |
| `idleTimeout`           | `60`                          | 閒置超時時間（秒），無互動超過此時間後自動斷線。預設 1 分鐘。 |
| `welcomeMessage`        | `"👋 您好！..."`               | 歡迎訊息文字，設為空字串可關閉。支援 `\n` 換行。首次載入與每次重新開啟面板時各推送一次，自動重連時不重複。 |
| `quickQuestions`        | `[{category, items}, ...]`    | 快選問題列表，詳見下方說明。 |
| `quickQuestionMode`     | `2`                           | 快選問題顯示模式：`0` 不顯示、`1` 顯示在輸入框下方、`2` 顯示在歡迎訊息對話框中。 |

#### `initialPosition` 說明

設定懸浮按鈕的初始位置，使用 `top`/`bottom` 控制垂直、`left`/`right` 控制水平，數值為距離視窗邊緣的像素。每個軸只需指定一個方向，放開後仍會自動吸附到最近的邊緣。

```vue
<!-- 右下角（預設） -->
<AiChatWidget :initial-position="{ bottom: 200, right: 1 }" />

<!-- 左下角 -->
<AiChatWidget :initial-position="{ bottom: 200, left: 1 }" />

<!-- 右上角 -->
<AiChatWidget :initial-position="{ top: 100, right: 1 }" />

<!-- 左上角 -->
<AiChatWidget :initial-position="{ top: 100, left: 1 }" />
```

| 屬性 | 說明 |
|:---|:---|
| `top` | 距離視窗**頂部**的距離 (px) |
| `bottom` | 距離視窗**底部**的距離 (px) |
| `left` | 距離視窗**左側**的距離 (px) |
| `right` | 距離視窗**右側**的距離 (px) |

> 若同時指定 `top` 和 `bottom`，以 `top` 為優先；同時指定 `left` 和 `right`，以 `left` 為優先。

#### `quickQuestions` 說明

以分類方式提供快選問題按鈕，每個分類包含類別名稱與問題陣列。當使用者點擊後會直接發送該問題給 AI。

```vue
<AiChatWidget
  :quick-questions="[
    {
      category: '公司資訊',
      items: ['請介紹公司', '公司成立時間', '聯絡方式']
    },
    {
      category: '業務資訊',
      items: ['主要業務內容', '服務建案類型', 'iHome 系統介紹']
    }
  ]"
/>
```

| 屬性 | 類型 | 說明 |
|:---|:---|:---|
| `category` | String | 分類名稱，顯示為標題 |
| `items` | String[] | 該分類下的問題列表，每個項目為一個按鈕 |

----

## Slots
| Slot 名稱 | 用途 | 預設內容 |
| :--- | :--- | :--- |
| `fab-label` | 懸浮按鈕上的字 | `AI` |
| `header-title` | 面板最上面的標題 | `AI Chat` |

**範例：**

```html
<AiChatWidget ...>
  <template #fab-label>
    <span>客服</span>
  </template>
  <template #header-title>
    智慧小幫手
  </template>
</AiChatWidget>
```