feat(ui): group Knobs experiments into cards (#58134)

This commit is contained in:
James Long
2026-10-22 10:31:00 -04:00
committed by GitHub
Unverified
parent 8b91299414
commit 8ffdee47ef
8 changed files with 55 additions and 6 deletions
@@ -1433,6 +1433,8 @@ function ComposerEditorAlternateDelivery(props: { controller: ComposerEditorMode
const sendIcon = motionTuning.choice("composer.sendIcon", {
label: "Send icon",
group: "Experiments",
card: "Icons",
order: 3,
value: "today",
labels: { today: "Today", ios: "iOS", heavy: "Heavier", filled: "Filled" },
})
@@ -1440,6 +1442,8 @@ const sendIcon = motionTuning.choice("composer.sendIcon", {
const stopIcon = motionTuning.choice("composer.stopIcon", {
label: "Stop icon",
group: "Experiments",
card: "Icons",
order: 3,
value: "today",
labels: { today: "Today", rounded: "Rounded", ios: "iOS", heavy: "Heavier" },
})
@@ -1447,6 +1451,8 @@ const stopIcon = motionTuning.choice("composer.stopIcon", {
const roundedStopSize = motionTuning.number("composer.stopIcon.size", {
label: "Rounded stop size",
group: "Experiments",
card: "Icons",
order: 3,
value: 10,
min: 6,
max: 14,
@@ -14,12 +14,14 @@ import "./activity-chips.css"
const chip = motionTuning.choice("activity.chip", {
label: en["experiment.activityChip"],
group: "Experiments",
order: 1,
value: "today",
labels: { today: en["experiment.activityChip.today"], orb: en["experiment.activityChip.orb"] },
})
const corners = motionTuning.choice("activity.chip.corners", {
label: en["experiment.chipCorners"],
group: "Experiments",
order: 1,
value: "capsule",
labels: { capsule: en["experiment.chipCorners.capsule"], composer: en["experiment.chipCorners.composer"] },
})
+13 -1
View File
@@ -3,10 +3,14 @@ import { Match, Switch } from "solid-js"
import { createStore } from "solid-js/store"
const group = "Experiments"
const card = "Icons"
const order = 3
const glyph = motionTuning.choice("sessionHeader.separator", {
label: "Header separator",
label: "Separator",
group,
card,
order,
value: "chevron",
labels: { divider: "Divider", slash: "Slash", chevron: "Chevron", bold: "Bold chevron", dot: "Dot" },
})
@@ -14,6 +18,8 @@ const glyph = motionTuning.choice("sessionHeader.separator", {
const size = motionTuning.number("sessionHeader.separator.size", {
label: "Separator size",
group,
card,
order,
value: 11,
min: 6,
max: 24,
@@ -24,6 +30,8 @@ const size = motionTuning.number("sessionHeader.separator.size", {
const weight = motionTuning.number("sessionHeader.separator.weight", {
label: "Separator weight",
group,
card,
order,
value: 1,
min: 0.5,
max: 3,
@@ -34,6 +42,8 @@ const weight = motionTuning.number("sessionHeader.separator.weight", {
const opacity = motionTuning.number("sessionHeader.separator.opacity", {
label: "Separator opacity",
group,
card,
order,
value: 1,
min: 0.2,
max: 1,
@@ -43,6 +53,8 @@ const opacity = motionTuning.number("sessionHeader.separator.opacity", {
const gap = motionTuning.number("sessionHeader.separator.gap", {
label: "Separator gap (ink to ink)",
group,
card,
order,
value: 10,
min: 4,
max: 20,
@@ -4,15 +4,19 @@ import { makeEventListener } from "@solid-primitives/event-listener"
import { createSignal, onCleanup, onMount, Show } from "solid-js"
const choice = motionTuning.choice("sidebar.hover", {
label: "Sidebar hover",
label: "Style",
group: "Experiments",
card: "Sidebar hover",
order: 2,
value: "today",
labels: { today: "Today", glide: "Glide" },
})
const tuning = motionTuning.spring("sidebar.hover.glide", {
label: "Sidebar hover glide",
label: "Glide",
group: "Experiments",
card: "Sidebar hover",
order: 2,
presets: {
snappy: { duration: 0.14, bounce: 0 },
smooth: { duration: 0.2, bounce: 0 },
+1
View File
@@ -10,6 +10,7 @@ export type SpinnerVariantName = "today" | keyof typeof spinnerVariants
const choice = motionTuning.choice("spinner.variant", {
label: "Activity spinner",
group: "Experiments",
order: 1,
value: "radar",
// SAFETY: built from spinnerVariants' own keys, plus "today".
labels: Object.fromEntries([
@@ -24,6 +24,7 @@ export { generateProjectIcon, GENERATED_PROJECT_ICON_STYLE, type GeneratedProjec
const avatarChoice = motionTuning.choice("projectAvatar.style", {
label: "Project avatars",
group: "Experiments",
order: 1,
value: "letters",
labels: { letters: "Letters", generated: "Generated" },
})
+10 -2
View File
@@ -31,7 +31,11 @@ export function mountMotionPanel(
const { action, choice, defineControls, easing, knob, spring, toggle } = await import("knobs")
const field = (item: MotionKnob) => {
const meta = { label: item.label, group: item.group, tab: item.group === global ? undefined : item.group }
const meta = {
label: item.label,
group: item.card ?? item.group,
tab: item.group === global ? undefined : item.group,
}
if (item.kind === "spring") return spring(item.initial, meta)
@@ -104,7 +108,11 @@ export function mountMotionPanel(
...Object.fromEntries(
[...list, ...actions]
.filter((item) => item.group !== global)
.toSorted((a, b) => sections.indexOf(a.group) - sections.indexOf(b.group))
.toSorted(
(a, b) =>
sections.indexOf(a.group) - sections.indexOf(b.group) ||
("order" in a ? (a.order ?? 0) : 0) - ("order" in b ? (b.order ?? 0) : 0),
)
.map((item) =>
"run" in item
? [
+16 -1
View File
@@ -9,6 +9,9 @@ type Knob<Kind extends string, Value> = {
id: string
label: string
group: string
/** Its own card within the group's tab, such as one experiment among several; `order` places that card. */
card?: string
order?: number
/** Shown only while this holds, e.g. the controls of the option a choice selects. */
when?: () => boolean
initial: Value
@@ -33,7 +36,7 @@ export type MotionKnob =
export type MotionAction = { id: string; label: string; group: string; run: () => void }
type Meta = { label: string; group: string; when?: () => boolean }
type Meta = { label: string; group: string; card?: string; order?: number; when?: () => boolean }
// Bumped when knobs are merged or renamed, so values saved for the old ones are dropped.
const key = "opencode.motion.v2"
@@ -57,6 +60,8 @@ export const motionTuning = {
id,
label: meta.label,
group: meta.group,
card: meta.card,
order: meta.order,
when: meta.when,
initial,
value: { duration: number(`${id}.duration`, initial.duration), bounce: number(`${id}.bounce`, initial.bounce) },
@@ -75,6 +80,8 @@ export const motionTuning = {
id,
label: meta.label,
group: meta.group,
card: meta.card,
order: meta.order,
when: meta.when,
initial: meta.value,
// SAFETY: mapping the four indices of a Bezier yields four numbers.
@@ -95,6 +102,8 @@ export const motionTuning = {
id,
label: meta.label,
group: meta.group,
card: meta.card,
order: meta.order,
when: meta.when,
initial,
value: number(id, initial ? 1 : 0) === 1,
@@ -108,6 +117,8 @@ export const motionTuning = {
id,
label: meta.label,
group: meta.group,
card: meta.card,
order: meta.order,
when: meta.when,
initial: meta.value,
value: number(id, meta.value ? 1 : 0) === 1,
@@ -125,6 +136,8 @@ export const motionTuning = {
id,
label: meta.label,
group: meta.group,
card: meta.card,
order: meta.order,
when: meta.when,
initial: meta.value,
value: options[number(id, options.indexOf(meta.value))] ?? meta.value,
@@ -148,6 +161,8 @@ export const motionTuning = {
id,
label: meta.label,
group: meta.group,
card: meta.card,
order: meta.order,
when: meta.when,
initial: meta.value,
value: number(id, meta.value),