// ==UserScript== // @name NoNinite: Production-Grade Script Generator // @namespace https://github.com/SysAdminDoc/NoNinite // @version 5.3.0 // @description A professional, modern, and powerful UI for generating Winget and Chocolatey installation scripts. Features fully implemented faceted search, canonical categories, live script preview, presets, and a high-quality, responsive, SaaS-like interface. // @author Matthew Parker // @match https://ninite.com/ // @icon https://raw.githubusercontent.com/SysAdminDoc/NoNinite/refs/heads/main/assets/icons/favicon.ico // @downloadURL https://github.com/SysAdminDoc/NoNinite/raw/main/src/NoNinite.user.js // @updateURL https://github.com/SysAdminDoc/NoNinite/raw/main/src/NoNinite.user.js // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_setClipboard // @grant GM_xmlhttpRequest // @connect raw.githubusercontent.com // @connect community.chocolatey.org // @require https://code.jquery.com/jquery-3.7.1.min.js // @require https://cdn.jsdelivr.net/npm/fuse.js@6.6.2/dist/fuse.min.js // @run-at document-start // ==/UserScript== /* jshint esversion: 11 */ // Hide the body immediately to prevent FOUC (Flash of Unstyled Content) GM_addStyle('body { visibility: hidden; }'); (async function () { 'use strict'; // ----------------------------------------------------------------------------- // CONFIG, CONSTANTS & DATA MAPS // ----------------------------------------------------------------------------- const SCRIPT_VERSION = '5.3.0'; const GH_REPO_RAW = 'https://raw.githubusercontent.com/SysAdminDoc/NoNinite/main'; const DATA_URL_PRIMARY = `https://raw.githubusercontent.com/SysAdminDoc/NoNinite/refs/heads/main/data/ChocolateyPackageExporter/chocoapplications.json`; const DATA_URL_FALLBACK = `https://raw.githubusercontent.com/SysAdminDoc/NoNinite/main/data/ChocolateyPackageExporter/chocoapplications.json`; const GENERIC_ICON_URL = `${GH_REPO_RAW}/assets/icons/favicon.ico`; const CACHE_DURATION_MS = 12 * 60 * 60 * 1000; // 12 hours const ALL_APPS_BATCH_SIZE = 50; const CATEGORY_COLUMN_INITIAL_COUNT = 8; const CATEGORY_COLUMN_BATCH_SIZE = 10; const SEARCH_DEBOUNCE_MS = 220; const CANONICAL_CATEGORIES = [ "Browsers & Internet", "Communication & Collaboration", "Productivity & Office", "Media & Design", "Development", "Utilities & Tools", "Security & Privacy", "Networking & Remote", "Gaming & Game Tools", "Virtualization & Emulation", "Data & Analytics", "Education & Reference", "Science & Engineering", "Business & Finance", "System & OS", "Cloud & DevOps" ]; const PINNED_CATEGORIES = ["Browsers & Internet", "Productivity & Office", "Utilities & Tools"]; const CANONICAL_CATEGORY_MAP = { 'web browsers': "Browsers & Internet", 'internet': "Browsers & Internet", 'browsers': "Browsers & Internet", 'browser extensions': "Browsers & Internet", 'messaging': "Communication & Collaboration", 'chat': "Communication & Collaboration", 'communication': "Communication & Collaboration", 'social and communication': "Communication & Collaboration", 'communication and collaboration': "Communication & Collaboration", 'communication and messaging': "Communication & Collaboration", 'communication and social': "Communication & Collaboration", 'communication tools': "Communication & Collaboration", 'social media': "Communication & Collaboration", 'documents': "Productivity & Office", 'office': "Productivity & Office", 'productivity': "Productivity & Office", 'office and productivity': "Productivity & Office", 'document management': "Productivity & Office", 'document tools': "Productivity & Office", 'productivity tools': "Productivity & Office", 'media': "Media & Design", 'imaging': "Media & Design", 'graphics': "Media & Design", 'design': "Media & Design", 'music and video': "Media & Design", 'creative tools': "Media & Design", 'creativity': "Media & Design", 'design and graphics': "Media & Design", 'design and media': "Media & Design", 'design and photography': "Media & Design", 'design and publishing': "Media & Design", 'design assets': "Media & Design", 'design tools': "Media & Design", 'entertainment': "Media & Design", 'graphic design': "Media & Design", 'graphics and design': "Media & Design", 'media and design': "Media & Design", 'media tools': "Media & Design", 'multimedia': "Media & Design", 'fonts': "Media & Design", 'developer': "Development", 'development tools': "Development", 'dev': "Development", 'utilities': "Utilities & Tools", 'tools': "Utilities & Tools", 'other': "Utilities & Tools", 'compression': "Utilities & Tools", 'accessibility': "Utilities & Tools", 'customization': "Utilities & Tools", 'general utilities': "Utilities & Tools", 'hardware utilities': "Utilities & Tools", 'hobbies and interests': "Utilities & Tools", 'hobbies and leisure': "Utilities & Tools", 'home automation': "Utilities & Tools", 'information and news': "Utilities & Tools", 'lifestyle': "Utilities & Tools", 'smart home': "Utilities & Tools", 'specialized tools': "Utilities & Tools", 'sports and fitness': "Utilities & Tools", 'sports and recreation': "Utilities & Tools", 'system utilities': "Utilities & Tools", 'travel and navigation': "Utilities & Tools", 'error': 'Utilities & Tools', 'security': "Security & Privacy", 'security and privacy': "Security & Privacy", 'security tools': "Security & Privacy", 'file sharing': "Networking & Remote", 'networking': "Networking & Remote", 'internet and network': "Networking & Remote", 'internet tools': "Networking & Remote", 'internet utilities': "Networking & Remote", 'network tools': "Networking & Remote", 'network utilities': "Networking & Remote", 'networking tools': "Networking & Remote", 'gaming': "Gaming & Game Tools", 'games': "Gaming & Game Tools", 'gaming tools': "Gaming & Game Tools", 'gaming utilities': "Gaming & Game Tools", 'virtualization and emulation': "Virtualization & Emulation", 'data and analytics': "Data & Analytics", 'education': "Education & Reference", 'education and reference': "Education & Reference", 'education and science': "Education & Reference", 'education software': "Education & Reference", 'science and education': "Science & Engineering", 'science and engineering': "Science & Engineering", 'engineering software': "Science & Engineering", 'industrial and engineering': "Science & Engineering", 'science and gis': "Science & Engineering", 'science and research': "Science & Engineering", 'scientific': "Science & Engineering", 'scientific software': "Science & Engineering", 'scientific tools': "Science & Engineering", 'business': "Business & Finance", 'business and finance': "Business & Finance", 'business and productivity': "Business & Finance", 'business software': "Business & Finance", 'business tools': "Business & Finance", 'cryptocurrency tools': "Business & Finance", 'finance': "Business & Finance", 'finance and blockchain tools': "Business & Finance", 'runtimes': "System & OS", 'it and management': "System & OS", 'it and remote management': "System & OS", 'it management': "System & OS", 'it tools': "System & OS", 'system administration': "System & OS", 'system and customization': "System & OS", 'system monitoring': "System & OS", 'system tool': "System & OS", 'system tools': "System & OS", 'online storage': "Cloud & DevOps", 'cloud services': "Cloud & DevOps", 'cloud tools': "Cloud & DevOps", }; const KEYWORD_CATEGORY_MAP = [ { keywords: ['browser', 'web'], category: "Browsers & Internet" }, { keywords: ['chat', 'collaboration', 'voip', 'remote desktop', 'rdp', 'vnc', 'messaging'], category: "Communication & Collaboration" }, { keywords: ['office', 'word processor', 'spreadsheet', 'presentation', 'notes', 'email', 'pdf', 'document'], category: "Productivity & Office" }, { keywords: ['video', 'audio', 'music', 'image', 'photo', 'editor', 'player', 'design', 'cad', '3d', 'font'], category: "Media & Design" }, { keywords: ['ide', 'code', 'git', 'database', 'devops', 'terminal', 'api', 'compiler', 'sdk'], category: "Development" }, { keywords: ['uninstaller', 'backup', 'recovery', 'launcher', 'cleaner', 'registry', 'archive', 'zip'], category: "Utilities & Tools" }, { keywords: ['antivirus', 'password', 'encryption', 'vpn', 'privacy', 'firewall', 'malware'], category: "Security & Privacy" }, { keywords: ['ftp', 'ssh', 'sftp', 'torrent', 'p2p', 'network'], category: "Networking & Remote" }, { keywords: ['gaming', 'game'], category: "Gaming & Game Tools" }, { keywords: ['virtualization', 'emulator', 'vm', 'container', 'docker'], category: "Virtualization & Emulation" }, { keywords: ['data', 'analytics', 'bi', 'statistics'], category: "Data & Analytics" }, { keywords: ['education', 'reference', 'learning'], category: "Education & Reference" }, { keywords: ['science', 'engineering', 'math', 'gis'], category: "Science & Engineering" }, { keywords: ['finance', 'business', 'crypto', 'accounting'], category: "Business & Finance" }, { keywords: ['runtime', 'framework', 'driver', 'system', 'wsl'], category: "System & OS" }, { keywords: ['cloud', 'storage', 'dropbox', 'gdrive'], category: "Cloud & DevOps" }, ]; const SEARCH_ALIASES = { 'vscode': 'visual studio code', 'vs code': 'visual studio code', '7zip': '7-zip', 'pwsh': 'powershell', 'winrar': 'winrar', 'ps': 'powershell', 'node': 'nodejs' }; const INTENT_CHIPS = { 'pdf': [ { label: 'PDF Reader', filters: { tags: ['Pdf', 'Viewer'] } }, { label: 'PDF Editor', filters: { tags: ['Pdf', 'Editor'] } }, { label: 'PDF Printer', filters: { tags: ['Pdf', 'Printer'] } }, ] }; const defaultPresets = [ { name: "Fresh Windows Install", icon: "💻", items: ["Google Chrome", "7-Zip", "VLC", "Spotify", "PowerShell", "PowerToys", "Microsoft Edge", "Everything"] }, { name: "Helpdesk Tools", icon: "🛠️", items: ["AnyDesk", "TeamViewer", "7-Zip", "Everything", "Revo Uninstaller", "WizTree", "PuTTY", "WinSCP"] }, { name: "Developer Workstation", icon: "🚀", items: ["Visual Studio Code", "Git", "NodeJS", "Python 3", "Windows Terminal", "Docker Desktop", "Notepad++", "Postman"] }, { name: "Media & Streaming", icon: "🎬", items: ["VLC", "Audacity", "HandBrake", "ShareX", "OBS Studio", "K-Lite Codec Pack Full"] }, { name: "Security Toolkit", icon: "🛡️", items: ["Bitwarden", "KeePassXC", "VeraCrypt", "Mozilla Firefox", "Wireshark", "Nmap"] }, ]; const FACET_KEYWORDS = [ 'pdf', 'ssh', 'vpn', 'sftp', 'rdp', 'open source', 'oss', 'portable', 'x86', 'x64', '32-bit', '64-bit', 'gis', 'forensics', 'blockchain', 'crypto', 'cad', 'emulator', 'streaming', 'backup', 'uninstaller', 'torrent' ]; // Polyfill for requestIdleCallback const idleCallback = window.requestIdleCallback || function (handler) { return setTimeout(() => handler({ didTimeout: false, timeRemaining: () => 50 }), 1); }; const cancelIdleCallback = window.cancelIdleCallback || function (id) { clearTimeout(id); }; // ----------------------------------------------------------------------------- // STATE MANAGEMENT // ----------------------------------------------------------------------------- const state = { allApps: [], filteredApps: [], topTags: [], theme: 'dark', viewMode: 'category', // category, all-apps, tasks density: 'comfy', // comfy, compact selection: {}, // { "AppName": "winget" | "choco" } isSelectionDrawerOpen: false, isHelpOpen: false, filters: { search: '', installer: 'any', // any, winget, choco license: [], // Open Source, Freeware, Commercial arch: [], // x86, x64 installType: [], // Portable popularity: 'all',// all, very-popular tags: [] }, scriptOptions: { type: 'powershell', // powershell, cmd verbose: false, scope: 'machine', // machine, user nonInteractive: true, acceptEulas: true, continueOnError: false }, options: { sortBy: 'smart', // smart, name, downloads, updated }, presets: [], __caps: { winget: true, choco: true }, // Dataset capabilities, default to true }; function loadState() { // Invalidate stale settings if the script version changes significantly const lastVersion = GM_getValue('scriptVersion'); if (lastVersion !== SCRIPT_VERSION) { GM_setValue('filters', {}); // Reset filters which are schema-dependent GM_setValue('options', {}); GM_setValue('scriptVersion', SCRIPT_VERSION); console.log(`NoNinite: Upgraded from ${lastVersion} to ${SCRIPT_VERSION}. Resetting filters.`); } const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; state.theme = GM_getValue('theme', systemTheme); state.density = GM_getValue('density', 'comfy'); state.filters = { ...state.filters, ...GM_getValue('filters', {}) }; state.options = { ...state.options, ...GM_getValue('options', {}) }; state.scriptOptions = { ...state.scriptOptions, ...GM_getValue('scriptOptions', {}) }; state.presets = GM_getValue('presets', structuredClone(defaultPresets)); state.selection = GM_getValue('selection', {}); } function saveState(key) { const stateToSave = { theme: state.theme, density: state.density, filters: state.filters, options: state.options, scriptOptions: state.scriptOptions, selection: state.selection, presets: state.presets }; if (key && stateToSave.hasOwnProperty(key)) { GM_setValue(key, stateToSave[key]); } else { for (const [k, v] of Object.entries(stateToSave)) GM_setValue(k, v); } } // ----------------------------------------------------------------------------- // UI - STYLES // ----------------------------------------------------------------------------- function injectStyles() { GM_addStyle(` :root { --nn-font-sans: Segoe UI, Inter, system-ui, -apple-system, BlinkMacSystemFont, sans-serif; --nn-font-mono: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', Consolas, monaco, monospace; --nn-z-modal: 10000; --nn-z-drawer: 9990; --nn-z-header: 9980; --nn-z-selection-bar: 9970; --nn-z-toast: 10010; --nn-header-height: 60px; --nn-nav-height: 50px; --nn-toolbar-height: 50px; --nn-selection-bar-height: 55px; --nn-filter-rail-width: 260px; --nn-drawer-width: 550px; --nn-ease-out: cubic-bezier(0.25, 0.46, 0.45, 0.94); --nn-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); } /* Themes */ :root, [data-theme="dark"] { --nn-bg-base: #121212; --nn-bg-surface: #1e1e1e; --nn-bg-surface-2: #2a2a2a; --nn-bg-surface-3: #333333; --nn-bg-surface-hover: #3c3c3c; --nn-bg-surface-active: #4a4a4a; --nn-text-primary: #e0e0e0; --nn-text-secondary: #a0a0a0; --nn-text-tertiary: #757575; --nn-text-link: #64b5f6; --nn-text-danger: #f47174; --nn-text-success: #81c784; --nn-text-warning: #ffd54f; --nn-border-color: #424242; --nn-border-color-strong: #616161; --nn-accent-primary: #2979ff; --nn-accent-primary-text: #ffffff; --nn-accent-secondary: #323232; --nn-scrollbar-thumb: #555; --nn-scrollbar-track: var(--nn-bg-surface); --nn-shadow-sm: 0 1px 3px rgba(0,0,0,0.3); --nn-shadow-md: 0 4px 6px rgba(0,0,0,0.4); --nn-shadow-lg: 0 10px 15px rgba(0,0,0,0.5); } [data-theme="light"] { --nn-bg-base: #f5f5f5; --nn-bg-surface: #ffffff; --nn-bg-surface-2: #f0f0f0; --nn-bg-surface-3: #e0e0e0; --nn-bg-surface-hover: #eeeeee; --nn-bg-surface-active: #e0e0e0; --nn-text-primary: #212121; --nn-text-secondary: #616161; --nn-text-tertiary: #9e9e9e; --nn-text-link: #1976d2; --nn-border-color: #e0e0e0; --nn-border-color-strong: #bdbdbd; --nn-accent-primary: #1e88e5; --nn-accent-secondary: #e3f2fd; --nn-scrollbar-thumb: #bdbdbd; --nn-scrollbar-track: var(--nn-bg-surface-2); --nn-shadow-sm: 0 1px 3px rgba(0,0,0,0.1); --nn-shadow-md: 0 4px 6px rgba(0,0,0,0.1); --nn-shadow-lg: 0 10px 15px rgba(0,0,0,0.1); } /* Reset & Base */ #noninite-root *, #noninite-root *::before, #noninite-root *::after { box-sizing: border-box; } #noninite-root { font-family: var(--nn-font-sans); background-color: var(--nn-bg-base); color: var(--nn-text-primary); font-size: 14px; line-height: 1.5; height: 100vh; width: 100vw; position: fixed; top: 0; left: 0; display: grid; grid-template-rows: var(--nn-header-height) auto 1fr; grid-template-columns: var(--nn-filter-rail-width) 1fr; grid-template-areas: "header header" "rail nav" "rail main"; transition: background-color 0.3s var(--nn-ease-out), color 0.3s var(--nn-ease-out); } #noninite-root a { color: var(--nn-text-link); text-decoration: none; } #noninite-root button { font-family: inherit; color: inherit; background: none; border: none; cursor: pointer; padding: 0; } #noninite-root button:disabled { cursor: not-allowed; opacity: 0.5; } #noninite-root ::-webkit-scrollbar { width: 10px; height: 10px; } #noninite-root ::-webkit-scrollbar-track { background: var(--nn-scrollbar-track); } #noninite-root ::-webkit-scrollbar-thumb { background: var(--nn-scrollbar-thumb); border-radius: 5px; } #noninite-root ::-webkit-scrollbar-thumb:hover { background: #777; } /* Focus Ring */ #noninite-root :focus-visible { outline: 2px solid var(--nn-accent-primary) !important; outline-offset: 2px; box-shadow: 0 0 0 4px color-mix(in srgb, var(--nn-accent-primary) 30%, transparent) !important; border-radius: 4px; } /* Layout components */ .nn-header { grid-area: header; z-index: var(--nn-z-header); display: flex; align-items: center; padding: 0 24px; background-color: var(--nn-bg-surface); border-bottom: 1px solid var(--nn-border-color); box-shadow: var(--nn-shadow-sm); } .nn-filter-rail { grid-area: rail; z-index: var(--nn-z-header); display: flex; flex-direction: column; background-color: var(--nn-bg-surface); border-right: 1px solid var(--nn-border-color); overflow-y: auto; padding: 16px; gap: 24px; } .nn-main-nav { grid-area: nav; display: flex; align-items: center; padding: 8px 24px; border-bottom: 1px solid var(--nn-border-color); } .nn-main-content { grid-area: main; position: relative; display: flex; flex-direction: column; overflow: hidden; } .nn-content-toolbar { display: flex; align-items: center; justify-content: flex-end; padding: 0 24px; height: var(--nn-toolbar-height); min-height: var(--nn-toolbar-height); border-bottom: 1px solid var(--nn-border-color); gap: 16px; } .nn-results-area { flex-grow: 1; overflow-y: auto; padding: 24px; } /* Header */ .nn-header__logo { display: flex; align-items: center; gap: 12px; font-size: 1.25rem; font-weight: 600; } .nn-header__logo img { height: 28px; } .nn-header__search-container { flex-grow: 1; margin: 0 40px; } .nn-search-wrapper { position: relative; } .nn-search-wrapper .nn-icon { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); color: var(--nn-text-tertiary); } .nn-search-input { width: 100%; height: 40px; padding: 0 16px 0 45px; font-size: 1rem; background-color: var(--nn-bg-base); color: var(--nn-text-primary); border: 1px solid var(--nn-border-color); border-radius: 8px; transition: border-color 0.2s, box-shadow 0.2s; } .nn-search-input:focus { border-color: var(--nn-accent-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--nn-accent-primary) 20%, transparent); } .nn-header__actions { display: flex; align-items: center; gap: 8px; } .nn-icon-btn { display: inline-flex; justify-content: center; align-items: center; width: 36px; height: 36px; border-radius: 50%; color: var(--nn-text-secondary); transition: background-color 0.2s, color 0.2s; } .nn-icon-btn:hover { background-color: var(--nn-bg-surface-hover); color: var(--nn-text-primary); } /* Filter Rail */ .nn-filter-group__title { font-size: 0.8rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; color: var(--nn-text-secondary); margin: 0 0 12px; } .nn-filter-options { display: flex; flex-direction: column; gap: 8px; } .nn-filter-options--row { flex-direction: row; flex-wrap: wrap; } .nn-checkbox-label, .nn-radio-label { display: flex; align-items: center; gap: 8px; cursor: pointer; } .nn-checkbox-label input, .nn-radio-label input { accent-color: var(--nn-accent-primary); } .nn-tag-chip { padding: 4px 10px; border-radius: 16px; font-size: 0.85rem; background-color: var(--nn-bg-surface-2); border: 1px solid var(--nn-border-color); color: var(--nn-text-secondary); transition: all 0.2s; cursor: pointer; } .nn-tag-chip:hover { background-color: var(--nn-bg-surface-hover); border-color: var(--nn-border-color-strong); color: var(--nn-text-primary); } .nn-tag-chip.nn-active { background-color: var(--nn-accent-secondary); border-color: var(--nn-accent-primary); color: var(--nn-accent-primary); font-weight: 500; } [data-theme="dark"] .nn-tag-chip.nn-active { color: var(--nn-text-link); } .nn-intent-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; } /* Main Nav & Toolbar */ .nn-main-nav__tabs { display: flex; gap: 8px; } .nn-tab-btn { padding: 10px 16px; border-radius: 999px; border: 1px solid var(--nn-border-color); background: var(--nn-bg-surface); color: var(--nn-text-secondary); transition: all .15s ease; font-weight: 500; position: relative; } .nn-tab-btn:hover { background: var(--nn-bg-surface-hover); color: var(--nn-text-primary); } .nn-tab-btn.nn-active { color: var(--nn-accent-primary-text); background: linear-gradient(180deg, var(--nn-accent-primary) 0%, #1a5cff 100%); border-color: var(--nn-accent-primary); box-shadow: 0 2px 8px rgba(0,0,0,.35); } .nn-tab-btn.nn-active::after { display: none; } .nn-select-control { padding: 6px 12px; border-radius: 6px; background-color: var(--nn-bg-surface-2); border: 1px solid var(--nn-border-color); } /* Results Area - Category View */ .nn-pinned-categories-container, .nn-other-categories-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 24px; } .nn-other-categories-container { margin-top: 24px; } .nn-category-column { display: flex; flex-direction: column; background-color: var(--nn-bg-surface); border: 1px solid var(--nn-border-color); border-radius: 8px; height: 420px; overflow: hidden; } .nn-category-column__title { font-size: 1.1rem; font-weight: 600; padding: 6px 12px; border-bottom: 1px solid var(--nn-border-color); flex-shrink: 0; } .nn-app-list { list-style: none; margin: 0; padding: 8px; flex-grow: 1; overflow-y: auto; } /* App List Item (Category View) */ .nn-app-list-item { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 12px; padding: 8px; border-radius: 6px; cursor: pointer; transition: background-color 0.2s; outline: 2px solid transparent; outline-offset: 0; } .nn-app-list-item:hover { background-color: var(--nn-bg-surface-hover); } .nn-app-list-item.nn-selected { background-color: color-mix(in srgb, var(--nn-accent-primary) 15%, transparent); outline: 2px solid color-mix(in srgb, var(--nn-accent-primary) 40%, transparent); } .nn-app-list-item:focus-within { background-color: var(--nn-bg-surface-hover); } .nn-app-list-item__icon { width: 32px; height: 32px; object-fit: contain; border-radius: 4px; } .nn-app-list-item__info { overflow: hidden; } .nn-app-list-item__name { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .nn-app-list-item__desc { font-size: 0.85rem; color: var(--nn-text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .nn-app-list-item__actions { display: flex; } .nn-installer-toggle { font-weight: 700; font-size: 0.75rem; width: 24px; height: 24px; border-radius: 4px; border: 1px solid var(--nn-border-color-strong); background-color: var(--nn-bg-surface-3); color: var(--nn-text-secondary); transition: all 0.2s; } .nn-installer-toggle:first-of-type { border-top-right-radius: 0; border-bottom-right-radius: 0; } .nn-installer-toggle:last-of-type { border-top-left-radius: 0; border-bottom-left-radius: 0; border-left: none; } .nn-installer-toggle:hover:not(:disabled) { background-color: var(--nn-bg-surface-hover); color: var(--nn-text-primary); } .nn-installer-toggle.nn-selected { background: linear-gradient(180deg, var(--nn-accent-primary) 0%, #1a5cff 100%); color: var(--nn-accent-primary-text); border-color: var(--nn-accent-primary); box-shadow: 0 0 0 2px color-mix(in srgb, var(--nn-accent-primary) 35%, transparent); transform: translateY(-1px); } /* Results Area - All Apps View */ .nn-all-apps-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; } .nn-app-card { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 16px; background-color: var(--nn-bg-surface); border-radius: 8px; border: 1px solid var(--nn-border-color); cursor: pointer; transition: transform 0.2s, box-shadow 0.2s, outline 0.2s; outline: 2px solid transparent; outline-offset: 0; } .nn-app-card:hover { transform: translateY(-3px); box-shadow: var(--nn-shadow-md); } .nn-app-card.nn-selected { border-color: var(--nn-accent-primary); box-shadow: 0 0 0 2px color-mix(in srgb, var(--nn-accent-primary) 50%, transparent); outline: 2px solid color-mix(in srgb, var(--nn-accent-primary) 40%, transparent); } .nn-app-card__icon { width: 48px; height: 48px; margin-bottom: 12px; } .nn-app-card__name { font-weight: 600; margin-bottom: 4px; } .nn-app-card__desc { font-size: 0.85rem; color: var(--nn-text-secondary); flex-grow: 1; margin-bottom: 12px; } /* Results Area - Tasks View */ .nn-tasks-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 16px; } .nn-preset-card { display: flex; flex-direction: column; padding: 20px; background-color: var(--nn-bg-surface); border-radius: 8px; border: 1px solid var(--nn-border-color); cursor: pointer; transition: transform 0.2s, box-shadow 0.2s; } .nn-preset-card:hover { transform: translateY(-3px); box-shadow: var(--nn-shadow-md); } .nn-preset-card__header { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; } .nn-preset-card__icon { font-size: 2rem; } .nn-preset-card__name { font-size: 1.1rem; font-weight: 600; } .nn-preset-card__items { list-style: none; padding: 0; margin: 0; color: var(--nn-text-secondary); font-size: 0.9rem; } /* Selection Bar */ .nn-selection-bar { grid-column: 1 / -1; position: fixed; bottom: 0; left: 0; right: 0; height: 0; z-index: var(--nn-z-selection-bar); background-color: var(--nn-bg-surface); box-shadow: 0 -2px 10px rgba(0,0,0,0.3); transform: translateY(100%); transition: transform 0.3s var(--nn-ease-in-out), height 0.3s var(--nn-ease-in-out); } .nn-selection-bar.nn-visible { transform: translateY(0); height: var(--nn-selection-bar-height); } .nn-selection-bar__content { max-width: 1200px; margin: 0 auto; display: flex; align-items: center; justify-content: flex-end; height: 100%; padding: 0 24px; position: relative; } #nn-selection-count { position: absolute; left: 50%; transform: translateX(-50%); text-align: center; font-weight: 700; } .nn-btn { padding: 8px 16px; border-radius: 6px; font-weight: 500; transition: background-color 0.2s; } #nn-open-drawer-btn { font-weight: 700; border-radius: 999px; padding: 10px 20px; background: linear-gradient(180deg, #00a2ff 0%, #006eff 100%); color: white; border: 1px solid #0a5cff; box-shadow: 0 6px 16px rgba(0, 82, 204, 0.35); } #nn-open-drawer-btn.nn-pulse { animation: nn-pulse 0.7s ease-out 1; } @keyframes nn-pulse { 0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(0, 82, 204, 0.5); } 70% { transform: scale(1.03); box-shadow: 0 0 0 8px rgba(0, 82, 204, 0); } 100% { transform: scale(1); } } .nn-btn--primary { background-color: var(--nn-accent-primary); color: var(--nn-accent-primary-text); } .nn-btn--primary:hover { background-color: color-mix(in srgb, var(--nn-accent-primary) 85%, black); } .nn-btn--secondary { background-color: var(--nn-bg-surface-3); color: var(--nn-text-primary); } .nn-btn--secondary:hover { background-color: var(--nn-bg-surface-hover); } /* Selection Drawer */ .nn-selection-drawer-scrim { position: fixed; inset: 0; background-color: rgba(0,0,0,0.5); z-index: calc(var(--nn-z-drawer) - 1); opacity: 0; visibility: hidden; transition: opacity 0.3s; } .nn-selection-drawer { position: fixed; top: 0; right: 0; bottom: 0; width: var(--nn-drawer-width); max-width: 100vw; background-color: var(--nn-bg-base); z-index: var(--nn-z-drawer); display: flex; flex-direction: column; box-shadow: var(--nn-shadow-lg); transform: translateX(100%); transition: transform 0.3s var(--nn-ease-in-out); } .nn-selection-drawer.nn-open { transform: translateX(0); } .nn-selection-drawer.nn-open + .nn-selection-drawer-scrim { opacity: 1; visibility: visible; } .nn-drawer-header { display: flex; justify-content: space-between; align-items: center; padding: 16px 24px; border-bottom: 1px solid var(--nn-border-color); flex-shrink: 0; } .nn-drawer-header__title { font-size: 1.25rem; font-weight: 600; } .nn-drawer-body { flex-grow: 1; display: flex; flex-direction: column; overflow-y: auto; } .nn-drawer-section { padding: 24px; border-bottom: 1px solid var(--nn-border-color); } .nn-drawer-section__title { font-weight: 600; margin-bottom: 16px; } .nn-selected-apps-list { list-style: none; padding: 0; margin: 0; } .nn-selected-app-item { display: flex; align-items: center; gap: 12px; padding: 8px 0; } .nn-selected-app-item__icon { width: 24px; height: 24px; } .nn-selected-app-item__name { flex-grow: 1; } .nn-remove-item { margin-left: auto; width: 28px; height: 28px; font-size: 1.2rem; color: var(--nn-text-tertiary); } .nn-remove-item:hover { color: var(--nn-text-danger); background-color: color-mix(in srgb, var(--nn-text-danger) 15%, transparent); } .nn-fallback-badge { font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; background-color: var(--nn-text-warning); color: #000; padding: 2px 6px; border-radius: 4px; margin-left: 8px; } .nn-script-options-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px 24px; } .nn-drawer-footer { flex-shrink: 0; padding: 16px 24px; background-color: var(--nn-bg-surface); display: flex; flex-direction: column; gap: 16px; border-top: 1px solid var(--nn-border-color); } .nn-script-preview-tabs { display: flex; border-bottom: 1px solid var(--nn-border-color); margin: -24px -24px 16px; } .nn-script-preview-tab { flex: 1; text-align: center; padding: 12px; font-weight: 500; color: var(--nn-text-secondary); cursor: pointer; border-bottom: 2px solid transparent; } .nn-script-preview-tab.nn-active { color: var(--nn-text-primary); border-color: var(--nn-accent-primary); } #nn-script-preview-container { background-color: var(--nn-bg-base); border: 1px solid var(--nn-border-color); border-radius: 6px; min-height: 200px; max-height: 40vh; overflow: auto; } #nn-script-preview { font-family: var(--nn-font-mono); font-size: 0.9rem; white-space: pre; padding: 16px; } .nn-drawer-footer__actions { display: flex; gap: 12px; } .nn-drawer-footer__actions .nn-btn { flex-grow: 1; padding: 10px 16px; border: 1px solid var(--nn-border-color); background: linear-gradient(180deg, var(--nn-bg-surface-2) 0%, var(--nn-bg-surface-3) 100%); box-shadow: 0 3px 10px rgba(0,0,0,0.25); } .nn-drawer-footer__actions .nn-btn--primary { background: linear-gradient(180deg, var(--nn-accent-primary) 0%, #1a5cff 100%); border-color: var(--nn-accent-primary); color: var(--nn-accent-primary-text); } /* Utility Classes */ .nn-no-results, .nn-loading { text-align: center; padding: 40px; color: var(--nn-text-secondary); font-size: 1.1rem; } .nn-sentinel { height: 1px; } /* Toast Notifications */ #nn-toast-container { position: fixed; bottom: 20px; right: 20px; z-index: var(--nn-z-toast); display: flex; flex-direction: column; gap: 10px; } .nn-toast { padding: 12px 20px; border-radius: 6px; color: var(--nn-accent-primary-text); box-shadow: var(--nn-shadow-lg); opacity: 0; transform: translateX(100%); animation: nn-toast-in 0.3s forwards, nn-toast-out 0.3s 2.7s forwards; display: flex; align-items: center; gap: 10px; } .nn-toast.nn-success { background-color: #4CAF50; } .nn-toast.nn-error { background-color: #F44336; } @keyframes nn-toast-in { from { opacity: 0; transform: translateX(100%); } to { opacity: 1; transform: translateX(0); } } @keyframes nn-toast-out { from { opacity: 1; transform: translateX(0); } to { opacity: 0; transform: translateX(100%); } } /* Help Modal */ .nn-modal-scrim { position: fixed; inset: 0; background-color: rgba(0,0,0,0.6); z-index: var(--nn-z-modal); opacity: 0; visibility: hidden; transition: opacity 0.3s; } .nn-modal-scrim.nn-visible { opacity: 1; visibility: visible; } .nn-modal-dialog { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0.95); background-color: var(--nn-bg-surface); border-radius: 12px; width: 90vw; max-width: 600px; box-shadow: var(--nn-shadow-lg); opacity: 0; visibility: hidden; transition: all 0.3s; } .nn-modal-scrim.nn-visible .nn-modal-dialog { transform: translate(-50%, -50%) scale(1); opacity: 1; visibility: visible; } .nn-modal-header { display: flex; justify-content: space-between; align-items: center; padding: 16px 24px; border-bottom: 1px solid var(--nn-border-color); } .nn-modal-title { font-size: 1.25rem; font-weight: 600; } .nn-modal-body { padding: 24px; max-height: 70vh; overflow-y: auto; } .nn-modal-body kbd { background-color: var(--nn-bg-surface-3); border: 1px solid var(--nn-border-color-strong); padding: 2px 6px; border-radius: 4px; font-family: var(--nn-font-mono); } #loading-indicator { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 1.2rem; color: var(--nn-text-secondary); } `); } // ----------------------------------------------------------------------------- // UI - SKELETON // ----------------------------------------------------------------------------- function buildSkeleton() { $('body > *').not('script, style').hide(); const skeletonHTML = `
Keyboard Shortcuts:
${escapeHtml(app.description)}
No applications match your criteria.
'); return; } const grouped = apps.reduce((acc, app) => { (acc[app.category] = acc[app.category] || []).push(app); return acc; }, {}); const $pinnedContainer = $('').appendTo($resultsArea); const $otherContainer = $('').appendTo($resultsArea); let columnsRendered = 0; const renderColumn = (category, container) => { if (!grouped[category] || grouped[category].length === 0) return; columnsRendered++; const $column = $(`No categories to show for your current filters.
No applications match your criteria.
'); return; } let currentOffset = 0; const renderBatch = () => { const batch = apps.slice(currentOffset, currentOffset + ALL_APPS_BATCH_SIZE); if (batch.length === 0) { if(allAppsObserver) { allAppsObserver.disconnect(); allAppsObserver = null; } return; } const html = batch.map(createAppCardHTML).join(''); const $sentinel = $resultsArea.find('.nn-sentinel'); if ($sentinel.length) { $(html).insertBefore($sentinel); } else { $resultsArea.append(html); } currentOffset += batch.length; wireIconFallbacks($resultsArea.find('.nn-app-card:not(.wired)')); updateSelectionStateOnItems($resultsArea.find('.nn-app-card:not(.wired-selection)')); if (currentOffset >= apps.length) { if(allAppsObserver) { allAppsObserver.disconnect(); allAppsObserver = null; } $sentinel.remove(); } }; $resultsArea.append(''); allAppsObserver = new IntersectionObserver((entries) => { if (entries[0].isIntersecting) { renderBatch(); } }, { root: null, rootMargin: '500px' }); allAppsObserver.observe($resultsArea.find('.nn-sentinel')[0]); renderBatch(); // Render the first batch immediately } function renderTasksView() { if (allAppsObserver) { allAppsObserver.disconnect(); allAppsObserver = null; } const $resultsArea = $('.nn-results-area').empty().addClass('nn-tasks-grid').removeClass('nn-all-apps-grid'); const html = state.presets.map(preset => `No applications selected.
'); generateScript(); return; } let wingetAppsHTML = ''; let chocoAppsHTML = ''; Object.entries(state.selection).forEach(([appName, installerType]) => { const app = state.allApps.find(a => a.name === appName); if (!app) return; const { winget, choco } = app.facets.installers; let finalInstaller = installerType; let isFallback = false; // Corrected fallback logic if (finalInstaller === 'winget' && !winget && choco) { finalInstaller = 'choco'; isFallback = true; } else if (finalInstaller === 'choco' && !choco && winget) { finalInstaller = 'winget'; isFallback = true; } const itemHTML = `