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,444 @@
1
+ import type { TagColor } from "naive-ui/es/tag/src/common-props";
2
+ import { default as t } from "./Translation/translate";
3
+ import { isArrayOfObjects } from "inibase/utils";
4
+
5
+ export { default as translationLanguages } from "./Translation/languages";
6
+ export { default as defineTranslation } from "./Translation/define";
7
+ export { default as fetchTranslation } from "./Translation/fetch";
8
+ export { default as t } from "./Translation/translate";
9
+ export { default as loadCoreTranslations } from "./Translation/loadCoreTranslations";
10
+
11
+ // Add a small deepClone helper
12
+ export function deepClone<T>(v: T): T | undefined {
13
+ if (v === undefined) return undefined;
14
+ try {
15
+ // Use structuredClone if available (preserves more types)
16
+ if (typeof (globalThis as any).structuredClone === "function")
17
+ return (globalThis as any).structuredClone(v);
18
+ } catch (e) {
19
+ // ignore and fallback
20
+ }
21
+ // Fallback
22
+ return JSON.parse(JSON.stringify(v)) as T;
23
+ }
24
+
25
+ export function debounce<T extends (...args: any[]) => void>(
26
+ func: T,
27
+ wait: number,
28
+ ): (...args: Parameters<T>) => void {
29
+ let timeout: ReturnType<typeof setTimeout>;
30
+ return (...args: Parameters<T>): void => {
31
+ clearTimeout(timeout);
32
+ timeout = setTimeout(() => func(...args), wait);
33
+ };
34
+ }
35
+
36
+ export function humanFileSize(bytes?: number) {
37
+ if (!bytes) bytes = 0;
38
+ const thresh = 1000;
39
+
40
+ if (Math.abs(bytes) < thresh) {
41
+ return `${bytes} B`;
42
+ }
43
+
44
+ const units = ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
45
+ let u = -1;
46
+ const r = 10 ** 2;
47
+
48
+ do {
49
+ bytes /= thresh;
50
+ ++u;
51
+ } while (
52
+ Math.round(Math.abs(bytes) * r) / r >= thresh &&
53
+ u < units.length - 1
54
+ );
55
+
56
+ return `${bytes.toFixed(2)} ${t(`units.${units[u]}`)}`;
57
+ }
58
+
59
+ export function hexToRGB(hex: string): number[] {
60
+ const r = Number.parseInt(hex.slice(1, 3), 16);
61
+ const g = Number.parseInt(hex.slice(3, 5), 16);
62
+ const b = Number.parseInt(hex.slice(5, 7), 16);
63
+ return [r, g, b];
64
+ }
65
+
66
+ export const randomID = (): string => Math.random().toString(36).substring(2);
67
+
68
+ export function getColorObj(
69
+ value: string | number,
70
+ fieldOptions: [string | number, string][],
71
+ ): TagColor {
72
+ const option = fieldOptions.find(([label]) => label === value);
73
+ if (option) {
74
+ return {
75
+ color: option[1],
76
+ borderColor: option[1],
77
+ textColor: getContrastColor(option[1]),
78
+ };
79
+ }
80
+ return {};
81
+ }
82
+
83
+ function getContrastColor(hex: string) {
84
+ const [r, g, b] = hexToRGB(hex);
85
+
86
+ if (r === undefined || g === undefined || b === undefined) return "black";
87
+
88
+ const brightness = (r * 299 + g * 587 + b * 114) / 1000;
89
+ return brightness > 128 ? "black" : "white";
90
+ }
91
+
92
+ export function handleSelectedSchemaType(type: string) {
93
+ switch (type) {
94
+ case "textarea":
95
+ return {
96
+ type: "string",
97
+ subType: "textarea",
98
+ };
99
+ case "role":
100
+ return {
101
+ type: "string",
102
+ subType: "role",
103
+ };
104
+ case "asset":
105
+ return {
106
+ type: "table",
107
+ table: "assets",
108
+ };
109
+ case "array-asset":
110
+ return {
111
+ type: "array",
112
+ children: "table",
113
+ table: "assets",
114
+ };
115
+ case "array-table":
116
+ return {
117
+ type: "array",
118
+ children: "table",
119
+ subType: "table",
120
+ };
121
+ case "tags":
122
+ return {
123
+ type: "array",
124
+ subType: "tags",
125
+ };
126
+ case "select":
127
+ return {
128
+ type: ["string", "number"],
129
+ subType: "select",
130
+ };
131
+ case "multiple":
132
+ return {
133
+ type: ["string"],
134
+ };
135
+ case "radio":
136
+ return {
137
+ type: ["string", "number"],
138
+ subType: "radio",
139
+ };
140
+ case "checkbox":
141
+ return {
142
+ type: "array",
143
+ children: ["string", "number"],
144
+ subType: "checkbox",
145
+ };
146
+ case "array-select":
147
+ return {
148
+ type: "array",
149
+ children: ["string", "number"],
150
+ subType: "select",
151
+ };
152
+ case "color":
153
+ return {
154
+ type: "string",
155
+ subType: "color",
156
+ };
157
+ case "date-datetime":
158
+ return {
159
+ type: "date",
160
+ date: "datetime",
161
+ };
162
+ case "date-daterange":
163
+ return {
164
+ type: "date",
165
+ date: "daterange",
166
+ };
167
+ case "date-datetimerange":
168
+ return {
169
+ type: "date",
170
+ date: "datetimerange",
171
+ };
172
+ case "date-month":
173
+ return {
174
+ type: "date",
175
+ date: "month",
176
+ };
177
+ case "date-monthrange":
178
+ return {
179
+ type: "date",
180
+ date: "monthrange",
181
+ };
182
+ case "date-year":
183
+ return {
184
+ type: "date",
185
+ date: "year",
186
+ };
187
+ case "date-yearrange":
188
+ return {
189
+ type: "date",
190
+ date: "yearrange",
191
+ };
192
+ case "date-quarter":
193
+ return {
194
+ type: "date",
195
+ date: "quarter",
196
+ };
197
+ case "date-quarterrange":
198
+ return {
199
+ type: "date",
200
+ date: "quarterrange",
201
+ };
202
+ case "date-week":
203
+ return {
204
+ type: "date",
205
+ date: "week",
206
+ };
207
+ case "array":
208
+ case "object":
209
+ return {
210
+ type: type,
211
+ children: [],
212
+ };
213
+ default:
214
+ return { type };
215
+ }
216
+ }
217
+
218
+ export async function copyToClipboard(textToCopy: string | number) {
219
+ // Navigator clipboard api needs a secure context (https)
220
+ if (navigator.clipboard && window.isSecureContext) {
221
+ await navigator.clipboard.writeText(String(textToCopy));
222
+ } else {
223
+ // Use the 'out of viewport hidden text area' trick
224
+ const textArea = document.createElement("textarea");
225
+ textArea.value = String(textToCopy);
226
+
227
+ // Move textarea out of the viewport so it's not visible
228
+ textArea.style.position = "absolute";
229
+ textArea.style.left = "-999999px";
230
+
231
+ document.body.prepend(textArea);
232
+ textArea.select();
233
+
234
+ try {
235
+ document.execCommand("copy");
236
+ } catch (error) {
237
+ console.error(error);
238
+ } finally {
239
+ textArea.remove();
240
+ }
241
+ }
242
+ }
243
+
244
+ export function comparisonOperatorOptions() {
245
+ return [
246
+ {
247
+ label: t("ComparisonOperator.equalTo"),
248
+ value: "=",
249
+ },
250
+ {
251
+ label: t("ComparisonOperator.notEqualTo"),
252
+ value: "!=",
253
+ },
254
+ {
255
+ label: t("ComparisonOperator.contains"),
256
+ value: "*",
257
+ },
258
+ {
259
+ label: t("ComparisonOperator.doesNotContain"),
260
+ value: "!*",
261
+ },
262
+ {
263
+ label: t("ComparisonOperator.isOneOf"),
264
+ value: "[]",
265
+ },
266
+ {
267
+ label: t("ComparisonOperator.isNotOneOf"),
268
+ value: "![]",
269
+ },
270
+ {
271
+ label: t("ComparisonOperator.greaterThan"),
272
+ value: ">",
273
+ },
274
+ {
275
+ label: t("ComparisonOperator.greaterOrEqualTo"),
276
+ value: ">=",
277
+ },
278
+ {
279
+ label: t("ComparisonOperator.lessThan"),
280
+ value: "<",
281
+ },
282
+ {
283
+ label: t("ComparisonOperator.lessOrEqualTo"),
284
+ value: "<=",
285
+ },
286
+ ];
287
+ }
288
+
289
+ export function checkFieldType(
290
+ fieldType: string | string[],
291
+ compareAtType: string | string[],
292
+ ) {
293
+ if (Array.isArray(fieldType))
294
+ return fieldType.some((type) =>
295
+ Array.isArray(compareAtType)
296
+ ? compareAtType.includes(type)
297
+ : compareAtType === type,
298
+ );
299
+
300
+ return Array.isArray(compareAtType)
301
+ ? compareAtType.includes(fieldType)
302
+ : compareAtType === fieldType;
303
+ }
304
+
305
+ export function getTableIcon(table: Table) {
306
+ if (table.icon) return `tabler:${table.icon}`;
307
+ switch (table.slug) {
308
+ case "assets":
309
+ return "tabler:folder";
310
+ case "translations":
311
+ return "tabler:language";
312
+ case "users":
313
+ return "tabler:users";
314
+ case "sessions":
315
+ return "tabler:fingerprint";
316
+ case "pages":
317
+ return "tabler:app-window";
318
+ case "blocks":
319
+ return "tabler:tournament";
320
+ case "templates":
321
+ return "tabler:template";
322
+ default:
323
+ return t(table.slug).charAt(0).toUpperCase();
324
+ }
325
+ }
326
+
327
+ export function getCollapseItemTitle(
328
+ field: Field,
329
+ item: Item,
330
+ index: number,
331
+ ): string {
332
+ const firstStringField = (field.children as Schema).find(({ type }) =>
333
+ typeof type === "string" ? type === "string" : type.includes("string"),
334
+ );
335
+ if (firstStringField && item[firstStringField.key])
336
+ return item[firstStringField.key];
337
+
338
+ return `${t(field.key)} ${index + 1}`;
339
+ }
340
+
341
+ export function getPath(
342
+ schema: Schema,
343
+ id: string | number | undefined,
344
+ supportWildcard = false,
345
+ listNumbers?: number | number[],
346
+ currentPath?: string,
347
+ ): string {
348
+ if (!id) return "";
349
+
350
+ for (const item of schema) {
351
+ const newPath = currentPath ? `${currentPath}.${item.key}` : item.key;
352
+
353
+ if (item.id === id) return newPath;
354
+
355
+ if (
356
+ item.type === "array" &&
357
+ item.children &&
358
+ isArrayOfObjects(item.children)
359
+ ) {
360
+ let nestedPath = "";
361
+ if (listNumbers) {
362
+ if (!Array.isArray(listNumbers)) listNumbers = [listNumbers];
363
+ const firstItem = listNumbers.shift();
364
+ nestedPath = getPath(
365
+ item.children,
366
+ id,
367
+ supportWildcard,
368
+ listNumbers,
369
+ newPath + (supportWildcard ? ".*" : ".") + firstItem,
370
+ );
371
+ } else
372
+ nestedPath = getPath(
373
+ item.children,
374
+ id,
375
+ supportWildcard,
376
+ undefined,
377
+ newPath + (supportWildcard ? ".*" : "."),
378
+ );
379
+ if (nestedPath) return nestedPath;
380
+ } else if (item.children && isArrayOfObjects(item.children)) {
381
+ const nestedPath = getPath(
382
+ item.children,
383
+ id,
384
+ supportWildcard,
385
+ undefined,
386
+ newPath,
387
+ );
388
+ if (nestedPath) return nestedPath;
389
+ }
390
+ }
391
+
392
+ return "";
393
+ }
394
+
395
+ export const imageExtensions = [
396
+ "png",
397
+ "jpg",
398
+ "jpeg",
399
+ "gif",
400
+ "webp",
401
+ "svg",
402
+ "bmp",
403
+ "tiff",
404
+ // "heic",
405
+ "avif",
406
+ "svg",
407
+ ];
408
+
409
+ export const videoExtensions = ["mp4", "webm", "ogg", "mov", "avi", "mkv"];
410
+
411
+ export const wordExtensions = ["doc", "docx"];
412
+ export const spreadsheetExtensions = ["xls", "xlsx"];
413
+ export const presentationExtensions = ["ppt", "pptx"];
414
+ export const officeExtensions = [
415
+ ...wordExtensions,
416
+ ...spreadsheetExtensions,
417
+ ...presentationExtensions,
418
+ ];
419
+
420
+ export const documentExtensions = ["pdf", ...officeExtensions];
421
+
422
+ /**
423
+ * Extracts the file name and extension from a file path or file name string.
424
+ * Works in browsers (no Node.js dependencies).
425
+ */
426
+ export function getFileNameAndExtension(path: string): {
427
+ name: string;
428
+ extension: string;
429
+ } {
430
+ // Extract just the file name (handles both "/" and "\" separators)
431
+ const fullName = path.split(/[/\\]/).pop() || path;
432
+
433
+ // Handle files without any dots
434
+ if (!fullName.includes(".")) {
435
+ return { name: fullName, extension: "" };
436
+ }
437
+
438
+ // Find the last dot to split name and extension
439
+ const lastDotIndex = fullName.lastIndexOf(".");
440
+ const name = fullName.substring(0, lastDotIndex);
441
+ const extension = fullName.substring(lastDotIndex + 1).toLowerCase(); // excludes the "."
442
+
443
+ return { name, extension };
444
+ }
@@ -0,0 +1,118 @@
1
+ import Inison from "inison";
2
+
3
+ function rollbackNestedWidthIncrements(index: number) {
4
+ const Drawers = useState<DrawerRef>("drawers", () => []);
5
+ for (let i = 0; i < index; i++) {
6
+ const drawer = Drawers.value[i];
7
+ if (!drawer || typeof drawer.width !== "number") continue;
8
+ const lastIncrement = drawer.nestedWidthIncrements?.pop();
9
+ if (!lastIncrement) continue;
10
+ drawer.width = Math.max(251, drawer.width - lastIncrement);
11
+ }
12
+ }
13
+
14
+ async function loadDrawer(index: number) {
15
+ const Drawers = useState<DrawerRef>("drawers", () => []);
16
+ const drawer = Drawers.value[index];
17
+ const database = useState<Database>("database");
18
+
19
+ const sessionID = useScopedCookie<string>("sid", database.value?.slug);
20
+
21
+ if (!drawer) return;
22
+
23
+ if (drawer.id) {
24
+ const Loading = useState<Record<string, boolean>>("Loading", () => ({}));
25
+ const config = useRuntimeConfig();
26
+ const Language = useScopedCookie<LanguagesType>("language", database.value?.slug);
27
+ const currentItem = useState<Item>("currentItem");
28
+
29
+ drawer.show = false;
30
+ const key = `Drawer_${drawer.table}_${drawer.id}`;
31
+ Loading.value[key] = true;
32
+
33
+ const table = database.value.tables?.find(
34
+ ({ slug }) => slug === drawer.table,
35
+ );
36
+ const response = await $fetch<apiResponse>(
37
+ `${config.public.apiBase}${database.value.slug}/${drawer.table}/${drawer.id}`,
38
+ {
39
+ params: {
40
+ options: Inison.stringify({
41
+ columns: table?.columns,
42
+ }),
43
+ locale: Language.value,
44
+ [`${database.value.slug}_sid`]: sessionID.value,
45
+ },
46
+ credentials: "include",
47
+ },
48
+ );
49
+
50
+ Loading.value[key] = false;
51
+ if (!response.result) {
52
+ rollbackNestedWidthIncrements(index);
53
+ Drawers.value.splice(index, 1);
54
+ window.$message.error(response.message);
55
+ return;
56
+ }
57
+ drawer.data = { ...response.result, ...(drawer.data ?? {}) };
58
+ currentItem.value = drawer.data as Item;
59
+ }
60
+
61
+ drawer.show = true;
62
+ }
63
+
64
+ export default function (
65
+ table: string,
66
+ id?: string | number,
67
+ data: Partial<Item> = {},
68
+ mode: "view" | "edit" = "edit",
69
+ ) {
70
+ const Drawers = useState<DrawerRef>("drawers", () => []);
71
+ const defaultWidth = useCookie<number | string>("width", {
72
+ sameSite: true,
73
+ });
74
+ const index = Drawers.value.findIndex(
75
+ (d) => d.table === table && d.id === id,
76
+ );
77
+
78
+ let width = defaultWidth.value ?? 251;
79
+ if (index === -1) {
80
+ if (Drawers.value.length) {
81
+ for (let index = 0; index < Drawers.value.length; index++) {
82
+ const drawer = Drawers.value[index];
83
+ if (!drawer) continue;
84
+ if (typeof drawer.width === "string") continue;
85
+ if (drawer.width) width = drawer.width;
86
+ if (typeof width === "number") {
87
+ const previousWidth =
88
+ typeof drawer.width === "number" ? drawer.width : width;
89
+ const nextWidth = Math.min(
90
+ window.screen.width,
91
+ previousWidth + previousWidth * 0.1,
92
+ );
93
+ drawer.width = nextWidth;
94
+ if (!drawer.nestedWidthIncrements)
95
+ drawer.nestedWidthIncrements = [];
96
+ drawer.nestedWidthIncrements.push(nextWidth - previousWidth);
97
+ }
98
+ }
99
+ }
100
+ // If drawer doesn't exist, create it
101
+ Drawers.value.push({
102
+ id,
103
+ table,
104
+ data,
105
+ show: false,
106
+ schema: undefined,
107
+ width, // Set width based on nesting level
108
+ mode,
109
+ });
110
+
111
+ loadDrawer(Drawers.value.length - 1);
112
+ } else if (Drawers.value[index]) {
113
+ // If drawer already exists, just update it
114
+ Drawers.value[index].mode = mode;
115
+ Drawers.value[index].show = true;
116
+ if (!Drawers.value[index].width) Drawers.value[index].width = width;
117
+ }
118
+ }