Skip to content

feat: 觀眾手機即時投票、互動頁參考與規劃設計原則 - #3

Merged
keanu77 merged 2 commits into
mainfrom
feat/live-poll
Oct 18, 2026
Merged

keanu77 merged 2 commits into
mainfrom
feat/live-poll

Conversation

@keanu77

@keanu77 keanu77 commented Oct 18, 2026

Copy link
Copy Markdown
Owner

內容

  • 手機即時投票 skills/html-slide-deck/references/live-poll/:Worker+Durable Object 後端、手機頁(揭曉前不含答案)、投影片端程式(A 揭曉、Shift+R 清空、斷線改輪詢)、load-test.mjs。
  • scripts/poll-slides.mjs:把 <section data-poll-join>/<section data-poll="id"> 占位換成完整投票頁並注入講者端;產出檔含 ADMIN_KEY,只放講者電腦。
  • references/interactive-pages.md:互動地圖、製作技巧頁、PPTX 轉 HTML 插入互動頁。
  • references/design-principles.md:docs/html-slides-tips-from-x-2026-10-17.md 的原則與工具清單併入 skill,SKILL.md 第 1 步引用。
  • TECHNIQUES 4.5–4.7、README、pitfalls、CHANGELOG、CI 同步。

測試

  • 本機 wrangler dev:load-test.mjs 三題各 150 支假手機全數通過(開題、投票、統計、揭曉)
  • Playwright E2E:加入頁人數、選擇題百分比、揭曉同步手機、猜數字中位數、Shift+R 清空、離開投票頁手機回等待,無 JS 錯誤
  • qa-screenshots.mjs(含 QA_OFFLINE=1)通過,投票頁 QR 解碼相符
  • 互動地圖範例程式碼 smoke test
  • CI

🤖 Generated with Claude Code

- references/live-poll/:Cloudflare Worker+Durable Object 投票後端、投影片端 live-poll.js/css、150 支手機壓測腳本
- scripts/poll-slides.mjs:占位 section → 加入頁與投票頁(QR 帶 data-url),注入講者端程式
- references/interactive-pages.md:互動地圖、製作技巧頁、PPTX 轉 HTML 插入互動頁
- references/design-principles.md:X 簡報技巧整理的原則與工具清單併入 skill,SKILL.md 第 1 步引用
- TECHNIQUES 4.5–4.7、README、pitfalls、CHANGELOG、CI 語法檢查同步

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-18T13:37:21.701369Z da5964d PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: da5964d8a0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}

const { event, polls } = JSON.parse(readFileSync(join(worker, 'src', 'polls.json'), 'utf8'));
const QR = requirePkg('qrcode');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Install qrcode before invoking the generator

On a fresh installation following the documented live-poll setup, this exits with “找不到套件 qrcode”: the skill package contains only jsqr and pngjs, while the npm install under worker/ installs only Wrangler and is not on the resolver's upward search path when the command is run from the deck project. Add an explicit npm install qrcode setup step or package and resolve qrcode as a skill-local dependency so the advertised generation command works.

AGENTS.md reference: AGENTS.md:L6-L10

Useful? React with 👍 / 👎.

Comment on lines +114 to +118
const res = await api('/api/results?poll=' + id);
results[id] = res;
if (res.phase !== 'revealed') {
const r = await control({ poll: id, phase: 'open' });
results[id] = r.results;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Guard poll activation against stale slide requests

If the presenter leaves this poll slide while the results request is pending, onShow sends {poll:null}, but this continuation can subsequently send {poll:id, phase:'open'} and reopen the question after the slide is no longer visible. Rapid navigation between poll slides can similarly leave the wrong poll active on audience phones; check that current === id after each await or cancel/serialize these transitions before issuing the control request.

Useful? React with 👍 / 👎.

} else {
const vals = rows.map(r => Number(r.value)).sort((a, b) => a - b);
out.counts = def.bins.map(([lo, hi]) => vals.filter(v => v >= lo && v <= hi).length);
out.median = vals.length ? vals[Math.floor((vals.length - 1) / 2)] : null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Average the middle values for even-sized numeric polls

For an even number of numeric responses, this reports the lower middle observation rather than the median of the two middle observations. For example, votes [0, 100] display a median of 0 instead of 50, making the live statistic inaccurate whenever a numeric poll has an even response count.

Useful? React with 👍 / 👎.

Comment on lines +143 to +146
ws.onclose = () => {
conn.textContent = "重新連線中…";
fallback();
setTimeout(connect, Math.min(15000, 1000 * 2 ** retry++));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Cancel pending reconnects before opening another socket

After a disconnect schedules this retry, returning the tab to the foreground can call connect() immediately via visibilitychange; the pending timeout then opens a second socket without closing or checking the first. Both remain tagged as phones, so the backend's socket-based phones count is inflated and repeated disconnects can accumulate additional connections; retain and clear the retry timer or make connect() return while a socket is already connecting/open.

Useful? React with 👍 / 👎.

互動內容原本收在「講者工具」的收合區塊裡,首頁看不到;改成不收合的章節並加入導覽列與流程圖。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@keanu77
keanu77 merged commit beefc93 into main Oct 18, 2026
2 checks passed
keanu77 added a commit that referenced this pull request Oct 19, 2026
* fix: 處理 PR #3/#4 的 Codex 審查意見

- 中位數偶數筆取中間兩筆平均(worker 抽出 stats.js 並加測試;模擬模式同步)
- 投影片端換頁競態:控制請求排隊、換頁序號擋掉過期的開題
- 手機頁重連:取消排定的重連、已在連線就不再開新 socket
- 模擬模式啟動即設定加入頁人數
- qrcode 改為 skill 自帶套件(resolve.mjs SKILL_LOCAL)
- README 部署指令改用子 shell;live-poll README 補 cd ..
- Live Demo 改為 Node 20+,重新 build docs/index.html
- CI 執行 worker 的 node --test

* fix: 控制請求排隊時略過過期的換頁指令

斷線時 /api/control 卡住、講者連換多頁,積壓的換頁指令輪到時若已換過頁就略過,
只送最新狀態;不會重播舊題、也不會疊加多次逾時。揭曉/清空照送。
重新 build docs/index.html。

---------

Co-authored-by: keanu77 <20503845+keanu77@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant