Merge pull request #2504 from SillyTavern/extension-list-info

Extensions & Assets UI updates - More info, tooltips & helpful guides
This commit is contained in:
Cohee 2024-07-10 21:47:09 +03:00 committed by GitHub
commit fb7e8e73ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 81 additions and 36 deletions

View File

@ -55,16 +55,11 @@
/* Flashing for highlighting animation */
@keyframes flash {
20%,
60%,
100% {
0%, 50%, 100% {
opacity: 1;
}
0%,
40%,
80% {
25%, 75% {
opacity: 0.2;
}
}

View File

@ -1,9 +0,0 @@
<div class="m-b-1">
Are you sure you want to connect to '{{url}}'?
</div>
<div class="flex-container justifyCenter">
<label class="checkbox_label" for="assets-remember">
<input type="checkbox" id="assets-remember">
Don't ask again for this URL
</label>
</div>

View File

@ -3,11 +3,11 @@ TODO:
*/
//const DEBUG_TONY_SAMA_FORK_MODE = true
import { getRequestHeaders, callPopup, processDroppedFiles, eventSource, event_types } from '../../../script.js';
import { getRequestHeaders, processDroppedFiles, eventSource, event_types } from '../../../script.js';
import { deleteExtension, extensionNames, getContext, installExtension, renderExtensionTemplateAsync } from '../../extensions.js';
import { POPUP_TYPE, callGenericPopup } from '../../popup.js';
import { POPUP_TYPE, Popup, callGenericPopup } from '../../popup.js';
import { executeSlashCommands } from '../../slash-commands.js';
import { getStringHash, isValidUrl } from '../../utils.js';
import { flashHighlight, getStringHash, isValidUrl } from '../../utils.js';
export { MODULE_NAME };
const MODULE_NAME = 'assets';
@ -219,6 +219,12 @@ function downloadAssetsList(url) {
$('#assets_menu').show();
})
.catch((error) => {
// Info hint if the user maybe... likely accidently was trying to install an extension and we wanna help guide them? uwu :3
const installButton = $('#third_party_extension_button');
flashHighlight(installButton, 10_000);
toastr.info('Click the flashing button at the top right corner of the menu.', 'Trying to install a custom extension?', { timeOut: 10_000 });
// Error logged after, to appear on top
console.error(error);
toastr.error('Problem with assets URL', DEBUG_PREFIX + 'Cannot get assets list');
$('#assets-connect-button').addClass('fa-plug-circle-exclamation');
@ -405,22 +411,31 @@ jQuery(async () => {
openCharacterBrowser(false);
});
const installHintButton = windowHtml.find('.assets-install-hint-link');
installHintButton.on('click', async function () {
const installButton = $('#third_party_extension_button');
flashHighlight(installButton, 5000);
toastr.info('Click the flashing button to install extensions.', 'How to install extensions?');
});
const connectButton = windowHtml.find('#assets-connect-button');
connectButton.on('click', async function () {
const url = String(assetsJsonUrl.val());
const url = DOMPurify.sanitize(String(assetsJsonUrl.val()));
const rememberKey = `Assets_SkipConfirm_${getStringHash(url)}`;
const skipConfirm = localStorage.getItem(rememberKey) === 'true';
const template = await renderExtensionTemplateAsync(MODULE_NAME, 'confirm', { url });
const confirmation = skipConfirm || await callPopup(template, 'confirm');
const confirmation = skipConfirm || await Popup.show.confirm('Loading Asset List', `<span>Are you sure you want to connect to the following url?</span><var>${url}</var>`, {
customInputs: [{ id: 'assets-remember', label: 'Don\'t ask again for this URL' }],
onClose: popup => {
if (popup.result) {
const rememberValue = popup.inputResults.get('assets-remember');
localStorage.setItem(rememberKey, String(rememberValue));
}
},
});
if (confirmation) {
try {
if (!skipConfirm) {
const rememberValue = Boolean($('#assets-remember').prop('checked'));
localStorage.setItem(rememberKey, String(rememberValue));
}
console.debug(DEBUG_PREFIX, 'Confimation, loading assets...');
downloadAssetsList(url);
connectButton.removeClass('fa-plug-circle-exclamation');

View File

@ -7,10 +7,18 @@
margin-left: 5px;
}
.assets-url-block {
display: flex;
flex-direction: column;
}
.assets-install-hint-link {
cursor: help;
}
.assets-connect-div {
display: flex;
flex-direction: row;
padding: 5px;
}
.assets-list-git {

View File

@ -5,10 +5,30 @@
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
</div>
<div class="inline-drawer-content">
<label for="assets-json-url-field" data-i18n="Assets URL">Assets URL</label>
<div class="assets-connect-div">
<input id="assets-json-url-field" class="text_pole widthUnset flex1">
<i id="assets-connect-button" class="menu_button fa-solid fa-plug-circle-exclamation fa-xl redOverlayGlow"></i>
<small>
<span data-i18n="Load a custom asset list or select">
Load a custom asset list or select
</span>
<a class="assets-install-hint-link" data-i18n="Install&nbsp;Extension">Install&nbsp;Extension</a>
<span data-i18n="to install 3rd party extensions.">
to install 3rd party extensions.
</span>
</small>
<div class="assets-url-block m-b-1 m-t-1">
<label for="assets-json-url-field" data-i18n="Assets URL">Assets URL</label>
<small title="Load a list of extensions & assets based on an asset list file.
The default Asset URL in this field points to the list of offical first party extensions and assets.
If you have a custom asset list, you can insert it here.
To install a single 3rd party extension, use the &quot;Install Extensions&quot; button on the top right.">
<span>Load an asset list</span>
<div class="fa-solid fa-circle-info opacity50p"></div>
</small>
<div class="assets-connect-div">
<input id="assets-json-url-field" class="text_pole widthUnset flex1">
<i id="assets-connect-button" class="menu_button fa-solid fa-plug-circle-exclamation fa-xl redOverlayGlow" title="Load Asset List" data-i18n="[title]Load Asset List"></i>
</div>
</div>
<div id="assets_filters" class="flex-container">
<select id="assets_type_select" class="text_pole flex1">

View File

@ -1571,13 +1571,29 @@ export function setValueByPath(obj, path, value) {
/**
* Flashes the given HTML element via CSS flash animation for a defined period
* @param {JQuery<HTMLElement>} element - The element to flash
* @param {number} timespan - A numer in milliseconds how the flash should last
* @param {number} timespan - A number in milliseconds how the flash should last (default is 2000ms. Multiples of 1000ms work best, as they end with the flash animation being at 100% opacity)
*/
export function flashHighlight(element, timespan = 2000) {
const flashDuration = 2000; // Duration of a single flash cycle in milliseconds
element.addClass('flash animated');
setTimeout(() => element.removeClass('flash animated'), timespan);
element.css('--animation-duration', `${flashDuration}ms`);
// Repeat the flash animation
const intervalId = setInterval(() => {
element.removeClass('flash animated');
void element[0].offsetWidth; // Trigger reflow to restart animation
element.addClass('flash animated');
}, flashDuration);
setTimeout(() => {
clearInterval(intervalId);
element.removeClass('flash animated');
element.css('--animation-duration', '');
}, timespan);
}
/**
* Checks if the given control has an animation applied to it
*

View File

@ -208,8 +208,8 @@ table.responsiveTable {
}
.animated {
-webkit-animation-duration: 3s !important;
animation-duration: 3s !important;
-webkit-animation-duration: var(--animation-duration, 3s) !important;
animation-duration: var(--animation-duration, 3s) !important;
-webkit-animation-fill-mode: both !important;
animation-fill-mode: both !important;
box-shadow: inset 0 0 5px var(--SmartThemeQuoteColor);