diff --git a/demos/sfwl_doc_merger/code_remaining_template.md.html b/demos/sfwl_doc_merger/code_remaining_template.md.html
new file mode 100644
index 0000000..53bf802
--- /dev/null
+++ b/demos/sfwl_doc_merger/code_remaining_template.md.html
@@ -0,0 +1,8 @@
+
+$NAME$
+----------------------------------------------------------------------------------------
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
+$CODE$
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
diff --git a/demos/sfwl_doc_merger/code_template.md.html b/demos/sfwl_doc_merger/code_template.md.html
new file mode 100644
index 0000000..78141b4
--- /dev/null
+++ b/demos/sfwl_doc_merger/code_template.md.html
@@ -0,0 +1,4 @@
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
+$CODE$
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/demos/sfwl_doc_merger/compilation_no_renderer.md.html b/demos/sfwl_doc_merger/compilation_no_renderer.md.html
new file mode 100644
index 0000000..955e315
--- /dev/null
+++ b/demos/sfwl_doc_merger/compilation_no_renderer.md.html
@@ -0,0 +1,53 @@
+
+You have 2 files: `sfw.h` and `sfw.cpp` or `sfwl.h` and `sfwl.cpp` depending on your choice.
+
+Note: You might need to set c++14 level compatibility depending on your compiler. While the codebase is somwhere between
+c++89 and c++11, threads use classes from the std namespace that were added in c++14. Nowadays these are usually available
+without any special setting, but if your compiler is older (or set differently) you might need to add something like:
+`-std=c++14` to your compile commands.
+
+## IDE Setup
+
+If you use an ide, just add these files to your project (so the .cpp file gets compiled), and you are done.
+
+## Manual setup
+
+### g++ / mingw
+
+If you are using a compiler directly, then just add `sfw.cpp` or `sfwl.cpp` to the list of files that you are compiling:
+
+
+```
+g++ -g sfw.cpp main.cpp -o prog
+```
+
+Note: -g means add debug information to the executable.
+
+If you are creating object files:
+
+```
+g++ -g -c sfw.cpp -o sfw.o
+g++ -g -c main.cpp -o main.o
+
+g++ -g sfw.o main.o -o prog
+```
+
+### MSVC
+
+If you are using a compiler directly, then just add `sfw.cpp` or `sfwl.cpp` to the list of files that you are compiling:
+
+```
+cl /Zi /EHsc /Feprog-vc.exe sfw.cpp main.cpp
+```
+
+Note: /Zi means add debug information to the executable.
+
+If you are creating object files:
+
+
+```
+cl /EHsc /Zi /c sfw.cpp /Fo:sfw.obj
+cl /EHsc /Zi /c main.cpp /Fo:main.obj
+
+cl /Zi /EHsc /Feprog-vc.exe sfw.obj main.obj
+```
diff --git a/demos/sfwl_doc_merger/compile_linux.sh b/demos/sfwl_doc_merger/compile_linux.sh
new file mode 100755
index 0000000..c3bf6c6
--- /dev/null
+++ b/demos/sfwl_doc_merger/compile_linux.sh
@@ -0,0 +1,11 @@
+
+cp -u ../tools/merger/out/sfwl_core/sfwl.h sfwl.h
+cp -u .../tools/merger/out/sfwl_core/sfwl.cpp sfwl.cpp
+
+ccache g++ -Wall -g -c sfwl.cpp -o sfwl.o
+ccache g++ -Wall -g -c main.cpp -o main.o
+
+#-static-libgcc -static-libstdc++
+
+ccache g++ -Wall -lpthread -static-libgcc -static-libstdc++ -g sfwl.o main.o -o game
+
diff --git a/demos/sfwl_doc_merger/index_remaining_template.md.html b/demos/sfwl_doc_merger/index_remaining_template.md.html
new file mode 100644
index 0000000..8a7da2e
--- /dev/null
+++ b/demos/sfwl_doc_merger/index_remaining_template.md.html
@@ -0,0 +1,162 @@
+
+ **Sample API Doc**
+ 1.1 Release
+
+
+This is the markdeep generic "Company API" template. Replace
+`company-logo-512.png` with your organization's logo and adjust the
+`company-api.css` styling to match the desired colors.
+
+See `CA::OpenHandle()` for an example of an auto-generated API link.
+The rest of the information on this page is bogus placeholder to show the formatting.
+
+
+API Modules
+====================================================================================
+
+The CA library is a layer on top of the resource manager and parsing libraries that
+provides utilities for streaming processing and client-side process construction.
+
+
+ENUMS
+====================================================================================
+
+$ENUMS$
+
+STRUCTS
+====================================================================================
+
+$STRUCTS$
+
+CLASSES
+====================================================================================
+
+$CLASSES$
+
+Modules
+----------------------------------------------------------------------------------------
+
+The CA library contains the APIs for applications to allocate and exchange resources.
+
+
+!!! Attention Attention
+ Always read all of the documentation.
+
+[CA APIs](#CA)
+: List of APIs to manage external processes.
+
+CA Buffer List APIs
+: Methods to process buffer resources separate from computation.
+
+CA Data Structures
+: Specifies the data structures used for complex cases.
+
+
+## Handles
+
+`CA::OpenHandle(const std::string&)`
+: Open a reference handle.
+
+`CA::ReadMetaData(const Handle&)`
+: Metadata about the event.
+
+`CA::CloseHandle(const Handle&)`
+: Free all handle resources, recursively.
+
+
+CA
+====================================================================================
+
+!!! Warning Warning
+ The API content represents the set of APIs you can use directly. Some APIs
+ are not documented and we advise you do not use them directly. Using undocumented
+ APIs can lead to incompatibility when upgrading to later releases.
+
+
+These examples assume that your directory structure is:
+
+**********************************************************
+*
+* 📂 ca1
+* |
+* +-- 📄 bar.txt
+* |
+* +-- 📂 foo
+* | |
+* | â‹®
+* |
+* +-- 📂 xsource
+* | |
+* | +-- 📂 data
+* | | |
+* | | +-- 📄 manifest.json
+* | | |
+* | â‹® â‹®
+* |
+* â‹®
+**********************************************************
+[Directory structure.]
+
+
+Table
+------------------------------------------------------------------
+
+
+Screen | Factor | Used | Efficiency
+----------:|------------:|----------:|---------:
+1366x768 | 3x | 1152x672 | 74%
+1440x900 | 3x | 1152x672 | 60%
+*1600x900* | *4x* | 1536x896 | *96%*
+1680x1050 | 4x | 1536x896 | 78%
+1920x1080 | 4x | 1536x896 | 66%
+*1920x1200*| *5x* | 1920x1120 | *93%*
+[A Table]
+
+
+More Info
+------------------------------------------------------------------
+
+******************************************************************
+* ^ y
+* |
+* .---------. .----|----.
+* | | | | |
+* | *-------> x | *--------> x
+* | | | | |
+* '----|----' '---------'
+* |
+* v y
+******************************************************************
+[Figure 1: Coordinate Example]
+
+
+ -----
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GLSL
+vec3 sphNormal(in vec3 pos, in vec4 sph) {
+ return normalize(pos-sph.xyz);
+}
+
+vec3 camera(vec2 U, vec2 r, vec3 ro, vec3 la, float fl) {
+ vec2 uv = (U - r*.5)/r.y;
+ vec3 fwd = normalize(la-ro),
+ rgt = normalize(vec3(fwd.z, 0., -fwd.x));
+ return normalize(fwd + fl*uv.x*rgt + fl*uv.y*cross(fwd, rgt));
+}
+
+float vMap(vec3 p) {
+ float hit = 0.0;
+ vec2 h2 = hash22(floor(p.xz*0.25));
+ if (p.z > 0.0) {
+ if (p.y < -10.0 && h2.x > 0.5) hit=1.0;
+ if (p.y > 10.0 && h2.y > 0.5) hit=1.0;
+ }
+ return hit;
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+[Code listing]
+
+
+
+
+
diff --git a/demos/sfwl_doc_merger/licenses_renderer.md.html b/demos/sfwl_doc_merger/licenses_renderer.md.html
new file mode 100644
index 0000000..644b193
--- /dev/null
+++ b/demos/sfwl_doc_merger/licenses_renderer.md.html
@@ -0,0 +1,148 @@
+
+RENDER CORE
+
+
+GLAD
+
+ This library uses the glad OpenGL Loader
+
+ https://glad.dav1d.de/
+
+
+
+
+GLFW
+
+ https://github.com/glfw/glfw
+
+ GLFW 3.3.7 - www.glfw.org
+ A library for OpenGL, window and input
+
+ Copyright (c) 2002-2006 Marcus Geelnard
+
+ Copyright (c) 2006-2019 Camilla Löwy
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would
+ be appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not
+ be misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+
+
+
+
+STB
+
+ STB Single Header Libraries
+
+ https://github.com/nothings/stb
+
+ 3rd_stb_image_write.h
+ 3rd_stb_image.h
+ 3rd_stb_truetype.h
+
+ This software is available under 2 licenses -- choose whichever you prefer.
+
+ ALTERNATIVE A - MIT License
+
+ Copyright (c) 2017 Sean Barrett
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
+ this software and associated documentation files (the "Software"), to deal in
+ the Software without restriction, including without limitation the rights to
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is furnished to do
+ so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+
+ ALTERNATIVE B - Public Domain (www.unlicense.org)
+
+ This is free and unencumbered software released into the public domain.
+ Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+ software, either in source code form or as a compiled binary, for any purpose,
+ commercial or non-commercial, and by any means.
+ In jurisdictions that recognize copyright laws, the author or authors of this
+ software dedicate any and all copyright interest in the software to the public
+ domain. We make this dedication for the benefit of the public at large and to
+ the detriment of our heirs and successors. We intend this dedication to be an
+ overt act of relinquishment in perpetuity of all present and future rights to
+ this software under copyright law.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+
+
+Font
+
+ Font
+
+ bm-mini.zip (public domain font)
+
+ http://bitmapmania.m78.com
+
+ cooz@m78.com
+
+
+
+
+Font Data Tables
+
+ Font Data Tables
+
+ The data tables are coming from Dear Imgui.
+ Re-licensed under permission as MIT-0.
+
+ Original License:
+
+ https://github.com/ocornut/imgui
+
+ The MIT License (MIT)
+
+ Copyright (c) 2014-2024 Omar Cornut
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+
diff --git a/demos/sfwl_doc_merger/main.cpp b/demos/sfwl_doc_merger/main.cpp
new file mode 100644
index 0000000..c549b69
--- /dev/null
+++ b/demos/sfwl_doc_merger/main.cpp
@@ -0,0 +1,611 @@
+
+#include "sfwl.h"
+
+void print_list(const List &list) {
+ for (const List::Element *E = list.front(); E; E = E->next()) {
+ ERR_PRINT(E->get());
+ }
+}
+
+void print_class_index_keys(const HashMap &class_index) {
+ for (const HashMap::Element *E = class_index.front(); E; E = E->next) {
+ ERR_PRINT(E->key());
+ }
+}
+
+String get_structure_name(const String &data) {
+ String fl = data.get_slicec('{', 0);
+ String l = fl.get_slicec('\n', fl.get_slice_count("\n") - 1);
+
+ l = l.get_slicec(':', 0);
+ l = l.replace("struct", "").replace("class", "").replace("enum", "").replace("union", "").strip_edges();
+
+ return l;
+}
+
+String get_structure_parents(const String &data) {
+ String fl = data.get_slicec('{', 0);
+ String l = fl.get_slicec('\n', fl.get_slice_count("\n") - 1);
+
+ if (!l.contains(":")) {
+ return String();
+ }
+
+ l = l.get_slicec(':', 1);
+
+ //l = l.replace("public", "").replace("protected", "").replace("private", "");
+ l = l.strip_edges();
+
+ return l;
+}
+
+bool is_structure_template_specialization_or_parent_is_template(const String &data) {
+ String fl = data.get_slicec('\n', 0);
+
+ if (fl.contains("<")) {
+ return true;
+ }
+
+ return get_structure_parents(data).contains("<");
+}
+
+String generate_section_class_list(const List &list, const String &cls_prefix, const HashSet &used_keywords) {
+ String code_template = FileAccess::get_file_as_string("code_remaining_template.md.html");
+ String d;
+
+ for (const List::Element *E = list.front(); E; E = E->next()) {
+ String c = E->get();
+
+ String sname = get_structure_name(c);
+
+ if (sname.empty()) {
+ //ERR_PRINT(sname);
+ continue;
+ }
+
+ if (used_keywords.has(cls_prefix + sname)) {
+ continue;
+ }
+
+ d += code_template.replace("$CODE$", c.xml_escape(true)).replace("$NAME$", sname);
+ }
+
+ return d;
+}
+
+void generate_class_index(const List &list, const String &cls_prefix, HashMap *cls_index) {
+ ERR_FAIL_COND(!cls_index);
+
+ for (const List::Element *E = list.front(); E; E = E->next()) {
+ String c = E->get();
+
+ String sname = get_structure_name(c);
+
+ if (sname.empty()) {
+ //ERR_PRINT(sname);
+ continue;
+ }
+
+ (*cls_index)[cls_prefix + sname] = c;
+ }
+}
+
+List get_template_keywords(const String &tmpl) {
+ List ret;
+
+ //awful, but oh well
+ Vector sp = tmpl.split("|||");
+
+ ERR_FAIL_COND_V_MSG(sp.size() % 2 == 0, ret, "Template has unterminated keywords!");
+
+ for (int i = 1; i < sp.size(); i += 2) {
+ ret.push_back(sp[i]);
+ }
+
+ return ret;
+}
+
+List process_classes_and_structs(const List &list) {
+ List ret;
+
+ for (const List::Element *E = list.front(); E; E = E->next()) {
+ String s = E->get();
+
+ s = s.replace(" _FORCE_INLINE_ ", " ");
+ s = s.replace("_FORCE_INLINE_ ", "");
+ s = s.replace(" _NO_DISCARD_CLASS_ ", " ");
+ s = s.replace(" inline ", " ");
+ s = s.replace("inline ", "");
+
+ Vector lines = s.split("\n");
+
+ if (lines.size() == 0) {
+ continue;
+ }
+
+ if (lines.size() == 1) {
+ ret.push_back(s);
+ continue;
+ }
+
+ String stripped;
+
+ //remove method implementations
+ int current_scope_count = 0;
+ int current_target_scope_count = -1;
+ int current_parenthesis_scope_count = 0;
+ bool in_method = false;
+ bool method_signature_found = false;
+ bool in_enum = false;
+ int enum_scope_start = 0;
+ String processed_line;
+ for (int i = 0; i < lines.size(); ++i) {
+ String l = lines[i];
+
+ if (l.strip_edges(true, false).begins_with("#")) {
+ // Skip #if-s
+ // Note this will fail for multi line defines, But those currently does not appear in class definitions
+ stripped += l + "\n";
+ continue;
+ }
+
+ if (l.contains("enum ")) {
+ in_enum = true;
+ enum_scope_start = current_scope_count;
+ }
+
+ for (int j = 0; j < l.length(); ++j) {
+ CharType current_char = l[j];
+
+ if (current_char == '{') {
+ ++current_scope_count;
+ } else if (current_char == '}') {
+ --current_scope_count;
+
+ if (in_enum) {
+ if (enum_scope_start == current_scope_count) {
+ in_enum = false;
+ }
+
+ continue;
+ }
+
+ if (in_method) {
+ if (current_target_scope_count == current_scope_count) {
+ //found method end
+
+ in_method = false;
+
+ processed_line += ";";
+ }
+ }
+ }
+
+ if (in_enum) {
+ continue;
+ }
+
+ if (method_signature_found) {
+ if (current_char == '(') {
+ ++current_parenthesis_scope_count;
+ continue;
+ } else if (current_char == ')') {
+ if (current_parenthesis_scope_count > 1) {
+ --current_parenthesis_scope_count;
+ continue;
+ } else {
+ method_signature_found = false;
+ in_method = true;
+
+ processed_line += l.substr_index(0, j + 1);
+ }
+ } else {
+ continue;
+ }
+ }
+
+ if (!in_method) {
+ if (current_char == '(') {
+ current_parenthesis_scope_count = 1;
+ method_signature_found = true;
+ current_target_scope_count = current_scope_count;
+ }
+ }
+
+ if (in_method) {
+ if (current_char == ';') {
+ if (current_target_scope_count == current_scope_count) {
+ //No implementation
+ in_method = false;
+
+ processed_line += ";";
+ }
+ }
+ }
+ }
+
+ if (!in_method) {
+ if (processed_line.size() != 0) {
+ stripped += processed_line + "\n";
+ processed_line.clear();
+ } else {
+ stripped += l + "\n";
+ }
+ }
+ }
+
+ ret.push_back(stripped.strip_edges());
+ }
+
+ return ret;
+}
+
+void process_file(const String &path, const String &out_file, const String &template_file, bool write_remaining) {
+ String file_data = FileAccess::get_file_as_string(path);
+
+ LOG_MSG("Processing file: " + path);
+
+ file_data = file_data.replace("\r", "");
+
+ // Strip comments probably in probably the worst (but simplest) way possible
+ List file_lines_no_comments;
+ Vector fl = file_data.split("\n");
+
+ file_data.clear();
+
+ bool in_multiline_comment = false;
+ bool in_comment = false;
+ for (int i = 0; i < fl.size(); ++i) {
+ String l = fl[i];
+
+ if (in_comment) {
+ if (l[l.length() - 1] != '\\') {
+ //if escaped newline in // style comment, then this will be skipped
+ in_comment = false;
+ continue;
+ }
+ }
+
+ String final_line;
+ CharType last_char = '\0';
+ int comment_start_index = 0;
+ bool had_comment = false;
+
+ for (int j = 0; j < l.length(); ++j) {
+ CharType current_char = l[j];
+
+ if (!in_multiline_comment) {
+ if (last_char == '/' && current_char == '*') {
+ in_multiline_comment = true;
+ had_comment = true;
+
+ final_line += l.substr_index(comment_start_index, j - 1);
+
+ comment_start_index = j - 1;
+ } else if (last_char == '/' && current_char == '/') {
+ had_comment = true;
+ final_line += l.substr_index(comment_start_index, j - 1);
+
+ in_comment = true;
+ break;
+ }
+ } else {
+ if (last_char == '*' && current_char == '/') {
+ comment_start_index = j + 1;
+
+ had_comment = true;
+
+ in_multiline_comment = false;
+
+ //to make sure */* wont be read as both the end and beginning of a comment block on the next iteration
+ ++j;
+ current_char = '\0';
+ }
+ }
+
+ last_char = current_char;
+ }
+
+ if (in_comment) {
+ if (l[l.length() - 1] != '\\') {
+ //if escaped newline in // style comment, then this will be skipped
+ in_comment = false;
+ }
+ }
+
+ if (!had_comment && !in_multiline_comment) {
+ file_lines_no_comments.push_back(l);
+ } else {
+ if (!final_line.empty()) {
+ file_lines_no_comments.push_back(final_line);
+ }
+ }
+ }
+
+ fl.clear();
+
+ //print_list(file_lines_no_comments);
+
+ // Strip more than one empty lines in a row
+ List file_lines_no_comments_processed;
+
+ int current_empty_line_count = 0;
+ for (const List::Element *E = file_lines_no_comments.front(); E; E = E->next()) {
+ String l = E->get();
+
+ if (l.strip_edges().empty()) {
+ ++current_empty_line_count;
+
+ if (current_empty_line_count <= 1) {
+ file_lines_no_comments_processed.push_back(l);
+ }
+ } else {
+ current_empty_line_count = 0;
+ file_lines_no_comments_processed.push_back(l);
+ }
+ }
+
+ //print_list(file_lines_no_comments_processed);
+
+ file_lines_no_comments.clear();
+
+ // in global scope
+ List enums;
+ List structs;
+ List classes;
+
+ enum Types {
+ TYPE_NONE = 0,
+ TYPE_ENUM,
+ TYPE_STRUCT,
+ TYPE_CLASS,
+ };
+
+ Types current_type = TYPE_NONE;
+ int current_scope_level = 0;
+ String current_str;
+ for (const List::Element *E = file_lines_no_comments_processed.front(); E; E = E->next()) {
+ String l = E->get();
+
+ if (current_type == TYPE_NONE) {
+ if (l.contains("template")) {
+ current_str = l + "\n";
+ continue;
+ }
+
+ //Not we should be able to do this, because of how the code style is
+ if (l.contains("enum ") && l.contains("{")) {
+ if (l.contains("}")) {
+ enums.push_back(current_str.strip_edges());
+ //ERR_PRINT("TYPE_ENUM");
+ //ERR_PRINT(current_str);
+ current_str.clear();
+ continue;
+ }
+
+ // We only care about global scope stuff, so this should always work
+ current_scope_level = 1;
+ current_type = TYPE_ENUM;
+ current_str += l + "\n";
+ continue;
+ } else if (l.contains("struct ") && l.contains("{")) {
+ if (l.contains("}")) {
+ structs.push_back(current_str);
+ //ERR_PRINT("TYPE_STRUCT");
+ //ERR_PRINT(current_str);
+ current_str.clear();
+ continue;
+ }
+
+ current_scope_level = 1;
+ current_type = TYPE_STRUCT;
+ current_str += l + "\n";
+ continue;
+ } else if (l.contains("class ") && l.contains("{")) {
+ if (l.contains("}")) {
+ classes.push_back(current_str);
+ //ERR_PRINT("TYPE_CLASS");
+ //ERR_PRINT(current_str);
+ current_str.clear();
+ continue;
+ }
+
+ current_scope_level = 1;
+ current_type = TYPE_CLASS;
+ current_str += l + "\n";
+ continue;
+ }
+
+ if (!current_str.empty()) {
+ current_str.clear();
+ }
+ } else {
+ for (int j = 0; j < l.length(); ++j) {
+ CharType current_char = l[j];
+
+ if (current_char == '}') {
+ --current_scope_level;
+
+ if (current_scope_level == 0) {
+ current_str += l + "\n";
+
+ switch (current_type) {
+ case TYPE_NONE:
+ //cant happen
+ break;
+ case TYPE_ENUM:
+ enums.push_back(current_str.strip_edges());
+ //ERR_PRINT("TYPE_ENUM");
+ break;
+ case TYPE_STRUCT:
+ structs.push_back(current_str);
+ //ERR_PRINT("TYPE_STRUCT");
+ break;
+ case TYPE_CLASS:
+ classes.push_back(current_str);
+ //ERR_PRINT("TYPE_CLASS");
+ break;
+ }
+
+ //ERR_PRINT(current_str);
+
+ current_type = TYPE_NONE;
+ current_str.clear();
+ continue;
+ }
+ } else if (current_char == '{') {
+ ++current_scope_level;
+ }
+ }
+
+ current_str += l + "\n";
+ }
+ }
+
+ file_lines_no_comments_processed.clear();
+
+ structs = process_classes_and_structs(structs);
+ classes = process_classes_and_structs(classes);
+
+ //ERR_PRINT("ENUMS");
+ //print_list(enums);
+ //ERR_PRINT("STRUCTS");
+ //print_list(structs);
+ //ERR_PRINT("CLASSES");
+ //print_list(classes);
+
+ /*
+ ERR_PRINT("ENUMS");
+
+ for (const List::Element *E = enums.front(); E; E = E->next()) {
+ ERR_PRINT(get_structure_name(E->get()));
+ }
+
+ ERR_PRINT("STRUCTS");
+
+ for (const List::Element *E = structs.front(); E; E = E->next()) {
+ ERR_PRINT(get_structure_name(E->get()));
+ ERR_PRINT(get_structure_parents(E->get()));
+ ERR_PRINT("=====");
+ if (is_structure_template_specialization_or_parent_is_template(E->get())) {
+ ERR_PRINT("!!!!!!");
+ }
+ }
+ ERR_PRINT("CLASSES");
+
+ for (const List::Element *E = classes.front(); E; E = E->next()) {
+ ERR_PRINT(get_structure_name(E->get()));
+ ERR_PRINT(get_structure_parents(E->get()));
+ ERR_PRINT("=====");
+ if (is_structure_template_specialization_or_parent_is_template(E->get())) {
+ ERR_PRINT("!!!!!!");
+ }
+ }*/
+
+ //ERR_PRINT("COUNT");
+ //ERR_PRINT(itos(enums.size()));
+ //ERR_PRINT(itos(structs.size()));
+ //ERR_PRINT(itos(classes.size()));
+
+ HashMap class_index;
+
+ generate_class_index(enums, "ENUM_", &class_index);
+ generate_class_index(structs, "STRUCT_", &class_index);
+ generate_class_index(classes, "CLASS_", &class_index);
+
+ //print_class_index_keys(class_index);
+
+ String index_template = FileAccess::get_file_as_string(template_file);
+ String code_template = FileAccess::get_file_as_string("code_template.md.html");
+ List index_template_keywords = get_template_keywords(index_template);
+ HashSet used_keywords;
+
+ //print_list(index_template_keywords);
+
+ String index_str = index_template;
+
+ for (const List::Element *E = index_template_keywords.front(); E; E = E->next()) {
+ String c = E->get();
+
+ ERR_CONTINUE_MSG(!class_index.has(c), "!class_index.has(): " + c);
+
+ String keyword = "|||" + E->get() + "|||";
+
+ String class_str = class_index[c];
+ //String class_name = get_structure_name(class_str);
+
+ index_str = index_str.replace(keyword, code_template.replace("$CODE$", class_str.xml_escape(true)));
+ used_keywords.insert(c);
+ }
+
+ String compilation_no_renderer = FileAccess::get_file_as_string("compilation_no_renderer.md.html");
+ String compilation_renderer = FileAccess::get_file_as_string("compilation_renderer.md.html");
+ String licenses_renderer = FileAccess::get_file_as_string("licenses_renderer.md.html");
+ String markdeep_min_js = FileAccess::get_file_as_string("markdeep.min.js");
+ String markdeep_theme = FileAccess::get_file_as_string("slate.css");
+
+ index_str = index_str.replace("$FILE_Compilation_No_Renderer$", compilation_no_renderer);
+ index_str = index_str.replace("$FILE_Compilation_Renderer$", compilation_renderer);
+ index_str = index_str.replace("$LICENSES_Renderer$", licenses_renderer);
+
+ index_str = index_str.replace("$MARKDEEP_MIN_JS$", markdeep_min_js);
+ index_str = index_str.replace("$MARKDEEP_THEME$", markdeep_theme);
+
+ FileAccess::write_file("out/" + out_file, index_str);
+
+ if (write_remaining) {
+ //Generate a list from the unused classes.
+ String index_remaining_template = FileAccess::get_file_as_string("index_remaining_template.md.html");
+ String d = index_remaining_template;
+
+ d = d.replace("$ENUMS$", generate_section_class_list(enums, "ENUM_", used_keywords));
+ d = d.replace("$STRUCTS$", generate_section_class_list(structs, "STRUCT_", used_keywords));
+ d = d.replace("$CLASSES$", generate_section_class_list(classes, "CLASS_", used_keywords));
+
+ FileAccess::write_file("out/index_remaining.gen.md.html", d);
+ }
+}
+
+int main(int argc, char **argv) {
+ SFWCore::setup();
+
+ DirAccess dir;
+
+ if (!dir.dir_exists("out")) {
+ dir.make_dir("out");
+
+ dir.copy("markdeep.min.js", "out/markdeep.min.js");
+ dir.copy("slate.css", "out/slate.css");
+ }
+
+ bool write_remaining = false;
+ List args;
+
+ String out_file = "index.md.html";
+ String template_file = "index_template.md.html";
+
+ for (int i = 1; i < argc; ++i) {
+ String arg = String::utf8(argv[i]);
+
+ if (arg == "--remaining") {
+ write_remaining = true;
+ continue;
+ } else if (arg.begins_with("-o")) {
+ out_file = arg.trim_prefix("-o").strip_edges();
+ continue;
+ } else if (arg.begins_with("-t")) {
+ template_file = arg.trim_prefix("-t").strip_edges();
+ continue;
+ }
+
+ args.push_back(arg);
+ }
+
+ for (List::Element *E = args.front(); E; E = E->next()) {
+ process_file(E->get(), out_file, template_file, write_remaining);
+ }
+
+ SFWCore::cleanup();
+
+ return 0;
+}
diff --git a/demos/sfwl_doc_merger/markdeep.min.js b/demos/sfwl_doc_merger/markdeep.min.js
new file mode 100644
index 0000000..72b44d0
--- /dev/null
+++ b/demos/sfwl_doc_merger/markdeep.min.js
@@ -0,0 +1,10 @@
+/**See https://casual-effects.com/markdeep for @license and documentation.
+markdeep.min.js 1.16 (C) 2023 Morgan McGuire
+highlight.min.js 11.6.0 (C) 2022 Ivan Sagalaev https://highlightjs.org */
+!function(){"use strict";var e=String.prototype;e.rp=e.replace,e.ss=e.substring,e.endsWith||(e.endsWith=function(e,t){return(void 0===t||t>this.length)&&(t=this.length),this.ss(t-e.length,t)===e}),e.regexIndexOf=function(e,t){var n=this.ss(t||0).search(e);return n>=0?n+(t||0):n};var t="*",n=Array(6).join(t);function a(e,t,n){return"<"+e+(n?" "+n:"")+">"+t+""+e+">"}"function"!=typeof Object.assign&&Object.defineProperty(Object,"assign",{value:function(e,t){if(null==e)throw new TypeError("Cannot convert undefined or null to object");for(var n=Object(e),a=1;add{margin-top:-8px; margin-bottom:8px}.md dl>table{margin:35px 0 30px}.md code{page-break-inside:avoid;} @media print{.md .listing code{white-space:pre-wrap}}.md .endnote{font-size:13px;line-height:15px;padding-left:10px;text-indent:-10px}.md .bib{padding-left:80px;text-indent:-80px;text-align:left}.markdeepFooter{font-size:9px;text-align:right;padding-top:80px;color:#999}.md .mediumTOC{float:right;font-size:12px;line-height:15px;border-left:1px solid #CCC;padding-left:15px;margin:15px 0px 15px 25px}.md .mediumTOC .level1{font-weight:600}.md .longTOC .level1{font-weight:600;display:block;padding-top:12px;margin:0 0 -20px}.md .shortTOC{text-align:center;font-weight:bold;margin-top:15px;font-size:14px}.md .img-attrib-container .img-attrib{font-size:50%;line-height:120%;writing-mode:vertical-rl;position:absolute;bottom:0;right:0;padding:8px 4px;color:#FFF;background-color:rgba(0,0,0,.3)}.md .img-attrib-container .img-attrib a{color:#FFF;text-decoration:none}.md .admonition{position:relative;margin:1em 0;padding:.4rem 1rem;border-radius:.2rem;border-left:2.5rem solid rgba(68,138,255,.4);background-color:rgba(68,138,255,.15);}.md .admonition-title{font-weight:bold;border-bottom:solid 1px rgba(68,138,255,.4);padding-bottom:4px;margin-bottom:4px;margin-left: -1rem;padding-left:1rem;margin-right:-1rem;border-color:rgba(68,138,255,.4)}.md .admonition.tip{border-left:2.5rem solid rgba(50,255,90,.4);background-color:rgba(50,255,90,.15)}.md .admonition.tip::before{content:\"\\24d8\";font-weight:bold;font-size:"+(o?"200%;":"150%;")+'position:relative;top:3px;color:rgba(26,128,46,.8);left:-2.95rem;display:block;width:0;height:0}.md .admonition.tip>.admonition-title{border-color:rgba(50,255,90,.4)}.md .admonition.warn,.md .admonition.warning{border-left:2.5rem solid rgba(255,145,0,.4);background-color:rgba(255,145,0,.15)}.md .admonition.warn::before,.md .admonition.warning::before{content:"\\26A0";font-weight:bold;'+(o?"":"font-size:150%;")+'position:relative;top:2px;color:rgba(128,73,0,.8);left:-2.95rem;display:block;width:0;height:0}.md .admonition.warn>.admonition-title,.md .admonition.warning>.admonition-title{border-color:rgba(255,145,0,.4)}.md .admonition.error{border-left: 2.5rem solid rgba(255,23,68,.4);background-color:rgba(255,23,68,.15)}.md .admonition.error>.admonition-title{border-color:rgba(255,23,68,.4)}.md .admonition.error::before{content: "\\2612";font-family:"Arial";font-size:'+(o?"150%;":"200%;")+'position:relative;color:rgba(128,12,34,.8);top:-2px;left:-3rem;display:block;width:0;height:0}.md .admonition p:last-child{margin-bottom:0}.md li.checked,.md li.unchecked{list-style:none;overflow:visible;text-indent:-1.2em}.md li.checked:before,.md li.unchecked:before{content:"\\2611";display:block;float:left;width:1em;font-size:120%}.md li.unchecked:before{content:"\\2610"}'),c={name:"French",keyword:{table:"tableau",figure:"figure",listing:"liste",diagram:"diagramme",contents:"Table des mati\xe8res",sec:"sec",section:"section",subsection:"paragraphe",chapter:"chapitre",Monday:"lundi",Tuesday:"mardi",Wednesday:"mercredi",Thursday:"jeudi",Friday:"vendredi",Saturday:"samedi",Sunday:"dimanche",January:"Janvier",February:"F\xe9vrier",March:"Mars",April:"Avril",May:"Mai",June:"Juin",July:"Juillet",August:"Ao\xfbt",September:"Septembre",October:"Octobre",November:"Novembre",December:"D\xe9cembre",jan:"janv.",feb:"f\xe9vr.",mar:"mars",apr:"avril",may:"mai",jun:"juin",jul:"juil.",aug:"ao\xfbt",sep:"sept.",oct:"oct.",nov:"nov.",dec:"d\xe9c.","“":"« ","&rtquo;":" »"}},d={name:"Spanish",keyword:{table:"Tabla",figure:"Figura",listing:"Listado",diagram:"Diagrama",contents:"Tabla de Contenidos",sec:"sec",section:"Secci\xf3n",subsection:"Subsecci\xf3n",chapter:"Cap\xedtulo",Monday:"Lunes",Tuesday:"Martes",Wednesday:"Mi\xe9rcoles",Thursday:"Jueves",Friday:"Viernes",Saturday:"S\xe1bado",Sunday:"Domingo",January:"Enero",February:"Febrero",March:"Marzo",April:"Abril",May:"Mayo",June:"Junio",July:"Julio",August:"Agosto",September:"Septiembre",October:"Octubre",November:"Noviembre",December:"Diciembre",jan:"ene",feb:"feb",mar:"mar",apr:"abr",may:"may",jun:"jun",jul:"jul",aug:"ago",sep:"sept",oct:"oct",nov:"nov",dec:"dic","“":"« ","&rtquo;":" »"}},u={name:"Catalan",keyword:{table:"Taula",figure:"Figura",listing:"Llistat",diagram:"Diagrama",contents:"Taula de Continguts",sec:"sec",section:"Secci\xf3",subsection:"Subsecci\xf3",chapter:"Cap\xedtol",Monday:"Dilluns",Tuesday:"Dimarts",Wednesday:"Dimecres",Thursday:"Dijous",Friday:"Divendres",Saturday:"Dissabte",Sunday:"Diumenge",January:"Gener",February:"Febrer",March:"Mar\xe7",April:"Abril",May:"Maig",June:"Juny",July:"Juliol",August:"Agost",September:"Setembre",October:"Octubre",November:"Novembre",December:"Desembre",jan:"gen",feb:"feb",mar:"mar",apr:"abr",may:"mai",jun:"jun",jul:"jul",aug:"ago",sep:"set",oct:"oct",nov:"nov",dec:"des","“":"« ","&rtquo;":" »"}},g={mode:"markdeep",detectMath:!0,lang:{keyword:{}},tocStyle:"auto",tocDepth:3,hideEmptyWeekends:!0,autoLinkImages:!0,showLabels:!1,sortScheduleLists:!0,definitionStyle:"auto",linkAPIDefinitions:!1,inlineCodeLang:!1,scrollThreshold:90,captionAbove:{diagram:!1,image:!1,table:!1,listing:!1},smartQuotes:!0},m={name:"English",keyword:{}},p={en:m,ru:{name:"Russian",keyword:{table:"\u0442\u0430\u0431\u043b\u0438\u0446\u0430",figure:"\u0440\u0438\u0441\u0443\u043d\u043e\u043a",listing:"\u043b\u0438\u0441\u0442\u0438\u043d\u0433",diagram:"\u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u0430",contents:"\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435",sec:"\u0441\u0435\u043a",section:"\u0440\u0430\u0437\u0434\u0435\u043b",subsection:"\u043f\u043e\u0434\u0440\u0430\u0437\u0434\u0435\u043b",chapter:"\u0433\u043b\u0430\u0432\u0430",Monday:"\u043f\u043e\u043d\u0435\u0434\u0435\u043b\u044c\u043d\u0438\u043a",Tuesday:"\u0432\u0442\u043e\u0440\u043d\u0438\u043a",Wednesday:"\u0441\u0440\u0435\u0434\u0430",Thursday:"\u0447\u0435\u0442\u0432\u0435\u0440\u0433",Friday:"\u043f\u044f\u0442\u043d\u0438\u0446\u0430",Saturday:"\u0441\u0443\u0431\u0431\u043e\u0442\u0430",Sunday:"\u0432\u043e\u0441\u043a\u0440\u0435\u0441\u0435\u043d\u044c\u0435",January:"\u044f\u043d\u0432\u0430\u0440\u044cr",February:"\u0444\u0435\u0432\u0440\u0430\u043b\u044c",March:"\u043c\u0430\u0440\u0442",April:"\u0430\u043f\u0440\u0435\u043b\u044c",May:"\u043c\u0430\u0439",June:"\u0438\u044e\u043d\u044c",July:"\u0438\u044e\u043b\u044c",August:"\u0430\u0432\u0433\u0443\u0441\u0442",September:"\u0441\u0435\u043d\u0442\u044f\u0431\u0440\u044c",October:"\u043e\u043a\u0442\u044f\u0431\u0440\u044c",November:"\u043d\u043e\u044f\u0431\u0440\u044c",December:"\u0434\u0435\u043a\u0430\u0431\u0440\u044c",jan:"\u044f\u043d\u0432",feb:"\u0444\u0435\u0432\u0440",mar:"\u043c\u0430\u0440\u0442",apr:"\u0430\u043f\u0440",may:"\u043c\u0430\u0439",jun:"\u0438\u044e\u043d\u044c",jul:"\u0438\u044e\u043b\u044c",aug:"\u0430\u0432\u0433",sep:"\u0441\u0435\u043d\u0442",oct:"\u043e\u043a\u0442",nov:"\u043d\u043e\u044f\u0431\u0440\u044c",dec:"\u0434\u0435\u043a","“":"\xab","”":"\xbb"}},fr:c,"fr-AD":c,"fr-BE":c,"fr-CA":c,"en-CA":m,"en-VI":m,pl:{name:"Polish",keyword:{table:"tabela",figure:"ilustracja",listing:"wykaz",diagram:"diagram",contents:"Spis tre\u015bci",sec:"rozdz.",section:"rozdzia\u0142",subsection:"podrozdzia\u0142",chapter:"kapitu\u0142a",Monday:"Poniedzia\u0142ek",Tuesday:"Wtorek",Wednesday:"\u015aroda",Thursday:"Czwartek",Friday:"Pi\u0105tek",Saturday:"Sobota",Sunday:"Niedziela",January:"Stycze\u0144",February:"Luty",March:"Marzec",April:"Kwiecie\u0144",May:"Maj",June:"Czerwiec",July:"Lipiec",August:"Sierpie\u0144",September:"Wrzesie\u0144",October:"Pa\u017adziernik",November:"Listopad",December:"Grudzie\u0144",jan:"sty",feb:"lut",mar:"mar",apr:"kwi",may:"maj",jun:"cze",jul:"lip",aug:"sie",sep:"wrz",oct:"pa\u017a",nov:"lis",dec:"gru","“":"„","”":"”"}},bg:{name:"Bulgarian",keyword:{table:"\u0442\u0430\u0431\u043b\u0438\u0446\u0430",figure:"\u0444\u0438\u0433\u0443\u0440\u0430",listing:"\u0441\u043f\u0438\u0441\u044a\u043a",diagram:"\u0434\u0438\u0430\u0433\u0440\u0430\u043c\u0430",contents:"c\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435",sec:"\u0441\u0435\u043a",section:"\u0440\u0430\u0437\u0434\u0435\u043b",subsection:"\u043f\u043e\u0434\u0440\u0430\u0437\u0434\u0435\u043b",chapter:"\u0433\u043b\u0430\u0432\u0430",Monday:"\u043f\u043e\u043d\u0435\u0434\u0435\u043b\u043d\u0438\u043a",Tuesday:"\u0432\u0442\u043e\u0440\u043d\u0438\u043a",Wednesday:"\u0441\u0440\u044f\u0434\u0430",Thursday:"\u0447\u0435\u0442\u0432\u044a\u0440\u0442\u044a\u043a",Friday:"\u043f\u0435\u0442\u044a\u043a",Saturday:"\u0441\u044a\u0431\u043e\u0442\u0430",Sunday:"\u043d\u0435\u0434\u0435\u043b\u044f",January:"\u044f\u043d\u0443\u0430\u0440\u0438",February:"\u0444\u0435\u0432\u0440\u0443\u0430\u0440\u0438",March:"\u043c\u0430\u0440\u0442",April:"\u0430\u043f\u0440\u0438\u043b",May:"\u043c\u0430\u0439",June:"\u044e\u043d\u0438",July:"\u044e\u043b\u0438",August:"\u0430\u0432\u0433\u0443\u0441\u0442",September:"\u0441\u0435\u043f\u0442\u0435\u043c\u0432\u0440\u0438",October:"\u043e\u043a\u0442\u043e\u043c\u0432\u0440\u0438",November:"\u043d\u043e\u0435\u043c\u0432\u0440\u0438",December:"\u0434\u0435\u043a\u0435\u043c\u0432\u0440\u0438",jan:"\u044f\u043d",feb:"\u0444\u0435\u0432\u0440",mar:"\u043c\u0430\u0440\u0442",apr:"\u0430\u043f\u0440",may:"\u043c\u0430\u0439",jun:"\u044e\u043d\u0438",jul:"\u044e\u043b\u0438",aug:"\u0430\u0432\u0433",sep:"\u0441\u0435\u043f\u0442",oct:"\u043e\u043a\u0442",nov:"\u043d\u043e\u0435\u043c",dec:"\u0434\u0435\u043a","“":"„","”":"”"}},de:{name:"German",keyword:{table:"Tabelle",figure:"Abbildung",listing:"Auflistung",diagram:"Diagramm",contents:"Inhaltsverzeichnis",sec:"Kap",section:"Kapitel",subsection:"Unterabschnitt",chapter:"Kapitel",Monday:"Montag",Tuesday:"Dienstag",Wednesday:"Mittwoch",Thursday:"Donnerstag",Friday:"Freitag",Saturday:"Samstag",Sunday:"Sonntag",January:"Januar",February:"Februar",March:"M\xe4rz",April:"April",May:"Mai",June:"Juni",July:"Juli",August:"August",September:"September",October:"Oktober",November:"November",December:"Dezember",jan:"Jan",feb:"Feb",mar:"M\xe4r",apr:"Apr",may:"Mai",jun:"Jun",jul:"Jul",aug:"Aug",sep:"Sep",oct:"Okt",nov:"Nov",dec:"Dez","“":"„","”":"“"}},hu:{name:"Hungarian",keyword:{table:"t\xe1bl\xe1zat",figure:"\xe1bra",listing:"lista",diagram:"diagramm",contents:"Tartalomjegyz\xe9k",sec:"fej",section:"fejezet",subsection:"alfejezet",chapter:"fejezet",Monday:"h\xe9tf\u0151",Tuesday:"kedd",Wednesday:"szerda",Thursday:"cs\xfct\xf6rt\xf6k",Friday:"p\xe9ntek",Saturday:"szombat",Sunday:"vas\xe1rnap",January:"janu\xe1r",February:"febru\xe1r",March:"m\xe1rcius",April:"\xe1prilis",May:"m\xe1jus",June:"j\xfanius",July:"j\xfalius",August:"augusztus",September:"szeptember",October:"okt\xf3ber",November:"november",December:"december",jan:"jan",feb:"febr",mar:"m\xe1rc",apr:"\xe1pr",may:"m\xe1j",jun:"j\xfan",jul:"j\xfal",aug:"aug",sep:"szept",oct:"okt",nov:"nov",dec:"dec","“":"„","”":"”"}},sv:{name:"Swedish",keyword:{table:"tabell",figure:"figur",listing:"lista",diagram:"diagram",contents:"Inneh\xe5llsf\xf6rteckning",sec:"sek",section:"sektion",subsection:"sektion",chapter:"kapitel",Monday:"m\xe5ndag",Tuesday:"tisdag",Wednesday:"onsdag",Thursday:"torsdag",Friday:"fredag",Saturday:"l\xf6rdag",Sunday:"s\xf6ndag",January:"januari",February:"februari",March:"mars",April:"april",May:"maj",June:"juni",July:"juli",August:"augusti",September:"september",October:"oktober",November:"november",December:"december",jan:"jan",feb:"feb",mar:"mar",apr:"apr",may:"maj",jun:"jun",jul:"jul",aug:"aug",sep:"sep",oct:"okt",nov:"nov",dec:"dec","“":"”","”":"”"}},pt:{name:"Portugese",keyword:{table:"tabela",figure:"figura",listing:"lista",diagram:"diagrama",contents:"conte\xfado",sec:"sec",section:"sec\xe7\xe3o",subsection:"subsec\xe7\xe3o",chapter:"cap\xedtulo",Monday:"Segunda-feira",Tuesday:"Ter\xe7a-feira",Wednesday:"Quarta-feira",Thursday:"Quinta-feira",Friday:"Sexta-feira",Saturday:"S\xe1bado",Sunday:"Domingo",January:"Janeiro",February:"Fevereiro",March:"Mar\xe7o",April:"Abril",May:"Maio",June:"Junho",July:"Julho",August:"Agosto",September:"Setembro",October:"Outubro",November:"Novembro",December:"Dezembro",jan:"jan",feb:"fev",mar:"mar",apr:"abr",may:"mai",jun:"jun",jul:"jul",aug:"ago",sep:"set",oct:"oct",nov:"nov",dec:"dez","“":"«","&rtquo;":"»"}},ja:{name:"Japanese",keyword:{table:"\u8868",figure:"\u56f3",listing:"\u4e00\u89a7",diagram:"\u56f3",contents:"\u76ee\u6b21",sec:"\u7bc0",section:"\u7bc0",subsection:"\u9805",chapter:"\u7ae0",Monday:"\u6708",Tuesday:"\u706b",Wednesday:"\u6c34",Thursday:"\u6728",Friday:"\u91d1",Saturday:"\u571f",Sunday:"\u65e5",January:"1\u6708",February:"2\u6708",March:"3\u6708",April:"4\u6708",May:"5\u6708",June:"6\u6708",July:"7\u6708",August:"8\u6708",September:"9\u6708",October:"10\u6708",November:"11\u6708",December:"12\u6708",jan:"1\u6708",feb:"2\u6708",mar:"3\u6708",apr:"4\u6708",may:"5\u6708",jun:"6\u6708",jul:"7\u6708",aug:"8\u6708",sep:"9\u6708",oct:"10\u6708",nov:"11\u6708",dec:"12\u6708","“":"\u300c","”":"\u300d"}},it:{name:"Italian",keyword:{table:"tabella",figure:"figura",listing:"lista",diagram:"diagramma",contents:"indice",sec:"sez",section:"sezione",subsection:"paragrafo",chapter:"capitolo",Monday:"luned\xec",Tuesday:"marted\xec",Wednesday:"mercoled\xec",Thursday:"gioved\xec",Friday:"venerd\xec",Saturday:"sabato",Sunday:"domenica",January:"Gennaio",February:"Febbraio",March:"Marzo",April:"Aprile",May:"Maggio",June:"Giugno",July:"Luglio",August:"Agosto",September:"Settembre",October:"Ottobre",November:"Novembre",December:"Dicembre",jan:"gen",feb:"feb",mar:"mar",apr:"apr",may:"mag",jun:"giu",jul:"lug",aug:"ago",sep:"set",oct:"ott",nov:"nov",dec:"dic","“":"“","&rtquo;":"”"}},lt:{name:"Lithuanian",keyword:{table:"lentel\u0117",figure:"paveiksl\u0117lis",listing:"s\u0105ra\u0161as",diagram:"diagrama",contents:"Turinys",sec:"sk",section:"skyrius",subsection:"poskyris",chapter:"skyrius",Monday:"pirmadienis",Tuesday:"antradienis",Wednesday:"tre\u010diadienis",Thursday:"ketvirtadienis",Friday:"penktadienis",Saturday:"\u0161e\u0161tadienis",Sunday:"sekmadienis",January:"Sausis",February:"Vasaris",March:"Kovas",April:"Balandis",May:"Gegu\u017e\u0117",June:"Bir\u017eelis",July:"Liepa",August:"Rugpj\u016btis",September:"Rugs\u0117jis",October:"Spalis",November:"Lapkritis",December:"Gruodis",jan:"saus",feb:"vas",mar:"kov",apr:"bal",may:"geg",jun:"bir\u017e",jul:"liep",aug:"rugpj",sep:"rugs",oct:"spal",nov:"lapkr",dec:"gruod","“":"„","&rtquo;":"“"}},cs:{name:"Czech",keyword:{table:"Tabulka",figure:"Obr\xe1zek",listing:"Seznam",diagram:"Diagram",contents:"Obsah",sec:"kap.",section:"kapitola",subsection:"podkapitola",chapter:"kapitola",Monday:"pond\u011bl\xed",Tuesday:"\xfater\xfd",Wednesday:"st\u0159eda",Thursday:"\u010dtvrtek",Friday:"p\xe1tek",Saturday:"sobota",Sunday:"ned\u011ble",January:"leden",February:"\xfanor",March:"b\u0159ezen",April:"duben",May:"kv\u011bten",June:"\u010derven",July:"\u010dervenec",August:"srpen",September:"z\xe1\u0159\xed",October:"\u0159\xedjen",November:"listopad",December:"prosinec",jan:"led",feb:"\xfano",mar:"b\u0159e",apr:"dub",may:"kv\u011b",jun:"\u010dvn",jul:"\u010dvc",aug:"srp",sep:"z\xe1\u0159",oct:"\u0159\xedj",nov:"lis",dec:"pro","“":"„","”":"“"}},es:d,"es-ES":d,"ca-ES":u,"es-CO":d,"es-US":d,"en-US":m,ca:u};[].slice.call(document.getElementsByTagName("meta")).forEach((function(e){var t=e.getAttribute("lang");if(t){var n=p[t];n&&(g.lang=n)}}));var b=Math.max,h=Math.min,f=Math.abs,_=Math.sign||function(e){return+e===e?0===e?e:e>0?1:-1:NaN};function y(e,t){if(window.markdeepOptions&&void 0!==window.markdeepOptions[e]){var n=window.markdeepOptions[e];return t?void 0!==(n=n[t])?n:g[e][t]:window.markdeepOptions[e]}return void 0!==g[e]?t?g[e][t]:g[e]:void console.warn('Illegal option: "'+e+'"')}function v(e,t){if(y("showLabels")){var n=" {\xa0"+e+"\xa0}";return t?a(t,n):n}return""}function x(e){return y("lang").keyword[e]||y("lang").keyword[e.toLowerCase()]||e}function E(e){return String(e).rp(/&/g,"&").rp(//g,">").rp(/"/g,""")}function w(e){return e.rp(/</g,"<").rp(/>/g,">").rp(/"/g,'"').rp(/'/g,"'").rp(/–/g,"\u2013").rp(/—/g,"---").rp(/&/g,"&")}function N(e){return e.rp(/<.*?>/g,"")}function A(e){return encodeURI(e.rp(/\s/g,"").toLowerCase())}function M(){for(var e="",t=1;t<=6;++t){e+=".md h"+t+"::before {\ncontent:";for(var n=1;n<=t;++n)e+="counter(h"+n+') "'+(n|<\/ftp:.*>|<\/[^ "\t\n>]+@[^ "\t\n>]+>/gi,"")).rp(/<(https?|ftp): (.*?)>/gi,(function(e,t,n){var a="<"+t+"://"+n.rp(/=""\s/g,"/");return'=""'===a.ss(a.length-3)&&(a=a.ss(0,a.length-3)),(a=a.rp(/"/g,""))+">"}))).rp(/";function F(e){return Array.prototype.slice.call(e)}if(!window.alreadyProcessedMarkdeep){window.alreadyProcessedMarkdeep=!0;var $=-1!==window.location.href.search(/\?.*noformat.*/i);window.markdeep=Object.freeze({format:R,formatDiagram:j,langTable:p,stylesheet:function(){return l+M()+z}});var q='
+
+
+
+
+
diff --git a/demos/sfwl_doc_merger/slate.css b/demos/sfwl_doc_merger/slate.css
new file mode 100644
index 0000000..9c769eb
--- /dev/null
+++ b/demos/sfwl_doc_merger/slate.css
@@ -0,0 +1,306 @@
+body#md {
+ font-weight: 400;
+ font-size: 16px;
+ font-family: Arial, Helvetica, "sans serif";
+ text-align: left;
+ line-height: 170%;
+}
+
+
+/* reset heading/link fonts to that of body */
+.md a,
+.md div.title, contents, .md .tocHeader,
+.md h1, .md h2, .md h3, .md h4, .md h5, .md h6,
+.md .nonumberh1, .md .nonumberh2, .md .nonumberh3, .md .nonumberh4, .md .nonumberh5, .md .nonumberh6,
+.md .shortTOC, .md .mediumTOC, .md .longTOC {
+ font-family: inherit;
+}
+
+.md .tocHeader {
+ border: none;
+ margin-top: 25px;
+ font-size: 100%;
+ font-family: inherit;
+ font-weight: 300;
+}
+
+.md .longTOC .level1 {
+ font-weight: 300;
+ margin-bottom: -30px;
+}
+
+.md div.title, .md div.subtitle, .md h1, .md h2, .md h3, .md h4, .md h5, .md h6 {
+ font-family: Arial, Helvetica, "sans serif";
+ font-weight: 300;
+}
+
+.md div.title {
+ font-size: 43px;
+ text-align: left;
+ margin-bottom: 0px;
+}
+
+.md div.subtitle {
+ text-align: left;
+ font-size: 115%;
+}
+
+.md div.afterTitles {
+ margin-top: 20px;
+ margin-left: -20px;
+ margin-right: -20px;
+ height: 0px;
+ border-bottom: 1px solid #000;
+ box-shadow: 0px 1px 2px rgba(0,0,0,0.5);
+}
+
+.md div.afterTitles + p {
+ margin-top: 30px;
+}
+
+.md h1 {
+ font-size: 175%;
+ margin-bottom: 25px;
+ margin-left:-20px;
+ margin-right:-20px;
+ padding-left:20px;
+ padding-top:25px;
+ border-bottom: none;
+ border-top:6px solid #5a5a5a;
+}
+
+.md h2 {
+ font-size: 150%;
+ border: none;
+}
+
+.md h3, .md h4, .md h5, .md h6 {
+ font-size: 120%;
+}
+
+.md code {
+ font-size: 90%;
+ background: #eee;
+ padding-left: 2px;
+ padding-right: 2px;
+}
+
+.md .longTOC {
+ font-family: 'Roboto', Arial, Helvetica, "sans serif";
+ font-weight: 300;
+}
+
+.md pre.listing {
+ font-size: 100%;
+ width: 97%; /*hack, i dont know how to align this width with every other element <@r-lyeh*/
+ margin-left: 8px;
+}
+
+.md pre.listing code {
+ font-weight: unset;
+ background: none;
+ color: unset;
+}
+
+
+/* Darkmode, wide screen: TOC on side */
+@media screen and (min-device-width: 600px) {
+ .md .longTOC {
+ display: block;
+ white-space: nowrap;
+ width: 170px;
+ border-right: 1px solid #777;
+ overflow-y:scroll;
+ font-family: inherit;
+ background: #202020;
+ position: fixed;
+ left: 0px;
+ top: 0px;
+ bottom:0px;
+ margin: 0px;
+ padding: 0px;
+ padding-left:10px;
+ }
+
+ body {
+ position: absolute;
+ left: 200px;
+ right:0px;
+ margin: 0px;
+ padding: 0px;
+ max-width: unset;
+ padding-right: 15px;
+ }
+}
+
+/* Dark mode with side TOC on screen */
+@media screen {
+ .md div.longTOC {
+ font-size: 15px;
+ }
+
+
+ .md svg.diagram {
+ stroke: #ccc;
+ fill: #ccc;
+ }
+
+ .md svg.diagram .opendot {
+ fill: #000;
+ }
+
+ .md table.table {
+ box-shadow: 0px 1px 2px rgba(0,0,0,0.5);
+ background-color: #2a2a2a;
+ }
+
+ .md table.table tr:nth-child(even) {
+ background-color: #202020;
+ }
+
+ .md table.table td, .md table.table th {
+ border: 1px solid #202020;
+ }
+
+ .md table.table th {
+ color: #000;
+ }
+
+ .md pre.listing {
+ background: #202020;
+ border: 1px solid #777;
+ box-shadow: 0px 1px 2px rgba(0,0,0,0.5);
+ }
+
+ .md code {
+ color: #fff;
+ background: unset;
+ }
+
+ .md .tocTop {
+ display: inline;
+ }
+
+ .md div.afterTitles {
+ border-bottom: 1px solid #fff;
+ }
+
+ .md div.title, .md div.subtitle, .md h1, .md h2, .md h3, .md h4, .md h5, .md h6 {
+ color: #fff;
+ }
+
+ .hljs-comment,.hljs-quote{color:#a0f0aa}.hljs-variable,.hljs-template-variable,.hljs-tag,.hljs-name,.hljs-selector-id,.hljs-selector-class,.hljs-regexp,.hljs-deletion{color:#cc6666}.hljs-number,.hljs-built_in,.hljs-builtin-name,.hljs-literal,.hljs-type,.hljs-params,.hljs-meta,.hljs-link{color:#de935f}.hljs-attribute{color:#f0c674}.hljs-string,.hljs-symbol,.hljs-bullet,.hljs-addition{color:#b5bd68}.hljs-title,.hljs-section{color:#81a2be}.hljs-keyword,.hljs-selector-tag{color:#b294bb}.hljs{display:block;overflow-x:auto;background:#1d1f21;color:#c5c8c6;padding:.5em}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold}
+ .hljs-function .hljs-title { color:#81a2be}
+
+ body {
+ color: #ccc;
+ background: #3a3a3a;
+ }
+
+ .md div.title, .md h1, .md h2, .md h3, .md h4, .md h5, .md h6, .md .longTOC a, .md .longTOC code, .md a:link, .md a:visited {
+ text-shadow: 0px 1px 2px rgba(0,0,0,0.5);
+ }
+
+ .md .admonition {
+ position: unset;
+ box-shadow: 0px 1px 2px rgba(0,0,0,0.5);
+ background: #202020;
+ border: 1px solid rgba(68,138,255,1);
+ border-left: 2.5rem solid rgba(68,138,255,1);
+ }
+
+ .md .admonition-title {
+ border-bottom: 1px solid rgba(68,138,255,1);
+ }
+
+ .md .admonition.warn, .md .admonition.warning {
+ border: 1px solid rgba(255,170,0,1);
+ border-left: 2.5rem solid rgba(255,170,0,1);
+ background: #202020;
+ }
+
+ .md .admonition.warn .admonition-title, .md .admonition.warning .admonition-title {
+ border-bottom: 1px solid rgba(68,138,255,1);
+ }
+
+ .md .admonition.tip {
+ border: 1px solid rgba(68,138,255,1);
+ border-left: 2.5rem solid rgba(68,138,255,1);
+ background: #202020;
+ }
+ .md .admonition.tip .admonition-title {
+ border-bottom: 1px solid rgba(68,138,255,1);
+ }
+
+ .md .admonition.error {
+ border: 1px solid rgba(255,23,68,1);
+ border-left: 2.5rem solid rgba(255,23,68,1);
+ background: #202020;
+ }
+
+ .md .admonition.error .admonition-title {
+ border-bottom: 1px solid rgba(255,23,68,1);
+ }
+
+ .md .longTOC a, .md .longTOC code, .md a:link, .md a:visited, .md a:link code, .md a:visited code {
+ color: #80bfff !important;
+ }
+
+}
+
+/* Cool details theme from FWK */
+/* Stylize details/summary html tags */
+/* Credits: https://dev.to/vtrpldn/show-and-hide-content-easily-with-details-and-summary-html-tags-3eif */
+
+/* The --padding variable help us control the and spacing */
+:root {
+ --padding: 4px; /*16px;*/ /*<@r-lyeh*/
+}
+details {
+ padding: 0 var(--padding);
+ box-shadow: inset 0 0 0 0px; /*4px;*/ /*<@r-lyeh*/
+ border-radius: 1px; /*4px*/ /*<@r-lyeh*/
+
+ background-color:#333; /*<@r-lyeh*/
+ margin: -15px 0 0 0; /*<@r-lyeh*/ /*fits all together */
+}
+summary {
+ padding-left: 16px; /*<@r-lyeh*/
+
+
+ text-overflow: ellipsis;
+ /* Both of the following are required for text-overflow */
+ white-space: nowrap;
+ overflow: hidden;
+}
+details[open] {
+ padding-bottom: var(--padding);
+ box-shadow: inset 0 0 0 1px; /*<@r-lyeh*/
+
+ background-color:transparent; /*<@r-lyeh*/
+}
+details > summary {
+ display: flex;
+ padding: var(--padding);
+ margin: 0 calc(var(--padding) * -1);
+ border-radius: 4px;
+ /*font-size: 24px;*/ /*<@r-lyeh*/
+ cursor: pointer;
+ justify-content: space-between;
+ list-style: none; /* Hides the default arrow */
+}
+details[open] > summary {
+ box-shadow: 0 0px; /*4px;*/ /*<@r-lyeh*/
+}
+/* Adds an (+) icon when the is closed... */
+details > summary::after {
+ content: "⇕";
+}
+/* ...and switches it when is open (-) icon */
+details[open] > summary::after {
+ content: "⇕";
+}
+/* Removes the ugly default arrow on Chrome */
+details > summary::-webkit-details-marker {
+ display: none;
+}