@jjlmoya/utils-tabletop 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +65 -0
- package/scripts/postinstall.mjs +27 -0
- package/src/category/TabletopCategorySEO.astro +14 -0
- package/src/category/i18n/de.ts +27 -0
- package/src/category/i18n/en.ts +27 -0
- package/src/category/i18n/es.ts +27 -0
- package/src/category/i18n/fr.ts +27 -0
- package/src/category/i18n/id.ts +27 -0
- package/src/category/i18n/it.ts +27 -0
- package/src/category/i18n/ja.ts +27 -0
- package/src/category/i18n/ko.ts +27 -0
- package/src/category/i18n/nl.ts +27 -0
- package/src/category/i18n/pl.ts +27 -0
- package/src/category/i18n/pt.ts +27 -0
- package/src/category/i18n/ru.ts +27 -0
- package/src/category/i18n/sv.ts +27 -0
- package/src/category/i18n/tr.ts +27 -0
- package/src/category/i18n/zh.ts +27 -0
- package/src/category/index.ts +27 -0
- package/src/components/PreviewNavSidebar.astro +116 -0
- package/src/components/PreviewToolbar.astro +143 -0
- package/src/data.ts +10 -0
- package/src/entries.ts +10 -0
- package/src/env.d.ts +5 -0
- package/src/index.ts +21 -0
- package/src/layouts/PreviewLayout.astro +117 -0
- package/src/pages/[locale]/[slug].astro +161 -0
- package/src/pages/[locale].astro +251 -0
- package/src/pages/index.astro +4 -0
- package/src/tests/faq_count.test.ts +18 -0
- package/src/tests/i18n_coverage.test.ts +36 -0
- package/src/tests/locale_completeness.test.ts +29 -0
- package/src/tests/mocks/astro_mock.js +2 -0
- package/src/tests/no_en_dash.test.ts +41 -0
- package/src/tests/no_h1_in_components.test.ts +48 -0
- package/src/tests/schemas_fulfillment.test.ts +23 -0
- package/src/tests/seo_length.test.ts +22 -0
- package/src/tests/shared-test-helpers.ts +56 -0
- package/src/tests/slug_language_code_format.test.ts +23 -0
- package/src/tests/slug_uniqueness.test.ts +81 -0
- package/src/tests/title_quality.test.ts +55 -0
- package/src/tests/tool_exports.test.ts +34 -0
- package/src/tests/tool_validation.test.ts +16 -0
- package/src/tool/board-game-timer/bibliography.astro +16 -0
- package/src/tool/board-game-timer/bibliography.ts +12 -0
- package/src/tool/board-game-timer/board-game-timer.css +1615 -0
- package/src/tool/board-game-timer/client.ts +17 -0
- package/src/tool/board-game-timer/component.astro +22 -0
- package/src/tool/board-game-timer/components/DuelTimer.astro +75 -0
- package/src/tool/board-game-timer/components/MultiplayerTimer.astro +58 -0
- package/src/tool/board-game-timer/components/SetupPanel.astro +155 -0
- package/src/tool/board-game-timer/components/StatsModal.astro +52 -0
- package/src/tool/board-game-timer/entry.ts +76 -0
- package/src/tool/board-game-timer/i18n/de.ts +303 -0
- package/src/tool/board-game-timer/i18n/en.ts +304 -0
- package/src/tool/board-game-timer/i18n/es.ts +303 -0
- package/src/tool/board-game-timer/i18n/fr.ts +303 -0
- package/src/tool/board-game-timer/i18n/id.ts +303 -0
- package/src/tool/board-game-timer/i18n/it.ts +303 -0
- package/src/tool/board-game-timer/i18n/ja.ts +306 -0
- package/src/tool/board-game-timer/i18n/ko.ts +303 -0
- package/src/tool/board-game-timer/i18n/nl.ts +303 -0
- package/src/tool/board-game-timer/i18n/pl.ts +303 -0
- package/src/tool/board-game-timer/i18n/pt.ts +303 -0
- package/src/tool/board-game-timer/i18n/ru.ts +303 -0
- package/src/tool/board-game-timer/i18n/sv.ts +303 -0
- package/src/tool/board-game-timer/i18n/tr.ts +303 -0
- package/src/tool/board-game-timer/i18n/zh.ts +303 -0
- package/src/tool/board-game-timer/index.ts +9 -0
- package/src/tool/board-game-timer/logic.test.ts +282 -0
- package/src/tool/board-game-timer/logic.ts +6 -0
- package/src/tool/board-game-timer/modules/ColorHelper.ts +26 -0
- package/src/tool/board-game-timer/modules/DuelViewManager.ts +120 -0
- package/src/tool/board-game-timer/modules/FormatHelper.ts +12 -0
- package/src/tool/board-game-timer/modules/GameRunner.ts +136 -0
- package/src/tool/board-game-timer/modules/GameState.ts +48 -0
- package/src/tool/board-game-timer/modules/MultiplayerViewManager.ts +163 -0
- package/src/tool/board-game-timer/modules/SetupPanelManager.ts +209 -0
- package/src/tool/board-game-timer/modules/SoundManager.ts +109 -0
- package/src/tool/board-game-timer/modules/SpeechManager.ts +1 -0
- package/src/tool/board-game-timer/modules/StatsManager.ts +87 -0
- package/src/tool/board-game-timer/modules/StatsModalManager.ts +74 -0
- package/src/tool/board-game-timer/modules/TimerEngine.ts +175 -0
- package/src/tool/board-game-timer/modules/TimerEngineBase.ts +49 -0
- package/src/tool/board-game-timer/modules/TimerEngineClock.ts +127 -0
- package/src/tool/board-game-timer/modules/TimerStateHelper.ts +55 -0
- package/src/tool/board-game-timer/modules/particles.ts +108 -0
- package/src/tool/board-game-timer/seo.astro +16 -0
- package/src/tool/board-game-timer/types.ts +97 -0
- package/src/tool/dice-roller-simulator/bibliography.astro +16 -0
- package/src/tool/dice-roller-simulator/bibliography.ts +12 -0
- package/src/tool/dice-roller-simulator/client.ts +273 -0
- package/src/tool/dice-roller-simulator/component.astro +75 -0
- package/src/tool/dice-roller-simulator/components/DiceGrid.astro +55 -0
- package/src/tool/dice-roller-simulator/components/RollHistorySection.astro +26 -0
- package/src/tool/dice-roller-simulator/components/RollResultBanner.astro +10 -0
- package/src/tool/dice-roller-simulator/components/StatsPanel.astro +36 -0
- package/src/tool/dice-roller-simulator/dice-roller-simulator.css +1319 -0
- package/src/tool/dice-roller-simulator/entry.ts +39 -0
- package/src/tool/dice-roller-simulator/i18n/de.ts +305 -0
- package/src/tool/dice-roller-simulator/i18n/en.ts +305 -0
- package/src/tool/dice-roller-simulator/i18n/es.ts +305 -0
- package/src/tool/dice-roller-simulator/i18n/fr.ts +305 -0
- package/src/tool/dice-roller-simulator/i18n/id.ts +305 -0
- package/src/tool/dice-roller-simulator/i18n/it.ts +305 -0
- package/src/tool/dice-roller-simulator/i18n/ja.ts +305 -0
- package/src/tool/dice-roller-simulator/i18n/ko.ts +305 -0
- package/src/tool/dice-roller-simulator/i18n/nl.ts +305 -0
- package/src/tool/dice-roller-simulator/i18n/pl.ts +305 -0
- package/src/tool/dice-roller-simulator/i18n/pt.ts +305 -0
- package/src/tool/dice-roller-simulator/i18n/ru.ts +305 -0
- package/src/tool/dice-roller-simulator/i18n/sv.ts +305 -0
- package/src/tool/dice-roller-simulator/i18n/tr.ts +305 -0
- package/src/tool/dice-roller-simulator/i18n/zh.ts +305 -0
- package/src/tool/dice-roller-simulator/index.ts +9 -0
- package/src/tool/dice-roller-simulator/modules/chart.ts +32 -0
- package/src/tool/dice-roller-simulator/modules/history.ts +87 -0
- package/src/tool/dice-roller-simulator/modules/particles.ts +22 -0
- package/src/tool/dice-roller-simulator/seo.astro +16 -0
- package/src/tools.ts +9 -0
- package/src/types.ts +70 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { GameState } from './modules/GameState';
|
|
2
|
+
import { SetupPanelManager } from './modules/SetupPanelManager';
|
|
3
|
+
import { GameRunner } from './modules/GameRunner';
|
|
4
|
+
|
|
5
|
+
function init() {
|
|
6
|
+
const state = new GameState();
|
|
7
|
+
const runner = new GameRunner(state);
|
|
8
|
+
const setup = new SetupPanelManager(state, () => {
|
|
9
|
+
runner.startGame(setup.getBaseDuration());
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (document.readyState === 'loading') {
|
|
14
|
+
document.addEventListener('DOMContentLoaded', init);
|
|
15
|
+
} else {
|
|
16
|
+
init();
|
|
17
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
import './board-game-timer.css';
|
|
3
|
+
import SetupPanel from './components/SetupPanel.astro';
|
|
4
|
+
import DuelTimer from './components/DuelTimer.astro';
|
|
5
|
+
import MultiplayerTimer from './components/MultiplayerTimer.astro';
|
|
6
|
+
import StatsModal from './components/StatsModal.astro';
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
ui: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { ui } = Astro.props;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<div class="timer-main-wrapper" data-ui={JSON.stringify(ui)}>
|
|
16
|
+
<SetupPanel ui={ui} />
|
|
17
|
+
<DuelTimer ui={ui} />
|
|
18
|
+
<MultiplayerTimer ui={ui} />
|
|
19
|
+
<StatsModal ui={ui} />
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<script src="./client.ts"></script>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Icon } from 'astro-icon/components';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
ui: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { ui } = Astro.props;
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<div id="duel-timer-panel" class="timer-duel-panel" style="display: none">
|
|
12
|
+
<div class="duel-player-side" id="duel-p1" data-player-id="">
|
|
13
|
+
<div class="duel-player-container">
|
|
14
|
+
<div class="side-header">
|
|
15
|
+
<span class="player-name-lbl" id="duel-p1-name">Player 1</span>
|
|
16
|
+
<button type="button" class="side-rotate-btn" title="Rotate Display">
|
|
17
|
+
<Icon name="mdi:rotate-3d-variant" width="20" height="20" />
|
|
18
|
+
</button>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="side-timer" id="duel-p1-timer">00:00</div>
|
|
21
|
+
<div class="side-meta">
|
|
22
|
+
<span class="rounds-count-lbl" id="duel-p1-rounds">Turns: 0</span>
|
|
23
|
+
<span class="side-status-badge" id="duel-p1-status">READY</span>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="duel-ripple-effect"></div>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div class="duel-controls-bar">
|
|
30
|
+
<button type="button" class="ctrl-bar-btn" id="duel-back-btn" title="Back to Setup">
|
|
31
|
+
<Icon name="mdi:cog" width="24" height="24" />
|
|
32
|
+
</button>
|
|
33
|
+
|
|
34
|
+
<div class="round-counter-display">
|
|
35
|
+
<span class="round-lbl">{ui.roundLabel || 'Round'}</span>
|
|
36
|
+
<span class="round-num" id="duel-round-number">1</span>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<button type="button" class="ctrl-bar-btn primary-ctrl" id="duel-pause-btn" title="Pause Game">
|
|
40
|
+
<Icon name="mdi:pause" width="28" height="28" />
|
|
41
|
+
</button>
|
|
42
|
+
|
|
43
|
+
<button type="button" class="ctrl-bar-btn" id="duel-reset-btn" title="Reset Game">
|
|
44
|
+
<Icon name="mdi:refresh" width="24" height="24" />
|
|
45
|
+
</button>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div class="duel-player-side" id="duel-p2" data-player-id="">
|
|
49
|
+
<div class="duel-player-container">
|
|
50
|
+
<div class="side-header">
|
|
51
|
+
<span class="player-name-lbl" id="duel-p2-name">Player 2</span>
|
|
52
|
+
<button type="button" class="side-rotate-btn" title="Rotate Display">
|
|
53
|
+
<Icon name="mdi:rotate-3d-variant" width="20" height="20" />
|
|
54
|
+
</button>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="side-timer" id="duel-p2-timer">00:00</div>
|
|
57
|
+
<div class="side-meta">
|
|
58
|
+
<span class="rounds-count-lbl" id="duel-p2-rounds">Turns: 0</span>
|
|
59
|
+
<span class="side-status-badge" id="duel-p2-status">READY</span>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="duel-ripple-effect"></div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div id="duel-paused-overlay" class="duel-overlay paused-overlay" style="display: none">
|
|
66
|
+
<div class="overlay-card">
|
|
67
|
+
<Icon name="mdi:pause-circle-outline" width="64" height="64" />
|
|
68
|
+
<h3>{ui.pausedOverlay || 'Game Paused'}</h3>
|
|
69
|
+
<button type="button" class="resume-hero-btn" id="duel-resume-btn">
|
|
70
|
+
<Icon name="mdi:play" width="24" height="24" />
|
|
71
|
+
<span>{ui.resumeGame || 'Resume'}</span>
|
|
72
|
+
</button>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Icon } from 'astro-icon/components';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
ui: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { ui } = Astro.props;
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<div id="multiplayer-timer-panel" class="timer-multiplayer-panel" style="display: none">
|
|
12
|
+
<div class="multiplayer-header-bar">
|
|
13
|
+
<button type="button" class="ctrl-bar-btn" id="multi-back-btn" title="Back to Setup">
|
|
14
|
+
<Icon name="mdi:cog" width="22" height="22" />
|
|
15
|
+
</button>
|
|
16
|
+
|
|
17
|
+
<div class="game-meta-indicators">
|
|
18
|
+
<div class="meta-indicator">
|
|
19
|
+
<span class="meta-lbl">{ui.roundLabel || 'Round'}</span>
|
|
20
|
+
<span class="meta-val" id="multi-round-number">1</span>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="meta-indicator">
|
|
23
|
+
<span class="meta-lbl">{ui.modeLabel || 'Mode'}</span>
|
|
24
|
+
<span class="meta-val" id="multi-mode-indicator">Normal</span>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div class="header-action-group">
|
|
29
|
+
<button type="button" class="ctrl-bar-btn primary-ctrl" id="multi-pause-btn" title="Pause Game">
|
|
30
|
+
<Icon name="mdi:pause" width="24" height="24" />
|
|
31
|
+
</button>
|
|
32
|
+
<button type="button" class="ctrl-bar-btn" id="multi-reset-btn" title="Reset Game">
|
|
33
|
+
<Icon name="mdi:refresh" width="22" height="22" />
|
|
34
|
+
</button>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<div class="multiplayer-grid" id="multiplayer-players-grid"></div>
|
|
39
|
+
|
|
40
|
+
<div class="giant-turn-button-container">
|
|
41
|
+
<button type="button" id="giant-next-turn-btn" class="giant-next-btn">
|
|
42
|
+
<Icon name="mdi:skip-next" width="48" height="48" />
|
|
43
|
+
<span class="giant-btn-lbl">{ui.nextTurn || 'End Turn'}</span>
|
|
44
|
+
<span class="giant-btn-sub" id="giant-active-player-name">Player Name</span>
|
|
45
|
+
</button>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div id="multi-paused-overlay" class="multi-overlay paused-overlay" style="display: none">
|
|
49
|
+
<div class="overlay-card">
|
|
50
|
+
<Icon name="mdi:pause-circle-outline" width="64" height="64" />
|
|
51
|
+
<h3>{ui.pausedOverlay || 'Game Paused'}</h3>
|
|
52
|
+
<button type="button" class="resume-hero-btn" id="multi-resume-btn">
|
|
53
|
+
<Icon name="mdi:play" width="24" height="24" />
|
|
54
|
+
<span>{ui.resumeGame || 'Resume'}</span>
|
|
55
|
+
</button>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Icon } from 'astro-icon/components';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
ui: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { ui } = Astro.props;
|
|
9
|
+
|
|
10
|
+
const presets = [
|
|
11
|
+
{ id: 'casual', label: 'Casual', mode: 'normal', time: 120, increment: 0, delay: 0 },
|
|
12
|
+
{ id: 'blitz', label: 'Blitz', mode: 'normal', time: 300, increment: 0, delay: 0 },
|
|
13
|
+
{ id: 'strategic', label: 'Strategic', mode: 'fischer', time: 1800, increment: 10, delay: 0 },
|
|
14
|
+
{ id: 'hourglass', label: 'Hourglass', mode: 'hourglass', time: 60, increment: 0, delay: 0 },
|
|
15
|
+
{ id: 'cooperative', label: 'Cooperative', mode: 'normal', time: 600, increment: 0, delay: 0 },
|
|
16
|
+
{ id: 'custom', label: 'Custom', mode: 'normal', time: 300, increment: 0, delay: 0 },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const colors = ['blue', 'red', 'green', 'yellow', 'purple', 'orange', 'teal', 'pink'] as const;
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
<div id="setup-panel" class="timer-setup-panel">
|
|
23
|
+
<div class="setup-grid">
|
|
24
|
+
<div class="setup-card settings-column">
|
|
25
|
+
<div class="settings-group">
|
|
26
|
+
<h4>{ui.presetsTitle || 'Presets'}</h4>
|
|
27
|
+
<div class="presets-grid">
|
|
28
|
+
{presets.map((preset) => (
|
|
29
|
+
<button
|
|
30
|
+
type="button"
|
|
31
|
+
class="preset-card"
|
|
32
|
+
data-preset={preset.id}
|
|
33
|
+
data-mode={preset.mode}
|
|
34
|
+
data-time={preset.time}
|
|
35
|
+
data-increment={preset.increment}
|
|
36
|
+
data-delay={preset.delay}
|
|
37
|
+
>
|
|
38
|
+
<span class="preset-name">{preset.label}</span>
|
|
39
|
+
<span class="preset-meta">
|
|
40
|
+
{preset.id === 'custom' ? 'Manual' : `${Math.floor(preset.time / 60)}m ${preset.increment > 0 ? `+${preset.increment}s` : ''}`}
|
|
41
|
+
</span>
|
|
42
|
+
</button>
|
|
43
|
+
))}
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div class="settings-group">
|
|
48
|
+
<h4>{ui.timeControlTitle || 'Time Control'}</h4>
|
|
49
|
+
<div class="settings-grid">
|
|
50
|
+
<div class="control-box">
|
|
51
|
+
<span class="config-label">{ui.modeLabel || 'Mode'}</span>
|
|
52
|
+
<select id="setting-mode" class="select-input">
|
|
53
|
+
<option value="normal">{ui.modeNormal || 'Normal Count'}</option>
|
|
54
|
+
<option value="fischer">{ui.modeFischer || 'Fischer (Increment)'}</option>
|
|
55
|
+
<option value="bronstein">{ui.modeBronstein || 'Bronstein (Delay)'}</option>
|
|
56
|
+
<option value="hourglass">{ui.modeHourglass || 'Hourglass'}</option>
|
|
57
|
+
<option value="turn">{ui.modeTurn || 'Turn Reset'}</option>
|
|
58
|
+
</select>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<div class="control-box">
|
|
62
|
+
<span class="config-label">{ui.baseTimeLabel || 'Base Time'}</span>
|
|
63
|
+
<div class="time-inputs-container">
|
|
64
|
+
<div class="time-adjuster-group">
|
|
65
|
+
<button type="button" class="time-adj-btn dec-min" aria-label="Decrease minutes">-</button>
|
|
66
|
+
<input type="number" id="setting-mins" class="num-input" value="5" min="0" max="99" />
|
|
67
|
+
<button type="button" class="time-adj-btn inc-min" aria-label="Increase minutes">+</button>
|
|
68
|
+
<span class="time-unit-lbl">m</span>
|
|
69
|
+
</div>
|
|
70
|
+
<span class="time-sep">:</span>
|
|
71
|
+
<div class="time-adjuster-group">
|
|
72
|
+
<button type="button" class="time-adj-btn dec-sec" aria-label="Decrease seconds">-</button>
|
|
73
|
+
<input type="number" id="setting-secs" class="num-input" value="0" min="0" max="59" />
|
|
74
|
+
<button type="button" class="time-adj-btn inc-sec" aria-label="Increase seconds">+</button>
|
|
75
|
+
<span class="time-unit-lbl">s</span>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div class="control-box active-fischer active-bronstein">
|
|
81
|
+
<span class="config-label">{ui.incrementLabel || 'Increment/Delay'}</span>
|
|
82
|
+
<div class="number-input-wrapper">
|
|
83
|
+
<input type="number" id="setting-increment" class="num-input-full" value="0" min="0" max="99" />
|
|
84
|
+
<span class="unit-label">s</span>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<div class="control-box">
|
|
89
|
+
<span class="config-label">{ui.warningTimeLabel || 'Warning Threshold'}</span>
|
|
90
|
+
<div class="number-input-wrapper">
|
|
91
|
+
<input type="number" id="setting-warning" class="num-input-full" value="30" min="0" max="99" />
|
|
92
|
+
<span class="unit-label">s</span>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<div class="settings-group">
|
|
99
|
+
<h4>{ui.audioTitle || 'Audio Alerts'}</h4>
|
|
100
|
+
<div class="audio-toggles">
|
|
101
|
+
<label class="toggle-control">
|
|
102
|
+
<input type="checkbox" id="toggle-sound" checked />
|
|
103
|
+
<span class="toggle-slider"></span>
|
|
104
|
+
<span class="toggle-label">{ui.soundAlerts || 'Sound Effects'}</span>
|
|
105
|
+
</label>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<div class="setup-card players-column">
|
|
111
|
+
<div class="settings-group">
|
|
112
|
+
<h4>{ui.playersTitle || 'Players'}</h4>
|
|
113
|
+
<div class="player-input-block">
|
|
114
|
+
<div class="player-input-row-nowrap">
|
|
115
|
+
<input type="text" id="new-player-name" class="text-input" placeholder={ui.playerNamePlaceholder} maxlength="15" />
|
|
116
|
+
<button type="button" id="add-player-btn" class="btn-action primary">
|
|
117
|
+
<Icon name="mdi:plus" width="20" height="20" />
|
|
118
|
+
</button>
|
|
119
|
+
</div>
|
|
120
|
+
<div class="color-picker-row">
|
|
121
|
+
<div class="color-picker-grid">
|
|
122
|
+
{colors.map((color) => (
|
|
123
|
+
<button
|
|
124
|
+
type="button"
|
|
125
|
+
class={`color-dot color-${color}`}
|
|
126
|
+
data-color={color}
|
|
127
|
+
aria-label={`Select ${color}`}
|
|
128
|
+
></button>
|
|
129
|
+
))}
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<div class="settings-group list-group">
|
|
136
|
+
<div class="player-list-header">
|
|
137
|
+
<span>{ui.playersAdded || 'Added Players'}</span>
|
|
138
|
+
<span id="player-count" class="count-badge">0</span>
|
|
139
|
+
</div>
|
|
140
|
+
<div id="player-list-container" class="player-list-scroll"></div>
|
|
141
|
+
<div id="empty-players-hint" class="players-hint">
|
|
142
|
+
<Icon name="mdi:account-group-outline" width="40" height="40" />
|
|
143
|
+
<p>{ui.noPlayersHint || 'Add at least 2 players to start'}</p>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<div class="setup-actions">
|
|
150
|
+
<button type="button" id="start-game-btn" class="start-hero-btn" disabled>
|
|
151
|
+
<Icon name="mdi:play" width="28" height="28" />
|
|
152
|
+
<span>{ui.startGame || 'Start Game'}</span>
|
|
153
|
+
</button>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Icon } from 'astro-icon/components';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
ui: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { ui } = Astro.props;
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<div id="stats-modal" class="timer-modal-backdrop" style="display: none">
|
|
12
|
+
<div class="stats-modal-card">
|
|
13
|
+
<div class="modal-header">
|
|
14
|
+
<div class="header-title-group">
|
|
15
|
+
<Icon name="mdi:chart-bar" width="28" height="28" />
|
|
16
|
+
<h3>{ui.statsTitle || 'Game Statistics'}</h3>
|
|
17
|
+
</div>
|
|
18
|
+
<button type="button" id="stats-close-btn" class="modal-close-btn" aria-label="Close">
|
|
19
|
+
<Icon name="mdi:close" width="24" height="24" />
|
|
20
|
+
</button>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<div class="modal-body">
|
|
24
|
+
<div class="general-stats-grid">
|
|
25
|
+
<div class="stat-card-mini">
|
|
26
|
+
<span class="mini-lbl">{ui.totalGameTime || 'Total Time'}</span>
|
|
27
|
+
<span class="mini-val" id="stats-total-time">00:00:00</span>
|
|
28
|
+
</div>
|
|
29
|
+
<div class="stat-card-mini">
|
|
30
|
+
<span class="mini-lbl">{ui.totalRounds || 'Total Rounds'}</span>
|
|
31
|
+
<span class="mini-val" id="stats-total-rounds">0</span>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="stat-card-mini highlighted-stat">
|
|
34
|
+
<span class="mini-lbl">{ui.slowestPlayer || 'Slowest Player'}</span>
|
|
35
|
+
<span class="mini-val" id="stats-slowest-player">-</span>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div class="charts-section">
|
|
40
|
+
<h4>{ui.playerPerformance || 'Player Time Analysis'}</h4>
|
|
41
|
+
<div class="players-ranking-list" id="stats-players-ranking"></div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div class="modal-footer">
|
|
46
|
+
<button type="button" id="stats-restart-btn" class="modal-action-btn primary">
|
|
47
|
+
<Icon name="mdi:autorenew" width="20" height="20" />
|
|
48
|
+
<span>{ui.playAgain || 'New Game'}</span>
|
|
49
|
+
</button>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { TabletopToolEntry, ToolLocaleContent } from '../../types';
|
|
2
|
+
|
|
3
|
+
export type BoardGameTimerUI = {
|
|
4
|
+
title: string;
|
|
5
|
+
setupTitle: string;
|
|
6
|
+
playerNamePlaceholder: string;
|
|
7
|
+
addPlayer: string;
|
|
8
|
+
removePlayer: string;
|
|
9
|
+
startGame: string;
|
|
10
|
+
pauseGame: string;
|
|
11
|
+
resumeGame: string;
|
|
12
|
+
resetGame: string;
|
|
13
|
+
nextTurn: string;
|
|
14
|
+
addTime: string;
|
|
15
|
+
removeTime: string;
|
|
16
|
+
playerLabel: string;
|
|
17
|
+
minutesLabel: string;
|
|
18
|
+
secondsLabel: string;
|
|
19
|
+
expiredLabel: string;
|
|
20
|
+
overtimeLabel: string;
|
|
21
|
+
turnIndicator: string;
|
|
22
|
+
warningLowTime: string;
|
|
23
|
+
pausedOverlay: string;
|
|
24
|
+
roundLabel: string;
|
|
25
|
+
settingsTitle: string;
|
|
26
|
+
baseTimeLabel: string;
|
|
27
|
+
incrementLabel: string;
|
|
28
|
+
warningTimeLabel: string;
|
|
29
|
+
confirmLabel: string;
|
|
30
|
+
cancelLabel: string;
|
|
31
|
+
noPlayersHint: string;
|
|
32
|
+
addPlayerHint: string;
|
|
33
|
+
timeUpLabel: string;
|
|
34
|
+
presetsTitle: string;
|
|
35
|
+
timeControlTitle: string;
|
|
36
|
+
modeLabel: string;
|
|
37
|
+
modeNormal: string;
|
|
38
|
+
modeFischer: string;
|
|
39
|
+
modeBronstein: string;
|
|
40
|
+
modeHourglass: string;
|
|
41
|
+
modeTurn: string;
|
|
42
|
+
audioTitle: string;
|
|
43
|
+
soundAlerts: string;
|
|
44
|
+
playersTitle: string;
|
|
45
|
+
playersAdded: string;
|
|
46
|
+
statsTitle: string;
|
|
47
|
+
totalGameTime: string;
|
|
48
|
+
totalRounds: string;
|
|
49
|
+
slowestPlayer: string;
|
|
50
|
+
playerPerformance: string;
|
|
51
|
+
playAgain: string;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type BoardGameTimerLocaleContent = ToolLocaleContent<BoardGameTimerUI>;
|
|
55
|
+
|
|
56
|
+
export const boardGameTimer: TabletopToolEntry<BoardGameTimerUI> = {
|
|
57
|
+
id: 'board-game-timer',
|
|
58
|
+
icons: { bg: 'mdi:timer-outline', fg: 'mdi:timer' },
|
|
59
|
+
i18n: {
|
|
60
|
+
en: () => import('./i18n/en').then((m) => m.content),
|
|
61
|
+
de: () => import('./i18n/de').then((m) => m.content),
|
|
62
|
+
es: () => import('./i18n/es').then((m) => m.content),
|
|
63
|
+
fr: () => import('./i18n/fr').then((m) => m.content),
|
|
64
|
+
id: () => import('./i18n/id').then((m) => m.content),
|
|
65
|
+
it: () => import('./i18n/it').then((m) => m.content),
|
|
66
|
+
ja: () => import('./i18n/ja').then((m) => m.content),
|
|
67
|
+
ko: () => import('./i18n/ko').then((m) => m.content),
|
|
68
|
+
nl: () => import('./i18n/nl').then((m) => m.content),
|
|
69
|
+
pl: () => import('./i18n/pl').then((m) => m.content),
|
|
70
|
+
pt: () => import('./i18n/pt').then((m) => m.content),
|
|
71
|
+
ru: () => import('./i18n/ru').then((m) => m.content),
|
|
72
|
+
sv: () => import('./i18n/sv').then((m) => m.content),
|
|
73
|
+
tr: () => import('./i18n/tr').then((m) => m.content),
|
|
74
|
+
zh: () => import('./i18n/zh').then((m) => m.content),
|
|
75
|
+
},
|
|
76
|
+
};
|