@xleddyl/nuxt-cms 0.1.9

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 (138) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +26 -0
  3. package/dist/module.d.mts +36 -0
  4. package/dist/module.json +9 -0
  5. package/dist/module.mjs +765 -0
  6. package/dist/runtime/app/components/cms/BlocksField.d.vue.ts +15 -0
  7. package/dist/runtime/app/components/cms/BlocksField.vue +110 -0
  8. package/dist/runtime/app/components/cms/BlocksField.vue.d.ts +15 -0
  9. package/dist/runtime/app/components/cms/EmptyState.d.vue.ts +18 -0
  10. package/dist/runtime/app/components/cms/EmptyState.vue +20 -0
  11. package/dist/runtime/app/components/cms/EmptyState.vue.d.ts +18 -0
  12. package/dist/runtime/app/components/cms/EntryForm.d.vue.ts +23 -0
  13. package/dist/runtime/app/components/cms/EntryForm.vue +78 -0
  14. package/dist/runtime/app/components/cms/EntryForm.vue.d.ts +23 -0
  15. package/dist/runtime/app/components/cms/FieldInput.d.vue.ts +16 -0
  16. package/dist/runtime/app/components/cms/FieldInput.vue +85 -0
  17. package/dist/runtime/app/components/cms/FieldInput.vue.d.ts +16 -0
  18. package/dist/runtime/app/components/cms/JsonField.d.vue.ts +10 -0
  19. package/dist/runtime/app/components/cms/JsonField.vue +51 -0
  20. package/dist/runtime/app/components/cms/JsonField.vue.d.ts +10 -0
  21. package/dist/runtime/app/components/cms/MediaField.d.vue.ts +16 -0
  22. package/dist/runtime/app/components/cms/MediaField.vue +81 -0
  23. package/dist/runtime/app/components/cms/MediaField.vue.d.ts +16 -0
  24. package/dist/runtime/app/components/cms/MediaGallery.d.vue.ts +19 -0
  25. package/dist/runtime/app/components/cms/MediaGallery.vue +309 -0
  26. package/dist/runtime/app/components/cms/MediaGallery.vue.d.ts +19 -0
  27. package/dist/runtime/app/components/cms/MediaUpload.d.vue.ts +14 -0
  28. package/dist/runtime/app/components/cms/MediaUpload.vue +132 -0
  29. package/dist/runtime/app/components/cms/MediaUpload.vue.d.ts +14 -0
  30. package/dist/runtime/app/components/cms/PageHeader.d.vue.ts +19 -0
  31. package/dist/runtime/app/components/cms/PageHeader.vue +23 -0
  32. package/dist/runtime/app/components/cms/PageHeader.vue.d.ts +19 -0
  33. package/dist/runtime/app/components/cms/RelationField.d.vue.ts +16 -0
  34. package/dist/runtime/app/components/cms/RelationField.vue +87 -0
  35. package/dist/runtime/app/components/cms/RelationField.vue.d.ts +16 -0
  36. package/dist/runtime/app/components/cms/RichTextField.d.vue.ts +10 -0
  37. package/dist/runtime/app/components/cms/RichTextField.vue +123 -0
  38. package/dist/runtime/app/components/cms/RichTextField.vue.d.ts +10 -0
  39. package/dist/runtime/app/components/cms/SlugField.d.vue.ts +14 -0
  40. package/dist/runtime/app/components/cms/SlugField.vue +44 -0
  41. package/dist/runtime/app/components/cms/SlugField.vue.d.ts +14 -0
  42. package/dist/runtime/app/components/cms/TranslatableField.d.vue.ts +15 -0
  43. package/dist/runtime/app/components/cms/TranslatableField.vue +64 -0
  44. package/dist/runtime/app/components/cms/TranslatableField.vue.d.ts +15 -0
  45. package/dist/runtime/app/composables/cms-query.d.ts +4 -0
  46. package/dist/runtime/app/composables/cms-query.js +18 -0
  47. package/dist/runtime/app/composables/cms-runtime.d.ts +5 -0
  48. package/dist/runtime/app/composables/cms-runtime.js +4 -0
  49. package/dist/runtime/app/i18n/en.json +93 -0
  50. package/dist/runtime/app/i18n/it.json +93 -0
  51. package/dist/runtime/app/layouts/cms-admin.d.vue.ts +13 -0
  52. package/dist/runtime/app/layouts/cms-admin.vue +97 -0
  53. package/dist/runtime/app/layouts/cms-admin.vue.d.ts +13 -0
  54. package/dist/runtime/app/middleware/cms-auth.d.ts +2 -0
  55. package/dist/runtime/app/middleware/cms-auth.js +7 -0
  56. package/dist/runtime/app/pages/admin-collection.d.vue.ts +3 -0
  57. package/dist/runtime/app/pages/admin-collection.vue +273 -0
  58. package/dist/runtime/app/pages/admin-collection.vue.d.ts +3 -0
  59. package/dist/runtime/app/pages/admin-entry.d.vue.ts +3 -0
  60. package/dist/runtime/app/pages/admin-entry.vue +156 -0
  61. package/dist/runtime/app/pages/admin-entry.vue.d.ts +3 -0
  62. package/dist/runtime/app/pages/admin-index.d.vue.ts +3 -0
  63. package/dist/runtime/app/pages/admin-index.vue +11 -0
  64. package/dist/runtime/app/pages/admin-index.vue.d.ts +3 -0
  65. package/dist/runtime/app/pages/admin-login.d.vue.ts +3 -0
  66. package/dist/runtime/app/pages/admin-login.vue +87 -0
  67. package/dist/runtime/app/pages/admin-login.vue.d.ts +3 -0
  68. package/dist/runtime/app/pages/admin-media.d.vue.ts +3 -0
  69. package/dist/runtime/app/pages/admin-media.vue +13 -0
  70. package/dist/runtime/app/pages/admin-media.vue.d.ts +3 -0
  71. package/dist/runtime/app/utils/api.d.ts +7 -0
  72. package/dist/runtime/app/utils/api.js +3 -0
  73. package/dist/runtime/app/utils/ui.d.ts +8 -0
  74. package/dist/runtime/app/utils/ui.js +9 -0
  75. package/dist/runtime/assets/main.css +1 -0
  76. package/dist/runtime/server/api/collection.get.d.ts +6 -0
  77. package/dist/runtime/server/api/collection.get.js +19 -0
  78. package/dist/runtime/server/api/collection.post.d.ts +2 -0
  79. package/dist/runtime/server/api/collection.post.js +30 -0
  80. package/dist/runtime/server/api/collection.put.d.ts +2 -0
  81. package/dist/runtime/server/api/collection.put.js +28 -0
  82. package/dist/runtime/server/api/item.delete.d.ts +4 -0
  83. package/dist/runtime/server/api/item.delete.js +19 -0
  84. package/dist/runtime/server/api/item.get.d.ts +2 -0
  85. package/dist/runtime/server/api/item.get.js +18 -0
  86. package/dist/runtime/server/api/item.put.d.ts +2 -0
  87. package/dist/runtime/server/api/item.put.js +40 -0
  88. package/dist/runtime/server/api/media-presign.post.d.ts +11 -0
  89. package/dist/runtime/server/api/media-presign.post.js +51 -0
  90. package/dist/runtime/server/api/media.delete.d.ts +4 -0
  91. package/dist/runtime/server/api/media.delete.js +25 -0
  92. package/dist/runtime/server/api/media.get.d.ts +5 -0
  93. package/dist/runtime/server/api/media.get.js +13 -0
  94. package/dist/runtime/server/api/media.post.d.ts +3 -0
  95. package/dist/runtime/server/api/media.post.js +32 -0
  96. package/dist/runtime/server/api/media.put.d.ts +3 -0
  97. package/dist/runtime/server/api/media.put.js +28 -0
  98. package/dist/runtime/server/plugins/migrate-postgres.d.ts +2 -0
  99. package/dist/runtime/server/plugins/migrate-postgres.js +9 -0
  100. package/dist/runtime/server/plugins/migrate-sqlite.d.ts +2 -0
  101. package/dist/runtime/server/plugins/migrate-sqlite.js +9 -0
  102. package/dist/runtime/server/plugins/session-check.d.ts +2 -0
  103. package/dist/runtime/server/plugins/session-check.js +9 -0
  104. package/dist/runtime/server/routes/auth/login.post.d.ts +4 -0
  105. package/dist/runtime/server/routes/auth/login.post.js +62 -0
  106. package/dist/runtime/server/routes/graphql.d.ts +2 -0
  107. package/dist/runtime/server/routes/graphql.js +34 -0
  108. package/dist/runtime/server/tsconfig.json +3 -0
  109. package/dist/runtime/server/utils/custom-id.d.ts +2 -0
  110. package/dist/runtime/server/utils/custom-id.js +10 -0
  111. package/dist/runtime/server/utils/db-errors.d.ts +1 -0
  112. package/dist/runtime/server/utils/db-errors.js +31 -0
  113. package/dist/runtime/server/utils/db-postgres.d.ts +3 -0
  114. package/dist/runtime/server/utils/db-postgres.js +16 -0
  115. package/dist/runtime/server/utils/db-sqlite.d.ts +4 -0
  116. package/dist/runtime/server/utils/db-sqlite.js +17 -0
  117. package/dist/runtime/server/utils/graphql-depth.d.ts +4 -0
  118. package/dist/runtime/server/utils/graphql-depth.js +72 -0
  119. package/dist/runtime/server/utils/graphql.d.ts +14 -0
  120. package/dist/runtime/server/utils/graphql.js +317 -0
  121. package/dist/runtime/server/utils/media.d.ts +24 -0
  122. package/dist/runtime/server/utils/media.js +38 -0
  123. package/dist/runtime/server/utils/registry.d.ts +20 -0
  124. package/dist/runtime/server/utils/registry.js +47 -0
  125. package/dist/runtime/server/utils/relations.d.ts +12 -0
  126. package/dist/runtime/server/utils/relations.js +80 -0
  127. package/dist/runtime/server/utils/require-admin.d.ts +2 -0
  128. package/dist/runtime/server/utils/require-admin.js +13 -0
  129. package/dist/runtime/shared/empty-config.d.ts +2 -0
  130. package/dist/runtime/shared/empty-config.js +1 -0
  131. package/dist/runtime/shared/graphql-sdl.d.ts +5 -0
  132. package/dist/runtime/shared/graphql-sdl.js +147 -0
  133. package/dist/runtime/shared/index.d.ts +62 -0
  134. package/dist/runtime/shared/index.js +55 -0
  135. package/dist/runtime/shared/validation.d.ts +16 -0
  136. package/dist/runtime/shared/validation.js +82 -0
  137. package/dist/types.d.mts +3 -0
  138. package/package.json +93 -0
