diff --git a/design/README.md b/design/README.md index 6a9e5cf..5668dfb 100644 --- a/design/README.md +++ b/design/README.md @@ -32,6 +32,14 @@ The HTML mock includes two chrome variants — **A (single-row)** and **B (two-r --- +## Changes since v1 + +- **Settings → Profile section** added at the top of the dialog with two new persisted preferences: **Username** (text input) and **Language** (segmented `English` / `Deutsch`). See "Settings dialog" below for shape + persistence keys. +- **Start Server** action added to the **game detail overlay**, next to **Play**, for installed games that support a dedicated server. Driven by a new `canHostServer: true` flag on the game record. See "Detail overlay → Actions row" and "Game data shape" for the full spec. +- Grid cards are **unchanged** — Start Server only ever appears in the detail overlay. + +--- + ## Screens / views ### 1. Main library (variant A — primary) @@ -88,12 +96,26 @@ Opens when the user **clicks anywhere on a game card except the action button**. 2. **Body** — 22px top, 26px bottom, 28px horizontal: - **Meta grid** — 4-column CSS grid, 12px gap. Each cell: `padding 10px 12px`, `background rgba(255,255,255,0.025)`, `1px solid var(--bd-1)`, `border-radius: 8px`. Cells (in order): `Size` (e.g. 8.2 GB), `Players` (icon + range), `Version` (mono, e.g. 2018.04.12), `Status` (Installed / Local / Not downloaded). - **Description** — 14px / 1.55 line-height, `var(--t-2)`, `text-wrap: pretty`, `max-width: 64ch`. - - **Actions row** — flex row, 10px gap, 4px top padding: - - Primary action button (44px tall, see "Action button" below — Play / Install / Download depending on state) - - If `state === 'installed'`: ghost-button **Uninstall** — 44px, `background rgba(255,255,255,0.04)`, `1px solid var(--bd-2)`, `border-radius: 8px`, text `#f87171`, trash icon. On hover: bg `rgba(239,68,68,0.10)`, border `rgba(239,68,68,0.40)`, text `#fca5a5`. - - If `state === 'local'`: ghost-button **Delete from disk** (same danger styling). - - Spacer (`flex: 1`). - - Ghost-button **View files** (neutral) — opens system file manager at the game folder. + - **Actions row** — flex row, 10px gap, 4px top padding. Order, left → right: + 1. **Primary action button** (44px tall, see "Action button" below — Play / Install / Download depending on state). + 2. **Start Server** — *only* when `game.canHostServer === true` **and** `state === 'installed'`. Same 44px height as Play, but visually a peer secondary action (see "Start Server button" below). Triggers a Tauri command that spawns the game's dedicated-server executable in headless mode against the local LAN (port + server config out of scope here — leave a `startServer(gameId)` IPC stub). + 3. If `state === 'installed'`: ghost-button **Uninstall** — 44px, `background rgba(255,255,255,0.04)`, `1px solid var(--bd-2)`, `border-radius: 8px`, text `#f87171`, trash icon. On hover: bg `rgba(239,68,68,0.10)`, border `rgba(239,68,68,0.40)`, text `#fca5a5`. + 4. If `state === 'local'`: ghost-button **Delete from disk** (same danger styling). + 5. If `state === 'downloading'`: ghost-button **Cancel** (same danger styling). + 6. Spacer (`flex: 1`). + 7. Ghost-button **View files** (neutral) — opens system file manager at the game folder. + +#### Start Server button + +A secondary-but-equal action that sits next to **Play**. The intent is to read as a host-action ("I want to put this game on the LAN") without competing with the green Play button for the player's primary attention. + +- Same shape and height as Play: 44px tall, `border-radius: 8px`, `font 14px / 600`, 8px gap between icon and label, padding `0 22px`. +- Surface: `background: color-mix(in srgb, var(--accent) 14%, rgba(255,255,255,0.04))`, `border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent)`, `box-shadow: inset 0 1px 0 rgba(255,255,255,0.06)`. Text in `--t-1`. +- **Icon** in `--accent`: a small server-rack glyph (two stacked rounded rectangles each with an LED dot and a hint of wiring). 13×13. SVG in `components.jsx → Icon.server`. +- Hover: `background: color-mix(in srgb, var(--accent) 22%, ...)`, border darkens to `color-mix(... 75%, transparent)`. Active: `transform: scale(0.98)` (shared with `.act-btn`). +- A future *running* state (live indicator dot + "Server running" label + click-to-stop) is **not** in this round — flag as a follow-up when wiring the real spawn. + +The button is purposefully **not** present on game cards in the grid — hosting a server is intentional and benefits from the context of the detail overlay (player count, version, etc.). Don't add it to cards. --- @@ -108,10 +130,20 @@ Opens when the user clicks **Settings** from the kebab menu. Same modal-scrim tr │ Settings [×] │ ← head: 22 28 18, 1px bottom border ├─────────────────────────────────────────┤ │ │ -│ APPEARANCE │ ← section title: 10.5px / 700 / 0.12em / uppercase / --t-3 +│ PROFILE │ ← section title (new): 10.5px / 700 / 0.12em / uppercase / --t-3 │ │ -│ Accent color │ ← row label: 14px / 600 / --t-1 -│ Used for primary actions and highlights │ ← row hint: 12px / --t-3 +│ Username │ ← row label: 14px / 600 / --t-1 +│ Shown to other players on the LAN │ ← row hint: 12px / --t-3 +│ [ Enter a username ] │ ← text input (220×36) +│ │ +│ Language │ +│ Interface language │ +│ [English│Deutsch] │ ← segmented radio (new) +│ │ +│ APPEARANCE │ +│ │ +│ Accent color │ +│ Used for primary actions and highlights │ │ ⬤⬤⬤⬤⬤⬤ │ ← 6 swatches, right-aligned │ │ │ Background │ @@ -138,6 +170,11 @@ Opens when the user clicks **Settings** from the kebab menu. Same modal-scrim tr - Left (`settings-row-info`): label (14px / 600 / `--t-1`) + hint (3px-top, 12px / `--t-3`) - Right (`settings-row-control`): the control +**Profile section** (new in this round). Two rows, rendered **above** Appearance — it's the most personal/identity-shaped setting so it's the first thing the user sees in Settings. + +- **Username** — `` wrapped in a styled container: 220px wide, 36px tall, `background var(--bg-3)`, `1px solid var(--bd-1)`, `border-radius: 8px`, `padding: 0 12px`. Input itself is transparent/borderless, `font 13.5px / 600`, color `--t-1`, placeholder `"Enter a username"` in `--t-3` / 500. `maxLength={24}`, `spellCheck={false}`. On focus the container gets `background var(--bg-2)`, border `var(--accent)`, and an accent focus ring `box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent)`. +- **Language** — same segmented-radio control as Background / Density / Cover aspect, with two options: `English` (value `'en'`) and `Deutsch` (value `'de'`). Active option gets the accent fill, same as the other segmented radios. + **Color swatch picker:** flex row of 8px-gapped buttons. Each swatch is 32×32, `border-radius: 9px`, no border. Inside, a 100% × 100% rounded-8 colored dot with inset shadow `0 0 0 1px rgba(255,255,255,0.08)`. Hover: dot scales 1.06. **Active**: dot has ring `box-shadow: 0 0 0 2px var(--bg-2), 0 0 0 4px ` and shows a centered white check icon with drop-shadow `0 1px 2px rgba(0,0,0,0.5)`. Six accent options: Blue `#3b82f6`, Cyan `#22d3ee`, Violet `#a855f7`, Green `#22c55e`, Amber `#f59e0b`, Red `#ef4444`. @@ -147,6 +184,8 @@ Six accent options: Blue `#3b82f6`, Cyan `#22d3ee`, Violet `#a855f7`, Green `#22 **Done button:** filled button in `--accent`, 36px tall, 13.5px / 600. Closes the dialog. Persisted settings (write through to local storage / Tauri config): +- `username`: string, max 24 chars. Default `"Commander"` (placeholder — feel free to default to the OS username on first run). Used as the network identity for LAN sessions; the hint copy *"Shown to other players on the LAN"* tells the user what it does. +- `language`: `'en'` | `'de'`. Default `'en'`. Drives an i18n layer (introduce one if it doesn't exist yet — `react-i18next` or similar). Initial copy is English-only in the mock; German translations need to be added as part of implementation. Recommend detecting the OS locale on first run and defaulting to `'de'` if the system language starts with `de`. - `accent`: one of the six hex values above. Default `#3b82f6`. - `bg`: `flat` | `gradient` | `animated`. Default `gradient`. - `density`: `compact` | `normal` | `large`. Default `normal`. @@ -363,12 +402,16 @@ type Game = { state: 'installed' | 'local' | 'downloading' | 'none'; progress?: number; // 0–1 — present only when state === 'downloading' speed?: number; // MB/s — present only when state === 'downloading' + peers?: number; // LAN peers currently seeding players: string; // e.g. "2–32" tags: string[]; cover: { c1: string; c2: string; accent: string; mood?: string }; + canHostServer?: boolean; // true if the game ships with a dedicated-server binary }; ``` +**Server-capable games** in the mock catalog (`canHostServer: true`): BF1942, BF2, CoD2, CoD4, CoD:UO, CS 1.6, CS:Source, Cube 2/Sauerbraten, Doom 3, L4D2, Minecraft, Quake III, TF2, UT2004. RTS / social-deduction / co-op-only-P2P games (AoE II HD, RA3, Generals ZH, Among Us, Portal 2, StarCraft, Warcraft III, AvP, 8-Bit Armies, BlazeRush) are not flagged — they host in-game. In production the flag should come from the same per-game manifest that drives titles / sizes / cover art. Wire each entry to whatever launch command the dedicated server uses (`hldsexec`, `srcds`, `minecraft_server.jar`, etc.); the IPC stub looks like `startServer(gameId)` returning a handle or process id. + **UI state:** ```ts type LauncherUI = { @@ -380,7 +423,19 @@ type LauncherUI = { }; ``` -**Persisted settings:** see Settings dialog section. Persist via Tauri's plugin-store or a local JSON file in app data dir. +**Persisted settings** (mirror of Settings dialog state): +```ts +type LauncherSettings = { + username: string; // new + language: 'en' | 'de'; // new + accent: string; // hex from the curated 6-color palette + bg: 'flat' | 'gradient' | 'animated'; + density: 'compact' | 'normal' | 'large'; + aspect: 'box' | 'square' | 'banner'; +}; +``` + +Persist via Tauri's plugin-store or a local JSON file in app data dir. Changes from the Settings dialog should write through immediately (no Apply button). **Storage figures:** computed by summing game sizes per state, plus free-space query via Tauri. @@ -448,7 +503,7 @@ Cover art in the design files is **stylized placeholder art** — generated enti In the production app, the launcher should ideally use real cover-art when available (fetch from IGDB / Steam / local game folder) and fall back to the placeholder generator for games without art. The placeholder generator is in `design_reference/components.jsx → GameCover`. -The icon set (search, play, install, download, folder, kebab, sort, users, close, check, chevron, trash) is in `design_reference/components.jsx → Icon`. They are 12-14px inline SVGs using `currentColor`. Reuse as-is or substitute with the codebase's existing icon library at the same visual weight. +The icon set (search, play, **server**, install, download, folder, kebab, sort, users, close, check, chevron, trash) is in `design_reference/components.jsx → Icon`. They are 12-14px inline SVGs using `currentColor`. Reuse as-is or substitute with the codebase's existing icon library at the same visual weight. The `server` glyph is new in this round — two stacked rounded rectangles with LED dots, used only on the Start Server button. Fonts to load: ```html @@ -475,8 +530,13 @@ design_reference/ To preview the design in a browser: 1. Open `SoftLAN Launcher.html` in a static-server (e.g. `python -m http.server` from the folder). -2. You'll see a design canvas with all variants (A, B, C, D, E) side-by-side. Click an artboard's expand button to view it full-screen. -3. The "Tweaks" floating panel in the bottom-right is dev-only — it lets you live-change accent / density / aspect / background. In the production app these live in the Settings dialog (variant E). +2. You'll see a design canvas with all variants (A, B, C, D, E, F) side-by-side. Click an artboard's expand button to view it full-screen. + - **A / B** — chrome variants (A is the chosen direction) + - **C** — detail overlay for an installed, server-capable game (Counter-Strike 1.6) → shows **Play + Start Server + Uninstall** + - **D** — detail overlay for a downloaded-but-not-installed game (CoD 4) → shows **Install + Delete from disk** + - **E** — detail overlay for a downloading game (AvP) → shows the live progress component + **Cancel** + - **F** — Settings dialog open, with the new **Profile** section at the top +3. The "Tweaks" floating panel in the bottom-right is dev-only — it lets you live-change every persisted setting (username / language / accent / background / density / aspect). In the production app these live in the Settings dialog. --- @@ -487,3 +547,5 @@ To preview the design in a browser: - **Error state on action** — if a Download / Install fails, show inline error on the affected card (red border + retry button), and a toast. - **Progress state** — designed. See "Download progress" section above. The action-button slot is swapped for a live `DownloadProgress` component (card + modal variants with container-query fallback for narrow tiles). Wire it to your real progress events; the rendering layer is dev-ready. - **Keyboard arrow nav** — arrow keys should move focus between cards in the grid; not implemented in the mock but mentioned as a goal. +- **"Server running" state** — once Start Server actually spawns a process, the button should switch to a *running* state (live indicator dot + "Server running" label + click-to-stop). Not designed this round — flag for follow-up alongside whatever server-status panel the app grows. +- **German translations** — the language toggle is wired in Settings, but the catalog of translated UI strings hasn't been compiled. Stand up `react-i18next` (or equivalent) and seed `en.json` from the existing copy; `de.json` is a translation task for whoever owns localization. diff --git a/design/design_reference/SoftLAN Launcher.html b/design/design_reference/SoftLAN Launcher.html index b98cf87..b2ef484 100644 --- a/design/design_reference/SoftLAN Launcher.html +++ b/design/design_reference/SoftLAN Launcher.html @@ -35,14 +35,16 @@ const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "accent": "#3b82f6", "density": "normal", "aspect": "square", - "bg": "gradient" + "bg": "gradient", + "username": "d", + "language": "en" }/*EDITMODE-END*/; const ACCENTS = ['#3b82f6', '#22d3ee', '#a855f7', '#22c55e', '#f59e0b', '#ef4444']; function App() { const [t, setTweak] = useTweaks(TWEAK_DEFAULTS); - const heroGame = GAMES.find(g => g.id === 'ra3'); // installed → modal shows Play + Uninstall + const heroGame = GAMES.find(g => g.id === 'cs'); // installed + canHostServer → shows Play + Start Server + Uninstall return ( @@ -62,7 +64,7 @@ function App() { - + @@ -90,6 +92,13 @@ function App() { + + setTweak('username', v)}/> + setTweak('language', v)}/> + setTweak('accent', v)}/> diff --git a/design/design_reference/components.jsx b/design/design_reference/components.jsx index b09162f..4abd6de 100644 --- a/design/design_reference/components.jsx +++ b/design/design_reference/components.jsx @@ -9,6 +9,7 @@ const { useState, useMemo, useRef, useEffect } = React; const Icon = { search: (p) => , play: (p) => , + server: (p) => , install:(p) => , download:(p)=> , folder: (p) => , @@ -435,6 +436,13 @@ function GameDetailModal({ game, accent, onClose }) {

{game.desc}

+ {game.canHostServer && game.state === 'installed' && ( + + )} {game.state === 'installed' && ( )} @@ -468,8 +476,22 @@ const SETTING_OPTIONS = { bg: [{ value: 'flat', label: 'Flat' }, { value: 'gradient', label: 'Gradient' }, { value: 'animated', label: 'Animated' }], density: [{ value: 'compact', label: 'Compact' }, { value: 'normal', label: 'Normal' }, { value: 'large', label: 'Large' }], aspect: [{ value: 'box', label: 'Box-art' }, { value: 'square', label: 'Square' }, { value: 'banner', label: 'Banner' }], + language: [{ value: 'en', label: 'English' }, { value: 'de', label: 'Deutsch' }], }; +function SettingsTextInput({ value, placeholder, maxLength = 24, onChange, accent }) { + return ( +
+ onChange(e.target.value)}/> +
+ ); +} + function SettingsRow({ label, hint, children }) { return (
@@ -524,6 +546,21 @@ function SettingsDialog({ settings, onChange, onClose }) {
+
+
Profile
+ + onChange('username', v)} + accent={settings.accent}/> + + + onChange('language', v)} + accent={settings.accent}/> + +
Appearance
diff --git a/design/design_reference/data.jsx b/design/design_reference/data.jsx index 9d95a5d..3bebafe 100644 --- a/design/design_reference/data.jsx +++ b/design/design_reference/data.jsx @@ -28,13 +28,13 @@ const GAMES = [ cover: { c1: '#ef4444', c2: '#1e3a8a', accent: '#fef08a', mood: 'playful' }, }, { - id: 'bf1942', title: 'Battlefield 1942', size: 7.8, version: '2016.01.30', + id: 'bf1942', title: 'Battlefield 1942', size: 7.8, version: '2016.01.30', canHostServer: true, desc: "The original Battlefield. WWII on land, sea, and air across 16 maps. The mod scene basically reinvented PC gaming on top of this engine.", state: 'installed', players: '2–64', tags: ['FPS', 'Vehicles', 'LAN'], cover: { c1: '#92400e', c2: '#1c1917', accent: '#facc15', mood: 'war' }, }, { - id: 'bf2', title: 'Battlefield 2 Complete', size: 8.0, version: '2021.12.27', + id: 'bf2', title: 'Battlefield 2 Complete', size: 8.0, version: '2021.12.27', canHostServer: true, desc: "Modern combat with commander mode, squads, and the kind of jet-vs-jet duels you tell stories about for a decade.", state: 'local', players: '2–64', tags: ['FPS', 'Vehicles', 'Tactical'], cover: { c1: '#3f3f46', c2: '#0a0a0a', accent: '#22d3ee', mood: 'tactical' }, @@ -46,19 +46,19 @@ const GAMES = [ cover: { c1: '#f97316', c2: '#7c2d12', accent: '#fde047', mood: 'arcade' }, }, { - id: 'cod2', title: 'Call of Duty 2', size: 7.0, version: '2016.09.22', + id: 'cod2', title: 'Call of Duty 2', size: 7.0, version: '2016.09.22', canHostServer: true, desc: "WWII shooter — Russian, British and American campaigns, plus the multiplayer that defined LAN parties for years.", state: 'installed', players: '2–32', tags: ['FPS', 'War'], cover: { c1: '#57534e', c2: '#1c1917', accent: '#fbbf24', mood: 'war' }, }, { - id: 'cod4mw', title: 'Call of Duty 4: Modern Warfare', size: 13.0, version: '2016.09.21', + id: 'cod4mw', title: 'Call of Duty 4: Modern Warfare', size: 13.0, version: '2016.09.21', canHostServer: true, desc: "The shooter that flipped the genre to modern combat and minted a generation of esports careers. All Ghillied Up still holds up.", state: 'local', players: '2–32', tags: ['FPS', 'Modern'], cover: { c1: '#525252', c2: '#0a0a0a', accent: '#84cc16', mood: 'tactical' }, }, { - id: 'coduo', title: 'Call of Duty: United Offensive', size: 3.8, version: '2018.09.08', + id: 'coduo', title: 'Call of Duty: United Offensive', size: 3.8, version: '2018.09.08', canHostServer: true, desc: "Expansion to the original CoD. Battle of the Bulge, Sicily, Kursk. Adds tanks, B-17 sequences, and the flamethrower nobody asked for but everybody loved.", state: 'none', players: '2–32', tags: ['FPS', 'Expansion'], cover: { c1: '#78716c', c2: '#292524', accent: '#fb923c', mood: 'war' }, @@ -76,37 +76,37 @@ const GAMES = [ cover: { c1: '#a16207', c2: '#422006', accent: '#facc15', mood: 'war' }, }, { - id: 'cs', title: 'Counter-Strike 1.6', size: 0.7, version: '2014.01.21', + id: 'cs', title: 'Counter-Strike 1.6', size: 0.7, version: '2014.01.21', canHostServer: true, desc: "The 1.6 build still everyone insists was the peak. Terrorists vs Counter-Terrorists, AWP camping, de_dust2.", state: 'installed', players: '2–32', tags: ['FPS', 'Competitive', 'LAN'], cover: { c1: '#1e40af', c2: '#0c1f3a', accent: '#fbbf24', mood: 'tactical' }, }, { - id: 'css', title: 'Counter-Strike: Source', size: 4.3, version: '2014.10.23', + id: 'css', title: 'Counter-Strike: Source', size: 4.3, version: '2014.10.23', canHostServer: true, desc: "CS reborn on the Source engine. Same maps, same rules, with physics that lets the molotovs work properly.", state: 'installed', players: '2–32', tags: ['FPS', 'Competitive'], cover: { c1: '#1d4ed8', c2: '#0c1f3a', accent: '#f59e0b', mood: 'tactical' }, }, { - id: 'cube2', title: 'Cube 2: Sauerbraten', size: 0.4, version: '2013.09.20', + id: 'cube2', title: 'Cube 2: Sauerbraten', size: 0.4, version: '2013.09.20', canHostServer: true, desc: "Open-source arena FPS with in-game level editing. Fast, free, and one of those things every LAN party always had a copy of.", state: 'none', players: '2–16', tags: ['FPS', 'Open Source'], cover: { c1: '#dc2626', c2: '#7f1d1d', accent: '#f1f5f9', mood: 'arcade' }, }, { - id: 'doom3', title: 'Doom 3', size: 2.2, version: '2012.01.31', + id: 'doom3', title: 'Doom 3', size: 2.2, version: '2012.01.31', canHostServer: true, desc: "Sci-fi horror reboot of the franchise. Mars Research Facility, demonic incursion, the shotgun that started a flashlight debate.", state: 'local', players: '2–16', tags: ['FPS', 'Horror'], cover: { c1: '#7f1d1d', c2: '#000000', accent: '#f97316', mood: 'dark' }, }, { - id: 'l4d2', title: 'Left 4 Dead 2', size: 6.5, version: '2020.08.15', + id: 'l4d2', title: 'Left 4 Dead 2', size: 6.5, version: '2020.08.15', canHostServer: true, desc: "Co-op zombie survival with the AI Director rewriting every campaign run. Bring four friends or four strangers; the chainsaw works the same.", state: 'installed', players: '1–8', tags: ['Co-op', 'FPS', 'Horror'], cover: { c1: '#15803d', c2: '#052e16', accent: '#fef08a', mood: 'horror' }, }, { - id: 'minecraft', title: 'Minecraft', size: 1.0, version: '2024.03.01', + id: 'minecraft', title: 'Minecraft', size: 1.0, version: '2024.03.01', canHostServer: true, desc: "Infinite voxel sandbox. Build, mine, survive, get blown up by a creeper. The LAN button is right there.", state: 'installed', players: '2–100', tags: ['Sandbox', 'Survival', 'LAN'], cover: { c1: '#15803d', c2: '#7c2d12', accent: '#fde68a', mood: 'sandbox' }, @@ -118,7 +118,7 @@ const GAMES = [ cover: { c1: '#ea580c', c2: '#1e293b', accent: '#22d3ee', mood: 'tech' }, }, { - id: 'quake3', title: 'Quake III Arena', size: 0.5, version: '2010.08.15', + id: 'quake3', title: 'Quake III Arena', size: 0.5, version: '2010.08.15', canHostServer: true, desc: "Arena FPS in its most distilled form. Rocket jumps, rail-gun duels, and a netcode that's still the benchmark.", state: 'downloading', progress: 0.71, speed: 12.8, peers: 3, players: '2–16', tags: ['FPS', 'Arena', 'LAN'], cover: { c1: '#7f1d1d', c2: '#0a0a0a', accent: '#fbbf24', mood: 'dark' }, @@ -130,13 +130,13 @@ const GAMES = [ cover: { c1: '#1e3a8a', c2: '#172554', accent: '#22d3ee', mood: 'scifi' }, }, { - id: 'tf2', title: 'Team Fortress 2', size: 22.0, version: '2023.06.18', + id: 'tf2', title: 'Team Fortress 2', size: 22.0, version: '2023.06.18', canHostServer: true, desc: "Class-based shooter with nine archetypes, absurd hats, and a meta with more history than most actual sports.", state: 'local', players: '2–32', tags: ['FPS', 'Class-based'], cover: { c1: '#b45309', c2: '#7c2d12', accent: '#fbbf24', mood: 'cartoon' }, }, { - id: 'ut2k4', title: 'Unreal Tournament 2004', size: 5.2, version: '2012.06.01', + id: 'ut2k4', title: 'Unreal Tournament 2004', size: 5.2, version: '2012.06.01', canHostServer: true, desc: "Arena shooter at maximum velocity. Onslaught, Assault, Bombing Run — vehicles, jump boots, the announcer screaming HEADSHOT.", state: 'none', players: '2–32', tags: ['FPS', 'Arena'], cover: { c1: '#854d0e', c2: '#422006', accent: '#fde047', mood: 'arena' }, diff --git a/design/design_reference/styles.css b/design/design_reference/styles.css index 582910c..dab3d2e 100644 --- a/design/design_reference/styles.css +++ b/design/design_reference/styles.css @@ -630,6 +630,22 @@ } .act-download:hover { background: rgba(255,255,255,0.12); border-color: var(--bd-3); } +/* Start Server — secondary "primary" action sitting next to Play. + Uses the accent as a tinted fill + border so it reads as host-action + without competing with the green Play button. */ +.act-server { + color: var(--t-1); + background: color-mix(in srgb, var(--accent) 14%, rgba(255,255,255,0.04)); + border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent); + box-shadow: inset 0 1px 0 rgba(255,255,255,0.06); +} +.act-server:hover { + background: color-mix(in srgb, var(--accent) 22%, rgba(255,255,255,0.04)); + border-color: color-mix(in srgb, var(--accent) 75%, transparent); + filter: none; +} +.act-server svg { color: var(--accent); } + /* ─── Download progress (in place of action button when state === 'downloading') ─── */ .dl { position: relative; @@ -1135,3 +1151,35 @@ color: white; box-shadow: 0 2px 8px -2px rgba(0,0,0,0.4), inset 0 1px 0 rgba(255,255,255,0.18); } + +/* ─── Settings: text input ─── */ +.settings-text { + display: inline-flex; + align-items: center; + background: var(--bg-3); + border: 1px solid var(--bd-1); + border-radius: 8px; + padding: 0 12px; + height: 36px; + width: 220px; + transition: border-color .15s, box-shadow .15s, background .15s; +} +.settings-text:focus-within { + background: var(--bg-2); + border-color: var(--accent, #3b82f6); + box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent, #3b82f6) 22%, transparent); +} +.settings-text input { + flex: 1; min-width: 0; + background: transparent; + border: 0; outline: 0; + color: var(--t-1); + font: inherit; + font-size: 13.5px; + font-weight: 600; + letter-spacing: 0.1px; +} +.settings-text input::placeholder { + color: var(--t-3); + font-weight: 500; +}