Merge branch 'api-log-capture'

This commit is contained in:
zadam 2022-09-17 23:06:42 +02:00
commit ea35b0c800
8 changed files with 188 additions and 3 deletions

View File

@ -79,6 +79,7 @@ import FloatingButtons from "../widgets/floating_buttons/floating_buttons.js";
import RelationMapButtons from "../widgets/floating_buttons/relation_map_buttons.js";
import MermaidExportButton from "../widgets/floating_buttons/mermaid_export_button.js";
import EditableCodeButtonsWidget from "../widgets/type_widgets/editable_code_buttons.js";
import ApiLogWidget from "../widgets/api_log.js";
export default class DesktopLayout {
constructor(customWidgets) {
@ -197,6 +198,7 @@ export default class DesktopLayout {
.child(new SqlResultWidget())
)
.child(new EditableCodeButtonsWidget())
.child(new ApiLogWidget())
.child(new FindWidget())
.child(
...this.customWidgets.get('node-detail-pane'), // typo, let's keep it for a while as BC

View File

@ -13,6 +13,7 @@ import appContext from "./app_context.js";
import NoteContextAwareWidget from "../widgets/note_context_aware_widget.js";
import NoteContextCachingWidget from "../widgets/note_context_caching_widget.js";
import BasicWidget from "../widgets/basic_widget.js";
import SpacedUpdate from "./spaced_update.js";
/**
* This is the main frontend API interface for scripts. It's published in the local "api" object.
@ -594,6 +595,33 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
* @returns {string} random string
*/
this.randomString = utils.randomString;
this.logMessages = {};
this.logSpacedUpdates = {};
/**
* Log given message to the log pane in UI
*
* @param message
*/
this.log = message => {
const {noteId} = this.startNote;
message = utils.now() + ": " + message;
console.log(`Script ${noteId}: ${message}`);
this.logMessages[noteId] = this.logMessages[noteId] || [];
this.logSpacedUpdates[noteId] = this.logSpacedUpdates[noteId] || new SpacedUpdate(() => {
const messages = this.logMessages[noteId];
this.logMessages[noteId] = [];
appContext.triggerEvent("apiLogMessages", {noteId, messages});
}, 100);
this.logMessages[noteId].push(message);
this.logSpacedUpdates[noteId].scheduleUpdate();
};
}
export default FrontendScriptApi;

View File

@ -3,6 +3,7 @@ import toastService from "./toast.js";
import server from "./server.js";
import options from "./options.js";
import frocaUpdater from "./froca_updater.js";
import appContext from "./app_context.js";
const messageHandlers = [];
@ -118,6 +119,9 @@ async function handleMessage(event) {
else if (message.type === 'consistency-checks-failed') {
toastService.showError("Consistency checks failed! See logs for details.", 50 * 60000);
}
else if (message.type === 'api-log-messages') {
appContext.triggerEvent("apiLogMessages", {noteId: message.noteId, messages: message.messages});
}
}
let entityChangeIdReachedListeners = [];

View File

@ -0,0 +1,54 @@
import NoteContextAwareWidget from "./note_context_aware_widget.js";
const TPL = `
<div class="api-log-widget">
<style>
.api-log-widget {
padding: 15px;
flex-grow: 1;
max-height: 40%;
}
.hidden-api-log {
display: none;
}
.api-log-container {
overflow: auto;
height: 100%;
}
</style>
<div class="api-log-container"></div>
</div>`;
export default class ApiLogWidget extends NoteContextAwareWidget {
isEnabled() {
return this.note
&& this.note.mime.startsWith('application/javascript;env=')
&& super.isEnabled();
}
doRender() {
this.$widget = $(TPL);
this.$widget.addClass("hidden-api-log");
this.$logContainer = this.$widget.find('.api-log-container');
}
async refreshWithNote(note) {
this.$logContainer.empty();
}
apiLogMessagesEvent({messages, noteId}) {
if (!this.isNote(noteId)) {
return;
}
this.$widget.removeClass("hidden-api-log");
for (const message of messages) {
this.$logContainer.append(message).append($("<br>"));
}
}
}

View File

@ -87,4 +87,10 @@ export default class EditableCodeButtonsWidget extends NoteContextAwareWidget {
this.$openTriliumApiDocsButton.toggle(note.mime.startsWith('application/javascript;env='));
}
async noteTypeMimeChangedEvent({noteId}) {
if (this.isNote(noteId)) {
await this.refresh();
}
}
}

View File

@ -14,6 +14,8 @@ const appInfo = require('./app_info');
const searchService = require('./search/services/search');
const SearchContext = require("./search/search_context");
const becca = require("../becca/becca");
const ws = require("./ws");
const SpacedUpdate = require("./spaced_update");
/**
* This is the main backend API interface for scripts. It's published in the local "api" object.
@ -288,12 +290,34 @@ function BackendScriptApi(currentNote, apiParams) {
});
};
this.logMessages = {};
this.logSpacedUpdates = {};
/**
* Log given message to trilium logs.
* Log given message to trilium logs and log pane in UI
*
* @param message
*/
this.log = message => log.info(message);
this.log = message => {
log.info(message);
const {noteId} = this.startNote;
this.logMessages[noteId] = this.logMessages[noteId] || [];
this.logSpacedUpdates[noteId] = this.logSpacedUpdates[noteId] || new SpacedUpdate(() => {
const messages = this.logMessages[noteId];
this.logMessages[noteId] = [];
ws.sendMessageToAllClients({
type: 'api-log-messages',
noteId,
messages
});
}, 100);
this.logMessages[noteId].push(message);
this.logSpacedUpdates[noteId].scheduleUpdate();
};
/**
* Returns root note of the calendar.

View File

@ -0,0 +1,67 @@
class SpacedUpdate {
constructor(updater, updateInterval = 1000) {
this.updater = updater;
this.lastUpdated = Date.now();
this.changed = false;
this.updateInterval = updateInterval;
}
scheduleUpdate() {
if (!this.changeForbidden) {
this.changed = true;
setTimeout(() => this.triggerUpdate());
}
}
async updateNowIfNecessary() {
if (this.changed) {
this.changed = false; // optimistic...
try {
await this.updater();
}
catch (e) {
this.changed = true;
throw e;
}
}
}
isAllSavedAndTriggerUpdate() {
const allSaved = !this.changed;
this.updateNowIfNecessary();
return allSaved;
}
triggerUpdate() {
if (!this.changed) {
return;
}
if (Date.now() - this.lastUpdated > this.updateInterval) {
this.updater();
this.lastUpdated = Date.now();
this.changed = false;
}
else {
// update not triggered but changes are still pending so we need to schedule another check
this.scheduleUpdate();
}
}
async allowUpdateWithoutChange(callback) {
this.changeForbidden = true;
try {
await callback();
}
finally {
this.changeForbidden = false;
}
}
}
module.exports = SpacedUpdate;

View File

@ -67,7 +67,7 @@ function sendMessageToAllClients(message) {
const jsonStr = JSON.stringify(message);
if (webSocketServer) {
if (message.type !== 'sync-failed') {
if (message.type !== 'sync-failed' && message.type !== 'api-log-messages') {
log.info("Sending message to all clients: " + jsonStr);
}