From d951ac32c2dfac41fd294efba7122cda313da35e Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Fri, 4 Jul 2025 13:39:58 +0800 Subject: [PATCH] feat: load prompts from database (#17) * feat: load prompts from database * chore: bump editor version * fix: dropdown disable state * fix: revert dropdown menu changes * chore: bump ai chat * fix: duplicated fields not immediately usable * chore: bump ai chat version * chore: move hook usage upwards * chore: export type --- package.json | 4 +- pnpm-lock.yaml | 297 +++++++++++++++++- src/application/database-yjs/context.ts | 4 + src/application/types.ts | 41 +++ src/components/ai-chat/AIChat.tsx | 7 +- src/components/app/app.hooks.tsx | 212 +++++++++++++ src/components/app/header/MoreActions.tsx | 58 ++-- .../database-row/DatabaseRowSubDocument.tsx | 1 + src/components/editor/CollaborativeEditor.tsx | 40 +-- src/components/editor/Editor.tsx | 5 +- src/components/editor/EditorContext.tsx | 38 ++- src/components/editor/EditorOverlay.tsx | 8 +- src/components/ui/dropdown-menu.tsx | 1 + 13 files changed, 626 insertions(+), 90 deletions(-) diff --git a/package.json b/package.json index 8afe09cf..88c0b5ff 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,8 @@ "generate-tokens": "node scripts/system-token/convert-tokens.cjs" }, "dependencies": { - "@appflowyinc/ai-chat": "0.1.30", - "@appflowyinc/editor": "^0.1.10", + "@appflowyinc/ai-chat": "0.1.33", + "@appflowyinc/editor": "^0.1.13", "@atlaskit/pragmatic-drag-and-drop": "1.5.2", "@atlaskit/pragmatic-drag-and-drop-auto-scroll": "2.1.0", "@atlaskit/pragmatic-drag-and-drop-flourish": "2.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a1100489..689b81a4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,11 +9,11 @@ importers: .: dependencies: '@appflowyinc/ai-chat': - specifier: 0.1.30 - version: 0.1.30(@appflowyinc/editor@0.1.10(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(i18next-resources-to-backend@1.2.1)(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-i18next@14.1.3(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(slate-history@0.100.0(slate@0.101.5))(slate-react@0.101.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.101.5))(slate@0.101.5)(ts-node@10.9.2(@types/node@20.17.47)(typescript@4.9.5)))(@emotion/is-prop-valid@1.3.1)(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(axios@1.9.0)(dompurify@3.2.5)(i18next-resources-to-backend@1.2.1)(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-i18next@14.1.3(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@types/node@20.17.47)(typescript@4.9.5)) + specifier: 0.1.33 + version: 0.1.33(@appflowyinc/editor@0.1.13(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(i18next-resources-to-backend@1.2.1)(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-i18next@14.1.3(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(slate-history@0.100.0(slate@0.101.5))(slate-react@0.101.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.101.5))(slate@0.101.5)(ts-node@10.9.2(@types/node@20.17.47)(typescript@4.9.5)))(@emotion/is-prop-valid@1.3.1)(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(axios@1.9.0)(dompurify@3.2.5)(i18next-resources-to-backend@1.2.1)(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-i18next@14.1.3(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@types/node@20.17.47)(typescript@4.9.5)) '@appflowyinc/editor': - specifier: ^0.1.10 - version: 0.1.10(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(i18next-resources-to-backend@1.2.1)(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-i18next@14.1.3(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(slate-history@0.100.0(slate@0.101.5))(slate-react@0.101.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.101.5))(slate@0.101.5)(ts-node@10.9.2(@types/node@20.17.47)(typescript@4.9.5)) + specifier: ^0.1.13 + version: 0.1.13(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(i18next-resources-to-backend@1.2.1)(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-i18next@14.1.3(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(slate-history@0.100.0(slate@0.101.5))(slate-react@0.101.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.101.5))(slate@0.101.5)(ts-node@10.9.2(@types/node@20.17.47)(typescript@4.9.5)) '@atlaskit/pragmatic-drag-and-drop': specifier: 1.5.2 version: 1.5.2 @@ -632,10 +632,10 @@ packages: '@antfu/utils@8.1.1': resolution: {integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==} - '@appflowyinc/ai-chat@0.1.30': - resolution: {integrity: sha512-hSZk/q4g7pOWS3oAA8np+a2WMWmiQgEGUtLJGyj1hbUv6Y/rv4qHvvSz+yTzPOz9YFoypMgfryOGSaxMTr8F0w==} + '@appflowyinc/ai-chat@0.1.33': + resolution: {integrity: sha512-tLq49wxg5XUqc7S3TllH7nlRI7cLzsdF4EviioNkRWxOdIhoyenLQ2FEvAsY2UiP7qtzR9ehm9lrmijOsf+2nA==} peerDependencies: - '@appflowyinc/editor': ^0.1.7 + '@appflowyinc/editor': ^0.1.13 axios: ^1.7.9 dompurify: ^3.2.4 i18next: ^22.4.10 @@ -644,8 +644,8 @@ packages: react-dom: ^18.2.0 react-i18next: ^14.1.0 - '@appflowyinc/editor@0.1.10': - resolution: {integrity: sha512-2/IcZ1FTIEhv9DzVdCu+9WvOnjSLENU3j08Uu2Pep52EsoYQpEHpdeKaYKqBpwESFGwI0vc2sOXFyKOel8UgTg==} + '@appflowyinc/editor@0.1.13': + resolution: {integrity: sha512-iMN+BgNF9bAXjrpqstakC4q/vsW4A37INBlATyEoC04dsMHvbKlZL8k/eA9DjHU6MEk+BijSpbN6oUTKlxIpdA==} peerDependencies: i18next: ^22.4.10 i18next-resources-to-backend: ^1.2.1 @@ -2328,6 +2328,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-arrow@1.1.7': + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-avatar@1.1.9': resolution: {integrity: sha512-10tQokfvZdFvnvDkcOJPjm2pWiP8A0R4T83MoD7tb15bC/k2GU7B1YBuzJi8lNQ8V1QqhP8ocNqp27ByZaNagQ==} peerDependencies: @@ -2398,6 +2411,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-dialog@1.1.14': + resolution: {integrity: sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-direction@1.1.1': resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} peerDependencies: @@ -2407,6 +2433,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-dismissable-layer@1.1.10': + resolution: {integrity: sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-dismissable-layer@1.1.9': resolution: {integrity: sha512-way197PiTvNp+WBP7svMJasHl+vibhWGQDb6Mgf5mhEWJkgb85z7Lfl9TUdkqpWsf8GRNmoopx9ZxCyDzmgRMQ==} peerDependencies: @@ -2433,6 +2472,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-dropdown-menu@2.1.15': + resolution: {integrity: sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-focus-guards@1.1.2': resolution: {integrity: sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==} peerDependencies: @@ -2455,6 +2507,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-focus-scope@1.1.7': + resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-id@1.1.1': resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} peerDependencies: @@ -2490,6 +2555,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-menu@2.1.15': + resolution: {integrity: sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-popover@1.1.13': resolution: {integrity: sha512-84uqQV3omKDR076izYgcha6gdpN8m3z6w/AeJ83MSBJYVG/AbOHdLjAgsPZkeC/kt+k64moXFCnio8BbqXszlw==} peerDependencies: @@ -2503,6 +2581,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-popover@1.1.14': + resolution: {integrity: sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-popper@1.2.6': resolution: {integrity: sha512-7iqXaOWIjDBfIG7aq8CUEeCSsQMLFdn7VEE8TaFz704DtEzpPHR7w/uuzRflvKgltqSAImgcmxQ7fFX3X7wasg==} peerDependencies: @@ -2516,6 +2607,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-popper@1.2.7': + resolution: {integrity: sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-portal@1.1.8': resolution: {integrity: sha512-hQsTUIn7p7fxCPvao/q6wpbxmCwgLrlz+nOrJgC+RwfZqWY/WN+UMqkXzrtKbPrF82P43eCTl3ekeKuyAQbFeg==} peerDependencies: @@ -2529,6 +2633,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-portal@1.1.9': + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-presence@1.1.4': resolution: {integrity: sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==} peerDependencies: @@ -8718,14 +8835,15 @@ snapshots: '@antfu/utils@8.1.1': {} - '@appflowyinc/ai-chat@0.1.30(@appflowyinc/editor@0.1.10(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(i18next-resources-to-backend@1.2.1)(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-i18next@14.1.3(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(slate-history@0.100.0(slate@0.101.5))(slate-react@0.101.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.101.5))(slate@0.101.5)(ts-node@10.9.2(@types/node@20.17.47)(typescript@4.9.5)))(@emotion/is-prop-valid@1.3.1)(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(axios@1.9.0)(dompurify@3.2.5)(i18next-resources-to-backend@1.2.1)(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-i18next@14.1.3(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@types/node@20.17.47)(typescript@4.9.5))': + '@appflowyinc/ai-chat@0.1.33(@appflowyinc/editor@0.1.13(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(i18next-resources-to-backend@1.2.1)(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-i18next@14.1.3(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(slate-history@0.100.0(slate@0.101.5))(slate-react@0.101.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.101.5))(slate@0.101.5)(ts-node@10.9.2(@types/node@20.17.47)(typescript@4.9.5)))(@emotion/is-prop-valid@1.3.1)(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(axios@1.9.0)(dompurify@3.2.5)(i18next-resources-to-backend@1.2.1)(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-i18next@14.1.3(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@types/node@20.17.47)(typescript@4.9.5))': dependencies: - '@appflowyinc/editor': 0.1.10(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(i18next-resources-to-backend@1.2.1)(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-i18next@14.1.3(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(slate-history@0.100.0(slate@0.101.5))(slate-react@0.101.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.101.5))(slate@0.101.5)(ts-node@10.9.2(@types/node@20.17.47)(typescript@4.9.5)) + '@appflowyinc/editor': 0.1.13(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(i18next-resources-to-backend@1.2.1)(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-i18next@14.1.3(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(slate-history@0.100.0(slate@0.101.5))(slate-react@0.101.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.101.5))(slate@0.101.5)(ts-node@10.9.2(@types/node@20.17.47)(typescript@4.9.5)) '@jest/globals': 29.7.0 '@radix-ui/react-avatar': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-dialog': 1.1.13(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-dialog': 1.1.14(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-dropdown-menu': 2.1.15(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-label': 2.1.6(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-popover': 1.1.13(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-popover': 1.1.14(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-radio-group': 1.3.6(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-select': 2.2.4(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-separator': 1.1.6(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -8761,13 +8879,13 @@ snapshots: - supports-color - ts-node - '@appflowyinc/editor@0.1.10(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(i18next-resources-to-backend@1.2.1)(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-i18next@14.1.3(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(slate-history@0.100.0(slate@0.101.5))(slate-react@0.101.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.101.5))(slate@0.101.5)(ts-node@10.9.2(@types/node@20.17.47)(typescript@4.9.5))': + '@appflowyinc/editor@0.1.13(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(i18next-resources-to-backend@1.2.1)(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-i18next@14.1.3(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(slate-history@0.100.0(slate@0.101.5))(slate-react@0.101.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.101.5))(slate@0.101.5)(ts-node@10.9.2(@types/node@20.17.47)(typescript@4.9.5))': dependencies: '@radix-ui/react-label': 2.1.6(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-popover': 1.1.13(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-popover': 1.1.14(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-select': 2.2.4(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-separator': 1.1.6(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.2.2(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.21)(react@18.3.1) '@radix-ui/react-switch': 1.2.4(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-tooltip': 1.2.6(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) class-variance-authority: 0.7.1 @@ -10843,6 +10961,15 @@ snapshots: '@types/react': 18.3.21 '@types/react-dom': 18.3.7(@types/react@18.3.21) + '@radix-ui/react-arrow@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.21 + '@types/react-dom': 18.3.7(@types/react@18.3.21) + '@radix-ui/react-avatar@1.1.9(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-context': 1.1.2(@types/react@18.3.21)(react@18.3.1) @@ -10914,12 +11041,47 @@ snapshots: '@types/react': 18.3.21 '@types/react-dom': 18.3.7(@types/react@18.3.21) + '@radix-ui/react-dialog@1.1.14(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.21)(react@18.3.1) + aria-hidden: 1.2.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.6.3(@types/react@18.3.21)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.21 + '@types/react-dom': 18.3.7(@types/react@18.3.21) + '@radix-ui/react-direction@1.1.1(@types/react@18.3.21)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: '@types/react': 18.3.21 + '@radix-ui/react-dismissable-layer@1.1.10(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@18.3.21)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.21 + '@types/react-dom': 18.3.7(@types/react@18.3.21) + '@radix-ui/react-dismissable-layer@1.1.9(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.2 @@ -10948,6 +11110,21 @@ snapshots: '@types/react': 18.3.21 '@types/react-dom': 18.3.7(@types/react@18.3.21) + '@radix-ui/react-dropdown-menu@2.1.15(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-menu': 2.1.15(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.21)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.21 + '@types/react-dom': 18.3.7(@types/react@18.3.21) + '@radix-ui/react-focus-guards@1.1.2(@types/react@18.3.21)(react@18.3.1)': dependencies: react: 18.3.1 @@ -10965,6 +11142,17 @@ snapshots: '@types/react': 18.3.21 '@types/react-dom': 18.3.7(@types/react@18.3.21) + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.21)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.21 + '@types/react-dom': 18.3.7(@types/react@18.3.21) + '@radix-ui/react-id@1.1.1(@types/react@18.3.21)(react@18.3.1)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.21)(react@18.3.1) @@ -11007,6 +11195,32 @@ snapshots: '@types/react': 18.3.21 '@types/react-dom': 18.3.7(@types/react@18.3.21) + '@radix-ui/react-menu@2.1.15(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.21)(react@18.3.1) + aria-hidden: 1.2.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.6.3(@types/react@18.3.21)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.21 + '@types/react-dom': 18.3.7(@types/react@18.3.21) + '@radix-ui/react-popover@1.1.13(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.2 @@ -11030,6 +11244,29 @@ snapshots: '@types/react': 18.3.21 '@types/react-dom': 18.3.7(@types/react@18.3.21) + '@radix-ui/react-popover@1.1.14(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.21)(react@18.3.1) + aria-hidden: 1.2.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.6.3(@types/react@18.3.21)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.21 + '@types/react-dom': 18.3.7(@types/react@18.3.21) + '@radix-ui/react-popper@1.2.6(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -11048,6 +11285,24 @@ snapshots: '@types/react': 18.3.21 '@types/react-dom': 18.3.7(@types/react@18.3.21) + '@radix-ui/react-popper@1.2.7(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-use-rect': 1.1.1(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.21)(react@18.3.1) + '@radix-ui/rect': 1.1.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.21 + '@types/react-dom': 18.3.7(@types/react@18.3.21) + '@radix-ui/react-portal@1.1.8(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-primitive': 2.1.2(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -11058,6 +11313,16 @@ snapshots: '@types/react': 18.3.21 '@types/react-dom': 18.3.7(@types/react@18.3.21) + '@radix-ui/react-portal@1.1.9(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.21)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.21 + '@types/react-dom': 18.3.7(@types/react@18.3.21) + '@radix-ui/react-presence@1.1.4(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.21)(react@18.3.1) diff --git a/src/application/database-yjs/context.ts b/src/application/database-yjs/context.ts index a8d109ae..4763e34d 100644 --- a/src/application/database-yjs/context.ts +++ b/src/application/database-yjs/context.ts @@ -7,9 +7,11 @@ import { DatabaseRelations, GenerateAISummaryRowPayload, GenerateAITranslateRowPayload, + LoadDatabasePrompts, LoadView, LoadViewMeta, RowId, + TestDatabasePromptConfig, UpdatePagePayload, View, YDatabase, @@ -48,6 +50,8 @@ export interface DatabaseContextState { loadViews?: () => Promise; uploadFile?: (file: File) => Promise; createOrphanedView?: (payload: { document_id: string }) => Promise; + loadDatabasePrompts?: LoadDatabasePrompts; + testDatabasePromptConfig?: TestDatabasePromptConfig; requestInstance?: AxiosInstance | null; } diff --git a/src/application/types.ts b/src/application/types.ts index 9470e39c..d17dcbe1 100644 --- a/src/application/types.ts +++ b/src/application/types.ts @@ -1,3 +1,4 @@ +import { PromptDatabaseConfiguration } from '@appflowyinc/ai-chat'; import { AxiosInstance } from 'axios'; import * as Y from 'yjs'; @@ -1068,6 +1069,14 @@ export interface ViewComponentProps { createFolderView?: (payload: CreateFolderViewPayload) => Promise; generateAISummaryForRow?: (payload: GenerateAISummaryRowPayload) => Promise; generateAITranslateForRow?: (payload: GenerateAITranslateRowPayload) => Promise; + loadDatabasePrompts?: (config: PromptDatabaseConfiguration) => Promise<{ + rawDatabasePrompts: DatabasePrompt[]; + fields: DatabasePromptField[]; + }>; + testDatabasePromptConfig?: (viewId: string) => Promise<{ + config: PromptDatabaseConfiguration; + fields: DatabasePromptField[]; + }>; } export interface CreatePagePayload { @@ -1142,3 +1151,35 @@ export interface GenerateAITranslateRowPayload { include_header?: boolean } +export type LoadDatabasePrompts = (config: PromptDatabaseConfiguration) => Promise<{ + rawDatabasePrompts: DatabasePrompt[]; + fields: DatabasePromptField[]; +}>; + +export type TestDatabasePromptConfig = (viewId: string) => Promise<{ + config: PromptDatabaseConfiguration; + fields: DatabasePromptField[]; +}>; + +export interface DatabasePrompt { + id: string; + name: string; + content: string; + example: string; + category: string; +} + +export interface DatabasePromptField { + id: string; + name: string; + isPrimary: boolean; + isSelect: boolean; +} + +export interface DatabasePromptRow { + id: string; + data: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [fieldId: string]: any; + }; +} diff --git a/src/components/ai-chat/AIChat.tsx b/src/components/ai-chat/AIChat.tsx index 23c15118..21695b3e 100644 --- a/src/components/ai-chat/AIChat.tsx +++ b/src/components/ai-chat/AIChat.tsx @@ -17,7 +17,7 @@ export function AIChat({ chatId, onRendered }: { chatId: string; onRendered?: () const isMobile = getPlatform().isMobile; const [openMobilePrompt, setOpenMobilePrompt] = React.useState(isMobile); - const { refreshOutline, updatePage } = useAppHandlers(); + const { refreshOutline, updatePage, loadDatabasePrompts, testDatabasePromptConfig } = useAppHandlers(); const { selectionMode, @@ -77,7 +77,7 @@ export function AIChat({ chatId, onRendered }: { chatId: string; onRendered?: () } }, [onRendered]); - if (!requestInstance) return null; + if (!requestInstance || !workspaceId) return null; return (
diff --git a/src/components/app/app.hooks.tsx b/src/components/app/app.hooks.tsx index 9ac0470c..75fd7bda 100644 --- a/src/components/app/app.hooks.tsx +++ b/src/components/app/app.hooks.tsx @@ -13,12 +13,17 @@ import { CreatePagePayload, CreateRowDoc, CreateSpacePayload, + DatabasePrompt, + DatabasePromptField, + DatabasePromptRow, DatabaseRelations, GenerateAISummaryRowPayload, GenerateAITranslateRowPayload, + LoadDatabasePrompts, LoadView, LoadViewMeta, Subscription, + TestDatabasePromptConfig, TextCount, Types, UIVariant, @@ -27,6 +32,7 @@ import { UserWorkspaceInfo, View, ViewLayout, + YDatabase, YjsDatabaseKey, YjsEditorKey, YSharedRoot, @@ -36,6 +42,9 @@ import { AppOverlayProvider } from '@/components/app/app-overlay/AppOverlayProvi import RequestAccess from '@/components/app/landing-pages/RequestAccess'; import { AFConfigContext, useService } from '@/components/main/app.hooks'; import { findAncestors, findView, findViewByLayout } from '@/components/_shared/outline/utils'; +import { PromptDatabaseConfiguration } from '@appflowyinc/ai-chat'; +import { FieldType } from '@/application/database-yjs'; +import { getCellDataText } from '@/application/database-yjs/cell.parse'; const ViewModal = React.lazy(() => import('@/components/app/ViewModal')); @@ -84,6 +93,8 @@ export interface AppContextType { generateAITranslateForRow?: (payload: GenerateAITranslateRowPayload) => Promise; loadDatabaseRelations?: () => Promise; createOrphanedView?: (payload: { document_id: string }) => Promise; + loadDatabasePrompts?: LoadDatabasePrompts; + testDatabasePromptConfig?: TestDatabasePromptConfig; eventEmitter?: EventEmitter; } @@ -832,6 +843,203 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => { [currentWorkspaceId, service] ); + const rowDocsRef = useRef>(new Map()); + + const getRows = useCallback( + async (viewId: string) => { + if (!currentWorkspaceId) return []; + + const doc = await loadView(viewId); + const database = doc?.getMap(YjsEditorKey.data_section)?.get(YjsEditorKey.database) as YDatabase; + const view = database.get(YjsDatabaseKey.views).get(viewId); + const rowOrders = view?.get(YjsDatabaseKey.row_orders) || []; + + const rowPromises = rowOrders + .map(async (row: { id: string }) => { + if (rowDocsRef.current.has(row.id)) { + return rowDocsRef.current.get(row.id); + } + + if (!createRowDoc) return; + + const rowKey = `${doc.guid}_rows_${row.id}`; + const rowDoc = await createRowDoc(rowKey); + + const rowSharedRoot = rowDoc?.getMap(YjsEditorKey.data_section); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const data = rowSharedRoot?.get(YjsEditorKey.database_row) as { [fieldId: string]: any }; + + const databaseRow = { + id: row.id, + data, + }; + + rowDocsRef.current.set(row.id, databaseRow); + + return databaseRow; + }) + .filter((p): p is Promise => p !== undefined); + + return Promise.all(rowPromises); + }, + [createRowDoc, currentWorkspaceId, loadView] + ); + + const getFields = useCallback( + async (viewId: string) => { + if (!currentWorkspaceId) return []; + + const doc = await loadView(viewId); + const database = doc?.getMap(YjsEditorKey.data_section)?.get(YjsEditorKey.database) as YDatabase; + const fields = database.get(YjsDatabaseKey.fields); + + return Array.from(fields.entries()) + .map(([id, field]) => { + const isPrimary = field.get(YjsDatabaseKey.is_primary) || false; + const name = field.get(YjsDatabaseKey.name) || ''; + const fieldType = Number(field.get(YjsDatabaseKey.type)); + const isSelect = fieldType === FieldType.SingleSelect || fieldType === FieldType.MultiSelect; + + return { + id, + name, + fieldType, + isPrimary, + isSelect, + data: field, + }; + }) + .filter((f) => [FieldType.RichText, FieldType.SingleSelect, FieldType.MultiSelect].includes(f.fieldType)); + }, + [currentWorkspaceId, loadView] + ); + + const loadDatabasePromptsWithFields = useCallback( + async ( + config: PromptDatabaseConfiguration, + fields: { + id: string; + name: string; + isPrimary: boolean; + fieldType: FieldType; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + data: any; + }[] + ) => { + const titleField = fields.find((field) => field.id === config.titleFieldId); + + if (!titleField) { + throw new Error('Cannot find title field'); + } + + const contentField = fields.find((field) => field.id === config.contentFieldId); + + if (!contentField) { + throw new Error('Cannot find content field'); + } + + const exampleField = config.exampleFieldId + ? fields.find((field) => field.id === config.exampleFieldId) + : undefined; + + const categoryField = config.categoryFieldId + ? fields.find((field) => field.id === config.categoryFieldId) + : undefined; + + const rows = await getRows(config.databaseViewId); + + return rows + .map((row) => { + const cells = row?.data.get(YjsDatabaseKey.cells); + + const nameCell = cells?.get(titleField.id); + const name = nameCell && titleField ? getCellDataText(nameCell, titleField.data) : ''; + + const contentCell = cells?.get(contentField.id); + const content = contentCell && contentField ? getCellDataText(contentCell, contentField.data) : ''; + + if (!name || !content) return null; + + const exampleCell = exampleField ? cells?.get(exampleField.id) : null; + const example = exampleCell && exampleField ? getCellDataText(exampleCell, exampleField.data) : ''; + + const categoryCell = categoryField ? cells?.get(categoryField.id) : null; + const category = categoryCell && categoryField ? getCellDataText(categoryCell, categoryField.data) : ''; + + return { + id: row.id, + name, + content, + example, + category, + }; + }) + .filter((prompt): prompt is DatabasePrompt => prompt !== null); + }, + [getRows] + ); + + const loadDatabasePrompts = async ( + config: PromptDatabaseConfiguration + ): Promise<{ + rawDatabasePrompts: DatabasePrompt[]; + fields: DatabasePromptField[]; + }> => { + const fields = await getFields(config.databaseViewId); + + const rawDatabasePrompts = await loadDatabasePromptsWithFields(config, fields); + + return { + rawDatabasePrompts, + fields: fields.map((field) => ({ + id: field.id, + name: field.name, + isPrimary: field.isPrimary, + isSelect: field.fieldType === FieldType.SingleSelect || field.fieldType === FieldType.MultiSelect, + })), + }; + }; + + const testDatabasePromptConfig = useCallback( + async (viewId: string) => { + const fields = await getFields(viewId); + const titleField = fields.find((field) => field.isPrimary); + + if (!titleField) { + throw new Error('Cannot find primary field'); + } + + const contentField = fields.find( + (field) => + !field.isPrimary && + ((field.name.toLowerCase() === 'content' && field.fieldType === FieldType.RichText) || + field.fieldType === FieldType.RichText) + ); + + if (!contentField) { + throw new Error('Cannot find content field'); + } + + const exampleField = fields.find( + (field) => field.name.toLowerCase() === 'example' && field.fieldType === FieldType.RichText + ); + const categoryField = fields.find( + (field) => field.name.toLowerCase() === 'category' && field.fieldType === FieldType.RichText + ); + + const config: PromptDatabaseConfiguration = { + databaseViewId: viewId, + titleFieldId: titleField.id, + contentFieldId: contentField.id, + exampleFieldId: exampleField?.id || null, + categoryFieldId: categoryField?.id || null, + }; + + return { config, fields }; + }, + [getFields] + ); + return ( { generateAITranslateForRow, loadDatabaseRelations, createOrphanedView, + loadDatabasePrompts, + testDatabasePromptConfig, eventEmitter: eventEmitterRef.current, }} > @@ -1035,6 +1245,8 @@ export function useAppHandlers() { generateAITranslateForRow: context.generateAITranslateForRow, loadDatabaseRelations: context.loadDatabaseRelations, createOrphanedView: context.createOrphanedView, + loadDatabasePrompts: context.loadDatabasePrompts, + testDatabasePromptConfig: context.testDatabasePromptConfig, eventEmitter: context.eventEmitter, }; } diff --git a/src/components/app/header/MoreActions.tsx b/src/components/app/header/MoreActions.tsx index e317c620..1c642a59 100644 --- a/src/components/app/header/MoreActions.tsx +++ b/src/components/app/header/MoreActions.tsx @@ -7,7 +7,9 @@ import { Button } from '@/components/ui/button'; import { DropdownMenu, DropdownMenuContent, - DropdownMenuGroup, DropdownMenuSeparator, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; @@ -15,15 +17,17 @@ import { useTranslation } from 'react-i18next'; import MoreActionsContent from './MoreActionsContent'; import { ReactComponent as AddToPageIcon } from '@/assets/icons/add_to_page.svg'; import { useService } from '@/components/main/app.hooks'; -import { Tooltip, TooltipContent } from '@/components/ui/tooltip'; +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; -function MoreActions ({ viewId, onDeleted, menuContentProps }: - { - viewId: string; - onDeleted?: () => void; - menuContentProps?: React.ComponentProps - } & React.ComponentProps, -) { +function MoreActions({ + viewId, + onDeleted, + menuContentProps, +}: { + viewId: string; + onDeleted?: () => void; + menuContentProps?: React.ComponentProps; +} & React.ComponentProps) { const workspaceId = useCurrentWorkspaceId(); const service = useService(); const { selectionMode, onOpenSelectionMode } = useAIChatContext(); @@ -59,21 +63,21 @@ function MoreActions ({ viewId, onDeleted, menuContentProps }: return view?.layout === ViewLayout.AIChat ? ( <> -
- -
- - {hasMessages ? '' : t('web.addMessagesToPageDisabled')} - + + + {!hasMessages && {t('web.addMessagesToPageDisabled')}}
@@ -85,23 +89,14 @@ function MoreActions ({ viewId, onDeleted, menuContentProps }: } return ( - + - - - {ChatOptions} - + {ChatOptions} { @@ -113,7 +108,6 @@ function MoreActions ({ viewId, onDeleted, menuContentProps }: - ); } diff --git a/src/components/database/components/database-row/DatabaseRowSubDocument.tsx b/src/components/database/components/database-row/DatabaseRowSubDocument.tsx index 36b70912..8286090a 100644 --- a/src/components/database/components/database-row/DatabaseRowSubDocument.tsx +++ b/src/components/database/components/database-row/DatabaseRowSubDocument.tsx @@ -26,6 +26,7 @@ export const DatabaseRowSubDocument = memo(({ rowId }: { rowId: string }) => { const context = useDatabaseContext(); const database = useDatabase(); const row = useRowData(rowId); + const getCellData = useCallback( (cell: YDatabaseCell, field: YDatabaseField) => { const type = Number(field?.get(YjsDatabaseKey.type)); diff --git a/src/components/editor/CollaborativeEditor.tsx b/src/components/editor/CollaborativeEditor.tsx index 40f051cf..78fefad1 100644 --- a/src/components/editor/CollaborativeEditor.tsx +++ b/src/components/editor/CollaborativeEditor.tsx @@ -13,9 +13,12 @@ import { clipboardFormatKey } from '@/components/editor/plugins/withCopy'; const defaultInitialValue: Descendant[] = []; -function CollaborativeEditor({ doc, onEditorConnected }: { - doc: Y.Doc, - onEditorConnected?: (editor: YjsEditor) => void +function CollaborativeEditor({ + doc, + onEditorConnected, +}: { + doc: Y.Doc; + onEditorConnected?: (editor: YjsEditor) => void; }) { const context = useEditorContext(); const readSummary = context.readSummary; @@ -25,13 +28,16 @@ function CollaborativeEditor({ doc, onEditorConnected }: { const viewId = context.viewId; const onWordCountChange = context.onWordCountChange; const [, setClock] = useState(0); - const onContentChange = useCallback((content: Descendant[]) => { - const wordCount = getTextCount(content); + const onContentChange = useCallback( + (content: Descendant[]) => { + const wordCount = getTextCount(content); - onWordCountChange?.(viewId, wordCount); - setClock((prev) => prev + 1); - onRendered?.(); - }, [onWordCountChange, viewId, onRendered]); + onWordCountChange?.(viewId, wordCount); + setClock((prev) => prev + 1); + onRendered?.(); + }, + [onWordCountChange, viewId, onRendered] + ); const editor = useMemo( () => @@ -46,18 +52,18 @@ function CollaborativeEditor({ doc, onEditorConnected }: { onContentChange, uploadFile, id: viewId, - }), + }) ), - clipboardFormatKey, - ), + clipboardFormatKey + ) ) as YjsEditor), // eslint-disable-next-line react-hooks/exhaustive-deps - [viewId, doc], + [viewId, doc] ); const [, setIsConnected] = useState(false); useEffect(() => { - if(!editor) return; + if (!editor) return; editor.connect(); setIsConnected(true); @@ -71,13 +77,9 @@ function CollaborativeEditor({ doc, onEditorConnected }: { }, [editor]); return ( - + - ); } diff --git a/src/components/editor/Editor.tsx b/src/components/editor/Editor.tsx index 9cb79754..271c83b6 100644 --- a/src/components/editor/Editor.tsx +++ b/src/components/editor/Editor.tsx @@ -24,10 +24,7 @@ export const Editor = memo(({ doc, onEditorConnected, layoutStyle = defaultLayou addCodeGrammars={handleAddCodeGrammars} layoutStyle={layoutStyle} > - + ); }); diff --git a/src/components/editor/EditorContext.tsx b/src/components/editor/EditorContext.tsx index 235972b5..a2dfdd76 100644 --- a/src/components/editor/EditorContext.tsx +++ b/src/components/editor/EditorContext.tsx @@ -3,7 +3,13 @@ import { FontLayout, LineHeightLayout, LoadView, - LoadViewMeta, UIVariant, View, CreatePagePayload, TextCount, + LoadViewMeta, + UIVariant, + View, + CreatePagePayload, + TextCount, + LoadDatabasePrompts, + TestDatabasePromptConfig, } from '@/application/types'; import { AxiosInstance } from 'axios'; import EventEmitter from 'events'; @@ -57,6 +63,8 @@ export interface EditorContextState { uploadFile?: (file: File) => Promise; requestInstance?: AxiosInstance | null; getMoreAIContext?: () => string; + loadDatabasePrompts?: LoadDatabasePrompts; + testDatabasePromptConfig?: TestDatabasePromptConfig; eventEmitter?: EventEmitter; } @@ -76,7 +84,7 @@ export const EditorContextProvider = ({ children, ...props }: EditorContextState setDecorateState((prev) => { const oldValue = prev[type]; - if(oldValue && Range.equals(oldValue.range, range) && oldValue.class_name === class_name) { + if (oldValue && Range.equals(oldValue.range, range) && oldValue.class_name === class_name) { return prev; } @@ -92,7 +100,7 @@ export const EditorContextProvider = ({ children, ...props }: EditorContextState const removeDecorate = useCallback((type: string) => { setDecorateState((prev) => { - if(prev[type] === undefined) { + if (prev[type] === undefined) { return prev; } @@ -103,16 +111,20 @@ export const EditorContextProvider = ({ children, ...props }: EditorContextState }); }, []); - return {children}; + return ( + + {children} + + ); }; export function useEditorContext() { diff --git a/src/components/editor/EditorOverlay.tsx b/src/components/editor/EditorOverlay.tsx index cbdc6505..b8dae044 100644 --- a/src/components/editor/EditorOverlay.tsx +++ b/src/components/editor/EditorOverlay.tsx @@ -23,7 +23,7 @@ import Panels from './components/panels'; import Toolbars from './components/toolbar'; function EditorOverlay({ viewId, workspaceId }: { viewId: string; workspaceId: string }) { - const { requestInstance } = useEditorContext(); + const { requestInstance, loadDatabasePrompts, testDatabasePromptConfig } = useEditorContext(); const editor = useSlate() as YjsEditor; const selection = editor.selection; const isRange = selection ? Range.isExpanded(selection) : false; @@ -172,7 +172,11 @@ function EditorOverlay({ viewId, workspaceId }: { viewId: string; workspaceId: s return ( null}> - + & { + className?: string; inset?: boolean; variant?: 'default' | 'destructive'; }