From 401c0885a16541797ec3fb05862214c4d5352b7b Mon Sep 17 00:00:00 2001 From: Ida Dittrich Date: Fri, 2 Jan 2026 11:51:01 +0100 Subject: [PATCH] fix: fixed styling of log messages --- .../UiComponents/Log/Log.module.css | 67 ++++++++---- src/components/UiComponents/Log/Log.tsx | 103 ++++++++++-------- src/hooks/playground/useDashboardLogTree.ts | 58 ++++++++-- 3 files changed, 157 insertions(+), 71 deletions(-) diff --git a/src/components/UiComponents/Log/Log.module.css b/src/components/UiComponents/Log/Log.module.css index 9f9800f..445c963 100644 --- a/src/components/UiComponents/Log/Log.module.css +++ b/src/components/UiComponents/Log/Log.module.css @@ -12,7 +12,10 @@ padding: 16px 20px; display: flex; flex-direction: column; - gap: 16px; + min-width: 0; + overflow-x: hidden; + box-sizing: border-box; + max-width: 100%; } .emptyState { @@ -33,18 +36,25 @@ flex-direction: column; gap: 12px; margin-bottom: 16px; + min-width: 0; + max-width: 100%; + box-sizing: border-box; + overflow-x: hidden; } .dashboardContainer { display: flex; flex-direction: column; - gap: 4px; - width: 100%; + min-width: 0; + overflow-x: hidden; + box-sizing: border-box; + max-width: 100%; } .dashboardContainer > .operationNode { - width: 100%; + min-width: 0; + max-width: 100%; } .operationNode { @@ -52,7 +62,8 @@ flex-direction: column; gap: 0; position: relative; - margin-bottom: 2px; + min-width: 0; + box-sizing: border-box; } .operationNodeIndented { @@ -76,23 +87,21 @@ flex-direction: row !important; align-items: flex-start; min-height: 32px; - width: 100%; + min-width: 0; + box-sizing: border-box; } .operationContent { flex: 1; display: flex !important; flex-direction: column !important; - gap: 4px; min-width: 0; - width: 100%; + box-sizing: border-box; } .operationHeader { display: flex !important; - flex-direction: row !important; - align-items: center !important; - gap: 8px; + flex-direction: column !important; padding: 6px 12px; background-color: var(--color-surface); border: 1px solid var(--color-border); @@ -101,7 +110,19 @@ transition: background-color 0.2s ease, border-color 0.2s ease; min-height: 32px; flex: 1; - width: 100%; + min-width: 0; + overflow: hidden; + margin-top: 4px; + box-sizing: border-box; +} + +.operationHeaderRow { + display: flex !important; + flex-direction: row !important; + align-items: center !important; + gap: 4px; + min-width: 0; + box-sizing: border-box; } .expandButton { @@ -181,6 +202,7 @@ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + max-width: 100%; } .statusMessageTag { @@ -246,7 +268,7 @@ background-color: var(--color-highlight-gray); border-radius: 2px; overflow: hidden; - margin-top: 4px; + width: 100%; } .progressBar { @@ -268,37 +290,41 @@ /* Log messages container */ .operationLogsContainer { - margin-top: 8px; display: flex; flex-direction: column; - gap: 4px; + min-width: 0; + box-sizing: border-box; } .operationLogsList { display: flex; flex-direction: column; - gap: 6px; padding-left: 0; border-left: 1px solid var(--color-border); - margin-left: 12px; + margin-left: 30px; /* Align with header content: operationNode paddingLeft (12px) + header padding (12px) */ + min-width: 0; + box-sizing: border-box; } .logEntry { display: flex; flex-direction: column; - gap: 4px; padding: 6px 8px; background-color: var(--color-surface); border-radius: var(--object-radius-small); border: 1px solid var(--color-border); + min-width: 0; + box-sizing: border-box; + margin-top: 4px; } .logEntryHeader { display: flex; align-items: center; - gap: 8px; font-size: 11px; flex-wrap: wrap; + min-width: 0; + box-sizing: border-box; } .logTimestamp { @@ -313,8 +339,11 @@ color: var(--color-text); line-height: 1.4; word-wrap: break-word; + word-break: break-word; + overflow-wrap: break-word; flex: 1; min-width: 0; + box-sizing: border-box; } .logProgress { diff --git a/src/components/UiComponents/Log/Log.tsx b/src/components/UiComponents/Log/Log.tsx index 548888e..369d479 100644 --- a/src/components/UiComponents/Log/Log.tsx +++ b/src/components/UiComponents/Log/Log.tsx @@ -94,7 +94,6 @@ const Log: React.FC = ({ const operationTimestamp = latestLog?.timestamp; // Calculate consistent indentation per level (24px per level) - const indentPx = depth * 24; const hasIndent = depth > 0; // Calculate log entry indentation to align with operation name @@ -107,13 +106,24 @@ const Log: React.FC = ({ // Root: 28px - 0 = 28px // Indented: 28px - 12px = 16px const logIndentPx = hasIndent ? 16 : 28; + + // Calculate header indentation to match message indentation + // Headers are inside operationNode which has paddingLeft: 12px (for indented) + // Messages container has marginLeft: logIndentPx from operationNode, and list has margin-left: 12px + // So messages start at: logIndentPx + 12px from operationNode's left edge + // Headers start at: operationNode marginLeft + paddingLeft = headerIndentPx + 12px + // To align: headerIndentPx + 12px = logIndentPx + 12px, so headerIndentPx = logIndentPx + // But headers have their own padding (12px), so header content starts at headerIndentPx + 12px + 12px + // Messages start at logIndentPx + 12px, so we need headerIndentPx = logIndentPx - 12px to align content + // Actually, we want the header box to align with messages, so headerIndentPx should account for header padding + const headerIndentPx = logIndentPx; // Headers and messages both use logIndentPx, paddingLeft handles alignment return (
= ({ {/* Operation content */}
- {hasContentToExpand && ( - + )} + {!hasContentToExpand && } + + {operationName} + + {/* Latest status message tag (updates with each poll) */} + {latestMessage && ( + + {latestMessage} - - )} - {!hasContentToExpand && } - - {operationName} - - {/* Latest status message tag (updates with each poll) */} - {latestMessage && ( - - {latestMessage} + )} + + {operationTimestamp && ( + + {formatLogTimestamp(operationTimestamp)} + + )} + + + {operationStatus} - )} - - {operationTimestamp && ( - - {formatLogTimestamp(operationTimestamp)} - - )} - - - {operationStatus} - + + {progressPercentage > 0 && ( + + {Math.round(progressPercentage)}% + + )} +
{progressPercentage > 0 && ( - - {Math.round(progressPercentage)}% - +
+
= 100 ? styles.progressCompleted : ''}`} + style={{ width: `${progressPercentage}%` }} + /> +
)}
- - {progressPercentage > 0 && ( -
-
= 100 ? styles.progressCompleted : ''}`} - style={{ width: `${progressPercentage}%` }} - /> -
- )}
@@ -180,7 +192,10 @@ const Log: React.FC = ({
diff --git a/src/hooks/playground/useDashboardLogTree.ts b/src/hooks/playground/useDashboardLogTree.ts index baf3633..0160b46 100644 --- a/src/hooks/playground/useDashboardLogTree.ts +++ b/src/hooks/playground/useDashboardLogTree.ts @@ -71,13 +71,23 @@ export function useDashboardLogTree() { // Log messages are status updates and should go in latestMessage, not operationName let operationName = existingOperation?.operationName || null; if (operationName === null) { - // Format operationId by splitting on dashes/underscores and capitalizing + // Remove UUIDs and timestamps from operationId before formatting + // UUID pattern: 8-4-4-4-12 hex digits (e.g., "1e6d7b14-4f30-40e2-b7a6-748b63b6a7f5") + // Also remove standalone long hex strings that might be timestamps or IDs + let cleanedId = operationId + .replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, '') // Remove UUIDs + .replace(/\b[0-9a-f]{32,}\b/gi, '') // Remove long hex strings (timestamps/IDs) + .replace(/\s+/g, ' ') // Normalize whitespace + .trim(); + + // Format by splitting on dashes/underscores and capitalizing // This creates a stable, readable name like "Workflow Planning" from "workflow-planning" - const formattedName = operationId - .split(/[-_]/) + const formattedName = cleanedId + .split(/[-_\s]+/) + .filter(word => word.length > 0) // Remove empty strings .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) .join(' '); - operationName = formattedName || operationId; + operationName = formattedName || operationId.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, '').trim(); } // Update latest message (for status tag) - this updates with each poll @@ -120,7 +130,25 @@ export function useDashboardLogTree() { rootOpsSet.add(opId); } }); - newTree.rootOperations = Array.from(rootOpsSet).sort(); // Alphabetical sort + // Sort by timestamp of earliest log entry (chronological order) + newTree.rootOperations = Array.from(rootOpsSet).sort((opIdA, opIdB) => { + const opA = newTree.operations.get(opIdA); + const opB = newTree.operations.get(opIdB); + if (!opA || !opB) return 0; + + // Get earliest log timestamp for each operation + const logsA = Array.from(opA.logs.values()); + const logsB = Array.from(opB.logs.values()); + + if (logsA.length === 0 && logsB.length === 0) return 0; + if (logsA.length === 0) return 1; // Put operations without logs at the end + if (logsB.length === 0) return -1; + + const earliestA = Math.min(...logsA.map(log => log.timestamp || 0)); + const earliestB = Math.min(...logsB.map(log => log.timestamp || 0)); + + return earliestA - earliestB; // Ascending order (oldest first) + }); return newTree; }); @@ -179,10 +207,24 @@ export function useDashboardLogTree() { const getChildOperations = useCallback((parentId: string | null): string[] => { const currentTree = treeRef.current; - return Array.from(currentTree.operations.entries()) + const childOps = Array.from(currentTree.operations.entries()) .filter(([_, op]) => op.parentId === parentId) - .map(([opId]) => opId) - .sort(); // Alphabetical sort + .map(([opId, op]) => ({ opId, op })); + + // Sort by timestamp of earliest log entry (chronological order) + return childOps.sort((a, b) => { + const logsA = Array.from(a.op.logs.values()); + const logsB = Array.from(b.op.logs.values()); + + if (logsA.length === 0 && logsB.length === 0) return 0; + if (logsA.length === 0) return 1; // Put operations without logs at the end + if (logsB.length === 0) return -1; + + const earliestA = Math.min(...logsA.map(log => log.timestamp || 0)); + const earliestB = Math.min(...logsB.map(log => log.timestamp || 0)); + + return earliestA - earliestB; // Ascending order (oldest first) + }).map(({ opId }) => opId); }, []); return {