@edgedev/create-edge-app 1.1.23 → 1.1.26

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 (116) hide show
  1. package/.env +1 -0
  2. package/.env.dev +1 -0
  3. package/README.md +55 -20
  4. package/{agent.md → agents.md} +2 -0
  5. package/bin/cli.js +6 -6
  6. package/edge/components/auth/login.vue +384 -0
  7. package/edge/components/auth/register.vue +396 -0
  8. package/edge/components/auth.vue +108 -0
  9. package/edge/components/autoFileUpload.vue +215 -0
  10. package/edge/components/billing.vue +8 -0
  11. package/edge/components/buttonDivider.vue +14 -0
  12. package/edge/components/chip.vue +34 -0
  13. package/edge/components/clipboardButton.vue +42 -0
  14. package/edge/components/cms/block.vue +529 -0
  15. package/edge/components/cms/blockApi.vue +212 -0
  16. package/edge/components/cms/blockEditor.vue +725 -0
  17. package/edge/components/cms/blockInput.vue +66 -0
  18. package/edge/components/cms/blockPicker.vue +486 -0
  19. package/edge/components/cms/blockRender.vue +78 -0
  20. package/edge/components/cms/blockSheetContent.vue +28 -0
  21. package/edge/components/cms/codeEditor.vue +466 -0
  22. package/edge/components/cms/fontUpload.vue +327 -0
  23. package/edge/components/cms/htmlContent.vue +807 -0
  24. package/edge/components/cms/init_blocks/api_with_subarrays.html +17 -0
  25. package/edge/components/cms/init_blocks/array_with_collection.html +7 -0
  26. package/edge/components/cms/init_blocks/array_with_objects.html +7 -0
  27. package/edge/components/cms/init_blocks/carousel.html +103 -0
  28. package/edge/components/cms/init_blocks/contact_us.html +69 -0
  29. package/edge/components/cms/init_blocks/content_with_left_image.html +27 -0
  30. package/edge/components/cms/init_blocks/footer.html +24 -0
  31. package/edge/components/cms/init_blocks/header_divider.html +7 -0
  32. package/edge/components/cms/init_blocks/hero.html +35 -0
  33. package/edge/components/cms/init_blocks/hero_carousel.html +52 -0
  34. package/edge/components/cms/init_blocks/newsletter.html +117 -0
  35. package/edge/components/cms/init_blocks/post_content.html +7 -0
  36. package/edge/components/cms/init_blocks/post_title_header.html +21 -0
  37. package/edge/components/cms/init_blocks/posts_list.html +20 -0
  38. package/edge/components/cms/init_blocks/properties_showcase.html +100 -0
  39. package/edge/components/cms/init_blocks/property_carousel.html +59 -0
  40. package/edge/components/cms/init_blocks/property_detail.html +112 -0
  41. package/edge/components/cms/init_blocks/property_detail_header.html +34 -0
  42. package/edge/components/cms/init_blocks/property_results.html +137 -0
  43. package/edge/components/cms/init_blocks/property_search.html +75 -0
  44. package/edge/components/cms/init_blocks/simple_array.html +7 -0
  45. package/edge/components/cms/mediaCard.vue +116 -0
  46. package/edge/components/cms/mediaManager.vue +386 -0
  47. package/edge/components/cms/menu.vue +1103 -0
  48. package/edge/components/cms/optionsSelect.vue +107 -0
  49. package/edge/components/cms/page.vue +1785 -0
  50. package/edge/components/cms/posts.vue +1083 -0
  51. package/edge/components/cms/site.vue +1298 -0
  52. package/edge/components/cms/themeDefaultMenu.vue +548 -0
  53. package/edge/components/cms/themeEditor.vue +426 -0
  54. package/edge/components/dashboard.vue +776 -0
  55. package/edge/components/editor.vue +671 -0
  56. package/edge/components/fileTree.vue +72 -0
  57. package/edge/components/files.vue +89 -0
  58. package/edge/components/formSubtypes/myOrgs.vue +214 -0
  59. package/edge/components/formSubtypes/users.vue +336 -0
  60. package/edge/components/functionChips.vue +57 -0
  61. package/edge/components/gError.vue +98 -0
  62. package/edge/components/gHelper.vue +67 -0
  63. package/edge/components/gInput.vue +1331 -0
  64. package/edge/components/loggingIn.vue +41 -0
  65. package/edge/components/menu.vue +137 -0
  66. package/edge/components/menuContent.vue +132 -0
  67. package/edge/components/myAccount.vue +317 -0
  68. package/edge/components/myOrganizations.vue +75 -0
  69. package/edge/components/myProfile.vue +122 -0
  70. package/edge/components/orgSwitcher.vue +25 -0
  71. package/edge/components/organizationMembers.vue +522 -0
  72. package/edge/components/organizationSettings.vue +271 -0
  73. package/edge/components/shad/breadcrumbs.vue +35 -0
  74. package/edge/components/shad/button.vue +43 -0
  75. package/edge/components/shad/checkbox.vue +73 -0
  76. package/edge/components/shad/combobox.vue +238 -0
  77. package/edge/components/shad/datepicker.vue +184 -0
  78. package/edge/components/shad/dialog.vue +32 -0
  79. package/edge/components/shad/dropdownMenu.vue +54 -0
  80. package/edge/components/shad/dropdownMenuItem.vue +21 -0
  81. package/edge/components/shad/form.vue +59 -0
  82. package/edge/components/shad/html.vue +877 -0
  83. package/edge/components/shad/input.vue +139 -0
  84. package/edge/components/shad/number.vue +109 -0
  85. package/edge/components/shad/select.vue +151 -0
  86. package/edge/components/shad/selectTags.vue +278 -0
  87. package/edge/components/shad/switch.vue +67 -0
  88. package/edge/components/shad/tags.vue +137 -0
  89. package/edge/components/shad/textarea.vue +102 -0
  90. package/edge/components/shad/typeMoney.vue +167 -0
  91. package/edge/components/sideBar.vue +288 -0
  92. package/edge/components/sideBarContent.vue +268 -0
  93. package/edge/components/sidebarProvider.vue +33 -0
  94. package/edge/components/tooltip.vue +16 -0
  95. package/edge/components/userMenu.vue +148 -0
  96. package/edge/components/v/alert.vue +59 -0
  97. package/edge/components/v/alertTitle.vue +18 -0
  98. package/edge/components/v/card.vue +53 -0
  99. package/edge/components/v/cardActions.vue +18 -0
  100. package/edge/components/v/cardText.vue +18 -0
  101. package/edge/components/v/cardTitle.vue +20 -0
  102. package/edge/components/v/col.vue +56 -0
  103. package/edge/components/v/list.vue +46 -0
  104. package/edge/components/v/listItem.vue +26 -0
  105. package/edge/components/v/listItemTitle.vue +18 -0
  106. package/edge/components/v/row.vue +42 -0
  107. package/edge/components/v/toolbar.vue +24 -0
  108. package/edge/composables/global.ts +519 -0
  109. package/edge-pull.sh +2 -0
  110. package/edge-push.sh +1 -0
  111. package/edge-status.sh +14 -0
  112. package/firebase.json +5 -2
  113. package/firebase_init.sh +21 -6
  114. package/package.json +1 -1
  115. package/plugins/firebase.client.ts +1 -0
  116. package/edge-components-install.sh +0 -1
