Skip to content

feat(file-browser): Modularize navigation history management - #2793

Open
AuDevTist1C wants to merge 1 commit into
Acode-Foundation:mainfrom
AuDevTist1C:refactor/fb-nav
Open

feat(file-browser): Modularize navigation history management#2793
AuDevTist1C wants to merge 1 commit into
Acode-Foundation:mainfrom
AuDevTist1C:refactor/fb-nav

Conversation

@AuDevTist1C

@AuDevTist1C AuDevTist1C commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

This pull request refactors and modernizes navigation history management within the Acode file browser module (src/pages/fileBrowser/). Previously, navigation state tracking, local storage persistence, navbar breadcrumb DOM updates, and back-action stack synchronization were directly coupled and scattered across procedural helper functions in fileBrowser.js.

To improve maintainability, reactivity, and performance, this change introduces a dedicated NavStack class (src/pages/fileBrowser/NavStack.js) extending the standard Web API EventTarget. The internal state management now utilizes $O(1)$ set lookups, microtask-batched reactivity for dynamic DOM diffing, and cleaner lifecycle tracking.


Architectural Motivation & Design Overview

1. Separation of Concerns & Class Encapsulation

In the original implementation, history state was stored in a mutable array (state) that required manual filtering, array slicing, continuous localStorage serialization calls, and DOM query cleanups across functions like Maps(), pushState(), loadStates(), and button action listeners.

