Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

## [0.2.3.3] - 2026-05-08

### Fixed
- **preload cancel 真正取消圖片載入** — `state.preloadAbort`(AbortController)是 dead code:`new Image()` 不接受 AbortSignal,`cancelPreload()` 呼叫後什麼都不做,快速切換手術時舊的圖片請求會繼續搶頻寬。改用 `state.preloadImages: []` 追蹤 Image 物件,`cancelPreload()` 逐一設 `img.src = ''` 通知瀏覽器中止載入,再清空陣列。

### Added
- **admin 表單新增 `type` 欄位** — 之前 admin.html 只能填 category(ent/surgery/weight/functional),但首頁篩選 chip 與 `app.js` 的 `item.type` 過濾需要獨立的 `type` 欄位(explain / surgery)。新增 `<select>`、串到 `enterEditMode` / `submitAdd` / `submitEdit`,後端 `handlePostProcedures` 與 `handlePutProcedure` 驗證並寫入 procedure file 與 index.json。Backfill 既有 4 篇(snore / nasal-obstruction / vocal-cord / influenza)為 `type: "explain"`
- **內容批次擴充(content)** — 新增 9 篇衛教:quit-smoke / oral-ulcer / menieres / tinnitus / ssnhl / otitis-media-effusion / vitd / atopic-dermatitis / testosterone(皆 `type: "explain"`,分屬 ent / functional 分類)
Expand Down
3 changes: 2 additions & 1 deletion TODOS.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ tests/unit/

### AbortController 用於圖片預載
**Priority:** Medium
**Completed:** v0.2.3.3 (2026-05-08)
**What:** 醫師點錯手術立即返回再點另一個時,前一個的預載請求應該被取消,不跟新的搶頻寬。
**Status:** 已排程 remote agent 於 2026-05-08 09:00 處理(trigger `trig_016HpfGshq8NjmNS9yv7ebLv`),將用 image array + `img.src = ''` 取消方式修正 `preloadAbort` dead code
**Fix:** 移除 dead code `state.preloadAbort` (AbortController,但 `new Image()` 無法訂閱 signal);改用 `state.preloadImages: []` 追蹤 Image 物件,`cancelPreload()` 逐一設 `img.src = ''` 通知瀏覽器停止載入,再清空陣列

### Keyboard navigation + ARIA 無障礙支援
**Priority:** Medium
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.3.2
0.2.3.3
10 changes: 6 additions & 4 deletions js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
stepIndex: 0,
activeTool: null, // pen | spot | laser | null
wakeLock: null,
preloadAbort: null,
preloadImages: [],
chromeHidden: false,
chromeTimer: null
};
Expand Down Expand Up @@ -757,11 +757,13 @@
// ============================================================
function preloadImages(steps) {
cancelPreload();
state.preloadAbort = new AbortController();
(steps || []).forEach(function (s) { if (s.image) { var img = new Image(); img.src = s.image; } });
(steps || []).forEach(function (s) {
if (s.image) { var img = new Image(); img.src = s.image; state.preloadImages.push(img); }
});
}
function cancelPreload() {
if (state.preloadAbort) { state.preloadAbort.abort(); state.preloadAbort = null; }
state.preloadImages.forEach(function (img) { img.src = ''; });
state.preloadImages = [];
}

// ============================================================
Expand Down
29 changes: 29 additions & 0 deletions tests/unit/app.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -377,3 +377,32 @@ describe('PWA manifest', () => {
expect(manifest.scope).toBe('./');
});
});

describe('cancelPreload clears preload image array', () => {
it('sets img.src to empty string for each tracked image and empties the array', () => {
const images = [];
const srcs = [];
// Simulate the state.preloadImages array with mock Image-like objects
['/a.jpg', '/b.jpg', '/c.jpg'].forEach(function (url) {
const img = { src: url };
images.push(img);
srcs.push(url);
});

// Inline the cancelPreload logic (mirrors js/app.js — no bundler available)
function cancelPreload(state) {
state.preloadImages.forEach(function (img) { img.src = ''; });
state.preloadImages = [];
}

const state = { preloadImages: images };
cancelPreload(state);

// All img.src should now be ''
images.forEach(function (img) {
expect(img.src).toBe('');
});
// Array should be cleared
expect(state.preloadImages).toHaveLength(0);
});
});