@lobehub/lobehub 1.143.0-next.2 → 2.0.0-next.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/.github/workflows/desktop-pr-build.yml +12 -10
  2. package/.github/workflows/docker.yml +25 -20
  3. package/.github/workflows/e2e.yml +3 -3
  4. package/.github/workflows/release-desktop-beta.yml +8 -8
  5. package/.github/workflows/release.yml +1 -1
  6. package/.github/workflows/test.yml +4 -4
  7. package/CHANGELOG.md +265 -0
  8. package/README.md +7 -0
  9. package/README.zh-CN.md +7 -0
  10. package/apps/desktop/src/main/utils/next-electron-rsc.ts +7 -5
  11. package/changelog/v1.json +85 -0
  12. package/docs/development/database-schema.dbml +11 -1
  13. package/docs/self-hosting/advanced/auth/next-auth/auth0.mdx +2 -2
  14. package/docs/self-hosting/advanced/auth/next-auth/auth0.zh-CN.mdx +2 -2
  15. package/docs/self-hosting/advanced/auth/next-auth/authelia.mdx +2 -2
  16. package/docs/self-hosting/advanced/auth/next-auth/authelia.zh-CN.mdx +2 -2
  17. package/docs/self-hosting/advanced/auth/next-auth/authentik.mdx +2 -2
  18. package/docs/self-hosting/advanced/auth/next-auth/authentik.zh-CN.mdx +2 -2
  19. package/docs/self-hosting/advanced/auth/next-auth/casdoor.mdx +2 -2
  20. package/docs/self-hosting/advanced/auth/next-auth/casdoor.zh-CN.mdx +2 -2
  21. package/docs/self-hosting/advanced/auth/next-auth/cloudflare-zero-trust.mdx +2 -2
  22. package/docs/self-hosting/advanced/auth/next-auth/cloudflare-zero-trust.zh-CN.mdx +2 -2
  23. package/docs/self-hosting/advanced/auth/next-auth/github.mdx +2 -2
  24. package/docs/self-hosting/advanced/auth/next-auth/github.zh-CN.mdx +2 -2
  25. package/docs/self-hosting/advanced/auth/next-auth/google.mdx +32 -29
  26. package/docs/self-hosting/advanced/auth/next-auth/keycloak.mdx +2 -2
  27. package/docs/self-hosting/advanced/auth/next-auth/keycloak.zh-CN.mdx +2 -2
  28. package/docs/self-hosting/advanced/auth/next-auth/logto.mdx +5 -3
  29. package/docs/self-hosting/advanced/auth/next-auth/logto.zh-CN.mdx +5 -3
  30. package/docs/self-hosting/advanced/auth/next-auth/microsoft-entra-id.mdx +2 -2
  31. package/docs/self-hosting/advanced/auth/next-auth/microsoft-entra-id.zh-CN.mdx +2 -2
  32. package/docs/self-hosting/advanced/auth/next-auth/okta.mdx +2 -2
  33. package/docs/self-hosting/advanced/auth/next-auth/okta.zh-CN.mdx +2 -2
  34. package/docs/self-hosting/advanced/auth/next-auth/wechat.mdx +2 -2
  35. package/docs/self-hosting/advanced/auth/next-auth/wechat.zh-CN.mdx +2 -2
  36. package/docs/self-hosting/advanced/auth/next-auth/zitadel.mdx +2 -2
  37. package/docs/self-hosting/advanced/auth/next-auth/zitadel.zh-CN.mdx +2 -2
  38. package/docs/self-hosting/advanced/auth.mdx +32 -21
  39. package/docs/self-hosting/advanced/auth.zh-CN.mdx +30 -19
  40. package/docs/self-hosting/advanced/online-search.mdx +30 -25
  41. package/docs/self-hosting/advanced/online-search.zh-CN.mdx +25 -23
  42. package/locales/ar/models.json +15 -6
  43. package/locales/bg-BG/models.json +15 -6
  44. package/locales/de-DE/models.json +15 -6
  45. package/locales/en-US/models.json +15 -6
  46. package/locales/es-ES/models.json +15 -6
  47. package/locales/fa-IR/models.json +15 -6
  48. package/locales/fr-FR/models.json +15 -6
  49. package/locales/it-IT/models.json +15 -6
  50. package/locales/ja-JP/models.json +15 -6
  51. package/locales/ko-KR/models.json +15 -6
  52. package/locales/nl-NL/models.json +15 -6
  53. package/locales/pl-PL/models.json +15 -6
  54. package/locales/pt-BR/models.json +15 -6
  55. package/locales/ru-RU/models.json +15 -6
  56. package/locales/tr-TR/models.json +15 -6
  57. package/locales/vi-VN/models.json +15 -6
  58. package/locales/zh-CN/models.json +15 -6
  59. package/locales/zh-TW/models.json +15 -6
  60. package/next.config.ts +2 -3
  61. package/package.json +13 -19
  62. package/packages/const/src/index.ts +0 -1
  63. package/packages/const/src/models.ts +13 -0
  64. package/packages/const/src/url.ts +1 -4
  65. package/packages/context-engine/src/index.ts +1 -6
  66. package/packages/context-engine/src/processors/GroupMessageFlatten.ts +12 -2
  67. package/packages/context-engine/src/processors/__tests__/GroupMessageFlatten.test.ts +73 -9
  68. package/packages/context-engine/src/providers/index.ts +0 -2
  69. package/packages/database/migrations/0041_improve_index.sql +10 -0
  70. package/packages/database/migrations/meta/0041_snapshot.json +7784 -0
  71. package/packages/database/migrations/meta/_journal.json +7 -0
  72. package/packages/database/package.json +1 -1
  73. package/packages/database/src/core/migrations.json +17 -0
  74. package/packages/database/src/models/__tests__/message.grouping.test.ts +812 -0
  75. package/packages/database/src/models/__tests__/message.test.ts +322 -170
  76. package/packages/database/src/models/message.ts +62 -24
  77. package/packages/database/src/models/session.ts +60 -19
  78. package/packages/database/src/schemas/agent.ts +10 -11
  79. package/packages/database/src/schemas/message.ts +5 -1
  80. package/packages/database/src/schemas/relations.ts +6 -4
  81. package/packages/database/src/schemas/session.ts +2 -0
  82. package/packages/database/src/schemas/topic.ts +6 -1
  83. package/packages/database/src/utils/__tests__/groupMessages.test.ts +145 -2
  84. package/packages/database/src/utils/groupMessages.ts +7 -5
  85. package/packages/electron-client-ipc/package.json +4 -1
  86. package/packages/file-loaders/package.json +1 -0
  87. package/packages/model-bank/src/aiModels/anthropic.ts +0 -63
  88. package/packages/model-bank/src/aiModels/azure.ts +155 -0
  89. package/packages/model-bank/src/aiModels/bedrock.ts +44 -0
  90. package/packages/model-bank/src/aiModels/higress.ts +0 -55
  91. package/packages/model-bank/src/aiModels/infiniai.ts +21 -0
  92. package/packages/model-bank/src/aiModels/ollamacloud.ts +13 -0
  93. package/packages/model-bank/src/aiModels/siliconcloud.ts +19 -0
  94. package/packages/model-runtime/src/core/openaiCompatibleFactory/createImage.ts +1 -1
  95. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.test.ts +33 -3
  96. package/packages/model-runtime/src/core/parameterResolver.ts +3 -0
  97. package/packages/model-runtime/src/core/streams/openai/__snapshots__/responsesStream.test.ts.snap +0 -38
  98. package/packages/model-runtime/src/providers/azureOpenai/index.ts +2 -1
  99. package/packages/model-runtime/src/providers/minimax/index.ts +5 -5
  100. package/packages/model-runtime/src/providers/search1api/index.test.ts +2 -2
  101. package/packages/types/src/message/common/base.ts +13 -0
  102. package/packages/types/src/message/common/image.ts +8 -0
  103. package/packages/types/src/message/common/metadata.ts +39 -0
  104. package/packages/types/src/message/common/tools.ts +10 -0
  105. package/packages/types/src/message/db/params.ts +47 -1
  106. package/packages/types/src/message/ui/chat.ts +4 -1
  107. package/packages/types/src/search.ts +16 -0
  108. package/packages/web-crawler/src/crawImpl/firecrawl.ts +39 -12
  109. package/scripts/migrateServerDB/index.ts +2 -1
  110. package/src/app/(backend)/oidc/consent/route.ts +0 -1
  111. package/src/app/(backend)/webapi/revalidate/route.ts +1 -1
  112. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/V1Mobile/index.tsx +2 -2
  113. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/V1Mobile/useSend.ts +6 -4
  114. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/useSend.ts +15 -10
  115. package/src/app/[variants]/(main)/chat/@session/features/SessionListContent/List/Item/index.tsx +4 -2
  116. package/src/app/[variants]/(main)/settings/_layout/SettingsContent.tsx +0 -3
  117. package/src/app/[variants]/layout.tsx +1 -0
  118. package/src/app/sitemap.tsx +7 -1
  119. package/src/components/Thinking/index.tsx +4 -3
  120. package/src/config/modelProviders/anthropic.ts +0 -23
  121. package/src/config/modelProviders/higress.ts +0 -23
  122. package/src/config/modelProviders/minimax.ts +1 -1
  123. package/src/config/modelProviders/qiniu.ts +1 -1
  124. package/src/envs/auth.ts +0 -179
  125. package/src/features/AgentSetting/AgentPlugin/index.tsx +21 -13
  126. package/src/features/ChatInput/ActionBar/STT/browser.tsx +2 -2
  127. package/src/features/ChatInput/ActionBar/STT/openai.tsx +2 -2
  128. package/src/features/ChatInput/ActionBar/Tools/useControls.tsx +1 -3
  129. package/src/features/Conversation/Error/ErrorJsonViewer.tsx +4 -3
  130. package/src/features/Conversation/Error/OllamaBizError/index.tsx +7 -2
  131. package/src/features/Conversation/Error/index.tsx +15 -5
  132. package/src/features/Conversation/MarkdownElements/LobeArtifact/Render/index.tsx +2 -2
  133. package/src/features/Conversation/Messages/Assistant/Extra/index.tsx +2 -2
  134. package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +5 -3
  135. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/BuiltinPluginTitle.tsx +2 -2
  136. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/ToolTitle.tsx +4 -2
  137. package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +2 -2
  138. package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +2 -2
  139. package/src/features/Conversation/Messages/Assistant/Tool/index.tsx +2 -2
  140. package/src/features/Conversation/Messages/Assistant/index.tsx +4 -4
  141. package/src/features/Conversation/Messages/Default.tsx +2 -2
  142. package/src/features/Conversation/Messages/User/Extra.tsx +2 -2
  143. package/src/features/Conversation/Messages/User/index.tsx +4 -4
  144. package/src/features/Conversation/Messages/index.tsx +3 -3
  145. package/src/features/Conversation/components/AutoScroll.tsx +2 -2
  146. package/src/features/Conversation/components/Extras/Usage/UsageDetail/index.tsx +9 -6
  147. package/src/features/PluginTag/index.tsx +1 -3
  148. package/src/features/PluginsUI/Render/BuiltinType/index.test.tsx +37 -28
  149. package/src/features/Portal/Artifacts/Body/index.tsx +2 -2
  150. package/src/libs/next-auth/auth.config.ts +1 -1
  151. package/src/libs/next-auth/sso-providers/auth0.ts +0 -7
  152. package/src/libs/next-auth/sso-providers/authelia.ts +3 -5
  153. package/src/libs/next-auth/sso-providers/authentik.ts +0 -7
  154. package/src/libs/next-auth/sso-providers/cloudflare-zero-trust.ts +3 -6
  155. package/src/libs/next-auth/sso-providers/cognito.ts +1 -5
  156. package/src/libs/next-auth/sso-providers/generic-oidc.ts +3 -5
  157. package/src/libs/next-auth/sso-providers/github.ts +0 -6
  158. package/src/libs/next-auth/sso-providers/google.ts +0 -2
  159. package/src/libs/next-auth/sso-providers/index.ts +0 -2
  160. package/src/libs/next-auth/sso-providers/keycloak.ts +0 -3
  161. package/src/libs/next-auth/sso-providers/logto.ts +3 -5
  162. package/src/libs/next-auth/sso-providers/okta.ts +0 -4
  163. package/src/libs/next-auth/sso-providers/zitadel.ts +0 -7
  164. package/src/libs/oidc-provider/provider.ts +1 -1
  165. package/src/server/modules/AssistantStore/index.ts +1 -1
  166. package/src/server/modules/ModelRuntime/trace.ts +11 -4
  167. package/src/server/routers/lambda/message.ts +14 -3
  168. package/src/server/routers/lambda/session.ts +8 -5
  169. package/src/server/services/search/impls/firecrawl/index.ts +51 -11
  170. package/src/server/services/search/impls/firecrawl/type.ts +60 -9
  171. package/src/services/chat/chat.test.ts +1 -40
  172. package/src/services/chat/contextEngineering.test.ts +0 -30
  173. package/src/services/chat/contextEngineering.ts +1 -12
  174. package/src/services/chat/index.ts +2 -7
  175. package/src/services/chat/types.ts +1 -1
  176. package/src/services/message/_deprecated.ts +1 -1
  177. package/src/services/message/client.ts +8 -2
  178. package/src/services/message/server.ts +7 -2
  179. package/src/services/message/type.ts +6 -1
  180. package/src/services/user/client.test.ts +4 -1
  181. package/src/store/chat/helpers.test.ts +99 -0
  182. package/src/store/chat/helpers.ts +21 -2
  183. package/src/store/chat/selectors.ts +1 -1
  184. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +3 -3
  185. package/src/store/chat/slices/builtinTool/actions/index.ts +1 -4
  186. package/src/store/chat/slices/message/action.test.ts +5 -1
  187. package/src/store/chat/slices/message/action.ts +102 -14
  188. package/src/store/chat/slices/message/reducer.test.ts +363 -5
  189. package/src/store/chat/slices/message/reducer.ts +87 -3
  190. package/src/store/chat/slices/message/{selectors.test.ts → selectors/chat.test.ts} +266 -30
  191. package/src/store/chat/slices/message/{selectors.ts → selectors/chat.ts} +29 -79
  192. package/src/store/chat/slices/message/selectors/index.ts +2 -0
  193. package/src/store/chat/slices/message/selectors/messageState.test.ts +36 -0
  194. package/src/store/chat/slices/message/selectors/messageState.ts +80 -0
  195. package/src/store/chat/slices/plugin/action.test.ts +34 -132
  196. package/src/store/chat/slices/plugin/action.ts +1 -44
  197. package/src/store/tool/selectors/tool.test.ts +1 -1
  198. package/src/store/tool/selectors/tool.ts +6 -8
  199. package/src/store/tool/slices/builtin/action.test.ts +83 -35
  200. package/src/store/tool/slices/builtin/action.ts +0 -9
  201. package/src/store/tool/slices/builtin/selectors.test.ts +4 -30
  202. package/src/store/tool/slices/builtin/selectors.ts +15 -21
  203. package/src/tools/index.ts +0 -6
  204. package/src/tools/renders.ts +0 -3
  205. package/src/tools/web-browsing/Portal/Search/Footer.tsx +2 -2
  206. package/tsconfig.json +9 -2
  207. package/packages/const/src/guide.ts +0 -89
  208. package/packages/context-engine/src/providers/InboxGuide.ts +0 -102
  209. package/packages/context-engine/src/providers/__tests__/InboxGuideProvider.test.ts +0 -121
  210. package/src/app/[variants]/(main)/settings/llm/ProviderList/Azure/index.tsx +0 -93
  211. package/src/app/[variants]/(main)/settings/llm/ProviderList/Bedrock/index.tsx +0 -70
  212. package/src/app/[variants]/(main)/settings/llm/ProviderList/Cloudflare/index.tsx +0 -39
  213. package/src/app/[variants]/(main)/settings/llm/ProviderList/Github/index.tsx +0 -52
  214. package/src/app/[variants]/(main)/settings/llm/ProviderList/HuggingFace/index.tsx +0 -52
  215. package/src/app/[variants]/(main)/settings/llm/ProviderList/Ollama/index.tsx +0 -20
  216. package/src/app/[variants]/(main)/settings/llm/ProviderList/OpenAI/index.tsx +0 -17
  217. package/src/app/[variants]/(main)/settings/llm/ProviderList/providers.tsx +0 -132
  218. package/src/app/[variants]/(main)/settings/llm/components/Checker.tsx +0 -118
  219. package/src/app/[variants]/(main)/settings/llm/components/ProviderConfig/index.tsx +0 -303
  220. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/CustomModelOption.tsx +0 -98
  221. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/ModelConfigModal/Form.tsx +0 -104
  222. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/ModelConfigModal/index.tsx +0 -77
  223. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/ModelFetcher.tsx +0 -105
  224. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/Option.tsx +0 -68
  225. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/index.tsx +0 -146
  226. package/src/app/[variants]/(main)/settings/llm/const.ts +0 -20
  227. package/src/app/[variants]/(main)/settings/llm/features/Footer.tsx +0 -35
  228. package/src/app/[variants]/(main)/settings/llm/index.tsx +0 -30
  229. package/src/app/[variants]/(main)/settings/llm/type.ts +0 -5
  230. package/src/envs/__tests__/auth.test.ts +0 -200
  231. package/src/libs/next-auth/sso-providers/azure-ad.ts +0 -33
  232. package/src/services/chat/__snapshots__/chat.test.ts.snap +0 -110
  233. package/src/store/chat/slices/builtinTool/actions/__tests__/dalle.test.ts +0 -121
  234. package/src/store/chat/slices/builtinTool/actions/dalle.ts +0 -124
  235. package/src/tools/dalle/Render/GalleyGrid.tsx +0 -60
  236. package/src/tools/dalle/Render/Item/EditMode.tsx +0 -66
  237. package/src/tools/dalle/Render/Item/Error.tsx +0 -49
  238. package/src/tools/dalle/Render/Item/Image.tsx +0 -44
  239. package/src/tools/dalle/Render/Item/ImageFileItem.tsx +0 -57
  240. package/src/tools/dalle/Render/Item/index.tsx +0 -88
  241. package/src/tools/dalle/Render/ToolBar.tsx +0 -56
  242. package/src/tools/dalle/Render/index.tsx +0 -52
  243. package/src/tools/dalle/index.ts +0 -92
  244. /package/src/{middleware.ts → proxy.ts} +0 -0
@@ -1,200 +0,0 @@
1
- // @vitest-environment node
2
- import { beforeEach, describe, expect, it, vi } from 'vitest';
3
-
4
- import { getAuthConfig } from '../auth';
5
-
6
- // Stub the global process object to safely mock environment variables
7
- vi.stubGlobal('process', {
8
- ...process, // Preserve the original process object
9
- env: { ...process.env }, // Clone the environment variables object for modification
10
- });
11
-
12
- const spyConsoleWarn = vi.spyOn(console, 'warn');
13
-
14
- describe('getAuthConfig', () => {
15
- beforeEach(() => {
16
- // Clear all environment variables before each test
17
- // @ts-expect-error
18
- process.env = {};
19
- });
20
-
21
- // TODO(NextAuth ENVs Migration): Remove once nextauth envs migration time end
22
- describe('should warn about deprecated environment variables', () => {
23
- it('should warn about Auth0 deprecated environment variables', () => {
24
- // Set all deprecated environment variables
25
- process.env.AUTH0_CLIENT_ID = 'auth0_client_id';
26
- process.env.AUTH0_CLIENT_SECRET = 'auth0_client_secret';
27
- process.env.AUTH0_ISSUER = 'auth0_issuer';
28
- // Call the function
29
- getAuthConfig();
30
-
31
- // Check that the spyConsoleWarn function was called for each deprecated environment variable
32
- // Example: A warning meassage should incloud: `<Old Env> .* <New Env>`
33
- // And the regex should match the warning message: `AUTH0_CLIENT_ID.*AUTH_AUTH0_ID`
34
- expect(spyConsoleWarn).toHaveBeenCalledWith(
35
- expect.stringMatching(/AUTH0_CLIENT_ID.*AUTH_AUTH0_ID/),
36
- );
37
- expect(spyConsoleWarn).toHaveBeenCalledWith(
38
- expect.stringMatching(/AUTH0_CLIENT_SECRET.*AUTH_AUTH0_SECRET/),
39
- );
40
- expect(spyConsoleWarn).toHaveBeenCalledWith(
41
- expect.stringMatching(/AUTH0_ISSUER.*AUTH_AUTH0_ISSUER/),
42
- );
43
- });
44
- it('should warn about Authentik deprecated environment variables', () => {
45
- // Set all deprecated environment variables
46
- process.env.AUTHENTIK_CLIENT_ID = 'authentik_client_id';
47
- process.env.AUTHENTIK_CLIENT_SECRET = 'authentik_client_secret';
48
- process.env.AUTHENTIK_ISSUER = 'authentik_issuer';
49
-
50
- // Call the function
51
- getAuthConfig();
52
-
53
- // Check that the spyConsoleWarn function was called for each deprecated environment variable
54
- expect(spyConsoleWarn).toHaveBeenCalledWith(
55
- expect.stringMatching(/AUTHENTIK_CLIENT_ID.*AUTH_AUTHENTIK_ID/),
56
- );
57
- expect(spyConsoleWarn).toHaveBeenCalledWith(
58
- expect.stringMatching(/AUTHENTIK_CLIENT_SECRET.*AUTH_AUTHENTIK_SECRET/),
59
- );
60
- expect(spyConsoleWarn).toHaveBeenCalledWith(
61
- expect.stringMatching(/AUTHENTIK_ISSUER.*AUTH_AUTHENTIK_ISSUER/),
62
- );
63
- });
64
- it('should warn about Authelia deprecated environment variables', () => {
65
- // Set all deprecated environment variables
66
- process.env.AUTHELIA_CLIENT_ID = 'authelia_client_id';
67
- process.env.AUTHELIA_CLIENT_SECRET = 'authelia_client_secret';
68
- process.env.AUTHELIA_ISSUER = 'authelia_issuer';
69
-
70
- // Call the function
71
- getAuthConfig();
72
-
73
- // Check that the spyConsoleWarn function was called for each deprecated environment variable
74
- expect(spyConsoleWarn).toHaveBeenCalledWith(
75
- expect.stringMatching(/AUTHELIA_CLIENT_ID.*AUTH_AUTHELIA_ID/),
76
- );
77
- expect(spyConsoleWarn).toHaveBeenCalledWith(
78
- expect.stringMatching(/AUTHELIA_CLIENT_SECRET.*AUTH_AUTHELIA_SECRET/),
79
- );
80
- expect(spyConsoleWarn).toHaveBeenCalledWith(
81
- expect.stringMatching(/AUTHELIA_ISSUER.*AUTH_AUTHELIA_ISSUER/),
82
- );
83
- });
84
- it('should warn about AzureAD deprecated environment variables', () => {
85
- // Set all deprecated environment variables
86
- process.env.AZURE_AD_CLIENT_ID = 'azure_ad_client_id';
87
- process.env.AZURE_AD_CLIENT_SECRET = 'azure_ad_client_secret';
88
- process.env.AZURE_AD_TENANT_ID = 'azure_ad_tenant_id';
89
-
90
- // Call the function
91
- getAuthConfig();
92
-
93
- // Check that the spyConsoleWarn function was called for each deprecated environment variable
94
- expect(spyConsoleWarn).toHaveBeenCalledWith(
95
- expect.stringMatching(/AZURE_AD_CLIENT_ID.*AUTH_AZURE_AD_ID/),
96
- );
97
- expect(spyConsoleWarn).toHaveBeenCalledWith(
98
- expect.stringMatching(/AZURE_AD_CLIENT_SECRET.*AUTH_AZURE_AD_SECRET/),
99
- );
100
- expect(spyConsoleWarn).toHaveBeenCalledWith(
101
- expect.stringMatching(/AZURE_AD_TENANT_ID.*AUTH_AZURE_AD_TENANT_ID/),
102
- );
103
- });
104
- it('should warn about Cloudflare Zero Trust deprecated environment variables', () => {
105
- // Set all deprecated environment variables
106
- process.env.CLOUDFLARE_ZERO_TRUST_CLIENT_ID = 'cloudflare_zero_trust_client_id';
107
- process.env.CLOUDFLARE_ZERO_TRUST_CLIENT_SECRET = 'cloudflare_zero_trust_client_secret';
108
- process.env.CLOUDFLARE_ZERO_TRUST_ISSUER = 'cloudflare_zero_trust_issuer';
109
-
110
- // Call the function
111
- getAuthConfig();
112
-
113
- // Check that the spyConsoleWarn function was called for each deprecated environment variable
114
- expect(spyConsoleWarn).toHaveBeenCalledWith(
115
- expect.stringMatching(/CLOUDFLARE_ZERO_TRUST_CLIENT_ID.*AUTH_CLOUDFLARE_ZERO_TRUST_ID/),
116
- );
117
- expect(spyConsoleWarn).toHaveBeenCalledWith(
118
- expect.stringMatching(
119
- /CLOUDFLARE_ZERO_TRUST_CLIENT_SECRET.*AUTH_CLOUDFLARE_ZERO_TRUST_SECRET/,
120
- ),
121
- );
122
- expect(spyConsoleWarn).toHaveBeenCalledWith(
123
- expect.stringMatching(/CLOUDFLARE_ZERO_TRUST_ISSUER.*AUTH_CLOUDFLARE_ZERO_TRUST_ISSUER/),
124
- );
125
- });
126
- it('should warn about Generic OIDC deprecated environment variables', () => {
127
- // Set all deprecated environment variables
128
- process.env.GENERIC_OIDC_CLIENT_ID = 'generic_oidc_client_id';
129
- process.env.GENERIC_OIDC_CLIENT_SECRET = 'generic_oidc_client_secret';
130
- process.env.GENERIC_OIDC_ISSUER = 'generic_oidc_issuer';
131
- // Call the function
132
- getAuthConfig();
133
-
134
- // Check that the spyConsoleWarn function was called for each deprecated environment variable
135
- expect(spyConsoleWarn).toHaveBeenCalledWith(
136
- expect.stringMatching(/GENERIC_OIDC_CLIENT_ID.*AUTH_GENERIC_OIDC_ID/),
137
- );
138
- expect(spyConsoleWarn).toHaveBeenCalledWith(
139
- expect.stringMatching(/GENERIC_OIDC_CLIENT_SECRET.*AUTH_GENERIC_OIDC_SECRET/),
140
- );
141
- expect(spyConsoleWarn).toHaveBeenCalledWith(
142
- expect.stringMatching(/GENERIC_OIDC_ISSUER.*AUTH_GENERIC_OIDC_ISSUER/),
143
- );
144
- });
145
- it('should warn about GitHub deprecated environment variables', () => {
146
- // Set all deprecated environment variables
147
- process.env.GITHUB_CLIENT_ID = 'github_client_id';
148
- process.env.GITHUB_CLIENT_SECRET = 'github_client_secret';
149
- // Call the function
150
- getAuthConfig();
151
-
152
- // Check that the spyConsoleWarn function was called for each deprecated environment variable
153
- expect(spyConsoleWarn).toHaveBeenCalledWith(
154
- expect.stringMatching(/GITHUB_CLIENT_ID.*AUTH_GITHUB_ID/),
155
- );
156
- expect(spyConsoleWarn).toHaveBeenCalledWith(
157
- expect.stringMatching(/GITHUB_CLIENT_SECRET.*AUTH_GITHUB_SECRET/),
158
- );
159
- });
160
- it('should warn about Logto deprecated environment variables', () => {
161
- // Set all deprecated environment variables
162
- process.env.LOGTO_CLIENT_ID = 'logto_client_id';
163
- process.env.LOGTO_CLIENT_SECRET = 'logto_client_secret';
164
- process.env.LOGTO_ISSUER = 'logto_issuer';
165
- // Call the function
166
- getAuthConfig();
167
-
168
- // Check that the spyConsoleWarn function was called for each deprecated environment variable
169
- expect(spyConsoleWarn).toHaveBeenCalledWith(
170
- expect.stringMatching(/LOGTO_CLIENT_ID.*AUTH_LOGTO_ID/),
171
- );
172
- expect(spyConsoleWarn).toHaveBeenCalledWith(
173
- expect.stringMatching(/LOGTO_CLIENT_SECRET.*AUTH_LOGTO_SECRET/),
174
- );
175
- expect(spyConsoleWarn).toHaveBeenCalledWith(
176
- expect.stringMatching(/LOGTO_ISSUER.*AUTH_LOGTO_ISSUER/),
177
- );
178
- });
179
- it('should warn about Zitadel deprecated environment variables', () => {
180
- // Set all deprecated environment variables
181
- process.env.ZITADEL_CLIENT_ID = 'zitadel_client_id';
182
- process.env.ZITADEL_CLIENT_SECRET = 'zitadel_client_secret';
183
- process.env.ZITADEL_ISSUER = 'zitadel_issuer';
184
- // Call the function
185
- getAuthConfig();
186
-
187
- // Check that the spyConsoleWarn function was called for each deprecated environment variable
188
- expect(spyConsoleWarn).toHaveBeenCalledWith(
189
- expect.stringMatching(/ZITADEL_CLIENT_ID.*AUTH_ZITADEL_ID/),
190
- );
191
- expect(spyConsoleWarn).toHaveBeenCalledWith(
192
- expect.stringMatching(/ZITADEL_CLIENT_SECRET.*AUTH_ZITADEL_SECRET/),
193
- );
194
- expect(spyConsoleWarn).toHaveBeenCalledWith(
195
- expect.stringMatching(/ZITADEL_ISSUER.*AUTH_ZITADEL_ISSUER/),
196
- );
197
- });
198
- });
199
- // Remove end
200
- });
@@ -1,33 +0,0 @@
1
- import AzureAD from 'next-auth/providers/azure-ad';
2
-
3
- import { authEnv } from '@/envs/auth';
4
-
5
- import { getMicrosoftEntraIdIssuer } from './microsoft-entra-id-helper';
6
- import { CommonProviderConfig } from './sso.config';
7
-
8
- const provider = {
9
- id: 'azure-ad',
10
- provider: AzureAD({
11
- ...CommonProviderConfig,
12
- // Specify auth scope, at least include 'openid email'
13
- // all scopes in Azure AD ref: https://learn.microsoft.com/en-us/entra/identity-platform/scopes-oidc#openid-connect-scopes
14
- authorization: { params: { scope: 'openid email profile' } },
15
- // TODO(NextAuth ENVs Migration): Remove once nextauth envs migration time end
16
- clientId: authEnv.AZURE_AD_CLIENT_ID ?? process.env.AUTH_AZURE_AD_ID,
17
- clientSecret: authEnv.AZURE_AD_CLIENT_SECRET ?? process.env.AUTH_AZURE_AD_SECRET,
18
- issuer: getMicrosoftEntraIdIssuer(),
19
- // Remove end
20
- // TODO(NextAuth): map unique user id to `providerAccountId` field
21
- // profile(profile) {
22
- // return {
23
- // email: profile.email,
24
- // image: profile.picture,
25
- // name: profile.name,
26
- // providerAccountId: profile.user_id,
27
- // id: profile.user_id,
28
- // };
29
- // },
30
- }),
31
- };
32
-
33
- export default provider;
@@ -1,110 +0,0 @@
1
- // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
-
3
- exports[`ChatService > createAssistantMessage > with tools messages > work with dalle3 1`] = `
4
- {
5
- "enabledSearch": undefined,
6
- "messages": [
7
- {
8
- "content": "<plugins description="The plugins you can use below">
9
- <collection name="DALL·E 3">
10
- <collection.instructions>Whenever a description of an image is given, use lobe-image-designer to create the images and then summarize the prompts used to generate the images in plain text. If the user does not ask for a specific number of images, default to creating four captions to send to lobe-image-designer that are written to be as diverse as possible.
11
-
12
- All captions sent to lobe-image-designer must abide by the following policies:
13
-
14
- 1. If the description is not in English, then translate it.
15
- 2. Do not create more than 4 images, even if the user requests more.
16
- 3. Don't create images of politicians or other public figures. Recommend other ideas instead.
17
- 5. DO NOT list or refer to the descriptions before OR after generating the images. They should ONLY ever be written out ONCE, in the \`prompts\` field of the request. You do not need to ask for permission to generate, just do it!
18
- 6. Always mention the image type (photo, oil painting, watercolor painting, illustration, cartoon, drawing, vector, render, etc.) at the beginning of the caption. Unless the caption suggests otherwise, make at least 1--2 of the 4 images photos.
19
- 7. Diversify depictions of ALL images with people to include DESCENT and GENDER for EACH person using direct terms. Adjust only human descriptions.
20
- - EXPLICITLY specify these attributes, not abstractly reference them. The attributes should be specified in a minimal way and should directly describe their physical form.
21
- - Your choices should be grounded in reality. For example, all of a given OCCUPATION should not be the same gender or race. Additionally, focus on creating diverse, inclusive, and exploratory scenes via the properties you choose during rewrites. Make choices that may be insightful or unique sometimes.
22
- - Use "various" or "diverse" ONLY IF the description refers to groups of more than 3 people. Do not change the number of people requested in the original description.
23
- - Don't alter memes, fictional character origins, or unseen people. Maintain the original prompt's intent and prioritize quality.
24
- - Do not create any imagery that would be offensive.
25
-
26
- 8. Silently modify descriptions that include names or hints or references of specific people or celebrities by carefully selecting a few minimal modifications to substitute references to the people with generic descriptions that don't divulge any information about their identities, except for their genders and physiques. Do this EVEN WHEN the instructions ask for the prompt to not be changed. Some special cases:
27
- - Modify such prompts even if you don't know who the person is, or if their name is misspelled (e.g. "Barake Obema")
28
- - If the reference to the person will only appear as TEXT out in the image, then use the reference as is and do not modify it.
29
- - When making the substitutions, don't use prominent titles that could give away the person's identity. E.g., instead of saying "president", "prime minister", or "chancellor", say "politician"; instead of saying "king", "queen", "emperor", or "empress", say "public figure"; instead of saying "Pope", say "religious figure"; and so on.
30
- - If any creative professional or studio is named, substitute the name with a description of their style that does not reference any specific people, or delete the reference if they are unknown. DO NOT refer to the artist or studio's style.
31
-
32
- The prompt must intricately describe every part of the image in concrete, objective detail. THINK about what the end goal of the description is, and extrapolate that to what would make satisfying images.
33
- All descriptions sent to lobe-image-designer should be a paragraph of text that is extremely descriptive and detailed. Each should be more than 3 sentences long.</collection.instructions>
34
- <api identifier="lobe-image-designer____text2image____builtin">Create images from a text-only prompt.</api>
35
- </collection>
36
- </plugins>",
37
- "role": "system",
38
- },
39
- {
40
- "content": "https://vercel.com/ 请分析 chatGPT 关键词
41
-
42
- ",
43
- "role": "user",
44
- },
45
- ],
46
- "model": "gpt-3.5-turbo-1106",
47
- "tools": [
48
- {
49
- "function": {
50
- "description": "Create images from a text-only prompt.",
51
- "name": "lobe-image-designer____text2image____builtin",
52
- "parameters": {
53
- "properties": {
54
- "prompts": {
55
- "description": "The user's original image description, potentially modified to abide by the lobe-image-designer policies. If the user does not suggest a number of captions to create, create four of them. If creating multiple captions, make them as diverse as possible. If the user requested modifications to previous images, the captions should not simply be longer, but rather it should be refactored to integrate the suggestions into each of the captions. Generate no more than 4 images, even if the user requests more.",
56
- "items": {
57
- "type": "string",
58
- },
59
- "maxItems": 4,
60
- "minItems": 1,
61
- "type": "array",
62
- },
63
- "quality": {
64
- "default": "standard",
65
- "description": "The quality of the image that will be generated. hd creates images with finer details and greater consistency across the image.",
66
- "enum": [
67
- "standard",
68
- "hd",
69
- ],
70
- "type": "string",
71
- },
72
- "seeds": {
73
- "description": "A list of seeds to use for each prompt. If the user asks to modify a previous image, populate this field with the seed used to generate that image from the image lobe-image-designer metadata.",
74
- "items": {
75
- "type": "integer",
76
- },
77
- "type": "array",
78
- },
79
- "size": {
80
- "default": "1024x1024",
81
- "description": "The resolution of the requested image, which can be wide, square, or tall. Use 1024x1024 (square) as the default unless the prompt suggests a wide image, 1792x1024, or a full-body portrait, in which case 1024x1792 (tall) should be used instead. Always include this parameter in the request.",
82
- "enum": [
83
- "1792x1024",
84
- "1024x1024",
85
- "1024x1792",
86
- ],
87
- "type": "string",
88
- },
89
- "style": {
90
- "default": "vivid",
91
- "description": "The style of the generated images. Must be one of vivid or natural. Vivid causes the model to lean towards generating hyper-real and dramatic images. Natural causes the model to produce more natural, less hyper-real looking images.",
92
- "enum": [
93
- "vivid",
94
- "natural",
95
- ],
96
- "type": "string",
97
- },
98
- },
99
- "required": [
100
- "prompts",
101
- ],
102
- "type": "object",
103
- },
104
- },
105
- "type": "function",
106
- },
107
- ],
108
- "top_p": 1,
109
- }
110
- `;
@@ -1,121 +0,0 @@
1
- import { UIChatMessage } from '@lobechat/types';
2
- import { act, renderHook } from '@testing-library/react';
3
- import { describe, expect, it, vi } from 'vitest';
4
-
5
- import { messageService } from '@/services/message';
6
- import { imageGenerationService } from '@/services/textToImage';
7
- import { uploadService } from '@/services/upload';
8
- import { useChatStore } from '@/store/chat';
9
- import { chatSelectors } from '@/store/chat/selectors';
10
- import { useFileStore } from '@/store/file';
11
- import { DallEImageItem } from '@/types/tool/dalle';
12
-
13
- describe('chatToolSlice - dalle', () => {
14
- describe('generateImageFromPrompts', () => {
15
- it('should generate images from prompts, update items, and upload images', async () => {
16
- const { result } = renderHook(() => useChatStore());
17
-
18
- const initialMessageContent = JSON.stringify([
19
- { prompt: 'test prompt', previewUrl: 'old-url', imageId: 'old-id' },
20
- ]);
21
-
22
- vi.spyOn(chatSelectors, 'getMessageById').mockImplementationOnce(
23
- (id) => () =>
24
- ({
25
- id,
26
- content: initialMessageContent,
27
- }) as UIChatMessage,
28
- );
29
-
30
- const messageId = 'message-id';
31
- const prompts = [
32
- { prompt: 'test prompt 1' },
33
- { prompt: 'test prompt 2' },
34
- ] as DallEImageItem[];
35
- const mockUrl = 'https://example.com/image.png';
36
- const mockId = 'image-id';
37
-
38
- vi.spyOn(imageGenerationService, 'generateImage').mockResolvedValue(mockUrl);
39
- vi.spyOn(uploadService, 'getImageFileByUrlWithCORS').mockResolvedValue(
40
- new File(['1'], 'file.png', { type: 'image/png' }),
41
- );
42
-
43
- // Mock the new uploadWithProgress method from useFileStore
44
- vi.spyOn(useFileStore, 'getState').mockReturnValue({
45
- uploadWithProgress: vi.fn().mockResolvedValue({
46
- id: mockId,
47
- url: '',
48
- dimensions: { width: 512, height: 512 },
49
- filename: 'file.png',
50
- }),
51
- } as any);
52
-
53
- // Mock store methods that are called in the implementation
54
- vi.spyOn(result.current, 'toggleDallEImageLoading');
55
- vi.spyOn(result.current, 'updatePluginState').mockResolvedValue(undefined);
56
- vi.spyOn(result.current, 'internal_updateMessageContent').mockResolvedValue(undefined);
57
-
58
- await act(async () => {
59
- await result.current.generateImageFromPrompts(prompts, messageId);
60
- });
61
- // For each prompt, loading is toggled on and then off
62
- expect(imageGenerationService.generateImage).toHaveBeenCalledTimes(prompts.length);
63
- expect(useFileStore.getState().uploadWithProgress).toHaveBeenCalledTimes(prompts.length);
64
- expect(result.current.toggleDallEImageLoading).toHaveBeenCalledTimes(prompts.length * 2);
65
- });
66
- });
67
-
68
- describe('updateImageItem', () => {
69
- it('should update image item correctly', async () => {
70
- const { result } = renderHook(() => useChatStore());
71
- const messageId = 'message-id';
72
- const initialMessageContent = JSON.stringify([
73
- { prompt: 'test prompt', previewUrl: 'old-url', imageId: 'old-id' },
74
- ]);
75
- const updateFunction = (draft: any) => {
76
- draft[0].previewUrl = 'new-url';
77
- draft[0].imageId = 'new-id';
78
- };
79
- vi.spyOn(result.current, 'internal_updateMessageContent').mockResolvedValue(undefined);
80
-
81
- // 模拟 getMessageById 返回消息内容
82
- vi.spyOn(chatSelectors, 'getMessageById').mockImplementationOnce(
83
- (id) => () =>
84
- ({
85
- id,
86
- content: initialMessageContent,
87
- }) as UIChatMessage,
88
- );
89
- vi.spyOn(messageService, 'updateMessage').mockResolvedValueOnce(undefined);
90
-
91
- await act(async () => {
92
- await result.current.updateImageItem(messageId, updateFunction);
93
- });
94
-
95
- // 验证 internal_updateMessageContent 是否被正确调用以更新内容
96
- expect(result.current.internal_updateMessageContent).toHaveBeenCalledWith(
97
- messageId,
98
- JSON.stringify([{ prompt: 'test prompt', previewUrl: 'new-url', imageId: 'new-id' }]),
99
- );
100
- });
101
- });
102
-
103
- describe('text2image', () => {
104
- it('should call generateImageFromPrompts with provided data', async () => {
105
- const { result } = renderHook(() => useChatStore());
106
- const id = 'message-id';
107
- const data = [{ prompt: 'prompt 1' }, { prompt: 'prompt 2' }] as DallEImageItem[];
108
-
109
- // Mock generateImageFromPrompts
110
- const generateImageFromPromptsMock = vi
111
- .spyOn(result.current, 'generateImageFromPrompts')
112
- .mockResolvedValue(undefined);
113
-
114
- await act(async () => {
115
- await result.current.text2image(id, data);
116
- });
117
-
118
- expect(generateImageFromPromptsMock).toHaveBeenCalledWith(data, id);
119
- });
120
- });
121
- });
@@ -1,124 +0,0 @@
1
- import { produce } from 'immer';
2
- import pMap from 'p-map';
3
- import { SWRResponse } from 'swr';
4
- import { StateCreator } from 'zustand/vanilla';
5
-
6
- import { useClientDataSWR } from '@/libs/swr';
7
- import { fileService } from '@/services/file';
8
- import { imageGenerationService } from '@/services/textToImage';
9
- import { uploadService } from '@/services/upload';
10
- import { chatSelectors } from '@/store/chat/selectors';
11
- import { ChatStore } from '@/store/chat/store';
12
- import { useFileStore } from '@/store/file';
13
- import { DallEImageItem } from '@/types/tool/dalle';
14
- import { setNamespace } from '@/utils/storeDebug';
15
-
16
- const n = setNamespace('tool');
17
-
18
- const SWR_FETCH_KEY = 'FetchImageItem';
19
-
20
- export interface ChatDallEAction {
21
- generateImageFromPrompts: (items: DallEImageItem[], id: string) => Promise<void>;
22
- text2image: (id: string, data: DallEImageItem[]) => Promise<void>;
23
- toggleDallEImageLoading: (key: string, value: boolean) => void;
24
- updateImageItem: (id: string, updater: (data: DallEImageItem[]) => void) => Promise<void>;
25
- useFetchDalleImageItem: (id: string) => SWRResponse;
26
- }
27
-
28
- export const dalleSlice: StateCreator<
29
- ChatStore,
30
- [['zustand/devtools', never]],
31
- [],
32
- ChatDallEAction
33
- > = (set, get) => ({
34
- generateImageFromPrompts: async (items, messageId) => {
35
- const { toggleDallEImageLoading, updateImageItem } = get();
36
- // eslint-disable-next-line unicorn/consistent-function-scoping
37
- const getMessageById = (id: string) => chatSelectors.getMessageById(id)(get());
38
-
39
- const message = getMessageById(messageId);
40
- if (!message) return;
41
-
42
- const parent = getMessageById(message!.parentId!);
43
- const originPrompt = parent?.content;
44
- let errorArray: any[] = [];
45
-
46
- await pMap(items, async (params, index) => {
47
- toggleDallEImageLoading(messageId + params.prompt, true);
48
-
49
- let url = '';
50
- try {
51
- url = await imageGenerationService.generateImage(params);
52
- } catch (e) {
53
- toggleDallEImageLoading(messageId + params.prompt, false);
54
- errorArray[index] = e;
55
-
56
- await get().updatePluginState(messageId, { error: errorArray });
57
- }
58
-
59
- if (!url) return;
60
-
61
- await updateImageItem(messageId, (draft) => {
62
- draft[index].previewUrl = url;
63
- });
64
-
65
- toggleDallEImageLoading(messageId + params.prompt, false);
66
- const imageFile = await uploadService.getImageFileByUrlWithCORS(
67
- url,
68
- `${originPrompt || params.prompt}_${index}.png`,
69
- );
70
-
71
- const data = await useFileStore.getState().uploadWithProgress({
72
- file: imageFile,
73
- });
74
-
75
- if (!data) return;
76
-
77
- await updateImageItem(messageId, (draft) => {
78
- draft[index].imageId = data.id;
79
- draft[index].previewUrl = undefined;
80
- });
81
- });
82
- },
83
- text2image: async (id, data) => {
84
- // const isAutoGen = settingsSelectors.isDalleAutoGenerating(useGlobalStore.getState());
85
- // if (!isAutoGen) return;
86
-
87
- await get().generateImageFromPrompts(data, id);
88
- },
89
-
90
- toggleDallEImageLoading: (key, value) => {
91
- set(
92
- { dalleImageLoading: { ...get().dalleImageLoading, [key]: value } },
93
- false,
94
- n('toggleDallEImageLoading'),
95
- );
96
- },
97
-
98
- updateImageItem: async (id, updater) => {
99
- const message = chatSelectors.getMessageById(id)(get());
100
- if (!message) return;
101
-
102
- const data: DallEImageItem[] = JSON.parse(message.content);
103
-
104
- const nextContent = produce(data, updater);
105
- await get().internal_updateMessageContent(id, JSON.stringify(nextContent));
106
- },
107
-
108
- useFetchDalleImageItem: (id) =>
109
- useClientDataSWR([SWR_FETCH_KEY, id], async () => {
110
- const item = await fileService.getFile(id);
111
-
112
- set(
113
- produce((draft) => {
114
- if (draft.dalleImageMap[id]) return;
115
-
116
- draft.dalleImageMap[id] = item;
117
- }),
118
- false,
119
- n('useFetchFile'),
120
- );
121
-
122
- return item;
123
- }),
124
- });
@@ -1,60 +0,0 @@
1
- import { useResponsive } from 'antd-style';
2
- import { ReactNode, memo, useMemo } from 'react';
3
- import { Flexbox } from 'react-layout-kit';
4
-
5
- import Grid from '@/components/GalleyGrid/Grid';
6
-
7
- const MAX_SIZE_DESKTOP = 640;
8
- const MAX_SIZE_MOBILE = 280;
9
-
10
- interface GalleyGridProps<T = any> {
11
- items: T[];
12
- renderItem: (props: T) => ReactNode;
13
- }
14
-
15
- const GalleyGrid = memo<GalleyGridProps>(({ items, renderItem: Render }) => {
16
- const { mobile } = useResponsive();
17
-
18
- const { firstRow, lastRow } = useMemo(() => {
19
- if (items.length === 4) {
20
- return {
21
- firstRow: items.slice(0, 2),
22
- lastRow: items.slice(2, 4),
23
- };
24
- }
25
-
26
- const firstCol = items.length % 3 === 0 ? 3 : items.length % 3;
27
-
28
- return {
29
- firstRow: items.slice(0, firstCol),
30
- lastRow: items.slice(firstCol, items.length),
31
- };
32
- }, [items]);
33
-
34
- const { gap, max } = useMemo(
35
- () => ({
36
- gap: mobile ? 4 : 6,
37
- max: (mobile ? MAX_SIZE_MOBILE : MAX_SIZE_DESKTOP) * firstRow.length,
38
- }),
39
- [mobile],
40
- );
41
-
42
- return (
43
- <Flexbox gap={gap}>
44
- <Grid col={firstRow.length} gap={gap} max={max}>
45
- {firstRow.map((i, index) => (
46
- <Render {...i} index={index} key={index} />
47
- ))}
48
- </Grid>
49
- {lastRow.length > 0 && (
50
- <Grid col={lastRow.length > 2 ? 3 : lastRow.length} gap={gap} max={max}>
51
- {lastRow.map((i, index) => (
52
- <Render {...i} index={index} key={index} />
53
- ))}
54
- </Grid>
55
- )}
56
- </Flexbox>
57
- );
58
- });
59
-
60
- export default GalleyGrid;