Commit Graph
28 Commits
Author SHA1 Message Date
Martin Vogel 9987a4eae0 Merge pull request #740 from rarepops/feat/graph-ui-routing-and-filter-ux
feat(graph-ui): URL routing, filter panel UX, and camera fly-to fix
2026-07-04 21:36:29 +02:00
Martin Vogel 093eaf17de Merge pull request #839 from sahil-mangla/main
test(graph-ui): fix locale-dependent number-format assertion in GraphTab.test.ts
2026-07-04 19:34:33 +02:00
Zadak c751240dc0 fix(graph-ui): keep the camera on the focused node after fly-to
The fly-to animation moved only the camera and called lookAt, leaving the OrbitControls pivot at the origin, so OrbitControls re-centred the view on the next frame and snapped the camera back once the animation ended.

Lerp the controls target to the focus point as well so the view stays on the clicked node or cluster and orbits around it afterwards.

Signed-off-by: Zadak <rarepops@protonmail.com>
2026-07-04 16:47:06 +02:00
Zadak dc8c5be587 fix(graph-ui): clarify graph HUD labels and empty state
Rename the ambiguous Clear button to Clear selection so it reads distinctly from Refresh, and point the empty-state hint at the Projects tab instead of the renamed Stats tab.

Signed-off-by: Zadak <rarepops@protonmail.com>
2026-07-04 16:47:06 +02:00
Zadak 0c7883eb2f feat(graph-ui): sync active tab and project to the URL
Store the active tab and selected project in the URL query string so the view survives refreshes and can be bookmarked or shared, syncing back on browser back and forward via popstate.

Query params are used rather than path segments because the embedded server only serves index.html at the root path. Opening the Projects tab now clears the active project, and the Graph tab stays disabled until a project is selected.

Signed-off-by: Zadak <rarepops@protonmail.com>
2026-07-04 16:47:06 +02:00
Zadak 8ea42911a4 feat(graph-ui): scrollable filter panel with clearer grouping
Cap the filter panel height and move the node and edge chips into a scroll area so a long type list no longer pushes the folder tree off screen.

Regroup the chips under Node types and Relationships, pin the show-labels toggle as a footer, and add a Folders heading (en and zh) above the folder tree so the section is clearly labelled.

Signed-off-by: Zadak <rarepops@protonmail.com>
2026-07-04 16:47:06 +02:00
Martin Vogel c7374135ac Merge pull request #847 from DeusData/distill/ui-label-and-ts-repro
fix(graph-ui): honest Project-ID label; RED repro for TS inherited-method gap
2026-07-04 15:31:27 +02:00
Martin Vogelandrarepops b56cb592e9 fix(graph-ui): label Project name honestly as permanent ID
The New Index modal presented the project name as an optional display
name, but the value becomes permanent identity: the database filename,
the projects primary key, and the QN prefix of every node. It can never
be renamed afterwards.

Implements the decision from the #805 review: keep the field, make it
honest. The label now reads "Project ID (optional — permanent, cannot
be renamed)", the placeholder and a new help line explain that the ID
becomes the database name and query prefix and that leaving it blank
derives it from the path. Both EN and zh locales updated, test asserting
the old label adjusted.

Co-authored-by: rarepops <rarepops@protonmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-04 11:35:41 +02:00
sahil-mangla 00b2a0e044 test(graph-ui): fix locale-dependent number-format assertion in GraphTab.test.ts
Pin formatGraphLimitNotice to 'en-US' to resolve unit test failures under non-en_US (e.g. German de_*) locales.

Fixes #825

Signed-off-by: sahil-mangla <manglasahil2017@gmail.com>
2026-07-04 14:32:29 +05:30
Zadak d72a84ff40 fix(graph-ui): scope typed-path picker refresh to Windows drive paths
The debounced folder-list refresh (and Enter-to-navigate) on the Repository path field now only fires for Windows drive paths (e.g. 'D:/'), where typing is the mechanism for switching drives. POSIX path navigation is left exactly as before.

Add a regression test asserting a typed POSIX path does not trigger a re-browse.

