From 2d57173e318bd14742d114591793638bd5f999aa Mon Sep 17 00:00:00 2001 From: hermes Date: Tue, 29 Sep 2026 16:38:48 +1000 Subject: [PATCH] fix: CodeGit looked like an empty black box - hardcoded panel height + cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported as "why can't i see anything from codegit its just a black box". NOT a codegit bug. Verified first, exhaustively, through the authenticated API: overview 200 940 bytes log 200 79596 bytes (full commit history) transfer 200 1460 bytes config 200 1174 bytes no JS errors in the console; every DOM helper codegit calls exists. ROOT CAUSE 1 - a generic CSS rule hardcodes a height theme/modules/common/_panel.scss -> theme/main.min.css: panel { ... height: 500px; ... } That forces EVERY modal panel to a fixed 500px box. Measured live: #dialog 404px (clamped by max-height:70vh + overflow-y:auto) #codegit panel 500px, while its content is only 119px toolbar ("Branch") top 620 vs dialog bottom 490 -> OUTSIDE the modal So ~380px of the dialog was empty dead space and the Branch line rendered below the modal, invisible. Fixed to height:auto with max-height:58vh (long content still scrolls): before: panel 500 -> toolbar top 620, dialog bottom 490 (outside) after: panel 136 -> toolbar top 256, dialog bottom 279 (inside) _panel.scss updated too so the source does not contradict the shipped CSS. ROOT CAUSE 2 - the fix was invisible because of the browser cache Atheos serves theme/main.min.css and the generated bundles from stable URLs with no cache headers, so browsers legitimately reuse their cached copy - which is why the first attempt appeared to do nothing. nginx now sends Cache-Control: no-cache, must-revalidate for *.css/*.js: caching still works (cheap 304s) but changes are always picked up. Adds scripts/fix-panel-layout.sh idempotent re-apply for both fixes (lost on update-atheos) docs/known-issues.md ยง4, ยง5 both findings, with the measurements Tests (44 passed, 1 failed - the failure is branch protection, a genuine open item) + panel must not be fixed at 500px + theme CSS must send Cache-Control: no-cache --- docs/known-issues.md | 86 ++++++++++++++++++++++++++++++++++--- files/atheos.conf | 27 ++++++++---- scripts/build-and-test.sh | 22 +++++++++- scripts/fix-panel-layout.sh | 76 ++++++++++++++++++++++++++++++++ 4 files changed, 194 insertions(+), 17 deletions(-) create mode 100644 scripts/fix-panel-layout.sh diff --git a/docs/known-issues.md b/docs/known-issues.md index 11863dc..0a76a29 100644 --- a/docs/known-issues.md +++ b/docs/known-issues.md @@ -224,7 +224,79 @@ Worth doing if the dialog starts 500-ing again after editing a macro in the UI. --- -## 4. Checking for plugin problems in future +## 4. ๐ŸŸ  The CodeGit panel looked like an empty black box + +**Symptom:** the CodeGit dialog appeared as a mostly-empty black rectangle. Nothing was *broken* โ€” +it was unreadable, which reads as "broken". + +**This was NOT a codegit bug.** Verified exhaustively first: + +``` +all four panels return real content through the authenticated API: + overview 200 940 bytes + log 200 79596 bytes (full commit history) + transfer 200 1460 bytes + config 200 1174 bytes +no JS errors in the browser console; all DOM helpers (replaceClass etc.) exist +``` + +**Root cause โ€” the generic `panel` rule hardcodes a height.** `theme/modules/common/_panel.scss` +(after minification, in `theme/main.min.css`): + +```css +panel { padding: 5px; width: calc(100% - 200px); overflow: auto; overflow-y: scroll; + height: 500px; position: relative; border-left: var(--borderLight); } +``` + +Measured live, that produces: + +| Element | Height | Problem | +|---|---|---| +| `#dialog` | 404px | clamped by `max-height: 70vh` + `overflow-y: auto` | +| `#codegit panel` | 500px | **forced**, while its content is 119px | +| `toolbar` ("Branch: โ€ฆ") | top **620** vs dialog bottom **490** | **pushed OUTSIDE the modal** | + +So ~380px of the dialog was **empty dead space**, and anything after the panel โ€” including the +Branch line โ€” rendered *below* the modal, invisible. A generic `panel { height: 500px }` is exactly +the kind of rule that looks like a void. + +**Fix applied:** + +```css +panel { ... height: auto; max-height: 58vh; overflow-y: auto; ... } +``` + +Content-sized, capped so long content (the 79KB log) still scrolls. Verified live: + +``` +before: panel 500px โ†’ toolbar top 620, dialog bottom 490 (outside) +after: panel 136px โ†’ toolbar top 256, dialog bottom 279 (inside) +``` + +`theme/modules/common/_panel.scss` is updated too, so the source does not contradict the shipped CSS +(there is no Sass compiler on the guest, so `main.min.css` is edited directly). + +--- + +## 5. ๐ŸŸ  CSS/JS changes were invisible behind the browser cache + +**Symptom:** a fix to the theme did not appear until a hard refresh โ€” so the panel fix above looked +like it had not worked at all. + +**Root cause:** Atheos serves `theme/main.min.css` and the generated `public/*.min.js|css` bundles +from **stable URLs with no cache headers**, so nginx adds only `Last-Modified`/`ETag` and browsers +legitimately reuse their cached copy. + +**Fix:** nginx now sends `Cache-Control: no-cache, must-revalidate` for `*.css`/`*.js`. Note this does +**not** disable caching โ€” it forces **revalidation**, so normal loads still get a cheap 304 while any +change is guaranteed to be picked up on the next load. + +**If you ever see a stale UI again:** force a reload (Ctrl/Cmd-Shift-R). That is the first thing to try +before assuming a fix failed. + +--- + +## 6. Checking for plugin problems in future The plugin's JS is bundled into `public/plugins.min.js` (built by `classes/sourcemanager.php`), and its rightbar button is rendered by `components/sidebars/sb-right.php` from `plugin.json`. Both were @@ -258,17 +330,17 @@ verify them before trusting it. --- -## 5. Follow-ups not done (recorded so they are not forgotten) +## 7. Follow-ups not done (recorded so they are not forgotten) These came out of review. They are **operator decisions**, not agent ones โ€” several change what the editor can do. -### 5.1 Remove the Terminal plugin โ€” โœ… DONE +### 7.1 Remove the Terminal plugin โ€” โœ… DONE Removed through the market's own code path once ยง1 was fixed (see ยง2). A browser shell was the largest attack surface in the container and macros already cover command execution. -### 5.2 โŒ `disable_functions` cannot be "on by default" here โ€” measured +### 7.2 โŒ `disable_functions` cannot be "on by default" here โ€” measured Review asked for `php.ini` to disable `exec`, `shell_exec`, `passthru`, `system` by default. **This would break the editor's core function.** Evidence: @@ -285,18 +357,18 @@ turns the editor into a text editor that cannot commit. So it stays **a delibera you would also lose macros and git integration, which is why S-7 already says *"test, do not blind-apply"*. -### 5.3 Rotate the terminal password automatically +### 7.3 Rotate the terminal password automatically If the plugin is kept, `provision.sh` should generate a random password rather than leaving the upstream default. Not implemented: it would hardcode a secret into a repo file, which is the same class of problem this file already documents. -### 5.4 Deploy key could be read-only +### 7.4 Deploy key could be read-only Review suggested making the deploy key read-only if writes are unnecessary. **They are necessary** โ€” that is the whole point of the writer-branch workflow (macro 2 pushes). Not applicable. -### 5.5 The real upstream fix for ยง3 +### 7.5 The real upstream fix for ยง3 `class.macro.php::save()` should write `[]` instead of `"N/A"` for folder macros. See ยง3 โ€” the fix is currently applied to the data, so it survives updates, at the cost of being reversible from the UI. diff --git a/files/atheos.conf b/files/atheos.conf index fd4b03f..b5c20c4 100644 --- a/files/atheos.conf +++ b/files/atheos.conf @@ -7,13 +7,12 @@ server { root /var/www/atheos; index index.php index.html; + # --- logging --------------------------------------------------------- access_log /var/log/nginx/atheos.access.log; error_log /var/log/nginx/atheos.error.log; - # --- PHP ----------------------------------------------------------------- - # The socket path here MUST byte-match the pool's `listen =` in - # /etc/php83/php-fpm.d/www.conf - # (interface I-1 โ€” a mismatch yields a bare 502 with no other diagnostic). + # --- PHP: the socket path here MUST byte-match the pool's `listen =` + # in /etc/php83/php-fpm.d/www.conf (interface I-1 -> else bare 502) location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; @@ -27,11 +26,12 @@ server { try_files $uri $uri/ =404; } - # --- hardening ----------------------------------------------------------- - location ~ /\.git { deny all; } - location ~ /\.ssh { deny all; } - location ~ /\. { deny all; } - location ~ ^/data/ { deny all; } + # --- hardening (free, no downside) ----------------------------------- + # never serve the VCS directory, the app's private data, or dotfiles + location ~ /\.git { deny all; } + location ~ /\.ssh { deny all; } + location ~ /\. { deny all; } + location ~ ^/data/ { deny all; } location ~ ^/(config\.php|common\.php|controller\.php|traits/) { deny all; } location ~* \.(md|py|sh|log|conf|ini|sql|bak|orig)$ { deny all; } @@ -40,4 +40,13 @@ server { add_header Referrer-Policy "strict-origin-when-cross-origin" always; client_max_body_size 64m; + + # Atheos serves theme/main.min.css and the generated *.min.js/css bundles from STABLE URLs + # with no cache headers. Without this, a CSS/JS change stays invisible behind a cached copy + # until the user hard-refreshes. "no-cache" permits caching but forces revalidation, so a + # 304 keeps normal loads fast while guaranteeing updates are picked up on the next load. + location ~* \.(css|js)$ { + add_header Cache-Control "no-cache, must-revalidate"; + try_files $uri =404; + } } diff --git a/scripts/build-and-test.sh b/scripts/build-and-test.sh index 44b8b88..98db00b 100644 --- a/scripts/build-and-test.sh +++ b/scripts/build-and-test.sh @@ -106,11 +106,31 @@ for d in mysqld mariadbd postgres redis-server; do if command -v "$d" >/dev/null 2>&1; then bad "$d present - unexpected"; else ok "$d absent (correct)"; fi done +echo +echo "==============================================================" +echo "REGRESSION: panel has no hardcoded height (empty-modal bug)" +echo "==============================================================" +# panel { height:500px } forced every modal panel to a fixed box: short content left a large +# empty black region and anything after the panel (codegit's Branch toolbar) was pushed outside +# the modal. docs/known-issues.md ยง4. Fails loudly if lost to an update. +PNL=$(grep -oE '(^|[;}])panel\{[^}]*\}' /var/www/atheos/theme/main.min.css 2>/dev/null | head -1) +if [ -z "$PNL" ]; then + bad "could not read the panel rule from theme/main.min.css" +elif echo "$PNL" | grep -q 'height:500px'; then + bad "panel is fixed at 500px - re-run scripts/fix-panel-layout.sh" +else + ok "panel is content-sized ($(echo "$PNL" | grep -oE 'height:[^;]*' | head -1))" +fi +# and the CSS/JS cache header must be present, or fixes stay invisible +if wget -qO /dev/null --server-response --header='Host: 127.0.0.1' http://127.0.0.1/theme/main.min.css 2>&1 | grep -qi 'Cache-Control.*no-cache'; then + ok "theme CSS sends Cache-Control: no-cache (changes are picked up)" +else + bad "theme CSS has no Cache-Control - a CSS fix can stay hidden behind the browser cache" +fi echo echo "==============================================================" echo "REGRESSION: rDelete works (GLOB_BRACE is glibc-only; musl lacks it)" echo "==============================================================" -# traits/helpers.php::rDelete() used GLOB_BRACE, which is undefined under musl -> PHP 8 fatal. # That broke deleting ANY file/folder in the editor AND removing a market plugin (the UI # would spin forever because the fatal killed the response before Common::send()). # docs/known-issues.md ยง1. This test fails loudly if the patch is lost (e.g. by an update). diff --git a/scripts/fix-panel-layout.sh b/scripts/fix-panel-layout.sh new file mode 100644 index 0000000..bc4e753 --- /dev/null +++ b/scripts/fix-panel-layout.sh @@ -0,0 +1,76 @@ +#!/bin/sh +# Re-apply the UI/layout fixes to a rebuilt or updated Atheos tree. +# +# Why this exists: Atheos has no releases and `scripts/update-atheos.sh` replaces the whole +# application directory, so any local patch is lost on update. These are the two that were +# needed to make the editor usable. Both are IDEMPOTENT - run it as often as you like. +# +# 1. panel { height: 500px } -> height:auto; max-height:58vh +# Every modal panel was forced to a fixed 500px box, so dialogs with short content showed +# a large empty black region and anything after the panel (codegit's "Branch:" toolbar) +# was pushed OUTSIDE the modal. See docs/known-issues.md ยง4. +# +# 2. nginx: Cache-Control: no-cache for *.css/*.js +# Theme/bundle changes were invisible behind the browser cache. ยง5. +# +# Usage: sh scripts/fix-panel-layout.sh (run ON the guest, as root) +set -e + +ATH=/var/www/atheos +CSS=$ATH/theme/main.min.css +SCSS=$ATH/theme/modules/common/_panel.scss +NGX=/etc/nginx/http.d/atheos.conf + +echo "=== 1. panel layout ===" +OLD='height:500px;position:relative;border-left:var(--borderLight)' +NEW='height:auto;max-height:58vh;position:relative;border-left:var(--borderLight)' +if [ ! -f "$CSS" ]; then + echo " !! $CSS not found - is Atheos installed?" +elif grep -qF "$NEW" "$CSS"; then + echo " already applied" +elif grep -qF "$OLD" "$CSS"; then + [ -f "$CSS.orig" ] || cp -a "$CSS" "$CSS.orig" + php83 -r '$f=$argv[1];$o=$argv[2];$n=$argv[3];$s=file_get_contents($f); + if(strpos($s,$o)===false){exit(1);} file_put_contents($f,str_replace($o,$n,$s));' "$CSS" "$OLD" "$NEW" + echo " applied to main.min.css" +else + echo " !! expected panel rule not found - inspect manually" + echo " (upstream may have changed it; diff against $CSS.orig)" +fi + +# keep the SCSS source consistent so the shipped CSS is not contradicted by the source +if [ -f "$SCSS" ] && grep -q 'height: 500px;' "$SCSS"; then + [ -f "$SCSS.orig" ] || cp -a "$SCSS" "$SCSS.orig" + php83 -r '$f=$argv[1];$s=file_get_contents($f); + $s=str_replace("height: 500px;","height: auto;\n\tmax-height: 58vh;",$s); + file_put_contents($f,$s);' "$SCSS" + echo " synchronised _panel.scss" +fi + +echo "=== 2. nginx cache headers ===" +if [ ! -f "$NGX" ]; then + echo " !! $NGX not found" +elif grep -q 'must-revalidate' "$NGX"; then + echo " already applied" +else + [ -f "$NGX.orig" ] || cp -a "$NGX" "$NGX.orig" + cat > /tmp/.cacheblock <<'EOF' + + # Atheos serves theme/main.min.css and the generated *.min.js/css bundles from STABLE URLs + # with no cache headers, so a CSS/JS fix stays invisible behind the browser cache until a + # hard refresh. "no-cache" permits caching but forces revalidation: normal loads still get a + # cheap 304, and a change is guaranteed to be picked up on the next load. + location ~* \.(css|js)$ { + add_header Cache-Control "no-cache, must-revalidate"; + try_files $uri =404; + } +EOF + php83 -r '$f=$argv[1];$b=$argv[2];$s=file_get_contents($f); + $p=strrpos($s,"}"); if($p===false){exit(1);} + file_put_contents($f,substr($s,0,$p).file_get_contents($b).substr($s,$p));' "$NGX" /tmp/.cacheblock + rm -f /tmp/.cacheblock + nginx -t >/dev/null 2>&1 && rc-service nginx reload >/dev/null 2>&1 && echo " applied + nginx reloaded" \ + || echo " !! nginx -t failed - restore $NGX.orig" +fi + +echo "=== done ===" -- 2.39.5