diff --git a/.gitignore b/.gitignore index ba221af..505a3b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +# Python-generated files __pycache__/ *.py[oc] build/ @@ -5,5 +6,5 @@ dist/ wheels/ *.egg-info +# Virtual environments .venv -public/fonts diff --git a/app.py b/app.py index dcddfbd..2ceaef1 100644 --- a/app.py +++ b/app.py @@ -5,17 +5,21 @@ import os from dataclasses import dataclass from pathlib import Path -from flask import Flask, abort, jsonify, render_template, request, send_from_directory +from flask import Flask, jsonify, render_template, send_from_directory, abort, request +from pygments import highlight +from pygments.formatters import HtmlFormatter +from pygments.lexers import TextLexer, get_lexer_for_filename +from pygments.util import ClassNotFound app = Flask(__name__, static_folder=None) -git_repo_path = str(Path.home() / "dev") -gist_path = str(Path.home() / "gists") -export_marker = "readme.md" +GIT_REPO_PATH = str(Path.home() / "dev") +GIST_PATH = str(Path.home() / "gists") +EXPORT_MARKER = "readme.md" if getpass.getuser() == "apache": - git_repo_path = "/srv/git" - gist_path = "/srv/gists" - export_marker = "git-daemon-export-ok" + GIT_REPO_PATH = "/srv/git" + GIST_PATH = "/srv/gists" + EXPORT_MARKER = "git-daemon-export-ok" @dataclass @@ -29,11 +33,11 @@ class Repository: def get_repositories(): repositories = [] - if not os.path.exists(git_repo_path): + if not os.path.exists(GIT_REPO_PATH): return repositories - for item in os.listdir(git_repo_path): - repo_path = os.path.join(git_repo_path, item) + for item in os.listdir(GIT_REPO_PATH): + repo_path = os.path.join(GIT_REPO_PATH, item) if not os.path.isdir(repo_path): continue @@ -44,7 +48,7 @@ def get_repositories(): ): continue - exported = os.path.exists(os.path.join(repo_path, export_marker)) + exported = os.path.exists(os.path.join(repo_path, EXPORT_MARKER)) description = "No description available" description_file = os.path.join(repo_path, "description") @@ -78,24 +82,6 @@ def get_repositories(): return repositories -@app.route("/gist/") -def serve_gist(filename): - base = Path(gist_path).resolve() - target = (base / filename).resolve() - if base not in target.parents and base != target: - abort(404) - if not target.exists() or not target.is_file(): - abort(404) - - try: - content = target.read_text(encoding="utf-8") - except UnicodeDecodeError: - return "Binary file cannot be displayed", 400 - - ext = target.suffix.lstrip(".") or "plaintext" - return render_template("gist.html", filename=filename, code=content, lang=ext) - - @app.route("/") def index(): repositories = [repo for repo in get_repositories() if repo.exported] @@ -112,6 +98,43 @@ def serve_scripts(filename): return send_from_directory("scripts", filename) +@app.route("/gist/") +def serve_gist(filename): + base = Path(GIST_PATH).resolve() + target = (base / filename).resolve() + if base not in target.parents and base != target: + abort(404) + + if not target.exists() or not target.is_file(): + abort(404) + + try: + content = target.read_text(encoding="utf-8") + except UnicodeDecodeError: + return "Binary file cannot be displayed", 400 + except OSError: + abort(404) + + try: + lexer = get_lexer_for_filename(target.name) + except ClassNotFound: + lexer = TextLexer() + + formatter = HtmlFormatter( + style="default", + cssclass="highlight", + linenos=False, + noclasses=True, + cssstyles="padding: 20px; font-size: 18px; background-color: #f8f8f8;", + ) + highlighted = highlight(content, lexer, formatter) + highlighted = highlighted.replace( + '', '' + ) + + return render_template("gist.html", filename=filename, highlighted_code=highlighted) + + @app.route("/") def serve_gist_root(filename): head = filename.split("/", 1)[0] @@ -143,3 +166,4 @@ def get_repo(repo_id): if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=True) + diff --git a/file b/file new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml index 1fc734e..c31394d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,4 +7,5 @@ requires-python = ">=3.11" dependencies = [ "flask>=3.1.1", "gunicorn>=23.0.0", + "pygments>=2.19.1", ] diff --git a/readme.md b/readme.md index 8ee9215..190159e 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,3 @@ # git server ui the ui for my git server. - -## Deprecation Notice - -Go to barrettruth.com/git instead. diff --git a/scripts/index.js b/scripts/index.js index ce15189..bcba2d6 100644 --- a/scripts/index.js +++ b/scripts/index.js @@ -61,7 +61,7 @@ function renderRepoDescription(repoLink) { const cloneUrl = document.createElement("div"); cloneUrl.style.marginTop = "15px"; - cloneUrl.innerHTML = `git clone git@git.barrettruth.com:${repoName}.git`; + cloneUrl.innerHTML = `git clone https://git.barrettruth.com/git/${repoName}.git`; post.appendChild(cloneUrl); diff --git a/styles/common.css b/styles/common.css index 02a8d3d..865d47e 100644 --- a/styles/common.css +++ b/styles/common.css @@ -1,33 +1,4 @@ -@font-face { - font-family: "Apercu Mono Pro"; - src: url("/public/fonts/apercu-mono/ApercuMonoProRegular.ttf") - format("truetype"); - font-weight: 400; - font-style: normal; -} - -@font-face { - font-family: "Apercu Mono Pro"; - src: url("/public/fonts/apercu-mono/ApercuMonoProMedium.ttf") - format("truetype"); - font-weight: 500; - font-style: normal; -} - -@font-face { - font-family: "Apercu Mono Pro"; - src: url("/public/fonts/apercu-mono/ApercuMonoProLight.ttf") - format("truetype"); - font-weight: 300; - font-style: normal; -} - -@font-face { - font-family: "Apercu Mono Pro"; - src: url("/public/fonts/apercu-mono/ApercuMonoProBold.ttf") format("truetype"); - font-weight: 700; - font-style: normal; -} +/* Fonts will be loaded from system */ html, body { @@ -79,10 +50,11 @@ li { font-family: "Courier New", monospace; } +/* Code styling */ :not(pre) > code { font-family: "Courier New", Courier, monospace; margin: 0 5px; - font-size: 0.8em; + font-size: 0.8em; /* Match the font size in barrettruth.com */ white-space: nowrap; border: 1px solid #e1e1e1; background-color: #f4f4f4; @@ -92,44 +64,3 @@ li { max-width: 100%; user-select: all; } - -pre code { - font-family: "Apercu Mono Pro", "Courier New", monospace; - font-size: 0.95rem; - line-height: 1.5; - background: transparent; - white-space: pre; - overflow-x: auto; -} - -.hljs-ln-numbers { - text-align: right; - color: #999; - border-right: 1em solid #ffffff; - user-select: none; -} - -main h2 { - margin-bottom: 0; -} - -.gist-outer { - display: flex; - justify-content: center; -} - -.gist-box { - display: inline-block; - min-width: 60vw; - max-width: 80vw; - width: max-content; - border: 1px solid #000; - border-radius: 0; - background: #fff; - box-sizing: border-box; -} - -.hljs-ln-numbers { - text-align: right; - color: #999; -} diff --git a/styles/index.css b/styles/index.css index 0743fd3..c9445c8 100644 --- a/styles/index.css +++ b/styles/index.css @@ -109,4 +109,3 @@ a { margin-top: 10px; font-size: 0.7em; } - diff --git a/templates/gist.html b/templates/gist.html index 4c730e0..06b02e6 100644 --- a/templates/gist.html +++ b/templates/gist.html @@ -7,55 +7,17 @@ - - - {{ filename }} - -
-

- {{ filename }} -

- -
-
-
{{ code | e }}
-
+
+

{{ filename }}

+
+ {{ highlighted_code|safe }}
- - - - - - - diff --git a/uv.lock b/uv.lock index 2ed07f6..6195cbc 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.11" [[package]] @@ -56,12 +56,14 @@ source = { virtual = "." } dependencies = [ { name = "flask" }, { name = "gunicorn" }, + { name = "pygments" }, ] [package.metadata] requires-dist = [ { name = "flask", specifier = ">=3.1.1" }, { name = "gunicorn", specifier = ">=23.0.0" }, + { name = "pygments", specifier = ">=2.19.1" }, ] [[package]] @@ -154,6 +156,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" }, +] + [[package]] name = "werkzeug" version = "3.1.3"