This commit is contained in:
LenAnderson 2024-06-22 10:44:34 -04:00
parent ec122d7050
commit e964a10612
6 changed files with 213 additions and 23 deletions

View File

@ -43,6 +43,10 @@
<div id="qr--resizeHandle"></div>
<div id="qr--qrOptions">
<h3 data-i18n="Context Menu">Context Menu</h3>
<div id="qr--ctxEditor">

View File

@ -65,6 +65,7 @@ export class QuickReply {
/**@type {HTMLElement}*/ editorDebugState;
/**@type {HTMLInputElement}*/ editorExecuteHide;
/**@type {Promise}*/ editorExecutePromise;
/**@type {boolean}*/ isExecuting;
/**@type {SlashCommandAbortController}*/ abortController;
/**@type {SlashCommandDebugController}*/ debugController;
@ -244,9 +245,15 @@ export class QuickReply {
if (wrap.checked) {
message.style.whiteSpace = 'pre-wrap';
messageSyntaxInner.style.whiteSpace = 'pre-wrap';
if (this.clone) {
this.clone.style.whiteSpace = 'pre-wrap';
}
} else {
message.style.whiteSpace = 'pre';
messageSyntaxInner.style.whiteSpace = 'pre';
if (this.clone) {
this.clone.style.whiteSpace = 'pre';
}
}
updateScrollDebounced();
};
@ -317,6 +324,7 @@ export class QuickReply {
setSlashCommandAutoComplete(message, true);
//TODO move tab support for textarea into its own helper(?) and use for both this and .editor_maximize
message.addEventListener('keydown', async(evt) => {
if (this.isExecuting) return;
if (evt.key == 'Tab' && !evt.shiftKey && !evt.ctrlKey && !evt.altKey) {
evt.preventDefault();
const start = message.selectionStart;
@ -569,6 +577,28 @@ export class QuickReply {
stepOutBtn.addEventListener('click', ()=>{
this.debugController?.stepOut();
});
/**@type {boolean}*/
let isResizing = false;
let resizeStart;
let wStart;
/**@type {HTMLElement}*/
const resizeHandle = dom.querySelector('#qr--resizeHandle');
resizeHandle.addEventListener('pointerdown', (evt)=>{
if (isResizing) return;
isResizing = true;
evt.preventDefault();
resizeStart = evt.x;
wStart = dom.querySelector('#qr--qrOptions').offsetWidth;
const dragListener = debounce((evt)=>{
const w = wStart + resizeStart - evt.x;
dom.querySelector('#qr--qrOptions').style.setProperty('--width', `${w}px`);
}, 5);
window.addEventListener('pointerup', ()=>{
window.removeEventListener('pointermove', dragListener);
isResizing = false;
}, { once:true });
window.addEventListener('pointermove', dragListener);
});
await popupResult;
@ -596,6 +626,7 @@ export class QuickReply {
});
mo.observe(this.editorMessage.parentElement, { childList:true });
}
this.clone.style.width = `${inputRect.width}px`;
this.clone.style.height = `${inputRect.height}px`;
this.clone.style.left = `${inputRect.left}px`;
this.clone.style.top = `${inputRect.top}px`;
@ -620,8 +651,13 @@ export class QuickReply {
return location;
}
async executeFromEditor() {
if (this.editorExecutePromise) return;
if (this.isExecuting) return;
this.isExecuting = true;
this.editorDom.classList.add('qr--isExecuting');
const noSyntax = this.editorDom.querySelector('#qr--modal-messageHolder').classList.contains('qr--noSyntax');
if (noSyntax) {
this.editorDom.querySelector('#qr--modal-messageHolder').classList.remove('qr--noSyntax');
}
this.editorExecuteBtn.classList.add('qr--busy');
this.editorExecuteProgress.style.setProperty('--prog', '0');
this.editorExecuteErrors.classList.remove('qr--hasErrors');
@ -658,17 +694,14 @@ export class QuickReply {
title.textContent = isCurrent ? 'Current Scope' : 'Parent Scope';
let hi;
title.addEventListener('pointerenter', ()=>{
const loc = this.getEditorPosition(c.executorList[0].start - 1, c.executorList.slice(-1)[0].end);
const loc = this.getEditorPosition(Math.max(0, c.executorList[0].start - 1), c.executorList.slice(-1)[0].end);
const layer = this.editorPopup.dlg.getBoundingClientRect();
hi = document.createElement('div');
hi.style.position = 'fixed';
hi.classList.add('qr--highlight-secondary');
hi.style.left = `${loc.left - layer.left}px`;
hi.style.width = `${loc.right - loc.left}px`;
hi.style.top = `${loc.top - layer.top}px`;
hi.style.height = `${loc.bottom - loc.top}px`;
hi.style.zIndex = '50000';
hi.style.pointerEvents = 'none';
hi.style.border = '3px solid red';
this.editorPopup.dlg.append(hi);
});
title.addEventListener('pointerleave', ()=>hi?.remove());
@ -696,7 +729,13 @@ export class QuickReply {
v.classList.add('qr--undefined');
v.textContent = 'undefined';
} else {
v.textContent = val;
let jsonVal;
try { jsonVal = JSON.parse(val); } catch { /* empty */ }
if (jsonVal && typeof jsonVal == 'object') {
v.textContent = JSON.stringify(jsonVal, null, 2);
} else {
v.textContent = val;
}
}
item.append(v);
}
@ -725,7 +764,13 @@ export class QuickReply {
v.classList.add('qr--undefined');
v.textContent = 'undefined';
} else {
v.textContent = val;
let jsonVal;
try { jsonVal = JSON.parse(val); } catch { /* empty */ }
if (jsonVal && typeof jsonVal == 'object') {
v.textContent = JSON.stringify(jsonVal, null, 2);
} else {
v.textContent = val;
}
}
item.append(v);
}
@ -750,7 +795,13 @@ export class QuickReply {
v.classList.add('qr--undefined');
v.textContent = 'undefined';
} else {
v.textContent = val;
let jsonVal;
try { jsonVal = JSON.parse(val); } catch { /* empty */ }
if (jsonVal && typeof jsonVal == 'object') {
v.textContent = JSON.stringify(jsonVal, null, 2);
} else {
v.textContent = val;
}
}
pipeItem.append(v);
}
@ -762,19 +813,51 @@ export class QuickReply {
}
return wrap;
};
const buildStack = ()=>{
const wrap = document.createElement('div'); {
wrap.classList.add('qr--stack');
const title = document.createElement('div'); {
title.classList.add('qr--title');
title.textContent = 'Call Stack';
wrap.append(title);
}
for (const executor of this.debugController.cmdStack.toReversed()) {
const item = document.createElement('div'); {
item.classList.add('qr--item');
item.textContent = `/${executor.name}`;
if (executor.command.name == 'run') {
item.textContent += `${(executor.name == ':' ? '' : ' ')}${executor.unnamedArgumentList[0]?.value}`;
}
let hi;
item.addEventListener('pointerenter', ()=>{
const loc = this.getEditorPosition(Math.max(0, executor.start - 1), executor.end);
const layer = this.editorPopup.dlg.getBoundingClientRect();
hi = document.createElement('div');
hi.classList.add('qr--highlight-secondary');
hi.style.left = `${loc.left - layer.left}px`;
hi.style.width = `${loc.right - loc.left}px`;
hi.style.top = `${loc.top - layer.top}px`;
hi.style.height = `${loc.bottom - loc.top}px`;
this.editorPopup.dlg.append(hi);
});
item.addEventListener('pointerleave', ()=>hi?.remove());
wrap.append(item);
}
}
}
return wrap;
};
this.editorDebugState.append(buildVars(closure.scope, true));
this.editorDebugState.append(buildStack());
this.editorDebugState.classList.add('qr--active');
const loc = this.getEditorPosition(executor.start - 1, executor.end);
const loc = this.getEditorPosition(Math.max(0, executor.start - 1), executor.end);
const layer = this.editorPopup.dlg.getBoundingClientRect();
const hi = document.createElement('div');
hi.style.position = 'fixed';
hi.classList.add('qr--highlight');
hi.style.left = `${loc.left - layer.left}px`;
hi.style.width = `${loc.right - loc.left}px`;
hi.style.top = `${loc.top - layer.top}px`;
hi.style.height = `${loc.bottom - loc.top}px`;
hi.style.zIndex = '50000';
hi.style.pointerEvents = 'none';
hi.style.backgroundColor = 'rgb(255 255 0 / 0.5)';
this.editorPopup.dlg.append(hi);
const isStepping = await this.debugController.awaitContinue();
hi.remove();
@ -807,10 +890,14 @@ export class QuickReply {
`;
}
}
if (noSyntax) {
this.editorDom.querySelector('#qr--modal-messageHolder').classList.add('qr--noSyntax');
}
this.editorExecutePromise = null;
this.editorExecuteBtn.classList.remove('qr--busy');
this.editorPopup.dlg.classList.remove('qr--hide');
this.editorDom.classList.remove('qr--isExecuting');
this.isExecuting = false;
}
updateEditorProgress(done, total) {

View File

@ -241,6 +241,18 @@
.popup:has(#qr--modalEditor):has(.qr--isExecuting) .popup-controls {
display: none;
}
.popup:has(#qr--modalEditor):has(.qr--isExecuting) .qr--highlight {
position: fixed;
z-index: 50000;
pointer-events: none;
background-color: rgba(255, 255, 0, 0.5);
}
.popup:has(#qr--modalEditor):has(.qr--isExecuting) .qr--highlight-secondary {
position: fixed;
z-index: 50000;
pointer-events: none;
border: 3px solid red;
}
.popup:has(#qr--modalEditor) .popup-content {
display: flex;
flex-direction: column;
@ -262,9 +274,26 @@
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor.qr--isExecuting #qr--qrOptions > .qr--ctxEditorActions + h3 + div {
display: none;
}
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor.qr--isExecuting #qr--main > .qr--modal-messageContainer > #qr--modal-messageHolder > #qr--modal-message {
visibility: hidden;
}
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor.qr--isExecuting #qr--modal-debugButtons {
display: flex;
}
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor.qr--isExecuting #qr--resizeHandle {
width: 6px;
background-color: var(--SmartThemeBorderColor);
border: 2px solid var(--SmartThemeBlurTintColor);
transition: border-color 200ms, background-color 200ms;
cursor: w-resize;
}
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor.qr--isExecuting #qr--resizeHandle:hover {
background-color: var(--SmartThemeQuoteColor);
border-color: var(--SmartThemeQuoteColor);
}
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor.qr--isExecuting #qr--qrOptions {
width: var(--width, auto);
}
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor > #qr--main {
flex: 1 1 auto;
display: flex;
@ -435,6 +464,7 @@
}
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugButtons .qr--modal-debugButton {
aspect-ratio: 1.25 / 1;
width: 2.25em;
}
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugButtons .qr--modal-debugButton.qr--glyph-combo {
display: grid;
@ -544,12 +574,12 @@
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugState .qr--scope .qr--pipe {
display: contents;
}
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugState .qr--scope .qr--var:nth-child(2n + 2) .qr--key,
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugState .qr--scope .qr--macro:nth-child(2n + 2) .qr--key,
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugState .qr--scope .qr--pipe:nth-child(2n + 2) .qr--key,
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugState .qr--scope .qr--var:nth-child(2n + 2) .qr--val,
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugState .qr--scope .qr--macro:nth-child(2n + 2) .qr--val,
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugState .qr--scope .qr--pipe:nth-child(2n + 2) .qr--val {
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugState .qr--scope .qr--var:nth-child(2n + 1) .qr--key,
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugState .qr--scope .qr--macro:nth-child(2n + 1) .qr--key,
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugState .qr--scope .qr--pipe:nth-child(2n + 1) .qr--key,
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugState .qr--scope .qr--var:nth-child(2n + 1) .qr--val,
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugState .qr--scope .qr--macro:nth-child(2n + 1) .qr--val,
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugState .qr--scope .qr--pipe:nth-child(2n + 1) .qr--val {
background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.25);
}
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugState .qr--scope .qr--var.qr--isHidden .qr--key,
@ -582,6 +612,19 @@
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugState .qr--scope .qr--scope .qr--pipe .qr--val {
opacity: 0.5;
}
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugState .qr--stack .qr--title {
grid-column: 1 / 3;
font-weight: bold;
background-color: var(--black50a);
padding: 0.25em;
margin-top: 1em;
}
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugState .qr--stack .qr--item {
margin-left: 0.5em;
}
.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-debugState .qr--stack .qr--item:nth-child(2n + 1) {
background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.25);
}
@keyframes qr--progressPulse {
0%,
100% {

View File

@ -262,8 +262,23 @@
.popup:has(#qr--modalEditor) {
aspect-ratio: unset;
&:has(.qr--isExecuting) .popup-controls {
display: none;
&:has(.qr--isExecuting) {
.popup-controls {
display: none;
}
.qr--highlight {
position: fixed;
z-index: 50000;
pointer-events: none;
background-color: rgba(255, 255, 0, 0.5);
}
.qr--highlight-secondary {
position: fixed;
z-index: 50000;
pointer-events: none;
border: 3px solid red;
}
}
.popup-content {
@ -289,9 +304,26 @@
{
display: none;
}
#qr--main > .qr--modal-messageContainer > #qr--modal-messageHolder > #qr--modal-message {
visibility: hidden;
}
#qr--modal-debugButtons {
display: flex;
}
#qr--resizeHandle {
width: 6px;
background-color: var(--SmartThemeBorderColor);
border: 2px solid var(--SmartThemeBlurTintColor);
transition: border-color 200ms, background-color 200ms;
cursor: w-resize;
&:hover {
background-color: var(--SmartThemeQuoteColor);
border-color: var(--SmartThemeQuoteColor);
}
}
#qr--qrOptions {
width: var(--width, auto);
}
}
> #qr--main {
@ -461,6 +493,7 @@
gap: 1em;
.qr--modal-debugButton {
aspect-ratio: 1.25 / 1;
width: 2.25em;
&.qr--glyph-combo {
display: grid;
grid-template-columns: 1fr;
@ -566,7 +599,7 @@
}
.qr--var, .qr--macro, .qr--pipe {
display: contents;
&:nth-child(2n + 2) {
&:nth-child(2n + 1) {
.qr--key, .qr--val {
background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.25);
}
@ -597,6 +630,22 @@
}
}
}
.qr--stack {
.qr--title {
grid-column: 1 / 3;
font-weight: bold;
background-color: var(--black50a);
padding: 0.25em;
margin-top: 1em;
}
.qr--item {
margin-left: 0.5em;
&:nth-child(2n + 1) {
background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.25);
}
}
}
}
}
}

View File

@ -213,6 +213,7 @@ export class SlashCommandClosure {
let done = 0;
for (const executor of this.executorList) {
this.onProgress?.(done, this.commandCount);
this.debugController?.setExecutor(executor);
yield executor;
if (executor instanceof SlashCommandClosureExecutor) {
const closure = this.scope.getVariable(executor.name);

View File

@ -3,6 +3,7 @@ import { SlashCommandExecutor } from './SlashCommandExecutor.js';
export class SlashCommandDebugController {
/**@type {SlashCommandClosure[]} */ stack = [];
/**@type {SlashCommandExecutor[]} */ cmdStack = [];
/**@type {boolean[]} */ stepStack = [];
/**@type {boolean} */ isStepping = false;
/**@type {boolean} */ isSteppingInto = false;
@ -31,9 +32,14 @@ export class SlashCommandDebugController {
}
up() {
this.stack.pop();
while (this.cmdStack.length > this.stack.length) this.cmdStack.pop();
this.stepStack.pop();
}
setExecutor(executor) {
this.cmdStack[this.stack.length - 1] = executor;
}
resume() {