Skip to content

id(web): Find panel and Go to Line dialog never appear after the editor is recreated — module singletons outlive the editor, destroy helpers have no production caller #718

Closed

Description

@dezren39

Problem

Find (Ctrl+F / Ctrl+H) and Go to Line (Ctrl+G) silently stop working after the editor is recreated — e.g. after disconnecting from one world/session and opening another file. The shortcuts return true but no panel/dialog ever appears in the new editor.

Root cause (same pattern, both modules)

Both modules keep their DOM in a module-level singleton that is created once for the first editor and never invalidated:

  • pkgs/id/web/src/search-panel.ts: panelState (lines 47–54) is module-global. openPanel only creates the panel if (!panelState.panelEl), and panelState.view / the event handlers captured by setupPanelEvents are bound to the first view forever.
  • pkgs/id/web/src/goto-line.ts: dialogEl / dialogInput (lines 14–16) are module-global. openGotoLine only creates the dialog if (!dialogEl). (It does refresh activeView, but the dialog element itself stays attached to the old container.)

Both modules export a destroy function (destroySearchPanel, destroyGotoLineDialog) documented as "Call this when the editor is being destroyed" — but nothing in production calls either one. The only callers are the test files' afterEach, which is why the existing suites never see the leak. Production teardown (pkgs/id/web/src/main.ts, disconnect path, ~line 1287) only calls this.collab.editor.view.destroy().

When the old container is removed from the document, the singleton element is detached with it. The next Ctrl+F / Ctrl+G "opens" the panel by setting style.display = "" on the detached node and focusing a detached input — invisible, and for search the handlers still dispatch to the destroyed first view.

Reproduction (verified this run, on main a8992eb)

A vitest case mirroring the production lifecycle — create editor 1, open the panel, view.destroy() + remove container (without calling the destroy helper, exactly as production does), create editor 2, trigger the shortcut — fails for both modules:

  • search: second container has no .search-panel after Mod-f (expected null not to be null)
  • goto-line: second container has no .goto-line-dialog after Mod-g (same)

Both pass if destroySearchPanel() / destroyGotoLineDialog() is called between editors, confirming the missing invalidation is the whole defect.

Acceptance criteria

  • After an editor is destroyed and a new one created (no manual destroy-helper call in between), Ctrl+F/Ctrl+H shows a working find/replace panel in the new editor's container, bound to the new view (typing searches the new document; Enter/replace act on it).
  • Same for Ctrl+G: the Go to Line dialog appears in the new container and jumps in the new document.
  • Regression tests for both modules that recreate the editor without calling the destroy helpers (mirroring production teardown) and assert the panel/dialog appears and works in the second editor.
  • Existing search-panel and goto-line suites still pass; typecheck clean.

Suggested fix shapes (either is fine): make openPanel/openGotoLine self-healing — if the singleton element is detached or belongs to a different container/view, destroy and recreate it bound to the current view — and/or call the destroy helpers from the editor teardown path in main.ts. The self-healing form is more robust: it also covers any future recreation path that forgets the teardown call.

Single-repo: no search-panel/goto-line copies exist in developing-today/opencode, developing-today/openchamber, or dezren39/nix (grep-verified this run).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions