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
31 changes: 23 additions & 8 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Plot Twists - Application Architecture & Documentation

**Last Updated:** 2026-01-23
**Version:** 1.0
**Last Updated:** 2026-02-01
**Version:** 1.4
**Status:** Active Development

---
Expand Down Expand Up @@ -1231,9 +1231,13 @@ interface CardPack {

- **Standard Pack**: Built-in default (200+ characters, 70+ settings, 60+ circumstances)
- **Custom Packs**: User-created and persisted to `data/cardpacks.json`
- **Example Packs**: "Office Comedy" and "Sci-Fi Adventures" included
- **Example Packs**: "Office Comedy" and "Sci-Fi Adventures" (deterministic IDs for persistence)
- **Pack Selection**: Host chooses pack in lobby
- **Rating System**: Users can rate packs (1-5 stars)
- **Rating System**: Interactive star rating (1-5 stars) with hover states
- **Pack Browser**: Full modal for discovering community packs with search, filter, and sort
- **Pack Editor**: Edit existing custom packs (3-step wizard)
- **Pack Deletion**: Delete custom packs with confirmation modal
- **Player Visibility**: Players see selected pack name in lobby

#### Socket Events

Expand All @@ -1243,11 +1247,21 @@ interface CardPack {
| `select_card_pack` | Client → Server | Choose pack for room |
| `card_pack_selected` | Server → Client | Broadcast selection |
| `create_card_pack` | Client → Server | Create new pack |
| `update_card_pack` | Client → Server | Update existing pack |
| `delete_card_pack` | Client → Server | Delete a pack |
| `rate_card_pack` | Client → Server | Rate a pack |

#### Files Added
- `server/services/cardpack.service.ts` - Pack CRUD operations
- `components/CardPackSelector.tsx` - Pack browser UI
| `search_card_packs` | Client → Server | Search packs by query |
| `get_featured_packs` | Client → Server | Get top-rated packs |
| `get_card_pack` | Client → Server | Get pack details by ID |

#### Files Added/Modified
- `server/services/cardpack.service.ts` - Pack CRUD operations (enhanced with deterministic IDs)
- `components/CardPackSelector.tsx` - Pack browser UI (enhanced with edit/delete/rate)
- `components/CardPackCreator.tsx` - 3-step pack creation wizard
- `components/CardPackEditor.tsx` - Edit existing packs
- `components/CardPackBrowser.tsx` - Full discovery modal with search/filter/sort
- `components/StarRating.tsx` - Interactive star rating component
- `components/DeleteConfirmModal.tsx` - Confirmation dialog for deletions
- `data/cardpacks.json` - Pack persistence

---
Expand Down Expand Up @@ -1396,6 +1410,7 @@ interface RoomSettings {

| Date | Version | Changes | Author |
|------|---------|---------|--------|
| 2026-02-01 | 1.4 | Card Pack Creator feature completion: CardPackEditor, DeleteConfirmModal, StarRating, CardPackBrowser components; search/featured/edit/delete socket events | Claude |
| 2026-01-24 | 1.3 | Added 4 major features: Audience Interaction System, AI Script Customization Engine, Custom Card Pack Creator, Voice & Audio Integration | Claude |
| 2026-01-23 | 1.2 | Enhanced card selection UX with "Shuffle All", progress indicators, haptic feedback, and selection preview | Claude |
| 2026-01-23 | 1.1 | Added security enhancements, rate limiting, testing infrastructure, PWA support, code organization | Claude |
Expand Down
1 change: 1 addition & 0 deletions app/host/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ export default function HostPage() {
roomCode={roomCode}
selectedPackId={selectedPackId}
onSelect={setSelectedPackId}
showCreateButton={true}
/>

<ScriptCustomizationPanel
Expand Down
29 changes: 28 additions & 1 deletion app/join/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ function JoinPageContent() {
const [showOnboarding, setShowOnboarding] = useState(false)
const [copySuccess, setCopySuccess] = useState(false)
const [myRole, setMyRole] = useState<PlayerRole>('PLAYER')
const [selectedPackName, setSelectedPackName] = useState<string | null>(null)
const previousSpeaker = React.useRef<string>('')

useEffect(() => {
Expand Down Expand Up @@ -106,6 +107,18 @@ function JoinPageContent() {
toast.error(errorMsg)
setError(errorMsg)
})
socket.on('card_pack_selected', (packId: string) => {
// Display a friendly name based on pack ID
if (packId === 'standard') {
setSelectedPackName('Standard Pack')
} else if (packId === 'example-office-comedy') {
setSelectedPackName('Office Comedy')
} else if (packId === 'example-scifi-adventures') {
setSelectedPackName('Sci-Fi Adventures')
} else {
setSelectedPackName('Custom Pack')
}
})
return () => {
socket.off('players_update')
socket.off('game_state_change')
Expand All @@ -115,6 +128,7 @@ function JoinPageContent() {
socket.off('sync_teleprompter')
socket.off('game_over')
socket.off('error')
socket.off('card_pack_selected')
}
}, [socket, isConnected, selection, myRole])

Expand Down Expand Up @@ -302,11 +316,24 @@ function JoinPageContent() {
<h1 className="text-4xl font-display mb-4" style={{ color: 'var(--color-success)' }}>
{myRole === 'SPECTATOR' ? 'Spectator Mode' : "You're In!"}
</h1>
<p className="text-lg mb-8" style={{ color: 'var(--color-text-secondary)' }}>
<p className="text-lg mb-4" style={{ color: 'var(--color-text-secondary)' }}>
{myRole === 'SPECTATOR'
? 'Sit back and enjoy the show! You can vote at the end.'
: 'Waiting for game to start...'}
</p>
{selectedPackName && (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="inline-flex items-center gap-2 px-4 py-2 rounded-full mb-6"
style={{ background: 'var(--color-highlight)', border: '1px solid var(--color-border)' }}
>
<span>📦</span>
<span className="text-sm font-medium" style={{ color: 'var(--color-text-primary)' }}>
{selectedPackName}
</span>
</motion.div>
)}
<div className="stack-sm">
{players.map((player) => (
<div
Expand Down
Loading
Loading