Signed-off-by: Zadak <rarepops@protonmail.com>
2026-07-04 09:39:28 +02:00
Zadak b15dff53a4 fix(graph-ui): drop the empty '/' root on Windows, offer the drive instead
On Windows the POSIX '/' quick-jump root is meaningless (browsing it returns an empty listing), yet the picker showed a '/' button whenever the backend did not enumerate drives (older builds return roots=['/']). Clicking it stranded the user on an empty view.

Derive Windows-aware quick-jump roots: drop non-drive roots and always include the current drive (parsed from the browsed path), so the button lists the drive root. Other drives remain reachable by typing. Add a regression test.

Signed-off-by: Zadak <rarepops@protonmail.com>
2026-07-04 09:39:28 +02:00
Zadak f50bf902aa fix(graph-ui): refresh folder list when a path is typed in the picker
Typing a path into the Repository path field updated currentPath (and thus the breadcrumb) but never re-fetched the directory listing, so switching drives by typing (e.g. 'D:/') left the previous location's folders showing. This is the only way to change drives when the backend does not enumerate drive roots.

Debounce a silent /api/browse refresh when the typed path changes (keeping the last good listing instead of flashing errors mid-typing), and navigate immediately on Enter. Add a regression test.

Signed-off-by: Zadak <rarepops@protonmail.com>
2026-07-04 09:39:28 +02:00
Zadak 4efa22cd95 fix(graph-ui): navigate Windows drive breadcrumbs to real paths
The index file picker built breadcrumb targets as '/' + segments, so on a Windows drive path (C:/Users/rap) clicking a crumb browsed to '/C:/...', which the backend rejected as 'not a directory'. Only the '.. (up)' button worked.

Build drive-aware crumb targets (C:/, C:/Users) and drop the bogus unified '/' root crumb on Windows drive paths; POSIX behavior is unchanged. Add a regression test for Windows breadcrumb navigation and cleanup() for test isolation.

Signed-off-by: Zadak <rarepops@protonmail.com>
2026-07-04 09:39:28 +02:00
Martin Vogelandsahil-mangla cfc73f8d0b fix(graph-ui): surface indexing-job failures instead of silently completing
The backend /api/index-status reports status:"error" plus an error
message for failed indexing jobs, but IndexProgress treated any
non-"indexing" state as successful completion: the spinner vanished
with no feedback (e.g. after an OOM-killed indexer subprocess), the
project never appeared, and no error was shown.

Render a visible error banner (path + error text) with a Dismiss
button instead, and keep the success flow unchanged. Beyond the
original PR:

- Restore the empty-jobs guard the PR dropped: the backend keeps
  finished jobs listed as "done"/"error" (handle_index_status only
  skips idle slots), so an empty list mid-index only occurs on
  transient state loss and must not be treated as completion.
- Route the new user-facing strings through the i18n system
  (projects.indexingFailed, common.dismiss; EN + zh entries).
- Cover error banner + dismiss, success flow and the empty-jobs
  guard with vitest cases on the now-exported IndexProgress.

Distilled from #549.

Refs #524

Co-authored-by: sahil-mangla <manglasahil2017@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-03 20:28:27 +02:00
Zadak a0e970d78d fix(graph-ui): keep filter sidebar visible when all nodes are filtered out
Clicking "None" (disable all filters) clears every label and edge type,
making filteredData.nodes empty. The early return then replaced the whole
layout — including the left filter sidebar — with a centered "All nodes
filtered out / Reset Filters" message, leaving no way to re-enable
individual filters without resetting everything.

Narrow the early return to the genuinely-empty / no-data case and render
the "all filtered out" state inside the graph area instead, so the
FilterPanel and Sidebar stay mounted. As a side effect, a project with
zero nodes now correctly reads "No nodes in this project" rather than
"All nodes filtered out".

Add a regression test that disables all filters via "None" and asserts the
Filters sidebar remains in the document.

