├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── appwindow
├── banner.png
├── code.js
├── defaultindexhtml.txt
├── fileicons
│ ├── bmp.png
│ ├── css.png
│ ├── foldericon.png
│ ├── html.png
│ ├── jpeg.png
│ ├── jpg.png
│ ├── js.png
│ ├── json.png
│ ├── md.png
│ ├── png.png
│ ├── py.png
│ ├── svg.png
│ ├── txt.png
│ └── webp.png
├── icon.png
├── index.html
├── settings
│ ├── index.html
│ └── settings.js
├── style.css
└── webviewPreload.js
├── banner.svg
├── icon.svg
├── main.js
├── package.json
├── resources
├── background.png
├── icon.icns
├── icon.png
└── icons
│ └── 512x512.png
└── screenshots
├── Screen Shot 2022-04-01 at 4.40.07 PM.png
├── Screen Shot 2022-05-12 at 9.22.25 PM.png
├── blog.PNG
├── lunalgraphics.PNG
└── yikuansun.PNG
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | node_modules
3 | .DS_Store
4 | dist
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Yikuan Sun
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | 
3 |
4 |
5 | # WebKitty
6 |
7 | **Web Development shouldn't be hard.**
8 |
9 | That's why I built WebKitty, an all-in one tool for editing, testing and debugging HTML projects.
10 |
11 |
12 | ## Features
13 |
14 | - Customizable theme
15 | - Integrated preview
16 | - Google Chrome-style debugging tools
17 | - Emulation of different hosts and browsers
18 | - Fast file navigation
19 | - Cross platform
20 | - Code suggestions with Ctrl+Space
21 |
22 |
23 | ## Screenshots
24 |
25 | 
26 |
27 | ## Supported Operating Systems
28 |
29 | - MacOS >=10.11
30 | - Windows >=7
31 | - Ubuntu >=14.04
32 | - Fedora >=28
33 | - Debian >=8
34 |
35 | ## Dependencies
36 |
37 | - electron 17.1.0
38 | - electron-builder 22.14.5
39 | - @electron/remote 2.0.5
40 | - adm-zip 0.5.5
41 | - codemirror 5.65.2
--------------------------------------------------------------------------------
/appwindow/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/appwindow/banner.png
--------------------------------------------------------------------------------
/appwindow/code.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const path = require("path");
3 | const { dialog, shell, BrowserWindow, app, Menu, nativeImage } = require("@electron/remote");
4 | const { ipcRenderer } = require("electron");
5 | const { basicSetup } = require("codemirror");
6 | const { EditorView, keymap, lineNumbers } = require("@codemirror/view");
7 | const { EditorState, Compartment } = require("@codemirror/state");
8 | const { defaultKeymap, history, historyKeymap, indentMore, indentLess } = require("@codemirror/commands");
9 | const { syntaxHighlighting, defaultHighlightStyle, indentUnit } = require("@codemirror/language");
10 | const { javascript } = require("@codemirror/lang-javascript");
11 | const { html } = require("@codemirror/lang-html");
12 | const { css } = require("@codemirror/lang-css");
13 | const { markdown } = require("@codemirror/lang-markdown");
14 | const { json } = require("@codemirror/lang-json");
15 | const { xml } = require("@codemirror/lang-xml");
16 | const { python } = require("@codemirror/lang-python"); // xd
17 | const { basicDark, basicDarkTheme, basicDarkHighlightStyle } = require("cm6-theme-basic-dark");
18 | const { materialDark, materialDarkTheme, materialDarkHighlightStyle } = require("cm6-theme-material-dark");
19 | const { oneDark, oneDarkTheme, oneDarkHighlightStyle } = require("@codemirror/theme-one-dark");
20 | const LocalServer = require("ezserv").server;
21 |
22 | let projectdirectory = "";
23 | var serve = new LocalServer(0);
24 |
25 | var currentTheme = new Compartment();
26 | var currentHighlightStyle = new Compartment();
27 | var updateListener = new Compartment();
28 | var languageMode = new Compartment();
29 | let options = {
30 | doc: "hi",
31 | extensions: [
32 | basicSetup,
33 | indentUnit.of(" ".repeat(4)),
34 | keymap.of([
35 | ...defaultKeymap,
36 | ...historyKeymap,
37 | {
38 | key: "Tab",
39 | preventDefault: true,
40 | run: indentMore,
41 | },
42 | {
43 | key: "Shift-Tab",
44 | preventDefault: true,
45 | run: indentLess,
46 | },
47 | ]),
48 | currentTheme.of(oneDarkTheme),
49 | history(),
50 | languageMode.of(html()),
51 | currentHighlightStyle.of(syntaxHighlighting(basicDarkHighlightStyle)),
52 | lineNumbers(),
53 | updateListener.of(EditorView.updateListener.of(function() {})),
54 | ],
55 | parent: document.getElementById("cdm")
56 | };
57 |
58 | let editor = new EditorView(options);
59 | function setCMHeight() {
60 | document.querySelector(".cm-editor").style.height = `calc(100vh - ${document.querySelector("#leftsection td").getBoundingClientRect().height + 28}px)`;
61 | }
62 | window.addEventListener("load", setCMHeight);
63 | window.addEventListener("resize", setCMHeight);
64 |
65 | function openFileInTextEditor(dir, rel_path, callback = false) {
66 | //let code = fs.readFileSync();
67 |
68 | fs.readFile(path.join(dir, rel_path), 'utf8' , (err, data) => {
69 | if (err) return console.error(err);
70 |
71 | editor.dispatch({
72 | changes: { from: 0, to: editor.state.doc.length, insert: data }
73 | });
74 | if (typeof callback == "function") callback();
75 |
76 | switch (path.extname(rel_path)) {
77 | case ".js":
78 | editor.dispatch({
79 | effects: languageMode.reconfigure(javascript()),
80 | })
81 | break;
82 | case ".css":
83 | editor.dispatch({
84 | effects: languageMode.reconfigure(css()),
85 | })
86 | break;
87 | case ".xml":
88 | editor.dispatch({
89 | effects: languageMode.reconfigure(xml()),
90 | })
91 | break;
92 | case ".json":
93 | editor.dispatch({
94 | effects: languageMode.reconfigure(json()),
95 | })
96 | break;
97 | case ".md":
98 | editor.dispatch({
99 | effects: languageMode.reconfigure(markdown()),
100 | })
101 | break;
102 | case ".py":
103 | editor.dispatch({
104 | effects: languageMode.reconfigure(python()),
105 | })
106 | break;
107 | default:
108 | editor.dispatch({
109 | effects: languageMode.reconfigure(html()),
110 | })
111 | break;
112 | }
113 | });
114 | }
115 |
116 | function setProject(dir) {
117 | fileselect.value = "index.html";
118 | openFileInTextEditor(dir, "index.html");
119 |
120 | serve.dir = dir;
121 |
122 | document.querySelector("#pagepreview").src = `http://localhost:${serve.port}/`;
123 | document.querySelector("#addressbar").value = `http://localhost:${serve.port}/`;
124 | }
125 |
126 | function saveTextFile(filepath, filecontents) {
127 | fs.writeFileSync(filepath, filecontents);
128 | }
129 |
130 | document.querySelector("#newprojectbutton").addEventListener("click", function() {
131 | var dir = dialog.showSaveDialogSync({
132 | title: "Make a New Project",
133 | defaultPath: app.getPath("documents") + "/New Project"
134 | });
135 | if (dir) {
136 | fs.mkdirSync(dir);
137 | fs.writeFileSync(dir + "/index.html", fs.readFileSync(__dirname + "/defaultindexhtml.txt"));
138 | fs.writeFileSync(dir + "/code.js", "");
139 | fs.writeFileSync(dir + "/style.css", "");
140 | projectdirectory = dir;
141 | setProject(projectdirectory);
142 | document.querySelector("#landingscreen").style.display = "none";
143 | }
144 | });
145 |
146 | document.querySelector("#projectselect").addEventListener("click", function() {
147 | var dir = dialog.showOpenDialogSync({
148 | title: "Open Project Folder",
149 | properties: ["openDirectory"]
150 | });
151 | if (dir[0]) {
152 | projectdirectory = dir[0];
153 | if (!fs.readdirSync(dir[0]).includes("index.html")) {
154 | alert("no index.html found in the selected project");
155 | return;
156 | }
157 | setProject(projectdirectory);
158 | document.querySelector("#landingscreen").style.display = "none";
159 | }
160 | });
161 |
162 | document.querySelector("#settingsbutton").addEventListener("click", function() {
163 | ipcRenderer.send("opensettingswin");
164 | });
165 |
166 | document.querySelector("#aboutbutton").addEventListener("click", function() {
167 | shell.openExternal("https://github.com/yikuansun/webkitty?tab=readme-ov-file#webkitty");
168 | });
169 |
170 | document.querySelector("#addressbar").addEventListener("change", function() {
171 | document.querySelector("#pagepreview").src = this.value;
172 | });
173 |
174 | document.querySelector("#reloadbutton").addEventListener("click", function() {
175 | document.querySelector("#pagepreview").src = document.querySelector("#addressbar").value;
176 | });
177 |
178 | document.querySelector("#devtoolsbutton").addEventListener("click", function() {
179 | document.querySelector("#pagepreview").openDevTools();
180 | });
181 |
182 | document.querySelector("#fileselect").addEventListener("mousedown", function(e) {
183 | e.preventDefault();
184 | var template = [];
185 | var constructTemplate = function(arr, directory, basedir) {
186 | var dircontents = fs.readdirSync(directory);
187 | for (var file of dircontents) {
188 | if (file == ".DS_Store" || file == ".git") continue;
189 | var buttonRepr = {label: file};
190 | if (fs.lstatSync(directory + "/" + file).isDirectory()) {
191 | var submenu = [];
192 | constructTemplate(submenu, directory + "/" + file, basedir);
193 | buttonRepr.submenu = submenu;
194 | buttonRepr.icon = nativeImage.createFromPath(`${__dirname}/fileicons/foldericon.png`).resize({ width: 12, height: 12 });
195 | }
196 | else {
197 | var rel_path = (directory + "/" + file).split(basedir + "/")[1];
198 | if (rel_path == document.querySelector("#fileselect").value) buttonRepr.enabled = false;
199 | buttonRepr.click = new Function(`
200 | document.querySelector("#fileselect").value = "${rel_path}";
201 | openFileInTextEditor(projectdirectory, "${rel_path}");
202 | document.querySelector("#fileselect").scrollLeft = document.querySelector("#fileselect").scrollWidth;
203 | `);
204 | var iconPath = `${__dirname}/fileicons/${path.extname(file).replace(".", "").toLowerCase()}.png`;
205 | if (fs.existsSync(iconPath)) {
206 | buttonRepr.icon = nativeImage.createFromPath(iconPath).resize({ width: 12, height: 12 });
207 | }
208 | }
209 | arr.push(buttonRepr);
210 | }
211 | };
212 | constructTemplate(template, projectdirectory, projectdirectory);
213 | var fileMenu = Menu.buildFromTemplate(template);
214 | var xy = this.getBoundingClientRect();
215 | fileMenu.popup({
216 | x: Math.floor(xy.x),
217 | y: Math.floor(xy.y + xy.height)
218 | });
219 | });
220 | /*
221 | document.querySelector("#savebutton").addEventListener("click", function() {
222 | saveTextFile(projectdirectory + "/" + document.querySelector("#fileselect").value, document.querySelector("#texteditor").value);
223 | document.querySelector("#savebutton").style.fontWeight = "";
224 | });
225 | */
226 | document.querySelector("#texteditor").addEventListener("keydown", function(e) {
227 | if (((process.platform == "darwin")?e.metaKey:e.ctrlKey) && e.key == "s") {
228 | saveTextFile(projectdirectory + "/" + document.querySelector("#fileselect").value, this.value);
229 | document.querySelector("#savebutton").style.fontWeight = "";
230 | }
231 | });
232 |
233 |
234 | // A function is used for dragging and moving
235 | function dragElement(element, direction)
236 | {
237 | var md; // remember mouse down info
238 | const first = document.getElementById("first");
239 | const second = document.getElementById("second");
240 |
241 | element.onmousedown = onMouseDown;
242 |
243 | function onMouseDown(e)
244 | {
245 | document.getElementById("rightsection").classList.add("nopointer");
246 | //console.log("mouse down: " + e.clientX);
247 | md = {e,
248 | offsetLeft: element.offsetLeft,
249 | offsetTop: element.offsetTop,
250 | firstWidth: first.offsetWidth,
251 | secondWidth: second.offsetWidth
252 | };
253 |
254 | document.onmousemove = onMouseMove;
255 | document.onmouseup = () => {
256 | //console.log("mouse up");
257 | document.onmousemove = document.onmouseup = null;
258 | document.getElementById("rightsection").classList.remove("nopointer");
259 | }
260 | }
261 |
262 | function onMouseMove(e)
263 | {
264 | //console.log("mouse move: " + e.clientX);
265 | var delta = {x: e.clientX - md.e.clientX,
266 | y: e.clientY - md.e.clientY};
267 |
268 | if (direction === "H" ) // Horizontal
269 | {
270 | // Prevent negative-sized elements
271 | delta.x = Math.min(Math.max(delta.x, -md.firstWidth),
272 | md.secondWidth);
273 |
274 | element.style.left = md.offsetLeft + delta.x + "px";
275 | first.style.width = (md.firstWidth + delta.x) + "px";
276 | second.style.width = (md.secondWidth - delta.x) + "px";
277 | }
278 | setCMHeight();
279 | }
280 | }
281 |
282 | dragElement( document.getElementById("separator"), "H" );
283 |
284 | var autosave = true;
285 | /*document.querySelector("#texteditor").addEventListener("change", function() {
286 | if (autosave) {
287 | saveTextFile(projectdirectory + "/" + document.querySelector("#fileselect").value, this.value);
288 | document.querySelector("#savebutton").style.fontWeight = "";
289 | }
290 | });*/
291 | editor.dispatch({
292 | effects: updateListener.reconfigure(EditorView.updateListener.of(function(e) {
293 | if (document.querySelector("#fileselect").value) {
294 | if (autosave) {
295 | saveTextFile(
296 | projectdirectory + "/" + document.querySelector("#fileselect").value,
297 | editor.state.doc.toString()
298 | );
299 | }
300 | else {
301 | document.querySelector("#fileselect").style.fontStyle = "italic";
302 | }
303 | }
304 | }))
305 | });
306 |
307 | var smallmenu = Menu.buildFromTemplate([
308 | {
309 | label: "Manage Files",
310 | click: function() {
311 | shell.openPath(projectdirectory);
312 | }
313 | },
314 | {
315 | label: "Close Project",
316 | click: function() {
317 | document.querySelector("#landingscreen").style.display = "";
318 | }
319 | },
320 | {
321 | label: "Settings",
322 | click: function() {
323 | ipcRenderer.send("opensettingswin");
324 | }
325 | }
326 | ]);
327 | document.querySelector("#menubutton").addEventListener("click", function() {
328 | var xy = this.getBoundingClientRect();
329 | smallmenu.popup({
330 | x: Math.floor(xy.x),
331 | y: Math.floor(xy.y + xy.height)
332 | });
333 | });
334 |
335 | /*document.querySelector("#filemanagerbutton").addEventListener("click", function() {
336 | shell.openPath(projectdirectory);
337 | });*/
338 |
339 | document.querySelector("#openexternalbutton").addEventListener("click", function() {
340 | shell.openExternal(`http://localhost:${serve.port}`);
341 | });
342 |
343 | document.querySelector("#publishbutton").addEventListener("click", function() {
344 | shell.openExternal("https://pages.github.com/")
345 | });
346 | /*
347 | document.querySelector("#htmlbuilderlink").addEventListener("click", function() {
348 | shell.openExternal("https://github.com/yikuansun/html-builder");
349 | });
350 | */
351 | var userDataPath = app.getPath("userData");
352 | if (!fs.existsSync(userDataPath + "/settings.json")) {
353 | fs.writeFileSync(userDataPath + "/settings.json", JSON.stringify({
354 | primarycolor: "#f5f5f5",
355 | secondarycolor: "#161616",
356 | backgroundcolor: "#222222",
357 | layout: "left-right",
358 | codefontsize: 12,
359 | autosave: true,
360 | httpreferrer: "",
361 | useragent: "",
362 | }));
363 | }
364 | function readSettings() {
365 | var userSettings = JSON.parse(fs.readFileSync(userDataPath + "/settings.json"));
366 | document.documentElement.style.setProperty("--background-color", userSettings.backgroundcolor);
367 | document.documentElement.style.setProperty("--ui-primary-color", userSettings.primarycolor);
368 | document.documentElement.style.setProperty("--ui-secondary-color", userSettings.secondarycolor);
369 | /*if (userSettings.layout == "top-bottom") {
370 | document.querySelector("#leftsection").style.width = "100vw";
371 | document.querySelector("#rightsection").style.width = "100vw";
372 | document.querySelector("#leftsection").style.height = "calc(50vh - 15px)";
373 | document.querySelector("#rightsection").style.height = "calc(50vh - 15px)";
374 | document.querySelector("#rightsection").style.left = "0";
375 | document.querySelector("#leftsection").style.bottom = "calc(50vh - 15px)";
376 | }
377 | else {
378 | document.querySelector("#leftsection").style.width = "";
379 | document.querySelector("#rightsection").style.width = "";
380 | document.querySelector("#leftsection").style.height = "";
381 | document.querySelector("#rightsection").style.height = "";
382 | document.querySelector("#rightsection").style.left = "";
383 | document.querySelector("#leftsection").style.bottom = "";
384 | }*/
385 | document.querySelector(".cm-editor").style.fontSize = `${userSettings.codefontsize}px`;
386 | autosave = userSettings.autosave;
387 | if (userSettings.httpreferrer) document.querySelector("#pagepreview").setAttribute("httpreferrer", userSettings.httpreferrer);
388 | else document.querySelector("#pagepreview").removeAttribute("httpreferrer");
389 | if (document.querySelector("#pagepreview").hasAttribute("#useragent")) {
390 | document.querySelector("#pagepreview").setUserAgent(userSettings.useragent);
391 | }
392 | else document.querySelector("#pagepreview").setAttribute("useragent", userSettings.useragent);
393 | }
394 | readSettings();
395 | ipcRenderer.on("updateappsettings", function(data) {
396 | readSettings();
397 | });
398 |
399 | window.addEventListener("keydown", function(e) {
400 | if (((process.platform == "darwin")?e.metaKey:e.ctrlKey) && e.key == "s") {
401 | saveTextFile(
402 | projectdirectory + "/" + document.querySelector("#fileselect").value,
403 | editor.state.doc.toString()
404 | );
405 | document.querySelector("#fileselect").style.fontStyle = "";
406 | }
407 | else if (((process.platform == "darwin")?e.metaKey:e.ctrlKey) && e.key == "r") {
408 | e.preventDefault();
409 | document.querySelector("#reloadbutton").click();
410 | }
411 | else if ((process.platform == "darwin")?(e.metaKey && e.altKey && e.key.toLowerCase() == "i"):(e.ctrlKey && e.shiftKey && e.key.toLowerCase() == "i")) {
412 | e.preventDefault();
413 | document.querySelector("#devtoolsbutton").click();
414 | }
415 | });
416 |
417 | var time = new Date();
418 | if (time.getMonth() == 4 && time.getDate() == 1) {
419 | for (var elem of document.querySelectorAll("*")) {
420 | elem.style.fontFamily = "cursive";
421 | }
422 | }
--------------------------------------------------------------------------------
/appwindow/defaultindexhtml.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | My HTML Project
5 |
6 |
7 |
8 | Hello World!
9 |
10 |
11 |
--------------------------------------------------------------------------------
/appwindow/fileicons/bmp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/appwindow/fileicons/bmp.png
--------------------------------------------------------------------------------
/appwindow/fileicons/css.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/appwindow/fileicons/css.png
--------------------------------------------------------------------------------
/appwindow/fileicons/foldericon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/appwindow/fileicons/foldericon.png
--------------------------------------------------------------------------------
/appwindow/fileicons/html.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/appwindow/fileicons/html.png
--------------------------------------------------------------------------------
/appwindow/fileicons/jpeg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/appwindow/fileicons/jpeg.png
--------------------------------------------------------------------------------
/appwindow/fileicons/jpg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/appwindow/fileicons/jpg.png
--------------------------------------------------------------------------------
/appwindow/fileicons/js.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/appwindow/fileicons/js.png
--------------------------------------------------------------------------------
/appwindow/fileicons/json.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/appwindow/fileicons/json.png
--------------------------------------------------------------------------------
/appwindow/fileicons/md.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/appwindow/fileicons/md.png
--------------------------------------------------------------------------------
/appwindow/fileicons/png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/appwindow/fileicons/png.png
--------------------------------------------------------------------------------
/appwindow/fileicons/py.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/appwindow/fileicons/py.png
--------------------------------------------------------------------------------
/appwindow/fileicons/svg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/appwindow/fileicons/svg.png
--------------------------------------------------------------------------------
/appwindow/fileicons/txt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/appwindow/fileicons/txt.png
--------------------------------------------------------------------------------
/appwindow/fileicons/webp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/appwindow/fileicons/webp.png
--------------------------------------------------------------------------------
/appwindow/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/appwindow/icon.png
--------------------------------------------------------------------------------
/appwindow/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | WebKitty
7 |
8 |
9 |
10 |
11 |
12 |

