diff --git a/Assets/BundledAssets.Source/air.css b/Assets/BundledAssets.Source/Md2html/air.css similarity index 100% rename from Assets/BundledAssets.Source/air.css rename to Assets/BundledAssets.Source/Md2html/air.css diff --git a/Assets/BundledAssets.Source/Md2html/links.txt b/Assets/BundledAssets.Source/Md2html/links.txt new file mode 100644 index 00000000..b09608ee --- /dev/null +++ b/Assets/BundledAssets.Source/Md2html/links.txt @@ -0,0 +1 @@ +https://github.com/dbohdan/classless-css \ No newline at end of file diff --git a/Assets/BundledAssets.Source/mvp.css b/Assets/BundledAssets.Source/Md2html/mvp.css similarity index 98% rename from Assets/BundledAssets.Source/mvp.css rename to Assets/BundledAssets.Source/Md2html/mvp.css index e7d64f82..cb5bc348 100644 --- a/Assets/BundledAssets.Source/mvp.css +++ b/Assets/BundledAssets.Source/Md2html/mvp.css @@ -1,4 +1,4 @@ -/* MVP.css v1.17.2 - https://github.com/andybrewer/mvp */ +/* MVP.css v1.17.3 - https://github.com/andybrewer/mvp */ :root { --active-brightness: 0.85; @@ -170,6 +170,10 @@ nav ul li:hover ul { display: block; } +nav ul li:focus-within ul { + display: block; +} + nav ul li ul { background: var(--color-bg); border: 1px solid var(--color-bg-secondary); @@ -302,6 +306,11 @@ a { text-decoration: underline; } +a:visited, +area:visited { + color: var(--color-secondary); +} + a:hover { filter: brightness(var(--hover-brightness)); } diff --git a/Assets/BundledAssets.Source/retro.css b/Assets/BundledAssets.Source/Md2html/retro.css similarity index 100% rename from Assets/BundledAssets.Source/retro.css rename to Assets/BundledAssets.Source/Md2html/retro.css diff --git a/Assets/BundledAssets.Source/tiny-dark.css b/Assets/BundledAssets.Source/Md2html/tiny-dark.css similarity index 100% rename from Assets/BundledAssets.Source/tiny-dark.css rename to Assets/BundledAssets.Source/Md2html/tiny-dark.css diff --git a/Assets/BundledAssets.Source/tiny-light.css b/Assets/BundledAssets.Source/Md2html/tiny-light.css similarity index 100% rename from Assets/BundledAssets.Source/tiny-light.css rename to Assets/BundledAssets.Source/Md2html/tiny-light.css diff --git a/Assets/BundledAssets.Source/bookgen.css b/Assets/BundledAssets.Source/bookgen.css index 83284059..4250c82a 100644 --- a/Assets/BundledAssets.Source/bookgen.css +++ b/Assets/BundledAssets.Source/bookgen.css @@ -12,10 +12,12 @@ Version: 2026-03-04 --blue: #003e8a; --brightBlue: #2e6cba; --selection: #CACACA; - --box-info: #d1e7dd; - --box-waring: #fff3cd; - --box-error: #f8d7da; - --box-foreground: #3e3e3e; + --box-note: #0eb3d8; + --box-tip: #24c27b; + --box-important: #0eb3d8; + --box-warning: #e1ae05; + --box-caution: #b82e39; + --box-foreground: #f4f4f4; --max-width: 1200px; } @@ -26,10 +28,12 @@ body.dark-mode { --blue: #82aaff; --brightBlue: #d6acff; --selection: #5D5D5D; - --box-info: #B9E5D1; - --box-waring: #FFECB5; - --box-error: #F7BEC4; - --box-foreground: #212121; + --box-note: #064f60; + --box-tip: #105637; + --box-important: #064f60; + --box-warning: #644d02; + --box-caution: #521419; + --box-foreground: #f8f8f2; } h1, @@ -151,6 +155,17 @@ pre:hover { transform: translateY(-4px); } +kbd { + font-family: "JetBrains Mono", monospace; + border-radius: 0.25rem; + border: 1px solid var(--foreground); + box-shadow: 0 2px 0 1px var(--selection); + line-height: 1; + min-width: 0.75rem; + text-align: center; + padding: 2px 5px; +} + /*Aside nav*/ .float { position: fixed; @@ -236,46 +251,66 @@ dialog #container #dialogBase { color: var(--foreground); } -/* Boxes */ - -.info { - background-color: var(--box-info); - color: var(--box-foreground); - margin: 1rem; +/* Alerts */ +.markdown-alert { padding: 1rem; border-radius: 0.5rem; + border: 1px solid var(--foreground); + margin-bottom: 1rem; } -.info::before { - content: "ℹ️"; - display: block; +.markdown-alert .markdown-alert-title { + font-weight: bold; } -.warning { - background-color: var(--box-waring); +.markdown-alert-note { + background-color: var(--box-note); color: var(--box-foreground); - margin: 1rem; - padding: 1rem; - border-radius: 0.5rem; } -.warning::before { - content: "⚠️"; - display: block; +.markdown-alert-note .markdown-alert-title::before { + content: "📝"; + font-size: 1.6rem; +} + +.markdown-alert-important { + background-color: var(--box-important); + color: var(--box-foreground); } +.markdown-alert-important .markdown-alert-title::before { + content: "🚩"; + font-size: 1.6rem; +} -.error { - background-color: var(--box-error); +.markdown-alert-tip { + background-color: var(--box-tip); color: var(--box-foreground); - margin: 1rem; - padding: 1rem; - border-radius: 0.5rem; } -.error::before { - content: "🛑"; - display: block; +.markdown-alert-tip .markdown-alert-title::before { + content: "💡"; + font-size: 1.6rem; +} + +.markdown-alert-warning { + background-color: var(--box-warning); + color: var(--box-foreground); +} + +.markdown-alert-warning .markdown-alert-title::before { + content: "⚠️"; + font-size: 1.6rem; +} + +.markdown-alert-caution { + background-color: var(--box-caution); + color: var(--box-foreground); +} + +.markdown-alert-caution .markdown-alert-title::before { + content: "❌"; + font-size: 1.6rem; } @media print { diff --git a/Assets/BundledAssets.Source/bookgen.epub.css b/Assets/BundledAssets.Source/bookgen.epub.css index 124841fa..406b2b57 100644 --- a/Assets/BundledAssets.Source/bookgen.epub.css +++ b/Assets/BundledAssets.Source/bookgen.epub.css @@ -4,9 +4,18 @@ Version: 2026-03-04 */ :root { + --background: #f4f4f4; + --foreground: #3e3e3e; --red: #970b16; --blue: #003e8a; --brightBlue: #2e6cba; + --selection: #CACACA; + --box-note: #0eb3d8; + --box-tip: #24c27b; + --box-important: #0eb3d8; + --box-warning: #e1ae05; + --box-caution: #b82e39; + --box-foreground: #f4f4f4; --max-width: 1200px; } @@ -193,48 +202,81 @@ figcaption { font-style: italic; } +kbd { + font-family: "JetBrains Mono", monospace; + border-radius: 0.25rem; + border: 1px solid var(--foreground); + box-shadow: 0 2px 0 1px var(--selection); + line-height: 1; + min-width: 0.75rem; + text-align: center; + padding: 2px 5px; +} + /* Boxes */ -.info { - background-color: var(--box-info); - color: var(--box-foreground); - margin: 1rem; +.markdown-alert { padding: 1rem; border-radius: 0.5rem; + border: 1px solid var(--foreground); + margin-bottom: 1rem; } -.info::before { - content: "ℹ️"; - display: block; +.markdown-alert .markdown-alert-title { + font-weight: bold; } -.warning { - background-color: var(--box-waring); +.markdown-alert-note { + background-color: var(--box-note); color: var(--box-foreground); - margin: 1rem; - padding: 1rem; - border-radius: 0.5rem; } -.warning::before { - content: "⚠️"; - display: block; +.markdown-alert-note .markdown-alert-title::before { + content: "📝"; + font-size: 1.6rem; +} + +.markdown-alert-important { + background-color: var(--box-important); + color: var(--box-foreground); +} + +.markdown-alert-important .markdown-alert-title::before { + content: "🚩"; + font-size: 1.6rem; +} + +.markdown-alert-tip { + background-color: var(--box-tip); + color: var(--box-foreground); } +.markdown-alert-tip .markdown-alert-title::before { + content: "💡"; + font-size: 1.6rem; +} -.error { - background-color: var(--box-error); +.markdown-alert-warning { + background-color: var(--box-warning); color: var(--box-foreground); - margin: 1rem; - padding: 1rem; - border-radius: 0.5rem; } -.error::before { - content: "🛑"; - display: block; +.markdown-alert-warning .markdown-alert-title::before { + content: "⚠️"; + font-size: 1.6rem; +} + +.markdown-alert-caution { + background-color: var(--box-caution); + color: var(--box-foreground); } +.markdown-alert-caution .markdown-alert-title::before { + content: "❌"; + font-size: 1.6rem; +} + + /* PrismJS 1.30.0 https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+abap+abnf+actionscript+ada+agda+al+antlr4+apacheconf+apex+apl+applescript+aql+arduino+arff+armasm+arturo+asciidoc+aspnet+asm6502+asmatmel+autohotkey+autoit+avisynth+avro-idl+awk+bash+basic+batch+bbcode+bbj+bicep+birb+bison+bnf+bqn+brainfuck+brightscript+bro+bsl+c+csharp+cpp+cfscript+chaiscript+cil+cilkc+cilkcpp+clojure+cmake+cobol+coffeescript+concurnas+csp+cooklang+coq+crystal+css-extras+csv+cue+cypher+d+dart+dataweave+dax+dhall+diff+django+dns-zone-file+docker+dot+ebnf+editorconfig+eiffel+ejs+elixir+elm+etlua+erb+erlang+excel-formula+fsharp+factor+false+firestore-security-rules+flow+fortran+ftl+gml+gap+gcode+gdscript+gedcom+gettext+gherkin+git+glsl+gn+linker-script+go+go-module+gradle+graphql+groovy+haml+handlebars+haskell+haxe+hcl+hlsl+hoon+http+hpkp+hsts+ichigojam+icon+icu-message-format+idris+ignore+inform7+ini+io+j+java+javadoc+javadoclike+javastacktrace+jexl+jolie+jq+jsdoc+js-extras+json+json5+jsonp+jsstacktrace+js-templates+julia+keepalived+keyman+kotlin+kumir+kusto+latex+latte+less+lilypond+liquid+lisp+livescript+llvm+log+lolcode+lua+magma+makefile+markdown+markup-templating+mata+matlab+maxscript+mel+mermaid+metafont+mizar+mongodb+monkey+moonscript+n1ql+n4js+nand2tetris-hdl+naniscript+nasm+neon+nevod+nginx+nim+nix+nsis+objectivec+ocaml+odin+opencl+openqasm+oz+parigp+parser+pascal+pascaligo+psl+pcaxis+peoplecode+perl+php+phpdoc+php-extras+plant-uml+plsql+powerquery+powershell+processing+prolog+promql+properties+protobuf+pug+puppet+pure+purebasic+purescript+python+qsharp+q+qml+qore+r+racket+cshtml+jsx+tsx+reason+regex+rego+renpy+rescript+rest+rip+roboconf+robotframework+ruby+rust+sas+sass+scss+scala+scheme+shell-session+smali+smalltalk+smarty+sml+solidity+solution-file+soy+sparql+splunk-spl+sqf+sql+squirrel+stan+stata+iecst+stylus+supercollider+swift+systemd+t4-templating+t4-cs+t4-vb+tap+tcl+tt2+textile+toml+tremor+turtle+twig+typescript+typoscript+unrealscript+uorazor+uri+v+vala+vbnet+velocity+verilog+vhdl+vim+visual-basic+warpscript+wasm+web-idl+wgsl+wiki+wolfram+wren+xeora+xml-doc+xojo+xquery+yaml+yang+zig */ code[class*=language-], diff --git a/Assets/BundledAssets.Source/test.html b/Assets/BundledAssets.Source/test.html index c018ad35..87bc5187 100644 --- a/Assets/BundledAssets.Source/test.html +++ b/Assets/BundledAssets.Source/test.html @@ -122,9 +122,32 @@
H6
-
Info box
-
Warning box
-
Error box
+
+

Note

+

Your alert content here.

+
+ +
+

Tip

+

Your alert content here.

+
+ +
+

Important

+

Your alert content here.

+
+ +
+

Warning

+

Your alert content here.

+
+ +
+

Caution

+

Your alert content here.

+
+ +Ctrl + C to copy diff --git a/Assets/BundledAssets/Epub.html b/Assets/BundledAssets/Epub.html index d0438c80..d0dad12f 100644 --- a/Assets/BundledAssets/Epub.html +++ b/Assets/BundledAssets/Epub.html @@ -10,7 +10,7 @@ -{{content}} +{{Content}} \ No newline at end of file diff --git a/Assets/BundledAssets/Md2Html.html b/Assets/BundledAssets/Md2Html.html index a5523a41..97d8432f 100644 --- a/Assets/BundledAssets/Md2Html.html +++ b/Assets/BundledAssets/Md2Html.html @@ -7,7 +7,7 @@ {{Title}} diff --git a/Assets/BundledAssets/air.template b/Assets/BundledAssets/Md2HtmlAir.html similarity index 100% rename from Assets/BundledAssets/air.template rename to Assets/BundledAssets/Md2HtmlAir.html diff --git a/Assets/BundledAssets/Md2HtmlMvp.html b/Assets/BundledAssets/Md2HtmlMvp.html new file mode 100644 index 00000000..30f7397f --- /dev/null +++ b/Assets/BundledAssets/Md2HtmlMvp.html @@ -0,0 +1,19 @@ + + + + + + + + {{Title}} + + + + +
+ {{Content}} +
+ + \ No newline at end of file diff --git a/Assets/BundledAssets/retro.template b/Assets/BundledAssets/Md2HtmlRetro.html similarity index 100% rename from Assets/BundledAssets/retro.template rename to Assets/BundledAssets/Md2HtmlRetro.html diff --git a/Assets/BundledAssets/tiny-dark.template b/Assets/BundledAssets/Md2HtmlTinyDark.html similarity index 100% rename from Assets/BundledAssets/tiny-dark.template rename to Assets/BundledAssets/Md2HtmlTinyDark.html diff --git a/Assets/BundledAssets/tiny-light.template b/Assets/BundledAssets/Md2HtmlTinyLight.html similarity index 100% rename from Assets/BundledAssets/tiny-light.template rename to Assets/BundledAssets/Md2HtmlTinyLight.html diff --git a/Assets/BundledAssets/Print.html b/Assets/BundledAssets/Print.html index 427eeda0..9a6071ce 100644 --- a/Assets/BundledAssets/Print.html +++ b/Assets/BundledAssets/Print.html @@ -7,7 +7,7 @@ {{Title}} diff --git a/Assets/BundledAssets/Static.html b/Assets/BundledAssets/Static.html index db4d6bca..e94180f8 100644 --- a/Assets/BundledAssets/Static.html +++ b/Assets/BundledAssets/Static.html @@ -8,7 +8,7 @@ {{Title}} diff --git a/Assets/BundledAssets/bookgen.epub.min.css b/Assets/BundledAssets/bookgen.epub.min.css index 9f2971af..7d22be8d 100644 --- a/Assets/BundledAssets/bookgen.epub.min.css +++ b/Assets/BundledAssets/bookgen.epub.min.css @@ -1 +1 @@ -:root{--red:#970b16;--blue:#003e8a;--brightBlue:#2e6cba;--max-width:1200px}@font-face{font-family:"JetBrains Mono";src:url(JetBrainsMono-Regular.ttf);font-weight:400;font-style:normal}@font-face{font-family:Nunito;src:url(Nunito-Bold.ttf);font-optical-sizing:auto;font-weight:700;font-style:normal}@font-face{font-family:"Open Sans";src:url(OpenSans-Regular.ttf);font-optical-sizing:auto;font-weight:400;font-style:normal;font-variation-settings:"wdth" 100}body{font-family:"Open Sans",sans-serif;font-size:1.1em;line-height:1.6;margin:0;padding:1em;color:#1a1a1a;background:0 0;widows:2;orphans:2}p{margin:1em 0;text-align:justify;text-indent:1.5em}h1,h2,h3,h4,h5,h6{font-family:Nunito,sans-serif;font-weight:700;color:#000;margin:1.5em 0 .5em;page-break-after:avoid;page-break-inside:avoid}h1{font-size:1.8em}h2{font-size:1.5em}h3{font-size:1.3em}h4{font-size:1.2em}h5{font-size:1.1em}h6{font-size:1em}blockquote{margin:1em 2em;font-style:italic;color:#555}em{font-style:italic}strong{font-weight:700}a{color:#0645ad;text-decoration:underline}ol,ul{margin:1em 0 1em 2em;padding:0}li{margin-bottom:.5em}img{max-width:100%;height:auto;display:block;margin:1em auto}p code{color:var(--red);font-style:italic}pre{transition:box-shadow .3s ease,transform .3s ease;font-family:"JetBrains Mono",monospace;font-optical-sizing:auto;font-weight:400;font-style:normal;font-variant-ligatures:contextual;font-feature-settings:"liga","calt"}pre code{font-family:"JetBrains Mono",monospace;font-optical-sizing:auto;font-weight:400;font-style:normal;font-variant-ligatures:contextual;font-feature-settings:"liga","calt"}hr{border:0;height:1px;background:#ccc;margin:2em 0}blockquote,h1,h2,h3,img,pre,table{page-break-inside:avoid}table{width:100%;border-collapse:collapse;margin:1em 0}td,th{border:1px solid #ccc;padding:.5em;text-align:left}figure{margin:0}figcaption{text-align:center;font-style:italic}.info{background-color:var(--box-info);color:var(--box-foreground);margin:1rem;padding:1rem;border-radius:.5rem}.info::before{content:"ℹ️";display:block}.warning{background-color:var(--box-waring);color:var(--box-foreground);margin:1rem;padding:1rem;border-radius:.5rem}.warning::before{content:"⚠️";display:block}.error{background-color:var(--box-error);color:var(--box-foreground);margin:1rem;padding:1rem;border-radius:.5rem}.error::before{content:"🛑";display:block}code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help} \ No newline at end of file +:root{--background:#f4f4f4;--foreground:#3e3e3e;--red:#970b16;--blue:#003e8a;--brightBlue:#2e6cba;--selection:#CACACA;--box-note:#0eb3d8;--box-tip:#24c27b;--box-important:#0eb3d8;--box-warning:#e1ae05;--box-caution:#b82e39;--box-foreground:#f4f4f4;--max-width:1200px}@font-face{font-family:"JetBrains Mono";src:url(JetBrainsMono-Regular.ttf);font-weight:400;font-style:normal}@font-face{font-family:Nunito;src:url(Nunito-Bold.ttf);font-optical-sizing:auto;font-weight:700;font-style:normal}@font-face{font-family:"Open Sans";src:url(OpenSans-Regular.ttf);font-optical-sizing:auto;font-weight:400;font-style:normal;font-variation-settings:"wdth" 100}body{font-family:"Open Sans",sans-serif;font-size:1.1em;line-height:1.6;margin:0;padding:1em;color:#1a1a1a;background:0 0;widows:2;orphans:2}p{margin:1em 0;text-align:justify;text-indent:1.5em}h1,h2,h3,h4,h5,h6{font-family:Nunito,sans-serif;font-weight:700;color:#000;margin:1.5em 0 .5em;page-break-after:avoid;page-break-inside:avoid}h1{font-size:1.8em}h2{font-size:1.5em}h3{font-size:1.3em}h4{font-size:1.2em}h5{font-size:1.1em}h6{font-size:1em}blockquote{margin:1em 2em;font-style:italic;color:#555}em{font-style:italic}strong{font-weight:700}a{color:#0645ad;text-decoration:underline}ol,ul{margin:1em 0 1em 2em;padding:0}li{margin-bottom:.5em}img{max-width:100%;height:auto;display:block;margin:1em auto}p code{color:var(--red);font-style:italic}pre{transition:box-shadow .3s ease,transform .3s ease;font-family:"JetBrains Mono",monospace;font-optical-sizing:auto;font-weight:400;font-style:normal;font-variant-ligatures:contextual;font-feature-settings:"liga","calt"}pre code{font-family:"JetBrains Mono",monospace;font-optical-sizing:auto;font-weight:400;font-style:normal;font-variant-ligatures:contextual;font-feature-settings:"liga","calt"}hr{border:0;height:1px;background:#ccc;margin:2em 0}blockquote,h1,h2,h3,img,pre,table{page-break-inside:avoid}table{width:100%;border-collapse:collapse;margin:1em 0}td,th{border:1px solid #ccc;padding:.5em;text-align:left}figure{margin:0}figcaption{text-align:center;font-style:italic}kbd{font-family:"JetBrains Mono",monospace;border-radius:.25rem;border:1px solid var(--foreground);box-shadow:0 2px 0 1px var(--selection);line-height:1;min-width:.75rem;text-align:center;padding:2px 5px}.markdown-alert{padding:1rem;border-radius:.5rem;border:1px solid var(--foreground);margin-bottom:1rem}.markdown-alert .markdown-alert-title{font-weight:700}.markdown-alert-note{background-color:var(--box-note);color:var(--box-foreground)}.markdown-alert-note .markdown-alert-title::before{content:"📝";font-size:1.6rem}.markdown-alert-important{background-color:var(--box-important);color:var(--box-foreground)}.markdown-alert-important .markdown-alert-title::before{content:"🚩";font-size:1.6rem}.markdown-alert-tip{background-color:var(--box-tip);color:var(--box-foreground)}.markdown-alert-tip .markdown-alert-title::before{content:"💡";font-size:1.6rem}.markdown-alert-warning{background-color:var(--box-warning);color:var(--box-foreground)}.markdown-alert-warning .markdown-alert-title::before{content:"⚠️";font-size:1.6rem}.markdown-alert-caution{background-color:var(--box-caution);color:var(--box-foreground)}.markdown-alert-caution .markdown-alert-title::before{content:"❌";font-size:1.6rem}code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help} \ No newline at end of file diff --git a/Assets/BundledAssets/mvp.template b/Assets/BundledAssets/mvp.template deleted file mode 100644 index 6f05cf06..00000000 --- a/Assets/BundledAssets/mvp.template +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - {{Title}} - - - - -
- {{Content}} -
- - \ No newline at end of file diff --git a/Assets/BundledAssets/prism.css b/Assets/BundledAssets/prism.css new file mode 100644 index 00000000..ba3a1f93 --- /dev/null +++ b/Assets/BundledAssets/prism.css @@ -0,0 +1,3 @@ +/* PrismJS 1.30.0 +https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+abap+abnf+actionscript+ada+agda+al+antlr4+apacheconf+apex+apl+applescript+aql+arduino+arff+armasm+arturo+asciidoc+aspnet+asm6502+asmatmel+autohotkey+autoit+avisynth+avro-idl+awk+bash+basic+batch+bbcode+bbj+bicep+birb+bison+bnf+bqn+brainfuck+brightscript+bro+bsl+c+csharp+cpp+cfscript+chaiscript+cil+cilkc+cilkcpp+clojure+cmake+cobol+coffeescript+concurnas+csp+cooklang+coq+crystal+css-extras+csv+cue+cypher+d+dart+dataweave+dax+dhall+diff+django+dns-zone-file+docker+dot+ebnf+editorconfig+eiffel+ejs+elixir+elm+etlua+erb+erlang+excel-formula+fsharp+factor+false+firestore-security-rules+flow+fortran+ftl+gml+gap+gcode+gdscript+gedcom+gettext+gherkin+git+glsl+gn+linker-script+go+go-module+gradle+graphql+groovy+haml+handlebars+haskell+haxe+hcl+hlsl+hoon+http+hpkp+hsts+ichigojam+icon+icu-message-format+idris+ignore+inform7+ini+io+j+java+javadoc+javadoclike+javastacktrace+jexl+jolie+jq+jsdoc+js-extras+json+json5+jsonp+jsstacktrace+js-templates+julia+keepalived+keyman+kotlin+kumir+kusto+latex+latte+less+lilypond+liquid+lisp+livescript+llvm+log+lolcode+lua+magma+makefile+markdown+markup-templating+mata+matlab+maxscript+mel+mermaid+metafont+mizar+mongodb+monkey+moonscript+n1ql+n4js+nand2tetris-hdl+naniscript+nasm+neon+nevod+nginx+nim+nix+nsis+objectivec+ocaml+odin+opencl+openqasm+oz+parigp+parser+pascal+pascaligo+psl+pcaxis+peoplecode+perl+php+phpdoc+php-extras+plant-uml+plsql+powerquery+powershell+processing+prolog+promql+properties+protobuf+pug+puppet+pure+purebasic+purescript+python+qsharp+q+qml+qore+r+racket+cshtml+jsx+tsx+reason+regex+rego+renpy+rescript+rest+rip+roboconf+robotframework+ruby+rust+sas+sass+scss+scala+scheme+shell-session+smali+smalltalk+smarty+sml+solidity+solution-file+soy+sparql+splunk-spl+sqf+sql+squirrel+stan+stata+iecst+stylus+supercollider+swift+systemd+t4-templating+t4-cs+t4-vb+tap+tcl+tt2+textile+toml+tremor+turtle+twig+typescript+typoscript+unrealscript+uorazor+uri+v+vala+vbnet+velocity+verilog+vhdl+vim+visual-basic+warpscript+wasm+web-idl+wgsl+wiki+wolfram+wren+xeora+xml-doc+xojo+xquery+yaml+yang+zig */ +code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help} diff --git a/BookGen.slnx b/BookGen.slnx index 620677c2..60704f35 100644 --- a/BookGen.slnx +++ b/BookGen.slnx @@ -1,11 +1,15 @@ + + + + + + - - @@ -23,9 +27,11 @@ + - + + diff --git a/BookGen.slnx.licenseheader b/BookGen.slnx.licenseheader index dd4ff349..09755acb 100644 --- a/BookGen.slnx.licenseheader +++ b/BookGen.slnx.licenseheader @@ -1,5 +1,5 @@ extensions: .cs //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Branding/icon-bookgen-large.png b/Branding/icon-bookgen-large.png new file mode 100644 index 00000000..ec9c6d48 Binary files /dev/null and b/Branding/icon-bookgen-large.png differ diff --git a/Branding/icon-bookgen.ico b/Branding/icon-bookgen.ico index 18c1f5df..8cc0f77b 100644 --- a/Branding/icon-bookgen.ico +++ b/Branding/icon-bookgen.ico @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4e60aa43a918a76f9871fb221b3cde0402b6e49de6d2a59e8ca888b0d922748 -size 15406 +oid sha256:5a5b151424b8643995f9baf96152065d8934a311e8e99a69674ca0be64154130 +size 4286 diff --git a/Branding/icon-bookgen.png b/Branding/icon-bookgen.png index cb66826e..a76dbd38 100644 --- a/Branding/icon-bookgen.png +++ b/Branding/icon-bookgen.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30ab4a8f2df19bc568f2d28f8da4526ba74a73674a9ff3618fd8fcf2a4e6e146 -size 19154 +oid sha256:a474dc34320d9116834d8c4e8f5a067b93c80a5a3b3a8b04731ba5aa8f2b3ef8 +size 68686 diff --git a/Branding/icon-bookgen.webp b/Branding/icon-bookgen.webp new file mode 100644 index 00000000..e685fa94 Binary files /dev/null and b/Branding/icon-bookgen.webp differ diff --git a/Branding/readme.md b/Branding/readme.md index 12e245e7..9df9b8df 100644 --- a/Branding/readme.md +++ b/Branding/readme.md @@ -1,3 +1,5 @@ # BookGen logos -Font: Noto Serif \ No newline at end of file +Font: Noto Serif + +Logo designed by: Mariann Kobzi diff --git a/Commands.md b/Commands.md deleted file mode 100644 index 8d5839c9..00000000 --- a/Commands.md +++ /dev/null @@ -1,858 +0,0 @@ -# Help - -BookGen - Markdown to Book tool. - -For the tool to work in the work folder there must be a bookgen.json config file. This config file can be created with the following command: - -`BookGen Newbook` - -To Get more help about a subcommand type: - -`BookGen Help [module]` - -To list available subcommands type: - -`BookGen SubCommands` - -General arguments: - -* `-wd` or `--wait-debugger` - - Waits for a debugger to be attached. Usefull for error reporting & error finding. - -* `-ad` or `--attach-debugger` - - Attaches a debugger. Usefull for error reporting & error finding. - -* `-js` or `--json-log` - - Outputs log in JSON format. Usefull for interop purposes. - -# Addfrontmatter - -Add a basic YAML frontmatter information to all markdown files located in the current folder -and it's subfolders. - -``` -BookGen Addfrontmatter [-v] [-d [directory]] -BookGen Addfrontmatter [--verbose] [--dir [directory]] -``` - -Arguments: - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues - - -# Assembly-document - -Generates a markdown file(s) from a given .NET assembly and it's XML documentation file. - -``` -BookGen Assembly-document -i -o [-d] [-n] -BookGen Assembly-document --input --output [--dry] [--namespace-pages] -``` - -Arguments: - -* `-i`, `--input`: - - Required argument. Specifies the input assembly file path. The file must be a .NET assembly. - -* `-o`, `--output`: - - Required argument. Specifies the output files path. - -* `-d`, `--dry`: - - Optional argument. If specified, the command will not write any files, but will only print the output to console. - -* `-n`, `--namespace-pages`: - - Optional argument. If specified, the command will create a separate markdown file for each namespace in the assembly. - -# BuildEpub - -Build an epub3 file from the book. - -``` -BookGen BuildEpub -o [-v] [-d [directory]] [-co [configoverlay]] -BookGen BuildEpub --output [--verbose] [--dir [directory]] [--configoverlay [configoverlay]] -``` - -Arguments: - -* `-o`, `--output`: - - Required argument. Specifies the output directory name. - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues - -* `-co`, `--configoverlay`: - - Optional argument. Specifies a json file that will be used as an overlay for the bookgen.json config file. The values in the overlay file will override the values in the config file. This can be used to specify different output settings for different builds, without having to change the main config file. - -# BuildExport - -Build a JSON file with schema for post processing of the book. - -``` -BookGen BuildExport -o [-v] [-d [directory]] [-co [configoverlay]] -BookGen BuildExport --output [--verbose] [--dir [directory]] [--configoverlay [configoverlay]] -``` - -Arguments: - -* `-o`, `--output`: - - Required argument. Specifies the output directory name. - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues - -* `-h`, `--host`: - - Optional argument. If specified, the host name set in the config file will be ignored and the host name will be set to the specified value. - -* `-co`, `--configoverlay`: - - Optional argument. Specifies a json file that will be used as an overlay for the bookgen.json config file. The values in the overlay file will override the values in the config file. This can be used to specify different output settings for different builds, without having to change the main config file. - - -# BuildFeed - -Build an RSS 2.0 and an Atom 1.0 feed from the book. - -``` -BookGen BuildFeed -o [-v] [-d [directory]] [-co [configoverlay]] -BookGen BuildFeed --output [--verbose] [--dir [directory]] [--configoverlay [configoverlay]] -``` - -Arguments: - -* `-o`, `--output`: - - Required argument. Specifies the output directory name. - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues - -* `-co`, `--configoverlay`: - - Optional argument. Specifies a json file that will be used as an overlay for the bookgen.json config file. The values in the overlay file will override the values in the config file. This can be used to specify different output settings for different builds, without having to change the main config file. - - -# BuildPrint - -Build a printable html & xhtml file from the book - -``` -BookGen BuildPrint -o [-v] [-d [directory]] [-co [configoverlay]] -BookGen BuildPrint --output [--verbose] [--dir [directory]] [--configoverlay [configoverlay]] -``` - -Arguments: - -* `-o`, `--output`: - - Required argument. Specifies the output directory name. - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues - -* `-h`, `--host`: - - Optional argument. If specified, the host name set in the config file will be ignored and the host name will be set to the specified value. - -* `-co`, `--configoverlay`: - - Optional argument. Specifies a json file that will be used as an overlay for the bookgen.json config file. The values in the overlay file will override the values in the config file. This can be used to specify different output settings for different builds, without having to change the main config file. - - -# BuildWeb - -Build a static website from the book - -``` -BookGen BuildWeb -o [-v] [-d [directory]] [-co [configoverlay]] -BookGen BuildWeb --output [--verbose] [--dir [directory]] [--configoverlay [configoverlay]] -``` - -Arguments: - -* `-o`, `--output`: - - Required argument. Specifies the output directory name. - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues - -* `-h`, `--host`: - - Optional argument. If specified, the host name set in the config file will be ignored and the host name will be set to the specified value. - -* `-co`, `--configoverlay`: - - Optional argument. Specifies a json file that will be used as an overlay for the bookgen.json config file. The values in the overlay file will override the values in the config file. This can be used to specify different output settings for different builds, without having to change the main config file. - - -# BuildWp - -Build a wordpress export file from the book. - -``` -BookGen BuildWp -o [-v] [-d [directory]] [-co [configoverlay]] -BookGen BuildWp --output [--verbose] [--dir [directory]] [--configoverlay [configoverlay]] -``` - -Arguments: - -* `-o`, `--output`: - - Required argument. Specifies the output directory name. - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will - be used as working directory. - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues - -* `-h`, `--host`: - - Optional argument. If specified, the host name set in the config file will be ignored and the host name will be set to the specified value. - - * `-co`, `--configoverlay`: - - Optional argument. Specifies a json file that will be used as an overlay for the bookgen.json config file. The values in the overlay file will override the values in the config file. This can be used to specify different output settings for different builds, without having to change the main config file. - -# Config - -Get or set bookgen application specific settings - -* `BookGen Config` - - List all currently supported application wide settings - -* `BookGen Config ` - - Gets a setting value, prints it to output and exits. - -* `BookGen Config ` - - Sets a setting value and exits - -# Edit - -Open a file for editing with configured editor. - -`BookGen edit [filename]` - -# Gui - -Starts the program with a command line gui interface - -``` -BookGen Gui [-v] [-d [directory]] -BookGen Gui [--verbose] [--dir [directory]] -``` - -Arguments: - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues. - -# Html2OpenXml - -Converts a HTML file to an Office Compatible DOCX file. - -``` -BookGen Html2OpenXml -i -o -BookGen Html2OpenXml --input --output -``` - -* `-i`, `--input`: - - Input html file with extension of .htm or .html - -* `-o`, `--output`: - - Output DOCX file. - -# Html2Pdf - -Converts a HTML file to a png using edges or chromes headless mode. The tool will use chrome, -if it's installed, otherwise it will use edge. This command is only supported on Windows OS. - -``` -BookGen Html2Pdf -i -o -BookGen Html2Pdf --input --output -``` - -* `-i`, `--input`: - - Input html file with extension of .htm or .html - -* `-o`, `--output`: - - Output PDF file. - -# Html2Png - -Converts a HTML file to a png using edges or chromes headless mode. The tool will use chrome, -if it's installed, otherwise it will use edge. This command is only supported on Windows OS. - -``` -BookGen Html2Png -i -o [-w [width]] [-h [height]] -BookGen Html2Png --input --output [--width [width]] [--height [height]] -``` - -* `-i`, `--input`: - - Input html file with extension of .htm or .html - -* `-o`, `--output`: - - Output PNG file. - -* `-w`, `--width`: - - Optional argument. Specifies the width of the output image in pixels. - -* `-h`, `--height`: - - Optional argument. Specifies the height of the output image in pixels. - -# ImgConvert - -Converts an image file to a different format. The tool supports png, jpeg, webp and svg formats. - -``` -BookGen ImgConvert -i -o -f [-q [quality]] [-r [resolution]] -``` - -Arguments: - -* `-i`, `--input`: - - Required argument. Specifies the input image file path. The file must be a valid image file or a directory containing image files. - -* `-o`, `--output`: - - Required argument. Specifies the output file path. The file must have a valid image file extension, like .png, .jpg, .jpeg, .webp. Can also be a directory, in which case the output files will be saved with the same name as the input files - -* `-f`, `--format`: - - Required argument. Specifies the output image format. Supported formats are png, jpeg, webp. - -* `-q`, `--quality`: - - Optional argument. Specifies the quality of the output image. The value must be between 0 and 100. Default is 90. If not specified, then the default value will be used. - -* `-r`, `--resolution`: - - Optional argument. Specifies the resolution of the output image. The value must be a valid resolution string, like 1920x1080 or 1280x720. If not specified, then the resolution will be the same as the input image. - -# Install - -Windows only command that installs BookGen to the system PATH & optionally to the windows terminal. - -``` -BookGen Install -``` - -# JsonArgs - -Creates an empty json arguments template file for a given bookgen command. - -``` -BookGen JsonArgs -c [-d [directory]] -BookGen JsonArgs --command [--dir [directory]] -``` - -A Json arguments template can be used to store command line arguments, so the bookgen command can be invoked with the same arguments without having to type them in again. - -Arguments: - -* `-c`, `--command`: - - Required argument. Specifies the command for which the json template will be created. - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -# Links - -Scans all markdown files in the current book and writes the lins to a markdown file, named links.md - -``` -BookGen Links [-vf] [-v] [-d [directory]] -BookGen Links [--verify] [--verbose] [--dir [directory]] -``` - -Arguments: - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues - -* `-vf`, `--verify`: - - Optional argument. When specified, the command will verify if the links are accessible and will print the result to the console. If not specified, the command will only write the links to the output file. - -# Math2Svg - -Renders a single markdown file containing Tex formulas to svg files - -``` -BookGen Math2Svg -f -o [-s [scale]] -BookGen Math2Svg --formula --output [--scale [scale]] -``` - -Arguments: - -* `-f`, `--formula`: - - Formula to render to svg. The formula must be a valid Tex formula. - -* `-o`, `--output`: - - Output svg file. - -* `-s`, `--scale`: - - Optional argument. Specifies the scale of the output svg file. Default is 1.0. If not specified, then the default value will be used. - -# Md2HTML - -Renders a single markdown file to an HTML file - -``` -BookGen Md2HTML -i -o -BookGen Md2HTML --input --output -``` - -Arguments: - -* `-i`, `--input`: - - Input markdown file path. Multiple files can be set with multiple `-i` arguments - -* `-o`, `--output`: - - Output html file path. If file name is "-", outputs to console. - -* `-tf`, `--template` - - Optional argument. If not specified, default template is used. If custom file provided, then the file must contain the folloing tags: - - * `` - For document title - * `` - For document content - -* `-ns`, `--no-syntax` - - Optional argument. Disables syntax highlighting. - -* `-ne`, `--no-embed` - - Optional argument. Disables embedding of media site links, like youtube. - -* `-r`, `--raw` - - Optional argument. Disables full html generation, only outputs the html produced by the markdown formatting. - -* `-s`, `--svg` - - Enables SVG Passthrough. When enabled SVG files will be embedded in resulting html, instead of being rendered to webp. - -* `-t`, `--title` - - Optional argument. Specifies the rendered HTML page title. Only has affect, when `-r` or `--raw` is not specified. - -# Md2terminal - -Converts a markdown file to terminal formatted text. - -``` -BookGen Md2terminal -i -o -BookGen Md2terminal --input --output -``` - -* `-i`, `--input`: - - Input markdown file path. Multiple files can be set with multiple `-i` arguments - -* `-o`, `--output`: - - Output html file path. If file name is "-", outputs to console. - -# Migrate - -Migrate an old Bookgen book to the new format. - -``` -BookGen Migrate [-v] [-d [directory]] -BookGen Migrate [--verbose] [--dir [directory]] -``` - -Arguments: - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues - -# NewBook - -Creates a new book structure in the given folder - -``` -BookGen NewBook [-v] [-d [directory]] -BookGen NewBook [--verbose] [-dir [directory]] -``` - -Arguments: - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -# NewPage - -Creates a new markdown page. - -``` -BookGen NewPage -n [-v] [-d [directory]] -BookGen NewPage --name [--verbose] [-dir [directory]] -``` - -Arguments: - -* `-n`, `--name`: - - File name. Specifies new file name - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -# QrCode - -Renders an url into a QRCode image - -``` -BookGen QrCode -d -o [-c [color]] -BookGen QrCode --data --output [--color [color]] -``` - -Arguments: - -* `-d`, `--data`: - - Url data to encode. Minimum 1 byte, Maximum 900 bytes - -* `-c`, `--color`: - - Optional argument. Specifies the color of the QRCode. The color must be a valid hex color code, like #FF0000 or #F00. - -* `-o`, `--output` - - Output file. Must have .png or .svg extension - -# Schemas - -Creates a schemas.md documentation file, describing the various config schemas used by bookgen. - -``` -BookGen Schemas [-v] [-d [directory]] -BookGen Schemas [--verbose] [--dir [directory]] -``` - -Arguments: - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues - -# Search - -Search for a given text in the markdown files of the book and print the results to the console. - -``` -Bookgen Search [-v] [-d [directory]] [-co [configoverlay]] -Bookgen Search [--verbose] [--dir [directory]] [--configoverlay [configoverlay]] -``` - -Arguments: - -* ``: - Required argument. The text to search for. The command will search for the given text in all markdown files in the book and will print the results to the console. - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues - - * `-co`, `--configoverlay`: - - Optional argument. Specifies a json file that will be used as an overlay for the bookgen.json config file. The values in the overlay file will override the values in the config file. This can be used to specify different output settings for different builds, without having to change the main config file. - -# Serve - -Starts a local only http server that serves file from the given directory - -``` -BookGen Serve [-d [directory]] -BookGen Serve [--dir [directory]] -``` - -Arguments: - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -# Shell - -Autocompleter command, that is used by Powershell - -``` -BookGen Shell -``` - -# Shortcut - -Create a cmd file in the current directory that can be used to start the bookgen Shell in the current directory. - -``` -BookGen Shortcut -``` - -This command is only supported on Windows OS. - -# Spellcheck - -Perform spell check on a given markdown file or text file. The command will print the misspelled words to the console. - -``` -BookGen Spellcheck -i [-l ] [-v] -BookGen Spellcheck --input [--language ] [--verbose] -``` - -Arguments: - -* `-i`, `--input`: - - Required argument. Specifies the input file path. The file must be a markdown file or a text file. - -* `-l`, `--language`: - - Optional argument. Specifies the language to use for spell checking. The value must be a valid language code, like en_US or hu_HU. If not specified, then en_US will be used as the default language. - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues - -* `-ld`, `--list-dictionaires` - - Optional argument. When specified, the command will list all available dictionaries and exit. - -# Stats - -Displays various statistics about the bookgen project. - -``` -BookGen Stats [-v] [-d [directory]] -BookGen Stats [--verbose] [--dir [directory]] -``` - -Arguments: - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues - -# Subcommands - -Listst all available subcommands - -``` -BookGen SubCommands -``` - -# Templates - -Lists all available templates, or extracts a single template to the current directory. - -``` -Bookgen Templates [-n [template name]] -Bookgen Templates [--name [template name]] -``` - -Arguments: - -* `-n`, `--name`: - - Optional argument. If specified, only the template with the given name will be extracted. If not specified, all available templates will be printed. - -# Terminalinstall - -Installs a bookgen profile to the Windows Termninal. - -This command is only supported on Windows OS. Without arguments, performs terminal profile install. - -``` -BookGen Terminalinstall [-c] [-t] -BookGen Terminalinstall [--checkinstall] [--checkterminalinstall] -``` - -Arguments: - -* `-c`, `--checkinstall`: - - Optional argument. When specified checks, if terminal profile installed or not. If exit code is 0, profile is installed. - -* `-t`, `--checkterminalinstall`: - - Optional argument. When specified checks, if windows terminal is installed or not. If exit code is 0, terminal is installed. - -# Tools - -Display a list of downloadable tools that can be installed and used with BookGen shell. - -This command is only supported on Windows OS. - -``` -BookGen Tools -``` - -# Upgrade - -Upgrades the bookgen project to the latest version. This command will upgrade the bookgen.json -config file to the latest version, and will also upgrade the bookgen.toc.json file to the -latest version. - -``` -BookGen Upgrade [-v] [-d [directory]] -BookGen Upgrade [--verbose] [--dir [directory]] -``` - -Arguments: - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues - - -# Validate - -Validate the configuration files used by bookgen in the specified folder. - -``` -BookGen Validate [-v] [-d [directory]] -BookGen Validate [--verbose] [--dir [directory]] -``` - -Arguments: - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues - -# Version - -Print the current program and config API version - -`BookGen Version` - -# Vstasks - -Generates a Visual Studio Code tasks.json file for the bookgen project. - -``` -BookGen Vstasks [-v] [-d [directory]] -BookGen Vstasks [--verbose] [--dir [directory]] -``` - -Arguments: - -* `-d`, `--dir`: - - Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. - -* `-v`, `--verbose`: - - Optional argument, turns on detailed logging. Usefull for locating issues diff --git a/Directory.Packages.props b/Directory.Packages.props index f7c7df59..d5516913 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,38 +4,39 @@ true - + - - + + - - - - - - + + + + + + - + - - - - - + + + + + + - + - + \ No newline at end of file diff --git a/Docs/bookgen.json b/Docs/bookgen.json new file mode 100644 index 00000000..68142e76 --- /dev/null +++ b/Docs/bookgen.json @@ -0,0 +1,123 @@ +{ + "$schema": "bookgen.schema.json", + "VersionTag": 2008, + "BookTitle": "BookGen documentation", + "Book2LetterISO639Language": "en", + "BookAuthor": "Ruzsinszki Gábor", + "StaticWebsiteConfig": { + "OpenLinksOutsideHostOnNewTab": false, + "DeployHost": "http://localhost/", + "CopyToOutput": [], + "TocConfiguration": { + "ContainerId": "tableofcontents", + "ContainerElement": "Nav", + "ContainerClass": "", + "ChapterContainer": "Section", + "ItemContainer": "UnorderedList", + "StandaloneFileName": "" + }, + "Images": { + "SvgRecode": "Passtrough", + "ResizeWith": -1, + "ResizeHeight": -1, + "ResizeAndRecodeImages": "Passtrough", + "ImageQualityOnResize": 90 + }, + "CssClasses": { + "H1": "", + "H2": "", + "H3": "", + "Img": "", + "Table": "", + "Blockquote": "", + "Figure": "", + "FigureCaption": "", + "Link": "", + "Ol": "", + "Ul": "", + "Li": "" + }, + "PreRenderCode": false, + "DefaultTempate": "" + }, + "WordpressConfig": { + "OpenLinksOutsideHostOnNewTab": true, + "DeployHost": "http://localhost/", + "ItemType": "docs", + "AllowComments": false, + "TagCategory": "post_tag", + "Images": { + "SvgRecode": "Passtrough", + "ResizeWith": -1, + "ResizeHeight": -1, + "ResizeAndRecodeImages": "Passtrough", + "ImageQualityOnResize": 90 + }, + "CssClasses": { + "H1": "", + "H2": "", + "H3": "", + "Img": "", + "Table": "", + "Blockquote": "", + "Figure": "", + "FigureCaption": "", + "Link": "", + "Ol": "", + "Ul": "", + "Li": "" + }, + "PreRenderCode": false, + "DefaultTempate": "" + }, + "PrintConfig": { + "Images": { + "SvgRecode": "Passtrough", + "ResizeWith": -1, + "ResizeHeight": -1, + "ResizeAndRecodeImages": "Passtrough", + "ImageQualityOnResize": 90 + }, + "CssClasses": { + "H1": "", + "H2": "", + "H3": "", + "Img": "", + "Table": "", + "Blockquote": "", + "Figure": "", + "FigureCaption": "", + "Link": "", + "Ol": "", + "Ul": "", + "Li": "" + }, + "PreRenderCode": false, + "DefaultTempate": "" + }, + "FeedConfig": { + "Images": { + "SvgRecode": "Passtrough", + "ResizeWith": -1, + "ResizeHeight": -1, + "ResizeAndRecodeImages": "Passtrough", + "ImageQualityOnResize": 90 + }, + "CssClasses": { + "H1": "", + "H2": "", + "H3": "", + "Img": "", + "Table": "", + "Blockquote": "", + "Figure": "", + "FigureCaption": "", + "Link": "", + "Ol": "", + "Ul": "", + "Li": "" + }, + "PreRenderCode": false, + "DefaultTempate": "" + } +} \ No newline at end of file diff --git a/Docs/bookgen.schema.json b/Docs/bookgen.schema.json new file mode 100644 index 00000000..5cd2b388 --- /dev/null +++ b/Docs/bookgen.schema.json @@ -0,0 +1,714 @@ +{ + "type": [ + "object", + "null" + ], + "properties": { + "$schema": { + "type": [ + "string", + "null" + ] + }, + "VersionTag": { + "type": "integer", + "description": "Configuration version", + "minimum": 2000, + "maximum": 2147483647 + }, + "BookTitle": { + "type": "string", + "description": "Book title", + "minLength": 1 + }, + "Book2LetterISO639Language": { + "type": "string", + "description": "Book language - 2 letter ISO 639 code" + }, + "BookAuthor": { + "type": "string", + "description": "Book author", + "minLength": 1 + }, + "StaticWebsiteConfig": { + "type": "object", + "properties": { + "OpenLinksOutsideHostOnNewTab": { + "type": "boolean", + "description": "When enabled, links that point outside of host are opened in a new tab" + }, + "DeployHost": { + "type": "string", + "description": "Deploy host name" + }, + "CopyToOutput": { + "type": "array", + "items": { + "type": [ + "string", + "null" + ] + }, + "description": "Files to copy to the output directory" + }, + "TocConfiguration": { + "type": "object", + "properties": { + "ContainerId": { + "type": "string", + "description": "The ID of the container element for the table of contents." + }, + "ContainerElement": { + "enum": [ + "None", + "Article", + "Aside", + "Details", + "Dialog", + "Div", + "Footer", + "Header", + "Main", + "Nav", + "Section" + ], + "description": "The HTML element type for the container of the table of contents." + }, + "ContainerClass": { + "type": "string", + "description": "The CSS class for the container element of the table of contents." + }, + "ChapterContainer": { + "enum": [ + "None", + "Article", + "Aside", + "Details", + "Dialog", + "Div", + "Footer", + "Header", + "Main", + "Nav", + "Section" + ], + "description": "The HTML element type for the container of each chapter in the table of contents." + }, + "ItemContainer": { + "enum": [ + "OrderedList", + "UnorderedList", + "Paragraph", + "Span", + "Details" + ], + "description": "The HTML element type for the items in the table of contents." + }, + "StandaloneFileName": { + "type": "string", + "description": "If set to a file name, then the table of contents will be rendered as a standalone page" + } + }, + "required": [ + "ContainerId", + "ContainerElement", + "ContainerClass", + "ChapterContainer", + "ItemContainer", + "StandaloneFileName" + ], + "additionalProperties": false, + "description": "Table of contents rendering configuration" + }, + "Images": { + "type": "object", + "properties": { + "SvgRecode": { + "enum": [ + "Passtrough", + "AsPng", + "AsWebp" + ], + "description": "Svg re-encode options" + }, + "ResizeWith": { + "type": "integer", + "description": "Resize to max width. -1 means use input width", + "minimum": -1, + "maximum": 2147483647 + }, + "ResizeHeight": { + "type": "integer", + "description": "Resize to max height. -1 means use input height", + "minimum": -1, + "maximum": 2147483647 + }, + "ResizeAndRecodeImages": { + "enum": [ + "Passtrough", + "AsPng", + "AsWebp" + ], + "description": "Sets Image resize and recoding behaviour" + }, + "ImageQualityOnResize": { + "type": "integer", + "description": "WebP compression quality level", + "minimum": 1, + "maximum": 100 + } + }, + "required": [ + "SvgRecode", + "ResizeWith", + "ResizeHeight", + "ResizeAndRecodeImages", + "ImageQualityOnResize" + ], + "additionalProperties": false, + "description": "Image processing settings" + }, + "CssClasses": { + "type": "object", + "properties": { + "H1": { + "type": "string", + "description": "css classes aplied to \u003Ch1\u003E element" + }, + "H2": { + "type": "string", + "description": "css classes aplied to \u003Ch2\u003E element" + }, + "H3": { + "type": "string", + "description": "css classes aplied to \u003Ch3\u003E element" + }, + "Img": { + "type": "string", + "description": "css classes aplied to \u003Cimg\u003E element" + }, + "Table": { + "type": "string", + "description": "css classes aplied to \u003Ctable\u003E element" + }, + "Blockquote": { + "type": "string", + "description": "css classes aplied to \u003Cbockquote\u003E element" + }, + "Figure": { + "type": "string", + "description": "css classes aplied to \u003Cfigure\u003E element" + }, + "FigureCaption": { + "type": "string", + "description": "css classes aplied to \u003Cfigcaption\u003E element" + }, + "Link": { + "type": "string", + "description": "css classes aplied to \u003Ca\u003E element" + }, + "Ol": { + "type": "string", + "description": "css classes aplied to \u003Col\u003E element" + }, + "Ul": { + "type": "string", + "description": "css classes aplied to \u003Cul\u003E element" + }, + "Li": { + "type": "string", + "description": "css classes aplied to \u003Cli\u003E element" + } + }, + "required": [ + "H1", + "H2", + "H3", + "Img", + "Table", + "Blockquote", + "Figure", + "FigureCaption", + "Link", + "Ol", + "Ul", + "Li" + ], + "additionalProperties": false, + "description": "Css classes to aply to various elements" + }, + "PreRenderCode": { + "type": "boolean", + "description": "Toggles pre-rendering of source code" + }, + "DefaultTempate": { + "type": "string", + "description": "Default template file name" + } + }, + "required": [ + "OpenLinksOutsideHostOnNewTab", + "DeployHost", + "CopyToOutput", + "TocConfiguration", + "Images", + "CssClasses", + "PreRenderCode", + "DefaultTempate" + ], + "additionalProperties": false, + "description": "Static website settings" + }, + "WordpressConfig": { + "type": "object", + "properties": { + "OpenLinksOutsideHostOnNewTab": { + "type": "boolean", + "description": "When enabled, links that point outside of host are opened in a new tab" + }, + "DeployHost": { + "type": "string", + "description": "Deploy host name" + }, + "ItemType": { + "type": "string", + "description": "Wordpress item type. Usually page, post or docs for WeDocs" + }, + "AllowComments": { + "type": "boolean", + "description": "Enable or disable comments" + }, + "TagCategory": { + "type": "string", + "description": "Wordpress tag category name. By default: post_tag" + }, + "Images": { + "type": "object", + "properties": { + "SvgRecode": { + "enum": [ + "Passtrough", + "AsPng", + "AsWebp" + ], + "description": "Svg re-encode options" + }, + "ResizeWith": { + "type": "integer", + "description": "Resize to max width. -1 means use input width", + "minimum": -1, + "maximum": 2147483647 + }, + "ResizeHeight": { + "type": "integer", + "description": "Resize to max height. -1 means use input height", + "minimum": -1, + "maximum": 2147483647 + }, + "ResizeAndRecodeImages": { + "enum": [ + "Passtrough", + "AsPng", + "AsWebp" + ], + "description": "Sets Image resize and recoding behaviour" + }, + "ImageQualityOnResize": { + "type": "integer", + "description": "WebP compression quality level", + "minimum": 1, + "maximum": 100 + } + }, + "required": [ + "SvgRecode", + "ResizeWith", + "ResizeHeight", + "ResizeAndRecodeImages", + "ImageQualityOnResize" + ], + "additionalProperties": false, + "description": "Image processing settings" + }, + "CssClasses": { + "type": "object", + "properties": { + "H1": { + "type": "string", + "description": "css classes aplied to \u003Ch1\u003E element" + }, + "H2": { + "type": "string", + "description": "css classes aplied to \u003Ch2\u003E element" + }, + "H3": { + "type": "string", + "description": "css classes aplied to \u003Ch3\u003E element" + }, + "Img": { + "type": "string", + "description": "css classes aplied to \u003Cimg\u003E element" + }, + "Table": { + "type": "string", + "description": "css classes aplied to \u003Ctable\u003E element" + }, + "Blockquote": { + "type": "string", + "description": "css classes aplied to \u003Cbockquote\u003E element" + }, + "Figure": { + "type": "string", + "description": "css classes aplied to \u003Cfigure\u003E element" + }, + "FigureCaption": { + "type": "string", + "description": "css classes aplied to \u003Cfigcaption\u003E element" + }, + "Link": { + "type": "string", + "description": "css classes aplied to \u003Ca\u003E element" + }, + "Ol": { + "type": "string", + "description": "css classes aplied to \u003Col\u003E element" + }, + "Ul": { + "type": "string", + "description": "css classes aplied to \u003Cul\u003E element" + }, + "Li": { + "type": "string", + "description": "css classes aplied to \u003Cli\u003E element" + } + }, + "required": [ + "H1", + "H2", + "H3", + "Img", + "Table", + "Blockquote", + "Figure", + "FigureCaption", + "Link", + "Ol", + "Ul", + "Li" + ], + "additionalProperties": false, + "description": "Css classes to aply to various elements" + }, + "PreRenderCode": { + "type": "boolean", + "description": "Toggles pre-rendering of source code" + }, + "DefaultTempate": { + "type": "string", + "description": "Default template file name" + } + }, + "required": [ + "OpenLinksOutsideHostOnNewTab", + "DeployHost", + "ItemType", + "AllowComments", + "TagCategory", + "Images", + "CssClasses", + "PreRenderCode", + "DefaultTempate" + ], + "additionalProperties": false, + "description": "Wordpress settings" + }, + "PrintConfig": { + "type": "object", + "properties": { + "Images": { + "type": "object", + "properties": { + "SvgRecode": { + "enum": [ + "Passtrough", + "AsPng", + "AsWebp" + ], + "description": "Svg re-encode options" + }, + "ResizeWith": { + "type": "integer", + "description": "Resize to max width. -1 means use input width", + "minimum": -1, + "maximum": 2147483647 + }, + "ResizeHeight": { + "type": "integer", + "description": "Resize to max height. -1 means use input height", + "minimum": -1, + "maximum": 2147483647 + }, + "ResizeAndRecodeImages": { + "enum": [ + "Passtrough", + "AsPng", + "AsWebp" + ], + "description": "Sets Image resize and recoding behaviour" + }, + "ImageQualityOnResize": { + "type": "integer", + "description": "WebP compression quality level", + "minimum": 1, + "maximum": 100 + } + }, + "required": [ + "SvgRecode", + "ResizeWith", + "ResizeHeight", + "ResizeAndRecodeImages", + "ImageQualityOnResize" + ], + "additionalProperties": false, + "description": "Image processing settings" + }, + "CssClasses": { + "type": "object", + "properties": { + "H1": { + "type": "string", + "description": "css classes aplied to \u003Ch1\u003E element" + }, + "H2": { + "type": "string", + "description": "css classes aplied to \u003Ch2\u003E element" + }, + "H3": { + "type": "string", + "description": "css classes aplied to \u003Ch3\u003E element" + }, + "Img": { + "type": "string", + "description": "css classes aplied to \u003Cimg\u003E element" + }, + "Table": { + "type": "string", + "description": "css classes aplied to \u003Ctable\u003E element" + }, + "Blockquote": { + "type": "string", + "description": "css classes aplied to \u003Cbockquote\u003E element" + }, + "Figure": { + "type": "string", + "description": "css classes aplied to \u003Cfigure\u003E element" + }, + "FigureCaption": { + "type": "string", + "description": "css classes aplied to \u003Cfigcaption\u003E element" + }, + "Link": { + "type": "string", + "description": "css classes aplied to \u003Ca\u003E element" + }, + "Ol": { + "type": "string", + "description": "css classes aplied to \u003Col\u003E element" + }, + "Ul": { + "type": "string", + "description": "css classes aplied to \u003Cul\u003E element" + }, + "Li": { + "type": "string", + "description": "css classes aplied to \u003Cli\u003E element" + } + }, + "required": [ + "H1", + "H2", + "H3", + "Img", + "Table", + "Blockquote", + "Figure", + "FigureCaption", + "Link", + "Ol", + "Ul", + "Li" + ], + "additionalProperties": false, + "description": "Css classes to aply to various elements" + }, + "PreRenderCode": { + "type": "boolean", + "description": "Toggles pre-rendering of source code" + }, + "DefaultTempate": { + "type": "string", + "description": "Default template file name" + } + }, + "required": [ + "Images", + "CssClasses", + "PreRenderCode", + "DefaultTempate" + ], + "additionalProperties": false, + "description": "Print settings" + }, + "FeedConfig": { + "type": "object", + "properties": { + "Images": { + "type": "object", + "properties": { + "SvgRecode": { + "enum": [ + "Passtrough", + "AsPng", + "AsWebp" + ], + "description": "Svg re-encode options" + }, + "ResizeWith": { + "type": "integer", + "description": "Resize to max width. -1 means use input width", + "minimum": -1, + "maximum": 2147483647 + }, + "ResizeHeight": { + "type": "integer", + "description": "Resize to max height. -1 means use input height", + "minimum": -1, + "maximum": 2147483647 + }, + "ResizeAndRecodeImages": { + "enum": [ + "Passtrough", + "AsPng", + "AsWebp" + ], + "description": "Sets Image resize and recoding behaviour" + }, + "ImageQualityOnResize": { + "type": "integer", + "description": "WebP compression quality level", + "minimum": 1, + "maximum": 100 + } + }, + "required": [ + "SvgRecode", + "ResizeWith", + "ResizeHeight", + "ResizeAndRecodeImages", + "ImageQualityOnResize" + ], + "additionalProperties": false, + "description": "Image processing settings" + }, + "CssClasses": { + "type": "object", + "properties": { + "H1": { + "type": "string", + "description": "css classes aplied to \u003Ch1\u003E element" + }, + "H2": { + "type": "string", + "description": "css classes aplied to \u003Ch2\u003E element" + }, + "H3": { + "type": "string", + "description": "css classes aplied to \u003Ch3\u003E element" + }, + "Img": { + "type": "string", + "description": "css classes aplied to \u003Cimg\u003E element" + }, + "Table": { + "type": "string", + "description": "css classes aplied to \u003Ctable\u003E element" + }, + "Blockquote": { + "type": "string", + "description": "css classes aplied to \u003Cbockquote\u003E element" + }, + "Figure": { + "type": "string", + "description": "css classes aplied to \u003Cfigure\u003E element" + }, + "FigureCaption": { + "type": "string", + "description": "css classes aplied to \u003Cfigcaption\u003E element" + }, + "Link": { + "type": "string", + "description": "css classes aplied to \u003Ca\u003E element" + }, + "Ol": { + "type": "string", + "description": "css classes aplied to \u003Col\u003E element" + }, + "Ul": { + "type": "string", + "description": "css classes aplied to \u003Cul\u003E element" + }, + "Li": { + "type": "string", + "description": "css classes aplied to \u003Cli\u003E element" + } + }, + "required": [ + "H1", + "H2", + "H3", + "Img", + "Table", + "Blockquote", + "Figure", + "FigureCaption", + "Link", + "Ol", + "Ul", + "Li" + ], + "additionalProperties": false, + "description": "Css classes to aply to various elements" + }, + "PreRenderCode": { + "type": "boolean", + "description": "Toggles pre-rendering of source code" + }, + "DefaultTempate": { + "type": "string", + "description": "Default template file name" + } + }, + "required": [ + "Images", + "CssClasses", + "PreRenderCode", + "DefaultTempate" + ], + "additionalProperties": false, + "description": "RSS/ATOM Feed settings" + } + }, + "required": [ + "$schema", + "VersionTag", + "BookTitle", + "Book2LetterISO639Language", + "BookAuthor", + "StaticWebsiteConfig", + "WordpressConfig", + "PrintConfig", + "FeedConfig" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/Docs/bookgen.toc.json b/Docs/bookgen.toc.json new file mode 100644 index 00000000..29967554 --- /dev/null +++ b/Docs/bookgen.toc.json @@ -0,0 +1,14 @@ +{ + "$schema": "bookgen.toc.schema.json", + "IndexFile": "readme.md", + "Chapters": [ + { + "Title": "Documentation", + "Files": [ + "manual.md", + "changelog.md", + "plugin.md" + ] + } + ] +} \ No newline at end of file diff --git a/Docs/bookgen.toc.schema.json b/Docs/bookgen.toc.schema.json new file mode 100644 index 00000000..e7e4648a --- /dev/null +++ b/Docs/bookgen.toc.schema.json @@ -0,0 +1,59 @@ +{ + "type": [ + "object", + "null" + ], + "properties": { + "$schema": { + "type": [ + "string", + "null" + ] + }, + "IndexFile": { + "type": "string", + "description": "First page of the book", + "minLength": 1 + }, + "Chapters": { + "type": "array", + "items": { + "type": [ + "object", + "null" + ], + "properties": { + "Title": { + "type": "string", + "description": "Chapter Title", + "minLength": 1 + }, + "Files": { + "type": "array", + "items": { + "type": [ + "string", + "null" + ] + }, + "description": "List of files associated with chapter", + "minItems": 1 + } + }, + "required": [ + "Title", + "Files" + ], + "additionalProperties": false + }, + "description": "List of chapters in the book", + "minItems": 1 + } + }, + "required": [ + "$schema", + "IndexFile", + "Chapters" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/Changelog.md b/Docs/changelog.md similarity index 90% rename from Changelog.md rename to Docs/changelog.md index 807a2ae3..9046dd40 100644 --- a/Changelog.md +++ b/Docs/changelog.md @@ -1,6 +1,34 @@ -# Next +--- +title: Changelog +tags: '' +--- +# 2026. 07 + +* Breaking: Removed assemblydocument command, since xmldoc2markdown provides better functionality +* Breaking: Removed edit command +* Breaking: Removed terminalinstall command, now part of install command +* Breaking: Removed Vstasks command and reworked it into a vscode command +* New: Build system can be extended with custom build plugins +* New: VSCode command that can generate VSCode tasks.json file for the current project and recomend extensions +* New: Mermaid diagram rendering supported via mmdr +* New: Nomnoml diagram rendering supported via nomnoml +* New: PlantUML diagram rendering supported. Note: Requires seperate PlantUMl download and installation +* New: Search command to search text in bookgen project contents +* New: Support for openCli specification +* New: Mixcloud media embeding supported +* New: Html2OpenXml command to convert HTML to OpenXML format for Word processing +* New: Template engine reworked, now supports functions and is more robust +* Change: Commands documentation generated from code and is now more complete +* Change: App settings reworked +* Change: Dropped mathjax as dependency for formula rendering. New formula rendering is based on katex and works with markdown syntax +* Change: XHTML output is now more compatible with word processors +* Change: Javascript interop now supports modules +* Change: Template engine supports functions and are actually better working * Change: Better logging of critical errors +* Fix: Fixed crash issue in imgconvert command +* Fix: Youtuibe media embedding was not working correctly +* Fix: Wordpress export was generating double page titles # 2026. 03. 04 (Prerelease) diff --git a/Docs/icon-bookgen.webp b/Docs/icon-bookgen.webp new file mode 100644 index 00000000..e685fa94 Binary files /dev/null and b/Docs/icon-bookgen.webp differ diff --git a/Docs/decision_notes.md b/Docs/internals/decision_notes.md similarity index 93% rename from Docs/decision_notes.md rename to Docs/internals/decision_notes.md index 4fcff076..beaf93fb 100644 --- a/Docs/decision_notes.md +++ b/Docs/internals/decision_notes.md @@ -1,3 +1,8 @@ +--- +title: 2026.04 +tags: '' +--- + # 2026.04 - KaTex requires DOM to work, so It can't be used in the current markdown rendering pipeline. diff --git a/Docs/rendering.excalidraw b/Docs/internals/rendering.excalidraw similarity index 100% rename from Docs/rendering.excalidraw rename to Docs/internals/rendering.excalidraw diff --git a/Docs/rendering.svg b/Docs/internals/rendering.svg similarity index 100% rename from Docs/rendering.svg rename to Docs/internals/rendering.svg diff --git a/Docs/manual.md b/Docs/manual.md new file mode 100644 index 00000000..344ef518 --- /dev/null +++ b/Docs/manual.md @@ -0,0 +1,438 @@ +--- +title: Bookgen manual +tags: '' +--- + +# Bookgen + +BookGen is a command-line Markdown processor designed to streamline the creation of books and technical documentation. +It consists of two binaries: `BookGen` is the main command-line program and `BookGen.Shellprog.exe` is the shell helper +for PowerShell and works on Windows. + +For existing books, the most common tasks are available through the `Gui` sub-command. Running `BookGen Gui` starts a +terminal user interface (TUI) that lets you perform various book maintenance and workflow tasks. + +To get the latest version of BookGen, please visit the releases page at: https://github.com/webmaster442/BookGen/releases + +## Listing commands and getting help + +BookGen is a command driven application. To get documentation on all the available commands use the `Bookgen doc commands` +command. This will display all the available commands. To get help on a specific command use the `help` sub-command with +the commands name. For example: `bookgen help doc commands`. + +## Installation + +Installation is straight-forward. Extract or copy BookGen into your folder of choice and you can then use it from that folder +with your shell. However I recommend running the `bookgen install` command that can install a Windows Terminal profile and add +BookGen into your `PATH` environment variable, so that you can use it from any folder. + +## Writing your book workflow + +The first step in writing any book is to know roughly what it's going to be about, but I suppose if you are looking for tools, +then you have your rough idea. The first step in using bookgen is to create a folder that you are going to be working in. +After this, navigate to the folder and follow these steps. + +Recommended tools to have installed: + +* [GIT](https://git-scm.com/) +* [Visual Studio code](https://code.visualstudio.com/) + +0. **Make sure that your folder is a GIT repository or some other version controlled folder** + + This is a somewhat optional step, nobody will stop you if it's not a GIT repository, but I highly recommend it, since if + you mess up something, than at least, you can revert to any revision at anytime. If you don't know what is GIT you can + find a detailed tutorial about it at https://www.w3schools.com/git/ + +1. **Create the Bookgen configuration files** + + This can be performed via executing the `bookgen newbook` command. This will create four files. A table of contents file + and a configuration file. For both files a JSON schema document is also created and for editing the configurations an + editor that supports JSON schema based autocomplete and validation is recommended, like Visual studio code. + +2. **Create a page** + + You can create a page by using your favorite text editor or by using the `bookgen newpage` command. The later adds the + necessary front matter configuration to the file as well. If you have existing files without front matter content you + can use the `bookgen addfrontmatter` command to add them to existing files. + +3. **Edit your page** + + Create your content. This is the hardest part and if you are like me, you will struggle a lot with finding proper words + and inspiration. What I can recommend is don't stress. There are some days, when writing just one line is exhausting and + there are others, where inspiration finds you and you just write and write. + +4. **Add page to table of contents** + + Your created page is not automatically part of your book. You need to place it your table of contents file into + your specific chapter. + +5. **Review and edit your configuration** + + Before building review your configuration and make necessary edits to it, if needed. + +6. **Build your book** + + This can be done by utilizing one of the build commands: + + * `buildepub` - Build an epub3 file + * `buildepxort` - Build a JSON export file, that can be post processed with your favorite JSON processor + * `buildfeed` - Build RSS and ATOM feed files from your book + * `buildprint` - Build a HTML and XHTML document that can be imported into word processors + * `buildweb` - Build a static website + * `buildwp` - Build a WordPress export XML that can be imported into a WordPress site + +### Convinience commands + +The philosophy behind BookGen that it should support the workflow as much as possible, that is why it has a few convenience +commands, that should make writing much more enjoyable. These commands are: + + * `bookgen vstasks` - Creates a `.vscode` folder with Visual Studio code and configures BookGen as available tasks. Optionally this command also creates a recommended extension list file to easily install the best tools for working with markdown files. + +* `bookgen shortcut` - This command creates a shortcut file in the current directory that can be used to start the bookgen Shell in the current directory. + +## Updating bookgen + +The BookGen configuration format is versioned and in some releases it's updated. Some settings are removed and some are added as the program evolves. Bookgen offers a migration mechanism to update your book configuration file to the latest one used by Bookgen. + +To make sure after updating your existing book is compatible with the tool run the `bookgen validate` command to validate your configuration file. If it needs upgrading then you will see an error message similar to this: + +``` +Config file is too old. Run bookgen upgrade to update it +``` + +To update to the latest configuration format run the `bookgen upgrade` command. This will upgrade your configuration to the latest version. **Warning**: +It's highly unlikely that this will fail and mess up your configuration, but as a fail-safe please back up your existing configuration before migrating. + +Migration also updates the configuration and the table of contents document schema files as well. + +After the migration, review the configuration file in your favorite editor and make necessary changes, if needed and validate your changes with `bookgen validate` + +## Converting a single markdown file to html + +Converting a single markdown file can be achieved with the `md2html` command: + +Renders a single markdown file to an HTML file. + +```sh +BookGen md2html \ + [-ne, --no-embed] \ + [-ns, --no-syntax] \ + [-r, --raw] \ + [-s, --svg] \ + [-t, --title ] \ + [-tf, --template ] \ + -i, --input \ + -o, --output +``` + +### Options + +* -`ne`, `--no-embed` + + **Optional option** + + Disables embedding of assets in the output HTML. + +* -`ns`, `--no-syntax` + + **Optional option** + + Disables syntax highlighting in the output HTML. + +* -`r`, `--raw` + + **Optional option** + + Disables full html generation, only outputs the html produced by the markdown formatting. + +* -`s`, `--svg` + + **Optional option** + + When enabled SVG files will be embedded in resulting html, instead of being rendered to webp. + +* -`t`, `--title` + + **Optional option** + + Specifies the rendered HTML page title. Only has affect, when `-r` or `--raw` is not specified. + +* -`tf`, `--template` + + **Optional option** + + If not specified, default template is used. If custom file provided, then the file must contain the folloing tags: ``, `` + +* -`i`, `--input` + + **Required option** + + Input markdown file path. Multiple files can be set with multiple `-i` arguments + +* -`o`, `--output` + + **Required option** + + Output html file path. If file name is `-`, outputs to console. + + +## Markdown files + +Markdown files are the main input format for BookGen. BookGen supports the following Markdown extensions on top of the [commonmark](https://commonmark.org/) specification: + +* [Citations](https://github.com/xoofx/markdig/blob/main/src/Markdig.Tests/Specs/FigureFooterAndCiteSpecs.md) +* [Custom containers](https://github.com/xoofx/markdig/blob/main/src/Markdig.Tests/Specs/CustomContainerSpecs.md) +* [Definition lists](https://github.com/xoofx/markdig/blob/main/src/Markdig.Tests/Specs/DefinitionListSpecs.md) +* [Extra emphasis](https://github.com/xoofx/markdig/blob/main/src/Markdig.Tests/Specs/EmphasisExtraSpecs.md) +* [Figures](https://github.com/xoofx/markdig/blob/main/src/Markdig.Tests/Specs/FigureFooterAndCiteSpecs.md) +* [Footers](https://github.com/xoofx/markdig/blob/main/src/Markdig.Tests/Specs/FigureFooterAndCiteSpecs.md) +* [Grid tables](https://github.com/xoofx/markdig/blob/main/src/Markdig.Tests/Specs/GridTableSpecs.md) +* [Mathematics](https://github.com/xoofx/markdig/blob/main/src/Markdig.Tests/Specs/MathSpecs.md) +* [Media links](https://github.com/xoofx/markdig/blob/main/src/Markdig.Tests/Specs/MediaSpecs.md) +* [Pipe tables](https://github.com/xoofx/markdig/blob/main/src/Markdig.Tests/Specs/PipeTableSpecs.md) +* [List extras](https://github.com/xoofx/markdig/blob/main/src/Markdig.Tests/Specs/ListExtraSpecs.md) +* [Task lists](https://github.com/xoofx/markdig/blob/main/src/Markdig.Tests/Specs/TaskListSpecs.md) +* [Diagrams](https://github.com/xoofx/markdig/blob/main/src/Markdig.Tests/Specs/DiagramsSpecs.md) +* [Auto links](https://github.com/xoofx/markdig/blob/main/src/Markdig.Tests/Specs/AutoLinks.md) +* [Alert blocks](https://xoofx.github.io/markdig/docs/extensions/alert-blocks/) +* YAML front matter for metadata +* Table of contents via the `[toc]` or `[toc maxlevel=n]` tag, where n is the level of headdings to include +* Keyboard input via the `<<` and `>>` tags, which will render the text between them as keyboard input. + +### YAML Front matter + +BookGen stores metadata information about each markdown file in a yaml front matter block, that will look something like this at the beginning of the markdown file: + +```yaml +title: "Getting Started with BookGen" +tags: "bookgen, markdown, publishing, tutorial" +template: "tiny-light.html" +data: + author: "Jane Doe" + audience: "Developers" + version: "1.0" + category: "Documentation" +``` + +The front matter used in the beginning of the files follows the following schema: + +```yaml +$schema: "https://json-schema.org/draft/2020-12/schema" +$id: "https://example.com/schemas/frontmatter.schema.yaml" +title: "FrontMatter" +type: object +additionalProperties: false +properties: + title: + type: string + minLength: 1 + description: "Document title" + tags: + type: string + minLength: 1 + description: "A comma separated list of tags" + template: + type: + - string + - "null" + description: "Template file to use. If empty, default template is used" + data: + type: + - object + - "null" + description: "Additional data that can be used during rendering" + additionalProperties: + type: string +required: + - title + - tags +``` + +## Templates + +Template tags use a mustache like syntax starting with the `{{` symbols and ending with `}}`. Tags can include simple properties or functions. **Function and property names are case sensitive** + +There are a few special properties that have special meaning. These are: + +* `{{Title}}` - Title of the acual page +* `{{Content}}` - Content of the actual page +* `{{Host}}` - Host url, set in the configuration file + +The `{{Content}}` mustache tag is a special placeholder used to represent the markdown content within a file. **Important:** A markdown document must not include the `{{Content}}` tag within its own content. Including this tag will cause infinite recursion during rendering, resulting in a failure to render the document. + +The `Title` attribute comes from the YAML front matter of the document. The YAML front matter must include a `Title` and a `Tags` property. The Title is the document title and the tags are a comma separated string of keywords that can be set for the metadata. + +You can also add additional data to the YAML front matter. The YAML front matter via the `Data` property, which is a Dictionary. + +```yaml +Data: + foo: bar +``` + +This will allow you to access this value in templates or markdown content using the mustache syntax `{{foo}}`. Property names are case sensitive. + +### Template functions + +| Function | Category | Description | +| ----------------------- | ------------ | ------------------------------------ | +| `ToUpper` | Case | Converts to uppercase | +| `ToLower` | Case | Converts to lowercase | +| `Substring` | Manipulation | Extracts a portion of a string | +| `Trim` | Manipulation | Removes surrounding whitespace | +| `TrimStart` | Manipulation | Removes leading whitespace | +| `TrimEnd` | Manipulation | Removes trailing whitespace | +| `Replace` | Manipulation | Replaces a substring | +| `Concat` | Manipulation | Joins multiple values | +| `RegexReplace` | Manipulation | Replaces via regex (5s timeout) | +| `HtmlEncode` | Encoding | HTML-encodes a value | +| `UrlEncode` | Encoding | URL-encodes a value | +| `UrlDecode` | Encoding | URL-decodes a value | +| `CurrentDate` | Date/Time | Current date (`yyyy-MM-dd`) | +| `CurrentDateFormat` | Date/Time | Current date, custom format | +| `CurrentTime` | Date/Time | Current time (`HH:mm:ss`) | +| `CurrentTimeFormat` | Date/Time | Current time, custom format | +| `CurrentDateTime` | Date/Time | Current date and time | +| `CurrentDateTimeFormat` | Date/Time | Current date and time, custom format | + +> **Note:** Numeric arguments (such as `startIndex` and `length`) are converted using the invariant culture. Any value that is `null` is treated as an empty string. + +#### String Case Functions + +* `ToUpper(obj)` + + Converts the given value to an uppercase string. + + - **Parameters:** `obj` — the value to convert. + - **Returns:** The uppercase representation of the value. + +* `ToLower(obj)` + + Converts the given value to a lowercase string. + + - **Parameters:** `obj` — the value to convert. + - **Returns:** The lowercase representation of the value. + +#### String Manipulation Functions + +* `Substring(obj, startIndex, length)` + + Extracts a sub-string from the value. + + - **Parameters:** + - `obj` — the source value. + - `startIndex` — the zero-based starting position. + - `length` — the number of characters to extract. + - **Returns:** The extracted sub-string. + +* `Trim(obj)` + + Removes all leading and trailing white-space from the value. + + - **Parameters:** `obj` — the value to trim. + - **Returns:** The trimmed string. + +* `TrimStart(obj)` + + Removes all leading white-space from the value. + + - **Parameters:** `obj` — the value to trim. + - **Returns:** The string without leading white-space. + +* `TrimEnd(obj)` + + Removes all trailing white-space from the value. + + - **Parameters:** `obj` — the value to trim. + - **Returns:** The string without trailing white-space. + +* `Replace(obj, oldValue, newValue)` + + Replaces all occurrences of a sub-string with another sub-string. + + - **Parameters:** + - `obj` — the source value. + - `oldValue` — the sub-string to find. + - `newValue` — the sub-string to substitute. + - **Returns:** The resulting string after replacement. + +* `Concat(args)` + + Concatenates multiple values into a single string. + + - **Parameters:** `args` — an array of values to concatenate. + - **Returns:** The combined string. + +* `RegexReplace(obj, pattern, replacement)` + + Replaces text matching a regular expression pattern. + + - **Parameters:** + - `obj` — the source value. + - `pattern` — the regular expression pattern. + - `replacement` — the replacement text. + - **Returns:** The resulting string after replacement. + - **Remarks:** Uses culture-invariant matching and enforces a **5 second** timeout to guard against catastrophic backtracking. + +#### Encoding Functions + +* `HtmlEncode(obj)` + + Encodes a value for safe inclusion in HTML. + + - **Parameters:** `obj` — the value to encode. + - **Returns:** The HTML-encoded string. + +* `UrlEncode(obj)` + + Encodes a value for safe inclusion in a URL. + + - **Parameters:** `obj` — the value to encode. + - **Returns:** The URL-encoded string. + +* `UrlDecode(obj)` + + Decodes a URL-encoded value. + + **Parameters:** `obj` — the value to decode. + **Returns:** The decoded string + +#### Date and Time Functions + +* `CurrentDate()` + + Returns the current date. + + - **Returns:** The date formatted as `yyyy-MM-dd`. + +* `CurrentDateFormat(format)` + + Returns the current date using a custom format. + + - **Parameters:** `format` — a .NET date/time format string. + - **Returns:** The date formatted with the specified format. + +* `CurrentTime()` + + Returns the current time. + + - **Returns:** The time formatted as `HH:mm:ss`. + +* `CurrentTimeFormat(format)` + + Returns the current time using a custom format. + + - **Parameters:** `format` — a .NET date/time format string. + - **Returns:** The time formatted with the specified format. + +* `CurrentDateTime()` + + Returns the current date and time. + + - **Returns:** The date and time formatted as `yyyy-MM-dd HH:mm:ss`. + +* `CurrentDateTimeFormat(format)` + + Returns the current date and time using a custom format. + + - **Parameters:** `format` — a .NET date/time format string. + - **Returns:** The date and time formatted with the specified format. \ No newline at end of file diff --git a/Docs/plugin.md b/Docs/plugin.md new file mode 100644 index 00000000..f2db6c7b --- /dev/null +++ b/Docs/plugin.md @@ -0,0 +1,215 @@ +--- +title: Plugin tutorial +tags: '' +--- + +# Bookgen plugin tutorial + +BookGen supports plugins that can hook into the book building process, allowing +you to extend and customize how your book is generated. + +## Requirements + +Plugins must target **.NET Standard 2.1**. This ensures that your plugin is +compatible with the BookGen runtime and can be loaded during the build. + +## Packaging + +A plugin is distributed as a ZIP file that uses the `.plugin` file extension. +The archive must contain the following: + +- The plugin **DLL** file (the entry assembly). +- Any **dependencies** that the plugin needs in order to run. +- A **`manifest.json`** file, placed in the root of the archive, that describes + the plugin. + +## The manifest.json file + +The `manifest.json` file describes the plugin and tells BookGen which assembly +to load. It contains the following properties: + +| Property | Required | Description | +| --------------- | -------- | --------------------------------------------------------------- | +| `entryAssembly` | Yes | The name of the plugin DLL file (must have a `.dll` extension). | +| `author` | Yes | The author of the plugin. | +| `description` | Yes | A short description of what the plugin does. | +| `apiVersion` | Yes | The plugin API version. Must be a valid version with major `1`. | +| `url` | No | An optional URL with more information about the plugin. | + +### Example + +```json +{ + "entryAssembly": "MyPlugin.dll", + "author": "Jane Doe", + "description": "An example plugin that customizes the build process.", + "apiVersion": "1.0.0", + "url": "https://example.com/my-plugin" +} +``` + +### JSON Schema for validating the manifest.json file: + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PackageManifest", + "description": "Manifest describing a BookGen plugin package.", + "type": "object", + "additionalProperties": false, + "required": [ + "entryAssembly", + "description", + "apiVersion", + "author" + ], + "properties": { + "entryAssembly": { + "type": "string", + "description": "The plugin entry assembly. Must have a .dll extension.", + "minLength": 1, + "pattern": "(?i)\\.dll$" + }, + "author": { + "type": "string", + "description": "The author of the plugin.", + "minLength": 1 + }, + "description": { + "type": "string", + "description": "A human readable description of the plugin.", + "minLength": 1 + }, + "apiVersion": { + "type": "string", + "description": "The API version. Must be a valid version string with a major version of 1.", + "minLength": 1, + "pattern": "^1(\\.\\d+){0,3}$" + }, + "url": { + "type": "string", + "description": "A URL to the plugin's homepage or repository.", + "minLength": 1, + "format": "uri" + } + }, + "additionalItems": false +} +``` + +## Project structure & setup + +All of the types that make up the plugin API (the interfaces, base classes and +data types you hook into) live in the **`BookGen.Api`** assembly. In order to +use the plugin API, your project **must reference `BookGen.Api`**. Without this +reference the plugin API types are not available and your plugin cannot be +compiled against them. + +You can add this as a reference to your project by either cloning the BookGen +repository and referencing the `BookGen.Api` project, or by adding a DLL +reference to the `BookGen.Api.dll` file that is included in the BookGen +distribution. + +NuGet package for the `BookGen.Api` assembly is **not yet available**, +since the plugin API isn't mature enough to be published as a NuGet package. + +A simple plugin project structure looks like this: + +```markup + + + + netstandard2.1 + enable + enable + + latest + + true + + + + + + + false + runtime + + + + + PreserveNewest + + + + + $(OutputPath)..\$(AssemblyName).plugin + + + + +``` + +The `TargetFramework` is set to `netstandard2.1` to ensure compatibility with the BookGen runtime. +The `EnableDynamicLoading` property is set to `true` to allow the plugin to be loaded dynamically +at runtime and **this is required for all BookGen plugins.** + +`LangVersion` is set to `latest` to allow the use of the latest C# language features, which can be useful for +plugin development, but it's not strictly required. You can set it to a specific version if you prefer. + +**Please note that even if you set `LangVersion` to `latest` there might be language features that will not compile, +because the `.netstandard2.1` target framework does not support all the latest C# features. +This is a limitation of the target framework, not the `LangVersion` setting.** + +When referencing the `BookGen.Api` project, the `Private` property is set to `false` to prevent the API assembly +from being copied to the output directory. The `ExcludeAssets` property is set to `runtime` to exclude the runtime +assets of the API assembly from the plugin package, as they are not needed for the plugin to function. +These settings ensure that the plugin package only contains the necessary files for the plugin to run, without +including unnecessary dependencies. + +The `manifest.json` file is included in the project and set to copy to the output directory, +so it will be included in the plugin package. + +The `PackagePlugin` target is defined to create the plugin package after the build process. +It uses the `ZipDirectory` task to create a ZIP file with the `.plugin` extension, containing +the plugin DLL and any dependencies. This way when you build the plugin project, +it will automatically create a `.plugin` file in the output directory. + +## Plugin API + +The main entry point for a plugin is the `IBookPlugin` interface. This interface is located in the +`Bookgen.Api.V1` namespace. A plugin assembly must contain only one class that implements this interface. +The class must have a public parameterless constructor, so that BookGen can instantiate it when loading the plugin. + +If the plugin assembly contains more than one class that implements `IBookPlugin`, BookGen will not load the plugin. + +The `IBookPlugin` defines a single method that needs to be implemented: + + +```csharp +public Task Build( + IBook book, + IBookgenServices bookgenServices, + CancellationToken cancellationToken) +``` + +**Parameters** + +| parameter | description | +| ------------------------------------- | ------------------------------------------------ | +| `IBook book` | The book to build. | +| `IBookgenServices bookgenServices` | The services offered by the Bookgen application. | +| `CancellationToken cancellationToken` | A token to monitor for cancellation requests. | + +The complete up-to-date API reference can be found in the Wiki page of BookGen at the following +link: [BookGen API Reference](https://github.com/webmaster442/BookGen/wiki) + +### API Versioning + +The plugin API is versioned using [Semantic Versioning](https://semver.org/). The major version of the API is `1`, +and the minor and patch versions can be incremented as needed. + +### Sample plugin + +The repository contains a sample plugin that demonstrates how to implement the `IBookPlugin` interface and use the plugin API. +It is located in the `BookGen.SamplePlugin` project. You can use this sample as a starting point for your own plugin development. \ No newline at end of file diff --git a/Docs/readme.md b/Docs/readme.md new file mode 100644 index 00000000..7e6dc760 --- /dev/null +++ b/Docs/readme.md @@ -0,0 +1,8 @@ +--- +title: Bookgen Docs +tags: '' +--- + +![BookGen Logo](icon-bookgen.webp) + +This is the documentation for BookGen, a static site generator for books and documentation. diff --git a/README.md b/README.md index 9c1c324e..2427eff0 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -![logo](Branding/icon-bookgen.png) +![logo](Branding/icon-bookgen.webp) # BookGen - A C# documentation generator -BookGen is a command line toolchain for writing books and documentation in markdown. It was inspired by GitBook. Generaly speeking you can call it a static website generator, but it has some neat features compared to other products. +BookGen is a command line toolchain for writing books and documentation in markdown. It was inspired by GitBook. Generally speaking you can call it a static website generator, but it has some neat features compared to other products. It is designed to be cross platform, but It's developed and tested under Windows. It features a web based, cross platform Markdown editor. @@ -11,24 +11,25 @@ It is designed to be cross platform, but It's developed and tested under Windows * Extremely fast, compared to GitBook and other NodeJs stuff * It hasn't got a template engine, so you don't have to learn a new template language. However it's extendable via shortcodes, like wordpress * Can be extended with Scripting API -* Can be extended with NodeJs, Python, Php Scripts -* Should be cross platform (Only tested & developed on windows) +* Can be extended with NodeJs and Python Scripts +* Should be cross platform, but I mainly test and use it on Windows * Can resize & convert pictures during build. * Syntax highlighting is rendered during compile for Epub and printable documents. * Many output formats: - * Static website with Bootstrap template - * Printable or Word processor importable plain HTML + * Static website + * Printable or Word processor importable plain XHTML * EPub v. 3.0 * Wordpress compatible XML export file + * RSS and Atom feeds + * Word OpenXML (*.docx) document -To Build and develop you will need: -* Visual Studio 2022 with latest updates - https://visualstudio.microsoft.com/vs/ -* .NET 7 SDK - https://dotnet.microsoft.com/download -# Getting the repo +# Build instructions ```bash git clone https://github.com/webmaster442/BookGen.git +.\download-assets.ps1 +dotnet build ``` ## Documentation diff --git a/Source/BookGen.Api/BookGen.Api.csproj b/Source/BookGen.Api/BookGen.Api.csproj new file mode 100644 index 00000000..70a9ea5e --- /dev/null +++ b/Source/BookGen.Api/BookGen.Api.csproj @@ -0,0 +1,25 @@ + + + + netstandard2.1 + enable + enable + ..\..\bin\$(Configuration)\ + true + True + latest + True + True + latest-recommended + false + $(WarningsAsErrors);NU1605;CS1591 + 1.0.0 + + + + + PreserveNewest + + + + diff --git a/Source/BookGen.Api/PluginManifest.json b/Source/BookGen.Api/PluginManifest.json new file mode 100644 index 00000000..ca4cb7fe --- /dev/null +++ b/Source/BookGen.Api/PluginManifest.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PackageManifest", + "description": "Manifest describing a BookGen plugin package.", + "type": "object", + "additionalProperties": false, + "required": [ + "entryAssembly", + "description", + "apiVersion", + "author" + ], + "properties": { + "entryAssembly": { + "type": "string", + "description": "The plugin entry assembly. Must have a .dll extension.", + "minLength": 1, + "pattern": "(?i)\\.dll$" + }, + "author": { + "type": "string", + "description": "The author of the plugin.", + "minLength": 1 + }, + "description": { + "type": "string", + "description": "A human readable description of the plugin.", + "minLength": 1 + }, + "apiVersion": { + "type": "string", + "description": "The API version. Must be a valid version string with a major version of 1.", + "minLength": 1, + "pattern": "^1(\\.\\d+){0,3}$" + }, + "url": { + "type": "string", + "description": "A URL to the plugin's homepage or repository.", + "minLength": 1, + "format": "uri" + } + }, + "additionalItems": false +} diff --git a/Source/BookGen.Api/TagBuilder.cs b/Source/BookGen.Api/TagBuilder.cs new file mode 100644 index 00000000..e8e61695 --- /dev/null +++ b/Source/BookGen.Api/TagBuilder.cs @@ -0,0 +1,255 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Net; +using System.Text; + +namespace BookGen.Api; + +/// +/// A builder class for generating HTML content in a fluent manner. +/// +/// +/// Initializes a new instance of the class with the specified initial capacity. +/// +/// The initial capacity of the underlying . +public sealed class HtmlBuilder(int size) +{ + private readonly StringBuilder _sb = new(size); + private readonly Stack _openTags = new(); + + /// + /// Creates a new instance of with the specified initial capacity. + /// + /// The initial capacity of the underlying . + /// A new instance of . + public static HtmlBuilder Create(int size = 4096) + { + return new(size); + } + + /// + /// Gets the name of the last unclosed HTML tag, or null if there are no open tags. + /// + public string? LastUnclosedTag => _openTags.Count > 0 ? _openTags.Peek() : null; + + /// + /// Gets an enumerable of all currently open HTML tags in reverse order (from the most recently opened to the least). + /// + public IEnumerable OpenTags => _openTags.Reverse(); + + /// + /// Creates an HTML element with the specified tag name and optional content. + /// + /// The name of the HTML tag. + /// An optional action to generate the content of the element. + /// The current instance. + public HtmlBuilder Element(string tag, Action? content = null) + { + _sb.Append('<').Append(tag).Append('>'); + + if (content == null) + { + _openTags.Push(tag); + } + else + { + content(this); + _sb.Append("'); + } + + return this; + } + /// + /// Creates an HTML element with the specified tag name, attributes, and optional content. + /// + /// The name of the HTML tag. + /// An action to configure the attributes of the tag. + /// An optional action to generate the content of the element. + /// The current instance. + public HtmlBuilder Element(string tag, Action configure, Action? content = null) + { + var tb = new TagBuilder(tag); + configure(tb); + + _sb.Append('<').Append(tag); + + foreach (var attr in tb.Attributes) + { + _sb.Append(' ') + .Append(attr.Key) + .Append("=\"") + .Append(WebUtility.HtmlEncode(attr.Value)) + .Append('"'); + } + + _sb.Append('>'); + + if (content == null) + { + _openTags.Push(tag); + } + else + { + content(this); + _sb.Append("'); + } + + return this; + } + + /// + /// Closes the most recently opened HTML element. + /// + /// The current instance. + /// Thrown if there are no open elements to close. + public HtmlBuilder CloseOpenTag() + { + if (_openTags.Count == 0) + { + throw new InvalidOperationException("No open elements."); + } + + string tag = _openTags.Pop(); + + _sb.Append("'); + return this; + } + + /// + /// Appends text content to the current HTML element, encoding it to prevent XSS attacks. + /// + /// The text content to append. + /// The current instance. + public HtmlBuilder Text(string text) + { + _sb.Append(WebUtility.HtmlEncode(text)); + return this; + } + + /// + /// Appends raw HTML content to the current HTML element without encoding. + /// + /// The raw HTML content to append. + /// The current instance. + public HtmlBuilder Raw(string html) + { + _sb.Append(html); + return this; + } + + /// + /// Creates a self-closing HTML element with the specified tag name and optional attributes. + /// + /// The name of the HTML tag. + /// An optional action to configure the attributes of the tag. + /// The current instance. + public HtmlBuilder SelfClosing(string tag, Action? configure = null) + { + _sb.Append('<').Append(tag); + + if (configure != null) + { + var tb = new TagBuilder(tag); + configure(tb); + + foreach (KeyValuePair attr in tb.Attributes) + { + _sb.Append(' ') + .Append(attr.Key) + .Append("=\"") + .Append(WebUtility.HtmlEncode(attr.Value)) + .Append('"'); + } + } + + _sb.Append(" />"); + + return this; + } + + /// + /// Returns the generated HTML content as a string. Throws an exception if there are unclosed HTML elements. + /// + /// The generated HTML content as a string. + /// Thrown if there are unclosed HTML elements. + public override string ToString() + { + return _openTags.Count != 0 + ? throw new InvalidOperationException("There are unclosed HTML elements.") + : _sb.ToString(); + } + + /// + /// A builder class for constructing HTML tags with attributes in a fluent manner. + /// + public sealed class TagBuilder + { + internal Dictionary Attributes { get; } = []; + + internal TagBuilder(string tag) + { + Tag = tag; + } + + /// + /// Gets the name of the HTML tag being constructed. + /// + public string Tag { get; } + + /// + /// Adds an attribute to the HTML tag being constructed. + /// + /// The name of the attribute. + /// The value of the attribute. + /// The current instance. + public TagBuilder Attr(string name, string value) + { + Attributes[name] = value; + return this; + } + + /// + /// Adds a class attribute to the HTML tag being constructed. + /// + /// The value of the class attribute. + /// The current instance. + public TagBuilder Class(string className) + { + return Attr("class", className); + } + + /// + /// Adds an id attribute to the HTML tag being constructed. + /// + /// The value of the id attribute. + /// The current instance. + public TagBuilder Id(string id) + { + return Attr("id", id); + } + + /// + /// Adds a style attribute to the HTML tag being constructed. + /// + /// The value of the style attribute. + /// The current instance. + public TagBuilder Style(string style) + { + return Attr("style", style); + } + + /// + /// Adds a data-* attribute to the HTML tag being constructed. + /// + /// The name of the data attribute. + /// The value of the data attribute. + /// The current instance. + public TagBuilder Data(string name, string value) + { + return Attr($"data-{name}", value); + } + } +} diff --git a/Source/BookGen.Api/V1/CssClasses.cs b/Source/BookGen.Api/V1/CssClasses.cs new file mode 100644 index 00000000..68b99695 --- /dev/null +++ b/Source/BookGen.Api/V1/CssClasses.cs @@ -0,0 +1,72 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Api.V1; + +/// +/// Represents CSS classes that can be applied to various HTML elements during rendering. +/// +public sealed class CssClasses +{ + /// + /// css classes aplied to `h1` element + /// + public string H1 { get; set; } = string.Empty; + + /// + /// css classes aplied to `h2` element + /// + public string H2 { get; set; } = string.Empty; + + /// + /// css classes aplied to `h3` element + /// + public string H3 { get; set; } = string.Empty; + + /// + /// css classes aplied to `img` element + /// + public string Img { get; set; } = string.Empty; + + /// + /// css classes aplied to `table` element + /// + public string Table { get; set; } = string.Empty; + + /// + /// css classes aplied to `blockquote` element + /// + public string Blockquote { get; set; } = string.Empty; + + /// + /// css classes aplied to `figure` element + /// + public string Figure { get; set; } = string.Empty; + + /// + /// css classes aplied to `figcaption` element + /// + public string FigureCaption { get; set; } = string.Empty; + + /// + /// css classes aplied to `a` element + /// + public string Link { get; set; } = string.Empty; + + /// + /// css classes aplied to `ol` element + /// + public string Ol { get; set; } = string.Empty; + + /// + /// css classes aplied to `ul` element + /// + public string Ul { get; set; } = string.Empty; + + /// + /// css classes aplied to `li` element + /// + public string Li { get; set; } = string.Empty; +} diff --git a/Source/BookGen.Api/V1/IAssetSource.cs b/Source/BookGen.Api/V1/IAssetSource.cs new file mode 100644 index 00000000..c92286ae --- /dev/null +++ b/Source/BookGen.Api/V1/IAssetSource.cs @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Diagnostics.CodeAnalysis; + +namespace BookGen.Api.V1; + +/// +/// Represents a source of assets that can be used by the Bookgen application. +/// +public interface IAssetSource +{ + /// + /// Gets a read-only list of available asset names provided by this asset source. + /// + IReadOnlyList AvailableAssets { get; } + /// + /// Attempts to retrieve the content of an asset by its name. Returns true if the asset is found, otherwise false. + /// + /// The name of the asset to retrieve. + /// When this method returns, contains the content of the asset if found; otherwise, null. + /// True if the asset is found; otherwise, false. + bool TryGetAsset(string name, [NotNullWhen(true)] out string? content); + /// + /// Gets a stream for reading the binary content of an asset by its name. + /// + /// The name of the asset to retrieve. + /// A stream for reading the binary content of the asset. + Stream GetBinaryAssetStream(string name); +} diff --git a/Source/BookGen.Api/V1/IBook.cs b/Source/BookGen.Api/V1/IBook.cs new file mode 100644 index 00000000..ec1ba411 --- /dev/null +++ b/Source/BookGen.Api/V1/IBook.cs @@ -0,0 +1,21 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Api.V1; + +/// +/// Represents a book with an index and chapters +/// +public interface IBook +{ + /// + /// Index document of the book + /// + IDocument Index { get; } + /// + /// Chapters of the book + /// + IReadOnlyList Chapters { get; } +} diff --git a/Source/BookGen.Api/V1/IBookgenServices.cs b/Source/BookGen.Api/V1/IBookgenServices.cs new file mode 100644 index 00000000..de11cedb --- /dev/null +++ b/Source/BookGen.Api/V1/IBookgenServices.cs @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Api.V1; + +/// +/// Represents services offered by the Bookgen application. +/// +public interface IBookgenServices +{ + /// + /// Creates a renderer based on the provided renderer options. + /// + /// The options to configure the renderer. + /// A renderer instance configured with the specified options. + IRenderer CreateRenderer(RendererOptions rendererOptions); + /// + /// Gets the output folder where the generated book files will be stored. + /// + IFileSystem OutputFolder { get; } + /// + /// Gets the asset source that provides access to the assets used by the Bookgen application. + /// + IAssetSource AssetSource { get; } + + /// + /// Gets the logger that can be used to log messages with different severity levels. + /// + IPluginLogger Logger { get; } + + /// + /// Gets the dynamic documentation service that provides access to the documentation of the Bookgen application. + /// + IDynamicDocumentation DynamicDocumentation { get; } +} diff --git a/Source/BookGen.Api/V1/IBuildPluginV1.cs b/Source/BookGen.Api/V1/IBuildPluginV1.cs new file mode 100644 index 00000000..26f811c4 --- /dev/null +++ b/Source/BookGen.Api/V1/IBuildPluginV1.cs @@ -0,0 +1,23 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Api.V1; + +/// +/// Represents a plugin that can be used during the build process of a book. +/// A single assembly must contain only one implementation of this interface. +/// If multiple implementations are found, an exception will be thrown. +/// +public interface IBuildPluginV1 +{ + /// + /// Builds the book using the provided IBook instance. + /// + /// The book to build. + /// The services offered by the Bookgen application. + /// A token to monitor for cancellation requests. + /// A task that represents the asynchronous build operation. The task result contains a boolean indicating whether the build was successful. + Task Build(IBook book, IBookgenServices bookgenServices, CancellationToken cancellationToken); +} diff --git a/Source/BookGen.Api/V1/IChapter.cs b/Source/BookGen.Api/V1/IChapter.cs new file mode 100644 index 00000000..d46ce15f --- /dev/null +++ b/Source/BookGen.Api/V1/IChapter.cs @@ -0,0 +1,21 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Api.V1; + +/// +/// Represents a chapter in a book +/// +public interface IChapter +{ + /// + /// Chapter title + /// + string Title { get; } + /// + /// Chapter contents + /// + IReadOnlyList Documents { get; } +} diff --git a/Source/BookGen.Api/V1/IDocument.cs b/Source/BookGen.Api/V1/IDocument.cs new file mode 100644 index 00000000..cccdb216 --- /dev/null +++ b/Source/BookGen.Api/V1/IDocument.cs @@ -0,0 +1,22 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Api.V1; + +/// +/// Represents a document in the book +/// +public interface IDocument +{ + /// + /// File path of the document + /// + string FilePath { get; } + /// + /// Reads the content of the document and its front matter asynchronously + /// + /// A tuple containing the content and front matter of the document + Task<(string content, IDocumentFrontMatter frontMatter)> ReadContent(); +} diff --git a/Source/BookGen.Api/V1/IDocumentFrontMatter.cs b/Source/BookGen.Api/V1/IDocumentFrontMatter.cs new file mode 100644 index 00000000..63cbbe46 --- /dev/null +++ b/Source/BookGen.Api/V1/IDocumentFrontMatter.cs @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Api.V1; + +/// +/// Document front matter metadata +/// +public interface IDocumentFrontMatter +{ + /// + /// Document title + /// + string Title { get; } + /// + /// Document tags + /// + IReadOnlyList Tags { get; } + /// + /// Custom template for the document, if any + /// + string? Template { get; } + /// + /// Additional document data + /// + IReadOnlyDictionary AdditionalData { get; } +} diff --git a/Source/BookGen.Api/V1/IDynamicDocumentation.cs b/Source/BookGen.Api/V1/IDynamicDocumentation.cs new file mode 100644 index 00000000..a72364e0 --- /dev/null +++ b/Source/BookGen.Api/V1/IDynamicDocumentation.cs @@ -0,0 +1,23 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Api.V1; + +/// +/// Represents accessor for dynamically generated documentation, such as commands and schemas, in markdown format. +/// +public interface IDynamicDocumentation +{ + /// + /// Gets the dynamically generated commands documentation in markdown format. + /// + /// The commands documentation in markdown format. + string GetCommandsMarkdown(); + /// + /// Gets the dynamically generated schemas documentation in markdown format. + /// + /// The schemas documentation in markdown format. + string GetSchemasMarkdown(); +} diff --git a/Source/BookGen.Api/V1/IFileSystem.cs b/Source/BookGen.Api/V1/IFileSystem.cs new file mode 100644 index 00000000..da019e30 --- /dev/null +++ b/Source/BookGen.Api/V1/IFileSystem.cs @@ -0,0 +1,27 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Api.V1; + +/// +/// Represents a file system abstraction for reading and writing files. +/// +public interface IFileSystem +{ + /// + /// Writes the specified content to a text file at the given relative path. + /// + /// The relative path to the file. + /// The content to write to the file. + /// A task that represents the asynchronous write operation. + Task WriteTextFile(string relativePath, string content); + + /// + /// Checks if a file exists at the specified relative path. + /// + /// The relative path to the file. + /// True if the file exists; otherwise, false. + bool FileExists(string relativePath); +} diff --git a/Source/BookGen.Api/V1/IPluginLogger.cs b/Source/BookGen.Api/V1/IPluginLogger.cs new file mode 100644 index 00000000..0722147d --- /dev/null +++ b/Source/BookGen.Api/V1/IPluginLogger.cs @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Api.V1; + +/// +/// Represents a logger that can be used to log messages with different severity levels. +/// +public interface IPluginLogger +{ + /// + /// Formats and writes an error log message. + /// + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}". + /// An object array that contains zero or more objects to format. + void LogError(string? message, params object?[] args); + /// + /// Formats and writes a critical log message. + /// + /// The exception to log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}". + /// An object array that contains zero or more objects to format. + void LogCritical(Exception? exception, string? message, params object?[] args); + /// + /// Formats and writes a critical log message. + /// + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}". + /// An object array that contains zero or more objects to format. + void LogCritical(string? message, params object?[] args); + /// + /// Formats and writes a debug log message. + /// + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}". + /// An object array that contains zero or more objects to format. + void LogDebug(string? message, params object?[] args); + /// + /// Formats and writes an information log message. + /// + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}". + /// An object array that contains zero or more objects to format. + void LogInformation(string? message, params object?[] args); + /// + /// Formats and writes a warning log message. + /// + /// The exception to log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}". + /// An object array that contains zero or more objects to format. + void LogWarning(Exception? exception, string? message, params object?[] args); + /// + /// Formats and writes a warning log message. + /// + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}". + /// An object array that contains zero or more objects to format. + void LogWarning(string? message, params object?[] args); + +} diff --git a/Source/BookGen.Api/V1/IRenderer.cs b/Source/BookGen.Api/V1/IRenderer.cs new file mode 100644 index 00000000..b9d031aa --- /dev/null +++ b/Source/BookGen.Api/V1/IRenderer.cs @@ -0,0 +1,43 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Api.V1; + +/// +/// Represents a renderer that can convert Markdown content to HTML. +/// +public interface IRenderer : IDisposable +{ + /// + /// Renders the specified Markdown content to HTML. + /// + /// The Markdown content to render. + /// The rendered HTML content. + string RenderMarkdownToRawHtml(string markdown); + + /// + /// Renders the specified Markdown content to HTML using the provided page template and document. + /// + /// The page template to use for rendering. + /// The document containing the Markdown content. + /// A task that represents the asynchronous operation. The task result contains the rendered HTML content. + Task RenderMarkdownToHtml(string pageTemplate, IDocument document); + + /// + /// Renders the specified Markdown content to HTML using the provided page template and document data. + /// + /// The page template to use for rendering. + /// The document data containing the Markdown content and front matter. + /// The rendered HTML content. + string RenderMarkdownToHtml(string pageTemplate, (string content, IDocumentFrontMatter frontMatter) docData); + + /// + /// Renders the specified Markdown content to HTML using the provided page template and tags. + /// + /// The page template to use for rendering. + /// The tags containing the content and metadata for rendering. + /// The rendered HTML content. + string RenderMarkdownToHtml(string pageTemplate, RenderTags tags); +} diff --git a/Source/BookGen.Api/V1/RenderTags.cs b/Source/BookGen.Api/V1/RenderTags.cs new file mode 100644 index 00000000..eed196f4 --- /dev/null +++ b/Source/BookGen.Api/V1/RenderTags.cs @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Api.V1; + +/// +/// Represents the tags used for rendering HTML content. +/// +public sealed class RenderTags +{ + /// + /// HTML document title + /// + public string Title { get; set; } = string.Empty; + + /// + /// HTML document content + /// + public string Content { get; set; } = string.Empty; + + /// + /// Host url for links and images + /// + public string Host { get; set; } = string.Empty; + + /// + /// Last modified date of the document + /// + public DateTime LastModified { get; set; } = DateTime.UnixEpoch; + + /// + /// Additional data that can be used in the rendering process. + /// + public Dictionary AdditionalData { get; set; } = new(); +} diff --git a/Source/BookGen.Api/V1/RendererOptions.cs b/Source/BookGen.Api/V1/RendererOptions.cs new file mode 100644 index 00000000..0a59c4dc --- /dev/null +++ b/Source/BookGen.Api/V1/RendererOptions.cs @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Api.V1; + +/// +/// Represents options for configuring the renderer used in the book generation process. +/// +public sealed class RendererOptions +{ + /// + /// Specifies how images should be processed during the rendering process. + /// + public enum ImageOption + { + /// + /// Indicates that images should be passed through without any modifications. + /// + Passtrough = 0, + /// + /// Indicates that images should be converted to PNG format during the rendering process. + /// + AsPng = 1, + /// + /// Indicates that images should be converted to WebP format during the rendering process. + /// + AsWebp = 2, + } + + /// + /// Specifies whether the first H1 heading in the content should be deleted during the rendering process. + /// + public bool DeleteFirstH1 { get; set; } + + /// + /// Specifies the base URL of the host where the book will be published. This is used for generating absolute URLs for links and resources in the rendered content. + /// + public string HostUrl { get; set; } = string.Empty; + + /// + /// Specifies whether supported links should be automatically embedded in the rendered content. + /// If set to true, links to supported content types (e.g., images, videos) will be embedded directly in the output. + /// + public bool AutoEmbedSupportedLinks { get; set; } = true; + + /// + /// Specifies the width to which images should be resized during the rendering process. + /// A value of -1 indicates that the original width should be preserved. + /// + public int ResizeWidth { get; set; } = -1; + + /// + /// Specifies the height to which images should be resized during the rendering process. + /// A value of -1 indicates that the original height should be preserved. + /// + public int ResizeHeight { get; set; } = -1; + + /// + /// Specifies how SVG images should be processed during the rendering process. + /// + public ImageOption SvgRecode { get; set; } = ImageOption.Passtrough; + + /// + /// Specifies how raster images (e.g., JPEG, PNG) should be processed during the rendering process. + /// + public ImageOption ImageRecode { get; set; } = ImageOption.Passtrough; + + /// + /// Specifies the number of heading levels by which to offset headings in the rendered content. + /// + public CssClasses CssClasses { get; set; } = new CssClasses(); +} diff --git a/Source/BookGen.Api/packages.lock.json b/Source/BookGen.Api/packages.lock.json new file mode 100644 index 00000000..a2617b35 --- /dev/null +++ b/Source/BookGen.Api/packages.lock.json @@ -0,0 +1,6 @@ +{ + "version": 2, + "dependencies": { + ".NETStandard,Version=v2.1": {} + } +} \ No newline at end of file diff --git a/Source/BookGen.Api/plugins/Readme.txt b/Source/BookGen.Api/plugins/Readme.txt new file mode 100644 index 00000000..a2ea83f5 --- /dev/null +++ b/Source/BookGen.Api/plugins/Readme.txt @@ -0,0 +1,3 @@ +This is the BookGen Plugins folder. Copy your Builder *.plugin files here. + +To use a plugin, you need to run the `build pugin` command with the plugin name. \ No newline at end of file diff --git a/Source/BookGen.Cli/Annotations/ArgumentAttribute.cs b/Source/BookGen.Cli/Annotations/ArgumentAttribute.cs index 6cb6f0b0..49df8d6b 100644 --- a/Source/BookGen.Cli/Annotations/ArgumentAttribute.cs +++ b/Source/BookGen.Cli/Annotations/ArgumentAttribute.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Cli/Annotations/CommandNameAttribute.cs b/Source/BookGen.Cli/Annotations/CommandNameAttribute.cs index b6f9f1f1..b3347ebd 100644 --- a/Source/BookGen.Cli/Annotations/CommandNameAttribute.cs +++ b/Source/BookGen.Cli/Annotations/CommandNameAttribute.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Cli/Annotations/ExitCodeAttribute.cs b/Source/BookGen.Cli/Annotations/ExitCodeAttribute.cs new file mode 100644 index 00000000..442bbac2 --- /dev/null +++ b/Source/BookGen.Cli/Annotations/ExitCodeAttribute.cs @@ -0,0 +1,21 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Cli.Annotations; + + +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +public sealed class ExitCodeAttribute : Attribute +{ + public int ExitCode { get; } + public string Description { get; } + + public ExitCodeAttribute(int exitCode, string description) + { + ExitCode = exitCode; + Description = description; + } + +} diff --git a/Source/BookGen.Cli/Annotations/SwitchAttribute.cs b/Source/BookGen.Cli/Annotations/SwitchAttribute.cs index e985b74b..2fb0be14 100644 --- a/Source/BookGen.Cli/Annotations/SwitchAttribute.cs +++ b/Source/BookGen.Cli/Annotations/SwitchAttribute.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -10,6 +10,7 @@ public sealed class SwitchAttribute : Attribute { public string LongName { get; } public string ShortName { get; } + public required bool Required { get; set; } public SwitchAttribute(string shortName, string longName) { diff --git a/Source/BookGen.Cli/ArgumentBase.cs b/Source/BookGen.Cli/ArgumentBase.cs index 5ae90694..9780adba 100644 --- a/Source/BookGen.Cli/ArgumentBase.cs +++ b/Source/BookGen.Cli/ArgumentBase.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Cli/ArgumentJsonItem.cs b/Source/BookGen.Cli/ArgumentJsonItem.cs index 1e118531..7a4e36d9 100644 --- a/Source/BookGen.Cli/ArgumentJsonItem.cs +++ b/Source/BookGen.Cli/ArgumentJsonItem.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Cli/ArgumentParser.cs b/Source/BookGen.Cli/ArgumentParser.cs index 0893b4de..2e266904 100644 --- a/Source/BookGen.Cli/ArgumentParser.cs +++ b/Source/BookGen.Cli/ArgumentParser.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Cli/ArgumentParsing/ArgumentBag.cs b/Source/BookGen.Cli/ArgumentParsing/ArgumentBag.cs index be393be2..b3363088 100644 --- a/Source/BookGen.Cli/ArgumentParsing/ArgumentBag.cs +++ b/Source/BookGen.Cli/ArgumentParsing/ArgumentBag.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Cli/ArgumentParsing/ArgumentParser.cs b/Source/BookGen.Cli/ArgumentParsing/ArgumentParser.cs index f12746b1..ad8c14f3 100644 --- a/Source/BookGen.Cli/ArgumentParsing/ArgumentParser.cs +++ b/Source/BookGen.Cli/ArgumentParsing/ArgumentParser.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Cli/ArgumentParsing/Autocomplete.cs b/Source/BookGen.Cli/ArgumentParsing/Autocomplete.cs index 4a6ed1d5..21bef903 100644 --- a/Source/BookGen.Cli/ArgumentParsing/Autocomplete.cs +++ b/Source/BookGen.Cli/ArgumentParsing/Autocomplete.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Cli/ArgumentParsing/ValueConverter.cs b/Source/BookGen.Cli/ArgumentParsing/ValueConverter.cs index 2a408582..5479e9ad 100644 --- a/Source/BookGen.Cli/ArgumentParsing/ValueConverter.cs +++ b/Source/BookGen.Cli/ArgumentParsing/ValueConverter.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Cli/AsyncCommand.cs b/Source/BookGen.Cli/AsyncCommand.cs index b789a8ad..f78f728c 100644 --- a/Source/BookGen.Cli/AsyncCommand.cs +++ b/Source/BookGen.Cli/AsyncCommand.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -10,11 +10,11 @@ namespace BookGen.Cli; /// public abstract class AsyncCommand : ICommand { - public abstract Task ExecuteAsync(IReadOnlyList context); + public abstract Task ExecuteAsync(IReadOnlyList context, CancellationToken token); - Task ICommand.ExecuteAsync(ArgumentsBase arguments, IReadOnlyList context) + Task ICommand.ExecuteAsync(ArgumentsBase arguments, IReadOnlyList context, CancellationToken token) { - return ExecuteAsync(context); + return ExecuteAsync(context, token); } public virtual SupportedOs SupportedOs diff --git a/Source/BookGen.Cli/AsyncCommandWithArgs.cs b/Source/BookGen.Cli/AsyncCommandWithArgs.cs index 824d8c10..8b176f7e 100644 --- a/Source/BookGen.Cli/AsyncCommandWithArgs.cs +++ b/Source/BookGen.Cli/AsyncCommandWithArgs.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -12,11 +12,11 @@ namespace BookGen.Cli; public abstract class AsyncCommand : ICommand where TArguments : ArgumentsBase { - public abstract Task ExecuteAsync(TArguments arguments, IReadOnlyList context); + public abstract Task ExecuteAsync(TArguments arguments, IReadOnlyList context, CancellationToken token); - Task ICommand.ExecuteAsync(ArgumentsBase arguments, IReadOnlyList context) + Task ICommand.ExecuteAsync(ArgumentsBase arguments, IReadOnlyList context, CancellationToken token) { - return ExecuteAsync((TArguments)arguments, context); + return ExecuteAsync((TArguments)arguments, context, token); } public virtual SupportedOs SupportedOs diff --git a/Source/BookGen.Cli/BookGen.Cli.csproj b/Source/BookGen.Cli/BookGen.Cli.csproj index 8e569b4b..d9942b02 100644 --- a/Source/BookGen.Cli/BookGen.Cli.csproj +++ b/Source/BookGen.Cli/BookGen.Cli.csproj @@ -12,6 +12,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Source/BookGen.Cli/Command.cs b/Source/BookGen.Cli/Command.cs index 73f1efbc..b409a6a3 100644 --- a/Source/BookGen.Cli/Command.cs +++ b/Source/BookGen.Cli/Command.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -17,7 +17,7 @@ public abstract class Command : ICommand /// reurtn value to shell public abstract int Execute(IReadOnlyList context); - Task ICommand.ExecuteAsync(ArgumentsBase arguments, IReadOnlyList context) + Task ICommand.ExecuteAsync(ArgumentsBase arguments, IReadOnlyList context, CancellationToken token) { return Task.FromResult(Execute(context)); } diff --git a/Source/BookGen.Cli/CommandHelpProvider.cs b/Source/BookGen.Cli/CommandHelpProvider.cs new file mode 100644 index 00000000..560ed0d7 --- /dev/null +++ b/Source/BookGen.Cli/CommandHelpProvider.cs @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using BookGen.Cli.OpenCli; +using BookGen.Cli.OpenCli.Draft; + +namespace BookGen.Cli; + +public class CommandHelpProvider : ICommandHelpProvider +{ + private Document _document; + + public CommandHelpProvider() + { + _document = new Document + { + Info = new CliInfo + { + Title = string.Empty, + Version = string.Empty, + }, + Command = new OpenCli.Draft.Command + { + Name = string.Empty, + }, + Opencli = "0.1" + }; + } + + public void CommandsChanged(Document openCliDocument) + { + _document = openCliDocument; + } + + public virtual string GetHelp(string commandName) + { + OpenCli.Draft.Command? command = _document.Commands?.FirstOrDefault(c => c.Name == commandName); + if (command == null) + return $"** No Help found for: {commandName} **"; + + return MarkdownGenerator.GenerateMarkdown(command, 1); + } +} + diff --git a/Source/BookGen.Cli/CommandRunner.cs b/Source/BookGen.Cli/CommandRunner.cs index 41948d53..d438272e 100644 --- a/Source/BookGen.Cli/CommandRunner.cs +++ b/Source/BookGen.Cli/CommandRunner.cs @@ -1,15 +1,17 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; -using System.Runtime.InteropServices; using System.Text.Json; -using BookGen.Cli.Annotations; using BookGen.Cli.ArgumentParsing; +using BookGen.Cli.Internals; +using BookGen.Cli.OpenCli; +using BookGen.Cli.OpenCli.Draft; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -19,90 +21,47 @@ namespace BookGen.Cli; public sealed class CommandRunner { private readonly JsonSerializerOptions _serializerOptions; - private readonly Dictionary _commands; + private readonly CommandTree _commands; private readonly IServiceProvider _serviceProvider; private readonly ICommandHelpProvider _helpProvider; private readonly ILogger _log; private readonly CommandRunnerSettings _settings; + private readonly List _globalOptionParsers; private readonly SupportedOs _currentOs; - private string? _defaultCommandName; - - private static string GetCommandName(Type t) - { - CommandNameAttribute? nameAttribure = t.GetCustomAttribute(); - return nameAttribure?.Name - ?? throw new InvalidOperationException($"Command {t.FullName} is missing a {nameof(CommandNameAttribute)}"); - } public IValidationContext ValidationContext { get; set; } + public Func, Task>? BeforeRunHook { get; set; } - private static SupportedOs GetCurrentOs() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return SupportedOs.Windows; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - return SupportedOs.Linux; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - return SupportedOs.OsX; - else - return SupportedOs.None; - } - private static Type? GetArgumentType(Type cmd) - { - Type originalType = cmd; + private void DefaultExceptionHandler(Exception obj) + => _log.LogCritical(obj, obj.Message); - // Walk up the inheritance hierarchy to find Command or AsyncCommand - while (cmd != null && cmd != typeof(object)) + private bool TryProvideInternal(Type parameterType, string commandName, [NotNullWhen(true)] out object? instance) + { + if (parameterType == typeof(BranchItemsProvider)) { - if (cmd.IsGenericType) + instance = new BranchItemsProvider() { - Type baseGeneric = cmd.GetGenericTypeDefinition(); - - if (baseGeneric == typeof(Command<>) || baseGeneric == typeof(AsyncCommand<>)) - { - // Get the concrete TArguments - Type tArguments = cmd.GetGenericArguments()[0]; -#if DEBUG - Debug.WriteLine($"Via generics: {tArguments.FullName}"); -#endif - return tArguments; - } - } - if (cmd.BaseType == null) - { - break; - } - cmd = cmd.BaseType; + BranchName = commandName, + BranchItems = _commands.CommandNames.Where(c => c.StartsWith(commandName)).ToList() + }; + return true; } - MethodInfo? method = originalType.GetMethod(nameof(AsyncCommand.ExecuteAsync)) - ?? originalType.GetMethod(nameof(Command.Execute)); - - if (method == null) - throw new InvalidOperationException($"Command {originalType.FullName} is missing Exetutable method"); - - Type? parameter = method - ?.GetParameters() - .FirstOrDefault(p => p.ParameterType.IsAssignableTo(typeof(ArgumentsBase))) - ?.ParameterType; - -#if DEBUG - Debug.WriteLine($"Via methodinfo: {parameter?.FullName}"); -#endif - - return parameter; - } + if (parameterType == typeof(ICommandHelpProvider)) + { + instance = _helpProvider; + return true; + } - private void DefaultExceptionHandler(Exception obj) - { - _log.LogCritical(obj, obj.Message); + instance = null; + return false; } - private ICommand CreateCommand(string commandName) + private ICommand CreateCommand(Type commandType, string commandName) { - ConstructorInfo constructor = _commands[commandName] + ConstructorInfo constructor = commandType .GetConstructors(BindingFlags.Public | BindingFlags.Instance) .OrderByDescending(c => c.GetParameters().Length) .First(); @@ -112,6 +71,12 @@ private ICommand CreateCommand(string commandName) { FromKeyedServicesAttribute? keyAttribute = param.GetCustomAttribute(); + if (TryProvideInternal(param.ParameterType, commandName, out object? dependency)) + { + constructorParameters.Add(dependency); + continue; + } + object parameterInstance = keyAttribute != null ? _serviceProvider.GetRequiredKeyedService(param.ParameterType, keyAttribute.Key) : _serviceProvider.GetRequiredService(param.ParameterType); @@ -119,14 +84,14 @@ private ICommand CreateCommand(string commandName) constructorParameters.Add(parameterInstance); } - var instance = Activator.CreateInstance(_commands[commandName], constructorParameters.ToArray()) + var instance = Activator.CreateInstance(commandType, constructorParameters.ToArray()) ?? throw new InvalidOperationException(); return (ICommand)instance; } public CommandRunner(IServiceProvider serviceProvider, - ICommandHelpProvider helpProvider, + ICommandHelpProvider helpProvider, ILogger log, CommandRunnerSettings settings) { @@ -136,49 +101,62 @@ public CommandRunner(IServiceProvider serviceProvider, AllowTrailingCommas = true, WriteIndented = true }; - _commands = new Dictionary(); + _globalOptionParsers = new List(); + _commands = new CommandTree(); _serviceProvider = serviceProvider; _helpProvider = helpProvider; _log = log; _settings = settings; ExceptionHandlerDelegate = DefaultExceptionHandler; - _currentOs = GetCurrentOs(); + _currentOs = Helpers.GetCurrentOs(); ValidationContext = new IoCValidationContext(serviceProvider); - ConfigureUtfSupport(_settings.EnableUtf8Output); - } - - private static void ConfigureUtfSupport(bool enableUtf8Output) - { - if (enableUtf8Output) - { - Console.OutputEncoding = System.Text.Encoding.UTF8; - Console.InputEncoding = System.Text.Encoding.UTF8; - } + Helpers.ConfigureUtfSupport(_settings.EnableUtf8Output); } public Action ExceptionHandlerDelegate { get; set; } public CommandRunner AddCommand() where TCommand : ICommand { - string name = GetCommandName(typeof(TCommand)); - _commands.Add(name.ToLower(), typeof(TCommand)); + string name = typeof(TCommand).GetCommandName(); + _commands.Add(name, typeof(TCommand)); return this; } public CommandRunner AddDefaultCommand() where TCommand : ICommand { - string name = GetCommandName(typeof(TCommand)); - if (!_commands.ContainsKey(name)) + string name = typeof(TCommand).GetCommandName(); + + if (_commands.IsDefaultCommandSet) + throw new InvalidOperationException("Default command has already been set"); + + _commands.AddDefault(typeof(TCommand)); + return this; + } + + public CommandRunner AddGlobalOptionParser(GlobalOptionParser parser) + { + _globalOptionParsers.Add(parser); + return this; + } + + public CommandRunner AddGlobalOptionParser() where TParser : GlobalOptionParser, new() + { + _globalOptionParsers.Add(new TParser()); + return this; + } + + public IEnumerable GetGlobalOptions() + { + foreach (GlobalOptionParser parser in _globalOptionParsers) { - AddCommand(); + yield return parser.ShortName; + yield return parser.LongName; } - _defaultCommandName = name; - return this; } - public CommandRunner AddCommandsFrom(Assembly assembly) + public CommandRunner AddCommandsFrom(Assembly assembly, bool includeDefault) { IEnumerable commands = assembly .GetTypes() @@ -187,8 +165,12 @@ public CommandRunner AddCommandsFrom(Assembly assembly) foreach (Type? command in commands) { - string name = GetCommandName(command); - if (!_commands.ContainsKey(name)) + string name = command.GetCommandName(); + + if (_commands.GetDefaultCommandName() == name && !includeDefault) + continue; + + if (!_commands.ContainsCommand(name)) { _commands.Add(name.ToLower(), command); } @@ -198,15 +180,15 @@ public CommandRunner AddCommandsFrom(Assembly assembly) } public IEnumerable CommandNames - => _commands.Keys; + => _commands.CommandNames; public string[] GetAutoCompleteItems(string commandName) { - if (_commands.TryGetValue(commandName, out Type? value)) + if (_commands.TryGetCommand(commandName, out Type? value)) { Type type = value; - Type? args = GetArgumentType(type); + Type? args = type.GetArgumentType(); if (args != null) { @@ -216,24 +198,59 @@ public string[] GetAutoCompleteItems(string commandName) return Array.Empty(); } + public Document GenerateOpenCliDocs() + { + if (!_commands.IsDefaultCommandSet) + throw new InvalidOperationException("Default command hasn't been set"); + + IEnumerable<(Type Value, Type?)> commands = _commands.CommandTypes.Select(x => (x, x.GetArgumentType())); + + return OpenCliDraftGenerator.GenerateOpenCli(_settings.ProgramMetaData.AppName, + _settings.ProgramMetaData.Version, + _commands.GetDefaultCommand(), + _globalOptionParsers, + commands, + _commands.BranchCommandNames); + } + public async Task Run(IReadOnlyList args) { + _helpProvider.CommandsChanged(GenerateOpenCliDocs()); + int skipCount = 1; try { string commandName; if (args.Count > 0) { - commandName = args[0].ToLower(); + if (args[0].StartsWith('-')) + { + commandName = _commands.GetDefaultCommandName(); + skipCount = 0; + } + else + { + commandName = args[0].ToLower(); + } } else { - if (string.IsNullOrEmpty(_defaultCommandName)) + if (!_commands.IsDefaultCommandSet) throw new InvalidOperationException("Default command hasn't been setup"); - commandName = _defaultCommandName; + commandName = _commands.GetDefaultCommandName(); + skipCount = 0; + } + + HashSet parsedGlobals = new(); + foreach (var parser in _globalOptionParsers) + { + if (parser.TryParseGlobalOption(args, out string? globalOption)) + { + parsedGlobals.Add(globalOption); + } } - var argsToParse = args.Skip(1).ToArray(); + List argsToParse = Helpers.GetArgsToParse(args, parsedGlobals, skipCount); return await RunCommand(commandName, argsToParse); } @@ -257,14 +274,18 @@ private async Task LoadFromJsonFile(string jsonFile) public async Task RunCommand(string commandName, IReadOnlyList argsToParse) { - if (!_commands.TryGetValue(commandName, out Type? value)) + Type? commandType; + if (!_commands.TryGetDefaultCommand(commandName, out commandType)) { - _log.LogCritical(_settings.UnknownCommandCodeAndMessage.message); - return _settings.UnknownCommandCodeAndMessage.code; + if (!_commands.TryGetCommand(commandName, out commandType)) + { + _log.LogCritical(_settings.UnknownCommandCodeAndMessage.message + " {cmdName}", commandName); + return _settings.UnknownCommandCodeAndMessage.code; + } } - Type? argumentType = GetArgumentType(value); - ICommand command = CreateCommand(commandName); + Type? argumentType = commandType.GetArgumentType(); + ICommand command = CreateCommand(commandType, commandName); if (!command.SupportedOs.HasFlag(_currentOs)) { @@ -272,28 +293,32 @@ public async Task RunCommand(string commandName, IReadOnlyList args return _settings.PlatformNotSupportedExitCode; } - if (argumentType == null) - return await command.ExecuteAsync(ArgumentsBase.Empty, argsToParse); + using (var tokenSource = new ConsoleCancellationTokenSource()) + { + if (argumentType == null) + return await command.ExecuteAsync(ArgumentsBase.Empty, argsToParse, tokenSource.Token); - string jsonFileName = Path.ChangeExtension(commandName, ".json"); + string jsonFileName = Path.ChangeExtension(commandName, ".json"); - string argsJson = Path.Combine(Environment.CurrentDirectory, jsonFileName); + string argsJson = Path.Combine(Environment.CurrentDirectory, jsonFileName); - if (argsToParse.Count < 1 - && File.Exists(argsJson)) - { - _log.LogInformation("Loading arguments from {filename}...", jsonFileName); - ArgumentJsonItem[] items = await LoadFromJsonFile(argsJson); + if (argsToParse.Count < 1 + && File.Exists(argsJson)) + { + _log.LogInformation("Loading arguments from {filename}...", jsonFileName); + ArgumentJsonItem[] items = await LoadFromJsonFile(argsJson); - return await ExecuteMultiple(items, argumentType, command, commandName); + return await ExecuteMultiple(items, argumentType, command, commandName, tokenSource.Token); + } + return await ExecuteSingle(argsToParse, argumentType, command, commandName, tokenSource.Token); } - return await ExecuteSingle(argsToParse, argumentType, command, commandName); } private async Task ExecuteSingle(IReadOnlyList argsToParse, Type argumentType, ICommand command, - string commandName) + string commandName, + CancellationToken token) { ArgumentsBase args = ArgumentsBase.Empty; ArgumentParser parser = new(argumentType, _log); @@ -307,7 +332,7 @@ private async Task ExecuteSingle(IReadOnlyList argsToParse, if (_settings.PrintHelpOnBadArgs) { - string help = _helpProvider.GetHelp(commandName, argumentType); + string help = _helpProvider.GetHelp(commandName); _log.LogInformation("Command help:\r\n{help}", help); } else @@ -322,18 +347,19 @@ private async Task ExecuteSingle(IReadOnlyList argsToParse, if (BeforeRunHook != null) await BeforeRunHook.Invoke(args, argsToParse); - return await command.ExecuteAsync(args, argsToParse); + return await command.ExecuteAsync(args, argsToParse, token); } private async Task ExecuteMultiple(ArgumentJsonItem[] items, Type argumentType, ICommand command, - string commandName) + string commandName, + CancellationToken token) { foreach (ArgumentJsonItem item in items) { _log.LogInformation("Executing {name} from json file...", item.Name); - int exitcode = await ExecuteSingle(item.Arguments, argumentType, command, commandName); + int exitcode = await ExecuteSingle(item.Arguments, argumentType, command, commandName, token); if (exitcode != 0) { _log.LogCritical("Failed to execute {name}. Exit code: {exitcode}", item.Name, exitcode); diff --git a/Source/BookGen.Cli/CommandRunnerProxy.cs b/Source/BookGen.Cli/CommandRunnerProxy.cs index 821fc84a..dac1795a 100644 --- a/Source/BookGen.Cli/CommandRunnerProxy.cs +++ b/Source/BookGen.Cli/CommandRunnerProxy.cs @@ -1,20 +1,26 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using BookGen.Cli.OpenCli.Draft; + namespace BookGen.Cli; public sealed class CommandRunnerProxy : ICommandRunnerProxy { private Func? _autoComplete; private Func, Task>? _runCommand; + private Func? _openCliGenerator; + private IEnumerable? _globalOptions; public void ConfigureWith(CommandRunner runner) { - CommandNames = runner.CommandNames; + CommandNames = runner.CommandNames.Order(); _autoComplete = runner.GetAutoCompleteItems; _runCommand = runner.RunCommand; + _openCliGenerator = runner.GenerateOpenCliDocs; + _globalOptions = runner.GetGlobalOptions(); } public IEnumerable CommandNames @@ -23,6 +29,10 @@ public IEnumerable CommandNames private set; } + public IEnumerable GlobalOptions + => _globalOptions + ?? throw new InvalidOperationException("Provider hasn't been setup correctly"); + public string[] GetAutoCompleteItems(string commandName) => _autoComplete?.Invoke(commandName) ?? throw new InvalidOperationException("Provider hasn't been setup correctly"); @@ -34,4 +44,8 @@ public async Task RunCommand(string commandName, IReadOnlyList args return await _runCommand.Invoke(commandName, argsToParse); } + + public Document GetOpenCliDocs() + => _openCliGenerator?.Invoke() + ?? throw new InvalidOperationException("Provider hasn't been setup correctly"); } diff --git a/Source/BookGen.Cli/CommandRunnerSettings.cs b/Source/BookGen.Cli/CommandRunnerSettings.cs index 18b9c848..0e75d3ab 100644 --- a/Source/BookGen.Cli/CommandRunnerSettings.cs +++ b/Source/BookGen.Cli/CommandRunnerSettings.cs @@ -13,6 +13,7 @@ public sealed record class CommandRunnerSettings public required int ExcptionExitCode { get; init; } public required bool EnableUtf8Output { get; init; } public required bool PrintHelpOnBadArgs { get; init; } + public required ProgramMetaData ProgramMetaData { get; init; } public static readonly CommandRunnerSettings Default = new() { @@ -22,5 +23,10 @@ public sealed record class CommandRunnerSettings ExcptionExitCode = -4, EnableUtf8Output = true, PrintHelpOnBadArgs = false, + ProgramMetaData = new ProgramMetaData + { + AppName = "", + Version = new Version() + } }; } diff --git a/Source/BookGen.Cli/CommandWithArgs.cs b/Source/BookGen.Cli/CommandWithArgs.cs index c477383c..b4c01128 100644 --- a/Source/BookGen.Cli/CommandWithArgs.cs +++ b/Source/BookGen.Cli/CommandWithArgs.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -14,7 +14,7 @@ public abstract class Command : ICommand { public abstract int Execute(TArguments arguments, IReadOnlyList context); - Task ICommand.ExecuteAsync(ArgumentsBase arguments, IReadOnlyList context) + Task ICommand.ExecuteAsync(ArgumentsBase arguments, IReadOnlyList context, CancellationToken token) { return Task.FromResult(Execute((TArguments)arguments, context)); } diff --git a/Source/BookGen.Cli/ConsoleCancellationTokenSource.cs b/Source/BookGen.Cli/ConsoleCancellationTokenSource.cs new file mode 100644 index 00000000..fff35913 --- /dev/null +++ b/Source/BookGen.Cli/ConsoleCancellationTokenSource.cs @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Cli; + +internal sealed class ConsoleCancellationTokenSource : IDisposable +{ + private readonly CancellationTokenSource _cancellationTokenSource; + private bool _disposed; + + public ConsoleCancellationTokenSource() + { + _cancellationTokenSource = new CancellationTokenSource(); + Console.CancelKeyPress += OnCancelKeyPress; + } + + public CancellationToken Token + { + get + { + ObjectDisposedException.ThrowIf(_disposed, this); + return _cancellationTokenSource.Token; + } + } + + private void OnCancelKeyPress(object? sender, ConsoleCancelEventArgs e) + { + _cancellationTokenSource.Cancel(); + e.Cancel = true; + } + + public void Dispose() + { + _cancellationTokenSource.Dispose(); + Console.CancelKeyPress -= OnCancelKeyPress; + _disposed = true; + } +} diff --git a/Source/BookGen.Cli/CrashDumpFactory.cs b/Source/BookGen.Cli/CrashDumpFactory.cs index 71ab36cb..ea5d7130 100644 --- a/Source/BookGen.Cli/CrashDumpFactory.cs +++ b/Source/BookGen.Cli/CrashDumpFactory.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Cli/ICommand.cs b/Source/BookGen.Cli/ICommand.cs index e95ea8b0..82492d13 100644 --- a/Source/BookGen.Cli/ICommand.cs +++ b/Source/BookGen.Cli/ICommand.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -7,6 +7,6 @@ namespace BookGen.Cli; public interface ICommand { - Task ExecuteAsync(ArgumentsBase arguments, IReadOnlyList context); + Task ExecuteAsync(ArgumentsBase arguments, IReadOnlyList context, CancellationToken token); SupportedOs SupportedOs { get; } } diff --git a/Source/BookGen.Cli/ICommandHelpProvider.cs b/Source/BookGen.Cli/ICommandHelpProvider.cs new file mode 100644 index 00000000..f3a069b0 --- /dev/null +++ b/Source/BookGen.Cli/ICommandHelpProvider.cs @@ -0,0 +1,14 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using BookGen.Cli.OpenCli.Draft; + +namespace BookGen.Cli; + +public interface ICommandHelpProvider +{ + void CommandsChanged(Document openCliDocument); + string GetHelp(string commandName); +} diff --git a/Source/BookGen.Cli/ICommandRunnerProxy.cs b/Source/BookGen.Cli/ICommandRunnerProxy.cs index efd7599c..cc8114da 100644 --- a/Source/BookGen.Cli/ICommandRunnerProxy.cs +++ b/Source/BookGen.Cli/ICommandRunnerProxy.cs @@ -1,14 +1,17 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using BookGen.Cli.OpenCli.Draft; + namespace BookGen.Cli; public interface ICommandRunnerProxy { IEnumerable CommandNames { get; } - + IEnumerable GlobalOptions { get; } string[] GetAutoCompleteItems(string commandName); Task RunCommand(string commandName, IReadOnlyList argsToParse); + Document GetOpenCliDocs(); } diff --git a/Source/BookGen.Cli/IGlobalOptionParser.cs b/Source/BookGen.Cli/IGlobalOptionParser.cs new file mode 100644 index 00000000..ced373a1 --- /dev/null +++ b/Source/BookGen.Cli/IGlobalOptionParser.cs @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Diagnostics.CodeAnalysis; + +namespace BookGen.Cli; + +public abstract class GlobalOptionParser +{ + public string LongName { get; } + public string ShortName { get; } + + public GlobalOptionParser(string shortName, string longName) + { + if (longName.StartsWith('-')) + throw new ArgumentException("Long name cannot start with '--'", nameof(longName)); + + if (shortName.StartsWith('-')) + throw new ArgumentException("Short name cannot start with '-'", nameof(shortName)); + + ShortName = $"-{shortName}"; + LongName = $"--{longName}"; + } + + public bool TryParseGlobalOption(IReadOnlyList args, [NotNullWhen(true)] out string? parsedOne) + { + bool handle = false; + parsedOne = null; + if (args.Contains(ShortName)) + { + handle = true; + parsedOne = ShortName; + } + else if (args.Contains(LongName)) + { + handle = true; + parsedOne = LongName; + } + + if (handle) + { + OnOptionWasPresent(); + } + + return handle; + } + + protected abstract void OnOptionWasPresent(); +} diff --git a/Source/BookGen.Cli/IHelpProvider.cs b/Source/BookGen.Cli/IHelpProvider.cs deleted file mode 100644 index a50b0c9e..00000000 --- a/Source/BookGen.Cli/IHelpProvider.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace BookGen.Cli; - -public interface ICommandHelpProvider -{ - string GetHelp(string commandName, Type argumentType); -} diff --git a/Source/BookGen.Cli/IValidationContext.cs b/Source/BookGen.Cli/IValidationContext.cs index b2b7f54d..cf09d728 100644 --- a/Source/BookGen.Cli/IValidationContext.cs +++ b/Source/BookGen.Cli/IValidationContext.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Cli/Internals/BranchCommand.cs b/Source/BookGen.Cli/Internals/BranchCommand.cs new file mode 100644 index 00000000..02019c4a --- /dev/null +++ b/Source/BookGen.Cli/Internals/BranchCommand.cs @@ -0,0 +1,100 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using BookGen.Cli.Annotations; + +namespace BookGen.Cli.Internals; + +[CommandName("")] +internal class BranchCommand : AsyncCommand +{ + internal class TreeNode + { + public string Name { get; } + public List Children { get; } + + public TreeNode(string name) + { + Name = name; + Children = new List(); + } + + public override string ToString() + => Name; + } + + private readonly TreeNode _tree; + private readonly BranchItemsProvider _branchItemsProvider; + private readonly ICommandRunnerProxy _commandRunnerProxy; + + private TreeNode BuildTree(List branchItems) + { + TreeNode rootNode = new TreeNode(""); + foreach (var item in branchItems) + { + var parts = item.Split(' '); + TreeNode currentNode = rootNode; + + foreach (var part in parts) + { + TreeNode? existingChild = currentNode?.Children.FirstOrDefault(c => c.Name == part); + if (existingChild == null) + { + var newNode = new TreeNode(part); + currentNode?.Children.Add(newNode); + currentNode = newNode; + } + else + { + currentNode = existingChild; + } + } + } + + return rootNode; + } + + public BranchCommand(BranchItemsProvider branchItemsProvider, ICommandRunnerProxy commandRunnerProxy) + { + _tree = BuildTree(branchItemsProvider.BranchItems); + _branchItemsProvider = branchItemsProvider; + _commandRunnerProxy = commandRunnerProxy; + } + + public override async Task ExecuteAsync(IReadOnlyList context, CancellationToken token) + { + if (context.Count > 0) + { + string commandToRun = $"{_branchItemsProvider.BranchName} {context[0]}"; + if (_branchItemsProvider.BranchItems.Contains(commandToRun, StringComparer.OrdinalIgnoreCase)) + { + return await _commandRunnerProxy.RunCommand(commandToRun, context.Skip(1).ToList()); + } + } + + Console.WriteLine("Available subcommands: "); + Console.WriteLine(); + foreach (TreeNode item in _tree.Children) + { + PrintNode(item, 0); + } + return 0; + } + + private static void PrintNode(TreeNode node, int level) + { + string spacing = new string(' ', level * 2); + Console.WriteLine($"{spacing}{node.Name}"); + foreach (TreeNode child in node.Children) + { + string childSpacing = new string(' ', level + 1 * 2); + Console.WriteLine($"{childSpacing}├ {child.Name}"); + if (child.Children.Count > 0) + { + PrintNode(child, level + 1); + } + } + } +} diff --git a/Source/BookGen.Cli/Internals/BranchItemsProvider.cs b/Source/BookGen.Cli/Internals/BranchItemsProvider.cs new file mode 100644 index 00000000..2447ecff --- /dev/null +++ b/Source/BookGen.Cli/Internals/BranchItemsProvider.cs @@ -0,0 +1,12 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Cli.Internals; + +internal class BranchItemsProvider +{ + public required string BranchName { get; set; } + public required List BranchItems { get; set; } +} diff --git a/Source/BookGen.Cli/Internals/CommandTree.cs b/Source/BookGen.Cli/Internals/CommandTree.cs new file mode 100644 index 00000000..b035d3b6 --- /dev/null +++ b/Source/BookGen.Cli/Internals/CommandTree.cs @@ -0,0 +1,118 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +using BookGen.Cli.Annotations; +using BookGen.Cli.OpenCli.Draft; + +namespace BookGen.Cli.Internals; + +internal sealed class CommandTree +{ + private readonly Dictionary _commands; + private Type? _defaultCommandType; + private string? _defaultCommandName; + + public CommandTree() + { + _commands = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + private static bool TryGetBranchNames(string name, out string[] branches) + { + string[] parts = name.Split(' ', StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 1) + { + branches = Array.Empty(); + return false; + } + var items = new List(); + for (int i = 1; i < parts.Length; i++) + { + string branch = string.Join(' ', parts, 0, i); + items.Add(branch); + } + branches = [.. items]; + return branches.Length > 0; + } + + public Type GetCommandByName(string name) + { + return _commands.TryGetValue(name, out Type? commandType) + ? commandType + : throw new KeyNotFoundException($"Command '{name}' not found."); + } + + public void AddDefault(Type type) + { + _defaultCommandType = type; + _defaultCommandName = type.GetCommandName(); + } + + public void Add(string name, Type type) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentException("Command name cannot be null or whitespace.", nameof(name)); + } + if (TryGetBranchNames(name, out string[] branches)) + { + foreach (var branch in branches) + { + if (!_commands.ContainsKey(branch)) + { + _commands[branch] = typeof(BranchCommand); + } + } + } + _commands[name] = type; + } + + public bool ContainsCommand(string name) + => _commands.ContainsKey(name); + + public IEnumerable CommandNames + => _commands.Keys; + + public IEnumerable CommandTypes + => _commands.Values; + + public bool TryGetCommand(string name, [NotNullWhen(true)] out Type? commandType) + => _commands.TryGetValue(name, out commandType); + + public bool TryGetDefaultCommand(string commandName, [NotNullWhen(true)] out Type? value) + { + if (string.Equals(commandName, _defaultCommandName, StringComparison.OrdinalIgnoreCase) + && _defaultCommandType != null) + { + value = _defaultCommandType; + return true; + } + value = null; + return false; + } + + public Type GetDefaultCommand() + { + return _defaultCommandType + ?? throw new InvalidOperationException("Default command hasn't been set"); + } + + public string GetDefaultCommandName() + { + return _defaultCommandName + ?? throw new InvalidOperationException("Default command hasn't been set"); + } + + public IEnumerable BranchCommandNames + => _commands.Where(k => k.Value == typeof(BranchCommand)) + .Select(k => k.Key) + .Order(); + + public bool IsDefaultCommandSet + => _defaultCommandType is not null; +} diff --git a/Source/BookGen.Cli/Internals/Extensions.cs b/Source/BookGen.Cli/Internals/Extensions.cs new file mode 100644 index 00000000..dec26bad --- /dev/null +++ b/Source/BookGen.Cli/Internals/Extensions.cs @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Diagnostics; +using System.Reflection; + +using BookGen.Cli.Annotations; + +namespace BookGen.Cli.Internals; + +internal static class Extensions +{ + public static string GetCommandName(this Type command) + { + CommandNameAttribute? nameAttribure = command.GetCustomAttribute(); + return nameAttribure?.Name + ?? throw new InvalidOperationException($"Command {command.FullName} is missing a {nameof(CommandNameAttribute)}"); + } + + + public static Type? GetArgumentType(this Type command) + { + Type originalType = command; + + // Walk up the inheritance hierarchy to find Command or AsyncCommand + while (command != null && command != typeof(object)) + { + if (command.IsGenericType) + { + Type baseGeneric = command.GetGenericTypeDefinition(); + + if (baseGeneric == typeof(Command<>) || baseGeneric == typeof(AsyncCommand<>)) + { + // Get the concrete TArguments + Type tArguments = command.GetGenericArguments()[0]; +#if DEBUG + Debug.WriteLine($"Via generics: {tArguments.FullName}"); +#endif + return tArguments; + } + } + if (command.BaseType == null) + { + break; + } + command = command.BaseType; + } + + MethodInfo? method = originalType.GetMethod(nameof(AsyncCommand.ExecuteAsync)) + ?? originalType.GetMethod(nameof(Command.Execute)); + + if (method == null) + throw new InvalidOperationException($"Command {originalType.FullName} is missing Exetutable method"); + + Type? parameter = method + ?.GetParameters() + .FirstOrDefault(p => p.ParameterType.IsAssignableTo(typeof(ArgumentsBase))) + ?.ParameterType; + +#if DEBUG + Debug.WriteLine($"Via methodinfo: {parameter?.FullName}"); +#endif + + return parameter; + } +} diff --git a/Source/BookGen.Cli/Internals/Helpers.cs b/Source/BookGen.Cli/Internals/Helpers.cs new file mode 100644 index 00000000..ab6ba97e --- /dev/null +++ b/Source/BookGen.Cli/Internals/Helpers.cs @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Runtime.InteropServices; + +namespace BookGen.Cli.Internals; + +internal static class Helpers +{ + public static SupportedOs GetCurrentOs() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return SupportedOs.Windows; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + return SupportedOs.Linux; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + return SupportedOs.OsX; + else + return SupportedOs.None; + } + + public static void ConfigureUtfSupport(bool enableUtf8Output) + { + if (enableUtf8Output) + { + Console.OutputEncoding = System.Text.Encoding.UTF8; + Console.InputEncoding = System.Text.Encoding.UTF8; + } + } + + // Skip the first argument (command name) and any parsed global options + public static List GetArgsToParse(IReadOnlyList args, + HashSet parsedGlobals, + int skipCount) + { + List results = new(); + for (int i = skipCount; i < args.Count; i++) + { + if (!parsedGlobals.Contains(args[i])) + { + results.Add(args[i]); + } + } + return results; + } +} diff --git a/Source/BookGen.Cli/IoCValidationContext.cs b/Source/BookGen.Cli/IoCValidationContext.cs index 8810726e..c7ed3cb2 100644 --- a/Source/BookGen.Cli/IoCValidationContext.cs +++ b/Source/BookGen.Cli/IoCValidationContext.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Cli/OpenCli/Draft/Argument.cs b/Source/BookGen.Cli/OpenCli/Draft/Argument.cs new file mode 100644 index 00000000..a631820e --- /dev/null +++ b/Source/BookGen.Cli/OpenCli/Draft/Argument.cs @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Text.Json.Serialization; + +namespace BookGen.Cli.OpenCli.Draft; + +public sealed class Argument +{ + /// + /// A list of accepted values + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("acceptedValues")] + public List? AcceptedValues { get; set; } + + /// + /// The argument arity. Arity defines the minimum and maximum number of argument values + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("arity")] + public Arity? Arity { get; set; } + + /// + /// The argument description + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("description")] + public string? Description { get; set; } + + /// + /// The argument group + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("group")] + public string? Group { get; set; } + + /// + /// Whether or not the argument is hidden + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("hidden")] + public bool? Hidden { get; set; } + + /// + /// Custom metadata + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("metadata")] + public List? Metadata { get; set; } + + /// + /// The argument name + /// + [JsonPropertyName("name")] + public required string Name { get; set; } + + /// + /// Whether or not the argument is required + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("required")] + public bool? OpenClRequired { get; set; } +} diff --git a/Source/BookGen.Cli/OpenCli/Draft/Arity.cs b/Source/BookGen.Cli/OpenCli/Draft/Arity.cs new file mode 100644 index 00000000..4022249a --- /dev/null +++ b/Source/BookGen.Cli/OpenCli/Draft/Arity.cs @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Text.Json.Serialization; + +namespace BookGen.Cli.OpenCli.Draft; + +/// +/// The argument arity. Arity defines the minimum and maximum number of argument values +/// +public sealed class Arity +{ + /// + /// The maximum number of values allowed + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("maximum")] + public long? Maximum + { + get => field; + set + { + if (value < Minimum) + throw new ArgumentOutOfRangeException(nameof(Maximum)); + + field = value; + } + } + + /// + /// The minimum number of values allowed + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("minimum")] + public long Minimum + { + get => field; + set + { + if (value < 1) + throw new ArgumentOutOfRangeException(nameof(Minimum)); + + field = value; + } + } = 1; +} diff --git a/Source/BookGen.Cli/OpenCli/Draft/CliInfo.cs b/Source/BookGen.Cli/OpenCli/Draft/CliInfo.cs new file mode 100644 index 00000000..be8bbfb3 --- /dev/null +++ b/Source/BookGen.Cli/OpenCli/Draft/CliInfo.cs @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Text.Json.Serialization; + +namespace BookGen.Cli.OpenCli.Draft; + +/// +/// Information about the CLI +/// +public sealed class CliInfo +{ + /// + /// The contact information + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("contact")] + public Contact? Contact { get; set; } + + /// + /// A description of the application + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("description")] + public string? Description { get; set; } + + /// + /// The application license + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("license")] + public License? License { get; set; } + + /// + /// A short summary of the application + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("summary")] + public string? Summary { get; set; } + + /// + /// The application title + /// + [JsonPropertyName("title")] + public required string Title { get; set; } + + /// + /// The application version + /// + [JsonPropertyName("version")] + public required string Version { get; set; } +} diff --git a/Source/BookGen.Cli/OpenCli/Draft/Command.cs b/Source/BookGen.Cli/OpenCli/Draft/Command.cs new file mode 100644 index 00000000..89a2892c --- /dev/null +++ b/Source/BookGen.Cli/OpenCli/Draft/Command.cs @@ -0,0 +1,90 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Text.Json.Serialization; + +namespace BookGen.Cli.OpenCli.Draft; + +/// +/// The root command +/// +public sealed class Command +{ + /// + /// The command aliases + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("aliases")] + public List? Aliases { get; set; } + + /// + /// The command arguments + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("arguments")] + public List? Arguments { get; set; } + + /// + /// The command's sub commands + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("commands")] + public List? Commands { get; set; } + + /// + /// The command description + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("description")] + public string? Description { get; set; } + + /// + /// Examples of how to use the command + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("examples")] + public List? Examples { get; set; } + + /// + /// The command's exit codes + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("exitCodes")] + public List? ExitCodes { get; set; } + + /// + /// Whether or not the command is hidden + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("hidden")] + public bool? Hidden { get; set; } + + /// + /// Indicate whether or not the command requires interactive input + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("interactive")] + public bool? Interactive { get; set; } + + /// + /// Custom metadata + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("metadata")] + public List? Metadata { get; set; } + + /// + /// The command name + /// + [JsonPropertyName("name")] + public required string Name { get; set; } + + /// + /// The command options + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("options")] + public List - + PreserveNewest @@ -34,8 +35,8 @@ PreserveNewest - - + + PreserveNewest diff --git a/Source/BookGen.Contents/BookGenShell.ps1 b/Source/BookGen.Contents/BookGenShell.ps1 index 4f20b002..a50398c8 100644 --- a/Source/BookGen.Contents/BookGenShell.ps1 +++ b/Source/BookGen.Contents/BookGenShell.ps1 @@ -300,13 +300,13 @@ function intro() { Write-Host "" Write-Host "To download additonal tools in this shell run bookgen tools command" - Bookgen.exe terminalinstall -t -nr + Bookgen.exe install -ct -nr if ($LastExitCode -eq 0) { - Bookgen.exe terminalinstall -c -nr + Bookgen.exe install -ctp -nr if ($LastExitCode -ne 0) { Write-Host "" Write-Host "To install this shell as a windows terminal profile run:"; - Write-Host "Bookgen terminalinstall" + Write-Host "Bookgen install" } } diff --git a/Source/BookGen.Contents/getting-started.mdr b/Source/BookGen.Contents/getting-started.mdr deleted file mode 100644 index 51845629..00000000 --- a/Source/BookGen.Contents/getting-started.mdr +++ /dev/null @@ -1,62 +0,0 @@ -Getting started - -BookGen installs various programs that can be used to achieve Book generation and writing tasks with the help of Markdown. - -Installed programs - -* BookGen - Main command line program -* BookGen.Shellprog - Shell helper program - -Bookgen shell commands - -cdg - Graphical change directory. Opens folder browser to set current working directory - -intro - BookGen shell short intro message - -bookgen-info - Displays this text - -organize - Organize files in the current working directory - -Common BookGen commands - -BookGen md2html -i input.md -o out.html - Convert the input.md file to out.html - -BookGen SubCommands - Lists all available subcommands - -BookGen Gui - Start in terminal gui mode. Only available, if folder contains a BookGen project. - -Markdown files - -Markdown files are the main input format for BookGen. They can contain text, images, links, and other elements. BookGen supports github flavored markdown, which means it supports additional features like tables, task lists, and more. Front matter is supported via YAML syntax. The front matter is used to store metadata about the document, such as title, tags, and other properties. - -To create a new page execute the command: BookGen newpage -n test.md. This will create a new markdown file named test.md in the current working directory. You can then edit this file with your favorite text editor. It will contain a basic template with the YAML front matter and a placeholder for the content. - -Templates - -Template tags use a mustache like syntax startiong with the {{ symbols and ending with }}. Tags can include simple properties or functions. Function and property names are case insensitive. - -There are a few special properties that have special meaning. These are: - -* {{Title}} - Title of the acual page -* {{Content}} - Content of the actual page -* {{Host}} - Host url, set in the configuration file - -The {{content}} mustache tag is a special placeholder used to represent the markdown content within a file. Important: A markdown document must not include the {{content}} tag within its own content. Including this tag will cause infinite recursion during rendering, resulting in a failure to render the document. - -The title attribute comes from the YAML front matter of the document. The YAML front matter must include a title and a tags property. The Title is the document title and the tags are a comma sepperated string of keywords that can be set for the metadata. - -You can also add additional data to the YAML front matter. The YAML front matter via the Data property, which is a Dictionary. For example, adding: - - Data: - foo: bar - -allows you to access this value in templates or markdown content using the mustache syntax {{foo}}. All property names are case insensitive in this case too, when accessing the front matter data. - -The LastModified time stamp is determined from the input file. - -Template functions - -* {{BuildDate(format)}} - Actual build date. Format is optional, default is yyyy-MM-dd HH:mm:ss. -* {{JSPageToc(source, target)}} - Generates a JavaScript table of contents from the source div's headdings and displays it to the target div. - - diff --git a/Source/Bookgen.Lib/AppSettings/AppSettings.cs b/Source/BookGen.Lib/AppSettings/AppSettings.cs similarity index 97% rename from Source/Bookgen.Lib/AppSettings/AppSettings.cs rename to Source/BookGen.Lib/AppSettings/AppSettings.cs index d3ad1edf..dea6d97d 100644 --- a/Source/Bookgen.Lib/AppSettings/AppSettings.cs +++ b/Source/BookGen.Lib/AppSettings/AppSettings.cs @@ -8,12 +8,12 @@ using System.Reflection; using System.Text.Json; -using Bookgen.Lib.Domain.IO; -using Bookgen.Lib.Internals; +using BookGen.Lib.Domain.IO; +using BookGen.Lib.Internals; using BookGen.Vfs; -namespace Bookgen.Lib.AppSettings; +namespace BookGen.Lib.AppSettings; public sealed class AppSettings : IAppSettings { @@ -120,6 +120,6 @@ public void Set(string setting, string value) Validate(); } - public void Save() + public void Save() => _fileSystem.Serialize(_appSettingsFilePath, _appSetting); } diff --git a/Source/Bookgen.Lib/AppSettings/IAppSettings.cs b/Source/BookGen.Lib/AppSettings/IAppSettings.cs similarity index 91% rename from Source/Bookgen.Lib/AppSettings/IAppSettings.cs rename to Source/BookGen.Lib/AppSettings/IAppSettings.cs index e19faf21..8f47d016 100644 --- a/Source/Bookgen.Lib/AppSettings/IAppSettings.cs +++ b/Source/BookGen.Lib/AppSettings/IAppSettings.cs @@ -3,7 +3,7 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.AppSettings; +namespace BookGen.Lib.AppSettings; public interface IAppSettings : IReadOnlyAppSettings { diff --git a/Source/Bookgen.Lib/AppSettings/IProgramPathResolver.cs b/Source/BookGen.Lib/AppSettings/IProgramPathResolver.cs similarity index 95% rename from Source/Bookgen.Lib/AppSettings/IProgramPathResolver.cs rename to Source/BookGen.Lib/AppSettings/IProgramPathResolver.cs index 43b9926e..39c7d33f 100644 --- a/Source/Bookgen.Lib/AppSettings/IProgramPathResolver.cs +++ b/Source/BookGen.Lib/AppSettings/IProgramPathResolver.cs @@ -5,7 +5,7 @@ using System.Diagnostics.CodeAnalysis; -namespace Bookgen.Lib.AppSettings; +namespace BookGen.Lib.AppSettings; public interface IProgramPathResolver { diff --git a/Source/Bookgen.Lib/AppSettings/IReadOnlyAppSettings.cs b/Source/BookGen.Lib/AppSettings/IReadOnlyAppSettings.cs similarity index 91% rename from Source/Bookgen.Lib/AppSettings/IReadOnlyAppSettings.cs rename to Source/BookGen.Lib/AppSettings/IReadOnlyAppSettings.cs index 71d6c469..030c59aa 100644 --- a/Source/Bookgen.Lib/AppSettings/IReadOnlyAppSettings.cs +++ b/Source/BookGen.Lib/AppSettings/IReadOnlyAppSettings.cs @@ -5,9 +5,9 @@ using System.Linq.Expressions; -using Bookgen.Lib.Domain.IO; +using BookGen.Lib.Domain.IO; -namespace Bookgen.Lib.AppSettings; +namespace BookGen.Lib.AppSettings; public interface IReadOnlyAppSettings { diff --git a/Source/Bookgen.Lib/AppSettings/ProgramPathResolver.cs b/Source/BookGen.Lib/AppSettings/ProgramPathResolver.cs similarity index 97% rename from Source/Bookgen.Lib/AppSettings/ProgramPathResolver.cs rename to Source/BookGen.Lib/AppSettings/ProgramPathResolver.cs index f1126b8c..227e4e35 100644 --- a/Source/Bookgen.Lib/AppSettings/ProgramPathResolver.cs +++ b/Source/BookGen.Lib/AppSettings/ProgramPathResolver.cs @@ -6,9 +6,9 @@ using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; -using Bookgen.Lib.Domain.IO; +using BookGen.Lib.Domain.IO; -namespace Bookgen.Lib.AppSettings; +namespace BookGen.Lib.AppSettings; public sealed class ProgramPathResolver : IProgramPathResolver { @@ -50,7 +50,8 @@ public ProgramPathResolver(IReadOnlyAppSettings appSettings) string fullPath = Path.Combine(path, binaryToSearch); if (File.Exists(fullPath)) return fullPath; - }; + } + ; return null; } diff --git a/Source/Bookgen.Lib/BookEnvironment.cs b/Source/BookGen.Lib/BookEnvironment.cs similarity index 95% rename from Source/Bookgen.Lib/BookEnvironment.cs rename to Source/BookGen.Lib/BookEnvironment.cs index 64e2e707..55ecf3b5 100644 --- a/Source/Bookgen.Lib/BookEnvironment.cs +++ b/Source/BookGen.Lib/BookEnvironment.cs @@ -6,16 +6,16 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json.Nodes; -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Confighandling; -using Bookgen.Lib.Domain.IO; -using Bookgen.Lib.Domain.IO.Configuration; -using Bookgen.Lib.Internals; -using Bookgen.Lib.Pipeline; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Confighandling; +using BookGen.Lib.Domain.IO; +using BookGen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Internals; +using BookGen.Lib.Pipeline; using BookGen.Vfs; -namespace Bookgen.Lib; +namespace BookGen.Lib; public sealed class BookEnvironment : IBookEnvironment { diff --git a/Source/Bookgen.Lib/Bookgen.Lib.csproj b/Source/BookGen.Lib/BookGen.Lib.csproj similarity index 89% rename from Source/Bookgen.Lib/Bookgen.Lib.csproj rename to Source/BookGen.Lib/BookGen.Lib.csproj index 8f679df5..513faa47 100644 --- a/Source/Bookgen.Lib/Bookgen.Lib.csproj +++ b/Source/BookGen.Lib/BookGen.Lib.csproj @@ -29,6 +29,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Source/Bookgen.Lib/BookStatFactory.cs b/Source/BookGen.Lib/BookStatFactory.cs similarity index 95% rename from Source/Bookgen.Lib/BookStatFactory.cs rename to Source/BookGen.Lib/BookStatFactory.cs index f4d16904..923c5a1e 100644 --- a/Source/Bookgen.Lib/BookStatFactory.cs +++ b/Source/BookGen.Lib/BookStatFactory.cs @@ -1,16 +1,16 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain; -using Bookgen.Lib.Domain.IO; -using Bookgen.Lib.Internals; -using Bookgen.Lib.Pipeline; +using BookGen.Lib.Domain; +using BookGen.Lib.Domain.IO; +using BookGen.Lib.Pipeline; +using BookGen.Lib.Rendering; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib; +namespace BookGen.Lib; public static class BookStatFactory { diff --git a/Source/Bookgen.Lib/BundledAssets.cs b/Source/BookGen.Lib/BundledAssets.cs similarity index 51% rename from Source/Bookgen.Lib/BundledAssets.cs rename to Source/BookGen.Lib/BundledAssets.cs index f4d7fcaa..0ba9fac3 100644 --- a/Source/Bookgen.Lib/BundledAssets.cs +++ b/Source/BookGen.Lib/BundledAssets.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib; +namespace BookGen.Lib; public static class BundledAssets { @@ -13,10 +13,30 @@ public static class BundledAssets public const string ProtectHtml = "protect.html"; public const string TemplateStaticWeb = "Static.html"; public const string TemplatePrint = "Print.html"; + public const string TemplateEpub = "Epub.html"; public const string TemplateBlank = "Blank.html"; public const string QrCodeJs = "qrcode.min.js"; public const string JsPageToc = "PageToc.js"; public const string NomnomlJs = "nomnoml.js"; public const string GraphreJs = "graphre.js"; public const string WordTemplate = "Template.docx"; + + public const string Md2HtmlTemplateAir = "Md2HtmlAir.html"; + public const string Md2HtmlTemplateRetro = "Md2HtmlRetro.html"; + public const string Md2HtmlTemplatteTinyDark = "Md2HtmlTinyDark.html"; + public const string Md2HtmlTemplatteTinyLight = "Md2HtmlTinyLight.html"; + public const string Md2HtmlTemplatteMvp = "Md2HtmlMvp.html"; + + public static IEnumerable Md2HtmlTemplates + { + get + { + yield return TemplateSinglePage; + yield return Md2HtmlTemplateAir; + yield return Md2HtmlTemplateRetro; + yield return Md2HtmlTemplatteTinyDark; + yield return Md2HtmlTemplatteTinyLight; + yield return Md2HtmlTemplatteMvp; + } + } } diff --git a/Source/Bookgen.Lib/Confighandling/ConfigUpgrader.cs b/Source/BookGen.Lib/Confighandling/ConfigUpgrader.cs similarity index 97% rename from Source/Bookgen.Lib/Confighandling/ConfigUpgrader.cs rename to Source/BookGen.Lib/Confighandling/ConfigUpgrader.cs index e2f3f9ef..8e437f5d 100644 --- a/Source/Bookgen.Lib/Confighandling/ConfigUpgrader.cs +++ b/Source/BookGen.Lib/Confighandling/ConfigUpgrader.cs @@ -1,17 +1,17 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text.Json.Nodes; -using Bookgen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Domain.IO.Configuration; using BookGen.Vfs; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Confighandling; +namespace BookGen.Lib.Confighandling; public sealed class ConfigUpgrader { diff --git a/Source/Bookgen.Lib/Confighandling/JsonMerger.cs b/Source/BookGen.Lib/Confighandling/JsonMerger.cs similarity index 91% rename from Source/Bookgen.Lib/Confighandling/JsonMerger.cs rename to Source/BookGen.Lib/Confighandling/JsonMerger.cs index f4e9c649..eb91aae3 100644 --- a/Source/Bookgen.Lib/Confighandling/JsonMerger.cs +++ b/Source/BookGen.Lib/Confighandling/JsonMerger.cs @@ -1,4 +1,9 @@ -using System.Text.Json; +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Text.Json; using System.Text.Json.Nodes; //----------------------------------------------------------------------------- @@ -8,7 +13,7 @@ using BookGen.Vfs; -namespace Bookgen.Lib.Confighandling; +namespace BookGen.Lib.Confighandling; internal sealed class JsonMerger { diff --git a/Source/Bookgen.Lib/Confighandling/JsonObjectExtensions.cs b/Source/BookGen.Lib/Confighandling/JsonObjectExtensions.cs similarity index 94% rename from Source/Bookgen.Lib/Confighandling/JsonObjectExtensions.cs rename to Source/BookGen.Lib/Confighandling/JsonObjectExtensions.cs index e979ef34..23ced83b 100644 --- a/Source/Bookgen.Lib/Confighandling/JsonObjectExtensions.cs +++ b/Source/BookGen.Lib/Confighandling/JsonObjectExtensions.cs @@ -1,12 +1,12 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Runtime.CompilerServices; using System.Text.Json.Nodes; -namespace Bookgen.Lib.Confighandling; +namespace BookGen.Lib.Confighandling; internal static class JsonObjectExtensions { diff --git a/Source/Bookgen.Lib/Confighandling/LegacyMigration/Conversions.cs b/Source/BookGen.Lib/Confighandling/LegacyMigration/Conversions.cs similarity index 84% rename from Source/Bookgen.Lib/Confighandling/LegacyMigration/Conversions.cs rename to Source/BookGen.Lib/Confighandling/LegacyMigration/Conversions.cs index 95599689..df04bd8f 100644 --- a/Source/Bookgen.Lib/Confighandling/LegacyMigration/Conversions.cs +++ b/Source/BookGen.Lib/Confighandling/LegacyMigration/Conversions.cs @@ -1,14 +1,14 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.IO.Configuration; -using Bookgen.Lib.Domain.IO.Legacy; +using BookGen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Domain.IO.Legacy; using Microsoft.AspNetCore.Authentication.OAuth.Claims; -namespace Bookgen.Lib.Confighandling.LegacyMigration; +namespace BookGen.Lib.Confighandling.LegacyMigration; internal static class Conversions { @@ -45,11 +45,13 @@ public static ImageConfig ToImageConfig(this ImageOptions imageOptions) private static ImgRecodeOption MapResizeOpion(ImageOptions imageOptions) { - if (imageOptions.RecodeJpegToWebp || imageOptions.RecodePngToWebp || imageOptions.EnableResize) - return ImgRecodeOption.AsWebp; - if (imageOptions.EnableResize) + { + if (imageOptions.RecodeJpegToWebp || imageOptions.RecodePngToWebp) + return ImgRecodeOption.AsWebp; + return ImgRecodeOption.AsPng; + } return ImgRecodeOption.Passtrough; } diff --git a/Source/Bookgen.Lib/Confighandling/LegacyMigration/IMigrationStep.cs b/Source/BookGen.Lib/Confighandling/LegacyMigration/IMigrationStep.cs similarity index 82% rename from Source/Bookgen.Lib/Confighandling/LegacyMigration/IMigrationStep.cs rename to Source/BookGen.Lib/Confighandling/LegacyMigration/IMigrationStep.cs index e294c3ba..fedba0fa 100644 --- a/Source/Bookgen.Lib/Confighandling/LegacyMigration/IMigrationStep.cs +++ b/Source/BookGen.Lib/Confighandling/LegacyMigration/IMigrationStep.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Confighandling.LegacyMigration; +namespace BookGen.Lib.Confighandling.LegacyMigration; internal interface IMigrationStep { diff --git a/Source/Bookgen.Lib/Confighandling/LegacyMigration/LoadLegacyConfig.cs b/Source/BookGen.Lib/Confighandling/LegacyMigration/LoadLegacyConfig.cs similarity index 91% rename from Source/Bookgen.Lib/Confighandling/LegacyMigration/LoadLegacyConfig.cs rename to Source/BookGen.Lib/Confighandling/LegacyMigration/LoadLegacyConfig.cs index d3ed2ce8..9aba8a88 100644 --- a/Source/Bookgen.Lib/Confighandling/LegacyMigration/LoadLegacyConfig.cs +++ b/Source/BookGen.Lib/Confighandling/LegacyMigration/LoadLegacyConfig.cs @@ -1,15 +1,15 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.IO.Legacy; +using BookGen.Lib.Domain.IO.Legacy; using BookGen.Vfs; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Confighandling.LegacyMigration; +namespace BookGen.Lib.Confighandling.LegacyMigration; internal sealed class LoadLegacyConfig : IMigrationStep { diff --git a/Source/Bookgen.Lib/Confighandling/LegacyMigration/LoadLegacyTags.cs b/Source/BookGen.Lib/Confighandling/LegacyMigration/LoadLegacyTags.cs similarity index 91% rename from Source/Bookgen.Lib/Confighandling/LegacyMigration/LoadLegacyTags.cs rename to Source/BookGen.Lib/Confighandling/LegacyMigration/LoadLegacyTags.cs index 985d977f..6a932d5b 100644 --- a/Source/Bookgen.Lib/Confighandling/LegacyMigration/LoadLegacyTags.cs +++ b/Source/BookGen.Lib/Confighandling/LegacyMigration/LoadLegacyTags.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Confighandling.LegacyMigration; +namespace BookGen.Lib.Confighandling.LegacyMigration; internal sealed class LoadLegacyTags : IMigrationStep { diff --git a/Source/Bookgen.Lib/Confighandling/LegacyMigration/LoadLegacyToc.cs b/Source/BookGen.Lib/Confighandling/LegacyMigration/LoadLegacyToc.cs similarity index 95% rename from Source/Bookgen.Lib/Confighandling/LegacyMigration/LoadLegacyToc.cs rename to Source/BookGen.Lib/Confighandling/LegacyMigration/LoadLegacyToc.cs index 50ee4f8f..5ce80fba 100644 --- a/Source/Bookgen.Lib/Confighandling/LegacyMigration/LoadLegacyToc.cs +++ b/Source/BookGen.Lib/Confighandling/LegacyMigration/LoadLegacyToc.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.IO.Legacy; +using BookGen.Lib.Domain.IO.Legacy; using BookGen.Vfs; @@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Confighandling.LegacyMigration; +namespace BookGen.Lib.Confighandling.LegacyMigration; internal sealed class LoadLegacyToc : IMigrationStep { diff --git a/Source/Bookgen.Lib/Confighandling/LegacyMigration/MigrateConfig.cs b/Source/BookGen.Lib/Confighandling/LegacyMigration/MigrateConfig.cs similarity index 95% rename from Source/Bookgen.Lib/Confighandling/LegacyMigration/MigrateConfig.cs rename to Source/BookGen.Lib/Confighandling/LegacyMigration/MigrateConfig.cs index f6272366..ca79d2b0 100644 --- a/Source/Bookgen.Lib/Confighandling/LegacyMigration/MigrateConfig.cs +++ b/Source/BookGen.Lib/Confighandling/LegacyMigration/MigrateConfig.cs @@ -1,15 +1,15 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Domain.IO.Configuration; using BookGen.Vfs; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Confighandling.LegacyMigration; +namespace BookGen.Lib.Confighandling.LegacyMigration; internal sealed class MigrateConfig : IMigrationStep { diff --git a/Source/Bookgen.Lib/Confighandling/LegacyMigration/MigrateFiles.cs b/Source/BookGen.Lib/Confighandling/LegacyMigration/MigrateFiles.cs similarity index 92% rename from Source/Bookgen.Lib/Confighandling/LegacyMigration/MigrateFiles.cs rename to Source/BookGen.Lib/Confighandling/LegacyMigration/MigrateFiles.cs index dcbf0c97..30be9009 100644 --- a/Source/Bookgen.Lib/Confighandling/LegacyMigration/MigrateFiles.cs +++ b/Source/BookGen.Lib/Confighandling/LegacyMigration/MigrateFiles.cs @@ -1,12 +1,12 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text; -using Bookgen.Lib.Domain.IO; -using Bookgen.Lib.Domain.IO.Legacy; +using BookGen.Lib.Domain.IO; +using BookGen.Lib.Domain.IO.Legacy; using BookGen.Vfs; @@ -14,7 +14,7 @@ using YamlDotNet.Serialization; -namespace Bookgen.Lib.Confighandling.LegacyMigration; +namespace BookGen.Lib.Confighandling.LegacyMigration; internal class MigrateFiles : IMigrationStep { diff --git a/Source/Bookgen.Lib/Confighandling/LegacyMigration/MigrateToc.cs b/Source/BookGen.Lib/Confighandling/LegacyMigration/MigrateToc.cs similarity index 91% rename from Source/Bookgen.Lib/Confighandling/LegacyMigration/MigrateToc.cs rename to Source/BookGen.Lib/Confighandling/LegacyMigration/MigrateToc.cs index b60135e7..6f74a60a 100644 --- a/Source/Bookgen.Lib/Confighandling/LegacyMigration/MigrateToc.cs +++ b/Source/BookGen.Lib/Confighandling/LegacyMigration/MigrateToc.cs @@ -1,15 +1,15 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.IO; +using BookGen.Lib.Domain.IO; using BookGen.Vfs; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Confighandling.LegacyMigration; +namespace BookGen.Lib.Confighandling.LegacyMigration; internal sealed class MigrateToc : IMigrationStep { diff --git a/Source/Bookgen.Lib/Confighandling/LegacyMigration/MigrationState.cs b/Source/BookGen.Lib/Confighandling/LegacyMigration/MigrationState.cs similarity index 88% rename from Source/Bookgen.Lib/Confighandling/LegacyMigration/MigrationState.cs rename to Source/BookGen.Lib/Confighandling/LegacyMigration/MigrationState.cs index 91c09d3f..e2dd1dc2 100644 --- a/Source/Bookgen.Lib/Confighandling/LegacyMigration/MigrationState.cs +++ b/Source/BookGen.Lib/Confighandling/LegacyMigration/MigrationState.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Confighandling.LegacyMigration; +namespace BookGen.Lib.Confighandling.LegacyMigration; internal sealed class MigrationState { diff --git a/Source/Bookgen.Lib/Confighandling/LegacyMigration/Migrator.cs b/Source/BookGen.Lib/Confighandling/LegacyMigration/Migrator.cs similarity index 93% rename from Source/Bookgen.Lib/Confighandling/LegacyMigration/Migrator.cs rename to Source/BookGen.Lib/Confighandling/LegacyMigration/Migrator.cs index d6ca3981..d2ea1acb 100644 --- a/Source/Bookgen.Lib/Confighandling/LegacyMigration/Migrator.cs +++ b/Source/BookGen.Lib/Confighandling/LegacyMigration/Migrator.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -9,7 +9,7 @@ using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Confighandling.LegacyMigration; +namespace BookGen.Lib.Confighandling.LegacyMigration; public static class Migrator { diff --git a/Source/Bookgen.Lib/Confighandling/UpgradeBase.cs b/Source/BookGen.Lib/Confighandling/UpgradeBase.cs similarity index 86% rename from Source/Bookgen.Lib/Confighandling/UpgradeBase.cs rename to Source/BookGen.Lib/Confighandling/UpgradeBase.cs index 9a96a2b3..b4fbe560 100644 --- a/Source/Bookgen.Lib/Confighandling/UpgradeBase.cs +++ b/Source/BookGen.Lib/Confighandling/UpgradeBase.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text.Json.Nodes; -namespace Bookgen.Lib.Confighandling; +namespace BookGen.Lib.Confighandling; internal abstract class UpgradeBase { diff --git a/Source/Bookgen.Lib/Confighandling/UpgradeSteps/FromVersion2003To2004.cs b/Source/BookGen.Lib/Confighandling/UpgradeSteps/FromVersion2003To2004.cs similarity index 92% rename from Source/Bookgen.Lib/Confighandling/UpgradeSteps/FromVersion2003To2004.cs rename to Source/BookGen.Lib/Confighandling/UpgradeSteps/FromVersion2003To2004.cs index c01fc7bc..9be850e9 100644 --- a/Source/Bookgen.Lib/Confighandling/UpgradeSteps/FromVersion2003To2004.cs +++ b/Source/BookGen.Lib/Confighandling/UpgradeSteps/FromVersion2003To2004.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text.Json.Nodes; -namespace Bookgen.Lib.Confighandling.UpgradeSteps; +namespace BookGen.Lib.Confighandling.UpgradeSteps; internal sealed class FromVersion2003To2004 : UpgradeBase { diff --git a/Source/Bookgen.Lib/Confighandling/UpgradeSteps/FromVersion2004To2005.cs b/Source/BookGen.Lib/Confighandling/UpgradeSteps/FromVersion2004To2005.cs similarity index 94% rename from Source/Bookgen.Lib/Confighandling/UpgradeSteps/FromVersion2004To2005.cs rename to Source/BookGen.Lib/Confighandling/UpgradeSteps/FromVersion2004To2005.cs index 5aaa91bf..a897c85e 100644 --- a/Source/Bookgen.Lib/Confighandling/UpgradeSteps/FromVersion2004To2005.cs +++ b/Source/BookGen.Lib/Confighandling/UpgradeSteps/FromVersion2004To2005.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text.Json.Nodes; -namespace Bookgen.Lib.Confighandling.UpgradeSteps; +namespace BookGen.Lib.Confighandling.UpgradeSteps; internal sealed class FromVersion2004To2005 : UpgradeBase { diff --git a/Source/Bookgen.Lib/Confighandling/UpgradeSteps/FromVersion2005To2006.cs b/Source/BookGen.Lib/Confighandling/UpgradeSteps/FromVersion2005To2006.cs similarity index 94% rename from Source/Bookgen.Lib/Confighandling/UpgradeSteps/FromVersion2005To2006.cs rename to Source/BookGen.Lib/Confighandling/UpgradeSteps/FromVersion2005To2006.cs index 7c4057d7..e00470c7 100644 --- a/Source/Bookgen.Lib/Confighandling/UpgradeSteps/FromVersion2005To2006.cs +++ b/Source/BookGen.Lib/Confighandling/UpgradeSteps/FromVersion2005To2006.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text.Json.Nodes; -namespace Bookgen.Lib.Confighandling.UpgradeSteps; +namespace BookGen.Lib.Confighandling.UpgradeSteps; internal sealed class FromVersion2005To2006 : UpgradeBase { diff --git a/Source/Bookgen.Lib/Confighandling/UpgradeSteps/FromVersion2006To2007.cs b/Source/BookGen.Lib/Confighandling/UpgradeSteps/FromVersion2006To2007.cs similarity index 89% rename from Source/Bookgen.Lib/Confighandling/UpgradeSteps/FromVersion2006To2007.cs rename to Source/BookGen.Lib/Confighandling/UpgradeSteps/FromVersion2006To2007.cs index d664d4b1..5d321473 100644 --- a/Source/Bookgen.Lib/Confighandling/UpgradeSteps/FromVersion2006To2007.cs +++ b/Source/BookGen.Lib/Confighandling/UpgradeSteps/FromVersion2006To2007.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text.Json.Nodes; -namespace Bookgen.Lib.Confighandling.UpgradeSteps; +namespace BookGen.Lib.Confighandling.UpgradeSteps; internal sealed class FromVersion2006To2007 : UpgradeBase { diff --git a/Source/Bookgen.Lib/Confighandling/UpgradeSteps/FromVersion2007To2008.cs b/Source/BookGen.Lib/Confighandling/UpgradeSteps/FromVersion2007To2008.cs similarity index 83% rename from Source/Bookgen.Lib/Confighandling/UpgradeSteps/FromVersion2007To2008.cs rename to Source/BookGen.Lib/Confighandling/UpgradeSteps/FromVersion2007To2008.cs index e14d7595..2fdc5174 100644 --- a/Source/Bookgen.Lib/Confighandling/UpgradeSteps/FromVersion2007To2008.cs +++ b/Source/BookGen.Lib/Confighandling/UpgradeSteps/FromVersion2007To2008.cs @@ -1,13 +1,13 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text.Json.Nodes; -using Bookgen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Domain.IO.Configuration; -namespace Bookgen.Lib.Confighandling.UpgradeSteps; +namespace BookGen.Lib.Confighandling.UpgradeSteps; internal sealed class FromVersion2007To2008 : UpgradeBase { diff --git a/Source/Bookgen.Lib/Confighandling/VersionTagInfo.cs b/Source/BookGen.Lib/Confighandling/VersionTagInfo.cs similarity index 90% rename from Source/Bookgen.Lib/Confighandling/VersionTagInfo.cs rename to Source/BookGen.Lib/Confighandling/VersionTagInfo.cs index 52216247..76fb27b6 100644 --- a/Source/Bookgen.Lib/Confighandling/VersionTagInfo.cs +++ b/Source/BookGen.Lib/Confighandling/VersionTagInfo.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Confighandling; +namespace BookGen.Lib.Confighandling; internal readonly record struct VersionTagInfo : IComparable { diff --git a/Source/Bookgen.Lib/Domain/BookStat.cs b/Source/BookGen.Lib/Domain/BookStat.cs similarity index 94% rename from Source/Bookgen.Lib/Domain/BookStat.cs rename to Source/BookGen.Lib/Domain/BookStat.cs index f14ad106..0e3bb92d 100644 --- a/Source/Bookgen.Lib/Domain/BookStat.cs +++ b/Source/BookGen.Lib/Domain/BookStat.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Domain; +namespace BookGen.Lib.Domain; public sealed class BookStat { diff --git a/Source/Bookgen.Lib/Domain/Epub/Container.cs b/Source/BookGen.Lib/Domain/Epub/Container.cs similarity index 91% rename from Source/Bookgen.Lib/Domain/Epub/Container.cs rename to Source/BookGen.Lib/Domain/Epub/Container.cs index cebd5920..dec5eca6 100644 --- a/Source/Bookgen.Lib/Domain/Epub/Container.cs +++ b/Source/BookGen.Lib/Domain/Epub/Container.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable()] [XmlType(AnonymousType = true, Namespace = "urn:oasis:names:tc:opendocument:xmlns:container")] diff --git a/Source/Bookgen.Lib/Domain/Epub/ContainerRootfiles.cs b/Source/BookGen.Lib/Domain/Epub/ContainerRootfiles.cs similarity index 88% rename from Source/Bookgen.Lib/Domain/Epub/ContainerRootfiles.cs rename to Source/BookGen.Lib/Domain/Epub/ContainerRootfiles.cs index f9f30d99..1e711161 100644 --- a/Source/Bookgen.Lib/Domain/Epub/ContainerRootfiles.cs +++ b/Source/BookGen.Lib/Domain/Epub/ContainerRootfiles.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable()] [XmlType(AnonymousType = true, Namespace = "urn:oasis:names:tc:opendocument:xmlns:container")] diff --git a/Source/Bookgen.Lib/Domain/Epub/ContainerRootfilesRootfile.cs b/Source/BookGen.Lib/Domain/Epub/ContainerRootfilesRootfile.cs similarity index 89% rename from Source/Bookgen.Lib/Domain/Epub/ContainerRootfilesRootfile.cs rename to Source/BookGen.Lib/Domain/Epub/ContainerRootfilesRootfile.cs index 6294f210..18e59ddc 100644 --- a/Source/Bookgen.Lib/Domain/Epub/ContainerRootfilesRootfile.cs +++ b/Source/BookGen.Lib/Domain/Epub/ContainerRootfilesRootfile.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable()] [XmlType(AnonymousType = true, Namespace = "urn:oasis:names:tc:opendocument:xmlns:container")] diff --git a/Source/Bookgen.Lib/Domain/Epub/Creator.cs b/Source/BookGen.Lib/Domain/Epub/Creator.cs similarity index 85% rename from Source/Bookgen.Lib/Domain/Epub/Creator.cs rename to Source/BookGen.Lib/Domain/Epub/Creator.cs index df7cf886..f2084b27 100644 --- a/Source/Bookgen.Lib/Domain/Epub/Creator.cs +++ b/Source/BookGen.Lib/Domain/Epub/Creator.cs @@ -1,12 +1,13 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; +[Serializable] [XmlType(AnonymousType = true, Namespace = "http://purl.org/dc/elements/1.1/")] [XmlRoot(Namespace = "http://purl.org/dc/elements/1.1/", IsNullable = false)] public sealed class Creator diff --git a/Source/Bookgen.Lib/Domain/Epub/Date.cs b/Source/BookGen.Lib/Domain/Epub/Date.cs similarity index 89% rename from Source/Bookgen.Lib/Domain/Epub/Date.cs rename to Source/BookGen.Lib/Domain/Epub/Date.cs index 7fd14413..e0b059b2 100644 --- a/Source/Bookgen.Lib/Domain/Epub/Date.cs +++ b/Source/BookGen.Lib/Domain/Epub/Date.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable] [XmlType(AnonymousType = true, Namespace = "http://purl.org/dc/elements/1.1/")] diff --git a/Source/Bookgen.Lib/Domain/Epub/Identifier.cs b/Source/BookGen.Lib/Domain/Epub/Identifier.cs similarity index 89% rename from Source/Bookgen.Lib/Domain/Epub/Identifier.cs rename to Source/BookGen.Lib/Domain/Epub/Identifier.cs index 13901713..60d25f04 100644 --- a/Source/Bookgen.Lib/Domain/Epub/Identifier.cs +++ b/Source/BookGen.Lib/Domain/Epub/Identifier.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable] [XmlType(AnonymousType = true, Namespace = "http://purl.org/dc/elements/1.1/")] diff --git a/Source/Bookgen.Lib/Domain/Epub/Ncx.cs b/Source/BookGen.Lib/Domain/Epub/Ncx.cs similarity index 93% rename from Source/Bookgen.Lib/Domain/Epub/Ncx.cs rename to Source/BookGen.Lib/Domain/Epub/Ncx.cs index 30d369ff..fa91d05a 100644 --- a/Source/Bookgen.Lib/Domain/Epub/Ncx.cs +++ b/Source/BookGen.Lib/Domain/Epub/Ncx.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable] [XmlType(AnonymousType = true, Namespace = "http://www.daisy.org/z3986/2005/ncx/")] diff --git a/Source/Bookgen.Lib/Domain/Epub/NcxMeta.cs b/Source/BookGen.Lib/Domain/Epub/NcxMeta.cs similarity index 90% rename from Source/Bookgen.Lib/Domain/Epub/NcxMeta.cs rename to Source/BookGen.Lib/Domain/Epub/NcxMeta.cs index 6f3474f8..37fbac0a 100644 --- a/Source/Bookgen.Lib/Domain/Epub/NcxMeta.cs +++ b/Source/BookGen.Lib/Domain/Epub/NcxMeta.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable] [System.ComponentModel.DesignerCategory("code")] diff --git a/Source/Bookgen.Lib/Domain/Epub/NcxNavInfoType.cs b/Source/BookGen.Lib/Domain/Epub/NcxNavInfoType.cs similarity index 88% rename from Source/Bookgen.Lib/Domain/Epub/NcxNavInfoType.cs rename to Source/BookGen.Lib/Domain/Epub/NcxNavInfoType.cs index 23df9903..a0d7f76f 100644 --- a/Source/Bookgen.Lib/Domain/Epub/NcxNavInfoType.cs +++ b/Source/BookGen.Lib/Domain/Epub/NcxNavInfoType.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable] [System.ComponentModel.DesignerCategory("code")] diff --git a/Source/Bookgen.Lib/Domain/Epub/NcxNavPoint.cs b/Source/BookGen.Lib/Domain/Epub/NcxNavPoint.cs similarity index 92% rename from Source/Bookgen.Lib/Domain/Epub/NcxNavPoint.cs rename to Source/BookGen.Lib/Domain/Epub/NcxNavPoint.cs index 3da5a383..6ff06096 100644 --- a/Source/Bookgen.Lib/Domain/Epub/NcxNavPoint.cs +++ b/Source/BookGen.Lib/Domain/Epub/NcxNavPoint.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable] [System.ComponentModel.DesignerCategory("code")] diff --git a/Source/Bookgen.Lib/Domain/Epub/NcxNavPointContent.cs b/Source/BookGen.Lib/Domain/Epub/NcxNavPointContent.cs similarity index 88% rename from Source/Bookgen.Lib/Domain/Epub/NcxNavPointContent.cs rename to Source/BookGen.Lib/Domain/Epub/NcxNavPointContent.cs index fbdbc84d..e8959a93 100644 --- a/Source/Bookgen.Lib/Domain/Epub/NcxNavPointContent.cs +++ b/Source/BookGen.Lib/Domain/Epub/NcxNavPointContent.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable] [System.ComponentModel.DesignerCategory("code")] diff --git a/Source/Bookgen.Lib/Domain/Epub/Package.cs b/Source/BookGen.Lib/Domain/Epub/Package.cs similarity index 94% rename from Source/Bookgen.Lib/Domain/Epub/Package.cs rename to Source/BookGen.Lib/Domain/Epub/Package.cs index 53231a7c..c0e31e8b 100644 --- a/Source/Bookgen.Lib/Domain/Epub/Package.cs +++ b/Source/BookGen.Lib/Domain/Epub/Package.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable] [XmlRoot(Namespace = "http://www.idpf.org/2007/opf", IsNullable = false, ElementName = "package")] diff --git a/Source/Bookgen.Lib/Domain/Epub/PackageGuide.cs b/Source/BookGen.Lib/Domain/Epub/PackageGuide.cs similarity index 87% rename from Source/Bookgen.Lib/Domain/Epub/PackageGuide.cs rename to Source/BookGen.Lib/Domain/Epub/PackageGuide.cs index 7405cb22..6c331600 100644 --- a/Source/Bookgen.Lib/Domain/Epub/PackageGuide.cs +++ b/Source/BookGen.Lib/Domain/Epub/PackageGuide.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable] [XmlType(AnonymousType = true, Namespace = "http://www.idpf.org/2007/opf")] diff --git a/Source/Bookgen.Lib/Domain/Epub/PackageGuideReference.cs b/Source/BookGen.Lib/Domain/Epub/PackageGuideReference.cs similarity index 90% rename from Source/Bookgen.Lib/Domain/Epub/PackageGuideReference.cs rename to Source/BookGen.Lib/Domain/Epub/PackageGuideReference.cs index d2269530..04dd3271 100644 --- a/Source/Bookgen.Lib/Domain/Epub/PackageGuideReference.cs +++ b/Source/BookGen.Lib/Domain/Epub/PackageGuideReference.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable] [XmlType(AnonymousType = true, Namespace = "http://www.idpf.org/2007/opf")] diff --git a/Source/Bookgen.Lib/Domain/Epub/PackageItem.cs b/Source/BookGen.Lib/Domain/Epub/PackageItem.cs similarity index 91% rename from Source/Bookgen.Lib/Domain/Epub/PackageItem.cs rename to Source/BookGen.Lib/Domain/Epub/PackageItem.cs index 4540d478..ca2478c8 100644 --- a/Source/Bookgen.Lib/Domain/Epub/PackageItem.cs +++ b/Source/BookGen.Lib/Domain/Epub/PackageItem.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable] [XmlType(AnonymousType = true, Namespace = "http://www.idpf.org/2007/opf")] diff --git a/Source/Bookgen.Lib/Domain/Epub/PackageMetadata.cs b/Source/BookGen.Lib/Domain/Epub/PackageMetadata.cs similarity index 94% rename from Source/Bookgen.Lib/Domain/Epub/PackageMetadata.cs rename to Source/BookGen.Lib/Domain/Epub/PackageMetadata.cs index 53ea564e..28183d23 100644 --- a/Source/Bookgen.Lib/Domain/Epub/PackageMetadata.cs +++ b/Source/BookGen.Lib/Domain/Epub/PackageMetadata.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable] [XmlType(AnonymousType = true, Namespace = "http://www.idpf.org/2007/opf")] diff --git a/Source/Bookgen.Lib/Domain/Epub/PackageMetadataMeta.cs b/Source/BookGen.Lib/Domain/Epub/PackageMetadataMeta.cs similarity index 88% rename from Source/Bookgen.Lib/Domain/Epub/PackageMetadataMeta.cs rename to Source/BookGen.Lib/Domain/Epub/PackageMetadataMeta.cs index ca3d9233..145eb1bc 100644 --- a/Source/Bookgen.Lib/Domain/Epub/PackageMetadataMeta.cs +++ b/Source/BookGen.Lib/Domain/Epub/PackageMetadataMeta.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable] [XmlType(AnonymousType = true, Namespace = "http://www.idpf.org/2007/opf")] diff --git a/Source/Bookgen.Lib/Domain/Epub/PackageSpine.cs b/Source/BookGen.Lib/Domain/Epub/PackageSpine.cs similarity index 89% rename from Source/Bookgen.Lib/Domain/Epub/PackageSpine.cs rename to Source/BookGen.Lib/Domain/Epub/PackageSpine.cs index 722f45b3..a77e1c99 100644 --- a/Source/Bookgen.Lib/Domain/Epub/PackageSpine.cs +++ b/Source/BookGen.Lib/Domain/Epub/PackageSpine.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable] [XmlType(AnonymousType = true, Namespace = "http://www.idpf.org/2007/opf")] diff --git a/Source/Bookgen.Lib/Domain/Epub/PackageSpineItemref.cs b/Source/BookGen.Lib/Domain/Epub/PackageSpineItemref.cs similarity index 89% rename from Source/Bookgen.Lib/Domain/Epub/PackageSpineItemref.cs rename to Source/BookGen.Lib/Domain/Epub/PackageSpineItemref.cs index 010d3564..4fd057ce 100644 --- a/Source/Bookgen.Lib/Domain/Epub/PackageSpineItemref.cs +++ b/Source/BookGen.Lib/Domain/Epub/PackageSpineItemref.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable] [XmlType(AnonymousType = true, Namespace = "http://www.idpf.org/2007/opf")] diff --git a/Source/Bookgen.Lib/Domain/Epub/Title.cs b/Source/BookGen.Lib/Domain/Epub/Title.cs similarity index 89% rename from Source/Bookgen.Lib/Domain/Epub/Title.cs rename to Source/BookGen.Lib/Domain/Epub/Title.cs index e665e282..4e0bca65 100644 --- a/Source/Bookgen.Lib/Domain/Epub/Title.cs +++ b/Source/BookGen.Lib/Domain/Epub/Title.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Epub; +namespace BookGen.Lib.Domain.Epub; [Serializable] [XmlType(AnonymousType = true, Namespace = "http://purl.org/dc/elements/1.1/")] diff --git a/Source/Bookgen.Lib/Domain/Github/Release.cs b/Source/BookGen.Lib/Domain/Github/Release.cs similarity index 85% rename from Source/Bookgen.Lib/Domain/Github/Release.cs rename to Source/BookGen.Lib/Domain/Github/Release.cs index 036b7d7d..339eda0d 100644 --- a/Source/Bookgen.Lib/Domain/Github/Release.cs +++ b/Source/BookGen.Lib/Domain/Github/Release.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text.Json.Serialization; -namespace Bookgen.Lib.Domain.Github; +namespace BookGen.Lib.Domain.Github; /// /// A release. diff --git a/Source/Bookgen.Lib/Domain/Github/ReleaseAsset.cs b/Source/BookGen.Lib/Domain/Github/ReleaseAsset.cs similarity index 95% rename from Source/Bookgen.Lib/Domain/Github/ReleaseAsset.cs rename to Source/BookGen.Lib/Domain/Github/ReleaseAsset.cs index d3bd251f..a921e1c2 100644 --- a/Source/Bookgen.Lib/Domain/Github/ReleaseAsset.cs +++ b/Source/BookGen.Lib/Domain/Github/ReleaseAsset.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text.Json.Serialization; -namespace Bookgen.Lib.Domain.Github; +namespace BookGen.Lib.Domain.Github; public class ReleaseAsset { diff --git a/Source/Bookgen.Lib/Domain/Github/State.cs b/Source/BookGen.Lib/Domain/Github/State.cs similarity index 86% rename from Source/Bookgen.Lib/Domain/Github/State.cs rename to Source/BookGen.Lib/Domain/Github/State.cs index 30d560b5..d998d5e1 100644 --- a/Source/Bookgen.Lib/Domain/Github/State.cs +++ b/Source/BookGen.Lib/Domain/Github/State.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text.Json.Serialization; -namespace Bookgen.Lib.Domain.Github; +namespace BookGen.Lib.Domain.Github; /// /// State of the release asset. diff --git a/Source/Bookgen.Lib/Domain/IO/AppSetting.cs b/Source/BookGen.Lib/Domain/IO/AppSetting.cs similarity index 61% rename from Source/Bookgen.Lib/Domain/IO/AppSetting.cs rename to Source/BookGen.Lib/Domain/IO/AppSetting.cs index a39cc506..87276ef1 100644 --- a/Source/Bookgen.Lib/Domain/IO/AppSetting.cs +++ b/Source/BookGen.Lib/Domain/IO/AppSetting.cs @@ -1,11 +1,14 @@ -using Bookgen.Lib.Domain.Validation; +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- -namespace Bookgen.Lib.Domain.IO; +using BookGen.Lib.Domain.Validation; + +namespace BookGen.Lib.Domain.IO; public sealed record class AppSetting { - public string? Editor { get; set; } - [WhenNotEmptyFileMustExist] public string? NodeJsPath { get; set; } @@ -25,7 +28,6 @@ public static AppSetting CreateDefault() { return new AppSetting { - Editor = "notepad.exe", }; } } diff --git a/Source/Bookgen.Lib/Domain/IO/Configuration/Config.cs b/Source/BookGen.Lib/Domain/IO/Configuration/Config.cs similarity index 93% rename from Source/Bookgen.Lib/Domain/IO/Configuration/Config.cs rename to Source/BookGen.Lib/Domain/IO/Configuration/Config.cs index fb805299..c3d16100 100644 --- a/Source/Bookgen.Lib/Domain/IO/Configuration/Config.cs +++ b/Source/BookGen.Lib/Domain/IO/Configuration/Config.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -7,9 +7,9 @@ using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; -using Bookgen.Lib.Domain.Validation; +using BookGen.Lib.Domain.Validation; -namespace Bookgen.Lib.Domain.IO.Configuration; +namespace BookGen.Lib.Domain.IO.Configuration; public sealed class Config { diff --git a/Source/Bookgen.Lib/Domain/IO/Configuration/ContainerElement.cs b/Source/BookGen.Lib/Domain/IO/Configuration/ContainerElement.cs similarity index 82% rename from Source/Bookgen.Lib/Domain/IO/Configuration/ContainerElement.cs rename to Source/BookGen.Lib/Domain/IO/Configuration/ContainerElement.cs index 72297ce6..549e17d3 100644 --- a/Source/Bookgen.Lib/Domain/IO/Configuration/ContainerElement.cs +++ b/Source/BookGen.Lib/Domain/IO/Configuration/ContainerElement.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Domain.IO.Configuration; +namespace BookGen.Lib.Domain.IO.Configuration; public enum ContainerElement { diff --git a/Source/Bookgen.Lib/Domain/IO/Configuration/CssClasses.cs b/Source/BookGen.Lib/Domain/IO/Configuration/CssClasses.cs similarity index 95% rename from Source/Bookgen.Lib/Domain/IO/Configuration/CssClasses.cs rename to Source/BookGen.Lib/Domain/IO/Configuration/CssClasses.cs index 442e0a1e..0a965b1d 100644 --- a/Source/Bookgen.Lib/Domain/IO/Configuration/CssClasses.cs +++ b/Source/BookGen.Lib/Domain/IO/Configuration/CssClasses.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.ComponentModel; -namespace Bookgen.Lib.Domain.IO.Configuration; +namespace BookGen.Lib.Domain.IO.Configuration; public sealed class CssClasses { diff --git a/Source/Bookgen.Lib/Domain/IO/Configuration/FeedConfig.cs b/Source/BookGen.Lib/Domain/IO/Configuration/FeedConfig.cs similarity index 77% rename from Source/Bookgen.Lib/Domain/IO/Configuration/FeedConfig.cs rename to Source/BookGen.Lib/Domain/IO/Configuration/FeedConfig.cs index dee38e36..a2701294 100644 --- a/Source/Bookgen.Lib/Domain/IO/Configuration/FeedConfig.cs +++ b/Source/BookGen.Lib/Domain/IO/Configuration/FeedConfig.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Domain.IO.Configuration; +namespace BookGen.Lib.Domain.IO.Configuration; public sealed class FeedConfig : OutputConfig { diff --git a/Source/Bookgen.Lib/Domain/IO/Configuration/ImageConfig.cs b/Source/BookGen.Lib/Domain/IO/Configuration/ImageConfig.cs similarity index 93% rename from Source/Bookgen.Lib/Domain/IO/Configuration/ImageConfig.cs rename to Source/BookGen.Lib/Domain/IO/Configuration/ImageConfig.cs index 3e9e0d2e..337527d1 100644 --- a/Source/Bookgen.Lib/Domain/IO/Configuration/ImageConfig.cs +++ b/Source/BookGen.Lib/Domain/IO/Configuration/ImageConfig.cs @@ -1,12 +1,12 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.ComponentModel; using System.ComponentModel.DataAnnotations; -namespace Bookgen.Lib.Domain.IO.Configuration; +namespace BookGen.Lib.Domain.IO.Configuration; public sealed class ImageConfig { diff --git a/Source/Bookgen.Lib/Domain/IO/Configuration/ImgRecodeOption.cs b/Source/BookGen.Lib/Domain/IO/Configuration/ImgRecodeOption.cs similarity index 79% rename from Source/Bookgen.Lib/Domain/IO/Configuration/ImgRecodeOption.cs rename to Source/BookGen.Lib/Domain/IO/Configuration/ImgRecodeOption.cs index 910bb33e..ce5f8167 100644 --- a/Source/Bookgen.Lib/Domain/IO/Configuration/ImgRecodeOption.cs +++ b/Source/BookGen.Lib/Domain/IO/Configuration/ImgRecodeOption.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Domain.IO.Configuration; +namespace BookGen.Lib.Domain.IO.Configuration; public enum ImgRecodeOption { diff --git a/Source/Bookgen.Lib/Domain/IO/Configuration/ItemContainer.cs b/Source/BookGen.Lib/Domain/IO/Configuration/ItemContainer.cs similarity index 80% rename from Source/Bookgen.Lib/Domain/IO/Configuration/ItemContainer.cs rename to Source/BookGen.Lib/Domain/IO/Configuration/ItemContainer.cs index 8a3ecbb6..2b282800 100644 --- a/Source/Bookgen.Lib/Domain/IO/Configuration/ItemContainer.cs +++ b/Source/BookGen.Lib/Domain/IO/Configuration/ItemContainer.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Domain.IO.Configuration; +namespace BookGen.Lib.Domain.IO.Configuration; public enum ItemContainer { diff --git a/Source/Bookgen.Lib/Domain/IO/Configuration/OutputConfig.cs b/Source/BookGen.Lib/Domain/IO/Configuration/OutputConfig.cs similarity index 89% rename from Source/Bookgen.Lib/Domain/IO/Configuration/OutputConfig.cs rename to Source/BookGen.Lib/Domain/IO/Configuration/OutputConfig.cs index 0cca824e..f5854638 100644 --- a/Source/Bookgen.Lib/Domain/IO/Configuration/OutputConfig.cs +++ b/Source/BookGen.Lib/Domain/IO/Configuration/OutputConfig.cs @@ -1,14 +1,14 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using Bookgen.Lib.Domain.Validation; +using BookGen.Lib.Domain.Validation; -namespace Bookgen.Lib.Domain.IO.Configuration; +namespace BookGen.Lib.Domain.IO.Configuration; public abstract class OutputConfig { diff --git a/Source/Bookgen.Lib/Domain/IO/Configuration/PrintConfig.cs b/Source/BookGen.Lib/Domain/IO/Configuration/PrintConfig.cs similarity index 77% rename from Source/Bookgen.Lib/Domain/IO/Configuration/PrintConfig.cs rename to Source/BookGen.Lib/Domain/IO/Configuration/PrintConfig.cs index 01f836a3..56e8bbbc 100644 --- a/Source/Bookgen.Lib/Domain/IO/Configuration/PrintConfig.cs +++ b/Source/BookGen.Lib/Domain/IO/Configuration/PrintConfig.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Domain.IO.Configuration; +namespace BookGen.Lib.Domain.IO.Configuration; public sealed class PrintConfig : OutputConfig { diff --git a/Source/Bookgen.Lib/Domain/IO/Configuration/StaticWebsiteConfig.cs b/Source/BookGen.Lib/Domain/IO/Configuration/StaticWebsiteConfig.cs similarity index 88% rename from Source/Bookgen.Lib/Domain/IO/Configuration/StaticWebsiteConfig.cs rename to Source/BookGen.Lib/Domain/IO/Configuration/StaticWebsiteConfig.cs index d6666f88..d97c8471 100644 --- a/Source/Bookgen.Lib/Domain/IO/Configuration/StaticWebsiteConfig.cs +++ b/Source/BookGen.Lib/Domain/IO/Configuration/StaticWebsiteConfig.cs @@ -1,14 +1,14 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using Bookgen.Lib.Domain.Validation; +using BookGen.Lib.Domain.Validation; -namespace Bookgen.Lib.Domain.IO.Configuration; +namespace BookGen.Lib.Domain.IO.Configuration; public sealed class StaticWebsiteConfig : OutputConfig { @@ -30,7 +30,7 @@ public sealed class StaticWebsiteConfig : OutputConfig public StaticWebsiteConfig() { - DeployHost = string.Empty; + DeployHost = "http://localhost/"; CssClasses = new CssClasses(); Images = new ImageConfig(); OpenLinksOutsideHostOnNewTab = false; diff --git a/Source/Bookgen.Lib/Domain/IO/Configuration/SvgRecodeOption.cs b/Source/BookGen.Lib/Domain/IO/Configuration/SvgRecodeOption.cs similarity index 79% rename from Source/Bookgen.Lib/Domain/IO/Configuration/SvgRecodeOption.cs rename to Source/BookGen.Lib/Domain/IO/Configuration/SvgRecodeOption.cs index 8a04fa2e..ce344d84 100644 --- a/Source/Bookgen.Lib/Domain/IO/Configuration/SvgRecodeOption.cs +++ b/Source/BookGen.Lib/Domain/IO/Configuration/SvgRecodeOption.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Domain.IO.Configuration; +namespace BookGen.Lib.Domain.IO.Configuration; public enum SvgRecodeOption { diff --git a/Source/Bookgen.Lib/Domain/IO/Configuration/TableOfContentsConfiguration.cs b/Source/BookGen.Lib/Domain/IO/Configuration/TableOfContentsConfiguration.cs similarity index 95% rename from Source/Bookgen.Lib/Domain/IO/Configuration/TableOfContentsConfiguration.cs rename to Source/BookGen.Lib/Domain/IO/Configuration/TableOfContentsConfiguration.cs index 7458fd0f..4d9bf7fb 100644 --- a/Source/Bookgen.Lib/Domain/IO/Configuration/TableOfContentsConfiguration.cs +++ b/Source/BookGen.Lib/Domain/IO/Configuration/TableOfContentsConfiguration.cs @@ -1,12 +1,12 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.ComponentModel; using System.ComponentModel.DataAnnotations; -namespace Bookgen.Lib.Domain.IO.Configuration; +namespace BookGen.Lib.Domain.IO.Configuration; public sealed class TableOfContentsConfiguration { diff --git a/Source/Bookgen.Lib/Domain/IO/Configuration/WordpressConfig.cs b/Source/BookGen.Lib/Domain/IO/Configuration/WordpressConfig.cs similarity index 88% rename from Source/Bookgen.Lib/Domain/IO/Configuration/WordpressConfig.cs rename to Source/BookGen.Lib/Domain/IO/Configuration/WordpressConfig.cs index 1eb59c00..73eefb74 100644 --- a/Source/Bookgen.Lib/Domain/IO/Configuration/WordpressConfig.cs +++ b/Source/BookGen.Lib/Domain/IO/Configuration/WordpressConfig.cs @@ -1,14 +1,14 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using Bookgen.Lib.Domain.Validation; +using BookGen.Lib.Domain.Validation; -namespace Bookgen.Lib.Domain.IO.Configuration; +namespace BookGen.Lib.Domain.IO.Configuration; public sealed class WordpressConfig : OutputConfig { @@ -34,7 +34,7 @@ public sealed class WordpressConfig : OutputConfig public WordpressConfig() { - DeployHost = string.Empty; + DeployHost = "http://localhost/"; ItemType = "docs"; OpenLinksOutsideHostOnNewTab = true; TagCategory = "post_tag"; diff --git a/Source/Bookgen.Lib/Domain/IO/FrontMatter.cs b/Source/BookGen.Lib/Domain/IO/FrontMatter.cs similarity index 91% rename from Source/Bookgen.Lib/Domain/IO/FrontMatter.cs rename to Source/BookGen.Lib/Domain/IO/FrontMatter.cs index 9415792e..feed6ec0 100644 --- a/Source/Bookgen.Lib/Domain/IO/FrontMatter.cs +++ b/Source/BookGen.Lib/Domain/IO/FrontMatter.cs @@ -1,15 +1,15 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.ComponentModel; -using Bookgen.Lib.Domain.Validation; +using BookGen.Lib.Domain.Validation; using YamlDotNet.Serialization; -namespace Bookgen.Lib.Domain.IO; +namespace BookGen.Lib.Domain.IO; public sealed class FrontMatter { diff --git a/Source/Bookgen.Lib/Domain/IO/Legacy/Asset.cs b/Source/BookGen.Lib/Domain/IO/Legacy/Asset.cs similarity index 88% rename from Source/Bookgen.Lib/Domain/IO/Legacy/Asset.cs rename to Source/BookGen.Lib/Domain/IO/Legacy/Asset.cs index 9cc447d7..b30c2602 100644 --- a/Source/Bookgen.Lib/Domain/IO/Legacy/Asset.cs +++ b/Source/BookGen.Lib/Domain/IO/Legacy/Asset.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Domain.IO.Legacy; +namespace BookGen.Lib.Domain.IO.Legacy; public sealed class Asset { diff --git a/Source/Bookgen.Lib/Domain/IO/Legacy/BuildConfig.cs b/Source/BookGen.Lib/Domain/IO/Legacy/BuildConfig.cs similarity index 94% rename from Source/Bookgen.Lib/Domain/IO/Legacy/BuildConfig.cs rename to Source/BookGen.Lib/Domain/IO/Legacy/BuildConfig.cs index c74f3c70..90c30f07 100644 --- a/Source/Bookgen.Lib/Domain/IO/Legacy/BuildConfig.cs +++ b/Source/BookGen.Lib/Domain/IO/Legacy/BuildConfig.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Domain.IO.Legacy; +namespace BookGen.Lib.Domain.IO.Legacy; public sealed class BuildConfig { diff --git a/Source/Bookgen.Lib/Domain/IO/Legacy/Config.cs b/Source/BookGen.Lib/Domain/IO/Legacy/Config.cs similarity index 97% rename from Source/Bookgen.Lib/Domain/IO/Legacy/Config.cs rename to Source/BookGen.Lib/Domain/IO/Legacy/Config.cs index 3ad19da6..1abd22cd 100644 --- a/Source/Bookgen.Lib/Domain/IO/Legacy/Config.cs +++ b/Source/BookGen.Lib/Domain/IO/Legacy/Config.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -7,7 +7,7 @@ using System.Globalization; using System.Text.Json.Serialization; -namespace Bookgen.Lib.Domain.IO.Legacy; +namespace BookGen.Lib.Domain.IO.Legacy; public sealed class Config { diff --git a/Source/Bookgen.Lib/Domain/IO/Legacy/ImageOptions.cs b/Source/BookGen.Lib/Domain/IO/Legacy/ImageOptions.cs similarity index 91% rename from Source/Bookgen.Lib/Domain/IO/Legacy/ImageOptions.cs rename to Source/BookGen.Lib/Domain/IO/Legacy/ImageOptions.cs index 5516c3b9..32120738 100644 --- a/Source/Bookgen.Lib/Domain/IO/Legacy/ImageOptions.cs +++ b/Source/BookGen.Lib/Domain/IO/Legacy/ImageOptions.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Domain.IO.Legacy; +namespace BookGen.Lib.Domain.IO.Legacy; public sealed class ImageOptions { diff --git a/Source/Bookgen.Lib/Domain/IO/Legacy/Link.cs b/Source/BookGen.Lib/Domain/IO/Legacy/Link.cs similarity index 93% rename from Source/Bookgen.Lib/Domain/IO/Legacy/Link.cs rename to Source/BookGen.Lib/Domain/IO/Legacy/Link.cs index a9c44558..27cd57c9 100644 --- a/Source/Bookgen.Lib/Domain/IO/Legacy/Link.cs +++ b/Source/BookGen.Lib/Domain/IO/Legacy/Link.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Domain.IO.Legacy; +namespace BookGen.Lib.Domain.IO.Legacy; /// /// Represents a link in the Markdown Table of Contents. diff --git a/Source/Bookgen.Lib/Domain/IO/Legacy/Metadata.cs b/Source/BookGen.Lib/Domain/IO/Legacy/Metadata.cs similarity index 89% rename from Source/Bookgen.Lib/Domain/IO/Legacy/Metadata.cs rename to Source/BookGen.Lib/Domain/IO/Legacy/Metadata.cs index a10418f3..aa4f198a 100644 --- a/Source/Bookgen.Lib/Domain/IO/Legacy/Metadata.cs +++ b/Source/BookGen.Lib/Domain/IO/Legacy/Metadata.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Domain.IO.Legacy; +namespace BookGen.Lib.Domain.IO.Legacy; public sealed class Metadata { diff --git a/Source/Bookgen.Lib/Domain/IO/Legacy/StyleClasses.cs b/Source/BookGen.Lib/Domain/IO/Legacy/StyleClasses.cs similarity index 95% rename from Source/Bookgen.Lib/Domain/IO/Legacy/StyleClasses.cs rename to Source/BookGen.Lib/Domain/IO/Legacy/StyleClasses.cs index c17187db..e4881f4d 100644 --- a/Source/Bookgen.Lib/Domain/IO/Legacy/StyleClasses.cs +++ b/Source/BookGen.Lib/Domain/IO/Legacy/StyleClasses.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Domain.IO.Legacy; +namespace BookGen.Lib.Domain.IO.Legacy; public sealed class StyleClasses { diff --git a/Source/Bookgen.Lib/Domain/IO/Legacy/TOC.cs b/Source/BookGen.Lib/Domain/IO/Legacy/TOC.cs similarity index 96% rename from Source/Bookgen.Lib/Domain/IO/Legacy/TOC.cs rename to Source/BookGen.Lib/Domain/IO/Legacy/TOC.cs index 9df791bc..71583d83 100644 --- a/Source/Bookgen.Lib/Domain/IO/Legacy/TOC.cs +++ b/Source/BookGen.Lib/Domain/IO/Legacy/TOC.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Collections; -namespace Bookgen.Lib.Domain.IO.Legacy; +namespace BookGen.Lib.Domain.IO.Legacy; public sealed class ToC : IEnumerable { diff --git a/Source/Bookgen.Lib/Domain/IO/Legacy/TemplateOptions.cs b/Source/BookGen.Lib/Domain/IO/Legacy/TemplateOptions.cs similarity index 97% rename from Source/Bookgen.Lib/Domain/IO/Legacy/TemplateOptions.cs rename to Source/BookGen.Lib/Domain/IO/Legacy/TemplateOptions.cs index 27ec1742..bde26166 100644 --- a/Source/Bookgen.Lib/Domain/IO/Legacy/TemplateOptions.cs +++ b/Source/BookGen.Lib/Domain/IO/Legacy/TemplateOptions.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Domain.IO.Legacy; +namespace BookGen.Lib.Domain.IO.Legacy; [Serializable] public sealed class TemplateOptions : Dictionary diff --git a/Source/Bookgen.Lib/Domain/IO/Legacy/Translations.cs b/Source/BookGen.Lib/Domain/IO/Legacy/Translations.cs similarity index 97% rename from Source/Bookgen.Lib/Domain/IO/Legacy/Translations.cs rename to Source/BookGen.Lib/Domain/IO/Legacy/Translations.cs index f0b7ffc1..dd09b7f1 100644 --- a/Source/Bookgen.Lib/Domain/IO/Legacy/Translations.cs +++ b/Source/BookGen.Lib/Domain/IO/Legacy/Translations.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Domain.IO.Legacy; +namespace BookGen.Lib.Domain.IO.Legacy; [Serializable] public sealed class Translations : Dictionary diff --git a/Source/Bookgen.Lib/Domain/IO/TableOfContents.cs b/Source/BookGen.Lib/Domain/IO/TableOfContents.cs similarity index 92% rename from Source/Bookgen.Lib/Domain/IO/TableOfContents.cs rename to Source/BookGen.Lib/Domain/IO/TableOfContents.cs index 9c962247..9c00f2a3 100644 --- a/Source/Bookgen.Lib/Domain/IO/TableOfContents.cs +++ b/Source/BookGen.Lib/Domain/IO/TableOfContents.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -7,9 +7,9 @@ using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; -using Bookgen.Lib.Domain.Validation; +using BookGen.Lib.Domain.Validation; -namespace Bookgen.Lib.Domain.IO; +namespace BookGen.Lib.Domain.IO; public sealed class TableOfContents { diff --git a/Source/Bookgen.Lib/Domain/IO/TocChapter.cs b/Source/BookGen.Lib/Domain/IO/TocChapter.cs similarity index 87% rename from Source/Bookgen.Lib/Domain/IO/TocChapter.cs rename to Source/BookGen.Lib/Domain/IO/TocChapter.cs index a8b17c5c..64484e53 100644 --- a/Source/Bookgen.Lib/Domain/IO/TocChapter.cs +++ b/Source/BookGen.Lib/Domain/IO/TocChapter.cs @@ -1,14 +1,14 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using Bookgen.Lib.Domain.Validation; +using BookGen.Lib.Domain.Validation; -namespace Bookgen.Lib.Domain.IO; +namespace BookGen.Lib.Domain.IO; public sealed class TocChapter { diff --git a/Source/Bookgen.Lib/Domain/PostProcess/ChapterItem.cs b/Source/BookGen.Lib/Domain/PostProcess/ChapterItem.cs similarity index 88% rename from Source/Bookgen.Lib/Domain/PostProcess/ChapterItem.cs rename to Source/BookGen.Lib/Domain/PostProcess/ChapterItem.cs index 13aa3d34..1747b935 100644 --- a/Source/Bookgen.Lib/Domain/PostProcess/ChapterItem.cs +++ b/Source/BookGen.Lib/Domain/PostProcess/ChapterItem.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.ComponentModel; -namespace Bookgen.Lib.Domain.PostProcess; +namespace BookGen.Lib.Domain.PostProcess; public sealed class ChapterItem { diff --git a/Source/Bookgen.Lib/Domain/PostProcess/ExportChapter.cs b/Source/BookGen.Lib/Domain/PostProcess/ExportChapter.cs similarity index 85% rename from Source/Bookgen.Lib/Domain/PostProcess/ExportChapter.cs rename to Source/BookGen.Lib/Domain/PostProcess/ExportChapter.cs index 09c59bf7..3f14a083 100644 --- a/Source/Bookgen.Lib/Domain/PostProcess/ExportChapter.cs +++ b/Source/BookGen.Lib/Domain/PostProcess/ExportChapter.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.ComponentModel; -namespace Bookgen.Lib.Domain.PostProcess; +namespace BookGen.Lib.Domain.PostProcess; public sealed class ExportChapter { diff --git a/Source/Bookgen.Lib/Domain/PostProcess/PostProcessExport.cs b/Source/BookGen.Lib/Domain/PostProcess/PostProcessExport.cs similarity index 86% rename from Source/Bookgen.Lib/Domain/PostProcess/PostProcessExport.cs rename to Source/BookGen.Lib/Domain/PostProcess/PostProcessExport.cs index de381f8b..33479186 100644 --- a/Source/Bookgen.Lib/Domain/PostProcess/PostProcessExport.cs +++ b/Source/BookGen.Lib/Domain/PostProcess/PostProcessExport.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.ComponentModel; -namespace Bookgen.Lib.Domain.PostProcess; +namespace BookGen.Lib.Domain.PostProcess; public sealed class PostProcessExport { diff --git a/Source/Bookgen.Lib/Domain/Resolution.cs b/Source/BookGen.Lib/Domain/Resolution.cs similarity index 95% rename from Source/Bookgen.Lib/Domain/Resolution.cs rename to Source/BookGen.Lib/Domain/Resolution.cs index 31326f33..44f4ac8e 100644 --- a/Source/Bookgen.Lib/Domain/Resolution.cs +++ b/Source/BookGen.Lib/Domain/Resolution.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Diagnostics.CodeAnalysis; -namespace Bookgen.Lib.Domain; +namespace BookGen.Lib.Domain; public record struct Resolution : IParsable { diff --git a/Source/Bookgen.Lib/Domain/Sitemap/Url.cs b/Source/BookGen.Lib/Domain/Sitemap/Url.cs similarity index 90% rename from Source/Bookgen.Lib/Domain/Sitemap/Url.cs rename to Source/BookGen.Lib/Domain/Sitemap/Url.cs index 17c6e0f7..89566823 100644 --- a/Source/Bookgen.Lib/Domain/Sitemap/Url.cs +++ b/Source/BookGen.Lib/Domain/Sitemap/Url.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Sitemap; +namespace BookGen.Lib.Domain.Sitemap; [XmlRoot(ElementName = "url", Namespace = "http://www.sitemaps.org/schemas/sitemap/0.9")] public sealed class Url diff --git a/Source/Bookgen.Lib/Domain/Sitemap/UrlSet.cs b/Source/BookGen.Lib/Domain/Sitemap/UrlSet.cs similarity index 89% rename from Source/Bookgen.Lib/Domain/Sitemap/UrlSet.cs rename to Source/BookGen.Lib/Domain/Sitemap/UrlSet.cs index 26f7597b..1725b8b4 100644 --- a/Source/Bookgen.Lib/Domain/Sitemap/UrlSet.cs +++ b/Source/BookGen.Lib/Domain/Sitemap/UrlSet.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Sitemap; +namespace BookGen.Lib.Domain.Sitemap; [XmlRoot(ElementName = "urlset", Namespace = "http://www.sitemaps.org/schemas/sitemap/0.9")] public sealed class UrlSet diff --git a/Source/Bookgen.Lib/Domain/SourceFile.cs b/Source/BookGen.Lib/Domain/SourceFile.cs similarity index 83% rename from Source/Bookgen.Lib/Domain/SourceFile.cs rename to Source/BookGen.Lib/Domain/SourceFile.cs index 1f54f04d..6aa313c7 100644 --- a/Source/Bookgen.Lib/Domain/SourceFile.cs +++ b/Source/BookGen.Lib/Domain/SourceFile.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.IO; +using BookGen.Lib.Domain.IO; -namespace Bookgen.Lib.Domain; +namespace BookGen.Lib.Domain; public sealed class SourceFile { diff --git a/Source/Bookgen.Lib/Domain/Validation/FileExistsAttribute.cs b/Source/BookGen.Lib/Domain/Validation/FileExistsAttribute.cs similarity index 94% rename from Source/Bookgen.Lib/Domain/Validation/FileExistsAttribute.cs rename to Source/BookGen.Lib/Domain/Validation/FileExistsAttribute.cs index 12633d8f..c4a0e1ee 100644 --- a/Source/Bookgen.Lib/Domain/Validation/FileExistsAttribute.cs +++ b/Source/BookGen.Lib/Domain/Validation/FileExistsAttribute.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -9,7 +9,7 @@ using Microsoft.Extensions.DependencyInjection; -namespace Bookgen.Lib.Domain.Validation; +namespace BookGen.Lib.Domain.Validation; [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class FileExistsAttribute : ValidationAttribute diff --git a/Source/Bookgen.Lib/Domain/Validation/Iso639Language.cs b/Source/BookGen.Lib/Domain/Validation/Iso639Language.cs similarity index 96% rename from Source/Bookgen.Lib/Domain/Validation/Iso639Language.cs rename to Source/BookGen.Lib/Domain/Validation/Iso639Language.cs index 71dd4b3c..973900af 100644 --- a/Source/Bookgen.Lib/Domain/Validation/Iso639Language.cs +++ b/Source/BookGen.Lib/Domain/Validation/Iso639Language.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.ComponentModel.DataAnnotations; -namespace Bookgen.Lib.Domain.Validation; +namespace BookGen.Lib.Domain.Validation; internal sealed class Iso639Language : ValidationAttribute { diff --git a/Source/Bookgen.Lib/Domain/Validation/NotNullOrWhiteSpaceAttribute.cs b/Source/BookGen.Lib/Domain/Validation/NotNullOrWhiteSpaceAttribute.cs similarity index 92% rename from Source/Bookgen.Lib/Domain/Validation/NotNullOrWhiteSpaceAttribute.cs rename to Source/BookGen.Lib/Domain/Validation/NotNullOrWhiteSpaceAttribute.cs index 82242366..a537f08d 100644 --- a/Source/Bookgen.Lib/Domain/Validation/NotNullOrWhiteSpaceAttribute.cs +++ b/Source/BookGen.Lib/Domain/Validation/NotNullOrWhiteSpaceAttribute.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.ComponentModel.DataAnnotations; -namespace Bookgen.Lib.Domain.Validation; +namespace BookGen.Lib.Domain.Validation; [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class NotNullOrWhiteSpaceAttribute : ValidationAttribute diff --git a/Source/Bookgen.Lib/Domain/Validation/ValidUrl.cs b/Source/BookGen.Lib/Domain/Validation/ValidUrl.cs similarity index 93% rename from Source/Bookgen.Lib/Domain/Validation/ValidUrl.cs rename to Source/BookGen.Lib/Domain/Validation/ValidUrl.cs index d7dd1f71..c8b473c8 100644 --- a/Source/Bookgen.Lib/Domain/Validation/ValidUrl.cs +++ b/Source/BookGen.Lib/Domain/Validation/ValidUrl.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.ComponentModel.DataAnnotations; -namespace Bookgen.Lib.Domain.Validation; +namespace BookGen.Lib.Domain.Validation; [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class ValidUrlAttribute : ValidationAttribute diff --git a/Source/Bookgen.Lib/Domain/Validation/WhenNotEmptyFileMustExistAttribute.cs b/Source/BookGen.Lib/Domain/Validation/WhenNotEmptyFileMustExistAttribute.cs similarity index 94% rename from Source/Bookgen.Lib/Domain/Validation/WhenNotEmptyFileMustExistAttribute.cs rename to Source/BookGen.Lib/Domain/Validation/WhenNotEmptyFileMustExistAttribute.cs index 4aa61dc5..50451dd4 100644 --- a/Source/Bookgen.Lib/Domain/Validation/WhenNotEmptyFileMustExistAttribute.cs +++ b/Source/BookGen.Lib/Domain/Validation/WhenNotEmptyFileMustExistAttribute.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -9,7 +9,7 @@ using Microsoft.Extensions.DependencyInjection; -namespace Bookgen.Lib.Domain.Validation; +namespace BookGen.Lib.Domain.Validation; [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class WhenNotEmptyFileMustExistAttribute : ValidationAttribute diff --git a/Source/Bookgen.Lib/Domain/VsCode/Group.cs b/Source/BookGen.Lib/Domain/VsCode/Group.cs similarity index 86% rename from Source/Bookgen.Lib/Domain/VsCode/Group.cs rename to Source/BookGen.Lib/Domain/VsCode/Group.cs index fdd7bdbf..51e8052c 100644 --- a/Source/Bookgen.Lib/Domain/VsCode/Group.cs +++ b/Source/BookGen.Lib/Domain/VsCode/Group.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text.Json.Serialization; -namespace Bookgen.Lib.Domain.VsCode; +namespace BookGen.Lib.Domain.VsCode; public enum Group { diff --git a/Source/Bookgen.Lib/Domain/VsCode/Presentation.cs b/Source/BookGen.Lib/Domain/VsCode/Presentation.cs similarity index 89% rename from Source/Bookgen.Lib/Domain/VsCode/Presentation.cs rename to Source/BookGen.Lib/Domain/VsCode/Presentation.cs index b1834a1a..7b73a522 100644 --- a/Source/Bookgen.Lib/Domain/VsCode/Presentation.cs +++ b/Source/BookGen.Lib/Domain/VsCode/Presentation.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text.Json.Serialization; -namespace Bookgen.Lib.Domain.VsCode; +namespace BookGen.Lib.Domain.VsCode; public sealed class Presentation { diff --git a/Source/Bookgen.Lib/Domain/VsCode/PresentationPanel.cs b/Source/BookGen.Lib/Domain/VsCode/PresentationPanel.cs similarity index 86% rename from Source/Bookgen.Lib/Domain/VsCode/PresentationPanel.cs rename to Source/BookGen.Lib/Domain/VsCode/PresentationPanel.cs index fc9fe266..e8ad95a1 100644 --- a/Source/Bookgen.Lib/Domain/VsCode/PresentationPanel.cs +++ b/Source/BookGen.Lib/Domain/VsCode/PresentationPanel.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text.Json.Serialization; -namespace Bookgen.Lib.Domain.VsCode; +namespace BookGen.Lib.Domain.VsCode; public enum PresentationPanel { diff --git a/Source/BookGen.Lib/Domain/VsCode/RecommendedExtensions.cs b/Source/BookGen.Lib/Domain/VsCode/RecommendedExtensions.cs new file mode 100644 index 00000000..22c114ae --- /dev/null +++ b/Source/BookGen.Lib/Domain/VsCode/RecommendedExtensions.cs @@ -0,0 +1,14 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Text.Json.Serialization; + +namespace BookGen.Lib.Domain.VsCode; + +public sealed class RecommendedExtensions +{ + [JsonPropertyName("recommendations")] + public required string[] Recommendations { get; set; } +} diff --git a/Source/Bookgen.Lib/Domain/VsCode/Reveal.cs b/Source/BookGen.Lib/Domain/VsCode/Reveal.cs similarity index 86% rename from Source/Bookgen.Lib/Domain/VsCode/Reveal.cs rename to Source/BookGen.Lib/Domain/VsCode/Reveal.cs index 435c8200..604ca8ed 100644 --- a/Source/Bookgen.Lib/Domain/VsCode/Reveal.cs +++ b/Source/BookGen.Lib/Domain/VsCode/Reveal.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text.Json.Serialization; -namespace Bookgen.Lib.Domain.VsCode; +namespace BookGen.Lib.Domain.VsCode; public enum Reveal { diff --git a/Source/Bookgen.Lib/Domain/VsCode/TaskType.cs b/Source/BookGen.Lib/Domain/VsCode/TaskType.cs similarity index 84% rename from Source/Bookgen.Lib/Domain/VsCode/TaskType.cs rename to Source/BookGen.Lib/Domain/VsCode/TaskType.cs index a7a23414..a97f73f7 100644 --- a/Source/Bookgen.Lib/Domain/VsCode/TaskType.cs +++ b/Source/BookGen.Lib/Domain/VsCode/TaskType.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text.Json.Serialization; -namespace Bookgen.Lib.Domain.VsCode; +namespace BookGen.Lib.Domain.VsCode; public enum TaskType { diff --git a/Source/Bookgen.Lib/Domain/VsCode/VsCodeTask.cs b/Source/BookGen.Lib/Domain/VsCode/VsCodeTask.cs similarity index 92% rename from Source/Bookgen.Lib/Domain/VsCode/VsCodeTask.cs rename to Source/BookGen.Lib/Domain/VsCode/VsCodeTask.cs index ebd21188..273cb683 100644 --- a/Source/Bookgen.Lib/Domain/VsCode/VsCodeTask.cs +++ b/Source/BookGen.Lib/Domain/VsCode/VsCodeTask.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text.Json.Serialization; -namespace Bookgen.Lib.Domain.VsCode; +namespace BookGen.Lib.Domain.VsCode; public sealed class VsCodeTask { diff --git a/Source/Bookgen.Lib/Domain/VsCode/VsCodeTasks.cs b/Source/BookGen.Lib/Domain/VsCode/VsCodeTasks.cs similarity index 86% rename from Source/Bookgen.Lib/Domain/VsCode/VsCodeTasks.cs rename to Source/BookGen.Lib/Domain/VsCode/VsCodeTasks.cs index f92ba76f..d49e8b14 100644 --- a/Source/Bookgen.Lib/Domain/VsCode/VsCodeTasks.cs +++ b/Source/BookGen.Lib/Domain/VsCode/VsCodeTasks.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text.Json.Serialization; -namespace Bookgen.Lib.Domain.VsCode; +namespace BookGen.Lib.Domain.VsCode; public sealed class VsCodeTasks { diff --git a/Source/Bookgen.Lib/Domain/VsCode/VsCodeVars.cs b/Source/BookGen.Lib/Domain/VsCode/VsCodeVars.cs similarity index 92% rename from Source/Bookgen.Lib/Domain/VsCode/VsCodeVars.cs rename to Source/BookGen.Lib/Domain/VsCode/VsCodeVars.cs index 7a32c792..1521a68e 100644 --- a/Source/Bookgen.Lib/Domain/VsCode/VsCodeVars.cs +++ b/Source/BookGen.Lib/Domain/VsCode/VsCodeVars.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Domain.VsCode; +namespace BookGen.Lib.Domain.VsCode; public static class VsCodeVars { diff --git a/Source/Bookgen.Lib/Domain/Wordpress/Author.cs b/Source/BookGen.Lib/Domain/Wordpress/Author.cs similarity index 94% rename from Source/Bookgen.Lib/Domain/Wordpress/Author.cs rename to Source/BookGen.Lib/Domain/Wordpress/Author.cs index 068bc380..5944b686 100644 --- a/Source/Bookgen.Lib/Domain/Wordpress/Author.cs +++ b/Source/BookGen.Lib/Domain/Wordpress/Author.cs @@ -1,12 +1,12 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml; using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Wordpress; +namespace BookGen.Lib.Domain.Wordpress; [XmlRoot(ElementName = "author", Namespace = "http://wordpress.org/export/1.2/")] public sealed class Author diff --git a/Source/Bookgen.Lib/Domain/Wordpress/CData.cs b/Source/BookGen.Lib/Domain/Wordpress/CData.cs similarity index 94% rename from Source/Bookgen.Lib/Domain/Wordpress/CData.cs rename to Source/BookGen.Lib/Domain/Wordpress/CData.cs index 3cdb3a2d..9dc0d4ca 100644 --- a/Source/Bookgen.Lib/Domain/Wordpress/CData.cs +++ b/Source/BookGen.Lib/Domain/Wordpress/CData.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -7,7 +7,7 @@ using System.Xml.Schema; using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Wordpress; +namespace BookGen.Lib.Domain.Wordpress; public class CData : IXmlSerializable { diff --git a/Source/Bookgen.Lib/Domain/Wordpress/Channel.cs b/Source/BookGen.Lib/Domain/Wordpress/Channel.cs similarity index 95% rename from Source/Bookgen.Lib/Domain/Wordpress/Channel.cs rename to Source/BookGen.Lib/Domain/Wordpress/Channel.cs index 3b872adb..ce045560 100644 --- a/Source/Bookgen.Lib/Domain/Wordpress/Channel.cs +++ b/Source/BookGen.Lib/Domain/Wordpress/Channel.cs @@ -1,12 +1,12 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml; using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Wordpress; +namespace BookGen.Lib.Domain.Wordpress; [XmlRoot(ElementName = "channel")] public sealed class Channel diff --git a/Source/Bookgen.Lib/Domain/Wordpress/Guid.cs b/Source/BookGen.Lib/Domain/Wordpress/Guid.cs similarity index 86% rename from Source/Bookgen.Lib/Domain/Wordpress/Guid.cs rename to Source/BookGen.Lib/Domain/Wordpress/Guid.cs index 2296fbc4..d0096b68 100644 --- a/Source/Bookgen.Lib/Domain/Wordpress/Guid.cs +++ b/Source/BookGen.Lib/Domain/Wordpress/Guid.cs @@ -1,12 +1,12 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml; using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Wordpress; +namespace BookGen.Lib.Domain.Wordpress; [XmlRoot(ElementName = "guid")] public sealed class Guid diff --git a/Source/Bookgen.Lib/Domain/Wordpress/Item.cs b/Source/BookGen.Lib/Domain/Wordpress/Item.cs similarity index 97% rename from Source/Bookgen.Lib/Domain/Wordpress/Item.cs rename to Source/BookGen.Lib/Domain/Wordpress/Item.cs index 12961de3..b660db76 100644 --- a/Source/Bookgen.Lib/Domain/Wordpress/Item.cs +++ b/Source/BookGen.Lib/Domain/Wordpress/Item.cs @@ -1,12 +1,12 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml; using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Wordpress; +namespace BookGen.Lib.Domain.Wordpress; [XmlRoot(ElementName = "item")] public sealed class Item diff --git a/Source/Bookgen.Lib/Domain/Wordpress/PostCategory.cs b/Source/BookGen.Lib/Domain/Wordpress/PostCategory.cs similarity index 89% rename from Source/Bookgen.Lib/Domain/Wordpress/PostCategory.cs rename to Source/BookGen.Lib/Domain/Wordpress/PostCategory.cs index 96c0a24a..6e213673 100644 --- a/Source/Bookgen.Lib/Domain/Wordpress/PostCategory.cs +++ b/Source/BookGen.Lib/Domain/Wordpress/PostCategory.cs @@ -1,12 +1,12 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml; using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Wordpress; +namespace BookGen.Lib.Domain.Wordpress; [XmlRoot(ElementName = "category")] public class PostCategory : CData diff --git a/Source/Bookgen.Lib/Domain/Wordpress/Postmeta.cs b/Source/BookGen.Lib/Domain/Wordpress/Postmeta.cs similarity index 89% rename from Source/Bookgen.Lib/Domain/Wordpress/Postmeta.cs rename to Source/BookGen.Lib/Domain/Wordpress/Postmeta.cs index 2071c48e..c96d20c3 100644 --- a/Source/Bookgen.Lib/Domain/Wordpress/Postmeta.cs +++ b/Source/BookGen.Lib/Domain/Wordpress/Postmeta.cs @@ -1,12 +1,12 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml; using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Wordpress; +namespace BookGen.Lib.Domain.Wordpress; [XmlRoot(ElementName = "postmeta", Namespace = "http://wordpress.org/export/1.2/")] public class Postmeta diff --git a/Source/Bookgen.Lib/Domain/Wordpress/Rss.cs b/Source/BookGen.Lib/Domain/Wordpress/Rss.cs similarity index 93% rename from Source/Bookgen.Lib/Domain/Wordpress/Rss.cs rename to Source/BookGen.Lib/Domain/Wordpress/Rss.cs index 10d634ef..011f7c92 100644 --- a/Source/Bookgen.Lib/Domain/Wordpress/Rss.cs +++ b/Source/BookGen.Lib/Domain/Wordpress/Rss.cs @@ -1,12 +1,12 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml; using System.Xml.Serialization; -namespace Bookgen.Lib.Domain.Wordpress; +namespace BookGen.Lib.Domain.Wordpress; [XmlRoot(ElementName = "rss")] public sealed class Rss diff --git a/Source/Bookgen.Lib/EnvironmentStatus.cs b/Source/BookGen.Lib/EnvironmentStatus.cs similarity index 94% rename from Source/Bookgen.Lib/EnvironmentStatus.cs rename to Source/BookGen.Lib/EnvironmentStatus.cs index 2f7212b0..0fa1cd1d 100644 --- a/Source/Bookgen.Lib/EnvironmentStatus.cs +++ b/Source/BookGen.Lib/EnvironmentStatus.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Collections; -namespace Bookgen.Lib; +namespace BookGen.Lib; public sealed class EnvironmentStatus : ICollection { diff --git a/Source/Bookgen.Lib/FileNameConstants.cs b/Source/BookGen.Lib/FileNameConstants.cs similarity index 92% rename from Source/Bookgen.Lib/FileNameConstants.cs rename to Source/BookGen.Lib/FileNameConstants.cs index 0a4841e4..2cb10a96 100644 --- a/Source/Bookgen.Lib/FileNameConstants.cs +++ b/Source/BookGen.Lib/FileNameConstants.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib; +namespace BookGen.Lib; public static class FileNameConstants { diff --git a/Source/Bookgen.Lib/Http/ApiMetaData.cs b/Source/BookGen.Lib/Http/ApiMetaData.cs similarity index 89% rename from Source/Bookgen.Lib/Http/ApiMetaData.cs rename to Source/BookGen.Lib/Http/ApiMetaData.cs index bf060bd8..e4e76f10 100644 --- a/Source/Bookgen.Lib/Http/ApiMetaData.cs +++ b/Source/BookGen.Lib/Http/ApiMetaData.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Diagnostics.CodeAnalysis; -namespace Bookgen.Lib.Http; +namespace BookGen.Lib.Http; internal sealed record class ApiMetaData : UrlMetaData { diff --git a/Source/Bookgen.Lib/Http/ApiMethod.cs b/Source/BookGen.Lib/Http/ApiMethod.cs similarity index 85% rename from Source/Bookgen.Lib/Http/ApiMethod.cs rename to Source/BookGen.Lib/Http/ApiMethod.cs index 258eda85..cb1f6189 100644 --- a/Source/Bookgen.Lib/Http/ApiMethod.cs +++ b/Source/BookGen.Lib/Http/ApiMethod.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Http; +namespace BookGen.Lib.Http; internal enum ApiMethod { diff --git a/Source/Bookgen.Lib/Http/ConsoleHttpServerRunner.cs b/Source/BookGen.Lib/Http/ConsoleHttpServerRunner.cs similarity index 93% rename from Source/Bookgen.Lib/Http/ConsoleHttpServerRunner.cs rename to Source/BookGen.Lib/Http/ConsoleHttpServerRunner.cs index 485986b9..6e01d5df 100644 --- a/Source/Bookgen.Lib/Http/ConsoleHttpServerRunner.cs +++ b/Source/BookGen.Lib/Http/ConsoleHttpServerRunner.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Http; +namespace BookGen.Lib.Http; public sealed class ConsoleHttpServerRunner : IAsyncDisposable { diff --git a/Source/Bookgen.Lib/Http/ErrorPageTemplate.html b/Source/BookGen.Lib/Http/ErrorPageTemplate.html similarity index 100% rename from Source/Bookgen.Lib/Http/ErrorPageTemplate.html rename to Source/BookGen.Lib/Http/ErrorPageTemplate.html diff --git a/Source/Bookgen.Lib/Http/HttpServer.cs b/Source/BookGen.Lib/Http/HttpServer.cs similarity index 98% rename from Source/Bookgen.Lib/Http/HttpServer.cs rename to Source/BookGen.Lib/Http/HttpServer.cs index 68ddfcd1..fa7977c6 100644 --- a/Source/Bookgen.Lib/Http/HttpServer.cs +++ b/Source/BookGen.Lib/Http/HttpServer.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -15,7 +15,7 @@ using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Http; +namespace BookGen.Lib.Http; internal sealed class HttpServer : IHttpServer { @@ -114,6 +114,7 @@ public void AddRoute(ApiMetaData metaData, RequestDelegate handler) case ApiMethod.Patch: _app.MapPatch(metaData.Path, async context => await handler(context)); break; + case ApiMethod.Connect: case ApiMethod.Head: case ApiMethod.Options: case ApiMethod.Trace: diff --git a/Source/Bookgen.Lib/Http/IHttpServer.cs b/Source/BookGen.Lib/Http/IHttpServer.cs similarity index 86% rename from Source/Bookgen.Lib/Http/IHttpServer.cs rename to Source/BookGen.Lib/Http/IHttpServer.cs index 1ec21cf6..0c149103 100644 --- a/Source/Bookgen.Lib/Http/IHttpServer.cs +++ b/Source/BookGen.Lib/Http/IHttpServer.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Http; +namespace BookGen.Lib.Http; public interface IHttpServer : IAsyncDisposable { diff --git a/Source/Bookgen.Lib/Http/LoggerProvider.cs b/Source/BookGen.Lib/Http/LoggerProvider.cs similarity index 89% rename from Source/Bookgen.Lib/Http/LoggerProvider.cs rename to Source/BookGen.Lib/Http/LoggerProvider.cs index 40408008..49f284ef 100644 --- a/Source/Bookgen.Lib/Http/LoggerProvider.cs +++ b/Source/BookGen.Lib/Http/LoggerProvider.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Http; +namespace BookGen.Lib.Http; internal sealed class LoggerProvider : ILoggerProvider { diff --git a/Source/Bookgen.Lib/Http/MimeTypes.cs b/Source/BookGen.Lib/Http/MimeTypes.cs similarity index 99% rename from Source/Bookgen.Lib/Http/MimeTypes.cs rename to Source/BookGen.Lib/Http/MimeTypes.cs index 852abaa1..8143a5bf 100644 --- a/Source/Bookgen.Lib/Http/MimeTypes.cs +++ b/Source/BookGen.Lib/Http/MimeTypes.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.StaticFiles; -namespace Bookgen.Lib.Http; +namespace BookGen.Lib.Http; /// /// Provides mime type resolving functions diff --git a/Source/Bookgen.Lib/Http/PageFactory.cs b/Source/BookGen.Lib/Http/PageFactory.cs similarity index 88% rename from Source/Bookgen.Lib/Http/PageFactory.cs rename to Source/BookGen.Lib/Http/PageFactory.cs index 0ab31d56..10f408cd 100644 --- a/Source/Bookgen.Lib/Http/PageFactory.cs +++ b/Source/BookGen.Lib/Http/PageFactory.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -7,7 +7,7 @@ using System.Text; using System.Web; -namespace Bookgen.Lib.Http; +namespace BookGen.Lib.Http; internal static class PageFactory { @@ -24,7 +24,7 @@ private static string GetResource(string resoruceName) public static string GetErrorPage(int code, string message) { - var page = GetResource($"Bookgen.Lib.Http.ErrorPageTemplate.html"); + var page = GetResource($"BookGen.Lib.Http.ErrorPageTemplate.html"); return page.Replace("{{code}}", code.ToString()).Replace("{{message}}", message); } @@ -39,6 +39,6 @@ public static string GetQrCodePage(IEnumerable urls) qrcodes.AppendLine(""); } - return GetResource("Bookgen.Lib.Http.QRCodeTemplate.html").Replace("{{links}}", qrcodes.ToString()); + return GetResource("BookGen.Lib.Http.QRCodeTemplate.html").Replace("{{links}}", qrcodes.ToString()); } } diff --git a/Source/Bookgen.Lib/Http/QRCodeTemplate.html b/Source/BookGen.Lib/Http/QRCodeTemplate.html similarity index 100% rename from Source/Bookgen.Lib/Http/QRCodeTemplate.html rename to Source/BookGen.Lib/Http/QRCodeTemplate.html diff --git a/Source/Bookgen.Lib/Http/ServerFactory.cs b/Source/BookGen.Lib/Http/ServerFactory.cs similarity index 97% rename from Source/Bookgen.Lib/Http/ServerFactory.cs rename to Source/BookGen.Lib/Http/ServerFactory.cs index fb2890b4..ca0fe4cc 100644 --- a/Source/Bookgen.Lib/Http/ServerFactory.cs +++ b/Source/BookGen.Lib/Http/ServerFactory.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Http; +namespace BookGen.Lib.Http; public static class ServerFactory { diff --git a/Source/Bookgen.Lib/Http/UrlMetaData.cs b/Source/BookGen.Lib/Http/UrlMetaData.cs similarity index 89% rename from Source/Bookgen.Lib/Http/UrlMetaData.cs rename to Source/BookGen.Lib/Http/UrlMetaData.cs index 518100ee..e1032938 100644 --- a/Source/Bookgen.Lib/Http/UrlMetaData.cs +++ b/Source/BookGen.Lib/Http/UrlMetaData.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Diagnostics.CodeAnalysis; -namespace Bookgen.Lib.Http; +namespace BookGen.Lib.Http; internal record class UrlMetaData { diff --git a/Source/BookGen.Lib/Internals/Extensions.cs b/Source/BookGen.Lib/Internals/Extensions.cs new file mode 100644 index 00000000..ef6ecec0 --- /dev/null +++ b/Source/BookGen.Lib/Internals/Extensions.cs @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Globalization; + +using BookGen.Lib.Domain.IO; + +using BookGen.Vfs; + +using Markdig.Syntax; +using Markdig.Syntax.Inlines; + +using Microsoft.Extensions.Logging; + +namespace BookGen.Lib.Internals; + +internal static class Extensions +{ + private const string W3cTime = "yyyy-MM-ddTHH:mm:sszzz"; + private const string W3zTime = "yyyy-MM-ddTHH:mm:ss"; + private const string WorpdressTime = "ddd, d MMM yyyy HH:mm:ss"; + private const string WordpressPostDate = "yyyy-MM-dd HH:mm:ss"; + + extension(DateTime dt) + { + public string ToW3CTimeFormat() + => dt.ToString(W3cTime); + + public string ToW3CZTimeFormat() + => dt.ToString(W3zTime) + "Z"; + + public string ToWordpressTime() + => dt.ToString(WorpdressTime, new CultureInfo("en-US")) + " +0000"; + + public string ToWordpressPostDate() + => dt.ToString(WordpressPostDate); + } + + extension(IReadOnlyFileSystem folder) + { + public async Task GetCoverFileName(TableOfContents tableOfContents, ILogger logger) + { + var contents = await folder.ReadAllTextAsync(tableOfContents.IndexFile); + foreach (Block block in Markdig.Markdown.Parse(contents)) + { + if (block is ParagraphBlock paragraph && paragraph.Inline != null) + { + foreach (Inline inline in paragraph.Inline) + { + if (inline is LinkInline link && link.IsImage) + { + return link.Url; + } + } + } + } + logger.LogWarning("No cover image found in {file}", tableOfContents.IndexFile); + return null; + } + } +} diff --git a/Source/Bookgen.Lib/Internals/IdGenerator.cs b/Source/BookGen.Lib/Internals/IdGenerator.cs similarity index 92% rename from Source/Bookgen.Lib/Internals/IdGenerator.cs rename to Source/BookGen.Lib/Internals/IdGenerator.cs index 5f2cae76..0bd44ff9 100644 --- a/Source/Bookgen.Lib/Internals/IdGenerator.cs +++ b/Source/BookGen.Lib/Internals/IdGenerator.cs @@ -1,14 +1,14 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Security.Cryptography; using System.Text; -using Bookgen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Images; -namespace Bookgen.Lib.Internals; +namespace BookGen.Lib.Internals; internal static class IdGenerator { diff --git a/Source/Bookgen.Lib/Internals/SerializedObjectValidator.cs b/Source/BookGen.Lib/Internals/SerializedObjectValidator.cs similarity index 98% rename from Source/Bookgen.Lib/Internals/SerializedObjectValidator.cs rename to Source/BookGen.Lib/Internals/SerializedObjectValidator.cs index 5cf380cd..cba8ca99 100644 --- a/Source/Bookgen.Lib/Internals/SerializedObjectValidator.cs +++ b/Source/BookGen.Lib/Internals/SerializedObjectValidator.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -11,7 +11,7 @@ using YamlDotNet.Serialization.BufferedDeserialization; -namespace Bookgen.Lib.Internals; +namespace BookGen.Lib.Internals; internal class SerializedObjectValidator { diff --git a/Source/Bookgen.Lib/Internals/StringExtensions.cs b/Source/BookGen.Lib/Internals/StringExtensions.cs similarity index 97% rename from Source/Bookgen.Lib/Internals/StringExtensions.cs rename to Source/BookGen.Lib/Internals/StringExtensions.cs index 2a277e5e..83f41a97 100644 --- a/Source/Bookgen.Lib/Internals/StringExtensions.cs +++ b/Source/BookGen.Lib/Internals/StringExtensions.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -7,7 +7,7 @@ using System.Text; using System.Text.RegularExpressions; -namespace Bookgen.Lib.Internals; +namespace BookGen.Lib.Internals; internal static partial class StringExtensions { diff --git a/Source/Bookgen.Lib/JavascriptBuilder.cs b/Source/BookGen.Lib/JavascriptBuilder.cs similarity index 95% rename from Source/Bookgen.Lib/JavascriptBuilder.cs rename to Source/BookGen.Lib/JavascriptBuilder.cs index c4a1cbc4..4f58f25c 100644 --- a/Source/Bookgen.Lib/JavascriptBuilder.cs +++ b/Source/BookGen.Lib/JavascriptBuilder.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text; -namespace Bookgen.Lib; +namespace BookGen.Lib; public sealed class JavascriptBuilder { diff --git a/Source/Bookgen.Lib/MarkdownBuilder.cs b/Source/BookGen.Lib/MarkdownBuilder.cs similarity index 97% rename from Source/Bookgen.Lib/MarkdownBuilder.cs rename to Source/BookGen.Lib/MarkdownBuilder.cs index 1a846626..1882a97a 100644 --- a/Source/Bookgen.Lib/MarkdownBuilder.cs +++ b/Source/BookGen.Lib/MarkdownBuilder.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text; -namespace Bookgen.Lib; +namespace BookGen.Lib; public sealed class MarkdownBuilder { diff --git a/Source/Bookgen.Lib/Pipeline/Epub/CreateContainer.cs b/Source/BookGen.Lib/Pipeline/Epub/CreateContainer.cs similarity index 92% rename from Source/Bookgen.Lib/Pipeline/Epub/CreateContainer.cs rename to Source/BookGen.Lib/Pipeline/Epub/CreateContainer.cs index 3243fd86..0cdfbcee 100644 --- a/Source/Bookgen.Lib/Pipeline/Epub/CreateContainer.cs +++ b/Source/BookGen.Lib/Pipeline/Epub/CreateContainer.cs @@ -1,13 +1,13 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.Epub; +using BookGen.Lib.Domain.Epub; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.Epub; +namespace BookGen.Lib.Pipeline.Epub; internal sealed class CreateContainer : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/Epub/CreateContentOpf.cs b/Source/BookGen.Lib/Pipeline/Epub/CreateContentOpf.cs similarity index 96% rename from Source/Bookgen.Lib/Pipeline/Epub/CreateContentOpf.cs rename to Source/BookGen.Lib/Pipeline/Epub/CreateContentOpf.cs index 60d7d100..d3875f13 100644 --- a/Source/Bookgen.Lib/Pipeline/Epub/CreateContentOpf.cs +++ b/Source/BookGen.Lib/Pipeline/Epub/CreateContentOpf.cs @@ -1,14 +1,14 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.Epub; -using Bookgen.Lib.Internals; +using BookGen.Lib.Domain.Epub; +using BookGen.Lib.Internals; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.Epub; +namespace BookGen.Lib.Pipeline.Epub; internal sealed class CreateContentOpf : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/Epub/CreateEpubCoverAndStyle.cs b/Source/BookGen.Lib/Pipeline/Epub/CreateEpubCoverAndStyle.cs similarity index 91% rename from Source/Bookgen.Lib/Pipeline/Epub/CreateEpubCoverAndStyle.cs rename to Source/BookGen.Lib/Pipeline/Epub/CreateEpubCoverAndStyle.cs index 4c36e529..662289b1 100644 --- a/Source/Bookgen.Lib/Pipeline/Epub/CreateEpubCoverAndStyle.cs +++ b/Source/BookGen.Lib/Pipeline/Epub/CreateEpubCoverAndStyle.cs @@ -5,13 +5,13 @@ using System.Text; -using Bookgen.Lib.Domain.Epub; -using Bookgen.Lib.Internals; -using Bookgen.Lib.Rendering.Images; +using BookGen.Lib.Domain.Epub; +using BookGen.Lib.Internals; +using BookGen.Lib.Rendering.Images; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.Epub; +namespace BookGen.Lib.Pipeline.Epub; internal sealed class CreateEpubCoverAndStyle : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/Epub/CreateFontFiles.cs b/Source/BookGen.Lib/Pipeline/Epub/CreateFontFiles.cs similarity index 94% rename from Source/Bookgen.Lib/Pipeline/Epub/CreateFontFiles.cs rename to Source/BookGen.Lib/Pipeline/Epub/CreateFontFiles.cs index e48cfd4a..1c5f9309 100644 --- a/Source/Bookgen.Lib/Pipeline/Epub/CreateFontFiles.cs +++ b/Source/BookGen.Lib/Pipeline/Epub/CreateFontFiles.cs @@ -1,15 +1,15 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.IO.Compression; -using Bookgen.Lib.Domain.Epub; +using BookGen.Lib.Domain.Epub; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.Epub; +namespace BookGen.Lib.Pipeline.Epub; internal sealed class CreateFontFiles : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/Epub/CreateHtmlPages.cs b/Source/BookGen.Lib/Pipeline/Epub/CreateHtmlPages.cs similarity index 90% rename from Source/Bookgen.Lib/Pipeline/Epub/CreateHtmlPages.cs rename to Source/BookGen.Lib/Pipeline/Epub/CreateHtmlPages.cs index a495f60b..dafdef94 100644 --- a/Source/Bookgen.Lib/Pipeline/Epub/CreateHtmlPages.cs +++ b/Source/BookGen.Lib/Pipeline/Epub/CreateHtmlPages.cs @@ -5,23 +5,23 @@ using System.Text; -using Bookgen.Lib.Domain; -using Bookgen.Lib.Domain.Epub; -using Bookgen.Lib.Domain.IO; -using Bookgen.Lib.Domain.IO.Configuration; -using Bookgen.Lib.Internals; -using Bookgen.Lib.Rendering.Images; -using Bookgen.Lib.Rendering.Markdown; -using Bookgen.Lib.Rendering.Markdown.RenderInterop; -using Bookgen.Lib.Rendering.Templates; -using Bookgen.Lib.Templates; +using BookGen.Lib.Domain; +using BookGen.Lib.Domain.Epub; +using BookGen.Lib.Domain.IO; +using BookGen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Internals; +using BookGen.Lib.Rendering; +using BookGen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Markdown; +using BookGen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Rendering.Templates; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; -using static Bookgen.Lib.Pipeline.Epub.EpubState; +using static BookGen.Lib.Pipeline.Epub.EpubState; -namespace Bookgen.Lib.Pipeline.Epub; +namespace BookGen.Lib.Pipeline.Epub; internal class CreateHtmlPages : PipeLineStep { @@ -71,7 +71,7 @@ public override async Task ExecuteAsync(IBookEnvironment environment int chapterId = 1; int fileId = 1; - string template = environment.GetAsset("Epub.html"); + string template = environment.GetAsset(BundledAssets.TemplateEpub); logger.LogInformation("Rendering cover/index..."); diff --git a/Source/Bookgen.Lib/Pipeline/Epub/CreateImageFiles.cs b/Source/BookGen.Lib/Pipeline/Epub/CreateImageFiles.cs similarity index 88% rename from Source/Bookgen.Lib/Pipeline/Epub/CreateImageFiles.cs rename to Source/BookGen.Lib/Pipeline/Epub/CreateImageFiles.cs index 688321b4..d1752534 100644 --- a/Source/Bookgen.Lib/Pipeline/Epub/CreateImageFiles.cs +++ b/Source/BookGen.Lib/Pipeline/Epub/CreateImageFiles.cs @@ -1,15 +1,15 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.Epub; -using Bookgen.Lib.Http; -using Bookgen.Lib.Internals; +using BookGen.Lib.Domain.Epub; +using BookGen.Lib.Http; +using BookGen.Lib.Internals; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.Epub; +namespace BookGen.Lib.Pipeline.Epub; internal sealed class CreateImageFiles : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/Epub/CreateMimeType.cs b/Source/BookGen.Lib/Pipeline/Epub/CreateMimeType.cs similarity index 91% rename from Source/Bookgen.Lib/Pipeline/Epub/CreateMimeType.cs rename to Source/BookGen.Lib/Pipeline/Epub/CreateMimeType.cs index 70230b6d..2f4a729a 100644 --- a/Source/Bookgen.Lib/Pipeline/Epub/CreateMimeType.cs +++ b/Source/BookGen.Lib/Pipeline/Epub/CreateMimeType.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -8,7 +8,7 @@ using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.Epub; +namespace BookGen.Lib.Pipeline.Epub; internal sealed class CreateMimeType : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/Epub/CreateNav.cs b/Source/BookGen.Lib/Pipeline/Epub/CreateNav.cs similarity index 94% rename from Source/Bookgen.Lib/Pipeline/Epub/CreateNav.cs rename to Source/BookGen.Lib/Pipeline/Epub/CreateNav.cs index 6772c169..9127fcb9 100644 --- a/Source/Bookgen.Lib/Pipeline/Epub/CreateNav.cs +++ b/Source/BookGen.Lib/Pipeline/Epub/CreateNav.cs @@ -1,18 +1,17 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text; -using Bookgen.Lib.Domain.Epub; -using Bookgen.Lib.Internals; -using Bookgen.Lib.Rendering.Templates; -using Bookgen.Lib.Templates; +using BookGen.Lib.Domain.Epub; +using BookGen.Lib.Internals; +using BookGen.Lib.Rendering.Templates; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.Epub; +namespace BookGen.Lib.Pipeline.Epub; internal sealed class CreateNav : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/Epub/DeInitialize.cs b/Source/BookGen.Lib/Pipeline/Epub/DeInitialize.cs similarity index 91% rename from Source/Bookgen.Lib/Pipeline/Epub/DeInitialize.cs rename to Source/BookGen.Lib/Pipeline/Epub/DeInitialize.cs index 45ed9698..a800b618 100644 --- a/Source/Bookgen.Lib/Pipeline/Epub/DeInitialize.cs +++ b/Source/BookGen.Lib/Pipeline/Epub/DeInitialize.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.Epub; +namespace BookGen.Lib.Pipeline.Epub; internal sealed class DeInitialize : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/Epub/EpubState.cs b/Source/BookGen.Lib/Pipeline/Epub/EpubState.cs similarity index 92% rename from Source/Bookgen.Lib/Pipeline/Epub/EpubState.cs rename to Source/BookGen.Lib/Pipeline/Epub/EpubState.cs index 6e25db3b..74c82b31 100644 --- a/Source/Bookgen.Lib/Pipeline/Epub/EpubState.cs +++ b/Source/BookGen.Lib/Pipeline/Epub/EpubState.cs @@ -1,14 +1,14 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.Epub; -using Bookgen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Domain.Epub; +using BookGen.Lib.Domain.IO.Configuration; using BookGen.Vfs; -namespace Bookgen.Lib.Pipeline.Epub; +namespace BookGen.Lib.Pipeline.Epub; internal sealed class EpubState { diff --git a/Source/Bookgen.Lib/Pipeline/Epub/Initialize.cs b/Source/BookGen.Lib/Pipeline/Epub/Initialize.cs similarity index 90% rename from Source/Bookgen.Lib/Pipeline/Epub/Initialize.cs rename to Source/BookGen.Lib/Pipeline/Epub/Initialize.cs index 398ce1c3..886cd3d6 100644 --- a/Source/Bookgen.Lib/Pipeline/Epub/Initialize.cs +++ b/Source/BookGen.Lib/Pipeline/Epub/Initialize.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.Epub; +namespace BookGen.Lib.Pipeline.Epub; internal sealed class Initialize : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/EpubTocRenderer.cs b/Source/BookGen.Lib/Pipeline/EpubTocRenderer.cs similarity index 96% rename from Source/Bookgen.Lib/Pipeline/EpubTocRenderer.cs rename to Source/BookGen.Lib/Pipeline/EpubTocRenderer.cs index 5453a5de..1653bae5 100644 --- a/Source/Bookgen.Lib/Pipeline/EpubTocRenderer.cs +++ b/Source/BookGen.Lib/Pipeline/EpubTocRenderer.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text; -namespace Bookgen.Lib.Pipeline; +namespace BookGen.Lib.Pipeline; internal sealed class EpubTocRenderer { diff --git a/Source/Bookgen.Lib/Pipeline/Extensions.cs b/Source/BookGen.Lib/Pipeline/Extensions.cs similarity index 91% rename from Source/Bookgen.Lib/Pipeline/Extensions.cs rename to Source/BookGen.Lib/Pipeline/Extensions.cs index a4db524a..a317f54d 100644 --- a/Source/Bookgen.Lib/Pipeline/Extensions.cs +++ b/Source/BookGen.Lib/Pipeline/Extensions.cs @@ -1,13 +1,13 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text; -using Bookgen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Domain.IO.Configuration; -namespace Bookgen.Lib.Pipeline; +namespace BookGen.Lib.Pipeline; internal static class Extensions { diff --git a/Source/Bookgen.Lib/Pipeline/Feed/CreateFeed.cs b/Source/BookGen.Lib/Pipeline/Feed/CreateFeed.cs similarity index 94% rename from Source/Bookgen.Lib/Pipeline/Feed/CreateFeed.cs rename to Source/BookGen.Lib/Pipeline/Feed/CreateFeed.cs index f8737fde..8cb9fe28 100644 --- a/Source/Bookgen.Lib/Pipeline/Feed/CreateFeed.cs +++ b/Source/BookGen.Lib/Pipeline/Feed/CreateFeed.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.Feed; +namespace BookGen.Lib.Pipeline.Feed; internal sealed class CreateFeed : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/Feed/CreateItems.cs b/Source/BookGen.Lib/Pipeline/Feed/CreateItems.cs similarity index 91% rename from Source/Bookgen.Lib/Pipeline/Feed/CreateItems.cs rename to Source/BookGen.Lib/Pipeline/Feed/CreateItems.cs index 1f3dac98..7830c185 100644 --- a/Source/Bookgen.Lib/Pipeline/Feed/CreateItems.cs +++ b/Source/BookGen.Lib/Pipeline/Feed/CreateItems.cs @@ -5,19 +5,18 @@ using System.ServiceModel.Syndication; -using Bookgen.Lib.Domain; -using Bookgen.Lib.Domain.IO; -using Bookgen.Lib.Internals; -using Bookgen.Lib.Rendering.Images; -using Bookgen.Lib.Rendering.Markdown; -using Bookgen.Lib.Rendering.Markdown.RenderInterop; -using Bookgen.Lib.Rendering.Templates; -using Bookgen.Lib.Templates; +using BookGen.Lib.Domain; +using BookGen.Lib.Domain.IO; +using BookGen.Lib.Rendering; +using BookGen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Markdown; +using BookGen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Rendering.Templates; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.Feed; +namespace BookGen.Lib.Pipeline.Feed; internal sealed class CreateItems : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/Feed/SyndicationFeedState.cs b/Source/BookGen.Lib/Pipeline/Feed/SyndicationFeedState.cs similarity index 84% rename from Source/Bookgen.Lib/Pipeline/Feed/SyndicationFeedState.cs rename to Source/BookGen.Lib/Pipeline/Feed/SyndicationFeedState.cs index 9b1d3006..0876d4ea 100644 --- a/Source/Bookgen.Lib/Pipeline/Feed/SyndicationFeedState.cs +++ b/Source/BookGen.Lib/Pipeline/Feed/SyndicationFeedState.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.ServiceModel.Syndication; -namespace Bookgen.Lib.Pipeline.Feed; +namespace BookGen.Lib.Pipeline.Feed; internal sealed class SyndicationFeedState { diff --git a/Source/Bookgen.Lib/Pipeline/Feed/WriteFeeds.cs b/Source/BookGen.Lib/Pipeline/Feed/WriteFeeds.cs similarity index 96% rename from Source/Bookgen.Lib/Pipeline/Feed/WriteFeeds.cs rename to Source/BookGen.Lib/Pipeline/Feed/WriteFeeds.cs index 72f4b520..ea5e6acc 100644 --- a/Source/Bookgen.Lib/Pipeline/Feed/WriteFeeds.cs +++ b/Source/BookGen.Lib/Pipeline/Feed/WriteFeeds.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -10,7 +10,7 @@ using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.Feed; +namespace BookGen.Lib.Pipeline.Feed; internal sealed class WriteFeeds : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/IBookEnvironment.cs b/Source/BookGen.Lib/Pipeline/IBookEnvironment.cs similarity index 77% rename from Source/Bookgen.Lib/Pipeline/IBookEnvironment.cs rename to Source/BookGen.Lib/Pipeline/IBookEnvironment.cs index 280a4ea6..49f8ae47 100644 --- a/Source/Bookgen.Lib/Pipeline/IBookEnvironment.cs +++ b/Source/BookGen.Lib/Pipeline/IBookEnvironment.cs @@ -1,15 +1,15 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Domain.IO; -using Bookgen.Lib.Domain.IO.Configuration; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Domain.IO; +using BookGen.Lib.Domain.IO.Configuration; using BookGen.Vfs; -namespace Bookgen.Lib.Pipeline; +namespace BookGen.Lib.Pipeline; public interface IBookEnvironment : IAssetSource, IDisposable { diff --git a/Source/Bookgen.Lib/Pipeline/IPipeLineStep.cs b/Source/BookGen.Lib/Pipeline/IPipeLineStep.cs similarity index 85% rename from Source/Bookgen.Lib/Pipeline/IPipeLineStep.cs rename to Source/BookGen.Lib/Pipeline/IPipeLineStep.cs index 5ee4fd9f..14f2d7b1 100644 --- a/Source/Bookgen.Lib/Pipeline/IPipeLineStep.cs +++ b/Source/BookGen.Lib/Pipeline/IPipeLineStep.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline; +namespace BookGen.Lib.Pipeline; public interface IPipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/PipeLineStep.cs b/Source/BookGen.Lib/Pipeline/PipeLineStep.cs similarity index 88% rename from Source/Bookgen.Lib/Pipeline/PipeLineStep.cs rename to Source/BookGen.Lib/Pipeline/PipeLineStep.cs index 876a77f0..451b3d82 100644 --- a/Source/Bookgen.Lib/Pipeline/PipeLineStep.cs +++ b/Source/BookGen.Lib/Pipeline/PipeLineStep.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline; +namespace BookGen.Lib.Pipeline; public abstract class PipeLineStep : IPipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/Pipeline.cs b/Source/BookGen.Lib/Pipeline/Pipeline.cs similarity index 93% rename from Source/Bookgen.Lib/Pipeline/Pipeline.cs rename to Source/BookGen.Lib/Pipeline/Pipeline.cs index fcfad91b..d3dec375 100644 --- a/Source/Bookgen.Lib/Pipeline/Pipeline.cs +++ b/Source/BookGen.Lib/Pipeline/Pipeline.cs @@ -3,17 +3,17 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Pipeline.Epub; -using Bookgen.Lib.Pipeline.Feed; -using Bookgen.Lib.Pipeline.PostProcess; -using Bookgen.Lib.Pipeline.Print; -using Bookgen.Lib.Pipeline.StaticWebsite; -using Bookgen.Lib.Pipeline.Wordpress; +using BookGen.Lib.Pipeline.Epub; +using BookGen.Lib.Pipeline.Feed; +using BookGen.Lib.Pipeline.PostProcess; +using BookGen.Lib.Pipeline.Print; +using BookGen.Lib.Pipeline.StaticWebsite; +using BookGen.Lib.Pipeline.Wordpress; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline; +namespace BookGen.Lib.Pipeline; public sealed class Pipeline { diff --git a/Source/Bookgen.Lib/Pipeline/PostProcess/PostProcessState.cs b/Source/BookGen.Lib/Pipeline/PostProcess/PostProcessState.cs similarity index 73% rename from Source/Bookgen.Lib/Pipeline/PostProcess/PostProcessState.cs rename to Source/BookGen.Lib/Pipeline/PostProcess/PostProcessState.cs index 0370fca8..6e4c0bcc 100644 --- a/Source/Bookgen.Lib/Pipeline/PostProcess/PostProcessState.cs +++ b/Source/BookGen.Lib/Pipeline/PostProcess/PostProcessState.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.PostProcess; +using BookGen.Lib.Domain.PostProcess; -namespace Bookgen.Lib.Pipeline.PostProcess; +namespace BookGen.Lib.Pipeline.PostProcess; internal sealed class PostProcessState { diff --git a/Source/Bookgen.Lib/Pipeline/PostProcess/RenderPagesForPostProcess.cs b/Source/BookGen.Lib/Pipeline/PostProcess/RenderPagesForPostProcess.cs similarity index 88% rename from Source/Bookgen.Lib/Pipeline/PostProcess/RenderPagesForPostProcess.cs rename to Source/BookGen.Lib/Pipeline/PostProcess/RenderPagesForPostProcess.cs index eac663d4..6f767e34 100644 --- a/Source/Bookgen.Lib/Pipeline/PostProcess/RenderPagesForPostProcess.cs +++ b/Source/BookGen.Lib/Pipeline/PostProcess/RenderPagesForPostProcess.cs @@ -3,19 +3,19 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain; -using Bookgen.Lib.Domain.IO; -using Bookgen.Lib.Domain.IO.Configuration; -using Bookgen.Lib.Domain.PostProcess; -using Bookgen.Lib.Internals; -using Bookgen.Lib.Rendering.Images; -using Bookgen.Lib.Rendering.Markdown; -using Bookgen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Domain; +using BookGen.Lib.Domain.IO; +using BookGen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Domain.PostProcess; +using BookGen.Lib.Rendering; +using BookGen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Markdown; +using BookGen.Lib.Rendering.Markdown.RenderInterop; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.PostProcess; +namespace BookGen.Lib.Pipeline.PostProcess; internal sealed class RenderPagesForPostProcess : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/PostProcess/WriteFile.cs b/Source/BookGen.Lib/Pipeline/PostProcess/WriteFile.cs similarity index 92% rename from Source/Bookgen.Lib/Pipeline/PostProcess/WriteFile.cs rename to Source/BookGen.Lib/Pipeline/PostProcess/WriteFile.cs index b1bd2f90..f7122ce3 100644 --- a/Source/Bookgen.Lib/Pipeline/PostProcess/WriteFile.cs +++ b/Source/BookGen.Lib/Pipeline/PostProcess/WriteFile.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.PostProcess; +namespace BookGen.Lib.Pipeline.PostProcess; internal sealed class WriteFile : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/Print/PrintState.cs b/Source/BookGen.Lib/Pipeline/Print/PrintState.cs similarity index 83% rename from Source/Bookgen.Lib/Pipeline/Print/PrintState.cs rename to Source/BookGen.Lib/Pipeline/Print/PrintState.cs index 0cb4eb36..5bf96e77 100644 --- a/Source/Bookgen.Lib/Pipeline/Print/PrintState.cs +++ b/Source/BookGen.Lib/Pipeline/Print/PrintState.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text; -namespace Bookgen.Lib.Pipeline.Print; +namespace BookGen.Lib.Pipeline.Print; internal class PrintState { diff --git a/Source/Bookgen.Lib/Pipeline/Print/RenderPages.cs b/Source/BookGen.Lib/Pipeline/Print/RenderPages.cs similarity index 89% rename from Source/Bookgen.Lib/Pipeline/Print/RenderPages.cs rename to Source/BookGen.Lib/Pipeline/Print/RenderPages.cs index 123ad211..a18e13bb 100644 --- a/Source/Bookgen.Lib/Pipeline/Print/RenderPages.cs +++ b/Source/BookGen.Lib/Pipeline/Print/RenderPages.cs @@ -3,17 +3,17 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain; -using Bookgen.Lib.Domain.IO; -using Bookgen.Lib.Internals; -using Bookgen.Lib.Rendering.Images; -using Bookgen.Lib.Rendering.Markdown; -using Bookgen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Domain; +using BookGen.Lib.Domain.IO; +using BookGen.Lib.Rendering; +using BookGen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Markdown; +using BookGen.Lib.Rendering.Markdown.RenderInterop; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.Print; +namespace BookGen.Lib.Pipeline.Print; internal sealed class RenderPages : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/Print/WriteHtml.cs b/Source/BookGen.Lib/Pipeline/Print/WriteHtml.cs similarity index 76% rename from Source/Bookgen.Lib/Pipeline/Print/WriteHtml.cs rename to Source/BookGen.Lib/Pipeline/Print/WriteHtml.cs index 4a99aefb..a0d745bd 100644 --- a/Source/Bookgen.Lib/Pipeline/Print/WriteHtml.cs +++ b/Source/BookGen.Lib/Pipeline/Print/WriteHtml.cs @@ -1,14 +1,13 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Rendering.Templates; -using Bookgen.Lib.Templates; +using BookGen.Lib.Rendering.Templates; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.Print; +namespace BookGen.Lib.Pipeline.Print; internal sealed class WriteHtml : PipeLineStep { @@ -21,8 +20,8 @@ public override async Task ExecuteAsync(IBookEnvironment environment logger.LogInformation("Writing print html..."); string tempate = await environment.GetTemplate(frontMatterTemplate: null, - fallbackTemplate: BundledAssets.TemplatePrint, - defaultTemplateSelector: cfg => cfg.PrintConfig.DefaultTempate); + fallbackTemplate: BundledAssets.TemplatePrint, + defaultTemplateSelector: cfg => cfg.PrintConfig.DefaultTempate); var renderer = new TemplateEngine(logger, environment); diff --git a/Source/Bookgen.Lib/Pipeline/Print/WriteXHtml.cs b/Source/BookGen.Lib/Pipeline/Print/WriteXHtml.cs similarity index 95% rename from Source/Bookgen.Lib/Pipeline/Print/WriteXHtml.cs rename to Source/BookGen.Lib/Pipeline/Print/WriteXHtml.cs index f0a3e0aa..5856de29 100644 --- a/Source/Bookgen.Lib/Pipeline/Print/WriteXHtml.cs +++ b/Source/BookGen.Lib/Pipeline/Print/WriteXHtml.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -7,9 +7,8 @@ using AngleSharp.Html.Dom; using AngleSharp.Html.Parser; -using Bookgen.Lib.Rendering.Images; -using Bookgen.Lib.Rendering.Templates; -using Bookgen.Lib.Templates; +using BookGen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Templates; using BookGen.Vfs; @@ -17,7 +16,7 @@ using PreMailer.Net; -namespace Bookgen.Lib.Pipeline.Print; +namespace BookGen.Lib.Pipeline.Print; internal sealed class WriteXHtml : PipeLineStep { @@ -72,7 +71,7 @@ private static async Task ExtractImages(IHtmlDocument document, IWritableFileSys foreach (IElement imageElement in imageElements) { string? src = imageElement.GetAttribute("src"); - + if (string.IsNullOrEmpty(src)) continue; @@ -123,7 +122,7 @@ public override async Task ExecuteAsync(IBookEnvironment environment await ExtractImages(rendered, environment.Output); logger.LogInformation("Moving css into inline atttibutes..."); - + using var pm = new PreMailer.Net.PreMailer(rendered); InlineResult result = pm.MoveCssInline(removeStyleElements: false, preserveMediaQueries: true); diff --git a/Source/Bookgen.Lib/Pipeline/StaticWebsite/CopyAssets.cs b/Source/BookGen.Lib/Pipeline/StaticWebsite/CopyAssets.cs similarity index 92% rename from Source/Bookgen.Lib/Pipeline/StaticWebsite/CopyAssets.cs rename to Source/BookGen.Lib/Pipeline/StaticWebsite/CopyAssets.cs index c7273eee..ba526610 100644 --- a/Source/Bookgen.Lib/Pipeline/StaticWebsite/CopyAssets.cs +++ b/Source/BookGen.Lib/Pipeline/StaticWebsite/CopyAssets.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.StaticWebsite; +namespace BookGen.Lib.Pipeline.StaticWebsite; /// /// Copy assets specified in configuration to output directory diff --git a/Source/Bookgen.Lib/Pipeline/StaticWebsite/CreateEmptyIndexPagesForFolders.cs b/Source/BookGen.Lib/Pipeline/StaticWebsite/CreateEmptyIndexPagesForFolders.cs similarity index 93% rename from Source/Bookgen.Lib/Pipeline/StaticWebsite/CreateEmptyIndexPagesForFolders.cs rename to Source/BookGen.Lib/Pipeline/StaticWebsite/CreateEmptyIndexPagesForFolders.cs index 6dd62480..77f38731 100644 --- a/Source/Bookgen.Lib/Pipeline/StaticWebsite/CreateEmptyIndexPagesForFolders.cs +++ b/Source/BookGen.Lib/Pipeline/StaticWebsite/CreateEmptyIndexPagesForFolders.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.StaticWebsite; +namespace BookGen.Lib.Pipeline.StaticWebsite; internal sealed class CreateEmptyIndexPagesForFolders : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/StaticWebsite/ExtractTemplateAssets.cs b/Source/BookGen.Lib/Pipeline/StaticWebsite/ExtractTemplateAssets.cs similarity index 94% rename from Source/Bookgen.Lib/Pipeline/StaticWebsite/ExtractTemplateAssets.cs rename to Source/BookGen.Lib/Pipeline/StaticWebsite/ExtractTemplateAssets.cs index d7f78fd3..7fadf40b 100644 --- a/Source/Bookgen.Lib/Pipeline/StaticWebsite/ExtractTemplateAssets.cs +++ b/Source/BookGen.Lib/Pipeline/StaticWebsite/ExtractTemplateAssets.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.StaticWebsite; +namespace BookGen.Lib.Pipeline.StaticWebsite; /// /// Extract embedded assets if, no default template is given diff --git a/Source/Bookgen.Lib/Pipeline/StaticWebsite/GeneratePager.cs b/Source/BookGen.Lib/Pipeline/StaticWebsite/GeneratePager.cs similarity index 94% rename from Source/Bookgen.Lib/Pipeline/StaticWebsite/GeneratePager.cs rename to Source/BookGen.Lib/Pipeline/StaticWebsite/GeneratePager.cs index 49e4fe87..17cad6bf 100644 --- a/Source/Bookgen.Lib/Pipeline/StaticWebsite/GeneratePager.cs +++ b/Source/BookGen.Lib/Pipeline/StaticWebsite/GeneratePager.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.StaticWebsite; +namespace BookGen.Lib.Pipeline.StaticWebsite; internal sealed class GeneratePager : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/StaticWebsite/ReadInFiles.cs b/Source/BookGen.Lib/Pipeline/StaticWebsite/ReadInFiles.cs similarity index 89% rename from Source/Bookgen.Lib/Pipeline/StaticWebsite/ReadInFiles.cs rename to Source/BookGen.Lib/Pipeline/StaticWebsite/ReadInFiles.cs index ee1015d3..c754b5b1 100644 --- a/Source/Bookgen.Lib/Pipeline/StaticWebsite/ReadInFiles.cs +++ b/Source/BookGen.Lib/Pipeline/StaticWebsite/ReadInFiles.cs @@ -1,14 +1,14 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain; -using Bookgen.Lib.Internals; +using BookGen.Lib.Domain; +using BookGen.Lib.Rendering; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.StaticWebsite; +namespace BookGen.Lib.Pipeline.StaticWebsite; internal sealed class ReadInFiles : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/StaticWebsite/RenderIndexPage.cs b/Source/BookGen.Lib/Pipeline/StaticWebsite/RenderIndexPage.cs similarity index 92% rename from Source/Bookgen.Lib/Pipeline/StaticWebsite/RenderIndexPage.cs rename to Source/BookGen.Lib/Pipeline/StaticWebsite/RenderIndexPage.cs index 985b212b..0e051405 100644 --- a/Source/Bookgen.Lib/Pipeline/StaticWebsite/RenderIndexPage.cs +++ b/Source/BookGen.Lib/Pipeline/StaticWebsite/RenderIndexPage.cs @@ -3,18 +3,17 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain; -using Bookgen.Lib.Rendering.Images; -using Bookgen.Lib.Rendering.Markdown; -using Bookgen.Lib.Rendering.Markdown.RenderInterop; -using Bookgen.Lib.Templates; - +using BookGen.Lib.Domain; +using BookGen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Markdown; +using BookGen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Rendering.Templates; using BookGen.Vfs; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.StaticWebsite; +namespace BookGen.Lib.Pipeline.StaticWebsite; internal sealed class RenderIndexPage : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/StaticWebsite/RenderStabdaloneToc.cs b/Source/BookGen.Lib/Pipeline/StaticWebsite/RenderStabdaloneToc.cs similarity index 94% rename from Source/Bookgen.Lib/Pipeline/StaticWebsite/RenderStabdaloneToc.cs rename to Source/BookGen.Lib/Pipeline/StaticWebsite/RenderStabdaloneToc.cs index 04dc226d..9d82416c 100644 --- a/Source/Bookgen.Lib/Pipeline/StaticWebsite/RenderStabdaloneToc.cs +++ b/Source/BookGen.Lib/Pipeline/StaticWebsite/RenderStabdaloneToc.cs @@ -1,16 +1,15 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Templates; - +using BookGen.Lib.Rendering.Templates; using BookGen.Vfs; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.StaticWebsite; +namespace BookGen.Lib.Pipeline.StaticWebsite; internal class RenderStabdaloneToc : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/StaticWebsite/RenderStaticPages.cs b/Source/BookGen.Lib/Pipeline/StaticWebsite/RenderStaticPages.cs similarity index 93% rename from Source/Bookgen.Lib/Pipeline/StaticWebsite/RenderStaticPages.cs rename to Source/BookGen.Lib/Pipeline/StaticWebsite/RenderStaticPages.cs index 1a5506d4..f54ec3eb 100644 --- a/Source/Bookgen.Lib/Pipeline/StaticWebsite/RenderStaticPages.cs +++ b/Source/BookGen.Lib/Pipeline/StaticWebsite/RenderStaticPages.cs @@ -5,18 +5,18 @@ using System.Diagnostics; -using Bookgen.Lib.Domain; -using Bookgen.Lib.Rendering.Images; -using Bookgen.Lib.Rendering.Markdown; -using Bookgen.Lib.Rendering.Markdown.RenderInterop; -using Bookgen.Lib.Templates; +using BookGen.Lib.Domain; +using BookGen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Markdown; +using BookGen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Rendering.Templates; using BookGen.Vfs; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.StaticWebsite; +namespace BookGen.Lib.Pipeline.StaticWebsite; internal sealed class RenderStaticPages : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/StaticWebsite/RenderTableOfContents.cs b/Source/BookGen.Lib/Pipeline/StaticWebsite/RenderTableOfContents.cs similarity index 89% rename from Source/Bookgen.Lib/Pipeline/StaticWebsite/RenderTableOfContents.cs rename to Source/BookGen.Lib/Pipeline/StaticWebsite/RenderTableOfContents.cs index 213de692..04e8e109 100644 --- a/Source/Bookgen.Lib/Pipeline/StaticWebsite/RenderTableOfContents.cs +++ b/Source/BookGen.Lib/Pipeline/StaticWebsite/RenderTableOfContents.cs @@ -1,20 +1,20 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Diagnostics; using System.Text; -using Bookgen.Lib.Domain; -using Bookgen.Lib.Domain.IO; -using Bookgen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Domain; +using BookGen.Lib.Domain.IO; +using BookGen.Lib.Domain.IO.Configuration; using BookGen.Vfs; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.StaticWebsite; +namespace BookGen.Lib.Pipeline.StaticWebsite; internal class RenderTableOfContents : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/StaticWebsite/StaticViewData.cs b/Source/BookGen.Lib/Pipeline/StaticWebsite/StaticViewData.cs similarity index 73% rename from Source/Bookgen.Lib/Pipeline/StaticWebsite/StaticViewData.cs rename to Source/BookGen.Lib/Pipeline/StaticWebsite/StaticViewData.cs index ddeb6432..f5197c74 100644 --- a/Source/Bookgen.Lib/Pipeline/StaticWebsite/StaticViewData.cs +++ b/Source/BookGen.Lib/Pipeline/StaticWebsite/StaticViewData.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Rendering.Templates; +using BookGen.Lib.Rendering.Templates; -namespace Bookgen.Lib.Pipeline.StaticWebsite; +namespace BookGen.Lib.Pipeline.StaticWebsite; public sealed class StaticViewData : ViewData { diff --git a/Source/Bookgen.Lib/Pipeline/StaticWebsite/StaticWebState.cs b/Source/BookGen.Lib/Pipeline/StaticWebsite/StaticWebState.cs similarity index 82% rename from Source/Bookgen.Lib/Pipeline/StaticWebsite/StaticWebState.cs rename to Source/BookGen.Lib/Pipeline/StaticWebsite/StaticWebState.cs index f6946a14..2ed7ee50 100644 --- a/Source/Bookgen.Lib/Pipeline/StaticWebsite/StaticWebState.cs +++ b/Source/BookGen.Lib/Pipeline/StaticWebsite/StaticWebState.cs @@ -1,13 +1,13 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Collections.Concurrent; -using Bookgen.Lib.Domain; +using BookGen.Lib.Domain; -namespace Bookgen.Lib.Pipeline.StaticWebsite; +namespace BookGen.Lib.Pipeline.StaticWebsite; internal sealed class StaticWebState { diff --git a/Source/Bookgen.Lib/Pipeline/StepResult.cs b/Source/BookGen.Lib/Pipeline/StepResult.cs similarity index 81% rename from Source/Bookgen.Lib/Pipeline/StepResult.cs rename to Source/BookGen.Lib/Pipeline/StepResult.cs index c299bf01..02cbfa32 100644 --- a/Source/Bookgen.Lib/Pipeline/StepResult.cs +++ b/Source/BookGen.Lib/Pipeline/StepResult.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Pipeline; +namespace BookGen.Lib.Pipeline; public enum StepResult { diff --git a/Source/Bookgen.Lib/Pipeline/TocRenderer.cs b/Source/BookGen.Lib/Pipeline/TocRenderer.cs similarity index 88% rename from Source/Bookgen.Lib/Pipeline/TocRenderer.cs rename to Source/BookGen.Lib/Pipeline/TocRenderer.cs index dfc3aa80..1587832a 100644 --- a/Source/Bookgen.Lib/Pipeline/TocRenderer.cs +++ b/Source/BookGen.Lib/Pipeline/TocRenderer.cs @@ -1,16 +1,16 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Diagnostics; using System.Text; -using Bookgen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Domain.IO.Configuration; using BookGen.Vfs; -namespace Bookgen.Lib.Pipeline; +namespace BookGen.Lib.Pipeline; internal sealed class TocRenderer { @@ -93,6 +93,12 @@ public void BeginOuterItemContainer() case ItemContainer.Details: _buffer.AppendLine("
"); break; + case ItemContainer.Paragraph: + _buffer.AppendLine("

"); + break; + case ItemContainer.Span: + _buffer.AppendLine(""); + break; } } @@ -129,6 +135,12 @@ public void EndOuterItemContainer() case ItemContainer.Details: _buffer.AppendLine("

"); break; + case ItemContainer.Paragraph: + _buffer.AppendLine("

"); + break; + case ItemContainer.Span: + _buffer.AppendLine(""); + break; } } diff --git a/Source/Bookgen.Lib/Pipeline/Wordpress/CreateWpChannel.cs b/Source/BookGen.Lib/Pipeline/Wordpress/CreateWpChannel.cs similarity index 91% rename from Source/Bookgen.Lib/Pipeline/Wordpress/CreateWpChannel.cs rename to Source/BookGen.Lib/Pipeline/Wordpress/CreateWpChannel.cs index 4dffec91..910ae26f 100644 --- a/Source/Bookgen.Lib/Pipeline/Wordpress/CreateWpChannel.cs +++ b/Source/BookGen.Lib/Pipeline/Wordpress/CreateWpChannel.cs @@ -1,14 +1,14 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.Wordpress; -using Bookgen.Lib.Internals; +using BookGen.Lib.Domain.Wordpress; +using BookGen.Lib.Internals; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.Wordpress; +namespace BookGen.Lib.Pipeline.Wordpress; internal sealed class CreateWpChannel : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/Wordpress/CreateWpPages.cs b/Source/BookGen.Lib/Pipeline/Wordpress/CreateWpPages.cs similarity index 95% rename from Source/Bookgen.Lib/Pipeline/Wordpress/CreateWpPages.cs rename to Source/BookGen.Lib/Pipeline/Wordpress/CreateWpPages.cs index ff2e7e8b..1a79d214 100644 --- a/Source/Bookgen.Lib/Pipeline/Wordpress/CreateWpPages.cs +++ b/Source/BookGen.Lib/Pipeline/Wordpress/CreateWpPages.cs @@ -7,20 +7,20 @@ using System.Globalization; using System.Text; -using Bookgen.Lib.Domain; -using Bookgen.Lib.Domain.IO; -using Bookgen.Lib.Domain.Wordpress; -using Bookgen.Lib.Internals; -using Bookgen.Lib.Rendering.Images; -using Bookgen.Lib.Rendering.Markdown; -using Bookgen.Lib.Rendering.Markdown.RenderInterop; -using Bookgen.Lib.Rendering.Templates; -using Bookgen.Lib.Templates; +using BookGen.Lib.Domain; +using BookGen.Lib.Domain.IO; +using BookGen.Lib.Domain.Wordpress; +using BookGen.Lib.Internals; +using BookGen.Lib.Rendering; +using BookGen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Markdown; +using BookGen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Rendering.Templates; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.Wordpress; +namespace BookGen.Lib.Pipeline.Wordpress; internal sealed class CreateWpPages : PipeLineStep { diff --git a/Source/Bookgen.Lib/Pipeline/Wordpress/WpState.cs b/Source/BookGen.Lib/Pipeline/Wordpress/WpState.cs similarity index 73% rename from Source/Bookgen.Lib/Pipeline/Wordpress/WpState.cs rename to Source/BookGen.Lib/Pipeline/Wordpress/WpState.cs index fd0e36e7..a174aa2d 100644 --- a/Source/Bookgen.Lib/Pipeline/Wordpress/WpState.cs +++ b/Source/BookGen.Lib/Pipeline/Wordpress/WpState.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.Wordpress; +using BookGen.Lib.Domain.Wordpress; -namespace Bookgen.Lib.Pipeline.Wordpress; +namespace BookGen.Lib.Pipeline.Wordpress; internal sealed class WpState { diff --git a/Source/Bookgen.Lib/Pipeline/Wordpress/WriteExportFile.cs b/Source/BookGen.Lib/Pipeline/Wordpress/WriteExportFile.cs similarity index 92% rename from Source/Bookgen.Lib/Pipeline/Wordpress/WriteExportFile.cs rename to Source/BookGen.Lib/Pipeline/Wordpress/WriteExportFile.cs index 6671be78..7bffc441 100644 --- a/Source/Bookgen.Lib/Pipeline/Wordpress/WriteExportFile.cs +++ b/Source/BookGen.Lib/Pipeline/Wordpress/WriteExportFile.cs @@ -1,15 +1,15 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Xml.Serialization; -using Bookgen.Lib.Domain.Wordpress; +using BookGen.Lib.Domain.Wordpress; using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Pipeline.Wordpress; +namespace BookGen.Lib.Pipeline.Wordpress; internal sealed class WriteExportFile : PipeLineStep { diff --git a/Source/Bookgen.Lib/Rendering/Images/CachedImageService.cs b/Source/BookGen.Lib/Rendering/Images/CachedImageService.cs similarity index 96% rename from Source/Bookgen.Lib/Rendering/Images/CachedImageService.cs rename to Source/BookGen.Lib/Rendering/Images/CachedImageService.cs index bbc9dcc1..104e6d4c 100644 --- a/Source/Bookgen.Lib/Rendering/Images/CachedImageService.cs +++ b/Source/BookGen.Lib/Rendering/Images/CachedImageService.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.Caching.Memory; -namespace Bookgen.Lib.Rendering.Images; +namespace BookGen.Lib.Rendering.Images; public sealed class CachedImageService : IImgService { diff --git a/Source/Bookgen.Lib/Rendering/Images/IImgService.cs b/Source/BookGen.Lib/Rendering/Images/IImgService.cs similarity index 89% rename from Source/Bookgen.Lib/Rendering/Images/IImgService.cs rename to Source/BookGen.Lib/Rendering/Images/IImgService.cs index 67f01151..9f8acbe0 100644 --- a/Source/Bookgen.Lib/Rendering/Images/IImgService.cs +++ b/Source/BookGen.Lib/Rendering/Images/IImgService.cs @@ -3,7 +3,7 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Rendering.Images; +namespace BookGen.Lib.Rendering.Images; public interface IImgService { diff --git a/Source/Bookgen.Lib/Rendering/Images/ImageConverter.cs b/Source/BookGen.Lib/Rendering/Images/ImageConverter.cs similarity index 95% rename from Source/Bookgen.Lib/Rendering/Images/ImageConverter.cs rename to Source/BookGen.Lib/Rendering/Images/ImageConverter.cs index 3ef5bf4f..2a7b3e4b 100644 --- a/Source/Bookgen.Lib/Rendering/Images/ImageConverter.cs +++ b/Source/BookGen.Lib/Rendering/Images/ImageConverter.cs @@ -3,11 +3,11 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Domain.IO.Configuration; using SkiaSharp; -namespace Bookgen.Lib.Rendering.Images; +namespace BookGen.Lib.Rendering.Images; public static class ImageConverter { diff --git a/Source/Bookgen.Lib/Rendering/Images/ImageResult.cs b/Source/BookGen.Lib/Rendering/Images/ImageResult.cs similarity index 97% rename from Source/Bookgen.Lib/Rendering/Images/ImageResult.cs rename to Source/BookGen.Lib/Rendering/Images/ImageResult.cs index ef0923ec..24f78edd 100644 --- a/Source/Bookgen.Lib/Rendering/Images/ImageResult.cs +++ b/Source/BookGen.Lib/Rendering/Images/ImageResult.cs @@ -3,10 +3,9 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- - using System.Diagnostics.CodeAnalysis; -namespace Bookgen.Lib.Rendering.Images; +namespace BookGen.Lib.Rendering.Images; public sealed record class ImageResult { diff --git a/Source/Bookgen.Lib/Rendering/Images/ImageType.cs b/Source/BookGen.Lib/Rendering/Images/ImageType.cs similarity index 80% rename from Source/Bookgen.Lib/Rendering/Images/ImageType.cs rename to Source/BookGen.Lib/Rendering/Images/ImageType.cs index 835cc95b..ba301ce7 100644 --- a/Source/Bookgen.Lib/Rendering/Images/ImageType.cs +++ b/Source/BookGen.Lib/Rendering/Images/ImageType.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Rendering.Images; +namespace BookGen.Lib.Rendering.Images; public enum ImageType { diff --git a/Source/Bookgen.Lib/Rendering/Images/ImageTypeExtensions.cs b/Source/BookGen.Lib/Rendering/Images/ImageTypeExtensions.cs similarity index 91% rename from Source/Bookgen.Lib/Rendering/Images/ImageTypeExtensions.cs rename to Source/BookGen.Lib/Rendering/Images/ImageTypeExtensions.cs index db13b6ae..f578a2c2 100644 --- a/Source/Bookgen.Lib/Rendering/Images/ImageTypeExtensions.cs +++ b/Source/BookGen.Lib/Rendering/Images/ImageTypeExtensions.cs @@ -1,12 +1,12 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Diagnostics; using System.Net.Mime; -namespace Bookgen.Lib.Rendering.Images; +namespace BookGen.Lib.Rendering.Images; public static class ImageTypeExtensions { diff --git a/Source/Bookgen.Lib/Rendering/Images/ImageUtils.cs b/Source/BookGen.Lib/Rendering/Images/ImageUtils.cs similarity index 98% rename from Source/Bookgen.Lib/Rendering/Images/ImageUtils.cs rename to Source/BookGen.Lib/Rendering/Images/ImageUtils.cs index 2eed17a0..b524a904 100644 --- a/Source/Bookgen.Lib/Rendering/Images/ImageUtils.cs +++ b/Source/BookGen.Lib/Rendering/Images/ImageUtils.cs @@ -5,13 +5,13 @@ using System.Diagnostics; -using Bookgen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Domain.IO.Configuration; using SkiaSharp; using Svg.Skia; -namespace Bookgen.Lib.Rendering.Images; +namespace BookGen.Lib.Rendering.Images; internal static class ImageUtils { diff --git a/Source/Bookgen.Lib/Rendering/Images/ImgService.cs b/Source/BookGen.Lib/Rendering/Images/ImgService.cs similarity index 98% rename from Source/Bookgen.Lib/Rendering/Images/ImgService.cs rename to Source/BookGen.Lib/Rendering/Images/ImgService.cs index 26760458..6b09f008 100644 --- a/Source/Bookgen.Lib/Rendering/Images/ImgService.cs +++ b/Source/BookGen.Lib/Rendering/Images/ImgService.cs @@ -5,7 +5,7 @@ using System.Diagnostics; -using Bookgen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Domain.IO.Configuration; using BookGen.Vfs; @@ -13,7 +13,7 @@ using SkiaSharp; -namespace Bookgen.Lib.Rendering.Images; +namespace BookGen.Lib.Rendering.Images; public sealed class ImgService : IImgService { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/BookGenExtension.cs b/Source/BookGen.Lib/Rendering/Markdown/BookGenExtension.cs similarity index 98% rename from Source/Bookgen.Lib/Rendering/Markdown/BookGenExtension.cs rename to Source/BookGen.Lib/Rendering/Markdown/BookGenExtension.cs index e188dd8e..79b03592 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/BookGenExtension.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/BookGenExtension.cs @@ -5,8 +5,8 @@ using System.Text.RegularExpressions; -using Bookgen.Lib.Rendering.Markdown; -using Bookgen.Lib.Rendering.Markdown.Renderers; +using BookGen.Lib.Rendering.Markdown; +using BookGen.Lib.Rendering.Markdown.Renderers; using Markdig; using Markdig.Extensions.Figures; @@ -18,7 +18,7 @@ using Markdig.Syntax; using Markdig.Syntax.Inlines; -namespace Bookgen.Lib.Markdown; +namespace BookGen.Lib.Markdown; internal sealed partial class BookGenExtension : IMarkdownExtension, IDisposable { @@ -70,19 +70,19 @@ public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) htmlRenderer.ObjectRenderers.AddIfNotAlready(new SvgMathBlockRenderer(_settings.RenderInterop)); } - HtmlMathInlineRenderer? mathInlineRenderer = htmlRenderer.ObjectRenderers.FindExact(); - if (mathInlineRenderer != null) - { - htmlRenderer.ObjectRenderers.Remove(mathInlineRenderer); - htmlRenderer.ObjectRenderers.AddIfNotAlready(new SvgMathInlineRenderer(_settings.RenderInterop)); - } - CodeBlockRenderer? codeBlockRenderer = htmlRenderer.ObjectRenderers.FindExact(); if (codeBlockRenderer != null) { htmlRenderer.ObjectRenderers.Remove(codeBlockRenderer); htmlRenderer.ObjectRenderers.AddIfNotAlready(new SyntaxRenderer(codeBlockRenderer, _settings.RenderInterop)); } + + HtmlMathInlineRenderer? mathInlineRenderer = htmlRenderer.ObjectRenderers.FindExact(); + if (mathInlineRenderer != null) + { + htmlRenderer.ObjectRenderers.Remove(mathInlineRenderer); + htmlRenderer.ObjectRenderers.AddIfNotAlready(new SvgMathInlineRenderer(_settings.RenderInterop)); + } } } diff --git a/Source/Bookgen.Lib/Rendering/Markdown/FootNoteReindexer.cs b/Source/BookGen.Lib/Rendering/Markdown/FootNoteReindexer.cs similarity index 98% rename from Source/Bookgen.Lib/Rendering/Markdown/FootNoteReindexer.cs rename to Source/BookGen.Lib/Rendering/Markdown/FootNoteReindexer.cs index 45e4dfe8..be484e63 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/FootNoteReindexer.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/FootNoteReindexer.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -8,7 +8,7 @@ using Microsoft.Extensions.Logging; -namespace Bookgen.Lib.Markdown; +namespace BookGen.Lib.Markdown; public sealed partial class FootNoteReindexer { diff --git a/Source/BookGen.Lib/Rendering/Markdown/Keyboard/KeyboardExtension.cs b/Source/BookGen.Lib/Rendering/Markdown/Keyboard/KeyboardExtension.cs new file mode 100644 index 00000000..be369889 --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Markdown/Keyboard/KeyboardExtension.cs @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Markdig; +using Markdig.Helpers; +using Markdig.Parsers; +using Markdig.Renderers; + +namespace BookGen.Lib.Rendering.Markdown.Keyboard; + +internal sealed class KeyboardExtension : IMarkdownExtension +{ + public void Setup(MarkdownPipelineBuilder pipeline) + { + OrderedList parsers = pipeline.InlineParsers; + if (!parsers.Contains()) + { + parsers.Add(new KeyboardInlineParser()); + } + } + + public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) + { + if (renderer is HtmlRenderer htmlRenderer) + { + ObjectRendererCollection renderers = htmlRenderer.ObjectRenderers; + if (!renderers.Contains()) + { + renderers.Add(new KeyboardHtmlRenderer()); + } + } + } +} diff --git a/Source/BookGen.Lib/Rendering/Markdown/Keyboard/KeyboardHtmlRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Keyboard/KeyboardHtmlRenderer.cs new file mode 100644 index 00000000..506571af --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Markdown/Keyboard/KeyboardHtmlRenderer.cs @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Markdig.Renderers; +using Markdig.Renderers.Html; + +namespace BookGen.Lib.Rendering.Markdown.Keyboard; + +internal sealed class KeyboardHtmlRenderer : HtmlObjectRenderer +{ + protected override void Write(HtmlRenderer renderer, KeyboardInline obj) + { + if (renderer.EnableHtmlForInline) + { + renderer.Write(""); + renderer.Write(obj.Text); + renderer.Write(""); + } + else + { + renderer.Write(obj.Text); + } + } +} diff --git a/Source/BookGen.Lib/Rendering/Markdown/Keyboard/KeyboardInline.cs b/Source/BookGen.Lib/Rendering/Markdown/Keyboard/KeyboardInline.cs new file mode 100644 index 00000000..cc373144 --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Markdown/Keyboard/KeyboardInline.cs @@ -0,0 +1,14 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Markdig.Helpers; +using Markdig.Syntax.Inlines; + +namespace BookGen.Lib.Rendering.Markdown.Keyboard; + +internal sealed class KeyboardInline : LeafInline +{ + public StringSlice Text { get; set; } +} diff --git a/Source/BookGen.Lib/Rendering/Markdown/Keyboard/KeyboardInlineParser.cs b/Source/BookGen.Lib/Rendering/Markdown/Keyboard/KeyboardInlineParser.cs new file mode 100644 index 00000000..5f1ee4f7 --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Markdown/Keyboard/KeyboardInlineParser.cs @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Markdig.Helpers; +using Markdig.Parsers; + +namespace BookGen.Lib.Rendering.Markdown.Keyboard; + +internal sealed class KeyboardInlineParser : InlineParser +{ + public KeyboardInlineParser() + { + OpeningCharacters = ['<']; + } + + public override bool Match(InlineProcessor processor, ref StringSlice slice) + { + bool matchFound; + char next; + + matchFound = false; + + next = slice.PeekCharExtra(1); + + if (next == '<') + { + char current; + int start; + int end; + + slice.NextChar(); + slice.NextChar(); // skip the opening pair + + current = slice.CurrentChar; + start = slice.Start; + end = start; + + while (current != '\0' && current != '>') + { + end++; + current = slice.NextChar(); + } + + if (end > start && current == '>' && slice.PeekCharExtra(1) == '>') + { + int inlineStart; + + end--; + inlineStart = processor.GetSourcePosition(slice.Start, out int line, out int column); + + processor.Inline = new KeyboardInline + { + Span = + { + Start = inlineStart, + End = inlineStart + (end - start) + }, + Line = line, + Column = column, + Text = new StringSlice(slice.Text, start, end) + }; + + slice.NextChar(); + slice.NextChar(); // skip the closing characters + + matchFound = true; + } + } + + return matchFound; + } +} diff --git a/Source/Bookgen.Lib/Rendering/Markdown/MarkdownConverter.cs b/Source/BookGen.Lib/Rendering/Markdown/MarkdownConverter.cs similarity index 81% rename from Source/Bookgen.Lib/Rendering/Markdown/MarkdownConverter.cs rename to Source/BookGen.Lib/Rendering/Markdown/MarkdownConverter.cs index 6d8da44d..6316c743 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/MarkdownConverter.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/MarkdownConverter.cs @@ -1,28 +1,30 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Markdown; -using Bookgen.Lib.Rendering.Markdown.Renderers.Terminal; -using Bookgen.Lib.Rendering.Markdown.TableOfContents; +using BookGen.Lib.Rendering.Markdown; + +using BookGen.Lib.Markdown; +using BookGen.Lib.Rendering.Markdown.Renderers.Terminal; using Markdig; using Markdig.Parsers; using Markdig.Syntax; -namespace Bookgen.Lib.Rendering.Markdown; +namespace BookGen.Lib.Rendering.Markdown; public sealed class MarkdownConverter : IDisposable { private readonly MarkdownPipeline _htmlPipeLine; - private readonly MarkdownPipeline _terminalPipeLine; public MarkdownConverter(MarkdownRenderSettings settings) { MarkdownPipelineBuilder configuration = new MarkdownPipelineBuilder() .UseAdvancedExtensions() .UseTableOfContents() .UseMathematics() + .UseAlertBlocks() + .UseKeyboard() .UseYamlFrontMatter() .Use(); @@ -35,11 +37,6 @@ public MarkdownConverter(MarkdownRenderSettings settings) } _htmlPipeLine = configuration.Build(); - - _terminalPipeLine = new MarkdownPipelineBuilder() - .UseYamlFrontMatter() - .UseAutoLinks() - .Build(); } public void Dispose() @@ -59,9 +56,14 @@ public string RenderMarkdownToHtml(string markdown) public string RenderToPlainText(string markdown) => Markdig.Markdown.ToPlainText(markdown, _htmlPipeLine); - public string RenderMarkdownToTerminal(string markdown, RenderOptions? renderOptions = null) + public static string RenderMarkdownToTerminal(string markdown, RenderOptions? renderOptions = null) { - MarkdownDocument document = MarkdownParser.Parse(markdown, _terminalPipeLine); + MarkdownPipeline pipeline = new MarkdownPipelineBuilder() + .UseAutoLinks() + .UseYamlFrontMatter() + .Build(); + + MarkdownDocument document = MarkdownParser.Parse(markdown, pipeline); using var writer = new StringWriter(); diff --git a/Source/Bookgen.Lib/Rendering/Markdown/MarkdownRenderSettings.cs b/Source/BookGen.Lib/Rendering/Markdown/MarkdownRenderSettings.cs similarity index 88% rename from Source/Bookgen.Lib/Rendering/Markdown/MarkdownRenderSettings.cs rename to Source/BookGen.Lib/Rendering/Markdown/MarkdownRenderSettings.cs index 38c5fe76..bcb4debe 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/MarkdownRenderSettings.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/MarkdownRenderSettings.cs @@ -3,11 +3,11 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.IO.Configuration; -using Bookgen.Lib.Rendering.Images; -using Bookgen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Markdown.RenderInterop; -namespace Bookgen.Lib.Rendering.Markdown; +namespace BookGen.Lib.Rendering.Markdown; public sealed class MarkdownRenderSettings : IDisposable { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/TocExtensions.cs b/Source/BookGen.Lib/Rendering/Markdown/PipelineBuilderExtensions.cs similarity index 63% rename from Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/TocExtensions.cs rename to Source/BookGen.Lib/Rendering/Markdown/PipelineBuilderExtensions.cs index 09ecb7a4..c126c3a5 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/TocExtensions.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/PipelineBuilderExtensions.cs @@ -1,15 +1,19 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using BookGen.Lib.Rendering.Markdown.Keyboard; +using BookGen.Lib.Rendering.Markdown.TableOfContents; + using Markdig; using Markdig.Extensions.AutoIdentifiers; using Markdig.Extensions.GenericAttributes; +using Markdig.Helpers; -namespace Bookgen.Lib.Rendering.Markdown.TableOfContents; +namespace BookGen.Lib.Rendering.Markdown; -internal static class TocExtensions +internal static class PipelineBuilderExtensions { public static MarkdownPipelineBuilder UseTableOfContents(this MarkdownPipelineBuilder pipelineBuilder) { @@ -27,4 +31,16 @@ public static MarkdownPipelineBuilder UseTableOfContents(this MarkdownPipelineBu return pipelineBuilder; } + + public static MarkdownPipelineBuilder UseKeyboard(this MarkdownPipelineBuilder pipeline) + { + OrderedList extensions = pipeline.Extensions; + + if (!extensions.Contains()) + { + extensions.Add(new KeyboardExtension()); + } + + return pipeline; + } } diff --git a/Source/Bookgen.Lib/Rendering/Markdown/RenderInterop/IRenderInterop.cs b/Source/BookGen.Lib/Rendering/Markdown/RenderInterop/IRenderInterop.cs similarity index 85% rename from Source/Bookgen.Lib/Rendering/Markdown/RenderInterop/IRenderInterop.cs rename to Source/BookGen.Lib/Rendering/Markdown/RenderInterop/IRenderInterop.cs index d58c04c4..e82d482f 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/RenderInterop/IRenderInterop.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/RenderInterop/IRenderInterop.cs @@ -3,13 +3,13 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Domain.IO.Configuration; -using Bookgen.Lib.Rendering.Images; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Rendering.Images; using BookGen.Vfs; -namespace Bookgen.Lib.Rendering.Markdown.RenderInterop; +namespace BookGen.Lib.Rendering.Markdown.RenderInterop; public interface IRenderInterop : IDisposable { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/RenderInterop/JavascriptEngine.cs b/Source/BookGen.Lib/Rendering/Markdown/RenderInterop/JavascriptEngine.cs similarity index 96% rename from Source/Bookgen.Lib/Rendering/Markdown/RenderInterop/JavascriptEngine.cs rename to Source/BookGen.Lib/Rendering/Markdown/RenderInterop/JavascriptEngine.cs index 7aa1f390..675fff9f 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/RenderInterop/JavascriptEngine.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/RenderInterop/JavascriptEngine.cs @@ -9,7 +9,7 @@ using Microsoft.ClearScript.JavaScript; using Microsoft.ClearScript.V8; -namespace Bookgen.Lib.Rendering.Markdown.RenderInterop; +namespace BookGen.Lib.Rendering.Markdown.RenderInterop; internal sealed class JavascriptEngine : IDisposable { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/RenderInterop/JavascriptModuleLoader.cs b/Source/BookGen.Lib/Rendering/Markdown/RenderInterop/JavascriptModuleLoader.cs similarity index 97% rename from Source/Bookgen.Lib/Rendering/Markdown/RenderInterop/JavascriptModuleLoader.cs rename to Source/BookGen.Lib/Rendering/Markdown/RenderInterop/JavascriptModuleLoader.cs index 1af143ec..78c515b3 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/RenderInterop/JavascriptModuleLoader.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/RenderInterop/JavascriptModuleLoader.cs @@ -9,7 +9,7 @@ using Microsoft.ClearScript; -namespace Bookgen.Lib.Rendering.Markdown.RenderInterop; +namespace BookGen.Lib.Rendering.Markdown.RenderInterop; internal sealed class JavascriptModuleLoader : DocumentLoader { @@ -73,11 +73,6 @@ public override async Task LoadDocumentAsync(DocumentSettings settings } } - if (exceptions.Count < 1) - { - throw new FileNotFoundException(null, specifier); - } - if (exceptions.Count == 1) { throw new FileLoadException(exceptions[0].Message, specifier, exceptions[0]); @@ -96,7 +91,7 @@ public override async Task LoadDocumentAsync(DocumentSettings settings } return !sourceUri.IsAbsoluteUri - ? null + ? null : sourceUri; } @@ -216,7 +211,7 @@ private async Task LoadDocumentAsync(Uri uri, DocumentCategory category, DocumentContextCallback contextCallback) { - if (_moduleAssets == null) + if (_moduleAssets == null) { throw new InvalidOperationException("Module assets source is not provided."); } diff --git a/Source/Bookgen.Lib/Rendering/Markdown/RenderInterop/RenderInterop.cs b/Source/BookGen.Lib/Rendering/Markdown/RenderInterop/RenderInterop.cs similarity index 98% rename from Source/Bookgen.Lib/Rendering/Markdown/RenderInterop/RenderInterop.cs rename to Source/BookGen.Lib/Rendering/Markdown/RenderInterop/RenderInterop.cs index 7d57a2be..d95654cd 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/RenderInterop/RenderInterop.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/RenderInterop/RenderInterop.cs @@ -5,17 +5,18 @@ using System.Diagnostics; using System.Globalization; +using System.Text; using System.Web; -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Domain.IO.Configuration; -using Bookgen.Lib.Rendering.Images; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Rendering.Images; using BookGen.Vfs; using SkiaSharp; -namespace Bookgen.Lib.Rendering.Markdown.RenderInterop; +namespace BookGen.Lib.Rendering.Markdown.RenderInterop; public sealed class RenderInterop : IRenderInterop { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Embedders/BaseLinkEmbedder.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Embedders/BaseLinkEmbedder.cs similarity index 96% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/Embedders/BaseLinkEmbedder.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/Embedders/BaseLinkEmbedder.cs index 2b433130..57b208b3 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Embedders/BaseLinkEmbedder.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Embedders/BaseLinkEmbedder.cs @@ -8,7 +8,7 @@ using BookGen.Vfs; -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Embedders; +namespace BookGen.Lib.Rendering.Markdown.Renderers.Embedders; internal abstract class BaseLinkEmbedder { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Embedders/MixCloudLinkEmbedder.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Embedders/MixCloudLinkEmbedder.cs similarity index 94% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/Embedders/MixCloudLinkEmbedder.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/Embedders/MixCloudLinkEmbedder.cs index 5a075a2d..03be8f60 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Embedders/MixCloudLinkEmbedder.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Embedders/MixCloudLinkEmbedder.cs @@ -6,7 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.Web; -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Embedders; +namespace BookGen.Lib.Rendering.Markdown.Renderers.Embedders; internal sealed class MixCloudLinkEmbedder : BaseLinkEmbedder { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Embedders/YoutubeLinkEmbedder.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Embedders/YoutubeLinkEmbedder.cs similarity index 95% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/Embedders/YoutubeLinkEmbedder.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/Embedders/YoutubeLinkEmbedder.cs index f60609e7..34457c6c 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Embedders/YoutubeLinkEmbedder.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Embedders/YoutubeLinkEmbedder.cs @@ -6,7 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.Net.Mime; -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Embedders; +namespace BookGen.Lib.Rendering.Markdown.Renderers.Embedders; internal sealed class YoutubeLinkEmbedder : BaseLinkEmbedder { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/ExtendedLinkInlineRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/ExtendedLinkInlineRenderer.cs similarity index 81% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/ExtendedLinkInlineRenderer.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/ExtendedLinkInlineRenderer.cs index c3a2bab7..8c9e7922 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/ExtendedLinkInlineRenderer.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/ExtendedLinkInlineRenderer.cs @@ -3,13 +3,13 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Rendering.Markdown.Renderers.Embedders; +using BookGen.Lib.Rendering.Markdown.Renderers.Embedders; using Markdig.Renderers; using Markdig.Renderers.Html.Inlines; using Markdig.Syntax.Inlines; -namespace Bookgen.Lib.Rendering.Markdown.Renderers; +namespace BookGen.Lib.Rendering.Markdown.Renderers; internal sealed class ExtendedLinkInlineRenderer : LinkInlineRenderer { @@ -34,10 +34,11 @@ protected override void Write(HtmlRenderer renderer, LinkInline obj) return; } - if (_autoEmbedSupportedLinks && !string.IsNullOrEmpty(obj.Url)) + if (_autoEmbedSupportedLinks + && !string.IsNullOrEmpty(obj.Url) + && Uri.TryCreate(obj.Url, UriKind.Absolute, out Uri? uri)) { - Uri uri = new Uri(obj.Url); - foreach (var embedder in _embedders) + foreach (BaseLinkEmbedder embedder in _embedders) { if (embedder.TryRender(uri, out string? rendered)) { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/MarkdownSyntaxExtensions.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/MarkdownSyntaxExtensions.cs similarity index 95% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/MarkdownSyntaxExtensions.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/MarkdownSyntaxExtensions.cs index 192d2944..5999a771 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/MarkdownSyntaxExtensions.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/MarkdownSyntaxExtensions.cs @@ -8,7 +8,7 @@ using Markdig.Helpers; using Markdig.Syntax; -namespace Bookgen.Lib.Rendering.Markdown.Renderers; +namespace BookGen.Lib.Rendering.Markdown.Renderers; internal static class MarkdownSyntaxExtensions { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SvgMathBlockRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SvgMathBlockRenderer.cs similarity index 85% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/SvgMathBlockRenderer.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/SvgMathBlockRenderer.cs index ac5e90fe..21777471 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SvgMathBlockRenderer.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SvgMathBlockRenderer.cs @@ -3,14 +3,14 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; -using Bookgen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; +using BookGen.Lib.Rendering.Markdown.RenderInterop; using Markdig.Extensions.Mathematics; using Markdig.Renderers; using Markdig.Renderers.Html; -namespace Bookgen.Lib.Rendering.Markdown.Renderers; +namespace BookGen.Lib.Rendering.Markdown.Renderers; internal class SvgMathBlockRenderer : HtmlObjectRenderer { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SvgMathInlineRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SvgMathInlineRenderer.cs similarity index 67% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/SvgMathInlineRenderer.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/SvgMathInlineRenderer.cs index 22dc7754..45986321 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SvgMathInlineRenderer.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SvgMathInlineRenderer.cs @@ -3,14 +3,14 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; -using Bookgen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; +using BookGen.Lib.Rendering.Markdown.RenderInterop; using Markdig.Extensions.Mathematics; using Markdig.Renderers; using Markdig.Renderers.Html; -namespace Bookgen.Lib.Rendering.Markdown.Renderers; +namespace BookGen.Lib.Rendering.Markdown.Renderers; internal sealed class SvgMathInlineRenderer : HtmlObjectRenderer { @@ -23,6 +23,13 @@ public SvgMathInlineRenderer(IRenderInterop renderInterop) protected override void Write(HtmlRenderer renderer, MathInline obj) { + if (!obj.Content.Text.StartsWith('$')) + { + // If the content does not start with a dollar sign, we assume it's not a valid math expression and render it as plain text. + renderer.Write(obj.Content.Text); + return; + } + renderer.Write(""); renderer.Write(_latexRenderPlugin.Render(obj.Content.Text)); renderer.Write(""); diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/LatexRenderPlugin.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/LatexRenderPlugin.cs similarity index 82% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/LatexRenderPlugin.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/LatexRenderPlugin.cs index 3e041129..38f680ac 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/LatexRenderPlugin.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/LatexRenderPlugin.cs @@ -3,9 +3,9 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Rendering.Markdown.RenderInterop; -namespace Bookgen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; +namespace BookGen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; internal sealed class LatexRenderPlugin(IRenderInterop renderInterop) : SyntaxRendererPlugin { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/MermaidRenderPlugin.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/MermaidRenderPlugin.cs similarity index 83% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/MermaidRenderPlugin.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/MermaidRenderPlugin.cs index ff7a7ef9..680fd04a 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/MermaidRenderPlugin.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/MermaidRenderPlugin.cs @@ -3,9 +3,9 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Rendering.Markdown.RenderInterop; -namespace Bookgen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; +namespace BookGen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; internal sealed class MermaidRenderPlugin(IRenderInterop renderInterop) : SyntaxRendererPlugin { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/NomnomlRenderPlugin.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/NomnomlRenderPlugin.cs similarity index 83% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/NomnomlRenderPlugin.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/NomnomlRenderPlugin.cs index 631a4924..6f524cdf 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/NomnomlRenderPlugin.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/NomnomlRenderPlugin.cs @@ -3,9 +3,9 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Rendering.Markdown.RenderInterop; -namespace Bookgen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; +namespace BookGen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; internal sealed class NomnomlRenderPlugin(IRenderInterop renderInterop) : SyntaxRendererPlugin { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/PlantUmlRenderPlugin.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/PlantUmlRenderPlugin.cs similarity index 82% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/PlantUmlRenderPlugin.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/PlantUmlRenderPlugin.cs index 8ad9988c..7291fc33 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/PlantUmlRenderPlugin.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/PlantUmlRenderPlugin.cs @@ -3,9 +3,9 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Rendering.Markdown.RenderInterop; -namespace Bookgen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; +namespace BookGen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; internal sealed class PlantUmlRenderPlugin(IRenderInterop renderInterop) : SyntaxRendererPlugin { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/QrCodeRenderPlugin.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/QrCodeRenderPlugin.cs similarity index 82% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/QrCodeRenderPlugin.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/QrCodeRenderPlugin.cs index e23b0984..3ff291d6 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/QrCodeRenderPlugin.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/QrCodeRenderPlugin.cs @@ -3,9 +3,9 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Rendering.Markdown.RenderInterop; -namespace Bookgen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; +namespace BookGen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; internal sealed class QrCodeRenderPlugin(IRenderInterop renderInterop) : SyntaxRendererPlugin { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/SyntaxRendererPlugin.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/SyntaxRendererPlugin.cs similarity index 86% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/SyntaxRendererPlugin.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/SyntaxRendererPlugin.cs index db05aedb..a4134219 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/SyntaxRendererPlugin.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/SyntaxRendererPlugin.cs @@ -3,9 +3,9 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Images; -namespace Bookgen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; +namespace BookGen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; internal abstract class SyntaxRendererPlugin { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/TerminalRenderPlugin.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/TerminalRenderPlugin.cs similarity index 91% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/TerminalRenderPlugin.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/TerminalRenderPlugin.cs index 2f4a7fdb..e885ec12 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/TerminalRenderPlugin.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderPlugins/TerminalRenderPlugin.cs @@ -5,9 +5,9 @@ using System.Web; -using Bookgen.Lib.Rendering.Markdown.Renderers; +using BookGen.Lib.Rendering.Markdown.Renderers; -namespace Bookgen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; +namespace BookGen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; internal sealed class TerminalRenderPlugin : SyntaxRendererPlugin { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderer.cs similarity index 94% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderer.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderer.cs index e7c6bdf2..869305ab 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/SyntaxRenderer.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/SyntaxRenderer.cs @@ -4,16 +4,18 @@ //----------------------------------------------------------------------------- using System.Text; +using System.Text.Encodings.Web; +using System.Web; -using Bookgen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; -using Bookgen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Rendering.Markdown.Renderers.SyntaxRenderPlugins; +using BookGen.Lib.Rendering.Markdown.RenderInterop; using Markdig.Parsers; using Markdig.Renderers; using Markdig.Renderers.Html; using Markdig.Syntax; -namespace Bookgen.Lib.Rendering.Markdown.Renderers; +namespace BookGen.Lib.Rendering.Markdown.Renderers; internal sealed class SyntaxRenderer : HtmlObjectRenderer, IDisposable { @@ -49,7 +51,7 @@ public SyntaxRenderer(CodeBlockRenderer underlyingRenderer, "sass", "scss", "scala", "scheme", "smalltalk", "smarty", "sql", "soy", "stylus", "swift", "tap", "tcl", "textile", "tt2", "twig", "typescript", "vbnet", "velocity", "verilog", "vhdl", "vim", "visual-basic", "wasm", "wiki", - "xeora", "xojo", "xquery", "yaml" + "xeora", "xojo", "xquery", "yaml", "xml" }; _plugins = new Dictionary(); RegisterPlugin(new TerminalRenderPlugin()); diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/AutolinkInlineRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/AutolinkInlineRenderer.cs similarity index 63% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/AutolinkInlineRenderer.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/AutolinkInlineRenderer.cs index 3c6566ee..d5959594 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/AutolinkInlineRenderer.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/AutolinkInlineRenderer.cs @@ -1,8 +1,13 @@ -using System.Web; +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Web; using Markdig.Syntax.Inlines; -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Terminal; +namespace BookGen.Lib.Rendering.Markdown.Renderers.Terminal; internal sealed class AutolinkInlineRenderer : TerminalObjectRenderer { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/CodeBlockRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/CodeBlockRenderer.cs similarity index 74% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/CodeBlockRenderer.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/CodeBlockRenderer.cs index 396f994b..0ba434f0 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/CodeBlockRenderer.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/CodeBlockRenderer.cs @@ -1,7 +1,12 @@ -using Markdig.Helpers; +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Markdig.Helpers; using Markdig.Syntax; -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Terminal; +namespace BookGen.Lib.Rendering.Markdown.Renderers.Terminal; internal sealed class CodeBlockRenderer : TerminalObjectRenderer { @@ -34,6 +39,7 @@ protected override void Write(TerminalRenderer renderer, CodeBlock obj) } } + renderer.WriteReset(); renderer.WriteLine(); } } diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/CodeInlineRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/CodeInlineRenderer.cs similarity index 56% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/CodeInlineRenderer.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/CodeInlineRenderer.cs index 6c64eda4..e7165c7a 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/CodeInlineRenderer.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/CodeInlineRenderer.cs @@ -1,6 +1,11 @@ -using Markdig.Syntax.Inlines; +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Terminal; +using Markdig.Syntax.Inlines; + +namespace BookGen.Lib.Rendering.Markdown.Renderers.Terminal; internal sealed class CodeInlineRenderer : TerminalObjectRenderer { diff --git a/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/DelimiterInlineRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/DelimiterInlineRenderer.cs new file mode 100644 index 00000000..02a084da --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/DelimiterInlineRenderer.cs @@ -0,0 +1,17 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Markdig.Syntax.Inlines; + +namespace BookGen.Lib.Rendering.Markdown.Renderers.Terminal; + +internal sealed class DelimiterInlineRenderer : TerminalObjectRenderer +{ + protected override void Write(TerminalRenderer renderer, DelimiterInline obj) + { + renderer.Write(obj.ToLiteral()); + renderer.WriteChildren(obj); + } +} diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/EmphasisInlineRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/EmphasisInlineRenderer.cs similarity index 75% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/EmphasisInlineRenderer.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/EmphasisInlineRenderer.cs index 5c07d911..0e6d8227 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/EmphasisInlineRenderer.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/EmphasisInlineRenderer.cs @@ -1,10 +1,15 @@ -using System.Diagnostics; +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Diagnostics; using Markdig.Syntax.Inlines; using Webmaster442.WindowsTerminal; -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Terminal; +namespace BookGen.Lib.Rendering.Markdown.Renderers.Terminal; internal sealed class EmphasisInlineRenderer : TerminalObjectRenderer { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/HeadingRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/HeadingRenderer.cs similarity index 64% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/HeadingRenderer.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/HeadingRenderer.cs index b1030c27..1e67a9a5 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/HeadingRenderer.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/HeadingRenderer.cs @@ -1,6 +1,11 @@ -using Markdig.Syntax; +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Terminal; +using Markdig.Syntax; + +namespace BookGen.Lib.Rendering.Markdown.Renderers.Terminal; internal sealed class HeadingRenderer : TerminalObjectRenderer { diff --git a/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/LineBreakInlineRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/LineBreakInlineRenderer.cs new file mode 100644 index 00000000..594c42c0 --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/LineBreakInlineRenderer.cs @@ -0,0 +1,20 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Markdig.Syntax.Inlines; + +namespace BookGen.Lib.Rendering.Markdown.Renderers.Terminal; + +internal sealed class LineBreakInlineRenderer : TerminalObjectRenderer +{ + protected override void Write(TerminalRenderer renderer, LineBreakInline obj) + { + if (obj.IsHard) + { + renderer.WriteLine(); + } + renderer.EnsureLine(); + } +} diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/LinkInlineRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/LinkInlineRenderer.cs similarity index 65% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/LinkInlineRenderer.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/LinkInlineRenderer.cs index 68238816..d7719940 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/LinkInlineRenderer.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/LinkInlineRenderer.cs @@ -1,6 +1,11 @@ -using Markdig.Syntax.Inlines; +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Terminal; +using Markdig.Syntax.Inlines; + +namespace BookGen.Lib.Rendering.Markdown.Renderers.Terminal; internal sealed class LinkInlineRenderer : TerminalObjectRenderer { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/ListRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/ListRenderer.cs similarity index 85% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/ListRenderer.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/ListRenderer.cs index da92a5bd..ad1e7670 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/ListRenderer.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/ListRenderer.cs @@ -1,6 +1,11 @@ -using Markdig.Syntax; +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Terminal; +using Markdig.Syntax; + +namespace BookGen.Lib.Rendering.Markdown.Renderers.Terminal; internal sealed class ListRenderer : TerminalObjectRenderer { diff --git a/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/LiteralInlineRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/LiteralInlineRenderer.cs new file mode 100644 index 00000000..ac8cacc3 --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/LiteralInlineRenderer.cs @@ -0,0 +1,17 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Markdig.Syntax.Inlines; + +namespace BookGen.Lib.Rendering.Markdown.Renderers.Terminal; + +internal sealed class LiteralInlineRenderer : TerminalObjectRenderer +{ + protected override void Write(TerminalRenderer renderer, LiteralInline obj) + { + string content = obj.Content.ToString(); + renderer.Write(content); + } +} diff --git a/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/ParagraphRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/ParagraphRenderer.cs new file mode 100644 index 00000000..1f693ae6 --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/ParagraphRenderer.cs @@ -0,0 +1,19 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Markdig.Syntax; + +namespace BookGen.Lib.Rendering.Markdown.Renderers.Terminal; + +internal sealed class ParagraphRenderer : TerminalObjectRenderer +{ + protected override void Write(TerminalRenderer renderer, ParagraphBlock obj) + { + renderer + .WriteLeafInline(obj) + .WriteLine() + .WriteLine(); + } +} diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/QuoteBlockRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/QuoteBlockRenderer.cs similarity index 56% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/QuoteBlockRenderer.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/QuoteBlockRenderer.cs index c72787f8..1f99be40 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/QuoteBlockRenderer.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/QuoteBlockRenderer.cs @@ -1,6 +1,11 @@ -using Markdig.Syntax; +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Terminal; +using Markdig.Syntax; + +namespace BookGen.Lib.Rendering.Markdown.Renderers.Terminal; internal sealed class QuoteBlockRenderer : TerminalObjectRenderer { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/RenderOptions.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/RenderOptions.cs similarity index 72% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/RenderOptions.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/RenderOptions.cs index 88de79dd..ba9a7c16 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/RenderOptions.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/RenderOptions.cs @@ -1,6 +1,11 @@ -using Webmaster442.WindowsTerminal; +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Terminal; +using Webmaster442.WindowsTerminal; + +namespace BookGen.Lib.Rendering.Markdown.Renderers.Terminal; public sealed class RenderOptions { diff --git a/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/TerminalObjectRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/TerminalObjectRenderer.cs new file mode 100644 index 00000000..1c3d54ca --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/TerminalObjectRenderer.cs @@ -0,0 +1,13 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Markdig.Renderers; +using Markdig.Syntax; + +namespace BookGen.Lib.Rendering.Markdown.Renderers.Terminal; + +public abstract class TerminalObjectRenderer : MarkdownObjectRenderer where TObject : MarkdownObject +{ +} diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/TerminalRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/TerminalRenderer.cs similarity index 76% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/TerminalRenderer.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/TerminalRenderer.cs index 8e1dc7d6..37f4823b 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/TerminalRenderer.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/TerminalRenderer.cs @@ -1,17 +1,22 @@ -using Markdig.Renderers; +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Markdig.Renderers; using Webmaster442.WindowsTerminal; -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Terminal; +namespace BookGen.Lib.Rendering.Markdown.Renderers.Terminal; public sealed class TerminalRenderer : TextRendererBase { - public TerminalRenderer(TextWriter writer, RenderOptions renderOptions) : base(writer) { RenderOptions = renderOptions; Builder = new TerminalFormattedStringBuilder(); + ObjectRenderers.Add(new YamlFrontMatterRenderer()); ObjectRenderers.Add(new CodeBlockRenderer()); ObjectRenderers.Add(new ListRenderer()); ObjectRenderers.Add(new HeadingRenderer()); diff --git a/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/ThematicBreakRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/ThematicBreakRenderer.cs new file mode 100644 index 00000000..16c18677 --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/ThematicBreakRenderer.cs @@ -0,0 +1,17 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Markdig.Syntax; + +namespace BookGen.Lib.Rendering.Markdown.Renderers.Terminal; + +internal sealed class ThematicBreakRenderer : TerminalObjectRenderer +{ + protected override void Write(TerminalRenderer renderer, ThematicBreakBlock obj) + { + renderer.WriteLine(new string('-', renderer.RenderOptions.Width)); + renderer.WriteLine(); + } +} diff --git a/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/YamlFrontMatterRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/YamlFrontMatterRenderer.cs new file mode 100644 index 00000000..c563cbd0 --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/Terminal/YamlFrontMatterRenderer.cs @@ -0,0 +1,16 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Markdig.Extensions.Yaml; + +namespace BookGen.Lib.Rendering.Markdown.Renderers.Terminal; + +internal sealed class YamlFrontMatterRenderer : TerminalObjectRenderer +{ + protected override void Write(TerminalRenderer renderer, YamlFrontMatterBlock obj) + { + // Do not render anything for YAML front matter in terminal output + } +} diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/TerminalRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/Renderers/TerminalRenderer.cs similarity index 98% rename from Source/Bookgen.Lib/Rendering/Markdown/Renderers/TerminalRenderer.cs rename to Source/BookGen.Lib/Rendering/Markdown/Renderers/TerminalRenderer.cs index f80aa1f1..08f00bc1 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/TerminalRenderer.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Renderers/TerminalRenderer.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text; -namespace Bookgen.Lib.Rendering.Markdown.Renderers; +namespace BookGen.Lib.Rendering.Markdown.Renderers; internal static partial class TerminalRenderer { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Search.cs b/Source/BookGen.Lib/Rendering/Markdown/Search.cs similarity index 80% rename from Source/Bookgen.Lib/Rendering/Markdown/Search.cs rename to Source/BookGen.Lib/Rendering/Markdown/Search.cs index dba84855..d90a7d3d 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/Search.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/Search.cs @@ -1,11 +1,29 @@ -using System.Diagnostics.CodeAnalysis; +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- -namespace Bookgen.Lib.Rendering.Markdown; +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; + +namespace BookGen.Lib.Rendering.Markdown; public static class Search { internal static readonly char[] Separators = ['\r', '\n', '.', '?', '!', '\t']; + public static bool RegexContains(string document, Regex pattern, [NotNullWhen(true)] out string? context) + { + MatchCollection matches = pattern.Matches(document); + if (matches.Count == 0) + { + context = null; + return false; + } + context = matches.First().Value; + return true; + } + public static bool Contains(string document, string seachTerm, float similarity, [NotNullWhen(true)] out string? context) { var lines = document.Split(Separators, StringSplitOptions.RemoveEmptyEntries); diff --git a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/CustomAutoIdExtension.cs b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/CustomAutoIdExtension.cs similarity index 98% rename from Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/CustomAutoIdExtension.cs rename to Source/BookGen.Lib/Rendering/Markdown/TableOfContents/CustomAutoIdExtension.cs index bc898e01..6dc3b984 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/CustomAutoIdExtension.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/CustomAutoIdExtension.cs @@ -1,12 +1,10 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gbor +// (c) 2019-2026 Ruzsinszki Gbor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text; -using ExCSS; - using Markdig; using Markdig.Extensions.AutoIdentifiers; using Markdig.Helpers; @@ -16,7 +14,7 @@ using Markdig.Syntax; using Markdig.Syntax.Inlines; -namespace Bookgen.Lib.Rendering.Markdown.TableOfContents; +namespace BookGen.Lib.Rendering.Markdown.TableOfContents; /// /// Custom auto indentifier, add some custom options by .
diff --git a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/CustomAutoIdOptions.cs b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/CustomAutoIdOptions.cs similarity index 94% rename from Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/CustomAutoIdOptions.cs rename to Source/BookGen.Lib/Rendering/Markdown/TableOfContents/CustomAutoIdOptions.cs index 85b85b49..cd165c83 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/CustomAutoIdOptions.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/CustomAutoIdOptions.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gbor +// (c) 2019-2026 Ruzsinszki Gbor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using Markdig.Extensions.AutoIdentifiers; -namespace Bookgen.Lib.Rendering.Markdown.TableOfContents; +namespace BookGen.Lib.Rendering.Markdown.TableOfContents; /// /// Delegate for handle custom heading id generate. diff --git a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/HeadingInfo.cs b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/HeadingInfo.cs similarity index 84% rename from Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/HeadingInfo.cs rename to Source/BookGen.Lib/Rendering/Markdown/TableOfContents/HeadingInfo.cs index d0c87894..384768af 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/HeadingInfo.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/HeadingInfo.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Rendering.Markdown.TableOfContents; +namespace BookGen.Lib.Rendering.Markdown.TableOfContents; internal sealed record class HeadingInfo { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/HeadingInfos.cs b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/HeadingInfos.cs similarity index 93% rename from Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/HeadingInfos.cs rename to Source/BookGen.Lib/Rendering/Markdown/TableOfContents/HeadingInfos.cs index 2994cda4..59a11dcb 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/HeadingInfos.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/HeadingInfos.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gbor +// (c) 2019-2026 Ruzsinszki Gbor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -7,7 +7,7 @@ using Markdig.Renderers; -namespace Bookgen.Lib.Rendering.Markdown.TableOfContents; +namespace BookGen.Lib.Rendering.Markdown.TableOfContents; [DebuggerDisplay("Level: {Level} Content: {Content}")] internal sealed class HeadingInfos : LevelList diff --git a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/HtmlTocRenderer.cs b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/HtmlTocRenderer.cs similarity index 94% rename from Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/HtmlTocRenderer.cs rename to Source/BookGen.Lib/Rendering/Markdown/TableOfContents/HtmlTocRenderer.cs index c6ae2af4..e28526dd 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/HtmlTocRenderer.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/HtmlTocRenderer.cs @@ -1,12 +1,12 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gbor +// (c) 2019-2026 Ruzsinszki Gbor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using Markdig.Renderers; using Markdig.Renderers.Html; -namespace Bookgen.Lib.Rendering.Markdown.TableOfContents; +namespace BookGen.Lib.Rendering.Markdown.TableOfContents; internal sealed class HtmlTocRenderer : HtmlObjectRenderer { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/LevelList.cs b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/LevelList.cs similarity index 98% rename from Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/LevelList.cs rename to Source/BookGen.Lib/Rendering/Markdown/TableOfContents/LevelList.cs index 1c21a8ab..03beb3b5 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/LevelList.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/LevelList.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gbor +// (c) 2019-2026 Ruzsinszki Gbor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Diagnostics; -namespace Bookgen.Lib.Rendering.Markdown.TableOfContents; +namespace BookGen.Lib.Rendering.Markdown.TableOfContents; /// /// Sort and merge with level diff --git a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/TocBlock.cs b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/TocBlock.cs similarity index 82% rename from Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/TocBlock.cs rename to Source/BookGen.Lib/Rendering/Markdown/TableOfContents/TocBlock.cs index 03657d86..6770e3fc 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/TocBlock.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/TocBlock.cs @@ -1,12 +1,12 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using Markdig.Parsers; using Markdig.Syntax; -namespace Bookgen.Lib.Rendering.Markdown.TableOfContents; +namespace BookGen.Lib.Rendering.Markdown.TableOfContents; internal sealed class TocBlock : HeadingBlock { diff --git a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/TocBlockParser.cs b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/TocBlockParser.cs similarity index 97% rename from Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/TocBlockParser.cs rename to Source/BookGen.Lib/Rendering/Markdown/TableOfContents/TocBlockParser.cs index e1a74e87..5ae60891 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/TocBlockParser.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/TocBlockParser.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text.RegularExpressions; -using Bookgen.Lib.Rendering.Markdown.TableOfContents; +using BookGen.Lib.Rendering.Markdown.TableOfContents; using Markdig.Helpers; using Markdig.Parsers; diff --git a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/TocExtension.cs b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/TocExtension.cs similarity index 94% rename from Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/TocExtension.cs rename to Source/BookGen.Lib/Rendering/Markdown/TableOfContents/TocExtension.cs index 4464ef2f..75320637 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/TocExtension.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/TocExtension.cs @@ -1,10 +1,8 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gbor +// (c) 2019-2026 Ruzsinszki Gbor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using System.Runtime.CompilerServices; - using BookGen.DomainServices.Markdown.TableOfContents; using Markdig; @@ -12,7 +10,7 @@ using Markdig.Renderers.Html; using Markdig.Syntax; -namespace Bookgen.Lib.Rendering.Markdown.TableOfContents; +namespace BookGen.Lib.Rendering.Markdown.TableOfContents; /// /// Table of content extension diff --git a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/TocState.cs b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/TocState.cs similarity index 86% rename from Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/TocState.cs rename to Source/BookGen.Lib/Rendering/Markdown/TableOfContents/TocState.cs index 8151a037..c57c6abf 100644 --- a/Source/Bookgen.Lib/Rendering/Markdown/TableOfContents/TocState.cs +++ b/Source/BookGen.Lib/Rendering/Markdown/TableOfContents/TocState.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gbor +// (c) 2019-2026 Ruzsinszki Gbor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace Bookgen.Lib.Rendering.Markdown.TableOfContents; +namespace BookGen.Lib.Rendering.Markdown.TableOfContents; internal sealed class TocState { diff --git a/Source/Bookgen.Lib/Internals/Extensions.cs b/Source/BookGen.Lib/Rendering/MarkdownLoader.cs similarity index 56% rename from Source/Bookgen.Lib/Internals/Extensions.cs rename to Source/BookGen.Lib/Rendering/MarkdownLoader.cs index f95d6046..b4627c33 100644 --- a/Source/Bookgen.Lib/Internals/Extensions.cs +++ b/Source/BookGen.Lib/Rendering/MarkdownLoader.cs @@ -1,27 +1,23 @@ -using System.Globalization; +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + using System.Text; -using Bookgen.Lib.Domain; -using Bookgen.Lib.Domain.IO; +using BookGen.Lib.Domain; +using BookGen.Lib.Domain.IO; using BookGen.Vfs; -using Markdig.Syntax; -using Markdig.Syntax.Inlines; - using Microsoft.Extensions.Logging; using YamlDotNet.Serialization; -namespace Bookgen.Lib.Internals; +namespace BookGen.Lib.Rendering; -internal static class Extensions +public static class MarkdownLoader { - private const string W3cTime = "yyyy-MM-ddTHH:mm:sszzz"; - private const string W3zTime = "yyyy-MM-ddTHH:mm:ss"; - private const string WorpdressTime = "ddd, d MMM yyyy HH:mm:ss"; - private const string WordpressPostDate = "yyyy-MM-dd HH:mm:ss"; - private static async Task<(string content, FrontMatter frontMatter)> GetFileContents(IReadOnlyFileSystem folder, string file, ILogger logger) @@ -65,21 +61,6 @@ static FrontMatter CreateDefaultFrontMatter(string diskPath, ILogger log) return (content.ToString(), frontMatter); } - extension(DateTime dt) - { - public string ToW3CTimeFormat() - => dt.ToString(W3cTime); - - public string ToW3CZTimeFormat() - => dt.ToString(W3zTime) + "Z"; - - public string ToWordpressTime() - => dt.ToString(WorpdressTime, new CultureInfo("en-US")) + " +0000"; - - public string ToWordpressPostDate() - => dt.ToString(WordpressPostDate); - } - extension(IReadOnlyFileSystem folder) { public async Task GetSourceFile(string file, ILogger logger) @@ -94,25 +75,5 @@ public async Task GetSourceFile(string file, ILogger logger) FrontMatter = frontMatter, }; } - - public async Task GetCoverFileName(TableOfContents tableOfContents, ILogger logger) - { - var contents = await folder.ReadAllTextAsync(tableOfContents.IndexFile); - foreach (Block block in Markdig.Markdown.Parse(contents)) - { - if (block is ParagraphBlock paragraph && paragraph.Inline != null) - { - foreach (Inline inline in paragraph.Inline) - { - if (inline is LinkInline link && link.IsImage) - { - return link.Url; - } - } - } - } - logger.LogWarning("No cover image found in {file}", tableOfContents.IndexFile); - return null; - } } } diff --git a/Source/BookGen.Lib/Rendering/Templates/Expressions/ExpressionFactory.cs b/Source/BookGen.Lib/Rendering/Templates/Expressions/ExpressionFactory.cs new file mode 100644 index 00000000..55fdb021 --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Templates/Expressions/ExpressionFactory.cs @@ -0,0 +1,168 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Globalization; +using System.Linq.Expressions; +using System.Reflection; + +namespace BookGen.Lib.Rendering.Templates.Expressions; + +internal ref struct ExpressionFactory +{ + // Variables are read from this single parameter at invoke time instead of being baked + // in as constants, so a compiled expression no longer depends on a specific model and + // can be cached and reused across renders. The instance is immutable and shared by all + // compiled lambdas. + private static readonly ParameterExpression VariablesParameter = + Expression.Parameter(typeof(IReadOnlyDictionary), "variables"); + + private static readonly MethodInfo GetVariableMethod = + typeof(ExpressionFactory).GetMethod(nameof(GetVariable), BindingFlags.NonPublic | BindingFlags.Static)!; + + public static Func, object?> Compile( + string expressionString, + Dictionary> functions) + { + TokenCollection tokens = Tokenizer.Tokenize(expressionString, x => functions.ContainsKey(x)); + + // The collection is positioned before the first token, advance onto it. + tokens.Next(); + + // A lone variable reference is the most common template token; serve it with a direct + // dictionary lookup instead of emitting and JIT-compiling IL. + if (tokens.Count == 2 && tokens.CurrentToken.Type == TokenType.Variable) + { + string name = tokens.CurrentToken.Value; + return variables => GetVariable(variables, name); + } + + Expression body = ParseExpression(tokens, functions); + + if (tokens.CurrentToken.Type != TokenType.EOF) + throw new InvalidOperationException($"Unexpected token after expression: {tokens.CurrentToken}"); + + // Constant (literal / constant-folded) expressions don't need IL emission either. + if (body is ConstantExpression constant) + { + object? value = constant.Value; + return _ => value; + } + + if (body.Type != typeof(object)) + body = Expression.Convert(body, typeof(object)); + + return Expression + .Lambda, object?>>(body, VariablesParameter) + .Compile(); + } + + private static Expression ParseExpression(TokenCollection tokens, + Dictionary> functions) + { + Token token = tokens.CurrentToken; + switch (token.Type) + { + case TokenType.Integer: + tokens.Eat(TokenType.Integer); + return Expression.Constant(long.Parse(token.Value, CultureInfo.InvariantCulture)); + case TokenType.Double: + tokens.Eat(TokenType.Double); + return Expression.Constant(double.Parse(token.Value, CultureInfo.InvariantCulture)); + case TokenType.Boolean: + tokens.Eat(TokenType.Boolean); + return Expression.Constant(bool.Parse(token.Value)); + case TokenType.String: + tokens.Eat(TokenType.String); + return Expression.Constant(token.Value); + case TokenType.Variable: + tokens.Eat(TokenType.Variable); + return Expression.Call(GetVariableMethod, VariablesParameter, Expression.Constant(token.Value)); + case TokenType.Function: + return ParseFunction(tokens, functions); + case TokenType.OpenParen: + tokens.Eat(TokenType.OpenParen); + Expression inner = ParseExpression(tokens, functions); + tokens.Eat(TokenType.CloseParen); + return inner; + default: + throw new InvalidOperationException($"Unexpected token: {token}"); + } + } + + private static InvocationExpression ParseFunction(TokenCollection tokens, + Dictionary> functions) + { + Token token = tokens.CurrentToken; + List overloads = functions[token.Value]; + + tokens.Eat(TokenType.Function); + tokens.Eat(TokenType.OpenParen); + + var arguments = new List(); + if (tokens.CurrentToken.Type != TokenType.CloseParen) + { + arguments.Add(ParseExpression(tokens, functions)); + while (tokens.CurrentToken.Type == TokenType.ArgumentDelimiter) + { + tokens.Eat(TokenType.ArgumentDelimiter); + arguments.Add(ParseExpression(tokens, functions)); + } + } + + tokens.Eat(TokenType.CloseParen); + + return BuildInvocation(overloads, arguments); + } + + private static object? GetVariable(IReadOnlyDictionary variables, string name) + { + if (!variables.TryGetValue(name, out object? value)) + throw new InvalidOperationException($"Unknown variable: {name}"); + + return value; + } + + private static InvocationExpression BuildInvocation(List overloads, List arguments) + { + foreach (FunctionOverload overload in overloads) + { + if (!overload.IsParamsArray && overload.ParameterTypes.Length == arguments.Count) + return BuildRegularFunction(overload, arguments); + } + + foreach (FunctionOverload overload in overloads) + { + if (overload.IsParamsArray) + return BuildArgsFunction(overload, arguments); + } + + throw new InvalidOperationException($"No suitable overload found that takes {arguments.Count} argument(s)"); + } + + private static InvocationExpression BuildArgsFunction(FunctionOverload overload, List arguments) + { + var parameters = new Expression[arguments.Count]; + for (int i = 0; i < arguments.Count; i++) + { + parameters[i] = Expression.Convert(arguments[i], typeof(object)); + } + + return Expression.Invoke(Expression.Constant(overload.Function), Expression.NewArrayInit(typeof(object), parameters)); + } + + private static InvocationExpression BuildRegularFunction(FunctionOverload overload, List arguments) + { + Type[] parameterTypes = overload.ParameterTypes; + + for (int i = 0; i < arguments.Count; i++) + { + Type parameterType = parameterTypes[i]; + if (arguments[i].Type != parameterType) + arguments[i] = Expression.Convert(arguments[i], parameterType); + } + + return Expression.Invoke(Expression.Constant(overload.Function), arguments); + } +} diff --git a/Source/BookGen.Lib/Rendering/Templates/Expressions/FunctionOverload.cs b/Source/BookGen.Lib/Rendering/Templates/Expressions/FunctionOverload.cs new file mode 100644 index 00000000..d61ecb43 --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Templates/Expressions/FunctionOverload.cs @@ -0,0 +1,27 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Reflection; + +namespace BookGen.Lib.Rendering.Templates.Expressions; + +internal sealed class FunctionOverload +{ + public Delegate Function { get; } + public Type[] ParameterTypes { get; } + public bool IsParamsArray { get; } + + public FunctionOverload(Delegate function) + { + Function = function; + ParameterInfo[] parameters = function.Method.GetParameters(); + ParameterTypes = new Type[parameters.Length]; + for (int i = 0; i < parameters.Length; i++) + { + ParameterTypes[i] = parameters[i].ParameterType; + } + IsParamsArray = ParameterTypes.Length == 1 && ParameterTypes[0] == typeof(object[]); + } +} diff --git a/Source/BookGen.Lib/Rendering/Templates/Expressions/SharedStringBuilder.cs b/Source/BookGen.Lib/Rendering/Templates/Expressions/SharedStringBuilder.cs new file mode 100644 index 00000000..5720396f --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Templates/Expressions/SharedStringBuilder.cs @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Text; + +namespace BookGen.Lib.Rendering.Templates.Expressions; + +internal sealed class SharedStringBuilder(int capacity) +{ + private readonly StringBuilder _sb = new(capacity); + + public SharedStringBuilder Append(char value) + { + _sb.Append(value); + return this; + } + + public override string ToString() + { + string value = _sb.ToString(); + _sb.Clear(); + return value; + } +} diff --git a/Source/BookGen.Lib/Rendering/Templates/Expressions/Token.cs b/Source/BookGen.Lib/Rendering/Templates/Expressions/Token.cs new file mode 100644 index 00000000..ab2d8a7c --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Templates/Expressions/Token.cs @@ -0,0 +1,15 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Lib.Rendering.Templates.Expressions; + +internal readonly struct Token(string value, TokenType type) +{ + public string Value { get; } = value; + public TokenType Type { get; } = type; + + public override string ToString() + => $"{Value} | {Type}"; +} diff --git a/Source/BookGen.Lib/Rendering/Templates/Expressions/TokenCollection.cs b/Source/BookGen.Lib/Rendering/Templates/Expressions/TokenCollection.cs new file mode 100644 index 00000000..47fa1d1a --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Templates/Expressions/TokenCollection.cs @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Lib.Rendering.Templates.Expressions; + +internal sealed class TokenCollection +{ + private readonly List _tokens; + private int _index; + + public TokenCollection() + { + _tokens = new List(); + CurrentToken = new Token(string.Empty, TokenType.None); + _index = -1; + } + + public int Count => _tokens.Count; + + public void Add(Token token) + => _tokens.Add(token); + + public Token CurrentToken + { + get; private set; + } + + public bool Next() + { + ++_index; + + if (CurrentToken.Type == TokenType.EOF) + throw new InvalidOperationException("Out of tokens"); + + CurrentToken = _tokens[_index]; + + return CurrentToken.Type != TokenType.EOF; + } + + public void Eat(TokenType type) + { + if (CurrentToken.Type != type) + throw new InvalidOperationException($"Expected a {type} token, got: {CurrentToken.Type}"); + + Next(); + } +} diff --git a/Source/BookGen.Lib/Rendering/Templates/Expressions/TokenType.cs b/Source/BookGen.Lib/Rendering/Templates/Expressions/TokenType.cs new file mode 100644 index 00000000..6bc909ef --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Templates/Expressions/TokenType.cs @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Globalization; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; + +namespace BookGen.Lib.Rendering.Templates.Expressions; + +internal enum TokenType +{ + None = 0, + Integer = 1, + Double = 2, + Boolean = 4, + String = 8, + ArgumentDelimiter = 16, + Variable = 32, + Function = 64, + OpenParen = 128, + CloseParen = 256, + EOF = int.MaxValue, +} diff --git a/Source/BookGen.Lib/Rendering/Templates/Expressions/Tokenizer.cs b/Source/BookGen.Lib/Rendering/Templates/Expressions/Tokenizer.cs new file mode 100644 index 00000000..dd1dc803 --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Templates/Expressions/Tokenizer.cs @@ -0,0 +1,214 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Lib.Rendering.Templates.Expressions; + +internal ref struct Tokenizer +{ + private static bool IsAdditionalyAllowedInNumber(char c) + => IsFloatCharacter(c) || c == '_'; + + private static bool IsFloatCharacter(char c) + { + return c == '.' + || c == '-' + || c == '+' + || c == 'E' + || c == 'e'; + } + + private static Token HandleNumber(SharedStringBuilder sb, string input, int start, out int newIndex) + { + int index = start; + bool containsAtLeastOneDigit = false; + bool isInScientificMode = false; + bool sciencemodePrefixed = false; + bool dotFound = false; + while (index < input.Length) + { + if (char.IsDigit(input[index])) + { + sb.Append(input[index]); + ++index; + containsAtLeastOneDigit = true; + } + else if (containsAtLeastOneDigit && IsAdditionalyAllowedInNumber(input[index])) + { + if (input[index] == '.' && !dotFound) + { + dotFound = true; + sb.Append(input[index]); + ++index; + } + else if ((input[index] == '+' || input[index] == '-') && isInScientificMode) + { + if (!sciencemodePrefixed) + { + sciencemodePrefixed = true; + sb.Append(input[index]); + ++index; + } + else + { + break; + } + } + else if ((input[index] == 'e' || input[index] == 'E') && !isInScientificMode) + { + isInScientificMode = true; + sb.Append(input[index]); + ++index; + } + else if (input[index] == '_') + { + sb.Append(input[index]); + ++index; + } + else + { + break; + } + } + else + { + break; + } + } + newIndex = index; + string tokenValue = sb.ToString(); + + if (tokenValue.Any(IsFloatCharacter)) + return new Token(tokenValue.Replace("_", ""), TokenType.Double); + else + return new Token(tokenValue.Replace("_", ""), TokenType.Integer); + } + + + private static Token HandleStringLiteral(SharedStringBuilder sb, string input, int start, char matcher, out int newIndex) + { + int index = start + 1; + while (index < input.Length) + { + if (input[index] == matcher) + { + index++; + break; + } + else + { + sb.Append(input[index]); + index++; + } + } + newIndex = index; + return new Token(sb.ToString(), TokenType.String); + } + + private static bool IsIdentifier(char c) + => c == '_' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); + + private static Token HandleIdentifier(SharedStringBuilder sb, + string input, + int start, + Predicate isFunctionCheck, + out int newIndex) + { + int index = start; + while (index < input.Length) + { + if (IsIdentifier(input[index])) + { + sb.Append(input[index]); + ++index; + } + else + { + break; + } + } + + string identifier = sb.ToString(); + newIndex = index; + switch (identifier) + { + case "true": + case "false": + { + return new Token(identifier, TokenType.Boolean); + } + default: + { + bool isFunction = isFunctionCheck.Invoke(identifier); + return isFunction + ? new Token(identifier, TokenType.Function) + : new Token(identifier, TokenType.Variable); + } + } + } + + public static TokenCollection Tokenize(string input, Predicate isFunctionCheck) + { + SharedStringBuilder sb = new(512); + TokenCollection tokens = new(); + int index = 0; + int newIndex = 0; + while (index < input.Length) + { + if (char.IsNumber(input[index])) + { + Token number = HandleNumber(sb, input, index, out newIndex); + tokens.Add(number); + index = newIndex; + } + else if (input[index] == '"') + { + Token stringLiteral = HandleStringLiteral(sb, input, index, '"', out newIndex); + tokens.Add(stringLiteral); + index = newIndex; + } + else if (input[index] == '\'') + { + Token stringLiteral = HandleStringLiteral(sb, input, index, '\'', out newIndex); + tokens.Add(stringLiteral); + index = newIndex; + } + else if (IsIdentifier(input[index])) + { + Token identifier = HandleIdentifier(sb, input, index, isFunctionCheck, out newIndex); + tokens.Add(identifier); + index = newIndex; + } + else + { + if (input[index] <= ' ') + { + ++index; + continue; + } + + char current = input[index]; + char next = index + 1 < input.Length ? input[index + 1] : '\0'; + Token @operator = HandleOperator(current, next, index, out newIndex); + tokens.Add(@operator); + index = newIndex; + } + } + tokens.Add(new Token(string.Empty, TokenType.EOF)); + return tokens; + + } + + private static Token HandleOperator(char current, char next, int index, out int newIndex) + { + newIndex = index + 1; + return current switch + { + '(' => new Token("(", TokenType.OpenParen), + ')' => new Token(")", TokenType.CloseParen), + ',' => new Token(",", TokenType.ArgumentDelimiter), + _ => throw new InvalidOperationException($"Invalid operator `{current}` at position {index}"), + }; + } +} diff --git a/Source/BookGen.Lib/Rendering/Templates/GenericTemplateEngine.cs b/Source/BookGen.Lib/Rendering/Templates/GenericTemplateEngine.cs new file mode 100644 index 00000000..27252b17 --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Templates/GenericTemplateEngine.cs @@ -0,0 +1,162 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Collections.Concurrent; +using System.Globalization; +using System.Reflection; +using System.Text; + +using BookGen.Lib.Rendering.Templates.Expressions; + +using Microsoft.Extensions.Logging; + +namespace BookGen.Lib.Rendering.Templates; + +public class GenericTemplateEngine +{ + private readonly Dictionary> _functions; + private readonly ConcurrentDictionary, object?>> _expressionCache; + private readonly Func, object?>> _compileExpression; + private readonly PropertyInfo[] _properties; + private readonly ILogger _logger; + private readonly TemplateEngineOptions _options; + + private const string ModelVariableName = "_model"; + + public GenericTemplateEngine(ILogger logger, TemplateEngineOptions options) + { + _functions = new Dictionary>(options.PropertyNameComparer); + _expressionCache = new ConcurrentDictionary, object?>>(options.PropertyNameComparer); + _compileExpression = expression => ExpressionFactory.Compile(expression, _functions); + _properties = typeof(TModel).GetProperties(BindingFlags.Public | BindingFlags.Instance); + _logger = logger; + _options = options; + } + + private Func, object?> GetCompiledExpression(string expression) + => _expressionCache.GetOrAdd(expression, _compileExpression); + + private void Register(string name, Delegate function) + { + if (!_functions.TryGetValue(name, out List? value)) + { + value = new List(); + _functions[name] = value; + } + + value.Add(new FunctionOverload(function)); + + // A newly registered function can change how previously seen expressions resolve. + _expressionCache.Clear(); + } + + private Dictionary GetValues(TModel? model) + { + if (model is IDictionaryConvertible dictionaryConvertible) + return dictionaryConvertible.ToDictionary(_options.PropertyNameComparer); + + Dictionary values = new(); + + values.Add(ModelVariableName, model); + foreach (PropertyInfo property in _properties) + { + object? value = property.GetValue(model); + values[property.Name] = value; + } + return values; + } + + + private string GetStr(object? value) + { + if (value is IFormattable formattable) + { + return formattable is DateTime dateTime + ? dateTime.ToString("yyyy-MM-dd HH:mm:ss") + : formattable.ToString(null, CultureInfo.InvariantCulture); + } + return value?.ToString() ?? (_options.EmitNullString ? "null" : string.Empty); + } + + private string Evaluate(string expression, Dictionary values) + { + try + { + Func, object?> compiled = GetCompiledExpression(expression); + object? result = compiled(values); + return GetStr(result); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error evaluating expression: {Expression}", expression); + return $"

{ex.Message}

"; + } + } + + private static StringBuilder AllocateBuffer(string template, TModel? model) + { + int size = template.Length * 2; + if (model is ICanProvideRoughSize roughSize) + { + size = template.Length + roughSize.CalculateRoughSize(); + } + return new StringBuilder(size); + } + + public void RegisterFunction(string name, Func function) + => Register(name, function); + + public void RegisterFunction(string name, Func function) + => Register(name, function); + + public void RegisterFunction(string name, Func function) + => Register(name, function); + + public void RegisterFunction(string name, Func function) + => Register(name, function); + + public void RegisterFunction(string name, Func function) + => Register(name, function); + + public string Render(string template, TModel model) + { + StringBuilder buffer = GenericTemplateEngine.AllocateBuffer(template, model); + using var stringWriter = new StringWriter(buffer); + Render(stringWriter, template, model); + return buffer.ToString(); + } + + public void Render(TextWriter target, string template, TModel model) + { + Dictionary values = GetValues(model); + int i = 0; + while (i < template.Length) + { + int start = template.IndexOf("{{", i, StringComparison.Ordinal); + if (start == -1) + { + target.Write(template[i..]); + break; + } + + if (start > i) + { + target.Write(template[i..start]); + } + + int end = template.IndexOf("}}", start + 2, StringComparison.Ordinal); + if (end == -1) + { + throw new InvalidOperationException("Unmatched opening braces in template."); + } + + string expression = template[(start + 2)..end].Trim(); + target.Write(Evaluate(expression, values)); + + i = end + 2; + } + } + +} diff --git a/Source/BookGen.Lib/Rendering/Templates/ICanProvideRoughSize.cs b/Source/BookGen.Lib/Rendering/Templates/ICanProvideRoughSize.cs new file mode 100644 index 00000000..a061418f --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Templates/ICanProvideRoughSize.cs @@ -0,0 +1,11 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Lib.Rendering.Templates; + +public interface ICanProvideRoughSize +{ + int CalculateRoughSize(); +} diff --git a/Source/BookGen.Lib/Rendering/Templates/IDictionaryConvertible.cs b/Source/BookGen.Lib/Rendering/Templates/IDictionaryConvertible.cs new file mode 100644 index 00000000..f84bdfdf --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Templates/IDictionaryConvertible.cs @@ -0,0 +1,11 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Lib.Rendering.Templates; + +public interface IDictionaryConvertible +{ + Dictionary ToDictionary(StringComparer comparer); +} diff --git a/Source/BookGen.Lib/Rendering/Templates/TemplateEngine.cs b/Source/BookGen.Lib/Rendering/Templates/TemplateEngine.cs new file mode 100644 index 00000000..c30744ec --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Templates/TemplateEngine.cs @@ -0,0 +1,43 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using BookGen.Vfs; + +using Microsoft.Extensions.Logging; + +namespace BookGen.Lib.Rendering.Templates; + +public sealed class TemplateEngine : GenericTemplateEngine +{ + private readonly IAssetSource _assetSource; + + public TemplateEngine(ILogger logger, IAssetSource assetSource) : this(logger, assetSource, new TemplateEngineOptions()) + { + } + + public TemplateEngine(ILogger logger, IAssetSource assetSource, TemplateEngineOptions options) : base(logger, options) + { + _assetSource = assetSource; + var functions = new TemplateFunctions(options.TimeProvider); + RegisterFunction(nameof(functions.ToUpper), functions.ToUpper); + RegisterFunction(nameof(functions.ToLower), functions.ToLower); + RegisterFunction(nameof(functions.Substring), functions.Substring); + RegisterFunction(nameof(functions.Trim), functions.Trim); + RegisterFunction(nameof(functions.TrimStart), functions.TrimStart); + RegisterFunction(nameof(functions.TrimEnd), functions.TrimEnd); + RegisterFunction(nameof(functions.Replace), functions.Replace); + RegisterFunction(nameof(functions.Concat), functions.Concat); + RegisterFunction(nameof(functions.RegexReplace), functions.RegexReplace); + RegisterFunction(nameof(functions.HtmlEncode), functions.HtmlEncode); + RegisterFunction(nameof(functions.UrlEncode), functions.UrlEncode); + RegisterFunction(nameof(functions.CurrentDate), functions.CurrentDate); + RegisterFunction(nameof(functions.CurrentDateFormat), functions.CurrentDateFormat); + RegisterFunction(nameof(functions.CurrentDateTime), functions.CurrentDateTime); + RegisterFunction(nameof(functions.CurrentDateTimeFormat), functions.CurrentDateTimeFormat); + RegisterFunction(nameof(functions.CurrentTime), functions.CurrentTime); + RegisterFunction(nameof(functions.CurrentTimeFormat), functions.CurrentTimeFormat); + RegisterFunction(nameof(functions.UrlDecode), functions.UrlDecode); + } +} diff --git a/Source/BookGen.Lib/Rendering/Templates/TemplateEngineOptions.cs b/Source/BookGen.Lib/Rendering/Templates/TemplateEngineOptions.cs new file mode 100644 index 00000000..efe6d650 --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Templates/TemplateEngineOptions.cs @@ -0,0 +1,13 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Lib.Rendering.Templates; + +public sealed class TemplateEngineOptions +{ + public bool EmitNullString { get; set; } = true; + public TimeProvider TimeProvider { get; set; } = TimeProvider.System; + public StringComparer PropertyNameComparer { get; set; } = StringComparer.Ordinal; +} diff --git a/Source/BookGen.Lib/Rendering/Templates/TemplateFunctions.cs b/Source/BookGen.Lib/Rendering/Templates/TemplateFunctions.cs new file mode 100644 index 00000000..351609b3 --- /dev/null +++ b/Source/BookGen.Lib/Rendering/Templates/TemplateFunctions.cs @@ -0,0 +1,115 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace BookGen.Lib.Rendering.Templates; + + +internal sealed class TemplateFunctions(TimeProvider timeProvider) +{ + private static string ToString(object? obj) + { + if (obj == null) + return string.Empty; + + if (obj is string str) + return str; + + if (obj is IFormattable formattable) + return formattable.ToString(null, System.Globalization.CultureInfo.InvariantCulture); + + return obj.ToString() ?? string.Empty; + } + + private static int ToInt(object obj) + { + if (obj is int i) + return i; + + if (obj is IConvertible convertible) + return convertible.ToInt32(System.Globalization.CultureInfo.InvariantCulture); + + throw new InvalidCastException($"Cannot convert object of type {obj.GetType()} to int."); + } + + internal string ToUpper(object obj) + => ToString(obj).ToUpper(); + + internal string ToLower(object obj) + => ToString(obj).ToLower(); + + internal string Substring(object obj, object startIndex, object length) + => ToString(obj).Substring(ToInt(startIndex), ToInt(length)); + + internal string Trim(object obj) + => ToString(obj).Trim(); + + internal string TrimStart(object obj) + => ToString(obj).TrimStart(); + + internal string TrimEnd(object obj) + => ToString(obj).TrimEnd(); + + internal string Replace(object obj, object oldValue, object newValue) + => ToString(obj).Replace(ToString(oldValue), ToString(newValue)); + + internal string Concat(object[] args) + { + StringBuilder result = new StringBuilder(); + foreach (object arg in args) + { + result.Append(ToString(arg)); + } + return result.ToString(); + } + + internal string RegexReplace(object obj, object pattern, object replacement) + { + var input = ToString(obj); + var regexPattern = ToString(pattern); + var replacementStr = ToString(replacement); + return Regex.Replace(input, regexPattern, replacementStr, RegexOptions.CultureInvariant, TimeSpan.FromSeconds(5)); + } + + internal string HtmlEncode(object obj) + { + var input = ToString(obj); + return System.Net.WebUtility.HtmlEncode(input); + } + + internal string UrlEncode(object obj) + { + var input = ToString(obj); + return System.Net.WebUtility.UrlEncode(input); + } + + internal string CurrentDate() + => timeProvider.GetLocalNow().ToString("yyyy-MM-dd"); + + internal string CurrentDateFormat(object format) + => timeProvider.GetLocalNow().ToString(ToString(format)); + + internal string CurrentTime() + => timeProvider.GetLocalNow().ToString("HH:mm:ss"); + + internal string CurrentTimeFormat(object format) + => timeProvider.GetLocalNow().ToString(ToString(format)); + + internal string CurrentDateTime() + => timeProvider.GetLocalNow().ToString("yyyy-MM-dd HH:mm:ss"); + + internal string CurrentDateTimeFormat(object format) + => timeProvider.GetLocalNow().ToString(ToString(format)); + + internal string UrlDecode(object obj) + { + var input = ToString(obj); + return System.Net.WebUtility.UrlDecode(input); + } +} diff --git a/Source/Bookgen.Lib/Rendering/Templates/ViewData.cs b/Source/BookGen.Lib/Rendering/Templates/ViewData.cs similarity index 69% rename from Source/Bookgen.Lib/Rendering/Templates/ViewData.cs rename to Source/BookGen.Lib/Rendering/Templates/ViewData.cs index ef27813f..4129b42f 100644 --- a/Source/Bookgen.Lib/Rendering/Templates/ViewData.cs +++ b/Source/BookGen.Lib/Rendering/Templates/ViewData.cs @@ -1,13 +1,13 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Reflection; -namespace Bookgen.Lib.Rendering.Templates; +namespace BookGen.Lib.Rendering.Templates; -public class ViewData +public class ViewData : IDictionaryConvertible, ICanProvideRoughSize { /// /// HTML document title @@ -31,16 +31,24 @@ public class ViewData public Dictionary AdditionalData { get; init; } = new(); - public Dictionary GetDataTable(StringComparer comparer) + public int CalculateRoughSize() { - Dictionary result = new(comparer); + return Title.Length + + Content.Length + + Host.Length + + AdditionalData.Sum(kvp => kvp.Key.Length + kvp.Value.Length); + } + + public Dictionary ToDictionary(StringComparer comparer) + { + Dictionary result = new(comparer); IEnumerable properties = GetType() .GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => p.Name != nameof(AdditionalData)); foreach (PropertyInfo? property in properties) { - result.Add(property.Name, property.GetValue(this)?.ToString() ?? ""); + result.Add(property.Name, property.GetValue(this)); } foreach (KeyValuePair kvp in AdditionalData) diff --git a/Source/Bookgen.Lib/ResourceHandler.cs b/Source/BookGen.Lib/ResourceHandler.cs similarity index 96% rename from Source/Bookgen.Lib/ResourceHandler.cs rename to Source/BookGen.Lib/ResourceHandler.cs index 91ea4af4..b2da5093 100644 --- a/Source/Bookgen.Lib/ResourceHandler.cs +++ b/Source/BookGen.Lib/ResourceHandler.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Reflection; -namespace Bookgen.Lib; +namespace BookGen.Lib; public static class ResourceHandler { diff --git a/Source/Bookgen.Lib/YamlSerializerFactory.cs b/Source/BookGen.Lib/YamlSerializerFactory.cs similarity index 94% rename from Source/Bookgen.Lib/YamlSerializerFactory.cs rename to Source/BookGen.Lib/YamlSerializerFactory.cs index e4af8c15..e6c5add9 100644 --- a/Source/Bookgen.Lib/YamlSerializerFactory.cs +++ b/Source/BookGen.Lib/YamlSerializerFactory.cs @@ -1,12 +1,12 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; -namespace Bookgen.Lib; +namespace BookGen.Lib; public static class YamlSerializerFactory { diff --git a/Source/Bookgen.Lib/packages.lock.json b/Source/BookGen.Lib/packages.lock.json similarity index 76% rename from Source/Bookgen.Lib/packages.lock.json rename to Source/BookGen.Lib/packages.lock.json index 3eb24f43..802ed806 100644 --- a/Source/Bookgen.Lib/packages.lock.json +++ b/Source/BookGen.Lib/packages.lock.json @@ -4,15 +4,15 @@ "net10.0": { "AngleSharp": { "type": "Direct", - "requested": "[1.5.1, )", - "resolved": "1.5.1", - "contentHash": "jFG0tKwFMNzIdv0rX3qvyiKyxAzUIvDyTLTJo2ORKtv/hiJC8juZTknc4b9DQmBFbrky+0twr58QQWt6DR48Og==" + "requested": "[1.5.2, )", + "resolved": "1.5.2", + "contentHash": "LVZ7rrr6GHbhERhTgVDarVygl1e6KwE96pronA90LBOMacDhxR4lB+xYLjn/vlnUu+xbOB2RM9fuCPEx5bdyWA==" }, "Markdig": { "type": "Direct", - "requested": "[1.3.0, )", - "resolved": "1.3.0", - "contentHash": "1cWDY3Rhd24SVe66p2ekhEPhaSAXuH3WgGn6EPNjqXL0Y4ycK7GXtq0UE5oeBYircNlqJIEQk9W2vz60hRaezA==" + "requested": "[1.3.2, )", + "resolved": "1.3.2", + "contentHash": "fZgOC/3CswUrndjDTac70aQpYdtxbW5+5bRumR7vzvI2HJbkmgKisB1c9oT+GA6v0jB/JDR9BLa9FiPzQmaK6A==" }, "Microsoft.ClearScript": { "type": "Direct", @@ -41,34 +41,40 @@ }, "PreMailer.Net": { "type": "Direct", - "requested": "[2.7.2, )", - "resolved": "2.7.2", - "contentHash": "OeN00ZA9ycJSSD4ZcjbWn5aJpJ+OfrAM3ti+9VI9Mc+o90QnWayPCU7PszopLa91lxKrL9ZnIcG33AAd5dypLA==", + "requested": "[2.7.3, )", + "resolved": "2.7.3", + "contentHash": "p6LB7Qu4ZsPmjiFtWmytiERhopuGxq3Ql/SVuOZueGzfrDNvvSi8sGaDVHVvJt0S+RCHU422TWLu2aMXDE9ejg==", "dependencies": { - "AngleSharp": "1.1.0" + "AngleSharp": "1.5.1" } }, + "Roslynator.Analyzers": { + "type": "Direct", + "requested": "[4.15.0, )", + "resolved": "4.15.0", + "contentHash": "E8fu71Y+vzhniJKy1K13Rzwm8Vjcj7CDFBpflqncKUZfc3SzuF0kMBiyMmgXNPkjWTdE3Z+30HlGgYjHONZY/Q==" + }, "SkiaSharp": { "type": "Direct", - "requested": "[3.119.4, )", - "resolved": "3.119.4", - "contentHash": "53NOSUZ1Us+91Sm0uCkIivh/k7jOowRErZT2sIWwPFN9mLUvdxnE6rS4sWo4255+Rd2MWUSF+j0NMZHD6Cke+Q==", + "requested": "[4.150.1, )", + "resolved": "4.150.1", + "contentHash": "5v3T8X1N62Dp+AkPO70GNBNS/NRBPGMOTiN+Prg33sZAcm/Ug3YOAH+3RTj/jxJV8NGTJs2idGpC2Qdae2mGLQ==", "dependencies": { - "SkiaSharp.NativeAssets.Win32": "3.119.4", - "SkiaSharp.NativeAssets.macOS": "3.119.4" + "SkiaSharp.NativeAssets.Win32": "4.150.1", + "SkiaSharp.NativeAssets.macOS": "4.150.1" } }, "SkiaSharp.NativeAssets.Linux": { "type": "Direct", - "requested": "[3.119.4, )", - "resolved": "3.119.4", - "contentHash": "UAyVzbqNfZsZbKbzj68zXLyUyF/SbTKmzTfOO6qDu++dtIUMMTzPBe8oOuzU/DiewpfKoUUlOSsJmqWc6blxBw==" + "requested": "[4.150.1, )", + "resolved": "4.150.1", + "contentHash": "7IZtljoTBp0EH7/6K3p3yey44db3W7hkV9UPdCOaQQ2TxFkmFOpXwOPiQ865DrG+szgp0aKpQs8vYrzRdF83Ng==" }, "SkiaSharp.NativeAssets.Win32": { "type": "Direct", - "requested": "[3.119.4, )", - "resolved": "3.119.4", - "contentHash": "XOpbx/4CReO2wYsq2s6rbvdauc6dntG4Zv499sHGTJ87bwZaFXszFkwql3+FIZMc8kUPeaj3Mx2ezIJmo8a1Kg==" + "requested": "[4.150.1, )", + "resolved": "4.150.1", + "contentHash": "qrLSL8OonbkMJdSH8heK0Jl39Y2xxZGdd7Ru4cyBXk2ITdx+Fu1sCFLmvCKmnMaYs//mlN/YSpSpOurEXErcQw==" }, "Svg.Skia": { "type": "Direct", @@ -89,15 +95,15 @@ }, "System.ServiceModel.Syndication": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "ZxViVHpGq/a/lZsNZlxeAO1e+U0ViqtB99gkQj0W8rD7FmIs1iyL7Ip8d7w9S76EZhRW8Obc74zuoIJpITqD6A==" + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "jU3WQG477Tk7RYlM2Ky8oySb6WHd744LgK7MYRMe4L8FcoJI3Gbpcf29sB2lIlJVS82ah9i4FUw3RfbXeJs8jw==" }, "YamlDotNet": { "type": "Direct", - "requested": "[18.0.0, )", - "resolved": "18.0.0", - "contentHash": "ptHVgcYmLejGuWXV7RMFoEqFKYMXnieOlWLPzEslfDtzZ9ngMhjYwykfqjBN2+fMEAEyobozkj07lKEpR4dssA==" + "requested": "[18.1.0, )", + "resolved": "18.1.0", + "contentHash": "5K+9KFg2TdTl7VXv88Qzi/0lqK6JFoNP3lRuImPYGRV7K/QYklDyTrj4+A+KAki1JsQi6qKY+hDyY7d6WRqjrw==" }, "ExCSS": { "type": "Transitive", @@ -181,13 +187,13 @@ }, "SkiaSharp.NativeAssets.macOS": { "type": "Transitive", - "resolved": "3.119.4", - "contentHash": "fgBOWEqbY012x7gMfJU4ezgz6dfhJb30Z6YdW35h85Zoe39+a8YNbAAwL29ihPfWoppg5AjvyKNzD1oCvlqWwA==" + "resolved": "4.150.1", + "contentHash": "r755HVwaHZhyf1clWjrM2/RoOZYCzkQEmE9pu/mVsebPejWu52niNPUwtfyf112qoF0PIk6OndqVOUoITj6TwQ==" }, "Spectre.Console.Ansi": { "type": "Transitive", - "resolved": "0.57.0", - "contentHash": "DGeYljyWAIHSeUAoUGqyzyOqS8WlsHKW+y4zOZ0OO61oqrhx90jHS+XG5Kpq/wWz6ovIs2VL/0CTKpUVa7tdWg==" + "resolved": "0.57.2", + "contentHash": "Y1+u73shwP+JYHmrkdN6bSt2kaGBEAafhi2nrecczbXdFRey+k7J/WF9YPfcB0rqivv2S8fmif6FSBq9m/AFvw==" }, "Svg.Animation": { "type": "Transitive", @@ -230,7 +236,7 @@ "bookgen.shell.shared": { "type": "Project", "dependencies": { - "Spectre.Console": "[0.57.0, )", + "Spectre.Console": "[0.57.2, )", "Webmaster442.WindowsTerminal": "[4.1.1, )" } }, @@ -239,11 +245,11 @@ }, "Spectre.Console": { "type": "CentralTransitive", - "requested": "[0.57.0, )", - "resolved": "0.57.0", - "contentHash": "X5+4ZsUYTPlTs5PEoKRlxUuKOa1AkEoAGM34pUeJPjsBdtOnvnaVb31hqewhooomihl1i9fEBid1fz8XxZTacQ==", + "requested": "[0.57.2, )", + "resolved": "0.57.2", + "contentHash": "wzsB+P9i6F9G+cFOJxRotCiQWNSzZVAMxi8YMiHlGXXU1JLMXGp7zSSqBMmROZS2bzpK5jk+Saa7Evp8HwqhKg==", "dependencies": { - "Spectre.Console.Ansi": "0.57.0" + "Spectre.Console.Ansi": "0.57.2" } }, "Webmaster442.WindowsTerminal": { diff --git a/Source/BookGen.SamplePlugin/BookGen.SamplePlugin.csproj b/Source/BookGen.SamplePlugin/BookGen.SamplePlugin.csproj new file mode 100644 index 00000000..96b10ff7 --- /dev/null +++ b/Source/BookGen.SamplePlugin/BookGen.SamplePlugin.csproj @@ -0,0 +1,40 @@ + + + + + netstandard2.1 + enable + enable + + latest + + true + ..\..\bin\sampleplugin\$(Configuration)\ + false + + + + + + + + + false + runtime + + + + + + PreserveNewest + + + + + + $(OutputPath)..\..\$(Configuration)\plugins\$(AssemblyName).plugin + + + + + diff --git a/Source/BookGen.SamplePlugin/Helpers.cs b/Source/BookGen.SamplePlugin/Helpers.cs new file mode 100644 index 00000000..8e153051 --- /dev/null +++ b/Source/BookGen.SamplePlugin/Helpers.cs @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.SamplePlugin; + +internal static class Helpers +{ + public static uint GetId(string filePath) + { + uint result = 2166136261; + foreach (char c in filePath) + { + result ^= c; + result *= 16777619; + } + return result; + } + + public static string ReadEmbeddedFile(string fileName) + { + using Stream stream = typeof(Helpers).Assembly.GetManifestResourceStream(fileName) + ?? throw new InvalidOperationException($"Embedded resource '{fileName}' not found."); + + using var reader = new StreamReader(stream); + + return reader.ReadToEnd(); + } +} diff --git a/Source/BookGen.SamplePlugin/SamplePlugin.cs b/Source/BookGen.SamplePlugin/SamplePlugin.cs new file mode 100644 index 00000000..2c2ce365 --- /dev/null +++ b/Source/BookGen.SamplePlugin/SamplePlugin.cs @@ -0,0 +1,110 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using BookGen.Api; +using BookGen.Api.V1; + +namespace BookGen.SamplePlugin; + +public sealed class SamplePlugin : IBuildPluginV1 +{ + public async Task Build(IBook book, + IBookgenServices bookgenServices, + CancellationToken cancellationToken) + { + HtmlBuilder contentHtml = new(32 * 1024); + HtmlBuilder navHtml = new(4 * 1024); + + IRenderer renderer = bookgenServices.CreateRenderer(new RendererOptions + { + SvgRecode = RendererOptions.ImageOption.Passtrough, + }); + + bookgenServices.Logger.LogDebug("Processing document: {DocumentPath}", book.Index.FilePath); + (string content, IDocumentFrontMatter frontMatter) indexData = await book.Index.ReadContent(); + + string IndexHtml = renderer.RenderMarkdownToRawHtml(indexData.content); + + Add(contentHtml, navHtml, renderer, indexData); + + foreach (IChapter chapter in book.Chapters) + { + foreach (IDocument document in chapter.Documents) + { + bookgenServices.Logger.LogDebug("Processing document: {DocumentPath}", document.FilePath); + (string content, IDocumentFrontMatter frontMatter) docData = await document.ReadContent(); + Add(contentHtml, navHtml, renderer, docData); + } + } + + Add(contentHtml, navHtml, renderer, "Schemas", bookgenServices.DynamicDocumentation.GetSchemasMarkdown()); + Add(contentHtml, navHtml, renderer, "Commands", bookgenServices.DynamicDocumentation.GetCommandsMarkdown()); + + if (navHtml.LastUnclosedTag != null) + navHtml.CloseOpenTag(); + + if (!bookgenServices.AssetSource.TryGetAsset("prism.css", out string? prismCss)) + { + bookgenServices.Logger.LogError("Failed to get asset 'prism.css'"); + return false; + } + + RenderTags tags = new() + { + Content = contentHtml.ToString(), + Title = indexData.frontMatter.Title, + AdditionalData = new() + { + { "Navigation", navHtml.ToString() }, + { "Index", IndexHtml }, + { "PrismCss", prismCss } + } + }; + + string template = Helpers.ReadEmbeddedFile("BookGen.SamplePlugin.Template.html"); + + string rendered = renderer.RenderMarkdownToHtml(template, tags); + + await bookgenServices.OutputFolder.WriteTextFile("rendered.html", rendered); + + return true; + } + + private void Add(HtmlBuilder contentHtml, + HtmlBuilder navHtml, + IRenderer renderer, + string title, + string content) + { + string generatedId = $"a{Helpers.GetId(title)}"; + + string html = renderer.RenderMarkdownToRawHtml(content); + + contentHtml.Element("div", + tag => tag.Id(generatedId), + div => div.Raw(html)); + + if (navHtml.LastUnclosedTag != "ul") + { + navHtml.Element("ul"); + } + + navHtml.Element("li", + li => li.Element("a", tag => + { + tag.Attr("href", $"#{generatedId}"); + }, + a => a.Text(title))); + } + + private void Add(HtmlBuilder contentHtml, + HtmlBuilder navHtml, + IRenderer renderer, + (string content, IDocumentFrontMatter frontMatter) docData) + + { + Add(contentHtml, navHtml, renderer, docData.frontMatter.Title, docData.content); + } +} diff --git a/Source/BookGen.SamplePlugin/Template.html b/Source/BookGen.SamplePlugin/Template.html new file mode 100644 index 00000000..480a0453 --- /dev/null +++ b/Source/BookGen.SamplePlugin/Template.html @@ -0,0 +1,177 @@ + + + + + + + {{Title}} + + + + + + + +
+ + +
+ {{Index}} +
+
+ + + + + + + + \ No newline at end of file diff --git a/Source/BookGen.SamplePlugin/manifest.json b/Source/BookGen.SamplePlugin/manifest.json new file mode 100644 index 00000000..0265a6d9 --- /dev/null +++ b/Source/BookGen.SamplePlugin/manifest.json @@ -0,0 +1,7 @@ +{ + "entryAssembly": "BookGen.SamplePlugin.dll", + "description": "BookGen self documentation generator sample plugin", + "apiVersion": "1.0", + "author": "Webmaster442", + "url": "https://github.com/webmaster442/BookGen" +} \ No newline at end of file diff --git a/Source/BookGen.Shell.Shared/BookGen.Shell.Shared.csproj b/Source/BookGen.Shell.Shared/BookGen.Shell.Shared.csproj index 399331cc..d8d56396 100644 --- a/Source/BookGen.Shell.Shared/BookGen.Shell.Shared.csproj +++ b/Source/BookGen.Shell.Shared/BookGen.Shell.Shared.csproj @@ -10,6 +10,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Source/BookGen.Shell.Shared/GitCommandProvider.cs b/Source/BookGen.Shell.Shared/GitCommandProvider.cs index 0fe6e658..b49528fe 100644 --- a/Source/BookGen.Shell.Shared/GitCommandProvider.cs +++ b/Source/BookGen.Shell.Shared/GitCommandProvider.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shell.Shared/GitParser.cs b/Source/BookGen.Shell.Shared/GitParser.cs index 23156e09..05a175ec 100644 --- a/Source/BookGen.Shell.Shared/GitParser.cs +++ b/Source/BookGen.Shell.Shared/GitParser.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shell.Shared/GitStatus.cs b/Source/BookGen.Shell.Shared/GitStatus.cs index ea4e5dd3..adf79195 100644 --- a/Source/BookGen.Shell.Shared/GitStatus.cs +++ b/Source/BookGen.Shell.Shared/GitStatus.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shell.Shared/LinkBuilder.cs b/Source/BookGen.Shell.Shared/LinkBuilder.cs index a639955a..7ea69b2a 100644 --- a/Source/BookGen.Shell.Shared/LinkBuilder.cs +++ b/Source/BookGen.Shell.Shared/LinkBuilder.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shell.Shared/Loging/ConsoleLogProvider.cs b/Source/BookGen.Shell.Shared/Loging/ConsoleLogProvider.cs index 44ff26e8..f9853101 100644 --- a/Source/BookGen.Shell.Shared/Loging/ConsoleLogProvider.cs +++ b/Source/BookGen.Shell.Shared/Loging/ConsoleLogProvider.cs @@ -28,7 +28,7 @@ public ILogger CreateLogger(string categoryName) } else { - logger = new ConsoleLogger(categoryName); + logger = new ConsoleLogger(); _loggers.Add(categoryName, logger); return logger; } @@ -46,7 +46,7 @@ internal sealed class ConsoleLogger : IDisposable, ILogger { private readonly List _logBuffer; - public ConsoleLogger(string categoryName) + public ConsoleLogger() { _logBuffer = new List(); } diff --git a/Source/BookGen.Shell.Shared/ProcessRunner.cs b/Source/BookGen.Shell.Shared/ProcessRunner.cs index 4d8801b9..0ff4f385 100644 --- a/Source/BookGen.Shell.Shared/ProcessRunner.cs +++ b/Source/BookGen.Shell.Shared/ProcessRunner.cs @@ -13,7 +13,7 @@ public static void OpenUrl(string url) { if (!Uri.TryCreate(url, UriKind.Absolute, out _)) throw new ArgumentException("invalid url", nameof(url)); - + using (var process = new Process()) { process.StartInfo.UseShellExecute = true; diff --git a/Source/BookGen.Shell.Shared/ShellAutoCompleteFilter.cs b/Source/BookGen.Shell.Shared/ShellAutoCompleteFilter.cs index 3a228434..6f562650 100644 --- a/Source/BookGen.Shell.Shared/ShellAutoCompleteFilter.cs +++ b/Source/BookGen.Shell.Shared/ShellAutoCompleteFilter.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shell.Shared/TerminalOutputBuilder.cs b/Source/BookGen.Shell.Shared/TerminalOutputBuilder.cs index acad41b8..27c1ea89 100644 --- a/Source/BookGen.Shell.Shared/TerminalOutputBuilder.cs +++ b/Source/BookGen.Shell.Shared/TerminalOutputBuilder.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shell.Shared/TerminalProfileInstaller.cs b/Source/BookGen.Shell.Shared/TerminalProfileInstaller.cs index f98c8b61..4bb144ed 100644 --- a/Source/BookGen.Shell.Shared/TerminalProfileInstaller.cs +++ b/Source/BookGen.Shell.Shared/TerminalProfileInstaller.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shell.Shared/packages.lock.json b/Source/BookGen.Shell.Shared/packages.lock.json index b71507fb..f96ef418 100644 --- a/Source/BookGen.Shell.Shared/packages.lock.json +++ b/Source/BookGen.Shell.Shared/packages.lock.json @@ -4,20 +4,26 @@ "net10.0": { "Microsoft.Extensions.Logging.Abstractions": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "9S/DFt4cohlMPpzIxjG6kk0L8MuN2vDm9pbMCulxtJzzk82oJHVLBd8vuQxaPskaYQwKqmFmbannf5eoChgjYg==", + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "zkFxGYUvdxAvIKTyXHrmW+Sux53D4SezD9dMyZ6hrwwzPQJNuwCRy1f5W7AvYTqacEGhWF2XderRQG1OvbV8og==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10" } }, + "Roslynator.Analyzers": { + "type": "Direct", + "requested": "[4.15.0, )", + "resolved": "4.15.0", + "contentHash": "E8fu71Y+vzhniJKy1K13Rzwm8Vjcj7CDFBpflqncKUZfc3SzuF0kMBiyMmgXNPkjWTdE3Z+30HlGgYjHONZY/Q==" + }, "Spectre.Console": { "type": "Direct", - "requested": "[0.57.0, )", - "resolved": "0.57.0", - "contentHash": "X5+4ZsUYTPlTs5PEoKRlxUuKOa1AkEoAGM34pUeJPjsBdtOnvnaVb31hqewhooomihl1i9fEBid1fz8XxZTacQ==", + "requested": "[0.57.2, )", + "resolved": "0.57.2", + "contentHash": "wzsB+P9i6F9G+cFOJxRotCiQWNSzZVAMxi8YMiHlGXXU1JLMXGp7zSSqBMmROZS2bzpK5jk+Saa7Evp8HwqhKg==", "dependencies": { - "Spectre.Console.Ansi": "0.57.0" + "Spectre.Console.Ansi": "0.57.2" } }, "Spectre.Console.Analyzer": { @@ -34,14 +40,14 @@ }, "Spectre.Console.Ansi": { "type": "Transitive", - "resolved": "0.57.0", - "contentHash": "DGeYljyWAIHSeUAoUGqyzyOqS8WlsHKW+y4zOZ0OO61oqrhx90jHS+XG5Kpq/wWz6ovIs2VL/0CTKpUVa7tdWg==" + "resolved": "0.57.2", + "contentHash": "Y1+u73shwP+JYHmrkdN6bSt2kaGBEAafhi2nrecczbXdFRey+k7J/WF9YPfcB0rqivv2S8fmif6FSBq9m/AFvw==" }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "CentralTransitive", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "g41l/30G3K4B/d/L8kjux0+30e27c8D0FVQ/PFCpbekgfDpj9mnDhieP67EqXWvl1EWNeZh2rpR4F5B/jcDOHA==" + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "z/2xXlFw2aLGjHyEm6E0tQ+In6VfzQzTrtArbQ2c0TQE16ZbyDCMGPvaUT9I0s8rgy9sRWlU2P9waW37qV04qA==" } } } diff --git a/Source/BookGen.Shellprog/BookGen.Shellprog.csproj b/Source/BookGen.Shellprog/BookGen.Shellprog.csproj index bf548b8a..2ccdc818 100644 --- a/Source/BookGen.Shellprog/BookGen.Shellprog.csproj +++ b/Source/BookGen.Shellprog/BookGen.Shellprog.csproj @@ -17,6 +17,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + all diff --git a/Source/BookGen.Shellprog/CdgCommand.cs b/Source/BookGen.Shellprog/CdgCommand.cs index 67cd9eef..f38cc157 100644 --- a/Source/BookGen.Shellprog/CdgCommand.cs +++ b/Source/BookGen.Shellprog/CdgCommand.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -13,7 +13,7 @@ namespace BookGen.Shellprog; [CommandName("cdg")] internal sealed class CdgCommand : AsyncCommand { - public override async Task ExecuteAsync(CdgArguments arguments, IReadOnlyList context) + public override async Task ExecuteAsync(CdgArguments arguments, IReadOnlyList context, CancellationToken token) { var menu = new CdgSelector(arguments.Folder, arguments.ShowHidden); await menu.ShowMenu(); diff --git a/Source/BookGen.Shellprog/CommandCode/Cdg/CdgArguments.cs b/Source/BookGen.Shellprog/CommandCode/Cdg/CdgArguments.cs index 539b32e4..fc2d8d8d 100644 --- a/Source/BookGen.Shellprog/CommandCode/Cdg/CdgArguments.cs +++ b/Source/BookGen.Shellprog/CommandCode/Cdg/CdgArguments.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -13,7 +13,7 @@ namespace BookGen.Shellprog.CommandCode.Cdg; internal sealed class CdgArguments : ArgumentsBase { [Description("Show hidden files and folders")] - [Switch("h", "hidden")] + [Switch("h", "hidden", Required = false)] public bool ShowHidden { get; set; } [Argument(0, IsOptional = true)] diff --git a/Source/BookGen.Shellprog/CommandCode/Cdg/CdgSelector.cs b/Source/BookGen.Shellprog/CommandCode/Cdg/CdgSelector.cs index bfa67f6a..973e5a04 100644 --- a/Source/BookGen.Shellprog/CommandCode/Cdg/CdgSelector.cs +++ b/Source/BookGen.Shellprog/CommandCode/Cdg/CdgSelector.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shellprog/CommandCode/Cdg/SelectionItemAction.cs b/Source/BookGen.Shellprog/CommandCode/Cdg/SelectionItemAction.cs index 82fb6558..c4291777 100644 --- a/Source/BookGen.Shellprog/CommandCode/Cdg/SelectionItemAction.cs +++ b/Source/BookGen.Shellprog/CommandCode/Cdg/SelectionItemAction.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shellprog/CommandCode/Cdg/SelectionItemBase.cs b/Source/BookGen.Shellprog/CommandCode/Cdg/SelectionItemBase.cs index 15669350..2be9b622 100644 --- a/Source/BookGen.Shellprog/CommandCode/Cdg/SelectionItemBase.cs +++ b/Source/BookGen.Shellprog/CommandCode/Cdg/SelectionItemBase.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shellprog/CommandCode/Cdg/SelectionItemDirectory.cs b/Source/BookGen.Shellprog/CommandCode/Cdg/SelectionItemDirectory.cs index 2dbd8866..d9e0685b 100644 --- a/Source/BookGen.Shellprog/CommandCode/Cdg/SelectionItemDirectory.cs +++ b/Source/BookGen.Shellprog/CommandCode/Cdg/SelectionItemDirectory.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shellprog/CommandCode/Cdg/SelectionItemFactory.cs b/Source/BookGen.Shellprog/CommandCode/Cdg/SelectionItemFactory.cs index daac663a..7a9604dd 100644 --- a/Source/BookGen.Shellprog/CommandCode/Cdg/SelectionItemFactory.cs +++ b/Source/BookGen.Shellprog/CommandCode/Cdg/SelectionItemFactory.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shellprog/CommandCode/Organize/DefaultRulesProvider.cs b/Source/BookGen.Shellprog/CommandCode/Organize/DefaultRulesProvider.cs index 95865f5c..e450f953 100644 --- a/Source/BookGen.Shellprog/CommandCode/Organize/DefaultRulesProvider.cs +++ b/Source/BookGen.Shellprog/CommandCode/Organize/DefaultRulesProvider.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shellprog/CommandCode/Organize/OrganizeArguments.cs b/Source/BookGen.Shellprog/CommandCode/Organize/OrganizeArguments.cs index f13571a3..1b08f4f2 100644 --- a/Source/BookGen.Shellprog/CommandCode/Organize/OrganizeArguments.cs +++ b/Source/BookGen.Shellprog/CommandCode/Organize/OrganizeArguments.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -16,7 +16,7 @@ internal class OrganizeArguments : ArgumentsBase [Description("Folder to organize")] public string Folder { get; set; } - [Switch("s", "simulate")] + [Switch("s", "simulate", Required = false)] [Description("Simulate the organize process without making any changes")] public bool Simulate { get; set; } diff --git a/Source/BookGen.Shellprog/CommandCode/Organize/OrganizeRule.cs b/Source/BookGen.Shellprog/CommandCode/Organize/OrganizeRule.cs index 6b618e2c..fc4db816 100644 --- a/Source/BookGen.Shellprog/CommandCode/Organize/OrganizeRule.cs +++ b/Source/BookGen.Shellprog/CommandCode/Organize/OrganizeRule.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shellprog/CommandCode/Organize/RuleEngine.cs b/Source/BookGen.Shellprog/CommandCode/Organize/RuleEngine.cs index 4d56706c..77566eb0 100644 --- a/Source/BookGen.Shellprog/CommandCode/Organize/RuleEngine.cs +++ b/Source/BookGen.Shellprog/CommandCode/Organize/RuleEngine.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shellprog/CommandCode/Organize/RuleLoader.cs b/Source/BookGen.Shellprog/CommandCode/Organize/RuleLoader.cs index 1e106b84..eb7dc172 100644 --- a/Source/BookGen.Shellprog/CommandCode/Organize/RuleLoader.cs +++ b/Source/BookGen.Shellprog/CommandCode/Organize/RuleLoader.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shellprog/CommandListCommand.cs b/Source/BookGen.Shellprog/CommandListCommand.cs index eb439702..03dcf3cf 100644 --- a/Source/BookGen.Shellprog/CommandListCommand.cs +++ b/Source/BookGen.Shellprog/CommandListCommand.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shellprog/GitAutoCompleteCommand.cs b/Source/BookGen.Shellprog/GitAutoCompleteCommand.cs index 2134c669..09af83b2 100644 --- a/Source/BookGen.Shellprog/GitAutoCompleteCommand.cs +++ b/Source/BookGen.Shellprog/GitAutoCompleteCommand.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shellprog/GitCommandBase.cs b/Source/BookGen.Shellprog/GitCommandBase.cs index 6d9920be..0a103afa 100644 --- a/Source/BookGen.Shellprog/GitCommandBase.cs +++ b/Source/BookGen.Shellprog/GitCommandBase.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shellprog/Program.cs b/Source/BookGen.Shellprog/Program.cs index 3bc44416..7da6d496 100644 --- a/Source/BookGen.Shellprog/Program.cs +++ b/Source/BookGen.Shellprog/Program.cs @@ -30,7 +30,7 @@ using ServiceProvider provider = ioc.BuildServiceProvider(); -CommandRunner runner = new(provider, new ReflectionCommandHelpProvider(), logger, new CommandRunnerSettings +CommandRunner runner = new(provider, new CommandHelpProvider(), logger, new CommandRunnerSettings { UnknownCommandCodeAndMessage = (-1, "Unknown command"), BadParametersExitCode = 2, @@ -38,6 +38,7 @@ PlatformNotSupportedExitCode = 4, EnableUtf8Output = true, PrintHelpOnBadArgs = true, + ProgramMetaData = ProgramMetaData.FromExecutingAssembly() }); runner diff --git a/Source/BookGen.Shellprog/PromptCommand.cs b/Source/BookGen.Shellprog/PromptCommand.cs index af0f57e3..7677491d 100644 --- a/Source/BookGen.Shellprog/PromptCommand.cs +++ b/Source/BookGen.Shellprog/PromptCommand.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shellprog/RepoWeb.cs b/Source/BookGen.Shellprog/RepoWeb.cs index 1f284dfb..19cae8b2 100644 --- a/Source/BookGen.Shellprog/RepoWeb.cs +++ b/Source/BookGen.Shellprog/RepoWeb.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Shellprog/packages.lock.json b/Source/BookGen.Shellprog/packages.lock.json index 222d7479..402e9f33 100644 --- a/Source/BookGen.Shellprog/packages.lock.json +++ b/Source/BookGen.Shellprog/packages.lock.json @@ -4,46 +4,52 @@ "net10.0": { "Microsoft.Extensions.DependencyInjection": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "NijozhERJDIaJ4k5TSMy1jOi0cSC2HfkvRD/Sl+kGSSKgVbFnF4GxgtMN/MrzHB8D1JxIrD4xSer9Blh9v3axQ==", + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "ANyvsgkNBRvcJh2XLgn8veGmajf+8m0AbKK+HPWdRL1yraSNVVSmQhFntLtdz/C795jxqqup+k05cs/3jZQPOA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "g41l/30G3K4B/d/L8kjux0+30e27c8D0FVQ/PFCpbekgfDpj9mnDhieP67EqXWvl1EWNeZh2rpR4F5B/jcDOHA==" + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "z/2xXlFw2aLGjHyEm6E0tQ+In6VfzQzTrtArbQ2c0TQE16ZbyDCMGPvaUT9I0s8rgy9sRWlU2P9waW37qV04qA==" }, "Microsoft.Extensions.Logging": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "N7Gm9SjugYjmmnhwbBKC9DFqGqjfJvh6YfOJgtwh0AW0Xpok3dIVors1ik050XmUxKAgAc7nNngDIJyFb06K2g==", + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "Tf6z5HsL0VDYRTfvsoNrTGHGheCwkTsZBA2FFh5ATJUbkAwug+FFNISJK2gjpUNemlAOoWllAK52HOWCjto3EQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "10.0.9", - "Microsoft.Extensions.Logging.Abstractions": "10.0.9", - "Microsoft.Extensions.Options": "10.0.9" + "Microsoft.Extensions.DependencyInjection": "10.0.10", + "Microsoft.Extensions.Logging.Abstractions": "10.0.10", + "Microsoft.Extensions.Options": "10.0.10" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "9S/DFt4cohlMPpzIxjG6kk0L8MuN2vDm9pbMCulxtJzzk82oJHVLBd8vuQxaPskaYQwKqmFmbannf5eoChgjYg==", + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "zkFxGYUvdxAvIKTyXHrmW+Sux53D4SezD9dMyZ6hrwwzPQJNuwCRy1f5W7AvYTqacEGhWF2XderRQG1OvbV8og==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10" } }, + "Roslynator.Analyzers": { + "type": "Direct", + "requested": "[4.15.0, )", + "resolved": "4.15.0", + "contentHash": "E8fu71Y+vzhniJKy1K13Rzwm8Vjcj7CDFBpflqncKUZfc3SzuF0kMBiyMmgXNPkjWTdE3Z+30HlGgYjHONZY/Q==" + }, "Spectre.Console": { "type": "Direct", - "requested": "[0.57.0, )", - "resolved": "0.57.0", - "contentHash": "X5+4ZsUYTPlTs5PEoKRlxUuKOa1AkEoAGM34pUeJPjsBdtOnvnaVb31hqewhooomihl1i9fEBid1fz8XxZTacQ==", + "requested": "[0.57.2, )", + "resolved": "0.57.2", + "contentHash": "wzsB+P9i6F9G+cFOJxRotCiQWNSzZVAMxi8YMiHlGXXU1JLMXGp7zSSqBMmROZS2bzpK5jk+Saa7Evp8HwqhKg==", "dependencies": { - "Spectre.Console.Ansi": "0.57.0" + "Spectre.Console.Ansi": "0.57.2" } }, "Spectre.Console.Analyzer": { @@ -54,29 +60,29 @@ }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "hyNdX4c2UwkRkzb9byw0H2DQkRzwBM3mzY2sCM9egwzTyg8dvQJmp5noQHGEaaCORQrNK3DD2gREBsc2DlXS4A==", + "resolved": "10.0.10", + "contentHash": "srnhnk7nE8krBiIXp71LvBmKBtraBONWSRzdjJgRv1Ko9Mp8IVNqv4vIS9hGeVteBig8aQkva9ZG+sC+o5sVcA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.9", - "Microsoft.Extensions.Primitives": "10.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10", + "Microsoft.Extensions.Primitives": "10.0.10" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "fmEbAUFsaIKirgLt/lYhuFRBwhcSJN31jjHgCdbQxJiWOum6EdLjkbgGuukSP9z/a+9LibaxII/kF+GwOXgC4g==" + "resolved": "10.0.10", + "contentHash": "5wu/GrYVd8mG2DVUw3vFJzF+O336TyTGg/Kmcgw9bfwYhCoFiV5lR5QeEmKecJyrW4W54nMfD3p3589E8a7czQ==" }, "Spectre.Console.Ansi": { "type": "Transitive", - "resolved": "0.57.0", - "contentHash": "DGeYljyWAIHSeUAoUGqyzyOqS8WlsHKW+y4zOZ0OO61oqrhx90jHS+XG5Kpq/wWz6ovIs2VL/0CTKpUVa7tdWg==" + "resolved": "0.57.2", + "contentHash": "Y1+u73shwP+JYHmrkdN6bSt2kaGBEAafhi2nrecczbXdFRey+k7J/WF9YPfcB0rqivv2S8fmif6FSBq9m/AFvw==" }, "bookgen.cli": { "type": "Project", "dependencies": { "BookGen.Vfs": "[1.0.0, )", - "Microsoft.Extensions.DependencyInjection.Abstractions": "[10.0.9, )", - "Microsoft.Extensions.Logging.Abstractions": "[10.0.9, )" + "Microsoft.Extensions.DependencyInjection.Abstractions": "[10.0.10, )", + "Microsoft.Extensions.Logging.Abstractions": "[10.0.10, )" } }, "bookgen.contents": { @@ -85,8 +91,8 @@ "bookgen.shell.shared": { "type": "Project", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "[10.0.9, )", - "Spectre.Console": "[0.57.0, )", + "Microsoft.Extensions.Logging.Abstractions": "[10.0.10, )", + "Spectre.Console": "[0.57.2, )", "Webmaster442.WindowsTerminal": "[4.1.1, )" } }, diff --git a/Source/BookGen.Vfs/ApiClient.cs b/Source/BookGen.Vfs/ApiClient.cs index 90466a36..80bb30d2 100644 --- a/Source/BookGen.Vfs/ApiClient.cs +++ b/Source/BookGen.Vfs/ApiClient.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Vfs/BookGen.Vfs.csproj b/Source/BookGen.Vfs/BookGen.Vfs.csproj index e4ae09ae..0eef9a3a 100644 --- a/Source/BookGen.Vfs/BookGen.Vfs.csproj +++ b/Source/BookGen.Vfs/BookGen.Vfs.csproj @@ -10,4 +10,11 @@ True + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/Source/BookGen.Vfs/EmptyAssetSource.cs b/Source/BookGen.Vfs/EmptyAssetSource.cs index 4badaba0..cb792ec9 100644 --- a/Source/BookGen.Vfs/EmptyAssetSource.cs +++ b/Source/BookGen.Vfs/EmptyAssetSource.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Vfs/FileSystem.cs b/Source/BookGen.Vfs/FileSystem.cs index 6e455d0d..eb792e1b 100644 --- a/Source/BookGen.Vfs/FileSystem.cs +++ b/Source/BookGen.Vfs/FileSystem.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Vfs/FolderLock.cs b/Source/BookGen.Vfs/FolderLock.cs index c90800e9..c1c20045 100644 --- a/Source/BookGen.Vfs/FolderLock.cs +++ b/Source/BookGen.Vfs/FolderLock.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Vfs/IApiClient.cs b/Source/BookGen.Vfs/IApiClient.cs index 98aad8dc..76f80f19 100644 --- a/Source/BookGen.Vfs/IApiClient.cs +++ b/Source/BookGen.Vfs/IApiClient.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Vfs/IAssetSource.cs b/Source/BookGen.Vfs/IAssetSource.cs index 7b6c0601..0b2732bf 100644 --- a/Source/BookGen.Vfs/IAssetSource.cs +++ b/Source/BookGen.Vfs/IAssetSource.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Vfs/IReadOnlyFileSystem.cs b/Source/BookGen.Vfs/IReadOnlyFileSystem.cs index cb8748ec..51cf4509 100644 --- a/Source/BookGen.Vfs/IReadOnlyFileSystem.cs +++ b/Source/BookGen.Vfs/IReadOnlyFileSystem.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Vfs/IWritableFileSystem.cs b/Source/BookGen.Vfs/IWritableFileSystem.cs index 325f38f7..97b5ece8 100644 --- a/Source/BookGen.Vfs/IWritableFileSystem.cs +++ b/Source/BookGen.Vfs/IWritableFileSystem.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Vfs/IZipBuilder.cs b/Source/BookGen.Vfs/IZipBuilder.cs index 3fabc4e0..571499e4 100644 --- a/Source/BookGen.Vfs/IZipBuilder.cs +++ b/Source/BookGen.Vfs/IZipBuilder.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Vfs/Internals/CultureInfoJsonConverter.cs b/Source/BookGen.Vfs/Internals/CultureInfoJsonConverter.cs index b62c8e24..07b40bc7 100644 --- a/Source/BookGen.Vfs/Internals/CultureInfoJsonConverter.cs +++ b/Source/BookGen.Vfs/Internals/CultureInfoJsonConverter.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Vfs/Internals/DateOnlyJsonConverter.cs b/Source/BookGen.Vfs/Internals/DateOnlyJsonConverter.cs index e90413bd..9f74ae24 100644 --- a/Source/BookGen.Vfs/Internals/DateOnlyJsonConverter.cs +++ b/Source/BookGen.Vfs/Internals/DateOnlyJsonConverter.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Vfs/Internals/IsoDateTimeOffsetJsonConverter.cs b/Source/BookGen.Vfs/Internals/IsoDateTimeOffsetJsonConverter.cs index 17964773..5462ed19 100644 --- a/Source/BookGen.Vfs/Internals/IsoDateTimeOffsetJsonConverter.cs +++ b/Source/BookGen.Vfs/Internals/IsoDateTimeOffsetJsonConverter.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Vfs/Internals/JsonSchemaTransformer.cs b/Source/BookGen.Vfs/Internals/JsonSchemaTransformer.cs index ddb7c69c..144198b6 100644 --- a/Source/BookGen.Vfs/Internals/JsonSchemaTransformer.cs +++ b/Source/BookGen.Vfs/Internals/JsonSchemaTransformer.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Vfs/Internals/TimeOnlyJsonConverter.cs b/Source/BookGen.Vfs/Internals/TimeOnlyJsonConverter.cs index 62d25720..31da243b 100644 --- a/Source/BookGen.Vfs/Internals/TimeOnlyJsonConverter.cs +++ b/Source/BookGen.Vfs/Internals/TimeOnlyJsonConverter.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Vfs/ReadOnlyFileSystem.cs b/Source/BookGen.Vfs/ReadOnlyFileSystem.cs index c861e75a..2a506fee 100644 --- a/Source/BookGen.Vfs/ReadOnlyFileSystem.cs +++ b/Source/BookGen.Vfs/ReadOnlyFileSystem.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -12,6 +12,9 @@ public class ReadOnlyFileSystem : IReadOnlyFileSystem { protected string GetAndValidateFullNameInScope(string path) { + if (string.IsNullOrEmpty(path)) + return string.Empty; + string returnValue = string.IsNullOrEmpty(Scope) ? Path.GetFullPath(path) : Path.GetFullPath(path, Scope); diff --git a/Source/BookGen.Vfs/ZipAssetSoruce.cs b/Source/BookGen.Vfs/ZipAssetSoruce.cs index 094e7bfb..c759d5ea 100644 --- a/Source/BookGen.Vfs/ZipAssetSoruce.cs +++ b/Source/BookGen.Vfs/ZipAssetSoruce.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen.Vfs/ZipBuilder.cs b/Source/BookGen.Vfs/ZipBuilder.cs index b0cd48f3..bce43113 100644 --- a/Source/BookGen.Vfs/ZipBuilder.cs +++ b/Source/BookGen.Vfs/ZipBuilder.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen/BookGen.csproj b/Source/BookGen/BookGen.csproj index 0b160c65..2c58dda1 100644 --- a/Source/BookGen/BookGen.csproj +++ b/Source/BookGen/BookGen.csproj @@ -15,9 +15,15 @@ + + + + + + - + @@ -31,21 +37,26 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + - + + + diff --git a/Source/BookGen/BookGenArgumentBase.cs b/Source/BookGen/BookGenArgumentBase.cs index 0c902746..eeeaa479 100644 --- a/Source/BookGen/BookGenArgumentBase.cs +++ b/Source/BookGen/BookGenArgumentBase.cs @@ -3,21 +3,25 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using System.ComponentModel; + using BookGen.Cli; using BookGen.Cli.Annotations; -using BookGen.Commands; namespace BookGen; public class BookGenArgumentBase : ArgumentsBase, IVerbosablityToggle { - [Switch("v", "verbose")] + [Switch("v", "verbose", Required = false)] + [Description("Optional argument, turns on detailed logging. Usefull for locating issues")] public bool Verbose { get; set; } - [Switch("d", "dir")] + [Switch("d", "dir", Required = true)] + [Description("Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory.")] public string Directory { get; set; } - [Switch("co", "configoverlay")] + [Switch("co", "configoverlay", Required = false)] + [Description("Optional argument. Specifies a config overlay file. If specified, then the config overlay file will be loaded and merged with the default configuration.")] public string ConfigOverlay { get; set; } = string.Empty; public BookGenArgumentBase() @@ -25,7 +29,7 @@ public BookGenArgumentBase() Directory = Environment.CurrentDirectory; } - override public ValidationResult Validate(IValidationContext context) + public override ValidationResult Validate(IValidationContext context) { if (!context.FileSystem.DirectoryExists(Directory)) { diff --git a/Source/BookGen/BuildArguments.cs b/Source/BookGen/BuildArguments.cs index 41f09d79..83f47530 100644 --- a/Source/BookGen/BuildArguments.cs +++ b/Source/BookGen/BuildArguments.cs @@ -1,8 +1,10 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using System.ComponentModel; + using BookGen.Cli; using BookGen.Cli.Annotations; @@ -10,21 +12,34 @@ namespace BookGen; public sealed class BuildArguments : BookGenArgumentBase { - [Switch("o", "output")] + [Switch("o", "output", Required = true)] + [Description("Required argument. Specifies the output directory name.")] public string OutputDirectory { get; set; } = string.Empty; - [Switch("h", "host")] + [Switch("h", "host", Required = false)] + [Description("Optional argument. Specifies the host override for the book. If specified, then the book will be generated with the given host override.")] public string HostOverride { get; set; } = string.Empty; public override ValidationResult Validate(IValidationContext context) { ValidationResult originalResult = base.Validate(context); - if (originalResult.IsOk - && !string.IsNullOrEmpty(HostOverride) + + if (!originalResult.IsOk) + { + return originalResult; + } + + if (!string.IsNullOrEmpty(HostOverride) && !HostOverride.EndsWith('/')) { return ValidationResult.Error("Host override must end with a slash."); } + + if (string.IsNullOrWhiteSpace(OutputDirectory)) + { + return ValidationResult.Error("Output directory must not be empty."); + } + return originalResult; } } diff --git a/Source/BookGen/BuildCommandBase.cs b/Source/BookGen/BuildCommandBase.cs index 9f4c19cc..35d59a34 100644 --- a/Source/BookGen/BuildCommandBase.cs +++ b/Source/BookGen/BuildCommandBase.cs @@ -3,12 +3,12 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib; -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Pipeline; - using BookGen.Cli; +using BookGen.Cli.Annotations; using BookGen.Infrastructure.Loging; +using BookGen.Lib; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Pipeline; using BookGen.Vfs; using Microsoft.Extensions.Caching.Memory; @@ -16,6 +16,9 @@ namespace BookGen; +[ExitCode(ExitCodes.Success, "The Book was built successfully.")] +[ExitCode(ExitCodes.ConfigError, "The configuration was invalid.")] +[ExitCode(ExitCodes.GeneralError, "An error occurred during the build.")] internal abstract class BuildCommandBase : AsyncCommand { protected readonly IWritableFileSystem _soruce; @@ -42,7 +45,7 @@ public BuildCommandBase(IWritableFileSystem soruce, public abstract Pipeline GetPipeLine(); - public override async Task ExecuteAsync(BuildArguments arguments, IReadOnlyList context) + public override async Task ExecuteAsync(BuildArguments arguments, IReadOnlyList context, CancellationToken token) { if (_target.DirectoryExists(arguments.OutputDirectory)) { @@ -71,8 +74,7 @@ public override async Task ExecuteAsync(BuildArguments arguments, IReadOnly Pipeline pipeline = GetPipeLine(); - - bool result = await pipeline.ExecuteAsync(env, _logger, CancellationToken.None); + bool result = await pipeline.ExecuteAsync(env, _logger, token); return result ? ExitCodes.Success : ExitCodes.GeneralError; } diff --git a/Source/BookGen/Commands/AssemblyDocument.cs b/Source/BookGen/Commands/AssemblyDocument.cs deleted file mode 100644 index 35fc02d8..00000000 --- a/Source/BookGen/Commands/AssemblyDocument.cs +++ /dev/null @@ -1,54 +0,0 @@ -//----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor -// This code is licensed under MIT license (see LICENSE for details) -//----------------------------------------------------------------------------- - -using BookGen.Cli; -using BookGen.Cli.Annotations; - -using Microsoft.Extensions.Logging; - -using XmlDocMarkdown.Core; - -namespace BookGen.Commands; - -[CommandName("assembly-document")] -internal sealed class AssemblyDocument : Command -{ - private readonly ILogger _logger; - - public AssemblyDocument(ILogger logger) - { - _logger = logger; - } - - public class Arguments : InputOutputArguments - { - [Switch("d", "dry")] - public bool DryRun { get; set; } - - [Switch("n", "namespace-pages")] - public bool NamespacePages { get; set; } - } - - public override int Execute(Arguments arguments, IReadOnlyList context) - { - XmlDocMarkdownResult result = XmlDocMarkdownGenerator.Generate(arguments.InputFile, arguments.OutputFile, new XmlDocMarkdownSettings - { - IsDryRun = arguments.DryRun, - IncludeObsolete = true, - NamespacePages = arguments.NamespacePages, - VisibilityLevel = XmlDocVisibilityLevel.Public, - ShouldClean = true, - SkipUnbrowsable = true, - }); - - foreach (string message in result.Messages) - { - _logger.LogInformation(message); - } - - return ExitCodes.Success; - - } -} diff --git a/Source/BookGen/Commands/LinksCommand.cs b/Source/BookGen/Commands/Book/LinksCommand.cs similarity index 89% rename from Source/BookGen/Commands/LinksCommand.cs rename to Source/BookGen/Commands/Book/LinksCommand.cs index cdba7d8a..c3727f87 100644 --- a/Source/BookGen/Commands/LinksCommand.cs +++ b/Source/BookGen/Commands/Book/LinksCommand.cs @@ -4,28 +4,31 @@ //----------------------------------------------------------------------------- using System.Collections.Concurrent; +using System.ComponentModel; using System.Net; using System.Text.RegularExpressions; -using Bookgen.Lib; -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Domain.IO; - using BookGen.Cli; using BookGen.Cli.Annotations; using BookGen.Infrastructure.Loging; +using BookGen.Lib; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Domain.IO; using BookGen.Vfs; using Microsoft.Extensions.Logging; namespace BookGen.Commands; -[CommandName("links")] -internal sealed partial class LinksCommand : AsyncCommand +[CommandName("book links")] +[Description("Scans all markdown files in the current book and writes the links to a markdown file, named links.md")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +internal sealed partial class LinksCommand : AsyncCommand { - public sealed class LinkArguments : BookGenArgumentBase + internal sealed class Arguments : BookGenArgumentBase { - [Switch("vf", "verify")] + [Switch("vf", "verify", Required = false)] + [Description("Verify if the links are accessible and will print the result to the console. If not specified, the command will only write the links to the output file.")] public bool Verify { get; set; } } @@ -41,7 +44,7 @@ public LinksCommand(IWritableFileSystem soruce, IProgramPathResolver programPath _logger = logger; } - public override async Task ExecuteAsync(LinkArguments arguments, IReadOnlyList context) + public override async Task ExecuteAsync(Arguments arguments, IReadOnlyList context, CancellationToken token) { _soruce.Scope = arguments.Directory; diff --git a/Source/BookGen/Commands/MigrateCommand.cs b/Source/BookGen/Commands/Book/MigrateCommand.cs similarity index 67% rename from Source/BookGen/Commands/MigrateCommand.cs rename to Source/BookGen/Commands/Book/MigrateCommand.cs index 4c308781..86f843cc 100644 --- a/Source/BookGen/Commands/MigrateCommand.cs +++ b/Source/BookGen/Commands/Book/MigrateCommand.cs @@ -1,19 +1,23 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Confighandling.LegacyMigration; +using System.ComponentModel; using BookGen.Cli; using BookGen.Cli.Annotations; +using BookGen.Lib.Confighandling.LegacyMigration; using BookGen.Vfs; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Book; -[CommandName("migrate")] +[CommandName("book migrate")] +[Description("Migrate an old Bookgen book to the new format.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +[ExitCode(ExitCodes.GeneralError, "An error occurred during the conversion.")] internal sealed class MigrateCommand : AsyncCommand { private readonly IWritableFileSystem _writableFileSystem; @@ -25,7 +29,7 @@ public MigrateCommand(IWritableFileSystem writableFileSystem, ILogger logger) _logger = logger; } - public override async Task ExecuteAsync(BookGenArgumentBase arguments, IReadOnlyList context) + public override async Task ExecuteAsync(BookGenArgumentBase arguments, IReadOnlyList context, CancellationToken token) { _writableFileSystem.Scope = arguments.Directory; diff --git a/Source/BookGen/Commands/NewBookCommand.cs b/Source/BookGen/Commands/Book/NewBookCommand.cs similarity index 70% rename from Source/BookGen/Commands/NewBookCommand.cs rename to Source/BookGen/Commands/Book/NewBookCommand.cs index 36590100..c791e58c 100644 --- a/Source/BookGen/Commands/NewBookCommand.cs +++ b/Source/BookGen/Commands/Book/NewBookCommand.cs @@ -1,21 +1,25 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib; -using Bookgen.Lib.Domain.IO; -using Bookgen.Lib.Domain.IO.Configuration; +using System.ComponentModel; using BookGen.Cli; using BookGen.Cli.Annotations; +using BookGen.Lib; +using BookGen.Lib.Domain.IO; +using BookGen.Lib.Domain.IO.Configuration; using BookGen.Vfs; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Book; -[CommandName("newbook")] +[CommandName("book new")] +[Description("Creates a new book structure in the given folder.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +[ExitCode(ExitCodes.GeneralError, "The specified folder contains an existing book structure.")] internal sealed class NewBookCommand : AsyncCommand { private readonly ILogger _logger; @@ -27,7 +31,7 @@ public NewBookCommand(ILogger logger, IWritableFileSystem writableFileSystem) _fileSystem = writableFileSystem; } - public override async Task ExecuteAsync(BookGenArgumentBase arguments, IReadOnlyList context) + public override async Task ExecuteAsync(BookGenArgumentBase arguments, IReadOnlyList context, CancellationToken token) { _fileSystem.Scope = arguments.Directory; @@ -41,7 +45,7 @@ public override async Task ExecuteAsync(BookGenArgumentBase arguments, IRea await _fileSystem.SerializeAsync(FileNameConstants.ConfigFile, new Config(), writeSchema: true); _logger.LogInformation("Creating {toc}...", FileNameConstants.TableOfContents); - await _fileSystem.SerializeAsync(FileNameConstants.TableOfContents, new Bookgen.Lib.Domain.IO.TableOfContents(), writeSchema: true); + await _fileSystem.SerializeAsync(FileNameConstants.TableOfContents, new BookGen.Lib.Domain.IO.TableOfContents(), writeSchema: true); return ExitCodes.Success; } diff --git a/Source/BookGen/Commands/NewPageCommand.cs b/Source/BookGen/Commands/Book/NewPageCommand.cs similarity index 78% rename from Source/BookGen/Commands/NewPageCommand.cs rename to Source/BookGen/Commands/Book/NewPageCommand.cs index 3ed533ce..09d1cd51 100644 --- a/Source/BookGen/Commands/NewPageCommand.cs +++ b/Source/BookGen/Commands/Book/NewPageCommand.cs @@ -1,27 +1,31 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib; -using Bookgen.Lib.Domain.IO; +using System.ComponentModel; using BookGen.Cli; using BookGen.Cli.Annotations; +using BookGen.Lib; +using BookGen.Lib.Domain.IO; using BookGen.Vfs; using Microsoft.Extensions.Logging; using YamlDotNet.Serialization; -namespace BookGen.Commands; +namespace BookGen.Commands.Book; -[CommandName("newpage")] +[CommandName("book newpage")] +[Description("Creates a new markdown page.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] internal sealed class NewPageCommand : Command { - internal class Arguments : BookGenArgumentBase + internal sealed class Arguments : BookGenArgumentBase { - [Switch("-n", "name")] + [Switch("-n", "name", Required = true)] + [Description("File name. Specifies new file name")] public string Name { get; set; } = string.Empty; public override ValidationResult Validate(IValidationContext context) @@ -35,7 +39,7 @@ public override ValidationResult Validate(IValidationContext context) public override void ModifyAfterValidation() { if (!string.Equals(Path.GetExtension(Name), ".md", StringComparison.OrdinalIgnoreCase)) - Path.ChangeExtension(Name, ".md"); + Name = Path.ChangeExtension(Name, ".md"); } } diff --git a/Source/BookGen/Commands/StatsCommand.cs b/Source/BookGen/Commands/Book/StatsCommand.cs similarity index 83% rename from Source/BookGen/Commands/StatsCommand.cs rename to Source/BookGen/Commands/Book/StatsCommand.cs index f66eec3c..95f3813b 100644 --- a/Source/BookGen/Commands/StatsCommand.cs +++ b/Source/BookGen/Commands/Book/StatsCommand.cs @@ -3,23 +3,27 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib; -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Domain; +using System.ComponentModel; using BookGen.Cli; using BookGen.Cli.Annotations; using BookGen.Infrastructure.Loging; using BookGen.Infrastructure.Terminal; +using BookGen.Lib; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Domain; using BookGen.Vfs; using Microsoft.Extensions.Logging; using Spectre.Console; -namespace BookGen.Commands; +namespace BookGen.Commands.Book; -[CommandName("stats")] +[CommandName("book stats")] +[Description("Displays various statistics about the bookgen project.")] +[ExitCode(ExitCodes.ConfigError, "The configuration was invalid.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] internal sealed class StatsCommand : AsyncCommand { private readonly IWritableFileSystem _soruce; @@ -33,7 +37,7 @@ public StatsCommand(IWritableFileSystem soruce, IProgramPathResolver programPath _logger = logger; } - public override async Task ExecuteAsync(BookGenArgumentBase arguments, IReadOnlyList context) + public override async Task ExecuteAsync(BookGenArgumentBase arguments, IReadOnlyList context, CancellationToken token) { _soruce.Scope = arguments.Directory; using var env = new BookEnvironment(_soruce, _soruce, _programPathResolver); diff --git a/Source/BookGen/Commands/UpgradeCommand.cs b/Source/BookGen/Commands/Book/UpgradeCommand.cs similarity index 74% rename from Source/BookGen/Commands/UpgradeCommand.cs rename to Source/BookGen/Commands/Book/UpgradeCommand.cs index 93107ed5..1eaab3b8 100644 --- a/Source/BookGen/Commands/UpgradeCommand.cs +++ b/Source/BookGen/Commands/Book/UpgradeCommand.cs @@ -1,22 +1,25 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib; -using Bookgen.Lib.Confighandling; -using Bookgen.Lib.Domain.IO; -using Bookgen.Lib.Domain.IO.Configuration; +using System.ComponentModel; using BookGen.Cli; using BookGen.Cli.Annotations; +using BookGen.Lib; +using BookGen.Lib.Confighandling; +using BookGen.Lib.Domain.IO; +using BookGen.Lib.Domain.IO.Configuration; using BookGen.Vfs; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Book; -[CommandName("upgrade")] +[CommandName("book upgrade")] +[Description("Upgrades the bookgen project to the latest version. This command will upgrade the bookgen.json config file to the latest version, and will also upgrade the bookgen.toc.json file to the latest version.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] internal class UpgradeCommand : AsyncCommand { private readonly ILogger _logger; @@ -28,7 +31,7 @@ public UpgradeCommand(ILogger logger, IWritableFileSystem source) _source = source; } - public override async Task ExecuteAsync(BookGenArgumentBase arguments, IReadOnlyList context) + public override async Task ExecuteAsync(BookGenArgumentBase arguments, IReadOnlyList context, CancellationToken token) { var upgrader = new ConfigUpgrader(_logger); _source.Scope = arguments.Directory; diff --git a/Source/BookGen/Commands/ValidateCommand.cs b/Source/BookGen/Commands/Book/ValidateCommand.cs similarity index 78% rename from Source/BookGen/Commands/ValidateCommand.cs rename to Source/BookGen/Commands/Book/ValidateCommand.cs index 64025f7e..90963aa0 100644 --- a/Source/BookGen/Commands/ValidateCommand.cs +++ b/Source/BookGen/Commands/Book/ValidateCommand.cs @@ -3,18 +3,22 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib; -using Bookgen.Lib.AppSettings; +using System.ComponentModel; using BookGen.Cli; using BookGen.Cli.Annotations; +using BookGen.Lib; +using BookGen.Lib.AppSettings; using BookGen.Vfs; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Book; -[CommandName("validate")] +[CommandName("book validate")] +[Description("Validate the configuration files used by bookgen in the specified folder.")] +[ExitCode(ExitCodes.Success, "The configuration is valid.")] +[ExitCode(ExitCodes.ConfigError, "The configuration was invalid.")] internal sealed class ValidateCommand : AsyncCommand { private readonly IWritableFileSystem _writableFileSystem; @@ -28,7 +32,7 @@ public ValidateCommand(IWritableFileSystem writableFileSystem, IProgramPathResol _logger = logger; } - public override async Task ExecuteAsync(BookGenArgumentBase arguments, IReadOnlyList context) + public override async Task ExecuteAsync(BookGenArgumentBase arguments, IReadOnlyList context, CancellationToken token) { _writableFileSystem.Scope = arguments.Directory; diff --git a/Source/BookGen/Commands/BuildEpub.cs b/Source/BookGen/Commands/Build/BuildEpub.cs similarity index 82% rename from Source/BookGen/Commands/BuildEpub.cs rename to Source/BookGen/Commands/Build/BuildEpub.cs index adaeb5e5..b5bff721 100644 --- a/Source/BookGen/Commands/BuildEpub.cs +++ b/Source/BookGen/Commands/Build/BuildEpub.cs @@ -3,18 +3,20 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Pipeline; +using System.ComponentModel; using BookGen.Cli.Annotations; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Pipeline; using BookGen.Vfs; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Build; -[CommandName("buildepub")] +[CommandName("build epub")] +[Description("Build an epub3 file from the book.")] internal sealed class BuildEpub : BuildCommandBase { public BuildEpub(IWritableFileSystem soruce, diff --git a/Source/BookGen/Commands/BuildExportCommand.cs b/Source/BookGen/Commands/Build/BuildExportCommand.cs similarity index 81% rename from Source/BookGen/Commands/BuildExportCommand.cs rename to Source/BookGen/Commands/Build/BuildExportCommand.cs index 00d7bbaa..22a80d9d 100644 --- a/Source/BookGen/Commands/BuildExportCommand.cs +++ b/Source/BookGen/Commands/Build/BuildExportCommand.cs @@ -3,18 +3,20 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Pipeline; +using System.ComponentModel; using BookGen.Cli.Annotations; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Pipeline; using BookGen.Vfs; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Build; -[CommandName("buildexport")] +[CommandName("build export")] +[Description("Build a JSON file with schema for post processing of the book.")] internal sealed class BuildExportCommand : BuildCommandBase { public BuildExportCommand(IWritableFileSystem soruce, diff --git a/Source/BookGen/Commands/BuildFeedCommand.cs b/Source/BookGen/Commands/Build/BuildFeedCommand.cs similarity index 77% rename from Source/BookGen/Commands/BuildFeedCommand.cs rename to Source/BookGen/Commands/Build/BuildFeedCommand.cs index 22079a78..a87afd07 100644 --- a/Source/BookGen/Commands/BuildFeedCommand.cs +++ b/Source/BookGen/Commands/Build/BuildFeedCommand.cs @@ -3,18 +3,20 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Pipeline; +using System.ComponentModel; using BookGen.Cli.Annotations; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Pipeline; using BookGen.Vfs; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Build; -[CommandName("buildfeed")] +[CommandName("build feed")] +[Description("Build an RSS 2.0 and an Atom 1.0 feed from the book.")] internal sealed class BuildFeedCommand : BuildCommandBase { public BuildFeedCommand(IWritableFileSystem soruce, @@ -22,7 +24,7 @@ public BuildFeedCommand(IWritableFileSystem soruce, IProgramPathResolver programPathResolver, ILogger logger, IAssetSource assetSource, - IMemoryCache memoryCache) + IMemoryCache memoryCache) : base(soruce, target, programPathResolver, logger, assetSource, memoryCache) { } diff --git a/Source/BookGen/Commands/Build/BuildPlugin.cs b/Source/BookGen/Commands/Build/BuildPlugin.cs new file mode 100644 index 00000000..cfef40ec --- /dev/null +++ b/Source/BookGen/Commands/Build/BuildPlugin.cs @@ -0,0 +1,159 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.ComponentModel; + +using BookGen.Api.V1; +using BookGen.Cli; +using BookGen.Cli.Annotations; +using BookGen.Cli.OpenCli.Draft; +using BookGen.Infrastructure; +using BookGen.Infrastructure.Loging; +using BookGen.Infrastructure.Plugins; +using BookGen.Infrastructure.Plugins.V1; +using BookGen.Lib; +using BookGen.Lib.AppSettings; +using BookGen.Vfs; + +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging; + +namespace BookGen.Commands.Build; + +[CommandName("build plugin")] +[Description("Builds a book using a plugin nuget file.")] +[ExitCode(ExitCodes.Success, "The book was built successfully.")] +[ExitCode(ExitCodes.PluginError, "Failed to load the plugin.")] +[ExitCode(ExitCodes.ConfigError, "Failed to initialize the book environment.")] +internal sealed class BuildPlugin : AsyncCommand +{ + internal sealed class Arguments : ArgumentsBase, IVerbosablityToggle + { + [Argument(0, IsOptional = false)] + [Description("Required argument. Specifies the plugin zip file name.")] + public string PluginFile { get; set; } = string.Empty; + + [Switch("v", "verbose", Required = false)] + [Description("Optional argument, turns on detailed logging. Usefull for locating issues")] + public bool Verbose { get; set; } + + [Switch("d", "dir", Required = true)] + [Description("Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory.")] + public string Directory { get; set; } + + [Switch("o", "output", Required = true)] + [Description("Required argument. Specifies the output directory name.")] + public string OutputDirectory { get; set; } = string.Empty; + + [Switch("dev", "devmode", Required = false)] + [Description("Optional argument. Enables developer mode. This allows loading plugins with dll file")] + public bool IsDevMode { get; set; } + + public Arguments() + { + Directory = Environment.CurrentDirectory; + } + + public override ValidationResult Validate(IValidationContext context) + { + if (!context.FileSystem.DirectoryExists(Directory)) + { + return ValidationResult.Error($"Directory '{Directory}' does not exist."); + } + + if (string.IsNullOrWhiteSpace(OutputDirectory)) + { + return ValidationResult.Error("Output directory must not be empty."); + } + + if (string.IsNullOrWhiteSpace(PluginFile)) + { + return ValidationResult.Error("Plugin file must not be empty."); + } + + string extension = Path.GetExtension(PluginFile); + + if (IsDevMode) + { + + if (!extension.Equals(".dll", StringComparison.OrdinalIgnoreCase)) + { + return ValidationResult.Error($"Plugin file '{PluginFile}' is not a valid dll file."); + } + } + else if (!extension.Equals(".plugin", StringComparison.OrdinalIgnoreCase)) + { + return ValidationResult.Error($"Plugin file '{PluginFile}' is not a valid plugin package."); + } + + return ValidationResult.Ok(); + } + } + + private readonly IWritableFileSystem _soruce; + private readonly IWritableFileSystem _target; + private readonly IProgramPathResolver _programPathResolver; + private readonly ILogger _logger; + private readonly Vfs.IAssetSource _assetSource; + private readonly IMemoryCache _memoryCache; + private readonly IDynamicDocumentGenerator _dynamicDocumentGenerator; + + public BuildPlugin(IWritableFileSystem soruce, + IWritableFileSystem target, + IProgramPathResolver programPathResolver, + ILogger logger, + Vfs.IAssetSource assetSource, + IMemoryCache memoryCache, + IDynamicDocumentGenerator dynamicDocumentGenerator) + { + _soruce = soruce; + _target = target; + _programPathResolver = programPathResolver; + _logger = logger; + _assetSource = assetSource; + _memoryCache = memoryCache; + _dynamicDocumentGenerator = dynamicDocumentGenerator; + } + + public override async Task ExecuteAsync(Arguments arguments, IReadOnlyList context, CancellationToken token) + { + string? pluginAssemblyPath = PluginPathResolver.Resolve(Path.GetFullPath(arguments.Directory), arguments.PluginFile, arguments.IsDevMode); + + if (string.IsNullOrEmpty(pluginAssemblyPath)) + { + _logger.LogError("Plugin assembly path could not be resolved."); + return ExitCodes.PluginError; + } + + _soruce.Scope = arguments.Directory; + _target.Scope = arguments.OutputDirectory; + + using var env = new BookEnvironment(_soruce, _target, _programPathResolver, _assetSource); + EnvironmentStatus status = await env.Initialize(null); + + if (!status.IsOk) + { + _logger.EnvironmentStatus(status); + return ExitCodes.ConfigError; + } + + _logger.LogInformation("Creating plugin services..."); + + IBook book = Infrastructure.Plugins.V1.Book.CreateFrom(env, _logger); + IBookgenServices services = new BookGenServices(env, _memoryCache, _logger, _dynamicDocumentGenerator); + + _logger.LogInformation("Loading & running plugin from: {PluginAssemblyPath}", pluginAssemblyPath); + + bool result = await PluginRunner.RunPlugin(_logger, book, services, pluginAssemblyPath, arguments.IsDevMode, token); + + if (!result) + { + _logger.LogError("Plugin build failed"); + return ExitCodes.PluginError; + } + + return ExitCodes.Success; + } +} diff --git a/Source/BookGen/Commands/BuildPrintCommand.cs b/Source/BookGen/Commands/Build/BuildPrintCommand.cs similarity index 77% rename from Source/BookGen/Commands/BuildPrintCommand.cs rename to Source/BookGen/Commands/Build/BuildPrintCommand.cs index 922e1051..ec2baad5 100644 --- a/Source/BookGen/Commands/BuildPrintCommand.cs +++ b/Source/BookGen/Commands/Build/BuildPrintCommand.cs @@ -3,18 +3,20 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Pipeline; +using System.ComponentModel; using BookGen.Cli.Annotations; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Pipeline; using BookGen.Vfs; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Build; -[CommandName("buildprint")] +[CommandName("build print")] +[Description("Build a printable HTML & XHTML file from the book.")] internal sealed class BuildPrintCommand : BuildCommandBase { public BuildPrintCommand(IWritableFileSystem soruce, @@ -22,7 +24,7 @@ public BuildPrintCommand(IWritableFileSystem soruce, IProgramPathResolver programPathResolver, ILogger logger, IAssetSource assetSource, - IMemoryCache memoryCache) + IMemoryCache memoryCache) : base(soruce, target, programPathResolver, logger, assetSource, memoryCache) { } diff --git a/Source/BookGen/Commands/BuildWebCommand.cs b/Source/BookGen/Commands/Build/BuildWebCommand.cs similarity index 82% rename from Source/BookGen/Commands/BuildWebCommand.cs rename to Source/BookGen/Commands/Build/BuildWebCommand.cs index 80c5f832..8e82fc9e 100644 --- a/Source/BookGen/Commands/BuildWebCommand.cs +++ b/Source/BookGen/Commands/Build/BuildWebCommand.cs @@ -3,18 +3,20 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Pipeline; +using System.ComponentModel; using BookGen.Cli.Annotations; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Pipeline; using BookGen.Vfs; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Build; -[CommandName("buildweb")] +[CommandName("build web")] +[Description("Build a static website from the book.")] internal sealed class BuildWebCommand : BuildCommandBase { public BuildWebCommand(IWritableFileSystem soruce, diff --git a/Source/BookGen/Commands/BuildWordpressCommand.cs b/Source/BookGen/Commands/Build/BuildWordpressCommand.cs similarity index 78% rename from Source/BookGen/Commands/BuildWordpressCommand.cs rename to Source/BookGen/Commands/Build/BuildWordpressCommand.cs index 3ca7075e..a3ae8217 100644 --- a/Source/BookGen/Commands/BuildWordpressCommand.cs +++ b/Source/BookGen/Commands/Build/BuildWordpressCommand.cs @@ -3,18 +3,20 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Pipeline; +using System.ComponentModel; using BookGen.Cli.Annotations; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Pipeline; using BookGen.Vfs; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Build; -[CommandName("buildwp")] +[CommandName("build wp")] +[Description("Build a wordpress XML export file from the book.")] internal sealed class BuildWordpressCommand : BuildCommandBase { public BuildWordpressCommand(IWritableFileSystem soruce, @@ -22,7 +24,7 @@ public BuildWordpressCommand(IWritableFileSystem soruce, IProgramPathResolver programPathResolver, ILogger logger, IAssetSource assetSource, - IMemoryCache memoryCache) + IMemoryCache memoryCache) : base(soruce, target, programPathResolver, logger, assetSource, memoryCache) { } diff --git a/Source/BookGen/Commands/CommandsCommand.cs b/Source/BookGen/Commands/CommandsCommand.cs new file mode 100644 index 00000000..a2cb53e2 --- /dev/null +++ b/Source/BookGen/Commands/CommandsCommand.cs @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.ComponentModel; + +using BookGen.Cli; +using BookGen.Cli.Annotations; + +using Spectre.Console; + +namespace BookGen.Commands; + +[CommandName("commands")] +[Description("Lists all available commands in a hierarchical structure")] +[ExitCode(ExitCodes.Success, "The command executed successfully.")] +internal sealed class CommandsCommand(ICommandRunnerProxy commandRunnerProxy) : Command +{ + private Dictionary> BuildFullTree() + { + Dictionary> tree = new(); + + foreach (var cmd in commandRunnerProxy.CommandNames) + { + if (string.IsNullOrWhiteSpace(cmd)) + { + continue; + } + + string[] parts = cmd.Split(' ', StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 2) + { + string toAdd = parts[1]; + if (tree.TryGetValue(parts[0], out List? value)) + { + value.Add(toAdd); + } + else + { + tree[parts[0]] = [toAdd]; + } + } + else if (parts.Length == 1) + { + if (!tree.ContainsKey(parts[0])) + { + string toAdd = parts[0]; + tree[toAdd] = new List(); + } + } + else + { + throw new InvalidOperationException("Too many nestings"); + } + } + return tree; + } + + public override int Execute(IReadOnlyList context) + { + AnsiConsole.MarkupLine("[green]Available commands: [/]"); + AnsiConsole.WriteLine(); + Dictionary> treeData = BuildFullTree(); + Dictionary commandDescriptions = commandRunnerProxy.GetOpenCliDocs().Commands? + .DistinctBy(x => x.Name) + .ToDictionary(x => x.Name, x => x.Description) ?? new Dictionary(); + + var tree = new Spectre.Console.Tree("BookGen"); + foreach (var item in treeData) + { + string nodeName = $"[bold green]{item.Key.EscapeMarkup()}[/]\r\n[italic]{commandDescriptions.GetValueOrDefault(item.Key)?.EscapeMarkup()}[/]"; + TreeNode node = tree.AddNode(nodeName); + if (item.Value.Count > 0) + { + foreach (var subItem in item.Value) + { + string subNodeName = $"[bold green]{subItem.EscapeMarkup()}[/]\r\n[italic]{commandDescriptions.GetValueOrDefault($"{item.Key} {subItem}")?.EscapeMarkup()}[/]"; + node.AddNode(subNodeName); + } + } + } + AnsiConsole.Write(tree); + + return ExitCodes.Success; + } +} diff --git a/Source/BookGen/Commands/ConfigCommand.cs b/Source/BookGen/Commands/ConfigCommand.cs index af485b1d..b5b3214f 100644 --- a/Source/BookGen/Commands/ConfigCommand.cs +++ b/Source/BookGen/Commands/ConfigCommand.cs @@ -1,12 +1,13 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.AppSettings; +using System.ComponentModel; using BookGen.Cli; using BookGen.Cli.Annotations; +using BookGen.Lib.AppSettings; using Microsoft.Extensions.Logging; @@ -15,14 +16,19 @@ namespace BookGen.Commands; [CommandName("config")] -internal sealed class ConfigCommand : Command +[Description("Get or set the application settings. Without any arguments, the command will display the current settings.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +[ExitCode(ExitCodes.GeneralError, "An error occurred while executing the command.")] +internal sealed class ConfigCommand : Command { - public class ConfigCommandSettings : ArgumentsBase + internal sealed class Arguments : ArgumentsBase { [Argument(0, IsOptional = true)] + [Description("The setting to be configured.")] public string Setting { get; set; } = string.Empty; [Argument(1, IsOptional = true)] + [Description("The value to set for the specified setting.")] public string Value { get; set; } = string.Empty; } @@ -35,7 +41,7 @@ public ConfigCommand(ILogger logger, IAppSettings appSettings) _appSettings = appSettings; } - public override int Execute(ConfigCommandSettings arguments, IReadOnlyList context) + public override int Execute(Arguments arguments, IReadOnlyList context) { if (string.IsNullOrEmpty(arguments.Setting) && string.IsNullOrEmpty(arguments.Value)) diff --git a/Source/BookGen/Commands/Html2OpenXmlCommand.cs b/Source/BookGen/Commands/Convert/Html2OpenXmlCommand.cs similarity index 82% rename from Source/BookGen/Commands/Html2OpenXmlCommand.cs rename to Source/BookGen/Commands/Convert/Html2OpenXmlCommand.cs index 7b6a7004..1f26f83e 100644 --- a/Source/BookGen/Commands/Html2OpenXmlCommand.cs +++ b/Source/BookGen/Commands/Convert/Html2OpenXmlCommand.cs @@ -1,12 +1,13 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib; +using System.ComponentModel; using BookGen.Cli; using BookGen.Cli.Annotations; +using BookGen.Lib; using BookGen.Vfs; using DocumentFormat.OpenXml; @@ -18,24 +19,29 @@ using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Convert; -[CommandName("html2openxml")] -internal sealed class Html2OpenXmlCommand : AsyncCommand +[CommandName("convert html2openxml")] +[Description("Converts a HTML file to an Office Compatible DOCX file.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +[ExitCode(ExitCodes.GeneralError, "The command failed.")] +internal sealed class Html2OpenXmlCommand : AsyncCommand { private readonly ILogger _logger; private readonly IAssetSource _assetSource; private readonly IWritableFileSystem _fileSystem; - internal sealed class Html2OpenXmlArguments : ArgumentsBase + internal sealed class Arguments : ArgumentsBase { - [Switch("i", "input")] + [Switch("i", "input", Required = true)] + [Description("Specifies the input HTML file.")] public string InputFile { get; set; } - [Switch("o", "output")] + [Switch("o", "output", Required = true)] + [Description("Specifies the output DOCX file.")] public string OutputFile { get; set; } - public Html2OpenXmlArguments() + public Arguments() { InputFile = string.Empty; OutputFile = string.Empty; @@ -74,7 +80,7 @@ public Html2OpenXmlCommand(ILogger logger, IAssetSource assetSource, IWritableFi } - public override async Task ExecuteAsync(Html2OpenXmlArguments arguments, IReadOnlyList context) + public override async Task ExecuteAsync(Arguments arguments, IReadOnlyList context, CancellationToken token) { using Stream generated = _fileSystem.CreateWriteStream(arguments.OutputFile); using (Stream template = _assetSource.GetBinaryAssetStream(BundledAssets.WordTemplate)) diff --git a/Source/BookGen/Commands/Html2PdfCommand.cs b/Source/BookGen/Commands/Convert/Html2PdfCommand.cs similarity index 68% rename from Source/BookGen/Commands/Html2PdfCommand.cs rename to Source/BookGen/Commands/Convert/Html2PdfCommand.cs index 029c00af..d54d5045 100644 --- a/Source/BookGen/Commands/Html2PdfCommand.cs +++ b/Source/BookGen/Commands/Convert/Html2PdfCommand.cs @@ -1,28 +1,35 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using System.ComponentModel; + using BookGen.Cli; using BookGen.Cli.Annotations; using BookGen.Shell.Shared; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Convert; -[CommandName("html2pdf")] -internal sealed class Html2PdfCommand : AsyncCommand +[CommandName("convert html2pdf")] +[Description("Converts a HTML file to a png using edges or chromes headless mode. The tool will use chrome, if it's installed, otherwise it will use edge. This command is only supported on Windows OS.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +[ExitCode(ExitCodes.GeneralError, "The command failed.")] +internal sealed class Html2PdfCommand : AsyncCommand { - internal sealed class Html2PdfArguments : ArgumentsBase + internal sealed class Arguments : ArgumentsBase { - [Switch("i", "input")] + [Switch("i", "input", Required = true)] + [Description("Specifies the input HTML file.")] public string InputFile { get; set; } - [Switch("o", "output")] + [Switch("o", "output", Required = true)] + [Description("Specifies the output PDF file.")] public string OutputFile { get; set; } - public Html2PdfArguments() + public Arguments() { InputFile = string.Empty; OutputFile = string.Empty; @@ -62,7 +69,7 @@ public Html2PdfCommand(ILogger log) _browser = new BrowserInteract(log); } - public override async Task ExecuteAsync(Html2PdfArguments arguments, IReadOnlyList context) + public override async Task ExecuteAsync(Arguments arguments, IReadOnlyList context, CancellationToken token) { bool result = await _browser.Html2Pdf(arguments.InputFile, arguments.OutputFile); diff --git a/Source/BookGen/Commands/Html2PngCommand.cs b/Source/BookGen/Commands/Convert/Html2PngCommand.cs similarity index 66% rename from Source/BookGen/Commands/Html2PngCommand.cs rename to Source/BookGen/Commands/Convert/Html2PngCommand.cs index 6640aa6e..d35fffd5 100644 --- a/Source/BookGen/Commands/Html2PngCommand.cs +++ b/Source/BookGen/Commands/Convert/Html2PngCommand.cs @@ -1,34 +1,43 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using System.ComponentModel; + using BookGen.Cli; using BookGen.Cli.Annotations; using BookGen.Shell.Shared; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Convert; -[CommandName("html2png")] -internal sealed class Html2PngCommand : AsyncCommand +[CommandName("convert html2png")] +[Description("Converts a HTML file to a png using edges or chromes headless mode. The tool will use chrome, if it's installed, otherwise it will use edge. This command is only supported on Windows OS.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +[ExitCode(ExitCodes.GeneralError, "The command failed.")] +internal sealed class Html2PngCommand : AsyncCommand { - internal sealed class Html2PngArguments : ArgumentsBase + internal sealed class Arguments : ArgumentsBase { - [Switch("i", "input")] + [Switch("i", "input", Required = true)] + [Description("Specifies the input HTML file.")] public string InputFile { get; set; } - [Switch("o", "output")] + [Switch("o", "output", Required = true)] + [Description("Specifies the output PNG file.")] public string OutputFile { get; set; } - [Switch("w", "width")] + [Switch("w", "width", Required = false)] + [Description("Specifies the width of the output PNG. If not given, the default is 1920.")] public int Width { get; set; } - [Switch("h", "height")] + [Switch("h", "height", Required = false)] + [Description("Specifies the height of the output PNG. If not given, the default is 1080.")] public int Height { get; set; } - public Html2PngArguments() + public Arguments() { InputFile = string.Empty; OutputFile = string.Empty; @@ -76,7 +85,7 @@ public Html2PngCommand(ILogger log) _browser = new BrowserInteract(log); } - public override async Task ExecuteAsync(Html2PngArguments arguments, IReadOnlyList context) + public override async Task ExecuteAsync(Arguments arguments, IReadOnlyList context, CancellationToken token) { bool result = await _browser.Html2Png(arguments.InputFile, arguments.OutputFile, diff --git a/Source/BookGen/Commands/ImgConvert.cs b/Source/BookGen/Commands/Convert/ImgConvert.cs similarity index 73% rename from Source/BookGen/Commands/ImgConvert.cs rename to Source/BookGen/Commands/Convert/ImgConvert.cs index 2b81e045..0a871155 100644 --- a/Source/BookGen/Commands/ImgConvert.cs +++ b/Source/BookGen/Commands/Convert/ImgConvert.cs @@ -3,47 +3,55 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using System.ComponentModel; using System.Globalization; -using Bookgen.Lib.Domain; -using Bookgen.Lib.Rendering.Images; - using BookGen.Cli; using BookGen.Cli.Annotations; +using BookGen.Lib.Domain; +using BookGen.Lib.Rendering.Images; using BookGen.Vfs; -namespace BookGen.Commands; +namespace BookGen.Commands.Convert; -[CommandName("imgconvert")] -internal sealed class ImgConvert : Command +[CommandName("convert images")] +[Description("Converts an image file to a different format. The tool supports png, jpeg, webp and svg formats.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +[ExitCode(ExitCodes.ArgumentsError, "Invalid arguments provided.")] +internal sealed class ImgConvert : Command { private readonly IWritableFileSystem _fileSystem; - public enum ImageFormat + internal enum ImageFormat { Jpg, Png, Webp } - public class ImgConvertArgs : ArgumentsBase + internal sealed class Arguments : ArgumentsBase { - [Switch("i", "input")] + [Switch("i", "input", Required = true)] + [Description("Specifies the input image file or directory.")] public string Input { get; set; } - [Switch("o", "output")] + [Switch("o", "output", Required = true)] + [Description("Specifies the output image file or directory.")] public string Output { get; set; } - [Switch("f", "format")] + [Switch("f", "format", Required = true)] + [Description("Specifies the output image format (jpg, png, webp).")] public string Format { get; set; } - [Switch("q", "quality")] + [Switch("q", "quality", Required = false)] + [Description("Specifies the quality of the output image (0-100). If not given, the default is 90.")] public int Quality { get; set; } = 90; - [Switch("r", "resolution")] + [Switch("r", "resolution", Required = false)] + [Description("Specifies the maximum resolution of the output image (WidthxHeight). If not given, the default is the size of the input image")] public string Resolution { get; set; } - public ImgConvertArgs() + public Arguments() { Input = string.Empty; Output = string.Empty; @@ -88,7 +96,7 @@ public ImgConvert(IWritableFileSystem fileSystem) _fileSystem = fileSystem; } - public override int Execute(ImgConvertArgs arguments, IReadOnlyList context) + public override int Execute(Arguments arguments, IReadOnlyList context) { HashSet supportedExtensions = new(StringComparer.InvariantCultureIgnoreCase) { @@ -101,7 +109,7 @@ public override int Execute(ImgConvertArgs arguments, IReadOnlyList cont Height = int.MaxValue }; - if (!string.IsNullOrEmpty(arguments.Resolution) + if (!string.IsNullOrEmpty(arguments.Resolution) && !Resolution.TryParse(arguments.Resolution, CultureInfo.InvariantCulture, out maxResolution)) { Console.Error.WriteLine($"Invalid resolution format: '{arguments.Resolution}'. Expected format is 'WidthxHeight'."); diff --git a/Source/BookGen/Commands/Math2SvgCommand.cs b/Source/BookGen/Commands/Convert/Math2SvgCommand.cs similarity index 67% rename from Source/BookGen/Commands/Math2SvgCommand.cs rename to Source/BookGen/Commands/Convert/Math2SvgCommand.cs index 65765efd..0651accd 100644 --- a/Source/BookGen/Commands/Math2SvgCommand.cs +++ b/Source/BookGen/Commands/Convert/Math2SvgCommand.cs @@ -1,37 +1,43 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Rendering.Images; -using Bookgen.Lib.Rendering.Markdown.RenderInterop; +using System.ComponentModel; using BookGen.Cli; using BookGen.Cli.Annotations; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Markdown.RenderInterop; using BookGen.Vfs; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Convert; -[CommandName("math2svg")] -internal sealed class Math2SvgCommand : AsyncCommand +[CommandName("convert math2svg")] +[Description("Renders a single markdown file containing Tex formulas to svg files.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +internal sealed class Math2SvgCommand : AsyncCommand { private readonly ILogger _log; private readonly IWritableFileSystem _fileSystem; private readonly IProgramPathResolver _programPathResolver; private readonly IAssetSource _assets; - public sealed class Math2SvgArguments : ArgumentsBase + internal sealed class Arguments : ArgumentsBase { - [Switch("f", "formula")] + [Switch("f", "formula", Required = true)] + [Description("The formula to render. It must be a valid Tex formula.")] public string Formula { get; set; } = string.Empty; - [Switch("o", "output")] + [Switch("o", "output", Required = true)] + [Description("The output file where the rendered SVG will be saved.")] public string OutputFile { get; set; } = string.Empty; - [Switch("s", "scale")] + [Switch("s", "scale", Required = false)] + [Description("The scale factor for the rendered SVG. Must be between 0.1 and 40.")] public double Scale { get; set; } = 1.0; public override ValidationResult Validate(IValidationContext context) @@ -64,7 +70,7 @@ public Math2SvgCommand(ILogger log, IWritableFileSystem fileSystem, IProgramPath _assets = assetSource; } - public override async Task ExecuteAsync(Math2SvgArguments arguments, IReadOnlyList context) + public override async Task ExecuteAsync(Arguments arguments, IReadOnlyList context, CancellationToken token) { using var render = IRenderInterop.CreateForSvg(_assets, _programPathResolver); diff --git a/Source/BookGen/Commands/Md2HtmlCommand.cs b/Source/BookGen/Commands/Convert/Md2HtmlCommand.cs similarity index 65% rename from Source/BookGen/Commands/Md2HtmlCommand.cs rename to Source/BookGen/Commands/Convert/Md2HtmlCommand.cs index 7caf3f17..38d94791 100644 --- a/Source/BookGen/Commands/Md2HtmlCommand.cs +++ b/Source/BookGen/Commands/Convert/Md2HtmlCommand.cs @@ -3,56 +3,70 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using System.ComponentModel; using System.Text; -using Bookgen.Lib; -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Domain.IO.Configuration; -using Bookgen.Lib.Rendering.Images; -using Bookgen.Lib.Rendering.Markdown; -using Bookgen.Lib.Rendering.Markdown.RenderInterop; -using Bookgen.Lib.Rendering.Templates; -using Bookgen.Lib.Templates; - using BookGen.Cli; using BookGen.Cli.Annotations; +using BookGen.Lib; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Markdown; +using BookGen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Rendering.Templates; using BookGen.Vfs; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +using Spectre.Console; + +namespace BookGen.Commands.Convert; -[CommandName("md2html")] -internal sealed class Md2HtmlCommand : Command +[CommandName("convert md2html")] +[Description("Renders a single markdown file to an HTML file.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +internal sealed class Md2HtmlCommand : Command { - internal sealed class Md2HtmlArguments : ArgumentsBase + internal sealed class Arguments : ArgumentsBase { - [Switch("i", "input")] + [Switch("i", "input", Required = true)] + [Description("Input markdown file path. Multiple files can be set with multiple `-i` arguments")] public string[] InputFiles { get; set; } - [Switch("o", "output")] + [Switch("o", "output", Required = true)] + [Description("Output html file path. If file name is `-`, outputs to console.")] public string OutputFile { get; set; } - [Switch("tf", "template")] + [Switch("tf", "template", Required = false)] + [Description("If not specified, default template is used. If custom file provided, then the file must contain the folloing tags: ``, ``")] public string Template { get; set; } - [Switch("ns", "no-syntax")] + [Switch("ns", "no-syntax", Required = false)] + [Description("Disables syntax highlighting in the output HTML.")] public bool NoSyntax { get; set; } - [Switch("ne", "no-embed")] + [Switch("ne", "no-embed", Required = false)] + [Description("Disables embedding of assets in the output HTML.")] public bool NoEmbed { get; set; } - [Switch("r", "raw")] + [Switch("r", "raw", Required = false)] + [Description("Disables full html generation, only outputs the html produced by the markdown formatting.")] public bool RawHtml { get; set; } - [Switch("s", "svg")] + [Switch("s", "svg", Required = false)] + [Description("When enabled SVG files will be embedded in resulting html, instead of being rendered to webp.")] public bool SvgPassthrough { get; set; } - [Switch("t", "title")] + [Switch("t", "title", Required = false)] + [Description("Specifies the rendered HTML page title. Only has affect, when `-r` or `--raw` is not specified.")] public string Title { get; set; } + [Switch("lt", "list-templates", Required = false)] + [Description("When specified, lists all available built-in templates to used with the `-tf` or `--template` option and exits")] + public bool ListTemplates { get; set; } - public Md2HtmlArguments() + public Arguments() { Template = string.Empty; Title = "Markdown document"; @@ -60,9 +74,9 @@ public Md2HtmlArguments() OutputFile = string.Empty; } - public override ValidationResult Validate(IValidationContext context) + public override Cli.ValidationResult Validate(IValidationContext context) { - ValidationResult result = new(); + Cli.ValidationResult result = new(); if (!string.IsNullOrEmpty(Template) && context.IsValidTemplateFile(Template)) @@ -109,8 +123,13 @@ public Md2HtmlCommand(ILogger log, IFileSystemFactory fileSystemFactory, IProgra _templateEngine = new TemplateEngine(log, assetSource); } - public override int Execute(Md2HtmlArguments arguments, IReadOnlyList context) + public override int Execute(Arguments arguments, IReadOnlyList context) { + if (arguments.ListTemplates) + { + return ListTemplatesAndExit(); + } + IEnumerable inputFolders = arguments.InputFiles.Select(i => Path.GetDirectoryName(i)); IReadOnlyFileSystem inputFilesScope = _fileSystemFactory.CreateMultiReadScopeFileSystem(inputFolders!); @@ -177,6 +196,16 @@ public override int Execute(Md2HtmlArguments arguments, IReadOnlyList co return ExitCodes.Success; } + private static int ListTemplatesAndExit() + { + AnsiConsole.WriteLine("Available built-in templates:"); + foreach (var template in BundledAssets.Md2HtmlTemplates) + { + AnsiConsole.WriteLine($"- {template}"); + } + return ExitCodes.Success; + } + private bool ValidateTemplate(string pageTemplate) { bool returnValue = true; @@ -198,6 +227,6 @@ private bool ValidateTemplate(string pageTemplate) private static void WriteToStdout(string rendered) { Console.OutputEncoding = Encoding.UTF8; - Spectre.Console.AnsiConsole.WriteLine(rendered); + AnsiConsole.WriteLine(rendered); } } diff --git a/Source/BookGen/Commands/Md2TerminalCommand.cs b/Source/BookGen/Commands/Convert/Md2TerminalCommand.cs similarity index 74% rename from Source/BookGen/Commands/Md2TerminalCommand.cs rename to Source/BookGen/Commands/Convert/Md2TerminalCommand.cs index 66bc7a72..4cc75d93 100644 --- a/Source/BookGen/Commands/Md2TerminalCommand.cs +++ b/Source/BookGen/Commands/Convert/Md2TerminalCommand.cs @@ -1,22 +1,24 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using System.ComponentModel; using System.Text; -using Bookgen.Lib.Domain.IO.Configuration; -using Bookgen.Lib.Rendering.Markdown; - using BookGen.Cli; using BookGen.Cli.Annotations; +using BookGen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Rendering.Markdown; using BookGen.Vfs; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Convert; -[CommandName("md2terminal")] +[CommandName("convert md2terminal")] +[Description("Converts a markdown file to terminal formatted text.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] internal sealed class Md2TerminalCommand : Command { private readonly ILogger _log; @@ -24,10 +26,12 @@ internal sealed class Md2TerminalCommand : Command internal sealed class Arguments : ArgumentsBase { - [Switch("i", "input")] + [Switch("i", "input", Required = true)] + [Description("Input markdown file path. Multiple files can be set with multiple `-i` arguments")] public string[] InputFiles { get; set; } - [Switch("o", "output")] + [Switch("o", "output", Required = true)] + [Description("Output html file path. If file name is `-`, outputs to console.")] public string OutputFile { get; set; } public Arguments() @@ -70,20 +74,9 @@ private static void WriteToStdout(string rendered) public override int Execute(Arguments arguments, IReadOnlyList context) { - (string md, _) = _fileSystem.ReadInputFiles(arguments.InputFiles); - - using var settings = new MarkdownRenderSettings(null!) - { - DeleteFirstH1 = false, - AutoEmbedSupportedLinks = false, - CssClasses = new CssClasses(), - HostUrl = string.Empty, - RenderInterop = null!, - }; - - using var markdonwConverter = new MarkdownConverter(settings); + (string md, _) = _fileSystem.ReadInputFiles(arguments.InputFiles);; - var rendered = markdonwConverter.RenderMarkdownToTerminal(md); + var rendered = MarkdownConverter.RenderMarkdownToTerminal(md); if (arguments.OutputFile == "-") WriteToStdout(rendered); diff --git a/Source/BookGen/Commands/QrCodeCommand.cs b/Source/BookGen/Commands/Convert/QrCodeCommand.cs similarity index 65% rename from Source/BookGen/Commands/QrCodeCommand.cs rename to Source/BookGen/Commands/Convert/QrCodeCommand.cs index 4e346f79..67d2a9f6 100644 --- a/Source/BookGen/Commands/QrCodeCommand.cs +++ b/Source/BookGen/Commands/Convert/QrCodeCommand.cs @@ -1,35 +1,37 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using System.Text.RegularExpressions; - -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Domain.IO.Legacy; -using Bookgen.Lib.Rendering.Images; -using Bookgen.Lib.Rendering.Markdown.RenderInterop; +using System.ComponentModel; using BookGen.Cli; using BookGen.Cli.Annotations; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Markdown.RenderInterop; using BookGen.Vfs; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Convert; -[CommandName("qrcode")] -internal sealed class QrCodeCommand : AsyncCommand +[CommandName("convert qrcode")] +[Description("Renders an url into a SVG QRCode image.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +internal sealed class QrCodeCommand : AsyncCommand { - internal sealed class QrCodeArguments : ArgumentsBase + internal sealed class Arguments : ArgumentsBase { - [Switch("o", "output")] + [Switch("o", "output", Required = true)] + [Description("Output file.")] public string Output { get; set; } - [Switch("d", "data")] + [Switch("d", "data", Required = true)] + [Description("Url data to encode. Minimum 1 byte, Maximum 900 bytes")] public string Data { get; set; } - public QrCodeArguments() + public Arguments() { Output = string.Empty; Data = string.Empty; @@ -47,6 +49,11 @@ public override ValidationResult Validate(IValidationContext context) return result; } + + public override void ModifyAfterValidation() + { + Output = Path.ChangeExtension(Output, ".svg"); + } } @@ -63,7 +70,7 @@ public QrCodeCommand(ILogger log, IWritableFileSystem fileSystem, IProgramPathRe _assetSource = assetSource; } - public override async Task ExecuteAsync(QrCodeArguments arguments, IReadOnlyList context) + public override async Task ExecuteAsync(Arguments arguments, IReadOnlyList context, CancellationToken token) { using var render = IRenderInterop.CreateForSvg(_assetSource, _programPathResolver); diff --git a/Source/BookGen/Commands/DefaultCommand.cs b/Source/BookGen/Commands/DefaultCommand.cs new file mode 100644 index 00000000..be572eca --- /dev/null +++ b/Source/BookGen/Commands/DefaultCommand.cs @@ -0,0 +1,27 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.ComponentModel; + +using BookGen.Cli; +using BookGen.Cli.Annotations; +using BookGen.Infrastructure; + +using Spectre.Console; + +namespace BookGen.Commands; + +[CommandName("default")] +[Description("The default command that displays the default help message.")] +[ExitCode(ExitCodes.Success, "The command executed successfully.")] +internal sealed class DefaultCommand : Command +{ + public override int Execute(IReadOnlyList context) + { + string text = Embedded.ReadEmbeddedResource("BookGen.Resources.default.txt"); + AnsiConsole.MarkupLine(text); + return ExitCodes.Success; + } +} diff --git a/Source/BookGen/Commands/Docs/ChangelogCommand.cs b/Source/BookGen/Commands/Docs/ChangelogCommand.cs new file mode 100644 index 00000000..2da03ece --- /dev/null +++ b/Source/BookGen/Commands/Docs/ChangelogCommand.cs @@ -0,0 +1,19 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.ComponentModel; + +using BookGen.Cli.Annotations; +using BookGen.Infrastructure; + +namespace BookGen.Commands.Docs; + +[CommandName("document changelog")] +[Description("Displays the changelog on the terminal. Output can be redirected to a file.")] +internal sealed class ChangelogCommand : DocumentCommandBase +{ + protected override string GetDocumentContent() + => Embedded.ReadEmbeddedResource("BookGen.Resources.changelog.md"); +} diff --git a/Source/BookGen/Commands/Docs/CommandsCommand.cs b/Source/BookGen/Commands/Docs/CommandsCommand.cs new file mode 100644 index 00000000..31fb38cc --- /dev/null +++ b/Source/BookGen/Commands/Docs/CommandsCommand.cs @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.ComponentModel; + +using BookGen.Cli.Annotations; +using BookGen.Infrastructure; + +namespace BookGen.Commands.Docs; + +[CommandName("document commands")] +[Description("Displays commands reference on the terminal. Output can be redirected to a file.")] +internal sealed class CommandsCommand : DocumentCommandBase +{ + private readonly IDynamicDocumentGenerator _dynamicDocumentGenerator; + + public CommandsCommand(IDynamicDocumentGenerator dynamicDocumentGenerator) + { + _dynamicDocumentGenerator = dynamicDocumentGenerator; + } + + protected override string GetDocumentContent() + => _dynamicDocumentGenerator.GenerateCommandsDocument(); +} diff --git a/Source/BookGen/Commands/Docs/DocumentCommandBase.cs b/Source/BookGen/Commands/Docs/DocumentCommandBase.cs new file mode 100644 index 00000000..06c09822 --- /dev/null +++ b/Source/BookGen/Commands/Docs/DocumentCommandBase.cs @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using BookGen.Cli.Annotations; +using BookGen.Infrastructure; + +using Spectre.Console; + +namespace BookGen.Commands.Docs; + +[ExitCode(ExitCodes.Success, "The command executed successfully.")] +internal abstract class DocumentCommandBase : Cli.Command +{ + protected abstract string GetDocumentContent(); + + public override int Execute(IReadOnlyList context) + { + + if (Console.IsOutputRedirected) + { + AnsiConsole.WriteLine(GetDocumentContent()); + return ExitCodes.Success; + } + + HelpRenderer.RenderHelp(GetDocumentContent()); + + return ExitCodes.Success; + } +} diff --git a/Source/BookGen/Commands/Docs/ManualCommand.cs b/Source/BookGen/Commands/Docs/ManualCommand.cs new file mode 100644 index 00000000..f5a16b21 --- /dev/null +++ b/Source/BookGen/Commands/Docs/ManualCommand.cs @@ -0,0 +1,19 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.ComponentModel; + +using BookGen.Cli.Annotations; +using BookGen.Infrastructure; + +namespace BookGen.Commands.Docs; + +[CommandName("document manual")] +[Description("Displays the manual on the terminal. Output can be redirected to a file.")] +internal sealed class ManualCommand : DocumentCommandBase +{ + protected override string GetDocumentContent() + => Embedded.ReadEmbeddedResource("BookGen.Resources.manual.md"); +} diff --git a/Source/BookGen/Commands/Docs/SchemasCommand.cs b/Source/BookGen/Commands/Docs/SchemasCommand.cs new file mode 100644 index 00000000..1676e6c4 --- /dev/null +++ b/Source/BookGen/Commands/Docs/SchemasCommand.cs @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.ComponentModel; + +using BookGen.Cli.Annotations; +using BookGen.Infrastructure; + +namespace BookGen.Commands.Docs; + +[CommandName("document schemas")] +[Description("Outputs the JSON schemas used by Bookgen on the terminal. Output can be redirected to a file.")] +internal class SchemasCommand : DocumentCommandBase +{ + private readonly IDynamicDocumentGenerator _dynamicDocumentGenerator; + + public SchemasCommand(IDynamicDocumentGenerator dynamicDocumentGenerator) + { + _dynamicDocumentGenerator = dynamicDocumentGenerator; + } + + protected override string GetDocumentContent() + => _dynamicDocumentGenerator.GenerateSchemasDocument(); +} diff --git a/Source/BookGen/Commands/EditCommand.cs b/Source/BookGen/Commands/EditCommand.cs deleted file mode 100644 index 35406ff4..00000000 --- a/Source/BookGen/Commands/EditCommand.cs +++ /dev/null @@ -1,84 +0,0 @@ -//----------------------------------------------------------------------------- -// (c) 2019-2026 Ruzsinszki Gábor -// This code is licensed under MIT license (see LICENSE for details) -//----------------------------------------------------------------------------- - -using System.Diagnostics; - -using Bookgen.Lib.AppSettings; - -using BookGen.Cli; -using BookGen.Cli.Annotations; - -using Microsoft.Extensions.Logging; - -namespace BookGen.Commands; - -[CommandName("edit")] -internal sealed class EditCommand : Command -{ - internal static class EditorHelper - { - private static readonly HashSet SupportedFileTypes = - [ - ".txt", ".md", ".js", - ".json", ".yaml", ".html", - ".htm", ".css", ".cmd", - ".ps", ".sh", ".css", - ".php", ".py", ".xml", - ]; - - public static bool IsSupportedFile(string file) - { - string? ext = Path.GetExtension(file).ToLower(); - return SupportedFileTypes.Contains(ext); - } - } - - private readonly ILogger _log; - private readonly IReadOnlyAppSettings _appSettings; - - public EditCommand(ILogger log, IReadOnlyAppSettings appSettings) - { - _log = log; - _appSettings = appSettings; - } - - public override int Execute(IReadOnlyList context) - { - if (context.Count != 1) - { - _log.LogWarning("No file name given"); - return ExitCodes.ArgumentsError; - } - - if (string.IsNullOrEmpty(_appSettings.Get(x => x.Editor))) - { - _log.LogWarning("No Editor configured"); - return ExitCodes.ArgumentsError; - } - - string? file = Path.GetFullPath(context[0]); - - if (!EditorHelper.IsSupportedFile(file)) - { - _log.LogWarning("Unsupported file type"); - return ExitCodes.ArgumentsError; - } - - try - { - using var p = new Process(); - p.StartInfo.FileName = _appSettings.Get(x => x.Editor); - p.StartInfo.Arguments = $"\"{file}\""; - p.StartInfo.UseShellExecute = false; - p.Start(); - return ExitCodes.Success; - } - catch (Exception ex) - { - _log.LogCritical(ex, "Critical Error: {ex}", ex.Message); - return ExitCodes.GeneralError; - } - } -} diff --git a/Source/BookGen/Commands/Folder/VsCodeCommand.cs b/Source/BookGen/Commands/Folder/VsCodeCommand.cs new file mode 100644 index 00000000..baf9ed8e --- /dev/null +++ b/Source/BookGen/Commands/Folder/VsCodeCommand.cs @@ -0,0 +1,224 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.ComponentModel; +using System.Globalization; +using System.Reflection; + +using BookGen.Cli; +using BookGen.Cli.Annotations; +using BookGen.Commands.Book; +using BookGen.Commands.Build; +using BookGen.Commands.Convert; +using BookGen.Lib.Domain.VsCode; +using BookGen.Vfs; + +namespace BookGen.Commands.Folder; + +[CommandName("folder vscode")] +[Description("Creates Vs Code task and extension files")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +internal sealed class VsCodeCommand : AsyncCommand +{ + internal sealed class Arguments : BookGenArgumentBase + { + [Switch("t", "tasks", Required = false)] + [Description("Create tasks.json file for VS Code")] + public bool CreateTasks { get; set; } + + [Switch("e", "extensions", Required = false)] + [Description("Create extensions.json file for VS Code")] + public bool CreateExtensions { get; set; } + } + + private sealed class VsCodeTaskBuilder + { + private readonly VsCodeTasks _tasks; + + public VsCodeTaskBuilder() + { + _tasks = new VsCodeTasks + { + Tasks = new List() + }; + } + + public VsCodeTaskBuilder AddBookgenTask(string name, string description, TArguments arguments) + where TCommand: ICommand + where TArguments : ArgumentsBase + { + string exePath = Environment.ProcessPath + ?? throw new InvalidOperationException("Path is not available"); + + _tasks.Tasks.Add(new VsCodeTask + { + Label = name, + Detail = description, + Command = exePath, + Args = GetArguments(arguments), + Group = Group.Build, + Type = TaskType.Shell, + Presentation = new() + { + Reveal = Reveal.Always, + Panel = PresentationPanel.Dedicated, + Clear = true, + ShowReuseMessage = true, + } + }); + + return this; + } + + private string[] GetArguments(TArguments arguments) + where TCommand : ICommand + where TArguments : ArgumentsBase + { + var args = new List(); + + var name = (typeof(TCommand).GetCustomAttribute()?.Name) + ?? throw new InvalidOperationException("Command name is not available"); + + args.Add(name); + + PropertyInfo[] properties = typeof(TArguments).GetProperties(); + + List<(string @switch, string value)> switches = new(); + List<(int position, string value)> positional = new(); + + foreach (PropertyInfo property in properties) + { + SwitchAttribute? switchAttribute = property.GetCustomAttribute(); + ArgumentAttribute? argumentAttribute = property.GetCustomAttribute(); + + if (switchAttribute != null) + { + object? value = property.GetValue(arguments); + if (value != null) + { + if (value is bool) + switches.Add((switchAttribute.LongName, "")); + else if (value is IFormattable formattable) + switches.Add((switchAttribute.LongName, formattable.ToString(null, CultureInfo.InvariantCulture))); + else + switches.Add((switchAttribute.LongName, value.ToString() ?? "")); + + } + } + if (argumentAttribute != null) + { + var value = property.GetValue(arguments); + if (value != null) + { + if (value is IFormattable formattable) + positional.Add((argumentAttribute.Index, formattable.ToString(null, CultureInfo.InvariantCulture))); + else + positional.Add((argumentAttribute.Index, value.ToString() ?? "")); + } + } + } + + foreach ((string @switch, string value) @switch in switches) + { + args.Add($"--{@switch.@switch}"); + + if (!string.IsNullOrEmpty(@switch.value)) + args.Add($"\"{@switch.value}\""); + } + + foreach (var position in positional.OrderBy(p => p.position)) + { + args.Add($"\"{position.value}\""); + } + + return args.ToArray(); + } + + public VsCodeTasks Build() + => _tasks; + } + + private readonly IWritableFileSystem _writableFileSystem; + + public VsCodeCommand(IWritableFileSystem writableFileSystem) + { + _writableFileSystem = writableFileSystem; + } + + public override async Task ExecuteAsync(Arguments arguments, IReadOnlyList context, CancellationToken token) + { + if (!arguments.CreateTasks && + !arguments.CreateExtensions) + { + arguments.CreateTasks = true; + arguments.CreateExtensions = true; + } + + if (arguments.CreateExtensions) + { + var extensionFile = Path.Combine(arguments.Directory, ".vscode", "extensions.json"); + RecommendedExtensions extensions = new() + { + Recommendations = new[] + { + "yzhang.markdown-all-in-one", + "DavidAnson.vscode-markdownlint", + } + }; + await _writableFileSystem.SerializeAsync(extensionFile, extensions, writeSchema: false); + } + + if (arguments.CreateTasks) + { + var file = Path.Combine(arguments.Directory, ".vscode", "tasks.json"); + VsCodeTaskBuilder taskBuilder = new(); + + taskBuilder + .AddBookgenTask("Md2html", "Render to html", new() + { + InputFiles = [VsCodeVars.File], + OutputFile = $"{VsCodeVars.File}.html" + }); + + taskBuilder + .AddBookgenTask("Get statistics", "Statistics information", new() + { + Directory = VsCodeVars.WorkspaceFolder + }); + + taskBuilder + .AddBookgenTask("Build web", "Build static website", new() + { + Directory = VsCodeVars.WorkspaceFolder, + OutputDirectory = "Output/Web" + }); + + taskBuilder + .AddBookgenTask("Build print", "Build printable html", new() + { + Directory = VsCodeVars.WorkspaceFolder, + OutputDirectory = "Output/Print" + }); + + taskBuilder + .AddBookgenTask("Build wordpress", "Build wordpress export xml", new() + { + Directory = VsCodeVars.WorkspaceFolder, + OutputDirectory = "Output/Wp" + }); + + taskBuilder + .AddBookgenTask("Build e-pub", "Build e-pub export", new() + { + Directory = VsCodeVars.WorkspaceFolder, + OutputDirectory = "Output/Epub" + }); + + await _writableFileSystem.SerializeAsync(file, taskBuilder.Build(), writeSchema: false); + } + + return ExitCodes.Success; + } +} diff --git a/Source/BookGen/Commands/GuiCommand.cs b/Source/BookGen/Commands/GuiCommand.cs index a26e2a68..7c043a0c 100644 --- a/Source/BookGen/Commands/GuiCommand.cs +++ b/Source/BookGen/Commands/GuiCommand.cs @@ -1,15 +1,20 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Http; +using System.ComponentModel; +using System.IO.Compression; using BookGen.Cli; using BookGen.Cli.Annotations; +using BookGen.Infrastructure.Plugins; using BookGen.Infrastructure.Terminal; +using BookGen.Lib.Http; using BookGen.Vfs; +using Microsoft.Extensions.Logging; + using Spectre.Console; using Color = Spectre.Console.Color; @@ -18,22 +23,29 @@ namespace BookGen.Commands; [CommandName("gui")] +[Description("Starts the program with a command line gui interface.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +[ExitCode(ExitCodes.GeneralError, "The command failed.")] internal sealed class GuiCommand : AsyncCommand { private readonly IWritableFileSystem _fileSystem; + private readonly ILogger _logger; private readonly ICommandRunnerProxy _commandRunnerProxy; private readonly CommandArgsBuilder _argsBuilder; private BookGenArgumentBase? _currentArgs; - public GuiCommand(IWritableFileSystem writableFileSystem, ICommandRunnerProxy commandRunnerProxy) + public GuiCommand(IWritableFileSystem writableFileSystem, + ILogger logger, + ICommandRunnerProxy commandRunnerProxy) { _argsBuilder = new(); _fileSystem = writableFileSystem; + _logger = logger; _commandRunnerProxy = commandRunnerProxy; } - public override async Task ExecuteAsync(BookGenArgumentBase arguments, IReadOnlyList context) + public override async Task ExecuteAsync(BookGenArgumentBase arguments, IReadOnlyList context, CancellationToken token) { WinTerminal.SetWindowTitle("BookGen Gui"); AnsiConsole.Clear(); @@ -64,20 +76,21 @@ public override async Task ExecuteAsync(BookGenArgumentBase arguments, IRea .UseConverter(mi => mi.ToString()) .AddChoiceGroup(MenuItem.GroupHeader("Folder operations"), [ - new(Emoji.Known.RedQuestionMark, "Validate current configuration", async () => await Run("validate")), - new(Emoji.Known.Information, " Statistics", async() => await Run("stats")), - new(Emoji.Known.SpiderWeb, " Start a webserver in curent directory", async () => await Run("serve")), - new(Emoji.Known.Toolbox, " Generate VS code tasks", async () => await Run("vstasks")), + new(Emoji.Known.RedQuestionMark, "Validate current configuration", async () => await Run("book validate")), + new(Emoji.Known.Information, " Statistics", async() => await Run("book stats")), + new(Emoji.Known.SpiderWeb, " Start a webserver in curent directory", async () => await Run("folder serve")), + new(Emoji.Known.Toolbox, " Generate VS code tasks", async () => await Run("folder vscode")), ]) .AddChoiceGroup(MenuItem.GroupHeader("Build"), [ new(Emoji.Known.ExclamationQuestionMark, "Build test website", OnTest), - new(Emoji.Known.GlobeShowingAmericas, "Build static website", async () => await Run("buildweb", "-o", "Output/Web")), - new(Emoji.Known.Printer, " Build printable html", async () => await Run("buildprint", "-o", "Output/Print")), - new(Emoji.Known.FileCabinet, " Build wordpress export", async () => await Run("buildwp", "-o", "Output/Wp")), - new(Emoji.Known.Star, " Build an RSS/Atom Feed", async () => await Run("buildfeed", "-o", "Output/Feed")), - new(Emoji.Known.GreenBook, "Build epub export", async() => await Run("buildepub", "-o", "Output/Epub")), + new(Emoji.Known.GlobeShowingAmericas, "Build static website", async () => await Run("build web", "-o", "Output/Web")), + new(Emoji.Known.Printer, " Build printable html", async () => await Run("build print", "-o", "Output/Print")), + new(Emoji.Known.FileCabinet, " Build wordpress export", async () => await Run("build wp", "-o", "Output/Wp")), + new(Emoji.Known.Star, " Build an RSS/Atom Feed", async () => await Run("build feed", "-o", "Output/Feed")), + new(Emoji.Known.GreenBook, "Build epub export", async() => await Run("build epub", "-o", "Output/Epub")), ]) + .AddChoiceGroup(MenuItem.GroupHeader("Build plugins"), GetPlugins()) .AddChoiceGroup(MenuItem.GroupHeader("Other"), [ new(Emoji.Known.Door, "Exit", OnExit) @@ -87,12 +100,32 @@ public override async Task ExecuteAsync(BookGenArgumentBase arguments, IRea return await selected.ExecuteAsync(); } - private async Task Run(string cmd, params string[] additionals) + private IEnumerable GetPlugins() { - if (_currentArgs == null) - throw new InvalidOperationException("Command not initialized"); + foreach (var plugin in PluginPathResolver.GetPluginPackages()) + { + using ZipArchive archive = ZipFile.OpenRead(plugin); + if (archive.TryGetPluginManifest(plugin, _logger, out PackageManifest? manifest)) + { + string pluginFile = Path.GetFileName(plugin); + string name = Truncate($"{pluginFile} - {manifest.Description}", 90); + yield return new MenuItem(Emoji.Known.Package, name, async () => await Run("build plugin", pluginFile, "-o", $"Output/Plugins/{pluginFile}")); + } + } + } - return await _commandRunnerProxy.RunCommand(cmd, _argsBuilder.New().Add(_currentArgs).Add(additionals).Build()); + private static string Truncate(string str, int maxLength) + { + return str.Length <= maxLength + ? str + : $"{str.AsSpan(0, maxLength - 3)}..."; + } + + private async Task Run(string cmd, params string[] additionals) + { + return _currentArgs == null + ? throw new InvalidOperationException("Command not initialized") + : await _commandRunnerProxy.RunCommand(cmd, _argsBuilder.New().Add(_currentArgs).Add(additionals).Build()); } private Task OnExit() @@ -107,12 +140,12 @@ private async Task OnTest() if (_currentArgs == null) return ExitCodes.GeneralError; - int result = await Run("buildweb", "-o", "Output/Test", "-h", $"http://localhost:{ServerFactory.HostingPort}/"); + int result = await Run("build web", "-o", "Output/Test", "-h", $"http://localhost:{ServerFactory.HostingPort}/"); if (result == ExitCodes.Success) { _currentArgs.Directory = Path.Combine(_currentArgs.Directory, "Output", "Test"); - return await Run("serve"); + return await Run("folder serve"); } return result; diff --git a/Source/BookGen/Commands/HelpCommand.cs b/Source/BookGen/Commands/HelpCommand.cs index bc52e1c0..cc10b9cc 100644 --- a/Source/BookGen/Commands/HelpCommand.cs +++ b/Source/BookGen/Commands/HelpCommand.cs @@ -1,8 +1,10 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using System.ComponentModel; + using BookGen.Cli; using BookGen.Cli.Annotations; using BookGen.Infrastructure; @@ -12,34 +14,49 @@ namespace BookGen.Commands; [CommandName("help")] -internal sealed class HelpCommand : Command +[Description("Displays help information about the specified command.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +[ExitCode(ExitCodes.GeneralError, "The command failed.")] +internal sealed class HelpCommand : Command { - private readonly IHelpProvider _helpProvider; + internal sealed class Arguments : ArgumentsBase + { + [Argument(0, IsOptional = true)] + [Description("The name of the command to display help for.")] + public string CommandName { get; set; } = string.Empty; + } + private readonly HashSet _commandNames; - private readonly HelpRenderer _renderer = new(); + private readonly ICommandHelpProvider _commandHelpProvider; - public HelpCommand(IHelpProvider helpProvider, ICommandRunnerProxy runnerProxy) + public HelpCommand(ICommandHelpProvider commandHelpProvider, ICommandRunnerProxy runnerProxy) { - _helpProvider = helpProvider; _commandNames = [.. runnerProxy.CommandNames]; + _commandHelpProvider = commandHelpProvider; } - public override int Execute(IReadOnlyList context) + public override int Execute(Arguments arguments, IReadOnlyList context) { - if (context.Count == 0) + if (string.IsNullOrEmpty(arguments.CommandName)) { - _renderer.RenderHelp(_helpProvider.GetCommandHelp("help")); - return ExitCodes.Success; + arguments.CommandName = "help"; } - - string command = context[0].ToLower(); - if (!_commandNames.Contains(command)) + if (!_commandNames.Contains(arguments.CommandName)) { - AnsiConsole.WriteLine("Unknown Command: {0}", command); + AnsiConsole.WriteLine("Unknown Command: {0}", arguments.CommandName); return ExitCodes.GeneralError; } - _renderer.RenderHelp(_helpProvider.GetCommandHelp(command)); + string helpdocument = _commandHelpProvider.GetHelp(arguments.CommandName); + + if (Console.IsOutputRedirected) + { + AnsiConsole.WriteLine(helpdocument); + } + else + { + HelpRenderer.RenderHelp(helpdocument.Split('\n')); + } return ExitCodes.Success; } diff --git a/Source/BookGen/Commands/InstallCommand.cs b/Source/BookGen/Commands/InstallCommand.cs index 8411688b..3c60bb6c 100644 --- a/Source/BookGen/Commands/InstallCommand.cs +++ b/Source/BookGen/Commands/InstallCommand.cs @@ -1,65 +1,67 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using System.ComponentModel; + using BookGen.Cli; using BookGen.Cli.Annotations; -using BookGen.Infrastructure.Terminal; +using BookGen.Shell.Shared; using Microsoft.Extensions.Logging; +using Webmaster442.WindowsTerminal; + + namespace BookGen.Commands; [CommandName("install")] -internal class InstallCommand : AsyncCommand +[Description("Windows only command that installs BookGen to the system PATH & optionally to the windows terminal.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +internal sealed class InstallCommand : AsyncCommand { - private readonly ILogger _logger; + internal sealed class Arguments : ArgumentsBase + { + [Switch("ctp", "check-terminal-profile", Required = false)] + [Description("When specified checks, if terminal profile installed or not. If exit code is 0, profile is installed.")] + public bool CheckTerminalProfileInstall { get; set; } + + [Switch("ct", "check-terminal-install", Required = false)] + [Description("When specified checks, if windows terminal is installed or not. If exit code is 0, terminal is installed.")] + public bool CheckTerminalInstall { get; set; } + } - private class InstallOption + private sealed class InstallOption { public required string DisplayText { get; init; } public required Task Action { get; init; } } + private readonly ILogger _logger; + public InstallCommand(ILogger logger) { _logger = logger; } - public override SupportedOs SupportedOs => SupportedOs.Windows; + public override SupportedOs SupportedOs + => SupportedOs.Windows; - public override async Task ExecuteAsync(IReadOnlyList context) + private async Task InstallTerminalProfile() { - var menu = new InstallOption[] + var result = await TerminalProfileInstaller.TryInstallAsync(); + if (result == null) { - new() { - DisplayText = "Add install folder to PATH variable", - Action = AddToPath(), - }, - new() { - DisplayText = "Install windows terminal profile", - Action = InstallTerminalProfile() - } - }; - List selction = Terminal.SelectionMenu(menu, "Bookgen installer", "Select install options", f => f.DisplayText); - - foreach (InstallOption item in selction) - { - await item.Action; + _logger.LogWarning("Windows terminal is not installed, can't proceed"); + Environment.Exit(ExitCodes.GeneralError); } - - return ExitCodes.Success; - } - - private async Task InstallTerminalProfile() - { - var terminalInstall = new TerminalInstallCommand(_logger); - await terminalInstall.ExecuteAsync(new TerminalInstallCommand.TerminalInstallArguments + else if (result == false) { - CheckInstall = false, - CheckTerminalInstall = false, - }, Array.Empty()); + _logger.LogCritical("Terminal profile install failed"); + Environment.Exit(ExitCodes.GeneralError); + } + _logger.LogInformation("Successfully installed windows terminal profile"); } private Task AddToPath() @@ -80,4 +82,39 @@ private Task AddToPath() return Task.CompletedTask; } + + public override async Task ExecuteAsync(Arguments arguments, IReadOnlyList context, CancellationToken token) + { + if (arguments.CheckTerminalInstall) + { + InstallResult installReult = InstallDetector.GetInstallResult(); + return installReult.IsWindowsTerminalInstalled ? ExitCodes.Success : ExitCodes.GeneralError; + } + + if (arguments.CheckTerminalProfileInstall) + { + bool installed = TerminalProfileInstaller.IsInstalled(); + return installed ? ExitCodes.Success : ExitCodes.GeneralError; + } + + var menu = new InstallOption[] + { + new() { + DisplayText = "Add install folder to PATH variable", + Action = AddToPath(), + }, + new() { + DisplayText = "Install windows terminal profile", + Action = InstallTerminalProfile() + } + }; + List selction = Infrastructure.Terminal.Terminal.SelectionMenu(menu, "Bookgen installer", "Select install options", f => f.DisplayText); + + foreach (InstallOption item in selction) + { + await item.Action; + } + + return ExitCodes.Success; + } } diff --git a/Source/BookGen/Commands/JsonArgsCommand.cs b/Source/BookGen/Commands/JsonArgsCommand.cs index 94e4c104..941ad01b 100644 --- a/Source/BookGen/Commands/JsonArgsCommand.cs +++ b/Source/BookGen/Commands/JsonArgsCommand.cs @@ -1,8 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using System.ComponentModel; using System.Text.Json; using BookGen.Cli; @@ -13,14 +14,17 @@ namespace BookGen.Commands; [CommandName("jsonargs")] -internal sealed class JsonArgsCommand : Command +[Description("Creates an empty json arguments template file for a given bookgen command.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +internal sealed class JsonArgsCommand : Command { - internal sealed class JsonArgsArguments : BookGenArgumentBase + internal sealed class Arguments : BookGenArgumentBase { - [Switch("c", "command")] + [Switch("c", "command", Required = true)] + [Description("Specifies the command for which the json template will be created.")] public string CommandName { get; set; } - public JsonArgsArguments() + public Arguments() { CommandName = string.Empty; } @@ -45,7 +49,7 @@ public JsonArgsCommand(ICommandRunnerProxy runnerProxy, ILogger log) _log = log; } - public override int Execute(JsonArgsArguments arguments, IReadOnlyList context) + public override int Execute(Arguments arguments, IReadOnlyList context) { if (!_commandNames.Contains(arguments.CommandName)) { diff --git a/Source/BookGen/Commands/ShellCommand.cs b/Source/BookGen/Commands/ShellCommand.cs index 6eb34d48..efb703f7 100644 --- a/Source/BookGen/Commands/ShellCommand.cs +++ b/Source/BookGen/Commands/ShellCommand.cs @@ -1,19 +1,20 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using System.Diagnostics; +using System.ComponentModel; using BookGen.Cli; using BookGen.Cli.Annotations; -using BookGen.Infrastructure; using Spectre.Console; namespace BookGen.Commands; [CommandName("shell")] +[Description("Autocompleter command, that is used by Powershell.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] internal sealed class ShellCommand : Command { private readonly ICommandRunnerProxy _commandNameProider; @@ -70,11 +71,9 @@ internal IEnumerable DoComplete(IReadOnlyList args) IEnumerable candidate = items.Where(arg => arg.StartsWith(words.Last(), StringComparison.OrdinalIgnoreCase)); - if (candidate.Any()) - return candidate; - else - return ProgramConfigurator.GeneralArguments.Where(c => c.StartsWith(words.Last(), StringComparison.OrdinalIgnoreCase)); - + return candidate.Any() + ? candidate + : _commandNameProider.GlobalOptions.Where(c => c.StartsWith(words.Last(), StringComparison.OrdinalIgnoreCase)); } } diff --git a/Source/BookGen/Commands/SpellCheckCommand.cs b/Source/BookGen/Commands/SpellCheckCommand.cs index af7af400..e3ab92e5 100644 --- a/Source/BookGen/Commands/SpellCheckCommand.cs +++ b/Source/BookGen/Commands/SpellCheckCommand.cs @@ -1,4 +1,10 @@ -using System.Text; +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.ComponentModel; +using System.Text; using BookGen.Cli; using BookGen.Cli.Annotations; @@ -15,20 +21,27 @@ namespace BookGen.Commands; [CommandName("spellcheck")] -internal sealed class SpellCheckCommand : AsyncCommand +[Description("Perform spell check on a given markdown file or text file. The command will print the misspelled words to the console.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +[ExitCode(ExitCodes.GeneralError, "Dictionary was not found or file contained spelling mistakes.")] +internal sealed class SpellCheckCommand : AsyncCommand { - public sealed class SpellCheckArguments : ArgumentsBase, IVerbosablityToggle + internal sealed class Arguments : ArgumentsBase, IVerbosablityToggle { - [Switch("i", "input")] + [Switch("i", "input", Required = true)] + [Description("Specifies the input file path. The file must be a markdown file or a text file.")] public string InputFile { get; set; } = string.Empty; - [Switch("v", "verbose")] + [Switch("v", "verbose", Required = false)] + [Description("Turns on detailed logging. Usefull for locating issues")] public bool Verbose { get; set; } - [Switch("l", "language")] + [Switch("l", "language", Required = false)] + [Description("Specifies the language to use for spell checking. The value must be a valid language code, like en_US or hu_HU. If not specified, then en_US will be used as the default language.")] public string Language { get; set; } = "en_US"; - [Switch("-ld", "--list-dictionaires")] + [Switch("-ld", "--list-dictionaries", Required = false)] + [Description("When specified, the command will list all available dictionaries and exit.")] public bool DictionariesDisplay { get; set; } = false; public override ValidationResult Validate(IValidationContext context) @@ -61,7 +74,7 @@ public SpellCheckCommand([FromKeyedServices("dictionaries")] IAssetSource dictio _fileSystem = fileSystem; } - public override async Task ExecuteAsync(SpellCheckArguments arguments, IReadOnlyList context) + public override async Task ExecuteAsync(Arguments arguments, IReadOnlyList context, CancellationToken token) { if (arguments.DictionariesDisplay) { diff --git a/Source/BookGen/Commands/SubCommandsCommand.cs b/Source/BookGen/Commands/SubCommandsCommand.cs index f48fbb1c..8c401692 100644 --- a/Source/BookGen/Commands/SubCommandsCommand.cs +++ b/Source/BookGen/Commands/SubCommandsCommand.cs @@ -1,8 +1,10 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using System.ComponentModel; + using BookGen.Cli; using BookGen.Cli.Annotations; using BookGen.Infrastructure.Terminal; @@ -12,6 +14,8 @@ namespace BookGen.Commands; [CommandName("subcommands")] +[Description("Listst all available subcommands.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] internal sealed class SubCommandsCommand : Command { private readonly IEnumerable> _commands; diff --git a/Source/BookGen/Commands/TemplatesCommand.cs b/Source/BookGen/Commands/TemplatesCommand.cs index 225e483c..4e2b841d 100644 --- a/Source/BookGen/Commands/TemplatesCommand.cs +++ b/Source/BookGen/Commands/TemplatesCommand.cs @@ -1,8 +1,10 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using System.ComponentModel; + using BookGen.Cli; using BookGen.Cli.Annotations; using BookGen.Infrastructure.Terminal; @@ -13,11 +15,15 @@ namespace BookGen.Commands; [CommandName("templates")] -internal class TemplatesCommand : AsyncCommand +[Description("Lists all available templates, or extracts a single template to the current directory.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +[ExitCode(ExitCodes.GeneralError, "Specified template was not found.")] +internal class TemplatesCommand : AsyncCommand { - internal class TemplatesArguments : ArgumentsBase + internal sealed class Arguments : ArgumentsBase { - [Switch("n", "name")] + [Switch("n", "name", Required = false)] + [Description(" If specified, only the template with the given name will be extracted. If not specified, all available templates will be printed.")] public string TemplateName { get; set; } = string.Empty; } @@ -34,7 +40,7 @@ public TemplatesCommand(ILogger logger, IAssetSource assetSource, IWritableFileS _defaultTemplates = ["Epub.html", "Md2Html.html", "Print.html", "Static.html"]; } - public override async Task ExecuteAsync(TemplatesArguments arguments, IReadOnlyList context) + public override async Task ExecuteAsync(Arguments arguments, IReadOnlyList context, CancellationToken token) { if (string.IsNullOrEmpty(arguments.TemplateName)) { diff --git a/Source/BookGen/Commands/TerminalInstallCommand.cs b/Source/BookGen/Commands/TerminalInstallCommand.cs deleted file mode 100644 index 7291a66a..00000000 --- a/Source/BookGen/Commands/TerminalInstallCommand.cs +++ /dev/null @@ -1,68 +0,0 @@ -//----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor -// This code is licensed under MIT license (see LICENSE for details) -//----------------------------------------------------------------------------- - -using BookGen.Cli; -using BookGen.Cli.Annotations; -using BookGen.Shell.Shared; - -using Microsoft.Extensions.Logging; - -using Webmaster442.WindowsTerminal; - -namespace BookGen.Commands; - -[CommandName("terminalinstall")] -internal sealed class TerminalInstallCommand : AsyncCommand -{ - internal sealed class TerminalInstallArguments : ArgumentsBase - { - [Switch("c", "checkinstall")] - public bool CheckInstall { get; set; } - - [Switch("t", "checkterminalinstall")] - public bool CheckTerminalInstall { get; set; } - } - - private readonly ILogger _log; - - public TerminalInstallCommand(ILogger log) - { - _log = log; - } - - public override SupportedOs SupportedOs => SupportedOs.Windows; - - public override async Task ExecuteAsync(TerminalInstallArguments arguments, IReadOnlyList context) - { - - if (arguments.CheckTerminalInstall) - { - InstallResult installReult = InstallDetector.GetInstallResult(); - return installReult.IsWindowsTerminalInstalled ? ExitCodes.Success : ExitCodes.GeneralError; - } - - if (arguments.CheckInstall) - { - bool installed = TerminalProfileInstaller.IsInstalled(); - return installed ? ExitCodes.Success : ExitCodes.GeneralError; - } - - var result = await TerminalProfileInstaller.TryInstallAsync(); - - if (result == null) - { - _log.LogWarning("Windows terminal is not installed, can't proceed"); - return ExitCodes.GeneralError; - } - else if (result == false) - { - _log.LogCritical("Terminal profile install failed"); - return ExitCodes.GeneralError; - } - - _log.LogInformation("Successfully installed windows terminal profile"); - return ExitCodes.Success; - } -} diff --git a/Source/BookGen/Commands/ToolsCommand.cs b/Source/BookGen/Commands/ToolsCommand.cs index 7f65ddbe..0adab19c 100644 --- a/Source/BookGen/Commands/ToolsCommand.cs +++ b/Source/BookGen/Commands/ToolsCommand.cs @@ -1,8 +1,10 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using System.ComponentModel; + using BookGen.Cli; using BookGen.Cli.Annotations; using BookGen.Infrastructure.Terminal; @@ -17,6 +19,8 @@ namespace BookGen.Commands; [CommandName("tools")] +[Description("Display a list of downloadable tools that can be installed and used with BookGen shell. This command is only supported on Windows OS.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] internal sealed class ToolsCommand : AsyncCommand { private readonly TooldownloaderBase[] _tooldownloaders; @@ -47,7 +51,7 @@ public ToolsCommand(IApiClient apiClient, ILogger logger) public string ToSDisplayString(TooldownloaderBase tool) => $"{tool.ToolInfo.Name} (~{tool.ToolInfo.ApproximateSize})"; - public override async Task ExecuteAsync(IReadOnlyList context) + public override async Task ExecuteAsync(IReadOnlyList context, CancellationToken token) { AnsiConsole.Clear(); AnsiConsole.Write(new FigletText("Tool installer")); diff --git a/Source/BookGen/Commands/VersionCommand.cs b/Source/BookGen/Commands/VersionCommand.cs index 0d54b04b..71e39c0d 100644 --- a/Source/BookGen/Commands/VersionCommand.cs +++ b/Source/BookGen/Commands/VersionCommand.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using System.Runtime.InteropServices.Marshalling; +using System.ComponentModel; using BookGen.Cli; using BookGen.Cli.Annotations; @@ -13,6 +13,8 @@ namespace BookGen.Commands; [CommandName("version")] +[Description("Print the current program and config API version.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] internal sealed class VersionCommand : Command { private readonly ProgramInfo _programInfo; diff --git a/Source/BookGen/Commands/VsTasksCommand.cs b/Source/BookGen/Commands/VsTasksCommand.cs deleted file mode 100644 index ee20c2ac..00000000 --- a/Source/BookGen/Commands/VsTasksCommand.cs +++ /dev/null @@ -1,82 +0,0 @@ -//----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor -// This code is licensed under MIT license (see LICENSE for details) -//----------------------------------------------------------------------------- - -using Bookgen.Lib.Domain.VsCode; - -using BookGen.Cli; -using BookGen.Cli.Annotations; -using BookGen.Vfs; - -namespace BookGen.Commands; - -[CommandName("vstasks")] -internal sealed class VsTasksCommand : AsyncCommand -{ - private readonly IWritableFileSystem _writableFileSystem; - - private sealed class VsCodeTaskBuilder - { - private readonly VsCodeTasks _tasks; - - public VsCodeTaskBuilder() - { - _tasks = new VsCodeTasks - { - Tasks = new List() - }; - } - - public VsCodeTaskBuilder AddBookgenTask(string name, string description, string[] args) - { - string exePath = Environment.ProcessPath - ?? throw new InvalidOperationException("Path is not available"); - - _tasks.Tasks.Add(new VsCodeTask - { - Label = name, - Detail = description, - Command = exePath, - Args = args, - Group = Group.Build, - Type = TaskType.Shell, - Presentation = new() - { - Reveal = Reveal.Always, - Panel = PresentationPanel.Dedicated, - Clear = true, - ShowReuseMessage = true, - } - }); - - return this; - } - - public VsCodeTasks Build() - => _tasks; - } - - public VsTasksCommand(IWritableFileSystem writableFileSystem) - { - _writableFileSystem = writableFileSystem; - } - - public override async Task ExecuteAsync(BookGenArgumentBase arguments, IReadOnlyList context) - { - var file = Path.Combine(arguments.Directory, ".vscode", "tasks.json"); - - VsCodeTaskBuilder taskBuilder = new(); - - taskBuilder - .AddBookgenTask("Md2html", "Render to html", ["md2html", "-i", VsCodeVars.File, "-o", $"{VsCodeVars.File}.html"]) - .AddBookgenTask("Get statistics", "Statistics information", ["stats", "-d", VsCodeVars.WorkspaceFolder]) - .AddBookgenTask("Build web", "Build static website", ["buildweb", "-d", VsCodeVars.WorkspaceFolder, "-o", "Output/Web"]) - .AddBookgenTask("Bild print", "Build printable html", ["buildprint", "-d", VsCodeVars.WorkspaceFolder, "-o", "Output/Print"]) - .AddBookgenTask("Build wordpress", "Build wordpress export xml", ["buildwp", "-d", VsCodeVars.WorkspaceFolder, "-o", "Output/Wp"]); - - await _writableFileSystem.SerializeAsync(file, taskBuilder.Build(), writeSchema: false); - - return ExitCodes.Success; - } -} diff --git a/Source/BookGen/Commands/AddFrontMatterCommand.cs b/Source/BookGen/Commands/folder/AddFrontMatterCommand.cs similarity index 84% rename from Source/BookGen/Commands/AddFrontMatterCommand.cs rename to Source/BookGen/Commands/folder/AddFrontMatterCommand.cs index 38072267..3a198739 100644 --- a/Source/BookGen/Commands/AddFrontMatterCommand.cs +++ b/Source/BookGen/Commands/folder/AddFrontMatterCommand.cs @@ -1,15 +1,15 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using System.ComponentModel; using System.Text; -using Bookgen.Lib; -using Bookgen.Lib.Domain.IO; - using BookGen.Cli; using BookGen.Cli.Annotations; +using BookGen.Lib; +using BookGen.Lib.Domain.IO; using BookGen.Vfs; using Markdig; @@ -19,9 +19,11 @@ using YamlDotNet.Serialization; -namespace BookGen.Commands; +namespace BookGen.Commands.Folder; -[CommandName("addfrontmatter")] +[CommandName("folder addfrontmatter")] +[Description("Add a basic YAML frontmatter information to all markdown files located in the current folder and it's subfolders.")] +[ExitCode(ExitCodes.Success, "The front matter was added successfully.")] internal sealed class AddFrontMatterCommand : AsyncCommand { private readonly IWritableFileSystem _writableFileSystem; @@ -33,7 +35,7 @@ public AddFrontMatterCommand(IWritableFileSystem writableFileSystem, ILogger log _logger = logger; } - public override async Task ExecuteAsync(BookGenArgumentBase arguments, IReadOnlyList context) + public override async Task ExecuteAsync(BookGenArgumentBase arguments, IReadOnlyList context, CancellationToken token) { int modified = 0; _writableFileSystem.Scope = arguments.Directory; diff --git a/Source/BookGen/Commands/SearchCommand.cs b/Source/BookGen/Commands/folder/SearchCommand.cs similarity index 56% rename from Source/BookGen/Commands/SearchCommand.cs rename to Source/BookGen/Commands/folder/SearchCommand.cs index 8ed3fa50..6383e1bd 100644 --- a/Source/BookGen/Commands/SearchCommand.cs +++ b/Source/BookGen/Commands/folder/SearchCommand.cs @@ -4,31 +4,41 @@ //----------------------------------------------------------------------------- using System.Collections.Concurrent; - -using Bookgen.Lib; -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Domain.IO.Configuration; -using Bookgen.Lib.Rendering.Images; -using Bookgen.Lib.Rendering.Markdown; -using Bookgen.Lib.Rendering.Markdown.RenderInterop; +using System.ComponentModel; +using System.Text.RegularExpressions; using BookGen.Cli; using BookGen.Cli.Annotations; using BookGen.Infrastructure.Loging; +using BookGen.Lib; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Markdown; +using BookGen.Lib.Rendering.Markdown.RenderInterop; using BookGen.Vfs; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Folder; -[CommandName("search")] -internal sealed class SearchCommand : AsyncCommand +[CommandName("folder search")] +[Description("Search for a given text in the markdown files of the book and print the results to the console.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +[ExitCode(ExitCodes.GeneralError, "Search produced no results.")] +[ExitCode(ExitCodes.ConfigError, "Project has config issues.")] +internal sealed class SearchCommand : AsyncCommand { - public sealed class SearchArguments : BookGenArgumentBase + public sealed class Arguments : BookGenArgumentBase { [Argument(0)] + [Description("Required argument. The text to search for. The command will search for the given text in all markdown files in the book and will print the results to the console.")] public string Query { get; set; } = string.Empty; + [Switch("r", "regex", Required = false)] + [Description("Optional switch. If specified, the query will be treated as a regular expression.")] + public bool Regex { get; set; } + public override ValidationResult Validate(IValidationContext context) { if (string.IsNullOrWhiteSpace(Query)) @@ -53,11 +63,8 @@ public SearchCommand(IWritableFileSystem soruce, IProgramPathResolver programPat _assetSource = assetSource; } - public override async Task ExecuteAsync(SearchArguments arguments, IReadOnlyList context) + public override async Task ExecuteAsync(Arguments arguments, IReadOnlyList context, CancellationToken token) { - _soruce.Scope = arguments.Directory; - - _soruce.Scope = arguments.Directory; IWritableFileSystem target = new ReadOnlyWritableFileSystem(); @@ -84,21 +91,37 @@ public override async Task ExecuteAsync(SearchArguments arguments, IReadOnl RenderInterop = new RenderInterop(_assetSource, _programPathResolver, imageConfig), }; settings.RenderInterop.PreRenderCode = false; - + using var markdownConverter = new MarkdownConverter(settings); ConcurrentDictionary searchResults = new(); - await Parallel.ForEachAsync(env.TableOfContents.GetFiles(), async (file, ct) => + if (arguments.Regex) { - string markdown = await env.Source.ReadAllTextAsync(file); - string plain = markdownConverter.RenderToPlainText(markdown); - - if (Search.Contains(plain, arguments.Query, 0.8f, out string? context)) + Regex regex = new Regex(arguments.Query, RegexOptions.Compiled, TimeSpan.FromSeconds(5)); + await Parallel.ForEachAsync(env.TableOfContents.GetFiles(), async (file, ct) => { - searchResults.TryAdd(file, context); - } - }); + string markdown = await env.Source.ReadAllTextAsync(file); + string plain = markdownConverter.RenderToPlainText(markdown); + if (Search.RegexContains(plain, regex, out string? context)) + { + searchResults.TryAdd(file, context); + } + }); + } + else + { + await Parallel.ForEachAsync(env.TableOfContents.GetFiles(), async (file, ct) => + { + string markdown = await env.Source.ReadAllTextAsync(file); + string plain = markdownConverter.RenderToPlainText(markdown); + + if (Search.Contains(plain, arguments.Query, 0.8f, out string? context)) + { + searchResults.TryAdd(file, context); + } + }); + } if (searchResults.IsEmpty) { diff --git a/Source/BookGen/Commands/ServeCommand.cs b/Source/BookGen/Commands/folder/ServeCommand.cs similarity index 79% rename from Source/BookGen/Commands/ServeCommand.cs rename to Source/BookGen/Commands/folder/ServeCommand.cs index 07026d9a..a06ded5a 100644 --- a/Source/BookGen/Commands/ServeCommand.cs +++ b/Source/BookGen/Commands/folder/ServeCommand.cs @@ -1,20 +1,24 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib; -using Bookgen.Lib.Http; +using System.ComponentModel; using BookGen.Cli; using BookGen.Cli.Annotations; +using BookGen.Lib; +using BookGen.Lib.Http; using BookGen.Vfs; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Folder; -[CommandName("serve")] +[CommandName("folder serve")] +[Description("Starts a local only http server that serves file from the given directory.")] +[ExitCode(ExitCodes.FolderLocked, "A serve command is running in the given folder.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] internal sealed class ServeCommand : AsyncCommand { private readonly ILogger _log; @@ -26,7 +30,7 @@ public ServeCommand(ILogger log, IWritableFileSystem fs) _fs = fs; } - public override async Task ExecuteAsync(BookGenArgumentBase arguments, IReadOnlyList context) + public override async Task ExecuteAsync(BookGenArgumentBase arguments, IReadOnlyList context, CancellationToken token) { _fs.Scope = arguments.Directory; diff --git a/Source/BookGen/Commands/ShortcutCommand.cs b/Source/BookGen/Commands/folder/ShortcutCommand.cs similarity index 77% rename from Source/BookGen/Commands/ShortcutCommand.cs rename to Source/BookGen/Commands/folder/ShortcutCommand.cs index ef315d08..1ea37922 100644 --- a/Source/BookGen/Commands/ShortcutCommand.cs +++ b/Source/BookGen/Commands/folder/ShortcutCommand.cs @@ -1,18 +1,22 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using System.ComponentModel; + using BookGen.Cli; using BookGen.Cli.Annotations; using BookGen.Shell.Shared; using Microsoft.Extensions.Logging; -namespace BookGen.Commands; +namespace BookGen.Commands.Folder; -[CommandName("shortcut")] -internal class ShortcutCommand : Command +[CommandName("folder shortcut")] +[Description("Create a shortcut file in the current directory that can be used to start the bookgen Shell in the current directory.")] +[ExitCode(ExitCodes.Success, "The command completed successfully.")] +internal sealed class ShortcutCommand : Command { private readonly ILogger _logger; diff --git a/Source/BookGen/ExitCodes.cs b/Source/BookGen/ExitCodes.cs index 20709518..13285914 100644 --- a/Source/BookGen/ExitCodes.cs +++ b/Source/BookGen/ExitCodes.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -24,6 +24,10 @@ internal static class ExitCodes ///
public const int FolderLocked = 3; /// + /// Plugin error exit code = 4 + /// + public const int PluginError = 4; + /// /// General error /// public const int GeneralError = int.MaxValue; diff --git a/Source/BookGen/Extensions.cs b/Source/BookGen/Extensions.cs index 504c974b..4a277361 100644 --- a/Source/BookGen/Extensions.cs +++ b/Source/BookGen/Extensions.cs @@ -1,4 +1,9 @@ -using BookGen.Cli; +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using BookGen.Cli; using BookGen.Vfs; namespace BookGen; diff --git a/Source/BookGen/GlobalOptionParsers/AttachDebuggerParser.cs b/Source/BookGen/GlobalOptionParsers/AttachDebuggerParser.cs new file mode 100644 index 00000000..bafba4e6 --- /dev/null +++ b/Source/BookGen/GlobalOptionParsers/AttachDebuggerParser.cs @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.ComponentModel; +using System.Diagnostics; + +using BookGen.Cli; + +using Spectre.Console; + +namespace BookGen.GlobalOptionParsers; + +[Description("Attaches a debugger to the process if one is not already attached.")] +internal sealed class AttachDebuggerParser : GlobalOptionParser +{ + private const string DebuggerStartShort = "ad"; + private const string DebuggerStartLong = "attach-debugger"; + + public AttachDebuggerParser() + : base(DebuggerStartShort, DebuggerStartLong) + { + } + + protected override void OnOptionWasPresent() + { + AnsiConsole.WriteLine("Attaching debugger..."); + if (!Debugger.IsAttached) + { + Debugger.Launch(); + } + } +} diff --git a/Source/BookGen/GlobalOptionParsers/JsonLogParser.cs b/Source/BookGen/GlobalOptionParsers/JsonLogParser.cs new file mode 100644 index 00000000..67fe0dbd --- /dev/null +++ b/Source/BookGen/GlobalOptionParsers/JsonLogParser.cs @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.ComponentModel; + +using BookGen.Cli; + +namespace BookGen.GlobalOptionParsers; + +[Description("Enables JSON logging for the application.")] +internal class JsonLogParser : GlobalOptionParser +{ + private const string JsonLogShort = "js"; + private const string JsonLogLong = "json-log"; + + private readonly ProgramInfo _info; + + public JsonLogParser(ProgramInfo info) + : base(JsonLogShort, JsonLogLong) + { + _info = info; + } + + protected override void OnOptionWasPresent() + { + _info.JsonLogging = true; + } +} diff --git a/Source/BookGen/GlobalOptionParsers/LogToFileParser.cs b/Source/BookGen/GlobalOptionParsers/LogToFileParser.cs new file mode 100644 index 00000000..efd73153 --- /dev/null +++ b/Source/BookGen/GlobalOptionParsers/LogToFileParser.cs @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.ComponentModel; + +using BookGen.Cli; + +namespace BookGen.GlobalOptionParsers; + +[Description("Enables logging to a file for the application.")] +internal class LogToFileParser : GlobalOptionParser +{ + private const string LogFileShort = "lf"; + private const string LogFileLong = "log-file"; + + private readonly ProgramInfo _info; + + public LogToFileParser(ProgramInfo info) + : base(LogFileShort, LogFileLong) + { + _info = info; + } + + protected override void OnOptionWasPresent() + { + _info.LogToFile = true; + } +} diff --git a/Source/BookGen/GlobalOptionParsers/RuntimePrintingParser.cs b/Source/BookGen/GlobalOptionParsers/RuntimePrintingParser.cs new file mode 100644 index 00000000..e1e2690b --- /dev/null +++ b/Source/BookGen/GlobalOptionParsers/RuntimePrintingParser.cs @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.ComponentModel; + +using BookGen.Cli; + +namespace BookGen.GlobalOptionParsers; + +[Description("Disables printing the command running time information.")] +internal class RuntimePrintingParser : GlobalOptionParser +{ + private const string NoRuntimeShort = "nr"; + private const string NoRuntimeLong = "no-runtime"; + + private readonly ProgramInfo _info; + + public RuntimePrintingParser(ProgramInfo info) + : base(NoRuntimeShort, NoRuntimeLong) + { + _info = info; + _info.PrintRuntime = true; + } + + protected override void OnOptionWasPresent() + { + _info.PrintRuntime = false; + } +} diff --git a/Source/BookGen/GlobalOptionParsers/WaitDebuggerParser.cs b/Source/BookGen/GlobalOptionParsers/WaitDebuggerParser.cs new file mode 100644 index 00000000..3eecc182 --- /dev/null +++ b/Source/BookGen/GlobalOptionParsers/WaitDebuggerParser.cs @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.ComponentModel; +using System.Diagnostics; + +using BookGen.Cli; + +using Spectre.Console; + +namespace BookGen.GlobalOptionParsers; + +[Description("Waits for a debugger to be attached before continuing execution.")] +internal sealed class WaitDebuggerParser : GlobalOptionParser +{ + private const string DebuggerShort = "wd"; + private const string DebuggerLong = "wait-debugger"; + + public WaitDebuggerParser() + : base(DebuggerShort, DebuggerLong) + { + } + + protected override void OnOptionWasPresent() + { + AnsiConsole.WriteLine("Waiting for debugger to be attached..."); + AnsiConsole.WriteLine("ESC to cancel & contine execution..."); + while (!Debugger.IsAttached) + { + if (Console.KeyAvailable) + { + ConsoleKeyInfo key = Console.ReadKey(true); + if (key.Key == ConsoleKey.Escape) + { + break; + } + } + else + { + Thread.Sleep(100); + } + } + Debugger.Break(); + //Now you can debug the code + } +} diff --git a/Source/BookGen/Commands/IVerbosablityToggle.cs b/Source/BookGen/IVerbosablityToggle.cs similarity index 82% rename from Source/BookGen/Commands/IVerbosablityToggle.cs rename to Source/BookGen/IVerbosablityToggle.cs index 59eaf9b4..766e931e 100644 --- a/Source/BookGen/Commands/IVerbosablityToggle.cs +++ b/Source/BookGen/IVerbosablityToggle.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -namespace BookGen.Commands; +namespace BookGen; public interface IVerbosablityToggle { diff --git a/Source/BookGen/Commands/SchemasCommand.cs b/Source/BookGen/Infrastructure/DynamicDocumentGenerator.cs similarity index 52% rename from Source/BookGen/Commands/SchemasCommand.cs rename to Source/BookGen/Infrastructure/DynamicDocumentGenerator.cs index 8ec9557d..90f346c6 100644 --- a/Source/BookGen/Commands/SchemasCommand.cs +++ b/Source/BookGen/Infrastructure/DynamicDocumentGenerator.cs @@ -3,34 +3,48 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using System.Text; using System.Text.Json; using System.Text.Json.Schema; -using Bookgen.Lib; -using Bookgen.Lib.Domain.IO; -using Bookgen.Lib.Domain.IO.Configuration; - using BookGen.Cli; -using BookGen.Cli.Annotations; +using BookGen.Cli.OpenCli; +using BookGen.Cli.OpenCli.Draft; +using BookGen.Lib; +using BookGen.Lib.Domain.IO; +using BookGen.Lib.Domain.IO.Configuration; using BookGen.Vfs; -using Microsoft.Extensions.Logging; - -namespace BookGen.Commands; +namespace BookGen.Infrastructure; -[CommandName("schemas")] -internal sealed class SchemasCommand : Command +internal sealed class DynamicDocumentGenerator : IDynamicDocumentGenerator { - private readonly IWritableFileSystem _writableFileSystem; - private readonly ILogger _logger; + private readonly ICommandRunnerProxy _commandRunnerProxy; - public SchemasCommand(IWritableFileSystem writableFileSystem, ILogger logger) + public DynamicDocumentGenerator(ICommandRunnerProxy commandRunnerProxy) { - _writableFileSystem = writableFileSystem; - _logger = logger; + _commandRunnerProxy = commandRunnerProxy; } - public override int Execute(BookGenArgumentBase arguments, IReadOnlyList context) + public string GenerateCommandsDocument() + { + Document openCliDocs = _commandRunnerProxy.GetOpenCliDocs(); + StringBuilder commandsDoc = new(openCliDocs.Commands?.Count * 1024 ?? 1024); + commandsDoc + .AppendLine("# Commands") + .AppendLine(); + + foreach (Cli.OpenCli.Draft.Command command in openCliDocs.Commands?.OrderBy(x => x.Name) ?? Enumerable.Empty()) + { + var cmd = MarkdownGenerator.GenerateMarkdown(command, 2); + commandsDoc + .Append(cmd); + } + + return commandsDoc.ToString(); + } + + public string GenerateSchemasDocument() { JsonSerializerOptions options = JsonOptions.SerializerOptions; JsonSchemaExporterOptions exporterOptions = JsonOptions.ExporterOptions; @@ -47,10 +61,7 @@ public override int Execute(BookGenArgumentBase arguments, IReadOnlyList .Paragraph("Each page in the table of contents must have a YAML front matter.") .CodeBlock(options.GetJsonSchemaAsNode(typeof(FrontMatter), exporterOptions).ToString(), "json"); - _logger.LogInformation("Writing schemas.md..."); - _writableFileSystem.Scope = arguments.Directory; - _writableFileSystem.WriteAllText("schemas.md", markdownBuilder.ToString()); - - return ExitCodes.Success; + return markdownBuilder.ToString(); } + } diff --git a/Source/BookGen/Infrastructure/Embedded.cs b/Source/BookGen/Infrastructure/Embedded.cs new file mode 100644 index 00000000..7e5aba86 --- /dev/null +++ b/Source/BookGen/Infrastructure/Embedded.cs @@ -0,0 +1,21 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Infrastructure; + +internal static class Embedded +{ + public static string ReadEmbeddedResource(string resourceName) + { + using var stream = typeof(Embedded).Assembly.GetManifestResourceStream(resourceName); + if (stream == null) + { + throw new InvalidOperationException($"Resource '{resourceName}' not found."); + } + + using var reader = new StreamReader(stream); + return reader.ReadToEnd(); + } +} diff --git a/Source/BookGen/Infrastructure/HelpProvider.cs b/Source/BookGen/Infrastructure/HelpProvider.cs deleted file mode 100644 index 1ba70a09..00000000 --- a/Source/BookGen/Infrastructure/HelpProvider.cs +++ /dev/null @@ -1,107 +0,0 @@ -//----------------------------------------------------------------------------- -// (c) 2019-2026 Ruzsinszki Gábor -// This code is licensed under MIT license (see LICENSE for details) -//----------------------------------------------------------------------------- - -using Bookgen.Lib; - -using BookGen.Cli; - -using Microsoft.Extensions.Logging; - -namespace BookGen.Infrastructure; - -internal class HelpProvider : IHelpProvider, ICommandHelpProvider -{ - private readonly ILogger _log; - private readonly ICommandRunnerProxy _commandNameProvider; - private readonly Dictionary _helpData; - private readonly Dictionary> _callbackTable; - - public HelpProvider(ILogger log, ICommandRunnerProxy nameProvider) - { - _log = log; - _commandNameProvider = nameProvider; - _helpData = new Dictionary(); - _callbackTable = new Dictionary>(); - - LoadHelpData(); - } - - public IEnumerable HelpEntries => _helpData.Keys; - - private void LoadHelpData() - { - IReadOnlyList lines = - ResourceHandler.GetResourceFileLines("Resources/Commands.md"); - - List chapterData = new(50); - string? currentChapter = null; - - foreach (var line in lines) - { - if (line.StartsWith("# ")) - { - if (chapterData.Count > 0 - && !string.IsNullOrEmpty(currentChapter)) - { - _helpData.Add(currentChapter, chapterData.ToArray()); - chapterData.Clear(); - } - currentChapter = line[2..].Trim().ToLower(); - chapterData.Add(line); - } - else - { - chapterData.Add(line); - } - } - if (chapterData.Count > 0 - && currentChapter != null - && !_helpData.ContainsKey(currentChapter)) - { - _helpData.Add(currentChapter, chapterData.ToArray()); - } - - - } - - public void VerifyHelpData() - { - foreach (var name in _commandNameProvider.CommandNames) - { - if (!_helpData.ContainsKey(name)) - { - _log.LogWarning("No help was found for command: {command}", name); -#if DEBUG - System.Diagnostics.Debugger.Break(); -#endif - } - } - - } - - public void RegisterCallback(string commandName, Func callback) - { - _callbackTable.Add(commandName, callback); - } - - public IEnumerable GetCommandHelp(string cmd) - { - foreach (var line in _helpData[cmd]) - { - yield return line; - } - if (_callbackTable.ContainsKey(cmd)) - { - var lines = _callbackTable[cmd].Invoke().Split('\n', StringSplitOptions.RemoveEmptyEntries); - foreach (var line in lines) - { - yield return line; - } - } - } - - public string GetHelp(string commandName, Type argumentType) - => string.Join(Environment.NewLine, GetCommandHelp(commandName)); -} diff --git a/Source/BookGen/Infrastructure/HelpRenderer.cs b/Source/BookGen/Infrastructure/HelpRenderer.cs index 716610b9..30ebc064 100644 --- a/Source/BookGen/Infrastructure/HelpRenderer.cs +++ b/Source/BookGen/Infrastructure/HelpRenderer.cs @@ -1,40 +1,28 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Rendering.Markdown.Renderers.Terminal; - -using Markdig; -using Markdig.Parsers; -using Markdig.Syntax; +using BookGen.Infrastructure.Terminal; +using BookGen.Lib.Rendering.Markdown; namespace BookGen.Infrastructure; -internal sealed class HelpRenderer +internal static class HelpRenderer { - private readonly MarkdownPipeline _terminalPipeLine; - - public HelpRenderer() + public static void RenderHelp(string markdown) { - _terminalPipeLine = new MarkdownPipelineBuilder().Build(); + string rendered = MarkdownConverter.RenderMarkdownToTerminal(markdown); + + Pager pager = new(rendered); + + pager.Show(false); } - public void RenderHelp(IEnumerable article) + public static void RenderHelp(IEnumerable article) { string md = string.Join(Environment.NewLine, article); - MarkdownDocument document = MarkdownParser.Parse(md, _terminalPipeLine); - - using var writer = new StringWriter(); - var renderer = new TerminalRenderer(writer, new RenderOptions()); - - renderer.Render(document); - renderer.Writer.Flush(); - - using var reader = new StringReader(writer.ToString()); - - Webmaster442.WindowsTerminal.Wigets.Pager pager = new(reader); - - pager.Show(false); + RenderHelp(md); } } + diff --git a/Source/BookGen/Infrastructure/IHelpProvider.cs b/Source/BookGen/Infrastructure/IDynamicDocumentGenerator.cs similarity index 53% rename from Source/BookGen/Infrastructure/IHelpProvider.cs rename to Source/BookGen/Infrastructure/IDynamicDocumentGenerator.cs index 26b86da1..7583bffd 100644 --- a/Source/BookGen/Infrastructure/IHelpProvider.cs +++ b/Source/BookGen/Infrastructure/IDynamicDocumentGenerator.cs @@ -1,13 +1,12 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- namespace BookGen.Infrastructure; -internal interface IHelpProvider +internal interface IDynamicDocumentGenerator { - IEnumerable HelpEntries { get; } - IEnumerable GetCommandHelp(string cmd); - void RegisterCallback(string commandName, Func callback); + string GenerateSchemasDocument(); + string GenerateCommandsDocument(); } diff --git a/Source/BookGen/Infrastructure/Loging/Extensions.cs b/Source/BookGen/Infrastructure/Loging/Extensions.cs index 60ce2850..f4710070 100644 --- a/Source/BookGen/Infrastructure/Loging/Extensions.cs +++ b/Source/BookGen/Infrastructure/Loging/Extensions.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib; +using BookGen.Lib; using Microsoft.Extensions.Logging; diff --git a/Source/BookGen/Infrastructure/Loging/FileLoggerProvider.cs b/Source/BookGen/Infrastructure/Loging/FileLoggerProvider.cs index 61d1c52d..1d771dfd 100644 --- a/Source/BookGen/Infrastructure/Loging/FileLoggerProvider.cs +++ b/Source/BookGen/Infrastructure/Loging/FileLoggerProvider.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen/Infrastructure/Plugins/Extensions.cs b/Source/BookGen/Infrastructure/Plugins/Extensions.cs new file mode 100644 index 00000000..76dd236d --- /dev/null +++ b/Source/BookGen/Infrastructure/Plugins/Extensions.cs @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; +using System.IO.Compression; +using System.Text.Json; + +using Microsoft.Extensions.Logging; + +namespace BookGen.Infrastructure.Plugins; + +internal static class Extensions +{ + public static bool TryGetPluginManifest(this ZipArchive archive, string packagePath, ILogger logger, [NotNullWhen(true)] out PackageManifest? manifest) + { + ZipArchiveEntry? manifestEntry = archive.GetEntry("manifest.json"); + if (manifestEntry == null) + { + logger.LogError("Plugin package does not contain a manifest.json: {PackagePath}", packagePath); + manifest = null; + return false; + } + using Stream manifestStream = manifestEntry.Open(); + try + { + PackageManifest? manifestObject = JsonSerializer.Deserialize(manifestStream, JsonSerializerOptions.Web); + if (manifestObject == null) + { + logger.LogError("Failed to deserialize manifest.json in plugin package: {PackagePath}", packagePath); + manifest = null; + return false; + } + IEnumerable validationResults = manifestObject.Validate(new ValidationContext(manifestObject)); + if (validationResults.Any()) + { + foreach (ValidationResult validationResult in validationResults) + { + logger.LogError("Manifest validation error: {ErrorMessage}", validationResult.ErrorMessage); + } + manifest = null; + return false; + } + + manifest = manifestObject; + return true; + } + catch (Exception) + { + logger.LogError("Failed to deserialize manifest.json in plugin package: {PackagePath}", packagePath); + manifest = null; + return false; + } + } +} diff --git a/Source/BookGen/Infrastructure/Plugins/PackageManifest.cs b/Source/BookGen/Infrastructure/Plugins/PackageManifest.cs new file mode 100644 index 00000000..3e67915f --- /dev/null +++ b/Source/BookGen/Infrastructure/Plugins/PackageManifest.cs @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; +using System.IO.Compression; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace BookGen.Infrastructure.Plugins; + +internal sealed class PackageManifest : IValidatableObject +{ + [JsonPropertyName("entryAssembly")] + public required string EntryAssembly { get; init; } + + [JsonPropertyName("author")] + public required string Author { get; init; } + + [JsonPropertyName("description")] + public required string Description { get; init; } + + [JsonPropertyName("apiVersion")] + public required string ApiVersion { get; init; } + + [JsonPropertyName("url")] + public string? Url { get; init; } + + public IEnumerable Validate(ValidationContext validationContext) + { + if (string.IsNullOrWhiteSpace(EntryAssembly)) + { + yield return new ValidationResult("EntryAssembly cannot be null or whitespace.", [nameof(EntryAssembly)]); + } + string extension = Path.GetExtension(EntryAssembly); + if (!string.Equals(extension, ".dll", StringComparison.OrdinalIgnoreCase)) + { + yield return new ValidationResult("EntryAssembly must have a .dll extension.", [nameof(EntryAssembly)]); + } + if (string.IsNullOrWhiteSpace(Author)) + { + yield return new ValidationResult("Author cannot be null or whitespace.", [nameof(Author)]); + } + if (string.IsNullOrWhiteSpace(Description)) + { + yield return new ValidationResult("Description cannot be null or whitespace.", [nameof(Description)]); + } + + if (string.IsNullOrWhiteSpace(ApiVersion)) + { + yield return new ValidationResult("ApiVersion cannot be null or whitespace.", [nameof(ApiVersion)]); + } + if (!Version.TryParse(ApiVersion, out Version? version)) + { + yield return new ValidationResult("ApiVersion must be a valid version string.", [nameof(ApiVersion)]); + } + if (version?.Major != 1) + { + yield return new ValidationResult("ApiVersion must be a major version of 1.", [nameof(ApiVersion)]); + } + } +} diff --git a/Source/BookGen/Infrastructure/Plugins/PluginPathResolver.cs b/Source/BookGen/Infrastructure/Plugins/PluginPathResolver.cs new file mode 100644 index 00000000..8691ce70 --- /dev/null +++ b/Source/BookGen/Infrastructure/Plugins/PluginPathResolver.cs @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Infrastructure.Plugins; + +internal static class PluginPathResolver +{ + public const string PluginFolder = "plugins"; + public const string PluginFileExtension = "*.plugin"; + public const string DllFileExtension = "*.dll"; + + public static IEnumerable GetPluginPackages() + { + string folder = Path.Combine(AppContext.BaseDirectory, PluginFolder); + if (!Directory.Exists(folder)) + { + yield break; + } + + foreach (string file in Directory.EnumerateFiles(folder, PluginFileExtension)) + { + yield return file; + } + } + + public static string? Resolve(string actualFolder, string requestedFile, bool isDevMode = false) + { + if (isDevMode) + { + return Path.GetFullPath(requestedFile, actualFolder); + } + + string probePath = Path.GetFullPath(requestedFile, actualFolder); + if (File.Exists(probePath)) + { + return probePath; + } + + probePath = Path.Combine(AppContext.BaseDirectory, PluginFolder, requestedFile); + return File.Exists(probePath) + ? probePath + : null; + } +} diff --git a/Source/BookGen/Infrastructure/Plugins/PluginRunner.cs b/Source/BookGen/Infrastructure/Plugins/PluginRunner.cs new file mode 100644 index 00000000..db31959e --- /dev/null +++ b/Source/BookGen/Infrastructure/Plugins/PluginRunner.cs @@ -0,0 +1,202 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO.Compression; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.Loader; + +using BookGen.Api.V1; + +using Microsoft.Extensions.Logging; + +namespace BookGen.Infrastructure.Plugins; + +internal sealed class PluginRunner +{ + private sealed class PluginTempFolder : IDisposable + { + private readonly string _tempFolder; + + public PluginTempFolder() + { + var name = $"bookgen_plugin_{Random.Shared.Next():X16}"; + _tempFolder = Path.Combine(Path.GetTempPath(), name); + } + + public string FullPath => _tempFolder; + + public void Dispose() + { + if (Directory.Exists(_tempFolder)) + { + Directory.Delete(_tempFolder, recursive: true); + } + } + } + + private sealed class PluginLoadContext : AssemblyLoadContext + { + private readonly AssemblyDependencyResolver _resolver; + + public PluginLoadContext(string pluginPath) : base(isCollectible: true) + { + _resolver = new AssemblyDependencyResolver(pluginPath); + } + + protected override Assembly? Load(AssemblyName assemblyName) + { + string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName); + return assemblyPath != null + ? LoadFromAssemblyPath(assemblyPath) + : null; + } + + protected override nint LoadUnmanagedDll(string unmanagedDllName) + { + string? libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName); + return libraryPath != null + ? LoadUnmanagedDllFromPath(libraryPath) + : IntPtr.Zero; + } + } + + private static bool TryExtractArchive(ILogger logger, ZipArchive archive, string fullPath) + { + try + { + archive.ExtractToDirectory(fullPath, overwriteFiles: true); + return true; + } + catch (Exception ex) + { + logger.LogError(ex, "Failed to extract plugin package to temporary folder: {TempFolder}", fullPath); + return false; + } + } + + private static bool ContainsPluginType(Assembly assembly, ILogger logger, [NotNullWhen(true)] out Type? pluginType) + { + Type[] buildPluginTypes = assembly.GetTypes() + .Where(type => type.IsAssignableTo(typeof(IBuildPluginV1))) + .ToArray(); + + if (buildPluginTypes.Length == 0) + { + logger.LogDebug("No IBuildPlugin implementation found in assembly: {AssemblyName}", assembly.FullName); + pluginType = null; + return false; + } + + if (buildPluginTypes.Length > 1) + { + logger.LogError("Multiple IBuildPlugin implementations found in assembly: {AssemblyName}", assembly.FullName); + pluginType = null; + return false; + } + + pluginType = buildPluginTypes[0]; + return true; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Task RunPlugin(ILogger logger, + IBook book, + IBookgenServices bookgenServices, + string pluginPath, + bool isDevMode, + CancellationToken cancellationToken) + { + string entryAssemblyPath = pluginPath; + + using (var tempFolder = new PluginTempFolder()) + { + if (!isDevMode) + { + using ZipArchive archive = ZipFile.OpenRead(entryAssemblyPath); + if (!archive.TryGetPluginManifest(entryAssemblyPath, logger, out PackageManifest? manifest)) + { + return Task.FromResult(false); + } + + if (!TryExtractArchive(logger, archive, tempFolder.FullPath)) + { + return Task.FromResult(false); + } + + entryAssemblyPath = Path.Combine(tempFolder.FullPath, manifest.EntryAssembly); + } + + if (!File.Exists(entryAssemblyPath)) + { + logger.LogError("Entry assembly specified in manifest does not exist: {EntryAssemblyPath}", entryAssemblyPath); + return Task.FromResult(false); + } + + (bool result, WeakReference loadContextWeakRef) = LoadAndExecutePlugin(logger, book, bookgenServices, entryAssemblyPath, cancellationToken); + + for (int i = 0; loadContextWeakRef.IsAlive && (i < 100); i++) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + if (loadContextWeakRef.IsAlive) + { + logger.LogWarning("Plugin load context is still alive after unloading: {EntryAssemblyPath}", entryAssemblyPath); + Debugger.Break(); + } + + return Task.FromResult(result); + } + } + + // IMPORTANT: This method must NOT be async and must stay NoInlining. + // A collectible AssemblyLoadContext can only be unloaded once there are no + // references (managed or on the stack) to the context or to any type/object + // from the loaded assembly. If this method were async, the compiler would + // hoist the locals (loadContext, assembly, pluginType, pluginInstance, + // buildPlugin, the plugin's Task) into a heap-allocated state machine whose + // awaiter fields are not cleared after completion, keeping the ALC rooted. + // Keeping it synchronous ensures all those references live on the stack and + // are released the moment the method returns, so the GC loop in the caller + // can collect the context. Only a bool and a WeakReference are handed back. + [MethodImpl(MethodImplOptions.NoInlining)] + private static (bool result, WeakReference loadContextWeakRef) LoadAndExecutePlugin(ILogger logger, + IBook book, + IBookgenServices bookgenServices, + string entryAssemblyPath, + CancellationToken cancellationToken) + { + var loadContext = new PluginLoadContext(entryAssemblyPath); + var loadContextWeakRef = new WeakReference(loadContext); + + try + { + Assembly assembly = loadContext.LoadFromAssemblyPath(entryAssemblyPath); + if (ContainsPluginType(assembly, logger, out Type? pluginType)) + { + object? pluginInstance = Activator.CreateInstance(pluginType); + if (pluginInstance is IBuildPluginV1 buildPlugin) + { + bool result = buildPlugin.Build(book, bookgenServices, cancellationToken).GetAwaiter().GetResult(); + return (result, loadContextWeakRef); + } + } + + return (false, loadContextWeakRef); + } + catch (Exception ex) + { + logger.LogError(ex, "Failed to load plugin assembly: {EntryAssemblyPath}", entryAssemblyPath); + return (false, loadContextWeakRef); + } + finally + { + loadContext.Unload(); + } + } +} diff --git a/Source/BookGen/Infrastructure/Plugins/V1/AssetSource.cs b/Source/BookGen/Infrastructure/Plugins/V1/AssetSource.cs new file mode 100644 index 00000000..1718a086 --- /dev/null +++ b/Source/BookGen/Infrastructure/Plugins/V1/AssetSource.cs @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Diagnostics.CodeAnalysis; + +using BookGen.Lib; + +namespace BookGen.Infrastructure.Plugins.V1; + +internal sealed class AssetSource : Api.V1.IAssetSource +{ + private readonly BookEnvironment _bookEnvironment; + + public AssetSource(BookEnvironment bookEnvironment) + { + _bookEnvironment = bookEnvironment; + } + + public IReadOnlyList AvailableAssets + => _bookEnvironment.AssetNames; + + public Stream GetBinaryAssetStream(string name) + => _bookEnvironment.GetBinaryAssetStream(name); + + public bool TryGetAsset(string name, [NotNullWhen(true)] out string? content) + => _bookEnvironment.TryGetAsset(name, out content); +} diff --git a/Source/BookGen/Infrastructure/Plugins/V1/Book.cs b/Source/BookGen/Infrastructure/Plugins/V1/Book.cs new file mode 100644 index 00000000..c30abc3e --- /dev/null +++ b/Source/BookGen/Infrastructure/Plugins/V1/Book.cs @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using BookGen.Api.V1; +using BookGen.Lib; +using BookGen.Lib.Domain.IO; + +using Microsoft.Extensions.Logging; + +namespace BookGen.Infrastructure.Plugins.V1; + +internal sealed class Book : IBook +{ + public IDocument Index { get; } + public IReadOnlyList Chapters { get; } + + private Book(IDocument index, List chapters) + { + Index = index; + Chapters = chapters; + } + + public static Book CreateFrom(BookEnvironment environment, ILogger logger) + { + var chapters = new List(); + foreach (TocChapter chapter in environment.TableOfContents.Chapters) + { + var documents = new List(); + foreach (string fileName in chapter.Files) + { + var doc = new Document(environment.Source, logger, fileName); + documents.Add(doc); + } + chapters.Add(new Chapter(chapter.Title, documents)); + } + return new Book(new Document(environment.Source, logger, environment.TableOfContents.IndexFile), chapters); + } +} diff --git a/Source/BookGen/Infrastructure/Plugins/V1/BookGenServices.cs b/Source/BookGen/Infrastructure/Plugins/V1/BookGenServices.cs new file mode 100644 index 00000000..4e69ca0a --- /dev/null +++ b/Source/BookGen/Infrastructure/Plugins/V1/BookGenServices.cs @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using BookGen.Api.V1; +using BookGen.Lib; + +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging; + +namespace BookGen.Infrastructure.Plugins.V1; + +internal class BookGenServices : IBookgenServices +{ + private readonly BookEnvironment _environment; + private readonly IMemoryCache _cache; + private readonly ILogger _logger; + + public BookGenServices(BookEnvironment environment, + IMemoryCache cache, + ILogger logger, + IDynamicDocumentGenerator dynamicDocumentGenerator) + { + OutputFolder = new FileSystem(environment.Output); + AssetSource = new AssetSource(environment); + Logger = new PluginLogger(logger); + DynamicDocumentation = new DynamicDocumentation(dynamicDocumentGenerator); + _environment = environment; + _cache = cache; + _logger = logger; + } + + public IFileSystem OutputFolder { get; } + + public IAssetSource AssetSource { get; } + + public IPluginLogger Logger { get; } + + public IDynamicDocumentation DynamicDocumentation { get; } + + public IRenderer CreateRenderer(RendererOptions rendererOptions) + => new Renderer(rendererOptions, _environment, _cache, _logger); +} diff --git a/Source/BookGen/Infrastructure/Plugins/V1/Chapter.cs b/Source/BookGen/Infrastructure/Plugins/V1/Chapter.cs new file mode 100644 index 00000000..1e241429 --- /dev/null +++ b/Source/BookGen/Infrastructure/Plugins/V1/Chapter.cs @@ -0,0 +1,21 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using BookGen.Api.V1; + +namespace BookGen.Infrastructure.Plugins.V1; + +internal sealed class Chapter : IChapter +{ + public Chapter(string title, List documents) + { + Title = title; + Documents = documents; + } + + public string Title { get; } + + public IReadOnlyList Documents { get; } +} diff --git a/Source/BookGen/Infrastructure/Plugins/V1/Document.cs b/Source/BookGen/Infrastructure/Plugins/V1/Document.cs new file mode 100644 index 00000000..abde26ae --- /dev/null +++ b/Source/BookGen/Infrastructure/Plugins/V1/Document.cs @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using BookGen.Api.V1; +using BookGen.Lib.Domain; +using BookGen.Lib.Rendering; +using BookGen.Vfs; + +using Microsoft.Extensions.Logging; + +namespace BookGen.Infrastructure.Plugins.V1; + +internal sealed class Document : IDocument +{ + private readonly IReadOnlyFileSystem _fileSystem; + private readonly ILogger _logger; + + public Document(IReadOnlyFileSystem fileSystem, ILogger logger, string filePath) + { + _fileSystem = fileSystem; + _logger = logger; + FilePath = filePath; + } + + public string FilePath { get; } + + public async Task<(string content, IDocumentFrontMatter frontMatter)> ReadContent() + { + SourceFile file = await _fileSystem.GetSourceFile(FilePath, _logger); + return (file.Content, DocumentFrontMatter.CreateFrom(file.FrontMatter)); + } +} diff --git a/Source/BookGen/Infrastructure/Plugins/V1/DocumentFrontMatter.cs b/Source/BookGen/Infrastructure/Plugins/V1/DocumentFrontMatter.cs new file mode 100644 index 00000000..031728e6 --- /dev/null +++ b/Source/BookGen/Infrastructure/Plugins/V1/DocumentFrontMatter.cs @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using BookGen.Api.V1; +using BookGen.Lib.Domain.IO; + +namespace BookGen.Infrastructure.Plugins.V1; + +internal sealed class DocumentFrontMatter : IDocumentFrontMatter +{ + private DocumentFrontMatter(string title, + IReadOnlyList tags, + string? template, + IReadOnlyDictionary additionalData) + { + Title = title; + Tags = tags; + Template = template; + AdditionalData = additionalData; + } + + public string Title { get; } + public IReadOnlyList Tags { get; } + public string? Template { get; } + public IReadOnlyDictionary AdditionalData { get; } + + internal static IDocumentFrontMatter CreateFrom(FrontMatter frontMatter) + { + return new DocumentFrontMatter( + frontMatter.Title, + frontMatter.TagArray, + frontMatter.Template, + frontMatter.Data ?? new Dictionary()); + } +} diff --git a/Source/BookGen/Infrastructure/Plugins/V1/DynamicDocumentation.cs b/Source/BookGen/Infrastructure/Plugins/V1/DynamicDocumentation.cs new file mode 100644 index 00000000..160d6d87 --- /dev/null +++ b/Source/BookGen/Infrastructure/Plugins/V1/DynamicDocumentation.cs @@ -0,0 +1,17 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using BookGen.Api.V1; + +namespace BookGen.Infrastructure.Plugins.V1; + +internal sealed class DynamicDocumentation(IDynamicDocumentGenerator dynamicDocumentGenerator) : IDynamicDocumentation +{ + public string GetCommandsMarkdown() + => dynamicDocumentGenerator.GenerateCommandsDocument(); + + public string GetSchemasMarkdown() + => dynamicDocumentGenerator.GenerateSchemasDocument(); +} diff --git a/Source/BookGen/Infrastructure/Plugins/V1/FileSystem.cs b/Source/BookGen/Infrastructure/Plugins/V1/FileSystem.cs new file mode 100644 index 00000000..26eddb54 --- /dev/null +++ b/Source/BookGen/Infrastructure/Plugins/V1/FileSystem.cs @@ -0,0 +1,24 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using BookGen.Api.V1; + +namespace BookGen.Infrastructure.Plugins.V1; + +internal sealed class FileSystem : IFileSystem +{ + private readonly Vfs.IWritableFileSystem _fileSystem; + + public FileSystem(Vfs.IWritableFileSystem writableFileSystem) + { + _fileSystem = writableFileSystem; + } + + public bool FileExists(string relativePath) + => _fileSystem.FileExists(relativePath); + + public Task WriteTextFile(string relativePath, string content) + => _fileSystem.WriteAllTextAsync(relativePath, content); +} diff --git a/Source/BookGen/Infrastructure/Plugins/V1/PluginLogger.cs b/Source/BookGen/Infrastructure/Plugins/V1/PluginLogger.cs new file mode 100644 index 00000000..105cc609 --- /dev/null +++ b/Source/BookGen/Infrastructure/Plugins/V1/PluginLogger.cs @@ -0,0 +1,36 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Diagnostics.CodeAnalysis; + +using BookGen.Api.V1; + +using Microsoft.Extensions.Logging; + +namespace BookGen.Infrastructure.Plugins.V1; + +internal sealed class PluginLogger(ILogger logger) : IPluginLogger +{ + public void LogCritical(Exception? exception, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[] args) + => logger.LogCritical(exception, message, args); + + public void LogCritical([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[] args) + => logger.LogCritical(message, args); + + public void LogDebug([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[] args) + => logger.LogDebug(message, args); + + public void LogError([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[] args) + => logger.LogError(message, args); + + public void LogInformation([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[] args) + => logger.LogInformation(message, args); + + public void LogWarning(Exception? exception, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[] args) + => logger.LogWarning(exception, message, args); + + public void LogWarning([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[] args) + => logger.LogWarning(message, args); +} diff --git a/Source/BookGen/Infrastructure/Plugins/V1/Renderer.cs b/Source/BookGen/Infrastructure/Plugins/V1/Renderer.cs new file mode 100644 index 00000000..74bb3578 --- /dev/null +++ b/Source/BookGen/Infrastructure/Plugins/V1/Renderer.cs @@ -0,0 +1,153 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Diagnostics; + +using BookGen.Api.V1; +using BookGen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Pipeline; +using BookGen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Markdown; +using BookGen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Rendering.Templates; + +using Microsoft.Extensions.Caching.Memory; + +namespace BookGen.Infrastructure.Plugins.V1; + +internal sealed class Renderer : IRenderer +{ + private readonly MarkdownRenderSettings _markdownRenderSettings; + private readonly MarkdownConverter _markdownConverter; + private readonly TemplateEngine _engine; + private bool _disposed; + + public Renderer(RendererOptions options, + IBookEnvironment environment, + IMemoryCache memoryCache, + Microsoft.Extensions.Logging.ILogger logger) + { + var imgConfig = new ImageConfig + { + SvgRecode = MapSvg(options.SvgRecode), + ResizeAndRecodeImages = MapResize(options.ImageRecode), + ImageQualityOnResize = 90, + ResizeWith = options.ResizeWidth, + ResizeHeight = options.ResizeHeight, + }; + + var imgService = new ImgService(environment.Source, logger, imgConfig); + var cachedImageService = new CachedImageService(imgService, memoryCache); + + _markdownRenderSettings = new MarkdownRenderSettings(cachedImageService) + { + CssClasses = Map(options.CssClasses), + DeleteFirstH1 = options.DeleteFirstH1, + HostUrl = options.HostUrl, + RenderInterop = new RenderInterop(environment, environment.ProgramPathResolver, imgConfig), + OffsetHeadingsBy = 0, + AutoEmbedSupportedLinks = options.AutoEmbedSupportedLinks, + }; + + _markdownConverter = new MarkdownConverter(_markdownRenderSettings); + _engine = new TemplateEngine(logger, environment); + } + + public void Dispose() + { + _markdownConverter.Dispose(); + _markdownRenderSettings.Dispose(); + _disposed = true; + } + + private static SvgRecodeOption MapSvg(RendererOptions.ImageOption svgRecode) + { + return svgRecode switch + { + RendererOptions.ImageOption.Passtrough => SvgRecodeOption.Passtrough, + RendererOptions.ImageOption.AsPng => SvgRecodeOption.AsPng, + RendererOptions.ImageOption.AsWebp => SvgRecodeOption.AsWebp, + _ => throw new UnreachableException(), + }; + } + + private static ImgRecodeOption MapResize(RendererOptions.ImageOption imageRecode) + { + return imageRecode switch + { + RendererOptions.ImageOption.Passtrough => ImgRecodeOption.Passtrough, + RendererOptions.ImageOption.AsPng => ImgRecodeOption.AsPng, + RendererOptions.ImageOption.AsWebp => ImgRecodeOption.AsWebp, + _ => throw new UnreachableException(), + }; + } + + private Lib.Domain.IO.Configuration.CssClasses Map(Api.V1.CssClasses cssClasses) + { + return new Lib.Domain.IO.Configuration.CssClasses + { + H1 = cssClasses.H1, + H2 = cssClasses.H2, + H3 = cssClasses.H3, + Img = cssClasses.Img, + Table = cssClasses.Table, + Blockquote = cssClasses.Blockquote, + Figure = cssClasses.Figure, + FigureCaption = cssClasses.FigureCaption, + Link = cssClasses.Link, + Ol = cssClasses.Ol, + Ul = cssClasses.Ul, + Li = cssClasses.Li + }; + } + + + public string RenderMarkdownToRawHtml(string markdown) + { + ObjectDisposedException.ThrowIf(_disposed, this); + return _markdownConverter.RenderMarkdownToHtml(markdown); + } + + public async Task RenderMarkdownToHtml(string pageTemplate, IDocument document) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + (string content, IDocumentFrontMatter frontMatter) = await document.ReadContent(); + + return RenderMarkdownToHtml(pageTemplate, (content, frontMatter)); + } + + public string RenderMarkdownToHtml(string pageTemplate, (string content, IDocumentFrontMatter frontMatter) docData) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var viewData = new ViewData + { + Title = docData.frontMatter.Title, + Content = RenderMarkdownToRawHtml(docData.content), + Host = _markdownRenderSettings.HostUrl ?? string.Empty, + AdditionalData = docData.frontMatter.AdditionalData.ToDictionary(), + LastModified = DateTime.Now, + }; + + return _engine.Render(pageTemplate, viewData); + } + + public string RenderMarkdownToHtml(string pageTemplate, RenderTags tags) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var viewData = new ViewData + { + Title = tags.Title, + Content = RenderMarkdownToRawHtml(tags.Content), + Host = tags.Host, + AdditionalData = tags.AdditionalData, + LastModified = tags.LastModified, + }; + + return _engine.Render(pageTemplate, viewData); + } +} diff --git a/Source/BookGen/Infrastructure/ProgramConfigurator.cs b/Source/BookGen/Infrastructure/ProgramConfigurator.cs deleted file mode 100644 index 270623b6..00000000 --- a/Source/BookGen/Infrastructure/ProgramConfigurator.cs +++ /dev/null @@ -1,131 +0,0 @@ -//----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor -// This code is licensed under MIT license (see LICENSE for details) -//----------------------------------------------------------------------------- - -using System.Diagnostics; - -using Spectre.Console; - -namespace BookGen.Infrastructure; - -internal static class ProgramConfigurator -{ - private const string DebuggerShort = "-wd"; - private const string DebuggerLong = "--wait-debugger"; - private const string DebuggerStartShort = "-ad"; - private const string DebuggerStartLong = "--attach-debugger"; - - private const string JsonLogShort = "-js"; - private const string JsonLogLong = "--json-log"; - private const string LogFileShort = "-lf"; - private const string LogFileLong = "--log-file"; - private const string NoRuntimeShort = "-nr"; - private const string NoRuntimeLong = "--no-runtime"; - - public static IEnumerable GeneralArguments - { - get - { - yield return DebuggerShort; - yield return DebuggerLong; - yield return DebuggerStartShort; - yield return DebuggerStartLong; - yield return JsonLogShort; - yield return JsonLogLong; - } - } - - public static List ParseGeneralArgs(string[] args, ProgramInfo info) - { - List results = new List(args.Length); - var removeIndexes = new HashSet(); - - AttachDebugger(removeIndexes, args); - WaitForDebugger(removeIndexes, args); - ConfigureLog(removeIndexes, info, args); - ConfigureRuntimePrinting(removeIndexes, info, args); - - for (int i = 0; i < args.Length; i++) - { - if (!removeIndexes.Contains(i)) - { - results.Add(args[i]); - } - } - return results; - } - - private static bool GetSwitch(HashSet state, IReadOnlyList args, string shortName, string longName) - { - bool found = false; - for (int i = 0; i < args.Count; i++) - { - if (args[i] == shortName || args[i] == longName) - { - state.Add(i); - found = true; - } - } - return found; - } - - - private static void AttachDebugger(HashSet removeIndexes, IReadOnlyList arguments) - { - if (GetSwitch(removeIndexes, arguments, DebuggerStartShort, DebuggerStartLong)) - { - AnsiConsole.WriteLine("Attaching debugger..."); - if (!Debugger.IsAttached) - { - Debugger.Launch(); - } - } - } - - private static void WaitForDebugger(HashSet removeIndexes, IReadOnlyList arguments) - { - if (GetSwitch(removeIndexes, arguments, DebuggerShort, DebuggerLong)) - { - AnsiConsole.WriteLine("Waiting for debugger to be attached..."); - AnsiConsole.WriteLine("ESC to cancel & contine execution..."); - while (!Debugger.IsAttached) - { - if (Console.KeyAvailable) - { - ConsoleKeyInfo key = Console.ReadKey(true); - if (key.Key == ConsoleKey.Escape) - { - return; - } - } - else - { - Thread.Sleep(100); - } - } - Debugger.Break(); - //Now you can debug the code - } - } - - private static void ConfigureLog(HashSet removeIndexes, ProgramInfo info, IReadOnlyList arguments) - { - if (GetSwitch(removeIndexes, arguments, JsonLogShort, JsonLogLong)) - { - info.JsonLogging = true; - } - - info.LogToFile = GetSwitch(removeIndexes, arguments, LogFileShort, LogFileLong); - } - - private static void ConfigureRuntimePrinting(HashSet removeIndexes, ProgramInfo info, IReadOnlyList arguments) - { - if (GetSwitch(removeIndexes, arguments, NoRuntimeShort, NoRuntimeLong)) - { - info.PrintRuntime = false; - return; - } - info.PrintRuntime = true; - } -} diff --git a/Source/BookGen/Infrastructure/Terminal/CommandArgsBuilder.cs b/Source/BookGen/Infrastructure/Terminal/CommandArgsBuilder.cs index 3f0ab1e4..2509b97f 100644 --- a/Source/BookGen/Infrastructure/Terminal/CommandArgsBuilder.cs +++ b/Source/BookGen/Infrastructure/Terminal/CommandArgsBuilder.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen/Infrastructure/Terminal/MenuItem.cs b/Source/BookGen/Infrastructure/Terminal/MenuItem.cs index 9a460c01..d31d5765 100644 --- a/Source/BookGen/Infrastructure/Terminal/MenuItem.cs +++ b/Source/BookGen/Infrastructure/Terminal/MenuItem.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen/Infrastructure/Terminal/NumberFormats.cs b/Source/BookGen/Infrastructure/Terminal/NumberFormats.cs index f3ff603b..b0e2e3aa 100644 --- a/Source/BookGen/Infrastructure/Terminal/NumberFormats.cs +++ b/Source/BookGen/Infrastructure/Terminal/NumberFormats.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen/Infrastructure/Terminal/Pager.cs b/Source/BookGen/Infrastructure/Terminal/Pager.cs new file mode 100644 index 00000000..981d263b --- /dev/null +++ b/Source/BookGen/Infrastructure/Terminal/Pager.cs @@ -0,0 +1,257 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Collections; +using System.Text.RegularExpressions; + +using Spectre.Console; + +namespace BookGen.Infrastructure.Terminal; + +/// +/// Allows to display text in a paginated way +/// +public partial class Pager : WigetBase +{ + private readonly List _pages; + private readonly PagerOptions _options; + + /// + /// Creates a new pager. The pager will read the text from the reader and paginate it + /// + /// A Text reader that supplies the text + /// /// Pager options + public Pager(TextReader reader, PagerOptions? options = default) + { + if (options == default) + options = PagerOptions.Default; + + _options = options; + _pages = new(); + AddPages(reader); + } + + /// + /// Creates a new pager. The pager will paginate the text + /// + /// Text to paginate + /// Pager options + public Pager(string text, PagerOptions? options = default) + { + if (options == default) + options = PagerOptions.Default; + + _options = options; + _pages = new(); + using var reader = new StringReader(text); + AddPages(reader); + } + + /// + /// Hides the pager + /// + public override void OnHide() + { + //Empty logic + } + + /// + /// Shows the pager + /// + public override void OnShow() + { + int currentPage = 0; + while (IsShowing) + { + Console.Clear(); + foreach (var line in _pages[currentPage]) + { + if (line == null) continue; + AnsiConsole.WriteLine(_options.LineFormatter(line)); + } + var navbar = $"{currentPage + 1} of {_pages.Count} | esc/q: quit | up: previous | down: next"; + AnsiConsole.WriteLine("═".PadLeft(_options.PageWidth, '═')); + AnsiConsole.Write(navbar); + + var key = Console.ReadKey(true); + switch (key.Key) + { + case ConsoleKey.Escape: + case ConsoleKey.Q: + Hide(); + return; + case ConsoleKey.UpArrow: + case ConsoleKey.LeftArrow: + case ConsoleKey.PageUp: + currentPage = Math.Max(0, currentPage - 1); + break; + case ConsoleKey.DownArrow: + case ConsoleKey.RightArrow: + case ConsoleKey.PageDown: + currentPage = Math.Min(_pages.Count - 1, currentPage + 1); + break; + } + } + } + + private const string AnsiReset = "\x1b[0m"; + + private void AddPages(TextReader reader) + { + Page current = new(_options.PageHeight); + string? line; + + while ((line = reader.ReadLine()) != null) + { + foreach (var toAdd in SplitToVisibleWidth(line, _options.PageWidth)) + { + if (!current.TryAdd(toAdd)) + { + _pages.Add(current); + current = new Page(_options.PageHeight); + current.TryAdd(toAdd); + } + } + } + _pages.Add(current); + ApplyFormattingAcrossPages(); + } + + /// + /// Splits a line into chunks that fit into the given visible width. + /// ANSI escape sequences are kept intact (never split) and do not count + /// towards the visible width, so a chunk/page break can never break a + /// formatting or reset sequence. + /// + private static IEnumerable SplitToVisibleWidth(string line, int width) + { + if (line.Length == 0) + { + yield return string.Empty; + yield break; + } + + var builder = new System.Text.StringBuilder(); + int visible = 0; + int index = 0; + + while (index < line.Length) + { + Match match = AnsiEscapeRegex().Match(line, index); + if (match.Success && match.Index == index) + { + builder.Append(match.Value); + index += match.Length; + continue; + } + + builder.Append(line[index]); + index++; + visible++; + + if (visible == width) + { + yield return builder.ToString(); + builder.Clear(); + visible = 0; + } + } + + if (builder.Length > 0) + yield return builder.ToString(); + } + + private void ApplyFormattingAcrossPages() + { + for (int i = 0; i < _pages.Count - 1; i++) + { + string activeFormatting = GetActiveFormatting(_pages[i]); + if (activeFormatting.Length > 0) + { + _pages[i].AppendToLastLine(AnsiReset); + _pages[i + 1].PrependToFirstLine(activeFormatting); + } + } + } + + private static string GetActiveFormatting(Page page) + { + var activeCodes = new List(); + + foreach (var line in page) + { + if (line == null) continue; + foreach (Match match in AnsiEscapeRegex().Matches(line)) + { + // Extract the parameters between the '[' and the trailing 'm' + var parameters = match.Value[2..^1]; + // An empty parameter list (\x1b[m) is equivalent to a reset + var codes = parameters.Length == 0 + ? ["0"] + : parameters.Split(';'); + + foreach (var code in codes) + { + // '0' (or an empty code) resets all previously active formatting + if (code.Length == 0 || code == "0") + activeCodes.Clear(); + else + activeCodes.Add(code); + } + } + } + + return activeCodes.Count == 0 + ? string.Empty + : $"\x1b[{string.Join(';', activeCodes)}m"; + } + + + private class Page : IEnumerable + { + private readonly string[] _lines; + private int _index; + + public Page(int count) + { + _lines = new string[count]; + } + + public IEnumerator GetEnumerator() + => ((IEnumerable)_lines).GetEnumerator(); + + public bool TryAdd(string line) + { + if (_index < _lines.Length) + { + _lines[_index++] = line; + return true; + } + return false; + } + + public void AppendToLastLine(string text) + { + if (_index > 0) + { + _lines[_index - 1] += text; + } + } + + public void PrependToFirstLine(string text) + { + if (_index > 0) + { + _lines[0] = text + _lines[0]; + } + } + + IEnumerator IEnumerable.GetEnumerator() + => _lines.GetEnumerator(); + } + + [GeneratedRegex(@"\x1b\[[0-9;]*m", RegexOptions.Compiled, 2000)] + private static partial Regex AnsiEscapeRegex(); +} diff --git a/Source/BookGen/Infrastructure/Terminal/PagerOptions.cs b/Source/BookGen/Infrastructure/Terminal/PagerOptions.cs new file mode 100644 index 00000000..0164ffce --- /dev/null +++ b/Source/BookGen/Infrastructure/Terminal/PagerOptions.cs @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Infrastructure.Terminal; + +/// +/// Pager options +/// +public sealed class PagerOptions +{ + /// + /// Default pager options + /// + public static readonly PagerOptions Default = new() + { + PageHeight = Console.WindowHeight - 2, + PageWidth = Console.WindowWidth, + }; + + /// + /// Line formatter. Before displaying a line, this function will be called to format the line + /// + public Func LineFormatter { get; set; } = DefaultFormatter; + + /// + /// Gets or sets the height of the page + /// + public int PageHeight + { + get => field; + set => field = value; + } + + /// + /// Gets or sets the width of the page + /// + public int PageWidth + { + get => field; + set => field = value; + } + private static string DefaultFormatter(string arg) + => arg; +} diff --git a/Source/BookGen/Infrastructure/Terminal/Palette.cs b/Source/BookGen/Infrastructure/Terminal/Palette.cs index 242208aa..cf60e7c9 100644 --- a/Source/BookGen/Infrastructure/Terminal/Palette.cs +++ b/Source/BookGen/Infrastructure/Terminal/Palette.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen/Infrastructure/Terminal/Terminal.cs b/Source/BookGen/Infrastructure/Terminal/Terminal.cs index 3d983ec7..65f498f1 100644 --- a/Source/BookGen/Infrastructure/Terminal/Terminal.cs +++ b/Source/BookGen/Infrastructure/Terminal/Terminal.cs @@ -1,9 +1,10 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Reflection; +using System.Runtime.InteropServices; using System.Xml.Serialization; using Spectre.Console; @@ -14,36 +15,6 @@ public static class Terminal { private static readonly Palette _palette = new(); - public static void Table(string[] headers, IEnumerable rows) - { - var table = new Table(); - table.AddColumns(headers); - - foreach (var row in rows) - { - table.AddRow(row); - } - - AnsiConsole.Write(table); - } - - public static void BarChart(IDictionary items, string title = "") - { - BarChart chart = new BarChart() - .Width(Console.WindowWidth) - .Label(title) - .CenterLabel(); - - _palette.Reset(); - - foreach (KeyValuePair item in items) - { - chart.AddItem(item.Key, item.Value, _palette.GetNextColor()); - } - - AnsiConsole.Write(chart); - } - public static void BreakDownChart(IDictionary items, string title, bool descendingOrder = true) { Rule rule = new Rule(title).Centered(); @@ -76,12 +47,6 @@ public static void Header(string title, int blankLineBefore = 0, int blankLineAf AnsiConsole.WriteLine(); } - public static bool Confirm(string message) - { - var prompt = new ConfirmationPrompt(message); - return prompt.Show(AnsiConsole.Console); - } - public static List SelectionMenu(IEnumerable items, string title, string instructions, Func displaySelector) where T : notnull { MultiSelectionPrompt prompt = new MultiSelectionPrompt() diff --git a/Source/BookGen/Infrastructure/Terminal/WigetBase.cs b/Source/BookGen/Infrastructure/Terminal/WigetBase.cs new file mode 100644 index 00000000..37997867 --- /dev/null +++ b/Source/BookGen/Infrastructure/Terminal/WigetBase.cs @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.Infrastructure.Terminal; + +/// +/// Base class for wigets +/// +public abstract class WigetBase +{ + /// + /// Gets if the wiget is currently shown. + /// + protected bool IsShowing { get; private set; } + + /// + /// Gets if the wiget is currently shown in the alternate buffer. + /// + protected bool IsAlternateBuffer { get; private set; } + + /// + /// Shows the wiget. If useAlternateBuffer is true, the wiget will be shown in the alternate buffer. + /// + /// id set to true the wiget will be shown in the alternate buffer. + public void Show(bool useAlternateBuffer) + { + IsShowing = true; + IsAlternateBuffer = useAlternateBuffer; + if (IsAlternateBuffer) + Webmaster442.WindowsTerminal.Terminal.SwitchToAlternateBuffer(); + OnShow(); + } + + /// + /// Hides the wiget + /// + public void Hide() + { + OnHide(); + if (IsAlternateBuffer) + Webmaster442.WindowsTerminal.Terminal.SwitchToMainBuffer(); + IsShowing = false; + } + + /// + /// An overridable method that is called when the wiget is shown + /// + public abstract void OnShow(); + + + /// + /// An overridable method that is called when the wiget is hidden + /// + public abstract void OnHide(); +} diff --git a/Source/BookGen/Infrastructure/Tools/Digest.cs b/Source/BookGen/Infrastructure/Tools/Digest.cs index 37eda317..61fd3a06 100644 --- a/Source/BookGen/Infrastructure/Tools/Digest.cs +++ b/Source/BookGen/Infrastructure/Tools/Digest.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen/Infrastructure/Tools/Extractor.cs b/Source/BookGen/Infrastructure/Tools/Extractor.cs index b2bcbe86..fe23ac12 100644 --- a/Source/BookGen/Infrastructure/Tools/Extractor.cs +++ b/Source/BookGen/Infrastructure/Tools/Extractor.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen/Infrastructure/Tools/IDownloadUi.cs b/Source/BookGen/Infrastructure/Tools/IDownloadUi.cs index b285e0da..9135a23e 100644 --- a/Source/BookGen/Infrastructure/Tools/IDownloadUi.cs +++ b/Source/BookGen/Infrastructure/Tools/IDownloadUi.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen/Infrastructure/Tools/ToolInfo.cs b/Source/BookGen/Infrastructure/Tools/ToolInfo.cs index 254513f7..0b47932c 100644 --- a/Source/BookGen/Infrastructure/Tools/ToolInfo.cs +++ b/Source/BookGen/Infrastructure/Tools/ToolInfo.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen/Commands/InputOutputArguments.cs b/Source/BookGen/InputOutputArguments.cs similarity index 74% rename from Source/BookGen/Commands/InputOutputArguments.cs rename to Source/BookGen/InputOutputArguments.cs index 50f602e5..ebaa53e2 100644 --- a/Source/BookGen/Commands/InputOutputArguments.cs +++ b/Source/BookGen/InputOutputArguments.cs @@ -1,19 +1,23 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using System.ComponentModel; + using BookGen.Cli; using BookGen.Cli.Annotations; -namespace BookGen.Commands; +namespace BookGen; public class InputOutputArguments : ArgumentsBase { - [Switch("i", "input")] + [Switch("i", "input", Required = true)] + [Description("Required argument. Specifies the input file.")] public string InputFile { get; set; } - [Switch("o", "output")] + [Switch("o", "output", Required = true)] + [Description("Required argument. Specifies the output file or path")] public string OutputFile { get; set; } public InputOutputArguments() diff --git a/Source/BookGen/Program.cs b/Source/BookGen/Program.cs index 52902207..6e6ed9eb 100644 --- a/Source/BookGen/Program.cs +++ b/Source/BookGen/Program.cs @@ -5,13 +5,13 @@ using System.Diagnostics; -using Bookgen.Lib.AppSettings; - using BookGen; using BookGen.Cli; using BookGen.Commands; +using BookGen.GlobalOptionParsers; using BookGen.Infrastructure; using BookGen.Infrastructure.Loging; +using BookGen.Lib.AppSettings; using BookGen.Shell.Shared.Loging; using BookGen.Vfs; @@ -22,8 +22,6 @@ ProgramInfo info = new(); -List argumentList = ProgramConfigurator.ParseGeneralArgs(args, info); - using ILoggerFactory factory = LoggerFactory .Create(builder => { @@ -46,16 +44,14 @@ ILogger logger = factory.CreateLogger("Bookgen"); CommandRunnerProxy runnerProxy = new(); -var helpProvider = new HelpProvider(logger, runnerProxy); - var ioc = new ServiceCollection(); ioc.AddMemoryCache(); ioc.AddSingleton(logger); ioc.AddSingleton(info); ioc.AddSingleton(runnerProxy); +ioc.AddSingleton(); ioc.AddSingleton(ZipAssetSoruce.DefaultAssets()); ioc.AddSingleton(); -ioc.AddSingleton(helpProvider); ioc.AddTransient(); ioc.AddTransient(); ioc.AddTransient(); @@ -74,7 +70,7 @@ using ServiceProvider provider = ioc.BuildServiceProvider(); -CommandRunner runner = new(provider, helpProvider, logger, new CommandRunnerSettings +CommandRunner runner = new(provider, new CommandHelpProvider(), logger, new CommandRunnerSettings { UnknownCommandCodeAndMessage = (-1, "Unknown command"), BadParametersExitCode = 2, @@ -82,6 +78,7 @@ PlatformNotSupportedExitCode = 4, EnableUtf8Output = true, PrintHelpOnBadArgs = true, + ProgramMetaData = ProgramMetaData.FromExecutingAssembly(), }) { ExceptionHandlerDelegate = OnException, @@ -89,16 +86,21 @@ }; runner - .AddDefaultCommand() - .AddCommandsFrom(typeof(HelpCommand).Assembly); + .AddGlobalOptionParser() + .AddGlobalOptionParser() + .AddGlobalOptionParser(new JsonLogParser(info)) + .AddGlobalOptionParser(new LogToFileParser(info)) + .AddGlobalOptionParser(new RuntimePrintingParser(info)); -runnerProxy.ConfigureWith(runner); +runner + .AddDefaultCommand() + .AddCommandsFrom(typeof(DefaultCommand).Assembly, includeDefault: false); -helpProvider.VerifyHelpData(); +runnerProxy.ConfigureWith(runner); Stopwatch stopwatch = Stopwatch.StartNew(); -int exitCode = await runner.Run(argumentList); +int exitCode = await runner.Run(args); stopwatch.Stop(); diff --git a/Source/BookGen/ProgramInfo.cs b/Source/BookGen/ProgramInfo.cs index 8a12c589..b509fa9f 100644 --- a/Source/BookGen/ProgramInfo.cs +++ b/Source/BookGen/ProgramInfo.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Reflection; -using Bookgen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Domain.IO.Configuration; using Microsoft.Extensions.Logging; diff --git a/Source/BookGen/Properties/launchSettings.json b/Source/BookGen/Properties/launchSettings.json index ab444ce0..da26a92a 100644 --- a/Source/BookGen/Properties/launchSettings.json +++ b/Source/BookGen/Properties/launchSettings.json @@ -5,37 +5,40 @@ "distributionName": "" }, "BookGen": { - "commandName": "Project", - "commandLineArgs": "Addfrontmatter ", - "workingDirectory": "G:\\HelloCsharp\\C" - }, - "TerminalInstall -t": { - "commandName": "Project", - "commandLineArgs": "terminalinstall -t" + "commandName": "Project" }, "Gui": { "commandName": "Project", "commandLineArgs": "Gui" }, - "Schemas": { + "book Links --verify": { "commandName": "Project", - "commandLineArgs": "schemas" + "commandLineArgs": "book links --verify", + "workingDirectory": "G:\\Konyv\\hellocsharp" }, - "md2html": { + "document schemas": { "commandName": "Project", - "commandLineArgs": "md2html -i schemas.md -o output.html" + "commandLineArgs": "document schemas" + }, + "document changelog": { + "commandName": "Project", + "commandLineArgs": "document changelog" }, "Migrate": { "commandName": "Project", "commandLineArgs": "migrate -d g:\\HelloCsharp\\C\\" }, - "Math2Svg": { + "convert md2html": { "commandName": "Project", - "commandLineArgs": "math2svg -f \"e=m \\cdot c^2\" -o test.svg -s 15" + "commandLineArgs": "convert md2html -i schemas.md -o output.html" }, - "QrCode": { + "convert Math2Svg": { "commandName": "Project", - "commandLineArgs": "qrcode -d test -o qrcode_test.svg -c #ff0000" + "commandLineArgs": "convert math2svg -f \"e=m \\cdot c^2\" -o test.svg -s 15" + }, + "convert QrCode": { + "commandName": "Project", + "commandLineArgs": "convert qrcode -d test -o qrcode_test.svg -c #ff0000" }, "Tools": { "commandName": "Project", @@ -49,10 +52,18 @@ "commandName": "Project", "commandLineArgs": "help" }, - "Links --verify": { + "commands": { "commandName": "Project", - "commandLineArgs": "links --verify", - "workingDirectory": "G:\\Konyv\\hellocsharp" + "commandLineArgs": "commands", + "workingDirectory": "p:\\" + }, + "Shell": { + "commandName": "Project", + "commandLineArgs": "Shell -nr bookgen assemblydo" + }, + "Build sampleplugin": { + "commandName": "Project", + "commandLineArgs": "build plugin ..\\..\\Source\\BookGen.SamplePlugin\\bin\\Debug\\netstandard2.1\\BookGen.SamplePlugin.dll -v -d ..\\..\\Docs -dev -o ..\\SamplePluginOutput" } } } \ No newline at end of file diff --git a/Source/BookGen/Resources/.gitkeep b/Source/BookGen/Resources/.gitkeep new file mode 100644 index 00000000..69ac10dc --- /dev/null +++ b/Source/BookGen/Resources/.gitkeep @@ -0,0 +1 @@ +# Folder for embedded resources \ No newline at end of file diff --git a/Source/BookGen/Resources/default.txt b/Source/BookGen/Resources/default.txt new file mode 100644 index 00000000..5b4b2ca1 --- /dev/null +++ b/Source/BookGen/Resources/default.txt @@ -0,0 +1,24 @@ + ______________________________ +/ [green bold]BookGen[/] - [green italic]A C# documentation[/] \ +\ [green italic]generator[/] / + ------------------------------ + \ + \ + + |\_/| + |o o|__ + --*--__\ + C_C_(___) +----------------------------------------------------------------------------------------------------------------------- + +[italic]BookGen is a command line toolchain for writing books and documentation in markdown. It was inspired by GitBook. +Generally speaking you can call it a static website generator, but it has some neat features compared to other products.[/] + +To get started read the docs by executing the following commands: + +* [bold]bookgen documents manual[/] +* [bold]bookgen documents commands[/] + +To get help on a command type [bold]bookgen help [/]. + +To list the availble command tree type [bold]bookgen commands[/]. \ No newline at end of file diff --git a/Source/BookGen/Tooldownloaders/ChromaDownloader.cs b/Source/BookGen/Tooldownloaders/ChromaDownloader.cs index a36a894a..1b0f7bfd 100644 --- a/Source/BookGen/Tooldownloaders/ChromaDownloader.cs +++ b/Source/BookGen/Tooldownloaders/ChromaDownloader.cs @@ -1,11 +1,10 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.Github; - using BookGen.Infrastructure.Tools; +using BookGen.Lib.Domain.Github; using BookGen.Vfs; using Microsoft.Extensions.Logging; diff --git a/Source/BookGen/Tooldownloaders/CopyPartyDownloader.cs b/Source/BookGen/Tooldownloaders/CopyPartyDownloader.cs index 2ad7b07c..a9a217fa 100644 --- a/Source/BookGen/Tooldownloaders/CopyPartyDownloader.cs +++ b/Source/BookGen/Tooldownloaders/CopyPartyDownloader.cs @@ -1,11 +1,10 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.Github; - using BookGen.Infrastructure.Tools; +using BookGen.Lib.Domain.Github; using BookGen.Vfs; using Microsoft.Extensions.Logging; diff --git a/Source/BookGen/Tooldownloaders/GithubDownloader.cs b/Source/BookGen/Tooldownloaders/GithubDownloader.cs index 1222e20e..b1ce9e6b 100644 --- a/Source/BookGen/Tooldownloaders/GithubDownloader.cs +++ b/Source/BookGen/Tooldownloaders/GithubDownloader.cs @@ -1,11 +1,10 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.Github; - using BookGen.Infrastructure.Tools; +using BookGen.Lib.Domain.Github; using BookGen.Vfs; using Microsoft.Extensions.Logging; diff --git a/Source/BookGen/Tooldownloaders/GlowDownloader.cs b/Source/BookGen/Tooldownloaders/GlowDownloader.cs index 985b11f1..1d896cee 100644 --- a/Source/BookGen/Tooldownloaders/GlowDownloader.cs +++ b/Source/BookGen/Tooldownloaders/GlowDownloader.cs @@ -1,11 +1,10 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.Github; - using BookGen.Infrastructure.Tools; +using BookGen.Lib.Domain.Github; using BookGen.Vfs; using Microsoft.Extensions.Logging; diff --git a/Source/BookGen/Tooldownloaders/MicrosoftEditToolDownloader.cs b/Source/BookGen/Tooldownloaders/MicrosoftEditToolDownloader.cs index 69472b04..3b6fa854 100644 --- a/Source/BookGen/Tooldownloaders/MicrosoftEditToolDownloader.cs +++ b/Source/BookGen/Tooldownloaders/MicrosoftEditToolDownloader.cs @@ -1,11 +1,10 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.Github; - using BookGen.Infrastructure.Tools; +using BookGen.Lib.Domain.Github; using BookGen.Vfs; using Microsoft.Extensions.Logging; diff --git a/Source/BookGen/Tooldownloaders/PandocTooldownloader.cs b/Source/BookGen/Tooldownloaders/PandocTooldownloader.cs index 5f6edb48..acf2fd87 100644 --- a/Source/BookGen/Tooldownloaders/PandocTooldownloader.cs +++ b/Source/BookGen/Tooldownloaders/PandocTooldownloader.cs @@ -1,11 +1,10 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.Github; - using BookGen.Infrastructure.Tools; +using BookGen.Lib.Domain.Github; using BookGen.Vfs; using Microsoft.Extensions.Logging; diff --git a/Source/BookGen/Tooldownloaders/ToolDownloadUi.cs b/Source/BookGen/Tooldownloaders/ToolDownloadUi.cs index 0bd06bd2..8aa906da 100644 --- a/Source/BookGen/Tooldownloaders/ToolDownloadUi.cs +++ b/Source/BookGen/Tooldownloaders/ToolDownloadUi.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Source/BookGen/Tooldownloaders/TooldownloaderBase.cs b/Source/BookGen/Tooldownloaders/TooldownloaderBase.cs index ae8355a8..26a18b59 100644 --- a/Source/BookGen/Tooldownloaders/TooldownloaderBase.cs +++ b/Source/BookGen/Tooldownloaders/TooldownloaderBase.cs @@ -1,11 +1,10 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.Github; - using BookGen.Infrastructure.Tools; +using BookGen.Lib.Domain.Github; using BookGen.Vfs; using Microsoft.Extensions.Logging; diff --git a/Source/BookGen/packages.lock.json b/Source/BookGen/packages.lock.json index f3efe4c1..b7e2c379 100644 --- a/Source/BookGen/packages.lock.json +++ b/Source/BookGen/packages.lock.json @@ -4,76 +4,76 @@ "net10.0": { "HtmlToOpenXml.dll": { "type": "Direct", - "requested": "[3.4.1, )", - "resolved": "3.4.1", - "contentHash": "JfjqNFWbELi++XjM6hDRmfKG039QKrQdzZCPgbGZY4SSVIXkqJR6TNQmB1l+n0vR0x0zietKScE6syS28HZ4fQ==", + "requested": "[3.5.0, )", + "resolved": "3.5.0", + "contentHash": "ULQqQidtvWfGhds11YWG0uAkRYGXFzn3mNA6ZVnByJUV/1pqkR4fVAF3f7V1cSbpdSWCwoPtFbAef//rUNCmKA==", "dependencies": { - "AngleSharp": "1.4.0", + "AngleSharp": "1.5.0", "DocumentFormat.OpenXml": "3.5.1", "Microsoft.Extensions.Logging.Abstractions": "6.0.0" } }, "Markdig": { "type": "Direct", - "requested": "[1.3.0, )", - "resolved": "1.3.0", - "contentHash": "1cWDY3Rhd24SVe66p2ekhEPhaSAXuH3WgGn6EPNjqXL0Y4ycK7GXtq0UE5oeBYircNlqJIEQk9W2vz60hRaezA==" + "requested": "[1.3.2, )", + "resolved": "1.3.2", + "contentHash": "fZgOC/3CswUrndjDTac70aQpYdtxbW5+5bRumR7vzvI2HJbkmgKisB1c9oT+GA6v0jB/JDR9BLa9FiPzQmaK6A==" }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "5fGxcw2vuYp8s0wio9H1ECiuk4iKSdTIlNuigdLIrkhg+5XAwgFVDB/5Ots3pfN/QhABLYXutA79JFtnUKDSHA==", + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "4ZFBNE+jzR+CrWWlhOesnmywCW7pYKT0dxyAQRdL11yJwxe4jvcAu31eorFtEkoFeCDcUTeNssgPv2yaRRptaQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.9" + "Microsoft.Extensions.Primitives": "10.0.10" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "G9mregdatGWMCQWeCw012LDeJVP7G/XIxH8Ddbjc8bD1//dA+8VVQdcRE9jI1moyoJxSSZhHITUnNQ8FUDl5+Q==", + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "N1w5H7uK6gCTnCBZAWzE0/EQYSPysij/uYwDqntqBVvBa6bjMmBKitsnEFd6yh/SX3wLm67nO6+OnZ84K+gZWg==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.9", - "Microsoft.Extensions.Logging.Abstractions": "10.0.9", - "Microsoft.Extensions.Options": "10.0.9", - "Microsoft.Extensions.Primitives": "10.0.9" + "Microsoft.Extensions.Caching.Abstractions": "10.0.10", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10", + "Microsoft.Extensions.Logging.Abstractions": "10.0.10", + "Microsoft.Extensions.Options": "10.0.10", + "Microsoft.Extensions.Primitives": "10.0.10" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "NijozhERJDIaJ4k5TSMy1jOi0cSC2HfkvRD/Sl+kGSSKgVbFnF4GxgtMN/MrzHB8D1JxIrD4xSer9Blh9v3axQ==", + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "ANyvsgkNBRvcJh2XLgn8veGmajf+8m0AbKK+HPWdRL1yraSNVVSmQhFntLtdz/C795jxqqup+k05cs/3jZQPOA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "g41l/30G3K4B/d/L8kjux0+30e27c8D0FVQ/PFCpbekgfDpj9mnDhieP67EqXWvl1EWNeZh2rpR4F5B/jcDOHA==" + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "z/2xXlFw2aLGjHyEm6E0tQ+In6VfzQzTrtArbQ2c0TQE16ZbyDCMGPvaUT9I0s8rgy9sRWlU2P9waW37qV04qA==" }, "Microsoft.Extensions.Logging": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "N7Gm9SjugYjmmnhwbBKC9DFqGqjfJvh6YfOJgtwh0AW0Xpok3dIVors1ik050XmUxKAgAc7nNngDIJyFb06K2g==", + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "Tf6z5HsL0VDYRTfvsoNrTGHGheCwkTsZBA2FFh5ATJUbkAwug+FFNISJK2gjpUNemlAOoWllAK52HOWCjto3EQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "10.0.9", - "Microsoft.Extensions.Logging.Abstractions": "10.0.9", - "Microsoft.Extensions.Options": "10.0.9" + "Microsoft.Extensions.DependencyInjection": "10.0.10", + "Microsoft.Extensions.Logging.Abstractions": "10.0.10", + "Microsoft.Extensions.Options": "10.0.10" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "9S/DFt4cohlMPpzIxjG6kk0L8MuN2vDm9pbMCulxtJzzk82oJHVLBd8vuQxaPskaYQwKqmFmbannf5eoChgjYg==", + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "zkFxGYUvdxAvIKTyXHrmW+Sux53D4SezD9dMyZ6hrwwzPQJNuwCRy1f5W7AvYTqacEGhWF2XderRQG1OvbV8og==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10" } }, "Microsoft.IO.RecyclableMemoryStream": { @@ -82,13 +82,19 @@ "resolved": "3.0.1", "contentHash": "s/s20YTVY9r9TPfTrN5g8zPF1YhwxyqO6PxUkrYTGI2B+OGPe9AdajWZrLhFqXIvqIW23fnUE4+ztrUWNU1+9g==" }, + "Roslynator.Analyzers": { + "type": "Direct", + "requested": "[4.15.0, )", + "resolved": "4.15.0", + "contentHash": "E8fu71Y+vzhniJKy1K13Rzwm8Vjcj7CDFBpflqncKUZfc3SzuF0kMBiyMmgXNPkjWTdE3Z+30HlGgYjHONZY/Q==" + }, "Spectre.Console": { "type": "Direct", - "requested": "[0.57.0, )", - "resolved": "0.57.0", - "contentHash": "X5+4ZsUYTPlTs5PEoKRlxUuKOa1AkEoAGM34pUeJPjsBdtOnvnaVb31hqewhooomihl1i9fEBid1fz8XxZTacQ==", + "requested": "[0.57.2, )", + "resolved": "0.57.2", + "contentHash": "wzsB+P9i6F9G+cFOJxRotCiQWNSzZVAMxi8YMiHlGXXU1JLMXGp7zSSqBMmROZS2bzpK5jk+Saa7Evp8HwqhKg==", "dependencies": { - "Spectre.Console.Ansi": "0.57.0" + "Spectre.Console.Ansi": "0.57.2" } }, "Spectre.Console.Analyzer": { @@ -103,12 +109,6 @@ "resolved": "7.0.1", "contentHash": "Ha0x66QWA+IPj2kJYoweA8MSnB1tymBW6kAIS6Kkj/U3SlMLCBPjDlUXzrrY1zJAxVItrGpFIXjkl3eEIYIE9A==" }, - "XmlDocMarkdown.Core": { - "type": "Direct", - "requested": "[2.9.0, )", - "resolved": "2.9.0", - "contentHash": "jlAslft87MA7mlUfU0YR6x4D6BpguIXB8VQ5bKh2D6XbphfNaLjhIGsTKGphiEahl3FGnOBcB8PFD/xaagij/w==" - }, "DocumentFormat.OpenXml": { "type": "Transitive", "resolved": "3.5.1", @@ -197,17 +197,17 @@ }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "hyNdX4c2UwkRkzb9byw0H2DQkRzwBM3mzY2sCM9egwzTyg8dvQJmp5noQHGEaaCORQrNK3DD2gREBsc2DlXS4A==", + "resolved": "10.0.10", + "contentHash": "srnhnk7nE8krBiIXp71LvBmKBtraBONWSRzdjJgRv1Ko9Mp8IVNqv4vIS9hGeVteBig8aQkva9ZG+sC+o5sVcA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.9", - "Microsoft.Extensions.Primitives": "10.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.10", + "Microsoft.Extensions.Primitives": "10.0.10" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "fmEbAUFsaIKirgLt/lYhuFRBwhcSJN31jjHgCdbQxJiWOum6EdLjkbgGuukSP9z/a+9LibaxII/kF+GwOXgC4g==" + "resolved": "10.0.10", + "contentHash": "5wu/GrYVd8mG2DVUw3vFJzF+O336TyTGg/Kmcgw9bfwYhCoFiV5lR5QeEmKecJyrW4W54nMfD3p3589E8a7czQ==" }, "Newtonsoft.Json": { "type": "Transitive", @@ -221,13 +221,13 @@ }, "SkiaSharp.NativeAssets.macOS": { "type": "Transitive", - "resolved": "3.119.4", - "contentHash": "fgBOWEqbY012x7gMfJU4ezgz6dfhJb30Z6YdW35h85Zoe39+a8YNbAAwL29ihPfWoppg5AjvyKNzD1oCvlqWwA==" + "resolved": "4.150.1", + "contentHash": "r755HVwaHZhyf1clWjrM2/RoOZYCzkQEmE9pu/mVsebPejWu52niNPUwtfyf112qoF0PIk6OndqVOUoITj6TwQ==" }, "Spectre.Console.Ansi": { "type": "Transitive", - "resolved": "0.57.0", - "contentHash": "DGeYljyWAIHSeUAoUGqyzyOqS8WlsHKW+y4zOZ0OO61oqrhx90jHS+XG5Kpq/wWz6ovIs2VL/0CTKpUVa7tdWg==" + "resolved": "0.57.2", + "contentHash": "Y1+u73shwP+JYHmrkdN6bSt2kaGBEAafhi2nrecczbXdFRey+k7J/WF9YPfcB0rqivv2S8fmif6FSBq9m/AFvw==" }, "Svg.Animation": { "type": "Transitive", @@ -272,12 +272,15 @@ "resolved": "10.0.2", "contentHash": "JTpM4z0wpoIHHDvlCU27HsXo+zVnpWib94HXQpzzr+jc/P9NYf4w353AK4MXyGq/grm1mbLi7eXsOsDU8sGmNg==" }, + "bookgen.api": { + "type": "Project" + }, "bookgen.cli": { "type": "Project", "dependencies": { "BookGen.Vfs": "[1.0.0, )", - "Microsoft.Extensions.DependencyInjection.Abstractions": "[10.0.9, )", - "Microsoft.Extensions.Logging.Abstractions": "[10.0.9, )" + "Microsoft.Extensions.DependencyInjection.Abstractions": "[10.0.10, )", + "Microsoft.Extensions.Logging.Abstractions": "[10.0.10, )" } }, "bookgen.contents": { @@ -286,27 +289,27 @@ "bookgen.lib": { "type": "Project", "dependencies": { - "AngleSharp": "[1.5.1, )", + "AngleSharp": "[1.5.2, )", "BookGen.Shell.Shared": "[1.0.0, )", "BookGen.Vfs": "[1.0.0, )", - "Markdig": "[1.3.0, )", + "Markdig": "[1.3.2, )", "Microsoft.ClearScript": "[7.5.1, )", "Microsoft.ClearScript.V8.Native.linux-x64": "[7.5.1, )", "Microsoft.ClearScript.V8.Native.win-x64": "[7.5.1, )", - "PreMailer.Net": "[2.7.2, )", - "SkiaSharp": "[3.119.4, )", - "SkiaSharp.NativeAssets.Linux": "[3.119.4, )", - "SkiaSharp.NativeAssets.Win32": "[3.119.4, )", + "PreMailer.Net": "[2.7.3, )", + "SkiaSharp": "[4.150.1, )", + "SkiaSharp.NativeAssets.Linux": "[4.150.1, )", + "SkiaSharp.NativeAssets.Win32": "[4.150.1, )", "Svg.Skia": "[5.1.1, )", - "System.ServiceModel.Syndication": "[10.0.9, )", - "YamlDotNet": "[18.0.0, )" + "System.ServiceModel.Syndication": "[10.0.10, )", + "YamlDotNet": "[18.1.0, )" } }, "bookgen.shell.shared": { "type": "Project", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "[10.0.9, )", - "Spectre.Console": "[0.57.0, )", + "Microsoft.Extensions.Logging.Abstractions": "[10.0.10, )", + "Spectre.Console": "[0.57.2, )", "Webmaster442.WindowsTerminal": "[4.1.1, )" } }, @@ -315,9 +318,9 @@ }, "AngleSharp": { "type": "CentralTransitive", - "requested": "[1.5.1, )", - "resolved": "1.5.1", - "contentHash": "jFG0tKwFMNzIdv0rX3qvyiKyxAzUIvDyTLTJo2ORKtv/hiJC8juZTknc4b9DQmBFbrky+0twr58QQWt6DR48Og==" + "requested": "[1.5.2, )", + "resolved": "1.5.2", + "contentHash": "LVZ7rrr6GHbhERhTgVDarVygl1e6KwE96pronA90LBOMacDhxR4lB+xYLjn/vlnUu+xbOB2RM9fuCPEx5bdyWA==" }, "Microsoft.ClearScript": { "type": "CentralTransitive", @@ -346,34 +349,34 @@ }, "PreMailer.Net": { "type": "CentralTransitive", - "requested": "[2.7.2, )", - "resolved": "2.7.2", - "contentHash": "OeN00ZA9ycJSSD4ZcjbWn5aJpJ+OfrAM3ti+9VI9Mc+o90QnWayPCU7PszopLa91lxKrL9ZnIcG33AAd5dypLA==", + "requested": "[2.7.3, )", + "resolved": "2.7.3", + "contentHash": "p6LB7Qu4ZsPmjiFtWmytiERhopuGxq3Ql/SVuOZueGzfrDNvvSi8sGaDVHVvJt0S+RCHU422TWLu2aMXDE9ejg==", "dependencies": { - "AngleSharp": "1.1.0" + "AngleSharp": "1.5.1" } }, "SkiaSharp": { "type": "CentralTransitive", - "requested": "[3.119.4, )", - "resolved": "3.119.4", - "contentHash": "53NOSUZ1Us+91Sm0uCkIivh/k7jOowRErZT2sIWwPFN9mLUvdxnE6rS4sWo4255+Rd2MWUSF+j0NMZHD6Cke+Q==", + "requested": "[4.150.1, )", + "resolved": "4.150.1", + "contentHash": "5v3T8X1N62Dp+AkPO70GNBNS/NRBPGMOTiN+Prg33sZAcm/Ug3YOAH+3RTj/jxJV8NGTJs2idGpC2Qdae2mGLQ==", "dependencies": { - "SkiaSharp.NativeAssets.Win32": "3.119.4", - "SkiaSharp.NativeAssets.macOS": "3.119.4" + "SkiaSharp.NativeAssets.Win32": "4.150.1", + "SkiaSharp.NativeAssets.macOS": "4.150.1" } }, "SkiaSharp.NativeAssets.Linux": { "type": "CentralTransitive", - "requested": "[3.119.4, )", - "resolved": "3.119.4", - "contentHash": "UAyVzbqNfZsZbKbzj68zXLyUyF/SbTKmzTfOO6qDu++dtIUMMTzPBe8oOuzU/DiewpfKoUUlOSsJmqWc6blxBw==" + "requested": "[4.150.1, )", + "resolved": "4.150.1", + "contentHash": "7IZtljoTBp0EH7/6K3p3yey44db3W7hkV9UPdCOaQQ2TxFkmFOpXwOPiQ865DrG+szgp0aKpQs8vYrzRdF83Ng==" }, "SkiaSharp.NativeAssets.Win32": { "type": "CentralTransitive", - "requested": "[3.119.4, )", - "resolved": "3.119.4", - "contentHash": "XOpbx/4CReO2wYsq2s6rbvdauc6dntG4Zv499sHGTJ87bwZaFXszFkwql3+FIZMc8kUPeaj3Mx2ezIJmo8a1Kg==" + "requested": "[4.150.1, )", + "resolved": "4.150.1", + "contentHash": "qrLSL8OonbkMJdSH8heK0Jl39Y2xxZGdd7Ru4cyBXk2ITdx+Fu1sCFLmvCKmnMaYs//mlN/YSpSpOurEXErcQw==" }, "Svg.Skia": { "type": "CentralTransitive", @@ -394,9 +397,9 @@ }, "System.ServiceModel.Syndication": { "type": "CentralTransitive", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "ZxViVHpGq/a/lZsNZlxeAO1e+U0ViqtB99gkQj0W8rD7FmIs1iyL7Ip8d7w9S76EZhRW8Obc74zuoIJpITqD6A==" + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "jU3WQG477Tk7RYlM2Ky8oySb6WHd744LgK7MYRMe4L8FcoJI3Gbpcf29sB2lIlJVS82ah9i4FUw3RfbXeJs8jw==" }, "Webmaster442.WindowsTerminal": { "type": "CentralTransitive", @@ -406,9 +409,9 @@ }, "YamlDotNet": { "type": "CentralTransitive", - "requested": "[18.0.0, )", - "resolved": "18.0.0", - "contentHash": "ptHVgcYmLejGuWXV7RMFoEqFKYMXnieOlWLPzEslfDtzZ9ngMhjYwykfqjBN2+fMEAEyobozkj07lKEpR4dssA==" + "requested": "[18.1.0, )", + "resolved": "18.1.0", + "contentHash": "5K+9KFg2TdTl7VXv88Qzi/0lqK6JFoNP3lRuImPYGRV7K/QYklDyTrj4+A+KAki1JsQi6qKY+hDyY7d6WRqjrw==" } } } diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/DelimiterInlineRenderer.cs b/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/DelimiterInlineRenderer.cs deleted file mode 100644 index 15c46d99..00000000 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/DelimiterInlineRenderer.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Markdig.Syntax.Inlines; - -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Terminal; - -internal sealed class DelimiterInlineRenderer : TerminalObjectRenderer -{ - protected override void Write(TerminalRenderer renderer, DelimiterInline obj) - { - renderer.Write(obj.ToLiteral()); - renderer.WriteChildren(obj); - } -} diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/LineBreakInlineRenderer.cs b/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/LineBreakInlineRenderer.cs deleted file mode 100644 index 2249068e..00000000 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/LineBreakInlineRenderer.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Markdig.Syntax.Inlines; - -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Terminal; - -internal sealed class LineBreakInlineRenderer : TerminalObjectRenderer -{ - protected override void Write(TerminalRenderer renderer, LineBreakInline obj) - { - if (obj.IsHard) - { - renderer.WriteLine(); - } - renderer.EnsureLine(); - } -} diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/LiteralInlineRenderer.cs b/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/LiteralInlineRenderer.cs deleted file mode 100644 index 24a30fc8..00000000 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/LiteralInlineRenderer.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Markdig.Syntax.Inlines; - -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Terminal; - -internal sealed class LiteralInlineRenderer : TerminalObjectRenderer -{ - protected override void Write(TerminalRenderer renderer, LiteralInline obj) - { - string content = obj.Content.ToString(); - renderer.Write(content); - } -} diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/ParagraphRenderer.cs b/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/ParagraphRenderer.cs deleted file mode 100644 index 4b67f2c8..00000000 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/ParagraphRenderer.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Markdig.Syntax; - -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Terminal; - -internal sealed class ParagraphRenderer : TerminalObjectRenderer -{ - protected override void Write(TerminalRenderer renderer, ParagraphBlock obj) - { - renderer - .WriteLeafInline(obj) - .WriteLine() - .WriteLine(); - } -} diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/TerminalObjectRenderer.cs b/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/TerminalObjectRenderer.cs deleted file mode 100644 index f920d205..00000000 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/TerminalObjectRenderer.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Markdig.Renderers; -using Markdig.Syntax; - -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Terminal; - -public abstract class TerminalObjectRenderer : MarkdownObjectRenderer where TObject : MarkdownObject -{ -} diff --git a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/ThematicBreakRenderer.cs b/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/ThematicBreakRenderer.cs deleted file mode 100644 index ae3b2d8b..00000000 --- a/Source/Bookgen.Lib/Rendering/Markdown/Renderers/Terminal/ThematicBreakRenderer.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Markdig.Syntax; - -namespace Bookgen.Lib.Rendering.Markdown.Renderers.Terminal; - -internal sealed class ThematicBreakRenderer : TerminalObjectRenderer -{ - protected override void Write(TerminalRenderer renderer, ThematicBreakBlock obj) - { - renderer.WriteLine(new string('-', renderer.RenderOptions.Width)); - renderer.WriteLine(); - } -} diff --git a/Source/Bookgen.Lib/Rendering/Templates/FunctionExtensions.cs b/Source/Bookgen.Lib/Rendering/Templates/FunctionExtensions.cs deleted file mode 100644 index 2bac43bd..00000000 --- a/Source/Bookgen.Lib/Rendering/Templates/FunctionExtensions.cs +++ /dev/null @@ -1,37 +0,0 @@ -//----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor -// This code is licensed under MIT license (see LICENSE for details) -//----------------------------------------------------------------------------- - -using System.Globalization; -using System.Text; - -namespace Bookgen.Lib.Rendering.Templates; - -internal static class FunctionExtensions -{ - public static TValue GetValueOrDefault(this IReadOnlyList arguments, - int index, - TValue defaultValue) - where TValue : IParsable - { - if (index < 0 || index >= arguments.Count) - return defaultValue; - var value = arguments[index]; - - if (TValue.TryParse(value, CultureInfo.InvariantCulture, out TValue? parsedValue)) - return parsedValue; - - return defaultValue; - } - - public static string ToJavacriptHtmlElement(this string code) - { - var sb = new StringBuilder(code.Length + 10); - sb.Append(""); - return sb.ToString(); - } - -} diff --git a/Source/Bookgen.Lib/Rendering/Templates/Functions.cs b/Source/Bookgen.Lib/Rendering/Templates/Functions.cs deleted file mode 100644 index 262bf173..00000000 --- a/Source/Bookgen.Lib/Rendering/Templates/Functions.cs +++ /dev/null @@ -1,36 +0,0 @@ -//----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor -// This code is licensed under MIT license (see LICENSE for details) -//----------------------------------------------------------------------------- - -using BookGen.Vfs; - -namespace Bookgen.Lib.Rendering.Templates; - -public sealed class DefaultFunctions -{ - private readonly IAssetSource _assetSource; - - public DefaultFunctions(IAssetSource assetSource) - { - _assetSource = assetSource; - } - - public string BuildDate(string[] arguments) - => DateTime.Now.ToString(arguments.GetValueOrDefault(0, "yy-MM-dd hh:mm:ss")); - - public string JSPageToc(string[] arguments) - { - string contentsDiv = arguments.GetValueOrDefault(0, string.Empty); - string targetDiv = arguments.GetValueOrDefault(1, string.Empty); - - if (string.IsNullOrEmpty(contentsDiv) || string.IsNullOrEmpty(targetDiv)) - throw new ArgumentException("JSPageToc requires two arguments: contentsDiv and targetDiv."); - - var pagetoc = _assetSource.GetAsset(BundledAssets.JsPageToc); - - string code = pagetoc.Replace("{{contents}}", contentsDiv).Replace("{{target}}", targetDiv); - - return code.ToJavacriptHtmlElement(); - } -} diff --git a/Source/Bookgen.Lib/Rendering/Templates/TemplateEngine.cs b/Source/Bookgen.Lib/Rendering/Templates/TemplateEngine.cs deleted file mode 100644 index f4904962..00000000 --- a/Source/Bookgen.Lib/Rendering/Templates/TemplateEngine.cs +++ /dev/null @@ -1,124 +0,0 @@ -//----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor -// This code is licensed under MIT license (see LICENSE for details) -//----------------------------------------------------------------------------- - -using System.Text; -using System.Text.RegularExpressions; - -using Bookgen.Lib.Rendering.Templates; - -using BookGen.Vfs; - -using Microsoft.Extensions.Logging; - -namespace Bookgen.Lib.Templates; - -public sealed partial class TemplateEngine -{ - private readonly StringComparer _comparer; - private readonly Dictionary> _lambdaTable; - private readonly ILogger _logger; - private readonly DefaultFunctions _functions; - - public void RegisterFunction(string name, Func function) - { - _lambdaTable.Add(name, function); - } - - public TemplateEngine(ILogger logger, IAssetSource assetSource) - { - _comparer = StringComparer.InvariantCultureIgnoreCase; - _logger = logger; - _functions = new DefaultFunctions(assetSource); - _lambdaTable = new Dictionary>(_comparer) - { - { "BuildDate", _functions.BuildDate }, - { "JSPageToc", _functions.JSPageToc }, - }; - } - - [GeneratedRegex(@"\b([\w\-]+)\s*\(\s*(?:""([^""]*)""(?:\s*,\s*""([^""]*)"")?(?:\s*,\s*""([^""]*)"")?(?:\s*,\s*""([^""]*)"")?(?:\s*,\s*""([^""]*)"")?(?:\s*,\s*""([^""]*)"")?(?:\s*,\s*""([^""]*)"")?(?:\s*,\s*""([^""]*)"")?)?\s*\)")] - private static partial Regex FunctionRegex(); - - [GeneratedRegex(@"\{\{([\w-\(\)\""\, -_])+\}\}")] - private static partial Regex TemplatePartRegex(); - - public string Render(string template, TData viewData) where TData : ViewData - { - using var stringWriter = new StringWriter(new StringBuilder(template.Length + viewData.Content.Length + viewData.Title.Length)); - Render(stringWriter, template, viewData); - return stringWriter.ToString(); - } - - public void Render(TextWriter target, string template, TData viewData) where TData : ViewData - { - Dictionary dataTable = viewData.GetDataTable(_comparer); - - StringBuilder lineBuffer = new(120); - - using StringReader reader = new(template.Replace("{{content}}", viewData.Content, StringComparison.InvariantCultureIgnoreCase)); - string? line; - - while ((line = reader.ReadLine()) != null) - { - MatchCollection templatePartsInLine = TemplatePartRegex().Matches(line); - - if (templatePartsInLine.Count < 1) - { - target.WriteLine(line); - continue; - } - - int lastIndex = 0; - lineBuffer.Clear(); - - foreach (Match templatePart in templatePartsInLine) - { - lineBuffer.Append(line, lastIndex, templatePart.Index - lastIndex); - if (FunctionRegex().IsMatch(templatePart.Value)) - { - string[] templateFunction = FunctionRegex().Split(templatePart.Value); - string functionName = templateFunction.Skip(1).First(); - string[] arguments = templateFunction.Skip(2).TakeWhile(f => f != "}}").ToArray(); - - if (!_lambdaTable.TryGetValue(functionName, out Func? function)) - { - _logger.LogWarning("Function {FunctionName} is not registered.", functionName); - lineBuffer.Append($"Function {functionName} is not registered."); - continue; - } - string result = _lambdaTable[functionName].Invoke(arguments); - lineBuffer.Append(result); - } - else - { - string key = templatePart.Value[2..^2]; - - if (key.Equals("content", StringComparison.InvariantCultureIgnoreCase)) - { - throw new InvalidOperationException("Content found in markdown document. Recursive replacement detected."); - } - - if (!dataTable.TryGetValue(key, out var value)) - { - _logger.LogWarning("Key {Key} is not found in data table.", key); - //lineBuffer.Append($"Key {key} is not found in data table."); - continue; - } - - lineBuffer.Append(dataTable[key]); - - } - lastIndex = templatePart.Index + templatePart.Length; - } - lineBuffer.Append(line, lastIndex, line.Length - lastIndex); - - if (lineBuffer.Length > 0) - { - target.Write(lineBuffer); - target.WriteLine(); - } - } - } -} diff --git a/Test/Bookgen.Tests/Bookgen.Tests.csproj b/Test/Bookgen.Tests/Bookgen.Tests.csproj index 39934bce..1265ed44 100644 --- a/Test/Bookgen.Tests/Bookgen.Tests.csproj +++ b/Test/Bookgen.Tests/Bookgen.Tests.csproj @@ -28,12 +28,16 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - + diff --git a/Test/Bookgen.Tests/Cli/UT_CommandRunner.cs b/Test/Bookgen.Tests/Cli/UT_CommandRunner.cs index 85242a2b..eafae942 100644 --- a/Test/Bookgen.Tests/Cli/UT_CommandRunner.cs +++ b/Test/Bookgen.Tests/Cli/UT_CommandRunner.cs @@ -5,6 +5,7 @@ using BookGen.Cli; using BookGen.Cli.Annotations; +using BookGen.Cli.OpenCli.Draft; using Microsoft.Extensions.Logging; @@ -25,6 +26,21 @@ private sealed class Dependency public int Value => 5; } + [CommandName("default")] + public class DefaultCommand: Command + { + public class Settings : ArgumentsBase + { + [Switch("v", "value", Required = false)] + public int Value { get; set; } = 0; + } + + public override int Execute(Settings arguments, IReadOnlyList context) + { + return arguments.Value; + } + } + [CommandName("test")] private sealed class TestCommand : Command { @@ -32,7 +48,7 @@ private sealed class TestCommand : Command public class Settings : ArgumentsBase { - [Switch("v", "value")] + [Switch("v", "value", Required = false)] public int Value { get; set; } } @@ -54,15 +70,33 @@ public void Setup() _serviceProviderMock.Setup(x => x.GetService(typeof(Dependency))).Returns(new Dependency()); _loggerMock = new Mock(MockBehavior.Strict); _helproviderMock = new Mock(MockBehavior.Strict); + _helproviderMock.Setup(x => x.CommandsChanged(It.IsAny())); _sut = new CommandRunner(_serviceProviderMock.Object, _helproviderMock.Object, _loggerMock.Object, CommandRunnerSettings.Default); + _sut.AddDefaultCommand(); _sut.AddCommand(); } [Test] - public async Task EnsureThat_Run_Works() + public async Task EnsureThat_Run_Works_For_Command() { string[] args = ["test", "-v", "2"]; int result = await _sut.Run(args); Assert.That(result, Is.EqualTo(10)); } + + [Test] + public async Task EnsureThat_Run_Works_For_DefaultCommand_WithArgs() + { + string[] args = ["-v", "2"]; + int result = await _sut.Run(args); + Assert.That(result, Is.EqualTo(2)); + } + + [Test] + public async Task EnsureThat_Run_Works_For_DefaultCommand_WithOutArgs() + { + string[] args = Array.Empty(); + int result = await _sut.Run(args); + Assert.That(result, Is.EqualTo(0)); + } } diff --git a/Test/Bookgen.Tests/Commands/CommandTestBase.cs b/Test/Bookgen.Tests/Commands/CommandTestBase.cs index 58c04645..c256d845 100644 --- a/Test/Bookgen.Tests/Commands/CommandTestBase.cs +++ b/Test/Bookgen.Tests/Commands/CommandTestBase.cs @@ -1,11 +1,10 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.AppSettings; - using BookGen.Cli; +using BookGen.Lib.AppSettings; using BookGen.Vfs; using Microsoft.Extensions.Logging; diff --git a/Test/Bookgen.Tests/Commands/UT_Md2HtmlCommand.cs b/Test/Bookgen.Tests/Commands/UT_Md2HtmlCommand.cs index b778dfa9..bd1ee52e 100644 --- a/Test/Bookgen.Tests/Commands/UT_Md2HtmlCommand.cs +++ b/Test/Bookgen.Tests/Commands/UT_Md2HtmlCommand.cs @@ -1,11 +1,10 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib; - -using BookGen.Commands; +using BookGen.Commands.Convert; +using BookGen.Lib; using BookGen.Vfs; using Moq; @@ -34,7 +33,7 @@ protected override Md2HtmlCommand CreateSut() [Test] public async Task EnsureThat_GenerateRawWorks() { - var arguments = new Md2HtmlCommand.Md2HtmlArguments + var arguments = new Md2HtmlCommand.Arguments { InputFiles = ["test.md"], NoSyntax = true, @@ -44,7 +43,7 @@ public async Task EnsureThat_GenerateRawWorks() Title = "Document title" }; - int exitCode = await Command.ExecuteAsync(arguments, Array.Empty()); + int exitCode = await Command.ExecuteAsync(arguments, Array.Empty(), CancellationToken.None); const string expectedContent = "

test

\n"; @@ -59,7 +58,7 @@ public async Task EnsureThat_GenerateRawWorks() [Test] public async Task EnsureThat_GenerateHtml_Works() { - var arguments = new Md2HtmlCommand.Md2HtmlArguments + var arguments = new Md2HtmlCommand.Arguments { InputFiles = ["test.md"], NoSyntax = true, @@ -69,7 +68,7 @@ public async Task EnsureThat_GenerateHtml_Works() Title = "Document title" }; - int exitCode = await Command.ExecuteAsync(arguments, Array.Empty()); + int exitCode = await Command.ExecuteAsync(arguments, Array.Empty(), CancellationToken.None); using (Assert.EnterMultipleScope()) { diff --git a/Test/Bookgen.Tests/Commands/UT_ShellCommand.cs b/Test/Bookgen.Tests/Commands/UT_ShellCommand.cs index b0b2d439..49a8673f 100644 --- a/Test/Bookgen.Tests/Commands/UT_ShellCommand.cs +++ b/Test/Bookgen.Tests/Commands/UT_ShellCommand.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -34,7 +34,7 @@ protected override void SetupMocks() [Test] public async Task Test_Execute_NoArgs() { - var result = await Command.ExecuteAsync(ArgumentsBase.Empty, ["c"]); + var result = await Command.ExecuteAsync(ArgumentsBase.Empty, ["c"], CancellationToken.None); Assert.That(result, Is.EqualTo(ExitCodes.Success)); CommandRunnerProxyMock.Verify(x => x.CommandNames, Times.Once); } diff --git a/Test/Bookgen.Tests/EmbeddedTestFolder.cs b/Test/Bookgen.Tests/EmbeddedTestFolder.cs index fd34bc20..453b7d5f 100644 --- a/Test/Bookgen.Tests/EmbeddedTestFolder.cs +++ b/Test/Bookgen.Tests/EmbeddedTestFolder.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Test/Bookgen.Tests/Lib/UT_AppSettingsAccessor.cs b/Test/Bookgen.Tests/Lib/UT_AppSettingsAccessor.cs deleted file mode 100644 index bb2a4807..00000000 --- a/Test/Bookgen.Tests/Lib/UT_AppSettingsAccessor.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Bookgen.Lib.AppSettings; - -using BookGen.Vfs; - -using Moq; - -namespace Bookgen.Tests.Lib; - -[TestFixture] -public class UT_AppSettingsAccessor -{ - private Mock _fileSystem; - private AppSettings _sut; - - [SetUp] - public void Setup() - { - _fileSystem = new Mock(MockBehavior.Strict); - _fileSystem.Setup(x => x.FileExists(It.IsAny())).Returns(false); - _sut = new AppSettings(_fileSystem.Object); - } - - [Test] - public void EnsureThat_GetWorks() - { - var value = _sut.Get(x => x.Editor); - bool isValid = _sut.IsSettingValid(x => x.Editor, out IReadOnlyList issues); - - using (Assert.EnterMultipleScope()) - { - Assert.That(value, Is.EqualTo("notepad.exe")); - Assert.That(isValid, Is.True); - Assert.That(issues, Is.Empty); - } - } -} diff --git a/Test/Bookgen.Tests/Lib/UT_FootNoteReindexer.cs b/Test/Bookgen.Tests/Lib/UT_FootNoteReindexer.cs index e4ee8042..62e58312 100644 --- a/Test/Bookgen.Tests/Lib/UT_FootNoteReindexer.cs +++ b/Test/Bookgen.Tests/Lib/UT_FootNoteReindexer.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Markdown; +using BookGen.Lib.Markdown; using Microsoft.Extensions.Logging; diff --git a/Test/Bookgen.Tests/Lib/UT_ImgService.cs b/Test/Bookgen.Tests/Lib/UT_ImgService.cs index 62c4dc02..8ca32b98 100644 --- a/Test/Bookgen.Tests/Lib/UT_ImgService.cs +++ b/Test/Bookgen.Tests/Lib/UT_ImgService.cs @@ -1,10 +1,10 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.IO.Configuration; -using Bookgen.Lib.Rendering.Images; +using BookGen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Rendering.Images; using Microsoft.Extensions.Logging; diff --git a/Test/Bookgen.Tests/Lib/UT_JsonMerger.cs b/Test/Bookgen.Tests/Lib/UT_JsonMerger.cs index a9c45f29..211b8602 100644 --- a/Test/Bookgen.Tests/Lib/UT_JsonMerger.cs +++ b/Test/Bookgen.Tests/Lib/UT_JsonMerger.cs @@ -6,8 +6,8 @@ using System.Text.Json; using System.Text.Json.Nodes; -using Bookgen.Lib.Confighandling; -using Bookgen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Confighandling; +using BookGen.Lib.Domain.IO.Configuration; namespace Bookgen.Tests.Lib; diff --git a/Test/Bookgen.Tests/Lib/UT_LevelList.cs b/Test/Bookgen.Tests/Lib/UT_LevelList.cs index c1fd23fa..e924a2e7 100644 --- a/Test/Bookgen.Tests/Lib/UT_LevelList.cs +++ b/Test/Bookgen.Tests/Lib/UT_LevelList.cs @@ -1,11 +1,11 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- using System.Text; -using Bookgen.Lib.Rendering.Markdown.TableOfContents; +using BookGen.Lib.Rendering.Markdown.TableOfContents; namespace Bookgen.Tests.Lib; diff --git a/Test/Bookgen.Tests/Lib/UT_MarkdownConverter.cs b/Test/Bookgen.Tests/Lib/UT_MarkdownConverter.cs index 4f611cda..dbcebcec 100644 --- a/Test/Bookgen.Tests/Lib/UT_MarkdownConverter.cs +++ b/Test/Bookgen.Tests/Lib/UT_MarkdownConverter.cs @@ -3,10 +3,10 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.IO.Configuration; -using Bookgen.Lib.Rendering.Images; -using Bookgen.Lib.Rendering.Markdown; -using Bookgen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Markdown; +using BookGen.Lib.Rendering.Markdown.RenderInterop; using Moq; @@ -19,7 +19,7 @@ internal class UT_MarkdownConverter private string _markdown; private string _soruceCode; private readonly IEqualityComparer comparer = new LineEndingIgnoreComparer(); - + [SetUp] public void Setup() diff --git a/Test/Bookgen.Tests/Lib/UT_MarkdownConverter_Specials.cs b/Test/Bookgen.Tests/Lib/UT_MarkdownConverter_Specials.cs index 5bdbb5c0..61499a5f 100644 --- a/Test/Bookgen.Tests/Lib/UT_MarkdownConverter_Specials.cs +++ b/Test/Bookgen.Tests/Lib/UT_MarkdownConverter_Specials.cs @@ -3,17 +3,17 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.IO.Configuration; -using Bookgen.Lib.Rendering.Images; -using Bookgen.Lib.Rendering.Markdown; -using Bookgen.Lib.Rendering.Markdown.RenderInterop; +using BookGen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Markdown; +using BookGen.Lib.Rendering.Markdown.RenderInterop; using Moq; namespace Bookgen.Tests.Lib; [TestFixture] -internal sealed class UT_MarkdownConverter_Specials +internal sealed class UT_MarkdownConverter_Specials { private Mock _imgServiceMock; private MarkdownRenderSettings _settings; @@ -167,12 +167,31 @@ public void EnsureThat_Math_Inline_Works() $$\frac{1}{2} + \sqrt{x}$$ """; - string expected = """ -

+ using var sut = new MarkdownConverter(_settings); - """; + string result = sut.RenderMarkdownToHtml(input); - using var sut = new MarkdownConverter(_settings); + Assert.That(result, Does.Contain(">+<>", "

Ctrl+O

\n")] + [TestCase("<<>>", "

<<>>

\n")] + [TestCase("Press <> to open ", "

Press Ctrl+O to open http://www.example.com

\n")] + public void EnsureThat_Kbd_Tag_Works(string input, string expected) + { + using var settings = new MarkdownRenderSettings(_imgServiceMock.Object) + { + CssClasses = new CssClasses(), + DeleteFirstH1 = false, + HostUrl = null, + AutoEmbedSupportedLinks = true, + RenderInterop = new RenderInterop(_testEnvironment, _testEnvironment.ProgramPathResolver, new ImageConfig + { + SvgRecode = SvgRecodeOption.Passtrough + }) + }; + + using var sut = new MarkdownConverter(settings); string result = sut.RenderMarkdownToHtml(input); diff --git a/Test/Bookgen.Tests/Lib/UT_RenderInterop.cs b/Test/Bookgen.Tests/Lib/UT_RenderInterop.cs index d27bc2ab..a93b17dc 100644 --- a/Test/Bookgen.Tests/Lib/UT_RenderInterop.cs +++ b/Test/Bookgen.Tests/Lib/UT_RenderInterop.cs @@ -1,8 +1,13 @@ -using System.Runtime.InteropServices; +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.IO.Configuration; -using Bookgen.Lib.Rendering.Images; -using Bookgen.Lib.Rendering.Markdown.RenderInterop; +using System.Runtime.InteropServices; + +using BookGen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Rendering.Images; +using BookGen.Lib.Rendering.Markdown.RenderInterop; namespace Bookgen.Tests.Lib; @@ -40,7 +45,7 @@ public void EnsureThat_RenderLatex_ReturnsSvg() } ImageResult svg = _sut.RenderLatex("\\frac{1}{2} + \\sqrt{x}"); - + using (Assert.EnterMultipleScope()) { Assert.That(svg.ImageType, Is.EqualTo(ImageType.Svg)); diff --git a/Test/Bookgen.Tests/Lib/UT_Search.cs b/Test/Bookgen.Tests/Lib/UT_Search.cs index 29df76aa..29d18207 100644 --- a/Test/Bookgen.Tests/Lib/UT_Search.cs +++ b/Test/Bookgen.Tests/Lib/UT_Search.cs @@ -1,4 +1,9 @@ -using Bookgen.Lib.Rendering.Markdown; +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using BookGen.Lib.Rendering.Markdown; namespace Bookgen.Tests.Lib; diff --git a/Test/Bookgen.Tests/Lib/UT_SerializedObjectValidator.cs b/Test/Bookgen.Tests/Lib/UT_SerializedObjectValidator.cs index 68b2a954..90144c3a 100644 --- a/Test/Bookgen.Tests/Lib/UT_SerializedObjectValidator.cs +++ b/Test/Bookgen.Tests/Lib/UT_SerializedObjectValidator.cs @@ -1,10 +1,10 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Domain.IO.Configuration; -using Bookgen.Lib.Internals; +using BookGen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Internals; using BookGen.Vfs; diff --git a/Test/Bookgen.Tests/Lib/UT_StringExtensions.cs b/Test/Bookgen.Tests/Lib/UT_StringExtensions.cs index d075fa7b..65177d6e 100644 --- a/Test/Bookgen.Tests/Lib/UT_StringExtensions.cs +++ b/Test/Bookgen.Tests/Lib/UT_StringExtensions.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gbor +// (c) 2019-2026 Ruzsinszki Gbor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Internals; +using BookGen.Lib.Internals; namespace Bookgen.Tests.Lib; diff --git a/Test/Bookgen.Tests/Lib/UT_TemplateEngine.cs b/Test/Bookgen.Tests/Lib/UT_TemplateEngine.cs index 669e2e0d..01c6d764 100644 --- a/Test/Bookgen.Tests/Lib/UT_TemplateEngine.cs +++ b/Test/Bookgen.Tests/Lib/UT_TemplateEngine.cs @@ -1,17 +1,13 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Rendering.Templates; -using Bookgen.Lib.Templates; +using BookGen.Lib; +using BookGen.Lib.Rendering.Templates; using BookGen.Vfs; -using Microsoft.Extensions.Logging; - -using Moq; - namespace Bookgen.Tests.Lib; [TestFixture] @@ -19,25 +15,57 @@ internal class UT_TemplateEngine { private TemplateEngine _sut; private TestLogger _logger; - private Mock _assetSourceMock; + private TestEnvironment _environment; + + public class TestTimeProvider : TimeProvider + { + public override DateTimeOffset GetUtcNow() + { + return new DateTimeOffset(2026, 1, 1, 11, 12, 13, TimeSpan.Zero); + } + + public override TimeZoneInfo LocalTimeZone + => TimeZoneInfo.Utc; + } + [SetUp] public void Setup() { - _assetSourceMock = new Mock(MockBehavior.Strict); + _environment = new TestEnvironment(); _logger = new TestLogger(); - _sut = new TemplateEngine(_logger, _assetSourceMock.Object); - _sut.RegisterFunction("MyFunction", (arg) => string.Join(',', arg)); + _sut = new TemplateEngine(_logger, _environment, new TemplateEngineOptions + { + TimeProvider = new TestTimeProvider() + }); + } + + [TearDown] + public void TearDown() + { + _environment.Dispose(); } [TestCase("", "")] - [TestCase("

No template tags

", "

No template tags

\r\n")] - [TestCase("

{{Title}}

{{Content}}

", "

title

This is content

\r\n")] - [TestCase("

{{Title}}

{{MyFunction(\"Foo\")}}

", "

title

Foo

\r\n")] - [TestCase("{{MyFunction(\"Foo\")}}", "Foo\r\n")] - [TestCase("{{MyFunction(\"Foo\", \"Bar\")}}", "Foo,Bar\r\n")] - [TestCase("{{MyFunction(\"Foo\", \"Bar\",\"Baz\")}}", "Foo,Bar,Baz\r\n")] - [TestCase("{{MyFunction(\"Foo\", \"Bar\",\"Baz\", \"Banana\")}}", "Foo,Bar,Baz,Banana\r\n")] + [TestCase("

No template tags

", "

No template tags

")] + [TestCase("

{{Title}}

{{Content}}

", "

title

This is content

")] + [TestCase("{{LastModified}}", "1987-10-11 00:00:00")] + [TestCase("{{ToUpper(Title)}}", "TITLE")] + [TestCase("{{ToLower(Title)}}", "title")] + [TestCase("{{Substring(Content, 0, 4)}}", "This")] + [TestCase("{{Trim(' Hello ')}}", "Hello")] + [TestCase("{{TrimStart(' Hello ')}}", "Hello ")] + [TestCase("{{TrimEnd(' Hello ')}}", " Hello")] + [TestCase("{{Replace(Content, 'content', 'Universe')}}", "This is Universe")] + [TestCase("{{Concat(Content, '-','Bar', '-', '42')}}", "This is content-Bar-42")] + [TestCase("{{Concat(Content, ' ', 42)}}", "This is content 42")] + [TestCase("{{HtmlEncode('
')}}", "<div>")] + [TestCase("{{UrlEncode('https://example.com')}}", "https%3A%2F%2Fexample.com")] + [TestCase("{{UrlDecode('https%3A%2F%2Fexample.com')}}", "https://example.com")] + [TestCase("{{CurrentDate()}}", "2026-01-01")] + [TestCase("{{CurrentTime()}}", "11:12:13")] + [TestCase("{{CurrentDateTime()}}", "2026-01-01 11:12:13")] + [TestCase("{{RegexReplace(Content, 's', 'z')}}", "Thiz iz content")] public void EnsureThat_Render_Works(string template, string expected) { var viewData = new ViewData @@ -55,7 +83,7 @@ public void EnsureThat_Render_Works(string template, string expected) } [Test] - public void EnsureThat_Render_Warns_Unrecognized_Template_Parts() + public void EnsureThat_Render_Errors_Unrecognized_Template_Parts() { var viewData = new ViewData { @@ -64,15 +92,16 @@ public void EnsureThat_Render_Warns_Unrecognized_Template_Parts() Host = string.Empty, LastModified = new DateTime(1987, 10, 11) }; + string template = "

{{Title}}

{{UnrecognizedPart}}

"; _sut.Render(template, viewData); - Assert.That(_logger.Warnings, Is.EqualTo(1)); + Assert.That(_logger.Errors, Is.EqualTo(1)); } [Test] - public void EnsureThat_Render_Warns_Unrecognized_Functions() + public void EnsureThat_Render_Errors_Unrecognized_Functions() { var viewData = new ViewData { @@ -81,8 +110,43 @@ public void EnsureThat_Render_Warns_Unrecognized_Functions() Host = string.Empty, LastModified = new DateTime(1987, 10, 11) }; + string template = "

{{Title}}

{{UnrecognizedFunction()}}

"; _sut.Render(template, viewData); - Assert.That(_logger.Warnings, Is.EqualTo(1)); + + Assert.That(_logger.Errors, Is.EqualTo(1)); + } + + [TestCase(BundledAssets.TemplateBlank)] + [TestCase(BundledAssets.TemplatePrint)] + [TestCase(BundledAssets.TemplateSinglePage)] + [TestCase(BundledAssets.TemplateStaticWeb)] + [TestCase(BundledAssets.TemplateEpub)] + [TestCase(BundledAssets.Md2HtmlTemplateAir)] + [TestCase(BundledAssets.Md2HtmlTemplateRetro)] + [TestCase(BundledAssets.Md2HtmlTemplatteTinyDark)] + [TestCase(BundledAssets.Md2HtmlTemplatteTinyLight)] + [TestCase(BundledAssets.Md2HtmlTemplatteMvp)] + public void EnsureThat_BundledTemplates_Correct(string template) + { + IAssetSource assetSource = _environment; + string TemplateContent = assetSource.GetAsset(template); + + var viewData = new ViewData + { + Content = "", + Title = "", + Host = string.Empty, + LastModified = new DateTime(1987, 10, 11) + }; + + string result = _sut.Render(template, viewData); + + using (Assert.EnterMultipleScope()) + { + Assert.That(result, Does.Not.Contain("{{Content}}")); + Assert.That(result, Does.Not.Contain("{{Title}}")); + Assert.That(_logger.Errors, Is.Zero); + } } } diff --git a/Test/Bookgen.Tests/Lib/TerminalRenderer.cs b/Test/Bookgen.Tests/Lib/UT_TerminalRenderer.cs similarity index 97% rename from Test/Bookgen.Tests/Lib/TerminalRenderer.cs rename to Test/Bookgen.Tests/Lib/UT_TerminalRenderer.cs index cfcb715b..cfbcc959 100644 --- a/Test/Bookgen.Tests/Lib/TerminalRenderer.cs +++ b/Test/Bookgen.Tests/Lib/UT_TerminalRenderer.cs @@ -1,9 +1,9 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Bookgen.Lib.Rendering.Markdown.Renderers; +using BookGen.Lib.Rendering.Markdown.Renderers; namespace Bookgen.Tests.Lib; diff --git a/Test/Bookgen.Tests/LineEndingIgnoreComparer.cs b/Test/Bookgen.Tests/LineEndingIgnoreComparer.cs index 1ae76b37..3e4803ce 100644 --- a/Test/Bookgen.Tests/LineEndingIgnoreComparer.cs +++ b/Test/Bookgen.Tests/LineEndingIgnoreComparer.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Test/Bookgen.Tests/Shell.Shared/UT_GitParser.cs b/Test/Bookgen.Tests/Shell.Shared/UT_GitParser.cs index 7497caf4..48f962cf 100644 --- a/Test/Bookgen.Tests/Shell.Shared/UT_GitParser.cs +++ b/Test/Bookgen.Tests/Shell.Shared/UT_GitParser.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Test/Bookgen.Tests/Shell.Shared/UT_ShellAutoCompleteFilter.cs b/Test/Bookgen.Tests/Shell.Shared/UT_ShellAutoCompleteFilter.cs index 99043447..d5a039c5 100644 --- a/Test/Bookgen.Tests/Shell.Shared/UT_ShellAutoCompleteFilter.cs +++ b/Test/Bookgen.Tests/Shell.Shared/UT_ShellAutoCompleteFilter.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Test/Bookgen.Tests/TestAppSettings.cs b/Test/Bookgen.Tests/TestAppSettings.cs index 4529c2c7..cd0f0563 100644 --- a/Test/Bookgen.Tests/TestAppSettings.cs +++ b/Test/Bookgen.Tests/TestAppSettings.cs @@ -5,8 +5,8 @@ using System.Linq.Expressions; -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Domain.IO; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Domain.IO; namespace Bookgen.Tests; diff --git a/Test/Bookgen.Tests/TestDocumentation.cs b/Test/Bookgen.Tests/TestDocumentation.cs deleted file mode 100644 index d55c512a..00000000 --- a/Test/Bookgen.Tests/TestDocumentation.cs +++ /dev/null @@ -1,73 +0,0 @@ -//----------------------------------------------------------------------------- -// (c) 2019-2026 Ruzsinszki Gábor -// This code is licensed under MIT license (see LICENSE for details) -//----------------------------------------------------------------------------- - -using System.Text; - -using BookGen.Cli; -using BookGen.Commands; -using BookGen.Infrastructure; - -using Microsoft.Extensions.Logging; - -using Moq; - -namespace Bookgen.Tests; - -[TestFixture] -internal class TestDocumentation -{ - private Mock _serviceProviderMock; - private Mock _helpProviderMock; - private Mock _loggerMock; - private HelpProvider _helpProvider; - private CommandRunnerProxy _commandRunnerProxy; - - [SetUp] - public void Setup() - { - _helpProviderMock = new Mock(MockBehavior.Strict); - _loggerMock = new Mock(MockBehavior.Strict); - _serviceProviderMock = new Mock(MockBehavior.Strict); - var testCommandRunner = new CommandRunner(_serviceProviderMock.Object, _helpProviderMock.Object, _loggerMock.Object, CommandRunnerSettings.Default); - testCommandRunner.AddCommandsFrom(typeof(HelpCommand).Assembly); - - _commandRunnerProxy = new(); - _commandRunnerProxy.ConfigureWith(testCommandRunner); - - _helpProvider = new HelpProvider(_loggerMock.Object, _commandRunnerProxy); - } - - [Test] - public void EnsureThat_Commands_HaveDocumentation() - { - StringBuilder errorMessage = new(); - var hasHelp = _helpProvider.HelpEntries.ToHashSet(); - foreach (var commandName in _commandRunnerProxy.CommandNames) - { - if (!hasHelp.Contains(commandName)) - { - errorMessage.AppendLine($"No help was found for command: {commandName}"); - } - } - if (errorMessage.Length > 0) - Assert.Fail(errorMessage.ToString()); - } - - [Test] - public void EnsureThat_Documentation_ContainsOnlyExistingCommands() - { - StringBuilder errorMessage = new(); - var commands = _commandRunnerProxy.CommandNames.ToHashSet(); - foreach (var helpEntry in _helpProvider.HelpEntries) - { - if (!commands.Contains(helpEntry)) - { - errorMessage.AppendLine($"No command found for documentation: {helpEntry}"); - } - } - if (errorMessage.Length > 0) - Assert.Fail(errorMessage.ToString()); - } -} diff --git a/Test/Bookgen.Tests/TestEnvironment.cs b/Test/Bookgen.Tests/TestEnvironment.cs index b6501f4a..c86cae56 100644 --- a/Test/Bookgen.Tests/TestEnvironment.cs +++ b/Test/Bookgen.Tests/TestEnvironment.cs @@ -5,10 +5,10 @@ using System.Diagnostics.CodeAnalysis; -using Bookgen.Lib.AppSettings; -using Bookgen.Lib.Domain.IO; -using Bookgen.Lib.Domain.IO.Configuration; -using Bookgen.Lib.Pipeline; +using BookGen.Lib.AppSettings; +using BookGen.Lib.Domain.IO; +using BookGen.Lib.Domain.IO.Configuration; +using BookGen.Lib.Pipeline; using BookGen.Vfs; diff --git a/Test/Bookgen.Tests/TestLogger.cs b/Test/Bookgen.Tests/TestLogger.cs index bf3d7021..9df1905a 100644 --- a/Test/Bookgen.Tests/TestLogger.cs +++ b/Test/Bookgen.Tests/TestLogger.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- diff --git a/Test/Bookgen.Tests/UT_OpenCli.cs b/Test/Bookgen.Tests/UT_OpenCli.cs new file mode 100644 index 00000000..753900cc --- /dev/null +++ b/Test/Bookgen.Tests/UT_OpenCli.cs @@ -0,0 +1,147 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using BookGen.Cli; +using BookGen.Cli.OpenCli.Draft; +using BookGen.Commands; +using BookGen.GlobalOptionParsers; + +using Microsoft.Extensions.Logging; + +using Moq; + +namespace Bookgen.Tests; + +[TestFixture] +internal class UT_OpenCli +{ + private Document _openCliDocument; + + private static CommandRunner SetupCommandRunner() + { + var helpProviderMock = new Mock(MockBehavior.Strict); + var loggerMock = new Mock(MockBehavior.Strict); + var serviceProviderMock = new Mock(MockBehavior.Strict); + var runner = new CommandRunner(serviceProviderMock.Object, helpProviderMock.Object, loggerMock.Object, CommandRunnerSettings.Default); + + var info = new BookGen.ProgramInfo(); + + runner + .AddGlobalOptionParser() + .AddGlobalOptionParser() + .AddGlobalOptionParser(new JsonLogParser(info)) + .AddGlobalOptionParser(new LogToFileParser(info)) + .AddGlobalOptionParser(new RuntimePrintingParser(info)); + + runner.AddDefaultCommand(); + runner.AddCommandsFrom(typeof(DefaultCommand).Assembly, includeDefault: false); + return runner; + } + + [SetUp] + public void Setup() + { + var runner = SetupCommandRunner(); + _openCliDocument = runner.GenerateOpenCliDocs(); + } + + public static IEnumerable Commands + { + get + { + var runner = SetupCommandRunner(); + return runner.CommandNames; + } + } + + public static IEnumerable GlobalOptions + { + get + { + var runner = SetupCommandRunner(); + return runner.GetGlobalOptions(); + } + } + + [Test] + public void EnsureThat_OpenCli_Documentation_Structure_Ok() + { + using (Assert.EnterMultipleScope()) + { + Assert.That(_openCliDocument.Commands, Is.Not.Empty); + Assert.That(_openCliDocument.Opencli, Is.EqualTo("0.1")); + Assert.That(_openCliDocument.Info.Version, Is.Not.Empty); + } + } + + [Test] + public void EnsureThat_OpenCli_Documentation_GlobalOptions_NotEmpty() + { + using (Assert.EnterMultipleScope()) + { + Assert.That(_openCliDocument.Command.Options, Is.Not.Empty); + } + } + + [TestCaseSource(nameof(GlobalOptions))] + public void EnsureThat_OpenCli_Documentation_GlobalOption_CorrectlyDocumented(string globalOption) + { + Option? option = _openCliDocument.Command.Options?.Where(o => o.Name == globalOption || o.Aliases?.Contains(globalOption) == true).FirstOrDefault(); + using (Assert.EnterMultipleScope()) + { + Assert.That(option, Is.Not.Null, $"Global option '{globalOption}' is missing in OpenCli documentation."); + Assert.That(option?.Description, Is.Not.Null.And.Not.Empty, $"Global option '{globalOption}' is missing description in OpenCli documentation."); + Assert.That(option?.OpenClRequired, Is.EqualTo(false), $"Global option '{globalOption}' should have OpenClRequired set to false in OpenCli documentation."); + Assert.That(option?.Aliases, Is.Not.Null, $"Global option '{globalOption}' is missing aliases in OpenCli documentation."); + Assert.That(option?.Name, Is.Not.Null.And.Not.Empty, $"Global option '{globalOption}' is missing name in OpenCli documentation."); + } + } + + + [TestCaseSource(nameof(Commands))] + public void EnsureThat_OpenCli_Documentation_Command_CorrectlyDocumented(string command) + { + BookGen.Cli.OpenCli.Draft.Command? cmd = _openCliDocument.Commands?.Where(c => c.Name == command).FirstOrDefault(); + using (Assert.EnterMultipleScope()) + { + Assert.That(cmd, Is.Not.Null, $"Command '{command}' is missing in OpenCli documentation."); + Assert.That(cmd?.Description, Is.Not.Null.And.Not.Empty, $"Command '{command}' is missing description in OpenCli documentation."); + Assert.That(cmd?.Name, Is.Not.Null.And.Not.Empty, $"Command '{command}' is missing name in OpenCli documentation."); + Assert.That(cmd?.ExitCodes, Has.Count.GreaterThanOrEqualTo(1), $"Command '{command}' is missing exit codes in OpenCli documentation."); + Assert.That(cmd?.Examples, Is.Not.Null.And.Count.GreaterThan(0), $"Command '{command}' is missing examples in OpenCli documentation."); + + foreach (string example in cmd?.Examples ?? Enumerable.Empty()) + { + Assert.That(example, Is.Not.Null.And.Not.Empty, $"Command '{command}' has an empty example in OpenCli documentation."); + } + + foreach (ExitCode exitCode in cmd?.ExitCodes ?? Enumerable.Empty()) + { + Assert.That(exitCode.Description, Is.Not.Null.And.Not.Empty, $"Command '{command}' has an exit code with null or empty description in OpenCli documentation."); + } + + if (cmd?.Options != null) + { + foreach (Option option in cmd.Options) + { + Assert.That(option.Description, Is.Not.Null.And.Not.Empty, $"Option '{option.Name}' of command '{command}' is missing description in OpenCli documentation."); + Assert.That(option.Name, Is.Not.Null.And.Not.Empty, $"Option of command '{command}' is missing name in OpenCli documentation."); + Assert.That(option.OpenClRequired, Is.Not.Null, $"Option '{option.Name}' of command '{command}' is missing OpenClRequired in OpenCli documentation."); + } + } + + if (cmd?.Arguments != null) + { + foreach (Argument argument in cmd.Arguments) + { + Assert.That(argument.Description, Is.Not.Null.And.Not.Empty, $"Argument '{argument.Name}' of command '{command}' is missing description in OpenCli documentation."); + Assert.That(argument.Name, Is.Not.Null.And.Not.Empty, $"Argument of command '{command}' is missing name in OpenCli documentation."); + Assert.That(argument.OpenClRequired, Is.Not.Null, $"Argument '{argument.Name}' of command '{command}' is missing OpenClRequired in OpenCli documentation."); + } + + } + } + } +} diff --git a/download-assets-pipeline.sh b/download-assets-pipeline.sh index e04a5d80..93cbcd40 100644 --- a/download-assets-pipeline.sh +++ b/download-assets-pipeline.sh @@ -4,9 +4,9 @@ mkdir -p "./Assets/dictionaries" touch "./Assets/dictionaries/keep.file" cd "./Assets/tools" -curl -L -O "https://github.com/erweixin/RaTeX/releases/download/v0.1.11/ratex-cli-v0.1.11-x86_64-unknown-linux-musl.tar.gz" -tar -xzf ./ratex-cli-v0.1.11-x86_64-unknown-linux-musl.tar.gz -C . -mv ./ratex-cli-v0.1.11-x86_64-unknown-linux-musl/render-svg ./ratex-svg +curl -L -O "https://github.com/erweixin/RaTeX/releases/download/v0.1.13/ratex-cli-v0.1.13-x86_64-unknown-linux-musl.tar.gz" +tar -xzf ./ratex-cli-v0.1.13-x86_64-unknown-linux-musl.tar.gz -C . +mv ./ratex-cli-v0.1.13-x86_64-unknown-linux-musl/render-svg ./ratex-svg -curl -L -O "https://github.com/1jehuang/mermaid-rs-renderer/releases/download/v0.2.2/mmdr-x86_64-unknown-linux-gnu.tar.gz" +curl -L -O "https://github.com/1jehuang/mermaid-rs-renderer/releases/download/v0.3.1/mmdr-x86_64-unknown-linux-gnu.tar.gz" tar -xzf ./mmdr-x86_64-unknown-linux-gnu.tar.gz -C . diff --git a/download-assets.ps1 b/download-assets.ps1 index 7abbfaa2..baf2fd65 100644 --- a/download-assets.ps1 +++ b/download-assets.ps1 @@ -27,10 +27,10 @@ function download-dictionaries { function download-tools { Set-Location $toolsPath - curl.exe -L "https://github.com/erweixin/RaTeX/releases/download/v0.1.11/ratex-cli-v0.1.11-x86_64-pc-windows-msvc.zip" -o ratex-windows.zip - curl.exe -L "https://github.com/erweixin/RaTeX/releases/download/v0.1.11/ratex-cli-v0.1.11-x86_64-unknown-linux-musl.tar.gz" -o ratex-linux.tar.gz - curl.exe -L "https://github.com/1jehuang/mermaid-rs-renderer/releases/download/v0.2.2/mmdr-x86_64-pc-windows-msvc.zip" -o mmdr-windows.zip - curl.exe -L "https://github.com/1jehuang/mermaid-rs-renderer/releases/download/v0.2.2/mmdr-x86_64-unknown-linux-gnu.tar.gz" -o mmdr-linux.tar.gz + curl.exe -L "https://github.com/erweixin/RaTeX/releases/download/v0.1.13/ratex-cli-v0.1.13-x86_64-pc-windows-msvc.zip" -o ratex-windows.zip + curl.exe -L "https://github.com/erweixin/RaTeX/releases/download/v0.1.13/ratex-cli-v0.1.13-x86_64-unknown-linux-musl.tar.gz" -o ratex-linux.tar.gz + curl.exe -L "https://github.com/1jehuang/mermaid-rs-renderer/releases/download/v0.3.1/mmdr-x86_64-pc-windows-msvc.zip" -o mmdr-windows.zip + curl.exe -L "https://github.com/1jehuang/mermaid-rs-renderer/releases/download/v0.3.1/mmdr-x86_64-unknown-linux-gnu.tar.gz" -o mmdr-linux.tar.gz # mmdr Expand-Archive .\mmdr-windows.zip -Force -DestinationPath . @@ -40,14 +40,14 @@ function download-tools { # RaTeX Expand-Archive .\ratex-windows.zip -Force -DestinationPath . - Move-Item -Force .\ratex-cli-v0.1.11-x86_64-pc-windows-msvc\render-svg.exe .\ratex-svg.exe + Move-Item -Force .\ratex-cli-v0.1.13-x86_64-pc-windows-msvc\render-svg.exe .\ratex-svg.exe Remove-Item .\ratex-windows.zip - Remove-Item .\ratex-cli-v0.1.11-x86_64-pc-windows-msvc -Recurse -Force + Remove-Item .\ratex-cli-v0.1.13-x86_64-pc-windows-msvc -Recurse -Force tar -xzf .\ratex-linux.tar.gz -C . - Move-Item -Force .\ratex-cli-v0.1.11-x86_64-unknown-linux-musl\render-svg .\ratex-svg + Move-Item -Force .\ratex-cli-v0.1.13-x86_64-unknown-linux-musl\render-svg .\ratex-svg Remove-Item .\ratex-linux.tar.gz - Remove-Item .\ratex-cli-v0.1.11-x86_64-unknown-linux-musl -Recurse -Force + Remove-Item .\ratex-cli-v0.1.13-x86_64-unknown-linux-musl -Recurse -Force Set-Location $startdir } diff --git a/getting-started.md b/getting-started.md deleted file mode 100644 index 79a7b8a2..00000000 --- a/getting-started.md +++ /dev/null @@ -1,64 +0,0 @@ -# Getting started - -BookGen installs various programs that can be used to achieve Book generation -and writing tasks with the help of Markdown. - -# Installed programs - -* BookGen - Main command line program -* BookGen.Shellprog - Shell helper program - -# Bookgen shell commands - -`cdg` - Graphical change directory. Opens folder browser to set current working directory - -`intro` - BookGen shell short intro message - -`bookgen-info` - Displays this text - -`organize` - Organize files in the current working directory - -# Common BookGen commands - -`BookGen md2html -i input.md -o out.html` - Convert the input.md file to out.html - -`BookGen SubCommands` - Lists all available subcommands - -`BookGen Gui` - Start in terminal gui mode. Only available, if folder contains a BookGen project. - -# Markdown files - -Markdown files are the main input format for BookGen. They can contain text, images, links, and other elements. BookGen supports github flavored markdown, which means it supports additional features like tables, task lists, and more. Front matter is supported via YAML syntax. The front matter is used to store metadata about the document, such as title, tags, and other properties. - -To create a new page execute the command: `BookGen newpage -n test.md`. This will create a new markdown file named `test.md` in the current working directory. You can then edit this file with your favorite text editor. It will contain a basic template with the YAML front matter and a placeholder for the content. - -# Templates - -Template tags use a mustache like syntax startiong with the `{{` symbols and ending with `}}`. Tags can include simple properties or functions. Function and property names are case insensitive. - -There are a few special properties that have special meaning. These are: - -* `{{Title}}` - Title of the acual page -* `{{Content}}` - Content of the actual page -* `{{Host}}` - Host url, set in the configuration file - -The `{{content}}` mustache tag is a special placeholder used to represent the markdown content within a file. **Important:** A markdown document must not include the `{{content}}` tag within its own content. Including this tag will cause infinite recursion during rendering, resulting in a failure to render the document. - -The `title` attribute comes from the YAML front matter of the document. The YAML front matter must include a `title` and a `tags` property. The Title is the document title and the tags are a comma sepperated string of keywords that can be set for the metadata. - -You can also add additional data to the YAML front matter. The YAML front matter via the `Data` property, which is a Dictionary. For example, adding: - -```yaml -Data: - foo: bar -``` -allows you to access this value in templates or markdown content using the mustache syntax `{{foo}}`. All property names are case insensitive in this case too, when accessing the front matter data. - -The `LastModified` time stamp is determined from the input file. - - -# Template functions - -* `{{BuildDate(format)}}` - Actual build date. Format is optional, default is `yyyy-MM-dd HH:mm:ss`. -* `{{JSPageToc(source, target)}}` - Generates a JavaScript table of contents from the source div's headdings and displays it to the target div. - diff --git a/publish.ps1 b/publish.ps1 index 1698df2e..cf2f0f6d 100644 --- a/publish.ps1 +++ b/publish.ps1 @@ -1,5 +1,3 @@ -Clear-Host - function New-Folders { if (Test-Path "bin\publish\windows") { Remove-Item "bin\publish\windows*" -Recurse -Force @@ -20,14 +18,18 @@ function Invoke-Publish { [string] $LinuxArchiveName ) + dotnet build .\Source\BookGen.SamplePlugin\ -c Release + # publish windows & linux if ($SelfContained) { - dotnet publish -c release -o "bin\publish\windows\bin" --self-contained true -r win-x64 -p PublishReadyToRun BookGen.slnx - dotnet publish -c release -o "bin\publish\linux\bin" --self-contained true -r linux-x64 -p PublishReadyToRun BookGen.slnx + dotnet publish -c release -o "bin\publish\windows\bin" --self-contained true -r win-x64 -p PublishReadyToRun BookGen.Build.slnx + dotnet publish -c release -o "bin\publish\linux\bin" --self-contained true -r linux-x64 -p PublishReadyToRun BookGen.Build.slnx + cp "bin\Release\plugins\BookGen.SamplePlugin.plugin" "bin\publish\windows\bin\plugins\BookGen.SamplePlugin.plugin" } else { - dotnet publish -c release -o "bin\publish\windows\bin" -r win-x64 -p PublishReadyToRun BookGen.slnx - dotnet publish -c release -o "bin\publish\linux\bin" -r linux-x64 -p PublishReadyToRun BookGen.slnx + dotnet publish -c release -o "bin\publish\windows\bin" -r win-x64 -p PublishReadyToRun BookGen.Build.slnx + dotnet publish -c release -o "bin\publish\linux\bin" -r linux-x64 -p PublishReadyToRun BookGen.Build.slnx + cp "bin\Release\plugins\BookGen.SamplePlugin.plugin" "bin\publish\linux\bin\plugins\BookGen.SamplePlugin.plugin" } # copy installer scripts @@ -40,7 +42,7 @@ function Invoke-Publish { # write version.txt .\bin\publish\windows\bin\BookGen.exe version > .\bin\publish\windows\version.txt - .\bin\publish\windows\bin\BookGen version > .\bin\publish\linux\version.txt + .\bin\publish\windows\bin\BookGen.exe version > .\bin\publish\linux\version.txt # make docs folder New-Item -Path "bin\publish\windows\docs" -ItemType Directory -Force @@ -51,14 +53,8 @@ function Invoke-Publish { Copy-Item ".\LICENCE" "bin\publish\linux\docs\LICENCE.txt" # Generate docs - .\bin\publish\windows\bin\BookGen Schemas - .\bin\publish\windows\bin\BookGen md2html -i Schemas.md -o "bin\publish\windows\docs\Schemas.html" -t "Configuration schemas" - .\bin\publish\windows\bin\BookGen md2html -i Schemas.md -o "bin\publish\linux\docs\Schemas.html" -t "Configuration schemas" - .\bin\publish\windows\bin\BookGen md2html -i Changelog.md -o "bin\publish\windows\docs\Changelog.html" -t "Change Log" - .\bin\publish\windows\bin\BookGen md2html -i Changelog.md -o "bin\publish\linux\docs\Changelog.html" -t "Change Log" - .\bin\publish\windows\bin\BookGen md2html -i Commands.md -o "bin\publish\windows\docs\Commands.html" -t "BookGen Commands" - .\bin\publish\windows\bin\BookGen md2html -i Commands.md -o "bin\publish\linux\docs\Commands.html" -t "BookGen Commands" - Remove-Item Schemas.md + .\bin\publish\windows\bin\BookGen.exe build plugin BookGen.SamplePlugin.plugin -d .\Docs -o "bin\publish\windows\docs" + .\bin\publish\windows\bin\BookGen.exe build plugin BookGen.SamplePlugin.plugin -d .\Docs -o "bin\publish\linux\docs" # zip if ($SelfContained) { @@ -73,9 +69,12 @@ function Invoke-Publish { } } +Clear-Host + New-Folders -Get-Tools +cp BookGen.slnx BookGen.Build.slnx +dotnet sln .\BookGen.Build.slnx remove .\Source\BookGen.SamplePlugin\ # Framework-dependent build and archives Invoke-Publish -SelfContained $false -WindowsArchiveName "BookGen-windows.zip" -LinuxArchiveName "BookGen-linux.tar.gz" @@ -85,3 +84,4 @@ Invoke-Publish -SelfContained $true -WindowsArchiveName "BookGen-windows-selefco .\PublishFiles\mkisofs.exe -V BookGen -o .\bin\publish\bookgen-windows.iso -udf .\bin\publish\windows +rm BookGen.Build.slnx \ No newline at end of file