Add Recent Drafts section and Drafts library tab#1219
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Here's a visual recap of what changed:
Open the full interactive recap
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
@builderio-bot look at latest PR feedback and fix anything you agree with. Be skeptical. Reply to every comment if you fixed it or not and why. |
|
There was a problem with your request, please try again later. Error id: |
|
@builderio-bot look at latest PR feedback and fix anything you agree with. Be skeptical. Reply to every comment if you fixed it or not and why. |
|
Reviewed the two bot comments — agreed with both, fixed both in the Drafts tab query (
Typecheck passes. |
|
Both PR review comments were valid, and I fixed both in the Drafts tab query (
I replied to each review comment explaining the fix, posted a PR summary, and typecheck passes. The changes haven't been pushed yet — they'll go up on the next push. ⠀ |
|
@builderio-bot ci/link and format check is failing. fix that |
|
Only the Lint & format check was failing — there's no separate "link" check (the user meant "lint"). The failure was Prettier formatting on my two changed files ( I ran Prettier ⠀ |
|
@steve8708 can you share how do I generate a deployed preview. I am trying to connect Builder account in fusion preview, but it is throwing error, so I am unable to test this change. I need to generate image/asset for this one. |
|
@SajalChaplot can you post in #product-agent-native-feedback this issue, we can tag Milos in there |
There was a problem hiding this comment.
Pull request overview
Adds UX affordances for revisiting unsaved AI-generated “draft” assets by surfacing a “Recent Drafts” panel on the home screen and introducing a dedicated “Drafts” tab in the Library view, backed by a new list-draft-assets action.
Changes:
- Added
list-draft-assetsaction to fetch recent candidate generated assets across accessible libraries. - Added
RecentDraftsSectioncomponent and rendered it on the home route. - Extended the library route to support a
draftstab and?tab=draftsURL selection.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| templates/assets/app/routes/library.tsx | Adds drafts tab type, draft filtering helper, URL param tab selection, and tab UI. |
| templates/assets/app/routes/_index.tsx | Renders the new Recent Drafts section below the home prompt box. |
| templates/assets/app/components/create/RecentDraftsSection.tsx | New component showing up to 5 recent drafts with thumbnails/skeleton states and link to Drafts tab. |
| templates/assets/actions/list-draft-assets.ts | New read-only action querying candidate generated assets across accessible libraries, newest-first. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| useEffect(() => { | ||
| if (urlAssetTab) setAssetTab(urlAssetTab); | ||
| }, [urlAssetTab]); |
There was a problem hiding this comment.
Fixed. The effect now resets to "all" when the tab param is removed: setAssetTab(urlAssetTab ?? "all"). Manual tab selection still works because urlAssetTab is memoized on the search params and only changes when the URL tab param changes.
| const assetsParams = useMemo( | ||
| () => ({ | ||
| libraryId: selectedLibraryId, | ||
| mediaType, | ||
| // Drafts can be images or videos, so don't constrain by media type there. | ||
| mediaType: viewingDrafts ? undefined : mediaType, | ||
| query: query.trim() || undefined, | ||
| includeCandidates: | ||
| mediaType === "image" && visibleCandidateRunIds.length > 0, | ||
| viewingDrafts || | ||
| (mediaType === "image" && visibleCandidateRunIds.length > 0), | ||
| // The Drafts tab shows every unsaved draft, not just the latest run batch. |
There was a problem hiding this comment.
Fixed. When viewingDrafts is true the request now sends role: "generated" + status: "candidate", so filtering happens in the SQL query rather than client-side over the full library.
| const accessibleLibraries = await db | ||
| .select({ id: schema.assetLibraries.id }) | ||
| .from(schema.assetLibraries) | ||
| .where( | ||
| and( | ||
| accessFilter(schema.assetLibraries, schema.assetLibraryShares), | ||
| isNull(schema.assetLibraries.archivedAt), | ||
| ), | ||
| ); | ||
| let libraryIds = accessibleLibraries.map((row) => row.id); | ||
| if (libraryId) libraryIds = libraryIds.filter((id) => id === libraryId); | ||
| if (!libraryIds.length) return { count: 0, assets: [] }; |
There was a problem hiding this comment.
Fixed. libraryId is now pushed into the SQL WHERE (eq(assetLibraries.id, libraryId)) alongside the access filter, so we no longer load every accessible library and filter in memory.
| export default defineAction({ | ||
| description: | ||
| "List unsaved draft generations (generated candidate assets) across accessible libraries, newest first.", | ||
| schema: z.object({ | ||
| libraryId: z.string().optional(), | ||
| limit: z.coerce.number().int().min(1).max(100).optional(), | ||
| }), | ||
| http: { method: "GET" }, |
There was a problem hiding this comment.
Fixed. Added actions/list-draft-assets.spec.ts covering schema defaults, limit coercion, and out-of-range rejection (mirroring list-assets.spec.ts). Passes locally.
|
@builderio-bot look at latest PR feedback and fix anything you agree with. Be skeptical. Reply to every comment (directly on the comment thread of each comment) if you fixed it or not and why. then check back every 2 minutes on a loop and see if any new feedback posted, until at least 20 minutes go by without any new feedback posted we want to address, including making sure CI passes too and no merge conflicts (make sure code is mergeable) |
|
Addressed the latest round of review feedback:
Also resolved the merge conflict in |
|
@builderio-bot look at latest PR feedback and fix anything you agree with. Be skeptical. Reply to every comment (directly on the comment thread of each comment) if you fixed it or not and why |
…e7ce0f2d56ed4d349867
|
Addressed the two new review comments:
Typecheck and prettier pass locally. |
|
@builderio-bot look at latest PR feedback and fix anything you agree with. Be skeptical. Reply to every comment (directly on the comment thread of each comment) if you fixed it or not and why. then check back every 2 minutes on a loop and see if any new feedback posted, until at least 20 minutes go by without any new feedback posted we want to address, including making sure CI passes too and no merge conflicts (make sure code is mergeable) |
…e7ce0f2d56ed4d349867
|
CI fix: both failing checks (Lint & format and Require changeset for publishable package changes) were caused by a stray tsup build artifact, |
There was a problem hiding this comment.
Builder reviewed your changes and found 2 potential issues 🟡
Review Details
This incremental update meaningfully improves the Drafts feature: the previously reported single-library mismatch and embedded picker media-type leak are now fixed, and I resolved both earlier review threads before continuing. The standalone Library Drafts tab now has a distinct global-drafts path, while embedded pickers stay scoped to the host’s requested library/media type.
Risk remains standard because these changes affect shared asset-listing behavior, picker routing, and a new cross-library action. I found two new medium-severity issues in the follow-up implementation. First, the standalone “all drafts” view is now hard-capped to the newest 100 rows, so older drafts silently disappear and become unsearchable despite the UI/comments describing it as the global drafts view. Second, the new tab query-param support is only read from the URL, not written back when users change tabs, so the visible tab and URL diverge after interaction; refreshing or sharing the URL can reopen the wrong tab.
The overall approach is close, but the global Drafts UX still needs its result set and URL state to match the feature’s user-facing behavior.
🧪 Browser testing: Will run after this review (PR touches UI code)
| const { data: globalDraftsData, isLoading: globalDraftsLoading } = | ||
| useActionQuery( | ||
| "list-draft-assets", | ||
| { limit: 100 } as any, |
There was a problem hiding this comment.
🟡 Standalone Drafts tab only loads the newest 100 drafts
The standalone global Drafts view now calls list-draft-assets with a hard-coded limit: 100, and the action schema also caps requests at 100. Users with more than 100 draft candidates silently lose access to older drafts, and those older drafts are also unsearchable from this view.
There was a problem hiding this comment.
Fixed. Raised the cap: list-draft-assets schema now allows limit up to 500 and the standalone Drafts tab requests limit: 500 (newest-first). This keeps the query bounded for performance while making truncation unlikely in practice. Full pagination/infinite-scroll would be a larger follow-up and is out of scope for this PR.
| <Tabs | ||
| value={assetTab} |
There was a problem hiding this comment.
🟡 Draft tab state is read from the URL but never written back
The new tab query param is parsed and used to initialize/reset assetTab, but clicking a tab only calls setAssetTab(...) and never updates the URL. After switching away from /library?tab=drafts, refresh/share/back-forward can reopen the wrong tab because the URL and visible state drift apart.
There was a problem hiding this comment.
Fixed. Clicking a tab now writes the ?tab= param via setSearchParams(..., { replace: true }) so the URL stays in sync (survives refresh/share and matches the home-page deep link). To avoid a regression, the tab param is excluded from the host-config key (hostParamsKey), so toggling tabs no longer retriggers the effect that resets media type / query / library from the URL.
|
@builderio-bot look at latest PR feedback and fix anything you agree with. Be skeptical. Reply to every comment (directly on the comment thread of each comment) if you fixed it or not and why also fix merge conflict |
|
@builderio-bot fix the merge conflicts |
|
Resolved the latest merge conflicts from
Typecheck, prettier, and the |
Summary
Adds a "Recent Drafts" section below the home prompt box showing the 5 most recently AI-generated candidate assets, and introduces a new "Drafts" tab in the Library view to house all unsaved draft generations.
Problem
There was no quick way for users to revisit AI-generated assets that hadn't been saved yet. Unsaved draft generations were mixed in with other assets in the library with no dedicated view, making them hard to find.
Solution
A new
list-draft-assetsserver action queries candidate-role assets across accessible libraries. ARecentDraftsSectioncomponent renders the 5 most recent drafts below the home prompt box with a "View all drafts" link pointing to the library's new Drafts tab. The library route gains adraftstab type that filters assets byrole === "generated" && status === "candidate", and the tab is pre-selected when navigating via the?tab=draftsURL parameter.Old UX
Screen.Recording.2026-06-18.at.9.32.15.AM.mov
###Updated UX
Screen.Recording.2026-06-18.at.9.25.12.AM.mov
Key Changes
actions/list-draft-assets.ts– New server action that fetches unsaved draft assets (role = "generated",status = "candidate") across accessible libraries, ordered newest first, with an optionallibraryIdfilter and configurablelimit.RecentDraftsSection.tsx– New React component that displays up to 5 recent drafts as a thumbnail grid with skeleton loading states, image/video thumbnail support, and a "View all drafts" button linking to/library?tab=drafts._index.tsx– Renders<RecentDraftsSection />below the prompt box on the home page.library.tsx– Adds adraftstab type andisDraftAssethelper; updatesassetMatchesTabso thegeneratedtab excludes drafts; reads the?tab=URL param on mount and syncs it to state; passesincludeCandidates: truewhen the drafts tab is active; adds the "Drafts"TabsTriggerto the UI; addsstatusfield to theAssettype.To clone this PR locally use the Github CLI with command
gh pr checkout 1219You can tag me at @BuilderIO for anything you want me to fix or change