From 801b93a6300c1f9f60890b764faad3b1f5f5d673 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 18 Oct 2026 14:35:07 +0000 Subject: [PATCH 1/6] feat(plugin-chatbot): the drafted result's Preview button appears for any draft kind (objectui#12968) A drafted tool result whose items include no app now draws Preview when the host passes the new `onPreviewDraft`; the click hands the host the drafted items and the package id (the app's route segment). A draft set that includes an app keeps `onPreviewDraftApp`, unchanged. Preview publishes nothing. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB --- .../plugin-chatbot/src/ChatbotEnhanced.tsx | 56 +++++-- ...botEnhanced.draftPreviewAny-12968.test.tsx | 152 ++++++++++++++++++ 2 files changed, 198 insertions(+), 10 deletions(-) create mode 100644 packages/plugin-chatbot/src/__tests__/ChatbotEnhanced.draftPreviewAny-12968.test.tsx diff --git a/packages/plugin-chatbot/src/ChatbotEnhanced.tsx b/packages/plugin-chatbot/src/ChatbotEnhanced.tsx index 286768dd2d..2e650291b5 100644 --- a/packages/plugin-chatbot/src/ChatbotEnhanced.tsx +++ b/packages/plugin-chatbot/src/ChatbotEnhanced.tsx @@ -695,6 +695,22 @@ export interface ChatbotEnhancedProps extends React.HTMLAttributes void; /** Label for the preview-draft action (default "Preview"). */ previewDraftLabel?: string; + /** + * objectui#12968 (objectstack#24324 ruling A, preview-first) — preview ANY + * drafted result, not only a drafted app. When provided, a drafted tool + * result whose items include no `app` and whose owning `packageId` is known + * renders a Preview button beside Publish and Review; the click hands the + * host the drafted `{ type, name }` items and the package id as the app's + * route segment (ADR-0048). The host resolves the affected surface (a page, + * the object's list on a view, the object's default list) and opens it in + * draft preview (`?preview=draft`). A draft set that includes an app keeps + * {@link ChatbotEnhancedProps.onPreviewDraftApp}, unchanged. Preview never + * publishes: publish stays the human gate (ADR-0033). + */ + onPreviewDraft?: ( + items: Array<{ type: string; name: string }>, + opts: { appSegment: string }, + ) => void; /** * ADR-0037 Live Canvas: notifies the host whenever AI-authored draft * artifacts land in the conversation (build-progress items + drafted @@ -1932,6 +1948,7 @@ const ChatbotEnhanced = React.forwardRef( getArtifactAction, onPreviewDraftApp, previewDraftLabel = 'Preview', + onPreviewDraft, onDraftArtifacts, onBuildMaterialized, onPlanApprovalPendingChange, @@ -2981,7 +2998,7 @@ const ChatbotEnhanced = React.forwardRef( {tool.draftReview && tool.draftReview.items.length > 0 && (onReviewDraft || - (onPublishDrafts && tool.draftReview.packageId)) ? ( + ((onPublishDrafts || onPreviewDraft) && tool.draftReview.packageId)) ? ( <> {/* flex-wrap so the action buttons drop to a new line on a narrow (mobile) row instead of forcing horizontal scroll. */} @@ -3027,19 +3044,38 @@ const ChatbotEnhanced = React.forwardRef( ) : null} {/* ADR-0037: drafted-app preview — see it as-if-published - without publishing. Only when the draft set includes an - app (the canvas previews whole apps, not single items). - Hidden once the package has no pending drafts (live - count when available, else this session's history). */} - {onPreviewDraftApp && !draftIsPublished + without publishing. A draft set that includes an app + previews the app (the canvas previews whole apps). + objectui#12968 (objectstack#24324 ruling A): any other + draft previews too, through `onPreviewDraft`, which + opens the affected surface; it needs the package id, + the app's route segment. Hidden once the package has + no pending drafts (live count when available, else this + session's history). */} + {!draftIsPublished ? (() => { - const app = tool.draftReview!.items.find((i) => i.type === 'app'); - return app ? ( + const review = tool.draftReview!; + const app = review.items.find((i) => i.type === 'app'); + if (app) { + return onPreviewDraftApp ? ( + + ) : null; + } + const appSegment = review.packageId; + return onPreviewDraft && appSegment ? (