// ============================================ // COOKBOOK MODULE (v2 — simplified) // What Fits? + Saved presets, inline action panels // ============================================ import uiModule from './ui.js'; import spinnerModule from './spinner.js'; import { providerLogo } from './providers.js'; import { makeWindowDraggable } from './windowDrag.js'; import { _diagnose, _showDiagnosis, _clearDiagnosis, _runQuickCmd, ERROR_PATTERNS } from './cookbook-diagnosis.js'; import { _hwfitCache, _hwfitDebounce, _hwfitFetch, _hwfitInit, _hwfitRenderList, _hwfitRenderHw, _renderGpuToggles, _expandModelRow, _fitColors, _hwfitColumns, _cachedModelIds, _gpuToggleTotal, _resetGpuToggleState } from './cookbook-hwfit.js'; // Sub-modules import { initRunning, _loadTasks, _saveTasks, _addTask, _removeTask, _tmuxCmd, _renderRunningTab, _clearCookbookNotif, _launchServeTask, _serveAutoFix, _serveAutoRetry, _serveAutoRetryReplace, _serveAutoRetryRemove, _startBackgroundMonitor, _syncFromServer, _retryDownload, _nextAvailablePort, _processQueue, _selfHealStaleTasks, } from './cookbookRunning.js'; import { initDownload, _setPanelField, _setPanelCheckbox, _wirePanelEvents, _runPanelCmd, _runModelDownload, _buildDownloadCmd, } from './cookbookDownload.js'; import { initServe, _fetchCachedModels, _cachedAllModels, _filterCachedList, _rerenderCachedModels, _deleteCachedModel, } from './cookbookServe.js'; const STORAGE_KEY = 'cookbook-presets'; const LAST_STATE_KEY = 'cookbook-last-state'; const SERVE_STATE_KEY = 'cookbook-serve-state'; // Global, once: tag chip rows (.doclib-lang-chips) scroll horizontally on mobile. // Stop their touch events (capture phase, before any ancestor sees them) so a // sideways tag scroll never triggers a swipe-to-change-tab / swipe-dismiss // gesture in ANY modal (cookbook, document library, etc.). We don't preventDefault, // so the browser's native horizontal scroll of the chips still works. if (typeof window !== 'undefined' && !window._tagScrollGuardWired) { window._tagScrollGuardWired = true; ['touchstart', 'touchmove'].forEach(evt => { document.addEventListener(evt, (e) => { const t = e.target; if (t && t.closest && t.closest('.doclib-lang-chips')) e.stopPropagation(); }, true); }); } // Radio-style check marking which model directory is a server's download target. // OFF = hollow circle (pickable); ON = checked circle (accent-tinted via CSS). export const _MODELDIR_CHECK_OFF = ''; export const _MODELDIR_CHECK_ON = ''; // Monochrome platform glyphs (currentColor) for a server's OS tag: a penguin for // Linux, the four-pane logo for Windows, an Android robot for Termux/Android. function _platformIcon(platform) { const k = (platform || '').toLowerCase(); if (k === 'windows') { return ''; } if (k === 'termux' || k === 'android') { return ''; } if (k === 'linux' || k === 'termux-linux') { return ''; } return ''; } export let _envState = { env: 'none', envPath: '', hfToken: '', hfTokenConfigured: false, hfTokenMasked: '', gpus: '', remoteHost: '', servers: [], modelPaths: [], platform: '', defaultServer: '' }; let _lastCacheHostVal = null; let _cookbookOpeningSpinners = []; export function _lastCacheHost() { return _lastCacheHostVal; } export function _setLastCacheHost(v) { _lastCacheHostVal = v; } function _setCookbookOpening(on) { // Sidebar (tool-cookbook-btn) deliberately excluded — the inline // whirlpool on the sidebar row read as "the click didn't register" // rather than "loading", which made users (rightly) think clicks // were being eaten. Keep only the icon-rail spinner since the // rail is narrow enough that an obvious loading state still helps. const targets = [ document.getElementById('rail-cookbook'), ].filter(Boolean); if (!on) { _cookbookOpeningSpinners.forEach(({ spinner, wrap, target }) => { try { spinner?.stop?.(); } catch {} try { wrap?.remove?.(); } catch {} target?.classList?.remove('cookbook-opening'); }); _cookbookOpeningSpinners = []; return; } if (_cookbookOpeningSpinners.length) return; targets.forEach(target => { const spinner = spinnerModule.create('', 'clean', 'whirlpool'); spinner._wpSize = target.id === 'rail-cookbook' ? 12 : 13; const wrap = document.createElement('span'); wrap.className = 'cookbook-open-loading'; wrap.appendChild(spinner.createElement()); target.appendChild(wrap); target.classList.add('cookbook-opening'); spinner.start(); _cookbookOpeningSpinners.push({ spinner, wrap, target }); }); } /** Build server