Skip to content

Playground something duplicates index.html when saving an instrument with index.html and reopening #932

Open

Description

@joshunrau

This rarely happens but it is not clear how or why. Needs investigation to figure out when this occurs.

Screenshot 2024-08-21 at 12 44 33 PM

Activity

  1. added
    BugType: existing behavior is wrong
    Difficulty: LowIsolated change, about 2 hours or less
    Area: Playgroundapps/playground (in-browser instrument editor)
    on Sep 27, 2026
  2. joshunrau commented on Oct 7, 2026

    @joshunrau
    CollaboratorAuthor

    Likely root cause: Save merges the editor's files into the saved instrument with lodash merge. merge combines arrays by index and never shortens the target array. For example, delete a.ts from a saved instrument whose files are [index.ts, a.ts, b.ts], then save. The editor's files [index.ts, b.ts] are merged onto the saved array, giving [index.ts, b.ts, b.ts], and that duplicated list is persisted. The same thing happens with index.html.

    apps/playground/src/store/slices/instrument.slice.ts:52-58:

    updateSelectedInstrument: (update) => {
      set((state) => {
        const selectedIndex = state.instruments.findIndex((instrument) => instrument.id === state.selectedInstrument.id);
        const updatedInstrument = merge(state.selectedInstrument, update);
        state.selectedInstrument = updatedInstrument;
        state.instruments[selectedIndex] = updatedInstrument;
      });
    }

    Suggested fix: replace files outright rather than deep-merging it, e.g. { ...state.selectedInstrument, ...update }.

  3. joshunrau commented on Oct 26, 2026

    @joshunrau
    CollaboratorAuthor

    Still reproduces on main (d86740e). updateSelectedInstrument has not changed since 2b61382 (2024-05-02). I ran the slice's merge (lodash-es 4.18.1) in a zustand immer store (immer 11.1.18), and the cause above holds. It does more than duplicate files:

    • Deleting the last entry of files (the most recently added file) duplicates nothing. The deletion is not saved, and the file is back the next time the instrument is opened. Deleting k files keeps the old last k entries, so a deleted file can come back next to a duplicate: removing a.ts and b.ts from [index.ts, a.ts, b.ts, c.ts] saves [index.ts, c.ts, b.ts, c.ts].
    • After such a save, Save stays enabled until the instrument is opened again. apps/playground/src/components/Header/SaveButton/SaveButton.tsx:18-22 compares with isEqual, and the lengths differ.
    • Once duplicates are saved, the editor cannot remove them. deleteFile filters by name (apps/playground/src/store/slices/editor.slice.ts:45), so deleting one copy deletes both, and the next save still cannot shorten the array.
    • After a reload, the playground shows the default template's files under the selected saved instrument, and Save overwrites it #1737 is a second trigger. After a reload the editor holds the template's single index.ts, and Save writes it over index 0 only. [a.ts, index.ts] saves as [index.ts (template), index.ts (yours)].

    Fix

    { ...state.selectedInstrument, ...update } works under immer. selectedInstrument and the instruments entry become the same frozen object, holding exactly the editor's files. Drop the merge import from instrument.slice.ts. The other two playground merge calls (editor.slice.ts:92, settings.slice.ts:20) merge flat objects with no arrays and can stay. Base the change on #1825, which edits this file and adds the store test harness in instrument.slice.test.ts.

    Land #1737 first or in the same PR. Today a Save after a reload overwrites index 0 of the saved files. With a full replace, the same click replaces all of them with the template's one file. Land this before #1767: its hasUnsavedChanges compares the same two arrays as Save, so it would report unsaved changes right after a save that deleted a file.

    The fix does not repair instruments already saved with duplicates. If #1756's migrate is used to clean them up, do not just keep the first copy of each name. After a delete and save, the later copies are the stale tail, but after #1737 the first index.ts is the template and the second is the user's code. A deleted file that came back cannot be detected at all.

    Tests

    Triage note written with Claude Code. Code references point at main @ d86740e.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area: Playgroundapps/playground (in-browser instrument editor)BugType: existing behavior is wrongDifficulty: LowIsolated change, about 2 hours or lessPriority: MediumReal value; schedule normally

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions