├── .gitignore ├── README.rst ├── docs.sh ├── docs ├── dochack.js ├── example.html ├── main.js ├── nimdoc.out.css ├── nimedit-worker.js ├── nimedit.js ├── nimexample.html ├── quill.html ├── quill.idx ├── quill │ ├── ext │ │ ├── gutters.html │ │ └── gutters.idx │ ├── utils.html │ └── utils.idx └── theindex.html ├── js ├── binds.nim ├── classbinder.nim ├── config.nims ├── makejs.sh ├── mybinds.nim ├── quill.css └── quill.js ├── quill.nimble ├── src ├── quill.nim └── quill │ ├── ext │ └── gutters.nim │ └── utils.nim └── tests ├── config.nims ├── main.nim └── nimedit ├── config.nims ├── main.nim ├── worker.nim └── workerapi.nim /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore files with no extention: 2 | * 3 | !*/ 4 | !*.* 5 | 6 | # normal ignores: 7 | *.exe 8 | nimcache 9 | *.pdb 10 | *.ilk 11 | .* 12 | 13 | # R ingores: 14 | *.nix 15 | nimv.txt 16 | BOOTLOG 17 | nimble-env 18 | boot.sh 19 | init.sh 20 | 21 | # Java 22 | **/*.jar 23 | 24 | # Ingore built 25 | **/*.js 26 | !docs/*.js 27 | !js/*.js 28 | 29 | # Save gitignore 30 | !*.gitignore -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ====== 2 | Quill 3 | ====== 4 | 5 | Quill is a nim js library for making text editors, it is made completely in nim 6 | and is designed to be easy to use. 7 | 8 | Example 9 | ======= 10 | 11 | The example can be found at https://thatrandomperson5.github.io/Quill/example 12 | 13 | Docs 14 | ===== 15 | Docs can be found at https://thatrandomperson5.github.io/Quill/quill 16 | 17 | Installing 18 | ========== 19 | .. code:: 20 | 21 | nimble install https://github.com/thatrandomperson5/Quill 22 | .. 23 | 24 | Guide 25 | ======= 26 | 27 | We first need to add some css to our main html file: 28 | 29 | .. code:: html 30 | 31 | 32 | 33 | .. 34 | 35 | After that we can start coding. A quill has a structure to get it to work: 36 | 37 | * Create the quill 38 | * Set any default text 39 | * Add `onDraw` 40 | 41 | * Main html generation 42 | * Sector entering or forced redraw 43 | * Init extentions and then quill 44 | 45 | Here is a basic example: 46 | 47 | .. code:: nim 48 | 49 | # From tests 50 | import quill, dom 51 | 52 | var myquill = newQuill(document.getElementById("quill"), "70vh") # create the quill with height of 70 53 | myquill.text = "Hello world" # Set the default text 54 | myquill.onDraw = proc (q: var Quill, str: cstring, isDel: bool) = 55 | # Main html generation 56 | let txt = document.createElement("span") 57 | txt.appendChild document.createTextNode(str) 58 | q.draw(txt) 59 | 60 | myquill.init() # Start the quill 61 | .. 62 | 63 | **Note:** There is no reason to redraw if you don't use `insert()`, will be explained in more detail later on. 64 | 65 | This is the most basic quill! But it does not do anything, so is there anything diffrent then a normal textarea? Well, the draw proc takes html, so you can color and style as much as you want! 66 | Try making the span a random color! You can also use the features described below to help you. 67 | 68 | Insert and sectors 69 | ================== 70 | 71 | You can have a `insert()` call in your ondraw proc. This adds text, for example an indent, to your quill. This inserts at the current cursor position, 72 | if you want to add text a diffrent way, use the `text=` proc. 73 | 74 | There are also sectors, which reduce the text passed to onDraw. Say for example your quill only needed to process words and you have this sentance: `hello world good souls`. Normally your draw proc would get passed the whole thing each time, but when processing "world", your code does not need anything from "hello". So, when you detect a space, you would enter a sector using `enter()`. This would mean if the word "hello" was changed to "hi" you would get passed "hi" instead of "hi world good souls". This feature is mainly for efficiency, and a full example can be found in the tests folder. 75 | 76 | **Note**: Why is my inserted text not showing? You have to have a `myquill.forceRedraw()` at the end to make it show. 77 | 78 | **Warning**: `forceRedraw()` and `enter()` must be the last call in your draw proc, also note that `enter()` replaces `forceRedraw()`. 79 | 80 | 81 | Gutter 82 | ======== 83 | To add a gutter (numbers on the side), just add the code below: 84 | 85 | .. code:: nim 86 | 87 | # After import quill 88 | import quill/ext/gutters 89 | 90 | # Right before myquill.init() 91 | myquill.initGutter() 92 | .. -------------------------------------------------------------------------------- /docs.sh: -------------------------------------------------------------------------------- 1 | cd ./src 2 | nim doc -d:nimsuggest --project --index:on --outdir:../docs quill.nim -------------------------------------------------------------------------------- /docs/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/nimdoc.out.css: -------------------------------------------------------------------------------- 1 | /* 2 | Stylesheet for use with Docutils/rst2html. 3 | 4 | See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to 5 | customize this style sheet. 6 | 7 | Modified from Chad Skeeters' rst2html-style 8 | https://bitbucket.org/cskeeters/rst2html-style/ 9 | 10 | Modified by Boyd Greenfield and narimiran 11 | */ 12 | 13 | :root { 14 | --primary-background: #fff; 15 | --secondary-background: ghostwhite; 16 | --third-background: #e8e8e8; 17 | --info-background: #50c050; 18 | --warning-background: #c0a000; 19 | --error-background: #e04040; 20 | --border: #dde; 21 | --text: #222; 22 | --anchor: #07b; 23 | --anchor-focus: #607c9f; 24 | --input-focus: #1fa0eb; 25 | --strong: #3c3c3c; 26 | --hint: #9A9A9A; 27 | --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN4AAAA9CAYAAADCt9ebAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjAzOjQ4KzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMjoyODo0MSswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMjoyODo0MSswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMzM0ZjAxYS0yMDExLWE1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"); 28 | 29 | --keyword: #5e8f60; 30 | --identifier: #222; 31 | --comment: #484a86; 32 | --operator: #155da4; 33 | --punctuation: black; 34 | --other: black; 35 | --escapeSequence: #c4891b; 36 | --number: #252dbe; 37 | --literal: #a4255b; 38 | --program: #6060c0; 39 | --option: #508000; 40 | --raw-data: #a4255b; 41 | } 42 | 43 | [data-theme="dark"] { 44 | --primary-background: #171921; 45 | --secondary-background: #1e202a; 46 | --third-background: #2b2e3b; 47 | --info-background: #008000; 48 | --warning-background: #807000; 49 | --error-background: #c03000; 50 | --border: #0e1014; 51 | --text: #fff; 52 | --anchor: #8be9fd; 53 | --anchor-focus: #8be9fd; 54 | --input-focus: #8be9fd; 55 | --strong: #bd93f9; 56 | --hint: #7A7C85; 57 | --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARMAAABMCAYAAABOBlMuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjE4OjIyKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQy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"); 58 | 59 | --keyword: #ff79c6; 60 | --identifier: #f8f8f2; 61 | --comment: #6272a4; 62 | --operator: #ff79c6; 63 | --punctuation: #f8f8f2; 64 | --other: #f8f8f2; 65 | --escapeSequence: #bd93f9; 66 | --number: #bd93f9; 67 | --literal: #f1fa8c; 68 | --program: #9090c0; 69 | --option: #90b010; 70 | --raw-data: #8be9fd; 71 | } 72 | 73 | .theme-switch-wrapper { 74 | display: flex; 75 | align-items: center; 76 | } 77 | 78 | .theme-switch-wrapper em { 79 | margin-left: 10px; 80 | font-size: 1rem; 81 | } 82 | 83 | .theme-switch { 84 | display: inline-block; 85 | height: 22px; 86 | position: relative; 87 | width: 50px; 88 | } 89 | 90 | .theme-switch input { 91 | display: none; 92 | } 93 | 94 | .slider { 95 | background-color: #ccc; 96 | bottom: 0; 97 | cursor: pointer; 98 | left: 0; 99 | position: absolute; 100 | right: 0; 101 | top: 0; 102 | transition: .4s; 103 | } 104 | 105 | .slider:before { 106 | background-color: #fff; 107 | bottom: 4px; 108 | content: ""; 109 | height: 13px; 110 | left: 4px; 111 | position: absolute; 112 | transition: .4s; 113 | width: 13px; 114 | } 115 | 116 | input:checked + .slider { 117 | background-color: #66bb6a; 118 | } 119 | 120 | input:checked + .slider:before { 121 | transform: translateX(26px); 122 | } 123 | 124 | .slider.round { 125 | border-radius: 17px; 126 | } 127 | 128 | .slider.round:before { 129 | border-radius: 50%; 130 | } 131 | 132 | html { 133 | font-size: 100%; 134 | -webkit-text-size-adjust: 100%; 135 | -ms-text-size-adjust: 100%; } 136 | 137 | body { 138 | font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; 139 | font-weight: 400; 140 | font-size: 1.125em; 141 | line-height: 1.5; 142 | color: var(--text); 143 | background-color: var(--primary-background); } 144 | 145 | /* Skeleton grid */ 146 | .container { 147 | position: relative; 148 | width: 100%; 149 | max-width: 1050px; 150 | margin: 0 auto; 151 | padding: 0; 152 | box-sizing: border-box; } 153 | 154 | .column, 155 | .columns { 156 | width: 100%; 157 | float: left; 158 | box-sizing: border-box; 159 | margin-left: 1%; 160 | } 161 | 162 | .column:first-child, 163 | .columns:first-child { 164 | margin-left: 0; } 165 | 166 | .three.columns { 167 | width: 22%; 168 | } 169 | 170 | .nine.columns { 171 | width: 77.0%; } 172 | 173 | .twelve.columns { 174 | width: 100%; 175 | margin-left: 0; } 176 | 177 | @media screen and (max-width: 860px) { 178 | .three.columns { 179 | display: none; 180 | } 181 | .nine.columns { 182 | width: 98.0%; 183 | } 184 | body { 185 | font-size: 1em; 186 | line-height: 1.35; 187 | } 188 | } 189 | 190 | cite { 191 | font-style: italic !important; } 192 | 193 | 194 | /* Nim search input */ 195 | div#searchInputDiv { 196 | margin-bottom: 1em; 197 | } 198 | input#searchInput { 199 | width: 80%; 200 | } 201 | 202 | /* 203 | * Some custom formatting for input forms. 204 | * This also fixes input form colors on Firefox with a dark system theme on Linux. 205 | */ 206 | input { 207 | -moz-appearance: none; 208 | background-color: var(--secondary-background); 209 | color: var(--text); 210 | border: 1px solid var(--border); 211 | font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; 212 | font-size: 0.9em; 213 | padding: 6px; 214 | } 215 | 216 | input:focus { 217 | border: 1px solid var(--input-focus); 218 | box-shadow: 0 0 3px var(--input-focus); 219 | } 220 | 221 | select { 222 | -moz-appearance: none; 223 | background-color: var(--secondary-background); 224 | color: var(--text); 225 | border: 1px solid var(--border); 226 | font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; 227 | font-size: 0.9em; 228 | padding: 6px; 229 | } 230 | 231 | select:focus { 232 | border: 1px solid var(--input-focus); 233 | box-shadow: 0 0 3px var(--input-focus); 234 | } 235 | 236 | /* Docgen styles */ 237 | 238 | :target { 239 | border: 2px solid #B5651D; 240 | border-style: dotted; 241 | } 242 | 243 | /* Links */ 244 | a { 245 | color: var(--anchor); 246 | text-decoration: none; 247 | } 248 | 249 | a span.Identifier { 250 | text-decoration: underline; 251 | text-decoration-color: #aab; 252 | } 253 | 254 | a.reference-toplevel { 255 | font-weight: bold; 256 | } 257 | 258 | a.toc-backref { 259 | text-decoration: none; 260 | color: var(--text); } 261 | 262 | a.link-seesrc { 263 | color: #607c9f; 264 | font-size: 0.9em; 265 | font-style: italic; } 266 | 267 | a:hover, 268 | a:focus { 269 | color: var(--anchor-focus); 270 | text-decoration: underline; } 271 | 272 | a:hover span.Identifier { 273 | color: var(--anchor); 274 | } 275 | 276 | 277 | sub, 278 | sup { 279 | position: relative; 280 | font-size: 75%; 281 | line-height: 0; 282 | vertical-align: baseline; } 283 | 284 | sup { 285 | top: -0.5em; } 286 | 287 | sub { 288 | bottom: -0.25em; } 289 | 290 | img { 291 | width: auto; 292 | height: auto; 293 | max-width: 100%; 294 | vertical-align: middle; 295 | border: 0; 296 | -ms-interpolation-mode: bicubic; } 297 | 298 | @media print { 299 | * { 300 | color: black !important; 301 | text-shadow: none !important; 302 | background: transparent !important; 303 | box-shadow: none !important; } 304 | 305 | a, 306 | a:visited { 307 | text-decoration: underline; } 308 | 309 | a[href]:after { 310 | content: " (" attr(href) ")"; } 311 | 312 | abbr[title]:after { 313 | content: " (" attr(title) ")"; } 314 | 315 | .ir a:after, 316 | a[href^="javascript:"]:after, 317 | a[href^="#"]:after { 318 | content: ""; } 319 | 320 | pre, 321 | blockquote { 322 | border: 1px solid #999; 323 | page-break-inside: avoid; } 324 | 325 | thead { 326 | display: table-header-group; } 327 | 328 | tr, 329 | img { 330 | page-break-inside: avoid; } 331 | 332 | img { 333 | max-width: 100% !important; } 334 | 335 | @page { 336 | margin: 0.5cm; } 337 | 338 | h1 { 339 | page-break-before: always; } 340 | 341 | h1.title { 342 | page-break-before: avoid; } 343 | 344 | p, 345 | h2, 346 | h3 { 347 | orphans: 3; 348 | widows: 3; } 349 | 350 | h2, 351 | h3 { 352 | page-break-after: avoid; } 353 | } 354 | 355 | 356 | p { 357 | margin-top: 0.5em; 358 | margin-bottom: 0.5em; 359 | } 360 | 361 | small { 362 | font-size: 85%; } 363 | 364 | strong { 365 | font-weight: 600; 366 | font-size: 0.95em; 367 | color: var(--strong); 368 | } 369 | 370 | em { 371 | font-style: italic; } 372 | 373 | h1 { 374 | font-size: 1.8em; 375 | font-weight: 400; 376 | padding-bottom: .25em; 377 | border-bottom: 6px solid var(--third-background); 378 | margin-top: 2.5em; 379 | margin-bottom: 1em; 380 | line-height: 1.2em; } 381 | 382 | h1.title { 383 | padding-bottom: 1em; 384 | border-bottom: 0px; 385 | font-size: 2.5em; 386 | text-align: center; 387 | font-weight: 900; 388 | margin-top: 0.75em; 389 | margin-bottom: 0em; 390 | } 391 | 392 | h2 { 393 | font-size: 1.3em; 394 | margin-top: 2em; } 395 | 396 | h2.subtitle { 397 | margin-top: 0em; 398 | text-align: center; } 399 | 400 | h3 { 401 | font-size: 1.125em; 402 | font-style: italic; 403 | margin-top: 1.5em; } 404 | 405 | h4 { 406 | font-size: 1.125em; 407 | margin-top: 1em; } 408 | 409 | h5 { 410 | font-size: 1.125em; 411 | margin-top: 0.75em; } 412 | 413 | h6 { 414 | font-size: 1.1em; } 415 | 416 | 417 | ul, 418 | ol { 419 | padding: 0; 420 | margin-top: 0.5em; 421 | margin-left: 0.75em; } 422 | 423 | ul ul, 424 | ul ol, 425 | ol ol, 426 | ol ul { 427 | margin-bottom: 0; 428 | margin-left: 1.25em; } 429 | 430 | ul.simple > li { 431 | list-style-type: circle; 432 | } 433 | 434 | ul.simple-boot li { 435 | list-style-type: none; 436 | margin-left: 0em; 437 | margin-bottom: 0.5em; 438 | } 439 | 440 | ol.simple > li, ul.simple > li { 441 | margin-bottom: 0.2em; 442 | margin-left: 0.4em } 443 | 444 | ul.simple.simple-toc > li { 445 | margin-top: 1em; 446 | } 447 | 448 | ul.simple-toc { 449 | list-style: none; 450 | font-size: 0.9em; 451 | margin-left: -0.3em; 452 | margin-top: 1em; } 453 | 454 | ul.simple-toc > li { 455 | list-style-type: none; 456 | } 457 | 458 | ul.simple-toc-section { 459 | list-style-type: circle; 460 | margin-left: 0.8em; 461 | color: #6c9aae; } 462 | 463 | ul.nested-toc-section { 464 | list-style-type: circle; 465 | margin-left: -0.75em; 466 | color: var(--text); 467 | } 468 | 469 | ul.nested-toc-section > li { 470 | margin-left: 1.25em; 471 | } 472 | 473 | 474 | ol.arabic { 475 | list-style: decimal; } 476 | 477 | ol.loweralpha { 478 | list-style: lower-alpha; } 479 | 480 | ol.upperalpha { 481 | list-style: upper-alpha; } 482 | 483 | ol.lowerroman { 484 | list-style: lower-roman; } 485 | 486 | ol.upperroman { 487 | list-style: upper-roman; } 488 | 489 | ul.auto-toc { 490 | list-style-type: none; } 491 | 492 | 493 | dl { 494 | margin-bottom: 1.5em; } 495 | 496 | dt { 497 | margin-bottom: -0.5em; 498 | margin-left: 0.0em; } 499 | 500 | dd { 501 | margin-left: 2.0em; 502 | margin-bottom: 3.0em; 503 | margin-top: 0.5em; } 504 | 505 | 506 | hr { 507 | margin: 2em 0; 508 | border: 0; 509 | border-top: 1px solid #aaa; } 510 | 511 | hr.footnote { 512 | width: 25%; 513 | border-top: 0.15em solid #999; 514 | margin-bottom: 0.15em; 515 | margin-top: 0.15em; 516 | } 517 | div.footnote-group { 518 | margin-left: 1em; } 519 | div.footnote-label { 520 | display: inline-block; 521 | min-width: 1.7em; 522 | } 523 | 524 | div.option-list { 525 | border: 0.1em solid var(--border); 526 | } 527 | div.option-list-item { 528 | padding-left: 12em; 529 | padding-right: 0; 530 | padding-bottom: 0.3em; 531 | padding-top: 0.3em; 532 | } 533 | div.odd { 534 | background-color: var(--secondary-background); 535 | } 536 | div.option-list-label { 537 | margin-left: -11.5em; 538 | margin-right: 0em; 539 | min-width: 11.5em; 540 | display: inline-block; 541 | vertical-align: top; 542 | } 543 | div.option-list-description { 544 | width: calc(100% - 1em); 545 | padding-left: 1em; 546 | padding-right: 0; 547 | display: inline-block; 548 | } 549 | 550 | blockquote { 551 | font-size: 0.9em; 552 | font-style: italic; 553 | padding-left: 0.5em; 554 | margin-left: 0; 555 | border-left: 5px solid #bbc; 556 | } 557 | 558 | .pre, span.tok { 559 | font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; 560 | font-weight: 500; 561 | font-size: 0.85em; 562 | color: var(--text); 563 | background-color: var(--third-background); 564 | padding-left: 3px; 565 | padding-right: 3px; 566 | border-radius: 4px; 567 | } 568 | 569 | span.tok { 570 | border: 1px solid #808080; 571 | padding-bottom: 0.1em; 572 | margin-right: 0.2em; 573 | } 574 | 575 | pre { 576 | font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; 577 | color: var(--text); 578 | font-weight: 500; 579 | display: inline-block; 580 | box-sizing: border-box; 581 | min-width: 100%; 582 | padding: 0.5em; 583 | margin-top: 0.5em; 584 | margin-bottom: 0.5em; 585 | font-size: 0.85em; 586 | white-space: pre !important; 587 | overflow-y: hidden; 588 | overflow-x: visible; 589 | background-color: var(--secondary-background); 590 | border: 1px solid var(--border); 591 | -webkit-border-radius: 6px; 592 | -moz-border-radius: 6px; 593 | border-radius: 6px; } 594 | 595 | .pre-scrollable { 596 | max-height: 340px; 597 | overflow-y: scroll; } 598 | 599 | 600 | /* Nim line-numbered tables */ 601 | .line-nums-table { 602 | width: 100%; 603 | table-layout: fixed; } 604 | 605 | table.line-nums-table { 606 | border-radius: 4px; 607 | border: 1px solid #cccccc; 608 | background-color: ghostwhite; 609 | border-collapse: separate; 610 | margin-top: 15px; 611 | margin-bottom: 25px; } 612 | 613 | .line-nums-table tbody { 614 | border: none; } 615 | 616 | .line-nums-table td pre { 617 | border: none; 618 | background-color: transparent; } 619 | 620 | .line-nums-table td.blob-line-nums { 621 | width: 28px; } 622 | 623 | .line-nums-table td.blob-line-nums pre { 624 | color: #b0b0b0; 625 | -webkit-filter: opacity(75%); 626 | filter: opacity(75%); 627 | text-align: right; 628 | border-color: transparent; 629 | background-color: transparent; 630 | padding-left: 0px; 631 | margin-left: 0px; 632 | padding-right: 0px; 633 | margin-right: 0px; } 634 | 635 | 636 | table { 637 | max-width: 100%; 638 | background-color: transparent; 639 | margin-top: 0.5em; 640 | margin-bottom: 1.5em; 641 | border-collapse: collapse; 642 | border-color: var(--third-background); 643 | border-spacing: 0; 644 | font-size: 0.9em; 645 | } 646 | 647 | table th, table td { 648 | padding: 0px 0.5em 0px; 649 | border-color: var(--third-background); 650 | } 651 | 652 | table th { 653 | background-color: var(--third-background); 654 | border-color: var(--third-background); 655 | font-weight: bold; } 656 | 657 | table th.docinfo-name { 658 | background-color: transparent; 659 | text-align: right; 660 | } 661 | 662 | table tr:hover { 663 | background-color: var(--third-background); } 664 | 665 | 666 | /* rst2html default used to remove borders from tables and images */ 667 | .borderless, table.borderless td, table.borderless th { 668 | border: 0; } 669 | 670 | table.borderless td, table.borderless th { 671 | /* Override padding for "table.docutils td" with "! important". 672 | The right padding separates the table cells. */ 673 | padding: 0 0.5em 0 0 !important; } 674 | 675 | .admonition { 676 | padding: 0.3em; 677 | background-color: var(--secondary-background); 678 | border-left: 0.4em solid #7f7f84; 679 | margin-bottom: 0.5em; 680 | -webkit-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); 681 | -moz-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); 682 | box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); 683 | } 684 | .admonition-info { 685 | border-color: var(--info-background); 686 | } 687 | .admonition-info-text { 688 | color: var(--info-background); 689 | } 690 | .admonition-warning { 691 | border-color: var(--warning-background); 692 | } 693 | .admonition-warning-text { 694 | color: var(--warning-background); 695 | } 696 | .admonition-error { 697 | border-color: var(--error-background); 698 | } 699 | .admonition-error-text { 700 | color: var(--error-background); 701 | } 702 | 703 | .first { 704 | /* Override more specific margin styles with "! important". */ 705 | margin-top: 0 !important; } 706 | 707 | .last, .with-subtitle { 708 | margin-bottom: 0 !important; } 709 | 710 | .hidden { 711 | display: none; } 712 | 713 | blockquote.epigraph { 714 | margin: 2em 5em; } 715 | 716 | dl.docutils dd { 717 | margin-bottom: 0.5em; } 718 | 719 | object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { 720 | overflow: hidden; } 721 | 722 | 723 | div.figure { 724 | margin-left: 2em; 725 | margin-right: 2em; } 726 | 727 | div.footer, div.header { 728 | clear: both; 729 | text-align: center; 730 | color: #666; 731 | font-size: smaller; } 732 | 733 | div.footer { 734 | padding-top: 5em; 735 | } 736 | 737 | div.line-block { 738 | display: block; 739 | margin-top: 1em; 740 | margin-bottom: 1em; } 741 | 742 | div.line-block div.line-block { 743 | margin-top: 0; 744 | margin-bottom: 0; 745 | margin-left: 1.5em; } 746 | 747 | div.topic { 748 | margin: 2em; } 749 | 750 | div.search_results { 751 | background-color: var(--third-background); 752 | margin: 3em; 753 | padding: 1em; 754 | border: 1px solid #4d4d4d; 755 | } 756 | 757 | div#global-links ul { 758 | margin-left: 0; 759 | list-style-type: none; 760 | } 761 | 762 | div#global-links > simple-boot { 763 | margin-left: 3em; 764 | } 765 | 766 | hr.docutils { 767 | width: 75%; } 768 | 769 | img.align-left, .figure.align-left, object.align-left { 770 | clear: left; 771 | float: left; 772 | margin-right: 1em; } 773 | 774 | img.align-right, .figure.align-right, object.align-right { 775 | clear: right; 776 | float: right; 777 | margin-left: 1em; } 778 | 779 | img.align-center, .figure.align-center, object.align-center { 780 | display: block; 781 | margin-left: auto; 782 | margin-right: auto; } 783 | 784 | .align-left { 785 | text-align: left; } 786 | 787 | .align-center { 788 | clear: both; 789 | text-align: center; } 790 | 791 | .align-right { 792 | text-align: right; } 793 | 794 | /* reset inner alignment in figures */ 795 | div.align-right { 796 | text-align: inherit; } 797 | 798 | p.attribution { 799 | text-align: right; 800 | margin-left: 50%; } 801 | 802 | p.caption { 803 | font-style: italic; } 804 | 805 | p.credits { 806 | font-style: italic; 807 | font-size: smaller; } 808 | 809 | p.label { 810 | white-space: nowrap; } 811 | 812 | p.rubric { 813 | font-weight: bold; 814 | font-size: larger; 815 | color: maroon; 816 | text-align: center; } 817 | 818 | p.topic-title { 819 | font-weight: bold; } 820 | 821 | pre.address { 822 | margin-bottom: 0; 823 | margin-top: 0; 824 | font: inherit; } 825 | 826 | pre.literal-block, pre.doctest-block, pre.math, pre.code { 827 | margin-left: 2em; 828 | margin-right: 2em; } 829 | 830 | pre.code .ln { 831 | color: grey; } 832 | 833 | /* line numbers */ 834 | pre.code, code { 835 | background-color: #eeeeee; } 836 | 837 | pre.code .comment, code .comment { 838 | color: #5c6576; } 839 | 840 | pre.code .keyword, code .keyword { 841 | color: #3B0D06; 842 | font-weight: bold; } 843 | 844 | pre.code .literal.string, code .literal.string { 845 | color: #0c5404; } 846 | 847 | pre.code .name.builtin, code .name.builtin { 848 | color: #352b84; } 849 | 850 | pre.code .deleted, code .deleted { 851 | background-color: #DEB0A1; } 852 | 853 | pre.code .inserted, code .inserted { 854 | background-color: #A3D289; } 855 | 856 | span.classifier { 857 | font-style: oblique; } 858 | 859 | span.classifier-delimiter { 860 | font-weight: bold; } 861 | 862 | span.problematic { 863 | color: #b30000; } 864 | 865 | span.section-subtitle { 866 | /* font-size relative to parent (h1..h6 element) */ 867 | font-size: 80%; } 868 | 869 | span.DecNumber { 870 | color: var(--number); } 871 | 872 | span.BinNumber { 873 | color: var(--number); } 874 | 875 | span.HexNumber { 876 | color: var(--number); } 877 | 878 | span.OctNumber { 879 | color: var(--number); } 880 | 881 | span.FloatNumber { 882 | color: var(--number); } 883 | 884 | span.Identifier { 885 | color: var(--identifier); } 886 | 887 | span.Keyword { 888 | font-weight: 600; 889 | color: var(--keyword); } 890 | 891 | span.StringLit { 892 | color: var(--literal); } 893 | 894 | span.LongStringLit { 895 | color: var(--literal); } 896 | 897 | span.CharLit { 898 | color: var(--literal); } 899 | 900 | span.EscapeSequence { 901 | color: var(--escapeSequence); } 902 | 903 | span.Operator { 904 | color: var(--operator); } 905 | 906 | span.Punctuation { 907 | color: var(--punctuation); } 908 | 909 | span.Comment, span.LongComment { 910 | font-style: italic; 911 | font-weight: 400; 912 | color: var(--comment); } 913 | 914 | span.RegularExpression { 915 | color: darkviolet; } 916 | 917 | span.TagStart { 918 | color: darkviolet; } 919 | 920 | span.TagEnd { 921 | color: darkviolet; } 922 | 923 | span.Key { 924 | color: #252dbe; } 925 | 926 | span.Value { 927 | color: #252dbe; } 928 | 929 | span.RawData { 930 | color: var(--raw-data); } 931 | 932 | span.Assembler { 933 | color: #252dbe; } 934 | 935 | span.Preprocessor { 936 | color: #252dbe; } 937 | 938 | span.Directive { 939 | color: #252dbe; } 940 | 941 | span.option { 942 | font-weight: bold; 943 | font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; 944 | color: var(--option); 945 | } 946 | 947 | span.Prompt { 948 | font-weight: bold; 949 | color: red; } 950 | 951 | span.ProgramOutput { 952 | font-weight: bold; 953 | color: #808080; } 954 | 955 | span.program { 956 | font-weight: bold; 957 | color: var(--program); 958 | text-decoration: underline; 959 | text-decoration-color: var(--hint); 960 | text-decoration-thickness: 0.05em; 961 | text-underline-offset: 0.15em; 962 | } 963 | 964 | span.Command, span.Rule, span.Hyperlink, span.Label, span.Reference, 965 | span.Other { 966 | color: var(--other); } 967 | 968 | /* Pop type, const, proc, and iterator defs in nim def blocks */ 969 | dt pre > span.Identifier, dt pre > span.Operator { 970 | color: var(--identifier); 971 | font-weight: 700; } 972 | 973 | dt pre > span.Keyword ~ span.Identifier, dt pre > span.Identifier ~ span.Identifier, 974 | dt pre > span.Operator ~ span.Identifier, dt pre > span.Other ~ span.Identifier { 975 | color: var(--identifier); 976 | font-weight: inherit; } 977 | 978 | /* Nim sprite for the footer (taken from main page favicon) */ 979 | .nim-sprite { 980 | display: inline-block; 981 | width: 51px; 982 | height: 14px; 983 | background-position: 0 0; 984 | background-size: 51px 14px; 985 | -webkit-filter: opacity(50%); 986 | filter: opacity(50%); 987 | background-repeat: no-repeat; 988 | background-image: var(--nim-sprite-base64); 989 | margin-bottom: 5px; } 990 | 991 | span.pragmadots { 992 | /* Position: relative frees us up to make the dots 993 | look really nice without fucking up the layout and 994 | causing bulging in the parent container */ 995 | position: relative; 996 | /* 1px down looks slightly nicer */ 997 | top: 1px; 998 | padding: 2px; 999 | background-color: var(--third-background); 1000 | border-radius: 4px; 1001 | margin: 0 2px; 1002 | cursor: pointer; 1003 | font-size: 0.8em; 1004 | } 1005 | 1006 | span.pragmadots:hover { 1007 | background-color: var(--hint); 1008 | } 1009 | span.pragmawrap { 1010 | display: none; 1011 | } 1012 | 1013 | span.attachedType { 1014 | display: none; 1015 | visibility: hidden; 1016 | } 1017 | -------------------------------------------------------------------------------- /docs/nimexample.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 14 | 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/quill.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Quill 21 | 22 | 23 | 24 | 25 | 67 | 68 | 69 | 70 |
71 |
72 |

Quill

73 |
74 |
75 |
76 | 80 |     Dark Mode 81 |
82 | 89 |
90 | Search: 92 |
93 |
94 | Group by: 95 | 99 |
100 | 207 | 208 |
209 | 210 |
211 |
212 | 213 |

Quill is a nim js library for making text editors, it is made completely in nim and is designed to be easy to use.

214 | 215 |

Example

The example can be found at https://thatrandomperson5.github.io/Quill/example

216 | 217 |

Docs

Docs can be found at https://thatrandomperson5.github.io/Quill/quill

218 | 219 |

Installing

nimble install https://github.com/thatrandomperson5/Quill
220 |

Guide

We first need to add some css to our main html file:

221 |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/thatrandomperson5/quill@master/js/quill.css">

After that we can start coding. A quill has a structure to get it to work:

222 |
  • Create the quill
  • 223 |
  • Set any default text
  • 224 |
  • Add onDraw
    • Main html generation
    • 225 |
    • Sector entering or forced redraw
    • 226 |
    227 |
  • 228 |
  • Init extentions and then quill
  • 229 |
230 |

Here is a basic example:

231 |
# From tests
232 | import quill, dom
233 | 
234 | var myquill = newQuill(document.getElementById("quill"), "70vh") # create the quill with height of 70
235 | myquill.text = "Hello world" # Set the default text
236 | myquill.onDraw = proc (q: var Quill, str: cstring, isDel: bool) =
237 |   # Main html generation
238 |   let txt = document.createElement("span")
239 |   txt.appendChild document.createTextNode(str)
240 |   q.draw(txt)
241 | 
242 | myquill.init() # Start the quill

Note: There is no reason to redraw if you don't use insert(), will be explained in more detail later on.

243 |

This is the most basic quill! But it does not do anything, so is there anything diffrent then a normal textarea? Well, the draw proc takes html, so you can color and style as much as you want! Try making the span a random color! You can also use the features described below to help you.

244 | 245 |

Insert and sectors

You can have a insert() call in your ondraw proc. This adds text, for example an indent, to your quill. This inserts at the current cursor position, if you want to add text a diffrent way, use the text= proc.

246 |

There are also sectors, which reduce the text passed to onDraw. Say for example your quill only needed to process words and you have this sentance: hello world good souls. Normally your draw proc would get passed the whole thing each time, but when processing "world", your code does not need anything from "hello". So, when you detect a space, you would enter a sector using enter(). This would mean if the word "hello" was changed to "hi" you would get passed "hi" instead of "hi world good souls". This feature is mainly for efficiency, and a full example can be found in the tests folder.

247 |

Note: Why is my inserted text not showing? You have to have a myquill.forceRedraw() at the end to make it show.

248 |

Warning: forceRedraw() and enter() must be the last call in your draw proc, also note that enter() replaces forceRedraw().

249 | 250 |

Gutter

To add a gutter (numbers on the side), just add the code below:

251 |
# After import quill
252 | import quill/ext/gutters
253 | 
254 | # Right before myquill.init()
255 | myquill.initGutter()

