Fix calendar activity heatmap in preview and generation
This commit is contained in:
@@ -592,9 +592,6 @@ fn serve_scoped_file(
|
||||
let Some(relative) = path.strip_prefix(prefix) else {
|
||||
return Ok(None);
|
||||
};
|
||||
let scope_root = data_dir.join(scope_dir);
|
||||
let candidate = scope_root.join(relative);
|
||||
if !candidate.exists() || !candidate.is_file() {
|
||||
let bundled_path = format!("{scope_dir}/{relative}");
|
||||
if let Some(bytes) = crate::engine::site_assets::bundled_site_asset(&bundled_path) {
|
||||
let mime = guess_content_type(Path::new(&bundled_path));
|
||||
@@ -602,6 +599,9 @@ fn serve_scoped_file(
|
||||
(StatusCode::OK, [(header::CONTENT_TYPE, mime)], bytes).into_response(),
|
||||
));
|
||||
}
|
||||
let scope_root = data_dir.join(scope_dir);
|
||||
let candidate = scope_root.join(relative);
|
||||
if !candidate.exists() || !candidate.is_file() {
|
||||
return Ok(Some(error_response(
|
||||
StatusCode::NOT_FOUND,
|
||||
"preview asset not found",
|
||||
@@ -1167,6 +1167,11 @@ mod tests {
|
||||
std::fs::create_dir_all(dir.path().join("html/pagefind")).unwrap();
|
||||
std::fs::write(dir.path().join("media/ok.txt"), "ok").unwrap();
|
||||
std::fs::write(dir.path().join("assets/site.css"), "body { color: red; }").unwrap();
|
||||
std::fs::write(
|
||||
dir.path().join("assets/calendar-runtime.js"),
|
||||
"window.staleCalendarRuntime = true;",
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::write(dir.path().join("images/custom.svg"), "<svg></svg>").unwrap();
|
||||
std::fs::write(
|
||||
dir.path().join("html/pagefind/pagefind-ui.js"),
|
||||
@@ -1194,6 +1199,14 @@ mod tests {
|
||||
.unwrap();
|
||||
let media_body = media.text().unwrap();
|
||||
let asset_body = asset.text().unwrap();
|
||||
let calendar_runtime_body = client
|
||||
.get(format!(
|
||||
"http://{PREVIEW_HOST}:{PREVIEW_PORT}/assets/calendar-runtime.js"
|
||||
))
|
||||
.send()
|
||||
.unwrap()
|
||||
.text()
|
||||
.unwrap();
|
||||
let image_body = client
|
||||
.get(format!(
|
||||
"http://{PREVIEW_HOST}:{PREVIEW_PORT}/images/custom.svg"
|
||||
@@ -1214,6 +1227,8 @@ mod tests {
|
||||
|
||||
assert_eq!(media_body, "ok");
|
||||
assert!(asset_body.contains("color: red"));
|
||||
assert!(calendar_runtime_body.contains("--blog-calendar-heat-strength"));
|
||||
assert!(!calendar_runtime_body.contains("staleCalendarRuntime"));
|
||||
assert!(image_body.contains("<svg>"));
|
||||
assert!(pagefind_body.contains("window.pagefind"));
|
||||
}
|
||||
|
||||
@@ -278,3 +278,30 @@ pub(crate) fn write_bundled_site_assets(
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::bundled_site_asset;
|
||||
|
||||
fn bundled_text(path: &str) -> &'static str {
|
||||
std::str::from_utf8(bundled_site_asset(path).unwrap()).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn calendar_heatmap_marks_only_populated_periods_with_one_accent() {
|
||||
let runtime = bundled_text("assets/calendar-runtime.js");
|
||||
let styles = bundled_text("assets/bds.css");
|
||||
|
||||
assert!(runtime.contains("--blog-calendar-heat-strength"));
|
||||
assert!(!runtime.contains("--blog-calendar-heat-hue"));
|
||||
assert!(styles.contains(
|
||||
"[data-vc-date]:not([data-blog-calendar-has-posts='true']) [data-vc-date-btn]"
|
||||
));
|
||||
assert!(styles.contains("background-color: transparent !important;"));
|
||||
assert!(styles.contains("border-color: transparent !important;"));
|
||||
assert!(styles.contains("color-mix(in srgb, var(--pico-primary"));
|
||||
assert!(styles.contains("var(--blog-calendar-heat-strength"));
|
||||
assert!(styles.contains("transparent) !important;"));
|
||||
assert!(!styles.contains("hsl(var(--blog-calendar-heat-hue"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,14 +49,20 @@ main { display: grid; gap: 1rem; }
|
||||
[data-blog-calendar-root] [data-vc-week-day] { font-size: .68rem; line-height: .9rem; min-width: 1.45rem; }
|
||||
[data-blog-calendar-root] [data-vc-date] { padding-top: 0; padding-bottom: 0; }
|
||||
[data-blog-calendar-root] [data-vc-date-btn] { min-height: 1.45rem; min-width: 1.45rem; font-size: .68rem; line-height: .9rem; }
|
||||
[data-blog-calendar-has-posts='true'] [data-vc-date-btn] {
|
||||
border-color: hsl(var(--blog-calendar-heat-hue, 210) 85% 42% / .95);
|
||||
background-color: hsl(var(--blog-calendar-heat-hue, 210) 88% 52% / var(--blog-calendar-heat-alpha, 0));
|
||||
[data-blog-calendar-root] [data-vc-date]:not([data-blog-calendar-has-posts='true']) [data-vc-date-btn],
|
||||
[data-blog-calendar-root] [data-vc-months-month]:not([data-blog-calendar-has-posts='true']),
|
||||
[data-blog-calendar-root] [data-vc-years-year]:not([data-blog-calendar-has-posts='true']) {
|
||||
background-color: transparent !important;
|
||||
border-color: transparent !important;
|
||||
}
|
||||
[data-blog-calendar-root] [data-vc-date][data-blog-calendar-has-posts='true'] [data-vc-date-btn] {
|
||||
background-color: color-mix(in srgb, var(--pico-primary, #0172ad) var(--blog-calendar-heat-strength, 0%), transparent) !important;
|
||||
border-color: var(--pico-primary, #0172ad) !important;
|
||||
}
|
||||
[data-blog-calendar-root] [data-vc-months-month][data-blog-calendar-has-posts='true'],
|
||||
[data-blog-calendar-root] [data-vc-years-year][data-blog-calendar-has-posts='true'] {
|
||||
background-color: hsl(var(--blog-calendar-heat-hue, 210) 88% 52% / var(--blog-calendar-heat-alpha, 0));
|
||||
border-color: hsl(var(--blog-calendar-heat-hue, 210) 85% 42% / .95);
|
||||
background-color: color-mix(in srgb, var(--pico-primary, #0172ad) var(--blog-calendar-heat-strength, 0%), transparent) !important;
|
||||
border-color: var(--pico-primary, #0172ad) !important;
|
||||
}
|
||||
.post { border: 1px solid var(--pico-muted-border-color, var(--muted-border-color)); padding: 1rem; background: var(--pico-card-background-color, var(--card-background-color)); min-width: 0; }
|
||||
.post pre { position: relative; overflow-x: auto; max-width: 100%; border: 1px solid var(--pico-muted-border-color, var(--muted-border-color)); border-radius: .3rem; margin: .9rem 0; padding: .85rem .9rem; background: var(--pico-code-background-color, rgba(33, 38, 45, .82)); box-sizing: border-box; }
|
||||
|
||||
@@ -55,17 +55,14 @@
|
||||
|
||||
function applyHeatStyle(target, count, maxCount) {
|
||||
if (!(target instanceof HTMLElement) || !Number.isFinite(count) || count <= 0 || !Number.isFinite(maxCount) || maxCount <= 0) {
|
||||
target?.style?.setProperty('--blog-calendar-heat-alpha', '0');
|
||||
target?.style?.setProperty('--blog-calendar-heat-hue', '210');
|
||||
target?.style?.setProperty('--blog-calendar-heat-strength', '0%');
|
||||
return;
|
||||
}
|
||||
|
||||
const normalized = Math.min(1, count / maxCount);
|
||||
const hue = Math.round(210 - (210 * normalized));
|
||||
const alpha = (0.30 + normalized * 0.65).toFixed(3);
|
||||
const strength = Math.round(30 + normalized * 60);
|
||||
|
||||
target.style.setProperty('--blog-calendar-heat-hue', String(hue));
|
||||
target.style.setProperty('--blog-calendar-heat-alpha', alpha);
|
||||
target.style.setProperty('--blog-calendar-heat-strength', String(strength) + '%');
|
||||
}
|
||||
|
||||
function navigateTo(pathname) {
|
||||
|
||||
@@ -156,6 +156,12 @@ invariant CompleteStarterNavigation {
|
||||
-- calendar entry, and provides localized search empty-result text.
|
||||
}
|
||||
|
||||
invariant CalendarActivityStyling {
|
||||
-- Calendar days, months, and years with no posts are visually unmarked.
|
||||
-- Populated periods use one theme-compatible accent whose intensity grows
|
||||
-- with the relative post count; count differences never change its hue.
|
||||
}
|
||||
|
||||
invariant LabelsUseContentLanguage {
|
||||
-- RenderLabels and the i18n filter resolve against the content/render
|
||||
-- language, consistent with i18n.allium's split-localization rule.
|
||||
|
||||
Reference in New Issue
Block a user