Wrap serialized gradient stops in a new GradientRamp struct and unify FillChoice (#4400)

* Introduce the GradientRamp exchange struct as the serialized TaggedValue::Gradient payload

* Unify FillChoice and FillChoiceUI into one enum generic over color format, carrying GradientRamp stops

* Rename the TaggedValue::Gradient variant to GradientRamp to match its payload

* Move the Color variant into the tagged_value macro list since its stored and wire forms match
This commit is contained in:
Keavon Chambers
2026-08-03 23:23:41 -07:00
committed by Dennis Kobert
parent a0df7c2513
commit 57c01ed413
32 changed files with 411 additions and 288 deletions

View File

@@ -5,14 +5,14 @@
import LayoutRow from "/src/components/layout/LayoutRow.svelte";
import WidgetLayout from "/src/components/widgets/WidgetLayout.svelte";
import type { ColorPickerCallbacks, ColorPickerStore } from "/src/stores/color-picker";
import type { EditorWrapper, FillChoiceUI, MenuDirection } from "/wrapper/pkg/graphite_wasm_wrapper";
import type { EditorWrapper, FillChoice, MenuDirection, SRGBA8 } from "/wrapper/pkg/graphite_wasm_wrapper";
const dispatch = createEventDispatcher<{ colorOrGradient: FillChoiceUI; startHistoryTransaction: undefined; commitHistoryTransaction: undefined }>();
const dispatch = createEventDispatcher<{ colorOrGradient: FillChoice<SRGBA8>; startHistoryTransaction: undefined; commitHistoryTransaction: undefined }>();
const editor = getContext<EditorWrapper>("editor");
const colorPickerStore = getContext<ColorPickerStore>("colorPicker");
export let colorOrGradient: FillChoiceUI;
export let colorOrGradient: FillChoice<SRGBA8>;
export let allowNone = false;
// export let allowTransparency = false; // TODO: Implement
export let disabled = false;

View File

@@ -13,7 +13,7 @@
import type { DocumentStore } from "/src/stores/document";
import type { SubscriptionsRouter } from "/src/subscriptions-router";
import type { MessageBody } from "/src/subscriptions-router";
import { fillChoiceUIColor, createSRgba8 } from "/src/utility-functions/colors";
import { fillChoiceColor, createSRgba8 } from "/src/utility-functions/colors";
import { pasteFile } from "/src/utility-functions/files";
import { textInputCleanup } from "/src/utility-functions/keyboard-entry";
import { rasterizeSVGCanvas } from "/src/utility-functions/rasterization";
@@ -680,7 +680,7 @@
}}
colorOrGradient={{ Solid: gradientStopPickerColor || createSRgba8(0, 0, 0, 255) }}
on:colorOrGradient={({ detail }) => {
const color = fillChoiceUIColor(detail);
const color = fillChoiceColor(detail);
if (color) editor.updateGradientStopColor(color);
}}
on:startHistoryTransaction={() => editor.startGradientStopColorTransaction()}

View File

