Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 4 additions & 69 deletions frontend/apps/rd-console/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import { Session } from './lib/session.svelte.js';
import ContextHeader from './ContextHeader.svelte';
import Breadcrumbs from './Breadcrumbs.svelte';
import Brand from './Brand.svelte';
import HomeHub from './screens/HomeHub.svelte';
import EventPicker from './screens/EventPicker.svelte';
import TimersPage from './screens/TimersPage.svelte';
Expand Down Expand Up @@ -266,23 +267,9 @@
<div class="gridfpv-root gridfpv-dense app">
<aside class="sidebar">
<!-- The brand is the home root from inside the workspace (#118): it leaves the event and
returns to the hub, mirroring the breadcrumb's "Home" crumb. -->
<button type="button" class="brand" onclick={goToHubFromWorkspace} title="Home — GridFPV hub">
<svg class="brand-mark" viewBox="20 20 60 60" role="img" aria-label="GridFPV">
<path
d="M71 33 H40 Q31 33 31 42 V58 Q31 67 40 67 H60 Q69 67 69 58 V51 H53"
fill="none"
stroke="var(--gf-brand-500)"
stroke-width="10"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<span class="wordmark">
<span class="name">Grid<span class="brand-fpv">FPV</span></span>
<span class="sub">RD Console</span>
</span>
</button>
returns to the hub, mirroring the breadcrumb's "Home" crumb. Shared with the
directory pages via Brand.svelte so home looks identical everywhere. -->
<Brand onclick={goToHubFromWorkspace} />

<nav aria-label="Screens">
{#each SCREENS as s (s.id)}
Expand Down Expand Up @@ -492,54 +479,6 @@
top: 0;
height: 100vh;
}
.brand {
display: flex;
align-items: center;
gap: var(--gf-space-3);
padding: var(--gf-space-2);
margin: calc(-1 * var(--gf-space-2));
border: none;
background: transparent;
color: inherit;
font-family: inherit;
text-align: left;
cursor: pointer;
border-radius: var(--gf-radius-sm);
transition: background var(--gf-motion-fast) var(--gf-ease-out);
}
.brand:hover {
background: var(--gf-elevated);
}
.brand:focus-visible {
outline: none;
box-shadow: var(--gf-focus-ring);
}
.brand-mark {
display: block;
flex-shrink: 0;
width: 30px;
height: 30px;
/* The simplified inline monogram sits directly on the dark surface — no tile. */
}
.wordmark {
display: flex;
flex-direction: column;
line-height: 1.1;
font-weight: var(--gf-font-weight-bold);
font-size: var(--gf-font-size-md);
letter-spacing: var(--gf-tracking-tight);
}
.wordmark .brand-fpv {
color: var(--gf-brand-500);
}
.wordmark .sub {
font-size: var(--gf-font-size-2xs);
font-weight: var(--gf-font-weight-medium);
text-transform: uppercase;
letter-spacing: var(--gf-tracking-caps);
color: var(--gf-text-muted);
}

nav {
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -751,15 +690,11 @@
.app {
grid-template-columns: 4rem 1fr;
}
.wordmark,
.nav-label,
kbd,
.base {
display: none;
}
.brand {
justify-content: center;
}
.nav-item {
justify-content: center;
}
Expand Down
97 changes: 97 additions & 0 deletions frontend/apps/rd-console/src/Brand.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<script lang="ts">
/**
* Brand — the GridFPV monogram + wordmark as the app's **home root** (#118).
*
* One shared rendering of the top-left brand button: the event workspace's sidebar and the
* app-level directory pages (Pilots / Classes / Events / Timers) all mount this, so leaving
* for the home hub looks and behaves identically everywhere. The markup/styles were lifted
* verbatim from the workspace sidebar (App.svelte) when the directory pages gained the brand.
*/
let {
onclick,
sub = 'RD Console'
}: {
/** Navigate home (the hub) — the button's only action. */
onclick: () => void;
/** The small caps sub-line under the wordmark. */
sub?: string;
} = $props();
</script>

<button type="button" class="brand" {onclick} title="Home — GridFPV hub">
<svg class="brand-mark" viewBox="20 20 60 60" role="img" aria-label="GridFPV">
<path
d="M71 33 H40 Q31 33 31 42 V58 Q31 67 40 67 H60 Q69 67 69 58 V51 H53"
fill="none"
stroke="var(--gf-brand-500)"
stroke-width="10"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<span class="wordmark">
<span class="name">Grid<span class="brand-fpv">FPV</span></span>
<span class="sub">{sub}</span>
</span>
</button>

<style>
.brand {
display: flex;
align-items: center;
gap: var(--gf-space-3);
padding: var(--gf-space-2);
margin: calc(-1 * var(--gf-space-2));
border: none;
background: transparent;
color: inherit;
font-family: inherit;
text-align: left;
cursor: pointer;
border-radius: var(--gf-radius-sm);
transition: background var(--gf-motion-fast) var(--gf-ease-out);
}
.brand:hover {
background: var(--gf-elevated);
}
.brand:focus-visible {
outline: none;
box-shadow: var(--gf-focus-ring);
}
.brand-mark {
display: block;
flex-shrink: 0;
width: 30px;
height: 30px;
/* The simplified inline monogram sits directly on the dark surface — no tile. */
}
.wordmark {
display: flex;
flex-direction: column;
line-height: 1.1;
font-weight: var(--gf-font-weight-bold);
font-size: var(--gf-font-size-md);
letter-spacing: var(--gf-tracking-tight);
}
.wordmark .brand-fpv {
color: var(--gf-brand-500);
}
.wordmark .sub {
font-size: var(--gf-font-size-2xs);
font-weight: var(--gf-font-weight-medium);
text-transform: uppercase;
letter-spacing: var(--gf-tracking-caps);
color: var(--gf-text-muted);
}

/* Collapse with the workspace sidebar on narrow viewports: mark only, centered (the same
rule that lived in App.svelte when the brand was inlined there). */
@media (max-width: 60rem) {
.wordmark {
display: none;
}
.brand {
justify-content: center;
}
}
</style>
8 changes: 8 additions & 0 deletions frontend/apps/rd-console/src/screens/ClassesPage.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
*/
import { Button, Card } from '@gridfpv/components';
import type { Session } from '../lib/session.svelte.js';
import Brand from '../Brand.svelte';
import Breadcrumbs from '../Breadcrumbs.svelte';
import ClassManager from './ClassManager.svelte';

Expand All @@ -26,6 +27,9 @@

<div class="page">
<div class="page-inner">
<!-- The brand is the home root here too (#118): the directory pages carry the same
top-left GridFPV mark as the workspace sidebar, leaving for the hub. -->
<div class="brand-row"><Brand onclick={onhome} /></div>
<Breadcrumbs crumbs={[{ label: 'Home', onclick: onhome }, { label: 'Classes' }]} />

<header class="page-head">
Expand All @@ -49,6 +53,10 @@
</div>

<style>
.brand-row {
margin-bottom: var(--gf-space-4);
}

.page {
min-height: 100vh;
padding: var(--gf-space-6) var(--gf-space-8) var(--gf-space-8);
Expand Down
8 changes: 8 additions & 0 deletions frontend/apps/rd-console/src/screens/EventPicker.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import type { EventMeta } from '@gridfpv/types';
import type { Session, CreateEventFields } from '../lib/session.svelte.js';
import { PRACTICE_EVENT_ID } from '../lib/session.svelte.js';
import Brand from '../Brand.svelte';
import Breadcrumbs from '../Breadcrumbs.svelte';

let {
Expand Down Expand Up @@ -229,6 +230,9 @@

<div class="picker">
<div class="picker-inner">
<!-- The brand is the home root here too (#118): the directory pages carry the same
top-left GridFPV mark as the workspace sidebar, leaving for the hub. -->
<div class="brand-row"><Brand onclick={onhome} /></div>
<Breadcrumbs crumbs={[{ label: 'Home', onclick: onhome }, { label: 'Events' }]} />

<header class="head">
Expand Down Expand Up @@ -419,6 +423,10 @@
</Dialog>

<style>
.brand-row {
margin-bottom: var(--gf-space-4);
}

.picker {
display: grid;
place-items: start center;
Expand Down
8 changes: 8 additions & 0 deletions frontend/apps/rd-console/src/screens/PilotsPage.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
*/
import { Button, Card } from '@gridfpv/components';
import type { Session } from '../lib/session.svelte.js';
import Brand from '../Brand.svelte';
import Breadcrumbs from '../Breadcrumbs.svelte';
import PilotManager from './PilotManager.svelte';

Expand All @@ -25,6 +26,9 @@

<div class="page">
<div class="page-inner">
<!-- The brand is the home root here too (#118): the directory pages carry the same
top-left GridFPV mark as the workspace sidebar, leaving for the hub. -->
<div class="brand-row"><Brand onclick={onhome} /></div>
<Breadcrumbs crumbs={[{ label: 'Home', onclick: onhome }, { label: 'Pilots' }]} />

<header class="page-head">
Expand All @@ -48,6 +52,10 @@
</div>

<style>
.brand-row {
margin-bottom: var(--gf-space-4);
}

.page {
min-height: 100vh;
padding: var(--gf-space-6) var(--gf-space-8) var(--gf-space-8);
Expand Down
8 changes: 8 additions & 0 deletions frontend/apps/rd-console/src/screens/TimersPage.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/
import { Button, Card } from '@gridfpv/components';
import type { Session } from '../lib/session.svelte.js';
import Brand from '../Brand.svelte';
import Breadcrumbs from '../Breadcrumbs.svelte';
import TimerManager from './TimerManager.svelte';

Expand All @@ -28,6 +29,9 @@

<div class="page">
<div class="page-inner">
<!-- The brand is the home root here too (#118): the directory pages carry the same
top-left GridFPV mark as the workspace sidebar, leaving for the hub. -->
<div class="brand-row"><Brand onclick={onhome} /></div>
<Breadcrumbs crumbs={[{ label: 'Home', onclick: onhome }, { label: 'Timers' }]} />

<header class="page-head">
Expand All @@ -50,6 +54,10 @@
</div>

<style>
.brand-row {
margin-bottom: var(--gf-space-4);
}

.page {
min-height: 100vh;
padding: var(--gf-space-6) var(--gf-space-8) var(--gf-space-8);
Expand Down
16 changes: 16 additions & 0 deletions frontend/apps/rd-console/tests/EventPicker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,22 @@ afterEach(() => {
vi.unstubAllGlobals();
});

describe('EventPicker — the brand home root (#118)', () => {
it('renders the GridFPV brand top-left and clicking it goes home', async () => {
const onhome = vi.fn();
const { session } = makeTestSession({
noEnter: true,
listEventsImpl: vi.fn(async () => [EVENT]),
getActiveEventImpl: vi.fn(async () => ({ event: null }))
});
render(EventPicker, { session, onhome });
// The button's accessible name comes from its wordmark content; the title is the tooltip.
const brand = await screen.findByTitle('Home — GridFPV hub');
await fireEvent.click(brand);
expect(onhome).toHaveBeenCalledTimes(1);
});
});

describe('EventPicker — selectable event-name box in the delete dialog', () => {
it('renders the event name in a user-select:all box', async () => {
const dialog = await openDeleteDialog();
Expand Down