Signed-off-by: Zadak <rarepops@protonmail.com>
2026-06-30 21:27:41 +02:00
Martin Vogel d5414111b3 feat(ui): add i18n and index picker UX
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-28 21:21:32 +02:00
Martin Vogel d9d70656b4 fix(ui): cap graph rendering and index spawn
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-28 20:57:14 +02:00
Martin Vogel a3336bf346 chore(security): resolve flagged Scorecard + Dependabot alerts
- graph-ui: bump vite ^6.4.2 -> ^6.4.3 and add overrides pinning the transitive
  form-data >=4.0.6 and @babel/core >=7.29.6 (all dev-scope build/test deps, not
  shipped in the binary). Clears the 4 open Dependabot alerts and Scorecard's
  VulnerabilitiesID. `npm audit` now reports 0 vulnerabilities.
- codeql.yml: move `security-events: write` from the workflow top level to the
  `analyze` job (top level is now `contents: read`), resolving Scorecard's
  TokenPermissionsID least-privilege finding. CodeQL still uploads results from the
  job-scoped token.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-24 00:34:05 +02:00
Nan 550a2cc65c fix(ui): render graph labels without remote font fetches (#496)
Signed-off-by: Nan <630971265@qq.com>
2026-06-22 22:04:54 +02:00
Martin Vogel 952eea9f1f Extend the license gate to ancestry coverage and the UI dependency tree
Structural layer: drop the hardcoded component list — any directory
containing vendored source or data files must now be covered by a
license file in itself or an ancestor within the vendored tree, so
newly vendored code without a license fails immediately. New third
layer: the graph-UI npm production tree is resolved (postinstall
scripts disabled) and every bundled package must be on the policy
allow-list; unknown licenses fail. Reclassifies @tailwindcss/vite as a
devDependency — it is a build plugin and was inflating the production
tree with native tooling.
2026-06-12 13:03:12 +02:00
Martin Vogel 64be28083e fix(deps): bump vitest 3 -> 4.1.8 (GHSA-5xrq-8626-4rwp)
Resolves the critical Dependabot/Scorecard alert for vitest <4.1.0 (UI server
arbitrary file read/exec). graph-ui has no test files, so the major bump is
risk-free here (CI uses 'vitest run'); lockfile regenerated, vitest 4.1.8.
2026-06-01 22:57:54 +02:00
Martin Vogel d1b676fc0d fix(deps): bump ws to 8.21.0 (CVE-2026-45736, GHSA-58qx-3vcg-4xpx)
ws 8.19.0 had a moderate uninitialized-memory-disclosure issue in websocket.close() (fixed in 8.20.1). Transitive dev dependency; the existing ^8.18.0 range already permits the fix, so this is a lockfile-only bump. npm audit now reports 0 vulnerabilities.
2026-05-29 00:36:24 +02:00
Martin Vogelandsponger94 7290c1fd2b feat(ui): satellite galaxy radius spacing + cross-galaxy edge rendering
Two visualization gaps in the cross-repo graph view:

1. Fixed LAYOUT_GALAXY_SPACING=600 buried satellite galaxies inside the
   primary cluster on any non-trivial project — a 1000-node primary
   cluster has bounding radius ~1500. Added layout_radius() (max distance
   from origin across all node positions) and compute the per-satellite
   distance as primary_radius + sat_radius + LAYOUT_GALAXY_PAD, with the
   old constant retained as a lower bound for tiny projects.

2. cross_edges was hard-coded to an empty array (TODO placeholder), so
   inter-galaxy CROSS_* edges never rendered even when the matcher had
   produced them. Populate from a join on the source store's CROSS_*
   edges → Route node qualified_name, then resolve the canonical QN in
   the linked store to get its node id. The Route QN is the cross-repo
   matching contract; properties.target_function isn't unique.

Required keeping both `store` and `lp_store` open through the linked-
projects loop instead of closing them right after layout compute.
Added cbm_store_close to all early-error paths to balance the lifetime.

Frontend side:
- EdgeLines accepts an optional targetNodes prop so a single component
  can render edges with source in one node array and target in another
  (offset-adjusted satellite nodes).
- GraphScene renders an EdgeLines layer per linked project for its
  cross_edges, using primary nodes as source and offsetNodes as target.
- GraphTab folds linked-project labels and edge types into the filter
  init / enableAll / filteredData paths so cross_edges respect the
  current filter state.
- Edge color palette extended with GRPC/GRAPHQL/TRPC/CROSS_* tones.

Co-authored-by: sponger94 <45746997+sponger94@users.noreply.github.com>
2026-05-10 18:56:30 +02:00
Martin Vogel 4fdcdd4239 fix(security): close open scanner alerts
Dependabot/CodeQL #33 — postcss XSS GHSA-qx2v-qp2m-jg93:
  npm audit fix in graph-ui (8.5.8 -> 8.5.14, above the <8.5.10 vuln range)

CodeQL #39 — TOCTOU race in artifact.c ensure_gitattributes():
  Replace stat() + fopen() with open(O_WRONLY|O_CREAT|O_EXCL).
  Atomic create-only-if-absent closes the check-vs-write window.
  Falls through to merge driver setup if file already exists.

CodeQL #55 — pip install not pinned in release.yml:
  Pin build==1.3.0 and twine==6.2.0. Comment explains why
  --require-hashes is not used (transitive-deps overhead).

Dismissed (won't-fix):
  - #56 contents: write — required for 'gh release edit --draft=false';
    no narrower permission exists.
  - #54-51 Crystal grammar warnings — vendored upstream code.
  - #50-40 Agda grammar warnings — vendored upstream code.
2026-05-05 00:51:52 +02:00
test b6eefe014c Add multi-galaxy UI layout and cross-repo architecture summary
Backend (http_server.c):
- Layout API detects CROSS_* edges and loads linked project graphs
- Each linked project computed at radial offset as satellite galaxy
- Response includes linked_projects with nodes, edges, offset

Frontend:
- LinkedProject type in types.ts
- GraphScene renders satellite galaxies at offset positions
- NodeCloud/EdgeLines accept opacity prop for dimmed rendering

Tool integration (mcp.c):
- get_architecture includes cross_repo_links summary
- Extract append_cross_repo_summary helper for linter compliance
2026-04-15 23:53:42 +02:00
DeusData e21eba6348 Bump vite to 6.4.2 to fix CVE GHSA-4w7w-66w2-5vf9 and GHSA-p9ff-h696-f583
Fixes arbitrary file read via dev server WebSocket and path traversal
in optimized deps .map handling.
2026-04-12 19:06:28 +01:00
Martin Vogel 1ad2568d66 Fix smoke 8o: OpenCode command array + update picomatch (dependabot) 2026-03-25 23:08:02 +01:00
Martin Vogel 18fa9979ee Replace vmem with mimalloc global allocator, add extraction-phase prescan, fix __init__.py QN collision
Memory management:
- Vendor mimalloc v2.1.9 as global allocator (MI_OVERRIDE=1 in prod)
- New mem.h/mem.c: RSS-based budget tracking via mi_process_info()
- Remove vmem.c/vmem.h (mmap-based budget tracking)
- Remove slab tier2 bump allocator (~300 LOC); >64B goes to mimalloc
- Slab tier1 pages from malloc (= mimalloc) instead of vmem
- Arena blocks from malloc instead of vmem
- Budget raised from 35% to 50% RAM (no more untracked C++ heap)

Extraction-phase prescan (eliminates disk re-reads):
- HTTP call sites: keyword check + URL extraction during extraction
- HTTP routes: decorator + source-based extraction during extraction
- Config file refs: regex scan during extraction
- httplinks: 41.8s → 13ms on Linux kernel (3,212x faster)
- configlink: 41.4s → 0.8s on Linux kernel (54x faster)
- Linux kernel fast-mode total: 2m38s → 1m18s

Bug fixes:
- __init__.py Module QN no longer collides with Folder QN
- index.ts same fix for JS/TS packages
- 13 regression tests for QN collision at FQN + extraction layers
- search_graph/search_code default limit raised from 10 to 500k
- Resolve all clang-tidy, cppcheck, and clang-format warnings

Repo cleanup:
- tree-sitter-form, tree-sitter-magma moved to tools/
- .gitignore: build/, node_modules/, graph-ui/dist/, TEST_PLAN.md
2026-03-17 22:05:57 +01:00