rimecms 0.27.6 → 0.28.1

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 (206) hide show
  1. package/dist/adapter-sqlite/{area.js → area.server.js} +2 -2
  2. package/dist/adapter-sqlite/{blocks.d.ts → blocks.server.d.ts} +1 -1
  3. package/dist/adapter-sqlite/{blocks.js → blocks.server.js} +1 -1
  4. package/dist/adapter-sqlite/{collection.js → collection.server.js} +17 -8
  5. package/dist/adapter-sqlite/generate-schema/write.server.js +2 -2
  6. package/dist/adapter-sqlite/index.server.d.ts +8 -8
  7. package/dist/adapter-sqlite/index.server.js +7 -7
  8. package/dist/adapter-sqlite/{orderBy.js → orderBy.server.js} +1 -1
  9. package/dist/adapter-sqlite/{relations.d.ts → relations.server.d.ts} +1 -1
  10. package/dist/adapter-sqlite/{relations.js → relations.server.js} +1 -1
  11. package/dist/adapter-sqlite/{transform.js → transform.server.js} +2 -3
  12. package/dist/adapter-sqlite/{tree.d.ts → tree.server.d.ts} +1 -1
  13. package/dist/adapter-sqlite/{tree.js → tree.server.js} +1 -1
  14. package/dist/adapter-sqlite/url.server.d.ts +1 -1
  15. package/dist/core/areas/local-api.server.d.ts +1 -1
  16. package/dist/core/areas/local-api.server.js +1 -0
  17. package/dist/core/collections/api/delete.server.js +1 -1
  18. package/dist/core/collections/api/get.server.js +1 -1
  19. package/dist/core/collections/api/getById.server.js +3 -1
  20. package/dist/core/collections/config/augment-panel.d.ts +13 -0
  21. package/dist/core/collections/config/augment-panel.js +19 -0
  22. package/dist/core/collections/config/augment-upload.server.d.ts +1 -0
  23. package/dist/core/collections/config/augment-upload.server.js +1 -0
  24. package/dist/core/collections/config/builder.js +3 -1
  25. package/dist/core/collections/config/builder.server.js +3 -1
  26. package/dist/core/collections/local-api.server.js +3 -1
  27. package/dist/core/collections/operations/findById.js +3 -1
  28. package/dist/core/collections/upload/hooks/convert-base64.server.js +1 -1
  29. package/dist/core/config/auth/better-auth.server.d.ts +3 -3
  30. package/dist/core/config/auth/better-auth.server.js +1 -1
  31. package/dist/core/config/client/augment-panel.d.ts +4 -4
  32. package/dist/core/config/client/build-config.d.ts +5 -5
  33. package/dist/core/config/server/augment-directories.server.js +3 -3
  34. package/dist/core/config/server/augment-staff.server.d.ts +1 -1
  35. package/dist/core/config/server/augment-staff.server.js +1 -1
  36. package/dist/core/config/server/build-config.server.d.ts +12 -12
  37. package/dist/core/config/server/build-config.server.js +1 -1
  38. package/dist/core/config/server/{validate.js → validate.server.js} +61 -29
  39. package/dist/core/config/server/{write.js → write.server.js} +1 -1
  40. package/dist/core/config/shared/augment-icons.d.ts +1 -2
  41. package/dist/core/config/shared/get-staff-collection.d.ts +1 -1
  42. package/dist/core/config/shared/upload-directories.js +1 -1
  43. package/dist/core/config/types.d.ts +20 -8
  44. package/dist/core/dev/cli/build/templates.d.ts +1 -1
  45. package/dist/core/dev/cli/build/templates.js +5 -2
  46. package/dist/core/dev/cli/generate/{index.js → index.server.js} +13 -9
  47. package/dist/core/dev/cli/index.js +11 -6
  48. package/dist/core/dev/cli/init/{index.js → index.server.js} +68 -25
  49. package/dist/core/dev/cli/init/{packageManagerUtil.js → package-manager-util.server.js} +5 -5
  50. package/dist/core/dev/generate/routes/{area.d.ts → area.server.d.ts} +1 -1
  51. package/dist/core/dev/generate/routes/{area.js → area.server.js} +1 -1
  52. package/dist/core/dev/generate/routes/{collection.d.ts → collection.server.d.ts} +1 -1
  53. package/dist/core/dev/generate/routes/{collection.js → collection.server.js} +1 -1
  54. package/dist/core/dev/generate/routes/{common.d.ts → common.server.d.ts} +1 -1
  55. package/dist/core/dev/generate/routes/{common.js → common.server.js} +3 -3
  56. package/dist/core/dev/generate/routes/{index.js → index.server.js} +5 -5
  57. package/dist/core/dev/generate/routes/{util.js → util.server.js} +1 -1
  58. package/dist/core/dev/generate/types/{index.js → index.server.js} +1 -1
  59. package/dist/core/dev/vite/{index.js → index.server.js} +8 -4
  60. package/dist/core/fields/util.d.ts +13 -0
  61. package/dist/core/fields/util.js +77 -4
  62. package/dist/core/i18n/en/common.d.ts +1 -0
  63. package/dist/core/i18n/en/common.js +1 -0
  64. package/dist/core/i18n/fr/common.d.ts +1 -0
  65. package/dist/core/i18n/fr/common.js +1 -0
  66. package/dist/core/operations/hooks/before-read/populate-url.server.js +10 -2
  67. package/dist/core/operations/hooks/before-upsert/handle-new-version.server.js +2 -2
  68. package/dist/core/operations/relations/diff.server.d.ts +1 -1
  69. package/dist/core/operations/relations/extract.server.d.ts +1 -1
  70. package/dist/core/rime.server.d.ts +6 -6
  71. package/dist/core/rime.server.js +15 -14
  72. package/dist/fields/blocks/component/Block.svelte +13 -19
  73. package/dist/fields/blocks/component/Blocks.svelte +6 -13
  74. package/dist/fields/group/component/Group.svelte +6 -13
  75. package/dist/fields/number/component/number.css +2 -0
  76. package/dist/fields/relation/component/default/Default.svelte +1 -7
  77. package/dist/fields/relation/component/upload/upload.css +1 -10
  78. package/dist/fields/relation/index.d.ts +21 -6
  79. package/dist/fields/relation/index.js +41 -11
  80. package/dist/fields/rich-text/component/RichText.svelte +4 -4
  81. package/dist/fields/rich-text/component/bubble-menu/bubble-menu.svelte +6 -6
  82. package/dist/fields/rich-text/component/bubble-menu/bubble-menu.svelte.d.ts +2 -0
  83. package/dist/fields/rich-text/component/drag-handle/drag-handle.css +1 -0
  84. package/dist/fields/rich-text/component/styles/rich-text.css +0 -7
  85. package/dist/fields/rich-text/core/features/resource/resource.svelte +2 -9
  86. package/dist/fields/rich-text/core/features/upload/upload.svelte +5 -12
  87. package/dist/fields/rich-text/core/render-rich-text.svelte +5 -3
  88. package/dist/fields/rich-text/index.d.ts +1 -1
  89. package/dist/fields/select/component/Select.svelte +5 -11
  90. package/dist/fields/tabs/index.d.ts +1 -0
  91. package/dist/fields/tabs/index.js +3 -0
  92. package/dist/fields/textarea/component/TextArea.svelte +5 -13
  93. package/dist/fields/tree/component/TreeBlock.svelte +12 -18
  94. package/dist/fields/types.d.ts +3 -2
  95. package/dist/live.d.ts +2 -1
  96. package/dist/live.js +2 -1
  97. package/dist/panel/components/fields/Error.svelte +1 -7
  98. package/dist/panel/components/sections/collection/Empty.svelte +1 -0
  99. package/dist/panel/components/sections/collection/folder/Folder.svelte +7 -16
  100. package/dist/panel/components/sections/collection/folder/FolderWithActions.svelte +1 -1
  101. package/dist/panel/components/sections/collection/grid/CollectionGrid.svelte +22 -12
  102. package/dist/panel/components/sections/collection/grid/create-directory-dialog/CreateDirectoryDialog.svelte +4 -5
  103. package/dist/panel/components/sections/collection/grid/grid-item/GridItem.svelte +41 -33
  104. package/dist/panel/components/sections/collection/grid/grid-item/GridItem.svelte.d.ts +4 -0
  105. package/dist/panel/components/sections/collection/header/ButtonCreate.svelte +14 -16
  106. package/dist/panel/components/sections/collection/header/ButtonCreate.svelte.d.ts +2 -0
  107. package/dist/panel/components/sections/collection/header/DisplayMode.svelte +10 -10
  108. package/dist/panel/components/sections/collection/header/SearchInput.svelte +2 -8
  109. package/dist/panel/components/sections/collection/list/CollectionList.svelte +32 -3
  110. package/dist/panel/components/sections/collection/list/header/Header.svelte +3 -9
  111. package/dist/panel/components/sections/collection/list/row/Row.svelte +34 -24
  112. package/dist/panel/components/sections/collection/list/row/Row.svelte.d.ts +9 -0
  113. package/dist/panel/components/sections/collection/upload-thumb-cell/UploadThumbCell.svelte +11 -1
  114. package/dist/panel/components/sections/document/ButtonStatus.svelte +3 -11
  115. package/dist/panel/components/sections/document/Document.svelte +22 -41
  116. package/dist/panel/components/sections/document/Document.svelte.d.ts +0 -1
  117. package/dist/panel/components/sections/document/upload-header/UploadHeader.svelte +11 -21
  118. package/dist/panel/components/sections/live/LiveEdit.svelte +114 -0
  119. package/dist/panel/components/sections/live/LiveEdit.svelte.d.ts +32 -0
  120. package/dist/panel/components/sections/live/LiveEditPanel.svelte +53 -0
  121. package/dist/panel/components/sections/live/LiveEditPanel.svelte.d.ts +11 -0
  122. package/dist/panel/components/sections/live/LiveFloatingUI.svelte +106 -0
  123. package/dist/panel/components/sections/live/LiveFloatingUI.svelte.d.ts +12 -0
  124. package/dist/panel/components/sections/live/Provider.svelte +4 -4
  125. package/dist/panel/components/sections/live/Provider.svelte.d.ts +5 -3
  126. package/dist/panel/components/sections/live/live-edit-panel.css +12 -0
  127. package/dist/panel/components/ui/breadcrumb/BreadCrumb.svelte +2 -9
  128. package/dist/panel/components/ui/button/button.svelte +35 -54
  129. package/dist/panel/components/ui/calendar/calendar.css +11 -0
  130. package/dist/panel/components/ui/card-document/card-document.svelte +21 -14
  131. package/dist/panel/components/ui/card-resource/card-resource.svelte +8 -14
  132. package/dist/panel/components/ui/checkbox/checkbox.svelte +1 -1
  133. package/dist/panel/components/ui/command/command-group.css +1 -0
  134. package/dist/panel/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +1 -7
  135. package/dist/panel/components/ui/input/input.svelte +13 -23
  136. package/dist/panel/components/ui/label/label.css +0 -1
  137. package/dist/panel/components/ui/nav/NavItemButton.svelte +2 -8
  138. package/dist/panel/components/ui/page-header/PageHeader.svelte +6 -13
  139. package/dist/panel/components/ui/radio-group/radio-group.css +2 -0
  140. package/dist/panel/components/ui/scroll-area/scroll-area.css +0 -4
  141. package/dist/panel/components/ui/sheet/sheet.css +0 -16
  142. package/dist/panel/components/ui/tabs/tabs.css +7 -0
  143. package/dist/panel/context/collection.svelte.d.ts +0 -4
  144. package/dist/panel/context/collection.svelte.js +0 -3
  145. package/dist/panel/context/documentForm.svelte.js +1 -0
  146. package/dist/panel/context/live.svelte.d.ts +8 -20
  147. package/dist/panel/context/live.svelte.js +65 -35
  148. package/dist/panel/context/livePanel.svelte.d.ts +11 -0
  149. package/dist/panel/context/livePanel.svelte.js +8 -0
  150. package/dist/panel/pages/auth/forgot-password/ForgotPassword.svelte +1 -7
  151. package/dist/panel/pages/collection/Collection.svelte +3 -1
  152. package/dist/panel/pages/dashboard/Dashboard.svelte +52 -28
  153. package/dist/panel/pages/dashboard/DashboardCollection.svelte +91 -0
  154. package/dist/panel/pages/dashboard/DashboardCollection.svelte.d.ts +9 -0
  155. package/dist/panel/pages/dashboard/load.server.js +30 -14
  156. package/dist/panel/pages/dashboard/types.d.ts +1 -0
  157. package/dist/panel/pages/live/Live.svelte +244 -67
  158. package/dist/panel/pages/live/Live.svelte.d.ts +9 -1
  159. package/dist/panel/pages/live/load.server.js +14 -1
  160. package/dist/panel/style/index.css +562 -8
  161. package/dist/panel/style/mixins/index.css +1 -3
  162. package/dist/panel/util/upload.d.ts +1 -1
  163. package/dist/util/object.js +3 -0
  164. package/package.json +12 -10
  165. package/dist/panel/components/sections/document/FloatingUI.svelte +0 -81
  166. package/dist/panel/components/sections/document/FloatingUI.svelte.d.ts +0 -8
  167. package/dist/panel/components/sections/live/SidePanel.svelte +0 -61
  168. package/dist/panel/components/sections/live/SidePanel.svelte.d.ts +0 -14
  169. package/dist/panel/components/sections/live/side-panel.css +0 -15
  170. /package/dist/adapter-sqlite/{area.d.ts → area.server.d.ts} +0 -0
  171. /package/dist/adapter-sqlite/{collection.d.ts → collection.server.d.ts} +0 -0
  172. /package/dist/adapter-sqlite/{orderBy.d.ts → orderBy.server.d.ts} +0 -0
  173. /package/dist/adapter-sqlite/{transform.d.ts → transform.server.d.ts} +0 -0
  174. /package/dist/adapter-sqlite/{types.d.ts → types.server.d.ts} +0 -0
  175. /package/dist/adapter-sqlite/{types.js → types.server.js} +0 -0
  176. /package/dist/adapter-sqlite/{util.d.ts → util.server.d.ts} +0 -0
  177. /package/dist/adapter-sqlite/{util.js → util.server.js} +0 -0
  178. /package/dist/adapter-sqlite/{where.d.ts → where.server.d.ts} +0 -0
  179. /package/dist/adapter-sqlite/{where.js → where.server.js} +0 -0
  180. /package/dist/adapter-sqlite/{with.d.ts → with.server.d.ts} +0 -0
  181. /package/dist/adapter-sqlite/{with.js → with.server.js} +0 -0
  182. /package/dist/core/collections/upload/util/{converter.d.ts → converter.server.d.ts} +0 -0
  183. /package/dist/core/collections/upload/util/{converter.js → converter.server.js} +0 -0
  184. /package/dist/core/config/auth/{better-auth-hooks.d.ts → better-auth-hooks.server.d.ts} +0 -0
  185. /package/dist/core/config/auth/{better-auth-hooks.js → better-auth-hooks.server.js} +0 -0
  186. /package/dist/core/config/server/{augment-cors.d.ts → augment-cors.server.d.ts} +0 -0
  187. /package/dist/core/config/server/{augment-cors.js → augment-cors.server.js} +0 -0
  188. /package/dist/core/config/server/{validate.d.ts → validate.server.d.ts} +0 -0
  189. /package/dist/core/config/server/{write.d.ts → write.server.d.ts} +0 -0
  190. /package/dist/core/dev/cache/{index.d.ts → index.server.d.ts} +0 -0
  191. /package/dist/core/dev/cache/{index.js → index.server.js} +0 -0
  192. /package/dist/core/dev/cli/clear/{index.d.ts → index.server.d.ts} +0 -0
  193. /package/dist/core/dev/cli/clear/{index.js → index.server.js} +0 -0
  194. /package/dist/core/dev/cli/generate/{index.d.ts → index.server.d.ts} +0 -0
  195. /package/dist/core/dev/cli/init/{getPackageName.d.ts → get-package-info.server.d.ts} +0 -0
  196. /package/dist/core/dev/cli/init/{getPackageName.js → get-package-info.server.js} +0 -0
  197. /package/dist/core/dev/cli/init/{index.d.ts → index.server.d.ts} +0 -0
  198. /package/dist/core/dev/cli/init/{packageManagerUtil.d.ts → package-manager-util.server.d.ts} +0 -0
  199. /package/dist/core/dev/generate/routes/{custom-css.d.ts → custom-css.server.d.ts} +0 -0
  200. /package/dist/core/dev/generate/routes/{custom-css.js → custom-css.server.js} +0 -0
  201. /package/dist/core/dev/generate/routes/{index.d.ts → index.server.d.ts} +0 -0
  202. /package/dist/core/dev/generate/routes/{util.d.ts → util.server.d.ts} +0 -0
  203. /package/dist/core/dev/generate/sanitize/{index.d.ts → index.server.d.ts} +0 -0
  204. /package/dist/core/dev/generate/sanitize/{index.js → index.server.js} +0 -0
  205. /package/dist/core/dev/generate/types/{index.d.ts → index.server.d.ts} +0 -0
  206. /package/dist/core/dev/vite/{index.d.ts → index.server.d.ts} +0 -0
