@nocobase/portal-template-default 1.0.8 → 1.0.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 (262) hide show
  1. package/.env.example +5 -0
  2. package/.github/workflows/publish.yml +106 -0
  3. package/.gitignore +33 -0
  4. package/AGENTS.md +19 -0
  5. package/README.MD +61 -180
  6. package/package.json +3 -5
  7. package/scripts/build-html.mjs +12 -0
  8. package/src/App.css +2 -0
  9. package/src/App.tsx +35 -186
  10. package/src/app/development.tsx +154 -0
  11. package/src/app/extension.ts +5 -0
  12. package/src/app/extensions.tsx +18 -4
  13. package/src/app/routes.tsx +65 -0
  14. package/src/components/access-control/acl-bootstrap.tsx +11 -3
  15. package/src/components/access-control/acl-gate.tsx +7 -6
  16. package/src/components/access-control/can-access.tsx +17 -0
  17. package/src/components/access-control/navigate-to-accessible-resource.tsx +8 -3
  18. package/src/components/app-shell/sidebar.tsx +21 -5
  19. package/src/components/auth/auto-redirect-provider.tsx +2 -1
  20. package/src/extensions/nocobase-acl/README.md +74 -0
  21. package/{registry → src/extensions}/nocobase-acl/components/acl-boundary.tsx +22 -25
  22. package/{registry → src/extensions}/nocobase-acl/components/role-switcher.tsx +26 -23
  23. package/src/extensions/nocobase-acl/demo/acl-preview-provider.tsx +81 -0
  24. package/{registry → src/extensions}/nocobase-acl/demo/boundary-api.tsx +58 -0
  25. package/{registry → src/extensions}/nocobase-acl/demo/index.tsx +243 -105
  26. package/{registry → src/extensions}/nocobase-acl/demo/prompt-generator.tsx +3 -1
  27. package/src/extensions/nocobase-acl/extension.tsx +73 -0
  28. package/{registry → src/extensions}/nocobase-ai/README.md +21 -5
  29. package/{registry → src/extensions}/nocobase-ai/components/chat/chat-composer.tsx +3 -1
  30. package/{registry → src/extensions}/nocobase-ai/components/chat/chat-messages.tsx +38 -3
  31. package/{registry → src/extensions}/nocobase-ai/components/chat/tool-call-card.tsx +1 -9
  32. package/{registry → src/extensions}/nocobase-ai/components/index.ts +5 -0
  33. package/src/extensions/nocobase-ai/components/surfaces/chat-dialog.tsx +17 -0
  34. package/src/extensions/nocobase-ai/components/surfaces/chat-side-panel.tsx +35 -0
  35. package/src/extensions/nocobase-ai/components/surfaces/chat-surface.tsx +108 -0
  36. package/{registry → src/extensions}/nocobase-ai/components/triggers/ai-chat-floating-trigger.tsx +2 -2
  37. package/{registry → src/extensions}/nocobase-ai/demo/configuration-gate.tsx +4 -1
  38. package/{registry → src/extensions}/nocobase-ai/demo/container-showcase.tsx +2 -2
  39. package/{registry → src/extensions}/nocobase-ai/demo/floating.tsx +45 -15
  40. package/{registry → src/extensions}/nocobase-ai/demo/index.tsx +116 -74
  41. package/{registry → src/extensions}/nocobase-ai/demo/prompt-generator.tsx +3 -3
  42. package/{registry → src/extensions}/nocobase-ai/demo/tool-cards.tsx +2 -3
  43. package/src/extensions/nocobase-ai/extension.tsx +160 -0
  44. package/{registry → src/extensions}/nocobase-ai/global-ai-chat.tsx +12 -28
  45. package/{registry → src/extensions}/nocobase-ai/providers/ai-provider.tsx +14 -1
  46. package/{registry → src/extensions}/nocobase-ai/providers/chat-context.tsx +1 -0
  47. package/{registry → src/extensions}/nocobase-ai/providers/chat-provider.tsx +29 -8
  48. package/{registry → src/extensions}/nocobase-ai/providers/use-chat-attachments.ts +6 -1
  49. package/{registry → src/extensions}/nocobase-ai/providers/use-chat-work-context.ts +4 -1
  50. package/{registry → src/extensions}/nocobase-route-surfaces/README.md +2 -1
  51. package/{registry → src/extensions}/nocobase-route-surfaces/demo/index.tsx +33 -7
  52. package/{registry → src/extensions}/nocobase-route-surfaces/demo/scenarios.ts +20 -14
  53. package/{registry → src/extensions}/nocobase-route-surfaces/extension.tsx +55 -52
  54. package/src/{pages/users → extensions/nocobase-users-example}/create.tsx +4 -4
  55. package/src/{pages/users → extensions/nocobase-users-example}/edit.tsx +5 -9
  56. package/src/extensions/nocobase-users-example/extension.tsx +109 -0
  57. package/src/{pages/users → extensions/nocobase-users-example}/form-context.ts +11 -9
  58. package/src/extensions/nocobase-users-example/layout.tsx +16 -0
  59. package/src/{pages/users → extensions/nocobase-users-example}/list.tsx +1 -1
  60. package/src/extensions/nocobase-users-example/locales/en-US.ts +77 -0
  61. package/src/extensions/nocobase-users-example/locales/index.ts +8 -0
  62. package/src/extensions/nocobase-users-example/locales/zh-CN.ts +73 -0
  63. package/src/extensions/nocobase-users-example/optional-ai.ts +67 -0
  64. package/src/{pages/users → extensions/nocobase-users-example}/role-detail.tsx +4 -15
  65. package/src/{pages/users → extensions/nocobase-users-example}/routes.ts +1 -4
  66. package/src/extensions/nocobase-users-example/show.tsx +266 -0
  67. package/src/lib/nocobase/acl/context.ts +32 -0
  68. package/src/lib/nocobase/acl/evaluator.ts +186 -0
  69. package/src/lib/nocobase/acl/hooks.ts +40 -0
  70. package/src/lib/nocobase/acl/index.ts +4 -1
  71. package/src/lib/nocobase/acl/menu.ts +6 -6
  72. package/src/lib/nocobase/acl/record-permissions.ts +37 -7
  73. package/src/lib/nocobase/acl/store-provider.tsx +14 -0
  74. package/src/lib/nocobase/acl/store.ts +92 -86
  75. package/src/lib/nocobase/acl/types.ts +52 -32
  76. package/src/lib/nocobase/auth-session.ts +128 -0
  77. package/src/lib/nocobase/client.ts +48 -50
  78. package/src/locales/en-US.ts +0 -78
  79. package/src/locales/index.ts +2 -7
  80. package/src/locales/zh-CN.ts +0 -74
  81. package/src/providers/access-control.ts +43 -8
  82. package/src/providers/auth-callback.ts +1 -0
  83. package/src/providers/auth.ts +24 -26
  84. package/src/providers/constants.ts +0 -4
  85. package/src/providers/data.ts +14 -26
  86. package/src/providers/runtime-config.ts +40 -0
  87. package/src/vite-env.d.ts +9 -0
  88. package/tests/auth-session-regression.mjs +101 -0
  89. package/tsconfig.json +2 -1
  90. package/vite.config.ts +9 -1
  91. package/registry/nocobase-acl/README.md +0 -19
  92. package/registry/nocobase-acl/extension.tsx +0 -71
  93. package/registry/nocobase-acl/tests/acl-regression.mjs +0 -314
  94. package/registry/nocobase-ai/components/surfaces/chat-dialog.tsx +0 -31
  95. package/registry/nocobase-ai/components/surfaces/chat-side-panel.tsx +0 -74
  96. package/registry/nocobase-ai/extension.tsx +0 -158
  97. package/registry/nocobase-ai/tests/business-report-regression.mjs +0 -70
  98. package/registry/nocobase-ai/tests/form-filler-regression.mjs +0 -129
  99. package/registry/nocobase-ai/tests/frontend-tool-regression.mjs +0 -128
  100. package/registry/nocobase-ai/tests/page-context-regression.mjs +0 -48
  101. package/registry/nocobase-ai/tests/stream-regression.mjs +0 -207
  102. package/registry/nocobase-auth-cas/README.md +0 -6
  103. package/registry/nocobase-auth-cas/cas-sign-in-button.tsx +0 -41
  104. package/registry/nocobase-auth-cas/demo.tsx +0 -20
  105. package/registry/nocobase-auth-cas/extension.tsx +0 -46
  106. package/registry/nocobase-auth-cas/index.ts +0 -1
  107. package/registry/nocobase-auth-cas/use-cas-sign-in.ts +0 -29
  108. package/registry/nocobase-auth-dingtalk/README.md +0 -7
  109. package/registry/nocobase-auth-dingtalk/auto-login-provider.tsx +0 -154
  110. package/registry/nocobase-auth-dingtalk/demo.tsx +0 -23
  111. package/registry/nocobase-auth-dingtalk/dingtalk-sign-in-button.tsx +0 -45
  112. package/registry/nocobase-auth-dingtalk/extension.tsx +0 -55
  113. package/registry/nocobase-auth-dingtalk/index.ts +0 -4
  114. package/registry/nocobase-auth-dingtalk/use-dingtalk-sign-in.ts +0 -64
  115. package/registry/nocobase-auth-ldap/README.md +0 -6
  116. package/registry/nocobase-auth-ldap/demo.tsx +0 -21
  117. package/registry/nocobase-auth-ldap/extension.tsx +0 -46
  118. package/registry/nocobase-auth-ldap/index.ts +0 -1
  119. package/registry/nocobase-auth-ldap/ldap-sign-in-form.tsx +0 -69
  120. package/registry/nocobase-auth-ldap/use-ldap-sign-in.ts +0 -5
  121. package/registry/nocobase-auth-oidc/README.md +0 -11
  122. package/registry/nocobase-auth-oidc/auto-redirect-provider.tsx +0 -13
  123. package/registry/nocobase-auth-oidc/demo.tsx +0 -20
  124. package/registry/nocobase-auth-oidc/extension.tsx +0 -51
  125. package/registry/nocobase-auth-oidc/index.ts +0 -1
  126. package/registry/nocobase-auth-oidc/oidc-sign-in-button.tsx +0 -44
  127. package/registry/nocobase-auth-oidc/use-oidc-sign-in.ts +0 -58
  128. package/registry/nocobase-auth-saml/README.md +0 -6
  129. package/registry/nocobase-auth-saml/auto-redirect-provider.tsx +0 -13
  130. package/registry/nocobase-auth-saml/demo.tsx +0 -20
  131. package/registry/nocobase-auth-saml/extension.tsx +0 -51
  132. package/registry/nocobase-auth-saml/index.ts +0 -1
  133. package/registry/nocobase-auth-saml/saml-sign-in-button.tsx +0 -43
  134. package/registry/nocobase-auth-saml/use-saml-sign-in.ts +0 -51
  135. package/registry/nocobase-auth-sms/README.md +0 -7
  136. package/registry/nocobase-auth-sms/demo.tsx +0 -25
  137. package/registry/nocobase-auth-sms/extension.tsx +0 -46
  138. package/registry/nocobase-auth-sms/index.ts +0 -1
  139. package/registry/nocobase-auth-sms/sms-sign-in-form.tsx +0 -107
  140. package/registry/nocobase-auth-sms/use-sms-sign-in.ts +0 -88
  141. package/registry/nocobase-auth-wecom/README.md +0 -6
  142. package/registry/nocobase-auth-wecom/auto-login-provider.tsx +0 -21
  143. package/registry/nocobase-auth-wecom/demo.tsx +0 -23
  144. package/registry/nocobase-auth-wecom/extension.tsx +0 -49
  145. package/registry/nocobase-auth-wecom/index.ts +0 -1
  146. package/registry/nocobase-auth-wecom/use-wecom-sign-in.ts +0 -62
  147. package/registry/nocobase-auth-wecom/wecom-sign-in-button.tsx +0 -70
  148. package/registry/nocobase-i18n/README.md +0 -44
  149. package/registry/nocobase-i18n/components/index.ts +0 -1
  150. package/registry/nocobase-i18n/components/language-switcher.tsx +0 -138
  151. package/registry/nocobase-i18n/demo/index.tsx +0 -209
  152. package/registry/nocobase-i18n/demo/prompt-generator.tsx +0 -132
  153. package/registry/nocobase-i18n/extension.tsx +0 -45
  154. package/registry/nocobase-i18n/index.ts +0 -7
  155. package/registry/nocobase-i18n/locales/en-US.ts +0 -39
  156. package/registry/nocobase-i18n/locales/index.ts +0 -8
  157. package/registry/nocobase-i18n/locales/zh-CN.ts +0 -35
  158. package/registry/nocobase-i18n/provider.tsx +0 -44
  159. package/registry/nocobase-i18n/server-resources.ts +0 -166
  160. package/registry/nocobase-i18n/tests/i18n-regression.mjs +0 -51
  161. package/registry.config.json +0 -302
  162. package/scripts/registry.mjs +0 -164
  163. package/src/components/access-control/resource-access-guard.tsx +0 -21
  164. package/src/components/access-control/use-acl-runtime.ts +0 -28
  165. package/src/lib/nocobase/acl/action.ts +0 -147
  166. package/src/pages/users/layout.tsx +0 -15
  167. package/src/pages/users/show.tsx +0 -278
  168. /package/{registry → src/extensions}/nocobase-acl/components/index.ts +0 -0
  169. /package/{registry → src/extensions}/nocobase-acl/components/role-options.ts +0 -0
  170. /package/{registry → src/extensions}/nocobase-acl/demo/components.tsx +0 -0
  171. /package/{registry → src/extensions}/nocobase-acl/demo/lazy-route.tsx +0 -0
  172. /package/{registry → src/extensions}/nocobase-acl/demo/role-switcher-prompt-generator.tsx +0 -0
  173. /package/{registry → src/extensions}/nocobase-acl/demo/scenario-section.tsx +0 -0
  174. /package/{registry → src/extensions}/nocobase-acl/index.ts +0 -0
  175. /package/{registry → src/extensions}/nocobase-ai/adapters/react-hook-form.ts +0 -0
  176. /package/{registry → src/extensions}/nocobase-ai/assets/nocobase-ai-chat.svg +0 -0
  177. /package/{registry → src/extensions}/nocobase-ai/components/ai-root-provider.tsx +0 -0
  178. /package/{registry → src/extensions}/nocobase-ai/components/chat/ai-employee-avatar.tsx +0 -0
  179. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-attachment.tsx +0 -0
  180. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-compact.tsx +0 -0
  181. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-empty-state.tsx +0 -0
  182. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-header.tsx +0 -0
  183. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-history-dialog.tsx +0 -0
  184. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-message.tsx +0 -0
  185. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-window.tsx +0 -0
  186. /package/{registry → src/extensions}/nocobase-ai/components/chat/conversation-list.tsx +0 -0
  187. /package/{registry → src/extensions}/nocobase-ai/components/chat/markdown-message.tsx +0 -0
  188. /package/{registry → src/extensions}/nocobase-ai/components/chat/model-select-options.tsx +0 -0
  189. /package/{registry → src/extensions}/nocobase-ai/components/chat/reasoning-panel.tsx +0 -0
  190. /package/{registry → src/extensions}/nocobase-ai/components/chat/sub-agent-conversation.tsx +0 -0
  191. /package/{registry → src/extensions}/nocobase-ai/components/chat/user-prompt-editor.tsx +0 -0
  192. /package/{registry → src/extensions}/nocobase-ai/components/chat/work-context-chip.tsx +0 -0
  193. /package/{registry → src/extensions}/nocobase-ai/components/page-elements/ai-form.tsx +0 -0
  194. /package/{registry → src/extensions}/nocobase-ai/components/page-elements/page-element-provider.tsx +0 -0
  195. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-inline.tsx +0 -0
  196. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-page.tsx +0 -0
  197. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-side-panel-layout.tsx +0 -0
  198. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-surface-actions.tsx +0 -0
  199. /package/{registry → src/extensions}/nocobase-ai/components/tools/builtin-tool-renderers.tsx +0 -0
  200. /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-dialog.tsx +0 -0
  201. /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-renderer.tsx +0 -0
  202. /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-utils.tsx +0 -0
  203. /package/{registry → src/extensions}/nocobase-ai/components/tools/chart-renderer.tsx +0 -0
  204. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-preview.tsx +0 -0
  205. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-advanced.ts +0 -0
  206. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-common.ts +0 -0
  207. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-components.ts +0 -0
  208. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-hierarchy.ts +0 -0
  209. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime.ts +0 -0
  210. /package/{registry → src/extensions}/nocobase-ai/components/tools/sub-agent-renderer.tsx +0 -0
  211. /package/{registry → src/extensions}/nocobase-ai/components/tools/suggestions-renderer.tsx +0 -0
  212. /package/{registry → src/extensions}/nocobase-ai/components/tools/tool-renderer-provider.tsx +0 -0
  213. /package/{registry → src/extensions}/nocobase-ai/components/tools/tool-renderer-utils.ts +0 -0
  214. /package/{registry → src/extensions}/nocobase-ai/components/tools/workflow-renderer.tsx +0 -0
  215. /package/{registry → src/extensions}/nocobase-ai/components/triggers/ai-employee-shortcut.tsx +0 -0
  216. /package/{registry → src/extensions}/nocobase-ai/demo/interaction-showcase.tsx +0 -0
  217. /package/{registry → src/extensions}/nocobase-ai/demo/page-context-prompt-generator.tsx +0 -0
  218. /package/{registry → src/extensions}/nocobase-ai/demo/page-context.tsx +0 -0
  219. /package/{registry → src/extensions}/nocobase-ai/demo/page-element-showcase.tsx +0 -0
  220. /package/{registry → src/extensions}/nocobase-ai/demo/prompt-card.tsx +0 -0
  221. /package/{registry → src/extensions}/nocobase-ai/demo/shortcut.tsx +0 -0
  222. /package/{registry → src/extensions}/nocobase-ai/index.ts +0 -0
  223. /package/{registry → src/extensions}/nocobase-ai/providers/avatars.ts +0 -0
  224. /package/{registry → src/extensions}/nocobase-ai/providers/chat-controller.ts +0 -0
  225. /package/{registry → src/extensions}/nocobase-ai/providers/chat-message-utils.ts +0 -0
  226. /package/{registry → src/extensions}/nocobase-ai/providers/chat-reducer.ts +0 -0
  227. /package/{registry → src/extensions}/nocobase-ai/providers/chat-task-utils.ts +0 -0
  228. /package/{registry → src/extensions}/nocobase-ai/providers/chat-transport.ts +0 -0
  229. /package/{registry → src/extensions}/nocobase-ai/providers/form-registry.tsx +0 -0
  230. /package/{registry → src/extensions}/nocobase-ai/providers/frontend-tool-registry.tsx +0 -0
  231. /package/{registry → src/extensions}/nocobase-ai/providers/index.ts +0 -0
  232. /package/{registry → src/extensions}/nocobase-ai/providers/model.ts +0 -0
  233. /package/{registry → src/extensions}/nocobase-ai/providers/page-context.tsx +0 -0
  234. /package/{registry → src/extensions}/nocobase-ai/providers/stream-coalescer.ts +0 -0
  235. /package/{registry → src/extensions}/nocobase-ai/providers/stream-event-utils.ts +0 -0
  236. /package/{registry → src/extensions}/nocobase-ai/providers/stream-parser.ts +0 -0
  237. /package/{registry → src/extensions}/nocobase-ai/providers/sub-agent-stream.ts +0 -0
  238. /package/{registry → src/extensions}/nocobase-ai/providers/types.ts +0 -0
  239. /package/{registry → src/extensions}/nocobase-ai/providers/ui-message-stream.ts +0 -0
  240. /package/{registry → src/extensions}/nocobase-ai/providers/use-automatic-tool-approval.ts +0 -0
  241. /package/{registry → src/extensions}/nocobase-ai/providers/use-chat-message-actions.ts +0 -0
  242. /package/{registry → src/extensions}/nocobase-ai/providers/use-chat-runtime.ts +0 -0
  243. /package/{registry → src/extensions}/nocobase-ai/providers/use-conversation-catalog.ts +0 -0
  244. /package/{registry → src/extensions}/nocobase-ai/providers/use-conversation-history.ts +0 -0
  245. /package/{registry → src/extensions}/nocobase-ai/services/index.ts +0 -0
  246. /package/{registry → src/extensions}/nocobase-ai/services/nocobase-ai-service.ts +0 -0
  247. /package/{registry → src/extensions}/nocobase-ai/services/types.ts +0 -0
  248. /package/{registry → src/extensions}/nocobase-route-surfaces/demo/lazy-route.tsx +0 -0
  249. /package/{registry → src/extensions}/nocobase-route-surfaces/demo/prompt-generator.tsx +0 -0
  250. /package/{registry → src/extensions}/nocobase-route-surfaces/index.ts +0 -0
  251. /package/{registry → src/extensions}/nocobase-route-surfaces/route-dialog.tsx +0 -0
  252. /package/{registry → src/extensions}/nocobase-route-surfaces/route-drawer.tsx +0 -0
  253. /package/{registry → src/extensions}/nocobase-route-surfaces/route-page.tsx +0 -0
  254. /package/{registry → src/extensions}/nocobase-route-surfaces/route-surface-context.ts +0 -0
  255. /package/{registry → src/extensions}/nocobase-route-surfaces/use-refine-unsaved-changes.tsx +0 -0
  256. /package/{registry → src/extensions}/nocobase-route-surfaces/use-route-surface-close.ts +0 -0
  257. /package/{registry → src/extensions}/nocobase-route-surfaces/use-route-surface-state.ts +0 -0
  258. /package/src/{pages/users → extensions/nocobase-users-example}/form-fields.tsx +0 -0
  259. /package/src/{pages/users → extensions/nocobase-users-example}/index.ts +0 -0
  260. /package/src/{pages/users → extensions/nocobase-users-example}/role-badges.tsx +0 -0
  261. /package/src/{pages/users → extensions/nocobase-users-example}/role-utils.ts +0 -0
  262. /package/src/{pages/users → extensions/nocobase-users-example}/types.ts +0 -0
@@ -1,314 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { createServer } from "vite";
3
-
4
- const server = await createServer({
5
- appType: "custom",
6
- logLevel: "silent",
7
- server: { middlewareMode: true },
8
- });
9
-
10
- try {
11
- const {
12
- canAccessWithSnapshot,
13
- getAclDataForDataSource,
14
- resolveActionPermission,
15
- } = await server.ssrLoadModule("/src/lib/nocobase/acl/action.ts");
16
- const { resolveAclDataSourceKey } = await server.ssrLoadModule(
17
- "/src/lib/nocobase/acl/data-source.ts"
18
- );
19
- const { filterMenuItemsByAcl, findFirstAccessibleRoute } =
20
- await server.ssrLoadModule("/src/lib/nocobase/acl/menu.ts");
21
- const {
22
- clearRecordPermissions,
23
- getRecordActionPermission,
24
- updateRecordPermissions,
25
- } = await server.ssrLoadModule("/src/lib/nocobase/acl/record-permissions.ts");
26
- const { RoleSwitcher } = await server.ssrLoadModule(
27
- "/registry/nocobase-acl/components/role-switcher.tsx"
28
- );
29
- const { getRoleOptions, resolveRoleTitle } = await server.ssrLoadModule(
30
- "/registry/nocobase-acl/components/role-options.ts"
31
- );
32
-
33
- const snapshot = {
34
- status: "ready",
35
- version: 1,
36
- data: {
37
- role: "editor",
38
- roleMode: "allow-use-union",
39
- resources: ["blog_posts"],
40
- actionAlias: {
41
- list: "view",
42
- get: "view",
43
- },
44
- actions: {
45
- "blog_posts:view": { fields: ["id", "title"] },
46
- "blog_posts:update": { fields: ["title"] },
47
- },
48
- strategy: {
49
- actions: ["view:all"],
50
- },
51
- snippets: ["pm.*"],
52
- },
53
- meta: {},
54
- };
55
-
56
- assert.deepEqual(
57
- resolveActionPermission({
58
- snapshot,
59
- resource: "blog_posts",
60
- action: "list",
61
- }),
62
- { fields: ["id", "title"] }
63
- );
64
- assert.equal(
65
- canAccessWithSnapshot(snapshot, {
66
- resource: "blog_posts",
67
- action: "create",
68
- }),
69
- false
70
- );
71
-
72
- const externalSnapshot = {
73
- ...snapshot,
74
- data: {
75
- ...snapshot.data,
76
- allowAll: true,
77
- actionAlias: { list: "view" },
78
- },
79
- meta: {
80
- dataSources: {
81
- analytics: {
82
- allowAll: false,
83
- resources: ["orders"],
84
- actionAlias: { list: "read" },
85
- actions: { "orders:read": {} },
86
- },
87
- },
88
- },
89
- };
90
- assert.deepEqual(getAclDataForDataSource(externalSnapshot, "analytics"), {
91
- ...externalSnapshot.data,
92
- allowAll: false,
93
- resources: ["orders"],
94
- actionAlias: { list: "read" },
95
- actions: { "orders:read": {} },
96
- snippets: externalSnapshot.data.snippets,
97
- });
98
- assert.equal(
99
- canAccessWithSnapshot(externalSnapshot, {
100
- resource: "orders",
101
- action: "list",
102
- params: { meta: { dataSourceKey: "analytics" } },
103
- }),
104
- true
105
- );
106
- assert.equal(
107
- resolveAclDataSourceKey({
108
- acl: {
109
- type: "collection",
110
- dataSourceKey: "analytics",
111
- },
112
- }),
113
- "analytics"
114
- );
115
- assert.equal(
116
- resolveAclDataSourceKey(
117
- { dataSourceKey: "reporting" },
118
- {
119
- acl: {
120
- type: "collection",
121
- dataSourceKey: "analytics",
122
- },
123
- }
124
- ),
125
- "reporting"
126
- );
127
- assert.equal(
128
- getRecordActionPermission({
129
- resource: "blog_posts",
130
- action: "update",
131
- id: 3,
132
- }),
133
- undefined
134
- );
135
- assert.equal(
136
- canAccessWithSnapshot(snapshot, {
137
- resource: "categories",
138
- action: "list",
139
- }),
140
- true
141
- );
142
- assert.equal(
143
- canAccessWithSnapshot(snapshot, {
144
- resource: "settings",
145
- action: "list",
146
- params: {
147
- resource: {
148
- name: "settings",
149
- meta: { acl: { type: "snippet", name: "*" } },
150
- },
151
- },
152
- }),
153
- true
154
- );
155
- assert.equal(
156
- canAccessWithSnapshot(
157
- {
158
- ...snapshot,
159
- data: {
160
- ...snapshot.data,
161
- snippets: ["pm", "pm.*", "!pm.data-source-manager*"],
162
- },
163
- },
164
- {
165
- resource: "settings",
166
- action: "list",
167
- params: {
168
- resource: {
169
- name: "settings",
170
- meta: {
171
- acl: { type: "snippet", name: "pm.data-source-manager" },
172
- },
173
- },
174
- },
175
- }
176
- ),
177
- false
178
- );
179
- assert.equal(
180
- updateRecordPermissions({
181
- resource: "blog_posts",
182
- recordIds: [4],
183
- allowedActions: { update: [] },
184
- }),
185
- true
186
- );
187
- assert.equal(
188
- getRecordActionPermission({
189
- resource: "blog_posts",
190
- action: "update",
191
- id: 4,
192
- }),
193
- false
194
- );
195
- assert.equal(
196
- canAccessWithSnapshot(snapshot, {
197
- resource: "settings",
198
- action: "list",
199
- params: {
200
- resource: {
201
- name: "settings",
202
- meta: { acl: { type: "snippet", name: "pm.acl" } },
203
- },
204
- },
205
- }),
206
- true
207
- );
208
- assert.equal(
209
- canAccessWithSnapshot(snapshot, {
210
- resource: "blog_posts",
211
- action: "edit",
212
- params: { field: "title" },
213
- }),
214
- true
215
- );
216
- assert.equal(
217
- canAccessWithSnapshot(snapshot, {
218
- resource: "blog_posts",
219
- action: "edit",
220
- params: { field: "status" },
221
- }),
222
- false
223
- );
224
-
225
- clearRecordPermissions();
226
- updateRecordPermissions({
227
- resource: "blog_posts",
228
- recordIds: [1, 2],
229
- allowedActions: {
230
- view: [1, 2],
231
- update: [1],
232
- destroy: [],
233
- },
234
- });
235
- assert.equal(
236
- canAccessWithSnapshot(snapshot, {
237
- resource: "blog_posts",
238
- action: "edit",
239
- params: { id: 1 },
240
- }),
241
- true
242
- );
243
- assert.equal(
244
- canAccessWithSnapshot(snapshot, {
245
- resource: "blog_posts",
246
- action: "edit",
247
- params: { id: 2 },
248
- }),
249
- false
250
- );
251
- assert.equal(
252
- updateRecordPermissions({
253
- resource: "blog_posts",
254
- recordIds: [1, 2],
255
- allowedActions: {
256
- view: [1, 2],
257
- update: [1],
258
- destroy: [],
259
- },
260
- }),
261
- false
262
- );
263
-
264
- const filteredMenu = filterMenuItemsByAcl(
265
- [
266
- {
267
- key: "restricted-parent",
268
- name: "restricted-parent",
269
- route: "/restricted-parent",
270
- children: [
271
- {
272
- key: "public-child",
273
- name: "public-child",
274
- route: "/public-child",
275
- meta: { acl: { type: "authenticated" } },
276
- children: [],
277
- },
278
- ],
279
- },
280
- ],
281
- {
282
- ...snapshot,
283
- data: { ...snapshot.data, strategy: { actions: [] } },
284
- }
285
- );
286
- assert.equal(filteredMenu[0].route, undefined);
287
- assert.equal(findFirstAccessibleRoute(filteredMenu), "/public-child");
288
-
289
- assert.deepEqual(
290
- getRoleOptions({
291
- roles: [
292
- { name: "admin", title: "Administrator" },
293
- { name: "member", title: "Member" },
294
- ],
295
- roleMode: "allow-use-union",
296
- allowAnonymous: true,
297
- }),
298
- [
299
- { name: "__union__", title: "Full permissions" },
300
- { name: "admin", title: "Administrator" },
301
- { name: "member", title: "Member" },
302
- { name: "anonymous", title: "Anonymous" },
303
- ]
304
- );
305
- assert.equal(
306
- resolveRoleTitle({ name: "admin", title: '{{t("Admin")}}' }),
307
- "Admin"
308
- );
309
-
310
- assert.equal(typeof RoleSwitcher, "function");
311
- console.log("NocoBase ACL regression tests passed");
312
- } finally {
313
- await server.close();
314
- }
@@ -1,31 +0,0 @@
1
- import {
2
- Dialog,
3
- DialogContent,
4
- DialogDescription,
5
- DialogTitle,
6
- } from "@/components/ui/dialog";
7
- import type { PropsWithChildren } from "react";
8
-
9
- export function ChatDialog({
10
- open,
11
- onOpenChange,
12
- children,
13
- }: PropsWithChildren<{
14
- open: boolean;
15
- onOpenChange: (open: boolean) => void;
16
- }>) {
17
- return (
18
- <Dialog open={open} onOpenChange={onOpenChange}>
19
- <DialogContent
20
- showCloseButton={false}
21
- className="h-[95svh] w-[95vw] max-w-[95vw] gap-0 overflow-hidden rounded-xl p-0 sm:max-w-[95vw]"
22
- >
23
- <DialogTitle className="sr-only">NocoBase AI employee</DialogTitle>
24
- <DialogDescription className="sr-only">
25
- Expanded AI conversation window.
26
- </DialogDescription>
27
- {children}
28
- </DialogContent>
29
- </Dialog>
30
- );
31
- }
@@ -1,74 +0,0 @@
1
- import { Button } from "@/components/ui/button";
2
- import { cn } from "@/lib/utils";
3
- import { PanelLeftClose, PanelRightClose } from "lucide-react";
4
- import { useEffect, type CSSProperties, type PropsWithChildren } from "react";
5
-
6
- export type ChatSidePanelProps = PropsWithChildren<{
7
- open: boolean;
8
- onOpenChange: (open: boolean) => void;
9
- side?: "left" | "right";
10
- width?: number | string;
11
- closeOnEscape?: boolean;
12
- showCloseHandle?: boolean;
13
- }>;
14
-
15
- export function ChatSidePanel({
16
- open,
17
- onOpenChange,
18
- side = "right",
19
- width = 450,
20
- closeOnEscape = true,
21
- showCloseHandle = true,
22
- children,
23
- }: ChatSidePanelProps) {
24
- useEffect(() => {
25
- if (!open || !closeOnEscape) return;
26
- const onKeyDown = (event: KeyboardEvent) => {
27
- if (event.key !== "Escape" || event.defaultPrevented) return;
28
- const openDialog = document.querySelector(
29
- '[role="dialog"][data-state="open"], [role="alertdialog"][data-state="open"]'
30
- );
31
- if (openDialog) return;
32
- onOpenChange(false);
33
- };
34
- window.addEventListener("keydown", onKeyDown);
35
- return () => window.removeEventListener("keydown", onKeyDown);
36
- }, [closeOnEscape, onOpenChange, open]);
37
-
38
- if (!open) return null;
39
-
40
- const panelWidth = typeof width === "number" ? `${width}px` : width;
41
-
42
- return (
43
- <aside
44
- role="complementary"
45
- aria-label="NocoBase AI employee"
46
- data-side={side}
47
- className={cn(
48
- "fixed inset-y-0 z-50 max-w-full overscroll-contain bg-background shadow-2xl animate-in duration-200",
49
- side === "right"
50
- ? "right-0 border-l slide-in-from-right"
51
- : "left-0 border-r slide-in-from-left"
52
- )}
53
- style={{ width: panelWidth } as CSSProperties}
54
- >
55
- {showCloseHandle ? (
56
- <Button
57
- variant="outline"
58
- size="icon-sm"
59
- className={cn(
60
- "absolute top-1/2 z-40 size-9 -translate-y-1/2 rounded-full bg-background shadow-md before:absolute before:-inset-2",
61
- side === "right"
62
- ? "left-0 -translate-x-1/2"
63
- : "right-0 translate-x-1/2"
64
- )}
65
- aria-label="Close side panel"
66
- onClick={() => onOpenChange(false)}
67
- >
68
- {side === "right" ? <PanelRightClose /> : <PanelLeftClose />}
69
- </Button>
70
- ) : null}
71
- {children}
72
- </aside>
73
- );
74
- }
@@ -1,158 +0,0 @@
1
- import { NocoBaseAIExtensionProvider } from "./global-ai-chat";
2
- import type { AppExtension } from "@/app/extension";
3
- import { LoadingState } from "@/components/app-shell/loading-state";
4
- import {
5
- Bot,
6
- MessageSquare,
7
- MousePointer2,
8
- PanelRight,
9
- Sparkles,
10
- Wrench,
11
- } from "lucide-react";
12
- import { lazy, Suspense, type ReactNode } from "react";
13
- import { Outlet, Route } from "react-router";
14
-
15
- const AIChatPage = lazy(() =>
16
- import("./demo").then((module) => ({ default: module.AIChatPage }))
17
- );
18
- const FloatingChatPage = lazy(() =>
19
- import("./demo/floating").then((module) => ({
20
- default: module.FloatingChatPage,
21
- }))
22
- );
23
- const ShortcutPage = lazy(() =>
24
- import("./demo/shortcut").then((module) => ({
25
- default: module.ShortcutPage,
26
- }))
27
- );
28
- const PageContextPage = lazy(() =>
29
- import("./demo/page-context").then((module) => ({
30
- default: module.PageContextPage,
31
- }))
32
- );
33
- const ToolCardsPage = lazy(() =>
34
- import("./demo/tool-cards").then((module) => ({
35
- default: module.ToolCardsPage,
36
- }))
37
- );
38
-
39
- function LazyDemoRoute({ children }: { children: ReactNode }) {
40
- return (
41
- <Suspense fallback={<LoadingState className="min-h-[320px]" />}>
42
- {children}
43
- </Suspense>
44
- );
45
- }
46
-
47
- const nocobaseAIExtension: AppExtension = {
48
- id: "nocobase-ai",
49
- Provider: NocoBaseAIExtensionProvider,
50
- resources: [
51
- {
52
- name: "ai-components",
53
- meta: {
54
- label: "AI Components",
55
- icon: <Bot />,
56
- acl: { type: "authenticated" },
57
- },
58
- },
59
- {
60
- name: "ai-chat-window",
61
- list: "/ai-chat",
62
- meta: {
63
- parent: "ai-components",
64
- label: "Chat window",
65
- icon: <MessageSquare />,
66
- description:
67
- "Build freely with AI while NocoBase keeps the application reliable.",
68
- acl: { type: "authenticated" },
69
- },
70
- },
71
- {
72
- name: "ai-floating-chat",
73
- list: "/ai-chat/floating",
74
- meta: {
75
- parent: "ai-components",
76
- label: "Floating chat",
77
- icon: <PanelRight />,
78
- acl: { type: "authenticated" },
79
- },
80
- },
81
- {
82
- name: "ai-employee-tasks",
83
- list: "/ai-chat/shortcut",
84
- meta: {
85
- parent: "ai-components",
86
- label: "Employee tasks",
87
- icon: <Sparkles />,
88
- acl: { type: "authenticated" },
89
- },
90
- },
91
- {
92
- name: "ai-page-context",
93
- list: "/ai-chat/context",
94
- meta: {
95
- parent: "ai-components",
96
- label: "Page context",
97
- icon: <MousePointer2 />,
98
- acl: { type: "authenticated" },
99
- },
100
- },
101
- {
102
- name: "ai-tool-cards",
103
- list: "/ai-chat/tools",
104
- meta: {
105
- parent: "ai-components",
106
- label: "Tool cards",
107
- icon: <Wrench />,
108
- acl: { type: "authenticated" },
109
- },
110
- },
111
- ],
112
- routes: (
113
- <Route key="nocobase-ai" path="/ai-chat" element={<Outlet />}>
114
- <Route
115
- index
116
- element={
117
- <LazyDemoRoute>
118
- <AIChatPage />
119
- </LazyDemoRoute>
120
- }
121
- />
122
- <Route
123
- path="floating"
124
- element={
125
- <LazyDemoRoute>
126
- <FloatingChatPage />
127
- </LazyDemoRoute>
128
- }
129
- />
130
- <Route
131
- path="shortcut"
132
- element={
133
- <LazyDemoRoute>
134
- <ShortcutPage />
135
- </LazyDemoRoute>
136
- }
137
- />
138
- <Route
139
- path="context"
140
- element={
141
- <LazyDemoRoute>
142
- <PageContextPage />
143
- </LazyDemoRoute>
144
- }
145
- />
146
- <Route
147
- path="tools"
148
- element={
149
- <LazyDemoRoute>
150
- <ToolCardsPage />
151
- </LazyDemoRoute>
152
- }
153
- />
154
- </Route>
155
- ),
156
- };
157
-
158
- export default nocobaseAIExtension;
@@ -1,70 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { createServer } from "vite";
3
-
4
- const server = await createServer({
5
- appType: "custom",
6
- logLevel: "silent",
7
- server: { middlewareMode: true },
8
- });
9
-
10
- try {
11
- const {
12
- buildBusinessReportMarkdown,
13
- renderBusinessReportMarkdownToHtml,
14
- splitBusinessReportMarkdown,
15
- } = await server.ssrLoadModule(
16
- "/registry/nocobase-ai/components/tools/business-report-utils.tsx"
17
- );
18
-
19
- const markdown = buildBusinessReportMarkdown({
20
- title: "Quarterly report",
21
- summary: "A complete report summary.",
22
- markdown: `## KPI overview
23
-
24
- | Metric | Value |
25
- | --- | ---: |
26
- | Revenue | 1286 |
27
-
28
- {{chart:1}}
29
-
30
- ## Trend analysis
31
-
32
- The trend continued after the first chart.
33
-
34
- {{chart:2}}
35
-
36
- ## Recommendations
37
-
38
- Keep the final section in the exported HTML.`,
39
- charts: [
40
- { title: "Trend", options: { series: [{ type: "line", data: [1, 2] }] } },
41
- { title: "Mix", options: { series: [{ type: "pie", data: [1, 2] }] } },
42
- ],
43
- });
44
- const parts = splitBusinessReportMarkdown(markdown);
45
-
46
- assert.equal(parts.length, 5);
47
- assert.equal(parts[0].type, "markdown");
48
- assert.match(parts[0].content, /Quarterly report/);
49
- assert.match(parts[0].content, /KPI overview/);
50
- assert.equal(parts[1].type, "chart");
51
- assert.match(parts[2].content, /Trend analysis/);
52
- assert.equal(parts[3].type, "chart");
53
- assert.match(parts[4].content, /Recommendations/);
54
-
55
- const firstSectionHtml = await renderBusinessReportMarkdownToHtml(
56
- parts[0].content
57
- );
58
- assert.match(firstSectionHtml, /<h1>Quarterly report<\/h1>/);
59
- assert.match(firstSectionHtml, /<h2>KPI overview<\/h2>/);
60
- assert.match(firstSectionHtml, /<table>/);
61
- assert.match(firstSectionHtml, /Revenue/);
62
-
63
- console.log("AI business report regression tests passed");
64
- } finally {
65
- await server.close();
66
- }
67
-
68
- // React's browser prerender runtime keeps an internal MessagePort alive in
69
- // Node even after the assertion and Vite server have completed.
70
- process.exit(0);