Compare commits

...
1037 Commits
Author SHA1 Message Date
Felix Rieseberg 26e054702a fix: Incorrect dropdowns 2019-12-12 14:23:11 -05:00
Felix Rieseberg 16f27132cb Revert "First basis for having Node in there, too"
This reverts commit 7613176f69.
2019-12-12 14:06:34 -05:00
Felix RiesebergandTierney Cyren 7613176f69 First basis for having Node in there, too
Co-authored-by: Tierney Cyren <ticyren@microsoft.com>
2019-12-11 12:19:16 -05:00
dependabot[bot] 470bea4fca build(deps): bump serialize-to-js from 3.0.0 to 3.0.1 (#280)
Bumps [serialize-to-js](https://github.com/commenthol/serialize-to-js) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/commenthol/serialize-to-js/releases)
- [Commits](https://github.com/commenthol/serialize-to-js/compare/v3.0.0...v3.0.1)

Signed-off-by: dependabot[bot] <support@github.com>
2019-12-10 00:30:59 -08:00
Felix Rieseberg 363febdb73 Merge pull request #279 from electron/notarize
build: Notarize the build with Apple
2019-12-04 13:43:38 -08:00
Felix Rieseberg 062ca4e205 build: Update to electron-forge b46 2019-12-04 10:44:13 -08:00
Felix Rieseberg 49053342a5 Merge pull request #278 from electron/electron-713
chore: Update Electron to 7.1.3
2019-12-04 10:09:10 -08:00
Felix Rieseberg fd582c67a8 build: Use a later macOS image 2019-12-03 18:08:51 -08:00
Felix Rieseberg 706b18808b build: Notarize the build with Apple 2019-12-03 17:52:52 -08:00
Felix Rieseberg a6ae8d5376 chore: Update Electron to 7.1.3 2019-12-03 17:45:02 -08:00
Erick Zhao 5034f8c853 feat: add bisect helper (#257) 2019-11-22 18:46:58 -05:00
Letra e916247013 fix: ensure dev tools only open in dev mode (#277) 2019-11-15 09:22:14 -05:00
Felix Rieseberg c052686de4 0.11.1 2019-11-12 14:39:38 +01:00
Erick Zhao 31aacef131 fix: update folder selection dialog for Electron 7 (#271) 2019-11-05 16:51:04 -05:00
Erick Zhao 932369b705 fix: update deprecated app.setName API (#273) 2019-11-05 16:24:43 -05:00
Shelley Vohr b835c34786 fix: update dialog example for promisification (#272) 2019-11-03 11:36:00 -05:00
Felix Rieseberg c9e1ba815a 0.11.0 2019-10-31 11:49:23 -04:00
Felix Rieseberg 374a85e2f6 Merge pull request #267 from electron/updates-updates
feat: Electron 7 (and more updates)
2019-10-27 11:26:14 -07:00
Felix Rieseberg 4282d0b93d fix: Better typing 2019-10-27 11:16:55 -07:00
Felix Rieseberg 549862b3bb chore: npm audit fix 2019-10-27 11:13:02 -07:00
Felix Rieseberg 4691c1b494 chore: Use a lockfile with latest npm 2019-10-27 11:10:36 -07:00
Felix Rieseberg 8125abc657 build: Update AppVeyor to Node 12 2019-10-27 11:01:39 -07:00
Felix Rieseberg 8cba7739f5 fix: Prepare for Electron 7 2019-10-25 11:59:05 -07:00
Felix Rieseberg 0b39fe40b1 chore: Update all the packages 2019-10-25 11:37:34 -07:00
Felix Rieseberg cae078ffe6 Merge pull request #266 from electron/ignored-commented-out-modules
fix: don't install commented-out requires
2019-10-15 18:14:33 -07:00
Ankit Arun bfb931435f fix: add whitespace to Welcome Tour (#263) 2019-10-14 20:48:40 -04:00
Shelley Vohr c878f72f2e fix: don't install commented-out requires 2019-10-14 13:59:00 -07:00
Felix Rieseberg 32494c057a Merge pull request #254 from electron/preload
feat: Enable support for a `preload.js`
2019-10-05 14:52:58 -10:00
Jason Zeng e732685fff fix: link to docs for content tracing show-me (#256) 2019-09-25 10:16:44 -04:00
Felix Rieseberg d9d5e0cbc9 test: Fix the tests 2019-09-17 16:14:38 -07:00
Felix Rieseberg 0e1d7b541e feat: If a file is newly empty, remove it 2019-09-17 15:59:27 -07:00
Felix Rieseberg 8a602d94b7 feat: Enable support for a preload.js 2019-09-17 15:50:49 -07:00
Erick Zhao 155889dbcc fix: ensure state consistency when replacing Fiddle (#253) 2019-09-16 14:53:53 -04:00
Felix Rieseberg 56ac6662b6 Merge pull request #248 from electron/add-new-themes
feat: ability to add new editor themes to Fiddle
2019-09-10 13:40:08 -04:00
Felix Rieseberg adb196549b chore: Bump version to 0.10 2019-09-08 15:45:59 -07:00
Felix Rieseberg 999facf062 Merge pull request #250 from electron/fix-console-stuff
fix: Console button / display issues
2019-09-08 15:39:55 -07:00
Felix Rieseberg fa0115f2db fix: Correctly toggle console state 2019-09-08 15:29:15 -07:00
Carlos Valdez c0a4c0e48d remove unused import 2019-09-07 11:05:13 -05:00
Carlos Valdez 3a5d2205fe remove unused imports 2019-09-07 11:03:27 -05:00
Carlos Valdez 1083b8f859 add warning dialog 2019-09-07 11:03:11 -05:00
Carlos Valdez 252d4c1de6 remove old code (yikes) 2019-09-07 10:25:10 -05:00
Carlos Valdez 0c44d2c9e4 remove check 2019-09-05 23:54:11 -05:00
Carlos Valdez 5eebc06514 add if statement inside try catch 2019-09-05 18:27:04 -05:00
Carlos Valdez eac1423177 remove comment 2019-09-05 18:18:44 -05:00
Carlos Valdez ae88780281 remove comment 2019-09-05 18:07:56 -05:00
Carlos Valdez 4bf9d48d70 remove commet 2019-09-05 18:07:36 -05:00
Carlos Valdez df6d4b5a9a remove monaco from observable variable name 2019-09-05 17:41:49 -05:00
Carlos Valdez 68202e2b23 change file name and change import 2019-09-05 17:23:40 -05:00
Felix Rieseberg acd5b35ca1 fix: Console button / display issues 2019-08-31 07:07:20 -07:00
Felix Rieseberg fae1ca3138 Merge pull request #249 from electron/update-dependencies
chore: Update all dependencies
2019-08-31 00:25:17 +02:00
Felix Rieseberg bd220d2690 chore: Update more dependencies 2019-08-30 15:07:07 -07:00
Felix Rieseberg d31f1016b8 test: Update all the tests 2019-08-30 15:05:07 -07:00
Felix Rieseberg 3b52be0f79 chore: Update templates for Standard 2019-08-28 09:44:10 +02:00
Felix Rieseberg 0e11c56f7f chore: Update all dependencies 2019-08-28 09:43:52 +02:00
Carlos Valdez 8019981d1a missing ; 2019-08-19 13:29:09 -07:00
Carlos Valdez 403a58e216 update snapshot 2019-08-19 13:28:42 -07:00
Carlos Valdez 3341d0ccaa fix json parsing 2019-08-19 11:13:32 -07:00
Carlos Valdez d42e83100a need to work on successful input 2019-08-16 17:58:27 -07:00
Carlos Valdez 50e9883d82 add new popup using react! 😰 2019-08-16 17:37:22 -07:00
Carlos Valdez f5aedac6a3 change wording of import monaco themes and add better formatting 2019-08-15 11:09:13 -07:00
Carlos Valdez a3f4aa8ed3 remove old code 2019-08-15 10:02:30 -07:00
Carlos Valdez 9c839aaad8 remove sad code 2019-08-15 09:57:30 -07:00
Carlos Valdez 3a7ca8ccd5 Merge branch 'add-new-themes' of github.com:electron/fiddle into add-new-themes 2019-08-15 09:56:15 -07:00
Carlos Valdez fef42293bc account for user checking an incorrect file 2019-08-15 09:56:02 -07:00
Felix Rieseberg 1f9db42c80 Merge pull request #247 from electron/feat/local-version-intellisense
feat: intellisense for local builds
2019-08-14 14:51:18 -07:00
Carlos Valdez 8e5657ef2d the coolest recursive function thanks to Samuel 2019-08-14 13:34:43 -07:00
Carlos Valdez a94105dd27 the coolest recursive function thanks to Samuel 2019-08-14 13:34:29 -07:00
Carlos Valdez cf214dc0d0 remove bad method 2019-08-14 11:03:54 -07:00
Carlos Valdez 6dfdf00437 add in new function and button 2019-08-14 11:03:36 -07:00
Carlos Valdez b3743a4c73 Merge branch 'add-new-themes' of github.com:electron/fiddle into add-new-themes 2019-08-13 17:07:15 -07:00
Carlos Valdez 3c0d6c2293 fix my mess 2019-08-13 17:06:23 -07:00
CarlosandErick Zhao 89b589fd2e Update tests/renderer/components/__snapshots__/settings-general-appearance-spec.tsx.snap
Co-Authored-By: Erick Zhao <erick@hotmail.ca>
2019-08-13 16:53:48 -07:00
CarlosandErick Zhao 42c577988d Update src/renderer/components/settings-general-appearance.tsx
Co-Authored-By: Erick Zhao <erick@hotmail.ca>
2019-08-13 16:53:37 -07:00
CarlosandErick Zhao d0e47b6b4d Update src/renderer/components/settings-general-appearance.tsx
Co-Authored-By: Erick Zhao <erick@hotmail.ca>
2019-08-13 16:41:34 -07:00
CarlosandErick Zhao f44a127012 Update tests/renderer/components/__snapshots__/settings-general-appearance-spec.tsx.snap
Co-Authored-By: Erick Zhao <erick@hotmail.ca>
2019-08-13 16:41:22 -07:00
Carlos Valdez fa2f5454ce don't throw! 2019-08-13 11:24:12 -07:00
Carlos Valdez 1021bb8b2b remove console.warn 2019-08-13 10:34:40 -07:00
Carlos Valdez cf4ad36ca2 remove optional arg 2019-08-13 10:33:38 -07:00
Carlos Valdez 4fe9c627b5 if json has name, set that as the filename otherwise take the filename 2019-08-13 10:25:21 -07:00
Carlos Valdez 262e636e8d update snapshot 2019-08-12 17:53:26 -07:00
Carlos Valdez bbe79ed6e2 added description 2019-08-12 15:32:46 -07:00
Carlos Valdez bc96a5013a added new VS Code option 2019-08-12 15:32:37 -07:00
Erick Zhao 66ba7b74d5 test: add spec 2019-08-12 10:13:08 -07:00
Erick Zhao 38e5aa98e3 test: remove '.only' 2019-08-09 13:06:28 -07:00
Erick Zhao a91504b173 feat: local typedefs 2019-08-09 13:05:07 -07:00
Erick Zhao 4685e88c86 style: run linter 2019-08-09 11:27:08 -07:00
Erick Zhao e0bb82d919 fix: throw appropriately if cannot fetch types 2019-08-08 15:43:14 -07:00
Erick Zhao 7035be3286 fix: adjust styling to avoid hiding intellisense (#243) 2019-07-30 10:43:44 -07:00
Erick Zhao c961cfadb8 chore: merge in settings UX improvements 2019-07-30 10:43:10 -07:00
Erick Zhao 7267a2ee88 fix: load empty files upon opening a fiddle (#241)
* fix: load empty files upon opening a fiddle
* test: add spec for no value passed to setValue
2019-07-30 10:42:16 -07:00
Erick Zhao bc03382dc5 test: test for event listener removal upon unmount 2019-07-29 08:48:59 -07:00
Erick Zhao 891c33ba68 fix: refresh theme list upon creation 2019-07-28 20:02:35 -07:00
Erick Zhao e7260c74a4 feat: close settings panel on Esc 2019-07-28 20:02:35 -07:00
Erick Zhao ed694a7044 chore: describe correct component in spec 2019-07-28 20:02:35 -07:00
Erick Zhao 14fc77c08c chore: 'Exection' -> 'Execution' 2019-07-28 20:02:35 -07:00
Erick Zhao f3079648cf fix: clean up typedefs when removing binaries (#240) 2019-07-26 18:03:57 -07:00
Felix Rieseberg 7272ec7899 Merge pull request #239 from electron/fix/consistent-tab-size
fix: enforce 2-space indents in all editors
2019-07-26 10:25:46 -07:00
Erick Zhao ac9ac701f7 test: add spec for forced tab size 2019-07-25 13:16:56 -07:00
Erick Zhao 7fce572823 fix: enforce tab-size 2 2019-07-25 12:40:53 -07:00
Erick Zhao 6186a80dad chore: 'monoaco' -> 'monaco' (#238) 2019-07-25 11:53:20 -07:00
Mark Lee ade6576285 build: upgrade Electron Forge to 6.0.0-beta.42 (#234) 2019-07-17 23:22:06 -07:00
Felix Rieseberg 61c58277f3 Merge pull request #233 from electron/chore/toogle
chore: `toogle` -> `toggle`
2019-07-17 19:48:00 -07:00
Erick Zhao c361d34a6d chore: toogle -> toggle 2019-07-16 17:54:02 -07:00
Erick Zhao 5887a6615a Merge pull request #232 from electron/feat/protocol-force-electron-version
feat: force electron version when loading from example protocol
2019-07-16 10:34:05 -07:00
Erick Zhao 7ba4cf5be7 style: run linter 2019-07-15 16:47:36 -07:00
Erick Zhao 0daf872d2b chore: conflict resolution w/ confirm prompt 2019-07-15 16:34:05 -07:00
Erick Zhao 75ec8ee9b1 chore: merge in master 2019-07-15 16:24:00 -07:00
Erick Zhao 183c152e40 test: create spec for remote-loader 2019-07-15 16:18:04 -07:00
Erick Zhao d6a807ba41 refactor: move version checking to its own fn 2019-07-15 15:23:30 -07:00
Carlos 24e0abf667 Merge pull request #230 from electron/warning-prompt
feat: Warning prompt
2019-07-15 14:05:37 -07:00
Carlos Valdez eb8261e240 remove obsolete snapshot 2019-07-15 13:38:20 -07:00
Erick Zhao 84cc07da90 feat: load examples with specific electron version 2019-07-15 13:21:44 -07:00
Erick Zhao b0f3837b47 refactor: move remote loading to its own file 2019-07-15 13:20:56 -07:00
Carlos Valdez e29664ca45 fix added event spec 2019-07-15 13:18:55 -07:00
Carlos Valdez ef36ee39f6 - 2019-07-15 11:14:56 -07:00
Carlos Valdez 73dadb815e removed spec test 2019-07-15 11:14:45 -07:00
Carlos Valdez 4945501703 fix test spec 2019-07-15 11:02:08 -07:00
Erick Zhao 77c87fbdeb feat: console resizing (#226)
Adds a `react-mosaic` wrapper to the console component to enable resizing in a similar
fashion to the editor components.
2019-07-15 10:33:01 -07:00
Carlos Valdez 2ef5b42f4a solved conflicts on git rebase 2019-07-15 10:31:07 -07:00
Carlos Valdez 9c4bc2ba52 fix test-spec failing 2019-07-15 10:30:31 -07:00
Carlos Valdez 80da4e73e3 fixed specs 2019-07-15 10:30:31 -07:00
Carlos Valdez eadd58d149 fix test 2019-07-15 10:28:09 -07:00
Carlos Valdez 1fc82f8f6d update specs 2019-07-15 10:28:09 -07:00
CarlosandFelix Rieseberg 9bd7043b2c Update src/renderer/components/dialog-confirm.tsx
Co-Authored-By: Felix Rieseberg <felix@felixrieseberg.com>
2019-07-15 10:28:09 -07:00
Carlos aaeef6fd06 Update commands-address-bar.tsx 2019-07-15 10:28:09 -07:00
CarlosandFelix Rieseberg 1195902bd5 Update src/renderer/components/dialog-warning.tsx
Co-Authored-By: Felix Rieseberg <felix@felixrieseberg.com>
2019-07-15 10:28:09 -07:00
CarlosandFelix Rieseberg c022d0f33d Update src/renderer/components/dialog-warning.tsx
Co-Authored-By: Felix Rieseberg <felix@felixrieseberg.com>
2019-07-15 10:28:09 -07:00
CarlosandFelix Rieseberg 996ae775f3 Update src/renderer/components/commands-address-bar.tsx
Co-Authored-By: Felix Rieseberg <felix@felixrieseberg.com>
2019-07-15 10:28:09 -07:00
Carlos Valdez ed44dda8fb better label 2019-07-15 10:28:09 -07:00
Carlos Valdez effba13e73 add to dialog class 2019-07-15 10:28:09 -07:00
Carlos Valdez 65886c3b52 added dialog-confirm prompt for users 2019-07-15 10:28:09 -07:00
Carlos Valdez 8bd4dd366a fix test-spec failing 2019-07-15 10:16:46 -07:00
Erick Zhao 4e19578e34 feat: add support for unchecked release channels 2019-07-14 18:45:18 -07:00
Erick Zhao c7fd0bc95e feat: force electron version when load w/ protocol 2019-07-14 18:45:18 -07:00
Erick Zhao 29e2f03b1a feat: allow version utility fn to accept a string 2019-07-14 18:45:18 -07:00
Felix Rieseberg 4cd6f330da Merge pull request #227 from electron/deermichel/error-msg
chore: add offline notice when loading fiddles
2019-07-13 16:10:27 -07:00
Carlos Valdez 79521eac7c fixed specs 2019-07-12 16:03:28 -07:00
Carlos Valdez 2736499d68 fix test 2019-07-12 15:36:14 -07:00
Carlos Valdez 1cbb781e8d update specs 2019-07-12 15:25:02 -07:00
CarlosandFelix Rieseberg e849a49f6f Update src/renderer/components/dialog-confirm.tsx
Co-Authored-By: Felix Rieseberg <felix@felixrieseberg.com>
2019-07-12 14:48:40 -07:00
Carlos 0114d2c1eb Update commands-address-bar.tsx 2019-07-12 14:48:09 -07:00
CarlosandFelix Rieseberg 365537012f Update src/renderer/components/dialog-warning.tsx
Co-Authored-By: Felix Rieseberg <felix@felixrieseberg.com>
2019-07-12 14:45:00 -07:00
CarlosandFelix Rieseberg 3e435cb64d Update src/renderer/components/dialog-warning.tsx
Co-Authored-By: Felix Rieseberg <felix@felixrieseberg.com>
2019-07-12 14:44:55 -07:00
CarlosandFelix Rieseberg 6981382793 Update src/renderer/components/commands-address-bar.tsx
Co-Authored-By: Felix Rieseberg <felix@felixrieseberg.com>
2019-07-12 14:42:53 -07:00
Felix Rieseberg f293ba84d1 Merge pull request #229 from electron/feat/protocol-dev-mode-support
feat: add dev mode support for protocol
2019-07-12 14:41:05 -07:00
Carlos Valdez 55cf507a96 better label 2019-07-12 14:23:21 -07:00
Carlos Valdez e71c2439cd add to dialog class 2019-07-12 13:49:52 -07:00
Carlos Valdez 9bd253951c added dialog-confirm prompt for users 2019-07-12 13:49:40 -07:00
Erick Zhao eda08c9aea feat: change args passed in to support yarn
NPM and Yarn both forward arguments via `process.env.npm_config_argv`.
The URL would be in the `remain` property in NPM, but in `original`
in both NPM and Yarn.
2019-07-12 11:50:35 -07:00
Felix Rieseberg 9414f66893 Merge pull request #228 from electron/no-prerelease
build: Don't mark releases as prereleases
2019-07-12 11:44:40 -07:00
Erick Zhao f4725b5dd8 feat: add dev mode support for protocol 2019-07-12 11:35:18 -07:00
Felix Rieseberg fd8be47494 build: Don't mark releases as prereleases 2019-07-12 11:10:09 -07:00
Micha Hanselmann 2cebe2ca7c add more verbose msg when offline 2019-07-12 11:07:51 -07:00
Felix Rieseberg 74135bab67 0.9.0 2019-07-08 10:53:09 -07:00
Felix Rieseberg a856340d01 Merge pull request #225 from electron/update-dependencies
chore: Update the dependencies
2019-07-08 10:52:25 -07:00
Felix Rieseberg bd8939c358 chore: Update the dependencies 2019-07-07 17:15:43 -07:00
Felix Rieseberg 9d3f3d61eb Merge pull request #222 from erickzhao/fix/browserwindow-showme-script
fix: correct button code for BrowserWindow show-me
2019-07-06 12:46:07 -07:00
Felix Rieseberg bdeaffcb7d Merge pull request #223 from DeerMichel/deermichel/nightlies
fix: fetch nightlies from `electron-nightlies` channel
2019-07-06 12:45:40 -07:00
Andrew Cherniavskii 344e1df375 fix: console message in powermonitor example 2019-07-06 09:34:03 -07:00
Micha Hanselmann d5b52dcae7 fix nightlies fetch 2019-07-05 13:48:07 -07:00
Erick Zhao 0faeb24761 fix: correct button code for BrowserWindow show-me 2019-07-03 15:23:07 -07:00
Micha Hanselmann 284b579d0c fix out-of-scope tray ref (#221) 2019-07-03 11:14:39 -05:00
Felix Rieseberg 5a1c24cdda Merge pull request #220 from erickzhao/feat/publish-ux-fixes
feat: clarify UX for Publish Gist feature
2019-07-02 10:13:02 -07:00
Erick Zhao 024d48a273 test: add spec for publishing gist disabled form 2019-07-01 22:54:44 -07:00
Erick Zhao 32155d652d chore: update enzyme version 2019-07-01 22:54:04 -07:00
Erick Zhao 874a8bb0f0 test: Move isPublishing from local state to store 2019-07-01 21:15:18 -07:00
Erick Zhao 73470640c7 test: Update snapshots to match DOM changes 2019-07-01 21:15:18 -07:00
Erick Zhao 679f731f4c chore: Consistent use of "publish" keyword 2019-07-01 21:15:18 -07:00
Erick Zhao a41ad11f8c chore: Remove "public" from gist setting callout
Users can either set their published Gists as public or private, so it's
best to remove the "public" qualifier from the callout.
2019-07-01 21:15:18 -07:00
Erick Zhao 7cc6d38dc2 feat: Disable fields while publishing gists 2019-07-01 21:15:18 -07:00
Felix Rieseberg 1e6ee7ab37 Merge pull request #216 from drewswaycool/add-missing-spaces
adds missing spaces
2019-06-27 15:43:25 -07:00
Felix Rieseberg c50c31ad80 Merge pull request #219 from electron/bypass-contributor-check
build: Don't throw if missing contributors in PR
2019-06-27 15:35:20 -07:00
Felix Rieseberg 5f8557fb13 build: Don't throw if missing contributors in PR 2019-06-24 19:51:34 -07:00
Felix Rieseberg 47b20fa3e4 Merge pull request #218 from DeerMichel/deermichel/clear-console
feat: add ability to clear console output
2019-06-24 19:47:21 -07:00
Micha Hanselmann b5c8ed60a9 remove clear console top bar button 2019-06-17 21:16:23 -07:00
Micha Hanselmann 5e3d382b85 minor style fix 2019-06-17 09:41:57 -07:00
Micha Hanselmann fed0107dd1 i'm a contributer now... 2019-06-16 00:11:31 -07:00
Micha Hanselmann ca9b9c633a add clear-on-run test 2019-06-15 23:50:36 -07:00
Micha Hanselmann fb4c6b3f33 add ui tests for console settings 2019-06-15 23:38:29 -07:00
Micha Hanselmann aa51c0cc5c implement clear on run 2019-06-15 23:28:20 -07:00
Micha Hanselmann e5236c5500 add settings ui for clear-on-run option 2019-06-15 23:22:49 -07:00
Micha Hanselmann 321f7a8bbc fix top bar buttons test 2019-06-15 23:02:28 -07:00
Micha Hanselmann 1ff7a3fe16 fix context menu test 2019-06-15 23:00:04 -07:00
Micha Hanselmann a1c74c018d add test for clearConsole 2019-06-15 22:53:48 -07:00
Micha Hanselmann 5df39ea51a add context menu entry to clear console 2019-06-15 22:41:44 -07:00
Micha Hanselmann 8a9591be9f add clear console button to top bar 2019-06-15 22:37:25 -07:00
Micha Hanselmann 3cc8fd41f6 add clearConsole method 2019-06-15 22:29:32 -07:00
Drew McMurry 25df3dd989 adds missing spaces 2019-06-14 15:38:39 -06:00
Felix Rieseberg abe7bdab23 Merge pull request #213 from electron/fix-touch-bar-example
fix: correct touch bar constructor in example
2019-06-11 21:16:44 -04:00
Shelley Vohr 16bea01ba5 fix: correct touch bar constructor in example 2019-06-11 14:17:44 -07:00
Felix Rieseberg 7417a82931 0.8.1 2019-06-11 11:35:40 -04:00
Felix Rieseberg 4c41db8595 Merge pull request #212 from electron/no-download-test
test: Mock electron-download
2019-06-11 17:35:23 +02:00
Felix Rieseberg 5da87ffca3 Merge pull request #211 from electron/fix-menu
fix: Don't attempt to set about panel on Windows
2019-06-11 17:05:07 +02:00
Felix Rieseberg 919e9cb6b6 test: Mock electron-download 2019-06-11 11:02:09 -04:00
Felix Rieseberg 14279283d6 fix: Don't attempt to set about panel on Windows 2019-06-11 10:51:42 -04:00
Felix Rieseberg 77a98112a1 Merge pull request #209 from amorist/fix-submit-token
Fix: submit token
2019-06-11 16:43:05 +02:00
amorist 08fb3f52bf fixed submit token 2019-06-11 14:27:47 +08:00
Felix Rieseberg b37ba895e5 0.8.0 2019-06-10 12:09:48 -04:00
Felix Rieseberg 33a236edd1 Merge pull request #203 from electron/load-electron-examples
feat: add support for launch examples from the Electron docs
2019-06-07 21:30:27 +02:00
Felix Rieseberg f7930fb523 test: Fix more tests 2019-06-07 12:15:49 -07:00
Felix Rieseberg 88cad66076 test: Add some tests 2019-06-07 11:01:36 -07:00
Felix Rieseberg f0afedf896 fix: Linter issues 2019-06-07 09:39:36 -07:00
Samuel Attard 38234dd0a3 chore: fix specs 2019-06-06 14:59:53 -07:00
Felix Rieseberg 83e834bea5 Merge pull request #205 from electron/touchbar
feat: Touchbar
2019-06-06 18:23:18 +02:00
Felix Rieseberg 38be3cca14 test: Add touchBarManager creation 2019-06-03 15:59:00 -07:00
Felix Rieseberg 4908cc6110 feat: Sort versions in touch bar 2019-06-03 13:11:11 -07:00
Felix Rieseberg ab4a9015d1 feat: Add a touchbar feature 2019-06-02 21:12:34 -07:00
Felix Rieseberg 73ab813efc Merge pull request #204 from electron/add-about-panel
feat: add more information to about panel
2019-06-02 09:30:00 +02:00
Shelley Vohr f3570c5069 spec: add a spec for about panel functionality 2019-06-01 09:54:25 -07:00
Shelley Vohr 4c564eaa63 feat: add more information to about panel 2019-05-31 22:32:05 -07:00
Samuel Attard a289842e32 feat: add support for launch examples from the Electron docs 2019-05-27 11:03:18 -07:00
Felix Rieseberg 9d697e7a3f Merge pull request #202 from electron/gist-launch
feat: add wiring for protocol launching gists
2019-05-27 00:16:51 -07:00
Felix Rieseberg e96e5345e6 Merge pull request #201 from electron/fix-audit
chore: fix npm audit output
2019-05-27 00:16:00 -07:00
Samuel Attard 7c97bc2ef6 feat: add wiring for protocol launching gists 2019-05-25 15:29:04 -07:00
Samuel Attard d55c5c067d chore: fix npm audit output 2019-05-25 14:08:14 -07:00
Felix Rieseberg 083ac3fbc2 chore: Update monaco-loader 2019-05-17 17:23:47 +09:00
Felix Rieseberg 5c8fe3d6e0 build: Fix x64 releases 2019-05-17 14:00:54 +09:00
Felix Rieseberg f034c5240c chore: Update version number 2019-05-17 13:29:40 +09:00
Felix Rieseberg 6421187db4 Merge pull request #197 from electron/editors-to-panels
feat: Panels
2019-05-17 13:00:26 +09:00
Felix Rieseberg 6fb385a95f fix: Import typo 2019-05-17 12:39:14 +09:00
Felix Rieseberg c47fea09ed fix: Don't whine plz 2019-05-17 12:31:10 +09:00
Felix Rieseberg ef17ccd9cb test: Update the tests 2019-05-17 12:31:10 +09:00
Felix Rieseberg 3f173aa4d4 chore: Better panel titles 2019-05-17 12:31:10 +09:00
Felix Rieseberg faf15081e0 fix: Fix linter issues 2019-05-17 12:31:10 +09:00
Felix Rieseberg 4306f53c70 feat: A first demo 2019-05-17 12:31:10 +09:00
Felix Rieseberg 52a8aac6c7 test: Add a test for the default show-me page 2019-05-17 12:31:10 +09:00
Felix Rieseberg 7660192dcb test: Update the tests 2019-05-17 12:31:10 +09:00
Felix Rieseberg 53ceeb5600 feat: New overview button for docs & demos 2019-05-17 12:31:10 +09:00
Felix Rieseberg 5cd7a57f08 feat: Basic implementation of demo pages 2019-05-17 12:31:10 +09:00
Felix Rieseberg e96a3da09b feat: A generic "this is a subset only" component 2019-05-17 12:31:10 +09:00
Felix Rieseberg 90c198d487 feat: A generic "more information" component 2019-05-17 12:31:10 +09:00
Felix Rieseberg 5b56333516 feat: A utility to get docs urls 2019-05-17 12:31:10 +09:00
Felix Rieseberg c87f65ca76 feat: A generic show me component 2019-05-17 12:31:10 +09:00
Felix Rieseberg 0f27c8aa7a fix: Typo in type-checks 2019-05-17 12:31:10 +09:00
Felix Rieseberg b5880aef46 feat: Give docs their own little button 2019-05-17 12:31:10 +09:00
Felix Rieseberg fb46eaf0e4 feat: Improve panel styles 2019-05-17 12:31:10 +09:00
Felix Rieseberg c1040097e3 fix: A small issue with panel visibility 2019-05-17 12:31:10 +09:00
Felix Rieseberg 333758b73d feat: Basic awareness of opened template 2019-05-17 12:31:10 +09:00
Felix Rieseberg 3c2a4e1b76 test: Improve the tests, cover more 2019-05-17 12:31:10 +09:00
Felix Rieseberg f3d42c107f test: Add test for type check 2019-05-17 12:31:10 +09:00
Felix Rieseberg 2ece4dc728 feat: Editors to Panels, for real 2019-05-17 12:31:10 +09:00
Felix Rieseberg dd2975bd0e feat: Add the ability to render generic panels 2019-05-17 12:31:10 +09:00
Felix Rieseberg fdf03dff9b Merge pull request #198 from electron/updates
feat: Update all dependencies (Electron 5)
2019-05-17 12:30:44 +09:00
Felix Rieseberg 48fa762f7f build: Fine, Node v10 it is >:( 2019-05-17 12:16:26 +09:00
Felix Rieseberg 7cd9a86920 fix: Appease AppVeyor 2019-05-17 12:12:30 +09:00
Felix Rieseberg db2caaad59 feat: Update all dependencies (Electron 5) 2019-05-17 12:09:49 +09:00
Felix Rieseberg 3b2cb93d8b Merge pull request #195 from ajphukan/require-not-defined-bug
Fix: Require not defined bug
2019-05-16 10:58:49 +09:00
Ankur Jyoti Phukan 9822753ffd Update contributors.js 2019-05-09 12:54:48 +05:30
Ankur Jyoti Phukan 8712907182 Removed trailing space 2019-05-09 12:40:00 +05:30
Ankur Jyoti Phukan 40055ee069 Removed trailing space 2019-05-09 12:38:53 +05:30
Ankur Jyoti Phukan a33f175867 Removed trailing space 2019-05-09 12:38:11 +05:30
Ankur Jyoti Phukan 107ba63a5a Removed trailing spcae 2019-05-09 12:36:56 +05:30
Ankur Jyoti Phukan b66ad7e247 Removed trailing space 2019-05-09 12:36:20 +05:30
Ankur Jyoti Phukan 7eb15b6666 Removed Trailing space 2019-05-09 12:35:43 +05:30
Ankur Jyoti Phukan 2ed29f68dd Update main.js 2019-05-09 12:34:46 +05:30
Ankur Jyoti Phukan d89d595b57 Removed Trailing Space 2019-05-09 12:29:07 +05:30
Ankur Jyoti Phukan 2b34ef5e67 Update main.js 2019-05-09 12:23:08 +05:30
Ankur Jyoti Phukan d6f7146447 Update main.js 2019-05-09 12:22:32 +05:30
Ankur Jyoti Phukan 006ba5f0a5 Update main.js 2019-05-09 12:21:55 +05:30
Ankur Jyoti Phukan 5df390b0fd Update main.js 2019-05-09 12:21:13 +05:30
Ankur Jyoti Phukan 3eb25fed91 Update main.js 2019-05-09 12:20:21 +05:30
Ankur Jyoti Phukan 9b06f792ea Update main.js 2019-05-09 12:19:50 +05:30
Ankur Jyoti Phukan c116152605 Update main.js 2019-05-09 12:19:33 +05:30
Ankur Jyoti Phukan b9f8d55809 Update main.js 2019-05-09 12:19:13 +05:30
Ankur Jyoti Phukan 89e697fcd6 Update main.js 2019-05-09 12:18:53 +05:30
Ankur Jyoti Phukan 9df5314c08 Update main.js 2019-05-09 12:17:43 +05:30
Ankur Jyoti Phukan d01ba1cdff Added webPreference 2019-05-09 12:01:19 +05:30
Ankur Jyoti Phukan ed5ccf133f Added webPreference 2019-05-09 12:00:28 +05:30
Ankur Jyoti Phukan e1decd4819 Added webPreference 2019-05-09 11:59:49 +05:30
Ankur Jyoti Phukan 3c9f477b89 Added webPreference 2019-05-09 11:57:22 +05:30
Ankur Jyoti Phukan 5cfbf2cab5 Added webPreference 2019-05-09 11:56:36 +05:30
Ankur Jyoti Phukan 89272fbaa9 Added webPreference 2019-05-09 11:55:47 +05:30
Ankur Jyoti Phukan 5d572f451c Added webPreference 2019-05-09 11:54:52 +05:30
Ankur Jyoti Phukan 319136ecc8 Added WebPreference 2019-05-09 11:51:08 +05:30
Ankur Jyoti Phukan 3a4aa06331 Added WebPreference 2019-05-09 11:48:17 +05:30
Felix Rieseberg e283a118f6 Merge pull request #187 from malept/update-forge-transform
fix: use the latest version of Forge v6 for Fiddle Forge actions
2019-04-12 11:44:24 -07:00
Mark Lee e67d1b0179 fix: use the latest version of Forge v6 for Fiddle Forge actions 2019-04-10 23:28:42 -07:00
Felix Rieseberg 9805d98dd8 Merge pull request #184 from electron/test-100
test: Ensure full coverage
2019-04-02 16:11:20 -07:00
Felix Rieseberg 009ad25983 test: Test GitHub authentication in address bar 2019-04-02 15:52:23 -07:00
Felix Rieseberg 9a65124289 test: Test editors with visible editors 2019-04-02 15:39:44 -07:00
Felix Rieseberg 4d7c81ee69 Merge pull request #183 from electron/panel-management
feat: Panel management (close and open editors)
2019-04-02 15:27:06 -07:00
Felix Rieseberg ed91cb5ba1 test: Increase test coverage 2019-04-01 16:23:17 -07:00
Felix Rieseberg 8b04f67f71 fix: Linting issues 2019-04-01 16:15:34 -07:00
Felix Rieseberg 2e183f0538 fix: Handle setting values on closed editors 2019-04-01 16:13:51 -07:00
Felix Rieseberg 6b022bbea7 test: Improve the tests 2019-04-01 15:54:03 -07:00
Felix Rieseberg 6bdb7d71ec feat: Add a non-ideal state for the editors 2019-03-31 17:31:22 -07:00
Felix Rieseberg 0ebe6ab9b3 fix: Style when there is only one panel 2019-03-31 16:24:05 -07:00
Felix Rieseberg 5e73deeb4f test: Polish and lint tests 2019-03-31 14:04:24 -07:00
Felix Rieseberg 02d81aa407 test: Add spec for the new state methods 2019-03-31 13:59:20 -07:00
Felix Rieseberg 46deeb857e test: Test editors-mosaic-arrangement 2019-03-31 13:35:34 -07:00
Felix Rieseberg ca55d2e620 test: Add a spec for editors-toolbar-button 2019-03-31 13:07:36 -07:00
Felix Rieseberg 88974d277f test: Update the editor spec 2019-03-31 13:01:06 -07:00
Felix Rieseberg 1239afafeb test: Add spec for commands-editor 2019-03-31 12:45:36 -07:00
Felix Rieseberg 415877c067 test: Add spec for devtools 2019-03-31 12:24:54 -07:00
Felix Rieseberg 0a0779ffa1 test: Add tests for the new editor utilities 2019-03-31 12:17:46 -07:00
Felix Rieseberg 9cf34ea983 test: Fix the existing tests 2019-03-31 12:06:04 -07:00
Felix Rieseberg 83a7d23b38 test: Fix Wallaby & Jest 2019-03-31 12:00:09 -07:00
Felix Rieseberg 7d19ad91a6 feat: Add panel management 2019-03-31 10:39:56 -07:00
Felix Rieseberg db13699790 feat: Install dev tools 2019-03-31 10:36:55 -07:00
Felix Rieseberg 4a8fc24ff6 fix: Version number test 2019-03-30 17:02:39 -07:00
Felix Rieseberg 3ed41626b1 chore: Update version, electron versions 2019-03-28 09:29:44 -07:00
Felix Rieseberg b3e25cfe79 Merge pull request #182 from electron/electron-logging
feat: add an execution toggle to enable advanced electron logging
2019-03-27 15:15:49 -07:00
Samuel Attard 00bb15a891 feat: add an execution toggle to enable advanced electron logging 2019-03-27 11:02:12 -07:00
Felix Rieseberg 94da51128a Merge pull request #181 from electron/update-dependencies
feat: Update all dependencies
2019-03-25 14:51:43 -04:00
Felix Rieseberg b573f09494 fix: Lockfile was somehow out of sync 2019-03-25 10:19:51 -07:00
Felix Rieseberg f05e36de34 infra: Update all dependencies 2019-03-25 10:17:14 -07:00
Felix Rieseberg 44c7c70793 Merge pull request #178 from electron/bump-version-06
build: Update the version number
2019-02-22 11:54:49 +00:00
Felix Rieseberg bcbca7aceb build: Update the version number 2019-02-22 11:54:34 +00:00
Felix Rieseberg 9515458545 Merge pull request #177 from electron/electron-405
build: Update to Electron 4.0.5
2019-02-22 11:53:45 +00:00
Felix Rieseberg 219e90cad8 build: Update to Electron 4.0.5 2019-02-22 11:44:27 +00:00
Felix Rieseberg ba23c8019d Merge pull request #175 from electron/update-octokit
feat: Update octokit, handle fiddle load errors
2019-02-22 11:42:18 +00:00
Felix Rieseberg 2fef96a2b2 Merge branch 'master' into update-octokit 2019-02-22 11:31:15 +00:00
Felix Rieseberg 2d8cb9748d Merge pull request #176 from electron/update-dependencies-2
build: Update all the dependencies
2019-02-22 11:30:38 +00:00
Felix Rieseberg eaefbb7ad0 infra: Update all the dependencies 2019-02-21 11:32:11 +00:00
Felix Rieseberg d5f28253f8 feat: Handle loadFiddle() errors 2019-02-21 11:15:50 +00:00
Felix Rieseberg ac451d6b4a infra: Update Octokit methods 2019-02-21 11:01:03 +00:00
Felix Rieseberg b5e0e63766 infra: Update Octokit 2019-02-21 11:00:50 +00:00
Felix Rieseberg 49538ea55a Merge pull request #174 from electron/fix-show-me
fix: Lower-case names for show-me-templates
2019-02-21 10:26:16 +00:00
Felix Rieseberg 23ac9b8e02 fix: Lower-case names for show-me-templates 2019-02-20 16:27:47 +00:00
Felix Rieseberg 4bf2bee7c8 docs: Update the readme with new screenshot 2019-02-01 16:44:58 -08:00
Felix Rieseberg e742d8426c Merge pull request #172 from electron/fix-contributors-script
fix: Allow contributors.js to use GitHub credentials
2019-02-01 15:54:38 -08:00
Felix Rieseberg 7a223fd3dc fix: Allow contributors.js to use GitHub credentials 2019-02-01 15:14:41 -08:00
Felix Rieseberg 6bdc995eb6 Merge pull request #170 from electron/feat-158
feat: Display only Electron versions with a certain state
2019-01-30 22:07:35 -08:00
Felix Rieseberg 72cbcd46d1 test: Update one last test 2019-01-30 21:55:18 -08:00
Felix Rieseberg f1e51c71a3 Merge pull request #171 from electron/properly-quit
fix: On quit, quit properly
2019-01-29 19:01:28 -08:00
Felix Rieseberg 95e88c0eee test: Fix tests 2019-01-27 00:25:26 -08:00
Felix Rieseberg a54037ef0e test: Update snapshot 2019-01-26 19:15:26 -08:00
Felix Rieseberg 83bb6fc73e test: Add more tests, 2019-01-26 19:10:15 -08:00
Felix Rieseberg f8f1f0ec08 fix: On quit, quit properly 2019-01-26 18:59:40 -08:00
Felix Rieseberg f7a19b92a8 Merge pull request #169 from electron/more-enoent-logging
fix: Add some detailed templates logging
2019-01-26 18:39:55 -08:00
Felix Rieseberg dc76b78337 feat: Include it in the version chooser 2019-01-26 18:39:28 -08:00
Felix Rieseberg f6f3e94a97 feat: Add a setting for states to show 2019-01-26 18:36:34 -08:00
Felix Rieseberg 89cede25b1 Merge pull request #168 from electron/fix-167
fix: Correctly load Fiddle without form submit
2019-01-26 18:15:12 -08:00
Felix Rieseberg 8e8eec3cc6 fix: Add some detailed templates logging 2019-01-26 18:14:15 -08:00
Felix Rieseberg a0d8ca4019 fix: Correctly load Fiddle without form submit 2019-01-26 17:58:42 -08:00
Felix Rieseberg 1f0581afbc Merge pull request #166 from electron/bump-version-050
chore: Bump version to 0.5.0
2019-01-23 12:44:07 -08:00
Felix Rieseberg 70d4902b87 chore: Bump version to 0.5.0 2019-01-22 17:37:12 -08:00
Felix Rieseberg a93a99e3f0 Merge pull request #165 from electron/electron-4
feat: Update to Electron 4
2019-01-22 17:32:10 -08:00
Felix Rieseberg 4c209159e6 chore: jk, 4.0.2 2019-01-22 17:21:34 -08:00
Felix Rieseberg b09a248c96 feat: Update to Electron 4 2019-01-22 17:04:29 -08:00
Felix Rieseberg 6f09ed9c2a Merge pull request #163 from electron/forge-6-b32
chore: update Electron Forge to 6.0.0-beta.32
2019-01-21 12:51:56 -08:00
Felix Rieseberg c1ae0ecd86 Merge pull request #162 from electron/miniak/node-integration
feat: support upcoming Electron 5.0 with nodeIntegration disabled by default
2019-01-21 12:51:17 -08:00
Mark Lee 1e9a81fcf4 chore: update Electron Forge to 6.0.0-beta.32 2019-01-16 16:37:40 -08:00
Milan Burda 2061b26ed6 feat: support upcoming Electron 5.0 with nodeIntegration disabled by default 2019-01-16 20:10:52 +01:00
Felix Rieseberg 88ce4a76e9 Merge pull request #160 from bpasero/bpasero-run-f5
feat: Allow to run a fiddle via F5
2019-01-11 14:40:49 -08:00
Benjamin Pasero 9b4ef4c0d2 Allow to run a fiddle via F5
Enables to run a fiddle just with the keyboard.
2019-01-11 11:50:13 +01:00
Felix Rieseberg dca2cd67e5 Merge pull request #156 from electron/mac-better-frame
feat: support double click action on custom mac window title
2019-01-10 14:18:56 -08:00
Samuel Attard 6bab353088 fix: properly animate the opening / closing of the terminal 2019-01-09 13:44:10 +13:00
Samuel Attard e6cc44ca37 feat: support double click action on the window title bar on macOS 2019-01-09 13:43:55 +13:00
Felix Rieseberg d039c5dbf5 Merge pull request #155 from electron/bump-version-042
chore: Bump version to 0.4.2
2019-01-08 12:31:30 -08:00
Felix Rieseberg c155fff8b0 chore: Bump version to 0.4.2 2019-01-08 11:40:45 -08:00
Felix Rieseberg 11202a8d6b Merge pull request #152 from electron/100-coverage
test: Full unit test coverage
2019-01-08 11:38:52 -08:00
Felix Rieseberg 475127f5b1 fix: Travis coverage report 2019-01-08 11:17:13 -08:00
Felix Rieseberg feee5be2f8 test: Improve coverage, fix report 2019-01-08 11:11:56 -08:00
Felix Rieseberg 678c7f1578 chore: Fix coverage report 2019-01-08 10:54:59 -08:00
Felix Rieseberg d2fcb8071b test: Correctly report error 2019-01-07 23:18:57 -08:00
Felix Rieseberg 0ab35dc867 test: Update snapshot 2019-01-07 23:16:07 -08:00
Felix Rieseberg 377d221191 chore: Final cleanup 2019-01-07 23:09:42 -08:00
Felix Rieseberg cbdab9d662 test: Cover all of exec 2019-01-07 23:09:42 -08:00
Felix Rieseberg 6875ab392e test: Cover all of versions 2019-01-07 23:09:42 -08:00
Felix Rieseberg 58ec0f015c test: Cover all of file-manager 2019-01-07 23:09:42 -08:00
Felix Rieseberg 2aef0e776e test: Cover all of app 2019-01-07 23:09:42 -08:00
Felix Rieseberg b2a7d6b2a4 test: Cover all of tour 2019-01-07 23:09:42 -08:00
Felix Rieseberg 303d6bc79a test: Cover all of tour-welcome 2019-01-07 23:09:42 -08:00
Felix Rieseberg d028e7c915 test: Cover all of settings-general-appearance 2019-01-07 23:09:42 -08:00
Felix Rieseberg f04631b6c1 test: Linting, cleanup 2019-01-07 23:09:42 -08:00
Felix Rieseberg c1bb524b59 test: Cover all of settings-electron 2019-01-07 23:09:42 -08:00
Felix Rieseberg 4a38fda293 test: Cover all of editors 2019-01-07 23:09:42 -08:00
Felix Rieseberg b98ad2b572 test: Cover all of dialog-token 2019-01-07 23:09:42 -08:00
Felix Rieseberg 6118b94b09 test: Cover all of add-version 2019-01-07 23:09:42 -08:00
Felix Rieseberg d0c1ee1a82 test: Cover all of publish-button 2019-01-07 23:09:42 -08:00
Felix Rieseberg 525715a911 test: Cover all of address-bar 2019-01-07 23:09:42 -08:00
Felix Rieseberg afbc0c9e6c test: Cover squirrel 2019-01-07 23:09:42 -08:00
Felix Rieseberg c1a9cdf88b chore: Add linter 2019-01-07 23:09:42 -08:00
Felix Rieseberg d281e61b8a test: Cover shared-constants (lol) 2019-01-07 23:09:42 -08:00
Felix Rieseberg 2fbbc10497 test: Cover update 2019-01-07 23:09:42 -08:00
Felix Rieseberg e8226ca97d test: Cover all of protocol 2019-01-07 23:09:42 -08:00
Felix Rieseberg 08f2dbe937 test: Cover all of menu 2019-01-07 23:09:42 -08:00
Felix Rieseberg 68813edb51 test: Cover all of main 2019-01-07 23:09:42 -08:00
Felix Rieseberg a32261414b test: Cover all of ipcMainManager 2019-01-07 23:09:42 -08:00
Felix Rieseberg 42f67e7b21 test: Cover all of first-run 2019-01-07 23:09:41 -08:00
Felix Rieseberg 7d9b0ed79d fix: Use localecompare for invalid versions 2019-01-07 23:09:41 -08:00
Felix Rieseberg 47f1b5c4c3 test: Cover all of files 2019-01-07 23:09:41 -08:00
Felix Rieseberg 432322694d test: Correctly use "it" 2019-01-07 23:09:41 -08:00
Felix Rieseberg 889e76cbae test: Cover all of output 2019-01-07 23:09:41 -08:00
Felix Rieseberg 4a689e5c06 test: Cover all of settings-credtis 2019-01-07 23:09:41 -08:00
Felix Rieseberg 3113f324cc test: Cover all of npm 2019-01-07 23:09:41 -08:00
Felix Rieseberg 64addb1fb0 test: Cover all of state 2019-01-07 23:09:41 -08:00
Felix Rieseberg e6facf60c1 test: Cover all of runner & binary 2019-01-07 23:09:41 -08:00
Felix Rieseberg 00cb5cf6c6 Merge pull request #151 from electron/different-constants
fix: Don't require all of electron
2019-01-07 00:39:19 +08:00
Felix Rieseberg 3e8dbebe42 Merge pull request #150 from electron/update-dependencies
feat: Update Electron, Monaco, React, and others
2019-01-07 00:30:24 +08:00
Felix Rieseberg dcdef4aa4e fix: One more incorrect import 2019-01-07 00:25:25 +08:00
Felix Rieseberg 590389abdc fix: Update tests 2019-01-07 00:14:37 +08:00
Felix Rieseberg 18a8f0c142 fix: Deal with incorrect types 2019-01-07 00:10:44 +08:00
Felix Rieseberg a8e8ab6f04 fix: Don't require all of electron 2019-01-07 00:05:17 +08:00
Felix Rieseberg 45ed35467a feat: Update react-mosaic-component 2019-01-06 23:47:36 +08:00
Felix Rieseberg eb49cb296a feat: Update to Electron 3.0.14 2019-01-06 23:39:48 +08:00
Felix Rieseberg c2344d50d2 feat: Update dependencies 2019-01-06 23:33:32 +08:00
Felix Rieseberg 7786e113d9 Merge pull request #148 from electron/fix-defaultversion
fix: Ensure correct version selection & display
2019-01-06 22:58:05 +08:00
Felix Rieseberg f8b51870d8 Merge pull request #146 from electron/use-classnames
refactor: Use classnames module
2019-01-06 21:51:46 +08:00
Felix Rieseberg 601c28dcf8 fix: Handle EEXIST in unzipper 2019-01-06 21:47:25 +08:00
Felix Rieseberg 49379af856 feat: Display "checking status" in run button 2019-01-06 21:47:11 +08:00
Felix Rieseberg 8347f79756 fix: Correctly choose defaultVersion 2019-01-06 21:32:21 +08:00
Felix Rieseberg c102128d5a infra: Use classnames module 2019-01-06 21:01:42 +08:00
Felix Rieseberg fcff053136 Merge pull request #145 from electron/coverage-report
test: Add coverage reports
2019-01-06 20:27:06 +08:00
Felix Rieseberg c3641cfa04 test: Add coverage reports 2019-01-06 20:09:21 +08:00
Felix Rieseberg 6eba3cbdcc Merge pull request #143 from electron/more-tests-2
test: Write even more tests
2019-01-06 19:57:47 +08:00
Felix Rieseberg 6393c59d5c test: One more app spec 2019-01-04 23:56:53 +08:00
Felix Rieseberg 543e86bc91 chore: Fix linter issue 2019-01-04 20:38:34 +08:00
Felix Rieseberg ea5fdd426c test: Add an app setup() spec 2019-01-04 20:33:45 +08:00
Felix Rieseberg fecc499cc5 test: Add an exec test case 2019-01-04 20:13:07 +08:00
Felix Rieseberg 8d0a1524b9 test: Remove impossible case in focused-editor 2019-01-04 20:12:58 +08:00
Felix Rieseberg 918e91965a test: Add a version spec 2019-01-04 19:59:38 +08:00
Felix Rieseberg 15bab9ff86 chore: Fix linter issues 2019-01-04 19:49:37 +08:00
Felix Rieseberg 7d879b59cf test: Add runner specs 2019-01-04 19:42:33 +08:00
Felix Rieseberg 72f7e058d6 test: Add some state tests 2019-01-04 19:26:07 +08:00
Felix Rieseberg a65e044b5c test: Add some file manager tests 2019-01-04 17:13:41 +08:00
Felix Rieseberg a4c4590816 test: Remove obselete snapshot 2019-01-04 16:58:14 +08:00
Felix Rieseberg 6fbb816be3 test: Add MockState 2019-01-04 16:56:32 +08:00
Felix Rieseberg ae0ba026f2 test: More setting tests 2019-01-04 16:56:27 +08:00
Felix Rieseberg cc2a44d10a test: More app tests 2019-01-04 16:56:03 +08:00
Felix Rieseberg 3e6eb16974 Merge pull request #142 from electron/more-tests
test: More test, more minor refactoring
2019-01-02 22:47:52 +08:00
Felix Rieseberg 4212284fc4 test: Update Electron mock 2019-01-02 21:54:46 +08:00
Felix Rieseberg 8b35ff8f95 test: Add octokit spec 2019-01-02 21:54:39 +08:00
Felix Rieseberg 4d00ee9fd4 test: Add files spec 2019-01-02 21:54:27 +08:00
Felix Rieseberg 590db5304d test: Cover sorted map better 2019-01-02 21:54:18 +08:00
Felix Rieseberg e519ce2f13 test: Add highlight spec 2019-01-02 21:54:09 +08:00
Felix Rieseberg 8221920fab test: Add main spec 2019-01-02 21:53:48 +08:00
Felix Rieseberg 5de7ed7d9d test: Move out first-run, add tests 2019-01-02 21:53:36 +08:00
Felix Rieseberg a1586e864a Merge pull request #141 from electron/tests-and-security
test: Increase test coverage, make some code safer
2019-01-02 09:31:36 +08:00
Felix Rieseberg 60745ff8bb test: Add fancy-import test 2018-12-30 19:09:28 +01:00
Felix Rieseberg 4ac20aa5c2 test: Add highlight-text test 2018-12-30 19:09:20 +01:00
Felix Rieseberg 48faec9741 test: Add tests for firstRun, use outputFile 2018-12-30 18:51:08 +01:00
Felix Rieseberg c0f4e95a67 test: Add some version tests 2018-12-30 18:39:39 +01:00
Felix Rieseberg d5c8c8bd96 test: Add some editors tests 2018-12-30 18:18:39 +01:00
Felix Rieseberg 80ff396879 test: Add dialog-warning tests 2018-12-30 17:34:35 +01:00
Felix Rieseberg bf42abc88d test: Update dialog-token tests 2018-12-30 17:11:49 +01:00
Felix Rieseberg eaa064ea8d test: Update dialog-add-version tests 2018-12-30 17:01:57 +01:00
Felix Rieseberg e804436669 test: Update commands-runner tests 2018-12-30 16:58:30 +01:00
Felix Rieseberg 08d07b34a4 chore: Safer commands-runner 2018-12-30 16:53:42 +01:00
Felix Rieseberg 74ba7db13c Merge pull request #140 from electron/fix-cert-file
ci: Properly find Windows codesign certificate
2018-12-24 18:06:42 +01:00
Felix Rieseberg 0fced3aefd ci: Resolve path correctly 2018-12-24 17:56:41 +01:00
Felix Rieseberg 13c9217f9f ci: Fix variable test 2018-12-24 17:52:31 +01:00
Felix Rieseberg 346742b242 ci: Properly find Windows codesign certificate 2018-12-24 17:21:15 +01:00
Felix Rieseberg aefe273516 Merge pull request #139 from electron/bump-041
chore: Bump version number
2018-12-24 16:20:59 +01:00
Felix Rieseberg 337d19c33a chore: Bump version number 2018-12-24 16:15:58 +01:00
Felix Rieseberg ebc6f91dea Merge pull request #138 from electron/fix-tour
fix: The Tour
2018-12-24 16:09:50 +01:00
Felix Rieseberg 25446aa9e4 chore: Update snapshots 2018-12-24 15:27:29 +01:00
Felix Rieseberg f2859c053b chore: Goodbye, dialog component 2018-12-24 14:39:56 +01:00
Felix Rieseberg 49cd192817 feat: Modernize and improve tour 2018-12-24 14:26:09 +01:00
Felix Rieseberg 9bdd09c6b5 fix: Tour selectors 2018-12-24 13:48:13 +01:00
Felix Rieseberg c3d36b29b0 feat: Modernize the tour buttons 2018-12-24 13:44:15 +01:00
Felix Rieseberg 1b69a2ff52 Merge pull request #137 from electron/ci-publish-win
ci: Allow AppVeyor to codesign
2018-12-24 12:19:18 +00:00
Felix Rieseberg 311497d080 ci: AppVeyor: Use Node 10 2018-12-24 13:01:13 +01:00
Felix Rieseberg 55b7739d36 ci: AppVeyor: Upload build artifacts, build ia32 2018-12-24 13:00:28 +01:00
Felix Rieseberg 0fda97eb2f ci: AppVeyor: Print build artifacts 2018-12-24 12:52:45 +01:00
Felix Rieseberg 879815fc02 ci: Allow AppVeyor to codesign 2018-12-24 12:39:33 +01:00
Felix Rieseberg c469c3e856 Merge pull request #136 from electron/fix-windows-tests
test: Fix Windows tests
2018-12-24 08:29:31 +00:00
Felix Rieseberg 1b6750e0c5 Merge pull request #135 from electron/ci-publish
ci: Allow Travis to create signed macOS builds
2018-12-24 08:23:41 +00:00
Felix Rieseberg f14bd5c041 test: Fix Windows tests 2018-12-24 09:22:15 +01:00
Felix Rieseberg 76115a3e9c fix: Little linter error 2018-12-24 09:13:43 +01:00
Felix Rieseberg 65f86e0177 Merge branch 'ci-publish' of https://github.com/electron/fiddle into ci-publish 2018-12-24 09:10:36 +01:00
Felix Rieseberg 18cacb1532 chore: Silence the import 2018-12-24 09:10:27 +01:00
Mark Leeandfelixrieseberg 6a924ac88b Update forge.config.js
Co-Authored-By: felixrieseberg <felix@felixrieseberg.com>
2018-12-24 07:56:55 +00:00
Felix Rieseberg d125af6775 chore: Update the CI config 2018-12-24 08:51:15 +01:00
Felix Rieseberg 1a9a0579a5 chore: Add intermediate macOS certificates 2018-12-24 07:21:30 +01:00
Felix Rieseberg acee131288 chore: Install publisher-github 2018-12-24 07:20:45 +01:00
Felix Rieseberg 66536a5260 Merge pull request #134 from electron/fix-data-dir
feat: Prune appData directories after runs
2018-12-24 05:27:42 +00:00
Shelley Vohr 7941a83109 chore: fix 8 low severity vulns 2018-12-23 10:26:57 -07:00
Felix Rieseberg b312b80b69 fix: Throw error in contributors.js 2018-12-23 18:26:22 +01:00
Shelley Vohr b3c3b73b7d feat: prompt users to move to application folder (#132) 2018-12-23 10:25:37 -07:00
Shelley Vohr 0a8e9cde1b chore: add codebytere to contributors 2018-12-23 10:21:55 -07:00
Felix Rieseberg ca3f48697d chore: Add some tests 2018-12-23 17:57:51 +01:00
Felix Rieseberg 067e5cd5e2 feat: Delete app data dir after run 2018-12-23 17:57:51 +01:00
Felix Rieseberg 7e16c8c1f2 chore: Move runner logic out of component 2018-12-23 17:57:51 +01:00
Felix Rieseberg e932aad47b Merge pull request #131 from electron/electron-311
feat: Update to Electron 3.0.11
2018-12-20 09:45:49 -08:00
Felix Rieseberg 82fb8290c3 Merge pull request #130 from electron/modern-add-version-dialog
feat: Modernize 'add local build' dialog
2018-12-20 09:38:29 -08:00
Felix Rieseberg 2eb8bb3cf5 infra: Update to Electron 3.0.11 2018-12-20 09:38:04 -08:00
Felix Rieseberg 3d6448030a feat: Modernize 'add local build' dialog 2018-12-20 09:28:50 -08:00
Felix Rieseberg 65855908d1 Merge pull request #128 from electron/fix-confusing-dialog
fix: Confusing "Fiddle unsaved" dialog
2018-12-20 09:11:08 -08:00
Felix Rieseberg 9e986e24e2 Merge pull request #129 from electron/bump-040
chore: Update version number
2018-12-20 08:57:38 -08:00
Felix Rieseberg 9abc358bd9 fix: Linter issues 2018-12-20 08:57:23 -08:00
Felix Rieseberg 7f7785c69c chore: Update version number 2018-12-19 17:17:40 -08:00
Felix Rieseberg f36f300feb feat: Improve exit dialog, too 2018-12-19 17:16:59 -08:00
Felix Rieseberg 2c49214a25 fix: Confusing unsaved dialog 2018-12-19 16:57:52 -08:00
Felix Rieseberg c7a8163990 Merge pull request #127 from electron/fix-show-me
fix: "Show me" casing issue on Windows
2018-12-19 11:03:03 -08:00
Felix Rieseberg 97995b0ec2 fix: Lowercase for all templates 2018-12-19 10:24:48 -08:00
Felix Rieseberg d2377dcc30 Merge pull request #126 from electron/feat-npm-releases
feat: Use npm registry to fetch releases
2018-12-18 14:39:41 -08:00
Felix Rieseberg ac1197faaa fix: Two more fixes 2018-12-18 13:59:43 -08:00
Felix Rieseberg 9fb9aa6c92 fix: Make linter happy 2018-12-18 13:44:53 -08:00
Felix Rieseberg 6c5e41a2f1 feat: Use npm to download releases 2018-12-18 12:32:13 -08:00
Felix Rieseberg 4b52c81101 chore: Update the built-in releases file 2018-12-18 12:31:47 -08:00
Felix Rieseberg 35f4f1293c infra: Have fetch-releases use npm 2018-12-18 12:31:47 -08:00
Felix Rieseberg 8840b1ef7f Merge pull request #125 from electron/forge-arch-squirrel
chore: automatically add arch to squirrel installer filename
2018-12-18 12:31:16 -08:00
Felix Rieseberg d516522af2 Merge pull request #124 from electron/feat-public-private
feat: Publish fiddles as public/private
2018-12-18 12:30:35 -08:00
Mark Lee 0d0a51a900 infra: automatically add arch to squirrel installer filename 2018-12-17 23:53:46 -08:00
Felix Rieseberg be3b1e4a5b feat: Publish fiddles as public/private 2018-12-17 16:42:55 -08:00
Felix Rieseberg 2acc7112b7 fix: gistId in address bar without url 2018-12-17 16:04:13 -08:00
Felix Rieseberg 407bf3caeb Merge pull request #120 from electron/redesign
feat: Minor redesign, new component library
2018-12-17 15:54:04 -08:00
Felix Rieseberg 49123116d5 fix: Incorrect "arch" reference 2018-12-17 14:08:57 -08:00
Felix Rieseberg 9f6bc2f26f fix: Remove FontAwesome 2018-12-17 09:04:20 -08:00
Felix Rieseberg 5585e2d5d0 feat: Modernize the Token Dialog 2018-12-13 16:37:51 -08:00
Felix Rieseberg 4235969526 fix: forge.config.js 2018-12-13 16:20:25 -08:00
Felix Rieseberg 659d41f1c8 feat: Modernize Electron settings, credits 2018-12-12 18:23:59 -08:00
Felix Rieseberg 2549992782 feat: Modernize settings menu items 2018-12-12 18:23:59 -08:00
Felix Rieseberg b7314b83ee feat: Modernize the GitHub settings 2018-12-12 18:23:59 -08:00
Felix Rieseberg 39c2a9164a feat: Modernize appearance settings 2018-12-12 18:23:59 -08:00
Felix Rieseberg 1a9dc1fbf7 chore: Reorganize some more files 2018-12-12 18:23:59 -08:00
Felix Rieseberg 0eb01c4692 feat: Modernize the publish button 2018-12-12 18:23:59 -08:00
Felix Rieseberg 96667a3b04 chore: Rename publish-button to commands-publish-button 2018-12-12 18:23:59 -08:00
Felix Rieseberg 4365513f57 feat: Modernize address bar 2018-12-12 18:23:59 -08:00
Felix Rieseberg 913b37c285 chore: Rename address-bar 2018-12-12 18:23:59 -08:00
Felix Rieseberg 66c55d39c0 fix: Correctly display colors 2018-12-12 18:23:59 -08:00
Felix Rieseberg 0f3a3aca72 chore: Reorganize styles 2018-12-12 18:23:59 -08:00
Felix Rieseberg 3fc974a758 feat: Modernize the run button 2018-12-12 18:23:59 -08:00
Felix Rieseberg 72b805059c feat: Update version selector 2018-12-12 18:23:59 -08:00
Felix Rieseberg 291882ac5c infra: Simplify and improve parcel build 2018-12-12 18:23:59 -08:00
Felix Rieseberg 3c05cc8ec7 Merge pull request #119 from electron/update-windows-installer-icon
chore: update windows installer icon
2018-12-12 15:30:13 -08:00
Mark Lee 75d2f9ff72 🎨 update windows installer icon 2018-12-12 13:30:35 -08:00
Felix Rieseberg 0119f0ce69 Merge pull request #118 from electron/new-icon
docs: New icon
2018-12-12 13:22:48 -08:00
Mark Lee 8ed5ea94b5 Add SVG to .deb file 2018-12-11 23:53:04 -08:00
simurai deaac4e15c docs: Add icon as SVG 2018-12-12 12:47:53 +09:00
simurai 48e58c095d docs: Use transparent background for README icon 2018-12-11 16:48:07 +09:00
simurai 28f0eca429 docs: Replace loading gif 2018-12-11 12:02:02 +09:00
simurai 529906e1a9 docs: Replace icons 2018-12-11 09:20:15 +09:00
simurai 40c9aaeb7a docs: Add icon to README 2018-12-10 16:36:03 +09:00
Felix Rieseberg 15924c7741 fix: Make the linter happy 2018-11-27 15:47:56 -08:00
Felix Rieseberg c62041a000 Merge pull request #115 from electron/updates-2
chore: Updates for everyone
2018-11-27 15:46:50 -08:00
Felix Rieseberg ff3e931d14 fix: Apply lint fixes 2018-11-27 13:40:58 -08:00
Felix Rieseberg 7cc16b6fab infra: Update to electron-forge v6 beta 30 2018-11-27 13:40:08 -08:00
Felix Rieseberg 45e8e311da Merge pull request #111 from electron/tour-buttons
fix: friendlier button text for intro tour
2018-11-27 13:08:39 -08:00
Felix Rieseberg b09e792583 chore: Fix type errors in npm 2018-11-27 13:07:57 -08:00
Felix Rieseberg cdb862442c infra: Use library mode in fontawesome 2018-11-27 13:03:19 -08:00
Felix Rieseberg 1e1566dbdb infra: Update TypeScript 2018-11-27 13:03:04 -08:00
Felix Rieseberg e571fcca4f fix: style and logic in npm.ts 2018-11-27 13:02:50 -08:00
Felix Rieseberg e61e49987e infra: Update node-abi, node-fetch 2018-11-27 11:47:45 -08:00
Felix Rieseberg 133411c150 infra: Update mobx 2018-11-27 11:46:48 -08:00
Felix Rieseberg db8c7c32ab infra: Update react, semver 2018-11-27 11:44:59 -08:00
Felix Rieseberg 003e27f57e infra: Update monaco-editor 2018-11-27 11:41:57 -08:00
Felix Rieseberg 72d717bd48 infra: Update Electron to 3.0.10 2018-11-26 15:11:34 -08:00
Felix Rieseberg 675500fe25 infra: Update fontawesome 2018-11-26 12:38:23 -08:00
Felix Rieseberg fbe2311d53 infra: Update electron-forge to lastest beta 2018-11-26 12:37:09 -08:00
Felix Rieseberg ef42b1affe tests: Fix tests 2018-11-23 11:35:53 -08:00
Felix Rieseberg 2adde7d9ce Merge pull request #114 from ada-lovecraft/master
fix: erroneous require behavior
2018-11-23 11:28:54 -08:00
Ada Lovecraft 5fa0ddcc73 cleaned up npm module searching and added inline docs 2018-11-21 12:22:46 -06:00
Ada Lovecraft 5d501f0827 fixed npm module loading 2018-11-21 00:33:35 -06:00
Jeremy Apthorp d738936952 friendlier button text for intro tour
In general I like it when buttons are labeled with what's going to happen when I click on them
2018-11-09 13:33:01 -08:00
Shelley Vohr 57ce8b0b79 chore: remove unused requires in main (#109) 2018-10-30 16:55:08 -07:00
Felix Rieseberg 46f4efb4b4 Merge pull request #106 from electron/minor-fixups
feat: Add key property for buttons, update electron and typescript
2018-10-11 16:35:51 -04:00
HashimotoYT 10a696a9d7 Update test snapshot 2018-10-11 23:25:38 +03:00
HashimotoYT 585c0e1183 Update Electron, add myself to contributors. 2018-10-11 21:40:16 +03:00
HashimotoYT 946d32fdd0 Update TypeScript and Electron 2018-10-11 02:19:17 +03:00
HashimotoYT f09363ec26 Add key prop for buttons in token dialog 2018-10-11 02:11:40 +03:00
Felix Rieseberg 1a6876497a Merge pull request #104 from electron/hashi/gist-error-message
feat: provide error message for Token dialog
2018-10-08 16:49:56 -07:00
HashimotoYT 6bc04f5d2a Trying revert templates 2018-10-09 02:16:46 +03:00
HashimotoYT 9f7bf9d299 Provide error message for token dialog 2018-10-08 01:33:09 +03:00
Felix Rieseberg 3b7873917f Merge pull request #103 from electron/local-versions
feat: Add support for local Electron builds (with versions)
2018-10-05 19:17:04 -07:00
Felix Rieseberg 4616f12d6c 👷 Fix snapshot issues 2018-10-05 19:02:57 -07:00
Felix Rieseberg 63f37c9553 👷 Add a few tests 2018-10-05 18:56:22 -07:00
Felix Rieseberg 09edfa16b0 👷 Update snapshots 2018-10-05 18:56:22 -07:00
Felix Rieseberg da3a607bc1 🔧 Pretty version chooser 2018-10-05 18:56:22 -07:00
Felix Rieseberg 5fcc34b035 🔧 Parse the version 2018-10-05 18:56:22 -07:00
Felix Rieseberg dfed297226 👷 Fix existing tests 2018-10-05 18:56:22 -07:00
Felix Rieseberg 7cb2c4d188 🔧 Run local versions 2018-10-05 18:56:22 -07:00
Felix Rieseberg f0215d8c90 🔧 Add local version dialog 2018-10-05 18:56:21 -07:00
Felix Rieseberg ffdb57e2f3 🔧 More of it all 2018-10-05 18:56:11 -07:00
Felix Rieseberg e5dd6ffb4e 🎨 Use the correct delete icon 2018-09-28 16:11:32 -07:00
Felix Rieseberg 1083b6c2de 🔧 Cleanup 2018-09-28 13:45:46 -07:00
Felix Rieseberg 73b6cb0cb2 🔧 Get all versions during refresh 2018-09-28 13:14:53 -07:00
Felix Rieseberg 3e5914b192 🔧 Basic state support for local versions 2018-09-28 13:11:30 -07:00
Felix Rieseberg fe81d5c14c Merge pull request #102 from electron/update-dependencies
chore: Update all the dependencies
2018-09-27 13:16:49 -07:00
Felix Rieseberg c85536a8b9 🔧 Implement deprecations 2018-09-27 13:05:03 -07:00
Felix Rieseberg 8d90dbed09 🔧 Update tests 2018-09-27 13:01:44 -07:00
Felix Rieseberg b918c13c72 🔧 Fix standardJs lint issues 2018-09-27 11:43:47 -07:00
Felix Rieseberg 570bab9c29 🔧 Refactor and fix code lint issues 2018-09-27 11:42:50 -07:00
Felix Rieseberg f0f7ed22ee 📦 Update stylelint and standard 2018-09-27 10:40:43 -07:00
Felix Rieseberg a62c1550a1 📦 Update TypeScript 2018-09-27 10:39:39 -07:00
Felix Rieseberg 754473e10a 📦 Update to Electron 3.0.1 2018-09-27 10:32:21 -07:00
Felix Rieseberg 732f81cd33 📦 Update namor 2018-09-27 10:20:01 -07:00
Felix Rieseberg 47e5182117 📦 Update Octokit 2018-09-27 10:18:55 -07:00
Felix Rieseberg 92c831cc7b 📦 Update FontAwesome 2018-09-27 10:18:04 -07:00
Felix Rieseberg 8ffb215797 📦 Bump React 2018-09-27 10:17:30 -07:00
Felix Rieseberg 616264f017 📦 Bump mobx 2018-09-27 10:16:21 -07:00
Felix Rieseberg ee6e474b04 📦 Bump the version number 2018-09-27 10:15:46 -07:00
Felix Rieseberg ee8fa678f9 Merge pull request #101 from electron/fix-download-link
fix: don't pin download link to 0.1 tag
2018-09-27 09:44:25 -07:00
Felix Rieseberg e605ef399d 📝 Point to latest 2018-09-27 09:44:18 -07:00
Charles Kerr e653cb5c0f fix: don't pin download link to 0.1 tag 2018-09-26 15:29:16 -05:00
Felix Rieseberg 44df77b7e8 Merge pull request #94 from BeniCheni/gist-link-input
fix: Adjust gist link address bar
2018-09-25 07:12:07 -07:00
Felix Rieseberg 99db07e000 👷 And again 2018-09-25 07:04:38 -07:00
Felix Rieseberg 0f7498e5ea 👷 Fix them again 2018-09-25 06:52:46 -07:00
Felix Rieseberg b30a3cb493 👷 Update snapshots 2018-09-25 06:45:17 -07:00
Felix Rieseberg 6686f8f1b3 🔧 Fix the pattern validation 2018-09-25 06:34:38 -07:00
Felix Rieseberg 5db586b34d 🎨 Fix the input field style 2018-09-25 06:34:15 -07:00
Felix Rieseberg 076c3dedaa 🔧 Correctly expand input field 2018-09-25 06:17:48 -07:00
Felix Rieseberg 2bf245bbb9 Merge branch 'gist-link-input' of https://github.com/BeniCheni/fiddle into BeniCheni-gist-link-input 2018-09-25 06:04:13 -07:00
Felix Rieseberg 6445d82dfa Merge pull request #95 from nctl144/soft_wrap
feat: enable options for the text editor
2018-09-25 06:03:21 -07:00
Benjamin Chen bbbbea8a27 🔧 Update gist link address bar per PR comments
Refs: https://github.com/electron/fiddle/pull/94#issuecomment-419883846
2018-09-24 23:07:48 -04:00
Mark Lee 7f4e6c70d9 🔧 don't hang on window close when run via npm start
Fixed via Electron Forge v6.0.0-beta.28

See: https://github.com/electron-userland/electron-forge/pull/567
2018-09-24 23:07:48 -04:00
nctl144 ebbd98b1dd fix equal syntax 2018-09-22 14:27:19 -04:00
nctl144 712707026c fix the var type 2018-09-22 13:12:45 -04:00
nctl144 595ad0e5ab enable minimap toggle 2018-09-21 16:51:32 -04:00
nctl144 4c09f69435 fix the syntax error 2018-09-21 16:25:59 -04:00
nctl144 deb6bd6323 delete debug log 2018-09-21 16:06:54 -04:00
nctl144 db7b8861b5 enable toggle soft wrap 2018-09-16 17:50:51 -04:00
nctl144 ca5600d521 test new methods 2018-09-16 17:17:38 -04:00
nctl144 619d48bfe3 get the value from the json file 2018-09-12 00:07:51 -04:00
nctl144 ab7dff11c9 fix the value in the json file 2018-09-12 00:07:17 -04:00
nctl144 877a2ec434 register events 2018-09-11 23:55:22 -04:00
nctl144 7806bb47c8 create json file that allows loading editor values 2018-09-11 15:42:03 -04:00
Felix Rieseberg 25f73e5390 Merge pull request #97 from electron/fix-forge-start-exit
fix: don't hang on window close when run via npm start
2018-09-11 11:08:24 +02:00
Mark Lee 9eeecc94d5 🔧 don't hang on window close when run via npm start
Fixed via Electron Forge v6.0.0-beta.28

See: https://github.com/electron-userland/electron-forge/pull/567
2018-09-10 07:55:34 -07:00
nctl144 bcd37ff67c init the button for the soft wrap 2018-09-06 15:49:16 -04:00
Benjamin Chen 6099c7b1ea 🔧 Update address bar's left padding style & test 2018-09-05 22:39:52 -04:00
Benjamin Chen 237ce59100 🔧 Remove "placehold" from Gist Link address bar 2018-09-05 01:04:53 -04:00
Benjamin Chen 0289a92a06 🔧 Fix bug for Gist Link address bar 2018-09-05 00:55:01 -04:00
Felix Rieseberg 86fb981c98 🔧 Add windows certificate info 2018-08-29 15:53:29 -07:00
Felix Rieseberg 3d7cd9eeba 🔧 Force close 2018-08-29 15:24:46 -07:00
Felix Rieseberg 066da6433e Merge pull request #81 from electron/road-to-020
chore: Update dependencies, update version number
2018-08-29 09:34:44 -07:00
Felix Rieseberg b83378df5a 👷 Fix tests 2018-08-28 18:38:02 -07:00
Felix Rieseberg 5aaf46beb9 📦 Update TypeScript & friends 2018-08-28 18:30:37 -07:00
Felix Rieseberg 066195ed39 📦 Update React types 2018-08-28 18:29:53 -07:00
Felix Rieseberg 96ea7bfe61 🔧 Fix isFocused for editor 2018-08-28 18:27:16 -07:00
Felix Rieseberg 0e68605a05 📦 Update Parcel 2018-08-28 18:26:47 -07:00
Felix Rieseberg 7a5d84908f 🔧 Harden version-chooser 2018-08-28 18:13:44 -07:00
Felix Rieseberg e3ff84f14e 🔧 Fix: Don't save 0 known versions 2018-08-28 18:02:50 -07:00
Felix Rieseberg 37d6f63b33 🔧 Fix: Always display cut, copy, paste 2018-08-28 17:55:13 -07:00
Felix Rieseberg 8867679f3a Merge pull request #92 from electron/margin-error
fix: Correct padding error
2018-08-28 17:49:42 -07:00
Felix Rieseberg 12d9892be5 Merge pull request #93 from electron/unchanged-error-guard
fix: Prevent error in isContentUnchanged
2018-08-28 17:49:32 -07:00
Felix Rieseberg 28ccff3254 📦 Update enzyme, less, jest 2018-08-28 17:48:56 -07:00
Felix Rieseberg e8ab965579 📦 Update Electron 2018-08-28 17:47:52 -07:00
Felix Rieseberg e0d5ceacdf 📦 Update mobx 2018-08-28 17:45:54 -07:00
Felix Rieseberg d62a2a2c78 📦 Update fs-extra, electron-download 2018-08-28 17:43:51 -07:00
Felix Rieseberg e5d2bfa7a3 📦 Update React, mobx, semver 2018-08-28 17:38:38 -07:00
Felix Rieseberg 0e05a34efa 🔧 Fix missing removeItem mock 2018-08-28 17:38:38 -07:00
Felix Rieseberg 1f4f489947 📦 Update version number to 0.2.0 2018-08-28 17:38:38 -07:00
Felix Rieseberg 9cab776159 🔧 Fix last bugs 2018-08-28 17:38:38 -07:00
Felix Rieseberg 26e3f09b95 🔧 Prevent error in isContentUnchanged 2018-08-28 17:36:45 -07:00
Felix Rieseberg 3c9f06909b 🔧 Fix margin error 2018-08-28 17:32:22 -07:00
Felix Rieseberg 779f273339 Merge pull request #89 from aaronabramov/enzyme-to-json
test: use enzyme-to-json and fix tests
2018-08-28 12:56:47 -07:00
Felix Rieseberg 1cdfb0f031 Merge pull request #84 from BeniCheni/adjust-mosaic-styles
fix: Update mosaic less to improve tooltip styles
2018-08-28 12:55:00 -07:00
Aaron Abramov 6103db3a80 use enzyme-to-json 2018-08-24 15:53:16 -07:00
Beni von Cheni 8b63134324 📚 Add "benicheni" to contributor list per prev help on #78 (#83) 2018-08-23 10:17:19 -05:00
Benjamin Chen 60e58dbd5a 🎨 Update mosaic less to improve tooltip styles 2018-08-22 01:21:05 -04:00
Felix Rieseberg 38b7b08691 Merge pull request #82 from nctl144/credit
chore: Add myself to the contributor list
2018-08-21 20:03:58 -07:00
nctl144 5acdcc4ab2 add myself to the contributor list 2018-08-21 22:47:54 -04:00
Felix Rieseberg 8dd587ac7e Merge pull request #80 from electron/flexible-quick-start
feat: Make quick start flexible
2018-08-21 19:00:07 -07:00
Felix Rieseberg aba46cd666 🔧 Do it on boot, too 2018-08-21 18:04:09 -07:00
Felix Rieseberg 3286852006 🔧 Make quick start flexible 2018-08-21 18:00:24 -07:00
Felix Rieseberg 2682a3f67c Merge pull request #79 from electron/old-versions
feat: Allow old versions, enable configurable list
2018-08-21 13:04:22 -07:00
Felix Rieseberg 6e7fb45334 👷 One more test fix 2018-08-21 12:56:19 -07:00
Felix Rieseberg 0ef82b6ad6 🔧 Linter 2018-08-21 12:37:45 -07:00
Felix Rieseberg 41c6dd3d44 👷 Add a few tests 2018-08-21 12:30:42 -07:00
Felix Rieseberg c6f90331b9 👷 Fix existing tests 2018-08-21 12:11:15 -07:00
Felix Rieseberg d7b00c4166 Merge pull request #77 from nctl144/sp_electron18
fix: Support Electron <1.8.x
2018-08-21 08:46:40 -07:00
Felix Rieseberg 3fef83356d Merge pull request #78 from BeniCheni/adjust-mosaic-window-styles
fix: Adjust .mosaic-window-body styles
2018-08-21 08:45:52 -07:00
Benjamin Chen 34be3369f3 🐛 Adjust .mosaic-window-body styles 2018-08-20 22:25:21 -04:00
nctl144 5c04d74ebc replace loadFile with loadURL 2018-08-20 15:57:35 -04:00
Felix Rieseberg 29ac8334fe 🔧 Configurable version options 2018-08-17 18:45:35 -07:00
Felix Rieseberg 604251260d 🔧 Allow for multiple pages 2018-08-17 10:53:20 -07:00
Gökay Gürcan 7015e49725 🔧 📝 Broken link (#73) 2018-08-14 06:41:56 -07:00
Felix Rieseberg d3a19c9b3c Merge pull request #71 from electron/open-folder-on-save
fix: Do not use "showItemInFolder" on save
2018-08-13 17:57:37 -07:00
Felix Rieseberg 6958192096 Merge pull request #72 from electron/fix-path-linux
fix: Path on Linux, too
2018-08-13 16:13:56 -07:00
Felix Rieseberg 100be9548e Merge pull request #70 from electron/themes
feat: Theme support
2018-08-13 16:13:42 -07:00
Felix Rieseberg bff1e23be3 🔧 Fix path on Linux, too 2018-08-13 08:34:15 -07:00
Felix Rieseberg 8e6ddd59fb 🔬 Update tests 2018-08-13 08:33:31 -07:00
Felix Rieseberg 419b5f57a0 🔧 Fix "showItemInFolder" use on save 2018-08-13 08:29:48 -07:00
Felix Rieseberg 27858cac6a 🔧 Update GitHub Settings header 2018-08-13 08:25:44 -07:00
Felix Rieseberg ac7221b117 🔬 Test create theme button 2018-08-13 08:24:30 -07:00
Felix Rieseberg 31c898f082 🔧 Create new theme from current 2018-08-13 08:04:17 -07:00
Felix Rieseberg 1bc381571c 🔬 Test CSS string methods 2018-08-13 07:04:58 -07:00
Felix Rieseberg 33902dbe01 🔧 Cleaner methods 2018-08-13 06:51:50 -07:00
Felix Rieseberg 2285b14184 🔧 Fix linter errors 2018-08-12 22:44:43 -07:00
Felix Rieseberg b67654769f 🔬 Add tests for settings-general 2018-08-12 22:34:49 -07:00
Felix Rieseberg 8cc6f766a5 🔬 Fix tests, add themes-spec 2018-08-12 21:58:53 -07:00
Felix Rieseberg c21e398c89 🎨 Improve themed console look 2018-08-12 20:53:31 -07:00
Felix Rieseberg 8019951e8c 🎨 Fix the Electron buttons 2018-08-12 20:37:07 -07:00
Felix Rieseberg 707ee2192f 🔧 Add theme settings and methods 2018-08-12 20:31:51 -07:00
Felix Rieseberg 2a4e59461e 🎨 Use nothing but variables in styles 2018-08-12 20:30:35 -07:00
Felix Rieseberg 22440298d7 🔧 Allow loading from disk 2018-08-12 18:00:39 -07:00
Felix Rieseberg 37168a99ee 🔧 Basic theme support 2018-08-12 17:48:19 -07:00
Charles Kerr 40254126d0 Merge pull request #65 from regularfry/patch-1
Punctuation fix
2018-08-11 10:25:09 -05:00
Alex Young 492893b71a Punctuation fix
"Let's" would mean "let us", or "of the let".  "Lets" is "allows", which is what's needed here.
2018-08-11 10:43:32 +01:00
Felix Rieseberg a1c91727ae Merge pull request #64 from cmpscabral/master
Add missing script tag for renderer process
2018-08-10 14:34:49 -07:00
Carlos 634d0ca734 Add missing script tag for renderer process 2018-08-10 20:43:24 +01:00
Felix Rieseberg 1557841c81 📝 Add a download button 2018-08-10 08:05:17 -07:00
Felix Rieseberg ff3fbac7ed 🔧 Fix "New Fiddle" 2018-08-10 07:45:17 -07:00
Felix Rieseberg 82f83acba4 🔧 Fix the menu shortcut 2018-08-09 18:37:35 -07:00
Felix Rieseberg f957e0c53a 🔧 Last minute npm fix 2018-08-09 18:34:26 -07:00
Felix Rieseberg bd1915a0f8 🔧 Case-sensitive folder names 2018-08-09 16:49:56 -07:00
Felix Rieseberg 01413c8830 🔧 Update Electron release 2018-08-09 16:40:22 -07:00
Felix Rieseberg 62002debd4 🔧 Fix typos 2018-08-09 16:27:07 -07:00
Felix Rieseberg 7fc1008121 Merge pull request #58 from electron/icons
feat: Update icons
2018-08-09 14:33:02 -07:00
Felix Rieseberg aaf183614f Merge pull request #59 from electron/update-electron
infra: Upgrade to Electron 2.0.7
2018-08-09 13:35:13 -07:00
Felix Rieseberg 4a2a73b780 🔧 Update path (take 2) 2018-08-09 13:23:41 -07:00
Felix Rieseberg a5985e8dde 🔧 Update path (take 1) 2018-08-09 13:22:57 -07:00
Felix Rieseberg b5bf652df0 🎨 Smaller ico file 2018-08-09 13:13:03 -07:00
Felix Rieseberg 79cdad1291 📦 Electron 2.0.7 2018-08-09 13:05:12 -07:00
Felix Rieseberg 35b7b8994f Merge pull request #57 from electron/security-basics
fix: Some sane security basics
2018-08-09 13:02:21 -07:00
Felix Rieseberg 1ca25fe515 🔬 Fix tests 2018-08-09 12:53:04 -07:00
Felix Rieseberg 82976cb269 🎨 Update icons 2018-08-09 12:46:40 -07:00
Felix Rieseberg 6487db8207 🔧 Some sane security basics 2018-08-09 07:08:59 -07:00
Felix Rieseberg 325cb3416a Merge pull request #56 from electron/unsaved-handling
feat: Handle saved/unsaved state
2018-08-09 07:04:23 -07:00
Felix Rieseberg 73ea0873f8 📦 0.1.0 2018-08-08 20:31:14 -07:00
Felix Rieseberg 987d05dd0f 🔧 Handle saved/unsaved state 2018-08-08 20:29:05 -07:00
Felix Rieseberg 61e8524996 Merge pull request #54 from electron/multi-window
feat: Multi window support
2018-08-08 20:02:08 -07:00
Felix Rieseberg 82b0fea020 Merge pull request #55 from electron/fix-fiddle
fix: Install beta.22 of Fiddle
2018-08-08 20:01:58 -07:00
Felix Rieseberg caff29dc39 🔧 Handle missing npm 2018-08-08 19:49:48 -07:00
Felix Rieseberg de1a908a6e 🔧 Install the beta.22 of forge 2018-08-08 19:44:02 -07:00
Felix Rieseberg 766cf9100a 🔬 Fix tests 2018-08-08 19:42:06 -07:00
Felix Rieseberg 27dc99e7cb 🔧 Create new windows 2018-08-08 19:38:50 -07:00
Felix Rieseberg d711082c67 🔧 Allow new window creation 2018-08-08 19:36:15 -07:00
Felix Rieseberg 7ac567e522 📝 Update Readme 2018-08-08 19:26:59 -07:00
Felix Rieseberg b074071333 Merge pull request #52 from electron/teach-me
feat: Teach me
2018-08-08 18:55:25 -07:00
Felix Rieseberg 82716bf02c 🔧 Cleanup 2018-08-08 18:42:49 -07:00
Felix Rieseberg 11d062e6e0 🏫 Template for webFrame 2018-08-08 18:41:49 -07:00
Felix Rieseberg 05ca117f02 🏫 Template for webContents 2018-08-08 18:34:19 -07:00
Felix Rieseberg 05e2785aa4 🏫 Template for Tray 2018-08-08 18:34:08 -07:00
Felix Rieseberg c2bc958504 🏫 Template for systemPreferences 2018-08-08 18:19:22 -07:00
Felix Rieseberg f2829e851c 🏫 Template for TouchBar 2018-08-08 18:17:18 -07:00
Felix Rieseberg b3d8971a1a 🏫 Template for shell 2018-08-08 18:09:18 -07:00
Felix Rieseberg b3c39f1546 🔧 Fix various errors 2018-08-08 18:09:12 -07:00
Felix Rieseberg a3ab1405a3 🔧 Clean old tmp folders 2018-08-08 18:04:05 -07:00
Felix Rieseberg 56632c94a0 🔧 Uppercase names 2018-08-08 17:53:48 -07:00
Felix Rieseberg 431b3d5bbc 🏫 Template for screen 2018-08-08 17:53:40 -07:00
Felix Rieseberg b4e1740294 🏫 Template for session 2018-08-08 17:53:27 -07:00
Felix Rieseberg 0d39c35081 🔧 Simplify HTML templates 2018-08-08 17:45:03 -07:00
Felix Rieseberg 685637e6c9 🏫 Template for powerMonitor 2018-08-08 17:44:53 -07:00
Felix Rieseberg 1b7f913bcf 🏫 Template for powerSaveBlocker 2018-08-08 17:44:44 -07:00
Felix Rieseberg 5b0d17c88a 🏫 Template for remote 2018-08-08 17:44:36 -07:00
Felix Rieseberg accf71da44 🏫 Template for net 2018-08-08 17:44:29 -07:00
Felix Rieseberg b2ff94744a 🏫 Template for Notification 2018-08-08 17:44:22 -07:00
Felix Rieseberg 58c656c2f3 🏫 Template for nativeImage 2018-08-08 17:11:39 -07:00
Felix Rieseberg 059dff3561 🏫 Template for Menu 2018-08-08 17:11:27 -07:00
Felix Rieseberg 32c4b6c804 🏫 Template for ipcRenderer, ipcMain 2018-08-08 16:45:32 -07:00
Felix Rieseberg 8b8197d3c8 🏫 Template for inAppPurchase 2018-08-08 16:45:17 -07:00
Felix Rieseberg dfd5afd115 🏫 Accelerator example is now the globalShortcut example 2018-08-08 16:45:01 -07:00
Felix Rieseberg 0336a4292e 🔧 Fix desktopCapturer template 2018-08-08 16:25:45 -07:00
Felix Rieseberg c39b84da3e 🔧 Fix linter errors 2018-08-08 09:05:56 -07:00
Felix Rieseberg 4c779b6033 🔧 Add standard linting to templates 2018-08-08 08:45:32 -07:00
Felix Rieseberg 2ebb46a988 🏫 Template for desktopCapturer 2018-08-07 19:23:28 -07:00
Felix Rieseberg 3413e1cb9f 🏫 Update various templates 2018-08-07 19:23:19 -07:00
Felix Rieseberg b1808ef8e6 🏫 Template for Cookies 2018-08-07 19:23:02 -07:00
Felix Rieseberg 558431a85c 🏫 Template for BrowserWindow 2018-08-07 19:22:47 -07:00
Felix Rieseberg 3edbc019c6 🏫 Template for dialog 2018-08-07 19:22:34 -07:00
Felix Rieseberg 06af8ba35f 🏫 Template for crashReporter 2018-08-07 19:22:27 -07:00
Felix Rieseberg 0aed40af5c 🏫 Template for Debugger 2018-08-07 19:22:19 -07:00
Felix Rieseberg bec4e2fd86 🏫 Template for contentTracing 2018-08-07 19:22:13 -07:00
Felix Rieseberg 5cd5881d35 🏫 Template for BrowserView 2018-08-07 19:22:03 -07:00
Felix Rieseberg fc3a7b2960 🏫 Template for clipboard 2018-08-07 19:21:51 -07:00
Felix Rieseberg 3f102b8ee7 Merge pull request #51 from electron/fix-dialog
fix: Dialog position on Linux and Windows
2018-08-07 17:06:14 -07:00
Felix Rieseberg 46450ee768 👷 Fix tests on Linux 2018-08-07 14:43:33 -07:00
Felix Rieseberg 33f9f9c1c3 🔧 More templates 2018-08-06 20:55:05 -07:00
Felix Rieseberg b731ae710c 🔧 A first batch of templates 2018-08-06 20:52:23 -07:00
Felix Rieseberg c486b0ee78 🔬 Add some tests 2018-08-06 20:35:52 -07:00
Felix Rieseberg 9e7dd4dc06 🔧 Add template support 2018-08-06 20:35:22 -07:00
Felix Rieseberg 99a37fbd82 🔧 Fix dialog position on Linux and Windows 2018-08-06 19:32:28 -07:00
Felix Rieseberg e27f7f29c1 Merge pull request #49 from electron/no-npm
feat: Handle missing npm
2018-08-06 19:20:29 -07:00
Felix Rieseberg 0b50e952ac Merge pull request #50 from electron/save-version
feat: Remember last selected version
2018-08-06 19:20:20 -07:00
Felix Rieseberg 58b252f7f2 🔧 Remember last selected version 2018-08-06 19:07:14 -07:00
Felix Rieseberg 5dffe5fce8 Merge branch 'master' into no-npm 2018-08-06 19:04:09 -07:00
Felix Rieseberg 2b6e4ee907 🔧 Handle missing npm better 2018-08-06 19:01:22 -07:00
Felix Rieseberg 9d85ae36d5 🔧 Check for npm 2018-08-05 22:33:41 -07:00
Felix Rieseberg 0f1ff6a70b Merge pull request #48 from electron/offline
feat: Support being offline better
2018-08-05 21:03:31 -07:00
Felix Rieseberg 1564284e30 🔬 Test runner/filemanager cleanup 2018-08-05 14:04:56 -07:00
Felix Rieseberg dd46a0fb7f 🔧 Remove temp directory on error 2018-08-05 14:04:43 -07:00
Felix Rieseberg 2536c0fc89 🔧 Allow contributors.js to work offline 2018-08-05 13:53:35 -07:00
Felix Rieseberg ff700845fb Merge pull request #47 from electron/more-unit-tests
More unit tests, fix "Save Fiddle"
2018-08-05 13:51:48 -07:00
Felix Rieseberg e74a2876b3 👷 Improve Travis test performance 2018-08-04 22:37:05 -07:00
Felix Rieseberg 440633ae3c :micrscope: Test App component 2018-08-04 15:38:39 -07:00
Felix Rieseberg 6b0ae32263 🔬 Test protocol 2018-08-04 13:45:46 -07:00
Felix Rieseberg 7d49f9076c 🤔 Let's cut "npm update" 2018-08-04 10:37:37 -07:00
Felix Rieseberg 7b2dbc5e1b 🔧 Goodbye, jest-localstorage-mock 2018-08-04 10:01:58 -07:00
Felix Rieseberg 331446a031 🔬 Test menu 2018-08-04 09:55:25 -07:00
Felix Rieseberg 62abe7e6e0 🔧 Set testURL 2018-08-04 08:55:02 -07:00
Felix Rieseberg c6ba5399a6 🔬 Test transforms 2018-08-03 20:12:55 -07:00
Felix Rieseberg b63302bb4b 🔧 Clean up all tests 2018-08-03 20:04:15 -07:00
Felix Rieseberg 9409b3280f 🔬 Test appState 2018-08-03 19:49:06 -07:00
Felix Rieseberg 5b3237d437 🔬 Test versions 2018-08-03 19:00:06 -07:00
Felix Rieseberg b606fa2bfe 🔬 Test themes 2018-08-03 18:25:19 -07:00
Felix Rieseberg 746ed2fd5d 🔬 Test npm 2018-08-03 18:23:47 -07:00
Felix Rieseberg 724fd39ba0 🔬 Test ipcRendererManager 2018-08-03 17:47:22 -07:00
Felix Rieseberg 65875235fe 🔬 Test FileManager component (ipc) 2018-08-03 17:42:49 -07:00
Felix Rieseberg 2d09369d6c 🔬 Test FileManager 2018-07-30 19:17:38 -07:00
Felix Rieseberg ee48344b9a Merge pull request #45 from electron/appveyor
test: Add AppVeyor, Readme badges
2018-07-30 18:40:09 -07:00
Felix Rieseberg bed5d7f739 Merge pull request #46 from electron/enforce-codestyle
test: Enfore a stricter code style
2018-07-30 18:39:56 -07:00
Felix Rieseberg efc2d6b3c4 🔧 Use npm ci 2018-07-30 18:20:10 -07:00
Felix Rieseberg aab01abff4 🔧 Apply linter changes 2018-07-30 18:07:46 -07:00
Felix Rieseberg 23aca61e4a 🔧 Clamp down on codestyle 2018-07-30 18:07:31 -07:00
Felix Rieseberg a057b2a26a 📝 Update Readme 2018-07-30 15:35:02 -07:00
Felix Rieseberg 8f754fb356 🔧 Add AppVeyor yaml file 2018-07-30 15:32:47 -07:00
Felix Rieseberg 528aba15e7 🔬 Add one more test 2018-07-30 15:29:43 -07:00
Felix Rieseberg 54dd7f8d3a Merge pull request #44 from electron/component-tests-2
test: More component tests (Editor, Dialogs, Header, Output)
2018-07-30 14:51:36 -07:00
Felix Rieseberg 6441c266b5 🔧 Update snapshots 2018-07-30 14:02:38 -07:00
Felix Rieseberg 66f19c3c6f 🔬 Increase component coverage 2018-07-30 13:28:27 -07:00
Felix Rieseberg ce7ba3cac1 🔧 Fix test output 2018-07-30 13:17:50 -07:00
Felix Rieseberg 9e53b09941 🔧 Enable "no-unused-expression" 2018-07-30 13:05:03 -07:00
Felix Rieseberg 6522890bcf 🔬 Test Runner component 2018-07-30 13:00:54 -07:00
Felix Rieseberg 8d9776da6a 🔬 Test WelcomeTour component 2018-07-30 10:55:54 -07:00
Felix Rieseberg 78905d9b6a 🔬 Test Tour component 2018-07-30 10:49:52 -07:00
Felix Rieseberg cfa0620aab 🔬 Test VersionChooser component 2018-07-30 10:26:27 -07:00
Felix Rieseberg 9e7cb2808e 🔬 Test TokenDialog component 2018-07-30 10:12:18 -07:00
Felix Rieseberg eb12f86725 🔬 Test Settings component 2018-07-30 09:24:28 -07:00
Felix Rieseberg d3651ef4ca 🔬 Test GitHubSettings component 2018-07-30 09:11:41 -07:00
Felix Rieseberg 6acce843c3 🔧 Update snapshots 2018-07-30 09:07:39 -07:00
Felix Rieseberg be6c1b0781 🔬 Test ElectronSettings component 2018-07-30 08:21:56 -07:00
Felix Rieseberg 241bec88b0 🔬 Test CreditsSettings component 2018-07-27 12:19:14 -07:00
Felix Rieseberg 48e5939d10 🔬 Test Publish Button component 2018-07-27 12:11:31 -07:00
Felix Rieseberg 2389e07781 🔬 Improve Output spec 2018-07-27 12:11:19 -07:00
Felix Rieseberg 56a4c8f641 🔧 Fix it harder 2018-07-27 11:54:24 -07:00
Felix Rieseberg c4a7929d82 🔧 Update the Output component, too 2018-07-27 08:39:29 -07:00
Felix Rieseberg 856b0cc437 🔧 Ugh, timezones. Output spec take II. 2018-07-27 08:39:10 -07:00
Felix Rieseberg 93613a421a 🔧 Update Output spec 2018-07-27 08:09:11 -07:00
Felix Rieseberg ea25ee4d78 🔬 Test Output component 2018-07-26 10:47:38 -07:00
Felix Rieseberg 20d3f351ff 🔬 Test Header component 2018-07-26 10:43:10 -07:00
Felix Rieseberg c64bb2c8ea 🔬 Test Editors component 2018-07-26 10:40:09 -07:00
Felix Rieseberg 5153921e43 🔬 Test editor 2018-07-26 10:25:14 -07:00
Felix Rieseberg aa763b839d 🔧 Test ergonomics 2018-07-26 10:25:10 -07:00
Felix Rieseberg fd1984d140 🔬 Test Dialogs component 2018-07-26 10:04:13 -07:00
Felix Rieseberg b63fe6bda2 🔬 Test Commands component 2018-07-26 09:54:32 -07:00
Felix Rieseberg a296945e0d 🔧 Enable noImplicitAny, noImplicitReturns 2018-07-26 09:47:08 -07:00
Felix Rieseberg 4938c0cd01 🔧 Consistent return 2018-07-26 09:46:33 -07:00
Felix Rieseberg cf30c9493e Merge pull request #42 from electron/component-tests
test: Test React components (a first set of them)
2018-07-26 09:45:36 -07:00
Felix Rieseberg 7a544cdd8e 🔧 Updating the snapshot, too 2018-07-26 09:13:02 -07:00
Felix Rieseberg 7e43acdd53 🔧 Specify platform in dialog tests 2018-07-25 23:23:00 -07:00
Felix Rieseberg 7ffe79c9e1 🔬 Test chrome-mac 2018-07-25 19:48:05 -07:00
Felix Rieseberg d68588aeb2 🔬 Test address bar 2018-07-25 19:45:27 -07:00
Felix Rieseberg 4b60316ebc 🔬 Test Dialog 2018-07-25 19:45:19 -07:00
Felix Rieseberg 4b5908b97d 🔧 Setup Enzyme 2018-07-25 19:45:02 -07:00
Felix Rieseberg 15f397f829 📦 Add Enzyme & friends 2018-07-25 19:44:53 -07:00
Felix Rieseberg e51f653ed9 Merge pull request #40 from electron/fix-settings-exit
fix: Settings toggle, context menu init, and a typo
2018-07-25 18:15:40 -07:00
Felix Rieseberg 8168ec9d26 👷 Fix test 2018-07-25 17:27:57 -07:00
Felix Rieseberg b11e6bd29e Merge pull request #41 from electron/save-as-dialog-titles
fix: save as dialog titles
2018-07-25 15:01:02 -07:00
Mark Lee 3ca0426994 fix: save as dialog titles
"Save as Forge Project" dialog title was incorrect.

"Save as" dialog title had `[Object BrowserWindow]` appended to it.
2018-07-24 23:50:20 -07:00
Felix Rieseberg 4106a63ee3 🔧 Fix context menu init 2018-07-24 08:53:34 -07:00
Felix Rieseberg ac4b5797fe 🔧 Fix settings toggle bug 2018-07-24 08:29:38 -07:00
Felix Rieseberg d66fecfb90 🔧 Fix welcome tour typo 2018-07-24 08:29:22 -07:00
Felix Rieseberg 50bbb895a9 Merge pull request #39 from electron/more-tests
test: More tests for binary, fetch-types, and content
2018-07-24 07:58:50 -07:00
Felix Rieseberg 970f06274a 🔧 Whoops, show the tasks menu 2018-07-23 22:33:40 -07:00
Felix Rieseberg 8ea6446dca 🔬 Test fetch-types 2018-07-23 22:28:19 -07:00
Felix Rieseberg 37e61a9cda 🔬 Test content 2018-07-23 21:38:16 -07:00
Felix Rieseberg 6defceb8d5 🔬 More binary tests 2018-07-23 21:35:13 -07:00
Felix Rieseberg 4e7349c48b 🔧 Fix this error 2018-07-23 21:32:20 -07:00
Felix Rieseberg 296396473c 🔬 Test binary manager 2018-07-23 21:31:23 -07:00
Felix Rieseberg a54a9d631e 🔧 Use custom import method 2018-07-23 21:31:13 -07:00
Felix Rieseberg 2e7c9ba7e4 Merge pull request #38 from electron/exec-fix
fix: exec `toString` test issue
2018-07-23 13:24:12 -07:00
Felix Rieseberg f58058a7da 🔧 Fix exec toString issue 2018-07-22 21:28:43 -07:00
Felix Rieseberg d3f3c047e9 Merge pull request #37 from electron/tests
infra: Add unit tests
2018-07-22 20:59:58 -07:00
Felix Rieseberg 4c1ec07684 👷 No, for real, surpress 2018-07-22 14:26:52 -07:00
Felix Rieseberg d4ccc0d17f 👷 Surpress console log/warn when in test 2018-07-22 14:13:14 -07:00
Felix Rieseberg edacde350f 🔧 Run test in Travis 2018-07-22 07:57:36 +08:00
Felix Rieseberg 48b49f48d0 🔧 Ignore yarn-error.log 2018-07-22 07:57:04 +08:00
Felix Rieseberg f21a4c72f6 🔬 Test binary (part I) 2018-07-22 07:56:00 +08:00
Felix Rieseberg e2bb88d4c5 🔬 Test windows 2018-07-22 07:18:25 +08:00
Felix Rieseberg 98d848a443 👷 Improve infrastructure 2018-07-22 07:02:27 +08:00
Felix Rieseberg e9a368adde 🔬 Test files 2018-07-22 06:56:11 +08:00
Felix Rieseberg 1194554a32 🔬 Test dialogs 2018-07-22 06:23:37 +08:00
Felix Rieseberg 816337f39c 🔬 Test context-menu 2018-07-22 06:13:45 +08:00
Felix Rieseberg 20a12db00d 🔬 Test content 2018-07-22 05:13:26 +08:00
Felix Rieseberg 8037a2e418 🔬 Test sorted-electron-map 2018-07-22 05:10:47 +08:00
Felix Rieseberg aed4c40956 🔬 Test sort-buttons 2018-07-22 05:04:35 +08:00
Felix Rieseberg d20d29c481 🔬 Test position-for-rect 2018-07-22 05:00:33 +08:00
Felix Rieseberg fcaa6b7a59 🔬 Test normalize-version 2018-07-22 04:46:14 +08:00
Felix Rieseberg 08dd132a4f 🔬 Test gist 2018-07-22 01:53:39 +08:00
Felix Rieseberg 92417e488c 🔬 Test get-username 2018-07-22 01:48:43 +08:00
Felix Rieseberg abc60ea21f 🔬 Test get-title 2018-07-22 01:45:30 +08:00
Felix Rieseberg 4a3a6d50f5 🔬 Test get-package 2018-07-22 01:39:27 +08:00
Felix Rieseberg a9d70f730c 🔬 Test focused-editor 2018-07-22 01:28:11 +08:00
Felix Rieseberg 2d184f1d72 🔬 Test exec 2018-07-22 01:22:26 +08:00
Felix Rieseberg abc251c318 🔬 Test editor-layout 2018-07-22 00:36:43 +08:00
Felix Rieseberg 001f377e29 🔬 Test devmode 2018-07-21 23:39:15 +08:00
Felix Rieseberg fbad873941 🔬 Test classnames 2018-07-21 23:33:33 +08:00
Felix Rieseberg ac5d34fe92 🔬 Test array-to-stringmap 2018-07-21 23:28:22 +08:00
Felix Rieseberg 7b5ee87c00 📦 Test infrastructure 2018-07-21 23:27:16 +08:00
Felix Rieseberg 1baf6459f1 Merge pull request #36 from electron/tour
feat: A "Welcome to Electron Fiddle" Tour
2018-07-21 23:01:23 +08:00
Felix Rieseberg 9be0559595 🔧 Cleanup 2018-07-21 16:57:33 +08:00
Felix Rieseberg 3dae0bfb44 🚀 Tie it all together 2018-07-21 16:23:14 +08:00
Felix Rieseberg aabf2827c1 🔧 Allow tour forks 2018-07-21 15:39:34 +08:00
Felix Rieseberg 2f77d1f85f 🔧 Better positioning, better look 2018-07-21 15:26:43 +08:00
Felix Rieseberg 14e4f0cf32 🎨 Animate each dialog 2018-07-21 15:22:14 +08:00
Felix Rieseberg ee809369e1 🚀 The tour is taking real shape here 2018-07-20 19:48:08 +08:00
Felix Rieseberg 0026453984 🚀 The tour takes shape 2018-07-20 18:49:35 +08:00
Felix Rieseberg 8f727af18f 🔧 Somewhat fancy positioning tool 2018-07-20 18:49:19 +08:00
Felix Rieseberg de535f3ac5 🚀 Beginnings of a tour 2018-07-20 16:37:26 +08:00
Felix Rieseberg 5a89324bbd 🔧 Make <Dialog /> slightly more generic 2018-07-20 16:36:53 +08:00
Felix Rieseberg c9fd2bfb97 🔧 Fix linter issues 2018-07-20 16:00:52 +08:00
Felix Rieseberg c5f61a8351 🔧 Make appState accessible from the console 2018-07-20 15:59:39 +08:00
Felix Rieseberg 2d55803ba0 🔧 Use it for the tokenDialog 2018-07-20 15:59:19 +08:00
Felix Rieseberg 06f81f0d24 :werench: A generic dialog class 2018-07-20 15:58:49 +08:00
Felix Rieseberg 28c1bf65b6 Merge pull request #35 from electron/make
Add a "Make installers" command
2018-07-19 19:43:08 +08:00
Felix Rieseberg 23f1812e14 Merge pull request #34 from bpasero/bpasero-layout
Avoid lodash#throttle and just use timeouts
2018-07-19 19:39:45 +08:00
Felix Rieseberg f3a1e153cd feat: Allow for "electron-forge make" 2018-07-15 11:14:57 +01:00
Felix Rieseberg 9ca0df0f3e chore: Abstract output operations into state 2018-07-15 11:14:23 +01:00
Felix Rieseberg 1bfa6033d3 feat: Add IPC event for "make" 2018-07-15 11:13:57 +01:00
Benjamin Pasero 03945118ce Avoid lodash#throttle and just use timeouts
fixes https://github.com/electron/fiddle/issues/33
2018-07-13 17:50:52 +02:00
Felix Rieseberg 48f9beba4d Merge pull request #32 from bpasero/bpasero-zoom
Add zooming related actions to the view menu
2018-07-13 10:22:14 +01:00
Benjamin Pasero 02bf3e3837 Add zooming related actions to the view menu
This allows to zoom in, out and reset.
2018-07-13 07:19:04 +02:00
Felix Rieseberg 7bc6fa1ee0 Merge pull request #31 from electron/package
feat: Add a "Package" task
2018-07-12 18:35:13 +01:00
Felix Rieseberg fa8898ed10 feat: Use a fancy name generator 2018-07-12 18:13:03 +01:00
Felix Rieseberg d226f29219 feat: Have terminal always scroll to bottom 2018-07-12 16:53:22 +01:00
Felix Rieseberg df89ff581a feat: Output packaging info to fiddle terminal 2018-07-12 16:53:12 +01:00
Felix Rieseberg 1cf4759483 feat: Basic package command 2018-07-12 16:27:36 +01:00
Felix Rieseberg 9c061b63e2 feat: Abstract saving fiddle to temp folder 2018-07-12 15:59:15 +01:00
Felix Rieseberg 2b92a391fa Merge pull request #30 from electron/forge
feat: Save as Forge Project
2018-07-12 12:58:27 +01:00
Felix Rieseberg cd99cda65c 🔧 Show output item in explorer/finder 2018-07-10 11:09:47 +01:00
Felix Rieseberg 57c0dee597 🔧 Save it all as an actual Forge project 2018-07-10 11:05:45 +01:00
Felix Rieseberg ea3a3473e8 🔧 Add a dotfiles transform 2018-07-10 10:52:21 +01:00
Felix Rieseberg 9c29590c90 🔧 Add a Forge transform 2018-07-10 10:50:35 +01:00
Felix Rieseberg 64c12b7245 🔧 Beef up package.json generation 2018-07-10 10:50:20 +01:00
Felix Rieseberg b996446d1c Merge pull request #29 from electron/transforms
feat: File transforms
2018-07-09 20:11:25 +01:00
Felix Rieseberg 3a928e2f52 🔧 Add file transforms 2018-07-09 11:54:26 +01:00
Felix Rieseberg b328798c93 📝 Update Readme a bit 2018-07-07 08:31:16 +01:00
Felix Rieseberg 3087b57dff 🔧 Default import 2018-07-07 08:20:14 +01:00
Felix Rieseberg 06e46959f8 🔧 Fix /Users/felixr/.nvm/versions/node/v9.8.0/bin:/Users/felixr/Code/go/bin:/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/emsdk-portable:/Applications/emsdk-portable/clang/fastcomp/build_incoming_64/bin:/Applications/emsdk-portable/emscripten/incoming on macOS 2018-07-07 08:00:37 +01:00
Felix Rieseberg 67ed669a36 🔧 Fix npm installation 2018-07-06 20:03:55 -04:00
Felix Rieseberg 680c4b140f 📦 Update to alpha 2 2018-07-06 19:44:05 -04:00
Felix Rieseberg 3830828e6f 🔧 Only set noAsar after require 2018-07-06 19:43:59 -04:00
Felix Rieseberg a509763b1e 📦 Update Monaco to 0.13 2018-07-06 18:01:29 -04:00
Felix Rieseberg 4f62229013 🔧 Fix forge config 2018-07-06 12:58:15 -07:00
Felix Rieseberg 42cff3e318 🔧 Update Forge config 2018-07-06 12:45:42 -07:00
Felix Rieseberg 98e7e31b1a 🔧 Save publishing with bad wifi 2018-07-06 12:41:31 -07:00
Felix Rieseberg bede240aee 🔧 Fix preferences position on macOS 2018-07-06 10:47:25 -07:00
Felix Rieseberg ffe9c8fa52 🎨 Use custom milligram, fix CSS issues 2018-07-06 10:45:48 -07:00
Felix Rieseberg 3e3e9be30e 🎨 Include milligram in main css 2018-07-05 17:12:10 -07:00
Felix Rieseberg fa26ec6ffb 🔧 Update downloaded status post-update 2018-07-05 17:12:04 -07:00
Felix Rieseberg 068c4ad14f 🔧 Fancy context menu 2018-07-05 16:58:51 -07:00
Felix Rieseberg abac62c3d8 🔧 Basic context menu 2018-07-05 14:38:49 -07:00
Felix Rieseberg d137a3761f 🔧 Fix missing semicolons 2018-07-05 14:38:42 -07:00
Felix Rieseberg 6980d50192 🎨 Use pre-release FontAwesome 2018-07-05 14:09:06 -07:00
Felix Rieseberg 0c421f0b38 📦 Update Service 2018-07-05 12:03:17 -07:00
Felix Rieseberg 84043e453f 📝 Properly name it all 2018-07-05 11:59:38 -07:00
Felix Rieseberg 45e7cb533e 🚀 Refresh Electron versions on launch 2018-07-05 11:57:42 -07:00
Felix Rieseberg 8fe3875d9a 🔧 Fix FontAwesome import 2018-07-05 11:49:11 -07:00
Felix Rieseberg 3e27cbae84 👷 Improve build 2018-07-05 11:47:56 -07:00
Felix Rieseberg a6e6fad4d9 📦 Use ParcelJS to build 2018-07-05 11:39:45 -07:00
Felix Rieseberg 09928cdeb3 🔧 Lazy-require as much as possible 2018-07-02 19:10:30 -07:00
Felix Rieseberg 49a1d057f2 🔧 Production mode for React & co 2018-07-02 17:41:10 -07:00
Felix Rieseberg 3ee349d8f1 🔧 Entry animation 2018-07-02 17:24:35 -07:00
Felix Rieseberg 00cb6ecf73 🔧 Let editors load Monaco 2018-07-02 15:56:13 -07:00
Felix Rieseberg b6b5c0ac57 🔧 Fix token dialog 2018-07-02 13:58:28 -07:00
Felix Rieseberg 08751d2c61 🔧 Editor: Disable HTML context menu 2018-07-02 12:55:37 -07:00
Felix Rieseberg 0725048f64 🔧 Appease the linter 2018-07-02 12:54:54 -07:00
Felix Rieseberg dc18199790 🔧 Save dependencies to package.json 2018-07-02 12:53:51 -07:00
Felix Rieseberg ef85111944 🔧 Fix contributors script, harder 2018-06-29 12:18:39 -07:00
Felix Rieseberg 2cef76865c 🔧 Fix contributors script 2018-06-29 12:13:08 -07:00
Felix Rieseberg befa3192f4 🎨 Make it all pretty (again) 2018-06-29 12:10:39 -07:00
Felix Rieseberg c21b157469 Merge pull request #28 from electron/file-operations
feat: File operations
2018-06-25 11:23:54 -07:00
Felix Rieseberg 51e7c609da 🔧 Add url display for gists 2018-06-24 21:39:50 -07:00
Felix Rieseberg cc493283bd 🔧 Set window title 2018-06-24 21:39:50 -07:00
Felix Rieseberg 41203ecd17 🔧 Enable "Save to Gist" 2018-06-24 21:39:50 -07:00
Felix Rieseberg d9d5335e93 🔧 Enable "Save as..." 2018-06-24 21:39:50 -07:00
Felix Rieseberg e968219491 🔧 Basic file operations 2018-06-24 21:39:50 -07:00
Felix Rieseberg 5e973723c6 🔧 Add menu and IPC events 2018-06-24 21:39:50 -07:00
Felix Rieseberg 1f6c2e9e89 Merge pull request #27 from electron/darwin-icon
fix: Add macOS icon
2018-06-24 21:37:32 -07:00
Felix Rieseberg bd1f4e9a0d 🎨 Add macOS icon 2018-06-24 19:30:07 -07:00
Felix Rieseberg e829e3a17e Merge pull request #26 from electron/windows-fixes
Add basic File menu, improve Windows CSS
2018-06-24 19:25:34 -07:00
Felix Rieseberg 11bf84189f 🔧 Minor Windows fixes 2018-06-24 19:16:11 -07:00
Felix Rieseberg 12f979b25e Merge pull request #25 from electron/less
Move to less, improve dev tools
2018-06-24 18:57:51 -07:00
Felix Rieseberg 70ae2e1806 🔧 Duh 2018-06-24 18:49:48 -07:00
Felix Rieseberg 546eee8d63 👷 Fix tests 2018-06-24 18:48:03 -07:00
Felix Rieseberg b5ea4a869b 🔧 Slightly better watcher 2018-06-24 18:45:13 -07:00
Felix Rieseberg e95976006e ❤️ Feedback 2018-06-24 18:45:06 -07:00
Felix Rieseberg 97a37ab800 🔧 File watcher 2018-06-24 18:33:49 -07:00
Felix Rieseberg 67aa09e717 🔧 Cleanup 2018-06-24 18:33:42 -07:00
Felix Rieseberg d371e4ee9f 🔧 🎨 Better less, better tools 2018-06-24 18:11:21 -07:00
Felix Rieseberg 4ce070c24b 🔧 Don't check css in 2018-06-24 17:39:40 -07:00
Felix Rieseberg c1320453a0 📦 Install stylelint 2018-06-24 17:38:11 -07:00
Felix Rieseberg b20c31b2da 🎨 Move to less 2018-06-24 17:31:52 -07:00
Felix Rieseberg a789b3fa60 📦 Install less 2018-06-24 17:31:45 -07:00
Felix Rieseberg a814118a19 Merge pull request #23 from electron/protocol-and-windows-stuff
[WIP] Protocol Handler + Windows fixes
2018-06-24 16:51:55 -07:00
Samuel Attard cec54617be add basic electron-fiddle protocol handling 2018-06-24 16:51:28 -07:00
Samuel Attard ac7bf44a25 basic windows packaging config 2018-06-24 16:51:28 -07:00
Samuel Attard 5c98534cd8 fix terminal output rendering on windows 2018-06-24 16:51:01 -07:00
Felix Rieseberg e085e90a7a Merge pull request #24 from electron/cleanup-modules
Remove Blueprint from package.json
2018-06-15 17:47:35 +02:00
Felix Rieseberg ab62d3bc10 📦 Remove Blueprint 2018-06-15 16:01:55 +02:00
Felix Rieseberg 5fe535ca29 Merge pull request #22 from electron/fix-first-version-download
Fix first electron version install
2018-06-15 15:32:35 +02:00
Issac Gerges f3904b0226 Fix BinaryManager so first version download happens in a way AppState can be aware of, not as a side-effect of class construction 2018-06-14 14:57:37 +02:00
Felix Rieseberg 01ae77d64c Merge pull request #21 from electron/whitelist-and-credits-text
Whitelist for credits view, new copy
2018-06-14 13:00:49 +02:00
Felix Rieseberg a74b7ae6d8 🔧 Update Credits view 2018-06-14 12:04:59 +02:00
Felix Rieseberg ccdd8d7fc6 Merge pull request #20 from electron/settings
Preferences
2018-06-13 16:17:32 +02:00
Felix Rieseberg a8a7764d64 🎨 Some last cosmetics 2018-06-13 16:16:58 +02:00
Felix Rieseberg 94ea9fc432 🎨 Some final CSS adjustments 2018-06-13 15:56:03 +02:00
Felix Rieseberg ed66edc612 🎨 Wrap when required 2018-06-13 15:52:14 +02:00
Felix Rieseberg a5c6695c4e 🔧 Insert "Preferences" in correct place 2018-06-13 15:50:22 +02:00
Felix Rieseberg 943d8f314f 🔧 A credits pane 2018-06-13 15:44:49 +02:00
Felix Rieseberg 04c65f0d60 👷 Fetch contributors during build 2018-06-13 15:43:59 +02:00
Felix Rieseberg 7d8983eaea 🔧 Wire up advanced Electron Settings 2018-06-13 12:34:41 +02:00
Felix Rieseberg fd812f9d0b 🚀 Add GitHub settings 2018-06-12 20:57:58 +02:00
Felix Rieseberg e45a25970e 🚀 Add Electron settings 2018-06-12 20:31:42 +02:00
Felix Rieseberg f3e9faa64e 🔧 State: New method: remove version 2018-06-12 20:10:20 +02:00
Felix Rieseberg 516c7e6816 🔧 New binary method: remove 2018-06-12 20:10:06 +02:00
Felix Rieseberg c80bfcc54f 🔧 Add utility: Sorted Electron map 2018-06-12 20:09:53 +02:00
Felix Rieseberg 48fd3960b1 🔧 Basic settings pane 2018-06-12 19:08:26 +02:00
Felix Rieseberg 2691486e73 📝 Document the token dialog class 2018-06-12 15:30:36 +02:00
Felix Rieseberg e66116cdb4 🔧 Store more GitHub information 2018-06-12 15:26:38 +02:00
Felix Rieseberg 3d1450f35a Merge pull request #19 from electron/graceful-opening
Graceful opening, central IPC, cleaner menu
2018-06-12 15:19:53 +02:00
Felix Rieseberg 1d010be01f 🔧 Move files around, add menu, add graceful opening 2018-06-12 15:17:10 +02:00
Felix Rieseberg 1214963212 🔧 Make a central place for IPC calls 2018-06-12 15:16:51 +02:00
Felix Rieseberg e4f8c3d434 🔧 Make sure dialogs don’t eat clicks 2018-06-12 15:16:09 +02:00
Felix Rieseberg 5ea418f18e Merge pull request #18 from electron/publish-improvements
Improve login/publish flow
2018-06-12 11:25:29 +02:00
Felix Rieseberg aad2b26de8 🔧 Make it all reactive 2018-06-11 23:15:58 +02:00
Felix Rieseberg 00d1b3c56d 🎨 Rework token dialog 2018-06-11 22:50:59 +02:00
Felix Rieseberg 74ea847327 🎨 Fix modal button color 2018-06-11 21:27:36 +02:00
Felix Rieseberg e53b40ca08 🔧 Cut login button, always use publish button 2018-06-11 21:19:35 +02:00
Felix Rieseberg bc79c66526 Merge pull request #17 from electron/windows-improvements
Allow app to start on Windows
2018-06-10 19:03:41 +02:00
Felix Rieseberg 1a1f01f639 🔧 Allow app to run on Windows 2018-06-10 18:34:47 +02:00
Felix Rieseberg a89d5ef91c Merge pull request #16 from electron/docs
Docs: Code of conduct, security
2018-06-10 14:34:54 +02:00
Felix Rieseberg 6aee1b1d68 📝 Various default files 2018-06-10 14:33:02 +02:00
Felix Rieseberg 055d6cc548 🔧 Fix handler context 2018-06-09 17:53:30 +02:00
Felix Rieseberg 9b86273aad Merge pull request #15 from electron/code-cleanup
Documentation, minor code cleanup
2018-06-09 17:52:19 +02:00
Felix Rieseberg 5cdebdd86a 💂‍♂️ Cleanup code, document it 2018-06-09 17:20:23 +02:00
Felix Rieseberg 689ef5d926 Merge pull request #14 from electron/60fps
60fps
2018-06-09 11:04:34 +02:00
Felix Rieseberg 8241c29521 🚀 60fps 2018-06-09 09:35:15 +02:00
Felix Rieseberg 2bebba5ff6 Merge pull request #13 from electron/window-management
Improved window management
2018-06-09 08:23:56 +02:00
Felix Rieseberg 7d8b7c6ed3 🎨 Fix console scrollbar 2018-06-08 17:23:55 +02:00
Felix Rieseberg 38dc15c1ab 🎨 Remove controls 2018-06-08 17:17:50 +02:00
Felix Rieseberg 9d65cc1083 🚀 First version of window management 2018-06-08 17:13:32 +02:00
Felix Rieseberg 260ada1ced Merge pull request #12 from electron/so-many-changes
Address bar, classnames, optics
2018-06-08 14:23:38 +02:00
Felix Rieseberg a04030904f 🚀 Make an address bar 2018-06-08 14:12:58 +02:00
Felix Rieseberg d5143a9224 📦 Add classnames 2018-06-08 14:12:45 +02:00
Felix Rieseberg 60df3aecc4 🔧 Add gist utilities 2018-06-08 14:12:28 +02:00
Felix Rieseberg 7016affadf 🔧 Update .gitignore 2018-06-08 14:11:42 +02:00
Felix Rieseberg d32f9af011 Merge pull request #11 from electron/more-gist
feat: token validation and basic publish/load gist logic
2018-06-08 09:05:28 +02:00
Samuel Attard bbbe7266af feat: token validation and basic publish/load gist logic 2018-06-08 01:17:49 +02:00
Felix Rieseberg 67b2819a85 Merge pull request #10 from electron/default-menu
Default menu, resizable editors
2018-06-07 18:03:01 +02:00
Felix Rieseberg 851ca5b390 🔧 Make editors resizable 2018-06-07 18:01:11 +02:00
Felix Rieseberg a28b9d34da 🔧 Add an app menu 2018-06-07 18:01:01 +02:00
Felix Rieseberg faf9c96fe2 Merge pull request #9 from electron/gist-token-auth
Rough draft: Accept a GitHub Gist token
2018-06-07 16:56:05 +02:00
Charlie Hess 344382a18d I'm real bad at CSS. 2018-06-07 15:28:33 +02:00
Charlie Hess 1865f66e29 Make it kinda work. 2018-06-07 15:28:27 +02:00
Charlie Hess fd458adc5e Fix an unrelated "child elements need key" error. 2018-06-07 15:28:03 +02:00
Felix Rieseberg 57b00c8126 Merge pull request #8 from electron/active-button
Fix color for active button
2018-06-07 14:18:36 +02:00
Felix Rieseberg dcbc9eff88 Merge pull request #7 from electron/type-definitions
Add TypeScript definitions for Electron
2018-06-07 14:17:43 +02:00
Felix Rieseberg b52ee70dde Merge pull request #6 from electron/better-tsc
build: use better tsc in build phase
2018-06-07 14:17:34 +02:00
Felix Rieseberg 316077e3b7 🎨 Fix color for active button 2018-06-07 14:17:04 +02:00
Felix Rieseberg 80993f2cac 🔧 Add TypeScript definitions for Electron 2018-06-07 14:09:23 +02:00
Samuel Attard 59ac41b369 build: use better tsc in build phase 2018-06-07 14:07:40 +02:00
Charlie Hess 6dffdef8f0 Add an auth button in the header. 2018-06-07 14:01:03 +02:00
380 changed files with 42576 additions and 3764 deletions
+1
View File
@@ -0,0 +1 @@
* text=auto
+40 -3
View File
@@ -1,3 +1,40 @@
node_modules
out
dist
.DS_Store
.env
.gclient_done
**/.npmrc
.tags*
.vs/
.vscode/
*.log
*.pyc
*.sln
*.swp
*.VC.db
*.VC.VC.opendb
*.vcxproj
*.vcxproj.filters
*.vcxproj.user
*.xcodeproj
/.idea/
/dist/
/external_binaries/
/out/
node_modules/
SHASUMS256.txt
**/yarn.lock
compile_commands.json
.envrc
.cache
yarn-error.log
.DS_Store
# Coverage
coverage
# npm package
/npm/dist
/npm/path.txt
# Electron Fiddle-Specific
/static/contributors.json
/static/css/root.css
+7
View File
@@ -0,0 +1,7 @@
{
"extends": "stylelint-config-standard",
"ignoreFiles": [
"./src/less/*-vendor.less"
],
"rules": { }
}
+35 -4
View File
@@ -1,10 +1,10 @@
language: node_js
node_js: "8"
node_js: "12"
os:
- linux
- osx
dist: trusty
osx_image: xcode8.3
osx_image: xcode10
sudo: false
cache:
@@ -25,9 +25,40 @@ branches:
install:
- npm install
- npm update
- |
if [[ "$TRAVIS_OS_NAME" == "osx" && "$TRAVIS_SECURE_ENV_VARS" == "true" ]]; then
export CERTIFICATE_P12=cert.p12;
echo $MACOS_CERT_P12 | base64 --decode > $CERTIFICATE_P12;
export KEYCHAIN=build.keychain;
# Create the keychain with a password
security create-keychain -p travis $KEYCHAIN;
# Make the custom keychain default, so xcodebuild will use it for signing
security default-keychain -s $KEYCHAIN;
# Unlock the keychain
security unlock-keychain -p travis $KEYCHAIN;
# Add certificates to keychain and allow codesign to access them
# Apple Worldwide Developer Relations Certification Authority
security import ./tools/certs/apple.cer -k ~/Library/Keychains/$KEYCHAIN -T /usr/bin/codesign
# Developer Authentication Certification Authority
security import ./tools/certs/dac.cer -k ~/Library/Keychains/$KEYCHAIN -T /usr/bin/codesign
# Developer ID Felix
security import $CERTIFICATE_P12 -k $KEYCHAIN -P $MACOS_CERT_PASSWORD -T /usr/bin/codesign 2>&1 >/dev/null;
rm $CERTIFICATE_P12;
security set-key-partition-list -S apple-tool:,apple: -s -k travis $KEYCHAIN
# Echo the identity
security find-identity -v -p codesigning
fi
script:
- npm run lint
- npm run test:ci
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then npm run test:coverage; fi
- if test -z "$TRAVIS_TAG"; then npm run make; fi
after_success: if test -n "$TRAVIS_TAG"; then npm run publish; fi
after_success: if test -n "$TRAVIS_TAG"; then npm run publish; fi
+46
View File
@@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct:
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [coc@electronjs.org](mailto:coc@electronjs.org). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor-Covenant][homepage], version 1.4, available at [https://contributor-covenant.org/version/1/4][version]
[homepage]: https://contributor-covenant.org
[version]: https://contributor-covenant.org/version/1/4/
+66 -3
View File
@@ -1,5 +1,68 @@
# Electron Fiddle
# <img src="https://user-images.githubusercontent.com/378023/49785546-4b7f7000-fd64-11e8-8033-a52c73a07fbf.png" width="60px" align="center" alt="Electron Fiddle icon"> Electron Fiddle
> Still a work in progress, please stay tuned.
[![Build Status](https://travis-ci.org/electron/fiddle.svg?branch=master)](https://travis-ci.org/electron/fiddle) [![Coverage Status](https://coveralls.io/repos/github/electron/fiddle/badge.svg?branch=master)](https://coveralls.io/github/electron/fiddle?branch=master)
![](https://user-images.githubusercontent.com/1426799/41096018-8499e31a-6a53-11e8-9887-7483fd38e58b.png)
Electron Fiddle lets you create and play with small Electron experiments. It
greets you with a quick-start template after opening change a few things,
choose the version of Electron you want to run it with, and play around. Then,
save your Fiddle either as a GitHub Gist or to a local folder. Once published
on GitHub, anyone can quickly try your Fiddle out by just entering it in the
address bar.
<h3 align="center">
<a href="https://github.com/electron/fiddle/releases/latest">
Download Electron Fiddle for macOS, Windows, and Linux
</a>
</h3>
<img src="https://user-images.githubusercontent.com/1426799/52155868-d3357c80-2639-11e9-9496-fa97b1dc7897.jpg" width="880px" alt="Electron Fiddle screenshot">
# Features
### Explore Electron
![Screenshot: Electron App running](https://user-images.githubusercontent.com/1426799/52155856-c0bb4300-2639-11e9-9962-a6354d08dc5a.jpg)
Try Electron without installing any dependencies: Fiddle includes everything
you'll need to explore the platform. It also includes examples for every API
available in Electron, so if you want to quickly see what a
[BrowserView][BrowserView] is or how the [desktopCapturer][desktopCapturer]
works, Fiddle has got you covered.
### Code with Types
![Screenshot: Fiddle's Types](https://user-images.githubusercontent.com/1426799/43874324-10e46eae-9b40-11e8-962b-8c793d73c259.png)
Fiddle includes Microsoft's excellent Monaco Editor, the same editor powering
Visual Studio Code. It also installs the type definitions for the currently
selected version of Electron automatically, ensuring that you always have
all Electron APIs only a few keystrokes away.
### Compile and Package
![Screenshot: Fiddle's Tasks Menu](https://user-images.githubusercontent.com/1426799/52155857-c0bb4300-2639-11e9-8776-e05dc528439c.png)
Fiddle can automatically turn your experiment into binaries you can share with
your friends, coworkers, or grandparents. It does so thanks to
[electron-forge][electron-forge], allowing you to package your fiddle as an
app for Windows, macOS, or Linux.
### Start with Fiddle, Continue Wherever
![Screenshot: Visual Studio Code with Fiddle Export](https://user-images.githubusercontent.com/1426799/43874411-9cfd5946-9b40-11e8-8797-dd4138e31933.png)
Fiddle is not an IDE  it is however an excellent starting point. Once your
fiddle has grown up, export it as a project with or without
[electron-forge][electron-forge]. Then, use your favorite editor and take on
the world!
## License
[MIT, please see the LICENSE file for full details](https://github.com/electron/fiddle/blob/master/LICENSE.md).
When using the Electron or other GitHub logos, be sure to follow the [GitHub
logo guidelines](https://github.com/logos).
[BrowserView]: https://electronjs.org/docs/api/browser-view
[desktopCapturer]: https://electronjs.org/docs/api/desktop-capturer
[electron-forge]: https://electronforge.io/
+12
View File
@@ -0,0 +1,12 @@
# Reporting Security Issues
The Electron team and community take security bugs in Electron seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
To report a security issue, email [security@electronjs.org](mailto:security@electronjs.org) and include the word "SECURITY" in the subject line.
The Electron team will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.
Report security bugs in third-party modules to the person or team maintaining the module. You can also report a vulnerability through the [Node Security Project](https://nodesecurity.io/report).
## Learning More About Security
To learn more about securing an Electron application, please see the [security tutorial](https://github.com/electron/electron/blob/master/docs/tutorial/security.md).
+37
View File
@@ -0,0 +1,37 @@
environment:
matrix:
- nodejs_version: "12"
init:
- git config --global core.symlinks true
install:
# Setup the code signing certificate
- ps: >-
if (Test-Path Env:\WINDOWS_CERTIFICATE_P12) {
$workingDirectory = Convert-Path (Resolve-Path -path ".")
$filename = "$workingDirectory\cert.p12"
$bytes = [Convert]::FromBase64String($env:WINDOWS_CERTIFICATE_P12)
[IO.File]::WriteAllBytes($filename, $bytes)
}
- ps: Install-Product node $env:nodejs_version x64
- node --version
- npm ci
cache:
- '%APPDATA%\npm-cache -> appveyor.yml'
test_script:
- node --version
- npm --version
- npm run lint
- npm run test
artifacts:
- path: 'out\make\squirrel.windows\**\*.exe'
build_script:
- if %APPVEYOR_REPO_TAG% EQU false npm run make
- if %APPVEYOR_REPO_TAG% EQU true npm run publish
- if %APPVEYOR_REPO_TAG% EQU true npm run publish -- --arch=ia32
- ps: Tree ./out/make /F
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1024px" height="1024px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="SVG" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Icon">
<circle id="BG" stroke="#FFFFFF" stroke-width="36" fill="#E79537" cx="512" cy="512" r="430"></circle>
<g id="Glyph" transform="translate(336.000000, 280.000000)">
<path d="M92,335.02442 L92,44 C92,30.745166 102.745166,20 116,20 L356,20 C369.254834,20 380,30.745166 380,44 C380,57.254834 369.254834,68 356,68 L140,68 L140,188 L239.428571,188 C250.789858,188 260,198.745166 260,212 C260,225.254834 250.789858,236 239.428571,236 L140,236 L140,335.02442 C181.405453,345.681447 212,383.267854 212,428 C212,481.019336 169.019336,524 116,524 C62.980664,524 20,481.019336 20,428 C20,383.267854 50.5945469,345.681447 92,335.02442 Z" id="Shadow" fill="#000000" fill-rule="nonzero" opacity="0.0980980283"></path>
<path d="M72,315.02442 L72,24 C72,10.745166 82.745166,0 96,0 L336,0 C349.254834,0 360,10.745166 360,24 C360,37.254834 349.254834,48 336,48 L120,48 L120,168 L219.428571,168 C230.789858,168 240,178.745166 240,192 C240,205.254834 230.789858,216 219.428571,216 L120,216 L120,315.02442 C161.405453,325.681447 192,363.267854 192,408 C192,461.019336 149.019336,504 96,504 C42.980664,504 0,461.019336 0,408 C0,363.267854 30.5945469,325.681447 72,315.02442 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
<path d="M96,456 C122.509668,456 144,434.509668 144,408 C144,381.490332 122.509668,360 96,360 C69.490332,360 48,381.490332 48,408 C48,434.509668 69.490332,456 96,456 Z" id="Dot" fill="#EB5E00"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

+106 -14
View File
@@ -1,26 +1,89 @@
/* tslint:disable */
module.exports = {
packagerConfig: {},
const path = require('path')
const fs = require('fs')
const packageJson = require('./package.json')
const { version } = packageJson
const iconDir = path.resolve(__dirname, 'assets', 'icons')
const config = {
hooks: {
generateAssets: require('./tools/generateAssets')
},
packagerConfig: {
name: 'Electron Fiddle',
executableName: 'electron-fiddle',
asar: true,
icon: path.resolve(__dirname, 'assets', 'icons', 'fiddle'),
// TODO: FIXME?
// ignore: [
// /^\/\.vscode\//,
// /^\/tools\//
// ],
appBundleId: 'com.electron.fiddle',
appCategoryType: 'public.app-category.developer-tools',
protocols: [{
name: 'Electron Fiddle Launch Protocol',
schemes: ['electron-fiddle']
}],
win32metadata: {
CompanyName: 'Electron Community',
OriginalFilename: 'Electron Fiddle',
},
osxSign: {
identity: 'Developer ID Application: Felix Rieseberg (LT94ZKYDCJ)',
'hardened-runtime': true,
'gatekeeper-assess': false,
'entitlements': 'static/entitlements.plist',
'entitlements-inherit': 'static/entitlements.plist',
'signature-flags': 'library'
}
},
makers: [
{
name: "@electron-forge/maker-squirrel",
platforms: ["win32"],
config: {
name: "electron_fiddle"
name: '@electron-forge/maker-squirrel',
platforms: ['win32'],
config: (arch) => {
const certificateFile = process.env.CI
? path.join(__dirname, 'cert.p12')
: process.env.WINDOWS_CERTIFICATE_FILE;
if (!certificateFile || !fs.existsSync(certificateFile)) {
console.warn(`Warning: Could not find certificate file at ${certificateFile}`)
}
return {
name: 'electron-fiddle',
authors: 'Electron Community',
exe: 'electron-fiddle.exe',
iconUrl: 'https://raw.githubusercontent.com/electron/fiddle/0119f0ce697f5ff7dec4fe51f17620c78cfd488b/assets/icons/fiddle.ico',
loadingGif: './assets/loading.gif',
noMsi: true,
remoteReleases: '',
setupExe: `electron-fiddle-${version}-${arch}-setup.exe`,
setupIcon: path.resolve(iconDir, 'fiddle.ico'),
certificatePassword: process.env.WINDOWS_CERTIFICATE_PASSWORD,
certificateFile
}
}
},
{
name: "@electron-forge/maker-zip",
platforms: ["darwin"]
name: '@electron-forge/maker-zip',
platforms: ['darwin']
},
{
name: "@electron-forge/maker-deb",
platforms: ["linux"]
name: '@electron-forge/maker-deb',
platforms: ['linux'],
config: {
icon: {
scalable: path.resolve(iconDir, 'fiddle.svg')
}
}
},
{
name: "@electron-forge/maker-rpm",
platforms: ["linux"]
name: '@electron-forge/maker-rpm',
platforms: ['linux']
}
],
publishers: [
@@ -31,8 +94,37 @@ module.exports = {
owner: 'electron',
name: 'fiddle'
},
prerelease: true
draft: true,
prerelease: false
}
}
]
};
}
function notarizeMaybe() {
if (process.platform !== 'darwin') {
return;
}
if (!process.env.CI) {
console.log(`Not in CI, skipping notarization`);
return;
}
if (!process.env.APPLE_ID || !process.env.APPLE_ID_PASSWORD) {
console.warn('Should be notarizing, but environment variables APPLE_ID or APPLE_ID_PASSWORD are missing!');
return;
}
config.packagerConfig.osxNotarize = {
appBundleId: 'com.electron.fiddle',
appleId: process.env.APPLE_ID,
appleIdPassword: process.env.APPLE_ID_PASSWORD,
ascProvider: 'LT94ZKYDCJ'
}
}
notarizeMaybe()
// Finally, export it
module.exports = config
+40
View File
@@ -0,0 +1,40 @@
{
"transform": {
".(js|jsx|ts|tsx)": "ts-jest"
},
"testURL": "http://localhost",
"testRegex": "(-spec)\\.(ts|tsx)$",
"bail": true,
"resetMocks": true,
"resetModules": true,
"setupFilesAfterEnv": [
"<rootDir>/tests/setup.js"
],
"moduleFileExtensions": [
"js",
"jsx",
"json",
"ts",
"tsx"
],
"testPathIgnorePatterns": [
"/.tmp/"
],
"coverageReporters": [
"json",
"html",
"lcov"
],
"collectCoverageFrom": [
"src/**/*.{ts,tsx}",
"!**/*.d.ts",
"!**/*constants.ts"
],
"coveragePathIgnorePatterns": [
"/node_modules/",
"/tests/.*\\.(ts|js)$"
],
"moduleNameMapper": {
".*releases.json$": "<rootDir>/static/releases.json"
}
}
+15600 -2398
View File
File diff suppressed because it is too large Load Diff
+90 -47
View File
@@ -1,65 +1,108 @@
{
"name": "electron-fiddle",
"productName": "electron-fiddle",
"version": "1.0.0",
"description": "My Electron application description",
"productName": "Electron Fiddle",
"version": "0.11.1",
"description": "The easiest way to get started with Electron",
"repository": "https://github.com/electron/fiddle",
"main": "./dist/main",
"main": "./dist/src/main/main",
"scripts": {
"build": "tsc -p tsconfig.json",
"start": "npm run build && electron-forge start",
"package": "electron-forge package",
"contributors": "node ./tools/contributors.js",
"less": "node ./tools/lessc.js",
"lint:style": "stylelint ./src/less/*.less --fix",
"lint:ts": "tslint -c tslint.json -p tsconfig.json -e \"node_modules/**/*.ts\" --fix",
"lint:tests": "tslint ./tests/**/*.ts{,x} -c tslint.json --fix",
"lint:templates": "standard ./static/show-me/**/*.js",
"lint": "npm-run-all lint:*",
"make": "electron-forge make",
"package": "electron-forge package",
"parcel:build": "node ./tools/parcel-build.js",
"parcel:watch": "node ./tools/parcel-watch.js",
"publish": "electron-forge publish",
"lint": "tslint -c tslint.json -p tsconfig.json -e \"node_modules/**/*.ts\""
"start": "rimraf ./dist && electron-forge start",
"test": "jest --config=jest.json --coverage",
"test:ci": "jest --config=jest.json --coverage --runInBand",
"test:coverage": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
"tsc": "node ./tools/tsc.js",
"electron-releases": "node ./tools/fetch-releases.js"
},
"keywords": [],
"author": "felixr",
"keywords": [
"Electron",
"Fiddle",
"Tutorial"
],
"author": "Felix Rieseberg <felix@felixrieseberg.com>",
"license": "MIT",
"config": {
"forge": "./forge.config.js"
},
"dependencies": {
"@fortawesome/fontawesome": "^1.1.8",
"@fortawesome/fontawesome-free-solid": "^5.0.13",
"@fortawesome/react-fontawesome": "0.0.20",
"builtin-modules": "^3.0.0",
"electron-download": "^4.1.0",
"@blueprintjs/core": "^3.19.1",
"@blueprintjs/select": "^3.11.1",
"@octokit/rest": "^16.34.0",
"builtin-modules": "^3.1.0",
"classnames": "^2.2.6",
"electron-default-menu": "^1.0.1",
"electron-devtools-installer": "^2.2.4",
"electron-download": "^4.1.1",
"electron-squirrel-startup": "^1.0.0",
"extract-zip": "^1.6.7",
"fs-extra": "^6.0.1",
"loader-utils": "^1.1.0",
"milligram": "^1.3.0",
"mobx": "^4.3.1",
"mobx-react": "^5.2.0",
"monaco-loader": "^0.8.2",
"react": "^16.4.0",
"react-dom": "^16.4.0",
"semver": "^5.5.0",
"tmp": "0.0.33",
"tslib": "^1.9.2",
"update-electron-app": "^1.2.0"
"fix-path": "^2.1.0",
"fs-extra": "^8.1.0",
"mobx": "^5.14.2",
"mobx-react": "^6.1.4",
"monaco-editor": "^0.18.1",
"monaco-loader": "1.0.0",
"namor": "^1.1.3",
"react": "^16.11.0",
"react-dom": "^16.11.0",
"react-mosaic-component": "^3.2.0",
"semver": "^6.3.0",
"tmp": "0.1.0",
"tslib": "^1.10.0",
"update-electron-app": "^1.5.0"
},
"devDependencies": {
"@electron-forge/cli": "^6.0.0-beta.17",
"@electron-forge/maker-deb": "^6.0.0-beta.17",
"@electron-forge/maker-rpm": "^6.0.0-beta.17",
"@electron-forge/maker-squirrel": "^6.0.0-beta.17",
"@electron-forge/maker-zip": "^6.0.0-beta.17",
"@types/builtin-modules": "^2.0.0",
"@types/fs-extra": "^5.0.2",
"@types/node": "^10.3.1",
"@types/react": "^16.3.16",
"@types/react-dom": "^16.0.6",
"@types/semver": "^5.5.0",
"@types/tmp": "0.0.33",
"css-loader": "^0.28.11",
"electron": "2.0.2",
"monaco-editor": "^0.13.1",
"style-loader": "^0.21.0",
"tslint": "^5.10.0",
"tslint-microsoft-contrib": "^5.0.3",
"tslint-react": "^3.6.0",
"typescript": "^2.9.1"
"@babel/core": "^7.6.4",
"@electron-forge/cli": "^6.0.0-beta.46",
"@electron-forge/maker-deb": "^6.0.0-beta.46",
"@electron-forge/maker-rpm": "^6.0.0-beta.46",
"@electron-forge/maker-squirrel": "^6.0.0-beta.46",
"@electron-forge/maker-zip": "^6.0.0-beta.46",
"@electron-forge/publisher-github": "^6.0.0-beta.46",
"@types/builtin-modules": "^3.1.1",
"@types/classnames": "^2.2.9",
"@types/enzyme": "^3.10.3",
"@types/fs-extra": "^8.0.1",
"@types/jest": "^24.0.19",
"@types/log-symbols": "^3.0.0",
"@types/node": "^12.11.7",
"@types/react": "^16.9.11",
"@types/react-dom": "^16.9.3",
"@types/semver": "^6.0.2",
"@types/tmp": "0.1.0",
"chokidar": "^3.2.2",
"coveralls": "^3.0.7",
"electron": "7.1.3",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.15.1",
"enzyme-to-json": "^3.4.3",
"jest": "^24.9.0",
"jest-fetch-mock": "^2.1.2",
"less": "^3.10.3",
"log-symbols": "^3.0.0",
"node-abi": "^2.12.0",
"node-fetch": "^2.6.0",
"npm-run-all": "^4.1.5",
"parcel-bundler": "^1.12.4",
"react-test-renderer": "^16.11.0",
"rimraf": "^3.0.0",
"standard": "^14.3.1",
"stylelint": "^11.1.1",
"stylelint-config-standard": "^19.0.0",
"ts-jest": "^24.1.0",
"tslint": "^5.20.0",
"tslint-microsoft-contrib": "^6.2.0",
"tslint-react": "^4.1.0",
"typescript": "^3.6.4"
}
}
+2
View File
@@ -0,0 +1,2 @@
declare module 'fix-path';
declare module 'namor';
+15
View File
@@ -0,0 +1,15 @@
import * as MonacoType from 'monaco-editor';
// Type-only-import
import { App as AppType } from './renderer/app';
import { EditorId } from './interfaces';
declare global {
interface Window {
ElectronFiddle: {
app: AppType;
contentChangeListeners: Array<any>,
editors: Record<EditorId, MonacoType.editor.IStandaloneCodeEditor | null>;
};
}
}
+3 -2
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html>
export const html = `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
@@ -16,4 +16,5 @@
require('./renderer.js')
</script>
</body>
</html>
</html>`;
+25 -29
View File
@@ -1,60 +1,56 @@
require('update-electron-app')();
const { app, BrowserWindow } = require('electron');
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) { // eslint-disable-line global-require
app.quit();
}
export const main = `// Modules to control application life and create native browser window
const {app, BrowserWindow} = require('electron')
const path = require('path')
const url = require('url')
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;
let mainWindow
const createWindow = () => {
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 1200,
height: 900,
titleBarStyle: 'hiddenInset',
acceptFirstMouse: true
});
mainWindow = new BrowserWindow({width: 800, height: 600})
// and load the index.html of the app.
mainWindow.loadFile('./static/index.html');
mainWindow.loadURL(url.format({
protocol: 'file',
slashes: true,
pathname: path.resolve(app.getAppPath(), 'index.html')
}))
// Open the DevTools.
// mainWindow.webContents.openDevTools();
// mainWindow.webContents.openDevTools()
// Emitted when the window is closed.
mainWindow.on('closed', () => {
mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null;
});
};
mainWindow = null
})
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);
app.on('ready', createWindow)
// Quit when all windows are closed.
app.on('window-all-closed', () => {
app.on('window-all-closed', function () {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit();
app.quit()
}
});
})
app.on('activate', () => {
app.on('activate', function () {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) {
createWindow();
createWindow()
}
});
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and import them here.
// code. You can also put them in separate files and require them here.`;
+9 -3
View File
@@ -1,4 +1,4 @@
// Modules to control application life and create native browser window
export const main = `// Modules to control application life and create native browser window
const {app, BrowserWindow} = require('electron')
// Keep a global reference of the window object, if you don't, the window will
@@ -7,7 +7,13 @@ let mainWindow
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({width: 800, height: 600})
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
@@ -47,4 +53,4 @@ app.on('activate', function () {
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
// code. You can also put them in separate files and require them here.`;
+3
View File
@@ -0,0 +1,3 @@
export const renderer = `// This file is required by the index.html file and will
// be executed in the renderer process for that window.
// All of the Node.js APIs are available in this process.`;
+77 -17
View File
@@ -1,27 +1,87 @@
export interface StringMap<T> {
[x: string]: T;
export type WindowNames = 'main';
export type Files = Map<string, string>;
export type FileTransform = (files: Files) => Promise<Files>;
export enum ElectronVersionState {
ready = 'ready',
downloading = 'downloading',
unknown = 'unknown'
}
export type ElectronVersionState = 'downloading' | 'ready' | 'unknown';
export interface GitHubVersion {
url: string;
assets_url: string;
html_url: string;
tag_name: string;
target_commitish: string;
name: string;
prerelease: boolean;
created_at: string;
published_at: string;
body: string;
export enum ElectronVersionSource {
remote = 'remote',
local = 'local'
}
export interface NpmVersion {
version: string;
name?: string;
localPath?: string;
}
export interface ElectronVersion extends GitHubVersion {
export interface EditorValues {
main: string;
renderer: string;
html: string;
preload: string;
package?: string;
}
export interface ElectronVersion extends NpmVersion {
state: ElectronVersionState;
source: ElectronVersionSource;
}
export interface SetFiddleOptions {
filePath?: string;
templateName?: string;
gistId?: string;
}
export interface OutputEntry {
text: string;
timestamp: number;
}
isNotPre?: boolean;
}
export interface OutputOptions {
bypassBuffer?: boolean;
isNotPre?: boolean;
}
export interface WarningDialogTexts {
ok?: string;
cancel?: string;
label: string;
}
export interface Templates {
[index: string]: string | Templates;
}
// Editors
export const enum EditorId {
'main' = 'main',
'renderer' = 'renderer',
'html' = 'html',
'preload' = 'preload'
}
// Panels that can show up as a mosaic
export const enum PanelId {
'docsDemo' = 'docsDemo'
}
export type MosaicId = EditorId | PanelId;
export const ALL_EDITORS = [ EditorId.main, EditorId.renderer, EditorId.preload, EditorId.html ];
export const ALL_PANELS = [ PanelId.docsDemo ];
export const ALL_MOSAICS = [ ...ALL_EDITORS, ...ALL_PANELS ];
export type ArrowPosition = 'top' | 'left' | 'bottom' | 'right';
export const enum DocsDemoPage {
DEFAULT = 'DEFAULT',
DEMO_APP = 'DEMO_APP'
}
+58
View File
@@ -0,0 +1,58 @@
export enum IpcEvents {
OPEN_SETTINGS = 'OPEN_SETTINGS',
LOAD_GIST_REQUEST = 'LOAD_GIST_REQUEST',
LOAD_ELECTRON_EXAMPLE_REQUEST = 'LOAD_ELECTRON_EXAMPLE_REQUEST',
FIDDLE_RUN = 'FIDDLE_RUN',
FIDDLE_PACKAGE = 'FIDDLE_PACKAGE',
FIDDLE_MAKE = 'FIDDLE_MAKE',
MONACO_EXECUTE_COMMAND = 'MONACO_EXECUTE_COMMAND',
MONACO_TOGGLE_OPTION = 'MONACO_TOGGLE_OPTION',
FS_NEW_FIDDLE = 'FS_NEW_FIDDLE',
FS_OPEN_FIDDLE = 'FS_OPEN_FIDDLE',
FS_OPEN_TEMPLATE = 'FS_OPEN_TEMPLATE',
FS_SAVE_FIDDLE = 'FS_SAVE_FIDDLE',
FS_SAVE_FIDDLE_AS = 'FS_SAVE_FIDDLE_AS',
FS_SAVE_FIDDLE_GIST = 'FS_SAVE_FIDDLE_GIST',
FS_SAVE_FIDDLE_FORGE = 'FS_SAVE_FIDDLE_FORGE',
FS_SAVE_FIDDLE_DIALOG = 'FS_SAVE_FIDDLE_DIALOG',
FS_SAVE_FIDDLE_ERROR = 'FS_SAVE_FIDDLE_ERROR',
SHOW_WARNING_DIALOG = 'SHOW_WARNING_DIALOG',
SHOW_CONFIRMATION_DIALOG = 'SHOW_CONFIRMATION_DIALOG',
SHOW_WELCOME_TOUR = 'SHOW_WELCOME_TOUR',
CLEAR_CONSOLE = 'CLEAR_CONSOLE',
LOAD_LOCAL_VERSION_FOLDER = 'LOAD_LOCAL_VERSION_FOLDER',
SHOW_LOCAL_VERSION_FOLDER_DIALOG = 'SHOW_LOCAL_VERSION_FOLDER_DIALOG',
BISECT_COMMANDS_TOGGLE = 'BISECT_COMMANDS_TOGGLE',
}
export const ipcMainEvents = [
IpcEvents.FS_SAVE_FIDDLE_DIALOG,
IpcEvents.FS_SAVE_FIDDLE,
IpcEvents.SHOW_WARNING_DIALOG,
IpcEvents.SHOW_CONFIRMATION_DIALOG,
IpcEvents.SHOW_LOCAL_VERSION_FOLDER_DIALOG
];
export const ipcRendererEvents = [
IpcEvents.OPEN_SETTINGS,
IpcEvents.LOAD_GIST_REQUEST,
IpcEvents.LOAD_ELECTRON_EXAMPLE_REQUEST,
IpcEvents.FIDDLE_RUN,
IpcEvents.FIDDLE_PACKAGE,
IpcEvents.FIDDLE_MAKE,
IpcEvents.MONACO_EXECUTE_COMMAND,
IpcEvents.MONACO_TOGGLE_OPTION,
IpcEvents.FS_NEW_FIDDLE,
IpcEvents.FS_OPEN_FIDDLE,
IpcEvents.FS_OPEN_TEMPLATE,
IpcEvents.FS_SAVE_FIDDLE,
IpcEvents.FS_SAVE_FIDDLE_AS,
IpcEvents.FS_SAVE_FIDDLE_FORGE,
IpcEvents.FS_SAVE_FIDDLE_GIST,
IpcEvents.SHOW_WELCOME_TOUR,
IpcEvents.CLEAR_CONSOLE,
IpcEvents.LOAD_LOCAL_VERSION_FOLDER,
IpcEvents.BISECT_COMMANDS_TOGGLE
];
export const WEBCONTENTS_READY_FOR_IPC_SIGNAL = 'WEBCONTENTS_READY_FOR_IPC_SIGNAL';
+45
View File
@@ -0,0 +1,45 @@
@import (inline) "../../node_modules/@blueprintjs/core/lib/css/blueprint.css";
@import (inline) "../../node_modules/@blueprintjs/select/lib/css/blueprint-select.css";
@import (inline) "../../node_modules/@blueprintjs/icons/lib/css/blueprint-icons.css";
@import (inline) "../../node_modules/@blueprintjs/core/lib/less/variables.less";
// Override some of the colors
.fiddle.bp3-dark {
.bp3-menu,
.bp3-popover .bp3-popover-content {
background-color: @background-1;
}
.bp3-popover .bp3-popover-arrow-fill {
fill: @background-1;
}
.bp3-button:not([class*="bp3-intent-"]) {
background-color: @background-1;
}
.bp3-button.bp3-minimal {
background-color: unset;
}
.bp3-button:hover,
.bp3-button.bp3-minimal:hover {
background-color: rgba(138, 155, 168, 0.15);
}
.bp3-menu-item.bp3-active.bp3-intent-primary {
background-color: @foreground-3;
}
.bp3-dialog {
background-color: @background-4;
.bp3-dialog-header {
background-color: @background-3;
}
}
.bp3-running-text {
font-size: 14px;
}
}
+17
View File
@@ -0,0 +1,17 @@
.darwin {
.chrome {
text-align: center;
background-color: @background-1;
border-bottom: @border;
-webkit-app-region: drag;
height: 24px;
font-size: 1.4rem;
line-height: 15px;
small,
p,
span {
user-select: none;
}
}
}
+73
View File
@@ -0,0 +1,73 @@
@import "../variables.less";
header {
box-shadow: 0 0 0 1px rgba(16, 22, 26, 0.2), 0 0 0 rgba(16, 22, 26, 0), 0 1px 1px rgba(16, 22, 26, 0.4);
background-color: @background-3;
-webkit-app-region: drag;
}
.commands {
display: flex;
justify-content: space-between;
height: 50px;
padding: 10px;
box-sizing: border-box;
font-family: @fonts-common;
button,
input,
select {
-webkit-app-region: no-drag;
margin-bottom: 0;
white-space: nowrap;
}
select {
width: 100px;
margin-left: 5px;
}
fieldset {
border: 0;
padding: 0;
margin: 0;
min-width: 0;
}
& > div {
display: flex;
&:last-of-type {
margin-right: 10px;
.bp3-button-group {
margin-left: 5px;
}
}
}
.bp3-control-group {
margin-right: 0.5rem;
}
.address-bar {
width: 420px;
transition: width 0.4s;
input,
span {
-webkit-app-region: no-drag;
}
&.empty {
width: 300px;
}
&:focus-within {
span,
input {
border-color: @foreground-2;
}
}
}
}
+213
View File
@@ -0,0 +1,213 @@
@import "../variables.less";
.dialogs {
z-index: 10;
position: fixed;
height: 100vh;
width: 100vw;
pointer-events: none;
* {
pointer-events: all;
}
}
.darwin {
.dialogs {
padding-top: 23px;
}
}
.dialog {
background-color: @background-3;
border: @border;
color: @text-color-1;
border-radius: 6px;
padding: 10px;
z-index: 100;
display: flex;
flex-direction: column;
justify-content: space-between;
animation: slide-up 0.4s ease;
p {
margin-bottom: 1.25rem;
font-size: 1.25rem;
}
code {
font-size: 12px;
background: rgba(255, 255, 255, 0.1);
padding: 2px;
line-height: 12px;
}
&-buttons {
margin-top: 1.25rem;
}
&.centered {
position: absolute;
width: 300px;
left: calc(50% - (300px / 2));
top: 100px;
}
&.arrow::after,
&.arrow::before {
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
&.arrow::after {
border-color: rgba(136, 183, 213, 0);
border-width: 9px;
}
&.arrow::before {
border-color: rgba(194, 225, 245, 0);
border-width: 10px;
}
&.arrow-top::after,
&.arrow-top::before {
bottom: 100%;
left: 50%;
}
&.arrow-top::after {
border-bottom-color: @background-3;
margin-left: -9px;
}
&.arrow-top::before {
border-bottom-color: @border-color-1;
margin-left: -10px;
}
&.arrow-bottom::after,
&.arrow-bottom::before {
top: 100%;
left: 50%;
}
&.arrow-bottom::after {
border-top-color: @background-3;
margin-left: -9px;
}
&.arrow-bottom::before {
border-top-color: @border-color-1;
margin-left: -10px;
}
&.arrow-left::after,
&.arrow-left::before {
right: 100%;
top: 20px;
}
&.arrow-left::after {
border-right-color: @background-3;
margin-top: -9px;
}
&.arrow-left::before {
border-right-color: @border-color-1;
margin-top: -10px;
}
&.arrow-right::after,
&.arrow-right::before {
left: 100%;
top: 20px;
}
&.arrow-right::after {
border-left-color: @background-3;
margin-top: -9px;
}
&.arrow-right::before {
border-left-color: @border-color-1;
margin-top: -10px;
}
button {
color: #1e2527;
float: right;
margin-left: 9px;
margin-bottom: 0;
}
a {
cursor: pointer;
}
h4 {
margin-bottom: 0;
}
p:last-of-type {
margin-bottom: 0;
}
input {
color: rgba(159, 234, 249, 0.5);
height: 28px;
margin-top: 10px;
background-color: @background-1;
border: 1px solid @border-color-2;
outline: 0 solid transparent;
&:focus {
border-color: @foreground-2;
}
}
}
.dialogDrop {
position: fixed;
z-index: 100;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: black;
transition: opacity 0.4s ease-in-out;
pointer-events: all;
opacity: 0.5;
}
.dialog-add-version {
.bp3-file-input {
width: 100%;
}
.bp3-callout {
margin-top: 1rem;
}
}
select {
font-size: 12px;
height: 28px;
background-color: white;
line-height: 15px;
}
@keyframes slide-up {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
+30
View File
@@ -0,0 +1,30 @@
.mosaic-tile {
animation: fadein 0.15s;
}
.mosaic-window-toolbar {
& > div {
display: flex;
justify-content: space-between;
width: 100%;
padding: 0 5px 0 5px;
.mosaic-controls {
transform: scale(0.75);
transform-origin: right;
}
h5 {
margin: 4px 0 0 0;
}
button {
margin-left: 8px;
}
}
}
@keyframes fadein {
from { opacity: 0; }
to { opacity: 1; }
}
+59
View File
@@ -0,0 +1,59 @@
@import "../variables.less";
.fiddle {
.mosaic-window-toolbar {
background: var(--background-1);
box-shadow: inset 0 0 100px 100px rgba(53, 53, 53, 0.1);
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.mosaic-window-toolbar.draggable:hover {
box-shadow: inset 0 0 100px 100px rgba(255, 255, 255, 0.1);
}
.mosaic-window-title {
color: var(--text-color-3);
}
button.mosaic-default-control {
padding: 0;
height: 20px;
width: 20px;
margin: 5px;
background-color: #314246;
border: 0.1rem solid #222b35;
}
button.mosaic-default-control:hover {
background-color: #131d1f;
}
.mosaic-window-body {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: stretch;
align-content: stretch;
overflow: visible;
z-index: 4;
background-color: @background-4;
}
.mosaic {
height: 100vh;
&.mosaic-blueprint-theme {
background: #bdbdbd15;
}
}
.editorContainer,
.monaco-editor {
width: 100%;
height: 100%;
}
.editorContainer {
background: @background-1;
}
}
+25
View File
@@ -0,0 +1,25 @@
@import "../variables.less";
.output {
font-size: 12px;
font-family: @fonts-common;
font-weight: 600;
color: var(--text-color-1);
background: var(--background-2);
box-shadow: inset 0 0 100px 100px rgba(0, 0, 0, 0.15);
border-top: @border;
top: 10vh;
padding: 0;
padding-top: 10px;
padding-left: 10px;
height: 100%;
overflow-y: scroll;
-webkit-app-region: no-drag;
p {
margin-bottom: 0;
white-space: pre;
padding-left: 5.6em;
text-indent: -5.6em;
}
}
+189
View File
@@ -0,0 +1,189 @@
@import "../variables.less";
.settings {
background: @background-3;
z-index: 10;
position: absolute;
height: 100%;
width: 100vw;
display: flex;
user-select: none;
cursor: initial;
a:hover {
cursor: pointer;
}
h1,
h2,
h3,
h4,
h5 {
color: @text-color-1;
cursor: initial;
}
h4 {
margin-bottom: 5px;
}
label {
font-weight: 200;
}
select {
width: 250px;
}
.bp3-divider {
margin-top: 1rem;
margin-bottom: 1rem;
margin-left: 0;
margin-right: 0;
}
.bp3-callout {
max-width: 850px;
}
.electron-versions-table {
width: 100%;
.action {
text-align: right;
}
}
.settings-content {
height: 100%;
width: 100%;
padding: 20px;
overflow: scroll;
color: @text-color-3;
box-sizing: border-box;
}
.settings-electron {
.advanced-options-buttons {
display: flex;
flex-flow: row wrap;
> div {
margin-right: 1rem;
button {
width: 100%;
}
}
> div:last-of-type {
margin-right: 0;
}
}
.versions-to-show {
margin-bottom: 2rem;
border-bottom: 1px solid @border-color-1;
padding-bottom: 1.5rem;
p {
margin-bottom: 1rem;
}
label {
display: inline;
margin-right: 10px;
}
}
}
.settings-section {
margin-bottom: 20px;
&:last-of-type {
margin: 0;
}
}
.settings-close {
position: fixed;
right: 30px;
height: 25px;
width: 25px;
text-align: center;
cursor: pointer;
border-radius: 15px;
color: var(--text-color-3);
&:hover {
color: @text-color-1;
}
svg {
background: @background-3;
box-shadow: 0 0 12px 6px @background-3;
}
}
.contributors {
display: flex;
flex-wrap: wrap;
.contributor {
width: 320px;
height: 120px;
background: rgba(255, 255, 255, 0.1);
padding: 10px;
margin: 0 10px 10px 0;
display: flex;
justify-content: flex-start;
cursor: pointer;
&:hover {
background: rgba(255, 255, 255, 0.15);
}
h5,
p {
margin-bottom: 0;
}
.avatar {
height: 100px;
width: 100px;
background-size: contain;
margin-right: 10px;
}
.details {
max-width: 190px;
overflow: scroll;
}
}
}
.settings-menu {
height: 100%;
width: 250px;
background-color: @background-1;
user-select: none;
ul {
list-style: none;
padding: 20px;
}
li {
font-size: 1rem;
padding-top: 10px;
padding-bottom: 10px;
border-bottom: @border;
color: var(--text-color-1);
opacity: 0.8;
&:hover {
opacity: 1;
}
}
}
}
+21
View File
@@ -0,0 +1,21 @@
.show-me-panel {
padding: 10px;
overflow: scroll;
& > *:first-child {
margin-top: 0;
};
.bp3-callout {
margin-bottom: 10px;
}
}
.show-me-list {
display: flex;
flex-direction: column;
flex-wrap: wrap;
list-style: none;
padding: 0;
max-height: 400px;
}
+12
View File
@@ -0,0 +1,12 @@
.tour {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
z-index: 200;
}
.tour-portal {
z-index: 1000;
}
+9
View File
@@ -0,0 +1,9 @@
.container {
display: flex;
flex-direction: column;
height: 100vh;
width: 100vw;
margin: 0;
padding: 0;
max-width: unset;
}
+85
View File
@@ -0,0 +1,85 @@
body {
padding: 0;
margin: 0;
background: @background-1;
overflow: hidden;
display: flex;
flex-direction: column;
height: 100vh;
*:focus {
outline: none;
}
}
.fiddle {
*::-webkit-scrollbar {
width: 14px;
}
*::-webkit-scrollbar-track {
background: unset;
}
*::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.4);
}
*::-webkit-scrollbar-corner {
background: unset;
}
.drag {
-webkit-app-region: drag;
}
.shadow {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.timestamp {
margin-right: 5px;
color: var(--text-color-1);
opacity: 0.7;
user-select: none;
}
}
#editors {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: stretch;
align-content: stretch;
height: 100%;
}
.editor {
flex-grow: 1;
width: 30%;
}
.resize {
width: 6px;
background: rgba(0, 0, 0, 0.2);
margin-left: 9px;
margin-right: 0;
cursor: col-resize;
flex-grow: 0;
flex-shrink: 0;
}
#runner {
height: 0;
}
#runner:not(:empty) {
height: 200px;
}
.editorTitle span {
width: 33%;
display: inline-block;
text-align: center;
font-size: 12px;
}
+334
View File
@@ -0,0 +1,334 @@
/*
* The CSS below is a modified version of the original, increasing the
* FPS from ~11 during scrolling to 60 by reducing the number of
* style calculations.
*/
/**
* @license
* Copyright 2016 Palantir Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.mosaic {
height: 100%;
width: 100%;
}
.mosaic,
.mosaic > * {
box-sizing: border-box;
}
.mosaic .mosaic-zero-state {
width: auto;
height: auto;
max-width: none;
z-index: 1;
}
.mosaic .mosaic-zero-state .pt-non-ideal-state-icon .pt-icon {
font-size: 120px;
}
.mosaic-root {
position: absolute;
top: 3px;
right: 3px;
bottom: 3px;
left: 3px;
}
.mosaic-split {
position: absolute;
z-index: 1;
}
.mosaic-split:hover {
background: black;
}
.mosaic-split .mosaic-split-line {
position: absolute;
}
.mosaic-split.-row {
margin-left: -3px;
width: 6px;
cursor: ew-resize;
}
.mosaic-split.-row .mosaic-split-line {
top: 0;
bottom: 0;
left: 3px;
right: 3px;
}
.mosaic-split.-column {
margin-top: -3px;
height: 6px;
cursor: ns-resize;
}
.mosaic-split.-column .mosaic-split-line {
top: 3px;
bottom: 3px;
left: 0;
right: 0;
}
.mosaic-tile {
position: absolute;
margin: 3px;
}
.mosaic-tile > * {
height: 100%;
width: 100%;
}
.mosaic-drop-target {
position: relative;
}
.mosaic-drop-target.drop-target-hover .drop-target-container {
display: block;
}
.mosaic-drop-target.mosaic > .drop-target-container .drop-target.left {
right: calc(100% - 10px );
}
.mosaic-drop-target.mosaic > .drop-target-container .drop-target.right {
left: calc(100% - 10px );
}
.mosaic-drop-target.mosaic > .drop-target-container .drop-target.bottom {
top: calc(100% - 10px );
}
.mosaic-drop-target.mosaic > .drop-target-container .drop-target.top {
bottom: calc(100% - 10px );
}
.mosaic-drop-target .drop-target-container {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: none;
}
.mosaic-drop-target .drop-target-container.-dragging {
display: block;
}
.mosaic-drop-target .drop-target-container .drop-target {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(0, 0, 0, 0.2);
border: 2px solid black;
opacity: 0;
z-index: 5;
}
.mosaic-drop-target .drop-target-container .drop-target.left {
right: calc(100% - 30% );
}
.mosaic-drop-target .drop-target-container .drop-target.right {
left: calc(100% - 30% );
}
.mosaic-drop-target .drop-target-container .drop-target.bottom {
top: calc(100% - 30% );
}
.mosaic-drop-target .drop-target-container .drop-target.top {
bottom: calc(100% - 30% );
}
.mosaic-drop-target .drop-target-container .drop-target.drop-target-hover {
opacity: 1;
}
.mosaic-drop-target .drop-target-container .drop-target.drop-target-hover.left {
right: calc(100% - 50% );
}
.mosaic-drop-target .drop-target-container .drop-target.drop-target-hover.right {
left: calc(100% - 50% );
}
.mosaic-drop-target .drop-target-container .drop-target.drop-target-hover.bottom {
top: calc(100% - 50% );
}
.mosaic-drop-target .drop-target-container .drop-target.drop-target-hover.top {
bottom: calc(100% - 50% );
}
.mosaic-window,
.mosaic-preview {
position: relative;
display: flex;
flex-direction: column;
box-shadow: 0 0 1px rgba(0, 0, 0, 0.2);
}
.mosaic-window .mosaic-window-toolbar,
.mosaic-preview .mosaic-window-toolbar {
z-index: 4;
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
height: 30px;
background: white;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
.mosaic-window .mosaic-window-toolbar.draggable,
.mosaic-preview .mosaic-window-toolbar.draggable {
cursor: move;
}
.mosaic-window .mosaic-window-title,
.mosaic-preview .mosaic-window-title {
padding-left: 15px;
flex: 1;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
min-height: 18px;
}
.mosaic-window .mosaic-window-controls,
.mosaic-preview .mosaic-window-controls {
display: flex;
height: 100%;
}
.mosaic-window .mosaic-window-controls .separator,
.mosaic-preview .mosaic-window-controls .separator {
height: 20px;
border-left: 1px solid black;
margin: 5px 4px;
}
.mosaic-window .mosaic-window-body,
.mosaic-preview .mosaic-window-body {
position: relative;
flex: 1;
height: 0;
background: white;
z-index: 1;
overflow: hidden;
}
.mosaic-window .mosaic-window-additional-actions-bar,
.mosaic-preview .mosaic-window-additional-actions-bar {
position: absolute;
top: 30px;
right: 0;
bottom: initial;
left: 0;
height: 0;
overflow: hidden;
background: white;
justify-content: flex-end;
display: flex;
z-index: 3;
}
.mosaic-window .mosaic-window-additional-actions-bar .pt-button,
.mosaic-preview .mosaic-window-additional-actions-bar .pt-button {
margin: 0;
}
.mosaic-window .mosaic-window-additional-actions-bar .pt-button::after,
.mosaic-preview .mosaic-window-additional-actions-bar .pt-button::after {
display: none;
}
.mosaic-window .mosaic-window-body-overlay,
.mosaic-preview .mosaic-window-body-overlay {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
opacity: 0;
background: white;
display: none;
z-index: 2;
}
.mosaic-window.additional-controls-open .mosaic-window-additional-actions-bar,
.mosaic-preview.additional-controls-open .mosaic-window-additional-actions-bar {
height: 30px;
}
.mosaic-window.additional-controls-open .mosaic-window-body-overlay,
.mosaic-preview.additional-controls-open .mosaic-window-body-overlay {
display: block;
}
.mosaic-window .mosaic-preview,
.mosaic-preview .mosaic-preview {
height: 100%;
width: 100%;
position: absolute;
z-index: 0;
border: 1px solid black;
max-height: 400px;
}
.mosaic-window .mosaic-preview .mosaic-window-body,
.mosaic-preview .mosaic-preview .mosaic-window-body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.mosaic-window .mosaic-preview h4,
.mosaic-preview .mosaic-preview h4 {
margin-bottom: 10px;
}
.mosaic-window .mosaic-preview .pt-icon,
.mosaic-preview .mosaic-preview .pt-icon {
font-size: 72px;
}
.mosaic:not(.mosaic-blueprint-theme) .mosaic-default-control.pt-icon-cross::before {
content: 'Close';
}
.mosaic:not(.mosaic-blueprint-theme) .mosaic-default-control.pt-icon-add-column-right::before {
content: 'Split';
}
.mosaic:not(.mosaic-blueprint-theme) .mosaic-default-control.pt-icon-exchange::before {
content: 'Replace';
}
.mosaic:not(.mosaic-blueprint-theme) .mosaic-default-control.pt-icon-maximize::before {
content: 'Expand';
}
+19
View File
@@ -0,0 +1,19 @@
// Blueprint
@import "blueprint.less";
// Variables, also imported in each file
@import "variables.less";
@import "main.less";
@import "container.less";
@import "mosaic-vendor.less";
// Components
@import "components/commands.less";
@import "components/output.less";
@import "components/dialogs.less";
@import "components/mosaic.less";
@import "components/settings.less";
@import "components/chrome-mac.less";
@import "components/editors.less";
@import "components/tour.less";
@import "components/show-me.less";
+25
View File
@@ -0,0 +1,25 @@
// We'll take these values from the theme.
// Colors
@foreground-1: var(--foreground-1);
@foreground-2: var(--foreground-2);
@foreground-3: var(--foreground-3);
@background-4: var(--background-4);
@background-3: var(--background-3);
@background-2: var(--background-2);
@background-1: var(--background-1);
@border-color-2: var(--border-color-2);
@border-color-1: var(--border-color-1);
@border: var(--border);
@text-color-1: var(--text-color-1);
@text-color-2: var(--text-color-2);
@text-color-3: var(--text-color-3);
// Dynamically created
@button-text-color: var(--button-text-color);
// Fonts
@fonts-common: var(--fonts-common);
// Lengths
@header-height: 50px;
+165
View File
@@ -0,0 +1,165 @@
import { BrowserWindow, ContextMenuParams, Menu, MenuItemConstructorOptions } from 'electron';
import { IpcEvents } from '../ipc-events';
import { isDevMode } from '../utils/devmode';
import { ipcMainManager } from './ipc';
/**
* Returns items related to running the current fiddle.
*
* @returns {Array<MenuItemConstructorOptions>}
*/
export function getRunItems(): Array<MenuItemConstructorOptions> {
return [
{
id: 'run',
label: 'Run Fiddle',
click: () => ipcMainManager.send(IpcEvents.FIDDLE_RUN)
},
{
id: 'clear_console',
label: 'Clear Console',
click: () => ipcMainManager.send(IpcEvents.CLEAR_CONSOLE)
},
{
type: 'separator'
}
];
}
/**
* Possibly returns items interacting with the Monaco editor.
* Our check for "are we in the Monaco editor" is pretty crude -
* we just assume that we are if we can paste text.
*
* @param {BrowserWindow} browserWindow
* @param {ContextMenuParams} { x, y }
* @returns {Array<MenuItemConstructorOptions>}
*/
export function getMonacoItems(
{ pageURL, editFlags }: ContextMenuParams
): Array<MenuItemConstructorOptions> {
if (!editFlags.canPaste || !/.*index\.html(#?)$/.test(pageURL || '')) {
return [];
}
return [
{
id: 'go_to_definition',
label: 'Go to Definition',
click() {
const cmd = [ 'editor.action.goToDeclaration' ];
ipcMainManager.send(IpcEvents.MONACO_EXECUTE_COMMAND, cmd);
}
},
{
id: 'peek_definition',
label: 'Peek Definition',
click() {
const cmd = [ 'editor.action.previewDeclaration' ];
ipcMainManager.send(IpcEvents.MONACO_EXECUTE_COMMAND, cmd);
}
},
{
id: 'references',
label: 'Find References',
click() {
const cmd = [ 'editor.action.referenceSearch.trigger' ];
ipcMainManager.send(IpcEvents.MONACO_EXECUTE_COMMAND, cmd);
}
},
{ type: 'separator' },
{
id: 'palette',
label: 'Command Palette',
click() {
const cmd = [ 'editor.action.quickCommand' ];
ipcMainManager.send(IpcEvents.MONACO_EXECUTE_COMMAND, cmd);
}
},
{ type: 'separator' },
{
id: 'format_document',
label: 'Format Document',
click() {
const cmd = [ 'editor.action.formatDocument' ];
ipcMainManager.send(IpcEvents.MONACO_EXECUTE_COMMAND, cmd);
}
},
{
id: 'format_selection',
label: 'Format Selection',
click() {
const cmd = [ 'editor.action.formatSelection' ];
ipcMainManager.send(IpcEvents.MONACO_EXECUTE_COMMAND, cmd);
}
},
{ type: 'separator' },
];
}
/**
* Possibly returns the `Inspect Element` item.
*
* @param {BrowserWindow} browserWindow
* @param {ContextMenuParams} { x, y }
* @returns {Array<MenuItemConstructorOptions>}
*/
export function getInspectItems(
browserWindow: BrowserWindow, { x, y }: ContextMenuParams
): Array<MenuItemConstructorOptions> {
if (!isDevMode()) return [];
return [{
id: 'inspect',
label: 'Inspect Element',
click: () => {
browserWindow.webContents.inspectElement(x, y);
try {
if (browserWindow.webContents.isDevToolsOpened()) {
browserWindow.webContents.devToolsWebContents.focus();
}
} catch (error) {
console.warn(`Tried to focus dev tools, but failed`, { error });
}
}
}];
}
/**
* Creates a context menu for a given BrowserWindow
*
* @param {BrowserWindow} browserWindow
*/
export function createContextMenu(browserWindow: BrowserWindow) {
browserWindow.webContents.on('context-menu', (_event, props) => {
const { editFlags } = props;
const template: Array<MenuItemConstructorOptions> = [
...getRunItems(),
...getMonacoItems(props),
{
id: 'cut',
label: 'Cut',
role: 'cut',
enabled: editFlags.canCut
}, {
id: 'copy',
label: 'Copy',
role: 'copy',
enabled: editFlags.canCopy
}, {
id: 'paste',
label: 'Paste',
role: 'paste',
enabled: editFlags.canPaste
}, {
type: 'separator'
},
...getInspectItems(browserWindow, props)
];
const menu = Menu.buildFromTemplate(template);
menu.popup({});
});
}
+27
View File
@@ -0,0 +1,27 @@
import { isDevMode } from '../utils/devmode';
/**
* Installs developer tools if we're in dev mode.
*
* @export
* @returns {Promise<void>}
*/
export async function setupDevTools(): Promise<void> {
if (!isDevMode()) return;
const {
default: installExtension,
REACT_DEVELOPER_TOOLS,
REACT_PERF
} = require('electron-devtools-installer');
try {
const react = await installExtension(REACT_DEVELOPER_TOOLS);
console.log(`installDevTools: Installed ${react}`);
const perf = await installExtension(REACT_PERF);
console.log(`installDevTools: Installed ${perf}`);
} catch (error) {
console.warn(`installDevTools: Error occured:`, error);
}
}
+61
View File
@@ -0,0 +1,61 @@
import { dialog, IpcMainEvent } from 'electron';
import { IpcEvents } from '../ipc-events';
import { ipcMainManager } from './ipc';
import { getOrCreateMainWindow } from './windows';
/**
* Listens to IPC events related to dialogs and message boxes
*
* @export
*/
export function setupDialogs() {
ipcMainManager.on(IpcEvents.SHOW_WARNING_DIALOG, (_event, args) => {
showWarningDialog(args);
});
ipcMainManager.on(IpcEvents.SHOW_CONFIRMATION_DIALOG, (_event, args) => {
showConfirmationDialog(args);
});
ipcMainManager.on(IpcEvents.SHOW_LOCAL_VERSION_FOLDER_DIALOG, async (event) => {
await showOpenDialog(event);
});
}
/**
* Shows a warning dialog
*
* @param {Electron.MessageBoxOptions} args
*/
function showWarningDialog(args: Electron.MessageBoxOptions) {
dialog.showMessageBox(getOrCreateMainWindow(), {
type: 'warning',
...args
});
}
/**
* Shows a confirmation dialog
*
* @param {Electron.MessageBoxOptions} args
*/
function showConfirmationDialog(args: Electron.MessageBoxOptions) {
dialog.showMessageBox(getOrCreateMainWindow(), {
type: 'warning',
...args
});
}
async function showOpenDialog(event: IpcMainEvent) {
const { filePaths } = await dialog.showOpenDialog({
title: 'Open Folder',
properties: ['openDirectory']
});
if (!filePaths || filePaths.length < 1) {
return;
}
event.reply(IpcEvents.LOAD_LOCAL_VERSION_FOLDER, [filePaths[0]]);
}
+105
View File
@@ -0,0 +1,105 @@
import { dialog } from 'electron';
import * as fs from 'fs-extra';
import * as path from 'path';
import { IpcEvents } from '../ipc-events';
import { INDEX_HTML_NAME, MAIN_JS_NAME, PACKAGE_NAME, RENDERER_JS_NAME } from '../shared-constants';
import { ipcMainManager } from './ipc';
/**
* Ensures that we're listening to file events
*/
export function setupFileListeners() {
ipcMainManager.on(IpcEvents.FS_SAVE_FIDDLE_DIALOG, () => {
showSaveDialog();
});
}
/**
* Shows the "Open Fiddle" dialog and forwards
* the path to the renderer
*/
export async function showOpenDialog() {
const { filePaths } = await dialog.showOpenDialog({
title: 'Open Fiddle',
properties: ['openDirectory']
});
if (!filePaths || filePaths.length < 1) {
return;
}
ipcMainManager.send(IpcEvents.FS_OPEN_FIDDLE, [ filePaths[0] ]);
}
/**
* Shows the "Save Fiddle" dialog and forwards
* the path to the renderer
*/
export async function showSaveDialog(event?: IpcEvents, as?: string) {
// We want to save to a folder, so we'll use an open dialog here
const { filePaths } = await dialog.showOpenDialog({
buttonLabel: 'Save here',
properties: ['openDirectory', 'createDirectory'],
title: `Save Fiddle${as ? ` as ${as}` : ''}`
});
if (!filePaths || filePaths.length < 1) {
return;
}
console.log(`Asked to save to ${filePaths[0]}`);
// Let's confirm real quick if we want this
if (await ensureSaveTargetEmpty(filePaths[0])) {
ipcMainManager.send(event || IpcEvents.FS_SAVE_FIDDLE, [ filePaths[0] ]);
}
}
/**
* Ensures that a folder designated for saving is empty
*
* @param {string} filePath
* @returns {Promise<boolean>}
*/
async function ensureSaveTargetEmpty(filePath: string): Promise<boolean> {
const targetPaths = [
path.join(filePath, INDEX_HTML_NAME),
path.join(filePath, RENDERER_JS_NAME),
path.join(filePath, MAIN_JS_NAME),
path.join(filePath, PACKAGE_NAME)
];
let noFilesOrOverwriteGranted = true;
for (const targetPath of targetPaths) {
if (fs.existsSync(targetPath) && noFilesOrOverwriteGranted) {
noFilesOrOverwriteGranted = await confirmFileOverwrite(targetPath);
}
}
return noFilesOrOverwriteGranted;
}
/**
* Pops open a confirmation dialog, asking the user if they really
* want to overwrite an existing file
*
* @param {string} filePath
* @returns {Promise<boolean>}
*/
async function confirmFileOverwrite(filePath: string): Promise<boolean> {
try {
const result = await dialog.showMessageBox({
type: 'warning',
buttons: [ 'Cancel', 'Yes' ],
message: 'Overwrite files?',
detail: `The file ${filePath} already exists. Do you want to overwrite it?`,
});
return !!result;
} catch (error) {
// Let's not overwrite files. We'd rather crash.
throw error;
}
}
+34
View File
@@ -0,0 +1,34 @@
import { app, dialog } from 'electron';
import { isFirstRun } from '../utils/check-first-run';
import { isDevMode } from '../utils/devmode';
/**
* Is this the first run of Fiddle? If so, perform
* tasks that we only want to do in this case.
*/
export async function onFirstRunMaybe() {
if (isFirstRun()) {
await promptMoveToApplicationsFolder();
}
}
/**
* Ask the user if the app should be moved to the
* applications folder.
*/
async function promptMoveToApplicationsFolder(): Promise<void> {
if (process.platform !== 'darwin') return;
if (isDevMode() || app.isInApplicationsFolder()) return;
const { response } = await dialog.showMessageBox({
type: 'question',
buttons: ['Move to Applications Folder', 'Do Not Move'],
defaultId: 0,
message: 'Move to Applications Folder?',
});
if (response === 0) {
app.moveToApplicationsFolder();
}
}
+59
View File
@@ -0,0 +1,59 @@
import { ipcMain } from 'electron';
import { EventEmitter } from 'events';
import { IpcEvents, ipcMainEvents, WEBCONTENTS_READY_FOR_IPC_SIGNAL } from '../ipc-events';
import { getOrCreateMainWindow } from './windows';
/**
* The main purpose of this class is to be the central
* gathering place for IPC calls the main process sends
* or listens to.
*
* @class IpcManager
* @extends {EventEmitter}
*/
export class IpcMainManager extends EventEmitter {
public readyWebContents = new WeakSet<Electron.WebContents>();
private messageQueue = new WeakMap<Electron.WebContents, Array<[IpcEvents, Array<any> | undefined]>>();
constructor() {
super();
ipcMainEvents.forEach((name) => {
ipcMain.on(name, (...args: Array<any>) => this.emit(name, ...args));
});
ipcMain.on(WEBCONTENTS_READY_FOR_IPC_SIGNAL, (event: Electron.IpcMainEvent) => {
this.readyWebContents.add(event.sender);
const queue = this.messageQueue.get(event.sender);
this.messageQueue.delete(event.sender);
if (!queue) return;
for (const item of queue) {
this.send(item[0], item[1], event.sender);
}
});
}
/**
* Send an IPC message to an instance of Electron.WebContents.
* If none is specified, we'll automatically go with the main window.
*
* @param {IpcEvents} channel
* @param {Array<any>} args
* @param {Electron.WebContents} [target]
*/
public send(channel: IpcEvents, args?: Array<any>, target?: Electron.WebContents) {
const _target = target || getOrCreateMainWindow().webContents;
const _args = args || [];
if (!this.readyWebContents.has(_target)) {
const existing = this.messageQueue.get(_target) || [];
this.messageQueue.set(_target, [...existing, [channel, args]]);
return;
}
_target.send(channel, ..._args);
}
}
export const ipcMainManager = new IpcMainManager();
+83
View File
@@ -0,0 +1,83 @@
import { app } from 'electron';
import { isDevMode } from '../utils/devmode';
import { setupAboutPanel } from '../utils/set-about-panel';
import { setupDevTools } from './devtools';
import { setupDialogs } from './dialogs';
import { onFirstRunMaybe } from './first-run';
import { listenForProtocolHandler, setupProtocolHandler } from './protocol';
import { shouldQuit } from './squirrel';
import { setupUpdates } from './update';
import { getOrCreateMainWindow } from './windows';
/**
* Handle the app's "ready" event. This is essentially
* the method that takes care of booting the application.
*/
export async function onReady() {
await onFirstRunMaybe();
if (!isDevMode()) process.env.NODE_ENV = 'production';
getOrCreateMainWindow();
setupAboutPanel();
const { setupMenu } = await import('./menu');
const { setupFileListeners } = await import('./files');
setupMenu();
setupProtocolHandler();
setupFileListeners();
setupUpdates();
setupDialogs();
setupDevTools();
}
/**
* Handle the "before-quit" event
*
* @export
*/
export function onBeforeQuit() {
(global as any).isQuitting = true;
}
/**
* All windows have been closed, quit on anything but
* macOS.
*/
export function onWindowsAllClosed() {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit();
}
}
/**
* The main method - and the first function to run
* when Fiddle is launched.
*
* Exported for testing purposes.
*/
export function main() {
// Handle creating/removing shortcuts on Windows when
// installing/uninstalling.
if (shouldQuit()) {
app.quit();
return;
}
// Set the app's name
app.name = 'Electron Fiddle';
// Ensure that there's only ever one Fiddle running
listenForProtocolHandler();
// Launch
app.on('ready', onReady);
app.on('before-quit', onBeforeQuit);
app.on('window-all-closed', onWindowsAllClosed);
app.on('activate', getOrCreateMainWindow);
}
main();
+284
View File
@@ -0,0 +1,284 @@
import { app, BrowserWindow, Menu, MenuItemConstructorOptions, shell } from 'electron';
import { Templates } from '../interfaces';
import { IpcEvents } from '../ipc-events';
import { SHOW_ME_TEMPLATES } from '../templates';
import { showOpenDialog, showSaveDialog } from './files';
import { ipcMainManager } from './ipc';
import { createMainWindow } from './windows';
/**
* Is the passed object a constructor for an Electron Menu?
*
* @param {(Array<Electron.MenuItemConstructorOptions> | Electron.Menu)} [submenu]
* @returns {submenu is Array<Electron.MenuItemConstructorOptions>}
*/
function isSubmenu(
submenu?: Array<MenuItemConstructorOptions> | Menu
): submenu is Array<MenuItemConstructorOptions> {
return !!submenu && Array.isArray(submenu);
}
/**
* Returns additional items for the help menu
*
* @returns {Array<Electron.MenuItemConstructorOptions>}
*/
function getHelpItems(): Array<MenuItemConstructorOptions> {
return [
{
type: 'separator'
},
{
label: 'Show Welcome Tour',
click() {
ipcMainManager.send(IpcEvents.SHOW_WELCOME_TOUR);
}
},
{
type: 'separator'
},
{
label: 'Toggle Developer Tools',
accelerator: 'CmdOrCtrl+Option+i',
click() {
const browserWindow = BrowserWindow.getFocusedWindow();
if (browserWindow && !browserWindow.isDestroyed()) {
browserWindow.webContents.openDevTools({ mode: 'bottom' });
}
}
},
{
type: 'separator'
},
{
label: 'Open Fiddle Repository...',
click() {
shell.openExternal('https://github.com/electron/fiddle');
}
},
{
label: 'Open Electron Repository...',
click() {
shell.openExternal('https://github.com/electron/electron');
}
},
{
label: 'Open Electron Issue Tracker...',
click() {
shell.openExternal('https://github.com/electron/electron/issues');
}
},
];
}
/**
* Depending on the OS, the `Preferences` either go into the `Fiddle`
* menu (macOS) or under `File` (Linux, Windows)
*
* @returns {Array<Electron.MenuItemConstructorOptions>}
*/
function getPreferencesItems(): Array<MenuItemConstructorOptions> {
return [
{
type: 'separator'
}, {
label: 'Preferences',
accelerator: 'CmdOrCtrl+,',
click() {
ipcMainManager.send(IpcEvents.OPEN_SETTINGS);
}
}, {
type: 'separator'
}
];
}
/**
* Returns the Exit items
*
* @returns {Array<Electron.MenuItemConstructorOptions>}
*/
function getQuitItems(): Array<MenuItemConstructorOptions> {
return [
{
type: 'separator'
}, {
label: 'Exit',
accelerator: 'Ctrl+Q,',
click: app.quit
}
];
}
/**
* Returns the top-level "File" menu
*
* @returns {Array<Electron.MenuItemConstructorOptions>}
*/
function getTasksMenu(): MenuItemConstructorOptions {
const tasksMenu: Array<MenuItemConstructorOptions> = [
{
label: 'Run Fiddle...',
accelerator: 'F5',
click: () => ipcMainManager.send(IpcEvents.FIDDLE_RUN)
},
{
label: 'Package Fiddle...',
click: () => ipcMainManager.send(IpcEvents.FIDDLE_PACKAGE)
},
{
label: 'Make installers for Fiddle...',
click: () => ipcMainManager.send(IpcEvents.FIDDLE_MAKE)
}
];
return {
label: 'Tasks',
submenu: tasksMenu
};
}
function getShowMeMenuItem(key: string, item: string | Templates): MenuItemConstructorOptions {
if (typeof item === 'string') {
return {
label: key,
click: () => ipcMainManager.send(IpcEvents.FS_OPEN_TEMPLATE, [key])
};
}
return {
label: key,
submenu: Object.keys(item).map((subkey) => {
return getShowMeMenuItem(subkey, item[subkey]);
})
};
}
function getShowMeMenu(): MenuItemConstructorOptions {
const showMeMenu: Array<MenuItemConstructorOptions> = Object.keys(SHOW_ME_TEMPLATES)
.map((key) => getShowMeMenuItem(key, SHOW_ME_TEMPLATES[key]));
return {
label: 'Show Me',
submenu: showMeMenu
};
}
/**
* Returns the top-level "File" menu
*
* @returns {Array<Electron.MenuItemConstructorOptions>}
*/
function getFileMenu(): MenuItemConstructorOptions {
const fileMenu: Array<MenuItemConstructorOptions> = [
{
label: 'New Fiddle',
click: () => ipcMainManager.send(IpcEvents.FS_NEW_FIDDLE),
accelerator: 'CmdOrCtrl+N'
}, {
label: 'New Window',
click: () => createMainWindow(),
accelerator: 'CmdOrCtrl+Shift+N'
}, {
type: 'separator'
},
{
label: 'Open',
click: showOpenDialog,
accelerator: 'CmdOrCtrl+O'
},
{
type: 'separator'
},
{
label: 'Save',
click: () => ipcMainManager.send(IpcEvents.FS_SAVE_FIDDLE),
accelerator: 'CmdOrCtrl+S'
},
{
label: 'Save as',
click: () => showSaveDialog(IpcEvents.FS_SAVE_FIDDLE),
accelerator: 'CmdOrCtrl+Shift+S'
},
{
type: 'separator'
},
{
label: 'Publish to Gist',
click: () => ipcMainManager.send(IpcEvents.FS_SAVE_FIDDLE_GIST),
},
{
label: 'Save as Forge Project',
click: () => showSaveDialog(IpcEvents.FS_SAVE_FIDDLE_FORGE, 'Forge Project')
}
];
// macOS has these items in the "Fiddle" menu
if (process.platform !== 'darwin') {
fileMenu.splice(fileMenu.length, 0, ...getPreferencesItems(), ...getQuitItems());
}
return {
label: 'File',
submenu: fileMenu
};
}
/**
* Creates the app's window menu.
*/
export function setupMenu() {
// Get template for default menu
const defaultMenu = require('electron-default-menu');
const menu = (defaultMenu(app, shell) as Array<MenuItemConstructorOptions>)
.map((item) => {
const { label } = item;
// Append the "Settings" item
if (
process.platform === 'darwin'
&& label === app.getName()
&& isSubmenu(item.submenu)
) {
item.submenu.splice(2, 0, ...getPreferencesItems());
}
// Tweak "View" menu
if (label === 'View' && isSubmenu(item.submenu)) {
item.submenu = item.submenu.filter((subItem) => subItem.label !== 'Toggle Developer Tools'); // Remove "Toggle Developer Tools"
item.submenu.push({ type: 'separator' }, { role: 'resetZoom' }, { role: 'zoomIn' }, { role: 'zoomOut' }); // Add zooming actions
item.submenu.push({ type: 'separator' }, {
label: 'Toggle Soft Wrap',
click: () => ipcMainManager.send(IpcEvents.MONACO_TOGGLE_OPTION, ['wordWrap']),
});
item.submenu.push({ type: 'separator' }, {
label: 'Toggle Mini Map',
click: () => ipcMainManager.send(IpcEvents.MONACO_TOGGLE_OPTION, ['minimap.enabled']),
});
item.submenu.push({ type: 'separator' }, {
label: 'Toggle Bisect Helper',
click: () => ipcMainManager.send(IpcEvents.BISECT_COMMANDS_TOGGLE),
accelerator: 'CommandorControl+Shift+B',
});
}
// Append items to "Help"
if (label === 'Help' && isSubmenu(item.submenu)) {
item.submenu = getHelpItems();
}
return item;
});
menu.splice(
process.platform === 'darwin' ? 1 : 0,
0,
getFileMenu()
);
menu.splice(menu.length - 1, 0, getTasksMenu(), getShowMeMenu());
Menu.setApplicationMenu(Menu.buildFromTemplate(menu));
}
+98
View File
@@ -0,0 +1,98 @@
import { app } from 'electron';
import * as fs from 'fs';
import * as path from 'path';
import * as nodeUrl from 'url';
import { IpcEvents } from '../ipc-events';
import { isDevMode } from '../utils/devmode';
import { ipcMainManager } from './ipc';
const PROTOCOL = 'electron-fiddle';
const squirrelPath = path.resolve(path.dirname(process.execPath), '..', 'electron-fiddle.exe');
const handlePotentialProtocolLaunch = (url: string) => {
if (!app.isReady()) {
app.once('ready', () => handlePotentialProtocolLaunch(url));
return;
}
const parsed = nodeUrl.parse(url.replace(/\/$/, ''));
if (!parsed.pathname || !parsed.hostname) return;
const pathParts = parsed.pathname.split('/').slice(1);
switch (parsed.hostname) {
// electron-fiddle://gist/blub
case 'gist':
if (pathParts.length === 1) {
// We only have a gist ID
ipcMainManager.send(IpcEvents.LOAD_GIST_REQUEST, [{
id: pathParts[0],
}]);
} else if (pathParts.length === 2) {
// We have a gist owner and gist ID, we can ignore the owner
ipcMainManager.send(IpcEvents.LOAD_GIST_REQUEST, [{
id: pathParts[1],
}]);
} else {
// This is a super invalid gist launch
return;
}
break;
// electron-fiddle://electron/{ref}/{path}
case 'electron':
if (pathParts.length > 1) {
// First part of the commit HASH / ref / branch
// Rest is the path to the example
ipcMainManager.send(IpcEvents.LOAD_ELECTRON_EXAMPLE_REQUEST, [{
ref: pathParts[0],
path: pathParts.slice(1).join('/'),
}]);
} else {
// This is an invalid electron launch
return;
}
break;
default:
return;
}
};
export const scanArgv = (argv: Array<string>) => {
const protocolArg = argv.find((arg) => arg.startsWith(`${PROTOCOL}://`));
if (protocolArg) {
console.info('Found protocol arg in argv:', protocolArg);
handlePotentialProtocolLaunch(protocolArg);
}
};
export const scanNpmArgv = (argv: string) => {
const parsedArgv = JSON.parse(argv);
const { original: args } = parsedArgv;
scanArgv(args);
};
export const listenForProtocolHandler = () => {
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) app.quit();
app.on('open-url', (_, url) => {
if (url.startsWith(`${PROTOCOL}://`)) {
handlePotentialProtocolLaunch(url);
}
});
// pass protocol URL via npm start args in dev mode
if (isDevMode() && process.env.npm_config_argv) {
scanNpmArgv(process.env.npm_config_argv);
} else if (process.platform === 'win32') {
scanArgv(process.argv);
}
};
export const setupProtocolHandler = () => {
if (process.platform === 'win32' && !fs.existsSync(squirrelPath)) return;
if (!app.isDefaultProtocolClient(PROTOCOL, squirrelPath)) {
app.setAsDefaultProtocolClient(PROTOCOL, squirrelPath);
}
};
+3
View File
@@ -0,0 +1,3 @@
export function shouldQuit() {
return require('electron-squirrel-startup');
}
+15
View File
@@ -0,0 +1,15 @@
/**
* Sets up the update service
*/
export function setupUpdates() {
// We delay this work by 10s to ensure that the
// app doesn't have to worry about updating during launch
setTimeout(() => {
const updateApp = require('update-electron-app');
updateApp({
repo: 'electron/fiddle',
updateInterval: '1 hour',
});
}, 10000);
}
+76
View File
@@ -0,0 +1,76 @@
import { BrowserWindow, shell } from 'electron';
import { createContextMenu } from './context-menu';
// Keep a global reference of the window objects, if we don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
export let browserWindows: Array<BrowserWindow> = [];
/**
* Gets default options for the main window
*
* @returns {Electron.BrowserWindowConstructorOptions}
*/
export function getMainWindowOptions(): Electron.BrowserWindowConstructorOptions {
return {
width: 1200,
height: 900,
minHeight: 600,
minWidth: 600,
titleBarStyle: process.platform === 'darwin' ? 'hidden' : undefined,
acceptFirstMouse: true,
backgroundColor: '#1d2427',
webPreferences: {
webviewTag: false,
nodeIntegration: true
}
};
}
/**
* Creates a new main window.
*
* @export
* @returns {Electron.BrowserWindow}
*/
export function createMainWindow(): Electron.BrowserWindow {
console.log(`Creating main window`);
const browserWindow = new BrowserWindow(getMainWindowOptions());
browserWindow.loadFile('./dist/static/index.html');
browserWindow.webContents.once('dom-ready', () => {
browserWindow.show();
if (browserWindow) {
createContextMenu(browserWindow);
}
});
browserWindow.on('closed', () => {
browserWindows = browserWindows
.filter((bw) => browserWindow !== bw);
});
browserWindow.webContents.on('new-window', (event, url) => {
event.preventDefault();
shell.openExternal(url);
});
browserWindow.webContents.on('will-navigate', (event, url) => {
event.preventDefault();
shell.openExternal(url);
});
browserWindows.push(browserWindow);
return browserWindow;
}
/**
* Gets or creates the main window, returning it in both cases.
*
* @returns {Electron.BrowserWindow}
*/
export function getOrCreateMainWindow(): Electron.BrowserWindow {
return BrowserWindow.getFocusedWindow() || browserWindows[0] || createMainWindow();
}
+213 -82
View File
@@ -1,96 +1,227 @@
import * as React from 'react';
import { render } from 'react-dom';
import * as loader from 'monaco-loader';
import { observable } from 'mobx';
import { when } from 'mobx';
import * as MonacoType from 'monaco-editor';
import { mainTheme } from './themes';
import { getContent } from './content';
import { Header } from './components/header';
import { BinaryManager } from './binary';
import { ElectronVersion, StringMap, OutputEntry } from '../interfaces';
import { arrayToStringMap } from '../utils/array-to-stringmap';
import { getKnownVersions } from './versions';
import { normalizeVersion } from '../utils/normalize-version';
import { ipcRenderer } from 'electron';
import {
ALL_EDITORS,
EditorId,
EditorValues,
SetFiddleOptions
} from '../interfaces';
import { WEBCONTENTS_READY_FOR_IPC_SIGNAL } from '../ipc-events';
import { updateEditorLayout } from '../utils/editor-layout';
import { getEditorValue } from '../utils/editor-value';
import { getPackageJson, PackageJsonOptions } from '../utils/get-package';
import { getTitle } from '../utils/get-title';
import { isEditorBackup } from '../utils/type-checks';
import { FileManager } from './file-manager';
import { RemoteLoader } from './remote-loader';
import { Runner } from './runner';
import { appState } from './state';
import { getTheme } from './themes';
import { TouchBarManager } from './touch-bar-manager';
const knownVersions = getKnownVersions();
const defaultVersion = normalizeVersion(knownVersions[0].tag_name);
export class AppState {
@observable public version: string = defaultVersion;
@observable public binaryManager: BinaryManager = new BinaryManager(defaultVersion);
@observable public versions: StringMap<ElectronVersion> = arrayToStringMap(knownVersions);
@observable public output: Array<OutputEntry> = [];
@observable public isConsoleShowing: boolean = false;
}
const appState = new AppState();
class App {
public editors: any = {
main: null,
renderer: null,
html: null
};
public monaco: any = null;
public name = 'test';
/**
* The top-level class controlling the whole app. This is *not* a React component,
* but it does eventually render all components.
*
* @class App
*/
export class App {
public typeDefDisposable: MonacoType.IDisposable | null = null;
public monaco: typeof MonacoType | null = null;
public state = appState;
public fileManager = new FileManager(appState);
public remoteLoader = new RemoteLoader(appState);
public runner = new Runner(appState);
public touchBarManager: TouchBarManager | undefined;
constructor() {
this.getValues = this.getValues.bind(this);
this.getEditorValues = this.getEditorValues.bind(this);
this.setEditorValues = this.setEditorValues.bind(this);
this.setup();
if (process.platform === 'darwin') {
this.touchBarManager = new TouchBarManager(appState);
}
}
private async setup() {
this.monaco = await loader();
public async replaceFiddle(
editorValues: Partial<EditorValues>,
{ filePath, gistId, templateName }: Partial<SetFiddleOptions>
) {
// if unsaved, prompt user to make sure they're okay with overwriting and changing directory
if (this.state.isUnsaved) {
this.state.setWarningDialogTexts({
label: `Opening this Fiddle will replace your unsaved changes. Do you want to proceed?`,
ok: 'Yes'
});
this.state.isWarningDialogShowing = true;
await when(() => !this.state.isWarningDialogShowing);
this.createThemes();
this.editors.html = this.createEditor('html');
this.editors.main = this.createEditor('main');
this.editors.renderer = this.createEditor('renderer');
render(<Header appState={appState} />, document.getElementById('header'));
}
private createThemes() {
this.monaco.editor.defineTheme('main', mainTheme);
}
private createEditor(id: string) {
if (!this.monaco) throw new Error('Monaco not ready');
const element = document.getElementById(`editor-${id}`);
const language = id === 'html' ? 'html' : 'javascript';
const value = getContent(id);
const options = {
language,
theme: 'main',
automaticLayout: true,
minimap: {
enabled: false
},
value
};
return this.monaco.editor.create(element, options);
}
private getValues() {
if (!this.editors.html || !this.editors.main || !this.editors.renderer) {
throw new Error('Editors not ready');
if (!this.state.warningDialogLastResult) {
return false;
}
}
return {
html: this.editors.html!.getValue(),
main: this.editors.main!.getValue(),
renderer: this.editors.renderer!.getValue(),
package: JSON.stringify({
name: this.name,
main: './main.js',
version: '1.0.0'
})
// set values once prompt approves
await this.setEditorValues(editorValues);
document.title = getTitle(this.state);
this.state.gistId = gistId || '';
this.state.localPath = filePath;
this.state.templateName = templateName;
// once loaded, we have a "saved" state
this.state.isUnsaved = false;
this.setupUnsavedOnChangeListener();
return true;
}
/**
* Sets the values on all three editors.
*
* @param {EditorValues} values
* @param {warn} warn - Should we warn before overwriting unsaved data?
*/
public async setEditorValues(values: Partial<EditorValues>): Promise<void> {
const { ElectronFiddle: fiddle } = window;
if (!fiddle) {
throw new Error('Fiddle not ready');
}
for (const name of ALL_EDITORS) {
const editor = fiddle.editors[name];
const backup = this.state.closedPanels[name];
if (typeof values[name] !== 'undefined') {
if (isEditorBackup(backup) && backup.model) {
// The editor does not exist, attempt to set it on the backup
backup.model.setValue(values[name]!);
} else if (editor && editor.setValue) {
// The editor exists, set the value directly
editor.setValue(values[name]!);
}
}
}
}
/**
* Gets the values on all three editors.
*
* @returns {EditorValues}
*/
public async getEditorValues(
options?: PackageJsonOptions
): Promise<EditorValues> {
const { ElectronFiddle: fiddle } = window;
if (!fiddle) {
throw new Error('Fiddle not ready');
}
const values: EditorValues = {
html: getEditorValue(EditorId.html),
main: getEditorValue(EditorId.main),
preload: getEditorValue(EditorId.preload),
renderer: getEditorValue(EditorId.renderer)
};
if (options && options.include !== false) {
values.package = await getPackageJson(this.state, values, options);
}
return values;
}
/**
* Initial setup call, loading Monaco and kicking off the React
* render process.
*/
public async setup(): Promise<void | Element | React.Component> {
this.setupTheme();
const React = await import('react');
const { render } = await import('react-dom');
const { Dialogs } = await import('./components/dialogs');
const { OutputEditorsWrapper } = await import(
'./components/output-editors-wrapper'
);
const { Header } = await import('./components/header');
const className = `${process.platform} container`;
const app = (
<div className={className}>
<Dialogs appState={this.state} />
<Header appState={this.state} />
<OutputEditorsWrapper appState={this.state} />
</div>
);
const rendered = render(app, document.getElementById('app'));
this.setupResizeListener();
ipcRenderer.send(WEBCONTENTS_READY_FOR_IPC_SIGNAL);
// TODO: A timer here is terrible. Let's fix this
// and ensure we actually do it once Editors have mounted.
setTimeout(() => {
this.setupUnsavedOnChangeListener();
}, 1500);
return rendered;
}
/**
* If the editor is changed for the first time, we'll
* set `isUnsaved` to true. That way, the app can warn you
* if you're about to throw things away.
*/
public setupUnsavedOnChangeListener() {
Object.keys(window.ElectronFiddle.editors).forEach((key) => {
const editor = window.ElectronFiddle.editors[key];
const disposable = editor.onDidChangeModelContent(() => {
this.state.isUnsaved = true;
disposable.dispose();
});
});
}
/**
* Loads theme CSS into the HTML document.
*
* @returns {Promise<void>}
*/
public async setupTheme(): Promise<void> {
const tag: HTMLStyleElement | null = document.querySelector(
'style#fiddle-theme'
);
const theme = await getTheme(this.state.theme);
if (tag && theme.css) {
tag.innerHTML = theme.css;
}
if (theme.isDark || theme.name.includes('dark')) {
document.body.classList.add('bp3-dark');
} else {
document.body.classList.remove('bp3-dark');
}
}
/**
* We need to possibly recalculate the layout whenever the window
* is resized. This method sets up the listener.
*/
public setupResizeListener(): void {
window.addEventListener('resize', updateEditorLayout);
}
}
(window as any).electronFiddle = new App();
window.ElectronFiddle = window.ElectronFiddle || {};
window.ElectronFiddle.contentChangeListeners =
window.ElectronFiddle.contentChangeListeners || [];
window.ElectronFiddle.app = window.ElectronFiddle.app || new App();
window.ElectronFiddle.app.setup();
+155 -34
View File
@@ -1,28 +1,83 @@
import { promisify } from 'util';
import * as os from 'os';
import * as fs from 'fs-extra';
import * as fsType from 'fs-extra';
import * as path from 'path';
import * as extract from 'extract-zip';
import { EventEmitter } from 'events';
import { userData } from './constants';
import { fancyImport } from '../utils/import';
import { normalizeVersion } from '../utils/normalize-version';
import { StringMap } from '../interfaces';
import { USER_DATA_PATH } from './constants';
import { getOfflineTypeDefinitionPath } from './fetch-types';
const eDownload = promisify(require('electron-download'));
/**
* The binary manager takes care of downloading Electron versions
*
* @export
* @class BinaryManager
*/
export class BinaryManager {
public state: Record<string, 'ready' | 'downloading'> = {};
export class BinaryManager extends EventEmitter {
public state: StringMap<'ready' | 'downloading'> = {};
public version: string = '2.0.2';
/**
* Remove a version from disk. Does not update state. We'll try up to
* four times before giving up if an error occurs.
*
* @param {string} iVersion
* @returns {Promise<void>}
*/
public async remove(iVersion: string): Promise<void> {
const version = normalizeVersion(iVersion);
const fs = await fancyImport<typeof fsType>('fs-extra');
let isDeleted = false;
constructor(version: string) {
super();
// utility to re-run removal functions upon failure
// due to windows filesystem lockfile jank
const rerunner = async (func: () => Promise<void>, counter: number = 1) => {
try {
await func();
} catch (error) {
console.warn(`Binary Manager: failed to run ${func.name} for ${version}, but failed`, error);
if (counter < 4) {
console.log(`Binary Manager: Trying again to run ${func.name}`);
await rerunner(func, counter + 1);
}
}
};
this.setup(version);
const binaryCleaner = async () => {
if (await this.getIsDownloaded(version)) {
// This is necessary since we're messing with .asar files inside
// the Electron binaries. Electron, powering Fiddle, will try to
// "correct" our calls, but we don't want that right here.
process.noAsar = true;
await fs.remove(this.getDownloadPath(version));
process.noAsar = false;
isDeleted = true;
}
};
const typeDefsCleaner = async () => {
await this.removeTypeDefsForVersion(version);
};
await rerunner(binaryCleaner);
if (isDeleted) {
await rerunner(typeDefsCleaner);
}
}
public async setup(iVersion: string) {
/**
* General setup, called with a version. Is called during construction
* to ensure that we always have or download at least one version.
*
* @param {string} iVersion
* @returns {Promise<void>}
*/
public async setup(iVersion: string): Promise<void> {
const version = normalizeVersion(iVersion);
const fs = await fancyImport<typeof fsType>('fs-extra');
const { promisify } = await import('util');
const eDownload = promisify(require('electron-download'));
await fs.mkdirp(this.getDownloadPath(version));
if (this.state[version] === 'downloading') {
@@ -30,7 +85,7 @@ export class BinaryManager extends EventEmitter {
return;
}
if (this.getIsDownloaded(version)) {
if (await this.getIsDownloaded(version)) {
console.log(`BinaryManager: Electron ${version} already downloaded.`);
this.state[version] = 'ready';
return;
@@ -41,18 +96,35 @@ export class BinaryManager extends EventEmitter {
const zipPath = await eDownload({ version });
const extractPath = this.getDownloadPath(version);
console.log(`BinaryManager: Electron ${version} downloaded, now unpacking`);
const electronFiles = await this.unzip(zipPath, extractPath);
console.log(electronFiles);
console.log(`BinaryManager: Electron ${version} downloaded, now unpacking to ${extractPath}`);
try {
// Ensure the target path is empty
await fs.emptyDir(extractPath);
const electronFiles = await this.unzip(zipPath, extractPath);
console.log(`Unzipped ${version}`, electronFiles);
} catch (error) {
console.warn(`Failure while unzipping ${version}`, error);
// TODO: Handle this case
}
this.state[version] = 'ready';
}
public getElectronBinary(version: string) {
const platform = os.platform();
const dir = this.getDownloadPath(version);
switch (platform) {
/**
* Gets the expected path for the binary of a given Electron version
*
* @param {string} version
* @param {string} dir
* @returns {string}
*/
public getElectronBinaryPath(
version: string,
dir: string = this.getDownloadPath(version),
): string {
switch (process.platform) {
case 'darwin':
return path.join(dir, 'Electron.app/Contents/MacOS/Electron');
case 'freebsd':
@@ -61,12 +133,18 @@ export class BinaryManager extends EventEmitter {
case 'win32':
return path.join(dir, 'electron.exe');
default:
throw new Error('Electron builds are not available on platform: ' + platform);
throw new Error(`Electron builds are not available for ${process.platform}`);
}
}
/**
* Returns an array of all versions downloaded to disk
*
* @returns {Promise<Array<string>>}
*/
public async getDownloadedVersions(): Promise<Array<string>> {
const downloadPath = path.join(userData, 'electron-bin');
const fs = await fancyImport<typeof fsType>('fs-extra');
const downloadPath = path.join(USER_DATA_PATH, 'electron-bin');
console.log(`BinaryManager: Checking for downloaded versions`);
try {
@@ -74,7 +152,7 @@ export class BinaryManager extends EventEmitter {
const knownVersions: Array<string> = [];
for (const directory of directories) {
if (this.getIsDownloaded(directory)) {
if (await this.getIsDownloaded(directory)) {
knownVersions.push(directory);
}
}
@@ -86,20 +164,63 @@ export class BinaryManager extends EventEmitter {
}
}
public getIsDownloaded(version: string) {
const expectedPath = this.getElectronBinary(version);
/**
* Did we already download a given version?
*
* @param {string} version
* @param {string} dir
* @returns {boolean}
*/
public async getIsDownloaded(version: string, dir?: string): Promise<boolean> {
const expectedPath = this.getElectronBinaryPath(version, dir);
const fs = await fancyImport<typeof fsType>('fs-extra');
return fs.existsSync(expectedPath);
}
private getDownloadPath(version: string) {
return path.join(userData, 'electron-bin', version);
public async removeTypeDefsForVersion(version: string) {
const fs = await fancyImport<typeof fsType>('fs-extra');
const _version = normalizeVersion(version);
const typeDefsDir = path.dirname(getOfflineTypeDefinitionPath(_version));
if (fs.existsSync(typeDefsDir)) {
try {
await fs.remove(typeDefsDir);
} catch (error) {
throw error;
}
}
}
private unzip(zipPath: string, extractPath: string) {
return new Promise((resolve, reject) => {
/**
* Gets the expected path for a given Electron version
*
* @param {string} version
* @returns {string}
*/
private getDownloadPath(version: string): string {
return path.join(USER_DATA_PATH, 'electron-bin', version);
}
/**
* Unzips an electron package so that we can actually use it.
*
* @param {string} zipPath
* @param {string} extractPath
* @returns {Promise<void>}
*/
private unzip(zipPath: string, extractPath: string): Promise<void> {
return new Promise(async (resolve, reject) => {
const extract = (await fancyImport<any>('extract-zip')).default;
process.noAsar = true;
extract(zipPath, { dir: extractPath }, (error) => {
const options = {
dir: extractPath,
};
extract(zipPath, options, (error: Error) => {
if (error) {
reject(error);
return;
+58
View File
@@ -0,0 +1,58 @@
import { ElectronVersion } from '../interfaces';
export class Bisector {
public revList: Array<ElectronVersion>;
public minRev: number;
public maxRev: number;
private pivot: number;
constructor(revList: Array<ElectronVersion>) {
this.getCurrentVersion = this.getCurrentVersion.bind(this);
this.continue = this.continue.bind(this);
this.calculatePivot = this.calculatePivot.bind(this);
this.revList = revList;
this.minRev = 0;
this.maxRev = revList.length - 1;
this.calculatePivot();
}
public getCurrentVersion() {
return this.revList[this.pivot];
}
public continue(isGoodVersion: boolean) {
let isBisectOver = false;
if (this.maxRev - this.minRev <= 1) {
isBisectOver = true;
}
if (isGoodVersion) {
const upPivot = Math.floor((this.maxRev - this.pivot) / 2) + this.pivot;
this.minRev = this.pivot;
if (upPivot !== this.maxRev && upPivot !== this.pivot) {
this.pivot = upPivot;
} else {
isBisectOver = true;
}
} else {
const downPivot = Math.floor((this.pivot - this.minRev) / 2) + this.minRev;
this.maxRev = this.pivot;
if (downPivot !== this.minRev && downPivot !== this.pivot) {
this.pivot = downPivot;
} else {
isBisectOver = true;
}
}
if (isBisectOver) {
return [this.revList[this.minRev], this.revList[this.maxRev]];
} else {
return this.revList[this.pivot];
}
}
private calculatePivot() {
this.pivot = Math.floor((this.maxRev - this.minRev) / 2);
}
}
+37
View File
@@ -0,0 +1,37 @@
import { remote } from 'electron';
import { observer } from 'mobx-react';
import * as React from 'react';
import { getTitle } from '../../utils/get-title';
import { AppState } from '../state';
export interface ChromeMacProps {
appState: AppState;
}
@observer
export class ChromeMac extends React.Component<ChromeMacProps> {
public handleDoubleClick = () => {
const doubleClickAction = remote.systemPreferences.getUserDefault('AppleActionOnDoubleClick', 'string');
const win = remote.getCurrentWindow();
if (doubleClickAction === 'Minimize') {
win.minimize();
} else if (doubleClickAction === 'Maximize') {
if (!win.isMaximized()) {
win.maximize();
} else {
win.unmaximize();
}
}
}
public render() {
if (process.platform !== 'darwin') return null;
return (
<div className='chrome drag' onDoubleClick={this.handleDoubleClick}>
<small>{getTitle(this.props.appState)}</small>
</div>
);
}
}
@@ -0,0 +1,131 @@
import { Button, InputGroup, Intent } from '@blueprintjs/core';
import classnames from 'classnames';
import { reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { IpcEvents } from '../../ipc-events';
import { idFromUrl, urlFromId } from '../../utils/gist';
import { ipcRendererManager } from '../ipc';
import { AppState } from '../state';
export interface AddressBarProps {
appState: AppState;
}
export interface AddressBarState {
value: string;
loaders: {
gist: any;
example: any;
};
}
@observer
export class AddressBar extends React.Component<AddressBarProps, AddressBarState> {
constructor(props: AddressBarProps) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
this.submit = this.submit.bind(this);
const { gistId } = this.props.appState;
const value = gistId ? urlFromId(gistId) : '';
const { remoteLoader } = window.ElectronFiddle.app;
this.state = {
value,
loaders: {
gist: remoteLoader.loadFiddleFromGist.bind(remoteLoader),
example: remoteLoader.loadFiddleFromElectronExample.bind(remoteLoader)
}
};
}
/**
* Handle the form's submit event, trying to load whatever
* URL was entered.
*
* @param {React.SyntheticEvent<HTMLFormElement>} event
*/
public handleSubmit(event: React.SyntheticEvent<HTMLFormElement>) {
event.preventDefault();
this.submit();
}
/**
* Commit the address bar's value to app state and load the fiddle.
*
* @memberof AddressBar
*/
public submit() {
const { remoteLoader } = window.ElectronFiddle.app;
if (this.state.value) {
remoteLoader.fetchGistAndLoad(idFromUrl(this.state.value) || this.state.value);
}
}
/**
* Once the component mounts, we'll subscribe to gistId changes
*/
public componentDidMount() {
const { appState } = this.props;
const { loaders } = this.state;
reaction(
() => appState.gistId,
(gistId: string) => this.setState({ value: urlFromId(gistId) })
);
ipcRendererManager.on(IpcEvents.LOAD_GIST_REQUEST, loaders.gist);
ipcRendererManager.on(IpcEvents.LOAD_ELECTRON_EXAMPLE_REQUEST, loaders.example);
}
public componentWillUnmount() {
const { loaders } = this.state;
ipcRendererManager.removeListener(IpcEvents.LOAD_GIST_REQUEST, loaders.gist);
ipcRendererManager.removeListener(IpcEvents.LOAD_ELECTRON_EXAMPLE_REQUEST, loaders.example);
}
/**
* Handle the change event, which usually just updates the address bar's value
*
* @param {React.ChangeEvent<HTMLInputElement>} event
*/
public handleChange(event: React.ChangeEvent<HTMLInputElement>) {
this.setState({ value: event.target.value });
}
public renderLoadButton(isValueCorrect: boolean): JSX.Element {
return (
<Button
disabled={!isValueCorrect}
icon='cloud-download'
text='Load Fiddle'
onClick={this.submit}
/>
);
}
public render() {
const { isUnsaved, isPublishing } = this.props.appState;
const { value } = this.state;
const isCorrect = /https:\/\/gist\.github\.com\/(.+)$/.test(value);
const className = classnames('address-bar', isUnsaved, { empty: !value });
return (
<form className={className} onSubmit={this.handleSubmit}>
<fieldset disabled={isPublishing}>
<InputGroup
key='addressbar'
leftIcon='geosearch'
intent={isCorrect || !value ? undefined : Intent.DANGER}
onChange={this.handleChange}
placeholder='https://gist.github.com/...'
value={value}
rightElement={this.renderLoadButton(isCorrect)}
/>
</fieldset>
</form>
);
}
}
@@ -0,0 +1,68 @@
import { Button } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as React from 'react';
import { AppState } from '../state';
interface BisectHandlerProps {
appState: AppState;
}
@observer
export class BisectHandler extends React.Component<BisectHandlerProps> {
constructor(props: BisectHandlerProps) {
super(props);
this.continueBisect = this.continueBisect.bind(this);
this.terminateBisect = this.terminateBisect.bind(this);
}
public continueBisect(isGood: boolean) {
const { appState } = this.props;
const response = appState.Bisector!.continue(isGood);
if (Array.isArray(response)) {
this.terminateBisect();
const [minRev, maxRev] = response;
appState.pushOutput(`[BISECT] Complete: Check between versions ${minRev.version} and ${maxRev.version}.`);
} else {
appState.setVersion(response.version);
}
}
public terminateBisect() {
const { appState } = this.props;
appState.Bisector = undefined;
}
public render() {
const { appState } = this.props;
if (!!appState.Bisector) {
return (
<>
<Button
icon={'thumbs-up'}
onClick={() => this.continueBisect(true)}
/>
<Button
icon={'thumbs-down'}
onClick={() => this.continueBisect(false)}
/>
<Button
icon={'cross'}
onClick={this.terminateBisect}
>
Cancel Bisect
</Button>
</>
);
} else {
return (
<Button
icon='git-branch'
text='Bisect'
onClick={appState.toggleBisectDialog}
/>
);
}
}
}
@@ -0,0 +1,100 @@
import { Button, Menu, MenuItem, Popover, Position } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ALL_EDITORS, MosaicId, PanelId } from '../../interfaces';
import { getVisibleMosaics } from '../../utils/editors-mosaic-arrangement';
import { AppState } from '../state';
import { TITLE_MAP } from './editors';
export interface EditorDropdownState {
value: string;
}
export interface EditorDropdownProps {
appState: AppState;
}
/**
* A dropdown allowing users to toggle the various editors
*
* @class EditorDropdown
* @extends {React.Component<EditorDropdownProps, EditorDropdownState>}
*/
@observer
export class EditorDropdown extends React.Component<EditorDropdownProps, EditorDropdownState> {
constructor(props: EditorDropdownProps) {
super(props);
this.onItemClick = this.onItemClick.bind(this);
}
public render() {
return (
<>
<Popover content={this.renderMenu()} position={Position.BOTTOM}>
<Button icon='applications' text='Editors' />
</Popover>
{this.renderDocsDemos()}
</>
);
}
public renderDocsDemos() {
if (!process.env.FIDDLE_DOCS_DEMOS) {
return null;
}
return (
<Button
icon='help'
text='Docs & Demos'
id={PanelId.docsDemo}
onClick={this.onItemClick}
active={!this.props.appState.closedPanels.docsDemo}
/>
);
}
public renderMenu() {
return (
<Menu>
{...this.renderMenuItems()}
</Menu>
);
}
public renderMenuItems() {
const { appState } = this.props;
const result: Array<JSX.Element> = [];
const visibleMosaics = getVisibleMosaics(appState.mosaicArrangement);
for (const id of ALL_EDITORS) {
result.push(
<MenuItem
icon={visibleMosaics.includes(id) ? 'eye-open' : 'eye-off'}
key={id}
text={TITLE_MAP[id]}
id={id}
onClick={this.onItemClick}
/>
);
}
return result;
}
public onItemClick(event: React.MouseEvent) {
const { id } = event.currentTarget;
const { appState } = this.props;
const visibleMosaics = getVisibleMosaics(appState.mosaicArrangement);
if (visibleMosaics.includes(id as MosaicId)) {
console.log(`EditorDropdown: Closing ${id}`);
appState.hideAndBackupMosaic(id as MosaicId);
} else {
console.log(`EditorDropdown: Opening ${id}`);
appState.showMosaic(id as MosaicId);
}
}
}
@@ -0,0 +1,175 @@
import { Button, ButtonGroup, Menu, MenuItem, Popover, Position } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as React from 'react';
import { when } from 'mobx';
import { IpcEvents } from '../../ipc-events';
import { INDEX_HTML_NAME, MAIN_JS_NAME, RENDERER_JS_NAME } from '../../shared-constants';
import { getOctokit } from '../../utils/octokit';
import { ipcRendererManager } from '../ipc';
import { AppState } from '../state';
export interface PublishButtonProps {
appState: AppState;
}
/**
* The "publish" button takes care of logging you in.
*
* @export
* @class PublishButton
* @extends {React.Component<PublishButtonProps, PublishButtonState>}
*/
@observer
export class PublishButton extends React.Component<PublishButtonProps> {
constructor(props: PublishButtonProps) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.publishFiddle = this.publishFiddle.bind(this);
this.setPrivate = this.setPrivate.bind(this);
this.setPublic = this.setPublic.bind(this);
}
public componentDidMount() {
ipcRendererManager.on(IpcEvents.FS_SAVE_FIDDLE_GIST, this.handleClick);
}
/**
* When the user clicks the publish button, we either show the
* authentication dialog or publish right away.
*
* If we're showing the authentication dialog, we wait for it
* to be closed again (or a GitHub token to show up) before
* we publish
*
* @returns {Promise<void>}
* @memberof PublishButton
*/
public async handleClick(): Promise<void> {
const { appState } = this.props;
if (!appState.gitHubToken) {
appState.toggleAuthDialog();
}
// Wait for the dialog to be closed again
await when(() => !!appState.gitHubToken || !appState.isTokenDialogShowing);
if (appState.gitHubToken) {
return this.publishFiddle();
}
}
/**
* Connect with GitHub, publish the current Fiddle as a gist,
* and update all related properties in the app state.
*/
public async publishFiddle(): Promise<void> {
const { appState } = this.props;
appState.isPublishing = true;
const octo = await getOctokit(this.props.appState);
const { gitHubPublishAsPublic } = this.props.appState;
const options = { includeDependencies: true, includeElectron: true };
const values = await window.ElectronFiddle.app.getEditorValues(options);
try {
const gist = await octo.gists.create({
public: !!gitHubPublishAsPublic,
description: 'Electron Fiddle Gist',
files: {
[INDEX_HTML_NAME]: {
content: values.html || '<!-- Empty -->',
},
[MAIN_JS_NAME]: {
content: values.main || '// Empty',
},
[RENDERER_JS_NAME]: {
content: values.renderer || '// Empty',
},
},
} as any); // Note: GitHub messed up, GistsCreateParamsFiles is an incorrect interface
appState.gistId = gist.data.id;
console.log(`Publish Button: Publishing done`, { gist });
} catch (error) {
console.warn(`Could not publish gist`, { error });
const messageBoxOptions: Electron.MessageBoxOptions = {
message: 'Publishing Fiddle to GitHub failed. Are you connected to the Internet?',
detail: `GitHub encountered the following error: ${error.message}`
};
ipcRendererManager.send(IpcEvents.SHOW_WARNING_DIALOG, messageBoxOptions);
}
appState.isPublishing = false;
}
/**
* Publish fiddles as private.
*
* @memberof PublishButton
*/
public setPrivate() {
this.setPrivacy(false);
}
/**
* Publish fiddles as public.
*
* @memberof PublishButton
*/
public setPublic() {
this.setPrivacy(true);
}
public render() {
const { gitHubPublishAsPublic } = this.props.appState;
const { isPublishing } = this.props.appState;
const privacyIcon = gitHubPublishAsPublic ? 'unlock' : 'lock';
const privacyMenu = (
<Menu>
<MenuItem
text='Private'
icon='lock'
active={!gitHubPublishAsPublic}
onClick={this.setPrivate}
/>
<MenuItem
text='Public'
icon='unlock'
active={gitHubPublishAsPublic}
onClick={this.setPublic}
/>
</Menu>
);
return (
<fieldset disabled={isPublishing}>
<ButtonGroup className='button-publish'>
<Popover
content={privacyMenu}
position={Position.BOTTOM}
>
<Button
icon={privacyIcon}
/>
</Popover>
<Button
onClick={this.handleClick}
loading={isPublishing}
icon='upload'
text={isPublishing ? 'Publishing...' : 'Publish'}
/>
</ButtonGroup>
</fieldset>
);
}
private setPrivacy(publishAsPublic: boolean) {
this.props.appState.gitHubPublishAsPublic = publishAsPublic;
}
}
@@ -0,0 +1,53 @@
import { Button, IButtonProps, Spinner } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ElectronVersionState } from '../../interfaces';
import { AppState } from '../state';
export interface RunnerState {
}
export interface RunnerProps {
appState: AppState;
}
/**
* The runner component is responsible for actually launching the fiddle
* with Electron. It also renders the button that does so.
*
* @class Runner
* @extends {React.Component<RunnerProps, RunnerState>}
*/
@observer
export class Runner extends React.Component<RunnerProps, RunnerState> {
public render() {
const { isRunning, currentElectronVersion } = this.props.appState;
const state = currentElectronVersion && currentElectronVersion.state;
const props: IButtonProps = { className: 'button-run' };
if (state === ElectronVersionState.downloading) {
props.text = 'Downloading';
props.disabled = true;
props.icon = <Spinner size={16} />;
} else if (state === ElectronVersionState.ready) {
if (isRunning) {
props.active = true;
props.text = 'Stop';
props.onClick = window.ElectronFiddle.app.runner.stop;
props.icon = 'stop';
} else {
props.text = 'Run';
props.onClick = window.ElectronFiddle.app.runner.run;
props.icon = 'play';
}
} else {
props.text = 'Checking status';
props.disabled = true;
props.icon = <Spinner size={16} />;
}
return <Button {...props} type={undefined} />;
}
}
@@ -0,0 +1,172 @@
import { Button, ButtonGroup, MenuItem } from '@blueprintjs/core';
import { ItemPredicate, ItemRenderer, Select } from '@blueprintjs/select';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ElectronVersion, ElectronVersionSource, ElectronVersionState } from '../../interfaces';
import { highlightText } from '../../utils/highlight-text';
import { sortedElectronMap } from '../../utils/sorted-electron-map';
import { AppState } from '../state';
import { getReleaseChannel } from '../versions';
const ElectronVersionSelect = Select.ofType<ElectronVersion>();
/**
* Helper method: Returns the <Select /> label for an Electron
* version.
*
* @param {ElectronVersion} { source, state }
* @returns {string}
*/
export function getItemLabel({ source, state, name }: ElectronVersion): string {
let label = '';
if (source === ElectronVersionSource.local) {
label = name || 'Local';
} else {
if (state === ElectronVersionState.unknown) {
label = `Not downloaded`;
} else if (state === ElectronVersionState.ready) {
label = `Downloaded`;
} else if (state === ElectronVersionState.downloading) {
label = `Downloading`;
}
}
return label;
}
/**
* Helper method: Returns the <Select /> icon for an Electron
* version.
*
* @param {ElectronVersion} { state }
* @returns
*/
export function getItemIcon({ state }: ElectronVersion) {
return state === 'ready'
? 'saved'
: state === 'downloading' ? 'cloud-download' : 'cloud';
}
/**
* Helper method: Returns the <Select /> predicate for an Electron
* version.
*
* @param {string} query
* @param {ElectronVersion} { version }
* @returns
*/
export const filterItem: ItemPredicate<ElectronVersion> = (query, { version }) => {
return version.toLowerCase().includes(query.toLowerCase());
};
/**
* Helper method: Returns the <Select /> <MenuItem /> for Electron
* versions.
*
* @param {ElectronVersion} item
* @param {IItemRendererProps} { handleClick, modifiers, query }
* @returns
*/
export const renderItem: ItemRenderer<ElectronVersion> = (item, { handleClick, modifiers, query }) => {
if (!modifiers.matchesPredicate) {
return null;
}
return (
<MenuItem
active={modifiers.active}
disabled={modifiers.disabled}
text={highlightText(item.version, query)}
key={item.version}
onClick={handleClick}
label={getItemLabel(item)}
icon={getItemIcon(item)}
/>
);
};
export interface VersionChooserState {
value: string;
}
export interface VersionChooserProps {
appState: AppState;
}
export const getVersionsFromAppState = (appState: AppState) => {
const { versions, versionsToShow, statesToShow } = appState;
return sortedElectronMap<ElectronVersion>(versions, (_key, item) => item)
.filter((item) => {
if (!item) {
return false;
}
// Check if we want to show the version
if (!versionsToShow.includes(getReleaseChannel(item))) {
return false;
}
// Check if we want to show the state
if (!statesToShow.includes(item.state)) {
return false;
}
return true;
});
};
/**
* A dropdown allowing the selection of Electron versions. The actual
* download is managed in the state.
*
* @class VersionChooser
* @extends {React.Component<VersionChooserProps, VersionChooserState>}
*/
@observer
export class VersionChooser extends React.Component<VersionChooserProps, VersionChooserState> {
constructor(props: VersionChooserProps) {
super(props);
this.onItemSelect = this.onItemSelect.bind(this);
}
/**
* Handle change, which usually means that we'd like update
* the selection version.
*
* @param {React.ChangeEvent<HTMLSelectElement>} event
*/
public onItemSelect({ version }: ElectronVersion) {
this.props.appState.setVersion(version);
}
public render() {
const { currentElectronVersion, Bisector } = this.props.appState;
const { version } = currentElectronVersion;
return (
<ButtonGroup>
<ElectronVersionSelect
filterable={true}
items={getVersionsFromAppState(this.props.appState)}
itemRenderer={renderItem}
itemPredicate={filterItem}
onItemSelect={this.onItemSelect}
noResults={<MenuItem disabled={true} text='No results.' />}
disabled={!!Bisector}
>
<Button
className='version-chooser'
text={`Electron v${version}`}
icon={getItemIcon(currentElectronVersion)}
disabled={!!Bisector}
/>
</ElectronVersionSelect>
</ButtonGroup>
);
}
}
+43 -17
View File
@@ -1,39 +1,65 @@
import * as React from 'react';
import { Button, ControlGroup } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as Icon from '@fortawesome/react-fontawesome';
import { faTerminal } from '@fortawesome/fontawesome-free-solid';
import * as React from 'react';
import { Runner } from './runner';
import { VersionChooser } from './version-chooser';
import { AppState } from '../app';
import { AppState } from '../state';
import { AddressBar } from './commands-address-bar';
import { BisectHandler } from './commands-bisect';
import { EditorDropdown } from './commands-editors';
import { PublishButton } from './commands-publish-button';
import { Runner } from './commands-runner';
import { VersionChooser } from './commands-version-chooser';
export interface CommandsProps {
appState: AppState;
}
/**
* The command bar, containing all the buttons doing
* all the things
*
* @class Commands
* @extends {React.Component<CommandsProps, {}>}
*/
@observer
export class Commands extends React.Component<CommandsProps, {}> {
constructor(props: CommandsProps) {
super(props);
this.toggleConsole = this.toggleConsole.bind(this);
}
public toggleConsole() {
this.props.appState.isConsoleShowing = !this.props.appState.isConsoleShowing;
}
public render() {
const { appState } = this.props;
const { isBisectCommandShowing: isBisectCommandShowing } = appState;
return (
<div className='commands'>
<div>
<Runner appState={this.props.appState} />
<VersionChooser appState={this.props.appState} />
<ControlGroup fill={true} vertical={false}>
<VersionChooser appState={appState} />
<Runner appState={appState} />
</ControlGroup>
{
// tslint:disable-next-line jsx-no-multiline-js
isBisectCommandShowing &&
(
<ControlGroup fill={true} vertical={false}>
<BisectHandler appState={appState} />
</ControlGroup>
)
}
<ControlGroup fill={true} vertical={false}>
<Button
active={appState.isConsoleShowing}
icon='console'
text='Console'
onClick={appState.toggleConsole}
/>
<EditorDropdown appState={appState} />
</ControlGroup>
</div>
<div>
<button className='button' onClick={this.toggleConsole}>
<Icon icon={faTerminal} />
</button>
<AddressBar appState={appState} />
<PublishButton appState={appState} />
</div>
</div>
);
@@ -0,0 +1,160 @@
import { Button, Dialog, FileInput } from '@blueprintjs/core';
import { shell } from 'electron';
import * as fsType from 'fs-extra';
import { observer } from 'mobx-react';
import * as MonacoType from 'monaco-editor';
import * as path from 'path';
import * as React from 'react';
import { getTheme, THEMES_PATH } from '../themes';
import { AppState } from '../state';
import { defaultDark, LoadedFiddleTheme } from '../themes-defaults';
export interface AddThemeDialogProps {
appState: AppState;
}
export interface AddThemeDialogState {
file?: File;
}
/**
* The "add monaco theme" dialog allows users to add custom editor themes.
*
* @class AddThemeDialog
* @extends {React.Component<AddThemeDialogProps, AddThemeDialogState>}
*/
@observer
export class AddThemeDialog extends React.Component<AddThemeDialogProps, AddThemeDialogState> {
public resetState = { file: undefined };
constructor(props: AddThemeDialogProps) {
super(props);
this.state = this.resetState;
this.onSubmit = this.onSubmit.bind(this);
this.onClose = this.onClose.bind(this);
this.onChangeFile = this.onChangeFile.bind(this);
this.reset = this.reset.bind(this);
}
/**
* Handles a change of the file input
*
* @param {React.ChangeEvent<HTMLInputElement>} event
*/
public async onChangeFile(event: React.FormEvent<HTMLInputElement>) {
const { files } = event.target as any;
const file = files && files[0] ? files[0] : undefined;
this.setState({ file });
}
/**
* Handles the submission of the dialog
*
* @returns {Promise<void>}
*/
public async onSubmit(): Promise<void> {
const { file } = this.state;
const defaultTheme = !!this.props.appState.theme ? await getTheme(this.props.appState.theme) : defaultDark;
if (!file) return;
try {
const editor = fsType.readJSONSync(file.path);
if (!editor.base && !editor.rules) throw Error('File does not match specifications'); // has to have these attributes
defaultTheme.editor = editor as Partial<MonacoType.editor.IStandaloneThemeData>;
const newTheme = defaultTheme;
const name = editor.name ? editor.name : file.name;
await this.createNewThemeFromMonaco(name, newTheme);
} catch (error) {
this.props.appState.setWarningDialogTexts({label: `Error: ${error}, please pick a different file.`});
this.props.appState.isWarningDialogShowing = true;
return;
}
this.onClose();
return;
}
public async createNewThemeFromMonaco(name: string, newTheme: LoadedFiddleTheme): Promise<boolean> {
if (!name) return false;
const themePath = path.join(THEMES_PATH, `${name}`);
await fsType.outputJSON(themePath, {
...newTheme,
name,
}, {spaces: 2});
this.props.appState.setTheme(themePath);
shell.showItemInFolder(themePath);
return true;
}
get buttons() {
const canSubmit = !!this.state.file;
return [
(
<Button
icon='add'
key='submit'
disabled={!canSubmit}
onClick={this.onSubmit}
text='Add'
/>
), (
<Button
icon='cross'
key='cancel'
onClick={this.onClose}
text='Cancel'
/>
)
];
}
public onClose() {
this.props.appState.isThemeDialogShowing = false;
this.reset();
}
public render() {
const { isThemeDialogShowing } = this.props.appState;
const inputProps = { accept: '.json' };
const { file } = this.state;
const text = file && file.path ? file.path : `Select the Monaco file...`;
return (
<Dialog
isOpen={isThemeDialogShowing}
onClose={this.onClose}
title='Add theme'
className='dialog-add-version'
>
<div className='bp3-dialog-body'>
<FileInput
onInputChange={this.onChangeFile}
inputProps={inputProps as any}
text={text}
/>
<br />
</div>
<div className='bp3-dialog-footer'>
<div className='bp3-dialog-footer-actions'>
{this.buttons}
</div>
</div>
</Dialog>
);
}
/**
* Reset this component's state
*/
private reset(): void {
this.setState(this.resetState);
return;
}
}
@@ -0,0 +1,221 @@
import { Button, Callout, Dialog, FileInput, InputGroup, Intent } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as path from 'path';
import * as React from 'react';
import * as semver from 'semver';
import { NpmVersion } from '../../interfaces';
import { IpcEvents } from '../../ipc-events';
import { getElectronNameForPlatform } from '../../utils/electron-name';
import { ipcRendererManager } from '../ipc';
import { AppState } from '../state';
export interface AddVersionDialogProps {
appState: AppState;
}
export interface AddVersionDialogState {
isValidElectron: boolean;
isValidVersion: boolean;
folderPath?: string;
version: string;
}
/**
* The "add version" dialog allows users to add custom builds of Electron.
*
* @class AddVersionDialog
* @extends {React.Component<AddVersionDialogProps, AddVersionDialogState>}
*/
@observer
export class AddVersionDialog extends React.Component<AddVersionDialogProps, AddVersionDialogState> {
constructor(props: AddVersionDialogProps) {
super(props);
this.state = {
isValidVersion: false,
isValidElectron: false,
version: ''
};
this.onSubmit = this.onSubmit.bind(this);
this.onClose = this.onClose.bind(this);
this.onChangeVersion = this.onChangeVersion.bind(this);
ipcRendererManager.on(IpcEvents.LOAD_LOCAL_VERSION_FOLDER, (_event, [file]) => {
this.setFolderPath(file);
});
}
public componentWillUnmount() {
ipcRendererManager.removeAllListeners(IpcEvents.LOAD_LOCAL_VERSION_FOLDER);
}
/**
* Handles a change of the file input
*
* @param {React.ChangeEvent<HTMLInputElement>} event
*/
public async setFolderPath(folderPath: string) {
const { binaryManager } = this.props.appState;
const isValidElectron = !!await binaryManager.getIsDownloaded('custom', folderPath);
this.setState({ folderPath, isValidElectron });
}
public onChangeVersion(event: React.ChangeEvent<HTMLInputElement>) {
const version = event.target.value || '';
const isValidVersion = !!semver.valid(version);
this.setState({
version,
isValidVersion,
});
}
/**
* Handles the submission of the dialog
*
* @returns {Promise<void>}
*/
public async onSubmit(): Promise<void> {
const { folderPath, version } = this.state;
if (!folderPath) return;
const name = folderPath
.slice(-20)
.split(path.sep)
.slice(1)
.join(path.sep);
const toAdd: NpmVersion = {
localPath: folderPath,
version,
name
};
this.props.appState.addLocalVersion(toAdd);
this.onClose();
}
/**
* Closes the dialog
*/
public onClose() {
this.props.appState.isAddVersionDialogShowing = false;
this.reset();
}
get buttons() {
const canSubmit = this.state.isValidElectron && this.state.isValidVersion;
return [
(
<Button
icon='add'
key='submit'
disabled={!canSubmit}
onClick={this.onSubmit}
text='Add'
/>
), (
<Button
icon='cross'
key='cancel'
onClick={this.onClose}
text='Cancel'
/>
)
];
}
public render() {
const { isAddVersionDialogShowing } = this.props.appState;
const inputProps = {
onClick: (e: React.MouseEvent<HTMLInputElement, MouseEvent>) => {
e.preventDefault();
ipcRendererManager.send(IpcEvents.SHOW_LOCAL_VERSION_FOLDER_DIALOG);
}
};
const { folderPath } = this.state;
const text = folderPath ||
`Select the folder containing ${getElectronNameForPlatform()}...`;
return (
<Dialog
isOpen={isAddVersionDialogShowing}
onClose={this.onClose}
title='Add local Electron build'
className='dialog-add-version'
>
<div className='bp3-dialog-body'>
<FileInput
id='custom-electron-version'
inputProps={inputProps as any}
text={text}
/>
<br />
{this.renderPath()}
</div>
<div className='bp3-dialog-footer'>
<div className='bp3-dialog-footer-actions'>
{this.buttons}
</div>
</div>
</Dialog>
);
}
private renderPath(): JSX.Element | null {
const { folderPath, isValidElectron } = this.state;
if (!folderPath) return null;
const info = isValidElectron
? `We found an ${getElectronNameForPlatform()} in this folder.`
: `We did not find a ${getElectronNameForPlatform()} in this folder...`;
return (
<Callout>
{info}
{this.renderVersionInput()}
</Callout>
);
}
private renderVersionInput(): JSX.Element | null {
const { isValidElectron, isValidVersion, version } = this.state;
if (!isValidElectron) return null;
return (
<>
<p>
Please specify a version, used for typings and the name.
Must be <code>semver</code> compliant.
</p>
<InputGroup
intent={isValidVersion ? undefined : Intent.DANGER}
value={version}
onChange={this.onChangeVersion}
placeholder='4.0.0'
/>
</>
);
}
/**
* Reset this component's state
*/
private reset(): void {
this.setState({
isValidElectron: false,
isValidVersion: false,
version: '',
folderPath: undefined
});
}
}
+164
View File
@@ -0,0 +1,164 @@
import { Button, Dialog, Label, MenuItem } from '@blueprintjs/core';
import { Select } from '@blueprintjs/select';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ElectronVersion } from '../../interfaces';
import { Bisector } from '../bisect';
import { AppState } from '../state';
import { filterItem, getItemIcon, getVersionsFromAppState, renderItem } from './commands-version-chooser';
const ElectronVersionSelect = Select.ofType<ElectronVersion>();
export interface BisectDialogProps {
appState: AppState;
}
export interface BisectDialogState {
startIndex?: number;
endIndex?: number;
allVersions: Array<ElectronVersion>;
}
/**
* The "add version" dialog allows users to add custom builds of Electron.
*
* @class AddVersionDialog
* @extends {React.Component<BisectDialogProps, BisectDialogState>}
*/
@observer
export class BisectDialog extends React.Component<BisectDialogProps, BisectDialogState> {
constructor(props: BisectDialogProps) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
this.onClose = this.onClose.bind(this);
this.onBeginSelect = this.onBeginSelect.bind(this);
this.onEndSelect = this.onEndSelect.bind(this);
const allVersions = getVersionsFromAppState(this.props.appState);
this.state = { allVersions };
}
public onBeginSelect(version: ElectronVersion) {
this.setState({ startIndex: this.state.allVersions.indexOf(version) });
}
public onEndSelect(version: ElectronVersion) {
this.setState({ endIndex: this.state.allVersions.indexOf(version) });
}
/**
* Handles the submission of the dialog
*
* @returns {Promise<void>}
*/
public async onSubmit(): Promise<void> {
const { endIndex, startIndex, allVersions } = this.state;
const { appState } = this.props;
if (endIndex === undefined || startIndex === undefined) {
return;
}
const bisectRange = allVersions
.slice(endIndex, startIndex + 1)
.reverse();
appState.Bisector = new Bisector(bisectRange);
const initialBisectPivot = appState.Bisector.getCurrentVersion().version;
appState.setVersion(initialBisectPivot);
this.onClose();
}
/**
* Closes the dialog
*/
public onClose() {
this.props.appState.isBisectDialogShowing = false;
}
get buttons() {
const canSubmit =
!!this.state.startIndex &&
!!this.state.endIndex &&
this.state.startIndex > this.state.endIndex;
return [
(
<Button
icon='play'
key='submit'
disabled={!canSubmit}
onClick={this.onSubmit}
text='Begin'
/>
), (
<Button
icon='cross'
key='cancel'
onClick={this.onClose}
text='Cancel'
/>
)
];
}
public render() {
const { isBisectDialogShowing } = this.props.appState;
const { startIndex, endIndex, allVersions } = this.state;
return (
<Dialog
isOpen={isBisectDialogShowing}
onClose={this.onClose}
title='Start a bisect session'
className='dialog-add-version'
>
<div className='bp3-dialog-body'>
<Label>
Earliest Version
<ElectronVersionSelect
filterable={true}
items={allVersions}
itemRenderer={renderItem}
itemPredicate={filterItem}
onItemSelect={this.onBeginSelect}
noResults={<MenuItem disabled={true} text='No results.' />}
>
<Button
text={startIndex ? `v${allVersions[startIndex].version}` : ``}
icon={startIndex ? getItemIcon(allVersions[startIndex]) : 'small-minus'}
fill={true}
/>
</ElectronVersionSelect>
</Label>
<Label>
Latest Version
<ElectronVersionSelect
filterable={true}
items={allVersions.slice(0, startIndex!)}
itemRenderer={renderItem}
itemPredicate={filterItem}
onItemSelect={this.onEndSelect}
noResults={<MenuItem disabled={true} text='No results.' />}
disabled={!startIndex}
>
<Button
text={endIndex ? `v${allVersions[endIndex].version}` : ``}
icon={endIndex ? getItemIcon(allVersions[endIndex]) : 'small-minus'}
fill={true}
disabled={!startIndex}
/>
</ElectronVersionSelect>
</Label>
</div>
<div className='bp3-dialog-footer'>
<div className='bp3-dialog-footer-actions'>
{this.buttons}
</div>
</div>
</Dialog>
);
}
}
@@ -0,0 +1,51 @@
import { Alert, Intent } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as React from 'react';
import { AppState } from '../state';
export interface ConfirmDialogProps {
appState: AppState;
}
export interface ConfirmDialogState {
}
/**
* The token dialog prompts the user to either continue or cancel the operation.
*
* @export
* @class ConfirmDialog
* @extends {React.Component<ConfirmDialogProps, ConfirmDialogState>}
*/
@observer
export class ConfirmDialog extends React.Component<ConfirmDialogProps, ConfirmDialogState> {
constructor(props: ConfirmDialogProps) {
super(props);
this.onClose = this.onClose.bind(this);
}
public onClose(result: boolean) {
this.props.appState.confirmationPromptLastResult = result;
this.props.appState.toggleConfirmationPromptDialog();
}
public render() {
const { isConfirmationPromptShowing, confirmationDialogTexts } = this.props.appState;
return (
<Alert
isOpen={isConfirmationPromptShowing}
onClose={this.onClose}
icon='help'
confirmButtonText={confirmationDialogTexts.ok}
cancelButtonText={confirmationDialogTexts.cancel}
intent={Intent.PRIMARY}
>
<p>{confirmationDialogTexts.label}</p>
</Alert>
);
}
}
+197
View File
@@ -0,0 +1,197 @@
import { Button, Callout, Dialog, InputGroup, Intent } from '@blueprintjs/core';
import { clipboard, shell } from 'electron';
import { observer } from 'mobx-react';
import * as React from 'react';
import { getOctokit } from '../../utils/octokit';
import { AppState } from '../state';
export interface TokenDialogProps {
appState: AppState;
}
export interface TokenDialogState {
tokenInput: string;
verifying: boolean;
error: boolean;
}
const TOKEN_SCOPES = [ 'gist' ].join();
const TOKEN_DESCRIPTION = encodeURIComponent('Fiddle Gist Token');
const GENERATE_TOKEN_URL = `https://github.com/settings/tokens/new?scopes=${TOKEN_SCOPES}&description=${TOKEN_DESCRIPTION}`;
/**
* The token dialog asks the user for a GitHub Personal Access Token.
* It's also responsible for checking if the token is correct.
*
* @export
* @class TokenDialog
* @extends {React.Component<TokenDialogProps, TokenDialogState>}
*/
@observer
export class TokenDialog extends React.Component<TokenDialogProps, TokenDialogState> {
constructor(props: TokenDialogProps) {
super(props);
this.state = {
verifying: false,
error: false,
tokenInput: ''
};
this.onSubmitToken = this.onSubmitToken.bind(this);
this.openGenerateTokenExternal = this.openGenerateTokenExternal.bind(this);
this.onTokenInputFocused = this.onTokenInputFocused.bind(this);
this.handleChange = this.handleChange.bind(this);
this.onClose = this.onClose.bind(this);
}
/**
* Handles the submission of a token
*
* @returns {Promise<void>}
* @memberof TokenDialog
*/
public async onSubmitToken(): Promise<void> {
if (!this.state.tokenInput) return;
this.setState({ verifying: true });
this.props.appState.gitHubToken = this.state.tokenInput;
const octo = await getOctokit(this.props.appState);
try {
const { data } = await octo.users.getAuthenticated();
this.props.appState.gitHubAvatarUrl = data.avatar_url;
this.props.appState.gitHubLogin = data.login;
this.props.appState.gitHubName = data.name;
this.setState({ verifying: false, error: false });
} catch (error) {
console.warn(`Authenticating against GitHub failed`, error);
this.setState({ verifying: false, error: true });
return;
}
this.props.appState.isTokenDialogShowing = false;
}
/**
* Closes the dialog
*/
public onClose() {
this.props.appState.isTokenDialogShowing = false;
this.reset();
}
/**
* Reset this component's state
*/
public reset(): void {
this.setState({
verifying: false,
error: false,
tokenInput: ''
});
}
/**
* Opens GitHub's page for token generation
*
* @memberof TokenDialog
*/
public openGenerateTokenExternal() {
shell.openExternal(GENERATE_TOKEN_URL);
}
/**
* When the input field receives focus, we check the clipboard.
* Maybe there's already something token-like there!
*
* @returns
* @memberof TokenDialog
*/
public onTokenInputFocused() {
const text = (clipboard.readText() || '').trim();
if (text.length !== 40) return;
if (!/^[a-z0-9]+$/.test(text)) return;
this.setState({ tokenInput: text });
}
/**
* Handle the change event, which usually just updates the address bar's value
*
* @param {React.ChangeEvent<HTMLInputElement>} event
* @memberof AddressBar
*/
public handleChange(event: React.ChangeEvent<HTMLInputElement>) {
this.setState({ tokenInput: event.target.value });
}
get buttons() {
const canSubmit = !!this.state.tokenInput;
return [
(
<Button
key='done'
disabled={!canSubmit}
onClick={this.onSubmitToken}
loading={this.state.verifying}
text='Done'
icon='log-in'
/>
), (
<Button
key='cancel'
text='Cancel'
icon='log-out'
onClick={this.onClose}
/>
)
];
}
get invalidWarning() {
return (
<>
<Callout intent={Intent.DANGER}>
Please provide a valid GitHub Personal Access Token
</Callout>
<br />
</>
);
}
public render() {
const { isTokenDialogShowing } = this.props.appState;
return (
<Dialog
isOpen={isTokenDialogShowing}
onClose={this.onClose}
title='GitHub Token'
>
<div className='bp3-dialog-body'>
<p>
Generate a <a onClick={this.openGenerateTokenExternal}>GitHub Personal Access Token</a> and paste it here:
</p>
{this.state.error ? this.invalidWarning : null}
<InputGroup
value={this.state.tokenInput || ''}
onFocus={this.onTokenInputFocused}
onChange={this.handleChange}
/>
</div>
<div className='bp3-dialog-footer'>
<div className='bp3-dialog-footer-actions'>
{this.buttons}
</div>
</div>
</Dialog>
);
}
}
@@ -0,0 +1,50 @@
import { Alert, Intent } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as React from 'react';
import { AppState } from '../state';
export interface WarningDialogProps {
appState: AppState;
}
export interface WarningDialogState {
}
/**
* The token dialog prompts the user to either continue or cancel the operation.
*
* @export
* @class WarningDialog
* @extends {React.Component<WarningDialogProps, WarningDialogState>}
*/
@observer
export class WarningDialog extends React.Component<WarningDialogProps, WarningDialogState> {
constructor(props: WarningDialogProps) {
super(props);
this.onClose = this.onClose.bind(this);
}
public onClose(result: boolean) {
this.props.appState.warningDialogLastResult = result;
this.props.appState.toggleWarningDialog();
}
public render() {
const { isWarningDialogShowing, warningDialogTexts } = this.props.appState;
return (
<Alert
isOpen={isWarningDialogShowing}
onClose={this.onClose}
icon='warning-sign'
confirmButtonText={warningDialogTexts.ok}
cancelButtonText={warningDialogTexts.cancel}
intent={Intent.DANGER}
>
<p>{warningDialogTexts.label}</p>
</Alert>
);
}
}
+63
View File
@@ -0,0 +1,63 @@
import { observer } from 'mobx-react';
import * as React from 'react';
import { AppState } from '../state';
import { AddThemeDialog } from './dialog-add-theme';
import { AddVersionDialog } from './dialog-add-version';
import { BisectDialog } from './dialog-bisect';
import { ConfirmDialog } from './dialog-confirm';
import { TokenDialog } from './dialog-token';
import { WarningDialog } from './dialog-warning';
import { Settings } from './settings';
export interface DialogsProps {
appState: AppState;
}
/**
* Dialogs (like the GitHub PAT input).
*
* @class Dialogs
* @extends {React.Component<DialogsProps, {}>}
*/
@observer
export class Dialogs extends React.Component<DialogsProps, {}> {
public render() {
const { appState } = this.props;
const {
isTokenDialogShowing,
isSettingsShowing,
isAddVersionDialogShowing,
isThemeDialogShowing,
isBisectDialogShowing
} = appState;
const maybeToken = isTokenDialogShowing
? <TokenDialog key='dialogs' appState={appState} />
: null;
const maybeSettings = isSettingsShowing
? <Settings key='settings' appState={appState} />
: null;
const maybeAddLocalVersion = isAddVersionDialogShowing
? <AddVersionDialog key='add-version-dialog' appState={appState} />
: null;
const maybeMonaco = isThemeDialogShowing ? <AddThemeDialog appState={appState} />
: null;
const maybeBisect = isBisectDialogShowing
? <BisectDialog key='bisect-dialog' appState={appState} />
: null;
const eitherWarningOrPrompt = appState.isWarningDialogShowing
? <WarningDialog appState={appState} />
: <ConfirmDialog appState={appState} />;
return (
<div key='dialogs' className='dialogs'>
{maybeToken}
{maybeSettings}
{maybeAddLocalVersion}
{maybeMonaco}
{maybeBisect}
{eitherWarningOrPrompt}
</div>
);
}
}
-13
View File
@@ -1,13 +0,0 @@
import * as React from 'react';
export class EditorTitle extends React.Component {
public render() {
return (
<div className='editorTitle'>
<span>Main Process</span>
<span>Renderer Process</span>
<span>HTML</span>
</div>
);
}
}
+131
View File
@@ -0,0 +1,131 @@
// Credit goes in large part to https://github.com/superRaytin/react-monaco-editor,
// this component is a changed version of it.
import * as MonacoType from 'monaco-editor';
import * as React from 'react';
import { EditorId } from '../../interfaces';
import { getContent } from '../content';
import { AppState } from '../state';
export interface EditorProps {
appState: AppState;
monaco: typeof MonacoType;
monacoOptions: MonacoType.editor.IEditorOptions;
id: EditorId;
options?: Partial<MonacoType.editor.IEditorConstructionOptions>;
editorDidMount?: (editor: MonacoType.editor.IStandaloneCodeEditor) => void;
onChange?: (value: string, event: MonacoType.editor.IModelContentChangedEvent) => void;
}
export class Editor extends React.Component<EditorProps> {
public editor: MonacoType.editor.IStandaloneCodeEditor;
public language: string = 'javascript';
public value: string = '';
private containerRef = React.createRef<HTMLDivElement>();
constructor(props: EditorProps) {
super(props);
this.language = props.id === 'html' ? 'html' : 'javascript';
}
public shouldComponentUpdate() {
return false;
}
public componentDidMount() {
this.initMonaco();
}
public componentWillUnmount() {
this.destroyMonaco();
}
/**
* Handle the editor having been mounted. This refers to Monaco's
* mount, not React's.
*
* @param {MonacoType.editor.IStandaloneCodeEditor} editor
*/
public async editorDidMount(editor: MonacoType.editor.IStandaloneCodeEditor) {
const { editorDidMount } = this.props;
// Set the content on the editor
await this.setContent();
// Set the editor as an available object
window.ElectronFiddle.editors[this.props.id] = editor;
// And notify others
if (editorDidMount) {
editorDidMount(editor);
}
}
/**
* Initialize Monaco.
*/
public async initMonaco() {
const { monaco, monacoOptions: monacoOptions } = this.props;
const ref = this.containerRef.current;
if (ref) {
this.editor = monaco.editor.create(ref, {
language: this.language,
theme: 'main',
contextmenu: false,
model: null,
...monacoOptions
});
await this.editorDidMount(this.editor);
}
}
/**
* Destroy Monaco.
*/
public destroyMonaco() {
if (typeof this.editor !== 'undefined') {
console.log('Editor: Disposing');
this.editor.dispose();
}
}
public render() {
return <div className='editorContainer' ref={this.containerRef} />;
}
/**
* Sets the content on the editor, including the model and the view state.
*
* @private
* @memberof Editor
*/
private async setContent() {
const { appState, id, monaco } = this.props;
const { version } = appState;
const backup = appState.getAndRemoveEditorValueBackup(id);
if (backup && backup.model) {
console.log(`Editor: Backup found, restoring state`);
if (backup.viewState) {
this.editor.restoreViewState(backup.viewState);
}
this.editor.setModel(backup.model);
} else {
const value = await getContent(id, version);
const model = monaco.editor.createModel(value, this.language);
model.updateOptions({
tabSize: 2
});
this.editor.setModel(model);
}
}
}
@@ -0,0 +1,24 @@
import * as React from 'react';
import { Button, NonIdealState } from '@blueprintjs/core';
import { EditorId } from '../../interfaces';
import { AppState } from '../state';
export function renderNonIdealState(appState: AppState) {
const allEditors = [ EditorId.html, EditorId.main, EditorId.renderer ];
const resolveButton = (
<Button
text='Open all editors'
onClick={() => appState.setVisibleMosaics(allEditors)}
/>
);
return (
<NonIdealState
action={resolveButton}
icon='applications'
description='You have closed all editors. You can open them again with the button below or the "Editors" button above!'
/>
);
}
@@ -0,0 +1,88 @@
import * as React from 'react';
import { Button } from '@blueprintjs/core';
import { MosaicContext, MosaicRootActions, MosaicWindowContext } from 'react-mosaic-component';
import { DocsDemoPage, MosaicId } from '../../interfaces';
import { AppState } from '../state';
export interface ToolbarButtonProps {
appState: AppState;
id: MosaicId;
}
export abstract class ToolbarButton extends React.PureComponent<ToolbarButtonProps> {
public static contextType = MosaicWindowContext;
public context: MosaicWindowContext;
constructor(props: ToolbarButtonProps) {
super(props);
}
public render() {
return (
<MosaicContext.Consumer>
{({ mosaicActions }) => this.createButton(mosaicActions)}
</MosaicContext.Consumer>
);
}
/**
* Create a button that performs the actual action
*/
public abstract createButton(_mosaicActions: MosaicRootActions<any>): React.ReactNode;
}
export class MaximizeButton extends ToolbarButton {
/**
* Create a button that can expand this panel
*/
public createButton(mosaicActions: MosaicRootActions<any>) {
const onClick = () => {
mosaicActions.expand(this.context.mosaicWindowActions.getPath());
};
return (
<Button
icon='maximize'
className='bp3-small'
onClick={onClick}
/>
);
}
}
export class RemoveButton extends ToolbarButton {
/**
* Create a button that can remove this panel
*/
public createButton(_mosaicActions: MosaicRootActions<any>) {
const onClick = () => this.props.appState.hideAndBackupMosaic(this.props.id);
return (
<Button
icon='cross'
className='bp3-small'
onClick={onClick}
/>
);
}
}
export class DocsDemoGoHomeButton extends ToolbarButton {
/**
* Create a button that can remove this panel
*/
public createButton(_mosaicActions: MosaicRootActions<any>) {
const onClick = () => this.props.appState.currentDocsDemoPage = DocsDemoPage.DEFAULT;
return (
<Button
icon='home'
className='bp3-small'
onClick={onClick}
text='Overview'
/>
);
}
}
+294
View File
@@ -0,0 +1,294 @@
import { reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as MonacoType from 'monaco-editor';
import * as React from 'react';
import { Mosaic, MosaicBranch, MosaicNode, MosaicWindow, MosaicWindowProps } from 'react-mosaic-component';
import { EditorId, MosaicId, PanelId } from '../../interfaces';
import { IpcEvents } from '../../ipc-events';
import { updateEditorLayout } from '../../utils/editor-layout';
import { getFocusedEditor } from '../../utils/focused-editor';
import { getAtPath, setAtPath } from '../../utils/js-path';
import { toggleMonaco } from '../../utils/toggle-monaco';
import { isEditorId } from '../../utils/type-checks';
import { getContent } from '../content';
import { ipcRendererManager } from '../ipc';
import { AppState } from '../state';
import { activateTheme } from '../themes';
import { Editor } from './editor';
import { renderNonIdealState } from './editors-non-ideal-state';
import { DocsDemoGoHomeButton, MaximizeButton, RemoveButton } from './editors-toolbar-button';
import { ShowMe } from './show-me';
const defaultMonacoOptions: MonacoType.editor.IEditorOptions = {
minimap: {
enabled: false
},
wordWrap: 'on'
};
export const TITLE_MAP: Record<MosaicId, string> = {
main: 'Main Process (main.js)',
renderer: 'Renderer Process (renderer.js)',
preload: 'Preload (preload.js)',
html: 'HTML (index.html)',
docsDemo: 'Docs & Demos',
};
export interface EditorsProps {
appState: AppState;
}
export interface EditorsState {
monaco?: typeof MonacoType;
isMounted?: boolean;
monacoOptions: MonacoType.editor.IEditorOptions;
}
@observer
export class Editors extends React.Component<EditorsProps, EditorsState> {
// A reaction: Each time mosaicArrangement is changed, we'll update
// the editor layout. That method is itself debounced.
public disposeLayoutAutorun = reaction(
() => this.props.appState.mosaicArrangement,
() => updateEditorLayout()
);
constructor(props: EditorsProps) {
super(props);
this.onChange = this.onChange.bind(this);
this.renderEditor = this.renderEditor.bind(this);
this.renderTile = this.renderTile.bind(this);
this.renderGenericPanel = this.renderGenericPanel.bind(this);
this.state = { monacoOptions: defaultMonacoOptions };
this.loadMonaco();
}
/**
* Executed right after the component mounts. We'll setup the IPC listeners here.
*
* @memberof Editors
*/
public componentDidMount() {
ipcRendererManager.on(IpcEvents.MONACO_EXECUTE_COMMAND, (_event, cmd: string) => {
this.executeCommand(cmd);
});
ipcRendererManager.on(IpcEvents.FS_NEW_FIDDLE, async (_event) => {
const { version } = this.props.appState;
await window.ElectronFiddle.app.replaceFiddle({
html: await getContent(EditorId.html, version),
renderer: await getContent(EditorId.renderer, version),
main: await getContent(EditorId.main, version),
}, {});
});
ipcRendererManager.on(IpcEvents.MONACO_TOGGLE_OPTION, (_event, cmd: string) => {
this.toggleEditorOption(cmd);
});
this.setState({ isMounted: true });
}
public componentWillUnmount() {
this.disposeLayoutAutorun();
}
/**
* Attempt to execute a given commandId on the currently focused editor
*
* @param {string} commandId
* @memberof Editors
*/
public executeCommand(commandId: string) {
const editor = getFocusedEditor();
if (editor) {
const command = editor.getAction(commandId);
console.log(`Editors: Trying to run ${command.id}. Supported: ${command.isSupported}`);
if (command && command.isSupported()) {
command.run();
}
}
}
public toggleEditorOption(path: string): boolean {
if (!window.ElectronFiddle.editors) {
return false;
}
try {
const { monacoOptions } = this.state;
const newOptions = { ...monacoOptions };
const currentSetting = getAtPath(path, newOptions);
setAtPath(path, newOptions, toggleMonaco(currentSetting));
Object.keys(window.ElectronFiddle.editors)
.forEach((key) => {
const editor: MonacoType.editor.IStandaloneCodeEditor | null
= window.ElectronFiddle.editors[key];
if (editor) {
editor.updateOptions(newOptions);
}
});
this.setState({ monacoOptions: newOptions });
return true;
} catch (error) {
console.warn(`Editors: Could not toggle property ${path}`, error);
return false;
}
}
/**
* Renders the little tool bar on top of each panel
*
* @param {MosaicWindowProps<MosaicId>} { title }
* @param {MosaicId} id
* @returns {JSX.Element}
*/
public renderToolbar(
{ title }: MosaicWindowProps<MosaicId>, id: MosaicId
): JSX.Element {
const docsDemoGoHomeMaybe = id === PanelId.docsDemo
? <DocsDemoGoHomeButton id={id} appState={this.props.appState} />
: null;
return (
<div>
{/* Left */}
<div>
<h5>
{title}
</h5>
</div>
{/* Middle */}
<div />
{/* Right */}
<div className='mosaic-controls'>
{docsDemoGoHomeMaybe}
<MaximizeButton id={id} appState={this.props.appState} />
<RemoveButton id={id} appState={this.props.appState} />
</div>
</div>
);
}
/**
* Renders a Mosaic tile
*
* @param {string} id
* @param {string} path
* @returns {JSX.Element | null}
*/
public renderTile(id: MosaicId, path: Array<MosaicBranch>): JSX.Element {
const { appState } = this.props;
const content = isEditorId(id)
? this.renderEditor(id)
: this.renderGenericPanel(id, appState);
return (
<MosaicWindow<EditorId>
className={id}
path={path}
title={TITLE_MAP[id]}
renderToolbar={(props: MosaicWindowProps<MosaicId>) => this.renderToolbar(props, id)}
>
{content}
</MosaicWindow>
);
}
/**
* Renders a generic panel  so not an editor, but something else
*
* @param {PanelId} _id
* @param {AppState} _appState
* @returns {JSX.Element}
*/
public renderGenericPanel(_id: PanelId, appState: AppState): JSX.Element {
return <ShowMe appState={appState} />;
}
/**
* Render an editor
*
* @param {EditorId} id
* @returns {(JSX.Element | null)}
* @memberof Editors
*/
public renderEditor(id: EditorId): JSX.Element | null {
const { appState } = this.props;
const { monaco } = this.state;
return (
<Editor
id={id}
monaco={monaco!}
appState={appState}
monacoOptions={defaultMonacoOptions}
/>
);
}
public render() {
const { appState } = this.props;
const { monaco } = this.state;
if (!monaco) return null;
return (
<Mosaic<EditorId | PanelId>
onChange={this.onChange}
value={appState.mosaicArrangement}
zeroStateView={renderNonIdealState(appState)}
renderTile={this.renderTile}
/>
);
}
/**
* Handles a change in the visible nodes
*
* @param {(MosaicNode<EditorId> | null)} currentNode
*/
public onChange(currentNode: MosaicNode<EditorId> | null) {
this.props.appState.mosaicArrangement = currentNode;
}
/**
* Loads monaco. If it's already loaded, it'll just set it on the current state.
* We're doing things a bit roundabout to ensure that we're not overloading the
* mobx state with a gigantic Monaco tree.
*/
public async loadMonaco(): Promise<void> {
const { app } = window.ElectronFiddle;
const loader = require('monaco-loader');
const monaco = app.monaco || await loader();
if (!app.monaco) {
app.monaco = monaco;
}
if (!this.state || !this.state.isMounted) {
this.state = {
monaco,
monacoOptions: defaultMonacoOptions
};
} else {
this.setState({ monaco });
}
activateTheme(monaco, undefined, this.props.appState.theme);
}
}
+19 -10
View File
@@ -1,21 +1,30 @@
import * as React from 'react';
import { observer } from 'mobx-react';
import { EditorTitle } from './editor-title';
import { AppState } from '../state';
import { ChromeMac } from './chrome-mac';
import { Commands } from './commands';
import { AppState } from '../app';
import { Output } from './output';
import { WelcomeTour } from './tour-welcome';
export interface HeaderProps {
appState: AppState;
}
@observer
/**
* Everything above the editors, so buttons and the address bar.
*
* @class Header
* @extends {React.Component<HeaderProps, HeaderState>}
*/
export class Header extends React.Component<HeaderProps, {}> {
public render() {
return [
<Commands key='commands' appState={this.props.appState} />,
<Output key='output' appState={this.props.appState} />,
<EditorTitle key='titles' />
];
return (
<>
<ChromeMac appState={this.props.appState} />
<header id='header'>
<Commands key='commands' appState={this.props.appState} />
</header>
<WelcomeTour appState={this.props.appState} />
</>
);
}
}
@@ -0,0 +1,59 @@
import * as React from 'react';
import { Mosaic, MosaicNode } from 'react-mosaic-component';
import { AppState } from '../state';
import { Editors } from './editors';
import { Output } from './output';
export interface WrapperProps {
appState: AppState;
}
export interface WrapperState {
mosaicArrangement: MosaicNode<WrapperMosaicId>;
}
export type WrapperMosaicId = 'output' | 'editors';
export class OutputEditorsWrapper extends React.Component<WrapperProps, WrapperState> {
private MOSAIC_ELEMENTS = {
output: <Output appState={this.props.appState} />,
editors: <Editors appState={this.props.appState} />
};
constructor(props: any) {
super(props);
this.state = {
mosaicArrangement: {
direction: 'column',
first: 'output',
second: 'editors',
splitPercentage: 25,
}
};
}
public render() {
return (
<>
<Mosaic<WrapperMosaicId>
renderTile={(id: string) => this.MOSAIC_ELEMENTS[id]}
resize={{minimumPaneSizePercentage: 0}}
value={this.state.mosaicArrangement}
onChange={this.onChange}
/>
</>
);
}
private onChange = (currentNode: any) => {
const isConsoleShowing = currentNode.splitPercentage !== 0;
if (isConsoleShowing !== this.props.appState.isConsoleShowing) {
this.props.appState.isConsoleShowing = isConsoleShowing;
}
this.setState({mosaicArrangement: currentNode});
}
}
+87 -12
View File
@@ -1,38 +1,113 @@
import * as React from 'react';
import { autorun } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { MosaicContext } from 'react-mosaic-component';
import { AppState } from '../app';
import { OutputEntry } from '../../interfaces';
import { AppState } from '../state';
import { WrapperMosaicId } from './output-editors-wrapper';
export interface CommandsProps {
appState: AppState;
// Used to keep testing conform
renderTimestamp?: (ts: number) => string;
}
/**
* This component represents the "console" that is shown
* whenever a Fiddle is launched in Electron.
*
* @class Output
* @extends {React.Component<CommandsProps, {}>}
*/
@observer
export class Output extends React.Component<CommandsProps, {}> {
public static contextType = MosaicContext;
public context: MosaicContext<WrapperMosaicId>;
private outputRef = React.createRef<HTMLDivElement>();
constructor(props: CommandsProps) {
super(props);
this.renderTimestamp = this.renderTimestamp.bind(this);
this.renderEntry = this.renderEntry.bind(this);
}
public renderEntry(entry: OutputEntry) {
const ts = new Date(entry.timestamp).toLocaleTimeString();
public componentDidMount() {
autorun(() => {
const { isConsoleShowing } = this.props.appState;
// this context should always exist, but mocking context in enzyme
// is not fully supported, so this condition makes the tests pass
if (this.context.mosaicActions && this.context.mosaicActions.expand) {
if (!isConsoleShowing) {
this.context.mosaicActions.expand(['first'], 0);
} else {
this.context.mosaicActions.expand(['first'], 25);
}
}
});
}
public componentDidUpdate() {
if (this.outputRef.current) {
this.outputRef.current.scrollTop = this.outputRef.current.scrollHeight;
}
}
/**
* Render the timestamp
*
* @param {number} ts
* @returns {string}
*/
public renderTimestamp(ts: number): string {
const { renderTimestamp } = this.props;
if (renderTimestamp) {
return renderTimestamp(ts);
} else {
return new Date(ts).toLocaleTimeString();
}
}
/**
* An individual entry might span multiple lines. To ensure that
* each line has a timestamp, this method might split up entries.
*
* @param {OutputEntry} entry
* @returns {Array<JSX.Element>}
* @memberof Output
*/
public renderEntry(entry: OutputEntry, index: number): Array<JSX.Element> {
const ts = this.renderTimestamp(entry.timestamp);
const timestamp = <span className='timestamp'>{ts}</span>;
const lines = entry.text.split(/\r?\n/);
const style: React.CSSProperties = entry.isNotPre ? { whiteSpace: 'initial' } : {};
return lines.map((text) => (
<p>{timestamp}{text}</p>
return lines.map((text, lineIndex) => (
<p style={style} key={`${entry.timestamp}--${index}--${lineIndex}`}>
{timestamp}{text}
</p>
));
}
public render() {
const { isConsoleShowing } = this.props.appState;
const className = isConsoleShowing ? 'output showing' : 'output';
public render(): JSX.Element | null {
const { output, isConsoleShowing } = this.props.appState;
const lines = this.props.appState.output.map(this.renderEntry);
if (!isConsoleShowing) {
return null;
}
// The last 1000 lines
const lines = output
.slice(Math.max(output.length - 1000, 1))
.map(this.renderEntry);
return (
<div className={className}>
<div className='output' ref={this.outputRef}>
{lines}
</div>
);
-128
View File
@@ -1,128 +0,0 @@
import * as React from 'react';
import * as tmp from 'tmp';
import * as fs from 'fs-extra';
import * as path from 'path';
import { observer } from 'mobx-react';
import { spawn, ChildProcess } from 'child_process';
import { normalizeVersion } from '../../utils/normalize-version';
import { AppState } from '../app';
import { findModules, installModules } from '../npm';
export interface RunnerState {
isRunning: boolean;
}
export interface RunnerProps {
appState: AppState;
}
@observer
export class Runner extends React.Component<RunnerProps, RunnerState> {
public child: ChildProcess | null = null;
constructor(props: RunnerProps) {
super(props);
this.run = this.run.bind(this);
this.pushData = this.pushData.bind(this);
this.stop = this.stop.bind(this);
this.state = {
isRunning: false
};
this.pushData('Console ready 🔬');
}
public render() {
const { versions, version } = this.props.appState;
const { isRunning } = this.state;
if (!versions || !version) return null;
const state = versions[normalizeVersion(version)].state;
let text = 'Run';
let action = this.run;
if (state === 'downloading') {
text = 'Downloading';
}
if (isRunning) {
text = 'Stop';
action = this.stop;
}
return <button className='button' onClick={() => action()}>{text}</button>;
}
public async stop() {
if (this.child) {
this.child.kill();
this.setState({
isRunning: false
});
}
}
public pushData(data: string | Buffer) {
const strData = data.toString();
if (strData.startsWith('Debugger listening on ws://')) return;
this.props.appState.output.push({
timestamp: Date.now(),
text: strData
});
}
public async installModules(values: any, dir: string) {
const files = [ values.main, values.renderer ];
const modules: Array<string> = [];
files.forEach((file) => {
modules.push(...findModules(file));
});
if (modules.length === 0) return;
this.pushData(`Installing npm modules: ${modules.join(', ')}...`);
this.pushData(await installModules({ dir }, ...modules));
}
public async run() {
const values = (window as any).electronFiddle.getValues();
const tmpdir = (tmp as any).dirSync();
const { binaryManager, version } = this.props.appState;
this.props.appState.isConsoleShowing = true;
try {
await fs.writeFile(path.join(tmpdir.name, 'index.html'), values.html);
await fs.writeFile(path.join(tmpdir.name, 'main.js'), values.main);
await fs.writeFile(path.join(tmpdir.name, 'renderer.js'), values.renderer);
await fs.writeFile(path.join(tmpdir.name, 'package.json'), values.package);
await this.installModules(values, tmpdir.name);
} catch (error) {
console.error('Could not write files', error);
}
if (!binaryManager.getIsDownloaded(version)) {
console.warn(`Binary ${version} not ready`);
return;
}
const binaryPath = this.props.appState.binaryManager.getElectronBinary(version);
console.log(`Binary ${binaryPath} ready, launching`);
this.child = spawn(binaryPath, [ tmpdir.name, '--inspect' ]);
this.setState({ isRunning: true });
this.pushData('Electron started.');
this.child.stdout.on('data', this.pushData);
this.child.stderr.on('data', this.pushData);
this.child.on('close', (code) => {
this.pushData(`Electron exited with code ${code.toString()}.`);
this.setState({ isRunning: false });
tmpdir.removeCallback();
});
}
}
@@ -0,0 +1,103 @@
import { Callout, Card } from '@blueprintjs/core';
import { shell } from 'electron';
import * as fs from 'fs-extra';
import * as path from 'path';
import * as React from 'react';
import { AppState } from '../state';
export interface CreditsSettingsProps {
appState: AppState;
}
export interface CreditsSettingsState {
contributors: Array<Contributor>;
}
export interface Contributor {
url: string;
api: string;
login: string;
avatar: string;
name: string;
bio: string;
location: string;
}
/**
* Settings content to manage Credits-related preferences.
*
* @class CreditsSettings
* @extends {React.Component<CreditsSettingsProps, CreditsSettingsState>}
*/
export class CreditsSettings extends React.Component<CreditsSettingsProps, CreditsSettingsState> {
constructor(props: CreditsSettingsProps) {
super(props);
this.state = {
contributors: []
};
this.getContributors();
}
/**
* Renders a list of contributors of Electron Fiddle.
*
* @returns {Array<JSX.Element>}
*/
public renderContributors(): Array<JSX.Element> {
const { contributors } = this.state;
return contributors.map(({ name, avatar, url, login, location, bio }) => {
const maybeLocation = location
? <p className='location'>📍 {location}</p>
: null;
const maybeBio = bio
? <small className='bio'>{bio}</small>
: null;
const style: React.CSSProperties = {
backgroundImage: `url(${avatar})`
};
const onClick = () => shell.openExternal(url);
return (
<Card interactive={true} key={login} className='contributor' onClick={onClick}>
<div className='avatar' style={style} />
<div className='details'>
<h5 className='name'>{name || login}</h5>
{maybeLocation}
{maybeBio}
</div>
</Card>
);
});
}
public render() {
return (
<div>
<h2>Credits</h2>
<Callout>
Electron Fiddle is, just like Electron, a free open source project
welcoming contributors of all genders, cultures, and backgrounds. We
would like to thank those who helped to make Electron Fiddle:
</Callout>
<br />
<div className='contributors'>
{this.renderContributors()}
</div>
</div>
);
}
public async getContributors() {
try {
const contributorsFile = path.join(__dirname, '../../static/contributors.json');
const contributors = await fs.readJSON(contributorsFile);
this.setState({ contributors });
} catch (error) {
console.warn(`CreditsSettings: Fetching contributors failed`, error);
}
}
}
@@ -0,0 +1,406 @@
import {
Button,
ButtonGroup,
Callout,
Checkbox,
FormGroup,
HTMLTable,
IButtonProps,
Icon,
IconName
} from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ElectronVersion, ElectronVersionSource, ElectronVersionState } from '../../interfaces';
import { normalizeVersion } from '../../utils/normalize-version';
import { sortedElectronMap } from '../../utils/sorted-electron-map';
import { AppState } from '../state';
import { ElectronReleaseChannel, getReleaseChannel } from '../versions';
export interface ElectronSettingsProps {
appState: AppState;
}
export interface ElectronSettingsState {
isDownloadingAll: boolean;
isDeletingAll: boolean;
}
/**
* Settings content to manage Electron-related preferences.
*
* @class ElectronSettings
* @extends {React.Component<ElectronSettingsProps, {}>}
*/
@observer
export class ElectronSettings extends React.Component<ElectronSettingsProps, ElectronSettingsState> {
constructor(props: ElectronSettingsProps) {
super(props);
this.handleDownloadAll = this.handleDownloadAll.bind(this);
this.handleDeleteAll = this.handleDeleteAll.bind(this);
this.handleChannelChange = this.handleChannelChange.bind(this);
this.handleStateChange = this.handleStateChange.bind(this);
this.handleDownloadClick = this.handleDownloadClick.bind(this);
this.handleAddVersion = this.handleAddVersion.bind(this);
this.state = {
isDownloadingAll: false,
isDeletingAll: false
};
}
public handleDownloadClick() {
this.props.appState.updateElectronVersions();
}
/**
* Handles a change in which channels should be displayed.
*
* @param {React.ChangeEvent<HTMLInputElement>} event
*/
public handleStateChange(
event: React.FormEvent<HTMLInputElement>
) {
const { id, checked } = event.currentTarget;
const { appState } = this.props;
if (!checked) {
appState.statesToShow = appState.statesToShow.filter((s) => s !== id);
} else {
appState.statesToShow.push(id as ElectronVersionState);
}
}
/**
* Handles a change in which channels should be displayed.
*
* @param {React.ChangeEvent<HTMLInputElement>} event
*/
public handleChannelChange(
event: React.FormEvent<HTMLInputElement>
) {
const { id, checked } = event.currentTarget;
const { appState } = this.props;
if (!checked) {
appState.versionsToShow = appState.versionsToShow.filter((c) => c !== id);
} else {
appState.versionsToShow.push(id as ElectronReleaseChannel);
}
}
/**
* Download all versions of Electron.
*
* @returns {Promise<void>}
*/
public async handleDownloadAll(): Promise<void> {
this.setState({ isDownloadingAll: true });
const { versions, downloadVersion } = this.props.appState;
for (const key of Object.keys(versions)) {
await downloadVersion(key);
}
this.setState({ isDownloadingAll: false });
}
/**
* Delete all downloaded versions of Electron.
*
* @returns {Promise<void>}
*/
public async handleDeleteAll(): Promise<void> {
this.setState({ isDeletingAll: true });
const { versions, removeVersion, version } = this.props.appState;
for (const key in versions) {
// If this isn't the currently selected version, remove it
if (normalizeVersion(key) !== normalizeVersion(version)) {
await removeVersion(key);
}
}
this.setState({ isDeletingAll: false });
}
/**
* Opens the "add local version" dialog
*/
public handleAddVersion(): void {
this.props.appState.toggleAddVersionDialog();
}
public render() {
return (
<div className='settings-electron'>
<h2>Electron Settings</h2>
<Callout>
{this.renderVersionChannelOptions()}
{this.renderVersionStateOptions()}
</Callout>
<br />
<Callout>
{this.renderAdvancedButtons()}
{this.renderTable()}
</Callout>
</div>
);
}
/**
* Renders the various buttons for advanced operations
*
* @private
* @returns {JSX.Element}
*/
private renderAdvancedButtons(): JSX.Element {
const { isDownloadingAll, isDeletingAll } = this.state;
const { isUpdatingElectronVersions } = this.props.appState;
const isWorking = isDownloadingAll || isDeletingAll;
return (
<ButtonGroup fill={true}>
<Button
disabled={isUpdatingElectronVersions}
onClick={this.handleDownloadClick}
loading={isUpdatingElectronVersions}
icon='numbered-list'
text='Update Electron Release List'
/>
<Button
disabled={isWorking}
icon='download'
onClick={this.handleDownloadAll}
text='Download All Versions'
/>
<Button
disabled={isWorking}
icon='trash'
onClick={this.handleDeleteAll}
text='Delete All Downloads'
/>
<Button
icon='document-open'
onClick={this.handleAddVersion}
text='Add Local Electron Build'
/>
</ButtonGroup>
);
}
/**
* Renders the various options for which versions should be displayed
* in the small dropdown.
*
* @private
* @returns {JSX.Element}
*/
private renderVersionStateOptions(): JSX.Element {
const { appState } = this.props;
const getIsChecked = (state: ElectronVersionState) => {
return appState.statesToShow.includes(state);
};
return (
<FormGroup
label='Include Electron versions that are:'
>
<Checkbox
checked={getIsChecked(ElectronVersionState.downloading)}
label='Downloading'
id='downloading'
onChange={this.handleStateChange}
inline={true}
/>
<Checkbox
checked={getIsChecked(ElectronVersionState.ready)}
label='Downloaded'
id='ready'
onChange={this.handleStateChange}
inline={true}
/>
<Checkbox
checked={getIsChecked(ElectronVersionState.unknown)}
label='Not Downloaded'
id='unknown'
onChange={this.handleStateChange}
inline={true}
/>
</FormGroup>
);
}
/**
* Renders the various options for which versions should be displayed
*
* @private
* @returns {JSX.Element}
*/
private renderVersionChannelOptions(): JSX.Element {
const { appState } = this.props;
const getIsChecked = (channel: ElectronReleaseChannel) => {
return appState.versionsToShow.includes(channel);
};
return (
<FormGroup
label='Include Electron versions from these release channels:'
>
<Checkbox
checked={getIsChecked(ElectronReleaseChannel.stable)}
label='Stable'
id='Stable'
onChange={this.handleChannelChange}
inline={true}
/>
<Checkbox
checked={getIsChecked(ElectronReleaseChannel.beta)}
label='Beta'
id='Beta'
onChange={this.handleChannelChange}
inline={true}
/>
<Checkbox
checked={getIsChecked(ElectronReleaseChannel.nightly)}
label='Nightly'
id='Nightly'
onChange={this.handleChannelChange}
inline={true}
/>
<Checkbox
checked={getIsChecked(ElectronReleaseChannel.unsupported)}
label='Unsupported'
id='Unsupported'
onChange={this.handleChannelChange}
inline={true}
/>
</FormGroup>
);
}
/**
* Renders the table with Electron versions.
*
* @private
* @returns {JSX.Element}
*/
private renderTable(): JSX.Element {
return (
<HTMLTable
className='electron-versions-table'
striped={true}
>
<thead>
<tr>
<th>Version</th>
<th>Status</th>
<th className='action'>Action</th>
</tr>
</thead>
<tbody>
{this.renderTableRows()}
</tbody>
</HTMLTable>
);
}
/**
* Renders the rows with Electron version, returning an Arary.
*
* @private
* @returns {Array<JSX.Element>}
*/
private renderTableRows(): Array<JSX.Element | null> {
const { versions, versionsToShow, statesToShow } = this.props.appState;
return sortedElectronMap<JSX.Element | null>(versions, (key, item) => {
// Check if we want to show the version
if (!versionsToShow.includes(getReleaseChannel(item))) {
return null;
}
// Check if we want to show the state
if (!statesToShow.includes(item.state)) {
return null;
}
return (
<tr key={item.version}>
<td>{item.version}</td>
<td>{this.renderHumanState(item)}</td>
<td className='action'>{this.renderAction(key, item)}</td>
</tr>
);
});
}
/**
* Returns a human-readable state indicator for an Electron version
*
* @param {ElectronVersion} item
* @returns {JSX.Element}
*/
private renderHumanState(item: ElectronVersion): JSX.Element {
const { state } = item;
let icon: IconName = 'box';
let humanState = 'Downloaded';
if (state === ElectronVersionState.downloading) {
icon = 'cloud-download';
humanState = 'Downloading';
} else if (state === ElectronVersionState.unknown) {
icon = 'cloud';
humanState = 'Not downloaded';
}
return (
<span>
<Icon icon={icon} /> {humanState}
</span>
);
}
/**
* Renders the action for a single Electron version
*
* @private
* @param {string} key
* @param {ElectronVersion} item
* @returns {JSX.Element}
*/
private renderAction(key: string, item: ElectronVersion): JSX.Element {
const { state, source } = item;
const { appState } = this.props;
const buttonProps: IButtonProps = {
fill: true,
small: true
};
// Already downloaded
if (state === 'ready') {
buttonProps.onClick = () => appState.removeVersion(key);
buttonProps.icon = 'trash';
buttonProps.text = source === ElectronVersionSource.local
? 'Remove'
: 'Delete';
} else if (state === 'downloading') {
buttonProps.disabled = true;
buttonProps.loading = true;
buttonProps.text = 'Downloading';
buttonProps.icon = 'cloud-download';
} else {
buttonProps.disabled = false;
buttonProps.loading = false;
buttonProps.text = 'Download';
buttonProps.icon = 'cloud-download';
buttonProps.onClick = () => appState.downloadVersion(key);
}
return <Button {...buttonProps} type={undefined} />;
}
}
@@ -0,0 +1,93 @@
import { Callout, Checkbox, FormGroup } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as React from 'react';
import { AppState } from '../state';
export interface ExecutionSettingsProps {
appState: AppState;
}
/**
* Settings content to manage execution-related preferences.
*
* @class ExecutionSettings
* @extends {React.Component<ExecutionSettingsProps, {}>}
*/
@observer
export class ExecutionSettings extends React.Component<ExecutionSettingsProps, {}> {
constructor(props: ExecutionSettingsProps) {
super(props);
this.handleDeleteDataChange = this.handleDeleteDataChange.bind(this);
this.handleElectronLoggingChange = this.handleElectronLoggingChange.bind(this);
}
/**
* Handles a change on whether or not the user data dir should be deleted
* after a run.
*
* @param {React.ChangeEvent<HTMLInputElement>} event
*/
public handleDeleteDataChange(
event: React.FormEvent<HTMLInputElement>
) {
const { checked } = event.currentTarget;
this.props.appState.isKeepingUserDataDirs = checked;
}
/**
* Handles a change on whether or not electron should log more things
*
* @param {React.ChangeEvent<HTMLInputElement>} event
*/
public handleElectronLoggingChange(
event: React.FormEvent<HTMLInputElement>
) {
const { checked } = event.currentTarget;
this.props.appState.isEnablingElectronLogging = checked;
}
public render() {
const { isKeepingUserDataDirs, isEnablingElectronLogging } = this.props.appState;
const deleteUserDirLabel = `
Whenever Electron runs, it creates a user data directory for cookies, the cache,
and various other things that it needs to keep around. Since fiddles are usually
just run once, we delete this directory after your fiddle exits. Enable this
setting to keep the user data directories around.
`.trim();
const electronLoggingLabel = `
There are some flags that Electron uses to log extra information both internally
and through Chromium. Enable this option to make Fiddle produce those logs.`.trim();
return (
<div>
<h2>Execution</h2>
<Callout>
These advanced settings control how Electron Fiddle executes your fiddles.
</Callout>
<br />
<Callout>
<FormGroup label={deleteUserDirLabel}>
<Checkbox
checked={isKeepingUserDataDirs}
label='Do not delete user data directories.'
onChange={this.handleDeleteDataChange}
/>
</FormGroup>
</Callout>
<br />
<Callout>
<FormGroup label={electronLoggingLabel}>
<Checkbox
checked={isEnablingElectronLogging}
label='Enable advanced Electron logging.'
onChange={this.handleElectronLoggingChange}
/>
</FormGroup>
</Callout>
</div>
);
}
}
@@ -0,0 +1,222 @@
import { Button, Callout, FormGroup, MenuItem } from '@blueprintjs/core';
import { ItemPredicate, ItemRenderer, Select } from '@blueprintjs/select';
import { shell } from 'electron';
import * as fsType from 'fs-extra';
import { observer } from 'mobx-react';
import * as path from 'path';
import * as React from 'react';
import { highlightText } from '../../utils/highlight-text';
import { fancyImport } from '../../utils/import';
import { AppState } from '../state';
import { getAvailableThemes, getTheme, THEMES_PATH } from '../themes';
import { LoadedFiddleTheme } from '../themes-defaults';
const ThemeSelect = Select.ofType<LoadedFiddleTheme>();
/**
* Helper method: Returns the <Select /> predicate for an Electron
* version.
*
* @param {string} query
* @param {ElectronVersion} { version }
* @returns
*/
export const filterItem: ItemPredicate<LoadedFiddleTheme> = (query, { name }) => {
return name.toLowerCase().includes(query.toLowerCase());
};
/**
* Helper method: Returns the <Select /> <MenuItem /> for Electron
* versions.
*
* @param {ElectronVersion} item
* @param {IItemRendererProps} { handleClick, modifiers, query }
* @returns
*/
export const renderItem: ItemRenderer<LoadedFiddleTheme> = (item, { handleClick, modifiers, query }) => {
if (!modifiers.matchesPredicate) {
return null;
}
return (
<MenuItem
active={modifiers.active}
disabled={modifiers.disabled}
text={highlightText(item.name, query)}
key={item.name}
onClick={handleClick}
icon='media'
/>
);
};
export interface AppearanceSettingsProps {
appState: AppState;
}
export interface AppearanceSettingsState {
themes: Array<LoadedFiddleTheme>;
selectedTheme?: LoadedFiddleTheme;
}
/**
* Settings content to manage GitHub-related preferences.
*
* @class GitHubSettings
* @extends {React.Component<AppearanceSettingsProps, {}>}
*/
@observer
export class AppearanceSettings extends React.Component<
AppearanceSettingsProps, AppearanceSettingsState
> {
public constructor(props: AppearanceSettingsProps) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.openThemeFolder = this.openThemeFolder.bind(this);
this.handleAddTheme = this.handleAddTheme.bind(this);
this.state = {
themes: []
};
getAvailableThemes().then((themes) => {
const { theme } = this.props.appState;
const selectedTheme = theme &&
themes.find(({ file }) => file === theme) || themes[0];
this.setState({ themes, selectedTheme });
});
this.createNewThemeFromCurrent = this.createNewThemeFromCurrent.bind(this);
this.openThemeFolder = this.openThemeFolder.bind(this);
}
/**
* Handle change, which usually means that we'd like update
* the current theme.
*
* @param {LoadedFiddleTheme} theme
*/
public handleChange(theme: LoadedFiddleTheme) {
this.setState({ selectedTheme: theme });
this.props.appState.setTheme(theme.file);
}
/**
* Creates a new theme from the current template.
*
* @returns {Promise<boolean>}
* @memberof AppearanceSettings
*/
public async createNewThemeFromCurrent(): Promise<boolean> {
const { appState } = this.props;
const fs = await fancyImport<typeof fsType>('fs-extra');
const theme = await getTheme(appState.theme);
try {
const namor = await fancyImport<any>('namor');
const name = namor.generate({ words: 2, numbers: 0 });
const themePath = path.join(THEMES_PATH, `${name}.json`);
await fs.outputJSON(themePath, {
...theme,
name,
file: undefined,
css: undefined
}, { spaces: 2 });
shell.showItemInFolder(themePath);
this.setState({themes: await getAvailableThemes()});
return true;
} catch (error) {
console.warn(`Themes: Failed to create new theme from current`, error);
return false;
}
}
/**
* Creates the themes folder in .electron-fiddle if one does not
* exist yet, then shows that folder in the Finder/Explorer.
*
* @returns {Promise<boolean>}
*/
public async openThemeFolder(): Promise<boolean> {
const fs = await fancyImport<typeof fsType>('fs-extra');
try {
await fs.ensureDir(THEMES_PATH);
await shell.showItemInFolder(THEMES_PATH);
return true;
} catch (error) {
console.warn(`Appearance Settings: Could not open themes folder`);
return false;
}
}
public render() {
const { selectedTheme } = this.state;
const selectedName = selectedTheme && selectedTheme.name || 'Select a theme';
return (
<div className='settings-appearance'>
<h4>Appearance</h4>
<FormGroup
label='Choose your theme'
inline={true}
>
<ThemeSelect
filterable={true}
items={this.state.themes}
itemRenderer={renderItem}
itemPredicate={filterItem}
onItemSelect={this.handleChange}
noResults={<MenuItem disabled={true} text='No results.' />}
>
<Button
text={selectedName}
icon='tint'
/>
</ThemeSelect>
</FormGroup>
<Callout>
<p>
To add themes, add JSON theme files to <a
id='open-theme-folder'
onClick={this.openThemeFolder}
>
<code>{THEMES_PATH}</code>
</a>. The easiest way to get started is to clone one of the two existing
themes and to add your own colors.
</p>
<p>
Additionally, if you wish to import a Monaco Editor theme, pick your JSON file and Fiddle will attempt to import it.
</p>
<Button
onClick={this.createNewThemeFromCurrent}
text='Create theme from current selection'
icon='duplicate'
/>
<Button
icon='document-open'
onClick={this.handleAddTheme}
text='Add a Monaco Editor theme'
/>
</Callout>
</div>
);
}
/**
* Opens the "add monaco theme" dialog
*/
public handleAddTheme(): void {
this.props.appState.toggleAddMonacoThemeDialog();
}
}
@@ -0,0 +1,60 @@
import { Callout, Checkbox, FormGroup } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as React from 'react';
import { AppState } from '../state';
export interface ConsoleSettingsProps {
appState: AppState;
}
/**
* Settings content to manage console-related preferences.
*
* @class ConsoleSettings
* @extends {React.Component<ConsoleSettingsProps, {}>}
*/
@observer
export class ConsoleSettings extends React.Component<ConsoleSettingsProps, {}> {
constructor(props: ConsoleSettingsProps) {
super(props);
this.handleClearOnRunChange = this.handleClearOnRunChange.bind(this);
}
/**
* Handles a change on whether or not the console should be cleared
* before fiddle is executed.
*
* @param {React.ChangeEvent<HTMLInputElement>} event
*/
public handleClearOnRunChange(
event: React.FormEvent<HTMLInputElement>
) {
const { checked } = event.currentTarget;
this.props.appState.isClearingConsoleOnRun = checked;
}
public render() {
const { isClearingConsoleOnRun } = this.props.appState;
const clearOnRunLabel = `
Enable this option to automatically clear the console whenever you run your
fiddle.`.trim();
return (
<div>
<h4>Console</h4>
<Callout>
<FormGroup label={clearOnRunLabel}>
<Checkbox
checked={isClearingConsoleOnRun}
label='Clear on run.'
onChange={this.handleClearOnRunChange}
/>
</FormGroup>
</Callout>
</div>
);
}
}
@@ -0,0 +1,84 @@
import { Button, Callout } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as React from 'react';
import { AppState } from '../state';
export interface GitHubSettingsProps {
appState: AppState;
}
/**
* Settings content to manage GitHub-related preferences.
*
* @class GitHubSettings
* @extends {React.Component<GitHubSettingsProps, {}>}
*/
@observer
export class GitHubSettings extends React.Component<GitHubSettingsProps, {}> {
constructor(props: GitHubSettingsProps) {
super(props);
this.signIn = this.signIn.bind(this);
}
/**
* Render the "logged out" settings experience.
*
* @returns {JSX.Element}
*/
public renderNotSignedIn(): JSX.Element {
return (
<Callout>
<p>
Your fiddles can be published as GitHub Gists -
that way you can share your fiddles with the world!
</p>
<Button onClick={this.signIn} icon='log-in' text='Sign in'/>
</Callout>
);
}
/**
* Render the "logged in" settings experience.
*
* @returns {JSX.Element}
*/
public renderSignedIn(): JSX.Element {
const { gitHubLogin } = this.props.appState;
const signOut = this.props.appState.signOutGitHub;
return (
<Callout>
<p>
Your fiddles can be published as public GitHub Gists.
Using the personal access token you gave us, we
logged you into GitHub as <code>{gitHubLogin}</code>.
</p>
<Button onClick={signOut} icon='log-out' text='Sign out'/>
</Callout>
);
}
public render() {
const { gitHubToken } = this.props.appState;
const maybeSignedIn = !!gitHubToken
? this.renderSignedIn()
: this.renderNotSignedIn();
return (
<div>
<h4>GitHub</h4>
{maybeSignedIn}
</div>
);
}
/**
* Simply shows the GitHub Token dialog.``
*/
private signIn() {
this.props.appState.isTokenDialogShowing = true;
}
}
@@ -0,0 +1,34 @@
import { Divider } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as React from 'react';
import { AppState } from '../state';
import { AppearanceSettings } from './settings-general-appearance';
import { ConsoleSettings } from './settings-general-console';
import { GitHubSettings } from './settings-general-github';
export interface GeneralSettingsProps {
appState: AppState;
}
/**
* Settings content to manage GitHub-related preferences.
*
* @class GitHubSettings
* @extends {React.Component<GeneralSettingsProps, {}>}
*/
@observer
export class GeneralSettings extends React.Component<GeneralSettingsProps, {}> {
public render() {
return (
<div>
<h2>General Settings</h2>
<AppearanceSettings appState={this.props.appState} />
<Divider />
<ConsoleSettings appState={this.props.appState} />
<Divider />
<GitHubSettings appState={this.props.appState} />
</div>
);
}
}
+161
View File
@@ -0,0 +1,161 @@
import { Icon, IconName, MenuItem } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as React from 'react';
import { AppState } from '../state';
import { CreditsSettings } from './settings-credits';
import { ElectronSettings } from './settings-electron';
import { ExecutionSettings } from './settings-execution';
import { GeneralSettings } from './settings-general';
enum SettingsSections {
General = 'General',
Electron = 'Electron',
Execution = 'Execution',
Credits = 'Credits'
}
const settingsSections = [
SettingsSections.General,
SettingsSections.Electron,
SettingsSections.Execution,
SettingsSections.Credits
];
export interface SettingsProps {
appState: AppState;
}
export interface SettingsState {
section: SettingsSections;
}
/**
* Everything above the editors, so buttons and the address bar.
*
* @class Settings
* @extends {React.Component<SettingsProps, SettingsState>}
*/
@observer
export class Settings extends React.Component<SettingsProps, SettingsState> {
constructor(props: SettingsProps) {
super(props);
this.state = {
section: SettingsSections.General
};
this.closeSettingsPanel = this.closeSettingsPanel.bind(this);
}
public componentDidMount() {
window.addEventListener('keyup', this.closeSettingsPanel, true);
}
public componentWillUnmount() {
window.removeEventListener('keyup', this.closeSettingsPanel);
}
/**
* Renders the content of the settings, usually by simply
* return the appropriate component.
*
* @returns {(JSX.Element | null)}
*/
public renderContent(): JSX.Element | null {
const { section } = this.state;
const { appState } = this.props;
if (section === SettingsSections.General) {
return <GeneralSettings appState={appState} />;
}
if (section === SettingsSections.Electron) {
return <ElectronSettings appState={appState} />;
}
if (section === SettingsSections.Execution) {
return <ExecutionSettings appState={appState} />;
}
if (section === SettingsSections.Credits) {
return <CreditsSettings appState={appState} />;
}
return null;
}
/**
* Renders the individual menu items
*
* @returns {Array<JSX.Element>}
*/
public renderOptions(): Array<JSX.Element> {
const { section } = this.state;
return settingsSections.map((name) => {
const isSelected = section === name;
const onClick = () => this.setState({ section: name });
return (
<MenuItem
onClick={onClick}
active={isSelected}
key={name}
id={`settings-link-${name}`}
text={name}
icon={this.getIconForSection(name)}
/>
);
});
}
public render() {
const { appState } = this.props;
const { isSettingsShowing } = appState;
if (!isSettingsShowing) return null;
return (
<div className='settings'>
<div className='settings-menu'>
<ul>{this.renderOptions()}</ul>
</div>
<div className='settings-content'>
<div className='settings-close' onClick={appState.toggleSettings}>
<Icon icon='cross' />
</div>
{this.renderContent()}
</div>
</div>
);
}
/**
* Get the settings icons
*
* @param {SettingsSections} section
* @memberof Settings
*/
private getIconForSection(section: SettingsSections): IconName {
if (section === SettingsSections.Credits) {
return 'heart';
} else if (section === SettingsSections.Electron) {
return 'floppy-disk';
} else if (section === SettingsSections.Execution) {
return 'play';
}
return 'cog';
}
/**
* Trigger closing of the settings panel upon Esc
*/
private closeSettingsPanel(event: KeyboardEvent) {
const { appState } = this.props;
if (event.code === 'Escape') {
appState.isSettingsShowing = false;
}
}
}
+30
View File
@@ -0,0 +1,30 @@
import { observer } from 'mobx-react';
import * as React from 'react';
import { AppState } from '../state';
import { DOCS_DEMO_COMPONENTS } from './show-me/index';
export interface ShowMeProps {
appState: AppState;
}
/**3
* The root component for "show me" content - the fourth helpful
* panel.
*
* @class ShowMe
* @extends {React.Component<ShowMeProps, ShowMeState>}
*/
@observer
export class ShowMe extends React.Component<ShowMeProps, {}> {
public render() {
const { currentDocsDemoPage: showMeName } = this.props.appState;
const Content = DOCS_DEMO_COMPONENTS[showMeName] || DOCS_DEMO_COMPONENTS.DEFAULT;
return (
<div className='show-me-panel'>
<Content appState={this.props.appState} />
</div>
);
}
}
+142
View File
@@ -0,0 +1,142 @@
import * as React from 'react';
import { Button, Callout, Icon } from '@blueprintjs/core';
import { remote } from 'electron';
import { renderMoreDocumentation } from './more-information';
import { getSubsetOnly } from './subset-only';
/**
* Show and hide button examples, only rendered on macOS
*
* @returns {JSX.Element | null}
*/
function ShowHide() {
if (process.platform !== 'darwin') return null;
const onClick = () => {
remote.app.hide();
setTimeout(() => {
remote.app.show();
remote.app.focus();
}, 1500);
};
return (
<>
<p>This button hides Electron Fiddle right away, showing it again in two seconds.</p>
<Button
id='show-hide'
icon='eye-off'
text='Hide Electron Fiddle'
onClick={onClick}
/>
</>
);
}
export function ShowMeApp(_props: any): JSX.Element {
const [ secondsLeft, setSeconds ] = React.useState(-1);
const playFocus = () => {
setSeconds(3);
setTimeout(() => setSeconds(2), 1000);
setTimeout(() => setSeconds(1), 2000);
setTimeout(() => {
remote.app.focus();
setSeconds(-1);
}, 3000);
};
const [ paths, setPaths ] = React.useState('');
const playPaths = () => {
const pathsToQuery = [
'home',
'appData',
'userData',
'temp',
'downloads',
'desktop'
];
let result = '';
pathsToQuery.forEach((item) => {
result += `${item}: ${remote.app.getPath(item as any)}\n`;
});
setPaths(result);
};
const [ metrics, setMetrics ] = React.useState('');
const playMetrics = () => {
setMetrics(JSON.stringify(remote.app.getAppMetrics(), undefined, 2));
};
return (
<>
<Icon icon='help' iconSize={40} style={{ float: 'left', margin: '0 10px 0 0' }} />
<p className='bp3r-running-text'>
The <code>app</code> module controls the app's application life-cycle. Most of the
events and methods available on this module are responsible for handling how your
interacts with the operating system or to set application-wide settings.
</p>
<h3>API Demos</h3>
{getSubsetOnly('app')}
<Callout
title='Hiding, Showing, Focussing'
icon='eye-open'
>
<p>
The app can ask the operating system for window focus. On macOS, it can additionally
request that the app be hidden or shown. Give it a try: Click on the button below,
focus another app, and wait for two seconds to see Electron Fiddle become the focused
app again.
</p>
<Button
id='focus'
icon='lightbulb'
text={`Focus Electron Fiddle${secondsLeft > 0 ? ` in ${secondsLeft}s` : ''}`}
onClick={playFocus}
/>
<ShowHide />
</Callout>
<Callout
title='Paths'
icon='folder-open'
>
<p>
Need to query information about various paths in a cross-platform manner? Electron
can help. The button queries the operating system for some of them.
</p>
<Button
id='special-paths'
icon='play'
text={`Get special directory paths`}
onClick={playPaths}
/>
<pre id='special-paths-content'>
{paths}
</pre>
</Callout>
<Callout
title='Process & Device Information'
icon='pulse'
>
<p>
Need to query information about the process or system? The <code>app</code> module lets
developers query for information about the running app, hardware, and operating system.
A good example are process metrics.
</p>
<Button
id='process-metrics'
icon='play'
text={`Get process metrics`}
onClick={playMetrics}
/>
<pre id='process-metrics-content'>
{metrics}
</pre>
</Callout>
{renderMoreDocumentation()}
</>
);
}
@@ -0,0 +1,43 @@
import { Button, Icon } from '@blueprintjs/core';
import * as React from 'react';
import { DocsDemoPage } from '../../../interfaces';
import { AppState } from '../../state';
import { DOCS_DEMO_COMPONENTS, DOCS_DEMO_NAMES } from './index';
import { renderMoreDocumentation } from './more-information';
export function ShowMeDefault(props: { appState: AppState }): JSX.Element {
const showMeMenu: Array<JSX.Element> = Object.keys(DOCS_DEMO_COMPONENTS)
.filter((key) => key !== DocsDemoPage.DEFAULT)
.map((key) => {
return (
<li key={key}>
<Button
minimal={true}
icon='play'
text={DOCS_DEMO_NAMES[key]}
onClick={() => (props.appState.currentDocsDemoPage = key as DocsDemoPage)}
/>
</li>
);
});
return (
<>
<Icon icon='help' iconSize={40} style={{ float: 'left', margin: '0 10px 0 0' }} />
<p className='bp3r-running-text'>
This panel offers useful information about Electron APIs  and
easy way to try some of the methods it offers. Fiddle has an example fiddle
for every module available in Electron.
</p>
<p>
Clicking on any of the modules below will open up a fiddle showcasing
that particular module.
</p>
<ul className='show-me-list'>
{...showMeMenu}
</ul>
{renderMoreDocumentation()}
</>
);
}
+14
View File
@@ -0,0 +1,14 @@
import { DocsDemoPage } from '../../../interfaces';
import { ShowMeApp } from './app';
import { ShowMeDefault } from './default';
export const DOCS_DEMO_COMPONENTS: Record<DocsDemoPage, any> = {
DEFAULT: ShowMeDefault,
DEMO_APP: ShowMeApp
};
export const DOCS_DEMO_NAMES: Record<DocsDemoPage, string> = {
DEFAULT: 'Home',
DEMO_APP: 'App Demos'
};
@@ -0,0 +1,32 @@
import * as React from 'react';
import { shell } from 'electron';
/**
* Renders a "more documentation" block, typically used at the end of a "show me" documentation piece.
*
* @export
* @param {string} [url='electronjs.org/docs']
* @returns {JSX.Element}
*/
export function renderMoreDocumentation(
url: string = 'electronjs.org/docs'
): JSX.Element {
const fullUrl = `https://${url}`;
const gitHubUrl = `https://github.com/electron/fiddle`;
return (
<>
<br />
<p className='b3-running-text'>
For more documentation, visit <a id='open-url' onClick={() => shell.openExternal(fullUrl)}>{url}</a>, where
you can find the full documentation for Electron.
</p>
<p className='bp3-text-muted'>
By the way, Electron Fiddle and the documentation you see here is entirely open source. If you
have ideas on how to improve it, we'd love to have your contributions! You can find the
repository on <a id='open-github' onClick={() => shell.openExternal(gitHubUrl)}>GitHub</a>.
</p>
</>
);
}
@@ -0,0 +1,26 @@
import * as React from 'react';
import { shell } from 'electron';
import { getDocsUrlForModule } from '../../../utils/docs-urls';
/**
* Returns a common string we use as a disclaimer to explain that this set of demos
* is only meant to be a short demonstration, not displaying all of the modules
* abilities.
*
* @export
* @param {string} moduleName
* @returns {JSX.Element}
*/
export function getSubsetOnly(moduleName: string): JSX.Element {
const { full, short } = getDocsUrlForModule(moduleName);
return (
<p className='bp3-running-text'>
The following demos display only a subset of what the <code>{moduleName}</code>
module is capable of. If you want to see its full abilities, check out the
documentation on <a id='open-url' onClick={() => shell.openExternal(full)}>{short}</a>.
</p>
);
}
+260
View File
@@ -0,0 +1,260 @@
import { Button, Classes, Dialog } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as React from 'react';
import { AppState } from '../state';
import { Tour, TourScriptStep, TourStepGetButtonParams } from './tour';
export interface WelcomeTourProps {
appState: AppState;
}
export interface WelcomeTourState {
isTourStarted: boolean;
}
/**
* This is our "Welcome to Electron Fiddle" Tour. It includes both an intro to
* the app and a short intro to Electron.
*
* @returns {Set<TourScriptStep>}
*/
export function getWelcomeTour(): Set<TourScriptStep> {
return new Set([
{
name: 'fiddle-editors',
selector: 'div.mosaic-root',
title: '📝 Fiddle Editors',
content: (
<>
<p>
Electron Fiddle allows you to build little experiments and mini-apps with
Electron. Each Fiddle has three files: A main script, a renderer script,
and an HTML file.
</p>
<p>
If you <code>require()</code> a module, Fiddle will install
it automatically. It will also automatically provide you with autocomplete
information for the <code>electron</code> module.
</p>
</>
)
},
{
name: 'select-versions',
selector: 'button.version-chooser',
title: '📇 Choose an Electron Version',
content: (
<>
<p>
Electron Fiddle knows about all released Electron versions, downloading
your versions automatically in the background.
</p>
<p>
Open the preferences to see all available versions and delete those previously
downloaded.
</p>
</>
)
},
{
name: 'button-run',
selector: '.button-run',
title: '🚀 Run Your Fiddle',
content: (
<p>
Hit this button to give your Fiddle a try and start it.
</p>
)
},
{
name: 'button-publish',
selector: '.button-publish',
title: '🗺 Share Your Fiddle',
content: (
<>
<p>
Like what you've built? You can save your Fiddle as a public GitHub Gist,
allowing other users to load it by pasting the URL into the address bar.
If they don't have Electron Fiddle, they can see and download your code
directly from GitHub.
</p>
<p>
You can also package your Fiddle as a standalone binary or as an installer
from the "Tasks" menu.
</p>
</>
)
},
{
name: 'first-time-electron',
selector: 'div.mosaic-root',
title: '👋 Getting Started With Electron?',
content: (
<p>
We've finished our tour of Electron Fiddle, but if this is your
first time using Electron, we could introduce you to its basics.
Interested?
</p>
),
getButtons: ({ stop, advance }: TourStepGetButtonParams): Array<JSX.Element> => {
return [
<Button key='btn-stop' onClick={stop} text="I'm good!" icon='stop' />,
<Button key='btn-adv' onClick={advance} text='Electron Basics' icon='help' />
];
}
},
{
name: 'main-editor',
selector: 'div.mosaic-window.main',
title: '📝 Main Script',
content: (
<>
<p>
Every Electron app starts with a main script, very similar to how
a Node.js application is started. The main script runs in the "main
process". To display a user interface, the main process creates renderer
processes  usually in the form of windows, which Electron calls
&nbsp;<code>BrowserWindow</code>.
</p>
<p>
To get started, pretend that the main process is just like a Node.js
process. All APIs and features found in Electron are accessible through
the <code>electron</code> module, which can be required like any other
Node.js module.
</p>
<p>
The default fiddle creates a new <code>BrowserWindow</code> and loads
an HTML file.
</p>
</>
)
},
{
name: 'html-editor',
selector: 'div.mosaic-window.html',
title: '📝 HTML',
content: (
<p>
In the default fiddle, this HTML file is loaded in the
&nbsp;<code>BrowserWindow</code>. Any HTML, CSS, or JavaScript that works
in a browser will work here, too. In addition, Electron allows you
to execute Node.js code. Take a close look at the
&nbsp;<code>&lt;script /&gt;</code> tag and notice how we can call <code>
require()</code> like we would in Node.js.
</p>
)
},
{
name: 'renderer-editor',
selector: 'div.mosaic-window.renderer',
title: '📝 Renderer Script',
content: (
<>
<p>
This is the script we just required from the HTML file. In here, you can
do anything that works in Node.js <i>and</i> anything that works in a browser.
</p>
<p>
By the way: If you want to use an <code>npm</code> module here, just
&nbsp;<code>require</code> it. Electron Fiddle will automatically detect that you
requested a module and install it as soon as you run your fiddle.
</p>
</>
)
}
]);
}
/**
* The "Welcome to Electron Fiddle" Tour.
*
* @export
* @class WelcomeTour
* @extends {React.Component<WelcomeTourProps, WelcomeTourState>}
*/
@observer
export class WelcomeTour extends React.Component<WelcomeTourProps, WelcomeTourState> {
constructor(props: WelcomeTourProps) {
super(props);
this.stopTour = this.stopTour.bind(this);
this.startTour = this.startTour.bind(this);
this.state = {
isTourStarted: false
};
}
/**
* Stops the tour, closing it.
*/
public stopTour() {
this.props.appState.disableTour();
}
/**
* Starts the tour.
*/
public startTour() {
this.setState({ isTourStarted: true });
}
get buttons() {
return (
<>
<Button
key='cancel'
onClick={this.stopTour}
icon='cross'
text={`I'll figure it out`}
/>
<Button
key='ok'
onClick={this.startTour}
icon='presentation'
text='Show me around'
/>
</>
);
}
public render() {
const { isTourShowing } = this.props.appState;
const { isTourStarted } = this.state;
if (!isTourShowing) return null;
if (!isTourStarted) {
return (
<Dialog
key='welcome-tour-dialog'
isOpen={true}
>
<div className={Classes.DIALOG_HEADER}>
<h4 className={Classes.HEADING}>🙋 Hey There!</h4>
</div>
<div className={Classes.DIALOG_BODY}>
<p>
Welcome to Electron Fiddle! If you're new to the app,
we'd like to give you a brief tour of its features.
</p>
<p>
We won't show this dialog again, but you can always
find the tour in the Help menu.
</p>
</div>
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
{this.buttons}
</div>
</div>
</Dialog>
);
} else {
return (
<Tour tour={getWelcomeTour()} onStop={this.stopTour} />
);
}
}
}
+228
View File
@@ -0,0 +1,228 @@
import { Button, Classes, Dialog } from '@blueprintjs/core';
import * as React from 'react';
import { positionForRect } from '../../utils/position-for-rect';
export interface TourScriptStep {
name: string;
selector: string;
title: string;
content: JSX.Element;
getButtons?: (handlers: TourStepGetButtonParams) => Array<JSX.Element>;
}
export interface TourProps {
tour: Set<TourScriptStep>;
onStop: () => void;
}
export interface TourState {
tour: IterableIterator<Array<TourScriptStep>>;
step: TourScriptStep | null;
i: number;
}
export interface TourStepGetButtonParams {
stop: () => void;
advance: () => void;
}
export class Tour extends React.Component<TourProps, TourState> {
private resizeHandle = 0;
constructor(props: TourProps) {
super(props);
this.advance = this.advance.bind(this);
this.stop = this.stop.bind(this);
this.onResize = this.onResize.bind(this);
this.state = {
tour: props.tour.entries(),
step: null,
i: 0
};
}
/**
* Handles a resize of the window.
*/
public onResize() {
if (!this.resizeHandle) {
this.resizeHandle = window.setTimeout(() => {
this.forceUpdate();
this.resizeHandle = 0;
}, 100);
}
}
/**
* Component was mounted: Sign up for the window's "resize"
* event and move to the first step of the tour.
*/
public componentDidMount() {
this.advance();
window.addEventListener('resize', this.onResize);
}
public componentWillUnmount() {
window.removeEventListener('resize', this.onResize);
}
/**
* Moves the tour to the next step
*/
public advance() {
const { done, value } = this.state.tour.next();
const step = done ? null : value[0];
this.setState({ step, i: this.state.i + 1 });
}
/**
* Stops the tour
*/
public stop() {
this.props.onStop();
}
public render() {
const { step } = this.state;
if (!step) return null;
return (
<div className='tour'>
{this.getStep(step)}
</div>
);
}
/**
* Return buttons for the dialog for the current step
* of the tour. By default, we just return a continue/stop
* combo
*
* @param {TourScriptStep} { getButtons }
* @returns {Array<JSX.Element>}
*/
private getButtons({ getButtons }: TourScriptStep): Array<JSX.Element> {
// Did the step bring its own buttons?
if (getButtons) {
return getButtons({
stop: this.stop,
advance: this.advance
});
}
// No? Fine! Are we at the end of the tour?
return this.props.tour.size === this.state.i
? [(
<Button
icon='tick-circle'
onClick={this.stop}
key='btn-stop'
text='Finish Tour'
/>
)] : [
(
<Button
icon='stop'
key='btn-stop'
onClick={this.stop}
text='Stop Tour'
/>
), (
<Button
icon='step-forward'
key='btn-adv'
onClick={this.advance}
text='Continue'
/>
)
];
}
/**
* Renders the dialog for the current step of the tour.
*
* @param {TourScriptStep} { content }
* @param {ClientRect} rect
* @returns {JSX.Element}
*/
private getDialogForStep(step: TourScriptStep, rect: ClientRect): JSX.Element {
const buttons = this.getButtons(step);
const size = { width: 400, height: 300 };
const margin = 10;
const position = positionForRect(rect, size);
const style: React.CSSProperties = {
position: 'absolute',
top: position.top,
left: position.left,
width: size.width - margin,
margin: `${margin}px`,
zIndex: 1000,
};
return (
<Dialog
key={step.name}
isOpen={true}
style={style}
portalClassName='tour-portal'
backdropProps={{ style: { visibility: 'hidden' }}}
>
<div className={Classes.DIALOG_HEADER}>
<h4 className={Classes.HEADING}>
{step.title}
</h4>
</div>
<div className={Classes.DIALOG_BODY}>
{step.content}
</div>
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
{buttons}
</div>
</div>
</Dialog>
);
}
/**
* Returns the "mask" for a given step.
*
* @param {TourScriptStep} { selector, text }
* @returns {(JSX.Element | null)}
*/
private getStep(step: TourScriptStep): JSX.Element | null {
const { selector } = step;
const element = document.querySelector(selector) || document.body!;
const rect = element.getBoundingClientRect();
const { top, left, width, height } = rect;
return (
<>
{this.getDialogForStep(step, rect)}
<svg height='100%' width='100%'>
<rect fill='rgba(0, 0, 0, 0.65)' x='0' y='0' mask='url(#mask)' height='100%' width='100%'/>
<mask id='mask' maskUnits='userSpaceOnUse' maskContentUnits='userSpaceOnUse'>
<rect className='bg' x='0' y='0' fill='white' height='100%' width='100%' />
<rect
x={left}
y={top}
width={width}
height={height}
fill='black'
rx='5'
ry='5'
strokeWidth='5'
stroke='black'
/>
</mask>
</svg>
</>
);
}
}
@@ -1,85 +0,0 @@
import * as React from 'react';
import * as semver from 'semver';
import { observer } from 'mobx-react';
import { normalizeVersion } from '../../utils/normalize-version';
import { AppState } from '../app';
export interface VersionChooserState {
value: string;
}
export interface VersionChooserProps {
appState: AppState;
}
@observer
export class VersionChooser extends React.Component<VersionChooserProps, VersionChooserState> {
constructor(props: VersionChooserProps) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.updateDownloadedVersionState();
}
public async updateDownloadedVersionState() {
const { binaryManager } = this.props.appState;
const downloadedVersions = await binaryManager.getDownloadedVersions();
const updatedVersions = { ...this.props.appState.versions };
console.log(`Version Chooser: Updating version state`);
downloadedVersions.forEach((version) => {
if (updatedVersions[version]) {
updatedVersions[version].state = 'ready';
}
});
this.props.appState.versions = updatedVersions;
}
public handleChange(event: React.ChangeEvent<HTMLSelectElement>) {
const version = normalizeVersion(event.target.value);
console.log(`Version Chooser: Switching to v${version}`);
this.props.appState.version = version;
// Fetch new binaries, maybe?
if ((this.props.appState.versions[version] || { state: '' }).state === 'ready') return;
console.log(`Version Chooser: Instructing BinaryManager to fetch v${version}`);
const updatedVersions = { ...this.props.appState.versions };
updatedVersions[normalizeVersion(version)].state = 'downloading';
this.props.appState.versions = updatedVersions;
this.props.appState.binaryManager.setup(version)
.then(() => this.updateDownloadedVersionState());
}
public renderOptions() {
const { versions } = this.props.appState;
return Object.keys(versions)
.sort((a, b) => semver.gt(a, b, true) ? -1 : 1)
.map((key) => {
const { tag_name, state } = versions[key];
const version = tag_name;
const icon = state === 'ready'
? '✅'
: state === 'downloading' ? '⏬' : '⏹';
return (
<option value={version} key={version}>
{icon} {version}
</option>
);
});
}
public render() {
return (
<select value={`v${this.props.appState.version}`} onChange={this.handleChange}>
{this.renderOptions()}
</select>
);
}
}
+21 -1
View File
@@ -1,3 +1,23 @@
import { remote } from 'electron';
import * as path from 'path';
import { MosaicNode } from 'react-mosaic-component';
export const userData = remote.app.getPath('userData');
import { EditorId, MosaicId } from '../interfaces';
// Reminder: When testing, this file is mocked in tests/setup.js
export const USER_DATA_PATH = remote.app.getPath('userData');
export const CONFIG_PATH = path.join(remote.app.getPath('home'), '.electron-fiddle');
export const DEFAULT_MOSAIC_ARRANGEMENT: MosaicNode<MosaicId> = {
direction: 'row',
first: EditorId.main,
second: {
direction: 'column',
first: EditorId.renderer,
second: EditorId.html,
}
};
export const ELECTRON_ORG = 'electron';
export const ELECTRON_REPO = 'electron';
+50 -9
View File
@@ -1,15 +1,56 @@
import * as fs from 'fs';
import * as path from 'path';
import { EditorId } from '../interfaces';
const simpleCache = {};
/**
* Returns expected content for a given name.
*
* @export
* @param {EditorId} name
* @param {string} [version]
* @returns {Promise<string>}
*/
export async function getContent(
name: EditorId,
version?: string,
): Promise<string> {
if (name === EditorId.html) {
return (await import('../content/html')).html;
}
export function getContent(name: string) {
if (simpleCache[name]) return simpleCache[name];
if (name === EditorId.renderer) {
return (await import('../content/renderer')).renderer;
}
const filePath = path.join(__dirname, '../../static/content', name);
const content = fs.readFileSync(filePath, 'utf-8');
if (name === EditorId.main) {
// We currently only distinguish between loadFile
// and loadURL. TODO: Properly version the quick-start.
if (version && version.startsWith('1.')) {
return (await import('../content/main-1-x-x')).main;
}
simpleCache[name] = content;
return (await import('../content/main')).main;
}
return content;
return '';
}
/**
* Did the content change?
*
* @param {EditorId} name
* @returns {Promise<boolean>}
*/
export async function isContentUnchanged(name: EditorId): Promise<boolean> {
if (!window.ElectronFiddle || !window.ElectronFiddle.app) return false;
const values = await window.ElectronFiddle.app.getEditorValues({ include: false });
// Handle main case, which needs to check both possible versions
if (name === EditorId.main) {
const isChanged1x = await getContent(EditorId.main, '1.0') === values.main;
const isChangedOther = await getContent(EditorId.main) === values.main;
return isChanged1x || isChangedOther;
} else {
return values[name] === await getContent(name);
}
}
+159
View File
@@ -0,0 +1,159 @@
import * as fsType from 'fs-extra';
import * as MonacoType from 'monaco-editor';
import * as path from 'path';
import { ElectronVersion, ElectronVersionSource } from '../interfaces';
import { callIn } from '../utils/call-in';
import { fancyImport } from '../utils/import';
import { USER_DATA_PATH } from './constants';
const definitionPath = path.join(USER_DATA_PATH, 'electron-typedef');
/**
* Fetch TypeScript definitions for the current version of Electron (online)
*
* @param {string} version
* @returns {Promise<string>}
*/
export async function fetchTypeDefinitions(version: string): Promise<string> {
const url = `https://unpkg.com/electron@${version}/electron.d.ts`;
let text: string;
try {
const response = await window.fetch(url);
text = await response.text();
} catch (error) {
console.warn(`Fetch Types: Could not fetch definitions`, error);
return '';
}
// for invalid packa
if (text.includes('Cannot find package')) {
console.warn(`Fetch Types: ${text}`);
return '';
} else {
return text;
}
}
/**
* Get the path for offline TypeScript definitions
*
* @param {string} version
* @returns {string}
*/
export function getOfflineTypeDefinitionPath(version: string): string {
return path.join(definitionPath, version, 'electron.d.ts');
}
/**
* Do TypeScript definitions for the current version of Electron exist on disk?
*
* @param {string} version
* @returns {boolean}
*/
export async function getOfflineTypeDefinitions(version: string): Promise<boolean> {
const fs = await fancyImport<typeof fsType>('fs-extra');
return fs.existsSync(getOfflineTypeDefinitionPath(version));
}
/**
* Get TypeScript definitions for a version of Electron. If none can be
* found, returns null.
*
* @param {string} version
* @returns {void}
*/
export async function getDownloadedVersionTypeDefs(version: ElectronVersion): Promise<string | null> {
const fs = await fancyImport<typeof fsType>('fs-extra');
await fs.mkdirp(definitionPath);
const offlinePath = getOfflineTypeDefinitionPath(version.version);
if (await getOfflineTypeDefinitions(version.version)) {
try {
return await fs.readFile(offlinePath, 'utf-8');
} catch (error) {
return null;
}
} else {
const typeDefs = await fetchTypeDefinitions(version.version);
if (typeDefs && typeDefs.length > 0) {
try {
await fs.outputFile(offlinePath, typeDefs);
return typeDefs;
} catch (error) {
console.warn(`Fetch Types: Could not write to disk`, error);
return typeDefs;
}
}
return null;
}
}
export async function getLocalVersionTypeDefs(version: ElectronVersion) {
if (version.source === ElectronVersionSource.local && !!version.localPath) {
const fs = await fancyImport<typeof fsType>('fs-extra');
const typesPath = getLocalTypePathForVersion(version);
if (!!typesPath && fs.existsSync(typesPath)) {
return fs.readFile(typesPath, 'utf-8');
}
}
return null;
}
/**
* Tries to update the editor with type definitions.
*
* @param {string} version
*/
export async function updateEditorTypeDefinitions(version: ElectronVersion, i: number = 0): Promise<void> {
const defer = async (): Promise<void> => {
if (i > 10) {
console.warn(`Fetch Types: Failed, dependencies do not exist`);
return;
}
console.warn(`Fetch Types: Called too soon, deferring`);
return callIn(i * 100 + 200, () => updateEditorTypeDefinitions(version, i + 1));
};
// If this method is called before we're ready, we'll delay this work a bit
if (!window.ElectronFiddle.app || !window.ElectronFiddle.app.monaco) return defer();
const { app } = window.ElectronFiddle;
const monaco: typeof MonacoType = app.monaco!;
const typeDefDisposable: MonacoType.IDisposable = app.typeDefDisposable!;
const getTypeDefs = (version.source === ElectronVersionSource.local) ?
getLocalVersionTypeDefs : getDownloadedVersionTypeDefs;
const typeDefs = await getTypeDefs(version);
if (typeDefDisposable) {
typeDefDisposable.dispose();
}
if (typeDefs) {
console.log(`Fetch Types: Updating Monaco types with electron.d.ts@${version.version}`);
const disposable = monaco.languages.typescript.javascriptDefaults.addExtraLib(typeDefs);
window.ElectronFiddle.app.typeDefDisposable = disposable;
} else {
console.log(`Fetch Types: No type definitions for ${version.version} 😢`);
}
}
export function getLocalTypePathForVersion(version: ElectronVersion) {
if (version.localPath) {
return path.join(
version.localPath,
'gen',
'electron',
'tsc',
'typings',
'electron.d.ts'
);
} else {
return null;
}
}

Some files were not shown because too many files have changed in this diff Show More