mirror of
https://github.com/ParisNeo/lollms-webui.git
synced 2025-03-24 12:55:15 +00:00
Merge branch 'main' of https://github.com/ParisNeo/lollms-webui into main
This commit is contained in:
commit
79381b63df
@ -38,6 +38,15 @@ from datetime import datetime
|
||||
from typing import List, Tuple
|
||||
import time
|
||||
|
||||
from lollms.utilities import find_first_available_file_index
|
||||
|
||||
if not PackageManager.check_package_installed("requests"):
|
||||
PackageManager.install_package("requests")
|
||||
if not PackageManager.check_package_installed("bs4"):
|
||||
PackageManager.install_package("beautifulsoup4")
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
def terminate_thread(thread):
|
||||
if thread:
|
||||
if not thread.is_alive():
|
||||
@ -223,6 +232,26 @@ class LoLLMsAPI(LollmsApplication):
|
||||
|
||||
ASCIIColors.error(f'Client {request.sid} disconnected')
|
||||
|
||||
@socketio.on('add_webpage')
|
||||
def add_webpage(data):
|
||||
ASCIIColors.yellow("Scaping web page")
|
||||
url = data['url']
|
||||
index = find_first_available_file_index(self.lollms_paths.personal_uploads_path,"web_",".txt")
|
||||
file_path=self.lollms_paths.personal_uploads_path/f"web_{index}.txt"
|
||||
self.scrape_and_save(url=url, file_path=file_path)
|
||||
try:
|
||||
if not self.personality.processor is None:
|
||||
self.personality.processor.add_file(file_path, partial(self.process_chunk, client_id = request.sid))
|
||||
# File saved successfully
|
||||
socketio.emit('web_page_added', {'status':True,})
|
||||
else:
|
||||
self.personality.add_file(file_path, partial(self.process_chunk, client_id = request.sid))
|
||||
# File saved successfully
|
||||
socketio.emit('web_page_added', {'status':True})
|
||||
except Exception as e:
|
||||
# Error occurred while saving the file
|
||||
socketio.emit('web_page_added', {'status':False})
|
||||
|
||||
@socketio.on('take_picture')
|
||||
def take_picture():
|
||||
try:
|
||||
@ -263,18 +292,22 @@ class LoLLMsAPI(LollmsApplication):
|
||||
|
||||
@socketio.on('start_webcam_video_stream')
|
||||
def start_webcam_video_stream():
|
||||
self.info("Starting video capture")
|
||||
self.webcam.start_capture()
|
||||
|
||||
@socketio.on('stop_webcam_video_stream')
|
||||
def stop_webcam_video_stream():
|
||||
self.info("Stopping video capture")
|
||||
self.webcam.stop_capture()
|
||||
|
||||
@socketio.on('start_audio_stream')
|
||||
def start_audio_stream():
|
||||
self.info("Starting audio capture")
|
||||
self.audio_cap.start_recording()
|
||||
|
||||
@socketio.on('stop_audio_stream')
|
||||
def stop_audio_stream():
|
||||
self.info("Stopping audio capture")
|
||||
self.audio_cap.stop_recording()
|
||||
|
||||
|
||||
@ -1097,6 +1130,25 @@ class LoLLMsAPI(LollmsApplication):
|
||||
else:
|
||||
if output["text"].lower()=="lollms":
|
||||
self.summoned = True
|
||||
def scrape_and_save(self, url, file_path):
|
||||
# Send a GET request to the URL
|
||||
response = requests.get(url)
|
||||
|
||||
# Parse the HTML content using BeautifulSoup
|
||||
soup = BeautifulSoup(response.content, 'html.parser')
|
||||
|
||||
# Find all the text content in the webpage
|
||||
text_content = soup.get_text()
|
||||
|
||||
# Remove extra returns and spaces
|
||||
text_content = ' '.join(text_content.split())
|
||||
|
||||
# Save the text content as a text file
|
||||
with open(file_path, 'w', encoding="utf-8") as file:
|
||||
file.write(text_content)
|
||||
|
||||
self.info(f"Webpage content saved to {file_path}")
|
||||
|
||||
def rebuild_personalities(self, reload_all=False):
|
||||
if reload_all:
|
||||
self.mounted_personalities=[]
|
||||
@ -1960,16 +2012,23 @@ class LoLLMsAPI(LollmsApplication):
|
||||
self.prepare_reception(client_id)
|
||||
self.generating = True
|
||||
self.connections[client_id]["processing"]=True
|
||||
self.generate(
|
||||
self.discussion_messages,
|
||||
self.current_message,
|
||||
n_predict = self.config.ctx_size-len(tokens)-1,
|
||||
client_id=client_id,
|
||||
callback=partial(self.process_chunk,client_id = client_id)
|
||||
)
|
||||
print()
|
||||
print("## Done Generation ##")
|
||||
print()
|
||||
try:
|
||||
self.generate(
|
||||
self.discussion_messages,
|
||||
self.current_message,
|
||||
n_predict = self.config.ctx_size-len(tokens)-1,
|
||||
client_id=client_id,
|
||||
callback=partial(self.process_chunk,client_id = client_id)
|
||||
)
|
||||
print()
|
||||
ASCIIColors.success("## Done Generation ##")
|
||||
print()
|
||||
except Exception as ex:
|
||||
trace_exception(ex)
|
||||
print()
|
||||
ASCIIColors.error("## Generation Error ##")
|
||||
print()
|
||||
|
||||
self.cancel_gen = False
|
||||
|
||||
# Send final message
|
||||
|
110
app.py
110
app.py
@ -13,7 +13,7 @@ __github__ = "https://github.com/ParisNeo/lollms-webui"
|
||||
__copyright__ = "Copyright 2023, "
|
||||
__license__ = "Apache 2.0"
|
||||
|
||||
__version__ ="8.0 (Beta)"
|
||||
__version__ ="8.0"
|
||||
|
||||
main_repo = "https://github.com/ParisNeo/lollms-webui.git"
|
||||
import os
|
||||
@ -511,6 +511,39 @@ try:
|
||||
self.add_endpoint(
|
||||
"/open_file", "open_file", self.open_file, methods=["GET"]
|
||||
)
|
||||
|
||||
self.add_endpoint(
|
||||
"/update_binding_settings", "update_binding_settings", self.update_binding_settings, methods=["GET"]
|
||||
)
|
||||
|
||||
|
||||
def update_binding_settings(self):
|
||||
if self.binding:
|
||||
self.binding.settings_updated()
|
||||
ASCIIColors.green("Binding setting updated successfully")
|
||||
return jsonify({"status":True})
|
||||
else:
|
||||
return jsonify({"status":False, 'error':"no binding found"})
|
||||
|
||||
def reload_binding(self, data):
|
||||
print(f"Roloading binding selected : {data['binding_name']}")
|
||||
self.config["binding_name"]=data['binding_name']
|
||||
try:
|
||||
if self.binding:
|
||||
self.binding.destroy_model()
|
||||
self.binding = None
|
||||
self.model = None
|
||||
for per in self.mounted_personalities:
|
||||
per.model = None
|
||||
gc.collect()
|
||||
self.binding = BindingBuilder().build_binding(self.config, self.lollms_paths, InstallOption.INSTALL_IF_NECESSARY, lollmsCom=self)
|
||||
self.model = None
|
||||
self.config.save_config()
|
||||
ASCIIColors.green("Binding loaded successfully")
|
||||
except Exception as ex:
|
||||
ASCIIColors.error(f"Couldn't build binding: [{ex}]")
|
||||
trace_exception(ex)
|
||||
return jsonify({"status":False, 'error':str(ex)})
|
||||
|
||||
def get_model_status(self):
|
||||
return jsonify({"status":self.model is not None})
|
||||
@ -1011,7 +1044,8 @@ try:
|
||||
try:
|
||||
self.model = None
|
||||
for per in self.mounted_personalities:
|
||||
per.model = None
|
||||
if per is not None:
|
||||
per.model = None
|
||||
self.model = self.binding.build_model()
|
||||
if self.model is not None:
|
||||
ASCIIColors.yellow("New model OK")
|
||||
@ -1638,7 +1672,6 @@ try:
|
||||
self.config.binding_name = data['name']
|
||||
self.binding = BindingBuilder().build_binding(self.config, self.lollms_paths, InstallOption.FORCE_INSTALL, lollmsCom=self)
|
||||
self.success("Binding installed successfully")
|
||||
self.InfoMessage("Please reboot the application so that the binding installation can be taken into consideration")
|
||||
del self.binding
|
||||
self.binding = None
|
||||
self.config.binding_name = old_bn
|
||||
@ -1649,7 +1682,7 @@ try:
|
||||
per.model = self.model
|
||||
return jsonify({"status": True})
|
||||
except Exception as ex:
|
||||
ASCIIColors.error(f"Couldn't build binding: [{ex}]")
|
||||
self.error(f"Couldn't build binding: [{ex}]")
|
||||
trace_exception(ex)
|
||||
return jsonify({"status":False, 'error':str(ex)})
|
||||
|
||||
@ -1671,12 +1704,12 @@ try:
|
||||
self.config.binding_name = data['name']
|
||||
self.binding = BindingBuilder().build_binding(self.config, self.lollms_paths, InstallOption.FORCE_INSTALL, lollmsCom=self)
|
||||
self.success("Binding reinstalled successfully")
|
||||
self.InfoMessage("Please reboot the application so that the binding installation can be taken into consideration")
|
||||
self.config.binding_name = old_bn
|
||||
self.binding = BindingBuilder().build_binding(self.config, self.lollms_paths, lollmsCom=self)
|
||||
self.model = self.binding.build_model()
|
||||
for per in self.mounted_personalities:
|
||||
per.model = self.model
|
||||
|
||||
return jsonify({"status": True})
|
||||
except Exception as ex:
|
||||
ASCIIColors.error(f"Couldn't build binding: [{ex}]")
|
||||
@ -1764,16 +1797,9 @@ try:
|
||||
return jsonify({'update_availability':False})
|
||||
|
||||
def restart_program(self):
|
||||
ASCIIColors.info("")
|
||||
ASCIIColors.info("")
|
||||
ASCIIColors.info("")
|
||||
ASCIIColors.info(" ╔══════════════════════════════════════════════════╗")
|
||||
ASCIIColors.info(" ║ Restarting backend ║")
|
||||
ASCIIColors.info(" ╚══════════════════════════════════════════════════╝")
|
||||
ASCIIColors.info("")
|
||||
ASCIIColors.info("")
|
||||
ASCIIColors.info("")
|
||||
run_restart_script(self.args)
|
||||
socketio.reboot=True
|
||||
self.socketio.stop()
|
||||
self.socketio.sleep(1)
|
||||
|
||||
|
||||
def update_software(self):
|
||||
@ -1786,6 +1812,7 @@ try:
|
||||
ASCIIColors.info("")
|
||||
ASCIIColors.info("")
|
||||
ASCIIColors.info("")
|
||||
self.socketio.stop()
|
||||
run_update_script(self.args)
|
||||
sys.exit()
|
||||
|
||||
@ -1831,46 +1858,6 @@ try:
|
||||
version = __version__
|
||||
ASCIIColors.yellow("Lollms webui version : "+ version)
|
||||
return jsonify({"version":version})
|
||||
|
||||
|
||||
def reload_binding(self):
|
||||
try:
|
||||
data = request.get_json()
|
||||
# Further processing of the data
|
||||
except Exception as e:
|
||||
print(f"Error occurred while parsing JSON: {e}")
|
||||
return jsonify({"status":False, 'error':str(e)})
|
||||
ASCIIColors.info(f"- Reloading binding {data['name']}...")
|
||||
try:
|
||||
ASCIIColors.info("Unmounting binding and model")
|
||||
self.binding = None
|
||||
self.model = None
|
||||
for personality in self.mounted_personalities:
|
||||
personality.model = None
|
||||
gc.collect()
|
||||
ASCIIColors.info("Reloading binding")
|
||||
self.binding = BindingBuilder().build_binding(self.config, self.lollms_paths, lollmsCom=self)
|
||||
ASCIIColors.info("Binding loaded successfully")
|
||||
|
||||
try:
|
||||
ASCIIColors.info("Reloading model")
|
||||
self.model = self.binding.build_model()
|
||||
ASCIIColors.info("Model reloaded successfully")
|
||||
except Exception as ex:
|
||||
print(f"Couldn't build model: [{ex}]")
|
||||
trace_exception(ex)
|
||||
try:
|
||||
self.rebuild_personalities(reload_all=True)
|
||||
except Exception as ex:
|
||||
print(f"Couldn't reload personalities: [{ex}]")
|
||||
return jsonify({"status": True})
|
||||
except Exception as ex:
|
||||
ASCIIColors.error(f"Couldn't build binding: [{ex}]")
|
||||
trace_exception(ex)
|
||||
return jsonify({"status":False, 'error':str(ex)})
|
||||
|
||||
|
||||
|
||||
|
||||
def p_mount_personality(self):
|
||||
print("- Mounting personality")
|
||||
@ -2637,9 +2624,22 @@ try:
|
||||
|
||||
|
||||
try:
|
||||
socketio.reboot = False
|
||||
socketio.run(app, host=config["host"], port=config["port"],
|
||||
# prevent error: The Werkzeug web server is not designed to run in production
|
||||
allow_unsafe_werkzeug=True)
|
||||
if socketio.reboot:
|
||||
ASCIIColors.info("")
|
||||
ASCIIColors.info("")
|
||||
ASCIIColors.info("")
|
||||
ASCIIColors.info(" ╔══════════════════════════════════════════════════╗")
|
||||
ASCIIColors.info(" ║ Restarting backend ║")
|
||||
ASCIIColors.info(" ╚══════════════════════════════════════════════════╝")
|
||||
ASCIIColors.info("")
|
||||
ASCIIColors.info("")
|
||||
ASCIIColors.info("")
|
||||
run_restart_script(args)
|
||||
|
||||
except Exception as ex:
|
||||
trace_exception(ex)
|
||||
# http_server = WSGIServer((config["host"], config["port"]), app, handler_class=WebSocketHandler)
|
||||
|
16
docs/youtube/lollms_contextual_summery.md
Normal file
16
docs/youtube/lollms_contextual_summery.md
Normal file
@ -0,0 +1,16 @@
|
||||
Hi there, this is a short to show you the potential of the hierarchical contextual summary of documents using lollms.
|
||||
|
||||
To do so, we first go to settings page. Under the personalities section, select the category data, and mount the docs_zipper personality.
|
||||
|
||||
Now, we go to the personality settings and we set some specific summery parameters. Here we say keep the method description, we select keep document title and authors in the summary. We set the summary size in tokens and we validate.
|
||||
|
||||
Now we add the document to summerize and we go to the personality menu and we select start
|
||||
|
||||
The document will be decomposed into a certain number of chunks, then each chunk is contextually summerized. After that the summeries are tied together then the operation is repeated until the compressed text is smaller than the maximum number of tokens set in the configuration.
|
||||
|
||||
The contextual nature of this algorithm will allow you to have better control over the summary. For example, here we asked for keeping the title, the author names and the results of the paper as well as the method.
|
||||
|
||||
As you can see, the summary has respected all the constraints that we did set. We can find the title, the authors names, the method and the numerical results.
|
||||
|
||||
Don't forget to like and subscribe
|
||||
Thanks for watching
|
39
docs/youtube/lollms_v8.0_install_gpu.md
Normal file
39
docs/youtube/lollms_v8.0_install_gpu.md
Normal file
@ -0,0 +1,39 @@
|
||||
Hi there,
|
||||
|
||||
In this short I'm going to show you how to install lollms in GPU mode and use the hugging face binding to run inference.
|
||||
|
||||
The steps to aceive this are simple:
|
||||
download the installer from the github of the project to an empty folder with a path that does not contain spaces.
|
||||
double click the installer and validate.
|
||||
select the install mode (Cuda, rocm or cpu)
|
||||
wait
|
||||
Run the app
|
||||
Choose the personal data folder
|
||||
Install a binding then Reboot the app
|
||||
Select the binding
|
||||
Install a model
|
||||
Select a model
|
||||
|
||||
And now you are ready to rock
|
||||
|
||||
Let me show you how I do it:
|
||||
|
||||
Start by opening the lollms web user interface link that you can find in the description. Then go to the latest release page and download the installer suited for your platform. Make sure you put it into a path with no spaces and inside an empty folder as it will fill out the folder with the install files.
|
||||
|
||||
Now execute the installer. As it says, make sure the path you are using is not very long and without spaces as conda may have bugs with paths that contains spaces.
|
||||
|
||||
Now press enter to continue.
|
||||
|
||||
Select the right configuration. if you have an nvisia gpu, select option a. if you have a Rocm compatible GPU like AMD GPUs press B. If you have no GPU or want to use remote generation, then select C.
|
||||
|
||||
Now just wait
|
||||
|
||||
Once the installation is done, close the installation script and go back to the install folder.
|
||||
run the win_run.bat file and wait for the program to run for the first time.
|
||||
Some additional libraries may need to be installed the first time you run it.
|
||||
|
||||
|
||||
The first time you run the app, you will need to specify a folder where to put your personal data. Make sure the disk containing this folder is big enough as it will hold your models as well as the personas outputs and your discussion databases. The default folder is a folder called lollms under your documents folder. Just press enter if it is ok or type another path then press enter.
|
||||
|
||||
Now lollms is installed, but there is no binding installed nor a model. To do this, go to settings tab, select binding s zoo and install a binding depending on your preferences. Here I am using a GPU and want local generation, so I will select Hugging face to use full sized models, GPTQ quantized models or AWQ quantized models. The installation may take some while, you can look at the install details in the console window.
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 691af56f46f4318f21975dff4aea5c1922ad2eb7
|
||||
Subproject commit 9acdc3e9cd7880451b57e290d08c2957347766bb
|
@ -1,18 +1,17 @@
|
||||
tqdm
|
||||
psutil
|
||||
setuptools
|
||||
pyyaml
|
||||
numpy
|
||||
flask
|
||||
flask_socketio
|
||||
pytest
|
||||
pyyaml
|
||||
markdown
|
||||
gevent
|
||||
gevent-websocket
|
||||
langchain
|
||||
requests
|
||||
eventlet
|
||||
websocket-client
|
||||
GitPython
|
||||
setuptools
|
||||
numpy
|
||||
flask_compress
|
||||
gevent-websocket
|
||||
websocket-client
|
||||
eventlet
|
||||
tqdm
|
||||
psutil
|
||||
pytest
|
||||
gevent
|
||||
requests
|
||||
GitPython
|
||||
ascii_colors>=0.1.4
|
||||
beautifulsoup4
|
@ -7,7 +7,6 @@ pyyaml
|
||||
markdown
|
||||
gevent
|
||||
gevent-websocket
|
||||
langchain
|
||||
requests
|
||||
eventlet
|
||||
websocket-client
|
||||
|
@ -98,13 +98,6 @@ source "$MINICONDA_DIR/bin/activate" || ( echo "Miniconda hook not found." && ex
|
||||
if [ ! -d "$INSTALL_ENV_DIR" ]; then
|
||||
echo "Packages to install: $PACKAGES_TO_INSTALL"
|
||||
conda create -y -k -p "$INSTALL_ENV_DIR" $CHANNEL $PACKAGES_TO_INSTALL || ( echo && echo "Conda environment creation failed." && exit 1 )
|
||||
if [[ "${gpuchoice^^}" == "A" ]]; then
|
||||
conda run --live-stream -p "$INSTALL_ENV_DIR" python -m pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 || ( echo && echo "Pytorch installation failed." && exit 1 )
|
||||
elif [[ "${gpuchoice^^}" == "B" ]]; then
|
||||
conda run --live-stream -p "$INSTALL_ENV_DIR" python -m pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm5.4.2 || ( echo && echo "Pytorch installation failed." && exit 1 )
|
||||
elif [[ "${gpuchoice^^}" == "C" ]]; then
|
||||
conda run --live-stream -p "$INSTALL_ENV_DIR" python -m pip install torch torchvision torchaudio || ( echo && echo "Pytorch installation failed." && exit 1 )
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if conda environment was actually created
|
||||
|
@ -95,12 +95,6 @@ if [ ! -d "$INSTALL_ENV_DIR" ]; then
|
||||
echo "Packages to install: $PACKAGES_TO_INSTALL"
|
||||
conda create -y -k -n "$ENV_NAME" $CHANNEL $PACKAGES_TO_INSTALL || ( echo && echo "Conda environment creation failed." && exit 1 )
|
||||
echo "Conda created and using packages: $PACKAGES_TO_INSTALL"
|
||||
echo "Installing tools"
|
||||
if [[ "$(echo $gpuchoice | tr '[:lower:]' '[:upper:]')" == "A" ]]; then
|
||||
conda run --live-stream -n "$ENV_NAME" python -m pip install torch torchvision torchaudio --channel pytorch --channel conda-forge || ( echo && echo "Pytorch installation failed." && exit 1 )
|
||||
elif [[ "$(echo $gpuchoice | tr '[:lower:]' '[:upper:]')" == "B" ]]; then
|
||||
conda run --live-stream -n "$ENV_NAME" python -m pip install torch torchvision torchaudio --channel pytorch --channel conda-forge || ( echo && echo "Pytorch installation failed." && exit 1 )
|
||||
fi
|
||||
fi
|
||||
|
||||
# Activate installer environment
|
||||
|
@ -107,9 +107,6 @@ if /I "%gpuchoice%" == "B" (
|
||||
if not exist "%INSTALL_ENV_DIR%" (
|
||||
echo Packages to install: %PACKAGES_TO_INSTALL%
|
||||
call conda create --no-shortcuts -y -k -p "%INSTALL_ENV_DIR%" %CHANNEL% %PACKAGES_TO_INSTALL% || ( echo. && echo Conda environment creation failed. && goto end )
|
||||
if /I "%gpuchoice%" == "A" call conda run --live-stream -p "%INSTALL_ENV_DIR%" python -m pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121|| ( echo. && echo Pytorch installation failed.&& goto end )
|
||||
if /I "%gpuchoice%" == "B" call conda run --live-stream -p "%INSTALL_ENV_DIR%" python -m pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm5.4.2|| ( echo. && echo Pytorch installation failed.&& goto end )
|
||||
if /I "%gpuchoice%" == "C" call conda run --live-stream -p "%INSTALL_ENV_DIR%" python -m pip install torch torchvision torchaudio|| ( echo. && echo Pytorch installation failed.&& goto end )
|
||||
)
|
||||
|
||||
@rem check if conda environment was actually created
|
||||
|
8
web/dist/assets/index-7674c890.css
vendored
Normal file
8
web/dist/assets/index-7674c890.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
8
web/dist/assets/index-f3c97c3f.css
vendored
8
web/dist/assets/index-f3c97c3f.css
vendored
File diff suppressed because one or more lines are too long
BIN
web/dist/disconnected.mp3
vendored
Normal file
BIN
web/dist/disconnected.mp3
vendored
Normal file
Binary file not shown.
4
web/dist/index.html
vendored
4
web/dist/index.html
vendored
@ -6,8 +6,8 @@
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>LoLLMS WebUI - Welcome</title>
|
||||
<script type="module" crossorigin src="/assets/index-715d9baa.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index-f3c97c3f.css">
|
||||
<script type="module" crossorigin src="/assets/index-e963577c.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index-7674c890.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
BIN
web/dist/rebooting.mp3
vendored
Normal file
BIN
web/dist/rebooting.mp3
vendored
Normal file
Binary file not shown.
BIN
web/public/disconnected.mp3
Normal file
BIN
web/public/disconnected.mp3
Normal file
Binary file not shown.
BIN
web/public/rebooting.mp3
Normal file
BIN
web/public/rebooting.mp3
Normal file
Binary file not shown.
@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="flex flex-col h-screen font-sans bg-bg-light text-slate-950 dark:bg-bg-dark dark:text-slate-50 overflow-y-scroll w-full dark:bg-bg-dark 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">
|
||||
<div class="flex flex-col h-screen font-sans bg-bg-light text-slate-950 dark:bg-bg-dark dark:text-slate-50 w-full dark:bg-bg-dark overflow-hidden">
|
||||
<TopBar />
|
||||
|
||||
<div class="flex overflow-hidden flex-grow ">
|
||||
<div class="flex overflow-hidden flex-grow w-full">
|
||||
<!-- VIEW CONTAINER -->
|
||||
<RouterView v-slot="{ Component }">
|
||||
<KeepAlive>
|
||||
|
@ -1,63 +1,124 @@
|
||||
<template>
|
||||
<div class="floating-frame">
|
||||
<img v-if="isAudioActive" :src="imageDataUrl" alt="Spectrogram" width="300" height="300" />
|
||||
<div class="floating-frame bg-white" :style="{ bottom: position.bottom + 'px', right: position.right + 'px', 'z-index': zIndex } " @mousedown.stop="startDrag" @mouseup.stop="stopDrag">
|
||||
<div class="handle" @mousedown.stop="startDrag" @mouseup.stop="stopDrag">Drag Me</div>
|
||||
<img v-if="isAudioActive && imageDataUrl != null" :src="imageDataUrl" alt="Spectrogram" width="300" height="300" />
|
||||
<div class="controls">
|
||||
<button v-if="!isAudioActive" class="w-full bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded" @click="startAudioStream">Activate Audio</button>
|
||||
<button v-if="isAudioActive" class="w-full bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded" @click="stopAudioStream">Deactivate Audio</button>
|
||||
<button v-if="!isAudioActive" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded" @click="startAudioStream"><i data-feather="mic"></i> </button>
|
||||
<button v-if="isAudioActive" class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded" @click="stopAudioStream"><i data-feather="mic"></i></button>
|
||||
<span v-if="isAudioActive">FPS: {{ frameRate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import socket from '@/services/websocket.js';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isAudioActive: false,
|
||||
imageDataUrl: null
|
||||
};
|
||||
import socket from '@/services/websocket.js';
|
||||
import feather from 'feather-icons'
|
||||
import { nextTick } from 'vue'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isAudioActive: false,
|
||||
imageDataUrl: null,
|
||||
isDragging: false,
|
||||
position: { bottom: 0, right: 0 },
|
||||
dragStart: { x: 0, y: 0 },
|
||||
zIndex: 0, // Add a data property for z-index
|
||||
frameRate: 0,
|
||||
frameCount: 0,
|
||||
lastFrameTime: Date.now(),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
startAudioStream() {
|
||||
this.isAudioActive = true;
|
||||
socket.emit('start_audio_stream');
|
||||
nextTick(() => {
|
||||
feather.replace()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
startAudioStream() {
|
||||
this.isAudioActive = true;
|
||||
socket.emit('start_audio_stream');
|
||||
},
|
||||
stopAudioStream() {
|
||||
this.isAudioActive = false;
|
||||
this.imageData = null;
|
||||
socket.emit('stop_audio_stream');
|
||||
stopAudioStream() {
|
||||
this.isAudioActive = false;
|
||||
this.imageDataUrl = null;
|
||||
socket.emit('stop_audio_stream');
|
||||
nextTick(() => {
|
||||
feather.replace()
|
||||
})
|
||||
},
|
||||
startDrag(event) {
|
||||
this.isDragging = true;
|
||||
this.zIndex = 5001; // Increase z-index when dragging starts
|
||||
this.dragStart.x = event.clientX;
|
||||
this.dragStart.y = event.clientY;
|
||||
document.addEventListener('mousemove', this.drag);
|
||||
document.addEventListener('mouseup', this.stopDrag);
|
||||
},
|
||||
drag(event) {
|
||||
if (this.isDragging) {
|
||||
const deltaX = event.clientX - this.dragStart.x;
|
||||
const deltaY = event.clientY - this.dragStart.y;
|
||||
this.position.bottom -= deltaY;
|
||||
this.position.right -= deltaX;
|
||||
this.dragStart.x = event.clientX;
|
||||
this.dragStart.y = event.clientY;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
socket.on('update_spectrogram', (imageBase64) => {
|
||||
if (this.isAudioActive) {
|
||||
this.imageDataUrl = 'data:image/jpeg;base64,' + imageBase64;
|
||||
}
|
||||
});
|
||||
stopDrag() {
|
||||
this.isDragging = false;
|
||||
this.zIndex = 0; // Reset z-index when dragging stops
|
||||
document.removeEventListener('mousemove', this.drag);
|
||||
document.removeEventListener('mouseup', this.stopDrag);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.floating-frame {
|
||||
margin: 15px;
|
||||
float: left;
|
||||
width: 800px;
|
||||
height: auto;
|
||||
border: 1px solid #000;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
z-index: 5000;
|
||||
},
|
||||
mounted() {
|
||||
feather.replace();
|
||||
socket.on('update_spectrogram', (imageBase64) => {
|
||||
if (this.isAudioActive) {
|
||||
this.imageDataUrl = 'data:image/jpeg;base64,' + imageBase64;
|
||||
this.frameCount++;
|
||||
const now = Date.now();
|
||||
const delta = now - this.lastFrameTime;
|
||||
if (delta >= 1000) { // Calculate FPS every second
|
||||
this.frameRate = this.frameCount;
|
||||
this.frameCount = 0;
|
||||
this.lastFrameTime = now;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
.floating-frame img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.floating-frame {
|
||||
margin: 15px;
|
||||
float: left;
|
||||
height: auto;
|
||||
border: 1px solid #000;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
z-index: 5000;
|
||||
position: fixed;
|
||||
cursor: move;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.handle {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
background: #ccc;
|
||||
cursor: move;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.floating-frame img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
@ -212,30 +212,38 @@
|
||||
|
||||
|
||||
</textarea>
|
||||
<input type="file" ref="fileDialog" style="display: none" @change="addFiles" multiple />
|
||||
<button type="button" @click.stop="$refs.fileDialog.click()" title="Add files"
|
||||
class="absolute inset-y-0 right-0 flex items-center mr-2 w-6 hover:text-secondary duration-75 active:scale-90">
|
||||
|
||||
<i data-feather="file-plus"></i>
|
||||
|
||||
</button>
|
||||
</div>
|
||||
<!-- BUTTONS -->
|
||||
<div class="inline-flex justify-center rounded-full ">
|
||||
<button v-if="!loading"
|
||||
type="button"
|
||||
@click="startSpeechRecognition"
|
||||
title="Press and talk"
|
||||
:class="{ 'text-red-500': isLesteningToVoice }"
|
||||
class="w-6 hover:text-secondary duration-75 active:scale-90 cursor-pointer"
|
||||
>
|
||||
<i data-feather="mic"></i>
|
||||
</button>
|
||||
|
||||
<input type="file" ref="fileDialog" style="display: none" @change="addFiles" multiple />
|
||||
<button type="button" @click.stop="$refs.fileDialog.click()" title="Add files"
|
||||
class="m-1 w-6 hover:text-secondary duration-75 active:scale-90">
|
||||
|
||||
<i data-feather="file-plus"></i>
|
||||
|
||||
</button>
|
||||
<button type="button" @click.stop="takePicture" title="take a shot from camera"
|
||||
class=" w-6 hover:text-secondary duration-75 active:scale-90">
|
||||
class="m-1 w-6 hover:text-secondary duration-75 active:scale-90">
|
||||
|
||||
<i data-feather="camera"></i>
|
||||
|
||||
</button>
|
||||
<button type="button" @click.stop="addWebLink" title="add web link"
|
||||
class="m-1 w-6 hover:text-secondary duration-75 active:scale-90">
|
||||
|
||||
<i data-feather="globe"></i>
|
||||
|
||||
</button>
|
||||
<button v-if="!loading" type="button" @click="makeAnEmptyUserMessage" title="New empty user message"
|
||||
class=" w-6 text-blue-400 hover:text-secondary duration-75 active:scale-90">
|
||||
|
||||
@ -251,7 +259,7 @@
|
||||
|
||||
<span class="sr-only">New empty message</span>
|
||||
</button>
|
||||
<button v-if="!loading" type="button" @click="submit"
|
||||
<button v-if="!loading" type="button" @click="submit" title="Send"
|
||||
class=" w-6 hover:text-secondary duration-75 active:scale-90">
|
||||
|
||||
<i data-feather="send"></i>
|
||||
@ -280,7 +288,6 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<Toast ref="toast"/>
|
||||
<UniversalForm ref="universalForm" class="z-20" />
|
||||
</template>
|
||||
<style scoped>
|
||||
@ -323,14 +330,13 @@ import InteractiveMenu from '@/components/InteractiveMenu.vue';
|
||||
import { useStore } from 'vuex'; // Import the useStore function
|
||||
import { inject } from 'vue';
|
||||
import socket from '@/services/websocket.js'
|
||||
import Toast from '../components/Toast.vue'
|
||||
import UniversalForm from '../components/UniversalForm.vue';
|
||||
import modelImgPlaceholder from "../assets/default_model.png"
|
||||
console.log("modelImgPlaceholder:",modelImgPlaceholder)
|
||||
const bUrl = import.meta.env.VITE_LOLLMS_API_BASEURL
|
||||
export default {
|
||||
name: 'ChatBox',
|
||||
emits: ["messageSentEvent", "sendCMDEvent", "stopGenerating", "loaded", "createEmptyUserMessage", "createEmptyAIMessage", "personalitySelected"],
|
||||
emits: ["messageSentEvent", "sendCMDEvent", "stopGenerating", "loaded", "createEmptyUserMessage", "createEmptyAIMessage", "personalitySelected","addWebLink"],
|
||||
props: {
|
||||
onTalk: Function,
|
||||
discussionList: Array,
|
||||
@ -339,7 +345,6 @@ export default {
|
||||
|
||||
},
|
||||
components: {
|
||||
Toast,
|
||||
UniversalForm,
|
||||
MountedPersonalities,
|
||||
MountedPersonalitiesList,
|
||||
@ -433,17 +438,17 @@ export default {
|
||||
|
||||
if (response && response.data) {
|
||||
console.log('binding set with new settings', response.data)
|
||||
this.$refs.toast.showToast("Binding settings updated successfully!", 4, true)
|
||||
this.$store.state.toast.showToast("Binding settings updated successfully!", 4, true)
|
||||
|
||||
} else {
|
||||
this.$refs.toast.showToast("Did not get binding settings responses.\n" + response, 4, false)
|
||||
this.$store.state.toast.showToast("Did not get binding settings responses.\n" + response, 4, false)
|
||||
this.isLoading = false
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
} catch (error) {
|
||||
this.$refs.toast.showToast("Did not get binding settings responses.\n Endpoint error: " + error.message, 4, false)
|
||||
this.$store.state.toast.showToast("Did not get binding settings responses.\n Endpoint error: " + error.message, 4, false)
|
||||
this.isLoading = false
|
||||
}
|
||||
|
||||
@ -451,7 +456,7 @@ export default {
|
||||
|
||||
})
|
||||
} else {
|
||||
this.$refs.toast.showToast("Binding has no settings", 4, false)
|
||||
this.$store.state.toast.showToast("Binding has no settings", 4, false)
|
||||
this.isLoading = false
|
||||
}
|
||||
|
||||
@ -460,7 +465,7 @@ export default {
|
||||
|
||||
} catch (error) {
|
||||
this.isLoading = false
|
||||
this.$refs.toast.showToast("Could not open binding settings. Endpoint error: " + error.message, 4, false)
|
||||
this.$store.state.toast.showToast("Could not open binding settings. Endpoint error: " + error.message, 4, false)
|
||||
}
|
||||
},
|
||||
async unmountPersonality(pers) {
|
||||
@ -472,7 +477,7 @@ export default {
|
||||
|
||||
if (res.status) {
|
||||
this.$store.state.config.personalities = res.personalities
|
||||
this.$refs.toast.showToast("Personality unmounted", 4, true)
|
||||
this.$store.state.toast.showToast("Personality unmounted", 4, true)
|
||||
|
||||
//pers.isMounted = false
|
||||
this.$store.dispatch('refreshMountedPersonalities');
|
||||
@ -483,12 +488,12 @@ export default {
|
||||
// const res2 = await this.select_personality(lastPers.personality)
|
||||
const res2 = await this.select_personality(pers.personality)
|
||||
if (res2.status) {
|
||||
this.$refs.toast.showToast("Selected personality:\n" + lastPers.name, 4, true)
|
||||
this.$store.state.toast.showToast("Selected personality:\n" + lastPers.name, 4, true)
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
this.$refs.toast.showToast("Could not unmount personality\nError: " + res.error, 4, false)
|
||||
this.$store.state.toast.showToast("Could not unmount personality\nError: " + res.error, 4, false)
|
||||
}
|
||||
|
||||
this.loading = false
|
||||
@ -523,7 +528,7 @@ export default {
|
||||
if (pers) {
|
||||
|
||||
if (pers.selected) {
|
||||
this.$refs.toast.showToast("Personality already selected", 4, true)
|
||||
this.$store.state.toast.showToast("Personality already selected", 4, true)
|
||||
return
|
||||
}
|
||||
|
||||
@ -537,10 +542,10 @@ export default {
|
||||
const res = await this.select_personality(pers)
|
||||
console.log('pers is mounted', res)
|
||||
if (res && res.status && res.active_personality_id > -1) {
|
||||
this.$refs.toast.showToast("Selected personality:\n" + pers.name, 4, true)
|
||||
this.$store.state.toast.showToast("Selected personality:\n" + pers.name, 4, true)
|
||||
|
||||
} else {
|
||||
this.$refs.toast.showToast("Error on select personality:\n" + pers.name, 4, false)
|
||||
this.$store.state.toast.showToast("Error on select personality:\n" + pers.name, 4, false)
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -615,10 +620,10 @@ export default {
|
||||
console.log(response);
|
||||
await this.$store.dispatch('refreshConfig');
|
||||
await this.$store.dispatch('refreshModels');
|
||||
this.$refs.toast.showToast(`Model changed to ${this.currentModel.name}`,4,true)
|
||||
this.$store.state.toast.showToast(`Model changed to ${this.currentModel.name}`,4,true)
|
||||
this.selecting_model=false
|
||||
}).catch(err=>{
|
||||
this.$refs.toast.showToast(`Error ${err}`,4,true)
|
||||
this.$store.state.toast.showToast(`Error ${err}`,4,true)
|
||||
this.selecting_model=false
|
||||
});
|
||||
|
||||
@ -663,7 +668,7 @@ export default {
|
||||
console.log('File sent successfully');
|
||||
this.isFileSentList[this.filesList.length-1]=true;
|
||||
console.log(this.isFileSentList)
|
||||
this.onShowToastMessage("File uploaded successfully",4,true);
|
||||
this.$store.state.toast.showToast("File uploaded successfully",4,true);
|
||||
this.loading = false
|
||||
next();
|
||||
}
|
||||
@ -769,6 +774,10 @@ export default {
|
||||
sendCMDEvent(cmd){
|
||||
this.$emit('sendCMDEvent', cmd)
|
||||
},
|
||||
addWebLink(){
|
||||
console.log("Emitting addWebLink")
|
||||
this.$emit('addWebLink')
|
||||
},
|
||||
takePicture(){
|
||||
socket.emit('take_picture')
|
||||
socket.on('picture_taken',()=>{
|
||||
|
@ -1,63 +0,0 @@
|
||||
<template>
|
||||
<div class="floating-frame">
|
||||
<img v-if="isVideoActive" :src="imageDataUrl" alt="Webcam Frame" width="300" height="300" />
|
||||
<div class="controls">
|
||||
<button v-if="!isVideoActive" class="w-full bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded" @click="startVideoStream">Activate Video</button>
|
||||
<button v-if="isVideoActive" class="w-full bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded" @click="stopVideoStream">Deactivate Video</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import socket from '@/services/websocket.js';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isVideoActive: false,
|
||||
imageDataUrl: null
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
startVideoStream() {
|
||||
this.isVideoActive = true;
|
||||
socket.emit('start_webcam_video_stream');
|
||||
},
|
||||
stopVideoStream() {
|
||||
this.isVideoActive = false;
|
||||
this.imageData = null;
|
||||
socket.emit('stop_webcam_video_stream');
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
socket.on('image', (imageBase64) => {
|
||||
if (this.isVideoActive) {
|
||||
this.imageDataUrl = 'data:image/jpeg;base64,' + imageBase64;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.floating-frame {
|
||||
margin: 15px;
|
||||
float: left;
|
||||
width: 200px;
|
||||
height: auto;
|
||||
border: 1px solid #000;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
z-index: 5000;
|
||||
}
|
||||
|
||||
.floating-frame img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="relative group rounded-lg m-2 shadow-lg hover:border-primary dark:hover:border-primary hover:border-solid hover:border-2 border-2 border-transparent even:bg-bg-light-discussion-odd dark:even:bg-bg-dark-discussion-odd flex flex-col flex-grow flex-wrap overflow-visible p-4 pb-2 ">
|
||||
class="relative w-full group rounded-lg m-2 shadow-lg hover:border-primary dark:hover:border-primary hover:border-solid hover:border-2 border-2 border-transparent even:bg-bg-light-discussion-odd dark:even:bg-bg-dark-discussion-odd flex flex-col flex-grow flex-wrap overflow-visible p-4 pb-2 ">
|
||||
<div class="flex flex-row gap-2 ">
|
||||
<div class=" flex-shrink-0">
|
||||
<!-- AVATAR -->
|
||||
|
@ -2,39 +2,33 @@
|
||||
<div class="absolute bottom-16 right-2 z-20 flex flex-col gap-3 min-w-[300px]">
|
||||
<TransitionGroup name="toastItem" tag="div">
|
||||
<div v-for=" t in toastArr" :key="t.id" class="relative">
|
||||
<div class="flex flex-row items-center w-full max-w-xs p-4 mb-4 text-gray-500 bg-white rounded-lg shadow dark:text-gray-400 dark:bg-gray-800"
|
||||
role="alert">
|
||||
<div class="flex flex-row flex-grow items-center">
|
||||
<slot>
|
||||
<div class="flex flex-row items-center w-full p-4 mb-4 text-gray-500 bg-white rounded-lg shadow dark:text-gray-400 dark:bg-gray-800" role="alert">
|
||||
<div class="flex flex-row flex-grow items-center h-auto">
|
||||
<div v-if="t.log_type==0"
|
||||
class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-red-500 bg-red-100 rounded-lg dark:bg-red-800 dark:text-red-200">
|
||||
<i data-feather="x"></i>
|
||||
<span class="sr-only">Cross icon</span>
|
||||
</div>
|
||||
<div v-if="t.log_type==1"
|
||||
class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-green-500 bg-green-100 rounded-lg dark:bg-green-800 dark:text-green-200">
|
||||
<i data-feather="check"></i>
|
||||
<span class="sr-only">Check icon</span>
|
||||
</div>
|
||||
<div v-if="t.log_type==2"
|
||||
class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-blue-500 bg-blue-100 rounded-lg dark:bg-blue-800 dark:text-blue-200">
|
||||
<i data-feather="info"></i>
|
||||
<span class="sr-only"></span>
|
||||
</div>
|
||||
<div v-if="t.log_type==3"
|
||||
class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-orange-500 bg-orange-100 rounded-lg dark:bg-orange-800 dark:text-orange-200">
|
||||
<i data-feather="alert-triangle"></i>
|
||||
<span class="sr-only"></span>
|
||||
</div>
|
||||
<div class="ml-3 text-sm font-normal whitespace-pre-wrap line-clamp-3 max-w-xs max-h-[400px] overflow-auto break-words" :title="t.message">{{ t.message }}</div>
|
||||
|
||||
<div v-if="t.log_type==0"
|
||||
class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-red-500 bg-red-100 rounded-lg dark:bg-red-800 dark:text-red-200">
|
||||
<i data-feather="x"></i>
|
||||
<span class="sr-only">Cross icon</span>
|
||||
</div>
|
||||
<div v-if="t.log_type==1"
|
||||
class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-green-500 bg-green-100 rounded-lg dark:bg-green-800 dark:text-green-200">
|
||||
<i data-feather="check"></i>
|
||||
<span class="sr-only">Check icon</span>
|
||||
</div>
|
||||
<div v-if="t.log_type==2"
|
||||
class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-blue-500 bg-blue-100 rounded-lg dark:bg-blue-800 dark:text-blue-200">
|
||||
<i data-feather="info"></i>
|
||||
<span class="sr-only"></span>
|
||||
</div>
|
||||
<div v-if="t.log_type==3"
|
||||
class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-orange-500 bg-orange-100 rounded-lg dark:bg-orange-800 dark:text-orange-200">
|
||||
<i data-feather="alert-triangle"></i>
|
||||
<span class="sr-only"></span>
|
||||
</div>
|
||||
<div class="ml-3 text-sm font-normal whitespace-pre-wrap line-clamp-3" :title="t.message">{{ t.message }}</div>
|
||||
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<div class="flex ">
|
||||
|
||||
|
||||
<button type="button" @click.stop="copyToClipBoard(t.message)" title="Copy message"
|
||||
class=" bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex h-8 w-8 dark:text-gray-500 dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700">
|
||||
<span class="sr-only">Copy message</span>
|
||||
|
@ -35,12 +35,12 @@
|
||||
<div v-if="!isConnected" title="Connection status: Not connected" class="text-red-500 cursor-pointer">
|
||||
<i data-feather="zap-off"></i>
|
||||
</div>
|
||||
<a href="#" @click="refreshPage">
|
||||
<div class="text-2xl hover:text-primary duration-150" title="refresh page">
|
||||
<a href="#" @click="restartProgram">
|
||||
<div class="text-2xl hover:text-primary duration-150" title="restart program">
|
||||
<i data-feather="refresh-ccw"></i>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
|
||||
<a href="https://github.com/ParisNeo/lollms-webui" target="_blank">
|
||||
|
||||
<div class="text-2xl hover:text-primary duration-150" title="Visit repository page">
|
||||
@ -87,7 +87,8 @@
|
||||
<div v-show="progress_visibility" role="status" class="fixed m-0 p-2 left-2 bottom-2 min-w-[24rem] max-w-[24rem] h-20 flex flex-col justify-center items-center pb-4 bg-blue-500 rounded-lg shadow-lg z-50 background-a">
|
||||
<ProgressBar ref="progress" :progress="progress_value" class="w-full h-4"></ProgressBar>
|
||||
<p class="text-2xl animate-pulse mt-2 text-white">{{ loading_infos }} ...</p>
|
||||
</div>
|
||||
</div>
|
||||
<UniversalForm ref="universalForm" class="z-20" />
|
||||
</header>
|
||||
|
||||
<body>
|
||||
@ -100,6 +101,8 @@ import Discussion from '../components/Discussion.vue'
|
||||
import Toast from '../components/Toast.vue'
|
||||
import MessageBox from "@/components/MessageBox.vue";
|
||||
import ProgressBar from "@/components/ProgressBar.vue";
|
||||
import UniversalForm from '../components/UniversalForm.vue';
|
||||
|
||||
|
||||
import { RouterLink } from 'vue-router'
|
||||
import Navigation from './Navigation.vue'
|
||||
@ -129,10 +132,15 @@ export default {
|
||||
Discussion,
|
||||
Toast,
|
||||
MessageBox,
|
||||
ProgressBar
|
||||
ProgressBar,
|
||||
UniversalForm,
|
||||
},
|
||||
watch:{
|
||||
isConnected(){
|
||||
if (!this.isConnected){
|
||||
this.disconnected_audio.play()
|
||||
this.$store.state.toast.showToast("Server suddenly disconnected. Please reboot the server", 410, false)
|
||||
}
|
||||
nextTick(() => {
|
||||
feather.replace()
|
||||
})
|
||||
@ -141,6 +149,8 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rebooting_the_tool_audio: new Audio("rebooting.mp3"),
|
||||
disconnected_audio: new Audio("disconnected.mp3"),
|
||||
database_selectorDialogVisible:false,
|
||||
progress_visibility:false,
|
||||
progress_value:0,
|
||||
@ -153,6 +163,8 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
this.$store.state.toast = this.$refs.toast
|
||||
this.$store.state.messageBox = this.$refs.messageBox
|
||||
this.$store.state.universalForm = this.$refs.universalForm
|
||||
this.sunIcon = document.querySelector(".sun");
|
||||
this.moonIcon = document.querySelector(".moon");
|
||||
this.userTheme = localStorage.getItem("theme");
|
||||
@ -172,9 +184,19 @@ export default {
|
||||
this.systemTheme = window.matchMedia("prefers-color-scheme: dark").matches;
|
||||
},
|
||||
methods: {
|
||||
refreshPage(event) {
|
||||
restartProgram(event) {
|
||||
event.preventDefault();
|
||||
window.location.reload();
|
||||
this.$store.state.api_get_req('restart_program')
|
||||
this.rebooting_the_tool_audio.play()
|
||||
this.$store.state.toast.showToast("Rebooting the app. Please wait...", 410, false)
|
||||
//self.$store.state.toast.showToast("Rebooting the app. Please wait...", 50, true);
|
||||
console.log("this.$store.state.api_get_req",this.$store.state.api_get_req)
|
||||
setTimeout(()=>{
|
||||
window.close();
|
||||
},2000)
|
||||
},
|
||||
handleOk(inputText) {
|
||||
console.log("Input text:", inputText);
|
||||
},
|
||||
// codeBlockTheme(theme) {
|
||||
// const styleDark = document.createElement('link');
|
||||
|
131
web/src/components/VideoFrame.vue
Normal file
131
web/src/components/VideoFrame.vue
Normal file
@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<div class="floating-frame bg-white" :style="{ bottom: position.bottom + 'px', right: position.right + 'px', 'z-index': zIndex }" @mousedown.stop="startDrag" @mouseup.stop="stopDrag">
|
||||
<div class="handle" @mousedown.stop="startDrag" @mouseup.stop="stopDrag">Drag Me</div>
|
||||
<img v-if="isVideoActive && imageDataUrl!=null" :src="imageDataUrl" alt="Webcam Frame" width="300" height="300" />
|
||||
<p v-if="isVideoActive && imageDataUrl==null" :src="imageDataUrl" alt="Webcam Frame" width="300" height="300" >Loading. Please wait...</p>
|
||||
<div class="controls">
|
||||
<button v-if="!isVideoActive" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded" @click="startVideoStream"><i data-feather='video'></i></button>
|
||||
<button v-if="isVideoActive" class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded" @click="stopVideoStream"><i data-feather='video'></i></button>
|
||||
<span v-if="isVideoActive">FPS: {{ frameRate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import socket from '@/services/websocket.js';
|
||||
import feather from 'feather-icons'
|
||||
import { nextTick } from 'vue'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isVideoActive: false,
|
||||
imageDataUrl: null,
|
||||
isDragging: false,
|
||||
position: { bottom: 0, right: 0 },
|
||||
dragStart: { x: 0, y: 0 },
|
||||
zIndex: 0, // Add a data property for z-index
|
||||
frameRate: 0,
|
||||
frameCount: 0,
|
||||
lastFrameTime: Date.now(),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
startVideoStream() {
|
||||
this.isVideoActive = true;
|
||||
socket.emit('start_webcam_video_stream');
|
||||
nextTick(() => {
|
||||
feather.replace()
|
||||
})
|
||||
},
|
||||
stopVideoStream() {
|
||||
this.isVideoActive = false;
|
||||
this.imageData = null;
|
||||
socket.emit('stop_webcam_video_stream');
|
||||
nextTick(() => {
|
||||
feather.replace()
|
||||
})
|
||||
},
|
||||
startDrag(event) {
|
||||
this.isDragging = true;
|
||||
this.zIndex = 5001; // Increase z-index when dragging starts
|
||||
this.dragStart.x = event.clientX;
|
||||
this.dragStart.y = event.clientY;
|
||||
document.addEventListener('mousemove', this.drag);
|
||||
document.addEventListener('mouseup', this.stopDrag);
|
||||
},
|
||||
drag(event) {
|
||||
if (this.isDragging) {
|
||||
const deltaX = event.clientX - this.dragStart.x;
|
||||
const deltaY = event.clientY - this.dragStart.y;
|
||||
this.position.bottom -= deltaY;
|
||||
this.position.right -= deltaX;
|
||||
this.dragStart.x = event.clientX;
|
||||
this.dragStart.y = event.clientY;
|
||||
}
|
||||
},
|
||||
stopDrag() {
|
||||
this.isDragging = false;
|
||||
this.zIndex = 0; // Reset z-index when dragging stops
|
||||
document.removeEventListener('mousemove', this.drag);
|
||||
document.removeEventListener('mouseup', this.stopDrag);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
feather.replace();
|
||||
socket.on('video_stream_image', (imageBase64) => {
|
||||
if (this.isVideoActive) {
|
||||
this.imageDataUrl = 'data:image/jpeg;base64,' + imageBase64;
|
||||
this.frameCount++;
|
||||
const now = Date.now();
|
||||
const delta = now - this.lastFrameTime;
|
||||
if (delta >= 1000) { // Calculate FPS every second
|
||||
this.frameRate = this.frameCount;
|
||||
this.frameCount = 0;
|
||||
this.lastFrameTime = now;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.floating-frame {
|
||||
margin: 15px;
|
||||
float: left;
|
||||
height: auto;
|
||||
border: 1px solid #000;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
z-index: 5000;
|
||||
position: fixed;
|
||||
cursor: move;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.handle {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
background: #ccc;
|
||||
cursor: move;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.floating-frame img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* Add a container for the floating frames and apply flexbox */
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
</style>
|
48
web/src/components/input_box.vue
Normal file
48
web/src/components/input_box.vue
Normal file
@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="show" class="fixed top-0 left-0 w-full h-full flex justify-center items-center bg-black bg-opacity-50">
|
||||
<div class="bg-white p-8 rounded">
|
||||
<h2 class="text-xl font-bold mb-4">{{ promptText }}</h2>
|
||||
<input type="text" v-model="inputText" class="border border-gray-300 px-4 py-2 rounded mb-4" />
|
||||
<button @click="ok" class="bg-blue-500 text-white px-4 py-2 rounded mr-2">OK</button>
|
||||
<button @click="cancel" class="bg-gray-500 text-white px-4 py-2 rounded">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
prompt: "",
|
||||
inputText: ""
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
showPanel() {
|
||||
this.show = true;
|
||||
},
|
||||
ok() {
|
||||
this.show = false;
|
||||
this.$emit("ok", this.inputText);
|
||||
},
|
||||
cancel() {
|
||||
this.show = false;
|
||||
this.inputText = "";
|
||||
}
|
||||
},
|
||||
props: {
|
||||
promptText: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
promptText(newPrompt) {
|
||||
this.prompt = newPrompt;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@ -24,6 +24,11 @@ export const store = createStore({
|
||||
state () {
|
||||
return {
|
||||
// count: 0,
|
||||
universalForm:null,
|
||||
toast:null,
|
||||
messageBox:null,
|
||||
api_get_req:null,
|
||||
startSpeechRecognition:null,
|
||||
ready:false,
|
||||
loading_infos: "",
|
||||
loading_progress: 0,
|
||||
@ -498,6 +503,7 @@ let actionsExecuted = false;
|
||||
app.mixin({
|
||||
async created() {
|
||||
if (!actionsExecuted) {
|
||||
this.$store.state.api_get_req = api_get_req
|
||||
actionsExecuted = true;
|
||||
console.log("Calling")
|
||||
this.$store.state.loading_infos = "Loading Configuration"
|
||||
|
@ -191,14 +191,14 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="relative flex flex-row flex-grow mb-10 z-0">
|
||||
<div class="relative flex flex-row flex-grow mb-10 z-0 w-full">
|
||||
|
||||
<!-- DISCUSSION LIST -->
|
||||
<div class="mx-4 flex flex-col flex-grow " :class="isDragOverDiscussion ? 'pointer-events-none' : ''">
|
||||
<div class="mx-4 flex flex-col flex-grow w-full " :class="isDragOverDiscussion ? 'pointer-events-none' : ''">
|
||||
|
||||
|
||||
<div id="dis-list" :class="filterInProgress ? 'opacity-20 pointer-events-none' : ''"
|
||||
class="flex flex-col flex-grow ">
|
||||
class="flex flex-col flex-grow w-full">
|
||||
<TransitionGroup v-if="list.length > 0" name="list">
|
||||
<Discussion v-for="(item, index) in list" :key="item.id" :id="item.id" :title="item.title"
|
||||
:selected="currentDiscussion.id == item.id" :loading="item.loading" :isCheckbox="isCheckbox"
|
||||
@ -226,13 +226,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
<div v-if="isReady" class="relative flex flex-col flex-grow" >
|
||||
<div v-if="isReady" class="relative flex flex-col flex-grow w-full" >
|
||||
<div id="messages-list"
|
||||
class=" z-0 flex flex-col flex-grow 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"
|
||||
class="w-full z-0 flex flex-col flex-grow 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"
|
||||
:class="isDragOverChat ? 'pointer-events-none' : ''">
|
||||
|
||||
<!-- CHAT AREA -->
|
||||
<div class=" container pt-4 pb-10 mb-28">
|
||||
<div class="container pt-4 pb-50 mb-50 w-full">
|
||||
<TransitionGroup v-if="discussionArr.length > 0" name="list">
|
||||
<Message v-for="(msg, index) in discussionArr"
|
||||
:key="msg.id" :message="msg" :id="'msg-' + msg.id"
|
||||
@ -249,7 +249,7 @@
|
||||
|
||||
</TransitionGroup>
|
||||
<WelcomeComponent v-if="!currentDiscussion.id" />
|
||||
|
||||
<div><br><br><br><br><br><br><br></div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
@ -261,9 +261,11 @@
|
||||
:discussionList="discussionArr"
|
||||
:on-show-toast-message="showToastMessage"
|
||||
:on-talk="talk"
|
||||
|
||||
@personalitySelected="recoverFiles"
|
||||
@messageSentEvent="sendMsg"
|
||||
@sendCMDEvent="sendCmd"
|
||||
@addWebLink="add_webpage"
|
||||
@createEmptyUserMessage="createEmptyUserMessage"
|
||||
@createEmptyAIMessage="createEmptyAIMessage"
|
||||
@stopGenerating="stopGenerating"
|
||||
@ -275,7 +277,6 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<MessageBox ref="messageBox" />
|
||||
<ChoiceDialog reference="database_selector" class="z-20"
|
||||
:show="database_selectorDialogVisible"
|
||||
:choices="databases"
|
||||
@ -287,6 +288,8 @@
|
||||
<ProgressBar ref="progress" :progress="progress_value" class="w-full h-4"></ProgressBar>
|
||||
<p class="text-2xl animate-pulse mt-2 text-white">{{ loading_infos }} ...</p>
|
||||
</div>
|
||||
<InputBox prompt-text="Enter the url to the page to use as discussion support" @ok="handleOk" ref="web_url_input_box"></InputBox>
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
@ -440,6 +443,24 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
add_webpage(){
|
||||
console.log("addWebLink received")
|
||||
this.$refs.web_url_input_box.showPanel();
|
||||
},
|
||||
handleOk(){
|
||||
console.log("OK")
|
||||
socket.on('web_page_added',()=>{
|
||||
axios.get('/get_current_personality_files_list').then(res=>{
|
||||
this.filesList = res.data.files;
|
||||
console.log("this.filesList",this.filesList)
|
||||
this.isFileSentList= res.data.files.map(file => {
|
||||
return true;
|
||||
});
|
||||
console.log(`Files recovered: ${this.filesList}`)
|
||||
})
|
||||
});
|
||||
socket.emit('add_webpage',{'url':this.$refs.web_url_input_box.inputText})
|
||||
},
|
||||
show_progress(data){
|
||||
this.progress_visibility_val = true;
|
||||
},
|
||||
@ -463,7 +484,7 @@ export default {
|
||||
|
||||
// open form
|
||||
|
||||
this.$refs.universalForm.showForm(res.data, "Binding settings - " + bindingEntry.binding.name, "Save changes", "Cancel").then(res => {
|
||||
this.$store.state.universalForm.showForm(res.data, "Binding settings - " + bindingEntry.binding.name, "Save changes", "Cancel").then(res => {
|
||||
// send new data
|
||||
try {
|
||||
axios.post('/set_active_binding_settings',
|
||||
@ -554,13 +575,13 @@ export default {
|
||||
this.$store.state.toast.showToast("Settings saved!",4,true)
|
||||
}
|
||||
else
|
||||
this.$refs.messageBox.showMessage("Error: Couldn't save settings!")
|
||||
this.$store.state.messageBox.showMessage("Error: Couldn't save settings!")
|
||||
return res.data;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error.message, 'save_configuration')
|
||||
this.$refs.messageBox.showMessage("Couldn't save settings!")
|
||||
this.$store.state.messageBox.showMessage("Couldn't save settings!")
|
||||
return { 'status': false }
|
||||
});
|
||||
|
||||
@ -1206,7 +1227,7 @@ export default {
|
||||
this.$store.state.toast.showToast(notif.content, notif.duration, notif.notification_type)
|
||||
}
|
||||
else if(notif.display_type==1){
|
||||
this.$refs.messageBox.showMessage(notif.content)
|
||||
this.$store.state.messageBox.showMessage(notif.content)
|
||||
}
|
||||
this.chime.play()
|
||||
},
|
||||
@ -1929,8 +1950,8 @@ export default {
|
||||
ChatBox,
|
||||
WelcomeComponent,
|
||||
ChoiceDialog,
|
||||
MessageBox,
|
||||
ProgressBar
|
||||
ProgressBar,
|
||||
InputBox
|
||||
},
|
||||
watch: {
|
||||
progress_visibility_val(newVal) {
|
||||
@ -2062,8 +2083,9 @@ export default {
|
||||
<script setup>
|
||||
import Discussion from '../components/Discussion.vue'
|
||||
import ChoiceDialog from '@/components/ChoiceDialog.vue'
|
||||
import MessageBox from "@/components/MessageBox.vue";
|
||||
import ProgressBar from "@/components/ProgressBar.vue";
|
||||
import InputBox from "@/components/input_box.vue";
|
||||
|
||||
|
||||
import Message from '../components/Message.vue'
|
||||
import ChatBox from '../components/ChatBox.vue'
|
||||
|
@ -1,28 +1,30 @@
|
||||
<template>
|
||||
<div class="flex-col w-[800]px y-overflow 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">
|
||||
<div v-if="!activePersonality || !activePersonality.scene_path" class="text-center">
|
||||
<!-- Display text when there's no scene_path or empty avatar -->
|
||||
Personality does not have a 3d avatar.
|
||||
</div>
|
||||
<div v-if="!activePersonality || (!activePersonality.avatar || activePersonality.avatar === '')" class="text-center">
|
||||
Personality does not have an avatar.
|
||||
</div>
|
||||
<FloatingFrame />
|
||||
<AudioFrame />
|
||||
<div class="floating-frame2">
|
||||
<div v-html="htmlContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="webglContainer">
|
||||
</div>
|
||||
<div ref="webglContainer">
|
||||
</div>
|
||||
<div class="flex-col y-overflow 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">
|
||||
<div v-if="!activePersonality || !activePersonality.scene_path" class="text-center">
|
||||
<!-- Display text when there's no scene_path or empty avatar -->
|
||||
Personality does not have a 3d avatar.
|
||||
</div>
|
||||
<div v-if="!activePersonality || (!activePersonality.avatar || activePersonality.avatar === '')" class="text-center">
|
||||
Personality does not have an avatar.
|
||||
</div>
|
||||
<div class="floating-frame2">
|
||||
<div v-html="htmlContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
<VideoFrame ref="video_frame"/>
|
||||
<AudioFrame ref="audio_frame"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as THREE from 'three';
|
||||
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
||||
import { TextureLoader } from 'three';
|
||||
import FloatingFrame from '@/components/FloatingFrame.vue';
|
||||
import VideoFrame from '@/components/VideoFrame.vue';
|
||||
import AudioFrame from '@/components/AudioFrame.vue';
|
||||
import feather from 'feather-icons'
|
||||
import { nextTick } from 'vue'
|
||||
|
||||
|
||||
export default {
|
||||
@ -38,7 +40,7 @@
|
||||
},
|
||||
},
|
||||
components: {
|
||||
FloatingFrame,
|
||||
VideoFrame,
|
||||
AudioFrame
|
||||
},
|
||||
computed: {
|
||||
@ -59,6 +61,11 @@
|
||||
console.log("Personality:", this.personality)
|
||||
this.initWebGLScene();
|
||||
this.updatePersonality();
|
||||
nextTick(() => {
|
||||
feather.replace()
|
||||
})
|
||||
this.$refs.video_frame.position = { bottom: 0, right: 0 }
|
||||
this.$refs.audio_frame.position = { bottom: 0, right: 100 }
|
||||
},
|
||||
beforeDestroy() {
|
||||
// Clean up WebGL resources here
|
||||
@ -163,7 +170,6 @@
|
||||
}
|
||||
.floating-frame2 {
|
||||
margin: 15px;
|
||||
float: left;
|
||||
width: 800px;
|
||||
height: auto;
|
||||
border: 1px solid #000;
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="container overflow-y-scroll flex flex-col shadow-lg p-10 pt-0 overflow-y-scroll w-full dark:bg-bg-dark 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">
|
||||
<div class="container overflow-y-scroll flex flex-row shadow-lg p-10 pt-0 overflow-y-scroll w-full dark:bg-bg-dark 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">
|
||||
<!-- CONTROL PANEL -->
|
||||
<div
|
||||
class="sticky top-0 z-10 flex flex-row mb-2 p-3 gap-3 w-full rounded-b-lg bg-bg-light-tone dark:bg-bg-dark-tone shadow-lg">
|
||||
@ -84,10 +84,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :class="isLoading ? 'pointer-events-none opacity-30' : ''">
|
||||
<div :class="isLoading ? 'pointer-events-none opacity-30 w-full' : 'w-full'">
|
||||
<!-- DISK AND RAM USAGE -->
|
||||
<div
|
||||
class="flex flex-col mb-2 rounded-lg bg-bg-light-tone dark:bg-bg-dark-tone hover:bg-bg-light-tone-panel hover:dark:bg-bg-dark-tone-panel duration-150 shadow-lg">
|
||||
<div class="flex flex-col mb-2 rounded-lg bg-bg-light-tone dark:bg-bg-dark-tone hover:bg-bg-light-tone-panel hover:dark:bg-bg-dark-tone-panel duration-150 shadow-lg">
|
||||
<div class="flex flex-row p-3">
|
||||
<button @click.stop="sc_collapsed = !sc_collapsed"
|
||||
class="text-2xl hover:text-primary p-2 -m-2 w-full text-left flex flex-row items-center ">
|
||||
@ -1652,8 +1651,6 @@
|
||||
</div>
|
||||
<YesNoDialog ref="yesNoDialog" class="z-20" />
|
||||
<AddModelDialog ref="addmodeldialog" />
|
||||
<MessageBox ref="messageBox" />
|
||||
<UniversalForm ref="universalForm" class="z-20" />
|
||||
<ChoiceDialog class="z-20"
|
||||
:show="variantSelectionDialogVisible"
|
||||
:choices="variant_choices"
|
||||
@ -1748,7 +1745,6 @@ import filesize from '../plugins/filesize'
|
||||
import axios from "axios";
|
||||
import feather from 'feather-icons'
|
||||
import { nextTick, TransitionGroup } from 'vue'
|
||||
import MessageBox from "@/components/MessageBox.vue";
|
||||
import YesNoDialog from "@/components/YesNoDialog.vue";
|
||||
import ModelEntry from '@/components/ModelEntry.vue';
|
||||
import PersonalityViewer from '@/components/PersonalityViewer.vue';
|
||||
@ -1762,7 +1758,6 @@ import defaultExtensionImgPlaceholder from "../assets/extension.png"
|
||||
import defaultImgPlaceholder from "../assets/default_model.png"
|
||||
|
||||
import AddModelDialog from "@/components/AddModelDialog.vue";
|
||||
import UniversalForm from '../components/UniversalForm.vue';
|
||||
|
||||
import ChoiceDialog from "@/components/ChoiceDialog.vue";
|
||||
import Card from "@/components/Card.vue"
|
||||
@ -1778,14 +1773,12 @@ axios.defaults.baseURL = import.meta.env.VITE_LOLLMS_API_BASEURL
|
||||
export default {
|
||||
components: {
|
||||
AddModelDialog,
|
||||
MessageBox,
|
||||
YesNoDialog,
|
||||
ModelEntry,
|
||||
// eslint-disable-next-line vue/no-unused-components
|
||||
PersonalityViewer,
|
||||
PersonalityEntry,
|
||||
BindingEntry,
|
||||
UniversalForm,
|
||||
ChoiceDialog,
|
||||
Card,
|
||||
RadioOptions,
|
||||
@ -1881,23 +1874,10 @@ export default {
|
||||
async created() {
|
||||
socket.on('loading_text',this.on_loading_text);
|
||||
this.updateHasUpdates();
|
||||
socket.on('notification', this.notify)
|
||||
//await socket.on('install_progress', this.progressListener);
|
||||
//refreshHardwareUsage()
|
||||
},
|
||||
methods: {
|
||||
notify(notif){
|
||||
nextTick(() => {
|
||||
const msgList = document.getElementById('messages-list')
|
||||
this.scrollBottom(msgList)
|
||||
})
|
||||
if(notif.display_type==0){
|
||||
this.$store.state.toast.showToast(notif.content, notif.duration, notif.notification_type)
|
||||
}
|
||||
else if(notif.display_type==1){
|
||||
this.$refs.messageBox.showMessage(notif.content)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
load_more_models(){
|
||||
if(this.models_zoo_initialLoadCount+10<this.models_zoo.length){
|
||||
this.models_zoo_initialLoadCount+=10
|
||||
@ -2616,8 +2596,8 @@ export default {
|
||||
this.isLoading = false
|
||||
console.log('install_binding', res)
|
||||
if (res.data.status) {
|
||||
this.$store.state.toast.showToast("Installed binding successfully!", 4, true)
|
||||
this.update_binding(binding_object.binding.folder);
|
||||
this.$store.state.toast.showToast("Binding installed successfully!", 4, true)
|
||||
this.$store.state.messageBox.showMessage("It is advised to reboot the application after installing a binding")
|
||||
} else {
|
||||
this.$store.state.toast.showToast("Could not reinstall binding", 4, false)
|
||||
}
|
||||
@ -2685,7 +2665,9 @@ export default {
|
||||
this.isLoading = false
|
||||
console.log('reinstall_binding', res)
|
||||
if (res.data.status) {
|
||||
this.$store.state.toast.showToast("Reinstalled binding successfully!", 4, true)
|
||||
this.$store.state.toast.showToast("Binding reinstalled successfully!", 4, true)
|
||||
this.$store.state.messageBox.showMessage("It is advised to reboot the application after installing a binding")
|
||||
|
||||
} else {
|
||||
this.$store.state.toast.showToast("Could not reinstall binding", 4, false)
|
||||
}
|
||||
@ -2732,7 +2714,7 @@ export default {
|
||||
console.log('ext sett', res)
|
||||
if (res.data && Object.keys(res.data).length > 0) {
|
||||
|
||||
this.$refs.universalForm.showForm(res.data, "Extension settings - " + extensionEntry.extension.name, "Save changes", "Cancel").then(res => {
|
||||
this.$store.state.universalForm.showForm(res.data, "Extension settings - " + extensionEntry.extension.name, "Save changes", "Cancel").then(res => {
|
||||
|
||||
// send new data
|
||||
try {
|
||||
@ -2782,22 +2764,21 @@ export default {
|
||||
|
||||
// open form
|
||||
|
||||
this.$refs.universalForm.showForm(res.data, "Binding settings - " + bindingEntry.binding.name, "Save changes", "Cancel").then(res => {
|
||||
this.$store.state.universalForm.showForm(res.data, "Binding settings - " + bindingEntry.binding.name, "Save changes", "Cancel").then(res => {
|
||||
// send new data
|
||||
try {
|
||||
axios.post('/set_active_binding_settings',
|
||||
res).then(response => {
|
||||
|
||||
if (response && response.data) {
|
||||
console.log('binding set with new settings', response.data)
|
||||
this.$store.state.toast.showToast("Binding settings updated successfully!", 4, true)
|
||||
|
||||
axios.get('/update_binding_settings').then((res) => {
|
||||
this.$store.state.toast.showToast("Binding settings committed successfully!", 4, true)
|
||||
})
|
||||
} else {
|
||||
this.$store.state.toast.showToast("Did not get binding settings responses.\n" + response, 4, false)
|
||||
this.isLoading = false
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
} catch (error) {
|
||||
this.$store.state.toast.showToast("Did not get binding settings responses.\n Endpoint error: " + error.message, 4, false)
|
||||
@ -2855,7 +2836,7 @@ export default {
|
||||
console.log('pers sett', res)
|
||||
if (res.data && Object.keys(res.data).length > 0) {
|
||||
|
||||
this.$refs.universalForm.showForm(res.data, "Personality settings - " + persEntry.personality.name, "Save changes", "Cancel").then(res => {
|
||||
this.$store.state.universalForm.showForm(res.data, "Personality settings - " + persEntry.personality.name, "Save changes", "Cancel").then(res => {
|
||||
|
||||
// send new data
|
||||
try {
|
||||
@ -3169,16 +3150,16 @@ export default {
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
if (res.status) {
|
||||
// this.$refs.messageBox.showMessage("Settings saved!")
|
||||
// this.$store.state.messageBox.showMessage("Settings saved!")
|
||||
}
|
||||
else
|
||||
this.$refs.messageBox.showMessage("Error: Couldn't save settings!")
|
||||
this.$store.state.messageBox.showMessage("Error: Couldn't save settings!")
|
||||
return res.data;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error.message, 'save_configuration')
|
||||
this.$refs.messageBox.showMessage("Couldn't save settings!")
|
||||
this.$store.state.messageBox.showMessage("Couldn't save settings!")
|
||||
return { 'status': false }
|
||||
});
|
||||
|
||||
@ -3191,15 +3172,15 @@ export default {
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
if (res.status)
|
||||
this.$refs.messageBox.showMessage("Settings have been reset correctly")
|
||||
this.$store.state.messageBox.showMessage("Settings have been reset correctly")
|
||||
else
|
||||
this.$refs.messageBox.showMessage("Couldn't reset settings!")
|
||||
this.$store.state.messageBox.showMessage("Couldn't reset settings!")
|
||||
return res.data;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error.message, 'reset_configuration')
|
||||
this.$refs.messageBox.showMessage("Couldn't reset settings!")
|
||||
this.$store.state.messageBox.showMessage("Couldn't reset settings!")
|
||||
return { 'status': false }
|
||||
});
|
||||
// Perform delete operation
|
||||
@ -3475,7 +3456,7 @@ export default {
|
||||
console.log('mount pers', pers)
|
||||
|
||||
if(pers.personality.disclaimer!=""){
|
||||
this.$refs.messageBox.showMessage(pers.personality.disclaimer)
|
||||
this.$store.state.messageBox.showMessage(pers.personality.disclaimer)
|
||||
}
|
||||
|
||||
if (!pers) { return }
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 7ac68c7e29775c79d97f3d1bd1aa2c7b5a46151f
|
||||
Subproject commit 0346a1efc2bc16d4e700d603fedc3ecbfe9b58bd
|
@ -1 +1 @@
|
||||
Subproject commit 7df292e43dd6f674cee6a620540836b77f0a2380
|
||||
Subproject commit 5385b1b599c0bbf9fbd71163be2f9a0184f19ab5
|
@ -1 +1 @@
|
||||
Subproject commit c1594f8e30bff5e5909f11289311079f13241260
|
||||
Subproject commit 97ec1416d0af1377a8cfc96d9f55cb3f31681293
|
Loading…
x
Reference in New Issue
Block a user