ARCLUX Progress — Web (apps/web components, graph viewer, vendor-ui, theme)
See PROGRES.md for the index. Split by topic from the original PROGRES-status.md.2026-08-03 — ✅ DONE — UI: layout, primitives, patterns (partial), marketing
components/layout/* (7 files, all production-quality): Sidebar.tsx,
SplitPane.tsx (resizable pane, pointer drag), WorkspaceLayout.tsx,
Navbar.tsx, Breadcrumbs.tsx, PageContainer.tsx, PageHeader.tsx,
Footer.tsx.
components/primitives/* (7 files): Avatar.tsx, Badge.tsx,
Checkbox.tsx, Kbd.tsx, Skeleton.tsx, Switch.tsx — thin re-exports
from vendor-ui/shadcn/*. All complete.
components/patterns/* — now 11/11 complete (see update below):
CommandPalette.tsx (uses cmdk as a dependency, see NOTICE),
LoadingState.tsx, ErrorState.tsx, ConfirmDialog.tsx, CopyButton.tsx,
DataTable.tsx, EmptyState.tsx, FilterBar.tsx, MobileBottomSheet.tsx,
SearchInput.tsx, StatusDot.tsx.
components/marketing/* (5 files, all complete): Hero.tsx, CTA.tsx,
Example.tsx, Features.tsx, Footer.tsx.
components/overview/*— onlyProjectStructure.tsx(99 lines, file tree UI, collapsible, usesd3-hierarchy) is complete.RepositoryHeader,RepositoryInfo,RepositoryOvervieware still stubs.
[STATUS UPDATE, 2026-08-14]: resolved — RepositoryHeader/Info/Overview implemented and mounted on the [org]/[repo] root page (see progres/status-web.md “Overview page implemented”).
components/explorer/* — only FileDetails.tsx (132 lines, fetch +
render source with syntax highlighting) is complete, not yet wired into
any page because Explorer.tsx itself is still a stub. DependencyList,
ImpactSummary are also still stubs.
hooks/useTheme.ts (36 lines) done. useClipboard,
useCommandPalette, useDebounce, useMediaQuery still stubs.
theme/colors.ts, theme.dark.ts, graphColors.ts done.
motion.ts, spacing.ts, typography.ts still stubs.
lib/utils.ts, lib/cn.ts done. lib/api.ts, lib/graph.ts stubs.
App routes: every page.tsx/error.tsx/loading.tsx under
app/[org]/[repo]/* already has real content (not default Next.js
boilerplate), including app/new/page.tsx.
API routes: POST /api/analyze, GET /api/graph, GET /api/file
(fetch raw from GitHub + Python highlighting) — all done (65-84 lines).
api/impact/route.ts and api/search/route.ts — see update below, now
implemented (previously 8-line stubs).
2026-08-03 — ✅ DONE — UI: graph viewer
Composition root:GraphViewport.tsx (used in
app/[org]/[repo]/graph/page.tsx, don’t call GraphCanvas manually).
GraphProvider (centralized state: transform, positions, dimensions,
contextMenuNodeId — GraphCanvas is the only writer), GraphCanvas
(260 lines, pan/zoom/Escape-deselect/double-click-zoom/event delegation),
GraphToolbar, GraphLegend, GraphSearch (73 lines, still exact/substring
match — not yet using fuzzyScore.ts), GraphSelection, GraphContextMenu,
Minimap, GraphNode, GraphEdge.
Not yet visually verified in a browser (only passed tsc --noEmit):
Minimapviewport-rect still assumes transform origin at (0,0)Minimap+GraphLegendcollide when rendered together (both usebottom-4 right-4absolute positioning) — currently onlyGraphLegendis rendered- Double-click-zoom + context menu together haven’t been tested on a real device
2026-08-03 — ✅ DONE — vendor-ui
Everything invendor-ui/shadcn/* (avatar, badge, button, checkbox,
command, dialog, dropdown-menu, input, input-group, popover, select,
separator, sheet, skeleton, switch, tabs, textarea, toast, tooltip),
vendor-ui/aceternity/* (5 files), vendor-ui/magic-ui/* (6 files,
including file-tree.tsx at 511 lines — the largest file in the whole
project), and vendor-ui/_inbox/* (4 custom files: neon-glow-card,
code-block-terminal, graph-particles-bg, keyboard-shortcut-hint) — all
installed/written in full.
2026-08-04 — Update — /api/impact and /api/search implemented (from empty stubs)
Found via dogfooding: detectRouteConvention (one of the 18 detectors) found that apps/web/app/api/impact/route.ts and …/search/route.ts exported no HTTP method at all. Checked manually — turns out it wasn’t a missing export, both were genuinely still 8-line stubs (license header only), never written at all. Design: AnalyzeRepositoryResult now carries arepository field (a full
Repository instance, NOT a plain object). IMPORTANT — this field is
server-side only. Repository.modules is a private Map, so if
JSON.stringify’d as-is it silently becomes {} (not a crash, silent data
loss). apps/web/app/api/analyze/route.ts (which has worked for a while)
was patched to strip the repository field before responding, so the
JSON shape doesn’t silently change now that this field exists.
/api/impact — composes calculateAffectedFiles + buildImpactTree (from
packages/impact, which turned out to already be done, see update above).
/api/search — uses fuzzyScore.ts (adapted from cmdk) to search by module
file path only. This is a stopgap, NOT real search — packages/search/
SearchEngine.ts etc. is still 0%.
STATUS: only passed tsc —noEmit, NOT YET tested end-to-end via a real
dev server (analyzeRepository needs a real repoUrl/clone, can’t be tested
via scripts/testPlayground.ts the way CLI/detector work was). Manual test
before trusting this: run pnpm dev in apps/web, then curl
‘localhost:3000/api/impact?repoUrl=<url>&moduleId=<path>’ against a real
small GitHub repo.
2026-08-04 — Update — components/patterns/* 8 stub files are now DONE
Wrote 8 files that were previously stubs (just an 8-line license header):ConfirmDialog.tsx, CopyButton.tsx, DataTable.tsx, EmptyState.tsx,
FilterBar.tsx, MobileBottomSheet.tsx, SearchInput.tsx, StatusDot.tsx.
Convention taken from the files that were already done
(LoadingState.tsx, ErrorState.tsx, CommandPalette.tsx): named
export, ComponentNameProps props interface, "use client" for
interactive ones, cn() from @/lib/cn, primitives from
@/components/ui/* (not directly from vendor-ui/shadcn/*).
ConfirmDialog&MobileBottomSheetuse theDialog/Sheetprimitives fromcomponents/ui/, with props APIs matched exactly to the real shape (DialogContent,SheetContent side="bottom", etc. — checked from vendor-ui source first, not guessed).DataTableis built from a native<table>+ Tailwind — there’s no shadcntable.tsxprimitive invendor-ui/yet.FilterBardeliberately does not useBadge(not yet wrapped incomponents/ui/, still an open item fromdetectMissingExports), uses aButtonvariant toggle instead.- Verification:
npx tsc --noEmit -p apps/web/tsconfig.json→ 0 errors from the new code. The remaining 5 errors in the project are pre-existing and unrelated (vendor-ui/magic-ui/file-tree.tsxneeds the@radix-ui/react-accordionpackage + a not-yet-writtenscroll-area.tsx;packages/graph/buildFolderGraph.tsneeds thed3-hierarchypackage, which isn’t installed — both are separate tasks). - Important note: running
npx tsc --noEmitWITHOUT-p apps/web/tsconfig.jsonproduces 130 false errors (every@/*alias fails to resolve) because the baseUrl path mapping in that tsconfig is relative toapps/web, not the repo root. To typecheck this app, you MUST use the-p apps/web/tsconfig.jsonflag. - No consumer imports these 8 components yet (checked via grep), so this is purely new, ready-to-use components with no breaking-change risk.
components/patterns/* is now 11/11 complete (including
CommandPalette.tsx, which was already done by a previous session).
2026-08-05 — Update — Graph node icons + edge labels/arrows (visual polish, dogfood-driven)
Requested after visually testing the graph viewer live in-browser against the arclux repo itself (localhost:3000, first real dogfood screenshot session). Two gaps found:- All nodes were plain colored circles, no visual distinction between node types beyond color alone.
- Edges were plain lines — no direction indicator, no way to tell edge type (import/export/call/route-link) without clicking through to inspect the underlying data.
apps/web/components/graph/nodeIcons.tsx(new) — minimal single-path SVG icons per GraphNodeType (file/folder/external-package/route/ component/hook), drawn inside GraphNode’s existing circle. Deliberately NOT using a full icon library import (e.g. lucide-react) here — graphs can have 1000+ nodes, each rendered icon is a cost, so this is a raw path string sized for an 8x8 viewBox instead.apps/web/theme/graphColors.ts— addedgetGraphEdgeHighlightColor(). The existing dark-modeimportedge color (#454545) was deliberately dim so a busy graph doesn’t look noisy at rest, but that same dimness made it nearly invisible once highlighted/selected against GraphCanvas’s black background. Highlight colors are a separate, brighter palette, not a theme mode swap (GraphCanvas’s background is hardcoded black regardless of app theme).apps/web/components/graph/GraphEdge.tsx— highlighted edges now show an arrowhead (SVG marker) and a type label (“imports”/“exports”/ “calls”/“routes to”) at the midpoint.apps/web/components/graph/GraphCanvas.tsx— registers 4<marker>defs (one per edge type) in<defs>, referenced by GraphEdge viamarkerEnd.
isHighlighted covers both
hover and selected states — hovering a hub like Repository.ts (34
incoming edges, seen in live testing) would pop 34 labels at once.
Flagged in the component’s own comment, not yet addressed.
Verified so far: tsc --noEmit -p apps/web/tsconfig.json clean. Node
icons confirmed visually in-browser (dogfood screenshot showed file-type
document icons rendering correctly on all nodes in a TypeScript-only
subgraph — other node type icons folder/component/hook/route/
external-package not yet visually confirmed since that subgraph happened
to be all-file). Edge label/arrow/color change was NOT visually verified
before merging — pushed under time pressure near a chat context limit,
confirm in-browser before relying on it.
2026-08-05 — Update — GraphFocusView (new): replaces overlapping edge labels on high-fan-in nodes
Dogfooding on the graph viewer against the arclux repo itself (mobile screenshot) showed hovering a high-fan-in node (index.ts, 85 incoming edges) popped dozens of overlapping “imports” labels on the canvas — unreadable. This is the same root cause already flagged in GraphEdge.tsx’s own comment (isHighlighted covers hover, not scoped per-edge). Rather than patch the label-overlap directly, built a different interaction:apps/web/components/graph/GraphFocusView.tsx — a full-panel
overlay (replaces GraphSelection.tsx in GraphViewport.tsx) that opens
on node selection, showing DIRECT dependencies/dependents as two columns
of labeled cards (icon + name + path, reusing getGraphNodeColor /
nodeIcons.tsx already in the codebase). Capped at 12 cards per side
(“+N more”) specifically because of the 85-incoming-edge case found in
this same repo — an uncapped list would just move the unreadability
problem into the panel instead of fixing it.
Scope: DIRECT neighbors only, not transitive — this is a readable local
map, not a replacement for traceConsumers/traceDependencies in
packages/impact/*.
NOT YET DONE: the underlying canvas label-overlap bug (GraphEdge.tsx)
is still there — GraphFocusView is a new, separate way to inspect a
node’s connections, it doesn’t fix hover behavior on the canvas itself.
Also not visually verified in-browser yet — only typechecked
(tsc --noEmit -p apps/web/tsconfig.json), pushed under time pressure
near a chat context limit. Confirm visually before relying on it,
same caveat as the earlier edge-label/arrow update.
Repo now requires branch protection on main (PR required, verified
by testing it against ourselves earlier) — this was pushed via
feat/graph-focus-view branch + PR, not direct push. Any future session:
you CANNOT git push directly to main anymore, always
git checkout -b <branch> first.
2026-08-05 — Update — components/workspace/* 8 stub files are now DONE
[STATUS UPDATE, 2026-08-14]: the “Workspace.tsx is not wired into any app/ route yet” note below is now RESOLVED — WorkspaceLayout (Navbar + Sidebar + Breadcrumbs) is the shared [org]/[repo] layout, and Workspace is mounted at /[org]/[repo]/workspace. See the “Workspace shell mounted” entry below.Wrote all 8 files: Workspace.tsx (composition root), WorkspaceHeader.tsx, WorkspaceCommand.tsx, WorkspaceSearch.tsx, WorkspaceSwitcher.tsx, and panels/{Files,Impact,Issues}Panel.tsx. Reference used: browsed ~/git-truck/src/routes/view.tsx and RevisionSelect.tsx for composition/dropdown-switcher PATTERNS only (concept, not code — that project is React Router SSR with a completely different loader/action model, not directly portable to Next.js App Router). No code copied, no NOTICE entry needed since nothing was adapted verbatim. Real vs honest-placeholder breakdown:
- WorkspaceSwitcher.tsx: functional for switching between recently-viewed repos (client-side list), but branch switching is NOT functional yet — pipeline.ts accepts a branch param but no UI lets the user pick one.
[STATUS UPDATE, 2026-08-14]: resolved — branch switching works. packages/git/getBranches.ts + detectDefaultBranch.ts implemented (git ls-remote, no clone) behind GET /api/branches; WorkspaceSwitcher lists branches and the workspace refetches panels on change. See “Branch switcher” below.
- WorkspaceSearch.tsx: real, hits GET /api/search (fuzzyScore.ts stopgap, same caveats as documented on that route — file-path-only, no caching, re-indexes whole repo per call).
- WorkspaceCommand.tsx: thin wrapper re-exporting the already-built CommandPalette.tsx, not new behavior.
- ImpactPanel.tsx: real, wraps the already-verified ImpactSummary.tsx. Needs a moduleId, which currently only comes from WorkspaceSearch selection (no file tree to click into yet).
- FilesPanel.tsx: honest “coming soon” EmptyState, NOT a fake file tree. Blocked on either fixing vendor-ui/magic-ui/file-tree.tsx’s missing deps (@radix-ui/react-accordion, scroll-area.tsx) or building a simpler tree view from graph data directly.
[STATUS UPDATE, 2026-08-14]: resolved — the “building a simpler tree
view” option won: POST /api/analyze gained a server-side folderTree
(issue #330) and FilesPanel renders it via ProjectStructure, wired to
the workspace selection state. See “FilesPanel real file tree” below.
- IssuesPanel.tsx: honest “coming soon” EmptyState, NOT fake detector
data. Detectors themselves are 18/18 done and already run via
apps/cli doctor, but nothing exposes them over HTTP yet — needs a new /api/doctor route.
[STATUS UPDATE, 2026-08-14]: resolved — /api/doctor exists (packages/engine/runDoctor.ts, all 19 detectors normalized to one flat finding list) and IssuesPanel renders it grouped by severity. See “IssuesPanel + /api/doctor” below.Verification: npx tsc —noEmit -p apps/web/tsconfig.json clean (only the 2 pre-existing file-tree.tsx errors, unrelated). NOT yet verified visually in-browser — Workspace.tsx is not wired into any app/ route yet (app/[org]/[repo]/page.tsx still shows its old “coming soon, see /graph” placeholder). Wiring it in and browser-testing is deliberately left as separate follow-up work, to keep this change’s review surface to “the workspace components exist and typecheck” rather than also redoing the main repo page.
2026-08-06 — Update — hooks/useDebounce.ts + components/search/GlobalSearch.tsx implemented
useDebounce.ts: generic debounce-a-value hook (standard setTimeout/ clearTimeout pattern). GlobalSearch.tsx: standalone search component hitting GET /api/search, using this hook. IMPORTANT CONTEXT: an earlier draft of GlobalSearch.tsx was floated (via external chat, never committed to this repo) that hardcodeditems: SearchItem[] = [] with a comment saying data would come from
“SearchProvider / API” later — meaning the search box would render and
accept input but silently return zero results forever, looking
functional while doing nothing. That draft was never written to disk and
is NOT what’s implemented here. This version actually calls /api/search
and returns real results (same fuzzyScore.ts stopgap backing
WorkspaceSearch.tsx and CommandPalette-adjacent search, with the same
caveats — file-path-only, no caching, re-indexes the whole repo per
call).
Deliberate overlap with components/workspace/WorkspaceSearch.tsx: both
hit the same API with the same debounce-then-fetch shape.
WorkspaceSearch.tsx is scoped to the workspace header (compact inline
dropdown) and inlines its own debounce logic rather than using this new
hook. GlobalSearch.tsx is the general-purpose version (e.g. for a global
navbar), shows an EmptyState on zero results, and uses useDebounce. Not
merged into one component since their result-UI differs. If this
divergence becomes annoying to maintain, consider extracting a shared
useRepoSearch(repoUrl, branch, query) hook both could call — not done
here to keep this change scoped.
NOT yet wired into any page/navbar — same status as most workspace
components, exists and typechecks but has no real consumer yet.
Verification: npx tsc —noEmit -p apps/web/tsconfig.json clean (only the
2 pre-existing file-tree.tsx errors, unrelated). Not yet visually
verified in-browser.
2026-08-06 — Update — theme/typography.ts, spacing.ts, motion.ts written (globals.css left untouched)
Three token files written from scratch:apps/web/theme/typography.ts
(font size/weight/line-height scale + graph label sizes),
apps/web/theme/spacing.ts (spacing scale + graph layout pixel
constants), apps/web/theme/motion.ts (duration/easing + interaction
timing constants like double-click window).
IMPORTANT — apps/web/app/globals.css was checked (209 lines,
NOT a stub) and deliberately NOT touched. It already carries the
dark-first ARCLUX theme applied in an earlier session — do not run any
“fill empty file” pass against it again, it only looks like a candidate
if you trust file NAME/folder location without checking actual line
count first (same class of mistake flagged repeatedly elsewhere in this
doc).
NOT YET DONE: none of these 3 new token files are actually imported
anywhere yet — GraphCanvas.tsx’s DOUBLE_CLICK_DELAY_MS and similar
inline constants are still local, not migrated to import from
motion.ts’s interactionTiming. Wiring these in is a follow-up, kept
out of scope here to avoid touching working interaction code just to
satisfy new token files existing.
STATUS: pushed near a chat context limit — typecheck result for these 3
files was requested but not confirmed back before this note was written.
Re-run npx tsc --noEmit -p apps/web/tsconfig.json and confirm clean
before trusting these beyond “written, looks syntactically right.”
2026-08-07 — useGraph re-export + centralized fetch helpers
apps/web/features/graph/useGraph.ts implemented as a thin re-export of GraphProvider’s useGraphContext() (per the 2026-08-03 decision). The other 4 files in features/graph/ stay deliberately empty but now have explanatory comments. Also implemented lib/api.ts (fetchJson helper: query params, res.ok check, error parsing) and lib/graph.ts (fetchGraph wrapping /api/graph), then refactored GraphProvider.tsx and DependencyList.tsx to use fetchGraph() instead of each having its own duplicated inline fetch block.2026-08-07 — useGraph re-export + centralized fetch helpers
apps/web/features/graph/useGraph.ts implemented as a thin re-export of GraphProvider’s useGraphContext() (per the 2026-08-03 decision). The other 4 files in features/graph/ stay deliberately empty but now have explanatory comments. Also implemented lib/api.ts (fetchJson helper: query params, res.ok check, error parsing) and lib/graph.ts (fetchGraph wrapping /api/graph), then refactored GraphProvider.tsx and DependencyList.tsx to use fetchGraph() instead of each having its own duplicated inline fetch block.2026-08-07 — README fixes: pnpm run dev, Contributors section clarified
Fixed a leftover ‘npm run dev’ instruction in README.md’s web dashboard section (earlier npm->pnpm cleanup only caught ‘npm install’, missed this). Also: Contributors section was intentionally removed by user via direct GitHub edit — a later session mistook this for accidental damage and restored it, then reverted after user clarified it was intentional. See decisions.md for the full context so this doesn’t happen a third time.2026-08-08 — LOD step 2: label gating done
Status: Done GraphNode.tsx now gates label visibility on zoom level, matching the icon LOD from step 1: labels hidden below zoomScale 0.5, always shown above 1.5 for high-importance nodes (importCount >= IMPACT_MEDIUM_THRESHOLD), unchanged (hover/select only) in between. Not yet visually verified in-browser — user will check separately. See decisions.md’s LOD entry for the full 3-step plan; step 3 (node radius scaling at low zoom + visual verification) still open.2026-08-09 — Explorer.tsx tabbed panel + DependencyList.tsx, plus missing vendor component
Status: Not Started apps/web/components/explorer/Explorer.tsx (new) wraps existing FileDetails.tsx and ImpactSummary.tsx plus new DependencyList.tsx into a tabbed panel (File/Dependencies/Impact). Zero prior consumers confirmed via grep before writing, so the prop shape is a new design, not an established contract. vendor-ui/shadcn/scroll-area.tsx was missing, blocking file-tree.tsx typecheck alongside a missing @radix-ui/react-accordion dependency; both fixed. tsc clean. Explorer.tsx not mounted on any page yet. Not visually verified in browser.2026-08-10 — Next.js API still returns graph.edges=0 while direct pipeline returns 607
Status: In Progress Confirmed pnpm typecheck (tsc —noEmit -p apps/web/tsconfig.json) passes clean, and pnpm test passes 5 test files / 23 tests. Direct analyzeRepository() via tsx produces 323 nodes and 607 edges for ManSio/mscodebase-intelligence, proving parser/indexer/resolvePath/buildDependencyGraph work outside Next.js. Running Next.js 16.2.12 with webpack on localhost, the real /api/graph request (verified via curl, not just browser) returns HTTP 200 with 323 nodes but 0 edges for the same repo. Therefore the issue is narrowed to the Next.js server runtime/request path or webpack-specific behavior, not the core graph engine — no code fix applied yet. Leading hypothesis, not yet confirmed: the earlier .wasm webpack fix (see gotchas 2026-08-04 entry) may not be effective inside the real webpack-bundled runtime that serves browser requests, even though it works when called directly via tsx (which bypasses webpack entirely). getPythonRuntime() could be silently failing and hitting the catch-all in parsePython.ts, which returns empty imports/exports with only a warnings[] message — DependencyGraph has no field to surface that to the API response. Next step: inspect the pnpm run dev terminal log at the exact moment an /api/graph request is made, looking for ENOENT or “Failed to parse” lines, before writing any fix.2026-08-11 — GraphNode memoized to reduce re-renders
Status: Done GraphNode.tsx wrapped in React.memo — previously every node instance re-rendered on any GraphCanvas.tsx transform change (pan/zoom), even when that node’s own props were unchanged. On graphs with hundreds of nodes this meant hundreds of wasted re-renders per pan/zoom frame. Default shallow compare sufficient since props are primitives/stable refs. Not benchmarked with before/after numbers, just a structural fix based on an obvious gap.2026-08-14 — Hooks done (issue #147) + /api/search on the real engine (issue #9)
[STATUS UPDATE, 2026-08-14]: the “useClipboard/useCommandPalette/ useDebounce/useMediaQuery still stubs” line above is stale — the 3 remaining hooks are now implemented, and the “/api/search … stopgap, SearchEngine.ts still 0%” note is obsolete.Status: Done
hooks/useMediaQuery.ts— thin wrapper over@base-ui/react’sunstable-use-media-query(issue note: prefer re-export over reimplementation).hooks/useClipboard.ts— async Clipboard API + execCommand fallback,copied/error/copywith auto-reset.hooks/useCommandPalette.ts— owns open-state + global shortcuts (Cmd/Ctrl+K, ”/”, Escape);CommandPalette.tsxrefactored to consume it (its inline keydown effect removed). Exported standalone for other surfaces./api/searchrewritten to usebuildSearchIndex+searchfrom packages/search (issue #9); response shape unchanged ({ query, results: [{ moduleId, filePath, score }] }).- Notes: WorkspaceSearch.tsx above says it hits the fuzzyScore stopgap — that’s since upgraded to the real search engine (issue #9), response shape unchanged; GlobalSearch (08-06 entry below) is now mounted on the [org]/[repo]/search page.
- tsc exit 0, eslint 0 on all changed files; not visually verified in browser (same standard gap as other entries).
2026-08-14 — Workspace shell mounted: shared [org]/[repo] layout + /workspace route
Status: Done- New
app/[org]/[repo]/layout.tsxrenders the pre-existing WorkspaceLayout (Navbar + Sidebar + Breadcrumbs) around every repo page — previously the pages rendered standalone full-screen with no app chrome. - New
/[org]/[repo]/workspaceroute renders the Workspace composition root (WorkspaceHeader switcher+search, CommandPalette, Files/Impact/ Issues split pane); Sidebar gained a Workspace link. - Search page upgraded: mounts GlobalSearch (its “waiting on SearchEngine” placeholder was stale — the engine landed in issue #9).
- Page heights adjusted h-screen → h-full so pages fit inside the shell’s flex column; RepositoryHeader nav links dropped (Sidebar owns navigation — no duplicate chrome).
- Verified live: overview/graph/search/workspace/settings all HTTP 200 on a dev server, shell present in SSR, no server errors; tsc 0, eslint 0, vitest 196/196. Not visually verified in a real browser (standard gap).
2026-08-14 — Explorer panel mounted into the graph page (backlog item)
[STATUS UPDATE, 2026-08-14]: the “Explorer.tsx not mounted on any page” notes (2026-08-06 “Still not done” list + status-backlog 08-11 entry) are now RESOLVED.Status: Done GraphViewport is now a flex row: canvas column (flex-1) + a 380px Explorer panel on the right, mounted when a FILE node is selected (folders/external packages have no file source — FileDetails hits /api/file). Explorer is a flex SIBLING of the canvas column, so GraphFocusView’s inset-4 overlay never collides with it; closing the Explorer deselects the node, closing the focus view alone keeps it open. Graph page SSR smoke-tested on a live dev server (HTTP 200, no errors); tsc 0, eslint 0. Not visually verified in a real browser (standard gap).
2026-08-14 — Overview page implemented ([org]/[repo] root page)
[STATUS UPDATE, 2026-08-14]: the “components/overview/ — only ProjectStructure is complete, RepositoryHeader/Info/Overview are stubs” line above is now resolved — all three are implemented and the [org]/[repo] root page renders them.*Status: Done [org]/[repo]/page.tsx (was a “coming soon” placeholder) now renders RepositoryOverview: RepositoryHeader (org/repo + branch badge + nav links), RepositoryInfo (stat strip: modules/nodes/edges/frameworks/ package manager/dependencies/analyzedAt), and the interactive ProjectStructure file tree. POST /api/analyze now also returns a server-side
folderTree (buildFolderGraph — needs the Repository, which
never leaves the server); lib/api.ts gained a postJson helper. Verified
live: /api/analyze on ARCLUX → 533 modules, folderTree root 8 children,
packages/ 36 children; overview page HTTP 200; tsc 0, eslint 0. Not
visually verified in a real browser (standard gap).
2026-08-14 — IssuesPanel + /api/doctor (workspace Issues tab is now real)
Status: Donepackages/engine/runDoctor.ts(new): runs all 19 detectors and normalizes every finding to{ checkId, severity, filePath?, message }— the HTTP counterpart ofarclux doctor’s terminal print. Severity per family: structural = error, hygiene/conventions = warning, informational classifiers (entryPoints, sharedModules, pure barrels) = info; ambiguous symbols map high→error, medium/low→warning.POST /api/doctor(new route): analyzeRepository → runDoctor →{ repoUrl, findings, errorCount, warningCount, infoCount }.IssuesPanel(was an honest “no issues panel yet” placeholder): fetches /api/doctor, renders findings grouped error → warning → info with severity dots + checkId + filePath + message; loading/error/retry states.Workspace.tsxpasses repoUrl/branch through.- Verified live: POST /api/doctor on GSF-001/ARCLUX → 200, 1203 findings (767 errors — real data: 4 circular deps incl. playground fixtures, 432 unused exports, 326 orphans; 386 warnings; 50 info). 6 unit tests (tests/runDoctor.test.ts); tsc 0, eslint 0, vitest 202/202. Not visually verified in a real browser (standard gap).
2026-08-14 — FilesPanel real file tree (last workspace placeholder resolved)
Status: Done FilesPanel (was “file browser coming soon”) now renders the interactive file tree: POST /api/analyze’s server-sidefolderTree (buildFolderGraph,
added in the Overview work) → ProjectStructure. Selection is lifted to
Workspace’s selectedModuleId (shared with ImpactPanel) — clicking a
file in the tree drives the Impact tab, same as WorkspaceSearch.
Cost note: triggers a full clone+index per call, consistent with the
other panels (no caching yet). Verified: /workspace page HTTP 200 on a
dev server, no errors; tsc 0, eslint 0, vitest 202/202. Not visually
verified in a real browser (standard gap).
2026-08-14 — Branch switcher (workspace; last WorkspaceSwitcher gap)
Status: Donepackages/git/getBranches.ts+detectDefaultBranch.tsimplemented (both were 8-line stubs):git ls-remote --heads/--symref HEADvia execFileSync array args (no shell, no injection — same pattern as KI-010).GET /api/branches?repoUrl=(new route):{ branches, defaultBranch }— lightweight, never clones.- WorkspaceSwitcher: branch section in the dropdown (list from
/api/branches, current branch checkmarked); seeds the active branch
from the repo default when none is set. Workspace owns
activeBranchstate and passes it to FilesPanel/ImpactPanel/IssuesPanel — panels refetch when it changes. - Verified live: /api/branches on ARCLUX → 200 with the real branch list; missing-repoUrl → 400; /workspace 200, no errors. tsc 0, eslint 0, vitest 202/202. Not visually verified in a real browser (standard gap).
2026-08-14 — Activity page: commit history + contributors (/api/history)
Status: DonecloneRepositorysupports full clones now:depth: 0means no--depthflag (the arg was previously unconditional, so a full clone wasn’t expressible). Documented on CloneOptions + the git-history helpers’ comments.GET /api/history?repoUrl=&maxCount=&branch=(new route): full clone (depth 0) → getCommitHistory + getContributors → cleanup; returns{ repoUrl, defaultBranch, commits, contributors }. maxCount capped 1..100 (default 50)./[org]/[repo]/activitypage (new) renders ActivityView: recent commits (short hash, date, author, message) + contributors sorted by commit count; Sidebar gained an Activity link.- Verified live: /api/history on ARCLUX → 200, 5 commits (maxCount=5, latest = the #336 merge), 6 contributors (top: GSF-001, 307); /activity page 200, no errors. tsc 0, eslint 0, vitest 208/208. Not visually verified in a real browser (standard gap).
2026-08-14 — Universal Responsive UI (mobile-first, Termux-friendly)
Status: Done (tsc 0, eslint 0, vitest 295/295, dev-server live: 6/6 pages HTTP 200, SSR HTML contains all new chrome). Visual pass on an actual phone still pending (Mikatoshi).hooks/useBreakpoint.ts(new): { isMobile, isTablet, isDesktop } over the existing useMediaQuery wrapper (issue #147: re-export, not reimplementation). Breakpoints match Tailwind defaults (md=48rem, lg=64rem). Visibility itself is Tailwind-class driven (hydration-safe); the hook only feeds interactive behavior.components/layout/BottomNav.tsx(new): mobile-only (<md) 5-tab bar (Overview/Graph/Search/Activity/Workspace) as a flex child of the layout (notfixed— never overlaps content or the graph canvas), safe-area padding for notched phones, active tab highlight, 44px+ targets.Sidebar.tsx: desktopw-64collapsible to aw-16icon rail (transition-all duration-300); tablet overlay mode (overlay+ onClose); mobile hidden (BottomNav owns nav). Premium:bg-sidebartoken + shadow, noborder-r.Navbar.tsx: org/repo props; menu button (tablet opens overlay, desktop collapses sidebar — decided via useBreakpoint); Settings link visible only <md (it lives in the sidebar on larger screens); icon buttonssize-11 md:size-8(44px touch target on mobile); noborder-b.WorkspaceLayout.tsx: now “use client” with sidebar collapse state + overlay drawer (always mounted, CSS-hidden on lg+ so resizing to desktop mid-gesture is safe). Breadcrumbs strip usesbg-muted/40instead ofborder-b.GraphCanvas.tsx: native pinch-to-zoom (two-pointer distance ratio, zoom-to-midpoint), single-finger pan fallback after lifting one finger,touch-noneon the canvas container (no browser scroll hijack), viewport culling for nodes AND edges (selected/hovered always render; CULL_MARGIN=100 world units), coarse-pointer hit radius (44px) via(pointer: coarse)media query.GraphNode.tsx: optional invisible hit-area circle (22px radius) for coarse pointers — visual 6px dot still meets the 44px tap target; rendered last so it sits on top (other shapes are pointer-events-none).GraphEdge.tsx: memoized (props stable across pan/zoom frames — same reasoning as GraphNode memo).GraphViewport.tsxExplorerPanel: <md becomes a full-screen overlay (a 380px flex sibling would leave the canvas ~0px on a phone); md+ keeps the 380px sibling. Branches on useMediaQuery — safe because the panel only mounts after a client-side node selection.GlobalSearch.tsx: results listborder→bg-card shadow-lg, taller tap targets.- No new npm dependencies (pinch is native Pointer Events; verified @use-gesture/react v10.3.1 is 2 years stale with unconfirmed React 19 compat).
- Doc contradiction fixed (§4.9):
theme/arclux.jsonreferenced in 7 places but never existed — all updated to theme/colors.ts + theme/theme.dark.ts (KI-030). - Pre-existing build blocker found (not touched, out of scope):
app/api/diagnostics/route.ts scaffold has no exports →
next buildfails its type-check on the generated route type (KI-029).
cd apps/web && npx tsc --noEmit 0; pnpm --filter web lint
0; pnpm test 295/295; pnpm --filter web build compiles but fails
final type-check on KI-029 only; dev-server: /test/test + graph/search/
workspace/activity/settings all HTTP 200, SSR HTML contains
Toggle-navigation / aria-label=Primary / bg-sidebar markers, no
console errors. Not visually verified on a real phone (standard gap).
2026-08-14 — Glassmorphism UI polish (1/3) + KI-029 systemic build fix
Status: Done (tsc 0, eslint 0, vitest 295/295,next build now
PASSES — previously failed on the scaffold routes, dev-server 6/6 pages
200). PR #363.
- Glass utilities in
app/globals.css(@layer components):.glass-panel(bg-background/60 + blur-md + shadow-2xl),.glass-overlay(bg-background/80 + blur-lg),.glass-card(bg-background/40 + blur-sm, hover→/50),.glass-topbar(bg-background/70 + blur-md, applied on scroll). Colors go through theme tokens (bg-background / border-foreground) instead of the hardcoded zinc from the request — equivalent effect in dark mode (#000 background), and the light theme keeps working. - Applied (chrome only, Mikatoshi’s “1/3”): Sidebar inline (glass-panel) + overlay drawer (glass-overlay), BottomNav (glass-overlay), Navbar (glass-topbar when content scrollTop > 10px — scroll listener lives on the overflow-auto container in WorkspaceLayout, not window), RepositoryInfo stat cards + ActivityView commit/contributor cards (glass-card, border/neutral hardcodes replaced with tokens), GraphFocusView + GraphContextMenu + CommandPalette (glass-panel). NOT applied: graph canvas (solid black, perf), code blocks/FileDetails, search result list (readability).
- Performance guards (in globals.css):
@media (max-width: 768px)→ backdrop-filter blur(4px);prefers-reduced-motion→ backdrop-filter none + solidvar(--background). - KI-029 systemic:
next buildtype-check failure was NOT one scaffold but six — app/api/{diagnostics,editor,notifications,processes,runtime, services}/route.ts were 8-line stubs with no exports. All six now export valid GET handlers returning explicitly-empty 200 JSON (honest “nothing reported yet” semantics documented in each file; NOT faking a scan).next buildpasses end-to-end. - Shipping note: PR #362 (responsive UI) was merged while this work was in flight — the glass commit was pushed to the already-merged branch, so it was re-based as a fresh branch (feat/glass-ki029) off the updated ARCLUX.main and opened as PR #363.
2026-08-15 — Inline diagnostic gutter markers in FileDetails (line-level, not just file-level impact)
Status: Done FileDetails.tsx previously rendered source as flat token-highlighted <pre> with no way to see WHICH line a diagnostic finding referred to — Impact tab only showed affected FILES, not line-level detail. Rewrote to render per-line: each line gets a gutter (colored bar for error/warning severity + line number), clicking a marked line expands the diagnostic message + fix suggestion inline, editor-style. Explorer.tsx now fetches POST /api/diagnostics once per repoUrl/branch (not per file switch — endpoint does full clone+index per call, same cost as /api/analyze) and filters events client-side to the open file’s path, passed to FileDetails as a diagnostics prop. See PR #389.2026-08-15 — Fixed gutter marker bugs found via browser testing
Status: Done PR #389’s inline gutter markers had 2 bugs caught by actual browser testing (screenshots): React key collision when the same checkId fires twice on one line (fixed: key is now checkId+index), and markers were nearly invisible (thin 2px bar only — changed to whole-row background tint at 10% opacity + thicker bar). Confirmed working visually: cyclicA.ts/cyclicB.ts (express-demo) show 2-3 circular dependency findings with expandable messages. See PR #391.2026-08-15 — Collapsible project structure tree in RepositoryOverview
Status: Done Added a chevron toggle button (only visible once a file is selected) between the project tree and Explorer panel. Collapsing the tree expands Explorer to full width instead of a fixed 45%, so File/Dependencies/Impact tabs aren’t cramped. Requested after visual testing showed the tree taking up too much space when viewing file diagnostics. See PR #393.2026-08-15 — Syntax highlighting extended to JS/TS (was Python-only)
Status: In Progress Added packages/parser/javascript/highlightJs.ts using the same walk-up-from-cwd wasm loading pattern as highlightPython.ts, with query merged from official tree-sitter-javascript + tree-sitter-typescript highlights.scm files. Wired into /api/file/route.ts. Still Go/Java/other languages — tree-sitter-wasms has grammars for go.wasm, java.wasm, etc (confirmed present), but each needs its own highlights.scm sourced from that language’s official tree-sitter grammar repo before highlighting can be added — same process as this PR, just not done yet for those languages. Not yet visually verified in browser for JS/TS either. See PR #395.2026-08-15 — Add docs page for SKILL.md
Status: Done docs-site/skill.mdx added under Getting Started: points to SKILL.md on GitHub, summarizes what it covers, one example command. Registered in docs.json nav.2026-08-16 — 3D graph canvas: unify node palette with the 2D view
Status: In Progress GraphCanvas3D.tsx hardcoded its own NODE_COLORS that differed from the 2D canvas for ALL six node types (e.g. external-package rendered as the 2D folder color), so toggling 2D/3D changed a node’s color and the GraphMenu legend was wrong in 3D. Now imports graphNodeColors from @/theme/graphColors (dark variant), matching GraphNode.tsx’s getGraphNodeColor(node.type, “dark”). Verified with npx tsc —noEmit -p apps/web/tsconfig.json. See PR #471.2026-08-16 — 3D graph view added
Status: In Progress New GraphCanvas3D.tsx using react-force-graph-3d (Three.js/WebGL, d3-force-3d physics). Toggle button added to GraphViewport.tsx to switch between 2D (existing custom d3-force canvas) and 3D view. Reuses same DependencyGraph data from GraphProvider context, no changes to data pipeline. Not yet visually tested end-to-end.2026-08-16 — 3D graph: 2D parity (importance rings, events, mobile perf) + restored deps
Status: In Progress GraphCanvas3D.tsx brought to functional parity with the 2D canvas: importance rings (torus halo for fan-in >= 20/100 — same tiers as GraphNode.tsx’s impact halo), node size by fan-in (nodeVal), selection white fill, hover tracking, background-click deselect, right-click context menu (position synthesized via PointerEvent since right-click alone doesn’t fire pointermove), double-click camera focus, controlType=“orbit” (predictable mobile gestures), showNavInfo off, and coarse-pointer optimizations (sphere resolution 6, no link particles, no MSAA via rendererConfig.antialias=false). P0 fix: the #470 revert silently dropped react-force-graph-3d/three from apps/web/package.json during the #471 3-way merge (revert’s deletion won), so deps restored + @types/three added. tsc 0, lint 0, suite 487/487.2026-08-16 — 2D graph: node color mapping fix (route/hook/component classification)
Status: In Progress Root cause of “uniform blue” 2D nodes: packages/graph/buildDependencyGraph.ts emits every node as type:“file” (semantic route/component/hook types are only assigned by indexer resolvers that feed detectors, not the rendered graph; external-package/folder nodes are TODO). Fixed 2D-side (GraphNode.tsx only, no shared/3D changes): getEffectiveNodeType() classifies from the same conventions when node.type===“file” (Next.js App Router entry set from resolveRoutes.ts, use* hook prefix, PascalCase .tsx components); real node.type wins otherwise. Icons follow the effective type (nodeIcons already covers all 6). Palette untouched (graphColors.ts is shared with 3D — legend already matches); rings/size/perf untouched (halo LOD + memo already in place). tsc 0, lint 0, classification validated on 8 real paths (incl. ARCLUX’s own app/hooks/components).2026-08-16 — 2D graph: fade-out on selection, selection glow, route-link dash
Status: In Progress Visual polish (GraphCanvas/GraphNode/GraphEdge, all 2D-only): (1) fade-out — selecting or hovering a node dims everything NOT connected to it (connected set memoized per active node; nodes opacity 0.3, edges 0.12, selected/hovered stay full) so the active subgraph reads instantly; (2) selection glow — double accent ring (r+5 @ 0.5, r+8 @ 0.15) around the selected node; (3) route-link edges now dashed (“4 3”) per the edge legend. Edge colors already matched the legend via getGraphEdgeColor (import #454545 / export #9D7CD8 / call #52A8FF / route-link #56B6C2). z-index overlay layering verified (Search z-10 left, Menu z-10/20 right, FocusView z-20, context menu z-50). tsc 0, lint 0, suite 487/487.2026-08-16 — 2D: material-style icons by file extension/folder + type color border; P0 junk purge
Status: In Progress (1) nodeIcons.tsx: getMaterialIcon(fileName, isFolder) — extension-based icons (.py/.ts/.tsx/.js/.json/.yaml/.toml/.env/.md/.txt/.sql/.db, Dockerfile/docker-compose) and architectural folder icons (src/tests/components/core/utils); getNodeIconPath(type, fileName?) resolves file/folder nodes through it, others keep their type shape. GraphNode + GraphFocusView pass node.label. (2) GraphNode: thin node-type-colored border (stroke 0.8 @ 0.4; white + glow when selected) — legend badge look. (3) P0: PR #474’s merge commit (6fb8557) injected merge-conflict junk lines (“fix/graph-menu-3d-toggle”, “ARCLUX.main”) into GraphCanvas3D.tsx (7 sites), apps/web/package.json (broken JSON), pnpm-lock.yaml (6 sites) — broke 3D chunk (ChunkLoadError) and tsc. Restored all 3 files from the clean 9c066ae (kept 3D dev’s rich-visual commit, @types/three ^0.185.4). tsc 0, lint 0, suite 487/487.2026-08-16 — Graph polish: hub count badges, drawer default tab = Impact, icon map extended
Status: Done (tsc 0 apps/web, eslint 0) (1) GraphNode.tsx: numeric hub badges — small pill at node top-right, ONLY when importCount >= 5 AND zoom >= 1.0 (same LOD tier as the impact halo). Tiers: red #FF3B30 for >100, orange #FFB224 for 20-100 (dark text), neutral #3D444D for 5-19. 2D impact halo re-stroked from node-type color to red/orange (was type-colored) so 2D/3D signal importance identically (3D already used IMPACT_TINT_HIGH/MEDIUM). (2) Explorer.tsx: drawer now opens on the Impact tab (was File) when a node is clicked, and resets to Impact when the inspected node changes; consistent with Workspace.tsx’s Tabs defaultValue=“impact”. (3) nodeIcons.tsx: material-style icon map extended to every ARCLUX-supported language (.mjs/.cjs/.go/.java/.cs/.php/.rb/.rs/.cpp/.cc/.h/.hpp) + web/assets (.css/.scss/.sass/.less/.html/.htm/.xml/.svg/.png/.jpg/.jpeg/.gif/.webp/.ico) + dotfiles (.gitignore/.editorconfig/.prettierrc/.eslintrc/.babelrc/.npmrc); Dockerfile/JSON/YAML/TOML/py/ts/js already mapped. Mapping logic verified 32/32 cases via sandbox test.2026-08-16 — Drawer fix: Dependencies default (instant, from context) + 3D hub count badges
Status: Done (tsc 0 apps/web, eslint 0, next build 0) (1) Drawer “didn’t show a panel” root cause: default tab was Impact, and GET /api/impact re-clones + re-indexes the ENTIRE repo per call (no caching) — the drawer sat on a spinner/error instead of content. Additionally DependencyList was re-fetching the graph via /api/graph (another full clone) even though the graph is already in GraphProvider context (Explorer renders inside it). Fixed: default tab = Dependencies; DependencyList rewritten to read useGraphContext() — zero network calls, instant content. Impact stays one click away (its slowness is a known backend limitation). (2) 3D hub count badges: canvas-texture sprites hovering above nodes (y=11, scale 8x2), same rule as 2D (importCount >= 5) and same tier colors (red >100 / orange 20-100 / neutral 5-19, reusing IMPACT_TINT_*); textures cached by count since nodeThreeObject rebuilds objects on selection/hover. No zoom gate in 3D (sprites scale naturally with camera distance; no per-frame camera hook) — documented in code.2026-08-16 — Central panel back for file nodes (no overlap), bigger badges, 3D zoom dolly fix
Status: Done (tsc 0 apps/web, eslint 0, next build 0) (1) GraphFocusView’sif (node.type === "file") return null guard removed — the central panel (This file needs / If you change this) now opens for EVERY node, including file nodes, and coexists with the right Code Drawer: on md+ the panel shifts left of the drawer’s 480px width (right-[31rem]) so the two never overlap. The old clipping bug (drawer covering the modal) is fixed by LAYOUT, not by hiding one surface. (2) Count badges enlarged: 2D pill 8→11px tall with 8.5px font (was 7), 3D sprite scale 8x2 → 16x4 (2x the sphere diameter) with 44px texture font (was 34) so numbers read at default camera distance. (3) 3D zoom buttons were moving the camera along the world Z axis only (x/y fixed) — with the isometric default view that shifts the view direction and reads as “rotating instead of zooming”. Fixed with dolly3D(): camera moves along the camera->lookAt axis (new pos = lookAt + (pos - lookAt) * factor), anchored on the focus point. GraphMenu.tsx.
2026-08-16 — Hub counts readable at overview + central panel collapse button + 3D edge highlight
Status: Done (tsc 0 apps/web, eslint 0, next build 0) (1) Count badges now keep a CONSTANT screen size at any zoom (user: “numbers only appeared at near zoom”). 2D: the badge group is counter-scaled (transform scale(1/zoomScale), anchored at the pill center so it stays glued to the node’s top-right); the zoom gate dropped from 1.0 to 0.35 (only prevents pill-on-pill overlap in the most zoomed-out overviews). 3D: SpriteMaterial sizeAttenuation:false, sprite scale 88x22 CSS px (matches 4:1 texture) — readable at overview distance. (2) GraphFocusView (the restored central panel) got a collapse/expand button pinned to its right edge, vertically centered (ChevronRight/ChevronLeft; moves with the panel to right-[31rem] when the drawer is open). The collapsed state persists across node changes for manual control — lets the user tuck the panel away and navigate the map by clicking nodes. (3) 3D now highlights the selected node’s edges: links carry ahighlighted flag (source/target === selectedNodeId, graphData depends on selectedNodeId) and linkColor renders bright #E5E5E5 for highlighted vs dim rgba(255,255,255,0.12) — same navigation affordance the 2D canvas already had via its connected-set fade-out.
2026-08-16 — Collapse button slides left when panel closed + desktop sidebar auto-collapse to icons
Status: Done (tsc 0 apps/web, eslint 0, next build 0) (1) GraphFocusView’s collapse button no longer floats on the right after collapsing: while the panel is open it stays pinned to the panel’s right edge (right-4 / right-[31rem], half-outside via translate-x-full); when collapsed it slides to the canvas LEFT edge (left-4) with the chevron flipped (open → ChevronLeft “collapse”, collapsed → ChevronRight “expand”); transition-all animates the move. (2) Desktop sidebar (WorkspaceLayout) now starts COLLAPSED to a w-16 icon rail and expands on hover (temporarily) or via the Navbar menu button (pinned): effective width = !sidebarCollapsed || sidebarHovered, with onMouseEnter/onMouseLeave on the desktop wrapper. (3) Sidebar.tsx got the transition-all duration-300 its own doc comment claimed for the width animation but was missing from the className.2026-08-16 — 3D white-out fixed: SpriteMaterial sizeAttenuation:false scale is NOT pixels
Status: Done (tsc 0 apps/web, eslint 0) The 3D view turned all-white/gray at every zoom level (colored circles and links faintly visible through it). Root cause: the constant-screen-size badge attempt usedSpriteMaterial({ sizeAttenuation: false }) with sprite.scale.set(88, 22, 1). In three.js the sprite vertex shader applies scale *= -mvPosition.z when sizeAttenuation is off, so scale is multiplied by camera DEPTH — an “88px” badge becomes a quad ~19,000 CSS px tall (screen px = scale * viewportHeight / (2*tan(fov/2))), a giant sheet over the whole viewport. The renderer clear color was correctly black (library setClearColor from backgroundColor) — the quad was the culprit. Fixed: sizeAttenuation stays TRUE (world-space, perspective-correct, scales with distance) with sprite scale 20x5 (2.5x the sphere’s diameter, matches the 4:1 texture) — readable at the default camera distance, shrinks naturally when zoomed out. Verified against the shader source node_modules/three/src/renderers/shaders/ShaderLib/sprite.glsl.js. Trap documented in progres/gotchas.md (KI-055).
2026-08-16 — Zoom anchors fixed (2D cursor / 3D real target) + python icon no longer reads as a crossed-out circle
Status: Done (tsc 0 apps/web, eslint 0, next build 0) (1) 2D mouse wheel zoom now zooms toward the CURSOR: the world point under the pointer stays fixed on screen (old code scaled around the top-left transform anchor, so the view drifted up-left on every wheel step — user: “it zooms somewhere above”). Touch pinch untouched (already zooms around the fingers’ midpoint). (2) 3D +/- dolly now anchors onfg.controls().target — the real orbit/rotation center (screen center). The previous anchor came from the cameraPosition() getter’s lookAt, which in 3d-force-graph is a SYNTHETIC point 1000 units in front of the camera ((0,0,-1000).applyQuaternion(…) + position, verified in the dist source) — for cameras closer than 1000 units that point sits BEYOND the graph, so each zoom pushed the target past the graph and the view drifted (“zooms above”). (3) The Python node icon (infinity + diagonal slash M-2,2 L2,-2) read as a prohibition sign — a crossed-out circle — at 6px node size on every .py file of Python repos. Removed the slash, kept the lemniscate.
2026-08-16 — 3D: clicking a node no longer restarts the d3 simulation
Status: Done (tsc 0 apps/web, eslint 0, next build 0) Clicking any node in 3D re-rendered the whole graph from scratch. Root cause: the edge-highlighting feature marked links with ahighlighted flag inside graphData, and graphData’s useMemo depended on selectedNodeId — a new nodes/links array on every click made react-force-graph-3d treat it as a full graphData change and restart the d3-force simulation (positions re-computed from scratch). Fixed: selectedNodeId removed from the graphData deps (simulation stays stable); the highlight now flows through the linkColor accessor closure — a fresh arrow function per render changes its identity, the kapsule’s changedProps check (hasAnyPropChanged in 3d-force-graph dist, linkColor is in the links-rebuild list) rebuilds LINKS ONLY, never the simulation. The nodeThreeObject closure (selected node → white core/glow) already worked the same way. Guard: graphData is the simulation source — never derive selection visuals from it; use accessor props (nodeThreeObject/linkColor/linkWidth).
2026-08-16 — Count badges: pill → CIRCLE (2D + 3D)
Status: Done (tsc 0 apps/web, eslint 0, next build 0) User: “make all the numbers be in a circle, the author wanted it that way”. (1) 2D GraphNode: the rounded-rect<rect rx> badge became a <circle> — radius grows with the digit count ((digits5.2)/2 + 4.5), centered where the pill’s center was (node top-right), counter-scale for constant screen size preserved; text stays 8.5px monospace with the same tier colors. (2) 3D getBadgeTexture: the rounded pill is now a ctx.arc circle filling the canvas height (diameter = height, horizontally centered); the font auto-shrinks to fit the circle (min(88, D0.52/(digits*0.58))), sprite scale 20x5 → 24x6 so the circle renders as a true 6-unit circle (0.75x sphere diameter) on the 4:1 quad. Tier colors (red/orange/neutral + dark text on orange) unchanged.
2026-08-16 — Count badge moved into the node center (2D): number in the middle of the circle
Status: Done (tsc 0 apps/web, eslint 0, next build 0) User: “the numbers should be not to the side but in the center of the circle”. The 2D badge was a circle sitting to the top-right of the node; now the badge circle is CENTERED ON THE NODE (the count sits in the middle of the node circle): the group is scaled 1/zoomScale around the node origin (no translate), radius still grows with digit count, tier color fills the disc, and the node TYPE color is preserved as a thin ring (stroke=color, opacity 0.8) so the legend identity survives. File icons are skipped for hubs (count >= 5) since the number replaces the icon at the node center. Counter-scale kept — readable at overview. BADGE_HEIGHT constant removed. 3D untouched (its badge floats ABOVE the node, not to the side).2026-08-16 — 3D badge centered on the sphere too (number in the middle of the node)
Status: Done (tsc 0 apps/web, eslint 0, next build 0) User: “in 3D too” (follow-up to the 2D centered badge). The sprite badge moved from y=12 (above the sphere) to the NODE CENTER (0,0,0) — the count now sits in the middle of the sphere circle like the 2D badge. The texture gained an optional node-TYPE-color ring (8px stroke at radius-4, ~6% of the diameter) so the legend identity survives, matching the 2D stroke. getBadgeTexture(count, ringColor?) now caches by${count}|${ringColor} (bounded: counts × type colors); NodeDatum.ringColor carries the base NODE_COLORS color (pre-importance-tint). Text centered exactly (fillText at cy). depthWrite:false and sizeAttenuation:true unchanged (the KI-055 trap).
2026-08-16 — 3D numbers were INVISIBLE: sprite at node center fails depth test — count now drawn ON the sphere
Status: Done (tsc 0 apps/web, eslint 0, next build 0) After moving the badge sprite to the node center (0,0,0) the numbers became completely invisible: a sprite is a flat quad at the sphere-center depth, while the sphere’s FRONT surface is closer to the camera — the sprite fails the depth test and is hidden by its own sphere (transparent pass renders after opaque but depthTest stays on). Fixed by removing the sprite entirely: for hubs (count >= 5) the number is drawn ON the sphere — a square, fully opaque canvas texture (tier fill + type-color ring + digit dead-center) becomes the sphere’s map (MeshBasicMaterial); the hub core is scaled x1.3 for readability. This is the true “number in the center of the node circle” in 3D, with no occlusion artifacts. getBadgeTexture/sprite cache removed, replaced by getSphereBadgeTexture(count, ringColor) cached by count|color. Guard: a flat sprite at the sphere center is invisible (depth test) — numbers belong on the sphere’s own texture; a sphere material with a transparent map would erase its edges — fill fully. (KI-061)2026-08-22 — Web parity wave 1: security/verify/script routes + panels
- /api/security, /api/verify, /api/script (HTTP counterparts of CLI commands; doctor pattern). /api/security uses result.securityAnalysis (in-pipeline snapshot) + mapAttackSurface.
- Workspace tabs: Security + Verify + Health + Calls (HealthPanel = computeHealthScore aggregation, roadmap Phase 2; CallsPanel = list view over buildCallGraph, GraphCanvas untouched).
- /script DSL playground v1 (examples dropdown, JSON tree output, localStorage). Landing LanguageCoverage fix: “2 languages” → 25+2.
2026-08-22 — Script playground v2: opencode-style terminal
- Full terminal window: titlebar tabs (script/audit), syntax highlight (zero-dep tokenizer overlay), slash-command palette (11 commands, arrow/tab/enter nav), transcript blocks with per-run echo+ms, ctrl+enter run, Tab indent, localStorage persist.
2026-08-22 — AUDIT feature (3 PR: #545 base, #546 theater, #557+#558 graph)
- /api/audit: composition only — runDoctor + securityAnalysis + mapAttackSurface, grouped into chapters, DOCTOR_CATEGORIES exported from healthScore.ts (single source of grouping truth).
- AuditWorkspace (used by playground mode AND /[org]/[repo]/audit): STREAM × FOCUS × GRAPH. Boot systemd-style ”[ OK ]”, scan script built from REAL findings, 55ms reveal, progress rail.
- FOCUS panel: one finding at a time (never a dump), severity banner, verbatim messages, file preview overlay via /api/file (raw.github).
- AuditGraphPane: isolated GraphProvider+GraphCanvas3D instance, camera flights via CameraControls pattern (GraphMenu), cycle path HUD.
- Graph audit theater (#557): useGraphAuditOverlay drives severity halos from OUTSIDE via fgRef (node.__threeObj) — zero core changes; GraphAuditOverlay chip replays findings 60ms apart.
- BUG FIXED (#558): halos never appeared — GraphCanvas3D node objects carry no filePath, so scene-built path map was always empty. Map now built from GraphProvider graph. Chip renders in 2D too; clicking auto-enables 3D.
2026-08-23 — Nav registry + pending indicators + premium terminal
- lib/navigation.ts: single source of truth; Sidebar (glass groups, motion layoutId sliding pill), BottomNav 4 fixed + spring More-sheet, CommandPalette upgraded + mounted globally (Ctrl+K / ’/’ everywhere).
- useLinkStatus pending spinners on every nav surface; palette keeps open with row spinner until pathname matches.
- Terminal premium: prompt ”$” prefixes, systemd [ OK ] tokens, cursor blink steps(1), CRT scanlines, seamless block, JetBrains Mono via next/font (—font-mono stack).
- ViewModeToggle (auto/desktop/mobile, localStorage persist).
- Registry labels English (Explore/Inspect).