Commit: 62df743
Parent: b74bf17

Add syntax highlighting

Mårten Åsberg committed on 2025-11-18 at 18:41
.containerfile +14 -1
diff --git a/.containerfile b/.containerfile
index 23dcbaf..f0a4164 100644
@@ -2,18 +2,31 @@ FROM mcr.microsoft.com/dotnet/sdk:10.0@sha256:c7445f141c04f1a6b454181bd098dcfa60
WORKDIR /app
RUN apt-get update && apt-get install -y build-essential curl
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"
COPY ./GitBrowser.SyntaxHighlighter/GitBrowser.SyntaxHighlighter.csproj ./GitBrowser.SyntaxHighlighter/
COPY ./GitBrowser.SyntaxHighlighter/packages.lock.json ./GitBrowser.SyntaxHighlighter/
COPY ./GitBrowser.SyntaxHighlighter/Cargo.toml ./GitBrowser.SyntaxHighlighter/
COPY ./GitBrowser.SyntaxHighlighter/Cargo.lock ./GitBrowser.SyntaxHighlighter/
RUN mkdir ./GitBrowser.SyntaxHighlighter/src && touch ./GitBrowser.SyntaxHighlighter/src/lib.rs
RUN cargo build --release --locked --manifest-path ./GitBrowser.SyntaxHighlighter/Cargo.toml
COPY ./GitBrowser.slnx ./global.json ./Directory.Packages.props ./Directory.Build.props ./.gitignore ./.editorconfig ./
COPY ./GitBrowser/GitBrowser.csproj ./GitBrowser/packages.lock.json ./GitBrowser/
RUN dotnet restore --locked-mode
COPY ./GitBrowser.SyntaxHighlighter ./GitBrowser.SyntaxHighlighter
RUN touch -am ./GitBrowser.SyntaxHighlighter/src/lib.rs
COPY ./GitBrowser/ ./GitBrowser/
RUN dotnet build ./GitBrowser/GitBrowser.csproj --no-restore --configuration Release
RUN dotnet publish ./GitBrowser/GitBrowser.csproj --no-build --configuration Release --output /out
FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine@sha256:049f2d7d7acfcbf09e1d15eb4faccec6453b0a98f0cb54d53bcbdc3ed91e96c8 AS release
FROM mcr.microsoft.com/dotnet/aspnet:10.0@sha256:7c4246c1c384319346d45b3e24a10a21d5b6fc9b36a04790e1588148ff8055b0 AS release
WORKDIR /app
.dockerignore +1 -0
diff --git a/.dockerignore b/.dockerignore
index 0cf204b..aeefa8a 100644
@@ -34,6 +34,7 @@
**/[Oo]bj/
**/[Ll]og/
**/[Ll]ogs/
**/[Tt]arget/
# Visual Studio 2015/2017 cache/options directory
**/.vs/
.gitignore +1 -0
diff --git a/.gitignore b/.gitignore
index 1605798..cdb9009 100644
@@ -34,6 +34,7 @@ bld/
[Oo]bj/
[Ll]og/
[Ll]ogs/
[Tt]arget/
# Visual Studio 2015/2017 cache/options directory
.vs/
Directory.Packages.props +2 -1
diff --git a/Directory.Packages.props b/Directory.Packages.props
index c2e6eaa..1257cde 100644
@@ -4,6 +4,7 @@
</PropertyGroup>
<ItemGroup>
<GlobalPackageReference Include="CSharpier.MsBuild" Version="1.0.2" />
<PackageVersion Include="ColorfulCode" Version="1.0.0-preview1" />
<PackageVersion Include="LibGit2Sharp" Version="0.30.0" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.13.1" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.13.1" />
@@ -11,4 +12,4 @@
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.13.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.13.0" />
</ItemGroup>
</Project>
</Project>
\ No newline at end of file
GitBrowser.SyntaxHighlighter/Cargo.lock +442 -0
diff --git a/GitBrowser.SyntaxHighlighter/Cargo.lock b/GitBrowser.SyntaxHighlighter/Cargo.lock
new file mode 100644
index 0000000..8802806
@@ -0,0 +1,442 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]]
name = "cc"
version = "1.2.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]]
name = "deranged"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
dependencies = [
"powerfmt",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "find-msvc-tools"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
[[package]]
name = "flate2"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "hashbrown"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
[[package]]
name = "indexmap"
version = "2.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "libc"
version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "onig"
version = "6.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0"
dependencies = [
"bitflags",
"libc",
"once_cell",
"onig_sys",
]
[[package]]
name = "onig_sys"
version = "69.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f86c6eef3d6df15f23bcfb6af487cbd2fed4e5581d58d5bf1f5f8b7f6727dc"
dependencies = [
"cc",
"pkg-config",
]
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "plist"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07"
dependencies = [
"base64",
"indexmap",
"quick-xml",
"serde",
"time",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "proc-macro2"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quick-xml"
version = "0.38.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c"
dependencies = [
"memchr",
]
[[package]]
name = "quote"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex-syntax"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
"serde_core",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "simd-adler32"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "syn"
version = "2.0.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syntax-highlighter"
version = "0.1.0"
dependencies = [
"syntect",
]
[[package]]
name = "syntect"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "656b45c05d95a5704399aeef6bd0ddec7b2b3531b7c9e900abbf7c4d2190c925"
dependencies = [
"bincode",
"flate2",
"fnv",
"once_cell",
"onig",
"plist",
"regex-syntax",
"serde",
"serde_derive",
"serde_json",
"thiserror",
"walkdir",
"yaml-rust",
]
[[package]]
name = "thiserror"
version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "time"
version = "0.3.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
dependencies = [
"deranged",
"itoa",
"num-conv",
"powerfmt",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
[[package]]
name = "time-macros"
version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "winapi-util"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
GitBrowser.SyntaxHighlighter/Cargo.toml +11 -0
diff --git a/GitBrowser.SyntaxHighlighter/Cargo.toml b/GitBrowser.SyntaxHighlighter/Cargo.toml
new file mode 100644
index 0000000..89f65d6
@@ -0,0 +1,11 @@
[package]
name = "syntax-highlighter"
version = "0.1.0"
edition = "2024"
[lib]
name = "syntax_highlighter"
crate-type = [ "cdylib" ]
[dependencies]
syntect = "5.3.0"
GitBrowser.SyntaxHighlighter/GitBrowser.SyntaxHighlighter.csproj +37 -0
diff --git a/GitBrowser.SyntaxHighlighter/GitBrowser.SyntaxHighlighter.csproj b/GitBrowser.SyntaxHighlighter/GitBrowser.SyntaxHighlighter.csproj
new file mode 100644
index 0000000..4075c6e
@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<Target Name="Rust Build " BeforeTargets="Compile">
<Exec Command="echo 'Configuration: $(Configuration)'" />
<Exec
Condition="'$(Configuration)' == 'Release'"
Command="cargo build --release --frozen"
WorkingDirectory="."
/>
<Exec Condition="'$(Configuration)' != 'Release'" Command="cargo build" WorkingDirectory="." />
</Target>
<ItemGroup>
<Content
Condition="$([MSBuild]::IsOSPlatform('Windows'))"
Include="./target/$(Configuration.ToLower())/syntax_highlighter.dll"
>
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content
Condition="$([MSBuild]::IsOSPlatform('Linux'))"
Include="./target/$(Configuration.ToLower())/libsyntax_highlighter.so"
>
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content
Condition="$([MSBuild]::IsOSPlatform('OSX'))"
Include="./target/$(Configuration.ToLower())/libsyntax_highlighter.dylib"
>
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
GitBrowser.SyntaxHighlighter/Syntax.cs +17 -0
diff --git a/GitBrowser.SyntaxHighlighter/Syntax.cs b/GitBrowser.SyntaxHighlighter/Syntax.cs
new file mode 100644
index 0000000..5f2d1f4
@@ -0,0 +1,17 @@
using System.Runtime.InteropServices;
namespace GitBrowser.SyntaxHighlighter;
public static partial class Syntax
{
[LibraryImport("syntax_highlighter", EntryPoint = "can_highlight_file", StringMarshalling = StringMarshalling.Utf8)]
[return: MarshalAs(UnmanagedType.Bool)]
public static partial bool CanHighlightFile(string extension);
[LibraryImport(
"syntax_highlighter",
EntryPoint = "highlight_syntax_html",
StringMarshalling = StringMarshalling.Utf8
)]
public static partial string HighlightSyntaxHtml(string extension, string text);
}
GitBrowser.SyntaxHighlighter/packages.lock.json +15 -0
diff --git a/GitBrowser.SyntaxHighlighter/packages.lock.json b/GitBrowser.SyntaxHighlighter/packages.lock.json
new file mode 100644
index 0000000..e856592
@@ -0,0 +1,15 @@
{
"version": 2,
"dependencies": {
"net10.0": {
"CSharpier.MsBuild": {
"type": "Direct",
"requested": "[1.0.2, )",
"resolved": "1.0.2",
"contentHash": "Y98ba4yeefqrJY+MbCXhvb8x4/mCy8zHeuu/dYKwaM4p0Yu2LToI5ffTg/ErcpPuLnwJBNE229STdhf/dlikIw=="
}
},
"net10.0/linux-x64": {},
"net10.0/win-arm64": {}
}
}
\ No newline at end of file
GitBrowser.SyntaxHighlighter/src/lib.rs +41 -0
diff --git a/GitBrowser.SyntaxHighlighter/src/lib.rs b/GitBrowser.SyntaxHighlighter/src/lib.rs
new file mode 100644
index 0000000..19074f7
@@ -0,0 +1,41 @@
use std::ffi::{CStr, CString, c_char};
use syntect::{highlighting::ThemeSet, html::highlighted_html_for_string, parsing::SyntaxSet};
#[unsafe(no_mangle)]
pub extern "C" fn can_highlight_file(file_extension: *const c_char) -> bool {
let Ok(ext) = unsafe { CStr::from_ptr(file_extension) }.to_str() else {
return false;
};
let ss = SyntaxSet::load_defaults_newlines();
ss.find_syntax_by_extension(&ext[1..]).is_some()
}
#[unsafe(no_mangle)]
pub extern "C" fn highlight_syntax_html(
file_extension: *const c_char,
text: *const c_char,
) -> *const c_char {
let Ok(ext) = unsafe { CStr::from_ptr(file_extension) }.to_str() else {
return CString::default().into_raw();
};
let Ok(text) = unsafe { CStr::from_ptr(text) }.to_str() else {
return CString::default().into_raw();
};
let ss = SyntaxSet::load_defaults_newlines();
let Some(syntax) = ss.find_syntax_by_extension(&ext[1..]) else {
return CString::default().into_raw();
};
let ts = ThemeSet::load_defaults();
let github_theme = &ts.themes["base16-ocean.dark"];
let Ok(highlighted) = highlighted_html_for_string(text, &ss, syntax, github_theme) else {
return CString::default().into_raw();
};
CString::new(highlighted)
.unwrap_or_else(|_| CString::default())
.into_raw()
}
GitBrowser.slnx +1 -0
diff --git a/GitBrowser.slnx b/GitBrowser.slnx
index feb893f..c3c1c05 100644
@@ -1,3 +1,4 @@
<Solution>
<Project Path="GitBrowser.SyntaxHighlighter/GitBrowser.SyntaxHighlighter.csproj" />
<Project Path="GitBrowser/GitBrowser.csproj" />
</Solution>
GitBrowser/Components/Pages/Repo.razor +2 -9
diff --git a/GitBrowser/Components/Pages/Repo.razor b/GitBrowser/Components/Pages/Repo.razor
index 8549418..9c9bc05 100644
@@ -1,5 +1,6 @@
@page "/{RepoName}"
@page "/{RepoName}/tree/{Branch}/{*Path}"
@using GitBrowser.Components.Shared
@using LibGit2Sharp
<PageTitle>@RepoName</PageTitle>
@@ -104,15 +105,7 @@
@if (!string.IsNullOrEmpty(fileContent))
{
<div class="file-viewer @(isReadme ? "readme-viewer" : "")">
<div class="file-viewer-header">
<span class="icon">@(isReadme ? "📖" : "📄")</span>
<span class="file-name">@fileName</span>
</div>
<div class="file-content">
<pre>@fileContent</pre>
</div>
</div>
<FilePreview IsReadme="@isReadme" FileName="@fileName" FileContent="@fileContent" />
}
}
</div>
GitBrowser/Components/Pages/Repo.razor.css +0 -33
diff --git a/GitBrowser/Components/Pages/Repo.razor.css b/GitBrowser/Components/Pages/Repo.razor.css
index f30509d..2070cc0 100644
@@ -153,36 +153,3 @@ tbody td {
.current-file:hover {
background: var(--gh-color-attention-subtle) !important;
}
.file-viewer {
margin-top: 1.5rem;
border: 1px solid var(--gh-color-border-default);
border-radius: 6px;
overflow: hidden;
}
.file-viewer-header {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1rem;
background: var(--gh-color-canvas-subtle);
border-bottom: 1px solid var(--gh-color-border-default);
font-weight: 600;
color: var(--gh-color-fg-default);
}
.file-content {
overflow-x: auto;
}
.file-content pre {
margin: 0;
padding: 1rem;
font-family: "Consolas", "Monaco", "Courier New", monospace;
font-size: 0.875rem;
line-height: 1.5;
color: var(--gh-color-fg-default);
white-space: pre;
overflow-x: auto;
}
GitBrowser/Components/Shared/FilePreview.razor +39 -0
diff --git a/GitBrowser/Components/Shared/FilePreview.razor b/GitBrowser/Components/Shared/FilePreview.razor
new file mode 100644
index 0000000..53239c2
@@ -0,0 +1,39 @@
@using System.IO
@using GitBrowser.SyntaxHighlighter
@using System.Web
<div class="file-viewer @(IsReadme ? "readme-viewer" : "")">
<div class="file-viewer-header">
<span class="icon">@(IsReadme ? "📖" : "📄")</span>
<span class="file-name">@FileName</span>
</div>
<div class="file-content">
@((MarkupString)filePreview)
</div>
</div>
@code {
[Parameter]
public bool IsReadme { get; set; } = false;
[Parameter]
public required string FileName { get; set; }
[Parameter]
public required string FileContent { get; set; }
private string filePreview = "";
protected override void OnInitialized()
{
var extension = Path.GetExtension(FileName);
if (Syntax.CanHighlightFile(extension))
{
filePreview = Syntax.HighlightSyntaxHtml(extension, FileContent);
}
else
{
filePreview = $"<pre class=\"plain\">{HttpUtility.HtmlEncode(FileContent)}</pre>";
}
}
}
GitBrowser/Components/Shared/FilePreview.razor.css +32 -0
diff --git a/GitBrowser/Components/Shared/FilePreview.razor.css b/GitBrowser/Components/Shared/FilePreview.razor.css
new file mode 100644
index 0000000..ba52a4c
@@ -0,0 +1,32 @@
.file-viewer {
margin-top: 1.5rem;
border: 1px solid var(--gh-color-border-default);
border-radius: 6px;
overflow: hidden;
}
.file-viewer-header {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1rem;
background: var(--gh-color-canvas-subtle);
border-bottom: 1px solid var(--gh-color-border-default);
font-weight: 600;
color: var(--gh-color-fg-default);
}
.file-content {
overflow-x: auto;
}
.file-content pre {
margin: 0;
padding: 1rem;
font-family: "Consolas", "Monaco", "Courier New", monospace;
font-size: 0.875rem;
line-height: 1.5;
color: var(--gh-color-fg-default);
white-space: pre;
overflow-x: auto;
}
GitBrowser/GitBrowser.csproj +3 -0
diff --git a/GitBrowser/GitBrowser.csproj b/GitBrowser/GitBrowser.csproj
index 935efcf..a3506b8 100644
@@ -11,4 +11,7 @@
<PackageReference Include="OpenTelemetry.Instrumentation.Http" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GitBrowser.SyntaxHighlighter\GitBrowser.SyntaxHighlighter.csproj" />
</ItemGroup>
</Project>
GitBrowser/packages.lock.json +3 -0
diff --git a/GitBrowser/packages.lock.json b/GitBrowser/packages.lock.json
index 347e01d..f0fab1f 100644
@@ -93,6 +93,9 @@
"dependencies": {
"OpenTelemetry.Api": "1.13.1"
}
},
"gitbrowser.syntaxhighlighter": {
"type": "Project"
}
},
"net10.0/linux-x64": {
GitBrowser/wwwroot/app.css +1 -1
diff --git a/GitBrowser/wwwroot/app.css b/GitBrowser/wwwroot/app.css
index 57fbd96..8a9e3e1 100644
@@ -101,11 +101,11 @@ code {
pre {
padding: 16px;
margin: 0;
overflow: auto;
font-size: 85%;
line-height: 1.45;
background-color: var(--gh-color-canvas-default);
border-radius: 6px;
}
pre code {