Forráskód Böngészése

PD.ColorList refactoring

David Sehnal 5 éve
szülő
commit
3dd57e9dc8

+ 1 - 1
src/apps/state-docs/pd-to-md.ts

@@ -19,7 +19,7 @@ function paramInfo(param: PD.Any, offset: number): string {
         case 'conditioned': return getParams(param.conditionParams, offset);
         case 'multi-select': return `Array of ${oToS(param.options)}`;
         case 'color': return 'Color as 0xrrggbb';
-        case 'color-list': return `One of ${oToS(param.options)}`;
+        case 'color-list': return `A list of colors as 0xrrggbb`;
         case 'vec3': return `3D vector [x, y, z]`;
         case 'file': return `JavaScript File Handle`;
         case 'file-list': return `JavaScript FileList Handle`;

+ 1 - 1
src/examples/proteopedia-wrapper/index.ts

@@ -31,7 +31,7 @@ import { getFormattedTime } from '../../mol-util/date';
 require('../../mol-plugin-ui/skin/light.scss')
 
 class MolStarProteopediaWrapper {
-    static VERSION_MAJOR = 4;
+    static VERSION_MAJOR = 5;
     static VERSION_MINOR = 0;
 
     private _ev = RxEventHelper.create();

+ 18 - 19
src/mol-geo/geometry/direct-volume/direct-volume.ts

@@ -4,29 +4,28 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ValueCell } from '../../../mol-util'
-import { Sphere3D, Box3D } from '../../../mol-math/geometry'
-import { ParamDefinition as PD } from '../../../mol-util/param-definition';
-import { DirectVolumeValues } from '../../../mol-gl/renderable/direct-volume';
-import { Vec3, Mat4, Vec2 } from '../../../mol-math/linear-algebra';
-import { Box } from '../../primitive/box';
-import { createTransferFunctionTexture, getControlPointsFromVec2Array } from './transfer-function';
-import { Texture } from '../../../mol-gl/webgl/texture';
-import { LocationIterator } from '../../../mol-geo/util/location-iterator';
-import { TransformData } from '../transform-data';
-import { createColors } from '../color-data';
-import { createMarkers } from '../marker-data';
-import { GeometryUtils } from '../geometry';
+import { hashFnv32a } from '../../../mol-data/util';
 import { transformPositionArray } from '../../../mol-geo/util';
+import { LocationIterator } from '../../../mol-geo/util/location-iterator';
+import { RenderableState } from '../../../mol-gl/renderable';
+import { DirectVolumeValues } from '../../../mol-gl/renderable/direct-volume';
 import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
+import { Texture } from '../../../mol-gl/webgl/texture';
+import { Box3D, Sphere3D } from '../../../mol-math/geometry';
+import { Mat4, Vec2, Vec3 } from '../../../mol-math/linear-algebra';
 import { Theme } from '../../../mol-theme/theme';
-import { RenderableState } from '../../../mol-gl/renderable';
-import { ColorListOptions, ColorListName } from '../../../mol-util/color/lists';
+import { ValueCell } from '../../../mol-util';
 import { Color } from '../../../mol-util/color';
+import { ParamDefinition as PD } from '../../../mol-util/param-definition';
+import { Box } from '../../primitive/box';
 import { BaseGeometry } from '../base';
+import { createColors } from '../color-data';
+import { GeometryUtils } from '../geometry';
+import { createMarkers } from '../marker-data';
 import { createEmptyOverpaint } from '../overpaint-data';
+import { TransformData } from '../transform-data';
 import { createEmptyTransparency } from '../transparency-data';
-import { hashFnv32a } from '../../../mol-data/util';
+import { createTransferFunctionTexture, getControlPointsFromVec2Array } from './transfer-function';
 
 const VolumeBox = Box()
 const RenderModeOptions = [['isosurface', 'Isosurface'], ['volume', 'Volume']] as [string, string][]
@@ -117,7 +116,7 @@ export namespace DirectVolume {
             Vec2.create(0.19, 0.0), Vec2.create(0.2, 0.05), Vec2.create(0.25, 0.05), Vec2.create(0.26, 0.0),
             Vec2.create(0.79, 0.0), Vec2.create(0.8, 0.05), Vec2.create(0.85, 0.05), Vec2.create(0.86, 0.0),
         ]),
-        list: PD.ColorList<ColorListName>('red-yellow-blue', ColorListOptions),
+        list: PD.ColorList('red-yellow-blue'),
     }
     export type Params = typeof Params
 
@@ -148,7 +147,7 @@ export namespace DirectVolume {
         const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
 
         const controlPoints = getControlPointsFromVec2Array(props.controlPoints)
-        const transferTex = createTransferFunctionTexture(controlPoints, props.list)
+        const transferTex = createTransferFunctionTexture(controlPoints, props.list.colors)
 
         const maxSteps = Math.ceil(Vec3.magnitude(gridDimension.ref.value)) * 2 * 5
 
@@ -193,7 +192,7 @@ export namespace DirectVolume {
         ValueCell.updateIfChanged(values.dRenderMode, props.renderMode)
 
         const controlPoints = getControlPointsFromVec2Array(props.controlPoints)
-        createTransferFunctionTexture(controlPoints, props.list, values.tTransferTex)
+        createTransferFunctionTexture(controlPoints, props.list.colors, values.tTransferTex)
     }
 
     function updateBoundingSphere(values: DirectVolumeValues, directVolume: DirectVolume) {

+ 2 - 3
src/mol-model-props/computed/themes/accessible-surface-area.ts

@@ -5,7 +5,6 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ColorListName, ColorListOptionsScale } from '../../../mol-util/color/lists'
 import { ParamDefinition as PD } from '../../../mol-util/param-definition'
 import { Color, ColorScale } from '../../../mol-util/color'
 import { ThemeDataContext } from '../../../mol-theme/theme'
@@ -20,7 +19,7 @@ const DefaultColor = Color(0xFAFAFA)
 const Description = 'Assigns a color based on the relative accessible surface area of a residue.'
 
 export const AccessibleSurfaceAreaColorThemeParams = {
-    list: PD.ColorList<ColorListName>('rainbow', ColorListOptionsScale)
+    list: PD.ColorList('rainbow', { presetKind: 'scale' })
 }
 export type AccessibleSurfaceAreaColorThemeParams = typeof AccessibleSurfaceAreaColorThemeParams
 export function getAccessibleSurfaceAreaColorThemeParams(ctx: ThemeDataContext) {
@@ -30,7 +29,7 @@ export function AccessibleSurfaceAreaColorTheme(ctx: ThemeDataContext, props: PD
     let color: LocationColor
 
     const scale = ColorScale.create({
-        listOrName: props.list,
+        listOrName: props.list.colors,
         minLabel: 'buried',
         maxLabel: 'exposed',
         domain: [0.0, 1.0]

+ 3 - 4
src/mol-model-props/integrative/cross-link-restraint/color.ts

@@ -8,7 +8,6 @@ import { Color, ColorScale } from '../../../mol-util/color';
 import { Location } from '../../../mol-model/location';
 import { ParamDefinition as PD } from '../../../mol-util/param-definition'
 import { ThemeDataContext } from '../../../mol-theme/theme';
-import { ColorListName, ColorListOptionsScale } from '../../../mol-util/color/lists';
 import { ColorTheme, LocationColor } from '../../../mol-theme/color';
 import { CustomProperty } from '../../common/custom-property';
 import { CrossLinkRestraintProvider, CrossLinkRestraint } from './property';
@@ -18,7 +17,7 @@ const Description = 'Colors cross-links by the deviation of the observed distanc
 
 export const CrossLinkColorThemeParams = {
     domain: PD.Interval([0.5, 1.5], { step: 0.01 }),
-    list: PD.ColorList<ColorListName>('red-grey', ColorListOptionsScale),
+    list: PD.ColorList('red-grey', { presetKind: 'scale' }),
 }
 export type CrossLinkColorThemeParams = typeof CrossLinkColorThemeParams
 export function getCrossLinkColorThemeParams(ctx: ThemeDataContext) {
@@ -34,7 +33,7 @@ export function CrossLinkColorTheme(ctx: ThemeDataContext, props: PD.Values<Cros
     if (crossLinkRestraints) {
         scale = ColorScale.create({
             domain: props.domain,
-            listOrName: props.list
+            listOrName: props.list.colors
         })
         const scaleColor = scale.color
 
@@ -72,5 +71,5 @@ export const CrossLinkColorThemeProvider: ColorTheme.Provider<CrossLinkColorThem
     ensureCustomProperties: {
         attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? CrossLinkRestraintProvider.attach(ctx, data.structure, void 0, true) : Promise.resolve(),
         detach: (_, data) => data.structure && data.structure.customPropertyDescriptors.reference(CrossLinkRestraintProvider.descriptor, false)
-    }    
+    }
 }

+ 1 - 1
src/mol-model-props/rcsb/themes/assembly-symmetry-cluster.ts

@@ -32,7 +32,7 @@ function clusterMemberKey(asymId: string, operList: string[]) {
 }
 
 export const AssemblySymmetryClusterColorThemeParams = {
-    ...getPaletteParams({ scaleList: 'red-yellow-blue' }),
+    ...getPaletteParams({ colorList: 'red-yellow-blue' }),
 }
 export type AssemblySymmetryClusterColorThemeParams = typeof AssemblySymmetryClusterColorThemeParams
 export function getAssemblySymmetryClusterColorThemeParams(ctx: ThemeDataContext) {

+ 14 - 7
src/mol-plugin-ui/controls/action-menu.tsx

@@ -14,7 +14,7 @@ export class ActionMenu extends React.PureComponent<ActionMenu.Props> {
 
     render() {
         const cmd = this.props;
-        return <div className='msp-action-menu-options' style={{ marginTop: cmd.header ? void 0 : '1px' }}>
+        return <div className='msp-action-menu-options' style={{ /* marginTop: cmd.header ? void 0 : '1px', */ maxHeight: '300px', overflow: 'hidden', overflowY: 'auto' }}>
             {cmd.header && <ControlGroup header={cmd.header} initialExpanded={true} hideExpander={true} hideOffset={false} onHeaderClick={this.hide} topRightIcon='off'></ControlGroup>}
             <Section items={cmd.items} onSelect={cmd.onSelect} current={cmd.current} multiselect={this.props.multiselect} noOffset={this.props.noOffset} />
         </div>
@@ -29,7 +29,7 @@ export namespace ActionMenu {
 
     export type Items =  Header | Item | Items[]
     export type Header = { kind: 'header', label: string, isIndependent?: boolean, initiallyExpanded?: boolean }
-    export type Item = { kind: 'item', label: string, icon?: IconName, disabled?: boolean, selected?: boolean, value: unknown }
+    export type Item = { kind: 'item', label: string, icon?: IconName, disabled?: boolean, selected?: boolean, value: unknown, addOn?: JSX.Element }
 
     export function Header(label: string, options?: { isIndependent?: boolean, initiallyExpanded?: boolean }): Header {
         return options ? { kind: 'header', label, ...options } : { kind: 'header', label };
@@ -48,11 +48,12 @@ export namespace ActionMenu {
         value?: (t: T) => any,
         category?: (t: T) => string | undefined,
         icon?: (t: T) => IconName | undefined,
-        selected?: (t: T) => boolean | undefined
+        selected?: (t: T) => boolean | undefined,
+        addOn?: (t: T) => JSX.Element | undefined
     }
 
     export function createItems<T>(xs: ArrayLike<T>, params?: CreateItemsParams<T>) {
-        const { label, value, category, selected, icon } = params || { };
+        const { label, value, category, selected, icon, addOn } = params || { };
         let cats: Map<string, (ActionMenu.Item | ActionMenu.Header)[]> | undefined = void 0;
         const items: (ActionMenu.Item | (ActionMenu.Item | ActionMenu.Header)[] | string)[] = [];
         for (let i = 0; i < xs.length; i++) {
@@ -78,7 +79,9 @@ export namespace ActionMenu {
                 cat = items as any;
             }
 
-            cat!.push({ kind: 'item', label: l, value: v, icon: icon ? icon(x) : void 0, selected: selected ? selected(x) : void 0 });
+            const ao = addOn?.(x);
+
+            cat!.push({ kind: 'item', label: l, value: v, icon: icon ? icon(x) : void 0, selected: selected ? selected(x) : void 0, addOn: ao });
         }
         return items as ActionMenu.Items;
     }
@@ -86,7 +89,7 @@ export namespace ActionMenu {
     type Opt = ParamDefinition.Select<any>['options'][0];
     const _selectOptions = { value: (o: Opt) => o[0], label: (o: Opt) => o[1], category: (o: Opt) => o[2] };
 
-    export function createItemsFromSelectOptions(options: ParamDefinition.Select<any>['options'], params?: CreateItemsParams<ParamDefinition.Select<any>['options'][0]>) {
+    export function createItemsFromSelectOptions<O extends ParamDefinition.Select<any>['options']>(options: O, params?: CreateItemsParams<O[0]>) {
         return createItems(options, params ? { ..._selectOptions, ...params } : _selectOptions);
     }
 
@@ -219,9 +222,13 @@ const Action: React.FC<{
     multiselect: boolean | undefined,
     current: ActionMenu.Item | undefined }> = ({ item, onSelect, current, multiselect }) => {
     const isCurrent = current === item;
-    return  <button className='msp-btn msp-btn-block msp-form-control msp-action-menu-button msp-no-overflow' onClick={() => onSelect(multiselect ? [item] : item as any)} disabled={item.disabled}>
+
+    const style: React.CSSProperties | undefined = item.addOn ? { position: 'relative' } : void 0;
+
+    return <button className='msp-btn msp-btn-block msp-form-control msp-action-menu-button msp-no-overflow' onClick={() => onSelect(multiselect ? [item] : item as any)} disabled={item.disabled} style={style}>
         {item.icon && <Icon name={item.icon} />}
         {isCurrent || item.selected ? <b>{item.label}</b> : item.label}
+        {item.addOn}
     </button>;
 }
 

+ 101 - 21
src/mol-plugin-ui/controls/parameters.tsx

@@ -9,7 +9,7 @@ import * as React from 'react';
 import { Vec2, Vec3 } from '../../mol-math/linear-algebra';
 import { PluginContext } from '../../mol-plugin/context';
 import { Color } from '../../mol-util/color';
-import { ColorListName, getColorListFromName } from '../../mol-util/color/lists';
+import { ColorListOptions, ColorListOptionsScale, ColorListOptionsSet, getColorListFromName, ColorListName } from '../../mol-util/color/lists';
 import { Legend as LegendData } from '../../mol-util/legend';
 import { memoize1, memoizeLatest } from '../../mol-util/memoize';
 import { getPrecision } from '../../mol-util/number';
@@ -79,7 +79,7 @@ export class ParameterControls<P extends PD.Params> extends React.PureComponent<
         return parts;
     }
 
-    paramGroups = memoize1((params: PD.Params) => classifyParams(params));
+    paramGroups = memoizeLatest((params: PD.Params) => classifyParams(params));
 
     render() {
         const groups = this.paramGroups(this.props.params);
@@ -586,33 +586,113 @@ const colorGradientBanded = memoize1((colors: Color[]) => {
     return `linear-gradient(to right, ${styles.join(', ')})`
 });
 
-function colorGradient(name: ColorListName, banded: boolean) {
-    const { list, type } = getColorListFromName(name)
-    return type === 'qualitative' ? colorGradientBanded(list) : colorGradientInterpolated(list)
+function colorStripStyle(list: PD.ColorList['defaultValue'], right = '0'): React.CSSProperties {
+    return {
+        background: colorGradient(list.colors, list.kind === 'set'),
+        position: 'absolute',
+        bottom: '0',
+        height: '4px',
+        right,
+        left: '0'
+    };
 }
 
-export class ColorListControl extends SimpleParam<PD.ColorList<any>> {
-    onChange = (e: React.ChangeEvent<HTMLSelectElement>) => { this.update(e.target.value); }
+function colorGradient(colors: Color[], banded: boolean) {
+    return banded ? colorGradientBanded(colors) : colorGradientInterpolated(colors)
+}
 
-    stripStyle(): React.CSSProperties {
-        return {
-            background: colorGradient(this.props.value, true),
-            position: 'absolute',
-            bottom: '0',
-            height: '4px',
-            right: '0',
-            left: '0'
-        };
+function createColorListHelpers() {
+
+    const addOn = (l: [ColorListName, any, any]) => {
+        const preset = getColorListFromName(l[0]);
+        return <div style={colorStripStyle({ kind: preset.type !== 'qualitative' ? 'interpolate' : 'set', colors: preset.list })} />;
+    }
+
+    return {
+        ColorPresets: {
+            all: ActionMenu.createItemsFromSelectOptions(ColorListOptions, { addOn }),
+            scale: ActionMenu.createItemsFromSelectOptions(ColorListOptionsScale, { addOn }),
+            set: ActionMenu.createItemsFromSelectOptions(ColorListOptionsSet, { addOn })
+        },
+        ColorsParam: PD.ObjectList({ color: PD.Color(0x0 as Color) }, ({ color }) => Color.toHexString(color).toUpperCase()),
+        IsInterpolatedParam: PD.Boolean(false, { label: 'Interpolated' })
+    };
+}
+
+let _colorListHelpers: ReturnType<typeof createColorListHelpers>
+function ColorListHelpers() {
+    if (_colorListHelpers) return _colorListHelpers;
+    _colorListHelpers = createColorListHelpers();
+    return _colorListHelpers;
+}
+
+export class ColorListControl extends React.PureComponent<ParamProps<PD.ColorList>, { showHelp: boolean, show?: 'edit' | 'presets' }> {
+    state = { showHelp: false, show: void 0 as 'edit' | 'presets' | undefined };
+
+    protected update(value: PD.ColorList['defaultValue']) {
+        this.props.onChange({ param: this.props.param, name: this.props.name, value });
     }
 
+    toggleEdit = () => this.setState({ show: this.state.show === 'edit' ? void 0 : 'edit' });
+    togglePresets = () => this.setState({ show: this.state.show === 'presets' ? void 0 : 'presets' });
+
     renderControl() {
-        return <div style={{ position: 'relative' }}>
-            <select value={this.props.value || ''} onChange={this.onChange} disabled={this.props.isDisabled}>
-                {this.props.param.options.map(([value, label]) => <option key={value} value={value}>{label}</option>)}
-            </select>
-            <div style={this.stripStyle()} />
+        const { value } = this.props;
+        // TODO: fix the button right offset
+        return <>
+            <button onClick={this.toggleEdit} style={{ position: 'relative', paddingRight: '33px' }}>
+                {value.colors.length === 1 ? '1 color' : `${value.colors.length} colors`}
+                <div style={colorStripStyle(value, '33px')} />
+            </button>
+            <IconButton icon='bookmarks' onClick={this.togglePresets} toggleState={this.state.show === 'presets'} title='Color Presets'
+                style={{ padding: 0, position: 'absolute', right: 0, top: 0, width: '32px' }} />
+        </>
+    }
+
+    selectPreset: ActionMenu.OnSelect = item => {
+        if (!item) return;
+        this.setState({ show: void 0 });
+
+        const preset = getColorListFromName(item.value as ColorListName);
+        this.update({ kind: preset.type !== 'qualitative' ? 'interpolate' : 'set', colors: preset.list });
+    }
+
+    colorsChanged: ParamOnChange = ({ value }) => {
+        this.update({
+            kind: this.props.value.kind,
+            colors: (value as (typeof _colorListHelpers)['ColorsParam']['defaultValue']).map(c => c.color)
+        });
+    }
+
+    isInterpolatedChanged: ParamOnChange = ({ value }) => {
+        this.update({ kind: value ? 'interpolate' : 'set', colors: this.props.value.colors })
+    }
+
+    renderColors() {
+        if (!this.state.show) return null;
+        const { ColorPresets, ColorsParam, IsInterpolatedParam } = ColorListHelpers();
+
+        const preset = ColorPresets[this.props.param.presetKind];
+        if (this.state.show === 'presets') return <ActionMenu items={preset} onSelect={this.selectPreset} />;
+
+        const values = this.props.value.colors.map(color => ({ color }));
+        return <div className='msp-control-offset'>
+            <ObjectListControl name='colors' param={ColorsParam} value={values} onChange={this.colorsChanged} isDisabled={this.props.isDisabled} onEnter={this.props.onEnter} />
+            <BoolControl name='isInterpolated' param={IsInterpolatedParam} value={this.props.value.kind === 'interpolate'} onChange={this.isInterpolatedChanged} isDisabled={this.props.isDisabled} onEnter={this.props.onEnter} />
         </div>;
     }
+
+    toggleHelp = () => this.setState({ showHelp: !this.state.showHelp });
+
+    render() {
+        return renderSimple({
+            props: this.props,
+            state: this.state,
+            control: this.renderControl(),
+            toggleHelp: this.toggleHelp,
+            addOn: this.renderColors()
+        });
+    }
 }
 
 export class Vec3Control extends React.PureComponent<ParamProps<PD.Vec3>, { isExpanded: boolean }> {

+ 5 - 0
src/mol-plugin-ui/skin/base/components/misc.scss

@@ -177,11 +177,16 @@
         }
     }
 
+    > button:first-child {
+        right: 33px;
+    }
+
     > .msp-btn-icon {
         position: absolute;
         right: 0;
         width: 32px;
         top: 0;
+        padding: 0;
     }
 }
 

+ 1 - 1
src/mol-plugin-ui/structure/components.tsx

@@ -237,7 +237,7 @@ class StructureComponentGroup extends PurePluginUIComponent<{ group: StructureCo
         const name = repr.cell.transform.params?.colorTheme.name;
         const themes = getStructureThemeTypes(this.plugin, this.pivot.cell.obj?.data);
         return ActionMenu.createItemsFromSelectOptions(themes, {
-            value: o => () => mng.updateRepresentationsTheme(this.props.group, { color: o[0] }),
+            value: o => () => mng.updateRepresentationsTheme(this.props.group, { color: o[0] as any }),
             selected: o => o[0] === name
         }) as ActionMenu.Item[];
     }

+ 2 - 2
src/mol-plugin-ui/structure/selection.tsx

@@ -11,7 +11,7 @@ import { InteractivityManager } from '../../mol-plugin-state/manager/interactivi
 import { StructureComponentManager } from '../../mol-plugin-state/manager/structure/component';
 import { StructureRef } from '../../mol-plugin-state/manager/structure/hierarchy-state';
 import { StructureSelectionModifier } from '../../mol-plugin-state/manager/structure/selection';
-import { memoize1 } from '../../mol-util/memoize';
+import { memoizeLatest } from '../../mol-util/memoize';
 import { ParamDefinition } from '../../mol-util/param-definition';
 import { stripTags } from '../../mol-util/string';
 import { CollapsableControls, CollapsableState, PurePluginUIComponent } from '../base';
@@ -177,7 +177,7 @@ interface ApplyColorControlsProps {
 }
 
 class ApplyColorControls extends PurePluginUIComponent<ApplyColorControlsProps, ApplyColorControlsState> {
-    _params = memoize1((pivot: StructureRef | undefined) => StructureComponentManager.getColorParams(this.plugin, pivot));
+    _params = memoizeLatest((pivot: StructureRef | undefined) => StructureComponentManager.getColorParams(this.plugin, pivot));
     get params() { return this._params(this.plugin.managers.structure.component.pivotStructure); }
 
     state = { values: ParamDefinition.getDefaultValues(this.params) };

+ 4 - 7
src/mol-theme/color/chain-id.ts

@@ -13,27 +13,24 @@ import { ThemeDataContext } from '../../mol-theme/theme';
 import { getPaletteParams, getPalette } from '../../mol-util/color/palette';
 import { TableLegend, ScaleLegend } from '../../mol-util/legend';
 import { Segmentation } from '../../mol-data/int';
-import { ColorLists } from '../../mol-util/color/lists';
+import { ColorLists, getColorListFromName } from '../../mol-util/color/lists';
 
 const DefaultList = 'dark-2'
 const DefaultColor = Color(0xFAFAFA)
 const Description = 'Gives every chain a color based on its `asym_id` value.'
 
 export const ChainIdColorThemeParams = {
-    ...getPaletteParams({ type: 'set', setList: DefaultList }),
+    ...getPaletteParams({ type: 'colors', colorList: DefaultList }),
 }
 export type ChainIdColorThemeParams = typeof ChainIdColorThemeParams
 export function getChainIdColorThemeParams(ctx: ThemeDataContext) {
     const params = PD.clone(ChainIdColorThemeParams)
     if (ctx.structure) {
         if (getAsymIdSerialMap(ctx.structure.root).size > ColorLists[DefaultList].list.length) {
-            params.palette.defaultValue.name = 'scale'
+            params.palette.defaultValue.name = 'colors'
             params.palette.defaultValue.params = {
                 ...params.palette.defaultValue.params,
-                list: {
-                    name: 'predefined',
-                    params: 'red-yellow-blue'
-                }
+                list: { kind: 'interpolate', colors: getColorListFromName(DefaultList).list }
             }
         }
     }

+ 1 - 1
src/mol-theme/color/element-index.ts

@@ -18,7 +18,7 @@ const DefaultColor = Color(0xCCCCCC)
 const Description = 'Gives every element (atom or coarse sphere/gaussian) a unique color based on the position (index) of the element in the list of elements in the structure.'
 
 export const ElementIndexColorThemeParams = {
-    ...getPaletteParams({ type: 'scale', scaleList: 'red-yellow-blue' }),
+    ...getPaletteParams({ type: 'colors', colorList: 'red-yellow-blue' }),
 }
 export type ElementIndexColorThemeParams = typeof ElementIndexColorThemeParams
 export function getElementIndexColorThemeParams(ctx: ThemeDataContext) {

+ 4 - 4
src/mol-theme/color/entity-source.ts

@@ -15,7 +15,7 @@ import { mmCIF_Schema } from '../../mol-io/reader/cif/schema/mmcif';
 import { getPaletteParams, getPalette } from '../../mol-util/color/palette';
 import { TableLegend, ScaleLegend } from '../../mol-util/legend';
 import { isInteger } from '../../mol-util/number';
-import { ColorLists } from '../../mol-util/color/lists';
+import { ColorLists, getColorListFromName } from '../../mol-util/color/lists';
 import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
 
 const DefaultList = 'dark-2'
@@ -23,17 +23,17 @@ const DefaultColor = Color(0xFAFAFA)
 const Description = 'Gives ranges of a polymer chain a color based on the entity source it originates from (e.g. gene, plasmid, organism).'
 
 export const EntitySourceColorThemeParams = {
-    ...getPaletteParams({ type: 'set', setList: DefaultList }),
+    ...getPaletteParams({ type: 'colors', colorList: DefaultList }),
 }
 export type EntitySourceColorThemeParams = typeof EntitySourceColorThemeParams
 export function getEntitySourceColorThemeParams(ctx: ThemeDataContext) {
     const params = PD.clone(EntitySourceColorThemeParams)
     if (ctx.structure) {
         if (getMaps(ctx.structure.root.models).srcKeySerialMap.size > ColorLists[DefaultList].list.length) {
-            params.palette.defaultValue.name = 'scale'
+            params.palette.defaultValue.name = 'colors'
             params.palette.defaultValue.params = {
                 ...params.palette.defaultValue.params,
-                list: 'red-yellow-blue'
+                list: { kind: 'interpolate', colors: getColorListFromName(DefaultList).list }
             }
         }
     }

+ 2 - 3
src/mol-theme/color/hydrophobicity.ts

@@ -11,12 +11,11 @@ import { ColorTheme } from '../color';
 import { ParamDefinition as PD } from '../../mol-util/param-definition'
 import { ThemeDataContext } from '../theme';
 import { ResidueHydrophobicity } from '../../mol-model/structure/model/types';
-import { ColorListName, ColorListOptionsScale } from '../../mol-util/color/lists';
 
 const Description = 'Assigns a color to every amino acid according to the "Experimentally determined hydrophobicity scale for proteins at membrane interfaces" by Wimely and White (doi:10.1038/nsb1096-842).'
 
 export const HydrophobicityColorThemeParams = {
-    list: PD.ColorList<ColorListName>('red-yellow-green', ColorListOptionsScale),
+    list: PD.ColorList('red-yellow-green', { presetKind: 'scale' }),
     scale: PD.Select('DGwif', [['DGwif', 'DG water-membrane'], ['DGwoct', 'DG water-octanol'], ['Oct-IF', 'DG difference']] as const)
 }
 export type HydrophobicityColorThemeParams = typeof HydrophobicityColorThemeParams
@@ -58,7 +57,7 @@ export function HydrophobicityColorTheme(ctx: ThemeDataContext, props: PD.Values
     }
 
     const scale = ColorScale.create({
-        listOrName: props.list,
+        listOrName: props.list.colors,
         domain: [ max, min ],
         minLabel: 'Hydrophobic',
         maxLabel: 'Hydrophilic'

+ 1 - 1
src/mol-theme/color/model-index.ts

@@ -17,7 +17,7 @@ const DefaultColor = Color(0xCCCCCC)
 const Description = 'Gives every model a unique color based on the position (index) of the model in the list of models in the structure.'
 
 export const ModelIndexColorThemeParams = {
-    ...getPaletteParams({ type: 'scale', scaleList: 'purples' }),
+    ...getPaletteParams({ type: 'colors', colorList: 'purples' }),
 }
 export type ModelIndexColorThemeParams = typeof ModelIndexColorThemeParams
 export function getModelIndexColorThemeParams(ctx: ThemeDataContext) {

+ 2 - 3
src/mol-theme/color/occupancy.ts

@@ -10,14 +10,13 @@ import { Location } from '../../mol-model/location';
 import { ColorTheme } from '../color';
 import { ParamDefinition as PD } from '../../mol-util/param-definition'
 import { ThemeDataContext } from '../theme';
-import { ColorListName, ColorListOptionsScale } from '../../mol-util/color/lists';
 
 const DefaultOccupancyColor = Color(0xCCCCCC)
 const Description = `Assigns a color based on the occupancy of an atom.`
 
 export const OccupancyColorThemeParams = {
     domain: PD.Interval([0, 1]),
-    list: PD.ColorList<ColorListName>('purples', ColorListOptionsScale),
+    list: PD.ColorList('purples', { presetKind: 'scale' }),
 }
 export type OccupancyColorThemeParams = typeof OccupancyColorThemeParams
 export function getOccupancyColorThemeParams(ctx: ThemeDataContext) {
@@ -36,7 +35,7 @@ export function OccupancyColorTheme(ctx: ThemeDataContext, props: PD.Values<Occu
     const scale = ColorScale.create({
         reverse: false,
         domain: props.domain,
-        listOrName: props.list,
+        listOrName: props.list.colors,
     })
 
     function color(location: Location): Color {

+ 4 - 7
src/mol-theme/color/operator-hkl.ts

@@ -14,27 +14,24 @@ import { getPaletteParams, getPalette } from '../../mol-util/color/palette';
 import { ScaleLegend, TableLegend } from '../../mol-util/legend';
 import { Vec3 } from '../../mol-math/linear-algebra';
 import { integerDigitCount } from '../../mol-util/number';
-import { ColorLists } from '../../mol-util/color/lists';
+import { ColorLists, getColorListFromName } from '../../mol-util/color/lists';
 
 const DefaultList = 'dark-2'
 const DefaultColor = Color(0xCCCCCC)
 const Description = `Assigns a color based on the operator HKL value of a transformed chain.`
 
 export const OperatorHklColorThemeParams = {
-    ...getPaletteParams({ type: 'set', setList: DefaultList }),
+    ...getPaletteParams({ type: 'colors', colorList: DefaultList }),
 }
 export type OperatorHklColorThemeParams = typeof OperatorHklColorThemeParams
 export function getOperatorHklColorThemeParams(ctx: ThemeDataContext) {
     const params = PD.clone(OperatorHklColorThemeParams)
     if (ctx.structure) {
         if (getOperatorHklSerialMap(ctx.structure.root).map.size > ColorLists[DefaultList].list.length) {
-            params.palette.defaultValue.name = 'scale'
+            params.palette.defaultValue.name = 'colors'
             params.palette.defaultValue.params = {
                 ...params.palette.defaultValue.params,
-                list: {
-                    name: 'predefined',
-                    params: 'red-yellow-blue'
-                }
+                list: { kind: 'interpolate', colors: getColorListFromName(DefaultList).list }
             }
         }
     }

+ 4 - 7
src/mol-theme/color/operator-name.ts

@@ -12,27 +12,24 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition'
 import { ThemeDataContext } from '../theme';
 import { getPaletteParams, getPalette } from '../../mol-util/color/palette';
 import { ScaleLegend, TableLegend } from '../../mol-util/legend';
-import { ColorLists } from '../../mol-util/color/lists';
+import { ColorLists, getColorListFromName } from '../../mol-util/color/lists';
 
 const DefaultList = 'dark-2'
 const DefaultColor = Color(0xCCCCCC)
 const Description = `Assigns a color based on the operator name of a transformed chain.`
 
 export const OperatorNameColorThemeParams = {
-    ...getPaletteParams({ type: 'set', setList: DefaultList }),
+    ...getPaletteParams({ type: 'colors', colorList: DefaultList }),
 }
 export type OperatorNameColorThemeParams = typeof OperatorNameColorThemeParams
 export function getOperatorNameColorThemeParams(ctx: ThemeDataContext) {
     const params = PD.clone(OperatorNameColorThemeParams)
     if (ctx.structure) {
         if (getOperatorNameSerialMap(ctx.structure.root).size > ColorLists[DefaultList].list.length) {
-            params.palette.defaultValue.name = 'scale'
+            params.palette.defaultValue.name = 'colors'
             params.palette.defaultValue.params = {
                 ...params.palette.defaultValue.params,
-                list: {
-                    name: 'predefined',
-                    params: 'red-yellow-blue'
-                }
+                list: { kind: 'interpolate', colors: getColorListFromName(DefaultList).list }
             }
         }
     }

+ 4 - 7
src/mol-theme/color/polymer-id.ts

@@ -14,27 +14,24 @@ import { ThemeDataContext } from '../../mol-theme/theme';
 import { getPalette, getPaletteParams } from '../../mol-util/color/palette';
 import { TableLegend, ScaleLegend } from '../../mol-util/legend';
 import { Segmentation } from '../../mol-data/int';
-import { ColorLists } from '../../mol-util/color/lists';
+import { ColorLists, getColorListFromName } from '../../mol-util/color/lists';
 
 const DefaultList = 'dark-2'
 const DefaultColor = Color(0xFAFAFA)
 const Description = 'Gives every polymer chain a color based on its `asym_id` value.'
 
 export const PolymerIdColorThemeParams = {
-    ...getPaletteParams({ type: 'set', setList: DefaultList }),
+    ...getPaletteParams({ type: 'colors', colorList: DefaultList }),
 }
 export type PolymerIdColorThemeParams = typeof PolymerIdColorThemeParams
 export function getPolymerIdColorThemeParams(ctx: ThemeDataContext) {
     const params = PD.clone(PolymerIdColorThemeParams)
     if (ctx.structure) {
         if (getPolymerAsymIdSerialMap(ctx.structure.root).size > ColorLists[DefaultList].list.length) {
-            params.palette.defaultValue.name = 'scale'
+            params.palette.defaultValue.name = 'colors'
             params.palette.defaultValue.params = {
                 ...params.palette.defaultValue.params,
-                list: {
-                    name: 'predefined',
-                    params: 'red-yellow-blue'
-                }
+                list: { kind: 'interpolate', colors: getColorListFromName(DefaultList).list }
             }
         }
     }

+ 4 - 7
src/mol-theme/color/polymer-index.ts

@@ -12,27 +12,24 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition'
 import { ThemeDataContext } from '../../mol-theme/theme';
 import { TableLegend, ScaleLegend } from '../../mol-util/legend';
 import { getPaletteParams, getPalette } from '../../mol-util/color/palette';
-import { ColorLists } from '../../mol-util/color/lists';
+import { ColorLists, getColorListFromName } from '../../mol-util/color/lists';
 
 const DefaultList = 'dark-2'
 const DefaultColor = Color(0xCCCCCC)
 const Description = 'Gives every polymer chain instance a unique color based on the position (index) of the polymer in the list of polymers in the structure.'
 
 export const PolymerIndexColorThemeParams = {
-    ...getPaletteParams({ type: 'set', setList: DefaultList }),
+    ...getPaletteParams({ type: 'colors', colorList: DefaultList }),
 }
 export type PolymerIndexColorThemeParams = typeof PolymerIndexColorThemeParams
 export function getPolymerIndexColorThemeParams(ctx: ThemeDataContext) {
     const params = PD.clone(PolymerIndexColorThemeParams)
     if (ctx.structure) {
         if (getPolymerChainCount(ctx.structure.root) > ColorLists[DefaultList].list.length) {
-            params.palette.defaultValue.name = 'scale'
+            params.palette.defaultValue.name = 'colors'
             params.palette.defaultValue.params = {
                 ...params.palette.defaultValue.params,
-                list: {
-                    name: 'predefined',
-                    params: 'red-yellow-blue'
-                }
+                list: { kind: 'interpolate', colors: getColorListFromName(DefaultList).list }
             }
         }
     }

+ 2 - 3
src/mol-theme/color/sequence-id.ts

@@ -11,13 +11,12 @@ import { Location } from '../../mol-model/location';
 import { ColorTheme } from '../color';
 import { ParamDefinition as PD } from '../../mol-util/param-definition'
 import { ThemeDataContext } from '../../mol-theme/theme';
-import { ColorListOptionsScale, ColorListName } from '../../mol-util/color/lists';
 
 const DefaultColor = Color(0xCCCCCC)
 const Description = 'Gives every polymer residue a color based on its `seq_id` value.'
 
 export const SequenceIdColorThemeParams = {
-    list: PD.ColorList<ColorListName>('turbo', ColorListOptionsScale),
+    list: PD.ColorList('turbo', { presetKind: 'scale' }),
 }
 export type SequenceIdColorThemeParams = typeof SequenceIdColorThemeParams
 export function getSequenceIdColorThemeParams(ctx: ThemeDataContext) {
@@ -66,7 +65,7 @@ function getSequenceLength(unit: Unit, element: ElementIndex) {
 
 export function SequenceIdColorTheme(ctx: ThemeDataContext, props: PD.Values<SequenceIdColorThemeParams>): ColorTheme<SequenceIdColorThemeParams> {
     const scale = ColorScale.create({
-        listOrName: props.list,
+        listOrName: props.list.colors,
         minLabel: 'Start',
         maxLabel: 'End',
     })

+ 2 - 3
src/mol-theme/color/uncertainty.ts

@@ -10,14 +10,13 @@ import { Location } from '../../mol-model/location';
 import { ColorTheme } from '../color';
 import { ParamDefinition as PD } from '../../mol-util/param-definition'
 import { ThemeDataContext } from '../theme';
-import { ColorListName, ColorListOptionsScale } from '../../mol-util/color/lists';
 
 const DefaultUncertaintyColor = Color(0xffff99)
 const Description = `Assigns a color based on the uncertainty or disorder of an element's position, e.g. B-factor or RMSF, depending on the data availability and experimental technique.`
 
 export const UncertaintyColorThemeParams = {
     domain: PD.Interval([0, 100]),
-    list: PD.ColorList<ColorListName>('red-white-blue', ColorListOptionsScale),
+    list: PD.ColorList('red-white-blue', { presetKind: 'scale' }),
 }
 export type UncertaintyColorThemeParams = typeof UncertaintyColorThemeParams
 export function getUncertaintyColorThemeParams(ctx: ThemeDataContext) {
@@ -38,7 +37,7 @@ export function UncertaintyColorTheme(ctx: ThemeDataContext, props: PD.Values<Un
     const scale = ColorScale.create({
         reverse: true,
         domain: props.domain,
-        listOrName: props.list,
+        listOrName: props.list.colors,
     })
 
     // TODO calc domain based on data, set min/max as 10/90 percentile to be robust against outliers

+ 4 - 7
src/mol-theme/color/unit-index.ts

@@ -12,27 +12,24 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition'
 import { ThemeDataContext } from '../../mol-theme/theme';
 import { getPaletteParams, getPalette } from '../../mol-util/color/palette';
 import { TableLegend, ScaleLegend } from '../../mol-util/legend';
-import { ColorLists } from '../../mol-util/color/lists';
+import { ColorLists, getColorListFromName } from '../../mol-util/color/lists';
 
 const DefaultList = 'dark-2'
 const DefaultColor = Color(0xCCCCCC)
 const Description = 'Gives every chain instance (single chain or collection of single elements) a unique color based on the position (index) of the chain in the list of chains in the structure.'
 
 export const UnitIndexColorThemeParams = {
-    ...getPaletteParams({ type: 'set', setList: DefaultList }),
+    ...getPaletteParams({ type: 'colors', colorList: DefaultList }),
 }
 export type UnitIndexColorThemeParams = typeof UnitIndexColorThemeParams
 export function getUnitIndexColorThemeParams(ctx: ThemeDataContext) {
     const params = PD.clone(UnitIndexColorThemeParams)
     if (ctx.structure) {
         if (ctx.structure.root.units.length > ColorLists[DefaultList].list.length) {
-            params.palette.defaultValue.name = 'scale'
+            params.palette.defaultValue.name = 'colors'
             params.palette.defaultValue.params = {
                 ...params.palette.defaultValue.params,
-                list: {
-                    name: 'predefined',
-                    params: 'red-yellow-blue'
-                }
+                list: { kind: 'interpolate', colors: getColorListFromName(DefaultList).list }
             }
         }
     }

+ 2 - 1
src/mol-util/color/lists.ts

@@ -5,6 +5,7 @@
  */
 
 import { ColorList } from './color';
+import { capitalize } from '../string';
 
 export const ColorLists = {
     /**
@@ -205,7 +206,7 @@ export const ColorLists = {
 
 export type ColorListName = keyof typeof ColorLists
 export const ColorListNames = Object.keys(ColorLists)
-export const ColorListOptions = ColorListNames.map(n => [n, ColorLists[n as ColorListName].label] as [ColorListName, string])
+export const ColorListOptions = ColorListNames.map(n => [n, ColorLists[n as ColorListName].label, capitalize(ColorLists[n as ColorListName].type)] as [ColorListName, string, string])
 
 export const ColorListOptionsScale = ColorListOptions.filter(v => ColorLists[v[0]].type === 'diverging' || ColorLists[v[0]].type === 'sequential')
 export const ColorListOptionsSet = ColorListOptions.filter(v => ColorLists[v[0]].type === 'qualitative')

+ 20 - 35
src/mol-util/color/palette.ts

@@ -5,45 +5,34 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { ParamDefinition as PD } from '../param-definition'
-import { DistinctColorsParams, distinctColors } from './distinct';
-import { ColorScale } from './scale';
 import { Color } from '.';
-import { ColorListName, ColorListOptionsScale, ColorListOptionsSet, getColorListFromName } from './lists';
-import { TableLegend, ScaleLegend } from '../legend';
-import { ColorNames } from './names';
+import { ScaleLegend, TableLegend } from '../legend';
+import { ParamDefinition as PD } from '../param-definition';
+import { distinctColors, DistinctColorsParams } from './distinct';
+import { ColorListName, getColorListFromName } from './lists';
+import { ColorScale } from './scale';
 
-type PaletteType = 'generate' | 'scale' | 'set'
+type PaletteType = 'generate' | 'colors'
 
 const DefaultGetPaletteProps = {
     type: 'generate' as PaletteType,
-    scaleList: 'red-yellow-blue' as ColorListName,
-    setList: 'set-1' as ColorListName
+    colorList: 'red-yellow-blue' as ColorListName
 }
 type GetPaletteProps = typeof DefaultGetPaletteProps
 
 const LabelParams = {
     valueLabel: PD.Value((i: number) => `${i + 1}`, { isHidden: true }),
     minLabel: PD.Value('Start', { isHidden: true }),
-    maxLabel: PD.Value('End', { isHidden: true }),
+    maxLabel: PD.Value('End', { isHidden: true })
 }
 
 export function getPaletteParams(props: Partial<GetPaletteProps> = {}) {
     const p = { ...DefaultGetPaletteProps, ...props }
     return {
         palette: PD.MappedStatic(p.type, {
-            scale: PD.Group({
-                ...LabelParams,
-                list: PD.MappedStatic('predefined', {
-                    predefined: PD.ColorList<ColorListName>(p.scaleList, ColorListOptionsScale),
-                    custom: PD.ObjectList({ color: PD.Color(0x0 as Color) }, ({ color }) => Color.toHexString(color), {
-                        defaultValue: [ { color: ColorNames.red }, { color: ColorNames.green }, { color: ColorNames.blue } ]
-                    })
-                })
-            }, { isFlat: true }),
-            set: PD.Group({
+            colors: PD.Group({
                 ...LabelParams,
-                list: PD.ColorList<ColorListName>(p.setList, ColorListOptionsSet),
+                list: PD.ColorList(p.colorList),
             }, { isFlat: true }),
             generate: PD.Group({
                 ...LabelParams,
@@ -52,8 +41,7 @@ export function getPaletteParams(props: Partial<GetPaletteProps> = {}) {
             }, { isFlat: true })
         }, {
             options: [
-                ['scale', 'Interpolate'],
-                ['set', 'From Set'],
+                ['colors', 'Color List'],
                 ['generate', 'Generate Distinct']
             ]
         })
@@ -72,26 +60,23 @@ export function getPalette(count: number, props: PaletteProps) {
     let color: (i: number) => Color
     let legend: ScaleLegend | TableLegend | undefined
 
-    if (props.palette.name === 'scale') {
+    if (props.palette.name === 'colors' && props.palette.params.list.kind === 'interpolate') {
         const { list, minLabel, maxLabel } = props.palette.params
         const domain: [number, number] = [0, count - 1]
 
-        let listOrName: ColorListName | Color[];
-        if (list.name === 'predefined') {
-            listOrName = list.params;
-        } else {
-            listOrName = list.params.map(c => c.color);
-            if (listOrName.length === 0) listOrName = [ColorNames.black];
-        }
+        console.log(list.colors);
+
+        let colors = list.colors;
+        if (colors.length === 0) colors = getColorListFromName(DefaultGetPaletteProps.colorList).list
 
-        const scale = ColorScale.create({ listOrName, domain, minLabel, maxLabel })
+        const scale = ColorScale.create({ listOrName: colors, domain, minLabel, maxLabel })
         legend = scale.legend
         color = scale.color
     } else {
         let colors: Color[]
-        if (props.palette.name === 'set') {
-            const listOrName = props.palette.params.list
-            colors = typeof listOrName === 'string' ? getColorListFromName(listOrName).list : listOrName
+        if (props.palette.name === 'colors') {
+            colors = props.palette.params.list.colors;
+            if (colors.length === 0) colors = getColorListFromName('dark-2').list
         } else {
             count = Math.min(count, props.palette.params.maxCount)
             colors = distinctColors(count, props.palette.params)

+ 17 - 10
src/mol-util/param-definition.ts

@@ -12,6 +12,7 @@ import { deepClone } from './object';
 import { Script as ScriptData } from '../mol-script/script';
 import { Legend } from './legend';
 import { stringToWords } from './string';
+import { getColorListFromName, ColorListName } from './color/lists';
 
 export namespace ParamDefinition {
     export interface Info {
@@ -80,15 +81,6 @@ export namespace ParamDefinition {
         return setInfo<Select<T>>({ type: 'select', defaultValue: checkDefaultKey(defaultValue, options), options, cycle: info?.cycle }, info)
     }
 
-    export interface ColorList<T extends string> extends Base<T> {
-        type: 'color-list'
-        /** array of (value, label) tuples */
-        options: [T, string][]
-    }
-    export function ColorList<T extends string>(defaultValue: T, options: [T, string][], info?: Info): ColorList<T> {
-        return setInfo<ColorList<T>>({ type: 'color-list', defaultValue, options }, info)
-    }
-
     export interface MultiSelect<E extends string, T = E[]> extends Base<T> {
         type: 'multi-select'
         /** array of (value, label) tuples */
@@ -126,6 +118,21 @@ export namespace ParamDefinition {
         return ret;
     }
 
+    export interface ColorList extends Base<{ kind: 'interpolate' | 'set', colors: ColorData[] }> {
+        type: 'color-list'
+        presetKind: 'all' | 'scale' | 'set'
+    }
+    export function ColorList(defaultValue: { kind: 'interpolate' | 'set', colors: ColorData[] } | ColorListName, info?: Info & { presetKind?: ColorList['presetKind'] }): ColorList {
+        let def: ColorList['defaultValue'];
+        if (typeof defaultValue === 'string') {
+            const colors = getColorListFromName(defaultValue);
+            def = { kind: colors.type !== 'qualitative' ? 'interpolate' : 'set', colors: colors.list };
+        } else {
+            def = defaultValue;
+        }
+        return setInfo<ColorList>({ type: 'color-list', presetKind: info?.presetKind || 'all', defaultValue: def }, info)
+    }
+
     export interface Vec3 extends Base<Vec3Data>, Range {
         type: 'vec3'
     }
@@ -284,7 +291,7 @@ export namespace ParamDefinition {
 
     export type Any =
         | Value<any> | Select<any> | MultiSelect<any> | BooleanParam | Text | Color | Vec3 | Numeric | FileParam | FileListParam | Interval | LineGraph
-        | ColorList<any> | Group<any> | Mapped<any> | Converted<any, any> | Conditioned<any, any, any> | Script | ObjectList
+        | ColorList | Group<any> | Mapped<any> | Converted<any, any> | Conditioned<any, any, any> | Script | ObjectList
 
     export type Params = { [k: string]: Any }
     export type Values<T extends Params> = { [k in keyof T]: T[k]['defaultValue'] }