# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## 專案概觀

這是一個純前端的靜態網頁，用 Leaflet 在台灣地圖上顯示寶可夢（Pokémon GO）出沒點資料。沒有建置系統、沒有套件管理、沒有測試。遠端 repo：`http://192.168.42.124:31337/timmy/pokemon`（Gitea，公開）。

整個專案只有三個真正的組件：
- `index.html`：主畫面。fetch `spots.json` 後，為每筆資料在地圖上建立帶圖示的 marker 與 popup。
- `spots.json`：資料來源。頂層是 `{ "items": [...] }`，也相容頂層直接是陣列（`toList()` 會處理兩種）。
- `img/<id>.png`：寶可夢圖鑑編號對應的圖示（已備齊 1–1000）。

## 常用指令

沒有 build / lint / test。直接開啟本機伺服器即可：

```bash
# 在專案根目錄起 server，直接訪問 http://localhost:8000/ 即可
python3 -m http.server 8000
```

圖示 URL 使用相對路徑 `img/${item.id}.png`（`index.html:139`），本地與生產都能運作。生產部署在 `https://<host>/pokemon/` 下，Caddy 把 `/opt/caddy/www/pokemon` 作為該路徑的 root。

### 批次下載寶可夢圖示

```bash
./download_poke_imgs.sh [START] [END]           # 預設 1..1000
CONCURRENCY=16 ./download_poke_imgs.sh 1 1025   # 自訂並行數
```

會從 `https://twpkinfo.com/images/poke1/<id>.png` 下載到 `img/`，已存在則跳過，用 `.part` 暫存避免半成品。

## 資料結構與相容性注意事項

`spots.json` 的每筆 item 欄位命名可能混用兩種來源，`index.html` 的 `buildPopup()` 與 marker 建立邏輯都必須容錯：

- 座標欄位有三組別名：`latitude`/`longitude`、`Latitude_Adjusted`/`Longitude_Adjusted`、`Latitude_Raw`/`Longitude_Raw`。程式碼用 `??` 鏈依序 fallback（`index.html:81-82`、`index.html:134-135`）。**新增欄位讀取時請沿用同一套 fallback 模式**，不要只讀單一命名。
- 數值欄位（`iv_pct`、`iv_atk`、`iv_def`、`iv_sta`、`cp`、`level`）一律用 `isNum()` 驗證後才顯示，否則顯示 `—`。缺欄位不視為錯誤。
- `is_perfect` 也可用 `iv_pct === 100` 推導（`index.html:93`）。
- **剩餘秒數用 `liveRemaining()` 動態算**（`index.html:59-64`）：以 `expire_time`（unix 秒）扣 `Date.now()/1000`，避免直接用 JSON 內寫死的 `remaining`。popup 透過 `bindPopup(() => buildPopup(item))`（`index.html:147`）延後到開啟時才建，確保每次開都是最新值。

## popup 互動的三種狀態

地圖同時支援 hover / click 固定 / 外部點擊取消，三者共用一個 module-scope 的 `pinnedPopup` 來協調：

- **hover**：開 popup + marker 放大 class `hovered`；離開時若**不是**目前被固定的，就關閉。
- **click marker**：若目前有其他 pinned popup 先關掉它、移除其放大樣式，再固定新的。
- **click 地圖空白處**：關閉 pinned popup。

修改互動行為時要同步更新這三個 handler，否則容易出現「popup 關不掉」或「圖示放大沒復原」的狀態不一致。

## 載入錯誤提示

`showLoadError(msg)`（`index.html:113-122`）會在地圖右上角顯示紅框 Leaflet control。三種情境會觸發：HTTP 非 2xx、JSON 解析失敗、有效座標為零。修改 fetch 流程時請維持這層使用者可見的回饋，不要退回只剩 `console.error`。

## 部署

生產機在 `192.168.42.104`，Caddy 服務根目錄為 `/opt/caddy/www/pokemon`。**一律透過 Gitea 推送，不再用 scp**：

```bash
git add -u            # 或指定檔案
git commit -m "..."
git -c http.extraheader="Authorization: token <TOKEN>" push
```

生產機由其他流程（cron 或 webhook）從 Gitea 同步到 Caddy root。本機禁用 scp 直接覆蓋生產檔，避免跟 git 狀態分叉。

（資料檔 `spots.json` 由其他流程寫入生產機，不要在本地修改後推上去覆蓋；若要動它，確認流程。）

## 其他檔案

- `IV100.png`：為 100% IV 完美個體預留的標示圖（目前 `index.html` 尚未引用）。
