mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
feat(ui): open Display on a first desktop visit to the tabbed rail
With every panel closed nothing shows that the sideways labels are launchers, so Display starts expanded for a first-time visitor on a desktop viewport; phones keep it collapsed and a share link decides for itself. A stored choice still wins. When the rail picks its one owner, a panel the visitor or a share link opened outranks one open only by default, so a stored CCTV choice is not displaced by the Display default. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
1 parent
575ff3678f
commit
f383174f3e
6 files changed
+72
-23
No files matched your search
@@ -3,6 +3,8 @@
|
||||
an accordion of collapsed launcher cards: the open panel sits beside the
|
||||
strip and at most one panel is open in every desktop HUD layout. The open
|
||||
tab and its panel share one surface and one outline, like a folder tab; the
|
||||
other tabs are plain labels. The tabs are a keyboard-navigable tablist,
|
||||
Escape inside a panel returns focus to its tab, and saved preferences and
|
||||
share links are unchanged. Phones keep their sheet shell (gekh, #1425).
|
||||
other tabs are plain labels. A first-time visitor on a desktop finds Display
|
||||
open, so the strip reads as launchers; a stored choice still wins. The tabs
|
||||
are a keyboard-navigable tablist, Escape inside a panel returns focus to its
|
||||
tab, and saved preferences and share links are unchanged. Phones keep their
|
||||
sheet shell (gekh, #1425).
|
||||
+12
-1
@@ -1524,6 +1524,15 @@ open panel's minimum height, so a short panel such as Context still reaches
|
||||
its tab. A first tab level with the panel's top shares its top edge, with no
|
||||
corner between them; Cyber's strip starts below the frame's upper chamfer.
|
||||
|
||||
A first-time visitor on a desktop viewport finds Display open
|
||||
(`src/ui/panelPositionControls.js`): with every panel closed nothing shows
|
||||
that the sideways labels are launchers, so the first panel opens to
|
||||
demonstrate the strip. Phones keep it closed (their sheets present the
|
||||
rail), a share link decides for itself, and a stored choice wins either way.
|
||||
When the rail picks its one owner, a panel the visitor or a share link opened
|
||||
outranks one open only by default, so a stored CCTV choice is not displaced
|
||||
by the Display default.
|
||||
|
||||
## Phone layout
|
||||
|
||||
At 720px and narrower, `src/ui/mobileShell.js` keeps the globe full screen and
|
||||
@@ -2157,7 +2166,9 @@ fetching, trailing-24-hour filtering and partial-success caching are unchanged.
|
||||
> Missions · Environmental · Explore manually**. No layer and no optional API
|
||||
> call happens until a tile is clicked. The right-hand DISPLAY rail
|
||||
> (`pp-toggles`) now starts **collapsed** on a first run rather than expanded —
|
||||
> a stored collapse state still wins, as before.
|
||||
> a stored collapse state still wins, as before. (Superseded 2026-10-27 by the
|
||||
> right rail tabs: on a desktop viewport DISPLAY starts open again on a first
|
||||
> run; see "Right rail tabs".)
|
||||
>
|
||||
> **The ENVIRONMENTAL tile is quakes AND fires** — live USGS earthquakes plus
|
||||
> NASA FIRMS active fires (`layerIds: ['earthquakes', 'local-firms']`), with the
|
||||
|
||||
@@ -981,19 +981,21 @@ test('the launcher keeps focus, restores it, and never disables the focused butt
|
||||
assert.match(keyboard, /addEventListener\('keydown', onKeyDown, true\)/);
|
||||
});
|
||||
|
||||
test('the DISPLAY rail starts collapsed on a first run, and a stored choice wins', () => {
|
||||
test('the DISPLAY rail starts open on a first desktop run, and a stored choice wins', () => {
|
||||
const ui = fs.readFileSync(
|
||||
new URL('./ui/panelPositionControls.js', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
// The rail opened by default to advertise HUD / DETECT / 3D. Those default ON
|
||||
// now, so it was opening to offer controls for things already happening —
|
||||
// while competing with the mission card for the one first impression there is.
|
||||
// The right rail is a strip of label tabs: with every panel closed nothing
|
||||
// shows that the labels are launchers, so DISPLAY opens on a first desktop
|
||||
// run to demonstrate the strip. Phones keep their sheets and a share link
|
||||
// decides for itself.
|
||||
assert.match(
|
||||
ui,
|
||||
/if \(panelId === 'pp-toggles' && stored === null\) collapsed = true;/,
|
||||
'first run must leave DISPLAY collapsed',
|
||||
/if \(panelId === 'pp-toggles' && stored === null\)\s+collapsed = !allowStored \|\| isPhoneViewport\(\);/,
|
||||
'first desktop run must open DISPLAY; phones and share links keep it closed',
|
||||
);
|
||||
assert.match(ui, /matchMedia\?\.\('\(max-width: 720px\)'\)/);
|
||||
// The first-run default may only apply when NOTHING is stored: a visitor who
|
||||
// opened the rail keeps it open, and one who closed it keeps it closed.
|
||||
const block = ui.slice(
|
||||
|
||||
@@ -13,6 +13,10 @@ const RETIRED_PANEL_KEY_PREFIXES = [
|
||||
`godsEyeView.${PANEL_POSITION_STORAGE_VERSION}.panelPos.`,
|
||||
`godsEyeView.${PANEL_POSITION_STORAGE_VERSION}.panelFloatHintShown`,
|
||||
];
|
||||
/** Phones present the rail as sheets (mobileShell.js), not as a tab strip. */
|
||||
const isPhoneViewport = () =>
|
||||
globalThis.matchMedia?.('(max-width: 720px)')?.matches === true;
|
||||
|
||||
export class PanelPositionControls {
|
||||
constructor({ syncPanelCollapseButton, showToast }) {
|
||||
this._syncPanelCollapseButton = syncPanelCollapseButton;
|
||||
@@ -66,15 +70,15 @@ export class PanelPositionControls {
|
||||
// storage unavailable
|
||||
}
|
||||
}
|
||||
// DISPLAY starts COLLAPSED for a first-time visitor, then respects the
|
||||
// user's persisted choice like every other panel.
|
||||
//
|
||||
// It used to start expanded, to advertise the HUD / DETECT / 3D toggles.
|
||||
// That reason expired when those became ON by default: the rail now opens
|
||||
// to offer controls for things already happening, while competing with the
|
||||
// first-run mission card for the one first impression there is. A stored
|
||||
// choice still wins in both directions, so anyone who opens it keeps it.
|
||||
if (panelId === 'pp-toggles' && stored === null) collapsed = true;
|
||||
// DISPLAY starts EXPANDED for a first-time visitor on a desktop viewport,
|
||||
// where the right rail is a strip of label tabs (rightRailTabs.js): with
|
||||
// every panel closed nothing shows that the labels are launchers, so the
|
||||
// first panel opens to demonstrate the strip. Phones keep it collapsed
|
||||
// (their sheets present the rail), and a share link decides for itself.
|
||||
// A stored choice still wins in both directions, so anyone who closes it
|
||||
// keeps it closed.
|
||||
if (panelId === 'pp-toggles' && stored === null)
|
||||
collapsed = !allowStored || isPhoneViewport();
|
||||
panelEl.classList.toggle('collapsed', collapsed);
|
||||
// Bodies that expand themselves on first appearance must not override a
|
||||
// choice the user (or a share link) already made.
|
||||
|
||||
@@ -295,6 +295,31 @@ test('right layout uses keyboard focus when there is no preferred panel', () =>
|
||||
assert.equal(f.second.classList.contains('collapsed'), false);
|
||||
});
|
||||
|
||||
test('right layout lets a stored or shared choice outrank a panel open only by default', () => {
|
||||
// Display opens by default on a first desktop visit; a visitor who had
|
||||
// opened CCTV keeps CCTV as the owner, whatever the rail order.
|
||||
const f = fixture('right');
|
||||
f.expand(f.first, 900);
|
||||
f.expand(f.second, 900);
|
||||
f.first.dataset.collapsedPreference = 'default';
|
||||
f.second.dataset.collapsedPreference = 'stored';
|
||||
f.run();
|
||||
assert.equal(f.first.classList.contains('rail-owner-collapsed'), true);
|
||||
assert.equal(f.second.classList.contains('collapsed'), false);
|
||||
const g = fixture('right');
|
||||
g.expand(g.first, 900);
|
||||
g.expand(g.second, 900);
|
||||
g.first.dataset.collapsedPreference = 'default';
|
||||
g.second.dataset.collapsedPreference = 'default';
|
||||
g.run();
|
||||
assert.equal(
|
||||
g.first.classList.contains('collapsed'),
|
||||
false,
|
||||
'all default: first wins',
|
||||
);
|
||||
assert.equal(g.second.classList.contains('rail-owner-collapsed'), true);
|
||||
});
|
||||
|
||||
for (const collapsed of [true, false]) {
|
||||
test(`right layout ignores a hidden ${collapsed ? 'collapsed' : 'expanded'} panel`, () => {
|
||||
const baseline = fixture('right');
|
||||
|
||||
@@ -75,10 +75,12 @@ export function layoutRightPanelRail({
|
||||
(hiddenByRail(panel) || getComputedStyle(panel).display !== 'none'),
|
||||
);
|
||||
// One expanded owner: the latest explicit disclosure, else the panel that
|
||||
// holds keyboard focus, else the first. Peers are presentation-collapsed
|
||||
// without overwriting saved panel preferences; the marker is distinct from
|
||||
// the left rail's space-based collapse. Phones present the rail as tab
|
||||
// sheets that show several panels at once, so they get every panel back.
|
||||
// holds keyboard focus, else one the visitor or a share link chose over one
|
||||
// open only by default (panelPositionControls.js marks each), else the
|
||||
// first. Peers are presentation-collapsed without overwriting saved panel
|
||||
// preferences; the marker is distinct from the left rail's space-based
|
||||
// collapse. Phones present the rail as tab sheets that show several panels
|
||||
// at once, so they get every panel back.
|
||||
const isMobile = windowRef.matchMedia('(max-width: 720px)').matches;
|
||||
if (!isMobile) {
|
||||
const expanded = panels.filter(
|
||||
@@ -87,6 +89,9 @@ export function layoutRightPanelRail({
|
||||
const owner =
|
||||
expanded.find((panel) => panel.id === preferredPanelId) ||
|
||||
expanded.find((panel) => panel.contains(documentRef?.activeElement)) ||
|
||||
expanded.find(
|
||||
(panel) => panel.dataset?.collapsedPreference !== 'default',
|
||||
) ||
|
||||
expanded[0];
|
||||
for (const panel of expanded) {
|
||||
if (panel === owner) continue;
|
||||
|
||||
Reference in new issue
Block a user