Skip to content

Add Recent Drafts section and Drafts library tab#1219

Open
SajalChaplot wants to merge 11 commits into
mainfrom
ai_main_e7ce0f2d56ed4d349867
Open

Add Recent Drafts section and Drafts library tab#1219
SajalChaplot wants to merge 11 commits into
mainfrom
ai_main_e7ce0f2d56ed4d349867

Conversation

@SajalChaplot

@SajalChaplot SajalChaplot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

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-assets server action queries candidate-role assets across accessible libraries. A RecentDraftsSection component 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 a drafts tab type that filters assets by role === "generated" && status === "candidate", and the tab is pre-selected when navigating via the ?tab=drafts URL 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 optional libraryId filter and configurable limit.
  • 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 a drafts tab type and isDraftAsset helper; updates assetMatchesTab so the generated tab excludes drafts; reads the ?tab= URL param on mount and syncs it to state; passes includeCandidates: true when the drafts tab is active; adds the "Drafts" TabsTrigger to the UI; adds status field to the Asset type.

Edit in Builder  Preview


To clone this PR locally use the Github CLI with command gh pr checkout 1219

You can tag me at @BuilderIO for anything you want me to fix or change

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@builder-io-integration builder-io-integration Bot changed the title Update from the Builder.io agent Add Recent Drafts section and Drafts library tab Jun 16, 2026
@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Here's a visual recap of what changed:

Visual recap

Open the full interactive recap

Large diff — this recap is a summarized view (top files + schema/API deltas).

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

builder-io-integration[bot]

This comment was marked as outdated.

@SajalChaplot

Copy link
Copy Markdown
Contributor Author

@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.

@builder-io-integration

Copy link
Copy Markdown
Contributor

There was a problem with your request, please try again later. Error id: 13e28aa54d104e79ab012671e19434a6

@SajalChaplot

Copy link
Copy Markdown
Contributor Author

@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.

@builder-io-integration

Copy link
Copy Markdown
Contributor

Reviewed the two bot comments — agreed with both, fixed both in the Drafts tab query (library.tsx):

  1. Video drafts excludedmediaType is now omitted when viewing Drafts, so image and video drafts show.
  2. Only latest run showncandidateRunIds is now omitted on the Drafts tab, so all unsaved drafts appear, not just the most recent batch.

Typecheck passes.

@builder-io-integration

Copy link
Copy Markdown
Contributor

Both PR review comments were valid, and I fixed both in the Drafts tab query (templates/assets/app/routes/library.tsx):

  1. Video drafts excluded — the query applied a default mediaType: "image" even on the Drafts tab, hiding video drafts that RecentDraftsSection would show. Now mediaType is omitted (undefined) when viewing Drafts, so both image and video drafts appear.

  2. Only the latest run showncandidateRunIds was passed regardless of tab, narrowing Drafts to the most recent generation batch. Now it's omitted on the Drafts tab, so all unsaved drafts appear.

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.



🟡 Both PR comments addressed and replied to; fixes are committed locally and need a push to update the PR.

@SajalChaplot

Copy link
Copy Markdown
Contributor Author

@builderio-bot ci/link and format check is failing. fix that

@builder-io-integration

Copy link
Copy Markdown
Contributor

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 (list-draft-assets.ts and library.tsx).

I ran Prettier --write on them and verified they now pass prettier --check. The reformatted files are staged and will update the PR on the next push.



🟢 Fixed the Prettier formatting failures in list-draft-assets.ts and library.tsx; both now pass the format check.

builder-io-integration[bot]

This comment was marked as outdated.

@SajalChaplot

Copy link
Copy Markdown
Contributor Author

@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.

@steve8708

Copy link
Copy Markdown
Contributor

@SajalChaplot can you post in #product-agent-native-feedback this issue, we can tag Milos in there

@SajalChaplot SajalChaplot requested review from a team, Copilot and teleaziz and removed request for a team June 18, 2026 04:01

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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-assets action to fetch recent candidate generated assets across accessible libraries.
  • Added RecentDraftsSection component and rendered it on the home route.
  • Extended the library route to support a drafts tab and ?tab=drafts URL 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.

