@appsforgood/next-supabase-kit 0.1.7 → 0.1.8

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.
@@ -600,6 +600,36 @@ code {
600
600
  max-height: calc(100vh - 64px);
601
601
  }
602
602
 
603
+ .studio-controls {
604
+ display: grid;
605
+ gap: 10px;
606
+ margin-bottom: 12px;
607
+ padding-bottom: 12px;
608
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
609
+ }
610
+
611
+ .studio-label {
612
+ font-size: 12px;
613
+ color: #94a3b8;
614
+ }
615
+
616
+ .studio-note-form {
617
+ display: grid;
618
+ gap: 8px;
619
+ }
620
+
621
+ .studio-note-form input,
622
+ .studio-note-form select,
623
+ #session-picker {
624
+ width: 100%;
625
+ padding: 8px 10px;
626
+ border-radius: 8px;
627
+ border: 1px solid rgba(255, 255, 255, 0.12);
628
+ background: #0f172a;
629
+ color: #e2e8f0;
630
+ font-size: 13px;
631
+ }
632
+
603
633
  .transcript-panel h2 {
604
634
  margin: 0 0 8px;
605
635
  font-size: 14px;
@@ -35,6 +35,7 @@
35
35
  handoffPulse: null,
36
36
  studioSessionId: boot.activeSessionId || "",
37
37
  studioEvents: [],
38
+ studioEventSource: null,
38
39
  speechBubbles: [],
39
40
  agenticLevel: null
40
41
  };
@@ -71,7 +72,12 @@
71
72
  bubbleLayer: document.getElementById("bubble-layer"),
72
73
  officeHint: document.getElementById("office-hint"),
73
74
  canvasWrap: document.querySelector(".canvas-wrap"),
74
- transcriptList: document.getElementById("transcript-list")
75
+ transcriptList: document.getElementById("transcript-list"),
76
+ sessionPicker: document.getElementById("session-picker"),
77
+ studioNoteForm: document.getElementById("studio-note-form"),
78
+ studioNoteAgent: document.getElementById("studio-note-agent"),
79
+ studioNoteText: document.getElementById("studio-note-text"),
80
+ studioRenderBtn: document.getElementById("studio-render-btn")
75
81
  };
76
82
 
77
83
  const ctx = els.canvas?.getContext("2d");
@@ -210,11 +216,15 @@
210
216
 
