mirror of
https://github.com/ONLYOFFICE/onlyoffice.github.io.git
synced 2026-04-07 14:04:30 +08:00
Add customProviders modal
This commit is contained in:
55
sdkjs-plugins/content/ai/customProviders.html
Normal file
55
sdkjs-plugins/content/ai/customProviders.html
Normal file
@ -0,0 +1,55 @@
|
||||
<!--
|
||||
(c) Copyright Ascensio System SIA 2020
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>Custom providers</title>
|
||||
|
||||
<script type="text/javascript" src="https://onlyoffice.github.io/sdkjs-plugins/v1/plugins.js"></script>
|
||||
<script type="text/javascript" src="https://onlyoffice.github.io/sdkjs-plugins/v1/plugins-ui.js"></script>
|
||||
<script type="text/javascript" src="components/Tooltip/script.js"></script>
|
||||
<script type="text/javascript" src="components/ListView/script.js"></script>
|
||||
<link rel="stylesheet" href="https://onlyoffice.github.io/sdkjs-plugins/v1/plugins.css">
|
||||
<link rel="stylesheet" href="./resources/styles/common.css">
|
||||
<link rel="stylesheet" href="./resources/styles/customProviders.css">
|
||||
<link rel="stylesheet" href="components/Tooltip/style.css">
|
||||
<link rel="stylesheet" href="components/ListView/style.css">
|
||||
</head>
|
||||
<body class="noselect">
|
||||
<div id="label-row">
|
||||
<label class="i18n">Connected custom providers</label>
|
||||
<img id="alert-icon" src="resources/icons/error-small/error.png"/>
|
||||
</div>
|
||||
<div id="list-row">
|
||||
<div id="providers-list" class="empty">
|
||||
<!-- Dynamic render items -->
|
||||
</div>
|
||||
<div id="buttons-block">
|
||||
<button id="add-btn" class="btn-text-default">
|
||||
<img src="resources/icons/light/btn-zoomup.png" class="icon"/>
|
||||
</button>
|
||||
<button id="delete-btn" class="btn-text-default" disabled>
|
||||
<img src="resources/icons/light/btn-remove.png" class="icon"/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<label id="error-label" class="hide"></label>
|
||||
<input id="file-input" type="file" multiple accept=".js"/>
|
||||
<script type="text/javascript" src="scripts/customProviders.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,90 @@
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#label-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
#label-row label {
|
||||
font-weight: bold;
|
||||
}
|
||||
#alert-icon {
|
||||
cursor: pointer;
|
||||
margin-left: 4px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
opacity: 0.6;
|
||||
filter: grayscale(1);
|
||||
}
|
||||
|
||||
#alert-inner-popover {
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
#list-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
height: calc(100% - (20px + 4px) - (12px + 4px));
|
||||
}
|
||||
|
||||
#providers-list {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
#providers-list.empty {
|
||||
cursor: pointer;
|
||||
}
|
||||
#providers-list.empty:hover {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
#providers-list.dragged {
|
||||
opacity: 0.75;
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
#providers-list .item {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#buttons-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#buttons-block button {
|
||||
padding: 0;
|
||||
height: auto;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#buttons-block button img {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
#buttons-block button:not(:first-child) {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
#file-input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#error-label {
|
||||
height: 12px;
|
||||
color: #F62211;
|
||||
margin-top: 4px;
|
||||
transition: 0.15s opacity;
|
||||
}
|
||||
#error-label.hide {
|
||||
opacity: 0;
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
let settingsWindow = null;
|
||||
let aiModelsListWindow = null;
|
||||
let aiModelEditWindow = null;
|
||||
let customProvidersWindow = null;
|
||||
let summarizationWindow = null;
|
||||
|
||||
let initCounter = 0;
|
||||
@ -39,6 +40,9 @@ window.Asc.plugin.button = function(id, windowId) {
|
||||
aiModelEditWindow.close();
|
||||
aiModelEditWindow = null;
|
||||
}
|
||||
} else if (customProvidersWindow && windowId === customProvidersWindow.id) {
|
||||
customProvidersWindow.close();
|
||||
customProvidersWindow = null;
|
||||
} else {
|
||||
window.Asc.plugin.executeMethod("CloseWindow", [windowId]);
|
||||
}
|
||||
@ -51,6 +55,7 @@ window.Asc.plugin.onThemeChanged = function(theme) {
|
||||
aiModelsListWindow && aiModelsListWindow.command('onThemeChanged', theme);
|
||||
aiModelEditWindow && aiModelEditWindow.command('onThemeChanged', theme);
|
||||
summarizationWindow && summarizationWindow.command('onThemeChanged', theme);
|
||||
customProvidersWindow && customProvidersWindow.command('onThemeChanged', theme);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -144,29 +149,73 @@ function onOpenEditModal(data) {
|
||||
],
|
||||
isModal : true,
|
||||
EditorsSupport : ["word", "slide", "cell"],
|
||||
size : [320, 330]
|
||||
size : [320, 370]
|
||||
};
|
||||
|
||||
aiModelEditWindow = new window.Asc.PluginWindow();
|
||||
aiModelEditWindow.attachEvent("onChangeModel", function(model){
|
||||
AI.Storage.addModel(model);
|
||||
aiModelEditWindow.close();
|
||||
aiModelEditWindow = null;
|
||||
});
|
||||
aiModelEditWindow.attachEvent("onGetModels", async function(provider){
|
||||
let models = await AI.getModels(provider);
|
||||
aiModelEditWindow && aiModelEditWindow.command("onGetModels", models);
|
||||
});
|
||||
|
||||
aiModelEditWindow.attachEvent("onInit", function() {
|
||||
aiModelEditWindow.command('onModelInfo', {
|
||||
model : data.model ? AI.Storage.getModelByName(data.model.name) : null,
|
||||
providers : AI.serializeProviders()
|
||||
if (!aiModelEditWindow) {
|
||||
aiModelEditWindow = new window.Asc.PluginWindow();
|
||||
aiModelEditWindow.attachEvent("onChangeModel", function(model){
|
||||
AI.Storage.addModel(model);
|
||||
aiModelEditWindow.close();
|
||||
aiModelEditWindow = null;
|
||||
});
|
||||
});
|
||||
aiModelEditWindow.attachEvent("onGetModels", async function(provider){
|
||||
let models = await AI.getModels(provider);
|
||||
aiModelEditWindow && aiModelEditWindow.command("onGetModels", models);
|
||||
});
|
||||
|
||||
aiModelEditWindow.attachEvent("onInit", function() {
|
||||
aiModelEditWindow.command('onModelInfo', {
|
||||
model : data.model ? AI.Storage.getModelByName(data.model.name) : null,
|
||||
providers : AI.serializeProviders()
|
||||
});
|
||||
});
|
||||
aiModelEditWindow.attachEvent('onOpenCustomProvidersModal', onOpenCustomProvidersModal);
|
||||
}
|
||||
aiModelEditWindow.show(variation);
|
||||
}
|
||||
|
||||
/**
|
||||
* CUSTOM PROVIDERS WINDOW
|
||||
*/
|
||||
function onOpenCustomProvidersModal() {
|
||||
let variation = {
|
||||
url : 'customProviders.html',
|
||||
description : window.Asc.plugin.tr('Custom providers'),
|
||||
isVisual : true,
|
||||
buttons : [
|
||||
{ text: window.Asc.plugin.tr('Back'), primary: false },
|
||||
],
|
||||
isModal : true,
|
||||
EditorsSupport : ["word", "slide", "cell"],
|
||||
size : [350, 222]
|
||||
};
|
||||
|
||||
if (!customProvidersWindow) {
|
||||
customProvidersWindow = new window.Asc.PluginWindow();
|
||||
customProvidersWindow.attachEvent("onInit", function() {
|
||||
//TODO: Add set custom providers
|
||||
customProvidersWindow.command('onSetCustomProvider', []);
|
||||
});
|
||||
customProvidersWindow.attachEvent("onAddCustomProvider", function(item) {
|
||||
console.log('Add custom provider', item);
|
||||
|
||||
// If the provider addition is successful, then call "onAddCustomProvider"
|
||||
// Else call "onErrorCustomProvider"
|
||||
let isError = false;
|
||||
if(isError) {
|
||||
customProvidersWindow.command('onErrorCustomProvider');
|
||||
} else {
|
||||
customProvidersWindow.command('onAddCustomProvider', item);
|
||||
}
|
||||
});
|
||||
customProvidersWindow.attachEvent("onDeleteCustomProvider", function(item) {
|
||||
console.log('Delete custom provider', item);
|
||||
});
|
||||
}
|
||||
customProvidersWindow.show(variation);
|
||||
}
|
||||
|
||||
/**
|
||||
* SUMMARIZATION WINDOW
|
||||
*/
|
||||
|
||||
190
sdkjs-plugins/content/ai/scripts/customProviders.js
Normal file
190
sdkjs-plugins/content/ai/scripts/customProviders.js
Normal file
@ -0,0 +1,190 @@
|
||||
let providersList = new ListView(document.getElementById('providers-list'), {
|
||||
emptyText: 'The list is empty, press + to add the file',
|
||||
renderItem: function(item) {
|
||||
var itemEl = document.createElement('div');
|
||||
itemEl.classList.add('item');
|
||||
itemEl.innerText = item.name;
|
||||
return itemEl;
|
||||
}
|
||||
});
|
||||
let scrollbarList = new PerfectScrollbar("#providers-list", {});
|
||||
providersList.on('select', function() {
|
||||
deleteBtnEl.removeAttribute('disabled');
|
||||
});
|
||||
providersList.on('deselect', function() {
|
||||
deleteBtnEl.setAttribute('disabled', true);
|
||||
});
|
||||
providersList.on('set', function() {
|
||||
if(providersList.getList().length > 0) {
|
||||
providersList.$el.classList.remove('empty');
|
||||
}
|
||||
});
|
||||
providersList.on('add', function() {
|
||||
if(providersList.getList().length > 0) {
|
||||
providersList.$el.classList.remove('empty');
|
||||
}
|
||||
});
|
||||
providersList.on('delete', function() {
|
||||
if(providersList.getList().length == 0) {
|
||||
providersList.$el.classList.add('empty');
|
||||
}
|
||||
});
|
||||
|
||||
providersList.$el.addEventListener('click', function() {
|
||||
if(providersList.getList().length == 0) {
|
||||
fileInputEl.click();
|
||||
}
|
||||
});
|
||||
providersList.$el.addEventListener('dragover', function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
providersList.$el.classList.add('dragged');
|
||||
});
|
||||
providersList.$el.addEventListener('dragleave', function() {
|
||||
providersList.$el.classList.remove('dragged');
|
||||
});
|
||||
providersList.$el.addEventListener('drop', function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
handlerChangeFileInput(e.dataTransfer.files);
|
||||
providersList.$el.classList.remove('dragged');
|
||||
});
|
||||
|
||||
|
||||
let addBtnEl = document.getElementById('add-btn');
|
||||
addBtnEl.addEventListener('click', function() {
|
||||
fileInputEl.click();
|
||||
});
|
||||
|
||||
let deleteBtnEl = document.getElementById('delete-btn');
|
||||
deleteBtnEl.addEventListener('click', function() {
|
||||
if(providersList.getSelected()) {
|
||||
window.Asc.plugin.sendToPlugin("onDeleteCustomProvider", providersList.getSelected());
|
||||
providersList.delete(providersList.getSelected());
|
||||
}
|
||||
});
|
||||
|
||||
let fileInputEl = document.getElementById('file-input');
|
||||
fileInputEl.addEventListener("change", function (event) {
|
||||
handlerChangeFileInput(event.target.files);
|
||||
});
|
||||
|
||||
let errorLabelEl = document.getElementById('error-label');
|
||||
let errorLabelTimeout = null;
|
||||
|
||||
// TODO: Change path for template file
|
||||
let templateFilePath = document.currentScript.src + '/../engine/providers/provider.js';
|
||||
|
||||
window.Asc.plugin.init = function() {
|
||||
window.Asc.plugin.sendToPlugin("onInit");
|
||||
window.Asc.plugin.attachEvent("onSetCustomProvider", function(list) {
|
||||
providersList.set(list);
|
||||
});
|
||||
window.Asc.plugin.attachEvent("onAddCustomProvider", function(item) {
|
||||
providersList.add({name: item.name});
|
||||
});
|
||||
window.Asc.plugin.attachEvent("onErrorCustomProvider", function(item) {
|
||||
showErrorLabel('Error adding provider from file, please try again');
|
||||
});
|
||||
window.Asc.plugin.attachEvent("onThemeChanged", onThemeChanged);
|
||||
}
|
||||
window.Asc.plugin.onThemeChanged = onThemeChanged;
|
||||
|
||||
window.Asc.plugin.onTranslate = function () {
|
||||
let elements = document.querySelectorAll('.i18n');
|
||||
elements.forEach(function(element) {
|
||||
element.innerText = window.Asc.plugin.tr(element.innerText);
|
||||
});
|
||||
|
||||
providersList.setEmptyText(window.Asc.plugin.tr(providersList.options.emptyText));
|
||||
|
||||
new Tooltip(document.getElementById('alert-icon'), {
|
||||
renderInner: function() {
|
||||
let innerEl = document.createElement("div");
|
||||
innerEl.id = 'alert-inner-popover';
|
||||
|
||||
let textEl = document.createElement("div");
|
||||
textEl.innerText = window.Asc.plugin.tr('Enter the configuration for the AI model API in JS format. Provide the model name, endpoint URLs, and headers.');
|
||||
|
||||
let linkEl = document.createElement("a");
|
||||
linkEl.id = 'popover-link';
|
||||
linkEl.href = templateFilePath;
|
||||
linkEl.download = 'providerTemplate.js';
|
||||
linkEl.innerText = window.Asc.plugin.tr('Download template');
|
||||
linkEl.addEventListener('click', function() {
|
||||
console.log('Download template');
|
||||
});
|
||||
|
||||
innerEl.appendChild(textEl);
|
||||
innerEl.appendChild(linkEl);
|
||||
return innerEl;
|
||||
},
|
||||
xAnchor: 'left',
|
||||
align: 'left',
|
||||
yOffset: 4,
|
||||
width: 150,
|
||||
hasShadow: true,
|
||||
keepAliveOnHover: true,
|
||||
delay: 200,
|
||||
hideDelay: 200
|
||||
});
|
||||
};
|
||||
|
||||
function handlerChangeFileInput(files) {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
let file = files[i];
|
||||
if (file.name.lastIndexOf(".js") == file.name.length - 3) {
|
||||
let reader = new FileReader();
|
||||
reader.readAsText(file);
|
||||
reader.onload = function(e) {
|
||||
let fileContent = e.target.result;
|
||||
window.Asc.plugin.sendToPlugin("onAddCustomProvider", {
|
||||
name: file.name,
|
||||
content: fileContent
|
||||
});
|
||||
};
|
||||
reader.onerror = function(e) {
|
||||
showErrorLabel('Error adding provider from file, please try again');
|
||||
};
|
||||
} else {
|
||||
showErrorLabel('Invalid file format, please upload the .js file');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showErrorLabel(text) {
|
||||
clearTimeout(errorLabelTimeout);
|
||||
errorLabelEl.innerText = window.Asc.plugin.tr(text);
|
||||
errorLabelEl.classList.remove('hide');
|
||||
errorLabelTimeout = setTimeout(function() {
|
||||
errorLabelEl.classList.add('hide');
|
||||
}, 10 * 1000);
|
||||
}
|
||||
|
||||
function hideErrorLabel() {
|
||||
clearTimeout(errorLabelTimeout);
|
||||
errorLabelEl.classList.add('hide');
|
||||
}
|
||||
|
||||
function onThemeChanged(theme) {
|
||||
window.Asc.plugin.onThemeChangedBase(theme);
|
||||
themeType = theme.type || 'light';
|
||||
|
||||
let classes = document.body.className.split(' ');
|
||||
classes.forEach(function(className) {
|
||||
if (className.indexOf('theme-') != -1) {
|
||||
document.body.classList.remove(className);
|
||||
}
|
||||
});
|
||||
document.body.classList.add(theme.name);
|
||||
document.body.classList.add('theme-type-' + themeType);
|
||||
|
||||
let btnIcons = document.getElementsByClassName('icon');
|
||||
for (let i = 0; i < btnIcons.length; i++) {
|
||||
let icon = btnIcons[i];
|
||||
let src = icon.getAttribute('src');
|
||||
let newSrc = src.replace(/(icons\/)([^\/]+)(\/)/, '$1' + themeType + '$3');
|
||||
icon.setAttribute('src', newSrc);
|
||||
}
|
||||
}
|
||||
@ -95,5 +95,14 @@
|
||||
"Chatbot": "Chatbot",
|
||||
"Ask AI a question about something...": "Ask AI a question about something...",
|
||||
|
||||
"This field is required": "This field is required"
|
||||
"This field is required": "This field is required",
|
||||
|
||||
"Custom providers": "Custom providers",
|
||||
"Back": "Back",
|
||||
"Connected custom providers": "Connected custom providers",
|
||||
"The list is empty, press + to add the file": "The list is empty, press + to add the file",
|
||||
"Enter the configuration for the AI model API in JS format. Provide the model name, endpoint URLs, and headers.": "Enter the configuration for the AI model API in JS format. Provide the model name, endpoint URLs, and headers.",
|
||||
"Download template": "Download template",
|
||||
"Invalid file format, please upload the .js file": "Invalid file format, please upload the .js file",
|
||||
"Error adding provider from file, please try again": "Error adding provider from file, please try again"
|
||||
}
|
||||
Reference in New Issue
Block a user