@kennofizet/apphub-frontend 0.1.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 (90) hide show
  1. package/README.md +84 -0
  2. package/package.json +31 -0
  3. package/src/api/coreApi.js +25 -0
  4. package/src/api/index.js +80 -0
  5. package/src/composables/createZoneContext.js +156 -0
  6. package/src/composables/useAppHubHostApi.js +24 -0
  7. package/src/composables/useAppHubZoneContext.js +11 -0
  8. package/src/composables/useDevOriginToggle.js +40 -0
  9. package/src/i18n/index.js +16 -0
  10. package/src/i18n/resolveLang.js +6 -0
  11. package/src/i18n/resolveTheme.js +30 -0
  12. package/src/i18n/translations/en.js +303 -0
  13. package/src/i18n/translations/vi.js +302 -0
  14. package/src/index.js +427 -0
  15. package/src/moduleStore.js +10 -0
  16. package/src/modules/app-store/components/AppHubAppStoreApp.vue +210 -0
  17. package/src/modules/app-store/components/AppHubAppStoreCard.vue +88 -0
  18. package/src/modules/app-store/components/AppHubAppStoreSettingsPanel.vue +266 -0
  19. package/src/modules/app-store/components/AppHubAppVersionHistory.vue +77 -0
  20. package/src/modules/app-store/components/AppHubDevReviewPanel.vue +206 -0
  21. package/src/modules/app-store/components/AppHubDraftStoreApp.vue +184 -0
  22. package/src/modules/app-store/components/AppHubDraftStoreCard.vue +116 -0
  23. package/src/modules/app-store/composables/useAppStore.js +206 -0
  24. package/src/modules/app-store/composables/useCatalogInfiniteScroll.js +47 -0
  25. package/src/modules/app-store/constants/catalogModes.js +2 -0
  26. package/src/modules/app-store/data/defaultCatalog.js +19 -0
  27. package/src/modules/app-store/index.js +9 -0
  28. package/src/modules/app-store/utils/normalizeCatalogApp.js +37 -0
  29. package/src/modules/desktop/components/AppHubDesktop.vue +1510 -0
  30. package/src/modules/desktop/components/AppHubDesktopDevOriginBar.vue +57 -0
  31. package/src/modules/desktop/components/AppHubDesktopDropLayer.vue +15 -0
  32. package/src/modules/desktop/components/AppHubDesktopDropTarget.vue +32 -0
  33. package/src/modules/desktop/components/AppHubDesktopIconContextMenu.vue +74 -0
  34. package/src/modules/desktop/components/AppHubDesktopIconFolder.vue +60 -0
  35. package/src/modules/desktop/components/AppHubDesktopIconGroup.vue +58 -0
  36. package/src/modules/desktop/components/AppHubDesktopIconInfoDialog.vue +33 -0
  37. package/src/modules/desktop/components/AppHubDesktopIconRenameDialog.vue +62 -0
  38. package/src/modules/desktop/components/AppHubDesktopSettings.vue +28 -0
  39. package/src/modules/desktop/components/AppHubDropInstallBadge.vue +65 -0
  40. package/src/modules/desktop/components/AppHubDuplicateAppDialog.vue +38 -0
  41. package/src/modules/desktop/components/AppHubGuideApp.vue +278 -0
  42. package/src/modules/desktop/components/AppHubOriginBlockScreen.vue +105 -0
  43. package/src/modules/desktop/components/AppHubOriginLoadingScreen.vue +23 -0
  44. package/src/modules/desktop/components/AppHubPlaceholderApp.vue +14 -0
  45. package/src/modules/desktop/components/AppHubSettingsApp.vue +319 -0
  46. package/src/modules/desktop/components/AppHubStartButton.vue +24 -0
  47. package/src/modules/desktop/components/AppHubStartMenu.vue +182 -0
  48. package/src/modules/desktop/components/AppHubTaskbarPins.vue +23 -0
  49. package/src/modules/desktop/components/settings/AppHubSettingsKeyboardPanel.vue +82 -0
  50. package/src/modules/desktop/components/settings/AppHubSettingsScreenPanel.vue +41 -0
  51. package/src/modules/desktop/components/settings/AppHubSettingsStartMenuPanel.vue +95 -0
  52. package/src/modules/desktop/composables/simulateInstallProgress.js +15 -0
  53. package/src/modules/desktop/composables/useDesktopDropInstall.js +272 -0
  54. package/src/modules/desktop/composables/useDesktopHubSettings.js +51 -0
  55. package/src/modules/desktop/composables/useDesktopIconDrag.js +207 -0
  56. package/src/modules/desktop/composables/useDesktopShell.js +335 -0
  57. package/src/modules/desktop/data/builtinApps.js +77 -0
  58. package/src/modules/desktop/index.js +12 -0
  59. package/src/modules/desktop/styles/desktop.css +3104 -0
  60. package/src/modules/desktop/styles/theme.css +616 -0
  61. package/src/modules/desktop/utils/desktopGrid.js +43 -0
  62. package/src/modules/desktop/utils/desktopIconGroups.js +103 -0
  63. package/src/modules/desktop/utils/desktopSession.js +40 -0
  64. package/src/modules/desktop/utils/desktopSettings.js +37 -0
  65. package/src/modules/desktop/utils/dropPackageParser.js +140 -0
  66. package/src/modules/desktop/utils/duplicateAppUtils.js +28 -0
  67. package/src/modules/desktop/utils/hubKeyboardSettings.js +63 -0
  68. package/src/modules/desktop/utils/recentApps.js +148 -0
  69. package/src/modules/desktop/utils/startMenuFavorites.js +100 -0
  70. package/src/modules/desktop/utils/startMenuPins.js +90 -0
  71. package/src/modules/notifications/components/AppHubDesktopNotifications.vue +54 -0
  72. package/src/modules/notifications/composables/createDesktopNotifications.js +86 -0
  73. package/src/modules/notifications/index.js +9 -0
  74. package/src/modules/notifications/styles/notifications.css +118 -0
  75. package/src/modules/notifications/utils/parseApiError.js +29 -0
  76. package/src/modules/runner/components/AppHubRunner.vue +292 -0
  77. package/src/modules/runner/index.js +1 -0
  78. package/src/modules/window-manager/components/AppHubWindowFrame.vue +224 -0
  79. package/src/modules/window-manager/composables/useWindowManager.js +652 -0
  80. package/src/modules/window-manager/index.js +7 -0
  81. package/src/modules/window-manager/utils/sessionLayout.js +28 -0
  82. package/src/modules/window-manager/utils/windowLayout.js +236 -0
  83. package/src/modules/window-manager/utils/windowSnap.js +146 -0
  84. package/src/utils/bootstrapCache.js +47 -0
  85. package/src/utils/devOriginSettings.js +22 -0
  86. package/src/utils/launchUrl.js +111 -0
  87. package/src/utils/originSafety.js +267 -0
  88. package/src/utils/safeStorage.js +191 -0
  89. package/src/utils/semver.js +30 -0
  90. package/src/utils/zoneContext.js +38 -0
@@ -0,0 +1,335 @@
1
+ import { computed, reactive, watchEffect } from 'vue'
2
+ import { AppHubAppStoreApp } from '../../app-store/index.js'
3
+ import { AppHubDraftStoreApp } from '../../app-store/index.js'
4
+ import { BUILTIN_APP_STORE_ID, getBuiltinDesktopApps, getTaskbarBuiltinApps } from '../data/builtinApps.js'
5
+ import AppHubGuideApp from '../components/AppHubGuideApp.vue'
6
+ import AppHubSettingsApp from '../components/AppHubSettingsApp.vue'
7
+ import AppHubPlaceholderApp from '../components/AppHubPlaceholderApp.vue'
8
+ import { AppHubRunner } from '../../runner/index.js'
9
+ import { findAppByName, nextDuplicateName, nextDuplicateSlug } from '../utils/duplicateAppUtils.js'
10
+
11
+ function isUserRuntimeApp(app) {
12
+ return Boolean(app && !app.builtin && app.slug)
13
+ }
14
+
15
+ function resolveShellLanguage(options) {
16
+ const lang = options.language
17
+ if (lang && typeof lang === 'object' && 'value' in lang) {
18
+ return lang.value ?? 'vi'
19
+ }
20
+ return typeof lang === 'string' ? lang : 'vi'
21
+ }
22
+
23
+ /**
24
+ * Desktop shell โ€” Windows-style surface, icons, launches windows via window-manager.
25
+ */
26
+ export function createDesktopShell(options = {}) {
27
+ const resolveLabels = typeof options.getLabels === 'function'
28
+ ? options.getLabels
29
+ : () => options.labels ?? {}
30
+
31
+ const state = reactive({
32
+ userApps: [],
33
+ startOpen: false,
34
+ clock: '',
35
+ })
36
+
37
+ const builtinApps = computed(() => getBuiltinDesktopApps(resolveLabels()))
38
+ const taskbarBuiltinApps = computed(() => getTaskbarBuiltinApps(resolveLabels()))
39
+
40
+ const desktopIcons = computed(() => {
41
+ const icons = [...(builtinApps.value ?? []), ...state.userApps]
42
+ return icons.filter((app) => app && app.id)
43
+ })
44
+
45
+ const iconList = reactive([])
46
+ watchEffect(() => {
47
+ iconList.splice(0, iconList.length, ...desktopIcons.value)
48
+ })
49
+
50
+ function allUserAppNames() {
51
+ return state.userApps.map((a) => a.name)
52
+ }
53
+
54
+ function resolveWindowComponent(app) {
55
+ if (app.module === 'app-store') return AppHubAppStoreApp
56
+ if (app.module === 'draft-store') return AppHubDraftStoreApp
57
+ if (app.module === 'guide') return AppHubGuideApp
58
+ if (app.module === 'settings') return AppHubSettingsApp
59
+ if (isUserRuntimeApp(app)) return AppHubRunner
60
+ return AppHubPlaceholderApp
61
+ }
62
+
63
+ const handleInstall = options.handleInstall ?? null
64
+ const handleUninstall = options.handleUninstall ?? null
65
+
66
+ function resolveWindowProps(app) {
67
+ if (app.module === 'app-store' || app.module === 'draft-store') {
68
+ return {
69
+ getInstalledVersion: (slug) => findUserAppBySlug(slug)?.installedVersion ?? null,
70
+ onInstalled: async (item) => {
71
+ if (handleInstall) return handleInstall(item, null, 'appstore')
72
+ return onUserAppInstalled(item)
73
+ },
74
+ onUpdateApp: async (item) => {
75
+ if (options.onUpdateApp) return options.onUpdateApp(item)
76
+ return updateInstalledVersion(item?.slug, item?.version)
77
+ },
78
+ onUninstalled: async (item) => {
79
+ if (handleUninstall) return handleUninstall(item)
80
+ if (item?.slug) removeUserApp(`user-${item.slug}`)
81
+ },
82
+ }
83
+ }
84
+ if (isUserRuntimeApp(app)) {
85
+ return {
86
+ slug: app.slug,
87
+ status: app.status ?? 'active',
88
+ installedVersion: app.installedVersion ?? app.version ?? null,
89
+ entryUrl: app.entry_url ?? null,
90
+ healthcheckUrl: app.healthcheck_url ?? null,
91
+ icon: app.icon ?? '๐Ÿ“ฆ',
92
+ language: resolveShellLanguage(options),
93
+ runtimeType: app.runtime_type ?? 'iframe',
94
+ }
95
+ }
96
+ return { title: app.name, icon: app.icon }
97
+ }
98
+
99
+ function buildWindowDefinition(app) {
100
+ const runner = isUserRuntimeApp(app)
101
+ const defaultWidth = runner ? 960 : 720
102
+ const defaultHeight = runner ? 600 : 480
103
+ const defaultMiniWidth = runner ? 720 : 720
104
+ const defaultMiniHeight = runner ? 480 : 480
105
+
106
+ return {
107
+ id: `win-${app.id}`,
108
+ title: app.windowTitle ?? app.name,
109
+ icon: app.icon,
110
+ component: resolveWindowComponent(app),
111
+ props: resolveWindowProps(app),
112
+ layoutKey: app.layoutKey,
113
+ defaultDisplay: app.defaultDisplay,
114
+ miniWidth: app.miniWidth ?? defaultMiniWidth,
115
+ miniHeight: app.miniHeight ?? defaultMiniHeight,
116
+ width: app.width ?? defaultWidth,
117
+ height: app.height ?? defaultHeight,
118
+ }
119
+ }
120
+
121
+ function findDesktopApp(appId) {
122
+ return desktopIcons.value.find((a) => a.id === appId)
123
+ ?? taskbarBuiltinApps.value.find((a) => a.id === appId)
124
+ ?? null
125
+ }
126
+
127
+ function openApp(app, windowManager, sessionState = null) {
128
+ if (app?.id && !sessionState) options.onAppOpened?.(app.id)
129
+ windowManager.openWindow(buildWindowDefinition(app), sessionState)
130
+ }
131
+
132
+ function restoreSession(session, windowManager) {
133
+ if (!session) return false
134
+
135
+ if (Array.isArray(session.userApps) && session.userApps.length) {
136
+ state.userApps.splice(0, state.userApps.length, ...session.userApps)
137
+ }
138
+
139
+ const savedWindows = session.windows ?? []
140
+ for (const saved of savedWindows) {
141
+ const app = findDesktopApp(saved.appId)
142
+ if (app) openApp(app, windowManager, saved)
143
+ }
144
+
145
+ windowManager.finishSessionRestore?.(session.activeId)
146
+ return savedWindows.length > 0
147
+ }
148
+
149
+ function removeUserApp(appOrId) {
150
+ const id = typeof appOrId === 'string' ? appOrId : appOrId?.id
151
+ const idx = state.userApps.findIndex((a) => a.id === id)
152
+ if (idx !== -1) state.userApps.splice(idx, 1)
153
+ }
154
+
155
+ function resolveInstalledVersion(app) {
156
+ if (typeof app.installedVersion === 'string' && app.installedVersion.trim()) {
157
+ return app.installedVersion.trim()
158
+ }
159
+ if (typeof app.version === 'string' && app.version.trim()) {
160
+ return app.version.trim()
161
+ }
162
+ return null
163
+ }
164
+
165
+ function buildUserApp(app, position, method = null) {
166
+ const status = typeof app.status === 'string' ? app.status : 'active'
167
+ const installedVersion = resolveInstalledVersion(app)
168
+ return {
169
+ id: `user-${app.slug}`,
170
+ slug: app.slug,
171
+ installedVersion,
172
+ version: installedVersion,
173
+ name: app.name,
174
+ icon: app.icon ?? '๐Ÿ“ฆ',
175
+ hint: app.description ?? '',
176
+ status,
177
+ runtime_type: typeof app.runtime_type === 'string' ? app.runtime_type : 'iframe',
178
+ entry_url: typeof app.entry_url === 'string' ? app.entry_url : null,
179
+ healthcheck_url: typeof app.healthcheck_url === 'string' ? app.healthcheck_url : null,
180
+ builtin: false,
181
+ local: method === 'local' || app.local === true,
182
+ installMethod: method === 'local' || method === 'appstore' || method === 'publish'
183
+ ? method
184
+ : (app.local ? 'local' : 'appstore'),
185
+ createdAt: app.createdAt ?? new Date().toISOString(),
186
+ windowTitle: app.name,
187
+ width: 960,
188
+ height: 600,
189
+ miniWidth: 720,
190
+ miniHeight: 480,
191
+ desktopX: position?.x ?? null,
192
+ desktopY: position?.y ?? null,
193
+ }
194
+ }
195
+
196
+ function renameUserApp(appId, newName) {
197
+ const app = state.userApps.find((a) => a.id === appId)
198
+ if (!app) return { ok: false, error: 'not_found' }
199
+ const name = String(newName ?? '').trim()
200
+ if (!name) return { ok: false, error: 'empty' }
201
+ const conflict = findAppByName(name, state.userApps)
202
+ if (conflict && conflict.id !== appId) return { ok: false, error: 'duplicate' }
203
+ app.name = name
204
+ app.windowTitle = name
205
+ return { ok: true, app }
206
+ }
207
+
208
+ /**
209
+ * Install user app with duplicate handling.
210
+ * @returns {'added'|'replaced'|'updated'|'cancelled'|null}
211
+ */
212
+ function installUserApp(app, position, method = 'local', duplicateChoice = null) {
213
+ if (!app?.slug && !app?.name) return null
214
+
215
+ const existingBySlug = app.slug
216
+ ? state.userApps.find((a) => a.slug === app.slug)
217
+ : null
218
+
219
+ // Same slug โ€” publish upgrade or catalog refresh. Never show duplicate dialog.
220
+ if (existingBySlug) {
221
+ if (app.status) existingBySlug.status = app.status
222
+ if (app.runtime_type) existingBySlug.runtime_type = app.runtime_type
223
+ if (app.entry_url) existingBySlug.entry_url = app.entry_url
224
+ if (app.healthcheck_url) existingBySlug.healthcheck_url = app.healthcheck_url
225
+ if (app.description) existingBySlug.hint = app.description
226
+ if (method !== 'publish') {
227
+ const nextVersion = resolveInstalledVersion(app)
228
+ if (nextVersion && !existingBySlug.installedVersion) {
229
+ existingBySlug.installedVersion = nextVersion
230
+ existingBySlug.version = nextVersion
231
+ }
232
+ }
233
+ if (position) {
234
+ existingBySlug.desktopX = position.x
235
+ existingBySlug.desktopY = position.y
236
+ }
237
+ return 'updated'
238
+ }
239
+
240
+ const existingByName = findAppByName(app.name, state.userApps)
241
+
242
+ if (existingByName && duplicateChoice === null) {
243
+ return { needsDuplicateChoice: true, existing: existingByName, app, position, method }
244
+ }
245
+
246
+ if (duplicateChoice === 'cancel') return 'cancelled'
247
+
248
+ if (existingByName && duplicateChoice === 'replace') {
249
+ removeUserApp(existingByName)
250
+ const entry = buildUserApp({ ...app, slug: app.slug ?? existingByName.slug }, position, method)
251
+ state.userApps.push(entry)
252
+ return 'replaced'
253
+ }
254
+
255
+ if (existingByName && duplicateChoice === 'keep') {
256
+ const name = nextDuplicateName(app.name, state.userApps)
257
+ const slug = nextDuplicateSlug(app.slug ?? app.name, state.userApps)
258
+ state.userApps.push(buildUserApp({ ...app, name, slug }, position, method))
259
+ return 'added'
260
+ }
261
+
262
+ state.userApps.push(buildUserApp(app, position, method))
263
+ return 'added'
264
+ }
265
+
266
+ function updateInstalledVersion(slug, version) {
267
+ const normalizedSlug = String(slug ?? '').trim()
268
+ const normalizedVersion = String(version ?? '').trim()
269
+ if (!normalizedSlug || !normalizedVersion) return false
270
+
271
+ const app = findUserAppBySlug(normalizedSlug)
272
+ if (!app) return false
273
+
274
+ app.installedVersion = normalizedVersion
275
+ app.version = normalizedVersion
276
+ return true
277
+ }
278
+
279
+ function moveUserApp(appId, x, y) {
280
+ const app = state.userApps.find((a) => a.id === appId)
281
+ if (!app) return
282
+ app.desktopX = x
283
+ app.desktopY = y
284
+ }
285
+
286
+ function findUserApp(appId) {
287
+ return state.userApps.find((a) => a.id === appId) ?? null
288
+ }
289
+
290
+ function findUserAppBySlug(slug) {
291
+ const normalized = String(slug ?? '').trim()
292
+ if (!normalized) return null
293
+ return state.userApps.find((a) => a.slug === normalized) ?? null
294
+ }
295
+
296
+ function onUserAppInstalled(app, position = null) {
297
+ return installUserApp(app, position, 'appstore')
298
+ }
299
+
300
+ function addDroppedApp(app, position, method = 'local', duplicateChoice = null) {
301
+ return installUserApp(app, position, method, duplicateChoice)
302
+ }
303
+
304
+ function openBuiltinAppStore(windowManager) {
305
+ const app = builtinApps.value.find((a) => a.id === BUILTIN_APP_STORE_ID)
306
+ if (app) openApp(app, windowManager)
307
+ }
308
+
309
+ function tickClock() {
310
+ const now = new Date()
311
+ state.clock = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
312
+ }
313
+
314
+ return {
315
+ state,
316
+ desktopIcons,
317
+ taskbarBuiltinApps,
318
+ iconList,
319
+ allUserAppNames,
320
+ openApp,
321
+ openBuiltinAppStore,
322
+ restoreSession,
323
+ findDesktopApp,
324
+ onUserAppInstalled,
325
+ installUserApp,
326
+ addDroppedApp,
327
+ moveUserApp,
328
+ findUserApp,
329
+ findUserAppBySlug,
330
+ updateInstalledVersion,
331
+ renameUserApp,
332
+ removeUserApp,
333
+ tickClock,
334
+ }
335
+ }
@@ -0,0 +1,77 @@
1
+ /** Built-in desktop apps (always on the Hub desktop). */
2
+ export const BUILTIN_APP_STORE_ID = 'builtin-app-store'
3
+ export const BUILTIN_GUIDE_ID = 'builtin-guide'
4
+ export const BUILTIN_SETTINGS_ID = 'builtin-settings'
5
+
6
+ /** Draft App Store โ€” taskbar only, not on desktop surface. */
7
+ export const BUILTIN_DRAFT_STORE_ID = 'builtin-draft-store'
8
+
9
+ export const PILOT_DRAFT_SLUG = 'pilot-draft'
10
+
11
+ export function getBuiltinDesktopApps(labels) {
12
+ return [
13
+ {
14
+ id: BUILTIN_APP_STORE_ID,
15
+ slug: BUILTIN_APP_STORE_ID,
16
+ name: labels.desktop_app_store,
17
+ hint: labels.desktop_app_store_hint,
18
+ icon: '๐Ÿ›’',
19
+ builtin: true,
20
+ windowTitle: labels.app_store_title,
21
+ module: 'app-store',
22
+ layoutKey: BUILTIN_APP_STORE_ID,
23
+ defaultDisplay: 'fullscreen',
24
+ miniWidth: 820,
25
+ miniHeight: 520,
26
+ },
27
+ {
28
+ id: BUILTIN_GUIDE_ID,
29
+ slug: BUILTIN_GUIDE_ID,
30
+ name: labels.guide_app_name,
31
+ hint: labels.guide_app_hint,
32
+ icon: '๐Ÿ“–',
33
+ builtin: true,
34
+ windowTitle: labels.guide_app_title,
35
+ module: 'guide',
36
+ layoutKey: BUILTIN_GUIDE_ID,
37
+ defaultDisplay: 'mini',
38
+ miniWidth: 760,
39
+ miniHeight: 560,
40
+ },
41
+ {
42
+ id: BUILTIN_SETTINGS_ID,
43
+ slug: BUILTIN_SETTINGS_ID,
44
+ name: labels.hub_settings_app_name,
45
+ hint: labels.hub_settings_app_hint,
46
+ icon: 'โš™๏ธ',
47
+ builtin: true,
48
+ windowTitle: labels.hub_settings_app_title,
49
+ module: 'settings',
50
+ layoutKey: BUILTIN_SETTINGS_ID,
51
+ defaultDisplay: 'mini',
52
+ miniWidth: 720,
53
+ miniHeight: 520,
54
+ },
55
+ ]
56
+ }
57
+
58
+ /** Built-ins that open from the taskbar only (no desktop icon). */
59
+ export function getTaskbarBuiltinApps(labels) {
60
+ return [
61
+ {
62
+ id: BUILTIN_DRAFT_STORE_ID,
63
+ slug: BUILTIN_DRAFT_STORE_ID,
64
+ name: labels.draft_store_app_name,
65
+ hint: labels.draft_store_app_hint,
66
+ icon: '๐Ÿงช',
67
+ builtin: true,
68
+ taskbarOnly: true,
69
+ windowTitle: labels.draft_store_title,
70
+ module: 'draft-store',
71
+ layoutKey: BUILTIN_DRAFT_STORE_ID,
72
+ defaultDisplay: 'mini',
73
+ miniWidth: 640,
74
+ miniHeight: 480,
75
+ },
76
+ ]
77
+ }
@@ -0,0 +1,12 @@
1
+ export {
2
+ BUILTIN_APP_STORE_ID,
3
+ BUILTIN_DRAFT_STORE_ID,
4
+ BUILTIN_GUIDE_ID,
5
+ PILOT_DRAFT_SLUG,
6
+ getBuiltinDesktopApps,
7
+ getTaskbarBuiltinApps,
8
+ } from './data/builtinApps.js'
9
+ export { createDesktopShell } from './composables/useDesktopShell.js'
10
+ export { default as AppHubDesktop } from './components/AppHubDesktop.vue'
11
+ export { default as AppHubGuideApp } from './components/AppHubGuideApp.vue'
12
+ export { default as AppHubPlaceholderApp } from './components/AppHubPlaceholderApp.vue'