mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
ui(mapillary): drop the keyless empty state
The panel no longer shows a separate keyless call-to-action; the fieldset gate and the layer-row tooltip already cover the missing-key case. The planner flag is tri-state (null until status answers). Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
1 parent
10a00873ca
commit
7f65a32877
11 files changed
+31
-60
No files matched your search
@@ -330,6 +330,13 @@ _The Space Missions layer replaying a Falcon 9 ascent — labeled `RECONSTRUCTED
|
||||
|
||||
> Needs a free **Mapillary client token**; the plain-English query box additionally needs an **Anthropic key**. Both go in POWER UP.
|
||||
|
||||
**Getting the keys**
|
||||
|
||||
1. **Mapillary client token (required, free).** Sign in at [mapillary.com/dashboard/developers](https://www.mapillary.com/dashboard/developers), choose **Register application**, give it any name and website, tick **Read** access, and copy the **Client Token** (it starts with `MLY|`). Paste it into **POWER UP → Mapillary**, or set `MAPILLARY_CLIENT_TOKEN` in `.env` and restart `npm run dev`.
|
||||
2. **Anthropic key (optional).** Create one at [console.anthropic.com](https://console.anthropic.com/settings/keys) and add it in **POWER UP → Anthropic** or as `ANTHROPIC_API_KEY` in `.env`. It stays on the server; only the plain-English query box uses it.
|
||||
|
||||
Without the Mapillary token the STREET LEVEL panel reads **KEY REQUIRED** and its controls stay greyed out; without the Anthropic key everything except the query box works.
|
||||
|
||||
Turn on **Street Level (Mapillary)** under DATA LAYERS → Cameras, or open the STREET LEVEL strip in the right rail. Mapillary's coverage draws as ground-hugging lines (recent captures bright green, older ones dim, 360° panoramas magenta); click a line for its image cones and a cone to open the photo in the embedded MapillaryJS viewer. **EXPAND** grows the viewer to most of the screen and **CAMERA FOLLOWS VIEW** drives the globe camera from the panorama. With an Anthropic key, type a request such as _"stop signs in downtown Detroit"_ and the matching detections stream onto the globe tile by tile with Mapillary's own icons; click one to see the photo with the detection outlined. Mapillary blurs faces and licence plates before publishing imagery, and the globe credits "© Mapillary contributors, CC BY-SA 4.0" while the layer is on.
|
||||
|
||||
## 🎖️ Field Missions
|
||||
|
||||
@@ -2697,7 +2697,8 @@ its criteria cannot be silently ignored.
|
||||
|
||||
Street Level (Mapillary) lives in the right context rail next to CCTV as an
|
||||
ordinary collapsible panel and starts collapsed. Without `MAPILLARY_CLIENT_TOKEN`
|
||||
the panel shows a gated empty state naming the variable; with it, coverage
|
||||
the header status reads KEY REQUIRED and the controls stay disabled (key setup
|
||||
is documented in the README's Street Level section); with it, coverage
|
||||
draws as ground-clamped sequence lines (z0–5 overview points from orbit,
|
||||
z11–14 sequences below 60 km) and the proxy strips the unused `image` point
|
||||
layer from z14 tiles in transit (12 MB → ~80 KB). The plain-English query box
|
||||
|
||||
@@ -108,7 +108,7 @@ async function main() {
|
||||
classes: [...el.classList],
|
||||
width: Math.round(box.width),
|
||||
right: Math.round(box.right),
|
||||
keyless: !document.getElementById('mly-keyless').hidden,
|
||||
status: document.getElementById('mly-status').textContent,
|
||||
controlsDisabled: document.getElementById('mly-controls').disabled,
|
||||
bodyDisplay: getComputedStyle(document.getElementById('mly-body'))
|
||||
.display,
|
||||
@@ -152,7 +152,7 @@ async function main() {
|
||||
);
|
||||
if (!status.configured) {
|
||||
await step(
|
||||
'keyless install gates the controls and names the token',
|
||||
'keyless install gates the controls and reports KEY REQUIRED',
|
||||
async () => {
|
||||
await page.evaluate(() =>
|
||||
window.__godsEyeView.dataManager.setEnabled('mapillary', true, {
|
||||
@@ -161,14 +161,8 @@ async function main() {
|
||||
);
|
||||
await sleep(800);
|
||||
const info = await panel();
|
||||
assert.equal(info.keyless, true);
|
||||
assert.equal(info.controlsDisabled, true);
|
||||
assert.match(
|
||||
await page.evaluate(
|
||||
() => document.getElementById('mly-keyless').textContent,
|
||||
),
|
||||
/MAPILLARY_CLIENT_TOKEN/,
|
||||
);
|
||||
assert.equal(info.status, 'KEY REQUIRED');
|
||||
},
|
||||
);
|
||||
console.log(
|
||||
@@ -217,7 +211,7 @@ async function main() {
|
||||
{ timeout: 90_000 },
|
||||
);
|
||||
const info = await panel();
|
||||
assert.equal(info.keyless, false);
|
||||
assert.equal(info.controlsDisabled, false);
|
||||
// Cesium paints on-screen credits a frame or two after they register.
|
||||
await page.waitForFunction(
|
||||
() => document.body.innerHTML.includes('Mapillary</a> contributors'),
|
||||
|
||||
@@ -180,7 +180,8 @@ export function createMapillaryLayer({ source, services = {} }) {
|
||||
return {
|
||||
enabled: state.enabled,
|
||||
keyRequired: state.keyRequired,
|
||||
planner: state.status?.planner === true,
|
||||
// null until the status call answers (the layer initialises lazily).
|
||||
planner: state.status ? state.status.planner === true : null,
|
||||
plannerModel: state.status?.plannerModel || null,
|
||||
coverage: {
|
||||
zoom: state.coverage.zoom,
|
||||
|
||||
@@ -63,9 +63,9 @@ test('panel styles stay inside GEV conventions: no !important, no fixed panel',
|
||||
assert.deepEqual(fixed, ['.mly-viewer-wrap-expanded']);
|
||||
});
|
||||
|
||||
test('keyless and no-planner states are real, gated UI rather than dead buttons', () => {
|
||||
assert.match(html, /id="mly-keyless"[^>]*hidden/);
|
||||
assert.match(html, /MAPILLARY_CLIENT_TOKEN/);
|
||||
test('keyless and no-planner states gate the controls rather than leave dead buttons', () => {
|
||||
// The key requirement is documented (README, .env.example), not repeated in the panel.
|
||||
assert.doesNotMatch(html, /mly-keyless/);
|
||||
assert.match(html, /<fieldset id="mly-controls"/);
|
||||
assert.match(html, /<ul id="mly-legend"/);
|
||||
assert.match(html, /id="mly-error"[^>]*role="alert"/);
|
||||
|
||||
@@ -900,7 +900,6 @@ export class StyleManager extends ShellFacade {
|
||||
}),
|
||||
setPanelCollapsed: (collapsed, options) =>
|
||||
this.setPanelCollapsed('mapillary-panel', collapsed, options),
|
||||
openKeySetup: () => document.getElementById('key-setup-chip')?.click(),
|
||||
showToast: (message) => this._showToast(message),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -36,8 +36,6 @@ export class MapillaryControls {
|
||||
const byId = (id) => this.root?.querySelector(`#${id}`) || null;
|
||||
return {
|
||||
status: byId('mly-status'),
|
||||
keyless: byId('mly-keyless'),
|
||||
keylessBtn: byId('mly-keyless-btn'),
|
||||
controls: byId('mly-controls'),
|
||||
enableBtn: byId('mly-enable-btn'),
|
||||
lookBtn: byId('mly-look-btn'),
|
||||
@@ -82,7 +80,6 @@ export class MapillaryControls {
|
||||
if (!this.root) return;
|
||||
this.mapillary.attachViewerHost?.(el.viewer);
|
||||
|
||||
this.listen(el.keylessBtn, 'click', () => this.actions.openKeySetup?.());
|
||||
this.listen(el.enableBtn, 'click', () => this._toggleEnabled());
|
||||
this.listen(el.lookBtn, 'click', async () => {
|
||||
if (!(await this._ensureEnabled())) return;
|
||||
@@ -306,7 +303,7 @@ export class MapillaryControls {
|
||||
const view = presentMapillaryPanel(state);
|
||||
this._view = view;
|
||||
this._renderHeader(view);
|
||||
this._renderEmptyState(view);
|
||||
this._renderGate(view);
|
||||
this._renderQuery(view);
|
||||
this._renderResults(view);
|
||||
this._renderFilters(view);
|
||||
@@ -331,9 +328,8 @@ export class MapillaryControls {
|
||||
}
|
||||
}
|
||||
|
||||
_renderEmptyState(view) {
|
||||
_renderGate(view) {
|
||||
const el = this._elements;
|
||||
if (el.keyless) el.keyless.hidden = !view.keyless.visible;
|
||||
if (el.controls) el.controls.disabled = view.controlsDisabled;
|
||||
}
|
||||
|
||||
|
||||
@@ -153,7 +153,9 @@ function presentMeta(state) {
|
||||
export function presentMapillaryPanel(state) {
|
||||
const enabled = state.enabled === true;
|
||||
const keyRequired = state.keyRequired === true;
|
||||
const planner = state.planner === true;
|
||||
// Unknown (status not fetched yet) reads as available: the server answers
|
||||
// a missing key with its own error, and the panel must not claim one is missing.
|
||||
const planner = state.planner !== false;
|
||||
const busy = state.query.busy === true;
|
||||
const error = state.query.error || state.street.error || null;
|
||||
let answer = state.query.answer || '';
|
||||
@@ -167,10 +169,6 @@ export function presentMapillaryPanel(state) {
|
||||
enabled,
|
||||
keyRequired,
|
||||
status: presentStatus(state),
|
||||
keyless: {
|
||||
visible: keyRequired,
|
||||
text: 'Street Level needs a free Mapillary client token (MAPILLARY_CLIENT_TOKEN). Coverage, photos and queries stay off until one is added.',
|
||||
},
|
||||
controlsDisabled: keyRequired,
|
||||
enableButton: {
|
||||
text: enabled ? 'STREET LEVEL ON' : 'STREET LEVEL OFF',
|
||||
|
||||
@@ -86,10 +86,8 @@ function deepMerge(target, source) {
|
||||
return out;
|
||||
}
|
||||
|
||||
test('a missing Mapillary token gates every control and names the variable', () => {
|
||||
test('a missing Mapillary token gates every control and flags KEY REQUIRED', () => {
|
||||
const view = presentMapillaryPanel(snapshot({ keyRequired: true }));
|
||||
assert.equal(view.keyless.visible, true);
|
||||
assert.match(view.keyless.text, /MAPILLARY_CLIENT_TOKEN/);
|
||||
assert.equal(view.controlsDisabled, true);
|
||||
assert.deepEqual(view.status, { text: 'KEY REQUIRED', tone: 'warn' });
|
||||
});
|
||||
@@ -103,10 +101,16 @@ test('without an Anthropic key the query box explains itself and stays disabled'
|
||||
assert.equal(view.query.hintWarn, true);
|
||||
assert.match(view.query.hint, /Anthropic key/);
|
||||
assert.ok(view.query.placeholder.length < 40, 'placeholder fits the field');
|
||||
assert.equal(view.keyless.visible, false);
|
||||
assert.equal(view.controlsDisabled, false);
|
||||
});
|
||||
|
||||
test('before the status call answers the query box does not claim a missing key', () => {
|
||||
const view = presentMapillaryPanel(snapshot({ planner: null }));
|
||||
assert.equal(view.query.inputDisabled, false);
|
||||
assert.equal(view.query.hintWarn, false);
|
||||
assert.doesNotMatch(view.query.placeholder, /Anthropic/);
|
||||
});
|
||||
|
||||
test('a running query turns ASK into STOP and shows indeterminate progress per stage', () => {
|
||||
const planning = presentMapillaryPanel(
|
||||
snapshot({ enabled: true, query: { busy: true, stage: 'planning' } }),
|
||||
|
||||
@@ -110,31 +110,6 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ── Keyless empty state ─────────────────────────────────────────────────── */
|
||||
.mly-keyless {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid rgba(255, 179, 0, 0.45);
|
||||
border-radius: 9px;
|
||||
background: rgba(255, 179, 0, 0.06);
|
||||
}
|
||||
|
||||
.mly-keyless-text {
|
||||
margin: 0;
|
||||
font-family: var(--font-sans);
|
||||
font-size: 0.72rem;
|
||||
line-height: 1.4;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.mly-keyless-text code {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.66rem;
|
||||
color: #ffb300;
|
||||
}
|
||||
|
||||
/* ── Query ───────────────────────────────────────────────────────────────── */
|
||||
.mly-query {
|
||||
display: flex;
|
||||
|
||||
@@ -85,10 +85,6 @@
|
||||
<button class="panel-collapse-btn" data-collapse-target="mapillary-panel" title="Expand panel">◀</button>
|
||||
</div>
|
||||
<div id="mly-body" class="mly-body">
|
||||
<div id="mly-keyless" class="mly-keyless" hidden>
|
||||
<p class="mly-keyless-text">Street Level needs a free Mapillary client token (<code>MAPILLARY_CLIENT_TOKEN</code>). Coverage, photos and queries stay off until one is added.</p>
|
||||
<button id="mly-keyless-btn" class="scene-btn" type="button">ADD KEY IN POWER UP</button>
|
||||
</div>
|
||||
<fieldset id="mly-controls" class="mly-controls">
|
||||
<div class="cctv-controls">
|
||||
<button id="mly-enable-btn" class="scene-btn" type="button" aria-pressed="false" title="Turn the Street Level layer on or off">STREET LEVEL OFF</button>
|
||||
|
||||
Reference in new issue
Block a user