@@ -26,7 +26,7 @@
import ShortcutLabel from "/src/components/widgets/labels/ShortcutLabel.svelte";
import TextLabel from "/src/components/widgets/labels/TextLabel.svelte";
import type { ColorPickerStore } from "/src/stores/color-picker";
import { parseFillChoiceUI } from "/src/utility-functions/colors";
import { parseFillChoice } from "/src/utility-functions/colors";
import type { EditorWrapper, LayoutTarget, Widget, WidgetInstance } from "/wrapper/pkg/graphite_wasm_wrapper";
// Extract the discriminant key names from the Widget tagged enum union (e.g. "TextButton" | "CheckboxInput" | ...)
@@ -138,7 +138,7 @@
component: ColorInput,
getProps: (props, index) => ({
...props,
value: parseFillChoiceUI(props.value),
value: parseFillChoice(props.value),
$$events: {
value: (e: CustomEvent) => widgetValueUpdate(index, e.detail, false),
startHistoryTransaction: () => widgetValueCommit(index, props.value),
@@ -150,7 +150,7 @@
getProps: (props, index) => ({
...props,
$$events: {
// The widget dispatches `"None"` or a bare `SRGBA8`, wrap the color in `{ Solid: ... }` so the payload matches Rust's `FillChoiceUI` shape (which the `Preset` variant expects).
// The widget dispatches `"None"` or a bare `SRGBA8`, wrap the color in `{ Solid: ... }` so the payload matches the Rust `FillChoice` shape the `Preset` variant expects
preset: (e: CustomEvent) => widgetValueCommitAndUpdate(index, { Preset: e.detail === "None" ? "None" : { Solid: e.detail } }, true),
eyedropperColorCode: (e: CustomEvent) => widgetValueCommitAndUpdate(index, { EyedropperColorCode: e.detail }, true),
},

View File

@@ -2,13 +2,13 @@
import { createEventDispatcher } from "svelte";
import ColorPicker from "/src/components/floating-menus/ColorPicker.svelte";
import LayoutCol from "/src/components/layout/LayoutCol.svelte";
import { contrastingOutlineFactor, fillChoiceUIColor, fillChoiceUIGradient } from "/src/utility-functions/colors";
import type { FillChoiceUI, MenuDirection, ActionShortcut } from "/wrapper/pkg/graphite_wasm_wrapper";
import { contrastingOutlineFactor, fillChoiceColor, fillChoiceGradient } from "/src/utility-functions/colors";
import type { FillChoice, MenuDirection, ActionShortcut, SRGBA8 } from "/wrapper/pkg/graphite_wasm_wrapper";
const dispatch = createEventDispatcher<{ value: FillChoiceUI; startHistoryTransaction: undefined }>();
const dispatch = createEventDispatcher<{ value: FillChoice<SRGBA8>; startHistoryTransaction: undefined }>();
// Content
export let value: FillChoiceUI;
export let value: FillChoice<SRGBA8>;
export let chosenGradient: string | undefined = undefined;
export let allowNone = false;
// export let allowTransparency = false; // TODO: Implement
@@ -29,8 +29,8 @@
$: outlineFactor = contrastingOutlineFactor(value, "--color-3-darkgray", 0.01);
$: outlined = outlineFactor > 0.0001;
$: gradient = fillChoiceUIGradient(value);
$: solidColor = fillChoiceUIColor(value);
$: gradient = fillChoiceGradient(value);
$: solidColor = fillChoiceColor(value);
$: none = value === "None";
$: transparency = gradient ? gradient.color.some((color) => color.alpha < 255) : solidColor ? solidColor.alpha < 255 : false;
</script>

View File

@@ -3,7 +3,7 @@
import ColorPicker from "/src/components/floating-menus/ColorPicker.svelte";
import LayoutCol from "/src/components/layout/LayoutCol.svelte";
import LayoutRow from "/src/components/layout/LayoutRow.svelte";
import { fillChoiceUIColor, sRgba8ToRgbaCSS } from "/src/utility-functions/colors";
import { fillChoiceColor, sRgba8ToRgbaCSS } from "/src/utility-functions/colors";
import type { SRGBA8, EditorWrapper } from "/wrapper/pkg/graphite_wasm_wrapper";
const editor = getContext<EditorWrapper>("editor");
@@ -42,7 +42,7 @@
on:open={({ detail }) => (primaryOpen = detail)}
colorOrGradient={{ Solid: primary }}
on:colorOrGradient={({ detail }) => {
const color = fillChoiceUIColor(detail);
const color = fillChoiceColor(detail);
if (color) primaryColorChanged(color);
}}
direction="Right"
@@ -55,7 +55,7 @@
on:open={({ detail }) => (secondaryOpen = detail)}
colorOrGradient={{ Solid: secondary }}
on:colorOrGradient={({ detail }) => {
const color = fillChoiceUIColor(detail);
const color = fillChoiceColor(detail);
if (color) secondaryColorChanged(color);
}}
direction="Right"