├── .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 | ![Logo](https://raw.githubusercontent.com/yikuansun/webkitty/master/banner.svg) 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 | ![App Screenshot](https://raw.githubusercontent.com/yikuansun/webkitty/30ace57f6263da706dce163de56b8becee2da4a6/screenshots/Screen%20Shot%202022-05-12%20at%209.22.25%20PM.png) 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 |
25 |
26 |
27 |
28 | 29 | 30 | 35 | 36 | 37 | 41 | 42 |
31 | 32 | 33 | 34 |
38 | 39 |
40 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | 50 | 51 | 66 | 67 | 68 | 71 | 72 | 73 | 77 | 78 |
52 | 53 | 54 | 57 | 60 | 63 | 64 |
55 | 56 | 58 | 59 | 61 | 62 |
65 |
69 | 70 |
74 | 75 | 76 |
79 |
80 |
81 |
82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /appwindow/settings/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Settings - WebKitty 5 | 37 | 38 | 39 |
40 |
41 |
42 | Editor 43 | 44 |
Code Font Size: 45 |
46 |



Theme 47 |
Primary Color: 48 |
Secondary Color: 49 |
Background Color: 50 |



Site Preview 51 |
Referrer URL: 52 |
Custom User Agent: 53 |



54 |
55 |
56 |
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 --------------------------------------------------------------------------------