cabloy 5.1.95 → 5.1.97

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 (208) hide show
  1. package/.cabloy-version +1 -0
  2. package/.claude/skills/cabloy-backend-scaffold/references/follow-up-checklist.md +4 -1
  3. package/.claude/skills/cabloy-resource-field-update/SKILL.md +6 -3
  4. package/.github/workflows/vona-test-crud.yml +2 -0
  5. package/.github/workflows/vona-test-mysql.yml +2 -0
  6. package/.github/workflows/vona-test-pg.yml +2 -0
  7. package/.github/workflows/vona-test-sqlite3.yml +2 -0
  8. package/CHANGELOG.md +38 -0
  9. package/CLAUDE.md +1 -0
  10. package/cabloy-docs/ai/playbook-backend-module.md +6 -3
  11. package/cabloy-docs/fullstack/framework-performance.md +3 -3
  12. package/cabloy-docs/fullstack/quickstart.md +1 -1
  13. package/package.json +1 -1
  14. package/scripts/release.ts +27 -1
  15. package/vona/docker-compose-original/docker-compose-dockerfile-app +4 -0
  16. package/vona/docker-compose-original/docker-compose.original.yml +0 -1
  17. package/vona/env/.env +0 -1
  18. package/vona/env/.env.prod +0 -1
  19. package/vona/env/.env.prod.docker +0 -7
  20. package/vona/package.original.json +3 -3
  21. package/vona/packages-cli/cabloy-cli/package.json +2 -2
  22. package/vona/packages-cli/cli/package.json +1 -1
  23. package/vona/packages-cli/cli-set-api/package.json +2 -2
  24. package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.build.ts +70 -2
  25. package/vona/packages-utils/babel-plugin-vona-bean-module/package.json +2 -2
  26. package/vona/packages-utils/module-glob/package.json +2 -2
  27. package/vona/packages-utils/module-info-pro/package.json +2 -2
  28. package/vona/packages-utils/utils/package.json +2 -2
  29. package/vona/packages-vona/vona/package.json +1 -1
  30. package/vona/packages-vona/vona-core/package.json +2 -2
  31. package/vona/packages-vona/vona-core/src/lib/addon/addon.ts +77 -0
  32. package/vona/packages-vona/vona-core/src/lib/addon/index.ts +3 -0
  33. package/vona/packages-vona/vona-core/src/lib/addon/sharp.ts +46 -0
  34. package/vona/packages-vona/vona-core/src/lib/addon/sqlite3.ts +23 -0
  35. package/vona/packages-vona/vona-core/src/lib/index.ts +1 -0
  36. package/vona/packages-vona/vona-core/src/lib/utils/index.ts +0 -1
  37. package/vona/packages-vona/vona-core/src/lib/utils/util.ts +5 -0
  38. package/vona/packages-vona/vona-mock/package.json +2 -2
  39. package/vona/pnpm-lock.yaml +441 -62
  40. package/vona/pnpm-workspace.yaml +1 -1
  41. package/vona/src/backend/cli.ts +2 -5
  42. package/vona/src/backend/config/config/config.ts +0 -4
  43. package/vona/src/suite/a-demo/modules/demo-basic/src/bean/ssrMenu.admin.ts +1 -1
  44. package/vona/src/suite/a-training/modules/training-record/src/.metadata/index.ts +26 -0
  45. package/vona/src/suite/a-training/modules/training-record/src/bean/imageScene.sceneImage.ts +11 -0
  46. package/vona/src/suite/a-training/modules/training-record/src/bean/meta.version.ts +1 -0
  47. package/vona/src/suite/a-training/modules/training-record/src/config/locale/en-us.ts +1 -0
  48. package/vona/src/suite/a-training/modules/training-record/src/config/locale/zh-cn.ts +1 -0
  49. package/vona/src/suite/a-training/modules/training-record/src/dto/recordSelectResItem.tsx +12 -0
  50. package/vona/src/suite/a-training/modules/training-record/src/dto/recordView.tsx +12 -0
  51. package/vona/src/suite/a-training/modules/training-record/src/entity/record.tsx +26 -1
  52. package/vona/src/suite/a-training/modules/training-record/src/index.ts +1 -0
  53. package/vona/src/suite/a-training/modules/training-student/src/.metadata/index.ts +27 -1
  54. package/vona/src/suite/a-training/modules/training-student/src/bean/imageScene.studentImage.ts +10 -0
  55. package/vona/src/suite/a-training/modules/training-student/src/bean/meta.version.ts +1 -0
  56. package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +1 -0
  57. package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +1 -0
  58. package/vona/src/suite/a-training/modules/training-student/src/dto/detailRecordBase.tsx +16 -2
  59. package/vona/src/suite/a-training/modules/training-student/src/dto/studentCreate.tsx +1 -1
  60. package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectResItem.tsx +12 -0
  61. package/vona/src/suite/a-training/modules/training-student/src/dto/studentUpdate.tsx +1 -1
  62. package/vona/src/suite/a-training/modules/training-student/src/dto/studentView.tsx +13 -1
  63. package/vona/src/suite/a-training/modules/training-student/src/entity/student.tsx +26 -1
  64. package/vona/src/suite/a-training/modules/training-student/src/index.ts +1 -0
  65. package/vona/src/suite/a-training/modules/training-student/src/model/student.ts +1 -0
  66. package/vona/src/suite-vendor/a-auth/modules/a-auth/package.json +1 -1
  67. package/vona/src/suite-vendor/a-auth/modules/a-auth/src/bean/bean.authProvider.ts +2 -5
  68. package/vona/src/suite-vendor/a-auth/package.json +1 -1
  69. package/vona/src/suite-vendor/a-image/modules/a-image/cli/imageProvider/boilerplate/{{sceneName}}.{{beanName}}.ts_ +23 -0
  70. package/vona/src/suite-vendor/a-image/modules/a-image/cli/imageScene/boilerplate/{{sceneName}}.{{beanName}}.ts_ +5 -0
  71. package/vona/src/suite-vendor/a-image/modules/a-image/package.json +68 -0
  72. package/vona/src/suite-vendor/a-image/modules/a-image/src/.metadata/index.ts +482 -0
  73. package/vona/src/suite-vendor/a-image/modules/a-image/src/.metadata/locales.ts +18 -0
  74. package/vona/src/suite-vendor/a-image/modules/a-image/src/.metadata/this.ts +2 -0
  75. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/bean.image.ts +443 -0
  76. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/bean.imageProvider.ts +55 -0
  77. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/bean.imageUploadPolicy.ts +174 -0
  78. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/meta.redlock.ts +11 -0
  79. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/meta.version.ts +40 -0
  80. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/serializerTransform.resolveView.ts +44 -0
  81. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/serializerTransform.resolveViews.ts +44 -0
  82. package/vona/src/suite-vendor/a-image/modules/a-image/src/config/config.ts +17 -0
  83. package/vona/src/suite-vendor/a-image/modules/a-image/src/config/locale/en-us.ts +1 -0
  84. package/vona/src/suite-vendor/a-image/modules/a-image/src/config/locale/zh-cn.ts +1 -0
  85. package/vona/src/suite-vendor/a-image/modules/a-image/src/controller/image.ts +165 -0
  86. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDeliveryRequest.ts +20 -0
  87. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDirectUploadRequest.ts +36 -0
  88. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDirectUploadResponse.ts +38 -0
  89. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageTransformOptions.tsx +42 -0
  90. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadResponse.ts +62 -0
  91. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadTokenRequest.ts +24 -0
  92. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadTokenResponse.ts +16 -0
  93. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadUrlRequest.ts +33 -0
  94. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageView.ts +46 -0
  95. package/vona/src/suite-vendor/a-image/modules/a-image/src/entity/image.ts +58 -0
  96. package/vona/src/suite-vendor/a-image/modules/a-image/src/entity/imageProvider.ts +24 -0
  97. package/vona/src/suite-vendor/a-image/modules/a-image/src/index.ts +4 -0
  98. package/vona/src/suite-vendor/a-image/modules/a-image/src/lib/imageProvider.ts +9 -0
  99. package/vona/src/suite-vendor/a-image/modules/a-image/src/lib/imageScene.ts +7 -0
  100. package/vona/src/suite-vendor/a-image/modules/a-image/src/lib/imageVariant.ts +46 -0
  101. package/vona/src/suite-vendor/a-image/modules/a-image/src/lib/index.ts +3 -0
  102. package/vona/src/suite-vendor/a-image/modules/a-image/src/model/image.ts +10 -0
  103. package/vona/src/suite-vendor/a-image/modules/a-image/src/model/imageProvider.ts +10 -0
  104. package/vona/src/suite-vendor/a-image/modules/a-image/src/types/image.ts +208 -0
  105. package/vona/src/suite-vendor/a-image/modules/a-image/src/types/imageProvider.ts +110 -0
  106. package/vona/src/suite-vendor/a-image/modules/a-image/src/types/imageScene.ts +52 -0
  107. package/vona/src/suite-vendor/a-image/modules/a-image/src/types/index.ts +3 -0
  108. package/vona/src/suite-vendor/a-image/modules/a-image/tsconfig.build.json +11 -0
  109. package/vona/src/suite-vendor/a-image/modules/a-image/tsconfig.json +7 -0
  110. package/vona/src/suite-vendor/a-image/modules/image-cloudflare/package.json +49 -0
  111. package/vona/src/suite-vendor/a-image/modules/image-cloudflare/src/.metadata/index.ts +89 -0
  112. package/vona/src/suite-vendor/a-image/modules/image-cloudflare/src/.metadata/this.ts +2 -0
  113. package/vona/src/suite-vendor/a-image/modules/image-cloudflare/src/bean/imageProvider.cloudflare.ts +155 -0
  114. package/vona/src/suite-vendor/a-image/modules/image-cloudflare/src/index.ts +1 -0
  115. package/vona/src/suite-vendor/a-image/modules/image-cloudflare/src/lib/cloudflareImageUrl.ts +112 -0
  116. package/vona/src/suite-vendor/a-image/modules/image-cloudflare/src/service/imageCloudflare.ts +273 -0
  117. package/vona/src/suite-vendor/a-image/modules/image-cloudflare/tsconfig.build.json +11 -0
  118. package/vona/src/suite-vendor/a-image/modules/image-cloudflare/tsconfig.json +7 -0
  119. package/vona/src/suite-vendor/a-image/modules/image-native/package.json +63 -0
  120. package/vona/src/suite-vendor/a-image/modules/image-native/src/.metadata/index.ts +114 -0
  121. package/vona/src/suite-vendor/a-image/modules/image-native/src/.metadata/this.ts +2 -0
  122. package/vona/src/suite-vendor/a-image/modules/image-native/src/bean/imageProvider.native.ts +131 -0
  123. package/vona/src/suite-vendor/a-image/modules/image-native/src/config/config.ts +11 -0
  124. package/vona/src/suite-vendor/a-image/modules/image-native/src/index.ts +1 -0
  125. package/vona/src/suite-vendor/a-image/modules/image-native/src/monkey.ts +33 -0
  126. package/vona/src/suite-vendor/a-image/modules/image-native/src/service/imageNative.ts +291 -0
  127. package/vona/src/suite-vendor/a-image/modules/image-native/tsconfig.build.json +11 -0
  128. package/vona/src/suite-vendor/a-image/modules/image-native/tsconfig.json +7 -0
  129. package/vona/src/suite-vendor/a-image/package.json +14 -0
  130. package/vona/src/suite-vendor/a-image/tsconfig.base.json +4 -0
  131. package/vona/src/suite-vendor/a-image/tsconfig.json +16 -0
  132. package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +2 -2
  133. package/vona/src/suite-vendor/a-vona/modules/a-jwt/package.json +1 -1
  134. package/vona/src/suite-vendor/a-vona/modules/a-jwt/src/service/jwtClient.ts +5 -0
  135. package/vona/src/suite-vendor/a-vona/modules/a-jwt/src/types/jwt.ts +1 -0
  136. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/package.json +1 -1
  137. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/src/lib/schema/makeSchemaLikes.ts +6 -3
  138. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/src/lib/utils.ts +13 -7
  139. package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
  140. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/database.ts +0 -1
  141. package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/package.json +9 -1
  142. package/vona/src/suite-vendor/a-vona/modules/a-upload/package.json +1 -1
  143. package/vona/src/suite-vendor/a-vona/modules/a-upload/src/bean/interceptor.upload.ts +39 -7
  144. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  145. package/zova/packages-cli/cli/package.json +3 -3
  146. package/zova/packages-cli/cli-set-front/package.json +9 -9
  147. package/zova/packages-cli/cli-set-front/src/lib/bean/cli.openapi.generate.ts +20 -10
  148. package/zova/packages-utils/babel-plugin-zova-bean-module/package.json +2 -2
  149. package/zova/packages-utils/babel-plugin-zova-bean-use/package.json +2 -2
  150. package/zova/packages-utils/babel-plugin-zova-behavior/package.json +2 -2
  151. package/zova/packages-utils/babel-plugin-zova-component/package.json +2 -2
  152. package/zova/packages-utils/zova-jsx/package.json +3 -3
  153. package/zova/packages-utils/zova-vite/package.json +7 -7
  154. package/zova/packages-zova/zova/package.json +3 -3
  155. package/zova/packages-zova/zova-core/package.json +3 -3
  156. package/zova/pnpm-lock.yaml +126 -74
  157. package/zova/src/suite/a-home/modules/home-api/src/api/captcha.ts +3 -1
  158. package/zova/src/suite/a-home/modules/home-api/src/api/home.ts +3 -1
  159. package/zova/src/suite/a-home/modules/home-api/src/api/homeBaseMenu.ts +3 -1
  160. package/zova/src/suite/a-home/modules/home-api/src/api/homeBasePermission.ts +3 -1
  161. package/zova/src/suite/a-home/modules/home-api/src/api/homeUserPassport.ts +22 -20
  162. package/zova/src/suite/a-home/modules/home-api/src/api/openapi/schemas.ts +163 -53
  163. package/zova/src/suite/a-home/modules/home-api/src/api/openapi/types.ts +2060 -675
  164. package/zova/src/suite/a-home/modules/home-api/src/api/testSsrToolOne.ts +3 -1
  165. package/zova/src/suite/a-home/modules/home-api/src/apiSchema/captcha.ts +3 -1
  166. package/zova/src/suite/a-home/modules/home-api/src/apiSchema/home.ts +3 -1
  167. package/zova/src/suite/a-home/modules/home-api/src/apiSchema/homeBaseMenu.ts +3 -1
  168. package/zova/src/suite/a-home/modules/home-api/src/apiSchema/homeBasePermission.ts +3 -1
  169. package/zova/src/suite/a-home/modules/home-api/src/apiSchema/homeUserPassport.ts +13 -11
  170. package/zova/src/suite/a-home/modules/home-api/src/apiSchema/testSsrToolOne.ts +3 -1
  171. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/schemas.ts +54 -16
  172. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/types.ts +468 -8
  173. package/zova/src/suite/a-training/modules/training-student/src/apiSchema/trainingStudent.ts +3 -1
  174. package/zova/src/suite/cabloy-basic/modules/basic-details/src/bean/tableCell.actionUpdate.tsx +1 -1
  175. package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/actionCreate/controller.tsx +1 -1
  176. package/zova/src/suite/cabloy-basic/modules/basic-details/src/service/detail.tsx +53 -1
  177. package/zova/src/suite/cabloy-basic/modules/basic-image/cli/openapi.config.ts +9 -0
  178. package/zova/src/suite/cabloy-basic/modules/basic-image/package.json +60 -0
  179. package/zova/src/suite/cabloy-basic/modules/basic-image/src/.metadata/component/formFieldImage.ts +31 -0
  180. package/zova/src/suite/cabloy-basic/modules/basic-image/src/.metadata/index.ts +191 -0
  181. package/zova/src/suite/cabloy-basic/modules/basic-image/src/.metadata/locales.ts +7 -0
  182. package/zova/src/suite/cabloy-basic/modules/basic-image/src/.metadata/this.ts +2 -0
  183. package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/image.ts +104 -0
  184. package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/openapi/baseURL.ts +5 -0
  185. package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/openapi/index.ts +3 -0
  186. package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/openapi/schemas.ts +262 -0
  187. package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/openapi/types.ts +5159 -0
  188. package/zova/src/suite/cabloy-basic/modules/basic-image/src/apiSchema/image.ts +35 -0
  189. package/zova/src/suite/cabloy-basic/modules/basic-image/src/bean/tableCell.image.tsx +200 -0
  190. package/zova/src/suite/cabloy-basic/modules/basic-image/src/component/formFieldImage/controller.tsx +781 -0
  191. package/zova/src/suite/cabloy-basic/modules/basic-image/src/config/locale/en-us.ts +18 -0
  192. package/zova/src/suite/cabloy-basic/modules/basic-image/src/config/locale/zh-cn.ts +18 -0
  193. package/zova/src/suite/cabloy-basic/modules/basic-image/src/index.ts +4 -0
  194. package/zova/src/suite/cabloy-basic/modules/basic-image/src/lib/index.ts +2 -0
  195. package/zova/src/suite/cabloy-basic/modules/basic-image/src/lib/preview.ts +105 -0
  196. package/zova/src/suite/cabloy-basic/modules/basic-image/src/lib/previewDialog.tsx +190 -0
  197. package/zova/src/suite/cabloy-basic/modules/basic-image/src/types/image.ts +5 -0
  198. package/zova/src/suite/cabloy-basic/modules/basic-image/src/types/index.ts +1 -0
  199. package/zova/src/suite/cabloy-basic/modules/basic-image/tsconfig.build.json +13 -0
  200. package/zova/src/suite/cabloy-basic/modules/basic-image/tsconfig.json +5 -0
  201. package/zova/src/suite/cabloy-basic/package.json +1 -0
  202. package/zova/src/suite-vendor/a-zova/modules/a-openapi/package.json +1 -1
  203. package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/image.ts +1 -0
  204. package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/index.ts +1 -0
  205. package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +5 -5
  206. package/zova/src/suite-vendor/a-zova/package.json +3 -3
  207. package/vona/packages-vona/vona-core/src/lib/utils/sqlite3.ts +0 -50
  208. /package/vona/patches/{zova-core@5.1.75.patch → zova-core@5.1.76.patch} +0 -0
@@ -0,0 +1,18 @@
1
+ export default {
2
+ AddImage: 'Add Image',
3
+ AdjustImage: 'Drag or resize the crop area to adjust the image.',
4
+ ApplyCrop: 'Apply Crop',
5
+ CancelCrop: 'Cancel',
6
+ CropImage: 'Crop Image',
7
+ ImageTooLarge: 'Image size exceeds the limit: {0}',
8
+ ImageTooSmall: 'Image size is smaller than the limit: {0}',
9
+ ImageUploadFailed: 'Image upload failed',
10
+ InvalidImageType: 'Unsupported image type',
11
+ NoImageSelected: 'No image selected',
12
+ PreviewImage: 'Preview',
13
+ RemoveImage: 'Remove',
14
+ ReplaceImage: 'Replace Image',
15
+ SelectImage: 'Select Image',
16
+ TooManyImages: 'You can upload up to {0} images',
17
+ Uploading: 'Uploading...',
18
+ };
@@ -0,0 +1,18 @@
1
+ export default {
2
+ AddImage: '继续添加图片',
3
+ AdjustImage: '请拖拽或缩放裁剪区域,调整图片显示范围。',
4
+ ApplyCrop: '应用裁剪',
5
+ CancelCrop: '取消',
6
+ CropImage: '裁剪图片',
7
+ ImageTooLarge: '图片大小超过限制:{0}',
8
+ ImageTooSmall: '图片大小小于限制:{0}',
9
+ ImageUploadFailed: '图片上传失败',
10
+ InvalidImageType: '图片类型不受支持',
11
+ NoImageSelected: '尚未选择图片',
12
+ PreviewImage: '预览',
13
+ RemoveImage: '移除',
14
+ ReplaceImage: '替换图片',
15
+ SelectImage: '选择图片',
16
+ TooManyImages: '最多允许上传 {0} 张图片',
17
+ Uploading: '正在上传...',
18
+ };
@@ -0,0 +1,4 @@
1
+ export * from './.metadata/index.js';
2
+ export * from './.metadata/locales.js';
3
+ export * from './lib/index.js';
4
+ export * from './types/index.js';
@@ -0,0 +1,2 @@
1
+ export * from './preview.js';
2
+ export * from './previewDialog.js';
@@ -0,0 +1,105 @@
1
+ export interface IImagePreviewItemBase {
2
+ url?: string;
3
+ filename?: string;
4
+ }
5
+
6
+ export interface IImagePreviewItem extends IImagePreviewItemBase {
7
+ url: string;
8
+ }
9
+
10
+ export function inferImageRelationName(fieldName?: string, relationName?: string) {
11
+ if (relationName) return relationName;
12
+ if (!fieldName) return undefined;
13
+ if (fieldName.endsWith('Ids')) {
14
+ return `${fieldName.slice(0, -3)}s`;
15
+ }
16
+ if (fieldName.endsWith('Id')) {
17
+ return fieldName.slice(0, -2);
18
+ }
19
+ return undefined;
20
+ }
21
+
22
+ export function resolveImagePreviewUrl(url: string | undefined, baseURL?: string) {
23
+ if (!url || !url.startsWith('/api/')) return url;
24
+ if (!baseURL) return url;
25
+ return `${baseURL.replace(/\/$/, '')}${url}`;
26
+ }
27
+
28
+ export function buildImagePreviewTitle(
29
+ fieldTitle: string | undefined,
30
+ count: number,
31
+ fallbackTitle: string | (() => string),
32
+ ) {
33
+ const normalizedTitle =
34
+ fieldTitle ?? (typeof fallbackTitle === 'function' ? fallbackTitle() : fallbackTitle);
35
+ if (count <= 1) return normalizedTitle;
36
+ return `${normalizedTitle}(${count})`;
37
+ }
38
+
39
+ export function summarizeImageRelationPreviewValue(value: unknown): {
40
+ count: number;
41
+ item?: IImagePreviewItem;
42
+ } {
43
+ if (!Array.isArray(value)) {
44
+ if (!isImagePreviewItem(value)) return { count: 0, item: undefined };
45
+ return { count: 1, item: value };
46
+ }
47
+ let count = 0;
48
+ let item: IImagePreviewItem | undefined;
49
+ for (const valueItem of value) {
50
+ if (!isImagePreviewItem(valueItem)) continue;
51
+ count += 1;
52
+ if (!item) item = valueItem;
53
+ }
54
+ return { count, item };
55
+ }
56
+
57
+ export function summarizeImageUrlPreviewValue(value: unknown): {
58
+ count: number;
59
+ item?: IImagePreviewItem;
60
+ } {
61
+ if (!Array.isArray(value)) {
62
+ if (!value) return { count: 0, item: undefined };
63
+ return { count: 1, item: { url: String(value) } };
64
+ }
65
+ let count = 0;
66
+ let item: IImagePreviewItem | undefined;
67
+ for (const valueItem of value) {
68
+ if (!valueItem) continue;
69
+ count += 1;
70
+ if (!item) item = { url: String(valueItem) };
71
+ }
72
+ return { count, item };
73
+ }
74
+
75
+ export function collectImageRelationPreviewItems(value: unknown): IImagePreviewItem[] {
76
+ if (!Array.isArray(value)) {
77
+ if (!isImagePreviewItem(value)) return [];
78
+ return [value];
79
+ }
80
+ const items: IImagePreviewItem[] = [];
81
+ for (const valueItem of value) {
82
+ if (!isImagePreviewItem(valueItem)) continue;
83
+ items.push(valueItem);
84
+ }
85
+ return items;
86
+ }
87
+
88
+ export function collectImageUrlPreviewItems(value: unknown): IImagePreviewItem[] {
89
+ if (!Array.isArray(value)) {
90
+ if (!value) return [];
91
+ return [{ url: String(value) }];
92
+ }
93
+ const items: IImagePreviewItem[] = [];
94
+ for (const valueItem of value) {
95
+ if (!valueItem) continue;
96
+ items.push({ url: String(valueItem) });
97
+ }
98
+ return items;
99
+ }
100
+
101
+ export function isImagePreviewItem(value: unknown): value is IImagePreviewItem {
102
+ if (!value || typeof value !== 'object') return false;
103
+ if (!('url' in value)) return false;
104
+ return typeof value.url === 'string' && !!value.url;
105
+ }
@@ -0,0 +1,190 @@
1
+ import type { VNode } from 'vue';
2
+
3
+ import { classes } from 'typestyle';
4
+ import { nextTick, reactive } from 'vue';
5
+
6
+ import { IImagePreviewItem, resolveImagePreviewUrl } from './preview.js';
7
+
8
+ interface IImagePreviewDialogState {
9
+ activeIndex: number;
10
+ }
11
+
12
+ interface IImagePreviewDialogAppModal {
13
+ dialog(
14
+ options?: {
15
+ title?: string;
16
+ slotDefault?: () => VNode;
17
+ },
18
+ dialogOptions?: {
19
+ maxWidth?: number | string;
20
+ maxHeight?: number | string;
21
+ closeOnBackdrop?: boolean;
22
+ closeOnEscape?: boolean;
23
+ showCloseButton?: boolean;
24
+ },
25
+ ): unknown;
26
+ }
27
+
28
+ export interface IOpenImagePreviewDialogOptions {
29
+ appModal: IImagePreviewDialogAppModal;
30
+ title: string;
31
+ items: IImagePreviewItem[];
32
+ initialIndex?: number;
33
+ baseURL?: string;
34
+ }
35
+
36
+ export function openImagePreviewDialog(options: IOpenImagePreviewDialogOptions) {
37
+ const items = options.items.filter(item => !!item.url);
38
+ if (items.length === 0) return;
39
+ const dialogState = reactive<IImagePreviewDialogState>({
40
+ activeIndex: _normalizePreviewDialogIndex(items.length, options.initialIndex),
41
+ });
42
+ const bodyRefHolder: { value?: HTMLElement } = {};
43
+ options.appModal.dialog(
44
+ {
45
+ title: options.title,
46
+ slotDefault: () =>
47
+ _renderPreviewDialogBody(items, dialogState, bodyRefHolder, options.baseURL),
48
+ },
49
+ {
50
+ maxWidth: 960,
51
+ maxHeight: 'calc(100vh - 2rem)',
52
+ closeOnBackdrop: true,
53
+ closeOnEscape: true,
54
+ showCloseButton: true,
55
+ },
56
+ );
57
+ }
58
+
59
+ function _renderPreviewDialogBody(
60
+ items: IImagePreviewItem[],
61
+ dialogState: IImagePreviewDialogState,
62
+ bodyRefHolder: { value?: HTMLElement },
63
+ baseURL?: string,
64
+ ): VNode {
65
+ const leadItem = items[dialogState.activeIndex] ?? items[0];
66
+ const leadPreviewUrl = resolveImagePreviewUrl(leadItem?.url, baseURL);
67
+ return (
68
+ <div
69
+ ref={ref => {
70
+ const element = ref as HTMLElement | null;
71
+ if (!element) return;
72
+ const isNewElement = bodyRefHolder.value !== element;
73
+ bodyRefHolder.value = element;
74
+ if (isNewElement) {
75
+ nextTick(() => {
76
+ element.focus();
77
+ });
78
+ }
79
+ }}
80
+ class="space-y-4 focus:outline-none"
81
+ tabindex={0}
82
+ onKeydown={event => {
83
+ _handlePreviewDialogKeydown(event, items.length, dialogState);
84
+ }}
85
+ >
86
+ {leadPreviewUrl && (
87
+ <div class="overflow-hidden rounded-box bg-base-200 p-2">
88
+ <img
89
+ class="mx-auto max-h-[70vh] w-full object-contain"
90
+ src={leadPreviewUrl}
91
+ alt={leadItem?.filename ?? 'image'}
92
+ />
93
+ </div>
94
+ )}
95
+ {(!!leadItem?.filename || items.length > 1) && (
96
+ <div class="flex items-center justify-between gap-3">
97
+ <div class="min-w-0 text-sm font-medium text-base-content/70">
98
+ {!!leadItem?.filename && <div class="truncate">{leadItem.filename}</div>}
99
+ </div>
100
+ {items.length > 1 && (
101
+ <div class="shrink-0 text-sm font-medium text-base-content/60">
102
+ {_getPreviewCounterText(items.length, dialogState.activeIndex)}
103
+ </div>
104
+ )}
105
+ </div>
106
+ )}
107
+ {items.length > 1 && (
108
+ <div class="grid grid-cols-2 gap-3 sm:grid-cols-3">
109
+ {items.map((item, index) =>
110
+ _renderPreviewDialogItem(
111
+ item,
112
+ index,
113
+ index === dialogState.activeIndex,
114
+ dialogState,
115
+ baseURL,
116
+ ),
117
+ )}
118
+ </div>
119
+ )}
120
+ </div>
121
+ );
122
+ }
123
+
124
+ function _renderPreviewDialogItem(
125
+ item: IImagePreviewItem,
126
+ index: number,
127
+ active: boolean,
128
+ dialogState: IImagePreviewDialogState,
129
+ baseURL?: string,
130
+ ): VNode {
131
+ const previewUrl = resolveImagePreviewUrl(item.url, baseURL);
132
+ return (
133
+ <button
134
+ key={`${item.url}-${index}`}
135
+ type="button"
136
+ class={classes(
137
+ 'space-y-2 rounded-box bg-base-200 p-2 text-left transition duration-150',
138
+ active && 'ring-2 ring-primary shadow-md',
139
+ !active && 'hover:shadow-sm hover:ring-1 hover:ring-base-300',
140
+ )}
141
+ title={item.filename ?? `image-${index + 1}`}
142
+ onClick={() => {
143
+ dialogState.activeIndex = index;
144
+ }}
145
+ >
146
+ <div class="aspect-square overflow-hidden rounded-box bg-base-100">
147
+ <img
148
+ class="h-full w-full object-cover"
149
+ loading="lazy"
150
+ src={previewUrl}
151
+ alt={item.filename ?? `image-${index + 1}`}
152
+ />
153
+ </div>
154
+ {!!item.filename && <div class="truncate text-sm text-base-content/70">{item.filename}</div>}
155
+ </button>
156
+ );
157
+ }
158
+
159
+ function _handlePreviewDialogKeydown(
160
+ event: KeyboardEvent,
161
+ count: number,
162
+ dialogState: IImagePreviewDialogState,
163
+ ) {
164
+ if (count <= 1) return;
165
+ if (event.key === 'ArrowLeft') {
166
+ event.preventDefault();
167
+ dialogState.activeIndex = _movePreviewDialogSelection(count, dialogState.activeIndex, -1);
168
+ return;
169
+ }
170
+ if (event.key === 'ArrowRight') {
171
+ event.preventDefault();
172
+ dialogState.activeIndex = _movePreviewDialogSelection(count, dialogState.activeIndex, 1);
173
+ }
174
+ }
175
+
176
+ function _movePreviewDialogSelection(count: number, activeIndex: number, step: number) {
177
+ return (activeIndex + step + count) % count;
178
+ }
179
+
180
+ function _getPreviewCounterText(count: number, activeIndex: number) {
181
+ return `${activeIndex + 1} / ${count}`;
182
+ }
183
+
184
+ function _normalizePreviewDialogIndex(count: number, initialIndex?: number) {
185
+ if (!count) return 0;
186
+ if (initialIndex === undefined) return 0;
187
+ if (initialIndex < 0) return 0;
188
+ if (initialIndex >= count) return count - 1;
189
+ return initialIndex;
190
+ }
@@ -0,0 +1,5 @@
1
+ import type { components } from '../api/openapi/index.js';
2
+
3
+ export type IImageTransformOptions = NonNullable<
4
+ NonNullable<components['schemas']['a-image.dto.imageUploadResponse']['variants']>[string]
5
+ >;
@@ -0,0 +1 @@
1
+ export * from './image.js';
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "../../tsconfig.base",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "dist",
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "emitDeclarationOnly": true,
9
+ "allowImportingTsExtensions": true,
10
+ "noEmit": false
11
+ },
12
+ "include": ["src", "src/.metadata/**/*"]
13
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "../../tsconfig.base",
3
+ "compilerOptions": {},
4
+ "include": ["src", "src/.metadata/**/*"]
5
+ }
@@ -16,6 +16,7 @@
16
16
  "zova-module-basic-date": "^5.0.6",
