mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 03:14:00 +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
|
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
|
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
|
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,
|
other tabs are plain labels. A first-time visitor on a desktop finds Display
|
||||||
Escape inside a panel returns focus to its tab, and saved preferences and
|
open, so the strip reads as launchers; a stored choice still wins. The tabs
|
||||||
share links are unchanged. Phones keep their sheet shell (gekh, #1425).
|
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
|
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.
|
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
|
## Phone layout
|
||||||
|
|
||||||
At 720px and narrower, `src/ui/mobileShell.js` keeps the globe full screen and
|
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
|
> Missions · Environmental · Explore manually**. No layer and no optional API
|
||||||
> call happens until a tile is clicked. The right-hand DISPLAY rail
|
> call happens until a tile is clicked. The right-hand DISPLAY rail
|
||||||
> (`pp-toggles`) now starts **collapsed** on a first run rather than expanded —
|
> (`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
|
> **The ENVIRONMENTAL tile is quakes AND fires** — live USGS earthquakes plus
|
||||||
> NASA FIRMS active fires (`layerIds: ['earthquakes', 'local-firms']`), with the
|
> 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\)/);
|
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(
|
const ui = fs.readFileSync(
|
||||||
new URL('./ui/panelPositionControls.js', import.meta.url),
|
new URL('./ui/panelPositionControls.js', import.meta.url),
|
||||||
'utf8',
|
'utf8',
|
||||||
);
|
);
|
||||||
// The rail opened by default to advertise HUD / DETECT / 3D. Those default ON
|
// The right rail is a strip of label tabs: with every panel closed nothing
|
||||||
// now, so it was opening to offer controls for things already happening —
|
// shows that the labels are launchers, so DISPLAY opens on a first desktop
|
||||||
// while competing with the mission card for the one first impression there is.
|
// run to demonstrate the strip. Phones keep their sheets and a share link
|
||||||
|
// decides for itself.
|
||||||
assert.match(
|
assert.match(
|
||||||
ui,
|
ui,
|
||||||
/if \(panelId === 'pp-toggles' && stored === null\) collapsed = true;/,
|
/if \(panelId === 'pp-toggles' && stored === null\)\s+collapsed = !allowStored \|\| isPhoneViewport\(\);/,
|
||||||
'first run must leave DISPLAY collapsed',
|
'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
|
// 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.
|
// opened the rail keeps it open, and one who closed it keeps it closed.
|
||||||
const block = ui.slice(
|
const block = ui.slice(
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ const RETIRED_PANEL_KEY_PREFIXES = [
|
|||||||
`godsEyeView.${PANEL_POSITION_STORAGE_VERSION}.panelPos.`,
|
`godsEyeView.${PANEL_POSITION_STORAGE_VERSION}.panelPos.`,
|
||||||
`godsEyeView.${PANEL_POSITION_STORAGE_VERSION}.panelFloatHintShown`,
|
`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 {
|
export class PanelPositionControls {
|
||||||
constructor({ syncPanelCollapseButton, showToast }) {
|
constructor({ syncPanelCollapseButton, showToast }) {
|
||||||
this._syncPanelCollapseButton = syncPanelCollapseButton;
|
this._syncPanelCollapseButton = syncPanelCollapseButton;
|
||||||
@@ -66,15 +70,15 @@ export class PanelPositionControls {
|
|||||||
// storage unavailable
|
// storage unavailable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// DISPLAY starts COLLAPSED for a first-time visitor, then respects the
|
// DISPLAY starts EXPANDED for a first-time visitor on a desktop viewport,
|
||||||
// user's persisted choice like every other panel.
|
// 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
|
||||||
// It used to start expanded, to advertise the HUD / DETECT / 3D toggles.
|
// first panel opens to demonstrate the strip. Phones keep it collapsed
|
||||||
// That reason expired when those became ON by default: the rail now opens
|
// (their sheets present the rail), and a share link decides for itself.
|
||||||
// to offer controls for things already happening, while competing with the
|
// A stored choice still wins in both directions, so anyone who closes it
|
||||||
// first-run mission card for the one first impression there is. A stored
|
// keeps it closed.
|
||||||
// choice still wins in both directions, so anyone who opens it keeps it.
|
if (panelId === 'pp-toggles' && stored === null)
|
||||||
if (panelId === 'pp-toggles' && stored === null) collapsed = true;
|
collapsed = !allowStored || isPhoneViewport();
|
||||||
panelEl.classList.toggle('collapsed', collapsed);
|
panelEl.classList.toggle('collapsed', collapsed);
|
||||||
// Bodies that expand themselves on first appearance must not override a
|
// Bodies that expand themselves on first appearance must not override a
|
||||||
// choice the user (or a share link) already made.
|
// 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);
|
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]) {
|
for (const collapsed of [true, false]) {
|
||||||
test(`right layout ignores a hidden ${collapsed ? 'collapsed' : 'expanded'} panel`, () => {
|
test(`right layout ignores a hidden ${collapsed ? 'collapsed' : 'expanded'} panel`, () => {
|
||||||
const baseline = fixture('right');
|
const baseline = fixture('right');
|
||||||
|
|||||||
@@ -75,10 +75,12 @@ export function layoutRightPanelRail({
|
|||||||
(hiddenByRail(panel) || getComputedStyle(panel).display !== 'none'),
|
(hiddenByRail(panel) || getComputedStyle(panel).display !== 'none'),
|
||||||
);
|
);
|
||||||
// One expanded owner: the latest explicit disclosure, else the panel that
|
// One expanded owner: the latest explicit disclosure, else the panel that
|
||||||
// holds keyboard focus, else the first. Peers are presentation-collapsed
|
// holds keyboard focus, else one the visitor or a share link chose over one
|
||||||
// without overwriting saved panel preferences; the marker is distinct from
|
// open only by default (panelPositionControls.js marks each), else the
|
||||||
// the left rail's space-based collapse. Phones present the rail as tab
|
// first. Peers are presentation-collapsed without overwriting saved panel
|
||||||
// sheets that show several panels at once, so they get every panel back.
|
// 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;
|
const isMobile = windowRef.matchMedia('(max-width: 720px)').matches;
|
||||||
if (!isMobile) {
|
if (!isMobile) {
|
||||||
const expanded = panels.filter(
|
const expanded = panels.filter(
|
||||||
@@ -87,6 +89,9 @@ export function layoutRightPanelRail({
|
|||||||
const owner =
|
const owner =
|
||||||
expanded.find((panel) => panel.id === preferredPanelId) ||
|
expanded.find((panel) => panel.id === preferredPanelId) ||
|
||||||
expanded.find((panel) => panel.contains(documentRef?.activeElement)) ||
|
expanded.find((panel) => panel.contains(documentRef?.activeElement)) ||
|
||||||
|
expanded.find(
|
||||||
|
(panel) => panel.dataset?.collapsedPreference !== 'default',
|
||||||
|
) ||
|
||||||
expanded[0];
|
expanded[0];
|
||||||
for (const panel of expanded) {
|
for (const panel of expanded) {
|
||||||
if (panel === owner) continue;
|
if (panel === owner) continue;
|
||||||
|
|||||||
Reference in new issue
Block a user