@aphexcms/cms-core 5.0.5 → 6.0.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 (53) hide show
  1. package/dist/components/AdminApp.svelte +3 -9
  2. package/dist/components/AdminApp.svelte.d.ts.map +1 -1
  3. package/dist/components/admin/AssetBrowserModal.svelte +2 -1
  4. package/dist/components/admin/DocumentEditor.svelte +7 -3
  5. package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
  6. package/dist/components/admin/MediaBrowser.svelte +3 -2
  7. package/dist/components/admin/ObjectModal.svelte +1 -1
  8. package/dist/components/admin/fields/ArrayField.svelte +23 -14
  9. package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +1 -1
  10. package/dist/components/admin/fields/FileField.svelte +17 -6
  11. package/dist/components/admin/fields/FileField.svelte.d.ts.map +1 -1
  12. package/dist/components/admin/fields/ImageField.svelte +21 -6
  13. package/dist/components/admin/fields/ImageField.svelte.d.ts.map +1 -1
  14. package/dist/components/admin/fields/ReferenceField.svelte +26 -9
  15. package/dist/components/admin/fields/ReferenceField.svelte.d.ts.map +1 -1
  16. package/dist/hooks.d.ts +11 -0
  17. package/dist/hooks.d.ts.map +1 -1
  18. package/dist/hooks.js +39 -22
  19. package/dist/lib/hooks.d.ts +11 -0
  20. package/dist/lib/hooks.d.ts.map +1 -1
  21. package/dist/lib/hooks.js +39 -22
  22. package/dist/lib/hooks.js.map +1 -1
  23. package/dist/lib/schema-utils/validator.d.ts.map +1 -1
  24. package/dist/lib/schema-utils/validator.js +10 -7
  25. package/dist/lib/schema-utils/validator.js.map +1 -1
  26. package/dist/lib/server/index.d.ts +1 -1
  27. package/dist/lib/server/index.d.ts.map +1 -1
  28. package/dist/lib/server/index.js +1 -1
  29. package/dist/lib/server/index.js.map +1 -1
  30. package/dist/lib/types/schemas.d.ts +2 -0
  31. package/dist/lib/types/schemas.d.ts.map +1 -1
  32. package/dist/lib/utils/preview.d.ts +20 -0
  33. package/dist/lib/utils/preview.d.ts.map +1 -0
  34. package/dist/lib/utils/preview.js +68 -0
  35. package/dist/lib/utils/preview.js.map +1 -0
  36. package/dist/lib/vite/index.d.ts +86 -0
  37. package/dist/lib/vite/index.d.ts.map +1 -0
  38. package/dist/lib/vite/index.js +274 -0
  39. package/dist/lib/vite/index.js.map +1 -0
  40. package/dist/schema-utils/validator.d.ts.map +1 -1
  41. package/dist/schema-utils/validator.js +10 -7
  42. package/dist/server/index.d.ts +1 -1
  43. package/dist/server/index.d.ts.map +1 -1
  44. package/dist/server/index.js +1 -1
  45. package/dist/types/schemas.d.ts +2 -0
  46. package/dist/types/schemas.d.ts.map +1 -1
  47. package/dist/utils/preview.d.ts +20 -0
  48. package/dist/utils/preview.d.ts.map +1 -0
  49. package/dist/utils/preview.js +67 -0
  50. package/dist/vite/index.d.ts +86 -0
  51. package/dist/vite/index.d.ts.map +1 -0
  52. package/dist/vite/index.js +273 -0
  53. package/package.json +6 -2
@@ -13,6 +13,7 @@
13
13
  import { SvelteURLSearchParams } from 'svelte/reactivity';
14
14
  import type { SchemaType } from '../types/index';
15
15
  import type { UserSessionPreferences } from '../types/organization';
16
+ import { resolvePreviewTitle, resolvePreviewSubtitle } from '../utils/preview';
16
17
  import DocumentEditor from './admin/DocumentEditor.svelte';
17
18
  import DocumentVersionPanel from './admin/DocumentVersionPanel.svelte';
18
19
  import DocumentsSkeleton from './admin/DocumentsSkeleton.svelte';
@@ -912,20 +913,13 @@
912
913
  }
913
914
  // Find schema for preview config
914
915
  const schema = schemas.find((s) => s.name === docType);
915
- const previewConfig = schema?.preview;
916
916
 
917
917
  documentsList = result.data.map((doc: any) => {
918
918
  // With LocalAPI, data is already flattened at top level (not in draftData)
919
919
  // The document itself IS the data, with _meta containing metadata
920
920
 
921
- // Use preview config if available
922
- const title = previewConfig?.select?.title
923
- ? doc[previewConfig.select.title] || `Untitled`
924
- : doc.title || `Untitled`;
925
-
926
- const subtitle = previewConfig?.select?.subtitle
927
- ? doc[previewConfig.select.subtitle]
928
- : undefined;
921
+ const title = resolvePreviewTitle(doc, schema);
922
+ const subtitle = resolvePreviewSubtitle(doc, schema) ?? undefined;
929
923
 
930
924
  // Metadata is in _meta field (from LocalAPI transformation)
931
925
  const meta = doc._meta || {};
@@ -1 +1 @@
1
- {"version":3,"file":"AdminApp.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/AdminApp.svelte.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AA2BnE,UAAU,KAAK;IACd,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5E,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACvE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE;QAAE,KAAK,EAAE,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAA;KAAE,CAAC;IACxD,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,eAAe,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;CAChD;AAggDF,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"AdminApp.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/AdminApp.svelte.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AA4BnE,UAAU,KAAK;IACd,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5E,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACvE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE;QAAE,KAAK,EAAE,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAA;KAAE,CAAC;IACxD,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,eAAe,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;CAChD;AA0/CF,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -39,7 +39,8 @@
39
39
  <Dialog.Root bind:open {onOpenChange}>
40
40
  <Dialog.Content
41
41
  showCloseButton={false}
42
- class="flex h-[95vh] max-w-[95vw] flex-col overflow-hidden p-0 sm:max-w-[95vw]"
42
+ class="!z-[9999] flex h-[95vh] max-w-[95vw] flex-col overflow-hidden p-0 sm:max-w-[95vw]"
43
+ overlayClass="!z-[9998]"
43
44
  >
44
45
  <Dialog.Header class="sr-only">
45
46
  <Dialog.Title>{multiSelect ? 'Select Assets' : 'Select Asset'}</Dialog.Title>
@@ -293,12 +293,16 @@
293
293
  (hasUnpublishedContent || isUnpublished) && !saving && documentId && !hasValidationErrors
294
294
  );
295
295
 
296
- // Get preview title based on schema config
296
+ // Get preview title based on schema config. When viewing in published
297
+ // perspective, read from the loaded published snapshot — otherwise the
298
+ // header shows the draft's title even though the body shows published
299
+ // fields, which is confusing.
297
300
  function getPreviewTitle(): string {
301
+ const source = perspective === 'published' && publishedData ? publishedData : documentData;
298
302
  if (!schema?.preview?.select?.title) {
299
- return documentData.title || `Untitled`;
303
+ return source.title || `Untitled`;
300
304
  }
301
- return documentData[schema.preview.select.title] || `Untitled`;
305
+ return source[schema.preview.select.title] || `Untitled`;
302
306
  }
303
307
 
304
308
  // CRITICAL: Clear state IMMEDIATELY when switching documents to prevent cross-contamination
@@ -1 +1 @@
1
- {"version":3,"file":"DocumentEditor.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/admin/DocumentEditor.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAaxD,UAAU,KAAK;IACd,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,oBAAoB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,sBAAsB,CAAC,EAAE;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC1B,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,IAAI,CAAC;IACT,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AA+9CF,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"DocumentEditor.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/admin/DocumentEditor.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAaxD,UAAU,KAAK;IACd,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,oBAAoB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,sBAAsB,CAAC,EAAE;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC1B,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,IAAI,CAAC;IACT,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAm+CF,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
@@ -1349,7 +1349,8 @@
1349
1349
  <Dialog.Root bind:open={lightboxOpen}>
1350
1350
  <Dialog.Content
1351
1351
  showCloseButton={false}
1352
- class="flex max-h-[90vh] max-w-[90vw] flex-col overflow-hidden p-0 sm:max-w-[90vw]"
1352
+ class="!z-[10000] flex max-h-[90vh] max-w-[90vw] flex-col overflow-hidden p-0 sm:max-w-[90vw]"
1353
+ overlayClass="!z-[9999]"
1353
1354
  >
1354
1355
  <Dialog.Header class="border-border border-b px-4 py-3">
1355
1356
  <Dialog.Title class="truncate text-sm font-medium"
@@ -1389,7 +1390,7 @@
1389
1390
  }
1390
1391
  }}
1391
1392
  >
1392
- <Dialog.Content class="max-w-lg">
1393
+ <Dialog.Content class="!z-[10000] max-w-lg" overlayClass="!z-[9999]">
1393
1394
  <Dialog.Header>
1394
1395
  <Dialog.Title>Upload Assets</Dialog.Title>
1395
1396
  </Dialog.Header>
@@ -84,7 +84,7 @@
84
84
  backdrops cover the full screen, but for now keeping it inside the
