diff --git a/packages/frontend/index.html b/packages/frontend/index.html
index 7806d3c..57fdc9d 100644
--- a/packages/frontend/index.html
+++ b/packages/frontend/index.html
@@ -5,6 +5,12 @@
Nexus Terminal
+
+
+
+
+
+
diff --git a/packages/frontend/public/icons/icon-144x144.png b/packages/frontend/public/icons/icon-144x144.png
new file mode 100644
index 0000000..fdf2d16
Binary files /dev/null and b/packages/frontend/public/icons/icon-144x144.png differ
diff --git a/packages/frontend/public/icons/icon-192x192.png b/packages/frontend/public/icons/icon-192x192.png
new file mode 100644
index 0000000..b590e7f
Binary files /dev/null and b/packages/frontend/public/icons/icon-192x192.png differ
diff --git a/packages/frontend/public/icons/icon-512x512.png b/packages/frontend/public/icons/icon-512x512.png
new file mode 100644
index 0000000..2d5ac19
Binary files /dev/null and b/packages/frontend/public/icons/icon-512x512.png differ
diff --git a/packages/frontend/public/manifest.json b/packages/frontend/public/manifest.json
new file mode 100644
index 0000000..eb9e11b
--- /dev/null
+++ b/packages/frontend/public/manifest.json
@@ -0,0 +1,31 @@
+{
+ "name": "Nexus Terminal",
+ "short_name": "NexusTerm",
+ "description": "A modern, web-based terminal.",
+ "icons": [
+ {
+ "src": "/icons/icon-144x144.png",
+ "sizes": "144x144",
+ "type": "image/png",
+ "purpose": "any"
+ },
+ {
+ "src": "/icons/icon-192x192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "any"
+ },
+ {
+ "src": "/icons/icon-512x512.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "any"
+ }
+ ],
+ "start_url": "/",
+ "display": "standalone",
+ "background_color": "#333333",
+ "theme_color": "#9370DB",
+ "scope": "/",
+ "orientation": "portrait-primary"
+}
\ No newline at end of file
diff --git a/packages/frontend/public/sw.js b/packages/frontend/public/sw.js
new file mode 100644
index 0000000..b4780bb
--- /dev/null
+++ b/packages/frontend/public/sw.js
@@ -0,0 +1,52 @@
+const CACHE_NAME = 'nexus-terminal-cache-v1';
+const urlsToCache = [
+ '/',
+ '/index.html',
+ '/manifest.json',
+ '/icons/icon-72x72.png',
+ '/icons/icon-96x96.png',
+ '/icons/icon-128x128.png',
+ '/icons/icon-144x144.png',
+ '/icons/icon-152x152.png',
+ '/icons/icon-192x192.png',
+ '/icons/icon-384x384.png',
+ '/icons/icon-512x512.png'
+];
+
+self.addEventListener('install', (event) => {
+ event.waitUntil(
+ caches.open(CACHE_NAME)
+ .then((cache) => {
+ console.log('Opened cache');
+ return cache.addAll(urlsToCache);
+ })
+ );
+});
+
+self.addEventListener('fetch', (event) => {
+ event.respondWith(
+ caches.match(event.request)
+ .then((response) => {
+ if (response) {
+ return response;
+ }
+ return fetch(event.request);
+ }
+ )
+ );
+});
+
+self.addEventListener('activate', (event) => {
+ const cacheWhitelist = [CACHE_NAME];
+ event.waitUntil(
+ caches.keys().then((cacheNames) => {
+ return Promise.all(
+ cacheNames.map((cacheName) => {
+ if (cacheWhitelist.indexOf(cacheName) === -1) {
+ return caches.delete(cacheName);
+ }
+ })
+ );
+ })
+ );
+});
\ No newline at end of file
diff --git a/packages/frontend/src/App.vue b/packages/frontend/src/App.vue
index 9e539b5..03bc57e 100644
--- a/packages/frontend/src/App.vue
+++ b/packages/frontend/src/App.vue
@@ -7,8 +7,8 @@ import { useDeviceDetection } from './composables/useDeviceDetection';
import { useSettingsStore } from './stores/settings.store';
import { useAppearanceStore } from './stores/appearance.store';
import { useLayoutStore } from './stores/layout.store';
-import { useFocusSwitcherStore } from './stores/focusSwitcher.store'; // +++ 导入焦点切换 Store +++
-import { useSessionStore } from './stores/session.store'; // +++ 导入 Session Store +++
+import { useFocusSwitcherStore } from './stores/focusSwitcher.store';
+import { useSessionStore } from './stores/session.store';
import { storeToRefs } from 'pinia';
// 导入通知显示组件
import UINotificationDisplay from './components/UINotificationDisplay.vue';
@@ -72,7 +72,16 @@ onMounted(() => {
// +++ 添加全局 Alt 键监听器 +++
window.addEventListener('keydown', handleAltKeyDown); // +++ 监听 keydown 设置状态 +++
window.addEventListener('keyup', handleGlobalKeyUp); // +++ 监听 keyup 执行切换 +++
+
+ // PWA Install Prompt
+ window.addEventListener('beforeinstallprompt', (e) => {
+ console.log('[App.vue] beforeinstallprompt event fired. Browser will handle install prompt.');
+ });
+ window.addEventListener('appinstalled', () => {
+ console.log('[App.vue] PWA was installed');
+ });
+
// +++ 加载 Header 可见性状态 +++
layoutStore.loadHeaderVisibility();
});
@@ -250,6 +259,8 @@ const isElementVisibleAndFocusable = (element: HTMLElement): boolean => {
return rect.width > 0 && rect.height > 0;
};
+
+
@@ -279,6 +290,7 @@ const isElementVisibleAndFocusable = (element: HTMLElement): boolean => {
+
{{ t('nav.login') }}
{{ t('nav.logout') }}
@@ -327,6 +339,7 @@ const isElementVisibleAndFocusable = (element: HTMLElement): boolean => {
@close="sessionStore.closeVncModal()"
/>
+
@@ -336,14 +349,12 @@ const isElementVisibleAndFocusable = (element: HTMLElement): boolean => {
flex-direction: column;
min-height: 100vh;
font-family: var(--font-family-sans-serif); /* 使用字体变量 */
- /* background-color: var(--app-bg-color); */ /* 移除容器背景色,让 body 背景透出来 */
}
-/* Removed header, nav, nav-left, nav-right, nav a, nav a:hover, nav a.router-link-exact-active, .nav-underline, and specific icon/login/logout link styles as they are now handled by Tailwind classes */
main {
flex-grow: 1;
- /* padding: var(--base-padding); */ /* Keep padding removed from main */
+
}
diff --git a/packages/frontend/src/main.ts b/packages/frontend/src/main.ts
index ffe834a..e2dccb8 100644
--- a/packages/frontend/src/main.ts
+++ b/packages/frontend/src/main.ts
@@ -83,4 +83,14 @@ app.use(i18n); // 使用 i18n
}
app.mount('#app');
}
+// --- PWA Service Worker Registration ---
+ if ('serviceWorker' in navigator) {
+ window.addEventListener('load', () => {
+ navigator.serviceWorker.register('/sw.js').then(registration => {
+ console.log('SW registered: ', registration);
+ }).catch(registrationError => {
+ console.log('SW registration failed: ', registrationError);
+ });
+ });
+ }
})();