shadok-ai 0.9.18 → 0.9.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/public/index.html +35 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shadok-ai",
3
- "version": "0.9.18",
3
+ "version": "0.9.20",
4
4
  "main": "dist/session.js",
5
5
  "scripts": {
6
6
  "test": "node --import tsx --test test/*.ts context/*/test/*.test.mjs",
package/public/index.html CHANGED
@@ -3377,12 +3377,45 @@
3377
3377
  * end down = negative angle) and the ball dips with the effort — shared by the
3378
3378
  * favicon and the in-page indicator so both pump the same way. */
3379
3379
  function pumpPose(p) { return { lever: 13 - p * 28, bob: +(p * 1.5).toFixed(2) }; }
3380
- /** The "working" favicon: the Shadok pumping its lever. `phase` rides a cycle. */
3380
+ /** Blend `t` of `b` into `a`, both 6-digit hex. Every palette colour is written
3381
+ * that way on purpose (see the theme blocks), so this needs no colour parser —
3382
+ * but anything else RETURNS `a` rather than producing `#NaNNaNNaN`, which is
3383
+ * an invalid fill and would take the whole favicon down silently. */
3384
+ const HEX6 = /^#[0-9a-f]{6}$/i;
3385
+ function mixHex(a, b, t) {
3386
+ if (!HEX6.test(a) || !HEX6.test(b)) return a;
3387
+ const ch = (c, i) => parseInt(c.slice(1 + i * 2, 3 + i * 2), 16);
3388
+ const out = [0, 1, 2].map((i) => Math.round(ch(a, i) + (ch(b, i) - ch(a, i)) * t));
3389
+ return "#" + out.map((v) => v.toString(16).padStart(2, "0")).join("");
3390
+ }
3391
+ /** The "working" favicon: the Shadok pumping its lever, on a tile that BREATHES
3392
+ * with the stroke. `phase` rides a cycle.
3393
+ *
3394
+ * The breathing is the part that survives. The pump is line work: the lever
3395
+ * swings 28° about a 5px radius and the body dips 1.5 — roughly 1.2 and 0.75
3396
+ * real pixels at the 16px a tab actually paints, i.e. under the threshold of a
3397
+ * rendered pixel. Idle and mid-stroke were pixel-identical there, so the whole
3398
+ * animation existed only for someone who zoomed in. Moving the GROUND moves
3399
+ * every pixel of the icon at once, which is the only thing that reads at that
3400
+ * size — the same lesson as the waiting glyph. The Shadok keeps pumping on top
3401
+ * for whoever looks closely. */
3381
3402
  const PUMP_FRAMES = 12;
3403
+ /** How far the ground lifts at full stroke, toward `--text-dim`.
3404
+ *
3405
+ * Toward the DIM TEXT colour, not toward the accent, and that is the whole
3406
+ * point: a waiting state is now a full tile of red or amber, so a ground
3407
+ * drifting far enough toward the accent to be noticed also starts to read as
3408
+ * "unread". Lifting into the neutral family is unmistakable at 16px and
3409
+ * cannot be confused with either. 0.7 sounds large because the stroke rides a
3410
+ * sine and barely rests at the peak — at 0.26 toward the accent, the swing
3411
+ * was there in a still and invisible in a tab. */
3412
+ const BREATHE = 0.7;
3382
3413
  function faviconPumpingSVG(phase) {
3383
3414
  const accent = themeColor("--amber", "#f0a848");
3415
+ const base = themeColor("--bg", "#14161d");
3416
+ const lift = themeColor("--text-dim", "#7d8293");
3384
3417
  const p = (Math.sin((phase / PUMP_FRAMES) * 2 * Math.PI - Math.PI / 2) + 1) / 2; // 0..1..0
3385
- return faviconWrap(shadokInner(accent, pumpPose(p)));
3418
+ return faviconWrap(shadokInner(accent, pumpPose(p)), mixHex(base, lift, p * BREATHE));
3386
3419
  }
3387
3420
  /** What each channel is asking for, as the pure module wants it. */
3388
3421
  function attentionInput() {