Rebuild the UI in an Apple style with spring-driven motion
Replace the look of every page (key, lobby, create, table, range, review) with light/dark semantic colors, translucent materials, iOS-style grouped lists and system typography. Add fluid.js, a small spring engine with interruptible, velocity-aware animations, and use it for draggable bottom sheets, sliding segmented controls, a custom bet slider with rubber-banding, swipe-to-delete in the lobby, and dealt/flipped cards. Seats and cards are now reused between renders so animations are not cut off. Honors reduced motion, reduced transparency and increased contrast. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d5ef2d310f
commit
c3fbfdd12e
18 files changed
+4877
-1278
No files matched your search
+179
-52
@@ -1,22 +1,8 @@
|
||||
/* ハンドレンジ学習(管理者専用)。基本のデザインは /assets/style.css を使う */
|
||||
/* ハンドレンジ学習(管理者専用)。基本のデザインとセグメントは /assets/style.css を使う */
|
||||
|
||||
.range-wrap { padding-bottom: 32px; }
|
||||
|
||||
/* タブ */
|
||||
.seg { display: flex; gap: 6px; background: var(--panel); border-radius: 12px; padding: 4px; margin: 12px 0 16px; }
|
||||
.seg-btn {
|
||||
flex: 1;
|
||||
font: inherit;
|
||||
font-size: 15px;
|
||||
font-weight: 800;
|
||||
border: 0;
|
||||
border-radius: 9px;
|
||||
padding: 10px 6px;
|
||||
background: transparent;
|
||||
color: var(--muted);
|
||||
cursor: pointer;
|
||||
.range-tabs {
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
.seg-btn.active { background: var(--panel-2); color: var(--text); }
|
||||
|
||||
/* レンジ表の分類ごとの色(レンジ表のデータとしての色なのでサイトの配色とは別) */
|
||||
.cat-navy { --c: #20365d; --fg: #fff; }
|
||||
@@ -30,57 +16,198 @@
|
||||
/* レイズを受けたときの対応の枠(3bet は水色、コールは黄)。下地の色と紛れないよう内側に暗い線を添える */
|
||||
.facing-3bet { --frame: #3fd4ff; }
|
||||
.facing-call { --frame: #ffe23a; }
|
||||
.range-cell.facing-3bet, .range-cell.facing-call { box-shadow: inset 0 0 0 3px var(--frame), inset 0 0 0 4px rgba(0, 0, 0, 0.45); }
|
||||
.range-cell.facing-3bet,
|
||||
.range-cell.facing-call {
|
||||
box-shadow: inset 0 0 0 3px var(--frame), inset 0 0 0 4px rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
/* 13×13 の表 */
|
||||
.range-matrix { display: grid; grid-template-columns: repeat(13, 1fr); gap: 2px; }
|
||||
.matrix-card {
|
||||
padding: 6px;
|
||||
border-radius: 16px;
|
||||
background: var(--bg-2);
|
||||
}
|
||||
.range-matrix {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(13, 1fr);
|
||||
gap: 2px;
|
||||
}
|
||||
.range-cell {
|
||||
aspect-ratio: 1;
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
aspect-ratio: 1;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-radius: 3px;
|
||||
border-radius: 4px;
|
||||
background: var(--c);
|
||||
color: var(--fg);
|
||||
font: inherit;
|
||||
font-size: clamp(8px, 2.4vw, 12px);
|
||||
font-weight: 800;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.04em;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: transform 0.45s var(--ease-spring);
|
||||
}
|
||||
.range-cell:active {
|
||||
transform: scale(0.9);
|
||||
transition: transform 0.1s ease-out;
|
||||
}
|
||||
.range-cell.selected {
|
||||
z-index: 1;
|
||||
outline: 2px solid var(--label);
|
||||
outline-offset: 1px;
|
||||
transform: scale(1.12);
|
||||
}
|
||||
.range-cell.selected { outline: 2px solid var(--gold); outline-offset: 1px; z-index: 1; }
|
||||
|
||||
/* 凡例 */
|
||||
.range-legend { display: flex; flex-wrap: wrap; gap: 6px 12px; margin-top: 12px; font-size: 12px; color: var(--muted); }
|
||||
.range-legend span { display: inline-flex; align-items: center; gap: 6px; }
|
||||
.swatch-dot { width: 14px; height: 14px; border-radius: 4px; background: var(--c); flex: none; }
|
||||
.swatch-dot.swatch-frame { background: transparent; box-shadow: inset 0 0 0 3px var(--frame); }
|
||||
.range-legend {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px 14px;
|
||||
margin: 12px 8px 0;
|
||||
color: var(--label-2);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.range-legend span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.swatch-dot {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
flex: none;
|
||||
border-radius: 4px;
|
||||
background: var(--c);
|
||||
box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.swatch-dot.swatch-frame {
|
||||
background: transparent;
|
||||
box-shadow: inset 0 0 0 3px var(--frame);
|
||||
}
|
||||
|
||||
/* 選択したハンド */
|
||||
.sel-hand { font-size: 28px; font-weight: 900; line-height: 1.1; }
|
||||
.sel-label { display: flex; align-items: center; gap: 8px; font-weight: 800; margin-top: 6px; }
|
||||
.selected-card {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.sel-hand {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.15;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
.sel-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 6px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.selected-card .hint {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
/* クイズ */
|
||||
.quiz-progress { height: 6px; background: var(--input); border-radius: 999px; overflow: hidden; margin: 16px 0 8px; }
|
||||
.quiz-progress i { display: block; height: 100%; width: 0; background: var(--green); transition: width 0.2s; }
|
||||
.quiz-stats { display: flex; justify-content: center; gap: 12px; font-size: 13px; color: var(--muted); }
|
||||
.quiz-hand { text-align: center; margin: 12px 0; padding: 18px 12px; }
|
||||
.quiz-cards { display: flex; justify-content: center; gap: 10px; min-height: 0; }
|
||||
.quiz-cards img { width: min(32%, 120px); height: auto; filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.4)); }
|
||||
.quiz-hand-name { font-size: 40px; font-weight: 900; margin-top: 8px; line-height: 1.1; }
|
||||
.quiz-answers { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
|
||||
.quiz-answers .btn {
|
||||
font-size: 15px;
|
||||
padding: 12px 8px;
|
||||
justify-content: flex-start;
|
||||
background: var(--panel-2);
|
||||
color: var(--text);
|
||||
border: 2px solid transparent;
|
||||
.quiz-progress {
|
||||
height: 6px;
|
||||
margin: 18px 0 8px;
|
||||
overflow: hidden;
|
||||
border-radius: 999px;
|
||||
background: var(--fill-2);
|
||||
}
|
||||
.quiz-progress i {
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
background: var(--tint);
|
||||
transition: width 0.5s var(--ease-spring);
|
||||
}
|
||||
.quiz-stats {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 14px;
|
||||
color: var(--label-2);
|
||||
font-size: 0.8125rem;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.quiz-hand {
|
||||
margin: 14px 0;
|
||||
padding: 20px 12px;
|
||||
text-align: center;
|
||||
}
|
||||
.quiz-cards {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.quiz-cards img {
|
||||
width: min(32%, 120px);
|
||||
height: auto;
|
||||
filter: drop-shadow(0 6px 12px rgba(0, 0, 0, 0.3));
|
||||
animation: card-in 0.55s var(--ease-spring) both;
|
||||
}
|
||||
.quiz-cards img + img {
|
||||
animation-delay: 70ms;
|
||||
}
|
||||
@keyframes card-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-14px) scale(0.92) rotate(-4deg);
|
||||
}
|
||||
}
|
||||
.quiz-hand-name {
|
||||
margin-top: 10px;
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
.quiz-answers {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 8px;
|
||||
}
|
||||
.quiz-answers .btn {
|
||||
justify-content: flex-start;
|
||||
padding: 12px 12px;
|
||||
background: var(--bg-2);
|
||||
color: var(--label);
|
||||
font-size: 0.9375rem;
|
||||
box-shadow: inset 0 0 0 0 transparent;
|
||||
transition: transform 0.45s var(--ease-spring), box-shadow 0.25s ease, background-color 0.25s ease;
|
||||
}
|
||||
.quiz-answers .btn:disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
.quiz-answers .btn.correct {
|
||||
background: var(--tint-soft);
|
||||
box-shadow: inset 0 0 0 2px var(--tint);
|
||||
}
|
||||
.quiz-answers .btn.wrong {
|
||||
background: rgba(255, 59, 48, 0.12);
|
||||
box-shadow: inset 0 0 0 2px var(--red);
|
||||
}
|
||||
.quiz-feedback {
|
||||
margin-top: 12px;
|
||||
padding: 12px 14px;
|
||||
border-radius: 14px;
|
||||
background: var(--bg-2);
|
||||
font-size: 0.9375rem;
|
||||
animation: fade-up 0.45s var(--ease-spring) both;
|
||||
}
|
||||
.quiz-feedback.good {
|
||||
box-shadow: inset 0 0 0 2px var(--tint);
|
||||
}
|
||||
.quiz-feedback.bad {
|
||||
box-shadow: inset 0 0 0 2px var(--red);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.quiz-cards img,
|
||||
.quiz-feedback {
|
||||
animation: fade-in 0.2s ease both;
|
||||
}
|
||||
.range-cell.selected {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
.quiz-answers .btn:disabled { opacity: 1; }
|
||||
.quiz-answers .btn.correct { border-color: var(--green); background: rgba(46, 204, 113, 0.18); }
|
||||
.quiz-answers .btn.wrong { border-color: var(--red); background: rgba(255, 90, 90, 0.15); }
|
||||
.quiz-feedback { margin-top: 12px; padding: 12px 14px; border-radius: 10px; background: var(--panel); font-size: 14px; }
|
||||
.quiz-feedback.good { box-shadow: inset 0 0 0 2px var(--green); }
|
||||
.quiz-feedback.bad { box-shadow: inset 0 0 0 2px var(--red); }
|
||||
+21
-13
@@ -3,42 +3,49 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<meta name="theme-color" content="#24493b">
|
||||
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#f2f2f7">
|
||||
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#000000">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<title>ハンドレンジ - Hold'em Night</title>
|
||||
<link rel="icon" href="/assets/favicon.ico">
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
<link rel="stylesheet" href="/range/files/range.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="wrap range-wrap">
|
||||
<div class="topbar">
|
||||
<a class="icon-btn" href="/" aria-label="ロビーへ戻る">←</a>
|
||||
<h1>ハンドレンジ</h1>
|
||||
<body class="page-range">
|
||||
<header class="nav-bar" id="nav">
|
||||
<div class="nav-inner">
|
||||
<a class="nav-back" href="/" aria-label="ロビーへ戻る"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M15 5l-7 7 7 7"/></svg>ロビー</a>
|
||||
<span class="nav-title"></span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="seg" id="tabs">
|
||||
<main class="wrap range-wrap">
|
||||
<h1 class="large-title" id="large-title">ハンドレンジ</h1>
|
||||
|
||||
<div class="segmented range-tabs" id="tabs">
|
||||
<button type="button" class="seg-btn active" data-view="range">レンジ表</button>
|
||||
<button type="button" class="seg-btn" data-view="quiz">クイズ</button>
|
||||
</div>
|
||||
|
||||
<!-- レンジ表 -->
|
||||
<section id="view-range">
|
||||
<div class="range-matrix" id="matrix" aria-label="13×13 ハンドレンジ表"></div>
|
||||
<div class="matrix-card">
|
||||
<div class="range-matrix" id="matrix" aria-label="13×13 ハンドレンジ表"></div>
|
||||
</div>
|
||||
<div class="range-legend" id="legend"></div>
|
||||
|
||||
<div class="card mt-12" id="selected">
|
||||
<div class="card selected-card" id="selected">
|
||||
<div class="sel-hand">—</div>
|
||||
<div class="hint">セルをタップすると分類を確認できます</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-orange btn-block mt-12" id="start-from-range">クイズを始める</button>
|
||||
<button type="button" class="btn btn-tint btn-lg btn-block mt-12" id="start-from-range">クイズを始める</button>
|
||||
</section>
|
||||
|
||||
<!-- クイズ -->
|
||||
<section id="view-quiz" hidden>
|
||||
<div class="label">問題数(全169ハンドからランダム)</div>
|
||||
<div class="seg" id="count">
|
||||
<div class="segmented" id="count">
|
||||
<button type="button" class="seg-btn active" data-count="10">10問</button>
|
||||
<button type="button" class="seg-btn" data-count="20">20問</button>
|
||||
<button type="button" class="seg-btn" data-count="0">全問</button>
|
||||
@@ -64,12 +71,13 @@
|
||||
<div class="quiz-feedback" id="feedback" aria-live="polite" hidden></div>
|
||||
|
||||
<div class="row mt-12">
|
||||
<button type="button" class="btn btn-dark" id="start-quiz">開始</button>
|
||||
<button type="button" class="btn btn-blue" id="next-question" disabled>次の問題 →</button>
|
||||
<button type="button" class="btn btn-lg" id="start-quiz">開始</button>
|
||||
<button type="button" class="btn btn-tint btn-lg" id="next-question" disabled>次の問題</button>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script src="/assets/fluid.js"></script>
|
||||
<script src="/range/files/range-data.js"></script>
|
||||
<script src="/range/files/range.js"></script>
|
||||
</body>
|
||||
|
||||
+10
-2
@@ -5,6 +5,10 @@
|
||||
const $ = (id) => document.getElementById(id);
|
||||
const { RANKS, CATEGORIES, FACING_ACTIONS, handAt, categoryOf, labelOf, facingOf, facingAction } = window.HandRange;
|
||||
|
||||
Fluid.navBar($('nav'), $('large-title'));
|
||||
const tabsSeg = Fluid.Segmented($('tabs'));
|
||||
Fluid.Segmented($('count'));
|
||||
|
||||
function swatch(id) {
|
||||
const s = document.createElement('span');
|
||||
s.className = `swatch-dot cat-${id}`;
|
||||
@@ -22,7 +26,8 @@
|
||||
function showView(name) {
|
||||
$('view-range').hidden = name !== 'range';
|
||||
$('view-quiz').hidden = name !== 'quiz';
|
||||
for (const b of $('tabs').children) b.classList.toggle('active', b.dataset.view === name);
|
||||
for (const b of $('tabs').querySelectorAll('[data-view]')) b.classList.toggle('active', b.dataset.view === name);
|
||||
tabsSeg.update();
|
||||
}
|
||||
$('tabs').addEventListener('click', (e) => {
|
||||
const b = e.target.closest('[data-view]');
|
||||
@@ -99,7 +104,7 @@
|
||||
const b = e.target.closest('[data-count]');
|
||||
if (!b) return;
|
||||
quizCount = Number(b.dataset.count);
|
||||
for (const x of $('count').children) x.classList.toggle('active', x === b);
|
||||
for (const x of $('count').querySelectorAll('[data-count]')) x.classList.toggle('active', x === b);
|
||||
});
|
||||
|
||||
function shuffle(a) {
|
||||
@@ -186,6 +191,9 @@
|
||||
fb.className = `quiz-feedback ${ok ? 'good' : 'bad'}`;
|
||||
fb.textContent = `${ok ? '正解!' : '不正解'} ${hand} は「${labelOf(right)}」です。`;
|
||||
fb.hidden = false;
|
||||
// 間違えたら選んだボタンが揺れる(振動と同じ瞬間に)
|
||||
if (!ok) Fluid.shake(button);
|
||||
Fluid.haptic(ok ? 10 : [18, 50, 18]);
|
||||
updateStats();
|
||||
$('next-question').disabled = false;
|
||||
}
|
||||
|
||||
+440
-121
@@ -1,146 +1,465 @@
|
||||
/* CPU戦のレビュー(管理者専用)。基本のデザインは /assets/style.css、タブは range.css、
|
||||
/* CPU戦のレビュー(管理者専用)。基本のデザイン・セグメント・リストは /assets/style.css、
|
||||
ハンドレンジの色・点数の印(.rv-color / .rv-mark など)は style.css の「レビュー付きCPU戦」を使う */
|
||||
|
||||
.review-wrap { padding-bottom: 40px; }
|
||||
.plus { color: var(--green); }
|
||||
.minus { color: var(--red); }
|
||||
|
||||
/* 点数の色(◎ ○ △ ✕ と同じ段階) */
|
||||
.score-great { color: var(--green); }
|
||||
.score-good { color: #8fd18f; }
|
||||
.score-good { color: #64b96a; }
|
||||
.score-warn { color: var(--orange); }
|
||||
.score-bad { color: var(--red); }
|
||||
.score-none { color: var(--muted); }
|
||||
.score-none { color: var(--label-2); }
|
||||
|
||||
/* 対局の一覧 */
|
||||
.rv-sessions { display: grid; gap: 8px; margin-top: 12px; }
|
||||
.rv-session {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
text-align: left;
|
||||
font: inherit;
|
||||
color: var(--text);
|
||||
background: var(--panel);
|
||||
border: 0;
|
||||
border-radius: var(--radius);
|
||||
padding: 14px 16px;
|
||||
cursor: pointer;
|
||||
.rv-sessions {
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
.rv-session {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
text-align: left;
|
||||
}
|
||||
.rv-session-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.rv-session:hover, .rv-session:focus-visible { background: var(--panel-2); }
|
||||
.rv-session-name { font-weight: 800; font-size: 16px; display: flex; align-items: center; gap: 8px; }
|
||||
.rv-session-meta { color: var(--muted); font-size: 12px; }
|
||||
|
||||
/* 概要とタイル */
|
||||
.rv-overview { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 12px; }
|
||||
.rv-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 8px; }
|
||||
.rv-tile { background: var(--panel); border-radius: 12px; padding: 10px 12px; display: grid; gap: 2px; min-width: 0; }
|
||||
.rv-tile span { font-size: 12px; color: var(--muted); font-weight: 700; }
|
||||
.rv-tile b { font-size: 22px; font-variant-numeric: tabular-nums; }
|
||||
.rv-tile small { font-size: 11px; color: var(--muted); line-height: 1.4; }
|
||||
.rv-tabs .seg-btn { font-size: 13px; padding: 9px 2px; }
|
||||
/* 概要とタイル(ウィジェットのような小さな面) */
|
||||
.rv-overview {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.rv-tiles {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
.rv-tile {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
padding: 12px 14px;
|
||||
border-radius: 16px;
|
||||
background: var(--bg-2);
|
||||
}
|
||||
.rv-tile span {
|
||||
color: var(--label-2);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
.rv-tile b {
|
||||
font-family: var(--font-num);
|
||||
font-size: 1.5rem;
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.02em;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.rv-tile small {
|
||||
color: var(--label-2);
|
||||
font-size: 0.6875rem;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.rv-tabs {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.rv-tabs .seg-btn {
|
||||
padding: 4px 2px;
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
#tab-body > .hint,
|
||||
.review-wrap p.hint {
|
||||
margin: 10px 4px;
|
||||
}
|
||||
|
||||
/* 表 */
|
||||
.rv-table-wrap { overflow-x: auto; margin-top: 8px; border-radius: 12px; background: var(--panel); }
|
||||
.rv-table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||||
.rv-table th, .rv-table td { padding: 8px 6px; text-align: left; white-space: nowrap; border-bottom: 1px solid var(--line); }
|
||||
.rv-table th { color: var(--muted); font-size: 11px; font-weight: 700; }
|
||||
.rv-table .num { text-align: right; font-variant-numeric: tabular-nums; font-weight: 700; }
|
||||
.rv-table tbody tr[data-hand] { cursor: pointer; }
|
||||
.rv-table tbody tr[data-hand]:hover, .rv-table tbody tr[data-hand]:focus-visible { background: var(--panel-2); outline: none; }
|
||||
.rv-table tfoot td { font-weight: 800; border-bottom: 0; }
|
||||
.rv-hand-cell b { margin-right: 2px; }
|
||||
.rv-table .rv-color { font-size: 11px; padding: 1px 6px; }
|
||||
.rv-table-wrap {
|
||||
margin-top: 8px;
|
||||
overflow-x: auto;
|
||||
border-radius: 14px;
|
||||
background: var(--bg-2);
|
||||
}
|
||||
.rv-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
.rv-table th,
|
||||
.rv-table td {
|
||||
padding: 9px 8px;
|
||||
border-bottom: 0.5px solid var(--separator);
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.rv-table th {
|
||||
color: var(--label-2);
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.rv-table .num {
|
||||
text-align: right;
|
||||
font-weight: 600;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.rv-table tbody tr[data-hand] {
|
||||
cursor: pointer;
|
||||
transition: background-color 0.35s ease;
|
||||
}
|
||||
.rv-table tbody tr[data-hand]:active {
|
||||
background: var(--fill-3);
|
||||
transition: none;
|
||||
}
|
||||
.rv-table tbody tr[data-hand]:focus-visible {
|
||||
outline: 2px solid var(--blue);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
@media (hover: hover) {
|
||||
.rv-table tbody tr[data-hand]:hover {
|
||||
background: var(--fill-3);
|
||||
}
|
||||
}
|
||||
.rv-table tfoot td {
|
||||
border-bottom: 0;
|
||||
font-weight: 700;
|
||||
}
|
||||
.rv-hand-cell b {
|
||||
margin-right: 2px;
|
||||
}
|
||||
.rv-table .rv-color {
|
||||
padding: 1px 6px;
|
||||
font-size: 0.6875rem;
|
||||
}
|
||||
|
||||
.rv-leaks { margin: 0; padding-left: 20px; display: grid; gap: 4px; font-size: 14px; }
|
||||
.rv-leaks b { color: var(--gold); }
|
||||
.rv-leaks {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
margin: 0;
|
||||
padding: 12px 14px 12px 32px;
|
||||
border-radius: 14px;
|
||||
background: var(--bg-2);
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
.rv-leaks b {
|
||||
color: var(--orange);
|
||||
}
|
||||
|
||||
/* 見直したいハンド */
|
||||
.rv-worst { display: grid; gap: 8px; }
|
||||
.rv-worst-item {
|
||||
display: grid;
|
||||
grid-template-columns: 28px 1fr;
|
||||
gap: 2px 8px;
|
||||
align-items: center;
|
||||
text-align: left;
|
||||
font: inherit;
|
||||
color: var(--text);
|
||||
background: var(--panel);
|
||||
border: 0;
|
||||
border-radius: 12px;
|
||||
padding: 10px 12px;
|
||||
cursor: pointer;
|
||||
.rv-worst {
|
||||
overflow: hidden;
|
||||
border-radius: 14px;
|
||||
background: var(--bg-2);
|
||||
}
|
||||
.rv-worst-item {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 32px 1fr;
|
||||
gap: 2px 10px;
|
||||
align-items: center;
|
||||
padding: 11px 14px;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--label);
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.35s ease;
|
||||
}
|
||||
.rv-worst-item + .rv-worst-item::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 56px;
|
||||
border-top: 0.5px solid var(--separator);
|
||||
}
|
||||
.rv-worst-item:active {
|
||||
background: var(--fill-3);
|
||||
transition: none;
|
||||
}
|
||||
.rv-rank {
|
||||
grid-row: span 2;
|
||||
color: var(--orange);
|
||||
font-family: var(--font-num);
|
||||
font-size: 1.375rem;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
}
|
||||
.rv-worst-meta {
|
||||
color: var(--label-2);
|
||||
font-size: 0.75rem;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.rv-worst-item .rv-color {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
padding: 0;
|
||||
vertical-align: -2px;
|
||||
}
|
||||
.rv-worst-item:hover, .rv-worst-item:focus-visible { background: var(--panel-2); }
|
||||
.rv-rank { grid-row: span 2; font-size: 20px; font-weight: 900; color: var(--gold); text-align: center; }
|
||||
.rv-worst-meta { font-size: 12px; color: var(--muted); }
|
||||
.rv-worst-item .rv-color { width: 14px; height: 14px; padding: 0; vertical-align: -2px; }
|
||||
|
||||
/* 収支のグラフ(実際 = 実線、運を除く = 破線。色だけに頼らない) */
|
||||
.rv-legend { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: 13px; margin-bottom: 6px; }
|
||||
.sw { display: inline-block; width: 16px; height: 0; border-top: 2px solid; vertical-align: middle; margin-right: 6px; }
|
||||
.sw.actual { border-color: #b8891c; }
|
||||
.sw.ev { border-color: #4a90e2; border-top-style: dashed; }
|
||||
.rv-chart { position: relative; background: var(--panel); border-radius: 12px; padding: 8px; }
|
||||
.rv-chart svg { display: block; width: 100%; height: auto; touch-action: pan-y; }
|
||||
.rv-chart .gl { stroke: var(--line); stroke-width: 1; }
|
||||
.rv-chart .zero { stroke: var(--muted); stroke-width: 1; stroke-dasharray: 2 3; }
|
||||
.rv-chart .tick { fill: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; }
|
||||
.rv-chart .line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
|
||||
.rv-chart .line.actual { stroke: #b8891c; }
|
||||
.rv-chart .line.ev { stroke: #4a90e2; stroke-dasharray: 6 4; }
|
||||
.rv-chart .cross { stroke: var(--muted); stroke-width: 1; }
|
||||
.rv-chart .dot { stroke: var(--panel); stroke-width: 2; }
|
||||
.rv-chart .dot.actual { fill: #b8891c; }
|
||||
.rv-chart .dot.ev { fill: #4a90e2; }
|
||||
.rv-chart .hit { fill: transparent; cursor: crosshair; }
|
||||
.rv-legend {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px 16px;
|
||||
margin: 0 4px 8px;
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
.sw {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 0;
|
||||
margin-right: 6px;
|
||||
border-top: 2px solid;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.sw.actual {
|
||||
border-color: var(--orange);
|
||||
}
|
||||
.sw.ev {
|
||||
border-color: var(--blue);
|
||||
border-top-style: dashed;
|
||||
}
|
||||
.rv-chart {
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
border-radius: 16px;
|
||||
background: var(--bg-2);
|
||||
}
|
||||
.rv-chart svg {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
touch-action: pan-y;
|
||||
}
|
||||
.rv-chart .gl {
|
||||
stroke: var(--separator);
|
||||
stroke-width: 0.5;
|
||||
}
|
||||
.rv-chart .zero {
|
||||
stroke: var(--label-3);
|
||||
stroke-width: 1;
|
||||
stroke-dasharray: 2 3;
|
||||
}
|
||||
.rv-chart .tick {
|
||||
fill: var(--label-2);
|
||||
font-size: 11px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.rv-chart .line {
|
||||
fill: none;
|
||||
stroke-width: 2;
|
||||
stroke-linejoin: round;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
.rv-chart .line.actual {
|
||||
stroke: var(--orange);
|
||||
}
|
||||
.rv-chart .line.ev {
|
||||
stroke: var(--blue);
|
||||
stroke-dasharray: 6 4;
|
||||
}
|
||||
.rv-chart .cross {
|
||||
stroke: var(--label-3);
|
||||
stroke-width: 1;
|
||||
}
|
||||
.rv-chart .dot {
|
||||
stroke: var(--bg-2);
|
||||
stroke-width: 2;
|
||||
}
|
||||
.rv-chart .dot.actual {
|
||||
fill: var(--orange);
|
||||
}
|
||||
.rv-chart .dot.ev {
|
||||
fill: var(--blue);
|
||||
}
|
||||
.rv-chart .hit {
|
||||
fill: transparent;
|
||||
cursor: crosshair;
|
||||
}
|
||||
.rv-tip {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
background: var(--bg-deep);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 6px 10px;
|
||||
font-size: 12px;
|
||||
pointer-events: none;
|
||||
top: 10px;
|
||||
padding: 7px 11px;
|
||||
border-radius: 12px;
|
||||
background: var(--material-thick);
|
||||
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
||||
backdrop-filter: blur(20px) saturate(180%);
|
||||
box-shadow: var(--shadow-m), inset 0 0 0 0.5px var(--material-edge);
|
||||
font-size: 0.75rem;
|
||||
font-variant-numeric: tabular-nums;
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* リプレイ */
|
||||
.rv-note { margin-top: 10px; font-size: 13px; background: var(--panel); border-radius: 10px; padding: 8px 10px; }
|
||||
.rv-copy { width: 100%; margin-top: 10px; }
|
||||
.rv-board-box { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 14px; background: var(--felt-dark); border-radius: 12px; padding: 10px; }
|
||||
.rv-board { display: flex; gap: 3px; }
|
||||
.rv-pot { font-weight: 900; color: #fff; font-variant-numeric: tabular-nums; }
|
||||
.rv-step { margin-top: 10px; background: var(--panel); border-radius: 12px; padding: 10px 12px; font-size: 14px; min-height: 64px; }
|
||||
.rv-step-title { font-size: 12px; font-weight: 800; color: var(--gold); margin-bottom: 2px; }
|
||||
.rv-controls { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; margin-top: 8px; }
|
||||
.rv-controls .btn { padding: 10px 0; font-size: 16px; }
|
||||
.rv-analysis { margin-top: 10px; background: var(--panel); border: 1px solid var(--gold); border-radius: 12px; padding: 6px 12px 10px; }
|
||||
.rv-analysis .rv-item:first-child { border-top: 0; }
|
||||
.rv-ev { margin-top: 6px; }
|
||||
.rv-ev tr.actual td { background: rgba(246, 199, 68, 0.08); }
|
||||
.rv-ev .best { color: var(--green); }
|
||||
.rv-players { display: grid; gap: 6px; margin-top: 12px; }
|
||||
.rv-player { display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: center; background: var(--panel); border-radius: 10px; padding: 6px 10px; border: 2px solid transparent; }
|
||||
.rv-player.hero { border-color: rgba(246, 199, 68, 0.35); }
|
||||
.rv-player.acting { border-color: var(--gold); }
|
||||
.rv-player.folded { opacity: 0.55; }
|
||||
.rv-player-info { min-width: 0; font-size: 14px; }
|
||||
.rv-player-act { font-size: 12px; font-weight: 800; color: var(--muted); text-align: right; }
|
||||
.rv-pos { font-size: 11px; font-weight: 800; color: var(--gold); margin-left: 4px; }
|
||||
.rv-log { display: flex; flex-wrap: wrap; gap: 4px; }
|
||||
.rv-log-street { width: 100%; font-size: 11px; color: var(--muted); font-weight: 800; margin-top: 6px; }
|
||||
.rv-log-item {
|
||||
font: inherit;
|
||||
font-size: 12px;
|
||||
color: var(--text);
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
.rv-note {
|
||||
margin-top: 10px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 14px;
|
||||
background: var(--bg-2);
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
.rv-copy {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.rv-board-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
margin-top: 14px;
|
||||
padding: 12px 14px;
|
||||
border-radius: 18px;
|
||||
background: radial-gradient(120% 140% at 30% 20%, var(--felt-hi), var(--felt) 50%, var(--felt-lo));
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.25), inset 0 8px 20px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.rv-board {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
.rv-pot {
|
||||
color: #fff;
|
||||
font-family: var(--font-num);
|
||||
font-weight: 700;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.rv-step {
|
||||
min-height: 68px;
|
||||
margin-top: 10px;
|
||||
padding: 10px 14px;
|
||||
border-radius: 14px;
|
||||
background: var(--bg-2);
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
.rv-step-title {
|
||||
margin-bottom: 2px;
|
||||
color: var(--label-2);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.rv-controls {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 8px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.rv-controls .btn {
|
||||
padding: 10px 0;
|
||||
}
|
||||
.rv-controls .btn .i {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.rv-analysis {
|
||||
margin-top: 10px;
|
||||
padding: 4px 14px 12px;
|
||||
border-radius: 14px;
|
||||
background: var(--bg-2);
|
||||
box-shadow: inset 0 0 0 1.5px var(--orange);
|
||||
}
|
||||
.rv-ev {
|
||||
margin-top: 6px;
|
||||
}
|
||||
.rv-ev tr.actual td {
|
||||
background: rgba(255, 149, 0, 0.1);
|
||||
}
|
||||
.rv-ev .best {
|
||||
color: var(--green);
|
||||
}
|
||||
.rv-players {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.rv-player {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
border-radius: 14px;
|
||||
background: var(--bg-2);
|
||||
box-shadow: inset 0 0 0 0 transparent;
|
||||
transition: box-shadow 0.3s ease, opacity 0.3s ease;
|
||||
}
|
||||
.rv-player.hero {
|
||||
box-shadow: inset 0 0 0 1.5px var(--tint-soft);
|
||||
}
|
||||
.rv-player.acting {
|
||||
box-shadow: inset 0 0 0 2px var(--orange);
|
||||
}
|
||||
.rv-player.folded {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.rv-player-info {
|
||||
min-width: 0;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.rv-player-act {
|
||||
color: var(--label-2);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
text-align: right;
|
||||
}
|
||||
.rv-pos {
|
||||
margin-left: 4px;
|
||||
color: var(--tint);
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
.rv-log {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
.rv-log-street {
|
||||
width: 100%;
|
||||
margin-top: 6px;
|
||||
color: var(--label-2);
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.rv-log-item {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
padding: 5px 10px;
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
background: var(--bg-2);
|
||||
color: var(--label);
|
||||
font-size: 0.75rem;
|
||||
cursor: pointer;
|
||||
box-shadow: inset 0 0 0 0.5px var(--separator);
|
||||
transition: background-color 0.25s ease, color 0.25s ease, transform 0.45s var(--ease-spring);
|
||||
}
|
||||
.rv-log-item:active {
|
||||
transform: scale(0.94);
|
||||
transition: transform 0.1s ease-out;
|
||||
}
|
||||
.rv-log-item .hint {
|
||||
margin: 0 4px;
|
||||
font-size: 0.6875rem;
|
||||
}
|
||||
.rv-log-item.mine {
|
||||
box-shadow: inset 0 0 0 1.5px var(--tint);
|
||||
}
|
||||
.rv-log-item.current {
|
||||
background: var(--orange);
|
||||
color: var(--on-orange);
|
||||
}
|
||||
.rv-log-item.current .hint {
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
.plus {
|
||||
color: var(--green);
|
||||
}
|
||||
.minus {
|
||||
color: var(--red);
|
||||
}
|
||||
.rv-log-item .hint { margin: 0 4px; font-size: 11px; }
|
||||
.rv-log-item.mine { border-color: rgba(246, 199, 68, 0.6); }
|
||||
.rv-log-item.current { background: var(--gold); color: #1a1a1a; }
|
||||
.rv-log-item.current .hint { color: #333; }
|
||||
+19
-14
@@ -3,31 +3,35 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<meta name="theme-color" content="#24493b">
|
||||
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#f2f2f7">
|
||||
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#000000">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<title>CPU戦のレビュー - Hold'em Night</title>
|
||||
<link rel="icon" href="/assets/favicon.ico">
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
<link rel="stylesheet" href="/range/files/range.css">
|
||||
<link rel="stylesheet" href="/range/files/review.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="wrap review-wrap">
|
||||
<div class="topbar">
|
||||
<a class="icon-btn" id="back" href="/" aria-label="戻る">←</a>
|
||||
<h1 id="title">CPU戦のレビュー</h1>
|
||||
<body class="page-review">
|
||||
<header class="nav-bar" id="nav">
|
||||
<div class="nav-inner">
|
||||
<a class="nav-back" id="back" href="/" aria-label="戻る"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M15 5l-7 7 7 7"/></svg><span id="back-label">ロビー</span></a>
|
||||
<span class="nav-title"></span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="wrap review-wrap">
|
||||
<h1 class="large-title" id="title">CPU戦のレビュー</h1>
|
||||
|
||||
<!-- 対局の一覧 -->
|
||||
<section id="view-list">
|
||||
<p class="hint">レビュー付きCPU戦の対局です(新しい順・最大50件)。</p>
|
||||
<div id="sessions" class="rv-sessions"></div>
|
||||
<div class="group-header">レビュー付きCPU戦の対局(新しい順・最大50件)</div>
|
||||
<div id="sessions" class="list rv-sessions"></div>
|
||||
</section>
|
||||
|
||||
<!-- 1回の対局 -->
|
||||
<section id="view-session" hidden>
|
||||
<div class="rv-overview" id="overview"></div>
|
||||
<div class="seg rv-tabs" id="tabs">
|
||||
<div class="segmented rv-tabs" id="tabs">
|
||||
<button type="button" class="seg-btn active" data-tab="scores">点数表</button>
|
||||
<button type="button" class="seg-btn" data-tab="stats">成績</button>
|
||||
<button type="button" class="seg-btn" data-tab="range">色・位置</button>
|
||||
@@ -46,10 +50,10 @@
|
||||
</div>
|
||||
<div class="rv-step" id="replay-step" aria-live="polite"></div>
|
||||
<div class="rv-controls">
|
||||
<button type="button" class="btn btn-dark" data-step="first" aria-label="最初へ">⏮</button>
|
||||
<button type="button" class="btn btn-dark" data-step="prev" aria-label="前へ">◀</button>
|
||||
<button type="button" class="btn btn-blue" data-step="next" aria-label="次へ">▶</button>
|
||||
<button type="button" class="btn btn-dark" data-step="last" aria-label="最後へ">⏭</button>
|
||||
<button type="button" class="btn" data-step="first" aria-label="最初へ"><svg class="i i-fill" viewBox="0 0 24 24" aria-hidden="true"><rect x="5" y="5.5" width="2.4" height="13" rx="1"/><path d="M19 6.6v10.8a.9.9 0 0 1-1.4.75L9.6 12.9a1.1 1.1 0 0 1 0-1.8l8-5.25a.9.9 0 0 1 1.4.75z"/></svg></button>
|
||||
<button type="button" class="btn" data-step="prev" aria-label="前へ"><svg class="i i-fill" viewBox="0 0 24 24" aria-hidden="true"><path d="M17 6.6v10.8a.9.9 0 0 1-1.4.75L7.6 12.9a1.1 1.1 0 0 1 0-1.8l8-5.25a.9.9 0 0 1 1.4.75z"/></svg></button>
|
||||
<button type="button" class="btn btn-tint" data-step="next" aria-label="次へ"><svg class="i i-fill" viewBox="0 0 24 24" aria-hidden="true"><path d="M7 6.6v10.8a.9.9 0 0 0 1.4.75l8-5.25a1.1 1.1 0 0 0 0-1.8l-8-5.25A.9.9 0 0 0 7 6.6z"/></svg></button>
|
||||
<button type="button" class="btn" data-step="last" aria-label="最後へ"><svg class="i i-fill" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 6.6v10.8a.9.9 0 0 0 1.4.75l8-5.25a1.1 1.1 0 0 0 0-1.8l-8-5.25A.9.9 0 0 0 5 6.6z"/><rect x="16.6" y="5.5" width="2.4" height="13" rx="1"/></svg></button>
|
||||
</div>
|
||||
<div id="replay-analysis"></div>
|
||||
<div class="rv-players" id="replay-players"></div>
|
||||
@@ -58,6 +62,7 @@
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script src="/assets/fluid.js"></script>
|
||||
<script src="/assets/common.js"></script>
|
||||
<script src="/range/files/review.js"></script>
|
||||
</body>
|
||||
|
||||
+18
-5
@@ -19,6 +19,12 @@
|
||||
let tab = 'scores';
|
||||
let replay = null; // { hand, step }
|
||||
|
||||
const CHEVRON = '<svg class="i chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="M9 5l7 7-7 7"/></svg>';
|
||||
const SPARKLES =
|
||||
'<svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M10 3.5l1.6 4.4L16 9.5l-4.4 1.6L10 15.5l-1.6-4.4L4 9.5l4.4-1.6z"/><path d="M18 14l.8 2.2L21 17l-2.2.8L18 20l-.8-2.2L15 17l2.2-.8z"/></svg>';
|
||||
Fluid.navBar($('nav'), $('title'));
|
||||
const tabsSeg = Fluid.Segmented($('tabs'));
|
||||
|
||||
const markOf = (score) => (score == null ? '' : score >= 90 ? '◎' : score >= 70 ? '○' : score >= 40 ? '△' : '✕');
|
||||
const scoreClass = (score) => (score == null ? 'none' : MARK_CLASS[markOf(score)]);
|
||||
const signed = (n) => (n > 0 ? '+' : n < 0 ? '−' : '±') + chips(Math.abs(n));
|
||||
@@ -55,6 +61,7 @@
|
||||
if (!id) {
|
||||
$('title').textContent = 'CPU戦のレビュー';
|
||||
$('back').href = '/';
|
||||
$('back-label').textContent = 'ロビー';
|
||||
show('list');
|
||||
return loadList();
|
||||
}
|
||||
@@ -71,10 +78,12 @@
|
||||
const h = session.hands.find((x) => x.number === hand);
|
||||
if (h) {
|
||||
$('back').href = `?s=${encodeURIComponent(id)}`;
|
||||
$('back-label').textContent = '対局';
|
||||
return openReplay(h);
|
||||
}
|
||||
}
|
||||
$('back').href = '/review';
|
||||
$('back-label').textContent = '一覧';
|
||||
show('session');
|
||||
renderSession();
|
||||
}
|
||||
@@ -105,9 +114,12 @@
|
||||
$('sessions').innerHTML = list
|
||||
.map(
|
||||
(s) => `
|
||||
<button type="button" class="rv-session" data-id="${esc(s.id)}">
|
||||
<span class="rv-session-name">${esc(s.tableName)}${s.active ? '<span class="badge live">PLAYING</span>' : ''}</span>
|
||||
<span class="rv-session-meta">${dateText(s.createdAt)} ・ ${s.hands}ハンド ・ ${esc(s.heroName)}</span>
|
||||
<button type="button" class="cell cell-link rv-session" data-id="${esc(s.id)}">
|
||||
<span class="cell-title">
|
||||
<span class="rv-session-name">${esc(s.tableName)}${s.active ? '<span class="badge live">PLAYING</span>' : ''}</span>
|
||||
<span class="cell-sub">${dateText(s.createdAt)} ・ ${s.hands}ハンド ・ ${esc(s.heroName)}</span>
|
||||
</span>
|
||||
${CHEVRON}
|
||||
</button>`
|
||||
)
|
||||
.join('');
|
||||
@@ -125,7 +137,8 @@
|
||||
<div class="rv-tile"><span>ハンド数</span><b>${s.stats.hands}</b></div>
|
||||
<div class="rv-tile"><span>平均点</span><b class="score-${scoreClass(s.total)}">${s.total == null ? '—' : s.total}</b></div>
|
||||
<div class="rv-tile"><span>収支</span><b class="${signClass(net)}">${signed(net)}</b></div>`;
|
||||
for (const b of $('tabs').children) b.classList.toggle('active', b.dataset.tab === tab);
|
||||
for (const b of $('tabs').querySelectorAll('[data-tab]')) b.classList.toggle('active', b.dataset.tab === tab);
|
||||
tabsSeg.update();
|
||||
const render = { scores: renderScores, stats: renderStats, range: renderRange, worst: renderWorst, graph: renderGraph }[tab];
|
||||
$('tab-body').innerHTML = '';
|
||||
render();
|
||||
@@ -370,7 +383,7 @@
|
||||
</div>
|
||||
<div class="rv-streets">${STREETS.map((st) => `<div class="rv-street"><span>${STREET_SHORT[st]}</span><b class="score-${scoreClass(h.streetScores[st])}">${h.streetScores[st] == null ? '—' : h.streetScores[st]}</b></div>`).join('')}</div>
|
||||
${allInHtml(h)}
|
||||
<button type="button" class="btn btn-dark rv-copy" id="replay-copy">🤖 AIに質問する用にコピー</button>`;
|
||||
<button type="button" class="btn btn-block rv-copy" id="replay-copy">${SPARKLES}AIに質問する用にコピー</button>`;
|
||||
$('replay-copy').addEventListener('click', () => copyHandPrompt(h));
|
||||
$('replay-log').innerHTML = logHtml(h);
|
||||
renderStep();
|
||||
|
||||
Reference in new issue
Block a user