Comment on lines +685 to +687
useEffect(() => {
if (urlAssetTab) setAssetTab(urlAssetTab);
}, [urlAssetTab]);

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.

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.

Comment on lines 809 to +818
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.

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.

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.

Comment on lines +19 to +30
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: [] };

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.

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.

Comment thread templates/assets/app/components/create/RecentDraftsSection.tsx
Comment on lines +8 to +15
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" },

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.

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.

@SajalChaplot

Copy link
Copy Markdown
Contributor Author

@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)

@builder-io-integration

Copy link
Copy Markdown
Contributor

Addressed the latest round of review feedback:

  • Drafts tab fetched whole library → now filters server-side via role: "generated" + status: "candidate" query params.
  • Tab stuck on Drafts after URL param removed → effect resets with setAssetTab(urlAssetTab ?? "all").
  • list-draft-assets loaded all libraries when libraryId givenlibraryId pushed into the SQL WHERE.
  • Draft <video> thumbnails → added preload="metadata".
  • No test coverage → added list-draft-assets.spec.ts.

Also resolved the merge conflict in _index.tsx (main redesigned the Create page into a chat surface; Recent Drafts is re-integrated into the hero intro and renders nothing when there are no drafts). Typecheck, prettier, and the new test pass locally.

builder-io-integration[bot]

This comment was marked as outdated.

@steve8708

Copy link
Copy Markdown
Contributor

@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

@builder-io-integration

Copy link
Copy Markdown
Contributor

Addressed the two new review comments:

  • Drafts tab only showed one library → the standalone Library Drafts tab now uses the cross-library list-draft-assets action (all accessible libraries), matching the home Recent Drafts section; the library dropdown shows "All libraries" in that view. The embedded picker stays per-library by design.
  • Drafts tab could return videos in an image-only pickermediaType is no longer dropped; the embedded picker always filters by media type, so an image picker never surfaces video drafts. The all-media view is limited to the standalone Library page.

Typecheck and prettier pass locally.

@steve8708

Copy link
Copy Markdown
Contributor

@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)

@builder-io-integration

Copy link
Copy Markdown
Contributor

CI fix: both failing checks (Lint & format and Require changeset for publishable package changes) were caused by a stray tsup build artifact, packages/pinpoint/tsup.config.bundled_v7eyzmd03o.mjs, that got swept into a commit. It is not source code (a transient tsup-generated file) and is not on main. Removed it and added tsup.config.bundled_*.mjs to .gitignore so it cannot recur. No publishable package source actually changed in this PR, so the changeset requirement no longer applies.

@builder-io-integration builder-io-integration Bot 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.

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)

Comment thread templates/assets/app/routes/library.tsx Outdated
Comment on lines +851 to +854
const { data: globalDraftsData, isLoading: globalDraftsLoading } =
useActionQuery(
"list-draft-assets",
{ limit: 100 } as any,

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.

🟡 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.

Fix in Builder

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.

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.

Comment on lines 1436 to 1437
<Tabs
value={assetTab}

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.

🟡 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.

Fix in Builder

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.

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.

@steve8708

Copy link
Copy Markdown
Contributor

@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

@steve8708

Copy link
Copy Markdown
Contributor

@builderio-bot fix the merge conflicts

@builder-io-integration

Copy link
Copy Markdown
Contributor

Resolved the latest merge conflicts from main in _index.tsx and library.tsx:

  • Merged main's i18n (t(...)) for the library toolbar/tabs/empty-state with the Drafts feature; added brandKitDetail.drafts, assetPicker.allLibraries, assetPicker.noDrafts, and assetPicker.noMatchingDrafts keys to the en-US base (other locales fall back).
  • Kept both urlAssetTab and main's mcpChatBridgeActive.
  • Re-applied two fixes that were dropped during the merge: draft limit cap raised to 500 (action + standalone tab), and the active tab now syncs to the URL via setSearchParams, with tab excluded from the host-config key so toggling tabs no longer resets media type / query / library.
  • Recent Drafts is still wired into the redesigned chat-surface hero.

Typecheck, prettier, and the list-draft-assets test all pass locally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants