mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
Panoramax, the open federated street-level commons, registers beside Mapillary, Google Street View and KartaView. Its meta-catalogue (api.panoramax.xyz) is keyless and CORS-open, so it runs in the browser: - coverage from its vector tiles: sequence lines at z11–14 below 30 km (longest first under the per-tile cap), and above that the grid layer's density cells as binned points, not counted as sequences; - a click on a line opens that sequence's picture nearest the click (STAC search, any direction), without moving the camera, then any sequence's if it has none within 50 m; the 360° and since filters go to the lines and to the API; - cones from the z15 tiles' pictures layer around the open picture (~110 bytes a picture, against ~7.5 kB for a STAC item), re-centred when a picture opens outside them; ‹ › follow the picture's prev/next links; - the caption names the author (not an account named by an email address) and the picture's licence (StreetPose `license`), and links to the picture in the Panoramax viewer; the globe credits Panoramax. KartaView's viewer becomes the shared photoViewer.js (flat <img>, 360° on panorama.js, ‹ ›, keyboard, pinch, context loss), driven by a per-provider PhotoSources table; the request limiter moves to limiter.js. Panoramax shows the 2,048 px SD derivative and fetches the HD original only when the viewer is wide enough, once, shrunk while it decodes (never above 8,192 px). Orange #ff6f00 (Panoramax's own), off by default, share option 0.m.0_0.r.panoramax-1. The MCP panel allows the meta-catalogue and the biggest instances. Tests: policy, decode, source, viewer, provider, and a Panoramax fixture in the hermetic browser gate. Co-Authored-By: Claude Opus 5.5 <[email protected]>
155 lines
6.0 KiB
JavaScript
155 lines
6.0 KiB
JavaScript
/**
|
|
* The God's Eye View panel: an MCP Apps view (`io.modelcontextprotocol/ui`) that shows
|
|
* God's Eye View inside a conversation. The panel is a small page that runs
|
|
* the app's panel build inside itself in embed mode, loading it through the
|
|
* MCP server, and sends it each view the show_in_gods_eye_view tool returns;
|
|
* see docs/TOOLS.md.
|
|
*/
|
|
|
|
export const GLOBE_PANEL_URI = 'ui://gods-eye-view/globe';
|
|
/** Where the app's server serves its panel build (see build/panel.js). */
|
|
export const PANEL_BASE = '/panel/';
|
|
/** The script the panel runs ahead of Cesium's workers, in the build. */
|
|
export const PANEL_WORKER_PRELUDE_PATH = 'cesium/worker-prelude.js';
|
|
|
|
/**
|
|
* Sites the app's browser code loads from directly: map imagery, 3D tiles,
|
|
* terrain, fonts, and Street Level's Mapillary Graph API and image CDN (its
|
|
* coverage tiles come through the app's server), Google Street View,
|
|
* KartaView and Panoramax. Everything from the app's own server comes
|
|
* through the MCP server.
|
|
*/
|
|
const PROVIDER_ORIGINS = Object.freeze([
|
|
'https://tile.googleapis.com',
|
|
'https://maps.googleapis.com',
|
|
'https://api.cesium.com',
|
|
'https://assets.ion.cesium.com',
|
|
'https://assets.cesium.com',
|
|
'https://dev.virtualearth.net',
|
|
'https://ecn.t0.tiles.virtualearth.net',
|
|
'https://ecn.t1.tiles.virtualearth.net',
|
|
'https://ecn.t2.tiles.virtualearth.net',
|
|
'https://ecn.t3.tiles.virtualearth.net',
|
|
'https://services.arcgisonline.com',
|
|
'https://server.arcgisonline.com',
|
|
'https://tile.openstreetmap.org',
|
|
'https://tiles.openfreemap.org',
|
|
'https://gibs.earthdata.nasa.gov',
|
|
'https://fonts.googleapis.com',
|
|
'https://fonts.gstatic.com',
|
|
'https://graph.mapillary.com',
|
|
// Google Street View's tiles, controls and contributed photos (the Maps
|
|
// JavaScript API itself comes from maps.googleapis.com above).
|
|
'https://streetviewpixels-pa.googleapis.com',
|
|
'https://maps.gstatic.com',
|
|
'https://*.ggpht.com',
|
|
// Mapillary photos come from regional hosts, e.g. scontent-man2-1.xx.fbcdn.net.
|
|
'https://*.xx.fbcdn.net',
|
|
// KartaView's API, its image proxy and its photo storage (storage1…storage13).
|
|
'https://api.kartaview.org',
|
|
'https://cdn.kartaview.org',
|
|
'https://*.openstreetcam.org',
|
|
// Panoramax's meta-catalogue, and the federated instances holding most of
|
|
// its pictures (a picture on another instance shows only in the app).
|
|
'https://api.panoramax.xyz',
|
|
'https://panoramax.openstreetmap.fr',
|
|
'https://panoramax.ign.fr',
|
|
// IGN's pictures redirect to its OVH object storage.
|
|
'https://panoramax-storage-public-fast.s3.gra.perf.cloud.ovh.net',
|
|
'https://panoramax.mapcomplete.org',
|
|
]);
|
|
|
|
export const MCP_APP_MIME_TYPE = 'text/html;profile=mcp-app';
|
|
const MCP_APPS_PROTOCOL_VERSION = '2026-01-26';
|
|
const PANEL_HEIGHT_PX = 520;
|
|
const LOAD_TIMEOUT_MS = 90_000;
|
|
/**
|
|
* The app's address inside the panel, for code that needs an https address
|
|
* (the page's own may use a host's scheme). The panel loads its paths
|
|
* through the MCP server; the name never resolves.
|
|
*/
|
|
const PANEL_APP_BASE_URL = 'https://app.gods-eye-view.invalid/';
|
|
/** The panel-only tool the panel loads the app through. */
|
|
export const PANEL_REQUEST_TOOL = 'panel_request';
|
|
|
|
/**
|
|
* The panel page: its status line, the Open in God's Eye View button, and
|
|
* the panel's script, `runtime`, which loads the app through the MCP server
|
|
* and shows each view a tool returns.
|
|
*/
|
|
function panelHtml(runtime, panelKey) {
|
|
const config = {
|
|
appBaseUrl: PANEL_APP_BASE_URL,
|
|
loadTimeoutMs: LOAD_TIMEOUT_MS,
|
|
panelBase: PANEL_BASE,
|
|
panelHeight: PANEL_HEIGHT_PX,
|
|
panelKey,
|
|
protocolVersion: MCP_APPS_PROTOCOL_VERSION,
|
|
toolName: PANEL_REQUEST_TOOL,
|
|
workerPreludePath: PANEL_WORKER_PRELUDE_PATH,
|
|
};
|
|
const script = `(${runtime})(${JSON.stringify(config)});`.replace(
|
|
/<\/script/gi,
|
|
'<\\/script',
|
|
);
|
|
return `<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>God's Eye View</title>
|
|
<style>
|
|
html, body { margin: 0; height: 100%; min-height: ${PANEL_HEIGHT_PX}px; background: #05070a;
|
|
color: #b8c4cc; font: 13px/1.4 system-ui, sans-serif; overflow: hidden; }
|
|
#status { position: absolute; inset: 0; z-index: 10000; display: flex; align-items: center;
|
|
justify-content: center; padding: 0 24px; text-align: center; }
|
|
#actions { position: absolute; right: 10px; top: 10px; z-index: 10001; display: flex; gap: 6px; }
|
|
#actions button { padding: 6px 10px; border: 1px solid #3a4a55; border-radius: 6px;
|
|
background: rgba(5, 7, 10, 0.75); color: #dfe8ee; font: inherit; cursor: pointer; }
|
|
#actions button[hidden] { display: none; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="status">Waiting for a view…</div>
|
|
<div id="actions">
|
|
<button id="expand" type="button" hidden>Expand</button>
|
|
<button id="open" type="button" hidden>Open in God's Eye View</button>
|
|
</div>
|
|
<script>${script}</script>
|
|
</body>
|
|
</html>
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* The panel as an MCP resource. `runtime` is the panel's script, a function
|
|
* of its configuration that runs in the panel page; the application supplies
|
|
* it (src/app/globePanelRuntime.js), as it is browser code. The security
|
|
* policy lets the panel load map imagery, tiles and fonts from their
|
|
* providers; everything from the app's own server arrives through the MCP
|
|
* server instead. `panelKey` is the key the panel's requests must carry
|
|
* (see src/tools/queries/panelRequest.js), which this page holds.
|
|
*/
|
|
export function createGlobePanelResource({ runtime, panelKey }) {
|
|
if (typeof runtime !== 'function')
|
|
throw new TypeError('The globe panel needs its runtime script');
|
|
if (typeof panelKey !== 'string' || panelKey.length === 0)
|
|
throw new TypeError('The globe panel needs its request key');
|
|
return Object.freeze({
|
|
uri: GLOBE_PANEL_URI,
|
|
name: 'globe',
|
|
title: "God's Eye View globe",
|
|
description: "Live God's Eye View, showing the view a tool returns.",
|
|
mimeType: MCP_APP_MIME_TYPE,
|
|
text: panelHtml(runtime, panelKey),
|
|
_meta: {
|
|
ui: {
|
|
csp: {
|
|
resourceDomains: [...PROVIDER_ORIGINS],
|
|
connectDomains: [...PROVIDER_ORIGINS],
|
|
},
|
|
prefersBorder: true,
|
|
},
|
|
},
|
|
});
|
|
}
|