mirror of
https://github.com/langgenius/webapp-conversation.git
synced 2026-02-02 00:25:19 +08:00
Compare commits
120 Commits
feat/suppo
...
510ac2afaa
| Author | SHA1 | Date | |
|---|---|---|---|
| 510ac2afaa | |||
| b26ffc7f1a | |||
| 123d55694a | |||
| 0d220f5a54 | |||
| ee5ed029bb | |||
| 6b0302e093 | |||
| 55a77ea86d | |||
| c4b8029702 | |||
| aabdcdb3df | |||
| 71a13ba418 | |||
| 6a8f4e6db1 | |||
| 9b1288fc96 | |||
| 518ec4fb9d | |||
| 12e52b5d7e | |||
| 238611f547 | |||
| d28e2c29fc | |||
| 34bb34f890 | |||
| 18a4229464 | |||
| 0cb8fdcf15 | |||
| 532aba026a | |||
| 7a07e8d56b | |||
| c907432782 | |||
| 672ad29e5d | |||
| 2d426902bd | |||
| 3025356fa7 | |||
| ba95a431b3 | |||
| 9ff81c0306 | |||
| f809b706a0 | |||
| 05dcfcf0ca | |||
| 2b1882a5e3 | |||
| d3909927af | |||
| 17007b2014 | |||
| e91a1f6194 | |||
| 74a656fda2 | |||
| 8f02afed98 | |||
| dc1659463e | |||
| b35f0effe5 | |||
| 60a33804cc | |||
| 7495bf44a2 | |||
| d009b00012 | |||
| 1249ea88c9 | |||
| d3482db74d | |||
| beda954867 | |||
| 4ae03c2101 | |||
| 7216f40bee | |||
| e9923e8220 | |||
| 9a7e1be35d | |||
| db4e78d796 | |||
| 87c81b99dd | |||
| a8ea35e5c6 | |||
| 41406a8596 | |||
| 9d2d092e9e | |||
| 1f5607221a | |||
| 009674b231 | |||
| 25ef02d2aa | |||
| f6f65cff68 | |||
| 8c6302d1fc | |||
| 291e9a067b | |||
| ac0e3e807d | |||
| b7f703852e | |||
| ef15747e4a | |||
| f9bd745bb0 | |||
| e2b37c1a9c | |||
| 0f490de7ff | |||
| aaeb440210 | |||
| b45262add9 | |||
| 368c6b3dae | |||
| f6fb9c7cea | |||
| 69044eb8a3 | |||
| cafd643c00 | |||
| 1c12b1dce3 | |||
| 94d09ed23b | |||
| 5d313f7463 | |||
| 97203f5ac6 | |||
| 349e081f1f | |||
| 7f24387eef | |||
| 5a1c84e79f | |||
| 8d21cbc2da | |||
| 7bb19ed8ec | |||
| 5a85f0d427 | |||
| 96bd12af44 | |||
| 484a5dc102 | |||
| 10eb176f72 | |||
| fcd6a0215d | |||
| f6b4b4a361 | |||
| df0ae34be1 | |||
| 884e72b4f0 | |||
| 6933b5923b | |||
| 30509d92a3 | |||
| c73753138d | |||
| 2bd93dcbaa | |||
| 1df62f49f1 | |||
| 0ddaa92a12 | |||
| 65f90f56ac | |||
| 924a008de8 | |||
| 631a4f1f21 | |||
| 25ba4ac476 | |||
| 83695999ea | |||
| b62e34c6fa | |||
| 2019d8f3e3 | |||
| ed839618a7 | |||
| 3de8abdd7a | |||
| 4a75e3774a | |||
| f7ff288ff1 | |||
| c8f730208d | |||
| 5dc3658e19 | |||
| ae70d2fee1 | |||
| dd1c540a61 | |||
| 1286683031 | |||
| 9c0ea56058 | |||
| dfba42c021 | |||
| a01fc9ef49 | |||
| bed53e4ad2 | |||
| 3838537a42 | |||
| 8f3dcd5462 | |||
| 2a5a282519 | |||
| 754b77657b | |||
| eeb54bc820 | |||
| 4867d52b8f | |||
| bd0b742247 |
16
.cursor/rules/api-client.mdc
Normal file
16
.cursor/rules/api-client.mdc
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
description: API client usage and streaming
|
||||
---
|
||||
|
||||
### API Client
|
||||
|
||||
- Use domain functions in `[service/index.ts](mdc:service/index.ts)` for app features.
|
||||
- Prefer `get/post/put/del` from `[service/base.ts](mdc:service/base.ts)`; they apply base options, timeout, and error toasts.
|
||||
- Set request bodies via `options.body`; it will be JSON-stringified automatically.
|
||||
- Add query via `options.params` on GET.
|
||||
- Downloads: set `Content-type` header to `application/octet-stream`.
|
||||
|
||||
### SSE Streaming
|
||||
|
||||
- For streaming responses, use `ssePost` from `[service/base.ts](mdc:service/base.ts)` and supply callbacks: `onData`, `onCompleted`, `onThought`, `onFile`, `onMessageEnd`, `onMessageReplace`, `onWorkflowStarted`, `onNodeStarted`, `onNodeFinished`, `onWorkflowFinished`, `onError`.
|
||||
- Chat messages helper: `sendChatMessage` in `[service/index.ts](mdc:service/index.ts)` preconfigures streaming.
|
||||
10
.cursor/rules/i18n.mdc
Normal file
10
.cursor/rules/i18n.mdc
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
description: i18n usage and locale resolution
|
||||
---
|
||||
|
||||
### i18n
|
||||
|
||||
- Server locale: `getLocaleOnServer()` reads cookie or negotiates from headers: `[i18n/server.ts](mdc:i18n/server.ts)`.
|
||||
- Client locale: use `getLocaleOnClient()` / `setLocaleOnClient()` in `[i18n/client.ts](mdc:i18n/client.ts)`; uses `LOCALE_COOKIE_NAME` from config.
|
||||
- Place translations in `i18n/lang/**`. Keep keys synchronized across locales.
|
||||
- Render `<html lang>` using the resolved locale in `[app/layout.tsx](mdc:app/layout.tsx)`.
|
||||
21
.cursor/rules/project-structure.mdc
Normal file
21
.cursor/rules/project-structure.mdc
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
### Project Structure Overview
|
||||
|
||||
- **App Router (Next.js 14)**: Entry is `app/layout.tsx` and `app/page.tsx`.
|
||||
- **API Routes**: Located under `app/api/**`. Server handlers live in `route.ts` files per folder.
|
||||
- **Components**: UI under `app/components/**` with feature folders (e.g., `chat`, `workflow`, `base`).
|
||||
- **Services (API client)**: Client-side HTTP/SSE utilities in `[service/base.ts](mdc:service/base.ts)` and domain methods in `[service/index.ts](mdc:service/index.ts)`.
|
||||
- **Config**: Global config in `[config/index.ts](mdc:config/index.ts)` and Next config in `[next.config.js](mdc:next.config.js)`.
|
||||
- **i18n**: Client/server helpers in `[i18n/client.ts](mdc:i18n/client.ts)` and `[i18n/server.ts](mdc:i18n/server.ts)`, with resources in `i18n/lang/**`.
|
||||
- **Styles**: Tailwind setup `[tailwind.config.js](mdc:tailwind.config.js)`, global styles under `app/styles/**`.
|
||||
|
||||
Key entrypoints:
|
||||
|
||||
- Layout: `[app/layout.tsx](mdc:app/layout.tsx)`
|
||||
- Home page: `[app/page.tsx](mdc:app/page.tsx)`
|
||||
- HTTP utilities: `[service/base.ts](mdc:service/base.ts)`
|
||||
- API domain functions: `[service/index.ts](mdc:service/index.ts)`
|
||||
- Internationalization: `[i18n/server.ts](mdc:i18n/server.ts)`, `[i18n/client.ts](mdc:i18n/client.ts)`
|
||||
20
.cursor/rules/typescript-react.mdc
Normal file
20
.cursor/rules/typescript-react.mdc
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
globs: *.ts,*.tsx
|
||||
---
|
||||
|
||||
### TypeScript/React Conventions
|
||||
|
||||
- **Strict TS**: `strict: true` in `tsconfig.json`. Avoid `any`. Prefer explicit function signatures for exports.
|
||||
- **Paths**: Use `@/*` alias (tsconfig `paths`) for absolute imports.
|
||||
- **React 18**: Prefer function components. Use `React.memo` only for measurable perf wins.
|
||||
- **Hooks**: Co-locate hooks under `hooks/**`. Keep hook names prefixed with `use`.
|
||||
- **App Router**: Server components by default. Mark client components with `'use client'` when needed.
|
||||
- **Styling**: Tailwind-first; SCSS only where necessary.
|
||||
- **Classnames**: Use `classnames` or `tailwind-merge` for conditional classes.
|
||||
- **Control Flow**: Early returns, handle edge cases first; avoid deep nesting.
|
||||
|
||||
### Next.js Notes
|
||||
|
||||
- Route handlers belong in `app/api/**/route.ts`.
|
||||
- Do not import server-only modules into client components.
|
||||
- Keep environment access to server files; avoid exposing secrets.
|
||||
11
.cursor/rules/ui-components.mdc
Normal file
11
.cursor/rules/ui-components.mdc
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
description: UI components and styling conventions
|
||||
---
|
||||
|
||||
### UI Components
|
||||
|
||||
- Component folders under `app/components/**`; keep base primitives in `base/**` (buttons, icons, inputs, uploader, etc.).
|
||||
- Larger features (chat, workflow) live in their own folders with `index.tsx` and submodules.
|
||||
- Prefer colocated `style.module.css` or Tailwind classes. Global styles in `app/styles/**`.
|
||||
- Use `app/components/base/toast` for error/display notifications.
|
||||
- Avoid unnecessary client components; mark with `'use client'` only when needed (state, effects, browser APIs).
|
||||
6
.env.example
Normal file
6
.env.example
Normal file
@ -0,0 +1,6 @@
|
||||
# APP ID
|
||||
NEXT_PUBLIC_APP_ID=
|
||||
# APP API key
|
||||
NEXT_PUBLIC_APP_KEY=
|
||||
# API url prefix
|
||||
NEXT_PUBLIC_API_URL=
|
||||
@ -1,28 +0,0 @@
|
||||
{
|
||||
"extends": [
|
||||
"@antfu",
|
||||
"plugin:react-hooks/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/consistent-type-definitions": [
|
||||
"error",
|
||||
"type"
|
||||
],
|
||||
"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"
|
||||
}
|
||||
}
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@ -28,6 +28,7 @@ yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
.env
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
@ -46,5 +47,7 @@ package-lock.json
|
||||
yarn.lock
|
||||
.yarnrc.yml
|
||||
|
||||
# mcp
|
||||
.serena
|
||||
# pmpm
|
||||
pnpm-lock.yaml
|
||||
pnpm-lock.yaml
|
||||
|
||||
1
.husky/pre-commit
Normal file
1
.husky/pre-commit
Normal file
@ -0,0 +1 @@
|
||||
pnpm lint-staged
|
||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -4,7 +4,7 @@
|
||||
"prettier.enable": false,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"[python]": {
|
||||
"editor.formatOnType": true
|
||||
@ -29,4 +29,4 @@
|
||||
"i18n/lang",
|
||||
"app/api/messages"
|
||||
]
|
||||
}
|
||||
}
|
||||
17
Dockerfile
17
Dockerfile
@ -1,12 +1,17 @@
|
||||
FROM --platform=linux/amd64 node:19-bullseye-slim
|
||||
|
||||
FROM node:22-alpine AS deps
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
RUN yarn install --frozen-lockfile
|
||||
|
||||
RUN yarn install
|
||||
FROM deps AS builder
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN yarn build
|
||||
|
||||
FROM node:22-alpine AS runner
|
||||
WORKDIR /app
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["yarn","start"]
|
||||
CMD ["node", "server.js"]
|
||||
|
||||
32
README.md
32
README.md
@ -1,19 +1,29 @@
|
||||
# Conversion Web App Template
|
||||
# Conversation Web App Template
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Config App
|
||||
Config app in `config/index.ts`.Please config:
|
||||
- APP_ID
|
||||
- API_KEY
|
||||
Create a file named `.env.local` in the current directory and copy the contents from `.env.example`. Setting the following content:
|
||||
```
|
||||
# APP ID: This is the unique identifier for your app. You can find it in the app's detail page URL.
|
||||
# For example, in the URL `https://cloud.dify.ai/app/xxx/workflow`, the value `xxx` is your APP ID.
|
||||
NEXT_PUBLIC_APP_ID=
|
||||
|
||||
More config:
|
||||
# APP API Key: This is the key used to authenticate your app's API requests.
|
||||
# You can generate it on the app's "API Access" page by clicking the "API Key" button in the top-right corner.
|
||||
NEXT_PUBLIC_APP_KEY=
|
||||
|
||||
# APP URL: This is the API's base URL. If you're using the Dify cloud service, set it to: https://api.dify.ai/v1.
|
||||
NEXT_PUBLIC_API_URL=
|
||||
```
|
||||
|
||||
Config more in `config/index.ts` file:
|
||||
```js
|
||||
export const APP_INFO: AppInfo = {
|
||||
"title": 'Chat APP',
|
||||
"description": '',
|
||||
"copyright": '',
|
||||
"privacy_policy": '',
|
||||
"default_language": 'zh-Hans'
|
||||
title: 'Chat APP',
|
||||
description: '',
|
||||
copyright: '',
|
||||
privacy_policy: '',
|
||||
default_language: 'zh-Hans'
|
||||
}
|
||||
|
||||
export const isShowPrompt = true
|
||||
@ -62,7 +72,7 @@ You can check out [the Next.js GitHub repository](https://github.com/vercel/next
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
> ⚠️ If you are using [Vercel Hobby](https://vercel.com/pricing), your message will be trucated due to the limitation of vercel.
|
||||
> ⚠️ If you are using [Vercel Hobby](https://vercel.com/pricing), your message will be truncated due to the limitation of vercel.
|
||||
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type NextRequest } from 'next/server'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { client, getInfo } from '@/app/api/utils/common'
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
@ -6,10 +6,11 @@ export async function POST(request: NextRequest) {
|
||||
const {
|
||||
inputs,
|
||||
query,
|
||||
files,
|
||||
conversation_id: conversationId,
|
||||
response_mode: responseMode,
|
||||
} = body
|
||||
const { user } = getInfo(request)
|
||||
const res = await client.createChatMessage(inputs, query, user, responseMode, conversationId)
|
||||
const res = await client.createChatMessage(inputs, query, user, responseMode, conversationId, files)
|
||||
return new Response(res.data as any)
|
||||
}
|
||||
|
||||
19
app/api/conversations/[conversationId]/name/route.ts
Normal file
19
app/api/conversations/[conversationId]/name/route.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { client, getInfo } from '@/app/api/utils/common'
|
||||
|
||||
export async function POST(request: NextRequest, { params }: {
|
||||
params: Promise<{ conversationId: string }>
|
||||
}) {
|
||||
const body = await request.json()
|
||||
const {
|
||||
auto_generate,
|
||||
name,
|
||||
} = body
|
||||
const { conversationId } = await params
|
||||
const { user } = getInfo(request)
|
||||
|
||||
// auto generate name
|
||||
const { data } = await client.renameConversation(conversationId, name, user, auto_generate)
|
||||
return NextResponse.json(data)
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import { type NextRequest } from 'next/server'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { client, getInfo, setSession } from '@/app/api/utils/common'
|
||||
|
||||
@ -9,7 +9,11 @@ export async function GET(request: NextRequest) {
|
||||
return NextResponse.json(data, {
|
||||
headers: setSession(sessionId),
|
||||
})
|
||||
} catch (error) {
|
||||
return NextResponse.json([]);
|
||||
}
|
||||
catch (error: any) {
|
||||
return NextResponse.json({
|
||||
data: [],
|
||||
error: error.message,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
15
app/api/file-upload/route.ts
Normal file
15
app/api/file-upload/route.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { client, getInfo } from '@/app/api/utils/common'
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const formData = await request.formData()
|
||||
const { user } = getInfo(request)
|
||||
formData.append('user', user)
|
||||
const res = await client.fileUpload(formData)
|
||||
return new Response(res.data.id as any)
|
||||
}
|
||||
catch (e: any) {
|
||||
return new Response(e.message)
|
||||
}
|
||||
}
|
||||
@ -1,15 +1,15 @@
|
||||
import { type NextRequest } from 'next/server'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { client, getInfo } from '@/app/api/utils/common'
|
||||
|
||||
export async function POST(request: NextRequest, { params }: {
|
||||
params: { messageId: string }
|
||||
params: Promise<{ messageId: string }>
|
||||
}) {
|
||||
const body = await request.json()
|
||||
const {
|
||||
rating,
|
||||
} = body
|
||||
const { messageId } = params
|
||||
const { messageId } = await params
|
||||
const { user } = getInfo(request)
|
||||
const { data } = await client.messageFeedback(messageId, rating, user)
|
||||
return NextResponse.json(data)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type NextRequest } from 'next/server'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { client, getInfo, setSession } from '@/app/api/utils/common'
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type NextRequest } from 'next/server'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { client, getInfo, setSession } from '@/app/api/utils/common'
|
||||
|
||||
@ -9,7 +9,8 @@ export async function GET(request: NextRequest) {
|
||||
return NextResponse.json(data as object, {
|
||||
headers: setSession(sessionId),
|
||||
})
|
||||
} catch (error) {
|
||||
return NextResponse.json([]);
|
||||
}
|
||||
catch (error) {
|
||||
return NextResponse.json([])
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { type NextRequest } from 'next/server'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { ChatClient } from 'dify-client'
|
||||
import { v4 } from 'uuid'
|
||||
import { API_KEY, API_URL, APP_ID } from '@/config'
|
||||
import { API_KEY, API_URL, APP_ID, APP_INFO } from '@/config'
|
||||
|
||||
const userPrefix = `user_${APP_ID}:`
|
||||
|
||||
@ -15,6 +15,9 @@ export const getInfo = (request: NextRequest) => {
|
||||
}
|
||||
|
||||
export const setSession = (sessionId: string) => {
|
||||
if (APP_INFO.disable_session_same_site)
|
||||
{ return { 'Set-Cookie': `session_id=${sessionId}; SameSite=None; Secure` } }
|
||||
|
||||
return { 'Set-Cookie': `session_id=${sessionId}` }
|
||||
}
|
||||
|
||||
|
||||
@ -3,26 +3,25 @@ import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
type IAppUnavailableProps = {
|
||||
isUnknwonReason: boolean
|
||||
interface IAppUnavailableProps {
|
||||
isUnknownReason: boolean
|
||||
errMessage?: string
|
||||
}
|
||||
|
||||
const AppUnavailable: FC<IAppUnavailableProps> = ({
|
||||
isUnknwonReason,
|
||||
isUnknownReason,
|
||||
errMessage,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
let message = errMessage
|
||||
if (!errMessage)
|
||||
message = (isUnknwonReason ? t('app.common.appUnkonwError') : t('app.common.appUnavailable')) as string
|
||||
if (!errMessage) { message = (isUnknownReason ? t('app.common.appUnkonwError') : t('app.common.appUnavailable')) as string }
|
||||
|
||||
return (
|
||||
<div className='flex items-center justify-center w-screen h-screen'>
|
||||
<h1 className='mr-5 h-[50px] leading-[50px] pr-5 text-[24px] font-medium'
|
||||
style={{
|
||||
borderRight: '1px solid rgba(0,0,0,.3)',
|
||||
}}>{(errMessage || isUnknwonReason) ? 500 : 404}</h1>
|
||||
}}>{(errMessage || isUnknownReason) ? 500 : 404}</h1>
|
||||
<div className='text-sm'>{message}</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
45
app/components/base/action-button/index.css
Normal file
45
app/components/base/action-button/index.css
Normal file
@ -0,0 +1,45 @@
|
||||
@tailwind components;
|
||||
|
||||
@layer components {
|
||||
.action-btn {
|
||||
@apply inline-flex justify-center items-center cursor-pointer text-text-tertiary hover:text-text-secondary hover:bg-state-base-hover
|
||||
}
|
||||
|
||||
.action-btn-hover {
|
||||
@apply bg-state-base-hover
|
||||
}
|
||||
|
||||
.action-btn-disabled {
|
||||
@apply cursor-not-allowed
|
||||
}
|
||||
|
||||
.action-btn-xl {
|
||||
@apply p-2 w-9 h-9 rounded-lg
|
||||
}
|
||||
|
||||
.action-btn-l {
|
||||
@apply p-1.5 w-8 h-8 rounded-lg
|
||||
}
|
||||
|
||||
/* m is for the regular button */
|
||||
.action-btn-m {
|
||||
@apply p-0.5 w-6 h-6 rounded-lg
|
||||
}
|
||||
|
||||
.action-btn-xs {
|
||||
@apply p-0 w-4 h-4 rounded
|
||||
}
|
||||
|
||||
.action-btn.action-btn-active {
|
||||
@apply text-text-accent bg-state-accent-active hover:bg-state-accent-active-alt
|
||||
}
|
||||
|
||||
.action-btn.action-btn-disabled {
|
||||
@apply text-text-disabled
|
||||
}
|
||||
|
||||
.action-btn.action-btn-destructive {
|
||||
@apply text-text-destructive bg-state-destructive-hover
|
||||
}
|
||||
|
||||
}
|
||||
73
app/components/base/action-button/index.tsx
Normal file
73
app/components/base/action-button/index.tsx
Normal file
@ -0,0 +1,73 @@
|
||||
import type { CSSProperties } from 'react'
|
||||
import React from 'react'
|
||||
import { type VariantProps, cva } from 'class-variance-authority'
|
||||
import classNames from '@/utils/classnames'
|
||||
|
||||
enum ActionButtonState {
|
||||
Destructive = 'destructive',
|
||||
Active = 'active',
|
||||
Disabled = 'disabled',
|
||||
Default = '',
|
||||
Hover = 'hover',
|
||||
}
|
||||
|
||||
const actionButtonVariants = cva(
|
||||
'action-btn',
|
||||
{
|
||||
variants: {
|
||||
size: {
|
||||
xs: 'action-btn-xs',
|
||||
m: 'action-btn-m',
|
||||
l: 'action-btn-l',
|
||||
xl: 'action-btn-xl',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
size: 'm',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
export type ActionButtonProps = {
|
||||
size?: 'xs' | 's' | 'm' | 'l' | 'xl'
|
||||
state?: ActionButtonState
|
||||
styleCss?: CSSProperties
|
||||
} & React.ButtonHTMLAttributes<HTMLButtonElement> & VariantProps<typeof actionButtonVariants>
|
||||
|
||||
function getActionButtonState(state: ActionButtonState) {
|
||||
switch (state) {
|
||||
case ActionButtonState.Destructive:
|
||||
return 'action-btn-destructive'
|
||||
case ActionButtonState.Active:
|
||||
return 'action-btn-active'
|
||||
case ActionButtonState.Disabled:
|
||||
return 'action-btn-disabled'
|
||||
case ActionButtonState.Hover:
|
||||
return 'action-btn-hover'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
const ActionButton = React.forwardRef<HTMLButtonElement, ActionButtonProps>(
|
||||
({ className, size, state = ActionButtonState.Default, styleCss, children, ...props }, ref) => {
|
||||
return (
|
||||
<button
|
||||
type='button'
|
||||
className={classNames(
|
||||
actionButtonVariants({ className, size }),
|
||||
getActionButtonState(state),
|
||||
)}
|
||||
ref={ref}
|
||||
style={styleCss}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
},
|
||||
)
|
||||
ActionButton.displayName = 'ActionButton'
|
||||
|
||||
export default ActionButton
|
||||
export { ActionButton, ActionButtonState, actionButtonVariants }
|
||||
@ -2,8 +2,8 @@ import type { FC } from 'react'
|
||||
import classNames from 'classnames'
|
||||
import style from './style.module.css'
|
||||
|
||||
export type AppIconProps = {
|
||||
size?: 'tiny' | 'small' | 'medium' | 'large'
|
||||
export interface AppIconProps {
|
||||
size?: 'xs' | 'tiny' | 'small' | 'medium' | 'large'
|
||||
rounded?: boolean
|
||||
icon?: string
|
||||
background?: string
|
||||
|
||||
@ -1,15 +1,23 @@
|
||||
.appIcon {
|
||||
@apply flex items-center justify-center relative w-9 h-9 text-lg bg-teal-100 rounded-lg grow-0 shrink-0;
|
||||
}
|
||||
|
||||
.appIcon.large {
|
||||
@apply w-10 h-10;
|
||||
}
|
||||
|
||||
.appIcon.small {
|
||||
@apply w-8 h-8;
|
||||
}
|
||||
|
||||
.appIcon.xs {
|
||||
@apply w-3 h-3 text-base;
|
||||
}
|
||||
|
||||
.appIcon.tiny {
|
||||
@apply w-6 h-6 text-base;
|
||||
}
|
||||
|
||||
.appIcon.rounded {
|
||||
@apply rounded-full;
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import { forwardRef, useEffect, useRef } from 'react'
|
||||
import cn from 'classnames'
|
||||
|
||||
type IProps = {
|
||||
interface IProps {
|
||||
placeholder?: string
|
||||
value: string
|
||||
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void
|
||||
@ -36,19 +36,16 @@ const AutoHeightTextarea = forwardRef(
|
||||
let hasFocus = false
|
||||
const runId = setInterval(() => {
|
||||
hasFocus = doFocus()
|
||||
if (hasFocus)
|
||||
clearInterval(runId)
|
||||
if (hasFocus) { clearInterval(runId) }
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (autoFocus)
|
||||
focus()
|
||||
if (autoFocus) { focus() }
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
if (controlFocus)
|
||||
focus()
|
||||
if (controlFocus) { focus() }
|
||||
}, [controlFocus])
|
||||
|
||||
return (
|
||||
|
||||
@ -2,7 +2,7 @@ import type { FC, MouseEventHandler } from 'react'
|
||||
import React from 'react'
|
||||
import Spinner from '@/app/components/base/spinner'
|
||||
|
||||
export type IButtonProps = {
|
||||
export interface IButtonProps {
|
||||
type?: string
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
@ -21,6 +21,9 @@ const Button: FC<IButtonProps> = ({
|
||||
}) => {
|
||||
let style = 'cursor-pointer'
|
||||
switch (type) {
|
||||
case 'link':
|
||||
style = disabled ? 'border-solid border border-gray-200 bg-gray-200 cursor-not-allowed text-gray-800' : 'border-solid border border-gray-200 cursor-pointer text-blue-600 bg-white hover:shadow-sm hover:border-gray-300'
|
||||
break
|
||||
case 'primary':
|
||||
style = (disabled || loading) ? 'bg-primary-600/75 cursor-not-allowed text-white' : 'bg-primary-600 hover:bg-primary-600/75 hover:shadow-md cursor-pointer text-white hover:shadow-sm'
|
||||
break
|
||||
|
||||
16
app/components/base/file-uploader-in-attachment/constants.ts
Normal file
16
app/components/base/file-uploader-in-attachment/constants.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { SupportUploadFileTypes } from './types'
|
||||
// fallback for file size limit of dify_config
|
||||
export const IMG_SIZE_LIMIT = 10 * 1024 * 1024
|
||||
export const FILE_SIZE_LIMIT = 15 * 1024 * 1024
|
||||
export const AUDIO_SIZE_LIMIT = 50 * 1024 * 1024
|
||||
export const VIDEO_SIZE_LIMIT = 100 * 1024 * 1024
|
||||
export const MAX_FILE_UPLOAD_LIMIT = 10
|
||||
|
||||
export const FILE_URL_REGEX = /^(https?|ftp):\/\//
|
||||
|
||||
export const FILE_EXTS: Record<string, string[]> = {
|
||||
[SupportUploadFileTypes.image]: ['JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'],
|
||||
[SupportUploadFileTypes.document]: ['TXT', 'MD', 'MDX', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOC', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'],
|
||||
[SupportUploadFileTypes.audio]: ['MP3', 'M4A', 'WAV', 'AMR', 'MPGA'],
|
||||
[SupportUploadFileTypes.video]: ['MP4', 'MOV', 'MPEG', 'WEBM'],
|
||||
}
|
||||
@ -0,0 +1,129 @@
|
||||
import {
|
||||
memo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiUploadCloud2Line } from '@remixicon/react'
|
||||
import FileInput from '../file-input'
|
||||
import { useFile } from '../hooks'
|
||||
import { useStore } from '../store'
|
||||
import { FILE_URL_REGEX } from '../constants'
|
||||
import type { FileUpload } from '../types'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import Button from '@/app/components/base/button'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
interface FileFromLinkOrLocalProps {
|
||||
showFromLink?: boolean
|
||||
showFromLocal?: boolean
|
||||
trigger: (open: boolean) => React.ReactNode
|
||||
fileConfig: FileUpload
|
||||
}
|
||||
const FileFromLinkOrLocal = ({
|
||||
showFromLink = true,
|
||||
showFromLocal = true,
|
||||
trigger,
|
||||
fileConfig,
|
||||
}: FileFromLinkOrLocalProps) => {
|
||||
const { t } = useTranslation()
|
||||
const files = useStore(s => s.files)
|
||||
const [open, setOpen] = useState(false)
|
||||
const [url, setUrl] = useState('')
|
||||
const [showError, setShowError] = useState(false)
|
||||
const { handleLoadFileFromLink } = useFile(fileConfig)
|
||||
const disabled = !!fileConfig.number_limits && files.length >= fileConfig.number_limits
|
||||
|
||||
const handleSaveUrl = () => {
|
||||
if (!url) { return }
|
||||
|
||||
if (!FILE_URL_REGEX.test(url)) {
|
||||
setShowError(true)
|
||||
return
|
||||
}
|
||||
handleLoadFileFromLink(url)
|
||||
setUrl('')
|
||||
}
|
||||
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
placement='top'
|
||||
offset={4}
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
>
|
||||
<PortalToFollowElemTrigger onClick={() => setOpen(v => !v)} asChild>
|
||||
{trigger(open)}
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-[1001]'>
|
||||
<div className='w-[280px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-3 shadow-lg'>
|
||||
{
|
||||
showFromLink && (
|
||||
<>
|
||||
<div className={cn(
|
||||
'flex h-8 items-center rounded-lg border border-components-input-border-active bg-components-input-bg-active p-1 shadow-xs',
|
||||
showError && 'border-components-input-border-destructive',
|
||||
)}>
|
||||
<input
|
||||
className='system-sm-regular mr-0.5 block grow appearance-none bg-transparent px-1 outline-none'
|
||||
placeholder={t('common.fileUploader.pasteFileLinkInputPlaceholder') || ''}
|
||||
value={url}
|
||||
onChange={(e) => {
|
||||
setShowError(false)
|
||||
setUrl(e.target.value.trim())
|
||||
}}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<Button
|
||||
className='shrink-0'
|
||||
// size='small'
|
||||
// variant='primary'
|
||||
type='primary'
|
||||
disabled={!url || disabled}
|
||||
onClick={handleSaveUrl}
|
||||
>
|
||||
{t('common.operation.ok')}
|
||||
</Button>
|
||||
</div>
|
||||
{
|
||||
showError && (
|
||||
<div className='body-xs-regular mt-0.5 text-text-destructive'>
|
||||
{t('common.fileUploader.pasteFileLinkInvalid')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
{
|
||||
showFromLink && showFromLocal && (
|
||||
<div className='system-2xs-medium-uppercase flex h-7 items-center p-2 text-text-quaternary'>
|
||||
<div className='mr-2 h-[1px] w-[93px] bg-gradient-to-l from-[rgba(16,24,40,0.08)]' />
|
||||
OR
|
||||
<div className='ml-2 h-[1px] w-[93px] bg-gradient-to-r from-[rgba(16,24,40,0.08)]' />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
showFromLocal && (
|
||||
<Button
|
||||
className='relative w-full'
|
||||
// variant='secondary-accent'
|
||||
disabled={disabled}
|
||||
>
|
||||
<RiUploadCloud2Line className='mr-1 h-4 w-4' />
|
||||
{t('common.fileUploader.uploadFromComputer')}
|
||||
<FileInput fileConfig={fileConfig} />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(FileFromLinkOrLocal)
|
||||
@ -0,0 +1,32 @@
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
interface FileImageRenderProps {
|
||||
imageUrl: string
|
||||
className?: string
|
||||
alt?: string
|
||||
onLoad?: () => void
|
||||
onError?: () => void
|
||||
showDownloadAction?: boolean
|
||||
}
|
||||
const FileImageRender = ({
|
||||
imageUrl,
|
||||
className,
|
||||
alt,
|
||||
onLoad,
|
||||
onError,
|
||||
showDownloadAction,
|
||||
}: FileImageRenderProps) => {
|
||||
return (
|
||||
<div className={cn('border-[2px] border-effects-image-frame shadow-xs', className)}>
|
||||
<img
|
||||
className={cn('h-full w-full object-cover', showDownloadAction && 'cursor-pointer')}
|
||||
alt={alt || 'Preview'}
|
||||
onLoad={onLoad}
|
||||
onError={onError}
|
||||
src={imageUrl}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FileImageRender
|
||||
@ -0,0 +1,48 @@
|
||||
import { useFile } from './hooks'
|
||||
import { useStore } from './store'
|
||||
import type { FileUpload } from './types'
|
||||
import { FILE_EXTS } from './constants'
|
||||
import { SupportUploadFileTypes } from './types'
|
||||
|
||||
interface FileInputProps {
|
||||
fileConfig: FileUpload
|
||||
}
|
||||
const FileInput = ({
|
||||
fileConfig,
|
||||
}: FileInputProps) => {
|
||||
const files = useStore(s => s.files)
|
||||
const { handleLocalFileUpload } = useFile(fileConfig)
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const targetFiles = e.target.files
|
||||
|
||||
if (targetFiles) {
|
||||
if (fileConfig.number_limits) {
|
||||
for (let i = 0; i < targetFiles.length; i++) {
|
||||
if (i + 1 + files.length <= fileConfig.number_limits) { handleLocalFileUpload(targetFiles[i]) }
|
||||
}
|
||||
}
|
||||
else {
|
||||
handleLocalFileUpload(targetFiles[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const allowedFileTypes = fileConfig.allowed_file_types
|
||||
const isCustom = allowedFileTypes?.includes(SupportUploadFileTypes.custom)
|
||||
const exts = isCustom ? (fileConfig.allowed_file_extensions || []) : (allowedFileTypes?.map(type => FILE_EXTS[type]) || []).flat().map(item => `.${item}`)
|
||||
const accept = exts.join(',')
|
||||
|
||||
return (
|
||||
<input
|
||||
className='absolute inset-0 block w-full cursor-pointer text-[0] opacity-0 disabled:cursor-not-allowed'
|
||||
onClick={e => ((e.target as HTMLInputElement).value = '')}
|
||||
type='file'
|
||||
onChange={handleChange}
|
||||
accept={accept}
|
||||
disabled={!!(fileConfig.number_limits && files.length >= fileConfig?.number_limits)}
|
||||
multiple={!!fileConfig.number_limits && fileConfig.number_limits > 1}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default FileInput
|
||||
154
app/components/base/file-uploader-in-attachment/file-item.tsx
Normal file
154
app/components/base/file-uploader-in-attachment/file-item.tsx
Normal file
@ -0,0 +1,154 @@
|
||||
import {
|
||||
memo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import {
|
||||
RiDeleteBinLine,
|
||||
RiDownloadLine,
|
||||
RiEyeLine,
|
||||
} from '@remixicon/react'
|
||||
import FileTypeIcon from './file-type-icon'
|
||||
import FileImageRender from './file-image-render'
|
||||
import type { FileEntity } from './types'
|
||||
import {
|
||||
downloadFile,
|
||||
fileIsUploaded,
|
||||
getFileAppearanceType,
|
||||
getFileExtension,
|
||||
} from './utils'
|
||||
import { SupportUploadFileTypes } from './types'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import ProgressCircle from '@/app/components/base/progress-bar/progress-circle'
|
||||
import { formatFileSize } from '@/utils/format'
|
||||
import cn from '@/utils/classnames'
|
||||
import ReplayLine from '@/app/components/base/icons/other/ReplayLine'
|
||||
import ImagePreview from '@/app/components/base/image-uploader/image-preview'
|
||||
|
||||
interface FileInAttachmentItemProps {
|
||||
file: FileEntity
|
||||
showDeleteAction?: boolean
|
||||
showDownloadAction?: boolean
|
||||
onRemove?: (fileId: string) => void
|
||||
onReUpload?: (fileId: string) => void
|
||||
canPreview?: boolean
|
||||
}
|
||||
const FileInAttachmentItem = ({
|
||||
file,
|
||||
showDeleteAction,
|
||||
showDownloadAction = true,
|
||||
onRemove,
|
||||
onReUpload,
|
||||
canPreview,
|
||||
}: FileInAttachmentItemProps) => {
|
||||
const { id, name, type, progress, supportFileType, base64Url, url, isRemote } = file
|
||||
const ext = getFileExtension(name, type, isRemote)
|
||||
const isImageFile = supportFileType === SupportUploadFileTypes.image
|
||||
const [imagePreviewUrl, setImagePreviewUrl] = useState('')
|
||||
return (
|
||||
<>
|
||||
<div className={cn(
|
||||
'flex h-12 items-center rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg pr-3 shadow-xs',
|
||||
progress === -1 && 'border-state-destructive-border bg-state-destructive-hover',
|
||||
)}>
|
||||
<div className='flex h-12 w-12 items-center justify-center'>
|
||||
{
|
||||
isImageFile && (
|
||||
<FileImageRender
|
||||
className='h-8 w-8'
|
||||
imageUrl={base64Url || url || ''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
!isImageFile && (
|
||||
<FileTypeIcon
|
||||
type={getFileAppearanceType(name, type)}
|
||||
size='lg'
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div className='mr-1 w-0 grow'>
|
||||
<div
|
||||
className='system-xs-medium mb-0.5 flex items-center truncate text-text-secondary'
|
||||
title={file.name}
|
||||
>
|
||||
<div className='truncate'>{name}</div>
|
||||
</div>
|
||||
<div className='system-2xs-medium-uppercase flex items-center text-text-tertiary'>
|
||||
{
|
||||
ext && (
|
||||
<span>{ext.toLowerCase()}</span>
|
||||
)
|
||||
}
|
||||
{
|
||||
ext && (
|
||||
<span className='system-2xs-medium mx-1'>•</span>
|
||||
)
|
||||
}
|
||||
{
|
||||
!!file.size && (
|
||||
<span>{formatFileSize(file.size)}</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex shrink-0 items-center'>
|
||||
{
|
||||
progress >= 0 && !fileIsUploaded(file) && (
|
||||
<ProgressCircle
|
||||
className='mr-2.5'
|
||||
percentage={progress}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
progress === -1 && (
|
||||
<ActionButton
|
||||
className='mr-1'
|
||||
onClick={() => onReUpload?.(id)}
|
||||
>
|
||||
<ReplayLine className='h-4 w-4 text-text-tertiary' />
|
||||
</ActionButton>
|
||||
)
|
||||
}
|
||||
{
|
||||
showDeleteAction && (
|
||||
<ActionButton onClick={() => onRemove?.(id)}>
|
||||
<RiDeleteBinLine className='h-4 w-4' />
|
||||
</ActionButton>
|
||||
)
|
||||
}
|
||||
{
|
||||
canPreview && isImageFile && (
|
||||
<ActionButton className='mr-1' onClick={() => setImagePreviewUrl(url || '')}>
|
||||
<RiEyeLine className='h-4 w-4' />
|
||||
</ActionButton>
|
||||
)
|
||||
}
|
||||
{
|
||||
showDownloadAction && (
|
||||
<ActionButton onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
downloadFile(url || base64Url || '', name)
|
||||
}}>
|
||||
<RiDownloadLine className='h-4 w-4' />
|
||||
</ActionButton>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
imagePreviewUrl && canPreview && (
|
||||
<ImagePreview
|
||||
title={name}
|
||||
url={imagePreviewUrl}
|
||||
onCancel={() => setImagePreviewUrl('')}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(FileInAttachmentItem)
|
||||
@ -0,0 +1,91 @@
|
||||
import { memo } from 'react'
|
||||
import {
|
||||
RiFile3Fill,
|
||||
RiFileCodeFill,
|
||||
RiFileExcelFill,
|
||||
RiFileGifFill,
|
||||
RiFileImageFill,
|
||||
RiFileMusicFill,
|
||||
RiFilePdf2Fill,
|
||||
RiFilePpt2Fill,
|
||||
RiFileTextFill,
|
||||
RiFileVideoFill,
|
||||
RiFileWordFill,
|
||||
RiMarkdownFill,
|
||||
} from '@remixicon/react'
|
||||
import { FileAppearanceTypeEnum } from './types'
|
||||
import type { FileAppearanceType } from './types'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
const FILE_TYPE_ICON_MAP = {
|
||||
[FileAppearanceTypeEnum.pdf]: {
|
||||
component: RiFilePdf2Fill,
|
||||
color: 'text-[#EA3434]',
|
||||
},
|
||||
[FileAppearanceTypeEnum.image]: {
|
||||
component: RiFileImageFill,
|
||||
color: 'text-[#00B2EA]',
|
||||
},
|
||||
[FileAppearanceTypeEnum.video]: {
|
||||
component: RiFileVideoFill,
|
||||
color: 'text-[#844FDA]',
|
||||
},
|
||||
[FileAppearanceTypeEnum.audio]: {
|
||||
component: RiFileMusicFill,
|
||||
color: 'text-[#FF3093]',
|
||||
},
|
||||
[FileAppearanceTypeEnum.document]: {
|
||||
component: RiFileTextFill,
|
||||
color: 'text-[#6F8BB5]',
|
||||
},
|
||||
[FileAppearanceTypeEnum.code]: {
|
||||
component: RiFileCodeFill,
|
||||
color: 'text-[#BCC0D1]',
|
||||
},
|
||||
[FileAppearanceTypeEnum.markdown]: {
|
||||
component: RiMarkdownFill,
|
||||
color: 'text-[#309BEC]',
|
||||
},
|
||||
[FileAppearanceTypeEnum.custom]: {
|
||||
component: RiFile3Fill,
|
||||
color: 'text-[#BCC0D1]',
|
||||
},
|
||||
[FileAppearanceTypeEnum.excel]: {
|
||||
component: RiFileExcelFill,
|
||||
color: 'text-[#01AC49]',
|
||||
},
|
||||
[FileAppearanceTypeEnum.word]: {
|
||||
component: RiFileWordFill,
|
||||
color: 'text-[#2684FF]',
|
||||
},
|
||||
[FileAppearanceTypeEnum.ppt]: {
|
||||
component: RiFilePpt2Fill,
|
||||
color: 'text-[#FF650F]',
|
||||
},
|
||||
[FileAppearanceTypeEnum.gif]: {
|
||||
component: RiFileGifFill,
|
||||
color: 'text-[#00B2EA]',
|
||||
},
|
||||
}
|
||||
interface FileTypeIconProps {
|
||||
type: FileAppearanceType
|
||||
size?: 'sm' | 'lg' | 'md'
|
||||
className?: string
|
||||
}
|
||||
const SizeMap = {
|
||||
sm: 'w-4 h-4',
|
||||
md: 'w-5 h-5',
|
||||
lg: 'w-6 h-6',
|
||||
}
|
||||
const FileTypeIcon = ({
|
||||
type,
|
||||
size = 'sm',
|
||||
className,
|
||||
}: FileTypeIconProps) => {
|
||||
const Icon = FILE_TYPE_ICON_MAP[type]?.component || FILE_TYPE_ICON_MAP[FileAppearanceTypeEnum.document].component
|
||||
const color = FILE_TYPE_ICON_MAP[type]?.color || FILE_TYPE_ICON_MAP[FileAppearanceTypeEnum.document].color
|
||||
|
||||
return <Icon className={cn('shrink-0', SizeMap[size], color, className)} />
|
||||
}
|
||||
|
||||
export default memo(FileTypeIcon)
|
||||
361
app/components/base/file-uploader-in-attachment/hooks.ts
Normal file
361
app/components/base/file-uploader-in-attachment/hooks.ts
Normal file
@ -0,0 +1,361 @@
|
||||
import type { ClipboardEvent } from 'react'
|
||||
import {
|
||||
useCallback,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useParams } from 'next/navigation'
|
||||
import produce from 'immer'
|
||||
import { v4 as uuid4 } from 'uuid'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { noop } from 'lodash-es'
|
||||
import type { FileEntity, FileUpload, FileUploadConfigResponse } from './types'
|
||||
import { useFileStore } from './store'
|
||||
import {
|
||||
fileUpload,
|
||||
getSupportFileType,
|
||||
isAllowedFileExtension,
|
||||
} from './utils'
|
||||
import {
|
||||
AUDIO_SIZE_LIMIT,
|
||||
FILE_SIZE_LIMIT,
|
||||
IMG_SIZE_LIMIT,
|
||||
MAX_FILE_UPLOAD_LIMIT,
|
||||
VIDEO_SIZE_LIMIT,
|
||||
} from './constants'
|
||||
import { SupportUploadFileTypes } from './types'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
import { formatFileSize } from '@/utils/format'
|
||||
|
||||
const uploadRemoteFileInfo = () => {
|
||||
console.log('TODO')
|
||||
}
|
||||
|
||||
export const useFileSizeLimit = (fileUploadConfig?: FileUploadConfigResponse) => {
|
||||
const imgSizeLimit = Number(fileUploadConfig?.image_file_size_limit) * 1024 * 1024 || IMG_SIZE_LIMIT
|
||||
const docSizeLimit = Number(fileUploadConfig?.file_size_limit) * 1024 * 1024 || FILE_SIZE_LIMIT
|
||||
const audioSizeLimit = Number(fileUploadConfig?.audio_file_size_limit) * 1024 * 1024 || AUDIO_SIZE_LIMIT
|
||||
const videoSizeLimit = Number(fileUploadConfig?.video_file_size_limit) * 1024 * 1024 || VIDEO_SIZE_LIMIT
|
||||
const maxFileUploadLimit = Number(fileUploadConfig?.workflow_file_upload_limit) || MAX_FILE_UPLOAD_LIMIT
|
||||
|
||||
return {
|
||||
imgSizeLimit,
|
||||
docSizeLimit,
|
||||
audioSizeLimit,
|
||||
videoSizeLimit,
|
||||
maxFileUploadLimit,
|
||||
}
|
||||
}
|
||||
|
||||
export const useFile = (fileConfig: FileUpload) => {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useToastContext()
|
||||
const fileStore = useFileStore()
|
||||
const params = useParams()
|
||||
const { imgSizeLimit, docSizeLimit, audioSizeLimit, videoSizeLimit } = useFileSizeLimit(fileConfig.fileUploadConfig)
|
||||
|
||||
const checkSizeLimit = useCallback((fileType: string, fileSize: number) => {
|
||||
switch (fileType) {
|
||||
case SupportUploadFileTypes.image: {
|
||||
if (fileSize > imgSizeLimit) {
|
||||
notify({
|
||||
type: 'error',
|
||||
message: t('common.fileUploader.uploadFromComputerLimit', {
|
||||
type: SupportUploadFileTypes.image,
|
||||
size: formatFileSize(imgSizeLimit),
|
||||
}),
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
case SupportUploadFileTypes.document: {
|
||||
if (fileSize > docSizeLimit) {
|
||||
notify({
|
||||
type: 'error',
|
||||
message: t('common.fileUploader.uploadFromComputerLimit', {
|
||||
type: SupportUploadFileTypes.document,
|
||||
size: formatFileSize(docSizeLimit),
|
||||
}),
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
case SupportUploadFileTypes.audio: {
|
||||
if (fileSize > audioSizeLimit) {
|
||||
notify({
|
||||
type: 'error',
|
||||
message: t('common.fileUploader.uploadFromComputerLimit', {
|
||||
type: SupportUploadFileTypes.audio,
|
||||
size: formatFileSize(audioSizeLimit),
|
||||
}),
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
case SupportUploadFileTypes.video: {
|
||||
if (fileSize > videoSizeLimit) {
|
||||
notify({
|
||||
type: 'error',
|
||||
message: t('common.fileUploader.uploadFromComputerLimit', {
|
||||
type: SupportUploadFileTypes.video,
|
||||
size: formatFileSize(videoSizeLimit),
|
||||
}),
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
case SupportUploadFileTypes.custom: {
|
||||
if (fileSize > docSizeLimit) {
|
||||
notify({
|
||||
type: 'error',
|
||||
message: t('common.fileUploader.uploadFromComputerLimit', {
|
||||
type: SupportUploadFileTypes.document,
|
||||
size: formatFileSize(docSizeLimit),
|
||||
}),
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
default: {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}, [audioSizeLimit, docSizeLimit, imgSizeLimit, notify, t, videoSizeLimit])
|
||||
|
||||
const handleAddFile = useCallback((newFile: FileEntity) => {
|
||||
const {
|
||||
files,
|
||||
setFiles,
|
||||
} = fileStore.getState()
|
||||
|
||||
const newFiles = produce(files, (draft) => {
|
||||
draft.push(newFile)
|
||||
})
|
||||
setFiles(newFiles)
|
||||
}, [fileStore])
|
||||
|
||||
const handleUpdateFile = useCallback((newFile: FileEntity) => {
|
||||
const {
|
||||
files,
|
||||
setFiles,
|
||||
} = fileStore.getState()
|
||||
|
||||
const newFiles = produce(files, (draft) => {
|
||||
const index = draft.findIndex(file => file.id === newFile.id)
|
||||
|
||||
if (index > -1) { draft[index] = newFile }
|
||||
})
|
||||
setFiles(newFiles)
|
||||
}, [fileStore])
|
||||
|
||||
const handleRemoveFile = useCallback((fileId: string) => {
|
||||
const {
|
||||
files,
|
||||
setFiles,
|
||||
} = fileStore.getState()
|
||||
|
||||
const newFiles = files.filter(file => file.id !== fileId)
|
||||
setFiles(newFiles)
|
||||
}, [fileStore])
|
||||
|
||||
const handleReUploadFile = useCallback((fileId: string) => {
|
||||
const {
|
||||
files,
|
||||
setFiles,
|
||||
} = fileStore.getState()
|
||||
const index = files.findIndex(file => file.id === fileId)
|
||||
|
||||
if (index > -1) {
|
||||
const uploadingFile = files[index]
|
||||
const newFiles = produce(files, (draft) => {
|
||||
draft[index].progress = 0
|
||||
})
|
||||
setFiles(newFiles)
|
||||
fileUpload({
|
||||
file: uploadingFile.originalFile!,
|
||||
onProgressCallback: (progress) => {
|
||||
handleUpdateFile({ ...uploadingFile, progress })
|
||||
},
|
||||
onSuccessCallback: (res) => {
|
||||
handleUpdateFile({ ...uploadingFile, uploadedId: res.id, progress: 100 })
|
||||
},
|
||||
onErrorCallback: () => {
|
||||
notify({ type: 'error', message: t('common.fileUploader.uploadFromComputerUploadError') })
|
||||
handleUpdateFile({ ...uploadingFile, progress: -1 })
|
||||
},
|
||||
})
|
||||
}
|
||||
}, [fileStore, notify, t, handleUpdateFile])
|
||||
|
||||
const startProgressTimer = useCallback((fileId: string) => {
|
||||
const timer = setInterval(() => {
|
||||
const files = fileStore.getState().files
|
||||
const file = files.find(file => file.id === fileId)
|
||||
|
||||
if (file && file.progress < 80 && file.progress >= 0) { handleUpdateFile({ ...file, progress: file.progress + 20 }) }
|
||||
else { clearTimeout(timer) }
|
||||
}, 200)
|
||||
}, [fileStore, handleUpdateFile])
|
||||
const handleLoadFileFromLink = useCallback((url: string) => {
|
||||
const allowedFileTypes = fileConfig.allowed_file_types
|
||||
|
||||
const uploadingFile = {
|
||||
id: uuid4(),
|
||||
name: url,
|
||||
type: '',
|
||||
size: 0,
|
||||
progress: 0,
|
||||
transferMethod: TransferMethod.remote_url,
|
||||
supportFileType: '',
|
||||
url,
|
||||
isRemote: true,
|
||||
}
|
||||
handleAddFile(uploadingFile)
|
||||
startProgressTimer(uploadingFile.id)
|
||||
|
||||
uploadRemoteFileInfo(url, !!params.token).then((res) => {
|
||||
const newFile = {
|
||||
...uploadingFile,
|
||||
type: res.mime_type,
|
||||
size: res.size,
|
||||
progress: 100,
|
||||
supportFileType: getSupportFileType(res.name, res.mime_type, allowedFileTypes?.includes(SupportUploadFileTypes.custom)),
|
||||
uploadedId: res.id,
|
||||
url: res.url,
|
||||
}
|
||||
if (!isAllowedFileExtension(res.name, res.mime_type, fileConfig.allowed_file_types || [], fileConfig.allowed_file_extensions || [])) {
|
||||
notify({ type: 'error', message: t('common.fileUploader.fileExtensionNotSupport') })
|
||||
handleRemoveFile(uploadingFile.id)
|
||||
}
|
||||
if (!checkSizeLimit(newFile.supportFileType, newFile.size)) { handleRemoveFile(uploadingFile.id) }
|
||||
else { handleUpdateFile(newFile) }
|
||||
}).catch(() => {
|
||||
notify({ type: 'error', message: t('common.fileUploader.pasteFileLinkInvalid') })
|
||||
handleRemoveFile(uploadingFile.id)
|
||||
})
|
||||
}, [checkSizeLimit, handleAddFile, handleUpdateFile, notify, t, handleRemoveFile, fileConfig?.allowed_file_types, fileConfig.allowed_file_extensions, startProgressTimer, params.token])
|
||||
|
||||
const handleLoadFileFromLinkSuccess = useCallback(noop, [])
|
||||
|
||||
const handleLoadFileFromLinkError = useCallback(noop, [])
|
||||
|
||||
const handleClearFiles = useCallback(() => {
|
||||
const {
|
||||
setFiles,
|
||||
} = fileStore.getState()
|
||||
setFiles([])
|
||||
}, [fileStore])
|
||||
|
||||
const handleLocalFileUpload = useCallback((file: File) => {
|
||||
if (!isAllowedFileExtension(file.name, file.type, fileConfig.allowed_file_types || [], fileConfig.allowed_file_extensions || [])) {
|
||||
notify({ type: 'error', message: t('common.fileUploader.fileExtensionNotSupport') })
|
||||
return
|
||||
}
|
||||
const allowedFileTypes = fileConfig.allowed_file_types
|
||||
const fileType = getSupportFileType(file.name, file.type, allowedFileTypes?.includes(SupportUploadFileTypes.custom))
|
||||
if (!checkSizeLimit(fileType, file.size)) { return }
|
||||
|
||||
const reader = new FileReader()
|
||||
const isImage = file.type.startsWith('image')
|
||||
|
||||
reader.addEventListener(
|
||||
'load',
|
||||
() => {
|
||||
const uploadingFile = {
|
||||
id: uuid4(),
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
progress: 0,
|
||||
transferMethod: TransferMethod.local_file,
|
||||
supportFileType: getSupportFileType(file.name, file.type, allowedFileTypes?.includes(SupportUploadFileTypes.custom)),
|
||||
originalFile: file,
|
||||
base64Url: isImage ? reader.result as string : '',
|
||||
}
|
||||
handleAddFile(uploadingFile)
|
||||
fileUpload({
|
||||
file: uploadingFile.originalFile,
|
||||
onProgressCallback: (progress) => {
|
||||
handleUpdateFile({ ...uploadingFile, progress })
|
||||
},
|
||||
onSuccessCallback: (res) => {
|
||||
handleUpdateFile({ ...uploadingFile, uploadedId: res.id, progress: 100 })
|
||||
},
|
||||
onErrorCallback: () => {
|
||||
notify({ type: 'error', message: t('common.fileUploader.uploadFromComputerUploadError') })
|
||||
handleUpdateFile({ ...uploadingFile, progress: -1 })
|
||||
},
|
||||
})
|
||||
},
|
||||
false,
|
||||
)
|
||||
reader.addEventListener(
|
||||
'error',
|
||||
() => {
|
||||
notify({ type: 'error', message: t('common.fileUploader.uploadFromComputerReadError') })
|
||||
},
|
||||
false,
|
||||
)
|
||||
reader.readAsDataURL(file)
|
||||
}, [checkSizeLimit, notify, t, handleAddFile, handleUpdateFile, params.token, fileConfig?.allowed_file_types, fileConfig?.allowed_file_extensions])
|
||||
|
||||
const handleClipboardPasteFile = useCallback((e: ClipboardEvent<HTMLTextAreaElement>) => {
|
||||
const file = e.clipboardData?.files[0]
|
||||
const text = e.clipboardData?.getData('text/plain')
|
||||
if (file && !text) {
|
||||
e.preventDefault()
|
||||
handleLocalFileUpload(file)
|
||||
}
|
||||
}, [handleLocalFileUpload])
|
||||
|
||||
const [isDragActive, setIsDragActive] = useState(false)
|
||||
const handleDragFileEnter = useCallback((e: React.DragEvent<HTMLElement>) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setIsDragActive(true)
|
||||
}, [])
|
||||
|
||||
const handleDragFileOver = useCallback((e: React.DragEvent<HTMLElement>) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}, [])
|
||||
|
||||
const handleDragFileLeave = useCallback((e: React.DragEvent<HTMLElement>) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setIsDragActive(false)
|
||||
}, [])
|
||||
|
||||
const handleDropFile = useCallback((e: React.DragEvent<HTMLElement>) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setIsDragActive(false)
|
||||
|
||||
const file = e.dataTransfer.files[0]
|
||||
|
||||
if (file) { handleLocalFileUpload(file) }
|
||||
}, [handleLocalFileUpload])
|
||||
|
||||
return {
|
||||
handleAddFile,
|
||||
handleUpdateFile,
|
||||
handleRemoveFile,
|
||||
handleReUploadFile,
|
||||
handleLoadFileFromLink,
|
||||
handleLoadFileFromLinkSuccess,
|
||||
handleLoadFileFromLinkError,
|
||||
handleClearFiles,
|
||||
handleLocalFileUpload,
|
||||
handleClipboardPasteFile,
|
||||
isDragActive,
|
||||
handleDragFileEnter,
|
||||
handleDragFileOver,
|
||||
handleDragFileLeave,
|
||||
handleDropFile,
|
||||
}
|
||||
}
|
||||
131
app/components/base/file-uploader-in-attachment/index.tsx
Normal file
131
app/components/base/file-uploader-in-attachment/index.tsx
Normal file
@ -0,0 +1,131 @@
|
||||
import {
|
||||
useCallback,
|
||||
} from 'react'
|
||||
import {
|
||||
RiLink,
|
||||
RiUploadCloud2Line,
|
||||
} from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useFile } from './hooks'
|
||||
import type { FileEntity, FileUpload } from './types'
|
||||
import FileFromLinkOrLocal from './file-from-link-or-local'
|
||||
import {
|
||||
FileContextProvider,
|
||||
useStore,
|
||||
} from './store'
|
||||
import FileInput from './file-input'
|
||||
import FileItem from './file-item'
|
||||
import Button from '@/app/components/base/button'
|
||||
import cn from '@/utils/classnames'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
|
||||
interface Option {
|
||||
value: string
|
||||
label: string
|
||||
icon: JSX.Element
|
||||
}
|
||||
interface FileUploaderInAttachmentProps {
|
||||
fileConfig: FileUpload
|
||||
}
|
||||
const FileUploaderInAttachment = ({
|
||||
fileConfig,
|
||||
}: FileUploaderInAttachmentProps) => {
|
||||
const { t } = useTranslation()
|
||||
const files = useStore(s => s.files)
|
||||
const {
|
||||
handleRemoveFile,
|
||||
handleReUploadFile,
|
||||
} = useFile(fileConfig)
|
||||
const options = [
|
||||
{
|
||||
value: TransferMethod.local_file,
|
||||
label: t('common.fileUploader.uploadFromComputer'),
|
||||
icon: <RiUploadCloud2Line className='h-4 w-4' />,
|
||||
},
|
||||
{
|
||||
value: TransferMethod.remote_url,
|
||||
label: t('common.fileUploader.pasteFileLink'),
|
||||
icon: <RiLink className='h-4 w-4' />,
|
||||
},
|
||||
]
|
||||
|
||||
const renderButton = useCallback((option: Option, open?: boolean) => {
|
||||
return (
|
||||
<Button
|
||||
key={option.value}
|
||||
// variant='tertiary'
|
||||
className={cn('relative grow', open && 'bg-components-button-tertiary-bg-hover')}
|
||||
disabled={!!(fileConfig.number_limits && files.length >= fileConfig.number_limits)}
|
||||
>
|
||||
{option.icon}
|
||||
<span className='ml-1'>{option.label}</span>
|
||||
{
|
||||
option.value === TransferMethod.local_file && (
|
||||
<FileInput fileConfig={fileConfig} />
|
||||
)
|
||||
}
|
||||
</Button>
|
||||
)
|
||||
}, [fileConfig, files.length])
|
||||
const renderTrigger = useCallback((option: Option) => {
|
||||
return (open: boolean) => renderButton(option, open)
|
||||
}, [renderButton])
|
||||
const renderOption = useCallback((option: Option) => {
|
||||
if (option.value === TransferMethod.local_file && fileConfig?.allowed_file_upload_methods?.includes(TransferMethod.local_file)) { return renderButton(option) }
|
||||
|
||||
if (option.value === TransferMethod.remote_url && fileConfig?.allowed_file_upload_methods?.includes(TransferMethod.remote_url)) {
|
||||
return (
|
||||
<FileFromLinkOrLocal
|
||||
key={option.value}
|
||||
showFromLocal={false}
|
||||
trigger={renderTrigger(option)}
|
||||
fileConfig={fileConfig}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}, [renderButton, renderTrigger, fileConfig])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='flex items-center space-x-1'>
|
||||
{options.map(renderOption)}
|
||||
</div>
|
||||
<div className='mt-1 space-y-1'>
|
||||
{
|
||||
files.map(file => (
|
||||
<FileItem
|
||||
key={file.id}
|
||||
file={file}
|
||||
showDeleteAction
|
||||
showDownloadAction={false}
|
||||
onRemove={() => handleRemoveFile(file.id)}
|
||||
onReUpload={() => handleReUploadFile(file.id)}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
interface FileUploaderInAttachmentWrapperProps {
|
||||
value?: FileEntity[]
|
||||
onChange: (files: FileEntity[]) => void
|
||||
fileConfig: FileUpload
|
||||
}
|
||||
const FileUploaderInAttachmentWrapper = ({
|
||||
value,
|
||||
onChange,
|
||||
fileConfig,
|
||||
}: FileUploaderInAttachmentWrapperProps) => {
|
||||
return (
|
||||
<FileContextProvider
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
>
|
||||
<FileUploaderInAttachment fileConfig={fileConfig} />
|
||||
</FileContextProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default FileUploaderInAttachmentWrapper
|
||||
65
app/components/base/file-uploader-in-attachment/store.tsx
Normal file
65
app/components/base/file-uploader-in-attachment/store.tsx
Normal file
@ -0,0 +1,65 @@
|
||||
import {
|
||||
createContext,
|
||||
useContext,
|
||||
useRef,
|
||||
} from 'react'
|
||||
import {
|
||||
create,
|
||||
useStore as useZustandStore,
|
||||
} from 'zustand'
|
||||
import type {
|
||||
FileEntity,
|
||||
} from './types'
|
||||
|
||||
interface Shape {
|
||||
files: FileEntity[]
|
||||
setFiles: (files: FileEntity[]) => void
|
||||
}
|
||||
|
||||
export const createFileStore = (
|
||||
value: FileEntity[] = [],
|
||||
onChange?: (files: FileEntity[]) => void,
|
||||
) => {
|
||||
return create<Shape>(set => ({
|
||||
files: value ? [...value] : [],
|
||||
setFiles: (files) => {
|
||||
set({ files })
|
||||
onChange?.(files)
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
type FileStore = ReturnType<typeof createFileStore>
|
||||
export const FileContext = createContext<FileStore | null>(null)
|
||||
|
||||
export function useStore<T>(selector: (state: Shape) => T): T {
|
||||
const store = useContext(FileContext)
|
||||
if (!store) { throw new Error('Missing FileContext.Provider in the tree') }
|
||||
|
||||
return useZustandStore(store, selector)
|
||||
}
|
||||
|
||||
export const useFileStore = () => {
|
||||
return useContext(FileContext)!
|
||||
}
|
||||
|
||||
interface FileProviderProps {
|
||||
children: React.ReactNode
|
||||
value?: FileEntity[]
|
||||
onChange?: (files: FileEntity[]) => void
|
||||
}
|
||||
export const FileContextProvider = ({
|
||||
children,
|
||||
value,
|
||||
onChange,
|
||||
}: FileProviderProps) => {
|
||||
const storeRef = useRef<FileStore | undefined>(undefined)
|
||||
|
||||
if (!storeRef.current) { storeRef.current = createFileStore(value, onChange) }
|
||||
|
||||
return (
|
||||
<FileContext.Provider value={storeRef.current}>
|
||||
{children}
|
||||
</FileContext.Provider>
|
||||
)
|
||||
}
|
||||
83
app/components/base/file-uploader-in-attachment/types.ts
Normal file
83
app/components/base/file-uploader-in-attachment/types.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import type { TransferMethod } from '@/types/app'
|
||||
|
||||
export enum FileAppearanceTypeEnum {
|
||||
image = 'image',
|
||||
video = 'video',
|
||||
audio = 'audio',
|
||||
document = 'document',
|
||||
code = 'code',
|
||||
pdf = 'pdf',
|
||||
markdown = 'markdown',
|
||||
excel = 'excel',
|
||||
word = 'word',
|
||||
ppt = 'ppt',
|
||||
gif = 'gif',
|
||||
custom = 'custom',
|
||||
}
|
||||
|
||||
export type FileAppearanceType = keyof typeof FileAppearanceTypeEnum
|
||||
|
||||
export interface FileEntity {
|
||||
id: string
|
||||
name: string
|
||||
size: number
|
||||
type: string
|
||||
progress: number
|
||||
transferMethod: TransferMethod
|
||||
supportFileType: string
|
||||
originalFile?: File
|
||||
uploadedId?: string
|
||||
base64Url?: string
|
||||
url?: string
|
||||
isRemote?: boolean
|
||||
}
|
||||
|
||||
export interface EnabledOrDisabled {
|
||||
enabled?: boolean
|
||||
}
|
||||
|
||||
export enum Resolution {
|
||||
low = 'low',
|
||||
high = 'high',
|
||||
}
|
||||
|
||||
export interface FileUploadConfigResponse {
|
||||
batch_count_limit: number
|
||||
image_file_size_limit?: number | string // default is 10MB
|
||||
file_size_limit: number // default is 15MB
|
||||
audio_file_size_limit?: number // default is 50MB
|
||||
video_file_size_limit?: number // default is 100MB
|
||||
workflow_file_upload_limit?: number // default is 10
|
||||
}
|
||||
|
||||
export type FileUpload = {
|
||||
image?: EnabledOrDisabled & {
|
||||
detail?: Resolution
|
||||
number_limits?: number
|
||||
transfer_methods?: TransferMethod[]
|
||||
}
|
||||
allowed_file_types?: string[]
|
||||
allowed_file_extensions?: string[]
|
||||
allowed_file_upload_methods?: TransferMethod[]
|
||||
number_limits?: number
|
||||
fileUploadConfig?: FileUploadConfigResponse
|
||||
} & EnabledOrDisabled
|
||||
|
||||
export enum SupportUploadFileTypes {
|
||||
image = 'image',
|
||||
document = 'document',
|
||||
audio = 'audio',
|
||||
video = 'video',
|
||||
custom = 'custom',
|
||||
}
|
||||
|
||||
export interface FileResponse {
|
||||
related_id: string
|
||||
extension: string
|
||||
filename: string
|
||||
size: number
|
||||
mime_type: string
|
||||
transfer_method: TransferMethod
|
||||
type: string
|
||||
url: string
|
||||
}
|
||||
174
app/components/base/file-uploader-in-attachment/utils.ts
Normal file
174
app/components/base/file-uploader-in-attachment/utils.ts
Normal file
@ -0,0 +1,174 @@
|
||||
import mime from 'mime'
|
||||
import { FileAppearanceTypeEnum, SupportUploadFileTypes } from './types'
|
||||
import type { FileEntity, FileResponse } from './types'
|
||||
import { FILE_EXTS } from './constants'
|
||||
import { upload } from '@/service/base'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
|
||||
interface FileUploadParams {
|
||||
file: File
|
||||
onProgressCallback: (progress: number) => void
|
||||
onSuccessCallback: (res: { id: string }) => void
|
||||
onErrorCallback: () => void
|
||||
}
|
||||
type FileUpload = (v: FileUploadParams, isPublic?: boolean, url?: string) => void
|
||||
export const fileUpload: FileUpload = ({
|
||||
file,
|
||||
onProgressCallback,
|
||||
onSuccessCallback,
|
||||
onErrorCallback,
|
||||
}) => {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
const onProgress = (e: ProgressEvent) => {
|
||||
if (e.lengthComputable) {
|
||||
const percent = Math.floor(e.loaded / e.total * 100)
|
||||
onProgressCallback(percent)
|
||||
}
|
||||
}
|
||||
|
||||
upload({
|
||||
xhr: new XMLHttpRequest(),
|
||||
data: formData,
|
||||
onprogress: onProgress,
|
||||
})
|
||||
.then((res: { id: string }) => {
|
||||
onSuccessCallback(res)
|
||||
})
|
||||
.catch(() => {
|
||||
onErrorCallback()
|
||||
})
|
||||
}
|
||||
|
||||
export const getFileExtension = (fileName: string, fileMimetype: string, isRemote?: boolean) => {
|
||||
let extension = ''
|
||||
if (fileMimetype) { extension = mime.getExtension(fileMimetype) || '' }
|
||||
|
||||
if (fileName && !extension) {
|
||||
const fileNamePair = fileName.split('.')
|
||||
const fileNamePairLength = fileNamePair.length
|
||||
|
||||
if (fileNamePairLength > 1) { extension = fileNamePair[fileNamePairLength - 1] }
|
||||
else { extension = '' }
|
||||
}
|
||||
|
||||
if (isRemote) { extension = '' }
|
||||
|
||||
return extension
|
||||
}
|
||||
|
||||
export const getFileAppearanceType = (fileName: string, fileMimetype: string) => {
|
||||
const extension = getFileExtension(fileName, fileMimetype)
|
||||
|
||||
if (extension === 'gif') { return FileAppearanceTypeEnum.gif }
|
||||
|
||||
if (FILE_EXTS.image.includes(extension.toUpperCase())) { return FileAppearanceTypeEnum.image }
|
||||
|
||||
if (FILE_EXTS.video.includes(extension.toUpperCase())) { return FileAppearanceTypeEnum.video }
|
||||
|
||||
if (FILE_EXTS.audio.includes(extension.toUpperCase())) { return FileAppearanceTypeEnum.audio }
|
||||
|
||||
if (extension === 'html') { return FileAppearanceTypeEnum.code }
|
||||
|
||||
if (extension === 'pdf') { return FileAppearanceTypeEnum.pdf }
|
||||
|
||||
if (extension === 'md' || extension === 'markdown' || extension === 'mdx') { return FileAppearanceTypeEnum.markdown }
|
||||
|
||||
if (extension === 'xlsx' || extension === 'xls') { return FileAppearanceTypeEnum.excel }
|
||||
|
||||
if (extension === 'docx' || extension === 'doc') { return FileAppearanceTypeEnum.word }
|
||||
|
||||
if (extension === 'pptx' || extension === 'ppt') { return FileAppearanceTypeEnum.ppt }
|
||||
|
||||
if (FILE_EXTS.document.includes(extension.toUpperCase())) { return FileAppearanceTypeEnum.document }
|
||||
|
||||
return FileAppearanceTypeEnum.custom
|
||||
}
|
||||
|
||||
export const getSupportFileType = (fileName: string, fileMimetype: string, isCustom?: boolean) => {
|
||||
if (isCustom) { return SupportUploadFileTypes.custom }
|
||||
|
||||
const extension = getFileExtension(fileName, fileMimetype)
|
||||
for (const key in FILE_EXTS) {
|
||||
if ((FILE_EXTS[key]).includes(extension.toUpperCase())) { return key }
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
export const getProcessedFiles = (files: FileEntity[]) => {
|
||||
return files.filter(file => file.progress !== -1).map(fileItem => ({
|
||||
type: fileItem.supportFileType,
|
||||
transfer_method: fileItem.transferMethod,
|
||||
url: fileItem.url || '',
|
||||
upload_file_id: fileItem.uploadedId || '',
|
||||
}))
|
||||
}
|
||||
|
||||
export const getProcessedFilesFromResponse = (files: FileResponse[]) => {
|
||||
return files.map((fileItem) => {
|
||||
return {
|
||||
id: fileItem.related_id,
|
||||
name: fileItem.filename,
|
||||
size: fileItem.size || 0,
|
||||
type: fileItem.mime_type,
|
||||
progress: 100,
|
||||
transferMethod: fileItem.transfer_method,
|
||||
supportFileType: fileItem.type,
|
||||
uploadedId: fileItem.related_id,
|
||||
url: fileItem.url,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const getFileNameFromUrl = (url: string) => {
|
||||
const urlParts = url.split('/')
|
||||
return urlParts[urlParts.length - 1] || ''
|
||||
}
|
||||
|
||||
export const getSupportFileExtensionList = (allowFileTypes: string[], allowFileExtensions: string[]) => {
|
||||
if (allowFileTypes.includes(SupportUploadFileTypes.custom)) { return allowFileExtensions.map(item => item.slice(1).toUpperCase()) }
|
||||
|
||||
return allowFileTypes.map(type => FILE_EXTS[type]).flat()
|
||||
}
|
||||
|
||||
export const isAllowedFileExtension = (fileName: string, fileMimetype: string, allowFileTypes: string[], allowFileExtensions: string[]) => {
|
||||
return getSupportFileExtensionList(allowFileTypes, allowFileExtensions).includes(getFileExtension(fileName, fileMimetype).toUpperCase())
|
||||
}
|
||||
|
||||
export const getFilesInLogs = (rawData: any) => {
|
||||
const result = Object.keys(rawData || {}).map((key) => {
|
||||
if (typeof rawData[key] === 'object' && rawData[key]?.dify_model_identity === '__dify__file__') {
|
||||
return {
|
||||
varName: key,
|
||||
list: getProcessedFilesFromResponse([rawData[key]]),
|
||||
}
|
||||
}
|
||||
if (Array.isArray(rawData[key]) && rawData[key].some(item => item?.dify_model_identity === '__dify__file__')) {
|
||||
return {
|
||||
varName: key,
|
||||
list: getProcessedFilesFromResponse(rawData[key]),
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}).filter(Boolean)
|
||||
return result
|
||||
}
|
||||
|
||||
export const fileIsUploaded = (file: FileEntity) => {
|
||||
if (file.uploadedId) { return true }
|
||||
|
||||
if (file.transferMethod === TransferMethod.remote_url && file.progress === 100) { return true }
|
||||
}
|
||||
|
||||
export const downloadFile = (url: string, filename: string) => {
|
||||
const anchor = document.createElement('a')
|
||||
anchor.href = url
|
||||
anchor.download = filename
|
||||
anchor.style.display = 'none'
|
||||
anchor.target = '_blank'
|
||||
anchor.title = filename
|
||||
document.body.appendChild(anchor)
|
||||
anchor.click()
|
||||
document.body.removeChild(anchor)
|
||||
}
|
||||
31
app/components/base/icons/IconBase.tsx
Normal file
31
app/components/base/icons/IconBase.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import { forwardRef } from 'react'
|
||||
import { generate } from './utils'
|
||||
import type { AbstractNode } from './utils'
|
||||
|
||||
export interface IconData {
|
||||
name: string
|
||||
icon: AbstractNode
|
||||
}
|
||||
|
||||
export interface IconBaseProps {
|
||||
data: IconData
|
||||
className?: string
|
||||
onClick?: React.MouseEventHandler<SVGElement>
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
|
||||
const IconBase = forwardRef<React.MutableRefObject<HTMLOrSVGElement>, IconBaseProps>((props, ref) => {
|
||||
const { data, className, onClick, style, ...restProps } = props
|
||||
|
||||
return generate(data.icon, `svg-${data.name}`, {
|
||||
className,
|
||||
onClick,
|
||||
style,
|
||||
'data-icon': data.name,
|
||||
'aria-hidden': 'true',
|
||||
...restProps,
|
||||
'ref': ref,
|
||||
})
|
||||
})
|
||||
|
||||
export default IconBase
|
||||
39
app/components/base/icons/line/alert-circle/AlertCircle.json
Normal file
39
app/components/base/icons/line/alert-circle/AlertCircle.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "16",
|
||||
"height": "17",
|
||||
"viewBox": "0 0 16 17",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "Error"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Icon",
|
||||
"d": "M7.99992 5.83337V8.50004M7.99992 11.1667H8.00659M14.6666 8.50004C14.6666 12.1819 11.6818 15.1667 7.99992 15.1667C4.31802 15.1667 1.33325 12.1819 1.33325 8.50004C1.33325 4.81814 4.31802 1.83337 7.99992 1.83337C11.6818 1.83337 14.6666 4.81814 14.6666 8.50004Z",
|
||||
"stroke": "currentColor",
|
||||
"stroke-width": "1.5",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "AlertCircle"
|
||||
}
|
||||
16
app/components/base/icons/line/alert-circle/index.tsx
Normal file
16
app/components/base/icons/line/alert-circle/index.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './AlertCircle.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 = 'AlertCircle'
|
||||
|
||||
export default Icon
|
||||
@ -0,0 +1,39 @@
|
||||
{
|
||||
"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": "alert-triangle"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Icon",
|
||||
"d": "M7.99977 5.33314V7.99981M7.99977 10.6665H8.00644M6.85977 1.90648L1.2131 11.3331C1.09668 11.5348 1.03508 11.7633 1.03443 11.9962C1.03378 12.229 1.0941 12.4579 1.20939 12.6602C1.32468 12.8624 1.49092 13.031 1.69157 13.149C1.89223 13.2671 2.1203 13.3306 2.3531 13.3331H13.6464C13.8792 13.3306 14.1073 13.2671 14.308 13.149C14.5086 13.031 14.6749 12.8624 14.7902 12.6602C14.9054 12.4579 14.9658 12.229 14.9651 11.9962C14.9645 11.7633 14.9029 11.5348 14.7864 11.3331L9.13977 1.90648C9.02092 1.71055 8.85358 1.54856 8.6539 1.43613C8.45422 1.32371 8.22893 1.26465 7.99977 1.26465C7.77061 1.26465 7.54532 1.32371 7.34564 1.43613C7.14596 1.54856 6.97862 1.71055 6.85977 1.90648Z",
|
||||
"stroke": "currentColor",
|
||||
"stroke-width": "1.25",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "AlertTriangle"
|
||||
}
|
||||
16
app/components/base/icons/line/alert-triangle/index.tsx
Normal file
16
app/components/base/icons/line/alert-triangle/index.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './AlertTriangle.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 = 'AlertTriangle'
|
||||
|
||||
export default Icon
|
||||
39
app/components/base/icons/line/arrows/chevron-down/data.json
Normal file
39
app/components/base/icons/line/arrows/chevron-down/data.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "12",
|
||||
"height": "12",
|
||||
"viewBox": "0 0 12 12",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "chevron-down"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Icon",
|
||||
"d": "M3 4.5L6 7.5L9 4.5",
|
||||
"stroke": "currentColor",
|
||||
"stroke-width": "1.5",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "ChevronDown"
|
||||
}
|
||||
16
app/components/base/icons/line/arrows/chevron-down/index.tsx
Normal file
16
app/components/base/icons/line/arrows/chevron-down/index.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './data.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 = 'ChevronDown'
|
||||
|
||||
export default Icon
|
||||
@ -0,0 +1,29 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "24",
|
||||
"height": "24",
|
||||
"viewBox": "0 0 24 24",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M4 14H10M10 14V20M10 14L3 21M20 10H14M14 10V4M14 10L21 3M20 14V16.8C20 17.9201 20 18.4802 19.782 18.908C19.5903 19.2843 19.2843 19.5903 18.908 19.782C18.4802 20 17.9201 20 16.8 20H14M10 4H7.2C6.0799 4 5.51984 4 5.09202 4.21799C4.71569 4.40973 4.40973 4.71569 4.21799 5.09202C4 5.51984 4 6.07989 4 7.2V10",
|
||||
"stroke": "currentColor",
|
||||
"stroke-width": "2",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Collapse04"
|
||||
}
|
||||
16
app/components/base/icons/line/arrows/collapse-04/index.tsx
Normal file
16
app/components/base/icons/line/arrows/collapse-04/index.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Collapse04.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 = 'Collapse04'
|
||||
|
||||
export default Icon
|
||||
66
app/components/base/icons/line/check-circle/CheckCircle.json
Normal file
66
app/components/base/icons/line/check-circle/CheckCircle.json
Normal file
@ -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": "check-circle",
|
||||
"clip-path": "url(#clip0_465_21765)"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Icon",
|
||||
"d": "M4.37533 6.99984L6.12533 8.74984L9.62533 5.24984M12.8337 6.99984C12.8337 10.2215 10.222 12.8332 7.00033 12.8332C3.77866 12.8332 1.16699 10.2215 1.16699 6.99984C1.16699 3.77818 3.77866 1.1665 7.00033 1.1665C10.222 1.1665 12.8337 3.77818 12.8337 6.99984Z",
|
||||
"stroke": "currentColor",
|
||||
"stroke-width": "1.5",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "defs",
|
||||
"attributes": {},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "clipPath",
|
||||
"attributes": {
|
||||
"id": "clip0_465_21765"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "rect",
|
||||
"attributes": {
|
||||
"width": "14",
|
||||
"height": "14",
|
||||
"fill": "white"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "CheckCircle"
|
||||
}
|
||||
16
app/components/base/icons/line/check-circle/index.tsx
Normal file
16
app/components/base/icons/line/check-circle/index.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './CheckCircle.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 = 'CheckCircle'
|
||||
|
||||
export default Icon
|
||||
@ -0,0 +1,39 @@
|
||||
{
|
||||
"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": "chevron-right"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Icon",
|
||||
"d": "M5.25 10.5L8.75 7L5.25 3.5",
|
||||
"stroke": "currentColor",
|
||||
"stroke-width": "1.25",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "ChevronRight"
|
||||
}
|
||||
16
app/components/base/icons/line/chevron-right/index.tsx
Normal file
16
app/components/base/icons/line/chevron-right/index.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './ChevronRight.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 = 'ChevronRight'
|
||||
|
||||
export default Icon
|
||||
29
app/components/base/icons/line/files/Clipboard.json
Normal file
29
app/components/base/icons/line/files/Clipboard.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "24",
|
||||
"height": "24",
|
||||
"viewBox": "0 0 24 24",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M16 4C16.93 4 17.395 4 17.7765 4.10222C18.8117 4.37962 19.6204 5.18827 19.8978 6.22354C20 6.60504 20 7.07003 20 8V17.2C20 18.8802 20 19.7202 19.673 20.362C19.3854 20.9265 18.9265 21.3854 18.362 21.673C17.7202 22 16.8802 22 15.2 22H8.8C7.11984 22 6.27976 22 5.63803 21.673C5.07354 21.3854 4.6146 20.9265 4.32698 20.362C4 19.7202 4 18.8802 4 17.2V8C4 7.07003 4 6.60504 4.10222 6.22354C4.37962 5.18827 5.18827 4.37962 6.22354 4.10222C6.60504 4 7.07003 4 8 4M9.6 6H14.4C14.9601 6 15.2401 6 15.454 5.89101C15.6422 5.79513 15.7951 5.64215 15.891 5.45399C16 5.24008 16 4.96005 16 4.4V3.6C16 3.03995 16 2.75992 15.891 2.54601C15.7951 2.35785 15.6422 2.20487 15.454 2.10899C15.2401 2 14.9601 2 14.4 2H9.6C9.03995 2 8.75992 2 8.54601 2.10899C8.35785 2.20487 8.20487 2.35785 8.10899 2.54601C8 2.75992 8 3.03995 8 3.6V4.4C8 4.96005 8 5.24008 8.10899 5.45399C8.20487 5.64215 8.35785 5.79513 8.54601 5.89101C8.75992 6 9.03995 6 9.6 6Z",
|
||||
"stroke": "currentColor",
|
||||
"stroke-width": "2",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Clipboard"
|
||||
}
|
||||
16
app/components/base/icons/line/files/Clipboard.tsx
Normal file
16
app/components/base/icons/line/files/Clipboard.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Clipboard.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 = 'Clipboard'
|
||||
|
||||
export default Icon
|
||||
29
app/components/base/icons/line/files/ClipboardCheck.json
Normal file
29
app/components/base/icons/line/files/ClipboardCheck.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "24",
|
||||
"height": "24",
|
||||
"viewBox": "0 0 24 24",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M16 4C16.93 4 17.395 4 17.7765 4.10222C18.8117 4.37962 19.6204 5.18827 19.8978 6.22354C20 6.60504 20 7.07003 20 8V17.2C20 18.8802 20 19.7202 19.673 20.362C19.3854 20.9265 18.9265 21.3854 18.362 21.673C17.7202 22 16.8802 22 15.2 22H8.8C7.11984 22 6.27976 22 5.63803 21.673C5.07354 21.3854 4.6146 20.9265 4.32698 20.362C4 19.7202 4 18.8802 4 17.2V8C4 7.07003 4 6.60504 4.10222 6.22354C4.37962 5.18827 5.18827 4.37962 6.22354 4.10222C6.60504 4 7.07003 4 8 4M9 15L11 17L15.5 12.5M9.6 6H14.4C14.9601 6 15.2401 6 15.454 5.89101C15.6422 5.79513 15.7951 5.64215 15.891 5.45399C16 5.24008 16 4.96005 16 4.4V3.6C16 3.03995 16 2.75992 15.891 2.54601C15.7951 2.35785 15.6422 2.20487 15.454 2.10899C15.2401 2 14.9601 2 14.4 2H9.6C9.03995 2 8.75992 2 8.54601 2.10899C8.35785 2.20487 8.20487 2.35785 8.10899 2.54601C8 2.75992 8 3.03995 8 3.6V4.4C8 4.96005 8 5.24008 8.10899 5.45399C8.20487 5.64215 8.35785 5.79513 8.54601 5.89101C8.75992 6 9.03995 6 9.6 6Z",
|
||||
"stroke": "currentColor",
|
||||
"stroke-width": "2",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "ClipboardCheck"
|
||||
}
|
||||
16
app/components/base/icons/line/files/ClipboardCheck.tsx
Normal file
16
app/components/base/icons/line/files/ClipboardCheck.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './ClipboardCheck.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 = 'ClipboardCheck'
|
||||
|
||||
export default Icon
|
||||
2
app/components/base/icons/line/files/index.ts
Normal file
2
app/components/base/icons/line/files/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { default as ClipboardCheck } from './ClipboardCheck'
|
||||
export { default as Clipboard } from './Clipboard'
|
||||
39
app/components/base/icons/line/image-plus/data.json
Normal file
39
app/components/base/icons/line/image-plus/data.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"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": "image-plus"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Icon",
|
||||
"d": "M8.33333 2.00016H5.2C4.0799 2.00016 3.51984 2.00016 3.09202 2.21815C2.71569 2.4099 2.40973 2.71586 2.21799 3.09218C2 3.52001 2 4.08006 2 5.20016V10.8002C2 11.9203 2 12.4803 2.21799 12.9081C2.40973 13.2845 2.71569 13.5904 3.09202 13.7822C3.51984 14.0002 4.07989 14.0002 5.2 14.0002H11.3333C11.9533 14.0002 12.2633 14.0002 12.5176 13.932C13.2078 13.7471 13.7469 13.208 13.9319 12.5178C14 12.2635 14 11.9535 14 11.3335M12.6667 5.3335V1.3335M10.6667 3.3335H14.6667M7 5.66683C7 6.40321 6.40305 7.00016 5.66667 7.00016C4.93029 7.00016 4.33333 6.40321 4.33333 5.66683C4.33333 4.93045 4.93029 4.3335 5.66667 4.3335C6.40305 4.3335 7 4.93045 7 5.66683ZM9.99336 7.94559L4.3541 13.0722C4.03691 13.3605 3.87831 13.5047 3.86429 13.6296C3.85213 13.7379 3.89364 13.8453 3.97546 13.9172C4.06985 14.0002 4.28419 14.0002 4.71286 14.0002H10.9707C11.9301 14.0002 12.4098 14.0002 12.7866 13.839C13.2596 13.6366 13.6365 13.2598 13.8388 12.7868C14 12.41 14 11.9303 14 10.9708C14 10.648 14 10.4866 13.9647 10.3363C13.9204 10.1474 13.8353 9.9704 13.7155 9.81776C13.6202 9.6963 13.4941 9.59546 13.242 9.3938L11.3772 7.90194C11.1249 7.7001 10.9988 7.59919 10.8599 7.56357C10.7374 7.53218 10.6086 7.53624 10.4884 7.57529C10.352 7.61959 10.2324 7.72826 9.99336 7.94559Z",
|
||||
"stroke": "currentColor",
|
||||
"stroke-width": "1.25",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "ImagePlus"
|
||||
}
|
||||
13
app/components/base/icons/line/image-plus/index.tsx
Normal file
13
app/components/base/icons/line/image-plus/index.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import * as React from 'react'
|
||||
import data from './data.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 = 'ImagePlus'
|
||||
|
||||
export default Icon
|
||||
57
app/components/base/icons/line/link-03/data.json
Normal file
57
app/components/base/icons/line/link-03/data.json
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "17",
|
||||
"height": "16",
|
||||
"viewBox": "0 0 17 16",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "link-03"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "Solid"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M9.01569 1.83378C9.7701 1.10515 10.7805 0.701975 11.8293 0.711089C12.8781 0.720202 13.8813 1.14088 14.623 1.88251C15.3646 2.62414 15.7853 3.62739 15.7944 4.67618C15.8035 5.72497 15.4003 6.73538 14.6717 7.48979L14.6636 7.49805L12.6637 9.49796C12.2581 9.90362 11.7701 10.2173 11.2327 10.4178C10.6953 10.6183 10.1211 10.7008 9.54897 10.6598C8.97686 10.6189 8.42025 10.4553 7.91689 10.1803C7.41354 9.90531 6.97522 9.52527 6.63165 9.06596C6.41112 8.77113 6.47134 8.35334 6.76618 8.1328C7.06101 7.91226 7.4788 7.97249 7.69934 8.26732C7.92838 8.57353 8.2206 8.82689 8.55617 9.01023C8.89174 9.19356 9.26281 9.30259 9.64422 9.3299C10.0256 9.35722 10.4085 9.30219 10.7667 9.16854C11.125 9.0349 11.4503 8.82576 11.7207 8.55532L13.7164 6.55956C14.1998 6.05705 14.4672 5.38513 14.4611 4.68777C14.455 3.98857 14.1746 3.31974 13.6802 2.82532C13.1857 2.3309 12.5169 2.05045 11.8177 2.04437C11.12 2.03831 10.4478 2.30591 9.94526 2.78967L8.80219 3.92609C8.54108 4.18568 8.11898 4.18445 7.85939 3.92334C7.5998 3.66223 7.60103 3.24012 7.86214 2.98053L9.0088 1.84053L9.01569 1.83378Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M5.76493 5.58217C6.30234 5.3817 6.87657 5.29915 7.44869 5.34012C8.0208 5.3811 8.57741 5.54463 9.08077 5.81964C9.58412 6.09465 10.0224 6.47469 10.366 6.93399C10.5865 7.22882 10.5263 7.64662 10.2315 7.86715C9.93665 8.08769 9.51886 8.02746 9.29832 7.73263C9.06928 7.42643 8.77706 7.17307 8.44149 6.98973C8.10592 6.80639 7.73485 6.69737 7.35344 6.67005C6.97203 6.64274 6.58921 6.69777 6.23094 6.83141C5.87266 6.96506 5.54733 7.17419 5.27699 7.44463L3.28123 9.44039C2.79787 9.94291 2.5305 10.6148 2.53656 11.3122C2.54263 12.0114 2.82309 12.6802 3.31751 13.1746C3.81193 13.6691 4.48076 13.9495 5.17995 13.9556C5.87732 13.9616 6.54923 13.6943 7.05174 13.2109L8.18743 12.0752C8.44777 11.8149 8.86988 11.8149 9.13023 12.0752C9.39058 12.3356 9.39058 12.7577 9.13023 13.018L7.99023 14.158L7.98197 14.1662C7.22756 14.8948 6.21715 15.298 5.16837 15.2889C4.11958 15.2798 3.11633 14.8591 2.3747 14.1174C1.63307 13.3758 1.21239 12.3726 1.20328 11.3238C1.19416 10.275 1.59734 9.26458 2.32597 8.51017L2.33409 8.50191L4.33401 6.50199C4.33398 6.50202 4.33404 6.50196 4.33401 6.50199C4.7395 6.09638 5.22756 5.78262 5.76493 5.58217Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Link03"
|
||||
}
|
||||
13
app/components/base/icons/line/link-03/index.tsx
Normal file
13
app/components/base/icons/line/link-03/index.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import * as React from 'react'
|
||||
import data from './data.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 = 'Link03'
|
||||
|
||||
export default Icon
|
||||
64
app/components/base/icons/line/loading-02/data.json
Normal file
64
app/components/base/icons/line/loading-02/data.json
Normal file
@ -0,0 +1,64 @@
|
||||
{
|
||||
"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": {
|
||||
"clip-path": "url(#clip0_6037_51601)"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M7.99992 1.33398V4.00065M7.99992 12.0007V14.6673M3.99992 8.00065H1.33325M14.6666 8.00065H11.9999M12.7189 12.7196L10.8333 10.834M12.7189 3.33395L10.8333 5.21956M3.28097 12.7196L5.16659 10.834M3.28097 3.33395L5.16659 5.21956",
|
||||
"stroke": "currentColor",
|
||||
"stroke-width": "1.25",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "defs",
|
||||
"attributes": {},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "clipPath",
|
||||
"attributes": {
|
||||
"id": "clip0_6037_51601"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "rect",
|
||||
"attributes": {
|
||||
"width": "16",
|
||||
"height": "16",
|
||||
"fill": "white"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Loading02"
|
||||
}
|
||||
13
app/components/base/icons/line/loading-02/index.tsx
Normal file
13
app/components/base/icons/line/loading-02/index.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import * as React from 'react'
|
||||
import data from './data.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 = 'Loading02'
|
||||
|
||||
export default Icon
|
||||
29
app/components/base/icons/line/refresh-ccw-01/data.json
Normal file
29
app/components/base/icons/line/refresh-ccw-01/data.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "24",
|
||||
"height": "24",
|
||||
"viewBox": "0 0 24 24",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M2 10C2 10 4.00498 7.26822 5.63384 5.63824C7.26269 4.00827 9.5136 3 12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21C7.89691 21 4.43511 18.2543 3.35177 14.5M2 10V4M2 10H8",
|
||||
"stroke": "currentColor",
|
||||
"stroke-width": "2",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "RefreshCcw01"
|
||||
}
|
||||
13
app/components/base/icons/line/refresh-ccw-01/index.tsx
Normal file
13
app/components/base/icons/line/refresh-ccw-01/index.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import * as React from 'react'
|
||||
import data from './data.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 = 'RefreshCcw01'
|
||||
|
||||
export default Icon
|
||||
66
app/components/base/icons/line/upload-03/data.json
Normal file
66
app/components/base/icons/line/upload-03/data.json
Normal file
@ -0,0 +1,66 @@
|
||||
{
|
||||
"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": "Left Icon",
|
||||
"clip-path": "url(#clip0_12728_40636)"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Icon",
|
||||
"d": "M10.6654 8.00016L7.9987 5.3335M7.9987 5.3335L5.33203 8.00016M7.9987 5.3335V10.6668M14.6654 8.00016C14.6654 11.6821 11.6806 14.6668 7.9987 14.6668C4.3168 14.6668 1.33203 11.6821 1.33203 8.00016C1.33203 4.31826 4.3168 1.3335 7.9987 1.3335C11.6806 1.3335 14.6654 4.31826 14.6654 8.00016Z",
|
||||
"stroke": "currentColor",
|
||||
"stroke-width": "1.5",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "defs",
|
||||
"attributes": {},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "clipPath",
|
||||
"attributes": {
|
||||
"id": "clip0_12728_40636"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "rect",
|
||||
"attributes": {
|
||||
"width": "16",
|
||||
"height": "16",
|
||||
"fill": "white"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Upload03"
|
||||
}
|
||||
13
app/components/base/icons/line/upload-03/index.tsx
Normal file
13
app/components/base/icons/line/upload-03/index.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import * as React from 'react'
|
||||
import data from './data.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 = 'Upload03'
|
||||
|
||||
export default Icon
|
||||
39
app/components/base/icons/line/x-close/data.json
Normal file
39
app/components/base/icons/line/x-close/data.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"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": "x-close"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Icon",
|
||||
"d": "M12 4L4 12M4 4L12 12",
|
||||
"stroke": "currentColor",
|
||||
"stroke-width": "1.25",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "XClose"
|
||||
}
|
||||
13
app/components/base/icons/line/x-close/index.tsx
Normal file
13
app/components/base/icons/line/x-close/index.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import * as React from 'react'
|
||||
import data from './data.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 = 'XClose'
|
||||
|
||||
export default Icon
|
||||
36
app/components/base/icons/other/ReplayLine.json
Normal file
36
app/components/base/icons/other/ReplayLine.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "20",
|
||||
"height": "20",
|
||||
"viewBox": "0 0 20 20",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "Retry"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Vector",
|
||||
"d": "M9.99996 1.66669C14.6023 1.66669 18.3333 5.39765 18.3333 10C18.3333 14.6024 14.6023 18.3334 9.99996 18.3334C5.39758 18.3334 1.66663 14.6024 1.66663 10H3.33329C3.33329 13.6819 6.31806 16.6667 9.99996 16.6667C13.6819 16.6667 16.6666 13.6819 16.6666 10C16.6666 6.31812 13.6819 3.33335 9.99996 3.33335C7.70848 3.33335 5.68702 4.48947 4.48705 6.25022L6.66663 6.25002V7.91669H1.66663V2.91669H3.33329L3.3332 4.99934C4.85358 2.97565 7.2739 1.66669 9.99996 1.66669Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "ReplayLine"
|
||||
}
|
||||
20
app/components/base/icons/other/ReplayLine.tsx
Normal file
20
app/components/base/icons/other/ReplayLine.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './ReplayLine.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.MutableRefObject<HTMLOrSVGElement>>
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
Icon.displayName = 'ReplayLine'
|
||||
|
||||
export default Icon
|
||||
64
app/components/base/icons/public/data-set/data.json
Normal file
64
app/components/base/icons/public/data-set/data.json
Normal file
@ -0,0 +1,64 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "12",
|
||||
"height": "12",
|
||||
"viewBox": "0 0 12 12",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"clip-path": "url(#clip0_7847_32895)"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M10.5 2.5C10.5 3.32843 8.48528 4 6 4C3.51472 4 1.5 3.32843 1.5 2.5M10.5 2.5C10.5 1.67157 8.48528 1 6 1C3.51472 1 1.5 1.67157 1.5 2.5M10.5 2.5V9.5C10.5 10.33 8.5 11 6 11C3.5 11 1.5 10.33 1.5 9.5V2.5M10.5 6C10.5 6.83 8.5 7.5 6 7.5C3.5 7.5 1.5 6.83 1.5 6",
|
||||
"stroke": "#667085",
|
||||
"stroke-width": "1.25",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "defs",
|
||||
"attributes": {},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "clipPath",
|
||||
"attributes": {
|
||||
"id": "clip0_7847_32895"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "rect",
|
||||
"attributes": {
|
||||
"width": "12",
|
||||
"height": "12",
|
||||
"fill": "white"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "DataSet"
|
||||
}
|
||||
16
app/components/base/icons/public/data-set/index.tsx
Normal file
16
app/components/base/icons/public/data-set/index.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './data.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 = 'DataSet'
|
||||
|
||||
export default Icon
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"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": "alert-circle"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Solid",
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M8 0.666626C3.94992 0.666626 0.666672 3.94987 0.666672 7.99996C0.666672 12.05 3.94992 15.3333 8 15.3333C12.0501 15.3333 15.3333 12.05 15.3333 7.99996C15.3333 3.94987 12.0501 0.666626 8 0.666626ZM8.66667 5.33329C8.66667 4.9651 8.36819 4.66663 8 4.66663C7.63181 4.66663 7.33334 4.9651 7.33334 5.33329V7.99996C7.33334 8.36815 7.63181 8.66663 8 8.66663C8.36819 8.66663 8.66667 8.36815 8.66667 7.99996V5.33329ZM8 9.99996C7.63181 9.99996 7.33334 10.2984 7.33334 10.6666C7.33334 11.0348 7.63181 11.3333 8 11.3333H8.00667C8.37486 11.3333 8.67334 11.0348 8.67334 10.6666C8.67334 10.2984 8.37486 9.99996 8.00667 9.99996H8Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "AlertCircle"
|
||||
}
|
||||
16
app/components/base/icons/solid/alert-circle/index.tsx
Normal file
16
app/components/base/icons/solid/alert-circle/index.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './AlertCircle.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 = 'AlertCircle'
|
||||
|
||||
export default Icon
|
||||
38
app/components/base/icons/solid/alert-triangle/data.json
Normal file
38
app/components/base/icons/solid/alert-triangle/data.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "12",
|
||||
"height": "12",
|
||||
"viewBox": "0 0 12 12",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "alert-triangle"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Solid",
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M6.40616 0.834185C6.14751 0.719172 5.85222 0.719172 5.59356 0.834185C5.3938 0.923011 5.26403 1.07947 5.17373 1.20696C5.08495 1.3323 4.9899 1.49651 4.88536 1.67711L0.751783 8.81693C0.646828 8.99818 0.551451 9.16289 0.486781 9.30268C0.421056 9.44475 0.349754 9.63572 0.372478 9.85369C0.401884 10.1357 0.549654 10.392 0.779012 10.5588C0.956259 10.6877 1.15726 10.7217 1.31314 10.736C1.46651 10.75 1.65684 10.75 1.86628 10.75H10.1334C10.3429 10.75 10.5332 10.75 10.6866 10.736C10.8425 10.7217 11.0435 10.6877 11.2207 10.5588C11.4501 10.392 11.5978 10.1357 11.6272 9.85369C11.65 9.63572 11.5787 9.44475 11.5129 9.30268C11.4483 9.1629 11.3529 8.9982 11.248 8.81697L7.11436 1.67709C7.00983 1.49651 6.91477 1.3323 6.82599 1.20696C6.73569 1.07947 6.60593 0.923011 6.40616 0.834185ZM6.49988 4.5C6.49988 4.22386 6.27602 4 5.99988 4C5.72374 4 5.49988 4.22386 5.49988 4.5V6.5C5.49988 6.77614 5.72374 7 5.99988 7C6.27602 7 6.49988 6.77614 6.49988 6.5V4.5ZM5.99988 8C5.72374 8 5.49988 8.22386 5.49988 8.5C5.49988 8.77614 5.72374 9 5.99988 9H6.00488C6.28102 9 6.50488 8.77614 6.50488 8.5C6.50488 8.22386 6.28102 8 6.00488 8H5.99988Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "AlertTriangle"
|
||||
}
|
||||
13
app/components/base/icons/solid/alert-triangle/index.tsx
Normal file
13
app/components/base/icons/solid/alert-triangle/index.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import * as React from 'react'
|
||||
import data from './data.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 = 'AlertTriangle'
|
||||
|
||||
export default Icon
|
||||
39
app/components/base/icons/solid/expand-04/Expand04.json
Normal file
39
app/components/base/icons/solid/expand-04/Expand04.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "14",
|
||||
"height": "15",
|
||||
"viewBox": "0 0 14 15",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "Icon"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Icon_2",
|
||||
"d": "M11.6667 8.66667V10.3C11.6667 10.9534 11.6667 11.2801 11.5395 11.5297C11.4277 11.7492 11.2492 11.9277 11.0297 12.0395C10.7801 12.1667 10.4534 12.1667 9.8 12.1667H8.16667M5.83333 2.83333H4.2C3.54661 2.83333 3.21991 2.83333 2.97034 2.96049C2.75082 3.07234 2.57234 3.25082 2.46049 3.47034C2.33333 3.71991 2.33333 4.04661 2.33333 4.7V6.33333M8.75 5.75L12.25 2.25M12.25 2.25H8.75M12.25 2.25V5.75M5.25 9.25L1.75 12.75M1.75 12.75H5.25M1.75 12.75L1.75 9.25",
|
||||
"stroke": "currentColor",
|
||||
"stroke-width": "1.25",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Expand04"
|
||||
}
|
||||
16
app/components/base/icons/solid/expand-04/index.tsx
Normal file
16
app/components/base/icons/solid/expand-04/index.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Expand04.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 = 'Expand04'
|
||||
|
||||
export default Icon
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"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": "check-circle"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Solid",
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M8 0.666626C3.94992 0.666626 0.666672 3.94987 0.666672 7.99996C0.666672 12.05 3.94992 15.3333 8 15.3333C12.0501 15.3333 15.3333 12.05 15.3333 7.99996C15.3333 3.94987 12.0501 0.666626 8 0.666626ZM11.4714 6.47136C11.7318 6.21101 11.7318 5.7889 11.4714 5.52855C11.2111 5.26821 10.7889 5.26821 10.5286 5.52855L7 9.05715L5.47141 7.52855C5.21106 7.2682 4.78895 7.2682 4.5286 7.52855C4.26825 7.7889 4.26825 8.21101 4.5286 8.47136L6.5286 10.4714C6.78895 10.7317 7.21106 10.7317 7.47141 10.4714L11.4714 6.47136Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "CheckCircle"
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './data.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 = 'CheckCircle'
|
||||
|
||||
export default Icon
|
||||
66
app/components/base/icons/utils.tsx
Normal file
66
app/components/base/icons/utils.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
import React from 'react'
|
||||
|
||||
export interface AbstractNode {
|
||||
name: string
|
||||
attributes: {
|
||||
[key: string]: string
|
||||
}
|
||||
children?: AbstractNode[]
|
||||
}
|
||||
|
||||
export interface Attrs {
|
||||
[key: string]: string
|
||||
}
|
||||
|
||||
export function normalizeAttrs(attrs: Attrs = {}): Attrs {
|
||||
return Object.keys(attrs).reduce((acc: Attrs, key) => {
|
||||
const val = attrs[key]
|
||||
key = key.replace(/([-]\w)/g, (g: string) => g[1].toUpperCase())
|
||||
key = key.replace(/([:]\w)/g, (g: string) => g[1].toUpperCase())
|
||||
switch (key) {
|
||||
case 'class':
|
||||
acc.className = val
|
||||
delete acc.class
|
||||
break
|
||||
case 'style':
|
||||
(acc.style as any) = val.split(';').reduce((prev, next) => {
|
||||
const pairs = next?.split(':')
|
||||
|
||||
if (pairs[0] && pairs[1]) {
|
||||
const k = pairs[0].replace(/([-]\w)/g, (g: string) => g[1].toUpperCase())
|
||||
prev[k] = pairs[1]
|
||||
}
|
||||
|
||||
return prev
|
||||
}, {} as Attrs)
|
||||
break
|
||||
default:
|
||||
acc[key] = val
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
export function generate(
|
||||
node: AbstractNode,
|
||||
key: string,
|
||||
rootProps?: { [key: string]: any } | false,
|
||||
): any {
|
||||
if (!rootProps) {
|
||||
return React.createElement(
|
||||
node.name,
|
||||
{ key, ...normalizeAttrs(node.attributes) },
|
||||
(node.children || []).map((child, index) => generate(child, `${key}-${node.name}-${index}`)),
|
||||
)
|
||||
}
|
||||
|
||||
return React.createElement(
|
||||
node.name,
|
||||
{
|
||||
key,
|
||||
...normalizeAttrs(node.attributes),
|
||||
...rootProps,
|
||||
},
|
||||
(node.children || []).map((child, index) => generate(child, `${key}-${node.name}-${index}`)),
|
||||
)
|
||||
}
|
||||
38
app/components/base/icons/workflow/Answer.json
Normal file
38
app/components/base/icons/workflow/Answer.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"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": "icons/answer"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Vector (Stroke)",
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M3.50114 1.67701L10.5011 1.677C11.5079 1.677 12.3241 2.49311 12.3241 3.49992V9.35414C12.3241 10.3609 11.5079 11.177 10.5012 11.1771H8.9954L7.41734 12.4845C7.17339 12.6866 6.81987 12.6856 6.57708 12.4821L5.02026 11.1771H3.50114C2.49436 11.1771 1.67822 10.3608 1.67822 9.35414V3.49993C1.67822 2.49316 2.49437 1.67701 3.50114 1.67701ZM10.5011 2.9895L3.50114 2.98951C3.21924 2.98951 2.99072 3.21803 2.99072 3.49993V9.35414C2.99072 9.63601 3.21926 9.86455 3.50114 9.86455H5.04675C5.33794 9.86455 5.61984 9.96705 5.84302 10.1541L7.00112 11.1249L8.17831 10.1496C8.40069 9.96537 8.68041 9.86455 8.96916 9.86455H10.5011C10.5011 9.86455 10.5011 9.86455 10.5011 9.86455C10.783 9.8645 11.0116 9.63592 11.0116 9.35414V3.49992C11.0116 3.21806 10.7831 2.9895 10.5011 2.9895ZM9.06809 4.93171C9.32437 5.18799 9.32437 5.60351 9.06809 5.85979L7.02642 7.90146C6.77014 8.15774 6.35464 8.15774 6.09835 7.90146L5.22333 7.02646C4.96704 6.77019 4.96704 6.35467 5.22332 6.09839C5.4796 5.8421 5.89511 5.8421 6.15139 6.09837L6.56238 6.50935L8.14001 4.93171C8.3963 4.67543 8.81181 4.67543 9.06809 4.93171Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Answer"
|
||||
}
|
||||
16
app/components/base/icons/workflow/Answer.tsx
Normal file
16
app/components/base/icons/workflow/Answer.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Answer.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 = 'Answer'
|
||||
|
||||
export default Icon
|
||||
38
app/components/base/icons/workflow/Code.json
Normal file
38
app/components/base/icons/workflow/Code.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"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": "icons/code"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Vector (Stroke)",
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M8.32593 1.69675C8.67754 1.78466 8.89132 2.14096 8.80342 2.49257L6.47009 11.8259C6.38218 12.1775 6.02588 12.3913 5.67427 12.3034C5.32265 12.2155 5.10887 11.8592 5.19678 11.5076L7.53011 2.17424C7.61801 1.82263 7.97431 1.60885 8.32593 1.69675ZM3.96414 4.20273C4.22042 4.45901 4.22042 4.87453 3.96413 5.13081L2.45578 6.63914C2.45577 6.63915 2.45578 6.63914 2.45578 6.63914C2.25645 6.83851 2.25643 7.16168 2.45575 7.36103C2.45574 7.36103 2.45576 7.36104 2.45575 7.36103L3.96413 8.86936C4.22041 9.12564 4.22042 9.54115 3.96414 9.79744C3.70787 10.0537 3.29235 10.0537 3.03607 9.79745L1.52769 8.28913C0.815811 7.57721 0.815803 6.42302 1.52766 5.7111L3.03606 4.20272C3.29234 3.94644 3.70786 3.94644 3.96414 4.20273ZM10.0361 4.20273C10.2923 3.94644 10.7078 3.94644 10.9641 4.20272L12.4725 5.71108C13.1843 6.423 13.1844 7.57717 12.4725 8.28909L10.9641 9.79745C10.7078 10.0537 10.2923 10.0537 10.036 9.79744C9.77977 9.54115 9.77978 9.12564 10.0361 8.86936L11.5444 7.36107C11.7437 7.16172 11.7438 6.83854 11.5444 6.63917C11.5444 6.63915 11.5445 6.63918 11.5444 6.63917L10.0361 5.13081C9.77978 4.87453 9.77978 4.45901 10.0361 4.20273Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Code"
|
||||
}
|
||||
16
app/components/base/icons/workflow/Code.tsx
Normal file
16
app/components/base/icons/workflow/Code.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Code.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 = 'Code'
|
||||
|
||||
export default Icon
|
||||
38
app/components/base/icons/workflow/End.json
Normal file
38
app/components/base/icons/workflow/End.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"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": "icons/end"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Vector (Stroke)",
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M6.67315 1.18094C6.87691 1.0639 7.12769 1.06475 7.33067 1.18315L10.8307 3.22481C11.0323 3.34242 11.1562 3.55826 11.1562 3.79167C11.1562 4.02507 11.0323 4.24091 10.8307 4.35852L7.65625 6.21026V9.91667C7.65625 10.2791 7.36244 10.5729 7 10.5729C6.63756 10.5729 6.34375 10.2791 6.34375 9.91667V5.84577C6.34361 5.83788 6.34361 5.83 6.34375 5.82213V1.75C6.34375 1.51502 6.46939 1.29797 6.67315 1.18094ZM7.65625 4.69078L9.19758 3.79167L7.65625 2.89256V4.69078ZM5.31099 8.25466C5.37977 8.61051 5.14704 8.95473 4.79119 9.0235C3.97285 9.18165 3.32667 9.41764 2.90374 9.67762C2.45323 9.95454 2.40625 10.1564 2.40625 10.2086C2.40625 10.2448 2.42254 10.3508 2.60674 10.5202C2.79151 10.6901 3.09509 10.8732 3.52555 11.0406C4.38229 11.3738 5.61047 11.594 7 11.594C8.38954 11.594 9.61773 11.3738 10.4745 11.0406C10.9049 10.8732 11.2085 10.6901 11.3933 10.5202C11.5775 10.3508 11.5938 10.2448 11.5938 10.2086C11.5938 10.1564 11.5468 9.95454 11.0963 9.67762C10.6733 9.41764 10.0271 9.18165 9.20881 9.0235C8.85296 8.95473 8.62023 8.61051 8.68901 8.25465C8.75778 7.8988 9.102 7.66608 9.45786 7.73485C10.3682 7.91077 11.1803 8.18867 11.7836 8.55947C12.3592 8.91331 12.9062 9.45912 12.9062 10.2086C12.9062 10.7361 12.6287 11.1672 12.2816 11.4864C11.935 11.805 11.4698 12.0618 10.9502 12.2639C9.90679 12.6696 8.50997 12.9065 7 12.9065C5.49004 12.9065 4.09322 12.6696 3.04983 12.2639C2.53023 12.0618 2.06497 11.805 1.7184 11.4864C1.37128 11.1672 1.09375 10.7361 1.09375 10.2086C1.09375 9.45913 1.64077 8.91332 2.21642 8.55947C2.81966 8.18867 3.63181 7.91077 4.54215 7.73485C4.898 7.66608 5.24222 7.8988 5.31099 8.25466Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "End"
|
||||
}
|
||||
16
app/components/base/icons/workflow/End.tsx
Normal file
16
app/components/base/icons/workflow/End.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './End.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 = 'End'
|
||||
|
||||
export default Icon
|
||||
38
app/components/base/icons/workflow/Home.json
Normal file
38
app/components/base/icons/workflow/Home.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"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": "icons/home"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Icon (Stroke)",
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M6.99999 2.44562C6.97241 2.46663 6.94086 2.49116 6.90151 2.52177L3.43971 5.21428C3.17896 5.41708 3.15115 5.44593 3.13396 5.46918C3.10759 5.50483 3.08794 5.545 3.07599 5.58771C3.0682 5.61555 3.0625 5.65522 3.0625 5.98554V9.67837C3.0625 9.97506 3.06301 10.1581 3.07422 10.2954C3.08463 10.4228 3.10101 10.4541 3.10219 10.4563C3.13714 10.5249 3.19296 10.5808 3.26156 10.6157C3.2638 10.6169 3.29514 10.6333 3.42254 10.6437C3.55984 10.6549 3.74289 10.6555 4.03958 10.6555H4.8125V7.53462C4.8125 7.52831 4.81249 7.52199 4.81249 7.51565C4.81247 7.38933 4.81245 7.25834 4.82163 7.14594C4.8319 7.02025 4.85685 6.86124 4.93966 6.69872C5.05151 6.4792 5.22998 6.30072 5.44951 6.18886C5.61203 6.10605 5.77104 6.08111 5.89673 6.07084C6.00913 6.06166 6.14012 6.06168 6.26644 6.0617C6.27278 6.0617 6.2791 6.06171 6.28541 6.06171H7.71458C7.72089 6.06171 7.72721 6.0617 7.73355 6.0617C7.85987 6.06168 7.99086 6.06166 8.10326 6.07084C8.22896 6.08111 8.38796 6.10605 8.55049 6.18886C8.77001 6.30072 8.94849 6.4792 9.06034 6.69872C9.14315 6.86124 9.16809 7.02025 9.17836 7.14594C9.18755 7.25834 9.18752 7.38933 9.1875 7.51565C9.1875 7.52199 9.1875 7.52831 9.1875 7.53462V10.6555H9.96041C10.2571 10.6555 10.4402 10.6549 10.5775 10.6437C10.7049 10.6333 10.7361 10.6169 10.7383 10.6158C10.8069 10.5808 10.8628 10.525 10.8978 10.4564C10.8989 10.4541 10.9154 10.4228 10.9258 10.2954C10.937 10.1581 10.9375 9.97506 10.9375 9.67837V5.98554C10.9375 5.65522 10.9318 5.61555 10.924 5.58771C10.912 5.545 10.8924 5.50483 10.866 5.46918C10.8488 5.44593 10.821 5.41708 10.5603 5.21428L7.09848 2.52177C7.05913 2.49116 7.02757 2.46663 6.99999 2.44562ZM9.98433 11.968C10.2497 11.968 10.4871 11.968 10.6843 11.9519C10.8951 11.9346 11.1172 11.8958 11.3343 11.7852C11.6499 11.6244 11.9064 11.3678 12.0672 11.0523C12.1778 10.8351 12.2167 10.6131 12.2339 10.4023C12.25 10.205 12.25 9.96764 12.25 9.70225L12.25 5.98554C12.25 5.9671 12.25 5.94866 12.2501 5.93025C12.2504 5.69307 12.2508 5.45861 12.1879 5.23392C12.1329 5.03748 12.0426 4.85272 11.9213 4.68871C11.7825 4.50112 11.5972 4.35747 11.4098 4.21216C11.3952 4.20087 11.3806 4.18958 11.3661 4.17826L7.90428 1.48574C7.89214 1.4763 7.87933 1.46621 7.86587 1.4556C7.73357 1.35131 7.53852 1.19755 7.3049 1.1343C7.10523 1.08023 6.89477 1.08023 6.69509 1.1343C6.46148 1.19755 6.26642 1.35131 6.13412 1.4556C6.12066 1.46621 6.10785 1.4763 6.09571 1.48574L2.63391 4.17826C2.61935 4.18958 2.60478 4.20088 2.59022 4.21216C2.40278 4.35747 2.21747 4.50112 2.07873 4.68871C1.95742 4.85271 1.86706 5.03748 1.81207 5.23392C1.74918 5.4586 1.74956 5.69307 1.74994 5.93024C1.74997 5.94866 1.75 5.96709 1.75 5.98554L1.75 9.70227C1.74998 9.96765 1.74997 10.205 1.76608 10.4023C1.78331 10.6131 1.82216 10.8351 1.93279 11.0523C2.09357 11.3678 2.35014 11.6244 2.6657 11.7852C2.88282 11.8958 3.10485 11.9346 3.31566 11.9519C3.5129 11.968 3.75029 11.968 4.01566 11.968H9.98433ZM7.875 10.6555V7.53462C7.875 7.47093 7.87498 7.41945 7.87447 7.37473C7.82975 7.37422 7.77828 7.37421 7.71458 7.37421H6.28541C6.22172 7.37421 6.17024 7.37422 6.12553 7.37473C6.12501 7.41945 6.125 7.47093 6.125 7.53462V10.6555H7.875Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Home"
|
||||
}
|
||||
16
app/components/base/icons/workflow/Home.tsx
Normal file
16
app/components/base/icons/workflow/Home.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Home.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 = 'Home'
|
||||
|
||||
export default Icon
|
||||
71
app/components/base/icons/workflow/Http.json
Normal file
71
app/components/base/icons/workflow/Http.json
Normal file
@ -0,0 +1,71 @@
|
||||
{
|
||||
"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": "icons/http"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "Vector"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M13.0968 4.66675H10.8387V9.18288H11.7419V7.82804H13.0968C13.3362 7.82772 13.5658 7.73245 13.7351 7.56313C13.9044 7.39382 13.9997 7.16426 14 6.92481V5.56997C13.9997 5.33051 13.9045 5.10093 13.7351 4.9316C13.5658 4.76227 13.3362 4.66702 13.0968 4.66675ZM11.7419 6.92481V5.56997H13.0968L13.0972 6.92481H11.7419Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M4.06452 5.56997H4.96774V9.18288H5.87097V5.56997H6.77419V4.66675H4.06452V5.56997Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M9.93548 4.66675H7.22581V5.56997H8.12903V9.18288H9.03226V5.56997H9.93548V4.66675Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M2.25806 4.66675V6.4732H0.903226V4.66675H0V9.18288H0.903226V7.37643H2.25806V9.18288H3.16129V4.66675H2.25806Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Http"
|
||||
}
|
||||
16
app/components/base/icons/workflow/Http.tsx
Normal file
16
app/components/base/icons/workflow/Http.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Http.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 = 'Http'
|
||||
|
||||
export default Icon
|
||||
38
app/components/base/icons/workflow/IfElse.json
Normal file
38
app/components/base/icons/workflow/IfElse.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"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": "icons/if-else"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Vector (Stroke)",
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M8.16667 2.98975C7.80423 2.98975 7.51042 2.69593 7.51042 2.3335C7.51042 1.97106 7.80423 1.67725 8.16667 1.67725H11.0833C11.4458 1.67725 11.7396 1.97106 11.7396 2.3335V5.25016C11.7396 5.6126 11.4458 5.90641 11.0833 5.90641C10.7209 5.90641 10.4271 5.6126 10.4271 5.25016V3.91782L7.34474 7.00016L10.4271 10.0825V8.75016C10.4271 8.38773 10.7209 8.09391 11.0833 8.09391C11.4458 8.09391 11.7396 8.38773 11.7396 8.75016V11.6668C11.7396 12.0293 11.4458 12.3231 11.0833 12.3231H8.16667C7.80423 12.3231 7.51042 12.0293 7.51042 11.6668C7.51042 11.3044 7.80423 11.0106 8.16667 11.0106H9.49901L6.14484 7.65641H1.75C1.38756 7.65641 1.09375 7.3626 1.09375 7.00016C1.09375 6.63773 1.38756 6.34391 1.75 6.34391H6.14484L9.49901 2.98975H8.16667Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "IfElse"
|
||||
}
|
||||
16
app/components/base/icons/workflow/IfElse.tsx
Normal file
16
app/components/base/icons/workflow/IfElse.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './IfElse.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 = 'IfElse'
|
||||
|
||||
export default Icon
|
||||
38
app/components/base/icons/workflow/KnowledgeRetrieval.json
Normal file
38
app/components/base/icons/workflow/KnowledgeRetrieval.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"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": "icons/knowledge-retrieval"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Vector (Stroke)",
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M3.78528 2.62834C3.78527 2.62834 3.78528 2.62834 3.78528 2.62834L8 3.56494L12.2147 2.62834C13.5158 2.33921 14.75 3.32924 14.75 4.66206V11.2637C14.75 12.2401 14.0718 13.0855 13.1187 13.2974L8.1627 14.3987C8.05554 14.4225 7.94446 14.4225 7.8373 14.3987L2.88139 13.2974C1.92824 13.0855 1.25 12.2401 1.25 11.2637V4.66206C1.25 3.32925 2.4842 2.33921 3.78528 2.62834ZM7.25 4.93487L3.45988 4.09262C3.09558 4.01166 2.75 4.28887 2.75 4.66206V11.2637C2.75 11.537 2.93986 11.7738 3.20679 11.8331C3.20678 11.8331 3.20681 11.8331 3.20679 11.8331L7.25 12.7316V4.93487ZM8.75 12.7316L12.7932 11.8331C13.0601 11.7738 13.25 11.537 13.25 11.2637V4.66206C13.25 4.28887 12.9044 4.01165 12.5401 4.09262L8.75 4.93487V12.7316Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "KnowledgeRetrieval"
|
||||
}
|
||||
16
app/components/base/icons/workflow/KnowledgeRetrieval.tsx
Normal file
16
app/components/base/icons/workflow/KnowledgeRetrieval.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './KnowledgeRetrieval.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 = 'KnowledgeRetrieval'
|
||||
|
||||
export default Icon
|
||||
38
app/components/base/icons/workflow/Llm.json
Normal file
38
app/components/base/icons/workflow/Llm.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"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": "icons/llm"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Vector (Stroke)",
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M5.83333 2.40625C5.04971 2.40625 4.39011 2.94431 4.20689 3.67206C4.13982 3.93846 3.91391 4.1349 3.64078 4.16432C2.94692 4.23906 2.40625 4.82766 2.40625 5.54167C2.40625 5.92943 2.56471 6.27904 2.82212 6.53129C2.94807 6.65472 3.01905 6.82365 3.01905 7C3.01905 7.17635 2.94807 7.34528 2.82212 7.46871C2.56471 7.72096 2.40625 8.07057 2.40625 8.45833C2.40625 9.03652 2.76061 9.53347 3.26651 9.74092C3.45247 9.81717 3.59324 9.97444 3.64849 10.1677C3.8841 10.9917 4.64342 11.5938 5.54167 11.5938C5.82802 11.5938 6.09916 11.533 6.34375 11.4237V9.91667C6.34375 9.31258 5.85409 8.82292 5.25 8.82292C4.88756 8.82292 4.59375 8.5291 4.59375 8.16667C4.59375 7.80423 4.88756 7.51042 5.25 7.51042C5.64385 7.51042 6.0156 7.60503 6.34375 7.77278V2.48514C6.18319 2.43393 6.01183 2.40625 5.83333 2.40625ZM7.65625 2.48514V4.08333C7.65625 4.6874 8.14592 5.17708 8.75 5.17708C9.11244 5.17708 9.40625 5.4709 9.40625 5.83333C9.40625 6.19577 9.11244 6.48958 8.75 6.48958C8.35615 6.48958 7.9844 6.39496 7.65625 6.22722V11.4237C7.90087 11.533 8.17199 11.5938 8.45833 11.5938C9.35657 11.5938 10.1159 10.9917 10.3515 10.1677C10.4068 9.97444 10.5475 9.81717 10.7335 9.74092C11.2394 9.53347 11.5938 9.03652 11.5938 8.45833C11.5938 8.07056 11.4353 7.72096 11.1779 7.46871C11.0519 7.34528 10.981 7.17635 10.981 7C10.981 6.82365 11.0519 6.65472 11.1779 6.53129C11.4353 6.27904 11.5938 5.92944 11.5938 5.54167C11.5938 4.82766 11.0531 4.23906 10.3592 4.16432C10.0861 4.1349 9.86022 3.93847 9.79315 3.67208C9.6099 2.94432 8.95027 2.40625 8.16667 2.40625C7.98817 2.40625 7.81681 2.43393 7.65625 2.48514ZM7.00001 12.565C6.56031 12.7835 6.06472 12.9062 5.54167 12.9062C4.14996 12.9062 2.96198 12.0403 2.48457 10.8188C1.65595 10.3591 1.09375 9.47501 1.09375 8.45833C1.09375 7.9213 1.2511 7.42042 1.52161 7C1.2511 6.57958 1.09375 6.0787 1.09375 5.54167C1.09375 4.30153 1.93005 3.25742 3.06973 2.94157C3.51828 1.85715 4.586 1.09375 5.83333 1.09375C6.24643 1.09375 6.64104 1.17788 7 1.33013C7.35896 1.17788 7.75357 1.09375 8.16667 1.09375C9.41399 1.09375 10.4817 1.85716 10.9303 2.94157C12.0699 3.25742 12.9062 4.30153 12.9062 5.54167C12.9062 6.07869 12.7489 6.57958 12.4784 7C12.7489 7.42043 12.9062 7.92131 12.9062 8.45833C12.9062 9.47502 12.344 10.3591 11.5154 10.8188C11.038 12.0403 9.85003 12.9062 8.45833 12.9062C7.93526 12.9062 7.4397 12.7834 7.00001 12.565Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Llm"
|
||||
}
|
||||
16
app/components/base/icons/workflow/Llm.tsx
Normal file
16
app/components/base/icons/workflow/Llm.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Llm.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 = 'Llm'
|
||||
|
||||
export default Icon
|
||||
38
app/components/base/icons/workflow/QuestionClassifier.json
Normal file
38
app/components/base/icons/workflow/QuestionClassifier.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"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": "icons/question-classifier"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Vector (Stroke)",
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M6.34379 3.53597C6.34379 2.35003 7.45832 1.47985 8.60885 1.76749L10.9422 2.35082C11.7537 2.55369 12.323 3.28283 12.323 4.1193V9.88081C12.323 10.7173 11.7537 11.4464 10.9422 11.6493L8.60886 12.2326C7.45832 12.5203 6.34379 11.6501 6.34379 10.4641V3.53597ZM8.29052 3.0408C7.96836 2.96026 7.65629 3.20392 7.65629 3.53597V10.4641C7.65629 10.7962 7.96836 11.0399 8.29051 10.9593L10.6238 10.376C10.6238 10.376 10.6238 10.376 10.6238 10.376C10.8511 10.3192 11.0105 10.115 11.0105 9.88081V4.1193C11.0105 3.88509 10.851 3.68093 10.6239 3.62413L8.29052 3.0408ZM4.66671 2.26048C5.02914 2.26048 5.32296 2.5543 5.32296 2.91673V11.0834C5.32296 11.4458 5.02914 11.7397 4.66671 11.7397C4.30427 11.7397 4.01046 11.4458 4.01046 11.0834V2.91673C4.01046 2.5543 4.30427 2.26048 4.66671 2.26048ZM2.33337 2.84382C2.69581 2.84382 2.98962 3.13763 2.98962 3.50007V10.5001C2.98962 10.8625 2.69581 11.1563 2.33337 11.1563C1.97094 11.1563 1.67712 10.8625 1.67712 10.5001V3.50007C1.67712 3.13763 1.97094 2.84382 2.33337 2.84382Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "QuestionClassifier"
|
||||
}
|
||||
16
app/components/base/icons/workflow/QuestionClassifier.tsx
Normal file
16
app/components/base/icons/workflow/QuestionClassifier.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './QuestionClassifier.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 = 'QuestionClassifier'
|
||||
|
||||
export default Icon
|
||||
154
app/components/base/icons/workflow/TemplatingTransform.json
Normal file
154
app/components/base/icons/workflow/TemplatingTransform.json
Normal file
@ -0,0 +1,154 @@
|
||||
{
|
||||
"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": "icons/templating-transform"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "Vector"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M6.34375 1.75C6.34375 1.38756 6.63756 1.09375 7 1.09375C10.262 1.09375 12.9062 3.73807 12.9062 7C12.9062 10.262 10.262 12.9062 7 12.9062C6.63756 12.9062 6.34375 12.6124 6.34375 12.25V1.75Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M5.54167 3.64583C5.54167 3.968 5.2805 4.22917 4.95833 4.22917C4.63617 4.22917 4.375 3.968 4.375 3.64583C4.375 3.32367 4.63617 3.0625 4.95833 3.0625C5.2805 3.0625 5.54167 3.32367 5.54167 3.64583Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M3.5 3.64583C3.5 3.968 3.23883 4.22917 2.91667 4.22917C2.5945 4.22917 2.33333 3.968 2.33333 3.64583C2.33333 3.32367 2.5945 3.0625 2.91667 3.0625C3.23883 3.0625 3.5 3.32367 3.5 3.64583Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M5.54167 10.3542C5.54167 10.6763 5.2805 10.9375 4.95833 10.9375C4.63617 10.9375 4.375 10.6763 4.375 10.3542C4.375 10.032 4.63617 9.77083 4.95833 9.77083C5.2805 9.77083 5.54167 10.032 5.54167 10.3542Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M5.39583 1.89583C5.39583 2.13746 5.19996 2.33333 4.95833 2.33333C4.71671 2.33333 4.52083 2.13746 4.52083 1.89583C4.52083 1.65421 4.71671 1.45833 4.95833 1.45833C5.19996 1.45833 5.39583 1.65421 5.39583 1.89583Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M1.75 5.83333C1.75 6.07495 1.55412 6.27083 1.3125 6.27083C1.07088 6.27083 0.875 6.07495 0.875 5.83333C0.875 5.59171 1.07088 5.39583 1.3125 5.39583C1.55412 5.39583 1.75 5.59171 1.75 5.83333Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M1.75 8.16667C1.75 8.40828 1.55412 8.60417 1.3125 8.60417C1.07088 8.60417 0.875 8.40828 0.875 8.16667C0.875 7.92505 1.07088 7.72917 1.3125 7.72917C1.55412 7.72917 1.75 7.92505 1.75 8.16667Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M5.39583 12.1042C5.39583 12.3458 5.19996 12.5417 4.95833 12.5417C4.71671 12.5417 4.52083 12.3458 4.52083 12.1042C4.52083 11.8625 4.71671 11.6667 4.95833 11.6667C5.19996 11.6667 5.39583 11.8625 5.39583 12.1042Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M5.83333 5.83333C5.83333 6.31657 5.44158 6.70833 4.95833 6.70833C4.47508 6.70833 4.08333 6.31657 4.08333 5.83333C4.08333 5.35008 4.47508 4.95833 4.95833 4.95833C5.44158 4.95833 5.83333 5.35008 5.83333 5.83333Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M5.83333 8.16667C5.83333 8.6499 5.44158 9.04167 4.95833 9.04167C4.47508 9.04167 4.08333 8.6499 4.08333 8.16667C4.08333 7.68343 4.47508 7.29167 4.95833 7.29167C5.44158 7.29167 5.83333 7.68343 5.83333 8.16667Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M3.5 5.83333C3.5 6.15551 3.23883 6.41667 2.91667 6.41667C2.5945 6.41667 2.33333 6.15551 2.33333 5.83333C2.33333 5.51117 2.5945 5.25 2.91667 5.25C3.23883 5.25 3.5 5.51117 3.5 5.83333Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M3.5 8.16667C3.5 8.48884 3.23883 8.75 2.91667 8.75C2.5945 8.75 2.33333 8.48884 2.33333 8.16667C2.33333 7.84449 2.5945 7.58333 2.91667 7.58333C3.23883 7.58333 3.5 7.84449 3.5 8.16667Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M3.5 10.3542C3.5 10.6763 3.23883 10.9375 2.91667 10.9375C2.5945 10.9375 2.33333 10.6763 2.33333 10.3542C2.33333 10.032 2.5945 9.77083 2.91667 9.77083C3.23883 9.77083 3.5 10.032 3.5 10.3542Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "TemplatingTransform"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user