add single plugin page

This commit is contained in:
Muhammad Labeeb 2025-02-14 19:13:11 +05:00 committed by Régis Behmo
parent a7ebb30ae1
commit 018784e113
11 changed files with 281 additions and 91 deletions

View File

@ -57,7 +57,8 @@ async def plugin_store() -> str:
"name": p.name,
"url": p.url,
"index": p.index,
"description": markdown(p.description),
"author": p.author.split('<')[0].strip(),
"description": markdown(p.description.replace("\n", " ")),
"is_installed": p.name in installed_plugins,
}
for p in tutorclient.Client.plugins_in_store()
@ -105,10 +106,16 @@ async def installed_plugins() -> str:
async def plugin(name: str) -> str:
# TODO check that plugin exists
is_enabled = name in tutorclient.Client.enabled_plugins()
is_installed = name in tutorclient.Client.installed_plugins()
author = next((p.author.split('<')[0].strip() for p in tutorclient.Client.plugins_in_store() if p.name == name), "")
description = next((markdown(p.description) for p in tutorclient.Client.plugins_in_store() if p.name == name), "")
return await render_template(
"plugin.html",
plugin_name=name,
is_enabled=is_enabled,
is_installed=is_installed,
author_name=author,
plugin_description=description,
plugin_config_unique=tutorclient.Client.plugin_config_unique(name),
plugin_config_defaults=tutorclient.Client.plugin_config_defaults(name),
user_config=tutorclient.Project.get_user_config(),

View File

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.2806 7.21937C14.3504 7.28903 14.4057 7.37175 14.4434 7.46279C14.4812 7.55384 14.5006 7.65144 14.5006 7.75C14.5006 7.84856 14.4812 7.94616 14.4434 8.03721C14.4057 8.12825 14.3504 8.21097 14.2806 8.28063L9.03063 13.5306C8.96097 13.6004 8.87826 13.6557 8.78721 13.6934C8.69616 13.7312 8.59857 13.7506 8.5 13.7506C8.40144 13.7506 8.30385 13.7312 8.2128 13.6934C8.12175 13.6557 8.03903 13.6004 7.96938 13.5306L5.71938 11.2806C5.57865 11.1399 5.49959 10.949 5.49959 10.75C5.49959 10.551 5.57865 10.3601 5.71938 10.2194C5.86011 10.0786 6.05098 9.99958 6.25 9.99958C6.44903 9.99958 6.6399 10.0786 6.78063 10.2194L8.5 11.9397L13.2194 7.21937C13.289 7.14964 13.3718 7.09432 13.4628 7.05658C13.5538 7.01884 13.6514 6.99941 13.75 6.99941C13.8486 6.99941 13.9462 7.01884 14.0372 7.05658C14.1283 7.09432 14.211 7.14964 14.2806 7.21937ZM19.75 10C19.75 11.9284 19.1782 13.8134 18.1068 15.4168C17.0355 17.0202 15.5127 18.2699 13.7312 19.0078C11.9496 19.7458 9.98919 19.9389 8.09787 19.5627C6.20656 19.1865 4.46927 18.2579 3.10571 16.8943C1.74215 15.5307 0.813554 13.7934 0.437348 11.9021C0.061142 10.0108 0.254224 8.05042 0.992179 6.26884C1.73013 4.48726 2.97982 2.96452 4.58319 1.89317C6.18657 0.821828 8.07164 0.25 10 0.25C12.585 0.25273 15.0634 1.28084 16.8913 3.10872C18.7192 4.93661 19.7473 7.41498 19.75 10ZM18.25 10C18.25 8.3683 17.7661 6.77325 16.8596 5.41655C15.9531 4.05984 14.6646 3.00242 13.1571 2.37799C11.6497 1.75357 9.99085 1.59019 8.39051 1.90852C6.79017 2.22685 5.32016 3.01259 4.16637 4.16637C3.01259 5.32015 2.22685 6.79016 1.90853 8.3905C1.5902 9.99085 1.75358 11.6496 2.378 13.1571C3.00242 14.6646 4.05984 15.9531 5.41655 16.8596C6.77326 17.7661 8.36831 18.25 10 18.25C12.1873 18.2475 14.2843 17.3775 15.8309 15.8309C17.3775 14.2843 18.2475 12.1873 18.25 10Z" fill="#009951"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -12,7 +12,7 @@ document.getElementById("search-input").addEventListener("input", function () {
});
});
let open = document.querySelectorAll(".open_modal_button");
let open = document.querySelectorAll(".open-modal-button");
const modal_container = document.getElementById("modal_container");
let close = document.querySelectorAll(".close-modal-button");

View File

