/** * Team Activity Log — Surface Entry Point * * Demonstrates: * - SDK boot + sw.* API access * - Preact rendering into #extension-mount * - Calling Starlark-backed ext API routes (/s/:slug/api/*) * - sw.toast, sw.auth, sw.on (WS event subscription) * - sw.userMenu for shell integration * - Platform CSS variables for theming */ (async function () { 'use strict'; var mount = document.getElementById('extension-mount'); if (!mount) return; // ── Boot SDK (extension surfaces don't auto-load it) ── var base = window.__BASE__ || ''; var ver = window.__VERSION__ || '0'; var slug = (window.__MANIFEST__ || {}).id || 'team-activity-log'; try { if (!window.preact) { var { h, render } = await import(base + '/js/sw/vendor/preact.module.js'); var hooksModule = await import(base + '/js/sw/vendor/hooks.module.js'); var htmModule = await import(base + '/js/sw/vendor/htm.module.js'); window.preact = { h, render }; window.hooks = hooksModule; window.html = htmModule.default.bind(h); } var sdk = await import(base + '/js/sw/sdk/index.js?v=' + ver); await sdk.boot(); } catch (e) { mount.innerHTML = '
SDK boot failed: ' + e.message + '
'; return; } var { html } = window; var { useState, useEffect, useCallback, useRef } = hooks; var { render } = preact; // ── API helper: call Starlark backend ── var API_BASE = base + '/s/' + slug + '/api'; async function api(method, path, body) { var token = sw.auth._getToken(); var opts = { method: method, headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' }, }; if (body) opts.body = JSON.stringify(body); var resp = await fetch(API_BASE + path, opts); var text = await resp.text(); try { return JSON.parse(text); } catch (_) { return { _raw: text, _status: resp.status }; } } // ── Components ───────────────────────────── var CATEGORIES = ['note', 'update', 'decision', 'action-item']; function App() { var [entries, setEntries] = useState([]); var [loading, setLoading] = useState(true); var [filter, setFilter] = useState(''); var menuRef = useRef(null); var load = useCallback(async function () { var path = '/entries' + (filter ? '?category=' + encodeURIComponent(filter) : ''); var d = await api('GET', path); setEntries(d.data || []); setLoading(false); }, [filter]); useEffect(function () { load(); }, [load]); // Mount user menu useEffect(function () { if (menuRef.current && sw.userMenu) { sw.userMenu(menuRef.current, { placement: 'down-right' }); } }, []); async function addEntry(category, message) { var user = sw.auth.user; var result = await api('POST', '/entries', { category: category, message: message, username: user ? (user.display_name || user.username) : '', created_at: new Date().toISOString(), }); if (result.error) { sw.toast(result.error, 'error'); return; } sw.toast('Entry added', 'success'); load(); } async function deleteEntry(id) { await api('DELETE', '/entries/' + id); sw.toast('Entry deleted', 'info'); load(); } return html`