feat(projects): fix cancelling, track shared projects, flag unreachable pipelines - #2790
Draft
camielvs wants to merge 7 commits into
Conversation
🎩 PreviewA preview build has been created at: |
This was referenced Sep 22, 2026
Collaborator
Author
camielvs
force-pushed
the
09-22-feat_projects_that_can_be_shared_and_cancelled
branch
2 times, most recently
from
September 23, 2026 00:32
29c5d18 to
d31482b
Compare
This was referenced Sep 23, 2026
camielvs
force-pushed
the
09-22-feat_autofit_the_canvas_while_an_agent_edits
branch
from
September 23, 2026 20:57
a7d6d5d to
2284c78
Compare
camielvs
force-pushed
the
09-22-feat_projects_that_can_be_shared_and_cancelled
branch
from
September 23, 2026 20:57
1007fa7 to
0847afe
Compare
camielvs
force-pushed
the
09-22-feat_autofit_the_canvas_while_an_agent_edits
branch
from
September 23, 2026 22:01
2284c78 to
ddd8377
Compare
camielvs
force-pushed
the
09-22-feat_projects_that_can_be_shared_and_cancelled
branch
from
September 23, 2026 22:01
0847afe to
817fb36
Compare
This was referenced Sep 23, 2026
Draft
This was referenced Sep 23, 2026
camielvs
force-pushed
the
09-22-feat_autofit_the_canvas_while_an_agent_edits
branch
from
September 23, 2026 23:00
ddd8377 to
4dce473
Compare
camielvs
force-pushed
the
09-22-feat_projects_that_can_be_shared_and_cancelled
branch
2 times, most recently
from
September 23, 2026 23:36
cbd78d9 to
37c0312
Compare
camielvs
force-pushed
the
09-22-feat_autofit_the_canvas_while_an_agent_edits
branch
from
September 23, 2026 23:36
4dce473 to
f5283a0
Compare
camielvs
force-pushed
the
09-22-feat_projects_that_can_be_shared_and_cancelled
branch
from
September 23, 2026 23:56
37c0312 to
74c9c32
Compare
camielvs
force-pushed
the
09-22-feat_autofit_the_canvas_while_an_agent_edits
branch
from
September 23, 2026 23:56
f5283a0 to
0b3f9d7
Compare
…le pipelines Three things, all downstream of projects becoming shareable by URL. The New Project dialog could not be cancelled. Blurring the autofocused name field mounted the validation alert between mousedown and mouseup, which grew the vertically-centred dialog and moved Cancel out from under the pointer, so no click was ever produced. The complaint now waits for a press of Create, which is also what asks for it, so Create stops being disabled while the name is empty. Rename had a verbatim copy of both. A shared project was unfindable once the link was gone, because the projects page asks the backend for the ones the caller created and nothing recorded a visit. Opening a project now records it, starring one keeps it past the hundred-visit cap, and the projects page grows a "Shared with me" section for the ones somebody else made. All local: the backend has no notion of who a project was shared with, so useSharedProjects is the single seam to replace if it ever does. A browser-held pipeline cannot travel with the project that lists it. Its row looked ordinary and fully clickable, and opening it failed deep in useLoadSpec, leaving a red error icon and a Bugsnag report for an entirely expected situation. Rows now say "Pipeline — not in this browser" and grey out, on whether this browser actually holds the pipeline rather than on who added it — which also covers your own pipeline seen from a second device. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e, add a way out of Tangent A recorded registry id was tried first but fell back to the name, which across browsers is a coincidence rather than a match: a project is shared, the pointer arrives at a browser holding an unrelated pipeline of the same name, and the name fallback claims it. Where the resource recorded a name-identity that opened the wrong pipeline outright; where it recorded an id it reported the row as available and then failed on click. An id is now the whole answer, and a pointer that recorded no id is trusted by name only in the browser that wrote it. The projects page now carries a Pinned section above My Projects, driven by pins alone rather than by pins and visits together, and projects are pinned and unpinned from the card's own menu as well as from the two project headers. Visits are still recorded, but they feed the existing Recently viewed list rather than this page. Tangent had no way back to the projects list except the logo, which leaves Tangent altogether. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…strip A pinned project of the caller's own appeared twice on one screen, once under Pinned and again in My Projects. Pinning now moves a project rather than copying it: the list leaves out whatever is pinned, so each project is on screen once whether it was pinned from the list or arrived by a shared link and was never in the list at all. The list's count follows. Pinned is also no longer a second wall of cards. It is a row of chips around 38px tall — folder, name, what is in it, and an unpin — so the section costs a line rather than a card row however few things are in it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The compact strip made a pinned project a second-class copy of itself: no menu, no resource counts, no dates. Pinned projects are ordinary cards again, sorted to the front of the single grid and marked with a pin. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…paged in The tail count added every pinned project to the total, so a pinned project of the user's own inflated the total by one until its page was fetched. Authorship answers it without the page: the user's own are already counted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Deleting a project left its links behind: it stayed in Recently Viewed, and following one opened its Tangent page with everything apparently intact. - The delete invalidated the detail query instead of removing it, so the next page to mount it was served the cached project in full and only then refetched its way to an error. - Recently Viewed and Favourites are local records the backend cannot prune, so the delete prunes them. Recent lists now also update in the tab that wrote them, rather than only in the others. - The Tangent page never asked whether the project existed. A deleted one has no workspace to take a Tangent url from, so it waited on a spinner forever. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
camielvs
force-pushed
the
09-22-feat_autofit_the_canvas_while_an_agent_edits
branch
from
September 24, 2026 00:14
0b3f9d7 to
67a04b0
Compare
camielvs
force-pushed
the
09-22-feat_projects_that_can_be_shared_and_cancelled
branch
from
September 24, 2026 00:14
74c9c32 to
e5a678c
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Four things, all downstream of projects becoming shareable by URL.
1. The New Project dialog could not be cancelled
Not a veto — a layout shift. The Name field is
autoFocused, so pressing themouse on Cancel blurred it,
onBlursetnameTouched, and a ~50px<Alert>mounted between mousedown and mouseup.
DialogContentistop-[50%] translate-y-[-50%], so the panel grew in both directions and thefooter slid out from under the cursor. A
clickneeds mousedown and mouseup onthe same element, so none was ever produced.
RenameProjectDialogwas averbatim copy of the same pattern.
The complaint now waits for a press of Create — which is also what asks for it,
so Create stops being disabled while the name is empty (a disabled button fires
no submit, and the message would be unreachable).
Measured in a real browser, one Cancel click:
2. Resolving a local pipeline by name was a wrong-pipeline hazard
A pointer recorded a registry id, but resolution fell back to the pipeline's
name when the id did not resolve. Within one browser that is merely
imprecise. Across browsers it is wrong: a project is shared, the pointer lands
in a browser holding an unrelated pipeline of the same name, and the fallback
claims it.
name/identity, that opened the wrongpipeline, silently;
id/identity, the row read as available and then failedon click.
A recorded id is now the whole answer: an id that does not resolve means the
pipeline is not here. A pointer that recorded no id has only ever had its name,
so it is still resolved by name — but trusted only in the browser that wrote it,
which is
resource.createdBy. Unknown authorship stays trusted, so single-userbackends are unaffected.
Two existing tests pinned the old fallback; both are replaced by tests asserting
the new rule, and the cross-browser collision is now covered directly.
3. Pipelines that cannot travel with the project listing them
Rows now read "Pipeline — not in this browser" and grey out, matching the
"Backend pipeline — not supported yet"row directly beside them. Remove stillworks, because unopenable is not unremovable. Previously such a row opened a tab
that collapsed into a red error icon and filed a Bugsnag report for an entirely
expected situation.
Only a definite negative greys a row out — while the lookup runs, rows are left
alone rather than flickering.
4. Pinning, and a way out of Tangent
/projectsonly lists what the caller created, so a shared project was gone withthe link.
of the one grid and carries a pin in place of its folder icon. It stays an
ordinary card — ⋮ menu, resource counts and dates all intact — and the list
leaves out whatever is pinned, so each project is on screen exactly once. A
project that arrived by a shared link was never in the list to begin with, and
simply joins the front. "Showing N of M" follows.
project may be someone else's.
so
usePinnedProjectsis deliberately the single seam to replace if it evergrows a server side. They ride on the existing favourites store, so a pinned
project also appears under Favourites.
rather than this page.
out was the logo, which leaves Tangent altogether rather than going up one
level.
5. A deleted project that would not go away
Deleting a project left its links behind, and following one opened its Tangent
page with everything apparently intact.
invalidated query keeps its data, so the next page to mount it was served the
cached project in full and only then refetched its way to an error. It now
removes it.
the delete prunes them. Recent lists also update in the tab that wrote them —
storageevents only ever reached the other tabs.resolves no workspace to take a Tangent url from, so it sat on
"Loading project…" indefinitely; it now says the project is gone and offers
the way back.
last-known name in the cache, which was enough to put it straight back on the
list.
Verified against the local backend: created a project, opened it, deleted it
from its card, and confirmed it is absent from Recently Viewed and that its url
reads "Project not found" rather than spinning.
Testing
Full suite green — 313 files, 3178 passed.
Verified in the browser against the local backend: one-click cancel; the card
menu reading
Pin project / Details / Share project / Delete project; a pinnedproject moving to the head of the grid, appearing once, keeping its menu,
counts and dates, and carrying the pin marker; a genuinely absent local pipeline
greyed with its message; and the back link landing on
/projects.jsdom cannot catch item 1 at all — it has no layout, so
userEvent.clickdispatches at the element reference wherever it has "moved".
🤖 Generated with Claude Code