### Install Dependencies Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/examples/plugin-component/README.md Run this command to install project dependencies using npm or yarn. ```bash npm install # 或 yarn ``` -------------------------------- ### Run Development Mode Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/examples/plugin-component/README.md Start the development server to test your remote components locally. Use npm or yarn. ```bash npm run dev # 或 yarn dev ``` -------------------------------- ### Frontend: Call Plugin API Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/module-federation-guide.md Example of how to call a plugin's backend API using the provided 'api' module. The API call is authenticated with 'bear'. ```typescript // 演示使用api模块调用插件接口 recentItems.value = await props.api.get(`plugin/MyPlugin/history`) ``` -------------------------------- ### Create Vite Project for Plugin Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/module-federation-guide.md Use npm create vite to scaffold a new Vue TypeScript project for your plugin. Install dependencies using yarn. ```bash # Create project npm create vite@latest my-plugin -- --template vue-ts # Enter project directory cd my-plugin # Install dependencies yarn ``` -------------------------------- ### Vite Config: Expose Multiple Components Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/module-federation-guide.md Vite configuration example for exposing multiple components for Module Federation. This allows the host application to load different entry points for a plugin. ```typescript exposes: { './AppPage': './src/components/AppPage.vue', './AppPageSettings': './src/components/AppPageSettings.vue', // ... } ``` -------------------------------- ### Module Federation Shared Dependencies Configuration Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/module-federation-guide.md Configuration for sharing additional dependencies in Module Federation. This example shows how to share 'vue', 'vuetify', '@vueuse/core', and 'pinia'. ```javascript shared: { vue: { requiredVersion: false }, vuetify: { requiredVersion: false }, '@vueuse/core': { requiredVersion: false }, pinia: { requiredVersion: false } } ``` -------------------------------- ### Backend: Get Render Mode Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/module-federation-guide.md Python function to specify the plugin's render mode and component path. It returns a tuple indicating the rendering mode (e.g., 'vue', 'vuetify') and the base path for components. ```python def get_render_mode() -> Tuple[str, str]: """ 获取插件渲染模式 :return: 1、渲染模式,支持:vue/vuetify,默认vuetify :return: 2、组件路径,默认 dist/assets """ return "vue", "dist/assets" ``` -------------------------------- ### Vue Component Scoped Styles Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/module-federation-guide.md Example of using scoped styles in a Vue component to prevent CSS conflicts. Styles defined within the ` ``` -------------------------------- ### Build Project Command Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/module-federation-guide.md Command to build the project for deployment. The output 'dist' folder contains the necessary files for the plugin backend. ```bash yarn build ``` -------------------------------- ### Import SCSS Styles Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/src/@core/scss/README.md Use this import statement to include all necessary SCSS styles in your project. This method automatically loads all required style files. ```scss @use "@core/scss"; ``` -------------------------------- ### Enable Top-Level Await Support Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/federation-troubleshooting.md Add `target: 'esnext'` to the build configuration in both the host application and plugin component projects to support top-level await, which is used by Module Federation. ```javascript build: { target: 'esnext', // 支持顶层await // 其他配置... } ``` -------------------------------- ### Enable Detailed Debugging Logs Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/federation-troubleshooting.md Set the 'debug' item in `localStorage` to 'vite:*' in the browser console to enable detailed logging for Vite-related issues. ```javascript localStorage.setItem('debug', 'vite:*') ``` -------------------------------- ### Configure Shared Dependencies in Host Application Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/federation-troubleshooting.md Ensure shared dependencies like 'vue' and 'vuetify' are correctly configured in the host application's `vite.config.ts`. ```typescript federation({ name: 'host', remotes: {}, shared: ['vue', 'vuetify'] }) ``` -------------------------------- ### Apply Launch Theme Chrome Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/index.html Applies the launch theme by resolving preferences, determining color schemes, and setting CSS variables for background and primary colors. It also updates meta tags for color scheme and theme color. ```javascript function applyLaunchThemeChrome() { const themePreference = getSavedThemePreference() const resolvedLaunchTheme = resolveLaunchTheme(themePreference) const colorScheme = getLaunchColorScheme(resolvedLaunchTheme) const palette = launchThemePalettes[resolvedLaunchTheme] || launchThemePalettes.light // auto 模式下系统明暗可能已变化,不能复用旧的启动背景缓存。 const storedLoaderColor = themePreference === 'auto' ? null : getLocalStorageValue('materio-initial-loader-bg') const loaderColor = storedLoaderColor || palette.background const primaryColor = getLocalStorageValue('materio-initial-loader-color') || palette.primary document.documentElement.setAttribute('data-launch-theme', resolvedLaunchTheme) document.documentElement.setAttribute('data-theme', resolvedLaunchTheme) document.documentElement.setAttribute('data-theme-preference', themePreference) document.documentElement.style.setProperty('--initial-loader-bg', loaderColor) document.documentElement.style.setProperty('--initial-loader-color', primaryColor) document.documentElement.style.setProperty('--initial-color-scheme', colorScheme) document.documentElement.style.backgroundColor = loaderColor document.documentElement.style.colorScheme = colorScheme if (document.body) { document.body.setAttribute('data-theme', resolvedLaunchTheme) document.body.setAttribute('data-theme-preference', themePreference) document.body.style.backgroundColor = loaderColor document.body.style.colorScheme = colorScheme } setMetaContent('meta[name="color-scheme"]', colorScheme === 'dark' ? 'dark light' : 'light dark') syncThemeColorMeta(loaderColor) return { background: loaderColor, colorScheme, resolvedLaunchTheme, themePreference, } } ``` -------------------------------- ### Config Component Structure Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/module-federation-guide.md Implement the Config component to manage plugin settings. It accepts initial configuration and an API object, and emits events to save configuration or navigate between pages. ```vue ``` -------------------------------- ### Configure Vite for Module Federation Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/module-federation-guide.md Configure vite.config.ts to enable Module Federation, define exposed components, shared dependencies, and build/server settings. Ensure the server port is different from the main application and CORS is enabled. ```typescript import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import federation from '@originjs/vite-plugin-federation' export default defineConfig({ plugins: [ vue(), federation({ name: 'MyPlugin', filename: 'remoteEntry.js', exposes: { './Page': './src/components/Page.vue', './Config': './src/components/Config.vue', './Dashboard': './src/components/Dashboard.vue', './AppPage': './src/components/AppPage.vue', './AppPageSettings': './src/components/AppPageSettings.vue', }, shared: { vue: { requiredVersion: false, generate: false, }, vuetify: { requiredVersion: false, generate: false, singleton: true, }, 'vuetify/styles': { requiredVersion: false, generate: false, singleton: true, }, }, format: 'esm' }) ], build: { target: 'esnext', // Must be set to esnext to support top-level await minify: false, // Recommended to disable minification during development cssCodeSplit: true, // Set to true to enable separate CSS file splitting }, css: { preprocessorOptions: { scss: { additionalData: '/* Override vuetify styles */', } }, postcss: { plugins: [ { postcssPlugin: 'internal:charset-removal', AtRule: { charset: (atRule) => { if (atRule.name === 'charset') { atRule.remove(); } } } }, { postcssPlugin: 'vuetify-filter', Root(root) { // Filter out all vuetify-related CSS root.walkRules(rule => { if (rule.selector && ( rule.selector.includes('.v-') || rule.selector.includes('.mdi-'))) { rule.remove(); } }); } } ] } }, server: { port: 5001, // Use a different port than the main application cors: true, // Enable CORS origin: 'http://localhost:5001' }, }) ``` -------------------------------- ### Sync Initial Viewport Dimensions Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/index.html Synchronizes the initial viewport dimensions by calculating the current height and width and setting them as CSS variables. This is crucial for locking the initial loader content height to prevent layout shifts, especially on iOS. ```javascript function syncInitialViewport(force) { const viewport = window.visualViewport const nextHeight = Math.round(viewport?.height || window.innerHeight || document.documentElement.clientHeight || 0) const nextWidth = Math.round(viewport?.width || window.innerWidth || document.documentElement.clientWidth || 0) const currentHeight = parseInt( document.documentElement.style.getPropertyValue('--initial-loader-height') || '0', 10, ) if (!nextHeight || !nextWidth) { return } if (!force && currentHeight && Math.abs(nextHeight - currentHeight) < 120) { return } document.documentElement.style.setProperty('--initial-loader-height', `${nextHeight}px`) document.documentElement.style.setProperty('--initial-loader-width', `${nextWidth}px`) } ``` -------------------------------- ### Vite Development Server Configuration Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/module-federation-guide.md Vite configuration for the development server. It sets a custom port, enables CORS, and defines the server origin for local testing. ```typescript // vite.config.ts export default defineConfig({ server: { port: 5001, // 使用不同于主应用的端口 cors: true, // 启用CORS origin: 'http://localhost:5001' } }) ``` -------------------------------- ### Backend: Register Plugin API Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/module-federation-guide.md Python function to register API endpoints for a plugin. It defines the path, endpoint function, allowed methods, authentication type, and summary for each API. ```python def get_api(self) -> List[Dict[str, Any]]: """ 注册插件API """ return [ { "path": "/history", "endpoint": self.get_history, "methods": ["GET"], "auth": "bear", # 认证类型设为bear "summary": "查询历史记录" } ] ``` -------------------------------- ### Backend: Register Sidebar Entry Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/module-federation-guide.md Python function to define sidebar navigation entries for a Vue rendering mode plugin. It returns a list of dictionaries, each specifying a navigation key, title, icon, section, and optional permission and order. ```python def get_sidebar_nav(self) -> List[Dict[str, Any]]: return [ { "nav_key": "main", "title": "示例订阅页", "icon": "mdi-rss", "section": "subscribe", "permission": "subscribe", "order": 10, } ] ``` -------------------------------- ### Sidebar Full-Page Application Component Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/module-federation-guide.md A Vue component for independent pages within the main application's sidebar navigation. It receives API, navKey, and pluginId as props and can emit an action event. ```vue ``` -------------------------------- ### Configure Shared Dependencies in Plugin Component Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/federation-troubleshooting.md Configure shared dependencies in the plugin component's `vite.config.js` to resolve remote component URL issues. Set `requiredVersion` to `false` to disable version checking for Vue. ```javascript federation({ // ... shared: { vue: { singleton: true, requiredVersion: false // 关闭版本检查 } } }) ``` -------------------------------- ### Page Component Structure Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/module-federation-guide.md Develop the Page component with defined props for API access and emits for notifying the main application about actions like refreshing data, switching to config, or closing the page. ```vue ``` -------------------------------- ### Display Timeout Message with Localization Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/index.html After a 15-second delay, this code displays a timeout message if an element with id 'loading-timeout' exists. It adapts the message text and button label based on the browser's language setting (supporting Chinese, Traditional Chinese, and English). ```javascript setTimeout(function() { const timeoutEl = document.getElementById('loading-timeout'); if (timeoutEl) { // 适配多语言 const lang = navigator.language || 'zh-CN'; const messages = { 'zh-CN': { text: '页面加载似乎遇到了阻碍,请尝试', btn: '清除缓存' }, 'zh-TW': { text: '頁面載入似乎遇到了阻礙,請嘗試', btn: '清除快取' }, 'en-US': { text: 'Page loading seems to be blocked, please try', btn: 'Clear Cache' } }; // 默认匹配前缀,如 en-GB 匹配 en-US 的逻辑 let msg = messages['zh-CN']; if (lang.startsWith('zh-TW') || lang.startsWith('zh-HK')) { msg = messages['zh-TW']; } else if (lang.startsWith('en')) { msg = messages['en-US']; } const textNode = document.createTextNode(msg.text + ' '); const btnLink = document.createElement('a'); btnLink.href = 'javascript:void(0)'; btnLink.id = 'timeout-btn'; btnLink.onclick = window.clearAndReload; btnLink.textContent = msg.btn; timeoutEl.innerHTML = ''; timeoutEl.appendChild(textNode); timeoutEl.appendChild(btnLink); timeoutEl.style.display = 'block'; } }, 15000); ``` -------------------------------- ### Dashboard Widget Component Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/docs/module-federation-guide.md A Vue component for creating dashboard widgets that accept configuration and refresh controls. It uses v-hover to conditionally display a drag icon. ```vue ``` -------------------------------- ### Update Safe Area Insets Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/index.html Updates the CSS variables for safe area insets (top and bottom) based on the computed styles. This is important for adapting layouts to devices with notches or dynamic islands. ```javascript function updateSafeArea() { const safeAreaTop = getComputedStyle(document.documentElement).getPropertyValue('env(safe-area-inset-top)') const safeAreaBottom = getComputedStyle(document.documentElement).getPropertyValue('env(safe-area-inset-bottom)', ) if (safeAreaTop) document.documentElement.style.setProperty('--safe-area-top', safeAreaTop) if (safeAreaBottom) document.documentElement.style.setProperty('--safe-area-bottom', safeAreaBottom) } ``` -------------------------------- ### Reload Page with Timestamp Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/index.html Reloads the current page, appending a unique timestamp to the URL to bypass browser caching. Useful for ensuring users see the latest version of a page. ```javascript const url = new URL(window.location.href) url.searchParams.set('_t', Date.now().toString()) window.location.replace(url.pathname + url.search + url.hash) ``` -------------------------------- ### Clear Browser Cache and Service Workers Source: https://github.com/jxxghp/moviepilot-frontend/blob/v2/index.html Clears all browser caches and unregisters all service workers. This function is intended for a complete refresh of the application, ensuring no old assets or service worker logic interfere with the new version. ```javascript window.clearAndReload = async function() { try { // 1. 清除所有缓存 if ('caches' in window) { const cacheNames = await caches.keys() await Promise.all(cacheNames.map(name => caches.delete(name))) console.log(' [VersionChecker] 已清除所有缓存') } // 2. 注销 Service Worker if ('serviceWorker' in navigator) { const registrations = await navigator.serviceWorker.getRegistrations() await Promise.all(registrations.map(registration => registration.unregister())) console.log(' [VersionChecker] 已注销所有 Service Worker') } } catch (e) { console.error(' [VersionChecker] 清除缓存时出错:', e) } finally { // 3. ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.