85
85
  editor is the design we want. -->
86
86
  <div
87
- class="bg-background/80 absolute inset-0 backdrop-blur-xs"
87
+ class="bg-background/80 absolute inset-0"
88
88
  style="z-index: {backdropZ}"
89
89
  onclick={handleBackdropClick}
90
90
  onkeydown={handleKeydown}
@@ -28,6 +28,7 @@
28
28
  import type { ImageValue } from '../../../types/asset';
29
29
  import { toast } from 'svelte-sonner';
30
30
  import AssetBrowserModal from '../AssetBrowserModal.svelte';
31
+ import { resolvePreviewTitle, resolvePreviewSubtitle } from '../../../utils/preview';
31
32
 
32
33
  interface Props {
33
34
  field: ArrayFieldType;
@@ -59,7 +60,9 @@
59
60
  type: 'object',
60
61
  name: inlineDef.name || typeName,
61
62
  title: inlineDef.title || typeName,
62
- fields: inlineDef.fields
63
+ fields: inlineDef.fields,
64
+ icon: inlineDef.icon,
65
+ preview: inlineDef.preview
63
66
  };
64
67
  }
65
68
 
@@ -401,21 +404,19 @@
401
404
 
402
405
  function getItemTitle(item: any): string {
403
406
  if (!item._type) return 'Unknown Item';
404
-
405
407
  const schema = getSchemaForType(item._type);
406
- if (!schema) return item._type;
407
-
408
- const titleField = item.title || item.heading || item.name || item.label;
409
- if (titleField && typeof titleField === 'string' && titleField.trim()) {
410
- return titleField;
411
- }
408
+ return resolvePreviewTitle(item, schema, item._type);
409
+ }
412
410
 
413
- return schema.title || item._type;
411
+ function getItemSubtitle(item: any): string | null {
412
+ if (!item._type) return null;
413
+ const schema = getSchemaForType(item._type);
414
+ return resolvePreviewSubtitle(item, schema);
414
415
  }
415
416
 
416
417
  function getItemIcon(item: any): any {
417
418
  if (!item._type) return null;
418
- const schema = getSchemaByName(schemas, item._type);
419
+ const schema = getSchemaForType(item._type);
419
420
  return schema?.icon || null;
420
421
  }
421
422
  </script>
@@ -771,7 +772,8 @@
771
772
 
772
773
  <!-- Title (clickable to edit) — wraps the type icon so the whole
773
774
  row body is a generous click target instead of just the
774
- truncated title text. -->
775
+ truncated title text. When the schema's preview config
776
+ supplies a `subtitle` field, render it on a second line. -->
775
777
  <button
776
778
  class="flex flex-1 cursor-pointer items-center gap-2 self-stretch truncate px-1 py-2 text-left text-sm"
777
779
  onclick={() => handleEditItem(index)}
@@ -784,7 +786,14 @@
784
786
  <FileText class="h-4 w-4" />
785
787
  {/if}
786
788
  </span>
