This commit is contained in:
RossAscends
2023-06-06 22:48:26 +09:00
8 changed files with 2491 additions and 129 deletions

View File

@ -34,6 +34,7 @@
<script src="scripts/cropper.min.js"></script> <script src="scripts/cropper.min.js"></script>
<script src="scripts/jquery-cropper.min.js"></script> <script src="scripts/jquery-cropper.min.js"></script>
<script src="scripts/toastr.min.js"></script> <script src="scripts/toastr.min.js"></script>
<script src="scripts/fuse.js"></script>
<script type="module" src="scripts/eventemitter.js"></script> <script type="module" src="scripts/eventemitter.js"></script>
<script type="module" src="scripts/power-user.js"></script> <script type="module" src="scripts/power-user.js"></script>
<script type="module" src="scripts/swiped-events.js"></script> <script type="module" src="scripts/swiped-events.js"></script>

View File

@ -349,7 +349,7 @@ const system_messages = {
'<i>(Not endorsed, your discretion is advised)</i>', '<i>(Not endorsed, your discretion is advised)</i>',
'<ol>', '<ol>',
'<li><a target="_blank" href="https://discord.gg/pygmalionai">Pygmalion AI Discord</a></li>', '<li><a target="_blank" href="https://discord.gg/pygmalionai">Pygmalion AI Discord</a></li>',
'<li><a target="_blank" href="https://www.characterhub.org/">CharacterHub (NSFW)</a></li>', '<li><a target="_blank" href="https://chub.ai/">Chub (NSFW)</a></li>',
'</ol>', '</ol>',
'<hr>', '<hr>',
'<h3>Where can I get help?</h3>', '<h3>Where can I get help?</h3>',
@ -1399,6 +1399,20 @@ function getStoppingStrings(isImpersonate, addSpace) {
return addSpace ? result.map(x => `${x} `) : result; return addSpace ? result.map(x => `${x} `) : result;
} }
// Background prompt generation
export async function generateQuietPrompt(quiet_prompt) {
return await new Promise(
async function promptPromise(resolve, reject) {
try {
await Generate('quiet', { resolve, reject, quiet_prompt, force_name2: true, });
}
catch {
reject();
}
});
}
function processCommands(message, type) { function processCommands(message, type) {
if (type == "regenerate" || type == "swipe" || type == 'quiet') { if (type == "regenerate" || type == "swipe" || type == 'quiet') {
return null; return null;

View File

@ -1,4 +1,7 @@
import { generateQuietPrompt } from "../../../script.js";
import { getContext, saveMetadataDebounced } from "../../extensions.js"; import { getContext, saveMetadataDebounced } from "../../extensions.js";
import { registerSlashCommand } from "../../slash-commands.js";
import { stringFormat } from "../../utils.js";
export { MODULE_NAME }; export { MODULE_NAME };
const MODULE_NAME = 'backgrounds'; const MODULE_NAME = 'backgrounds';
@ -87,6 +90,30 @@ function onSelectBackgroundClick() {
} }
} }
const autoBgPrompt = `Pause your roleplay and choose a location ONLY from the provided list that is the most suitable for the current scene. Do not output any other text:\n{0}`;
async function autoBackgroundCommand() {
const options = Array.from(document.querySelectorAll('.BGSampleTitle')).map(x => ({ element: x, text: x.innerText.trim() })).filter(x => x.text.length > 0);
if (options.length == 0) {
toastr.warning('No backgrounds to choose from. Please upload some images to the "backgrounds" folder.');
return;
}
const list = options.map(option => `- ${option.text}`).join('\n');
const prompt = stringFormat(autoBgPrompt, list);
const reply = await generateQuietPrompt(prompt);
const fuse = new Fuse(options, { keys: ['text'] });
const bestMatch = fuse.search(reply, { limit: 1 });
if (bestMatch.length == 0) {
toastr.warning('No match found. Please try again.');
return;
}
console.debug('Automatically choosing background:', bestMatch);
bestMatch[0].item.element.click();
}
$(document).ready(function () { $(document).ready(function () {
function addSettings() { function addSettings() {
const html = ` const html = `
@ -111,6 +138,16 @@ $(document).ready(function () {
Any background image selected while lock is engaged will be saved automatically. Any background image selected while lock is engaged will be saved automatically.
</small> </small>
</div> </div>
<div class="background_controls">
<div id="auto_background" class="menu_button">
<i class="fa-solid fa-wand-magic"></i>
Auto
</div>
<small>
Automatically select a background based on the chat context.<br>
Respects the "Lock" setting state.
</small>
</div>
<div>Preview</div> <div>Preview</div>
<div id="custom_bg_preview"> <div id="custom_bg_preview">
</div> </div>
@ -122,8 +159,12 @@ $(document).ready(function () {
$('#lock_background').on('click', onLockBackgroundClick); $('#lock_background').on('click', onLockBackgroundClick);
$('#unlock_background').on('click', onUnlockBackgroundClick); $('#unlock_background').on('click', onUnlockBackgroundClick);
$(document).on("click", ".bg_example", onSelectBackgroundClick); $(document).on("click", ".bg_example", onSelectBackgroundClick);
$('#auto_background').on("click", autoBackgroundCommand);
} }
addSettings(); addSettings();
setInterval(moduleWorker, UPDATE_INTERVAL); setInterval(moduleWorker, UPDATE_INTERVAL);
registerSlashCommand('lock', onLockBackgroundClick, [], " locks a background for the currently selected chat", true, true);
registerSlashCommand('unlock', onUnlockBackgroundClick, [], ' unlocks a background for the currently selected chat', true, true);
registerSlashCommand('autobg', autoBackgroundCommand, ['bgauto'], ' automatically changes the background based on the chat context', true, true);
}); });

View File

@ -904,7 +904,9 @@ function setExpressionOverrideHtml(forceClear = false) {
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div> <div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
</div> </div>
<div class="inline-drawer-content"> <div class="inline-drawer-content">
<p class="offline_mode">You are in offline mode. Click on the image below to set the expression.</p> <div class="offline_mode">
<small>You are in offline mode. Click on the image below to set the expression.</small>
</div>
<div class="flex-container flexnowrap"> <div class="flex-container flexnowrap">
<input id="expression_override" type="text" class="text_pole" placeholder="Override folder name" /> <input id="expression_override" type="text" class="text_pole" placeholder="Override folder name" />
<input id="expression_override_button" class="menu_button" type="submit" value="Submit" /> <input id="expression_override_button" class="menu_button" type="submit" value="Submit" />

View File

@ -143,11 +143,6 @@ img.expression.default {
color: red; color: red;
} }
.expression_settings p {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
.expression_settings label { .expression_settings label {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -4,7 +4,9 @@ import {
substituteParams, substituteParams,
eventSource, eventSource,
event_types, event_types,
generateQuietPrompt,
} from "../../../script.js"; } from "../../../script.js";
import { registerSlashCommand } from "../../slash-commands.js";
const MODULE_NAME = "Objective" const MODULE_NAME = "Objective"
@ -12,7 +14,7 @@ const MODULE_NAME = "Objective"
let globalObjective = "" let globalObjective = ""
let globalTasks = [] let globalTasks = []
let currentChatId = "" let currentChatId = ""
let currentTask = {} let currentTask = null
let checkCounter = 0 let checkCounter = 0
@ -38,90 +40,70 @@ const objectivePrompts = {
` `
} }
const injectPrompts = { const extensionPrompt = "Your current task is [{{task}}]. Balance existing roleplay with completing this task."
"task": "Your current task is [{{task}}]. Balance existing roleplay with completing this task."
}
// Background prompt generation
async function generateQuietPrompt(quiet_prompt) {
return await new Promise(
async function promptPromise(resolve, reject) {
try {
await getContext().generate('quiet', { resolve, reject, quiet_prompt, force_name2: true, });
}
catch {
reject();
}
});
}
//###############################// //###############################//
//# Task Management #// //# Task Management #//
//###############################// //###############################//
// Accepts optional position. Defaults to adding to end of list. // Accepts optional index. Defaults to adding to end of list.
function addTask(description, position = null) { function addTask(description, index = null) {
position = position != null ? position : position = globalTasks.length index = index != null ? index: index = globalTasks.length
globalTasks.splice(position, 0, { globalTasks.splice(index, 0, new ObjectiveTask(
"description": description, {description: description}
"completed": false ))
})
saveState() saveState()
} }
// Get a task either by index or task description. Return current task if none specified // Return the task and index or throw an error
function getTask(index = null, taskDescription = null) { function getTaskById(taskId){
let task = {} if (taskId == null) {
if (index == null && taskDescription == null) { throw `Null task id`
task = currentTask }
} else if (index != null) { const index = globalTasks.findIndex((task) => task.id === taskId);
task = globalObjective[index] if (index !== -1) {
} else if (taskDescription != null) { return { task: globalTasks[index], index: index };
task = globalTasks.find(task => { } else {
return task.description == description ? true : false throw `Cannot find task with ${taskId}`
})
} }
return task
} }
function deleteTask(index) { function deleteTask(taskId){
const { task, index } = getTaskById(taskId)
globalTasks.splice(index, 1) globalTasks.splice(index, 1)
setCurrentTask() setCurrentTask()
updateUiTaskList() updateUiTaskList()
} }
// Complete the current task, setting next task to next incomplete task
function completeTask(task) {
task.completed = true
console.info(`Task successfully completed: ${JSON.stringify(task)}`)
setCurrentTask()
updateUiTaskList()
}
// Call Quiet Generate to create task list using character context, then convert to tasks. Should not be called much. // Call Quiet Generate to create task list using character context, then convert to tasks. Should not be called much.
async function generateTasks() { async function generateTasks() {
const prompt = substituteParams(objectivePrompts["createTask"].replace(/{{objective}}/gi, globalObjective)); const prompt = substituteParams(objectivePrompts["createTask"].replace(/{{objective}}/gi, globalObjective));
console.log(`Generating tasks for objective with prompt`) console.log(`Generating tasks for objective with prompt`)
toastr.info('Generating tasks for objective', 'Please wait...'); toastr.info('Generating tasks for objective', 'Please wait...');
const taskResponse = await generateQuietPrompt(prompt) const taskResponse = await generateQuietPrompt(prompt)
// Clear all existing global tasks when generating
globalTasks = [] globalTasks = []
const numberedListPattern = /^\d+\./ const numberedListPattern = /^\d+\./
// Add numbered tasks, store them without the numbers. // Create tasks from generated task list
for (const task of taskResponse.split('\n').map(x => x.trim())) { for (const task of taskResponse.split('\n').map(x => x.trim())) {
if (task.match(numberedListPattern) != null) { if (task.match(numberedListPattern) != null) {
addTask(task.replace(numberedListPattern, '').trim()) addTask(task.replace(numberedListPattern,"").trim())
} }
} }
updateUiTaskList() updateUiTaskList()
console.info(`Response for Objective: '${globalObjective}' was \n'${taskResponse}', \nwhich created tasks \n${JSON.stringify(globalTasks, null, 2)} `) console.info(`Response for Objective: '${globalObjective}' was \n'${taskResponse}', \nwhich created tasks \n${JSON.stringify(globalTasks.map(v => {return v.toSaveState()}), null, 2)} `)
toastr.success(`Generated ${globalTasks.length} tasks`, 'Done!'); toastr.success(`Generated ${globalTasks.length} tasks`, 'Done!');
} }
// Call Quiet Generate to check if a task is completed // Call Quiet Generate to check if a task is completed
async function checkTaskCompleted() { async function checkTaskCompleted() {
// Make sure there are tasks // Make sure there are tasks
if (Object.keys(currentTask).length == 0) { if (jQuery.isEmptyObject(currentTask)) {
return return
} }
checkCounter = $('#objective-check-frequency').val() checkCounter = $('#objective-check-frequency').val()
@ -131,8 +113,8 @@ async function checkTaskCompleted() {
// Check response if task complete // Check response if task complete
if (taskResponse.includes("true")) { if (taskResponse.includes("true")) {
console.info(`Character determined task '${JSON.stringify(currentTask)} is completed.`) console.info(`Character determined task '${JSON.stringify(currentTask.toSaveState())} is completed.`)
completeTask(getTask()) currentTask.completeTask()
} else if (!(taskResponse.includes("false"))) { } else if (!(taskResponse.includes("false"))) {
console.warn(`checkTaskCompleted response did not contain true or false. taskResponse: ${taskResponse}`) console.warn(`checkTaskCompleted response did not contain true or false. taskResponse: ${taskResponse}`)
} else { } else {
@ -142,21 +124,26 @@ async function checkTaskCompleted() {
// Set a task in extensionPrompt context. Defaults to first incomplete // Set a task in extensionPrompt context. Defaults to first incomplete
function setCurrentTask(index = null) { function setCurrentTask(taskId = null) {
const context = getContext(); const context = getContext();
currentTask = {}; currentTask = {};
if (index === null) { // Set current task to either the next incomplete task, or the index
if (taskId === null) {
currentTask = globalTasks.find(task => !task.completed) || {}; currentTask = globalTasks.find(task => !task.completed) || {};
} else if (index >= 0 && index < globalTasks.length) { } else {
const { _, index } = getTaskById(taskId)
currentTask = globalTasks[index]; currentTask = globalTasks[index];
} }
const { description } = currentTask; // Get the task description and add to extension prompt
const injectPromptsTask = injectPrompts["task"].replace(/{{task}}/gi, description); const description = currentTask.description || null;
// Now update the extension prompt
if (description) { if (description) {
context.setExtensionPrompt(MODULE_NAME, injectPromptsTask, 1, $('#objective-chat-depth').val()); const extensionPromptText = extensionPrompt.replace(/{{task}}/gi, description);
context.setExtensionPrompt(MODULE_NAME, extensionPromptText, 1, $('#objective-chat-depth').val());
console.info(`Current task in context.extensionPrompts.Objective is ${JSON.stringify(context.extensionPrompts.Objective)}`); console.info(`Current task in context.extensionPrompts.Objective is ${JSON.stringify(context.extensionPrompts.Objective)}`);
} else { } else {
context.setExtensionPrompt(MODULE_NAME, ''); context.setExtensionPrompt(MODULE_NAME, '');
@ -166,7 +153,114 @@ function setCurrentTask(index = null) {
saveState(); saveState();
} }
let taskIdCounter = 0
function getNextTaskId(){
// Make sure id does not exist
while (globalTasks.find(task => task.id == taskIdCounter) != undefined) {
taskIdCounter += 1
}
const nextId = taskIdCounter
console.log(`TaskID assigned: ${nextId}`)
taskIdCounter += 1
return nextId
}
class ObjectiveTask {
id
description
completed
parent
children
// UI Elements
taskHtml
descriptionSpan
completedCheckbox
deleteTaskButton
addTaskButton
constructor ({id=undefined, description, completed=false, parent=null}) {
this.description = description
this.parent = parent
this.children = []
this.completed = completed
// Generate a new ID if none specified
if (id==undefined){
this.id = getNextTaskId()
} else {
this.id=id
}
}
// Complete the current task, setting next task to next incomplete task
completeTask() {
this.completed = true
console.info(`Task successfully completed: ${JSON.stringify(this.description)}`)
setCurrentTask()
updateUiTaskList()
}
// Add a single task to the UI and attach event listeners for user edits
addUiElement() {
const template = `
<div id="objective-task-label-${this.id}" class="flex1 checkbox_label">
<input id="objective-task-complete-${this.id}" type="checkbox">
<span class="text_pole" style="display: block" id="objective-task-description-${this.id}" contenteditable>${this.description}</span>
<div id="objective-task-delete-${this.id}" class="objective-task-button fa-solid fa-xmark fa-2x" title="Delete Task"></div>
<div id="objective-task-add-${this.id}" class="objective-task-button fa-solid fa-plus fa-2x" title="Add Task"></div>
</div><br>
`;
// Add the filled out template
$('#objective-tasks').append(template);
this.completedCheckbox = $(`#objective-task-complete-${this.id}`);
this.descriptionSpan = $(`#objective-task-description-${this.id}`);
this.addButton = $(`#objective-task-add-${this.id}`);
this.deleteButton = $(`#objective-task-delete-${this.id}`);
// Add event listeners and set properties
$(`#objective-task-complete-${this.id}`).prop('checked', this.completed);
$(`#objective-task-complete-${this.id}`).on('click', () => (this.onCompleteClick()));
$(`#objective-task-description-${this.id}`).on('keyup', () => (this.onDescriptionUpdate()));
$(`#objective-task-description-${this.id}`).on('focusout', () => (this.onDescriptionFocusout()));
$(`#objective-task-delete-${this.id}`).on('click', () => (this.onDeleteClick()));
$(`#objective-task-add-${this.id}`).on('click', () => (this.onAddClick()));
}
onCompleteClick(){
this.completed = this.completedCheckbox.prop('checked')
setCurrentTask();
}
onDescriptionUpdate(){
this.description = this.descriptionSpan.text();
}
onDescriptionFocusout(){
setCurrentTask();
}
onDeleteClick(){
deleteTask(this.id);
}
onAddClick(){
const {_, index} = getTaskById(this.id)
addTask("", index + 1);
setCurrentTask();
updateUiTaskList();
}
toSaveState() {
return {
"id":this.id,
"description":this.description,
"completed":this.completed,
"parent": this.parent,
}
}
}
//###############################// //###############################//
//# UI AND Settings #// //# UI AND Settings #//
@ -194,9 +288,12 @@ function saveState() {
currentChatId = context.chatId currentChatId = context.chatId
} }
// Convert globalTasks for saving
const tasks = globalTasks.map(task => {return task.toSaveState()})
chat_metadata['objective'] = { chat_metadata['objective'] = {
objective: globalObjective, objective: globalObjective,
tasks: globalTasks, tasks: tasks,
checkFrequency: $('#objective-check-frequency').val(), checkFrequency: $('#objective-check-frequency').val(),
chatDepth: $('#objective-chat-depth').val(), chatDepth: $('#objective-chat-depth').val(),
hideTasks: $('#objective-hide-tasks').prop('checked'), hideTasks: $('#objective-hide-tasks').prop('checked'),
@ -208,11 +305,11 @@ function saveState() {
// Dump core state // Dump core state
function debugObjectiveExtension() { function debugObjectiveExtension() {
console.log(JSON.stringify({ console.log(JSON.stringify({
"currentTask": currentTask, "currentTask": currentTask.toSaveState(),
"currentChatId": currentChatId, "currentChatId": currentChatId,
"checkCounter": checkCounter, "checkCounter": checkCounter,
"globalObjective": globalObjective, "globalObjective": globalObjective,
"globalTasks": globalTasks, "globalTasks": globalTasks.map(v => {return v.toSaveState()}),
"extension_settings": chat_metadata['objective'], "extension_settings": chat_metadata['objective'],
}, null, 2)) }, null, 2))
} }
@ -220,55 +317,13 @@ function debugObjectiveExtension() {
window.debugObjectiveExtension = debugObjectiveExtension window.debugObjectiveExtension = debugObjectiveExtension
// Add a single task to the UI and attach event listeners for user edits
function addUiTask(taskIndex, taskComplete, taskDescription) {
const template = `
<div id="objective-task-label-${taskIndex}" class="flex1 checkbox_label">
<span>${taskIndex}</span>
<input id="objective-task-complete-${taskIndex}" type="checkbox">
<span class="text_pole" style="display: block" id="objective-task-description-${taskIndex}" contenteditable>${taskDescription}</span>
<div id="objective-task-delete-${taskIndex}" class="objective-task-button fa-solid fa-xmark fa-2x" title="Delete Task"></div>
<div id="objective-task-add-${taskIndex}" class="objective-task-button fa-solid fa-plus fa-2x" title="Add Task"></div>
</div>
`;
// Add the filled out template
$('#objective-tasks').append(template);
// Add event listeners and set properties
$(`#objective-task-complete-${taskIndex}`).prop('checked', taskComplete);
$(`#objective-task-complete-${taskIndex}`).on('click', event => {
const index = Number(event.target.id.split('-').pop());
globalTasks[index].completed = event.target.checked;
setCurrentTask();
});
$(`#objective-task-description-${taskIndex}`).on('keyup', event => {
const index = Number(event.target.id.split('-').pop());
globalTasks[index].description = event.target.textContent;
});
$(`#objective-task-delete-${taskIndex}`).on('click', event => {
const index = Number(event.target.id.split('-').pop());
deleteTask(index)
});
$(`#objective-task-add-${taskIndex}`).on('click', event => {
const index = Number(event.target.id.split('-').pop()) + 1;
addTask("", index)
setCurrentTask()
updateUiTaskList()
});
}
// Populate UI task list // Populate UI task list
function updateUiTaskList() { function updateUiTaskList() {
$('#objective-tasks').empty() $('#objective-tasks').empty()
// Show tasks if there are any // Show tasks if there are any
if (globalTasks.length > 0) { if (globalTasks.length > 0){
for (const index in globalTasks) { for (const task of globalTasks) {
addUiTask( task.addUiElement()
index,
globalTasks[index].completed,
globalTasks[index].description
)
} }
} else { } else {
// Show button to add tasks if there are none // Show button to add tasks if there are none
@ -283,14 +338,6 @@ function updateUiTaskList() {
} }
} }
function addManualTaskCheck() {
$('#extensionsMenu').prepend(`
<div id="objective-task-manual-check-menu-item" class="list-group-item flex-container flexGap5">
<div id="objective-task-manual-check" class="extensionsMenuExtensionButton fa-regular fa-square-check"/></div>
Manual Task Check
</div>`)
$('#objective-task-manual-check-menu-item').attr('title', 'Trigger AI check of completed tasks').on('click', checkTaskCompleted)
}
// Trigger creation of new tasks with given objective. // Trigger creation of new tasks with given objective.
async function onGenerateObjectiveClick() { async function onGenerateObjectiveClick() {
@ -338,7 +385,14 @@ function loadSettings() {
// Update globals // Update globals
globalObjective = chat_metadata['objective'].objective globalObjective = chat_metadata['objective'].objective
globalTasks = chat_metadata['objective'].tasks globalTasks = chat_metadata['objective'].tasks.map(task => {
return new ObjectiveTask({
id: task.id,
description: task.description,
completed: task.completed,
parent: task.parent,
})
});
checkCounter = chat_metadata['objective'].checkFrequency checkCounter = chat_metadata['objective'].checkFrequency
// Update UI elements // Update UI elements
@ -352,6 +406,15 @@ function loadSettings() {
setCurrentTask() setCurrentTask()
} }
function addManualTaskCheckUi() {
$('#extensionsMenu').prepend(`
<div id="objective-task-manual-check-menu-item" class="list-group-item flex-container flexGap5">
<div id="objective-task-manual-check" class="extensionsMenuExtensionButton fa-regular fa-square-check"/></div>
Manual Task Check
</div>`)
$('#objective-task-manual-check-menu-item').attr('title', 'Trigger AI check of completed tasks').on('click', checkTaskCompleted)
}
jQuery(() => { jQuery(() => {
const settingsHtml = ` const settingsHtml = `
<div class="objective-settings"> <div class="objective-settings">
@ -373,9 +436,10 @@ jQuery(() => {
<div class="objective_block objective_block_control flex1 flexFlowColumn"> <div class="objective_block objective_block_control flex1 flexFlowColumn">
<label for="objective-chat-depth">Position in Chat</label> <label for="objective-chat-depth">Position in Chat</label>
<input id="objective-chat-depth" class="text_pole widthUnset" type="number" min="0" max="99" /> <input id="objective-chat-depth" class="text_pole widthUnset" type="number" min="0" max="99" />
<small>Depth</small>
</div> </div>
<div class="objective_block objective_block_control flex1 flexFlowColumn"> <br>
<div class="objective_block objective_block_control flex1">
<label for="objective-check-frequency">Task Check Frequency</label> <label for="objective-check-frequency">Task Check Frequency</label>
<input id="objective-check-frequency" class="text_pole widthUnset" type="number" min="0" max="99" /> <input id="objective-check-frequency" class="text_pole widthUnset" type="number" min="0" max="99" />
<small>(0 = disabled)</small> <small>(0 = disabled)</small>
@ -386,7 +450,7 @@ jQuery(() => {
</div> </div>
</div>`; </div>`;
addManualTaskCheck() addManualTaskCheckUi()
$('#extensions_settings').append(settingsHtml); $('#extensions_settings').append(settingsHtml);
$('#objective-generate').on('click', onGenerateObjectiveClick) $('#objective-generate').on('click', onGenerateObjectiveClick)
$('#objective-chat-depth').on('input', onChatDepthInput) $('#objective-chat-depth').on('input', onChatDepthInput)
@ -412,4 +476,6 @@ jQuery(() => {
setCurrentTask(); setCurrentTask();
$('#objective-counter').text(checkCounter) $('#objective-counter').text(checkCounter)
}); });
registerSlashCommand('taskcheck', checkTaskCompleted, [], ' checks if the current task is completed', true, true);
}); });

View File

@ -5,12 +5,15 @@
.objective_block { .objective_block {
display: flex; display: flex;
/* flex-direction: row; */
align-items: center; align-items: center;
column-gap: 5px; column-gap: 5px;
flex-wrap: wrap; flex-wrap: wrap;
} }
.objective_block_control {
align-items: baseline;
}
.objective_block_control small, .objective_block_control small,
.objective_block_control label { .objective_block_control label {
width: max-content; width: max-content;

2240
public/scripts/fuse.js Normal file

File diff suppressed because it is too large Load Diff