@aphexcms/cms-core 5.0.0 → 5.0.3
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.
- package/dist/cli/generate-types.d.ts.map +1 -1
- package/dist/cli/generate-types.js +24 -12
- package/dist/cli/generate-types.js.map +1 -1
- package/dist/components/AdminApp.svelte +124 -12
- package/dist/components/AdminApp.svelte.d.ts.map +1 -1
- package/dist/components/admin/DocumentEditor.svelte +88 -14
- package/dist/components/admin/DocumentEditor.svelte.d.ts +4 -0
- package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
- package/dist/components/admin/DocumentVersionPanel.svelte +6 -0
- package/dist/components/admin/DocumentVersionPanel.svelte.d.ts +3 -1
- package/dist/components/admin/DocumentVersionPanel.svelte.d.ts.map +1 -1
- package/dist/components/admin/ObjectModal.svelte +55 -12
- package/dist/components/admin/ObjectModal.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/ArrayField.svelte +15 -15
- package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +1 -1
- package/dist/db/interfaces/document.d.ts +2 -0
- package/dist/db/interfaces/document.d.ts.map +1 -1
- package/dist/graphql/resolvers.d.ts.map +1 -1
- package/dist/graphql/resolvers.js +116 -0
- package/dist/graphql/schema.d.ts.map +1 -1
- package/dist/graphql/schema.js +19 -0
- package/dist/lib/db/interfaces/document.d.ts +2 -0
- package/dist/lib/db/interfaces/document.d.ts.map +1 -1
- package/dist/lib/graphql/resolvers.d.ts.map +1 -1
- package/dist/lib/graphql/resolvers.js +116 -0
- package/dist/lib/graphql/resolvers.js.map +1 -1
- package/dist/lib/graphql/schema.d.ts.map +1 -1
- package/dist/lib/graphql/schema.js +19 -0
- package/dist/lib/graphql/schema.js.map +1 -1
- package/dist/lib/local-api/collection-api.d.ts +30 -0
- package/dist/lib/local-api/collection-api.d.ts.map +1 -1
- package/dist/lib/local-api/collection-api.js +83 -1
- package/dist/lib/local-api/collection-api.js.map +1 -1
- package/dist/lib/local-api/index.d.ts +1 -1
- package/dist/lib/local-api/index.d.ts.map +1 -1
- package/dist/lib/local-api/index.js +18 -1
- package/dist/lib/local-api/index.js.map +1 -1
- package/dist/lib/schema-utils/index.d.ts +1 -0
- package/dist/lib/schema-utils/index.d.ts.map +1 -1
- package/dist/lib/schema-utils/index.js +1 -0
- package/dist/lib/schema-utils/index.js.map +1 -1
- package/dist/lib/schema-utils/singleton.d.ts +12 -0
- package/dist/lib/schema-utils/singleton.d.ts.map +1 -0
- package/dist/lib/schema-utils/singleton.js +40 -0
- package/dist/lib/schema-utils/singleton.js.map +1 -0
- package/dist/lib/server/api/routes/documents-by-id.d.ts.map +1 -1
- package/dist/lib/server/api/routes/documents-by-id.js +4 -0
- package/dist/lib/server/api/routes/documents-by-id.js.map +1 -1
- package/dist/lib/server/index.d.ts +1 -1
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +1 -1
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/types/schemas.d.ts +8 -0
- package/dist/lib/types/schemas.d.ts.map +1 -1
- package/dist/local-api/collection-api.d.ts +30 -0
- package/dist/local-api/collection-api.d.ts.map +1 -1
- package/dist/local-api/collection-api.js +83 -1
- package/dist/local-api/index.d.ts +1 -1
- package/dist/local-api/index.d.ts.map +1 -1
- package/dist/local-api/index.js +18 -1
- package/dist/schema-utils/index.d.ts +1 -0
- package/dist/schema-utils/index.d.ts.map +1 -1
- package/dist/schema-utils/index.js +1 -0
- package/dist/schema-utils/singleton.d.ts +12 -0
- package/dist/schema-utils/singleton.d.ts.map +1 -0
- package/dist/schema-utils/singleton.js +39 -0
- package/dist/server/api/routes/documents-by-id.d.ts.map +1 -1
- package/dist/server/api/routes/documents-by-id.js +4 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/types/schemas.d.ts +8 -0
- package/dist/types/schemas.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-types.d.ts","sourceRoot":"","sources":["../../src/cli/generate-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,UAAU,EAAS,MAAM,sBAAsB,CAAC;AA+
|
|
1
|
+
{"version":3,"file":"generate-types.d.ts","sourceRoot":"","sources":["../../src/cli/generate-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,UAAU,EAAS,MAAM,sBAAsB,CAAC;AA+L9D;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CA4C3D;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC5C,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CA2Ff"}
|
|
@@ -2,7 +2,7 @@ import { isFieldRequired } from '../lib/field-validation/utils.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Map Aphex field types to TypeScript types
|
|
4
4
|
*/
|
|
5
|
-
function mapFieldTypeToTS(field, schemaMap) {
|
|
5
|
+
function mapFieldTypeToTS(field, schemaMap, inArray = false) {
|
|
6
6
|
switch (field.type) {
|
|
7
7
|
case 'string':
|
|
8
8
|
case 'text':
|
|
@@ -26,18 +26,20 @@ function mapFieldTypeToTS(field, schemaMap) {
|
|
|
26
26
|
if (!('of' in field) || !field.of || field.of.length === 0) {
|
|
27
27
|
return 'unknown[]';
|
|
28
28
|
}
|
|
29
|
-
// Map each array item type
|
|
29
|
+
// Map each array item type. Items get `_key?` injected at runtime by
|
|
30
|
+
// ArrayField for stable DnD ordering; reflect that in the type.
|
|
30
31
|
const types = field.of
|
|
31
32
|
.map((item) => {
|
|
32
33
|
// Check if it's a reference to a named object schema
|
|
33
34
|
const refSchema = schemaMap.get(item.type);
|
|
34
35
|
if (refSchema && refSchema.type === 'object') {
|
|
35
|
-
// Named reference
|
|
36
|
-
|
|
36
|
+
// Named reference — already has `_type?` baked into its
|
|
37
|
+
// interface, just intersect with `_key?`.
|
|
38
|
+
return `(${pascalCase(item.type)} & { _key?: string })`;
|
|
37
39
|
}
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
return mapFieldTypeToTS(item, schemaMap);
|
|
40
|
+
// Inline object or primitive — recurse with inArray=true so
|
|
41
|
+
// inline object items pick up `_key?` and `_type?`.
|
|
42
|
+
return mapFieldTypeToTS(item, schemaMap, true);
|
|
41
43
|
})
|
|
42
44
|
.filter((t) => t !== 'unknown');
|
|
43
45
|
if (types.length === 0) {
|
|
@@ -49,7 +51,10 @@ function mapFieldTypeToTS(field, schemaMap) {
|
|
|
49
51
|
if (!('fields' in field) || !field.fields) {
|
|
50
52
|
return 'Record<string, unknown>';
|
|
51
53
|
}
|
|
52
|
-
// Generate inline interface for object fields
|
|
54
|
+
// Generate inline interface for object fields. When this object is
|
|
55
|
+
// itself an array item, prepend the runtime-only `_key?` and
|
|
56
|
+
// `_type?` discriminator props.
|
|
57
|
+
const arrayMeta = inArray ? ' _key?: string;\n _type?: string;\n' : '';
|
|
53
58
|
const props = field.fields
|
|
54
59
|
.map((f) => {
|
|
55
60
|
const tsType = mapFieldTypeToTS(f, schemaMap);
|
|
@@ -57,7 +62,7 @@ function mapFieldTypeToTS(field, schemaMap) {
|
|
|
57
62
|
return ` ${f.name}${optional}: ${tsType};`;
|
|
58
63
|
})
|
|
59
64
|
.join('\n');
|
|
60
|
-
return `{\n${props}\n}`;
|
|
65
|
+
return `{\n${arrayMeta}${props}\n}`;
|
|
61
66
|
}
|
|
62
67
|
case 'reference': {
|
|
63
68
|
// References store document ID as string
|
|
@@ -143,13 +148,20 @@ ${fields}`;
|
|
|
143
148
|
return `export interface ${interfaceName} {\n${finalFields}\n}`;
|
|
144
149
|
}
|
|
145
150
|
/**
|
|
146
|
-
* Generate the Collections interface augmentation
|
|
151
|
+
* Generate the Collections interface augmentation. Singleton-flagged schemas
|
|
152
|
+
* are typed as `SingletonCollection<T>` (a narrowed pick exposing only
|
|
153
|
+
* `get`/`update`/`publish`/`unpublish`/`singletonId`/`schema`) so consumers
|
|
154
|
+
* can't accidentally call list/findByID/create/delete on them at compile
|
|
155
|
+
* time. The runtime is still the same CollectionAPI instance.
|
|
147
156
|
*/
|
|
148
157
|
function generateCollectionsAugmentation(documentSchemas) {
|
|
149
158
|
const mappings = documentSchemas
|
|
150
159
|
.map((schema) => {
|
|
151
160
|
const interfaceName = pascalCase(schema.name);
|
|
152
|
-
|
|
161
|
+
const collectionType = schema.singleton
|
|
162
|
+
? `SingletonCollection<${interfaceName}>`
|
|
163
|
+
: `CollectionAPI<${interfaceName}>`;
|
|
164
|
+
return ` ${schema.name}: ${collectionType};`;
|
|
153
165
|
})
|
|
154
166
|
.join('\n');
|
|
155
167
|
return `declare module '@aphexcms/cms-core/server' {
|
|
@@ -179,7 +191,7 @@ export function generateTypes(schemas) {
|
|
|
179
191
|
* Generated types for Aphex CMS
|
|
180
192
|
* This file is auto-generated - DO NOT EDIT manually
|
|
181
193
|
*/
|
|
182
|
-
import type { CollectionAPI } from '@aphexcms/cms-core/server';
|
|
194
|
+
import type { CollectionAPI, SingletonCollection } from '@aphexcms/cms-core/server';
|
|
183
195
|
|
|
184
196
|
// ============================================================================
|
|
185
197
|
// Object Types (nested in documents)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-types.js","sourceRoot":"","sources":["../../src/cli/generate-types.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAEhE;;GAEG;AACH,SAAS,gBAAgB,
|
|
1
|
+
{"version":3,"file":"generate-types.js","sourceRoot":"","sources":["../../src/cli/generate-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -96,7 +96,8 @@
|
|
|
96
96
|
...docType,
|
|
97
97
|
icon: schema?.icon,
|
|
98
98
|
group: schema?.group,
|
|
99
|
-
access: schema?.access
|
|
99
|
+
access: schema?.access,
|
|
100
|
+
singleton: schema?.singleton ?? false
|
|
100
101
|
};
|
|
101
102
|
})
|
|
102
103
|
.filter((docType) => {
|
|
@@ -156,6 +157,51 @@
|
|
|
156
157
|
let editingDocumentId = $state<string | null>(null);
|
|
157
158
|
let isCreatingDocument = $state(false);
|
|
158
159
|
|
|
160
|
+
// Focus mode — when on, the editor takes the full admin panel.
|
|
161
|
+
// Side panels (types sidebar + documents list) collapse to hidden, the
|
|
162
|
+
// existing mobile breadcrumb is reused as a navigation strip, and the
|
|
163
|
+
// URL gets `?focus=1` so refresh keeps you in focus mode. Esc exits.
|
|
164
|
+
let focusModeOn = $state(false);
|
|
165
|
+
|
|
166
|
+
function toggleFocusMode() {
|
|
167
|
+
focusModeOn = !focusModeOn;
|
|
168
|
+
const params = new SvelteURLSearchParams(page.url.searchParams);
|
|
169
|
+
if (focusModeOn) {
|
|
170
|
+
params.set('focus', '1');
|
|
171
|
+
} else {
|
|
172
|
+
params.delete('focus');
|
|
173
|
+
}
|
|
174
|
+
goto(`/admin?${params.toString()}`, { replaceState: true });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function exitFocusMode() {
|
|
178
|
+
if (!focusModeOn) return;
|
|
179
|
+
focusModeOn = false;
|
|
180
|
+
const params = new SvelteURLSearchParams(page.url.searchParams);
|
|
181
|
+
params.delete('focus');
|
|
182
|
+
goto(`/admin?${params.toString()}`, { replaceState: true });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Sync focus state from URL — covers refresh, deep links, back button.
|
|
186
|
+
$effect(() => {
|
|
187
|
+
const urlFocus = page.url.searchParams.get('focus') === '1';
|
|
188
|
+
if (urlFocus !== focusModeOn) {
|
|
189
|
+
focusModeOn = urlFocus;
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Esc to exit focus mode.
|
|
194
|
+
$effect(() => {
|
|
195
|
+
if (typeof window === 'undefined') return;
|
|
196
|
+
const handler = (e: KeyboardEvent) => {
|
|
197
|
+
if (e.key === 'Escape' && focusModeOn) {
|
|
198
|
+
exitFocusMode();
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
window.addEventListener('keydown', handler);
|
|
202
|
+
return () => window.removeEventListener('keydown', handler);
|
|
203
|
+
});
|
|
204
|
+
|
|
159
205
|
// Version history panel state
|
|
160
206
|
let showVersionPanel = $state(false);
|
|
161
207
|
let versionPanelDocId = $state<string | null>(null);
|
|
@@ -401,6 +447,10 @@
|
|
|
401
447
|
});
|
|
402
448
|
|
|
403
449
|
let typesPanel = $derived.by(() => {
|
|
450
|
+
// Focus mode hides the types sidebar entirely so the editor can take
|
|
451
|
+
// the full admin panel.
|
|
452
|
+
if (focusModeOn) return 'hidden';
|
|
453
|
+
|
|
404
454
|
if (windowWidth < 620) {
|
|
405
455
|
return mobileView === 'types' ? 'w-full' : 'hidden';
|
|
406
456
|
}
|
|
@@ -408,7 +458,17 @@
|
|
|
408
458
|
return layoutConfig.typesExpanded ? 'w-[350px]' : 'w-[60px]';
|
|
409
459
|
});
|
|
410
460
|
|
|
461
|
+
// True when the user is currently looking at a singleton-flagged doc type.
|
|
462
|
+
// Drives layout adjustments — singletons skip the document-list panel
|
|
463
|
+
// entirely and just show types-sidebar + editor.
|
|
464
|
+
const currentTypeIsSingleton = $derived(
|
|
465
|
+
!!selectedDocumentType &&
|
|
466
|
+
(schemas.find((s) => s.name === selectedDocumentType)?.singleton ?? false)
|
|
467
|
+
);
|
|
468
|
+
|
|
411
469
|
let documentsPanelState = $derived.by(() => {
|
|
470
|
+
if (focusModeOn) return { visible: false, width: 'none' };
|
|
471
|
+
if (currentTypeIsSingleton) return { visible: false, width: 'none' };
|
|
412
472
|
if (windowWidth < 620) {
|
|
413
473
|
const state = { visible: mobileView === 'documents', width: 'full' };
|
|
414
474
|
cmsLogger.debug('[Mobile Documents Panel]', { windowWidth, mobileView, state });
|
|
@@ -551,6 +611,14 @@
|
|
|
551
611
|
}
|
|
552
612
|
} else if (docType) {
|
|
553
613
|
cmsLogger.debug('[URL Effect]', 'Branch: DOCUMENTS (docType only)');
|
|
614
|
+
// Singletons never render the list — bounce straight to the editor.
|
|
615
|
+
// Covers direct URLs, refresh, back-button — anything that lands on
|
|
616
|
+
// `?docType=<singleton>` without a docId.
|
|
617
|
+
const docTypeSchema = schemas.find((s) => s.name === docType);
|
|
618
|
+
if (docTypeSchema?.singleton) {
|
|
619
|
+
navigateToDocumentType(docType);
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
554
622
|
currentView = 'documents';
|
|
555
623
|
mobileView = 'documents';
|
|
556
624
|
editingDocumentId = null;
|
|
@@ -590,6 +658,20 @@
|
|
|
590
658
|
if (activeTab.value !== 'structure') {
|
|
591
659
|
handleTabChange('structure');
|
|
592
660
|
}
|
|
661
|
+
|
|
662
|
+
// Singletons skip the list view: list-by-type lazy-creates and returns
|
|
663
|
+
// the canonical row, so we just open the editor on the resolved id.
|
|
664
|
+
const schema = schemas.find((s) => s.name === docType);
|
|
665
|
+
if (schema?.singleton) {
|
|
666
|
+
const response = await documents.list({ type: docType });
|
|
667
|
+
if (response.success && response.data?.[0]?.id) {
|
|
668
|
+
await navigateToEditDocument(response.data[0].id, docType, false);
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
// Fall through to the list view if resolution fails so the user
|
|
672
|
+
// at least sees an error surface rather than a stuck sidebar click.
|
|
673
|
+
}
|
|
674
|
+
|
|
593
675
|
const params = new SvelteURLSearchParams(page.url.searchParams);
|
|
594
676
|
params.set('docType', docType);
|
|
595
677
|
params.delete('docId');
|
|
@@ -621,6 +703,12 @@
|
|
|
621
703
|
}
|
|
622
704
|
|
|
623
705
|
async function navigateBack() {
|
|
706
|
+
// Going back always exits focus mode — otherwise the user lands on
|
|
707
|
+
// the doc-list view with side panels still hidden, which feels stuck.
|
|
708
|
+
if (focusModeOn) {
|
|
709
|
+
focusModeOn = false;
|
|
710
|
+
}
|
|
711
|
+
|
|
624
712
|
// Check if we came from another document (mobile reference navigation)
|
|
625
713
|
const fromDocId = page.url.searchParams.get('fromDocId');
|
|
626
714
|
const fromDocType = page.url.searchParams.get('fromDocType');
|
|
@@ -635,6 +723,7 @@
|
|
|
635
723
|
params.delete('docId');
|
|
636
724
|
params.delete('action');
|
|
637
725
|
params.delete('stack');
|
|
726
|
+
params.delete('focus');
|
|
638
727
|
await goto(`/admin?${params.toString()}`, { replaceState: false });
|
|
639
728
|
mobileView = 'documents';
|
|
640
729
|
} else {
|
|
@@ -644,6 +733,7 @@
|
|
|
644
733
|
params.delete('docId');
|
|
645
734
|
params.delete('action');
|
|
646
735
|
params.delete('stack');
|
|
736
|
+
params.delete('focus');
|
|
647
737
|
await goto(`/admin?${params.toString()}`, { replaceState: false });
|
|
648
738
|
mobileView = 'types';
|
|
649
739
|
}
|
|
@@ -751,12 +841,23 @@
|
|
|
751
841
|
activeEditorIndex = index;
|
|
752
842
|
}
|
|
753
843
|
|
|
844
|
+
let versionPanelRef = $state<{ refresh: () => void } | null>(null);
|
|
845
|
+
|
|
754
846
|
function handleAutoSave(documentId: string, title: string) {
|
|
755
847
|
if (documentsList.length > 0) {
|
|
756
848
|
documentsList = documentsList.map((doc) =>
|
|
757
849
|
doc.id === documentId ? { ...doc, title: title } : doc
|
|
758
850
|
);
|
|
759
851
|
}
|
|
852
|
+
if (showVersionPanel && versionPanelDocId === documentId) {
|
|
853
|
+
versionPanelRef?.refresh();
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
function handleDocumentPublished(documentId: string) {
|
|
858
|
+
if (showVersionPanel && versionPanelDocId === documentId) {
|
|
859
|
+
versionPanelRef?.refresh();
|
|
860
|
+
}
|
|
760
861
|
}
|
|
761
862
|
|
|
762
863
|
async function fetchDocumentForEditing(docId: string) {
|
|
@@ -868,8 +969,8 @@
|
|
|
868
969
|
</svelte:head>
|
|
869
970
|
|
|
870
971
|
<div class="flex h-full flex-col overflow-hidden">
|
|
871
|
-
<!--
|
|
872
|
-
{#if windowWidth < 620 && activeTab.value === 'structure'}
|
|
972
|
+
<!-- Breadcrumb navigation: mobile (< 620px) or focus mode on any width -->
|
|
973
|
+
{#if (windowWidth < 620 || focusModeOn) && activeTab.value === 'structure'}
|
|
873
974
|
<div class="border-border bg-background border-b">
|
|
874
975
|
<div class="flex h-12 items-center px-4">
|
|
875
976
|
{#if mobileView === 'documents' && selectedDocumentType}
|
|
@@ -1016,7 +1117,9 @@
|
|
|
1016
1117
|
<FileText class="h-4 w-4" />
|
|
1017
1118
|
{/if}
|
|
1018
1119
|
</div>
|
|
1019
|
-
<span class="text-sm"
|
|
1120
|
+
<span class="text-sm"
|
|
1121
|
+
>{docType.singleton ? docType.title : pluralize(docType.title)}</span
|
|
1122
|
+
>
|
|
1020
1123
|
</div>
|
|
1021
1124
|
<svg
|
|
1022
1125
|
class="text-muted-foreground h-4 w-4 opacity-0 transition-opacity group-hover:opacity-100"
|
|
@@ -1113,7 +1216,7 @@
|
|
|
1113
1216
|
</div>
|
|
1114
1217
|
</div>
|
|
1115
1218
|
<div class="flex items-center gap-1">
|
|
1116
|
-
{#if perms.can('document.create')}
|
|
1219
|
+
{#if perms.can('document.create') && !schemas.find((s) => s.name === selectedDocumentType)?.singleton}
|
|
1117
1220
|
<Button
|
|
1118
1221
|
size="sm"
|
|
1119
1222
|
variant="ghost"
|
|
@@ -1420,6 +1523,8 @@
|
|
|
1420
1523
|
documentType={selectedDocumentType!}
|
|
1421
1524
|
documentId={editingDocumentId}
|
|
1422
1525
|
isCreating={isCreatingDocument}
|
|
1526
|
+
focusMode={focusModeOn}
|
|
1527
|
+
onToggleFocus={toggleFocusMode}
|
|
1423
1528
|
onBack={navigateBack}
|
|
1424
1529
|
onOpenReference={handleOpenReference}
|
|
1425
1530
|
onOpenVersionHistory={handleOpenVersionHistory}
|
|
@@ -1458,17 +1563,20 @@
|
|
|
1458
1563
|
}
|
|
1459
1564
|
}}
|
|
1460
1565
|
onAutoSaved={handleAutoSave}
|
|
1461
|
-
onPublished={async (
|
|
1566
|
+
onPublished={async (docId) => {
|
|
1567
|
+
handleDocumentPublished(docId);
|
|
1462
1568
|
if (selectedDocumentType) {
|
|
1463
1569
|
await fetchDocuments(selectedDocumentType);
|
|
1464
1570
|
}
|
|
1465
1571
|
}}
|
|
1466
|
-
onUnpublished={async (
|
|
1572
|
+
onUnpublished={async (docId) => {
|
|
1573
|
+
handleDocumentPublished(docId);
|
|
1467
1574
|
if (selectedDocumentType) {
|
|
1468
1575
|
await fetchDocuments(selectedDocumentType);
|
|
1469
1576
|
}
|
|
1470
1577
|
}}
|
|
1471
|
-
onRestored={async (
|
|
1578
|
+
onRestored={async (docId) => {
|
|
1579
|
+
handleDocumentPublished(docId);
|
|
1472
1580
|
if (selectedDocumentType) {
|
|
1473
1581
|
await fetchDocuments(selectedDocumentType);
|
|
1474
1582
|
}
|
|
@@ -1533,18 +1641,21 @@
|
|
|
1533
1641
|
? versionPreviewData
|
|
1534
1642
|
: null}
|
|
1535
1643
|
onSaved={async () => {}}
|
|
1536
|
-
onAutoSaved={
|
|
1537
|
-
onPublished={async () => {
|
|
1644
|
+
onAutoSaved={handleAutoSave}
|
|
1645
|
+
onPublished={async (docId) => {
|
|
1646
|
+
handleDocumentPublished(docId);
|
|
1538
1647
|
if (selectedDocumentType) {
|
|
1539
1648
|
await fetchDocuments(selectedDocumentType);
|
|
1540
1649
|
}
|
|
1541
1650
|
}}
|
|
1542
|
-
onUnpublished={async () => {
|
|
1651
|
+
onUnpublished={async (docId) => {
|
|
1652
|
+
handleDocumentPublished(docId);
|
|
1543
1653
|
if (selectedDocumentType) {
|
|
1544
1654
|
await fetchDocuments(selectedDocumentType);
|
|
1545
1655
|
}
|
|
1546
1656
|
}}
|
|
1547
|
-
onRestored={async () => {
|
|
1657
|
+
onRestored={async (docId) => {
|
|
1658
|
+
handleDocumentPublished(docId);
|
|
1548
1659
|
if (selectedDocumentType) {
|
|
1549
1660
|
await fetchDocuments(selectedDocumentType);
|
|
1550
1661
|
}
|
|
@@ -1581,6 +1692,7 @@
|
|
|
1581
1692
|
class="border-rule h-full w-[280px] min-w-[250px] overflow-y-auto border-l transition-all duration-200"
|
|
1582
1693
|
>
|
|
1583
1694
|
<DocumentVersionPanel
|
|
1695
|
+
bind:this={versionPanelRef}
|
|
1584
1696
|
documentId={versionPanelDocId}
|
|
1585
1697
|
onClose={handleCloseVersionPanel}
|
|
1586
1698
|
onPreviewVersion={(v) => {
|
|
@@ -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;
|
|
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"}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import { cmsLogger } from '../../utils/logger';
|
|
17
17
|
import { toast } from 'svelte-sonner';
|
|
18
18
|
import { confirmDialog } from './confirm-dialog/confirm-dialog.svelte';
|
|
19
|
-
import { History, Trash2, Ellipsis, Code } from '@lucide/svelte';
|
|
19
|
+
import { History, Trash2, Ellipsis, Code, Maximize2, Minimize2 } from '@lucide/svelte';
|
|
20
20
|
|
|
21
21
|
interface Props {
|
|
22
22
|
schemas: SchemaType[];
|
|
@@ -39,6 +39,10 @@
|
|
|
39
39
|
createdAt?: string;
|
|
40
40
|
} | null;
|
|
41
41
|
isReadOnly?: boolean;
|
|
42
|
+
/** When true, the host has hidden side panels — show a Minimize toggle. */
|
|
43
|
+
focusMode?: boolean;
|
|
44
|
+
/** Toggle host-driven focus mode. Omit to hide the focus button entirely. */
|
|
45
|
+
onToggleFocus?: () => void;
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
let {
|
|
@@ -56,7 +60,9 @@
|
|
|
56
60
|
onOpenReference,
|
|
57
61
|
onOpenVersionHistory,
|
|
58
62
|
externalVersionPreview = null,
|
|
59
|
-
isReadOnly = false
|
|
63
|
+
isReadOnly = false,
|
|
64
|
+
focusMode = false,
|
|
65
|
+
onToggleFocus
|
|
60
66
|
}: Props = $props();
|
|
61
67
|
|
|
62
68
|
// Set schema context for child components (ArrayField, etc.)
|
|
@@ -188,6 +194,37 @@
|
|
|
188
194
|
const activePreview = $derived(externalVersionPreview || previewingVersion);
|
|
189
195
|
const isPreviewingVersion = $derived(!!activePreview);
|
|
190
196
|
|
|
197
|
+
// Version list state — kept in component state so we can refresh it after
|
|
198
|
+
// autosaves while the panel is open (otherwise new history events are
|
|
199
|
+
// invisible until the panel is closed and reopened).
|
|
200
|
+
let versionsList = $state<any[]>([]);
|
|
201
|
+
let versionsLoading = $state(false);
|
|
202
|
+
let versionsError = $state(false);
|
|
203
|
+
|
|
204
|
+
async function loadVersions() {
|
|
205
|
+
if (!documentId) return;
|
|
206
|
+
versionsLoading = true;
|
|
207
|
+
versionsError = false;
|
|
208
|
+
try {
|
|
209
|
+
const response = await documents.listVersions(documentId);
|
|
210
|
+
if (response.success && response.data) {
|
|
211
|
+
versionsList = response.data;
|
|
212
|
+
} else {
|
|
213
|
+
versionsError = true;
|
|
214
|
+
}
|
|
215
|
+
} catch {
|
|
216
|
+
versionsError = true;
|
|
217
|
+
} finally {
|
|
218
|
+
versionsLoading = false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
$effect(() => {
|
|
223
|
+
if (showVersionHistory && documentId) {
|
|
224
|
+
loadVersions();
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
191
228
|
// Ticker to keep relative time updating
|
|
192
229
|
let now = $state(Date.now());
|
|
193
230
|
let tickerInterval: ReturnType<typeof setInterval> | null = null;
|
|
@@ -617,6 +654,9 @@
|
|
|
617
654
|
onAutoSaved(documentId, getPreviewTitle());
|
|
618
655
|
}
|
|
619
656
|
}
|
|
657
|
+
if (showVersionHistory) {
|
|
658
|
+
loadVersions();
|
|
659
|
+
}
|
|
620
660
|
} else {
|
|
621
661
|
throw new Error(response?.error || 'Failed to save document');
|
|
622
662
|
}
|
|
@@ -641,6 +681,17 @@
|
|
|
641
681
|
async function publishDocument() {
|
|
642
682
|
if (!documentId || saving) return;
|
|
643
683
|
|
|
684
|
+
// Flush any pending debounced autosave so we publish the user's latest
|
|
685
|
+
// edits, not the previously-saved draft.
|
|
686
|
+
if (autoSaveTimer) {
|
|
687
|
+
clearTimeout(autoSaveTimer);
|
|
688
|
+
autoSaveTimer = null;
|
|
689
|
+
}
|
|
690
|
+
if (hasUnsavedChanges) {
|
|
691
|
+
await saveDocument(false);
|
|
692
|
+
if (saveError) return;
|
|
693
|
+
}
|
|
694
|
+
|
|
644
695
|
// Check for validation errors before publishing (Sanity-style)
|
|
645
696
|
const invalid = await validateAllFields();
|
|
646
697
|
|
|
@@ -668,7 +719,13 @@
|
|
|
668
719
|
const response = await documents.publish(documentId);
|
|
669
720
|
|
|
670
721
|
if (response.success && response.data) {
|
|
671
|
-
// Update local state with new published hash
|
|
722
|
+
// Update local state with new published hash. Sync documentData from
|
|
723
|
+
// the response too — the server normalises data (e.g. dates) before
|
|
724
|
+
// hashing for the published_hash, so the client must use the same
|
|
725
|
+
// normalised values, otherwise hasUnpublishedChanges always returns
|
|
726
|
+
// true and the publish button never disables.
|
|
727
|
+
const { id: _id, _meta: _m, ...syncedData } = response.data as any;
|
|
728
|
+
documentData = syncedData;
|
|
672
729
|
fullDocument = response.data;
|
|
673
730
|
lastSaved = new Date();
|
|
674
731
|
publishSuccess = new Date();
|
|
@@ -679,6 +736,9 @@
|
|
|
679
736
|
if (onPublished && documentId) {
|
|
680
737
|
onPublished(documentId);
|
|
681
738
|
}
|
|
739
|
+
if (showVersionHistory) {
|
|
740
|
+
loadVersions();
|
|
741
|
+
}
|
|
682
742
|
} else {
|
|
683
743
|
throw new Error(response.error || 'Failed to publish document');
|
|
684
744
|
}
|
|
@@ -1026,6 +1086,22 @@
|
|
|
1026
1086
|
</div>
|
|
1027
1087
|
{/if}
|
|
1028
1088
|
|
|
1089
|
+
{#if onToggleFocus}
|
|
1090
|
+
<Button
|
|
1091
|
+
variant="ghost"
|
|
1092
|
+
size="icon"
|
|
1093
|
+
onclick={onToggleFocus}
|
|
1094
|
+
class="hidden h-8 w-8 hover:cursor-pointer lg:flex"
|
|
1095
|
+
title={focusMode ? 'Exit focus mode' : 'Enter focus mode'}
|
|
1096
|
+
>
|
|
1097
|
+
{#if focusMode}
|
|
1098
|
+
<Minimize2 class="h-4 w-4" />
|
|
1099
|
+
{:else}
|
|
1100
|
+
<Maximize2 class="h-4 w-4" />
|
|
1101
|
+
{/if}
|
|
1102
|
+
</Button>
|
|
1103
|
+
{/if}
|
|
1104
|
+
|
|
1029
1105
|
<Button
|
|
1030
1106
|
variant="ghost"
|
|
1031
1107
|
size="icon"
|
|
@@ -1379,7 +1455,7 @@
|
|
|
1379
1455
|
<Badge variant="secondary" class="text-xs">Read Only</Badge>
|
|
1380
1456
|
{/if}
|
|
1381
1457
|
|
|
1382
|
-
{#if canDelete}
|
|
1458
|
+
{#if canDelete && !schema?.singleton}
|
|
1383
1459
|
<Button
|
|
1384
1460
|
variant="ghost"
|
|
1385
1461
|
size="icon"
|
|
@@ -1429,14 +1505,17 @@
|
|
|
1429
1505
|
</button>
|
|
1430
1506
|
</div>
|
|
1431
1507
|
<div class="flex-1 overflow-auto">
|
|
1432
|
-
{#
|
|
1508
|
+
{#if versionsLoading && versionsList.length === 0}
|
|
1433
1509
|
<div class="p-4 text-center">
|
|
1434
1510
|
<span class="text-muted-foreground text-sm">Loading versions...</span>
|
|
1435
1511
|
</div>
|
|
1436
|
-
{:
|
|
1437
|
-
|
|
1438
|
-
<
|
|
1439
|
-
|
|
1512
|
+
{:else if versionsError}
|
|
1513
|
+
<div class="p-4 text-center">
|
|
1514
|
+
<span class="text-destructive text-sm">Failed to load versions</span>
|
|
1515
|
+
</div>
|
|
1516
|
+
{:else if versionsList.length > 0}
|
|
1517
|
+
<div class="divide-y">
|
|
1518
|
+
{#each versionsList as version}
|
|
1440
1519
|
<button
|
|
1441
1520
|
class="hover:bg-muted w-full space-y-1 px-4 py-3 text-left transition-colors {previewingVersion?.versionNumber ===
|
|
1442
1521
|
version.versionNumber
|
|
@@ -1481,11 +1560,6 @@
|
|
|
1481
1560
|
<span class="text-muted-foreground text-sm">No versions yet</span>
|
|
1482
1561
|
</div>
|
|
1483
1562
|
{/if}
|
|
1484
|
-
{:catch}
|
|
1485
|
-
<div class="p-4 text-center">
|
|
1486
|
-
<span class="text-destructive text-sm">Failed to load versions</span>
|
|
1487
|
-
</div>
|
|
1488
|
-
{/await}
|
|
1489
1563
|
</div>
|
|
1490
1564
|
</div>
|
|
1491
1565
|
</div>
|
|
@@ -20,6 +20,10 @@ interface Props {
|
|
|
20
20
|
createdAt?: string;
|
|
21
21
|
} | null;
|
|
22
22
|
isReadOnly?: boolean;
|
|
23
|
+
/** When true, the host has hidden side panels — show a Minimize toggle. */
|
|
24
|
+
focusMode?: boolean;
|
|
25
|
+
/** Toggle host-driven focus mode. Omit to hide the focus button entirely. */
|
|
26
|
+
onToggleFocus?: () => void;
|
|
23
27
|
}
|
|
24
28
|
declare const DocumentEditor: import("svelte").Component<Props, {}, "">;
|
|
25
29
|
type DocumentEditor = ReturnType<typeof DocumentEditor>;
|
|
@@ -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;
|
|
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"}
|
|
@@ -34,6 +34,12 @@
|
|
|
34
34
|
loadVersions();
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
+
// Exposed so the parent (AdminApp) can refresh the list after autosave /
|
|
38
|
+
// publish events without remounting the panel.
|
|
39
|
+
export function refresh() {
|
|
40
|
+
return loadVersions();
|
|
41
|
+
}
|
|
42
|
+
|
|
37
43
|
async function loadVersions() {
|
|
38
44
|
loading = true;
|
|
39
45
|
try {
|
|
@@ -9,7 +9,9 @@ interface Props {
|
|
|
9
9
|
createdAt?: string;
|
|
10
10
|
} | null) => void;
|
|
11
11
|
}
|
|
12
|
-
declare const DocumentVersionPanel: import("svelte").Component<Props, {
|
|
12
|
+
declare const DocumentVersionPanel: import("svelte").Component<Props, {
|
|
13
|
+
refresh: () => Promise<void>;
|
|
14
|
+
}, "">;
|
|
13
15
|
type DocumentVersionPanel = ReturnType<typeof DocumentVersionPanel>;
|
|
14
16
|
export default DocumentVersionPanel;
|
|
15
17
|
//# sourceMappingURL=DocumentVersionPanel.svelte.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocumentVersionPanel.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/admin/DocumentVersionPanel.svelte.ts"],"names":[],"mappings":"AASC,UAAU,KAAK;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,gBAAgB,CAAC,EAAE,CAClB,OAAO,EAAE;QACR,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,KACJ,IAAI,CAAC;CACV;
|
|
1
|
+
{"version":3,"file":"DocumentVersionPanel.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/admin/DocumentVersionPanel.svelte.ts"],"names":[],"mappings":"AASC,UAAU,KAAK;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,gBAAgB,CAAC,EAAE,CAClB,OAAO,EAAE;QACR,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,KACJ,IAAI,CAAC;CACV;AAkIF,QAAA,MAAM,oBAAoB;;MAAwC,CAAC;AACnE,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACpE,eAAe,oBAAoB,CAAC"}
|