256 |
257 |

Imports

258 |
259 | quill/ext/gutters, quill/utils 260 |
261 |
262 |

Types

263 |
264 |
265 |
Quill = ref object
266 |   internalElm: Element
267 |   onDraw*: QuillOnDraw
268 |   sectors: seq[int]
269 |   current: int
270 |   plen: int
271 | 
272 |
273 | 274 | 275 | 276 |
277 |
278 |
279 |
QuillOnDraw = (var Quill, cstring, bool) -> void
280 |
281 | 282 | 283 | 284 |
285 |
286 |
287 |
QuillOnSegment = (Quill, cstring) -> seq[cstring]
288 |
289 | 290 | 291 | 292 |
293 |
294 | 295 |
296 |
297 |

Vars

298 |
299 |
300 |
documentElement: Element
301 |
302 | 303 | 304 | 305 |
306 |
307 | 308 |
309 |
310 |

Procs

311 |
312 |
313 |
proc draw(q: var Quill; n: Node) {....raises: [], tags: [].}
314 |
315 | 316 | Draw dom node n to quill q 317 | 318 |
319 |
320 |
321 |
proc element(q: Quill): Element {....raises: [], tags: [].}
322 |
323 | 324 | For extentions, like quill/ext/gutters 325 | 326 |
327 |
328 |
329 |
proc enter(q: var Quill; pos: int) {....raises: [Exception], tags: [RootEffect].}
330 |
331 | 332 |

Enter a new sector at pos relative to the current sector

333 |

Note: Must be the last call in a onDraw

334 | 335 | 336 |
337 |
338 |
339 |
proc eventElement(q: Quill): Element {....raises: [], tags: [].}
340 |
341 | 342 | For extentions, like quill/ext/gutters 343 | 344 |
345 |
346 |
347 |
proc forceRedraw(q: var Quill) {....raises: [Exception], tags: [RootEffect].}
348 |
349 | 350 | Force the redrawing of quill q 351 | 352 |
353 |
354 |
355 |
proc init(q: var Quill) {....raises: [Exception], tags: [RootEffect].}
356 |
357 | 358 | "Turns on" the quill, nothing will work or show properly until this is called 359 | 360 |
361 |
362 |
363 |
proc insert(q: var Quill; text: cstring) {....raises: [], tags: [].}
364 |
365 | 366 | Insert text at users current position 367 | 368 |
369 |
370 |
371 |
proc newQuill(e: Element; height: cstring = "30vh"): Quill {....raises: [],
372 |     tags: [].}
373 |
374 | 375 |

Create a new quill of height height and make all needed elements

376 |

Note: Might not look right without the proper css

377 | 378 | 379 |
380 |
381 |
382 |
proc text(q: Quill): cstring {....raises: [], tags: [].}
383 |
384 | 385 | Get the text/value of a quill, directly what the user inputed 386 | 387 |
388 |
389 |
390 |
proc text=(q: var Quill; replacment: cstring) {....raises: [], tags: [].}
391 |
392 | 393 | Set the text of a quill (as an user input, so you cannot set html tags) 394 | 395 |
396 |
397 |
398 |
proc visualElement(q: Quill): Element {....raises: [], tags: [].}
399 |
400 | 401 | For extentions, like quill/ext/gutters 402 | 403 |
404 |
405 | 406 |
407 |
408 |

Exports

409 |
410 | toJsStr, createRawText, toCstr 411 |
412 | 413 |
414 |
415 | 416 |
417 | 422 |
423 |
424 |
425 | 426 | 427 | 428 | -------------------------------------------------------------------------------- /docs/quill.idx: -------------------------------------------------------------------------------- 1 | Quill quill.html 2 | QuillOnDraw quill.html#QuillOnDraw quill: QuillOnDraw 3 | QuillOnSegment quill.html#QuillOnSegment quill: QuillOnSegment 4 | Quill quill.html#Quill quill: Quill 5 | documentElement quill.html#documentElement quill: documentElement 6 | newQuill quill.html#newQuill,Element,cstring quill: newQuill(e: Element; height: cstring = "30vh"): Quill 7 | element quill.html#element,Quill quill: element(q: Quill): Element 8 | eventElement quill.html#eventElement,Quill quill: eventElement(q: Quill): Element 9 | visualElement quill.html#visualElement,Quill quill: visualElement(q: Quill): Element 10 | text quill.html#text,Quill quill: text(q: Quill): cstring 11 | text= quill.html#text=,Quill,cstring quill: text=(q: var Quill; replacment: cstring) 12 | init quill.html#init,Quill quill: init(q: var Quill) 13 | draw quill.html#draw,Quill,Node quill: draw(q: var Quill; n: Node) 14 | enter quill.html#enter,Quill,int quill: enter(q: var Quill; pos: int) 15 | insert quill.html#insert,Quill,cstring quill: insert(q: var Quill; text: cstring) 16 | forceRedraw quill.html#forceRedraw,Quill quill: forceRedraw(q: var Quill) 17 | Example quill.html#example Example 18 | Docs quill.html#docs Docs 19 | Installing quill.html#installing Installing 20 | Guide quill.html#guide Guide 21 | Insert and sectors quill.html#insert-and-sectors Insert and sectors 22 | Gutter quill.html#gutter Gutter 23 | -------------------------------------------------------------------------------- /docs/quill/ext/gutters.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | src/quill/ext/gutters 21 | 22 | 23 | 24 | 25 | 67 | 68 | 69 | 70 |
71 |
72 |

src/quill/ext/gutters

73 |
74 |
75 |
76 | 80 |     Dark Mode 81 |
82 | 89 |
90 | Search: 92 |
93 |
94 | Group by: 95 | 99 |
100 | 114 | 115 |
116 | 117 |
118 |
119 | 120 |

121 |
122 |

Procs

123 |
124 |
125 |
proc initGutter(q: var Quill) {....raises: [], tags: [].}
126 |
127 | 128 | Add a responsive gutter to your quill 129 | 130 |
131 |
132 | 133 |
134 | 135 |
136 |
137 | 138 |
139 | 144 |
145 |
146 |
147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /docs/quill/ext/gutters.idx: -------------------------------------------------------------------------------- 1 | initGutter quill/ext/gutters.html#initGutter,Quill gutters: initGutter(q: var Quill) 2 | -------------------------------------------------------------------------------- /docs/quill/utils.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | src/quill/utils 21 | 22 | 23 | 24 | 25 | 67 | 68 | 69 | 70 |
71 |
72 |

src/quill/utils

73 |
74 |
75 |
76 | 80 |     Dark Mode 81 |
82 | 89 |
90 | Search: 92 |
93 |
94 | Group by: 95 | 99 |
100 | 175 | 176 |
177 | 178 |
179 |
180 | 181 |

182 |
183 |

Procs

184 |
185 |
186 |
proc `[]=`(parent: Element; i: int; child: Node) {....raises: [], tags: [].}
187 |
188 | 189 | Set child i of parent to child 190 | 191 |
192 |
193 |
194 |
func `[]`[T, U](s: cstring; i: HSlice[T, U]): cstring
195 |
196 | 197 | Slice a cstring 198 | 199 |
200 |
201 |
202 |
proc clear(e: Element) {.importcpp: "#.innerHTML = \'\'", ...raises: [], tags: [].}
203 |
204 | 205 | Clear an dom element 206 | 207 |
208 |
209 |
210 |
proc createRawText(str: string): Node {....raises: [], tags: [].}
211 |
212 | 213 | 214 | 215 |
216 |
217 |
218 |
proc innerHTML(e: Element): cstring {.importcpp: "#.innerHTML", ...raises: [],
219 |                                       tags: [].}
220 |
221 | 222 | Get the inner html of a dom element 223 | 224 |
225 |
226 |
227 |
proc innerHTML=(e: Element; s: cstring) {.importcpp: "#.innerHTML = #",
228 |     ...raises: [], tags: [].}
229 |
230 | 231 | Set the inner html of a dom element 232 | 233 |
234 |
235 |
236 |
proc lastChild(e: Element): Node {.importcpp: "#.lastChild", ...raises: [],
237 |                                    tags: [].}
238 |
239 | 240 | Get the last child of a dom element 241 | 242 |
243 |
244 |
245 |
proc replace(tg: cstring; a: cstring; b: cstring): cstring {.
246 |     importcpp: "#.replace(#, #)", ...raises: [], tags: [].}
247 |
248 | 249 | Replace a for b in tg 250 | 251 |
252 |
253 |
254 |
proc selectionEnd(txtarea: Element): int {.importcpp: "#.selectionEnd",
255 |     ...raises: [], tags: [].}
256 |
257 | 258 | Direct wrapper 259 | 260 |
261 |
262 |
263 |
proc selectionStart(txtarea: Element): int {.importcpp: "#.selectionStart",
264 |     ...raises: [], tags: [].}
265 |
266 | 267 | Direct wrapper 268 | 269 |
270 |
271 |
272 |
proc toJsStr(s: string): cstring {....raises: [], tags: [].}
273 |
274 | 275 | Turn a string into a cstring with no escaping 276 | 277 |
278 |
279 | 280 |
281 |
282 |

