Spaces:
Running
Running
openoperator / plugins /_model_config /extensions /webui /chat-input-progress-start /model-switcher.html
Download plugins/_model_config/extensions/webui/chat-input-progress-start/model-switcher.html from Leon4gr45/openoperator: direct link, hf CLI and curl.
- Browser
- Download file 17.3 kB
-
https://huggingface.co/spaces/Leon4gr45/openoperator/resolve/main/plugins/_model_config/extensions/webui/chat-input-progress-start/model-switcher.html
- Command line
-
hf download hf://spaces/Leon4gr45/openoperator/plugins/_model_config/extensions/webui/chat-input-progress-start/model-switcher.html
-
curl -L -o model-switcher.html https://huggingface.co/spaces/Leon4gr45/openoperator/resolve/main/plugins/_model_config/extensions/webui/chat-input-progress-start/model-switcher.html
17.3 kB
| <!-- Model Switcher - Floating Preset Selector --> | |
| <script type="module"> | |
| import { store } from "/plugins/_model_config/webui/model-config-store.js"; | |
| </script> | |
| <div x-data> | |
| <template x-if="$store.modelConfig"> | |
| <div x-data="{ showDropdown: false, showProfileDropdown: false }" | |
| x-init=" | |
| await Promise.all([ | |
| $store.modelConfig.refreshSwitcher($store.chats?.selected || ''), | |
| $store.modelConfig.loadAgentProfiles(), | |
| ]); | |
| $watch('$store.chats.selected', v => $store.modelConfig.refreshSwitcher(v || '')); | |
| "> | |
| <template x-if="($store.modelConfig.switcherAllowed && !$store.modelConfig.switcherLoading) || $store.chats?.selectedContext?.agent_profile"> | |
| <div class="model-switcher-container"> | |
| <div class="model-context-strip"> | |
| <template x-if="$store.modelConfig.switcherAllowed && !$store.modelConfig.switcherLoading"> | |
| <div class="model-switcher-anchor" @click.outside="showDropdown = false"> | |
| <button class="btn-icon-action model-switcher-btn" | |
| :class="{ 'has-override': !!$store.modelConfig.switcherOverride }" | |
| @click="showDropdown = !showDropdown; showProfileDropdown = false"> | |
| <x-icon style="font-size: 16px;" name="neurology"></x-icon> | |
| <span class="model-switcher-label" x-text="$store.modelConfig.getSwitcherLabel()"></span> | |
| <x-icon style="font-size: 0.7rem;" | |
| :name="showDropdown ? 'expand_less' : 'expand_more'"></x-icon> | |
| </button> | |
| <!-- Dropdown (opens upward) --> | |
| <div class="model-switcher-dropdown" x-show="showDropdown" x-transition.opacity style="display: none;"> | |
| <!-- Return to the project/profile selection --> | |
| <template x-if="$store.modelConfig.switcherOverride"> | |
| <div class="model-switcher-item revert" @click=" | |
| $store.modelConfig.clearOverrideSwitch($store.chats?.selected || ''); | |
| showDropdown = false; | |
| "> | |
| <x-icon style="font-size: 15px;" name="undo"></x-icon> | |
| <span>Use scoped preset</span> | |
| </div> | |
| </template> | |
| <div class="model-switcher-divider" x-show="$store.modelConfig.switcherOverride"></div> | |
| <!-- No presets message --> | |
| <template x-if="$store.modelConfig.switcherPresets.length === 0"> | |
| <div class="model-switcher-item disabled">No presets configured</div> | |
| </template> | |
| <!-- Preset list --> | |
| <template x-for="preset in $store.modelConfig.switcherPresets" :key="preset.name"> | |
| <div class="model-switcher-item" | |
| :class="{ 'active': $store.modelConfig.switcherEffectivePreset === preset.name }" | |
| @click=" | |
| $store.modelConfig.selectPresetSwitch($store.chats?.selected || '', preset.name); | |
| showDropdown = false; | |
| "> | |
| <div class="model-switcher-preset-name" x-text="preset.name"></div> | |
| <div class="model-switcher-preset-models"> | |
| <template x-if="preset.chat?.name"> | |
| <div class="model-switcher-model-row"> | |
| <span class="model-switcher-model-label">Main</span> | |
| <span class="model-switcher-model-value"> | |
| <span x-text="preset.chat.provider" style="opacity:0.5;"></span> | |
| <span style="opacity:0.3; margin:0 3px;">/</span> | |
| <span x-text="preset.chat.name"></span> | |
| </span> | |
| </div> | |
| </template> | |
| <template x-if="preset.vision?.name && (!preset.chat?.vision || preset.vision?.override_main)"> | |
| <div class="model-switcher-model-row"> | |
| <span class="model-switcher-model-label">Vision</span> | |
| <span class="model-switcher-model-value"> | |
| <span x-text="preset.vision.provider" style="opacity:0.5;"></span> | |
| <span style="opacity:0.3; margin:0 3px;">/</span> | |
| <span x-text="preset.vision.name"></span> | |
| </span> | |
| </div> | |
| </template> | |
| <template x-if="preset.utility?.name"> | |
| <div class="model-switcher-model-row"> | |
| <span class="model-switcher-model-label">Utility</span> | |
| <span class="model-switcher-model-value"> | |
| <span x-text="preset.utility.provider" style="opacity:0.5;"></span> | |
| <span style="opacity:0.3; margin:0 3px;">/</span> | |
| <span x-text="preset.utility.name"></span> | |
| </span> | |
| </div> | |
| </template> | |
| <template x-if="preset.embedding?.name"> | |
| <div class="model-switcher-model-row"> | |
| <span class="model-switcher-model-label">Embed</span> | |
| <span class="model-switcher-model-value"> | |
| <span x-text="preset.embedding.provider" style="opacity:0.5;"></span> | |
| <span style="opacity:0.3; margin:0 3px;">/</span> | |
| <span x-text="preset.embedding.name"></span> | |
| </span> | |
| </div> | |
| </template> | |
| </div> | |
| </div> | |
| </template> | |
| <!-- Edit Presets shortcut --> | |
| <div class="model-switcher-divider" style="opacity:0.2;"></div> | |
| <div class="model-switcher-item model-switcher-edit" @click=" | |
| $store.modelConfig.openPresetEditor( | |
| $store.modelConfig.switcherEffectivePreset, | |
| $store.chats?.selected || '' | |
| ); | |
| showDropdown = false; | |
| "> | |
| <x-icon style="font-size: 14px;" name="tune"></x-icon> | |
| <span>Edit Presets</span> | |
| </div> | |
| </div> | |
| </div> | |
| </template> | |
| <template x-if="$store.chats?.selectedContext?.agent_profile"> | |
| <div class="agent-profile-anchor" @click.outside="showProfileDropdown = false"> | |
| <button class="btn-icon-action agent-profile-btn" | |
| title="Active agent profile" | |
| @click="showProfileDropdown = !showProfileDropdown; showDropdown = false"> | |
| <span class="agent-profile-avatar" | |
| :style="`background:${$store.modelConfig.getAgentProfileVisual($store.chats.selectedContext.agent_profile, $store.chats.selectedContext.agent_profile_label).color}`"> | |
| <img x-show="$store.modelConfig.getAgentProfileVisual($store.chats.selectedContext.agent_profile, $store.chats.selectedContext.agent_profile_label).url" | |
| :src="$store.modelConfig.getAgentProfileVisual($store.chats.selectedContext.agent_profile, $store.chats.selectedContext.agent_profile_label).url" | |
| alt=""> | |
| <span x-show="!$store.modelConfig.getAgentProfileVisual($store.chats.selectedContext.agent_profile, $store.chats.selectedContext.agent_profile_label).url" | |
| x-text="$store.modelConfig.getAgentProfileVisual($store.chats.selectedContext.agent_profile, $store.chats.selectedContext.agent_profile_label).initials"></span> | |
| </span> | |
| <span class="agent-profile-label" | |
| x-text="$store.chats.selectedContext.agent_profile_label || $store.chats.selectedContext.agent_profile"></span> | |
| <x-icon style="font-size: 0.7rem;" | |
| :name="showProfileDropdown ? 'expand_less' : 'expand_more'"></x-icon> | |
| </button> | |
| <div class="agent-profile-dropdown" x-show="showProfileDropdown" x-transition.opacity style="display: none;"> | |
| <template x-if="$store.modelConfig.agentProfilesLoading"> | |
| <div class="model-switcher-item disabled">Loading profiles...</div> | |
| </template> | |
| <template x-for="profile in $store.modelConfig.getAgentProfileList($store.chats.selectedContext.agent_profile, $store.chats.selectedContext.agent_profile_label)" :key="profile.key"> | |
| <div class="agent-profile-row" | |
| x-show="!$store.modelConfig.agentProfilesLoading" | |
| :class="{ 'active': profile.key === $store.chats.selectedContext.agent_profile }"> | |
| <button type="button" class="model-switcher-item agent-profile-item" | |
| :disabled="$store.chats.selectedContext.running || $store.modelConfig.agentProfileSaving" | |
| :class="{ 'disabled': $store.chats.selectedContext.running || $store.modelConfig.agentProfileSaving }" | |
| @click=" | |
| if (profile.key === $store.chats.selectedContext.agent_profile) { | |
| showProfileDropdown = false; | |
| } else { | |
| $store.modelConfig.selectAgentProfile($store.chats?.selected || '', profile.key) | |
| .then(ok => { if (ok) showProfileDropdown = false; }); | |
| } | |
| "> | |
| <span class="agent-profile-avatar" :style="`background:${$store.modelConfig.getAgentProfileVisual(profile.key, profile.label).color}`"> | |
| <img x-show="$store.modelConfig.getAgentProfileVisual(profile.key, profile.label).url" :src="$store.modelConfig.getAgentProfileVisual(profile.key, profile.label).url" alt=""> | |
| <span x-show="!$store.modelConfig.getAgentProfileVisual(profile.key, profile.label).url" x-text="$store.modelConfig.getAgentProfileVisual(profile.key, profile.label).initials"></span> | |
| </span> | |
| <div class="model-switcher-preset-name" x-text="profile.label || profile.key"></div> | |
| </button> | |
| <button type="button" class="agent-profile-row-edit" | |
| :aria-label="`Edit ${profile.label || profile.key}`" | |
| @click=" | |
| showProfileDropdown = false; | |
| window.openAgentEditor?.({ | |
| view: 'edit', | |
| profileId: profile.key, | |
| contextId: $store.chats?.selected || '' | |
| }); | |
| "> | |
| <x-icon name="edit"></x-icon> | |
| <span>Edit</span> | |
| </button> | |
| </div> | |
| </template> | |
| <div class="model-switcher-divider" style="opacity:0.2;"></div> | |
| <button class="model-switcher-item agent-profile-settings" @click=" | |
| showProfileDropdown = false; | |
| window.openAgentEditor?.({ view: 'manage', contextId: $store.chats?.selected || '' }); | |
| "> | |
| <x-icon style="font-size: 14px;" name="manage_accounts"></x-icon> | |
| <span>Manage agents</span> | |
| </button> | |
| </div> | |
| </div> | |
| </template> | |
| <x-extension id="model-context-strip-end"></x-extension> | |
| </div> | |
| </div> | |
| </template> | |
| </div> | |
| </template> | |
| </div> | |
| <style> | |
| .model-switcher-container { | |
| position: relative; | |
| z-index: 10; | |
| } | |
| .model-context-strip { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| min-width: 0; | |
| flex-wrap: wrap; | |
| } | |
| .model-context-strip > x-extension, | |
| .model-context-strip > x-extension > x-component, | |
| .model-context-strip > x-extension > x-component > div[x-data] { | |
| display: contents; | |
| } | |
| .agent-profile-avatar { | |
| width: 1.35rem; | |
| height: 1.35rem; | |
| display: inline-grid; | |
| place-items: center; | |
| flex: 0 0 auto; | |
| overflow: hidden; | |
| border-radius: 50%; | |
| color: #fff; | |
| font-size: .58rem; | |
| font-weight: 700; | |
| } | |
| .agent-profile-avatar img { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| } | |
| .model-switcher-anchor { | |
| position: relative; | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .model-switcher-btn { | |
| width: auto; | |
| height: auto; | |
| gap: 4px; | |
| padding: var(--spacing-xs) var(--spacing-sm); | |
| border: none; | |
| font-size: 0.75rem; | |
| white-space: nowrap; | |
| flex-shrink: 0; | |
| } | |
| .model-switcher-btn:hover { | |
| border: none; | |
| } | |
| .model-switcher-btn.has-override { | |
| /* color: var(--color-text); */ | |
| } | |
| .model-switcher-label { | |
| max-width: min(240px, 38vw); | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| .agent-profile-anchor { | |
| position: relative; | |
| display: flex; | |
| align-items: center; | |
| } | |
| .agent-profile-btn { | |
| width: auto; | |
| height: auto; | |
| gap: 4px; | |
| padding: var(--spacing-xs) var(--spacing-sm); | |
| border: none; | |
| font-size: 0.75rem; | |
| white-space: nowrap; | |
| flex-shrink: 0; | |
| } | |
| .agent-profile-btn:hover { | |
| border: none; | |
| } | |
| .agent-profile-label { | |
| max-width: 140px; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| /* Dropdown - opens upward, aligned to left */ | |
| .model-switcher-dropdown { | |
| position: absolute; | |
| bottom: calc(100% + 6px); | |
| left: 0; | |
| min-width: 280px; | |
| max-height: 550px; | |
| overflow-y: auto; | |
| background-color: var(--color-panel); | |
| border: 1px solid var(--color-border); | |
| border-radius: 8px; | |
| padding: 6px; | |
| box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); | |
| z-index: 100; | |
| } | |
| .agent-profile-dropdown { | |
| position: absolute; | |
| bottom: calc(100% + 6px); | |
| left: 0; | |
| min-width: 260px; | |
| max-width: min(320px, 90vw); | |
| max-height: 550px; | |
| overflow-y: auto; | |
| background-color: var(--color-panel); | |
| border: 1px solid var(--color-border); | |
| border-radius: 8px; | |
| padding: 6px; | |
| box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); | |
| z-index: 100; | |
| } | |
| .model-switcher-item { | |
| padding: 6px 10px; | |
| border-radius: 6px; | |
| cursor: pointer; | |
| font-size: 0.8rem; | |
| transition: background 0.1s ease; | |
| } | |
| .model-switcher-item:hover { | |
| background: var(--color-background-hover, rgba(255,255,255,0.06)); | |
| } | |
| .model-switcher-item.active { | |
| background: color-mix(in srgb, var(--color-highlight) 12%, transparent); | |
| } | |
| .agent-profile-row { | |
| display: grid; | |
| grid-template-columns: minmax(0, 1fr) auto; | |
| align-items: center; | |
| border-radius: 6px; | |
| transition: background 0.1s ease; | |
| } | |
| .agent-profile-row:hover { | |
| background: var(--color-background-hover, rgba(255,255,255,0.06)); | |
| } | |
| .agent-profile-row.active { | |
| background: color-mix(in srgb, var(--color-highlight) 12%, transparent); | |
| } | |
| .agent-profile-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 7px; | |
| width: 100%; | |
| min-width: 0; | |
| border: 0; | |
| background: transparent; | |
| color: var(--color-text); | |
| text-align: left; | |
| } | |
| .agent-profile-row .agent-profile-item:hover { | |
| background: transparent; | |
| } | |
| .agent-profile-row-edit { | |
| display: flex; | |
| align-items: center; | |
| gap: 4px; | |
| margin-right: 4px; | |
| min-height: 24px; | |
| padding: 4px 6px; | |
| border: 0; | |
| border-radius: 4px; | |
| background: transparent; | |
| color: var(--color-text); | |
| font: inherit; | |
| font-size: 0.72rem; | |
| cursor: pointer; | |
| opacity: 0.72; | |
| white-space: nowrap; | |
| } | |
| .agent-profile-row-edit:hover { | |
| background: color-mix(in srgb, var(--color-text) 8%, transparent); | |
| opacity: 1; | |
| } | |
| .agent-profile-row-edit x-icon { | |
| font-size: 0.8rem; | |
| } | |
| .model-switcher-item.revert { | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| opacity: 0.8; | |
| } | |
| .model-switcher-item.disabled { | |
| opacity: 0.4; | |
| cursor: default; | |
| font-style: italic; | |
| } | |
| .model-switcher-item.disabled:hover { | |
| background: transparent; | |
| } | |
| .model-switcher-divider { | |
| border-top: 1px solid var(--color-border); | |
| margin: 4px 0; | |
| opacity: 0.3; | |
| } | |
| .model-switcher-preset-name { | |
| font-weight: 500; | |
| } | |
| .model-switcher-preset-models { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 2px; | |
| margin-top: 4px; | |
| } | |
| .model-switcher-model-row { | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| font-size: 0.7rem; | |
| opacity: 0.7; | |
| } | |
| .model-switcher-model-label { | |
| width: 40px; | |
| flex-shrink: 0; | |
| font-weight: 500; | |
| opacity: 0.7; | |
| } | |
| .model-switcher-model-value { | |
| flex: 1; | |
| min-width: 0; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| white-space: nowrap; | |
| } | |
| .model-switcher-edit { | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| opacity: 0.6; | |
| font-size: 0.75rem; | |
| } | |
| .agent-profile-settings { | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| width: 100%; | |
| border: none; | |
| background: transparent; | |
| color: var(--color-text); | |
| opacity: 0.7; | |
| text-align: left; | |
| font-family: inherit; | |
| } | |
| @media (max-width: 600px) { | |
| .model-switcher-label { | |
| max-width: 160px; | |
| } | |
| .agent-profile-label { | |
| max-width: 92px; | |
| } | |
| } | |
| </style> | |