Stet 0.6.0
Fixed
- The window can be dragged and double-click-zoomed again. The overlay
title bar needs the header rows as drag regions, and the start-dragging
window capability was never granted, so macOS silently ignored every
drag. - Traffic lights center on the header line, the closed-rail toggle no
longer overlaps them, and the header New-document button is gone. - Both side rails stay open together at everyday window sizes. Overlay
mode (one rail at a time, floating) now starts only when the center
column would drop below 480px. - Seamless documents no longer slide under the left rail on narrower
windows; prose centers inside the center column and rail toggles slide
it smoothly. - The Changes rail's toggle no longer fights the automatic review
open/close: once you touch the toggle for the current document, the
async "anything to review here?" pass can no longer close the rail out
from under your click. - Creating a new document no longer risks a white-screen: overlay
measurements are rounded so the webview can't crash on fractional
layout, and error boundaries now recover the window instead of going
blank (2026-08-05 test session). - A failed refresh no longer strands a just-activated workspace in a
half-loaded state. - A brand-new file landing in an auto-accept workspace is now detected as
an unseen change like any edit to an existing file; before this, whole
new files could slip in without the green unseen marker. - Escape reliably returns focus from the menu-bar window's drill-in
views. - Links in documents: left-click follows the link, right-click opens the
edit popover (instead of the format toolbar), hardened against
WKWebView focus races (2026-08-05 test session).
Added
- Comments on local documents: any markdown file in a watched workspace
can take comments now, not just Stet-shared ones. They are notes to
self, stored in the workspace's own.stet/comments.jsonusing the
same shapes as shared comments, so they ship with the workspace if it
is shared later. The same Comments lens, composer, resolve flow, and
tree badges apply; you act as the owner. - Comments and AI notes now follow one color rule: human things are
gold, AI things are blue. The selection toolbar gained a gold
speech-bubble "Add comment" action next to the (now blue) AI-note
action, replacing the old floating Comment button — one popup near a
selection instead of two. Unread comment marks, dots, and file-tree
badges are gold; the comment composer picked up a bordered, calmer
design with gold authoring accents. - The menu-bar icon is the new S-with-dots mark as a template image, so
it renders white on dark menu bars and dark on light ones. Its menu now
groups workspaces the same way the sidebar does — Synced workspaces,
Local workspaces, Shared with you — with the same ordering. - The open document's name shows in Stet red in the file tree, matching
the web client. The keyboard-cursor ring only appears while actually
arrowing through the tree, never on a mouse click. - Collaborator comments, desktop collaboration loop (Slice 3 of the
collaborator-comments spec, completing the backlog item): a Stet-shared
markdown file now supports the full comment/reply/resolve loop. Selecting
text shows the toolbar's "Add comment" action (also Review >
Add Comment and⌘⌥M); the composer opens with the passage quoted above
a plain-text box, flushes any pending autosave before capturing the
anchor, and never pushes your branch.Leave a general commentat the
top of the panel adds an unanchored comment. Expanding a thread shows a
reply box and a Resolve/Reopen control (Commenter can resolve only their
own thread; Editor and Owner can resolve any). New activity while a reply
box is open stays marked unread even after you send your own reply.
Comments post optimistically with a quiet "Waiting to sync" state; a
permanent failure (e.g. your role changed) keeps the draft with Retry /
Discard instead of disappearing. Offline drafts queue locally and replay
once, in order, on reconnect — including across an app restart. The
file tree now shows a small speech-bubble unread count beside (not
instead of) each file's existing change badge, and new comment activity
can trigger the same quiet OS notification the folder's other shared
changes already use, if Notify is on for that folder.
Internal
-
New
stet_comments.rsRust client (list_stet_comment_threads,
stet_comment_summary) mirrorsstet_account.rs's
send_authed/scoped_errorpattern against stet_server's Slice 1
comment-thread API, with a per-workspace/path/resolved-variant cache in
Tauri app data. New frontend modules:lib/commentAnchor.ts(pure
anchor-resolution core — normalized text stream, the spec's 5-step
resolution order, orphan handling),lib/humanComment.ts(the TipTap
decoration layer, view-only, no schema/serialization impact),
lib/useComments.ts(fetch/poll),lib/commentsLens.tsand
lib/commentTime.ts, andcomponents/CommentsSidebar.tsx. -
Slice 3: four new mutation commands (
create_stet_comment_thread,
reply_stet_comment,set_stet_comment_resolved,
mark_stet_comment_read) plus a durable mutation outbox
(comment_outbox.rs) persisted in Tauri app data — dependency-ordered
(a reply against a not-yet-confirmed thread waits for its create),
bounded exponential backoff for network/5xx failures, immediate
stop-and-surface for 4xx.lib/commentState.tsis the pure optimistic
reducer/server-merge/unread-override/outbox-hydration core the new
useComments.tsmutation wrappers andCommentComposer.tsxbuild on. -
The folder inspector's single "Remote review" setting is now a stack of
per-source review lanes: Local edits, and (only when the workspace is
shared through that provider) From stet sync and From GitHub. Each has
its own Needs approval/Auto-accept control..stet/config.jsongains
optionalstetReview/githubReviewfields per folder; a config that
never sets them falls back to the existingremoteReviewvalue, so
nothing changes for a workspace that hasn't customized this yet. Notify
now derives on if any applicable review lane requires approval, not just
local review, and a folder row in the list view gets a small marker when
a remote lane's effective policy differs from local. -
Share Settings gains a Review rules section for Stet-shared workspaces:
owner-editable "Changes by owners" / "Changes by editors" rules (Land
instantly / Need approval), an explanatory note when an owner's own
changes are covered by their own rule, and a live count of any per-person
exceptions. These rules act as a ceiling over the per-folder review lane
above: a folder can always ask for more review than the workspace
requires, never less, and the inspector shows "Workspace rule (shared)" or
"Tightened here" accordingly with a link back to Share Settings.
GitHub-linked workspaces get a simpler provider-wide rule instead, since
GitHub access has no server-verified identity to attach per-person rules
to. -
People gains a per-person review menu next to each collaborator's role:
Group rule, Lands instantly, or Needs approval, overriding the workspace's
role default for just that person (a person's own setting always wins).
Owner-only, like the role select; removing a collaborator also clears
their exception. -
The sidebar Controls lens's list step shows a "Sharing" section above the
folder tree, listing every stet-shared watched workspace by name and
role; selecting one switches to it and jumps straight to its Sharing
step. Replaces the old "Shared workspaces" list from the Stet account
panel. -
Plain-text files (
.txt) now flow through propose, review, and approve
the same way markdown files do: an out-of-band edit is captured as a
reviewable proposal, and the file shows up with the usual pending-change
pill. The editor renders.txtcontent exactly as written, never
reformatted into markdown structure, so a raw transcript with a line that
happens to start with "#" or "-" stays plain text. -
Sharing management now assigns and edits roles instead of only "owner" and
"editor" being possible. Roles are the four-value ladder Viewer,
Commenter, Editor, Owner. An owner can change another member's role from
the Collaborators list via a text-pipe selector (promoting to Owner asks
for confirmation first, since it grants full control); pending invites
show their assigned role, and both the invite form in the Sharing panel
and the Share dialog's "Invite another collaborator" form let you pick a
role before sending (defaulting to Editor). If a workspace's server
hasn't shipped the role-change endpoint yet, the panel falls back to
showing each member's role as plain text instead of erroring. -
The share badge on a shared workspace's root now shows a styled hover card
instead of a plain browser tooltip: who it's shared with (first two
collaborators, then a count), your role, and whether it's shared on Stet
or via GitHub. The sidebar Filters panel also gets a "Shared" option that
narrows the "All folders" list to shared workspace roots.
Changed
- Viewer parity follow-ups on top of the structural redesign below. The
native macOS title bar is gone; the traffic lights now sit in the rail
header row beside the "Stet." wordmark, which now opens the
all-workspaces view when clicked. The left rail's top structure is a
plain "Workspaces" back row with the filter icon, a workspace row with a
folder icon and a three-dot "Folder controls..." menu, then "New
document" and "Open folder..." action rows above the tree, replacing the
old breadcrumb and hamburger menu. The right rail is titled "Changes"
instead of "Edits" (the Edits | Comments lens pair inside a shared
document keeps its own names). Paper and Seamless moved from the header
into the View menu as a checkable pair. All-workspaces rows show a plain
folder icon instead of a colored diamond, and synced rows drop their
separate share icon (its description now shows on the sync button's
tooltip instead). The all-workspaces list gained a "Workspaces" title
above its "Your workspaces" eyebrow, and its plus menu's two actions now
show a one-line description under each label. Tabs gained a document
icon. The sign-in popover was
restyled with a plain "Sign in to Stet" title, a black "Send sign-in
link" button, and a "Create an account on the web" link, and dropped the
"without GitHub" line. The sign-in row's placeholder avatar and the
Terms/Privacy links are gone from the rail footer. - Moved Local account access into a compact bottom-left profile area shared by
the workspace tree and all-workspaces rail. Signed-out users get the
existing email magic-link flow plus an explicit Web account-creation
branch; signed-in users see their name/email, Manage account, and Sign out.
Terms and Privacy now remain as quiet shell links outside the popover,
including a compact treatment when the files rail is closed. - Reworked the desktop sidebar's all-workspaces view into a compact workspace
home grouped into Synced workspaces, Local workspaces, and Shared with you.
The in-workspace back arrow now enters this view; its header keeps only the
workspace filter control, while a+beside Your workspaces branches to the
native open-folder flow or a name-first create-and-choose-destination flow.
Rows share one visual treatment and only show paths when names collide.
Synced rows can now force-sync their exact local path without switching the
open workspace; owned Stet rows await fetch/land, home push, and proposal-ref
sync before opening their persisted hosted workspace ID in the browser.
Shared-with-you Stet rows can open that hosted view but cannot publish the
owner's home, GitHub rows sync home but never open as Stet Web workspaces,
and local-only setup remains disabled until a confirmation-based flow exists. - Reworked the document window into one structural three-column frame: the
off-white files and review rails now run full height, their headers align
with the white tab strip, and opening a rail pushes and resizes the center
header instead of floating over it. Tabs use a quiet dark active label with
a solid red underline, and the Local viewer now remembers an in-app Paper
or Seamless presentation choice (defaulting to Seamless) without reloading
the open editor. Small windows now switch to a single keyboard-accessible
overlay rail before the document becomes cramped, while preserving the
wide-window rail preferences and whole-viewer Seamless centering. - Replaced the paragraph-and-strikethrough app artwork with Stet's simpler
editorial S and three-dot proofreader mark across desktop and webview icons. - The Controls lens's "Stet account" panel is now a short summary (display
name/email, "Manage account", sign out) instead of duplicating profile,
billing, and shared-workspaces detail inline. The account area at
sync.getstet.app/account is now the canonical place for that; "Manage
account" opens it in the browser. - An Editor's accepted changes on a Stet-hosted shared workspace no longer
attempt to push to the sharedmainbranch — only an Owner's changes do.
Non-owners' accepts still land locally; the server now enforces this same
rule, so this only avoids a push attempt that would otherwise be rejected. - Collapse/expand-all moved off the sidebar header — the icon button next to
the hamburger menu is gone — and onto a folder's right-click menu: "Expand
all" and "Collapse all" now sit at the top of every folder's context menu,
including in browse mode. They fold the WHOLE tree, not just the
right-clicked folder's subtree ("it's expand ALL") — option-clicking a
folder's row is still the subtree-scoped, Finder-style gesture. - The in-app File dropdown is gone. Every File action now lives only in
the native macOS File menu, which gained the flat "Open in <agent>"
primary-agent shortcut that used to live above the in-window submenu.
Removing a single recent workspace from the list one at a time is no
longer possible; use Clear Menu to clear the whole "Open Recent" list,
same as the native menu already offered.
Fixed
- The file tree's indentation is consistent again. A file and a folder at
the same depth now line up; before this, a file's icon sat noticeably
left of a folder's icon at the same level because the disclosure caret
column wasn't reserved for rows that don't have one. - The floating pending-changes summary no longer covers the "Restore this
version" button in history mode, and no longer sits on top of the
document while the Changes rail is open. It now only shows as a
rail-closed discovery affordance outside history preview. - Reopening the workspace you're already in from the all-workspaces list no
longer closes every open tab and clears review state. - The active tab's underline no longer runs under its close button.
- Relative document links whose path begins with a normal folder name (for
exampledocs/readme.mdordocs/readme) now remain clickable when a
Markdown document loads. TipTap's default URI validator was stripping those
explicit link marks while allowing only dot-relative forms such as
../readme.md, so the in-app document navigation added in this batch never
received the click. - "Copy web review link" no longer stays hidden after signing in to your
Stet account from the Controls lens's "Stet account" panel. Signing in
there is a separate surface from the Share dialog. It wasn't telling the
app to refresh its share status, so the workspace owner's context menu
kept reading the pre-sign-in, signed-out state. It stayed that way until
something else triggered a refresh, like switching workspaces or
relaunching. Sign-in and sign-out now refresh it immediately. - A Stet-hosted share now syncs pending proposal branches, not just the home
branch. Before this, a workspace with pending proposals showed nothing in
the web app's "Pending changes" list, because the desktop app never sent
those branches to the server. Proposal branches sync on share creation, as
new proposals are captured, and after a proposal is accepted or rejected;
a resolved proposal's remote copy is deleted so it stops showing as
pending on the web once it's handled locally. This only applies to
Stet-hosted shares; GitHub-hosted shares are unchanged. - Documents in an unwatched folder now always show what's actually on disk,
even after another app or an AI agent edits them outside Stet. Before this
fix, an unwatched folder's document view could go stale, and saving from
Stet's editor could silently overwrite a newer external edit. Saving now
refuses and reloads with the newer content instead, with a banner
explaining what happened. - A new folder that appears inside a watched workspace (created by an agent,
a drag-drop, or any other tool) no longer sits silently frozen out of
review. A banner offers to watch it in one click. - Widening a project root to a parent folder now asks whether to watch
everything in the new root, instead of always leaving sibling folders
unwatched and unexpectedly missing from the "watched only" sidebar view. - Opening a folder that's already inside a watched project, but isn't itself
watched, now offers "Watch this folder" and opens the existing project
scoped to it, instead of confusingly offering to create a second workspace. - Left-clicking a link in the document now OPENS it — an external URL (any
scheme) via the OS, a relative reference to another document in the
workspace (docs/readme.md,../a.md, with or without the.md
extension) right in Stet, the same as clicking it in the sidebar. A
relative reference that would climb outside the workspace is quietly
ignored rather than crashing. Right-click now opens the edit popover
(what left-click used to do); ⌘K / Format > Link… are unchanged. The
popover's Open button uses the same open logic, so it also handles
relative document links. - Fixed a companion bug in the above: a relative document link whose path
started with a plain folder name (docs/readme.md, not../readme.md)
was silently losing its link mark the moment the document loaded — nothing
to click, and worse, a subsequent save would have written the plain text
back with the link gone entirely. The Link extension's default URL
validator has a regex quirk that rejects exactly that shape; Stet's own
validator now allows any scheme-less reference through (still rejecting
javascript:/other dangerous schemes exactly as before). - Right-clicking a link now reliably shows the edit popover instead of the
floating format toolbar. WebKit auto-selects the clicked word on
right-click, which also satisfies the format toolbar's "non-empty
selection" trigger — both were anchoring to the same point, and the
toolbar's higher stacking order was hiding the popover underneath it. The
selection is now collapsed to a cursor before the popover opens, the same
state a left click already leaves things in, so the format toolbar no
longer shows at the same time. - With every tab closed, the app header (tab strip + save status) no longer
sits there as an empty white strip — the row collapses to nothing and
reappears the moment a tab opens. - A brand-new file that arrives as an unseen auto-accepted change (not a
pending proposal — e.g. an agent creating a file in an auto-accept
workspace) no longer wrongly counts as "reviewable" and auto-opens the
review sidebar; only a real line-by-line change does. The whole-new-file
detection now agrees between the proposal and unseen-change code paths. - Escape/back in the Controls surfaces once again matches each surface's
intended step: the tray dashboard's drilled-into-a-workspace step still
goes back to the workspace list, and the sidebar Controls lens still exits
to the Files lens at its top level and steps back one level when drilled
in. Only the tray dashboard's own top level (the workspace list) stays a
no-op. The routing decision is now one pure, unit-tested function
(escapeActionin lib/controlsDrill.ts) shared by both surfaces. - The tray dashboard's Controls tab now reclaims real OS keyboard focus on
every navigation (and whenever the window is shown again) instead of
relying solely on Tauri's one-timeset_focus()when the window was
first created. That window is never torn down between opens, and
reasserting real key-window status on a reopened auxiliary window isn't
reliable on macOS — Escape (a keyboard event) was silently dead once
drilled into a workspace, while the back arrow (a mouse click, which
doesn't need key-window status) kept working. - Tailwind color tokens re-mirrored from design-tokens.css. The accept green
(positive, positive-bg), negative-bg, and diff-add had drifted to
pre-redesign values, and border-negative-border compiled to nothing. A unit
test now fails if the two files disagree. - A brand-new file no longer auto-opens the review sidebar on its own — "the
whole file is new" has nothing to review line by line, so opening one now
only shows its accept-all bubble, reachable even with the sidebar closed.
Switching to a file that already has pending or unseen changes opens the
sidebar to match; switching to a clean file closes it. - The tray dashboard's Controls tab now sits on the app's plain background
instead of the sidebar Controls lens's inset grey (the sidebar keeps its
own inset look). Its top level (the workspace list) no longer shows a back
arrow, and Escape there is now a no-op instead of jumping to the Review
tab — Controls and Review are separate tabs, not a drill-in stack. The
arrow (and Escape's normal behavior) still work once you've drilled into a
workspace. - Agent launch failures (File > Open in AI Agent) now show a dialog naming
the failure instead of only logging to the console. - The Open in AI Agent submenu shows a disabled "No AI agents detected"
row instead of just going grey when nothing is installed, and now also
refreshes on window focus (not just on opening a workspace), so a newly
installed agent shows up without switching folders. - Open Recent disables the entry matching the workspace already open,
and disambiguates two recents that share a folder name as "name — parent
folder" instead of showing the same label twice. - Opening a brand-new file (a whole-new-file change, the kind an agent drops
when it creates a document from scratch) no longer white-screens the window.
The change-mark overlay's full-document "wash" could feed the editor's
resize observer its own layout nudge, looping measurement until React gave
up and unmounted (error #185). Measurements now round to whole pixels and
skip no-op updates, so the loop can't start — and a new error boundary
around the overlay (plus one at the app root with a Reload button) means a
render failure can never blank the whole window again. - A folder adopted into Stet after its files already existed no longer badges
its whole tree green. An unseen change that landed more than a week ago and
still hasn't been looked at now settles into history on its own, the same
seven-day fade the blue NEW badge already used. Nothing is lost — the change
had already landed, and Version history still has it. - A folder's green "unseen" dot can no longer be left with nothing to clear
it. Dots now count only the files the sidebar is actually showing, so an
HTML file hidden by the Filters panel stops lighting up its parent folders,
and reading the last unseen file in a folder clears its dot right away
instead of at the next unrelated refresh. - Clicking a file in the sidebar no longer scrolls the file list. It scrolls
only when the file isn't already visible — coming back to a tab whose file
sits far down the tree — and then only far enough to clear the header,
instead of tucking the row underneath it. - The shipped Open in AI agent chooser is now mounted in the active
sidebar header instead of an unused legacy header component. Agent choices
made from either the in-window or native File menu become the persisted
one-click action, and Codex launch failures are reported instead of being
mistaken for a successful process start. - Invite emails ("Send invite email" / "Copy invite text" in the Share
dialog) no longer show a stray "+" where a space should be. The invite
link now uses proper URL encoding, so mail clients that display the
address literally instead of decoding it show clean text. - A file that fails to load into the diff view because of a real read or
git problem now shows an error message instead of silently looking like
its whole content was deleted or added. Previously a read failure and a
genuine full-file change were indistinguishable, so a reviewer could
accidentally accept a change that never happened. - Auto-landed change pills no longer float away from the text they describe.
A pill, its margin mark, and "Jump to change" now always attach to the
document as it currently reads, instead of searching for text that a
replacement or deletion may have already removed — the most common way a
pill used to end up parked at the wrong spot, or nowhere at all, in a long
auto-accepted file. Several auto-landed edits inside the same fenced code
block now attach to their own distinct lines instead of collapsing onto one
position. - Several auto-landed changes no longer show a false "N proposals overlap
here — pick one" conflict. Landed changes have already landed; there is
nothing to choose between, and they now render through their own small
viewer instead of the pending-proposal conflict UI. - Viewing part of a long auto-landed file no longer corrupts what "seen"
means for the rest of it. A file's seen state is now one durable
checkpoint per file ("caught up through here"), not a set of per-change
hashes that could reshuffle and resurface old content as new whenever a
later change landed nearby. - An auto-landed preview pinned open no longer gets cleared out by
scroll-dwell while you're still looking at it. - A save you make yourself no longer shows up as an unread "Auto change" —
Stet now recognizes its own users' saves and quietly folds them into what's
already been seen. If your own save lands in the same window as an unread
Auto change, Stet still shows the difference but labels it neutrally and
won't offer a one-click Revert that could undo your own writing (Version
history is always there for a closer look). - In a shared workspace with Remote review set to Auto-accept, landing a
collaborator's edits can no longer overwrite work you did after they
forked. Stet now merges their changes into your current document piece by
piece instead of dropping in their whole file. If any one of their edits no
longer lines up cleanly with what you have now, none of that file's edits
land automatically; the whole file waits for you to review it by hand
instead of guessing. - Turning a loose file into a workspace ("Watch this folder") no longer
strands you with a dead sidebar if anything after the folder is created
fails to load. The folder still becomes a real, reviewable workspace and
the tab still switches over to it; a fallback message with a Retry button
appears if the file list itself couldn't be fetched. A single slow or
failed background check (like the branches list) no longer blanks the
whole file tree either. The "All folders" list also now stays current
while it's open, instead of only refreshing the next time you open it.
Added
- The file sidebar takes the keyboard. Click a file and it opens, but the
sidebar keeps the cursor: up and down walk the tree from there (opening each
file as you land on it), left and right close and open folders, Enter opens
a file in its own tab. The cursor stays put until you click into the
document, which is what hands the keyboard back to the editor.
Changed
- File moved out of the sidebar and into the app header at the top of the
window, where it belongs. The header is now always present rather than
appearing only once a second tab opens; the tab strip inside it still shows
only when there's more than one tab. - The sidebar's collapse/expand-all button has a new icon. The old pair of
chevrons met in the middle and drew a literal X, which read as "close". - The sidebar's view checkboxes ("Watched only", "Show CLAUDE.md") moved out
of the hamburger menu into a "Filters…" panel of their own, directly below
the breadcrumb. Two new toggles joined them: "AGENT.md" (off by default,
hides AGENT.md/AGENTS.md) and "HTML files" (on by default). A fifth,
"Only documents with changes", narrows the tree to files with a pending or
unseen change. A dot on the "Filters…" row shows whenever any toggle is
off its default, so a narrowed tree is never mistaken for the full one. - The Auto-changes popover now spells out the contract instead of showing
just a count and an icon: how many changes landed since you last caught
up, how many remain in this session, and a labeled Mark file caught up
button (replacing the bare checkmark). Dismissing a change from its pill is
now labeled Hide for now, and its tooltip says plainly that it comes
back next time unless the file is marked caught up — dismissing one no
longer durably marks it seen on its own.
Security
- The HTML-annotate viewer and the history timeline's word diff now sanitize
their rendered HTML before it reaches the page, through a dedicated
allowlist sanitizer. Markdown previews were already sanitized
(renderSafeMarkdown); the raw-HTML annotate path was the remaining route
where a<script>tag or anonerrorhandler in an opened HTML file (or a
proposal against it) could execute inside the webview. - The Content-Security-Policy's
connect-srcnow includes the feedback
endpoint, which the shipped CSP had locked out — feedback submissions from
the app were being blocked by the app's own policy.
Added
- Turning a loose document into a Stet workspace now lets you choose which
folder becomes the project root. Pick the file's immediate parent (as
before) or one of a couple of folders above it, instead of always rooting
at the immediate parent. - Open in AI agent now supports Claude Code in Claude Desktop and Codex in
the ChatGPT desktop app, opening the current workspace directly in either
visual agent. Stet remembers the last successfully launched agent and makes
it the primary one-click File-menu action; terminal Claude Code, Cursor, and
VS Code/Cline remain available in the secondary chooser. - Web review links: the owner of a Stet-hosted share can right-click any
markdown file and choose "Copy web review link" to mint a private URL
anyone can open in a browser to read the document and leave comments, no
install or account needed. Minting again for the same file rotates the
link (the old URL stops working; comments carry over). The Sharing & sync
step lists the workspace's active review links with their comment counts
(owners can revoke one inline) and shows reviewer comments, with the
quoted passage each comment is about, to every collaborator. - Sync visibility badges: a watched workspace's own row (in the breadcrumb
and the sidebar's "All folders" list) now shows a small icon naming who
else can see it. A two-person glyph marks a Stet-hosted share (with your
role); a branch glyph marks a GitHub share; an unshared folder shows
nothing. Per-file Google Drive sync badges are unchanged. - Sharing & sync management: the Controls lens's root folder step gained a
"Sharing & sync…" row leading to a new step that lists collaborators and
pending invites for a Stet-hosted share (owners can remove a collaborator
or revoke an invite inline; non-owners can leave), lets the owner invite
another collaborator, and lists every Google Drive-synced file in the
workspace with a link straight to its sync panel. - Profile section: the Stet account block (Controls, signed in) now shows a
click-to-edit display name, the current beta billing plan with a note that
billing is coming, a workspace-count/storage usage line, and a "Shared
workspaces" list that jumps straight to that workspace's Sharing step. All
of this degrades gracefully (the row just doesn't appear) if the sync
server hasn't deployed the corresponding endpoint yet. - Stet-hosted workspace sharing: Share Workspace now defaults to a private
Stet Sync remote, emails the collaborator a join code, and lets the
collaborator clone it through Join Shared Workspace without needing a
GitHub account. Existing GitHub shares keep working, and GitHub remains
available as the secondary "use your own GitHub" option. - The changes sidebar's toggle button now shows a small dot when it's
closed and the open document has changes to review: blue for pending
proposals, green for unseen auto-accepted changes. - Bigger, easier-to-hit folder disclosure chevrons in the file tree. Option-
click a chevron to fold that folder's whole subtree at once (Finder
convention), and a new collapse/expand-all button sits next to the
sidebar's hamburger menu. - Google Drive sync (experimental): link a markdown file to an existing
Google Doc or Google Sheet and push or pull it on demand. Credentials are
a Google service account key, pasted once in File > Google Drive Sync
Settings… and stored in the OS keychain — app-wide, not per workspace.
Linking is by pasting the URL of a Doc/Sheet already shared with the
service account's email as Editor (service accounts have no Drive storage
of their own, so creating a new file isn't offered). Linked files show a
cloud badge in the file tree (synced, local ahead, remote ahead, or
diverged), and a Drive panel on the file offers Push, Pull, and Unlink.
Pull never overwrites your file: remote edits arrive as a proposal in the
review sidebar, so rejecting a change and pushing makes your markdown the
source of truth. Sheets sync the file's first markdown table; prose around
it stays put. - Stet accounts (phase 1 of Stet-hosted sharing): sign in with an email
magic link from the sidebar's Controls lens. Enter your email, click the
link Stet sends you (or type the 6-digit code from the same email), and
the app holds your session in the OS keychain. The same session now powers
Stet-hosted sharing; GitHub sharing remains available. - Focus Mode (F8) dims everything but the paragraph you're writing;
Typewriter Mode (F9) keeps the caret vertically centered. Both live in
the View menu, remember their state, and respect Reduce Motion. - Export as HTML (File menu): a self-contained, styled HTML file of the
current document. Print (⌘P) prints just the document; PDF export comes
free via the macOS print dialog's "Save as PDF". - File > Revert To opens the document's version history panel.
- Outline (View menu): a collapsible headings list above the file tree;
click a heading to jump to it. Updates live as you write. - Smart Typography (Format menu, off by default since it changes what's
saved): curly quotes and … as you type. Em dashes are never
auto-inserted. Existing documents are never rewritten - it only affects
what you type while it's on. - Images: paste a screenshot, drag an image file in, or use Format >
Image… — the file is saved into anassets/folder next to the document
and referenced with a plain relative markdown link that renders inline.
Image files themselves stay out of review; only the markdown reference
does. Supported: png, jpg, gif, webp, svg, avif. - A full word-processor menu bar: Paragraph (headings, lists, quote, code
fences, table, horizontal line), Format (bold, italic, strikethrough,
inline code, clear formatting), View (sidebar toggle, zoom), Window, and
Help (searchable, with site links and Send Feedback) — all with visible
keyboard shortcuts, live checkmarks that track the cursor, and items that
grey out instead of hiding. A Review menu is scaffolded (greyed) for
accept/reject shortcuts. The app also gains a proper macOS app menu. - Edit menu extras: Paste as Plain Text (⇧⌘V), Copy as Markdown, Copy as
Plain Text. Find items are visible (greyed) with their shortcuts reserved. - File menu: Close (⌘W) closes the active tab; Save (⌘S) saves immediately.
- Task lists are now a first-class node in the editor schema (GFM
checkboxes round-trip properly instead of degrading to bullet lists). - Find and replace (⌘F / ⌥⌘F): an inline bar above the document with live
match count, next/previous, case toggle, and replace / replace all
(replace all is a single undo step). - Word count: View > Show Word Count toggles a quiet footer strip with
words and characters; shows selection counts when text is selected. The
preference persists across launches. - Link editing (⌘K or Format > Link…): a small popover on the selection to
add, edit, open, or remove a link. Clicking an existing link opens it for
editing; pasting a URL over selected text turns the selection into a link. - Pasting a table from Word or Google Docs now saves as a clean markdown
table. Those apps never mark header cells, which previously made pasted
tables embed as raw HTML; the first row is now promoted to a header at
paste time. - The Review menu is live: Accept Change (⌥⌘A) and Reject Change (⌥⌘R) act
on the highlighted change (or the first pending one), Accept All in File
lands everything pending, Next/Previous Change cycle through pills with
scroll-and-highlight, Insert AI Note drops an editable note at the
cursor, and Show Review Sidebar toggles the panel. Items grey out when
nothing is pending — a full review round-trip now works keyboard-only. - Google Drive sync (experimental): an agent that just created a Google Doc
or Sheet can link it to a workspace file itself, without the user pasting
a URL. Writing.stet/gdrive-link.jsonwith the file's workspace-relative
path and the Doc/Sheet URL links it in the background; a result file
reports success or a plain-language error, and the sidebar's sync badges
refresh automatically. Stet-aware workspace CLAUDE.md files now document
the protocol for agents.
Changed
- File > Google Drive Sync Settings… is now File > Google Drive Sync
Settings (Beta)… and lives next to Share Workspace… / Join Shared
Workspace…, the menu's other sync integrations, instead of the folder
and hook-command settings group.
Fixed
- The GitHub connection no longer vanishes after a rebuild + restart in
dev and ad-hoc builds. macOS binds keychain entries to the binary's code
signature, so a rebuilt binary was denied access to the token it stored
and the file copy had already been deleted. The token is now kept in the
keychain and in an app-local owner-only file at the same time (the same
fix the Google Drive service-account key got), so a keychain denial
falls back to the file instead of forcing the device flow again. - The left sidebar's open/closed state now persists across restarts, like
the right sidebar and the view toggles already did. Previously a fresh
app shell always reopened it, so closing it while reading a loose file
didn't stick — the next loose file you opened re-opened the sidebar
anyway. When the sidebar is closed and the active tab is a loose file, its
toggle button now shows a small muted dot, flagging that the loose-file
panel (path + watch/move actions) is behind it. - The file tree's right-click menu (Show in Finder, Folder settings…) no
longer gets clipped when it opens near the sidebar's edge — it now renders
through a portal instead of inside the sidebar's own stacking context, and
clamps itself back onto screen if it would overflow the right or bottom
edge. - Accepting a change could silently corrupt the document when it had moved
since the proposal was computed. Accepts spliced the proposed text in at
the hunk's original line numbers, so an earlier accept, ordinary typing,
or a version restore above the change shifted the target and the wrong
lines were replaced, with no error. Accepts are now anchored by content
in the backend: the exact proposed-over text is located in the current
document before anything is written, and when it can't be found (you
rewrote it) or appears in more than one place, the accept is refused
with a "That change was not applied" banner instead of guessing. Accept
All skips refused changes and reports how many. This also closes a race
where an autosave landing mid-accept could be overwritten. - A change you already accepted or rejected could come back as unreviewed.
Review decisions were keyed by a 32-bit hash of the diff's raw lines, so
two different changes could collide, and an incidental whitespace shift
(trailing spaces, CRLF) gave the same change a new identity that no
longer matched its record. Decisions are now keyed by a 64-bit digest of
what the change actually does (its old and new text, whitespace
normalized). Decisions recorded by older builds still count: both
identities are checked for one release. - Changes in table documents were invisible: a change pill would show a
count, but no pill sat next to the changed row, hovering showed no diff
text, and "Jump to change" did nothing. Change anchoring now understands
pipe-table rows (it searches by cell text, preferring each row's most
distinctive cell), and jumping to a change that can't be text-anchored
falls back to the change's position instead of a silent no-op. Documents
that are mostly tables — trackers, backlogs — now review like any other
document. - Release builds opened with an invisible (zero-size) main window after the
image support landed: enabling the asset protocol makes release builds
create the window with a 0x0 frame (dev builds were unaffected). The app
now detects an absurdly small frame at launch and on every
bring-to-front, restores the default size, and recenters. - Paths in the All folders list now sit below folder names, so long paths no
longer cover the active folder in a narrow sidebar. - Packaged builds no longer open to a blank white window when React's initial
commit takes longer than TipTap's one-millisecond editor cleanup timer. The
editor is now created after mount, so toolbars never receive an already-
destroyed editor instance. - The app could launch frozen with no visible window: startup read each
tray-watched workspace's config on the main thread, and macOS blocks that
read on a folder-permission prompt (fresh installs, or a rebuild with a
new signing identity) — freezing launch before the window ever drew.
Watcher rehydration and config reads now run off the launch path; the
tray menu fills in a moment after startup. A defensive frame heal also
restores the default window size if a zero-size frame ever appears. - The File > Open Recent and Open in AI Agent submenus were silently broken:
the menu-item lookup only searched top-level menus, so the nested submenus
were never found or populated. Lookups now walk the whole tree. - "Start a new project" on the welcome screen: name a project and Stet
creates the folder, a starter document seeded with an AI note, and a
Stet-aware CLAUDE.md, then opens it watched and ready to edit. No picker
dialogs, no separate CLAUDE.md offer step. - A demo-completion bridge: deciding the last pending proposal in the demo
workspace surfaces a banner offering to start your own project (or open
an existing folder), so the demo hands off into real work instead of
dead-ending.
Fixed
- A save of your own could come back as a phantom proposal and revert the
document. Stet's self-write suppression was purely time-based (1.5s), but
macOS can deliver file events later than that; a late event made Stet
treat its own save as an external edit — capturing it as a proposal and
restoring older content over the file. Self-writes are now also recognized
by content for 30 seconds, so a late event for Stet's own write can never
become a proposal or trigger a restore. This closes the standing
autosave revert/duplication loop (backlog
2026-05-13-autosave-content-duplication.md). - Self-write suppression now works under symlinked workspace paths even for
files that don't exist yet at note time (the note happens before the
write; canonicalization now falls back to the parent directory). - A save whose disk write succeeds but whose home-branch history commit
fails now surfaces the error instead of silently claiming success; the
editor keeps the content buffered and retries instead of losing it. - The autosave-vs-external-edit drop decision now keys on the pending
save's own target file rather than the currently selected tab, so a fast
tab switch can no longer let a stale buffer clobber a captured edit (or
drop a buffer for an unrelated file). - The demo's "Using with AI" and "Using with coworkers" guides pointed at
~/Documents/Stet Demo, but the demo unpacks to~/Downloads/Stet Demo.
The instructions now match the real location. - Google Drive sync (experimental) credentials no longer disappear across a
rebuild in dev. The app-local fallback copy was being deleted the moment
the OS keychain write succeeded, but keychain entries are bound to the
app binary's code signature, so a rebuilt dev build lost keychain access
and had no fallback left to read. Both stores are now kept in sync.
Security
- The feedback shared secret and receiver endpoint are now configurable via
env vars (VITE_FEEDBACK_SECRET,VITE_FEEDBACK_ENDPOINT) instead of
hardcoded in the client bundle. The feedback receiver also accepts a
second, previous secret (SHARED_SECRET_PREVIOUS) during a migration
window, so a secret rotation doesn't break in-flight clients.
Internal
- Replaced the stock Tauri+React+TypeScript template README with a real one:
what Stet is, dev quickstart, and pointers to CLAUDE.md and qa/. - Sync (fetch) and Push no longer run their libgit2/network work directly on
a Tauri async runtime thread — both were declaredasync fnbut called
blocking git code inline, which could still stall other in-flight command
work. A new per-repository coordinator (src-tauri/src/repo_coordinator.rs,
keyed by the canonical git worktree root) moves that work to a blocking
worker and guarantees the two can never mutate the same repository's git
state at once, while unrelated repositories stay independent. Every
.stet/*.jsonledger written throughfs_ops::write_json(config,
seen-state, acceptances, batch-pending, proposal-sync state, onboarding)
now writes via a same-directory temp file + atomic rename, so a crash or
force-quit mid-write can no longer leave a truncated ledger;
batch-pending.json's read-modify-write is also now serialized against
itself. This is the coordinator-primitive slice of the async-git-commands
backlog item; migrating the rest of the git-mutating command surface onto
the same coordinator is tracked separately. - Removed two orphaned frontend files nothing imported anymore: the old
Comment Callouts TipTap extension (src/lib/tiptap-comments.ts) and the
unusedChangeCardcomponent (src/components/ChangeCard.tsx), plus the
leftover.comment-calloutstyles inindex.css. AI notes
(src/lib/aiNote.ts) are the annotation mechanism that shipped instead. npm run build(and sotauri build) no longer type-checks test files:
tsconfig.jsonexcludessrc/**/*.test.ts(x)from the production tsc
pass. The new design-token drift test imports node builtins, which the
browser-targeted config has no types for — it broke the release build
whilenpm teststayed green.