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
109 changes: 109 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 34 additions & 34 deletions src/filigree/static/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

// --- Module imports ---

import { computeHealthScore, computeImpactScores } from "./analytics.js";
import { fetchAllData, fetchDashboardConfig, fetchFileSchema, fetchProjects } from "./api.js";
import {
applyFilters,
Expand All @@ -21,15 +22,15 @@ import {
callbacks as filtersCallbacks,
loadPreset,
loadProjectFilterSettings,
onDoneTimeBoundChange,
populatePresets,
savePreset,
populateTypeFilter,
savePreset,
toggleBlocked,
toggleCardSelect,
toggleMultiSelect,
toggleReady,
toggleStatusPill,
onDoneTimeBoundChange,
trackChanges,
updateTypeFilterUI,
} from "./filters.js";
Expand All @@ -48,9 +49,9 @@ import {
batchSetPriority,
closeSettingsMenu,
copyIssueId,
endTour,
escHtml,
escJsSingleAttr,
endTour,
reloadServer,
showCreateForm,
showToast,
Expand Down Expand Up @@ -82,22 +83,40 @@ import {
showAddBlocker,
updateIssue,
} from "./views/detail.js";
import { computeHealthScore, computeImpactScores } from "./analytics.js";
import {
clearScanSourceFilter,
closeFileDetail,
closeFinding,
createIssueFromFinding,
filesPageNext,
filesPagePrev,
filterFindings,
filterTimeline,
loadFiles,
loadMoreFindings,
loadMoreTimeline,
openFileDetail,
selectFinding,
showLinkIssueModal,
sortFiles,
submitLinkIssue,
switchFileTab,
} from "./views/files.js";
import {
callbacks as graphCallbacks,
graphFit,
renderGraph,
refreshCriticalPathState,
renderGraph,
setCriticalPathStateFromPath,
showHealthBreakdown,
toggleCriticalPath,
} from "./views/graph.js";
import {
renderGraphSidebar,
graphSidebarSelectAll,
attachSidebarListeners,
graphSidebarClearAll,
graphSidebarSelectAll,
rebuildTreeIndex,
attachSidebarListeners,
renderGraphSidebar,
callbacks as sidebarCallbacks,
} from "./views/graphSidebar.js";
import {
Expand All @@ -112,31 +131,7 @@ import {
showStaleIssues,
updateStaleBadge,
} from "./views/metrics.js";
import {
loadPlanView,
loadWorkflow,
loadWorkflowInModal,
showWorkflowModal,
} from "./views/workflow.js";
import {
closeFinding,
clearScanSourceFilter,
closeFileDetail,
createIssueFromFinding,
filesPageNext,
filesPagePrev,
filterFindings,
filterTimeline,
loadFiles,
loadMoreFindings,
loadMoreTimeline,
openFileDetail,
selectFinding,
showLinkIssueModal,
sortFiles,
submitLinkIssue,
switchFileTab,
} from "./views/files.js";
import { loadReady } from "./views/ready.js";
import {
collapseAllReleaseTree,
loadReleases,
Expand All @@ -145,7 +140,12 @@ import {
toggleReleaseExpand,
toggleReleaseTreeNode,
} from "./views/releases.js";
import { loadReady } from "./views/ready.js";
import {
loadPlanView,
loadWorkflow,
loadWorkflowInModal,
showWorkflowModal,
} from "./views/workflow.js";

// ---------------------------------------------------------------------------
// Core data fetching (lives here because it touches every module)
Expand Down
2 changes: 1 addition & 1 deletion src/filigree/static/js/views/detail.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export async function openDetail(issueId) {
`<span class="w-2 h-2 rounded-full shrink-0" style="background:${sc}"></span>` +
`<span class="cursor-pointer flex-1" style="color:var(--accent)" role="button" tabindex="0" aria-label="Open blocker ${escHtml(det.title.slice(0, 40))}" onclick="openDetail('${safeBid}')">${escHtml(det.title.slice(0, 40))}</span>` +
`<span style="color:var(--text-muted)">${escHtml(det.status || "")}</span>` +
`<button onclick="event.stopPropagation();removeDependency('${safeIssueId}','${safeBid}')" class="text-red-400 hover:text-red-300 ml-1" title="Remove dependency">&times;</button></div>`
`<button onclick="event.stopPropagation();removeDependency('${safeIssueId}','${safeBid}')" class="text-red-400 hover:text-red-300 ml-1" aria-label="Remove dependency" title="Remove dependency">&times;</button></div>`
);
})
.join("");
Expand Down
4 changes: 2 additions & 2 deletions src/filigree/static/js/views/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ export async function loadFiles() {
'<span class="font-medium" style="color:var(--text-primary)">' +
escHtml(state.filesScanSource) +
"</span>" +
'<button onclick="clearScanSourceFilter()" class="ml-1 rounded-full px-1.5" style="color:var(--text-muted)" title="Clear filter">&times;</button>' +
'<button onclick="clearScanSourceFilter()" class="ml-1 rounded-full px-1.5" style="color:var(--text-muted)" title="Clear filter" aria-label="Clear filter">&times;</button>' +
"</div>"
: "";

Expand Down Expand Up @@ -346,7 +346,7 @@ function renderFileDetail(data) {
// Header
let html =
'<div class="flex items-center gap-2 mb-4">' +
'<button onclick="closeFileDetail()" class="text-xs bg-overlay px-2 py-1 rounded bg-overlay-hover" title="Close">&times;</button>' +
'<button onclick="closeFileDetail()" class="text-xs bg-overlay px-2 py-1 rounded bg-overlay-hover" aria-label="Close file detail" title="Close">&times;</button>' +
`<span class="text-sm font-semibold truncate" style="color:var(--text-primary)" title="${escHtml(f.path)}">${escHtml(f.path)}</span>` +
"</div>";

Expand Down
11 changes: 5 additions & 6 deletions src/filigree/static/js/views/graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { fetchCriticalPath } from "../api.js";
import { CATEGORY_COLORS, state, THEME_COLORS } from "../state.js";
import { resolveGraphScope, handleGhostClick } from "./graphSidebar.js";
import { handleGhostClick, resolveGraphScope } from "./graphSidebar.js";

// --- Callbacks for functions not yet available at import time ---

Expand Down Expand Up @@ -319,7 +319,7 @@ export function renderGraph() {
const filteredIds = new Set(filteredNodes.map((n) => n.id));
const search = document.getElementById("filterSearch")?.value?.toLowerCase().trim() || "";

let cyNodes = filteredNodes.map((n) => {
const cyNodes = filteredNodes.map((n) => {
const title = n.title || n.id;
const isGhost = ghostIds.has(n.id);
const matchesSearch =
Expand All @@ -340,7 +340,7 @@ export function renderGraph() {
};
});

let cyEdges = scopeEdges
const cyEdges = scopeEdges
.filter((e) => filteredIds.has(e.source) && filteredIds.has(e.target))
.map((e) => ({
data: {
Expand Down Expand Up @@ -407,8 +407,7 @@ export function renderGraph() {
state.cy.destroy();

const canReusePositions =
cyNodes.length > 0 &&
cyNodes.every((n) => Object.prototype.hasOwnProperty.call(previousPositions, n.data.id));
cyNodes.length > 0 && cyNodes.every((n) => Object.hasOwn(previousPositions, n.data.id));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep graph frontend contract tests passing

In this checkout, uv run pytest tests/api/test_graph_api.py -q now fails in TestGraphFrontendContracts.test_topology_change_reuses_positions_only_when_all_nodes_have_positions because that contract test still asserts the previous Object.prototype.hasOwnProperty.call(...) guard. Since this formatter change rewrites the guarded position-reuse check without updating the frontend contract, the Python test suite is left red for any normal validation run even though the accessibility-only change should not break tests.

Useful? React with 👍 / 👎.

const graphMinZoom = computeGraphMinZoom(cyNodes.length);
state.cy = cytoscape({
container,
Expand Down Expand Up @@ -495,7 +494,7 @@ export function showHealthBreakdown() {
panel.innerHTML =
'<div class="flex items-center justify-between mb-3">' +
`<span class="text-base font-semibold" style="color:var(--text-primary)">System Health: ${b.score}/100</span>` +
'<button onclick="closeDetail()" class="text-muted text-primary-hover text-lg">&times;</button></div>' +
'<button onclick="closeDetail()" class="text-muted text-primary-hover text-lg" aria-label="Close system health detail" title="Close">&times;</button></div>' +
["blocked", "freshness", "ready", "balance"]
.map((k) => {
const f = b[k];
Expand Down
2 changes: 1 addition & 1 deletion src/filigree/static/js/views/health.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Code Health view — hotspots, severity donut, scan coverage, recent scans.
// ---------------------------------------------------------------------------

import { fetchFiles, fetchFileStats, fetchHotspots, fetchScanRuns } from "../api.js";
import { fetchFileStats, fetchFiles, fetchHotspots, fetchScanRuns } from "../api.js";
import { SEVERITY_COLORS, state } from "../state.js";
import { escHtml, escJsSingleAttr, relativeTime } from "../ui.js";

Expand Down
2 changes: 1 addition & 1 deletion src/filigree/static/js/views/ready.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
// ---------------------------------------------------------------------------

import { fetchReady } from "../api.js";
import { escHtml, escJsSingleAttr } from "../ui.js";
import { callbacks } from "../router.js";
import { escHtml, escJsSingleAttr } from "../ui.js";

const PRIORITY_LABELS = ["P0", "P1", "P2", "P3", "P4"];
const PRIORITY_COLORS = [
Expand Down
10 changes: 5 additions & 5 deletions src/filigree/static/js/views/releases.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import { escHtml, escJsSingleAttr } from "../ui.js";

// --- Module-level state ---

let expandedReleaseIds = new Set();
let releaseTreeCache = new Map();
let expandedNodeIds = new Set();
const expandedReleaseIds = new Set();
const releaseTreeCache = new Map();
const expandedNodeIds = new Set();
let showReleased = false;
let loadingReleaseIds = new Set();
let errorReleaseIds = new Set();
const loadingReleaseIds = new Set();
const errorReleaseIds = new Set();
let _pendingFocusTarget = null;

export function scrollToReleaseCard(cardId) {
Expand Down
2 changes: 1 addition & 1 deletion src/filigree/static/js/views/workflow.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ export function showWorkflowModal(type) {
'<select id="workflowModalType" onchange="loadWorkflowInModal()" class="bg-overlay text-primary text-xs rounded px-2 py-1 border border-strong">' +
'<option value="">Select type...</option></select>' +
"</div>" +
'<button id="workflowModalClose" class="text-muted text-primary-hover text-lg">&times;</button>' +
'<button id="workflowModalClose" class="text-muted text-primary-hover text-lg" aria-label="Close workflow diagram" title="Close">&times;</button>' +
"</div>" +
'<div id="workflowModalCy" class="flex-1" style="min-height:0"></div>' +
"</div>";
Expand Down
2 changes: 1 addition & 1 deletion tests/api/test_graph_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def test_hover_traversal_uses_outgoers_not_full_edge_scan(self) -> None:
def test_topology_change_reuses_positions_only_when_all_nodes_have_positions(self) -> None:
graph_js = (STATIC_DIR / "js" / "views" / "graph.js").read_text()
assert "const canReusePositions =" in graph_js
assert "cyNodes.every((n) => Object.prototype.hasOwnProperty.call(previousPositions, n.data.id))" in graph_js
assert "cyNodes.every((n) => Object.hasOwn(previousPositions, n.data.id))" in graph_js
assert "positions: (node) => previousPositions[node.id()]," in graph_js
assert "previousPositions[node.id()] || { x: 0, y: 0 }" not in graph_js

Expand Down