211
217
  async function loadStudioState() {
212
218
  const sessionsRes = await api("/api/sessions");
213
- state.studioSessionId = sessionsRes.activeSessionId || boot.activeSessionId || "";
219
+ const sessions = sessionsRes.sessions || [];
220
+ state.studioSessionId = sessionsRes.activeSessionId || boot.activeSessionId || sessions[0]?.sessionId || "";
221
+ populateSessionPicker(sessions, state.studioSessionId);
222
+ populateStudioAgents();
214
223
  if (els.sessionPill) {
215
224
  els.sessionPill.textContent = state.studioSessionId ? state.studioSessionId.slice(0, 24) : "No session";
216
225
  }
217
- if (els.projectName) els.projectName.textContent = sessionsRes.sessions?.[0]?.title || "Council session";
226
+ const activeSession = sessions.find((s) => s.sessionId === state.studioSessionId);
227
+ if (els.projectName) els.projectName.textContent = activeSession?.title || "Council session";
218
228
  if (els.officeHint) {
219
229
  els.officeHint.classList.remove("hidden");
220
230
  window.setTimeout(() => els.officeHint?.classList.add("hidden"), 6000);
@@ -222,10 +232,94 @@
222
232
  connectStudioStream();
223
233
  }
224
234
 
235
+ function populateSessionPicker(sessions, selectedId) {
236
+ if (!els.sessionPicker) return;
237
+ els.sessionPicker.innerHTML = sessions.length
238
+ ? sessions
239
+ .map(
240
+ (s) =>
241
+ '<option value="' +
242
+ escapeHtml(s.sessionId) +
243
+ '"' +
244
+ (s.sessionId === selectedId ? " selected" : "") +
245
+ ">" +
246
+ escapeHtml((s.title || s.sessionId).slice(0, 48)) +
247
+ "</option>"
248
+ )
249
+ .join("")
250
+ : '<option value="">No sessions</option>';
251
+ }
252
+
253
+ function populateStudioAgents() {
254
+ if (!els.studioNoteAgent) return;
255
+ els.studioNoteAgent.innerHTML = state.agents.length
256
+ ? state.agents.map((a) => '<option value="' + escapeHtml(a.id) + '">' + escapeHtml(a.name || a.id) + "</option>").join("")
257
+ : '<option value="planner">Planner</option>';
258
+ }
259
+
260
+ function bindStudioControls() {
261
+ if (els.sessionPicker) {
262
+ els.sessionPicker.addEventListener("change", () => {
263
+ state.studioSessionId = els.sessionPicker.value;
264
+ if (els.sessionPill) {
265
+ els.sessionPill.textContent = state.studioSessionId ? state.studioSessionId.slice(0, 24) : "No session";
266
+ }
267
+ connectStudioStream();
268
+ });
269
+ }
270
+ if (els.studioNoteForm) {
271
+ els.studioNoteForm.addEventListener("submit", async (event) => {
272
+ event.preventDefault();
273
+ if (!state.studioSessionId) {
274
+ setStatus("error", "Select a session first.");
275
+ return;
276
+ }
277
+ const agent = els.studioNoteAgent?.value || "planner";
278
+ const text = (els.studioNoteText?.value || "").trim();
279
+ if (!text) {
280
+ setStatus("error", "Enter note text.");
281
+ return;
282
+ }
283
+ try {
284
+ await api("/api/sessions/" + encodeURIComponent(state.studioSessionId) + "/note", {
285
+ method: "POST",
286
+ headers: { "Content-Type": "application/json" },
287
+ body: JSON.stringify({ agent, text })
288
+ });
289
+ if (els.studioNoteText) els.studioNoteText.value = "";
290
+ setStatus("ok", "Note recorded.");
291
+ } catch (error) {
292
+ setStatus("error", error.message);
293
+ }
294
+ });
295
+ }
296
+ if (els.studioRenderBtn) {
297
+ els.studioRenderBtn.addEventListener("click", async () => {
298
+ if (!state.studioSessionId) {
299
+ setStatus("error", "Select a session first.");
300
+ return;
301
+ }
302
+ try {
303
+ const result = await api("/api/sessions/" + encodeURIComponent(state.studioSessionId) + "/render", {
304
+ method: "POST"
305
+ });
306
+ setStatus("ok", "Rendered " + (result.files?.length || 0) + " markdown files.");
307
+ } catch (error) {
308
+ setStatus("error", error.message);
309
+ }
310
+ });
311
+ }
312
+ }
313
+
225
314
  function connectStudioStream() {
226
315
  if (!state.studioSessionId) return;
316
+ if (state.studioEventSource) {
317
+ state.studioEventSource.close();
318
+ state.studioEventSource = null;
319
+ }
227
320
  const url = "/api/events/stream?sessionId=" + encodeURIComponent(state.studioSessionId);
228
321
  const source = new EventSource(url);
322
+ state.studioEventSource = source;
229
323
  source.addEventListener("snapshot", (ev) => {
230
324
  try {
231
325
  const payload = JSON.parse(ev.data);
@@ -249,6 +343,7 @@
249
343
  });
250
344
  source.onerror = () => {
251
345
  source.close();
346
+ if (state.studioEventSource === source) state.studioEventSource = null;
252
347
  };
253
348
  }
254
349
 
@@ -1119,6 +1214,7 @@
1119
1214
  }
1120
1215
 
1121
1216
  loop();
1217
+ if (isStudio) bindStudioControls();
1122
1218
  loadState().catch((error) => setStatus("error", error.message));
1123
1219
  window.addEventListener("resize", () => {
1124
1220
  renderNameplates();
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "packageName": "@appsforgood/next-supabase-kit",
3
- "packageVersion": "0.1.7",
3
+ "packageVersion": "0.1.8",
4
4
  "stack": "next-supabase",
5
5
  "installedAt": "2026-07-04T01:32:06.844Z",
6
6
  "docs": [
@@ -19,7 +19,7 @@
19
19
  {
20
20
  "level": "pass",
21
21
  "area": "install",
22
- "message": "Agent kit installed at version 0.1.7."
22
+ "message": "Agent kit installed at version 0.1.8."
23
23
  },
24
24
  {
25
25
  "level": "pass",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appsforgood/next-supabase-kit",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Open agent council, skills, prompts, checklists, and markdown templates for Next.js and Supabase projects.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -84,6 +84,7 @@
84
84
  "smoke:install": "node scripts/smoke-install.mjs",
85
85
  "smoke:studio": "node scripts/smoke-studio.mjs",
86
86
  "smoke:setup": "node scripts/smoke-setup.mjs",
87
+ "smoke:ui-screens": "node scripts/smoke-ui-screens.mjs",
87
88
  "smoke:audit-gate": "node scripts/smoke-audit-gate.mjs",
88
89
  "package:validate": "node dist/index.js package validate",
89
90
  "examples:check": "node scripts/example-check.mjs",
@@ -114,6 +115,7 @@
114
115
  "eslint": "^9.39.4",
115
116
  "eslint-config-prettier": "^10.1.8",
116
117
  "globals": "^17.7.0",
118
+ "playwright": "^1.55.1",
117
119
  "prettier": "^3.9.4",
118
120
  "tsup": "^8.3.5",
119
121
  "tsx": "^4.19.2",