Skip to content
Open
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
36 changes: 36 additions & 0 deletions electron-ui/graph-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ class GraphRenderer {
};

this.cy = null;
this.resizeObserver = null;
this.boundWindowResize = this._handleResize.bind(this);
this.resizeFrame = null;
this.currentLayout = this.options.layout;
this.pendingChanges = {
nodes: { create: [], update: [], delete: [] },
Expand All @@ -58,6 +61,7 @@ class GraphRenderer {
};

this._initCytoscape();
this._initResizeHandling();
}

/**
Expand Down Expand Up @@ -275,6 +279,29 @@ class GraphRenderer {

this._setupEventHandlers();
}

_initResizeHandling() {
if (typeof ResizeObserver !== 'undefined') {
this.resizeObserver = new ResizeObserver(() => this._handleResize());
this.resizeObserver.observe(this.container);
}

window.addEventListener('resize', this.boundWindowResize);
}

_handleResize() {
if (!this.cy) return;

if (this.resizeFrame) {
window.cancelAnimationFrame(this.resizeFrame);
}

this.resizeFrame = window.requestAnimationFrame(() => {
this.resizeFrame = null;
if (!this.cy) return;
this.cy.resize();
});
}

/**
* Set up event handlers
Expand Down Expand Up @@ -912,6 +939,15 @@ class GraphRenderer {
* Destroy the renderer
*/
destroy() {
if (this.resizeObserver) {
this.resizeObserver.disconnect();
this.resizeObserver = null;
}
window.removeEventListener('resize', this.boundWindowResize);
if (this.resizeFrame) {
window.cancelAnimationFrame(this.resizeFrame);
this.resizeFrame = null;
}
if (this.cy) {
this.cy.destroy();
this.cy = null;
Expand Down
95 changes: 75 additions & 20 deletions electron-ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@
</svg>
<span class="nav-label">Agents</span>
</button>
<button class="nav-btn" data-tab="cases" title="Investigations and Case Reports">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
<polyline points="14 2 14 8 20 8"/>
<line x1="8" y1="13" x2="16" y2="13"/>
<line x1="8" y1="17" x2="14" y2="17"/>
</svg>
<span class="nav-label">Cases</span>
</button>
<button class="nav-btn" data-tab="database" title="Database">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<ellipse cx="12" cy="5" rx="9" ry="3"/>
Expand Down Expand Up @@ -635,6 +644,56 @@ <h3>Subsystem Agents</h3>
</div>
</section>

<!-- Cases Tab -->
<section class="tab-content" id="tab-cases">
<header class="tab-header">
<h2>Cases & Reports</h2>
<p>Promote agent events into investigations, track notes, and generate post-incident reports</p>
</header>

<div class="cases-toolbar">
<div class="cases-toolbar-copy">
<span class="status-chip" id="cases-status-chip">Cases</span>
<span class="cases-status-text" id="cases-status-text">Select a case or create one from the Agents tab.</span>
</div>
<div class="cases-toolbar-actions">
<select class="input input-sm" id="cases-filter-status">
<option value="">All statuses</option>
<option value="open">Open</option>
<option value="in_review">In Review</option>
<option value="closed">Closed</option>
</select>
<button class="btn btn-ghost" id="btn-cases-refresh">Refresh</button>
<button class="btn btn-secondary" id="btn-cases-generate-report" disabled>Generate Report</button>
<button class="btn btn-primary" id="btn-cases-save-report" disabled>Save Report</button>
</div>
</div>

<div class="cases-layout">
<aside class="cases-sidebar">
<div class="cases-sidebar-header">
<h3>Investigations</h3>
<span id="cases-count-label">0 cases</span>
</div>
<div class="cases-list" id="cases-list">
<div class="cases-empty">No investigation cases yet. Promote an anomaly from the Agents tab to start one.</div>
</div>
</aside>

<section class="cases-detail" id="cases-detail">
<div class="cases-empty cases-empty-detail">Choose a case to inspect the event context, capture notes, and generate a report.</div>
</section>
</div>

<div class="output-panel cases-log-panel" id="cases-log-panel">
<div class="output-header">
<h4>Case Python Log</h4>
<button class="btn btn-sm btn-ghost" id="btn-clear-cases-log">Clear</button>
</div>
<pre class="output-content" id="cases-log"></pre>
</div>
</section>

<!-- Database Tab -->
<section class="tab-content" id="tab-database">
<header class="tab-header">
Expand Down Expand Up @@ -700,26 +759,6 @@ <h2>Ontology Graph</h2>
</header>

<div class="graph-layout">
<!-- Left Sidebar: Pending Changes -->
<aside class="graph-sidebar graph-sidebar-left" id="graph-changes-panel">
<div class="sidebar-section-collapsible" id="pending-changes-section">
<button class="section-trigger" onclick="this.parentElement.classList.toggle('collapsed')">
<span>Pending Changes</span>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
</button>
<div class="section-body">
<div class="pending-changes-summary" id="pending-changes-summary">
<p class="no-changes text-muted text-sm">No pending changes</p>
</div>
<div class="pending-changes-list" id="pending-changes-list"></div>
<div class="pending-changes-actions">
<button class="btn btn-sm btn-primary" id="btn-apply-changes" disabled>Apply All</button>
<button class="btn btn-sm btn-secondary" id="btn-discard-changes" disabled>Discard</button>
</div>
</div>
</div>
</aside>

<!-- Center: Graph Canvas -->
<div class="graph-main">
<div class="toolbar">
Expand Down Expand Up @@ -1027,6 +1066,22 @@ <h4>Add New Node</h4>
<button class="btn btn-secondary" id="btn-add-node">Add Node</button>
</div>
</div>
<div class="sidebar-section-collapsible pending-changes-docked" id="pending-changes-section">
<button class="section-trigger" onclick="this.parentElement.classList.toggle('collapsed')">
<span>Pending Changes</span>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
</button>
<div class="section-body">
<div class="pending-changes-summary" id="pending-changes-summary">
<p class="no-changes text-muted text-sm">No pending changes</p>
</div>
<div class="pending-changes-list" id="pending-changes-list"></div>
<div class="pending-changes-actions">
<button class="btn btn-sm btn-primary" id="btn-apply-changes" disabled>Apply All</button>
<button class="btn btn-sm btn-secondary" id="btn-discard-changes" disabled>Discard</button>
</div>
</div>
</div>
</aside>
</div>
</section>
Expand Down
Loading