Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ qx.Class.define("osparc.conversation.NotificationUI", {
this.getChildControl("menu-button").hide();
this.getChildControl("message-bubble").exclude();

const isMyMessage = osparc.conversation.MessageUI.isMyMessage(message);
const isMyMessage = osparc.data.model.Message.isMyMessage(message);

let msgContent = "🔔 ";
const messageContent = this.getChildControl("notification-content");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ qx.Theme.define("osparc.theme.mixin.Color", {

"visual-blue": "#007fd4", // Visual Studio blue

"logger-warning-message": "warning-yellow",
"logger-error-message": "failed-red",

"workbench-edge": "#787878",
"workbench-edge-selected": "busy-orange",

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
/* ************************************************************************

osparc - the simcore frontend

https://osparc.io

Copyright:
2026 IT'IS Foundation, https://itis.swiss

License:
MIT: https://opensource.org/licenses/MIT

Authors:
* Odei Maiz (odeimaiz)

************************************************************************ */

/**
* Row renderer for the Logger table that shows a popup with
* the full log message, timestamp, Node label, and log level when a row is clicked.
*
* The popup is added to the application root to avoid
* qooxdoo table pane clipping constraints (overflow:hidden at multiple levels).
*/
qx.Class.define("osparc.ui.table.rowrenderer.PopUpLogMessage", {
extend: qx.ui.table.rowrenderer.Default,

construct: function(table, messageColPos) {
this.base(arguments);

this.__table = table;
this.__messageColPos = messageColPos;
},

statics: {
LOG_LEVEL_INFO: {
"-1": {
label: "DEBUG",
textColor: "white",
bgColor: "product-color"
},
"0": {
label: "INFO",
textColor: "white",
bgColor: "product-color"
},
"1": {
label: "WARNING",
textColor: "black",
bgColor: "warning-yellow"
},
"2": {
label: "ERROR",
textColor: "black",
bgColor: "failed-red"
},
},
},

members: {
__table: null,
__messageColPos: null,
__popup: null,
__activeRowIndex: null,
__scrollHandler: null,
__clickHandler: null,

__removeGlobalListeners: function() {
if (this.__scrollHandler) {
document.removeEventListener("scroll", this.__scrollHandler, true);
this.__scrollHandler = null;
}
if (this.__clickHandler) {
document.removeEventListener("mousedown", this.__clickHandler, true);
this.__clickHandler = null;
}
},

__closePopup: function() {
this.__removeGlobalListeners();
if (this.__popup) {
const root = qx.core.Init.getApplication().getRoot();
if (root.indexOf(this.__popup) >= 0) {
root.remove(this.__popup);
}
this.__popup.dispose();
this.__popup = null;
}
this.__activeRowIndex = null;
},

__createBadge: function(rowData) {
if (!rowData || rowData.logLevel == null) {
return null;
}
const entry = this.self().LOG_LEVEL_INFO[String(rowData.logLevel)];
if (!entry) {
return null;
}
const badge = new qx.ui.basic.Label(entry.label).set({
font: "text-10",
padding: [2, 8],
textColor: entry.textColor,
backgroundColor: entry.bgColor,
decorator: "rounded",
allowGrowY: false,
allowGrowX: false,
});
return badge;
},

__showPopup: function(rowElem, rowIndex, rowData) {
this.__closePopup();

const rect = rowElem.getBoundingClientRect();

// Container
const popup = new qx.ui.container.Composite(new qx.ui.layout.VBox(4)).set({
backgroundColor: "background-main-1",
padding: 8,
maxHeight: Math.round(window.innerHeight * 0.5),
width: Math.round(rect.width),
zIndex: osparc.utils.Utils.FLOATING_Z_INDEX,
});

// Header
const header = new qx.ui.container.Composite(new qx.ui.layout.HBox(10).set({
alignY: "middle",
})).set({
paddingBottom: 4,
});

// Log level badge
const badge = this.__createBadge(rowData);
if (badge) {
header.add(badge);
}

// Timestamp
if (rowData && rowData.timeStamp) {
header.add(new qx.ui.basic.Label(String(rowData.timeStamp)).set({
font: "text-11",
textColor: "text-opa70",
}));
}

// Node
if (rowData && rowData.label) {
header.add(new qx.ui.basic.Label(rowData.label).set({
font: "text-11",
textColor: "text-opa70",
}));
}

// Spacer
header.add(new qx.ui.core.Spacer(), {
flex: 1
});

// Copy button
const copyBtn = osparc.utils.Utils.getCopyButton().set({
backgroundColor: "transparent",
padding: 2,
});
copyBtn.addListener("execute", () => {
const text = osparc.widget.logger.LoggerView.printRow(rowData);
osparc.utils.Utils.copyTextToClipboard(text);
});
header.add(copyBtn);

// Close button
const closeBtn = new qx.ui.form.Button(null, "@MaterialIcons/close/14").set({
backgroundColor: "transparent",
allowGrowY: false,
padding: 2,
});
closeBtn.addListener("execute", () => this.__closePopup());
header.add(closeBtn);

popup.add(header);

// Divider
const divider = new qx.ui.core.Widget().set({
height: 1,
backgroundColor: "text-opa30",
allowGrowY: false,
});
popup.add(divider);

// Message body (scrollable)
const scroll = new qx.ui.container.Scroll();
const messageLabel = new qx.ui.basic.Label(rowData.msgRich || rowData.msg || "").set({
rich: true,
selectable: true,
wrap: true,
});
scroll.add(messageLabel);
popup.add(scroll, { flex: 1 });

// Position: anchor to row, flip above if near bottom
const root = qx.core.Init.getApplication().getRoot();
const viewportHeight = window.innerHeight;
let top;
if (rect.bottom > viewportHeight * 0.65) {
// Place above: we'll adjust after rendering
top = Math.max(0, Math.round(rect.top) - 200);
} else {
top = Math.round(rect.top);
}
root.add(popup, {
left: Math.round(rect.left),
top: top,
});

// Adjust position after popup is rendered and has actual height
if (rect.bottom > viewportHeight * 0.65) {
popup.addListenerOnce("appear", () => {
const bounds = popup.getBounds();
if (bounds) {
const adjustedTop = Math.max(0, Math.round(rect.bottom) - bounds.height);
popup.setLayoutProperties({
left: Math.round(rect.left),
top: adjustedTop,
});
}
});
}

this.__popup = popup;
this.__activeRowIndex = rowIndex;

// Close on scroll since the fixed position becomes stale
this.__scrollHandler = () => this.__closePopup();
document.addEventListener("scroll", this.__scrollHandler, true);

// Close on click outside
this.__clickHandler = e => {
if (popup.isDisposed()) {
this.__removeGlobalListeners();
return;
}
const popupElem = popup.getContentElement().getDomElement();
if (popupElem && !popupElem.contains(e.target)) {
this.__closePopup();
}
};
popup.addListenerOnce("appear", () => {
setTimeout(() => document.addEventListener("mousedown", this.__clickHandler, true), 0);
});
},

// overridden
updateDataRowElement: function(rowInfo, rowElem) {
this.base(arguments, rowInfo, rowElem);

const self = this;
const rowIndex = rowInfo.row;
// Capture rowData now — rowInfo is a shared mutable object reused across rows
const rowData = rowInfo.rowData;
rowElem.style.cursor = "pointer";
rowElem.onclick = function() {
if (self.__activeRowIndex === rowIndex) {
self.__closePopup();
} else {
self.__showPopup(rowElem, rowIndex, rowData);
}
};
}
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
*
* <pre class='javascript'>
* let tableModel = this.__logModel = new osparc.widget.logger.LoggerTable();
* tableModel.setColumns(["Level", "Time", "Origin", "Message"], ["level", "time", "who", "whatRich"]);
* tableModel.setColumns(["Level", "Time", "Node", "Message"], ["level", "time", "who", "msgRich"]);
* let custom = {
* tableColumnModel : function(obj) {
* return new qx.ui.table.columnmodel.Resize(obj);
Expand All @@ -51,7 +51,7 @@ qx.Class.define("osparc.widget.logger.LoggerModel", {
this.setColumns([
"",
"Time",
"Origin",
"Node",
"Message"
], [
"level",
Expand All @@ -78,6 +78,8 @@ qx.Class.define("osparc.widget.logger.LoggerModel", {
},

statics: {
MAX_ROWS: 2000,

getLevelIcon: function(logLevel) {
const logLevels = osparc.widget.logger.LoggerView.LOG_LEVELS;
let iconSource = "";
Expand Down Expand Up @@ -121,6 +123,11 @@ qx.Class.define("osparc.widget.logger.LoggerModel", {

this.__rawData.push(newRow);
});

const max = this.self().MAX_ROWS;
if (this.__rawData.length > max) {
this.__rawData.splice(0, this.__rawData.length - max);
}
},

nodeLabelChanged: function(nodeId, newLabel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
* - some log type filtering buttons
* - log messages table
*
* Log messages have two inputs: "Origin" and "Message".
* Log messages have two inputs: "Node" and "Message".
*
* Depending on the log level, "Origin"'s color will change, also "Message"s coming from the same
* origin will be rendered with the same color.
* Depending on the log level, "Node"'s color will change, also "Message"s coming from the same
* node will be rendered with the same color.
*
* *Example*
*
Expand Down Expand Up @@ -298,7 +298,7 @@ qx.Class.define("osparc.widget.logger.LoggerView", {
resizeBehavior.setWidth(this.self().POS.TIMESTAMP, 80);
resizeBehavior.setWidth(this.self().POS.ORIGIN, 100);

table.setDataRowRenderer(new osparc.ui.table.rowrenderer.ExpandSelection(table, this.self().POS.MESSAGE));
table.setDataRowRenderer(new osparc.ui.table.rowrenderer.PopUpLogMessage(table, this.self().POS.MESSAGE));
Comment thread
odeimaiz marked this conversation as resolved.

this.__applyFilters();

Expand Down
Loading