- feature-device-mgmt-tdd.md + api-device-mgmt.md(B 設備管理 TDD) - feature-model-sharing-tdd.md + api-model-sharing.md + PRD feature + 設計規格(C 模型共享三方規劃) - adr-020-ffmpeg-camera-indev.md(camera 三平台 indev) - PRD.md / TDD.md 索引增補 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
462 lines
33 KiB
Markdown
462 lines
33 KiB
Markdown
# 模型共享 設計規格 — visionA Cloud
|
||
|
||
> **L 級新功能**「模型共享」的 Design 部分(三方聯合規劃:PM 寫 PRD / Architect 寫 TDD / Design 寫本檔)。
|
||
>
|
||
> **核心定位**:本功能是**擴充既有模型庫 UI**,不是全新模組。100% 沿用既有 Design Tokens(`design-tokens.md`)、既有元件(`components.md`)、既有 `/models` 三區版型與 `ModelCard` / `ModelGrid` / `ModelSection` / `ModelFilters` / `ModelDetailClient`。本檔只定義「共享」這個維度帶來的新視覺 / 互動增量。
|
||
>
|
||
> **不新增 Design Token**。狀態色沿用既有 `chart-*` token 與 §2.1 半語義約定。
|
||
>
|
||
> 對應頁面:`/models`(列表擴充)+ `/models/[id]`(詳情頁擴充成 owner / 公開兩態)+ 新增「共享對象設定」互動(Dialog)+ 新增「共享模型庫」瀏覽維度。
|
||
>
|
||
> 配套格式參考:`pages.md` §8、`flows/flow-model-upload.md`、`components.md`。
|
||
|
||
---
|
||
|
||
## 0. 名詞與待確認前提(先給 PM / Architect)
|
||
|
||
本設計基於以下**假設**,凡標 🔶 者為「需 PM 確認需求細節」、標 🔷 者為「需 Architect 確認技術可行性」。設計會標出因假設不同而分歧的 UI 分支。
|
||
|
||
| # | 名詞 / 假設 | 說明 | 標記 |
|
||
|---|-----------|------|------|
|
||
| A1 | **公開對象(visibility)三態** | 假設分 `private`(僅自己)/ `public`(所有登入使用者可見)/ `shared`(指定對象)三種 | 🔶 PM:是否只做 private/public 兩態?「指定對象」Phase 0 是否需要? |
|
||
| A2 | **「指定對象」的粒度** | 假設可指定「個別使用者(by email)」。是否還有「群組 / 組織 / 團隊」概念? | 🔶 PM + 🔷 Architect:有無 org/team 資料模型? |
|
||
| A3 | **可見模型的三分類** | 列表要區分「我的(owner)/ 公開(public)/ 共享給我的(shared-with-me)」 | 🔶 PM:分類命名與是否需要「我公開出去的」獨立檢視 |
|
||
| A4 | **共享 = 唯讀** | 假設別人共享 / 公開給我的模型我**只能檢視 + 下載 + 燒錄**,不能改名 / 刪除 / 再共享 | 🔶 PM:可否「再共享」(re-share)?可否下載? |
|
||
| A5 | **profile 頁 = 擴充既有 detail** | 假設「模型 profile 頁」就是擴充現有 `/models/[id]`,依 viewer 身份切 owner 版 / 公開版,不另建路由 | 🔷 Architect:是否需要獨立 public 路由(如 `/m/[shareId]` 供分享連結)? |
|
||
| A6 | **分頁(pagination)** | 共享模型庫可能量大需分頁。假設用 offset/limit 或 cursor | 🔷 Architect:後端分頁機制(offset vs cursor)決定 UI(頁碼 vs 無限捲動 / 載入更多) |
|
||
| A7 | **搜尋範圍** | 假設搜尋 = 對「當前可見的所有模型」搜名稱(+ 選填分類 / 晶片) | 🔶 PM:搜尋要不要跨到「公開但未共享給我」的全庫探索? |
|
||
| A8 | **公開對象變更權限** | 假設只有 owner 能改自己模型的公開設定 | 🔷 Architect:權限在 API 層把關(前端只是 UI 便利) |
|
||
|
||
> **設計原則**:以上假設我採「最完整但可降級」的畫法——先畫三態 visibility + 三分類列表,PM 若砍到兩態 / 兩分類,UI 直接移除對應分支即可,不需重畫。
|
||
|
||
---
|
||
|
||
## 1. User Story(Design 視角)
|
||
|
||
> **作為** 一個 Kneron 開發者,
|
||
> **我想要** 把我的模型公開或共享給特定同事,並瀏覽別人共享給我的模型,
|
||
> **這樣** 團隊就能共用模型,不用每個人重複上傳 / 轉檔。
|
||
|
||
**體驗成功條件:**
|
||
- 使用者一眼能分辨列表中哪些是「我的 / 公開 / 共享給我的」(不只靠顏色,用 badge 文字 + 圖示雙編碼)
|
||
- 設定公開對象在 2 次點擊內可達(卡片選單 or 詳情頁按鈕 → Dialog)
|
||
- 共享模型庫量大時,瀏覽 / 搜尋 / 篩選 / 排序不卡頓、狀態清楚(載入 / 空 / 無結果 / 無權限四態齊備)
|
||
- 公開版 profile 頁不洩漏 owner 的私有操作(刪除 / 改公開設定按鈕對非 owner 隱藏)
|
||
|
||
---
|
||
|
||
## 2. 範圍與 Phase 0 降級
|
||
|
||
| 項目 | Phase 0(本次) | Phase 1+ |
|
||
|------|----------------|---------|
|
||
| Visibility 態 | private / public / shared(🔶 待 PM 砍) | + 到期共享 / 密碼保護連結 |
|
||
| 指定對象粒度 | 個別使用者 by email(🔶) | + 群組 / 組織 |
|
||
| 列表分類 | 我的 / 公開 / 共享給我的 三分類(🔶) | + 我公開出去的獨立檢視 |
|
||
| 分頁 | 「載入更多」按鈕 or 頁碼(依 🔷 A6) | 無限捲動 + 虛擬列表 |
|
||
| 排序 | 名稱 / 建立時間 / 共享時間(3 選項) | + 熱門度 / 下載數 |
|
||
| 篩選 | 既有 targetChip + 新增「共享狀態」維度 | + 分類 / 標籤 / owner |
|
||
| 搜尋 | 名稱模糊搜(前端 or 後端依 🔷 A6) | 全文 + tag |
|
||
| profile 公開版 | owner 版 / 公開版兩態 | 分享連結 `/m/[shareId]` |
|
||
| 再共享 / 轉讓 | 不做(🔶 A4) | 視需求 |
|
||
|
||
---
|
||
|
||
## 3. 資訊架構變更(IA)
|
||
|
||
沿用既有 `/models` 與 `/models/[id]`,不新增頂層導航。共享是「模型」的一個屬性維度,落在既有模型庫內。
|
||
|
||
```
|
||
/models (模型庫 — 擴充:新增「共享」瀏覽維度)
|
||
│
|
||
├── 檢視模式切換(新增):
|
||
│ ┌─ 依來源分區(既有:preset / converted / uploaded) ← 預設,僅「我的」模型
|
||
│ └─ 依共享關係分區(新增):我的 / 公開 / 共享給我的
|
||
│
|
||
├── 搜尋框(新增,跨當前檢視)
|
||
├── 篩選(擴充 ModelFilters:targetChip + 共享狀態)
|
||
├── 排序(新增:名稱 / 建立時間 / 共享時間)
|
||
├── 分頁 / 載入更多(新增)
|
||
│
|
||
└── /models/[id] (模型 profile 頁 — 擴充成雙態)
|
||
├── owner 版:既有全操作(改名 / 刪除 / 下載 / 燒錄 / 【新增】公開設定)
|
||
└── 公開 / 共享版:唯讀(檢視 / 下載 / 燒錄),隱藏 owner-only 操作
|
||
+ 新增「擁有者」資訊列(誰共享的 / 共享時間 / 公開對象徽章)
|
||
```
|
||
|
||
**🔶 待 PM 確認**:兩種檢視模式(來源分區 vs 共享關係分區)是否都要?還是共享關係分區直接取代來源分區?我建議**保留切換**(Tab 或 Segmented Control),因為「來源分區」對管理自己的模型仍有用,「共享關係分區」才是本功能重點。
|
||
|
||
---
|
||
|
||
## 4. 共享模型庫列表設計
|
||
|
||
### 4.1 檢視模式切換(新增元件 `ModelViewToggle`)
|
||
|
||
在 `/models` 頁標題列下、`ModelFilters` 上方,新增一個 Segmented Control(用既有 `Tabs` 元件實作,`variant` 沿用):
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 模型庫 [上傳模型] │
|
||
│ 管理、公開與共享你的 Kneron 模型 │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ ┌──────────────┬──────────────┐ │
|
||
│ │ 依來源 ● │ 依共享關係 │ ← Tabs(既有元件) │
|
||
│ └──────────────┴──────────────┘ │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ 🔍 [搜尋模型名稱...........] [晶片 ▾] [共享狀態 ▾] [排序 ▾]│ ← 篩選列(擴充)
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ (分區內容依所選檢視模式渲染) │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
- **依來源**(既有預設,不變):preset / converted / uploaded 三區,僅顯示「我的」模型。
|
||
- **依共享關係**(新增):三區 —
|
||
- 🟢 **我的**(owner):`chart-1` 色點呼應
|
||
- 🔵 **公開**(public,含我公開的 + 別人公開的,🔶 A7 待確認範圍):`chart-2`
|
||
- 🟣 **共享給我的**(shared-with-me):`chart-3`
|
||
|
||
> 沿用既有 `ModelSection` 的「色點 + 標題 + 計數」樣式,只是換分組維度。`SOURCE_DOT_CLASS` 邏輯延伸出一份 `SHARE_DOT_CLASS`,色 token 一致。
|
||
|
||
### 4.2 卡片上的共享標示(擴充 `ModelCard`)
|
||
|
||
既有 `ModelCard` 已有 status badge + targetChip badge + source badge。**新增一個 visibility badge**,放在 badge 列,用**圖示 + 文字雙編碼**(不僅靠顏色,滿足無障礙 De1):
|
||
|
||
| Visibility | 圖示(Lucide) | 文字 | 配色(沿用 token tint 風格) | 顯示條件 |
|
||
|-----------|--------------|------|---------------------------|---------|
|
||
| private | `Lock` | 私有 | `text-muted-foreground` + `border-border`(中性)| owner 檢視自己模型時 |
|
||
| public | `Globe` | 公開 | `border-chart-2/30 bg-chart-2/10 text-chart-2` | 任何人 |
|
||
| shared | `Users` | 已共享 / 共享給我 | `border-chart-3/30 bg-chart-3/10 text-chart-3` | owner 看到「已共享給 N 人」;receiver 看到「{owner} 共享」|
|
||
|
||
**owner 額外資訊**:private/shared/public 卡片右上角,owner 檢視時可在 badge 顯示對象數,如「公開」「共享 · 3 人」。
|
||
|
||
**receiver 視角**:卡片新增一行**次要資訊**(`text-xs text-muted-foreground`):「由 {ownerEmail} 共享 · {relativeTime}」。沿用 `RemoteDeviceBadge` 的相對時間格式化 util(剛剛 / X 分鐘前 / 絕對時間)。
|
||
|
||
**卡片操作差異**:
|
||
- owner 卡片:既有下載按鈕(若 downloadable)+ **新增卡片右上角 `⋮` 選單**(Dropdown),內含「公開設定」「刪除」等(見 §5 觸發點)。
|
||
- receiver 卡片:只保留「下載」(若 A4 允許)+ 點擊進 profile 頁。無 `⋮` 選單(無 owner 權限)。
|
||
|
||
> 🔷 **Architect 確認**:`ModelSummary` 需新增欄位 `visibility: 'private' | 'public' | 'shared'`、`ownerEmail?`、`sharedAt?`、`sharedCount?`(owner 視角)、`isOwner: boolean`。前端 UI 依這些欄位渲染,實際權限由 API 把關。
|
||
|
||
### 4.3 篩選擴充(擴充 `ModelFilters`)
|
||
|
||
既有 `ModelFilters` 只有 targetChip。新增「共享狀態」維度(第二個 `Select`),僅在「依共享關係」檢視模式下顯示:
|
||
|
||
```tsx
|
||
export interface ModelFilterValue {
|
||
targetChip: TargetChip | "all";
|
||
visibility?: "all" | "private" | "public" | "shared"; // 新增,僅共享關係檢視用
|
||
}
|
||
```
|
||
|
||
- targetChip 篩選跨所有分區作用(沿用既有行為)。
|
||
- visibility 篩選:選「公開」時只顯示 public 區,其餘區收合為 0(沿用「區內為空仍顯示標題 + 精簡空狀態」的既有慣例)。
|
||
|
||
### 4.4 搜尋框(新增)
|
||
|
||
- 位置:篩選列最左,`Input` + 前綴 `Search` icon(既有 `Input` 元件 h-9)。
|
||
- 行為:🔷 A6 —
|
||
- 若後端分頁:搜尋走後端(debounce 300ms,送 query),配合分頁。
|
||
- 若前端全量:即時前端 filter(無 debounce 需求)。
|
||
- Placeholder:`搜尋模型名稱...`
|
||
- 清除:有輸入時右側顯示 `X` 清除鈕(`ghost` icon button)。
|
||
- 無障礙:`role="searchbox"`、`aria-label="搜尋模型"`;搜尋無結果時 `aria-live="polite"` 播報結果數。
|
||
|
||
### 4.5 排序(新增 `ModelSortSelect`)
|
||
|
||
`Select`(h-9),選項:
|
||
|
||
| key | 文字 | 說明 |
|
||
|-----|------|------|
|
||
| `name-asc` | 名稱 A→Z | 預設(我的區)|
|
||
| `createdAt-desc` | 最新建立 | |
|
||
| `sharedAt-desc` | 最近共享 | 僅「共享給我的」區有意義;其他區隱藏此選項 |
|
||
|
||
🔷 排序在後端還前端由 A6 決定。若後端分頁 → 排序參數送後端。
|
||
|
||
### 4.6 分頁(新增,依 🔷 A6 二選一)
|
||
|
||
**方案 P1(cursor / 無限捲動友善)→「載入更多」按鈕**(Phase 0 建議,實作簡單、無頁碼狀態同步問題):
|
||
|
||
```
|
||
┌────────────────────────────────────────────┐
|
||
│ [卡片] [卡片] [卡片] [卡片] │
|
||
│ [卡片] [卡片] [卡片] [卡片] │
|
||
│ │
|
||
│ [ 載入更多 (顯示 24 / 87) ] │ ← Button variant=outline,loading 時 Spinner
|
||
└────────────────────────────────────────────┘
|
||
```
|
||
|
||
**方案 P2(offset / 傳統頁碼)→ 底部 Pagination 元件**(若後端只支援 offset):
|
||
|
||
```
|
||
‹ 上一頁 1 2 [3] 4 5 … 12 下一頁 ›
|
||
```
|
||
|
||
> 🔷 **Architect 裁決 A6**:後端分頁是 cursor 還 offset?我**建議 P1「載入更多」**——對「瀏覽型」的共享庫體驗較順、行動裝置友善、無「換頁後捲動位置重置」問題。若 Architect 已定 offset,則走 P2。**兩案我都出規格,選一即可,不需重畫卡片。**
|
||
|
||
**分頁 UX 細節**:
|
||
- 每頁 24 筆(3 欄 × 8 列 desktop;卡片沿用既有 `grid-cols-1 sm:2 lg:3 xl:4`)。
|
||
- 載入更多 loading:按鈕內 `Spinner` + 文字「載入中」;同時底部補 4 個 `Skeleton` 卡片(沿用既有 skeleton 樣式)。
|
||
- 搜尋 / 篩選 / 排序變更 → 重置分頁到第 1 頁 / 清空已載入。
|
||
|
||
---
|
||
|
||
## 5. 模型公開設定 UI(新增 `ModelVisibilityDialog`)
|
||
|
||
### 5.1 觸發點(兩處,owner-only)
|
||
|
||
1. **列表卡片** `⋮` 選單 →「公開設定」(§4.2 新增的 Dropdown)。
|
||
2. **profile 頁**(owner 版)操作列 → 新增按鈕 `[公開設定]`(`variant=outline` + `Globe` icon),放在既有「下載 / 刪除」旁。
|
||
|
||
> 🔶 PM:觸發點以上兩處是否足夠?是否需要在上傳完成後直接引導設定公開對象?(我建議上傳 Dialog 完成後加一句「模型預設為私有,之後可在詳情頁設定公開」的提示,不強迫當下設定。)
|
||
|
||
### 5.2 Dialog 版型(沿用既有 `Dialog`)
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────┐
|
||
│ 公開設定 — {模型名稱} [X] │
|
||
├──────────────────────────────────────────────────┤
|
||
│ 誰可以看到並使用這個模型? │
|
||
│ │
|
||
│ ○ 🔒 私有 只有你自己 │
|
||
│ ○ 🌐 公開 所有 visionA 使用者 │
|
||
│ ● 👥 指定對象 只有你選的人 │ ← RadioGroup
|
||
│ │
|
||
│ ┌─ 指定對象(僅「指定對象」選中時展開) ──────────┐ │
|
||
│ │ [輸入 email 加入........] [加入] │ │
|
||
│ │ ┌──────────────────────────────────────────┐ │ │
|
||
│ │ │ alice@corp.com 可檢視+下載 [✕] │ │ │
|
||
│ │ │ bob@corp.com 可檢視+下載 [✕] │ │ │
|
||
│ │ └──────────────────────────────────────────┘ │ │
|
||
│ └────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ⚠ 公開後,所有使用者都能下載此模型(若允許下載) │ ← public 選中時的提示
|
||
├──────────────────────────────────────────────────┤
|
||
│ [取消] [儲存變更] │
|
||
└──────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**元件組成(全部沿用既有 shadcn 元件):**
|
||
- 三態選擇:`RadioGroup`(🔷 若 components.md 未列 RadioGroup,需 Architect / Frontend 確認 shadcn 是否已有,或用 `Select` 降級)。
|
||
- 圖示:`Lock` / `Globe` / `Users`(與列表 badge 同圖示,維持一致性)。
|
||
- 指定對象加入:`Input`(email)+ `Button`(加入)。email 格式即時驗證(失焦時),錯誤走既有 `aria-invalid` + `border-destructive` 樣式。
|
||
- 對象清單:每列 = email + 權限標籤(Phase 0 固定「可檢視+下載」,🔶 A4)+ 移除鈕(`ghost` icon `X`)。
|
||
- public 警告:選 public 時顯示 `bg-amber-50 dark:bg-amber-950/30` 提示條(沿用 §2.1 半語義約定)。
|
||
- 底部:`[取消]`(`variant=outline`)+ `[儲存變更]`(`variant=default`,loading 時 Spinner)。
|
||
|
||
**🔶 待 PM 確認**:
|
||
- 「指定對象」若 Phase 0 不做,Dialog 降為 private / public 兩選(RadioGroup 兩項,移除展開區),版型不變。
|
||
- 權限粒度:Phase 0 是否只有「可檢視+下載」單一權限?還是要分「僅檢視 / 檢視+下載 / 檢視+燒錄」?
|
||
|
||
### 5.3 互動與回饋
|
||
|
||
| 動作 | 回饋 |
|
||
|------|------|
|
||
| 切換 private→public | 顯示 amber 警告條;儲存前不生效 |
|
||
| 加入 email(格式錯)| Input 紅框 + 下方「Email 格式不正確」|
|
||
| 加入 email(不存在的使用者)| 🔷 Architect:後端驗證,回錯時 toast「找不到使用者 {email}」|
|
||
| 加入重複 email | Input 提示「已在清單中」,不重複加入 |
|
||
| 儲存成功 | 關 Dialog + toast「已更新公開設定」+ 列表 / 卡片 badge 即時更新 |
|
||
| 儲存失敗 | Dialog 內 error banner「儲存失敗,請重試」,不關閉 |
|
||
| public→private(曾共享給人)| AlertDialog 二次確認「改為私有後,已共享的對象將無法再存取,確定?」|
|
||
|
||
### 5.4 UX Writing(本功能新增文案)
|
||
|
||
| key | 繁中 | English |
|
||
|-----|------|---------|
|
||
| `models.visibility.title` | 公開設定 | Visibility |
|
||
| `models.visibility.question` | 誰可以看到並使用這個模型? | Who can access this model? |
|
||
| `models.visibility.private` | 私有 | Private |
|
||
| `models.visibility.private.desc` | 只有你自己 | Only you |
|
||
| `models.visibility.public` | 公開 | Public |
|
||
| `models.visibility.public.desc` | 所有 visionA 使用者 | All visionA users |
|
||
| `models.visibility.shared` | 指定對象 | Specific people |
|
||
| `models.visibility.shared.desc` | 只有你選的人 | Only people you choose |
|
||
| `models.visibility.addEmail` | 輸入 email 加入 | Add by email |
|
||
| `models.visibility.publicWarning` | 公開後,所有使用者都能下載此模型 | Once public, anyone can download this model |
|
||
| `models.visibility.saved` | 已更新公開設定 | Visibility updated |
|
||
| `models.visibility.emailInvalid` | Email 格式不正確 | Invalid email format |
|
||
| `models.visibility.emailDuplicate` | 已在清單中 | Already in the list |
|
||
| `models.visibility.userNotFound` | 找不到使用者 {email} | User {email} not found |
|
||
| `models.visibility.revokeConfirm` | 改為私有後,已共享的對象將無法再存取,確定? | Setting to private revokes access for shared users. Continue? |
|
||
| `models.sharedBy` | 由 {email} 共享 | Shared by {email} |
|
||
|
||
---
|
||
|
||
## 6. 模型 profile 頁設計(擴充 `/models/[id]`)
|
||
|
||
### 6.1 設計取捨:擴充既有 detail,依身份切兩態
|
||
|
||
**決策:不另建路由**,沿用 `/models/[id]` + `ModelDetailClient`,依 `model.isOwner`(🔷 Architect 提供)分支渲染。理由:
|
||
- 既有 detail 頁已有完整資訊展示(描述 / 大小 / framework / inputShape / classes / checksum),共享 profile 需要的資訊 90% 重疊,重畫浪費。
|
||
- 唯一差異是「操作區權限」與「擁有者資訊」,用條件渲染即可。
|
||
|
||
> 🔷 **Architect 確認 A5**:若未來要「分享連結給未登入者 / 站外」,才需獨立 public 路由 `/m/[shareId]`(免登入、SEO 友善)。Phase 0 假設僅站內登入使用者可見,沿用 `/models/[id]` 即可。**若需求包含站外分享連結,請 PM 明確,會影響是否要新路由。**
|
||
|
||
### 6.2 兩態差異表
|
||
|
||
| 區塊 | owner 版(既有)| 公開 / 共享版(新增分支)|
|
||
|------|---------------|----------------------|
|
||
| 返回鈕 | `← 返回`(回 /models)| 同 |
|
||
| 標題 + badge 列 | name + targetChip + status + source | + **visibility badge**(公開 / 共享)|
|
||
| **擁有者資訊列**(新增)| 不顯示(自己就是 owner)| 顯示:`由 {ownerEmail} 共享 · {time}` + owner 頭像(首字母圓形,沿用 UserMenu avatar 樣式)|
|
||
| 操作列 | 下載 + 刪除 + **【新增】公開設定** | **僅**下載(若 A4 允許);隱藏刪除 / 公開設定 |
|
||
| 描述 / metadata 卡片 | 完整 | 完整(唯讀,相同)|
|
||
| classes 區塊 | 完整 | 完整(唯讀)|
|
||
| 燒錄至裝置(未來 F7+)| 列出自己在線裝置 | 同(共享模型也能燒錄到自己的裝置)|
|
||
|
||
### 6.3 公開版 profile 版型
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────┐
|
||
│ ← 返回 │
|
||
│ │
|
||
│ ResNet50-KL720 👥 共享給我 │ ← 標題 + visibility badge
|
||
│ [KL720] [就緒] [轉檔] │ ← 既有 badge 列
|
||
│ [⬇ 下載] │ ← 僅下載(無刪除/公開設定)
|
||
│ ┌──────────────────────────────────────────────────┐│
|
||
│ │ 👤 由 alice@corp.com 共享 · 3 天前 ││ ← 擁有者資訊列(新增)
|
||
│ └──────────────────────────────────────────────────┘│
|
||
│ │
|
||
│ ┌─ 模型描述 ─────────────────────────────────────┐ │ ← 既有卡片,唯讀
|
||
│ │ 一般物件偵測模型... │ │
|
||
│ │ 大小: 24.3 MB 建立: 2026/07/20 │ │
|
||
│ │ Framework: onnx Input: 1×3×224×224 │ │
|
||
│ │ Classes (80): [person][car][dog] +77 │ │
|
||
│ └────────────────────────────────────────────────┘ │
|
||
└──────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 6.4 擁有者資訊列規格(新增小元件 `ModelOwnerBar`)
|
||
|
||
- 容器:`flex items-center gap-2` + `bg-muted/50 rounded-md px-3 py-2 text-sm`
|
||
- 頭像:40px→改 24px(`h-6 w-6 rounded-full`)圓形,email 首字母,沿用 UserMenu avatar 配色
|
||
- 文字:`由 {ownerEmail} 共享 · {relativeTime}`(相對時間 util 沿用 RemoteDeviceBadge)
|
||
- 僅在非 owner 檢視時渲染
|
||
- 無障礙:`aria-label="模型擁有者資訊"`
|
||
|
||
---
|
||
|
||
## 7. 各狀態設計(空 / 載入 / 無權限 / 無結果)
|
||
|
||
沿用既有 `EmptyState` / `Skeleton` / toast,文案走本功能語氣。
|
||
|
||
| 狀態 | 觸發 | 呈現 | 文案 |
|
||
|------|------|------|------|
|
||
| **載入中** | 首次載入 / 換頁 / 搜尋 | 既有 `Skeleton` 卡片網格(8 個)| — |
|
||
| **空 · 共享給我的(無)** | 「共享給我的」區無資料 | `EmptyState` icon=`Users` | 標題「還沒有人共享模型給你」/ 描述「當同事把模型共享給你時,會出現在這裡」|
|
||
| **空 · 公開(無)** | 公開區無資料 | 精簡 inline 空狀態(沿用區內空狀態樣式)| 「目前沒有公開的模型」|
|
||
| **搜尋無結果** | 搜尋 query 無 match | `EmptyState` icon=`SearchX` + `[清除搜尋]` CTA | 標題「找不到符合『{query}』的模型」/ 描述「試試其他關鍵字或清除篩選」|
|
||
| **無權限** | 直接開別人私有模型的 `/models/[id]` | 全頁 `EmptyState` icon=`Lock` + `[返回模型庫]` | 標題「沒有權限檢視此模型」/ 描述「這個模型未公開或未共享給你」|
|
||
| **公開設定儲存失敗** | API 錯 | Dialog 內 error banner | 「儲存失敗,請重試」|
|
||
| **載入更多失敗** | 分頁 API 錯 | 「載入更多」按鈕轉為 `[重試]` + toast | 「載入失敗,點擊重試」|
|
||
|
||
**無權限狀態(`/models/[id]` 403)版型:**
|
||
```
|
||
┌──────────────────────────────────────┐
|
||
│ ← 返回 │
|
||
│ │
|
||
│ 🔒 │
|
||
│ 沒有權限檢視此模型 │
|
||
│ 這個模型未公開或未共享給你 │
|
||
│ │
|
||
│ [ 返回模型庫 ] │
|
||
└──────────────────────────────────────┘
|
||
```
|
||
> 🔷 Architect:403 vs 404 的取捨——為避免「模型是否存在」的資訊洩漏,建議私有模型對無權限者回 **404**(當作不存在),UI 走「找不到模型」而非「無權限」。**請 Architect 確認採 403 揭露存在 or 404 隱藏存在**,UI 兩版文案我都備。
|
||
|
||
---
|
||
|
||
## 8. 響應式(沿用既有斷點)
|
||
|
||
沿用 `pages.md` §11 的斷點與 `/models` 既有規則(Mobile 單欄 / Tablet 2 欄 / Desktop 3–4 欄)。本功能新增元素的響應式:
|
||
|
||
| 元素 | Mobile (<640) | Tablet (640–1024) | Desktop (≥1024) |
|
||
|------|--------------|-------------------|-----------------|
|
||
| 檢視模式 Tabs | 全寬 2 等分 | 同 | 靠左自然寬 |
|
||
| 篩選 / 搜尋 / 排序列 | **堆疊**(搜尋佔滿寬一行 + 篩選鈕 wrap 下一行)| 部分同行 wrap | 單行 |
|
||
| visibility badge | 顯示(badge 列 `flex-wrap` 既有)| 同 | 同 |
|
||
| 擁有者資訊列 | 全寬,email 過長 `truncate` | 同 | 同 |
|
||
| 公開設定 Dialog | `max-w-[calc(100vw-2rem)]`,對象清單可捲動 | `max-w-lg` | `max-w-lg` |
|
||
| 分頁「載入更多」| 全寬按鈕 | 置中 | 置中 |
|
||
| 頁碼(P2)| 精簡(僅 ‹ 頁 X/Y › )| 完整頁碼 | 完整頁碼 |
|
||
|
||
---
|
||
|
||
## 9. 無障礙(沿用既有 + 本功能新增)
|
||
|
||
- **visibility badge**:圖示(`aria-hidden`)+ 文字雙編碼,不僅靠顏色(De1)。SR 讀得到「公開」「已共享」文字。
|
||
- **檢視模式 Tabs**:Radix Tabs 內建 `role="tablist"` + Arrow 鍵導航 + `aria-selected`。
|
||
- **公開設定 RadioGroup**:Arrow 鍵切換選項、`aria-checked`、每選項有可見 label。
|
||
- **email 加入**:`Input` `aria-label`;加入成功後焦點回 input 便於連續加入;對象清單移除鈕 `aria-label="移除 {email}"`。
|
||
- **搜尋框**:`role="searchbox"`;無結果 `aria-live="polite"` 播報「找到 0 個模型」。
|
||
- **載入更多**:`aria-label` 含當前 / 總數「載入更多,已顯示 24 之 87」。
|
||
- **狀態改變**(儲存 / 共享):toast 走既有 Sonner(`aria-live` 內建)。
|
||
- **觸控目標**:所有新增互動元素(`⋮` 選單鈕、移除鈕、Tab、清除鈕)≥ 44×44px(`⋮` 用 `icon` size = 36px 需注意,建議 profile / 卡片上用 `size=icon` 但外圍點擊區補到 44px,或用既有 button 慣例)。🔷 Frontend 實作注意。
|
||
- **對比度**:所有新 badge 用既有 `chart-*` token tint 風格(`/10` 底 + 純色文字),與既有 source badge 同做法,既有已通過 WCAG AA。Dark Mode 由 token 自動處理。
|
||
|
||
> **Dark Mode**:本功能不新增顏色,全用既有 token(`chart-1/2/3`、`muted`、`amber-*` 半語義),Dark Mode 自動生效。無需獨立 Dark 版設計。(本任務 A 層 Dark Mode 截圖延到 B 層 / 實作期補,因為零新 token、純沿用。)
|
||
|
||
---
|
||
|
||
## 10. 沿用 vs 新增總表
|
||
|
||
### 10.1 沿用既有(不改)
|
||
| 既有資產 | 用途 |
|
||
|---------|------|
|
||
| Design Tokens 全部 | 零新增 |
|
||
| `ModelGrid` | 卡片網格 + skeleton + 空狀態(分頁時擴充「載入更多」)|
|
||
| `ModelSection` | 分區色點 + 標題 + 計數(共享關係分區沿用)|
|
||
| `Card` / `Badge` / `Button` / `Dialog` / `AlertDialog` / `Select` / `Input` / `Tabs` / `EmptyState` / `Skeleton` / `Sonner` | 全部 |
|
||
| `RemoteDeviceBadge` 的相對時間 util | 共享時間 / owner 資訊列 |
|
||
| UserMenu avatar 樣式 | owner 頭像 |
|
||
| §2.1 amber 半語義 | public 警告條 |
|
||
|
||
### 10.2 新增(Design 定義,Frontend 實作)
|
||
| 新增 | 類型 | 基底 |
|
||
|------|------|------|
|
||
| `ModelViewToggle` | 元件 | `Tabs` |
|
||
| visibility badge(含於 ModelCard 擴充)| 元件擴充 | `Badge` + Lucide `Lock`/`Globe`/`Users` |
|
||
| ModelCard `⋮` owner 選單 | 元件擴充 | `DropdownMenu`(🔷 確認 shadcn 已有)|
|
||
| `ModelFilters` 共享狀態維度 | 元件擴充 | `Select` |
|
||
| 搜尋框 | 元件擴充 | `Input` + `Search` icon |
|
||
| `ModelSortSelect` | 元件 | `Select` |
|
||
| 分頁「載入更多」or Pagination | 元件 | `Button` / 新 Pagination(🔷 A6 二選一)|
|
||
| `ModelVisibilityDialog` | 元件 | `Dialog` + `RadioGroup` + `Input` + `Button` |
|
||
| `ModelOwnerBar` | 元件 | `Card`/div + avatar |
|
||
| ModelDetailClient owner/公開雙態分支 | 頁面擴充 | 條件渲染 |
|
||
| 無權限 / 搜尋無結果 / 共享空狀態 | 狀態 | `EmptyState` |
|
||
|
||
---
|
||
|
||
## 11. 給三方互審的重點清單
|
||
|
||
### 需 PM 確認需求細節 🔶
|
||
1. **A1 Visibility 態數**:private/public/shared 三態 vs 只 private/public 兩態?「指定對象」Phase 0 要不要做?
|
||
2. **A2 指定對象粒度**:個別 email vs 群組/組織/團隊?
|
||
3. **A3 列表分類**:「我的 / 公開 / 共享給我的」三分類命名 OK 嗎?要不要「我公開出去的」獨立區?
|
||
4. **A4 共享權限**:receiver 能否下載?能否再共享(re-share)?權限是否分「僅檢視 / 檢視+下載 / 檢視+燒錄」?
|
||
5. **A7 搜尋範圍**:搜尋只搜「可見的」還是要能「探索全站公開庫」?
|
||
6. **檢視模式**:「依來源」與「依共享關係」兩種 Tab 都保留,還是共享關係取代來源?
|
||
7. **觸發引導**:上傳完成後要不要引導設定公開對象?
|
||
|
||
### 需 Architect 確認可行性 🔷
|
||
1. **A5 / 6.1 profile 路由**:沿用 `/models/[id]` 雙態 vs 需獨立 public 路由 `/m/[shareId]`(站外分享連結)?
|
||
2. **A6 分頁機制**:後端 cursor(→ P1 載入更多)還 offset(→ P2 頁碼)?搜尋 / 排序在前端還後端?
|
||
3. **`ModelSummary` 新欄位**:`visibility` / `ownerEmail` / `sharedAt` / `sharedCount` / `isOwner` 可否由 API 提供?
|
||
4. **7 無權限回應碼**:私有模型對無權限者回 403(揭露存在)還 404(隱藏存在)?影響 UI 文案。
|
||
5. **RadioGroup / DropdownMenu**:shadcn 元件庫是否已含這兩個?未含則需先補 or 用 Select 降級。
|
||
6. **email 對象驗證**:加入指定對象時,後端能否即時驗證使用者存在?
|
||
|
||
---
|
||
|
||
## 12. Verification(本設計任務自檢)
|
||
|
||
- **De-A1 三斷點**:§8 已定義 mobile/tablet/desktop 三斷點行為(純規格 + ASCII 版型;實際截圖於 prototype / 實作期補,本階段為規格文件無渲染產出)。
|
||
- **De-A2 對比度**:零新 token,全沿用既有 `chart-*` tint + `muted` + `amber` 半語義,既有已過 WCAG AA(design-tokens.md 已確認)。
|
||
- **De-A3 無 hardcode**:本檔所有色彩引用皆為 token 名(`chart-1/2/3`、`muted-foreground`、`amber-50` 半語義約定),無裸 hex / rgb。
|
||
- **De-A4 三態覆蓋**:§7 已定義空 / 載入 / 無結果 / 無權限 / 失敗多態。
|
||
- **De-A5 觸控目標**:§9 已標註新增互動元素 ≥ 44px 需求 + `⋮` 鈕注意事項。
|
||
- **De-B1 互動五狀態 / De-B2 Dark Mode / De-B4 三方互審**:延到 milestone(互動狀態沿用既有元件既有態;Dark Mode 零新 token 自動生效;三方互審即本檔 §11 待 PM/Architect 回審)。
|
||
- **No silent failures**:所有 🔶🔷 未定項已明確標「待 PM/Architect 確認」+ 對應假設,無「TBD 無負責人」。
|
||
- **Doc 同步**:本檔為新增;pages.md / components.md 的增補建議見下方回報(本任務未直接改,避免撞名覆蓋,交 Orchestrator 決定增補方式)。
|