| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | AUTHORS | 12 | ||||
| -rw-r--r-- | Makefile | 6 | ||||
| -rw-r--r-- | README | 99 | ||||
| -rw-r--r-- | README.md | 95 | ||||
| -rw-r--r-- | cgit.c | 12 | ||||
| -rw-r--r-- | cgit.css | 412 | ||||
| -rw-r--r-- | cgit.h | 2 | ||||
| -rw-r--r-- | cgit.svg | 5 | ||||
| -rw-r--r-- | cgitrc.5.txt | 19 | ||||
| -rw-r--r-- | cmd.c | 4 | ||||
| -rw-r--r-- | favicon.svg | 5 | ||||
| -rw-r--r-- | filter.c | 4 | ||||
| -rwxr-xr-x | filters/html-converters/md2html | 84 | ||||
| -rwxr-xr-x | filters/syntax-highlighting.py | 45 | ||||
| -rwxr-xr-x | filters/syntax-highlighting.sh | 14 | ||||
| -rw-r--r-- | shared.c | 12 | ||||
| -rw-r--r-- | solarized.css | 140 | ||||
| -rwxr-xr-x | tests/t0106-diff.sh | 82 | ||||
| -rw-r--r-- | tests/t0114-broken-repo.sh | 71 | ||||
| -rw-r--r-- | ui-ci.c | 2 | ||||
| -rw-r--r-- | ui-diff.c | 86 | ||||
| -rw-r--r-- | ui-shared.c | 87 |
23 files changed, 1000 insertions, 299 deletions
@@ -10,3 +10,4 @@ cgitrc.5.pdf cgitrc.5.xml *.o *.d +__pycache__/ @@ -1,13 +1,5 @@ Maintainer: - Jason A. Donenfeld <Jason@zx2c4.com> + Saya Andy <saya.andy@posteo.com> Contributors: - Jason A. Donenfeld <Jason@zx2c4.com> - Lukas Fleischer <cgit@cryptocrack.de> - Johan Herland <johan@herland.net> - Lars Hjemli <hjemli@gmail.com> - Ferry Huberts <ferry.huberts@pelagic.nl> - John Keeping <john@keeping.me.uk> - -Previous Maintainer: - Lars Hjemli <hjemli@gmail.com> + Saya Andy <saya.andy@posteo.com> @@ -87,9 +87,12 @@ install: all $(INSTALL) -m 0755 cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) $(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_DATA_PATH) $(INSTALL) -m 0644 cgit.css $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css + $(INSTALL) -m 0644 solarized.css $(DESTDIR)$(CGIT_DATA_PATH)/solarized.css $(INSTALL) -m 0644 cgit.js $(DESTDIR)$(CGIT_DATA_PATH)/cgit.js $(INSTALL) -m 0644 cgit.png $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png + $(INSTALL) -m 0644 cgit.svg $(DESTDIR)$(CGIT_DATA_PATH)/cgit.svg $(INSTALL) -m 0644 favicon.ico $(DESTDIR)$(CGIT_DATA_PATH)/favicon.ico + $(INSTALL) -m 0644 favicon.svg $(DESTDIR)$(CGIT_DATA_PATH)/favicon.svg $(INSTALL) -m 0644 robots.txt $(DESTDIR)$(CGIT_DATA_PATH)/robots.txt $(INSTALL) -m 0755 -d $(DESTDIR)$(filterdir) $(COPYTREE) filters/* $(DESTDIR)$(filterdir) @@ -116,9 +119,12 @@ endef uninstall: rm -f $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css + rm -f $(DESTDIR)$(CGIT_DATA_PATH)/solarized.css rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.js rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png + rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.svg rm -f $(DESTDIR)$(CGIT_DATA_PATH)/favicon.ico + rm -f $(DESTDIR)$(CGIT_DATA_PATH)/favicon.svg rm -f $(DESTDIR)$(CGIT_DATA_PATH)/robots.txt $(foreach file,$(patsubst filters/%,%,$(shell find filters/ ! -type d)), \ $(call rm_f,$(DESTDIR)$(filterdir)/$(file))) @@ -1,99 +0,0 @@ -cgit - CGI for Git -================== - -This is an attempt to create a fast web interface for the Git SCM, using a -built-in cache to decrease server I/O pressure. - -Installation ------------- - -Building cgit involves building a proper version of Git. How to do this -depends on how you obtained the cgit sources: - -a) If you're working in a cloned cgit repository, you first need to -initialize and update the Git submodule: - - $ git submodule init # register the Git submodule in .git/config - $ $EDITOR .git/config # if you want to specify a different url for git - $ git submodule update # clone/fetch and checkout correct git version - -b) If you're building from a cgit tarball, you can download a proper git -version like this: - - $ make get-git - -When either a) or b) has been performed, you can build and install cgit like -this: - - $ make - $ sudo make install - -This will install `cgit.cgi` and `cgit.css` into `/var/www/htdocs/cgit`. You -can configure this location (and a few other things) by providing a `cgit.conf` -file (see the Makefile for details). - -If you'd like to compile without Lua support, you may use: - - $ make NO_LUA=1 - -And if you'd like to specify a Lua implementation, you may use: - - $ make LUA_PKGCONFIG=lua5.1 - -If this is not specified, the Lua implementation will be auto-detected, -preferring LuaJIT if many are present. Acceptable values are generally "lua", -"luajit", "lua5.1", and "lua5.2". - - -Dependencies ------------- - -* libzip -* libcrypto (OpenSSL) -* libssl (OpenSSL) -* optional: luajit or lua, most reliably used when pkg-config is available - -Apache configuration --------------------- - -A new `Directory` section must probably be added for cgit, possibly something -like this: - - <Directory "/var/www/htdocs/cgit/"> - AllowOverride None - Options +ExecCGI - Order allow,deny - Allow from all - </Directory> - - -Runtime configuration ---------------------- - -The file `/etc/cgitrc` is read by cgit before handling a request. In addition -to runtime parameters, this file may also contain a list of repositories -displayed by cgit (see `cgitrc.5.txt` for further details). - -The cache ---------- - -When cgit is invoked it looks for a cache file matching the request and -returns it to the client. If no such cache file exists (or if it has expired), -the content for the request is written into the proper cache file before the -file is returned. - -If the cache file has expired but cgit is unable to obtain a lock for it, the -stale cache file is returned to the client. This is done to favour page -throughput over page freshness. - -The generated content contains the complete response to the client, including -the HTTP headers `Modified` and `Expires`. - -Online presence ---------------- - -* The cgit homepage is hosted by cgit at <https://git.zx2c4.com/cgit/about/> - -* Patches, bug reports, discussions and support should go to the cgit - mailing list: <cgit@lists.zx2c4.com>. To sign up, visit - <https://lists.zx2c4.com/mailman/listinfo/cgit> diff --git a/README.md b/README.md new file mode 100644 index 0000000..1e13a37 --- /dev/null +++ b/README.md @@ -0,0 +1,95 @@ +## cgitext – CGI for Git extension by Saya Andy + +This is a custom expansion to cgit. Its most prominent changes by now are: + +* CI/CD tab, linking to an external service on ref pages by using template. The Jenkins plugin is added to support a scenario when a ref lacks a pipeline. + +* Theming now relies on CSS variables set in cgit.css, to allow easier custom additions and implicit filter theming. + +* Allow to use multiple favicons and of MIME type different from x-icon (f.ex. svg/png/webp). + +* Use vector-based format for favicon and cgit logo and make it default. + +* Include optional solarized.css addon theme. + +* Allow user to pick variable refs for diff in web ui. + +The original cgit project is hosted at [git.zx2c4.com](https://git.zx2c4.com/cgit/about/) + +## cgit – CGI for Git + +This is an attempt to create a fast web interface for the Git SCM, using a built-in cache to decrease server I/O pressure. + +### Installation + +Building cgit involves building a proper version of Git. How to do this depends on how you obtained the cgit sources: + +1. If you're working in a cloned cgit repository, you first need to initialize and update the Git submodule: + + $ git submodule init # register the Git submodule in .git/config + $ $EDITOR .git/config # if you want to specify a different url for git + $ git submodule update # clone/fetch and checkout correct git version + +2. If you're building from a cgit tarball, you can download a proper git version like this: + + $ make get-git + +When either `1.` or `2.` has been performed, you can build and install cgit like +this: +``` +make +sudo make install +``` + +This will install `cgit.cgi` and `cgit.css` into `/var/www/htdocs/cgit`. You can configure this location (and a few other things) by providing a `cgit.conf` file (see the Makefile for details). + +If you'd like to compile without Lua support, you may use: +``` +make NO_LUA=1 +``` + +And if you'd like to specify a Lua implementation, you may use: +``` +make LUA_PKGCONFIG=lua5.1 +``` + +If this is not specified, the Lua implementation will be auto-detected, preferring LuaJIT if many are present. Acceptable values are generally "lua", "luajit", "lua5.1", and "lua5.2". + + +### Dependencies + +* libzip +* libcrypto (OpenSSL) +* libssl (OpenSSL) +* optional: luajit or lua, most reliably used when pkg-config is available + +### Apache configuration + +A new `Directory` section must probably be added for cgit, possibly something like this: + +``` +<Directory "/var/www/htdocs/cgit/"> + AllowOverride None + Options +ExecCGI + Order allow,deny + Allow from all +</Directory> +``` + +### Runtime configuration + +The file `/etc/cgitrc` is read by cgit before handling a request. In addition to runtime parameters, this file may also contain a list of repositories displayed by cgit (see `cgitrc.5.txt` for further details). + +### The cache + +When cgit is invoked it looks for a cache file matching the request and returns it to the client. If no such cache file exists (or if it has expired), the content for the request is written into the proper cache file before the file is returned. + +If the cache file has expired but cgit is unable to obtain a lock for it, the stale cache file is returned to the client. This is done to favour page throughput over page freshness. + +The generated content contains the complete response to the client, including the HTTP headers `Modified` and `Expires`. + +### Online presence + +* The cgit homepage is hosted by cgit at [git.zx2c4.com](https://git.zx2c4.com/cgit/about/) + +* Patches, bug reports, discussions and support should go to the cgit mailing list: [cgit@lists.zx2c4.com](mailto:cgit@lists.zx2c4.com). To sign up, visit [lists.zx2c4.com](https://lists.zx2c4.com/mailman/listinfo/cgit) @@ -1,6 +1,11 @@ /* cgit.c: cgi for the git scm * * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * Copyright (C) 2026 Saya Andy <saya.andy@posteo.com> + * + * Modified 2026 by Saya Andy: + * Always check there is git repo on repo.path + * Allow to include multiple favicons * * Licensed under GNU General Public License v2 * (see COPYING for full license text) @@ -166,7 +171,7 @@ static void config_cb(const char *name, const char *value) else if (!strcmp(name, "js")) string_list_append(&ctx.cfg.js, strdup_first_line(value)); else if (!strcmp(name, "favicon")) - ctx.cfg.favicon = strdup_first_line(value); + string_list_append(&ctx.cfg.favicon, strdup_first_line(value)); else if (!strcmp(name, "footer")) ctx.cfg.footer = strdup_first_line(value); else if (!strcmp(name, "head-include")) @@ -408,8 +413,7 @@ static void prepare_context(void) ctx.cfg.case_sensitive_sort = 1; ctx.cfg.branch_sort = 0; ctx.cfg.commit_sort = 0; - ctx.cfg.logo = "/cgit.png"; - ctx.cfg.favicon = "/favicon.ico"; + ctx.cfg.logo = "/cgit.svg"; ctx.cfg.local_time = 0; ctx.cfg.enable_http_clone = 1; ctx.cfg.enable_index_owner = 1; @@ -997,7 +1001,7 @@ static void cgit_parse_args(int argc, const char **argv) for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "--version")) { - printf("CGit %s | https://git.zx2c4.com/cgit/\n\nCompiled in features:\n", CGIT_VERSION); + printf("CGit %s | https://sayag.it/cgitext.git | https://git.zx2c4.com/cgit/\n\nCompiled in features:\n", CGIT_VERSION); #ifdef NO_LUA printf("[-] "); #else @@ -1,15 +1,88 @@ div#cgit { + --bg: white; + --bg-zebra: #f7f7f7; + --bg-hl: #eee; + --bg-hl2: #ccc; + --fg: #333; + --fg-strong: #000; + --fg-mid: #444; + --fg-mute: #777; + --fg-dim: #888; + --fg-faint: #bbb; + --fg-faintest: #ccc; + --fg-on-tint: black; + --fg-linenum: gray; + --border: #ccc; + --border-mid: #aaa; + --border-dark: #777; + --border-strong: black; + + --red: red; + --red-mid: #c00; + --red-dark: #800; + --green: green; + --green-mid: #080; + --green-dark: #040; + --blue: blue; + --blue-dark: #009; + + --graph-col1: #a00; + --graph-col2: #0a0; + --graph-col3: #aa0; + --graph-col4: #00a; + --graph-col5: #a0a; + --graph-col6: #0aa; + + --graph-add-bg: #5c5; + --graph-rem-bg: #c55; + + --notes-bg: #ffd; + --notes-border: #ee9; + --add-bg: #cfc; + --add-bg-dark: #aca; + --del-bg: #fcc; + --del-bg-dark: #caa; + --changed-bg: #ffc; + --changed-bg-dark: #cca; + --hunk-bg: #ccf; + + --deco-branch-bg: #8f8; + --deco-branch-border: #070; + --deco-tag-bg: #ff8; + --deco-tag-border: #770; + --deco-tag-annotated-bg: #fc8; + --deco-tag-annotated-border: #770; + --deco-remote-bg: #ccf; + --deco-remote-border: #007; + --deco-other-bg: #f88; + --deco-other-border: #700; + + --syn-keyword: #000; + --syn-keyword-alt: #0057ae; + --syn-type: #010181; + --syn-string: #bf0303; + --syn-string-alt: #818100; + --syn-escape: #f0f; + --syn-comment: #838183; + --syn-number: #b07e00; + --syn-preproc: #008200; + --syn-operator: #000; + --syn-interpolation: #0057ae; + --syn-linenum: #555; + --syn-error: #bf0303; + --syn-mark-bg: #ffb; + padding: 0em; margin: 0em; font-family: sans-serif; font-size: 10pt; - color: #333; - background: white; + color: var(--fg); + background: var(--bg); padding: 4px; } div#cgit a { - color: blue; + color: var(--blue); text-decoration: none; } @@ -38,7 +111,7 @@ div#cgit table#header td.main { } div#cgit table#header td.main a { - color: #000; + color: var(--fg-strong); } div#cgit table#header td.form { @@ -56,13 +129,13 @@ div#cgit table#header td.form select { } div#cgit table#header td.sub { - color: #777; - border-top: solid 1px #ccc; + color: var(--fg-mute); + border-top: solid 1px var(--border); padding-left: 10px; } div#cgit table.tabs { - border-bottom: solid 3px #ccc; + border-bottom: solid 3px var(--border); border-collapse: collapse; margin-top: 2em; margin-bottom: 0px; @@ -76,13 +149,13 @@ div#cgit table.tabs td { div#cgit table.tabs td a { padding: 2px 0.75em; - color: #777; + color: var(--fg-mute); font-size: 110%; } div#cgit table.tabs td a.active { - color: #000; - background-color: #ccc; + color: var(--fg-strong); + background-color: var(--bg-hl2); } div#cgit table.tabs a[href^="http://"]:after, div#cgit table.tabs a[href^="https://"]:after { @@ -109,14 +182,14 @@ div#cgit table.tabs td.form select { div#cgit div.path { margin: 0px; padding: 5px 2em 2px 2em; - color: #000; - background-color: #eee; + color: var(--fg-strong); + background-color: var(--bg-hl); } div#cgit div.content { margin: 0px; padding: 2em; - border-bottom: solid 3px #ccc; + border-bottom: solid 3px var(--border); } @@ -127,46 +200,46 @@ div#cgit table.list { } div#cgit table.list tr { - background: white; + background: var(--bg); } div#cgit table.list tr.logheader { - background: #eee; + background: var(--bg-hl); } div#cgit table.list tr:nth-child(even) { - background: #f7f7f7; + background: var(--bg-zebra); } div#cgit table.list tr:nth-child(odd) { - background: white; + background: var(--bg); } div#cgit table.list tr:hover { - background: #eee; + background: var(--bg-hl); } div#cgit table.list tr.nohover { - background: white; + background: var(--bg); } div#cgit table.list tr.nohover:hover { - background: white; + background: var(--bg); } div#cgit table.list tr.nohover-highlight:hover:nth-child(even) { - background: #f7f7f7; + background: var(--bg-zebra); } div#cgit table.list tr.nohover-highlight:hover:nth-child(odd) { - background: white; + background: var(--bg); } div#cgit table.list th { font-weight: bold; - /* color: #888; - border-top: dashed 1px #888; - border-bottom: dashed 1px #888; + /* color: var(--fg-dim); + border-top: dashed 1px var(--fg-dim); + border-bottom: dashed 1px var(--fg-dim); */ padding: 0.1em 0.5em 0.05em 0.5em; vertical-align: baseline; @@ -183,27 +256,27 @@ div#cgit table.list td.commitgraph { } div#cgit table.list td.commitgraph .column1 { - color: #a00; + color: var(--graph-col1); } div#cgit table.list td.commitgraph .column2 { - color: #0a0; + color: var(--graph-col2); } div#cgit table.list td.commitgraph .column3 { - color: #aa0; + color: var(--graph-col3); } div#cgit table.list td.commitgraph .column4 { - color: #00a; + color: var(--graph-col4); } div#cgit table.list td.commitgraph .column5 { - color: #a0a; + color: var(--graph-col5); } div#cgit table.list td.commitgraph .column6 { - color: #0aa; + color: var(--graph-col6); } div#cgit table.list td.logsubject { @@ -218,16 +291,16 @@ div#cgit table.list td.logmsg { } div#cgit table.list td a { - color: black; + color: var(--fg-strong); } div#cgit table.list td a.ls-dir { font-weight: bold; - color: #00f; + color: var(--blue); } div#cgit table.list td a:hover { - color: #00f; + color: var(--blue); } div#cgit img { @@ -261,21 +334,21 @@ div#cgit div#summary { div#cgit table#downloads { float: right; border-collapse: collapse; - border: solid 1px #777; + border: solid 1px var(--border-dark); margin-left: 0.5em; margin-bottom: 0.5em; } div#cgit table#downloads th { - background-color: #ccc; + background-color: var(--bg-hl2); } div#cgit div#blob { - border: solid 1px black; + border: solid 1px var(--border-strong); } div#cgit div.error { - color: red; + color: var(--red); font-weight: bold; margin: 1em 2em; } @@ -297,21 +370,21 @@ div#cgit td.ls-mode { div#cgit table.blob { margin-top: 0.5em; - border-top: solid 1px black; + border-top: solid 1px var(--border-strong); } div#cgit table.blob td.hashes, div#cgit table.blob td.lines { margin: 0; padding: 0 0 0 0.5em; vertical-align: top; - color: black; + color: var(--fg-strong); } div#cgit table.blob td.linenumbers { margin: 0; padding: 0 0.5em 0 0.5em; vertical-align: top; text-align: right; - border-right: 1px solid gray; + border-right: 1px solid var(--fg-linenum); } div#cgit table.blob pre { @@ -320,18 +393,18 @@ div#cgit table.blob pre { div#cgit table.blob td.linenumbers a, div#cgit table.ssdiff td.lineno a { - color: gray; + color: var(--fg-linenum); text-align: right; text-decoration: none; } div#cgit table.blob td.linenumbers a:hover, div#cgit table.ssdiff td.lineno a:hover { - color: black; + color: var(--fg-strong); } div#cgit table.blob td.linenumbers a:target:before { - color: red; + color: var(--red); content: "\2BA9"; } @@ -351,11 +424,11 @@ div#cgit table.blame td.linenumbers div.alt { } div#cgit table.blame div.alt:nth-child(even) { - background: #eee; + background: var(--bg-hl); } div#cgit table.blame div.alt:nth-child(odd) { - background: white; + background: var(--bg); } div#cgit table.blame td.lines > div { @@ -374,20 +447,20 @@ div#cgit table.blame .oid { div#cgit table.bin-blob { margin-top: 0.5em; - border: solid 1px black; + border: solid 1px var(--border-strong); } div#cgit table.bin-blob th { font-family: monospace; white-space: pre; - border: solid 1px #777; + border: solid 1px var(--border-dark); padding: 0.5em 1em; } div#cgit table.bin-blob td { font-family: monospace; white-space: pre; - border-left: solid 1px #777; + border-left: solid 1px var(--border-dark); padding: 0em 1em; } @@ -405,10 +478,18 @@ div#cgit div.cgit-panel { margin-top: 1.5em; } +div#cgit form.diff-ctrls div.cgit-panel { + float: none; +} + +div#cgit form.diff-ctrls { + float: right; +} + div#cgit div.cgit-panel table { border-collapse: collapse; - border: solid 1px #aaa; - background-color: #eee; + border: solid 1px var(--border-mid); + background-color: var(--bg-hl); } div#cgit div.cgit-panel th { @@ -459,8 +540,8 @@ div#cgit div.notes-header { div#cgit div.notes { white-space: pre; font-family: monospace; - border: solid 1px #ee9; - background-color: #ffd; + border: solid 1px var(--notes-border); + background-color: var(--notes-bg); padding: 0.3em 2em 0.3em 1em; float: left; } @@ -476,8 +557,8 @@ div#cgit div.diffstat-header { div#cgit table.diffstat { border-collapse: collapse; - border: solid 1px #aaa; - background-color: #eee; + border: solid 1px var(--border-mid); + background-color: var(--bg-hl); } div#cgit table.diffstat th { @@ -500,19 +581,19 @@ div#cgit table.diffstat td.mode { div#cgit table.diffstat td span.modechange { padding-left: 1em; - color: red; + color: var(--red); } div#cgit table.diffstat td.add a { - color: green; + color: var(--green); } div#cgit table.diffstat td.del a { - color: red; + color: var(--red); } div#cgit table.diffstat td.upd a { - color: blue; + color: var(--blue); } div#cgit table.diffstat td.graph { @@ -531,15 +612,15 @@ div#cgit table.diffstat td.graph td { } div#cgit table.diffstat td.graph td.add { - background-color: #5c5; + background-color: var(--graph-add-bg); } div#cgit table.diffstat td.graph td.rem { - background-color: #c55; + background-color: var(--graph-rem-bg); } div#cgit div.diffstat-summary { - color: #888; + color: var(--fg-dim); padding-top: 0.5em; } @@ -555,19 +636,19 @@ div#cgit table.diff td { div#cgit table.diff td div.head { font-weight: bold; margin-top: 1em; - color: black; + color: var(--fg-strong); } div#cgit table.diff td div.hunk { - color: #009; + color: var(--blue-dark); } div#cgit table.diff td div.add { - color: green; + color: var(--green); } div#cgit table.diff td div.del { - color: red; + color: var(--red); } div#cgit .oid { @@ -585,7 +666,7 @@ div#cgit .right { div#cgit table.list td.reposection { font-style: italic; - color: #888; + color: var(--fg-dim); } div#cgit a.button { @@ -593,6 +674,11 @@ div#cgit a.button { padding: 0em 0.5em; } +div#cgit form.diff-ctrls div.cgit-panel a.button { + border: solid 1px var(--border-mid); + background-color: var(--bg-hl); +} + div#cgit a.primary { font-size: 100%; } @@ -622,7 +708,7 @@ div#cgit ul.pager li { } div#cgit ul.pager a { - color: #777; + color: var(--fg-mute); } div#cgit ul.pager .current { @@ -631,46 +717,46 @@ div#cgit ul.pager .current { div#cgit span.age-mins { font-weight: bold; - color: #080; + color: var(--green-mid); } div#cgit span.age-hours { - color: #080; + color: var(--green-mid); } div#cgit span.age-days { - color: #040; + color: var(--green-dark); } div#cgit span.age-weeks { - color: #444; + color: var(--fg-mid); } div#cgit span.age-months { - color: #888; + color: var(--fg-dim); } div#cgit span.age-years { - color: #bbb; + color: var(--fg-faint); } div#cgit span.insertions { - color: #080; + color: var(--green-mid); } div#cgit span.deletions { - color: #800; + color: var(--red-dark); } div#cgit div.footer { margin-top: 0.5em; text-align: center; font-size: 80%; - color: #ccc; + color: var(--fg-faintest); } div#cgit div.footer a { - color: #ccc; + color: var(--fg-faintest); text-decoration: none; } @@ -679,47 +765,47 @@ div#cgit div.footer a:hover { } div#cgit a.branch-deco { - color: #000; + color: var(--fg-strong); margin: 0px 0.5em; padding: 0px 0.25em; - background-color: #88ff88; - border: solid 1px #007700; + background-color: var(--deco-branch-bg); + border: solid 1px var(--deco-branch-border); border-radius: 2px; } div#cgit a.tag-deco { - color: #000; + color: var(--fg-strong); margin: 0px 0.5em; padding: 0px 0.25em; - background-color: #ffff88; - border: solid 1px #777700; + background-color: var(--deco-tag-bg); + border: solid 1px var(--deco-tag-border); border-radius: 2px; } div#cgit a.tag-annotated-deco { - color: #000; + color: var(--fg-strong); margin: 0px 0.5em; padding: 0px 0.25em; - background-color: #ffcc88; - border: solid 1px #777700; + background-color: var(--deco-tag-annotated-bg); + border: solid 1px var(--deco-tag-annotated-border); border-radius: 2px; } div#cgit a.remote-deco { - color: #000; + color: var(--fg-strong); margin: 0px 0.5em; padding: 0px 0.25em; - background-color: #ccccff; - border: solid 1px #000077; + background-color: var(--deco-remote-bg); + border: solid 1px var(--deco-remote-border); border-radius: 2px; } div#cgit a.deco { - color: #000; + color: var(--fg-strong); margin: 0px 0.5em; padding: 0px 0.25em; - background-color: #ff8888; - border: solid 1px #770000; + background-color: var(--deco-other-bg); + border: solid 1px var(--deco-other-border); border-radius: 2px; } @@ -733,21 +819,21 @@ div#cgit div.commit-subject a.deco { } div#cgit table.stats { - border: solid 1px black; + border: solid 1px var(--border-strong); border-collapse: collapse; } div#cgit table.stats th { text-align: left; padding: 1px 0.5em; - background-color: #eee; - border: solid 1px black; + background-color: var(--bg-hl); + border: solid 1px var(--border-strong); } div#cgit table.stats td { text-align: right; padding: 1px 0.5em; - border: solid 1px black; + border: solid 1px var(--border-strong); } div#cgit table.stats td.total { @@ -756,9 +842,9 @@ div#cgit table.stats td.total { } div#cgit table.stats td.sum { - color: #c00; + color: var(--red-mid); font-weight: bold; -/* background-color: #eee; */ +/* background-color: var(--bg-hl); */ } div#cgit table.stats td.left { @@ -767,14 +853,14 @@ div#cgit table.stats td.left { div#cgit table.vgraph { border-collapse: separate; - border: solid 1px black; + border: solid 1px var(--border-strong); height: 200px; } div#cgit table.vgraph th { - background-color: #eee; + background-color: var(--bg-hl); font-weight: bold; - border: solid 1px white; + border: solid 1px var(--bg); padding: 1px 0.5em; } @@ -784,18 +870,18 @@ div#cgit table.vgraph td { } div#cgit table.vgraph div.bar { - background-color: #eee; + background-color: var(--bg-hl); } div#cgit table.hgraph { - border: solid 1px black; + border: solid 1px var(--border-strong); width: 800px; } div#cgit table.hgraph th { - background-color: #eee; + background-color: var(--bg-hl); font-weight: bold; - border: solid 1px black; + border: solid 1px var(--border-strong); padding: 1px 0.5em; } @@ -805,7 +891,7 @@ div#cgit table.hgraph td { } div#cgit table.hgraph div.bar { - background-color: #eee; + background-color: var(--bg-hl); height: 1em; } @@ -818,83 +904,83 @@ div#cgit table.ssdiff td { font-family: monospace; white-space: pre; padding: 1px 4px 1px 4px; - border-left: solid 1px #aaa; - border-right: solid 1px #aaa; + border-left: solid 1px var(--border-mid); + border-right: solid 1px var(--border-mid); } div#cgit table.ssdiff td.add { - color: black; - background: #cfc; + color: var(--fg-on-tint); + background: var(--add-bg); min-width: 50%; } div#cgit table.ssdiff td.add_dark { - color: black; - background: #aca; + color: var(--fg-on-tint); + background: var(--add-bg-dark); min-width: 50%; } div#cgit table.ssdiff span.add { - background: #cfc; + background: var(--add-bg); font-weight: bold; } div#cgit table.ssdiff td.del { - color: black; - background: #fcc; + color: var(--fg-on-tint); + background: var(--del-bg); min-width: 50%; } div#cgit table.ssdiff td.del_dark { - color: black; - background: #caa; + color: var(--fg-on-tint); + background: var(--del-bg-dark); min-width: 50%; } div#cgit table.ssdiff span.del { - background: #fcc; + background: var(--del-bg); font-weight: bold; } div#cgit table.ssdiff td.changed { - color: black; - background: #ffc; + color: var(--fg-on-tint); + background: var(--changed-bg); min-width: 50%; } div#cgit table.ssdiff td.changed_dark { - color: black; - background: #cca; + color: var(--fg-on-tint); + background: var(--changed-bg-dark); min-width: 50%; } div#cgit table.ssdiff td.lineno { - color: black; - background: #eee; + color: var(--fg-on-tint); + background: var(--bg-hl); text-align: right; width: 3em; min-width: 3em; } div#cgit table.ssdiff td.hunk { - color: black; - background: #ccf; - border-top: solid 1px #aaa; - border-bottom: solid 1px #aaa; + color: var(--fg-on-tint); + background: var(--hunk-bg); + border-top: solid 1px var(--border-mid); + border-bottom: solid 1px var(--border-mid); } div#cgit table.ssdiff td.head { - border-top: solid 1px #aaa; - border-bottom: solid 1px #aaa; + border-top: solid 1px var(--border-mid); + border-bottom: solid 1px var(--border-mid); } div#cgit table.ssdiff td.head div.head { font-weight: bold; - color: black; + color: var(--fg-on-tint); } div#cgit table.ssdiff td.foot { - border-top: solid 1px #aaa; + border-top: solid 1px var(--border-mid); border-left: none; border-right: none; border-bottom: none; @@ -907,3 +993,63 @@ div#cgit table.ssdiff td.space { div#cgit table.ssdiff td.space div { min-height: 3em; } + +div#cgit .hl.kwa, +div#cgit .hl.kwc { + color: var(--syn-keyword); + font-weight: bold; +} + +div#cgit .hl.kwb { + color: var(--syn-keyword-alt); +} + +div#cgit .hl.kwd { + color: var(--syn-type); +} + +div#cgit .hl.str { + color: var(--syn-string); +} + +div#cgit .hl.dstr, +div#cgit .hl.pps { + color: var(--syn-string-alt); +} + +div#cgit .hl.esc { + color: var(--syn-escape); +} + +div#cgit .hl.com, +div#cgit .hl.slc { + color: var(--syn-comment); + font-style: italic; +} + +div#cgit .hl.num { + color: var(--syn-number); +} + +div#cgit .hl.dir, +div#cgit .hl.ppc { + color: var(--syn-preproc); +} + +div#cgit .hl.opt, +div#cgit .hl.sym { + color: var(--syn-operator); +} + +div#cgit .hl.ipl { + color: var(--syn-interpolation); +} + +div#cgit .hl.lin, +div#cgit .hl.line { + color: var(--syn-linenum); +} + +div#cgit .hl.mark { + background-color: var(--syn-mark-bg); +} @@ -205,7 +205,7 @@ struct cgit_config { char *ci_tag_url; char *clone_prefix; char *clone_url; - char *favicon; + struct string_list favicon; char *footer; char *head_include; char *header; diff --git a/cgit.svg b/cgit.svg new file mode 100644 index 0000000..43fee2c --- /dev/null +++ b/cgit.svg @@ -0,0 +1,5 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="96" height="64" viewBox="0 0 96 64" fill="none" stroke="#a00" stroke-width="5"> + <title>cgit</title> + <path d="M31.5 27.5A9 9 0 0 0 22.5 18.5h-5A9 9 0 0 0 8.5 27.5V38a9 9 0 0 0 9 9h5a9 9 0 0 0 9-9M31.5 38v12.5a8 8 0 0 1-8 8H17a8 8 0 0 1-8-8M46.5 3v5m0 8v33M62.5 9v29.75a7.75 7.75 0 0 0 15.5 0V37"/> + <path stroke-width="4" d="M56 18h25"/> +</svg> diff --git a/cgitrc.5.txt b/cgitrc.5.txt index c03579f..527233d 100644 --- a/cgitrc.5.txt +++ b/cgitrc.5.txt @@ -240,9 +240,14 @@ enable-tree-linenumbers:: for plaintext blobs printed in the tree view. Default value: "1". favicon:: - Url used as link to a shortcut icon for cgit. It is suggested to use - the value "/favicon.ico" since certain browsers will ignore other - values. Default value: "/favicon.ico". + Url used as link to a shortcut icon for cgit. It is suggested to + always include the value "/favicon.ico" since certain browsers will + ignore other values. May be given multiple times, each favicon URL + path is added in the head section of the document in turn, letting + the browser pick the icon it prefers. The mimetype of each icon is + deduced from the extension of its url ("ico", "png", "svg", "gif", + "jpg", "jpeg", "webp" and "avif" are recognized) and emitted as a + "type" attribute. Default value: "/favicon.svg", "/favicon.ico". footer:: The content of the file specified with this option will be included @@ -272,7 +277,7 @@ local-time:: logo:: Url which specifies the source of an image which will be used as a logo - on all cgit pages. Default value: "/cgit.png". + on all cgit pages. Default value: "/cgit.svg". logo-link:: Url loaded when clicking on the cgit logo image. If unspecified the @@ -916,8 +921,10 @@ cache-size=1000 # Specify some default clone urls using macro expansion clone-url=git://foo.org/$CGIT_REPO_URL git@foo.org:$CGIT_REPO_URL + # Specify the css url css=/css/cgit.css +css=/css/solarized.css # Add a "ci" tab pointing at a Jenkins multibranch pipeline @@ -957,7 +964,8 @@ enable-log-linecount=1 branch-sort=age -# Add a cgit favicon +# Add cgit favicons +favicon=/favicon.svg favicon=/favicon.ico @@ -1117,3 +1125,4 @@ AUTHOR ------ Lars Hjemli <hjemli@gmail.com> Jason A. Donenfeld <Jason@zx2c4.com> +Saya Andy <saya.andy@posteo.com> @@ -1,6 +1,10 @@ /* cmd.c: the cgit command dispatcher * * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com> + * Copyright (C) 2026 Saya Andy <saya.andy@posteo.com> + * + * Modified 2026 by Saya Andy: + * Include ci ui element * * Licensed under GNU General Public License v2 * (see COPYING for full license text) diff --git a/favicon.svg b/favicon.svg new file mode 100644 index 0000000..6be3df4 --- /dev/null +++ b/favicon.svg @@ -0,0 +1,5 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="52" height="52" viewBox="0 0 52 52" fill="none" stroke-width="6"> + <title>cgit</title> + <g stroke="#f33"><path id="g" d="M37.5 15A9 9 0 0 0 28.5 6h-5A9 9 0 0 0 14.5 15v10.5a9 9 0 0 0 9 9h5a9 9 0 0 0 9-9M37.5 25.5V38a8 8 0 0 1-8 8H23a8 8 0 0 1-8-8"/></g> + <use href="#g" stroke="#a00" stroke-width="4"/> +</svg> @@ -1,6 +1,10 @@ /* filter.c: filter framework functions * * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * Copyright (C) 2026 Saya Andy <saya.andy@posteo.com> + * + * Modified 2026 by Saya Andy: + * Integrate ci filter * * Licensed under GNU General Public License v2 * (see COPYING for full license text) diff --git a/filters/html-converters/md2html b/filters/html-converters/md2html index 59f43a8..4f6ad37 100755 --- a/filters/html-converters/md2html +++ b/filters/html-converters/md2html @@ -2,8 +2,46 @@ import markdown import sys import io -from pygments.formatters import HtmlFormatter from markdown.extensions.toc import TocExtension + +# Pygments' token classes, mapped onto the --syn-* custom properties that +# cgit.css defines. The fallbacks are the palette those properties default to, +# so that the output stays readable under a stylesheet without them. +SYNTAX_COLORS = ( + (('k', 'kc', 'kd', 'kn', 'kr'), '--syn-keyword', '#000', + 'font-weight: bold'), + (('kp',), '--syn-keyword', '#000', None), + (('kt',), '--syn-type', '#010181', None), + (('nc', 'nf', 'nn', 'ne'), '--syn-keyword-alt', '#0057ae', None), + (('s', 'sa', 'sb', 'sc', 'dl', 'sd', 's2', 'sh', 'sx', 'sr', 's1', 'ss'), + '--syn-string', '#bf0303', None), + (('se',), '--syn-escape', '#f0f', None), + (('si',), '--syn-interpolation', '#0057ae', None), + (('c', 'ch', 'cm', 'c1', 'cs'), '--syn-comment', '#838183', + 'font-style: italic'), + (('cp', 'cpf'), '--syn-preproc', '#008200', None), + (('m', 'mb', 'mf', 'mh', 'mi', 'mo', 'il'), '--syn-number', '#b07e00', + None), + (('o', 'ow', 'p'), '--syn-operator', '#000', None), + (('err',), '--syn-error', '#bf0303', None), + (('gd',), '--red', 'red', None), + (('gi',), '--green', 'green', None), + (('lineno', 'linenos'), '--syn-linenum', '#555', None), +) + + +def style_defs(prefix='.highlight'): + rules = [] + for classes, prop, fallback, extra in SYNTAX_COLORS: + selector = ', '.join('%s .%s' % (prefix, c) for c in classes) + decls = 'color: var(%s, %s)' % (prop, fallback) + if extra: + decls += '; ' + extra + rules.append('%s { %s }' % (selector, decls)) + rules.append('%s .hll { background-color: var(--syn-mark-bg, #ffb) }' + % prefix) + return '\n'.join(rules) + '\n' + sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8') sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') sys.stdout.write(''' @@ -20,7 +58,7 @@ sys.stdout.write(''' margin-bottom: 0 !important; } .markdown-body a.absent { - color: #c00; + color: var(--red-mid, #c00); } .markdown-body a.anchor { display: block; @@ -42,7 +80,7 @@ sys.stdout.write(''' } .markdown-body h1 .mini-icon-link, .markdown-body h2 .mini-icon-link, .markdown-body h3 .mini-icon-link, .markdown-body h4 .mini-icon-link, .markdown-body h5 .mini-icon-link, .markdown-body h6 .mini-icon-link { display: none; - color: #000; + color: var(--fg-strong, #000); } .markdown-body h1:hover a.anchor, .markdown-body h2:hover a.anchor, .markdown-body h3:hover a.anchor, .markdown-body h4:hover a.anchor, .markdown-body h5:hover a.anchor, .markdown-body h6:hover a.anchor { text-decoration: none; @@ -55,19 +93,19 @@ sys.stdout.write(''' display: inline-block; } div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div#cgit .markdown-body h3 a.toclink, div#cgit .markdown-body h4 a.toclink, div#cgit .markdown-body h5 a.toclink, div#cgit .markdown-body h6 a.toclink { - color: black; + color: var(--fg-strong, #000); } .markdown-body h1 tt, .markdown-body h1 code, .markdown-body h2 tt, .markdown-body h2 code, .markdown-body h3 tt, .markdown-body h3 code, .markdown-body h4 tt, .markdown-body h4 code, .markdown-body h5 tt, .markdown-body h5 code, .markdown-body h6 tt, .markdown-body h6 code { font-size: inherit; } .markdown-body h1 { font-size: 28px; - color: #000; + color: var(--fg-strong, #000); } .markdown-body h2 { font-size: 24px; - border-bottom: 1px solid #ccc; - color: #000; + border-bottom: 1px solid var(--border, #ccc); + color: var(--fg-strong, #000); } .markdown-body h3 { font-size: 18px; @@ -79,14 +117,14 @@ div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div# font-size: 14px; } .markdown-body h6 { - color: #777; + color: var(--fg-mute, #777); font-size: 14px; } .markdown-body p, .markdown-body blockquote, .markdown-body ul, .markdown-body ol, .markdown-body dl, .markdown-body table, .markdown-body pre { margin: 15px 0; } .markdown-body hr { - border: 2px solid #ccc; + border: 2px solid var(--border, #ccc); } .markdown-body>h2:first-child, .markdown-body>h1:first-child, .markdown-body>h1:first-child+h2, .markdown-body>h3:first-child, .markdown-body>h4:first-child, .markdown-body>h5:first-child, .markdown-body>h6:first-child { margin-top: 0; @@ -148,9 +186,9 @@ div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div# margin-bottom: 0px; } .markdown-body blockquote { - border-left: 4px solid #DDD; + border-left: 4px solid var(--border, #ddd); padding: 0 15px; - color: #777; + color: var(--fg-mute, #777); } .markdown-body blockquote>:first-child { margin-top: 0px; @@ -162,15 +200,15 @@ div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div# font-weight: bold; } .markdown-body table th, .markdown-body table td { - border: 1px solid #ccc; + border: 1px solid var(--border, #ccc); padding: 6px 13px; } .markdown-body table tr { - border-top: 1px solid #ccc; - background-color: #fff; + border-top: 1px solid var(--border, #ccc); + background-color: var(--bg, #fff); } .markdown-body table tr:nth-child(2n) { - background-color: #f8f8f8; + background-color: var(--bg-zebra, #f8f8f8); } .markdown-body img { max-width: 100%; @@ -182,7 +220,7 @@ div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div# overflow: hidden; } .markdown-body span.frame>span { - border: 1px solid #ddd; + border: 1px solid var(--border, #ddd); display: block; float: left; overflow: hidden; @@ -196,7 +234,7 @@ div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div# } .markdown-body span.frame span span { clear: both; - color: #333; + color: var(--fg, #333); display: block; padding: 5px 0 0; } @@ -254,8 +292,8 @@ div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div# .markdown-body code, .markdown-body tt { margin: 0 2px; padding: 0px 5px; - border: 1px solid #eaeaea; - background-color: #f8f8f8; + border: 1px solid var(--border, #eaeaea); + background-color: var(--bg-zebra, #f8f8f8); border-radius: 3px; } .markdown-body code { @@ -269,8 +307,8 @@ div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div# background: transparent; } .markdown-body .highlight pre, .markdown-body pre { - background-color: #f8f8f8; - border: 1px solid #ccc; + background-color: var(--bg-zebra, #f8f8f8); + border: 1px solid var(--border, #ccc); font-size: 13px; line-height: 19px; overflow: auto; @@ -284,9 +322,9 @@ div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div# border: none; } ''') -sys.stdout.write(HtmlFormatter(style='pastie').get_style_defs('.highlight')) +sys.stdout.write(style_defs('.highlight')) sys.stdout.write(''' -</style> +</style> ''') sys.stdout.write("<div class='markdown-body'>") sys.stdout.flush() diff --git a/filters/syntax-highlighting.py b/filters/syntax-highlighting.py index e912594..d757e2d 100755 --- a/filters/syntax-highlighting.py +++ b/filters/syntax-highlighting.py @@ -30,11 +30,50 @@ from pygments.lexers import guess_lexer_for_filename from pygments.formatters import HtmlFormatter +# Pygments' token classes, mapped onto the --syn-* custom properties that +# cgit.css defines. The fallbacks are the palette those properties default to, +# so that the output stays readable under a stylesheet without them. +SYNTAX_COLORS = ( + (('k', 'kc', 'kd', 'kn', 'kr'), '--syn-keyword', '#000', + 'font-weight: bold'), + (('kp',), '--syn-keyword', '#000', None), + (('kt',), '--syn-type', '#010181', None), + (('nc', 'nf', 'nn', 'ne'), '--syn-keyword-alt', '#0057ae', None), + (('s', 'sa', 'sb', 'sc', 'dl', 'sd', 's2', 'sh', 'sx', 'sr', 's1', 'ss'), + '--syn-string', '#bf0303', None), + (('se',), '--syn-escape', '#f0f', None), + (('si',), '--syn-interpolation', '#0057ae', None), + (('c', 'ch', 'cm', 'c1', 'cs'), '--syn-comment', '#838183', + 'font-style: italic'), + (('cp', 'cpf'), '--syn-preproc', '#008200', None), + (('m', 'mb', 'mf', 'mh', 'mi', 'mo', 'il'), '--syn-number', '#b07e00', + None), + (('o', 'ow', 'p'), '--syn-operator', '#000', None), + (('err',), '--syn-error', '#bf0303', None), + (('gd',), '--red', 'red', None), + (('gi',), '--green', 'green', None), + (('lineno', 'linenos'), '--syn-linenum', '#555', None), +) + + +def style_defs(prefix='.highlight'): + rules = [] + for classes, prop, fallback, extra in SYNTAX_COLORS: + selector = ', '.join('%s .%s' % (prefix, c) for c in classes) + decls = 'color: var(%s, %s)' % (prop, fallback) + if extra: + decls += '; ' + extra + rules.append('%s { %s }' % (selector, decls)) + rules.append('%s .hll { background-color: var(--syn-mark-bg, #ffb) }' + % prefix) + return '\n'.join(rules) + '\n' + + sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8', errors='replace') sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') data = sys.stdin.read() filename = sys.argv[1] -formatter = HtmlFormatter(style='pastie', nobackground=True) +formatter = HtmlFormatter(nobackground=True) try: lexer = guess_lexer_for_filename(filename, data) @@ -48,8 +87,8 @@ except TypeError: lexer = TextLexer() # highlight! :-) -# printout pygments' css definitions as well +# printout the css definitions for pygments' token classes as well sys.stdout.write('<style>') -sys.stdout.write(formatter.get_style_defs('.highlight')) +sys.stdout.write(style_defs('.highlight')) sys.stdout.write('</style>') sys.stdout.write(highlight(data, lexer, formatter, outfile=None)) diff --git a/filters/syntax-highlighting.sh b/filters/syntax-highlighting.sh index 840bc34..bb8d60c 100755 --- a/filters/syntax-highlighting.sh +++ b/filters/syntax-highlighting.sh @@ -8,8 +8,10 @@ # might have to use an external call to sed instead. # # Note: the highlight command (http://www.andre-simon.de/) uses css for syntax -# highlighting, so you'll probably want something like the following included -# in your css file: +# highlighting. cgit.css defines these classes in terms of its --syn-* custom +# properties, so nothing has to be added when using the default stylesheet. +# The palette below, from highlight 3.13, is what those properties default to; +# a custom stylesheet wanting the same colors can use it directly: # # Style definition file generated by highlight 2.4.8, http://www.andre-simon.de/ # @@ -114,8 +116,12 @@ EXTENSION="${BASENAME##*.}" # Version 2 can be found (for example) on EPEL 5, while version 3 can be # found (for example) on EPEL 6. # +# Note that -I (--include-style) is not used: the stylesheet it embeds in the +# output would override the .hl rules in cgit.css and with them any theming +# done through the --syn-* custom properties. +# # This is for version 2 -exec highlight --force -f -I -X -S "$EXTENSION" 2>/dev/null +exec highlight --force -f -X -S "$EXTENSION" 2>/dev/null # This is for version 3 -#exec highlight --force -f -I -O xhtml -S "$EXTENSION" 2>/dev/null +#exec highlight --force -f -O xhtml -S "$EXTENSION" 2>/dev/null @@ -1,6 +1,11 @@ /* shared.c: global vars + some callback functions * * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * Copyright (C) 2026 Saya Andy <saya.andy@posteo.com> + * + * Modified 2026 by Saya Andy: + * Add cgit_have_repository() + * Add ci filter configuration * * Licensed under GNU General Public License v2 * (see COPYING for full license text) @@ -13,13 +18,6 @@ struct cgit_repolist cgit_repolist; struct cgit_context ctx; -/* Returns true when the git repository belonging to this request was opened - * successfully. Some requests print a page for a repository which could not - * be opened, e.g. because repo.path is wrong, and the object database and - * ref stores must not be touched in that case: they would abort with - * "reference backend is unknown" or dereference the not yet known hash - * algorithm. - */ int cgit_have_repository(void) { return startup_info->have_repository; diff --git a/solarized.css b/solarized.css new file mode 100644 index 0000000..2e6e3d4 --- /dev/null +++ b/solarized.css @@ -0,0 +1,140 @@ +/* + * Solarized theme (Ethan Schoonover) with automatic light/dark selection + * based on the prefers-color-scheme media query. + * + * Must be listed after cgit.css, e.g. + * + * css=/cgit.css + * css=/solarized.css + */ + +:root { + --sol-base03: #002b36; + --sol-base02: #073642; + --sol-base01: #586e75; + --sol-base00: #657b83; + --sol-base0: #839496; + --sol-base1: #93a1a1; + --sol-base2: #eee8d5; + --sol-base3: #fdf6e3; + + --sol-yellow: #b58900; + --sol-orange: #cb4b16; + --sol-red: #dc322f; + --sol-magenta: #d33682; + --sol-violet: #6c71c4; + --sol-blue: #268bd2; + --sol-cyan: #2aa198; + --sol-green: #859900; + + --sol-canvas: var(--sol-base3); + --sol-panel: var(--sol-base2); + --sol-body: var(--sol-base00); + --sol-emph: var(--sol-base01); + --sol-muted: var(--sol-base0); + --sol-secondary: var(--sol-base1); + --sol-shade: var(--sol-base03); + --sol-page: var(--sol-panel); + + --sol-tint-soft: 18%; + --sol-tint: 25%; + --sol-tint-strong: 45%; + + color-scheme: light dark; +} + +@media (prefers-color-scheme: dark) { + :root { + --sol-canvas: var(--sol-base03); + --sol-panel: var(--sol-base02); + --sol-body: var(--sol-base0); + --sol-emph: var(--sol-base1); + --sol-muted: var(--sol-base00); + --sol-secondary: var(--sol-base01); + --sol-shade: var(--sol-base3); + + --sol-tint-soft: 30%; + --sol-tint: 35%; + --sol-tint-strong: 55%; + } +} + +body:has(> div#cgit) { + margin: 0; + background-color: var(--sol-page); +} + +div#cgit { + --bg: var(--sol-canvas); + --bg-zebra: color-mix(in srgb, var(--sol-panel) 40%, var(--sol-canvas)); + --bg-hl: var(--sol-panel); + --bg-hl2: color-mix(in srgb, var(--sol-panel) 85%, var(--sol-shade)); + --fg: var(--sol-body); + --fg-strong: var(--sol-emph); + --fg-mid: var(--sol-body); + --fg-mute: var(--sol-muted); + --fg-dim: var(--sol-secondary); + --fg-faint: color-mix(in srgb, var(--sol-secondary) 65%, var(--sol-canvas)); + --fg-faintest: color-mix(in srgb, var(--sol-secondary) 45%, var(--sol-canvas)); + --fg-on-tint: var(--sol-emph); + --fg-linenum: var(--sol-secondary); + --border: var(--sol-panel); + --border-mid: var(--sol-secondary); + --border-dark: var(--sol-muted); + --border-strong: var(--sol-emph); + + --red: var(--sol-red); + --red-mid: color-mix(in srgb, var(--sol-red) 75%, var(--sol-shade)); + --red-dark: color-mix(in srgb, var(--sol-red) 55%, var(--sol-shade)); + --green: var(--sol-green); + --green-mid: color-mix(in srgb, var(--sol-green) 70%, var(--sol-shade)); + --green-dark: color-mix(in srgb, var(--sol-green) 50%, var(--sol-shade)); + --blue: var(--sol-blue); + --blue-dark: color-mix(in srgb, var(--sol-blue) 70%, var(--sol-shade)); + + --graph-col1: var(--sol-red); + --graph-col2: var(--sol-green); + --graph-col3: var(--sol-yellow); + --graph-col4: var(--sol-blue); + --graph-col5: var(--sol-magenta); + --graph-col6: var(--sol-cyan); + + --graph-add-bg: var(--sol-green); + --graph-rem-bg: var(--sol-red); + + --notes-bg: color-mix(in srgb, var(--sol-yellow) var(--sol-tint-soft), var(--sol-canvas)); + --notes-border: var(--sol-yellow); + --add-bg: color-mix(in srgb, var(--sol-green) var(--sol-tint), var(--sol-canvas)); + --add-bg-dark: color-mix(in srgb, var(--sol-green) var(--sol-tint-strong), var(--sol-canvas)); + --del-bg: color-mix(in srgb, var(--sol-red) var(--sol-tint), var(--sol-canvas)); + --del-bg-dark: color-mix(in srgb, var(--sol-red) var(--sol-tint-strong), var(--sol-canvas)); + --changed-bg: color-mix(in srgb, var(--sol-yellow) var(--sol-tint), var(--sol-canvas)); + --changed-bg-dark: color-mix(in srgb, var(--sol-yellow) var(--sol-tint-strong), var(--sol-canvas)); + --hunk-bg: color-mix(in srgb, var(--sol-blue) var(--sol-tint), var(--sol-canvas)); + + --deco-branch-bg: color-mix(in srgb, var(--sol-green) var(--sol-tint-soft), var(--sol-canvas)); + --deco-branch-border: var(--sol-green); + --deco-tag-bg: color-mix(in srgb, var(--sol-yellow) var(--sol-tint-soft), var(--sol-canvas)); + --deco-tag-border: var(--sol-yellow); + --deco-tag-annotated-bg: color-mix(in srgb, var(--sol-orange) var(--sol-tint-soft), var(--sol-canvas)); + --deco-tag-annotated-border: var(--sol-yellow); + --deco-remote-bg: color-mix(in srgb, var(--sol-violet) var(--sol-tint-soft), var(--sol-canvas)); + --deco-remote-border: var(--sol-violet); + --deco-other-bg: color-mix(in srgb, var(--sol-red) var(--sol-tint-soft), var(--sol-canvas)); + --deco-other-border: var(--sol-red); + + --syn-keyword: var(--sol-emph); + --syn-keyword-alt: var(--sol-blue); + --syn-type: var(--blue-dark); + --syn-string: var(--sol-red); + --syn-string-alt: var(--sol-green); + --syn-escape: var(--sol-magenta); + --syn-comment: var(--sol-secondary); + --syn-number: var(--sol-yellow); + --syn-preproc: var(--green-mid); + --syn-operator: var(--sol-emph); + --syn-interpolation: var(--sol-blue); + --syn-linenum: var(--sol-secondary); + --syn-error: var(--sol-red); + --syn-mark-bg: color-mix(in srgb, var(--sol-yellow) var(--sol-tint), var(--sol-canvas)); +} diff --git a/tests/t0106-diff.sh b/tests/t0106-diff.sh index 82b645e..1100596 100755 --- a/tests/t0106-diff.sh +++ b/tests/t0106-diff.sh @@ -16,4 +16,86 @@ test_expect_success 'find added line' ' grep "<div class=.add.>+5</div>" tmp ' +test_expect_success 'ref picker: from: select present' ' + grep "<select name=.id2." tmp +' + +test_expect_success 'ref picker: to: select present' ' + grep "<select name=.id." tmp +' + +test_expect_success 'ref picker: from: label present' ' + grep "from:" tmp +' + +test_expect_success 'ref picker: to: label present' ' + grep "to:" tmp +' + +test_expect_success 'ref pickers live in a separate refs panel' ' + grep "<b>diff refs</b>" tmp && + grep "<b>diff options</b>" tmp +' + +test_expect_success 'ref picker: master branch listed' ' + grep "master" tmp +' + +test_expect_success 'ref picker: auto-submit on change' ' + grep "onchange=.this.form.submit()." tmp +' + +test_expect_success 'flip link present' ' + grep "<a title=.swap from and to. class=.button. href=./foo/diff/?" tmp +' + +test_expect_success 'flip link swaps the default refs' ' + old=$(git --git-dir="$PWD/repos/foo/.git" rev-parse master~1) && + grep "/foo/diff/?id=$old&id2=master" tmp +' + +test_expect_success 'compare two refs: foo/diff?id=master&id2=HEAD' ' + cgit_query "url=foo/diff&id=master&id2=HEAD" >tmp +' + +test_expect_success 'diff with explicit refs shows diffstat' ' + grep "diffstat" tmp +' + +test_expect_success 'diff with oid2 parameter uses it for old ref' ' + cgit_query "url=foo/diff&id=master&id2=HEAD" >tmp && + grep "from:" tmp +' + +test_expect_success 'ref picker: to: shows master when id=master' ' + cgit_query "url=foo/diff&id=master" >tmp && + grep "master" tmp +' + +test_expect_success 'ref picker: from: empty when no id2' ' + cgit_query "url=foo/diff" >tmp && + grep "<select name=.id2." tmp +' + +test_expect_success 'compare initial commit with HEAD' ' + root=$(git --git-dir="$PWD/repos/foo/.git" rev-list --max-parents=0 HEAD) && + cgit_query "url=foo/diff&id=HEAD&id2=$root" >tmp +' + +test_expect_success 'diff with HEAD and root shows content' ' + grep "diffstat" tmp +' + +test_expect_success 'flip link swaps explicit refs' ' + cgit_query "url=foo/diff&id=master&id2=HEAD" >tmp && + new=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD) && + grep "/foo/diff/?id=$new&id2=master" tmp +' + +test_expect_success 'flip link omits id for a diff against a root commit' ' + root=$(git --git-dir="$PWD/repos/foo/.git" rev-list --max-parents=0 HEAD) && + cgit_query "url=foo/diff&id=$root" >tmp && + grep "href=./foo/diff/?id2=$root." tmp +' + test_done diff --git a/tests/t0114-broken-repo.sh b/tests/t0114-broken-repo.sh new file mode 100644 index 0000000..61367e6 --- /dev/null +++ b/tests/t0114-broken-repo.sh @@ -0,0 +1,71 @@ +#!/bin/sh + +test_description='Check that a misconfigured repo.path is reported' +. ./setup.sh + +mkdir -p plaindir + +cat >cgitrc.broken <<EOF +virtual-root=/ +cache-root=$PWD/cache +cache-size=0 + +ci-branch-url=https://ci.example.org/job/\$slug/job/\$ref + +repo.url=missing +repo.path=$PWD/no/such/repo.git + +repo.url=notarepo +repo.path=$PWD/plaindir +EOF + +# cgit used to spin forever on these requests, so give it a deadline and +# report a failure rather than hanging the test suite. +cgit_broken() +{ + CGIT_CONFIG="$PWD/cgitrc.broken" QUERY_STRING="url=$1" perl -e ' + my $pid = fork(); + if ($pid == 0) { + exec("cgit") or die "exec: $!"; + } + eval { + local $SIG{ALRM} = sub { die "timeout\n" }; + alarm(20); + waitpid($pid, 0); + alarm(0); + }; + if ($@) { + kill "KILL", $pid; + print STDERR "cgit did not terminate\n"; + exit 1; + } + ' </dev/null +} + +test_expect_success 'report a repo.path that does not exist' ' + cgit_broken "missing/&h=master" >tmp && + grep "Failed to open missing" tmp +' + +test_expect_success 'report a repo.path that is not a repository' ' + cgit_broken "notarepo/&h=master" >tmp && + grep "Failed to open notarepo" tmp +' + +test_expect_success 'report an unknown page for a broken repo' ' + cgit_broken "missing/nosuchpage/&h=master" >tmp && + grep "^Status: 404 Not found$" tmp && + grep "Invalid request" tmp +' + +test_expect_success 'no ci tab for a broken repo' ' + cgit_broken "missing/nosuchpage/&h=master" >tmp && + ! grep ">ci</a>" tmp +' + +test_expect_success 'report a broken repo on the ci page' ' + cgit_broken "missing/ci/&h=master" >tmp && + grep "Failed to open missing" tmp +' + +test_done @@ -1,6 +1,6 @@ /* ui-ci.c: redirect to an external CI system * - * Copyright (C) 2006-2025 cgit Development Team <cgit@lists.zx2c4.com> + * Copyright (C) 2026 Saya Andy <saya.andy@posteo.com> * * Licensed under GNU General Public License v2 * (see COPYING for full license text) @@ -1,6 +1,10 @@ /* ui-diff.c: show diff between two blobs * * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * Copyright (C) 2026 Saya Andy <saya.andy@posteo.com> + * + * Modified 2026 by Saya Andy: + * Select variable refs from web ui for diff * * Licensed under GNU General Public License v2 * (see COPYING for full license text) @@ -336,16 +340,92 @@ static void filepair_cb(struct diff_filepair *pair) cgit_ssdiff_footer(); } +static int print_ref_option(const struct reference *ref, void *cb_data) +{ + const char **sel = (const char **)cb_data; + + html_option(ref->name, ref->name, sel[0]); + return 0; +} + +static void cgit_print_ref_select(const char *name, const char *selected) +{ + /* + * Offer every branch and tag so that any two refs can be compared. + */ + html("<select name='"); + html_attr(name); + html("' onchange='this.form.submit();'>"); + refs_for_each_branch_ref(get_main_ref_store(the_repository), + print_ref_option, (void *)&selected); + if (ctx.repo->enable_remote_branches) + refs_for_each_remote_ref(get_main_ref_store(the_repository), + print_ref_option, (void *)&selected); + refs_for_each_tag_ref(get_main_ref_store(the_repository), + print_ref_option, (void *)&selected); + html("</select>"); +} + void cgit_print_diff_ctrls(void) { int i, curr; + html("<form class='diff-ctrls' method='get'>"); + /* + * Emit the same hidden fields as cgit_add_hidden_formfields(), except + * id/id2, which are carried by the ref-picker selects below. One + * form wraps both panels so any submission carries all controls. + */ + if (!ctx.cfg.virtual_root) { + struct strbuf url = STRBUF_INIT; + strbuf_addf(&url, "%s/%s", ctx.qry.repo, ctx.qry.page); + if (ctx.qry.vpath) + strbuf_addf(&url, "/%s", ctx.qry.vpath); + html_hidden("url", url.buf); + strbuf_release(&url); + } + if (ctx.qry.head && ctx.repo->defbranch && + strcmp(ctx.qry.head, ctx.repo->defbranch)) + html_hidden("h", ctx.qry.head); + if (ctx.qry.showmsg) + html_hidden("showmsg", "1"); + + html("<div class='cgit-panel'>"); + html("<b>diff refs</b>"); + html("<table>"); + html("<tr><td colspan='2'/></tr>"); + + /* ref pickers: compare any two refs (branches/tags) */ + { + const char *new_ref = ctx.qry.oid ? ctx.qry.oid : ctx.qry.head; + const char *old_ref = ctx.qry.oid2; + + html("<tr>"); + html("<td class='label'>from:</td>"); + html("<td class='ctrl'>"); + cgit_print_ref_select("id2", old_ref); + html("</td></tr><tr>"); + html("<td class='label'>to:</td>"); + html("<td class='ctrl'>"); + cgit_print_ref_select("id", new_ref); + html("</td></tr>"); + + html("<tr><td/><td class='ctrl'>"); + cgit_diff_link("flip", "swap from and to", "button", + ctx.qry.head, + is_null_oid(old_rev_oid) ? NULL : + oid_to_hex(old_rev_oid), + new_ref, ctx.qry.path); + html("</td></tr>"); + } + html("</table>"); + html("</div>"); + html("<div class='cgit-panel'>"); html("<b>diff options</b>"); - html("<form method='get'>"); - cgit_add_hidden_formfields(1, 0, ctx.qry.page); html("<table>"); html("<tr><td colspan='2'/></tr>"); + html("<tr>"); html("<td class='label'>context:</td>"); html("<td class='ctrl'>"); @@ -379,8 +459,8 @@ void cgit_print_diff_ctrls(void) html("<tr><td/><td class='ctrl'>"); html("<noscript><input type='submit' value='reload'/></noscript>"); html("</td></tr></table>"); - html("</form>"); html("</div>"); + html("</form>"); } void cgit_print_diff(const char *new_rev, const char *old_rev, diff --git a/ui-shared.c b/ui-shared.c index 9a9d014..686fde0 100644 --- a/ui-shared.c +++ b/ui-shared.c @@ -1,6 +1,14 @@ /* ui-shared.c: common web output functions * * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com> + * Copyright (C) 2026 Saya Andy <saya.andy@posteo.com> + * + * Modified 2026 by Saya Andy: + * Implement ci link tab + * Check repo existence + * Replace memrchr() for macOS compatibility + * Allow multiple favicons of different mime types to be used + * Include favicon.svg as default * * Licensed under GNU General Public License v2 * (see COPYING for full license text) @@ -768,6 +776,51 @@ void cgit_redirect(const char *url, bool permanent) html("\n\n"); } +static const struct { + const char *ext; + const char *mimetype; +} favicon_mimetypes[] = { + { "ico", "image/x-icon" }, + { "png", "image/png" }, + { "svg", "image/svg+xml" }, + { "gif", "image/gif" }, + { "jpg", "image/jpeg" }, + { "jpeg", "image/jpeg" }, + { "webp", "image/webp" }, + { "avif", "image/avif" }, +}; + +/* Deduce the mimetype of a favicon from the extension of its url, ignoring + * any query string or fragment. Returns NULL for unknown extensions. + */ +static const char *favicon_mimetype(const char *url) +{ + const char *p, *end, *ext = NULL; + size_t len; + int i; + + end = strpbrk(url, "?#"); + if (!end) + end = url + strlen(url); + + for (p = url; p < end; p++) { + if (*p == '/') + ext = NULL; + else if (*p == '.') + ext = p + 1; + } + if (!ext) + return NULL; + len = end - ext; + + for (i = 0; i < ARRAY_SIZE(favicon_mimetypes); i++) { + if (strlen(favicon_mimetypes[i].ext) == len && + !strncasecmp(favicon_mimetypes[i].ext, ext, len)) + return favicon_mimetypes[i].mimetype; + } + return NULL; +} + static void print_rel_vcs_link(const char *url) { html("<link rel='vcs-git' href='"); @@ -809,6 +862,27 @@ static int emit_js_link(struct string_list_item *s, void *arg) return 0; } +static int emit_favicon_link(struct string_list_item *s, void *arg) +{ + const char *url = s ? s->string : (const char *)arg; + const char *mimetype; + + /* Do not emit anything if favicon= is specified. */ + if (!*url) + return 0; + + html("<link rel='shortcut icon' href='"); + html_attr(url); + mimetype = favicon_mimetype(url); + if (mimetype) { + html("' type='"); + html_attr(mimetype); + } + html("'/>\n"); + + return 0; +} + void cgit_print_docstart(void) { char *host = cgit_hosturl(); @@ -839,11 +913,12 @@ void cgit_print_docstart(void) else emit_js_link(NULL, "/cgit.js"); - if (ctx.cfg.favicon && *ctx.cfg.favicon) { - html("<link rel='shortcut icon' href='"); - html_attr(ctx.cfg.favicon); - html("'/>\n"); - } + if (ctx.cfg.favicon.items) + for_each_string_list(&ctx.cfg.favicon, emit_favicon_link, NULL); + else + emit_favicon_link(NULL, "/favicon.svg"); + emit_favicon_link(NULL, "/favicon.ico"); + if (host && ctx.repo && ctx.qry.head) { char *fileurl; struct strbuf sb = STRBUF_INIT; @@ -884,7 +959,7 @@ void cgit_print_docend(void) if (ctx.cfg.footer) html_include(ctx.cfg.footer); else { - htmlf("<div class='footer'>generated by <a href='https://git.zx2c4.com/cgit/about/'>cgit %s</a> " + htmlf("<div class='footer'>generated by <a href='https://sayag.it/cgitext.git/about/'>cgitext %s</a> " "(<a href='https://git-scm.com/'>git %s</a>) at ", cgit_version, git_version_string); html_txt(show_date(time(NULL), 0, cgit_date_mode(DATE_ISO8601))); html("</div>\n"); |