787
- <span class="truncate">{getItemTitle(item)}</span>
789
+ <span class="flex min-w-0 flex-1 flex-col">
790
+ <span class="truncate">{getItemTitle(item)}</span>
791
+ {#if getItemSubtitle(item)}
792
+ <span class="text-muted-foreground truncate text-xs"
793
+ >{getItemSubtitle(item)}</span
794
+ >
795
+ {/if}
796
+ </span>
788
797
  </button>
789
798
 
790
799
  <!-- Context menu -->
@@ -875,7 +884,7 @@
875
884
  <!-- Image upload modal -->
876
885
  {#if imageModalOpen}
877
886
  <div
878
- class="bg-background/80 fixed top-12 right-0 bottom-0 left-0 z-40 flex items-center justify-center p-6 backdrop-blur-xs sm:absolute sm:top-0 sm:p-4"
887
+ class="bg-background/80 fixed top-12 right-0 bottom-0 left-0 z-40 flex items-center justify-center p-6 sm:absolute sm:top-0 sm:p-4"
879
888
  onclick={(e) => {
880
889
  if (e.target === e.currentTarget) handleImageModalClose();
881
890
  }}
@@ -931,7 +940,7 @@
931
940
  <!-- Text editing modal -->
932
941
  {#if textModalOpen}
933
942
  <div
934
- class="bg-background/80 fixed top-12 right-0 bottom-0 left-0 z-[100] flex items-center justify-center p-6 backdrop-blur-xs sm:absolute sm:top-0 sm:p-4"
943
+ class="bg-background/80 fixed top-12 right-0 bottom-0 left-0 z-[100] flex items-center justify-center p-6 sm:absolute sm:top-0 sm:p-4"
935
944
  onclick={(e) => {
936
945
  if (e.target === e.currentTarget) handleTextModalClose();
937
946
  }}
@@ -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;AAyBtF,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;AAwxBF,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;AA0BtF,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;AA8xBF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -52,6 +52,8 @@
52
52
  const isReadOnly = $derived(readonly || !perms.can('asset.upload'));
53
53
 
54
54
  let isDragging = $state(false);
55
+ // Drag-counter to defeat the dragleave/dragenter flicker — see ImageField for rationale.
56
+ let dragDepth = 0;
55
57
  let isUploading = $state(false);
56
58
  let uploadError = $state<string | null>(null);
57
59
  let fileInputRef: HTMLInputElement;
@@ -121,21 +123,29 @@
121
123
  }
122
124
  }
123
125
 
124
- function handleDragOver(event: DragEvent) {
126
+ function handleDragEnter(event: DragEvent) {
125
127
  if (isReadOnly) return;
126
128
  event.preventDefault();
129
+ dragDepth++;
127
130
  isDragging = true;
128
131
  }
129
132
 
133
+ function handleDragOver(event: DragEvent) {
134
+ if (isReadOnly) return;
135
+ event.preventDefault();
136
+ }
137
+
130
138
  function handleDragLeave(event: DragEvent) {
131
139
  if (isReadOnly) return;
132
140
  event.preventDefault();
133
- isDragging = false;
141
+ dragDepth = Math.max(0, dragDepth - 1);
142
+ if (dragDepth === 0) isDragging = false;
134
143
  }
135
144
 
136
145
  function handleDrop(event: DragEvent) {
137
146
  if (isReadOnly) return;
138
147
  event.preventDefault();
148
+ dragDepth = 0;
139
149
  isDragging = false;
140
150
  handleFileSelect(event.dataTransfer?.files || null);
141
151
  }
@@ -285,7 +295,7 @@
285
295
  <Ellipsis size={16} />
286
296
  </Button>
287
297
  </DropdownMenuTrigger>
288
- <DropdownMenuContent align="end">
298
+ <DropdownMenuContent align="end" class="!z-[9999]">
289
299
  <DropdownMenuGroup>
290
300
  <DropdownMenuItem onclick={openFileDialog} disabled={isUploading}>
291
301
  <Upload size={16} />
@@ -365,7 +375,7 @@
365
375
  <Ellipsis size={16} />
366
376
  </Button>
367
377
  </DropdownMenuTrigger>
368
- <DropdownMenuContent align="end">
378
+ <DropdownMenuContent align="end" class="!z-[9999]">
369
379
  <DropdownMenuGroup>
370
380
  <DropdownMenuItem onclick={openFileDialog} disabled={isUploading}>
371
381
  <Upload size={16} />
@@ -409,13 +419,14 @@
409
419
  {:else}
410
420
  <!-- Upload bar -->
411
421
  <div
412
- class="border-border flex h-10 items-center overflow-hidden rounded-md border transition-colors {validationClasses} {isReadOnly
422
+ class="border-border flex h-12 items-center overflow-hidden rounded-md border transition-colors {validationClasses} {isReadOnly
413
423
  ? ''
414
424
  : isDragging
415
- ? 'bg-primary/5'
425
+ ? 'border-primary bg-primary/5'
416
426
  : ''}"
417
427
  use:elementEvents={{
418
428
  events: [
429
+ { name: 'dragenter', handler: handleDragEnter },
419
430
  { name: 'dragover', handler: handleDragOver },
420
431
  { name: 'drop', handler: handleDrop },
421
432
  { name: 'dragleave', handler: handleDragLeave }
@@ -1 +1 @@
1
- {"version":3,"file":"FileField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/FileField.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAkBxE,UAAU,KAAK;IACd,KAAK,EAAE,aAAa,CAAC;IACrB,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAuaF,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"FileField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/FileField.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAkBxE,UAAU,KAAK;IACd,KAAK,EAAE,aAAa,CAAC;IACrB,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAkbF,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -53,6 +53,12 @@
53
53
 
54
54
  // Component state
55
55
  let isDragging = $state(false);
56
+ // Drag-counter to defeat the dragleave/dragenter flicker: every child
57
+ // element the cursor crosses fires `dragleave` on the parent immediately
58
+ // followed by `dragenter`, so a naive `dragover → true / dragleave → false`
59
+ // flickers `isDragging` rapidly. Counting enters/leaves and only resetting
60
+ // at zero gives stable highlight state.
61
+ let dragDepth = 0;
56
62
  let isUploading = $state(false);
57
63
  let uploadError = $state<string | null>(null);
58
64
  let fileInputRef: HTMLInputElement;
@@ -112,21 +118,29 @@
112
118
  }
113
119
 
114
120
  // Drag and drop handlers
115
- function handleDragOver(event: DragEvent) {
121
+ function handleDragEnter(event: DragEvent) {
116
122
  if (isReadOnly) return;
117
123
  event.preventDefault();
124
+ dragDepth++;
118
125
  isDragging = true;
119
126
  }
120
127
 
128
+ function handleDragOver(event: DragEvent) {
129
+ if (isReadOnly) return;
130
+ event.preventDefault(); // required to allow `drop` to fire
131
+ }
132
+
121
133
  function handleDragLeave(event: DragEvent) {
122
134
  if (isReadOnly) return;
123
135
  event.preventDefault();
124
- isDragging = false;
136
+ dragDepth = Math.max(0, dragDepth - 1);
137
+ if (dragDepth === 0) isDragging = false;
125
138
  }
126
139
 
127
140
  function handleDrop(event: DragEvent) {
128
141
  if (isReadOnly) return;
129
142
  event.preventDefault();
143
+ dragDepth = 0;
130
144
  isDragging = false;
131
145
  handleFileSelect(event.dataTransfer?.files || null);
132
146
  }
@@ -312,7 +326,7 @@
312
326
  <Ellipsis size={16} />
313
327
  </Button>
314
328
  </DropdownMenuTrigger>
315
- <DropdownMenuContent align="end">
329
+ <DropdownMenuContent align="end" class="!z-[9999]">
316
330
  <DropdownMenuGroup>
317
331
  <DropdownMenuItem onclick={openFileDialog} disabled={isUploading}>
318
332
  <Upload size={16} />
@@ -397,7 +411,7 @@
397
411
  <Ellipsis size={16} />
398
412
  </Button>
399
413
  </DropdownMenuTrigger>
400
- <DropdownMenuContent align="end">
414
+ <DropdownMenuContent align="end" class="!z-[9999]">
401
415
  <DropdownMenuGroup>
402
416
  <DropdownMenuItem onclick={openFileDialog} disabled={isUploading}>
403
417
  <Upload size={16} />
@@ -456,13 +470,14 @@
456
470
  {:else}
457
471
  <!-- Sanity-style upload bar -->
458
472
  <div
459
- class="border-border flex h-10 items-center overflow-hidden rounded-md border transition-colors {validationClasses} {isReadOnly
473
+ class="border-border flex h-12 items-center overflow-hidden rounded-md border transition-colors {validationClasses} {isReadOnly
460
474
  ? ''
461
475
  : isDragging
462
- ? 'bg-primary/5'
476
+ ? 'border-primary bg-primary/5'
463
477
  : ''}"
464
478
  use:elementEvents={{
465
479
  events: [
480
+ { name: 'dragenter', handler: handleDragEnter },
466
481
  { name: 'dragover', handler: handleDragOver },
467
482
  { name: 'drop', handler: handleDrop },
468
483
  { name: 'dragleave', handler: handleDragLeave }
@@ -1 +1 @@
1
- {"version":3,"file":"ImageField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ImageField.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAmB1E,UAAU,KAAK;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AA6bF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"ImageField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ImageField.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAmB1E,UAAU,KAAK;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AA4cF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -15,6 +15,7 @@
15
15
  import { toast } from 'svelte-sonner';
16
16
  import { cmsLogger } from '../../../utils/logger';
17
17
  import { pluralize } from '../../../utils/pluralize';
18
+ import { resolvePreviewTitle, resolvePreviewSubtitle } from '../../../utils/preview';
18
19
 
19
20
  interface Props {
20
21
  field: Field;
@@ -141,17 +142,19 @@
141
142
  }
142
143
 
143
144
  function getDocumentTitle(doc: any): string {
144
- // With LocalAPI, data is flattened at top level
145
- return doc.title || doc.name || doc.heading || 'Untitled';
145
+ return resolvePreviewTitle(doc, targetSchema);
146
146
  }
147
147
 
148
- const selectedLabel = $derived(selectedDocument ? getDocumentTitle(selectedDocument) : null);
148
+ function getDocumentSubtitle(doc: any): string | null {
149
+ return resolvePreviewSubtitle(doc, targetSchema);
150
+ }
149
151
  </script>
150
152
 
151
153
  {#if selectedDocument}
152
- <!-- Selected document — row styled like an ArrayField item -->
154
+ <!-- Selected document — row styled like an ArrayField item. Grows to two
155
+ lines when the target schema's preview config exposes a subtitle. -->
153
156
  <div
154
- class="border-border/50 bg-background hover:bg-muted/50 flex h-10 items-center gap-1 rounded border px-1 transition-colors"
157
+ class="border-border/50 bg-background hover:bg-muted/50 flex min-h-10 items-center gap-1 rounded border px-1 transition-colors"
155
158
  >
156
159
  <!-- Type icon -->
157
160
  <div class="text-muted-foreground flex h-8 w-8 shrink-0 items-center justify-center">
@@ -162,13 +165,20 @@
162
165
  {/if}
163
166
  </div>
164
167
 
165
- <!-- Title + status dot (clickable to open referenced document) -->
168
+ <!-- Title (+ subtitle) + status dot (clickable to open referenced document) -->
166
169
  <button
167
- class="flex min-w-0 flex-1 cursor-pointer items-center gap-2 text-left"
170
+ class="flex min-w-0 flex-1 cursor-pointer items-center gap-2 py-1 text-left"
168
171
  onclick={openReference}
169
172
  title="Open referenced document"
170
173
  >
171
- <span class="truncate text-sm">{getDocumentTitle(selectedDocument)}</span>
174
+ <span class="flex min-w-0 flex-1 flex-col">
175
+ <span class="truncate text-sm">{getDocumentTitle(selectedDocument)}</span>
176
+ {#if getDocumentSubtitle(selectedDocument)}
177
+ <span class="text-muted-foreground truncate text-xs"
178
+ >{getDocumentSubtitle(selectedDocument)}</span
179
+ >
180
+ {/if}
181
+ </span>
172
182
  {#if selectedDocument.status === 'published'}
173
183
  <span class="h-1.5 w-1.5 shrink-0 rounded-full bg-green-500" title="Published"></span>
174
184
  {:else if selectedDocument.status === 'unpublished'}
@@ -261,7 +271,14 @@
261
271
  <CheckIcon
262
272
  class={cn('h-4 w-4 shrink-0', value !== doc.id && 'text-transparent')}
263
273
  />
264
- <span class="truncate text-sm">{getDocumentTitle(doc)}</span>
274
+ <span class="flex min-w-0 flex-1 flex-col">
275
+ <span class="truncate text-sm">{getDocumentTitle(doc)}</span>
276
+ {#if getDocumentSubtitle(doc)}
277
+ <span class="text-muted-foreground truncate text-xs"
278
+ >{getDocumentSubtitle(doc)}</span
279
+ >
280
+ {/if}
281
+ </span>
265
282
  </div>
266
283
  {#if doc.status === 'published'}
267
284
  <span class="h-1.5 w-1.5 shrink-0 rounded-full bg-green-500" title="Published"
@@ -1 +1 @@
1
- {"version":3,"file":"ReferenceField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ReferenceField.svelte.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,KAAK,EAAwC,MAAM,wBAAwB,CAAC;AASzF,UAAU,KAAK;IACd,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzC,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAqQF,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":"AAYA,OAAO,KAAK,EAAE,KAAK,EAAwC,MAAM,wBAAwB,CAAC;AAUzF,UAAU,KAAK;IACd,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzC,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAiRF,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
package/dist/hooks.d.ts CHANGED
@@ -24,5 +24,16 @@ export interface CMSInstances {
24
24
  graphqlSettings?: GraphQLSettings | null;
25
25
  apiApp: Hono<AphexEnv>;
26
26
  }
27
+ /**
28
+ * Called by the Vite HMR plugin (`@aphexcms/cms-core/vite`) when schema
29
+ * files or `aphex.config.ts` change. Replaces the captured config with the
30
+ * freshly re-evaluated module and marks instances for re-initialization on
31
+ * the next request — no Vite dev-server restart required.
32
+ *
33
+ * Module-level state means the Vite plugin and the running SvelteKit hook
34
+ * share the same cms-core instance through Vite's module graph, so this
35
+ * setter mutates the same `activeConfig` the hook reads on each request.
36
+ */
37
+ export declare function __notifyAphexConfigChanged(newConfig: CMSConfig): void;
27
38
  export declare function createCMSHook(config: CMSConfig): Handle;
28
39
  //# sourceMappingURL=hooks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/lib/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAKvD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAa,SAAS,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAIN,KAAK,QAAQ,EACb,MAAM,oBAAoB,CAAC;AAG5B,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,EAAE,YAAY,CAAC;IAC3B,cAAc,EAAE,cAAc,CAAC;IAC/B,eAAe,EAAE,eAAe,CAAC;IACjC,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;IAC3B,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACzC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;CACvB;AA0BD,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAgJvD"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/lib/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAKvD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAa,SAAS,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAIN,KAAK,QAAQ,EACb,MAAM,oBAAoB,CAAC;AAG5B,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,EAAE,YAAY,CAAC;IAC3B,cAAc,EAAE,cAAc,CAAC;IAC/B,eAAe,EAAE,eAAe,CAAC;IACjC,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;IAC3B,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACzC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;CACvB;AAQD;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAGrE;AAgBD,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAwJvD"}
package/dist/hooks.js CHANGED
@@ -9,16 +9,27 @@ import { createAphexApi, mountAphexBuiltins, toHonoHandler } from './server/api/
9
9
  let cmsInstances = null;
10
10
  let schemaError = null;
11
11
  let initPromise = null;
12
+ let activeConfig = null;
13
+ let configDirty = false;
12
14
  /**
13
- * Check if schemas are dirty (changed via Vite HMR)
14
- * Vite plugin sets a global flag when schema files change
15
+ * Called by the Vite HMR plugin (`@aphexcms/cms-core/vite`) when schema
16
+ * files or `aphex.config.ts` change. Replaces the captured config with the
17
+ * freshly re-evaluated module and marks instances for re-initialization on
18
+ * the next request — no Vite dev-server restart required.
19
+ *
20
+ * Module-level state means the Vite plugin and the running SvelteKit hook
21
+ * share the same cms-core instance through Vite's module graph, so this
22
+ * setter mutates the same `activeConfig` the hook reads on each request.
15
23
  */
24
+ export function __notifyAphexConfigChanged(newConfig) {
25
+ activeConfig = newConfig;
26
+ configDirty = true;
27
+ }
16
28
  function checkSchemasDirty() {
17
- const dirty = global.__aphexSchemasDirty === true;
18
- if (dirty) {
19
- global.__aphexSchemasDirty = false; // Reset the flag
20
- }
21
- return dirty;
29
+ if (!configDirty)
30
+ return false;
31
+ configDirty = false;
32
+ return true;
22
33
  }
23
34
  // Factory function to create the default local storage adapter
24
35
  function createDefaultStorageAdapter() {
@@ -35,7 +46,13 @@ export function createCMSHook(config) {
35
46
  // Apply log level from config (if provided)
36
47
  if (config.logLevel)
37
48
  setLogLevel(config.logLevel);
49
+ // Stash the config in module scope so the HMR plugin can replace it via
50
+ // __notifyAphexConfigChanged() without going through this factory again.
51
+ activeConfig = config;
38
52
  return async ({ event, resolve }) => {
53
+ // Use the latest config that the HMR plugin handed us; falls back to
54
+ // the one captured at hook construction time.
55
+ const currentConfig = activeConfig ?? config;
39
56
  // Note: In dev mode, /storage/ might be accessible via Vite dev server
40
57
  // In production, only /static/ folder is served - /storage/ is private
41
58
  // Initialize CMS instances once at application startup
@@ -59,20 +76,20 @@ export function createCMSHook(config) {
59
76
  let resolveInit;
60
77
  initPromise = new Promise((r) => (resolveInit = r));
61
78
  cmsLogger.info('[CMS]', 'Initializing...');
62
- const databaseAdapter = config.database;
79
+ const databaseAdapter = currentConfig.database;
63
80
  // Use the storage adapter from config, or create the default local one.
64
- const storageAdapter = config.storage ?? createDefaultStorageAdapter();
65
- const emailAdapter = config.email ?? null;
81
+ const storageAdapter = currentConfig.storage ?? createDefaultStorageAdapter();
82
+ const emailAdapter = currentConfig.email ?? null;
66
83
  const assetService = new AssetServiceClass(storageAdapter, databaseAdapter);
67
- const cmsEngine = createCMS(config, databaseAdapter);
68
- const rolesService = new RolesService(databaseAdapter, config.cache ?? null);
84
+ const cmsEngine = createCMS(currentConfig, databaseAdapter);
85
+ const rolesService = new RolesService(databaseAdapter, currentConfig.cache ?? null);
69
86
  // Initialize Local API (unified operations layer)
70
- const localAPI = createLocalAPI(config, databaseAdapter);
87
+ const localAPI = createLocalAPI(currentConfig, databaseAdapter);
71
88
  // Build the Hono API app shell. User middleware/overrides register
72
89
  // FIRST (so they sit ahead of built-ins in the chain), then we mount
73
90
  // the built-in routes, then GraphQL.
74
91
  const apiApp = createAphexApi();
75
- config.api?.(apiApp);
92
+ currentConfig.api?.(apiApp);
76
93
  mountAphexBuiltins(apiApp);
77
94
  // Initialize schemas with validation
78
95
  try {
@@ -84,12 +101,12 @@ export function createCMSHook(config) {
84
101
  }
85
102
  // Initialize built-in GraphQL (enabled by default, opt-out with graphql: false)
86
103
  let graphqlSettings = null;
87
- if (config.graphql !== false) {
104
+ if (currentConfig.graphql !== false) {
88
105
  try {
89
106
  const { createGraphQLHandler } = await import('./graphql/index.js');
90
- const graphqlConfig = typeof config.graphql === 'object' ? config.graphql : {};
107
+ const graphqlConfig = typeof currentConfig.graphql === 'object' ? currentConfig.graphql : {};
91
108
  const result = await createGraphQLHandler({
92
- config,
109
+ config: currentConfig,
93
110
  databaseAdapter,
94
111
  assetService,
95
112
  storageAdapter,
@@ -97,9 +114,9 @@ export function createCMSHook(config) {
97
114
  cmsEngine,
98
115
  localAPI,
99
116
  rolesService,
100
- auth: config.auth?.provider,
117
+ auth: currentConfig.auth?.provider,
101
118
  apiApp
102
- }, config.schemaTypes, graphqlConfig);
119
+ }, currentConfig.schemaTypes, graphqlConfig);
103
120
  // Register GraphQL directly on the Hono app. The Yoga handler
104
121
  // internally distinguishes GET (GraphiQL UI) from POST (queries),
105
122
  // so we mount with `app.all()` and let it route by method.
@@ -120,7 +137,7 @@ export function createCMSHook(config) {
120
137
  }
121
138
  }
122
139
  cmsInstances = {
123
- config,
140
+ config: currentConfig,
124
141
  databaseAdapter: databaseAdapter,
125
142
  assetService: assetService,
126
143
  storageAdapter: storageAdapter,
@@ -128,7 +145,7 @@ export function createCMSHook(config) {
128
145
  cmsEngine: cmsEngine,
129
146
  localAPI: localAPI,
130
147
  rolesService,
131
- auth: config.auth?.provider,
148
+ auth: currentConfig.auth?.provider,
132
149
  graphqlSettings,
133
150
  apiApp
134
151
  };
@@ -142,7 +159,7 @@ export function createCMSHook(config) {
142
159
  event.locals.aphexCMS = cmsInstances;
143
160
  // Auth protection if configured
144
161
  if (cmsInstances.auth) {
145
- const authResponse = await handleAuthHook(event, config, cmsInstances.auth, cmsInstances.databaseAdapter, cmsInstances.rolesService);
162
+ const authResponse = await handleAuthHook(event, currentConfig, cmsInstances.auth, cmsInstances.databaseAdapter, cmsInstances.rolesService);
146
163
  if (authResponse)
147
164
  return authResponse;
148
165
  }
@@ -24,5 +24,16 @@ export interface CMSInstances {
24
24
  graphqlSettings?: GraphQLSettings | null;
25
25
  apiApp: Hono<AphexEnv>;
26
26
  }
27
+ /**
28
+ * Called by the Vite HMR plugin (`@aphexcms/cms-core/vite`) when schema
29
+ * files or `aphex.config.ts` change. Replaces the captured config with the
30
+ * freshly re-evaluated module and marks instances for re-initialization on
31
+ * the next request — no Vite dev-server restart required.
32
+ *
33
+ * Module-level state means the Vite plugin and the running SvelteKit hook
34
+ * share the same cms-core instance through Vite's module graph, so this
35
+ * setter mutates the same `activeConfig` the hook reads on each request.
36
+ */
37
+ export declare function __notifyAphexConfigChanged(newConfig: CMSConfig): void;
27
38
  export declare function createCMSHook(config: CMSConfig): Handle;
28
39
  //# sourceMappingURL=hooks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/lib/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAKvD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAa,SAAS,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAIN,KAAK,QAAQ,EACb,MAAM,oBAAoB,CAAC;AAG5B,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,EAAE,YAAY,CAAC;IAC3B,cAAc,EAAE,cAAc,CAAC;IAC/B,eAAe,EAAE,eAAe,CAAC;IACjC,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;IAC3B,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACzC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;CACvB;AA0BD,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAgJvD"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/lib/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAKvD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAa,SAAS,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAIN,KAAK,QAAQ,EACb,MAAM,oBAAoB,CAAC;AAG5B,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,EAAE,YAAY,CAAC;IAC3B,cAAc,EAAE,cAAc,CAAC;IAC/B,eAAe,EAAE,eAAe,CAAC;IACjC,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;IAC3B,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACzC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;CACvB;AAQD;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAGrE;AAgBD,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAwJvD"}