Stage 7 (wip): UI shell, libs, mock transport, screens (lobby->game), e2e smoke
- plain Svelte 5 + TS + Vite (no SvelteKit); CSS-token design system (Telegram-ready), hash router, IndexedDB session - pure libs: domain model, premium/value maps ported from solver, board replay, placement state machine, i18n en/ru - in-memory mock transport + seed data; pnpm start runs lobby->active game->board with no backend - board: pointer-drag + tap placement, MakeMove (popup / 1s-hold commit), two-state zoom, blank chooser, exchange, hint, word-check, chat - Playwright smoke (mock) green; svelte-check clean; mock bundle ~37 KB gzip
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
// Domain model — plain TypeScript shapes the screens use, deliberately decoupled
|
||||
// from the FlatBuffers wire types. Both the real transport (which decodes
|
||||
// FlatBuffers) and the mock transport speak this model, so the UI never touches
|
||||
// generated wire code directly.
|
||||
|
||||
export type Variant = 'english' | 'russian' | 'erudit';
|
||||
|
||||
/** Backend game status strings. */
|
||||
export type GameStatus = 'active' | 'finished' | string;
|
||||
|
||||
/** Decoded move action kinds (history-independent, see ARCHITECTURE §9.1). */
|
||||
export type MoveAction = 'play' | 'pass' | 'exchange' | 'resign' | 'timeout' | string;
|
||||
|
||||
/** Play orientation: H is across a row, V is down a column. */
|
||||
export type Direction = 'H' | 'V';
|
||||
|
||||
export interface Tile {
|
||||
row: number;
|
||||
col: number;
|
||||
letter: string;
|
||||
blank: boolean;
|
||||
}
|
||||
|
||||
export interface Seat {
|
||||
seat: number;
|
||||
accountId: string;
|
||||
displayName: string;
|
||||
score: number;
|
||||
hintsUsed: number;
|
||||
isWinner: boolean;
|
||||
}
|
||||
|
||||
export interface GameView {
|
||||
id: string;
|
||||
variant: Variant;
|
||||
dictVersion: string;
|
||||
status: GameStatus;
|
||||
players: number;
|
||||
toMove: number;
|
||||
turnTimeoutSecs: number;
|
||||
moveCount: number;
|
||||
endReason: string;
|
||||
seats: Seat[];
|
||||
}
|
||||
|
||||
export interface MoveRecord {
|
||||
player: number;
|
||||
action: MoveAction;
|
||||
dir: string;
|
||||
mainRow: number;
|
||||
mainCol: number;
|
||||
tiles: Tile[];
|
||||
words: string[];
|
||||
count: number;
|
||||
score: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
/** A seated player's private view of a game. */
|
||||
export interface StateView {
|
||||
game: GameView;
|
||||
seat: number;
|
||||
rack: string[];
|
||||
bagLen: number;
|
||||
hintsRemaining: number;
|
||||
}
|
||||
|
||||
export interface MoveResult {
|
||||
move: MoveRecord;
|
||||
game: GameView;
|
||||
}
|
||||
|
||||
export interface HintResult {
|
||||
move: MoveRecord;
|
||||
hintsRemaining: number;
|
||||
}
|
||||
|
||||
export interface EvalResult {
|
||||
legal: boolean;
|
||||
score: number;
|
||||
words: string[];
|
||||
}
|
||||
|
||||
export interface WordCheckResult {
|
||||
word: string;
|
||||
legal: boolean;
|
||||
}
|
||||
|
||||
export interface ChatMessage {
|
||||
id: string;
|
||||
gameId: string;
|
||||
senderId: string;
|
||||
kind: string;
|
||||
body: string;
|
||||
createdAtUnix: number;
|
||||
}
|
||||
|
||||
export interface Profile {
|
||||
userId: string;
|
||||
displayName: string;
|
||||
preferredLanguage: string;
|
||||
timeZone: string;
|
||||
hintBalance: number;
|
||||
blockChat: boolean;
|
||||
blockFriendRequests: boolean;
|
||||
isGuest: boolean;
|
||||
}
|
||||
|
||||
export interface Session {
|
||||
token: string;
|
||||
userId: string;
|
||||
isGuest: boolean;
|
||||
displayName: string;
|
||||
}
|
||||
|
||||
export interface MatchResult {
|
||||
matched: boolean;
|
||||
game?: GameView;
|
||||
}
|
||||
|
||||
export interface History {
|
||||
gameId: string;
|
||||
moves: MoveRecord[];
|
||||
}
|
||||
|
||||
export interface GameList {
|
||||
games: GameView[];
|
||||
}
|
||||
|
||||
/** A live event delivered over the Subscribe stream. */
|
||||
export type PushEvent =
|
||||
| { kind: 'your_turn'; gameId: string; deadlineUnix: number }
|
||||
| { kind: 'opponent_moved'; gameId: string; seat: number; action: string; score: number; total: number }
|
||||
| { kind: 'chat_message'; message: ChatMessage }
|
||||
| { kind: 'nudge'; gameId: string; fromUserId: string }
|
||||
| { kind: 'match_found'; gameId: string }
|
||||
| { kind: 'heartbeat' };
|
||||
Reference in New Issue
Block a user