Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui): introduce app templates #58

Merged
merged 14 commits into from
Mar 29, 2024
1 change: 0 additions & 1 deletion ui/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ dist
dist-ssr
*.local
.env
examples/
.yarn
src/api

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ config:
openai_api_key:
name: Secret
slots:
plaintext:
plaintext:
prompt_template_type:
name: Chat_Message_Prompt
slots:
Expand All @@ -37,37 +37,43 @@ config:
case: null
ui:
nodes:
- width: 342
- width: 355
height: 111
position:
x: 6.999999999999915
'y': 432
x: 0
'y': 97.88427734375
id: Ub01XPpYeAVyMFoWPfCGj
dragging: false
positionAbsolute:
x: 6.999999999999915
'y': 432
x: 0
'y': 97.88427734375
selected: false
type: custom_block_node
- width: 359
targetPosition: left
sourcePosition: right
- width: 381
height: 314
position:
x: 595.1765624959938
'y': 293.88988734077503
x: 641.9974060058594
'y': 0
id: 0i1IUkK4JLXJ_rW6YHdG5
dragging: false
positionAbsolute:
x: 595.1765624959938
'y': 293.88988734077503
x: 641.9974060058594
'y': 0
selected: true
type: custom_block_node
- width: 350
targetPosition: left
sourcePosition: right
- width: 360
height: 111
position:
x: 1075.7204734354546
'y': 635.1457081528558
x: 1308.6255798339844
'y': 97.88429260253906
id: z-VDaVqKg4ONmIlAIFehb
positionAbsolute:
x: 1075.7204734354546
'y': 635.1457081528558
x: 1308.6255798339844
'y': 97.88429260253906
type: custom_block_node
targetPosition: left
sourcePosition: right
1 change: 1 addition & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
},
"devDependencies": {
"@mdx-js/rollup": "^3.0.1",
"@modyfi/vite-plugin-yaml": "^1.1.0",
"@types/axios": "^0.14.0",
"@types/dagre": "^0.7.48",
"@types/downloadjs": "^1.4.3",
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/Layout/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const Footer: React.FC = () => {
<Stack gap="xs">
<Box>© 2024 LinguFlow</Box>
<Group mx="-xs" gap="xs">
<Anchor href="https://github.com/pingcap/LinguFlow" target="_blank">
<Anchor href="https://www.linguflow.com/docs/overview/" target="_blank">
<Button variant="subtle" size="compact-lg" color="dark">
Docs
</Button>
Expand Down
3 changes: 2 additions & 1 deletion ui/src/modules/app_builder/Block/Boolean.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { Controller } from 'react-hook-form'
import { Box, Switch } from '@mantine/core'
import type { SlotTypeComponentProps } from './Slot'

export const Boolean: React.FC<SlotTypeComponentProps> = ({ slot, formPath, disabled }) => {
export const Boolean: React.FC<SlotTypeComponentProps> = ({ slot, formPath, disabled, required }) => {
return (
<Controller
name={formPath}
render={({ field: { ref, value, onChange } }) => (
<Switch
required={required}
size="xs"
ref={ref}
disabled={disabled}
Expand Down
3 changes: 2 additions & 1 deletion ui/src/modules/app_builder/Block/Float.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { Controller } from 'react-hook-form'
import { NumberInput } from '@mantine/core'
import type { SlotTypeComponentProps } from './Slot'

export const Float: React.FC<SlotTypeComponentProps> = ({ slot, formPath, disabled }) => {
export const Float: React.FC<SlotTypeComponentProps> = ({ slot, formPath, disabled, required }) => {
return (
<Controller
name={formPath}
render={({ field: { ref, value, onChange } }) => (
<NumberInput
required={required}
decimalScale={2}
variant={disabled ? 'filled' : 'default'}
hideControls={disabled}
Expand Down
3 changes: 2 additions & 1 deletion ui/src/modules/app_builder/Block/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { Controller } from 'react-hook-form'
import { Textarea } from '@mantine/core'
import type { SlotTypeComponentProps } from './Slot'

export const Input: React.FC<SlotTypeComponentProps> = ({ slot, formPath, disabled }) => {
export const Input: React.FC<SlotTypeComponentProps> = ({ slot, formPath, disabled, required }) => {
return (
<Controller
name={formPath}
render={({ field: { ref, value, onChange } }) => (
<Textarea
required={required}
variant={disabled ? 'filled' : 'default'}
ref={ref}
label={slot.name}
Expand Down
3 changes: 2 additions & 1 deletion ui/src/modules/app_builder/Block/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useState } from 'react'
import { MultiSelect } from '@mantine/core'
import type { SlotTypeComponentProps } from './Slot'

export const ListComponent: React.FC<SlotTypeComponentProps> = ({ slot, formPath, disabled }) => {
export const ListComponent: React.FC<SlotTypeComponentProps> = ({ slot, formPath, disabled, required }) => {
const { getValues } = useFormContext()
const _data = (getValues(formPath) as string[]) || slot.default || []
const [data] = useState(_data)
Expand All @@ -12,6 +12,7 @@ export const ListComponent: React.FC<SlotTypeComponentProps> = ({ slot, formPath
name={formPath}
render={({ field: { onChange, value, ref } }) => (
<MultiSelect
required={required}
ref={ref}
disabled={disabled}
label={slot.name}
Expand Down
3 changes: 2 additions & 1 deletion ui/src/modules/app_builder/Block/Number.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { Controller } from 'react-hook-form'
import { NumberInput } from '@mantine/core'
import type { SlotTypeComponentProps } from './Slot'

export const NumberComponent: React.FC<SlotTypeComponentProps> = ({ slot, formPath, disabled }) => {
export const NumberComponent: React.FC<SlotTypeComponentProps> = ({ slot, formPath, disabled, required }) => {
return (
<Controller
name={formPath}
render={({ field: { onChange, value, ref } }) => (
<NumberInput
required={required}
variant={disabled ? 'filled' : 'default'}
hideControls={disabled}
ref={ref}
Expand Down
4 changes: 2 additions & 2 deletions ui/src/modules/app_builder/Block/Secret.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { PasswordInput } from '@mantine/core'
import { useEffect } from 'react'
import type { SlotTypeComponentProps } from './Slot'

export const Secret: React.FC<SlotTypeComponentProps> = ({ slot, formPath, disabled }) => {
export const Secret: React.FC<SlotTypeComponentProps> = ({ slot, formPath, disabled, required }) => {
const { register } = useFormContext()
useEffect(() => {
register(`${formPath}.name`, { value: 'Secret' })
Expand All @@ -14,7 +14,7 @@ export const Secret: React.FC<SlotTypeComponentProps> = ({ slot, formPath, disab
name={`${formPath}.slots.plaintext`}
render={({ field: { onChange, value, ref } }) => (
<PasswordInput
withAsterisk
withAsterisk={required}
variant={disabled ? 'filled' : 'default'}
ref={ref}
label={slot.name}
Expand Down
12 changes: 11 additions & 1 deletion ui/src/modules/app_builder/Block/Slot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface SlotTypeComponentProps {
slot: Parameter
disabled?: boolean
stackIndex?: number
required?: boolean
}

const BuiltinTypeComponent: { [k: string]: React.FC<SlotTypeComponentProps> } = {
Expand All @@ -36,7 +37,15 @@ export const Slot: React.FC<SlotTypeComponentProps> = React.memo(({ formPath, sl
const subFormPath = `${formPath}.slots.${slot.name}`
const SlotComponent =
BuiltinTypeComponent[slot.class_name] || ExternalTypeComponent[slot.class_name] || ExternalTypeSelect
return <SlotComponent formPath={subFormPath} slot={slot} disabled={disabled} stackIndex={stackIndex} />
return (
<SlotComponent
formPath={subFormPath}
slot={slot}
disabled={disabled}
stackIndex={stackIndex}
required={slot.default === null}
/>
)
})

const ExternalTypeSelect: React.FC<SlotTypeComponentProps> = ({ formPath, slot, disabled, stackIndex }) => {
Expand All @@ -55,6 +64,7 @@ const ExternalTypeSelect: React.FC<SlotTypeComponentProps> = ({ formPath, slot,
render={({ field: { value, onChange } }) => (
<>
<Select
required
placeholder="Pick candidates"
size="xs"
allowDeselect={false}
Expand Down
1 change: 1 addition & 0 deletions ui/src/modules/app_builder/Block/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ const ConfigDrawer: React.FC<{ drawerProps: DrawerProps; slots: Parameter[]; pro
<Drawer
{...drawerProps}
keepMounted={false}
shadow="xs"
position="right"
size="sm"
withOverlay={false}
Expand Down
13 changes: 11 additions & 2 deletions ui/src/modules/app_builder/Toolbar/AppInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ApplicationInfo, ApplicationVersionInfo } from '@api/linguflow.schemas'
import { ActionIcon, Group, Stack, Text, TextInput } from '@mantine/core'
import { IconCheck, IconPencil, IconX } from '@tabler/icons-react'
import { ActionIcon, CopyButton, Group, Stack, Text, TextInput, Tooltip, rem } from '@mantine/core'
import { IconCheck, IconCopy, IconPencil, IconX } from '@tabler/icons-react'
import { useState } from 'react'
import { useGetAppVersionApplicationsApplicationIdVersionsVersionIdGet } from '@api/linguflow'
import { useParams } from 'react-router-dom'
Expand Down Expand Up @@ -46,6 +46,15 @@ export const AppInfo: React.FC<{
<Text fw="bold" fz="xs" span>
{app?.id}
</Text>
<CopyButton value={app?.id || ''} timeout={2000}>
{({ copied, copy }) => (
<Tooltip label={copied ? 'Copied' : 'Copy'} withArrow position="right">
<ActionIcon color={copied ? 'teal' : 'gray'} variant="subtle" onClick={copy}>
{copied ? <IconCheck style={{ width: rem(16) }} /> : <IconCopy style={{ width: rem(16) }} />}
</ActionIcon>
</Tooltip>
)}
</CopyButton>
</Group>

<Group align="center" gap="xs" h="30">
Expand Down
45 changes: 22 additions & 23 deletions ui/src/modules/app_builder/Toolbar/Debug/ListInteraction.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
import { ActionIcon, Button, Card, FocusTrap, Stack, Text, Textarea } from '@mantine/core'
import { IconCircleX } from '@tabler/icons-react'
import { useDisclosure } from '@mantine/hooks'
import { getHotkeyHandler, useDisclosure } from '@mantine/hooks'
import type { InteractionProps } from '.'

export const ListIntercation: React.FC<InteractionProps<string[]>> = ({ value = [], onChange }) => {
const [showAddInput, { open, close }] = useDisclosure(false)
const handleSubmit = (e: React.FocusEvent<HTMLTextAreaElement>) => {
if (!e.target.value) {
return
}
onChange([...value, e.target.value])
close()
}

return (
<Stack>
{value?.map((item, index) => (
<ListItem
key={index}
data={item}
onDelete={() => onChange(value.filter((_, _index) => index !== _index))}
onEdit={(v) => onChange([...value.slice(0, index), v, ...value.slice(index + 1)])}
/>
))}
{showAddInput && (
<FocusTrap active>
<Textarea
size="xs"
autosize
onBlur={(e) => {
if (!e.target.value) {
return
}
onChange([...value, e.target.value])
close()
}}
/>
<Textarea size="xs" autosize onBlur={handleSubmit} onKeyDown={getHotkeyHandler([['Enter', handleSubmit]])} />
</FocusTrap>
)}
{!showAddInput && (
Expand All @@ -44,10 +43,20 @@ const ListItem: React.FC<{ data: string; onDelete: () => void; onEdit: (v: strin
onEdit
}) => {
const [isEdit, { open, close }] = useDisclosure(false)
const handleSubmit = (e: React.FocusEvent<HTMLTextAreaElement>) => {
if (!e.target.value) {
return
}
onEdit(e.target.value)
close()
}

return !isEdit ? (
<Card p="xs" style={{ position: 'relative', cursor: 'pointer' }} withBorder shadow="xs" onClick={open}>
<Text>{data}</Text>
<ActionIcon
variant="subtle"
color="gray"
style={{ position: 'absolute', right: 10, top: 10 }}
onClick={(e) => {
e.stopPropagation()
Expand All @@ -59,17 +68,7 @@ const ListItem: React.FC<{ data: string; onDelete: () => void; onEdit: (v: strin
</Card>
) : (
<FocusTrap active>
<Textarea
size="xs"
autosize
onBlur={(e) => {
if (!e.target.value) {
return
}
onEdit(e.target.value)
close()
}}
/>
<Textarea size="xs" autosize onBlur={handleSubmit} onKeyDown={getHotkeyHandler([['Enter', handleSubmit]])} />
</FocusTrap>
)
}
Loading