@@ -155,13 +155,7 @@
155
155
  {/if}
156
156
  </div>
157
157
 
158
- <style>/**************************************/
159
-
160
- /* Font */
161
-
162
- /**************************************/
163
-
164
- .rz-relation {
158
+ <style>.rz-relation {
165
159
  position: relative;
166
160
 
167
161
  :global {
@@ -24,11 +24,10 @@
24
24
  flex-wrap: wrap;
25
25
  gap: var(--rz-size-4);
26
26
  }
27
-
28
27
  .rz-relation-upload__list:not(.rz-relation-upload__list[data-many]) {
29
28
  @container relation-upload-list (min-width: 285px) {
30
29
  .rz-card-resource {
31
- width: min(400px, 100%);
30
+ width: min(310px, 100%);
32
31
  .rz-card-resource__info {
33
32
  display: block;
34
33
  }
@@ -41,7 +40,6 @@
41
40
  }
42
41
  }
43
42
  }
44
-
45
43
  .rz-relation-upload__actions {
46
44
  display: flex;
47
45
  margin-top: var(--rz-size-3);
@@ -58,7 +56,6 @@
58
56
  .rz-relation-upload__actions.rz-relation-upload__actions--list-empty {
59
57
  margin-top: 0;
60
58
  }
61
-
62
59
  .rz-relation-upload__search {
63
60
  height: var(--rz-size-10);
64
61
  align-items: center;
@@ -67,30 +64,25 @@
67
64
  border-bottom-left-radius: 0;
68
65
  border-bottom-right-radius: 0;
69
66
  }
70
-
71
67
  .rz-relation-upload__command-list {
72
68
  padding: var(--rz-size-2);
73
69
  }
74
-
75
70
  .rz-relation-upload__command-item[aria-selected='true'] {
76
71
  /* background-color: hsl(var(--rz-gray-5)); */
77
72
  background-color: var(--rz-popover-high-light-bg);
78
73
  }
79
-
80
74
  .rz-relation-upload__grid-item {
81
75
  display: flex;
82
76
  gap: var(--rz-size-4);
83
77
  padding-left: var(--rz-size-1);
84
78
  padding-right: var(--rz-size-1);
85
79
  }
86
-
87
80
  .rz-relation-upload__grid-thumbnail {
88
81
  width: var(--rz-size-16);
89
82
  height: var(--rz-size-16);
90
83
  overflow: hidden;
91
84
  border-radius: var(--rz-radius-lg);
92
85
  }
93
-
94
86
  .rz-relation-upload__grid-filename {
95
87
  margin-top: var(--rz-size-2);
96
88
  display: -webkit-box;
@@ -102,7 +94,6 @@
102
94
  font-variation-settings: 'wght' 600;
103
95
  font-weight: 600;
104
96
  }
105
-
106
97
  .rz-relation-upload__grid-filesize,
107
98
  .rz-relation-upload__grid-mimetype {
108
99
  text-align: left;
@@ -1,6 +1,6 @@
1
1
  import { FormFieldBuilder } from '../../core/fields/builders/form-field-builder.js';
2
2
  import type { CollectionSlug, GenericDoc } from '../../core/types/doc.js';
3
- import type { DefaultValueFn, Field, FieldHookShared, FormField } from '../types.js';
3
+ import type { DefaultValueFn, Field, FieldHookShared, FormField, RelationRef, RelationValue } from '../types.js';
4
4
  import type { RegisterCollection } from '../../index.js';
5
5
  import type { WithOptional } from '../../util/types';
6
6
  export declare const ensureRelationExists: FieldHookShared;
@@ -38,6 +38,14 @@ export declare const isRelationField: (field: Field) => field is RelationField;
38
38
  * isRelationResolved({ title: 'Home Page', _prototype: 'collection', _type: 'pages' });
39
39
  */
40
40
  export declare const isRelationResolved: <T>(value: any) => value is T;
41
+ /**
42
+ * Checks if a relation value is a reference (contains only the relationTo and documentId).
43
+ *
44
+ * @example
45
+ * // Returns true for a relation reference
46
+ * isRelationRef({ relationTo: 'pages', documentId: '123' });
47
+ */
48
+ export declare const isRelationRef: (value: unknown) => value is RelationRef;
41
49
  /**
42
50
  * Checks if a relation value is unresolved (contains only reference information).
43
51
  *
@@ -47,14 +55,21 @@ export declare const isRelationResolved: <T>(value: any) => value is T;
47
55
  */
48
56
  export declare const isRelationUnresolved: (value: any) => value is Omit<Relation, "path" | "position" | "ownerId">;
49
57
  /**
50
- * Resolves a relation by fetching the referenced document.
51
- * If the relation is already resolved, returns it as is.
58
+ * Resolves a relation reference to the actual document it points to.
59
+ *
60
+ * @example
61
+ * // Resolves a relation reference to the actual document
62
+ * const doc = await resolveRelationRef({ relationTo: 'pages', documentId: '123' });
63
+ */
64
+ export declare function resolveRelationRef<T>(item: T | RelationRef | string): Promise<T>;
65
+ /**
66
+ * Resolves a relation field value to the actual documents it points to.
52
67
  *
53
68
  * @example
54
- * // Resolves a relation to its full document
55
- * const page = await resolveRelation({ relationTo: 'pages', documentId: '123' });
69
+ * // Resolves a relation value to the actual documents
70
+ * const docs = await resolveRelation([{ relationTo: 'pages', documentId: '123' }]);
56
71
  */
57
- export declare const resolveRelation: <T>(value: any) => Promise<T>;
72
+ export declare function resolveRelation<T>(value: RelationValue<T> | null | undefined): Promise<T[] | null | undefined>;
58
73
  /****************************************************/
59
74
  export type RelationField<Doc extends GenericDoc = GenericDoc> = FormField & {
60
75
  type: 'relation';
@@ -102,6 +102,17 @@ export const isRelationField = (field) => field.type === 'relation';
102
102
  export const isRelationResolved = (value) => {
103
103
  return value && isObjectLiteral(value) && hasProps(['title', '_prototype', '_type'], value);
104
104
  };
105
+ /**
106
+ * Checks if a relation value is a reference (contains only the relationTo and documentId).
107
+ *
108
+ * @example
109
+ * // Returns true for a relation reference
110
+ * isRelationRef({ relationTo: 'pages', documentId: '123' });
111
+ */
112
+ export const isRelationRef = (value) => typeof value === 'object' &&
113
+ value !== null &&
114
+ typeof value.relationTo === 'string' &&
115
+ typeof value.documentId === 'string';
105
116
  /**
106
117
  * Checks if a relation value is unresolved (contains only reference information).
107
118
  *
@@ -113,18 +124,37 @@ export const isRelationUnresolved = (value) => {
113
124
  return value && isObjectLiteral(value) && hasProps(['relationTo', 'documentId'], value);
114
125
  };
115
126
  /**
116
- * Resolves a relation by fetching the referenced document.
117
- * If the relation is already resolved, returns it as is.
127
+ * Resolves a relation reference to the actual document it points to.
118
128
  *
119
129
  * @example
120
- * // Resolves a relation to its full document
121
- * const page = await resolveRelation({ relationTo: 'pages', documentId: '123' });
130
+ * // Resolves a relation reference to the actual document
131
+ * const doc = await resolveRelationRef({ relationTo: 'pages', documentId: '123' });
122
132
  */
123
- export const resolveRelation = async (value) => {
124
- if (isRelationResolved(value)) {
125
- return value;
133
+ export async function resolveRelationRef(item) {
134
+ if (isRelationRef(item)) {
135
+ return fetch(`api/${item.relationTo}/${item.documentId}`)
136
+ .then((r) => r.json())
137
+ .then((r) => r.doc);
126
138
  }
127
- return (await fetch(`api/${value.relationTo}/${value.documentId}`)
128
- .then((r) => r.json())
129
- .then((r) => r.doc));
130
- };
139
+ if (typeof item === 'string') {
140
+ throw new Error(`Cannot resolve relation from a bare id ("${item}") — missing "relationTo". ` +
141
+ `resolveRelationRef only works on populated docs or { relationTo, documentId } objects.`);
142
+ }
143
+ if (isRelationResolved(item)) {
144
+ return item;
145
+ }
146
+ throw new Error(`Unrecognized relation shape: ${JSON.stringify(item)}`);
147
+ }
148
+ /**
149
+ * Resolves a relation field value to the actual documents it points to.
150
+ *
151
+ * @example
152
+ * // Resolves a relation value to the actual documents
153
+ * const docs = await resolveRelation([{ relationTo: 'pages', documentId: '123' }]);
154
+ */
155
+ export async function resolveRelation(value) {
156
+ if (value === null || value === undefined)
157
+ return value;
158
+ const items = Array.isArray(value) ? value : [value];
159
+ return Promise.all(items.map((item) => resolveRelationRef(item)));
160
+ }
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { Field } from '../../../panel/components/fields/index.js';
3
3
  import { root } from '../../../panel/components/fields/root.svelte.js';
4
+ import { random } from '../../../util/index.js';
4
5
  import { Editor, type JSONContent } from '@tiptap/core';
5
6
  import { onMount } from 'svelte';
6
7
  import { buildEditorConfig } from '../core/build-editor-config.js';
@@ -22,10 +23,9 @@
22
23
  let editor = $state<Editor>();
23
24
  let features = $state<RichTextFeature[]>([]);
24
25
  const field = $derived(form.useField<JSONContent>(path, config));
26
+ const instanceId = random.randomId(8);
25
27
 
26
- $effect(() => {
27
- setRichTextContext(path);
28
- });
28
+ const ctx = setRichTextContext(instanceId);
29
29
 
30
30
  const withSuggestion = $derived(hasSuggestion(config.features || defaultFeatures));
31
31
 
@@ -82,7 +82,7 @@
82
82
  {/if}
83
83
 
84
84
  {#key key}
85
- <EditorBubbleMenu {features} {editor} {path} />
85
+ <EditorBubbleMenu {features} {editor} {path} context={ctx} />
86
86
  {/key}
87
87
  {/if}
88
88
  </div>
@@ -7,7 +7,7 @@
7
7
  RichTextFeatureMark,
8
8
  RichTextFeatureNode
9
9
  } from '../../core/types.js';
10
- import { getRichTextContext } from '../context.svelte';
10
+ import type { RichTextContext } from '../context.svelte';
11
11
  import './bubble-menu.css';
12
12
  import IconButton from './icon-button/icon-button.svelte';
13
13
  import NodeSelector, { type NodeSelectorItem } from './node-selector/node-selector.svelte';
@@ -18,12 +18,13 @@
18
18
  };
19
19
 
20
20
  type Props = {
21
+ context: RichTextContext;
21
22
  editor: Editor;
22
23
  features: RichTextFeature[];
23
24
  path: string;
24
25
  };
25
26
 
26
- const { editor, path, features = [] }: Props = $props();
27
+ const { editor, path, features = [], context }: Props = $props();
27
28
 
28
29
  let element: HTMLElement;
29
30
  let isOpen = $state(false);
@@ -60,7 +61,6 @@
60
61
 
61
62
  const pluginKey = $derived(path);
62
63
  const updateDelay = 250;
63
- const richTextContext = $derived(getRichTextContext(path));
64
64
 
65
65
  const shouldShow = ({ editor }: { editor: Editor }) => {
66
66
  return (
@@ -87,11 +87,11 @@
87
87
  updateDelay,
88
88
  options: {
89
89
  onShow() {
90
- richTextContext.bubbleOpen = true;
90
+ context.bubbleOpen = true;
91
91
  isOpen = true;
92
92
  },
93
93
  onHide() {
94
- richTextContext.bubbleOpen = false;
94
+ context.bubbleOpen = false;
95
95
  isOpen = false;
96
96
  }
97
97
  }
@@ -128,7 +128,7 @@
128
128
  options={item.options}
129
129
  {editor}
130
130
  {path}
131
- context={richTextContext}
131
+ {context}
132
132
  />
133
133
  {:else if item.bubbleMenu?.command}
134
134
  <IconButton
@@ -1,7 +1,9 @@
1
1
  import type { Editor } from '@tiptap/core';
2
2
  import type { RichTextFeature } from '../../core/types.js';
3
+ import type { RichTextContext } from '../context.svelte';
3
4
  import './bubble-menu.css';
4
5
  type Props = {
6
+ context: RichTextContext;
5
7
  editor: Editor;
6
8
  features: RichTextFeature[];
7
9
  path: string;
@@ -13,6 +13,7 @@
13
13
  }
14
14
 
15
15
  /** Show drag handle only when more than 1 node */
16
+
16
17
  .ProseMirror:not(:has(> *:nth-child(2))) + div > .rz-rich-text-drag-handle {
17
18
  display: none !important;
18
19
  }
@@ -22,7 +22,6 @@
22
22
  display: inline-block;
23
23
  }
24
24
  }
25
-
26
25
  /** Disabled style */
27
26
  .rz-rich-text__editor > [contenteditable='false'] {
28
27
  color: hsl(var(--rz-color-fg) / 0.5);
@@ -32,7 +31,6 @@
32
31
  opacity: 0.9;
33
32
  }
34
33
  }
35
-
36
34
  /** All editor > node */
37
35
  .ProseMirror > *:not(.ProseMirror-gapcursor) {
38
36
  background-color: hsl(var(--rz-input-bg));
@@ -55,16 +53,13 @@
55
53
  border-bottom: 1px solid var(--rz-input-border-color);
56
54
  }
57
55
  }
58
-
59
56
  /** Disable focus-style on the whole editor */
60
57
  .ProseMirror-focused {
61
58
  outline: none;
62
59
  }
63
-
64
60
  .rz-rich-text__editor[data-error]:has(.ProseMirror-focused) {
65
61
  border-color: hsl(var(--rz-color-alert));
66
62
  }
67
-
68
63
  .rz-rich-text__editor .ProseMirror .is-empty:not(table)::before {
69
64
  content: attr(data-placeholder);
70
65
  color: hsl(var(--rz-color-fg) / 0.3);
@@ -77,7 +72,6 @@
77
72
  .rz-rich-text__editor .ProseMirror blockquote.is-empty::before {
78
73
  margin-left: var(--rz-size-6);
79
74
  }
80
-
81
75
  /** Text styles */
82
76
  .rz-rich-text__editor .ProseMirror {
83
77
  h1 {
@@ -176,7 +170,6 @@
176
170
  font-weight: 500;
177
171
  }
178
172
  }
179
-
180
173
  /** Fix gap cursor position */
181
174
  .rz-field-rich-text .ProseMirror-gapcursor:after {
182
175
  left: calc(var(--left-spacing) + var(--rz-size-4));
@@ -127,13 +127,7 @@
127
127
  </Command.List>
128
128
  </Command.Dialog>
129
129
 
130
- <style>/**************************************/
131
-
132
- /* Font */
133
-
134
- /**************************************/
135
-
136
- :global(.ProseMirror-focused .ProseMirror-selectednode .rz-richtext-resource) {
130
+ <style>:global(.ProseMirror-focused .ProseMirror-selectednode .rz-richtext-resource) {
137
131
  :global(.rz-card-resource),
138
132
  :global(button.rz-richtext-resource__add) {
139
133
  box-shadow:
@@ -141,8 +135,7 @@
141
135
  0 0 0 calc(var(--rz-ring-offset, 0px) + 1px) hsl(var(--rz-color-spot) / var(--rz-ring-opacity, 1));
142
136
  }
143
137
  }
144
-
145
- .rz-richtext-resource {
138
+ .rz-richtext-resource {
146
139
  position: relative;
147
140
  --rz-border-radius: var(--rz-radius-xl);
148
141
  --rz-ressource-card-thumbnail-bg: light-dark(hsl(var(--rz-gray-15)), hsl(var(--rz-gray-3)));
@@ -207,13 +207,7 @@
207
207
  </Dialog.Content>
208
208
  </Dialog.Root>
209
209
 
210
- <style>/**************************************/
211
-
212
- /* Font */
213
-
214
- /**************************************/
215
-
216
- :global(.ProseMirror-selectednode .rz-richtext-media) {
210
+ <style>:global(.ProseMirror-selectednode .rz-richtext-media) {
217
211
  .rz-richtext-media__media,
218
212
  :global(button.rz-richtext-media__add) {
219
213
  box-shadow:
@@ -225,16 +219,15 @@
225
219
  }
226
220
  }
227
221
 
228
- .rz-richtext-media {
222
+ .rz-richtext-media {
229
223
  position: relative;
230
224
  }
231
225
 
232
- .rz-richtext-media__media {
226
+ .rz-richtext-media__media {
233
227
  border-radius: var(--rz-radius-lg);
234
228
  overflow: hidden;
235
229
  }
236
-
237
- .rz-richtext-media__actions {
230
+ .rz-richtext-media__actions {
238
231
  position: absolute;
239
232
  font-size: var(--rz-text-sm);
240
233
  right: var(--rz-size-3);
@@ -261,7 +254,7 @@
261
254
  }
262
255
  }
263
256
 
264
- .rz-richtext-media__legend {
257
+ .rz-richtext-media__legend {
265
258
  font-style: italic;
266
259
  font-size: var(--rz-text-sm);
267
260
  opacity: 0.5;
@@ -137,16 +137,18 @@
137
137
  <li {...node.attrs}><RenderRichText {components} json={node} /></li>
138
138
  {/if}
139
139
  <!-- -->
140
- {:else if node.type === 'media'}
140
+ {:else if node.type?.startsWith('rich-text-upload-')}
141
141
  {#if components?.media}
142
142
  <components.media {components} {node}>
143
143
  <RenderRichText {components} json={node} />
144
144
  </components.media>
145
+ {:else if node.attrs?.url && node.attrs.mimeType?.startsWith('image/')}
146
+ <img alt={node.attrs?.alt || ''} style="width:100%;height:auto;" src={node.attrs?.url} />
145
147
  {:else}
146
148
  [Provide a media component to render this node]
147
149
  {/if}
148
150
  <!-- -->
149
- {:else if node.type === 'resource'}
151
+ {:else if node.type?.startsWith('rich-text-resource-')}
150
152
  {#if components?.resource}
151
153
  <components.resource {components} {node}>
152
154
  <RenderRichText {components} json={node} />
@@ -154,7 +156,7 @@
154
156
  {:else}
155
157
  [Provide a resource component to render this node]
156
158
  {/if}
157
- {:else if node.type === 'fields'}
159
+ {:else if node.type?.startsWith('rich-text-fields-')}
158
160
  {#if components?.fields}
159
161
  <components.fields {components} {node}>
160
162
  <RenderRichText {components} json={node} />
@@ -46,7 +46,7 @@ export declare const isJSONContent: (value: any) => value is JSONContent;
46
46
  * // Returns "Hello world"
47
47
  * richTextJSONToText('{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"Hello world"}]}]}');
48
48
  */
49
- export declare const richTextJSONToText: (value: string | JSONContent) => string;
49
+ export declare const richTextJSONToText: (value: string | JSONContent | undefined) => string;
50
50
  type RichTextContent = {
51
51
  type: 'doc';
52
52
  content: JSONContent[];
@@ -151,13 +151,7 @@
151
151
  <Field.Hint {config} />
152
152
  </fieldset>
153
153
 
154
- <style>/**************************************/
155
-
156
- /* Font */
157
-
158
- /**************************************/
159
-
160
- .rz-select {
154
+ <style>.rz-select {
161
155
  margin-bottom: var(--rz-size-2);
162
156
  position: relative;
163
157
 
@@ -187,7 +181,7 @@
187
181
  }
188
182
  }
189
183
 
190
- .rz-select__list {
184
+ .rz-select__list {
191
185
  background-color: hsl(var(--rz-input-bg));
192
186
  border: var(--rz-border);
193
187
  border-radius: var(--rz-radius-md);
@@ -198,19 +192,19 @@
198
192
  padding: var(--rz-size-2) var(--rz-size-3);
199
193
  }
200
194
 
201
- .rz-select__list[data-focused] {
195
+ .rz-select__list[data-focused] {
202
196
  box-shadow:
203
197
  0 0 0 var(--rz-ring-offset, 0px) hsl(var(--rz-ring-offset-bg, var(--rz-gray-6)) / 1),
204
198
  0 0 0 calc(var(--rz-ring-offset, 0px) + 1px) hsl(var(--rz-color-ring) / var(--rz-ring-opacity, 1));
205
199
  }
206
200
 
207
- .rz-select__list[data-error] {
201
+ .rz-select__list[data-error] {
208
202
  box-shadow:
209
203
  0 0 0 var(--rz-ring-offset, 0px) hsl(var(--rz-ring-offset-bg, var(--rz-gray-6)) / 1),
210
204
  0 0 0 calc(var(--rz-ring-offset, 0px) + 1px) hsl(var(--rz-color-alert) / var(--rz-ring-opacity, 1));
211
205
  }
212
206
 
213
- .rz-select__list--readonly {
207
+ .rz-select__list--readonly {
214
208
  cursor: no-drop;
215
209
  }
216
210
  </style>
@@ -43,6 +43,7 @@ export declare class TabBuilder {
43
43
  constructor(name: string);
44
44
  label(label: string): this;
45
45
  get name(): string;
46
+ get tab(): TabsFieldTab;
46
47
  fields(...fields: FieldBuilder<Field>[]): this;
47
48
  get raw(): {
48
49
  name: string;
@@ -36,6 +36,9 @@ export class TabBuilder {
36
36
  get name() {
37
37
  return this.#tab.name;
38
38
  }
39
+ get tab() {
40
+ return this.#tab;
41
+ }
39
42
  fields(...fields) {
40
43
  this.#tab.fields = fields;
41
44
  return this;
@@ -28,13 +28,7 @@
28
28
  <Field.Hint {config} />
29
29
  </fieldset>
30
30
 
31
- <style>/**************************************/
32
-
33
- /* Font */
34
-
35
- /**************************************/
36
-
37
- textarea {
31
+ <style>textarea {
38
32
  field-sizing: content;
39
33
  border: 1px solid var(--rz-input-border-color);
40
34
  background-color: hsl(var(--rz-input-bg));
@@ -54,20 +48,18 @@ textarea {
54
48
  }
55
49
  }
56
50
 
57
- textarea:disabled {
51
+ textarea:disabled {
58
52
  opacity: 0.5;
59
53
  cursor: not-allowed;
60
54
  }
61
-
62
- textarea::-moz-placeholder {
55
+ textarea::-moz-placeholder {
63
56
  color: hsl(var(--rz-color-fg) / 0.5);
64
57
  }
65
-
66
- textarea::placeholder {
58
+ textarea::placeholder {
67
59
  color: hsl(var(--rz-color-fg) / 0.5);
68
60
  }
69
61
 
70
- textarea:focus-visible {
62
+ textarea:focus-visible {
71
63
  outline: none;
72
64
  /* --rz-ring-offset: 1px; */
73
65
  box-shadow:
@@ -118,31 +118,25 @@
118
118
  {/if}
119
119
  </div>
120
120
 
121
- <style>/**************************************/
122
-
123
- /* Font */
124
-
125
- /**************************************/
126
-
127
- .rz-tree-item {
121
+ <style>.rz-tree-item {
128
122
  position: relative;
129
123
  }
130
124
 
131
- .rz-tree-item__grip {
125
+ .rz-tree-item__grip {
132
126
  cursor: grab;
133
127
  position: absolute;
134
128
  top: 1rem;
135
129
  left: -1rem;
136
130
  }
137
131
 
138
- .rz-tree-item__header:hover {
132
+ .rz-tree-item__header:hover {
139
133
  > :global(.rz-tree-item-actions) {
140
134
  opacity: 1;
141
135
  pointer-events: all;
142
136
  }
143
137
  }
144
138
 
145
- .rz-tree-item__header {
139
+ .rz-tree-item__header {
146
140
  display: flex;
147
141
  align-items: center;
148
142
  justify-content: space-between;
@@ -155,7 +149,7 @@
155
149
  width: 100%;
156
150
  }
157
151
 
158
- .rz-tree-item__content--closed {
152
+ .rz-tree-item__content--closed {
159
153
  .rz-tree-item__header {
160
154
  border-radius: var(--rz-radius-md);
161
155
  }
@@ -166,20 +160,20 @@
166
160
  }
167
161
  }
168
162
 
169
- .rz-tree-item__content + .rz-tree-item__list-placeholder,
163
+ .rz-tree-item__content + .rz-tree-item__list-placeholder,
170
164
  .rz-tree-item__content + .rz-tree__list {
171
165
  margin-top: 1rem;
172
166
  min-height: 5px;
173
167
  }
174
168
 
175
- .rz-tree-item__title {
169
+ .rz-tree-item__title {
176
170
  display: flex;
177
171
  align-items: center;
178
172
  justify-content: center;
179
173
  gap: var(--rz-size-2);
180
174
  }
181
175
 
182
- .rz-tree-item__fields {
176
+ .rz-tree-item__fields {
183
177
  --rz-fields-padding: var(--rz-size-4);
184
178
  background-color: var(--rz-collapse-fields-content-bg);
185
179
  border: var(--rz-border);
@@ -190,21 +184,21 @@
190
184
  flex: 1;
191
185
  }
192
186
 
193
- .rz-tree-item__fields--hidden {
187
+ .rz-tree-item__fields--hidden {
194
188
  display: none;
195
189
  }
196
190
 
197
- .rz-tree-item[data-sorting='true'] .rz-tree-item__grip {
191
+ .rz-tree-item[data-sorting='true'] .rz-tree-item__grip {
198
192
  display: none;
199
193
  }
200
194
 
201
- .rz-tree-item__content--closed:has(:global(.rz-field-error)) {
195
+ .rz-tree-item__content--closed:has(:global(.rz-field-error)) {
202
196
  box-shadow:
203
197
  0 0 0 var(--rz-ring-offset, 0px) hsl(var(--rz-ring-offset-bg, var(--rz-gray-6)) / 1),
204
198
  0 0 0 calc(var(--rz-ring-offset, 0px) + 1px) hsl(var(--rz-color-alert) / var(--rz-ring-opacity, 1));
205
199
  }
206
200
 
207
- :global(.rz-tree-item-actions) {
201
+ :global(.rz-tree-item-actions) {
208
202
  opacity: 0;
209
203
  pointer-events: none;
210
204
  }
@@ -107,11 +107,12 @@ export type OptionWithIcon = {
107
107
  label?: string;
108
108
  icon?: Component<IconProps>;
109
109
  };
110
- export type RelationValue<T> = T[] | {
110
+ export type RelationRef = {
111
111
  id?: string;
112
112
  relationTo: string;
113
113
  documentId: string;
114
- }[] | string[] | string;
114
+ };
115
+ export type RelationValue<T> = T[] | RelationRef[] | string[] | string;
115
116
  export type SimplerField<T extends FormField> = WithRequired<Partial<T>, 'name' | 'isEmpty' | 'type'>;
116
117
  export type BaseUseFieldReturn = {
117
118
  path: string;