Compare commits
142 Commits
main
...
build/esli
| Author | SHA1 | Date |
|---|---|---|
|
|
2fe28279c8 | 1 year ago |
|
|
3d165ec7d9 | 1 year ago |
|
|
0b8c896481 | 1 year ago |
|
|
15fe635465 | 1 year ago |
|
|
f8c3189f4d | 1 year ago |
|
|
f215db87e3 | 1 year ago |
|
|
67d02212b4 | 1 year ago |
|
|
cff9adaf8e | 1 year ago |
|
|
cdd2a40086 | 1 year ago |
|
|
024028bc52 | 1 year ago |
|
|
0ae085b48a | 1 year ago |
|
|
2094c54951 | 1 year ago |
|
|
f4f11135d3 | 1 year ago |
|
|
8e9d7a229d | 1 year ago |
|
|
8f49572f85 | 1 year ago |
|
|
5aa7696cc3 | 1 year ago |
|
|
15dd79e822 | 1 year ago |
|
|
4651ab4195 | 1 year ago |
|
|
5e3160e6f6 | 1 year ago |
|
|
973cd126bb | 1 year ago |
|
|
ebaf8766ef | 1 year ago |
|
|
d2190e9c3a | 1 year ago |
|
|
37f55098fe | 1 year ago |
|
|
b1771194cc | 1 year ago |
|
|
0279bd8c75 | 1 year ago |
|
|
5e077e4ce8 | 1 year ago |
|
|
64067e1f20 | 1 year ago |
|
|
5295c72ca1 | 1 year ago |
|
|
1ecea62052 | 1 year ago |
|
|
307af29b65 | 1 year ago |
|
|
10190a9aa5 | 1 year ago |
|
|
7c5c35600c | 1 year ago |
|
|
63b333cdb1 | 1 year ago |
|
|
a6776190bd | 1 year ago |
|
|
9577cbac27 | 1 year ago |
|
|
f6ae13abad | 1 year ago |
|
|
f3d501e7d5 | 1 year ago |
|
|
2eab8fcc33 | 1 year ago |
|
|
bdb81fe20d | 1 year ago |
|
|
0f60fe7f2a | 1 year ago |
|
|
425f624de5 | 1 year ago |
|
|
b1919745e2 | 1 year ago |
|
|
9a242bcac9 | 1 year ago |
|
|
a6109a60b8 | 1 year ago |
|
|
28f7bbf83a | 1 year ago |
|
|
cac04c5f3c | 1 year ago |
|
|
18f5f9cc37 | 1 year ago |
|
|
1787c5c93f | 1 year ago |
|
|
f981494613 | 1 year ago |
|
|
fbc853af92 | 1 year ago |
|
|
1a64c660ba | 1 year ago |
|
|
846555af1b | 1 year ago |
|
|
bca94854f7 | 1 year ago |
|
|
1bd70bd8bf | 1 year ago |
|
|
d1dcd39191 | 1 year ago |
|
|
35384bda41 | 1 year ago |
|
|
89fb6eb648 | 1 year ago |
|
|
aa61a890b2 | 1 year ago |
|
|
31ece363c3 | 1 year ago |
|
|
70a5d78cc5 | 1 year ago |
|
|
57f4dfdb6f | 1 year ago |
|
|
aa9028a607 | 1 year ago |
|
|
d83f94c55c | 1 year ago |
|
|
a8c5e0b0b0 | 1 year ago |
|
|
177e8cbf73 | 1 year ago |
|
|
23828fd15a | 1 year ago |
|
|
2cc37ac8e5 | 1 year ago |
|
|
c9ee1e9ff2 | 1 year ago |
|
|
4f10f5d5f4 | 1 year ago |
|
|
c48c84674e | 1 year ago |
|
|
1e9fbbf41b | 1 year ago |
|
|
4dd144ce43 | 1 year ago |
|
|
a387ff1c38 | 1 year ago |
|
|
a9e367e6de | 1 year ago |
|
|
e2fec587f8 | 1 year ago |
|
|
39a6f0943d | 1 year ago |
|
|
684896d100 | 1 year ago |
|
|
54f911f6cd | 1 year ago |
|
|
0e5c16d0c2 | 1 year ago |
|
|
b8cd6ea478 | 1 year ago |
|
|
fc61fd0f50 | 1 year ago |
|
|
2fbfc988c4 | 1 year ago |
|
|
99f5fea001 | 1 year ago |
|
|
ecd2a1be9f | 1 year ago |
|
|
49ee9ca5f1 | 1 year ago |
|
|
6d0eef12b1 | 1 year ago |
|
|
c1e0a939b0 | 1 year ago |
|
|
060a894bd1 | 1 year ago |
|
|
c75e02b5b2 | 1 year ago |
|
|
fcf43ee845 | 1 year ago |
|
|
466f61d044 | 1 year ago |
|
|
27ae74af50 | 1 year ago |
|
|
8dd941e3d2 | 1 year ago |
|
|
dec4bf6b98 | 1 year ago |
|
|
e2c33fc40f | 1 year ago |
|
|
c74e59d1f4 | 1 year ago |
|
|
1fcb902715 | 1 year ago |
|
|
c08f98218c | 1 year ago |
|
|
c6377f6e38 | 1 year ago |
|
|
3cb0a5bd68 | 1 year ago |
|
|
95777d23e0 | 1 year ago |
|
|
e7dc16fd08 | 1 year ago |
|
|
495dec143c | 1 year ago |
|
|
4cc6dfa232 | 1 year ago |
|
|
d1452d4af4 | 1 year ago |
|
|
d68ca56b3a | 1 year ago |
|
|
49856d8d17 | 1 year ago |
|
|
902de72cc0 | 1 year ago |
|
|
76f6b8d104 | 1 year ago |
|
|
f111e605c4 | 1 year ago |
|
|
b358ed3a5b | 1 year ago |
|
|
88dbf639e0 | 1 year ago |
|
|
aa8b525b48 | 1 year ago |
|
|
c990bc61db | 1 year ago |
|
|
6d7588f236 | 1 year ago |
|
|
8257c7bf02 | 1 year ago |
|
|
946068967b | 1 year ago |
|
|
19f5684960 | 1 year ago |
|
|
6d62840aff | 1 year ago |
|
|
ab868ac979 | 1 year ago |
|
|
1d74e693ea | 1 year ago |
|
|
fa43d4202f | 1 year ago |
|
|
6b29860788 | 1 year ago |
|
|
36800eeaba | 1 year ago |
|
|
67acd174ac | 1 year ago |
|
|
b5edc64b2a | 1 year ago |
|
|
d00b2724cc | 1 year ago |
|
|
43f87c0b86 | 1 year ago |
|
|
7a43f48c95 | 1 year ago |
|
|
58a913b09d | 1 year ago |
|
|
cd03795f2c | 1 year ago |
|
|
36f8b5711d | 2 years ago |
|
|
f9c48e9ea9 | 2 years ago |
|
|
3b48f8c98e | 2 years ago |
|
|
cef1010cb5 | 2 years ago |
|
|
cb4875a3a7 | 2 years ago |
|
|
bbca708832 | 2 years ago |
|
|
05aec43ee3 | 2 years ago |
|
|
e8127756e0 | 2 years ago |
|
|
792595a46f | 2 years ago |
|
|
d7d7281c93 | 2 years ago |
|
|
21193c2fbf | 2 years ago |
@ -1,11 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd web && npm install
|
||||
npm add -g pnpm@9.12.2
|
||||
cd web && pnpm install
|
||||
pipx install poetry
|
||||
|
||||
echo 'alias start-api="cd /workspaces/dify/api && poetry run python -m flask run --host 0.0.0.0 --port=5001 --debug"' >> ~/.bashrc
|
||||
echo 'alias start-worker="cd /workspaces/dify/api && poetry run python -m celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,generation,mail,ops_trace,app_deletion"' >> ~/.bashrc
|
||||
echo 'alias start-web="cd /workspaces/dify/web && npm run dev"' >> ~/.bashrc
|
||||
echo 'alias start-web="cd /workspaces/dify/web && pnpm dev"' >> ~/.bashrc
|
||||
echo 'alias start-containers="cd /workspaces/dify/docker && docker-compose -f docker-compose.middleware.yaml -p dify up -d"' >> ~/.bashrc
|
||||
|
||||
source /home/vscode/.bashrc
|
||||
source /home/vscode/.bashrc
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
/**/node_modules/*
|
||||
node_modules/
|
||||
|
||||
dist/
|
||||
build/
|
||||
out/
|
||||
.next/
|
||||
@ -1,31 +0,0 @@
|
||||
{
|
||||
"extends": [
|
||||
"next",
|
||||
"@antfu",
|
||||
"plugin:storybook/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/consistent-type-definitions": [
|
||||
"error",
|
||||
"type"
|
||||
],
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"no-console": "off",
|
||||
"indent": "off",
|
||||
"@typescript-eslint/indent": [
|
||||
"error",
|
||||
2,
|
||||
{
|
||||
"SwitchCase": 1,
|
||||
"flatTernaryExpressions": false,
|
||||
"ignoredNodes": [
|
||||
"PropertyDefinition[decorators]",
|
||||
"TSUnionType",
|
||||
"FunctionExpression[params]:has(Identifier[decorators])"
|
||||
]
|
||||
}
|
||||
],
|
||||
"react-hooks/exhaustive-deps": "warn",
|
||||
"react/display-name": "warn"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
import PluginPage from '@/app/components/plugins/plugin-page'
|
||||
import PluginsPanel from '@/app/components/plugins/plugin-page/plugins-panel'
|
||||
import Marketplace from '@/app/components/plugins/marketplace'
|
||||
|
||||
const PluginList = async () => {
|
||||
return (
|
||||
<PluginPage
|
||||
plugins={<PluginsPanel />}
|
||||
marketplace={<Marketplace />}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export const metadata = {
|
||||
title: 'Plugins - Dify',
|
||||
}
|
||||
|
||||
export default PluginList
|
||||
@ -0,0 +1,14 @@
|
||||
'use server'
|
||||
|
||||
import { revalidatePath } from 'next/cache'
|
||||
|
||||
// Server Actions
|
||||
export async function handleDelete() {
|
||||
// revalidatePath only invalidates the cache when the included path is next visited.
|
||||
revalidatePath('/')
|
||||
}
|
||||
|
||||
export async function fetchPluginDetail(org: string, name: string) {
|
||||
// Fetch plugin detail TODO
|
||||
return { org, name }
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
import { handleDelete } from './actions'
|
||||
import Card from '@/app/components/plugins/card'
|
||||
import { customTool, extensionDallE, modelGPT4, toolNotion } from '@/app/components/plugins/card/card-mock'
|
||||
import PluginItem from '@/app/components/plugins/plugin-item'
|
||||
import CardMoreInfo from '@/app/components/plugins/card/card-more-info'
|
||||
import ProviderCard from '@/app/components/plugins/provider-card'
|
||||
import Badge from '@/app/components/base/badge'
|
||||
|
||||
const PluginList = async () => {
|
||||
const pluginList = [toolNotion, extensionDallE, modelGPT4, customTool]
|
||||
|
||||
return (
|
||||
<div className='pb-3 bg-white'>
|
||||
<div className='mx-3 '>
|
||||
<h2 className='my-3'>Dify Plugin list</h2>
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
{pluginList.map((plugin, index) => (
|
||||
<PluginItem
|
||||
key={index}
|
||||
payload={plugin as any}
|
||||
onDelete={handleDelete}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<h2 className='my-3'>Install Plugin / Package under bundle</h2>
|
||||
<div className='w-[512px] rounded-2xl bg-background-section-burn p-2'>
|
||||
<Card
|
||||
payload={toolNotion as any}
|
||||
descriptionLineRows={1}
|
||||
titleLeft={
|
||||
<Badge className='ml-1' text={toolNotion.version} />
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<h3 className='my-1'>Installed</h3>
|
||||
<div className='w-[512px] rounded-2xl bg-background-section-burn p-2'>
|
||||
<Card
|
||||
payload={toolNotion as any}
|
||||
descriptionLineRows={1}
|
||||
installed
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h3 className='my-1'>Install model provide</h3>
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
{pluginList.map((plugin, index) => (
|
||||
<ProviderCard key={index} payload={plugin as any} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className='my-3 h-[px] bg-gray-50'></div>
|
||||
<h2 className='my-3'>Marketplace Plugin list</h2>
|
||||
<div className='grid grid-cols-4 gap-3'>
|
||||
{pluginList.map((plugin, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
payload={plugin as any}
|
||||
footer={
|
||||
<CardMoreInfo downloadCount={index % 2 === 0 ? 1234 : 6} tags={index % 2 === 0 ? ['Search', 'Tag that has very very long name', 'Productivity', 'Tag2'] : []} />
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const metadata = {
|
||||
title: 'Plugins - Card',
|
||||
}
|
||||
|
||||
export default PluginList
|
||||
@ -0,0 +1,20 @@
|
||||
'use client'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import UpdatePlugin from '@/app/components/plugins/update-plugin'
|
||||
|
||||
const Page = () => {
|
||||
const [isShowUpdateModal, {
|
||||
setTrue: showUpdateModal,
|
||||
setFalse: hideUpdateModal,
|
||||
}] = useBoolean(false)
|
||||
return (
|
||||
<div>
|
||||
<div onClick={showUpdateModal}>Show Upgrade</div>
|
||||
{isShowUpdateModal && (
|
||||
<UpdatePlugin onHide={hideUpdateModal} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Page
|
||||
@ -0,0 +1,39 @@
|
||||
'use client'
|
||||
import { useEffect, useState } from 'react'
|
||||
import AllTools from '@/app/components/workflow/block-selector/all-tools'
|
||||
import {
|
||||
fetchAllBuiltInTools,
|
||||
fetchAllCustomTools,
|
||||
fetchAllWorkflowTools,
|
||||
} from '@/service/tools'
|
||||
import type { ToolWithProvider } from '@/app/components/workflow/types'
|
||||
|
||||
const ToolsPicker = () => {
|
||||
const [buildInTools, setBuildInTools] = useState<ToolWithProvider[]>([])
|
||||
const [customTools, setCustomTools] = useState<ToolWithProvider[]>([])
|
||||
const [workflowTools, setWorkflowTools] = useState<ToolWithProvider[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const buildInTools = await fetchAllBuiltInTools()
|
||||
const customTools = await fetchAllCustomTools()
|
||||
const workflowTools = await fetchAllWorkflowTools()
|
||||
setBuildInTools(buildInTools)
|
||||
setCustomTools(customTools)
|
||||
setWorkflowTools(workflowTools)
|
||||
})()
|
||||
})
|
||||
return (
|
||||
<div className="relative mt-5 mx-auto w-[320px] bg-white">
|
||||
<AllTools
|
||||
searchText=""
|
||||
onSelect={() => { }}
|
||||
buildInTools={buildInTools}
|
||||
customTools={customTools}
|
||||
workflowTools={workflowTools}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ToolsPicker
|
||||
@ -0,0 +1,28 @@
|
||||
@tailwind components;
|
||||
|
||||
@layer components {
|
||||
.badge {
|
||||
@apply inline-flex justify-center items-center text-text-tertiary border border-divider-deep
|
||||
}
|
||||
|
||||
.badge-l {
|
||||
@apply rounded-md gap-1 min-w-6
|
||||
}
|
||||
|
||||
/* m is for the regular button */
|
||||
.badge-m {
|
||||
@apply rounded-md gap-[3px] min-w-5
|
||||
}
|
||||
|
||||
.badge-s {
|
||||
@apply rounded-[5px] gap-0.5 min-w-[18px]
|
||||
}
|
||||
|
||||
.badge.badge-warning {
|
||||
@apply text-text-warning border border-text-warning
|
||||
}
|
||||
|
||||
.badge.badge-accent {
|
||||
@apply text-text-accent-secondary border border-text-accent-secondary
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,81 @@
|
||||
import type { CSSProperties, ReactNode } from 'react'
|
||||
import React from 'react'
|
||||
import { type VariantProps, cva } from 'class-variance-authority'
|
||||
import classNames from '@/utils/classnames'
|
||||
import './index.css'
|
||||
|
||||
enum BadgeState {
|
||||
Warning = 'warning',
|
||||
Accent = 'accent',
|
||||
Default = '',
|
||||
}
|
||||
|
||||
const BadgeVariants = cva(
|
||||
'badge',
|
||||
{
|
||||
variants: {
|
||||
size: {
|
||||
s: 'badge-s',
|
||||
m: 'badge-m',
|
||||
l: 'badge-l',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
size: 'm',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
type BadgeProps = {
|
||||
size?: 's' | 'm' | 'l'
|
||||
iconOnly?: boolean
|
||||
uppercase?: boolean
|
||||
state?: BadgeState
|
||||
styleCss?: CSSProperties
|
||||
children?: ReactNode
|
||||
} & React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof BadgeVariants>
|
||||
|
||||
function getBadgeState(state: BadgeState) {
|
||||
switch (state) {
|
||||
case BadgeState.Warning:
|
||||
return 'badge-warning'
|
||||
case BadgeState.Accent:
|
||||
return 'badge-accent'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
const Badge: React.FC<BadgeProps> = ({
|
||||
className,
|
||||
size,
|
||||
state = BadgeState.Default,
|
||||
iconOnly = false,
|
||||
uppercase = false,
|
||||
styleCss,
|
||||
children,
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
BadgeVariants({ size, className }),
|
||||
getBadgeState(state),
|
||||
size === 's'
|
||||
? (iconOnly ? 'p-[3px]' : 'px-[5px] py-[3px]')
|
||||
: size === 'l'
|
||||
? (iconOnly ? 'p-1.5' : 'px-2 py-1')
|
||||
: (iconOnly ? 'p-1' : 'px-[5px] py-[2px]'),
|
||||
uppercase ? 'system-2xs-medium-uppercase' : 'system-2xs-medium',
|
||||
)}
|
||||
style={styleCss}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Badge.displayName = 'Badge'
|
||||
|
||||
export default Badge
|
||||
export { Badge, BadgeState, BadgeVariants }
|
||||
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
@ -0,0 +1,8 @@
|
||||
<svg width="14" height="16" viewBox="0 0 14 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Group">
|
||||
<path id="Vector" d="M5.6475 8.0115L0.333496 5.05884V11.3335C0.333491 11.4524 0.365258 11.569 0.425506 11.6715C0.485754 11.7739 0.572294 11.8584 0.676163 11.9162L6.3335 15.0588V9.17684C6.33344 8.93907 6.26981 8.70565 6.14919 8.50075C6.02857 8.29586 5.85536 8.12694 5.6475 8.0115Z" fill="#354052"/>
|
||||
<path id="Vector_2" d="M7.66699 9.17684V15.0588L13.3243 11.9162C13.4282 11.8584 13.5147 11.7739 13.575 11.6715C13.6352 11.569 13.667 11.4524 13.667 11.3335V5.05884L8.35299 8.0115C8.14513 8.12694 7.97192 8.29586 7.8513 8.50075C7.73068 8.70565 7.66705 8.93907 7.66699 9.17684Z" fill="#676F83"/>
|
||||
<path id="Vector_3" d="M10.1913 2.34351C9.804 3.33351 8.588 4.00017 7 4.00017C5.412 4.00017 4.196 3.33351 3.80867 2.34351L1 3.90417L6.35267 6.87817C6.5507 6.98815 6.77348 7.04586 7 7.04586C7.22652 7.04586 7.4493 6.98815 7.64733 6.87817L13 3.90417L10.1913 2.34351Z" fill="#676F83"/>
|
||||
<path id="Vector_4" d="M7 2.66675C8.10457 2.66675 9 2.21903 9 1.66675C9 1.11446 8.10457 0.666748 7 0.666748C5.89543 0.666748 5 1.11446 5 1.66675C5 2.21903 5.89543 2.66675 7 2.66675Z" fill="#354052"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,9 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Icon">
|
||||
<path id="Vector" fill-rule="evenodd" clip-rule="evenodd" d="M11.3891 2.41987C11.6635 2.58871 11.749 2.94802 11.5802 3.22239L10.3324 5.25H11.0833C11.4055 5.25 11.6667 5.51117 11.6667 5.83334V11.6667C11.6667 12.311 11.1444 12.8333 10.5 12.8333H3.50001C2.85568 12.8333 2.33334 12.311 2.33334 11.6667V5.83334C2.33334 5.51117 2.59451 5.25 2.91668 5.25H8.96252L10.5865 2.61094C10.7554 2.33657 11.1147 2.25102 11.3891 2.41987ZM5.83334 7.58334C5.51118 7.58334 5.25001 7.84449 5.25001 8.16667C5.25001 8.48884 5.51118 8.75 5.83334 8.75H8.16668C8.48885 8.75 8.75001 8.48884 8.75001 8.16667C8.75001 7.84449 8.48885 7.58334 8.16668 7.58334H5.83334Z" fill="#676F83"/>
|
||||
<g id="Vector_2" opacity="0.5">
|
||||
<path d="M6.91257 1.79347C6.96898 1.76525 7.01477 1.71948 7.043 1.66303L7.32195 1.10508C7.42946 0.890105 7.73623 0.890105 7.84374 1.10508L8.12269 1.66303C8.15093 1.71948 8.19672 1.76525 8.25313 1.79347L8.81108 2.07245C9.0261 2.17994 9.0261 2.48672 8.81108 2.5942L8.25313 2.87318C8.19672 2.9014 8.15093 2.94717 8.12269 3.00362L7.84374 3.56158C7.73623 3.77655 7.42946 3.77655 7.32195 3.56158L7.043 3.00362C7.01477 2.94717 6.96898 2.9014 6.91257 2.87318L6.35461 2.5942C6.13965 2.48672 6.13965 2.17994 6.35461 2.07245L6.91257 1.79347Z" fill="#676F83"/>
|
||||
<path d="M3.80145 2.7657C3.85789 2.73748 3.90366 2.69171 3.93189 2.63526L4.11364 2.27174C4.22113 2.05677 4.5279 2.05677 4.63539 2.27174L4.81715 2.63526C4.84537 2.6917 4.89114 2.73748 4.94759 2.7657L5.3111 2.94745C5.52607 3.05494 5.52607 3.36172 5.3111 3.4692L4.94759 3.65096C4.89114 3.67919 4.84537 3.72495 4.81715 3.7814L4.63539 4.14491C4.5279 4.35988 4.22113 4.35988 4.11364 4.14491L3.93189 3.7814C3.90366 3.72495 3.85789 3.67919 3.80145 3.65096L3.43793 3.4692C3.22296 3.36172 3.22296 3.05494 3.43793 2.94745L3.80145 2.7657Z" fill="#676F83"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@ -0,0 +1,3 @@
|
||||
<svg width="13" height="20" viewBox="0 0 13 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path id="Shape" d="M0 0H13V20C9.98017 20 7.26458 18.1615 6.14305 15.3576L0 0Z" fill="#F9FAFB"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 200 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,6 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Icon">
|
||||
<path id="Vector" d="M3.99999 1.33325H7.99999V5.33325C7.99999 6.06963 8.59692 6.66659 9.33332 6.66659H13.3333V13.3333C13.3333 14.0697 12.7364 14.6666 12 14.6666H6.66666V13.3333H7.99999V11.9999H6.66666V10.6666H7.99999V9.33325H6.66666V7.99992H5.33332V9.33325H6.66666V10.6666H5.33332V11.9999H6.66666V13.3333H5.33332V14.6666H3.99999C3.26361 14.6666 2.66666 14.0697 2.66666 13.3333V2.66659C2.66666 1.93021 3.26361 1.33325 3.99999 1.33325Z" fill="#676F83"/>
|
||||
<path id="Vector_2" opacity="0.5" d="M12.9428 4.99993C13.0415 5.09868 13.1232 5.21133 13.1859 5.33327H9.33334V1.48071C9.45528 1.54338 9.56794 1.62504 9.66668 1.72379L12.9428 4.99993Z" fill="#676F83"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 775 B |
@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Icon">
|
||||
<path id="Vector" d="M8 1C4.1325 1 1 4.1325 1 8C1 11.0975 3.00375 13.7137 5.78625 14.6413C6.13625 14.7025 6.2675 14.4925 6.2675 14.3088C6.2675 14.1425 6.25875 13.5913 6.25875 13.005C4.5 13.3288 4.045 12.5763 3.905 12.1825C3.82625 11.9812 3.485 11.36 3.1875 11.1937C2.9425 11.0625 2.5925 10.7387 3.17875 10.73C3.73 10.7212 4.12375 11.2375 4.255 11.4475C4.885 12.5062 5.89125 12.2088 6.29375 12.025C6.355 11.57 6.53875 11.2638 6.74 11.0887C5.1825 10.9137 3.555 10.31 3.555 7.6325C3.555 6.87125 3.82625 6.24125 4.2725 5.75125C4.2025 5.57625 3.9575 4.85875 4.3425 3.89625C4.3425 3.89625 4.92875 3.7125 6.2675 4.61375C6.8275 4.45625 7.4225 4.3775 8.0175 4.3775C8.6125 4.3775 9.2075 4.45625 9.7675 4.61375C11.1063 3.70375 11.6925 3.89625 11.6925 3.89625C12.0775 4.85875 11.8325 5.57625 11.7625 5.75125C12.2087 6.24125 12.48 6.8625 12.48 7.6325C12.48 10.3187 10.8438 10.9137 9.28625 11.0887C9.54 11.3075 9.75875 11.7275 9.75875 12.3837C9.75875 13.32 9.75 14.0725 9.75 14.3088C9.75 14.4925 9.88125 14.7113 10.2312 14.6413C11.6209 14.1721 12.8284 13.279 13.6839 12.0877C14.5393 10.8963 14.9996 9.46668 15 8C15 4.1325 11.8675 1 8 1Z" fill="#676F83"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@ -1,8 +1,9 @@
|
||||
const path = require('node:path')
|
||||
const { open, readdir, access, mkdir, writeFile, appendFile, rm } = require('node:fs/promises')
|
||||
const { parseXml } = require('@rgrove/parse-xml')
|
||||
const camelCase = require('lodash/camelCase')
|
||||
const template = require('lodash/template')
|
||||
import path from 'node:path'
|
||||
import { access, appendFile, mkdir, open, readdir, rm, writeFile } from 'node:fs/promises'
|
||||
import { parseXml } from '@rgrove/parse-xml'
|
||||
import { camelCase, template } from 'lodash-es'
|
||||
|
||||
const __dirname = path.dirname(new URL(import.meta.url).pathname)
|
||||
|
||||
const generateDir = async (currentPath) => {
|
||||
try {
|
||||
@ -0,0 +1,66 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "14",
|
||||
"height": "16",
|
||||
"viewBox": "0 0 14 16",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "Group"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Vector",
|
||||
"d": "M5.6475 8.0115L0.333496 5.05884V11.3335C0.333491 11.4524 0.365258 11.569 0.425506 11.6715C0.485754 11.7739 0.572294 11.8584 0.676163 11.9162L6.3335 15.0588V9.17684C6.33344 8.93907 6.26981 8.70565 6.14919 8.50075C6.02857 8.29586 5.85536 8.12694 5.6475 8.0115Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Vector_2",
|
||||
"d": "M7.66699 9.17684V15.0588L13.3243 11.9162C13.4282 11.8584 13.5147 11.7739 13.575 11.6715C13.6352 11.569 13.667 11.4524 13.667 11.3335V5.05884L8.35299 8.0115C8.14513 8.12694 7.97192 8.29586 7.8513 8.50075C7.73068 8.70565 7.66705 8.93907 7.66699 9.17684Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Vector_3",
|
||||
"d": "M10.1913 2.34351C9.804 3.33351 8.588 4.00017 7 4.00017C5.412 4.00017 4.196 3.33351 3.80867 2.34351L1 3.90417L6.35267 6.87817C6.5507 6.98815 6.77348 7.04586 7 7.04586C7.22652 7.04586 7.4493 6.98815 7.64733 6.87817L13 3.90417L10.1913 2.34351Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Vector_4",
|
||||
"d": "M7 2.66675C8.10457 2.66675 9 2.21903 9 1.66675C9 1.11446 8.10457 0.666748 7 0.666748C5.89543 0.666748 5 1.11446 5 1.66675C5 2.21903 5.89543 2.66675 7 2.66675Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Group"
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Group.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||
props,
|
||||
ref,
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||
|
||||
Icon.displayName = 'Group'
|
||||
|
||||
export default Icon
|
||||
@ -1 +1,2 @@
|
||||
export { default as Generator } from './Generator'
|
||||
export { default as Group } from './Group'
|
||||
|
||||
@ -0,0 +1,66 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "14",
|
||||
"height": "14",
|
||||
"viewBox": "0 0 14 14",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "Icon"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Vector",
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M11.3891 2.41987C11.6635 2.58871 11.749 2.94802 11.5802 3.22239L10.3324 5.25H11.0833C11.4055 5.25 11.6667 5.51117 11.6667 5.83334V11.6667C11.6667 12.311 11.1444 12.8333 10.5 12.8333H3.50001C2.85568 12.8333 2.33334 12.311 2.33334 11.6667V5.83334C2.33334 5.51117 2.59451 5.25 2.91668 5.25H8.96252L10.5865 2.61094C10.7554 2.33657 11.1147 2.25102 11.3891 2.41987ZM5.83334 7.58334C5.51118 7.58334 5.25001 7.84449 5.25001 8.16667C5.25001 8.48884 5.51118 8.75 5.83334 8.75H8.16668C8.48885 8.75 8.75001 8.48884 8.75001 8.16667C8.75001 7.84449 8.48885 7.58334 8.16668 7.58334H5.83334Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "Vector_2",
|
||||
"opacity": "0.5"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M6.91257 1.79347C6.96898 1.76525 7.01477 1.71948 7.043 1.66303L7.32195 1.10508C7.42946 0.890105 7.73623 0.890105 7.84374 1.10508L8.12269 1.66303C8.15093 1.71948 8.19672 1.76525 8.25313 1.79347L8.81108 2.07245C9.0261 2.17994 9.0261 2.48672 8.81108 2.5942L8.25313 2.87318C8.19672 2.9014 8.15093 2.94717 8.12269 3.00362L7.84374 3.56158C7.73623 3.77655 7.42946 3.77655 7.32195 3.56158L7.043 3.00362C7.01477 2.94717 6.96898 2.9014 6.91257 2.87318L6.35461 2.5942C6.13965 2.48672 6.13965 2.17994 6.35461 2.07245L6.91257 1.79347Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M3.80145 2.7657C3.85789 2.73748 3.90366 2.69171 3.93189 2.63526L4.11364 2.27174C4.22113 2.05677 4.5279 2.05677 4.63539 2.27174L4.81715 2.63526C4.84537 2.6917 4.89114 2.73748 4.94759 2.7657L5.3111 2.94745C5.52607 3.05494 5.52607 3.36172 5.3111 3.4692L4.94759 3.65096C4.89114 3.67919 4.84537 3.72495 4.81715 3.7814L4.63539 4.14491C4.5279 4.35988 4.22113 4.35988 4.11364 4.14491L3.93189 3.7814C3.90366 3.72495 3.85789 3.67919 3.80145 3.65096L3.43793 3.4692C3.22296 3.36172 3.22296 3.05494 3.43793 2.94745L3.80145 2.7657Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "BoxSparkleFill"
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './BoxSparkleFill.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||
props,
|
||||
ref,
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||
|
||||
Icon.displayName = 'BoxSparkleFill'
|
||||
|
||||
export default Icon
|
||||
@ -0,0 +1,27 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "13",
|
||||
"height": "20",
|
||||
"viewBox": "0 0 13 20",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Shape",
|
||||
"d": "M0 0H13V20C9.98017 20 7.26458 18.1615 6.14305 15.3576L0 0Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "LeftCorner"
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './LeftCorner.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||
props,
|
||||
ref,
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||
|
||||
Icon.displayName = 'LeftCorner'
|
||||
|
||||
export default Icon
|
||||
@ -0,0 +1,2 @@
|
||||
export { default as BoxSparkleFill } from './BoxSparkleFill'
|
||||
export { default as LeftCorner } from './LeftCorner'
|
||||
@ -0,0 +1,47 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "16",
|
||||
"height": "16",
|
||||
"viewBox": "0 0 16 16",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "Icon"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Vector",
|
||||
"d": "M3.99999 1.33325H7.99999V5.33325C7.99999 6.06963 8.59692 6.66659 9.33332 6.66659H13.3333V13.3333C13.3333 14.0697 12.7364 14.6666 12 14.6666H6.66666V13.3333H7.99999V11.9999H6.66666V10.6666H7.99999V9.33325H6.66666V7.99992H5.33332V9.33325H6.66666V10.6666H5.33332V11.9999H6.66666V13.3333H5.33332V14.6666H3.99999C3.26361 14.6666 2.66666 14.0697 2.66666 13.3333V2.66659C2.66666 1.93021 3.26361 1.33325 3.99999 1.33325Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Vector_2",
|
||||
"opacity": "0.5",
|
||||
"d": "M12.9428 4.99993C13.0415 5.09868 13.1232 5.21133 13.1859 5.33327H9.33334V1.48071C9.45528 1.54338 9.56794 1.62504 9.66668 1.72379L12.9428 4.99993Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "FileZip"
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './FileZip.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||
props,
|
||||
ref,
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||
|
||||
Icon.displayName = 'FileZip'
|
||||
|
||||
export default Icon
|
||||
@ -1,3 +1,4 @@
|
||||
export { default as File05 } from './File05'
|
||||
export { default as FileSearch02 } from './FileSearch02'
|
||||
export { default as FileZip } from './FileZip'
|
||||
export { default as Folder } from './Folder'
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "16",
|
||||
"height": "16",
|
||||
"viewBox": "0 0 16 16",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "Icon"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Vector",
|
||||
"d": "M8 1C4.1325 1 1 4.1325 1 8C1 11.0975 3.00375 13.7137 5.78625 14.6413C6.13625 14.7025 6.2675 14.4925 6.2675 14.3088C6.2675 14.1425 6.25875 13.5913 6.25875 13.005C4.5 13.3288 4.045 12.5763 3.905 12.1825C3.82625 11.9812 3.485 11.36 3.1875 11.1937C2.9425 11.0625 2.5925 10.7387 3.17875 10.73C3.73 10.7212 4.12375 11.2375 4.255 11.4475C4.885 12.5062 5.89125 12.2088 6.29375 12.025C6.355 11.57 6.53875 11.2638 6.74 11.0887C5.1825 10.9137 3.555 10.31 3.555 7.6325C3.555 6.87125 3.82625 6.24125 4.2725 5.75125C4.2025 5.57625 3.9575 4.85875 4.3425 3.89625C4.3425 3.89625 4.92875 3.7125 6.2675 4.61375C6.8275 4.45625 7.4225 4.3775 8.0175 4.3775C8.6125 4.3775 9.2075 4.45625 9.7675 4.61375C11.1063 3.70375 11.6925 3.89625 11.6925 3.89625C12.0775 4.85875 11.8325 5.57625 11.7625 5.75125C12.2087 6.24125 12.48 6.8625 12.48 7.6325C12.48 10.3187 10.8438 10.9137 9.28625 11.0887C9.54 11.3075 9.75875 11.7275 9.75875 12.3837C9.75875 13.32 9.75 14.0725 9.75 14.3088C9.75 14.4925 9.88125 14.7113 10.2312 14.6413C11.6209 14.1721 12.8284 13.279 13.6839 12.0877C14.5393 10.8963 14.9996 9.46668 15 8C15 4.1325 11.8675 1 8 1Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Github"
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Github.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||
props,
|
||||
ref,
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||
|
||||
Icon.displayName = 'Github'
|
||||
|
||||
export default Icon
|
||||
@ -1,8 +0,0 @@
|
||||
.modal {
|
||||
max-width: 1024px !important;
|
||||
border-radius: 12px !important;
|
||||
margin-top: 60px;
|
||||
margin-bottom: 60px;
|
||||
padding: 0 !important;
|
||||
overflow-y: auto;
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
import { Fragment, useCallback, useEffect } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import { RiCloseLine } from '@remixicon/react'
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import Button from '@/app/components/base/button'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type DialogProps = {
|
||||
className?: string
|
||||
children: ReactNode
|
||||
show: boolean
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
const MenuDialog = ({
|
||||
className,
|
||||
children,
|
||||
show,
|
||||
onClose,
|
||||
}: DialogProps) => {
|
||||
const close = useCallback(() => onClose?.(), [onClose])
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape')
|
||||
close()
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', handleKeyDown)
|
||||
return () => {
|
||||
document.removeEventListener('keydown', handleKeyDown)
|
||||
}
|
||||
}, [close])
|
||||
|
||||
return (
|
||||
<Transition appear show={show} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-40" onClose={() => {}}>
|
||||
<div className="fixed inset-0">
|
||||
<div className="flex flex-col items-center justify-center min-h-full">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className={cn('grow relative w-full h-full p-0 overflow-hidden text-left align-middle transition-all transform bg-background-sidenav-bg backdrop-blur-md', className)}>
|
||||
<div className='absolute right-0 top-0 h-full w-1/2 bg-components-panel-bg'/>
|
||||
<div className='absolute top-6 right-6 flex flex-col items-center'>
|
||||
<Button
|
||||
variant='tertiary'
|
||||
size='large'
|
||||
className='px-2'
|
||||
onClick={close}
|
||||
>
|
||||
<RiCloseLine className='w-5 h-5' />
|
||||
</Button>
|
||||
<div className='mt-1 text-text-tertiary system-2xs-medium-uppercase'>ESC</div>
|
||||
</div>
|
||||
{children}
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition >
|
||||
)
|
||||
}
|
||||
|
||||
export default MenuDialog
|
||||
@ -0,0 +1,30 @@
|
||||
'use client'
|
||||
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Link from 'next/link'
|
||||
import classNames from '@/utils/classnames'
|
||||
import { Group } from '@/app/components/base/icons/src/vender/other'
|
||||
type PluginsNavProps = {
|
||||
className?: string
|
||||
}
|
||||
|
||||
const PluginsNav = ({
|
||||
className,
|
||||
}: PluginsNavProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Link href="/plugins" className={classNames(
|
||||
className, 'group',
|
||||
)}>
|
||||
<div className='flex flex-row p-1.5 gap-0.5 items-center justify-center rounded-xl system-xs-medium-uppercase hover:bg-state-base-hover text-text-tertiary hover:text-text-secondary'>
|
||||
<div className='flex w-4 h-4 justify-center items-center'>
|
||||
<Group />
|
||||
</div>
|
||||
<span className='px-0.5'>{t('common.menus.plugins')}</span>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export default PluginsNav
|
||||
@ -0,0 +1,62 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import copy from 'copy-to-clipboard'
|
||||
|
||||
import {
|
||||
RiClipboardLine,
|
||||
} from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ClipboardCheck } from '../../base/icons/src/vender/line/files'
|
||||
import Tooltip from '../../base/tooltip'
|
||||
import cn from '@/utils/classnames'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
type Props = {
|
||||
label: string
|
||||
labelWidthClassName?: string
|
||||
value: string
|
||||
}
|
||||
|
||||
const KeyValueItem: FC<Props> = ({
|
||||
label,
|
||||
labelWidthClassName = 'w-10',
|
||||
value,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [isCopied, setIsCopied] = useState(false)
|
||||
const handleCopy = useCallback(() => {
|
||||
copy(value)
|
||||
setIsCopied(true)
|
||||
}, [value])
|
||||
|
||||
useEffect(() => {
|
||||
if (isCopied) {
|
||||
const timer = setTimeout(() => {
|
||||
setIsCopied(false)
|
||||
}, 2000)
|
||||
return () => {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
}
|
||||
}, [isCopied])
|
||||
|
||||
const CopyIcon = isCopied ? ClipboardCheck : RiClipboardLine
|
||||
|
||||
return (
|
||||
<div className='flex items-center gap-1 self-stretch'>
|
||||
<span className={cn('flex flex-col justify-center items-start text-text-tertiary system-xs-medium', labelWidthClassName)}>{label}</span>
|
||||
<div className='flex justify-center items-center gap-0.5'>
|
||||
<span className='system-xs-medium text-text-secondary'>
|
||||
{value}
|
||||
</span>
|
||||
<Tooltip popupContent={t(`common.operation.${isCopied ? 'copied' : 'copy'}`)} position='top'>
|
||||
<ActionButton onClick={handleCopy}>
|
||||
<CopyIcon className='w-3.5 h-3.5 text-text-tertiary' />
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(KeyValueItem)
|
||||
@ -0,0 +1,46 @@
|
||||
import { RiCheckLine } from '@remixicon/react'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
const Icon = ({
|
||||
className,
|
||||
src,
|
||||
installed = false,
|
||||
}: {
|
||||
className?: string
|
||||
src: string | {
|
||||
'content': string
|
||||
'background': string
|
||||
}
|
||||
installed?: boolean
|
||||
}) => {
|
||||
if (typeof src === 'object') {
|
||||
return (
|
||||
<div className={cn('relative', className)}>
|
||||
<AppIcon
|
||||
size='large'
|
||||
iconType={'emoji'}
|
||||
icon={src.content}
|
||||
background={src.background}
|
||||
className='rounded-md'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={cn('shrink-0 relative w-10 h-10 rounded-md bg-center bg-no-repeat bg-contain', className)}
|
||||
style={{
|
||||
backgroundImage: `url(${src})`,
|
||||
}}
|
||||
>
|
||||
{installed
|
||||
&& <div className='flex justify-center items-center gap-2 absolute bottom-[-4px] right-[-4px] w-[18px] h-[18px] rounded-full border-2 border-components-panel-bg bg-state-success-solid'>
|
||||
<RiCheckLine className='w-3 h-3 text-text-primary-on-surface' />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Icon
|
||||
@ -0,0 +1,12 @@
|
||||
import { LeftCorner } from '../../../base/icons/src/vender/plugin'
|
||||
|
||||
const CornerMark = ({ text }: { text: string }) => {
|
||||
return (
|
||||
<div className='absolute top-0 right-0 flex pl-[13px] '>
|
||||
<LeftCorner className="text-background-section" />
|
||||
<div className="h-5 leading-5 rounded-tr-xl pr-2 bg-background-section text-text-tertiary system-2xs-medium-uppercase">{text}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CornerMark
|
||||
@ -0,0 +1,31 @@
|
||||
import type { FC } from 'react'
|
||||
import React, { useMemo } from 'react'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
text: string
|
||||
descriptionLineRows: number
|
||||
}
|
||||
|
||||
const Description: FC<Props> = ({
|
||||
className,
|
||||
text,
|
||||
descriptionLineRows,
|
||||
}) => {
|
||||
const lineClassName = useMemo(() => {
|
||||
if (descriptionLineRows === 1)
|
||||
return 'h-4 truncate'
|
||||
else if (descriptionLineRows === 2)
|
||||
return 'h-8 line-clamp-2'
|
||||
else
|
||||
return 'h-12 line-clamp-3'
|
||||
}, [descriptionLineRows])
|
||||
return (
|
||||
<div className={cn('text-text-tertiary system-xs-regular', lineClassName, className)}>
|
||||
{text}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Description
|
||||
@ -0,0 +1,19 @@
|
||||
import { RiInstallLine } from '@remixicon/react'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
|
||||
type Props = {
|
||||
downloadCount: number
|
||||
}
|
||||
|
||||
const DownloadCount = ({
|
||||
downloadCount,
|
||||
}: Props) => {
|
||||
return (
|
||||
<div className="flex items-center space-x-1 text-text-tertiary">
|
||||
<RiInstallLine className="shrink-0 w-3 h-3" />
|
||||
<div className="system-xs-regular">{formatNumber(downloadCount)}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DownloadCount
|
||||
@ -0,0 +1,30 @@
|
||||
import cn from '@/utils/classnames'
|
||||
type Props = {
|
||||
className?: string
|
||||
orgName?: string
|
||||
packageName: string
|
||||
packageNameClassName?: string
|
||||
}
|
||||
|
||||
const OrgInfo = ({
|
||||
className,
|
||||
orgName,
|
||||
packageName,
|
||||
packageNameClassName,
|
||||
}: Props) => {
|
||||
return (
|
||||
<div className={cn('flex items-center h-4 space-x-0.5', className)}>
|
||||
{orgName && (
|
||||
<>
|
||||
<span className='shrink-0 text-text-tertiary system-xs-regular'>{orgName}</span>
|
||||
<span className='shrink-0 text-text-quaternary system-xs-regular'>/</span>
|
||||
</>
|
||||
)}
|
||||
<span className={cn('shrink-0 w-0 grow truncate text-text-tertiary system-xs-regular', packageNameClassName)}>
|
||||
{packageName}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default OrgInfo
|
||||
@ -0,0 +1,46 @@
|
||||
import { Group } from '../../../base/icons/src/vender/other'
|
||||
import Title from './title'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
wrapClassName: string
|
||||
loadingFileName: string
|
||||
}
|
||||
|
||||
export const LoadingPlaceholder = ({ className }: { className?: string }) => (
|
||||
<div className={cn('h-2 rounded-sm opacity-20 bg-text-quaternary', className)} />
|
||||
)
|
||||
|
||||
const Placeholder = ({
|
||||
wrapClassName,
|
||||
loadingFileName,
|
||||
}: Props) => {
|
||||
return (
|
||||
<div className={wrapClassName}>
|
||||
<div className="flex">
|
||||
<div
|
||||
className='flex w-10 h-10 p-1 justify-center items-center gap-2 rounded-[10px]
|
||||
border-[0.5px] border-components-panel-border bg-background-default backdrop-blur-sm'>
|
||||
<div className='flex w-5 h-5 justify-center items-center'>
|
||||
<Group className='text-text-tertiary' />
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-3 grow">
|
||||
<div className="flex items-center h-5">
|
||||
<Title title={loadingFileName} />
|
||||
</div>
|
||||
<div className={cn('flex items-center h-4 space-x-0.5')}>
|
||||
<LoadingPlaceholder className="w-[41px]" />
|
||||
<span className='shrink-0 text-text-quaternary system-xs-regular'>
|
||||
·
|
||||
</span>
|
||||
<LoadingPlaceholder className="w-[180px]" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<LoadingPlaceholder className="mt-3 w-[420px]" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Placeholder
|
||||
@ -0,0 +1,13 @@
|
||||
const Title = ({
|
||||
title,
|
||||
}: {
|
||||
title: string
|
||||
}) => {
|
||||
return (
|
||||
<div className='max-w-[150px] truncate text-text-secondary system-md-semibold'>
|
||||
{title}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Title
|
||||
@ -0,0 +1,73 @@
|
||||
import { PluginType } from '../types'
|
||||
|
||||
export const toolNotion = {
|
||||
type: PluginType.tool,
|
||||
org: 'Notion',
|
||||
name: 'notion page search',
|
||||
version: '1.2.0',
|
||||
latest_version: '1.3.0',
|
||||
icon: 'https://via.placeholder.com/150',
|
||||
label: {
|
||||
'en-US': 'Notion Page Search',
|
||||
'zh-Hans': 'Notion 页面搜索',
|
||||
},
|
||||
brief: {
|
||||
'en-US': 'Description: Search Notion pages and open visited ones faster. No admin access required.More and more info...More and more info...More and more info...',
|
||||
'zh-Hans': '搜索 Notion 页面并更快地打开已访问的页面。无需管理员访问权限。More and more info...More and more info...More and more info...',
|
||||
},
|
||||
}
|
||||
|
||||
export const extensionDallE = {
|
||||
type: PluginType.extension,
|
||||
org: 'OpenAI',
|
||||
name: 'DALL-E',
|
||||
version: '1.1.0',
|
||||
latest_version: '1.2.0',
|
||||
install_count: 1234,
|
||||
icon: 'https://via.placeholder.com/150',
|
||||
label: {
|
||||
'en-US': 'DALL-E',
|
||||
'zh-Hans': 'DALL-E',
|
||||
},
|
||||
brief: {
|
||||
'en-US': 'Description: A simple plugin to use OpenAI DALL-E model.',
|
||||
'zh-Hans': '一个使用 OpenAI DALL-E 模型的简单插件。',
|
||||
},
|
||||
}
|
||||
|
||||
export const modelGPT4 = {
|
||||
type: PluginType.model,
|
||||
org: 'OpenAI',
|
||||
name: 'GPT-4',
|
||||
version: '1.0.0',
|
||||
latest_version: '1.0.0',
|
||||
install_count: 99999,
|
||||
icon: 'https://via.placeholder.com/150',
|
||||
label: {
|
||||
'en-US': 'GPT-4',
|
||||
'zh-Hans': 'GPT-4',
|
||||
},
|
||||
brief: {
|
||||
'en-US': 'Description: A simple plugin to use OpenAI GPT-4 model.',
|
||||
'zh-Hans': '一个使用 OpenAI GPT-4 模型的简单插件。',
|
||||
},
|
||||
}
|
||||
|
||||
export const customTool = {
|
||||
type: PluginType.tool,
|
||||
name: 'notion page search',
|
||||
version: '1.2.0',
|
||||
latest_version: '1.3.0',
|
||||
icon: {
|
||||
content: '🕵️',
|
||||
background: '#FEF7C3',
|
||||
},
|
||||
label: {
|
||||
'en-US': 'Notion Page Search',
|
||||
'zh-Hans': 'Notion 页面搜索',
|
||||
},
|
||||
brief: {
|
||||
'en-US': 'Description: Search Notion pages and open visited ones faster. No admin access required.More and more info...More and more info...More and more info...',
|
||||
'zh-Hans': '搜索 Notion 页面并更快地打开已访问的页面。无需管理员访问权限。More and more info...More and more info...More and more info...',
|
||||
},
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
import DownloadCount from './base/download-count'
|
||||
|
||||
type Props = {
|
||||
downloadCount?: number
|
||||
tags: string[]
|
||||
}
|
||||
|
||||
const CardMoreInfo = ({
|
||||
downloadCount,
|
||||
tags,
|
||||
}: Props) => {
|
||||
return (
|
||||
<div className="flex items-center h-5">
|
||||
{downloadCount !== undefined && <DownloadCount downloadCount={downloadCount} />}
|
||||
{downloadCount !== undefined && tags && tags.length > 0 && <div className="mx-2 text-text-quaternary system-xs-regular">·</div>}
|
||||
{tags && tags.length > 0 && (
|
||||
<>
|
||||
<div className="flex flex-wrap space-x-2 h-4 overflow-hidden">
|
||||
{tags.map(tag => (
|
||||
<div
|
||||
key={tag}
|
||||
className="flex space-x-1 system-xs-regular max-w-[120px] overflow-hidden"
|
||||
title={`# ${tag}`}
|
||||
>
|
||||
<span className="text-text-quaternary">#</span>
|
||||
<span className="truncate text-text-tertiary">{tag}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CardMoreInfo
|
||||
@ -0,0 +1,83 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { RiVerifiedBadgeLine } from '@remixicon/react'
|
||||
import type { Plugin } from '../types'
|
||||
import Icon from '../card/base/card-icon'
|
||||
import CornerMark from './base/corner-mark'
|
||||
import Title from './base/title'
|
||||
import OrgInfo from './base/org-info'
|
||||
import Description from './base/description'
|
||||
import Placeholder from './base/placeholder'
|
||||
import cn from '@/utils/classnames'
|
||||
import { useGetLanguage } from '@/context/i18n'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
payload: Plugin
|
||||
titleLeft?: React.ReactNode
|
||||
installed?: boolean
|
||||
hideCornerMark?: boolean
|
||||
descriptionLineRows?: number
|
||||
footer?: React.ReactNode
|
||||
isLoading?: boolean
|
||||
loadingFileName?: string
|
||||
}
|
||||
|
||||
const Card = ({
|
||||
className,
|
||||
payload,
|
||||
titleLeft,
|
||||
installed,
|
||||
hideCornerMark,
|
||||
descriptionLineRows = 2,
|
||||
footer,
|
||||
isLoading = false,
|
||||
loadingFileName,
|
||||
}: Props) => {
|
||||
const locale = useGetLanguage()
|
||||
|
||||
const { type, name, org, label, brief, icon } = payload
|
||||
|
||||
const getLocalizedText = (obj: Record<string, string> | undefined) =>
|
||||
obj?.[locale] || obj?.['en-US'] || ''
|
||||
|
||||
const wrapClassName = cn('relative p-4 pb-3 border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg hover-bg-components-panel-on-panel-item-bg rounded-xl shadow-xs', className)
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Placeholder
|
||||
wrapClassName={wrapClassName}
|
||||
loadingFileName={loadingFileName!}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={wrapClassName}>
|
||||
{!hideCornerMark && <CornerMark text={type} />}
|
||||
{/* Header */}
|
||||
<div className="flex">
|
||||
<Icon src={icon} installed={installed} />
|
||||
<div className="ml-3 grow">
|
||||
<div className="flex items-center h-5">
|
||||
<Title title={getLocalizedText(label)} />
|
||||
<RiVerifiedBadgeLine className="shrink-0 ml-0.5 w-4 h-4 text-text-accent" />
|
||||
{titleLeft} {/* This can be version badge */}
|
||||
</div>
|
||||
<OrgInfo
|
||||
className="mt-0.5"
|
||||
orgName={org}
|
||||
packageName={name}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Description
|
||||
className="mt-3"
|
||||
text={getLocalizedText(brief)}
|
||||
descriptionLineRows={descriptionLineRows}
|
||||
/>
|
||||
{footer && <div>{footer}</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(Card)
|
||||
@ -0,0 +1,270 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Button from '@/app/components/base/button'
|
||||
import type { Item } from '@/app/components/base/select'
|
||||
import { PortalSelect } from '@/app/components/base/select'
|
||||
import type { GitHubRepoReleaseResponse } from '@/app/components/plugins/types'
|
||||
import { installPackageFromGitHub } from '@/service/plugins'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
|
||||
type InstallFromGitHubProps = {
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
type InstallStep = 'url' | 'version' | 'package' | 'installed'
|
||||
|
||||
type GitHubUrlInfo = {
|
||||
isValid: boolean
|
||||
owner?: string
|
||||
repo?: string
|
||||
}
|
||||
|
||||
const InstallFromGitHub: React.FC<InstallFromGitHubProps> = ({ onClose }) => {
|
||||
const [step, setStep] = useState<InstallStep>('url')
|
||||
const [repoUrl, setRepoUrl] = useState('')
|
||||
const [selectedVersion, setSelectedVersion] = useState('')
|
||||
const [selectedPackage, setSelectedPackage] = useState('')
|
||||
const [releases, setReleases] = useState<GitHubRepoReleaseResponse[]>([])
|
||||
|
||||
const versions: Item[] = releases.map(release => ({
|
||||
value: release.tag_name,
|
||||
name: release.tag_name,
|
||||
}))
|
||||
|
||||
const packages: Item[] = selectedVersion
|
||||
? (releases
|
||||
.find(release => release.tag_name === selectedVersion)
|
||||
?.assets.map(asset => ({
|
||||
value: asset.browser_download_url,
|
||||
name: asset.name,
|
||||
})) || [])
|
||||
: []
|
||||
|
||||
const parseGitHubUrl = (url: string): GitHubUrlInfo => {
|
||||
const githubUrlRegex = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/?$/
|
||||
const match = url.match(githubUrlRegex)
|
||||
|
||||
if (match) {
|
||||
return {
|
||||
isValid: true,
|
||||
owner: match[1],
|
||||
repo: match[2],
|
||||
}
|
||||
}
|
||||
|
||||
return { isValid: false }
|
||||
}
|
||||
|
||||
const handleInstall = async () => {
|
||||
try {
|
||||
const response = await installPackageFromGitHub({ repo: repoUrl, version: selectedVersion, package: selectedPackage })
|
||||
if (response.plugin_unique_identifier) {
|
||||
setStep('installed')
|
||||
console.log('Package installed:')
|
||||
}
|
||||
else {
|
||||
console.error('Failed to install package:')
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error installing package:')
|
||||
}
|
||||
}
|
||||
|
||||
const handleNext = async () => {
|
||||
switch (step) {
|
||||
case 'url': {
|
||||
const { isValid, owner, repo } = parseGitHubUrl(repoUrl)
|
||||
if (!isValid || !owner || !repo) {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: 'Invalid GitHub URL. Please enter a valid URL in the format: https://github.com/owner/repo',
|
||||
})
|
||||
break
|
||||
}
|
||||
try {
|
||||
const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/releases`)
|
||||
if (!res.ok)
|
||||
throw new Error('Failed to fetch releases')
|
||||
const data = await res.json()
|
||||
const formattedReleases = data.map((release: any) => ({
|
||||
tag_name: release.tag_name,
|
||||
assets: release.assets.map((asset: any) => ({
|
||||
browser_download_url: asset.browser_download_url,
|
||||
id: asset.id,
|
||||
name: asset.name,
|
||||
})),
|
||||
}))
|
||||
setReleases(formattedReleases)
|
||||
setStep('version')
|
||||
}
|
||||
catch (error) {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: 'Failed to fetch repository release',
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'version':
|
||||
setStep('package')
|
||||
break
|
||||
case 'package':
|
||||
handleInstall()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const isInputValid = () => {
|
||||
switch (step) {
|
||||
case 'url':
|
||||
return !!repoUrl.trim()
|
||||
case 'version':
|
||||
return !!selectedVersion
|
||||
case 'package':
|
||||
return !!selectedPackage
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
const InfoRow = ({ label, value }: { label: string; value: string }) => (
|
||||
<div className='flex items-center gap-3'>
|
||||
<div className='flex w-[72px] items-center gap-2'>
|
||||
<div className='text-text-tertiary system-sm-medium'>
|
||||
{label}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex-grow overflow-hidden text-text-secondary text-ellipsis system-sm-medium'>
|
||||
{value}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isShow={true}
|
||||
onClose={onClose}
|
||||
className='flex min-w-[480px] p-0 flex-col items-start rounded-2xl border-[0.5px]
|
||||
border-components-panel-border bg-components-panel-bg shadows-shadow-xl'
|
||||
closable
|
||||
>
|
||||
<div className='flex pt-6 pl-6 pb-3 pr-14 items-start gap-2 self-stretch'>
|
||||
<div className='flex flex-col items-start gap-1 flex-grow'>
|
||||
<div className='self-stretch text-text-primary title-2xl-semi-bold'>
|
||||
Install plugin from GitHub
|
||||
</div>
|
||||
<div className='self-stretch text-text-tertiary system-xs-regular'>
|
||||
{step !== 'installed' && 'Please make sure that you only install plugins from a trusted source.'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`flex px-6 py-3 flex-col justify-center items-start self-stretch ${step === 'installed' ? 'gap-2' : 'gap-4'}`}>
|
||||
{step === 'url' && (
|
||||
<>
|
||||
<label
|
||||
htmlFor='repoUrl'
|
||||
className='flex flex-col justify-center items-start self-stretch text-text-secondary'
|
||||
>
|
||||
<span className='system-sm-semibold'>GitHub repository</span>
|
||||
</label>
|
||||
<input
|
||||
type='url'
|
||||
id='repoUrl'
|
||||
name='repoUrl'
|
||||
value={repoUrl}
|
||||
onChange={e => setRepoUrl(e.target.value)} // TODO: needs to verify the url
|
||||
className='flex items-center self-stretch rounded-lg border border-components-input-border-active
|
||||
bg-components-input-bg-active shadows-shadow-xs p-2 gap-[2px] flex-grow overflow-hidden
|
||||
text-components-input-text-filled text-ellipsis system-sm-regular'
|
||||
placeholder='Please enter GitHub repo URL'
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{step === 'version' && (
|
||||
<>
|
||||
<label
|
||||
htmlFor='version'
|
||||
className='flex flex-col justify-center items-start self-stretch text-text-secondary'
|
||||
>
|
||||
<span className='system-sm-semibold'>Select version</span>
|
||||
</label>
|
||||
<PortalSelect
|
||||
value={selectedVersion}
|
||||
onSelect={item => setSelectedVersion(item.value as string)}
|
||||
items={versions}
|
||||
placeholder="Please select a version"
|
||||
popupClassName='w-[432px] z-[1001]'
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{step === 'package' && (
|
||||
<>
|
||||
<label
|
||||
htmlFor='package'
|
||||
className='flex flex-col justify-center items-start self-stretch text-text-secondary'
|
||||
>
|
||||
<span className='system-sm-semibold'>Select package</span>
|
||||
</label>
|
||||
<PortalSelect
|
||||
value={selectedPackage}
|
||||
onSelect={item => setSelectedPackage(item.value as string)}
|
||||
items={packages}
|
||||
placeholder="Please select a package"
|
||||
popupClassName='w-[432px] z-[1001]'
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{step === 'installed' && (
|
||||
<>
|
||||
<div className='text-text-secondary system-md-regular'>The plugin has been installed successfully.</div>
|
||||
<div className='flex w-full p-4 flex-col justify-center items-start gap-2 rounded-2xl bg-background-section-burn'>
|
||||
{[
|
||||
{ label: 'Repository', value: repoUrl },
|
||||
{ label: 'Version', value: selectedVersion },
|
||||
{ label: 'Package', value: selectedPackage },
|
||||
].map(({ label, value }) => (
|
||||
<InfoRow key={label} label={label} value={value} />
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className='flex p-6 pt-5 justify-end items-center gap-2 self-stretch'>
|
||||
{step === 'installed'
|
||||
? (
|
||||
<Button
|
||||
variant='primary'
|
||||
className='min-w-[72px]'
|
||||
onClick={onClose}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
)
|
||||
: (
|
||||
<>
|
||||
<Button
|
||||
variant='secondary'
|
||||
className='min-w-[72px]'
|
||||
onClick={onClose}
|
||||
>
|
||||
{step === 'url' ? 'Cancel' : 'Back'}
|
||||
</Button>
|
||||
<Button
|
||||
variant='primary'
|
||||
className='min-w-[72px]'
|
||||
onClick={handleNext}
|
||||
disabled={!isInputValid()}
|
||||
>
|
||||
{step === 'package' ? 'Install' : 'Next'}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default InstallFromGitHub
|
||||