From 994af0f48724e4305e6e424eb2e157c2120e58d9 Mon Sep 17 00:00:00 2001 From: Elias Meire Date: Mon, 28 Sep 2026 10:52:15 +0200 Subject: [PATCH] feat(editor): Match app builder idea pills to the assistant home suggestions (no-changelog) --- .../frontend/@n8n/i18n/src/locales/en.json | 11 +- .../components/InstanceAiConversation.vue | 100 ++++++++---------- 2 files changed, 52 insertions(+), 59 deletions(-) diff --git a/packages/frontend/@n8n/i18n/src/locales/en.json b/packages/frontend/@n8n/i18n/src/locales/en.json index 1b9dbe76328..1ddc103a36c 100644 --- a/packages/frontend/@n8n/i18n/src/locales/en.json +++ b/packages/frontend/@n8n/i18n/src/locales/en.json @@ -7717,9 +7717,14 @@ "instanceAi.backgroundTask.completed": "Background task finished", "instanceAi.backgroundTask.failed": "Background task failed", "instanceAi.input.newAppPlaceholder": "Describe the app you want to build...", - "instanceAi.newApp.example.todo": "A todo app that saves tasks in a data table", - "instanceAi.newApp.example.form": "A form that submits to one of my workflows and shows the reply", - "instanceAi.newApp.example.agentChat": "A chat page for one of my published agents", + "instanceAi.newApp.suggestions.eventRsvp.label": "Event RSVP page", + "instanceAi.newApp.suggestions.eventRsvp.prompt": "An RSVP page for our next community meetup. Guests pick the sessions they want to join, tell us about dietary needs, and get a confirmation email with a calendar invite.", + "instanceAi.newApp.suggestions.feedbackBoard.label": "Feedback board", + "instanceAi.newApp.suggestions.feedbackBoard.prompt": "A feedback board where customers suggest features and upvote ideas from others. My team marks ideas as planned or shipped, and the people who voted get notified.", + "instanceAi.newApp.suggestions.equipmentBooking.label": "Equipment booking", + "instanceAi.newApp.suggestions.equipmentBooking.prompt": "A booking app for our shared office gear. People reserve cameras, mics, and meeting kits by the hour, see what's free right now, and get a reminder to bring items back.", + "instanceAi.newApp.suggestions.onboardingHub.label": "Onboarding hub", + "instanceAi.newApp.suggestions.onboardingHub.prompt": "An onboarding hub for new hires. Each person gets a checklist for their first two weeks, their manager sees their progress, and HR hears about it when someone gets stuck.", "instanceAi.appBlueprint.title": "Review app blueprint", "instanceAi.appBlueprint.titleResolved": "App blueprint", "instanceAi.appBlueprint.titleExpired": "App blueprint (expired)", diff --git a/packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiConversation.vue b/packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiConversation.vue index 43a6eaed562..4668f619598 100644 --- a/packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiConversation.vue +++ b/packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiConversation.vue @@ -11,7 +11,7 @@ import { watch, } from 'vue'; import { storeToRefs } from 'pinia'; -import { N8nChatMessage, N8nIcon, N8nIconButton, N8nScrollArea, N8nText } from '@n8n/design-system'; +import { N8nChatMessage, N8nIconButton, N8nScrollArea, N8nText } from '@n8n/design-system'; import { useScroll } from '@vueuse/core'; import { useI18n } from '@n8n/i18n'; import type { @@ -62,7 +62,8 @@ import { stashPendingHandoffContext, type PendingComposerDraft, } from '../composables/useInstanceAiHandoff'; -import { INSTANCE_AI_PREFILL_TYPE_FALLBACK, type InstanceAiMessageAuthorship } from '../prefills'; +import type { InstanceAiMessageAuthorship } from '../prefills'; +import type { InstanceAiEmptyStateSuggestion } from '../emptyStateSuggestions'; import type { AppPreviewDiagnostics } from '../composables/useAppPreviewDiagnostics'; import { useAppThreadScope } from '@/features/apps/composables/useAppThreadScope'; import type { @@ -251,21 +252,44 @@ const hasAssistantResponse = computed(() => displayedMessages.some((m) => m.role // The new-app page: a bound app does not exist yet, so the composer asks for the idea first. const appScope = useAppThreadScope(); const isNewAppPage = computed(() => appScope !== null && !appScope.value.appId); -const NEW_APP_EXAMPLES = [ - { key: 'instanceAi.newApp.example.todo', icon: 'square-check' }, - { key: 'instanceAi.newApp.example.form', icon: 'file-text' }, - { key: 'instanceAi.newApp.example.agentChat', icon: 'message-circle' }, -] as const; -const showNewAppExamples = computed( - () => isNewAppPage.value && displayedMessages.length === 0 && !thread.isStreaming, +// Idea starters, rendered by the composer like the assistant home page's pills. Its own +// catalog version keeps their suggestion telemetry apart from the home page's. +const NEW_APP_SUGGESTIONS_VERSION = 'app-builder-v1'; +const NEW_APP_SUGGESTIONS: readonly InstanceAiEmptyStateSuggestion[] = [ + { + type: 'prompt', + id: 'event-rsvp', + icon: 'calendar', + labelKey: 'instanceAi.newApp.suggestions.eventRsvp.label', + promptKey: 'instanceAi.newApp.suggestions.eventRsvp.prompt', + }, + { + type: 'prompt', + id: 'feedback-board', + icon: 'messages-square', + labelKey: 'instanceAi.newApp.suggestions.feedbackBoard.label', + promptKey: 'instanceAi.newApp.suggestions.feedbackBoard.prompt', + }, + { + type: 'prompt', + id: 'equipment-booking', + icon: 'laptop', + labelKey: 'instanceAi.newApp.suggestions.equipmentBooking.label', + promptKey: 'instanceAi.newApp.suggestions.equipmentBooking.prompt', + }, + { + type: 'prompt', + id: 'onboarding-hub', + icon: 'clipboard-list', + labelKey: 'instanceAi.newApp.suggestions.onboardingHub.label', + promptKey: 'instanceAi.newApp.suggestions.onboardingHub.prompt', + }, +]; +const newAppSuggestions = computed(() => + isNewAppPage.value && displayedMessages.length === 0 && !thread.isStreaming + ? NEW_APP_SUGGESTIONS + : undefined, ); -function useNewAppExample(key: (typeof NEW_APP_EXAMPLES)[number]['key']) { - chatInputRef.value?.setPrefill({ - text: i18n.baseText(key), - prefillType: INSTANCE_AI_PREFILL_TYPE_FALLBACK, - }); - chatInputRef.value?.focus(); -} // True when at least one pending confirmation should occupy the chat-input // slot (questions, generic approvals, or domain/web-search access). Drives @@ -1040,23 +1064,6 @@ defineExpose({ @upgrade-click="goToUpgrade('instance-ai', 'upgrade-instance-ai')" @dismiss="creditBanner.dismiss()" /> -
- -
@@ -1091,6 +1098,10 @@ defineExpose({ :placeholder-key=" isNewAppPage ? 'instanceAi.input.newAppPlaceholder' : undefined " + :suggestions="newAppSuggestions" + :suggestion-catalog-version=" + newAppSuggestions ? NEW_APP_SUGGESTIONS_VERSION : undefined + " @submit="handleSubmit" @stop="handleStop" @dismiss-context-chip="dismissComposerContextChip" @@ -1112,8 +1123,6 @@ defineExpose({