@kolkrabbi/kol-component 0.225.0 → 0.227.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolkrabbi/kol-component",
3
- "version": "0.225.0",
3
+ "version": "0.227.0",
4
4
  "description": "KOL design-system components — atoms through organisms, emitting canonical kol-* classes. Pairs with @kolkrabbi/kol-theme for styling.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -35,7 +35,7 @@
35
35
  "react-dom": "^18.3.0 || ^19.0.0"
36
36
  },
37
37
  "devDependencies": {
38
- "@kolkrabbi/kol-icons": "^0.27.1"
38
+ "@kolkrabbi/kol-icons": "^0.29.0"
39
39
  },
40
40
  "files": [
41
41
  "src",
@@ -56,6 +56,9 @@ import { glyphSize } from '../hooks/glyphLadders.js'
56
56
  * iconLeft/prefix — the paint-bar anatomy ([swatch] FFFFFF is ONE
57
57
  * container, not two boxes; ColorSwatchFieldSizing 2026-08-12). The
58
58
  * consumer owns the node's sizing; the shell's padding frames it.
59
+ * slotRight — its trailing mirror: an interactive node inside the shell, after the unit /
60
+ * suffix — a combo field's chevron (the design editor's type size, 2026-09-27: the chevron
61
+ * sat OUTSIDE the field as a second control). Not aria-hidden: it is a control.
59
62
  *
60
63
  * Chrome (bg/border/padding/transition/disabled) comes from .kol-control;
61
64
  * Input owns prefix/suffix/icon layout + the inner <input> styling.
@@ -75,6 +78,7 @@ export default function Input({
75
78
  prefix,
76
79
  suffix,
77
80
  slotLeft,
81
+ slotRight,
78
82
  affordance,
79
83
  unit,
80
84
  iconLeft,
@@ -217,6 +221,9 @@ export default function Input({
217
221
  {suffix !== undefined && (
218
222
  <span aria-hidden="true" className="text-meta pl-1 shrink-0">{suffix}</span>
219
223
  )}
224
+ {slotRight && (
225
+ <span className="flex items-center shrink-0 pl-1 ml-auto">{slotRight}</span>
226
+ )}
220
227
  </label>
221
228
  )
222
229
  }
@@ -29,7 +29,8 @@
29
29
  * strip (canvas alignment, transform cluster); onChange is the
30
30
  * action dispatch.
31
31
  * onChange — handler (newValue) => void
32
- * options — [{ value, label, ariaLabel? }]
32
+ * options — [{ value, label, ariaLabel?, tooltip? }] — a cell whose label is not a string
33
+ * (a glyph) shows its `ariaLabel` as a KOL Tooltip; `tooltip` overrides it
33
34
  * variant — 'default' (shipped chrome: shared outer stroke + dividers,
34
35
  * filled active cell) | 'filled' (the state law's tiles: every
35
36
  * cell a surface-secondary tile with 1px transparent gaps, NO
@@ -53,6 +54,7 @@
53
54
  * className — additional classes on the outer shell
54
55
  */
55
56
  import { toneClass } from '../utilities/tone.js'
57
+ import { Tooltip } from '../utilities/Popover.jsx'
56
58
 
57
59
  export default function SegmentedToggle({ value, onChange, options = [], variant = 'default', size = 'md', tone = 'default', ariaLabel, className = '' }) {
58
60
  const cellType = { xs: 'kol-mono-8', sm: 'kol-mono-12', md: 'kol-mono-14', lg: 'kol-mono-16' }[size]
@@ -86,8 +88,8 @@ export default function SegmentedToggle({ value, onChange, options = [], variant
86
88
  {options.map((opt, i) => {
87
89
  const isActive = !stateless && opt.value === value
88
90
  return (
91
+ <Tooltip key={opt.value} asChild label={opt.tooltip ?? (typeof opt.label === 'string' ? null : opt.ariaLabel)}>
89
92
  <button
90
- key={opt.value}
91
93
  type="button"
92
94
  role={stateless ? undefined : 'radio'}
93
95
  aria-checked={stateless ? undefined : isActive}
@@ -98,6 +100,7 @@ export default function SegmentedToggle({ value, onChange, options = [], variant
98
100
  >
99
101
  {opt.label}
100
102
  </button>
103
+ </Tooltip>
101
104
  )
102
105
  })}
103
106
  </div>
@@ -76,14 +76,14 @@ export default function XYPad({
76
76
  ref={ref}
77
77
  onPointerDown={onPointerDown}
78
78
  onPointerMove={onPointerMove}
79
- className="relative w-full aspect-square border border-fg-16 bg-fg-04 rounded cursor-crosshair touch-none"
79
+ className="relative w-full aspect-square border border-oq-16 bg-fg-04 rounded cursor-crosshair touch-none"
80
80
  >
81
81
  {/* crosshair guides */}
82
- <div className="absolute inset-x-0 top-1/2 border-t border-fg-08" />
83
- <div className="absolute inset-y-0 left-1/2 border-l border-fg-08" />
82
+ <div className="absolute inset-x-0 top-1/2 border-t border-oq-08" />
83
+ <div className="absolute inset-y-0 left-1/2 border-l border-oq-08" />
84
84
  {/* puck */}
85
85
  <div
86
- className="absolute w-3 h-3 -ml-1.5 -mt-1.5 rounded-full bg-fg-96 border border-fg-04 pointer-events-none"
86
+ className="absolute w-3 h-3 -ml-1.5 -mt-1.5 rounded-full bg-fg-96 border border-oq-04 pointer-events-none"
87
87
  style={{ left: `${puckX}%`, top: `${puckY}%` }}
88
88
  />
89
89
  </div>
@@ -148,7 +148,7 @@ export default function FieldRow({
148
148
  onClick={onPick}
149
149
  /* reference empty tile: FILLED, no border — a plain grey slab with the
150
150
  * + centred (frame 2026-08-09) */
151
- className="flex h-18 w-34 items-center justify-center rounded border-0 bg-fg-04 text-body transition-colors duration-150 enabled:cursor-pointer enabled:hover:text-emphasis disabled:opacity-60"
151
+ className="flex h-18 w-34 items-center justify-center rounded border-0 bg-fg-04 text-oq-64 transition-colors duration-150 enabled:cursor-pointer enabled:hover:text-emphasis disabled:opacity-60"
152
152
  >
153
153
  <Icon name="plus" size={16} />
154
154
  </button>
@@ -217,7 +217,7 @@ export default function FieldRow({
217
217
  type="button"
218
218
  aria-label={label}
219
219
  onClick={() => onChange(null)}
220
- className="inline-flex shrink-0 cursor-pointer border-0 bg-transparent p-0 text-body hover:text-emphasis"
220
+ className="inline-flex shrink-0 cursor-pointer border-0 bg-transparent p-0 text-oq-64 hover:text-emphasis"
221
221
  >
222
222
  <Icon name="x" size={12} />
223
223
  </button>
@@ -17,12 +17,42 @@
17
17
  * ponytail: a `SectionStack` parent could own this instead of each child
18
18
  * declaring it — that is the upgrade path if a consumer ever needs the stack
19
19
  * to vary the rule per-gap. One prop is a smaller API than a new component.
20
+ *
21
+ * `pane` (editor inspector rebuild, 2026-09-27 — user: "why are we not sectioning off the panes?
22
+ * saying 'this is type' 'this is transform'"): the Affinity panel shape instead of a labelled form
23
+ * group — a titled header strip, a padded body, and a FULL-WIDTH rule between panes. Mount panes
24
+ * in a container with no horizontal padding; each pane pads itself, so its rule runs edge to edge.
25
+ *
26
+ * @param {ReactNode} label the group's name
27
+ * @param {ReactNode} actions controls on the label's row, right-aligned
28
+ * @param {boolean} divided a hairline between divided siblings
29
+ * @param {boolean} pane the panel shape: header strip + body, full-width rules between panes
30
+ * @param {string} className
20
31
  */
21
- export default function InspectorSection({ label, children, divided = false, className = '' }) {
32
+ export default function InspectorSection({ label, actions = null, children, divided = false, pane = false, className = '' }) {
33
+ if (pane) {
34
+ return (
35
+ <section className={`kol-inspector-pane ${className}`.trim()}>
36
+ {(label || actions) && (
37
+ <header className="kol-inspector-pane-head">
38
+ {label && <h3 className="kol-inspector-pane-title">{label}</h3>}
39
+ {actions}
40
+ </header>
41
+ )}
42
+ <div className="kol-inspector-pane-body">{children}</div>
43
+ </section>
44
+ )
45
+ }
22
46
  return (
23
47
  <div className={`flex flex-col gap-2${divided ? ' kol-section--divided' : ''} ${className}`}>
24
- {label && (
25
- <p className="kol-helper-10 tracking-widest text-meta">{label}</p>
48
+ {/* `actions` — controls on the label's row, right-aligned (Figma's eye / plus on Fill and
49
+ * Stroke). From the design editor's own Section, retired into this (editor DS sync,
50
+ * 2026-09-27). */}
51
+ {(label || actions) && (
52
+ <div className="flex items-center gap-2 min-h-6">
53
+ {label && <p className="kol-helper-10 tracking-widest text-meta flex-1">{label}</p>}
54
+ {actions}
55
+ </div>
26
56
  )}
27
57
  {children}
28
58
  </div>
@@ -51,7 +51,7 @@ export function MenuItem({
51
51
  ref={popover.refs.setReference}
52
52
  {...popover.getReferenceProps()}
53
53
  type="button"
54
- className={`kol-helper-12 px-3 h-8 inline-flex items-center gap-2 rounded text-body hover:text-emphasis transition-colors ${buttonClassName}`}
54
+ className={`kol-menu-btn kol-helper-12 px-3 h-8 inline-flex items-center gap-2 rounded text-body hover:text-emphasis transition-colors ${buttonClassName}`}
55
55
  >
56
56
  <span>{label}</span>
57
57
  {caret && (
@@ -125,7 +125,7 @@ export function MenuDropdownItem({ onClick, onPointerEnter, onPointerLeave, disa
125
125
  disabled={disabled}
126
126
  role="menuitem"
127
127
  style={height != null ? { height } : undefined}
128
- className={`w-full ${ROW_BY_SIZE[size] ?? ROW_BY_SIZE.sm} shrink-0 inline-flex items-center gap-2 text-body ${hover ? 'hover:text-emphasis' : ''} disabled:opacity-40 disabled:cursor-not-allowed text-left`}
128
+ className={`kol-menu-btn w-full ${ROW_BY_SIZE[size] ?? ROW_BY_SIZE.sm} shrink-0 inline-flex items-center gap-2 text-body ${hover ? 'hover:text-emphasis' : ''} disabled:opacity-40 disabled:cursor-not-allowed text-left`}
129
129
  >
130
130
  {prefix && <span className="shrink-0 inline-flex items-center">{prefix}</span>}
131
131
  {iconLeft && <span className="shrink-0 w-4 inline-flex items-center justify-center">{iconLeft}</span>}
@@ -140,7 +140,7 @@ export function MenuDropdownItem({ onClick, onPointerEnter, onPointerLeave, disa
140
140
  }
141
141
 
142
142
  export function MenuDropdownDivider() {
143
- return <div className="border-t border-fg-08 my-1" />
143
+ return <div className="border-t border-oq-08 my-1" />
144
144
  }
145
145
 
146
146
  /**
@@ -161,7 +161,7 @@ export function MenuDropdownNest({ prefix, iconLeft, label, children }) {
161
161
  type="button"
162
162
  onClick={() => setOpen((v) => !v)}
163
163
  aria-expanded={open}
164
- className="w-full kol-helper-12 px-3 h-8 inline-flex items-center gap-2 text-body hover:text-emphasis text-left"
164
+ className="kol-menu-btn w-full kol-helper-12 px-3 h-8 inline-flex items-center gap-2 text-body hover:text-emphasis text-left"
165
165
  >
166
166
  {prefix && <span className="shrink-0 inline-flex items-center">{prefix}</span>}
167
167
  {iconLeft && <span className="shrink-0 w-4 inline-flex items-center justify-center">{iconLeft}</span>}
@@ -175,7 +175,7 @@ export function MenuDropdownNest({ prefix, iconLeft, label, children }) {
175
175
  />
176
176
  </button>
177
177
  {open && (
178
- <div className="ml-3 border-l border-fg-08">
178
+ <div className="ml-3 border-l border-oq-08">
179
179
  {children}
180
180
  </div>
181
181
  )}
@@ -245,7 +245,7 @@ export default function SearchInput({
245
245
  /* preventDefault on mousedown keeps focus in the input across the clear */
246
246
  onMouseDown={(e) => e.preventDefault()}
247
247
  onClick={onClear}
248
- className="inline-flex items-center justify-center shrink-0 cursor-pointer text-fg-48 hover:text-fg-96 transition-colors"
248
+ className="inline-flex items-center justify-center shrink-0 cursor-pointer text-oq-48 hover:text-oq-96 transition-colors"
249
249
  >
250
250
  <Icon name="x" size={12} />
251
251
  </button>
@@ -101,6 +101,8 @@ export default function SectionText({
101
101
  const cls = (base, slot) => `${base} ${slotClass[slot] ?? (slot === 'eyebrow' ? slotClass.label : undefined) ?? ''}`.trim()
102
102
  const alignCls = align === 'center' ? 'items-center text-center' : 'items-start text-left'
103
103
  const inlineActions = actionsPlacement === 'inline' && !!actions && !!body
104
+ const inlineHead = actionsPlacement === 'inline' && !!actions && !body && !!headline
105
+ const headlineNode = headline && <Headline className={cls(`kol-section-text-headline ${headlineClass ?? (HEADLINE_ROLE[headlineSize] ?? HEADLINE_ROLE['heading-02'])}${headlineCase === 'upper' ? ' kol-section-text-caps' : ''}`, 'headline')} style={slotStyle.headline}>{headline}</Headline>
104
106
  const bodyNode = body && (typeof body === 'string'
105
107
  ? <p className={cls(bodyClass, 'body')} style={slotStyle.body}>{body}</p>
106
108
  : <div className={cls(bodyClass, 'body')} style={slotStyle.body}>{body}</div>)
@@ -112,7 +114,19 @@ export default function SectionText({
112
114
  {/* `kol-section-text-eyebrow` = uppercase by ROLE (kol-theme ≥0.55.0);
113
115
  * `labelClass` is the voice riding beside it */}
114
116
  {eb && <span className={cls(`kol-section-text-eyebrow ${ebClass}`, 'eyebrow')} style={slotStyle.eyebrow ?? slotStyle.label}>{eb}</span>}
115
- {headline && <Headline className={cls(`kol-section-text-headline ${headlineClass ?? (HEADLINE_ROLE[headlineSize] ?? HEADLINE_ROLE['heading-02'])}${headlineCase === 'upper' ? ' kol-section-text-caps' : ''}`, 'headline')} style={slotStyle.headline}>{headline}</Headline>}
117
+ {headlineNode && (inlineHead
118
+ ? (
119
+ /* INLINE WITH NO BODY → the cluster shares the HEADLINE's row (the tool frame, 2026-09-27 —
120
+ * a tool's masthead is its title with its controls on the right, apps/media's). This
121
+ * comment used to sit on the fallthrough below, which stacked the cluster under the
122
+ * headline instead: the docstring promised a row the code never drew. It WRAPS: on a
123
+ * phone a wide cluster drops under the title rather than pushing the page sideways. */
124
+ <div className="flex flex-wrap self-stretch items-baseline justify-between gap-x-6 gap-y-3">
125
+ {headlineNode}
126
+ <div className={cls(`${actionsClass} shrink-0`, 'actions')} style={slotStyle.actions}>{actions}</div>
127
+ </div>
128
+ )
129
+ : headlineNode)}
116
130
  {bodyNode && (inlineActions
117
131
  ? (
118
132
  /* SELF-STRETCH (the Hub review, 2026-09-26 — user, on fxr's /settings: "there should
@@ -126,8 +140,7 @@ export default function SectionText({
126
140
  )
127
141
  : bodyNode)}
128
142
  {children}
129
- {/* inline with no body → the cluster shares the HEADLINE's row instead */}
130
- {actions && !inlineActions && <div className={cls(actionsClass, 'actions')} style={slotStyle.actions}>{actions}</div>}
143
+ {actions && !inlineActions && !inlineHead && <div className={cls(actionsClass, 'actions')} style={slotStyle.actions}>{actions}</div>}
131
144
  </div>
132
145
  )
133
146
  }
@@ -140,7 +140,7 @@ const SplitToolButton = ({
140
140
  popover={popover}
141
141
  panel={false}
142
142
  focus={false}
143
- className="w-max bg-surface-secondary border border-fg-08 rounded shadow-lg"
143
+ className="w-max bg-surface-secondary border border-oq-08 rounded shadow-lg"
144
144
  >
145
145
  {variants.map((variant) => {
146
146
  const isActive = active && variant.id === value
@@ -123,7 +123,7 @@ export default function Stepper({
123
123
  <button
124
124
  type="button"
125
125
  onClick={handleIncrement}
126
- className="flex items-center justify-center text-meta hover:text-emphasis transition-colors"
126
+ className="flex items-center justify-center text-oq-48 hover:text-emphasis transition-colors"
127
127
  aria-label="Increment"
128
128
  >
129
129
  <Icon name="chevron-up" size={chevronWidth} />
@@ -131,7 +131,7 @@ export default function Stepper({
131
131
  <button
132
132
  type="button"
133
133
  onClick={handleDecrement}
134
- className="flex items-center justify-center text-meta hover:text-emphasis transition-colors"
134
+ className="flex items-center justify-center text-oq-48 hover:text-emphasis transition-colors"
135
135
  aria-label="Decrement"
136
136
  >
137
137
  <Icon name="chevron-down" size={chevronWidth} />
@@ -71,7 +71,7 @@ export default function TabsRow({ tabs = [], value, onChange, onClose, onMinimis
71
71
  type="button"
72
72
  onClick={onMinimise}
73
73
  aria-label="Minimise"
74
- className="ml-auto text-meta hover:text-emphasis self-center"
74
+ className="ml-auto text-oq-48 hover:text-emphasis self-center"
75
75
  style={{ lineHeight: 0 }}
76
76
  >
77
77
  <Icon name="chevron-down" size={12} />
@@ -87,10 +87,15 @@ export function CanvasFrame({
87
87
  customRatio,
88
88
  aspects,
89
89
  bgColor,
90
- guideColor = CANVAS_DEFAULTS.guideColor,
90
+ guideColor,
91
91
  children,
92
92
  }) {
93
93
  const { ratio, label } = resolveAspect(aspect, customRatio, aspects)
94
+ /* NO guideColor → THEME INK (editor DS sync 2026-09-27, from the design editor's copy, which had
95
+ * it and this lift had not): the border and label follow the theme, so the frame reads on a light
96
+ * page and a dark one. A guideColor (a type frame's own colour) still tints both. */
97
+ const borderColor = guideColor ? `color-mix(in srgb, ${guideColor} 24%, transparent)` : 'var(--kol-oq-24)'
98
+ const labelColor = guideColor ? `color-mix(in srgb, ${guideColor} 70%, transparent)` : 'var(--kol-fg-64)'
94
99
  const virtualH = CANVAS_VIRTUAL_W / ratio
95
100
 
96
101
  const rectRef = useRef(null)
@@ -127,7 +132,7 @@ export function CanvasFrame({
127
132
  className="absolute inset-0 pointer-events-none z-[2]"
128
133
  style={{
129
134
  border: '1px solid',
130
- borderColor: `color-mix(in srgb, ${guideColor} 24%, transparent)`,
135
+ borderColor,
131
136
  }}
132
137
  />
133
138
  <span
@@ -139,7 +144,7 @@ export function CanvasFrame({
139
144
  fontSize: 10,
140
145
  fontFamily: 'var(--kol-font-family-mono)',
141
146
  letterSpacing: '0.1em',
142
- color: `color-mix(in srgb, ${guideColor} 70%, transparent)`,
147
+ color: labelColor,
143
148
  pointerEvents: 'none',
144
149
  }}
145
150
  >
@@ -197,7 +202,7 @@ export default function Canvas({
197
202
  customRatio,
198
203
  aspects,
199
204
  bgColor,
200
- guideColor = CANVAS_DEFAULTS.guideColor,
205
+ guideColor,
201
206
  align = 'center',
202
207
  panEnabled = false,
203
208
  backdrop,
@@ -820,7 +825,7 @@ export function CanvasRuler({ containerRef, virtualWidth = CANVAS_VIRTUAL_W, dis
820
825
  const tickColor = 'var(--kol-fg-48)'
821
826
  const textColor = 'var(--kol-fg-64)'
822
827
  const barBg = 'var(--kol-fg-08)'
823
- const borderColor = 'var(--kol-fg-16)'
828
+ const borderColor = 'var(--kol-oq-16)'
824
829
  const labelStyle = { fontFamily: 'var(--kol-font-family-mono)', fontSize: 9 }
825
830
 
826
831
  return (
@@ -42,7 +42,7 @@ export const DEFAULT_KEYFRAMES = [
42
42
  *
43
43
  * @param {Array<Object>} keyframes - The track; empty falls back to `DEFAULT_KEYFRAMES`
44
44
  * @param {Function} onChange - `(keyframes) => void` — the whole track, sorted
45
- * @param {number} t - The clock's current LAYER-LOCAL phase, 0..1 — what "Add @ playhead" stamps
45
+ * @param {number} t - The clock's current LAYER-LOCAL phase, 0..1 — what "Add @ playhead" stamps; or a function returning it, read at the click — a clock that runs without re-rendering (the design editor's transport) would otherwise stamp the phase of the last render
46
46
  * @param {Function} onSeek - `(t) => void` — a key was selected; seek the clock to its local `t`
47
47
  * @param {Function} [onPause] - Called before the seek, so the render holds on the pose
48
48
  * @param {Array<{value: string, label: string}>} [easeOptions] - The Ease menu (default `KEYFRAME_EASES`)
@@ -72,7 +72,7 @@ export default function KeyframeEditor({
72
72
  const onAdd = () => {
73
73
  const base = kfs[sel] || { rot: [0, 0, 0], pos: [0, 0, 0], scale: 1, ease: 'inout' }
74
74
  const nk = {
75
- t: Math.max(0, Math.min(1, t)),
75
+ t: Math.max(0, Math.min(1, typeof t === 'function' ? t() : t)),
76
76
  rot: [...(base.rot || [0, 0, 0])],
77
77
  pos: [...(base.pos || [0, 0, 0])],
78
78
  scale: base.scale ?? 1,
@@ -334,7 +334,7 @@ function FolderRow({ name, icon = 'folder', onClick, onDoubleClick, depth = 0, e
334
334
  {onToggle ? (
335
335
  <button type="button" aria-label={expanded ? `Collapse ${name}` : `Expand ${name}`} aria-expanded={!!expanded}
336
336
  onClick={(e) => { e.stopPropagation(); onToggle() }}
337
- className="w-4 h-4 shrink-0 flex items-center justify-center text-fg-32 hover:text-fg-default transition-transform"
337
+ className="w-4 h-4 shrink-0 flex items-center justify-center text-oq-32 hover:text-fg-default transition-transform"
338
338
  style={{ transform: expanded ? 'rotate(90deg)' : 'none' }}>
339
339
  <Icon name="chevron-right" size={12} />
340
340
  </button>
@@ -27,7 +27,7 @@ const ToolbarIcon = ({ name, label, onClick, active = false, triggerClassName })
27
27
  * icon, and only when pressed". Rest is bare; hover is ink only. */
28
28
  /* 20px glyph, half-step pad — "TOO SMALL, TOO MUCH PADDING" (user
29
29
  * frames 2026-08-09): the reference icons nearly fill their box. */
30
- className={`inline-flex cursor-pointer rounded-sm border-0 p-0.5 transition-colors duration-150 ${active ? 'bg-fg-08 text-emphasis' : 'bg-transparent text-body hover:text-emphasis'}`}
30
+ className={`inline-flex cursor-pointer rounded-sm border-0 p-0.5 transition-colors duration-150 ${active ? 'bg-fg-08 text-emphasis' : 'bg-transparent text-oq-64 hover:text-emphasis'}`}
31
31
  >
32
32
  <Icon name={name} size={20} />
33
33
  </button>
@@ -255,7 +255,7 @@ export default function RecordManager({
255
255
  <button
256
256
  type="button"
257
257
  aria-label={reorderLabel(i + 1)}
258
- className="inline-flex w-4 cursor-grab touch-none justify-center border-0 bg-transparent p-0 text-meta hover:text-emphasis"
258
+ className="inline-flex w-4 cursor-grab touch-none justify-center border-0 bg-transparent p-0 text-oq-48 hover:text-emphasis"
259
259
  onPointerDown={(e) => startDrag(e, i)}
260
260
  >
261
261
  <Icon name="drag-handle" size={16} />
@@ -250,7 +250,7 @@ export default function TimelineDock({ tracks = [], t = 0, onSeek, onChange, eas
250
250
  }
251
251
 
252
252
  return (
253
- <div className={`kol-timeline-dock border-t border-fg-08 px-4 py-2 flex flex-col gap-1 select-none ${className}`.trim()} style={{ background: 'var(--kol-surface-primary)' }}>
253
+ <div className={`kol-timeline-dock border-t border-oq-08 px-4 py-2 flex flex-col gap-1 select-none ${className}`.trim()} style={{ background: 'var(--kol-surface-primary)' }}>
254
254
  <ScrubRuler t={t} onSeek={onSeek} />
255
255
  {tracks.map((track) => (
256
256
  <TrackRow key={track.id} track={track} t={t} selected={selected} setSelected={setSelected} writeKeys={writeKeys} />
@@ -43,7 +43,7 @@ const ToolPalette = ({
43
43
  iconComponent = Icon,
44
44
  className = '',
45
45
  }) => (
46
- <div className={`flex items-center gap-1 min-w-0 overflow-x-auto ${className}`.trim()}>
46
+ <div className={`kol-tool-palette flex items-center gap-1 min-w-0 overflow-x-auto ${className}`.trim()}>
47
47
  {items.map((it, i) => {
48
48
  if (it.kind === 'divider') return <Divider key={`d${i}`} variant="vertical" height={20} className="mx-1.5 shrink-0" />
49
49
  if (it.kind === 'split') {