17
17
  "zova-module-basic-details": "^5.0.0",
18
18
  "zova-module-basic-form": "^5.0.7",
19
+ "zova-module-basic-image": "^5.0.0",
19
20
  "zova-module-basic-input": "^5.0.7",
20
21
  "zova-module-basic-page": "^5.0.7",
21
22
  "zova-module-basic-pageentry": "^5.0.7",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova-module-a-openapi",
3
- "version": "5.1.41",
3
+ "version": "5.1.42",
4
4
  "gitHead": "09d901d17140a80ee0764211b441cda72fd94663",
5
5
  "description": "",
6
6
  "keywords": [
@@ -0,0 +1 @@
1
+ export interface IImageSceneRecord {}
@@ -1,5 +1,6 @@
1
1
  export * from './action.js';
2
2
  export * from './captcha.js';
3
+ export * from './image.js';
3
4
  export * from './database.js';
4
5
  export * from './formMeta.js';
5
6
  export * from './formProvider.js';
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova-module-a-zova",
3
- "version": "5.1.90",
3
+ "version": "5.1.91",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "zova",
6
6
  "keywords": [
@@ -37,16 +37,16 @@
37
37
  "@cabloy/compose": "^2.1.8",
38
38
  "@cabloy/deps": "^1.1.8",
39
39
  "@cabloy/json5": "^1.1.9",
40
- "@cabloy/module-info": "^2.0.0",
41
- "@cabloy/utils": "^2.1.25",
40
+ "@cabloy/module-info": "^2.0.3",
41
+ "@cabloy/utils": "^2.1.26",
42
42
  "@cabloy/vue-router": "^4.4.16",
43
43
  "@cabloy/word-utils": "^2.1.14",
44
44
  "defu": "^6.1.7",
45
45
  "luxon": "^3.7.2",
46
- "zova-jsx": "^1.1.75"
46
+ "zova-jsx": "^1.1.76"
47
47
  },
48
48
  "devDependencies": {
49
- "@cabloy/cli": "^3.1.27",
49
+ "@cabloy/cli": "^3.1.28",
50
50
  "@types/luxon": "^3.7.1",
51
51
  "clean-package": "^2.2.0",
52
52
  "fs-extra": "^11.3.5",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova-suite-a-zova",
3
- "version": "5.1.129",
3
+ "version": "5.1.130",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "zova",
6
6
  "license": "MIT",
@@ -22,7 +22,7 @@
22
22
  "zova-module-a-logger": "^5.1.26",
23
23
  "zova-module-a-meta": "^5.1.21",
24
24
  "zova-module-a-model": "^5.1.30",
25
- "zova-module-a-openapi": "^5.1.41",
25
+ "zova-module-a-openapi": "^5.1.42",
26
26
  "zova-module-a-router": "^5.1.29",
27
27
  "zova-module-a-routerstack": "^5.1.26",
28
28
  "zova-module-a-routertabs": "^5.1.32",
@@ -32,7 +32,7 @@
32
32
  "zova-module-a-style": "^5.1.32",
33
33
  "zova-module-a-table": "^5.1.37",
34
34
  "zova-module-a-zod": "^5.1.36",
35
- "zova-module-a-zova": "^5.1.90"
35
+ "zova-module-a-zova": "^5.1.91"
36
36
  },
37
37
  "title": "a-zova"
38
38
  }
