mirror of
https://github.com/langgenius/dify.git
synced 2025-11-24 08:52:43 +00:00
361 lines
11 KiB
TypeScript
361 lines
11 KiB
TypeScript
import type { Meta, StoryObj } from '@storybook/nextjs'
|
|
import { useState } from 'react'
|
|
|
|
// Mock component to avoid complex initialization issues
|
|
const PromptEditorMock = ({ value, onChange, placeholder, editable, compact, className, wrapperClassName }: any) => {
|
|
const [content, setContent] = useState(value || '')
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
setContent(e.target.value)
|
|
onChange?.(e.target.value)
|
|
}
|
|
|
|
return (
|
|
<div className={wrapperClassName}>
|
|
<textarea
|
|
className={`w-full resize-none outline-none ${compact ? 'text-[13px] leading-5' : 'text-sm leading-6'} ${className}`}
|
|
value={content}
|
|
onChange={handleChange}
|
|
placeholder={placeholder}
|
|
disabled={!editable}
|
|
style={{ minHeight: '120px' }}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const meta = {
|
|
title: 'Base/Data Entry/PromptEditor',
|
|
component: PromptEditorMock,
|
|
parameters: {
|
|
layout: 'centered',
|
|
docs: {
|
|
description: {
|
|
component: 'Rich text prompt editor built on Lexical. Supports variable blocks, context blocks, and slash commands for inserting dynamic content. Use `/` or `{` to trigger component picker.\n\n**Note:** This is a simplified version for Storybook. The actual component uses Lexical editor with advanced features.',
|
|
},
|
|
},
|
|
},
|
|
tags: ['autodocs'],
|
|
argTypes: {
|
|
value: {
|
|
control: 'text',
|
|
description: 'Editor content',
|
|
},
|
|
placeholder: {
|
|
control: 'text',
|
|
description: 'Placeholder text',
|
|
},
|
|
editable: {
|
|
control: 'boolean',
|
|
description: 'Whether the editor is editable',
|
|
},
|
|
compact: {
|
|
control: 'boolean',
|
|
description: 'Compact mode with smaller text',
|
|
},
|
|
className: {
|
|
control: 'text',
|
|
description: 'CSS class for editor content',
|
|
},
|
|
wrapperClassName: {
|
|
control: 'text',
|
|
description: 'CSS class for editor wrapper',
|
|
},
|
|
},
|
|
} satisfies Meta<typeof PromptEditorMock>
|
|
|
|
export default meta
|
|
type Story = StoryObj<typeof meta>
|
|
|
|
// Interactive demo wrapper
|
|
const PromptEditorDemo = (args: any) => {
|
|
const [value, setValue] = useState(args.value || '')
|
|
|
|
return (
|
|
<div style={{ width: '600px' }}>
|
|
<div className="min-h-[120px] rounded-lg border border-gray-300 p-4">
|
|
<PromptEditorMock
|
|
{...args}
|
|
value={value}
|
|
onChange={(text: string) => {
|
|
setValue(text)
|
|
console.log('Content changed:', text)
|
|
}}
|
|
/>
|
|
</div>
|
|
{value && (
|
|
<div className="mt-4 rounded-lg bg-gray-50 p-3">
|
|
<div className="mb-2 text-xs font-medium text-gray-600">Current Value:</div>
|
|
<div className="whitespace-pre-wrap font-mono text-sm text-gray-800">
|
|
{value}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
// Default state
|
|
export const Default: Story = {
|
|
render: args => <PromptEditorDemo {...args} />,
|
|
args: {
|
|
placeholder: 'Type / for commands...',
|
|
editable: true,
|
|
compact: false,
|
|
},
|
|
}
|
|
|
|
// With initial value
|
|
export const WithInitialValue: Story = {
|
|
render: args => <PromptEditorDemo {...args} />,
|
|
args: {
|
|
value: 'Write a summary about the following topic:\n\nPlease include key points and examples.',
|
|
placeholder: 'Type / for commands...',
|
|
editable: true,
|
|
},
|
|
}
|
|
|
|
// Compact mode
|
|
export const CompactMode: Story = {
|
|
render: args => <PromptEditorDemo {...args} />,
|
|
args: {
|
|
value: 'This is a compact editor with smaller text size.',
|
|
placeholder: 'Type / for commands...',
|
|
editable: true,
|
|
compact: true,
|
|
},
|
|
}
|
|
|
|
// Read-only mode
|
|
export const ReadOnlyMode: Story = {
|
|
render: args => <PromptEditorDemo {...args} />,
|
|
args: {
|
|
value: 'This content is read-only and cannot be edited.\n\nYou can select and copy text, but not modify it.',
|
|
editable: false,
|
|
},
|
|
}
|
|
|
|
// With variables example
|
|
export const WithVariablesExample: Story = {
|
|
render: args => <PromptEditorDemo {...args} />,
|
|
args: {
|
|
value: 'Hello, please analyze the following data and provide insights.',
|
|
placeholder: 'Type / to insert variables...',
|
|
editable: true,
|
|
},
|
|
}
|
|
|
|
// Long content example
|
|
export const LongContent: Story = {
|
|
render: args => <PromptEditorDemo {...args} />,
|
|
args: {
|
|
value: `You are a helpful AI assistant. Your task is to provide accurate, helpful, and friendly responses.
|
|
|
|
Guidelines:
|
|
1. Be clear and concise
|
|
2. Provide examples when helpful
|
|
3. Ask clarifying questions if needed
|
|
4. Maintain a professional yet friendly tone
|
|
|
|
Please analyze the user's request and provide a comprehensive response.`,
|
|
placeholder: 'Enter your prompt...',
|
|
editable: true,
|
|
},
|
|
}
|
|
|
|
// Custom placeholder
|
|
export const CustomPlaceholder: Story = {
|
|
render: args => <PromptEditorDemo {...args} />,
|
|
args: {
|
|
placeholder: 'Describe the task you want the AI to perform... (Press / for variables)',
|
|
editable: true,
|
|
},
|
|
}
|
|
|
|
// Multiple editors
|
|
const MultipleEditorsDemo = () => {
|
|
const [systemPrompt, setSystemPrompt] = useState('You are a helpful assistant.')
|
|
const [userPrompt, setUserPrompt] = useState('')
|
|
|
|
return (
|
|
<div style={{ width: '700px' }} className="flex flex-col gap-6">
|
|
<div className="flex flex-col gap-2">
|
|
<label className="text-sm font-medium text-gray-700">System Prompt</label>
|
|
<div className="min-h-[100px] rounded-lg border border-gray-300 bg-blue-50 p-4">
|
|
<PromptEditorMock
|
|
value={systemPrompt}
|
|
onChange={setSystemPrompt}
|
|
placeholder="Enter system instructions..."
|
|
editable={true}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-col gap-2">
|
|
<label className="text-sm font-medium text-gray-700">User Prompt</label>
|
|
<div className="min-h-[100px] rounded-lg border border-gray-300 p-4">
|
|
<PromptEditorMock
|
|
value={userPrompt}
|
|
onChange={setUserPrompt}
|
|
placeholder="Enter user message template..."
|
|
editable={true}
|
|
/>
|
|
</div>
|
|
</div>
|
|
{(systemPrompt || userPrompt) && (
|
|
<div className="rounded-lg bg-gray-50 p-4">
|
|
<div className="mb-2 text-xs font-medium text-gray-600">Combined Output:</div>
|
|
<div className="whitespace-pre-wrap text-sm text-gray-800">
|
|
{systemPrompt && (
|
|
<>
|
|
<strong>System:</strong> {systemPrompt}
|
|
{userPrompt && '\n\n'}
|
|
</>
|
|
)}
|
|
{userPrompt && (
|
|
<>
|
|
<strong>User:</strong> {userPrompt}
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export const MultipleEditors: Story = {
|
|
render: () => <MultipleEditorsDemo />,
|
|
}
|
|
|
|
// Real-world example - Email template
|
|
const EmailTemplateDemo = () => {
|
|
const [subject, setSubject] = useState('Welcome to our platform!')
|
|
const [body, setBody] = useState(`Hi,
|
|
|
|
Thank you for signing up! We're excited to have you on board.
|
|
|
|
To get started, please verify your email address by clicking the button below.
|
|
|
|
Best regards,
|
|
The Team`)
|
|
|
|
return (
|
|
<div style={{ width: '700px' }} className="rounded-lg border border-gray-200 bg-white p-6">
|
|
<h3 className="mb-4 text-lg font-semibold">Email Template Editor</h3>
|
|
<div className="flex flex-col gap-4">
|
|
<div className="flex flex-col gap-2">
|
|
<label className="text-sm font-medium text-gray-700">Subject Line</label>
|
|
<div className="rounded-lg border border-gray-300 p-3">
|
|
<PromptEditorMock
|
|
value={subject}
|
|
onChange={setSubject}
|
|
placeholder="Enter email subject..."
|
|
compact={true}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-col gap-2">
|
|
<label className="text-sm font-medium text-gray-700">Email Body</label>
|
|
<div className="min-h-[200px] rounded-lg border border-gray-300 p-4">
|
|
<PromptEditorMock
|
|
value={body}
|
|
onChange={setBody}
|
|
placeholder="Type your email content... Use / to insert variables"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export const EmailTemplate: Story = {
|
|
render: () => <EmailTemplateDemo />,
|
|
}
|
|
|
|
// Real-world example - Chat prompt builder
|
|
const ChatPromptBuilderDemo = () => {
|
|
const [prompt, setPrompt] = useState(`Analyze the following conversation and provide insights:
|
|
|
|
1. Identify the main topics discussed
|
|
2. Detect the sentiment and tone
|
|
3. Summarize key points
|
|
4. Suggest follow-up questions`)
|
|
|
|
const [characterCount, setCharacterCount] = useState(prompt.length)
|
|
|
|
const handleChange = (text: string) => {
|
|
setPrompt(text)
|
|
setCharacterCount(text.length)
|
|
}
|
|
|
|
return (
|
|
<div style={{ width: '700px' }} className="rounded-lg border border-gray-200 bg-white p-6">
|
|
<div className="mb-4 flex items-center justify-between">
|
|
<h3 className="text-lg font-semibold">Chat Prompt Builder</h3>
|
|
<span className="text-xs text-gray-500">{characterCount} characters</span>
|
|
</div>
|
|
<div className="min-h-[200px] rounded-lg border border-gray-300 bg-gray-50 p-4">
|
|
<PromptEditorMock
|
|
value={prompt}
|
|
onChange={handleChange}
|
|
placeholder="Design your chat prompt... Use / for templates"
|
|
/>
|
|
</div>
|
|
<div className="mt-4 rounded-lg bg-blue-50 p-3 text-sm text-blue-800">
|
|
💡 <strong>Tip:</strong> Type <code className="rounded bg-blue-100 px-1 py-0.5">/</code> to insert variables or templates
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export const ChatPromptBuilder: Story = {
|
|
render: () => <ChatPromptBuilderDemo />,
|
|
}
|
|
|
|
// Real-world example - API instruction editor
|
|
const APIInstructionEditorDemo = () => {
|
|
const [instructions, setInstructions] = useState(`Process the incoming API request and:
|
|
|
|
1. Validate all required fields are present
|
|
2. Transform the data according to the schema
|
|
3. Apply business logic rules
|
|
4. Return the formatted response`)
|
|
|
|
return (
|
|
<div style={{ width: '700px' }} className="rounded-lg border border-gray-200 bg-white p-6">
|
|
<h3 className="mb-4 text-lg font-semibold">API Processing Instructions</h3>
|
|
<div className="min-h-[180px] rounded-lg border-2 border-indigo-300 bg-indigo-50 p-4">
|
|
<PromptEditorMock
|
|
value={instructions}
|
|
onChange={setInstructions}
|
|
placeholder="Enter processing instructions..."
|
|
/>
|
|
</div>
|
|
<div className="mt-4 flex items-center gap-2">
|
|
<button className="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-medium text-white hover:bg-indigo-700">
|
|
Save Instructions
|
|
</button>
|
|
<button className="rounded-lg bg-gray-200 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-300">
|
|
Test
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export const APIInstructionEditor: Story = {
|
|
render: () => <APIInstructionEditorDemo />,
|
|
}
|
|
|
|
// Interactive playground
|
|
export const Playground: Story = {
|
|
render: args => <PromptEditorDemo {...args} />,
|
|
args: {
|
|
value: '',
|
|
placeholder: 'Type / for commands...',
|
|
editable: true,
|
|
compact: false,
|
|
},
|
|
}
|