diff --git a/.gitignore b/.gitignore
index 5344bd22..9b5846d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,10 @@
+#################
+## Checker
+#################
+
+Checker/.out.spv
+tests/out/
+
#################
## Eclipse
#################
diff --git a/Checker/checker-linux.fsproj b/Checker/checker-linux.fsproj
new file mode 100644
index 00000000..b8bc3d70
--- /dev/null
+++ b/Checker/checker-linux.fsproj
@@ -0,0 +1,32 @@
+
+
+
+ Exe
+ net48
+
+
+
+
+
+
+
+
+ ..\lib\Argu.dll
+
+
+ ..\lib\FSharp.Core.dll
+
+
+ ..\lib\OpenTK.dll
+
+
+ Shader Minifier Library
+ True
+
+
+
+
+ $(MSBuildProjectDirectory)/..
+
+
+
diff --git a/Checker/compression_test.fs b/Checker/compression_test.fs
index bfc22d56..1785a153 100644
--- a/Checker/compression_test.fs
+++ b/Checker/compression_test.fs
@@ -1,5 +1,6 @@
module CompressionTests
+open ShaderMinifier
open System.Runtime.InteropServices
open System.IO
open System.Text
@@ -47,16 +48,17 @@ let log fmt =
Printf.ksprintf logger fmt
-let compressionTest args files =
- Options.init(args)
+let compressionTest args filenames =
+ let options = Options.init(args)
let minified =
use out = new StringWriter()
- let shaders, exportedNames = ShaderMinifier.minifyFiles [|for f in files -> "tests/real/" + f|]
- Formatter.print out shaders exportedNames Options.Text
+ let files = [|for f in filenames -> f, File.ReadAllText("tests/real/" + f)|]
+ let minifier = Minifier(options, files)
+ minifier.Format(out)
out.ToString().ToCharArray()
let pointer = &&minified.[0]
- log "%-40s " (match files with [f] -> f | f::_ -> f + "..." | [] -> "?")
+ log "%-40s " (match filenames with [f] -> f | f::_ -> f + " (and others)" | [] -> "?")
log "%5d " minified.Length
let compressedSize = Crinkler.ApproximateModels4k(pointer, minified.Length)
log "=> %8.3f\n" compressedSize
diff --git a/Checker/glslang.exe b/Checker/glslang.exe
new file mode 100644
index 00000000..15af68a1
Binary files /dev/null and b/Checker/glslang.exe differ
diff --git a/Checker/main.fs b/Checker/main.fs
index 541d30b6..b899d915 100644
--- a/Checker/main.fs
+++ b/Checker/main.fs
@@ -1,61 +1,157 @@
open OpenTK.Graphics.OpenGL
-open Options.Globals
open System
open System.Diagnostics
open System.IO
open Argu
open System.Text.RegularExpressions
+open ShaderMinifier
type CliArguments =
| Update_Golden
- | Skip_GLSL_Compile
+ | Compile_Golden
+ | Skip_Glslang_Compile
+ | GLSL_Driver_Compile
| Skip_Performance_Tests
interface IArgParserTemplate with
member s.Usage =
match s with
- | Update_Golden _ -> "Update the golden tests"
- | Skip_GLSL_Compile _ -> "Skip the GLSL compilation of shaders"
- | Skip_Performance_Tests _ -> "Skip the tests of performance"
+ | Update_Golden -> "Update the golden tests"
+ | Compile_Golden -> "Always test compilation of the golden tests"
+ | Skip_Glslang_Compile -> "Skip testing compilation of shaders with glslang"
+ | GLSL_Driver_Compile -> "Test GLSL compilation of shaders agaist current OpenGL driver"
+ | Skip_Performance_Tests -> "Skip the tests of performance"
let cliArgs = ArgumentParser.Create().ParseCommandLine()
//let cliArgs = ArgumentParser.Create().ParseCommandLine([|"--update-golden"|])
let initOpenTK () =
// OpenTK requires a GameWindow
- if not (cliArgs.Contains(Skip_GLSL_Compile)) then
+ if cliArgs.Contains(GLSL_Driver_Compile) then
new OpenTK.GameWindow() |> ignore
-// Return true if the file can be compiled as a GLSL shader.
-let canBeCompiled content =
- if cliArgs.Contains(Skip_GLSL_Compile) then
+// Return true if the file can be compiled as a GLSL shader by the current OpenGL driver
+let canBeCompiledByDriver stage (content: string) =
+ if not (cliArgs.Contains(GLSL_Driver_Compile)) then
true
else
- let fragmentShader = GL.CreateShader(ShaderType.FragmentShader)
- GL.ShaderSource(fragmentShader, content)
- GL.CompileShader(fragmentShader)
- let info = GL.GetShaderInfoLog(fragmentShader)
- GL.DeleteShader(fragmentShader)
- if info = "" then
- true
+ let fragmentShader = GL.CreateShader(
+ match stage with
+ | "vert" -> ShaderType.VertexShader
+ | "geom" -> ShaderType.GeometryShader
+ | "frag" -> ShaderType.FragmentShader
+ | "comp" -> ShaderType.ComputeShader
+ | _ -> ShaderType.FragmentShader
+ )
+ GL.ShaderSource(fragmentShader, content)
+ GL.CompileShader(fragmentShader)
+ let driverinfo = sprintf "%s / %s / %s / %s" (GL.GetString(StringName.Vendor)) (GL.GetString(StringName.Renderer)) (GL.GetString(StringName.Version)) (GL.GetString(StringName.ShadingLanguageVersion))
+ let mutable status = 0
+ GL.GetShader(fragmentShader, ShaderParameter.CompileStatus, &status)
+ let info = GL.GetShaderInfoLog(fragmentShader)
+ let info = if info.EndsWith("\n") then info else info + "\n"
+ GL.DeleteShader(fragmentShader)
+ if status = 1 then
+ // getting warnings is not reliable: the shader may be cached but the warnings are not
+ //if info.Length > 1 then
+ // printf "%s\nDriver compilation warnings: %s" driverinfo info
+ true
+ else
+ printf "%s\nDriver compilation failed:\n%s" driverinfo info
+ false
+
+let canBeCompiledByGlslang lang stage (content: string) =
+ if cliArgs.Contains(Skip_Glslang_Compile) then
+ true
+ else
+ let mutable tgtopt = ""
+ let mutable versopt = "-d"
+ if lang = "hlsl" then
+ versopt <- "-D --hlsl-dx9-compatible"
+ tgtopt <- "--target-env spirv1.6 -V"
+ else
+ // glslang bug workarounds:
+ // after 330, spir-v targeting must be enabled or else not all OpenGL GLSL features are supported
+ // for 1xx languages, spir-v targeting must be disabled
+ if Regex.Match(content, @"^\s*#version\s+[34]", RegexOptions.Multiline).Success then
+ tgtopt <- "--target-env opengl --amb --aml -o Checker/.out.spv"
+ // 300es is not supported, override it to 310es when detected
+ if Regex.Match(content, @"^\s*#version\s+300\s+es", RegexOptions.Multiline).Success then
+ versopt <- "--glsl-version 310es"
+ let si = ProcessStartInfo()
+ si.FileName <-
+ if Environment.OSVersion.Platform = PlatformID.Win32NT then
+ "Checker/glslang.exe"
else
- printfn "compilation failed: %s" info
- false
+ // must be installed externally
+ "glslang"
+ si.Arguments <- sprintf "%s %s --stdin -S %s" versopt tgtopt stage
+ si.UseShellExecute <- false
+ si.RedirectStandardInput <- true
+ si.RedirectStandardOutput <- true
+ si.RedirectStandardError <- true
+ let proc = Process.Start(si)
+ proc.StandardInput.Write(content)
+ proc.StandardInput.Close()
+ let info = (proc.StandardOutput.ReadToEnd()) + (proc.StandardError.ReadToEnd())
+ proc.WaitForExit()
+ if proc.ExitCode = 0 then
+ true
+ else
+ printf "glslang compilation failed:\n%s\n" info
+ false
+
+// Note that "100" is ESSL, "#version 100 es" is not valid!
+let shaderlangs = set ("110 120 130 140 150 330 400 410 420 430 440 450 460 100 300es hlsl shadertoy".Split ' ')
-let doMinify content =
- let arr = ShaderMinifier.minify [|"input", content|] |> fst |> Array.map (fun s -> s.code)
- Printer.printText arr.[0]
+let canBeCompiled lang stage content =
+ if not (shaderlangs.Contains(lang)) then raise (ArgumentException(sprintf "unknown lang %s" lang))
+ let mutable lang = lang
+ let mutable stage = stage
+ let mutable fullsrc = content
+ if lang <> "hlsl" then
+ let mutable lineadj = 0
+ let setline num file =
+ // "#line line srcnum" sets line numbering in error messages
+ sprintf "\n#line %d %d\n" (num + lineadj) file
+ if lang = "shadertoy" then
+ let header = File.ReadAllText "tests/shadertoy.h.glsl"
+ fullsrc <- header + (setline 1 1) + fullsrc
+ lang <- "300es"
+ stage <- "frag"
+ let mutable verstr = Regex.Replace(lang, @"(\d+)(\w*)", @"$1 $2") // "450" -> "450", "300es" -> "300 es"
+ let versmatch = Regex.Match(fullsrc, @"^\s*#version\s+(\d.*)", RegexOptions.Multiline)
+ if versmatch.Success then
+ verstr <- versmatch.Groups[1].Value
+ // #line directive is off-by-one before GLSL 330
+ if Regex.Match(verstr, @"1[1-5]").Success then
+ lineadj <- -1
+ // If there is no "#version" line, add one
+ if not versmatch.Success then
+ fullsrc <- "#version " + verstr + (setline 1 1) + fullsrc
+ // If there is no "void main", add one (at the end, to not disturb any #version line)
+ if not (Regex.Match(" " + fullsrc, @"(?s)[^\w]void\s+main\s*(\s*)").Success) then
+ fullsrc <- fullsrc + (setline 1 2) + "void main(){}\n"
+ canBeCompiledByGlslang lang stage fullsrc && ((lang = "hlsl") || canBeCompiledByDriver stage fullsrc)
-let testMinifyAndCompile (file: string) =
+let doMinify options file content =
+ let minifier = Minifier(options, [|file, content|])
+ use tw = new System.IO.StringWriter()
+ minifier.Format(tw)
+ tw.ToString()
+
+let testMinifyAndCompile options lang (file: string) =
try
let content = File.ReadAllText file
- if not (canBeCompiled content) then
+ let stage = Regex.Match(file, @"[^\.]+$").Value
+ let compile = not (Regex.Match(content, @"^//NOCOMPILE").Success)
+ if compile && not (canBeCompiled lang stage content) then
printfn "Invalid input file '%s'" file
false
else
- let minified = doMinify content + "\n"
- if not (canBeCompiled minified) then
+ let minified = doMinify options file content + "\n"
+ if compile && not (canBeCompiled lang stage minified) then
printfn "Minification broke the file '%s'" file
printfn "%s" minified
false
@@ -71,28 +167,45 @@ let testPerformance files =
let contents = files |> Array.map File.ReadAllText
let stopwatch = Stopwatch.StartNew()
for str in contents do
- doMinify str |> ignore
+ let options = Options.init([|"--format"; "text"; "--no-remove-unused"|])
+ doMinify options "perf test" str |> ignore
let time = stopwatch.Elapsed
printfn "%i files minified in %f seconds." files.Length time.TotalSeconds
// Generated files may contain the Shader Minifier version.
// Ignore version changes in the tests.
-let versionRegex = new Regex(@"\bShader Minifier \d(\.\d+)+");
+let versionRegex = new Regex(@"\bShader Minifier \d(\.\d+)+")
let runCommand argv =
let cleanString (s: string) =
let s = s.Replace("\r\n", "\n").Trim()
versionRegex.Replace(s, "")
- Options.init argv
+ let options, filenames = Minifier.ParseOptionsWithFiles(argv)
let expected =
try File.ReadAllText options.outputName |> cleanString
with _ when cliArgs.Contains(Update_Golden) -> ""
| _ -> reraise ()
+ let files = [|for f in filenames -> f, File.ReadAllText(f)|]
+ let minifier = Minifier(options, files)
let result =
use out = new StringWriter()
- let shaders, exportedNames = ShaderMinifier.minifyFiles options.filenames
- Formatter.print out shaders exportedNames options.outputFormat
+ minifier.Format(out)
out.ToString() |> cleanString
+
+ let options = { options with outputFormat = Options.OutputFormat.IndentedText; exportKkpSymbolMaps = false}
+ if filenames.Length = 1 then
+ let shader = minifier.GetShaders[0]
+ let resultindented =
+ use out = new StringWriter()
+ minifier.Format(out, options)
+ out.ToString() |> cleanString
+ let outdir = "tests/out/" + Regex.Replace(options.outputName, @"^tests/(.*)/[^/]*$", @"$1") + "/"
+ let split = Regex.Match(shader.mangledFilename, @"(^.*)_([^_]+)$").Groups
+ let name = split[1].Value
+ let ext = split[2].Value
+ Directory.CreateDirectory outdir |> ignore
+ File.WriteAllText(outdir + name + ".minind." + ext, resultindented + "\n")
+
if result = expected then
printfn "Success: %s" options.outputName
0
@@ -108,12 +221,41 @@ let runCommand argv =
let testGolden () =
let commands = File.ReadAllLines "tests/commands.txt" |> Array.choose (fun line ->
let line = line.Trim()
- if line.Length = 0 || line.[0] = '#' then
+ if line.Length = 0 || line.[0] = '#' then
None
else
Some (line.Split([|' '|]))
)
- commands |> Array.sumBy runCommand
+ commands |> Array.Parallel.map runCommand |> Array.sum
+
+let runCompiler (argv: string array) =
+ if (cliArgs.Contains(Skip_Glslang_Compile)) && (not (cliArgs.Contains(GLSL_Driver_Compile))) then
+ 0
+ else
+ let lang = argv[0]
+ let stage = argv[1]
+ let filename = argv[2]
+ let content = File.ReadAllText filename
+ let info = sprintf "%s (%s %s)" filename lang stage
+ if canBeCompiled lang stage content then
+ printfn "Compiled: %s" info
+ 0
+ else
+ printfn "Compile failure: %s" info
+ 1
+
+let testCompiled () =
+ if not (cliArgs.Contains(Update_Golden) || cliArgs.Contains(Compile_Golden)) then
+ 0
+ else
+ let commands = File.ReadAllLines "tests/compile.txt" |> Array.choose (fun line ->
+ let line = line.Trim()
+ if line.Length = 0 || line.[0] = '#' then
+ None
+ else
+ Some (line.Split([|' '|]))
+ )
+ commands |> Array.Parallel.map runCompiler |> Array.sum
[]
let main argv =
@@ -121,18 +263,22 @@ let main argv =
// CompressionTests.run ()
initOpenTK()
- let mutable failures = testGolden()
- Options.init([|"--format"; "text"; "fake.frag"|])
- let unitTests = Directory.GetFiles("tests/unit", "*.frag")
- let realTests = Directory.GetFiles("tests/real", "*.frag");
+ let mutable failures = 0
+ failures <- failures + testGolden()
+ failures <- failures + testCompiled()
+ let srcfilter e = Regex.Match(e, @"\.(vert|geom|frag|comp)$").Success
+ let unitTests = Directory.GetFiles("tests/unit", "*") |> Array.filter srcfilter
+ let realTests = Directory.GetFiles("tests/real", "*.frag")
for f in unitTests do
- if not (testMinifyAndCompile f) then
+ // tests with no #version default to 110
+ let options = Options.init([|"--format"; "text"; "--no-remove-unused"|])
+ if not (testMinifyAndCompile options "110" f) then
failures <- failures + 1
testPerformance (Seq.concat [realTests; unitTests] |> Seq.toArray)
if failures = 0 then
printfn "All good."
else
printfn "%d failures." failures
-
+
//System.Console.ReadLine() |> ignore
if failures = 0 then 0 else 1
diff --git a/Example/Example.csproj b/Example/Example.csproj
new file mode 100644
index 00000000..2a1316b1
--- /dev/null
+++ b/Example/Example.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/Example/Program.cs b/Example/Program.cs
new file mode 100644
index 00000000..aeb15274
--- /dev/null
+++ b/Example/Program.cs
@@ -0,0 +1,18 @@
+/**
+ * Example of use of the ShaderMinifier library from C#.
+ */
+
+using ShaderMinifier;
+
+var shader = """
+out vec4 fragColor;
+void main()
+{
+ fragColor = vec4(1., 1., 1., 1.);
+}
+""";
+
+var file = Tuple.Create("filename.frag", shader);
+var options = Minifier.ParseOptions(new[] { "--format", "text" });
+var minifier = new Minifier(options, new[] { file });
+minifier.Format(System.Console.Out);
diff --git a/README.md b/README.md
index ec3d72d1..9f359e0b 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Shader Minifier
-[](https://ci.appveyor.com/project/laurentlb/shader-minifier/branch/master)
+[](https://ci.appveyor.com/project/laurentlb/shader-minifier-r5gmt)
Shader Minifier is a tool that minifies and obfuscates shader code (GLSL and
HLSL) without affecting its behaviour. It is also suitable for reducing the size
@@ -14,9 +14,12 @@ shader is minified by hand by experienced demosceners, Shader Minifier is often
able to optimize it further. See this [2010
report](https://www.ctrl-alt-test.fr/2010/glsl-minifier-smaller-and-smaller/).
-To be notified of new releases, use the watch feature of GitHub.
+Shader Minifier is available:
+* As an online website: https://ctrl-alt-test.fr/minifier/
+* As a command-line tool (download from [Releases](https://github.com/laurentlb/shader-minifier/releases))
+* As a .NET library (see [Example](https://github.com/laurentlb/shader-minifier/tree/master/Example))
-Try the online version here: https://ctrl-alt-test.fr/minifier/
+To be notified of new releases, use the watch feature of GitHub.
## Features
@@ -100,7 +103,7 @@ USAGE: Shader Minifier [--help] [-o ] [-v] [--hlsl]
[--preserve-all-globals] [--no-inlining]
[--aggressive-inlining] [--no-renaming]
[--no-renaming-list ] [--no-sequence]
- [--smoothstep] [--no-remove-unused]
+ [--no-remove-unused]
[--move-declarations] [...]
FILENAMES:
@@ -134,10 +137,11 @@ OPTIONS:
--no-renaming-list
Comma-separated list of functions to preserve
--no-sequence Do not use the comma operator trick
- --smoothstep Use IQ's smoothstep trick
--no-remove-unused Do not remove unused code
--move-declarations Move declarations to group them
--preprocess Evaluate some of the file preprocessor directives
+ --export-kkp-symbol-maps
+ Export kkpView symbol maps
--help display this list of options.
```
@@ -278,6 +282,12 @@ the output. If two functions have a different number of arguments, they may have
the same name in the output. This reduces the number of identifiers used by the
shader and make it more compression friendly.
+### kkpView symbol maps
+
+Shader Minifier can export symbol files that map the minified code back to names
+from the original source code. This lets you visualize shader size using
+https://github.com/ConspiracyHu/rekkrunchy-with-analytics
+
### Shader Minifier performance
On my machine, it takes around 1s to minify a shader. A lot of that time comes
@@ -305,12 +315,14 @@ them all at the same time by listing them all on the command-line.
- [Automatic inlining](TRANSFORMATIONS.md#Automatic-inlining)
- [Aggressive inlining](TRANSFORMATIONS.md#Aggressive-inlining)
- [Explicit inlining](TRANSFORMATIONS.md#Explicit-inlining)
+- [Reassignment merging](TRANSFORMATIONS.md#Reassignment-merging)
+- [Variable reuse](TRANSFORMATIONS.md#Variable-reuse)
- [Vector constructors](TRANSFORMATIONS.md#Vector-constructors)
- [Function predeclarations](TRANSFORMATIONS.md#Function-predeclarations)
- [Unused local variables](TRANSFORMATIONS.md#Unused-local-variables)
- [Dead code removal](TRANSFORMATIONS.md#Dead-code-removal)
- [Unused function removal](TRANSFORMATIONS.md#Unused-function-removal)
-- [Smoothstep transformation](TRANSFORMATIONS.md#Smoothstep-transformation)
+- [Other functions](TRANSFORMATIONS.md#Other-functions)
- [Renaming](TRANSFORMATIONS.md#Renaming)
diff --git a/SMBolero.Client/Main.fs b/SMBolero.Client/Main.fs
index c2e9f943..826ee787 100644
--- a/SMBolero.Client/Main.fs
+++ b/SMBolero.Client/Main.fs
@@ -5,20 +5,29 @@ open Bolero
open Bolero.Html
open Bolero.Remoting.Client
open Bolero.Templating.Client
+open ShaderMinifier
/// Routing endpoints definition.
type Page =
| [] Home
| [] FlagPage
- | [] Help
+ | [] About
+
+type Flags = {
+ mutable format: string
+ mutable inlining: bool
+ mutable removeUnused: bool
+ mutable renaming: bool
+ mutable other: string
+}
/// The Elmish application's model.
type Model =
{
page: Page
shaderInput: string
- shaderOutput: string
- flags: string
+ shaderSize: int
+ flags: Flags
error: string option
}
@@ -26,8 +35,8 @@ let initModel =
{
page = Home
shaderInput = "out vec4 fragColor;\nvoid main() {\n fragColor = vec4(1.,1.,1.,1.);\n}"
- shaderOutput = ""
- flags = "--format text"
+ shaderSize = 0
+ flags = {Flags.inlining=true; removeUnused=true; renaming=true; other=""; format="text"}
error = None
}
@@ -36,33 +45,50 @@ type Message =
| SetPage of Page
| Minify
| SetShader of string
- | SetFlags of string
| Error of exn
| ClearError
let minify flags content =
- try
- Options.init flags
- let shaders, exportedNames = ShaderMinifier.minify [|"input", content|]
- let out = new System.IO.StringWriter()
- Formatter.print out shaders exportedNames Options.Globals.options.outputFormat
- out.ToString()
- with
- | e -> e.Message
-
-let update message model =
+ let options = Minifier.ParseOptions(flags)
+ let minifier = Minifier(options, [|"input", content|])
+ let out = new System.IO.StringWriter()
+ minifier.Format(out)
+
+ let withLoc = new System.IO.StringWriter()
+ minifier.FormatWithLocations(withLoc)
+
+ out.ToString(), minifier.GetSize, withLoc.ToString()
+
+module API =
+ []
+ let minify flags content =
+ let result, _, _ = minify flags content
+ result
+
+let update (jsRuntime: Microsoft.JSInterop.IJSRuntime) message model =
match message with
| SetPage page ->
{ model with page = page }, Cmd.none
| Minify ->
- printfn "Minify %s" model.flags
- let out = minify (model.flags.Split(' ')) model.shaderInput
- { model with shaderOutput = out }, Cmd.none
+ let allFlags = [|
+ yield! ["--format"; model.flags.format]
+ if not model.flags.inlining then yield "--no-inlining"
+ if not model.flags.removeUnused then yield "--no-remove-unused"
+ if not model.flags.renaming then yield "--no-renaming"
+ yield! model.flags.other.Split([|' '|], System.StringSplitOptions.RemoveEmptyEntries)
+ |]
+ printfn "Minify %s" (String.concat " " allFlags)
+ try
+ let out, size, withLoc = minify allFlags model.shaderInput
+ jsRuntime.InvokeAsync("updateShader", [|model.shaderInput; withLoc|]) |> ignore
+ { model with shaderSize = size }, Cmd.none
+ with
+ | e ->
+ printfn "Error: %s" e.Message
+ jsRuntime.InvokeAsync("updateShader", [|model.shaderInput; e.Message|]) |> ignore
+ { model with shaderSize = 0 }, Cmd.none
| SetShader value ->
{ model with shaderInput = value }, Cmd.none
- | SetFlags value ->
- { model with flags = value }, Cmd.none
-
| Error exn ->
{ model with error = Some exn.Message }, Cmd.none
| ClearError ->
@@ -86,8 +112,12 @@ let homePage model dispatch =
Main.Home()
.Minify(fun _ -> dispatch Minify)
.ShaderInput(model.shaderInput, fun v -> dispatch (SetShader v))
- .ShaderOutput(model.shaderOutput)
- .Flags(model.flags, fun v -> dispatch (SetFlags v))
+ .ShaderSize(if model.shaderSize = 0 then "" else $"generated, size: {model.shaderSize}")
+ .Format(model.flags.format, fun v -> model.flags.format <- v)
+ .Inlining(model.flags.inlining, fun v -> model.flags.inlining <- v)
+ .RemoveUnused(model.flags.removeUnused, fun v -> model.flags.removeUnused <- v)
+ .Renaming(model.flags.renaming, fun v -> model.flags.renaming <- v)
+ .OtherFlags(model.flags.other, fun v -> model.flags.other <- v)
.Elt()
let menuItem (model: Model) (page: Page) (text: string) =
@@ -101,12 +131,12 @@ let view model dispatch =
.Menu(concat {
menuItem model Home "Minifier"
menuItem model FlagPage "Flags"
- menuItem model Help "Help"
+ menuItem model About "About"
})
.Body(
cond model.page <| function
| Home -> homePage model dispatch
- | Help -> aboutPage model dispatch
+ | About -> aboutPage model dispatch
| FlagPage -> flagPage model dispatch
)
.Error(
@@ -124,7 +154,7 @@ type MyApp() =
inherit ProgramComponent()
override this.Program =
- Program.mkProgram (fun _ -> initModel, Cmd.ofMsg (Message.SetPage Home)) update view
+ Program.mkProgram (fun _ -> initModel, Cmd.ofMsg (Message.SetPage Home)) (update this.JSRuntime) view
|> Program.withRouter router
#if DEBUG
|> Program.withHotReload
diff --git a/SMBolero.Client/wwwroot/css/index.css b/SMBolero.Client/wwwroot/css/index.css
index 346ef354..f28a0e2b 100644
--- a/SMBolero.Client/wwwroot/css/index.css
+++ b/SMBolero.Client/wwwroot/css/index.css
@@ -1,7 +1,11 @@
:root {
- --bg-main: #f1b99c;
- --bg-light: #ffece3;
- --border: #971f1f;
+ --bg-main: #101020;
+ --bg-light: #202040;
+ --border: #404080;
+ --text-color: #f0f0f0;
+ --alt-text-color: #aac;
+ scrollbar-width: thin;
+ scrollbar-color: var(--alt-text-color) transparent;
}
body {
@@ -9,7 +13,7 @@ body {
font-size: 1em;
margin: 0px;
margin-bottom: 10px;
- color: #333;
+ color: var(--text-color);
background-color: var(--bg-main);
}
@@ -17,9 +21,10 @@ body {
width: 100%;
}
+
.header {
background-color: black;
- color: var(--bg-light);
+ color: var(--alt-text-color);
padding-left: 188px;
padding-right: 0px;
line-height: 1.6em;
@@ -35,15 +40,12 @@ body {
.left {
font-size: 1em;
width: 164px;
- background: var(--bg-light) repeat-x top;
- border-bottom: solid 3px black;
- border-right: solid 3px black;
- border-left: solid 1px black;
- border-top: none;
+ background-color: var(--bg-light);
+ border: solid 1px #000;
position: absolute;
- padding-top: 6px;
top: 0px;
left: 10px;
+ color: var(--alt-text-color);
}
.center h1 {
@@ -65,10 +67,6 @@ body {
letter-spacing: 2px;
}
-.center form div {
- margin-bottom: 5px;
-}
-
.center h3 {
font-size: 1.1em;
margin-top: 1em;
@@ -80,41 +78,31 @@ body {
font-weight: bold;
}
-h5 {
- font-size: 1em;
+label {
+ min-width: 150px;
+ display: inline-block;
}
-h6 {
- font-size: 1em;
+summary {
+ cursor: pointer;
}
-label {
- display: inline-block;
- min-width: 150px;
- font-variant: small-caps;
- text-align: right;
+.center {
+ margin: 10px 10px 10px 188px;
}
-.center big {
- margin-top: 8px;
+details ul {
+ padding-left: 1em;
+ list-style: none;
}
-.center {
- margin-top: 10px;
- margin-left: 188px;
- margin-right: 10px;
- margin-bottom: 10px;
+details li {
+ display: flex;
}
#content {
- background-color: white;
- border-left: solid 1px black;
- border-top: solid 1px black;
- border-right: solid 3px black;
- border-bottom: solid 3px black;
- max-width: 800px;
+ max-width: 1500px;
margin: 0px auto;
- padding: 10px 20px;
}
#menu ul, #menu li {
@@ -126,100 +114,58 @@ label {
#menu a {
display: block;
margin: 0;
- margin-left: 4px;
- /* border-left: 4px transparent solid; */
- border-bottom: 1px dashed #A0C0F0;
text-decoration: none;
- color: #333;
- padding-left: 5px;
+ color: var(--alt-text-color);
+ padding: 4px;
}
- #menu a:hover {
- margin-left: 0px;
- border-left: 4px solid #002299;
- border-bottom: 1px solid #3388AA;
- background-color: var(--bg-main);
- color: black;
- }
-
-.field legend:hover {
- color: white;
- background-color: var(--border);
- border: solid 1px #225;
+#menu a:hover {
+ background-color: var(--bg-main);
}
.center a {
- color: #338;
+ color: #88f;
text-decoration: none;
font-weight: bold;
}
.center a:visited {
- color: #068;
text-decoration: none;
font-weight: bold;
}
.center a:hover {
- color: black;
text-decoration: underline;
font-weight: bold;
}
-.center .field a:hover {
- text-decoration: none;
-}
-
-.center p {
- margin-right: 10px;
- margin-left: 10px;
-}
-
-.center .item, .center .block, fieldset {
- margin-bottom: 20px;
- margin-left: 20px;
- margin-right: 20px;
- background-color: white;
- padding: 4px;
- border: solid 1px var(--border);
- display: block;
-}
-
-.center .item a {
- background-color: #AADDFF;
+button, textarea, input, select {
+ background-color: #1a1a2a;
+ border: 1px solid var(--border);
+ color: var(--text-color);
+ font-size: 1em;
}
-.center .item a:after {
- content: ':';
+button {
+ cursor: pointer;
+ background-color: #404080;
+ font-variant: small-caps;
+ font-size: 1em;
}
-
textarea {
box-sizing: border-box;
width: 100%;
+ padding: 4px;
+ scrollbar-width: thin;
}
-#display {
- width: 50em;
- background-color: black;
- display: block;
- color: white;
- font-family: monospace;
- font-size: 11px;
- border: 1px solid white;
-}
-
-.code {
- font-size: 11px;
- white-space: pre;
- font-family: monospace;
- border-left: 3px double black;
- margin: 5px;
- padding-left: 5px;
+code {
+ color: #ccf;
+ padding: 8px;
}
.center pre {
- font-size: 11px;
white-space: pre;
font-family: monospace;
border-left: solid 3px #666;
@@ -228,68 +174,90 @@ textarea {
padding-left: 8px;
}
-.box {
- border: 2px black solid;
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 0.8em;
- padding: 6px;
- background-color: #EEEEEE;
- color: black;
- font-variant: normal;
+#minify-btn {
+ margin: 8px 0;
+ padding: 8px;
+ float: right;
}
-.box h4 {
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 1em;
- border: 0px black;
- font-variant: normal;
- color: white;
- background-color: #AAAAAA;
+.output-header {
+ font-size: 0.8em;
+ color: var(--alt-text-color);
+ width: 49.5%;
text-align: center;
}
-.box2 {
- border: 1px black solid;
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 0.8em;
- padding: 4px;
- background-color: #EEEEEE;
- color: black;
- font-variant: normal;
+
+.clickable {
+ cursor: pointer;
}
-.center ul {
- list-style-type: square;
+/* Style for the source/minified columns. */
+
+.comment {
+ color: #888;
}
-legend {
- font-weight: bold;
- border: solid 1px var(--border);
+.output {
+ width: 100%;
+ display: flex;
+ flex-wrap: wrap;
+ place-content: space-between;
+ position: relative;
}
-.clickable {
+.source, .minified {
+ padding: 3em 0em 3em 1em;
+ font-size: 1.2em;
+ font-family: monospace;
+ white-space: pre-wrap;
+
+ line-break: anywhere;
+ box-sizing: border-box;
+ width: 49.5%;
+
+ overflow-y: scroll;
+ scrollbar-width: thin;
+ max-height: 100vh;
+ background-color: rgb(26, 26, 42);
+ border: 1px solid var(--border);
+}
+
+.highlight {
+ background-color: var(--bg-light);
+ color: #ff0 !important;
+ text-decoration-line: underline !important;
+}
+
+.ident {
+ color: #88c;
+ font-weight: bold;
cursor: pointer;
}
-img {
- border: none;
- background: transparent;
+.animated-icon {
+ transition: transform 0.5s;
+ animation: rotate 0.5s;
}
+@keyframes rotate {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+/* */
/* Let's be modern and support mobile*/
@media (max-width: 750px) {
.left {
- font-size: 1em;
position: static;
- background-color: var(--bg-light);
margin: 1em;
- padding: 10px;
- left: 0px;
width: auto;
- border-top: solid 1px black;
}
#menu {
@@ -316,7 +284,7 @@ img {
margin: 1em;
}
- #header {
+ .header {
padding-left: 1em;
}
diff --git a/SMBolero.Client/wwwroot/index.html b/SMBolero.Client/wwwroot/index.html
index 97d1a100..12d5bba2 100644
--- a/SMBolero.Client/wwwroot/index.html
+++ b/SMBolero.Client/wwwroot/index.html
@@ -9,5 +9,6 @@
Loading...
+