13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
65 | |
66 |
67 |
68 |
69 |
70 | |
71 |
72 |
73 |
74 |
75 |
76 | |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/appwindow/settings/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Settings - WebKitty
5 |
37 |
38 |
39 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/appwindow/settings/settings.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const { ipcRenderer } = require('electron');
3 | const { app } = require("@electron/remote");
4 | const userDataPath = app.getPath("userData");
5 |
6 | var currentSettings = JSON.parse(fs.readFileSync(userDataPath + "/settings.json", "utf-8"));
7 | document.getElementById("primarycolorpicker").value = currentSettings.primarycolor;
8 | document.getElementById("secondarycolorpicker").value = currentSettings.secondarycolor;
9 | document.getElementById("backgroundcolorpicker").value = currentSettings.backgroundcolor;
10 | //document.getElementById("layoutpicker").value = currentSettings.layout;
11 | document.getElementById("codefontsize").value = currentSettings.codefontsize;
12 | document.getElementById("autosave").checked = currentSettings.autosave;
13 | document.getElementById("httpreferrer").value = currentSettings.httpreferrer;
14 | document.getElementById("useragent").value = currentSettings.useragent;
15 |
16 | function save_options() {
17 | fs.writeFileSync(userDataPath + "/settings.json", JSON.stringify({
18 | // layout: document.getElementById("layoutpicker").value,
19 | primarycolor: document.getElementById("primarycolorpicker").value,
20 | secondarycolor: document.getElementById("secondarycolorpicker").value,
21 | backgroundcolor: document.getElementById("backgroundcolorpicker").value,
22 | codefontsize: parseInt(document.getElementById("codefontsize").value),
23 | autosave: document.getElementById("autosave").checked,
24 | httpreferrer: document.getElementById("httpreferrer").value,
25 | useragent: document.getElementById("useragent").value
26 | }));
27 |
28 | ipcRenderer.send("updateappsettings");
29 |
30 | window.close();
31 |
32 | return false;
33 | }
34 |
35 | document.querySelector("form").onsubmit = save_options;
--------------------------------------------------------------------------------
/appwindow/style.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --background-color: #222222;
3 | --ui-primary-color: #f5f5f5;
4 | --ui-secondary-color: #161616;
5 | --ui-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
6 | }
7 |
8 | html, body {
9 | height: 100%;
10 | }
11 |
12 | body {
13 | margin: 0;
14 |
15 | background-color: var(--background-color);
16 | font-family: var(--ui-font);
17 |
18 | user-select: none;
19 | }
20 |
21 | /* BEGIN NEW */
22 |
23 | .main {
24 | display: flex;
25 | position: fixed;
26 | left: 0;
27 | top: 0;
28 | right: 0;
29 | bottom: 0;
30 | overflow: hidden;
31 | }
32 |
33 | .file-browser {
34 | display: none;
35 | }
36 |
37 | .project, .preview {
38 | width: 50%;
39 | overflow: hidden;
40 | position: relative;
41 | flex-grow: 1;
42 | min-width: 10px;
43 | }
44 |
45 | .editor {
46 | height: 100%;
47 | }
48 |
49 | #separator {
50 | cursor: ew-resize;
51 | background-color: var(--ui-primary-color);
52 | background-repeat: no-repeat;
53 | background-position: center;
54 | width: 10px;
55 | height: 100vh;
56 | border: 4px solid var(--background-color);
57 | box-sizing: border-box;
58 | opacity: 0.36;
59 |
60 | /* Prevent the browser's built-in drag from interfering */
61 | -moz-user-select: none; /*Literally Electron???*/
62 | -ms-user-select: none;
63 | user-select: none;
64 | }
65 |
66 | /* END NEW */
67 |
68 | ::-webkit-scrollbar {
69 | width: 10px;
70 | height: 10px;
71 | }
72 |
73 | ::-webkit-scrollbar-track {
74 | opacity: 0;
75 | }
76 |
77 | ::-webkit-scrollbar-thumb {
78 | background: #80808069;
79 | background-clip: padding-box;
80 | }
81 |
82 | ::-webkit-scrollbar-thumb:hover {
83 | background: #80808042;
84 | background-clip: padding-box;
85 | }
86 |
87 | ::-webkit-scrollbar-corner {
88 | background: #00000000;
89 | }
90 |
91 | #landingscreen {
92 | background-color: var(--background-color);
93 | width: 100vw;
94 | height: 100vh;
95 | position: fixed;
96 | z-index: 69;
97 | top: 0;
98 | left: 0;
99 | display: flex;
100 | flex-direction: column;
101 | justify-content: center;
102 | align-items: center;
103 | text-align: center;
104 | user-select: none;
105 | }
106 |
107 | #landingscreen button {
108 | background-color: var(--ui-secondary-color);
109 | color: var(--ui-primary-color);
110 | padding: 10px;
111 | font-size: 36px;
112 | font-family: var(--ui-font);
113 | border: 3px outset var(--ui-secondary-color);
114 | }
115 |
116 | #landingscreen button:active {
117 | border-style: inset;
118 | }
119 |
120 | #menubutton, #publishbutton, #htmlbuilderlink {
121 | background-color: var(--ui-secondary-color);
122 | color: var(--ui-primary-color);
123 | font-family: var(--ui-font);
124 | border: 3px outset var(--ui-secondary-color);
125 | }
126 |
127 | #menubutton:active, #publishbutton:active, #htmlbuilderlink:active {
128 | border-style: inset;
129 | }
130 |
131 | .sectiontable {
132 | width: 100%;
133 | height: 100%;
134 | box-sizing: border-box;
135 | table-layout: fixed;
136 | }
137 |
138 | #leftsection {
139 | width: 100%;
140 | height: 100vh;
141 | box-sizing: border-box;
142 | padding: 10px;
143 | position: absolute;
144 | z-index: 1;
145 | left: 0;
146 | bottom: 0;
147 | }
148 |
149 | #texteditor {
150 | width: 100%;
151 | height: 100%;
152 | resize: none;
153 | background-color: var(--ui-secondary-color);
154 | color: var(--ui-primary-color);
155 | white-space: nowrap;
156 | border-width: 0;
157 | }
158 |
159 | .nopointer {
160 | pointer-events: none;
161 | }
162 |
163 | input:focus, textarea:focus, select:focus, button:focus {
164 | outline: none!important;
165 | }
166 |
167 | #rightsection {
168 | width: 100%;
169 | height: 100vh;
170 | box-sizing: border-box;
171 | padding: 10px;
172 | position: absolute;
173 | z-index: 1;
174 | left: 0;
175 | bottom: 0;
176 | }
177 |
178 | #pagepreview {
179 | width: 100%;
180 | height: 100%;
181 | background-color: white;
182 | border: 0.3px var(--ui-primary-color) solid;
183 | box-sizing: border-box;
184 | }
185 |
186 | #browsertopbar {
187 | width: 100%;
188 | box-sizing: border-box;
189 | }
190 |
191 | #addressbar {
192 | width: 100%;
193 | box-sizing: border-box;
194 | background-color: var(--ui-secondary-color);
195 | color: var(--ui-primary-color);
196 | font-family: var(--ui-font);
197 | border: 3px inset var(--ui-secondary-color);
198 | }
199 |
200 | #reloadbutton, #savebutton, #devtoolsbutton, #filemanagerbutton, #openexternalbutton {
201 | background-color: var(--ui-secondary-color);
202 | color: var(--ui-primary-color);
203 | user-select: none;
204 | font-family: var(--ui-font);
205 | border: 3px outset var(--ui-secondary-color);
206 | }
207 |
208 | #reloadbutton:active, #savebutton:active, #devtoolsbutton:active, #filemanagerbutton:active, #openexternalbutton:active {
209 | border-style: inset;
210 | }
211 |
212 | #fileselect {
213 | background-color: var(--ui-secondary-color);
214 | color: var(--ui-primary-color);
215 | font-family: var(--ui-font);
216 | border: 3px inset var(--ui-secondary-color);
217 | }
218 |
219 | #cdm {
220 | height: 100%;
221 | width: 100%;
222 | position: relative;
223 | }
224 |
225 | #cdm .CodeMirror {
226 | height: 100%;
227 | width: 100%;
228 | }
--------------------------------------------------------------------------------
/appwindow/webviewPreload.js:
--------------------------------------------------------------------------------
1 | window.addEventListener("keydown", function(e) {
2 | if (e.key == "r" && e.ctrlKey) {
3 | e.preventDefault();
4 | location.reload();
5 | }
6 | });
--------------------------------------------------------------------------------
/banner.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | const { app, BrowserWindow, ipcMain, Menu, nativeTheme } = require("electron");
2 | require("@electron/remote/main").initialize();
3 | const fs = require("fs");
4 |
5 | nativeTheme.themeSource = "dark";
6 |
7 | function createWindow() {
8 | var mainWindow = new BrowserWindow({
9 | width: 1400,
10 | height: 800,
11 | backgroundColor: "#222222",
12 | webPreferences: {
13 | nodeIntegration: true,
14 | enableRemoteModule: true,
15 | contextIsolation: false,
16 | webviewTag: true,
17 | devTools: false
18 | },
19 | icon: "appwindow/icon.png",
20 | });
21 |
22 | mainWindow.setMenuBarVisibility(false);
23 | mainWindow.loadFile("appwindow/index.html");
24 |
25 | require("@electron/remote/main").enable(mainWindow.webContents);
26 |
27 | ipcMain.on("updateappsettings", function(data) {
28 | mainWindow.webContents.send("updateappsettings");
29 | });
30 |
31 | }
32 |
33 | app.whenReady().then(function() {
34 | createWindow();
35 | });
36 |
37 | app.on("window-all-closed", function() { app.quit(); });
38 |
39 | ipcMain.on("opensettingswin", function(data) {
40 | var settingsWin = new BrowserWindow({
41 | height: 555,
42 | width: 400,
43 | backgroundColor: "#222222",
44 | resizable: false,
45 | webPreferences: {
46 | devTools: false,
47 | nodeIntegration: true,
48 | enableRemoteModule: true,
49 | contextIsolation: false
50 | }
51 | });
52 |
53 | settingsWin.setMenuBarVisibility(false);
54 | settingsWin.loadFile(__dirname + "/appwindow/settings/index.html");
55 |
56 | require("@electron/remote/main").enable(settingsWin.webContents);
57 | });
58 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webkitty",
3 | "productName": "WebKitty",
4 | "description": "Native web development IDE",
5 | "version": "3.2.1",
6 | "author": {
7 | "name": "Yikuan Sun",
8 | "url": "https://yikuansun.github.io"
9 | },
10 | "contributors": [
11 | "MySpaceEmoCat (https://github.com/MySpaceEmoCat)"
12 | ],
13 | "main": "main.js",
14 | "scripts": {
15 | "start": "electron .",
16 | "pack": "electron-builder --dir",
17 | "dist": "electron-builder --mac --linux --windows"
18 | },
19 | "devDependencies": {
20 | "electron": "^17.1.0",
21 | "electron-builder": "^22.14.5"
22 | },
23 | "dependencies": {
24 | "@codemirror/lang-css": "^6.0.2",
25 | "@codemirror/lang-html": "^6.4.2",
26 | "@codemirror/lang-javascript": "^6.1.4",
27 | "@codemirror/lang-json": "^6.0.1",
28 | "@codemirror/lang-markdown": "^6.1.0",
29 | "@codemirror/lang-python": "^6.1.2",
30 | "@codemirror/lang-xml": "^6.0.2",
31 | "@codemirror/theme-one-dark": "^6.1.1",
32 | "@electron/remote": "^2.0.5",
33 | "adm-zip": "^0.5.5",
34 | "cm6-theme-basic-dark": "^0.2.0",
35 | "cm6-theme-material-dark": "^0.2.0",
36 | "codemirror": "^6.0.1",
37 | "express": "^4.18.2",
38 | "ezserv": "^1.0.0",
39 | "mime-types": "^2.1.35"
40 | },
41 | "build": {
42 | "appId": "com.electron.webkitty",
43 | "directories": {
44 | "buildResources": "resources"
45 | },
46 | "dmg": {
47 | "window": {
48 | "width": 512,
49 | "height": 320
50 | },
51 | "contents": [
52 | {
53 | "x": 160,
54 | "y": 180
55 | },
56 | {
57 | "x": 352,
58 | "y": 180,
59 | "type": "link",
60 | "path": "/Applications"
61 | }
62 | ]
63 | },
64 | "mac": {
65 | "target": "zip"
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/resources/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/resources/background.png
--------------------------------------------------------------------------------
/resources/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/resources/icon.icns
--------------------------------------------------------------------------------
/resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/resources/icon.png
--------------------------------------------------------------------------------
/resources/icons/512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/resources/icons/512x512.png
--------------------------------------------------------------------------------
/screenshots/Screen Shot 2022-04-01 at 4.40.07 PM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/screenshots/Screen Shot 2022-04-01 at 4.40.07 PM.png
--------------------------------------------------------------------------------
/screenshots/Screen Shot 2022-05-12 at 9.22.25 PM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/screenshots/Screen Shot 2022-05-12 at 9.22.25 PM.png
--------------------------------------------------------------------------------
/screenshots/blog.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/screenshots/blog.PNG
--------------------------------------------------------------------------------
/screenshots/lunalgraphics.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/screenshots/lunalgraphics.PNG
--------------------------------------------------------------------------------
/screenshots/yikuansun.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yikuansun/webkitty/eb05ee27ae21aa1b7c76769f62d8b41e7ea69c69/screenshots/yikuansun.PNG
--------------------------------------------------------------------------------