09eac536b5203ff91051378818a9d44ba87e3d8e
35
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
01f5139740 |
Let the dealer find one hand in the log instead of scrolling the night
The hand log exists for one moment: somebody at the rail says they won a pot an hour ago and wants it looked up. A night runs to sixty hands or more and the panel showed five of them at a time, in a box fixed at 260px whether the dealer was on a phone or a 27" screen with room for twenty. There was no way to ask for a hand — only to scroll and read every row. A filter box over the list now takes a name, an amount or a hand number. Matching is loose on purpose: "fay" whatever case it's typed in, "190" for a $190 pot or a $190 payout, "$1,90" for the same typed badly, "#12" when the dealer knows the hand and wants that exact one. Beside it, how much of the night is hidden — "8 of 60" — since a filter matching one hand otherwise looks like a table that only played one. The list also takes the room it has: clamp(200px, 42vh, 560px) rather than a flat 260, which is seven or eight rows instead of five on a desktop and no worse on a phone. Export stays the whole night, always. A CSV quietly missing the hands scrolled past would be a record with holes in it and nothing on the file to say so — if a filter is up when it's exported, the toast says how many went in. Matching deliberately covers only what the row displays. Searching the full ledger as well was the first attempt, so the hand that broke somebody could be found under the loser's name — but the ledger holds everyone who put a chip in, and measured on a seven-handed night where nobody folded, "Fay" returned 60 of 60. A filter that searches text the panel doesn't show looks broken even when it's right. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
cc060b29b8 |
Keep the big blind moving one player at a time when somebody leaves out of the blinds
Passing the button to the next player is right until a chair between the button and
the blinds empties. Then it pulls the whole blind order forward a place. Measured,
four-handed, button on Ben, blinds Cal and Dee, with Cal cashing out:
tool: button Dee, small blind Ann, big blind Ben
correct: dead button on Cal's chair, small blind Dee, big blind Ann
Ann is never asked for the big blind she owes, and Ben pays one a full orbit early.
That is a real big blind out of a real stack, and it happens every time somebody
stands up out of the blinds — which in a cash game is constantly. It is also the
kind of thing the dealer gets blamed for, because the tool is the thing everyone at
the table is reading position off.
The dead button rule holds the rotation to the seats for one hand instead of to the
players. The button follows the small blind round, which at a table with nobody
missing is the same next-player-along it always was; when that player has gone the
button rests on their empty chair for a hand, and when the big blind has gone no
small blind is posted. Either way the big blind advances exactly one player, which
is the whole point of it.
Worked out from who actually posted last hand rather than from the seating as it
stands, because by the time the question is asked the people it turns on are the
ones who have left. Heads-up is exempt — the button is the small blind there and
there is no position to leave dead — as is a table too sparse to have a rotation to
hold to, and a dealer who moves the button or names a blind by hand overrules it.
Both dead positions are said out loud rather than left to look like a fault: the
deal prompt explains them in a sentence, and the dead button goes hollow and dashed
so the felt agrees with the words.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
|
||
|
|
3149b48c14 |
Remember the pots the dealer throws away, so the bank check stops blaming them
Starting the next hand with money still in the middle destroys it. The tool asks
first, and says afterwards how much went. Nothing wrote it down, though, so when
the bank check came round at cash-out it saw only that chips were gone:
$5 more cash than chips in play. Someone has paid for chips they haven't
been given.
and settle-up told the dealer to fix it before anyone cashed out. Both were
accusing the dealer of a miscount for money the tool itself had removed, at the
dealer's instruction, minutes earlier — the one kind of shortfall that isn't a
mistake, reported as the one kind that is.
Discards are now recorded on the table as they happen (undoably, with the rest
of the Next Hand step), the reconciliation counts them alongside the chips, and
the panel shows a "Taken off the table" line so the number is visible rather
than merely subtracted. A genuine shortfall on top of a discard still reports,
at its true size — the discard accounts for its own $5 and not a cent more.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
|
||
|
|
ab92259e3e |
Put the dealer's own notes in the file they hand over
The CSV export carries the hands, the standings and the settle-up. It carried neither of the two places a dealer types context. Each seat can hold custom fields -- "IOU: $20 to Ann", "Seat rent: paid to 2am", "Venmo: @ann" -- and the table has a notes box for the rest: side bets, a high-hand promo, who owes the box from last week. Both are typed precisely because they matter to settling up, and both were visible only inside the tool. The one place the context was written down was the one place it could not leave. Now the standings rows end with a Notes column carrying that seat's fields, and the table's notes go in a section of their own at the end. Player,Paid In,Won,Net,Bought In,Chips Now,Up/Down,Notes Ann,20.00,60.00,40.00,200.00,150.00,-50.00,Seat rent: paid to 2am; Venmo: @ann Ben,20.00,0.00,-20.00,200.00,200.00,0.00,IOU: $20 to Ann Cal,20.00,0.00,-20.00,200.00,250.00,50.00, Table Notes "High-hand promo until midnight. Mo owes the box $15, from last week." Notes go last so the money columns keep the shape a spreadsheet expects, and the multi-line note is quoted as one cell rather than becoming rows that read as more players. An existing test caught this, correctly "every money cell parses as a number" asserted that every column after the name was numeric, and a Notes column is text by definition. The invariant it protects is real -- the file exists so somebody can total a column -- so it now names the six money columns and checks those, rather than "everything after the first". That is the assertion it always meant. 9 tests (278 total): a seat's fields ride along, the money columns keep their order, a seat with nothing written gets an empty cell rather than the word undefined, a blank field does not leave a stray colon, a multi-line note survives as one cell, no notes means no section rather than an empty heading, and asking for the standings does not change the seats. Driven through the real Export CSV button, not just the function: the downloaded file carries both seats' notes and the table's. Audit and contrast clean, no console errors. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
4cc8faf8cb |
Notice when the same table is open twice
Opening the tool in a second tab -- tapping the bookmark again rather than hunting for
the tab you already had -- silently throws away whatever the first one is doing.
Each tab holds its own copy in memory and each save writes the whole table, so the
second one to act wins. Driven with two tabs on one profile:
tab A: deal, and Ann calls stored -> bettingOpen true, committed [0,1,2,2]
tab B: type a name in seat 5 stored -> bettingOpen false, committed [0,0,0,0]
The deal, the blinds and the call are gone, and neither tab said anything. That is the
same silent shape as the save and load failures either side of this change.
The browser already tells us: a storage event fires in every other tab when the key
changes, and never in the one that wrote. So a tab that sees its table move underneath
it now says so, once, with the way out attached:
toast: "This table is open in another tab. Whichever acts last wins." [Reload]
panel: "This table is open in another tab, and both are writing to it — whichever
acts last wins, so work done in the other one can vanish. Close one, then
reload this tab to pick up the table as it stands."
Watched rather than merged, deliberately. Replacing the table under a dealer's hands
in the middle of a hand would be its own hazard, and reloading is the only resolution
that does not throw somebody's work away -- so it is offered rather than taken.
6 tests (269 total): a write from elsewhere is noticed, another app's key and our own
recovery key are not our business, ten writes produce one warning, watching twice
listens once, no listener is not an error, and no window at all is not an error.
Driven with two live tabs: both quiet at first, A deals and B warns with the reload
offer, A stays quiet about its own writes, two more actions in A add no further toasts
in B, and reloading B picks up A's table -- bettingOpen true, committed [2,1,2,2] --
with the warning cleared. Audit and contrast clean, no console errors.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
|
||
|
|
cfb83a2ba6 |
Don't quietly write over a save that couldn't be read
Last change made a failing save say so. This is the other direction, and it turned out to be worse. load() is well defended -- try/catch, a shape check, sensible fallbacks -- and every one of those paths ends in `return this.createDefault()`. So an unreadable save becomes an empty table. Driven with a save truncated to 70% of its length, the way an interrupted write leaves one: 1762 bytes stored, 1233 after truncating loaded: 0 named seats, hand #1 instead of #34 toast: none. warning: none. anything on the page: no. A dealer mid-session sees an empty felt and cannot tell whether they mis-tapped Reset, whether the browser lost the night, or what. And then the first edit destroys the evidence. I typed a name into a seat and storage went from the 1233 unreadable bytes to 2549 bytes of fresh default state. Whatever a person could have picked out of that JSON -- the names, the buy-ins, the standings -- was gone one tap after the tool decided it could not read it. Now the unreadable value is moved aside to its own key before the fallback, and the table says what happened: toast: "The saved table couldn't be read, so this one started empty." panel: "The saved table couldn't be read, so this one started empty. The unreadable copy has been kept rather than written over." It is deliberately not offered as a restore. Half a JSON document is not a table, and pretending otherwise would be worse than an empty one. It just stops being destroyed. The two warnings share the Session panel line and both can show at once -- a browser having trouble with storage tends to have it in both directions -- with the load message first, because "why is my table empty" is the question the dealer is standing there asking. 8 tests (263 total): nothing stored is a new table rather than a failure, a good save loads clean, a truncated one is reported, valid JSON of the wrong shape counts too, the copy is kept, a later save does not touch it, the listener fires once across three reads, and storage with no room to keep the copy is not a crash -- which is exactly the storage most likely to hand back half a value in the first place. Driven through the UI: warning and toast on a corrupted save, the set-aside copy still 1233 bytes intact after editing a seat, and nothing shown at all on a healthy table. One audit correction rides along. The collapsed-column check measured inside hidden elements, so the standings header -- hidden with its table until somebody buys in -- was reported as three collapsed columns on every empty table. It skips hidden subtrees now, and still catches a real one: squeezing a visible column reports the header and all three rows. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
4071be5329 |
Say so when the browser refuses to save
Every save goes through one try/catch, and the catch was empty:
} catch {
// Storage full/unavailable (private browsing, etc.) — the table still works
// for the rest of the session, it just won't survive a refresh.
}
The comment is accurate and the behaviour is right -- the chips have already moved in
memory and refusing to continue would be worse. What was missing is anybody being
told. Driven with setItem throwing: two saves failed, no toast, nothing on the page,
and a refresh came back to before the hand was dealt with the dealer none the wiser.
That is the same shape of fault the bank reconciliation exists for. Silent until it is
too late to act on, and the way you find out is the last two players cashing out.
Now it says so twice, in the two places that suit the two timescales.
A toast the first time, and only the first time -- a dealer taps this bar thirty times
a hand and does not need thirty warnings. Measured: four failed saves, one toast.
"This table isn't being saved — refreshing would lose it. Keep this tab open."
And a standing line at the top of the Session panel, because a toast is a moment and
this is a fact about the rest of the night. The raise pad dismisses toasts, and the one
warning that must not be missed is not the one to leave to a toast.
"This table isn't being saved — the browser refused. It'll keep working, but
closing or refreshing this tab loses the session. Export CSV below keeps a copy."
Amber, like the bank check's disagreement, because both mean the same thing: your
record of the night is wrong and you can still do something about it. The export is
named because it is the thing that helps.
6 tests (255 total): a good save reports success and leaves no mark, a failing one
reports failure rather than throwing, the listener fires exactly once across twenty
failures, it is handed the error so the reason can be reported later, a failing save
still does not stop the table, and no listener at all is not an error.
Driven through the UI with setItem throwing: hidden while saving works, toast on the
first failure, zero further toasts across three more actions, the panel line present
and readable. Contrast clean, layout audit clean, no console errors.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
|
||
|
|
8a7d7b09bf |
Print the two shortcuts a dealer had no way to find
Seven keys drive this tool. Five of them are printed on the control they press -- F on Fold, C on Call, R on Raise, the return arrow on Deal and on Raise to, esc on Cancel. Two were printed on nothing at all: A opens the amount pad on All-in U takes back the last action A dealer who learned F, C and R from the buttons had no way to discover the other two existed. They are the two worth knowing: A is the fastest action in the tool and U is the one that saves a misclick. Both are on their controls now. The key lookup matches any of a button's classes, so the raise pad's four quick amounts can share one class for their looks while only All-in carries a key. And a legend in the sidebar, listing all seven with what they do, built from the same list the handlers are keyed off. There is a test that every key in the map has a line in the legend and that every line names a key that really is bound -- a shortcut with no line is one nobody can find, which is the whole bug this fixes. The panel is hidden on a touch device. A phone has no keys to press, and it is the same pointer question the touch-target rules ask rather than a guess about width. One thing I broke and fixed on the way. .actionKey pins itself to the top-right corner of a big action button. The undo control is a 30px pill with overflow hidden and no positioning context, so the U escaped it and floated above the action bar -- visible in the screenshot before I looked properly. It has its own inline class now, checked at six sizes: the cap sits inside the pill and inside the bar, and the pill is not clipped. 5 tests (249 total). Driven at six sizes across touch and pointer contexts: the undo pill shows U, the raise pad shows A on All-in and nothing on Min, half pot and Pot, the legend lists all seven, and it is display:none on a phone. Contrast clean, layout audit clean but for the landscape phone already known from #63, no console errors. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
ebf22e3153 |
Shorten the rulings index to the words a dealer would say
Last change added an index to the rulings sheet and I wrote down what it cost: 342px at the top of a 390px-wide sheet, so a dealer reading straight through scrolls past the whole thing first. That was an honest trade at the time. It is also fixable. The index was labelled with the section headings, which are sentences -- "A short all-in doesn't reopen the betting" -- so nine of them stacked one per row. A dealer hunting for that ruling is looking for the words "short all-in", not the sentence. Misdeal Board card early Out of turn Short all-in Uncalled bet Oversized chip Minimum raise Odd chip String bet phone 342px -> 143px 9 rows -> 3 desktop ~200 -> 70px -> 2 landscape -> 94px -> 2 The first ruling now starts 309px into the sheet on a phone rather than 440. Each short label lives on its own section as data-short, next to the heading it shortens, and the index falls back to the heading when there is not one -- so a ruling added later still appears, still without anyone having to write it twice. The full sentence stays as the button's aria-label, so the short label never costs a screen reader the heading. Checked at four sizes across touch and pointer contexts: nine labels, nine aria-labels, every chip 44px on touch, jumping still lands and focuses the right heading, contrast clean, no console errors. 244 tests still passing. Also looked at, and left alone: the card picker refuses a card already dealt -- disabled, titled "Already dealt to Ann" -- and every one of its 52 buttons clears 44px. A messy nine-seat table with 25/50 blinds, two folded players, a busted seat, accented and hyphenated names, a $125,000 stack and a filled notes field audits clean at every size but the landscape phone already known from #63. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
f0994a8aed |
Get to the right ruling in one tap, and cover the string bet
The rulings sheet is opened mid-argument, with players waiting. It was eight rulings of prose in a row with no way in. Reaching the last one meant scrolling: 1.6 screens on a desktop 2.1 on a phone 3.5 on a phone held sideways A dealer looking for the odd-chip rule had to read past seven others to find it. There is an index across the top now, one chip per ruling, and tapping one scrolls its section into view and puts the keyboard and a screen reader on its heading. Built from the sections rather than written out beside them, so a ruling added later turns up in the index without anyone having to remember to add it twice. And the missing ruling I went looking for what home games actually argue about rather than guessing, and the answer that kept coming back alongside misdeals -- which were already here -- was the string bet. It was the one common dispute the sheet had nothing to say about. Betting in one motion - the string bet A bet is whatever goes in on the first trip to the stack, unless the amount was said out loud first. Reaching back for more chips without having said "raise" is a string bet, and only the first motion stands. Saying the number is always enough on its own: "raise to sixty" binds whatever order the chips arrive in. It's the rule home games invoke most and enforce least, and both are fine -- the point is that a player can't watch the table react and then decide how much to bet. Say it, or push it all at once. That second paragraph is deliberate. The sheet's whole stance is that house rules win, so a ruling that tells a home game it must enforce something would be the wrong voice. What it costs: the index is 342px at the top of a 390px-wide sheet, so a dealer who wants to read straight through now scrolls past it first. That is the trade -- it is a jump table you use once and then read, and the alternative was scrolling blind. Driven through at four sizes across touch and pointer contexts: nine rulings, nine index entries, every one of them 44px on touch, tapping the last one lands it on screen and focused. Focus stays inside the sheet across fourteen tabs, Enter on a chip jumps, Escape closes, and opening it three times still builds nine entries rather than twenty-seven. Contrast clean, no console errors, 244 tests still passing. Sources: cardplayerlifestyle.com and pokernews on string bets; the sheet still follows the TDA rules it cites. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
3fe16fa7f2 |
Let a phone move a player to another seat
The hint under the table has said this on every screen since the gestures went in:
Tap a seat to edit - double-tap to fold - press and hold for quick actions -
drag to swap seats.
Three of those work on a phone. The fourth never has. Swapping seats is HTML5
drag-and-drop -- draggable="true", dragstart, drop -- and a touch screen does not
generate drag events at all. So the one clause written in the same breath as "tap"
and "press and hold" was the one thing a thumb could never do.
I found it by dragging seat 1 onto seat 4 with real touch pointer events and watching
nothing happen: no swap, no menu, no error.
Moving a seat is in the long-press menu now, which is the route a thumb already takes
for everything else. "Move to..." replaces the action list with the other chairs, by
number and name, and picking one makes the move:
Move Ben to...
Their chips, cards and bet go with them. The button and the blinds stay on the chair.
Seat 1 - Ann Seat 3 - Cal Seat 4 - Dee Seat 5 - empty ...
That subtitle is the rule, and it is the one a card room uses: the whole seat travels,
while everything naming a position stays put. Somebody who sits down in the big
blind's chair is the big blind.
Driven through on a phone: Ben, small blind, $109 behind with $1 posted, moves to
seat 5 with both numbers; seat 2 goes empty; the button stays on seat 1; the toast
reads "Ben moved to seat 5" and undo puts it all back. Reopening the menu on another
seat shows actions again rather than the chair list it closed on.
The hint tells the truth per device now -- dragging is only mentioned where a mouse
exists. Dragging still works there, checked.
8 tests (232 total): the whole seat moves, the bet moves with it, chips are conserved,
the button and the blinds stay on their chairs, swapping twice is a no-op, and a chair
that is not there is refused rather than swallowed.
One audit fix rides along. The "one dealer button" check counted the empty slot that a
hovered seat deliberately fades in, so it reported two buttons whenever a mouse
happened to rest on the table -- which is most of the time a mouse exists. It ignores
the hovered seat now, and still catches the real fault: with the pre-#62 behaviour put
back it reports six, or five while hovering.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
|
||
|
|
f199148046 |
A pre-filled amount is a suggestion, so reaching for the box replaces it
Every amount box in this tool opens pre-filled: the raise pad offers the minimum, Add chips offers a buy-in, the seat editor offers the stack a player already has. A dealer taps one because they want a different number -- and a tap puts the caret after the suggestion rather than over it. Driven on an emulated phone: raise pad shows 4 tap, type "60" -> 460 add chips shows 200 tap, type "50" -> 20050 seat stack shows 200 tap, type "175" -> 200175 The raise pad is the most time-critical control in the tool, and 460 is a number the dealer never said. The all-in warning happens to catch that one against a $200 stack; against a deeper stack it is simply accepted, and the pot is wrong from then on. Number boxes now select what is in them when they take focus, so the first digit typed replaces the suggestion. raise pad 4 -> "60" add chips 200 -> "50" seat stack 200 -> "175" a name Cal -> "CalX" That last line is the point of the rule. Only number boxes: type=number, or the type=text/inputmode=decimal pair the raise pad uses to get a keypad without spinner arrows. A name or a note is a thing you edit, and selecting all of it on focus would be the same mistake pointing the other way. Delegated from the document rather than wired per field. The raise pad, the add-chips sheet and the seat editor all rebuild their inputs on every render, and a box added later should not have to remember to ask for this. The select is deferred a tick. Doing it inside the focus handler loses the race with the browser placing its own caret, and the selection collapses again the moment the tap completes. 10 tests (224 total) on the predicate, including the ones that must stay false -- name, note, colour swatch, checkbox, select, and an input with no type stated. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
96cd7684f0 |
Buying chips puts you back in the game
Handing over cash and being dealt in are one event at a real table. Here they were two, and the second one was easy to miss. Last change deals a busted player around until they rebuy. So the dealer takes their money, taps Add chips, and: Ben: inHand false -> false stack 0 -> 100 buyIns 100 -> 200 toast: "Ben added $100." He has chips, he is not in the game, and nothing on screen says so. He gets skipped hand after hand until somebody notices and finds "Deal in" in the seat menu. Now the buy-in does both, and says which: mid-hand: "Ben added $100 - in from the next hand." inHand true, folded true between hands: "Ben added $100 - dealt in." inHand true, folded false Folding him for the hand in progress is the rule "Deal in" already follows -- you can't buy into a pot you have not been paying for -- so the two doors into the game behave the same way. It is one undoable step: undo takes back the chips, the cash record and the seat. Not for a seat that was sat out while holding chips. That is a dealer who sat someone out on purpose and is topping them up, and dealing them back in would be the tool guessing at something it was not told. The test reads the seat's own recorded stack rather than stackOf(), which substitutes the table's starting stack and would make a seat with nothing recorded look like it was holding chips. busted, sat out -> back in new chair, no stack yet -> in sat out holding $100 -> stays out already in the hand -> nothing to do, including an all-in at zero chips 8 tests (214 total). Driven through the UI: the rebuy, the toast, the next hand actually dealing him in (4 players, live 4), and undo putting all three fields back. Found on the way, not fixed here Buying in on a seat with no stack of its own credits the table's starting stack *plus* the buy-in. A new player handed $100 on a table with a $100 default ends up with $200 of chips against $100 of recorded cash. I am leaving it because the right answer is genuinely unclear and it is not mine to pick. A blank stack means "no chips recorded" to the bank check, which counts it as zero, and "the table's starting stack" to the betting code and the felt, which show it as $100. Both are defensible for their own job, and which one a buy-in should follow decides whether a casual dealer who never types stacks gets $200 or $100 after a rebuy. Worth saying out loud rather than quietly picking one. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
c77161c3e9 |
Stop the last two things the table did behind the dealer's back
Your bug report was about a category, not one button, so I went looking for the
rest of the guessing rather than picking a new topic. Two left, both about the
button -- and one of them was in the fix I'd just shipped.
The deal preview lied
The prompt added last time promises who will post. With the button parked on an
empty chair it promised the wrong pair: it read the button where it currently sat,
while dealing relocates the button onto a player first and only then works out the
blinds.
three players in seats 2-4, button parked on empty seat 1
preview said: s2 posts $1, s3 posts $2
dealing gave: s3 posts $1, s4 posts $2
Exactly the fault you reported -- the display and the thing it describes computed
from different moments -- inside the change meant to remove it. buttonSeatForDeal
answers where the button will be without moving it, so the preview asks the same
question the deal will answer. And the move is now stated before it happens:
"Button moves to Ben - seat 1 is empty."
Moving the button after the deal moved the blinds silently
Once the blinds are posted, moving the button takes them back and re-posts them,
because otherwise the badges and the money disagree. That's the right behaviour
and it was completely silent. The toast says so now: "Button on Dee - blinds taken
back and re-posted."
It also only does it when there is actually something posted to move. Before the
deal there is nothing committed, so passing the button is just moving a marker --
which is the whole point of the setup step.
Checked and deliberately left alone
Streets still advance one press at a time during an all-in run-out, which is right
-- the dealer is physically dealing the board between them. A player who joins
mid-hand is still marked folded for that hand and plays the next one. The
base-pot-amount shorthand still stands in for "everyone antes the same". Nothing
mutates state during a render.
4 tests (152 total): the preview matches what dealing produces with the button on
an empty chair, asking where the button will be doesn't move it, a button already
on a player is left alone, and an empty table doesn't break the question.
Driven through the UI: the prompt warns about the move and then the deal matches
it exactly; moving the button after the deal announces the re-post; and the blinds
survive being passed around three seats with the chip count fixed at 400. Layout
and contrast audits clean.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
|
||
|
|
843ef9cd42 |
Wait to be told to deal, instead of guessing
Dealing and betting were one button. "Next Hand" moved the button and posted the
blinds in the same breath, so the moment between hands -- the one where people are
still sitting down, standing up and arguing about whose turn it is -- was spent
with money already committed.
That's where the guessing came from. The blind badges recompute from `inHand`
every render, but the posted chips are frozen from whenever beginHand ran, so the
two drift apart the instant the table changes:
seat a fourth player after Next Hand
badges said sb=s3 bb=s4, money on the table was s1=$2 s3=$1
s4 was shown as the big blind having posted nothing
take out the big blind after Next Hand
badges moved to bb=s1, s4's $2 stayed in the pot, s1 had posted nothing
press Next Hand twice
burned a hand, moved the button twice, chips went 300 -> 297
Now the dealer says when. Next Hand advances the button and clears the table, and
stops. The bar shows who will post -- "Ben posts $1, Cal posts $2" -- and waits on
"Post blinds & deal". Enter does it too, so a dealer can carry a hand from setup
through the streets to showdown without reaching for the trackpad.
Until that press nothing is committed, so setting the table is free: seat someone,
take someone out, pass the button around, name the blinds by hand. The preview
follows every change and the chip count never moves. Settling a pot ends the hand
rather than starting the next one, for the same reason -- people cash out between
hands.
The preview is honest about what it can't do: fewer than two players and there's
no button at all, just what's missing.
A table saved before this existed has no flag, and guessing "not dealt" would
offer to deal over a live hand. Anything already committed, or anyone already on
the clock, means the betting was open when the tab was closed.
11 tests (148 total): the table waits, no money moves until the deal, seating and
unseating and passing the button all cost nothing, what the preview promises is
what gets posted, and a mid-hand save comes back mid-hand.
Driven through the real UI: seat a player and move the button while waiting, watch
the preview follow with the chip count fixed at 400, deal, play it out, settle, and
land back in setup. Triple-tapping Next Hand now costs nothing but button position
-- it used to cost a hand and three chips. Layout and contrast audits clean in both
the new setup state and after dealing.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
|
||
|
|
cc6a1902bc |
Stop the tool from throwing a pot away without saying so
Two ways to lose a hand's money by accident, both found by walking the settle flow and checking the chip count afterwards. Next Hand destroyed an unresolved pot silently Starting the next hand clears every seat's contribution, so money still in the middle simply vanished. Measured at a four-handed table with $8 in the pot: the table went from 800 chips to 792, and the toast said "Next hand — button on seat 3. Blinds posted." — not a word about the eight chips that had just left. It now asks, names the amount, and says what will happen to it. Confirming still works and the toast says what went. The undo was always there, but a dealer can't undo something they were never told about. The wizard's "Close" abandoned a payout that looked finished After picking winners and confirming, the wizard shows "Final Payouts — Ann $8" above two buttons: "Start Next Hand" and "Close". Close pays nobody. Under that heading, with a name and an amount beside it, a dealer taps Close believing the pot has been pushed — and walks away with the chips still in the middle and the hand unlogged. The button now says "Cancel — don't pay yet", with a line underneath spelling out that nothing has been paid. The warning measures the right thing The first version counted the pot badge, which was wrong in a way the tests caught before it shipped: the table-wide base pot is a standing ante for dealers who don't track stacks, and it re-applies to the next hand rather than being lost. Quoting it would have put a confirmation in front of those dealers before every single hand — a nag, on the simplest way to run a table, for money that wasn't going anywhere. HoldemState.unresolvedContributions() measures exactly the fields startNextHand clears: banked contributions, chip counts, and whatever is still in front of players on this street. Verified both ways in the browser — a base-pot table showing a $100 pot is not asked at all, a stack-tracking table with $8 live is. 6 tests (115 total): the quoted number equals what actually leaves the table, mid-street chips are included, a settled hand leaves nothing to warn about, five settled hands conserve every chip, an empty table has no phantom pot, and a standing ante is never warned about. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
9ee4816fa4 |
Make the overlays usable from a keyboard
Measured every overlay in the tool before touching anything. For a dealer running this on a laptop, all six were broken in the same three ways. - focus never moved into the dialog: 0 of 5 measured - Tab walked straight out of the dialog into the page behind the scrim: 5 of 5 - Escape did nothing: only the rulings sheet handled it, 1 of 5 - the pot wizard — the dialog that pays people — had no role and no accessible name at all So opening the pot wizard left focus on the button behind it, tabbing wandered through a page nobody can see or click, and there was no way back out except the mouse. One implementation, not six The overlays are opened and closed by five different modules, which is why they each missed the same things. Rather than patch this into all of them, modal.js watches the `hidden` attribute they already toggle: focus moves to the first real control on open, Tab and Shift-Tab wrap inside the dialog, Escape closes it, and focus returns to whatever opened it. `aria-modal` is set while open and removed on close. An overlay added later gets all of it without anyone remembering to wire it up, which is the failure being fixed. Escape presses the dialog's own cancel button rather than just hiding the overlay, so the confirm dialog still resolves its promise — verified: Escape resolves it to false rather than leaving an await hanging forever. Shortcuts.overlayOpen() had the same shape of bug, listing the six overlay ids by hand. It asks the document now, so a sheet added later can't quietly start letting "f" fold the table behind it. Focus you can see Three focus rules existed for the whole stylesheet, so tabbing relied on the browser default ring, which on these dark custom-painted controls is close to invisible. `:focus-visible` keeps it to keyboard use so a dealer tapping Fold isn't left with a ring on it. That surfaced something small: `.holdemBtn` used `transition: all`, so the new outline faded in over 150ms and the ring lagged a step behind the Tab key. The transition names its properties now. Measured after: 16 of 16 tab stops show a ring immediately, and the press state from before still animates. Also - a mistyped negative stack read back as negative, and flowed into putIn() where Math.min(amount, -50) moves a negative amount and adds chips to the table. Clamped at zero. - accessible names for the chip colour, chip value and notes fields, which had none 11 new tests (99 total), covering negative stacks, negative and unreadable blinds, blinds nobody can cover, and a save that comes back corrupt, truncated, or from before a field existed. Playwright: all five overlays re-measured — focus moves in 5/5, Tab and Shift-Tab trapped 5/5, Escape closes 5/5, focus restored 5/5, role and aria-modal present 5/5. Shortcuts still fire when no dialog is open and are still blocked when one is. Touch guarantees from the earlier round unchanged: 0 of 49 controls on touch-action auto, 0 tap-highlighted. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
3d63ba50ee |
Handle the messy table: people joining, leaving, and arguing about blinds
Home games don't run like a cardroom. People walk up mid-hand, storm off mid-hand, and pass the button round three times before anyone deals. Probing each of those found four problems, one of which loses chips. A player taken out mid-hand took their chips with them `computeContribution` opened with `if (!player.inHand) return 0`, so the moment a dealer switched a seat off, everything that seat had already put in vanished from the pot. Measured: four players in for $2 each, one taken out, pot drops from $8 to $6. Their $2 was still sitting on the seat record — it just stopped counting. A dealer following the tool would have paid out $6 of an $8 pot. Chips already pushed into the middle are dead money. They stay in the pot and go to whoever wins, whether or not the player is still in the hand, and it is not theirs to take back. The `inHand` check now guards only the table-wide base-pot default, which does have to stay tied to being dealt in or every empty chair would be treated as having anted. The pot wizard counts a departed seat as folded, so its chips are in the pot but it can never be picked as a winner. Passing the button before the cards come out Moving the button left the posted blinds where they were while everything positional started reading off the new spot, so the money and the blind positions silently disagreed. While nothing but the blinds are in, moving the button now refunds them and re-posts from where it lands — pass it round six times and the chip count is unchanged. Once anybody has acted the blinds are settled and it stops offering. Naming the blinds by hand Blinds were purely positional with no way to overrule them. The seat menu now offers "Make small blind" / "Make big blind" while only the blinds are in, and a badge with a ring marks a blind that was named rather than dealt. Naming one pins the other where it is. It's refused if it would put both blinds on one player or post to somebody who isn't dealt in, and it lasts one hand — carrying it forward would freeze the blinds on two people for the night. The first hand, and a table that empties out A fresh table puts the button on seat 1 whether or not anyone is sitting there, and a table can empty out around it. The button is moved onto a real player before a hand is dealt. Joining mid-hand A newcomer was added straight into the live seats and would be asked to act in a hand they hadn't been paying into. They now sit the hand out and are a full player from the next one. Nothing is covered on a phone any more Swept 13 viewport sizes with elementFromPoint, five points per control, on the table and every overlay. Before: the action bar covered the bottom seats on short screens (62px of felt at 360x640), the toast covered up to six seats at once — Undo button included, so a tap meant for a player hit "Undo" — and in landscape the table came out 765px tall in a 390px window, putting six of nine seats off-screen entirely. - the action bar's real height is published as `--actionBarHeight` and drives both the page's bottom padding and the toast, replacing a hardcoded 9rem that was wrong the moment the raise pad opened - landscape sizes the felt from the height that's left, not from width. The first attempt did nothing: `padding-bottom` is a percentage of the containing block's *width*, so narrowing the wrap left it full height, and a `max-width: 900px` rule later in the file was overriding it anyway. It's set outright now and lives at the end of the sheet. - on short screens the toast floats over the action bar's status readout rather than over the seats. Nowhere is clear of both; covering something you re-read beats covering something you tap. - headers go compact on short screens, which is where the vertical room came from 13/13 sizes clean, 360x640 through 844x390, portrait and landscape. Also fixes a marker that flickered: `classList.toggle(cls, undefined)` toggles rather than clearing, and `manual` is undefined for positional blinds, so the hand-picked ring appeared on alternate renders. 18 new tests (76 total, all passing). Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
c1a484b97a |
Pay down the Hold'em dealer's tech debt and give it a test suite
Every module in the dealer ends with a `module.exports` guard, so they were all written to be testable, and none of them were ever tested. The correctness fixes that went in recently were checked by hand in a browser console that has since been closed. This puts a real safety net under them and clears out the duplication that made those bugs possible in the first place. Tests (58, ~300ms, no dependencies) Node's built-in runner, no package.json and no install step: the modules are plain script-tag files, so `load.js` just requires them and puts them on globalThis under the names they use to find each other in the page. The randomised suite is the part that matters. Every bug found in this tool so far has been shape-dependent — blinds posted to empty chairs, the button skipping players, heads-up acting out of order — and none of them show up on the full six-handed table anyone would write a fixed example for. It deals 1000 hands per property across random seat counts, empty chairs, stack depths and dealer input, checking that chips are never created or destroyed, that the pot breakdown plus uncalled returns equals the money in, that paying out settles to the cent, and that the button and blinds always land on real players. The generator is seeded so a failure reproduces instead of flaking. One implementation each - `Math.round(x * 100) / 100` was written out 16 times across 6 files. It's `Money.round` / `toCents` / `fromCents` now, in the module both pages already load first. - The chip epsilon was declared twice with the same value under two names (`PotCalculator.EPSILON`, `Betting.EPSILON_CHIPS`). Both now point at `Money.EPSILON`. - `Betting.seatAt` and `HoldemState.nextSeatIndex` were the same modular arithmetic written twice. Walking the seats is exactly where the button, the blinds and the action drifted apart from each other before, so there is one of it now. - The undo snapshot/restore dance appeared three times in main.js, each one re-deleting every key on `state` in place. It's `snapshotState` / `restoreState` once, with the reason it can't just reassign written down. A bug the tests found `layers()` and `uncalledReturns()` both decide what money was never contested, separately, and disagreed. `uncalledReturns` measured each player against the biggest live contribution *including their own*, which makes the biggest bet its own yardstick — so the one case it exists for came back empty, and `layers()` announced the overbet as a side pot only the bettor could win. With Ann shoving 200 into Ben's 60, the dealer was told "main pot 4, side pot 3, side pot 116, side pot 140" — four pots, one of them fictional. It now reads "main pot 123, uncalled 140 back to Ann", which is what a dealer says out loud. Layers are derived from contributions with the uncalled money already removed, so the two can't disagree again. A band every payer folded out of is now merged into the pot below rather than dropped, which was quietly shrinking the announced pot in another edge case. Validated with Playwright against a real hand on both surfaces — the table's live pot readout and Quick Split's all-in mode — plus undo, with no console errors. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
4f72dda6fa |
Ask before a smaller table deletes the players sitting in it
Typing a smaller number into "Seats at table" silently threw away every seat past the new count. Nine players down to six removed three of them — names, stacks and buy-ins gone, no confirmation, no way back. It also quietly falsified the settle-up. Those players keep their rows in the session standings, because the hand log remembers who won what, but the seat they were read from no longer exists — so a player who had just won a pot showed as having bought in for nothing and holding nothing. Shrinking now says who it is about to remove and asks, and either way the change is undoable like the rest of the table's actions. Growing the table, and shrinking onto chairs nobody is sitting in, still happen without a word. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
552ec1a542 |
Add chips to a seat without leaving the table
Somebody rebuying is the most common interruption in a cash game, and the only way to record it was to open the seat editor and retype a stack from memory. Press and hold a seat and there's now "Add chips…": quick amounts off the table's starting stack — half a buy-in, a buy-in, two — with a keypad for the times it isn't one of those. It's undoable like every other seat action, and it records cash handed over as well as chips added, because those answer different questions. The stack moves every hand; what a player has paid in only moves when they buy. The export gains bought in, chips now, and up/down, so settling up at the end of the night is reading a row rather than reconstructing it. Getting that last column right needed care. Chips in front of a player disagree with the hand log for as long as a hand is open — and since blinds post the instant the previous hand settles, that window is almost always. Counting what a player already has in the current pot as still theirs makes the cash position agree with the hand log at any moment, including across a rebuy. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
9a5b974fda |
Add a rulings reference for the calls that come up mid-hand
The tool handles the money; the other half of dealing is the calls. A home-game dealer who isn't a pro has to rule on a misdeal or a prematurely turned board card with a table watching, and the usual outcome is an argument settled by whoever is loudest. A "Common rulings" link under the table opens a reference for exactly the situations that come up: what counts as a misdeal, what to do when the fourth board card gets turned before the betting is finished, and how acting out of turn is normally treated. It follows the Tournament Directors Association rules, and says plainly that house rules win and that a dispute gets settled between hands rather than during one. It's a sheet rather than a sixth sidebar panel — a dealer consults this rarely and needs it immediately, which is the opposite of what a panel is for. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
4ceeed71a2 |
Add keyboard control for a dealer working on a laptop
The phone got gestures a few rounds back; the desktop still had a mouse and nothing else. On a bar that gets tapped thirty times a hand, moving a hand to the trackpad between every decision is the slow part. F folds, C checks or calls, R opens the raise pad, A shoves, Enter confirms a raise or moves the hand on, Esc backs out, U takes the last action back. Each button wears its key in the corner, hidden on touch devices where it would just be clutter on a button being tapped anyway. The shortcuts press the same buttons a dealer would tap rather than calling the betting engine themselves, so there's no second code path to drift out of step with what the bar is actually offering — if a button isn't on screen, its key does nothing. Nothing fires while a name, amount or note is being typed, or while an overlay owns the keyboard. Typing "Francesca" into a seat name must not fold the table. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
95c523738c |
Let the dealer take back an action from the bar
An audit of what's accumulated turned up an inconsistency worth fixing: double- tapping a seat to fold someone was undoable, but tapping Fold in the action bar — the button a dealer actually uses all hand — was not. Nor was Call, Raise, or dealing a street. The safest path through the tool was the shortcut, and the main one had no way back. Every action taken from the bar now goes through one place that records it first, and the bar carries a standing Undo naming what it will take back: "Ben's fold", "dealing the flop". Twelve steps deep, because a misclick usually gets noticed a beat late and one level of undo would only cover the luckiest case. It's a control rather than a toast per action. A dealer taps this bar constantly, and a toast after every fold is noise they'd learn to ignore — which is precisely when they'd miss the one that mattered. The history is discarded whenever a hand starts or settles, because those snapshots describe a hand that no longer exists. The whole-hand Undo on the toast already covers that boundary. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
dabf1100c9 |
Add double-tap and press-and-hold shortcuts to the seats
Folding someone meant opening their editor, finding the Fold box, ticking it, and dismissing the sheet — four interactions for the thing that happens most often in a hand. Double-tap a seat to fold, and again to put them back. Press and hold for a quick menu of everything else you do to one player: fold, all-in, sit out or deal in, hand them the button, or open the full editor. Both are undoable the same way the header buttons are, because a gesture is easier to fire by accident than a button. The single tap now waits out the double-tap window before opening the editor — a fifth of a second of latency on opening a form, in exchange for a fold that doesn't also fling a sheet up over the table. A press that moves is a drag, so the hold is cancelled and swapping seats still works, and the puck shrinks slightly while held so a press that's being counted looks different from one that's being ignored. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
0759d89d13 |
Pick hole cards from a grid instead of typing them
Typing "AH" into a two-character box is fine at a desk and miserable at a table: it needs a keyboard, it accepts nonsense like "ZZ", and it happily deals the same card to two players. The two card fields become face-up card slots. Tapping one opens a 52-card grid — four suit columns, thirteen rank rows, one tap per card — and picking the first card moves straight on to the second, because a dealer picking hole cards is always picking two. Cards already dealt elsewhere at the table are greyed out and say who has them, so a duplicate is visibly out of play rather than silently accepted. Cards are still stored as the same two plain characters everything has always read; the suit only becomes a symbol on its way to a human. That formatting is now shared, so the table pucks, the seat editor, the pot wizard, the hand history and the CSV all say A♥ K♦ rather than three of them saying AH KD. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
7bedee2934 |
Track the betting live so the dealer taps instead of types
Until now a dealer kept the money in their head and typed each player's total into a form. This adds the running hand: blinds post themselves, every seat has a stack, and one tap per decision moves the action on. The bar sits at the bottom of the screen where a thumb already is, and shows only what's true right now — Fold, then Check or Call with the amount owed, then Bet or Raise. Calling more than a player has left says "Call all-in" rather than quoting a number they can't cover. Raising offers min, half pot, pot and all-in before it offers a keypad, because a dealer calling "raise to sixty" wants a button. When everyone has answered, the bar becomes Deal Flop, then Turn, River, and finally Showdown, which hands off to the pot wizard that already existed. Winnings go straight back in front of the winner, so the next hand's stacks are already right. The money hooks into what was already there rather than running beside it: a street's chips sweep into each seat's amountOverride, the same field the pot badge, the pot wizard, the session standings and the CSV export all read. So nothing downstream changed, and a dealer who prefers the old typed workflow still has it. Tables saved before this exist without any of these fields, so load() fills them in rather than making someone reset a table mid-session. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
d01c67fc45 |
Replace the browser confirm boxes with undo and in-page dialogs
Three system confirm() dialogs sat in the middle of dealing. The worst was on Next Hand — the most-pressed button in a session, asking "are you sure?" every single time, stealing focus and, on a phone, taking over the screen. Next Hand now just happens, and a toast says where the button landed with an Undo beside it for the few seconds anyone would notice a misclick. The same undo covers the pot wizard, which is the more valuable case: paying the wrong player is a real mistake, and rolling it back restores the hand history entry and the session standings along with the seats. The snapshot is in-memory only — an undo is for the mistake you just made, not one from last night. The two genuinely destructive actions keep a confirmation, but as an in-page dialog that matches the table instead of the browser's own: Escape or the backdrop cancels, Enter confirms, and on a phone the buttons go full width with Cancel nearest the thumb. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
a39675f975 |
Make the dealer table usable on a phone
Two things were broken on a narrow screen. The seat ring is drawn on a radius measured from the table wrap, so at phone width the left- and right-most seats hung off the edge of the viewport — at a ten-handed table two players were simply not on screen. And tapping a seat opened an editor far enough down the page that it looked like nothing had happened. The ring is now inset by half a puck on phones, which keeps every seat on screen, and the pucks are narrower there so neighbours stop overlapping where the ring flattens out at the top and bottom. The seat editor becomes a bottom sheet that slides up over the table: grip, Done button, tap the same seat again to dismiss, table still visible above it. Its controls grow to thumb size, and every field goes to 16px — below that iOS zooms the page on focus and the dealer has to pinch back out mid-hand. Header actions become full-width tap targets, and the page padding tightens to give the table the width it needs. Nothing above 700px changes. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
bcfc1f2f52 |
Track session standings so the dealer can settle up at the end of the night
The hand log answered "what happened in hand 12"; it couldn't answer "what does Alice owe". Each resolved pot now records a per-seat ledger (what that player put in, what they took down) alongside the payouts, and a Session Standings panel sums it into one row per player — paid in, won, net, sorted by who's up. The CSV export carries the same table, so one file covers both the hand-by-hand trail and the cash-out numbers. Keyed by seat id rather than name, so renaming or reseating a player doesn't split their totals. Hands recorded before this change have no ledger and are skipped rather than counted wrong. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
d168fedf1f |
Add a CSV export for the Texas Hold'em hand history log
A dedicated dealer needs a paper trail that outlives the browser tab, not just a scrollable in-app panel — the Export CSV button downloads the full resolved-pot log (hand #, time, pot, payouts) for recordkeeping or a disputed chip count after the session ends. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
68e263f325 |
Add a persistent hand history log to the Texas Hold'em dealer tool
Give the dealer a running record of resolved pots — hand number, total pot, who got paid what, and the time — instead of losing that information the moment the pot wizard closes. It's the kind of audit trail a real dealer needs when a chip count gets disputed after the fact. - state.js tracks state.handNumber and state.handHistory (capped at 200 entries) and persists both through the existing localStorage save/load path, with backfill defaults for tables saved before this change. - potWizard.js now hands its final payouts back to the caller instead of discarding them once "Start Next Hand" is clicked. - main.js records a history entry (skipping hands where nobody actually won anything) before advancing to the next hand, and wires up a "Clear history" control that also resets the hand counter. - A new sidebar panel lists history entries newest-first; the header shows a running "Hand #N" badge next to the existing subtitle. Verified via Playwright MCP: seated two players, resolved a pot, and confirmed the header's hand counter incremented, the history panel listed the payout, the entry survived a full page reload (localStorage persistence), and Clear History reset both the log and the counter. No console errors during the flow. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01C3NiiUDimoKDJ8GjC3KL3x |
||
|
|
3851786b90 |
Add dedicated-dealer mode and redesign the table's visuals
Add a "Dedicated dealer (not a seat)" toggle for tables run by someone outside the game: it hides every seat's dealer button while blinds keep rotating automatically off the same internal position each hand. Also gave the table a real casino look, based on reference felt/rail and seat-pod patterns found via web search (huggingface.co/spaces/Estazz/ PokerGameDesigner, github.com/Tehes/poker): a richer radial felt gradient, a gold piping ring inside a wood-toned rail, a subtle center watermark, a pill-shaped chip-well pot badge, softer seat pucks with a pill-badge total and red/black suit coloring on cards, and casino-chip-styled dealer/blind badges (the dealer chip now dims to a faint disc on other seats and only lights up on hover, decluttering the ring of ten clickable buttons down to one visible button on the actual dealer). Verified via Playwright MCP: toggle hides/reveals dealer chips correctly, SB/BB still track it, no console errors. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_018CmAti1baCjidbJM2HeJfk |
||
|
|
25bf67062a |
Fix Texas Hold'em dealer page: script timing crash and seat overlap
main.js ran an IIFE immediately, but the layout renders @section Scripts inside <head> before <body> exists, so every getElementById() call hit null. Wrapped init in DOMContentLoaded, matching the rest of the codebase (e.g. ChessScripts/chessMain.js). Separately, the original seat cards packed a full data-entry form (name, toggles, 2 cards, 5 chip counters, $ override, custom fields) into each of up to 24 positions around a circle — at any screen size, adjacent seats overlapped badly. Replaced with compact seat "pucks" for the table view (name, cards, total, blind/dealer badges) plus a dedicated seat editor panel in the sidebar that opens for whichever seat is clicked. Verified via Playwright MCP: load with no console errors, drag-and-drop seat swap, and a 3-way side-pot resolution (short all-in capped correctly, remainder awarded to the next-best hand) end to end. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_018CmAti1baCjidbJM2HeJfk |
||
|
|
98f95df8fb |
Add Casino demo section with a Texas Hold'em dealer's pot-splitting assistant
New /casino hub page (Texas Hold'em live, other table games marked coming soon) and /casino/texasholdem: a drag-and-drop seating table with auto-rotating blinds, per-seat chip/dollar contribution tracking, and a side-pot resolution wizard that asks who won and who's next until every dollar in the pot (including dead money from folds) is assigned. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_018CmAti1baCjidbJM2HeJfk |