demo:
full integration code for Keyboard Maestro
:
function TaskPaperContextScript(editor, options) {
const outline = editor.outline;
const selection = editor.selection;
let childTexts = [];
// dont support multiselect for now
if (editor.selection.selectedItems.length !== 1) return;
const hasChildren = item => item.children?.length > 0;
const firstSelectedItem = editor.selection.selectedItems[0];
const selectedItem = hasChildren(firstSelectedItem)?firstSelectedItem:firstSelectedItem.parent;
selectedItem.children.forEach(child => {
childTexts.push(child.bodyContentString);
});
// TODO: fix bug where context (previous variables) seem to still be in memory
const evaluateEachLineInBlob = inputBlob => {
const lines = inputBlob.split('\n');
// sanitize lines
const sanitizedLines = lines.map(line => line.split('==>')[0].trim());
// convert to script
const scriptLines = sanitizedLines.map((line, lineI) => {
if (!line.trim()) { // empty
return line;
} else if (line.includes('=>')) { // function
return line;
} else if (line.includes('=')) { // equation
return line;
} else if (line.startsWith('//')) { // comment
return line;
} else { // unknown
return `// ${line}`;
}
});
// execute each line in script
let didError = false;
const resultOfEachLine = scriptLines.map((scriptLine, scriptLineI) => {
if (didError) return scriptLine;
const prevLines = scriptLines.slice(0, scriptLineI);
const isEquation = !scriptLine.includes('=>') && scriptLine.includes('=');
if (isEquation) {
const [lhs, rhs] = scriptLine.split('=');
const script = `${prevLines.join('\n') +'\n'+ scriptLine}\n${lhs.trim()}`;
let result;
try {
result = eval(script);
} catch(e) {
didError = true;
let errHint = '';
const errRawFirstLine = e.toString().split('\n')[0];
if (errRawFirstLine.startsWith('ReferenceError: ')) {
errHint = errRawFirstLine
.replace('ReferenceError: ', '')
.replace('is not defined', '')
.replace('Can\'t find variable: ', '')
.trim();
}
result = ` // ERROR(${errHint})`; //! add error var
}
return result.toLocaleString();
} else {
return '';
}
});
const output = scriptLines.map((scriptLine, i) => `${scriptLine}${resultOfEachLine[i]?(' ==> '+resultOfEachLine[i]):''}`);
return output.join('\n');
};
const inputMathBlob = childTexts.join('\n');
const outputMathBlob = evaluateEachLineInBlob(inputMathBlob);
const outputMathBlobLines = outputMathBlob.split('\n');
const setRealAns = () => {
outline.groupUndoAndChanges(() => {
selectedItem.children.forEach((child, i) => {
const curContent = child.bodyContentString;
const newContent = outputMathBlobLines[i];
if (curContent === newContent) return;
child.bodyContentString = newContent;
});
});
if (editor.selection.selectedItems[0] !== firstSelectedItem) {
editor.moveSelectionToItems(firstSelectedItem);
}
};
const setRefreshingAnimationFrame = () => {
outline.groupUndoAndChanges(() => {
selectedItem.children.forEach((child, i) => {
const curText = child.bodyContentString;
const newText = childTexts[i].split('==>')[0];
if (curText === newText) return;
child.bodyContentString = newText;
});
});
};
setRefreshingAnimationFrame();
setTimeout(setRealAns, 250);
}
Application("TaskPaper").documents[0].evaluate({script: TaskPaperContextScript.toString()});
disclaimer: mvp prototype, use at your own risk