Docs

Pulsar Upstream Fix Candidates

A running list of bugs we’ve fixed (or pinned down) in the Tranquil fork that exist in upstream Pulsar and would be worth contributing back. Entries here are inherited defects with a general-purpose fix — not Tranquil features or fork-specific behavior.

When adding an entry, include: the symptom, the root cause, where our fix lives, and its upstream status.

1. Cross-container pane-item moves desync the ItemRegistry

Status: fixed in Tranquil (2026-08-03), not yet upstreamed.

Symptom: Uncaught Error: The workspace can only contain one instance of item [object Object] from ItemRegistry.addItem, typically after dragging a pane item between docks (or dock ⇄ center) and then clicking a tab. A long-standing Atom-era error inherited by Pulsar.[^pulsar-item-registry]

Root cause: each pane container (center + each dock) has its own ItemRegistry, updated only by didAddPaneItem / didDestroyPaneItem — both of which are skipped when an item is added/removed with moved: true. Every move path funnels into Pane.moveItemToPane,[^pulsar-move-item] which always passes moved: true. Within one container that’s correct; across containers it leaves the source registry holding a stale entry and the destination registry missing one. Any later genuine re-add of that item instance into the stale container throws. A secondary effect: the throw can interrupt a pane event emission mid-flight, leaving the tabs package’s tab bar holding a stale, clickable tab for an item its pane no longer owns — which is how an innocent tab click surfaces the error.

Fix (tranquil-client src/pane.js, moveItemToPane): detect a cross-container move and transfer the registration explicitly — fromContainer.itemRegistry.removeItem(item) plus a hasItem-guarded toContainer.itemRegistry.addItem(item) (the guard lets an already-desynced session self-heal on its next move). No behavior change for same-container moves.

Producers covered by the one fix: the tabs package’s drag-to-dock (layout.js dragend), drop-on-tab-bar (tab-bar-view.js moveItemBetweenPanes), and any API caller of moveItemToPane (e.g. Tranquil’s Tabs-view dock toggle).

2. tabs package: removeTabForItem deletes the wrong WeakMap key

Status: observed, not fixed anywhere (third-party package; harmless in practice for us).

In tab-bar-view.js, tabs are indexed by element (this.tabsByElement.set(tabView.element, tabView)), but removeTabForItem calls this.tabsByElement.delete(tab) — the TabView object, not tab.element — so the deletion is a no-op and removed tabs linger in the map until GC.[^pulsar-tabs] Benign today because tab.destroy() also removes the element from the DOM, but it defeats the map’s purpose as a liveness check and could resurface a destroyed tab via tabForElement if an element is ever re-attached. One-line fix: this.tabsByElement.delete(tab.element).

[^pulsar-item-registry]: Upstream source: pulsar-edit/pulsar src/item-registry.js and src/pane-container.js (didAddPaneItem / didDestroyPaneItem).

[^pulsar-move-item]: Upstream source: pulsar-edit/pulsar src/pane.js (moveItemToPane, addItem, removeItem — the moved flag short-circuits both registry hooks).

[^pulsar-tabs]: Upstream source: pulsar-edit/tabs lib/tab-bar-view.js (addTabForItem / removeTabForItem).