@nocobase/portal-template-default 1.0.9 → 1.0.11

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 (338) 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/dist/assets/{LineDraw-C0m1GONT.js → LineDraw-iG-ADes-.js} +1 -1
  7. package/dist/assets/{SymbolDraw-CCospe5G.js → SymbolDraw-DXoSw1tM.js} +1 -1
  8. package/dist/assets/VisualMapping-C_ee4Eht.js +1 -0
  9. package/dist/assets/auth-method-demo-DaOCuvsq.js +1 -0
  10. package/dist/assets/auto-login-provider-5Uxza9Ms.js +2 -0
  11. package/dist/assets/auto-login-provider-DyaPGK2D.js +1 -0
  12. package/dist/assets/auto-redirect-provider-CfwRvYOI.js +1 -0
  13. package/dist/assets/auto-redirect-provider-Cr3jmYts.js +1 -0
  14. package/dist/assets/auto-redirect-provider-QkJQRBbG.js +1 -0
  15. package/dist/assets/calendar-DjDRfD5t.js +1 -0
  16. package/dist/assets/card-DuQBOS6A.js +1 -0
  17. package/dist/assets/cas-sign-in-button-CyqG1QME.js +1 -0
  18. package/dist/assets/components-D-ptYjX_.js +20 -0
  19. package/dist/assets/configuration-gate-C08mkkiX.js +1 -0
  20. package/dist/assets/{createClipPathFromCoordSys-uEftn1Mq.js → createClipPathFromCoordSys-wj34-b1B.js} +1 -1
  21. package/dist/assets/createSeriesDataSimply-D0A6SAVQ.js +1 -0
  22. package/dist/assets/customGraphicKeyframeAnimation-DWrhrHL_.js +3 -0
  23. package/dist/assets/demo-BYWHeKUe.js +1 -0
  24. package/dist/assets/demo-CPWoNtx8.js +1 -0
  25. package/dist/assets/demo-CyvJk5zV.js +1 -0
  26. package/dist/assets/demo-D4OiKplc.js +1 -0
  27. package/dist/assets/demo-DBueJg0m.js +1 -0
  28. package/dist/assets/demo-DkIhyiRK.js +1 -0
  29. package/dist/assets/demo-tpTjO0sC.js +1 -0
  30. package/dist/assets/dingtalk-sign-in-button-DSWsSruv.js +1 -0
  31. package/dist/assets/echarts-preview-DxHNfq1C.js +1 -0
  32. package/dist/assets/{echarts-runtime-CEgJbv68.js → echarts-runtime-D8kXn6ME.js} +3 -3
  33. package/dist/assets/{echarts-runtime-advanced-Buhm2BHz.js → echarts-runtime-advanced-QY8v95qK.js} +1 -1
  34. package/dist/assets/{echarts-runtime-common-DLAdBAIa.js → echarts-runtime-common-D-yqAK-r.js} +1 -1
  35. package/dist/assets/{echarts-runtime-components-ypnyLkYx.js → echarts-runtime-components-P4-i8jPU.js} +1 -1
  36. package/dist/assets/{echarts-runtime-hierarchy-ClStquCF.js → echarts-runtime-hierarchy-ByQZ-DbP.js} +1 -1
  37. package/dist/assets/floating-BFVEyPEp.js +15 -0
  38. package/dist/assets/index-BgCbGDMc.js +43 -0
  39. package/dist/assets/index-Btk35Nx-.js +23 -0
  40. package/dist/assets/index-CwNxd66J.js +1 -0
  41. package/dist/assets/index-MB-jfQld.js +686 -0
  42. package/dist/assets/index-NH3aqqQz.css +1 -0
  43. package/dist/assets/index-gFtCXH3o.js +58 -0
  44. package/dist/assets/index-um5hdKdZ.js +23 -0
  45. package/dist/assets/{labelHelper-C137W5gU.js → labelHelper-DQqRy3a_.js} +1 -1
  46. package/dist/assets/ldap-sign-in-form--6Gt3yGq.js +1 -0
  47. package/dist/assets/oidc-sign-in-button-CQdRcR_p.js +1 -0
  48. package/dist/assets/{page-context-Cxx11gzg.js → page-context-CnQ4GYM7.js} +10 -10
  49. package/dist/assets/prompt-card-B2RS4nt2.js +1 -0
  50. package/dist/assets/prompt-output-DZHRM2ft.js +1 -0
  51. package/dist/assets/saml-sign-in-button-BHL5a0JV.js +1 -0
  52. package/dist/assets/{sectorHelper-Fqgz3uEk.js → sectorHelper-DN_Vv6OL.js} +1 -1
  53. package/dist/assets/shortcut-C5x5LQWU.js +47 -0
  54. package/dist/assets/sms-sign-in-form-DTX9GV2I.js +1 -0
  55. package/dist/assets/{static.browser-CfQQzERe.js → static.browser-jI1psK0d.js} +8 -8
  56. package/dist/assets/{switch-DrxImI8F.js → switch-CRL8PmRI.js} +1 -1
  57. package/dist/assets/{tool-cards-BGfsY8JF.js → tool-cards-DFttLAfo.js} +6 -7
  58. package/dist/assets/use-authenticator-sign-in-CRGjzXju.js +1 -0
  59. package/dist/assets/use-dingtalk-sign-in-8WYzJ2jf.js +1 -0
  60. package/dist/assets/use-wecom-sign-in-C9j8wfCD.js +1 -0
  61. package/dist/assets/wecom-sign-in-button-mIS1zGSU.js +1 -0
  62. package/dist/index.html +2 -2
  63. package/dist/index.raw.html +5 -2
  64. package/package.json +3 -5
  65. package/scripts/build-html.mjs +12 -0
  66. package/src/App.css +2 -0
  67. package/src/App.tsx +35 -186
  68. package/src/app/development.tsx +154 -0
  69. package/src/app/extension.ts +5 -0
  70. package/src/app/extensions.tsx +18 -4
  71. package/src/app/routes.tsx +65 -0
  72. package/src/components/access-control/acl-bootstrap.tsx +11 -3
  73. package/src/components/access-control/acl-gate.tsx +7 -6
  74. package/src/components/access-control/can-access.tsx +17 -0
  75. package/src/components/access-control/navigate-to-accessible-resource.tsx +8 -3
  76. package/src/components/app-shell/sidebar.tsx +21 -5
  77. package/src/components/auth/auto-redirect-provider.tsx +2 -1
  78. package/src/extensions/nocobase-acl/README.md +74 -0
  79. package/{registry → src/extensions}/nocobase-acl/components/acl-boundary.tsx +22 -25
  80. package/{registry → src/extensions}/nocobase-acl/components/role-switcher.tsx +26 -23
  81. package/src/extensions/nocobase-acl/demo/acl-preview-provider.tsx +81 -0
  82. package/{registry → src/extensions}/nocobase-acl/demo/boundary-api.tsx +58 -0
  83. package/{registry → src/extensions}/nocobase-acl/demo/index.tsx +243 -105
  84. package/{registry → src/extensions}/nocobase-acl/demo/prompt-generator.tsx +3 -1
  85. package/src/extensions/nocobase-acl/extension.tsx +73 -0
  86. package/{registry → src/extensions}/nocobase-ai/README.md +21 -5
  87. package/{registry → src/extensions}/nocobase-ai/components/chat/chat-composer.tsx +3 -1
  88. package/{registry → src/extensions}/nocobase-ai/components/chat/chat-messages.tsx +38 -3
  89. package/{registry → src/extensions}/nocobase-ai/components/chat/tool-call-card.tsx +1 -9
  90. package/{registry → src/extensions}/nocobase-ai/components/index.ts +5 -0
  91. package/src/extensions/nocobase-ai/components/surfaces/chat-dialog.tsx +17 -0
  92. package/src/extensions/nocobase-ai/components/surfaces/chat-side-panel.tsx +35 -0
  93. package/src/extensions/nocobase-ai/components/surfaces/chat-surface.tsx +108 -0
  94. package/{registry → src/extensions}/nocobase-ai/components/triggers/ai-chat-floating-trigger.tsx +2 -2
  95. package/{registry → src/extensions}/nocobase-ai/demo/configuration-gate.tsx +4 -1
  96. package/{registry → src/extensions}/nocobase-ai/demo/container-showcase.tsx +2 -2
  97. package/{registry → src/extensions}/nocobase-ai/demo/floating.tsx +45 -15
  98. package/{registry → src/extensions}/nocobase-ai/demo/index.tsx +116 -74
  99. package/{registry → src/extensions}/nocobase-ai/demo/prompt-generator.tsx +3 -3
  100. package/{registry → src/extensions}/nocobase-ai/demo/tool-cards.tsx +2 -3
  101. package/src/extensions/nocobase-ai/extension.tsx +160 -0
  102. package/{registry → src/extensions}/nocobase-ai/global-ai-chat.tsx +12 -28
  103. package/{registry → src/extensions}/nocobase-ai/providers/ai-provider.tsx +14 -1
  104. package/{registry → src/extensions}/nocobase-ai/providers/chat-context.tsx +1 -0
  105. package/{registry → src/extensions}/nocobase-ai/providers/chat-provider.tsx +29 -8
  106. package/{registry → src/extensions}/nocobase-ai/providers/use-chat-attachments.ts +6 -1
  107. package/{registry → src/extensions}/nocobase-ai/providers/use-chat-work-context.ts +4 -1
  108. package/{registry → src/extensions}/nocobase-route-surfaces/README.md +2 -1
  109. package/{registry → src/extensions}/nocobase-route-surfaces/demo/index.tsx +33 -7
  110. package/{registry → src/extensions}/nocobase-route-surfaces/demo/scenarios.ts +20 -14
  111. package/{registry → src/extensions}/nocobase-route-surfaces/extension.tsx +55 -52
  112. package/src/{pages/users → extensions/nocobase-users-example}/create.tsx +4 -4
  113. package/src/{pages/users → extensions/nocobase-users-example}/edit.tsx +5 -9
  114. package/src/extensions/nocobase-users-example/extension.tsx +109 -0
  115. package/src/{pages/users → extensions/nocobase-users-example}/form-context.ts +11 -9
  116. package/src/extensions/nocobase-users-example/layout.tsx +16 -0
  117. package/src/{pages/users → extensions/nocobase-users-example}/list.tsx +1 -1
  118. package/src/extensions/nocobase-users-example/locales/en-US.ts +77 -0
  119. package/src/extensions/nocobase-users-example/locales/index.ts +8 -0
  120. package/src/extensions/nocobase-users-example/locales/zh-CN.ts +73 -0
  121. package/src/extensions/nocobase-users-example/optional-ai.ts +67 -0
  122. package/src/{pages/users → extensions/nocobase-users-example}/role-detail.tsx +4 -15
  123. package/src/{pages/users → extensions/nocobase-users-example}/routes.ts +1 -4
  124. package/src/extensions/nocobase-users-example/show.tsx +266 -0
  125. package/src/lib/nocobase/acl/context.ts +32 -0
  126. package/src/lib/nocobase/acl/evaluator.ts +186 -0
  127. package/src/lib/nocobase/acl/hooks.ts +40 -0
  128. package/src/lib/nocobase/acl/index.ts +4 -1
  129. package/src/lib/nocobase/acl/menu.ts +6 -6
  130. package/src/lib/nocobase/acl/record-permissions.ts +37 -7
  131. package/src/lib/nocobase/acl/store-provider.tsx +14 -0
  132. package/src/lib/nocobase/acl/store.ts +92 -86
  133. package/src/lib/nocobase/acl/types.ts +52 -32
  134. package/src/lib/nocobase/auth-session.ts +128 -0
  135. package/src/lib/nocobase/client.ts +48 -50
  136. package/src/locales/en-US.ts +0 -78
  137. package/src/locales/index.ts +2 -7
  138. package/src/locales/zh-CN.ts +0 -74
  139. package/src/providers/access-control.ts +43 -8
  140. package/src/providers/auth-callback.ts +1 -0
  141. package/src/providers/auth.ts +24 -26
  142. package/src/providers/constants.ts +0 -4
  143. package/src/providers/data.ts +14 -26
  144. package/src/providers/runtime-config.ts +40 -0
  145. package/src/vite-env.d.ts +9 -0
  146. package/tests/auth-session-regression.mjs +101 -0
  147. package/tsconfig.json +2 -1
  148. package/vite.config.ts +9 -1
  149. package/dist/assets/VisualMapping-Cbv1v7Cd.js +0 -1
  150. package/dist/assets/ai-employee-shortcut-B253WWow.js +0 -1
  151. package/dist/assets/calendar-B3dXXyIv.js +0 -1
  152. package/dist/assets/chat-inline-xpQ9iq4p.js +0 -1
  153. package/dist/assets/chat-side-panel-layout-BVWsp5rr.js +0 -1
  154. package/dist/assets/components-ChhJaNRc.js +0 -20
  155. package/dist/assets/configuration-gate-Dwdq_lxf.js +0 -1
  156. package/dist/assets/createSeriesDataSimply-CI7l7Ufv.js +0 -1
  157. package/dist/assets/customGraphicKeyframeAnimation-DE3jSfMd.js +0 -3
  158. package/dist/assets/echarts-preview-C-cNUtJF.js +0 -1
  159. package/dist/assets/floating-BarTmn-b.js +0 -13
  160. package/dist/assets/index-BJIcYTdc.js +0 -53
  161. package/dist/assets/index-C6xNKxpg.js +0 -656
  162. package/dist/assets/index-D4JQi1TM.js +0 -54
  163. package/dist/assets/index-YA7-AUO8.css +0 -1
  164. package/dist/assets/index-g8zWVpP9.js +0 -23
  165. package/dist/assets/prompt-card-BsYMW_rq.js +0 -1
  166. package/dist/assets/shortcut-xtghDP63.js +0 -47
  167. package/registry/nocobase-acl/README.md +0 -19
  168. package/registry/nocobase-acl/extension.tsx +0 -71
  169. package/registry/nocobase-acl/tests/acl-regression.mjs +0 -314
  170. package/registry/nocobase-ai/components/surfaces/chat-dialog.tsx +0 -31
  171. package/registry/nocobase-ai/components/surfaces/chat-side-panel.tsx +0 -74
  172. package/registry/nocobase-ai/extension.tsx +0 -158
  173. package/registry/nocobase-ai/tests/business-report-regression.mjs +0 -70
  174. package/registry/nocobase-ai/tests/form-filler-regression.mjs +0 -129
  175. package/registry/nocobase-ai/tests/frontend-tool-regression.mjs +0 -128
  176. package/registry/nocobase-ai/tests/page-context-regression.mjs +0 -48
  177. package/registry/nocobase-ai/tests/stream-regression.mjs +0 -207
  178. package/registry/nocobase-auth-cas/README.md +0 -6
  179. package/registry/nocobase-auth-cas/cas-sign-in-button.tsx +0 -41
  180. package/registry/nocobase-auth-cas/demo.tsx +0 -20
  181. package/registry/nocobase-auth-cas/extension.tsx +0 -46
  182. package/registry/nocobase-auth-cas/index.ts +0 -1
  183. package/registry/nocobase-auth-cas/use-cas-sign-in.ts +0 -29
  184. package/registry/nocobase-auth-dingtalk/README.md +0 -7
  185. package/registry/nocobase-auth-dingtalk/auto-login-provider.tsx +0 -154
  186. package/registry/nocobase-auth-dingtalk/demo.tsx +0 -23
  187. package/registry/nocobase-auth-dingtalk/dingtalk-sign-in-button.tsx +0 -45
  188. package/registry/nocobase-auth-dingtalk/extension.tsx +0 -55
  189. package/registry/nocobase-auth-dingtalk/index.ts +0 -4
  190. package/registry/nocobase-auth-dingtalk/use-dingtalk-sign-in.ts +0 -64
  191. package/registry/nocobase-auth-ldap/README.md +0 -6
  192. package/registry/nocobase-auth-ldap/demo.tsx +0 -21
  193. package/registry/nocobase-auth-ldap/extension.tsx +0 -46
  194. package/registry/nocobase-auth-ldap/index.ts +0 -1
  195. package/registry/nocobase-auth-ldap/ldap-sign-in-form.tsx +0 -69
  196. package/registry/nocobase-auth-ldap/use-ldap-sign-in.ts +0 -5
  197. package/registry/nocobase-auth-oidc/README.md +0 -11
  198. package/registry/nocobase-auth-oidc/auto-redirect-provider.tsx +0 -13
  199. package/registry/nocobase-auth-oidc/demo.tsx +0 -20
  200. package/registry/nocobase-auth-oidc/extension.tsx +0 -51
  201. package/registry/nocobase-auth-oidc/index.ts +0 -1
  202. package/registry/nocobase-auth-oidc/oidc-sign-in-button.tsx +0 -44
  203. package/registry/nocobase-auth-oidc/use-oidc-sign-in.ts +0 -58
  204. package/registry/nocobase-auth-saml/README.md +0 -6
  205. package/registry/nocobase-auth-saml/auto-redirect-provider.tsx +0 -13
  206. package/registry/nocobase-auth-saml/demo.tsx +0 -20
  207. package/registry/nocobase-auth-saml/extension.tsx +0 -51
  208. package/registry/nocobase-auth-saml/index.ts +0 -1
  209. package/registry/nocobase-auth-saml/saml-sign-in-button.tsx +0 -43
  210. package/registry/nocobase-auth-saml/use-saml-sign-in.ts +0 -51
  211. package/registry/nocobase-auth-sms/README.md +0 -7
  212. package/registry/nocobase-auth-sms/demo.tsx +0 -25
  213. package/registry/nocobase-auth-sms/extension.tsx +0 -46
  214. package/registry/nocobase-auth-sms/index.ts +0 -1
  215. package/registry/nocobase-auth-sms/sms-sign-in-form.tsx +0 -107
  216. package/registry/nocobase-auth-sms/use-sms-sign-in.ts +0 -88
  217. package/registry/nocobase-auth-wecom/README.md +0 -6
  218. package/registry/nocobase-auth-wecom/auto-login-provider.tsx +0 -21
  219. package/registry/nocobase-auth-wecom/demo.tsx +0 -23
  220. package/registry/nocobase-auth-wecom/extension.tsx +0 -49
  221. package/registry/nocobase-auth-wecom/index.ts +0 -1
  222. package/registry/nocobase-auth-wecom/use-wecom-sign-in.ts +0 -62
  223. package/registry/nocobase-auth-wecom/wecom-sign-in-button.tsx +0 -70
  224. package/registry/nocobase-i18n/README.md +0 -44
  225. package/registry/nocobase-i18n/components/index.ts +0 -1
  226. package/registry/nocobase-i18n/components/language-switcher.tsx +0 -138
  227. package/registry/nocobase-i18n/demo/index.tsx +0 -209
  228. package/registry/nocobase-i18n/demo/prompt-generator.tsx +0 -132
  229. package/registry/nocobase-i18n/extension.tsx +0 -45
  230. package/registry/nocobase-i18n/index.ts +0 -7
  231. package/registry/nocobase-i18n/locales/en-US.ts +0 -39
  232. package/registry/nocobase-i18n/locales/index.ts +0 -8
  233. package/registry/nocobase-i18n/locales/zh-CN.ts +0 -35
  234. package/registry/nocobase-i18n/provider.tsx +0 -44
  235. package/registry/nocobase-i18n/server-resources.ts +0 -166
  236. package/registry/nocobase-i18n/tests/i18n-regression.mjs +0 -51
  237. package/registry.config.json +0 -302
  238. package/scripts/registry.mjs +0 -164
  239. package/src/components/access-control/resource-access-guard.tsx +0 -21
  240. package/src/components/access-control/use-acl-runtime.ts +0 -28
  241. package/src/lib/nocobase/acl/action.ts +0 -147
  242. package/src/pages/users/layout.tsx +0 -15
  243. package/src/pages/users/show.tsx +0 -278
  244. /package/{registry → src/extensions}/nocobase-acl/components/index.ts +0 -0
  245. /package/{registry → src/extensions}/nocobase-acl/components/role-options.ts +0 -0
  246. /package/{registry → src/extensions}/nocobase-acl/demo/components.tsx +0 -0
  247. /package/{registry → src/extensions}/nocobase-acl/demo/lazy-route.tsx +0 -0
  248. /package/{registry → src/extensions}/nocobase-acl/demo/role-switcher-prompt-generator.tsx +0 -0
  249. /package/{registry → src/extensions}/nocobase-acl/demo/scenario-section.tsx +0 -0
  250. /package/{registry → src/extensions}/nocobase-acl/index.ts +0 -0
  251. /package/{registry → src/extensions}/nocobase-ai/adapters/react-hook-form.ts +0 -0
  252. /package/{registry → src/extensions}/nocobase-ai/assets/nocobase-ai-chat.svg +0 -0
  253. /package/{registry → src/extensions}/nocobase-ai/components/ai-root-provider.tsx +0 -0
  254. /package/{registry → src/extensions}/nocobase-ai/components/chat/ai-employee-avatar.tsx +0 -0
  255. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-attachment.tsx +0 -0
  256. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-compact.tsx +0 -0
  257. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-empty-state.tsx +0 -0
  258. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-header.tsx +0 -0
  259. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-history-dialog.tsx +0 -0
  260. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-message.tsx +0 -0
  261. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-window.tsx +0 -0
  262. /package/{registry → src/extensions}/nocobase-ai/components/chat/conversation-list.tsx +0 -0
  263. /package/{registry → src/extensions}/nocobase-ai/components/chat/markdown-message.tsx +0 -0
  264. /package/{registry → src/extensions}/nocobase-ai/components/chat/model-select-options.tsx +0 -0
  265. /package/{registry → src/extensions}/nocobase-ai/components/chat/reasoning-panel.tsx +0 -0
  266. /package/{registry → src/extensions}/nocobase-ai/components/chat/sub-agent-conversation.tsx +0 -0
  267. /package/{registry → src/extensions}/nocobase-ai/components/chat/user-prompt-editor.tsx +0 -0
  268. /package/{registry → src/extensions}/nocobase-ai/components/chat/work-context-chip.tsx +0 -0
  269. /package/{registry → src/extensions}/nocobase-ai/components/page-elements/ai-form.tsx +0 -0
  270. /package/{registry → src/extensions}/nocobase-ai/components/page-elements/page-element-provider.tsx +0 -0
  271. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-inline.tsx +0 -0
  272. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-page.tsx +0 -0
  273. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-side-panel-layout.tsx +0 -0
  274. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-surface-actions.tsx +0 -0
  275. /package/{registry → src/extensions}/nocobase-ai/components/tools/builtin-tool-renderers.tsx +0 -0
  276. /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-dialog.tsx +0 -0
  277. /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-renderer.tsx +0 -0
  278. /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-utils.tsx +0 -0
  279. /package/{registry → src/extensions}/nocobase-ai/components/tools/chart-renderer.tsx +0 -0
  280. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-preview.tsx +0 -0
  281. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-advanced.ts +0 -0
  282. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-common.ts +0 -0
  283. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-components.ts +0 -0
  284. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-hierarchy.ts +0 -0
  285. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime.ts +0 -0
  286. /package/{registry → src/extensions}/nocobase-ai/components/tools/sub-agent-renderer.tsx +0 -0
  287. /package/{registry → src/extensions}/nocobase-ai/components/tools/suggestions-renderer.tsx +0 -0
  288. /package/{registry → src/extensions}/nocobase-ai/components/tools/tool-renderer-provider.tsx +0 -0
  289. /package/{registry → src/extensions}/nocobase-ai/components/tools/tool-renderer-utils.ts +0 -0
  290. /package/{registry → src/extensions}/nocobase-ai/components/tools/workflow-renderer.tsx +0 -0
  291. /package/{registry → src/extensions}/nocobase-ai/components/triggers/ai-employee-shortcut.tsx +0 -0
  292. /package/{registry → src/extensions}/nocobase-ai/demo/interaction-showcase.tsx +0 -0
  293. /package/{registry → src/extensions}/nocobase-ai/demo/page-context-prompt-generator.tsx +0 -0
  294. /package/{registry → src/extensions}/nocobase-ai/demo/page-context.tsx +0 -0
  295. /package/{registry → src/extensions}/nocobase-ai/demo/page-element-showcase.tsx +0 -0
  296. /package/{registry → src/extensions}/nocobase-ai/demo/prompt-card.tsx +0 -0
  297. /package/{registry → src/extensions}/nocobase-ai/demo/shortcut.tsx +0 -0
  298. /package/{registry → src/extensions}/nocobase-ai/index.ts +0 -0
  299. /package/{registry → src/extensions}/nocobase-ai/providers/avatars.ts +0 -0
  300. /package/{registry → src/extensions}/nocobase-ai/providers/chat-controller.ts +0 -0
  301. /package/{registry → src/extensions}/nocobase-ai/providers/chat-message-utils.ts +0 -0
  302. /package/{registry → src/extensions}/nocobase-ai/providers/chat-reducer.ts +0 -0
  303. /package/{registry → src/extensions}/nocobase-ai/providers/chat-task-utils.ts +0 -0
  304. /package/{registry → src/extensions}/nocobase-ai/providers/chat-transport.ts +0 -0
  305. /package/{registry → src/extensions}/nocobase-ai/providers/form-registry.tsx +0 -0
  306. /package/{registry → src/extensions}/nocobase-ai/providers/frontend-tool-registry.tsx +0 -0
  307. /package/{registry → src/extensions}/nocobase-ai/providers/index.ts +0 -0
  308. /package/{registry → src/extensions}/nocobase-ai/providers/model.ts +0 -0
  309. /package/{registry → src/extensions}/nocobase-ai/providers/page-context.tsx +0 -0
  310. /package/{registry → src/extensions}/nocobase-ai/providers/stream-coalescer.ts +0 -0
  311. /package/{registry → src/extensions}/nocobase-ai/providers/stream-event-utils.ts +0 -0
  312. /package/{registry → src/extensions}/nocobase-ai/providers/stream-parser.ts +0 -0
  313. /package/{registry → src/extensions}/nocobase-ai/providers/sub-agent-stream.ts +0 -0
  314. /package/{registry → src/extensions}/nocobase-ai/providers/types.ts +0 -0
  315. /package/{registry → src/extensions}/nocobase-ai/providers/ui-message-stream.ts +0 -0
  316. /package/{registry → src/extensions}/nocobase-ai/providers/use-automatic-tool-approval.ts +0 -0
  317. /package/{registry → src/extensions}/nocobase-ai/providers/use-chat-message-actions.ts +0 -0
  318. /package/{registry → src/extensions}/nocobase-ai/providers/use-chat-runtime.ts +0 -0
  319. /package/{registry → src/extensions}/nocobase-ai/providers/use-conversation-catalog.ts +0 -0
  320. /package/{registry → src/extensions}/nocobase-ai/providers/use-conversation-history.ts +0 -0
  321. /package/{registry → src/extensions}/nocobase-ai/services/index.ts +0 -0
  322. /package/{registry → src/extensions}/nocobase-ai/services/nocobase-ai-service.ts +0 -0
  323. /package/{registry → src/extensions}/nocobase-ai/services/types.ts +0 -0
  324. /package/{registry → src/extensions}/nocobase-route-surfaces/demo/lazy-route.tsx +0 -0
  325. /package/{registry → src/extensions}/nocobase-route-surfaces/demo/prompt-generator.tsx +0 -0
  326. /package/{registry → src/extensions}/nocobase-route-surfaces/index.ts +0 -0
  327. /package/{registry → src/extensions}/nocobase-route-surfaces/route-dialog.tsx +0 -0
  328. /package/{registry → src/extensions}/nocobase-route-surfaces/route-drawer.tsx +0 -0
  329. /package/{registry → src/extensions}/nocobase-route-surfaces/route-page.tsx +0 -0
  330. /package/{registry → src/extensions}/nocobase-route-surfaces/route-surface-context.ts +0 -0
  331. /package/{registry → src/extensions}/nocobase-route-surfaces/use-refine-unsaved-changes.tsx +0 -0
  332. /package/{registry → src/extensions}/nocobase-route-surfaces/use-route-surface-close.ts +0 -0
  333. /package/{registry → src/extensions}/nocobase-route-surfaces/use-route-surface-state.ts +0 -0
  334. /package/src/{pages/users → extensions/nocobase-users-example}/form-fields.tsx +0 -0
  335. /package/src/{pages/users → extensions/nocobase-users-example}/index.ts +0 -0
  336. /package/src/{pages/users → extensions/nocobase-users-example}/role-badges.tsx +0 -0
  337. /package/src/{pages/users → extensions/nocobase-users-example}/role-utils.ts +0 -0
  338. /package/src/{pages/users → extensions/nocobase-users-example}/types.ts +0 -0
@@ -12,6 +12,7 @@ import {
12
12
 
13
13
  import { Badge } from "@/components/ui/badge";
14
14
  import { Button } from "@/components/ui/button";
15
+ import { CanAccess } from "@/components/access-control/can-access";
15
16
  import {
16
17
  Card,
17
18
  CardContent,
@@ -20,6 +21,9 @@ import {
20
21
  CardTitle,
21
22
  } from "@/components/ui/card";
22
23
  import { Input } from "@/components/ui/input";
24
+ import type { RoleConstraint } from "@/lib/nocobase/acl";
25
+ import { AclField, AclPage, AclRegion } from "../components/acl-boundary";
26
+ import { AclPreviewProvider } from "./acl-preview-provider";
23
27
  import { AclBoundaryApi } from "./boundary-api";
24
28
  import { AclScenarioSection } from "./scenario-section";
25
29
 
@@ -50,6 +54,55 @@ const demoUsers = [
50
54
  },
51
55
  ];
52
56
 
57
+ const usersRouteRoles = {
58
+ anyOf: ["admin"],
59
+ } satisfies RoleConstraint;
60
+
61
+ const allowedRegionPermissions = {
62
+ "users:list": {},
63
+ "departments:list": {},
64
+ };
65
+
66
+ const restrictedRegionPermissions = {
67
+ "users:list": {},
68
+ };
69
+
70
+ const allowedActionPermissions = {
71
+ "users:get": {},
72
+ "users:create": {},
73
+ "users:update": {},
74
+ "users:destroy": {},
75
+ };
76
+
77
+ const restrictedActionPermissions = {
78
+ "users:get": {},
79
+ "users:update": {},
80
+ "users:destroy": {},
81
+ };
82
+
83
+ const restrictedRecordPermissions = demoUsers.flatMap((user) => [
84
+ {
85
+ resource: "users",
86
+ action: "update",
87
+ id: user.id,
88
+ allowed: false,
89
+ },
90
+ {
91
+ resource: "users",
92
+ action: "destroy",
93
+ id: user.id,
94
+ allowed: false,
95
+ },
96
+ ]);
97
+
98
+ const editableFieldPermissions = {
99
+ "users:update": { fields: ["nickname", "email", "phone"] },
100
+ };
101
+
102
+ const restrictedFieldPermissions = {
103
+ "users:update": { fields: ["nickname"] },
104
+ };
105
+
53
106
  export function AclPatternsPage() {
54
107
  return (
55
108
  <div className="space-y-10 pb-12">
@@ -65,18 +118,19 @@ export function AclPatternsPage() {
65
118
 
66
119
  <AclScenarioSection
67
120
  eyebrow="Navigation permission"
68
- title="Inaccessible resources disappear from navigation"
69
- description="A denied collection is removed from the sidebar and its direct route is guarded. Other accessible navigation remains unchanged."
121
+ title="Role-restricted routes disappear from navigation"
122
+ description="A route outside the current effective role is removed from the sidebar and its direct URL is guarded. Other accessible navigation remains unchanged."
70
123
  prompt={{
71
124
  title: "Resource navigation",
72
125
  description:
73
126
  "Generate application resources, sidebar behavior, and guarded routes backed by NocoBase collections.",
74
127
  defaultScene: "User and role administration",
75
128
  defaultTarget:
76
- "Allow the Users resource but remove Roles from navigation and block its direct URL.",
77
- requirements: `- Mark collection resources with meta.acl.type = "collection".
129
+ "Allow administrators to open Users, but remove it from navigation and block its direct URL for other roles.",
130
+ requirements: `- Put role constraints on the route resource through meta.acl.roles using anyOf, allOf, or noneOf.
131
+ - Use meta.acl.type = "authenticated" when the route itself is role-controlled and its inner regions perform collection checks.
78
132
  - Let the Starter filter inaccessible sidebar items and choose the first accessible default route.
79
- - Wrap list, create, show, and edit routes with ResourceAccessGuard.
133
+ - Wrap list, create, show, and edit routes with the Starter CanAccess and provide an AccessDenied fallback.
80
134
  - Mark non-collection utility pages as authenticated, snippet, route, or acl: false instead of treating them as collections.
81
135
  - If a parent route is denied but an accessible child remains, navigation must continue to the child route.`,
82
136
  }}
@@ -84,8 +138,16 @@ export function AclPatternsPage() {
84
138
  <OutcomeComparison
85
139
  allowedTitle="Users permission granted"
86
140
  deniedTitle="Users permission denied"
87
- allowed={<NavigationPreview showUsers />}
88
- denied={<NavigationPreview showUsers={false} />}
141
+ allowed={
142
+ <AclPreviewProvider roles={["admin"]}>
143
+ <NavigationPreview />
144
+ </AclPreviewProvider>
145
+ }
146
+ denied={
147
+ <AclPreviewProvider roles={["member"]}>
148
+ <NavigationPreview />
149
+ </AclPreviewProvider>
150
+ }
89
151
  />
90
152
  </AclScenarioSection>
91
153
 
@@ -99,9 +161,10 @@ export function AclPatternsPage() {
99
161
  "Generate a complete page whose availability depends on NocoBase permissions.",
100
162
  defaultScene: "User directory and account administration page",
101
163
  defaultTarget:
102
- "Require users:list before rendering the complete page.",
164
+ "Allow administrators and user managers to render the complete page.",
103
165
  requirements: `- Wrap the complete business page with AclPage.
104
- - Use anyOf when one accessible collection is enough, or allOf when every permission is required.
166
+ - Use the AclPage roles prop with anyOf, allOf, or noneOf for page-level role access.
167
+ - Keep anyOf and allOf for resource permissions when the page also needs collection access.
105
168
  - Provide a useful page-level fallback instead of rendering an empty screen.
106
169
  - Keep each inner data region independently protected when the page combines multiple collections.`,
107
170
  }}
@@ -109,34 +172,56 @@ export function AclPatternsPage() {
109
172
  <OutcomeComparison
110
173
  allowedTitle="Page allowed"
111
174
  deniedTitle="Page denied"
112
- allowed={<UserDirectoryPreview />}
113
- denied={<PageDeniedPreview />}
175
+ allowed={
176
+ <AclPreviewProvider roles={["user-manager"]}>
177
+ <PagePermissionPreview />
178
+ </AclPreviewProvider>
179
+ }
180
+ denied={
181
+ <AclPreviewProvider roles={["member"]}>
182
+ <PagePermissionPreview />
183
+ </AclPreviewProvider>
184
+ }
114
185
  />
115
186
  </AclScenarioSection>
116
187
 
117
188
  <AclScenarioSection
118
189
  eyebrow="Region permission"
119
190
  title="Only the protected region changes"
120
- description="On a page with multiple data sources, denying Roles must not hide the Users table. The restricted panel can be hidden or replaced with a local explanation."
191
+ description="On a page with multiple resources, denying Departments hides only that panel and must not affect the Users table."
121
192
  prompt={{
122
193
  title: "Region permission",
123
194
  description:
124
- "Generate a multi-table page where each region follows its own collection ACL.",
125
- defaultScene: "Identity administration dashboard",
195
+ "Generate a multi-resource page where each region follows its own collection ACL.",
196
+ defaultScene: "Organization management dashboard",
126
197
  defaultTarget:
127
- "Keep Users visible while replacing the denied Roles panel with a local fallback.",
198
+ "Keep Users visible while hiding the denied Departments panel.",
128
199
  requirements: `- Keep the outer page visible when its page-level permission is satisfied.
129
200
  - Wrap every collection-backed panel in its own AclRegion.
130
201
  - Enable each data query only when the corresponding list permission is allowed.
131
- - Use fallback="hidden" for optional panels and fallback="forbidden" or a custom ReactNode when the missing region should be explained.
202
+ - Use fallback="hidden" so denied regions do not reveal unavailable resources or placeholders.
132
203
  - Never use permission for one collection to hide unrelated tables or controls.`,
133
204
  }}
134
205
  >
135
206
  <OutcomeComparison
136
207
  allowedTitle="Both regions allowed"
137
- deniedTitle="Roles region denied"
138
- allowed={<RegionPreview showRoles />}
139
- denied={<RegionPreview showRoles={false} />}
208
+ deniedTitle="Departments resource hidden"
209
+ allowed={
210
+ <AclPreviewProvider
211
+ roles={["member"]}
212
+ permissions={allowedRegionPermissions}
213
+ >
214
+ <RegionPreview />
215
+ </AclPreviewProvider>
216
+ }
217
+ denied={
218
+ <AclPreviewProvider
219
+ roles={["member"]}
220
+ permissions={restrictedRegionPermissions}
221
+ >
222
+ <RegionPreview />
223
+ </AclPreviewProvider>
224
+ }
140
225
  />
141
226
  </AclScenarioSection>
142
227
 
@@ -161,8 +246,23 @@ export function AclPatternsPage() {
161
246
  <OutcomeComparison
162
247
  allowedTitle="Create and row actions allowed"
163
248
  deniedTitle="Create, Update, and Delete denied"
164
- allowed={<ActionsPreview allowed />}
165
- denied={<ActionsPreview allowed={false} />}
249
+ allowed={
250
+ <AclPreviewProvider
251
+ roles={["admin"]}
252
+ permissions={allowedActionPermissions}
253
+ >
254
+ <ActionsPreview />
255
+ </AclPreviewProvider>
256
+ }
257
+ denied={
258
+ <AclPreviewProvider
259
+ roles={["viewer"]}
260
+ permissions={restrictedActionPermissions}
261
+ recordPermissions={restrictedRecordPermissions}
262
+ >
263
+ <ActionsPreview />
264
+ </AclPreviewProvider>
265
+ }
166
266
  />
167
267
  </AclScenarioSection>
168
268
 
@@ -187,8 +287,22 @@ export function AclPatternsPage() {
187
287
  <OutcomeComparison
188
288
  allowedTitle="All fields editable"
189
289
  deniedTitle="Email read-only and phone hidden"
190
- allowed={<ProfileFormPreview restricted={false} />}
191
- denied={<ProfileFormPreview restricted />}
290
+ allowed={
291
+ <AclPreviewProvider
292
+ roles={["admin"]}
293
+ permissions={editableFieldPermissions}
294
+ >
295
+ <ProfileFormPreview />
296
+ </AclPreviewProvider>
297
+ }
298
+ denied={
299
+ <AclPreviewProvider
300
+ roles={["member"]}
301
+ permissions={restrictedFieldPermissions}
302
+ >
303
+ <ProfileFormPreview />
304
+ </AclPreviewProvider>
305
+ }
192
306
  />
193
307
  </AclScenarioSection>
194
308
 
@@ -228,7 +342,7 @@ function OutcomeComparison({
228
342
  );
229
343
  }
230
344
 
231
- function NavigationPreview({ showUsers }: { showUsers: boolean }) {
345
+ function NavigationPreview() {
232
346
  return (
233
347
  <div className="grid min-h-48 grid-cols-[150px_minmax(0,1fr)] overflow-hidden rounded-xl border bg-background">
234
348
  <aside className="border-r bg-muted/35 p-3">
@@ -236,13 +350,24 @@ function NavigationPreview({ showUsers }: { showUsers: boolean }) {
236
350
  Workspace
237
351
  </div>
238
352
  <NavItem icon={<LayoutPanelTop />} label="Dashboard" />
239
- {showUsers ? (
353
+ <CanAccess roles={usersRouteRoles}>
240
354
  <NavItem icon={<UsersRound />} label="Users" active />
241
- ) : null}
355
+ </CanAccess>
242
356
  <NavItem icon={<UserRoundCog />} label="Roles" />
243
357
  </aside>
244
358
  <div className="flex items-center justify-center p-5 text-center">
245
- {showUsers ? (
359
+ <CanAccess
360
+ roles={usersRouteRoles}
361
+ fallback={
362
+ <div>
363
+ <LayoutPanelTop className="mx-auto size-7 text-muted-foreground" />
364
+ <div className="mt-2 font-medium">Dashboard</div>
365
+ <div className="mt-1 max-w-48 text-xs text-muted-foreground">
366
+ Users is absent from navigation and its direct route is guarded.
367
+ </div>
368
+ </div>
369
+ }
370
+ >
246
371
  <div>
247
372
  <UsersRound className="mx-auto size-7 text-primary" />
248
373
  <div className="mt-2 font-medium">Users</div>
@@ -250,15 +375,7 @@ function NavigationPreview({ showUsers }: { showUsers: boolean }) {
250
375
  Route is available
251
376
  </div>
252
377
  </div>
253
- ) : (
254
- <div>
255
- <LayoutPanelTop className="mx-auto size-7 text-muted-foreground" />
256
- <div className="mt-2 font-medium">Dashboard</div>
257
- <div className="mt-1 max-w-48 text-xs text-muted-foreground">
258
- Users is absent from navigation and its direct route is guarded.
259
- </div>
260
- </div>
261
- )}
378
+ </CanAccess>
262
379
  </div>
263
380
  </div>
264
381
  );
@@ -285,6 +402,17 @@ function NavItem({
285
402
  );
286
403
  }
287
404
 
405
+ function PagePermissionPreview() {
406
+ return (
407
+ <AclPage
408
+ roles={{ anyOf: ["admin", "user-manager"] }}
409
+ fallback={<PageDeniedPreview />}
410
+ >
411
+ <UserDirectoryPreview />
412
+ </AclPage>
413
+ );
414
+ }
415
+
288
416
  function UserDirectoryPreview() {
289
417
  return (
290
418
  <Card>
@@ -317,39 +445,35 @@ function PageDeniedPreview() {
317
445
  );
318
446
  }
319
447
 
320
- function RegionPreview({ showRoles }: { showRoles: boolean }) {
448
+ function RegionPreview() {
321
449
  return (
322
- <div
323
- className={`grid gap-3 rounded-xl border bg-background p-3 ${
324
- showRoles
325
- ? "md:grid-cols-[minmax(0,1.3fr)_minmax(180px,0.7fr)]"
326
- : "grid-cols-1"
327
- }`}
328
- >
329
- <Card size="sm">
330
- <CardHeader>
331
- <CardTitle>Users</CardTitle>
332
- </CardHeader>
333
- <CardContent>
334
- <CompactUsersTable limit={2} />
335
- </CardContent>
336
- </Card>
337
- {showRoles ? (
338
- <Card size="sm">
450
+ <div className="flex flex-col gap-3 rounded-xl border bg-background p-3 md:flex-row">
451
+ <AclRegion resource="users" action="list">
452
+ <Card size="sm" className="min-w-0 flex-1">
339
453
  <CardHeader>
340
- <CardTitle>Roles</CardTitle>
454
+ <CardTitle>Users</CardTitle>
455
+ </CardHeader>
456
+ <CardContent>
457
+ <CompactUsersTable limit={2} />
458
+ </CardContent>
459
+ </Card>
460
+ </AclRegion>
461
+ <AclRegion resource="departments" action="list">
462
+ <Card size="sm" className="md:w-56">
463
+ <CardHeader>
464
+ <CardTitle>Departments</CardTitle>
341
465
  </CardHeader>
342
466
  <CardContent className="space-y-2">
343
- <RoleRow title="Administrator" name="admin" />
344
- <RoleRow title="Member" name="member" />
467
+ <ResourceRow title="Engineering" name="engineering" />
468
+ <ResourceRow title="Operations" name="operations" />
345
469
  </CardContent>
346
470
  </Card>
347
- ) : null}
471
+ </AclRegion>
348
472
  </div>
349
473
  );
350
474
  }
351
475
 
352
- function ActionsPreview({ allowed }: { allowed: boolean }) {
476
+ function ActionsPreview() {
353
477
  return (
354
478
  <Card>
355
479
  <CardHeader className="flex-row items-center justify-between gap-3">
@@ -357,11 +481,11 @@ function ActionsPreview({ allowed }: { allowed: boolean }) {
357
481
  <CardTitle>Users</CardTitle>
358
482
  <CardDescription>Record actions</CardDescription>
359
483
  </div>
360
- {allowed ? (
484
+ <CanAccess resource="users" action="create">
361
485
  <Button size="sm">
362
486
  <Plus /> New user
363
487
  </Button>
364
- ) : null}
488
+ </CanAccess>
365
489
  </CardHeader>
366
490
  <CardContent className="overflow-x-auto">
367
491
  <table className="w-full min-w-[420px] text-sm">
@@ -379,31 +503,33 @@ function ActionsPreview({ allowed }: { allowed: boolean }) {
379
503
  <td className="py-3 text-muted-foreground">{user.role}</td>
380
504
  <td className="py-3">
381
505
  <div className="flex justify-end gap-1">
382
- <Button
383
- variant="ghost"
384
- size="icon-sm"
385
- aria-label="View user"
386
- >
387
- <Eye />
388
- </Button>
389
- {allowed ? (
390
- <>
391
- <Button
392
- variant="ghost"
393
- size="icon-sm"
394
- aria-label="Edit user"
395
- >
396
- <Pencil />
397
- </Button>
398
- <Button
399
- variant="ghost"
400
- size="icon-sm"
401
- aria-label="Delete user"
402
- >
403
- <Trash2 />
404
- </Button>
405
- </>
406
- ) : null}
506
+ <CanAccess resource="users" action="show" id={user.id}>
507
+ <Button
508
+ variant="ghost"
509
+ size="icon-sm"
510
+ aria-label="View user"
511
+ >
512
+ <Eye />
513
+ </Button>
514
+ </CanAccess>
515
+ <CanAccess resource="users" action="edit" id={user.id}>
516
+ <Button
517
+ variant="ghost"
518
+ size="icon-sm"
519
+ aria-label="Edit user"
520
+ >
521
+ <Pencil />
522
+ </Button>
523
+ </CanAccess>
524
+ <CanAccess resource="users" action="delete" id={user.id}>
525
+ <Button
526
+ variant="ghost"
527
+ size="icon-sm"
528
+ aria-label="Delete user"
529
+ >
530
+ <Trash2 />
531
+ </Button>
532
+ </CanAccess>
407
533
  </div>
408
534
  </td>
409
535
  </tr>
@@ -415,8 +541,9 @@ function ActionsPreview({ allowed }: { allowed: boolean }) {
415
541
  );
416
542
  }
417
543
 
418
- function ProfileFormPreview({ restricted }: { restricted: boolean }) {
544
+ function ProfileFormPreview() {
419
545
  const user = demoUsers[1];
546
+
420
547
  return (
421
548
  <Card>
422
549
  <CardHeader>
@@ -424,27 +551,38 @@ function ProfileFormPreview({ restricted }: { restricted: boolean }) {
424
551
  <CardDescription>{user.name}</CardDescription>
425
552
  </CardHeader>
426
553
  <CardContent className="space-y-4">
427
- <DemoField label="Nickname">
428
- <Input defaultValue={user.name} />
429
- </DemoField>
430
- <DemoField label="Email">
431
- {restricted ? (
432
- <div className="rounded-md border bg-muted/45 px-3 py-2 text-sm text-muted-foreground">
433
- {user.email}
434
- <Badge className="ml-2" variant="outline">
435
- Read only
436
- </Badge>
437
- </div>
438
- ) : (
554
+ <AclField resource="users" action="edit" field="nickname">
555
+ <DemoField label="Nickname">
556
+ <Input defaultValue={user.name} />
557
+ </DemoField>
558
+ </AclField>
559
+ <AclField
560
+ resource="users"
561
+ action="edit"
562
+ field="email"
563
+ fallback={
564
+ <DemoField label="Email">
565
+ <div className="rounded-md border bg-muted/45 px-3 py-2 text-sm text-muted-foreground">
566
+ {user.email}
567
+ <Badge className="ml-2" variant="outline">
568
+ Read only
569
+ </Badge>
570
+ </div>
571
+ </DemoField>
572
+ }
573
+ >
574
+ <DemoField label="Email">
439
575
  <Input defaultValue={user.email} />
440
- )}
441
- </DemoField>
442
- {!restricted ? (
576
+ </DemoField>
577
+ </AclField>
578
+ <AclField resource="users" action="edit" field="phone">
443
579
  <DemoField label="Phone">
444
580
  <Input defaultValue={user.phone} />
445
581
  </DemoField>
446
- ) : null}
447
- <Button size="sm">Save changes</Button>
582
+ </AclField>
583
+ <CanAccess resource="users" action="edit">
584
+ <Button size="sm">Save changes</Button>
585
+ </CanAccess>
448
586
  </CardContent>
449
587
  </Card>
450
588
  );
@@ -493,7 +631,7 @@ function CompactUsersTable({ limit = 3 }: { limit?: number }) {
493
631
  );
494
632
  }
495
633
 
496
- function RoleRow({ title, name }: { title: string; name: string }) {
634
+ function ResourceRow({ title, name }: { title: string; name: string }) {
497
635
  return (
498
636
  <div className="rounded-lg border px-3 py-2">
499
637
  <div className="text-sm font-medium">{title}</div>
@@ -43,7 +43,9 @@ ${config.requirements}
43
43
 
44
44
  Implementation contract
45
45
  - Use the Starter's built-in NocoBase accessControlProvider and ACL store.
46
- - NocoBase roles:check is the source of truth. Do not create a second permission store or hard-code role names.
46
+ - NocoBase roles:check is the source of truth. Do not create a second permission store. Use configured role names only when the requirement explicitly includes a role constraint.
47
+ - Import the Starter CanAccess from @/components/access-control/can-access. Use it with AclPage, AclRegion, and AclField for permission presentation. Do not copy their checks into local booleans or role comparisons.
48
+ - Use useGetRoles only when UI needs to display or pass through the current effective ACL roles; it is not a replacement permission evaluator.
47
49
  - Keep backend ACL enforcement in place; frontend checks only control presentation and navigation.
48
50
  - Import reusable components from the installed local entry point at @/extensions/nocobase-acl when page composition is needed.
49
51
  - Resource actions use the application names list, show, create, edit, and delete. Let the Starter map them to NocoBase list/get/create/update/destroy.
@@ -0,0 +1,73 @@
1
+ import type { AppExtension } from "@/app/extensions";
2
+ import { Blocks, PanelsTopLeft, ShieldCheck } from "lucide-react";
3
+ import { lazy } from "react";
4
+ import { Outlet, Route } from "react-router";
5
+ import { LazyAclRoute } from "./demo/lazy-route";
6
+
7
+ const AclComponentsPage = lazy(() =>
8
+ import("./demo/components").then((module) => ({
9
+ default: module.AclComponentsPage,
10
+ }))
11
+ );
12
+ const AclPatternsPage = lazy(() =>
13
+ import("./demo").then((module) => ({ default: module.AclPatternsPage }))
14
+ );
15
+
16
+ const nocobaseAclExtension: AppExtension = {
17
+ id: "nocobase-acl",
18
+ dev: {
19
+ resources: [
20
+ {
21
+ name: "acl-integration",
22
+ meta: {
23
+ label: "Access control",
24
+ icon: <ShieldCheck />,
25
+ description: "NocoBase ACL integration for admin applications.",
26
+ acl: { type: "authenticated" },
27
+ },
28
+ },
29
+ {
30
+ name: "acl-components",
31
+ list: "acl",
32
+ meta: {
33
+ parent: "acl-integration",
34
+ label: "Role switcher",
35
+ icon: <Blocks />,
36
+ acl: { type: "authenticated" },
37
+ },
38
+ },
39
+ {
40
+ name: "acl-patterns",
41
+ list: "acl/patterns",
42
+ meta: {
43
+ parent: "acl-integration",
44
+ label: "Permission patterns",
45
+ icon: <PanelsTopLeft />,
46
+ acl: { type: "authenticated" },
47
+ },
48
+ },
49
+ ],
50
+ routes: (
51
+ <Route key="nocobase-acl" path="acl" element={<Outlet />}>
52
+ <Route
53
+ index
54
+ element={
55
+ <LazyAclRoute>
56
+ <AclComponentsPage />
57
+ </LazyAclRoute>
58
+ }
59
+ />
60
+ <Route
61
+ path="patterns"
62
+ element={
63
+ <LazyAclRoute>
64
+ <AclPatternsPage />
65
+ </LazyAclRoute>
66
+ }
67
+ />
68
+ </Route>
69
+ ),
70
+ },
71
+ };
72
+
73
+ export default nocobaseAclExtension;
@@ -61,10 +61,26 @@ Frontend Tools are page-local browser actions and remain separate from Form fill
61
61
 
62
62
  Pass application-specific message renderers through `NocoBaseAIRootProvider.toolRenderers`. Built-in renderers remain available automatically. A custom renderer receives the Tool part, disabled state, edit/approve/reject actions, and a composer-focus callback.
63
63
 
64
- ## Local Registry development
64
+ ## Chat surfaces
65
65
 
66
- - `pnpm registry:build` regenerates `registry.json` and the static shadcn Registry output.
67
- - `pnpm registry:install` installs the Registry source only when the target directory is missing.
68
- - `pnpm registry:preview` intentionally replaces the local installed preview with the authoritative source.
66
+ Use one `AIChatWindow` inside `ChatSurface` when a global conversation can switch between a side panel and an expanded dialog. Change only the `variant` prop so the message list, composer, scroll position, and renderer state remain mounted:
69
67
 
70
- Normal install and `postinstall` never overwrite an existing `src/extensions/nocobase-ai` directory, so downstream applications can edit and commit installed source.
68
+ ```tsx
69
+ <ChatSurface
70
+ open={open}
71
+ variant={expanded ? "dialog" : "side-panel"}
72
+ onOpenChange={setOpen}
73
+ >
74
+ <AIChatWindow />
75
+ </ChatSurface>
76
+ ```
77
+
78
+ `ChatDialog` and `ChatSidePanel` remain available as convenience wrappers for fixed, non-switching placements.
79
+
80
+ ## Registry development
81
+
82
+ - In the Registry source repository, edit `registry/nocobase-ai` directly. The normal `pnpm dev` and `pnpm build` commands load canonical Registry source without a preview copy.
83
+ - Keep relative imports inside this Registry root. Use the host application's `@/` alias for application services and shared components.
84
+ - During publication, the release pipeline materializes default Registry items into the npm template's `src/extensions` directory.
85
+
86
+ Once installed in an application, `src/extensions/nocobase-ai` is application-owned source and can be edited and committed normally.
@@ -71,6 +71,7 @@ export function ChatComposer({
71
71
  employees,
72
72
  currentEmployee,
73
73
  currentModel,
74
+ canSend,
74
75
  selectModel,
75
76
  selectEmployee,
76
77
  composerFocusRequest,
@@ -193,7 +194,7 @@ export function ChatComposer({
193
194
  className="max-h-44 min-h-16 px-3 pt-3 text-sm"
194
195
  onChange={(event) => setDraft(event.target.value)}
195
196
  onPaste={(event) => {
196
- if (!enableAttachments) return;
197
+ if (!enableAttachments || !canSend) return;
197
198
  const files = Array.from(event.clipboardData.items)
198
199
  .filter((item) => item.kind === "file")
199
200
  .map((item) => item.getAsFile())
@@ -360,6 +361,7 @@ export function ChatComposer({
360
361
  className="rounded-lg"
361
362
  aria-label="Send message"
362
363
  disabled={
364
+ !canSend ||
363
365
  uploadingAttachments ||
364
366
  (!draft.trim() &&
365
367
  !attachments.some(