Files
TREK/plugin-sdk/examples/trip-countdown/client/index.html
T
mauriceboe 0a5c8be83d docs(plugins): plugin wiki + reference plugin (M7)
- Wiki pages (sync to the GitHub wiki on push to main): Plugins overview + trust
  model, Plugin Development (SDK, definePlugin, ctx, routes/jobs, the client
  bridge, testing with the mock host), Plugin Permissions reference, and
  Publishing (registry PR + CI gates + provenance). Linked from the sidebar.
- Reference plugin plugin-sdk/examples/trip-countdown: a complete, minimal-
  permission widget (reads trip data through ctx, renders in the sandboxed
  iframe via the bridge, filled-in README). Validated in the SDK test suite so it
  passes the exact gate authors face.
2026-07-03 17:55:07 +02:00

63 lines
2.4 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Trip Countdown</title>
<style>
:root { color-scheme: light dark; }
body { margin: 0; font: 14px system-ui, sans-serif; display: grid; place-items: center; height: 100vh;
background: transparent; color: #111827; }
body[data-theme="dark"] { color: #e5e7eb; }
.card { text-align: center; padding: 16px; }
.days { font-size: 40px; font-weight: 800; letter-spacing: -0.02em; }
.label { font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em; opacity: 0.6; margin-top: 2px; }
.name { margin-top: 6px; opacity: 0.85; }
.empty { opacity: 0.6; }
</style>
</head>
<body>
<div class="card" id="root"><span class="empty">Loading…</span></div>
<script>
var ctx = null
var reqSeq = 0
function send(msg) { window.parent.postMessage(msg, '*') }
function render(data) {
var root = document.getElementById('root')
if (!data || !data.trip) { root.innerHTML = '<span class="empty">No upcoming trip</span>'; return }
var d = data.trip.days
root.innerHTML = d == null
? '<div class="name">' + escapeHtml(data.trip.name) + '</div><div class="label">in progress</div>'
: '<div class="days">' + d + '</div><div class="label">days to go</div><div class="name">' + escapeHtml(data.trip.name) + '</div>'
}
function escapeHtml(s) { return String(s).replace(/[&<>"]/g, function (c) {
return ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' })[c] }) }
function load() {
var id = 'r' + (++reqSeq)
send({ type: 'trek:invoke', requestId: id, sub: '/next?tripId=' + (ctx && ctx.tripId ? ctx.tripId : ''), method: 'GET' })
window._pending = id
}
window.addEventListener('message', function (e) {
var m = e.data
if (!m || typeof m !== 'object') return
if (m.type === 'trek:context') {
ctx = m
document.body.dataset.theme = m.theme
load()
} else if (m.type === 'trek:response' && m.requestId === window._pending) {
render(m.data)
} else if (m.type === 'trek:error' && m.requestId === window._pending) {
document.getElementById('root').innerHTML = '<span class="empty">Unavailable</span>'
}
})
// Ask TREK for context (theme, current trip id) — it replies with trek:context.
send({ type: 'trek:ready' })
</script>
</body>
</html>