@aphexcms/cms-core 9.0.0 → 9.2.0

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 (114) hide show
  1. package/dist/cli/generate-types.d.ts.map +1 -1
  2. package/dist/cli/generate-types.js +197 -8
  3. package/dist/cli/generate-types.js.map +1 -1
  4. package/dist/components/AdminApp.svelte +46 -137
  5. package/dist/components/AdminApp.svelte.d.ts.map +1 -1
  6. package/dist/components/admin/DocumentEditor.svelte +237 -206
  7. package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
  8. package/dist/components/admin/DocumentVersionPanel.svelte +3 -2
  9. package/dist/components/admin/DocumentVersionPanel.svelte.d.ts.map +1 -1
  10. package/dist/components/admin/MediaBrowser.svelte +22 -4
  11. package/dist/components/admin/MediaBrowser.svelte.d.ts.map +1 -1
  12. package/dist/components/admin/ObjectModal.svelte +62 -75
  13. package/dist/components/admin/ObjectModal.svelte.d.ts.map +1 -1
  14. package/dist/components/admin/SchemaField.svelte +2 -2
  15. package/dist/components/admin/fields/ArrayField.svelte +9 -1
  16. package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +1 -1
  17. package/dist/components/admin/fields/ReferenceField.svelte +37 -8
  18. package/dist/components/admin/fields/ReferenceField.svelte.d.ts.map +1 -1
  19. package/dist/components/admin/fields/richtext/PortableTextImageView.svelte +116 -0
  20. package/dist/components/admin/fields/richtext/PortableTextImageView.svelte.d.ts +12 -0
  21. package/dist/components/admin/fields/richtext/PortableTextImageView.svelte.d.ts.map +1 -0
  22. package/dist/components/admin/fields/richtext/PortableTextInlineView.svelte +111 -0
  23. package/dist/components/admin/fields/richtext/PortableTextInlineView.svelte.d.ts +12 -0
  24. package/dist/components/admin/fields/richtext/PortableTextInlineView.svelte.d.ts.map +1 -0
  25. package/dist/components/admin/fields/richtext/PortableTextObjectView.svelte +67 -0
  26. package/dist/components/admin/fields/richtext/PortableTextObjectView.svelte.d.ts +12 -0
  27. package/dist/components/admin/fields/richtext/PortableTextObjectView.svelte.d.ts.map +1 -0
  28. package/dist/components/admin/fields/richtext/RichtextField.svelte +1314 -0
  29. package/dist/components/admin/fields/richtext/RichtextField.svelte.d.ts +15 -0
  30. package/dist/components/admin/fields/richtext/RichtextField.svelte.d.ts.map +1 -0
  31. package/dist/components/admin/fields/richtext/custom-annotation-mark.d.ts +7 -0
  32. package/dist/components/admin/fields/richtext/custom-annotation-mark.d.ts.map +1 -0
  33. package/dist/components/admin/fields/richtext/custom-annotation-mark.js +33 -0
  34. package/dist/components/admin/fields/richtext/portable-text-inline-object.d.ts +11 -0
  35. package/dist/components/admin/fields/richtext/portable-text-inline-object.d.ts.map +1 -0
  36. package/dist/components/admin/fields/richtext/portable-text-inline-object.js +31 -0
  37. package/dist/components/admin/fields/richtext/portable-text-object-node.d.ts +11 -0
  38. package/dist/components/admin/fields/richtext/portable-text-object-node.d.ts.map +1 -0
  39. package/dist/components/admin/fields/richtext/portable-text-object-node.js +31 -0
  40. package/dist/components/admin/fields/richtext/portable-text-serializer.d.ts +38 -0
  41. package/dist/components/admin/fields/richtext/portable-text-serializer.d.ts.map +1 -0
  42. package/dist/components/admin/fields/richtext/portable-text-serializer.js +318 -0
  43. package/dist/components/admin/fields/richtext/svelte-inline-node-view.d.ts +8 -0
  44. package/dist/components/admin/fields/richtext/svelte-inline-node-view.d.ts.map +1 -0
  45. package/dist/components/admin/fields/richtext/svelte-inline-node-view.js +67 -0
  46. package/dist/components/admin/fields/richtext/svelte-node-view.d.ts +8 -0
  47. package/dist/components/admin/fields/richtext/svelte-node-view.d.ts.map +1 -0
  48. package/dist/components/admin/fields/richtext/svelte-node-view.js +63 -0
  49. package/dist/db/interfaces/document.d.ts +8 -2
  50. package/dist/db/interfaces/document.d.ts.map +1 -1
  51. package/dist/graphql/resolvers.d.ts.map +1 -1
  52. package/dist/graphql/resolvers.js +13 -11
  53. package/dist/graphql/schema.d.ts.map +1 -1
  54. package/dist/graphql/schema.js +7 -3
  55. package/dist/lib/components/admin/fields/richtext/custom-annotation-mark.d.ts +7 -0
  56. package/dist/lib/components/admin/fields/richtext/custom-annotation-mark.d.ts.map +1 -0
  57. package/dist/lib/components/admin/fields/richtext/custom-annotation-mark.js +34 -0
  58. package/dist/lib/components/admin/fields/richtext/custom-annotation-mark.js.map +1 -0
  59. package/dist/lib/components/admin/fields/richtext/portable-text-inline-object.d.ts +11 -0
  60. package/dist/lib/components/admin/fields/richtext/portable-text-inline-object.d.ts.map +1 -0
  61. package/dist/lib/components/admin/fields/richtext/portable-text-inline-object.js +32 -0
  62. package/dist/lib/components/admin/fields/richtext/portable-text-inline-object.js.map +1 -0
  63. package/dist/lib/components/admin/fields/richtext/portable-text-object-node.d.ts +11 -0
  64. package/dist/lib/components/admin/fields/richtext/portable-text-object-node.d.ts.map +1 -0
  65. package/dist/lib/components/admin/fields/richtext/portable-text-object-node.js +32 -0
  66. package/dist/lib/components/admin/fields/richtext/portable-text-object-node.js.map +1 -0
  67. package/dist/lib/components/admin/fields/richtext/portable-text-serializer.d.ts +38 -0
  68. package/dist/lib/components/admin/fields/richtext/portable-text-serializer.d.ts.map +1 -0
  69. package/dist/lib/components/admin/fields/richtext/portable-text-serializer.js +319 -0
  70. package/dist/lib/components/admin/fields/richtext/portable-text-serializer.js.map +1 -0
  71. package/dist/lib/components/admin/fields/richtext/svelte-inline-node-view.d.ts +8 -0
  72. package/dist/lib/components/admin/fields/richtext/svelte-inline-node-view.d.ts.map +1 -0
  73. package/dist/lib/components/admin/fields/richtext/svelte-inline-node-view.js +68 -0
  74. package/dist/lib/components/admin/fields/richtext/svelte-inline-node-view.js.map +1 -0
  75. package/dist/lib/components/admin/fields/richtext/svelte-node-view.d.ts +8 -0
  76. package/dist/lib/components/admin/fields/richtext/svelte-node-view.d.ts.map +1 -0
  77. package/dist/lib/components/admin/fields/richtext/svelte-node-view.js +64 -0
  78. package/dist/lib/components/admin/fields/richtext/svelte-node-view.js.map +1 -0
  79. package/dist/lib/db/interfaces/document.d.ts +8 -2
  80. package/dist/lib/db/interfaces/document.d.ts.map +1 -1
  81. package/dist/lib/graphql/resolvers.d.ts.map +1 -1
  82. package/dist/lib/graphql/resolvers.js +13 -11
  83. package/dist/lib/graphql/resolvers.js.map +1 -1
  84. package/dist/lib/graphql/schema.d.ts.map +1 -1
  85. package/dist/lib/graphql/schema.js +7 -3
  86. package/dist/lib/graphql/schema.js.map +1 -1
  87. package/dist/lib/local-api/collection-api.d.ts.map +1 -1
  88. package/dist/lib/local-api/collection-api.js +7 -0
  89. package/dist/lib/local-api/collection-api.js.map +1 -1
  90. package/dist/lib/schema-utils/validator.d.ts.map +1 -1
  91. package/dist/lib/schema-utils/validator.js +5 -1
  92. package/dist/lib/schema-utils/validator.js.map +1 -1
  93. package/dist/lib/server/api/routes/assets-by-id.d.ts.map +1 -1
  94. package/dist/lib/server/api/routes/assets-by-id.js +10 -2
  95. package/dist/lib/server/api/routes/assets-by-id.js.map +1 -1
  96. package/dist/lib/server/api/routes/assets-references.d.ts.map +1 -1
  97. package/dist/lib/server/api/routes/assets-references.js +6 -4
  98. package/dist/lib/server/api/routes/assets-references.js.map +1 -1
  99. package/dist/lib/types/schemas.d.ts +27 -0
  100. package/dist/lib/types/schemas.d.ts.map +1 -1
  101. package/dist/lib/types/schemas.js +3 -1
  102. package/dist/lib/types/schemas.js.map +1 -1
  103. package/dist/local-api/collection-api.d.ts.map +1 -1
  104. package/dist/local-api/collection-api.js +7 -0
  105. package/dist/schema-utils/validator.d.ts.map +1 -1
  106. package/dist/schema-utils/validator.js +5 -1
  107. package/dist/server/api/routes/assets-by-id.d.ts.map +1 -1
  108. package/dist/server/api/routes/assets-by-id.js +10 -2
  109. package/dist/server/api/routes/assets-references.d.ts.map +1 -1
  110. package/dist/server/api/routes/assets-references.js +6 -4
  111. package/dist/types/schemas.d.ts +27 -0
  112. package/dist/types/schemas.d.ts.map +1 -1
  113. package/dist/types/schemas.js +3 -1
  114. package/package.json +6 -1
@@ -53,10 +53,6 @@
53
53
  const backdropZ = $derived(50 + Math.max(myDepth - 1, 0) * 10);
54
54
  const panelZ = $derived(backdropZ + 1);
55
55
 
56
- // Handle backdrop click — only close if this is the topmost modal, so a
57
- // click that visually lands on a parent's backdrop (because the inner
58
- // backdrop doesn't extend that far) doesn't accidentally close the
59
- // parent.
60
56
  function isTopmost(): boolean {
61
57
  return myDepth === modalStack;
62
58
  }
@@ -75,89 +71,80 @@
75
71
  </script>
76
72
 
77
73
  {#if open}
78
- <!-- NOTE: scoped to the editor area via `absolute inset-0` (DocumentEditor
79
- is the nearest positioned ancestor). The trade-off: a nested modal's
80
- backdrop only covers its parent panel, so clicks in the truly
81
- "outside" region land on the grandparent backdrop. The modalStack
82
- check above neutralises that case (only the topmost modal reacts).
83
- We may later move this to a viewport-level portal so nested
84
- backdrops cover the full screen, but for now keeping it inside the
85
- editor is the design we want. -->
86
74
  <div
87
- class="bg-background/80 absolute inset-0"
75
+ class="bg-background/80 absolute inset-0 flex items-center justify-center p-4 lg:p-6"
88
76
  style="z-index: {backdropZ}"
89
77
  onclick={handleBackdropClick}
90
78
  onkeydown={handleKeydown}
91
79
  role="button"
92
80
  tabindex="-1"
93
- ></div>
94
-
95
- <!-- Panel: centered dialog. max-h is relative to the editor container
96
- so tall schemas scroll inside the panel instead of being clipped;
97
- min-h-0 on the body (below) is what actually lets the inner
98
- overflow-auto take effect inside this flex column. -->
99
- <div
100
- class="border-border bg-background absolute top-1/2 left-1/2 flex max-h-[calc(100%-4rem)] w-[calc(100%-2rem)] max-w-3xl -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-lg border shadow-lg"
101
- style="z-index: {panelZ}"
102
81
  >
82
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
103
83
  <div
104
- class="border-border bg-background flex shrink-0 items-center justify-between gap-3 border-b px-4 py-2"
84
+ class="border-border bg-background max-h-full w-full max-w-3xl overflow-auto rounded-lg border shadow-lg"
85
+ style="z-index: {panelZ}"
86
+ onclick={(e) => e.stopPropagation()}
87
+ onkeydown={(e) => e.stopPropagation()}
105
88
  >
106
- <span class="text-sm font-medium">Edit {schema.title}</span>
107
- <div class="flex items-center gap-2">
108
- {#if saveState}
109
- {#if saveState.saving}
110
- <span
111
- class="text-muted-foreground inline-flex items-center gap-1.5 text-[10px] font-medium tracking-wider whitespace-nowrap uppercase"
112
- >
113
- <span class="bg-muted-foreground/60 h-1.5 w-1.5 animate-pulse rounded-full"></span>
114
- Saving
115
- </span>
116
- {:else if saveState.hasUnsavedChanges}
117
- <span
118
- class="text-muted-foreground inline-flex items-center gap-1.5 text-[10px] font-medium tracking-wider whitespace-nowrap uppercase"
119
- >
120
- <span class="bg-muted-foreground/60 h-1.5 w-1.5 rounded-full"></span>
121
- Unsaved
122
- </span>
123
- {:else if saveState.savedAgoText}
124
- <span
125
- class="text-muted-foreground inline-flex items-center gap-1.5 text-[10px] font-medium tracking-wider whitespace-nowrap uppercase"
126
- >
127
- <span class="bg-muted-foreground/60 h-1.5 w-1.5 rounded-full"></span>
128
- Auto-saved
129
- </span>
89
+ <div
90
+ class="border-border bg-background sticky top-0 z-10 flex items-center justify-between gap-3 border-b px-4 py-2"
91
+ >
92
+ <span class="text-sm font-medium">Edit {schema.title}</span>
93
+ <div class="flex items-center gap-2">
94
+ {#if saveState}
95
+ {#if saveState.saving}
96
+ <span
97
+ class="text-muted-foreground inline-flex items-center gap-1.5 text-[10px] font-medium tracking-wider whitespace-nowrap uppercase"
98
+ >
99
+ <span class="bg-muted-foreground/60 h-1.5 w-1.5 animate-pulse rounded-full"></span>
100
+ Saving
101
+ </span>
102
+ {:else if saveState.hasUnsavedChanges}
103
+ <span
104
+ class="text-muted-foreground inline-flex items-center gap-1.5 text-[10px] font-medium tracking-wider whitespace-nowrap uppercase"
105
+ >
106
+ <span class="bg-muted-foreground/60 h-1.5 w-1.5 rounded-full"></span>
107
+ Unsaved
108
+ </span>
109
+ {:else if saveState.savedAgoText}
110
+ <span
111
+ class="text-muted-foreground inline-flex items-center gap-1.5 text-[10px] font-medium tracking-wider whitespace-nowrap uppercase"
112
+ >
113
+ <span class="bg-muted-foreground/60 h-1.5 w-1.5 rounded-full"></span>
114
+ Auto-saved
115
+ </span>
116
+ {/if}
130
117
  {/if}
131
- {/if}
132
- <Button variant="ghost" size="icon" class="h-7 w-7" onclick={onClose}>
133
- <svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
134
- <path
135
- stroke-linecap="round"
136
- stroke-linejoin="round"
137
- stroke-width="2"
138
- d="M6 18L18 6M6 6l12 12"
139
- />
140
- </svg>
141
- </Button>
118
+ <Button variant="ghost" size="icon" class="h-7 w-7" onclick={onClose}>
119
+ <svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
120
+ <path
121
+ stroke-linecap="round"
122
+ stroke-linejoin="round"
123
+ stroke-width="2"
124
+ d="M6 18L18 6M6 6l12 12"
125
+ />
126
+ </svg>
127
+ </Button>
128
+ </div>
142
129
  </div>
143
- </div>
144
130
 
145
- <div class="bg-background min-h-0 flex-1 space-y-3 overflow-auto p-4">
146
- {#if schema.fields}
147
- {#each schema.fields as field, index (index)}
148
- <SchemaField
149
- {field}
150
- value={(value ?? {})[field.name]}
151
- documentData={value ?? {}}
152
- onUpdate={(newValue) => {
153
- onUpdate({ ...value, [field.name]: newValue });
154
- }}
155
- {onOpenReference}
156
- {readonly}
157
- {organizationId}
158
- />
159
- {/each}
160
- {/if}
131
+ <div class="bg-background space-y-3 p-4">
132
+ {#if schema.fields}
133
+ {#each schema.fields as field, index (index)}
134
+ <SchemaField
135
+ {field}
136
+ value={(value ?? {})[field.name]}
137
+ documentData={value ?? {}}
138
+ onUpdate={(newValue) => {
139
+ onUpdate({ ...value, [field.name]: newValue });
140
+ }}
141
+ {onOpenReference}
142
+ {readonly}
143
+ {organizationId}
144
+ />
145
+ {/each}
146
+ {/if}
147
+ </div>
161
148
  </div>
162
149
  </div>
163
150
  {/if}
@@ -1 +1 @@
1
- {"version":3,"file":"ObjectModal.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/admin/ObjectModal.svelte.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAKxD,UAAU,KAAK;IACd,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;IAC/C,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AA6GF,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"ObjectModal.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/admin/ObjectModal.svelte.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAKxD,UAAU,KAAK;IACd,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;IAC/C,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAwGF,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -61,7 +61,7 @@
61
61
  }: Props = $props();
62
62
 
63
63
  // Build full field path
64
- const fieldPath = parentPath ? `${parentPath}.${field.name}` : field.name;
64
+ const fieldPath = $derived(parentPath ? `${parentPath}.${field.name}` : field.name);
65
65
 
66
66
  // Validation state for the wrapper (displays errors and status)
67
67
  let validationErrors = $state<ValidationError[]>([]);
@@ -239,7 +239,7 @@
239
239
  {/each}
240
240
  </div>
241
241
 
242
- <!-- Array Field -->
242
+ <!-- Array Field (includes block content editor when of contains {type: 'block'}) -->
243
243
  {:else if field.type === 'array' && field.of}
244
244
  <ArrayField {field} {value} {onUpdate} {onOpenReference} {readonly} {organizationId} />
245
245
 
@@ -33,6 +33,7 @@
33
33
  import { documents } from '../../../api/documents';
34
34
  import { SvelteMap } from 'svelte/reactivity';
35
35
  import { getDocumentVersion } from '../../../document-refresh.svelte';
36
+ import RichtextField from './richtext/RichtextField.svelte';
36
37
 
37
38
  interface Props {
38
39
  field: ArrayFieldType;
@@ -73,6 +74,7 @@
73
74
  return getSchemaByName(schemas, typeName);
74
75
  }
75
76
 
77
+ const isBlockArray = $derived(field.of.some((ref) => ref.type === 'block'));
76
78
  const isGridLayout = $derived(field.options?.layout === 'grid');
77
79
  const isReferenceArray = $derived(
78
80
  field.of && field.of.length > 0 && field.of[0]?.type === 'reference'
@@ -494,7 +496,9 @@
494
496
  }
495
497
  </script>
496
498
 
497
- {#if isReferenceArray && referenceFieldShape}
499
+ {#if isBlockArray}
500
+ <RichtextField {field} {value} {onUpdate} {readonly} {onOpenReference} {organizationId} />
501
+ {:else if isReferenceArray && referenceFieldShape}
498
502
  <!-- Reference array — each row reuses ReferenceField. Drag handle on the
499
503
  left, ReferenceField fills the row, an X button on empty rows lets the
500
504
  user discard a never-picked row (populated rows use ReferenceField's
@@ -599,6 +603,8 @@
599
603
  class="border-border/50 bg-background group relative aspect-square overflow-hidden rounded-md border"
600
604
  class:opacity-50={sortable.isDragging}
601
605
  >
606
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
607
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
602
608
  <div
603
609
  class="h-full w-full cursor-pointer"
604
610
  onclick={() => handleOpenImageModal(index)}
@@ -695,6 +701,8 @@
695
701
  </button>
696
702
  {/if}
697
703
 
704
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
705
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
698
706
  <div
699
707
  class="min-w-0 flex-1 cursor-pointer text-left"
700
708
  onclick={() => handleOpenImageModal(index)}
@@ -1 +1 @@
1
- {"version":3,"file":"ArrayField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ArrayField.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAc,MAAM,wBAAwB,CAAC;AA8BtF,UAAU,KAAK;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAq5BF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"ArrayField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ArrayField.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAc,MAAM,wBAAwB,CAAC;AA+BtF,UAAU,KAAK;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AA65BF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -42,14 +42,16 @@
42
42
  const refId = $derived(value?._ref ?? null);
43
43
 
44
44
  // Cast to reference field type
45
- const referenceField = field as ReferenceFieldType;
46
- const targetType = referenceField.to?.[0]?.type;
45
+ const referenceField = $derived(field as ReferenceFieldType);
46
+ const targetType = $derived(referenceField.to?.[0]?.type);
47
47
 
48
48
  const schemas = getSchemaContext();
49
49
  const targetSchema = $derived(targetType ? getSchemaByName(schemas, targetType) : null);
50
50
  const TargetIcon = $derived(targetSchema?.icon ?? null);
51
51
 
52
52
  // State
53
+ let inputWrapperEl = $state<HTMLElement>();
54
+ let dropdownPos = $state({ top: 0, left: 0, width: 0 });
53
55
  let open = $state(false);
54
56
  let searchResults = $state<any[]>([]);
55
57
  let selectedDocument = $state<any>(null);
@@ -145,6 +147,18 @@
145
147
  });
146
148
  });
147
149
 
150
+ function updateDropdownPos() {
151
+ if (!inputWrapperEl) return;
152
+ const rect = inputWrapperEl.getBoundingClientRect();
153
+ dropdownPos = { top: rect.bottom + 4, left: rect.left, width: rect.width };
154
+ }
155
+
156
+ $effect(() => {
157
+ if (open && inputWrapperEl) {
158
+ updateDropdownPos();
159
+ }
160
+ });
161
+
148
162
  function closeAndFocusTrigger() {
149
163
  open = false;
150
164
  }
@@ -291,22 +305,33 @@
291
305
  {:else}
292
306
  <!-- Inline empty state: search input + chevron + Create button. The input
293
307
  is the primary focus target — the results dropdown is a sibling div
294
- positioned absolutely under the input wrapper, with click-outside
295
- closing it. No popover wrapping the input, so no double-click. -->
308
+ positioned with fixed so it escapes overflow containers (e.g. modals),
309
+ with click-outside closing it. No popover wrapping the input, so no
310
+ double-click. -->
296
311
  <div class="flex items-center gap-1">
297
- <div class="relative min-w-0 flex-1" use:clickOutside={() => (open = false)}>
312
+ <div
313
+ bind:this={inputWrapperEl}
314
+ class="relative min-w-0 flex-1"
315
+ use:clickOutside={() => (open = false)}
316
+ >
298
317
  <div
299
318
  class="border-input bg-background focus-within:ring-ring flex h-9 items-center rounded-md border focus-within:ring-1"
300
319
  >
301
320
  <Input
302
321
  bind:value={query}
303
322
  placeholder="Type to search..."
304
- onfocus={() => (open = true)}
323
+ onfocus={() => {
324
+ updateDropdownPos();
325
+ open = true;
326
+ }}
305
327
  class="h-8 border-0 bg-transparent shadow-none focus-visible:ring-0"
306
328
  />
307
329
  <button
308
330
  type="button"
309
- onclick={() => (open = !open)}
331
+ onclick={() => {
332
+ updateDropdownPos();
333
+ open = !open;
334
+ }}
310
335
  class="text-muted-foreground hover:text-foreground flex h-8 w-8 shrink-0 items-center justify-center"
311
336
  aria-label="Toggle results"
312
337
  >
@@ -315,7 +340,11 @@
315
340
  </div>
316
341
  {#if open}
317
342
  <div
318
- class="bg-popover text-popover-foreground absolute top-full left-0 z-[9999] mt-1 w-[400px] max-w-[min(400px,calc(100vw-2rem))] rounded-md border p-0 shadow-md"
343
+ class="bg-popover text-popover-foreground fixed z-[9999] rounded-md border p-0 shadow-md"
344
+ style="top: {dropdownPos.top}px; left: {dropdownPos.left}px; width: {Math.min(
345
+ 400,
346
+ dropdownPos.width
347
+ )}px;"
319
348
  >
320
349
  <Command.Root shouldFilter={false}>
321
350
  <Command.List class="max-h-[300px] overflow-y-auto">
@@ -1 +1 @@
1
- {"version":3,"file":"ReferenceField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ReferenceField.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,KAAK,EAAwC,MAAM,wBAAwB,CAAC;AAUzF,KAAK,cAAc,GAAG;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1E,UAAU,KAAK;IACd,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,KAAK,IAAI,CAAC;IACjD,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AAqUF,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"ReferenceField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ReferenceField.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,KAAK,EAAwC,MAAM,wBAAwB,CAAC;AAUzF,KAAK,cAAc,GAAG;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1E,UAAU,KAAK;IACd,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,KAAK,IAAI,CAAC;IACjD,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AA4VF,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
@@ -0,0 +1,116 @@
1
+ <script lang="ts">
2
+ import { Trash2, Image as ImageIcon } from '@lucide/svelte';
3
+ import { assets } from '../../../../api/assets';
4
+
5
+ interface Props {
6
+ type: string;
7
+ nodeKey: string;
8
+ data: Record<string, unknown>;
9
+ selected?: boolean;
10
+ onEdit: () => void;
11
+ onDelete: () => void;
12
+ }
13
+
14
+ let { data, selected = false, onEdit, onDelete }: Props = $props();
15
+
16
+ const assetRef = $derived((data?.asset as any)?._ref as string | undefined);
17
+ const assetPromise = $derived(assetRef ? assets.getById(assetRef) : null);
18
+ </script>
19
+
20
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
21
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
22
+ <div class="image-block-view group" class:selected onclick={onEdit}>
23
+ {#if assetPromise}
24
+ {#await assetPromise}
25
+ <div class="image-block-placeholder">
26
+ <div
27
+ class="border-primary h-5 w-5 animate-spin rounded-full border-2 border-t-transparent"
28
+ ></div>
29
+ </div>
30
+ {:then result}
31
+ {#if result.success && result.data?.url}
32
+ <img src={result.data.url} alt={result.data.alt || ''} class="image-block-img" />
33
+ {:else}
34
+ <div class="image-block-placeholder">
35
+ <ImageIcon class="text-muted-foreground h-8 w-8" />
36
+ <span class="text-muted-foreground text-xs">Image not found</span>
37
+ </div>
38
+ {/if}
39
+ {:catch}
40
+ <div class="image-block-placeholder">
41
+ <ImageIcon class="text-muted-foreground h-8 w-8" />
42
+ <span class="text-muted-foreground text-xs">Failed to load</span>
43
+ </div>
44
+ {/await}
45
+ {:else}
46
+ <div class="image-block-placeholder">
47
+ <ImageIcon class="text-muted-foreground h-8 w-8" />
48
+ <span class="text-muted-foreground text-xs">No image</span>
49
+ </div>
50
+ {/if}
51
+
52
+ <div class="image-block-actions">
53
+ <button
54
+ type="button"
55
+ class="text-muted-foreground hover:text-destructive rounded p-1 transition-colors"
56
+ onclick={(e) => {
57
+ e.stopPropagation();
58
+ onDelete();
59
+ }}
60
+ title="Remove"
61
+ >
62
+ <Trash2 class="h-3.5 w-3.5" />
63
+ </button>
64
+ </div>
65
+ </div>
66
+
67
+ <style>
68
+ .image-block-view {
69
+ position: relative;
70
+ width: 100%;
71
+ display: flex;
72
+ align-items: center;
73
+ justify-content: center;
74
+ border: 1px solid var(--border);
75
+ border-radius: var(--radius);
76
+ background: var(--muted);
77
+ margin: 0.5em 0;
78
+ overflow: hidden;
79
+ cursor: pointer;
80
+ transition: border-color 0.15s;
81
+ }
82
+
83
+ .image-block-view.selected {
84
+ border-color: var(--primary);
85
+ box-shadow: 0 0 0 1px var(--primary);
86
+ }
87
+
88
+ .image-block-img {
89
+ display: block;
90
+ max-width: 100%;
91
+ max-height: 400px;
92
+ object-fit: contain;
93
+ }
94
+
95
+ .image-block-placeholder {
96
+ display: flex;
97
+ flex-direction: column;
98
+ align-items: center;
99
+ justify-content: center;
100
+ gap: 0.25rem;
101
+ width: 100%;
102
+ height: 120px;
103
+ }
104
+
105
+ .image-block-actions {
106
+ position: absolute;
107
+ top: 0.5rem;
108
+ right: 0.5rem;
109
+ opacity: 0;
110
+ transition: opacity 0.15s;
111
+ }
112
+
113
+ .image-block-view:hover .image-block-actions {
114
+ opacity: 1;
115
+ }
116
+ </style>
@@ -0,0 +1,12 @@
1
+ interface Props {
2
+ type: string;
3
+ nodeKey: string;
4
+ data: Record<string, unknown>;
5
+ selected?: boolean;
6
+ onEdit: () => void;
7
+ onDelete: () => void;
8
+ }
9
+ declare const PortableTextImageView: import("svelte").Component<Props, {}, "">;
10
+ type PortableTextImageView = ReturnType<typeof PortableTextImageView>;
11
+ export default PortableTextImageView;
12
+ //# sourceMappingURL=PortableTextImageView.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PortableTextImageView.svelte.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/admin/fields/richtext/PortableTextImageView.svelte.ts"],"names":[],"mappings":"AAOC,UAAU,KAAK;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACrB;AAwDF,QAAA,MAAM,qBAAqB,2CAAwC,CAAC;AACpE,KAAK,qBAAqB,GAAG,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACtE,eAAe,qBAAqB,CAAC"}
@@ -0,0 +1,111 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ type: string;
4
+ nodeKey: string;
5
+ data: Record<string, unknown>;
6
+ selected: boolean;
7
+ onEdit: () => void;
8
+ onDelete: () => void;
9
+ }
10
+
11
+ let { type, data, selected, onEdit, onDelete }: Props = $props();
12
+
13
+ const label = $derived(
14
+ (data?.title as string) || (data?.text as string) || (data?.name as string) || type
15
+ );
16
+ </script>
17
+
18
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
19
+ <span
20
+ class="inline-object-chip"
21
+ class:selected
22
+ onclick={onEdit}
23
+ title="Edit {type}"
24
+ role="button"
25
+ tabindex="0"
26
+ onkeydown={(e) => {
27
+ if (e.key === 'Enter') onEdit();
28
+ }}
29
+ >
30
+ <span class="inline-object-type">{type.slice(0, 2).toUpperCase()}</span>
31
+ <span class="inline-object-label">{label}</span>
32
+ <button
33
+ class="inline-object-delete"
34
+ onclick={(e) => {
35
+ e.stopPropagation();
36
+ onDelete();
37
+ }}
38
+ title="Remove">×</button
39
+ >
40
+ </span>
41
+
42
+ <style>
43
+ .inline-object-chip {
44
+ display: inline-flex;
45
+ align-items: center;
46
+ gap: 2px;
47
+ padding: 1px 6px 1px 2px;
48
+ border-radius: 4px;
49
+ border: 1px solid var(--border);
50
+ background: var(--muted);
51
+ font-size: 0.75rem;
52
+ line-height: 1.4;
53
+ cursor: pointer;
54
+ vertical-align: baseline;
55
+ transition: border-color 0.15s;
56
+ }
57
+
58
+ .inline-object-chip:hover,
59
+ .inline-object-chip.selected {
60
+ border-color: var(--primary);
61
+ }
62
+
63
+ .inline-object-type {
64
+ display: inline-flex;
65
+ align-items: center;
66
+ justify-content: center;
67
+ width: 16px;
68
+ height: 16px;
69
+ border-radius: 2px;
70
+ background: var(--muted-foreground);
71
+ color: var(--background);
72
+ font-size: 8px;
73
+ font-weight: 600;
74
+ flex-shrink: 0;
75
+ }
76
+
77
+ .inline-object-label {
78
+ max-width: 120px;
79
+ overflow: hidden;
80
+ text-overflow: ellipsis;
81
+ white-space: nowrap;
82
+ color: var(--foreground);
83
+ }
84
+
85
+ .inline-object-delete {
86
+ display: inline-flex;
87
+ align-items: center;
88
+ justify-content: center;
89
+ width: 14px;
90
+ height: 14px;
91
+ border: none;
92
+ background: none;
93
+ color: var(--muted-foreground);
94
+ font-size: 12px;
95
+ cursor: pointer;
96
+ padding: 0;
97
+ border-radius: 2px;
98
+ opacity: 0;
99
+ transition:
100
+ opacity 0.15s,
101
+ color 0.15s;
102
+ }
103
+
104
+ .inline-object-chip:hover .inline-object-delete {
105
+ opacity: 1;
106
+ }
107
+
108
+ .inline-object-delete:hover {
109
+ color: var(--destructive);
110
+ }
111
+ </style>
@@ -0,0 +1,12 @@
1
+ interface Props {
2
+ type: string;
3
+ nodeKey: string;
4
+ data: Record<string, unknown>;
5
+ selected: boolean;
6
+ onEdit: () => void;
7
+ onDelete: () => void;
8
+ }
9
+ declare const PortableTextInlineView: import("svelte").Component<Props, {}, "">;
10
+ type PortableTextInlineView = ReturnType<typeof PortableTextInlineView>;
11
+ export default PortableTextInlineView;
12
+ //# sourceMappingURL=PortableTextInlineView.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PortableTextInlineView.svelte.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/admin/fields/richtext/PortableTextInlineView.svelte.ts"],"names":[],"mappings":"AAGC,UAAU,KAAK;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACrB;AA2BF,QAAA,MAAM,sBAAsB,2CAAwC,CAAC;AACrE,KAAK,sBAAsB,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACxE,eAAe,sBAAsB,CAAC"}
@@ -0,0 +1,67 @@
1
+ <script lang="ts">
2
+ import { Pencil, Trash2 } from '@lucide/svelte';
3
+
4
+ interface Props {
5
+ type: string;
6
+ nodeKey: string;
7
+ data: Record<string, unknown>;
8
+ selected?: boolean;
9
+ onEdit: () => void;
10
+ onDelete: () => void;
11
+ }
12
+
13
+ let { type, data, selected = false, onEdit, onDelete }: Props = $props();
14
+
15
+ const title = $derived((data.title as string) || (data.name as string) || type);
16
+
17
+ const subtitle = $derived.by(() => {
18
+ const preview: string[] = [];
19
+ for (const [key, val] of Object.entries(data)) {
20
+ if (key.startsWith('_') || key === 'title' || key === 'name') continue;
21
+ if (typeof val === 'string' && val.length > 0) {
22
+ preview.push(val.length > 40 ? val.slice(0, 40) + '...' : val);
23
+ if (preview.length >= 2) break;
24
+ }
25
+ }
26
+ return preview.join(' - ');
27
+ });
28
+ </script>
29
+
30
+ <div
31
+ class="border-rule bg-muted/20 hover:bg-muted/40 group my-2 flex items-center gap-2 rounded-md border px-3 py-2 transition-colors"
32
+ class:ring-primary={selected}
33
+ class:ring-2={selected}
34
+ >
35
+ <button type="button" class="flex min-w-0 flex-1 items-center gap-3 text-left" onclick={onEdit}>
36
+ <div
37
+ class="bg-muted text-muted-foreground flex h-8 w-8 shrink-0 items-center justify-center rounded text-xs font-medium uppercase"
38
+ >
39
+ {type.slice(0, 2)}
40
+ </div>
41
+ <div class="min-w-0 flex-1">
42
+ <div class="text-foreground text-sm font-medium capitalize">{title}</div>
43
+ {#if subtitle}
44
+ <div class="text-muted-foreground truncate text-xs">{subtitle}</div>
45
+ {/if}
46
+ </div>
47
+ </button>
48
+
49
+ <div class="flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100">
50
+ <button
51
+ type="button"
52
+ class="text-muted-foreground hover:text-foreground rounded p-1 transition-colors"
53
+ onclick={onEdit}
54
+ title="Edit"
55
+ >
56
+ <Pencil class="h-3.5 w-3.5" />
57
+ </button>
58
+ <button
59
+ type="button"
60
+ class="text-muted-foreground hover:text-destructive rounded p-1 transition-colors"
61
+ onclick={onDelete}
62
+ title="Remove"
63
+ >
64
+ <Trash2 class="h-3.5 w-3.5" />
65
+ </button>
66
+ </div>
67
+ </div>