mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-17 05:29:44 +02:00
feat(editor): Match app builder idea pills to the assistant home suggestions (no-changelog)
Some checks failed
CI: Python / Checks (push) Has been cancelled
Some checks failed
CI: Python / Checks (push) Has been cancelled
This commit is contained in:
parent
9536d15c94
commit
994af0f487
|
|
@ -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)",
|
||||
|
|
|
|||
|
|
@ -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()"
|
||||
/>
|
||||
<div
|
||||
v-if="showNewAppExamples"
|
||||
:class="$style.newAppExamples"
|
||||
data-test-id="instance-ai-new-app-examples"
|
||||
>
|
||||
<button
|
||||
v-for="(example, index) in NEW_APP_EXAMPLES"
|
||||
:key="example.key"
|
||||
type="button"
|
||||
:class="$style.newAppExample"
|
||||
:style="{ animationDelay: `${index * 50}ms` }"
|
||||
@click="useNewAppExample(example.key)"
|
||||
>
|
||||
<N8nIcon :icon="example.icon" :size="12" :class="$style.newAppExampleIcon" />
|
||||
<span>{{ i18n.baseText(example.key) }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<slot name="above-input" />
|
||||
<div :class="$style.inputSwap">
|
||||
<Transition name="input-swap">
|
||||
|
|
@ -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({
|
|||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
@use '../../shared/styles/prompt-suggestion-buttons' as promptSuggestions;
|
||||
|
||||
@property --instance-ai-artifacts-layout-width {
|
||||
syntax: '<length>';
|
||||
inherits: true;
|
||||
|
|
@ -1255,27 +1264,6 @@ defineExpose({
|
|||
line-height: var(--line-height--md);
|
||||
}
|
||||
|
||||
.newAppExamples {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: var(--spacing--2xs);
|
||||
margin-bottom: var(--spacing--xs);
|
||||
}
|
||||
|
||||
.newAppExample {
|
||||
@include promptSuggestions.prompt-suggestion-button;
|
||||
}
|
||||
|
||||
.newAppExampleIcon {
|
||||
@include promptSuggestions.prompt-suggestion-icon;
|
||||
|
||||
.newAppExample:hover &,
|
||||
.newAppExample:focus-visible & {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// The leaving child is detached from layout (see `.input-swap-leave-active`
|
||||
// below) so the slot follows the entering child's intrinsic height during
|
||||
// the cross-fade.
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user