You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Turn the connected GitHub account (#320) into a way of working: browse your gists, open their files
in the editor, and edit them against a local sandbox that only reaches GitHub when you publish.
The rail (common/EditorSidebar) grows to three rows sharing one panel column — Files, Gists, and
the account pinned to the bottom. Picking a gist in Gists switches the panel to Files, since
choosing a gist is a request to see what is in it.
The sandbox. Every change to a gist is staged rather than sent:
opening a gist file tags the document with its origin ({ gistId, filename }), so Save stages
the content instead of writing to disk — Save As still means a file on disk
Add file stages an addition, the row's ✕ stages a deletion, and a staged row's ↺ unstages it
the description is read-only with an Edit button; editing opens a multiline field with Save
and Cancel, because a description can run to several lines and Enter belongs to the text
rows carry A / M / D the way a diff would mark them, with an unpublished-change count that
includes the description
Publish sends the whole draft as one PATCH /gists/{id} — files and description together; Reset discards it, behind a confirmation, since it destroys work that exists nowhere else
Switching files must not force a choice between losing work and publishing. The unsaved-changes
prompt is therefore two buttons — Save as draft and Discard changes — where saving stages the
document as one more change in the sandbox. Several files can be edited in turn and published
together. There is no cancel: keeping the work already costs nothing, and Escape maps to keeping it
so no accidental dismissal loses anything. A document that is not from a gist gets a plain Save,
which still writes to disk.
Drafts persist to userData/gist-drafts.json, keyed by gist id — plain JSON, deliberately not
encrypted, because it is the user's own draft text rather than a secret. Quitting mid-edit does not
silently lose staged work.
Main gains fetchGists, fetchGistFiles, patchGist, draftStore, gistService and ipc/gistHandlers; the renderer gains useGists, useGistFiles, useGistDraft, useWindowTitle, common/GistList, common/GistFiles and EditorSidebar. Saving became origin-aware inside useDocument, so the menu, the Save button and the prompt all go through one path.
The rail lists the account's gists; picking one shows its files in the panel
Opening a file loads it into the editor; a dirty document is never replaced silently
The unsaved-changes prompt offers Save as draft and Discard changes, and saving stages the
document rather than writing it to disk or publishing it
Work is kept when the prompt is dismissed with Escape, and the document stays put whenever the
save could not be completed
Save on a gist file stages it locally and never writes to disk; Save As still writes to disk
Add, delete, edit and the description are all staged — nothing reaches GitHub until Publish
The description reads as text until Edit is pressed, then edits over several lines; Cancel stages
nothing, and Save on an untouched description creates no change
Several files can be edited and staged in turn, then published in one request
Publish sends one request containing the whole draft; a failed publish keeps the draft intact
Reset discards the draft only after a confirmation
Staged changes survive quitting and reopening the app
Staging from the editor and from the panel stay in step — saving updates the panel's change count
without reopening it
The document name and the window title both follow the open file, with an unsaved marker
pnpm lint, pnpm typecheck, pnpm test:coverage green; 100% coverage on every touched file
Sandbox scope agreed: edits, adds, deletes and the description are all staged, rather than only
content edits
Persistence agreed: drafts are written to disk, not held in renderer memory only
Definition of Done (DoD) 🏁
Implemented
Unit tests at 100% coverage on touched files
Verified by hand against a real gist: open several files, save each as a draft, edit the
description, publish once
Additional Notes or Resources 📎
A gist cannot have zero files, so staging D on every file and publishing returns 422. The
error message says so rather than reporting a bare status code.
GitHub rejects a gist file whose content is the empty string with a 422, so toFilePatch
publishes empty content as a blank line. Without it, adding a file and publishing before typing
anything could never succeed.
A draft is { files, description? } rather than a bare file map. Drafts written before the
description could be staged are still read as a file map, so existing staged work is not lost.
Only the renderer can save — it holds the content and knows where the document belongs — so
choosing to keep the work when closing the window sends the save and leaves the window open;
closing again then goes straight through. Worth revisiting if that extra click grates.
Still outstanding, worth its own task: widening the design system's SidebarItem.icon to IconName | ReactNode. All three rail rows are hand-rolled Pressables porting into the panel
through SidebarContext only because icon takes a registry name.
No e2e coverage — the Playwright-Electron suite would need a stubbed fetch in main.
Task Description ✏️
Turn the connected GitHub account (#320) into a way of working: browse your gists, open their files
in the editor, and edit them against a local sandbox that only reaches GitHub when you publish.
The rail (
common/EditorSidebar) grows to three rows sharing one panel column — Files, Gists, andthe account pinned to the bottom. Picking a gist in Gists switches the panel to Files, since
choosing a gist is a request to see what is in it.
The sandbox. Every change to a gist is staged rather than sent:
{ gistId, filename }), so Save stagesthe content instead of writing to disk — Save As still means a file on disk
and Cancel, because a description can run to several lines and Enter belongs to the text
A/M/Dthe way a diff would mark them, with an unpublished-change count thatincludes the description
PATCH /gists/{id}— files and description together;Reset discards it, behind a confirmation, since it destroys work that exists nowhere else
Switching files must not force a choice between losing work and publishing. The unsaved-changes
prompt is therefore two buttons — Save as draft and Discard changes — where saving stages the
document as one more change in the sandbox. Several files can be edited in turn and published
together. There is no cancel: keeping the work already costs nothing, and Escape maps to keeping it
so no accidental dismissal loses anything. A document that is not from a gist gets a plain Save,
which still writes to disk.
Drafts persist to
userData/gist-drafts.json, keyed by gist id — plain JSON, deliberately notencrypted, because it is the user's own draft text rather than a secret. Quitting mid-edit does not
silently lose staged work.
Main gains
fetchGists,fetchGistFiles,patchGist,draftStore,gistServiceandipc/gistHandlers; the renderer gainsuseGists,useGistFiles,useGistDraft,useWindowTitle,common/GistList,common/GistFilesandEditorSidebar. Saving became origin-aware insideuseDocument, so the menu, the Save button and the prompt all go through one path.Parent Epic: #295.
Acceptance Criteria ✅
document rather than writing it to disk or publishing it
save could not be completed
nothing, and Save on an untouched description creates no change
without reopening it
pnpm lint,pnpm typecheck,pnpm test:coveragegreen; 100% coverage on every touched fileDependencies 🔗
Definition of Ready (DoR) 📋
content edits
Definition of Done (DoD) 🏁
description, publish once
Additional Notes or Resources 📎
Don every file and publishing returns 422. Theerror message says so rather than reporting a bare status code.
toFilePatchpublishes empty content as a blank line. Without it, adding a file and publishing before typing
anything could never succeed.
contentat 1MB, andloading a partial file into the editor and then publishing it would silently drop the rest.
{ files, description? }rather than a bare file map. Drafts written before thedescription could be staged are still read as a file map, so existing staged work is not lost.
choosing to keep the work when closing the window sends the save and leaves the window open;
closing again then goes straight through. Worth revisiting if that extra click grates.
SidebarItem.icontoIconName | ReactNode. All three rail rows are hand-rolledPressables porting into the panelthrough
SidebarContextonly becauseicontakes a registry name.fetchin main.