@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,278 @@
1
+ <template>
2
+ <div class="apphub-guide">
3
+ <header class="apphub-guide__header">
4
+ <h2 class="apphub-guide__title">{{ labels.title }}</h2>
5
+ <p class="apphub-guide__subtitle">{{ labels.subtitle }}</p>
6
+ </header>
7
+
8
+ <nav class="apphub-guide__tabs" role="tablist">
9
+ <button
10
+ type="button"
11
+ role="tab"
12
+ class="apphub-guide__tab"
13
+ :class="{ 'apphub-guide__tab--active': tab === 'user' }"
14
+ :aria-selected="tab === 'user'"
15
+ @click="tab = 'user'"
16
+ >
17
+ {{ labels.tab_user }}
18
+ </button>
19
+ <button
20
+ type="button"
21
+ role="tab"
22
+ class="apphub-guide__tab"
23
+ :class="{ 'apphub-guide__tab--active': tab === 'dev' }"
24
+ :aria-selected="tab === 'dev'"
25
+ @click="tab = 'dev'"
26
+ >
27
+ {{ labels.tab_dev }}
28
+ </button>
29
+ </nav>
30
+
31
+ <div class="apphub-guide__body">
32
+ <article v-show="tab === 'user'" class="apphub-guide__article">
33
+ <section v-for="block in userSections" :key="block.title" class="apphub-guide__section">
34
+ <h3>{{ block.title }}</h3>
35
+ <ul>
36
+ <li v-for="(line, i) in block.lines" :key="i">{{ line }}</li>
37
+ </ul>
38
+ </section>
39
+ </article>
40
+
41
+ <article v-show="tab === 'dev'" class="apphub-guide__article">
42
+ <section v-for="block in devSections" :key="block.title" class="apphub-guide__section">
43
+ <h3>{{ block.title }}</h3>
44
+ <ul v-if="block.lines?.length">
45
+ <li v-for="(line, i) in block.lines" :key="i">{{ line }}</li>
46
+ </ul>
47
+ <pre v-if="block.code" class="apphub-guide__code"><code>{{ block.code }}</code></pre>
48
+ </section>
49
+ </article>
50
+ </div>
51
+ </div>
52
+ </template>
53
+
54
+ <script setup>
55
+ import { computed, inject, ref } from 'vue'
56
+ import { resolveLang } from '../../../i18n/resolveLang.js'
57
+ import { t } from '../../../i18n/index.js'
58
+
59
+ const tab = ref('user')
60
+ const lang = computed(() => resolveLang(inject('apphubOptions', {})?.language, 'vi'))
61
+
62
+ const labels = computed(() => ({
63
+ title: t('guide_app_title', lang.value),
64
+ subtitle: t('guide_app_subtitle', lang.value),
65
+ tab_user: t('guide_tab_user', lang.value),
66
+ tab_dev: t('guide_tab_dev', lang.value),
67
+ }))
68
+
69
+ const userSections = computed(() => [
70
+ {
71
+ title: t('guide_user_welcome_title', lang.value),
72
+ lines: [
73
+ t('guide_user_welcome_1', lang.value),
74
+ t('guide_user_welcome_2', lang.value),
75
+ ],
76
+ },
77
+ {
78
+ title: t('guide_user_desktop_title', lang.value),
79
+ lines: [
80
+ t('guide_user_desktop_1', lang.value),
81
+ t('guide_user_desktop_2', lang.value),
82
+ t('guide_user_desktop_3', lang.value),
83
+ ],
84
+ },
85
+ {
86
+ title: t('guide_user_windows_title', lang.value),
87
+ lines: [
88
+ t('guide_user_windows_1', lang.value),
89
+ t('guide_user_windows_2', lang.value),
90
+ t('guide_user_windows_3', lang.value),
91
+ ],
92
+ },
93
+ {
94
+ title: t('guide_user_install_title', lang.value),
95
+ lines: [
96
+ t('guide_user_install_1', lang.value),
97
+ t('guide_user_install_2', lang.value),
98
+ t('guide_user_install_3', lang.value),
99
+ ],
100
+ },
101
+ {
102
+ title: t('guide_user_start_title', lang.value),
103
+ lines: [
104
+ t('guide_user_start_1', lang.value),
105
+ t('guide_user_start_2', lang.value),
106
+ ],
107
+ },
108
+ {
109
+ title: t('guide_user_tips_title', lang.value),
110
+ lines: [
111
+ t('guide_user_tips_1', lang.value),
112
+ t('guide_user_tips_2', lang.value),
113
+ t('guide_user_tips_3', lang.value),
114
+ ],
115
+ },
116
+ ])
117
+
118
+ const devSections = computed(() => [
119
+ {
120
+ title: t('guide_dev_who_title', lang.value),
121
+ lines: [
122
+ t('guide_dev_who_1', lang.value),
123
+ t('guide_dev_who_2', lang.value),
124
+ ],
125
+ },
126
+ {
127
+ title: t('guide_dev_bridge_title', lang.value),
128
+ lines: [
129
+ t('guide_dev_bridge_1', lang.value),
130
+ t('guide_dev_bridge_2', lang.value),
131
+ t('guide_dev_bridge_3', lang.value),
132
+ ],
133
+ },
134
+ {
135
+ title: t('guide_dev_permissions_title', lang.value),
136
+ lines: [
137
+ t('guide_dev_permissions_1', lang.value),
138
+ t('guide_dev_permissions_2', lang.value),
139
+ t('guide_dev_permissions_3', lang.value),
140
+ t('guide_dev_permissions_4', lang.value),
141
+ ],
142
+ },
143
+ {
144
+ title: t('guide_dev_bridge_code_title', lang.value),
145
+ code: t('guide_dev_bridge_code', lang.value),
146
+ },
147
+ {
148
+ title: t('guide_dev_user_title', lang.value),
149
+ lines: [
150
+ t('guide_dev_user_1', lang.value),
151
+ t('guide_dev_user_2', lang.value),
152
+ t('guide_dev_user_3', lang.value),
153
+ ],
154
+ },
155
+ {
156
+ title: t('guide_dev_desktop_title', lang.value),
157
+ lines: [
158
+ t('guide_dev_desktop_1', lang.value),
159
+ t('guide_dev_desktop_2', lang.value),
160
+ t('guide_dev_desktop_3', lang.value),
161
+ ],
162
+ },
163
+ {
164
+ title: t('guide_dev_deploy_title', lang.value),
165
+ lines: [
166
+ t('guide_dev_deploy_1', lang.value),
167
+ t('guide_dev_deploy_2', lang.value),
168
+ ],
169
+ },
170
+ {
171
+ title: t('guide_dev_docs_title', lang.value),
172
+ lines: [
173
+ t('guide_dev_docs_1', lang.value),
174
+ t('guide_dev_docs_2', lang.value),
175
+ t('guide_dev_docs_3', lang.value),
176
+ ],
177
+ },
178
+ ])
179
+ </script>
180
+
181
+ <style scoped>
182
+ .apphub-guide {
183
+ display: flex;
184
+ flex-direction: column;
185
+ height: 100%;
186
+ min-height: 0;
187
+ color: var(--ah-text-secondary, #cbd5e1);
188
+ background: var(--ah-surface, #1e293b);
189
+ }
190
+
191
+ .apphub-guide__header {
192
+ padding: 20px 24px 12px;
193
+ border-bottom: 1px solid var(--ah-border-subtle, rgba(255, 255, 255, 0.08));
194
+ }
195
+
196
+ .apphub-guide__title {
197
+ margin: 0;
198
+ font-size: 1.35rem;
199
+ color: var(--ah-text, #f0f4fc);
200
+ }
201
+
202
+ .apphub-guide__subtitle {
203
+ margin: 6px 0 0;
204
+ font-size: 0.88rem;
205
+ color: var(--ah-text-muted, #94a3b8);
206
+ }
207
+
208
+ .apphub-guide__tabs {
209
+ display: flex;
210
+ gap: 4px;
211
+ padding: 10px 16px;
212
+ border-bottom: 1px solid var(--ah-border-subtle, rgba(255, 255, 255, 0.08));
213
+ }
214
+
215
+ .apphub-guide__tab {
216
+ padding: 8px 16px;
217
+ border: 1px solid transparent;
218
+ border-radius: 6px;
219
+ background: transparent;
220
+ color: var(--ah-text-muted, #94a3b8);
221
+ font-size: 0.85rem;
222
+ font-weight: 600;
223
+ cursor: pointer;
224
+ transition: background 0.15s ease, color 0.15s ease;
225
+ }
226
+
227
+ .apphub-guide__tab:hover {
228
+ background: var(--ah-hover, rgba(255, 255, 255, 0.08));
229
+ color: var(--ah-text, #f0f4fc);
230
+ }
231
+
232
+ .apphub-guide__tab--active {
233
+ background: var(--ah-hover-strong, rgba(255, 255, 255, 0.12));
234
+ border-color: var(--ah-border, rgba(255, 255, 255, 0.1));
235
+ color: var(--ah-text, #f0f4fc);
236
+ }
237
+
238
+ .apphub-guide__body {
239
+ flex: 1;
240
+ overflow-y: auto;
241
+ padding: 16px 24px 24px;
242
+ }
243
+
244
+ .apphub-guide__section {
245
+ margin-bottom: 22px;
246
+ }
247
+
248
+ .apphub-guide__section h3 {
249
+ margin: 0 0 10px;
250
+ font-size: 1rem;
251
+ color: var(--ah-text, #f0f4fc);
252
+ }
253
+
254
+ .apphub-guide__section ul {
255
+ margin: 0;
256
+ padding-left: 1.25rem;
257
+ line-height: 1.55;
258
+ font-size: 0.875rem;
259
+ }
260
+
261
+ .apphub-guide__section li {
262
+ margin-bottom: 6px;
263
+ }
264
+
265
+ .apphub-guide__code {
266
+ margin: 10px 0 0;
267
+ padding: 12px 14px;
268
+ border-radius: 6px;
269
+ background: var(--ah-hover, rgba(0, 0, 0, 0.25));
270
+ border: 1px solid var(--ah-border-subtle, rgba(255, 255, 255, 0.08));
271
+ overflow-x: auto;
272
+ font-size: 0.78rem;
273
+ line-height: 1.5;
274
+ color: var(--ah-text, #e2e8f0);
275
+ white-space: pre-wrap;
276
+ word-break: break-word;
277
+ }
278
+ </style>
@@ -0,0 +1,105 @@
1
+ <template>
2
+ <div class="apphub-origin-block" role="alert">
3
+ <div class="apphub-origin-block__panel">
4
+ <span class="apphub-origin-block__icon" aria-hidden="true">🛡️</span>
5
+ <h1 class="apphub-origin-block__title">{{ labels.title }}</h1>
6
+ <p class="apphub-origin-block__message">{{ message }}</p>
7
+ <p class="apphub-origin-block__hint">{{ labels.hint }}</p>
8
+ <dl v-if="showOrigins" class="apphub-origin-block__dl">
9
+ <div class="apphub-origin-block__row">
10
+ <dt>{{ labels.current_origin }}</dt>
11
+ <dd>{{ currentOrigin }}</dd>
12
+ </div>
13
+ <div v-if="expectedHubOrigin" class="apphub-origin-block__row">
14
+ <dt>{{ labels.expected_hub_origin }}</dt>
15
+ <dd>{{ expectedHubOrigin }}</dd>
16
+ </div>
17
+ <div v-if="expectedRuntimeOrigin" class="apphub-origin-block__row">
18
+ <dt>{{ labels.expected_runtime_origin }}</dt>
19
+ <dd>{{ expectedRuntimeOrigin }}</dd>
20
+ </div>
21
+ <div v-if="parentOrigin" class="apphub-origin-block__row">
22
+ <dt>{{ labels.parent_origin }}</dt>
23
+ <dd>{{ parentOrigin }}</dd>
24
+ </div>
25
+ </dl>
26
+
27
+ <div v-if="devOriginVisible" class="apphub-origin-block__dev">
28
+ <p class="apphub-origin-block__dev-title">{{ devOriginLabels.title }}</p>
29
+ <p class="apphub-origin-block__dev-status">
30
+ {{ devFriendlyOn ? devOriginLabels.status_relaxed : devOriginLabels.status_strict }}
31
+ </p>
32
+ <button type="button" class="apphub-origin-block__dev-btn" @click="toggleDevOrigin">
33
+ {{ devFriendlyOn ? devOriginLabels.action_enable_strict : devOriginLabels.action_enable_relaxed }}
34
+ </button>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ </template>
39
+
40
+ <script setup>
41
+ import { computed, inject } from 'vue'
42
+ import { t } from '../../../i18n/index.js'
43
+ import { resolveLang } from '../../../i18n/resolveLang.js'
44
+ import { useDevOriginToggle } from '../../../composables/useDevOriginToggle.js'
45
+ import {
46
+ ORIGIN_UNSAFE_NOT_CONFIGURED,
47
+ ORIGIN_UNSAFE_RUNTIME_NOT_CONFIGURED,
48
+ ORIGIN_UNSAFE_RUNTIME_SAME_ORIGIN,
49
+ ORIGIN_UNSAFE_SAME_ORIGIN_EMBED,
50
+ ORIGIN_UNSAFE_WRONG_ORIGIN,
51
+ } from '../../../utils/originSafety.js'
52
+
53
+ const props = defineProps({
54
+ reason: { type: String, default: null },
55
+ parentOrigin: { type: String, default: null },
56
+ expectedHubOrigin: { type: String, default: null },
57
+ expectedRuntimeOrigin: { type: String, default: null },
58
+ labels: { type: Object, required: true },
59
+ })
60
+
61
+ const moduleOptions = inject('apphubOptions', {})
62
+ const lang = computed(() => resolveLang(moduleOptions?.language, 'vi'))
63
+ const { visible: devOriginVisible, devFriendlyOn, toggle: toggleDevOrigin } = useDevOriginToggle()
64
+
65
+ const devOriginLabels = computed(() => ({
66
+ title: t('dev_origin_bar_title', lang.value),
67
+ status_relaxed: t('dev_origin_status_relaxed', lang.value),
68
+ status_strict: t('dev_origin_status_strict', lang.value),
69
+ action_enable_strict: t('dev_origin_action_strict', lang.value),
70
+ action_enable_relaxed: t('dev_origin_action_relaxed', lang.value),
71
+ }))
72
+
73
+ const currentOrigin = computed(() => {
74
+ if (typeof window === 'undefined') return ''
75
+ return window.location.origin
76
+ })
77
+
78
+ const message = computed(() => {
79
+ if (props.reason === ORIGIN_UNSAFE_SAME_ORIGIN_EMBED) {
80
+ return props.labels.same_origin_embed
81
+ }
82
+ if (props.reason === ORIGIN_UNSAFE_NOT_CONFIGURED) {
83
+ return props.labels.not_configured
84
+ }
85
+ if (props.reason === ORIGIN_UNSAFE_WRONG_ORIGIN) {
86
+ return props.labels.wrong_origin
87
+ }
88
+ if (props.reason === ORIGIN_UNSAFE_RUNTIME_NOT_CONFIGURED) {
89
+ return props.labels.runtime_not_configured
90
+ }
91
+ if (props.reason === ORIGIN_UNSAFE_RUNTIME_SAME_ORIGIN) {
92
+ return props.labels.runtime_same_origin
93
+ }
94
+ return props.labels.generic
95
+ })
96
+
97
+ const showOrigins = computed(() =>
98
+ Boolean(
99
+ currentOrigin.value
100
+ || props.parentOrigin
101
+ || props.expectedHubOrigin
102
+ || props.expectedRuntimeOrigin,
103
+ ),
104
+ )
105
+ </script>
@@ -0,0 +1,23 @@
1
+ <template>
2
+ <div class="apphub-origin-loading" role="status" aria-live="polite">
3
+ <div class="apphub-origin-loading__panel">
4
+ <div class="apphub-origin-loading__spinner" aria-hidden="true" />
5
+ <p class="apphub-origin-loading__title">{{ labels.title }}</p>
6
+ <p class="apphub-origin-loading__hint">{{ labels.hint }}</p>
7
+ </div>
8
+ </div>
9
+ </template>
10
+
11
+ <script setup>
12
+ import { computed, inject } from 'vue'
13
+ import { t } from '../../../i18n/index.js'
14
+ import { resolveLang } from '../../../i18n/resolveLang.js'
15
+
16
+ const moduleOptions = inject('apphubOptions', {})
17
+ const lang = computed(() => resolveLang(moduleOptions?.language, 'vi'))
18
+
19
+ const labels = computed(() => ({
20
+ title: t('origin_loading_title', lang.value),
21
+ hint: t('origin_loading_hint', lang.value),
22
+ }))
23
+ </script>
@@ -0,0 +1,14 @@
1
+ <template>
2
+ <div class="apphub-placeholder">
3
+ <p class="apphub-placeholder__icon">{{ icon }}</p>
4
+ <h3>{{ title }}</h3>
5
+ <p class="apphub-placeholder__hint">Demo app shell — connect backend launch URL later.</p>
6
+ </div>
7
+ </template>
8
+
9
+ <script setup>
10
+ defineProps({
11
+ title: { type: String, default: 'App' },
12
+ icon: { type: String, default: '📦' },
13
+ })
14
+ </script>