inicontent 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/.github/pull_request.yml +18 -0
  2. package/CONTRIBUTING.md +67 -0
  3. package/LICENSE +21 -0
  4. package/README.md +142 -0
  5. package/app/app.vue +106 -0
  6. package/app/assets/main.css +974 -0
  7. package/app/components/Api/EndpointCard.vue +169 -0
  8. package/app/components/Api/SchemaDocsTable.vue +208 -0
  9. package/app/components/Asset/Card.vue +1363 -0
  10. package/app/components/Asset/Grid.vue +295 -0
  11. package/app/components/Asset/Icon.vue +38 -0
  12. package/app/components/Asset/Preview.vue +486 -0
  13. package/app/components/Asset/Scanner.vue +700 -0
  14. package/app/components/Asset/Thumb.vue +708 -0
  15. package/app/components/Auth/Reset.vue +99 -0
  16. package/app/components/Auth/index.vue +346 -0
  17. package/app/components/ChatInterface.vue +2326 -0
  18. package/app/components/ChatPagePreview.vue +34 -0
  19. package/app/components/Column/Array.vue +29 -0
  20. package/app/components/Column/Asset.vue +21 -0
  21. package/app/components/Column/Boolean.vue +12 -0
  22. package/app/components/Column/Color.vue +9 -0
  23. package/app/components/Column/Date.vue +57 -0
  24. package/app/components/Column/Edit.vue +215 -0
  25. package/app/components/Column/Email.vue +9 -0
  26. package/app/components/Column/Html.vue +27 -0
  27. package/app/components/Column/Id.vue +20 -0
  28. package/app/components/Column/Locale.vue +32 -0
  29. package/app/components/Column/Object.vue +18 -0
  30. package/app/components/Column/Password.vue +7 -0
  31. package/app/components/Column/Role.vue +15 -0
  32. package/app/components/Column/S.vue +14 -0
  33. package/app/components/Column/Table/Single.vue +49 -0
  34. package/app/components/Column/Table/index.vue +42 -0
  35. package/app/components/Column/Tags.vue +31 -0
  36. package/app/components/Column/Text.vue +9 -0
  37. package/app/components/Column/Url.vue +9 -0
  38. package/app/components/Column/index.vue +41 -0
  39. package/app/components/Dashboard/Editor.vue +221 -0
  40. package/app/components/Dashboard/Grid.vue +270 -0
  41. package/app/components/Dashboard/View.vue +131 -0
  42. package/app/components/Dashboard/Widget/BarChart.vue +73 -0
  43. package/app/components/Dashboard/Widget/Counter.vue +51 -0
  44. package/app/components/Dashboard/Widget/LineChart.vue +76 -0
  45. package/app/components/Dashboard/Widget/PieChart.vue +75 -0
  46. package/app/components/Dashboard/Widget/Table.vue +94 -0
  47. package/app/components/Dashboard/WidgetEditor.vue +274 -0
  48. package/app/components/Data/Array.vue +102 -0
  49. package/app/components/Data/Html.vue +9 -0
  50. package/app/components/Data/Icon.vue +9 -0
  51. package/app/components/Data/Object.vue +10 -0
  52. package/app/components/Data/S.vue +16 -0
  53. package/app/components/Data/Table.vue +53 -0
  54. package/app/components/Data/Value.vue +34 -0
  55. package/app/components/Data/index.vue +14 -0
  56. package/app/components/Field/Array.vue +324 -0
  57. package/app/components/Field/Asset/Actions.vue +97 -0
  58. package/app/components/Field/Asset/index.vue +639 -0
  59. package/app/components/Field/Boolean.vue +26 -0
  60. package/app/components/Field/Checkbox.vue +48 -0
  61. package/app/components/Field/Color.vue +27 -0
  62. package/app/components/Field/Date.vue +30 -0
  63. package/app/components/Field/Email.vue +51 -0
  64. package/app/components/Field/EmailTemplate.vue +420 -0
  65. package/app/components/Field/Html/ColorPicker.vue +127 -0
  66. package/app/components/Field/Html/Editor.vue +544 -0
  67. package/app/components/Field/Html/index.vue +25 -0
  68. package/app/components/Field/Icon.vue +114 -0
  69. package/app/components/Field/JSON.vue +113 -0
  70. package/app/components/Field/Mention.vue +36 -0
  71. package/app/components/Field/Number.vue +27 -0
  72. package/app/components/Field/Object.vue +92 -0
  73. package/app/components/Field/Password.vue +55 -0
  74. package/app/components/Field/Radio.vue +47 -0
  75. package/app/components/Field/S.vue +44 -0
  76. package/app/components/Field/Select.vue +49 -0
  77. package/app/components/Field/Table.vue +348 -0
  78. package/app/components/Field/Tags.vue +113 -0
  79. package/app/components/Field/Text.vue +31 -0
  80. package/app/components/Field/Textarea.vue +33 -0
  81. package/app/components/Field/Url.vue +40 -0
  82. package/app/components/Field/Wrapper.vue +128 -0
  83. package/app/components/Field/index.vue +68 -0
  84. package/app/components/FloatingChatbot.vue +73 -0
  85. package/app/components/Form/Card.vue +151 -0
  86. package/app/components/Form/Drawer.vue +195 -0
  87. package/app/components/Form/index.vue +621 -0
  88. package/app/components/Header.vue +328 -0
  89. package/app/components/Offline/ConflictModal.vue +200 -0
  90. package/app/components/Offline/SyncStatus.vue +249 -0
  91. package/app/components/PlatformFeatureLanding.vue +65 -0
  92. package/app/components/Table/Backups.vue +638 -0
  93. package/app/components/Table/Flows.vue +814 -0
  94. package/app/components/Table/Grid.vue +296 -0
  95. package/app/components/Table/Icon.vue +36 -0
  96. package/app/components/Table/Logs.vue +181 -0
  97. package/app/components/Table/Schedules.vue +722 -0
  98. package/app/components/Table/Search/Button.vue +256 -0
  99. package/app/components/Table/Search/Items.vue +286 -0
  100. package/app/components/Table/Search/index.vue +87 -0
  101. package/app/components/Table/Settings/Schema.vue +801 -0
  102. package/app/components/Table/Settings/index.vue +631 -0
  103. package/app/components/Table/TranslateDrawer.vue +526 -0
  104. package/app/components/Table/Views/Kanban.vue +289 -0
  105. package/app/components/Table/Views/Table.vue +1273 -0
  106. package/app/components/Table/index.vue +890 -0
  107. package/app/composables/Translation/define.ts +41 -0
  108. package/app/composables/Translation/fetch.ts +107 -0
  109. package/app/composables/Translation/languages.ts +1 -0
  110. package/app/composables/Translation/loadCoreTranslations.ts +38 -0
  111. package/app/composables/Translation/translate.ts +250 -0
  112. package/app/composables/databaseCookies.ts +79 -0
  113. package/app/composables/fieldValidator.ts +72 -0
  114. package/app/composables/fieldsList.ts +301 -0
  115. package/app/composables/formatDatabase.ts +101 -0
  116. package/app/composables/index.ts +444 -0
  117. package/app/composables/openDrawer.ts +118 -0
  118. package/app/composables/optimizeFile.ts +287 -0
  119. package/app/composables/renderLabel.ts +88 -0
  120. package/app/composables/schemaClipboard.ts +95 -0
  121. package/app/composables/search.ts +353 -0
  122. package/app/composables/setThemeConfig.ts +48 -0
  123. package/app/composables/translationValue.ts +169 -0
  124. package/app/composables/useAssetPreview.ts +84 -0
  125. package/app/composables/useAssetUploader.ts +56 -0
  126. package/app/composables/useDashboardData.ts +310 -0
  127. package/app/composables/useDocumentThumbnail.ts +351 -0
  128. package/app/composables/useEcharts.ts +33 -0
  129. package/app/composables/useLocalDatabaseConfig.ts +51 -0
  130. package/app/composables/useOcr.ts +300 -0
  131. package/app/composables/useOfflineFetch.ts +194 -0
  132. package/app/composables/useOfflineItem.ts +107 -0
  133. package/app/composables/useOfflineQueue.ts +302 -0
  134. package/app/composables/useOfflineSync.ts +235 -0
  135. package/app/composables/usePasskeyAuth.ts +156 -0
  136. package/app/composables/usePdfCompressor.ts +180 -0
  137. package/app/composables/usePlatformRedirect.ts +72 -0
  138. package/app/composables/useRealtimeSync.ts +233 -0
  139. package/app/composables/useReferencedItems.ts +180 -0
  140. package/app/composables/useScanner.ts +664 -0
  141. package/app/composables/useSubscription.ts +650 -0
  142. package/app/composables/useThumbnailCache.ts +136 -0
  143. package/app/composables/useVideoCompressor.ts +194 -0
  144. package/app/error.vue +112 -0
  145. package/app/layouts/api.vue +188 -0
  146. package/app/layouts/dashboard.vue +91 -0
  147. package/app/layouts/default.vue +228 -0
  148. package/app/layouts/table.vue +471 -0
  149. package/app/locales/ar.ts +996 -0
  150. package/app/locales/en.ts +655 -0
  151. package/app/locales/es.ts +996 -0
  152. package/app/locales/fr.ts +997 -0
  153. package/app/middleware/dashboard.ts +33 -0
  154. package/app/middleware/database.ts +83 -0
  155. package/app/middleware/global.ts +1 -0
  156. package/app/middleware/sid-ingest.global.ts +29 -0
  157. package/app/middleware/table.ts +54 -0
  158. package/app/middleware/user.ts +26 -0
  159. package/app/pages/[[database]]/admin/api/auth/index.vue +306 -0
  160. package/app/pages/[[database]]/admin/api/index.vue +18 -0
  161. package/app/pages/[[database]]/admin/api/tables/[table]/index.vue +497 -0
  162. package/app/pages/[[database]]/admin/api/tables/index.vue +87 -0
  163. package/app/pages/[[database]]/admin/billing/index.vue +16 -0
  164. package/app/pages/[[database]]/admin/dashboards/[id]/edit.vue +66 -0
  165. package/app/pages/[[database]]/admin/dashboards/[id]/index.vue +117 -0
  166. package/app/pages/[[database]]/admin/dashboards/index.vue +20 -0
  167. package/app/pages/[[database]]/admin/index.vue +28 -0
  168. package/app/pages/[[database]]/admin/settings.vue +526 -0
  169. package/app/pages/[[database]]/admin/tables/[table]/[id]/edit.vue +143 -0
  170. package/app/pages/[[database]]/admin/tables/[table]/[id]/index.vue +146 -0
  171. package/app/pages/[[database]]/admin/tables/[table]/flows.vue +10 -0
  172. package/app/pages/[[database]]/admin/tables/[table]/index.vue +10 -0
  173. package/app/pages/[[database]]/admin/tables/[table]/new.vue +12 -0
  174. package/app/pages/[[database]]/admin/tables/[table]/schedules.vue +10 -0
  175. package/app/pages/[[database]]/admin/tables/[table]/settings.vue +30 -0
  176. package/app/pages/[[database]]/admin/tables/assets/[...path].vue +24 -0
  177. package/app/pages/[[database]]/admin/tables/assets/flows.vue +10 -0
  178. package/app/pages/[[database]]/admin/tables/backups/index.vue +10 -0
  179. package/app/pages/[[database]]/admin/tables/blocks/index.vue +16 -0
  180. package/app/pages/[[database]]/admin/tables/index.vue +20 -0
  181. package/app/pages/[[database]]/admin/tables/pages/index.vue +16 -0
  182. package/app/pages/[[database]]/admin/tables/templates/index.vue +16 -0
  183. package/app/pages/[[database]]/api/index.vue +120 -0
  184. package/app/pages/[[database]]/auth/index.vue +12 -0
  185. package/app/pages/[[database]]/auth/reset.vue +10 -0
  186. package/app/pages/[[database]]/index.vue +10 -0
  187. package/app/plugins/offline-warmup.client.ts +53 -0
  188. package/app/plugins/template-globals.ts +9 -0
  189. package/app/spa-loading-template.html +93 -0
  190. package/biome.json +53 -0
  191. package/index.d.ts +380 -0
  192. package/modules/naiveui.ts +41 -0
  193. package/nuxt.config.ts +269 -0
  194. package/package.json +56 -0
  195. package/pnpm-workspace.yaml +5 -0
  196. package/public/apple-touch-icon.png +0 -0
  197. package/public/favicon-16x16.png +0 -0
  198. package/public/favicon-32x32.png +0 -0
  199. package/public/favicon.ico +0 -0
  200. package/public/pwa-192x192.png +0 -0
  201. package/public/pwa-512x512.png +0 -0
  202. package/public/pwa-maskable-192x192.png +0 -0
  203. package/public/pwa-maskable-512x512.png +0 -0
  204. package/public/vendor/opencv.js +48 -0
  205. package/tsconfig.json +9 -0
