@omerdlw/base-framework 2.0.0 → 3.0.0

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 (101) hide show
  1. package/README.md +4 -1
  2. package/dist/{chunk-XASQT4CQ.js → chunk-66EYJBIP.js} +47 -27
  3. package/dist/chunk-66EYJBIP.js.map +1 -0
  4. package/dist/{chunk-65ZB4QTQ.js → chunk-CYU6SGAL.js} +3 -3
  5. package/dist/{chunk-65ZB4QTQ.js.map → chunk-CYU6SGAL.js.map} +1 -1
  6. package/dist/{chunk-6SG7V77J.js → chunk-HHWRTQKQ.js} +100 -67
  7. package/dist/chunk-HHWRTQKQ.js.map +1 -0
  8. package/dist/chunk-HLL5EHXC.js +485 -0
  9. package/dist/chunk-HLL5EHXC.js.map +1 -0
  10. package/dist/{chunk-3MSK4XGK.js → chunk-JIXBFFRD.js} +258 -405
  11. package/dist/chunk-JIXBFFRD.js.map +1 -0
  12. package/dist/{chunk-KKQT46TP.js → chunk-M57SNWXK.js} +8 -6
  13. package/dist/chunk-M57SNWXK.js.map +1 -0
  14. package/dist/chunk-RIOKLMYM.js +145 -0
  15. package/dist/chunk-RIOKLMYM.js.map +1 -0
  16. package/dist/{chunk-QXR55VVS.js → chunk-TG6NMHXI.js} +241 -162
  17. package/dist/chunk-TG6NMHXI.js.map +1 -0
  18. package/dist/index.js +7 -5
  19. package/dist/modules/ambient/analyze.d.ts +18 -3
  20. package/dist/modules/ambient/analyze.d.ts.map +1 -1
  21. package/dist/modules/ambient/dom.d.ts +14 -1
  22. package/dist/modules/ambient/dom.d.ts.map +1 -1
  23. package/dist/modules/ambient/hooks.d.ts.map +1 -1
  24. package/dist/modules/ambient/index.d.ts +2 -2
  25. package/dist/modules/ambient/index.d.ts.map +1 -1
  26. package/dist/modules/ambient/index.js +1 -1
  27. package/dist/modules/ambient/server/index.d.ts +1 -1
  28. package/dist/modules/ambient/server/index.d.ts.map +1 -1
  29. package/dist/modules/ambient/server/index.js +2 -2
  30. package/dist/modules/ambient/server/index.js.map +1 -1
  31. package/dist/modules/ambient/settings.d.ts +1 -0
  32. package/dist/modules/ambient/settings.d.ts.map +1 -1
  33. package/dist/modules/ambient/types.d.ts +13 -0
  34. package/dist/modules/ambient/types.d.ts.map +1 -1
  35. package/dist/modules/ambient/worker/index.js +1 -1
  36. package/dist/modules/context-menu/index.js +4 -3
  37. package/dist/modules/dock/cards.d.ts +3 -0
  38. package/dist/modules/dock/cards.d.ts.map +1 -1
  39. package/dist/modules/dock/constants.d.ts +1 -20
  40. package/dist/modules/dock/constants.d.ts.map +1 -1
  41. package/dist/modules/dock/index.js +3 -2
  42. package/dist/modules/dock/layout.d.ts.map +1 -1
  43. package/dist/modules/dock/motion.d.ts +6 -0
  44. package/dist/modules/dock/motion.d.ts.map +1 -1
  45. package/dist/modules/dock/overlay.d.ts +1 -1
  46. package/dist/modules/dock/overlay.d.ts.map +1 -1
  47. package/dist/modules/dock/routing/guards.d.ts +1 -1
  48. package/dist/modules/dock/routing/navigation.d.ts +7 -4
  49. package/dist/modules/dock/routing/navigation.d.ts.map +1 -1
  50. package/dist/modules/dock/runtime/display.d.ts +31 -2
  51. package/dist/modules/dock/runtime/display.d.ts.map +1 -1
  52. package/dist/modules/dock/runtime/dock.d.ts +2 -2
  53. package/dist/modules/dock/runtime/dock.d.ts.map +1 -1
  54. package/dist/modules/dock/surface/view-model.d.ts.map +1 -1
  55. package/dist/modules/dock/types.d.ts +9 -27
  56. package/dist/modules/dock/types.d.ts.map +1 -1
  57. package/dist/modules/index.d.ts +3 -2
  58. package/dist/modules/index.d.ts.map +1 -1
  59. package/dist/modules/index.js +7 -5
  60. package/dist/modules/loading/context.d.ts.map +1 -1
  61. package/dist/modules/loading/index.js +1 -1
  62. package/dist/modules/navigation/constants.d.ts +37 -0
  63. package/dist/modules/navigation/constants.d.ts.map +1 -0
  64. package/dist/modules/navigation/context.d.ts +10 -0
  65. package/dist/modules/navigation/context.d.ts.map +1 -0
  66. package/dist/modules/navigation/hooks.d.ts +29 -0
  67. package/dist/modules/navigation/hooks.d.ts.map +1 -0
  68. package/dist/modules/navigation/index.d.ts +10 -0
  69. package/dist/modules/navigation/index.d.ts.map +1 -0
  70. package/dist/modules/navigation/index.js +81 -0
  71. package/dist/modules/navigation/index.js.map +1 -0
  72. package/dist/modules/navigation/listeners.d.ts +15 -0
  73. package/dist/modules/navigation/listeners.d.ts.map +1 -0
  74. package/dist/modules/navigation/module.d.ts +25 -0
  75. package/dist/modules/navigation/module.d.ts.map +1 -0
  76. package/dist/modules/navigation/navigator.d.ts +8 -0
  77. package/dist/modules/navigation/navigator.d.ts.map +1 -0
  78. package/dist/modules/navigation/settings.d.ts +10 -0
  79. package/dist/modules/navigation/settings.d.ts.map +1 -0
  80. package/dist/modules/navigation/state.d.ts +20 -0
  81. package/dist/modules/navigation/state.d.ts.map +1 -0
  82. package/dist/modules/navigation/types.d.ts +168 -0
  83. package/dist/modules/navigation/types.d.ts.map +1 -0
  84. package/dist/modules/navigation/utils.d.ts +11 -0
  85. package/dist/modules/navigation/utils.d.ts.map +1 -0
  86. package/docs/getting-started.md +2 -0
  87. package/docs/modules/README.md +5 -1
  88. package/docs/modules/ambient.md +9 -6
  89. package/docs/modules/dock.md +13 -11
  90. package/docs/modules/loading.md +2 -2
  91. package/docs/modules/navigation.md +216 -0
  92. package/docs/proposals/navigation.md +209 -0
  93. package/package.json +6 -1
  94. package/templates/AGENTS.md +2 -0
  95. package/dist/chunk-3MSK4XGK.js.map +0 -1
  96. package/dist/chunk-6SG7V77J.js.map +0 -1
  97. package/dist/chunk-KKQT46TP.js.map +0 -1
  98. package/dist/chunk-QXR55VVS.js.map +0 -1
  99. package/dist/chunk-XASQT4CQ.js.map +0 -1
  100. package/dist/modules/dock/routing/transactions.d.ts +0 -21
  101. package/dist/modules/dock/routing/transactions.d.ts.map +0 -1
@@ -130,10 +130,12 @@ var DEFAULT_CSS_VAR_NAMES = Object.freeze({
130
130
  tone: "--ambient-{role}-{tone}"
131
131
  });
132
132
  var DEFAULT_PROXY = "/api/ambient/proxy?url=";
133
+ var DEFAULT_LINGER_MS = 3e3;
133
134
  var createDefaults = () => ({
134
135
  analysis: { ...DEFAULT_ANALYSIS },
135
136
  cacheLimit: 100,
136
137
  cssVars: { ...DEFAULT_CSS_VAR_NAMES, tones: AMBIENT_TONES },
138
+ linger: DEFAULT_LINGER_MS,
137
139
  proxy: DEFAULT_PROXY,
138
140
  strategy: "auto",
139
141
  worker: null
@@ -148,6 +150,7 @@ function mergeAmbientSettings(base, input) {
148
150
  analysis: { ...base.analysis, ...input.analysis },
149
151
  cacheLimit: input.cacheLimit ?? base.cacheLimit,
150
152
  cssVars: { ...base.cssVars, ...input.cssVars },
153
+ linger: input.linger ?? base.linger,
151
154
  proxy: input.proxy === void 0 ? base.proxy : input.proxy,
152
155
  strategy: input.strategy ?? base.strategy,
153
156
  worker: input.worker === void 0 ? base.worker : input.worker
@@ -238,6 +241,7 @@ function kmeans(points, weights, requested, { iterations = 16, lightnessWeight =
238
241
  }
239
242
 
240
243
  // src/modules/ambient/analyze.ts
244
+ var AMBIENT_ANALYSIS_VERSION = 2;
241
245
  var MIN_CHROMA = 0.022;
242
246
  var MIN_PRESENCE = 0.03;
243
247
  var MIN_POPULATION = 4e-3;
@@ -294,26 +298,47 @@ function pickBest(items, score) {
294
298
  return best;
295
299
  }
296
300
  var round3 = (value) => Math.round(value * 1e3) / 1e3;
297
- function buildSwatch(source, { cap, max, min, rel }, fallback, fromImage, minContrast) {
298
- const h = source?.h ?? fallback.h;
299
- const l = clamp(source?.l ?? fallback.l, min, max);
300
- const tone = toneOf(h, l, rel, cap);
301
+ function skinLikeness({ c, h, l }) {
302
+ return (1 - smoothstep(12, 26, hueDistance(h, 54))) * smoothstep(0.02, 0.035, c) * (1 - smoothstep(0.11, 0.15, c)) * smoothstep(0.26, 0.38, l) * (1 - smoothstep(0.88, 0.95, l));
303
+ }
304
+ var warmth = (h) => 1 - smoothstep(22, 50, hueDistance(h, 42));
305
+ var salience = (color) => 1 - 0.65 * skinLikeness(color);
306
+ var roleChroma = (source, { cap, gain }) => Math.min(
307
+ cap * (1 - 0.3 * warmth(source.h)),
308
+ // A faint tint is raised further, so it still reads as a colour.
309
+ Math.max(source.c * gain, Math.min(0.05, source.c * 1.6)) * (1 - 0.3 * skinLikeness(source))
310
+ );
311
+ var fitTone = (h, l, c) => ({
312
+ c: Math.max(0, Math.min(c, maxChroma(l, h) * 0.96)),
313
+ h,
314
+ l
315
+ });
316
+ function buildSwatch(tone, population, fromImage, minContrast) {
317
+ const room = maxChroma(tone.l, tone.h) * 0.96;
318
+ const rel = room > 0 ? tone.c / room : 0;
301
319
  const ramp = {};
302
320
  for (const step of AMBIENT_TONES) {
303
- ramp[step] = formatOklch(toneOf(h, step / 100, rel, cap));
321
+ ramp[step] = formatOklch(toneOf(tone.h, step / 100, rel, Math.max(tone.c, 0.04)));
304
322
  }
305
323
  return {
306
324
  c: round3(tone.c),
307
325
  color: formatOklch(tone),
308
326
  fromImage,
309
- h: Math.round(h * 10) / 10,
327
+ h: Math.round(tone.h * 10) / 10,
310
328
  l: round3(tone.l),
311
329
  on: formatOklch(onColorFor(tone, minContrast)),
312
- population: round3(source?.pop ?? 0),
330
+ population: round3(population),
313
331
  ramp
314
332
  };
315
333
  }
316
- var lift = (rel, base, gain, ceiling) => clamp(base + gain * rel, 0, ceiling);
334
+ function roleTone(source, spec, fallback, dilution = 1) {
335
+ if (!source) return fitTone(fallback.h, clamp(fallback.l, spec.min, spec.max), fallback.c);
336
+ return fitTone(
337
+ source.h,
338
+ clamp(source.l, spec.min, spec.max),
339
+ roleChroma(source, spec) * dilution
340
+ );
341
+ }
317
342
  function clusterAmbientPixels(pixels, options) {
318
343
  const { data, width, height } = pixels;
319
344
  const bounds = options.letterbox ? findContentBounds(pixels) : { bottom: height, left: 0, right: width, top: 0 };
@@ -385,18 +410,23 @@ function clusterAmbientPixels(pixels, options) {
385
410
  points[i * 3 + 2] = b;
386
411
  weights[i] = w * (1 + 5 * Math.sqrt(a * a + b * b));
387
412
  });
388
- const { assignment, centroids, k } = kmeans(points, weights, budgets[group]);
389
- const pop = new Float64Array(k);
390
- members.forEach((bin, i) => pop[assignment[i]] += binSums[bin * 4 + 3]);
413
+ const { assignment, k } = kmeans(points, weights, budgets[group]);
414
+ const sums = new Float64Array(k * 4);
415
+ members.forEach((bin, i) => {
416
+ for (let channel = 0; channel < 4; channel++) {
417
+ sums[assignment[i] * 4 + channel] += binSums[bin * 4 + channel];
418
+ }
419
+ });
391
420
  const index = new Int32Array(k).fill(-1);
392
421
  for (let j = 0; j < k; j++) {
393
- if (pop[j] <= 0) continue;
394
- const lch = labToLch([centroids[j * 3], centroids[j * 3 + 1], centroids[j * 3 + 2]]);
422
+ const pop = sums[j * 4 + 3];
423
+ if (pop <= 0) continue;
424
+ const lch = labToLch([sums[j * 4] / pop, sums[j * 4 + 1] / pop, sums[j * 4 + 2] / pop]);
395
425
  const room = maxChroma(lch.l, lch.h);
396
426
  index[j] = clusters.length;
397
427
  clusters.push({
398
428
  ...lch,
399
- pop: pop[j] / total,
429
+ pop: pop / total,
400
430
  rel: room > 0 ? clamp(lch.c / room, 0, 1) : 0,
401
431
  rely: reliability(lch.l)
402
432
  });
@@ -404,20 +434,26 @@ function clusterAmbientPixels(pixels, options) {
404
434
  members.forEach((bin, i) => clusterOfBin[bin] = index[assignment[i]]);
405
435
  });
406
436
  const regionWeights = AMBIENT_REGIONS.map(() => new Float64Array(clusters.length));
437
+ const regionLab = AMBIENT_REGIONS.map(() => new Float64Array(clusters.length * 3));
407
438
  for (let i = 0; i < pixelBins.length; i++) {
408
439
  const bin = pixelBins[i];
409
440
  if (bin < 0) continue;
410
441
  const cluster = clusterOfBin[bin];
411
442
  const bits = pixelRegions[i];
443
+ const w = binSums[bin * 4 + 3];
412
444
  AMBIENT_REGIONS.forEach((region, r) => {
413
- if (bits & REGION_BITS[region]) regionWeights[r][cluster] += 1;
445
+ if (!(bits & REGION_BITS[region])) return;
446
+ regionWeights[r][cluster] += 1;
447
+ for (let channel = 0; channel < 3; channel++) {
448
+ regionLab[r][cluster * 3 + channel] += binSums[bin * 4 + channel] / w;
449
+ }
414
450
  });
415
451
  }
416
- return { clusters, meanL, regionWeights };
452
+ return { clusters, meanL, regionLab, regionWeights };
417
453
  }
418
454
  function analyzeAmbientPixels(pixels, overrides = {}) {
419
455
  const options = { ...DEFAULT_ANALYSIS, ...overrides };
420
- const { clusters, meanL, regionWeights } = clusterAmbientPixels(pixels, options);
456
+ const { clusters, meanL, regionLab, regionWeights } = clusterAmbientPixels(pixels, options);
421
457
  const chromatic = clusters.filter(isChromatic);
422
458
  const presence = chromatic.reduce(
423
459
  (sum, cluster) => sum + cluster.pop * colorfulness(cluster.c) * cluster.rely,
@@ -426,7 +462,7 @@ function analyzeAmbientPixels(pixels, overrides = {}) {
426
462
  const neutral = presence < MIN_PRESENCE;
427
463
  const dilution = 0.55 + 0.45 * Math.min(1, presence / 0.25);
428
464
  const { minContrast, minHueDistance } = options;
429
- const grey = { h: 0, l: clamp(meanL, 0.3, 0.8) };
465
+ const grey = { c: 0, h: 0, l: clamp(meanL, 0.3, 0.8) };
430
466
  const family = /* @__PURE__ */ new Map();
431
467
  for (const cluster of chromatic) {
432
468
  let share2 = 0;
@@ -436,69 +472,62 @@ function analyzeAmbientPixels(pixels, overrides = {}) {
436
472
  family.set(cluster, share2);
437
473
  }
438
474
  const share = (cluster) => family.get(cluster) ?? cluster.pop;
439
- const withShare = (cluster) => cluster ? { ...cluster, pop: share(cluster) } : null;
440
- const dominantPick = neutral ? null : pickBest(chromatic, (cluster) => share(cluster) * cluster.c * cluster.rely);
475
+ const dominantPick = neutral ? null : pickBest(
476
+ chromatic,
477
+ (cluster) => share(cluster) * cluster.c * cluster.rely * salience(cluster)
478
+ );
441
479
  const vividPick = neutral ? null : pickBest(
442
480
  chromatic.filter((cluster) => share(cluster) >= MIN_POPULATION),
443
- (cluster) => cluster.c * cluster.c * Math.pow(share(cluster), 0.3) * cluster.rely
481
+ (cluster) => cluster.c * cluster.c * Math.pow(share(cluster), 0.3) * cluster.rely * salience(cluster)
444
482
  );
445
483
  const accentPick = pickBest(
446
484
  chromatic.filter(
447
485
  (cluster) => neutral ? share(cluster) >= 6e-3 && cluster.c >= 0.06 : share(cluster) >= MIN_POPULATION && cluster.c >= 0.035 && (!dominantPick || hueDistance(cluster.h, dominantPick.h) >= minHueDistance)
448
486
  ),
449
- (cluster) => Math.pow(share(cluster), 0.6) * cluster.c * cluster.rely
487
+ (cluster) => Math.pow(share(cluster), 0.6) * cluster.c * cluster.rely * salience(cluster)
450
488
  );
451
489
  const mutedPick = neutral ? null : pickBest(
452
490
  chromatic.filter((cluster) => cluster.c <= 0.09),
453
491
  (cluster) => share(cluster) * cluster.rely * (1 - cluster.c / 0.12)
454
492
  );
455
493
  const dominantHue = dominantPick?.h ?? vividPick?.h ?? 0;
494
+ const accentSource = accentPick ?? vividPick;
456
495
  const roles = {
457
496
  dominant: buildSwatch(
458
- withShare(dominantPick),
459
- {
460
- cap: 0.22,
461
- max: 0.74,
462
- min: 0.42,
463
- rel: dominantPick ? lift(dominantPick.rel, 0.25, 0.85, 0.9) : 0
464
- },
465
- grey,
497
+ roleTone(dominantPick, { cap: 0.2, gain: 1.15, max: 0.74, min: 0.42 }, grey),
498
+ dominantPick ? share(dominantPick) : 0,
466
499
  true,
467
500
  minContrast
468
501
  ),
469
502
  vibrant: buildSwatch(
470
- withShare(vividPick),
471
- {
472
- cap: 0.24,
473
- max: 0.84,
474
- min: 0.62,
475
- rel: vividPick ? lift(vividPick.rel, 0.4, 0.75, 0.95) * dilution : 0
476
- },
477
- { h: dominantHue, l: 0.78 },
503
+ roleTone(
504
+ vividPick,
505
+ { cap: 0.22, gain: 1.35, max: 0.84, min: 0.62 },
506
+ { c: 0, h: dominantHue, l: 0.78 },
507
+ dilution
508
+ ),
509
+ vividPick ? share(vividPick) : 0,
478
510
  true,
479
511
  minContrast
480
512
  ),
481
513
  muted: buildSwatch(
482
- withShare(mutedPick),
483
- {
484
- cap: 0.08,
485
- max: 0.66,
486
- min: 0.38,
487
- rel: mutedPick ? clamp(mutedPick.rel, 0.1, 0.3) : dominantPick ? 0.2 : 0
488
- },
489
- { h: dominantHue, l: clamp(meanL, 0.38, 0.66) },
514
+ roleTone(
515
+ mutedPick,
516
+ { cap: 0.07, gain: 1, max: 0.66, min: 0.38 },
517
+ { c: dominantPick ? 0.035 : 0, h: dominantHue, l: clamp(meanL, 0.38, 0.66) }
518
+ ),
519
+ mutedPick ? share(mutedPick) : 0,
490
520
  Boolean(mutedPick) || neutral,
491
521
  minContrast
492
522
  ),
493
523
  accent: buildSwatch(
494
- withShare(accentPick ?? vividPick),
495
- {
496
- cap: 0.24,
497
- max: 0.8,
498
- min: 0.55,
499
- rel: accentPick ?? vividPick ? lift((accentPick ?? vividPick).rel, 0.35, 0.75, 0.92) * dilution : 0
500
- },
501
- { h: dominantHue, l: 0.72 },
524
+ roleTone(
525
+ accentSource,
526
+ { cap: 0.22, gain: 1.3, max: 0.8, min: 0.55 },
527
+ { c: 0, h: dominantHue, l: 0.72 },
528
+ dilution
529
+ ),
530
+ accentSource ? share(accentSource) : 0,
502
531
  Boolean(accentPick),
503
532
  minContrast
504
533
  )
@@ -515,35 +544,39 @@ function analyzeAmbientPixels(pixels, overrides = {}) {
515
544
  options.shadowLightness - 0.02,
516
545
  options.shadowLightness + 0.02
517
546
  );
518
- const shadow = deepSource ? toneOf(deepSource.h, shadowL, lift(deepSource.rel, 0.3, 0.7, 0.85), 0.1) : { c: 0, h: 0, l: shadowL };
547
+ const shadow = deepSource ? toneOf(deepSource.h, shadowL, clamp(0.3 + 0.7 * deepSource.rel, 0, 0.85), 0.1) : { c: 0, h: 0, l: shadowL };
519
548
  const regions = {};
520
549
  AMBIENT_REGIONS.forEach((region, r) => {
521
550
  const weights = regionWeights[r];
551
+ const lab = regionLab[r];
522
552
  let regionTotal = 0;
523
553
  let regionL = 0;
524
554
  clusters.forEach((cluster, j) => {
525
555
  regionTotal += weights[j];
526
556
  regionL += cluster.l * weights[j];
527
557
  });
528
- const share2 = (j) => regionTotal > 0 ? weights[j] / regionTotal : 0;
529
558
  let strength = 0;
530
559
  let pick = null;
531
560
  let pickScore = 0;
532
- clusters.forEach((cluster, j) => {
533
- if (!isChromatic(cluster)) return;
534
- strength += share2(j) * colorfulness(cluster.c) * cluster.rely;
535
- const score = share2(j) * cluster.c * cluster.rely;
561
+ clusters.forEach((_, j) => {
562
+ const count = weights[j];
563
+ if (count <= 0) return;
564
+ const local = labToLch([lab[j * 3] / count, lab[j * 3 + 1] / count, lab[j * 3 + 2] / count]);
565
+ const rely = reliability(local.l);
566
+ if (local.c < MIN_CHROMA || rely <= 0) return;
567
+ const part = count / regionTotal;
568
+ strength += part * colorfulness(local.c) * rely;
569
+ const score = part * local.c * rely * salience(local);
536
570
  if (score > pickScore) {
537
571
  pickScore = score;
538
- pick = cluster;
572
+ pick = local;
539
573
  }
540
574
  });
541
575
  const chosen = neutral || strength < MIN_PRESENCE ? null : pick;
542
- const tone = chosen ? toneOf(
576
+ const tone = chosen ? fitTone(
543
577
  chosen.h,
544
578
  clamp(chosen.l, 0.42, 0.72),
545
- lift(chosen.rel, 0.25, 0.85, 0.9),
546
- 0.22
579
+ roleChroma(chosen, { cap: 0.18, gain: 1.15 })
547
580
  ) : {
548
581
  c: 0,
549
582
  h: 0,
@@ -572,6 +605,6 @@ function fitSample(width, height, size) {
572
605
  };
573
606
  }
574
607
 
575
- export { analyzeAmbientPixels, configureAmbient, contrastRatio, findContentBounds, fitSample, formatOklch, getAmbientSettings, maxChroma, onColorFor, resolveAnalysis, srgbToOklab, toneOf };
576
- //# sourceMappingURL=chunk-6SG7V77J.js.map
577
- //# sourceMappingURL=chunk-6SG7V77J.js.map
608
+ export { AMBIENT_ANALYSIS_VERSION, analyzeAmbientPixels, configureAmbient, contrastRatio, findContentBounds, fitSample, formatOklch, getAmbientSettings, maxChroma, onColorFor, resolveAnalysis, srgbToOklab, toneOf };
609
+ //# sourceMappingURL=chunk-HHWRTQKQ.js.map
610
+ //# sourceMappingURL=chunk-HHWRTQKQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/modules/ambient/oklab.ts","../src/modules/ambient/settings.ts","../src/modules/ambient/quantize.ts","../src/modules/ambient/analyze.ts"],"names":["round","share"],"mappings":"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","file":"chunk-HHWRTQKQ.js","sourcesContent":["import type { OklchColor } from \"./types\";\n\n// Pure OKLab / OKLCH math shared by the client, the worker and the server.\n// Nothing here touches the DOM or React.\n\nexport type Lab = [l: number, a: number, b: number];\nexport type Rgb = [r: number, g: number, b: number];\n\nconst DEG = Math.PI / 180;\n\nconst LINEAR_LUT = new Float32Array(256);\nfor (let i = 0; i < 256; i++) {\n const v = i / 255;\n LINEAR_LUT[i] = v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);\n}\n\nexport function clamp(value: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, value));\n}\n\nexport function smoothstep(edge0: number, edge1: number, value: number): number {\n const t = clamp((value - edge0) / (edge1 - edge0), 0, 1);\n return t * t * (3 - 2 * t);\n}\n\n/** 8-bit sRGB channels → OKLab. */\nexport function srgbToOklab(r: number, g: number, b: number): Lab {\n const lr = LINEAR_LUT[r & 255];\n const lg = LINEAR_LUT[g & 255];\n const lb = LINEAR_LUT[b & 255];\n\n const l = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);\n const m = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);\n const s = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);\n\n return [\n 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,\n 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,\n 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,\n ];\n}\n\n/** OKLCH → linear sRGB; channels outside 0–1 mean the colour is out of gamut. */\nexport function oklchToLinearSrgb(l: number, c: number, h: number): Rgb {\n const a = c * Math.cos(h * DEG);\n const b = c * Math.sin(h * DEG);\n const l_ = (l + 0.3963377774 * a + 0.2158037573 * b) ** 3;\n const m_ = (l - 0.1055613458 * a - 0.0638541728 * b) ** 3;\n const s_ = (l - 0.0894841775 * a - 1.291485548 * b) ** 3;\n return [\n 4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_,\n -1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_,\n -0.0041960863 * l_ - 0.7034186147 * m_ + 1.707614701 * s_,\n ];\n}\n\nexport function labToLch([l, a, b]: Lab): OklchColor {\n const c = Math.sqrt(a * a + b * b);\n const h = c < 1e-6 ? 0 : (Math.atan2(b, a) / DEG + 360) % 360;\n return { c, h, l };\n}\n\nconst inGamut = (rgb: Rgb) => rgb.every((v) => v >= -1e-5 && v <= 1 + 1e-5);\n\n/**\n * The most chroma sRGB can show at this lightness and hue (binary search on\n * the gamut boundary). Dark blues and purples have far more room than dark\n * greens or yellows, which is why one fixed chroma never looks right.\n */\nexport function maxChroma(l: number, h: number): number {\n if (l <= 0 || l >= 1) return 0;\n let low = 0;\n let high = 0.4;\n for (let step = 0; step < 18; step++) {\n const middle = (low + high) / 2;\n if (inGamut(oklchToLinearSrgb(l, middle, h))) low = middle;\n else high = middle;\n }\n return low;\n}\n\n/** Share of the available chroma a colour uses (0 grey … 1 on the gamut edge). */\nexport function relativeChroma({ c, h, l }: OklchColor): number {\n const room = maxChroma(l, h);\n return room <= 0 ? 0 : clamp(c / room, 0, 1);\n}\n\nexport function hueDistance(a: number, b: number): number {\n const d = Math.abs(a - b) % 360;\n return d > 180 ? 360 - d : d;\n}\n\n/** WCAG 2 relative luminance of an OKLCH colour (gamut-clipped). */\nexport function luminance({ c, h, l }: OklchColor): number {\n const [r, g, b] = oklchToLinearSrgb(l, c, h).map((v) => clamp(v, 0, 1));\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\nexport function contrastRatio(a: OklchColor, b: OklchColor): number {\n const la = luminance(a);\n const lb = luminance(b);\n return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);\n}\n\nconst round = (value: number, digits: number) => {\n const factor = 10 ** digits;\n return Math.round(value * factor) / factor;\n};\n\nexport function formatOklch({ c, h, l }: OklchColor): string {\n const chroma = c < 0.0005 ? 0 : c;\n return `oklch(${round(clamp(l, 0, 1), 3).toFixed(3)} ${round(chroma, 3).toFixed(3)} ${round(chroma === 0 ? 0 : h, 1).toFixed(1)})`;\n}\n\n/**\n * A tone of a hue at lightness `l`, using `rel` of the chroma sRGB allows\n * there (never more than `cap`). This keeps a hue's character across a whole\n * ramp: dark tones of blue stay blue instead of collapsing to grey.\n */\nexport function toneOf(\n h: number,\n l: number,\n rel: number,\n cap = 0.3,\n): OklchColor {\n const room = maxChroma(l, h) * 0.96;\n return { c: Math.min(cap, clamp(rel, 0, 1) * room), h, l };\n}\n\n/**\n * Text colour for a background: a near-white or near-black carrying a hint of\n * the background's hue, pushed toward pure white / black until it reaches\n * `minContrast` (WCAG 2 ratio).\n */\nexport function onColorFor(\n background: OklchColor,\n minContrast = 4.5,\n): OklchColor {\n const light = (l: number) => toneOf(background.h, l, 0.12, 0.012);\n const dark = (l: number) => toneOf(background.h, l, 0.35, 0.03);\n const towardLight = contrastRatio(light(0.985), background);\n const towardDark = contrastRatio(dark(0.18), background);\n const useLight = towardLight >= minContrast || towardLight >= towardDark;\n\n for (let step = 0; step <= 12; step++) {\n const candidate = useLight\n ? light(Math.min(1, 0.985 + step * 0.0025))\n : dark(Math.max(0, 0.18 - step * 0.015));\n if (contrastRatio(candidate, background) >= minContrast) return candidate;\n }\n return useLight ? { c: 0, h: 0, l: 1 } : { c: 0, h: 0, l: 0 };\n}\n","import type {\n AmbientAnalysisOptions,\n AmbientCssVarNames,\n AmbientProxy,\n AmbientSettings,\n AmbientSettingsInput,\n AmbientTone,\n} from \"./types\";\n\nexport const AMBIENT_TONES = Object.freeze([\n 10, 15, 20, 30, 40, 50, 60, 70, 80, 90, 95,\n] as const) satisfies readonly AmbientTone[];\n\nexport const AMBIENT_ROLES = Object.freeze([\n \"dominant\",\n \"vibrant\",\n \"muted\",\n \"accent\",\n] as const);\n\nexport const AMBIENT_REGIONS = Object.freeze([\n \"top\",\n \"bottom\",\n \"left\",\n \"right\",\n \"center\",\n] as const);\n\nexport const DEFAULT_ANALYSIS: Readonly<AmbientAnalysisOptions> = Object.freeze({\n centerWeight: 0.5,\n clusters: 7,\n letterbox: true,\n minContrast: 4.5,\n minHueDistance: 40,\n sampleSize: 128,\n shadowLightness: 0.16,\n});\n\nexport const DEFAULT_CSS_VAR_NAMES: Readonly<AmbientCssVarNames> = Object.freeze({\n on: \"--ambient-on-{role}\",\n region: \"--ambient-{region}\",\n role: \"--ambient-{role}\",\n shadow: \"--ambient-shadow\",\n tone: \"--ambient-{role}-{tone}\",\n});\n\nexport const DEFAULT_PROXY = \"/api/ambient/proxy?url=\";\n\nexport const DEFAULT_LINGER_MS = 3_000;\n\nconst createDefaults = (): AmbientSettings => ({\n analysis: { ...DEFAULT_ANALYSIS },\n cacheLimit: 100,\n cssVars: { ...DEFAULT_CSS_VAR_NAMES, tones: AMBIENT_TONES },\n linger: DEFAULT_LINGER_MS,\n proxy: DEFAULT_PROXY,\n strategy: \"auto\",\n worker: null,\n});\n\n// Kept on globalThis so every bundled copy of this module (one per package\n// entry) shares one configuration.\nconst SETTINGS_KEY = Symbol.for(\"@omerdlw/base-framework/ambient/settings\");\ntype GlobalWithSettings = typeof globalThis & {\n [SETTINGS_KEY]?: AmbientSettings;\n};\nconst scope = globalThis as GlobalWithSettings;\n\nexport function getAmbientSettings(): AmbientSettings {\n return (scope[SETTINGS_KEY] ??= createDefaults());\n}\n\nexport function mergeAmbientSettings(\n base: AmbientSettings,\n input: AmbientSettingsInput,\n): AmbientSettings {\n return {\n analysis: { ...base.analysis, ...input.analysis },\n cacheLimit: input.cacheLimit ?? base.cacheLimit,\n cssVars: { ...base.cssVars, ...input.cssVars },\n linger: input.linger ?? base.linger,\n proxy: input.proxy === undefined ? base.proxy : input.proxy,\n strategy: input.strategy ?? base.strategy,\n worker: input.worker === undefined ? base.worker : input.worker,\n };\n}\n\n/**\n * Sets the proxy, CSS variable names, extraction strategy, worker and analysis\n * defaults for every ambient theme. Returns a function that restores the\n * previous settings.\n */\nexport function configureAmbient(input: AmbientSettingsInput): () => void {\n const previous = getAmbientSettings();\n const next = mergeAmbientSettings(previous, input);\n scope[SETTINGS_KEY] = next;\n return () => {\n if (scope[SETTINGS_KEY] === next) scope[SETTINGS_KEY] = previous;\n };\n}\n\nexport function resolveAnalysis(\n overrides?: Partial<AmbientAnalysisOptions> | null,\n): AmbientAnalysisOptions {\n return { ...getAmbientSettings().analysis, ...overrides };\n}\n\n/** The URL to retry through, or `null` when no proxy applies. */\nexport function resolveProxyUrl(proxy: AmbientProxy, src: string): string | null {\n if (!proxy || !/^https?:\\/\\//i.test(src)) return null;\n if (typeof proxy === \"function\") return proxy(src);\n const encoded = encodeURIComponent(src);\n return proxy.includes(\"{url}\")\n ? proxy.replace(\"{url}\", encoded)\n : `${proxy}${encoded}`;\n}\n","export interface KMeansResult {\n /** Index of the cluster each point belongs to. */\n assignment: Int32Array;\n /** `k × 3` OKLab centroids. */\n centroids: Float64Array;\n /** Number of clusters actually found (≤ the requested k). */\n k: number;\n}\n\nexport interface KMeansOptions {\n iterations?: number;\n /** Weight of ΔL² against Δa² + Δb²; below 1 favours hue over lightness. */\n lightnessWeight?: number;\n}\n\n/**\n * Weighted k-means in OKLab with deterministic seeding (the heaviest point,\n * then repeatedly the point that is heaviest × furthest from every seed), so\n * the same pixels always give the same palette on the server and the client.\n *\n * `points` holds `n × 3` OKLab values and `weights` one weight per point.\n */\nexport function kmeans(\n points: Float64Array,\n weights: Float64Array,\n requested: number,\n { iterations = 16, lightnessWeight = 0.45 }: KMeansOptions = {},\n): KMeansResult {\n const n = weights.length;\n const assignment = new Int32Array(n);\n const k = Math.max(0, Math.min(requested, n));\n const centroids = new Float64Array(k * 3);\n if (k === 0) return { assignment, centroids, k };\n\n const distance = (i: number, j: number) => {\n const dl = points[i * 3] - centroids[j * 3];\n const da = points[i * 3 + 1] - centroids[j * 3 + 1];\n const db = points[i * 3 + 2] - centroids[j * 3 + 2];\n return lightnessWeight * dl * dl + da * da + db * db;\n };\n\n let first = 0;\n for (let i = 1; i < n; i++) if (weights[i] > weights[first]) first = i;\n centroids.set(points.subarray(first * 3, first * 3 + 3), 0);\n\n const nearest = new Float64Array(n).fill(Infinity);\n let found = 1;\n for (; found < k; found++) {\n let best = -1;\n let bestScore = 0;\n for (let i = 0; i < n; i++) {\n const d = distance(i, found - 1);\n if (d < nearest[i]) nearest[i] = d;\n const score = weights[i] * nearest[i];\n if (score > bestScore) {\n bestScore = score;\n best = i;\n }\n }\n if (best < 0) break; // every remaining point sits on a seed\n centroids.set(points.subarray(best * 3, best * 3 + 3), found * 3);\n }\n\n const sums = new Float64Array(found * 4);\n for (let round = 0; round < iterations; round++) {\n let moved = round === 0;\n for (let i = 0; i < n; i++) {\n let best = 0;\n let bestDistance = Infinity;\n for (let j = 0; j < found; j++) {\n const d = distance(i, j);\n if (d < bestDistance) {\n bestDistance = d;\n best = j;\n }\n }\n if (assignment[i] !== best) {\n assignment[i] = best;\n moved = true;\n }\n }\n if (!moved) break;\n\n sums.fill(0);\n for (let i = 0; i < n; i++) {\n const j = assignment[i] * 4;\n const w = weights[i];\n sums[j] += points[i * 3] * w;\n sums[j + 1] += points[i * 3 + 1] * w;\n sums[j + 2] += points[i * 3 + 2] * w;\n sums[j + 3] += w;\n }\n for (let j = 0; j < found; j++) {\n const w = sums[j * 4 + 3];\n if (w <= 0) continue; // an empty cluster keeps its seed\n centroids[j * 3] = sums[j * 4] / w;\n centroids[j * 3 + 1] = sums[j * 4 + 1] / w;\n centroids[j * 3 + 2] = sums[j * 4 + 2] / w;\n }\n }\n\n return { assignment, centroids: centroids.slice(0, found * 3), k: found };\n}\n","import {\n clamp,\n formatOklch,\n hueDistance,\n labToLch,\n maxChroma,\n onColorFor,\n smoothstep,\n srgbToOklab,\n toneOf,\n} from \"./oklab\";\nimport { kmeans } from \"./quantize\";\nimport { AMBIENT_REGIONS, AMBIENT_TONES, DEFAULT_ANALYSIS } from \"./settings\";\nimport type {\n AmbientAnalysisOptions,\n AmbientPalette,\n AmbientPixels,\n AmbientRamp,\n AmbientRegion,\n AmbientRegionColor,\n AmbientRole,\n AmbientSwatch,\n OklchColor,\n} from \"./types\";\n\n/**\n * Bumped whenever the same pixels would give a different palette. Put it in\n * the key of any palette cache that outlives a deploy (`unstable_cache`, a\n * database), so stored palettes are computed again.\n */\nexport const AMBIENT_ANALYSIS_VERSION = 2;\n\n// Below this chroma a colour reads as grey; its hue is noise.\nconst MIN_CHROMA = 0.022;\n// Below this share of coloured pixels an image is treated as neutral.\nconst MIN_PRESENCE = 0.03;\n// A colour this small still counts as a role when it is vivid enough.\nconst MIN_POPULATION = 0.004;\n\nconst REGION_BITS: Record<AmbientRegion, number> = {\n top: 1,\n bottom: 2,\n left: 4,\n right: 8,\n center: 16,\n};\n\nexport interface Cluster extends OklchColor {\n /** Share of the (centre-weighted) image. */\n pop: number;\n /** Share of the chroma sRGB allows at this lightness and hue. */\n rel: number;\n /** How far the hue can be trusted at this lightness (0–1). */\n rely: number;\n}\n\ninterface Bounds {\n bottom: number;\n left: number;\n right: number;\n top: number;\n}\n\nconst lightnessAt = (data: ArrayLike<number>, index: number) =>\n srgbToOklab(data[index], data[index + 1], data[index + 2])[0];\n\n/**\n * Finds black bars along the edges (letterbox / pillarbox): rows or columns\n * that are uniformly near-black. At most 22% of each side is cut.\n */\nexport function findContentBounds({ data, height, width }: AmbientPixels): Bounds {\n const isBar = (count: number, at: (step: number) => number) => {\n let sum = 0;\n let max = 0;\n let seen = 0;\n for (let step = 0; step < count; step++) {\n const index = at(step);\n if (data[index + 3] < 128) continue;\n const l = lightnessAt(data, index);\n sum += l;\n if (l > max) max = l;\n seen++;\n }\n return seen > 0 && sum / seen < 0.12 && max < 0.2;\n };\n const row = (y: number) => isBar(width, (x) => (y * width + x) * 4);\n const maxRows = Math.floor(height * 0.22);\n const maxColumns = Math.floor(width * 0.22);\n\n let top = 0;\n while (top < maxRows && row(top)) top++;\n let bottom = height;\n while (height - bottom < maxRows && bottom - 1 > top && row(bottom - 1)) bottom--;\n\n const column = (x: number) =>\n isBar(bottom - top, (step) => ((top + step) * width + x) * 4);\n let left = 0;\n while (left < maxColumns && column(left)) left++;\n let right = width;\n while (width - right < maxColumns && right - 1 > left && column(right - 1)) right--;\n\n return { bottom, left, right, top };\n}\n\nconst reliability = (l: number) =>\n smoothstep(0.1, 0.28, l) * (1 - smoothstep(0.9, 0.98, l));\n\nconst colorfulness = (c: number) => smoothstep(MIN_CHROMA, 0.06, c);\n\nconst isChromatic = (cluster: Cluster) =>\n cluster.c >= MIN_CHROMA && cluster.rely > 0;\n\nfunction pickBest<T>(items: T[], score: (item: T) => number): T | null {\n let best: T | null = null;\n let bestScore = 0;\n for (const item of items) {\n const value = score(item);\n if (value > bestScore) {\n bestScore = value;\n best = item;\n }\n }\n return best;\n}\n\nconst round3 = (value: number) => Math.round(value * 1000) / 1000;\n\n/**\n * How much a colour looks like skin, hair, wood or a tungsten-lit wall: the\n * warm, low-chroma band most film frames are full of. It is what a frame\n * shows, rarely its mood.\n */\nexport function skinLikeness({ c, h, l }: OklchColor): number {\n return (\n (1 - smoothstep(12, 26, hueDistance(h, 54))) *\n smoothstep(0.02, 0.035, c) *\n (1 - smoothstep(0.11, 0.15, c)) *\n smoothstep(0.26, 0.38, l) *\n (1 - smoothstep(0.88, 0.95, l))\n );\n}\n\n// Reds and oranges read louder than blues or greens at the same chroma\n// (Helmholtz–Kohlrausch), so they are given less of it.\nconst warmth = (h: number) => 1 - smoothstep(22, 50, hueDistance(h, 42));\n\n// Weight of a colour in the role and region picks: skin still counts, less.\nconst salience = (color: OklchColor) => 1 - 0.65 * skinLikeness(color);\n\ninterface ChromaSpec {\n cap: number;\n gain: number;\n}\n\n/**\n * The chroma a role carries: its source's own, raised by `gain`, held back\n * for skin and warm hues, never above `cap`. Chroma, not a share of the\n * gamut: at mid lightness orange has twice the room teal has, so carrying\n * the share would turn a pale face into a loud orange.\n */\nconst roleChroma = (source: OklchColor, { cap, gain }: ChromaSpec) =>\n Math.min(\n cap * (1 - 0.3 * warmth(source.h)),\n // A faint tint is raised further, so it still reads as a colour.\n Math.max(source.c * gain, Math.min(0.05, source.c * 1.6)) *\n (1 - 0.3 * skinLikeness(source)),\n );\n\n/** `c` at hue `h` and lightness `l`, inside sRGB. */\nconst fitTone = (h: number, l: number, c: number): OklchColor => ({\n c: Math.max(0, Math.min(c, maxChroma(l, h) * 0.96)),\n h,\n l,\n});\n\nfunction buildSwatch(\n tone: OklchColor,\n population: number,\n fromImage: boolean,\n minContrast: number,\n): AmbientSwatch {\n // The ramp keeps the swatch's share of the gamut, so its dark tones stay\n // hued instead of collapsing to grey.\n const room = maxChroma(tone.l, tone.h) * 0.96;\n const rel = room > 0 ? tone.c / room : 0;\n const ramp = {} as AmbientRamp;\n for (const step of AMBIENT_TONES) {\n ramp[step] = formatOklch(toneOf(tone.h, step / 100, rel, Math.max(tone.c, 0.04)));\n }\n return {\n c: round3(tone.c),\n color: formatOklch(tone),\n fromImage,\n h: Math.round(tone.h * 10) / 10,\n l: round3(tone.l),\n on: formatOklch(onColorFor(tone, minContrast)),\n population: round3(population),\n ramp,\n };\n}\n\ninterface RoleSpec extends ChromaSpec {\n max: number;\n min: number;\n}\n\n/** A role's tone from its source cluster, or `fallback` when there is none. */\nfunction roleTone(\n source: Cluster | null,\n spec: RoleSpec,\n fallback: OklchColor,\n dilution = 1,\n): OklchColor {\n if (!source) return fitTone(fallback.h, clamp(fallback.l, spec.min, spec.max), fallback.c);\n return fitTone(\n source.h,\n clamp(source.l, spec.min, spec.max),\n roleChroma(source, spec) * dilution,\n );\n}\n\nexport interface AmbientClustering {\n clusters: Cluster[];\n /** Centre-weighted mean lightness. */\n meanL: number;\n /** Per region, the OKLab sum (`L, a, b` per cluster) of each cluster's pixels. */\n regionLab: Float64Array[];\n /** Per region, the pixel count of each cluster. */\n regionWeights: Float64Array[];\n}\n\n/**\n * Step one of the analysis: the image's colours as OKLab k-means clusters,\n * with each cluster's share of the frame and of each region.\n */\nexport function clusterAmbientPixels(\n pixels: AmbientPixels,\n options: AmbientAnalysisOptions,\n): AmbientClustering {\n const { data, width, height } = pixels;\n const bounds = options.letterbox\n ? findContentBounds(pixels)\n : { bottom: height, left: 0, right: width, top: 0 };\n const boxWidth = Math.max(1, bounds.right - bounds.left);\n const boxHeight = Math.max(1, bounds.bottom - bounds.top);\n\n // 1. Bin pixels by 15-bit RGB, averaging their OKLab values per bin.\n const binOf = new Int32Array(32768).fill(-1);\n const binSums: number[] = [];\n const pixelBins = new Int32Array(boxWidth * boxHeight).fill(-1);\n const pixelRegions = new Uint8Array(boxWidth * boxHeight);\n const sigma2 = 2 * 0.35 * 0.35;\n let total = 0;\n let sumL = 0;\n\n for (let y = bounds.top; y < bounds.bottom; y++) {\n const v = (y - bounds.top + 0.5) / boxHeight;\n for (let x = bounds.left; x < bounds.right; x++) {\n const index = (y * width + x) * 4;\n if (data[index + 3] < 128) continue;\n const r = data[index];\n const g = data[index + 1];\n const b = data[index + 2];\n const [l, a, bb] = srgbToOklab(r, g, b);\n const u = (x - bounds.left + 0.5) / boxWidth;\n const du = (u - 0.5) * 2;\n const dv = (v - 0.5) * 2;\n const weight =\n 1 -\n options.centerWeight +\n 2 * options.centerWeight * Math.exp(-(du * du + dv * dv) / sigma2);\n\n const key = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);\n let bin = binOf[key];\n if (bin < 0) {\n bin = binSums.length / 4;\n binOf[key] = bin;\n binSums.push(0, 0, 0, 0);\n }\n binSums[bin * 4] += l * weight;\n binSums[bin * 4 + 1] += a * weight;\n binSums[bin * 4 + 2] += bb * weight;\n binSums[bin * 4 + 3] += weight;\n total += weight;\n sumL += l * weight;\n\n const local = (y - bounds.top) * boxWidth + (x - bounds.left);\n pixelBins[local] = bin;\n pixelRegions[local] =\n (v < 1 / 3 ? REGION_BITS.top : 0) |\n (v > 2 / 3 ? REGION_BITS.bottom : 0) |\n (u < 1 / 3 ? REGION_BITS.left : 0) |\n (u > 2 / 3 ? REGION_BITS.right : 0) |\n (Math.abs(u - 0.5) < 0.25 && Math.abs(v - 0.5) < 0.25\n ? REGION_BITS.center\n : 0);\n }\n }\n\n const meanL = total > 0 ? sumL / total : 0.5;\n const bins = binSums.length / 4;\n\n // 2. Cluster coloured and grey bins apart, so the greys of a mostly dark\n // poster cannot use up every cluster and swallow its one red coat.\n const groups: [number[], number[]] = [[], []];\n for (let i = 0; i < bins; i++) {\n const w = binSums[i * 4 + 3];\n const a = binSums[i * 4 + 1] / w;\n const b = binSums[i * 4 + 2] / w;\n const coloured =\n Math.sqrt(a * a + b * b) >= MIN_CHROMA && binSums[i * 4] / w >= 0.06;\n groups[coloured ? 0 : 1].push(i);\n }\n const requested = clamp(Math.round(options.clusters), 2, 16);\n const greyClusters =\n groups[0].length === 0 ? requested : groups[1].length === 0 ? 0 : requested >= 8 ? 3 : 2;\n const budgets = [requested - greyClusters, greyClusters];\n\n const clusters: Cluster[] = [];\n const clusterOfBin = new Int32Array(bins).fill(-1);\n groups.forEach((members, group) => {\n if (members.length === 0 || budgets[group] <= 0) return;\n const points = new Float64Array(members.length * 3);\n const weights = new Float64Array(members.length);\n members.forEach((bin, i) => {\n const w = binSums[bin * 4 + 3];\n const a = binSums[bin * 4 + 1] / w;\n const b = binSums[bin * 4 + 2] / w;\n points[i * 3] = binSums[bin * 4] / w;\n points[i * 3 + 1] = a;\n points[i * 3 + 2] = b;\n // Vivid colours pull harder, so a small saturated area keeps its own cluster.\n weights[i] = w * (1 + 5 * Math.sqrt(a * a + b * b));\n });\n const { assignment, k } = kmeans(points, weights, budgets[group]);\n // A cluster's colour is the plain mean of its pixels: the vivid pull that\n // shaped the clusters would tint a face toward the red title beside it.\n const sums = new Float64Array(k * 4);\n members.forEach((bin, i) => {\n for (let channel = 0; channel < 4; channel++) {\n sums[assignment[i] * 4 + channel] += binSums[bin * 4 + channel];\n }\n });\n const index = new Int32Array(k).fill(-1);\n for (let j = 0; j < k; j++) {\n const pop = sums[j * 4 + 3];\n if (pop <= 0) continue;\n const lch = labToLch([sums[j * 4] / pop, sums[j * 4 + 1] / pop, sums[j * 4 + 2] / pop]);\n const room = maxChroma(lch.l, lch.h);\n index[j] = clusters.length;\n clusters.push({\n ...lch,\n pop: pop / total,\n rel: room > 0 ? clamp(lch.c / room, 0, 1) : 0,\n rely: reliability(lch.l),\n });\n }\n members.forEach((bin, i) => (clusterOfBin[bin] = index[assignment[i]]));\n });\n\n // 3. Region shares per cluster, and the OKLab sum of each cluster's pixels\n // in each region: a cluster spanning the frame looks different in parts.\n const regionWeights = AMBIENT_REGIONS.map(() => new Float64Array(clusters.length));\n const regionLab = AMBIENT_REGIONS.map(() => new Float64Array(clusters.length * 3));\n for (let i = 0; i < pixelBins.length; i++) {\n const bin = pixelBins[i];\n if (bin < 0) continue;\n const cluster = clusterOfBin[bin];\n const bits = pixelRegions[i];\n const w = binSums[bin * 4 + 3];\n AMBIENT_REGIONS.forEach((region, r) => {\n if (!(bits & REGION_BITS[region])) return;\n regionWeights[r][cluster] += 1;\n for (let channel = 0; channel < 3; channel++) {\n regionLab[r][cluster * 3 + channel] += binSums[bin * 4 + channel] / w;\n }\n });\n }\n\n return { clusters, meanL, regionLab, regionWeights };\n}\n\n/**\n * The palette of an image: OKLab k-means clusters scored by population ×\n * chroma (skin tones counted less), turned into four roles (each with a tonal\n * ramp and a contrast-safe text colour), five region colours, and the deep\n * `shadow` tone. Roles keep about their source's chroma rather than its share\n * of the gamut, so warm hues are not inflated.\n */\nexport function analyzeAmbientPixels(\n pixels: AmbientPixels,\n overrides: Partial<AmbientAnalysisOptions> = {},\n): AmbientPalette {\n const options = { ...DEFAULT_ANALYSIS, ...overrides };\n const { clusters, meanL, regionLab, regionWeights } = clusterAmbientPixels(pixels, options);\n\n // 4. Roles.\n const chromatic = clusters.filter(isChromatic);\n const presence = chromatic.reduce(\n (sum, cluster) => sum + cluster.pop * colorfulness(cluster.c) * cluster.rely,\n 0,\n );\n const neutral = presence < MIN_PRESENCE;\n // A coloured detail on an almost grey image is kept, but quieter.\n const dilution = 0.55 + 0.45 * Math.min(1, presence / 0.25);\n const { minContrast, minHueDistance } = options;\n const grey = { c: 0, h: 0, l: clamp(meanL, 0.3, 0.8) };\n\n // k-means splits one hue into several lightness levels; a hue's share is\n // that of its whole family (clusters within 20°), so it is not undercounted.\n const family = new Map<Cluster, number>();\n for (const cluster of chromatic) {\n let share = 0;\n for (const other of chromatic) {\n if (hueDistance(cluster.h, other.h) <= 20) share += other.pop;\n }\n family.set(cluster, share);\n }\n const share = (cluster: Cluster) => family.get(cluster) ?? cluster.pop;\n\n const dominantPick = neutral\n ? null\n : pickBest(\n chromatic,\n (cluster) => share(cluster) * cluster.c * cluster.rely * salience(cluster),\n );\n // A grey image keeps a grey `vibrant`: no hue is invented.\n const vividPick = neutral\n ? null\n : pickBest(\n chromatic.filter((cluster) => share(cluster) >= MIN_POPULATION),\n (cluster) =>\n cluster.c *\n cluster.c *\n Math.pow(share(cluster), 0.3) *\n cluster.rely *\n salience(cluster),\n );\n // …but a coloured detail on it (the red coat) becomes the accent.\n const accentPick = pickBest(\n chromatic.filter((cluster) =>\n neutral\n ? share(cluster) >= 0.006 && cluster.c >= 0.06\n : share(cluster) >= MIN_POPULATION &&\n cluster.c >= 0.035 &&\n (!dominantPick || hueDistance(cluster.h, dominantPick.h) >= minHueDistance),\n ),\n (cluster) =>\n Math.pow(share(cluster), 0.6) * cluster.c * cluster.rely * salience(cluster),\n );\n const mutedPick = neutral\n ? null\n : pickBest(\n chromatic.filter((cluster) => cluster.c <= 0.09),\n (cluster) => share(cluster) * cluster.rely * (1 - cluster.c / 0.12),\n );\n\n const dominantHue = dominantPick?.h ?? vividPick?.h ?? 0;\n const accentSource = accentPick ?? vividPick;\n const roles: Record<AmbientRole, AmbientSwatch> = {\n dominant: buildSwatch(\n roleTone(dominantPick, { cap: 0.2, gain: 1.15, max: 0.74, min: 0.42 }, grey),\n dominantPick ? share(dominantPick) : 0,\n true,\n minContrast,\n ),\n vibrant: buildSwatch(\n roleTone(\n vividPick,\n { cap: 0.22, gain: 1.35, max: 0.84, min: 0.62 },\n { c: 0, h: dominantHue, l: 0.78 },\n dilution,\n ),\n vividPick ? share(vividPick) : 0,\n true,\n minContrast,\n ),\n muted: buildSwatch(\n roleTone(\n mutedPick,\n { cap: 0.07, gain: 1, max: 0.66, min: 0.38 },\n { c: dominantPick ? 0.035 : 0, h: dominantHue, l: clamp(meanL, 0.38, 0.66) },\n ),\n mutedPick ? share(mutedPick) : 0,\n Boolean(mutedPick) || neutral,\n minContrast,\n ),\n accent: buildSwatch(\n roleTone(\n accentSource,\n { cap: 0.22, gain: 1.3, max: 0.8, min: 0.55 },\n { c: 0, h: dominantHue, l: 0.72 },\n dilution,\n ),\n accentSource ? share(accentSource) : 0,\n Boolean(accentPick),\n minContrast,\n ),\n };\n\n // 5. `shadow`: the shadows' own hue when they have one, else the dominant's.\n // In the dark, relative chroma says more than chroma: a navy at L 0.14\n // uses most of what sRGB has there.\n const shadowPick = neutral\n ? null\n : pickBest(\n clusters.filter(\n (cluster) => cluster.l <= 0.5 && cluster.c >= 0.02 && cluster.pop >= 0.02,\n ),\n (cluster) => cluster.pop * cluster.rel * smoothstep(0.05, 0.14, cluster.l),\n );\n const deepSource = shadowPick ?? dominantPick;\n const shadowL = clamp(\n options.shadowLightness + (meanL - 0.45) * 0.06,\n options.shadowLightness - 0.02,\n options.shadowLightness + 0.02,\n );\n const shadow = deepSource\n ? toneOf(deepSource.h, shadowL, clamp(0.3 + 0.7 * deepSource.rel, 0, 0.85), 0.1)\n : { c: 0, h: 0, l: shadowL };\n\n // 6. Regions: the most telling colour in each part of the frame, as that\n // part shows it (a cluster's own pixels there, not its frame-wide mean).\n const regions = {} as Record<AmbientRegion, AmbientRegionColor>;\n AMBIENT_REGIONS.forEach((region, r) => {\n const weights = regionWeights[r];\n const lab = regionLab[r];\n let regionTotal = 0;\n let regionL = 0;\n clusters.forEach((cluster, j) => {\n regionTotal += weights[j];\n regionL += cluster.l * weights[j];\n });\n let strength = 0;\n let pick: OklchColor | null = null;\n let pickScore = 0;\n clusters.forEach((_, j) => {\n const count = weights[j];\n if (count <= 0) return;\n const local = labToLch([lab[j * 3] / count, lab[j * 3 + 1] / count, lab[j * 3 + 2] / count]);\n const rely = reliability(local.l);\n if (local.c < MIN_CHROMA || rely <= 0) return;\n const part = count / regionTotal;\n strength += part * colorfulness(local.c) * rely;\n const score = part * local.c * rely * salience(local);\n if (score > pickScore) {\n pickScore = score;\n pick = local;\n }\n });\n const chosen = neutral || strength < MIN_PRESENCE ? null : (pick as OklchColor | null);\n const tone = chosen\n ? fitTone(\n chosen.h,\n clamp(chosen.l, 0.42, 0.72),\n roleChroma(chosen, { cap: 0.18, gain: 1.15 }),\n )\n : {\n c: 0,\n h: 0,\n l: clamp(regionTotal > 0 ? regionL / regionTotal : meanL, 0.3, 0.7),\n };\n regions[region] = {\n c: round3(tone.c),\n color: formatOklch(tone),\n h: Math.round(tone.h * 10) / 10,\n l: round3(tone.l),\n strength: round3(chosen ? clamp(strength, 0, 1) : 0),\n };\n });\n\n return {\n neutral,\n regions,\n roles,\n shadow: formatOklch(shadow),\n };\n}\n\n/** Width and height that fit `width × height` inside `size` on the long side. */\nexport function fitSample(width: number, height: number, size: number) {\n const scale = Math.min(1, size / Math.max(width, height, 1));\n return {\n height: Math.max(1, Math.round(height * scale)),\n width: Math.max(1, Math.round(width * scale)),\n };\n}\n"]}