@omerdlw/base-framework 1.0.5 → 1.0.6
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.
- package/dist/index.js +84 -74
- package/dist/index.js.map +1 -1
- package/dist/modules/ambient/color.d.ts +9 -4
- package/dist/modules/ambient/color.d.ts.map +1 -1
- package/dist/modules/ambient/extractor.d.ts +8 -0
- package/dist/modules/ambient/extractor.d.ts.map +1 -1
- package/dist/modules/ambient/index.js +78 -69
- package/dist/modules/ambient/index.js.map +1 -1
- package/dist/modules/dock/index.js +377 -375
- package/dist/modules/dock/index.js.map +1 -1
- package/dist/modules/dock/surface/context.d.ts.map +1 -1
- package/dist/modules/dock/surface/hooks.d.ts +5 -3
- package/dist/modules/dock/surface/hooks.d.ts.map +1 -1
- package/dist/modules/dock/surface/stack.d.ts.map +1 -1
- package/dist/modules/index.js +84 -74
- package/dist/modules/index.js.map +1 -1
- package/docs/modules/ambient.md +4 -4
- package/package.json +1 -1
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import type { AmbientPalette, OklchColor } from "./types";
|
|
2
2
|
export declare const COLOR_EXTRACT_CONFIG: Readonly<{
|
|
3
3
|
readonly blackChromaFactor: 0.35;
|
|
4
|
-
readonly primaryLightness: 0.74;
|
|
5
|
-
readonly maxBlackChroma: 0.055;
|
|
6
4
|
readonly blackLightness: 0.15;
|
|
7
5
|
readonly cacheLimit: 100;
|
|
6
|
+
readonly chromaGain: 9.4;
|
|
7
|
+
readonly hueBins: 36;
|
|
8
|
+
readonly maxBlackChroma: 0.055;
|
|
8
9
|
readonly maxChroma: 0.28;
|
|
9
|
-
readonly
|
|
10
|
-
readonly
|
|
10
|
+
readonly maxPrimaryChroma: 0.24;
|
|
11
|
+
readonly minPixelChroma: 0.03;
|
|
12
|
+
readonly minPresence: 0.04;
|
|
13
|
+
readonly neutralChroma: 0.012;
|
|
14
|
+
readonly primaryLightness: 0.74;
|
|
15
|
+
readonly sampleSize: 64;
|
|
11
16
|
}>;
|
|
12
17
|
export declare function srgbToLinear(c: number): number;
|
|
13
18
|
export declare function rgbToOklch(r: number, g: number, b: number): OklchColor;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../../src/modules/ambient/color.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1D,eAAO,MAAM,oBAAoB
|
|
1
|
+
{"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../../src/modules/ambient/color.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1D,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;EActB,CAAC;AAEZ,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAG9C;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,UAAU,CAyBtE;AAED,wBAAgB,aAAa,CAC3B,CAAC,EAAE,MAAM,GAAG,MAAM,EAClB,CAAC,EAAE,MAAM,GAAG,MAAM,EAClB,CAAC,EAAE,MAAM,GAAG,MAAM,EAClB,KAAK,GAAE,MAAM,GAAG,MAAU,GACzB,MAAM,CASR;AAMD,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG,cAAc,CAgCjE"}
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type { AmbientExtractOptions, AmbientImageSource, AmbientPalette, OklchColor } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Picks the most representative accent of an image.
|
|
4
|
+
*
|
|
5
|
+
* Pixels vote for their hue with a weight that grows with chroma (vivid pixels
|
|
6
|
+
* beat muddy ones) and fades toward very dark / very light pixels. Images
|
|
7
|
+
* without meaningful colour resolve to a neutral (`c: 0`) instead of an
|
|
8
|
+
* invented hue.
|
|
9
|
+
*/
|
|
2
10
|
export declare function sampleImageData(imageData: ImageData): OklchColor;
|
|
3
11
|
export declare function createDefaultPalette(fallback?: AmbientPalette | null): AmbientPalette;
|
|
4
12
|
export declare function extractPaletteFromImage(imageSource: AmbientImageSource, options?: AmbientExtractOptions): Promise<AmbientPalette>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../../src/modules/ambient/extractor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,EACd,UAAU,EACX,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../../src/modules/ambient/extractor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,EACd,UAAU,EACX,MAAM,SAAS,CAAC;AAUjB;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,UAAU,CAsFhE;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,CAAC,EAAE,cAAc,GAAG,IAAI,GAC/B,cAAc,CAKhB;AAsED,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,kBAAkB,EAC/B,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,cAAc,CAAC,CAqBzB"}
|
|
@@ -175,13 +175,18 @@ var ambientTheme = defineThemeSpec("ambient");
|
|
|
175
175
|
// src/modules/ambient/color.ts
|
|
176
176
|
var COLOR_EXTRACT_CONFIG = Object.freeze({
|
|
177
177
|
blackChromaFactor: 0.35,
|
|
178
|
-
primaryLightness: 0.74,
|
|
179
|
-
maxBlackChroma: 0.055,
|
|
180
178
|
blackLightness: 0.15,
|
|
181
179
|
cacheLimit: 100,
|
|
180
|
+
chromaGain: 9.4,
|
|
181
|
+
hueBins: 36,
|
|
182
|
+
maxBlackChroma: 0.055,
|
|
182
183
|
maxChroma: 0.28,
|
|
183
|
-
|
|
184
|
-
|
|
184
|
+
maxPrimaryChroma: 0.24,
|
|
185
|
+
minPixelChroma: 0.03,
|
|
186
|
+
minPresence: 0.04,
|
|
187
|
+
neutralChroma: 0.012,
|
|
188
|
+
primaryLightness: 0.74,
|
|
189
|
+
sampleSize: 64
|
|
185
190
|
});
|
|
186
191
|
function srgbToLinear(c) {
|
|
187
192
|
const v = c / 255;
|
|
@@ -218,100 +223,104 @@ function oklchToString(l, c, h, alpha = 1) {
|
|
|
218
223
|
}
|
|
219
224
|
return `oklch(${safeL} ${safeC} ${safeH})`;
|
|
220
225
|
}
|
|
226
|
+
function clamp(value, min, max) {
|
|
227
|
+
return Math.max(min, Math.min(max, value));
|
|
228
|
+
}
|
|
221
229
|
function derivePalette(sampled) {
|
|
222
230
|
const {
|
|
223
231
|
blackChromaFactor,
|
|
224
232
|
blackLightness,
|
|
233
|
+
chromaGain,
|
|
225
234
|
maxBlackChroma,
|
|
226
235
|
maxChroma,
|
|
227
|
-
|
|
236
|
+
maxPrimaryChroma,
|
|
237
|
+
neutralChroma,
|
|
228
238
|
primaryLightness
|
|
229
239
|
} = COLOR_EXTRACT_CONFIG;
|
|
230
|
-
const chroma =
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
0.
|
|
237
|
-
Math.min(0.24, Math.max(chroma * 1.15, 0.18))
|
|
240
|
+
const chroma = sampled.c < neutralChroma ? 0 : clamp(sampled.c, 0, maxChroma);
|
|
241
|
+
const hue = chroma === 0 ? 0 : sampled.h;
|
|
242
|
+
const primaryChroma = maxPrimaryChroma * (1 - Math.exp(-chroma * chromaGain));
|
|
243
|
+
const primaryL = clamp(
|
|
244
|
+
primaryLightness + (sampled.l - 0.6) * 0.5,
|
|
245
|
+
0.6,
|
|
246
|
+
0.86
|
|
238
247
|
);
|
|
248
|
+
const blackChroma = Math.min(maxBlackChroma, chroma * blackChromaFactor);
|
|
249
|
+
const blackL = clamp(blackLightness + (sampled.l - 0.6) * 0.06, 0.12, 0.18);
|
|
239
250
|
return {
|
|
240
|
-
black: oklchToString(
|
|
241
|
-
primary: oklchToString(
|
|
251
|
+
black: oklchToString(blackL, blackChroma, hue),
|
|
252
|
+
primary: oklchToString(primaryL, primaryChroma, hue)
|
|
242
253
|
};
|
|
243
254
|
}
|
|
244
255
|
|
|
245
256
|
// src/modules/ambient/extractor.ts
|
|
246
257
|
function sampleImageData(imageData) {
|
|
258
|
+
const { hueBins, minPixelChroma, minPresence } = COLOR_EXTRACT_CONFIG;
|
|
247
259
|
const data = imageData.data;
|
|
248
|
-
const
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
count: 0,
|
|
260
|
+
const binAngle = 360 / hueBins;
|
|
261
|
+
const bins = Array.from({ length: hueBins }, () => ({
|
|
262
|
+
cos: 0,
|
|
263
|
+
sin: 0,
|
|
253
264
|
sumC: 0,
|
|
254
|
-
sumCos: 0,
|
|
255
265
|
sumL: 0,
|
|
256
|
-
|
|
266
|
+
weight: 0
|
|
257
267
|
}));
|
|
258
|
-
let
|
|
259
|
-
let
|
|
268
|
+
let opaqueCount = 0;
|
|
269
|
+
let opaqueSumL = 0;
|
|
260
270
|
let chromaticCount = 0;
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (
|
|
269
|
-
|
|
270
|
-
neutralSumL += oklch.l;
|
|
271
|
+
let midNeutralCount = 0;
|
|
272
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
273
|
+
if (data[i + 3] < 128) continue;
|
|
274
|
+
const { c, h, l } = rgbToOklch(data[i], data[i + 1], data[i + 2]);
|
|
275
|
+
opaqueCount++;
|
|
276
|
+
opaqueSumL += l;
|
|
277
|
+
if (l < 0.06 || l > 0.96) continue;
|
|
278
|
+
if (c < minPixelChroma) {
|
|
279
|
+
midNeutralCount++;
|
|
271
280
|
continue;
|
|
272
281
|
}
|
|
273
|
-
const
|
|
274
|
-
const
|
|
275
|
-
const
|
|
276
|
-
bin.
|
|
277
|
-
bin.
|
|
278
|
-
bin.sumC +=
|
|
279
|
-
bin.
|
|
280
|
-
bin.
|
|
282
|
+
const usable = 1 - 0.7 * Math.min(1, Math.abs(l - 0.62) / 0.62);
|
|
283
|
+
const weight2 = c * c * usable;
|
|
284
|
+
const rad = h * (Math.PI / 180);
|
|
285
|
+
const bin = bins[Math.floor(h / binAngle) % hueBins];
|
|
286
|
+
bin.weight += weight2;
|
|
287
|
+
bin.sumC += c * weight2;
|
|
288
|
+
bin.sumL += l * weight2;
|
|
289
|
+
bin.cos += Math.cos(rad) * weight2;
|
|
290
|
+
bin.sin += Math.sin(rad) * weight2;
|
|
281
291
|
chromaticCount++;
|
|
282
292
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
293
|
+
const averageL = opaqueCount > 0 ? opaqueSumL / opaqueCount : 0.5;
|
|
294
|
+
const neutral = { c: 0, h: 0, l: Number(averageL.toFixed(3)) };
|
|
295
|
+
const presence = chromaticCount / Math.max(1, chromaticCount + midNeutralCount);
|
|
296
|
+
if (chromaticCount === 0 || presence < minPresence) return neutral;
|
|
287
297
|
let bestIndex = 0;
|
|
288
|
-
let
|
|
289
|
-
for (let i = 0; i <
|
|
290
|
-
const
|
|
291
|
-
const
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
maxWindowCount = windowCount;
|
|
298
|
+
let bestScore = -1;
|
|
299
|
+
for (let i = 0; i < hueBins; i++) {
|
|
300
|
+
const prev = bins[(i - 1 + hueBins) % hueBins];
|
|
301
|
+
const next = bins[(i + 1) % hueBins];
|
|
302
|
+
const score = prev.weight * 0.5 + bins[i].weight + next.weight * 0.5;
|
|
303
|
+
if (score > bestScore) {
|
|
304
|
+
bestScore = score;
|
|
296
305
|
bestIndex = i;
|
|
297
306
|
}
|
|
298
307
|
}
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const
|
|
307
|
-
const
|
|
308
|
-
const
|
|
309
|
-
const
|
|
310
|
-
const
|
|
308
|
+
const window2 = [
|
|
309
|
+
bins[(bestIndex - 1 + hueBins) % hueBins],
|
|
310
|
+
bins[bestIndex],
|
|
311
|
+
bins[(bestIndex + 1) % hueBins]
|
|
312
|
+
];
|
|
313
|
+
const weight = window2.reduce((sum, bin) => sum + bin.weight, 0);
|
|
314
|
+
if (weight === 0) return neutral;
|
|
315
|
+
const sumC = window2.reduce((sum, bin) => sum + bin.sumC, 0);
|
|
316
|
+
const sumL = window2.reduce((sum, bin) => sum + bin.sumL, 0);
|
|
317
|
+
const sin = window2.reduce((sum, bin) => sum + bin.sin, 0);
|
|
318
|
+
const cos = window2.reduce((sum, bin) => sum + bin.cos, 0);
|
|
319
|
+
const dilution = 0.45 + 0.55 * Math.min(1, presence / 0.3);
|
|
311
320
|
return {
|
|
312
|
-
c: Number(
|
|
313
|
-
h: Number(
|
|
314
|
-
l: Number(
|
|
321
|
+
c: Number((sumC / weight * dilution).toFixed(3)),
|
|
322
|
+
h: Number(((Math.atan2(sin, cos) * (180 / Math.PI) + 360) % 360).toFixed(1)),
|
|
323
|
+
l: Number((sumL / weight).toFixed(3))
|
|
315
324
|
};
|
|
316
325
|
}
|
|
317
326
|
function createDefaultPalette(fallback) {
|