Rebuild bet sizing as a bottom sheet with situation-aware presets

Bet/Raise now opens a sheet whose presets follow the spot: open sizes
in BB (adjusted for limpers), 3bet/4bet multiples, postflop pot
fractions and raise multiples. Adds -/+ steppers with hold-to-repeat,
Min/All-in at the slider ends, and a square-curve slider so small sizes
stay precise with deep stacks. The hand view now exposes the street's
raise count so the client can tell a 3bet from a 4bet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
agomizi1cmandClaude Opus 5.5 committed 2026-10-21 22:18:26 +09:00
1 parent ed5617a47a
commit b6ce648b5a
4 files changed
+412 -77

No files matched your search

+184 -30
View File
@@ -2342,52 +2342,74 @@ body.page-table {
color: var(--label-3); color: var(--label-3);
} }
/* レイズ額: レイズボタンから上に開き、閉じるときはボタンへ戻る */ /* ベット/レイズ額のシート: テーブル(ポット・相手のベット)が見えるよう背景は薄く暗くするだけ */
.raise-panel { .bet-backdrop::before {
position: absolute; background: rgba(0, 0, 0, 0.16);
left: 12px; }
right: 12px; .bet-sheet {
bottom: calc(100% + 10px); max-width: 520px;
z-index: 6;
max-width: 496px;
margin: 0 auto;
padding: 14px 16px 14px;
border-radius: 24px;
background: var(--material-thick); background: var(--material-thick);
-webkit-backdrop-filter: blur(30px) saturate(180%); -webkit-backdrop-filter: blur(30px) saturate(180%);
backdrop-filter: blur(30px) saturate(180%); backdrop-filter: blur(30px) saturate(180%);
box-shadow: var(--shadow-l), inset 0 0 0 0.5px var(--material-edge); box-shadow: 0 -12px 40px rgba(0, 0, 0, 0.2), inset 0 0.5px 0 var(--material-edge);
transform-origin: 82% 100%;
} }
.raise-head { .bet-sheet::after {
background: var(--material-thick);
}
.bet-header {
padding-bottom: 4px;
}
.bet-heading {
display: flex; display: flex;
align-items: baseline; align-items: baseline;
justify-content: space-between; justify-content: space-between;
gap: 10px; gap: 10px;
text-align: left;
}
.bet-context {
min-width: 0;
overflow: hidden;
color: var(--label-2);
font-size: 0.8125rem;
font-variant-numeric: tabular-nums;
text-overflow: ellipsis;
white-space: nowrap;
}
.bet-body {
padding: 4px 20px 2px;
}
.bet-amount-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
} }
.raise-value { .raise-value {
display: flex; display: flex;
align-items: baseline; align-items: baseline;
min-width: 0; min-width: 0;
font-family: var(--font-num); font-family: var(--font-num);
cursor: text;
} }
.raise-value .cur { .raise-value .cur {
color: var(--label-2); color: var(--label-2);
font-size: 1.5rem; font-size: 1.75rem;
font-weight: 700; font-weight: 700;
} }
.raise-amount { .raise-amount {
width: 8ch; width: 7.5ch;
min-width: 0; min-width: 0;
padding: 0; padding: 0;
border: 0; border: 0;
border-radius: 0; border-radius: 0;
outline: none; outline: none;
background: transparent; background: transparent;
color: var(--label);
font-family: var(--font-num); font-family: var(--font-num);
font-size: 2.125rem; font-size: 2.5rem;
font-weight: 700; font-weight: 700;
letter-spacing: -0.022em; line-height: 1.15;
letter-spacing: -0.025em;
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
-moz-appearance: textfield; -moz-appearance: textfield;
appearance: textfield; appearance: textfield;
@@ -2401,17 +2423,131 @@ body.page-table {
box-shadow: inset 0 -2px 0 var(--orange); box-shadow: inset 0 -2px 0 var(--orange);
} }
.raise-meta { .raise-meta {
display: grid;
flex: none; flex: none;
justify-items: end;
gap: 1px;
font-variant-numeric: tabular-nums;
}
.raise-meta b {
font-size: 1.0625rem;
font-weight: 700;
}
.raise-meta span {
color: var(--label-2); color: var(--label-2);
font-size: 0.8125rem; font-size: 0.8125rem;
font-weight: 600; font-weight: 600;
}
.raise-meta span:empty {
display: none;
}
/* 場面ごとの候補(3bet の 3x・ベットの 50% など)。上に倍率、下にその額 */
.bet-presets {
display: grid;
grid-auto-columns: 1fr;
grid-auto-flow: column;
gap: 6px;
margin-top: 12px;
}
.chip-btn {
-webkit-appearance: none;
appearance: none;
display: grid;
place-content: center;
gap: 1px;
min-width: 0;
min-height: 54px;
padding: 6px 2px;
border: 0;
border-radius: 14px;
background: var(--fill-2);
color: var(--label);
font: inherit;
cursor: pointer;
transition: background-color 0.2s ease, color 0.2s ease, transform 0.45s var(--ease-spring);
}
.chip-btn b {
font-size: 0.9375rem;
font-weight: 700;
letter-spacing: -0.01em;
}
.chip-btn span {
overflow: hidden;
color: var(--label-2);
font-size: 0.6875rem;
font-weight: 600;
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
text-overflow: ellipsis;
white-space: nowrap;
transition: color 0.2s ease;
}
.chip-btn:active:not(:disabled) {
transform: scale(0.93);
transition: transform 0.1s ease-out;
}
.chip-btn.active {
background: var(--orange);
color: var(--on-orange);
}
.chip-btn.active span {
color: var(--on-orange);
opacity: 0.72;
}
.chip-btn:disabled {
color: var(--label-3);
cursor: default;
}
.chip-btn:disabled span {
color: var(--label-3);
}
/* −/+ とスライダー */
.bet-adjust {
display: flex;
align-items: center;
gap: 10px;
margin-top: 10px;
}
.step-btn {
-webkit-appearance: none;
appearance: none;
display: grid;
place-items: center;
flex: none;
width: 40px;
height: 40px;
padding: 0;
border: 0;
border-radius: 50%;
background: var(--fill-2);
color: var(--label);
cursor: pointer;
touch-action: manipulation;
-webkit-user-select: none;
user-select: none;
-webkit-touch-callout: none;
transition: transform 0.45s var(--ease-spring), opacity 0.2s ease;
}
.step-btn .i {
width: 18px;
height: 18px;
stroke-width: 2.6;
}
.step-btn:active:not(:disabled) {
transform: scale(0.88);
transition: transform 0.1s ease-out;
}
.step-btn:disabled {
opacity: 0.35;
cursor: default;
} }
/* 自前のスライダー: つかんだ位置のまま指に付いてくる。端を越えるとゴムのように抵抗する */ /* 自前のスライダー: つかんだ位置のまま指に付いてくる。端を越えるとゴムのように抵抗する */
.bet-slider { .bet-slider {
position: relative; position: relative;
flex: 1;
min-width: 0;
height: 44px; height: 44px;
margin: 4px 0 8px;
outline: none; outline: none;
touch-action: none; touch-action: none;
cursor: pointer; cursor: pointer;
@@ -2460,33 +2596,51 @@ body.page-table {
.bet-slider:focus-visible .bet-thumb::before { .bet-slider:focus-visible .bet-thumb::before {
box-shadow: 0 0 0 3px var(--blue), 0 3px 8px rgba(0, 0, 0, 0.2); box-shadow: 0 0 0 3px var(--blue), 0 3px 8px rgba(0, 0, 0, 0.2);
} }
.presets {
display: grid; /* スライダーの両端: 最小とオールイン */
grid-template-columns: repeat(5, 1fr); .bet-ends {
gap: 6px; display: flex;
justify-content: space-between;
gap: 8px;
margin-top: 2px;
} }
.chip-btn { .end-btn {
-webkit-appearance: none; -webkit-appearance: none;
appearance: none; appearance: none;
min-height: 34px; min-height: 30px;
padding: 4px 0; padding: 4px 12px;
border: 0; border: 0;
border-radius: 999px; border-radius: 999px;
background: var(--fill-2); background: transparent;
color: var(--label); color: var(--label-2);
font: inherit;
font-size: 0.8125rem; font-size: 0.8125rem;
font-weight: 600; font-weight: 600;
font-variant-numeric: tabular-nums;
cursor: pointer; cursor: pointer;
transition: background-color 0.2s ease, color 0.2s ease, transform 0.45s var(--ease-spring); transition: background-color 0.2s ease, color 0.2s ease, transform 0.45s var(--ease-spring);
} }
.chip-btn:active { .end-btn span {
color: var(--label);
}
.end-btn:active {
transform: scale(0.93); transform: scale(0.93);
transition: transform 0.1s ease-out; transition: transform 0.1s ease-out;
} }
.chip-btn.active { .end-btn.active {
background: var(--fill-2);
color: var(--label);
}
.end-btn.allin.active {
background: var(--orange); background: var(--orange);
color: var(--on-orange); color: var(--on-orange);
} }
.end-btn.allin.active span {
color: inherit;
}
.bet-sheet .sheet-footer .btn {
font-variant-numeric: tabular-nums;
}
/* ショーダウンの勝者選択(キャッシュオンリー) */ /* ショーダウンの勝者選択(キャッシュオンリー) */
.award-panel { .award-panel {
+184 -24
View File
@@ -31,6 +31,15 @@
const reviewSheet = Fluid.Sheet($('review-modal')); const reviewSheet = Fluid.Sheet($('review-modal'));
const nameSheet = Fluid.Sheet($('name-modal'), { dismissible: false, focus: '#name-input' }); const nameSheet = Fluid.Sheet($('name-modal'), { dismissible: false, focus: '#name-input' });
// 払って閉じた・背景を押した・Esc のときも「戻る」と同じ
const betSheet = Fluid.Sheet($('bet-modal'), {
focus: '#raise-range',
onClose: () => {
if (!raising) return;
raising = false;
render();
},
});
// ---------- 名前 ---------- // ---------- 名前 ----------
function ensureName() { function ensureName() {
@@ -108,6 +117,8 @@
$('main').hidden = true; $('main').hidden = true;
$('action-bar').hidden = true; $('action-bar').hidden = true;
reviewSheet.close(); reviewSheet.close();
raising = false;
betSheet.close();
$('not-found').hidden = false; $('not-found').hidden = false;
// 自分から切断するので「接続が切れました」は出さない // 自分から切断するので「接続が切れました」は出さない
if (socket) { if (socket) {
@@ -246,6 +257,7 @@
renderSeats(s); renderSeats(s);
if (me) renderActionBar(s, me); if (me) renderActionBar(s, me);
else betSheet.close();
renderReview(s); renderReview(s);
renderTimer(); renderTimer();
@@ -473,10 +485,14 @@
$('sit-in').hidden = !m.sittingOut; $('sit-in').hidden = !m.sittingOut;
$('rebuy').hidden = !m.canRebuy; $('rebuy').hidden = !m.canRebuy;
$('rebuy').textContent = `リバイ(${chips(s.startingStack)})`; $('rebuy').textContent = `リバイ(${chips(s.startingStack)})`;
$('actions').hidden = alt || raising || awarding || reviewWaiting; $('actions').hidden = alt || awarding || reviewWaiting;
$('raise-actions').hidden = alt || !raising; if (raising && o && !alt) {
// レイズ額のパネルはレイズボタンから出て、閉じるときはボタンへ戻る renderRaisePanel(o);
Fluid.presence($('raise-panel'), !alt && raising, { scale: 0.86, y: 28 }); betSheet.open();
} else {
raising = false;
betSheet.close();
}
const fold = $('act-fold'); const fold = $('act-fold');
const call = $('act-call'); const call = $('act-call');
@@ -494,8 +510,6 @@
: o.minRaiseTo >= o.maxRaiseTo : o.minRaiseTo >= o.maxRaiseTo
? `All-in ${chips(o.maxRaiseTo)}` ? `All-in ${chips(o.maxRaiseTo)}`
: o.isBet ? 'Bet' : 'Raise'; : o.isBet ? 'Bet' : 'Raise';
if (raising) renderRaisePanel(o);
} }
// ---------- レビュー(レビュー付きCPU戦の作成者のみ) ---------- // ---------- レビュー(レビュー付きCPU戦の作成者のみ) ----------
@@ -665,18 +679,78 @@
}); });
// ---------- レイズ ---------- // ---------- レイズ ----------
function presetValue(o, kind) { // 場面ごとの額の候補。プリフロップはオープン(BB の何倍)・3bet 以降(相手の額の何倍)、
// フロップ以降はベット(ポットの何割)・レイズ(相手のベットの何倍)。
// ポットの何割は「コールしたあとのポット」に対する額を上乗せする(ポットレイズと同じ考え方)。
// def は開いたときに選んでおく候補
function betContext(o) {
const h = state.hand; const h = state.hand;
const bb = state.bigBlind;
const cur = h.currentBet;
const potAfterCall = h.potTotal + o.toCall; const potAfterCall = h.potTotal + o.toCall;
const base = h.currentBet; const pot = (f) => cur + potAfterCall * f;
const v = { const times = (x) => ({ label: `${x}x`, to: cur * x });
min: o.minRaiseTo, const potPreset = { label: 'Pot', to: pot(1) };
half: base + Math.round(potAfterCall / 2),
threeq: base + Math.round((potAfterCall * 3) / 4), if (h.street === 'PREFLOP') {
pot: base + potAfterCall, const r = h.raises || 0;
max: o.maxRaiseTo, if (r === 0) {
}[kind]; // リンプした人がいれば 1人につき 1BB 足す
return clampRaise(o, v); const limpers = Object.entries(h.players).filter(
([seat, p]) => Number(seat) !== h.bbSeat && !p.folded && p.bet >= cur
).length;
return {
title: limpers ? 'アイソレート' : 'オープン',
note: limpers ? `リンプ ${limpers}人(+${limpers}BB)` : '',
presets: [2, 2.5, 3, 4].map((x) => ({ label: `${x + limpers}BB`, to: (x + limpers) * bb })).concat(potPreset),
def: limpers ? 2 : 1,
};
}
const xs = r === 1 ? [2.5, 3, 4] : r === 2 ? [2.2, 2.5, 3] : [2, 2.5];
return {
title: `${r + 2}bet`,
note: `相手 ${chips(cur)}`,
presets: xs.map(times).concat(potPreset),
def: 1,
};
}
if (o.isBet) {
return {
title: 'ベット',
note: '',
presets: [
{ label: '33%', to: pot(1 / 3) },
{ label: '50%', to: pot(1 / 2) },
{ label: '75%', to: pot(3 / 4) },
potPreset,
{ label: '150%', to: pot(1.5) },
],
def: 1,
};
}
return {
title: (h.raises || 0) >= 2 ? 'リレイズ' : 'レイズ',
note: `相手 ${chips(cur)}`,
presets: [2.5, 3, 4].map(times).concat(potPreset),
def: 1,
};
}
// 候補の額を出せる範囲に収める(SB 単位に丸めるとブラインドが小さいうちは 50% が 57% になったりするので 1 単位)。
// 最小に届かない候補は押せない。オールインを超える候補は最初の 1つだけ All-in として残す。
// 左から額の小さい順に並べる(Pot が 4BB より小さいこともある)。def は並べ替える前の番号
function betPresets(o, ctx) {
let allInShown = false;
return ctx.presets
.map((p, i) => ({ label: p.label, raw: Math.round(p.to), def: i === ctx.def }))
.sort((a, b) => a.raw - b.raw)
.map((p) => {
const to = clampRaise(o, p.raw);
const allIn = to >= o.maxRaiseTo;
const disabled = p.raw < o.minRaiseTo || (allIn && allInShown);
if (allIn) allInShown = true;
return { label: p.label, to, allIn, disabled, def: p.def };
});
} }
function clampRaise(o, v) { function clampRaise(o, v) {
@@ -704,12 +778,15 @@
knob.onUpdate = paintKnob; knob.onUpdate = paintKnob;
if ('ResizeObserver' in window) new ResizeObserver(() => paintKnob(knob.value)).observe(slider); if ('ResizeObserver' in window) new ResizeObserver(() => paintKnob(knob.value)).observe(slider);
const fracOf = (o, v) => (o.maxRaiseTo > o.minRaiseTo ? (v - o.minRaiseTo) / (o.maxRaiseTo - o.minRaiseTo) : 1); // つまみの位置と額は 2乗の関係にする(スタックが深くても、よく使う小さい額を細かく選べるように)
const CURVE = 2;
const fracOf = (o, v) =>
o.maxRaiseTo > o.minRaiseTo ? Fluid.clamp((v - o.minRaiseTo) / (o.maxRaiseTo - o.minRaiseTo), 0, 1) ** (1 / CURVE) : 1;
// SB 単位に丸める(最大はオールイン額そのまま) // SB 単位に丸める(最大はオールイン額そのまま)
function valueAt(o, f) { function valueAt(o, f) {
if (f >= 1) return o.maxRaiseTo; if (f >= 1) return o.maxRaiseTo;
const unit = Math.max(1, state.smallBlind); const unit = Math.max(1, state.smallBlind);
const raw = o.minRaiseTo + f * (o.maxRaiseTo - o.minRaiseTo); const raw = o.minRaiseTo + f ** CURVE * (o.maxRaiseTo - o.minRaiseTo);
return clampRaise(o, o.minRaiseTo + Math.round((raw - o.minRaiseTo) / unit) * unit); return clampRaise(o, o.minRaiseTo + Math.round((raw - o.minRaiseTo) / unit) * unit);
} }
const formatBB = (x) => (Math.round(x * 10) / 10).toLocaleString('en-US'); const formatBB = (x) => (Math.round(x * 10) / 10).toLocaleString('en-US');
@@ -730,10 +807,42 @@
if (knob.target !== f) knob.to(f, { damping: 1, response: 0.3 }); if (knob.target !== f) knob.to(f, { damping: 1, response: 0.3 });
} }
paintKnob(knob.value); paintKnob(knob.value);
const h = state.hand;
const ctx = betContext(o);
$('bet-title').textContent = ctx.title;
$('bet-context').textContent = [`ポット ${chips(h.potTotal)}`, ctx.note].filter(Boolean).join(' · ');
const presets = betPresets(o, ctx);
patch(
$('presets'),
presets
.map(
(p) =>
`<button type="button" class="chip-btn${p.allIn ? ' allin' : ''}" data-to="${p.to}"${p.disabled ? ' disabled' : ''}>` +
`<b>${esc(p.label)}</b><span>${p.disabled ? '—' : p.allIn ? 'All-in' : chips(p.to)}</span></button>`
)
.join('')
);
// 同じ額の候補が 2つあっても光らせるのは 1つだけ
let lit = false;
for (const b of $('presets').children) { for (const b of $('presets').children) {
b.classList.toggle('active', presetValue(o, b.dataset.preset) === raiseValue); const on = !lit && !b.disabled && Number(b.dataset.to) === raiseValue;
if (on) lit = true;
b.classList.toggle('active', on);
} }
$('raise-meta').textContent = state.bigBlind > 0 ? `${formatBB(raiseValue / state.bigBlind)} BB` : ''; $('raise-min-amt').textContent = chips(o.minRaiseTo);
$('raise-max-amt').textContent = chips(o.maxRaiseTo);
$('raise-min').classList.toggle('active', raiseValue === o.minRaiseTo);
$('raise-max').classList.toggle('active', raiseValue === o.maxRaiseTo);
$('raise-minus').disabled = raiseValue <= o.minRaiseTo;
$('raise-plus').disabled = raiseValue >= o.maxRaiseTo;
$('raise-bb').textContent = state.bigBlind > 0 ? `${formatBB(raiseValue / state.bigBlind)} BB` : '';
// 相手の額の何倍か(オープン以外のプリフロップとレイズ)、ポットの何%か(ベット)
let ratio = '';
if (o.isBet) ratio = h.potTotal > 0 ? `ポットの ${Math.round((raiseValue / h.potTotal) * 100)}%` : '';
else if (h.street !== 'PREFLOP' || h.raises > 0) ratio = `${formatBB(raiseValue / h.currentBet)}x`;
$('raise-ratio').textContent = ratio;
const allIn = raiseValue >= o.maxRaiseTo; const allIn = raiseValue >= o.maxRaiseTo;
$('raise-confirm').textContent = allIn $('raise-confirm').textContent = allIn
? `All-in ${chips(raiseValue)}` ? `All-in ${chips(raiseValue)}`
@@ -846,9 +955,13 @@
const o = options(); const o = options();
if (!o || !o.canRaise) return; if (!o || !o.canRaise) return;
if (o.minRaiseTo >= o.maxRaiseTo) return sendAction('raise', o.maxRaiseTo); // オールインしかない if (o.minRaiseTo >= o.maxRaiseTo) return sendAction('raise', o.maxRaiseTo); // オールインしかない
// 場面のよくある額(オープン 2.5BB・3bet 3x・ベット 50% など)を選んだ状態で開く
const ctx = betContext(o);
const presets = betPresets(o, ctx);
const def = presets.find((p) => p.def && !p.disabled) || presets.find((p) => !p.disabled);
raising = true; raising = true;
raiseValue = o.minRaiseTo; raiseValue = def ? def.to : o.minRaiseTo;
knob.set(0); knob.set(fracOf(o, raiseValue));
render(); render();
}); });
$('raise-cancel').addEventListener('click', () => { $('raise-cancel').addEventListener('click', () => {
@@ -860,10 +973,57 @@
if (o) sendAction('raise', clampRaise(o, raiseValue)); if (o) sendAction('raise', clampRaise(o, raiseValue));
}); });
$('presets').addEventListener('click', (e) => { $('presets').addEventListener('click', (e) => {
const b = e.target.closest('[data-preset]'); const b = e.target.closest('[data-to]');
const o = options(); const o = options();
if (b && o) setRaise(o, presetValue(o, b.dataset.preset)); if (!b || b.disabled || !o) return;
Fluid.haptic(6);
setRaise(o, Number(b.dataset.to));
}); });
$('raise-min').addEventListener('click', () => {
const o = options();
if (o) setRaise(o, o.minRaiseTo);
});
$('raise-max').addEventListener('click', () => {
const o = options();
if (o) setRaise(o, o.maxRaiseTo);
});
// −/+: 1BB ずつ。押し続けると少し待ってから連続で、だんだん速く動く
function stepper(btn, dir) {
let timer = null;
let count = 0;
const step = () => {
const o = options();
if (!o) return stop();
const before = raiseValue;
setRaise(o, raiseValue + dir * Math.max(1, state.bigBlind));
if (raiseValue === before) return stop();
if (raiseValue === o.maxRaiseTo || raiseValue === o.minRaiseTo) Fluid.haptic(8);
};
const stop = () => {
clearTimeout(timer);
timer = null;
};
const repeat = () => {
step();
count++;
if (timer) timer = setTimeout(repeat, count < 6 ? 140 : count < 20 ? 70 : 35);
};
btn.addEventListener('pointerdown', (e) => {
if (e.pointerType === 'mouse' && e.button !== 0) return;
e.preventDefault(); // 長押しで文字選択・メニューが出ないように
step();
count = 0;
timer = setTimeout(repeat, 380);
});
for (const type of ['pointerup', 'pointerleave', 'pointercancel']) btn.addEventListener(type, stop);
// キーボード(Enter / Space)から押されたとき
btn.addEventListener('click', (e) => {
if (e.detail === 0) step();
});
}
stepper($('raise-minus'), -1);
stepper($('raise-plus'), 1);
$('raise-amount').addEventListener('input', (e) => { $('raise-amount').addEventListener('input', (e) => {
const o = options(); const o = options();
const v = Number(e.target.value); const v = Number(e.target.value);
+43 -23
View File
@@ -67,25 +67,6 @@
</main> </main>
<section class="action-bar" id="action-bar" hidden> <section class="action-bar" id="action-bar" hidden>
<!-- レイズ額: レイズボタンから上に開く(すりガラスを重ねないよう .inner の外に置く) -->
<div class="raise-panel" id="raise-panel" hidden>
<div class="raise-head">
<div class="raise-value"><span class="cur" aria-hidden="true">$</span><input type="number" class="raise-amount" id="raise-amount" inputmode="numeric" aria-label="レイズ額"></div>
<span class="raise-meta" id="raise-meta"></span>
</div>
<div class="bet-slider" id="raise-range" role="slider" tabindex="0" aria-label="レイズ額">
<div class="bet-track"><div class="bet-fill"></div></div>
<div class="bet-thumb"></div>
</div>
<div class="presets" id="presets">
<button type="button" class="chip-btn" data-preset="min">Min</button>
<button type="button" class="chip-btn" data-preset="half">½ Pot</button>
<button type="button" class="chip-btn" data-preset="threeq">¾ Pot</button>
<button type="button" class="chip-btn" data-preset="pot">Pot</button>
<button type="button" class="chip-btn" data-preset="max">All-in</button>
</div>
</div>
<div class="inner"> <div class="inner">
<div class="me-row"> <div class="me-row">
<div class="my-cards" id="my-cards" hidden></div> <div class="my-cards" id="my-cards" hidden></div>
@@ -108,10 +89,6 @@
<button class="btn act-call" id="act-call" disabled>Check</button> <button class="btn act-call" id="act-call" disabled>Check</button>
<button class="btn act-raise" id="act-raise" disabled>Raise</button> <button class="btn act-raise" id="act-raise" disabled>Raise</button>
</div> </div>
<div class="actions raising" id="raise-actions" hidden>
<button class="btn" id="raise-cancel">戻る</button>
<button class="btn act-raise" id="raise-confirm">Raise</button>
</div>
<div class="actions raising" id="review-actions" hidden> <div class="actions raising" id="review-actions" hidden>
<button class="btn" id="review-show">レビュー</button> <button class="btn" id="review-show">レビュー</button>
<button class="btn btn-tint" id="review-next">次のハンドへ</button> <button class="btn btn-tint" id="review-next">次のハンドへ</button>
@@ -130,6 +107,49 @@
<a class="btn btn-tint btn-lg" href="/">ロビーへ戻る</a> <a class="btn btn-tint btn-lg" href="/">ロビーへ戻る</a>
</div> </div>
<!-- ベット/レイズ額(Bet・Raise ボタンで下から出るシート。つまみを下へ払うと閉じる)。
候補はプリフロップのオープン・3bet・4bet、フロップ以降のベット・レイズで入れ替わる -->
<div class="sheet-backdrop bet-backdrop" id="bet-modal" hidden>
<div class="sheet bet-sheet" role="dialog" aria-modal="true" aria-labelledby="bet-title" tabindex="-1">
<div class="sheet-header bet-header" data-sheet-drag>
<span class="sheet-grabber" aria-hidden="true"></span>
<div class="bet-heading">
<h2 class="sheet-title" id="bet-title">Raise</h2>
<span class="bet-context" id="bet-context"></span>
</div>
</div>
<div class="bet-body">
<div class="bet-amount-row">
<label class="raise-value"><span class="cur" aria-hidden="true">$</span><input type="number" class="raise-amount" id="raise-amount" inputmode="numeric" aria-label="ベット額"></label>
<div class="raise-meta"><b id="raise-bb"></b><span id="raise-ratio"></span></div>
</div>
<div class="bet-presets" id="presets"></div>
<div class="bet-adjust">
<button type="button" class="step-btn" id="raise-minus" aria-label="1BB 減らす">
<svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 12h12"/></svg>
</button>
<div class="bet-slider" id="raise-range" role="slider" tabindex="0" aria-label="ベット額">
<div class="bet-track"><div class="bet-fill"></div></div>
<div class="bet-thumb"></div>
</div>
<button type="button" class="step-btn" id="raise-plus" aria-label="1BB 増やす">
<svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 6v12M6 12h12"/></svg>
</button>
</div>
<div class="bet-ends">
<button type="button" class="end-btn" id="raise-min">Min <span id="raise-min-amt"></span></button>
<button type="button" class="end-btn allin" id="raise-max">All-in <span id="raise-max-amt"></span></button>
</div>
</div>
<div class="sheet-footer">
<div class="btn-row">
<button type="button" class="btn btn-lg" id="raise-cancel">戻る</button>
<button type="button" class="btn btn-lg act-raise" id="raise-confirm">Raise</button>
</div>
</div>
</div>
</div>
<!-- 直前のハンドのレビュー(下から出るシート。つまみを下へ払うと閉じる) --> <!-- 直前のハンドのレビュー(下から出るシート。つまみを下へ払うと閉じる) -->
<div class="sheet-backdrop" id="review-modal" hidden> <div class="sheet-backdrop" id="review-modal" hidden>
<div class="sheet review-sheet" role="dialog" aria-modal="true" aria-labelledby="review-title" tabindex="-1"> <div class="sheet review-sheet" role="dialog" aria-modal="true" aria-labelledby="review-title" tabindex="-1">
+1
View File
@@ -444,6 +444,7 @@ class Hand {
pot: this.pot, pot: this.pot,
potTotal: this.potTotal, potTotal: this.potTotal,
currentBet: this.currentBet, currentBet: this.currentBet,
raises: this.raises, // ベット額の候補を 3bet・4bet などに合わせるため
button: this.button, button: this.button,
sbSeat: this.sbSeat, sbSeat: this.sbSeat,
bbSeat: this.bbSeat, bbSeat: this.bbSeat,