├── .gitignore ├── LICENSE ├── README.md ├── dist ├── index.bundle.html ├── index.bundle.js ├── mode-glsl.js └── theme-monokai.js ├── docs └── example.gif ├── electron └── app.js ├── elements ├── ace-editor.html ├── image-magnifier.html ├── shader-editor.html ├── shader-preview.html ├── zoomable-canvas.html ├── zoomable-element.html └── zoomable-image.html ├── empty-template.ejs ├── images └── cube-icon.png ├── index.html ├── package.json ├── scripts └── DebugShaders.js ├── textures ├── checkerboard.png ├── clouds.png └── random.png └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Garrett Johnson 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 | # webgl-shader-editor 2 | Realtime editor for creating webgl shaders with debugging tools to allow for inspecting local variable definitions in a fragment shader. 3 | 4 | [Demo](https://gkjohnson.github.io/webgl-shader-editor/dist/index.bundle.html) 5 | 6 |  7 | 8 | ## Goal 9 | After lot of frustration with developing shaders and not being able to easily track the calculations within a shader, I wanted explore what the possibilities for enabling a rich shader debugger might be light while still affording the direct shader code editing. 10 | 11 | ## Features 12 | #### Real Time Compilation and Render 13 | As the vertex and fragment shader are updated, the render display is updated and errors are displayed as annotations. 14 | 15 | #### Pixel Zoom 16 | Zooming in on the image zooms in to a pixel view of the image. 17 | 18 | #### Local Variable Value Inspection 19 | Hovering over a pixel in the preview pane will display the values of all the local variables used for that pixel. 20 | 21 | #### Local Variable Color Display 22 | Every local variable will be displayed as a rendered image in the preview bar as though that variable were used to output the color for that fragment. 23 | 24 | #### LocalStorage Saving 25 | The vertex and fragment shader being written are saved and reloaded on refresh 26 | 27 | ## Caveats 28 | - Because colors are stored as 4 8bit values, there is a severe loss of precision when reading out floating point values. [Issue 1](https://github.com/gkjohnson/webgl-shader-editor/issues/2), [Issue 2](https://github.com/gkjohnson/webgl-shader-editor/issues/1) 29 | 30 | ## TODO 31 | - [ ] Add pause button for animated variables 32 | - [ ] Texture upload 33 | - [ ] Uniform variable edit UI 34 | - [ ] Multi-pass shaders, stencil buffer, and screen post-effects 35 | - [ ] Add mouse position into shader uniforms 36 | -------------------------------------------------------------------------------- /dist/index.bundle.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /dist/mode-glsl.js: -------------------------------------------------------------------------------- 1 | define('ace/mode/doc_comment_highlight_rules', ['require', 'exports', 'module', 'ace/lib/oop', 'ace/mode/text_highlight_rules'], function (require, exports, module) { 2 | 3 | 'use strict'; 4 | 5 | var oop = require('../lib/oop'); 6 | var TextHighlightRules = require('./text_highlight_rules').TextHighlightRules; 7 | 8 | var DocCommentHighlightRules = function () { 9 | 10 | this.$rules = { 11 | 'start': [ { 12 | token: 'comment.doc.tag', 13 | regex: '@[\\w\\d_]+' // TODO: fix email addresses 14 | }, 15 | DocCommentHighlightRules.getTagRule(), 16 | { 17 | defaultToken: 'comment.doc', 18 | caseInsensitive: true 19 | }] 20 | }; 21 | 22 | }; 23 | 24 | oop.inherits(DocCommentHighlightRules, TextHighlightRules); 25 | 26 | DocCommentHighlightRules.getTagRule = function (start) { 27 | 28 | return { 29 | token: 'comment.doc.tag.storage.type', 30 | regex: '\\b(?:TODO|FIXME|XXX|HACK)\\b' 31 | }; 32 | 33 | }; 34 | 35 | DocCommentHighlightRules.getStartRule = function (start) { 36 | 37 | return { 38 | token: 'comment.doc', // doc comment 39 | regex: '\\/\\*(?=\\*)', 40 | next: start 41 | }; 42 | 43 | }; 44 | 45 | DocCommentHighlightRules.getEndRule = function (start) { 46 | 47 | return { 48 | token: 'comment.doc', // closing comment 49 | regex: '\\*\\/', 50 | next: start 51 | }; 52 | 53 | }; 54 | 55 | exports.DocCommentHighlightRules = DocCommentHighlightRules; 56 | 57 | }); 58 | 59 | define('ace/mode/c_cpp_highlight_rules', ['require', 'exports', 'module', 'ace/lib/oop', 'ace/mode/doc_comment_highlight_rules', 'ace/mode/text_highlight_rules'], function (require, exports, module) { 60 | 61 | 'use strict'; 62 | 63 | var oop = require('../lib/oop'); 64 | var DocCommentHighlightRules = require('./doc_comment_highlight_rules').DocCommentHighlightRules; 65 | var TextHighlightRules = require('./text_highlight_rules').TextHighlightRules; 66 | var cFunctions = exports.cFunctions = '\\b(?:hypot(?:f|l)?|s(?:scanf|ystem|nprintf|ca(?:nf|lb(?:n(?:f|l)?|ln(?:f|l)?))|i(?:n(?:h(?:f|l)?|f|l)?|gn(?:al|bit))|tr(?:s(?:tr|pn)|nc(?:py|at|mp)|c(?:spn|hr|oll|py|at|mp)|to(?:imax|d|u(?:l(?:l)?|max)|k|f|l(?:d|l)?)|error|pbrk|ftime|len|rchr|xfrm)|printf|et(?:jmp|vbuf|locale|buf)|qrt(?:f|l)?|w(?:scanf|printf)|rand)|n(?:e(?:arbyint(?:f|l)?|xt(?:toward(?:f|l)?|after(?:f|l)?))|an(?:f|l)?)|c(?:s(?:in(?:h(?:f|l)?|f|l)?|qrt(?:f|l)?)|cos(?:h(?:f)?|f|l)?|imag(?:f|l)?|t(?:ime|an(?:h(?:f|l)?|f|l)?)|o(?:s(?:h(?:f|l)?|f|l)?|nj(?:f|l)?|pysign(?:f|l)?)|p(?:ow(?:f|l)?|roj(?:f|l)?)|e(?:il(?:f|l)?|xp(?:f|l)?)|l(?:o(?:ck|g(?:f|l)?)|earerr)|a(?:sin(?:h(?:f|l)?|f|l)?|cos(?:h(?:f|l)?|f|l)?|tan(?:h(?:f|l)?|f|l)?|lloc|rg(?:f|l)?|bs(?:f|l)?)|real(?:f|l)?|brt(?:f|l)?)|t(?:ime|o(?:upper|lower)|an(?:h(?:f|l)?|f|l)?|runc(?:f|l)?|gamma(?:f|l)?|mp(?:nam|file))|i(?:s(?:space|n(?:ormal|an)|cntrl|inf|digit|u(?:nordered|pper)|p(?:unct|rint)|finite|w(?:space|c(?:ntrl|type)|digit|upper|p(?:unct|rint)|lower|al(?:num|pha)|graph|xdigit|blank)|l(?:ower|ess(?:equal|greater)?)|al(?:num|pha)|gr(?:eater(?:equal)?|aph)|xdigit|blank)|logb(?:f|l)?|max(?:div|abs))|di(?:v|fftime)|_Exit|unget(?:c|wc)|p(?:ow(?:f|l)?|ut(?:s|c(?:har)?|wc(?:har)?)|error|rintf)|e(?:rf(?:c(?:f|l)?|f|l)?|x(?:it|p(?:2(?:f|l)?|f|l|m1(?:f|l)?)?))|v(?:s(?:scanf|nprintf|canf|printf|w(?:scanf|printf))|printf|f(?:scanf|printf|w(?:scanf|printf))|w(?:scanf|printf)|a_(?:start|copy|end|arg))|qsort|f(?:s(?:canf|e(?:tpos|ek))|close|tell|open|dim(?:f|l)?|p(?:classify|ut(?:s|c|w(?:s|c))|rintf)|e(?:holdexcept|set(?:e(?:nv|xceptflag)|round)|clearexcept|testexcept|of|updateenv|r(?:aiseexcept|ror)|get(?:e(?:nv|xceptflag)|round))|flush|w(?:scanf|ide|printf|rite)|loor(?:f|l)?|abs(?:f|l)?|get(?:s|c|pos|w(?:s|c))|re(?:open|e|ad|xp(?:f|l)?)|m(?:in(?:f|l)?|od(?:f|l)?|a(?:f|l|x(?:f|l)?)?))|l(?:d(?:iv|exp(?:f|l)?)|o(?:ngjmp|cal(?:time|econv)|g(?:1(?:p(?:f|l)?|0(?:f|l)?)|2(?:f|l)?|f|l|b(?:f|l)?)?)|abs|l(?:div|abs|r(?:int(?:f|l)?|ound(?:f|l)?))|r(?:int(?:f|l)?|ound(?:f|l)?)|gamma(?:f|l)?)|w(?:scanf|c(?:s(?:s(?:tr|pn)|nc(?:py|at|mp)|c(?:spn|hr|oll|py|at|mp)|to(?:imax|d|u(?:l(?:l)?|max)|k|f|l(?:d|l)?|mbs)|pbrk|ftime|len|r(?:chr|tombs)|xfrm)|to(?:b|mb)|rtomb)|printf|mem(?:set|c(?:hr|py|mp)|move))|a(?:s(?:sert|ctime|in(?:h(?:f|l)?|f|l)?)|cos(?:h(?:f|l)?|f|l)?|t(?:o(?:i|f|l(?:l)?)|exit|an(?:h(?:f|l)?|2(?:f|l)?|f|l)?)|b(?:s|ort))|g(?:et(?:s|c(?:har)?|env|wc(?:har)?)|mtime)|r(?:int(?:f|l)?|ound(?:f|l)?|e(?:name|alloc|wind|m(?:ove|quo(?:f|l)?|ainder(?:f|l)?))|a(?:nd|ise))|b(?:search|towc)|m(?:odf(?:f|l)?|em(?:set|c(?:hr|py|mp)|move)|ktime|alloc|b(?:s(?:init|towcs|rtowcs)|towc|len|r(?:towc|len))))\\b'; 67 | 68 | var c_cppHighlightRules = function () { 69 | 70 | var keywordControls = ( 71 | 'break|case|continue|default|do|else|for|goto|if|_Pragma|' + 72 | 'return|switch|while|catch|operator|try|throw|using' 73 | ); 74 | 75 | var storageType = ( 76 | 'asm|__asm__|auto|bool|_Bool|char|_Complex|double|enum|float|' + 77 | '_Imaginary|int|long|short|signed|struct|typedef|union|unsigned|void|' + 78 | 'class|wchar_t|template|char16_t|char32_t' 79 | ); 80 | 81 | var storageModifiers = ( 82 | 'const|extern|register|restrict|static|volatile|inline|private|' + 83 | 'protected|public|friend|explicit|virtual|export|mutable|typename|' + 84 | 'constexpr|new|delete|alignas|alignof|decltype|noexcept|thread_local' 85 | ); 86 | 87 | var keywordOperators = ( 88 | 'and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq' + 89 | 'const_cast|dynamic_cast|reinterpret_cast|static_cast|sizeof|namespace' 90 | ); 91 | 92 | var builtinConstants = ( 93 | 'NULL|true|false|TRUE|FALSE|nullptr' 94 | ); 95 | 96 | var keywordMapper = this.$keywords = this.createKeywordMapper({ 97 | 'keyword.control': keywordControls, 98 | 'storage.type': storageType, 99 | 'storage.modifier': storageModifiers, 100 | 'keyword.operator': keywordOperators, 101 | 'variable.language': 'this', 102 | 'constant.language': builtinConstants 103 | }, 'identifier'); 104 | 105 | var identifierRe = '[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*\\b'; 106 | var escapeRe = /\\(?:['"?\\abfnrtv]|[0-7]{1,3}|x[a-fA-F\d]{2}|u[a-fA-F\d]{4}U[a-fA-F\d]{8}|.)/.source; 107 | 108 | this.$rules = { 109 | 'start': [ 110 | { 111 | token: 'comment', 112 | regex: '//$', 113 | next: 'start' 114 | }, { 115 | token: 'comment', 116 | regex: '//', 117 | next: 'singleLineComment' 118 | }, 119 | DocCommentHighlightRules.getStartRule('doc-start'), 120 | { 121 | token: 'comment', // multi line comment 122 | regex: '\\/\\*', 123 | next: 'comment' 124 | }, { 125 | token: 'string', // character 126 | regex: "'(?:" + escapeRe + "|.)?'" 127 | }, { 128 | token: 'string.start', 129 | regex: '"', 130 | stateName: 'qqstring', 131 | next: [ 132 | { token: 'string', regex: /\\\s*$/, next: 'qqstring' }, 133 | { token: 'constant.language.escape', regex: escapeRe }, 134 | { token: 'constant.language.escape', regex: /%[^'"\\]/ }, 135 | { token: 'string.end', regex: '"|$', next: 'start' }, 136 | { defaultToken: 'string'} 137 | ] 138 | }, { 139 | token: 'string.start', 140 | regex: 'R"\\(', 141 | stateName: 'rawString', 142 | next: [ 143 | { token: 'string.end', regex: '\\)"', next: 'start' }, 144 | { defaultToken: 'string'} 145 | ] 146 | }, { 147 | token: 'constant.numeric', // hex 148 | regex: '0[xX][0-9a-fA-F]+(L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b' 149 | }, { 150 | token: 'constant.numeric', // float 151 | regex: '[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?(L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b' 152 | }, { 153 | token: 'keyword', // pre-compiler directives 154 | regex: '#\\s*(?:include|import|pragma|line|define|undef)\\b', 155 | next: 'directive' 156 | }, { 157 | token: 'keyword', // special case pre-compiler directive 158 | regex: '#\\s*(?:endif|if|ifdef|else|elif|ifndef)\\b' 159 | }, { 160 | token: 'support.function.C99.c', 161 | regex: cFunctions 162 | }, { 163 | token: keywordMapper, 164 | regex: '[a-zA-Z_$][a-zA-Z0-9_$]*' 165 | }, { 166 | token: 'keyword.operator', 167 | regex: /--|\+\+|<<=|>>=|>>>=|<>|&&|\|\||\?:|[*%\/+\-&\^|~!<>=]=?/ 168 | }, { 169 | token: 'punctuation.operator', 170 | regex: '\\?|\\:|\\,|\\;|\\.' 171 | }, { 172 | token: 'paren.lparen', 173 | regex: '[[({]' 174 | }, { 175 | token: 'paren.rparen', 176 | regex: '[\\])}]' 177 | }, { 178 | token: 'text', 179 | regex: '\\s+' 180 | } 181 | ], 182 | 'comment': [ 183 | { 184 | token: 'comment', // closing comment 185 | regex: '\\*\\/', 186 | next: 'start' 187 | }, { 188 | defaultToken: 'comment' 189 | } 190 | ], 191 | 'singleLineComment': [ 192 | { 193 | token: 'comment', 194 | regex: /\\$/, 195 | next: 'singleLineComment' 196 | }, { 197 | token: 'comment', 198 | regex: /$/, 199 | next: 'start' 200 | }, { 201 | defaultToken: 'comment' 202 | } 203 | ], 204 | 'directive': [ 205 | { 206 | token: 'constant.other.multiline', 207 | regex: /\\/ 208 | }, 209 | { 210 | token: 'constant.other.multiline', 211 | regex: /.*\\/ 212 | }, 213 | { 214 | token: 'constant.other', 215 | regex: '\\s*<.+?>', 216 | next: 'start' 217 | }, 218 | { 219 | token: 'constant.other', // single line 220 | regex: '\\s*["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]', 221 | next: 'start' 222 | }, 223 | { 224 | token: 'constant.other', // single line 225 | regex: "\\s*['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']", 226 | next: 'start' 227 | }, 228 | { 229 | token: 'constant.other', 230 | regex: /[^\\\/]+/, 231 | next: 'start' 232 | } 233 | ] 234 | }; 235 | 236 | this.embedRules(DocCommentHighlightRules, 'doc-', 237 | [ DocCommentHighlightRules.getEndRule('start') ]); 238 | this.normalizeRules(); 239 | 240 | }; 241 | 242 | oop.inherits(c_cppHighlightRules, TextHighlightRules); 243 | 244 | exports.c_cppHighlightRules = c_cppHighlightRules; 245 | 246 | }); 247 | 248 | define('ace/mode/matching_brace_outdent', ['require', 'exports', 'module', 'ace/range'], function (require, exports, module) { 249 | 250 | 'use strict'; 251 | 252 | var Range = require('../range').Range; 253 | 254 | var MatchingBraceOutdent = function () {}; 255 | 256 | (function () { 257 | 258 | this.checkOutdent = function (line, input) { 259 | 260 | if (!/^\s+$/.test(line)) { 261 | 262 | return false; 263 | 264 | } 265 | 266 | return /^\s*\}/.test(input); 267 | 268 | }; 269 | 270 | this.autoOutdent = function (doc, row) { 271 | 272 | var line = doc.getLine(row); 273 | var match = line.match(/^(\s*\})/); 274 | 275 | if (!match) return 0; 276 | 277 | var column = match[1].length; 278 | var openBracePos = doc.findMatchingBracket({row: row, column: column}); 279 | 280 | if (!openBracePos || openBracePos.row == row) return 0; 281 | 282 | var indent = this.$getIndent(doc.getLine(openBracePos.row)); 283 | doc.replace(new Range(row, 0, row, column - 1), indent); 284 | 285 | }; 286 | 287 | this.$getIndent = function (line) { 288 | 289 | return line.match(/^\s*/)[0]; 290 | 291 | }; 292 | 293 | }).call(MatchingBraceOutdent.prototype); 294 | 295 | exports.MatchingBraceOutdent = MatchingBraceOutdent; 296 | 297 | }); 298 | 299 | define('ace/mode/folding/cstyle', ['require', 'exports', 'module', 'ace/lib/oop', 'ace/range', 'ace/mode/folding/fold_mode'], function (require, exports, module) { 300 | 301 | 'use strict'; 302 | 303 | var oop = require('../../lib/oop'); 304 | var Range = require('../../range').Range; 305 | var BaseFoldMode = require('./fold_mode').FoldMode; 306 | 307 | var FoldMode = exports.FoldMode = function (commentRegex) { 308 | 309 | if (commentRegex) { 310 | 311 | this.foldingStartMarker = new RegExp( 312 | this.foldingStartMarker.source.replace(/\|[^|]*?$/, '|' + commentRegex.start) 313 | ); 314 | this.foldingStopMarker = new RegExp( 315 | this.foldingStopMarker.source.replace(/\|[^|]*?$/, '|' + commentRegex.end) 316 | ); 317 | 318 | } 319 | 320 | }; 321 | oop.inherits(FoldMode, BaseFoldMode); 322 | 323 | (function () { 324 | 325 | this.foldingStartMarker = /(\{|\[)[^\}\]]*$|^\s*(\/\*)/; 326 | this.foldingStopMarker = /^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/; 327 | this.singleLineBlockCommentRe = /^\s*(\/\*).*\*\/\s*$/; 328 | this.tripleStarBlockCommentRe = /^\s*(\/\*\*\*).*\*\/\s*$/; 329 | this.startRegionRe = /^\s*(\/\*|\/\/)#?region\b/; 330 | this._getFoldWidgetBase = this.getFoldWidget; 331 | this.getFoldWidget = function (session, foldStyle, row) { 332 | 333 | var line = session.getLine(row); 334 | 335 | if (this.singleLineBlockCommentRe.test(line)) { 336 | 337 | if (!this.startRegionRe.test(line) && !this.tripleStarBlockCommentRe.test(line)) { 338 | 339 | return ''; 340 | 341 | } 342 | 343 | } 344 | 345 | var fw = this._getFoldWidgetBase(session, foldStyle, row); 346 | 347 | if (!fw && this.startRegionRe.test(line)) { 348 | 349 | return 'start'; 350 | 351 | } // lineCommentRegionStart 352 | 353 | return fw; 354 | 355 | }; 356 | 357 | this.getFoldWidgetRange = function (session, foldStyle, row, forceMultiline) { 358 | 359 | var line = session.getLine(row); 360 | 361 | if (this.startRegionRe.test(line)) { 362 | 363 | return this.getCommentRegionBlock(session, line, row); 364 | 365 | } 366 | 367 | var match = line.match(this.foldingStartMarker); 368 | if (match) { 369 | 370 | var i = match.index; 371 | 372 | if (match[1]) { 373 | 374 | return this.openingBracketBlock(session, match[1], row, i); 375 | 376 | } 377 | 378 | var range = session.getCommentFoldRange(row, i + match[0].length, 1); 379 | 380 | if (range && !range.isMultiLine()) { 381 | 382 | if (forceMultiline) { 383 | 384 | range = this.getSectionRange(session, row); 385 | 386 | } else if (foldStyle != 'all') { 387 | 388 | range = null; 389 | 390 | } 391 | 392 | } 393 | 394 | return range; 395 | 396 | } 397 | 398 | if (foldStyle === 'markbegin') { 399 | 400 | return; 401 | 402 | } 403 | 404 | var match = line.match(this.foldingStopMarker); 405 | if (match) { 406 | 407 | var i = match.index + match[0].length; 408 | 409 | if (match[1]) { 410 | 411 | return this.closingBracketBlock(session, match[1], row, i); 412 | 413 | } 414 | 415 | return session.getCommentFoldRange(row, i, -1); 416 | 417 | } 418 | 419 | }; 420 | 421 | this.getSectionRange = function (session, row) { 422 | 423 | var line = session.getLine(row); 424 | var startIndent = line.search(/\S/); 425 | var startRow = row; 426 | var startColumn = line.length; 427 | row = row + 1; 428 | var endRow = row; 429 | var maxRow = session.getLength(); 430 | while (++row < maxRow) { 431 | 432 | line = session.getLine(row); 433 | var indent = line.search(/\S/); 434 | if (indent === -1) { 435 | 436 | continue; 437 | 438 | } 439 | if (startIndent > indent) { 440 | 441 | break; 442 | 443 | } 444 | var subRange = this.getFoldWidgetRange(session, 'all', row); 445 | 446 | if (subRange) { 447 | 448 | if (subRange.start.row <= startRow) { 449 | 450 | break; 451 | 452 | } else if (subRange.isMultiLine()) { 453 | 454 | row = subRange.end.row; 455 | 456 | } else if (startIndent == indent) { 457 | 458 | break; 459 | 460 | } 461 | 462 | } 463 | endRow = row; 464 | 465 | } 466 | 467 | return new Range(startRow, startColumn, endRow, session.getLine(endRow).length); 468 | 469 | }; 470 | this.getCommentRegionBlock = function (session, line, row) { 471 | 472 | var startColumn = line.search(/\s*$/); 473 | var maxRow = session.getLength(); 474 | var startRow = row; 475 | 476 | var re = /^\s*(?:\/\*|\/\/|--)#?(end)?region\b/; 477 | var depth = 1; 478 | while (++row < maxRow) { 479 | 480 | line = session.getLine(row); 481 | var m = re.exec(line); 482 | if (!m) continue; 483 | if (m[1]) depth--; 484 | else depth++; 485 | 486 | if (!depth) break; 487 | 488 | } 489 | 490 | var endRow = row; 491 | if (endRow > startRow) { 492 | 493 | return new Range(startRow, startColumn, endRow, line.length); 494 | 495 | } 496 | 497 | }; 498 | 499 | }).call(FoldMode.prototype); 500 | 501 | }); 502 | 503 | define('ace/mode/c_cpp', ['require', 'exports', 'module', 'ace/lib/oop', 'ace/mode/text', 'ace/mode/c_cpp_highlight_rules', 'ace/mode/matching_brace_outdent', 'ace/range', 'ace/mode/behaviour/cstyle', 'ace/mode/folding/cstyle'], function (require, exports, module) { 504 | 505 | 'use strict'; 506 | 507 | var oop = require('../lib/oop'); 508 | var TextMode = require('./text').Mode; 509 | var c_cppHighlightRules = require('./c_cpp_highlight_rules').c_cppHighlightRules; 510 | var MatchingBraceOutdent = require('./matching_brace_outdent').MatchingBraceOutdent; 511 | var Range = require('../range').Range; 512 | var CstyleBehaviour = require('./behaviour/cstyle').CstyleBehaviour; 513 | var CStyleFoldMode = require('./folding/cstyle').FoldMode; 514 | 515 | var Mode = function () { 516 | 517 | this.HighlightRules = c_cppHighlightRules; 518 | 519 | this.$outdent = new MatchingBraceOutdent(); 520 | this.$behaviour = new CstyleBehaviour(); 521 | 522 | this.foldingRules = new CStyleFoldMode(); 523 | 524 | }; 525 | oop.inherits(Mode, TextMode); 526 | 527 | (function () { 528 | 529 | this.lineCommentStart = '//'; 530 | this.blockComment = {start: '/*', end: '*/'}; 531 | 532 | this.getNextLineIndent = function (state, line, tab) { 533 | 534 | var indent = this.$getIndent(line); 535 | 536 | var tokenizedLine = this.getTokenizer().getLineTokens(line, state); 537 | var tokens = tokenizedLine.tokens; 538 | var endState = tokenizedLine.state; 539 | 540 | if (tokens.length && tokens[tokens.length - 1].type == 'comment') { 541 | 542 | return indent; 543 | 544 | } 545 | 546 | if (state == 'start') { 547 | 548 | var match = line.match(/^.*[\{\(\[]\s*$/); 549 | if (match) { 550 | 551 | indent += tab; 552 | 553 | } 554 | 555 | } else if (state == 'doc-start') { 556 | 557 | if (endState == 'start') { 558 | 559 | return ''; 560 | 561 | } 562 | var match = line.match(/^\s*(\/?)\*/); 563 | if (match) { 564 | 565 | if (match[1]) { 566 | 567 | indent += ' '; 568 | 569 | } 570 | indent += '* '; 571 | 572 | } 573 | 574 | } 575 | 576 | return indent; 577 | 578 | }; 579 | 580 | this.checkOutdent = function (state, line, input) { 581 | 582 | return this.$outdent.checkOutdent(line, input); 583 | 584 | }; 585 | 586 | this.autoOutdent = function (state, doc, row) { 587 | 588 | this.$outdent.autoOutdent(doc, row); 589 | 590 | }; 591 | 592 | this.$id = 'ace/mode/c_cpp'; 593 | 594 | }).call(Mode.prototype); 595 | 596 | exports.Mode = Mode; 597 | 598 | }); 599 | 600 | define('ace/mode/glsl_highlight_rules', ['require', 'exports', 'module', 'ace/lib/oop', 'ace/mode/c_cpp_highlight_rules'], function (require, exports, module) { 601 | 602 | 'use strict'; 603 | 604 | var oop = require('../lib/oop'); 605 | var c_cppHighlightRules = require('./c_cpp_highlight_rules').c_cppHighlightRules; 606 | 607 | var glslHighlightRules = function () { 608 | 609 | var keywords = ( 610 | 'attribute|const|uniform|varying|break|continue|do|for|while|' + 611 | 'if|else|in|out|inout|float|int|void|bool|true|false|' + 612 | 'lowp|mediump|highp|precision|invariant|discard|return|mat2|mat3|' + 613 | 'mat4|vec2|vec3|vec4|ivec2|ivec3|ivec4|bvec2|bvec3|bvec4|sampler2D|' + 614 | 'samplerCube|struct' 615 | ); 616 | 617 | var buildinConstants = ( 618 | 'radians|degrees|sin|cos|tan|asin|acos|atan|pow|' + 619 | 'exp|log|exp2|log2|sqrt|inversesqrt|abs|sign|floor|ceil|fract|mod|' + 620 | 'min|max|clamp|mix|step|smoothstep|length|distance|dot|cross|' + 621 | 'normalize|faceforward|reflect|refract|matrixCompMult|lessThan|' + 622 | 'lessThanEqual|greaterThan|greaterThanEqual|equal|notEqual|any|all|' + 623 | 'not|dFdx|dFdy|fwidth|texture2D|texture2DProj|texture2DLod|' + 624 | 'texture2DProjLod|textureCube|textureCubeLod|' + 625 | 'gl_MaxVertexAttribs|gl_MaxVertexUniformVectors|gl_MaxVaryingVectors|' + 626 | 'gl_MaxVertexTextureImageUnits|gl_MaxCombinedTextureImageUnits|' + 627 | 'gl_MaxTextureImageUnits|gl_MaxFragmentUniformVectors|gl_MaxDrawBuffers|' + 628 | 'gl_DepthRangeParameters|gl_DepthRange|' + 629 | 'gl_Position|gl_PointSize|' + 630 | 'gl_FragCoord|gl_FrontFacing|gl_PointCoord|gl_FragColor|gl_FragData' 631 | ); 632 | 633 | var keywordMapper = this.createKeywordMapper({ 634 | 'variable.language': 'this', 635 | 'keyword': keywords, 636 | 'constant.language': buildinConstants 637 | }, 'identifier'); 638 | 639 | this.$rules = new c_cppHighlightRules().$rules; 640 | this.$rules.start.forEach(function (rule) { 641 | 642 | if (typeof rule.token === 'function') { 643 | 644 | rule.token = keywordMapper; 645 | 646 | } 647 | 648 | }); 649 | 650 | }; 651 | 652 | oop.inherits(glslHighlightRules, c_cppHighlightRules); 653 | 654 | exports.glslHighlightRules = glslHighlightRules; 655 | 656 | }); 657 | 658 | define('ace/mode/glsl', ['require', 'exports', 'module', 'ace/lib/oop', 'ace/mode/c_cpp', 'ace/mode/glsl_highlight_rules', 'ace/mode/matching_brace_outdent', 'ace/range', 'ace/mode/behaviour/cstyle', 'ace/mode/folding/cstyle'], function (require, exports, module) { 659 | 660 | 'use strict'; 661 | 662 | var oop = require('../lib/oop'); 663 | var CMode = require('./c_cpp').Mode; 664 | var glslHighlightRules = require('./glsl_highlight_rules').glslHighlightRules; 665 | var MatchingBraceOutdent = require('./matching_brace_outdent').MatchingBraceOutdent; 666 | var Range = require('../range').Range; 667 | var CstyleBehaviour = require('./behaviour/cstyle').CstyleBehaviour; 668 | var CStyleFoldMode = require('./folding/cstyle').FoldMode; 669 | 670 | var Mode = function () { 671 | 672 | this.HighlightRules = glslHighlightRules; 673 | 674 | this.$outdent = new MatchingBraceOutdent(); 675 | this.$behaviour = new CstyleBehaviour(); 676 | this.foldingRules = new CStyleFoldMode(); 677 | 678 | }; 679 | oop.inherits(Mode, CMode); 680 | 681 | (function () { 682 | 683 | this.$id = 'ace/mode/glsl'; 684 | 685 | }).call(Mode.prototype); 686 | 687 | exports.Mode = Mode; 688 | 689 | }); 690 | -------------------------------------------------------------------------------- /dist/theme-monokai.js: -------------------------------------------------------------------------------- 1 | define('ace/theme/monokai', ['require', 'exports', 'module', 'ace/lib/dom'], function (require, exports, module) { 2 | 3 | exports.isDark = true; 4 | exports.cssClass = 'ace-monokai'; 5 | exports.cssText = '.ace-monokai .ace_gutter {\ 6 | background: #2F3129;\ 7 | color: #8F908A\ 8 | }\ 9 | .ace-monokai .ace_print-margin {\ 10 | width: 1px;\ 11 | background: #555651\ 12 | }\ 13 | .ace-monokai {\ 14 | background-color: #272822;\ 15 | color: #F8F8F2\ 16 | }\ 17 | .ace-monokai .ace_cursor {\ 18 | color: #F8F8F0\ 19 | }\ 20 | .ace-monokai .ace_marker-layer .ace_selection {\ 21 | background: #49483E\ 22 | }\ 23 | .ace-monokai.ace_multiselect .ace_selection.ace_start {\ 24 | box-shadow: 0 0 3px 0px #272822;\ 25 | }\ 26 | .ace-monokai .ace_marker-layer .ace_step {\ 27 | background: rgb(102, 82, 0)\ 28 | }\ 29 | .ace-monokai .ace_marker-layer .ace_bracket {\ 30 | margin: -1px 0 0 -1px;\ 31 | border: 1px solid #49483E\ 32 | }\ 33 | .ace-monokai .ace_marker-layer .ace_active-line {\ 34 | background: #202020\ 35 | }\ 36 | .ace-monokai .ace_gutter-active-line {\ 37 | background-color: #272727\ 38 | }\ 39 | .ace-monokai .ace_marker-layer .ace_selected-word {\ 40 | border: 1px solid #49483E\ 41 | }\ 42 | .ace-monokai .ace_invisible {\ 43 | color: #52524d\ 44 | }\ 45 | .ace-monokai .ace_entity.ace_name.ace_tag,\ 46 | .ace-monokai .ace_keyword,\ 47 | .ace-monokai .ace_meta.ace_tag,\ 48 | .ace-monokai .ace_storage {\ 49 | color: #F92672\ 50 | }\ 51 | .ace-monokai .ace_punctuation,\ 52 | .ace-monokai .ace_punctuation.ace_tag {\ 53 | color: #fff\ 54 | }\ 55 | .ace-monokai .ace_constant.ace_character,\ 56 | .ace-monokai .ace_constant.ace_language,\ 57 | .ace-monokai .ace_constant.ace_numeric,\ 58 | .ace-monokai .ace_constant.ace_other {\ 59 | color: #AE81FF\ 60 | }\ 61 | .ace-monokai .ace_invalid {\ 62 | color: #F8F8F0;\ 63 | background-color: #F92672\ 64 | }\ 65 | .ace-monokai .ace_invalid.ace_deprecated {\ 66 | color: #F8F8F0;\ 67 | background-color: #AE81FF\ 68 | }\ 69 | .ace-monokai .ace_support.ace_constant,\ 70 | .ace-monokai .ace_support.ace_function {\ 71 | color: #66D9EF\ 72 | }\ 73 | .ace-monokai .ace_fold {\ 74 | background-color: #A6E22E;\ 75 | border-color: #F8F8F2\ 76 | }\ 77 | .ace-monokai .ace_storage.ace_type,\ 78 | .ace-monokai .ace_support.ace_class,\ 79 | .ace-monokai .ace_support.ace_type {\ 80 | font-style: italic;\ 81 | color: #66D9EF\ 82 | }\ 83 | .ace-monokai .ace_entity.ace_name.ace_function,\ 84 | .ace-monokai .ace_entity.ace_other,\ 85 | .ace-monokai .ace_entity.ace_other.ace_attribute-name,\ 86 | .ace-monokai .ace_variable {\ 87 | color: #A6E22E\ 88 | }\ 89 | .ace-monokai .ace_variable.ace_parameter {\ 90 | font-style: italic;\ 91 | color: #FD971F\ 92 | }\ 93 | .ace-monokai .ace_string {\ 94 | color: #E6DB74\ 95 | }\ 96 | .ace-monokai .ace_comment {\ 97 | color: #75715E\ 98 | }\ 99 | .ace-monokai .ace_indent-guide {\ 100 | background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWPQ0FD0ZXBzd/wPAAjVAoxeSgNeAAAAAElFTkSuQmCC) right repeat-y\ 101 | }'; 102 | 103 | var dom = require('../lib/dom'); 104 | dom.importCssString(exports.cssText, exports.cssClass); 105 | 106 | }); 107 | -------------------------------------------------------------------------------- /docs/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gkjohnson/webgl-shader-editor/4bcfb53aec5ad59b1c7fc180c892169964914eca/docs/example.gif -------------------------------------------------------------------------------- /electron/app.js: -------------------------------------------------------------------------------- 1 | const { 2 | app, 3 | BrowserWindow, 4 | Menu 5 | } = require('electron'); 6 | 7 | app.on('ready', () => { 8 | 9 | const main = new BrowserWindow(); 10 | main.loadURL(`${__dirname}/../index.html`); 11 | main.setMenu(null); 12 | 13 | }); 14 | 15 | app.on('window-all-closed', () => app.quit()); 16 | 17 | Menu.setApplicationMenu(new Menu()); 18 | -------------------------------------------------------------------------------- /elements/ace-editor.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 99 | -------------------------------------------------------------------------------- /elements/image-magnifier.html: -------------------------------------------------------------------------------- 1 |