diff --git a/.editorconfig b/.editorconfig index 2812d3f1..a9ffe46b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -178,6 +178,7 @@ csharp_style_prefer_local_over_anonymous_function = true:suggestion csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion csharp_style_prefer_tuple_swap = true:suggestion csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_prefer_primary_constructors = true:suggestion #### Naming styles #### [*.{cs,vb}] @@ -373,4 +374,5 @@ tab_width = 4 indent_size = 4 end_of_line = crlf dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion diff --git a/.github/releases.xml b/.github/releases.xml index 016009f1..3ab3659e 100644 --- a/.github/releases.xml +++ b/.github/releases.xml @@ -29,7 +29,7 @@ 👁‍🗨 Change: Removed ZIP format release 👁‍🗨 Change: Folder locking is now process based 👁‍🗨 Change: Improved stat command - 👁‍🗨 Change: HTTP server has favicons & last acces time is sent in header + 👁‍🗨 Change: HTTP server has favicons & last acces time is sent in header 👁‍🗨 Change: Replaces File based folder locking wiht a process based one 👁‍🗨 Change: Auto updater removed, because of multiple platform issues 👁‍🗨 Change: Launcher file browser usability improvements diff --git a/BookGen.sln b/BookGen.sln index c4c0b14f..e195f9e5 100644 --- a/BookGen.sln +++ b/BookGen.sln @@ -60,6 +60,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BookGen.Settings", "Libs\Bo EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BookGen.RenderEngine", "Libs\BookGen.RenderEngine\BookGen.RenderEngine.csproj", "{4E7C4D96-32DD-4E72-8B28-F4DCDB74E177}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BookGen.FormulaEdit", "Prog\BookGen.FormulaEdit\BookGen.FormulaEdit.csproj", "{C03D39B0-697B-4A51-AEF8-BFE094A45F4F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -138,6 +140,10 @@ Global {4E7C4D96-32DD-4E72-8B28-F4DCDB74E177}.Debug|Any CPU.Build.0 = Debug|Any CPU {4E7C4D96-32DD-4E72-8B28-F4DCDB74E177}.Release|Any CPU.ActiveCfg = Release|Any CPU {4E7C4D96-32DD-4E72-8B28-F4DCDB74E177}.Release|Any CPU.Build.0 = Release|Any CPU + {C03D39B0-697B-4A51-AEF8-BFE094A45F4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C03D39B0-697B-4A51-AEF8-BFE094A45F4F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C03D39B0-697B-4A51-AEF8-BFE094A45F4F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C03D39B0-697B-4A51-AEF8-BFE094A45F4F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -161,6 +167,7 @@ Global {985728E4-6353-44A1-87BB-77BF366A5977} = {05B69A0E-343B-4E7C-B77B-24E84B57D5D2} {B1905B26-0649-4911-BA5F-9781BBC3D3E2} = {9C47BBFF-ACD8-40D8-9FB1-A3FA524B27C4} {4E7C4D96-32DD-4E72-8B28-F4DCDB74E177} = {9C47BBFF-ACD8-40D8-9FB1-A3FA524B27C4} + {C03D39B0-697B-4A51-AEF8-BFE094A45F4F} = {05B69A0E-343B-4E7C-B77B-24E84B57D5D2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {772515BA-79D4-4C5D-AF16-29DE649EC13E} diff --git a/BookGenShell.ps1 b/BookGenShell.ps1 index 796d2fcd..e41c6eb3 100644 --- a/BookGenShell.ps1 +++ b/BookGenShell.ps1 @@ -1,9 +1,112 @@ # ----------------------------------------------------------------------------- # BookGen PowerShell Registration script -# Version 2.9.0 -# Last modified: 2024-05-20 +# Version 3.0 +# Last modified: 2024-07-21 # ----------------------------------------------------------------------------- +# NodeJS install test +function Test-NodeJs +{ + try + { + # Try to get the node version + $nodeVersion = node --version + if ($nodeVersion) + { + return $true + } + } + catch + { + return $false + } +} + +# Node commands + +function Get-NodePath +{ + try + { + $nodePath = (Get-Command node.exe -ErrorAction Stop).Source + $nodeDir = Split-Path $nodePath + return $nodeDir + } + catch + { + Write-Error "node.exe not found in the system PATH." + } +} + +function npm { + param ( + [string[]]$Args + ) + + $scriptPath = Get-NodePath + $nodeExe = Join-Path $scriptPath "node.exe" + + if (-Not (Test-Path $nodeExe)) + { + $nodeExe = "node" + } + + $npmPrefixJs = Join-Path $scriptPath "node_modules/npm/bin/npm-prefix.js" + $npmCliJs = Join-Path $scriptPath "node_modules/npm/bin/npm-cli.js" + + $npmPrefixNpmCliJs = & $nodeExe $npmPrefixJs + $npmPrefixNpmCliJsPath = Join-Path $npmPrefixNpmCliJs "node_modules/npm/bin/npm-cli.js" + + if (Test-Path $npmPrefixNpmCliJsPath) + { + $npmCliJs = $npmPrefixNpmCliJsPath + } + + & $nodeExe $npmCliJs @Args +} + +function npx { + param ( + [string[]]$Args + ) + + $scriptPath = Get-NodePath + $nodeExe = Join-Path $scriptPath "node.exe" + + if (-Not (Test-Path $nodeExe)) { + $nodeExe = "node" + } + + $npmPrefixJs = Join-Path $scriptPath "node_modules/npm/bin/npm-prefix.js" + $npxCliJs = Join-Path $scriptPath "node_modules/npm/bin/npx-cli.js" + + $npmPrefixNpxCliJsPath = & $nodeExe $npmPrefixJs + $npmPrefixNpxCliJs = Join-Path $npmPrefixNpxCliJsPath "node_modules/npm/bin/npx-cli.js" + + if (Test-Path $npmPrefixNpxCliJs) { + $npxCliJs = $npmPrefixNpxCliJs + } + + & $nodeExe $npxCliJs @Args +} + +function corepack { + param ( + [string[]]$Args + ) + + $scriptPath = Get-NodePath + $nodeExe = Join-Path $scriptPath "node.exe" + $corepackJs = Join-Path $scriptPath "node_modules/corepack/dist/corepack.js" + + if (Test-Path $nodeExe) { + & $nodeExe $corepackJs @Args + } else { + $env:PATHEXT = $env:PATHEXT -replace ";.JS;", ";" + & node $corepackJs @Args + } +} + # cdg command function cdg { @@ -80,14 +183,22 @@ function intro() Write-Host " .( o )." Bookgen.exe terminalinstall -t - if ($LastExitCode -eq 0) { + if ($LastExitCode -eq 0) + { Bookgen.exe terminalinstall -c - if ($LastExitCode -ne 0) { + if ($LastExitCode -ne 0) + { Write-Host "" Write-Host "To install this shell as a windows terminal profile run:"; Write-Host "Bookgen terminalinstall" } - } + } + + if (Test-NodeJs) + { + $nodeVersion = node --version + Write-Host "Node version: $nodeVersion" + } } #Set UTF8 encoding @@ -100,6 +211,24 @@ $env:BookGenPath = $PSScriptRoot # register scripts folder to the path $env:Path += ";$PSScriptRoot" +if (-not (Test-NodeJs)) { + #check if it's bundled + $nodeDirs = Get-ChildItem -Path $PSScriptRoot -Directory | Where-Object { $_.Name -like "node-*" } + foreach ($dir in $nodeDirs) + { + $nodePath = Join-Path -Path $dir.FullName -ChildPath "node.exe" + if (Test-Path -Path $nodePath) + { + $env:NodeJsDir = "$($dir.FullName)" + $env:NodeExe = $nodePath + + $env:Path += ";$($dir.FullName)" + Write-Host "Added $($dir.FullName) to PATH." + break + } + } +} + # set colors Set-PSReadLineOption -Colors @{ Parameter = 'Red' @@ -141,7 +270,12 @@ Register-ArgumentCompleter -Native -CommandName git -ScriptBlock { # set prompt function prompt { $git = $(BookGen.Shell.exe "prompt" $(Get-Location).Path) - 'PS ' + $(Get-Location) + ' '+$git+ $(if ($NestedPromptLevel -ge 1) { '>>' }) + ' > ' + if (-not [string]::IsNullOrWhiteSpace($git)) { + 'PS ' + $(Get-Location) + "`n"+$git+ $(if ($NestedPromptLevel -ge 1) { '>>' }) + ' > ' + } + else { + 'PS ' + $(Get-Location) + $(if ($NestedPromptLevel -ge 1) { '>>' }) + ' > ' + } } diff --git a/Bootstrappers/BookGen.FormulaEdit/BookGen.FormulaEdit.csproj b/Bootstrappers/BookGen.FormulaEdit/BookGen.FormulaEdit.csproj new file mode 100644 index 00000000..0bfa8bdb --- /dev/null +++ b/Bootstrappers/BookGen.FormulaEdit/BookGen.FormulaEdit.csproj @@ -0,0 +1,16 @@ + + + + WinExe + net4.8 + true + ..\..\bin\bootstaper\$(Configuration)\ + false + ..\..\Branding\icon-formulaedit.ico + + + + + + + \ No newline at end of file diff --git a/Bootstrappers/BookGen.FormulaEdit/Program.cs b/Bootstrappers/BookGen.FormulaEdit/Program.cs new file mode 100644 index 00000000..bc1edafb --- /dev/null +++ b/Bootstrappers/BookGen.FormulaEdit/Program.cs @@ -0,0 +1,34 @@ +using System; +//----------------------------------------------------------------------------- +// (c) 2023 Ruzsinszki Gbor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Bookgen.Win; + +namespace BookGen.Launcher +{ + public static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + public static void Main(string[] args) + { + ExceptionHandler.Try(() => + { + InstallVerify.ThrowIfNotExist(); + using (var process = + new ProcessBuilder() + .SetProgram(AppDomain.CurrentDomain.BaseDirectory, Constants.DataFolder, Constants.BookGenFormulaEditor) + .SetWorkDir(AppDomain.CurrentDomain.BaseDirectory, Constants.DataFolder) + .SetArguments(args) + .Build()) + { + process.Start(); + } + }); + } + } +} \ No newline at end of file diff --git a/Bootstrappers/BookGen.Launcher/BookGen.Launcher.csproj b/Bootstrappers/BookGen.Launcher/BookGen.Launcher.csproj index 5f93d786..25e954e0 100644 --- a/Bootstrappers/BookGen.Launcher/BookGen.Launcher.csproj +++ b/Bootstrappers/BookGen.Launcher/BookGen.Launcher.csproj @@ -6,7 +6,7 @@ true ..\..\bin\bootstaper\$(Configuration)\ false - ..\..\Icon.ico + ..\..\Branding\icon-laucher.ico diff --git a/Bootstrappers/BookGen/BookGen.csproj b/Bootstrappers/BookGen/BookGen.csproj index 49fc245a..ebc4236c 100644 --- a/Bootstrappers/BookGen/BookGen.csproj +++ b/Bootstrappers/BookGen/BookGen.csproj @@ -6,7 +6,7 @@ true ..\..\bin\bootstaper\$(Configuration)\ false - ..\..\Icon.ico + ..\..\Branding\icon-bookgen.ico diff --git a/Bootstrappers/Bookgen.Win/Constants.cs b/Bootstrappers/Bookgen.Win/Constants.cs index 15c301b2..e884c771 100644 --- a/Bootstrappers/Bookgen.Win/Constants.cs +++ b/Bootstrappers/Bookgen.Win/Constants.cs @@ -9,6 +9,7 @@ public static class Constants { public const string BookGen = "BookGen.exe"; public const string BookGenLauncher = "BookGen.Launcher.exe"; + public const string BookGenFormulaEditor = "BookGen.FormulaEdit.exe"; public const string DataFolder = "Data"; public const string WindowsTerminal = "wt.exe"; public const string PowershellCore = "pwsh.exe"; diff --git a/Bootstrappers/Bookgen.Win/ProcessBuilder.cs b/Bootstrappers/Bookgen.Win/ProcessBuilder.cs index 2eaa7432..238e3d95 100644 --- a/Bootstrappers/Bookgen.Win/ProcessBuilder.cs +++ b/Bootstrappers/Bookgen.Win/ProcessBuilder.cs @@ -7,8 +7,6 @@ using System.IO; using System.Linq; -using Bookgen.Win.Properties; - namespace Bookgen.Win { public sealed class ProcessBuilder diff --git a/Bootstrappers/Bookgen.Win/Try.cs b/Bootstrappers/Bookgen.Win/Try.cs index e62f5b23..f2484233 100644 --- a/Bootstrappers/Bookgen.Win/Try.cs +++ b/Bootstrappers/Bookgen.Win/Try.cs @@ -20,7 +20,7 @@ public static void Try(Action action) catch (Exception ex) { #if DEBUG - Debug.WriteLine(ex); + Trace.TraceError(ex.Message); #endif MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } diff --git a/Bootstrappers/Bootstrappers.sln b/Bootstrappers/Bootstrappers.sln index 375c759a..e9ee4e2c 100644 --- a/Bootstrappers/Bootstrappers.sln +++ b/Bootstrappers/Bootstrappers.sln @@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BookGen.Launcher", "BookGen EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bookgen.Win", "Bookgen.Win\Bookgen.Win.csproj", "{9832DC6D-3F44-4221-BB59-2FF64D650C56}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BookGen.FormulaEdit", "BookGen.FormulaEdit\BookGen.FormulaEdit.csproj", "{9C1A7AB7-8703-4CAD-8A7E-13A753F25778}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,10 @@ Global {9832DC6D-3F44-4221-BB59-2FF64D650C56}.Debug|Any CPU.Build.0 = Debug|Any CPU {9832DC6D-3F44-4221-BB59-2FF64D650C56}.Release|Any CPU.ActiveCfg = Release|Any CPU {9832DC6D-3F44-4221-BB59-2FF64D650C56}.Release|Any CPU.Build.0 = Release|Any CPU + {9C1A7AB7-8703-4CAD-8A7E-13A753F25778}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9C1A7AB7-8703-4CAD-8A7E-13A753F25778}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C1A7AB7-8703-4CAD-8A7E-13A753F25778}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9C1A7AB7-8703-4CAD-8A7E-13A753F25778}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Branding/.gitattributes b/Branding/.gitattributes new file mode 100644 index 00000000..fb813510 --- /dev/null +++ b/Branding/.gitattributes @@ -0,0 +1,8 @@ +icon-laucher.ico filter=lfs diff=lfs merge=lfs -text +icon-shell.ico filter=lfs diff=lfs merge=lfs -text +icon-bookgen.ico filter=lfs diff=lfs merge=lfs -text +icon-formulaedit.ico filter=lfs diff=lfs merge=lfs -text +icon-bookgen.png filter=lfs diff=lfs merge=lfs -text +icon-formulaedit.png filter=lfs diff=lfs merge=lfs -text +icon-launcher.png filter=lfs diff=lfs merge=lfs -text +icon-shell.png filter=lfs diff=lfs merge=lfs -text diff --git a/Branding/background.png b/Branding/background.png new file mode 100644 index 00000000..ce500c2c Binary files /dev/null and b/Branding/background.png differ diff --git a/Branding/background.svg b/Branding/background.svg new file mode 100644 index 00000000..429f3429 --- /dev/null +++ b/Branding/background.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + B + + diff --git a/Branding/bookgen-icon.svg b/Branding/bookgen-icon.svg deleted file mode 100644 index ccb0237e..00000000 --- a/Branding/bookgen-icon.svg +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/Branding/bookgen-splash.png b/Branding/bookgen-splash.png deleted file mode 100644 index 66a9041d..00000000 Binary files a/Branding/bookgen-splash.png and /dev/null differ diff --git a/Branding/bookgen-splash.svg b/Branding/bookgen-splash.svg deleted file mode 100644 index 94ae9e3f..00000000 --- a/Branding/bookgen-splash.svg +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - - - - - - - - - - - - - ookGen - ookGen - - - - - - - - - - - - diff --git a/Branding/icon-bookgen.ico b/Branding/icon-bookgen.ico new file mode 100644 index 00000000..18c1f5df --- /dev/null +++ b/Branding/icon-bookgen.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e4e60aa43a918a76f9871fb221b3cde0402b6e49de6d2a59e8ca888b0d922748 +size 15406 diff --git a/Branding/icon-bookgen.png b/Branding/icon-bookgen.png new file mode 100644 index 00000000..cb66826e --- /dev/null +++ b/Branding/icon-bookgen.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30ab4a8f2df19bc568f2d28f8da4526ba74a73674a9ff3618fd8fcf2a4e6e146 +size 19154 diff --git a/Branding/icon-formulaedit.ico b/Branding/icon-formulaedit.ico new file mode 100644 index 00000000..5621a9f6 --- /dev/null +++ b/Branding/icon-formulaedit.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be260828da5822171a886afc33b8ca0e7281cf9053082b32be31aaa428b16dcc +size 15406 diff --git a/Branding/icon-formulaedit.png b/Branding/icon-formulaedit.png new file mode 100644 index 00000000..cda9f36d --- /dev/null +++ b/Branding/icon-formulaedit.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9c3cad07861eadc35f26152f87dfa9d0c0617a253c32295fd8b289152743c8e4 +size 26336 diff --git a/Branding/icon-laucher.ico b/Branding/icon-laucher.ico new file mode 100644 index 00000000..f8cc0520 --- /dev/null +++ b/Branding/icon-laucher.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dc349789fc642209b0cbadc7f3c5df34e553044bc78270b602c31d6b007adabc +size 15406 diff --git a/Branding/icon-launcher.png b/Branding/icon-launcher.png new file mode 100644 index 00000000..5ed31f18 --- /dev/null +++ b/Branding/icon-launcher.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c69a328cd395c106429b82d48e3a529b2a07f1b6d4f46f25b33a2417ef7424b +size 10054 diff --git a/Branding/icon-shell.ico b/Branding/icon-shell.ico new file mode 100644 index 00000000..8b823c2f --- /dev/null +++ b/Branding/icon-shell.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cf148b44ed6dae7edc86d98c0d7aa718fe582fc03188a86b5c2d407d8aae211e +size 15406 diff --git a/Branding/icon-shell.png b/Branding/icon-shell.png new file mode 100644 index 00000000..08d20876 --- /dev/null +++ b/Branding/icon-shell.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c312673b75e65e210cca2c14e5139c05cf743d0eea7c5f7ca93e022fa541c86d +size 19411 diff --git a/Changelog.md b/Changelog.md index 336627d9..02410dd6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,7 @@ -# 2024. 06. 04 (Pre release) +# 2024. 06. +* New: Added new command to create new files +* New: C# scripting support in markdown files via the '''script ''' tag * New: Added autocomplete in bookgen shell for basic git commands * New: ISO build now includes powershell core * New: Added table of contents support via the [toc] tag in markdown diff --git a/Commands.md b/Commands.md index 7951e035..6d302157 100644 --- a/Commands.md +++ b/Commands.md @@ -300,6 +300,14 @@ Arguments: Note: the command gets the data from the clipboard and the generated markdown is also written to the clipboard. +# New + +Creates a new file with the given template. If no arguments are given, then +it lists the available templates with descriptions. + +`Bookgen New {-t [template]} {-o [fileName]}` +`Bookgen New {--template [template]} {--output [fileName]}` + # Pack Pack / backup the bookgen related files of the current project into a diff --git a/Icon.ico b/Icon.ico deleted file mode 100644 index 2e008a58..00000000 Binary files a/Icon.ico and /dev/null differ diff --git a/Libs/BookGen.Contents/BookGen.Contents.csproj b/Libs/BookGen.Contents/BookGen.Contents.csproj index 7baf2cb3..ce4ce86e 100644 --- a/Libs/BookGen.Contents/BookGen.Contents.csproj +++ b/Libs/BookGen.Contents/BookGen.Contents.csproj @@ -8,6 +8,15 @@ false + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest @@ -15,21 +24,9 @@ PreserveNewest - - PreserveNewest - PreserveNewest - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - diff --git a/Libs/BookGen.Contents/ShortCodes/Readme.txt b/Libs/BookGen.Contents/ShortCodes/Readme.txt deleted file mode 100644 index 14641b3e..00000000 --- a/Libs/BookGen.Contents/ShortCodes/Readme.txt +++ /dev/null @@ -1,8 +0,0 @@ -Shortcodes - -Place your custom implemented Shortcodes in this folder. - -1. Create a C# project -2. Add reference to BookGen.Api assembly -3. Implement the ITemplateShortCode interface -4. Place compiled dll to this folder. \ No newline at end of file diff --git a/Libs/BookGen.Domain/Formulas/Formulas.cs b/Libs/BookGen.Domain/Formulas/Formulas.cs new file mode 100644 index 00000000..b3f05a38 --- /dev/null +++ b/Libs/BookGen.Domain/Formulas/Formulas.cs @@ -0,0 +1,16 @@ +using System.Xml.Serialization; + +namespace BookGen.Domain.Formulas; + +[XmlRoot("formulas")] +public sealed class Formulas +{ + [XmlArray("f")] + [XmlArrayItem("formula")] + public CData[] Items { get; set; } + + public Formulas() + { + Items = []; + } +} diff --git a/Libs/BookGen.DomainServices/BookGen.DomainServices.csproj b/Libs/BookGen.DomainServices/BookGen.DomainServices.csproj index a06be2b5..a9c10af7 100644 --- a/Libs/BookGen.DomainServices/BookGen.DomainServices.csproj +++ b/Libs/BookGen.DomainServices/BookGen.DomainServices.csproj @@ -20,8 +20,10 @@ - - + + + + diff --git a/Libs/BookGen.DomainServices/Markdown/BookGenPipeline.cs b/Libs/BookGen.DomainServices/Markdown/BookGenPipeline.cs index 127d902f..14856e82 100644 --- a/Libs/BookGen.DomainServices/Markdown/BookGenPipeline.cs +++ b/Libs/BookGen.DomainServices/Markdown/BookGenPipeline.cs @@ -4,6 +4,7 @@ //----------------------------------------------------------------------------- using BookGen.DomainServices.Markdown.Modifiers; +using BookGen.DomainServices.Markdown.Scripting; using BookGen.DomainServices.Markdown.TableOfContents; using BookGen.Interfaces; using Markdig; @@ -17,6 +18,7 @@ public static MarkdownPipeline Web get => new MarkdownPipelineBuilder() .UseAdvancedExtensions() .UseTableOfContents() + .UseScripting() .Use() .Build(); } @@ -26,6 +28,7 @@ public static MarkdownPipeline Print get => new MarkdownPipelineBuilder() .UseAdvancedExtensions() .UseTableOfContents() + .UseScripting() .Use() .Build(); } @@ -41,6 +44,7 @@ public static MarkdownPipeline Epub get => new MarkdownPipelineBuilder() .UseAdvancedExtensions() .UseTableOfContents() + .UseScripting() .Use() .Build(); } @@ -50,6 +54,7 @@ public static MarkdownPipeline Preview get => new MarkdownPipelineBuilder() .UseAdvancedExtensions() .UseTableOfContents() + .UseScripting() .Use() .Build(); } @@ -59,6 +64,7 @@ public static MarkdownPipeline Wordpress get => new MarkdownPipelineBuilder() .UseAdvancedExtensions() .UseTableOfContents() + .UseScripting() .Use() .Build(); } diff --git a/Libs/BookGen.DomainServices/Markdown/Modifiers/WordpressModifier.cs b/Libs/BookGen.DomainServices/Markdown/Modifiers/WordpressModifier.cs index be2bb19d..a9fb82e9 100644 --- a/Libs/BookGen.DomainServices/Markdown/Modifiers/WordpressModifier.cs +++ b/Libs/BookGen.DomainServices/Markdown/Modifiers/WordpressModifier.cs @@ -35,6 +35,7 @@ public void Setup(MarkdownPipelineBuilder pipeline) public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) { // Method intentionally left empty. + PipelineHelpers.SetupSyntaxRenderForWeb(renderer); } private void PipelineOnDocumentProcessed(MarkdownDocument document) diff --git a/Libs/BookGen.DomainServices/Markdown/Scripting/CsharpScriptExecutor.cs b/Libs/BookGen.DomainServices/Markdown/Scripting/CsharpScriptExecutor.cs new file mode 100644 index 00000000..c0e86eaa --- /dev/null +++ b/Libs/BookGen.DomainServices/Markdown/Scripting/CsharpScriptExecutor.cs @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// (c) 2024 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; + +using Microsoft.CodeAnalysis.CSharp.Scripting; +using Microsoft.CodeAnalysis.Scripting; + +namespace BookGen.DomainServices.Markdown.Scripting; +internal sealed class CsharpScriptExecutor +{ + private readonly ScriptOptions _options; + + public CsharpScriptExecutor() + { + _options = ScriptOptions.Default + .WithCheckOverflow(true) + .WithFileEncoding(Encoding.UTF8) + .WithLanguageVersion(Microsoft.CodeAnalysis.CSharp.LanguageVersion.Latest) + .WithOptimizationLevel(Microsoft.CodeAnalysis.OptimizationLevel.Release) + .WithImports(GetImports()) + .WithReferences(GetReferences()); + } + + private static HashSet GetReferences() + { + HashSet references = + [ + typeof(Console).Assembly, + typeof(object).Assembly, + typeof(List<>).Assembly, + typeof(Enumerable).Assembly, + typeof(Task).Assembly, + typeof(File).Assembly, + typeof(Regex).Assembly + ]; + return references; + } + + private static IEnumerable GetImports() + { + yield return "System"; + yield return "System.Collections.Generic"; + yield return "System.Linq"; + yield return "System.Text"; + yield return "System.Threading.Tasks"; + yield return "System.IO"; + yield return "System.Text.RegularExpressions"; + } + + public async Task Execute(string code) + { + var originalConsoleOut = Console.Out; + var originalConsoleIn = Console.In; + using (var writer = new StringWriter()) + { + Console.SetOut(writer); + Console.SetIn(new NotSupportedReader()); + try + { + await CSharpScript.RunAsync(code, _options); + } + catch (Exception ex) + { + writer.WriteLine(ex.Message); + } + finally + { + Console.SetIn(originalConsoleIn); + Console.SetOut(originalConsoleOut); + } + return writer.ToString(); + } + } +} diff --git a/Libs/BookGen.DomainServices/Markdown/Scripting/NotSupportedReader.cs b/Libs/BookGen.DomainServices/Markdown/Scripting/NotSupportedReader.cs new file mode 100644 index 00000000..b7ff6234 --- /dev/null +++ b/Libs/BookGen.DomainServices/Markdown/Scripting/NotSupportedReader.cs @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// (c) 2024 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +namespace BookGen.DomainServices.Markdown.Scripting; +internal sealed class NotSupportedReader : TextReader +{ + private const string Message = "Console Inputs is not supported in scripting"; + + public override int Read() + => throw new NotSupportedException(Message); + + public override int Read(char[] buffer, int index, int count) + => throw new NotSupportedException(Message); + + public override int Read(Span buffer) + => throw new NotSupportedException(Message); + + public override Task ReadAsync(char[] buffer, int index, int count) + => throw new NotSupportedException(Message); + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + => throw new NotSupportedException(Message); + + public override int ReadBlock(char[] buffer, int index, int count) + => throw new NotSupportedException(Message); + + public override int ReadBlock(Span buffer) + => throw new NotSupportedException(Message); + + public override Task ReadBlockAsync(char[] buffer, int index, int count) + => throw new NotSupportedException(Message); + + public override ValueTask ReadBlockAsync(Memory buffer, CancellationToken cancellationToken = default) + => throw new NotSupportedException(Message); + + public override string? ReadLine() + => throw new NotSupportedException(Message); + + public override Task ReadLineAsync() + => throw new NotSupportedException(Message); + + public override ValueTask ReadLineAsync(CancellationToken cancellationToken) + => throw new NotSupportedException(Message); + + public override string ReadToEnd() + => throw new NotSupportedException(Message); + + public override Task ReadToEndAsync() + => throw new NotSupportedException(Message); + + public override Task ReadToEndAsync(CancellationToken cancellationToken) + => throw new NotSupportedException(Message); +} diff --git a/Libs/BookGen.DomainServices/Markdown/Scripting/ScriptBlock.cs b/Libs/BookGen.DomainServices/Markdown/Scripting/ScriptBlock.cs new file mode 100644 index 00000000..ae0e7366 --- /dev/null +++ b/Libs/BookGen.DomainServices/Markdown/Scripting/ScriptBlock.cs @@ -0,0 +1,21 @@ +//----------------------------------------------------------------------------- +// (c) 2024 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Markdig.Parsers; +using Markdig.Syntax; + +namespace BookGen.DomainServices.Markdown.Scripting; + +internal sealed class ScriptBlock : FencedCodeBlock +{ + public ScriptBlock(BlockParser parser) : base(parser) + { + } + + public string GetScript() + { + return string.Join(Environment.NewLine, Lines); + } +} diff --git a/Libs/BookGen.DomainServices/Markdown/Scripting/ScriptBlockParser.cs b/Libs/BookGen.DomainServices/Markdown/Scripting/ScriptBlockParser.cs new file mode 100644 index 00000000..80a62306 --- /dev/null +++ b/Libs/BookGen.DomainServices/Markdown/Scripting/ScriptBlockParser.cs @@ -0,0 +1,86 @@ +//----------------------------------------------------------------------------- +// (c) 2024 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Markdig.Helpers; +using Markdig.Parsers; +using Markdig.Syntax; + +namespace BookGen.DomainServices.Markdown.Scripting; + +internal sealed class ScriptBlockParser : FencedBlockParserBase +{ + public ScriptBlockParser() + { + OpeningCharacters = new[] { '\'' }; + InfoPrefix = "script"; + InfoParser = ScriptInfoParser; + } + + protected override ScriptBlock CreateFencedBlock(BlockProcessor processor) + { + var block = new ScriptBlock(this); + return block; + } + + private bool ScriptInfoParser(BlockProcessor state, + ref StringSlice line, + IFencedBlock fenced, + char openingCharacter) + { + string infoString; + string? argString = null; + + var c = line.CurrentChar; + // An info string cannot contain any backsticks + int firstSpace = -1; + for (int i = line.Start; i <= line.End; i++) + { + c = line.Text[i]; + if (c == '\'') + { + return false; + } + + if (firstSpace < 0 && c.IsSpaceOrTab()) + { + firstSpace = i; + } + } + + if (firstSpace > 0) + { + infoString = line.Text[line.Start..firstSpace].Trim(); + + // Skip any spaces after info string + firstSpace++; + while (true) + { + c = line[firstSpace]; + if (c.IsSpaceOrTab()) + { + firstSpace++; + } + else + { + break; + } + } + + argString = line.Text.Substring(firstSpace, line.End - firstSpace + 1).Trim(); + } + else + { + infoString = line.ToString().Trim(); + } + + if (infoString != "script") + return false; + + fenced.Info = HtmlHelper.Unescape(infoString); + fenced.Arguments = HtmlHelper.Unescape(argString); + + return true; + } +} \ No newline at end of file diff --git a/Libs/BookGen.DomainServices/Markdown/Scripting/ScriptBlockRenderer.cs b/Libs/BookGen.DomainServices/Markdown/Scripting/ScriptBlockRenderer.cs new file mode 100644 index 00000000..9e27b523 --- /dev/null +++ b/Libs/BookGen.DomainServices/Markdown/Scripting/ScriptBlockRenderer.cs @@ -0,0 +1,25 @@ +//----------------------------------------------------------------------------- +// (c) 2024 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Markdig.Renderers; +using Markdig.Renderers.Html; + +namespace BookGen.DomainServices.Markdown.Scripting; + +internal sealed class ScriptBlockRenderer : HtmlObjectRenderer +{ + private readonly CsharpScriptExecutor _scriptExecutor; + + public ScriptBlockRenderer(CsharpScriptExecutor scriptExecutor) + { + _scriptExecutor = scriptExecutor; + } + + protected override void Write(HtmlRenderer renderer, ScriptBlock obj) + { + var result = _scriptExecutor.Execute(obj.GetScript()).Result; + renderer.Write(result); + } +} diff --git a/Libs/BookGen.DomainServices/Markdown/Scripting/ScriptExtension.cs b/Libs/BookGen.DomainServices/Markdown/Scripting/ScriptExtension.cs new file mode 100644 index 00000000..598e7da4 --- /dev/null +++ b/Libs/BookGen.DomainServices/Markdown/Scripting/ScriptExtension.cs @@ -0,0 +1,36 @@ +//----------------------------------------------------------------------------- +// (c) 2024 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Markdig; +using Markdig.Renderers; +using Markdig.Renderers.Html; + +namespace BookGen.DomainServices.Markdown.Scripting; + +internal sealed class ScriptExtension : IMarkdownExtension +{ + private readonly CsharpScriptExecutor _scriptExecutor; + + public ScriptExtension() + { + _scriptExecutor = new CsharpScriptExecutor(); + } + + public void Setup(MarkdownPipelineBuilder pipeline) + { + pipeline.BlockParsers.AddIfNotAlready(new ScriptBlockParser()); + } + + public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) + { + if (renderer is HtmlRenderer htmlRenderer) + { + if (htmlRenderer.ObjectRenderers.Contains()) + htmlRenderer.ObjectRenderers.InsertBefore(new ScriptBlockRenderer(_scriptExecutor)); + else + htmlRenderer.ObjectRenderers.AddIfNotAlready(new ScriptBlockRenderer(_scriptExecutor)); + } + } +} diff --git a/Libs/BookGen.DomainServices/Markdown/Scripting/ScriptExtensions.cs b/Libs/BookGen.DomainServices/Markdown/Scripting/ScriptExtensions.cs new file mode 100644 index 00000000..77064a3b --- /dev/null +++ b/Libs/BookGen.DomainServices/Markdown/Scripting/ScriptExtensions.cs @@ -0,0 +1,17 @@ +//----------------------------------------------------------------------------- +// (c) 2024 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Markdig; + +namespace BookGen.DomainServices.Markdown.Scripting; + +internal static class ScriptExtensions +{ + public static MarkdownPipelineBuilder UseScripting(this MarkdownPipelineBuilder pipelineBuilder) + { + pipelineBuilder.Extensions.AddIfNotAlready(new ScriptExtension()); + return pipelineBuilder; + } +} diff --git a/Libs/BookGen.DomainServices/ShellAutoCompleteFilter.cs b/Libs/BookGen.DomainServices/ShellAutoCompleteFilter.cs index 985dc109..85e25378 100644 --- a/Libs/BookGen.DomainServices/ShellAutoCompleteFilter.cs +++ b/Libs/BookGen.DomainServices/ShellAutoCompleteFilter.cs @@ -11,19 +11,39 @@ public static IEnumerable DoFilter(IReadOnlyList candidates, str { if (candidates.Count < 1 || string.IsNullOrEmpty(input) - || cursorposition < 0 + || cursorposition < 0 || cursorposition > input.Length) { - return Array.Empty(); + yield break; } string prefix = input[..cursorposition]; - int prefixLength = cursorposition >= prefix.Length ? prefix.Length - 1: cursorposition; + int prefixLength = cursorposition >= prefix.Length ? prefix.Length - 1 : cursorposition; var filteredCommands = candidates - .Where(cmd => cmd.StartsWith(prefix)) - .Select(cmd => cmd.Substring(prefixLength).TrimStart()); + .Where(cmd => cmd.StartsWith(prefix)); - return filteredCommands; + foreach (var filtered in filteredCommands) + { + var (start, _) = GetWordPositions(filtered).FirstOrDefault(p => cursorposition >= p.start && cursorposition <= p.end); + yield return filtered[start..]; + } + } + + internal static IEnumerable<(int start, int end)> GetWordPositions(string str) + { + int start = 0; + int pos = 0; + foreach (var c in str) + { + if (char.IsWhiteSpace(c)) + { + var item = (start, pos); + start = pos + 1; + yield return item; + } + ++pos; + } + yield return (start, pos); } } diff --git a/Libs/BookGen.DomainServices/TerminalProfileInstaller.cs b/Libs/BookGen.DomainServices/TerminalProfileInstaller.cs index f7c1b821..4d3f37ae 100644 --- a/Libs/BookGen.DomainServices/TerminalProfileInstaller.cs +++ b/Libs/BookGen.DomainServices/TerminalProfileInstaller.cs @@ -23,12 +23,12 @@ private static WindowsTerminalProfile CreateProfile(string title) { StartingDirectory = "%userprofile%", Hidden = false, - Icon = Path.Combine(AppContext.BaseDirectory, "bookgen-icon.png"), + Icon = Path.Combine(AppContext.BaseDirectory, "icon-bookgen.png"), Name = title, TabTitle = title, CommandLine = GetCommandLine(), ColorScheme = WindowsTerminalScheme.DefaultShemeName, - BackgroundImage = Path.Combine(AppContext.BaseDirectory, "bookgen-bg.png"), + BackgroundImage = Path.Combine(AppContext.BaseDirectory, "background.png"), BackgroundImageStretchMode = TerminalBackgroundImageStretchMode.None, BackgroundImageAlignment = TerminalBackgroundImageAlignment.BottomRight, UseAcrylic = true, diff --git a/Libs/BookGen.Gui/MenuEnums/MainMenuAction.cs b/Libs/BookGen.Gui/MenuEnums/MainMenuAction.cs index b31375d1..36732880 100644 --- a/Libs/BookGen.Gui/MenuEnums/MainMenuAction.cs +++ b/Libs/BookGen.Gui/MenuEnums/MainMenuAction.cs @@ -29,8 +29,6 @@ public enum MainMenuAction PreviewServer, [Text("ID_Stat")] Stat, - [Text("ID_Update")] - Update, [Text("ID_Help")] Help, [Text("ID_Exit")] diff --git a/Libs/BookGen.Gui/Properties/Resources.Designer.cs b/Libs/BookGen.Gui/Properties/Resources.Designer.cs index 3db503b7..99ee141b 100644 --- a/Libs/BookGen.Gui/Properties/Resources.Designer.cs +++ b/Libs/BookGen.Gui/Properties/Resources.Designer.cs @@ -213,15 +213,6 @@ internal static string ID_Stat { } } - /// - /// Looks up a localized string similar to Check for updates. - /// - internal static string ID_Update { - get { - return ResourceManager.GetString("ID_Update", resourceCulture); - } - } - /// /// Looks up a localized string similar to Validate config. /// diff --git a/Libs/BookGen.Gui/Properties/Resources.resx b/Libs/BookGen.Gui/Properties/Resources.resx index 9dd01794..7d044728 100644 --- a/Libs/BookGen.Gui/Properties/Resources.resx +++ b/Libs/BookGen.Gui/Properties/Resources.resx @@ -168,9 +168,6 @@ Statistics - - Check for updates - Validate config diff --git a/Libs/BookGen.Resources/BookGen.Resources.csproj b/Libs/BookGen.Resources/BookGen.Resources.csproj index b38d8a3e..ed6a2697 100644 --- a/Libs/BookGen.Resources/BookGen.Resources.csproj +++ b/Libs/BookGen.Resources/BookGen.Resources.csproj @@ -41,6 +41,11 @@ + + + + + @@ -73,6 +78,11 @@ + + + + + diff --git a/Libs/BookGen.Resources/NewFiles.cs b/Libs/BookGen.Resources/NewFiles.cs new file mode 100644 index 00000000..e44334c8 --- /dev/null +++ b/Libs/BookGen.Resources/NewFiles.cs @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// (c) 2024 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Text; + +namespace BookGen.Resources; +public sealed class NewFiles +{ + private readonly Dictionary _files; + + internal record class FileKey + { + public FileKey(string name, string path) + { + Name = name; + Path = path; + } + + public string Name { get; } + public string Path { get; } + } + + public NewFiles() + { + _files = new Dictionary + { + { new FileKey("html", "/NewFiles/html.html"), "Blank HTML5 page" }, + { new FileKey("markdown", "/NewFiles/markdown.md"), "Blank markdown document" }, + { new FileKey("mvpcss", "/NewFiles/mvpcss.css"), "A minimalist stylesheet for HTML elements - https://andybrewer.github.io/mvp/" }, + { new FileKey("newcss", "/NewFiles/newcss.css"), "new.css is a classless CSS framework to write modern websites using only HTML. - https://newcss.net/" }, + { new FileKey("simple", "/NewFiles/simple.css"), "Simple.css is a CSS framework that makes semantic HTML look good, really quickly. - https://simplecss.org/" }, + }; + } + + public string GetHelp() + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine("Available templates:"); + foreach (var file in _files) + { + sb.AppendLine($"* {file.Key.Name}") + .AppendLine($" {file.Value}"); + } + return sb.ToString(); + } + + public bool TryGetFile(string name, out string content) + { + var key = _files.Keys.FirstOrDefault(k => k.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); + if (key != null) + { + content = ResourceHandler.GetResourceFile(key.Path); + return true; + } + content = string.Empty; + return false; + } +} diff --git a/Libs/BookGen.Resources/NewFiles/html.html b/Libs/BookGen.Resources/NewFiles/html.html new file mode 100644 index 00000000..4d70452f --- /dev/null +++ b/Libs/BookGen.Resources/NewFiles/html.html @@ -0,0 +1,14 @@ + + + + + + + Page Title + + + +

