mirror of
https://github.com/nexu-io/open-design.git
synced 2026-05-31 19:04:39 +07:00
fix home example prompt presets
This commit is contained in:
parent
f677563313
commit
48f3404051
7 changed files with 136 additions and 15 deletions
|
|
@ -2556,11 +2556,29 @@ function homeHeroExamplePluginsForChip(
|
|||
plugins: InstalledPluginRecord[],
|
||||
locale: Locale,
|
||||
): InstalledPluginRecord[] {
|
||||
return plugins
|
||||
const presets = plugins
|
||||
.filter((plugin) => pluginMatchesExampleChip(plugin, chipId))
|
||||
.filter((plugin) => Boolean(pluginPresetQuery(plugin, locale)))
|
||||
.sort((a, b) => pluginPresetRank(b, chipId) - pluginPresetRank(a, chipId))
|
||||
.slice(0, 18);
|
||||
if (chipId === 'image') {
|
||||
return movePluginPresetToEnd(presets, 'example-hatch-pet');
|
||||
}
|
||||
return presets;
|
||||
}
|
||||
|
||||
function movePluginPresetToEnd(
|
||||
records: InstalledPluginRecord[],
|
||||
pluginId: string,
|
||||
): InstalledPluginRecord[] {
|
||||
const index = records.findIndex((record) => record.id === pluginId);
|
||||
if (index < 0 || index === records.length - 1) return records;
|
||||
const record = records[index]!;
|
||||
return [
|
||||
...records.slice(0, index),
|
||||
...records.slice(index + 1),
|
||||
record,
|
||||
];
|
||||
}
|
||||
|
||||
function pluginMatchesExampleChip(record: InstalledPluginRecord, chipId: string): boolean {
|
||||
|
|
@ -2579,8 +2597,10 @@ function pluginMatchesExampleChip(record: InstalledPluginRecord, chipId: string)
|
|||
return has('deck', 'slides', 'slide-deck') || hasPart('slide', 'deck');
|
||||
case 'hyperframes':
|
||||
return hasPart('hyperframes', 'hyperframe');
|
||||
case 'live-artifact':
|
||||
return has('live-artifact') || hasPart('live-artifact');
|
||||
case 'image':
|
||||
return (has('image') || hasPart('image-template')) && !hasPart('video', 'audio');
|
||||
return (has('image') || hasPart('image-template')) && !hasPart('video', 'audio', 'live-artifact');
|
||||
case 'video':
|
||||
return (has('video') || hasPart('video-template')) && !hasPart('hyperframes', 'audio');
|
||||
case 'audio':
|
||||
|
|
|
|||
|
|
@ -860,14 +860,16 @@ export function HomeView({
|
|||
|
||||
function useExamplePlugin(record: InstalledPluginRecord, chipId: string, promptText: string) {
|
||||
const projectKind = projectKindForExamplePlugin(record, chipId);
|
||||
requestActivePlugin(record, promptText, {
|
||||
projectKind,
|
||||
chipId,
|
||||
inputs: {},
|
||||
inputFields: [],
|
||||
queryTemplate: null,
|
||||
replaceWithoutConfirmation: true,
|
||||
});
|
||||
activePluginApplyRequestRef.current += 1;
|
||||
setActive(null);
|
||||
setActiveSkill(null);
|
||||
setFallbackProjectKind(projectKind);
|
||||
setPendingApplyId(null);
|
||||
setPendingChipId(null);
|
||||
setError(null);
|
||||
setPrompt(promptText);
|
||||
setPromptEditedByUser(false);
|
||||
focusPromptAtEnd();
|
||||
}
|
||||
|
||||
function removePluginContext(pluginId: string) {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,12 @@ afterEach(() => {
|
|||
cleanup();
|
||||
});
|
||||
|
||||
function makePlugin(id: string, mode: string, title = id): InstalledPluginRecord {
|
||||
function makePlugin(
|
||||
id: string,
|
||||
mode: string,
|
||||
title = id,
|
||||
extraTags: string[] = [],
|
||||
): InstalledPluginRecord {
|
||||
return {
|
||||
id,
|
||||
title,
|
||||
|
|
@ -36,7 +41,7 @@ function makePlugin(id: string, mode: string, title = id): InstalledPluginRecord
|
|||
version: '1.0.0',
|
||||
title,
|
||||
description: 'Plugin preset fixture',
|
||||
tags: [mode],
|
||||
tags: [mode, ...extraTags],
|
||||
od: {
|
||||
mode,
|
||||
useCase: {
|
||||
|
|
@ -167,6 +172,51 @@ describe('HomeHero intent rail', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('keeps Hatch Pet at the end of the image example presets', () => {
|
||||
const hatchPet = makePlugin('example-hatch-pet', 'image', 'Hatch Pet');
|
||||
const imagePoster = makePlugin('image-template-poster', 'image', 'Image Poster');
|
||||
const stoneInfographic = makePlugin('image-template-stone', 'image', 'Stone Infographic');
|
||||
renderHero({
|
||||
activeChipId: 'image',
|
||||
pluginOptions: [hatchPet, imagePoster, stoneInfographic],
|
||||
});
|
||||
|
||||
const presets = screen.getAllByTestId('home-hero-plugin-preset');
|
||||
expect(presets.map((preset) => preset.textContent)).toEqual([
|
||||
expect.stringContaining('Image Poster'),
|
||||
expect.stringContaining('Stone Infographic'),
|
||||
expect.stringContaining('Hatch Pet'),
|
||||
]);
|
||||
});
|
||||
|
||||
it('moves live artifact presets out of Image and into Live artifact examples', () => {
|
||||
const imagePoster = makePlugin('image-template-poster', 'image', 'Image Poster');
|
||||
const notionDashboard = makePlugin(
|
||||
'image-template-notion-team-dashboard-live-artifact',
|
||||
'image',
|
||||
'Notion-style Team Dashboard (Live Artifact)',
|
||||
['live-artifact'],
|
||||
);
|
||||
renderHero({
|
||||
activeChipId: 'image',
|
||||
pluginOptions: [imagePoster, notionDashboard],
|
||||
});
|
||||
|
||||
let presets = screen.getAllByTestId('home-hero-plugin-preset');
|
||||
expect(presets).toHaveLength(1);
|
||||
expect(presets[0]?.textContent).toContain('Image Poster');
|
||||
|
||||
cleanup();
|
||||
renderHero({
|
||||
activeChipId: 'live-artifact',
|
||||
pluginOptions: [imagePoster, notionDashboard],
|
||||
});
|
||||
|
||||
presets = screen.getAllByTestId('home-hero-plugin-preset');
|
||||
expect(presets).toHaveLength(1);
|
||||
expect(presets[0]?.textContent).toContain('Notion-style Team Dashboard (Live Artifact)');
|
||||
});
|
||||
|
||||
it('disables every visible chip while a plugin apply is in flight', () => {
|
||||
renderHero({ pendingPluginId: 'od-figma-migration', pendingChipId: 'figma' });
|
||||
for (const chip of HOME_HERO_CHIPS.filter((item) => item.group === 'create')) {
|
||||
|
|
|
|||
|
|
@ -581,6 +581,55 @@ describe('HomeView prompt handoff', () => {
|
|||
expect(screen.queryByRole('alert')).toBeNull();
|
||||
});
|
||||
|
||||
it('uses example preset cards as plain-text prompt fillers without binding plugin inputs', async () => {
|
||||
const fetchMock = vi.fn<typeof fetch>(async (url) => {
|
||||
if (typeof url === 'string' && url === '/api/plugins') {
|
||||
return new Response(JSON.stringify({ plugins: [WEB_PROTOTYPE_PLUGIN] }), {
|
||||
status: 200,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
});
|
||||
}
|
||||
if (typeof url === 'string' && url.includes('/apply')) {
|
||||
return new Response(JSON.stringify(WEB_PROTOTYPE_APPLY_RESULT), {
|
||||
status: 200,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
});
|
||||
}
|
||||
throw new Error(`unexpected fetch ${url}`);
|
||||
});
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
stubAnimationFrame();
|
||||
|
||||
render(
|
||||
<HomeView
|
||||
projects={[]}
|
||||
onSubmit={() => undefined}
|
||||
onOpenProject={() => undefined}
|
||||
onViewAllProjects={() => undefined}
|
||||
/>,
|
||||
);
|
||||
|
||||
await clearActiveTypeChip();
|
||||
fireEvent.click(await screen.findByTestId('home-hero-rail-prototype'));
|
||||
fireEvent.click(await screen.findByTestId('home-hero-plugin-preset'));
|
||||
|
||||
const input = screen.getByTestId('home-hero-input') as HTMLTextAreaElement;
|
||||
await waitFor(() => {
|
||||
expect(input.value).toBe(
|
||||
'Build a high-fidelity web prototype for product evaluators using the active project design system from the bundled web prototype seed.',
|
||||
);
|
||||
});
|
||||
expect(fetchMock.mock.calls.some(([url]) => (
|
||||
typeof url === 'string' && url.includes('/api/plugins/example-web-prototype/apply')
|
||||
))).toBe(false);
|
||||
expect(screen.queryByTestId('home-hero-active-type-chip')).toBeNull();
|
||||
expect(screen.queryByTestId('plugin-inputs-form')).toBeNull();
|
||||
expect(screen.queryByTestId('home-hero-prompt-slot-fidelity')).toBeNull();
|
||||
expect(screen.queryByTestId('home-hero-prompt-slot-artifactKind')).toBeNull();
|
||||
expect(screen.queryByTestId('home-hero-prompt-slot-designSystem')).toBeNull();
|
||||
expect(screen.queryByTestId('home-hero-prompt-slot-template')).toBeNull();
|
||||
});
|
||||
|
||||
it('binds the Home rail Live artifact chip with live-artifact metadata and applies it on submit', async () => {
|
||||
const fetchMock = vi.fn<typeof fetch>(async (url) => {
|
||||
if (typeof url === 'string' && url === '/api/plugins') {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
"surface": "image",
|
||||
"preview": {
|
||||
"type": "image",
|
||||
"poster": "https://raw.githubusercontent.com/joeylee12629-star/open-design/feat/prompt-template-live-artifact-dashboard/prompt-templates/image/notion-team-dashboard-live-artifact.preview.png"
|
||||
"poster": "https://raw.githubusercontent.com/nexu-io/open-design/main/prompt-templates/image/notion-team-dashboard-live-artifact.preview.png"
|
||||
},
|
||||
"useCase": {
|
||||
"query": {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue