@volter/teams 0.1.21 → 0.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/viewer/index.html +9 -5
- package/viewer/viewer.mjs +32 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/teams",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.22",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Volter Teams, the neutral team plane: machine identity and daemon core, the local door and server link, admission from machine grants, product doors, and the Teams server's neutral routes (company RFC 0008).",
|
|
6
6
|
"license": "MIT",
|
package/viewer/index.html
CHANGED
|
@@ -7,13 +7,14 @@
|
|
|
7
7
|
<title>Viewer</title>
|
|
8
8
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/css/xterm.css">
|
|
9
9
|
<style>
|
|
10
|
-
|
|
10
|
+
/* The page's scheme is the host's when it sets one (`--scui-color-scheme` through the theme door), else dark. */
|
|
11
|
+
:root { --scui-color-scheme: dark; color-scheme: var(--scui-color-scheme); --bg: #0b0d10; --panel: #13161b; --line: #262b33; --text: #e6e8eb; --muted: #8b939e; --accent: #7aa2f7; }
|
|
11
12
|
* { box-sizing: border-box; }
|
|
12
13
|
html, body { height: 100%; margin: 0; background: var(--bg); color: var(--text); font: 13px/1.4 ui-sans-serif, system-ui, -apple-system, sans-serif; }
|
|
13
14
|
body { display: flex; flex-direction: column; }
|
|
14
|
-
header { display: flex; align-items: center; gap: 8px; padding: 6px 10px; border-bottom: 1px solid var(--line); background: var(--panel); min-height: 32px; }
|
|
15
|
-
header .target { font-family: ui-monospace, Menlo, monospace; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
16
|
-
header .scope { margin-left: auto; font-size: 11px; padding: 1px 6px; border: 1px solid var(--line); border-radius: 4px; color: var(--muted); }
|
|
15
|
+
body > header { display: flex; align-items: center; gap: 8px; padding: 6px 10px; border-bottom: 1px solid var(--line); background: var(--panel); min-height: 32px; }
|
|
16
|
+
body > header .target { font-family: ui-monospace, Menlo, monospace; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
17
|
+
body > header .scope { margin-left: auto; font-size: 11px; padding: 1px 6px; border: 1px solid var(--line); border-radius: 4px; color: var(--muted); }
|
|
17
18
|
#status { padding: 4px 10px; color: var(--muted); font-size: 12px; border-bottom: 1px solid var(--line); }
|
|
18
19
|
#status:empty { display: none; }
|
|
19
20
|
main { flex: 1; min-height: 0; display: flex; }
|
|
@@ -34,11 +35,14 @@
|
|
|
34
35
|
button.action:disabled { opacity: .5; cursor: default; }
|
|
35
36
|
label.action input { display: none; }
|
|
36
37
|
/* supercode-ui follows this page's dark scheme; its composer spans the page instead of its 420px default. */
|
|
37
|
-
#session { flex: 1; min-width: 0; min-height: 0; display: flex;
|
|
38
|
+
#session { flex: 1; min-width: 0; min-height: 0; display: flex; }
|
|
38
39
|
#session .session-view { flex: 1; min-width: 0; display: flex; flex-direction: column; }
|
|
39
40
|
#session .session-view > section { flex: 1; min-height: 0; overflow: auto; }
|
|
40
41
|
#session .session-composer { border-top: 1px solid var(--line); padding: 8px; --scui-width: 100%; --scui-height: auto; }
|
|
41
42
|
#session .session-composer > * { flex: 1; min-width: 0; }
|
|
43
|
+
/* The host's header carries the session's details when it hides this one. */
|
|
44
|
+
#session.header-hidden .scui-team-transcript > header { display: none; }
|
|
45
|
+
body:has(#session:not([hidden])) { background: var(--scui-bg, var(--volter-surface-page, var(--bg))); color: var(--scui-fg, var(--text)); }
|
|
42
46
|
[hidden] { display: none !important; }
|
|
43
47
|
</style>
|
|
44
48
|
</head>
|
package/viewer/viewer.mjs
CHANGED
|
@@ -10,13 +10,32 @@ const $ = (id) => document.getElementById(id);
|
|
|
10
10
|
const decoder = new TextDecoder();
|
|
11
11
|
const status = (text) => { $('status').textContent = text; };
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
const fragment = new URLSearchParams(location.hash.slice(1));
|
|
14
|
+
let token = fragment.get('token');
|
|
14
15
|
history.replaceState(null, '', location.pathname);
|
|
15
16
|
let waiting = null;
|
|
16
17
|
|
|
17
18
|
const embedded = window.parent !== window;
|
|
18
19
|
const ask = (type) => { if (embedded) window.parent.postMessage({ type }, '*'); };
|
|
20
|
+
// The host's style, as supercode-ui is themed: its `--volter-*` brand tokens and `--scui-*` component tokens, set on
|
|
21
|
+
// the session's root (every component reads them from an ancestor). Nothing else is taken from the host.
|
|
22
|
+
function applyTheme(properties) {
|
|
23
|
+
if (!properties || typeof properties !== 'object') return;
|
|
24
|
+
for (const [name, value] of Object.entries(properties)) {
|
|
25
|
+
if (!/^--(volter|scui)-[a-z0-9-]+$/.test(name) || typeof value !== 'string' || value.length > 400) continue;
|
|
26
|
+
// on the page (its background) and on the session root (which sets its own scheme default)
|
|
27
|
+
for (const element of [document.documentElement, $('session')]) element.style.setProperty(name, value);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// The session header is the host's to show: framed, it is hidden unless the host asks for it (the host's own header
|
|
31
|
+
// carries the session's details, which this page posts to it as `viewer-session`).
|
|
32
|
+
function showHeader(shown) { $('session').classList.toggle('header-hidden', !shown); }
|
|
33
|
+
try { applyTheme(JSON.parse(atob((fragment.get('theme') ?? '').replaceAll('-', '+').replaceAll('_', '/')) || 'null')); } catch { /* no theme */ }
|
|
34
|
+
showHeader(fragment.has('header') ? fragment.get('header') !== 'hidden' : !embedded);
|
|
35
|
+
|
|
19
36
|
window.addEventListener('message', (event) => {
|
|
37
|
+
if (event.data?.type === 'viewer-theme') { applyTheme(event.data.properties); return; }
|
|
38
|
+
if (event.data?.type === 'viewer-header') { showHeader(!event.data.hidden); return; }
|
|
20
39
|
if (event.data?.type !== 'delegated-token' || typeof event.data.token !== 'string') return;
|
|
21
40
|
token = event.data.token;
|
|
22
41
|
if (waiting) { const resume = waiting; waiting = null; resume(); }
|
|
@@ -86,7 +105,18 @@ async function showSession(session, control) {
|
|
|
86
105
|
},
|
|
87
106
|
});
|
|
88
107
|
const headers = async () => ({ Authorization: `Bearer ${await freshToken()}` });
|
|
89
|
-
const
|
|
108
|
+
const HARNESSES = { 'claude-code': 'Claude Code', codex: 'Codex', gemini: 'Gemini CLI', goose: 'Goose', opencode: 'OpenCode', pi: 'Pi', grok: 'Grok' };
|
|
109
|
+
const apply = (body) => {
|
|
110
|
+
view.update({ descriptor: body.descriptor, messages: body.messages.map((message) => message.value), members: body.names?.members ?? [], machines: body.names?.machines ?? [], status: 'ready', error: null });
|
|
111
|
+
// What the host's own header shows about this session: who owns it, what runs it, where, and what this viewer may do.
|
|
112
|
+
const descriptor = body.descriptor ?? {};
|
|
113
|
+
const owner = (body.names?.members ?? []).find((member) => member.principal?.id === descriptor.user?.principal_id)?.principal?.display_name ?? descriptor.user?.reported_user ?? null;
|
|
114
|
+
if (embedded) window.parent.postMessage({ type: 'viewer-session', session: {
|
|
115
|
+
id: descriptor.id ?? session.id, title: descriptor.title ?? null, harness: descriptor.harness ?? null, harnessLabel: HARNESSES[descriptor.harness] ?? descriptor.harness ?? null,
|
|
116
|
+
owner, machines: (descriptor.machine_ids ?? []).map((id) => (body.names?.machines ?? []).find((machine) => machine.id === id)?.name ?? id),
|
|
117
|
+
scope: control ? 'control' : 'view', working: body.working ?? null,
|
|
118
|
+
} }, '*');
|
|
119
|
+
};
|
|
90
120
|
let reading = null;
|
|
91
121
|
const read = () => (reading ??= (async () => {
|
|
92
122
|
try {
|