Added wip plugin store

- Added plugin store snippet
- Added plugin list sync functions
- Work in progress install / uninstall plugin function
This commit is contained in:
Toby Chui
2025-04-22 07:15:30 +08:00
parent 36c2c9a00e
commit 6750c7fe3d
10 changed files with 469 additions and 3 deletions

View File

@ -185,6 +185,8 @@
</tbody>
</table>
<br>
<button class="ui violet button" onclick="openPluginStore();"><i class="cart arrow down icon"></i>Get More Plugins!</button>
</div>
<script>
@ -563,6 +565,11 @@ function getPluginInfo(pluginId, btn){
showSideWrapper("snippet/pluginInfo.html?t=" + Date.now() + "#" + payload);
}
function openPluginStore(){
//Open plugin store in extended mode
showSideWrapper("snippet/pluginstore.html?t=" + Date.now(), true);
}
</script>

View File

@ -197,6 +197,12 @@ body.darkTheme .menubar{
max-width: calc(80% - 1em);
}
@media screen and (max-width: 478px) {
.sideWrapper.extendedMode {
max-width: calc(100% - 1em);
}
}
.sideWrapper .content{
height: 100%;
width: 100%;

View File

@ -0,0 +1,258 @@
<!DOCTYPE html>
<html>
<head>
<!-- Notes: This should be open in its original path-->
<meta charset="utf-8">
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<title>Plugin Store</title>
<link rel="stylesheet" href="../script/semantic/semantic.min.css">
<script src="../script/jquery-3.6.0.min.js"></script>
<script src="../script/semantic/semantic.min.js"></script>
<script src="../script/utils.js"></script>
<style>
#pluginList{
padding: 1em;
border: 1px solid #ccc;
height: 500px;
overflow-y: scroll;
}
body.darkTheme #pluginList .header{
color: #fff;
}
.installablePlugin{
position: relative;
}
.installablePlugin .action{
position: absolute;
top: 0.4em;
right: 0.4em;
}
@media screen and (max-width: 768px) {
#pluginList .item .image {
display: none;
}
}
</style>
</head>
<body>
<link rel="stylesheet" href="../darktheme.css">
<script src="../script/darktheme.js"></script>
<br>
<div class="ui container">
<div class="ui fluid search">
<div class="ui fluid icon input">
<input id="searchInput" class="prompt" type="text" placeholder="Search plugins">
<i class="search icon"></i>
</div>
</div>
<div class="ui divided items" id="pluginList">
</div>
<button class="ui basic button" onclick="forceResyncPlugins();"><i class="ui green refresh icon"></i> Update Plugin List</button>
<div class="ui divider"></div>
<div class="ui basic segment advanceoptions">
<div class="ui accordion advanceSettings">
<div class="title">
<i class="dropdown icon"></i>
Advance Settings
</div>
<div class="content">
<p>Plugin Store URLs</p>
<div class="ui form">
<div class="field">
<textarea id="pluginStoreURLs" rows="5"></textarea>
<label>Enter plugin store URLs, separating each URL with a new line</label>
</div>
<button class="ui basic button" onclick="savePluginStoreURLs()">
<i class="ui green save icon"></i>Save
</button>
</div>
</div>
</div>
</div>
<div class="ui divider"></div>
<div class="field" >
<button class="ui basic button" style="float: right;" onclick="closeThisWrapper();">Close</button>
</div>
<br><br><br><br>
</div>
<script>
let availablePlugins = [];
let installedPlugins = [];
$(".accordion").accordion();
function initStoreList(){
$.get("/api/plugins/list", function(data) {
if (data.error != undefined) {
parent.msgbox(data.error, false);
return;
}else{
installedPlugins = data || [];
console.log(installedPlugins);
}
$.cjax({
url: '/api/plugins/store/list',
type: 'GET',
success: function(data) {
if (data.error != undefined) {
parent.msgbox(data.error, false);
}else{
availablePlugins = data || [];
populatePluginList(availablePlugins);
}
}
});
});
}
initStoreList();
/* Plugin Search */
function searchPlugins() {
const query = document.getElementById('searchInput').value.toLowerCase();
const items = document.querySelectorAll('#pluginList .item');
if (query.trim() === '') {
items.forEach(item => {
item.style.display = '';
});
return;
}
items.forEach(item => {
const name = item.querySelector('.header').textContent.toLowerCase();
const description = item.querySelector('.description p').textContent.toLowerCase();
const author = item.querySelector('.meta span:nth-child(2)').textContent.toLowerCase();
const id = item.querySelector('.extra button').getAttribute('onclick').match(/'(.*?)'/)[1].toLowerCase();
if (name.includes(query) || description.includes(query) || author.includes(query) || id.includes(query)) {
item.style.display = '';
} else {
item.style.display = 'none';
}
});
}
//Bind search function to input field and Enter key
document.getElementById('searchInput').addEventListener('input', searchPlugins);
document.getElementById('searchInput').addEventListener('keydown', function(event) {
if (event.key === 'Enter') {
searchPlugins();
}
});
function forceResyncPlugins() {
$.cjax({
url: '/api/plugins/store/resync',
type: 'POST',
success: function(data) {
if (data.error != undefined) {
parent.msgbox(data.error, false);
} else {
parent.msgbox("Plugin list updated successfully", true);
initStoreList();
}
}
});
}
/* Plugin Store */
function populatePluginList(plugins) {
const pluginList = document.getElementById('pluginList');
pluginList.innerHTML = ''; // Clear existing items
plugins.forEach(plugin => {
console.log(plugin);
let thisPluginIsInstalled = false;
installedPlugins.forEach(installedPlugin => {
if (installedPlugin.Spec.id == plugin.PluginIntroSpect.id) {
thisPluginIsInstalled = true;
}
});
const item = `
<div class="item installablePlugin" plugin_id="${plugin.PluginIntroSpect.id}">
<div class="ui tiny image">
<img src="${plugin.IconPath}" alt="${plugin.PluginIntroSpect.name}">
</div>
<div class="content">
<div class="header">${plugin.PluginIntroSpect.name}</div>
<div class="meta">
<span>Version: ${plugin.PluginIntroSpect.version_major}.${plugin.PluginIntroSpect.version_minor}.${plugin.PluginIntroSpect.version_patch}</span>
<span>${plugin.PluginIntroSpect.author}</span>
<span><a href="${plugin.PluginIntroSpect.url}">Website</a></span>
</div>
<div class="description">
<p>${plugin.PluginIntroSpect.description}</p>
</div>
<div class="action">
${thisPluginIsInstalled
? `<button class="ui basic circular red button" onclick="uninstallPlugin('${plugin.PluginIntroSpect.id}')"><i class="ui trash icon"></i> Remove</button>`
: `<button class="ui basic circular button" onclick="installPlugin('${plugin.PluginIntroSpect.id}')"><i class="ui download icon"></i> Install</button>`}
</div>
</div>
</div>
`;
$('#pluginList').append(item);
});
}
/* Plugin Actions */
function installPlugin(pluginId) {
$.cjax({
url: '/api/plugins/store/install',
type: 'POST',
data: { pluginId },
success: function(data) {
if (data.error != undefined) {
parent.msgbox(data.error, false);
} else {
parent.msgbox("Plugin installed successfully", true);
initStoreList();
}
}
});
}
function uninstallPlugin(pluginId) {
$.cjax({
url: '/api/plugins/store/uninstall',
type: 'POST',
data: { pluginId },
success: function(data) {
if (data.error != undefined) {
parent.msgbox(data.error, false);
} else {
parent.msgbox("Plugin uninstalled successfully", true);
initStoreList();
}
}
});
}
function closeThisWrapper(){
parent.hideSideWrapper(true);
}
/* Advanced Options */
function savePluginManagerURLs() {
const urls = document.getElementById('pluginStoreURLs').value.split('\n').map(url => url.trim()).filter(url => url !== '');
console.log('Saving URLs:', urls);
// Add your logic to save the URLs here, e.g., send them to the server
$.cjax({
url: '/api/plugins/store/saveURLs',
type: 'POST',
data: { urls },
success: function(data) {
if (data.error != undefined) {
parent.msgbox(data.error, false);
} else {
parent.msgbox("URLs saved successfully", true);
}
}
});
}
</script>
</body>
</html>