import type { IObjectOf } from "@thi.ng/api" ; import { isMobile } from "@thi.ng/checks/is-mobile" ; import { isString } from "@thi.ng/checks/is-string" ; import type { ColorRangePreset , ColorThemePart , CSSColorName , } from "@thi.ng/color" ; import { colorsFromTheme } from "@thi.ng/color/color-range" ; import { distCIEDE2000 } from "@thi.ng/color/distance" ; import { type , LCH , lch } from "@thi.ng/color/lch/lch" ; import { proximity , sort } from "@thi.ng/color/sort" ; import { SYSTEM } from "@thi.ng/random/system" ; import { XsAdd } from "@thi.ng/random/xsadd" ; import { debounce } from "@thi.ng/rstream/debounce" ; import { reactive , stream , Stream } from "@thi.ng/rstream/stream" ; import { sync } from "@thi.ng/rstream/sync" ; import { map } from "@thi.ng/transducers/map" ; import { type , MainInputs , type , MainOutputs , RANGE_IDs } from "./api" ; import { downloadACT } from "./palette" ; import { attachSerializer , initFromHash } from "./serialize" ; const themePart = ( range , base , weight = 1 ) => reactive < ColorThemePart > ( { range , base : isString ( base ) ? lch ( base ) : base , weight , } ) ; export const randomizeThemeParts = ( ) => { for ( let part of Object . values ( parts ) ) { part . next ( { range : RANGE_IDs [ SYSTEM . int ( ) % RANGE_IDs . length ] , base : lch . random ( ) , weight : SYSTEM . float ( ) , } ) ; } } ; param state export const computeSwatches = ( state ) => { const { parts , num , variance , seed , sorted } = state ; const colors = [ ... colorsFromTheme ( Object . values ( parts ) , { num , variance , rnd : new XsAdd ( seed ) , } ) , ] ; if ( sorted ) { sort ( colors , proximity ( lch ( 1 , 0 , 0 ) , distCIEDE2000 ( ) ) ) ; } return { ... state , colors } ; } ; export const parts = { 0 : themePart ( "bright" , "goldenrod" ) , 1 : themePart ( "hard" , "turquoise" , 0.33 ) , 2 : themePart ( "cool" , "fuchsia" , 0.5 ) , 3 : themePart ( "warm" , "seagreen" , 0.1 ) , } ; export const debouncedParts = sync ( { src : parts , id : "parts" } ) . subscribe ( debounce ( 1 ) ) ; export const num = reactive ( isMobile ( ) ? 128 : 256 , { id : "num" } ) ; export const variance = reactive ( 0.05 , { id : "variance" } ) ; export const sorted = reactive ( false , { id : "sorted" } ) ; export const seed = reactive ( 0xdecafbad , { id : "seed" } ) ; initFromHash ( parts , seed , num , variance ) ; const mainInputs = < const > { parts : debouncedParts , num , variance , seed , sorted , } ; export const main = sync < typeof mainInputs , MainOutputs > ( { src : mainInputs , xform : map ( computeSwatches ) , id : "main" , } ) ; attachSerializer ( main ) ; export const downloadTrigger = stream < boolean > ( ) ; sync ( { src : { main , trigger : downloadTrigger } , reset : true , } ) . subscribe ( { next : ( state ) => downloadACT ( state . main . colors ) , } ) ;