Merge pull request #2964 from DarokCx/release Featherless model search improvements.
Signed| @@ -2260,7 +2260,37 @@ | ||
| 2260 | 2260 | <div data-for="api_key_featherless" class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you reload the page."> |
| 2261 | 2261 | For privacy reasons, your API key will be hidden after you reload the page. |
| 2262 | 2262 | </div> |
| 2263 | - <select id="featherless_model"> | |
| 2263 | + <hr> | |
| 2264 | + <h4 data-i18n="Featherless Model Selection">Featherless Model Selection</h4> | |
| 2265 | + <div class="flex-container wide100p flexGap10"> | |
| 2266 | + <div class="flex1 overflowHidden wide100p"> | |
| 2267 | + <div class="flex-container marginBot10 alignitemscenter"> | |
| 2268 | + <input id="featherless_model_search_bar" class="text_pole width100p flex1 margin0" type="search" data-i18n="[placeholder]Search..." placeholder="Search..."> | |
| 2269 | + <select id="featherless_model_sort_order" class="margin0 text_pole"> | |
| 2270 | + <option value="search" data-i18n="Search" hidden>A-Z</option> | |
| 2271 | + <option value="asc">A-Z</option> | |
| 2272 | + <option value="desc">Z-A</option> | |
| 2273 | + <option value="date_asc">Date Asc</option> | |
| 2274 | + <option value="date_desc">Date Desc</option> | |
| 2275 | + </select> | |
| 2276 | + <select id="featherless_category_selection" class="text_pole"> | |
| 2277 | + <option value="" disabled selected data-i18n="category">category</option> | |
| 2278 | + <!-- <option value="Favorite" data-i18n="Top">Favorite</option> --> | |
| 2279 | + <option value="Top" data-i18n="Top">Top</option> | |
| 2280 | + <option value="New" data-i18n="New">New</option> | |
| 2281 | + <option value="All" data-i18n="All">All</option> | |
| 2282 | + </select> | |
| 2283 | + <select id="featherless_class_selection" class="text_pole"> | |
| 2284 | + <option value="" selected data-i18n="class">All Classes</option> | |
| 2285 | + </select> | |
| 2286 | + <div id="featherless_model_pagination_container" class="flex1"></div> | |
| 2287 | + <i id="featherless_model_grid_toggle" class="fa-solid fa-table-cells-large menu_button" data-i18n="[title]Toggle grid view" title="Toggle grid view"></i> | |
| 2288 | + </div> | |
| 2289 | + <div id="featherless_model_card_block" data-i18n="[no_desc_text]No model description" no_desc_text="[No description]"></div> | |
| 2290 | + </div> | |
| 2291 | + </div> | |
| 2292 | + <!-- Do not remove. Needed for the /model command to function --> | |
| 2293 | + <select id="featherless_model" class="displayNone"> | |
| 2264 | 2294 | <option value="" data-i18n="-- Connect to the API --"> |
| 2265 | 2295 | -- Connect to the API -- |
| 2266 | 2296 | </option> |
| @@ -4,6 +4,7 @@ import { textgenerationwebui_settings as textgen_settings, textgen_types } from | ||
| 4 | 4 | import { tokenizers } from './tokenizers.js'; |
| 5 | 5 | import { renderTemplateAsync } from './templates.js'; |
| 6 | 6 | import { POPUP_TYPE, callGenericPopup } from './popup.js'; |
| 7 | +import { PAGINATION_TEMPLATE } from './utils.js'; | |
| 7 | 8 | |
| 8 | 9 | let mancerModels = []; |
| 9 | 10 | let togetherModels = []; |
| @@ -265,19 +266,212 @@ export async function loadAphroditeModels(data) { | ||
| 265 | 266 | } |
| 266 | 267 | } |
| 267 | 268 | |
| 269 | +let featherlessCurrentPage = 1; | |
| 268 | 270 | export async function loadFeatherlessModels(data) { |
| 271 | + const searchBar = document.getElementById('featherless_model_search_bar'); | |
| 272 | + const modelCardBlock = document.getElementById('featherless_model_card_block'); | |
| 273 | + const paginationContainer = $('#featherless_model_pagination_container'); | |
| 274 | + const sortOrderSelect = document.getElementById('featherless_model_sort_order'); | |
| 275 | + const classSelect = document.getElementById('featherless_class_selection'); | |
| 276 | + const categoriesSelect = document.getElementById('featherless_category_selection'); | |
| 277 | + const storageKey = 'FeatherlessModels_PerPage'; | |
| 278 | + | |
| 279 | + // Store the original models data for search and filtering | |
| 280 | + let originalModels = []; | |
| 281 | + | |
| 269 | 282 | if (!Array.isArray(data)) { |
| 270 | 283 | console.error('Invalid Featherless models data', data); |
| 271 | 284 | return; |
| 272 | 285 | } |
| 273 | 286 | |
| 287 | + // Sort the data by model id (default A-Z) | |
| 274 | 288 | data.sort((a, b) => a.id.localeCompare(b.id)); |
| 289 | + originalModels = data; // Store the original data for search | |
| 275 | 290 | featherlessModels = data; |
| 276 | 291 | |
| 277 | - if (!data.find(x => x.id === textgen_settings.featherless_model)) { | |
| 292 | + // Populate class select options with unique classes | |
| 278 | - textgen_settings.featherless_model = data[0]?.id || ''; | |
| 293 | + populateClassSelection(data); | |
| 294 | + | |
| 295 | + // Retrieve the stored number of items per page or default to 5 | |
| 296 | + const perPage = Number(localStorage.getItem(storageKey)) || 10; | |
| 297 | + | |
| 298 | + // Initialize pagination with the full set of models | |
| 299 | + const selectedModelPage = (data.findIndex(x => x.id === textgen_settings.featherless_model) / perPage) + 1; | |
| 300 | + featherlessCurrentPage = selectedModelPage > 0 ? selectedModelPage : 1; | |
| 301 | + setupPagination(originalModels, perPage); | |
| 302 | + | |
| 303 | + // Function to set up pagination (also used for filtered results) | |
| 304 | + function setupPagination(models, perPage, pageNumber = featherlessCurrentPage) { | |
| 305 | + paginationContainer.pagination({ | |
| 306 | + dataSource: models, | |
| 307 | + pageSize: perPage, | |
| 308 | + pageNumber: pageNumber, | |
| 309 | + sizeChangerOptions: [6, 10, 26, 50, 100, 250, 500, 1000], | |
| 310 | + pageRange: 1, | |
| 311 | + showPageNumbers: true, | |
| 312 | + showSizeChanger: false, | |
| 313 | + prevText: '<', | |
| 314 | + nextText: '>', | |
| 315 | + formatNavigator: function (currentPage, totalPage) { | |
| 316 | + return (currentPage - 1) * perPage + 1 + ' - ' + currentPage * perPage + ' of ' + totalPage * perPage; | |
| 317 | + }, | |
| 318 | + showNavigator: true, | |
| 319 | + callback: function (modelsOnPage, pagination) { | |
| 320 | + modelCardBlock.innerHTML = ''; | |
| 321 | + | |
| 322 | + modelsOnPage.forEach(model => { | |
| 323 | + const card = document.createElement('div'); | |
| 324 | + card.classList.add('model-card'); | |
| 325 | + | |
| 326 | + const modelNameContainer = document.createElement('div'); | |
| 327 | + modelNameContainer.classList.add('model-name-container'); | |
| 328 | + | |
| 329 | + const modelTitle = document.createElement('div'); | |
| 330 | + modelTitle.classList.add('model-title'); | |
| 331 | + modelTitle.textContent = model.id.replace(/_/g, '_\u200B'); | |
| 332 | + modelNameContainer.appendChild(modelTitle); | |
| 333 | + | |
| 334 | + const detailsContainer = document.createElement('div'); | |
| 335 | + detailsContainer.classList.add('details-container'); | |
| 336 | + | |
| 337 | + const modelClassDiv = document.createElement('div'); | |
| 338 | + modelClassDiv.classList.add('model-class'); | |
| 339 | + modelClassDiv.textContent = `Class: ${model.model_class || 'N/A'}`; | |
| 340 | + | |
| 341 | + const contextLengthDiv = document.createElement('div'); | |
| 342 | + contextLengthDiv.classList.add('model-context-length'); | |
| 343 | + contextLengthDiv.textContent = `Context Length: ${model.context_length}`; | |
| 344 | + | |
| 345 | + const dateAddedDiv = document.createElement('div'); | |
| 346 | + dateAddedDiv.classList.add('model-date-added'); | |
| 347 | + dateAddedDiv.textContent = `Added On: ${new Date(model.updated_at).toLocaleDateString()}`; | |
| 348 | + | |
| 349 | + detailsContainer.appendChild(modelClassDiv); | |
| 350 | + detailsContainer.appendChild(contextLengthDiv); | |
| 351 | + detailsContainer.appendChild(dateAddedDiv); | |
| 352 | + | |
| 353 | + card.appendChild(modelNameContainer); | |
| 354 | + card.appendChild(detailsContainer); | |
| 355 | + | |
| 356 | + modelCardBlock.appendChild(card); | |
| 357 | + | |
| 358 | + if (model.id === textgen_settings.featherless_model) { | |
| 359 | + card.classList.add('selected'); | |
| 360 | + } | |
| 361 | + | |
| 362 | + card.addEventListener('click', function () { | |
| 363 | + document.querySelectorAll('.model-card').forEach(c => c.classList.remove('selected')); | |
| 364 | + card.classList.add('selected'); | |
| 365 | + onFeatherlessModelSelect(model.id); | |
| 366 | + }); | |
| 367 | + }); | |
| 368 | + | |
| 369 | + // Update the current page value whenever the page changes | |
| 370 | + featherlessCurrentPage = pagination.pageNumber; | |
| 371 | + }, | |
| 372 | + afterSizeSelectorChange: function (e) { | |
| 373 | + const newPerPage = e.target.value; | |
| 374 | + localStorage.setItem('Models_PerPage', newPerPage); | |
| 375 | + setupPagination(models, Number(newPerPage), featherlessCurrentPage); // Use the stored current page number | |
| 376 | + }, | |
| 377 | + }); | |
| 279 | 378 | } |
| 280 | 379 | |
| 380 | + // Unset previously added listeners | |
| 381 | + $(searchBar).off('input'); | |
| 382 | + $(sortOrderSelect).off('change'); | |
| 383 | + $(classSelect).off('change'); | |
| 384 | + $(categoriesSelect).off('change'); | |
| 385 | + | |
| 386 | + // Add event listener for input on the search bar | |
| 387 | + searchBar.addEventListener('input', function () { | |
| 388 | + applyFiltersAndSort(); | |
| 389 | + }); | |
| 390 | + | |
| 391 | + // Add event listener for the sort order select | |
| 392 | + sortOrderSelect.addEventListener('change', function () { | |
| 393 | + applyFiltersAndSort(); | |
| 394 | + }); | |
| 395 | + | |
| 396 | + // Add event listener for the class select | |
| 397 | + classSelect.addEventListener('change', function () { | |
| 398 | + applyFiltersAndSort(); | |
| 399 | + }); | |
| 400 | + | |
| 401 | + categoriesSelect.addEventListener('change', function () { | |
| 402 | + applyFiltersAndSort(); | |
| 403 | + }); | |
| 404 | + | |
| 405 | + // Function to populate class selection dropdown | |
| 406 | + function populateClassSelection(models) { | |
| 407 | + const uniqueClasses = [...new Set(models.map(model => model.model_class).filter(Boolean))]; // Get unique class names | |
| 408 | + uniqueClasses.sort((a, b) => a.localeCompare(b)); | |
| 409 | + uniqueClasses.forEach(className => { | |
| 410 | + const option = document.createElement('option'); | |
| 411 | + option.value = className; | |
| 412 | + option.textContent = className; | |
| 413 | + classSelect.appendChild(option); | |
| 414 | + }); | |
| 415 | + } | |
| 416 | + | |
| 417 | + // Function to apply sorting and filtering based on user input | |
| 418 | + async function applyFiltersAndSort() { | |
| 419 | + if (!(searchBar instanceof HTMLInputElement) || | |
| 420 | + !(sortOrderSelect instanceof HTMLSelectElement) || | |
| 421 | + !(classSelect instanceof HTMLSelectElement) || | |
| 422 | + !(categoriesSelect instanceof HTMLSelectElement)) { | |
| 423 | + return; | |
| 424 | + } | |
| 425 | + const searchQuery = searchBar.value.toLowerCase(); | |
| 426 | + const selectedSortOrder = sortOrderSelect.value; | |
| 427 | + const selectedClass = classSelect.value; | |
| 428 | + const selectedCategory = categoriesSelect.value; | |
| 429 | + let featherlessTop = []; | |
| 430 | + let featherlessNew = []; | |
| 431 | + | |
| 432 | + if (selectedCategory === 'Top') { | |
| 433 | + featherlessTop = await fetchFeatherlessStats(); | |
| 434 | + } | |
| 435 | + const featherlessIds = featherlessTop.map(stat => stat.id); | |
| 436 | + if (selectedCategory === 'New') { | |
| 437 | + featherlessNew = await fetchFeatherlessNew(); | |
| 438 | + } | |
| 439 | + const featherlessNewIds = featherlessNew.map(stat => stat.id); | |
| 440 | + | |
| 441 | + let filteredModels = originalModels.filter(model => { | |
| 442 | + const matchesSearch = model.id.toLowerCase().includes(searchQuery); | |
| 443 | + const matchesClass = selectedClass ? model.model_class === selectedClass : true; | |
| 444 | + const matchesTop = featherlessIds.includes(model.id); | |
| 445 | + const matchesNew = featherlessNewIds.includes(model.id); | |
| 446 | + | |
| 447 | + if (selectedCategory === 'All') { | |
| 448 | + return matchesSearch && matchesClass; | |
| 449 | + } | |
| 450 | + else if (selectedCategory === 'Top') { | |
| 451 | + return matchesSearch && matchesClass && matchesTop; | |
| 452 | + } | |
| 453 | + else if (selectedCategory === 'New') { | |
| 454 | + return matchesSearch && matchesClass && matchesNew; | |
| 455 | + } | |
| 456 | + else { | |
| 457 | + return matchesSearch; | |
| 458 | + } | |
| 459 | + }); | |
| 460 | + | |
| 461 | + if (selectedSortOrder === 'asc') { | |
| 462 | + filteredModels.sort((a, b) => a.id.localeCompare(b.id)); | |
| 463 | + } else if (selectedSortOrder === 'desc') { | |
| 464 | + filteredModels.sort((a, b) => b.id.localeCompare(a.id)); | |
| 465 | + } else if (selectedSortOrder === 'date_asc') { | |
| 466 | + filteredModels.sort((a, b) => a.updated_at.localeCompare(b.updated_at)); | |
| 467 | + } else if (selectedSortOrder === 'date_desc') { | |
| 468 | + filteredModels.sort((a, b) => b.updated_at.localeCompare(a.updated_at)); | |
| 469 | + } | |
| 470 | + | |
| 471 | + setupPagination(filteredModels, Number(localStorage.getItem(storageKey)) || perPage, featherlessCurrentPage); | |
| 472 | + } | |
| 473 | + | |
| 474 | + // Required to keep the /model command function | |
| 281 | 475 | $('#featherless_model').empty(); |
| 282 | 476 | for (const model of data) { |
| 283 | 477 | const option = document.createElement('option'); |
| @@ -288,15 +482,49 @@ export async function loadFeatherlessModels(data) { | ||
| 288 | 482 | } |
| 289 | 483 | } |
| 290 | 484 | |
| 291 | 485 | async function onFeatherlessModelSelectfetchFeatherlessStats() { |
| 292 | - const modelId = String($('#featherless_model').val()); | |
| 486 | + const response = await fetch('https://api.featherless.ai/feather/popular'); | |
| 487 | + const data = await response.json(); | |
| 488 | + return data.popular; | |
| 489 | +} | |
| 490 | + | |
| 491 | +async function fetchFeatherlessNew() { | |
| 492 | + const response = await fetch('https://api.featherless.ai/feather/models?sort=-created_at&perPage=10'); | |
| 493 | + const data = await response.json(); | |
| 494 | + return data.items; | |
| 495 | +} | |
| 496 | + | |
| 497 | +function onFeatherlessModelSelect(modelId) { | |
| 498 | + const model = featherlessModels.find(x => x.id === modelId); | |
| 293 | 499 | textgen_settings.featherless_model = modelId; |
| 500 | + $('#featherless_model').val(modelId); | |
| 294 | 501 | $('#api_button_textgenerationwebui').trigger('click'); |
| 295 | - const model = featherlessModels.find(x => x.id === modelId); | |
| 296 | 502 | setGenerationParamsFromPreset({ max_length: model.context_length }); |
| 297 | 503 | } |
| 298 | 504 | |
| 505 | +let featherlessIsGridView = false; // Default state set to grid view | |
| 506 | + | |
| 507 | +// Ensure the correct initial view is applied when the page loads | |
| 508 | +document.addEventListener('DOMContentLoaded', function () { | |
| 509 | + const modelCardBlock = document.getElementById('featherless_model_card_block'); | |
| 510 | + modelCardBlock.classList.add('list-view'); | |
| 511 | + | |
| 512 | + const toggleButton = document.getElementById('featherless_model_grid_toggle'); | |
| 513 | + toggleButton.addEventListener('click', function () { | |
| 514 | + // Toggle between grid and list view | |
| 515 | + if (featherlessIsGridView) { | |
| 516 | + modelCardBlock.classList.remove('grid-view'); | |
| 517 | + modelCardBlock.classList.add('list-view'); | |
| 518 | + this.title = 'Toggle to grid view'; | |
| 519 | + } else { | |
| 520 | + modelCardBlock.classList.remove('list-view'); | |
| 521 | + modelCardBlock.classList.add('grid-view'); | |
| 522 | + this.title = 'Toggle to list view'; | |
| 523 | + } | |
| 299 | 524 | |
| 525 | + featherlessIsGridView = !featherlessIsGridView; | |
| 526 | + }); | |
| 527 | +}); | |
| 300 | 528 | function onMancerModelSelect() { |
| 301 | 529 | const modelId = String($('#mancer_model').val()); |
| 302 | 530 | textgen_settings.mancer_model = modelId; |
| @@ -469,20 +697,6 @@ function getAphroditeModelTemplate(option) { | ||
| 469 | 697 | `)); |
| 470 | 698 | } |
| 471 | 699 | |
| 472 | -function getFeatherlessModelTemplate(option) { | |
| 473 | - const model = featherlessModels.find(x => x.id === option?.element?.value); | |
| 474 | - | |
| 475 | - if (!option.id || !model) { | |
| 476 | - return option.text; | |
| 477 | - } | |
| 478 | - | |
| 479 | - return $((` | |
| 480 | - <div class="flex-container flexFlowColumn"> | |
| 481 | - <div><strong>${DOMPurify.sanitize(model.name)}</strong> | <span>${model.context_length || '???'} tokens</span></div> | |
| 482 | - </div> | |
| 483 | - `)); | |
| 484 | -} | |
| 485 | - | |
| 486 | 700 | async function downloadOllamaModel() { |
| 487 | 701 | try { |
| 488 | 702 | const serverUrl = textgen_settings.server_urls[textgen_types.OLLAMA]; |
| @@ -670,9 +884,9 @@ export function initTextGenModels() { | ||
| 670 | 884 | $('#ollama_download_model').on('click', downloadOllamaModel); |
| 671 | 885 | $('#vllm_model').on('change', onVllmModelSelect); |
| 672 | 886 | $('#aphrodite_model').on('change', onAphroditeModelSelect); |
| 673 | - $('#featherless_model').on('change', onFeatherlessModelSelect); | |
| 674 | 887 | $('#tabby_download_model').on('click', downloadTabbyModel); |
| 675 | 888 | $('#tabby_model').on('change', onTabbyModelSelect); |
| 889 | + $('#featherless_model').on('change', () => onFeatherlessModelSelect(String($('#featherless_model').val()))); | |
| 676 | 890 | |
| 677 | 891 | const providersSelect = $('.openrouter_providers'); |
| 678 | 892 | for (const provider of OPENROUTER_PROVIDERS) { |
| @@ -745,13 +959,6 @@ export function initTextGenModels() { | ||
| 745 | 959 | width: '100%', |
| 746 | 960 | templateResult: getAphroditeModelTemplate, |
| 747 | 961 | }); |
| 748 | - $('#featherless_model').select2({ | |
| 749 | - placeholder: 'Select a model', | |
| 750 | - searchInputPlaceholder: 'Search models...', | |
| 751 | - searchInputCssClass: 'text_pole', | |
| 752 | - width: '100%', | |
| 753 | - templateResult: getFeatherlessModelTemplate, | |
| 754 | - }); | |
| 755 | 962 | providersSelect.select2({ |
| 756 | 963 | sorter: data => data.sort((a, b) => a.text.localeCompare(b.text)), |
| 757 | 964 | placeholder: 'Select providers. No selection = all providers.', |
| @@ -193,6 +193,7 @@ const settings = { | ||
| 193 | 193 | openrouter_allow_fallbacks: true, |
| 194 | 194 | xtc_threshold: 0.1, |
| 195 | 195 | xtc_probability: 0, |
| 196 | + featherless_model: '', | |
| 196 | 197 | }; |
| 197 | 198 | |
| 198 | 199 | export let textgenerationwebui_banned_in_macros = []; |
| @@ -5541,3 +5541,129 @@ body:not(.movingUI) .drawer-content.maximized { | ||
| 5541 | 5541 | #InstructSequencesColumn details:not(:last-of-type) { |
| 5542 | 5542 | margin-bottom: 5px; |
| 5543 | 5543 | } |
| 5544 | + | |
| 5545 | +#user_avatar_block { | |
| 5546 | + display: flex; | |
| 5547 | + flex-wrap: wrap; | |
| 5548 | + gap: 10px; | |
| 5549 | +} | |
| 5550 | + | |
| 5551 | +/* Main structure for the model cards */ | |
| 5552 | +.model-card { | |
| 5553 | + display: flex; | |
| 5554 | + justify-content: space-between; | |
| 5555 | + align-items: center; | |
| 5556 | + padding: 5px; | |
| 5557 | + border: 1px solid #333; | |
| 5558 | + border-radius: 8px; | |
| 5559 | + background-color: #222; | |
| 5560 | + color: #fff; | |
| 5561 | + margin: 7px; | |
| 5562 | + width: calc(100% - 7px); | |
| 5563 | + box-sizing: border-box; | |
| 5564 | + transition: transform 0.2s ease-in-out, background-color 0.2s ease-in-out, border 0.2s ease-in-out; | |
| 5565 | +} | |
| 5566 | + | |
| 5567 | +.model-card .details-container { | |
| 5568 | + text-align: right; | |
| 5569 | +} | |
| 5570 | + | |
| 5571 | +.model-card:hover { | |
| 5572 | + transform: scale(1.01); | |
| 5573 | + background-color: #444; | |
| 5574 | + transition: transform 0.2s ease-in-out, background-color 0.2s ease-in-out; /* Smooth transition */ | |
| 5575 | +} | |
| 5576 | + | |
| 5577 | +.model-card.selected { | |
| 5578 | + border: 2px solid var(--okGreen70a); | |
| 5579 | + background-color: var(--okGreen70a); | |
| 5580 | +} | |
| 5581 | + | |
| 5582 | +.model-info { | |
| 5583 | + flex: 1; | |
| 5584 | + white-space: nowrap; | |
| 5585 | + overflow: hidden; | |
| 5586 | + text-overflow: ellipsis; | |
| 5587 | +} | |
| 5588 | + | |
| 5589 | +.model-title { | |
| 5590 | + font-size: 13px; | |
| 5591 | + font-weight: bold; | |
| 5592 | + overflow: hidden; | |
| 5593 | +} | |
| 5594 | + | |
| 5595 | +.model-details { | |
| 5596 | + display: flex; | |
| 5597 | + flex-direction: column; | |
| 5598 | + align-items: flex-end; | |
| 5599 | + text-align: right; | |
| 5600 | + min-width: 120px; | |
| 5601 | +} | |
| 5602 | + | |
| 5603 | +.model-class, .model-context-length, .model-date-added { | |
| 5604 | + font-size: 10px; | |
| 5605 | +} | |
| 5606 | + | |
| 5607 | +.model-class, .model-context-length { | |
| 5608 | + margin-bottom: 5px; | |
| 5609 | +} | |
| 5610 | + | |
| 5611 | +#featherless_model_pagination_container .paginationjs-nav { | |
| 5612 | + min-width: max-content; | |
| 5613 | +} | |
| 5614 | + | |
| 5615 | +#featherless_model_card_block.grid-view { | |
| 5616 | + grid-template-columns: repeat(2, 1fr); | |
| 5617 | + display: flex; | |
| 5618 | + flex-wrap: wrap; | |
| 5619 | + /* gap: 3px; */ | |
| 5620 | + justify-content: flex-start; | |
| 5621 | +} | |
| 5622 | + | |
| 5623 | +/* Grid-view card */ | |
| 5624 | +#featherless_model_card_block.grid-view .model-card { | |
| 5625 | + flex-direction: column; | |
| 5626 | + flex: 1 1 calc(50% - 30px); | |
| 5627 | +} | |
| 5628 | + | |
| 5629 | +#featherless_model_search_bar { | |
| 5630 | + width: 15ch; | |
| 5631 | + flex-grow: 0; | |
| 5632 | + align-self: center; | |
| 5633 | +} | |
| 5634 | + | |
| 5635 | +#featherless_model_sort_order { | |
| 5636 | + width: auto; | |
| 5637 | + flex-shrink: 0; | |
| 5638 | + align-self: center; | |
| 5639 | +} | |
| 5640 | + | |
| 5641 | +#featherless_model_grid_toggle { | |
| 5642 | + flex-shrink: 0; | |
| 5643 | + width: auto; | |
| 5644 | + cursor: pointer; | |
| 5645 | +} | |
| 5646 | + | |
| 5647 | +#featherless_category_selection, | |
| 5648 | +#featherless_class_selection { | |
| 5649 | + display: flex; | |
| 5650 | + width: auto; | |
| 5651 | + align-self: center; | |
| 5652 | +} | |
| 5653 | + | |
| 5654 | +@media (max-width: 768px) { | |
| 5655 | + .model-card { | |
| 5656 | + flex-direction: column; | |
| 5657 | + align-items: stretch; | |
| 5658 | + } | |
| 5659 | + | |
| 5660 | + .model-info, .model-details, .model-date-added { | |
| 5661 | + width: 100%; | |
| 5662 | + text-align: left; | |
| 5663 | + } | |
| 5664 | + | |
| 5665 | + #featherless_model_search_bar { | |
| 5666 | + width: 100%; | |
| 5667 | + } | |
| 5668 | + | |
| 5669 | +} | |