@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.
- package/CHANGELOG.md +8 -0
- package/DOGFOOD.md +10 -0
- package/README.md +3 -3
- package/antigravity/plugin.json +1 -1
- package/dist/index.js +93 -28
- package/dist/index.js.map +1 -1
- package/dist/studio/office/assets/office.css +30 -0
- package/dist/studio/office/assets/office.js +99 -3
- package/examples/next-supabase-installed/.agent-kit/manifest.json +1 -1
- package/examples/next-supabase-installed/audit-output.json +1 -1
- package/package.json +3 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
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();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appsforgood/next-supabase-kit",
|
|
3
|
-
"version": "0.1.
|
|
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",
|