mirror of
https://github.com/ParisNeo/lollms-webui.git
synced 2025-02-20 17:22:47 +00:00
Enhanced documentation
This commit is contained in:
parent
074fbb5988
commit
b75d26861e
2
app.py
2
app.py
@ -17,7 +17,7 @@ from lollms.utilities import PackageManager
|
||||
|
||||
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
|
||||
|
||||
expected_ascii_colors_version = "0.5.0"
|
||||
expected_ascii_colors_version = "0.5.1"
|
||||
print(
|
||||
f"Checking ascii_colors ({expected_ascii_colors_version}) ...", end="", flush=True
|
||||
)
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 6d1c0b6b697f96b69e447ee5eccde642d4b278a2
|
||||
Subproject commit 13b4e756578ac79b8c5926bd355dec6bfb434e80
|
370
tools/code_doc/code_doc.py
Normal file
370
tools/code_doc/code_doc.py
Normal file
@ -0,0 +1,370 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Enhanced Python Code Documentation Generator
|
||||
|
||||
This script creates a PyQt application that generates comprehensive markdown documentation
|
||||
from Python source files. It extracts complete information about classes, functions,
|
||||
methods, variables, decorators and their associated docstrings.
|
||||
|
||||
Author: Generated by LoLLMs
|
||||
Date: 2025-01-16
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import ast
|
||||
import inspect
|
||||
from typing import Dict, List, Optional, Tuple, Union, Any
|
||||
from PyQt5.QtWidgets import (
|
||||
QApplication,
|
||||
QMainWindow,
|
||||
QWidget,
|
||||
QVBoxLayout,
|
||||
QHBoxLayout,
|
||||
QPushButton,
|
||||
QTextEdit,
|
||||
QFileDialog,
|
||||
QMessageBox,
|
||||
QLabel,
|
||||
QStyle
|
||||
)
|
||||
from PyQt5.QtGui import QFont, QIcon
|
||||
from PyQt5.QtCore import Qt
|
||||
import sys
|
||||
|
||||
class CodeParser:
|
||||
"""
|
||||
A class to parse Python source code and extract comprehensive documentation.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the CodeParser."""
|
||||
self.markdown: str = ""
|
||||
self.current_class: Optional[str] = None
|
||||
|
||||
def get_decorator_list(self, node: ast.AST) -> str:
|
||||
"""
|
||||
Extract decorator information from a node.
|
||||
|
||||
Args:
|
||||
node: AST node containing decorators
|
||||
|
||||
Returns:
|
||||
str: Formatted decorator string
|
||||
"""
|
||||
decorators = []
|
||||
for decorator in node.decorator_list:
|
||||
if isinstance(decorator, ast.Name):
|
||||
decorators.append(f"@{decorator.id}")
|
||||
elif isinstance(decorator, ast.Call):
|
||||
if isinstance(decorator.func, ast.Name):
|
||||
args = []
|
||||
for arg in decorator.args:
|
||||
if isinstance(arg, ast.Constant):
|
||||
args.append(str(arg.value))
|
||||
if args:
|
||||
decorators.append(f"@{decorator.func.id}({', '.join(args)})")
|
||||
else:
|
||||
decorators.append(f"@{decorator.func.id}()")
|
||||
return "\n".join(decorators)
|
||||
|
||||
def get_arguments(self, node: ast.AST) -> str:
|
||||
"""
|
||||
Extract function arguments information.
|
||||
|
||||
Args:
|
||||
node: AST node containing function arguments
|
||||
|
||||
Returns:
|
||||
str: Formatted argument string
|
||||
"""
|
||||
args = []
|
||||
|
||||
# Process arguments
|
||||
for arg in node.args.args:
|
||||
arg_str = arg.arg
|
||||
if hasattr(arg, 'annotation') and arg.annotation is not None:
|
||||
if isinstance(arg.annotation, ast.Name):
|
||||
arg_str += f": {arg.annotation.id}"
|
||||
elif isinstance(arg.annotation, ast.Subscript):
|
||||
arg_str += f": {ast.unparse(arg.annotation)}"
|
||||
args.append(arg_str)
|
||||
|
||||
# Process default values
|
||||
defaults = node.args.defaults
|
||||
if defaults:
|
||||
default_offset = len(args) - len(defaults)
|
||||
for i, default in enumerate(defaults):
|
||||
args[default_offset + i] += f" = {ast.unparse(default)}"
|
||||
|
||||
# Process variable arguments
|
||||
if node.args.vararg:
|
||||
args.append(f"*{node.args.vararg.arg}")
|
||||
|
||||
# Process keyword arguments
|
||||
if node.args.kwarg:
|
||||
args.append(f"**{node.args.kwarg.arg}")
|
||||
|
||||
return ", ".join(args)
|
||||
|
||||
def get_return_annotation(self, node: ast.AST) -> str:
|
||||
"""
|
||||
Extract return type annotation.
|
||||
|
||||
Args:
|
||||
node: AST node containing return annotation
|
||||
|
||||
Returns:
|
||||
str: Formatted return type string
|
||||
"""
|
||||
if node.returns:
|
||||
return f" -> {ast.unparse(node.returns)}"
|
||||
return ""
|
||||
|
||||
def parse_assign(self, node: ast.AST, level: int) -> None:
|
||||
"""
|
||||
Parse assignment nodes to extract class/module variables.
|
||||
|
||||
Args:
|
||||
node: AST assignment node
|
||||
level: Current nesting level
|
||||
"""
|
||||
for target in node.targets:
|
||||
if isinstance(target, ast.Name):
|
||||
name = target.id
|
||||
value = ast.unparse(node.value)
|
||||
if self.current_class:
|
||||
self.markdown += f"{'#' * (level + 3)} Class Variable: {name} = {value}\n\n"
|
||||
else:
|
||||
self.markdown += f"{'#' * (level + 2)} Module Variable: {name} = {value}\n\n"
|
||||
|
||||
def parse_class_bases(self, node: ast.ClassDef) -> str:
|
||||
"""
|
||||
Extract class inheritance information.
|
||||
|
||||
Args:
|
||||
node: Class definition node
|
||||
|
||||
Returns:
|
||||
str: Formatted base classes string
|
||||
"""
|
||||
bases = []
|
||||
for base in node.bases:
|
||||
if isinstance(base, ast.Name):
|
||||
bases.append(base.id)
|
||||
elif isinstance(base, ast.Attribute):
|
||||
bases.append(ast.unparse(base))
|
||||
return ", ".join(bases)
|
||||
|
||||
def parse_node(self, node: ast.AST, level: int = 0) -> None:
|
||||
"""
|
||||
Parse an AST node and extract relevant documentation.
|
||||
|
||||
Args:
|
||||
node: The AST node to parse
|
||||
level: The current nesting level for markdown formatting
|
||||
"""
|
||||
# Handle class definitions
|
||||
if isinstance(node, ast.ClassDef):
|
||||
bases = self.parse_class_bases(node)
|
||||
class_decor = self.get_decorator_list(node)
|
||||
|
||||
if class_decor:
|
||||
self.markdown += f"```python\n{class_decor}\n```\n"
|
||||
|
||||
self.markdown += f"{'#' * (level + 2)} Class: {node.name}"
|
||||
if bases:
|
||||
self.markdown += f" ({bases})"
|
||||
self.markdown += "\n\n"
|
||||
|
||||
if ast.get_docstring(node):
|
||||
self.markdown += f"```python\n{ast.get_docstring(node)}\n```\n\n"
|
||||
|
||||
# Parse class body
|
||||
self.current_class = node.name
|
||||
for child in node.body:
|
||||
if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef, ast.Assign)):
|
||||
self.parse_node(child, level + 1)
|
||||
self.current_class = None
|
||||
|
||||
# Handle function definitions
|
||||
elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
||||
prefix = "Method" if self.current_class else "Function"
|
||||
is_async = isinstance(node, ast.AsyncFunctionDef)
|
||||
|
||||
# Get decorators
|
||||
decorators = self.get_decorator_list(node)
|
||||
if decorators:
|
||||
self.markdown += f"```python\n{decorators}\n```\n"
|
||||
|
||||
# Function signature
|
||||
args = self.get_arguments(node)
|
||||
returns = self.get_return_annotation(node)
|
||||
async_prefix = "async " if is_async else ""
|
||||
|
||||
self.markdown += f"{'#' * (level + 2)} {prefix}: {async_prefix}{node.name}({args}){returns}\n\n"
|
||||
|
||||
# Function docstring
|
||||
if ast.get_docstring(node):
|
||||
self.markdown += f"```python\n{ast.get_docstring(node)}\n```\n\n"
|
||||
|
||||
# Handle assignments
|
||||
elif isinstance(node, ast.Assign):
|
||||
self.parse_assign(node, level)
|
||||
|
||||
def parse_imports(self, tree: ast.AST) -> None:
|
||||
"""
|
||||
Parse and format import statements.
|
||||
|
||||
Args:
|
||||
tree: AST tree
|
||||
"""
|
||||
imports = []
|
||||
for node in tree.body:
|
||||
if isinstance(node, ast.Import):
|
||||
for name in node.names:
|
||||
imports.append(f"import {name.name}")
|
||||
elif isinstance(node, ast.ImportFrom):
|
||||
names = ", ".join(name.name for name in node.names)
|
||||
imports.append(f"from {node.module} import {names}")
|
||||
|
||||
if imports:
|
||||
self.markdown += "## Imports\n\n```python\n"
|
||||
self.markdown += "\n".join(imports)
|
||||
self.markdown += "\n```\n\n"
|
||||
|
||||
def parse_file(self, file_path: Path) -> str:
|
||||
"""
|
||||
Parse a Python file and generate comprehensive markdown documentation.
|
||||
|
||||
Args:
|
||||
file_path: Path to the Python file
|
||||
|
||||
Returns:
|
||||
str: Generated markdown documentation
|
||||
"""
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
source = f.read()
|
||||
|
||||
self.markdown = f"# Documentation for {file_path.name}\n\n"
|
||||
|
||||
tree = ast.parse(source)
|
||||
|
||||
# Get module docstring
|
||||
module_doc = ast.get_docstring(tree)
|
||||
if module_doc:
|
||||
self.markdown += f"## Module Documentation\n\n```python\n{module_doc}\n```\n\n"
|
||||
|
||||
# Parse imports
|
||||
self.parse_imports(tree)
|
||||
|
||||
# Parse all top-level nodes
|
||||
for node in tree.body:
|
||||
if isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef, ast.Assign)):
|
||||
self.parse_node(node)
|
||||
|
||||
return self.markdown
|
||||
|
||||
except Exception as e:
|
||||
return f"Error parsing file: {str(e)}"
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
"""
|
||||
Main application window.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the main window."""
|
||||
super().__init__()
|
||||
self.setWindowTitle("Python Code Documentation Generator")
|
||||
self.setMinimumSize(800, 600)
|
||||
|
||||
# Initialize UI
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self) -> None:
|
||||
"""Initialize the user interface."""
|
||||
# Create central widget and layout
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
layout = QVBoxLayout(central_widget)
|
||||
|
||||
# Create top button bar
|
||||
button_layout = QHBoxLayout()
|
||||
|
||||
# Open file button
|
||||
self.open_btn = QPushButton("Open Python File")
|
||||
self.open_btn.clicked.connect(self.open_file)
|
||||
self.open_btn.setIcon(self.style().standardIcon(QStyle.SP_DialogOpenButton))
|
||||
button_layout.addWidget(self.open_btn)
|
||||
|
||||
# Copy button
|
||||
self.copy_btn = QPushButton("Copy to Clipboard")
|
||||
self.copy_btn.clicked.connect(self.copy_to_clipboard)
|
||||
self.copy_btn.setIcon(self.style().standardIcon(QStyle.SP_DialogSaveButton))
|
||||
button_layout.addWidget(self.copy_btn)
|
||||
|
||||
# Export button
|
||||
self.export_btn = QPushButton("Export Markdown")
|
||||
self.export_btn.clicked.connect(self.export_markdown)
|
||||
self.export_btn.setIcon(self.style().standardIcon(QStyle.SP_DialogSaveButton))
|
||||
button_layout.addWidget(self.export_btn)
|
||||
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
# Create text display area
|
||||
self.text_edit = QTextEdit()
|
||||
self.text_edit.setFont(QFont("Consolas", 10))
|
||||
self.text_edit.setReadOnly(True)
|
||||
layout.addWidget(self.text_edit)
|
||||
|
||||
# Status bar
|
||||
self.statusBar().showMessage("Ready")
|
||||
|
||||
def open_file(self) -> None:
|
||||
"""Handle file opening."""
|
||||
file_path, _ = QFileDialog.getOpenFileName(
|
||||
self,
|
||||
"Select Python File",
|
||||
str(Path.home()),
|
||||
"Python Files (*.py)"
|
||||
)
|
||||
|
||||
if file_path:
|
||||
parser = CodeParser()
|
||||
markdown = parser.parse_file(Path(file_path))
|
||||
self.text_edit.setPlainText(markdown)
|
||||
self.statusBar().showMessage(f"Loaded: {file_path}")
|
||||
|
||||
def copy_to_clipboard(self) -> None:
|
||||
"""Copy the generated markdown to clipboard."""
|
||||
QApplication.clipboard().setText(self.text_edit.toPlainText())
|
||||
self.statusBar().showMessage("Copied to clipboard!")
|
||||
|
||||
def export_markdown(self) -> None:
|
||||
"""Export the markdown to a file."""
|
||||
file_path, _ = QFileDialog.getSaveFileName(
|
||||
self,
|
||||
"Save Markdown File",
|
||||
str(Path.home()),
|
||||
"Markdown Files (*.md)"
|
||||
)
|
||||
|
||||
if file_path:
|
||||
try:
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(self.text_edit.toPlainText())
|
||||
self.statusBar().showMessage(f"Saved to: {file_path}")
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Error", f"Failed to save file: {str(e)}")
|
||||
|
||||
def main():
|
||||
"""Main application entry point."""
|
||||
app = QApplication(sys.argv)
|
||||
app.setStyle('Fusion')
|
||||
window = MainWindow()
|
||||
window.show()
|
||||
sys.exit(app.exec_())
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
8
web/dist/assets/index-0YxKCr9N.css
vendored
8
web/dist/assets/index-0YxKCr9N.css
vendored
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-Dou72F5q.css
vendored
Normal file
8
web/dist/assets/index-Dou72F5q.css
vendored
Normal file
File diff suppressed because one or more lines are too long
4
web/dist/index.html
vendored
4
web/dist/index.html
vendored
@ -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-Pb4Zykpb.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-0YxKCr9N.css">
|
||||
<script type="module" crossorigin src="/assets/index-DT-TjDjA.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Dou72F5q.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
@ -1320,7 +1320,7 @@ export default {
|
||||
"LoLLMs' version naming often contains clever easter eggs and references to AI advancements.",
|
||||
"The 'Strawberry' version of LoLLMs was a playful nod to ChatGPT's internal codename for one of its versions.",
|
||||
"The 'Saïph' version name was an intentional reference to Orion, anticipating OpenAI's rumored AGI-capable model codenamed 'Orion'.",
|
||||
"LoLLMs' evolution can be traced through its version names: Warp, Starship, Robot, Brainwave, Strawberry, Feather and Saïph.",
|
||||
"LoLLMs' evolution can be traced through its version names: Warp, Starship, Robot, Brainwave, Strawberry, Feather, Saïph, Nexus, Pulsar.",
|
||||
"Each LoLLMs version name reflects either technological advancement or pays homage to significant developments in AI.",
|
||||
"'Warp' and 'Starship' versions symbolized the quantum leap in AI capabilities and speed improvements.",
|
||||
"'Robot' represented the system's growing autonomy and ability to perform complex tasks.",
|
||||
|
@ -1255,13 +1255,16 @@
|
||||
v-model="configFile.rag_vectorizer_model"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-600 dark:border-gray-500"
|
||||
>
|
||||
<input v-if="configFile.rag_vectorizer === 'ollama'"
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col md:flex-row md:items-start gap-4">
|
||||
<label class="text-sm font-bold w-64 pt-2">RAG server address:</label>
|
||||
<input v-if="configFile.rag_vectorizer === 'ollama'"
|
||||
v-model="configFile.rag_service_url"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-600 dark:border-gray-500"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Numeric inputs with sliders -->
|
||||
<div class="space-y-6">
|
||||
<!-- Chunk Size -->
|
||||
@ -2191,54 +2194,61 @@
|
||||
|
||||
</Card>
|
||||
<Card title="Whisper audio transcription" :is_subcard="true" class="pb-2 m-2">
|
||||
<table 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">
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="whisper_activate" class="text-sm font-bold" style="margin-right: 1rem;">Activate Whisper at startup:</label>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex flex-row">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="whisper_activate"
|
||||
required
|
||||
v-model="configFile.whisper_activate"
|
||||
@change="settingsChanged=true"
|
||||
class="mt-1 px-2 py-1 border border-gray-300 rounded dark:bg-gray-600"
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 space-y-6">
|
||||
<!-- Whisper Activation Section -->
|
||||
<div class="flex items-center justify-between p-4 hover:bg-gray-50 dark:hover:bg-gray-700 rounded-lg transition-all">
|
||||
<label for="whisper_activate" class="text-gray-700 dark:text-gray-200 font-medium">
|
||||
Activate Whisper at startup
|
||||
</label>
|
||||
<label class="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="whisper_activate"
|
||||
v-model="configFile.whisper_activate"
|
||||
@change="settingsChanged=true"
|
||||
class="sr-only peer"
|
||||
>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="xtts_current_language" class="text-sm font-bold" style="margin-right: 1rem;"></label>
|
||||
</td>
|
||||
<td>
|
||||
<button class="hover:text-primary bg-green-200 rounded-lg p-4 m-4 w-full text-center items-center" @click="reinstallWhisperService">install whisper</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="whisper_model" class="text-sm font-bold" style="margin-right: 1rem;">Whisper model:</label>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex flex-row">
|
||||
<select
|
||||
id="whisper_model"
|
||||
v-model="configFile.whisper_model"
|
||||
@change="settingsChanged=true"
|
||||
class="w-full mt-1 px-2 py-1 border border-gray-300 rounded dark:bg-gray-600"
|
||||
>
|
||||
<!-- Options with language codes and corresponding language names -->
|
||||
<option v-for="whispermodel in whisperModels" :key="whispermodel" :value="whispermodel">
|
||||
{{ whispermodel }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</table>
|
||||
<!-- Install Whisper Button Section -->
|
||||
<div class="p-4">
|
||||
<button
|
||||
@click="reinstallWhisperService"
|
||||
class="w-full py-3 px-4 bg-gradient-to-r from-green-400 to-green-500 hover:from-green-500 hover:to-green-600 text-white font-medium rounded-lg shadow-md hover:shadow-lg transition-all duration-300 flex items-center justify-center space-x-2"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/>
|
||||
</svg>
|
||||
<span>Install Whisper</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Whisper Model Selection Section -->
|
||||
<div class="p-4">
|
||||
<label for="whisper_model" class="block text-sm font-medium text-gray-700 dark:text-gray-200 mb-2">
|
||||
Whisper Model
|
||||
</label>
|
||||
<div class="relative">
|
||||
<select
|
||||
id="whisper_model"
|
||||
v-model="configFile.whisper_model"
|
||||
@change="settingsChanged=true"
|
||||
class="block w-full px-4 py-3 text-base border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white appearance-none"
|
||||
>
|
||||
<option v-for="whispermodel in whisperModels" :key="whispermodel" :value="whispermodel">
|
||||
{{ whispermodel }}
|
||||
</option>
|
||||
</select>
|
||||
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700 dark:text-gray-300">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<Card title="Open AI Whisper audio transcription" :is_subcard="true" class="pb-2 m-2">
|
||||
<table 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">
|
||||
@ -3331,25 +3341,43 @@
|
||||
</Card>
|
||||
</Card>
|
||||
<Card title="TTV settings" :is_subcard="true" class="pb-2 m-2">
|
||||
<table 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">
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="lumalabs_key" class="text-sm font-bold" style="margin-right: 1rem;">Lumalabs key:</label>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex flex-row">
|
||||
<input
|
||||
type="text"
|
||||
id="lumalabs_key"
|
||||
required
|
||||
v-model="configFile.lumalabs_key"
|
||||
@change="settingsChanged=true"
|
||||
class="mt-1 px-2 py-1 border border-gray-300 rounded dark:bg-gray-600"
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 max-w-2xl mx-auto">
|
||||
<div class="flex flex-col md:flex-row items-start md:items-center space-y-4 md:space-y-0 md:space-x-6">
|
||||
<div class="w-full md:w-1/3">
|
||||
<label
|
||||
for="lumalabs_key"
|
||||
class="block text-sm font-semibold text-gray-700 dark:text-gray-200"
|
||||
>
|
||||
Lumalabs Key
|
||||
</label>
|
||||
</div>
|
||||
<div class="w-full md:w-2/3">
|
||||
<div class="relative">
|
||||
<input
|
||||
type="text"
|
||||
id="lumalabs_key"
|
||||
required
|
||||
v-model="configFile.lumalabs_key"
|
||||
@change="settingsChanged=true"
|
||||
class="w-full px-4 py-2.5 rounded-lg border border-gray-300 dark:border-gray-600
|
||||
bg-gray-50 dark:bg-gray-700
|
||||
text-gray-900 dark:text-white
|
||||
focus:ring-2 focus:ring-blue-500 focus:border-transparent
|
||||
transition duration-150 ease-in-out"
|
||||
placeholder="Enter your Lumalabs API key"
|
||||
>
|
||||
<div class="absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<svg class="h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
Your API key will be securely stored
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Card>
|
||||
<Card title="Misc" :is_shrunk="true" :is_subcard="true" class="pb-2 m-2">
|
||||
|
Loading…
x
Reference in New Issue
Block a user