Content

+ + + diff --git a/Libs/BookGen.Resources/NewFiles/markdown.md b/Libs/BookGen.Resources/NewFiles/markdown.md new file mode 100644 index 00000000..c9abf1fc --- /dev/null +++ b/Libs/BookGen.Resources/NewFiles/markdown.md @@ -0,0 +1,3 @@ +# Title + +content \ No newline at end of file diff --git a/Libs/BookGen.Resources/NewFiles/mvpcss.css b/Libs/BookGen.Resources/NewFiles/mvpcss.css new file mode 100644 index 00000000..a44c50bc --- /dev/null +++ b/Libs/BookGen.Resources/NewFiles/mvpcss.css @@ -0,0 +1,538 @@ +/* MVP.css v1.15 - https://github.com/andybrewer/mvp */ + +:root { + --active-brightness: 0.85; + --border-radius: 5px; + --box-shadow: 2px 2px 10px; + --color-accent: #118bee15; + --color-bg: #fff; + --color-bg-secondary: #e9e9e9; + --color-link: #118bee; + --color-secondary: #920de9; + --color-secondary-accent: #920de90b; + --color-shadow: #f4f4f4; + --color-table: #118bee; + --color-text: #000; + --color-text-secondary: #999; + --color-scrollbar: #cacae8; + --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + --hover-brightness: 1.2; + --justify-important: center; + --justify-normal: left; + --line-height: 1.5; + --width-card: 285px; + --width-card-medium: 460px; + --width-card-wide: 800px; + --width-content: 1080px; +} + +@media (prefers-color-scheme: dark) { + :root[color-mode="user"] { + --color-accent: #0097fc4f; + --color-bg: #333; + --color-bg-secondary: #555; + --color-link: #0097fc; + --color-secondary: #e20de9; + --color-secondary-accent: #e20de94f; + --color-shadow: #bbbbbb20; + --color-table: #0097fc; + --color-text: #f7f7f7; + --color-text-secondary: #aaa; + } +} + +html { + scroll-behavior: smooth; +} + +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } +} + +/* Layout */ +article aside { + background: var(--color-secondary-accent); + border-left: 4px solid var(--color-secondary); + padding: 0.01rem 0.8rem; +} + +body { + background: var(--color-bg); + color: var(--color-text); + font-family: var(--font-family); + line-height: var(--line-height); + margin: 0; + overflow-x: hidden; + padding: 0; +} + +footer, +header, +main { + margin: 0 auto; + max-width: var(--width-content); + padding: 3rem 1rem; +} + +hr { + background-color: var(--color-bg-secondary); + border: none; + height: 1px; + margin: 4rem 0; + width: 100%; +} + +section { + display: flex; + flex-wrap: wrap; + justify-content: var(--justify-important); +} + +section img, +article img { + max-width: 100%; +} + +section pre { + overflow: auto; +} + +section aside { + border: 1px solid var(--color-bg-secondary); + border-radius: var(--border-radius); + box-shadow: var(--box-shadow) var(--color-shadow); + margin: 1rem; + padding: 1.25rem; + width: var(--width-card); +} + +section aside:hover { + box-shadow: var(--box-shadow) var(--color-bg-secondary); +} + +[hidden] { + display: none; +} + +/* Headers */ +article header, +div header, +main header { + padding-top: 0; +} + +header { + text-align: var(--justify-important); +} + +header a b, +header a em, +header a i, +header a strong { + margin-left: 0.5rem; + margin-right: 0.5rem; +} + +header nav img { + margin: 1rem 0; +} + +section header { + padding-top: 0; + width: 100%; +} + +/* Nav */ +nav { + align-items: center; + display: flex; + font-weight: bold; + justify-content: space-between; + margin-bottom: 7rem; +} + +nav ul { + list-style: none; + padding: 0; +} + +nav ul li { + display: inline-block; + margin: 0 0.5rem; + position: relative; + text-align: left; +} + +/* Nav Dropdown */ +nav ul li:hover ul { + display: block; +} + +nav ul li ul { + background: var(--color-bg); + border: 1px solid var(--color-bg-secondary); + border-radius: var(--border-radius); + box-shadow: var(--box-shadow) var(--color-shadow); + display: none; + height: auto; + left: -2px; + padding: .5rem 1rem; + position: absolute; + top: 1.7rem; + white-space: nowrap; + width: auto; + z-index: 1; +} + +nav ul li ul::before { + /* fill gap above to make mousing over them easier */ + content: ""; + position: absolute; + left: 0; + right: 0; + top: -0.5rem; + height: 0.5rem; +} + +nav ul li ul li, +nav ul li ul li a { + display: block; +} + +/* Typography */ +code, +samp { + background-color: var(--color-accent); + border-radius: var(--border-radius); + color: var(--color-text); + display: inline-block; + margin: 0 0.1rem; + padding: 0 0.5rem; +} + +details { + margin: 1.3rem 0; +} + +details summary { + font-weight: bold; + cursor: pointer; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + line-height: var(--line-height); + text-wrap: balance; +} + +mark { + padding: 0.1rem; +} + +ol li, +ul li { + padding: 0.2rem 0; +} + +p { + margin: 0.75rem 0; + padding: 0; + width: 100%; +} + +pre { + margin: 1rem 0; + max-width: var(--width-card-wide); + padding: 1rem 0; +} + +pre code, +pre samp { + display: block; + max-width: var(--width-card-wide); + padding: 0.5rem 2rem; + white-space: pre-wrap; +} + +small { + color: var(--color-text-secondary); +} + +sup { + background-color: var(--color-secondary); + border-radius: var(--border-radius); + color: var(--color-bg); + font-size: xx-small; + font-weight: bold; + margin: 0.2rem; + padding: 0.2rem 0.3rem; + position: relative; + top: -2px; +} + +/* Links */ +a { + color: var(--color-link); + display: inline-block; + font-weight: bold; + text-decoration: underline; +} + +a:hover { + filter: brightness(var(--hover-brightness)); +} + +a:active { + filter: brightness(var(--active-brightness)); +} + +a b, +a em, +a i, +a strong, +button, +input[type="submit"] { + border-radius: var(--border-radius); + display: inline-block; + font-size: medium; + font-weight: bold; + line-height: var(--line-height); + margin: 0.5rem 0; + padding: 1rem 2rem; +} + +button, +input[type="submit"] { + font-family: var(--font-family); +} + +button:hover, +input[type="submit"]:hover { + cursor: pointer; + filter: brightness(var(--hover-brightness)); +} + +button:active, +input[type="submit"]:active { + filter: brightness(var(--active-brightness)); +} + +a b, +a strong, +button, +input[type="submit"] { + background-color: var(--color-link); + border: 2px solid var(--color-link); + color: var(--color-bg); +} + +a em, +a i { + border: 2px solid var(--color-link); + border-radius: var(--border-radius); + color: var(--color-link); + display: inline-block; + padding: 1rem 2rem; +} + +article aside a { + color: var(--color-secondary); +} + +/* Images */ +figure { + margin: 0; + padding: 0; +} + +figure img { + max-width: 100%; +} + +figure figcaption { + color: var(--color-text-secondary); +} + +/* Forms */ +button:disabled, +input:disabled { + background: var(--color-bg-secondary); + border-color: var(--color-bg-secondary); + color: var(--color-text-secondary); + cursor: not-allowed; +} + +button[disabled]:hover, +input[type="submit"][disabled]:hover { + filter: none; +} + +form { + border: 1px solid var(--color-bg-secondary); + border-radius: var(--border-radius); + box-shadow: var(--box-shadow) var(--color-shadow); + display: block; + max-width: var(--width-card-wide); + min-width: var(--width-card); + padding: 1.5rem; + text-align: var(--justify-normal); +} + +form header { + margin: 1.5rem 0; + padding: 1.5rem 0; +} + +input, +label, +select, +textarea { + display: block; + font-size: inherit; + max-width: var(--width-card-wide); +} + +input[type="checkbox"], +input[type="radio"] { + display: inline-block; +} + +input[type="checkbox"]+label, +input[type="radio"]+label { + display: inline-block; + font-weight: normal; + position: relative; + top: 1px; +} + +input[type="range"] { + padding: 0.4rem 0; +} + +input, +select, +textarea { + border: 1px solid var(--color-bg-secondary); + border-radius: var(--border-radius); + margin-bottom: 1rem; + padding: 0.4rem 0.8rem; +} + +input[type="text"], +input[type="password"] +textarea { + width: calc(100% - 1.6rem); +} + +input[readonly], +textarea[readonly] { + background-color: var(--color-bg-secondary); +} + +label { + font-weight: bold; + margin-bottom: 0.2rem; +} + +/* Popups */ +dialog { + border: 1px solid var(--color-bg-secondary); + border-radius: var(--border-radius); + box-shadow: var(--box-shadow) var(--color-shadow); + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 50%; + z-index: 999; +} + +/* Tables */ +table { + border: 1px solid var(--color-bg-secondary); + border-radius: var(--border-radius); + border-spacing: 0; + display: inline-block; + max-width: 100%; + overflow-x: auto; + padding: 0; + white-space: nowrap; +} + +table td, +table th, +table tr { + padding: 0.4rem 0.8rem; + text-align: var(--justify-important); +} + +table thead { + background-color: var(--color-table); + border-collapse: collapse; + border-radius: var(--border-radius); + color: var(--color-bg); + margin: 0; + padding: 0; +} + +table thead tr:first-child th:first-child { + border-top-left-radius: var(--border-radius); +} + +table thead tr:first-child th:last-child { + border-top-right-radius: var(--border-radius); +} + +table thead th:first-child, +table tr td:first-child { + text-align: var(--justify-normal); +} + +table tr:nth-child(even) { + background-color: var(--color-accent); +} + +/* Quotes */ +blockquote { + display: block; + font-size: x-large; + line-height: var(--line-height); + margin: 1rem auto; + max-width: var(--width-card-medium); + padding: 1.5rem 1rem; + text-align: var(--justify-important); +} + +blockquote footer { + color: var(--color-text-secondary); + display: block; + font-size: small; + line-height: var(--line-height); + padding: 1.5rem 0; +} + +/* Scrollbars */ +* { + scrollbar-width: thin; + scrollbar-color: var(--color-scrollbar) transparent; +} + +*::-webkit-scrollbar { + width: 5px; + height: 5px; +} + +*::-webkit-scrollbar-track { + background: transparent; +} + +*::-webkit-scrollbar-thumb { + background-color: var(--color-scrollbar); + border-radius: 10px; +} diff --git a/Libs/BookGen.Resources/NewFiles/newcss.css b/Libs/BookGen.Resources/NewFiles/newcss.css new file mode 100644 index 00000000..586a6938 --- /dev/null +++ b/Libs/BookGen.Resources/NewFiles/newcss.css @@ -0,0 +1,451 @@ +:root { + --nc-font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + --nc-font-mono: Consolas, monaco, 'Ubuntu Mono', 'Liberation Mono', 'Courier New', Courier, monospace; + + /* Light theme */ + --nc-tx-1: #000000; + --nc-tx-2: #1A1A1A; + --nc-bg-1: #FFFFFF; + --nc-bg-2: #F6F8FA; + --nc-bg-3: #E5E7EB; + --nc-lk-1: #0070F3; + --nc-lk-2: #0366D6; + --nc-lk-tx: #FFFFFF; + --nc-ac-1: #79FFE1; + --nc-ac-tx: #0C4047; + + /* Dark theme */ + --nc-d-tx-1: #ffffff; + --nc-d-tx-2: #eeeeee; + --nc-d-bg-1: #000000; + --nc-d-bg-2: #111111; + --nc-d-bg-3: #222222; + --nc-d-lk-1: #3291FF; + --nc-d-lk-2: #0070F3; + --nc-d-lk-tx: #FFFFFF; + --nc-d-ac-1: #7928CA; + --nc-d-ac-tx: #FFFFFF; +} + +@media (prefers-color-scheme: dark) { + :root { + --nc-tx-1: var(--nc-d-tx-1); + --nc-tx-2: var(--nc-d-tx-2); + --nc-bg-1: var(--nc-d-bg-1); + --nc-bg-2: var(--nc-d-bg-2); + --nc-bg-3: var(--nc-d-bg-3); + --nc-lk-1: var(--nc-d-lk-1); + --nc-lk-2: var(--nc-d-lk-2); + --nc-lk-tx: var(--nc--dlk-tx); + --nc-ac-1: var(--nc-d-ac-1); + --nc-ac-tx: var(--nc--dac-tx); + } +} + +* { + /* Reset margins and padding */ + margin: 0; + padding: 0; +} + +address, +area, +article, +aside, +audio, +blockquote, +datalist, +details, +dl, +fieldset, +figure, +form, +input, +iframe, +img, +meter, +nav, +ol, +optgroup, +option, +output, +p, +pre, +progress, +ruby, +section, +table, +textarea, +ul, +video { + /* Margins for most elements */ + margin-bottom: 1rem; +} + +html,input,select,button { + /* Set body font family and some finicky elements */ + font-family: var(--nc-font-sans); +} + +body { + /* Center body in page */ + margin: 0 auto; + max-width: 750px; + padding: 2rem; + border-radius: 6px; + overflow-x: hidden; + word-break: break-word; + overflow-wrap: break-word; + background: var(--nc-bg-1); + + /* Main body text */ + color: var(--nc-tx-2); + font-size: 1.03rem; + line-height: 1.5; +} + +::selection { + /* Set background color for selected text */ + background: var(--nc-ac-1); + color: var(--nc-ac-tx); +} + +h1,h2,h3,h4,h5,h6 { + line-height: 1; + color: var(--nc-tx-1); + padding-top: .875rem; +} + +h1, +h2, +h3 { + color: var(--nc-tx-1); + padding-bottom: 2px; + margin-bottom: 8px; + border-bottom: 1px solid var(--nc-bg-2); +} + +h4, +h5, +h6 { + margin-bottom: .3rem; +} + +h1 { + font-size: 2.25rem; +} + +h2 { + font-size: 1.85rem; +} + +h3 { + font-size: 1.55rem; +} + +h4 { + font-size: 1.25rem; +} + +h5 { + font-size: 1rem; +} + +h6 { + font-size: .875rem; +} + +a { + color: var(--nc-lk-1); +} + +a:hover { + color: var(--nc-lk-2); +} + +abbr:hover { + /* Set the '?' cursor while hovering an abbreviation */ + cursor: help; +} + +blockquote { + padding: 1.5rem; + background: var(--nc-bg-2); + border-left: 5px solid var(--nc-bg-3); +} + +abbr { + cursor: help; +} + +blockquote *:last-child { + padding-bottom: 0; + margin-bottom: 0; +} + +header { + background: var(--nc-bg-2); + border-bottom: 1px solid var(--nc-bg-3); + padding: 2rem 1.5rem; + + /* This sets the right and left margins to cancel out the body's margins. It's width is still the same, but the background stretches across the page's width. */ + + margin: -2rem calc(50% - 50vw) 2rem; + + /* Shorthand for: + + margin-top: -2rem; + margin-bottom: 2rem; + + margin-left: calc(50% - 50vw); + margin-right: calc(50% - 50vw); */ + + padding-left: calc(50vw - 50%); + padding-right: calc(50vw - 50%); +} + +header h1, +header h2, +header h3 { + padding-bottom: 0; + border-bottom: 0; +} + +header > *:first-child { + margin-top: 0; + padding-top: 0; +} + +header > *:last-child { + margin-bottom: 0; +} + +a button, +button, +input[type="submit"], +input[type="reset"], +input[type="button"] { + font-size: 1rem; + display: inline-block; + padding: 6px 12px; + text-align: center; + text-decoration: none; + white-space: nowrap; + background: var(--nc-lk-1); + color: var(--nc-lk-tx); + border: 0; + border-radius: 4px; + box-sizing: border-box; + cursor: pointer; + color: var(--nc-lk-tx); +} + +a button[disabled], +button[disabled], +input[type="submit"][disabled], +input[type="reset"][disabled], +input[type="button"][disabled] { + cursor: default; + opacity: .5; + + /* Set the [X] cursor while hovering a disabled link */ + cursor: not-allowed; +} + +.button:focus, +.button:enabled:hover, +button:focus, +button:enabled:hover, +input[type="submit"]:focus, +input[type="submit"]:enabled:hover, +input[type="reset"]:focus, +input[type="reset"]:enabled:hover, +input[type="button"]:focus, +input[type="button"]:enabled:hover { + background: var(--nc-lk-2); +} + +a img { + margin-bottom: 0px; +} + +code, +pre, +kbd, +samp { + /* Set the font family for monospaced elements */ + font-family: var(--nc-font-mono); +} + +code, +samp, +kbd, +pre { + /* The main preformatted style. This is changed slightly across different cases. */ + background: var(--nc-bg-2); + border: 1px solid var(--nc-bg-3); + border-radius: 4px; + padding: 3px 6px; + /* ↓ font-size is relative to containing element, so it scales for titles*/ + font-size: 0.9em; +} + +kbd { + /* Makes the kbd element look like a keyboard key */ + border-bottom: 3px solid var(--nc-bg-3); +} + +pre { + padding: 1rem 1.4rem; + max-width: 100%; + overflow: auto; +} + +pre code { + /* When is in a
, reset it's formatting to blend in */
+	background: inherit;
+	font-size: inherit;
+	color: inherit;
+	border: 0;
+	padding: 0;
+	margin: 0;
+}
+
+code pre {
+	/* When 
 is in a , reset it's formatting to blend in */
+	display: inline;
+	background: inherit;
+	font-size: inherit;
+	color: inherit;
+	border: 0;
+	padding: 0;
+	margin: 0;
+}
+
+details {
+	/* Make the 
look more "clickable" */ + padding: .6rem 1rem; + background: var(--nc-bg-2); + border: 1px solid var(--nc-bg-3); + border-radius: 4px; +} + +summary { + /* Makes the look more like a "clickable" link with the pointer cursor */ + cursor: pointer; + font-weight: bold; +} + +details[open] { + /* Adjust the
padding while open */ + padding-bottom: .75rem; +} + +details[open] summary { + /* Adjust the
padding while open */ + margin-bottom: 6px; +} + +details[open]>*:last-child { + /* Resets the bottom margin of the last element in the
while
is opened. This prevents double margins/paddings. */ + margin-bottom: 0; +} + +dt { + font-weight: bold; +} + +dd::before { + /* Add an arrow to data table definitions */ + content: '→ '; +} + +hr { + /* Reset the border of the
separator, then set a better line */ + border: 0; + border-bottom: 1px solid var(--nc-bg-3); + margin: 1rem auto; +} + +fieldset { + margin-top: 1rem; + padding: 2rem; + border: 1px solid var(--nc-bg-3); + border-radius: 4px; +} + +legend { + padding: auto .5rem; +} + +table { + /* border-collapse sets the table's elements to share borders, rather than floating as separate "boxes". */ + border-collapse: collapse; + width: 100% +} + +td, +th { + border: 1px solid var(--nc-bg-3); + text-align: left; + padding: .5rem; +} + +th { + background: var(--nc-bg-2); +} + +tr:nth-child(even) { + /* Set every other cell slightly darker. Improves readability. */ + background: var(--nc-bg-2); +} + +table caption { + font-weight: bold; + margin-bottom: .5rem; +} + +textarea { + /* Don't let the