Eagle AI — Field InspectionEagle AI — 現場巡檢
An installable, local-first field-observation app. Walk a site → capture observations (photo + voice/text + annotation) → review → assemble a report → issue → PDF. Part of Eagle AI's thesis: turn field evidence into payment confidence.一款可安裝、本地優先的現場觀察 App。巡檢工地 → 擷取觀察項(照片+語音/文字+標註)→ 審查 → 彙整報告 → 出具 → PDF。這是 Eagle AI 核心主張的一環:把現場證據轉化為付款信心。
This document describes the current build (a working PWA, deployed) and, where relevant, what the real product will do. It covers the two editions (MVP / Full), the end-to-end user flows, the platform constraints of shipping as a PWA, and the roadmap seams where a real backend plugs in.本文件說明目前的版本(一個已部署、可運作的 PWA),並在相關處說明正式產品將會如何運作。內容涵蓋兩個版本(MVP/完整版)、端到端的使用流程、以 PWA 形式上線的平台限制,以及未來正式後端接入的介面點。
The whole flow at a glance整體流程一覽
flowchart LR A["Set up project
location · drawings"]:::full --> B["Start report
the walk"] B --> C["Capture observation
photo · voice/text · markup"] C --> C C --> D["Review / assemble
pick observations"] D --> E{"Preview
+ Confirm"} E -->|Confirm| F["Issued report
PDF + public link"] F --> G["Send
recipients"]:::full C -.not issued.-> H["Observations
Not-issued tab"]:::full H -.aging.-> D classDef full fill:#e6f1eb,stroke:#1a7050,color:#14513a;
Two surfaces, one system兩個介面,同一套系統
Eagle AI is delivered on two platform surfaces — web and app — over the same system. This document is the app (Field Inspection PWA). It is a reference implementation of the field-capture half; the payment-verification half lives on the web surface.Eagle AI 以兩個平台介面交付 —— 網頁與 App —— 建立在同一套系統之上。本文件說明的是 App(現場巡檢 PWA)。它是「現場擷取」這一半的參考實作;「付款驗證」那一半則在網頁介面上。


01 Editions — MVP vs Full版本 — MVP 與完整版
One codebase produces two builds via a build-time flag (VITE_EDITION) plus a per-feature registry (src/edition.ts), deployed as two Cloudflare Workers. No git fork — a fork would recreate the design drift we exist to avoid.同一套程式碼透過建置期旗標(VITE_EDITION)加上一份逐功能的註冊表(src/edition.ts)產生兩個建置版本,部署為兩個 Cloudflare Worker。不做 git 分叉 —— 分叉只會重現我們正是要避免的設計偏移。
| MVP(對外) (external) | Full(對內) (internal) | |
|---|---|---|
| Worker | field-report | field-report-full |
| URL | field-report.jaredsong.workers.dev | field-report-full.jaredsong.workers.dev |
| Audience對象 | Testers / clients測試者/客戶 | Our team內部團隊 |
| Deploy部署 | Manual手動 — npm run deploy | Auto自動 on push to推送至 main |
| Features功能 | Confirmed only僅已確認的功能 | Everything we're building所有開發中的功能 |
git push updates only Full.單純的 git push 只會更新完整版。 MVP must be deployed deliberately with npm run deploy. Same core flow and the same API in both — they share one KV + R2; data is isolated per account (your email is the sync key), not per edition.MVP 必須以 npm run deploy 刻意部署。兩個版本的核心流程相同、API 也相同 —— 共用同一組 KV + R2;資料以帳號為單位隔離(你的 email 就是同步金鑰),而非以版本隔離。How a feature is promoted功能如何升級到對外版
The differences are feature flags. 'full' = internal-only; flip a line to 'mvp' to promote a feature to external, then deploy MVP. Because it's a build-time constant, full-only branches tree-shake out of the MVP bundle — not hidden, absent.差異都是功能旗標。'full'=僅限內部;把某一行改成 'mvp' 即可把該功能升級到對外版,再部署 MVP。由於它是建置期常數,full 專屬的分支會從 MVP 打包中被搖樹移除(tree-shake) —— 不是隱藏,而是根本不存在。
flowchart LR R["src/edition.ts
FEATURES registry"] -->|"line = 'mvp'"| M["MVP bundle
feature present"] R -->|"line = 'full'"| X["MVP bundle
feature tree-shaken OUT"] R -->|any| FUL["Full bundle
everything present"]
Feature registry (gated features only)功能註冊表(僅列受控功能)
Core screens — Home, Reports, Walk/capture, report draft/issue/PDF, Attendance, Preferences, voice — are always on and not gated.核心畫面 —— 首頁、報告、巡檢/擷取、報告草稿/出具/PDF、出席、偏好設定、語音 —— 一律開啟,不受旗標控制。
| Feature功能 | Edition版本 | What it is說明 |
|---|---|---|
emailLogin | mvp | Email sign-in + onboarding + cloud sync (in both)Email 登入+首次引導+雲端同步(兩版皆有) |
photos | full | “Observations” gallery (bottom-nav tab)「觀察項」相簿(底部導覽分頁) |
profileTab | full | Worker-profile tab (MVP uses gear → Preferences)個人檔案分頁(MVP 改用齒輪 → 偏好設定) |
projectCreate | full | Create / set up a project建立/設定專案 |
floorplanSetup | full | Upload drawing sheets, assign to floors上傳圖說、指派到樓層 |
drawings | full | Drawing-sheet viewer (by-floor / all-sheets)圖說檢視器(依樓層/全部圖說) |
pinDrop | full | Floor-plan location pin per observation每個觀察項在平面圖上的圖釘定位 |
prevReportLink | full | Link an observation to a prior one把觀察項連結到先前的觀察項 |
referenceDetail | full | Link an observation to a drawing-set sheet把觀察項連結到某張圖說 |
recordLock | full | Passcode + Face ID gate (revisions · deletions · setup · personal info)密碼+Face ID 保護(修訂 · 刪除 · 設定 · 個人資料) |
reportTrades | full | Editable “Trades observed today” (MVP derives from observations)可編輯的「今日現場工種」(MVP 由觀察項自動推導) |
reportDistribution | full | Issue → send sheet + Sent “Delivered to” (MVP issues straight to Sent)出具 → 派送表單+「已送達對象」(MVP 出具後直接進入已寄送) |
projectCreate · floorplanSetup · specMap are declared in the registry but not yet wired as direct gates — those flows are full-only in practice because their only entry points (the Home Drawings / floor-plan cards and the Profile tab) are gated by drawings / profileTab. Notifications is not gated — the Home bell + full-page centre ship in both editions.projectCreate · floorplanSetup · specMap雖在註冊表中宣告,但尚未接成直接的旗標;這些流程實務上仍是完整版專屬,因為唯一的入口(首頁的圖說/平面圖卡片與個人檔案分頁)本身就被 drawings / profileTab 控制。通知未受旗標控制 —— 首頁鈴鐺+整頁通知中心在兩個版本皆會出貨。
What MVP has vs what Full addsMVP 有什麼、完整版又多了什麼
The flags above cascade — one gated tab removes a whole surface. In plain terms, MVP is the capture → assemble → issue → PDF spine on a single project; Full adds the gallery, the worker profile, project setup, the location/link richness, distribution, and the security gate.上面的旗標會層層擴散 —— 一個被關掉的分頁就抽掉一整個介面。簡單說,MVP 是單一專案上的擷取 → 彙整 → 出具 → PDF主幹;完整版再加上相簿、個人檔案、專案設定、位置/連結的豐富功能、派送,以及安全鎖。
| Area面向 | MVP (external)(對外) | Full (internal) adds(對內)新增 |
|---|---|---|
| Bottom-nav tabs底部導覽分頁 | Home · Reports首頁 · 報告 | + Photos照片 · Profile個人檔案 |
| Projects專案 | One prebuilt starter project一個預建的起始專案 | Create / configure projects建立/設定專案 — letterhead, report prefix, floor + elevation structure, per-floor drawings信箋抬頭、報告前綴、樓層+立面結構、各樓層圖說 |
| Capture (the walk)擷取(巡檢) | Camera · multi-photo · voice · text · sketch · markup · pause/resume · QR zone相機 · 多張照片 · 語音 · 文字 · 手繪 · 標註 · 暫停/續行 · QR 區域 | — (same)— (相同) |
| Observation location觀察項位置 | Zone picker (building → floor → area)區域選擇(棟別 → 樓層 → 區域) | + pin-drop圖釘定位 on the floor plan於平面圖上 · elevation box立面框選 |
| Observation links觀察項連結 | — | Reference detail參考圖說 (drawing sheet + box)(圖說+框選) · Previous-observation link前次觀察項連結 |
| Photos gallery照片相簿 | — | Observations tab觀察項分頁 — grid · search · filter sheet · lightbox · Issued / Not-issued格狀 · 搜尋 · 篩選表單 · 燈箱 · 已出具/尚未出具 |
| Drawings圖說 | — | Home首頁 floor-plan cards平面圖卡片 + Drawings viewer圖說檢視器 (by-floor / all sheets)(依樓層/全部圖說) |
| Assemble彙整 | Pick observations · attendance · pre-issue check · preview; trades auto-derived (read-only)挑選觀察項 · 出席 · 出具前檢查 · 預覽;工種自動推導(唯讀) | + editable “Trades observed today”可編輯的「今日現場工種」 |
| Issue & deliver出具與派送 | Issue → Sent出具 → 已寄送 | + recipient distribution收件派送 send sheet派送表單 · Sent已寄送顯示 “Delivered to”「已送達對象」 |
| Server-rendered A4 · Save PDF (share sheet / download)伺服器產生的 A4 · 儲存 PDF(分享表單/下載) | — (same)— (相同) | |
| Reports & revisions報告與修訂 | Drafts · issued · revisions (sorted by issue date)草稿 · 已出具 · 修訂(依出具日期排序) | — (same)— (相同) |
| Notifications通知 | Bell + full-page centre鈴鐺+整頁通知中心 | — (same; ungated)— (相同;未受旗標控制) |
| Worker profile個人檔案 | — (settings via gear → Preferences; account fields there)— (設定經齒輪 → 偏好設定;帳號欄位放在那裡) | Profile tab個人檔案分頁 · practices · live stats ·事務所 · 即時統計 · Field Record現場紀錄 (PDF + public page + QR poster)(PDF+公開頁面+QR 海報) |
| Security安全 | — | Record lock紀錄鎖 — passcode + Face ID gate on revisions · deletions · setup · personal info密碼+Face ID,保護修訂 · 刪除 · 設定 · 個人資料 |
| Sync · photos · auth同步 · 照片 · 認證 | Email-key sync · content-addressed photos · pull-to-refresh · email-code login · Face ID re-entryEmail 金鑰同步 · 內容定址照片 · 下拉重新整理 · Email 驗證碼登入 · Face ID 再登入 | — (same)— (相同) |
02 Site setup工地設定 Full
Before a walk, a project carries the context the report reads. Setup lives under Profile → project (Full edition; MVP works against a seeded/simple project).巡檢之前,專案承載了報告所引用的背景資料。設定位於個人檔案 → 專案(完整版;MVP 則對著一個預置/簡化的專案運作)。
- Project — created under one practice (its letterhead). Address, client, prevailing building code, drawing set, spec, distribution list.專案 —— 建立於某一間事務所之下(使用其信箋抬頭)。含地址、業主、適用建築法規、圖說集、規範、派送清單。
- Location — buildings → floors → zones/areas. This tree is the single source for where an observation is and for the zone QR codes.位置 —— 棟別 → 樓層 → 區域。這棵樹是「觀察項在哪裡」以及區域 QR 碼的唯一來源。
- Drawings — three grouped kinds in All drawings: Floor plans (one per floor) · Elevations (per-building N/S/E/W faces) · Sheets (the drawing-set register). Every upload gets a crop + rotate preview first; PDF uploads render as images with a multi-page page picker (pick one).圖說 —— 在所有圖說中分成三類:平面圖(每樓一張)· 立面圖(每棟 東/南/西/北 各面)· 圖說(圖說集清單)。每次上傳都先經裁切+旋轉預覽;上傳 PDF 會轉成圖片並提供多頁選頁器(選其中一頁)。
- Zone QR codes — auto-generated per floor/zone from Location. Print all or one as a label sheet or A4 poster; “Generated with Eagle AI” on every QR PDF. Scanning a label deep-links (
#loc=…) and stages that zone for 15 minutes.區域 QR 碼 —— 依「位置」逐樓層/逐區域自動產生。可整批或單張印成標籤頁或 A4 海報;每份 QR PDF 都標示「Generated with Eagle AI」。掃描標籤會深層連結(#loc=…)並將該區域暫存 15 分鐘。
03 The walk — capture巡檢 — 擷取
The core loop. Start report opens a walk; each observation is a photo (or several) plus a voice or text note and optional annotation.核心循環。開始報告會開啟一次巡檢;每個觀察項是一張(或數張)照片,加上語音或文字備註,以及可選的標註。
flowchart TB S([Start report]) --> W["Walk in progress
timer running, parked as a draft"] W --> Cap[Capture observation] Cap --> P["Photos
camera / album"] Cap --> N["Note
voice → Whisper, or text"] Cap --> M["Markup
annotate the photo"]:::full Cap --> L["Location
zone / pin / elevation box"]:::full P & N --> W W --> R([Review / assemble]) classDef full fill:#e6f1eb,stroke:#1a7050,color:#14513a;
- Voice notes — recorded audio → Workers AI Whisper → text (Mandarin transcribed to Traditional Chinese via OpenCC). A manual text path is always available.語音備註 —— 錄音 → Workers AI Whisper → 文字(國語經 OpenCC 轉成繁體中文)。手動輸入文字的路徑一律保留。
- Staged zone (QR) — a scanned label docks a green chip on Start report; the first capture pre-fills that location.暫存區域(QR) —— 掃描標籤後,會在「開始報告」上停靠一個綠色標籤;第一次擷取會自動帶入該位置。
- Resume — a parked walk shows a resume card on Home; the walk-in-progress is never silently dropped.續行 —— 暫存的巡檢會在首頁顯示續行卡片;進行中的巡檢絕不會被默默丟棄。
- Pull-to-refresh — every content screen: drag down → sync, with the Eagle-wing spinner + a “Pull to refresh” / “Reloading…” label.下拉重新整理 —— 每個內容畫面皆有:下拉 → 同步,搭配 Eagle 之翼載入動畫+「下拉以重新整理」/「重新載入中…」文字。

04 Observations觀察項 Full
The Photos tab is the gallery of everything captured, filterable by trade. Its most important job is the Not issued classification — the one shared definition (src/domain/notIssued.ts) so the tab and the Home pool card can never disagree.照片分頁是所有已擷取內容的相簿,可依工種篩選。它最重要的任務是尚未出具的分類 —— 由單一共用定義(src/domain/notIssued.ts)決定,讓分頁與首頁的待處理卡片永遠一致。
| State狀態 | Meaning意義 |
|---|---|
| issued | Its report is issued and actually included it.其所屬報告已出具,且確實包含了它。 |
| draft | An open draft includes it (shown on Home by the draft card).某份開啟中的草稿包含了它(在首頁以草稿卡片顯示)。 |
| not issued | Everything else on record — the loose pool (streams into the next walk) and claimed-but-unchecked (only a revision brings it back).其餘所有已記錄的項目 —— 未歸屬的待處理池(會流入下一次巡檢)以及已納入但未勾選的(只有修訂才會把它帶回來)。 |

05 Assemble → issue彙整 → 出具
The report lifecycle. Assemble picks which observations go in; a Preview step precedes issuing; the report is not issued until you Confirm.報告的生命週期。彙整時挑選要納入哪些觀察項;出具前有一道預覽步驟;報告在你確認之前都尚未出具。
flowchart LR A["Review / assemble
pick observations · trades"] --> B[Preview] B -->|completeness check| C{"Send sheet
= Confirm step"} C -->|X / overlay| B C -->|Confirm / Send| D[[Officially issued]] D --> E["Sent
Delivered-to card"]:::full D --> F[(PDF + public link)] classDef full fill:#e6f1eb,stroke:#1a7050,color:#14513a;
- Report codes — e.g.
DT-14(project prefix + sequence); the draft carries the code, not the project name.報告編號 —— 例如DT-14(專案前綴+序號);草稿帶著編號,而非專案名稱。 - Recipient distribution full — the send sheet picks recipients (real Copy-link + channel tiles); Sent shows a “Delivered to” card. MVP issues straight to Sent.收件派送 full —— 派送表單挑選收件人(實際可用的複製連結+管道方塊);「已寄送」會顯示「已送達對象」卡片。MVP 出具後直接進入已寄送。
- PDF — server-rendered A4; preview == PDF == the design mock. Report map = the pinned floor plan or boxed elevation, only when marked.PDF —— 伺服器產生的 A4;預覽 == PDF == 設計稿。報告地圖=已釘選的平面圖或框選的立面圖,僅在有標記時顯示。
- Optional links full — reference a drawing sheet, or link a previous observation (“Previously noted in …”).選用連結 full —— 參照某張圖說,或連結先前的觀察項(「先前已記錄於…」)。


06 Notifications通知
The Home bell opens a full-page notification centre — a grouped worklist of actionable exceptions. The bell is filled with a red dot when there's anything to act on, outline when clear. One shared source (useNotifications) drives both the badge and the page.首頁的鈴鐺會開啟整頁的通知中心 —— 一份分組的待辦清單,列出需要處理的例外。有事要處理時鈴鐺呈實心並帶紅點,都清空時則為外框。單一共用來源(useNotifications)同時驅動角標與頁面。
| Group分組 | What surfaces浮現的內容 | Tap →點擊 → |
|---|---|---|
| Needs attention需要注意 | Aging un-issued observations (> 3 days on record, in no issued/draft report)逾期未出具的觀察項(記錄超過 3 天,且不在任何已出具/草稿報告中) | Photos (Not issued)照片(尚未出具) |
| Drafts草稿 | Open draft reports開啟中的草稿報告 | Reopen the draft重新開啟草稿 |

07 Profile & Field Record個人檔案與現場紀錄 Full
The worker profile is a person's identity across their practices. It exports a Field Record — a portable PDF + public page of their recorded activity.個人檔案是一個人橫跨其各事務所的身分。它可匯出一份現場紀錄 —— 一份可攜的 PDF+一個公開頁面,呈現其被記錄下來的活動。
- Identity model — a worker (the person) works under one or more practices; an always-present “Independent” practice is the default letterhead.身分模型 —— 一位工作者(人)隸屬於一或多間事務所;一個永遠存在的「獨立」事務所即為預設信箋抬頭。
- Cumulative stat graphs — projects · reports · on-site hours · observations, bucketed by week/month/year.累計統計圖 —— 專案 · 報告 · 現場時數 · 觀察項,依週/月/年分組。
- Field Record export — a PDF + a published public page (
/p/<token>). The masthead is the practice letterhead; the stats-hero avatar is the account photo. Span reads first activity → export date.現場紀錄匯出 —— 一份 PDF+一個已發布的公開頁面(/p/<token>)。抬頭是事務所信箋抬頭;統計首圖頭像是帳號照片。時間跨度為首次活動 → 匯出日期。 - Branded QR poster — “Save QR” makes a print-ready card: the Eagle logo knocked into the QR centre (error-correction H so it still scans), name, and a “Generated with Eagle AI” footer. The QR carries the public link.品牌 QR 海報 —— 「儲存 QR」會產生可列印的卡片:QR 中央嵌入 Eagle 標誌(採用 H 級容錯,仍可掃描)、姓名,以及「Generated with Eagle AI」頁尾。QR 帶著公開連結。

08 PWA constraintsPWA 限制
Shipping as an installable PWA (not a native app) shapes real product decisions. What follows is what the platform allows today and how the app works within it.以可安裝的 PWA(而非原生 App)出貨,會實際影響產品決策。以下是平台目前允許的範圍,以及 App 如何在其中運作。
| Area面向 | Constraint → how the app handles it限制 → App 如何因應 |
|---|---|
| Offline離線 | Local-first. All work persists in IndexedDB (Dexie) and functions with no network. The service worker (vite-plugin-pwa) uses NetworkFirst, so an online open always pulls the latest bundle.本地優先。所有作業都存在 IndexedDB(Dexie),無網路也能運作。Service worker(vite-plugin-pwa)採 NetworkFirst,因此有網路開啟時一律抓取最新的打包。 |
| Refresh重新整理 | No pull-to-refresh from the OS in standalone mode → the app implements its own pull-to-refresh (drag from top → sync) on every screen.獨立模式下作業系統不提供下拉重新整理 → App 在每個畫面自行實作自己的下拉重新整理(從頂端下拉 → 同步)。 |
| Push notifications推播通知 | Unreliable for installed PWAs (esp. iOS) → notifications are an in-app bell, computed from local data, not OS push.已安裝的 PWA 推播不可靠(尤其 iOS)→ 通知改為 App 內鈴鐺,由本地資料算出,而非作業系統推播。 |
| SSO / OAuth | The OAuth redirect can't complete inside an installed PWA → SSO buttons are placeholders (“Soon”); login is passwordless email code. Revisit with a native wrapper.OAuth 導轉無法在已安裝的 PWA 內完成 → SSO 按鈕為佔位(「即將推出」);登入採無密碼 email 驗證碼。日後以原生外殼再處理。 |
| Account deletion帳號刪除 | Apple 5.1.1(v) requires in-app deletion once there's account creation → deletion UI exists but the action is disconnected in this preview build; wired for the real build.Apple 5.1.1(v) 規定一旦有帳號建立就須提供 App 內刪除 → 刪除介面已存在,但此預覽版尚未接上動作;正式版會接上。 |
| Lock / privacy鎖定/隱私 | recordLock full — a passcode + Face ID gate for revisions, deletions, project setup, and personal info.以密碼+Face ID 保護修訂、刪除、專案設定與個人資料。 |
| Camera / photos相機/照片 | Standard web camera/file input; images are downscaled locally before upload and stored by content hash.標準的網頁相機/檔案輸入;圖片在上傳前於本地縮小,並以內容雜湊值儲存。 |
09 Login & sync登入與同步
- Login = passwordless email code. Enter email → a 6-digit code is emailed (via Resend) → verified. Sign-up and sign-in are one flow (first verification creates the account).登入=無密碼 email 驗證碼。輸入 email → 寄送 6 位數驗證碼(透過 Resend)→ 驗證。註冊與登入是同一流程(首次驗證即建立帳號)。
- Email-as-key. Your email is the sync identity — sign in with the same email on another device and your work restores. No codes to create, remember, or paste.Email 即金鑰。你的 email 就是同步身分 —— 在另一台裝置以同一 email 登入即可還原你的作業。不需建立、記憶或貼上任何代碼。
- Optimistic concurrency.
PUT /api/syncreturns HTTP 409 when another device wrote first → re-pull + merge.樂觀並行控制。當另一台裝置先寫入時,PUT /api/sync會回傳 HTTP 409 → 重新拉取+合併。 - Photos are content-addressed by SHA and account-independent, so bytes are portable across keys/devices/import; a valid sign-in key is the access gate.照片以 SHA 內容定址且不綁帳號,因此位元組可跨金鑰/裝置/匯入攜帶;有效的登入金鑰即為存取關卡。
repo + src/storage/sync.ts). Local-first stays the default.「Email 即金鑰」可由地址推導,並非加密 —— 任何知道該 email 的人都可能取得資料。日後會由真正的帳號後端取代,接在同一個資料介面點上(repo + src/storage/sync.ts)。本地優先仍為預設。
sequenceDiagram
participant D as Device
participant W as Worker
participant KV as KV (sync)
participant R2 as R2 (photos)
D->>W: PUT /api/photo (bytes)
W->>R2: store by sha
W-->>D: { sha }
D->>W: PUT /api/sync (manifest, x-sync-pass)
W->>KV: put @ SHA-256(account key)
alt another device wrote first
W-->>D: 409 → re-pull + merge
else ok
W-->>D: { version }
end
10 Worker APIWorker API
One Worker (worker/index.ts) serves the static PWA assets and the API. Endpoints are identical for both editions.單一 Worker(worker/index.ts)同時提供靜態 PWA 資源與 API。兩個版本的端點完全相同。
| Endpoint | Purpose用途 |
|---|---|
GET /api/sync | → { version, data } — projects · reports · observations · account profile (KV).→ { version, data } —— 專案 · 報告 · 觀察項 · 帳號檔案(KV)。 |
PUT /api/sync | Push with optimistic concurrency (409 on conflict). Header x-sync-pass.以樂觀並行控制推送(衝突時回 409)。標頭 x-sync-pass。 |
PUT /api/photo | Raw image bytes → { sha } (R2, content-addressed).原始圖片位元組 → { sha }(R2,內容定址)。 |
GET /api/photo/<sha> | → the image bytes.→ 圖片位元組。 |
POST /api/auth/email/send | { email } → 6-digit code in KV (10-min TTL), emailed via Resend.{ email } → 6 位數驗證碼存於 KV(10 分鐘 TTL),經 Resend 寄出。 |
POST /api/auth/email/verify | { email, code } → one-time verify (consumed on success).{ email, code } → 一次性驗證(成功即失效)。 |
POST /api/transcribe | Raw audio → { text } (Workers AI Whisper + OpenCC).原始音訊 → { text }(Workers AI Whisper + OpenCC)。 |
POST /api/publish · GET /p/<token> | Publish / serve the Field Record public page.發布/提供現場紀錄的公開頁面。 |
Secrets: RESEND_API_KEY (+ optional EMAIL_FROM). Bindings in wrangler.jsonc (default env = MVP; env.full = Full).密鑰:RESEND_API_KEY(+選用的 EMAIL_FROM)。綁定寫在 wrangler.jsonc(預設環境=MVP;env.full=完整版)。
11 Feature reference — every function功能總覽 — 每一項功能
The complete built inventory, grouped by area — everything the shipped code does today. full = full edition only; unmarked = both editions. A few capabilities are split — a base that ships in both plus a full-only enhancement (e.g. you enter a trade per observation in both, but the editable “Trades observed today” report summary is full); those are called out inline. The flow sections above tell the story; this is the checklist.完整的已建功能清單,依面向分組 —— 目前出貨程式碼所做的一切。full=僅完整版;未標記=兩版皆有。少數功能是拆分的 —— 兩版皆有的基礎,再加上完整版才有的加強(例如:兩版都能逐觀察項填工種,但可編輯的「今日現場工種」報告摘要為完整版專屬);這些會就地標註。上面的流程章節在講故事,這裡則是檢查清單。
Capture — the walk擷取 — 巡檢
- Real camera (
getUserMedia) with a multi-photo composer; avideo.play()nudge fixes the black viewfinder.真實相機(getUserMedia)搭配多張照片編排;以video.play()輕觸修正黑畫面觀景窗。 - Flashlight / torch toggle in the viewfinder, gated on device torch capability (Android; iOS Safari doesn't expose torch to web apps).手電筒切換位於觀景窗,依裝置手電筒能力啟用(Android;iOS Safari 不對網頁 App 開放手電筒)。
- Voice note → Workers AI Whisper, bilingual auto-detect; the Worker converts Mandarin → Traditional Chinese via OpenCC. Manual text note is always available.語音備註 → Workers AI Whisper,雙語自動偵測;Worker 以 OpenCC 將國語轉為繁體中文。手動文字備註一律可用。
- Sketch on a blank canvas — a drawing when there's no photo to mark.空白畫布手繪 —— 沒有照片可標記時,直接畫。
- Pause / Resume walk — a parked walk persists (Dexie
walkdrafts) → green Resume card on Home; discard deletes that walk's observations.暫停/續行巡檢 —— 暫存的巡檢會保存(Dexiewalkdrafts)→ 首頁出現綠色續行卡片;捨棄會刪除該次巡檢的觀察項。 - Staged zone (QR) — a scanned label docks a green chip on Start report; the first capture pre-fills that location.暫存區域(QR) —— 掃描標籤後在「開始報告」停靠綠色標籤;第一次擷取自動帶入該位置。
- Capture resilience — double-submit guard, Blob → data-URL fallback (Safari IndexedDB Blob reject), orphan rollback on failure, inline error surfacing.擷取韌性 —— 防重複送出、Blob → data-URL 備援(因應 Safari IndexedDB 拒收 Blob)、失敗時回滾孤兒資料、就地顯示錯誤。
Annotation — markup surface (Markup.tsx)標註 —— 標記介面(Markup.tsx)
- Real
<canvas>at the photo's native resolution — crop · draw · arrow · shape · text, with undo.以照片原生解析度的真實<canvas>—— 裁切 · 手繪 · 箭頭 · 形狀 · 文字,可復原。 - 8 colours, background-cache so there's no per-move lag.8 種顏色,背景快取,移動時不卡頓。
- Text tool — top input bar, A− / A+ scale with live preview, draggable, colour-following pill (50% fill + solid border). Header shows just the photo ID.文字工具 —— 頂端輸入列、A− / A+ 縮放即時預覽、可拖曳、跟色膠囊(50% 填色+實線邊框)。標題只顯示照片 ID。
- Crop & rotate (ImagePrep) before annotating.裁切與旋轉(ImagePrep)於標註前進行。
Observation觀察項
- Photo rows (tap to annotate) · Add photo · Sketch · editable note (voice / text) · Details (no AI).照片列(點擊即標註)· 加照片 · 手繪 · 可編輯備註(語音/文字)· 細節(無 AI)。
- Location picker — building → floor → tap-a-grid-cell area (drops a pin) → one zone string; selection-only (no free text).位置選擇器 —— 棟別 → 樓層 → 點格狀區域(放下圖釘)→ 一個區域字串;僅供選取(不可自由輸入)。
- Trade picker — full page, search + suggestions, rows for long names; multi-trade supported. (Entering trades is in both editions; the report's editable Trades observed summary is full — see Report.)工種選擇器 —— 整頁、搜尋+建議、長名稱以列呈現;支援多工種。(填工種於兩版皆有;報告中可編輯的現場工種摘要為 full —— 見「報告」。)
- Reference detail full — link an observation to a drawing-set sheet and drag a box on it (“Ref. detail”); never blocking.參考圖說 full —— 把觀察項連結到某張圖說並在其上拖曳框選(「參考細部」);絕不阻擋流程。
- Previous-observation link full — “Previously noted in …” cross-reference to a prior observation.前次觀察項連結 full —— 「先前已記錄於…」交叉參照到某個先前的觀察項。
- Locators full — floor-plan pin drop or building-elevation box (at most one).定位 full —— 平面圖圖釘或棟別立面框選(至多一種)。
- Unified ID scheme —
{PREFIX}-{report}-{obs}-{photo}(e.g.DT-1-1-1); prefix = project initials or a custom override.統一編號規則 ——{PREFIX}-{report}-{obs}-{photo}(例如DT-1-1-1);前綴=專案縮寫或自訂覆寫。
Location model位置模型 full
- Project setup — create / configure a project: name, letterhead (practice), report prefix, building/floor/elevation structure, drawings. (MVP runs on the single prebuilt starter project — no in-app project creation.)專案設定 —— 建立/設定專案:名稱、信箋抬頭(事務所)、報告前綴、棟別/樓層/立面結構、圖說。(MVP 只跑單一預建起始專案 —— App 內不建立專案。)
- Building → Floors (editable areas) + Elevations (N/S/E/W, stored as codes; toggle off the ones that don't apply).棟別 → 樓層(可編輯區域)+立面(東/南/西/北,以代碼儲存;不適用者可關閉)。
- Smart naming (
lib/naming.ts) —1F→2F,1st Floor→2nd Floor,A1→A2,Building A→B.智慧命名(lib/naming.ts)——1F→2F、1st Floor→2nd Floor、A1→A2、Building A→B。 - Floor drawings — one drawing per floor (thumbnail + add / replace / remove); the Home Drawings card is a read-only mirror with a per-floor count.樓層圖說 —— 每樓一張圖說(縮圖+新增/替換/移除);首頁的圖說卡片是唯讀鏡像,並顯示各樓數量。
Observations tab觀察項分頁 full
- Camera-roll 3-column grid of every observation photo (newest first), photo fills the tile, annotated badge, ID label.相機膠捲式的三欄格狀,列出每張觀察項照片(新的在前),照片鋪滿方塊、標註角標、ID 標籤。
- Search (ID · trade · location) + Filter sheet (annotated only · trade chips · date range).搜尋(ID · 工種 · 位置)+篩選表單(僅已標註 · 工種標籤 · 日期範圍)。
- Lightbox on tap → full photo + its ID + trade · location + Open observation.點擊開燈箱 → 完整照片+其 ID+工種 · 位置+開啟觀察項。
- Issued / Not-issued classification from one shared definition (
domain/notIssued.ts) — excluded-at-issue stays Not issued.已出具/尚未出具分類,來自單一共用定義(domain/notIssued.ts)—— 出具時被排除者仍為尚未出具。
Report — assemble → issue報告 —— 彙整 → 出具
- Review / assemble — pick included observations (Select all / Clear), edit info inline.審查/彙整 —— 挑選要納入的觀察項(全選/清除),就地編輯資訊。
- In-attendance picker — author pinned + add / remove attendees; lists on the PDF.出席選擇器 —— 作者固定+新增/移除出席者;會列於 PDF。
- Trades observed on site today full — an editable summary pre-filled from the included observations' trades; drives the PDF (hides when empty). MVP derives this list from the observations automatically (not editable).今日現場工種 full —— 一份可編輯的摘要,由已納入觀察項的工種預先填入;驅動 PDF(空白時隱藏)。MVP 由觀察項自動推導此清單(不可編輯)。
- Pre-issue check (non-blocking) — a bottom drawer warns how many included observations lack location / trade, with Review / Issue anyway.出具前檢查(不阻擋)—— 底部抽屜提醒有幾個已納入觀察項缺位置/工種,並提供檢視/仍要出具。
- Preview step, then not issued until Confirm — the send sheet is the commit; X / overlay cancels with nothing written.預覽步驟,接著確認前皆未出具 —— 派送表單即為送出點;X/遮罩取消則不寫入任何東西。
- Report codes —
{PREFIX}-{report}; the draft carries the code, not the project name.報告編號 ——{PREFIX}-{report};草稿帶著編號,而非專案名稱。 - Recipient distribution full — send sheet picks recipients (Copy-link + channel tiles, per-person Email / WhatsApp / In-app); Sent shows a Delivered to card. MVP issues straight to Sent.收件派送 full —— 派送表單挑選收件人(複製連結+管道方塊,逐人 Email/WhatsApp/App 內);「已寄送」顯示已送達對象卡片。MVP 出具後直接進入已寄送。
- Server-rendered A4 — a data-bound iframe of the
for-09-cleantemplate + native print, so preview == PDF == mock (page breaks, repeating Observations header,@pagefooter, monogram masthead).伺服器產生的 A4 —— 以for-09-clean樣板做資料綁定的 iframe+原生列印,因此預覽 == PDF == 設計稿(分頁、觀察項標頭重複、@page頁尾、字母標誌抬頭)。 - Save PDF → real file — POSTs the report HTML to
/api/pdf, rendered by headless Chrome (Cloudflare Browser Rendering) → OS share sheet on mobile / download on desktop. Native print is the offline fallback.儲存 PDF → 真實檔案 —— 將報告 HTML POST 到/api/pdf,由無頭 Chrome(Cloudflare Browser Rendering)產生 → 行動裝置走作業系統分享表單/桌機下載。原生列印為離線備援。 - PDF loading overlay — Eagle-wing loader + “Generating PDF…”, blocks a double-tap (free-tier 502) with a cancel ✕ that aborts the fetch.PDF 載入遮罩 —— Eagle 之翼載入動畫+「產生 PDF 中…」,可擋掉重複點擊(免費方案的 502),並有取消 ✕ 中止請求。
- Report map — the pinned floor plan or boxed elevation, rendered only when the observation is marked; “Generated with Eagle AI” footer.報告地圖 —— 已釘選的平面圖或框選的立面圖,僅在觀察項有標記時繪出;頁尾標「Generated with Eagle AI」。
Reports & revisions報告與修訂
- Reports tab — project header + latest-report hero (Continue a draft; Open + Create Revision an issued one) + Drafts / Issued lists.報告分頁 —— 專案標頭+最新報告主卡(草稿可繼續;已出具者可開啟+建立修訂)+草稿/已出具清單。
- Each issued number is a version stack (Original + revisions); the list is ordered by issue date.每個已出具編號都是一疊版本堆疊(原始+修訂);清單依出具日期排序。
- Revision — reopen an issued report to edit; keeps its number, bumps
rev, no new walk.修訂 —— 重新開啟已出具報告以編輯;沿用編號、遞增rev、不需重新巡檢。 - Issuing claims that walk's observations (the next walk starts empty) and advances the report number.出具即認領該次巡檢的觀察項(下一次巡檢從空白開始)並推進報告編號。
Notifications通知
- Full-page bell centre — grouped worklist; bell is filled + red dot when actionable, outline when clear (one shared
useNotificationssource).整頁鈴鐺中心 —— 分組待辦清單;有事處理時鈴鐺實心+紅點,清空時為外框(單一共用useNotifications來源)。 - Needs attention — un-issued observations aging > 3 days; Drafts — open draft reports; each row deep-links.需要注意 —— 逾 3 天未出具的觀察項;草稿 —— 開啟中的草稿報告;每一列都深層連結。
Profile & Field Record個人檔案與現場紀錄 full
- Worker profile — identity across practices (an always-present “Independent” letterhead by default); real live stats (projects / observations / issued reports / on-site hours = summed walk-timer).個人檔案 —— 橫跨各事務所的身分(預設有一個永遠存在的「獨立」信箋抬頭);真實的即時統計(專案/觀察項/已出具報告/現場時數=巡檢計時加總)。
- Field Record export — a portable PDF + a published public page (
/p/<token>); masthead = practice letterhead, hero avatar = account photo.現場紀錄匯出 —— 可攜 PDF+已發布公開頁面(/p/<token>);抬頭=事務所信箋抬頭,首圖頭像=帳號照片。 - Branded QR poster — “Save QR” makes a print-ready card: Eagle logo knocked into the QR centre (error-correction H), name, footer.品牌 QR 海報 —— 「儲存 QR」產生可列印卡片:QR 中央嵌入 Eagle 標誌(H 級容錯)、姓名、頁尾。
- Practice roster — people live on the practice (edited there); a project selects a subset + project-only “Other members”; author pinned.事務所名冊 —— 人員隸屬於事務所(在該處編輯);專案挑選其子集+僅該專案的「其他成員」;作者固定。
Sync & storage同步與儲存
- Local-first Dexie/IndexedDB; cloud sync email-keyed, auto-pulls on launch, optimistic concurrency (409 on conflict).本地優先 Dexie/IndexedDB;雲端同步以 email 為鍵、啟動時自動拉取、樂觀並行控制(衝突回 409)。
- Content-addressed photos — R2 keyed by
shaalone (portable across keys / devices / import); the key-gated manifest is the access gate.內容定址照片 —— R2 僅以sha為鍵(可跨金鑰/裝置/匯入攜帶);受金鑰保護的清單即為存取關卡。 - Tombstones ride the manifest so deletes don't resurrect on union-merge; “Keep photos on this device” toggle enables cloud-only mode.墓碑標記隨清單傳遞,讓刪除不會在聯集合併時復活;「將照片保留在此裝置」切換可啟用僅雲端模式。
- Pull-to-refresh on every content screen — drag → sync, Eagle-wing spinner + “Pull to refresh” / “Reloading…”.下拉重新整理於每個內容畫面 —— 下拉 → 同步,Eagle 之翼動畫+「下拉以重新整理」/「重新載入中…」。
Login & account登入與帳號
- Passwordless email — 6-digit code via Resend (
/api/auth/email/send+/verify, KV 10-min TTL, one-time); sign-up = sign-in.無密碼 email —— 6 位數驗證碼經 Resend 寄送(/api/auth/email/send+/verify,KV 10 分鐘 TTL,一次性);註冊=登入。 - Email-as-key — the email is the sync key; the same email on another device restores your work.Email 即金鑰 —— email 就是同步金鑰;在另一台裝置以同一 email 即可還原你的作業。
- Face ID re-entry — device-bound WebAuthn credential re-opens the last account without an email round-trip.Face ID 再登入 —— 綁定裝置的 WebAuthn 憑證可重新開啟上一個帳號,免走一次 email 流程。
- First-run onboarding — name + role. SSO buttons are UI-only placeholders (OAuth can't complete inside an installed PWA). Testing bypass code for internal sign-in.首次引導 —— 姓名+角色。SSO 按鈕僅為介面佔位(OAuth 無法在已安裝的 PWA 內完成)。內部登入用測試略過代碼。
- Record lock full — a passcode + Face ID gate on sensitive actions (revisions · deletions · project setup · personal info).紀錄鎖 full —— 對敏感動作(修訂 · 刪除 · 專案設定 · 個人資料)以密碼+Face ID 保護。
Platform & UX平台與體驗
- iOS keyboard fix — the shell pins to
window.visualViewportwhile typing (--app-h/--app-top,kb-open), self-adjusting on any iOS version; bottom-nav untouched.iOS 鍵盤修正 —— 輸入時外殼固定到window.visualViewport(--app-h/--app-top、kb-open),可在任何 iOS 版本自我調整;底部導覽不受影響。 - Toasts — global top toast + robust copy helper (
clipboard→execCommandfallback); auto-save fires a debounced “Saved”, header keeps a static “Auto-saved”.Toast 提示 —— 全域頂端提示+穩健的複製輔助(clipboard→execCommand備援);自動儲存以防抖方式跳「已儲存」,標頭保留靜態「已自動儲存」。 - Animated Eagle-wing loader — pre-React splash + React boot (holds one full cycle so it's seen) + inline data-loading states, themed via
currentColor.Eagle 之翼動畫載入 —— React 前的啟動畫面+React 開機(維持一個完整循環讓人看見)+就地資料載入狀態,透過currentColor套色。 - Design-system type scale swept across every size; edition badge (MVP / FULL) is dynamic on Login + Home.設計系統字級已套用到每個尺寸;版本徽章(MVP/FULL)在登入與首頁為動態顯示。
- Home context — live weather (Open-Meteo) on the project card + a real GPS on-site chip (On site now / Not on site / Locating… / GPS off).首頁情境 —— 專案卡片上的即時天氣(Open-Meteo)+真實的 GPS 現場標籤(現在在現場/不在現場/定位中…/GPS 關閉)。
- Theme — light / dark / system; a version tag shows the build.主題 —— 淺色/深色/跟隨系統;版本標籤顯示目前建置。
- Help & About — locale-aware Help & Support + Terms & Privacy pages (
eagleai.tw/gw/…), and the app version.說明與關於 —— 依語系的說明與支援+條款與隱私頁面(eagleai.tw/gw/…),以及 App 版本。 - Every AI feature has a manual fallback — voice → type, AI trade → edit; the AI-suggest indicator is the Eagle wing mark + “AI”.每個 AI 功能都有手動備援 —— 語音 → 打字、AI 工種 → 可編輯;AI 建議標示為 Eagle 之翼標記+「AI」。
- Spec-coverage audit full — an internal screen that checks the build's screens against the spec (dev tool, not a field feature).規格涵蓋稽核 full —— 一個內部畫面,把建置的各畫面對照規格檢查(開發工具,非現場功能)。
Editions版本
- One codebase, build-time
VITE_EDITION+ a per-feature registry (src/edition.ts) → two Workers:field-report(MVP / external) andfield-report-full(Full / internal). Shared KV + R2. Promote a feature = flip its registry line and deploy MVP.單一程式碼、建置期VITE_EDITION+逐功能註冊表(src/edition.ts)→ 兩個 Worker:field-report(MVP/對外)與field-report-full(完整版/對內)。共用 KV + R2。升級功能=改註冊表那一行並部署 MVP。
12 Data model & architecture資料模型與架構
Strict logic ↔ view split so the model is portable to the real backend.嚴格區分邏輯 ↔ 檢視,讓模型可移植到正式後端。
| Layer層 | What lives there內容 |
|---|---|
src/domain/ | Portable model + rules (no JSX): types, report, project, notIssued, statSeries, elevations, weather, seed.可移植模型+規則(無 JSX):types、report、project、notIssued、statSeries、elevations、weather、seed。 |
src/data/ | Dexie DB (accounts · projects · observations · photos · reports · walkdrafts), repo (local-first CRUD + report lifecycle), AuthProvider.Dexie 資料庫(accounts · projects · observations · photos · reports · walkdrafts)、repo(本地優先 CRUD+報告生命週期)、AuthProvider。 |
src/storage/ | sync.ts (cross-device sync + emailToSyncKey())(跨裝置同步+emailToSyncKey()), photos.ts (R2 transport)(R2 傳輸). |
src/edition.ts | Edition flag + feature registry (isOn()).版本旗標+功能註冊表(isOn())。 |
src/lib/ | type.ts (fz()/isz() locale scale — 中文 +2px)(fz()/isz() 語系字級 —— 中文 +2px), usePullToRefresh, useNotifications. |
src/i18n/ | en + zh-TW; every string is a key.en + zh-TW;每個字串都是一個鍵。 |
The seam: a real auth + backend plugs into src/data/repo.ts + src/storage/sync.ts — nothing above changes.介面點:真正的認證+後端接在 src/data/repo.ts + src/storage/sync.ts —— 以上的東西都不用改。
13 Screens & routes畫面與路由
| Screen畫面 | Role角色 | Edition版本 |
|---|---|---|
| Home | Front door — current project, resume walk, Start report, bell門面 —— 目前專案、續行巡檢、開始報告、鈴鐺 | both兩版 |
| Walk / Markup / ImagePrep | Capture: photo, annotate, crop/rotate擷取:照片、標註、裁切/旋轉 | both兩版 |
| ObsDetail | One observation — note, location, links單一觀察項 —— 備註、位置、連結 | both兩版 |
| Review / ReportDraft | Assemble a report彙整報告 | both兩版 |
| ReportView | Preview → Confirm/issue → send sheet預覽 → 確認/出具 → 派送表單 | both兩版 |
| Sent | Issued confirmation + Delivered-to出具確認+已送達對象 | both兩版 |
| Reports | Report list (drafts + issued)報告清單(草稿+已出具) | both兩版 |
| Observations | Photos gallery + Not-issued照片相簿+尚未出具 | full |
| Notifications | Full-page bell worklist整頁鈴鐺待辦清單 | both兩版 |
| Profile / ProfileRecord | Worker profile + Field Record個人檔案+現場紀錄 | full |
| Drawings | Floor plans / elevations / sheets viewer平面圖/立面圖/圖說檢視器 | full |
| Attendance | Who was on site現場有誰 | both兩版 |
| Preferences / CloudSync | Settings, sync/backup設定、同步/備份 | both兩版 |
| Login / Onboarding | Email code + first-run (name + role)Email 驗證碼+首次啟動(姓名+角色) | both兩版 |
14 The real app — roadmap正式產品 — 路線圖
What the current build stubs or defers, and what the shipping product will do. Each item plugs into an existing seam.目前版本先以佔位或延後處理的部分,以及正式產品將如何實作。每一項都接在既有的介面點上。
| Item項目 | Now現在 | Real app正式產品 |
|---|---|---|
| Auth + backend認證+後端 | Email-as-key (prototype)Email 即金鑰(原型) | Real accounts + database at the repo/sync seam; local-first stays default在 repo/sync 介面點接上真正的帳號+資料庫;本地優先仍為預設 |
| Not-issued lifecycle尚未出具的生命週期 | Not-issued tab + bell aging (>3d)尚未出具分頁+鈴鐺逾期(>3 天) | Aging reminders (3 / 7 days) → delete (no longer a concern) or issue a revision that adds it逾期提醒(3/7 天)→ 刪除(已不需在意)或出具修訂把它補進去 |
| Presence-gated capture現場定位才可擷取 | GPS on-site chip is informationalGPS 現場標籤僅供參考 | Capture only active while on site — off site, Start report is disabled (evidence tied to real presence)只有在現場時才能擷取 —— 不在現場則停用「開始報告」(證據對應真實到場) |
| Floor-plan pin-drop平面圖圖釘 | Per-obs map is a greyed stub逐觀察項地圖為灰化佔位 | Real pin-drop on the plan + data-bound PDF map平面圖上真正的圖釘+資料綁定的 PDF 地圖 |
| AI trade detectionAI 工種辨識 | Trade tag shown; entered by hand顯示工種標籤;以手動輸入 | AI-suggested trades (with manual override — every AI has a fallback)AI 建議工種(可手動覆寫 —— 每個 AI 都有備援) |
| Delivery receipts送達回條 | — | “Owner opened DT-13” once the live link tracks opens當即時連結能追蹤開啟後,顯示「業主已開啟 DT-13」 |
| SSO | Placeholder buttons佔位按鈕 | Real SSO via a native wrapper透過原生外殼的真正 SSO |
| Account deletion帳號刪除 | UI present, action disconnected介面已有,動作尚未接上 | Wired in-app deletion (Apple 5.1.1(v))接上 App 內刪除(Apple 5.1.1(v)) |