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
62 changes: 0 additions & 62 deletions src/loadpath/static/assets/index-DASg3n-q.js

This file was deleted.

Large diffs are not rendered by default.

62 changes: 62 additions & 0 deletions src/loadpath/static/assets/index-DKfAmZh8.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/loadpath/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&family=IBM+Plex+Sans:wght@400;500;600;700&display=swap" rel="stylesheet" />
<script type="module" crossorigin src="./assets/index-DASg3n-q.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-DA_Qg5fM.css">
<script type="module" crossorigin src="./assets/index-DKfAmZh8.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-DDj-QTuJ.css">
</head>
<body>
<div id="root"></div>
Expand Down
13 changes: 13 additions & 0 deletions tests/e2e/test_ui_flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,19 @@ def test_ui_index_review_graph_copy_and_workspace(live_app, browser_page):
inspector = page.get_by_test_id("graph-inspector")
inspector.wait_for(timeout=10_000)
assert inspector.inner_text().strip()
overflow = inspector.evaluate(
"""el => {
const pane = el.parentElement;
const box = el.getBoundingClientRect();
const paneBox = pane.getBoundingClientRect();
return {
content: el.scrollWidth <= el.clientWidth + 1,
in_pane: box.right <= paneBox.right + 1 && box.left >= paneBox.left - 1,
};
}"""
)
assert overflow["content"], "selected-node inspector overflows horizontally"
assert overflow["in_pane"], "selected-node inspector extends outside the graph pane"

page.get_by_test_id("graph-mode-architecture").click()
assert page.get_by_test_id("graph-mode-architecture").get_attribute("aria-pressed") == "true"
Expand Down
17 changes: 10 additions & 7 deletions ui/src/ImpactGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
type NodeMouseHandler,
} from "@xyflow/react";
import "@xyflow/react/dist/style.css";
import { typeLabel } from "./format";
import { typeLabel, wrapHint } from "./format";
import { layoutNodes, type GraphEdge, type GraphNode } from "./types";

const WEIGHT_COLOR: Record<string, string> = {
Expand All @@ -23,7 +23,9 @@ function LoadNode({ data, selected }: { data: { name: string; type: string }; se
return (
<div className={selected ? "lp-node selected" : "lp-node"}>
<div className="t">{typeLabel(data.type)}</div>
<div className="n">{data.name}</div>
<div className="n" title={data.name}>
{data.name}
</div>
</div>
);
}
Expand Down Expand Up @@ -109,15 +111,16 @@ export function ImpactGraph({ nodes, edges }: { nodes: GraphNode[]; edges: Graph
{selected ? (
<aside className="inspector" data-testid="graph-inspector">
<div className="t">{typeLabel(selected.type)}</div>
<div className="n">{selected.name}</div>
{selected.context ? <div className="muted">{selected.context}</div> : null}
<div className="n">{wrapHint(selected.name)}</div>
{selected.context ? <div className="muted">{wrapHint(selected.context)}</div> : null}
{selected.file_path ? (
<div className="file">
{selected.file_path}
{selected.start_line ? `:${selected.start_line}` : ""}
{wrapHint(
`${selected.file_path}${selected.start_line ? `:${selected.start_line}` : ""}`,
)}
</div>
) : null}
<div className="muted">{selected.qualified_name}</div>
<div className="muted">{wrapHint(selected.qualified_name)}</div>
</aside>
) : null}
</div>
Expand Down
8 changes: 7 additions & 1 deletion ui/src/format.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { formatWhen, kindLabel, repoName, typeLabel } from "./format";
import { formatWhen, kindLabel, repoName, typeLabel, wrapHint } from "./format";

describe("display helpers", () => {
it("humanizes kinds and types", () => {
Expand All @@ -13,4 +13,10 @@ describe("display helpers", () => {
expect(formatWhen()).toBe("");
expect(formatWhen("2026-08-14T00:00:00Z")).toMatch(/2026/);
});

it("inserts wrap hints after identifier separators", () => {
expect(wrapHint("tests/unit/test_django_extractors.py")).toBe(
"tests/\u200bunit/\u200btest_\u200bdjango_\u200bextractors.\u200bpy",
);
});
});
5 changes: 5 additions & 0 deletions ui/src/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,8 @@ export function formatWhen(iso?: string | null): string {
export function repoName(path: string): string {
return path.split(/[\\/]/).filter(Boolean).pop() || path;
}

/** Soft wrap opportunities after path/identifier separators. */
export function wrapHint(text: string): string {
return text.replace(/([/\\._:@-])/g, "$1\u200b");
}
17 changes: 16 additions & 1 deletion ui/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -885,27 +885,42 @@ h2 { font-size: 13px; margin: 0; font-weight: 600; }
right: 12px;
z-index: 5;
width: 260px;
max-width: calc(100% - 24px);
overflow-x: hidden;
overflow-wrap: break-word;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--radius-lg);
padding: 10px 12px;
box-shadow: 0 8px 24px var(--shadow);
}
.inspector .t { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; }
.inspector .n,
.inspector .file,
.inspector .muted {
overflow-wrap: break-word;
}
.inspector .n { font-weight: 600; margin: 4px 0; }
.lp-node {
padding: 8px 10px;
border-radius: var(--radius);
border: 1px solid var(--node-line);
background: var(--node-bg);
width: 180px;
max-width: 100%;
height: 56px;
box-sizing: border-box;
box-shadow: 0 0 0 1px var(--shadow);
overflow: hidden;
}
.lp-node .t { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.08em; }
.lp-node .n { font-size: 13px; font-weight: 600; }
.lp-node .n {
font-size: 13px;
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.lp-node.selected { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.type-table {
width: 100%;
Expand Down
40 changes: 40 additions & 0 deletions ui/src/styles.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { afterEach, describe, expect, it } from "vitest";

const css = readFileSync(join(dirname(fileURLToPath(import.meta.url)), "styles.css"), "utf8");

function mount(html: string) {
const style = document.createElement("style");
style.textContent = css;
document.head.appendChild(style);
document.body.innerHTML = html;
}

describe("graph selected-node overflow", () => {
afterEach(() => {
document.head.replaceChildren();
document.body.replaceChildren();
});

it("wraps long identifiers in the selected-node inspector", () => {
mount(
`<aside class="inspector"><div class="n">test_index_summary_includes_contexts_and_more</div><div class="file">tests/e2e/test_index_architecture_flow.py</div></aside>`,
);
const inspector = document.querySelector(".inspector") as HTMLElement;
const name = document.querySelector(".inspector .n") as HTMLElement;
expect(getComputedStyle(inspector).overflowWrap).toBe("break-word");
expect(getComputedStyle(inspector).overflowX).toBe("hidden");
expect(getComputedStyle(inspector).maxWidth).not.toBe("none");
expect(getComputedStyle(name).overflowWrap).toBe("break-word");
});

it("ellipsizes long titles inside graph nodes", () => {
mount(`<div class="lp-node"><div class="n">test_index_summary_includes_contexts_and_more</div></div>`);
const name = document.querySelector(".lp-node .n") as HTMLElement;
expect(getComputedStyle(name).textOverflow).toBe("ellipsis");
expect(getComputedStyle(name).overflow).toBe("hidden");
expect(getComputedStyle(name).whiteSpace).toBe("nowrap");
});
});
3 changes: 2 additions & 1 deletion ui/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
"noFallthroughCasesInSwitch": true,
"types": ["vite/client"]
},
"include": ["src"]
"include": ["src"],
"exclude": ["src/**/*.test.ts"]
}
Loading