Templates

283 |
284 |
285 |
template toCstr(c: char): untyped
286 |
287 | 288 | Turn a char into a cstring 289 | 290 |
291 |
292 | 293 |
294 | 295 |
296 |
297 | 298 |
299 | 304 |
305 |
306 |
307 | 308 | 309 | 310 | -------------------------------------------------------------------------------- /docs/quill/utils.idx: -------------------------------------------------------------------------------- 1 | `[]` quill/utils.html#[],cstring,HSlice[T,U] utils: `[]`[T, U](s: cstring; i: HSlice[T, U]): cstring 2 | clear quill/utils.html#clear,Element utils: clear(e: Element) 3 | lastChild quill/utils.html#lastChild,Element utils: lastChild(e: Element): Node 4 | toCstr quill/utils.html#toCstr.t,char utils: toCstr(c: char): untyped 5 | toJsStr quill/utils.html#toJsStr,string utils: toJsStr(s: string): cstring 6 | createRawText quill/utils.html#createRawText,string utils: createRawText(str: string): Node 7 | replace quill/utils.html#replace,cstring,cstring,cstring utils: replace(tg: cstring; a: cstring; b: cstring): cstring 8 | innerHTML quill/utils.html#innerHTML,Element utils: innerHTML(e: Element): cstring 9 | innerHTML= quill/utils.html#innerHTML=,Element,cstring utils: innerHTML=(e: Element; s: cstring) 10 | selectionStart quill/utils.html#selectionStart,Element utils: selectionStart(txtarea: Element): int 11 | selectionEnd quill/utils.html#selectionEnd,Element utils: selectionEnd(txtarea: Element): int 12 | `[]=` quill/utils.html#[]=,Element,int,Node utils: `[]=`(parent: Element; i: int; child: Node) 13 | -------------------------------------------------------------------------------- /docs/theindex.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Index 21 | 22 | 23 | 24 | 25 | 67 | 68 | 69 | 70 |
71 |
72 |

Index

73 | Documents: Quill.

Modules: quill/ext/gutters, quill/utils.

API symbols

