Skip to content

feat: replace Fiddle with the 2027 rewrite - #2032

Open
felixrieseberg wants to merge 3 commits into
electron:mainfrom
felixrieseberg:claude/rewrite-onto-main-i2lmvk
Open

felixrieseberg wants to merge 3 commits into
electron:mainfrom
felixrieseberg:claude/rewrite-onto-main-i2lmvk

Conversation

@felixrieseberg

Copy link
Copy Markdown
Member

Before: main holds the current Electron Fiddle app.

After: main holds the rewrite of Fiddle, with the same contents as the fiddle-2027 branch. The current app stays available on the legacy branch.

This PR replaces every file on main, so a line-by-line review of the diff is not practical. The sections below are a tour of the new app: what changes for users, the architecture decisions, where the code is, and how the new app compares with the current one. No features are removed.

How this PR is built

fiddle-2027 and main share no history, so GitHub cannot open a PR between them. This branch sits on top of main instead:

  1. The first commit swaps the whole tree for the tree of fiddle-2027 at 0b27045. The two trees are identical.
  2. The second commit changes the two places that name the fiddle-2027 branch so that they name main: the Windows installer icon URL in packages/app/forge.config.ts and one line in CLAUDE.md.
  3. The third commit keeps the check-download-links workflow, which checks the download links on electronjs.org/fiddle and does not depend on the app code.

Before the merge:

  • Snapshot main as legacy:
    gh api repos/electron/fiddle/git/refs -f ref=refs/heads/legacy -f sha=$(gh api repos/electron/fiddle/git/ref/heads/main --jq .object.sha)
  • Check that branch protection on main does not require the old job names "Test" and "Build". The "GitHub Actions Completed" job still exists.
  • Do not merge other PRs into main until this one lands. A new commit on main makes this PR conflict.

Workflows: ci.yml, release.yml, semantic.yml and add-to-project.yml are replaced by the rewrite's versions. build.yml and test.yml are removed because the new ci.yml does their work itself. coveralls.yml is removed and coverage is no longer uploaded to Coveralls. refresh-data.yml is new: it refreshes the bundled Electron release list and contributor list each week.

What is new for users

  1. You do not lose your work. The app saves a draft in less than 5 seconds. A crash or a reload does not lose your code. A save that fails shows an error.
  2. Code from other people is safer to run. A gist from another person does not run until you approve that exact revision. The app removes tokens and keys from the environment of the fiddle. Install scripts of npm packages do not run until you approve them.
  3. 109 bugs of the current app are fixed. 7 of them lose work, leak secrets or crash.
  4. The app starts two times faster. On a Mac, the editor is ready in 0.51 s against 1.10 s. Memory after startup is 464 MB against 527 MB.
  5. The window has tabs and split panes. They replace the mosaic.
  6. The code editor is current. It is Monaco 0.56 from July 2026. The current app has Monaco 0.22 from February 2021.
  7. A command palette (Cmd/Ctrl+Shift+P or F1) holds all of Fiddle's actions and all of Monaco's actions.
  8. More people can use the app. The Lighthouse accessibility score is 100 against 86. Each control in the main window has a name for screen readers. The menus, the file list and the panel splitters work with the keyboard.
  9. The app has 11 languages. They match the languages of Electron's docs. The current app has English only.
  10. A fiddle can use ESM main files, and you can select the version of each npm package. These are 2 of the 12 open feature requests that the new app delivers.
  11. Two windows can run different Electron versions at the same time.
  12. Downloads use your system proxy and certificates.
  13. You can change the keyboard shortcuts.
  14. The settings are in a file, settings.json. You can open, import and export it. A damaged file does not stop the app.
  15. The first launch imports your settings, token, themes and local builds from the current app.

Architecture decisions

  • The main process owns all state. The App store has the data that all windows share, and the Window store has the data for one window. The main process sends a store to the window when it changes, and the renderer shows it. In the current app, each renderer holds its own copy of the state in a MobX class.
  • One schema defines all IPC. fiddle.eipc lists the functions that the renderer can call. EIPC generates typed code from it. That code checks every argument and accepts calls only from the app's own page. The current app has 87 channels with no types.
  • All network requests go through Chromium. Only the main process makes network requests. It uses Electron's net.fetch, so the system proxy and the system certificates apply. The content security policy of the page allows connections only to the app itself.
  • Every user action is a command. All commands are in one file, shared/commands.ts. The menu, the command palette and the keyboard shortcuts are built from it.
  • The fiddle logic does not import Electron. The code in fiddle/ runs in Node. Its tests use the real code and do not need mocks.
  • Each data file has a schema, a version and a backup. The app writes a file to a temporary name and then renames it, so a crash cannot damage the file. The current app keeps its settings in localStorage.
  • The app has its own component library. It is called Lucent and it uses React Aria for accessibility. It replaces Blueprint 3, which gets no updates. All colours and sizes are tokens.
  • All text is in a catalog. Components get their text from i18n/. A lint rule rejects text that is written in a component.
  • End-to-end tests run the real app. A small driver in the app finds controls through the accessibility tree. The tests do not use Playwright or WebDriver. The current app has no end-to-end tests.

Where the code is

Paths are in packages/app/src/ unless they start with packages/.

New code What it does Replaces
shared/ The types that both processes use: the two stores, the settings, the list of commands interfaces.ts, ipc-events.ts, constants.ts
ipc/fiddle.eipc The functions that the renderer can call ipc-events.ts, preload.ts
fiddle/ The rules for a fiddle: its files, its package.json, gists, templates, bisect renderer/utils/, src/utils/, templates.ts
main/index.ts, main/services.ts Start the app, create the services and connect them main/main.ts
main/state-hub.ts Holds the two stores and sends them to the windows renderer/state.ts
main/documents/ Opens, edits and saves the fiddle in each window. Saves drafts file-manager.ts, remote-loader.ts, main/files.ts
main/versions/ Gets the list of Electron releases. Downloads and removes versions renderer/versions.ts, main/versions.ts
main/run/ Starts a fiddle, sends its output to the console, stops it renderer/runner.ts, main/fiddle-core.ts
main/github/ Keeps the GitHub token. Publishes, updates and deletes gists commands-action-button.tsx, dialog-token.tsx
main/modules/ Installs the npm packages of a fiddle main/npm.ts
main/bisect/ Runs a bisect autobisect.ts, commands-bisect.tsx
main/packaging/ Builds a package or an installer from a fiddle The Forge tasks in renderer/runner.ts
main/settings/, main/themes/ Read and write settings.json. Load the editor themes localStorage in renderer/state.ts, main/themes.ts
main/migration/ Imports your data from the current app on the first launch New
main/menu.ts Builds the application menu from the list of commands main/menu.ts
main/security.ts Sets the security rules for every window: permissions, navigation, CSP New
main/platform/, main/updates/, main/crash/ The installer, updates and crash reports squirrel.ts, update.ts, sentry.ts, first-run.ts
main/test-driver/ Lets the end-to-end tests control the app. Release builds do not contain it New
renderer/state.ts Receives the two stores and gives them to the components renderer/state.ts, app.tsx
renderer/shell/ The window frame: title bar, tabs, panes, status bar header.tsx, commands.tsx, editors.tsx
renderer/editor/ The Monaco editor, with its themes, the Electron types and the formatter editor.tsx, editor-mosaic.ts
renderer/features/ One folder for each feature, for example run/, versions/, gists/, settings/ renderer/components/
ui/ Lucent, the component library Blueprint 3, src/less/
i18n/ All the text that the user sees, in 11 languages New
packages/core/ Downloads and installs Electron versions. It is a smaller version of @electron/fiddle-core and is meant to be temporary, until fiddle-core has what the app needs (for example abort signals) The npm package
packages/app/e2e/ The end-to-end tests. They run the real app on macOS, Windows and Linux New

How the new app compares

Current app at d4f03ba, rewrite at 0b27045. Mac numbers are from production builds on an M4 Max.

Measure Current Rewrite
Packages installed (full install) 975 505
Packages shipped at runtime 271 74
Returning launch to a rendered editor (Mac) 1.10 s 0.51 s
Memory after startup (Mac, physical footprint) 527 MB 464 MB
Synchronous IPC calls at startup ~3,400 0
Our JavaScript before first render (editor excluded) 2,259 KB 946 KB
Code editor (Monaco) release 0.22, Feb 2021 0.56, Jul 2026
Accessibility score (Lighthouse, main window) 86 100
Controls without an accessible name 15 of 27 0 of 30
End-to-end test cases 0 104
Unit test cases 877 1,966
Unit test line coverage 84% 93%
Languages 1 11
any in production code 59 0
Production build (Mac) 38 s 6.4 s
Open issues the rewrite closes (bugs + requests) 13 + 12
Bugs in the current app that the rewrite fixes (P0 / P1 / P2) 7 / 37 / 65
The 7 most serious bugs that the rewrite fixes
  • Save says "saved" when the write fails. Write errors are logged and swallowed. Now they show as errors.
  • A renderer crash, or "Reload window", loses unsaved work. Now main owns the document, drafts save within 5 s, and a crashed window reloads with your code intact.
  • Fiddles receive your secrets. A fiddle, even one from a stranger's gist, runs with your whole environment. Now about 20 secret patterns are stripped.
  • Fiddles inherit Fiddle's macOS privacy grants. Camera, microphone, location, contacts and calendar access carry over to user code without a prompt. Now fiddles get their own prompts.
  • Every run can wipe another app's data. Fiddle deletes <appData>/<name> after every run, and the name is the opened folder's. Now each run gets its own --user-data-dir inside the run folder.
  • Deleting a gist turns the unsaved-changes prompt off. Now an empty baseline means unsaved.
  • Crash reports upload memory dumps and cannot be turned off. Now there is a setting, reports are scrubbed, and native dumps are never sent.
Security
Current Rewrite
Renderer-to-main API 87 untyped channels, raw ipcRenderer Schema-checked calls; every argument and result validated
Who may call it Any frame in the window The main frame at exactly app://main
Permission requests (camera, mic, USB, HID) Allowed Denied by default
Content Security Policy Meta tag, 5 remote hosts HTTP header, no remote hosts, Trusted Types
Secrets passed to user code Whole environment, minus 4 keys About 20 secret patterns stripped, NODE_OPTIONS blocked
Install scripts of a fiddle's npm packages Run automatically Off unless you approve the listed packages
Running a remote gist One confirm at load Approval tied to the exact gist revision
External links Opened without asking Confirmation that shows the URL
HTML injection sinks 4 0

Swaps the whole tree for the contents of the fiddle-2027 branch at
0b27045. The previous app lives on
the legacy branch.
@felixrieseberg
felixrieseberg requested review from a team and codebytere as code owners October 17, 2026 21:20
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updated@​electron-forge/​shared-types@​8.0.0-alpha.9 ⏵ 8.0.0-alpha.101001006897 +4100
Updated@​electron-forge/​cli@​8.0.0-alpha.9 ⏵ 8.0.0-alpha.10100 +11006997100
Updated@​electron-forge/​maker-zip@​8.0.0-alpha.9 ⏵ 8.0.0-alpha.10100 +11007097100
Updated@​electron-forge/​maker-squirrel@​8.0.0-alpha.9 ⏵ 8.0.0-alpha.1010010072 +197100
Updated@​electron-forge/​maker-msix@​8.0.0-alpha.9 ⏵ 8.0.0-alpha.1073 +11007596100
Updated@​electron-forge/​maker-rpm@​8.0.0-alpha.9 ⏵ 8.0.0-alpha.101001007497 +4100
Updated@​types/​semver@​7.5.0 ⏵ 7.8.01001007481 +1100
Updated@​electron-forge/​maker-deb@​8.0.0-alpha.9 ⏵ 8.0.0-alpha.101001007493 -4100
Addedtypescript-eslint@​8.69.01001007498100
Updated@​types/​react-dom@​16.9.12 ⏵ 19.2.7100 +110075 +288 +4100
Updated@​electron-forge/​publisher-github@​8.0.0-alpha.9 ⏵ 8.0.0-alpha.1095 +11007697100
Updated@​electron-forge/​plugin-fuses@​8.0.0-alpha.9 ⏵ 8.0.0-alpha.10100 +11007797 +3100
Added@​marshallofsound/​ipc@​2.7.17810010093100
Updatedsfw@​2.0.4 ⏵ 2.0.6100 +2310078 +284100
Updated@​types/​react@​16.14.6 ⏵ 19.2.18100 +110079 +290 -5100
Updated@​vitest/​coverage-v8@​5.0.1 ⏵ 5.0.0991007999 +2100
Updatedvitest@​5.0.1 ⏵ 5.0.0981007998100
Addedreact-aria-components@​1.21.1981008097100
Updatedjiti@​2.5.1 ⏵ 2.7.09710010081100
Updated@​types/​debug@​4.1.12 ⏵ 4.1.131001008781100
Updated@​types/​node@​22.19.1 ⏵ 22.20.1100 +110081 +196 +1100
Updated@​types/​node@​22.19.1 ⏵ 24.13.3100 +110081 +196 +1100
Updated@​reforged/​maker-appimage@​5.1.0 ⏵ 5.3.194 +1010081 +188 +1100
Updated@​electron/​get@​5.0.0 ⏵ 5.1.099 +1100100 +182 -2100
Added@​electron-forge/​plugin-vite@​8.0.0-alpha.101001008297100
Updatedreact@​19.1.0 ⏵ 19.2.8100 +110084 +197100
Added@​eslint/​js@​10.0.110010010086100
Updatedsemver@​5.7.2 ⏵ 7.8.5100100100 +188100
Addedeslint-plugin-i18next@​6.1.51001008988100
Updatedupdate-electron-app@​3.2.0 ⏵ 3.3.098 +1100100 +188 +6100
Updated@​testing-library/​dom@​8.13.0 ⏵ 10.4.19910010089 +2100
Updated@​testing-library/​react@​12.1.5 ⏵ 16.3.310010010090 +3100
See 14 more rows in the dashboard

View full report

@socket-security

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Priority Alert  (click "▶" to expand/collapse) Action
Low priority
Low adoption: npm @marshallofsound/ipc

Location: Package overview

From: packages/app/package.json → npm/@marshallofsound/ipc@2.7.1

ℹ Read more on: This package | This alert | What are unpopular packages?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Unpopular packages may have less maintenance and contain other problems.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@marshallofsound/ipc@2.7.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn

View full report

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants