mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
@ -1120,7 +1120,7 @@ export function initRossMods() {
|
|||||||
const result = await Popup.show.confirm('Regenerate Message', 'Are you sure you want to regenerate the latest message?', {
|
const result = await Popup.show.confirm('Regenerate Message', 'Are you sure you want to regenerate the latest message?', {
|
||||||
customInputs: [{ id: 'regenerateWithCtrlEnter', label: 'Don\'t ask again' }],
|
customInputs: [{ id: 'regenerateWithCtrlEnter', label: 'Don\'t ask again' }],
|
||||||
onClose: (popup) => {
|
onClose: (popup) => {
|
||||||
regenerateWithCtrlEnter = popup.inputResults.get('regenerateWithCtrlEnter') ?? false;
|
regenerateWithCtrlEnter = Boolean(popup.inputResults.get('regenerateWithCtrlEnter') ?? false);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!result) {
|
if (!result) {
|
||||||
|
@ -1061,7 +1061,7 @@ async function getExtensionVersion(extensionName, abortSignal) {
|
|||||||
* @param {boolean} global Is the extension global?
|
* @param {boolean} global Is the extension global?
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
export async function installExtension(url, global) {
|
export async function installExtension(url, global, branch = '') {
|
||||||
console.debug('Extension installation started', url);
|
console.debug('Extension installation started', url);
|
||||||
|
|
||||||
toastr.info(t`Please wait...`, t`Installing extension`);
|
toastr.info(t`Please wait...`, t`Installing extension`);
|
||||||
@ -1072,6 +1072,7 @@ export async function installExtension(url, global) {
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
url,
|
url,
|
||||||
global,
|
global,
|
||||||
|
branch,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1406,9 +1407,17 @@ export async function openThirdPartyExtensionMenu(suggestUrl = '') {
|
|||||||
await popup.complete(POPUP_RESULT.AFFIRMATIVE);
|
await popup.complete(POPUP_RESULT.AFFIRMATIVE);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
/** @type {import('./popup.js').CustomPopupInput} */
|
||||||
|
const branchNameInput = {
|
||||||
|
id: 'extension_branch_name',
|
||||||
|
label: t`Branch name (optional)`,
|
||||||
|
type: 'text',
|
||||||
|
tooltip: 'e.g. main, master, dev',
|
||||||
|
};
|
||||||
|
|
||||||
const customButtons = isCurrentUserAdmin ? [installForAllButton] : [];
|
const customButtons = isCurrentUserAdmin ? [installForAllButton] : [];
|
||||||
const popup = new Popup(html, POPUP_TYPE.INPUT, suggestUrl ?? '', { okButton, customButtons });
|
const customInputs = [branchNameInput];
|
||||||
|
const popup = new Popup(html, POPUP_TYPE.INPUT, suggestUrl ?? '', { okButton, customButtons, customInputs });
|
||||||
const input = await popup.show();
|
const input = await popup.show();
|
||||||
|
|
||||||
if (!input) {
|
if (!input) {
|
||||||
@ -1417,7 +1426,8 @@ export async function openThirdPartyExtensionMenu(suggestUrl = '') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const url = String(input).trim();
|
const url = String(input).trim();
|
||||||
await installExtension(url, global);
|
const branchName = String(popup.inputResults.get('extension_branch_name') ?? '').trim();
|
||||||
|
await installExtension(url, global, branchName);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function initExtensions() {
|
export async function initExtensions() {
|
||||||
|
@ -291,7 +291,7 @@ async function installAsset(url, assetType, filename) {
|
|||||||
try {
|
try {
|
||||||
if (category === 'extension') {
|
if (category === 'extension') {
|
||||||
console.debug(DEBUG_PREFIX, 'Installing extension ', url);
|
console.debug(DEBUG_PREFIX, 'Installing extension ', url);
|
||||||
await installExtension(url);
|
await installExtension(url, false);
|
||||||
console.debug(DEBUG_PREFIX, 'Extension installed.');
|
console.debug(DEBUG_PREFIX, 'Extension installed.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -309,7 +309,7 @@ async function installAsset(url, assetType, filename) {
|
|||||||
console.debug(DEBUG_PREFIX, 'Importing character ', filename);
|
console.debug(DEBUG_PREFIX, 'Importing character ', filename);
|
||||||
const blob = await result.blob();
|
const blob = await result.blob();
|
||||||
const file = new File([blob], filename, { type: blob.type });
|
const file = new File([blob], filename, { type: blob.type });
|
||||||
await processDroppedFiles([file], true);
|
await processDroppedFiles([file]);
|
||||||
console.debug(DEBUG_PREFIX, 'Character downloaded.');
|
console.debug(DEBUG_PREFIX, 'Character downloaded.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,8 @@ export const POPUP_RESULT = {
|
|||||||
* @property {string} id - The id for the html element
|
* @property {string} id - The id for the html element
|
||||||
* @property {string} label - The label text for the input
|
* @property {string} label - The label text for the input
|
||||||
* @property {string?} [tooltip=null] - Optional tooltip icon displayed behind the label
|
* @property {string?} [tooltip=null] - Optional tooltip icon displayed behind the label
|
||||||
* @property {boolean?} [defaultState=false] - The default state when opening the popup (false if not set)
|
* @property {boolean|string|undefined} [defaultState=false] - The default state when opening the popup (false if not set)
|
||||||
|
* @property {string?} [type='checkbox'] - The type of the input (default is checkbox)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,7 +158,7 @@ export class Popup {
|
|||||||
|
|
||||||
/** @type {POPUP_RESULT|number} */ result;
|
/** @type {POPUP_RESULT|number} */ result;
|
||||||
/** @type {any} */ value;
|
/** @type {any} */ value;
|
||||||
/** @type {Map<string,boolean>?} */ inputResults;
|
/** @type {Map<string,string|boolean>?} */ inputResults;
|
||||||
/** @type {any} */ cropData;
|
/** @type {any} */ cropData;
|
||||||
|
|
||||||
/** @type {HTMLElement} */ lastFocus;
|
/** @type {HTMLElement} */ lastFocus;
|
||||||
@ -260,28 +261,52 @@ export class Popup {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const label = document.createElement('label');
|
if (!input.type || input.type === 'checkbox') {
|
||||||
label.classList.add('checkbox_label', 'justifyCenter');
|
const label = document.createElement('label');
|
||||||
label.setAttribute('for', input.id);
|
label.classList.add('checkbox_label', 'justifyCenter');
|
||||||
const inputElement = document.createElement('input');
|
label.setAttribute('for', input.id);
|
||||||
inputElement.type = 'checkbox';
|
const inputElement = document.createElement('input');
|
||||||
inputElement.id = input.id;
|
inputElement.type = 'checkbox';
|
||||||
inputElement.checked = input.defaultState ?? false;
|
inputElement.id = input.id;
|
||||||
label.appendChild(inputElement);
|
inputElement.checked = Boolean(input.defaultState ?? false);
|
||||||
const labelText = document.createElement('span');
|
label.appendChild(inputElement);
|
||||||
labelText.innerText = input.label;
|
const labelText = document.createElement('span');
|
||||||
labelText.dataset.i18n = input.label;
|
labelText.innerText = input.label;
|
||||||
label.appendChild(labelText);
|
labelText.dataset.i18n = input.label;
|
||||||
|
label.appendChild(labelText);
|
||||||
|
|
||||||
if (input.tooltip) {
|
if (input.tooltip) {
|
||||||
const tooltip = document.createElement('div');
|
const tooltip = document.createElement('div');
|
||||||
tooltip.classList.add('fa-solid', 'fa-circle-info', 'opacity50p');
|
tooltip.classList.add('fa-solid', 'fa-circle-info', 'opacity50p');
|
||||||
tooltip.title = input.tooltip;
|
tooltip.title = input.tooltip;
|
||||||
tooltip.dataset.i18n = '[title]' + input.tooltip;
|
tooltip.dataset.i18n = '[title]' + input.tooltip;
|
||||||
label.appendChild(tooltip);
|
label.appendChild(tooltip);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inputControls.appendChild(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.inputControls.appendChild(label);
|
if (input.type === 'text') {
|
||||||
|
const label = document.createElement('label');
|
||||||
|
label.classList.add('text_label', 'justifyCenter');
|
||||||
|
label.setAttribute('for', input.id);
|
||||||
|
|
||||||
|
const inputElement = document.createElement('input');
|
||||||
|
inputElement.classList.add('text_pole');
|
||||||
|
inputElement.type = 'text';
|
||||||
|
inputElement.id = input.id;
|
||||||
|
inputElement.value = String(input.defaultState ?? '');
|
||||||
|
inputElement.placeholder = input.tooltip ?? '';
|
||||||
|
|
||||||
|
const labelText = document.createElement('span');
|
||||||
|
labelText.innerText = input.label;
|
||||||
|
labelText.dataset.i18n = input.label;
|
||||||
|
|
||||||
|
label.appendChild(labelText);
|
||||||
|
label.appendChild(inputElement);
|
||||||
|
|
||||||
|
this.inputControls.appendChild(label);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set the default button class
|
// Set the default button class
|
||||||
@ -529,7 +554,8 @@ export class Popup {
|
|||||||
this.inputResults = new Map(this.customInputs.map(input => {
|
this.inputResults = new Map(this.customInputs.map(input => {
|
||||||
/** @type {HTMLInputElement} */
|
/** @type {HTMLInputElement} */
|
||||||
const inputControl = this.dlg.querySelector(`#${input.id}`);
|
const inputControl = this.dlg.querySelector(`#${input.id}`);
|
||||||
return [inputControl.id, inputControl.checked];
|
const value = input.type === 'text' ? inputControl.value : inputControl.checked;
|
||||||
|
return [inputControl.id, value];
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,7 +645,7 @@ export class Popup {
|
|||||||
/** @readonly @type {Popup[]} Remember all popups */
|
/** @readonly @type {Popup[]} Remember all popups */
|
||||||
popups: [],
|
popups: [],
|
||||||
|
|
||||||
/** @type {{value: any, result: POPUP_RESULT|number?, inputResults: Map<string, boolean>?}?} Last popup result */
|
/** @type {{value: any, result: POPUP_RESULT|number?, inputResults: Map<string, string|boolean>?}?} Last popup result */
|
||||||
lastResult: null,
|
lastResult: null,
|
||||||
|
|
||||||
/** @returns {boolean} Checks if any modal popup dialog is open */
|
/** @returns {boolean} Checks if any modal popup dialog is open */
|
||||||
|
@ -76,7 +76,7 @@ router.post('/install', async (request, response) => {
|
|||||||
fs.mkdirSync(PUBLIC_DIRECTORIES.globalExtensions);
|
fs.mkdirSync(PUBLIC_DIRECTORIES.globalExtensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { url, global } = request.body;
|
const { url, global, branch } = request.body;
|
||||||
|
|
||||||
if (global && !request.user.profile.admin) {
|
if (global && !request.user.profile.admin) {
|
||||||
console.error(`User ${request.user.profile.handle} does not have permission to install global extensions.`);
|
console.error(`User ${request.user.profile.handle} does not have permission to install global extensions.`);
|
||||||
@ -90,8 +90,12 @@ router.post('/install', async (request, response) => {
|
|||||||
return response.status(409).send(`Directory already exists at ${extensionPath}`);
|
return response.status(409).send(`Directory already exists at ${extensionPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await git.clone(url, extensionPath, { '--depth': 1 });
|
const cloneOptions = { '--depth': 1 };
|
||||||
console.info(`Extension has been cloned at ${extensionPath}`);
|
if (branch) {
|
||||||
|
cloneOptions['--branch'] = branch;
|
||||||
|
}
|
||||||
|
await git.clone(url, extensionPath, cloneOptions);
|
||||||
|
console.info(`Extension has been cloned to ${extensionPath} from ${url} at ${branch || '(default)'} branch`);
|
||||||
|
|
||||||
const { version, author, display_name } = await getManifest(extensionPath);
|
const { version, author, display_name } = await getManifest(extensionPath);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user