Files
logseq/scripts/build_color_dicts.bb
scheinriese 50b3cdfbc9 fix: asset deletion lifecycle + PR-review cleanup
Asset deletion (three independent failures stacked):
- Unlink file on asset-block retract via the existing <unlink-asset
  helper at handler/assets.cljs (modules/outliner/pipeline.cljs:74-80).
  The block was commented out; deleted-assets was computed but never
  consumed, so files leaked indefinitely.
- Reactive existence check in image-icon-cp / avatar-image-cp via
  state/sub :db/latest-transacted-entity-uuids. model/sub-block can't
  drive this — worker/react.cljs:63-67 calls (d/entity db-after id),
  which returns nil for retracted entities, so [::block id] is never
  emitted in affected-keys and subscriptions never fire on deletion.
- Force-remount Avatar root via :key on asset-missing? toggle. Radix's
  Avatar primitive tracks image-loading status in context; once
  AvatarImage reported 'loaded', the status sticks even after the image
  unmounts, so AvatarFallback stays hidden.

PR-review cleanup (no behavior change):
- Strip 17+ [DEBUG ...] console statements across icon, block, views,
  two editor handlers, and assets.
- Close content.cljs preview gap: pass :preview-target-db-id +
  :property to icon-search so the block bullet observes hover preview
  consistently with every other surface.
- Scope-correct close-fallback-menu! — use dissoc-icon-preview-field!
  instead of unconditional state/set-state! nil.
- Delete dead code: heal-dangling-asset-icon (raced lazy hydrate),
  asset-uuid->entity, rgb-string->hex, name->hex (named + css),
  format-pairs.
- Hoist icon-grid-cols / custom-tab-cols magic numbers (9 sites).
- Search inputs: add :type "search" and :aria-label (both picker +
  asset-picker).
- Replace hardcoded #6B7280 with (colors/variable :gray :09).
- Reconcile recents-cap doc drift in storage.cljc + recents-lane
  docstring (cap lives in handler/icon-color/max-recents).
- Remove orphan-asset cleanup from outliner/core save-block-inner. Root
  cause (migration 65.27's :url ref-type) is fixed by 65.28/65.29; all
  asset writers are atomic (save-image-asset!, new-asset-block,
  build-new-asset), and the cleanup ran on every save with a silent-
  retract failure mode.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 16:28:06 +02:00

121 lines
3.9 KiB
Clojure
Executable File

#!/usr/bin/env bb
;; build_color_dicts.bb
;;
;; Downloads the XKCD color survey (`https://xkcd.com/color/rgb.txt`) and
;; emits `src/main/frontend/colors/named.cljs` as a deterministic, hermetic
;; CLJS source file containing the dataset as a `def` map literal.
;;
;; The XKCD dataset is licensed CC0 (public domain). It hasn't changed
;; since 2010. We embed it at build time so the bundle works fully offline.
;;
;; Usage: bb scripts/build_color_dicts.bb
;;
;; Idempotent: rerunning produces byte-identical output.
(ns build-color-dicts
(:require [babashka.curl :as curl]
[babashka.fs :as fs]
[clojure.java.io :as io]
[clojure.string :as str]))
(def ^:private xkcd-url "https://xkcd.com/color/rgb.txt")
(def ^:private cache-path "/tmp/xkcd_rgb.txt")
(def ^:private repo-root
(-> *file* fs/parent fs/parent str))
(def ^:private out-path
(str repo-root "/src/main/frontend/colors/named.cljs"))
(defn- fetch!
"Return the raw rgb.txt body. Caches at /tmp/xkcd_rgb.txt to avoid hammering xkcd.com."
[]
(if (fs/exists? cache-path)
(slurp cache-path)
(let [body (:body (curl/get xkcd-url))]
(spit cache-path body)
body)))
(defn- parse-line
"Parse one entry line. Returns [name hex] or nil for the license comment."
[line]
(when-not (or (str/blank? line)
(str/starts-with? line "#"))
(let [[raw-name raw-hex] (str/split line #"\t")]
(when (and raw-name raw-hex)
(let [nm (str/lower-case (str/trim raw-name))
hx (-> raw-hex str/trim (str/replace #"^#" "") str/lower-case)]
(when (and (re-matches #"[0-9a-f]{6}" hx)
(seq nm))
[nm hx]))))))
(defn- parse-all [body]
(->> (str/split-lines body)
(keep parse-line)
(into (sorted-map))))
(def ^:private file-template
"(ns frontend.colors.named
\"XKCD color survey dataset (~949 colors).
Public domain (CC0): https://xkcd.com/color/rgb.txt
Generated by scripts/build_color_dicts.bb -- do not edit by hand.\")
(def ^:export named-colors
\"Map of normalized lowercase color name -> 6-char hex (no leading '#').
Names with spaces are stored as-is; the resolver normalizes input the
same way before lookup.\"
{%PAIRS%})
(def ^:private by-hex
(into {} (map (fn [[n h]] [h n])) named-colors))
(defn name->hex
\"Look up a normalized color name (lowercase, single-spaced). Returns
6-char hex without '#' or nil.\"
[s]
(get named-colors s))
(defn hex->name
\"Reverse lookup: 6-char lowercase hex (no '#') -> canonical name or nil.\"
[h]
(get by-hex h))
")
(defn- render [pairs]
;; The opening `{` sits flush at column 3 (after ` `). We render the
;; first pair on the same line as the brace and subsequent pairs at the
;; same column, matching the spec example formatting.
(let [lines (->> pairs
(map (fn [[nm hx]]
(str (pr-str nm) " " (pr-str hx)))))
indent " "
joined (->> (rest lines)
(map #(str indent %))
(cons (first lines))
(str/join "\n"))]
(str/replace file-template "%PAIRS%" joined)))
(defn- gzip-size
"Estimate gzipped size of a string in bytes."
[s]
(let [bs (.getBytes ^String s "UTF-8")
baos (java.io.ByteArrayOutputStream.)]
(with-open [gz (java.util.zip.GZIPOutputStream. baos)]
(.write gz bs))
(count (.toByteArray baos))))
(defn -main [& _]
(let [body (fetch!)
pairs (parse-all body)
out (render pairs)]
(fs/create-dirs (fs/parent out-path))
(spit out-path out)
(let [raw-bytes (count (.getBytes ^String out "UTF-8"))]
(println "wrote" out-path)
(println " entries: " (count pairs))
(println " raw size: " raw-bytes "bytes")
(println " gzipped est:" (gzip-size out) "bytes"))))
(when (= *file* (System/getProperty "babashka.file"))
(apply -main *command-line-args*))