@@ -0,0 +1,544 @@
1
+ <template>
2
+ <NDrawer v-model:show="showAssetsModal" defaultHeight="50%" placement="bottom" resizable>
3
+ <NDrawerContent id="assetsModal" :nativeScrollbar="false" :bodyContentStyle="{ padding: 0 }">
4
+ <AssetCard targetID="assetsModal">
5
+ <template v-slot="{ asset }">
6
+ <NRadio v-if="asset.type !== 'dir'"
7
+ @update:checked="(editor?.chain().focus().setImage({ src: asset.publicURL }).run(), showAssetsModal = false)" />
8
+ </template>
9
+ </AssetCard>
10
+ </NDrawerContent>
11
+ </NDrawer>
12
+
13
+ <LazyAssetScanner v-model:show="showScanner" :allowOCR="true"
14
+ @scanned="(blob, dataUrl) => { editor?.chain().focus().setImage({ src: dataUrl }).run(); showScanner = false; }"
15
+ @insertText="(text, blocks) => { editor?.chain().focus().insertContent(blocks).run(); showScanner = false; }" />
16
+
17
+ <NFlex class="richEditorWrapper" vertical style="width: 100%">
18
+ <NScrollbar x-scrollable>
19
+ <NFlex :wrap="false" align="center">
20
+ <NButtonGroup size="small">
21
+ <NButton @click="editor?.chain().focus().toggleBold().run()"
22
+ :disabled="!editor?.can().chain().focus().toggleBold().run()"
23
+ :type="editor?.isActive('bold') ? 'primary' : 'default'">
24
+ <NIcon>
25
+ <Icon name="tabler:bold" />
26
+ </NIcon>
27
+ </NButton>
28
+
29
+ <NButton @click="editor?.chain().focus().toggleItalic().run()"
30
+ :disabled="!editor?.can().chain().focus().toggleItalic().run()"
31
+ :type="editor?.isActive('italic') ? 'primary' : 'default'">
32
+ <NIcon>
33
+ <Icon name="tabler:italic" />
34
+ </NIcon>
35
+ </NButton>
36
+
37
+ <NButton @click="editor?.chain().focus().toggleUnderline().run()"
38
+ :disabled="!editor?.can().chain().focus().toggleUnderline().run()"
39
+ :type="editor?.isActive('underline') ? 'primary' : 'default'">
40
+ <NIcon>
41
+ <Icon name="tabler:underline" />
42
+ </NIcon>
43
+ </NButton>
44
+
45
+ <NButton :type="editor?.isActive('strike') ? 'primary' : 'default'"
46
+ @click="editor?.chain().focus().toggleStrike().run()"
47
+ :disabled="!editor?.can().chain().focus().toggleStrike().run()">
48
+ <NIcon>
49
+ <Icon name="tabler:strikethrough" />
50
+ </NIcon>
51
+ </NButton>
52
+
53
+ <NPopselect :disabled="!editor?.can().chain().focus().toggleHeading({ level: 1 }).run()" size="small"
54
+ :render-label="renderHeadingOption" :value="editor?.getAttributes('heading').level"
55
+ @update:value="(value) => editor?.chain().focus().toggleHeading({ level: value }).run()"
56
+ :options="headingOptions">
57
+ <NButton :type="editor?.isActive('heading') ? 'primary' : 'default'">
58
+ <NIcon>
59
+ <Icon name="tabler:heading" />
60
+ </NIcon>
61
+ </NButton>
62
+ </NPopselect>
63
+
64
+ <NPopselect size="small" scrollable :render-label="renderFontSizeOption"
65
+ :value="editor?.getAttributes('font-size').size?.replace('px', '')"
66
+ @update:value="(value: string) => editor?.chain().focus().setFontSize(value).run()"
67
+ :options="fontSizeOptions">
68
+ <NButton @click="editor?.chain().focus().unsetFontSize().run()">
69
+ <NIcon>
70
+ <Icon name="tabler:text-size" />
71
+ </NIcon>
72
+ </NButton>
73
+ </NPopselect>
74
+
75
+ <NPopover>
76
+ <template #trigger>
77
+ <NButton :style="{ color: fontColor }"
78
+ :disabled="!editor?.can().chain().focus().setColor('#ff9800').run()">
79
+ <NIcon>
80
+ <Icon name="tabler:color-picker" />
81
+ </NIcon>
82
+ </NButton>
83
+ </template>
84
+ <LazyFieldHtmlColorPicker :modelValue="fontColor"
85
+ @update:modelValue="(value) => (fontColor = value, editor?.chain().focus().setColor(value).run())" />
86
+ </NPopover>
87
+
88
+ <NPopover>
89
+ <template #trigger>
90
+ <NButton @click="editor?.chain().focus().toggleHighlight().run()" :style="{ color: fontBgColor }">
91
+ <NIcon>
92
+ <Icon name="tabler:highlight" />
93
+ </NIcon>
94
+ </NButton>
95
+ </template>
96
+ <LazyFieldHtmlColorPicker :modelValue="fontBgColor"
97
+ @update:modelValue="(value) => (fontBgColor = value, editor?.chain().focus().toggleHighlight({ color: value }).run())" />
98
+ </NPopover>
99
+
100
+ </NButtonGroup>
101
+
102
+
103
+ <NDivider vertical />
104
+
105
+
106
+ <NButtonGroup size="small">
107
+
108
+ <NButton @click="showAssetsModal = true">
109
+ <NIcon>
110
+ <Icon name="tabler:upload" />
111
+ </NIcon>
112
+ </NButton>
113
+
114
+ <NButton @click="showScanner = true">
115
+ <NIcon>
116
+ <Icon name="tabler:scan" />
117
+ </NIcon>
118
+ </NButton>
119
+
120
+ <!-- TO-DO: Fix URL -->
121
+ <NPopover @update:show="(show: boolean) => {
122
+ if (show) url = editor?.getAttributes('link').href
123
+ else url = undefined
124
+ }">
125
+ <template #trigger>
126
+ <NButton :type="editor?.isActive('link') ? 'primary' : 'default'">
127
+ <NIcon>
128
+ <Icon name="tabler:link" />
129
+ </NIcon>
130
+ </NButton>
131
+ </template>
132
+ <NFlex :reverse="Language === 'ar'" :wrap="false">
133
+ <NInput :input-props="{ type: 'url' }" size="small" v-model="url" />
134
+ <NButtonGroup>
135
+ <NButton :type="'error'" @click="editor?.chain().focus().unsetLink().run()"
136
+ :disabled="!editor?.isActive('link')">
137
+ <NIcon>
138
+ <Icon name="tabler:link-off" />
139
+ </NIcon>
140
+ </NButton>
141
+ <NButton type="primary" @click="setLink">
142
+ <NIcon>
143
+ <Icon name="tabler:arrow-right" />
144
+ </NIcon>
145
+ </NButton>
146
+ </NButtonGroup>
147
+ </NFlex>
148
+ </NPopover>
149
+
150
+ <NButton :type="editor?.isActive('bulletList') ? 'primary' : 'default'"
151
+ @click="editor?.chain().focus().toggleBulletList().run()">
152
+ <NIcon>
153
+ <Icon name="tabler:list-numbers" />
154
+ </NIcon>
155
+ </NButton>
156
+
157
+ <NButton :type="editor?.isActive('orderedList') ? 'primary' : 'default'"
158
+ @click="editor?.chain().focus().toggleOrderedList().run()">
159
+ <NIcon>
160
+ <Icon name="tabler:list" />
161
+ </NIcon>
162
+ </NButton>
163
+
164
+ </NButtonGroup>
165
+
166
+
167
+ <NDivider vertical />
168
+
169
+
170
+ <NButtonGroup size="small">
171
+
172
+ <NButton :type="editor?.isActive({ textAlign: 'left' }) ? 'primary' : 'default'"
173
+ @click="editor?.chain().focus().setTextAlign('left').run()">
174
+ <NIcon>
175
+ <Icon name="tabler:align-left" />
176
+ </NIcon>
177
+ </NButton>
178
+
179
+ <NButton :type="editor?.isActive({ textAlign: 'center' }) ? 'primary' : 'default'"
180
+ @click="editor?.chain().focus().setTextAlign('center').run()">
181
+ <NIcon>
182
+ <Icon name="tabler:align-center" />
183
+ </NIcon>
184
+ </NButton>
185
+
186
+ <NButton :type="editor?.isActive({ textAlign: 'right' }) ? 'primary' : 'default'"
187
+ @click="editor?.chain().focus().setTextAlign('right').run()">
188
+ <NIcon>
189
+ <Icon name="tabler:align-right" />
190
+ </NIcon>
191
+ </NButton>
192
+
193
+
194
+ <NButton :type="editor?.isActive({ textAlign: 'justify' }) ? 'primary' : 'default'"
195
+ @click="editor?.chain().focus().setTextAlign('justify').run()">
196
+ <NIcon>
197
+ <Icon name="tabler:align-justified" />
198
+ </NIcon>
199
+ </NButton>
200
+
201
+
202
+
203
+ <NButton
204
+ :type="editor?.isActive({ textAlign: 'left' }) || editor?.isActive({ textAlign: 'center' }) || editor?.isActive({ textAlign: 'right' }) || editor?.isActive({ textAlign: 'justify' }) ? 'primary' : 'default'"
205
+ @click="editor?.chain().focus().unsetTextAlign().run()">
206
+ <NIcon>
207
+ <Icon name="tabler:text-wrap" />
208
+ </NIcon>
209
+ </NButton>
210
+
211
+ </NButtonGroup>
212
+
213
+ <NDivider vertical />
214
+
215
+
216
+ <NButtonGroup size="small">
217
+ <NButton @click="editor?.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()">
218
+ <NIcon>
219
+ <Icon name="tabler:table-plus" />
220
+ </NIcon>
221
+ </NButton>
222
+ <template
223
+ v-if="editor?.isActive('table') || editor?.isActive('tableCell') || editor?.isActive('tableHeader') || editor?.isActive('tableRow')">
224
+ <NTooltip>
225
+ <template #trigger>
226
+ <NButton @click="editor?.chain().focus().deleteTable().run()">
227
+ <NIcon>
228
+ <Icon name="tabler:table-minus" />
229
+ </NIcon>
230
+ </NButton>
231
+ </template>
232
+ {{ t('deleteTable') }}
233
+ </NTooltip>
234
+
235
+ <NTooltip>
236
+ <template #trigger>
237
+ <NButton @click="editor?.chain().focus().addColumnAfter().run()">
238
+ <NIcon>
239
+ <Icon name="tabler:column-insert-right" />
240
+ </NIcon>
241
+ </NButton>
242
+ </template>
243
+ {{ t('addColumnAfter') }}
244
+ </NTooltip>
245
+
246
+ <NTooltip>
247
+ <template #trigger>
248
+ <NButton @click="editor?.chain().focus().addColumnBefore().run()">
249
+ <NIcon>
250
+ <Icon name="tabler:column-insert-left" />
251
+ </NIcon>
252
+ </NButton>
253
+ </template>
254
+ {{ t('addColumnBefore') }}
255
+ </NTooltip>
256
+
257
+ <NTooltip>
258
+ <template #trigger>
259
+ <NButton @click="editor?.chain().focus().deleteColumn().run()">
260
+ <NIcon>
261
+ <Icon name="tabler:column-remove" />
262
+ </NIcon>
263
+ </NButton>
264
+ </template>
265
+ {{ t('deleteColumn') }}
266
+ </NTooltip>
267
+
268
+ <NTooltip>
269
+ <template #trigger>
270
+ <NButton @click="editor?.chain().focus().addRowBefore().run()">
271
+ <NIcon>
272
+ <Icon name="tabler:row-insert-top" />
273
+ </NIcon>
274
+ </NButton>
275
+ </template>
276
+ {{ t('addRowBefore') }}
277
+ </NTooltip>
278
+
279
+ <NTooltip>
280
+ <template #trigger>
281
+ <NButton @click="editor?.chain().focus().addColumnBefore().run()">
282
+ <NIcon>
283
+ <Icon name="tabler:column-insert-left" />
284
+ </NIcon>
285
+ </NButton>
286
+ </template>
287
+ {{ t('addColumnBefore') }}
288
+ </NTooltip>
289
+
290
+ <NTooltip>
291
+ <template #trigger>
292
+ <NButton @click="editor?.chain().focus().addRowAfter().run()">
293
+ <NIcon>
294
+ <Icon name="tabler:row-insert-bottom" />
295
+ </NIcon>
296
+ </NButton>
297
+ </template>
298
+ {{ t('addRowAfter') }}
299
+ </NTooltip>
300
+
301
+ <NTooltip>
302
+ <template #trigger>
303
+ <NButton @click="editor?.chain().focus().deleteRow().run()">
304
+ <NIcon>
305
+ <Icon name="tabler:row-remove" />
306
+ </NIcon>
307
+ </NButton>
308
+ </template>
309
+ {{ t('deleteRow') }}
310
+ </NTooltip>
311
+
312
+
313
+ <NTooltip>
314
+ <template #trigger>
315
+ <NButton @click="editor?.chain().focus().mergeCells().run()">
316
+ <NIcon>
317
+ <Icon name="tabler:table-row" />
318
+ </NIcon>
319
+ </NButton>
320
+ </template>
321
+ {{ t('mergeCells') }}
322
+ </NTooltip>
323
+
324
+ </template>
325
+ </NButtonGroup>
326
+
327
+ <NDivider vertical />
328
+
329
+ <NButtonGroup size="small">
330
+ <NButton @click="editor?.chain().focus().undo().run()"
331
+ :disabled="!editor?.can().chain().focus().undo().run()">
332
+ <NIcon>
333
+ <Icon name="tabler:arrow-back-up" />
334
+ </NIcon>
335
+ </NButton>
336
+
337
+ <NButton @click="editor?.chain().focus().redo().run()"
338
+ :disabled="!editor?.can().chain().focus().redo().run()">
339
+ <NIcon>
340
+ <Icon name="tabler:arrow-forward-up" />
341
+ </NIcon>
342
+ </NButton>
343
+ </NButtonGroup>
344
+
345
+ </NFlex>
346
+ </NScrollbar>
347
+
348
+ <NScrollbar class="externalRichEditor">
349
+ <TiptapEditorContent class="internalRichEditor" :editor="editor" />
350
+ </NScrollbar>
351
+ </NFlex>
352
+ </template>
353
+
354
+ <script setup lang="ts">
355
+ import { Color as TiptapColor } from "@tiptap/extension-color";
356
+ import TiptapHighlight from "@tiptap/extension-highlight";
357
+ import { Table } from "@tiptap/extension-table";
358
+ import TableCell from "@tiptap/extension-table-cell";
359
+ import TableHeader from "@tiptap/extension-table-header";
360
+ import TableRow from "@tiptap/extension-table-row";
361
+ import TiptapTextAlign from "@tiptap/extension-text-align";
362
+ import { TextStyle as TiptapTextStyle } from "@tiptap/extension-text-style";
363
+
364
+ const modelValue = defineModel<string>();
365
+
366
+ const showAssetsModal = ref(false);
367
+ const showScanner = ref(false);
368
+ const fontColor = ref();
369
+ const fontBgColor = ref();
370
+ const url = ref();
371
+
372
+ function renderHeadingOption({ value }: { value: number }) {
373
+ return h(`h${value}`, { style: { margin: 0 } }, `Heading ${value}`);
374
+ }
375
+
376
+ const database = useState<Database>("database");
377
+ const Language = useScopedCookie<LanguagesType>("language", database.value?.slug);
378
+
379
+ const headingOptions = [
380
+ {
381
+ value: 1,
382
+ },
383
+ {
384
+ value: 2,
385
+ },
386
+ {
387
+ value: 3,
388
+ },
389
+ {
390
+ value: 4,
391
+ },
392
+ {
393
+ value: 5,
394
+ },
395
+ {
396
+ value: 6,
397
+ },
398
+ ];
399
+
400
+ function renderFontSizeOption({ value }: { value: number }) {
401
+ return h("span", { style: { fontSize: `${value}px` } }, "Paragraph");
402
+ }
403
+
404
+ const fontSizeOptions = [
405
+ {
406
+ value: 8,
407
+ },
408
+ {
409
+ value: 10,
410
+ },
411
+ {
412
+ value: 12,
413
+ },
414
+ {
415
+ value: 14,
416
+ },
417
+ {
418
+ value: 16,
419
+ },
420
+ {
421
+ value: 18,
422
+ },
423
+ {
424
+ value: 20,
425
+ },
426
+ {
427
+ value: 24,
428
+ },
429
+ {
430
+ value: 30,
431
+ },
432
+ {
433
+ value: 48,
434
+ },
435
+ {
436
+ value: 60,
437
+ },
438
+ {
439
+ value: 72,
440
+ },
441
+ ];
442
+
443
+ function setLink() {
444
+ // cancelled
445
+ if (url.value === null) {
446
+ return;
447
+ }
448
+
449
+ // empty
450
+ if (url.value === "") {
451
+ editor.value?.chain().focus().extendMarkRange("link").unsetLink().run();
452
+
453
+ return;
454
+ }
455
+
456
+ // update link
457
+ editor.value
458
+ ?.chain()
459
+ .focus()
460
+ .extendMarkRange("link")
461
+ .setLink({ href: url.value })
462
+ .run();
463
+ }
464
+
465
+ declare module "@tiptap/core" {
466
+ interface Commands<ReturnType> {
467
+ fontSize: {
468
+ /**
469
+ * Set the font size
470
+ */
471
+ setFontSize: (size: string) => ReturnType;
472
+ /**
473
+ * Unset the font size
474
+ */
475
+ unsetFontSize: () => ReturnType;
476
+ };
477
+ }
478
+ }
479
+
480
+ const TextStyleExtended = TiptapTextStyle.extend({
481
+ addAttributes() {
482
+ return {
483
+ ...this.parent?.(),
484
+ fontSize: {
485
+ default: null,
486
+ parseHTML: (element) => element.style.fontSize.replace("px", ""),
487
+ renderHTML: (attributes) => {
488
+ if (!attributes.fontSize) {
489
+ return {};
490
+ }
491
+ return {
492
+ style: `font-size: ${attributes.fontSize}px`,
493
+ };
494
+ },
495
+ },
496
+ };
497
+ },
498
+
499
+ addCommands() {
500
+ return {
501
+ ...this.parent?.(),
502
+ setFontSize:
503
+ (fontSize) =>
504
+ ({ commands }) => {
505
+ return commands.setMark(this.name, { fontSize: fontSize });
506
+ },
507
+ unsetFontSize:
508
+ () =>
509
+ ({ chain }) => {
510
+ return chain()
511
+ .setMark(this.name, { fontSize: null })
512
+ .removeEmptyTextStyle()
513
+ .run();
514
+ },
515
+ };
516
+ },
517
+ });
518
+
519
+ const editor = useEditor({
520
+ content: modelValue.value,
521
+ extensions: [
522
+ TiptapStarterKit,
523
+ TiptapImage,
524
+ TiptapColor,
525
+ TextStyleExtended,
526
+ TiptapHighlight,
527
+ TiptapTextAlign,
528
+ Table.configure({
529
+ resizable: true,
530
+ }),
531
+ TableRow,
532
+ TableHeader,
533
+ TableCell,
534
+ ],
535
+ onUpdate: ({ editor }) => {
536
+ const content = editor.getHTML();
537
+ modelValue.value = content;
538
+ },
539
+ });
540
+
541
+ onBeforeUnmount(() => {
542
+ unref(editor)?.destroy();
543
+ });
544
+ </script>
@@ -0,0 +1,25 @@
1
+ <template>
2
+ <FieldWrapper :field :rule v-model="modelValue">
3
+ <FieldHtmlEditor v-model="modelValue" v-bind="field.inputProps
4
+ ? typeof field.inputProps === 'function'
5
+ ? field.inputProps(modelValue) ?? {}
6
+ : field.inputProps
7
+ : {}" />
8
+ </FieldWrapper>
9
+ </template>
10
+
11
+ <script lang="ts" setup>
12
+ import type { FormItemRule } from "naive-ui"
13
+
14
+ const { field } = defineProps<{ field: Field }>()
15
+
16
+ const modelValue = defineModel<string>()
17
+
18
+ const rule: FormItemRule = {
19
+ required: field.required,
20
+ validator() {
21
+ if (!modelValue.value && field.required)
22
+ return new Error(`${t(field.key)} ${t("isRequired")}`)
23
+ },
24
+ }
25
+ </script>
@@ -0,0 +1,114 @@
1
+ <template>
2
+ <FieldWrapper :field :rule v-model="modelValue">
3
+ <NPopover trigger="manual" v-model:show="showPopover" :onClickoutside>
4
+ <template #trigger>
5
+ <NInput :inputProps="{ class: 'IconPickerInput' }" @focus="showPopover = true"
6
+ v-model:value="searchQuery" :default-value="modelValue" :placeholder="t(field.key)" clearable
7
+ v-bind="field.inputProps
8
+ ? typeof field.inputProps === 'function'
9
+ ? field.inputProps(modelValue) ?? {}
10
+ : field.inputProps
11
+ : {}">
12
+ <template #prefix>
13
+ <Icon :name="`tabler:${modelValue ?? 'question-mark'}`" />
14
+ </template>
15
+
16
+ <template #suffix>
17
+ <NIcon>
18
+ <Icon name="tabler:icons" />
19
+ </NIcon>
20
+ </template>
21
+ </NInput>
22
+ </template>
23
+ <NVirtualList v-if="formatedIconsList.length" style="max-height: 250px" :item-size="42" :distance="0"
24
+ :items="formatedIconsList" ignore-item-resize>
25
+ <template #default="{ item }: { item: { key: string; value: string } }">
26
+ <NButton :key="item.key" strong :secondary="modelValue === item.value"
27
+ :quaternary="modelValue !== item.value" round
28
+ :type="modelValue === item.value ? 'primary' : 'default'" @click="modelValue = item.value"
29
+ class="IconPickerItem">
30
+ <template #icon>
31
+ <Icon :name="`tabler:${item.value}`" />
32
+ </template>
33
+ <span> {{ item.value }}</span>
34
+ </NButton>
35
+ </template>
36
+ </NVirtualList>
37
+ <NEmpty v-else />
38
+ </NPopover>
39
+ </FieldWrapper>
40
+ </template>
41
+
42
+ <script lang="ts" setup>
43
+ import type { FormItemRule } from "naive-ui"
44
+
45
+ const { field } = defineProps<{ field: Field }>()
46
+ const searchQuery = ref<string>()
47
+ const modelValue = defineModel<string>()
48
+
49
+ const rule: FormItemRule = {
50
+ required: field.required,
51
+ validator: async () => {
52
+ await nextTick()
53
+ return fieldValidator(field, modelValue.value)
54
+ },
55
+ }
56
+ function IconsListSingle(icon: string) {
57
+ return {
58
+ key: icon.replace("tabler:", ""),
59
+ value: icon.replace("tabler:", ""),
60
+ }
61
+ }
62
+ const formatedIconsList = ref<
63
+ {
64
+ key: string
65
+ value: string
66
+ }[]
67
+ >([])
68
+
69
+ watch(
70
+ searchQuery,
71
+ (newValue) => {
72
+ if (newValue !== modelValue.value) getIconsList(newValue)
73
+ },
74
+ { immediate: true },
75
+ )
76
+
77
+ async function getIconsList(search?: string) {
78
+ if (!search) {
79
+ formatedIconsList.value = []
80
+ return // Return an empty list if no search query is provided
81
+ }
82
+
83
+ try {
84
+ const response = await $fetch<{ icons: string[] }>(
85
+ `https://api.iconify.design/search?prefix=tabler&query=${search}`,
86
+ )
87
+ const icons = response.icons || []
88
+ formatedIconsList.value = icons.map(IconsListSingle)
89
+ } catch (error) {
90
+ console.error("Error fetching icons:", error)
91
+ formatedIconsList.value = []
92
+ }
93
+ }
94
+ const showPopover = ref(false)
95
+ watch(modelValue, (newValue) => {
96
+ if (newValue) {
97
+ searchQuery.value = newValue
98
+ }
99
+ })
100
+ function onClickoutside(event: MouseEvent) {
101
+ if (!(event.target as HTMLElement).classList.contains("IconPickerInput"))
102
+ showPopover.value = false
103
+ }
104
+ </script>
105
+
106
+ <style scoped>
107
+ .IconPickerItem {
108
+ display: flex;
109
+ justify-content: start;
110
+ width: 100%;
111
+ height: 42px;
112
+ margin-top: 5px;
113
+ }
114
+ </style>