74 |
`[]=`:
78 |
`[]`:
82 |
clear:
86 |
createRawText:
90 |
initGutter:
94 |
innerHTML:
98 |
innerHTML=:
102 |
lastChild:
106 |
replace:
110 |
selectionEnd:
114 |
selectionStart:
118 |
toCstr:
122 |
toJsStr:
126 |
127 |
128 | 133 |
134 |
135 |
136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /js/binds.nim: -------------------------------------------------------------------------------- 1 | import quill, dom, macros 2 | 3 | 4 | proc makeCall(call: NimNode, prms: NimNode): NimNode = 5 | prms.expectKind(nnkFormalParams) 6 | result = newNimNode(nnkCall) 7 | result.add call 8 | for def in prms[1..^1]: 9 | result.add newIdentNode(def[0].strVal) 10 | 11 | 12 | macro autoExport(name: typed): untyped = 13 | let njs = genSym(nskProc, name.strVal) 14 | let nlit = newLit(name.strVal) 15 | let iname = newIdentNode(name.strVal) 16 | let args = name.getImpl[3] 17 | let theCall = makeCall(iname, args) 18 | result = quote do: 19 | proc `njs`*(tmp: auto): auto {.exportc: `nlit`} = `thecall` 20 | result[3] = args 21 | 22 | proc settext(q: var Quill, value: cstring) {.exportc.} = q.text = value 23 | 24 | autoExport newQuill 25 | autoExport element 26 | autoExport eventElement 27 | autoExport visualElement 28 | autoExport init 29 | autoExport text 30 | autoExport draw 31 | autoExport enter 32 | -------------------------------------------------------------------------------- /js/classbinder.nim: -------------------------------------------------------------------------------- 1 | import std/[strutils, strformat, sequtils] 2 | 3 | type ArgFunc* = ref object 4 | name*: string 5 | params*: int 6 | kind: int # 0: Normal, 1: Getter, 2: Setter 7 | 8 | proc newArgFunc*(name: string, params=0, kind=0): ArgFunc = ArgFunc(name: name, params: params, kind: kind) 9 | 10 | template af*(name: string, params=0, kind=0): untyped = 11 | newArgFunc(name, params, kind) 12 | 13 | const lets = toSeq({'a'..'z'} + {'A'..'Z'}) 14 | 15 | proc makeParams(amnt: int): string = 16 | if amnt == 0: 17 | return "" 18 | for i in 1..amnt: 19 | result.add &"{lets[i-1]}, " 20 | 21 | proc makeFuncCopy(af: ArgFunc): string = 22 | let cparams = makeParams(af.params) 23 | return &"{af.name}({cparams})" & '{' & &"return {af.name}(this.core, {cparams});" & "}\n" 24 | 25 | proc makeGetter(af: ArgFunc): string = 26 | let cparams = makeParams(af.params) 27 | return &"get {af.name}({cparams})" & '{' & &"return {af.name}(this.core, {cparams});" & "}\n" 28 | 29 | proc makeSetter(af: ArgFunc): string = 30 | let cparams = makeParams(af.params) 31 | return &"set {af.name}({cparams})" & '{' & &"set{af.name}(this.core, {cparams});" & "}\n" 32 | 33 | proc makeFunc(af: ArgFunc): string = 34 | case af.kind: 35 | of 0: 36 | return makeFuncCopy(af) 37 | of 1: 38 | return makeGetter(af) 39 | of 2: 40 | return makeSetter(af) 41 | else: 42 | discard 43 | 44 | proc bindClass*(name: string, args: int, funcs: seq[ArgFunc]): string = 45 | result = "{\n " 46 | let cparams = makeParams(args) 47 | result.add ( 48 | &"constructor({cparams})" & "{" & &"this.core = new{name}({cparams});" & "}\n" 49 | ) 50 | for f in funcs: 51 | result.add " " 52 | result.add makeFunc(f) 53 | result &= "}" 54 | result = &"class {name} {result}" 55 | 56 | -------------------------------------------------------------------------------- /js/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "$projectDir/../src") -------------------------------------------------------------------------------- /js/makejs.sh: -------------------------------------------------------------------------------- 1 | nim js -d:release --opt:speed js/binds.nim 2 | nim e js/mybinds.nim >> js/binds.js 3 | closure-compiler --js js/binds.js --js_output_file js/quill.js 4 | rm js/binds.js -------------------------------------------------------------------------------- /js/mybinds.nim: -------------------------------------------------------------------------------- 1 | import classbinder 2 | 3 | 4 | let funcs = @[ 5 | af"init", 6 | af("draw", 1), 7 | af("enter", 1), 8 | af("element", 0, 1), 9 | af("eventElement", 0, 1), 10 | af("visualElement", 0, 1), 11 | af("text", 0, 1), 12 | af("text", 1, 2), 13 | ] 14 | 15 | echo "Quill".bindClass(1, funcs) -------------------------------------------------------------------------------- /js/quill.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --quill-height: 30vh; 3 | --quill-width: 100%; 4 | --quill-font-size: 15px; 5 | --quill-font-family: hack, monospace; 6 | --quill-border: 1px solid black; 7 | --quill-padding: 5px; 8 | --quill-margin: 0; 9 | --quill-scrollbar-thumb: rgb(170, 170, 170); 10 | --quill-background: white; 11 | --quill-secondary-background: #f7f7f7; 12 | } 13 | 14 | .quill-sizing { 15 | width: 100%; 16 | height: 100%; 17 | padding: var(--quill-padding); 18 | margin: var(--quill-margin); 19 | font-size: var(--quill-font-size); 20 | font-family: var(--quill-font-family); 21 | resize: none; 22 | white-space: pre; 23 | } 24 | .quill-invis { 25 | position: absolute; 26 | background: none; 27 | border: none; 28 | color: rgba(0, 0, 0, 0); 29 | caret-color: black; 30 | } 31 | .quill-text { 32 | /* padding: 2px 8px; */ 33 | /* transform: translateY( -16px ); */ 34 | position: absolute; 35 | background-color: var(--quill-background); 36 | } 37 | 38 | .quill > textarea:focus { 39 | outline: none; 40 | } 41 | 42 | 43 | .quill { 44 | position: relative; 45 | width: var(--quill-width); 46 | height: var(--quill-height); 47 | background-color: var(--quill-background); 48 | } 49 | 50 | .quill-wrap { 51 | overflow-y: scroll; 52 | overflow-x: hidden; 53 | width: var(--quill-width); 54 | height: var(--quill-height); 55 | border: var(--quill-border); 56 | border-radius: 5px; 57 | display: flex; 58 | } 59 | 60 | .quill-wrap::-webkit-scrollbar-thumb { 61 | background: var(--quill-scrollbar-thumb); 62 | border-radius: 6px; 63 | } 64 | .quill-wrap::-webkit-scrollbar-track { 65 | background: var(--quill-background); 66 | border-radius: 6px; 67 | } 68 | .quill-wrap::-webkit-scrollbar { 69 | height: 10px; 70 | width: 10px; 71 | } 72 | 73 | .quill-gutter { 74 | padding: var(--quill-padding); 75 | margin: var(--quill-margin); 76 | font-size: var(--quill-font-size); 77 | font-family: var(--quill-font-family); 78 | height: 100%; 79 | border-right: var(--quill-border); 80 | display: flex; 81 | flex-direction: column; 82 | background: var(--quill-secondary-background); 83 | } 84 | .quill-gutter div { 85 | height: 1.2em; 86 | width: 1.8em; 87 | display: inline-flex; 88 | justify-content: flex-end; 89 | padding-right: 4px; 90 | } 91 | -------------------------------------------------------------------------------- /js/quill.js: -------------------------------------------------------------------------------- 1 | var framePtr=null,excHandler=0,lastJSError=null,NTI33554435={size:0,kind:31,base:null,node:null,finalizer:null},NTI939524165={size:0,kind:18,base:null,node:null,finalizer:null},NTI134217745={size:0,kind:17,base:null,node:null,finalizer:null},NTI134217749={size:0,kind:17,base:null,node:null,finalizer:null},NTI134217751={size:0,kind:17,base:null,node:null,finalizer:null},NTI33555173={size:0,kind:17,base:null,node:null,finalizer:null},NTI33555181={size:0,kind:22,base:null,node:null,finalizer:null},NTI33554449= 2 | {size:0,kind:28,base:null,node:null,finalizer:null},NTI33554450={size:0,kind:29,base:null,node:null,finalizer:null},NTI33555180={size:0,kind:22,base:null,node:null,finalizer:null},NTI33555177={size:0,kind:17,base:null,node:null,finalizer:null},NTI33555178={size:0,kind:17,base:null,node:null,finalizer:null},NTI134217741={size:0,kind:17,base:null,node:null,finalizer:null},NTI134217743={size:0,kind:17,base:null,node:null,finalizer:null},NNI134217743={kind:2,len:0,offset:0,typ:null,name:null,sons:[]}; 3 | NTI134217743.node=NNI134217743;var NNI134217741={kind:2,len:0,offset:0,typ:null,name:null,sons:[]};NTI134217741.node=NNI134217741;var NNI33555178={kind:2,len:0,offset:0,typ:null,name:null,sons:[]};NTI33555178.node=NNI33555178;NTI33555180.base=NTI33555177;NTI33555181.base=NTI33555177; 4 | var NNI33555177={kind:2,len:5,offset:0,typ:null,name:null,sons:[{kind:1,offset:"parent",len:0,typ:NTI33555180,name:"parent",sons:null},{kind:1,offset:"name",len:0,typ:NTI33554450,name:"name",sons:null},{kind:1,offset:"message",len:0,typ:NTI33554449,name:"msg",sons:null},{kind:1,offset:"trace",len:0,typ:NTI33554449,name:"trace",sons:null},{kind:1,offset:"up",len:0,typ:NTI33555181,name:"up",sons:null}]};NTI33555177.node=NNI33555177;var NNI33555173={kind:2,len:0,offset:0,typ:null,name:null,sons:[]}; 5 | NTI33555173.node=NNI33555173;NTI33555177.base=NTI33555173;NTI33555178.base=NTI33555177;NTI134217741.base=NTI33555178;NTI134217743.base=NTI134217741;var NNI134217751={kind:2,len:0,offset:0,typ:null,name:null,sons:[]};NTI134217751.node=NNI134217751;NTI134217751.base=NTI33555178;var NNI134217749={kind:2,len:0,offset:0,typ:null,name:null,sons:[]};NTI134217749.node=NNI134217749;NTI134217749.base=NTI33555178;var NNI134217745={kind:2,len:0,offset:0,typ:null,name:null,sons:[]};NTI134217745.node=NNI134217745; 6 | NTI134217745.base=NTI33555178;var NNI939524165={kind:2,len:2,offset:0,typ:null,name:null,sons:[{kind:1,offset:"a",len:0,typ:NTI33554435,name:"a",sons:null},{kind:1,offset:"b",len:0,typ:NTI33554435,name:"b",sons:null}]};NTI939524165.node=NNI939524165;function mnewString(a){for(var b=Array(a),c=0;ce)b[d]=String.fromCharCode(e),c+=1;else{var f=newSeq_33556919(0);c:for(;;){e=e.toString(16);1==(null==e?0:e.length)?f.push("%0"):f.push("%");f.push(e);c+=1;if(a.length<=c||128>a[c])break c;e=a[c]}++excHandler;try{b[d]=decodeURIComponent(f.join("")),--excHandler}catch(h){e=lastJSError,lastJSError=h,--excHandler,b[d]=f.join(""),lastJSError=e}finally{}}d+=1}if(b.lengthf?c[d]=f:(2048>f?c[d]=f>>6|192:(55296>f||57344<=f?c[d]=f>>12|224:(++e,f=65536+((f&1023)<<10|a.charCodeAt(e)&1023),c[d]=f>>18|240,++d,c[d]=f>>12&63|128),++d,c[d]=f>>6&63|128),++d,c[d]=f&63|128);++d}return c}var objectID_838860977=[0]; 12 | function eventElement_704643180(a){return a.internalElm.childNodes[1]}function textHEX3D_704643189(a,b,c){eventElement_704643180(a[b]).value=c}function settext(a,b,c){textHEX3D_704643189(a,b,c)} 13 | function newQuill_704643098(a,b){var c=document.createElement("div");c.className="quill";document.documentElement.setAttribute("style","--quill-height: "+b);b=document.createElement("textarea");b.className="quill-sizing quill-invis";b.setAttribute("spellcheck","false");var d=document.createElement("pre");d.className="quill-sizing quill-text";d.appendChild(document.createElement("span"));var e=document.createElement("div");e.className="quill-wrap";c.appendChild(d);c.appendChild(b);e.appendChild(c); 14 | a.appendChild(e);a={internalElm:c,sectors:[0],plen:0,onDraw:null,current:0};return a}function newQuill(a,b){return newQuill_704643098(a,b)}function element_704643177(a){return a.internalElm}function element(a){return element_704643177(a)}function eventElement(a){return eventElement_704643180(a)}function visualElement_704643183(a){return a.internalElm.childNodes[0]}function visualElement(a){return visualElement_704643183(a)}function text_704643186(a){return eventElement_704643180(a).value} 15 | function add_33556373(a,b,c){null===a[b]&&(a[b]=[]);var d=a[b].length;a[b].length+=c.length;for(var e=0;ea)&&raiseOverflow()}function raiseRangeError(){raiseException({message:[118,97,108,117,101,32,111,117,116,32,111,102,32,114,97,110,103,101],parent:null,m_type:NTI134217751,name:null,trace:[],up:null},"RangeDefect")} 18 | function isFatPointer_33557259(a){a=void 0==ConstSet1[a.base.kind];return a}function nimCopyAux(a,b,c){switch(c.kind){case 1:a[c.offset]=nimCopy(a[c.offset],b[c.offset],c.typ);break;case 2:for(var d=0;dd)}function init_704643399(a,b){eventElement_704643180(a[b]).addEventListener("input",function(c){quillInputHandle_704643192(a,b)},!1);a[b].onDraw(a,b,text_704643186(a[b]),!1)}function init(a,b){init_704643399(a,b)}function text(a){return text_704643186(a)} 28 | function HEX5BHEX5DHEX3D_1107296323(a,b,c){a.replaceChild(c,a.childNodes[b])}function draw_704643421(a,b,c){HEX5BHEX5DHEX3D_1107296323(visualElement_704643183(a[b]),a[b].current,c)}function draw(a,b,c){draw_704643421(a,b,c)}function HEX5BHEX5D_704643427(a,b){b=chckIndx(subInt(a.length,b),0,a.length-1);return[a,b]} 29 | function enter_704643424(a,b,c){var d;a[b].sectors.push(addInt((d=HEX5BHEX5D_704643427(a[b].sectors,1),d)[0][d[1]],c));visualElement_704643183(a[b]).appendChild(document.createElement("span"));quillInputHandle_704643192(a,b)}function enter(a,b,c){enter_704643424(a,b,c)} 30 | class Quill{constructor(a){this.core=newQuill(a)}init(){return init(this.core)}draw(a){return draw(this.core,a)}enter(a){return enter(this.core,a)}get element(){return element(this.core)}get eventElement(){return eventElement(this.core)}get visualElement(){return visualElement(this.core)}get text(){return text(this.core)}set text(a){settext(this.core,a)}}; 31 | -------------------------------------------------------------------------------- /quill.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.1" 4 | author = "thatrandomperson5" 5 | description = "A new awesome nimble package" 6 | license = "MIT" 7 | srcDir = "src" 8 | 9 | 10 | # Dependencies 11 | 12 | requires "nim >= 1.4.8" 13 | 14 | task test, "Test": 15 | exec "nim js -o:./docs/main.js tests/main.nim" 16 | exec "nim js -o:./docs/nimedit.js tests/nimedit/main.nim" 17 | exec "nim js -o:./docs/nimedit-worker.js tests/nimedit/worker.nim" 18 | 19 | task make, "Build js bindings": 20 | exec "sh js/makejs.sh" -------------------------------------------------------------------------------- /src/quill.nim: -------------------------------------------------------------------------------- 1 | when defined(nimdoc): 2 | import quill/ext/gutters 3 | else: 4 | when not defined(js): 5 | {.error: "Please use js with quill".} 6 | 7 | when defined(quillDebug): 8 | {.warning: "Quill debug is for devs!".} 9 | 10 | {.warning[CStringConv]:off.} 11 | import std/[dom, sugar, jsffi] 12 | import quill/utils 13 | 14 | 15 | # Docs 16 | ##[ 17 | ====== 18 | Quill 19 | ====== 20 | 21 | Quill is a nim js library for making text editors, it is made completely in nim 22 | and is designed to be easy to use. 23 | 24 | Example 25 | ======= 26 | 27 | The example can be found at https://thatrandomperson5.github.io/Quill/example 28 | 29 | Docs 30 | ===== 31 | Docs can be found at https://thatrandomperson5.github.io/Quill/quill 32 | 33 | Installing 34 | ========== 35 | .. code:: 36 | 37 | nimble install https://github.com/thatrandomperson5/Quill 38 | .. 39 | 40 | Guide 41 | ======= 42 | 43 | We first need to add some css to our main html file: 44 | 45 | .. code:: html 46 | 47 | 48 | 49 | .. 50 | 51 | After that we can start coding. A quill has a structure to get it to work: 52 | 53 | * Create the quill 54 | * Set any default text 55 | * Add `onDraw` 56 | 57 | * Main html generation 58 | * Sector entering or forced redraw 59 | * Init extentions and then quill 60 | 61 | Here is a basic example: 62 | 63 | .. code:: nim 64 | 65 | # From tests 66 | import quill, dom 67 | 68 | var myquill = newQuill(document.getElementById("quill"), "70vh") # create the quill with height of 70 69 | myquill.text = "Hello world" # Set the default text 70 | myquill.onDraw = proc (q: var Quill, str: cstring, isDel: bool) = 71 | # Main html generation 72 | let txt = document.createElement("span") 73 | txt.appendChild document.createTextNode(str) 74 | q.draw(txt) 75 | 76 | myquill.init() # Start the quill 77 | .. 78 | 79 | **Note:** There is no reason to redraw if you don't use `insert()`, will be explained in more detail later on. 80 | 81 | This is the most basic quill! But it does not do anything, so is there anything diffrent then a normal textarea? Well, the draw proc takes html, so you can color and style as much as you want! 82 | Try making the span a random color! You can also use the features described below to help you. 83 | 84 | Insert and sectors 85 | ================== 86 | 87 | You can have a `insert()` call in your ondraw proc. This adds text, for example an indent, to your quill. This inserts at the current cursor position, 88 | if you want to add text a diffrent way, use the `text=` proc. 89 | 90 | There are also sectors, which reduce the text passed to onDraw. Say for example your quill only needed to process words and you have this sentance: `hello world good souls`. Normally your draw proc would get passed the whole thing each time, but when processing "world", your code does not need anything from "hello". So, when you detect a space, you would enter a sector using `enter()`. This would mean if the word "hello" was changed to "hi" you would get passed "hi" instead of "hi world good souls". This feature is mainly for efficiency, and a full example can be found in the tests folder. 91 | 92 | **Note**: Why is my inserted text not showing? You have to have a `myquill.forceRedraw()` at the end to make it show. 93 | 94 | **Warning**: `forceRedraw()` and `enter()` must be the last call in your draw proc, also note that `enter()` replaces `forceRedraw()`. 95 | 96 | Gutter 97 | ======== 98 | To add a gutter (numbers on the side), just add the code below: 99 | 100 | .. code:: nim 101 | 102 | # After import quill 103 | import quill/ext/gutters 104 | 105 | # Right before myquill.init() 106 | myquill.initGutter() 107 | .. 108 | ]## 109 | 110 | 111 | type 112 | QuillOnDraw* = (var Quill, cstring, bool) -> void 113 | QuillOnSegment* = (Quill, cstring) -> seq[cstring] 114 | Quill* = ref object 115 | internalElm: Element 116 | onDraw*: QuillOnDraw 117 | sectors: seq[int] 118 | current: int 119 | plen: int 120 | 121 | var documentElement* {.importc: "document.documentElement".}: Element 122 | 123 | proc newQuill*(e: Element, height: cstring="30vh"): Quill = 124 | ## Create a new quill of height `height` and make all needed elements 125 | ## 126 | ## **Note**: Might not look right without the proper css 127 | 128 | var q = document.createElement("div") 129 | q.class = "quill" 130 | documentElement.setAttr("style", cstring"--quill-height: " & height) 131 | let textEditor = document.createElement("textarea") 132 | textEditor.class = "quill-sizing quill-invis" 133 | textEditor.setAttr("spellcheck", "false") 134 | # textEditor.setAttr("contenteditable", "true") 135 | let body = document.createElement("pre") 136 | body.class = "quill-sizing quill-text" 137 | body.appendChild(document.createElement("span")) 138 | let wrap = document.createElement("div") 139 | wrap.class = "quill-wrap" 140 | q.appendChild(body) 141 | q.appendChild(textEditor) 142 | wrap.appendChild(q) 143 | e.appendChild(wrap) 144 | return Quill(internalElm: q, sectors: @[0], plen: 0) 145 | 146 | proc element*(q: Quill): Element = q.internalElm 147 | ## For extentions, like `quill/ext/gutters` 148 | 149 | proc eventElement*(q: Quill): Element = q.internalElm[1] 150 | ## For extentions, like `quill/ext/gutters` 151 | 152 | proc visualElement*(q: Quill): Element = q.internalElm[0] 153 | ## For extentions, like `quill/ext/gutters` 154 | 155 | proc text*(q: Quill): cstring = q.eventElement.value 156 | ## Get the text/value of a quill, directly what the user inputed 157 | 158 | proc `text=`*(q: var Quill, replacment: cstring) = 159 | ## Set the text of a quill (as an user input, so you cannot set html tags) 160 | q.eventElement.value = replacment 161 | 162 | 163 | when defined(quillDebug): 164 | proc dumpSectors(q: Quill): string = 165 | let sectors = q.sectors 166 | let v = q.text 167 | var res = newSeq[cstring](0) 168 | for i, sect in sectors: 169 | if i == 0: 170 | res.add v[0..sect-1] 171 | else: 172 | res.add v[sectors[i-1]..sect-1] 173 | return $res 174 | 175 | proc quillInputHandle(q: var Quill) = 176 | let v = q.text 177 | q.eventElement.setAttr("style", 178 | "min-height: " & $(q.visualElement.scrollHeight+20) & 179 | "px; min-width: " & $(q.visualElement.scrollWidth+20) & "px;" 180 | ) 181 | 182 | let pos = q.eventElement.selectionStart 183 | var currentSector = -1 184 | if q.sectors.len > 0: 185 | while q.sectors[currentSector+1] <= pos: # prev <= 186 | currentSector += 1 187 | if currentSector == q.sectors.high: 188 | break 189 | let adjust = v.len - q.plen 190 | let currentPos = q.sectors[currentSector] 191 | var cacheDelete = newSeq[int](0) 192 | if currentSector < q.sectors.high: 193 | for i in countup(currentSector+1, q.sectors.high): 194 | q.sectors[i] += adjust 195 | if q.sectors[i] <= currentPos or q.sectors[i] >= v.len: 196 | if q.sectors.len > 1: 197 | let child = q.visualElement[i] 198 | if not isUndefined(child): 199 | q.visualElement.removeChild(child) 200 | cacheDelete.add i 201 | for i, pos in cacheDelete: 202 | q.sectors.delete(pos-i) 203 | #[ 204 | if q.sectors.len > 0 and v.len < q.sectors[^1]: 205 | while q.sectors.len > 0 and v.len < q.sectors[^1]: 206 | discard q.sectors.pop() 207 | let lc = q.visualElement.lastChild 208 | q.visualElement.removeChild(lc) 209 | ]# 210 | q.current = currentSector 211 | q.plen = v.len 212 | var e: int 213 | if currentSector < q.sectors.high: 214 | e = q.sectors[currentSector+1]-1 215 | when defined(quillDebug): 216 | echo "Using sector" 217 | else: 218 | e = v.high 219 | when defined(quillDebug): 220 | echo "Using end" 221 | when defined(quillDebug): 222 | echo "Current sector: ", currentSector 223 | echo dumpSectors(q) 224 | q.onDraw(q, v[currentPos..e], adjust < 0) 225 | 226 | proc init*(q: var Quill) = 227 | ## "Turns on" the quill, nothing will work or show properly until this is called 228 | q.eventElement.addEventListener("input", proc (ev: Event) = 229 | quillInputHandle(q) 230 | ) 231 | q.onDraw(q, q.text, false) 232 | 233 | 234 | #[ 235 | proc replaceLast(e: Element, n: Node) = 236 | e.replaceChild(n, lastChild(e)) 237 | ]# 238 | proc draw*(q: var Quill, n: Node) = 239 | ## Draw dom node `n` to quill `q` 240 | q.visualElement[q.current] = n 241 | 242 | proc enter*(q: var Quill, pos: int) = 243 | ## Enter a new sector at `pos` relative to the current sector 244 | ## 245 | ## **Note**: Must be the last call in a onDraw 246 | q.sectors.add q.sectors[^1] + pos 247 | q.visualElement.appendChild(document.createElement("span")) 248 | quillInputHandle(q) 249 | 250 | proc insert*(q: var Quill, text: cstring) = 251 | ## Insert text at users current position 252 | var txt = $(q.text) 253 | txt.insert($(text), q.eventElement.selectionStart) 254 | q.text = txt 255 | 256 | proc forceRedraw*(q: var Quill) = quillInputHandle(q) 257 | ## Force the redrawing of quill `q` 258 | 259 | export toJsStr, createRawText, toCstr -------------------------------------------------------------------------------- /src/quill/ext/gutters.nim: -------------------------------------------------------------------------------- 1 | import std/[dom, jsffi], quill 2 | # import quill/utils 3 | 4 | proc parent(n: Node): Node {.importcpp: "#.parentNode".} 5 | 6 | proc initGutter*(q: var Quill) = 7 | ## Add a responsive gutter to your quill 8 | 9 | let tg = q.element.parent 10 | let gutter = document.createElement("div") 11 | gutter.class = "quill-gutter" 12 | tg.insertBefore(gutter, q.element) 13 | var plinecount = 0 14 | var linecount = 1 15 | proc handleGutter() = 16 | gutter.setAttr("style", cstring"min-height: " & cstring($(q.visualElement.scrollHeight+20)) & "px;".cstring ) 17 | linecount = 1 18 | for c in q.eventElement.value: 19 | if c == '\n': 20 | linecount += 1 21 | if plinecount > linecount: 22 | for _ in 1..plinecount-linecount: 23 | let lc = gutter.lastChild 24 | gutter.removeChild(lc) 25 | elif plinecount < linecount: 26 | let c = document.createElement("div") 27 | c.appendChild(document.createTextNode(cstring($(linecount)))) 28 | gutter.appendChild(c) 29 | plinecount = linecount 30 | q.eventElement.addEventListener("input", proc (e: Event) = 31 | handleGutter() 32 | ) 33 | handleGutter() -------------------------------------------------------------------------------- /src/quill/utils.nim: -------------------------------------------------------------------------------- 1 | import std/dom 2 | 3 | func `[]`*[T, U](s: cstring, i: HSlice[T, U]): cstring = ($s)[i] 4 | ## Slice a cstring 5 | 6 | # Natives 7 | 8 | proc clear*(e: Element) {.importcpp: "#.innerHTML = ''".} 9 | ## Clear an dom element 10 | 11 | proc lastChild*(e: Element): Node {.importcpp: "#.lastChild".} 12 | ## Get the last child of a dom element 13 | 14 | proc fromCharCode(c: char): cstring {.importc: "String.fromCharCode".} 15 | template toCstr*(c: char): untyped = fromCharCode(c) 16 | ## Turn a char into a cstring 17 | proc join(x: openArray[cstring]; d = cstring""): cstring {.importcpp: "#.join(@)".} 18 | 19 | proc toJsStr*(s: string): cstring = 20 | ## Turn a string into a cstring with no escaping 21 | var res = newSeq[cstring](s.len) 22 | for c in s: 23 | res.add c.fromCharCode() 24 | return res.join() 25 | 26 | proc createRawText*(str: string): Node = 27 | return document.createTextNode(str.toJsStr) 28 | 29 | 30 | 31 | proc replace*(tg: cstring, a: cstring, b: cstring): cstring {.importcpp: "#.replace(#, #)".} 32 | ## Replace `a` for `b` in `tg` 33 | 34 | proc innerHTML*(e: Element): cstring {.importcpp: "#.innerHTML".} 35 | ## Get the inner html of a dom element 36 | 37 | proc `innerHTML=`*(e: Element, s: cstring) {.importcpp: "#.innerHTML = #".} 38 | ## Set the inner html of a dom element 39 | 40 | proc selectionStart*(txtarea: Element): int {.importcpp: "#.selectionStart".} 41 | ## Direct wrapper 42 | 43 | proc selectionEnd*(txtarea: Element): int {.importcpp: "#.selectionEnd".} 44 | ## Direct wrapper 45 | 46 | proc `[]=`*(parent: Element, i: int, child: Node) = 47 | ## Set child `i` of `parent` to `child` 48 | 49 | parent.replaceChild(child, parent[i]) -------------------------------------------------------------------------------- /tests/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "$projectDir/../src") 2 | # switch("d", "release") 3 | switch("d", "quillDebug") -------------------------------------------------------------------------------- /tests/main.nim: -------------------------------------------------------------------------------- 1 | # From nim test 2 | import quill, dom, strformat, jscore, strutils 3 | import quill/ext/gutters 4 | 5 | proc genRandomColor(): string = 6 | let a = Math.round(Math.random() * 255) 7 | let b = Math.round(Math.random() * 255) 8 | let c = Math.round(Math.random() * 255) 9 | return fmt"rgb({a}, {b}, {c})" 10 | 11 | 12 | 13 | 14 | var myquill = newQuill(document.getElementById("quill"), "70vh") # init the quill with height of 70 15 | myquill.text = "Hello world" # Set the default text 16 | myquill.onDraw = proc (q: var Quill, str: cstring, isDel: bool) = 17 | # Create a span of random color and then draw it 18 | let txt = document.createElement("span") 19 | let color = genRandomColor() 20 | txt.setAttr("style", $(fmt"color: {color}")) 21 | txt.appendChild document.createTextNode(str) 22 | q.draw(txt) 23 | # If the last character is a newline on add only 24 | if q.text.len != 0: 25 | if q.text[q.text.len-1] == '\n' and not isDel: 26 | q.insert("Hey you made a newline!") # insert some text, rember that it does not redraw 27 | q.enter(str.len) # Enter a new sector for efficiency 28 | 29 | myquill.initGutter() # Add a gutter 30 | myquill.init() # Start the quill 31 | echo "Setup Complete!" -------------------------------------------------------------------------------- /tests/nimedit/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "$projectDir/../../src") 2 | 3 | if defined(emscripten): 4 | # This path will only run if -d:emscripten is passed to nim. 5 | 6 | --nimcache:tmp # Store intermediate files close by in the ./tmp dir. 7 | 8 | --os:linux # Emscripten pretends to be linux. 9 | --cpu:wasm32 # Emscripten is 32bits. 10 | --cc:clang # Emscripten is very close to clang, so we ill replace it. 11 | when defined(windows): 12 | --clang.exe:emcc.bat # Replace C 13 | --clang.linkerexe:emcc.bat # Replace C linker 14 | --clang.cpp.exe:emcc.bat # Replace C++ 15 | --clang.cpp.linkerexe:emcc.bat # Replace C++ linker. 16 | else: 17 | --clang.exe:emcc # Replace C 18 | --clang.linkerexe:emcc # Replace C linker 19 | --clang.cpp.exe:emcc # Replace C++ 20 | --clang.cpp.linkerexe:emcc # Replace C++ linker. 21 | when compileOption("threads"): 22 | # We can have a pool size to populate and be available on page run 23 | # --passL:"-sPTHREAD_POOL_SIZE=2" 24 | discard 25 | --listCmd # List what commands we are running so that we can debug them. 26 | 27 | --gc:arc # GC:arc is friendlier with crazy platforms. 28 | --exceptions:goto # Goto exceptions are friendlier with crazy platforms. 29 | --define:noSignalHandler # Emscripten doesn't support signal handlers. 30 | 31 | # Pass this to Emscripten linker to generate html file scaffold for us. 32 | switch("passL", "-o ../../docs/nimedit-worker.wasm --shell-file shell_minimal.html") -------------------------------------------------------------------------------- /tests/nimedit/main.nim: -------------------------------------------------------------------------------- 1 | import quill, dom, workerapi, jsffi 2 | import quill/[ext/gutters, utils] 3 | 4 | #[ 5 | proc makeStyleSpan(content, style: string): Element = 6 | result = document.createElement("span") 7 | result.appendChild(document.createTextNode($content)) 8 | result.setAttr("style", $style) 9 | ]# 10 | 11 | # proc highlight(e: Element, lang: cstring) {.importcpp: "hljs.highlightElement(#, #)".} 12 | 13 | type Result = ref object 14 | kind: cstring 15 | value: cstring 16 | 17 | let mainWorker = newWorker(cstring"nimedit-worker.js") 18 | 19 | proc getIndent(str: cstring): cstring = 20 | result = "" 21 | for c in str: 22 | if c notin {'\t', ' '}: 23 | if c == '\n' and result.len > 1: 24 | result = result[0..^3] 25 | break 26 | result.add c.toCstr 27 | 28 | proc highlight(str: cstring) = 29 | mainWorker.postMessage(str & cstring("\n")) 30 | 31 | proc strip(str: cstring): cstring {.importcpp: "#.trim()".} 32 | 33 | 34 | var myquill = newQuill(document.getElementById("quill"), "70vh") # create the quill with height of 70 35 | myquill.text = "echo \"Hello world\", 10" # Set the default text 36 | myquill.onDraw = proc (q: var Quill, str: cstring, isDel: bool) = 37 | # Main html generation 38 | 39 | highlight(str) 40 | echo '"', str, '"' 41 | let istr = str.strip() 42 | if str.len != 0: 43 | if str[str.len-1] == '\n' and not isDel: 44 | var indent: cstring = getIndent(str) 45 | if istr.len > 1 and istr[istr.len-1] in {':', '='}: 46 | indent.add " " 47 | q.insert(indent) 48 | q.enter(str.len) 49 | 50 | mainWorker.onMessage = proc (e: js) = 51 | let content = e["data"] 52 | # asm "console.log(`content`)" 53 | let typ = cast[cstring](content["type"]) 54 | if typ == cstring"error": 55 | echo "Error: ", cast[cstring](content["msg"]) 56 | elif typ == cstring"data": 57 | let data = cast[seq[Result]](content["data"]) 58 | let root = document.createElement("span") 59 | for res in data: 60 | if res.kind == cstring"comment": 61 | let elm = document.createElement("span") 62 | elm.setAttr("class", "hljs-comment") 63 | elm.appendChild(document.createTextNode(res.value)) 64 | root.appendChild(elm) 65 | elif res.kind == cstring"num": 66 | let elm = document.createElement("span") 67 | elm.setAttr("class", "hljs-number") 68 | elm.appendChild(document.createTextNode(res.value)) 69 | root.appendChild(elm) 70 | elif res.kind == cstring"str": 71 | let elm = document.createElement("span") 72 | elm.setAttr("class", "hljs-string") 73 | elm.appendChild(document.createTextNode(res.value)) 74 | root.appendChild(elm) 75 | elif res.kind == cstring"keyword": 76 | let elm = document.createElement("span") 77 | elm.setAttr("class", "hljs-keyword") 78 | elm.appendChild(document.createTextNode(res.value)) 79 | root.appendChild(elm) 80 | elif res.kind == cstring"common": 81 | let elm = document.createElement("span") 82 | elm.setAttr("class", "hljs-built_in") 83 | elm.appendChild(document.createTextNode(res.value)) 84 | root.appendChild(elm) 85 | 86 | else: 87 | root.appendChild(document.createTextNode(res.value)) 88 | # asm "console.log(`root`)" 89 | myquill.draw(root) 90 | 91 | myquill.initGutter() 92 | myquill.init() # Start the quill -------------------------------------------------------------------------------- /tests/nimedit/worker.nim: -------------------------------------------------------------------------------- 1 | when defined(js): 2 | import std/[dom, jsffi] 3 | import npeg 4 | 5 | 6 | proc postMessage*(s: cstring) {.importcpp: "postMessage(#)".} 7 | 8 | proc postMessage*(obj: js) {.importcpp: "postMessage(#)".} 9 | 10 | type Result = ref object 11 | kind: cstring 12 | value: cstring 13 | 14 | proc `$`(rs: seq[Result]): string = 15 | for r in rs: 16 | if r.kind == "": 17 | result.add r.value 18 | else: 19 | result.add "<" & $(r.kind) & ">" & $(r.value) & "" 20 | 21 | let parser = peg("nim", r: seq[Result]): 22 | other <- >(+(!content * 1)): 23 | r.add Result(kind: "", value: ($1)) 24 | endings <- &{' ', '\n', '\t', '=', '#', ':', ')', '[', '{', ']', '}', '('} 25 | keywords <- ("addr" | "and" | "as" | "asm" | "bind" | "block" | "break" | 26 | "case" | "cast" | "concept" | "const" | "continue" | "converter" | 27 | "defer" | "discard" | "distinct" | "div" | "do" | "elif" | 28 | "else" | "end" | "enum" | "except" | "export" | "finally" | "for" | 29 | "from" | "func" | "if" | "import" | "in" | "include" | "interface" | 30 | "is" | "isnot" | "iterator" | "let" | "macro" | "method" | "mixin" | 31 | "mod" | "nil" | "not" | "notin" | "object" | "of" | "or" | "out" | 32 | "proc" | "ptr" | "raise" | "ref" | "return" | "shl" | "shr" | 33 | "static" | "template" | "try" | "tuple" | "type" | "using" | "var" | 34 | "when" | "while" | "xor" | "yield") * endings 35 | keywordsc <- >(keywords): 36 | r.add Result(kind: cstring"keyword", value: cstring($1)) 37 | 38 | commons <- ("new" | "await" | "assert" | "echo" | "defined" | "declared" | 39 | "newException" | "countup" | "countdown" | "high" | "low" | "stdin" | 40 | "stdout" | "stderr" | "result" | "true" | "false" | "Inf" | "NegInf" | 41 | "NaN" | "nil" | "int" | "int8" | "int16" | "int32" | "int64" | "uint" | 42 | "uint8" | "uint16" | "uint32" | "uint64" | "float" | "float32" | "float64" | 43 | "bool" | "char" | "string" | "cstring" | "pointer" | "expr" | "stmt" | 44 | "untyped" | "typed" | "void" | "auto" | "any" | "range" | "openArray" | 45 | "varargs" | "seq" | "set" | "clong" | "culong" | "cchar" | "cschar" | 46 | "cshort" | "cint" | "csize" | "clonglong" | "cfloat" | "cdouble" | 47 | "clongdouble" | "cuchar" | "cushort" | "cuint" | "culonglong" | 48 | "cstringArray" | "array" | "RootObject" | "add" | "pop" | "delete") * endings 49 | 50 | commonsc <- >commons: 51 | r.add Result(kind: cstring"common", value: cstring($1)) 52 | 53 | strhead <- {'"', '\''} # | "\"\"\"" 54 | 55 | str <- ?Alpha * R("c", strhead) * *(1 - R("c") - '\n') * R("c") 56 | 57 | strc <- >str: 58 | r.add Result(kind: cstring"str", value: cstring($1)) 59 | 60 | num <- +Digit * ?('.' * +Digit) * ?(?'\'' * {'i', 'u', 'f', 'd'} * ?Digit * ?Digit) 61 | 62 | numc <- >num: 63 | r.add Result(kind: cstring"num", value: cstring($1)) 64 | 65 | comment <- '#' * *(1 - '\n') * ('\n' | !1) 66 | 67 | commentc <- >comment: 68 | r.add Result(kind: cstring"comment", value: cstring($1)) 69 | 70 | content <- num | str | comment | keywords | commons 71 | nim <- *(numc | strc | commentc | keywordsc | commonsc | other) * !1 72 | 73 | proc handleRequest(e: js) = 74 | let code = $(cast[cstring](e["data"])) 75 | # echo code 76 | var res = newSeq[Result](0) 77 | if not parser.match(code, res).ok: 78 | postMessage(js{"type": cstring"error", "msg": cstring"parsing failed"}) 79 | else: 80 | let data = cast[js](res) 81 | postMessage(js{"type": cstring"data", "data": data}) 82 | 83 | 84 | proc `onmessage=`(p: proc (e: js)) {.importcpp: "(onmessage = #)"} 85 | 86 | onmessage = handleRequest 87 | 88 | -------------------------------------------------------------------------------- /tests/nimedit/workerapi.nim: -------------------------------------------------------------------------------- 1 | import std/[jsffi, dom] 2 | 3 | type Worker* {.importc.} = ref object of RootObj 4 | 5 | proc newWorker*(path: cstring): Worker {.importcpp: "(new Worker(#))".} 6 | 7 | proc postMessage*(w: Worker, s: cstring) {.importcpp: "#.postMessage(#)".} 8 | 9 | proc postMessage*(w: Worker, obj: js) {.importcpp: "#.postMessage(#)".} 10 | 11 | proc `onMessage=`*(w: Worker, cb: proc (ev: Event)) {.importcpp: "#.onmessage = (#)"} 12 | 13 | proc `onMessage=`*(w: Worker, cb: proc (ev: js)) {.importcpp: "#.onmessage = (#)"} 14 | 15 | --------------------------------------------------------------------------------