@hywax/cms 0.0.5 → 0.0.7

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 (70) hide show
  1. package/.nuxt/cms/editor/callout.ts +21 -0
  2. package/.nuxt/cms/editor/index.ts +2 -0
  3. package/.nuxt/cms/editor/uplora-image.ts +5 -0
  4. package/.nuxt/cms/editor-content-full.ts +6 -0
  5. package/.nuxt/cms/editor-content-light.ts +6 -0
  6. package/.nuxt/cms/index.ts +3 -0
  7. package/.nuxt/cms/input-uplora-image.ts +15 -0
  8. package/cli/templates.mjs +7 -7
  9. package/dist/module.d.mts +1 -0
  10. package/dist/module.json +1 -1
  11. package/dist/module.mjs +193 -56
  12. package/dist/runtime/components/ButtonDelete.vue +13 -5
  13. package/dist/runtime/components/ButtonDelete.vue.d.ts +9 -2
  14. package/dist/runtime/components/EditorContentFull.vue +53 -0
  15. package/dist/runtime/components/EditorContentFull.vue.d.ts +20 -0
  16. package/dist/runtime/components/EditorContentLight.vue +46 -0
  17. package/dist/runtime/components/EditorContentLight.vue.d.ts +20 -0
  18. package/dist/runtime/components/InputSlug.vue +6 -2
  19. package/dist/runtime/components/InputSlug.vue.d.ts +3 -1
  20. package/dist/runtime/components/InputUploraImage.vue +131 -0
  21. package/dist/runtime/components/InputUploraImage.vue.d.ts +35 -0
  22. package/dist/runtime/components/ModalConfirm.vue +14 -8
  23. package/dist/runtime/components/ModalConfirm.vue.d.ts +8 -2
  24. package/dist/runtime/components/TablePanel.vue +2 -2
  25. package/dist/runtime/components/TablePanelColumnSorting.vue +1 -1
  26. package/dist/runtime/components/UploraImage.vue +2 -2
  27. package/dist/runtime/components/UploraImage.vue.d.ts +1 -1
  28. package/dist/runtime/components/prose/UploraImage.vue +1 -1
  29. package/dist/runtime/composables/useAdmin.d.ts +6 -0
  30. package/dist/runtime/composables/useAdmin.js +14 -0
  31. package/dist/runtime/composables/useAsyncHandler.d.ts +12 -0
  32. package/dist/runtime/composables/useAsyncHandler.js +30 -0
  33. package/dist/runtime/composables/useDeleteConfirm.js +1 -3
  34. package/dist/runtime/composables/useUplora.d.ts +20 -0
  35. package/dist/runtime/composables/useUplora.js +52 -0
  36. package/dist/runtime/editor/components/BlockMenu.vue +43 -0
  37. package/dist/runtime/editor/components/BlockMenu.vue.d.ts +2 -0
  38. package/dist/runtime/editor/components/SlashCommand.vue +92 -0
  39. package/dist/runtime/editor/components/SlashCommand.vue.d.ts +7 -0
  40. package/dist/runtime/editor/components/TooltipLink.vue +81 -0
  41. package/dist/runtime/editor/components/TooltipLink.vue.d.ts +9 -0
  42. package/dist/runtime/editor/components/TooltipMenu.vue +75 -0
  43. package/dist/runtime/editor/components/TooltipMenu.vue.d.ts +9 -0
  44. package/dist/runtime/editor/extensions/callout/CalloutView.vue +85 -0
  45. package/dist/runtime/editor/extensions/callout/CalloutView.vue.d.ts +7 -0
  46. package/dist/runtime/editor/extensions/callout/extension.d.ts +13 -0
  47. package/dist/runtime/editor/extensions/callout/extension.js +48 -0
  48. package/dist/runtime/editor/extensions/callout/index.d.ts +2 -0
  49. package/dist/runtime/editor/extensions/callout/index.js +2 -0
  50. package/dist/runtime/editor/extensions/callout/types.d.ts +3 -0
  51. package/dist/runtime/editor/extensions/callout/types.js +0 -0
  52. package/dist/runtime/editor/extensions/index.d.ts +26 -0
  53. package/dist/runtime/editor/extensions/index.js +85 -0
  54. package/dist/runtime/editor/extensions/uplora-image/UploraImageView.vue +29 -0
  55. package/dist/runtime/editor/extensions/uplora-image/UploraImageView.vue.d.ts +7 -0
  56. package/dist/runtime/editor/extensions/uplora-image/extension.d.ts +13 -0
  57. package/dist/runtime/editor/extensions/uplora-image/extension.js +60 -0
  58. package/dist/runtime/editor/extensions/uplora-image/index.d.ts +2 -0
  59. package/dist/runtime/editor/extensions/uplora-image/index.js +2 -0
  60. package/dist/runtime/editor/extensions/uplora-image/types.d.ts +5 -0
  61. package/dist/runtime/editor/extensions/uplora-image/types.js +0 -0
  62. package/dist/runtime/index.css +1 -1
  63. package/dist/runtime/server/api/uplora/[id].delete.d.ts +2 -0
  64. package/dist/runtime/server/api/uplora/[id].delete.js +4 -0
  65. package/dist/runtime/server/api/uplora/index.post.d.ts +5 -0
  66. package/dist/runtime/server/api/uplora/index.post.js +8 -0
  67. package/dist/runtime/types/index.d.ts +4 -0
  68. package/dist/runtime/types/index.js +3 -0
  69. package/dist/runtime/types/tv.d.ts +11 -5
  70. package/package.json +1 -1
@@ -0,0 +1,21 @@
1
+ export default {
2
+ "slots": {
3
+ "root": "px-4 py-3 text-sm/6 rounded-md border flex gap-2"
4
+ },
5
+ "variants": {
6
+ "type": {
7
+ "caution": {
8
+ "root": "border border-error/25 bg-error/10 text-error-600 dark:text-error-300"
9
+ },
10
+ "note": {
11
+ "root": "border border-info/25 bg-info/10 text-info-600 dark:text-info-300"
12
+ },
13
+ "warning": {
14
+ "root": "border border-warning/25 bg-warning/10 text-warning-600 dark:text-warning-300"
15
+ },
16
+ "tip": {
17
+ "root": "border border-success/25 bg-success/10 text-success-600 dark:text-success-300"
18
+ }
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,2 @@
1
+ export { default as callout } from './callout'
2
+ export { default as uploraImage } from './uplora-image'
@@ -0,0 +1,5 @@
1
+ export default {
2
+ "slots": {
3
+ "root": ""
4
+ }
5
+ }
@@ -0,0 +1,6 @@
1
+ export default {
2
+ "slots": {
3
+ "root": "h-full w-full relative",
4
+ "editor": "min-h-full w-full focus:outline-none editor-content-full relative"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ export default {
2
+ "slots": {
3
+ "root": "relative",
4
+ "editor": "w-full focus:outline-none editor-content-light relative"
5
+ }
6
+ }
@@ -2,11 +2,14 @@ export { default as autocompleteSelect } from './autocomplete-select'
2
2
  export { default as buttonClear } from './button-clear'
3
3
  export { default as buttonCopy } from './button-copy'
4
4
  export { default as buttonDelete } from './button-delete'
5
+ export { default as editorContentFull } from './editor-content-full'
6
+ export { default as editorContentLight } from './editor-content-light'
5
7
  export { default as formPanel } from './form-panel'
6
8
  export { default as formPanelAsideSection } from './form-panel-aside-section'
7
9
  export { default as formPanelSection } from './form-panel-section'
8
10
  export { default as inputSeo } from './input-seo'
9
11
  export { default as inputSlug } from './input-slug'
12
+ export { default as inputUploraImage } from './input-uplora-image'
10
13
  export { default as modalConfirm } from './modal-confirm'
11
14
  export { default as tableCellPreview } from './table-cell-preview'
12
15
  export { default as tableCellSeo } from './table-cell-seo'
@@ -0,0 +1,15 @@
1
+ export default {
2
+ "slots": {
3
+ "root": "relative w-full rounded-md overflow-hidden border border-default aspect-3/2 text-sm",
4
+ "image": "",
5
+ "imageActions": "absolute top-4 right-4 flex gap-2",
6
+ "uploader": "flex flex-col items-center justify-center w-full h-full p-4",
7
+ "uploaderPendingIcon": "size-6 animate-spin",
8
+ "uploaderIdleButton": "cursor-pointer bg-muted/50 border border-default border-dashed rounded-md h-full w-full",
9
+ "uploaderIdleIcon": "size-10 text-primary",
10
+ "uploaderIdleText": "mt-2 font-medium",
11
+ "uploaderIdleExtensions": "mt-2 text-xs text-muted uppercase",
12
+ "uploaderErrorText": "text-center font-medium",
13
+ "uploaderErrorActions": "flex gap-2 mt-4"
14
+ }
15
+ }
package/cli/templates.mjs CHANGED
@@ -66,16 +66,16 @@ import { describe, expect, it } from 'vitest'
66
66
  import ${upperName} from '../../../src/runtime/components/${upperName}.vue'
67
67
  import ComponentRender from '../../component-render'
68
68
 
69
- describe('${upperName}', () => {
69
+ describe('${upperName}.vue', () => {
70
70
  it.each([
71
71
  // Props
72
- ['базовый компонент', { props: {} }],
73
- ['с алиасом', { props: { as: 'section' } }],
74
- ['с class', { props: { class: '' } }],
75
- ['с ui', { props: { ui: {} } }],
72
+ ['base component', { props: {} }],
73
+ ['with as', { props: { as: 'section' } }],
74
+ ['with class', { props: { class: '' } }],
75
+ ['with ui', { props: { ui: {} } }],
76
76
  // Slots
77
- ['с дефолтным слотом', { slots: { default: () => 'Default slot' } }]
78
- ])('рендерит %s корректно', async (nameOrHtml: string, options: { props?: ${upperName}Props, slots?: Partial<${upperName}Slots> }) => {
77
+ ['with default slot', { slots: { default: () => 'Default slot' } }],
78
+ ])('renders %s correctly', async (nameOrHtml: string, options: { props?: ${upperName}Props, slots?: Partial<${upperName}Slots> }) => {
79
79
  const html = await ComponentRender(nameOrHtml, options, ${upperName})
80
80
  expect(html).toMatchSnapshot()
81
81
  })
package/dist/module.d.mts CHANGED
@@ -35,6 +35,7 @@ declare module '@nuxt/schema' {
35
35
  fluxorUrl: string;
36
36
  version: string;
37
37
  };
38
+ uploraApiKey: string;
38
39
  }
39
40
  }
40
41
 
package/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hywax/cms",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "configKey": "cms",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.1",
package/dist/module.mjs CHANGED
@@ -1,11 +1,12 @@
1
- import { createResolver, addComponentsDir, addImportsDir, addPlugin, addImports, addServerImports, addServerImportsDir, addTypeTemplate, addTemplate, addServerTemplate, defineNuxtModule, hasNuxtModule, installModule } from '@nuxt/kit';
2
- import { defu } from 'defu';
1
+ import { createResolver, addComponentsDir, addImportsDir, addPlugin, addImports, addServerImports, addServerImportsDir, hasNuxtModule, installModule, addServerHandler, addTypeTemplate, addTemplate, addServerTemplate, defineNuxtModule } from '@nuxt/kit';
3
2
  import { fileURLToPath } from 'node:url';
4
- import { dirname } from 'pathe';
3
+ import { dirname, join } from 'pathe';
4
+ import { defu } from 'defu';
5
5
  import { snakeCase, kebabCase } from 'scule';
6
+ import { readFile, writeFile } from 'node:fs/promises';
6
7
 
7
8
  const name = "@hywax/cms";
8
- const version = "0.0.5";
9
+ const version = "0.0.7";
9
10
 
10
11
  function createContext(options, nuxt) {
11
12
  const { resolve } = createResolver(import.meta.url);
@@ -20,9 +21,7 @@ function createContext(options, nuxt) {
20
21
  };
21
22
  }
22
23
 
23
- function getDefaultCMSConfig() {
24
- return {};
25
- }
24
+ const defaultCMSConfig = {};
26
25
  const defaultModuleOptions = {
27
26
  name: "cms",
28
27
  prefix: "C",
@@ -77,6 +76,115 @@ function prepareAutoImports({ resolve, options, nuxt }) {
77
76
  addServerImportsDir(resolve("./runtime/server/utils"));
78
77
  nuxt.options.nitro.alias ||= {};
79
78
  nuxt.options.nitro.alias["#cms/http-codes"] = httpCodesPath;
79
+ nuxt.options.alias["#cms"] = resolve("./runtime");
80
+ nuxt.options.appConfig.cms = defu(nuxt.options.appConfig.cms || {}, defaultCMSConfig);
81
+ }
82
+
83
+ async function prepareGenerateEnv({ nuxt }) {
84
+ const runtimeConfig = nuxt.options.runtimeConfig;
85
+ if (nuxt.options.dev && !runtimeConfig.uploraApiKey) {
86
+ const envPath = join(nuxt.options.rootDir, ".env");
87
+ const envContent = await readFile(envPath, "utf-8").catch(() => "");
88
+ const envKey = `${runtimeConfig.nitro?.envPrefix || "NUXT_"}UPLORA_API_KEY`;
89
+ if (!envContent.includes(envKey)) {
90
+ await writeFile(
91
+ envPath,
92
+ `${envContent ? `${envContent}
93
+ ` : envContent}${envKey}=put-your-api-key-here`,
94
+ "utf-8"
95
+ );
96
+ }
97
+ }
98
+ }
99
+
100
+ async function prepareInstallModules(_context) {
101
+ if (!hasNuxtModule("@nuxt/ui-pro")) {
102
+ await installModule("@nuxt/ui-pro");
103
+ }
104
+ if (!hasNuxtModule("@nuxtjs/mdc")) {
105
+ await installModule("@nuxtjs/mdc");
106
+ }
107
+ if (!hasNuxtModule("@vueuse/nuxt")) {
108
+ await installModule("@vueuse/nuxt");
109
+ }
110
+ if (!hasNuxtModule("nuxt-auth-utils")) {
111
+ await installModule("nuxt-auth-utils");
112
+ }
113
+ }
114
+
115
+ const icons = {
116
+ edit: "lucide:pencil",
117
+ trash: "lucide:trash-2",
118
+ imageUp: "lucide:image-up",
119
+ arrowDownUp: "lucide:arrow-down-up",
120
+ sortAsc: "lucide:arrow-up-wide-narrow",
121
+ sortDesc: "lucide:arrow-down-wide-narrow",
122
+ columns: "lucide:columns-3-cog",
123
+ filter: "lucide:filter",
124
+ link: "lucide:link-2",
125
+ linkOff: "lucide:link-2-off",
126
+ ellipsisVertical: "lucide:ellipsis-vertical",
127
+ heading2: "lucide:heading-2",
128
+ heading3: "lucide:heading-3",
129
+ heading4: "lucide:heading-4",
130
+ list: "lucide:list",
131
+ listOrdered: "lucide:list-ordered",
132
+ checkSquare: "lucide:check-square",
133
+ image: "lucide:image",
134
+ quote: "lucide:quote",
135
+ code: "lucide:code",
136
+ paragraph: "lucide:paragraph",
137
+ pilcrow: "lucide:pilcrow",
138
+ info: "lucide:info",
139
+ bold: "lucide:bold",
140
+ italic: "lucide:italic",
141
+ underline: "lucide:underline",
142
+ strike: "lucide:strikethrough"
143
+ };
144
+
145
+ function prepareMergeConfigs({ nuxt, options }) {
146
+ nuxt.options.runtimeConfig.public = defu(nuxt.options.runtimeConfig.public || {}, {
147
+ fluxorUrl: options.fluxorUrl,
148
+ version: ""
149
+ });
150
+ nuxt.options.runtimeConfig.nitro ||= {};
151
+ nuxt.options.runtimeConfig.nitro.envPrefix = options.envPrefix;
152
+ nuxt.options.appConfig.ui = defu(nuxt.options.appConfig.ui || {}, {
153
+ icons
154
+ });
155
+ nuxt.options.ui = defu(nuxt.options.ui || {}, {
156
+ colorMode: true,
157
+ fonts: true
158
+ });
159
+ nuxt.options.colorMode = defu(nuxt.options.colorMode || {}, {
160
+ storageKey: `${options.name}-color-mode`
161
+ });
162
+ nuxt.options.mdc = defu(nuxt.options.mdc || {}, {
163
+ components: {
164
+ map: {
165
+ "uplora-image": "ProseUploraImage"
166
+ }
167
+ }
168
+ });
169
+ nuxt.options.auth = defu(nuxt.options.auth || {}, {
170
+ name: `${options.name}-session`
171
+ });
172
+ nuxt.options.runtimeConfig.uplora = defu(nuxt.options.runtimeConfig.uplora || {}, {
173
+ apiKey: ""
174
+ });
175
+ }
176
+
177
+ function prepareServerRoutes({ resolve }) {
178
+ addServerHandler({
179
+ route: "/api/_uplora",
180
+ method: "post",
181
+ handler: resolve("./runtime/server/api/uplora/index.post.ts")
182
+ });
183
+ addServerHandler({
184
+ route: "/api/_uplora/:id",
185
+ method: "delete",
186
+ handler: resolve("./runtime/server/api/uplora/[id].delete.ts")
187
+ });
80
188
  }
81
189
 
82
190
  const autocompleteSelect = {
@@ -103,6 +211,20 @@ const buttonDelete = {
103
211
  }
104
212
  };
105
213
 
214
+ const editorContentFull = {
215
+ slots: {
216
+ root: "h-full w-full relative",
217
+ editor: "min-h-full w-full focus:outline-none editor-content-full relative"
218
+ }
219
+ };
220
+
221
+ const editorContentLight = {
222
+ slots: {
223
+ root: "relative",
224
+ editor: "w-full focus:outline-none editor-content-light relative"
225
+ }
226
+ };
227
+
106
228
  const formPanel = {
107
229
  slots: {
108
230
  root: "",
@@ -150,6 +272,22 @@ const inputSlug = {
150
272
  }
151
273
  };
152
274
 
275
+ const inputUploraImage = {
276
+ slots: {
277
+ root: "relative w-full rounded-md overflow-hidden border border-default aspect-3/2 text-sm",
278
+ image: "",
279
+ imageActions: "absolute top-4 right-4 flex gap-2",
280
+ uploader: "flex flex-col items-center justify-center w-full h-full p-4",
281
+ uploaderPendingIcon: "size-6 animate-spin",
282
+ uploaderIdleButton: "cursor-pointer bg-muted/50 border border-default border-dashed rounded-md h-full w-full",
283
+ uploaderIdleIcon: "size-10 text-primary",
284
+ uploaderIdleText: "mt-2 font-medium",
285
+ uploaderIdleExtensions: "mt-2 text-xs text-muted uppercase",
286
+ uploaderErrorText: "text-center font-medium",
287
+ uploaderErrorActions: "flex gap-2 mt-4"
288
+ }
289
+ };
290
+
153
291
  const modalConfirm = {
154
292
  slots: {
155
293
  root: ""
@@ -205,7 +343,7 @@ const tablePanelFilters = {
205
343
  }
206
344
  };
207
345
 
208
- const uploraImage$1 = {
346
+ const uploraImage$2 = {
209
347
  slots: {
210
348
  root: "relative grid grid-cols-[100%] grid-rows-[100%] overflow-hidden",
211
349
  lqip: "bg-cover bg-no-repeat bg-center col-[1] row-[1]",
@@ -220,11 +358,14 @@ const theme = {
220
358
  buttonClear: buttonClear,
221
359
  buttonCopy: buttonCopy,
222
360
  buttonDelete: buttonDelete,
361
+ editorContentFull: editorContentFull,
362
+ editorContentLight: editorContentLight,
223
363
  formPanel: formPanel,
224
364
  formPanelAsideSection: formPanelAsideSection,
225
365
  formPanelSection: formPanelSection,
226
366
  inputSeo: inputSeo,
227
367
  inputSlug: inputSlug,
368
+ inputUploraImage: inputUploraImage,
228
369
  modalConfirm: modalConfirm,
229
370
  tableCellPreview: tableCellPreview,
230
371
  tableCellSeo: tableCellSeo,
@@ -233,6 +374,40 @@ const theme = {
233
374
  tablePanelColumnSorting: tablePanelColumnSorting,
234
375
  tablePanelColumnVisibility: tablePanelColumnVisibility,
235
376
  tablePanelFilters: tablePanelFilters,
377
+ uploraImage: uploraImage$2
378
+ };
379
+
380
+ const callout = {
381
+ slots: {
382
+ root: "px-4 py-3 text-sm/6 rounded-md border flex gap-2"
383
+ },
384
+ variants: {
385
+ type: {
386
+ caution: {
387
+ root: "border border-error/25 bg-error/10 text-error-600 dark:text-error-300"
388
+ },
389
+ note: {
390
+ root: "border border-info/25 bg-info/10 text-info-600 dark:text-info-300"
391
+ },
392
+ warning: {
393
+ root: "border border-warning/25 bg-warning/10 text-warning-600 dark:text-warning-300"
394
+ },
395
+ tip: {
396
+ root: "border border-success/25 bg-success/10 text-success-600 dark:text-success-300"
397
+ }
398
+ }
399
+ }
400
+ };
401
+
402
+ const uploraImage$1 = {
403
+ slots: {
404
+ root: ""
405
+ }
406
+ };
407
+
408
+ const themeEditor = {
409
+ __proto__: null,
410
+ callout: callout,
236
411
  uploraImage: uploraImage$1
237
412
  };
238
413
 
@@ -284,12 +459,18 @@ function getAppTemplates({ options }) {
284
459
  }
285
460
  }
286
461
  writeThemeTemplate(themeProse, "prose");
462
+ writeThemeTemplate(themeEditor, "editor");
287
463
  writeThemeTemplate(theme);
288
464
  templates.push({
289
465
  filename: `cms/prose/index.ts`,
290
466
  write: true,
291
467
  getContents: () => Object.keys(themeProse).map((component) => `export { default as ${component} } from './${kebabCase(component)}'`).join("\n")
292
468
  });
469
+ templates.push({
470
+ filename: `cms/editor/index.ts`,
471
+ write: true,
472
+ getContents: () => Object.keys(themeEditor).map((component) => `export { default as ${component} } from './${kebabCase(component)}'`).join("\n")
473
+ });
293
474
  templates.push({
294
475
  filename: "cms.css",
295
476
  write: true,
@@ -359,14 +540,6 @@ function prepareTemplates(context) {
359
540
  }
360
541
  }
361
542
 
362
- const icons = {
363
- sortAsc: "lucide:arrow-up-wide-narrow",
364
- sortDesc: "lucide:arrow-down-wide-narrow",
365
- columns: "lucide:columns-3-cog",
366
- filter: "lucide:filter",
367
- link: "lucide:link-2"
368
- };
369
-
370
543
  const module = defineNuxtModule({
371
544
  meta: {
372
545
  name,
@@ -376,48 +549,12 @@ const module = defineNuxtModule({
376
549
  defaults: defaultModuleOptions,
377
550
  async setup(options, nuxt) {
378
551
  const context = createContext(options, nuxt);
379
- nuxt.options.runtimeConfig.public = defu(nuxt.options.runtimeConfig.public || {}, {
380
- fluxorUrl: options.fluxorUrl,
381
- version: ""
382
- });
383
- nuxt.options.runtimeConfig.nitro ||= {};
384
- nuxt.options.runtimeConfig.nitro.envPrefix = options.envPrefix;
385
- nuxt.options.appConfig.ui = defu(nuxt.options.appConfig.ui || {}, {
386
- icons
387
- });
388
- nuxt.options.ui = defu(nuxt.options.ui || {}, {
389
- colorMode: true,
390
- fonts: true
391
- });
392
- nuxt.options.colorMode = defu(nuxt.options.colorMode || {}, {
393
- storageKey: `${options.name}-color-mode`
394
- });
395
- nuxt.options.mdc = defu(nuxt.options.mdc || {}, {
396
- components: {
397
- map: {
398
- "uplora-image": "ProseUploraImage"
399
- }
400
- }
401
- });
402
- nuxt.options.auth = defu(nuxt.options.auth || {}, {
403
- name: `${options.name}-session`
404
- });
405
- if (!hasNuxtModule("@nuxt/ui-pro")) {
406
- await installModule("@nuxt/ui-pro");
407
- }
408
- if (!hasNuxtModule("@nuxtjs/mdc")) {
409
- await installModule("@nuxtjs/mdc");
410
- }
411
- if (!hasNuxtModule("@vueuse/nuxt")) {
412
- await installModule("@vueuse/nuxt");
413
- }
414
- if (!hasNuxtModule("nuxt-auth-utils")) {
415
- await installModule("nuxt-auth-utils");
416
- }
417
- nuxt.options.alias["#cms"] = context.resolve("./runtime");
418
- nuxt.options.appConfig.cms = defu(nuxt.options.appConfig.cms || {}, getDefaultCMSConfig());
552
+ prepareMergeConfigs(context);
553
+ await prepareInstallModules();
419
554
  prepareAutoImports(context);
420
555
  prepareTemplates(context);
556
+ prepareServerRoutes(context);
557
+ await prepareGenerateEnv(context);
421
558
  }
422
559
  });
423
560
 
@@ -7,10 +7,12 @@
7
7
  disable-closing-trigger
8
8
  >
9
9
  <UButton
10
- v-bind="buttonProps"
11
- color="error"
12
- auto-loading
10
+ :color="color"
11
+ :variant="variant"
12
+ :size="size"
13
+ :icon="icon"
13
14
  :class="ui.root({ class: [props.ui?.root, props.class] })"
15
+ auto-loading
14
16
  @click="handleDeleteClick()"
15
17
  />
16
18
  </UTooltip>
@@ -26,11 +28,15 @@ import ModalConfirm from "./ModalConfirm.vue";
26
28
  <script setup>
27
29
  const props = defineProps({
28
30
  modalProps: { type: Object, required: false },
29
- buttonProps: { type: Object, required: false },
30
31
  confirmText: { type: String, required: false },
32
+ confirmLabel: { type: String, required: false, default: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C" },
31
33
  title: { type: String, required: false },
32
34
  message: { type: String, required: false },
33
35
  tooltipText: { type: String, required: false },
36
+ color: { type: null, required: false, default: "error" },
37
+ variant: { type: null, required: false },
38
+ icon: { type: null, required: false },
39
+ size: { type: null, required: false },
34
40
  class: { type: null, required: false },
35
41
  ui: { type: null, required: false },
36
42
  onConfirm: { type: Function, required: false }
@@ -43,7 +49,9 @@ function handleDeleteClick() {
43
49
  ...props.modalProps,
44
50
  title: props.title ?? "\u0423\u0434\u0430\u043B\u0435\u043D\u0438\u0435",
45
51
  message: props.message ?? "\u0412\u044B \u0434\u0435\u0438\u0306\u0441\u0442\u0432\u0438\u0442\u0435\u043B\u044C\u043D\u043E \u0445\u043E\u0442\u0438\u0442\u0435 \u0443\u0434\u0430\u043B\u0438\u0442\u044C?",
46
- confirmButton: { color: "error", label: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C" },
52
+ color: "error",
53
+ variant: props.variant,
54
+ confirmLabel: props.confirmLabel,
47
55
  confirmText: props.confirmText,
48
56
  onConfirm: props.onConfirm
49
57
  });
@@ -8,11 +8,15 @@ import ModalConfirm from './ModalConfirm.vue';
8
8
  type ButtonDelete = ComponentConfig<typeof theme, AppConfig, 'buttonDelete'>;
9
9
  export interface ButtonDeleteProps extends Pick<ModalConfirmProps, 'onConfirm'> {
10
10
  modalProps?: Omit<ModalProps & ComponentProps<typeof ModalConfirm>, 'title' | 'message' | 'onConfirm' | 'confirmButton'>;
11
- buttonProps?: Omit<ButtonProps, 'color'>;
12
11
  confirmText?: string;
12
+ confirmLabel?: string;
13
13
  title?: string;
14
14
  message?: string;
15
15
  tooltipText?: string;
16
+ color?: ButtonProps['color'];
17
+ variant?: ButtonProps['variant'];
18
+ icon?: ButtonProps['icon'];
19
+ size?: ButtonProps['size'];
16
20
  class?: any;
17
21
  ui?: ButtonDelete['slots'];
18
22
  }
@@ -24,5 +28,8 @@ declare const _default: import("vue").DefineComponent<ButtonDeleteProps, {}, {},
24
28
  }, string, import("vue").PublicProps, Readonly<ButtonDeleteProps> & Readonly<{
25
29
  onClose?: (() => any) | undefined;
26
30
  onConfirm?: (() => any) | undefined;
27
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
31
+ }>, {
32
+ color: "primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral";
33
+ confirmLabel: string;
34
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
28
35
  export default _default;
@@ -0,0 +1,53 @@
1
+ <template>
2
+ <Primitive :as="as" :class="ui.root({ class: [props.ui?.root, props.class] })">
3
+ <ProseKit :editor="editor">
4
+ <div ref="editorRef" :class="ui.editor({ class: [props.ui?.editor, 'pl-14'] })" />
5
+
6
+ <BlockMenu />
7
+ <SlashCommand />
8
+ <TooltipMenu />
9
+ </ProseKit>
10
+ </Primitive>
11
+ </template>
12
+
13
+ <script>
14
+ import theme from "#build/cms/editor-content-full";
15
+ import { computed, useAppConfig, useTemplateRef, watchPostEffect } from "#imports";
16
+ import { createEditor } from "prosekit/core";
17
+ import { ProseKit, useDocChange } from "prosekit/vue";
18
+ import { Primitive } from "reka-ui";
19
+ import BlockMenu from "../editor/components/BlockMenu.vue";
20
+ import SlashCommand from "../editor/components/SlashCommand.vue";
21
+ import TooltipMenu from "../editor/components/TooltipMenu.vue";
22
+ import { defineFullExtension } from "../editor/extensions";
23
+ import { docToMarkdown, markdownToDoc } from "../editor/markdown";
24
+ import { tv } from "../utils/tv";
25
+ import "prosekit/basic/style.css";
26
+ </script>
27
+
28
+ <script setup>
29
+ const props = defineProps({
30
+ as: { type: null, required: false },
31
+ class: { type: null, required: false },
32
+ ui: { type: null, required: false }
33
+ });
34
+ const modelValue = defineModel({ type: String, ...{ default: "" } });
35
+ const appConfig = useAppConfig();
36
+ const editor = createEditor({
37
+ extension: defineFullExtension(),
38
+ defaultContent: await markdownToDoc(modelValue.value)
39
+ });
40
+ const editorRef = useTemplateRef("editorRef");
41
+ useDocChange(async () => {
42
+ modelValue.value = await docToMarkdown(editor.getDocJSON());
43
+ }, { editor });
44
+ watchPostEffect((onCleanup) => {
45
+ editor.mount(editorRef.value);
46
+ onCleanup(() => editor.unmount());
47
+ });
48
+ const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.editorContentFull || {} })());
49
+ </script>
50
+
51
+ <style scoped>
52
+ ::v-deep(.editor-content-full){line-height:calc(var(--spacing)*7)}::v-deep(.editor-content-full) [data-placeholder]:before{color:var(--ui-text-dimmed);content:attr(data-placeholder);height:0;pointer-events:none;position:absolute}::v-deep(.editor-content-full) h2,::v-deep(.editor-content-full) h3,::v-deep(.editor-content-full) h4{font-weight:600;margin-block:calc(var(--spacing)*5)}::v-deep(.editor-content-full) h2{font-size:var(--text-2xl)}::v-deep(.editor-content-full) h3{font-size:var(--text-xl)}::v-deep(.editor-content-full) h4{font-size:var(--text-lg)}::v-deep(.editor-content-full) blockquote{border-left:4px solid var(--ui-border-accented);margin-block:calc(var(--spacing)*4);padding-inline-start:calc(var(--spacing)*4)}::v-deep(.editor-content-full) [data-node-view-root=true],::v-deep(.editor-content-full)>p{margin-block:calc(var(--spacing)*5)}::v-deep(.editor-content-full) hr{border-block-start:1px solid var(--ui-border);margin-block:calc(var(--spacing)*5)}::v-deep(.editor-content-full) pre{background-color:var(--ui-bg-muted);border:1px solid var(--ui-border-muted);border-radius:calc(var(--ui-radius)*1.5);font-family:var(--font-mono);font-size:var(--text-sm);line-height:calc(var(--spacing)*6);overflow-x:auto;padding-block:calc(var(--spacing)*3);padding-inline:calc(var(--spacing)*4)}::v-deep(.editor-content-full) p>code{background-color:var(--ui-bg-muted);border:1px solid var(--ui-border-muted);border-radius:calc(var(--ui-radius)*1.5);font-family:var(--font-mono);font-size:var(--text-sm);padding-inline:var(--spacing)}::v-deep(.editor-content-full) a{color:var(--ui-primary);cursor:pointer;text-decoration:underline;-webkit-user-select:all;-moz-user-select:all;user-select:all}
53
+ </style>
@@ -0,0 +1,20 @@
1
+ import type { AppConfig } from '@nuxt/schema';
2
+ import type { ComponentConfig } from '../types';
3
+ import theme from '#build/cms/editor-content-full';
4
+ import 'prosekit/basic/style.css';
5
+ type EditorContentFull = ComponentConfig<typeof theme, AppConfig, 'editorContentFull'>;
6
+ export interface EditorContentFullProps {
7
+ as?: any;
8
+ class?: any;
9
+ ui?: EditorContentFull['slots'];
10
+ }
11
+ declare const _default: import("vue").DefineComponent<EditorContentFullProps & {
12
+ modelValue?: string;
13
+ }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
14
+ "update:modelValue": (value: string) => any;
15
+ }, string, import("vue").PublicProps, Readonly<EditorContentFullProps & {
16
+ modelValue?: string;
17
+ }> & Readonly<{
18
+ "onUpdate:modelValue"?: ((value: string) => any) | undefined;
19
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
20
+ export default _default;
@@ -0,0 +1,46 @@
1
+ <template>
2
+ <Primitive :as="as" :class="ui.root({ class: [props.ui?.root, props.class] })">
3
+ <ProseKit :editor="editor">
4
+ <div ref="editorRef" :class="ui.editor({ class: props.ui?.editor })" />
5
+ </ProseKit>
6
+ </Primitive>
7
+ </template>
8
+
9
+ <script>
10
+ import theme from "#build/cms/editor-content-light";
11
+ import { computed, useAppConfig, useTemplateRef, watchPostEffect } from "#imports";
12
+ import { createEditor } from "prosekit/core";
13
+ import { ProseKit, useDocChange } from "prosekit/vue";
14
+ import { Primitive } from "reka-ui";
15
+ import { defineLightExtension } from "../editor/extensions";
16
+ import { docToMarkdown, markdownToDoc } from "../editor/markdown";
17
+ import { tv } from "../utils/tv";
18
+ import "prosekit/basic/style.css";
19
+ </script>
20
+
21
+ <script setup>
22
+ const props = defineProps({
23
+ as: { type: null, required: false },
24
+ class: { type: null, required: false },
25
+ ui: { type: null, required: false }
26
+ });
27
+ const modelValue = defineModel({ type: String, ...{ default: "" } });
28
+ const appConfig = useAppConfig();
29
+ const editor = createEditor({
30
+ extension: defineLightExtension(),
31
+ defaultContent: await markdownToDoc(modelValue.value)
32
+ });
33
+ const editorRef = useTemplateRef("editorRef");
34
+ useDocChange(async () => {
35
+ modelValue.value = await docToMarkdown(editor.getDocJSON());
36
+ }, { editor });
37
+ watchPostEffect((onCleanup) => {
38
+ editor.mount(editorRef.value);
39
+ onCleanup(() => editor.unmount());
40
+ });
41
+ const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.editorContentLight || {} })());
42
+ </script>
43
+
44
+ <style scoped>
45
+ ::v-deep(.editor-content-light){line-height:calc(var(--spacing)*7)}::v-deep(.editor-content-light) [data-placeholder]:before{color:var(--ui-text-dimmed);content:attr(data-placeholder);height:0;pointer-events:none;position:absolute}::v-deep(.editor-content-light) blockquote{border-left:4px solid var(--ui-border-accented);margin-block:calc(var(--spacing)*4);padding-inline-start:calc(var(--spacing)*4)}::v-deep(.editor-content-light) [data-node-view-root=true],::v-deep(.editor-content-light)>p{margin-block:calc(var(--spacing)*5)}::v-deep(.editor-content-light) pre{background-color:var(--ui-bg-muted);border:1px solid var(--ui-border-muted);border-radius:calc(var(--ui-radius)*1.5);font-family:var(--font-mono);font-size:var(--text-sm);line-height:calc(var(--spacing)*6);overflow-x:auto;padding-block:calc(var(--spacing)*3);padding-inline:calc(var(--spacing)*4)}::v-deep(.editor-content-light) p>code{background-color:var(--ui-bg-muted);border:1px solid var(--ui-border-muted);border-radius:calc(var(--ui-radius)*1.5);font-family:var(--font-mono);font-size:var(--text-sm);padding-inline:var(--spacing)}::v-deep(.editor-content-light) a{color:var(--ui-primary);cursor:pointer;text-decoration:underline;-webkit-user-select:all;-moz-user-select:all;user-select:all}
46
+ </style>