@@ -0,0 +1,426 @@
1
+ <script setup>
2
+ import { toTypedSchema } from '@vee-validate/zod'
3
+ import * as z from 'zod'
4
+ const props = defineProps({
5
+ themeId: {
6
+ type: String,
7
+ required: true,
8
+ },
9
+ })
10
+
11
+ const emit = defineEmits(['head'])
12
+ const edgeFirebase = inject('edgeFirebase')
13
+ const state = reactive({
14
+ filter: '',
15
+ workingDoc: {},
16
+ newDocs: {
17
+ themes: {
18
+ name: { value: '' },
19
+ headJSON: {
20
+ value: `{
21
+ "link": [
22
+ {
23
+ "rel": "preconnect",
24
+ "href": "https://fonts.googleapis.com"
25
+ },
26
+ {
27
+ "rel": "preconnect",
28
+ "href": "https://fonts.gstatic.com",
29
+ "crossorigin": ""
30
+ },
31
+ {
32
+ "rel": "stylesheet",
33
+ "href": "https://fonts.googleapis.com/css2?family=Overpass:wght@400;700&family=Kode+Mono:wght@400;700&display=swap"
34
+ }
35
+ ]
36
+ }`,
37
+ },
38
+ theme: {
39
+ value: `{
40
+ "extend": {
41
+ "colors": {
42
+ "brand": "#3B82F6",
43
+ "accent": "#F59E0B",
44
+ "surface": "#FAFAFA",
45
+ "subtle": "#F3F4F6",
46
+ "text": "#1F2937",
47
+ "muted": "#9CA3AF",
48
+ "success": "#22C55E",
49
+ "danger": "#EF4444",
50
+ "border": "#E5E7EB",
51
+ "ring": "#93C5FD",
52
+ "link": "#3B82F6",
53
+ "linkHover": "#1D4ED8",
54
+ "navBg": "#000000",
55
+ "navText": "#FFFFFF",
56
+ "navMuted": "#6B7280",
57
+ "navBorder": "",
58
+ "navActive": "#3B82F6",
59
+ "navHoverBg": "",
60
+ "navActiveBg": ""
61
+ },
62
+ "fontFamily": {
63
+ "sans": ["Overpass", "sans-serif"],
64
+ "serif": ["Kode Mono", "monospace"],
65
+ "mono": ["Overpass", "sans-serif"],
66
+ "brand": ["Kode Mono", "monospace"]
67
+ }
68
+ },
69
+ "apply": {},
70
+ "slots": {},
71
+ "variants": {
72
+ "light": {
73
+ "apply": {}
74
+ },
75
+ "dark": {
76
+ "apply": {},
77
+ "slots": {}
78
+ }
79
+ }
80
+ }`,
81
+ },
82
+ version: 1,
83
+ defaultPages: { value: [] },
84
+ defaultMenus: {
85
+ value: {
86
+ 'Site Root': [],
87
+ 'Not In Menu': [],
88
+ },
89
+ },
90
+ },
91
+ },
92
+ mounted: false,
93
+ loading: false,
94
+ })
95
+
96
+ const blockSchema = toTypedSchema(z.object({
97
+ name: z.string({
98
+ required_error: 'Name is required',
99
+ }).min(1, { message: 'Name is required' }),
100
+ }))
101
+
102
+ onMounted(() => {
103
+ // state.mounted = true
104
+ })
105
+
106
+ const headObject = computed(() => {
107
+ try {
108
+ return JSON.parse(state.workingDoc.headJSON || '{}')
109
+ }
110
+ catch (e) {
111
+ return {}
112
+ }
113
+ })
114
+
115
+ watch(headObject, (newHeadElements) => {
116
+ emit('head', newHeadElements)
117
+ }, { immediate: true, deep: true })
118
+
119
+ const sites = computed(() => {
120
+ return Object.values(edgeFirebase.data?.[`${edgeGlobal.edgeState.organizationDocPath}/sites`] || {})
121
+ })
122
+
123
+ const templatePages = computed(() => {
124
+ return edgeFirebase.data?.[`${edgeGlobal.edgeState.organizationDocPath}/sites/templates/pages`] || {}
125
+ })
126
+
127
+ const templatePageName = (pageId, fallback) => {
128
+ return templatePages.value?.[pageId]?.name || fallback || 'Untitled Page'
129
+ }
130
+
131
+ const DEFAULT_MENU_KEYS = ['Site Root', 'Not In Menu']
132
+
133
+ const ensureDefaultPagesArray = (doc = state.workingDoc) => {
134
+ if (!doc)
135
+ return []
136
+ if (!Array.isArray(doc.defaultPages))
137
+ doc.defaultPages = []
138
+ return doc.defaultPages
139
+ }
140
+
141
+ const ensureDefaultMenusObject = (doc = state.workingDoc) => {
142
+ if (!doc)
143
+ return {}
144
+ if (!doc.defaultMenus || typeof doc.defaultMenus !== 'object' || Array.isArray(doc.defaultMenus))
145
+ doc.defaultMenus = {}
146
+ for (const key of DEFAULT_MENU_KEYS) {
147
+ if (!Array.isArray(doc.defaultMenus[key]))
148
+ doc.defaultMenus[key] = []
149
+ }
150
+ return doc.defaultMenus
151
+ }
152
+
153
+ const collectTemplateIdsFromMenus = (menus = {}) => {
154
+ const ids = new Set()
155
+ const traverse = (items = []) => {
156
+ if (!Array.isArray(items))
157
+ return
158
+ for (const entry of items) {
159
+ if (!entry || entry.item === undefined || entry.item === null)
160
+ continue
161
+ if (typeof entry.item === 'string' && entry.item)
162
+ ids.add(entry.item)
163
+ else if (typeof entry.item === 'object') {
164
+ for (const nested of Object.values(entry.item || {}))
165
+ traverse(nested)
166
+ }
167
+ }
168
+ }
169
+ for (const key of Object.keys(menus))
170
+ traverse(menus[key])
171
+ return ids
172
+ }
173
+
174
+ const collectMenuSlugs = (menus = {}) => {
175
+ const slugs = new Set()
176
+ const traverse = (items = []) => {
177
+ if (!Array.isArray(items))
178
+ return
179
+ for (const entry of items) {
180
+ if (!entry || entry.item === undefined || entry.item === null)
181
+ continue
182
+ if (typeof entry.item === 'string' && entry.name)
183
+ slugs.add(entry.name)
184
+ else if (typeof entry.item === 'object') {
185
+ for (const nested of Object.values(entry.item || {}))
186
+ traverse(nested)
187
+ }
188
+ }
189
+ }
190
+ for (const key of Object.keys(menus))
191
+ traverse(menus[key])
192
+ return slugs
193
+ }
194
+
195
+ const slugify = (value) => {
196
+ return String(value || '')
197
+ .trim()
198
+ .toLowerCase()
199
+ .replace(/[^a-z0-9]+/g, '-')
200
+ .replace(/(^-|-$)+/g, '') || 'page'
201
+ }
202
+
203
+ const makeUniqueSlug = (value, existing = new Set()) => {
204
+ const base = slugify(value)
205
+ let candidate = base || 'page'
206
+ let suffix = 1
207
+ while (existing.has(candidate)) {
208
+ candidate = `${base}-${suffix}`
209
+ suffix += 1
210
+ }
211
+ return candidate
212
+ }
213
+
214
+ const hydrateMenusFromDefaultPages = (doc = state.workingDoc) => {
215
+ const menus = ensureDefaultMenusObject(doc)
216
+ const hasExistingMenus = Object.values(menus).some(items => Array.isArray(items) && items.length)
217
+ if (hasExistingMenus)
218
+ return
219
+ const defaults = ensureDefaultPagesArray(doc)
220
+ if (!defaults.length)
221
+ return
222
+ const existingSlugs = collectMenuSlugs(menus)
223
+ menus['Site Root'] = defaults.map((entry) => {
224
+ const slug = makeUniqueSlug(entry?.name || templatePageName(entry.pageId), existingSlugs)
225
+ existingSlugs.add(slug)
226
+ return {
227
+ name: slug,
228
+ item: entry.pageId,
229
+ }
230
+ })
231
+ }
232
+
233
+ const flattenMenusToDefaultPages = (menus = {}) => {
234
+ const collected = []
235
+ const traverse = (items = []) => {
236
+ if (!Array.isArray(items))
237
+ return
238
+ for (const entry of items) {
239
+ if (!entry || entry.item === undefined || entry.item === null)
240
+ continue
241
+ if (typeof entry.item === 'string' && entry.item) {
242
+ collected.push({
243
+ pageId: entry.item,
244
+ name: templatePageName(entry.item, entry.name),
245
+ })
246
+ }
247
+ else if (typeof entry.item === 'object') {
248
+ for (const nested of Object.values(entry.item || {}))
249
+ traverse(nested)
250
+ }
251
+ }
252
+ }
253
+ for (const key of DEFAULT_MENU_KEYS)
254
+ traverse(menus[key])
255
+ return collected
256
+ }
257
+
258
+ const syncDefaultPagesFromMenus = () => {
259
+ if (!state.workingDoc)
260
+ return
261
+ const menus = ensureDefaultMenusObject()
262
+ const normalized = flattenMenusToDefaultPages(menus)
263
+ const defaults = ensureDefaultPagesArray()
264
+ const sameLength = defaults.length === normalized.length
265
+ const sameOrder = sameLength && defaults.every((entry, index) => entry.pageId === normalized[index].pageId)
266
+ if (sameLength && sameOrder)
267
+ return
268
+ defaults.splice(0, defaults.length, ...normalized)
269
+ }
270
+
271
+ const editorDocUpdates = (workingDoc) => {
272
+ state.workingDoc = workingDoc
273
+ ensureDefaultPagesArray(state.workingDoc)
274
+ ensureDefaultMenusObject(state.workingDoc)
275
+ hydrateMenusFromDefaultPages(state.workingDoc)
276
+ syncDefaultPagesFromMenus()
277
+ }
278
+
279
+ const templatePageOptions = computed(() => {
280
+ return Object.entries(templatePages.value)
281
+ .map(([value, doc]) => ({
282
+ value,
283
+ label: doc?.name || 'Untitled Page',
284
+ }))
285
+ .sort((a, b) => a.label.localeCompare(b.label))
286
+ })
287
+
288
+ watch (sites, async (newSites) => {
289
+ state.loading = true
290
+ if (!edgeGlobal.edgeState.blockEditorSite && newSites.length > 0) {
291
+ edgeGlobal.edgeState.blockEditorSite = newSites[0].docId
292
+ }
293
+ await nextTick()
294
+ state.loading = false
295
+ }, { immediate: true, deep: true })
296
+
297
+ watch(templatePages, (pages) => {
298
+ const selected = ensureDefaultPagesArray()
299
+ for (const entry of selected) {
300
+ const latestName = pages?.[entry.pageId]?.name
301
+ if (latestName && entry.name !== latestName)
302
+ entry.name = latestName
303
+ }
304
+ }, { deep: true })
305
+
306
+ watch(() => state.workingDoc?.defaultMenus, () => {
307
+ if (!state.workingDoc)
308
+ return
309
+ syncDefaultPagesFromMenus()
310
+ }, { deep: true })
311
+
312
+ onBeforeMount(async () => {
313
+ if (!edgeFirebase.data?.[`organizations/${edgeGlobal.edgeState.currentOrganization}/sites`]) {
314
+ await edgeFirebase.startSnapshot(`organizations/${edgeGlobal.edgeState.currentOrganization}/sites`)
315
+ }
316
+ if (!edgeFirebase.data?.[`organizations/${edgeGlobal.edgeState.currentOrganization}/sites/templates/pages`]) {
317
+ await edgeFirebase.startSnapshot(`organizations/${edgeGlobal.edgeState.currentOrganization}/sites/templates/pages`)
318
+ }
319
+ state.mounted = true
320
+ })
321
+ </script>
322
+
323
+ <template>
324
+ <div
325
+ v-if="edgeGlobal.edgeState.organizationDocPath && state.mounted"
326
+ >
327
+ <edge-editor
328
+ collection="themes"
329
+ :doc-id="props.themeId"
330
+ :schema="blockSchema"
331
+ :new-doc-schema="state.newDocs.themes"
332
+ class="w-full mx-auto flex-1 bg-transparent flex flex-col border-none shadow-none"
333
+ :show-footer="false"
334
+ :no-close-after-save="true"
335
+ :working-doc-overrides="state.workingDoc"
336
+ @working-doc="editorDocUpdates"
337
+ >
338
+ <template #header-start="slotProps">
339
+ <FilePenLine class="mr-2" />
340
+ {{ slotProps.title }}
341
+ </template>
342
+ <template #header-center>
343
+ <div class="w-full flex gap-1 px-4">
344
+ <div class="w-full">
345
+ <edge-shad-select
346
+ v-if="!state.loading"
347
+ v-model="edgeGlobal.edgeState.blockEditorSite"
348
+ label="Preview Site"
349
+ name="site"
350
+ :items="sites.map(s => ({ title: s.name, name: s.docId }))"
351
+ placeholder="Select Site"
352
+ class="w-full"
353
+ />
354
+ </div>
355
+ </div>
356
+ </template>
357
+ <template #main="slotProps">
358
+ <div class="pt-4 flex flex-col gap-6 lg:flex-row">
359
+ <div class="lg:w-1/3 lg:max-w-sm w-full space-y-4">
360
+ <Card class="h-full">
361
+ <CardHeader class="pb-2">
362
+ <CardTitle class="text-base">
363
+ Default Template Pages
364
+ </CardTitle>
365
+ <CardDescription class="text-xs">
366
+ Choose which template pages are created for new sites and organize them into Site Menu or Not In Menu.
367
+ </CardDescription>
368
+ </CardHeader>
369
+ <CardContent>
370
+ <edge-cms-theme-default-menu
371
+ v-if="slotProps.workingDoc"
372
+ v-model="slotProps.workingDoc.defaultMenus"
373
+ :template-options="templatePageOptions"
374
+ :template-pages="templatePages"
375
+ />
376
+ </CardContent>
377
+ </Card>
378
+ </div>
379
+ <div class="flex-1 space-y-4">
380
+ <edge-shad-input
381
+ v-model="slotProps.workingDoc.name"
382
+ label="Theme Name"
383
+ name="name"
384
+ />
385
+ <div class="flex flex-col gap-4 xl:flex-row">
386
+ <div class="w-1/2">
387
+ <edge-cms-code-editor
388
+ v-model="slotProps.workingDoc.theme"
389
+ title="Theme JSON"
390
+ language="json"
391
+ name="content"
392
+ height="400px"
393
+ class="mb-4 w-full"
394
+ />
395
+ <edge-cms-font-upload
396
+ v-if="slotProps.workingDoc"
397
+ v-model:head-json="slotProps.workingDoc.headJSON"
398
+ :theme-id="props.themeId"
399
+ class="mb-4"
400
+ />
401
+ <edge-cms-code-editor
402
+ v-model="slotProps.workingDoc.headJSON"
403
+ title="Head JSON"
404
+ language="json"
405
+ name="headJSON"
406
+ height="400px"
407
+ class="mb-4 w-full"
408
+ />
409
+ </div>
410
+ <div class="w-1/2">
411
+ <div class="w-full mx-auto bg-white drop-shadow-[4px_4px_6px_rgba(0,0,0,0.5)] shadow-lg shadow-black/30">
412
+ <edge-cms-block-picker
413
+ :site-id="edgeGlobal.edgeState.blockEditorSite"
414
+ class="!h-[calc(100vh-220px)] overflow-y-auto"
415
+ list-only
416
+ :theme="JSON.parse(slotProps.workingDoc.theme)"
417
+ />
418
+ </div>
419
+ </div>
420
+ </div>
421
+ </div>
422
+ </div>
423
+ </template>
424
+ </edge-editor>
425
+ </div>
426
+ </template>