@@ -1,50 +0,0 @@
1
- import fse from 'fs-extra';
2
- import { createRequire } from 'node:module';
3
- import path from 'node:path';
4
-
5
- import type { VonaApplication } from '../core/application.ts';
6
-
7
- import { getHomeVonaAppDir } from '../core/config.ts';
8
- import { pathToHref } from './util.ts';
9
-
10
- export function getSqlite3DatabaseNameDefault(app: VonaApplication) {
11
- const mode = app.meta.env.META_MODE;
12
- if (mode !== 'prod') return '';
13
- const dbPath = path.join(getHomeVonaAppDir(app), 'sqlite3');
14
- fse.ensureDirSync(dbPath);
15
- return path.join(dbPath, `${app.name}.db`);
16
- }
17
-
18
- // string/true/false
19
- export function getSqlite3NativeBinding(_app: VonaApplication, nativeBinding: string | undefined) {
20
- nativeBinding = prepareNativeBinding(nativeBinding);
21
- if (!nativeBinding) return nativeBinding;
22
- const nativeBindingPath = path.isAbsolute(nativeBinding)
23
- ? nativeBinding
24
- : path.join(import.meta.dirname, nativeBinding);
25
- const require = createRequire(import.meta.url);
26
- const addon = require(nativeBindingPath);
27
- return addon as any;
28
- }
29
-
30
- export async function copySqlite3NativeBinding(
31
- projectPath: string,
32
- outDir: string,
33
- env: NodeJS.ProcessEnv,
34
- ) {
35
- // dest
36
- const nativeBinding = prepareNativeBinding(env.DATABASE_CLIENT_SQLITE3_NATIVEBINDING);
37
- if (!nativeBinding || path.isAbsolute(nativeBinding)) return;
38
- const fileDest = path.join(outDir, nativeBinding);
39
- // src
40
- const require = createRequire(pathToHref(path.join(projectPath, '/')));
41
- const modulePath = require.resolve('better-sqlite3/package.json');
42
- const fileSrc = path.join(path.dirname(modulePath), 'build/Release/better_sqlite3.node');
43
- // copy
44
- await fse.copy(fileSrc, fileDest);
45
- }
46
-
47
- function prepareNativeBinding(nativeBinding: string | undefined) {
48
- if (!nativeBinding || nativeBinding === 'false') return null as unknown as undefined;
49
- return nativeBinding === 'true' ? 'node/better_sqlite3.node' : nativeBinding;
50
- }