├── .gitignore ├── README.md ├── backends.rst ├── clean ├── doc.nim ├── notworking.rst ├── nsw.exe ├── nsw.nim ├── nsw.nimble └── test.rst /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TerseNet 2 | 3 | For some context, read [Adblocking, How about Nah?](https://www.eff.org/deeplinks/2019/07/adblocking-how-about-nah) 4 | 5 | # Work in Progress 6 | 7 | *Note: What this actually does so far* Right now (after two days of actual coding) it is sort of like a janky RST viewer that only knows about headings and paragraphs. Not useable for anything. 8 | 9 | I have also done some research, such as `dat`, `ipfs`, `gnunet` and `libp2p`, web assembly, etc. `ipfs` seems to be performing better now at retrieving content than it did a few years ago. 10 | 11 | # Everything below here is just an idea -- not actually coded yet 12 | 13 | TerseNet is a new type of lightweight, high-performance document, media and application browser suite that is *100% JavaScript free*. Pages are encoded in stricly size-limited subset of restructuredtext distributed in a content-oriented (rather than server-centric) fashion. They load almost instantly from network peers. 14 | 15 | This software prototype is written in the [Nim](https://nim-lang.org) programming language. One of the primary goals is for it to be feasible for individuals or small teams to implement some or all types of TerseNet browsers, so there should be multiple clients available, and large companies cannot control the platform, as is the case with browsers essentially incorporating a full bloated operating system that can only be implemented by large teams over the course of years. 16 | 17 | ## Ad-free 18 | 19 | It is impossible for ads from the traditional web to display in TerseNet, or for companies to track you -- because no content or links from the traditional web can be displayed by TerseNet browsers. 20 | 21 | This web browser is a relatively simple system, rather than an entire operating system-in-a-box like Chrome or Firefox. It is supported by decentralized protocols. 22 | 23 | # Multiple Browser Types with a Simple Standard for Integration Between Them 24 | 25 | TerseNet functionality is separated into three types of browsers: 26 | 27 | 1. Info browsers, for distributing text and very lightweight diagrams, 28 | 2. Media Applications browsers, which run web assembly applications that can optionally be used to display or interact with media from TerseNet sites, 29 | 3. Extended Media Applications browsers, handling web assembly applications that support various extensions via a type of device driver API. 30 | 31 | There should be multiple implementations of each type of browser. To make this practical, the required functionality is kept as sparse as possible while still remaining as flexible as necessary for each type of functionality. 32 | 33 | ## TerseNet Info Pages 34 | 35 | ### RST (restructured text) subset 36 | 37 | TerseNet Info RST supports the following features: headings (with `#`), escape with backtick, bold, italics, bullets, and links (only to other TerseNet info pages, e.g. terse://reports.frontline/aug2019austin). Pages may also incoroporate IconVG files in between text paragraphs. 38 | 39 | Each page may be a maximum of 5KiB of text and/or IconVG data when uncompressed, with an additional optional 1KiB of attachment listings. 40 | 41 | 42 | 43 | ## TerseNet Media Applications 44 | 45 | ## TerseNet Extended Applications 46 | 47 | 48 | ## Attachments 49 | 50 | All non-RST content (except for one small image) is loaded *only* upon demand by the user. 51 | 52 | Media and applications can be attached to pages, but have restrictions. No more than 10KB of images will be loaded by default. 53 | 54 | Other than that small image, media must be launched explicitly from the Media tab. Zero bytes will be loaded, processed, or transmitted without prompting by the user. 55 | 56 | ### Media attachments 57 | 58 | Pages may attach images or videos. Their descriptions appear on the Media Tab. Users must switch tabs and click the individual media item before any loading or processing is done. Images and media may not be loaded from traditional web sources outside of TerseNet protocols. 59 | 60 | ### Application attachments 61 | 62 | Applications may be attached to pages. They appear on the Applications tab and are not downloaded or executed unless explicitly launched by the user. 63 | 64 | Attached applications are written in web assembly with some basic I/O abilities which include: 65 | 66 | * A [simple UI API](https://github.com/simple2d/simple2d) 67 | 68 | * A communications API that connects applications with TerseNet's p2p backbone 69 | 70 | * Carefully controlled payment prompts that integrate popular cryptocurrencies. Available cryptocurrencies currently include: Bitcoin, Ethereum, and Bitcoin Cash. 71 | 72 | ## P2P Search 73 | 74 | All markdown content is automatically added to a full-text search system. This search capability is powered by the peers in the network. 75 | -------------------------------------------------------------------------------- /backends.rst: -------------------------------------------------------------------------------- 1 | ================================ 2 | Nim Backend Integration 3 | ================================ 4 | 5 | :Author: Puppet Master 6 | :Version: |nimversion| 7 | 8 | .. contents:: 9 | "Heresy grows from idleness." -- Unknown. 10 | 11 | 12 | Introduction 13 | ============ 14 | 15 | The `Nim Compiler User Guide `_ documents the typical 16 | compiler invocation, using the ``compile`` or ``c`` command to transform a 17 | ``.nim`` file into one or more ``.c`` files which are then compiled with the 18 | platform's C compiler into a static binary. However there are other commands 19 | to compile to C++, Objective-C or JavaScript. This document tries to 20 | concentrate in a single place all the backend and interfacing options. 21 | 22 | The Nim compiler supports mainly two backend families: the C, C++ and 23 | Objective-C targets and the JavaScript target. `The C like targets 24 | <#backends-the-c-like-targets>`_ creates source files which can be compiled 25 | into a library or a final executable. `The JavaScript target 26 | <#backends-the-javascript-target>`_ can generate a ``.js`` file which you 27 | reference from an HTML file or create a `standalone nodejs program 28 | `_. 29 | 30 | On top of generating libraries or standalone applications, Nim offers 31 | bidirectional interfacing with the backend targets through generic and 32 | specific pragmas. 33 | 34 | 35 | Backends 36 | ======== 37 | 38 | The C like targets 39 | ------------------ 40 | 41 | The commands to compile to either C, C++ or Objective-C are: 42 | 43 | //compileToC, cc compile project with C code generator 44 | //compileToCpp, cpp compile project to C++ code 45 | //compileToOC, objc compile project to Objective C code 46 | 47 | The most significant difference between these commands is that if you look 48 | into the ``nimcache`` directory you will find ``.c``, ``.cpp`` or ``.m`` 49 | files, other than that all of them will produce a native binary for your 50 | project. This allows you to take the generated code and place it directly 51 | into a project using any of these languages. Here are some typical command 52 | line invocations:: 53 | 54 | $ nim c hallo.nim 55 | $ nim cpp hallo.nim 56 | $ nim objc hallo.nim 57 | 58 | The compiler commands select the target backend, but if needed you can 59 | `specify additional switches for cross compilation 60 | `_ to select the target CPU, operative system 61 | or compiler/linker commands. 62 | 63 | 64 | The JavaScript target 65 | --------------------- 66 | 67 | Nim can also generate `JavaScript`:idx: code through the ``js`` command. 68 | 69 | Nim targets JavaScript 1.5 which is supported by any widely used browser. 70 | Since JavaScript does not have a portable means to include another module, 71 | Nim just generates a long ``.js`` file. 72 | 73 | Features or modules that the JavaScript platform does not support are not 74 | available. This includes: 75 | 76 | * manual memory management (``alloc``, etc.) 77 | * casting and other unsafe operations (``cast`` operator, ``zeroMem``, etc.) 78 | * file management 79 | * most modules of the standard library 80 | * proper 64 bit integer arithmetic 81 | * unsigned integer arithmetic 82 | 83 | However, the modules `strutils `_, `math `_, and 84 | `times `_ are available! To access the DOM, use the `dom 85 | `_ module that is only available for the JavaScript platform. 86 | 87 | To compile a Nim module into a ``.js`` file use the ``js`` command; the 88 | default is a ``.js`` file that is supposed to be referenced in an ``.html`` 89 | file. However, you can also run the code with `nodejs`:idx: 90 | (``_):: 91 | 92 | nim js -d:nodejs -r examples/hallo.nim 93 | 94 | 95 | Interfacing 96 | =========== 97 | 98 | Nim offers bidirectional interfacing with the target backend. This means 99 | that you can call backend code from Nim and Nim code can be called by 100 | the backend code. Usually the direction of which calls which depends on your 101 | software architecture (is Nim your main program or is Nim providing a 102 | component?). 103 | 104 | 105 | Nim code calling the backend 106 | ---------------------------- 107 | 108 | Nim code can interface with the backend through the `Foreign function 109 | interface `_ mainly through the 110 | `importc pragma `_. The ``importc`` pragma is the 111 | *generic* way of making backend symbols available in Nim and is available 112 | in all the target backends (JavaScript too). The C++ or Objective-C backends 113 | have their respective `ImportCpp `_ and 114 | `ImportObjC `_ pragmas to call methods from 115 | classes. 116 | 117 | Whenever you use any of these pragmas you need to integrate native code into 118 | your final binary. In the case of JavaScript this is no problem at all, the 119 | same html file which hosts the generated JavaScript will likely provide other 120 | JavaScript functions which you are importing with ``importc``. 121 | 122 | However, for the C like targets you need to link external code either 123 | statically or dynamically. The preferred way of integrating native code is to 124 | use dynamic linking because it allows you to compile Nim programs without 125 | the need for having the related development libraries installed. This is done 126 | through the `dynlib pragma for import 127 | `_, though more specific control can be 128 | gained using the `dynlib module `_. 129 | 130 | The `dynlibOverride `_ command line switch allows 131 | to avoid dynamic linking if you need to statically link something instead. 132 | Nim wrappers designed to statically link source files can use the `compile 133 | pragma `_ if there are few sources or providing 134 | them along the Nim code is easier than using a system library. Libraries 135 | installed on the host system can be linked in with the `PassL pragma 136 | `_. 137 | 138 | To wrap native code, take a look at the `c2nim tool `_ which helps 139 | with the process of scanning and transforming header files into a Nim 140 | interface. 141 | 142 | C invocation example 143 | ~~~~~~~~~~~~~~~~~~~~ 144 | 145 | Create a ``logic.c`` file with the following content: 146 | 147 | .. code-block:: c 148 | int addTwoIntegers(int a, int b) 149 | { 150 | return a + b; 151 | } 152 | 153 | Create a ``calculator.nim`` file with the following content: 154 | 155 | .. code-block:: nim 156 | 157 | {.compile: "logic.c".} 158 | proc addTwoIntegers(a, b: cint): cint {.importc.} 159 | 160 | when isMainModule: 161 | echo addTwoIntegers(3, 7) 162 | 163 | With these two files in place, you can run ``nim c -r calculator.nim`` and 164 | the Nim compiler will compile the ``logic.c`` file in addition to 165 | ``calculator.nim`` and link both into an executable, which outputs ``10`` when 166 | run. Another way to link the C file statically and get the same effect would 167 | be remove the line with the ``compile`` pragma and run the following typical 168 | Unix commands:: 169 | 170 | $ gcc -c logic.c 171 | $ ar rvs mylib.a logic.o 172 | $ nim c --passL:mylib.a -r calculator.nim 173 | 174 | Just like in this example we pass the path to the ``mylib.a`` library (and we 175 | could as well pass ``logic.o``) we could be passing switches to link any other 176 | static C library. 177 | 178 | 179 | JavaScript invocation example 180 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 181 | 182 | Create a ``host.html`` file with the following content: 183 | 184 | .. code-block:: 185 | 186 | 187 | 193 | 194 | 195 | 196 | Create a ``calculator.nim`` file with the following content (or reuse the one 197 | from the previous section): 198 | 199 | .. code-block:: nim 200 | 201 | proc addTwoIntegers(a, b: int): int {.importc.} 202 | 203 | when isMainModule: 204 | echo addTwoIntegers(3, 7) 205 | 206 | Compile the Nim code to JavaScript with ``nim js -o:calculator.js 207 | calculator.nim`` and open ``host.html`` in a browser. If the browser supports 208 | javascript, you should see the value ``10`` in the browser's console. Use the 209 | `dom module `_ for specific DOM querying and modification procs 210 | or take a look at `karax `_ for how to 211 | develop browser based applications. 212 | 213 | 214 | Backend code calling Nim 215 | ------------------------ 216 | 217 | Backend code can interface with Nim code exposed through the `exportc 218 | pragma `_. The ``exportc`` pragma is the *generic* 219 | way of making Nim symbols available to the backends. By default the Nim 220 | compiler will mangle all the Nim symbols to avoid any name collision, so 221 | the most significant thing the ``exportc`` pragma does is maintain the Nim 222 | symbol name, or if specified, use an alternative symbol for the backend in 223 | case the symbol rules don't match. 224 | 225 | The JavaScript target doesn't have any further interfacing considerations 226 | since it also has garbage collection, but the C targets require you to 227 | initialize Nim's internals, which is done calling a ``NimMain`` function. 228 | Also, C code requires you to specify a forward declaration for functions or 229 | the compiler will assume certain types for the return value and parameters 230 | which will likely make your program crash at runtime. 231 | 232 | The Nim compiler can generate a C interface header through the ``--header`` 233 | command line switch. The generated header will contain all the exported 234 | symbols and the ``NimMain`` proc which you need to call before any other 235 | Nim code. 236 | 237 | 238 | Nim invocation example from C 239 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 240 | 241 | Create a ``fib.nim`` file with the following content: 242 | 243 | .. code-block:: nim 244 | 245 | proc fib(a: cint): cint {.exportc.} = 246 | if a <= 2: 247 | result = 1 248 | else: 249 | result = fib(a - 1) + fib(a - 2) 250 | 251 | Create a ``maths.c`` file with the following content: 252 | 253 | .. code-block:: c 254 | 255 | #include "fib.h" 256 | #include 257 | 258 | int main(void) 259 | { 260 | NimMain(); 261 | for (int f = 0; f < 10; f++) 262 | printf("Fib of %d is %d\n", f, fib(f)); 263 | return 0; 264 | } 265 | 266 | Now you can run the following Unix like commands to first generate C sources 267 | form the Nim code, then link them into a static binary along your main C 268 | program:: 269 | 270 | $ nim c --noMain --noLinking --header:fib.h fib.nim 271 | $ gcc -o m -I$HOME/.cache/nim/fib_d -Ipath/to/nim/lib $HOME/.cache/nim/fib_d/*.c maths.c 272 | 273 | The first command runs the Nim compiler with three special options to avoid 274 | generating a ``main()`` function in the generated files, avoid linking the 275 | object files into a final binary, and explicitly generate a header file for C 276 | integration. All the generated files are placed into the ``nimcache`` 277 | directory. That's why the next command compiles the ``maths.c`` source plus 278 | all the ``.c`` files form ``nimcache``. In addition to this path, you also 279 | have to tell the C compiler where to find Nim's ``nimbase.h`` header file. 280 | 281 | Instead of depending on the generation of the individual ``.c`` files you can 282 | also ask the Nim compiler to generate a statically linked library:: 283 | 284 | $ nim c --app:staticLib --noMain --header fib.nim 285 | $ gcc -o m -Inimcache -Ipath/to/nim/lib libfib.nim.a maths.c 286 | 287 | The Nim compiler will handle linking the source files generated in the 288 | ``nimcache`` directory into the ``libfib.nim.a`` static library, which you can 289 | then link into your C program. Note that these commands are generic and will 290 | vary for each system. For instance, on Linux systems you will likely need to 291 | use ``-ldl`` too to link in required dlopen functionality. 292 | 293 | 294 | Nim invocation example from JavaScript 295 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 296 | 297 | Create a ``mhost.html`` file with the following content: 298 | 299 | .. code-block:: 300 | 301 | 302 | 303 | 306 | 307 | 308 | Create a ``fib.nim`` file with the following content (or reuse the one 309 | from the previous section): 310 | 311 | .. code-block:: nim 312 | 313 | proc fib(a: cint): cint {.exportc.} = 314 | if a <= 2: 315 | result = 1 316 | else: 317 | result = fib(a - 1) + fib(a - 2) 318 | 319 | Compile the Nim code to JavaScript with ``nim js -o:fib.js fib.nim`` and 320 | open ``mhost.html`` in a browser. If the browser supports javascript, you 321 | should see an alert box displaying the text ``Fib for 9 is 34``. As mentioned 322 | earlier, JavaScript doesn't require an initialisation call to ``NimMain`` or 323 | similar function and you can call the exported Nim proc directly. 324 | 325 | 326 | Nimcache naming logic 327 | --------------------- 328 | 329 | The `nimcache`:idx: directory is generated during compilation and will hold 330 | either temporary or final files depending on your backend target. The default 331 | name for the directory depends on the used backend and on your OS but you can 332 | use the ``--nimcache`` `compiler switch `_ to 333 | change it. 334 | 335 | Nimcache and C like targets 336 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 337 | 338 | The C like backends will place their temporary ``.c``, ``.cpp`` or ``.m`` files 339 | in the ``nimcache`` directory. The naming of these files follows the pattern 340 | ``nimblePackageName_`` + ``nimSource``: 341 | 342 | * Filenames for modules imported from `nimble packages 343 | `_ will end up with 344 | ``nimblePackageName_module.c``. For example, if you import the 345 | ``argument_parser`` module from the same name nimble package you 346 | will end up with a ``argument_parser_argument_parser.c`` file 347 | under ``nimcache``. The name of the nimble package comes from the 348 | ``proj.nimble`` file, the actual contents are not read by the 349 | compiler. 350 | 351 | * Filenames for non nimble packages (like your project) will be 352 | renamed from ``.nim`` to have the extension of your target backend 353 | (from now on ``.c`` for these examples), but otherwise nothing 354 | else will change. This will quickly break if your project consists 355 | of a main ``proj.nim`` file which includes a ``utils/proj.nim`` 356 | file: both ``proj.nim`` files will generate the same name ``proj.c`` 357 | output in the ``nimcache`` directory overwriting themselves! 358 | 359 | * Filenames for modules found in the standard library will be named 360 | ``stdlib_module.c``. Unless you are doing something special, you 361 | will end up with at least ``stdlib_system.c``, since the `system 362 | module `_ is always imported automatically. Same for 363 | the `hashes module `_ which will be named 364 | ``stdlib_hashes.c``. The ``stdlib_`` prefix comes from the *fake* 365 | ``lib/stdlib.nimble`` file. 366 | 367 | To find the name of a nimble package the compiler searches for a ``*.nimble`` 368 | file in the parent directory hierarchy of whatever module you are compiling. 369 | Even if you are in a subdirectory of your project, a parent ``*.nimble`` file 370 | will influence the naming of the nimcache name. This means that on Unix systems 371 | creating the file ``~/foo.nimble`` will automatically prefix all nimcache files 372 | not part of another package with the string ``foo_``. 373 | 374 | 375 | Nimcache and the Javascript target 376 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 377 | 378 | Unless you explicitly use the ``-o:filename.js`` switch as mentioned in the 379 | previous examples, the compiler will create a ``filename.js`` file in the 380 | ``nimcache`` directory using the name of your input nim file. There are no 381 | other temporary files generated, the output is always a single self contained 382 | ``.js`` file. 383 | 384 | 385 | Memory management 386 | ================= 387 | 388 | In the previous sections the ``NimMain()`` function reared its head. Since 389 | JavaScript already provides automatic memory management, you can freely pass 390 | objects between the two language without problems. In C and derivate languages 391 | you need to be careful about what you do and how you share memory. The 392 | previous examples only dealt with simple scalar values, but passing a Nim 393 | string to C, or reading back a C string in Nim already requires you to be 394 | aware of who controls what to avoid crashing. 395 | 396 | 397 | Strings and C strings 398 | --------------------- 399 | 400 | The manual mentions that `Nim strings are implicitly convertible to 401 | cstrings `_ which makes interaction usually 402 | painless. Most C functions accepting a Nim string converted to a 403 | ``cstring`` will likely not need to keep this string around and by the time 404 | they return the string won't be needed any more. However, for the rare cases 405 | where a Nim string has to be preserved and made available to the C backend 406 | as a ``cstring``, you will need to manually prevent the string data from being 407 | freed with `GC_ref `_ and `GC_unref 408 | `_. 409 | 410 | A similar thing happens with C code invoking Nim code which returns a 411 | ``cstring``. Consider the following proc: 412 | 413 | .. code-block:: nim 414 | 415 | proc gimme(): cstring {.exportc.} = 416 | result = "Hey there C code! " & $random(100) 417 | 418 | Since Nim's garbage collector is not aware of the C code, once the 419 | ``gimme`` proc has finished it can reclaim the memory of the ``cstring``. 420 | However, from a practical standpoint, the C code invoking the ``gimme`` 421 | function directly will be able to use it since Nim's garbage collector has 422 | not had a chance to run *yet*. This gives you enough time to make a copy for 423 | the C side of the program, as calling any further Nim procs *might* trigger 424 | garbage collection making the previously returned string garbage. Or maybe you 425 | are `yourself triggering the collection `_. 426 | 427 | 428 | Custom data types 429 | ----------------- 430 | 431 | Just like strings, custom data types that are to be shared between Nim and 432 | the backend will need careful consideration of who controls who. If you want 433 | to hand a Nim reference to C code, you will need to use `GC_ref 434 | `_ to mark the reference as used, so it does not get 435 | freed. And for the C backend you will need to expose the `GC_unref 436 | `_ proc to clean up this memory when it is not required 437 | any more. 438 | 439 | Again, if you are wrapping a library which *mallocs* and *frees* data 440 | structures, you need to expose the appropriate *free* function to Nim so 441 | you can clean it up. And of course, once cleaned you should avoid accessing it 442 | from Nim (or C for that matter). Typically C data structures have their own 443 | ``malloc_structure`` and ``free_structure`` specific functions, so wrapping 444 | these for the Nim side should be enough. 445 | 446 | 447 | Thread coordination 448 | ------------------- 449 | 450 | When the ``NimMain()`` function is called Nim initializes the garbage 451 | collector to the current thread, which is usually the main thread of your 452 | application. If your C code later spawns a different thread and calls Nim 453 | code, the garbage collector will fail to work properly and you will crash. 454 | 455 | As long as you don't use the threadvar emulation Nim uses native thread 456 | variables, of which you get a fresh version whenever you create a thread. You 457 | can then attach a GC to this thread via 458 | 459 | .. code-block:: nim 460 | 461 | system.setupForeignThreadGc() 462 | 463 | It is **not** safe to disable the garbage collector and enable it after the 464 | call from your background thread even if the code you are calling is short 465 | lived. 466 | 467 | Before the thread exits, you should tear down the thread's GC to prevent memory 468 | leaks by calling 469 | 470 | .. code-block:: nim 471 | 472 | system.tearDownForeignThreadGc() 473 | 474 | -------------------------------------------------------------------------------- /clean: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm *~ 3 | 4 | -------------------------------------------------------------------------------- /doc.nim: -------------------------------------------------------------------------------- 1 | import packages/docutils/rst 2 | import packages/docutils/rstast 3 | import nigui 4 | import math, times, os, strutils 5 | 6 | const spaceWidth = 3 7 | var x = 0 8 | var y = 0 9 | var size = [40f, 35f, 25f, 20f ] 10 | var textWidth = 0 11 | var textHeight = 22 12 | const wScale = 0.9 13 | var pageWidth = 570 14 | var lastUnknown = false 15 | var pageHeight* = 400 16 | var drawTextCalls = 0 17 | var lineWidth = 0 18 | var textRun = false 19 | var words: seq[string] = @[] 20 | var level = 3 21 | var scrollY = 0 22 | var controlHeight = 500 23 | var firstPass = true 24 | 25 | proc parse*(rst:string): PRstNode = 26 | var opts = {roSupportSmilies} 27 | var hasToc = false 28 | let time = cpuTime() 29 | result = rstParse(rst,"",0,0,hasToc,opts) 30 | echo "parse in ", (cpuTime()-time) 31 | 32 | proc splitLines(canvas: Canvas) : seq[string] = 33 | var textWidth = 0 34 | var line = "" 35 | var lines:seq[string] = @[] 36 | var n = 0 37 | for word in words: 38 | n += 1 39 | line &= word 40 | textWidth = getTextLineWidth(canvas, line) 41 | if (n == len(words)) or (textWidth > pageWidth): 42 | for subline in line.splitLines(): 43 | lines.add(subline) 44 | line = "" 45 | lineWidth = textWidth 46 | return lines 47 | 48 | proc inView():bool = 49 | let startY = scrollY 50 | let endY = scrollY + controlHeight 51 | #echo startY, ",", endY, ", ", scrollY 52 | result = (y >= startY and y <= endY) 53 | 54 | proc renderLeaf(canvas: Canvas) = 55 | if x > pageWidth: 56 | x = spaceWidth 57 | y += (textHeight.toFloat * 1.5).toInt 58 | 59 | var lines = splitLines(canvas) 60 | for line in lines: 61 | canvas.fontSize = size[level-1] 62 | if inView(): 63 | canvas.drawText(line, x, y) 64 | drawTextCalls += 1 65 | if len(lines) != 1: 66 | y += textHeight 67 | #x = floor(textWidth.toFloat * wScale).toInt 68 | x = spaceWidth 69 | else: 70 | x += lineWidth 71 | 72 | lastUnknown = false 73 | 74 | proc renderHeadline(canvas: Canvas, node: PRstNode) = 75 | y += (textHeight.toFloat * 1.5).toInt 76 | x = spaceWidth 77 | canvas.fontSize = size[node.level-1] 78 | level = node.level 79 | #textHeight = getTextLineHeight(canvas) 80 | textHeight = 30 81 | lastUnknown = false 82 | 83 | proc renderParagraph(canvas: Canvas, node: PRstNode) = 84 | canvas.fontSize = size[3] 85 | level = 3 86 | y += (textHeight.toFloat * 1.5).toInt 87 | x = spaceWidth 88 | #textHeight = getTextLineHeight(canvas) 89 | textHeight = 22 90 | lastUnknown = false 91 | 92 | proc renderUnknown(canvas: Canvas, node: PRstNode) = 93 | #echo node.kind 94 | level = 3 95 | if not lastUnknown: 96 | x += spaceWidth 97 | #textHeight = getTextLineHeight(canvas) 98 | #textHeight = 22 99 | #y += (textHeight.toFloat * 1.5).toInt 100 | #x = spaceWidth 101 | lastUnknown = true 102 | 103 | proc renderNode(canvas: Canvas, node: PRstNode) = 104 | if firstPass and not inView(): return 105 | if node == nil: return 106 | #echo node.kind 107 | var dummy = 0 108 | var foundText = false 109 | 110 | if textRun and node.kind != rnLeaf: 111 | renderLeaf(canvas) 112 | textRun = false 113 | 114 | case node.kind: 115 | of rnHeadline: renderHeadline(canvas, node) 116 | of rnParagraph: renderParagraph(canvas, node) 117 | of rnLeaf: 118 | if not textRun: 119 | textRun = true 120 | words = newSeq[string]() 121 | foundText = true 122 | words &= node.text 123 | else: renderUnknown(canvas, node) 124 | 125 | if not foundText: textRun = false 126 | 127 | for son in node.sons: 128 | renderNode(canvas, son) 129 | 130 | proc render*(canvas: Canvas, root: PRstNode, scrollPos:int) = 131 | x = 0 132 | y = 0 133 | scrollY = scrollPos 134 | var n = 0 135 | textRun = false 136 | words = newSeq[string]() 137 | level = 3 138 | 139 | drawTextCalls = 0 140 | 141 | let time = cpuTime() 142 | for son in root.sons: 143 | n += 1 144 | renderNode(canvas, son) 145 | 146 | if pageHeight < y: 147 | pageHeight = y + 100 148 | echo "render in ",(cpuTime()-time)*1000.0 149 | echo "total drawText calls: ",drawTextCalls 150 | firstPass = false 151 | 152 | -------------------------------------------------------------------------------- /notworking.rst: -------------------------------------------------------------------------------- 1 | """"""""""""""""" 2 | Document Title 3 | """"""""""""""""" 4 | ........... 5 | Subtitle 6 | ........... 7 | 8 | .. contents:: Overview 9 | :depth: 3 10 | 11 | =================== 12 | Section 1 13 | =================== 14 | 15 | Text can be *italicized* or **bolded** as well as ``monospaced``. 16 | You can \*escape certain\* special characters. 17 | 18 | ---------------------- 19 | Subsection 1 (Level 2) 20 | ---------------------- 21 | 22 | Some section 2 text 23 | 24 | Sub-subsection 1 (level 3) 25 | -------------------------- 26 | 27 | Some more text. 28 | 29 | ========= 30 | Examples 31 | ========= 32 | 33 | -------- 34 | Comments 35 | -------- 36 | 37 | .. This is a comment 38 | Special notes that are not shown but might come out as HTML comments 39 | 40 | ------ 41 | Images 42 | ------ 43 | 44 | Add an image with: 45 | 46 | .. image:: screenshots/file.png 47 | :height: 100 48 | :width: 200 49 | :alt: alternate text 50 | 51 | You can inline an image or other directive with the |customsub| command. 52 | 53 | .. |customsub| image:: image/image.png 54 | :alt: (missing image text) 55 | 56 | ----- 57 | Lists 58 | ----- 59 | 60 | - Bullet are made like this 61 | - Point levels must be consistent 62 | * Sub-bullets 63 | + Sub-sub-bullets 64 | - Lists 65 | 66 | Term 67 | Definition for term 68 | Term2 69 | Definition for term 2 70 | 71 | :List of Things: 72 | item1 - these are 'field lists' not bulleted lists 73 | item2 74 | item 3 75 | 76 | :Something: single item 77 | :Someitem: single item 78 | 79 | ----------------- 80 | Preformatted text 81 | ----------------- 82 | 83 | A code example prefix must always end with double colon like it's presenting something:: 84 | 85 | Anything indented is part of the preformatted block 86 | Until 87 | It gets back to 88 | Allll the way left 89 | 90 | Now we're out of the preformatted block. 91 | 92 | ------------ 93 | Code blocks 94 | ------------ 95 | 96 | There are three equivalents: ``code``, ``sourcecode``, and ``code-block``. 97 | 98 | .. code:: python 99 | 100 | import os 101 | print(help(os)) 102 | 103 | .. code-block:: 104 | 105 | # Equivalent 106 | 107 | ----- 108 | Links 109 | ----- 110 | 111 | Web addresses by themselves will auto link, like this: https://www.devdungeon.com 112 | 113 | You can also inline custom links: `Google search engine `_ 114 | 115 | This is a simple link_ to Google with the link defined separately. 116 | 117 | .. _link: https://www.google.com 118 | 119 | This is a link to the `Python website`_. 120 | 121 | .. _Python website: http://www.python.org/ 122 | 123 | This is a link back to `Section 1`_. You can link based off of the heading name 124 | within a document. 125 | 126 | --------- 127 | Footnotes 128 | --------- 129 | 130 | Footnote Reference [1]_ 131 | 132 | .. [1] This is footnote number one that would go at the bottom of the document. 133 | 134 | Or autonumbered [#] 135 | 136 | .. [#] This automatically becomes second, based on the 1 already existing. 137 | 138 | ----------------- 139 | Lines/Transitions 140 | ----------------- 141 | 142 | Any 4+ repeated characters with blank lines surrounding it becomes an hr line, like this. 143 | 144 | ==================================== 145 | 146 | ------ 147 | Tables 148 | ------ 149 | 150 | ---------------------- 151 | Preserving line breaks 152 | ---------------------- 153 | 154 | Normally you can break the line in the middle of a paragraph and it will 155 | ignore the newline. If you want to preserve the newlines, use the ``|`` prefix 156 | on the lines. For example: 157 | 158 | | These lines will 159 | | break exactly 160 | | where we told them to. 161 | -------------------------------------------------------------------------------- /nsw.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/runvnc/tersenet/0585edad5ac1df52c86d8abdd8c9fc3eeaa6b0af/nsw.exe -------------------------------------------------------------------------------- /nsw.nim: -------------------------------------------------------------------------------- 1 | import nigui, packages/docutils/rstast, doc 2 | 3 | app.init() 4 | 5 | var rst = readFile("backends.rst") 6 | 7 | var root = parse(rst) 8 | 9 | #echo renderRstToJson(root) 10 | 11 | var window = newWindow("NoScript Web") 12 | 13 | window.width = 600 14 | window.height = 400 15 | 16 | # icon path executable file without extension + ".png" 17 | 18 | var scrollContainer = newLayoutContainer(Layout_Vertical) 19 | window.add(scrollContainer) 20 | 21 | scrollContainer.widthMode = WidthMode_Expand 22 | scrollContainer.heightMode = HeightMode_Expand 23 | 24 | var control1 = newControl() 25 | scrollContainer.add(control1) 26 | 27 | control1.widthMode = WidthMode_Expand 28 | control1.heightMode = HeightMode_Static 29 | 30 | #control1.height = 700 31 | #control1.width = 600 32 | 33 | #control1.height = pageHeight 34 | 35 | var initialized = false 36 | 37 | 38 | control1.onDraw = proc (event: DrawEvent) = 39 | if not initialized: 40 | control1.canvas.textColor = rgb(0, 0, 0) 41 | control1.canvas.fontSize = 20 42 | control1.canvas.fontFamily = "Arial" 43 | initialized = true 44 | 45 | render(event.control.canvas, root, scrollContainer.yScrollPos()) 46 | if control1.height != pageHeight: 47 | echo "fix height" 48 | control1.height = pageHeight 49 | 50 | window.show() 51 | 52 | app.run() 53 | # "app.quit()". 54 | -------------------------------------------------------------------------------- /nsw.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | # (application) 3 | 4 | version = "0.1.0" 5 | author = "Jason Livesay" 6 | description = "p2p rst/wasm browser (WIP)" 7 | license = "MIT" 8 | 9 | # Deps 10 | 11 | requires "nim >= 0.20.0" 12 | requires "nigui" 13 | 14 | -------------------------------------------------------------------------------- /test.rst: -------------------------------------------------------------------------------- 1 | This is a Heading 2 | ================= 3 | 4 | Hello there. The quick brown fox jumped over the fence. 5 | Here is some more text for you bro. And some more. 6 | 7 | This is the second paragraph. 8 | 9 | 10 | --------------------------------------------------------------------------------