Upgraded ui

This commit is contained in:
Saifeddine ALOUI 2024-10-24 01:46:59 +02:00
parent 792e2fad47
commit e97e3bcfd8
9 changed files with 1636 additions and 939 deletions

File diff suppressed because one or more lines are too long

8
web/dist/assets/index-DgI2-Yh4.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
web/dist/index.html vendored
View File

@ -6,8 +6,8 @@
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LoLLMS WebUI</title>
<script type="module" crossorigin src="/assets/index-C2ppRpsB.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BpT69KYd.css">
<script type="module" crossorigin src="/assets/index-TthUmaOU.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DgI2-Yh4.css">
</head>
<body>
<div id="app"></div>

View File

@ -1,17 +1,17 @@
<template>
<div class="relative group/item">
<button @click.prevent="toggleShowPersList" class="w-10 h-10 rounded-full overflow-hidden transition-transform duration-200 transform group-hover/item:scale-110 focus:outline-none">
<button @click.prevent="onSettingsPersonality" class="w-6 h-6 rounded-full overflow-hidden transition-transform duration-200 transform group-hover/item:scale-110 focus:outline-none">
<img :src="bUrl + mountedPers.avatar" @error="personalityImgPlacehodler" :alt="mountedPers.name" class="w-full h-full object-cover" :class="{'border-2 border-secondary': isActive}">
</button>
<div class="absolute -bottom-4 left-0 w-full flex items-center justify-center opacity-0 group-hover/item:opacity-100 transition-opacity duration-200 p-1">
<div class="absolute bottom-6 left-0 w-full flex items-center justify-center opacity-0 group-hover/item:opacity-100 transition-opacity duration-200 p-1">
<button @click.prevent="remount_personality()" class="p-1 bg-blue-500 rounded-full text-white hover:bg-blue-600 focus:outline-none" title="Remount">
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path></svg>
</button>
<button @click.prevent="handleOnTalk()" class="p-1 bg-green-500 rounded-full text-white hover:bg-green-600 focus:outline-none ml-1" title="Talk">
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"></path></svg>
</button>
<button @click.prevent="toggleShowPersList" class="p-1 bg-gray-500 rounded-full text-white hover:bg-gray-600 focus:outline-none ml-1" title="Show more">
<button class="p-1 bg-gray-500 rounded-full text-white hover:bg-gray-600 focus:outline-none ml-1" title="Show more">
<span class="text-xs font-bold">+{{ mountedPersArr.length - 1 }}</span>
</button>
</div>
@ -206,10 +206,6 @@ export default {
this.$refs.toast.showToast("Could not open personality settings. Endpoint error: " + error.message, 4, false)
}
},
toggleShowPersList() {
//this.show = !this.show
this.onShowPersList()
},
async constructor() {
nextTick(() => {

View File

@ -1,6 +1,15 @@
<template>
<div class="topbar-container" @mouseenter="show" @mouseleave="hide">
<div class="topbar" :class="{ 'topbar-hidden': !isVisible }">
<div class="topbar-container">
<!-- Add a thin strip at the top that triggers the hover -->
<div
class="hover-zone"
@mouseenter="show"
style="position: fixed; top: 0; left: 0; width: 100%; height: 10px; z-index: 50;"
></div>
<div class="topbar" :class="{ 'topbar-hidden': !isVisible }" @mouseleave="hide">
<!-- Rest of your topbar content -->
<div class="topbar-content">
<slot name="navigation"></slot>
<button class="pin-button" @click="togglePin" :title="isPinned ? 'Unpin' : 'Pin'">
@ -18,9 +27,142 @@
</svg>
</button>
<Navigation></Navigation>
<div class="toolbar-button" @mouseleave="hideInfosMenu">
<div class="relative inline-block">
<!-- Infos menu positioned above the button -->
<div v-show="isInfosMenuVisible" @mouseenter="showInfosMenu" class="absolute m-0 p-0 z-50 top-full right-0 transform bg-white dark:bg-gray-900 rounded-md shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none transition-all duration-300 ease-out mb-2">
<div class="p-4 container flex flex-col lg:flex-row items-center gap-2">
<!-- SYSTEM STATUS -->
<div class="flex gap-3 flex-1 items-center justify-end">
<div v-if="isModelOK" title="Model is ok" class="text-green-500 dark:text-green-400 cursor-pointer transition-transform hover:scale-110">
<svg class="w-8 h-8" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 12L11 14L15 10" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div v-else title="Model is not ok" class="text-red-500 dark:text-red-400 cursor-pointer transition-transform hover:scale-110">
<svg class="w-8 h-8" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15 9L9 15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 9L15 15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div v-if="!isGenerating" title="Text is not being generated. Ready to generate" class="text-green-500 dark:text-green-400 cursor-pointer transition-transform hover:scale-110">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 21v-4m0 0V5a2 2 0 012-2h6.5l1 1H21l-3 6 3 6h-8.5l-1-1H5a2 2 0 00-2 2zm9-13.5V9"></path>
</svg>
</div>
<div v-else title="Generation in progress..." class="text-yellow-500 dark:text-yellow-400 cursor-pointer transition-transform hover:scale-110">
<svg class="w-6 h-6 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
</div>
<div v-if="isConnected" title="Connection status: Connected" class="text-green-500 dark:text-green-400 cursor-pointer transition-transform hover:scale-110">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
</svg>
</div>
<div v-else title="Connection status: Not connected" class="text-red-500 dark:text-red-400 cursor-pointer transition-transform hover:scale-110">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"></path>
</svg>
</div>
</div>
<div class="flex items-center space-x-4">
<ActionButton @click="restartProgram" icon="power" title="restart program" />
<ActionButton @click="refreshPage" icon="refresh-ccw" title="refresh page" />
<ActionButton href="/docs" icon="file-text" title="Fast API doc" />
</div>
<!-- SOCIALS -->
<SocialIcon href="https://github.com/ParisNeo/lollms-webui" icon="github" />
<SocialIcon href="https://www.youtube.com/channel/UCJzrg0cyQV2Z30SQ1v2FdSQ" icon="youtube" />
<SocialIcon href="https://x.com/ParisNeo_AI" icon="x" />
<SocialIcon href="https://discord.com/channels/1092918764925882418" icon="discord" />
<div class="relative group" title="Lollms News">
<div @click="showNews()" class="text-2xl w-8 h-8 cursor-pointer transition-colors duration-300 text-gray-600 hover:text-primary dark:text-gray-300 dark:hover:text-primary">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-full h-full">
<path d="M19 20H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v1m2 13a2 2 0 0 1-2-2V7m2 13a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"></path>
</svg>
</div>
<span class="absolute hidden group-hover:block bg-gray-800 text-white text-xs rounded py-1 px-2 top-full left-1/2 transform -translate-x-1/2 mt-2 whitespace-nowrap">
Lollms News
</span>
</div>
</div>
</div>
<!-- Info Button -->
<div @mouseenter="showInfosMenu" class="infos-hover-area">
<button class="w-8 h-8">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 25" class="w-full h-full">
<!-- Circle background -->
<circle cx="12.5" cy="12.5" r="11.25" fill="#3498db"/>
<!-- "i" stem -->
<rect x="11.25" y="10" width="2.5" height="8.75" fill="white"/>
<!-- "i" dot -->
<circle cx="12.5" cy="6.25" r="1.25" fill="white"/>
</svg>
</button>
</div>
</div>
</div>
<div
v-if="is_fun_mode"
title="Fun mode is on, press to turn off"
class="w-8 h-8 cursor-pointer text-green-500 dark:text-green-400 hover:text-green-600 dark:hover:text-green-300 transition-colors duration-300"
@click="fun_mode_off()"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-full h-full animate-bounce">
<circle cx="12" cy="12" r="10"></circle>
<path d="M8 14s1.5 2 4 2 4-2 4-2"></path>
<line x1="9" y1="9" x2="9.01" y2="9"></line>
<line x1="15" y1="9" x2="15.01" y2="9"></line>
</svg>
</div>
<div
v-else
title="Fun mode is off, press to turn on"
class="w-8 h-8 cursor-pointer text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors duration-300"
@click="fun_mode_on()"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-full h-full">
<circle cx="12" cy="12" r="10"></circle>
<line x1="8" y1="15" x2="16" y2="15"></line>
<line x1="9" y1="9" x2="9.01" y2="9"></line>
<line x1="15" y1="9" x2="15.01" y2="9"></line>
</svg>
</div>
<span class="absolute hidden group-hover:block bg-gray-800 text-white text-xs rounded py-1 px-2 top-full left-1/2 transform -translate-x-1/2 mb-2 whitespace-nowrap">
{{ is_fun_mode ? 'Turn off fun mode' : 'Turn on fun mode' }}
</span>
<div class="language-selector relative">
<button @click="toggleLanguageMenu" class="bg-transparent text-black dark:text-white py-1 px-1 rounded font-bold uppercase transition-colors duration-300 hover:bg-blue-500">
{{ $store.state.language.slice(0, 2) }}
</button>
<div v-if="isLanguageMenuVisible" ref="languageMenu" class="container language-menu absolute left-0 mt-1 bg-white dark:bg-bg-dark-tone rounded shadow-lg z-10 overflow-y-auto scrollbar-thin scrollbar-track-bg-light-tone scrollbar-thumb-bg-light-tone-panel hover:scrollbar-thumb-primary dark:scrollbar-track-bg-dark-tone dark:scrollbar-thumb-bg-dark-tone-panel dark:hover:scrollbar-thumb-primary active:scrollbar-thumb-secondary" style="position: absolute; top: 100%; width: 200px; max-height: 300px; overflow-y: auto;">
<ul style="list-style-type: none; padding-left: 0; margin-left: 0;">
<li v-for="language in languages" :key="language" class="relative flex items-center" style="padding-left: 0; margin-left: 0;">
<button @click="deleteLanguage(language)" class="mr-2 text-red-500 hover:text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50 rounded-full"></button>
<div @click="selectLanguage(language)" :class="{'cursor-pointer hover:bg-blue-500 hover:text-white py-2 px-4 block whitespace-no-wrap': true, 'bg-blue-500 text-white': language === $store.state.language, 'flex-grow': true}">
{{ language }}
</div>
</li>
<li class="cursor-pointer hover:text-white py-0 px-0 block whitespace-no-wrap">
<input type="text" v-model="customLanguage" @keyup.enter.prevent="addCustomLanguage" placeholder="Enter language..." class="bg-transparent border border-gray-300 rounded py-0 px-0 mx-0 my-1 w-full">
</li>
</ul>
</div>
</div>
<div v-if="isDarkMode" class="sun text-2xl w-6 hover:text-primary duration-150 cursor-pointer" title="Switch to Light theme" @click="themeSwitch()">
<i data-feather="sun"></i>
</div>
<div v-else class="moon text-2xl w-6 hover:text-primary duration-150 cursor-pointer" title="Switch to Dark theme" @click="themeSwitch()">
<i data-feather="moon"></i>
</div>
</div>
</div>
<div class="placeholder" v-if="!isVisible"></div>
</div>
</template>
@ -34,11 +176,98 @@ export default {
},
data() {
return {
isinfosMenuVisible: false,
isVisible: false,
isPinned: false,
selectedLanguage: '',
isLanguageMenuVisible: false,
}
},
computed:{
isModelOK(){
return this.$store.state.isModelOk;
},
isDarkMode(){
return document.documentElement.classList.contains("dark");
},
languages: {
get(){
console.log("searching languages", this.$store.state.languages)
return this.$store.state.languages
}
},
language: {
get(){
console.log("searching language", this.$store.state.language)
return this.$store.state.language
}
},
is_fun_mode(){
try{
if (this.$store.state.config){
return this.$store.state.config.fun_mode;
}
else{
return false;
}
}
catch(error){
console.error("Oopsie! Looks like we hit a snag: ", error);
return false;
}
},
isGenerating(){
return this.$store.state.isGenerating;
},
isConnected(){
return this.$store.state.isConnected;
},
},
methods: {
themeSwitch() {
if (document.documentElement.classList.contains("dark")) {
document.documentElement.classList.remove("dark");
localStorage.setItem("theme", "light")
this.userTheme == "light"
this.iconToggle()
return
}
import('highlight.js/styles/tokyo-night-dark.css');
document.documentElement.classList.add("dark");
localStorage.setItem("theme", "dark")
this.userTheme == "dark"
this.iconToggle()
// Dispatch the themeChanged event
window.dispatchEvent(new Event('themeChanged'));
},
async selectLanguage(language) {
await this.$store.dispatch('changeLanguage', language);
this.toggleLanguageMenu(); // Fermer le menu après le changement de langue
this.language = language
},
async deleteLanguage(language) {
await this.$store.dispatch('deleteLanguage', language);
this.toggleLanguageMenu(); // Fermer le menu après le changement de langue
this.language = language
},
toggleLanguageMenu() {
console.log("Toggling language ",this.isLanguageMenuVisible)
this.isLanguageMenuVisible = !this.isLanguageMenuVisible;
},
showInfosMenu() {
console.log("showing menu")
this.isInfosMenuVisible = true;
},
hideInfosMenu() {
this.isInfosMenuVisible = false;
},
show() {
this.isVisible = true
},
@ -51,6 +280,59 @@ export default {
this.isPinned = !this.isPinned
this.isVisible = this.isPinned
},
fun_mode_on(){
console.log("Turning on fun mode")
this.$store.state.config.fun_mode=true;
this.applyConfiguration()
},
fun_mode_off(){
console.log("Turning off fun mode")
this.$store.state.config.fun_mode=false;
this.applyConfiguration()
},
showNews(){
this.$store.state.news.show()
},
themeCheck() {
if (this.userTheme == "dark" || (!this.userTheme && this.systemTheme)) {
document.documentElement.classList.add("dark");
this.moonIcon.classList.add("display-none");
nextTick(()=>{
//import('highlight.js/styles/tokyo-night-dark.css');
import('highlight.js/styles/stackoverflow-dark.css');
})
return
}
nextTick(()=>{
//import('highlight.js/styles/tomorrow-night-blue.css');
import('highlight.js/styles/stackoverflow-light.css');
})
this.sunIcon.classList.add("display-none")
},
iconToggle() {
this.sunIcon.classList.toggle("display-none");
this.moonIcon.classList.toggle("display-none");
},
refreshPage() {
const hostnameParts = window.location.href.split('/');
if(hostnameParts.length > 4){
window.location.href='/'
}
else{
window.location.reload(true);
}
},
handleOk(inputText) {
console.log("Input text:", inputText);
},
},
}
</script>
@ -107,4 +389,36 @@ export default {
.placeholder {
height: 10px;
}
.toolbar-button {
@apply bg-transparent border-none cursor-pointer p-2 transition-colors duration-300;
}
.toolbar-button:hover {
@apply text-blue-500; /* Assuming #007bff is close to Tailwind's blue-500 */
}
.topbar-container {
position: relative;
width: 100%;
}
.topbar {
position: fixed;
top: 0;
left: 0;
width: 100%;
background-color: white; /* or your preferred background color */
transition: transform 0.3s ease-in-out;
z-index: 40;
}
.topbar-hidden {
transform: translateY(-100%);
}
.hover-zone {
opacity: 0;
}
</style>

View File

@ -1,39 +1,167 @@
<template>
<div v-if="show"
class="fixed top-0 left-0 right-0 bottom-0 flex items-center justify-center bg-black bg-opacity-50 p-4">
<div class="relative w-full max-w-md">
<div
class="flex flex-col rounded-lg bg-bg-light-tone-panel dark:bg-bg-dark-tone-panel duration-150 shadow-lg max-h-screen">
<div class="flex flex-row flex-grow items-center m-2 p-1">
class="fixed top-0 left-0 right-0 bottom-0 flex items-center justify-center bg-black bg-opacity-50 p-4 overflow-hidden">
<div class="relative w-full max-w-md max-h-[80vh]">
<!-- Main Container with shadow and rounded corners -->
<div class="flex flex-col rounded-lg bg-bg-light-tone-panel dark:bg-bg-dark-tone-panel shadow-lg">
<!-- Header -->
<div class="flex flex-row items-center p-4 border-b border-gray-200 dark:border-gray-700">
<div class="grow flex items-center">
<i data-feather="sliders" class="mr-2 flex-shrink-0"></i>
<h3 class="text-lg font-semibold select-none mr-2">
{{ title }}</h3>
</div>
<!-- CLOSE BUTTON -->
<div class="items-end">
<button type="button" @click.stop="hide(false)" title="Close"
class=" bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-800 dark:hover:text-white">
<svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd"></path>
</svg>
<span class="sr-only">Close form modal</span>
</button>
<h3 class="text-lg font-semibold select-none">{{ title }}</h3>
</div>
<!-- Close Button -->
<button @click.stop="hide(false)" title="Close"
class="p-1.5 hover:bg-gray-200 rounded-lg dark:hover:bg-gray-800">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z">
</path>
</svg>
</button>
</div>
<!-- FORM AREA -->
<div class="flex flex-col relative no-scrollbar overflow-y-scroll p-2">
<!-- odd:bg-bg-light-tone odd:dark:bg-bg-dark-tone even:bg-bg-light-tone-panel dark:even:bg-bg-dark-tone-panel -->
<div class="px-2 " v-for="(item, index) in controls_array">
<div v-if="item.type == 'str' || item.type == 'string'">
<div v-if="!item.options">
<!-- Scrollable Content Area -->
<div class="overflow-y-auto p-4 max-h-[60vh] custom-scrollbar">
<div class="space-y-2">
<div v-for="(item, index) in controls_array" :key="index" class="p-1">
<div v-if="item.type == 'str' || item.type == 'string'">
<div v-if="!item.options">
<label
class="mb-2 relative flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white select-none"
:class="item.help ? 'cursor-pointer ' : ''">
<!-- TITLE -->
<div class="text-base font-semibold">
{{ item.name }}:
</div>
<!-- HELP BUTTON -->
<label v-if="item.help" class="relative inline-flex">
<input type="checkbox" v-model="item.isHelp" class="sr-only peer">
<div
class="hover:text-secondary duration-75 active:scale-90 peer-checked:text-primary">
<i data-feather="help-circle" class="w-5 h-5 "></i>
</div>
</label>
</label>
<!-- HELP DESCRIPTION -->
<p v-if="item.isHelp" class="text-sm font-normal text-gray-700 dark:text-gray-400 mb-2">
{{ item.help }}
</p>
<input type="text" v-model="item.value"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Enter string">
</div>
<div v-if="item.options">
<label
class="mb-2 relative flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white select-none"
:class="item.help ? 'cursor-pointer ' : ''">
<!-- TITLE -->
<div class="text-base font-semibold">
{{ item.name }}:
</div>
<!-- HELP BUTTON -->
<label v-if="item.help" class="relative inline-flex">
<input type="checkbox" v-model="item.isHelp" class="sr-only peer">
<div
class="hover:text-secondary duration-75 active:scale-90 peer-checked:text-primary">
<i data-feather="help-circle" class="w-5 h-5 "></i>
</div>
</label>
</label>
<!-- HELP DESCRIPTION -->
<p v-if="item.isHelp" class="text-sm font-normal text-gray-700 dark:text-gray-400 mb-2">
{{ item.help }}
</p>
<select v-model="item.value"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option v-for="op in item.options" :value="op" :selected="item.value === op">{{
op
}}
</option>
</select>
</div>
</div>
<div v-if="item.type == 'btn'">
<button class="" onclick="btn_clicked(item)"> {{ item.name }} </button>
</div>
<div v-if="item.type == 'text'">
<div v-if="!item.options">
<label
class="mb-2 relative flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white select-none"
:class="item.help ? 'cursor-pointer ' : ''">
<!-- TITLE -->
<div class="text-base font-semibold">
{{ item.name }}:
</div>
<!-- HELP BUTTON -->
<label v-if="item.help" class="relative inline-flex">
<input type="checkbox" v-model="item.isHelp" class="sr-only peer">
<div
class="hover:text-secondary duration-75 active:scale-90 peer-checked:text-primary">
<i data-feather="help-circle" class="w-5 h-5 "></i>
</div>
</label>
</label>
<!-- HELP DESCRIPTION -->
<p v-if="item.isHelp" class="text-sm font-normal text-gray-700 dark:text-gray-400 mb-2">
{{ item.help }}
</p>
<textarea v-model="item.value"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Enter string"></textarea>
</div>
<div v-if="item.options">
<label
class="mb-2 relative flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white select-none"
:class="item.help ? 'cursor-pointer ' : ''">
<!-- TITLE -->
<div class="text-base font-semibold">
{{ item.name }}:
</div>
<!-- HELP BUTTON -->
<label v-if="item.help" class="relative inline-flex">
<input type="checkbox" v-model="item.isHelp" class="sr-only peer">
<div
class="hover:text-secondary duration-75 active:scale-90 peer-checked:text-primary">
<i data-feather="help-circle" class="w-5 h-5 "></i>
</div>
</label>
</label>
<!-- HELP DESCRIPTION -->
<p v-if="item.isHelp" class="text-sm font-normal text-gray-700 dark:text-gray-400 mb-2">
{{ item.help }}
</p>
<select v-model="item.value"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option v-for="op in item.options" :value="op" :selected="item.value === op">{{
op
}}
</option>
</select>
</div>
</div>
<div v-if="item.type == 'int'">
<label
class="mb-2 relative flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white select-none"
:class="item.help ? 'cursor-pointer ' : ''">
@ -45,8 +173,96 @@
<!-- HELP BUTTON -->
<label v-if="item.help" class="relative inline-flex">
<input type="checkbox" v-model="item.isHelp" class="sr-only peer">
<div
class="hover:text-secondary duration-75 active:scale-90 peer-checked:text-primary">
<div class="hover:text-secondary duration-75 active:scale-90 peer-checked:text-primary">
<i data-feather="help-circle" class="w-5 h-5 "></i>
</div>
</label>
</label>
<!-- HELP DESCRIPTION -->
<p v-if="item.isHelp" class="text-sm font-normal text-gray-700 dark:text-gray-400 mb-2">
{{ item.help }}
</p>
<input type="number" v-model="item.value" step="1"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Enter number">
<input v-if="(item.min != null && item.max != null)" type="range" v-model="item.value"
:min="item.min" :max="item.max" step="1"
class="flex-none h-2 w-full bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 focus:ring-blue-500 focus:border-blue-500 dark:border-gray-600 dark:placeholder-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500">
</div>
<div v-if="item.type == 'float'">
<label
class="mb-2 relative flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white select-none"
:class="item.help ? 'cursor-pointer ' : ''">
<!-- TITLE -->
<div class="text-base font-semibold">
{{ item.name }}:
</div>
<!-- HELP BUTTON -->
<label v-if="item.help" class="relative inline-flex">
<input type="checkbox" v-model="item.isHelp" class="sr-only peer">
<div class="hover:text-secondary duration-75 active:scale-90 peer-checked:text-primary">
<i data-feather="help-circle" class="w-5 h-5 "></i>
</div>
</label>
</label>
<!-- HELP DESCRIPTION -->
<p v-if="item.isHelp" class="text-sm font-normal text-gray-700 dark:text-gray-400 mb-2">
{{ item.help }}
</p>
<input type="number" v-model="item.value"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Enter number">
<input v-if="(item.min != null && item.max != null)" type="range" v-model="item.value"
:min="item.min" :max="item.max" step="0.1"
class="flex-none h-2 w-full bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 focus:ring-blue-500 focus:border-blue-500 dark:border-gray-600 dark:placeholder-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500">
</div>
<div v-if="item.type == 'bool'">
<div class="mb-2 relative flex items-center gap-2">
<label for="default-checkbox" class="text-base font-semibold">
{{ item.name }}:
</label>
<input type="checkbox" v-model="item.value"
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
<!-- HELP BUTTON -->
<label v-if="item.help" class="relative inline-flex">
<input type="checkbox" v-model="item.isHelp" class="sr-only peer">
<div class="hover:text-secondary duration-75 active:scale-90 peer-checked:text-primary">
<i data-feather="help-circle" class="w-5 h-5 "></i>
</div>
</label>
</div>
<!-- HELP DESCRIPTION -->
<p v-if="item.isHelp" class="text-sm font-normal text-gray-700 dark:text-gray-400 mb-2">
{{ item.help }}
</p>
</div>
<div v-if="item.type == 'list'">
<label
class="mb-2 relative flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white select-none"
:class="item.help ? 'cursor-pointer ' : ''">
<!-- TITLE -->
<div class="text-base font-semibold">
{{ item.name }}:
</div>
<!-- HELP BUTTON -->
<label v-if="item.help" class="relative inline-flex">
<input type="checkbox" v-model="item.isHelp" class="sr-only peer">
<div class="hover:text-secondary duration-75 active:scale-90 peer-checked:text-primary">
<i data-feather="help-circle" class="w-5 h-5 "></i>
</div>
</label>
@ -59,260 +275,58 @@
<input type="text" v-model="item.value"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Enter string">
placeholder="Enter comma separated values">
</div>
<div v-if="item.options">
<label
class="mb-2 relative flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white select-none"
:class="item.help ? 'cursor-pointer ' : ''">
<!-- TITLE -->
<div class="text-base font-semibold">
{{ item.name }}:
</div>
<!-- HELP BUTTON -->
<!-- New File/Folder Input Type -->
<div v-if="item.type === 'file' || item.type === 'folder'" class="space-y-2">
<label class="flex items-center gap-2">
<span class="text-base font-semibold">{{ item.name }}:</span>
<!-- Help Button -->
<label v-if="item.help" class="relative inline-flex">
<input type="checkbox" v-model="item.isHelp" class="sr-only peer">
<div
class="hover:text-secondary duration-75 active:scale-90 peer-checked:text-primary">
<i data-feather="help-circle" class="w-5 h-5 "></i>
<div class="hover:text-secondary duration-75 active:scale-90 peer-checked:text-primary">
<i data-feather="help-circle" class="w-5 h-5"></i>
</div>
</label>
</label>
<!-- HELP DESCRIPTION -->
<p v-if="item.isHelp" class="text-sm font-normal text-gray-700 dark:text-gray-400 mb-2">
<!-- Help Text -->
<p v-if="item.isHelp" class="text-sm text-gray-600 dark:text-gray-400">
{{ item.help }}
</p>
<select v-model="item.value"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option v-for="op in item.options" :value="op" :selected="item.value === op">{{
op
}}
</option>
</select>
</div>
</div>
<div v-if="item.type == 'btn'">
<button class="" onclick="btn_clicked(item)"> {{ item.name }} </button>
</div>
<div v-if="item.type == 'text'">
<div v-if="!item.options">
<label
class="mb-2 relative flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white select-none"
:class="item.help ? 'cursor-pointer ' : ''">
<!-- TITLE -->
<div class="text-base font-semibold">
{{ item.name }}:
</div>
<!-- HELP BUTTON -->
<label v-if="item.help" class="relative inline-flex">
<input type="checkbox" v-model="item.isHelp" class="sr-only peer">
<div
class="hover:text-secondary duration-75 active:scale-90 peer-checked:text-primary">
<i data-feather="help-circle" class="w-5 h-5 "></i>
</div>
</label>
</label>
<!-- HELP DESCRIPTION -->
<p v-if="item.isHelp" class="text-sm font-normal text-gray-700 dark:text-gray-400 mb-2">
{{ item.help }}
</p>
<textarea v-model="item.value"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Enter string"></textarea>
</div>
<div v-if="item.options">
<label
class="mb-2 relative flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white select-none"
:class="item.help ? 'cursor-pointer ' : ''">
<!-- TITLE -->
<div class="text-base font-semibold">
{{ item.name }}:
</div>
<!-- HELP BUTTON -->
<label v-if="item.help" class="relative inline-flex">
<input type="checkbox" v-model="item.isHelp" class="sr-only peer">
<div
class="hover:text-secondary duration-75 active:scale-90 peer-checked:text-primary">
<i data-feather="help-circle" class="w-5 h-5 "></i>
</div>
</label>
</label>
<!-- HELP DESCRIPTION -->
<p v-if="item.isHelp" class="text-sm font-normal text-gray-700 dark:text-gray-400 mb-2">
{{ item.help }}
</p>
<select v-model="item.value"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option v-for="op in item.options" :value="op" :selected="item.value === op">{{
op
}}
</option>
</select>
</div>
</div>
<div v-if="item.type == 'int'">
<label
class="mb-2 relative flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white select-none"
:class="item.help ? 'cursor-pointer ' : ''">
<!-- TITLE -->
<div class="text-base font-semibold">
{{ item.name }}:
<!-- File/Folder Selection Input -->
<div class="flex gap-2">
<input type="text" v-model="item.value" readonly
class="flex-1 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:text-white"
:placeholder="item.type === 'file' ? 'Select file...' : 'Select folder...'">
<button @click="openFileDialog(item)"
class="px-3 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-300 rounded-lg hover:bg-gray-100 dark:bg-gray-700 dark:text-white dark:border-gray-600 dark:hover:bg-gray-600">
...
</button>
</div>
<!-- HELP BUTTON -->
<label v-if="item.help" class="relative inline-flex">
<input type="checkbox" v-model="item.isHelp" class="sr-only peer">
<div class="hover:text-secondary duration-75 active:scale-90 peer-checked:text-primary">
<i data-feather="help-circle" class="w-5 h-5 "></i>
</div>
</label>
</label>
<!-- HELP DESCRIPTION -->
<p v-if="item.isHelp" class="text-sm font-normal text-gray-700 dark:text-gray-400 mb-2">
{{ item.help }}
</p>
<input type="number" v-model="item.value" step="1"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Enter number">
<input v-if="(item.min != null && item.max != null)" type="range" v-model="item.value"
:min="item.min" :max="item.max" step="1"
class="flex-none h-2 w-full bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 focus:ring-blue-500 focus:border-blue-500 dark:border-gray-600 dark:placeholder-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500">
</div>
<div v-if="item.type == 'float'">
<label
class="mb-2 relative flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white select-none"
:class="item.help ? 'cursor-pointer ' : ''">
<!-- TITLE -->
<div class="text-base font-semibold">
{{ item.name }}:
</div>
<!-- HELP BUTTON -->
<label v-if="item.help" class="relative inline-flex">
<input type="checkbox" v-model="item.isHelp" class="sr-only peer">
<div class="hover:text-secondary duration-75 active:scale-90 peer-checked:text-primary">
<i data-feather="help-circle" class="w-5 h-5 "></i>
</div>
</label>
</label>
<!-- HELP DESCRIPTION -->
<p v-if="item.isHelp" class="text-sm font-normal text-gray-700 dark:text-gray-400 mb-2">
{{ item.help }}
</p>
<input type="number" v-model="item.value"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Enter number">
<input v-if="(item.min != null && item.max != null)" type="range" v-model="item.value"
:min="item.min" :max="item.max" step="0.1"
class="flex-none h-2 w-full bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700 focus:ring-blue-500 focus:border-blue-500 dark:border-gray-600 dark:placeholder-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500">
</div>
<div v-if="item.type == 'bool'">
<div class="mb-2 relative flex items-center gap-2">
<label for="default-checkbox" class="text-base font-semibold">
{{ item.name }}:
</label>
<input type="checkbox" v-model="item.value"
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
<!-- HELP BUTTON -->
<label v-if="item.help" class="relative inline-flex">
<input type="checkbox" v-model="item.isHelp" class="sr-only peer">
<div class="hover:text-secondary duration-75 active:scale-90 peer-checked:text-primary">
<i data-feather="help-circle" class="w-5 h-5 "></i>
</div>
</label>
</div>
<!-- HELP DESCRIPTION -->
<p v-if="item.isHelp" class="text-sm font-normal text-gray-700 dark:text-gray-400 mb-2">
{{ item.help }}
</p>
<hr v-if="index < controls_array.length - 1"
class="h-px my-4 bg-gray-200 border-0 dark:bg-gray-700">
</div>
<div v-if="item.type == 'list'">
<label
class="mb-2 relative flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white select-none"
:class="item.help ? 'cursor-pointer ' : ''">
<!-- TITLE -->
<div class="text-base font-semibold">
{{ item.name }}:
</div>
<!-- HELP BUTTON -->
<label v-if="item.help" class="relative inline-flex">
<input type="checkbox" v-model="item.isHelp" class="sr-only peer">
<div class="hover:text-secondary duration-75 active:scale-90 peer-checked:text-primary">
<i data-feather="help-circle" class="w-5 h-5 "></i>
</div>
</label>
</label>
<!-- HELP DESCRIPTION -->
<p v-if="item.isHelp" class="text-sm font-normal text-gray-700 dark:text-gray-400 mb-2">
{{ item.help }}
</p>
<input type="text" v-model="item.value"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Enter comma separated values">
</div>
<hr class="h-px my-4 bg-gray-200 border-0 dark:bg-gray-700">
</div>
<!-- SUBMIT AND CANCEL BUTTONS -->
<div class="flex flex-row flex-grow gap-3">
<div class="p-2 text-center grow">
<button @click.stop="hide(true)" type="button"
class="mr-2 text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
{{ ConfirmButtonText }}
</button>
<button @click.stop="hide(false)" type="button"
class="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-gray-200 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-11 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">
{{ DenyButtonText }}
</button>
</div>
</div>
</div>
<!-- Footer with Buttons -->
<div class="flex justify-center gap-3 p-4 border-t border-gray-200 dark:border-gray-700">
<button @click.stop="hide(true)"
class="px-5 py-2.5 text-sm font-medium text-white bg-blue-700 rounded-lg hover:bg-blue-800 dark:bg-blue-600 dark:hover:bg-blue-700">
{{ ConfirmButtonText }}
</button>
<button @click.stop="hide(false)"
class="px-5 py-2.5 text-sm font-medium text-gray-500 bg-white rounded-lg border border-gray-200 hover:bg-gray-100 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:bg-gray-600">
{{ DenyButtonText }}
</button>
</div>
</div>
</div>
</div>
</template>
<script >
import feather from 'feather-icons'
import { nextTick, TransitionGroup } from 'vue'
@ -374,6 +388,30 @@ export default {
console.log('show form', this.controls_array)
});
},
openFileDialog(item) {
// Create input element
const input = document.createElement('input');
input.type = 'file';
if (item.type === 'folder') {
input.webkitdirectory = true;
input.directory = true;
}
// Set accepted file types if specified
if (item.accept) {
input.accept = item.accept;
}
// Handle file selection
input.onchange = (e) => {
if (e.target.files.length > 0) {
item.value = e.target.files[0].path;
}
};
// Trigger file dialog
input.click();
}
},
watch: {
@ -399,4 +437,26 @@ export default {
}
}
</script>
<style scoped>
.custom-scrollbar {
scrollbar-width: thin;
scrollbar-color: rgba(156, 163, 175, 0.5) transparent;
}
.custom-scrollbar::-webkit-scrollbar {
width: 6px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: transparent;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background-color: rgba(156, 163, 175, 0.5);
border-radius: 3px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background-color: rgba(156, 163, 175, 0.7);
}
</style>

View File

@ -23,6 +23,7 @@ function copyObject(obj) {
export const store = createStore({
state () {
return {
personalities_ready: false,
is_rt_on:false,
language: "english",
languages: [],
@ -102,6 +103,9 @@ export const store = createStore({
localStorage.setItem('lollms_webui_view_mode', mode); // Sync with localStorage
},
setpersonalitiesReady(state, personalities_ready) {
state.personalities_ready = personalities_ready;
},
setisRTOn(state, is_rt_on) {
state.is_rt_on = is_rt_on;
},
@ -189,6 +193,9 @@ export const store = createStore({
return state.view_mode;
},
getpersonalitiesReady(state){
return state.personalities_ready;
},
getisRTOn(state) {
return state.is_rt_on;
},

File diff suppressed because it is too large Load Diff