docs: update launcher design for profile and server actions
Document the profile settings added to the launcher design and the new Start Server detail action. The settings contract now includes a persisted username and language choice, and the game detail overlay shows Start Server only for installed games that can host a dedicated server. The reference mock now includes the matching Profile controls, a server icon, server-capable sample catalog entries, and the updated detail/settings artboards so implementation can follow the selected design direction. Test Plan: - git diff --cached --check Refs: design/README.md
This commit is contained in:
@@ -9,6 +9,7 @@ const { useState, useMemo, useRef, useEffect } = React;
|
||||
const Icon = {
|
||||
search: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" {...p}><circle cx="7" cy="7" r="5"/><path d="m13.5 13.5-3-3"/></svg>,
|
||||
play: (p) => <svg viewBox="0 0 16 16" width="12" height="12" fill="currentColor" {...p}><path d="M4 2.5v11l10-5.5z"/></svg>,
|
||||
server: (p) => <svg viewBox="0 0 16 16" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}><rect x="2" y="3" width="12" height="4.5" rx="1"/><rect x="2" y="8.5" width="12" height="4.5" rx="1"/><circle cx="4.6" cy="5.25" r=".55" fill="currentColor" stroke="none"/><circle cx="4.6" cy="10.75" r=".55" fill="currentColor" stroke="none"/><path d="M7 5.25h4.5M7 10.75h4.5"/></svg>,
|
||||
install:(p) => <svg viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M8 2v8"/><path d="m4.5 7 3.5 3.5L11.5 7"/><path d="M2.5 12.5h11"/></svg>,
|
||||
download:(p)=> <svg viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M8 2v8"/><path d="m4.5 7 3.5 3.5L11.5 7"/><path d="M2.5 13.5h11"/></svg>,
|
||||
folder: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" {...p}><path d="M1.75 3.75v8.5a1 1 0 0 0 1 1h10.5a1 1 0 0 0 1-1v-7a1 1 0 0 0-1-1H7.5L6 2.75H2.75a1 1 0 0 0-1 1z"/></svg>,
|
||||
@@ -435,6 +436,13 @@ function GameDetailModal({ game, accent, onClose }) {
|
||||
<p className="modal-desc">{game.desc}</p>
|
||||
<div className="modal-actions">
|
||||
<ActionButton state={game.state} accent={accent} size="lg" game={game}/>
|
||||
{game.canHostServer && game.state === 'installed' && (
|
||||
<button className="act-btn act-lg act-server"
|
||||
style={{ '--accent': accent }}
|
||||
onClick={(e) => e.stopPropagation()}>
|
||||
<Icon.server/><span>Start Server</span>
|
||||
</button>
|
||||
)}
|
||||
{game.state === 'installed' && (
|
||||
<button className="ghost-btn ghost-danger"><Icon.trash/><span>Uninstall</span></button>
|
||||
)}
|
||||
@@ -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 (
|
||||
<div className="settings-text" style={{ '--accent': accent }}>
|
||||
<input type="text"
|
||||
value={value || ''}
|
||||
placeholder={placeholder}
|
||||
maxLength={maxLength}
|
||||
spellCheck={false}
|
||||
onChange={(e) => onChange(e.target.value)}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SettingsRow({ label, hint, children }) {
|
||||
return (
|
||||
<div className="settings-row">
|
||||
@@ -524,6 +546,21 @@ function SettingsDialog({ settings, onChange, onClose }) {
|
||||
<button className="modal-close settings-close" onClick={onClose} aria-label="Close"><Icon.close/></button>
|
||||
</div>
|
||||
<div className="settings-body">
|
||||
<div className="settings-section">
|
||||
<div className="settings-section-title">Profile</div>
|
||||
<SettingsRow label="Username" hint="Shown to other players on the LAN">
|
||||
<SettingsTextInput value={settings.username}
|
||||
placeholder="Enter a username"
|
||||
onChange={(v) => onChange('username', v)}
|
||||
accent={settings.accent}/>
|
||||
</SettingsRow>
|
||||
<SettingsRow label="Language" hint="Interface language">
|
||||
<SegmentedRadio value={settings.language || 'en'}
|
||||
options={SETTING_OPTIONS.language}
|
||||
onChange={(v) => onChange('language', v)}
|
||||
accent={settings.accent}/>
|
||||
</SettingsRow>
|
||||
</div>
|
||||
<div className="settings-section">
|
||||
<div className="settings-section-title">Appearance</div>
|
||||
<SettingsRow label="Accent color" hint="Used for primary actions and highlights">
|
||||
|
||||
Reference in New Issue
Block a user