By abstracting state into an isolated NavStack class, navigation operations (push, pop, popUntil, get, has) are encapsulated behind a clear API contract. Internal tracking uses private class fields (#arr for chronological stack ordering and #urlSet for $O(1)$ duplicate checking), preventing illegal external mutations and guaranteeing data integrity.

2. Batched Reactivity via Microtask Scheduling

Rather than triggering synchronous UI redraws or immediate localStorage writes on every individual stack mutation, NavStack leverages queueMicrotask() to batch pending updates. When history changes occur, additions and removals are recorded in a transient #updatedURLs structure containing an added map and a removed set.

At the end of the current task execution tick, an update CustomEvent is dispatched. UI listeners (such as the main handler in fileBrowser.js) receive read-only iterators over event.detail.added and event.detail.removed, enabling unified, single-pass DOM breadcrumb synchronization, back-action stack pruning, and state persistence.


Technical Implementation Details

1. NavStack Class (src/pages/fileBrowser/NavStack.js)

  • Inheritance & Custom Tag: Inherits from native EventTarget and defines Symbol.toStringTag as "NavStack" on its prototype for proper introspection.
  • Encapsulated Private Fields:
    • #arr: Internal Array<Location> preserving historical stack ordering.
    • #urlSet: Internal Set<string> used for instant URL membership checks and duplicate prevention.
    • #updatedURLs: Internal state object capturing differential additions (Map<string, {name, index}>) and removals (Set<string>) between task ticks.
  • Microtask Scheduler (#queueUpdateEvent): Ensures event emission is deferred to microtask timing, coalescing multiple sequential stack operations into a single update dispatch.
  • Core API Methods:
    • push(url, name): Accepts location objects or raw URL strings, validates inputs, checks #urlSet, appends state, and updates batch diffs.
    • popUntil(url): Truncates history backwards to a target URL while collecting removed URLs for event notification.
    • pop(): Convenience wrapper over #popUntil() to pop the top location entry.
    • get(i): Provides standard and negative-indexed array lookup (e.g., get(-1) yields the current top location) returning a cloned location object to preserve immutability.
    • has(url): Instant $O(1)$ set validation replacing full array iteration.
    • toJSON(): Returns a deep copy array of location objects for seamless compatibility with JSON.stringify().

2. File Browser Refactoring (src/pages/fileBrowser/fileBrowser.js)

  • State Management Integration: Replaced the local state array and explicit pushState() helper function with a NavStack instance (navStack).
  • Navbar Element Caching: Introduced a navBarEls Map to cache HTMLSpanElement DOM nodes and avoid redundant DOM element recreations.
  • Event Listener Integration:
    • Automatically serializes navStack to localStorage.fileBrowserState upon update events.
    • Removes actionStack entries and corresponding navbar DOM nodes for all items in ev.detail.removed.
    • Registers back-navigation actions in actionStack and calls pushToNavbar for newly added locations in ev.detail.added.
  • Method Simplification:
    • Maps(url, name): Polymorphic parameters (supports location objects or string pairs). Uses navStack.has() and navStack.popUntil() for back-navigation truncation, followed by directory fetching and navStack.push().
    • loadStates(states): Sequentially pushes stored location history into navStack within an error-guarded loop and immediately navigates to the top element using navStack.get(-1).
    • render(dir): Centralized folder-mode selection button state evaluation ($openFolder.disabled = (url || "/") === "/").

Performance & Benchmark Comparison

Metric / Scenario Legacy Implementation NavStack Implementation Operational Impact
Duplicate Checking Complexity $O(N)$ iteration via Array.prototype.find $O(1)$ lookup via Set.prototype.has Instant validation regardless of history depth
Navbar DOM Removal Loop through $navigation.lastChild until target Batched removed Set iteration via event detail Prevents layout thrashing and unnecessary reflows
Navbar Element Creation Re-creates span elements on each navigation Cached via Map Reduced DOM allocations and Garbage Collection load
Storage Persistence Synchronous localStorage write on every pushState Batched once per microtask turn Minimizes main-thread I/O blocking
Negative Stack Indexing Manual length calculation (state[state.length - 1]) Native negative index support (navStack.get(-1)) Cleaner code readability and immutability

Synchronous Rendering & Microtask Decoupling

It is important to emphasize that while internal history diffing, event notification, navbar DOM updates, and localStorage writes are queued asynchronously via microtasks in NavStack, directory listing fetching and UI component rendering (render()) continue to be executed synchronously outside microtasks inside Maps().

Architectural Rationale:

  1. Immediate Visual Feedback: Leaving getDir() and render() directly within the main call stack ensures that directory content renders without frame delays or visual flicker during user interaction.
  2. Decoupled View vs. State Sync: The NavStack microtask pipeline is strictly responsible for updating background infrastructure (breadcrumb navigation elements, back-button action stacks, and local storage state). Separating view rendering from state event batching guarantees that layout painting remains responsive while background synchronization happens cleanly before the next paint frame.

(PR name and description are AI generated (Gemini 3.6 Flash))

@greptile-apps

greptile-apps Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Commit a478f9083bfa486db6713a875790a15dbffd3d00 modularizes file-browser navigation history without leaving an eligible follow-up finding.

  • Introduces an event-driven NavStack with deduplicated URLs and batched update notifications.
  • Moves breadcrumb, back-action, and persisted-state synchronization into the stack update listener.
  • Updates navigation and session restoration to use the new stack abstraction.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains within the eligible follow-up-review scope.

No blocking failure remains.

Important Files Changed

Filename Overview
src/pages/fileBrowser/NavStack.js Adds the encapsulated navigation stack, duplicate tracking, immutable reads, and microtask-batched differential events.
src/pages/fileBrowser/fileBrowser.js Integrates NavStack with navigation, breadcrumb rendering, back actions, persistence, and session restoration.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Navigation request] --> B[getDir]
  B -->|Success| C[NavStack mutation]
  C --> D[Render directory]
  C --> E[Microtask update event]
  E --> F[Persist history]
  E --> G[Update breadcrumbs]
  E --> H[Synchronize back actions]
Loading

Reviews (5): Last reviewed commit: "feat(file-browser): Implement `NavStack`..." | Re-trigger Greptile

@AuDevTist1C
AuDevTist1C marked this pull request as ready for review August 22, 2026 12:10
@AuDevTist1C
AuDevTist1C force-pushed the refactor/fb-nav branch 4 times, most recently from f338380 to 1e95b19 Compare August 22, 2026 14:27
… management

Abstract navigation tracking, history state management, and navbar UI syncing into a dedicated `EventTarget` class.

Create `NavStack` class (`src/pages/fileBrowser/NavStack.js`):
- Implement `NavStack` extending `EventTarget` with a custom `Symbol.toStringTag` property
- Add `push`, `pop`, `popUntil`, `get` (supporting negative indexing), `has`, `on`, `off`, and `toJSON` methods with parameter validation
- Maintain an internal `#urlSet` to prevent duplicate stack entries
- Queue microtasks for `update` event dispatching, providing read-only `added` and `removed` location diffs in event details

Integrate `NavStack` into file browser (`src/pages/fileBrowser/fileBrowser.js`):
- Replace manual `state` array and direct `localStorage` persistence with a `NavStack` instance
- Listen to `update` events on `NavStack` to persist state to `localStorage`, clean up removed navbar elements and `actionStack` entries, and register new back-navigation actions
- Cache navbar DOM elements using a `navBarEls` Map with `getOrInsertComputed`
- Refactor `navigate` to accept location objects or strings and manage stack state using `navStack.has`, `navStack.popUntil`, and `navStack.push`
- Refactor `loadStates` to push history entries into `navStack` and navigate directly to the top item (`navStack.get(-1)`)
- Update folder selection button state (`$openFolder.disabled`) in `render()` and remove obsolete `pushState()` helper function

(AI generated commit message)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

1 participant