@@ -0,0 +1,10 @@
1
+ type __VLS_ModelProps = {
2
+ modelValue: string | null;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
+ "update:modelValue": (value: string | null) => any;
6
+ }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
7
+ "onUpdate:modelValue"?: ((value: string | null) => any) | undefined;
8
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
+ declare const _default: typeof __VLS_export;
10
+ export default _default;
@@ -0,0 +1,123 @@
1
+ <template>
2
+ <div class="cms-richtext">
3
+ <div class="cms-richtext-toolbar">
4
+ <UButton
5
+ v-for="action in actions"
6
+ :key="action.icon"
7
+ :icon="action.icon"
8
+ size="xs"
9
+ :variant="editor?.isActive(action.mark, action.attrs) ? 'soft' : 'ghost'"
10
+ color="neutral"
11
+ @click="action.run"
12
+ />
13
+ <span class="grow" />
14
+ <UButton
15
+ icon="i-lucide-undo-2"
16
+ size="xs"
17
+ variant="ghost"
18
+ color="neutral"
19
+ :disabled="!editor?.can().undo()"
20
+ @click="undo"
21
+ />
22
+ <UButton
23
+ icon="i-lucide-redo-2"
24
+ size="xs"
25
+ variant="ghost"
26
+ color="neutral"
27
+ :disabled="!editor?.can().redo()"
28
+ @click="redo"
29
+ />
30
+ </div>
31
+ <EditorContent :editor="editor" />
32
+ </div>
33
+ </template>
34
+
35
+ <script setup>
36
+ import StarterKit from "@tiptap/starter-kit";
37
+ import { EditorContent, useEditor } from "@tiptap/vue-3";
38
+ import { watch } from "#imports";
39
+ const model = defineModel({ type: [String, null], ...{ required: true } });
40
+ const editor = useEditor({
41
+ content: model.value ?? "",
42
+ extensions: [StarterKit],
43
+ editorProps: {
44
+ attributes: { class: "cms-richtext-body" }
45
+ },
46
+ onUpdate: ({ editor: instance }) => {
47
+ model.value = instance.isEmpty ? null : instance.getHTML();
48
+ }
49
+ });
50
+ watch(model, (value) => {
51
+ if (!editor.value) return;
52
+ const current = editor.value.isEmpty ? null : editor.value.getHTML();
53
+ if ((value ?? null) !== current) {
54
+ editor.value.commands.setContent(value ?? "");
55
+ }
56
+ });
57
+ const actions = [
58
+ {
59
+ icon: "i-lucide-bold",
60
+ mark: "bold",
61
+ run: () => {
62
+ editor.value?.chain().focus().toggleBold().run();
63
+ }
64
+ },
65
+ {
66
+ icon: "i-lucide-italic",
67
+ mark: "italic",
68
+ run: () => {
69
+ editor.value?.chain().focus().toggleItalic().run();
70
+ }
71
+ },
72
+ {
73
+ icon: "i-lucide-strikethrough",
74
+ mark: "strike",
75
+ run: () => {
76
+ editor.value?.chain().focus().toggleStrike().run();
77
+ }
78
+ },
79
+ {
80
+ icon: "i-lucide-heading-2",
81
+ mark: "heading",
82
+ attrs: { level: 2 },
83
+ run: () => {
84
+ editor.value?.chain().focus().toggleHeading({ level: 2 }).run();
85
+ }
86
+ },
87
+ {
88
+ icon: "i-lucide-heading-3",
89
+ mark: "heading",
90
+ attrs: { level: 3 },
91
+ run: () => {
92
+ editor.value?.chain().focus().toggleHeading({ level: 3 }).run();
93
+ }
94
+ },
95
+ {
96
+ icon: "i-lucide-list",
97
+ mark: "bulletList",
98
+ run: () => {
99
+ editor.value?.chain().focus().toggleBulletList().run();
100
+ }
101
+ },
102
+ {
103
+ icon: "i-lucide-list-ordered",
104
+ mark: "orderedList",
105
+ run: () => {
106
+ editor.value?.chain().focus().toggleOrderedList().run();
107
+ }
108
+ },
109
+ {
110
+ icon: "i-lucide-text-quote",
111
+ mark: "blockquote",
112
+ run: () => {
113
+ editor.value?.chain().focus().toggleBlockquote().run();
114
+ }
115
+ }
116
+ ];
117
+ function undo() {
118
+ editor.value?.chain().focus().undo().run();
119
+ }
120
+ function redo() {
121
+ editor.value?.chain().focus().redo().run();
122
+ }
123
+ </script>
@@ -0,0 +1,10 @@
1
+ type __VLS_ModelProps = {
2
+ modelValue: string | null;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
+ "update:modelValue": (value: string | null) => any;
6
+ }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
7
+ "onUpdate:modelValue"?: ((value: string | null) => any) | undefined;
8
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
+ declare const _default: typeof __VLS_export;
10
+ export default _default;
@@ -0,0 +1,14 @@
1
+ type __VLS_Props = {
2
+ source?: string | null;
3
+ };
4
+ type __VLS_ModelProps = {
5
+ modelValue: string | null;
6
+ };
7
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
8
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
+ "update:modelValue": (value: string | null) => any;
10
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
11
+ "onUpdate:modelValue"?: ((value: string | null) => any) | undefined;
12
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
+ declare const _default: typeof __VLS_export;
14
+ export default _default;
@@ -0,0 +1,44 @@
1
+ <template>
2
+ <UInput
3
+ :model-value="model ?? ''"
4
+ size="lg"
5
+ class="w-full font-mono"
6
+ @update:model-value="
7
+ (v) => {
8
+ model = v || null;
9
+ touched = true;
10
+ }
11
+ "
12
+ >
13
+ <template v-if="source !== void 0" #trailing>
14
+ <UButton
15
+ icon="i-lucide-refresh-cw"
16
+ size="xs"
17
+ variant="ghost"
18
+ color="neutral"
19
+ @click="regenerate"
20
+ />
21
+ </template>
22
+ </UInput>
23
+ </template>
24
+
25
+ <script setup>
26
+ import { ref, watch } from "#imports";
27
+ import { slugify } from "#nuxt-cms";
28
+ const props = defineProps({
29
+ source: { type: [String, null], required: false }
30
+ });
31
+ const model = defineModel({ type: [String, null], ...{ required: true } });
32
+ const touched = ref(!!model.value);
33
+ watch(
34
+ () => props.source,
35
+ (value) => {
36
+ if (touched.value) return;
37
+ model.value = value ? slugify(value) : null;
38
+ }
39
+ );
40
+ function regenerate() {
41
+ model.value = props.source ? slugify(props.source) : null;
42
+ touched.value = false;
43
+ }
44
+ </script>
@@ -0,0 +1,14 @@
1
+ type __VLS_Props = {
2
+ source?: string | null;
3
+ };
4
+ type __VLS_ModelProps = {
5
+ modelValue: string | null;
6
+ };
7
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
8
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
+ "update:modelValue": (value: string | null) => any;
10
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
11
+ "onUpdate:modelValue"?: ((value: string | null) => any) | undefined;
12
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
+ declare const _default: typeof __VLS_export;
14
+ export default _default;
@@ -0,0 +1,15 @@
1
+ import type { FieldConfig } from '#nuxt-cms';
2
+ type __VLS_Props = {
3
+ field: FieldConfig;
4
+ };
5
+ type __VLS_ModelProps = {
6
+ modelValue: Record<string, string> | null;
7
+ };
8
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
10
+ "update:modelValue": (value: Record<string, string> | null) => any;
11
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
12
+ "onUpdate:modelValue"?: ((value: Record<string, string> | null) => any) | undefined;
13
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
+ declare const _default: typeof __VLS_export;
15
+ export default _default;
@@ -0,0 +1,64 @@
1
+ <template>
2
+ <div class="flex flex-col gap-2">
3
+ <div v-if="i18n.locales.length > 1" class="flex items-center gap-1">
4
+ <UButton
5
+ v-for="locale in i18n.locales"
6
+ :key="locale"
7
+ size="xs"
8
+ :variant="active === locale ? 'soft' : 'ghost'"
9
+ color="neutral"
10
+ class="font-mono uppercase"
11
+ @click="
12
+ () => {
13
+ active = locale;
14
+ }
15
+ "
16
+ >
17
+ {{ locale }}
18
+ <span
19
+ class="size-1.5 rounded-full"
20
+ :class="hasContent(locale) ? 'bg-(--cms-fern)' : 'bg-(--cms-line)'"
21
+ />
22
+ </UButton>
23
+ <span
24
+ v-if="props.field.required && active === i18n.defaultLocale"
25
+ class="text-(--ui-text-muted) ml-auto font-mono text-xs"
26
+ >
27
+ {{ i18n.defaultLocale }} *
28
+ </span>
29
+ </div>
30
+ <CmsRichTextField v-if="props.field.type === 'richtext'" v-model="current" />
31
+ <UTextarea
32
+ v-else-if="props.field.textarea"
33
+ v-model="current"
34
+ :rows="4"
35
+ size="lg"
36
+ class="w-full"
37
+ />
38
+ <UInput v-else v-model="current" size="lg" class="w-full" />
39
+ </div>
40
+ </template>
41
+
42
+ <script setup>
43
+ import { computed, ref } from "#imports";
44
+ import { useCmsRuntime } from "../../composables/cms-runtime";
45
+ const props = defineProps({
46
+ field: { type: Object, required: true }
47
+ });
48
+ const model = defineModel({ type: [Object, null], ...{ required: true } });
49
+ const { i18n } = useCmsRuntime();
50
+ const active = ref(i18n.defaultLocale);
51
+ const current = computed({
52
+ get: () => model.value?.[active.value] ?? "",
53
+ set: (value) => {
54
+ const next = Object.fromEntries(
55
+ Object.entries(model.value ?? {}).filter(([locale]) => locale !== active.value)
56
+ );
57
+ if (value != null && value !== "") next[active.value] = value;
58
+ model.value = Object.keys(next).length ? next : null;
59
+ }
60
+ });
61
+ function hasContent(locale) {
62
+ return !!model.value?.[locale]?.trim();
63
+ }
64
+ </script>
@@ -0,0 +1,15 @@
1
+ import type { FieldConfig } from '#nuxt-cms';
2
+ type __VLS_Props = {
3
+ field: FieldConfig;
4
+ };
5
+ type __VLS_ModelProps = {
6
+ modelValue: Record<string, string> | null;
7
+ };
8
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
10
+ "update:modelValue": (value: Record<string, string> | null) => any;
11
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
12
+ "onUpdate:modelValue"?: ((value: Record<string, string> | null) => any) | undefined;
13
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
+ declare const _default: typeof __VLS_export;
15
+ export default _default;
@@ -0,0 +1,4 @@
1
+ import type { CmsResult, CmsVariables } from '#cms-graphql';
2
+ import type { AsyncData } from 'nuxt/app';
3
+ export declare function $cmsQuery<const Q extends string>(query: Q, variables?: CmsVariables<Q>): Promise<CmsResult<Q>>;
4
+ export declare function useCms<const Q extends string>(query: Q, variables?: CmsVariables<Q>): AsyncData<CmsResult<Q> | undefined, Error | undefined>;
@@ -0,0 +1,18 @@
1
+ import { useAsyncData } from "#imports";
2
+ const ENDPOINT = "/api/cms/graphql";
3
+ export async function $cmsQuery(query, variables) {
4
+ const res = await $fetch(ENDPOINT, {
5
+ method: "POST",
6
+ body: { query, variables }
7
+ });
8
+ if (res.errors?.length) {
9
+ throw new Error(res.errors.map((e) => e.message).join("; "));
10
+ }
11
+ return res.data;
12
+ }
13
+ export function useCms(query, variables) {
14
+ return useAsyncData(
15
+ `cms-gql:${query}:${JSON.stringify(variables ?? {})}`,
16
+ () => $cmsQuery(query, variables)
17
+ );
18
+ }
@@ -0,0 +1,5 @@
1
+ import type { CmsI18n } from '../../shared/index.js';
2
+ export declare function useCmsRuntime(): {
3
+ mediaBaseUrl: string;
4
+ i18n: CmsI18n;
5
+ };
@@ -0,0 +1,4 @@
1
+ import { useRuntimeConfig } from "#imports";
2
+ export function useCmsRuntime() {
3
+ return useRuntimeConfig().public.cms;
4
+ }
@@ -0,0 +1,93 @@
1
+ {
2
+ "cms": {
3
+ "sidebar": {
4
+ "tagline": "content studio",
5
+ "collections": "Collections",
6
+ "singles": "Singles",
7
+ "library": "Library",
8
+ "media": "Media",
9
+ "signOut": "Sign out"
10
+ },
11
+ "login": {
12
+ "title": "Welcome back",
13
+ "subtitle": "Sign in to manage your content.",
14
+ "email": "Email",
15
+ "password": "Password",
16
+ "submit": "Sign in",
17
+ "invalid": "Invalid email or password.",
18
+ "failed": "Login failed. Please try again."
19
+ },
20
+ "collection": {
21
+ "kicker": "collection · {count} entry | collection · {count} entries",
22
+ "kickerSingle": "single document",
23
+ "new": "New entry",
24
+ "edit": "Edit entry",
25
+ "back": "Back",
26
+ "emptyTitle": "Nothing here yet",
27
+ "emptyBody": "Entries you create will show up here.",
28
+ "deleteConfirm": "Delete this row?",
29
+ "status": "Status",
30
+ "columns": "Columns"
31
+ },
32
+ "entry": {
33
+ "unsavedChanges": "You have unsaved changes. Leave anyway?"
34
+ },
35
+ "status": {
36
+ "published": "Published",
37
+ "draft": "Draft",
38
+ "publish": "Publish",
39
+ "unpublish": "Make draft"
40
+ },
41
+ "media": {
42
+ "kicker": "media library",
43
+ "title": "Media",
44
+ "emptyTitle": "No media yet",
45
+ "emptyBody": "Files you upload will show up here.",
46
+ "notConfiguredTitle": "Media storage not configured",
47
+ "notConfiguredBody": "Set cms.media in nuxt.config to enable uploads.",
48
+ "dropHint": "Drop files here or click to browse",
49
+ "dropHintSingle": "Drop a file here or click to browse",
50
+ "uploading": "Uploading {done} of {total}…",
51
+ "uploadFailed": "{count} upload failed | {count} uploads failed",
52
+ "tooLarge": "File too large (max 10 MB): {name}",
53
+ "filter": {
54
+ "all": "All",
55
+ "image": "Images",
56
+ "video": "Videos",
57
+ "file": "Files"
58
+ },
59
+ "loadError": "Could not load media",
60
+ "retry": "Retry",
61
+ "copy": "Copy URL",
62
+ "copied": "Copied",
63
+ "copyError": "Copy failed",
64
+ "delete": "Delete",
65
+ "deleteConfirm": "Delete this file? Entries referencing it will keep a broken link.",
66
+ "deleteFailed": "Delete failed",
67
+ "edit": "Edit details",
68
+ "alt": "Alt text",
69
+ "folder": "Folder"
70
+ },
71
+ "toast": {
72
+ "saved": "Saved",
73
+ "saveFailed": "Save failed",
74
+ "loadFailed": "Loading failed"
75
+ },
76
+ "form": {
77
+ "save": "Save",
78
+ "mediaChoose": "Choose from the gallery",
79
+ "mediaChange": "Change",
80
+ "mediaRemove": "Remove",
81
+ "invalidJson": "Invalid JSON",
82
+ "addBlock": "Add block"
83
+ },
84
+ "validation": {
85
+ "required": "Required field",
86
+ "invalidDate": "Invalid date (yyyy-mm-dd)",
87
+ "invalidEmail": "Invalid email",
88
+ "invalidSlug": "Invalid slug (lowercase letters, numbers, dashes)",
89
+ "unknownLocale": "Unknown locale key",
90
+ "requiredLocale": "Required field (locale \"{locale}\")"
91
+ }
92
+ }
93
+ }
@@ -0,0 +1,93 @@
1
+ {
2
+ "cms": {
3
+ "sidebar": {
4
+ "tagline": "content studio",
5
+ "collections": "Collezioni",
6
+ "singles": "Singoli",
7
+ "library": "Libreria",
8
+ "media": "Media",
9
+ "signOut": "Esci"
10
+ },
11
+ "login": {
12
+ "title": "Bentornato",
13
+ "subtitle": "Accedi per gestire i tuoi contenuti.",
14
+ "email": "Email",
15
+ "password": "Password",
16
+ "submit": "Accedi",
17
+ "invalid": "Email o password non validi.",
18
+ "failed": "Accesso non riuscito. Riprova."
19
+ },
20
+ "collection": {
21
+ "kicker": "collezione · {count} voce | collezione · {count} voci",
22
+ "kickerSingle": "documento singolo",
23
+ "new": "Nuova voce",
24
+ "edit": "Modifica voce",
25
+ "back": "Indietro",
26
+ "emptyTitle": "Ancora niente qui",
27
+ "emptyBody": "Le voci che crei appariranno qui.",
28
+ "deleteConfirm": "Eliminare questa voce?",
29
+ "status": "Stato",
30
+ "columns": "Colonne"
31
+ },
32
+ "entry": {
33
+ "unsavedChanges": "Ci sono modifiche non salvate. Uscire comunque?"
34
+ },
35
+ "status": {
36
+ "published": "Pubblicato",
37
+ "draft": "Bozza",
38
+ "publish": "Pubblica",
39
+ "unpublish": "Riporta in bozza"
40
+ },
41
+ "media": {
42
+ "kicker": "libreria media",
43
+ "title": "Media",
44
+ "emptyTitle": "Ancora nessun media",
45
+ "emptyBody": "I file che carichi appariranno qui.",
46
+ "notConfiguredTitle": "Storage media non configurato",
47
+ "notConfiguredBody": "Imposta cms.media in nuxt.config per abilitare i caricamenti.",
48
+ "dropHint": "Trascina i file qui o clicca per sfogliare",
49
+ "dropHintSingle": "Trascina un file qui o clicca per sfogliare",
50
+ "uploading": "Caricamento {done} di {total}…",
51
+ "uploadFailed": "{count} caricamento non riuscito | {count} caricamenti non riusciti",
52
+ "tooLarge": "File troppo grande (max 10 MB): {name}",
53
+ "filter": {
54
+ "all": "Tutti",
55
+ "image": "Immagini",
56
+ "video": "Video",
57
+ "file": "File"
58
+ },
59
+ "loadError": "Impossibile caricare i media",
60
+ "retry": "Riprova",
61
+ "copy": "Copia URL",
62
+ "copied": "Copiato",
63
+ "copyError": "Copia non riuscita",
64
+ "delete": "Elimina",
65
+ "deleteConfirm": "Eliminare questo file? Le voci che lo usano manterranno un riferimento rotto.",
66
+ "deleteFailed": "Eliminazione non riuscita",
67
+ "edit": "Modifica dettagli",
68
+ "alt": "Testo alternativo",
69
+ "folder": "Cartella"
70
+ },
71
+ "toast": {
72
+ "saved": "Salvato",
73
+ "saveFailed": "Salvataggio non riuscito",
74
+ "loadFailed": "Caricamento non riuscito"
75
+ },
76
+ "form": {
77
+ "save": "Salva",
78
+ "mediaChoose": "Scegli dalla galleria",
79
+ "mediaChange": "Cambia",
80
+ "mediaRemove": "Rimuovi",
81
+ "invalidJson": "JSON non valido",
82
+ "addBlock": "Aggiungi blocco"
83
+ },
84
+ "validation": {
85
+ "required": "Campo obbligatorio",
86
+ "invalidDate": "Data non valida (aaaa-mm-gg)",
87
+ "invalidEmail": "Email non valida",
88
+ "invalidSlug": "Slug non valido (lettere minuscole, numeri, trattini)",
89
+ "unknownLocale": "Chiave di lingua sconosciuta",
90
+ "requiredLocale": "Campo obbligatorio (lingua \"{locale}\")"
91
+ }
92
+ }
93
+ }
@@ -0,0 +1,13 @@
1
+ declare var __VLS_19: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_19) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };
@@ -0,0 +1,97 @@
1
+ <template>
2
+ <div class="cms-scope cms-canvas cms-grain min-h-screen">
3
+ <div class="flex min-h-screen">
4
+ <aside
5
+ class="border-(--cms-line) sticky top-0 flex h-screen w-64 shrink-0 flex-col gap-7 border-r px-4 py-7"
6
+ >
7
+ <div class="px-3">
8
+ <div class="cms-display text-(--ui-text-highlighted) text-[22px] font-semibold">
9
+ nuxt<span class="text-(--cms-fern)">·</span>cms
10
+ </div>
11
+ <div class="cms-kicker mt-1.5">
12
+ {{ t("cms.sidebar.tagline") }}
13
+ </div>
14
+ </div>
15
+
16
+ <nav class="flex flex-1 flex-col gap-6 overflow-y-auto">
17
+ <div v-for="group in groups" :key="group.title" class="flex flex-col gap-1">
18
+ <div class="cms-kicker mb-2 px-3">
19
+ {{ group.title }}
20
+ </div>
21
+ <NuxtLink
22
+ v-for="link in group.links"
23
+ :key="link.name"
24
+ :to="link.to"
25
+ class="cms-navlink"
26
+ :class="{ 'is-active': route.path === link.to }"
27
+ >
28
+ <UIcon
29
+ :name="group.icon"
30
+ class="cms-navlink-icon text-(--ui-text-dimmed) size-4 shrink-0"
31
+ />
32
+ <span class="truncate">{{ link.label }}</span>
33
+ </NuxtLink>
34
+ </div>
35
+ </nav>
36
+
37
+ <div class="border-(--cms-line) flex flex-col gap-3 border-t px-3 pt-5">
38
+ <div class="text-(--ui-text-muted) truncate text-[11px] font-medium">
39
+ {{ user?.email }}
40
+ </div>
41
+ <UButton
42
+ :label="t('cms.sidebar.signOut')"
43
+ icon="i-lucide-log-out"
44
+ variant="ghost"
45
+ color="neutral"
46
+ size="xs"
47
+ class="-ml-2 justify-start rounded-full"
48
+ @click="logout"
49
+ />
50
+ </div>
51
+ </aside>
52
+
53
+ <main class="min-w-0 flex-1 px-10 py-10">
54
+ <div class="mx-auto w-full max-w-5xl">
55
+ <slot />
56
+ </div>
57
+ </main>
58
+ </div>
59
+ </div>
60
+ </template>
61
+
62
+ <script setup>
63
+ import { computed, navigateTo, useI18n, useRoute, useUserSession } from "#imports";
64
+ import cmsConfig from "#cms-config";
65
+ const route = useRoute();
66
+ const { user, clear } = useUserSession();
67
+ const { t } = useI18n();
68
+ const links = Object.entries(cmsConfig).map(([name, entry]) => ({
69
+ name,
70
+ label: entry.label,
71
+ kind: entry.kind,
72
+ to: `/cms/${name}`
73
+ }));
74
+ const groups = computed(
75
+ () => [
76
+ {
77
+ title: t("cms.sidebar.collections"),
78
+ icon: "i-lucide-layers",
79
+ links: links.filter((l) => l.kind === "collection")
80
+ },
81
+ {
82
+ title: t("cms.sidebar.singles"),
83
+ icon: "i-lucide-file-text",
84
+ links: links.filter((l) => l.kind === "single")
85
+ },
86
+ {
87
+ title: t("cms.sidebar.library"),
88
+ icon: "i-lucide-image",
89
+ links: [{ name: "media", label: t("cms.sidebar.media"), kind: "media", to: "/cms/media" }]
90
+ }
91
+ ].filter((g) => g.links.length)
92
+ );
93
+ async function logout() {
94
+ await clear();
95
+ await navigateTo("/cms/login");
96
+ }
97
+ </script>
@@ -0,0 +1,13 @@
1
+ declare var __VLS_19: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_19) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };