Imported from johnnyyu0930/awoo-drawing (
AGENTS.md). Install upstream withnpx skills add johnnyyu0930/awoo-drawing. Copyright stays with the author.
AGENTS.md
This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
Project Overview
A Vue 3 lottery/drawing web app for company year-end banquet events (尾牙抽獎系統), deployed to GitHub Pages at https://johnnyyu0930.github.io/awoo-drawing/. Built with Vite, TypeScript, and WindiCSS.
Commands
pnpm install # install dependencies
pnpm dev # dev server at http://localhost:8888
pnpm build # type-check (vue-tsc) then Vite build
pnpm preview # preview the production build
There are no tests. The build command runs vue-tsc && vite build — TypeScript errors will fail the build.
Architecture
The app is a single-page Vue 3 application with a simple manual router using a pageState ref in App.vue. There is no Vue Router.
Page flow
init → loading (5s timer) → result → init (loop)
↕
record
App.vue— owns all shared state (awards,headcount,nameList,winners) and thepageStateswitcher. Orchestrates transitions between pages.InitPage.vue— input form; receives props viav-modelbindings and emitsstart/goRecord.LoadingPage.vue— playsloading-music.mp3from/public; shown for 5 seconds while the draw runs (the actual draw result is computed inApp.vueafter the timeout).ResultPage.vue— displays winners; emitsnextto loop back to init, playsresult-music.mp3.RecordPage.vue— reads and clears the localStorage history via Lockr; emitsback.src/libs/libs.ts— single exported functiondrawing(list, count)that shuffles with lodash and splices winners.
State persistence
Past draws are stored in localStorage under the key awardsStore (an array of { awards, winners }) using the Lockr wrapper. After each draw, App.vue pushes the result and removes winners from the name list using lodash xor.
Styling
WindiCSS (Windi CSS, not Tailwind) with a custom theme defined in windi.config.ts:
- Colors:
bg(#FFEFEF),red(#CD0000),secondary-80(#4E4C4C),secondary-100(#222222) - Breakpoints:
2xl= 1729px,3xl= 1920px (non-standard, larger than typical) - Scrollbar plugin from
@windicss/plugin-scrollbaris active.
Use WindiCSS utility classes, not Tailwind — they differ in some utilities and the JIT scanning behavior.
Deployment
Merging to master triggers the GitHub Actions workflow (.github/workflows/deploy.yml) which builds and pushes ./dist to gh-pages. The Vite base is set to /awoo-drawing/.
Slack avatars on the loading screen
LoadingPage.vue renders each name as a "lottery ball" with an avatar. Avatars come from public/slack-avatars.json (a { "name": "image_url" } map). Names not in the map fall back to a gradient + initials placeholder rendered by src/libs/avatar.ts.
To refresh the map:
- Local:
SLACK_BOT_TOKEN=xoxb-... pnpm sync-avatars(token needsusers:readscope). - Automated: the
Sync Slack avatarsworkflow (.github/workflows/sync-slack-avatars.yml) runs weekly and on manual dispatch, requires theSLACK_BOT_TOKENrepo secret, and commits any change directly tomaster(which then redeploys viadeploy.yml).
The script keys each avatar by real_name, display_name, and Slack handle, so most name-list shapes match.
Updating the title banner image
Per the README workflow:
- Place the new image in
src/assets/ - Update the
srcattribute insrc/components/InitPage.vue(the<img>in the<header>) - Bump
versioninpackage.json - Commit and open a PR to
master