Improve offline support

* Add a command line option for supplying local web files
* Consolodate references into a single file
* Resolves #1486
This commit is contained in:
ds5678 2024-09-07 09:12:15 -07:00
parent 06010534f1
commit fd0f5fd014
7 changed files with 174 additions and 44 deletions

View File

@ -1,25 +0,0 @@
namespace AssetRipper.GUI.Web;
internal static class Bootstrap
{
internal static void WriteStyleSheetReference(TextWriter writer)
{
new Link(writer)
{
Rel = "stylesheet",
Href = "https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css",
Integrity = "sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN",
Crossorigin = "anonymous"
}.Close();
}
internal static void WriteScriptReference(TextWriter writer)
{
new Script(writer)
{
Src = "https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js",
Integrity = "sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL",
Crossorigin = "anonymous"
}.Close();
}
}

View File

@ -15,7 +15,7 @@ public abstract class DefaultPage : HtmlPage
new Meta(writer).WithCharset("utf-8").Close();
new Meta(writer).WithName("viewport").WithContent("width=device-width, initial-scale=1.0").Close();
new Title(writer).Close(GetTitle());
Bootstrap.WriteStyleSheetReference(writer);
OnlineDependencies.Bootstrap.WriteStyleSheetReference(writer);
new Link(writer).WithRel("stylesheet").WithHref("/css/site.css").Close();
}
using (new Body(writer).WithCustomAttribute("data-bs-theme", "dark").End())
@ -202,8 +202,8 @@ public abstract class DefaultPage : HtmlPage
protected virtual void WriteScriptReferences(TextWriter writer)
{
writer.Write("""<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>""");
Bootstrap.WriteScriptReference(writer);
OnlineDependencies.Popper.WriteScriptReference(writer);
OnlineDependencies.Bootstrap.WriteScriptReference(writer);
new Script(writer).WithSrc("/js/site.js").Close();
}
}

View File

@ -0,0 +1,141 @@
using AssetRipper.Import;
namespace AssetRipper.GUI.Web;
/// <summary>
/// Contains references to online dependencies.
/// </summary>
internal static class OnlineDependencies
{
// Dependencies are ordered alphabetically in this class.
/// <summary>
/// <see href="https://www.babylonjs.com/"/>
/// </summary>
internal static class Babylon
{
internal static void WriteScriptReference(TextWriter writer)
{
if (!TryWriteCachedScriptReference(writer, "/js/babylon.js"))
{
new Script(writer)
{
Src = "https://cdn.babylonjs.com/babylon.js"
}.Close();
}
if (!TryWriteCachedScriptReference(writer, "/js/babylonjs.loaders.min.js"))
{
new Script(writer)
{
Src = "https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"
}.Close();
}
}
}
/// <summary>
/// <see href="https://getbootstrap.com/"/>
/// </summary>
internal static class Bootstrap
{
internal static void WriteStyleSheetReference(TextWriter writer)
{
if (!TryWriteCachedStyleSheetReference(writer, "/css/bootstrap.min.css"))
{
new Link(writer)
{
Rel = "stylesheet",
Href = "https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css",
Integrity = "sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN",
Crossorigin = "anonymous"
}.Close();
}
}
internal static void WriteScriptReference(TextWriter writer)
{
if (!TryWriteCachedScriptReference(writer, "/js/bootstrap.bundle.min.js"))
{
new Script(writer)
{
Src = "https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js",
Integrity = "sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL",
Crossorigin = "anonymous"
}.Close();
}
}
}
/// <summary>
/// <see href="https://jquery.com/"/>
/// </summary>
internal static class Popper
{
internal static void WriteScriptReference(TextWriter writer)
{
if (!TryWriteCachedScriptReference(writer, "/js/popper.min.js"))
{
new Script(writer)
{
Src = "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js",
Integrity = "sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r",
Crossorigin = "anonymous"
}.Close();
}
}
}
/// <summary>
/// <see href="https://vuejs.org/"/>
/// </summary>
internal static class Vue
{
internal static void WriteScriptReference(TextWriter writer)
{
if (!TryWriteCachedScriptReference(writer, "/js/vue.global.prod.js") && !TryWriteCachedScriptReference(writer, "/js/vue.global.js"))
{
if (AssetRipperRuntimeInformation.Build.Debug)
{
new Script(writer)
{
Src = "https://unpkg.com/vue@3/dist/vue.global.js"
}.Close();
}
else
{
new Script(writer)
{
Src = "https://unpkg.com/vue@3/dist/vue.global.prod.js"
}.Close();
}
}
}
}
private static bool TryWriteCachedStyleSheetReference(TextWriter writer, string path)
{
if (StaticContentLoader.Cache.ContainsKey(path))
{
new Link(writer)
{
Rel = "stylesheet",
Href = path
}.Close();
return true;
}
return false;
}
private static bool TryWriteCachedScriptReference(TextWriter writer, string path)
{
if (StaticContentLoader.Cache.ContainsKey(path))
{
new Script(writer)
{
Src = path
}.Close();
return true;
}
return false;
}
}

View File

@ -36,8 +36,7 @@ public sealed class ViewPage : DefaultPage
protected override void WriteScriptReferences(TextWriter writer)
{
base.WriteScriptReferences(writer);
new Script(writer).WithSrc("https://cdn.babylonjs.com/babylon.js").Close();
new Script(writer).WithSrc("https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js").Close();
OnlineDependencies.Babylon.WriteScriptReference(writer);
new Script(writer).WithSrc("/js/mesh_preview.js").Close();
}
}

View File

@ -6,7 +6,7 @@ namespace AssetRipper.GUI.Web;
public static partial class StaticContentLoader
{
private const string Prefix = "AssetRipper.GUI.Web.StaticContent.";
private static readonly ConcurrentDictionary<string, byte[]> Cache = new();
public static ConcurrentDictionary<string, byte[]> Cache { get; } = new();
public static async ValueTask<byte[]> Load(string path)
{

View File

@ -1,19 +1,10 @@
using AssetRipper.Import;
namespace AssetRipper.GUI.Web;
namespace AssetRipper.GUI.Web;
public abstract class VuePage : DefaultPage
{
protected override void WriteScriptReferences(TextWriter writer)
{
base.WriteScriptReferences(writer);
if (AssetRipperRuntimeInformation.Build.Debug)
{
writer.Write("""<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>""");
}
else
{
writer.Write("""<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>""");
}
OnlineDependencies.Vue.WriteScriptReference(writer);
}
}

View File

@ -47,16 +47,40 @@ public static class WebApplicationLauncher
getDefaultValue: () => Defaults.LaunchBrowser);
rootCommand.AddOption(launchBrowserOption);
Option<string[]> localWebFilesOption = new Option<string[]>(
name: "--local-web-file",
description: "Files provided with this option will replace online sources.",
getDefaultValue: () => []);
rootCommand.AddOption(localWebFilesOption);
bool shouldRun = false;
int port = Defaults.Port;
bool launchBrowser = Defaults.LaunchBrowser;
rootCommand.SetHandler((int portParsed, bool launchBrowserParsed) =>
rootCommand.SetHandler((int portParsed, bool launchBrowserParsed, string[] localWebFilesParsed) =>
{
shouldRun = true;
port = portParsed;
launchBrowser = launchBrowserParsed;
}, portOption, launchBrowserOption);
foreach (string localWebFile in localWebFilesParsed)
{
if (File.Exists(localWebFile))
{
string fileName = Path.GetFileName(localWebFile);
string webPrefix = Path.GetExtension(fileName) switch
{
".css" => "/css/",
".js" => "/js/",
_ => "/"
};
StaticContentLoader.Cache.TryAdd(webPrefix + fileName, File.ReadAllBytes(localWebFile));
}
else
{
Console.WriteLine($"File '{localWebFile}' does not exist.");
}
}
}, portOption, launchBrowserOption, localWebFilesOption);
rootCommand.Invoke(args);