@ -6,6 +6,7 @@ $black: #181d27;
$white: #e6f3ff;
$blue: #1570ef;
$light-blue: #2e90fa;
$green: #009951;
$font-family_1: Arial;
* {
@ -121,14 +122,23 @@ body {
font-weight: 600;
}
.page-description {
font-size: 16px;
font-size: 1.25em;
color: $gray;
}
}
.launch-button {
.page-button {
display: flex;
justify-content: center;
.installed {
display: flex;
align-items: center;
width: 6em;
justify-content: space-around;
color: $green;
font-size: 1em;
}
button {
width: 9em;
height: 2.5em;
@ -212,65 +222,83 @@ body {
.details {
width: 30em;
}
.form-switch {
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
}
.store-plugins {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: 0.4s;
transition: 0.4s;
&:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: 0.4s;
transition: 0.4s;
.plugin {
border: 1px solid $light-gray;
border-radius: 1em;
display: flex;
flex-direction: column;
width: 26em;
margin-bottom: 1.5em;
padding: 1em;
justify-content: space-between;
.header {
display: flex;
flex-direction: row;
justify-content: space-between;
margin: 0px;
height: 4em;
.title {
display: flex;
flex-direction: column;
text-transform: capitalize;
justify-content: space-around;
.name {
a {
color: $blue;
text-decoration: none;
font-size: 1.75em;
&:visited {
text-decoration: none;
}
}
}
.slider.round {
border-radius: 34px;
.author {
font-size: 1em;
color: $gray;
}
}
.slider.round:before {
border-radius: 50%;
}
input {
opacity: 0;
width: 0;
height: 0;
&:checked + .slider {
background-color: #2196f3;
}
&:focus + .slider {
box-shadow: 0 0 1px #2196f3;
}
&:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
.status {
img {
width: 1.5em;
}
}
}
.body {
p {
width: 100%;
height: auto;
display: -webkit-box;
-webkit-line-clamp: 3; // Limit to 3 lines
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
}
}
}
}
.status {
display: flex;
.status-text {
width: 25em;
}
.switch-text {
margin-left: 1em;
}
}
}
@ -367,3 +395,63 @@ body {
}
}
}
.form-switch {
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: 0.4s;
transition: 0.4s;
&:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: 0.4s;
transition: 0.4s;
}
}
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
input {
opacity: 0;
width: 0;
height: 0;
&:checked + .slider {
background-color: #2196f3;
}
&:focus + .slider {
box-shadow: 0 0 1px #2196f3;
}
&:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
}
}
}

View File

@ -6,7 +6,7 @@
<div class="page-title">{% block page_title %}{% endblock %}</div>
<div class="page-description">{% block page_description %}{% endblock %}</div>
</div>
<div class="launch-button">
<div class="page-button">
{% block page_button %}{% endblock %}
</div>
</div>

View File

@ -1,5 +1,7 @@
{% extends "base_header.html" %}
{% from 'switch_macro.html' import switch %}
{% block page_title %}
My Plugins
{% endblock %}
@ -9,7 +11,7 @@ View all your installed plugins in one place.
{% endblock %}
{% block page_button %}
<button class="modal-button open_modal_button" type="button">Local Launch</button>
<button class="modal-button open-modal-button" type="button">Local Launch</button>
{% endblock %}
{% block model_icon %}
@ -43,14 +45,7 @@ Running local launch will allow all changes to plugins to take effect. This coul
<div class="description">{{ plugin.description|safe }}</div>
</div>
<div class="form-switch">
<label class="switch">
<form method="POST" action="{{ url_for('plugin_toggle', name=plugin.name) }}">
<input type="checkbox" name="checked" onchange="this.form.submit()" {% if plugin.is_enabled %} checked {% endif %} />
<span class="slider round"></span>
</form>
</label>
</div>
{{ switch(plugin.name, plugin.is_enabled)}}
</div>
{% endfor %}
</div>

View File

@ -1,18 +1,33 @@
{% extends "index.html" %}
{% extends "plugin_header.html" %}
{% block workspace_header %}
{{ plugin_name }}
{% endblock %}
{% set sidebar_active_tab = "" %}
{% set sidebar_active_tab = "my-plugins" %}
{% from 'switch_macro.html' import switch %}
{% block workspace_content %}
Enabled: <form method="POST" action="{{ url_for('plugin_toggle', name=plugin_name) }}">
<input type="checkbox" name="enabled" onchange="this.form.submit()" {% if is_enabled %}checked{% endif %} />
</form>
<div class="status">
{% if is_installed %}
<div class="status-text">
Status
</div>
{{ switch(plugin_name, is_enabled) }}
<div class="switch-text">
<div class="title">
{% if is_enabled %} Enabled {% else %} Disabled {% endif %}
</div>
<div class="description">
{% if not is_enabled %} Enable the plugin to edit parameters. {% endif %}
</div>
</div>
{% endif %}
</div>
{% if is_enabled %}
<h2>Configuration</h2>
<div class="parameter-info">
<h2>Plugin Parameters</h2>
<p>This plugin has default parameters. If you make any changes, save them and run a local launch to make the changes effective.</p>
</div>
<h3>Unique settings</h3>
{% if plugin_config_unique %}
@ -29,4 +44,5 @@ Enabled: <form method="POST" action="{{ url_for('plugin_toggle', name=plugin_nam
{% endif %}
{% endif %}
{% endblock %}

View File

@ -0,0 +1,34 @@
{% extends "index.html" %}
{% block workspace_header %}
<div class="header-address">
<span>Plugin Marketplace</span>
<span>/ {{ plugin_name }}</span>
</div>
<div class="header-bar">
<div class="info-container">
<div class="page-title">{{ plugin_name }}</div>
<div class="page-description">By {{ author_name }}</div>
</div>
<div class="page-button">
{% block page_button %}
{% if plugin_name in installed_plugins %}
<div class="installed">
<img src="{{ url_for('static', filename='/img/CheckCircle.svg')}}" alt="">
Installed
</div>
{% else %}
<form action="{{ url_for('plugin_install', name=plugin_name) }}" method="POST">
<button type="submit">Install</button>
</form>
{% endif %}
{% endblock %}
</div>
</div>
{% block plugin_description %}
<div>
{{ plugin_description | safe }}
</div>
{% endblock %}
{% endblock %}

View File

@ -8,26 +8,61 @@ Plugin Marketplace
View and install available plugins.
{% endblock %}
{% block page_button %}
<form action="{{ url_for('plugins_update') }}" method="POST">
<button class="modal-button" type="submit">Refresh</button>
</form>
{% endblock %}
{% set sidebar_active_tab = "plugin-marketplace" %}
{% block workspace_content %}
<form action="{{ url_for('plugins_update') }}" method="POST">
<button type="submit">Refresh</button>
</form>
<div class="store-plugins">
{% for plugin in plugins %}
<div class="plugin">
<div class="header">
<div class="title">
<div class="name">
<a href="{{ url_for('plugin', name=plugin.name) }}">{{ plugin.name }}</a>
</div>
<div class="author">
By {{ plugin.author }}
</div>
</div>
<div class="status">
{% if plugin.is_installed %}
<img src="{{ url_for('static', filename='/img/CheckCircle.svg') }}" alt="">
{% endif %}
</div>
</div>
<div class="body">
<!-- TODO is that actually safe? -->
{{ plugin.description|safe }}
</div>
<div class="footer">
<div class="meta">
{% for plugin in plugins %}
<h2>{{ plugin.name }}</h2>
{% if plugin.is_installed %}
<p>Installed: ✅ <form action="{{ url_for('plugin_upgrade', name=plugin.name) }}" method="POST"><button type="submit">Upgrade</button></form></p>
{% else %}
<p><form action="{{ url_for('plugin_upgrade', name=plugin.name) }}" method="POST"><button type="submit">Install</button></form></p>
{% endif %}
<p{{ plugin.short_description }}></p>
<!-- TODO is that actually safe? -->
<p>{{ plugin.description|safe }}</p>
<ul>
<li>url: {{ plugin.url }}</li>
<li>index: {{ plugin.index }}</li>
</ul>
{% endfor %}
</div>
<div class="plugin-button">
{% if plugin.is_installed %}
<form action="{{ url_for('plugin_upgrade', name=plugin.name) }}" method="POST">
<button type="submit">Upgrade</button>
</form>
{% else %}
<form action="{{ url_for('plugin_install', name=plugin.name) }}" method="POST">
<button type="submit">Install</button>
</form>
{% endif %}
</div>
</div>
<!-- <p{{ plugin.short_description }}></p>
<ul>
<li>url: {{ plugin.url }}</li>
<li>index: {{ plugin.index }}</li>
</ul> -->
</div>
{% endfor %}
</div>
{% endblock %}

View File

@ -0,0 +1,12 @@
{% macro switch(plugin_name, is_enabled) %}
<div class="switch">
<div class="form-switch">
<label class="switch">
<form method="POST" action="{{ url_for('plugin_toggle', name=plugin_name) }}">
<input type="checkbox" name="checked" onchange="this.form.submit()" {% if is_enabled %} checked {% endif %} />
<span class="slider round"></span>
</form>
</label>
</div>
</div>
{% endmacro %}