@hywax/cms 0.0.6 → 0.0.8

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 (58) hide show
  1. package/.nuxt/cms/editor/uplora-image.ts +1 -1
  2. package/.nuxt/cms/editor-content-full.ts +6 -0
  3. package/.nuxt/cms/editor-content-light.ts +6 -0
  4. package/.nuxt/cms/index.ts +3 -0
  5. package/.nuxt/cms/input-uplora-image.ts +15 -0
  6. package/cli/templates.mjs +6 -6
  7. package/dist/module.d.mts +7 -0
  8. package/dist/module.json +1 -1
  9. package/dist/module.mjs +141 -54
  10. package/dist/runtime/components/AutocompleteSelect.vue +1 -2
  11. package/dist/runtime/components/ButtonDelete.vue +3 -2
  12. package/dist/runtime/components/ButtonDelete.vue.d.ts +1 -0
  13. package/dist/runtime/components/EditorContentFull.vue +53 -0
  14. package/dist/runtime/components/EditorContentFull.vue.d.ts +20 -0
  15. package/dist/runtime/components/EditorContentLight.vue +46 -0
  16. package/dist/runtime/components/EditorContentLight.vue.d.ts +20 -0
  17. package/dist/runtime/components/InputUploraImage.vue +136 -0
  18. package/dist/runtime/components/InputUploraImage.vue.d.ts +35 -0
  19. package/dist/runtime/components/ModalConfirm.vue +2 -1
  20. package/dist/runtime/components/TablePanel.vue +1 -1
  21. package/dist/runtime/components/TablePanelColumnSorting.vue +1 -1
  22. package/dist/runtime/components/TablePanelFilters.vue.d.ts +1 -1
  23. package/dist/runtime/components/UploraImage.vue +2 -2
  24. package/dist/runtime/components/UploraImage.vue.d.ts +1 -1
  25. package/dist/runtime/components/prose/UploraImage.vue +1 -1
  26. package/dist/runtime/composables/useAdmin.d.ts +1 -1
  27. package/dist/runtime/composables/useAdmin.js +1 -2
  28. package/dist/runtime/composables/useAsyncHandler.d.ts +12 -0
  29. package/dist/runtime/composables/useAsyncHandler.js +30 -0
  30. package/dist/runtime/composables/useLogout.d.ts +8 -0
  31. package/dist/runtime/composables/useLogout.js +21 -0
  32. package/dist/runtime/composables/useUplora.d.ts +20 -0
  33. package/dist/runtime/composables/useUplora.js +52 -0
  34. package/dist/runtime/editor/components/BlockMenu.vue +43 -0
  35. package/dist/runtime/editor/components/BlockMenu.vue.d.ts +2 -0
  36. package/dist/runtime/editor/components/SlashCommand.vue +92 -0
  37. package/dist/runtime/editor/components/SlashCommand.vue.d.ts +7 -0
  38. package/dist/runtime/editor/components/TooltipLink.vue +81 -0
  39. package/dist/runtime/editor/components/TooltipLink.vue.d.ts +9 -0
  40. package/dist/runtime/editor/components/TooltipMenu.vue +75 -0
  41. package/dist/runtime/editor/components/TooltipMenu.vue.d.ts +9 -0
  42. package/dist/runtime/editor/extensions/callout/CalloutView.vue +7 -1
  43. package/dist/runtime/editor/extensions/uplora-image/UploraImageView.vue +7 -4
  44. package/dist/runtime/index.css +1 -1
  45. package/dist/runtime/server/api/uplora/[id].delete.d.ts +9 -1
  46. package/dist/runtime/server/api/uplora/[id].delete.js +13 -2
  47. package/dist/runtime/server/api/uplora/index.post.d.ts +7 -1
  48. package/dist/runtime/server/api/uplora/index.post.js +31 -2
  49. package/dist/runtime/server/errors/InternalHttpError.d.ts +5 -1
  50. package/dist/runtime/server/errors/InternalHttpError.js +2 -1
  51. package/dist/runtime/server/utils/errors.js +1 -1
  52. package/dist/runtime/server/utils/validation.d.ts +6 -2
  53. package/dist/runtime/server/utils/validation.js +28 -0
  54. package/dist/runtime/types/index.d.ts +4 -0
  55. package/dist/runtime/types/index.js +3 -0
  56. package/dist/runtime/types/query.d.ts +0 -3
  57. package/dist/runtime/types/tv.d.ts +2 -2
  58. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  export default {
2
2
  "slots": {
3
- "root": "rounded-md overflow-hidden"
3
+ "root": ""
4
4
  }
5
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
@@ -69,13 +69,13 @@ import ComponentRender from '../../component-render'
69
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
@@ -17,6 +17,11 @@ interface CMSOptions {
17
17
  * @defaultValue 'https://fluxor.uplora.ru'
18
18
  */
19
19
  fluxorUrl?: string;
20
+ /**
21
+ * Uplora URL
22
+ * @defaultValue 'http://uplora.ru'
23
+ */
24
+ uploraUrl?: string;
20
25
  /**
21
26
  * Prefix for the environment variables
22
27
  * @defaultValue 'APP_'
@@ -35,6 +40,8 @@ declare module '@nuxt/schema' {
35
40
  fluxorUrl: string;
36
41
  version: string;
37
42
  };
43
+ uploraUrl: string;
44
+ uploraApiKey: string;
38
45
  }
39
46
  }
40
47
 
package/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hywax/cms",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
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, addServerHandler, 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.6";
9
+ const version = "0.0.8";
9
10
 
10
11
  function createContext(options, nuxt) {
11
12
  const { resolve } = createResolver(import.meta.url);
@@ -24,6 +25,7 @@ const defaultCMSConfig = {};
24
25
  const defaultModuleOptions = {
25
26
  name: "cms",
26
27
  prefix: "C",
28
+ uploraUrl: "http://uplora.ru",
27
29
  fluxorUrl: "https://fluxor.uplora.ru",
28
30
  envPrefix: "APP_",
29
31
  httpCodes: {
@@ -52,7 +54,7 @@ function prepareAutoImports({ resolve, options, nuxt }) {
52
54
  path: resolve("./runtime/components"),
53
55
  pathPrefix: false,
54
56
  prefix: options?.prefix,
55
- ignore: ["prose/**", "editor/**"]
57
+ ignore: ["prose/**"]
56
58
  });
57
59
  addComponentsDir({
58
60
  path: resolve("./runtime/components/prose"),
@@ -75,6 +77,103 @@ function prepareAutoImports({ resolve, options, nuxt }) {
75
77
  addServerImportsDir(resolve("./runtime/server/utils"));
76
78
  nuxt.options.nitro.alias ||= {};
77
79
  nuxt.options.nitro.alias["#cms/http-codes"] = httpCodesPath;
80
+ nuxt.options.alias["#cms"] = resolve("./runtime");
81
+ nuxt.options.appConfig.cms = defu(nuxt.options.appConfig.cms || {}, defaultCMSConfig);
82
+ }
83
+
84
+ async function prepareGenerateEnv({ nuxt }) {
85
+ const runtimeConfig = nuxt.options.runtimeConfig;
86
+ if (nuxt.options.dev && !runtimeConfig.uploraApiKey) {
87
+ const envPath = join(nuxt.options.rootDir, ".env");
88
+ const envContent = await readFile(envPath, "utf-8").catch(() => "");
89
+ const envKey = `${runtimeConfig.nitro?.envPrefix || "NUXT_"}UPLORA_API_KEY`;
90
+ if (!envContent.includes(envKey)) {
91
+ await writeFile(
92
+ envPath,
93
+ `${envContent ? `${envContent}
94
+ ` : envContent}${envKey}=put-your-api-key-here`,
95
+ "utf-8"
96
+ );
97
+ }
98
+ }
99
+ }
100
+
101
+ async function prepareInstallModules(_context) {
102
+ if (!hasNuxtModule("@nuxt/ui-pro")) {
103
+ await installModule("@nuxt/ui-pro");
104
+ }
105
+ if (!hasNuxtModule("@nuxtjs/mdc")) {
106
+ await installModule("@nuxtjs/mdc");
107
+ }
108
+ if (!hasNuxtModule("@vueuse/nuxt")) {
109
+ await installModule("@vueuse/nuxt");
110
+ }
111
+ if (!hasNuxtModule("nuxt-auth-utils")) {
112
+ await installModule("nuxt-auth-utils");
113
+ }
114
+ }
115
+
116
+ const icons = {
117
+ edit: "lucide:pencil",
118
+ trash: "lucide:trash-2",
119
+ imageUp: "lucide:image-up",
120
+ arrowDownUp: "lucide:arrow-down-up",
121
+ sortAsc: "lucide:arrow-up-wide-narrow",
122
+ sortDesc: "lucide:arrow-down-wide-narrow",
123
+ columns: "lucide:columns-3-cog",
124
+ filter: "lucide:filter",
125
+ link: "lucide:link-2",
126
+ linkOff: "lucide:link-2-off",
127
+ ellipsisVertical: "lucide:ellipsis-vertical",
128
+ heading2: "lucide:heading-2",
129
+ heading3: "lucide:heading-3",
130
+ heading4: "lucide:heading-4",
131
+ list: "lucide:list",
132
+ listOrdered: "lucide:list-ordered",
133
+ checkSquare: "lucide:check-square",
134
+ image: "lucide:image",
135
+ quote: "lucide:quote",
136
+ code: "lucide:code",
137
+ paragraph: "lucide:paragraph",
138
+ pilcrow: "lucide:pilcrow",
139
+ info: "lucide:info",
140
+ bold: "lucide:bold",
141
+ italic: "lucide:italic",
142
+ underline: "lucide:underline",
143
+ strike: "lucide:strikethrough"
144
+ };
145
+
146
+ function prepareMergeConfigs({ nuxt, options }) {
147
+ nuxt.options.runtimeConfig.public = defu(nuxt.options.runtimeConfig.public || {}, {
148
+ fluxorUrl: options.fluxorUrl,
149
+ version: ""
150
+ });
151
+ nuxt.options.runtimeConfig.nitro ||= {};
152
+ nuxt.options.runtimeConfig.nitro.envPrefix = options.envPrefix;
153
+ nuxt.options.appConfig.ui = defu(nuxt.options.appConfig.ui || {}, {
154
+ icons
155
+ });
156
+ nuxt.options.ui = defu(nuxt.options.ui || {}, {
157
+ colorMode: true,
158
+ fonts: true
159
+ });
160
+ nuxt.options.colorMode = defu(nuxt.options.colorMode || {}, {
161
+ storageKey: `${options.name}-color-mode`
162
+ });
163
+ nuxt.options.mdc = defu(nuxt.options.mdc || {}, {
164
+ components: {
165
+ map: {
166
+ "uplora-image": "ProseUploraImage"
167
+ }
168
+ }
169
+ });
170
+ nuxt.options.auth = defu(nuxt.options.auth || {}, {
171
+ name: `${options.name}-session`
172
+ });
173
+ nuxt.options.runtimeConfig.uplora = defu(nuxt.options.runtimeConfig.uplora || {}, {
174
+ url: options.uploraUrl,
175
+ apiKey: ""
176
+ });
78
177
  }
79
178
 
80
179
  function prepareServerRoutes({ resolve }) {
@@ -114,6 +213,20 @@ const buttonDelete = {
114
213
  }
115
214
  };
116
215
 
216
+ const editorContentFull = {
217
+ slots: {
218
+ root: "h-full w-full relative",
219
+ editor: "min-h-full w-full focus:outline-none editor-content-full relative"
220
+ }
221
+ };
222
+
223
+ const editorContentLight = {
224
+ slots: {
225
+ root: "relative",
226
+ editor: "w-full focus:outline-none editor-content-light relative"
227
+ }
228
+ };
229
+
117
230
  const formPanel = {
118
231
  slots: {
119
232
  root: "",
@@ -161,6 +274,22 @@ const inputSlug = {
161
274
  }
162
275
  };
163
276
 
277
+ const inputUploraImage = {
278
+ slots: {
279
+ root: "relative w-full rounded-md overflow-hidden border border-default aspect-3/2 text-sm",
280
+ image: "",
281
+ imageActions: "absolute top-4 right-4 flex gap-2",
282
+ uploader: "flex flex-col items-center justify-center w-full h-full p-4",
283
+ uploaderPendingIcon: "size-6 animate-spin",
284
+ uploaderIdleButton: "cursor-pointer bg-muted/50 border border-default border-dashed rounded-md h-full w-full",
285
+ uploaderIdleIcon: "size-10 text-primary",
286
+ uploaderIdleText: "mt-2 font-medium",
287
+ uploaderIdleExtensions: "mt-2 text-xs text-muted uppercase",
288
+ uploaderErrorText: "text-center font-medium",
289
+ uploaderErrorActions: "flex gap-2 mt-4"
290
+ }
291
+ };
292
+
164
293
  const modalConfirm = {
165
294
  slots: {
166
295
  root: ""
@@ -231,11 +360,14 @@ const theme = {
231
360
  buttonClear: buttonClear,
232
361
  buttonCopy: buttonCopy,
233
362
  buttonDelete: buttonDelete,
363
+ editorContentFull: editorContentFull,
364
+ editorContentLight: editorContentLight,
234
365
  formPanel: formPanel,
235
366
  formPanelAsideSection: formPanelAsideSection,
236
367
  formPanelSection: formPanelSection,
237
368
  inputSeo: inputSeo,
238
369
  inputSlug: inputSlug,
370
+ inputUploraImage: inputUploraImage,
239
371
  modalConfirm: modalConfirm,
240
372
  tableCellPreview: tableCellPreview,
241
373
  tableCellSeo: tableCellSeo,
@@ -271,7 +403,7 @@ const callout = {
271
403
 
272
404
  const uploraImage$1 = {
273
405
  slots: {
274
- root: "rounded-md overflow-hidden"
406
+ root: ""
275
407
  }
276
408
  };
277
409
 
@@ -410,14 +542,6 @@ function prepareTemplates(context) {
410
542
  }
411
543
  }
412
544
 
413
- const icons = {
414
- sortAsc: "lucide:arrow-up-wide-narrow",
415
- sortDesc: "lucide:arrow-down-wide-narrow",
416
- columns: "lucide:columns-3-cog",
417
- filter: "lucide:filter",
418
- link: "lucide:link-2"
419
- };
420
-
421
545
  const module = defineNuxtModule({
422
546
  meta: {
423
547
  name,
@@ -427,49 +551,12 @@ const module = defineNuxtModule({
427
551
  defaults: defaultModuleOptions,
428
552
  async setup(options, nuxt) {
429
553
  const context = createContext(options, nuxt);
430
- nuxt.options.runtimeConfig.public = defu(nuxt.options.runtimeConfig.public || {}, {
431
- fluxorUrl: options.fluxorUrl,
432
- version: ""
433
- });
434
- nuxt.options.runtimeConfig.nitro ||= {};
435
- nuxt.options.runtimeConfig.nitro.envPrefix = options.envPrefix;
436
- nuxt.options.appConfig.ui = defu(nuxt.options.appConfig.ui || {}, {
437
- icons
438
- });
439
- nuxt.options.ui = defu(nuxt.options.ui || {}, {
440
- colorMode: true,
441
- fonts: true
442
- });
443
- nuxt.options.colorMode = defu(nuxt.options.colorMode || {}, {
444
- storageKey: `${options.name}-color-mode`
445
- });
446
- nuxt.options.mdc = defu(nuxt.options.mdc || {}, {
447
- components: {
448
- map: {
449
- "uplora-image": "ProseUploraImage"
450
- }
451
- }
452
- });
453
- nuxt.options.auth = defu(nuxt.options.auth || {}, {
454
- name: `${options.name}-session`
455
- });
456
- if (!hasNuxtModule("@nuxt/ui-pro")) {
457
- await installModule("@nuxt/ui-pro");
458
- }
459
- if (!hasNuxtModule("@nuxtjs/mdc")) {
460
- await installModule("@nuxtjs/mdc");
461
- }
462
- if (!hasNuxtModule("@vueuse/nuxt")) {
463
- await installModule("@vueuse/nuxt");
464
- }
465
- if (!hasNuxtModule("nuxt-auth-utils")) {
466
- await installModule("nuxt-auth-utils");
467
- }
468
- nuxt.options.alias["#cms"] = context.resolve("./runtime");
469
- nuxt.options.appConfig.cms = defu(nuxt.options.appConfig.cms || {}, defaultCMSConfig);
554
+ prepareMergeConfigs(context);
555
+ await prepareInstallModules();
470
556
  prepareAutoImports(context);
471
557
  prepareTemplates(context);
472
558
  prepareServerRoutes(context);
559
+ await prepareGenerateEnv(context);
473
560
  }
474
561
  });
475
562
 
@@ -31,8 +31,7 @@
31
31
 
32
32
  <script>
33
33
  import theme from "#build/cms/autocomplete-select";
34
- import { computed, ref, refDebounced, shallowRef, toRaw, triggerRef, useAppConfig, useAsyncData, useId, useNuxtData, watch } from "#imports";
35
- import { useInfiniteScroll, useOffsetPagination } from "@vueuse/core";
34
+ import { computed, ref, refDebounced, shallowRef, toRaw, triggerRef, useAppConfig, useAsyncData, useId, useInfiniteScroll, useNuxtData, useOffsetPagination, watch } from "#imports";
36
35
  import { tv } from "../utils/tv";
37
36
  </script>
38
37
 
@@ -10,6 +10,7 @@
10
10
  :color="color"
11
11
  :variant="variant"
12
12
  :size="size"
13
+ :icon="icon"
13
14
  :class="ui.root({ class: [props.ui?.root, props.class] })"
14
15
  auto-loading
15
16
  @click="handleDeleteClick()"
@@ -34,6 +35,7 @@ const props = defineProps({
34
35
  tooltipText: { type: String, required: false },
35
36
  color: { type: null, required: false, default: "error" },
36
37
  variant: { type: null, required: false },
38
+ icon: { type: null, required: false },
37
39
  size: { type: null, required: false },
38
40
  class: { type: null, required: false },
39
41
  ui: { type: null, required: false },
@@ -47,9 +49,8 @@ function handleDeleteClick() {
47
49
  ...props.modalProps,
48
50
  title: props.title ?? "\u0423\u0434\u0430\u043B\u0435\u043D\u0438\u0435",
49
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?",
50
- color: props.color,
52
+ color: "error",
51
53
  variant: props.variant,
52
- size: props.size,
53
54
  confirmLabel: props.confirmLabel,
54
55
  confirmText: props.confirmText,
55
56
  onConfirm: props.onConfirm
@@ -15,6 +15,7 @@ export interface ButtonDeleteProps extends Pick<ModalConfirmProps, 'onConfirm'>
15
15
  tooltipText?: string;
16
16
  color?: ButtonProps['color'];
17
17
  variant?: ButtonProps['variant'];
18
+ icon?: ButtonProps['icon'];
18
19
  size?: ButtonProps['size'];
19
20
  class?: any;
20
21
  ui?: ButtonDelete['slots'];
@@ -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>
@@ -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-light';
4
+ import 'prosekit/basic/style.css';
5
+ type EditorContentLight = ComponentConfig<typeof theme, AppConfig, 'editorContentLight'>;
6
+ export interface EditorContentLightProps {
7
+ as?: any;
8
+ class?: any;
9
+ ui?: EditorContentLight['slots'];
10
+ }
11
+ declare const _default: import("vue").DefineComponent<EditorContentLightProps & {
12
+ modelValue?: string;
13
+ }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
14
+ "update:modelValue": (value: string) => any;
15
+ }, string, import("vue").PublicProps, Readonly<EditorContentLightProps & {
16
+ modelValue?: string;
17
+ }> & Readonly<{
18
+ "onUpdate:modelValue"?: ((value: string) => any) | undefined;
19
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
20
+ export default _default;