- Fastify API: demos/steps CRUD, content-addressed assets, scoped API keys (read/author/publish/admin, draft-only default per D4) - Player: image + sandboxed DOM steps, hotspots, tooltips, keyboard nav, privacy-first beacons (no IP storage, viewer tokens only — R7) - Editor: React SPA — demo list, drag-to-draw hotspots, annotations, publish - MCP server: Streamable HTTP, scope-filtered tools, untrusted-data wrapping on viewer-supplied content (R8); platform never calls a model - Capture extension (MV3): screenshot-flow recording, hotspots pre-placed from real clicks - Docker: single app image + postgres, Traefik-ready (expose only) E2E verified: capture->author->publish->play->beacon->lead->analytics + full MCP tool surface with scope filtering.
39 lines
2.3 KiB
HTML
39 lines
2.3 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Demo</title>
|
|
<style>
|
|
:root { --accent: #2563eb; --bg: #f8fafc; --fg: #0f172a; }
|
|
* { box-sizing: border-box; margin: 0; }
|
|
body { background: var(--bg); color: var(--fg); font: 15px/1.5 system-ui, sans-serif; height: 100vh; display: flex; flex-direction: column; }
|
|
#stage { position: relative; flex: 1; display: flex; align-items: center; justify-content: center; overflow: hidden; padding: 12px; }
|
|
#snap { position: relative; max-width: 100%; max-height: 100%; box-shadow: 0 4px 24px rgba(15,23,42,.12); border-radius: 8px; overflow: hidden; background:#fff; }
|
|
#snap img { display: block; max-width: 100%; max-height: calc(100vh - 90px); }
|
|
#snap iframe { border: 0; width: 1280px; height: 720px; max-width: 100%; }
|
|
.hotspot { position: absolute; border: 2px solid var(--accent); border-radius: 6px; cursor: pointer; background: rgba(37,99,235,.08); animation: pulse 1.6s ease-in-out infinite; }
|
|
@keyframes pulse { 0%,100% { box-shadow: 0 0 0 0 rgba(37,99,235,.35);} 50% { box-shadow: 0 0 0 8px rgba(37,99,235,0);} }
|
|
.tooltip { position: absolute; max-width: 280px; background: #fff; border: 1px solid #e2e8f0; border-radius: 8px; padding: 10px 12px; box-shadow: 0 8px 30px rgba(15,23,42,.15); z-index: 5; }
|
|
.tooltip h3 { font-size: 14px; margin-bottom: 4px; }
|
|
.tooltip p { font-size: 13px; color: #475569; }
|
|
#bar { display: flex; align-items: center; gap: 10px; padding: 10px 16px; background: #fff; border-top: 1px solid #e2e8f0; }
|
|
#dots { display: flex; gap: 6px; flex: 1; justify-content: center; }
|
|
.dot { width: 8px; height: 8px; border-radius: 50%; background: #cbd5e1; }
|
|
.dot.active { background: var(--accent); }
|
|
button { border: 1px solid #e2e8f0; background: #fff; border-radius: 6px; padding: 6px 14px; cursor: pointer; font: inherit; }
|
|
button:hover { border-color: var(--accent); color: var(--accent); }
|
|
#title { font-weight: 600; font-size: 14px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="stage"><div id="snap"></div></div>
|
|
<div id="bar">
|
|
<span id="title"></span>
|
|
<div id="dots"></div>
|
|
<button id="prev">Back</button>
|
|
<button id="next">Next</button>
|
|
</div>
|
|
<script src="/player.js"></script>
|
|
</body>
|
|
</html>
|