Skip to content

Add Site Studio canvas extension #2117

Open
ayangupt wants to merge 1 commit into
github:mainfrom
ayangupt:add-site-studio-canvas
Open

Add Site Studio canvas extension #2117
ayangupt wants to merge 1 commit into
github:mainfrom
ayangupt:add-site-studio-canvas

Conversation

@ayangupt

Copy link
Copy Markdown
Contributor

Pull Request Checklist

  • I have read and followed the CONTRIBUTING.md guidelines.
  • I have read and followed the Guidance for submissions involving paid services.
  • My contribution adds a new canvas extension in the correct directory (extensions/site-studio/).
  • The file follows the required naming convention.
  • The content is clearly structured and follows the example format.
  • I have tested my canvas extension with GitHub Copilot.
  • I have run npm start and verified that README.md is up to date.
  • I am targeting the staged branch for this pull request.

Description

Site Studio is a canvas extension for planning, drafting, and tracking a personal website — section by section, collaboratively with your agent.

  • Plan — a status board for every section (Not Started -> In Progress -> Built -> Review -> Approved)
  • Draft — an autosaving, schema-validated content editor with [Sample: ...] starter placeholders and an AI-draft action that builds on context you have already provided
  • Review — a live feed of every content change, status change, and milestone

Folder extensions/site-studio/: extension.mjs, canvas.json, package.json, assets/preview.png.


Type of Contribution

  • New canvas extension.

Additional Notes

Built and tested with GitHub Copilot. AI-assisted (🤖🤖🤖 in title per CONTRIBUTING.md). Note: canvas extensions are not part of README generation, so npm start produces no README changes.

Site Studio is a canvas extension for planning, drafting, and tracking a
personal website section by section. It gives you and your agent a shared
dashboard with a status board, an autosaving content editor (with AI-draft
and [Sample: ...] placeholders), and a live feed of every change and milestone.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 24, 2026 19:37
@ayangupt ayangupt requested a review from aaronpowell as a code owner June 24, 2026 19:37
@github-actions github-actions Bot added canvas-extension PR touches canvas extensions new-submission PR adds at least one new contribution labels Jun 24, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Site Studio Copilot canvas extension under extensions/site-studio/, providing an interactive UI + local HTTP API for planning, drafting, and reviewing personal website sections with agent collaboration.

Changes:

  • Introduces the Site Studio extension runtime (extension.mjs) with state persistence, SSE updates, template packs, and canvas actions.
  • Adds extension metadata (canvas.json) including keywords and screenshot paths.
  • Adds extension package metadata (package.json) for module/type entrypoint and SDK dependency.
Show a summary per file
File Description
extensions/site-studio/package.json Declares the extension package metadata and Copilot SDK dependency.
extensions/site-studio/extension.mjs Implements the Site Studio canvas (server, UI rendering, persistence, actions).
extensions/site-studio/canvas.json Registers canvas metadata, keywords, version, and screenshot references.

Copilot's findings

  • Files reviewed: 2/4 changed files
  • Comments generated: 8

Comment on lines +24 to +26
const VALID_STATUS = new Set(["Not Started", "In Progress", "Built", "Review", "Approved", "Needs Changes"]);
const VALID_CHANGE_TYPES = new Set(["file_modified", "file_added", "file_deleted", "status_change", "commit", "milestone"]);
const STATUS_ORDER = ["Not Started", "In Progress", "Built", "Review", "Approved", "Needs Changes"];
Comment on lines +409 to +413
for (const cwd of candidates) {
try {
const branch = execSync("git --no-pager rev-parse --abbrev-ref HEAD", { cwd, stdio: ["ignore", "pipe", "ignore"] })
.toString()
.trim();
Comment on lines +561 to +567
async function persistState() {
if (!stateCache) {
return;
}
await mkdir(ARTIFACTS_DIR, { recursive: true });
await writeFile(STATE_FILE, JSON.stringify(stateCache, null, 2), "utf8");
}
Comment on lines +828 to +845
async function readBodyJson(req) {
const chunks = [];
for await (const chunk of req) {
chunks.push(chunk);
}
if (!chunks.length) {
return {};
}
const body = Buffer.concat(chunks).toString("utf8");
if (!body.trim()) {
return {};
}
try {
return JSON.parse(body);
} catch {
throw new CanvasError("invalid_json", "Request body must be valid JSON.");
}
}
Comment on lines +1535 to +1546
if (req.method === "GET" && pathname === "/events") {
const entry = servers.get(instanceId);
res.statusCode = 200;
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
res.write("event: state_update\n");
res.write(`data: ${JSON.stringify(stateCache)}\n\n`);
entry.clients.add(res);
req.on("close", () => entry.clients.delete(res));
return;
}
Comment on lines +700 to +710
function upsertSectionContent(sectionId, field, value, mark = "draft", author = "agent") {
const section = findSectionOrThrow(sectionId);
const normalizedField = safeString(field).trim();
if (!normalizedField) {
throw new CanvasError("invalid_field", "Field name is required.");
}
const normalizedValue = safeString(value, "");
validateFieldValue(section.id, normalizedField, normalizedValue, mark);
const current = safeString(section.content[normalizedField], "");
section.content[normalizedField] = normalizedValue;
section.lastModified = nowIso();
Comment on lines +734 to +736
if (!section.content || !(normalizedField in section.content)) {
return { section, field: normalizedField, removed: false };
}
Comment on lines +1236 to +1241
const aiAssist = '<div class="ai-assist">' +
'<span class="ai-tip-wrap">' +
'<span class="ai-label">✨ Generate with AI</span>' +
'<span class="ai-info" aria-hidden="true">ⓘ</span>' +
'<span class="ai-tip-bubble" role="tooltip">' + esc(aiTip) + '</span>' +
'</span>' +
@aaronpowell

Copy link
Copy Markdown
Contributor

Couple of CCR comments that are worth reviewing

@aaronpowell aaronpowell changed the base branch from staged to main June 25, 2026 04:41
@github-actions github-actions Bot added the targets-main PR targets main instead of staged label Jun 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

canvas-extension PR touches canvas extensions new-submission PR adds at least one new contribution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants