From 8f02afed98d32d23aade0672c92042a50fb758c3 Mon Sep 17 00:00:00 2001 From: Eric-Guo Date: Mon, 8 Sep 2025 11:31:25 +0800 Subject: [PATCH 1/4] Bump some outdated package version --- package.json | 72 ++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/package.json b/package.json index e7e20b5..a460bbe 100644 --- a/package.json +++ b/package.json @@ -12,73 +12,73 @@ "prepare": "husky install ./.husky" }, "dependencies": { - "@floating-ui/react": "^0.26.2", - "@formatjs/intl-localematcher": "^0.2.32", - "@headlessui/react": "^1.7.13", + "@floating-ui/react": "^0.26.25", + "@formatjs/intl-localematcher": "^0.5.6", + "@headlessui/react": "2.2.1", "@heroicons/react": "^2.0.16", - "@mdx-js/loader": "^2.3.0", - "@mdx-js/react": "^2.3.0", + "@mdx-js/loader": "^3.1.0", + "@mdx-js/react": "^3.1.0", "@monaco-editor/react": "^4.6.0", "@remixicon/react": "^4.6.0", "@tailwindcss/line-clamp": "^0.4.2", - "@types/node": "18.15.0", - "@types/react": "18.0.28", - "@types/react-dom": "18.0.11", + "@types/node": "~18.19.0", + "@types/react": "~18.3.23", + "@types/react-dom": "~18.3.7", "@types/react-syntax-highlighter": "^15.5.6", - "ahooks": "^3.7.5", + "ahooks": "^3.8.4", "axios": "^1.3.5", "class-variance-authority": "^0.7.1", - "classnames": "^2.3.2", + "classnames": "^2.5.1", "copy-to-clipboard": "^3.3.3", "dify-client": "^2.3.1", - "eslint": "8.36.0", - "eslint-config-next": "13.4.0", + "eslint": "~8.57.1", + "eslint-config-next": "~14.2.32", "eventsource-parser": "^1.0.0", - "husky": "^8.0.3", - "i18next": "^22.4.13", - "i18next-resources-to-backend": "^1.1.3", + "husky": "^9.1.7", + "i18next": "^23.16.4", + "i18next-resources-to-backend": "^1.2.1", "immer": "^9.0.19", "js-cookie": "^3.0.1", - "katex": "^0.16.7", + "katex": "^0.16.21", "lodash-es": "^4.17.21", "mime": "^4.0.7", "negotiator": "^0.6.3", - "next": "^14.0.4", + "next": "^14.2.32", "rc-textarea": "^1.5.3", - "react": "18.2.0", - "react-dom": "18.2.0", + "react": "~18.3.0", + "react-dom": "~18.3.0", "react-error-boundary": "^4.0.2", "react-headless-pagination": "^1.1.4", "react-i18next": "^12.2.0", "react-markdown": "^8.0.6", "react-syntax-highlighter": "^15.5.0", - "react-tooltip": "5.8.3", - "rehype-katex": "^6.0.2", - "remark-breaks": "^3.0.2", - "remark-gfm": "^3.0.1", - "remark-math": "^5.1.1", + "react-tooltip": "~5.8.3", + "rehype-katex": "^7.0.1", + "remark-breaks": "^4.0.0", + "remark-gfm": "^4.0.0", + "remark-math": "^6.0.0", "sass": "^1.61.0", "scheduler": "^0.23.0", "server-only": "^0.0.1", - "swr": "^2.1.0", + "swr": "^2.3.0", "tailwind-merge": "^3.2.0", "typescript": "4.9.5", - "use-context-selector": "^1.4.1", - "uuid": "^9.0.0", + "use-context-selector": "^2.0.0", + "uuid": "^10.0.0", "zustand": "^4.5.2" }, "devDependencies": { - "@antfu/eslint-config": "0.36.0", - "@faker-js/faker": "^7.6.0", + "@antfu/eslint-config": "~5.2.2", + "@faker-js/faker": "^9.0.3", "@tailwindcss/typography": "^0.5.9", - "@types/js-cookie": "^3.0.3", + "@types/js-cookie": "^3.0.6", "@types/lodash-es": "^4.17.12", - "@types/negotiator": "^0.6.1", - "autoprefixer": "^10.4.14", - "eslint-plugin-react-hooks": "^4.6.0", - "lint-staged": "^13.2.2", - "postcss": "^8.4.21", - "tailwindcss": "^3.2.7" + "@types/negotiator": "^0.6.3", + "autoprefixer": "^10.4.20", + "eslint-plugin-react-hooks": "^5.1.0", + "lint-staged": "^15.2.10", + "postcss": "^8.4.47", + "tailwindcss": "^3.4.14" }, "lint-staged": { "**/*.js?(x)": [ From 74a656fda2adce37294e9155cc9b110a1a6187c9 Mon Sep 17 00:00:00 2001 From: Eric-Guo Date: Mon, 8 Sep 2025 14:51:22 +0800 Subject: [PATCH 2/4] Initial GPT-5-high generated cursor rules --- .cursor/rules/api-client.mdc | 16 ++++++++++++++++ .cursor/rules/i18n.mdc | 10 ++++++++++ .cursor/rules/project-structure.mdc | 21 +++++++++++++++++++++ .cursor/rules/typescript-react.mdc | 20 ++++++++++++++++++++ .cursor/rules/ui-components.mdc | 11 +++++++++++ 5 files changed, 78 insertions(+) create mode 100644 .cursor/rules/api-client.mdc create mode 100644 .cursor/rules/i18n.mdc create mode 100644 .cursor/rules/project-structure.mdc create mode 100644 .cursor/rules/typescript-react.mdc create mode 100644 .cursor/rules/ui-components.mdc diff --git a/.cursor/rules/api-client.mdc b/.cursor/rules/api-client.mdc new file mode 100644 index 0000000..7b7c91c --- /dev/null +++ b/.cursor/rules/api-client.mdc @@ -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. diff --git a/.cursor/rules/i18n.mdc b/.cursor/rules/i18n.mdc new file mode 100644 index 0000000..900b06b --- /dev/null +++ b/.cursor/rules/i18n.mdc @@ -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 `` using the resolved locale in `[app/layout.tsx](mdc:app/layout.tsx)`. diff --git a/.cursor/rules/project-structure.mdc b/.cursor/rules/project-structure.mdc new file mode 100644 index 0000000..873f454 --- /dev/null +++ b/.cursor/rules/project-structure.mdc @@ -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)` diff --git a/.cursor/rules/typescript-react.mdc b/.cursor/rules/typescript-react.mdc new file mode 100644 index 0000000..7959f71 --- /dev/null +++ b/.cursor/rules/typescript-react.mdc @@ -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. diff --git a/.cursor/rules/ui-components.mdc b/.cursor/rules/ui-components.mdc new file mode 100644 index 0000000..cdce641 --- /dev/null +++ b/.cursor/rules/ui-components.mdc @@ -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). From e91a1f6194f598e2b367fe4f468833584f3cfbb1 Mon Sep 17 00:00:00 2001 From: Eric-Guo Date: Mon, 8 Sep 2025 14:56:12 +0800 Subject: [PATCH 3/4] @tailwindcss/line-clamp include by default. --- package.json | 1 - tailwind.config.js | 1 - 2 files changed, 2 deletions(-) diff --git a/package.json b/package.json index a460bbe..cf945aa 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,6 @@ "@mdx-js/react": "^3.1.0", "@monaco-editor/react": "^4.6.0", "@remixicon/react": "^4.6.0", - "@tailwindcss/line-clamp": "^0.4.2", "@types/node": "~18.19.0", "@types/react": "~18.3.23", "@types/react-dom": "~18.3.7", diff --git a/tailwind.config.js b/tailwind.config.js index ac78da3..f8e30d5 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -61,6 +61,5 @@ module.exports = { }, plugins: [ require('@tailwindcss/typography'), - require('@tailwindcss/line-clamp'), ], } From 17007b201475ca525417d19b64771596645df2f8 Mon Sep 17 00:00:00 2001 From: Eric-Guo Date: Mon, 8 Sep 2025 16:15:46 +0800 Subject: [PATCH 4/4] Allow file upload in chat. --- app/components/chat/index.tsx | 27 +++++++++++++++++++++++++-- app/components/index.tsx | 17 +++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/app/components/chat/index.tsx b/app/components/chat/index.tsx index 0f95615..fc67d85 100644 --- a/app/components/chat/index.tsx +++ b/app/components/chat/index.tsx @@ -15,6 +15,9 @@ import Toast from '@/app/components/base/toast' import ChatImageUploader from '@/app/components/base/image-uploader/chat-image-uploader' import ImageList from '@/app/components/base/image-uploader/image-list' import { useImageFiles } from '@/app/components/base/image-uploader/hooks' +import FileUploaderInAttachmentWrapper from '@/app/components/base/file-uploader-in-attachment' +import type { FileEntity, FileUpload } from '@/app/components/base/file-uploader-in-attachment/types' +import { getProcessedFiles } from '@/app/components/base/file-uploader-in-attachment/utils' export type IChatProps = { chatList: ChatItem[] @@ -33,6 +36,7 @@ export type IChatProps = { isResponding?: boolean controlClearQuery?: number visionConfig?: VisionSettings + fileConfig?: FileUpload } const Chat: FC = ({ @@ -46,6 +50,7 @@ const Chat: FC = ({ isResponding, controlClearQuery, visionConfig, + fileConfig, }) => { const { t } = useTranslation() const { notify } = Toast @@ -89,15 +94,20 @@ const Chat: FC = ({ onClear, } = useImageFiles() + const [attachmentFiles, setAttachmentFiles] = React.useState([]) + const handleSend = () => { if (!valid() || (checkCanSend && !checkCanSend())) return - onSend(queryRef.current, files.filter(file => file.progress !== -1).map(fileItem => ({ + const imageFiles: VisionFile[] = files.filter(file => file.progress !== -1).map(fileItem => ({ type: 'image', transfer_method: fileItem.type, url: fileItem.url, upload_file_id: fileItem.fileId, - }))) + })) + const docAndOtherFiles: VisionFile[] = getProcessedFiles(attachmentFiles) + const combinedFiles: VisionFile[] = [...imageFiles, ...docAndOtherFiles] + onSend(queryRef.current, combinedFiles) if (!files.find(item => item.type === TransferMethod.local_file && !item.fileId)) { if (files.length) onClear() @@ -106,6 +116,8 @@ const Chat: FC = ({ queryRef.current = '' } } + if (!attachmentFiles.find(item => item.transferMethod === TransferMethod.local_file && !item.uploadedId)) + setAttachmentFiles([]) } const handleKeyUp = (e: any) => { @@ -187,6 +199,17 @@ const Chat: FC = ({ ) } + { + fileConfig?.enabled && ( +
+ +
+ ) + }