Skip to content

Drop Database in the playground's Storage Usage dialog is undone by the next edit and asks for no confirmation #1747

Open

Description

@joshunrau

Drop Database in the playground's Storage Usage dialog is undone by the next store update, because it never clears the in-memory store. The button calls useAppStore.persist.clearStorage(), which deletes the IndexedDB key but leaves every saved instrument (and the stored API token) in the running store. The persist middleware writes the whole state back on every set, so the next keystroke, rebuild or Monaco marker update rewrites everything that was just "dropped". Meanwhile the selector keeps listing the saved instruments, and the dialog shows fresh usage figures after a fixed 2-second wait as if the drop had worked. Whether the data is really gone depends on whether the user happens to reload before touching anything. The button is also a one-click, irreversible action inside a dialog the user opened only to read storage figures, with no confirmation step, unlike Delete Instrument and Restore Defaults.

Where

apps/playground/src/components/Header/ActionsDropdown/StorageUsageDialog.tsx:62-73:

<Button
  variant="danger"
  onClick={() => {
    useAppStore.persist.clearStorage();
    setMessage(t({ en: 'Deleting...', fr: 'Suppression...' }));
    void new Promise((resolve) => setTimeout(resolve, 2000)).then(() => {
      setUpdateKey(updateKey + 1);
    });
  }}
>
  {t({ en: 'Drop Database (Irreversible)', fr: 'Supprimer la base de données (Irréversible)' })}
</Button>

Reproduce

  1. Click Create New Instrument and save an instrument named Mine.
  2. Open the actions menu, choose Storage Usage, and click Drop Database (Irreversible).
  3. Close the dialog, type one character in the editor, and reload.

Actual: Mine is still listed under Saved, both before and after the reload.
Expected: after the user confirms, Mine disappears from the selector at once and stays gone after a reload.

A node test of the store with a mocked idb-keyval confirms the mechanism: after clearStorage() the app key is gone, and the next set writes it back with every instrument.

Tests

Put the reset in a store action (for example dropDatabase) and test it in apps/playground/src/store/__tests__/index.test.ts: it('should clear saved instruments from memory when the database is dropped, so the next update cannot write them back').

E2E in testing/src/specs/playground.spec.ts: save an instrument, drop the database through the confirmation, type in the editor, reload, and assert that the instrument is not listed.

Suggested fix

Ask for confirmation first. Then reset the in-memory state (resetInstruments, resetSettings, and clear auth) before calling clearStorage(), or call clearStorage() and reload the page so nothing in memory can repopulate storage.

Activity

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

    @joshunrau
    CollaboratorAuthor

    Still reproduces on main (d86740e). Nothing on main has touched StorageUsageDialog.tsx or apps/playground/src/store/ since this was filed, and no open PR fixes it.

    After the fix, Drop Database is Restore Defaults plus logout

    Restore Defaults already resets memory (apps/playground/src/components/Header/ActionsDropdown/RestoreDefaultsDialog.tsx:39-44), and persist then writes the defaults over app. Once Drop Database resets memory too, the only thing it adds is forgetting the API token. That still justifies the button: apps/playground/src/store/slices/auth.slice.ts has only login and revalidateToken, so nothing else removes a stored token before it expires. Keep it, and have the confirmation list what goes: saved instruments, settings (including the API URL) and the login.

    Corrections to the suggested fix

    • Reset in one set, so there is one write and no half-reset state. Add dropDatabase next to resetInstruments in instrument.slice.ts, with its type in store/types.ts: instruments = defaultInstruments, fix: resolve 26 low-difficulty issues, one commit each #1825's loadDefaultInstrument(state), default settings, auth = null. defaultSettings is module-private (apps/playground/src/store/slices/settings.slice.ts:7-9), so export it.
    • Then drop the clearStorage() call. The reset's own write leaves no Saved instrument and no _accessToken in app, and the next set rewrites the key anyway.
    • The reload alternative is racy. In zustand 5.0.15, clearStorage is typed () => void and does not return the removeItem promise (zustand/esm/middleware.mjs:450-453). There is nothing to await before location.reload(), and any set that lands before unload writes the key back.
    • Replace the 2 s sleep with the RestoreDefaultsDialog flow: confirm, run the action, addNotification({ type: 'success' }), close. updateKey and the Deleting... message then go. New strings use t({ en, fr }).
    • Until Every playground store update rewrites about 1.4 MB of built-in examples to IndexedDB #1756 trims partialize (apps/playground/src/store/index.ts:77-82), the reset still writes the whole example catalog, so the Usage figure barely drops. Do not assert on it.

    Tests

    Order

    This touches the slices, types.ts and StorageUsageDialog.tsx, not store/index.ts, and with no clearStorage() call it does not depend on how #1756 changes persistence. It needs only #1825, and can land before or after #1737 and #1756. #1825, #1817 and #1660 all edit testing/src/specs/playground.spec.ts.

    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