From 291ba3f3731d2ace5baee3d4f1981fff2c611bc1 Mon Sep 17 00:00:00 2001
From: nakst <>
Date: Sun, 3 Oct 2021 21:17:43 +0100
Subject: [PATCH] remove old designer source

---
 util/build.c                   |   24 +-
 util/designer/designer.c       | 6345 --------------------------------
 util/designer/designer.rf      |  432 ---
 util/designer/designer_luigi.c |    8 -
 util/designer/reflect.h        |  654 ----
 util/designer/reflect_gen.c    |  531 ---
 6 files changed, 2 insertions(+), 7992 deletions(-)
 delete mode 100644 util/designer/designer.c
 delete mode 100644 util/designer/designer.rf
 delete mode 100644 util/designer/designer_luigi.c
 delete mode 100644 util/designer/reflect.h
 delete mode 100644 util/designer/reflect_gen.c

diff --git a/util/build.c b/util/build.c
index 21e9cce..10999b6 100644
--- a/util/build.c
+++ b/util/build.c
@@ -331,29 +331,9 @@ void BuildUtilities() {
 
 	BUILD_UTILITY("render_svg", "-lm", "");
 	BUILD_UTILITY("build_core", "-pthread -DPARALLEL_BUILD", "");
+
 #ifndef __APPLE__ // Luigi doesn't support macOS.
 	BUILD_UTILITY("config_editor", "-lX11 -Wno-unused-parameter", "");
-#endif
-#ifndef __APPLE__ // Luigi doesn't support macOS.
-	BUILD_UTILITY("reflect_gen", "", "designer/");
-
-	if (CheckDependencies("Utilities.DesignerHeader")) {
-		if (!CallSystem("bin/reflect_gen util/designer/designer.rf > bin/designer.h")) {
-			FILE *f = fopen("bin/designer_header.d", "wb");
-			fprintf(f, ": util/designer/designer.rf bin/reflect_gen\n");
-			fclose(f);
-			ParseDependencies("bin/designer_header.d", "Utilities.DesignerHeader", false);
-		}
-	}
-
-	if (CheckDependencies("Utilities.OldDesigner1") || CheckDependencies("Utilities.OldDesigner2")) {
-		if (!CallSystem("gcc -MMD -o bin/designer.o -c util/designer/designer.c -g -std=c2x -fsanitize=address " WARNING_FLAGS_C)
-					&& !CallSystem("gcc -MMD -o bin/designer_luigi.o -c util/designer/designer_luigi.c -g -std=c2x " WARNING_FLAGS_C)
-					&& !CallSystem("gcc -o bin/designer -g bin/designer.o bin/designer_luigi.o -lX11 -lm -fsanitize=address ")) {
-			ParseDependencies("bin/designer.d", "Utilities.Designer1", false);
-			ParseDependencies("bin/designer_luigi.d", "Utilities.Designer2", false);
-		}
-	}
 
 	if (CheckDependencies("Utilities.Designer")) {
 		if (!CallSystem("g++ -MMD -D UI_LINUX util/designer2.cpp -o bin/designer2 -g -lX11 -Wno-unused-parameter " WARNING_FLAGS)) {
@@ -1396,7 +1376,7 @@ void DoCommand(const char *l) {
 		LineCountFile("", "start.sh");
 
 		const char *folders[] = {
-			"desktop/", "boot/x86/", "drivers/", "kernel/", "apps/", "apps/file_manager/", "shared/", "util/", "util/designer/"
+			"desktop/", "boot/x86/", "drivers/", "kernel/", "apps/", "apps/file_manager/", "shared/", "util/"
 		};
 
 		for (uintptr_t i = 0; i < sizeof(folders) / sizeof(folders[0]); i++) {
diff --git a/util/designer/designer.c b/util/designer/designer.c
deleted file mode 100644
index a4a6207..0000000
--- a/util/designer/designer.c
+++ /dev/null
@@ -1,6345 +0,0 @@
-// TODO I think this needs to be rewritten...
-// TODO Converting linear gradients where all stops are the same color to a solid paint.
-// TODO Why does cap mode on dashes not show selected?
-// TODO Undoing creating layer, styles, constants.
-// TODO Prevent deleting metrics layer.
-// TODO Overrides on reused layers (additionally on non-interpolables; but not structural values).
-// TODO Global layer list.
-// TODO Animations with multiple keyframes.
-// TODO More paint types.
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <assert.h>
-#include <string.h>
-#include <math.h>
-#include <ctype.h>
-#include <limits.h>
-
-#define ES_TEXT_H_LEFT 	 (1 << 0)
-#define ES_TEXT_H_CENTER (1 << 1)
-#define ES_TEXT_H_RIGHT  (1 << 2)
-#define ES_TEXT_V_TOP 	 (1 << 3)
-#define ES_TEXT_V_CENTER (1 << 4)
-#define ES_TEXT_V_BOTTOM (1 << 5)
-#define ES_TEXT_ELLIPSIS (1 << 6)
-#define ES_TEXT_WRAP 	 (1 << 7)
-#define EsCRTsqrtf sqrtf
-#define EsAssert assert
-#define EsHeapAllocate(x, y) ((y) ? calloc(1, (x)) : malloc((x)))
-#define EsHeapFree free
-#define EsCRTfabsf fabsf
-#define EsCRTfmodf fmodf
-#define EsCRTisnanf isnan
-#define EsCRTfloorf floorf
-#define AbsoluteFloat fabsf
-#define EsCRTatan2f atan2f
-#define EsCRTsinf sinf
-#define EsCRTcosf cosf
-#define EsCRTacosf acosf
-#define EsCRTceilf ceilf
-#define EsMemoryCopy memcpy
-#define EsMemoryZero(a, b) memset((a), 0, (b))
-#define ES_FUNCTION_OPTIMISE_O2 __attribute__((optimize("-O2")))
-#define ES_INFINITY INFINITY
-#define IN_DESIGNER
-
-typedef struct EsBuffer {
-	union { const uint8_t *in; uint8_t *out; };
-	size_t position, bytes;
-	bool error;
-	void *context;
-} EsBuffer;
-
-#include "../luigi.h"
-#define NANOSVG_IMPLEMENTATION
-#include "../nanosvg.h"
-#define STB_TRUETYPE_IMPLEMENTATION
-#include "../stb_truetype.h"
-#define STB_DS_IMPLEMENTATION
-#include "../stb_ds.h"
-#include "../../shared/hash.cpp"
-#define SHARED_COMMON_WANT_BUFFERS
-#include "../../shared/common.cpp"
-
-#define OP_DO_MOD           (1)
-#define OP_MAKE_UI          (2)
-#define OP_EXPORT           (3)
-#define OP_GET_PALETTE      (4)
-#define OP_REPLACE_COLOR    (5)
-#define OP_FIND_COLOR_USERS (6)
-
-#define PATH_IN_KEYFRAME (0xFFFFFFFE)
-#define PATH_ANY (0xFFFFFFFD)
-
-typedef struct StringOption {
-	const char *string;
-} StringOption;
-
-typedef struct ModContext {
-	struct Style *style;
-	struct Layer *layer;
-	struct Sequence *sequence;
-	struct Keyframe *keyframe;
-} ModContext;
-
-const uint32_t saveFormatVersion = 22;
-
-void SetSelectedItems(ModContext context);
-void ColorListRefresh();
-
-#define MOD_CONTEXT(style, layer, sequence, keyframe) ((ModContext) { (style), (layer), (sequence), (keyframe) })
-
-#define REFLECT_IMPLEMENTATION
-#include "reflect.h"
-#include "../../bin/designer.h"
-
-typedef struct PaletteItem {
-	uint32_t key;
-	int value;
-} PaletteItem;
-
-typedef struct ColorUsersItem {
-	uint32_t key;
-	Style **value;
-} ColorUsersItem;
-
-PaletteItem *palette;
-ColorUsersItem *colorUsers;
-uint32_t replaceColorFrom, replaceColorTo;
-void *currentPaletteOpLayer;
-
-void PropertyOp(RfState *state, RfItem *item, void *pointer) {
-	Property *property = (Property *) pointer;
-	
-	if (state->op == RF_OP_FREE) {
-		free(property->path);
-		RfStructOp(state, item, pointer);
-	} else if (state->op == RF_OP_SAVE) {
-		uint8_t count = 0;
-
-		if (property->path) {
-			for (; property->path[count] != RF_PATH_TERMINATOR; count++);
-			state->access(state, &count, sizeof(count));
-
-			for (uintptr_t i = 0; property->path[i] != RF_PATH_TERMINATOR; i++) {
-				RfIntegerSave(state, property->path + i, sizeof(uint32_t));
-			}
-		} else {
-			count = 0xFF;
-			state->access(state, &count, sizeof(count));
-		}
-
-		RfStructOp(state, item, pointer);
-	} else if (state->op == RF_OP_LOAD) {
-		uint8_t count = 0;
-		state->access(state, &count, sizeof(count));
-
-		if (count != 0xFF) {
-			property->path = malloc(sizeof(uint32_t) * (count + 1));
-			property->path[count] = RF_PATH_TERMINATOR;
-
-			for (uintptr_t i = 0; i < count; i++) {
-				RfIntegerLoad(state, property->path + i, sizeof(uint32_t));
-			}
-		} else {
-			property->path = NULL;
-		}
-
-		RfStructOp(state, item, pointer);
-	} else if (state->op == OP_GET_PALETTE || state->op == OP_REPLACE_COLOR || state->op == OP_FIND_COLOR_USERS) {
-		void *pointer = currentPaletteOpLayer;
-		RfItem item = {};
-		item.type = &Layer_Type;
-		item.byteCount = sizeof(Layer);
-		bool success = RfPathResolve((RfPath *) (property->path + 1), &item, &pointer);
-		assert(success);
-
-		if (item.type->op == StyleColorOp) {
-			item.type->op(state, &item, property->data.buffer);
-		}
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-#define MSG_PROPERTY_CHANGED ((UIMessage) (UI_MSG_USER + 1))
-
-typedef struct MakeUIState {
-	RfState s;
-	uint32_t index;
-	struct MakeUIState *parent;
-	uint32_t *basePath;
-	bool recurse, inKeyframe;
-} MakeUIState;
-
-UIWindow *window;
-UITable *tableLayers, *tableSequences, *tableKeyframes;
-UIButton *buttonAddLayer, *buttonAddExistingLayer, *buttonPublicStyle;
-
-typedef struct AnimatingValue {
-	uint16_t offset; 
-#define ANIMATING_VALUE_TYPE_I8 (1)
-#define ANIMATING_VALUE_TYPE_I16 (2)
-#define ANIMATING_VALUE_TYPE_COLOR (3)
-#define ANIMATING_VALUE_TYPE_FLOAT (4)
-#define ANIMATING_VALUE_TYPE_UNUSED (1 << 7)
-	uint8_t type;
-	uint8_t layer;
-	uint16_t duration, elapsed; // Milliseconds.
-	union { int8_t i8; int16_t i16; uint32_t u32; float f32; } from, to;
-} AnimatingValue;
-
-typedef struct SequenceStateSelector {
-	uint32_t primary;
-	bool focused, checked, indeterminate, _default, itemFocus, listFocus, selected, enter, exit;
-} SequenceStateSelector;
-
-UIElement *elementCanvas;
-UISlider *previewWidth, *previewHeight, *previewScale;
-UIButton *previewTransition, *previewShowGuides, *previewShowComputed, *previewFixAspectRatio;
-uint64_t previewTransitionLastTime;
-AnimatingValue *animatingValues;
-UIPanel *previewPrimaryStatePanel;
-UIButton *previewPrimaryStateIdle;
-UIButton *previewPrimaryStateHovered;
-UIButton *previewPrimaryStatePressed;
-UIButton *previewPrimaryStateDisabled;
-UIButton *previewPrimaryStateInactive;
-UIButton *previewStateFocused;
-UIButton *previewStateChecked;
-UIButton *previewStateIndeterminate;
-UIButton *previewStateDefault;
-UIButton *previewStateItemFocus;
-UIButton *previewStateListFocus;
-UIButton *previewStateSelected;
-UIButton *previewStateBeforeEnter;
-UIButton *previewStateAfterExit;
-UIButton *editPoints;
-UIColorPicker *previewBackgroundColor;
-uint32_t previewPrimaryState;
-SequenceStateSelector currentStateSelector;
-
-UIPanel *panelInspector;
-UIElement **inspectorSubscriptions;
-
-UIWindow *importDialog;
-UITextbox *importPathTextbox;
-UILabel *importPathMessage;
-
-Mod *undoStack, *redoStack;
-bool modApplyUndo;
-
-uint32_t *menuPath;
-
-StyleSet styleSet;
-ModContext selected = MOD_CONTEXT(NULL, NULL, NULL, NULL);
-
-char temporaryOverride[4096];
-
-char *filePath, *exportPath, *stylesPath;
-
-void ModApply(ModData *mod);
-
-char *LoadFile(const char *inputFileName, size_t *byteCount);
-
-void MakeUI(MakeUIState *state, RfItem *item, void *pointer);
-void MakeHeaderAndIndentUI(const char *format, RfState *state, RfItem *item, void *pointer);
-
-#define MAKE_UI(c, p, k) \
-	do { \
-		for (uintptr_t i = 0; i < (c ## _Type).fieldCount; i++) { \
-			MakeUIState state = { 0 }; \
-			state.s.op = OP_MAKE_UI; \
-			state.index = i; \
-			state.recurse = true; \
-			state.inKeyframe = k; \
-			RfItem item = (c ## _Type).fields[i].item; \
-			void *pointer = (uint8_t *) p + (c ## _Type).fields[i].offset; \
-			item.type->op(&state.s, &item, pointer); \
-			if (state.recurse) MakeUI(&state, &item, pointer); \
-		} \
-	} while (0)
-
-// ------------------- Renderer -------------------
-
-int ClampInteger(int from, int to, int value) {
-	if (value < from) return from;
-	if (value > to) return to;
-	return value;
-}
-
-float LinearInterpolate(float from, float to, float progress) {
-	return from + progress * (to - from);
-}
-
-float GammaInterpolate(float from, float to, float progress) {
-	from = from * from;
-	to = to * to;
-	return sqrtf(from + progress * (to - from));
-}
-
-int MaximumInteger3(int a, int b, int c) {
-	if (a >= b && a >= c) {
-		return a;
-	} else if (b >= c) {
-		return b;
-	} else {
-		return c;
-	}
-}
-
-int MinimumInteger3(int a, int b, int c) {
-	if (a <= b && a <= c) {
-		return a;
-	} else if (b <= c) {
-		return b;
-	} else {
-		return c;
-	}
-}
-
-int MaximumInteger(int a, int b) {
-	if (a >= b) {
-		return a;
-	} else {
-		return b;
-	}
-}
-
-int MinimumInteger(int a, int b) {
-	if (a <= b) {
-		return a;
-	} else {
-		return b;
-	}
-}
-
-typedef struct Corners32 { int32_t tl, tr, bl, br; } Corners32;
-typedef struct Rectangle32 { int32_t l, r, t, b; } Rectangle32;
-typedef struct EsRectangle { int32_t l, r, t, b; } EsRectangle;
-
-bool EsRectangleClip(EsRectangle parent, EsRectangle rectangle, EsRectangle *output) {
-	EsRectangle current = parent;
-	EsRectangle intersection = { 0 };
-
-	if (!((current.l > rectangle.r && current.r > rectangle.l)
-			|| (current.t > rectangle.b && current.b > rectangle.t))) {
-		intersection.l = current.l > rectangle.l ? current.l : rectangle.l;
-		intersection.t = current.t > rectangle.t ? current.t : rectangle.t;
-		intersection.r = current.r < rectangle.r ? current.r : rectangle.r;
-		intersection.b = current.b < rectangle.b ? current.b : rectangle.b;
-	}
-
-	if (output) {
-		*output = intersection;
-	}
-
-	return intersection.l < intersection.r && intersection.t < intersection.b;
-}
-
-void BlendPixel(uint32_t *destinationPixel, uint32_t modified, bool fullAlpha) {
-	if ((modified & 0xFF000000) == 0xFF000000) {
-		*destinationPixel = modified;
-	} else if ((modified & 0xFF000000) == 0x00000000) {
-	} else if ((*destinationPixel & 0xFF000000) != 0xFF000000 && fullAlpha) {
-		uint32_t original = *destinationPixel;
-		uint32_t alpha1 = (modified & 0xFF000000) >> 24;
-		uint32_t alpha2 = 255 - alpha1;
-		uint32_t alphaD = (original & 0xFF000000) >> 24;
-		uint32_t alphaD2 = alphaD * alpha2;
-		uint32_t alphaOut = alpha1 + (alphaD2 >> 8);
-
-		if (alphaOut) {
-			uint32_t m2 = alphaD2 / alphaOut;
-			uint32_t m1 = (alpha1 << 8) / alphaOut;
-			if (m2 == 0x100) m2--;
-			if (m1 == 0x100) m1--;
-			uint32_t r2 = m2 * ((original & 0x000000FF) >> 0);
-			uint32_t g2 = m2 * ((original & 0x0000FF00) >> 8);
-			uint32_t b2 = m2 * ((original & 0x00FF0000) >> 16);
-			uint32_t r1 = m1 * ((modified & 0x000000FF) >> 0);
-			uint32_t g1 = m1 * ((modified & 0x0000FF00) >> 8);
-			uint32_t b1 = m1 * ((modified & 0x00FF0000) >> 16);
-			uint32_t result = (alphaOut << 24) 
-				| (0x00FF0000 & ((b1 + b2) << 8)) 
-				| (0x0000FF00 & ((g1 + g2) << 0)) 
-				| (0x000000FF & ((r1 + r2) >> 8));
-			*destinationPixel = result;
-		}
-	} else {
-		uint32_t original = *destinationPixel;
-		uint32_t alpha1 = (modified & 0xFF000000) >> 24;
-		uint32_t alpha2 = 255 - alpha1;
-		uint32_t r2 = alpha2 * ((original & 0x000000FF) >> 0);
-		uint32_t g2 = alpha2 * ((original & 0x0000FF00) >> 8);
-		uint32_t b2 = alpha2 * ((original & 0x00FF0000) >> 16);
-		uint32_t r1 = alpha1 * ((modified & 0x000000FF) >> 0);
-		uint32_t g1 = alpha1 * ((modified & 0x0000FF00) >> 8);
-		uint32_t b1 = alpha1 * ((modified & 0x00FF0000) >> 16);
-		uint32_t result = 0xFF000000 | (0x00FF0000 & ((b1 + b2) << 8)) 
-			| (0x0000FF00 & ((g1 + g2) << 0)) 
-			| (0x000000FF & ((r1 + r2) >> 8));
-		*destinationPixel = result;
-	}
-}
-
-typedef struct EsPaintTarget {
-	void *bits;
-	uint32_t width, height, stride;
-	bool fullAlpha, readOnly;
-} EsPaintTarget;
-
-typedef struct EsPainter {
-	EsRectangle clip;
-	EsPaintTarget *target;
-} EsPainter;
-
-#include "../../desktop/renderer.cpp"
-#include "../../desktop/theme.cpp"
-
-// ------------------- Reflect utilities -------------------
-
-int StringCompareRaw(const char *s1, ptrdiff_t length1, const char *s2, ptrdiff_t length2) {
-	while (length1 || length2) {
-		if (!length1) return -1;
-		if (!length2) return 1;
-
-		char c1 = *s1;
-		char c2 = *s2;
-
-		if (c1 != c2) {
-			return c1 - c2;
-		}
-
-		length1--;
-		length2--;
-		s1++;
-		s2++;
-	}
-
-	return 0;
-}
-
-RfData SaveToGrowableBuffer(RfType *type, size_t byteCount, void *options, void *pointer) {
-	RfGrowableBuffer state = { 0 };
-	state.s.op = RF_OP_SAVE;
-	state.s.allocate = RfRealloc;
-	state.s.access = RfWriteGrowableBuffer;
-	RfItem item = { 0 };
-	item.type = type;
-	item.byteCount = byteCount;
-	item.options = options;
-	type->op(&state.s, &item, pointer);
-	state.data.buffer = realloc(state.data.buffer, state.data.byteCount);
-	return state.data;
-}
-
-bool ArePathsEqual(uint32_t *path1, uint32_t *path2) {
-	if (!path1 && !path2) return true;
-	if (!path1 || !path2) return false;
-	
-	while (true) {
-		if (*path1 != *path2) {
-			return false;
-		}
-
-		if (*path1 == RF_PATH_TERMINATOR) {
-			return true;
-		}
-
-		path1++;
-		path2++;
-	}
-}
-
-uint32_t *DuplicatePath(uint32_t *indices) {
-	uintptr_t count = 0;
-	for (; indices[count] != RF_PATH_TERMINATOR; count++);
-	uint32_t *path = (uint32_t *) malloc((count + 1) * sizeof(uint32_t));
-	memcpy(path, indices, (count + 1) * sizeof(uint32_t));
-	return path;
-}
-
-void PrintPath(uint32_t *indices) {
-	printf("{ ");
-
-	for (uintptr_t i = 0; indices[i] != RF_PATH_TERMINATOR; i++) {
-		printf("%d, ", (int32_t) indices[i]);
-	}
-
-	printf(" }\n");
-}
-
-void *ResolveDataObject(RfPath *path, RfItem *item) {
-	RfItem _item;
-	if (!item) item = &_item;
-
-	void *pointer;
-	item->options = NULL;
-
-	bool inKeyframe = false;
-
-	if (path->indices[0] == PATH_IN_KEYFRAME) {
-		inKeyframe = true;
-
-		// PrintPath(path->indices);
-
-		// Has the property been overridden in the keyframe?
-		for (uintptr_t i = 0; i < arrlenu(selected.keyframe->properties); i++) {
-			// PrintPath(selected.keyframe->properties[i].path);
-
-			if (ArePathsEqual(path->indices, selected.keyframe->properties[i].path)) {
-				// Get the RfItem.
-				pointer = selected.layer;
-				item->type = &Layer_Type;
-				item->byteCount = sizeof(Layer);
-				bool success = RfPathResolve(path + 1, item, &pointer);
-				assert(success);
-				assert(sizeof(temporaryOverride) >= sizeof(item->byteCount));
-
-				// Load the override.
-				RfGrowableBuffer state = { 0 };
-				state.s.allocate = RfRealloc;
-				state.s.access = RfReadGrowableBuffer;
-				state.s.op = RF_OP_LOAD;
-				state.data = selected.keyframe->properties[i].data;
-				item->type->op(&state.s, item, temporaryOverride);
-
-				// Return the temporary buffer.
-				return temporaryOverride;
-			}
-		}
-
-		path++;
-	}
-
-	if (selected.layer && selected.sequence && selected.keyframe && !inKeyframe) {
-		pointer = selected.keyframe;
-		item->type = &Keyframe_Type;
-		item->byteCount = sizeof(Keyframe);
-	} else if (selected.layer && selected.sequence && !inKeyframe) {
-		pointer = selected.sequence;
-		item->type = &Sequence_Type;
-		item->byteCount = sizeof(Sequence);
-	} else if (selected.layer) {
-		pointer = selected.layer;
-		item->type = &Layer_Type;
-		item->byteCount = sizeof(Layer);
-	} else {
-		pointer = selected.style;
-		item->type = &Style_Type;
-		item->byteCount = sizeof(Style);
-	}
-
-	bool success = RfPathResolve(path, item, &pointer);
-	assert(success);
-	return pointer;
-}
-
-// ------------------- Exporting -------------------
-
-typedef struct PathToOffset {
-	uint32_t *path;
-	uintptr_t offset;
-} PathToOffset;
-
-typedef struct ExportState {
-	RfGrowableBuffer buffer;
-	uint32_t *pathStack;
-	PathToOffset *pathToOffsetList;
-} ExportState;
-
-void ExportAddPathToOffset(ExportState *export, uint32_t last, ptrdiff_t offset) {
-	uintptr_t stackPosition = arrlenu(export->pathStack);
-	arrput(export->pathStack, last);
-	arrput(export->pathStack, RF_PATH_TERMINATOR);
-	PathToOffset pathToOffset = { 0 };
-	pathToOffset.path = DuplicatePath(export->pathStack);
-	pathToOffset.offset = export->buffer.data.byteCount + offset;
-	arrsetlen(export->pathStack, stackPosition);
-	arrput(export->pathToOffsetList, pathToOffset);
-}
-
-void ExportAddPathToOffset2(ExportState *export, uint32_t last1, uint32_t last2, ptrdiff_t offset) {
-	arrput(export->pathStack, last1);
-	ExportAddPathToOffset(export, last2, offset);
-	(void) arrpop(export->pathStack);
-}
-
-void ExportAddPathToOffsetForRectangle(ExportState *export, uint32_t last, ptrdiff_t offset) {
-	uintptr_t stackPosition = arrlenu(export->pathStack);
-	arrput(export->pathStack, last);
-	arrput(export->pathStack, RF_PATH_TERMINATOR);
-	PathToOffset pathToOffset = { 0 };
-	pathToOffset.path = DuplicatePath(export->pathStack);
-	pathToOffset.offset = export->buffer.data.byteCount + offset;
-	arrsetlen(export->pathStack, stackPosition);
-	arrput(export->pathToOffsetList, pathToOffset);
-}
-
-void ExportFreePathToOffsetList(PathToOffset *pathToOffsetList) {
-	for (uintptr_t i = 0; i < arrlenu(pathToOffsetList); i++) {
-		free(pathToOffsetList[i].path);
-	}
-
-	arrfree(pathToOffsetList);
-}
-
-RfData ExportToGrowableBuffer(RfType *type, size_t byteCount, void *options, void *pointer, PathToOffset **pathToOffsetList) {
-	ExportState state = { 0 };
-	state.buffer.s.op = OP_EXPORT;
-	state.buffer.s.allocate = RfRealloc;
-	state.buffer.s.access = RfWriteGrowableBuffer;
-
-	RfItem item = { 0 };
-	item.type = type;
-	item.byteCount = byteCount;
-	item.options = options;
-
-	type->op(&state.buffer.s, &item, pointer);
-	state.buffer.data.buffer = realloc(state.buffer.data.buffer, state.buffer.data.byteCount);
-
-	arrfree(state.pathStack);
-
-	*pathToOffsetList = state.pathToOffsetList;
-	return state.buffer.data;
-}
-
-uint32_t ColorLookup(uint32_t id) {
-	for (uintptr_t i = 0; i < arrlenu(styleSet.colors); i++) {
-		if (styleSet.colors[i]->id == id) {
-			return styleSet.colors[i]->value;
-		}
-	}
-
-	assert(false);
-	return 0;
-}
-
-Color *ColorLookupPointer(uint32_t id) {
-	for (uintptr_t i = 0; i < arrlenu(styleSet.colors); i++) {
-		if (styleSet.colors[i]->id == id) {
-			return styleSet.colors[i];
-		}
-	}
-
-	assert(false);
-	return NULL;
-}
-
-#define EXPORT_FIELD(fromType, fromVariable, toType, toVariable, fromField, toField) \
-	ExportAddPathToOffset(export, fromType ## _ ## fromField, offsetof(toType, toField)); \
-	toVariable.toField = fromVariable->fromField;
-#define EXPORT_FIELD_COLOR(fromType, fromVariable, toType, toVariable, fromField, toField) \
-	ExportAddPathToOffset(export, fromType ## _ ## fromField, offsetof(toType, toField)); \
-	toVariable.toField = ColorLookup(fromVariable->fromField);
-#define EXPORT_FIELD_ALIGN(fromType, fromVariable, toType, toVariable, fromHField, fromVField, toField) \
-	toVariable.toField = ((fromVariable->fromHField == ALIGN_START)  ? ES_TEXT_H_LEFT   : 0) \
-			   | ((fromVariable->fromHField == ALIGN_CENTER) ? ES_TEXT_H_CENTER : 0) \
-			   | ((fromVariable->fromHField == ALIGN_END)    ? ES_TEXT_H_RIGHT  : 0) \
-			   | ((fromVariable->fromVField == ALIGN_START)  ? ES_TEXT_V_TOP    : 0) \
-			   | ((fromVariable->fromVField == ALIGN_CENTER) ? ES_TEXT_V_CENTER : 0) \
-			   | ((fromVariable->fromVField == ALIGN_END)    ? ES_TEXT_V_BOTTOM : 0);
-#define EXPORT_RECTANGLE8_FIELD(fromType, fromVariable, toType, toVariable, fromField, toField) \
-	ExportAddPathToOffset2(export, fromType ## _ ## fromField, Rectangle8_l, offsetof(toType, toField.l)); \
-	ExportAddPathToOffset2(export, fromType ## _ ## fromField, Rectangle8_r, offsetof(toType, toField.r)); \
-	ExportAddPathToOffset2(export, fromType ## _ ## fromField, Rectangle8_t, offsetof(toType, toField.t)); \
-	ExportAddPathToOffset2(export, fromType ## _ ## fromField, Rectangle8_b, offsetof(toType, toField.b)); \
-	toVariable.toField = fromVariable->fromField;
-#define EXPORT_RECTANGLE16_FIELD(fromType, fromVariable, toType, toVariable, fromField, toField) \
-	ExportAddPathToOffset2(export, fromType ## _ ## fromField, Rectangle16_l, offsetof(toType, toField.l)); \
-	ExportAddPathToOffset2(export, fromType ## _ ## fromField, Rectangle16_r, offsetof(toType, toField.r)); \
-	ExportAddPathToOffset2(export, fromType ## _ ## fromField, Rectangle16_t, offsetof(toType, toField.t)); \
-	ExportAddPathToOffset2(export, fromType ## _ ## fromField, Rectangle16_b, offsetof(toType, toField.b)); \
-	toVariable.toField.l = fromVariable->fromField.l; \
-	toVariable.toField.r = fromVariable->fromField.r; \
-	toVariable.toField.t = fromVariable->fromField.t; \
-	toVariable.toField.b = fromVariable->fromField.b;
-
-void PaintSolidOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_EXPORT) {
-		PaintSolid *solid = (PaintSolid *) pointer;
-		ThemePaintSolid themeSolid = { 0 };
-		ExportAddPathToOffset((ExportState *) state, PaintSolid_color, offsetof(ThemePaintSolid, color));
-		themeSolid.color = ColorLookup(solid->color);
-		state->access(state, &themeSolid, sizeof(themeSolid));
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void PaintOverwriteOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_EXPORT) {
-		PaintOverwrite *overwrite = (PaintOverwrite *) pointer;
-		ThemePaintSolid themeSolid = { 0 };
-		ExportAddPathToOffset((ExportState *) state, PaintOverwrite_color, offsetof(ThemePaintSolid, color));
-		themeSolid.color = ColorLookup(overwrite->color);
-		state->access(state, &themeSolid, sizeof(themeSolid));
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void GradientStopOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_EXPORT) {
-		GradientStop *stop = (GradientStop *) pointer;
-		ThemeGradientStop themeStop = { 0 };
-		ExportAddPathToOffset((ExportState *) state, GradientStop_color, offsetof(ThemeGradientStop, color));
-		themeStop.color = ColorLookup(stop->color);
-		ExportAddPathToOffset((ExportState *) state, GradientStop_position, offsetof(ThemeGradientStop, position));
-		themeStop.position = stop->position;
-		state->access(state, &themeStop, sizeof(themeStop));
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void PaintLinearGradientOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_EXPORT) {
-		ExportState *export = (ExportState *) state;
-		PaintLinearGradient *gradient = (PaintLinearGradient *) pointer;
-		ThemePaintLinearGradient themeGradient = { 0 };
-		EXPORT_FIELD(PaintLinearGradient, gradient, ThemePaintLinearGradient, themeGradient, transformX, transform[0]);
-		EXPORT_FIELD(PaintLinearGradient, gradient, ThemePaintLinearGradient, themeGradient, transformY, transform[1]);
-		EXPORT_FIELD(PaintLinearGradient, gradient, ThemePaintLinearGradient, themeGradient, transformStart, transform[2]);
-		themeGradient.useGammaInterpolation = gradient->useGammaInterpolation;
-		themeGradient.useDithering = gradient->useDithering;
-		themeGradient.useSystemColor = gradient->useSystemHue;
-		themeGradient.stopCount = arrlenu(gradient->stops);
-		themeGradient.repeatMode = gradient->repeat == GRADIENT_REPEAT_CLAMP ? RAST_REPEAT_CLAMP
-			: gradient->repeat == GRADIENT_REPEAT_NORMAL ? RAST_REPEAT_NORMAL
-			: gradient->repeat == GRADIENT_REPEAT_MIRROR ? RAST_REPEAT_MIRROR : 0;
-		state->access(state, &themeGradient, sizeof(themeGradient));
-		uintptr_t stackPosition = arrlenu(export->pathStack);
-
-		for (uintptr_t i = 0; i < arrlenu(gradient->stops); i++) {
-			arrput(export->pathStack, PaintLinearGradient_stops);
-			arrput(export->pathStack, i);
-			GradientStopOp(state, NULL, gradient->stops + i);
-			arrsetlen(export->pathStack, stackPosition);
-		}
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void PaintRadialGradientOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_EXPORT) {
-		ExportState *export = (ExportState *) state;
-		PaintRadialGradient *gradient = (PaintRadialGradient *) pointer;
-		ThemePaintRadialGradient themeGradient = { 0 };
-		EXPORT_FIELD(PaintRadialGradient, gradient, ThemePaintRadialGradient, themeGradient, transform0, transform[0]);
-		EXPORT_FIELD(PaintRadialGradient, gradient, ThemePaintRadialGradient, themeGradient, transform1, transform[1]);
-		EXPORT_FIELD(PaintRadialGradient, gradient, ThemePaintRadialGradient, themeGradient, transform2, transform[2]);
-		EXPORT_FIELD(PaintRadialGradient, gradient, ThemePaintRadialGradient, themeGradient, transform3, transform[3]);
-		EXPORT_FIELD(PaintRadialGradient, gradient, ThemePaintRadialGradient, themeGradient, transform4, transform[4]);
-		EXPORT_FIELD(PaintRadialGradient, gradient, ThemePaintRadialGradient, themeGradient, transform5, transform[5]);
-		themeGradient.useGammaInterpolation = gradient->useGammaInterpolation;
-		themeGradient.stopCount = arrlenu(gradient->stops);
-		themeGradient.repeatMode = gradient->repeat == GRADIENT_REPEAT_CLAMP ? RAST_REPEAT_CLAMP
-			: gradient->repeat == GRADIENT_REPEAT_NORMAL ? RAST_REPEAT_NORMAL
-			: gradient->repeat == GRADIENT_REPEAT_MIRROR ? RAST_REPEAT_MIRROR : 0;
-		state->access(state, &themeGradient, sizeof(themeGradient));
-		uintptr_t stackPosition = arrlenu(export->pathStack);
-
-		for (uintptr_t i = 0; i < arrlenu(gradient->stops); i++) {
-			arrput(export->pathStack, PaintRadialGradient_stops);
-			arrput(export->pathStack, i);
-			GradientStopOp(state, NULL, gradient->stops + i);
-			arrsetlen(export->pathStack, stackPosition);
-		}
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-Layer *LayerLookup(uint64_t id) {
-	for (uintptr_t i = 0; i < arrlenu(styleSet.layers); i++) {
-		if (styleSet.layers[i]->id == id) {
-			return styleSet.layers[i];
-		}
-	}
-
-	return NULL;
-}
-
-void LayerBoxOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_EXPORT) {
-		ExportState *export = (ExportState *) state;
-		LayerBox *box = (LayerBox *) pointer;
-
-#if 0
-#define EsContainerOf(type, member, pointer) ((type *) ((uint8_t *) pointer - offsetof(type, member)))
-		uint64_t layerID = EsContainerOf(Layer, base.box, box)->id;
-#endif
-
-		ThemeLayerBox themeBox = { 0 };
-		ExportAddPathToOffset2(export, LayerBox_borders, Rectangle8_l, offsetof(ThemeLayerBox, borders.l));
-		ExportAddPathToOffset2(export, LayerBox_borders, Rectangle8_r, offsetof(ThemeLayerBox, borders.r));
-		ExportAddPathToOffset2(export, LayerBox_borders, Rectangle8_t, offsetof(ThemeLayerBox, borders.t));
-		ExportAddPathToOffset2(export, LayerBox_borders, Rectangle8_b, offsetof(ThemeLayerBox, borders.b));
-		themeBox.borders = box->borders;
-		ExportAddPathToOffset2(export, LayerBox_corners, Corners8_tl, offsetof(ThemeLayerBox, corners.tl));
-		ExportAddPathToOffset2(export, LayerBox_corners, Corners8_tr, offsetof(ThemeLayerBox, corners.tr));
-		ExportAddPathToOffset2(export, LayerBox_corners, Corners8_bl, offsetof(ThemeLayerBox, corners.bl));
-		ExportAddPathToOffset2(export, LayerBox_corners, Corners8_br, offsetof(ThemeLayerBox, corners.br));
-		themeBox.corners = box->corners;
-		if (box->blurred) themeBox.flags |= THEME_LAYER_BOX_IS_BLURRED;
-		if (box->autoCorners) themeBox.flags |= THEME_LAYER_BOX_AUTO_CORNERS;
-		if (box->autoBorders) themeBox.flags |= THEME_LAYER_BOX_AUTO_BORDERS;
-		if (box->shadowHiding) themeBox.flags |= THEME_LAYER_BOX_SHADOW_HIDING;
-		themeBox.mainPaintType = box->mainPaint.tag == Paint_solid + 1 ? THEME_PAINT_SOLID 
-			: box->mainPaint.tag == Paint_linearGradient + 1 ? THEME_PAINT_LINEAR_GRADIENT 
-			: box->mainPaint.tag == Paint_overwrite + 1 ? THEME_PAINT_OVERWRITE : 0;
-		themeBox.borderPaintType = box->borderPaint.tag == Paint_solid + 1 ? THEME_PAINT_SOLID
-			: box->borderPaint.tag == Paint_linearGradient + 1 ? THEME_PAINT_LINEAR_GRADIENT 
-			: box->borderPaint.tag == Paint_overwrite + 1 ? THEME_PAINT_OVERWRITE : 0;
-		state->access(state, &themeBox, sizeof(themeBox));
-
-		uintptr_t stackPosition = arrlenu(export->pathStack);
-
-		if (box->mainPaint.tag) {
-			RfField *mainPaintField = Paint_Type.fields + box->mainPaint.tag - 1;
-			arrput(export->pathStack, LayerBox_mainPaint);
-			arrput(export->pathStack, box->mainPaint.tag - 1);
-			mainPaintField->item.type->op(state, &mainPaintField->item, (uint8_t *) pointer 
-					+ mainPaintField->offset + LayerBox_Type.fields[LayerBox_mainPaint].offset);
-			arrsetlen(export->pathStack, stackPosition);
-		}
-
-		if (box->borderPaint.tag) {
-			RfField *borderPaintField = Paint_Type.fields + box->borderPaint.tag - 1;
-			arrput(export->pathStack, LayerBox_borderPaint);
-			arrput(export->pathStack, box->borderPaint.tag - 1);
-			borderPaintField->item.type->op(state, &borderPaintField->item, (uint8_t *) pointer 
-					+ borderPaintField->offset + LayerBox_Type.fields[LayerBox_borderPaint].offset);
-			arrsetlen(export->pathStack, stackPosition);
-		}
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void LayerTextOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_EXPORT) {
-		ExportState *export = (ExportState *) state;
-		LayerText *layer = (LayerText *) pointer;
-
-		ThemeLayerText themeLayer = { 0 };
-
-		EXPORT_FIELD_COLOR(LayerText, layer, ThemeLayerText, themeLayer, color, color);
-		EXPORT_FIELD(LayerText, layer, ThemeLayerText, themeLayer, blur, blur);
-
-		state->access(state, &themeLayer, sizeof(themeLayer));
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void PathFillSolidOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_EXPORT) {
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void PathFillContourOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_EXPORT) {
-		ExportState *export = (ExportState *) state;
-		PathFillContour *fill = (PathFillContour *) pointer;
-
-		ThemeLayerPathFillContour themeFill = { 0 };
-
-		EXPORT_FIELD(PathFillContour, fill, ThemeLayerPathFillContour, themeFill, internalWidth, internalWidth);
-		EXPORT_FIELD(PathFillContour, fill, ThemeLayerPathFillContour, themeFill, externalWidth, externalWidth);
-
-		if (fill->joinMode != JOIN_MODE_BEVEL) {
-			EXPORT_FIELD(PathFillContour, fill, ThemeLayerPathFillContour, themeFill, miterLimit, miterLimit);
-		}
-
-		themeFill.mode = (fill->joinMode == JOIN_MODE_MITER ? RAST_LINE_JOIN_MITER 
-				: fill->joinMode == JOIN_MODE_ROUND ? RAST_LINE_JOIN_ROUND
-				: fill->joinMode == JOIN_MODE_BEVEL ? RAST_LINE_JOIN_MITER : 0)
-				| ((fill->capMode == CAP_MODE_FLAT ? RAST_LINE_CAP_FLAT
-				: fill->capMode == CAP_MODE_ROUND ? RAST_LINE_CAP_ROUND
-				: fill->capMode == CAP_MODE_SQUARE ? RAST_LINE_CAP_SQUARE : 0) << 2)
-				| (fill->integerWidthsOnly ? 0x80 : 0);
-
-		state->access(state, &themeFill, sizeof(themeFill));
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void PathFillDashOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_EXPORT) {
-		ExportState *export = (ExportState *) state;
-		PathFillDash *fill = (PathFillDash *) pointer;
-
-		ThemeLayerPathFillDash themeFill = { 0 };
-
-		EXPORT_FIELD(PathFillDash, fill, ThemeLayerPathFillDash, themeFill, gap, gap);
-		EXPORT_FIELD(PathFillDash, fill, ThemeLayerPathFillDash, themeFill, length, length);
-
-		arrput(export->pathStack, PathFillDash_contour);
-
-		EXPORT_FIELD(PathFillContour, (&fill->contour), ThemeLayerPathFillContour, themeFill.contour, internalWidth, internalWidth);
-		EXPORT_FIELD(PathFillContour, (&fill->contour), ThemeLayerPathFillContour, themeFill.contour, externalWidth, externalWidth);
-
-		if (fill->contour.joinMode != JOIN_MODE_BEVEL) {
-			EXPORT_FIELD(PathFillContour, (&fill->contour), ThemeLayerPathFillContour, themeFill.contour, miterLimit, miterLimit);
-		}
-
-		(void) arrpop(export->pathStack);
-
-		themeFill.contour.mode = (fill->contour.joinMode == JOIN_MODE_MITER ? RAST_LINE_JOIN_MITER 
-				: fill->contour.joinMode == JOIN_MODE_ROUND ? RAST_LINE_JOIN_ROUND
-				: fill->contour.joinMode == JOIN_MODE_BEVEL ? RAST_LINE_JOIN_MITER : 0)
-				| ((fill->contour.capMode == CAP_MODE_FLAT ? RAST_LINE_CAP_FLAT
-				: fill->contour.capMode == CAP_MODE_ROUND ? RAST_LINE_CAP_ROUND
-				: fill->contour.capMode == CAP_MODE_SQUARE ? RAST_LINE_CAP_SQUARE : 0) << 2)
-				| (fill->contour.integerWidthsOnly ? 0x80 : 0);
-
-		state->access(state, &themeFill, sizeof(themeFill));
-	} else if (state->op == OP_MAKE_UI) {
-		MakeHeaderAndIndentUI("Dash %d", state, item, pointer);
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void PathFillDashedOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_EXPORT) {
-		ExportState *export = (ExportState *) state;
-		PathFillDashed *fill = (PathFillDashed *) pointer;
-		uintptr_t stackPosition = arrlenu(export->pathStack);
-
-		for (uintptr_t i = 0; i < arrlenu(fill->dashes); i++) {
-			arrput(export->pathStack, PathFillDashed_dashes);
-			arrput(export->pathStack, i);
-			PathFillDashOp(state, NULL, fill->dashes + i);
-			arrsetlen(export->pathStack, stackPosition);
-		}
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void PathFillOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_EXPORT) {
-		ExportState *export = (ExportState *) state;
-		PathFill *fill = (PathFill *) pointer;
-
-		ThemeLayerPathFill themeFill = { 0 };
-		themeFill.paintAndFillType |= fill->paint.tag == Paint_solid + 1 ? THEME_PAINT_SOLID 
-			: fill->paint.tag == Paint_linearGradient + 1 ? THEME_PAINT_LINEAR_GRADIENT 
-			: fill->paint.tag == Paint_radialGradient + 1 ? THEME_PAINT_RADIAL_GRADIENT 
-			: fill->paint.tag == Paint_overwrite + 1 ? THEME_PAINT_OVERWRITE : 0;
-		themeFill.paintAndFillType |= fill->mode.tag == PathFillMode_solid + 1 ? THEME_PATH_FILL_SOLID
-			: fill->mode.tag == PathFillMode_contour + 1 ? THEME_PATH_FILL_CONTOUR
-			: fill->mode.tag == PathFillMode_dashed + 1 ? THEME_PATH_FILL_DASHED : 0;
-		themeFill.dashCount = fill->mode.tag == PathFillMode_dashed + 1 ? arrlen(fill->mode.dashed.dashes) : 0;
-		state->access(state, &themeFill, sizeof(themeFill));
-
-		uintptr_t stackPosition = arrlenu(export->pathStack);
-
-		if (fill->paint.tag) {
-			RfField *paintField = Paint_Type.fields + fill->paint.tag - 1;
-			arrput(export->pathStack, PathFill_paint);
-			arrput(export->pathStack, fill->paint.tag - 1);
-			paintField->item.type->op(state, &paintField->item, (uint8_t *) pointer 
-					+ paintField->offset + PathFill_Type.fields[PathFill_paint].offset);
-			arrsetlen(export->pathStack, stackPosition);
-		}
-
-		if (fill->mode.tag) {
-			RfField *modeField = PathFillMode_Type.fields + fill->mode.tag - 1;
-			arrput(export->pathStack, PathFill_mode);
-			arrput(export->pathStack, fill->mode.tag - 1);
-			modeField->item.type->op(state, &modeField->item, (uint8_t *) pointer 
-					+ modeField->offset + PathFill_Type.fields[PathFill_mode].offset);
-			arrsetlen(export->pathStack, stackPosition);
-		}
-	} else if (state->op == OP_MAKE_UI) {
-		MakeHeaderAndIndentUI("Fill %d", state, item, pointer);
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void LayerPathOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_EXPORT) {
-		ExportState *export = (ExportState *) state;
-		LayerPath *layer = (LayerPath *) pointer;
-
-		ThemeLayerPath themeLayer = { 0 };
-		if (layer->evenOdd) themeLayer.flags |= THEME_LAYER_PATH_FILL_EVEN_ODD;
-		if (layer->closed) themeLayer.flags |= THEME_LAYER_PATH_CLOSED;
-		themeLayer.pointCount = arrlen(layer->points);
-		themeLayer.fillCount = arrlen(layer->fills);
-		EXPORT_FIELD(LayerPath, layer, ThemeLayerPath, themeLayer, alpha, alpha);
-		state->access(state, &themeLayer, sizeof(themeLayer));
-
-		uintptr_t stackPosition = arrlenu(export->pathStack);
-
-		for (uintptr_t i = 0; i < arrlenu(layer->points); i++) {
-			arrput(export->pathStack, LayerPath_points);
-			arrput(export->pathStack, i);
-			PathPointOp(state, NULL, layer->points + i);
-			arrsetlen(export->pathStack, stackPosition);
-		}
-
-		for (uintptr_t i = 0; i < arrlenu(layer->fills); i++) {
-			arrput(export->pathStack, LayerPath_fills);
-			arrput(export->pathStack, i);
-			PathFillOp(state, NULL, layer->fills + i);
-			arrsetlen(export->pathStack, stackPosition);
-		}
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-Rectangle8 StyleCalculateMaximumGlobalOutsets(uint64_t *styleLayers) {
-	Rectangle8 globalOutsets = { 0 };
-
-	for (uintptr_t i = 0; i < arrlenu(styleLayers); i++) {
-		Layer *layer = LayerLookup(styleLayers[i]);
-		
-		if (layer->base.tag != LayerBase_metrics + 1) {
-			continue;
-		}
-
-		globalOutsets.l = MaximumInteger(0, -layer->base.metrics.globalOffset.l);
-		globalOutsets.r = MaximumInteger(0,  layer->base.metrics.globalOffset.r);
-		globalOutsets.t = MaximumInteger(0, -layer->base.metrics.globalOffset.t);
-		globalOutsets.b = MaximumInteger(0,  layer->base.metrics.globalOffset.b);
-
-		for (uintptr_t j = 0; j < arrlenu(layer->sequences); j++) {
-			for (uintptr_t k = 0; k < arrlenu(layer->sequences[j]->keyframes); k++) {
-				for (uintptr_t l = 0; l < arrlenu(layer->sequences[j]->keyframes[k]->properties); l++) {
-					Property property = layer->sequences[j]->keyframes[k]->properties[l];
-
-					if (property.path[0] != PATH_IN_KEYFRAME || property.path[1] != Layer_base 
-							|| property.path[2] != 0 || property.path[3] != LayerMetrics_globalOffset) {
-						continue;
-					}
-
-					int8_t value = 0;
-					RfGrowableBuffer state = { 0 };
-					state.s.access = RfReadGrowableBuffer;
-					state.data = property.data;
-					state.s.access(&state.s, &value, sizeof(int8_t));
-
-					if (layer->position.l == 0 && -value > globalOutsets.l && property.path[4] == Rectangle8_l) {
-						globalOutsets.l = -value;
-					}
-
-					if (layer->position.r == 100 && value > globalOutsets.r && property.path[4] == Rectangle8_r) {
-						globalOutsets.r = value;
-					}
-
-					if (layer->position.t == 0 && -value > globalOutsets.t && property.path[4] == Rectangle8_t) {
-						globalOutsets.t = -value;
-					}
-
-					if (layer->position.b == 100 && value > globalOutsets.b && property.path[4] == Rectangle8_b) {
-						globalOutsets.b = value;
-					} 
-				}
-			}
-		}
-
-		break;
-	}
-
-	return globalOutsets;
-}
-
-Rectangle8 Rectangle8Add(Rectangle8 a, Rectangle8 b) {
-	a.l += b.l;
-	a.t += b.t;
-	a.r += b.r;
-	a.b += b.b;
-	return a;
-}
-
-Rectangle8 StyleCalculatePaintOutsets(uint64_t *styleLayers) {
-	Rectangle8 paintOutsets = { 0 };
-
-	for (uintptr_t i = 0; i < arrlenu(styleLayers); i++) {
-		Layer *layer = LayerLookup(styleLayers[i]);
-
-		if (layer->position.l == 0 && -layer->offset.l > paintOutsets.l) {
-			paintOutsets.l = -layer->offset.l;
-		}
-
-		if (layer->position.r == 100 && layer->offset.r > paintOutsets.r) {
-			paintOutsets.r = layer->offset.r;
-		}
-
-		if (layer->position.t == 0 && -layer->offset.t > paintOutsets.t) {
-			paintOutsets.t = -layer->offset.t;
-		}
-
-		if (layer->position.b == 100 && layer->offset.b > paintOutsets.b) {
-			paintOutsets.b = layer->offset.b;
-		}
-
-		for (uintptr_t j = 0; j < arrlenu(layer->sequences); j++) {
-			for (uintptr_t k = 0; k < arrlenu(layer->sequences[j]->keyframes); k++) {
-				for (uintptr_t l = 0; l < arrlenu(layer->sequences[j]->keyframes[k]->properties); l++) {
-					Property property = layer->sequences[j]->keyframes[k]->properties[l];
-
-					if (property.path[0] != PATH_IN_KEYFRAME || property.path[1] != Layer_offset) {
-						continue;
-					}
-
-					int8_t value = 0;
-					RfGrowableBuffer state = { 0 };
-					state.s.access = RfReadGrowableBuffer;
-					state.data = property.data;
-					state.s.access(&state.s, &value, sizeof(int8_t));
-
-					if (layer->position.l == 0 && -value > paintOutsets.l && property.path[2] == Rectangle8_l) {
-						paintOutsets.l = -value;
-					}
-
-					if (layer->position.r == 100 && value > paintOutsets.r && property.path[2] == Rectangle8_r) {
-						paintOutsets.r = value;
-					}
-
-					if (layer->position.t == 0 && -value > paintOutsets.t && property.path[2] == Rectangle8_t) {
-						paintOutsets.t = -value;
-					}
-
-					if (layer->position.b == 100 && value > paintOutsets.b && property.path[2] == Rectangle8_b) {
-						paintOutsets.b = value;
-					} 
-				}
-			}
-		}
-	}
-
-	return Rectangle8Add(paintOutsets, StyleCalculateMaximumGlobalOutsets(styleLayers));
-}
-
-Rectangle8 StyleCalculateOpaqueInsets(uint64_t *styleLayers) {
-	Rectangle8 opaqueInsets = (Rectangle8) { 0x7F, 0x7F, 0x7F, 0x7F };
-
-	for (uintptr_t i = 0; i < arrlenu(styleLayers); i++) {
-		Layer *layer = LayerLookup(styleLayers[i]);
-
-		if (layer->position.l != 0 || layer->position.r != 100 || layer->position.t != 0 || layer->position.b != 100
-				|| layer->base.tag != LayerBase_box + 1 || layer->base.box.shadowHiding || layer->base.box.blurred) {
-			continue;
-		}
-		
-		Paint *paint = &layer->base.box.mainPaint;
-
-		bool isOpaque = false;
-
-		if (paint->tag == Paint_solid + 1) {
-			isOpaque = (paint->solid.color & 0xFF000000) == 0xFF000000;
-		} else if (paint->tag == Paint_linearGradient + 1) {
-			isOpaque = true;
-
-			for (uintptr_t j = 0; j < arrlenu(paint->linearGradient.stops); j++) {
-				if ((paint->linearGradient.stops[j].color & 0xFF000000) != 0xFF000000) {
-					isOpaque = false;
-				}
-			}
-		} else if (paint->tag == Paint_overwrite + 1) {
-			isOpaque = true;
-		}
-
-		Rectangle8 largestBorders = layer->base.box.borders;
-		Corners8 largestCorners = layer->base.box.corners;
-
-		for (uintptr_t j = 0; j < arrlenu(layer->sequences); j++) {
-			for (uintptr_t k = 0; k < arrlenu(layer->sequences[j]->keyframes); k++) {
-				for (uintptr_t l = 0; l < arrlenu(layer->sequences[j]->keyframes[k]->properties); l++) {
-					Property property = layer->sequences[j]->keyframes[k]->properties[l];
-
-					if (property.path[0] == PATH_IN_KEYFRAME && property.path[1] == Layer_base
-							&& property.path[2] == LayerBase_box && property.path[3] == LayerBox_borders) {
-						int8_t value = 0;
-						RfGrowableBuffer state = { 0 };
-						state.s.access = RfReadGrowableBuffer;
-						state.data = property.data;
-						state.s.access(&state.s, &value, sizeof(int8_t));
-
-						if (property.path[4] == Rectangle8_l && largestBorders.l < value) largestBorders.l = value;
-						if (property.path[4] == Rectangle8_r && largestBorders.r < value) largestBorders.r = value;
-						if (property.path[4] == Rectangle8_t && largestBorders.t < value) largestBorders.t = value;
-						if (property.path[4] == Rectangle8_b && largestBorders.b < value) largestBorders.b = value;
-					} else if (property.path[0] == PATH_IN_KEYFRAME && property.path[1] == Layer_base
-							&& property.path[2] == LayerBase_box && property.path[3] == LayerBox_corners) {
-						int8_t value = 0;
-						RfGrowableBuffer state = { 0 };
-						state.s.access = RfReadGrowableBuffer;
-						state.data = property.data;
-						state.s.access(&state.s, &value, sizeof(int8_t));
-
-						if (property.path[4] == Corners8_tl && largestCorners.tl < value) largestCorners.tl = value;
-						if (property.path[4] == Corners8_tr && largestCorners.tr < value) largestCorners.tr = value;
-						if (property.path[4] == Corners8_bl && largestCorners.bl < value) largestCorners.bl = value;
-						if (property.path[4] == Corners8_br && largestCorners.br < value) largestCorners.br = value;
-					}
-
-					if (property.path[0] != PATH_IN_KEYFRAME || property.path[1] != Layer_base
-							|| property.path[2] != LayerBase_box || property.path[3] != LayerBox_mainPaint
-							|| property.path[4] != paint->tag - 1) {
-						continue;
-					}
-
-					if (paint->tag == Paint_solid + 1) {
-						if (property.path[5] != PaintSolid_color) {
-							continue;
-						}
-					} else if (paint->tag == Paint_linearGradient + 1) {
-						if (property.path[5] != PaintLinearGradient_stops
-								|| property.path[7] != GradientStop_color) {
-							continue;
-						}
-					}
-
-					uint32_t value = 0;
-					RfGrowableBuffer state = { 0 };
-					state.s.access = RfReadGrowableBuffer;
-					state.data = property.data;
-					state.s.access(&state.s, &value, sizeof(uint32_t));
-
-					if ((value & 0xFF000000) != 0xFF000000) {
-						isOpaque = false;
-					}
-				}
-			}
-		}
-
-		if (isOpaque) {
-			opaqueInsets.l = MinimumInteger(opaqueInsets.l, MaximumInteger3(largestCorners.tl, largestCorners.bl, largestBorders.l));
-			opaqueInsets.r = MinimumInteger(opaqueInsets.r, MaximumInteger3(largestCorners.tr, largestCorners.br, largestBorders.r));
-			opaqueInsets.t = MinimumInteger(opaqueInsets.t, MaximumInteger3(largestCorners.tl, largestCorners.tr, largestBorders.t));
-			opaqueInsets.b = MinimumInteger(opaqueInsets.b, MaximumInteger3(largestCorners.bl, largestCorners.br, largestBorders.b));
-		}
-	}
-
-	return Rectangle8Add(opaqueInsets, StyleCalculateMaximumGlobalOutsets(styleLayers));
-}
-
-Rectangle8 StyleCalculateApproximateBorders(uint64_t *styleLayers) {
-	for (uintptr_t i = 0; i < arrlenu(styleLayers); i++) {
-		Layer *layer = LayerLookup(styleLayers[i]);
-
-		if (layer->position.l != 0 || layer->position.r != 100 || layer->position.t != 0 || layer->position.b != 100
-				|| layer->base.tag != LayerBase_box + 1 || layer->base.box.shadowHiding 
-				|| layer->base.box.blurred || !layer->base.box.borderPaint.tag
-				|| layer->mode != LAYER_MODE_BACKGROUND) {
-			continue;
-		}
-
-		return layer->base.box.borders;
-	}
-
-	return (Rectangle8) { 0 };
-}
-
-void LayerMetricsOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_EXPORT) {
-		ThemeMetrics metrics = { 0 };
-		ExportState *export = (ExportState *) state;
-		LayerMetrics *layer = (LayerMetrics *) pointer;
-		LayerMetrics *inherit = NULL;
-
-		if (layer->inheritText.byteCount) {
-			for (uintptr_t i = 0; i < arrlenu(styleSet.styles); i++) {
-				Style *style = styleSet.styles[i];
-
-				if (layer->inheritText.byteCount == style->name.byteCount 
-						&& 0 == memcmp(layer->inheritText.buffer, style->name.buffer, style->name.byteCount)) {
-					inherit = &LayerLookup(style->layers[0])->base.metrics;
-					break;
-				}
-			}
-		}
-
-		metrics.insets.l = layer->insets.l;
-		metrics.insets.r = layer->insets.r;
-		metrics.insets.t = layer->insets.t;
-		metrics.insets.b = layer->insets.b;
-		metrics.clipEnabled = layer->clipEnabled == CLIP_MODE_ENABLED;
-		metrics.clipInsets.l = layer->clipInsets.l;
-		metrics.clipInsets.r = layer->clipInsets.r;
-		metrics.clipInsets.t = layer->clipInsets.t;
-		metrics.clipInsets.b = layer->clipInsets.b;
-		metrics.cursor = layer->cursor;
-		metrics.preferredWidth = layer->preferredSize.width;
-		metrics.preferredHeight = layer->preferredSize.height;
-		metrics.minimumWidth = layer->minimumSize.width;
-		metrics.minimumHeight = layer->minimumSize.height;
-		metrics.maximumWidth = layer->maximumSize.width;
-		metrics.maximumHeight = layer->maximumSize.height;
-		metrics.gapMajor = layer->gaps.major;
-		metrics.gapMinor = layer->gaps.minor;
-		metrics.gapWrap = layer->gaps.wrap;
-
-		int fontFamily = inherit ? inherit->fontFamily : layer->fontFamily;
-		metrics.fontFamily = fontFamily == FONT_FAMILY_SANS ? 0xFFFF : fontFamily == FONT_FAMILY_SERIF ? 0xFFFE : 0xFFFD;
-
-		if (inherit) {
-			EXPORT_FIELD_COLOR(LayerMetrics, inherit, ThemeMetrics, metrics, textColor, textColor);
-			EXPORT_FIELD_COLOR(LayerMetrics, inherit, ThemeMetrics, metrics, selectedBackground, selectedBackground);
-			EXPORT_FIELD_COLOR(LayerMetrics, inherit, ThemeMetrics, metrics, selectedText, selectedText);
-			EXPORT_FIELD(LayerMetrics, inherit, ThemeMetrics, metrics, textSize, textSize);
-			EXPORT_FIELD(LayerMetrics, inherit, ThemeMetrics, metrics, fontWeight, fontWeight);
-			EXPORT_FIELD(LayerMetrics, inherit, ThemeMetrics, metrics, italic, isItalic);
-		} else {
-			EXPORT_FIELD_COLOR(LayerMetrics, layer, ThemeMetrics, metrics, textColor, textColor);
-			EXPORT_FIELD_COLOR(LayerMetrics, layer, ThemeMetrics, metrics, selectedBackground, selectedBackground);
-			EXPORT_FIELD_COLOR(LayerMetrics, layer, ThemeMetrics, metrics, selectedText, selectedText);
-			EXPORT_FIELD(LayerMetrics, layer, ThemeMetrics, metrics, textSize, textSize);
-			EXPORT_FIELD(LayerMetrics, layer, ThemeMetrics, metrics, fontWeight, fontWeight);
-			EXPORT_FIELD(LayerMetrics, layer, ThemeMetrics, metrics, italic, isItalic);
-		}
-
-		EXPORT_FIELD(LayerMetrics, layer, ThemeMetrics, metrics, iconSize, iconSize);
-		EXPORT_FIELD_COLOR(LayerMetrics, layer, ThemeMetrics, metrics, iconColor, iconColor);
-		EXPORT_FIELD_ALIGN(LayerMetrics, layer, ThemeMetrics, metrics, textHorizontalAlign, textVerticalAlign, textAlign);
-		if (layer->ellipsis) metrics.textAlign |= ES_TEXT_ELLIPSIS;
-		if (layer->wrapText) metrics.textAlign |= ES_TEXT_WRAP;
-		state->access(state, &metrics, sizeof(metrics));
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void LayerOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_EXPORT) {
-		ExportState *export = (ExportState *) state;
-		Layer *layer = (Layer *) pointer;
-		ThemeLayer themeLayer = { 0 };
-		ExportAddPathToOffset2(export, Layer_offset, Rectangle8_l, offsetof(ThemeLayer, offset.l));
-		ExportAddPathToOffset2(export, Layer_offset, Rectangle8_r, offsetof(ThemeLayer, offset.r));
-		ExportAddPathToOffset2(export, Layer_offset, Rectangle8_t, offsetof(ThemeLayer, offset.t));
-		ExportAddPathToOffset2(export, Layer_offset, Rectangle8_b, offsetof(ThemeLayer, offset.b));
-		themeLayer.offset = layer->offset;
-		ExportAddPathToOffset2(export, Layer_position, Rectangle8_l, offsetof(ThemeLayer, position.l));
-		ExportAddPathToOffset2(export, Layer_position, Rectangle8_r, offsetof(ThemeLayer, position.r));
-		ExportAddPathToOffset2(export, Layer_position, Rectangle8_t, offsetof(ThemeLayer, position.t));
-		ExportAddPathToOffset2(export, Layer_position, Rectangle8_b, offsetof(ThemeLayer, position.b));
-		themeLayer.position = layer->position;
-		themeLayer.mode = layer->mode;
-		if (layer->base.tag == LayerBase_box + 1) themeLayer.type = THEME_LAYER_BOX;
-		if (layer->base.tag == LayerBase_metrics + 1) themeLayer.type = THEME_LAYER_METRICS;
-		if (layer->base.tag == LayerBase_text + 1) themeLayer.type = THEME_LAYER_TEXT;
-		if (layer->base.tag == LayerBase_path + 1) themeLayer.type = THEME_LAYER_PATH;
-		assert(themeLayer.type);
-		state->access(state, &themeLayer, sizeof(themeLayer));
-		uintptr_t stackPosition = arrlenu(export->pathStack);
-		RfField *baseField = LayerBase_Type.fields + layer->base.tag - 1;
-		arrput(export->pathStack, Layer_base);
-		arrput(export->pathStack, 0);
-		baseField->item.type->op(state, &baseField->item, (uint8_t *) pointer 
-			+ baseField->offset + Layer_Type.fields[Layer_base].offset);
-		arrsetlen(export->pathStack, stackPosition);
-	} else if (state->op == OP_GET_PALETTE || state->op == OP_REPLACE_COLOR || state->op == OP_FIND_COLOR_USERS) {
-		currentPaletteOpLayer = pointer;
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void StyleSetOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_EXPORT) {
-		ExportState *export = (ExportState *) state;
-		StyleSet *styleSet = (StyleSet *) pointer;
-
-		// Write the header.
-
-		ThemeHeader header = { 0 };
-		header.signature = THEME_HEADER_SIGNATURE;
-		header.styleCount = arrlenu(styleSet->styles);
-		header.constantCount = arrlenu(styleSet->constants);
-		state->access(state, &header, sizeof(header));
-		assert((export->buffer.data.byteCount & 3) == 0);
-
-		// Write the list of styles.
-
-		uint32_t styleListOffset = export->buffer.data.byteCount;
-
-		FILE *f = stylesPath ? fopen(stylesPath, "wb") : NULL;
-
-		for (uintptr_t i = 0; i < header.styleCount; i++) {
-			Style *style = styleSet->styles[i];
-			ThemeStyle entry = { 0 };
-			entry.id = (style->id << 1) | 1;
-			entry.layerCount = arrlenu(style->layers);
-			entry.paintOutsets = StyleCalculatePaintOutsets(style->layers);
-			entry.opaqueInsets = StyleCalculateOpaqueInsets(style->layers);
-			entry.approximateBorders = StyleCalculateApproximateBorders(style->layers);
-			state->access(state, &entry, sizeof(entry));
-			assert((export->buffer.data.byteCount & 3) == 0);
-
-			printf("exporting '%.*s' (id: %ld)\n", (int) style->name.byteCount, (char *) style->name.buffer, (style->id << 1) | 1);
-
-			for (uintptr_t i = 0; i < arrlenu(style->layers); i++) {
-				printf("\thas layer %ld\n", style->layers[i]);
-			}
-
-			if (style->id && stylesPath) {
-				fprintf(f, "%s ES_STYLE_", style->publicStyle ? "define" : "private define");
-
-				bool dot = false;
-
-				for (uintptr_t j = 0; j < style->name.byteCount; j++) {
-					char c = ((const char *) style->name.buffer)[j];
-
-					if (c == '.') {
-						fprintf(f, "_");
-						dot = true;
-					} else if (c >= 'A' && c <= 'Z' && j && !dot) {
-						fprintf(f, "_%c", c);
-					} else {
-						fprintf(f, "%c", toupper(c));
-						dot = false;
-					}
-				}
-
-				fprintf(f, " (ES_STYLE_CAST(%ld))\n", (style->id << 1) | 1);
-			}
-		}
-
-		if (stylesPath) fclose(f);
-
-		// Write the list of constants.
-
-		for (uintptr_t i = 0; i < header.constantCount; i++) {
-			Constant *constant = styleSet->constants[i];
-			ThemeConstant entry = { 0 };
-			assert(constant->value.byteCount + 1 < sizeof(entry.cValue));
-			entry.hash = CalculateCRC64(constant->key.buffer, constant->key.byteCount, 0);
-			entry.scale = constant->scale;
-			memcpy(entry.cValue, constant->value.buffer, constant->value.byteCount);
-			entry.cValue[constant->value.byteCount] = 0;
-			state->access(state, &entry, sizeof(entry));
-			assert((export->buffer.data.byteCount & 3) == 0);
-		}
-
-		// Write out all layers.
-
-		for (uintptr_t i = 0; i < arrlenu(styleSet->layers); i++) {
-			Layer *layer = styleSet->layers[i];
-			layer->exportOffset = export->buffer.data.byteCount;
-			assert((layer->exportOffset & 3) == 0);
-			RfItem item = { 0 };
-			item.type = &Layer_Type;
-			item.byteCount = sizeof(Layer);
-			LayerOp(state, &item, layer);
-			ThemeLayer *entry = (ThemeLayer *) ((uint8_t *) export->buffer.data.buffer + layer->exportOffset);
-			entry->dataByteCount = export->buffer.data.byteCount - layer->exportOffset;
-#if 0
-			entry->sequenceDataOffset = arrlenu(layer->sequences) ? export->buffer.data.byteCount : 0;
-#endif
-
-			Layer *previousLayer = selected.layer;
-			selected.layer = layer; // HACK!
-
-#if 0
-			// Write out the sequences.
-
-			for (uintptr_t j = 0; j < arrlenu(layer->sequences); j++) {
-				Sequence *sequence = layer->sequences[j];
-				uint32_t headerOffset = export->buffer.data.byteCount;
-
-				{
-					ThemeSequenceHeader header = { 0 };
-					header.state = sequence->primaryState
-						| (sequence->flagFocused ? THEME_STATE_FOCUSED : 0)
-						| (sequence->flagChecked ? THEME_STATE_CHECKED : 0)
-						| (sequence->flagIndeterminate ? THEME_STATE_INDETERMINATE : 0)
-						| (sequence->flagDefault ? THEME_STATE_DEFAULT_BUTTON : 0)
-						| (sequence->flagItemFocus ? THEME_STATE_FOCUSED_ITEM : 0)
-						| (sequence->flagListFocus ? THEME_STATE_LIST_FOCUSED : 0)
-						| (sequence->flagBeforeEnter ? THEME_STATE_BEFORE_ENTER : 0)
-						| (sequence->flagAfterExit ? THEME_STATE_AFTER_EXIT : 0)
-						| (sequence->flagSelected ? THEME_STATE_SELECTED : 0);
-					header.duration = sequence->duration;
-					header.isLastSequence = j == arrlenu(layer->sequences) - 1;
-					state->access(state, &header, sizeof(header));
-
-					if (sequence->flagBeforeEnter) printf("before enter %ld\n", layer->id);
-					if (sequence->flagAfterExit) printf("after exit %ld\n", layer->id);
-				}
-
-				uint32_t overrideCount = 0;
-
-				for (uintptr_t k = 0; k < arrlenu(sequence->keyframes); k++) {
-					for (uintptr_t l = 0; l < arrlenu(sequence->keyframes[k]->properties); l++) {
-						Property *property = sequence->keyframes[k]->properties + l;
-
-						for (uintptr_t m = 0; m < arrlenu(export->pathToOffsetList); m++) {
-							const PathToOffset *pathToOffset = export->pathToOffsetList + m;
-
-							if (!ArePathsEqual(pathToOffset->path, property->path + 1)) {
-								continue;
-							}
-
-							RfItem item;
-							Keyframe *previousKeyframe = selected.keyframe;
-							selected.keyframe = (Keyframe *) sequence->keyframes[k]; // HACK!
-							void *source = ResolveDataObject((RfPath *) property->path, &item);
-							selected.keyframe = previousKeyframe;
-
-							ThemeOverride override = { 0 };
-							override.offset = pathToOffset->offset - layer->exportOffset;
-
-							if (item.type == &StyleI8_Type) {
-								override.type = THEME_OVERRIDE_I8;
-								override.data.i8 = *(int8_t *) source;
-							} else if (item.type == &StyleI16_Type) {
-								override.type = THEME_OVERRIDE_I16;
-								override.data.i16 = *(int16_t *) source;
-							} else if (item.type == &StyleFloat_Type) {
-								override.type = THEME_OVERRIDE_F32;
-								override.data.f32 = *(float *) source;
-							} else if (item.type == &StyleColor_Type) {
-								override.type = THEME_OVERRIDE_COLOR;
-								override.data.u32 = ColorLookup(*(uint32_t *) source);
-							} else {
-								assert(false);
-							}
-
-							overrideCount++;
-							state->access(state, &override, sizeof(override));
-
-							break;
-						}
-					}
-				}
-
-				ThemeSequenceHeader *header = (ThemeSequenceHeader *) ((uint8_t *) export->buffer.data.buffer + headerOffset);
-				header->overrideCount = overrideCount;
-			}
-#endif
-
-			selected.layer = previousLayer;
-
-			ExportFreePathToOffsetList(export->pathToOffsetList);
-			export->pathToOffsetList = NULL;
-		}
-
-		// Write out layer lists for styles, and update the style list to point to them.
-
-		for (uintptr_t i = 0; i < header.styleCount; i++) {
-			Style *style = styleSet->styles[i];
-			uint32_t layerListOffset = export->buffer.data.byteCount;
-			assert((export->buffer.data.byteCount & 3) == 0);
-
-			for (uintptr_t j = 0; j < arrlenu(style->layers); j++) {
-				uint32_t layerOffset = LayerLookup(style->layers[j])->exportOffset;
-				state->access(state, &layerOffset, sizeof(layerOffset));
-			}
-
-			ThemeStyle *entry = (ThemeStyle *) ((uint8_t *) export->buffer.data.buffer + styleListOffset) + i;
-			entry->layerListOffset = layerListOffset;
-		}
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void ActionExport(void *_unused) {
-	PathToOffset *pathToOffsetList;
-	RfData data = ExportToGrowableBuffer(&StyleSet_Type, sizeof(StyleSet), NULL, &styleSet, &pathToOffsetList);
-	ExportFreePathToOffsetList(pathToOffsetList);
-	FILE *f = fopen(exportPath, "wb");
-	fwrite(data.buffer, 1, data.byteCount, f);
-	fclose(f);
-	free(data.buffer);
-
-	{
-		RfState state = { 0 };
-		state.op = OP_FIND_COLOR_USERS;
-		RfItem item = { 0 };
-		item.type = &StyleSet_Type;
-		item.byteCount = sizeof(StyleSet);
-		item.options = NULL;
-		RfBroadcast(&state, &item, &styleSet, true);
-
-		for (uintptr_t i = 0; i < hmlenu(colorUsers); i++) {
-			Color *color = ColorLookupPointer(colorUsers[i].key);
-
-			printf("%.*s - ", (int) color->key.byteCount, (char *) color->key.buffer);
-
-			for (uintptr_t j = 0; j < arrlenu(colorUsers[i].value); j++) {
-				Style *style = colorUsers[i].value[j];
-				printf("%.*s, ", (int) style->name.byteCount, (char *) style->name.buffer);
-			}
-
-			printf("\n");
-			arrfree(colorUsers[i].value);
-		}
-
-		for (uintptr_t i = 0; i < arrlenu(styleSet.colors); i++) {
-			printf("%.*s\t%.8X\n", (int) styleSet.colors[i]->key.byteCount, (char *) styleSet.colors[i]->key.buffer, styleSet.colors[i]->value);
-		}
-
-		for (uintptr_t i = 0; i < arrlenu(styleSet.colors); i++) {
-			for (uintptr_t j = 0; j < hmlenu(colorUsers); j++) {
-				if (styleSet.colors[i]->id == colorUsers[j].key) {
-					goto used;
-				}
-			}
-
-			printf("Color '%.*s' is unused.\n", (int) styleSet.colors[i]->key.byteCount, (char *) styleSet.colors[i]->key.buffer);
-			used:;
-		}
-
-		hmfree(colorUsers);
-	}
-}
-
-// ------------------- Exporting to Designer2 -------------------
-
-enum PropertyType {
-	PROP_NONE,
-	PROP_COLOR,
-	PROP_INT,
-	PROP_OBJECT,
-	PROP_FLOAT,
-};
-
-typedef struct Property2 {
-	uint8_t type;
-#define PROPERTY_NAME_SIZE (31)
-	char cName[PROPERTY_NAME_SIZE];
-
-	union {
-		int32_t integer;
-		uint64_t object;
-		float floating;
-	};
-} Property2;
-
-enum ObjectType {
-	OBJ_NONE,
-
-	OBJ_STYLE,
-	OBJ_COMMENT,
-	OBJ_INSTANCE,
-
-	OBJ_VAR_COLOR = 0x40,
-	OBJ_VAR_INT,
-	OBJ_VAR_TEXT_STYLE,
-	OBJ_VAR_CONTOUR_STYLE,
-	
-	OBJ_PAINT_OVERWRITE = 0x60,
-	OBJ_PAINT_LINEAR_GRADIENT,
-	OBJ_PAINT_RADIAL_GRADIENT,
-
-	OBJ_LAYER_BOX = 0x80,
-	OBJ_LAYER_METRICS,
-	OBJ_LAYER_TEXT,
-	OBJ_LAYER_GROUP,
-	OBJ_LAYER_PATH,
-
-	OBJ_MOD_COLOR = 0xC0,
-	OBJ_MOD_MULTIPLY,
-};
-
-typedef struct Object2 {
-	uint8_t type;
-#define OBJECT_NAME_SIZE (46)
-	char cName[OBJECT_NAME_SIZE];
-#define OBJECT_IS_SELECTED (1 << 0)
-#define OBJECT_IN_PROTOTYPE (1 << 1)
-	uint8_t flags;
-	uint64_t id;
-	Property2 *properties;
-} Object2;
-
-void ObjectAddIntegerProperty(Object2 *object, const char *cName, int32_t value) {
-	Property2 property = { 0 };
-	property.type = PROP_INT;
-	strcpy(property.cName, cName);
-	property.integer = value;
-	arrput(object->properties, property);
-}
-
-void ObjectAddColorProperty(Object2 *object, const char *cName, uint32_t value) {
-	Property2 property = { 0 };
-	property.type = PROP_COLOR;
-	strcpy(property.cName, cName);
-	property.integer = value;
-	arrput(object->properties, property);
-}
-
-void ObjectAddFloatProperty(Object2 *object, const char *cName, float value) {
-	Property2 property = { 0 };
-	property.type = PROP_FLOAT;
-	strcpy(property.cName, cName);
-	property.floating = value;
-	arrput(object->properties, property);
-}
-
-void ObjectAddObjectProperty(Object2 *object, const char *cName, uint64_t value) {
-	Property2 property = { 0 };
-	property.type = PROP_OBJECT;
-	strcpy(property.cName, cName);
-	property.object = value;
-	arrput(object->properties, property);
-}
-
-void AutoNameOverrideObject(Object2 *override, uint32_t primaryState, uint32_t stateBits) {
-	const char *cPrimaryStateStrings[] = {
-		"Any", "Idle", "Hovered", "Pressed", "Disabled", "Inactive",
-	};
-
-	const char *cStateBitStrings[] = {
-		"Focus", "Check", "Indtm", "DefBtn", "Sel", "FcItem", "ListFc", "BfEnt", "AfExt",
-	};
-
-	snprintf(override->cName, sizeof(override->cName), "?%s", primaryState ? cPrimaryStateStrings[primaryState] : "");
-
-	for (uintptr_t i = 0; i < 16; i++) {
-		if (stateBits & (1 << (15 - i))) {
-			snprintf(override->cName + strlen(override->cName), sizeof(override->cName) - strlen(override->cName), 
-					"%s%s", i || primaryState ? "&" : "", cStateBitStrings[i]);
-		}
-	}
-}
-
-uint64_t ExportPaint2(Paint *paint, int *x, int y, Object2 **objects, uint64_t *idAllocator, Layer *layer) {
-	char cPropertyName[PROPERTY_NAME_SIZE];
-
-	if (!paint->tag) {
-		return 0;
-	} else if (paint->tag == Paint_solid + 1) {
-		return ColorLookupPointer(paint->solid.color)->object2ID;
-	} else if (paint->tag == Paint_linearGradient + 1) {
-		Object2 object = { .type = OBJ_PAINT_LINEAR_GRADIENT, .id = ++(*idAllocator) };
-
-		ObjectAddIntegerProperty(&object, "_graphX", *x);
-		ObjectAddIntegerProperty(&object, "_graphY", y);
-		ObjectAddIntegerProperty(&object, "_graphW", 80);
-		ObjectAddIntegerProperty(&object, "_graphH", 60);
-		ObjectAddFloatProperty(&object, "transformX", paint->linearGradient.transformX);
-		ObjectAddFloatProperty(&object, "transformY", paint->linearGradient.transformY);
-		ObjectAddFloatProperty(&object, "transformStart", paint->linearGradient.transformStart);
-		ObjectAddIntegerProperty(&object, "repeatMode", paint->linearGradient.repeat);
-		ObjectAddIntegerProperty(&object, "useGammaInterpolation", paint->linearGradient.useGammaInterpolation);
-		ObjectAddIntegerProperty(&object, "useSystemColor", paint->linearGradient.useSystemHue);
-		ObjectAddIntegerProperty(&object, "stops_count", arrlenu(paint->linearGradient.stops));
-
-		for (uintptr_t i = 0; i < arrlenu(paint->linearGradient.stops); i++) {
-			sprintf(cPropertyName, "stops_%d_position", (int) i);
-			ObjectAddIntegerProperty(&object, cPropertyName, paint->linearGradient.stops[i].position);
-			sprintf(cPropertyName, "stops_%d_color", (int) i);
-			ObjectAddObjectProperty(&object, cPropertyName, ColorLookupPointer(paint->linearGradient.stops[i].color)->object2ID);
-		}
-
-		*x += 100;
-		arrput(*objects, object);
-
-		if (layer) {
-			for (uintptr_t i = 0; i < arrlenu(layer->sequences); i++) {
-				Sequence *s = layer->sequences[i];
-				assert(arrlenu(s->keyframes) == 1);
-				Keyframe *keyframe = s->keyframes[0];
-
-				uint32_t stateBits = 0;
-				if (s->flagFocused) stateBits |= THEME_STATE_FOCUSED;
-				if (s->flagChecked) stateBits |= THEME_STATE_CHECKED;
-				if (s->flagIndeterminate) stateBits |= THEME_STATE_INDETERMINATE;
-				if (s->flagDefault) stateBits |= THEME_STATE_DEFAULT_BUTTON;
-				if (s->flagItemFocus) stateBits |= THEME_STATE_FOCUSED_ITEM;
-				if (s->flagListFocus) stateBits |= THEME_STATE_LIST_FOCUSED;
-				if (s->flagBeforeEnter) stateBits |= THEME_STATE_BEFORE_ENTER;
-				if (s->flagAfterExit) stateBits |= THEME_STATE_AFTER_EXIT;
-				if (s->flagSelected) stateBits |= THEME_STATE_SELECTED;
-
-				Object2 override = { .type = object.type, .id = ++(*idAllocator) };
-				ObjectAddIntegerProperty(&override, "_graphX", *x);
-				ObjectAddIntegerProperty(&override, "_graphY", y);
-				ObjectAddIntegerProperty(&override, "_graphW", 80);
-				ObjectAddIntegerProperty(&override, "_graphH", 60);
-				ObjectAddObjectProperty(&override, "_parent", object.id);
-				ObjectAddIntegerProperty(&override, "_primaryState", s->primaryState);
-				ObjectAddIntegerProperty(&override, "_stateBits", stateBits);
-				ObjectAddIntegerProperty(&override, "_duration", s->duration);
-				AutoNameOverrideObject(&override, s->primaryState, stateBits);
-
-				bool addObject = false;
-
-				for (uintptr_t i = 0; i < arrlenu(keyframe->properties); i++) {
-					Property *property = &keyframe->properties[i];
-
-					if (layer->base.tag == LayerBase_box + 1 && property->path[0] == (uint32_t) -2 && property->path[1] == 8 
-							&& property->path[2] == 0 && property->path[3] == 2 && property->path[4] == 2) {
-						assert(property->path[5] == 2 && property->path[7] == 0);
-						RfGrowableBuffer state = { 0 };
-						state.s.op = RF_OP_LOAD;
-						state.s.access = RfReadGrowableBuffer;
-						state.data = property->data;
-						RfItem item = GradientStop_Type.fields[GradientStop_color].item;
-						uint32_t value; item.type->op(&state.s, &item, &value);
-						char cPropertyName[PROPERTY_NAME_SIZE];
-						snprintf(cPropertyName, sizeof(cPropertyName), "stops_%d_color", property->path[6]);
-						ObjectAddObjectProperty(&override, cPropertyName, ColorLookupPointer(value)->object2ID);
-						addObject = true;
-					} else {
-						continue;
-					}
-				}
-
-				if (addObject) {
-					arrput(*objects, override);
-					object.id = override.id;
-					*x += 100;
-				} else {
-					arrfree(override.properties);
-				}
-			}	
-		}
-
-		return object.id;
-	} else if (paint->tag == Paint_radialGradient + 1) {
-		Object2 object = { .type = OBJ_PAINT_RADIAL_GRADIENT, .id = ++(*idAllocator) };
-
-		ObjectAddIntegerProperty(&object, "_graphX", *x);
-		ObjectAddIntegerProperty(&object, "_graphY", y);
-		ObjectAddIntegerProperty(&object, "_graphW", 80);
-		ObjectAddIntegerProperty(&object, "_graphH", 60);
-		ObjectAddFloatProperty(&object, "transform0", paint->radialGradient.transform0);
-		ObjectAddFloatProperty(&object, "transform1", paint->radialGradient.transform1);
-		ObjectAddFloatProperty(&object, "transform2", paint->radialGradient.transform2);
-		ObjectAddFloatProperty(&object, "transform3", paint->radialGradient.transform3);
-		ObjectAddFloatProperty(&object, "transform4", paint->radialGradient.transform4);
-		ObjectAddFloatProperty(&object, "transform5", paint->radialGradient.transform5);
-		ObjectAddIntegerProperty(&object, "repeatMode", paint->radialGradient.repeat);
-		ObjectAddIntegerProperty(&object, "useGammaInterpolation", paint->radialGradient.useGammaInterpolation);
-		ObjectAddIntegerProperty(&object, "stops_count", arrlenu(paint->radialGradient.stops));
-
-		for (uintptr_t i = 0; i < arrlenu(paint->radialGradient.stops); i++) {
-			sprintf(cPropertyName, "stops_%d_position", (int) i);
-			ObjectAddIntegerProperty(&object, cPropertyName, paint->radialGradient.stops[i].position);
-			sprintf(cPropertyName, "stops_%d_color", (int) i);
-			ObjectAddObjectProperty(&object, cPropertyName, ColorLookupPointer(paint->radialGradient.stops[i].color)->object2ID);
-		}
-
-		*x += 100;
-		arrput(*objects, object);
-		return object.id;
-	} else if (paint->tag == Paint_overwrite + 1) {
-		Object2 object = { .type = OBJ_PAINT_OVERWRITE, .id = ++(*idAllocator) };
-		ObjectAddIntegerProperty(&object, "_graphX", *x);
-		ObjectAddIntegerProperty(&object, "_graphY", y);
-		ObjectAddIntegerProperty(&object, "_graphW", 80);
-		ObjectAddIntegerProperty(&object, "_graphH", 60);
-		ObjectAddObjectProperty(&object, "color", ColorLookupPointer(paint->overwrite.color)->object2ID);
-		*x += 100;
-		arrput(*objects, object);
-
-		if (layer) {
-			for (uintptr_t i = 0; i < arrlenu(layer->sequences); i++) {
-				Sequence *s = layer->sequences[i];
-				assert(arrlenu(s->keyframes) == 1);
-				Keyframe *keyframe = s->keyframes[0];
-
-				uint32_t stateBits = 0;
-				if (s->flagFocused) stateBits |= THEME_STATE_FOCUSED;
-				if (s->flagChecked) stateBits |= THEME_STATE_CHECKED;
-				if (s->flagIndeterminate) stateBits |= THEME_STATE_INDETERMINATE;
-				if (s->flagDefault) stateBits |= THEME_STATE_DEFAULT_BUTTON;
-				if (s->flagItemFocus) stateBits |= THEME_STATE_FOCUSED_ITEM;
-				if (s->flagListFocus) stateBits |= THEME_STATE_LIST_FOCUSED;
-				if (s->flagBeforeEnter) stateBits |= THEME_STATE_BEFORE_ENTER;
-				if (s->flagAfterExit) stateBits |= THEME_STATE_AFTER_EXIT;
-				if (s->flagSelected) stateBits |= THEME_STATE_SELECTED;
-
-				Object2 override = { .type = object.type, .id = ++(*idAllocator) };
-				ObjectAddIntegerProperty(&override, "_graphX", *x);
-				ObjectAddIntegerProperty(&override, "_graphY", y);
-				ObjectAddIntegerProperty(&override, "_graphW", 80);
-				ObjectAddIntegerProperty(&override, "_graphH", 60);
-				ObjectAddObjectProperty(&override, "_parent", object.id);
-				ObjectAddIntegerProperty(&override, "_primaryState", s->primaryState);
-				ObjectAddIntegerProperty(&override, "_stateBits", stateBits);
-				ObjectAddIntegerProperty(&override, "_duration", s->duration);
-				AutoNameOverrideObject(&override, s->primaryState, stateBits);
-
-				bool addObject = false;
-
-				for (uintptr_t i = 0; i < arrlenu(keyframe->properties); i++) {
-					Property *property = &keyframe->properties[i];
-
-					if (layer->base.tag == LayerBase_box + 1 && property->path[0] == (uint32_t) -2 && property->path[1] == 8 
-							&& property->path[2] == 0 && property->path[3] == 2 && property->path[4] == 3) {
-						assert(property->path[5] == 0);
-						RfGrowableBuffer state = { 0 };
-						state.s.op = RF_OP_LOAD;
-						state.s.access = RfReadGrowableBuffer;
-						state.data = property->data;
-						RfItem item = PaintOverwrite_Type.fields[PaintOverwrite_color].item;
-						uint32_t value; item.type->op(&state.s, &item, &value);
-						ObjectAddObjectProperty(&override, "color", ColorLookupPointer(value)->object2ID);
-						addObject = true;
-					} else {
-						continue;
-					}
-				}
-
-				if (addObject) {
-					arrput(*objects, override);
-					object.id = override.id;
-					*x += 100;
-				}
-			}	
-		}
-
-		return object.id;
-	} else {
-		assert(false);
-		return 0;
-	}
-}
-
-uint64_t ExportFillMode2(PathFillMode *fill, int *x, int y, Object2 **objects, uint64_t *idAllocator) {
-	if (fill->tag == PathFillMode_solid + 1) {
-		return 0;
-	} else if (fill->tag == PathFillMode_contour + 1) {
-		Object2 object = { .type = OBJ_VAR_CONTOUR_STYLE, .id = ++(*idAllocator) };
-		ObjectAddIntegerProperty(&object, "_graphX", *x);
-		ObjectAddIntegerProperty(&object, "_graphY", y);
-		ObjectAddIntegerProperty(&object, "_graphW", 80);
-		ObjectAddIntegerProperty(&object, "_graphH", 60);
-		ObjectAddIntegerProperty(&object, "internalWidth", fill->contour.internalWidth);
-		ObjectAddIntegerProperty(&object, "externalWidth", fill->contour.externalWidth);
-		ObjectAddIntegerProperty(&object, "integerWidthsOnly", fill->contour.integerWidthsOnly);
-		ObjectAddIntegerProperty(&object, "joinMode", fill->contour.joinMode == JOIN_MODE_ROUND ? RAST_LINE_JOIN_ROUND : RAST_LINE_JOIN_MITER);
-		ObjectAddIntegerProperty(&object, "capMode", fill->contour.capMode == CAP_MODE_FLAT ? RAST_LINE_CAP_FLAT 
-				: fill->contour.capMode == CAP_MODE_ROUND ? RAST_LINE_CAP_ROUND : RAST_LINE_CAP_SQUARE);
-		ObjectAddFloatProperty(&object, "miterLimit", fill->contour.joinMode == JOIN_MODE_BEVEL ? 0.0f : fill->contour.miterLimit);
-		*x += 100;
-		arrput(*objects, object);
-		return object.id;
-	} else {
-		assert(false);
-		return 0;
-	}
-}
-
-void ExportProperty2(Layer *layer, Property *property, Object2 *override) {
-	bool unhandled = false;
-
-	RfGrowableBuffer state = { 0 };
-	state.s.op = RF_OP_LOAD;
-	state.s.access = RfReadGrowableBuffer;
-	state.data = property->data;
-
-	if (property->path[0] == (uint32_t) -2 && property->path[1] == 8 && property->path[2] == 0) {
-		if (layer->base.tag == LayerBase_box + 1) {
-			if (property->path[3] == LayerBox_borders && property->path[4] == Rectangle8_l) {
-				RfItem item = Rectangle8_Type.fields[Rectangle8_l].item;
-				int8_t value; item.type->op(&state.s, &item, &value);
-				ObjectAddIntegerProperty(override, "borders0", value);
-			} else if (property->path[3] == LayerBox_borders && property->path[4] == Rectangle8_r) {
-				RfItem item = Rectangle8_Type.fields[Rectangle8_r].item;
-				int8_t value; item.type->op(&state.s, &item, &value);
-				ObjectAddIntegerProperty(override, "borders1", value);
-			} else if (property->path[3] == LayerBox_borders && property->path[4] == Rectangle8_t) {
-				RfItem item = Rectangle8_Type.fields[Rectangle8_t].item;
-				int8_t value; item.type->op(&state.s, &item, &value);
-				ObjectAddIntegerProperty(override, "borders2", value);
-			} else if (property->path[3] == LayerBox_borders && property->path[4] == Rectangle8_b) {
-				RfItem item = Rectangle8_Type.fields[Rectangle8_b].item;
-				int8_t value; item.type->op(&state.s, &item, &value);
-				ObjectAddIntegerProperty(override, "borders3", value);
-			} else if (property->path[3] == LayerBox_corners && property->path[4] == Corners8_tl) {
-				RfItem item = Corners8_Type.fields[Corners8_tl].item;
-				int8_t value; item.type->op(&state.s, &item, &value);
-				ObjectAddIntegerProperty(override, "corners0", value);
-			} else if (property->path[3] == LayerBox_corners && property->path[4] == Corners8_tr) {
-				RfItem item = Corners8_Type.fields[Corners8_tr].item;
-				int8_t value; item.type->op(&state.s, &item, &value);
-				ObjectAddIntegerProperty(override, "corners1", value);
-			} else if (property->path[3] == LayerBox_corners && property->path[4] == Corners8_bl) {
-				RfItem item = Corners8_Type.fields[Corners8_bl].item;
-				int8_t value; item.type->op(&state.s, &item, &value);
-				ObjectAddIntegerProperty(override, "corners2", value);
-			} else if (property->path[3] == LayerBox_corners && property->path[4] == Corners8_br) {
-				RfItem item = Corners8_Type.fields[Corners8_br].item;
-				int8_t value; item.type->op(&state.s, &item, &value);
-				ObjectAddIntegerProperty(override, "corners3", value);
-			} else if (property->path[3] == LayerBox_mainPaint && property->path[4] == Paint_solid && property->path[5] == PaintSolid_color) {
-				RfItem item = PaintSolid_Type.fields[PaintSolid_color].item;
-				uint32_t value; item.type->op(&state.s, &item, &value);
-				ObjectAddObjectProperty(override, "mainPaint", ColorLookupPointer(value)->object2ID);
-			} else if (property->path[3] == LayerBox_borderPaint && property->path[4] == Paint_solid && property->path[5] == PaintSolid_color) {
-				RfItem item = PaintSolid_Type.fields[PaintSolid_color].item;
-				uint32_t value; item.type->op(&state.s, &item, &value);
-				ObjectAddObjectProperty(override, "borderPaint", ColorLookupPointer(value)->object2ID);
-			} else {
-				unhandled = true;
-			}
-		} else if (layer->base.tag == LayerBase_metrics + 1) {
-			if (property->path[3] == LayerMetrics_textSize || property->path[3] == LayerMetrics_selectedText) {
-				// Ignore.
-			} else if (property->path[3] == LayerMetrics_textColor) {
-				RfItem item = LayerMetrics_Type.fields[LayerMetrics_textColor].item;
-				uint32_t value; item.type->op(&state.s, &item, &value);
-				ObjectAddObjectProperty(override, "textColor", ColorLookupPointer(value)->object2ID);
-			} else if (property->path[3] == LayerMetrics_iconColor) {
-				RfItem item = LayerMetrics_Type.fields[LayerMetrics_iconColor].item;
-				uint32_t value; item.type->op(&state.s, &item, &value);
-				ObjectAddObjectProperty(override, "iconColor", ColorLookupPointer(value)->object2ID);
-			} else {
-				unhandled = true;
-			}
-		} else if (layer->base.tag == LayerBase_path + 1) {
-			if (property->path[3] == LayerPath_alpha) {
-				RfItem item = LayerPath_Type.fields[LayerPath_alpha].item;
-				int16_t value; item.type->op(&state.s, &item, &value);
-				ObjectAddIntegerProperty(override, "alpha", value);
-			} else if (property->path[3] == LayerPath_fills && property->path[4] == 0 && property->path[5] == PathFill_paint
-					&& property->path[6] == Paint_solid && property->path[7] == PaintSolid_color) {
-				RfItem item = PaintSolid_Type.fields[PaintSolid_color].item;
-				uint32_t value; item.type->op(&state.s, &item, &value);
-				ObjectAddObjectProperty(override, "fills_0_paint", ColorLookupPointer(value)->object2ID);
-				ObjectAddIntegerProperty(override, "fills_count", 1);
-			} else {
-				unhandled = true;
-			}
-		} else {
-			unhandled = true;
-		}
-	} else if (property->path[0] == (uint32_t) -2 && property->path[1] == 5) {
-		if (layer->base.tag == LayerBase_box + 1) {
-			if (property->path[2] == Rectangle8_l) {
-				RfItem item = Rectangle8_Type.fields[Rectangle8_l].item;
-				int8_t value; item.type->op(&state.s, &item, &value);
-				ObjectAddIntegerProperty(override, "offset0", value - layer->offset.l);
-			} else if (property->path[2] == Rectangle8_r) {
-				RfItem item = Rectangle8_Type.fields[Rectangle8_r].item;
-				int8_t value; item.type->op(&state.s, &item, &value);
-				ObjectAddIntegerProperty(override, "offset1", value - layer->offset.r);
-			} else if (property->path[2] == Rectangle8_t) {
-				RfItem item = Rectangle8_Type.fields[Rectangle8_t].item;
-				int8_t value; item.type->op(&state.s, &item, &value);
-				ObjectAddIntegerProperty(override, "offset2", value - layer->offset.t);
-			} else if (property->path[2] == Rectangle8_b) {
-				RfItem item = Rectangle8_Type.fields[Rectangle8_b].item;
-				int8_t value; item.type->op(&state.s, &item, &value);
-				ObjectAddIntegerProperty(override, "offset3", value - layer->offset.b);
-			}
-		} else {
-			unhandled = true;
-		}
-	} else {
-		unhandled = true;
-	}
-
-	assert(!unhandled);
-
-#if 0
-	if (unhandled) {
-		fprintf(stderr, "\tunhandled on %s: ", LayerBase_Type.fields[layer->base.tag - 1].cName);
-
-		for (uintptr_t i = 0; property->path[i] != RF_PATH_TERMINATOR; i++) {
-			fprintf(stderr, "%d, ", property->path[i]);
-		}
-
-		fprintf(stderr, "\n");
-	}
-#endif
-}
-
-void ActionExportDesigner2(void *cp) {
-	// TODO Merging identical layers and styles.
-
-	Object2 *objects = NULL;
-	uint64_t objectIDAllocator = 0;
-	char cPropertyName[PROPERTY_NAME_SIZE];
-
-	int y = 0;
-
-	// Colors.
-
-	for (uintptr_t i = 0; i < arrlenu(styleSet.colors); i++) {
-		Object2 object = { .type = OBJ_VAR_COLOR, .id = ++objectIDAllocator };
-		snprintf(object.cName, sizeof(object.cName), "%.*s", (int) styleSet.colors[i]->key.byteCount, (const char *) styleSet.colors[i]->key.buffer);
-		ObjectAddIntegerProperty(&object, "_graphX", (i % 10) * 180);
-		ObjectAddIntegerProperty(&object, "_graphY", (i / 10) * 100 + y);
-		ObjectAddIntegerProperty(&object, "_graphW", 80);
-		ObjectAddIntegerProperty(&object, "_graphH", 60);
-		ObjectAddColorProperty(&object, "color", styleSet.colors[i]->value);
-		ObjectAddIntegerProperty(&object, "isExported", 0);
-		arrput(objects, object);
-		styleSet.colors[i]->object2ID = object.id;
-	}
-
-	y += (arrlenu(styleSet.colors) / 10) * 100 + 200;
-
-	// Constants.
-
-	for (uintptr_t i = 0; i < arrlenu(styleSet.constants); i++) {
-		char value[64];
-		snprintf(value, sizeof(value), "%.*s", (int) styleSet.constants[i]->value.byteCount, (const char *) styleSet.constants[i]->value.buffer);
-		bool isColor = value[0] == '0' && value[1] == 'x';
-		Object2 object = { .type = isColor ? OBJ_VAR_COLOR : OBJ_VAR_INT, .id = ++objectIDAllocator };
-		snprintf(object.cName, sizeof(object.cName), "%.*s", (int) styleSet.constants[i]->key.byteCount, (const char *) styleSet.constants[i]->key.buffer);
-		ObjectAddIntegerProperty(&object, "_graphX", (i % 5) * 360);
-		ObjectAddIntegerProperty(&object, "_graphY", (i / 5) * 100 + y);
-		ObjectAddIntegerProperty(&object, "_graphW", 80);
-		ObjectAddIntegerProperty(&object, "_graphH", 60);
-		if (isColor) ObjectAddColorProperty(&object, "color", strtol(value, NULL, 0));
-		else ObjectAddIntegerProperty(&object, "value", strtol(value, NULL, 0));
-		ObjectAddIntegerProperty(&object, "isScaled", styleSet.constants[i]->scale);
-		ObjectAddIntegerProperty(&object, "isExported", 1);
-		arrput(objects, object);
-	}
-
-	y += (arrlenu(styleSet.constants) / 5) * 100 + 200;
-
-	// Styles.
-
-	int x0 = 180 * 10 + 200;
-	y = 0;
-
-	for (uintptr_t i = 0; i < arrlenu(styleSet.styles); i++) {
-		int x = x0;
-		Style *style = styleSet.styles[i];
-
-		fprintf(stderr, "style: %.*s\n", (int) style->name.byteCount, (const char *) style->name.buffer);
-		
-		Object2 layerGroup = { .type = OBJ_LAYER_GROUP, .id = ++objectIDAllocator };
-		Object2 metrics = { 0 }, textStyle = { 0 };
-		int32_t layerCount = 0;
-
-		for (uintptr_t i = 0; i < arrlenu(style->layers); i++) {
-			Layer *layer = LayerLookup(style->layers[i]);
-			bool addToLayerGroup = false;
-			Object2 object = { 0 };
-
-			if (layer->base.tag == LayerBase_box + 1) {
-				object.type = OBJ_LAYER_BOX, object.id = ++objectIDAllocator;
-				LayerBox *box = &layer->base.box;
-				ObjectAddIntegerProperty(&object, "borders0", box->borders.l);
-				ObjectAddIntegerProperty(&object, "borders1", box->borders.r);
-				ObjectAddIntegerProperty(&object, "borders2", box->borders.t);
-				ObjectAddIntegerProperty(&object, "borders3", box->borders.b);
-				ObjectAddIntegerProperty(&object, "corners0", box->corners.tl);
-				ObjectAddIntegerProperty(&object, "corners1", box->corners.tr);
-				ObjectAddIntegerProperty(&object, "corners2", box->corners.bl);
-				ObjectAddIntegerProperty(&object, "corners3", box->corners.br);
-				ObjectAddIntegerProperty(&object, "offset0", 0);
-				ObjectAddIntegerProperty(&object, "offset1", 0);
-				ObjectAddIntegerProperty(&object, "offset2", 0);
-				ObjectAddIntegerProperty(&object, "offset3", 0);
-				ObjectAddIntegerProperty(&object, "isBlurred", box->blurred);
-				ObjectAddIntegerProperty(&object, "autoCorners", box->autoCorners);
-				ObjectAddIntegerProperty(&object, "autoBorders", box->autoBorders);
-				ObjectAddIntegerProperty(&object, "shadowHiding", box->shadowHiding);
-				ObjectAddObjectProperty(&object, "mainPaint", ExportPaint2(&box->mainPaint, &x, y, &objects, &objectIDAllocator, layer));
-				ObjectAddObjectProperty(&object, "borderPaint", ExportPaint2(&box->borderPaint, &x, y, &objects, &objectIDAllocator, NULL));
-				addToLayerGroup = true;
-			} else if (layer->base.tag == LayerBase_text + 1) {
-				object.type = OBJ_LAYER_TEXT, object.id = ++objectIDAllocator;
-				ObjectAddObjectProperty(&object, "color", ColorLookupPointer(layer->base.text.color)->object2ID);
-				ObjectAddIntegerProperty(&object, "blur", layer->base.text.blur);
-				addToLayerGroup = true;
-			} else if (layer->base.tag == LayerBase_path + 1) {
-				object.type = OBJ_LAYER_PATH, object.id = ++objectIDAllocator;
-				LayerPath *path = &layer->base.path;
-				ObjectAddIntegerProperty(&object, "pathFillEvenOdd", path->evenOdd);
-				ObjectAddIntegerProperty(&object, "pathClosed", path->closed);
-				ObjectAddIntegerProperty(&object, "alpha", path->alpha);
-				ObjectAddIntegerProperty(&object, "points_count", arrlenu(path->points));
-				ObjectAddIntegerProperty(&object, "fills_count", arrlenu(path->fills));
-
-				for (uintptr_t i = 0; i < arrlenu(path->points); i++) {
-					sprintf(cPropertyName, "points_%d_x0", (int) i);
-					ObjectAddFloatProperty(&object, cPropertyName, path->points[i].x0);
-					sprintf(cPropertyName, "points_%d_y0", (int) i);
-					ObjectAddFloatProperty(&object, cPropertyName, path->points[i].y0);
-					sprintf(cPropertyName, "points_%d_x1", (int) i);
-					ObjectAddFloatProperty(&object, cPropertyName, path->points[i].x1);
-					sprintf(cPropertyName, "points_%d_y1", (int) i);
-					ObjectAddFloatProperty(&object, cPropertyName, path->points[i].y1);
-					sprintf(cPropertyName, "points_%d_x2", (int) i);
-					ObjectAddFloatProperty(&object, cPropertyName, path->points[i].x2);
-					sprintf(cPropertyName, "points_%d_y2", (int) i);
-					ObjectAddFloatProperty(&object, cPropertyName, path->points[i].y2);
-				}
-
-				for (uintptr_t i = 0; i < arrlenu(path->fills); i++) {
-					sprintf(cPropertyName, "fills_%d_paint", (int) i);
-					ObjectAddObjectProperty(&object, cPropertyName, ExportPaint2(&path->fills[i].paint, &x, y, &objects, &objectIDAllocator, NULL));
-					sprintf(cPropertyName, "fills_%d_mode", (int) i);
-					ObjectAddObjectProperty(&object, cPropertyName, ExportFillMode2(&path->fills[i].mode, &x, y, &objects, &objectIDAllocator));
-				}
-
-				addToLayerGroup = true;
-			} else if (layer->base.tag == LayerBase_metrics + 1) {
-				LayerMetrics *m = &layer->base.metrics;
-				assert(!m->globalOffset.l && !m->globalOffset.r && !m->globalOffset.t && !m->globalOffset.b);
-				LayerMetrics *inherit = NULL;
-				object.type = OBJ_VAR_TEXT_STYLE, object.id = ++objectIDAllocator;
-
-				if (m->inheritText.byteCount) {
-					for (uintptr_t i = 0; i < arrlenu(styleSet.styles); i++) {
-						Style *style = styleSet.styles[i];
-
-						if (m->inheritText.byteCount == style->name.byteCount 
-								&& 0 == memcmp(m->inheritText.buffer, style->name.buffer, style->name.byteCount)) {
-							inherit = &LayerLookup(style->layers[0])->base.metrics;
-							break;
-						}
-					}
-				}
-
-				if (inherit) {
-					ObjectAddObjectProperty(&object, "textColor", ColorLookupPointer(inherit->textColor)->object2ID);
-					ObjectAddObjectProperty(&object, "selectedBackground", ColorLookupPointer(inherit->selectedBackground)->object2ID);
-					ObjectAddObjectProperty(&object, "selectedText", ColorLookupPointer(inherit->selectedText)->object2ID);
-					ObjectAddIntegerProperty(&object, "textSize", inherit->textSize);
-					ObjectAddIntegerProperty(&object, "fontWeight", inherit->fontWeight);
-					ObjectAddIntegerProperty(&object, "isItalic", inherit->italic);
-					ObjectAddIntegerProperty(&object, "fontFamily", inherit->fontFamily == FONT_FAMILY_MONO ? 0xFFFD : 0xFFFF);
-				} else {
-					ObjectAddObjectProperty(&object, "textColor", ColorLookupPointer(m->textColor)->object2ID);
-					ObjectAddObjectProperty(&object, "selectedBackground", ColorLookupPointer(m->selectedBackground)->object2ID);
-					ObjectAddObjectProperty(&object, "selectedText", ColorLookupPointer(m->selectedText)->object2ID);
-					ObjectAddIntegerProperty(&object, "textSize", m->textSize);
-					ObjectAddIntegerProperty(&object, "fontWeight", m->fontWeight);
-					ObjectAddIntegerProperty(&object, "isItalic", m->italic);
-					ObjectAddIntegerProperty(&object, "fontFamily", m->fontFamily == FONT_FAMILY_MONO ? 0xFFFD : 0xFFFF);
-				}
-
-				ObjectAddIntegerProperty(&object, "iconSize", m->iconSize);
-				ObjectAddObjectProperty(&object, "iconColor", ColorLookupPointer(m->iconColor)->object2ID);
-				textStyle = object;
-
-				metrics.type = OBJ_LAYER_METRICS, metrics.id = ++objectIDAllocator;
-				ObjectAddIntegerProperty(&metrics, "clipEnabled", m->clipEnabled == CLIP_MODE_ENABLED);
-				ObjectAddIntegerProperty(&metrics, "wrapText", m->wrapText);
-				ObjectAddIntegerProperty(&metrics, "ellipsis", m->ellipsis);
-				ObjectAddIntegerProperty(&metrics, "insets0", m->insets.l);
-				ObjectAddIntegerProperty(&metrics, "insets1", m->insets.r);
-				ObjectAddIntegerProperty(&metrics, "insets2", m->insets.t);
-				ObjectAddIntegerProperty(&metrics, "insets3", m->insets.b);
-				ObjectAddIntegerProperty(&metrics, "clipInsets0", m->clipInsets.l);
-				ObjectAddIntegerProperty(&metrics, "clipInsets1", m->clipInsets.r);
-				ObjectAddIntegerProperty(&metrics, "clipInsets2", m->clipInsets.t);
-				ObjectAddIntegerProperty(&metrics, "clipInsets3", m->clipInsets.b);
-				ObjectAddIntegerProperty(&metrics, "preferredWidth", m->preferredSize.width);
-				ObjectAddIntegerProperty(&metrics, "preferredHeight", m->preferredSize.height);
-				ObjectAddIntegerProperty(&metrics, "minimumWidth", m->minimumSize.width);
-				ObjectAddIntegerProperty(&metrics, "minimumHeight", m->minimumSize.height);
-				ObjectAddIntegerProperty(&metrics, "maximumWidth", m->maximumSize.width);
-				ObjectAddIntegerProperty(&metrics, "maximumHeight", m->maximumSize.height);
-				ObjectAddIntegerProperty(&metrics, "gapMajor", m->gaps.major);
-				ObjectAddIntegerProperty(&metrics, "gapMinor", m->gaps.minor);
-				ObjectAddIntegerProperty(&metrics, "gapWrap", m->gaps.wrap);
-				ObjectAddIntegerProperty(&metrics, "cursor", m->cursor);
-				ObjectAddIntegerProperty(&metrics, "horizontalTextAlign", m->textHorizontalAlign + 1);
-				ObjectAddIntegerProperty(&metrics, "verticalTextAlign", m->textVerticalAlign + 1);
-				ObjectAddIntegerProperty(&metrics, "_graphX", x);
-				ObjectAddIntegerProperty(&metrics, "_graphY", y);
-				ObjectAddIntegerProperty(&metrics, "_graphW", 80);
-				ObjectAddIntegerProperty(&metrics, "_graphH", 60);
-				arrput(objects, metrics);
-				x += 100;
-			} else {
-				assert(false);
-			}
-
-			ObjectAddIntegerProperty(&object, "_graphX", x);
-			ObjectAddIntegerProperty(&object, "_graphY", y);
-			ObjectAddIntegerProperty(&object, "_graphW", 80);
-			ObjectAddIntegerProperty(&object, "_graphH", 60);
-			arrput(objects, object);
-			x += 100;
-
-			uint64_t previousOverrideID = object.id;
-
-			for (uintptr_t i = 0; i < arrlenu(layer->sequences); i++) {
-				Sequence *s = layer->sequences[i];
-				assert(arrlenu(s->keyframes) == 1);
-				Keyframe *keyframe = s->keyframes[0];
-
-#if 0
-				char buffer[256];
-				snprintf(buffer, sizeof(buffer), "%s%s%s%s%s%s%s%s%s%s", 
-						((StringOption *) PrimaryState_Type.fields[s->primaryState].item.options)->string,
-						s->flagFocused ? " (focused)" : "",
-						s->flagChecked ? " (checked)" : "",
-						s->flagIndeterminate ? " (indeterminate)" : "",
-						s->flagDefault ? " (default)" : "",
-						s->flagItemFocus ? " (list item focus)" : "",
-						s->flagListFocus ? " (list focus)" : "",
-						s->flagBeforeEnter ? " (before enter)" : "",
-						s->flagAfterExit ? " (after exit)" : "",
-						s->flagSelected ? " (selected)" : "");
-				fprintf(stderr, "%.*s:%.*s:%s:%d\n", (int) style->name.byteCount, (char *) style->name.buffer,
-						(int) layer->name.byteCount, (char *) layer->name.buffer, buffer, s->duration);
-#endif
-
-				uint32_t stateBits = 0;
-				if (s->flagFocused) stateBits |= THEME_STATE_FOCUSED;
-				if (s->flagChecked) stateBits |= THEME_STATE_CHECKED;
-				if (s->flagIndeterminate) stateBits |= THEME_STATE_INDETERMINATE;
-				if (s->flagDefault) stateBits |= THEME_STATE_DEFAULT_BUTTON;
-				if (s->flagItemFocus) stateBits |= THEME_STATE_FOCUSED_ITEM;
-				if (s->flagListFocus) stateBits |= THEME_STATE_LIST_FOCUSED;
-				if (s->flagBeforeEnter) stateBits |= THEME_STATE_BEFORE_ENTER;
-				if (s->flagAfterExit) stateBits |= THEME_STATE_AFTER_EXIT;
-				if (s->flagSelected) stateBits |= THEME_STATE_SELECTED;
-
-				Object2 override = { .type = object.type, .id = ++objectIDAllocator };
-				ObjectAddIntegerProperty(&override, "_graphX", x);
-				ObjectAddIntegerProperty(&override, "_graphY", y);
-				ObjectAddIntegerProperty(&override, "_graphW", 80);
-				ObjectAddIntegerProperty(&override, "_graphH", 60);
-				ObjectAddObjectProperty(&override, "_parent", previousOverrideID);
-				ObjectAddIntegerProperty(&override, "_primaryState", s->primaryState);
-				ObjectAddIntegerProperty(&override, "_stateBits", stateBits);
-				ObjectAddIntegerProperty(&override, "_duration", s->duration);
-				AutoNameOverrideObject(&override, s->primaryState, stateBits);
-
-				bool addObject = false;
-
-				for (uintptr_t i = 0; i < arrlenu(keyframe->properties); i++) {
-					Property *property = &keyframe->properties[i];
-
-					if (layer->base.tag == LayerBase_box + 1 && property->path[0] == (uint32_t) -2 && property->path[1] == 8 && property->path[2] == 0
-							&& property->path[3] == 2 && (property->path[4] == 2 || property->path[4] == 3)) {
-						continue;
-					}
-
-					ExportProperty2(layer, property, &override);
-					addObject = true;
-				}
-
-				if (addObject) {
-					arrput(objects, override);
-					previousOverrideID = override.id;
-					x += 100;
-				} else {
-					arrfree(override.properties);
-				}
-			}	
-
-			if (object.type == OBJ_VAR_TEXT_STYLE) {
-				textStyle.id = previousOverrideID;
-			}
-
-			if (addToLayerGroup) {
-				sprintf(cPropertyName, "layers_%d_layer", layerCount);
-				ObjectAddObjectProperty(&layerGroup, cPropertyName, previousOverrideID);
-				sprintf(cPropertyName, "layers_%d_offset0", layerCount);
-				ObjectAddIntegerProperty(&layerGroup, cPropertyName, layer->offset.l);
-				sprintf(cPropertyName, "layers_%d_offset1", layerCount);
-				ObjectAddIntegerProperty(&layerGroup, cPropertyName, layer->offset.r);
-				sprintf(cPropertyName, "layers_%d_offset2", layerCount);
-				ObjectAddIntegerProperty(&layerGroup, cPropertyName, layer->offset.t);
-				sprintf(cPropertyName, "layers_%d_offset3", layerCount);
-				ObjectAddIntegerProperty(&layerGroup, cPropertyName, layer->offset.b);
-				sprintf(cPropertyName, "layers_%d_position0", layerCount);
-				ObjectAddIntegerProperty(&layerGroup, cPropertyName, layer->position.l);
-				sprintf(cPropertyName, "layers_%d_position1", layerCount);
-				ObjectAddIntegerProperty(&layerGroup, cPropertyName, layer->position.r);
-				sprintf(cPropertyName, "layers_%d_position2", layerCount);
-				ObjectAddIntegerProperty(&layerGroup, cPropertyName, layer->position.t);
-				sprintf(cPropertyName, "layers_%d_position3", layerCount);
-				ObjectAddIntegerProperty(&layerGroup, cPropertyName, layer->position.b);
-				sprintf(cPropertyName, "layers_%d_mode", layerCount);
-				ObjectAddIntegerProperty(&layerGroup, cPropertyName, layer->mode);
-				layerCount++;
-			}
-		}
-
-		if (layerCount) {
-			Object2 object = layerGroup;
-			ObjectAddIntegerProperty(&object, "_graphX", x);
-			ObjectAddIntegerProperty(&object, "_graphY", y);
-			ObjectAddIntegerProperty(&object, "_graphW", 80);
-			ObjectAddIntegerProperty(&object, "_graphH", 60);
-			ObjectAddIntegerProperty(&object, "layers_count", layerCount);
-			arrput(objects, object);
-			x += 100;
-		} else {
-			arrfree(layerGroup.properties);
-			layerGroup.id = 0;
-		}
-
-		{
-			Object2 object = { .type = OBJ_STYLE, .id = ++objectIDAllocator };
-			snprintf(object.cName, sizeof(object.cName), "%.*s", (int) style->name.byteCount, (const char *) style->name.buffer);
-			ObjectAddIntegerProperty(&object, "_graphX", x);
-			ObjectAddIntegerProperty(&object, "_graphY", y);
-			ObjectAddIntegerProperty(&object, "_graphW", 80);
-			ObjectAddIntegerProperty(&object, "_graphH", 60);
-			ObjectAddIntegerProperty(&object, "isPublic", style->publicStyle);
-			ObjectAddIntegerProperty(&object, "headerID", style->id);
-			ObjectAddObjectProperty(&object, "appearance", layerGroup.id);
-			ObjectAddObjectProperty(&object, "metrics", metrics.id);
-			ObjectAddObjectProperty(&object, "textStyle", textStyle.id);
-			arrput(objects, object);
-			x += 100;
-		}
-
-		y += 200;
-	}
-
-	// Saving.
-
-	FILE *f = fopen("bin/designer2.dat", "wb");
-	uint32_t version = 1;
-	fwrite(&version, 1, sizeof(uint32_t), f);
-	uint32_t objectCount = arrlenu(objects);
-	fwrite(&objectCount, 1, sizeof(uint32_t), f);
-	fwrite(&objectIDAllocator, 1, sizeof(uint64_t), f);
-
-	for (uintptr_t i = 0; i < arrlenu(objects); i++) {
-		Object2 copy = objects[i];
-		uint32_t propertyCount = arrlenu(copy.properties);
-		copy.properties = NULL;
-		fwrite(&copy, 1, sizeof(Object2), f);
-		fwrite(&propertyCount, 1, sizeof(uint32_t), f);
-		fwrite(objects[i].properties, 1, sizeof(Property2) * propertyCount, f);
-		arrfree(objects[i].properties);
-		assert(objects[i].id);
-	}
-
-	fclose(f);
-	arrfree(objects);
-}
-
-// ------------------- Preview canvas -------------------
-
-float Smooth(float x) {
-	return x * x * (3 - 2 * x);
-}
-
-void ApplyKeyframeOverrides(const Keyframe *keyframe, const PathToOffset *pathToOffsetList, const RfData data) {
-	for (uintptr_t j = 0; j < arrlenu(keyframe->properties); j++) {
-		const Property *property = keyframe->properties + j;
-		bool found = false;
-
-		for (uintptr_t k = 0; k < arrlenu(pathToOffsetList); k++) {
-			const PathToOffset *pathToOffset = pathToOffsetList + k;
-
-			if (!ArePathsEqual(pathToOffset->path, property->path + 1)) {
-				continue;
-			}
-
-			found = true;
-
-			RfItem item;
-			Keyframe *previousKeyframe = selected.keyframe;
-			selected.keyframe = (Keyframe *) keyframe; // HACK!
-			void *source = ResolveDataObject((RfPath *) property->path, &item);
-			selected.keyframe = previousKeyframe;
-			void *destination = (uint8_t *) data.buffer + pathToOffset->offset;
-
-			if (pathToOffset->offset + item.byteCount > data.byteCount) {
-				break;
-			}
-
-			if (item.type->op == StyleColorOp) {
-				*(uint32_t *) destination = ColorLookup(*(uint32_t *) source);
-			} else {
-				memcpy(destination, source, item.byteCount);
-			}
-
-			break;
-		}
-
-		// If it wasn't found, that's fine.
-		// e.g. deleting gradient stop, but a keyframe still has an override.
-		(void) found;
-	}
-}
-
-void AnimatingValueCalculate(AnimatingValue *value) {
-	float progress = value->duration ? Smooth((float) value->elapsed / value->duration) : 1;
-
-	if (value->type == ANIMATING_VALUE_TYPE_COLOR) {
-		uint32_t from = value->from.u32;
-		float fr = UI_COLOR_RED_F(from);
-		float fg = UI_COLOR_GREEN_F(from);
-		float fb = UI_COLOR_BLUE_F(from);
-		float fa = UI_COLOR_ALPHA_F(from);
-		uint32_t to = value->to.u32;
-		float tr = UI_COLOR_RED_F(to);
-		float tg = UI_COLOR_GREEN_F(to);
-		float tb = UI_COLOR_BLUE_F(to);
-		float ta = UI_COLOR_ALPHA_F(to);
-		if (!fa) fr = tr, fg = tg, fb = tb;
-		if (!ta) tr = fr, tg = fg, tb = fb;
-		float dr = (tr - fr) * progress + fr;
-		float dg = (tg - fg) * progress + fg;
-		float db = (tb - fb) * progress + fb;
-		float da = (ta - fa) * progress + fa;
-		value->from.u32 = UI_COLOR_FROM_RGBA_F(dr, dg, db, da);
-	} else if (value->type == ANIMATING_VALUE_TYPE_I8) {
-		value->from.i8 = (value->to.i8 - value->from.i8) * progress + value->from.i8;
-	} else if (value->type == ANIMATING_VALUE_TYPE_I16) {
-		value->from.i16 = (value->to.i16 - value->from.i16) * progress + value->from.i16;
-	} else if (value->type == ANIMATING_VALUE_TYPE_FLOAT) {
-		value->from.f32 = (value->to.f32 - value->from.f32) * progress + value->from.f32;
-	} else {
-		assert(false);
-	}
-}
-
-SequenceStateSelector GetCurrentSequenceStateSelector() {
-	SequenceStateSelector s = { 0 };
-	s.primary = previewPrimaryState;
-	s.focused = previewStateFocused->e.flags & UI_BUTTON_CHECKED;
-	s.checked = previewStateChecked->e.flags & UI_BUTTON_CHECKED;
-	s.indeterminate = previewStateIndeterminate->e.flags & UI_BUTTON_CHECKED;
-	s._default = previewStateDefault->e.flags & UI_BUTTON_CHECKED;
-	s.itemFocus = previewStateItemFocus->e.flags & UI_BUTTON_CHECKED;
-	s.listFocus = previewStateListFocus->e.flags & UI_BUTTON_CHECKED;
-	s.selected = previewStateSelected->e.flags & UI_BUTTON_CHECKED;
-	s.enter = previewStateBeforeEnter->e.flags & UI_BUTTON_CHECKED;
-	s.exit = previewStateAfterExit->e.flags & UI_BUTTON_CHECKED;
-	return s;
-}
-
-bool SequenceMatchesPreviewState(Sequence *sequence, SequenceStateSelector selector) {
-	return (sequence->primaryState == selector.primary || sequence->primaryState == PRIMARY_STATE_ANY)
-		&& (!sequence->flagFocused || selector.focused)
-		&& (!sequence->flagChecked || selector.checked)
-		&& (!sequence->flagIndeterminate || selector.indeterminate)
-		&& (!sequence->flagDefault || selector._default)
-		&& (!sequence->flagItemFocus || selector.itemFocus)
-		&& (!sequence->flagListFocus || selector.listFocus)
-		&& (!sequence->flagBeforeEnter || selector.enter)
-		&& (!sequence->flagAfterExit || selector.exit)
-		&& (!sequence->flagSelected || selector.selected);
-}
-
-void ApplySequenceOverrides(Layer *layer, PathToOffset *pathToOffsetList, RfData data, SequenceStateSelector selector) {
-	for (uintptr_t j = 0; j < arrlenu(layer->sequences); j++) {
-		if (SequenceMatchesPreviewState(layer->sequences[j], selector)) {
-			for (uintptr_t k = 0; k < arrlenu(layer->sequences[j]->keyframes); k++) {
-				Layer *previousLayer = selected.layer;
-				selected.layer = layer; // HACK!
-				ApplyKeyframeOverrides(layer->sequences[j]->keyframes[k], pathToOffsetList, data);
-				selected.layer = previousLayer;
-			}
-		}
-	}
-}
-
-void *PrepareThemeDataForLayer(EsBuffer *themeData, EsPainter *themePainter, Layer *layer, bool applyOverrides, uintptr_t index, UIPainter *painter) {
-	PathToOffset *pathToOffsetList = NULL;
-	RfData data = ExportToGrowableBuffer(&Layer_Type, sizeof(Layer), NULL, layer, &pathToOffsetList);
-
-	if (applyOverrides) {
-		Keyframe *keyframe = NULL;
-
-		if (layer == selected.layer && selected.keyframe) {
-			keyframe = selected.keyframe;
-		}
-
-		if (keyframe) {
-			ApplyKeyframeOverrides(keyframe, pathToOffsetList, data);
-		} else {
-			ApplySequenceOverrides(layer, pathToOffsetList, data, currentStateSelector);
-
-			for (uintptr_t j = 0; j < arrlenu(animatingValues); j++) {
-				if (animatingValues[j].layer != index) {
-					continue;
-				}
-
-				AnimatingValue value = animatingValues[j];
-				uint8_t *destination = (uint8_t *) data.buffer + value.offset;
-				AnimatingValueCalculate(&value);
-
-				if (value.type == ANIMATING_VALUE_TYPE_COLOR) {
-					*(uint32_t *) destination = value.from.u32;
-				} else if (value.type == ANIMATING_VALUE_TYPE_I8) {
-					*(int8_t *) destination = value.from.i8;
-				} else if (value.type == ANIMATING_VALUE_TYPE_I16) {
-					*(int16_t *) destination = value.from.i16;
-				} else if (value.type == ANIMATING_VALUE_TYPE_FLOAT) {
-					*(float *) destination = value.from.f32;
-				} else {
-					assert(false);
-				}
-			}
-		}
-	}
-
-	ExportFreePathToOffsetList(pathToOffsetList);
-
-	themeData->in = data.buffer;
-	themeData->bytes = data.byteCount;
-	themePainter->clip.l = painter->clip.l;
-	themePainter->clip.r = painter->clip.r;
-	themePainter->clip.t = painter->clip.t;
-	themePainter->clip.b = painter->clip.b;
-	themePainter->target->bits = painter->bits;
-	themePainter->target->width = painter->width;
-	themePainter->target->height = painter->height;
-	themePainter->target->stride = painter->width * 4;
-	return data.buffer;
-}
-
-void DrawStyle(UIPainter *painter, UIRectangle generalBounds, UIRectangle *globalOffset, float scale, UIRectangle *opaqueRegion, bool applyOverrides, Style *style) {
-	for (uintptr_t i = 0; i < arrlenu(style->layers); i++) {
-		Layer *layer = LayerLookup(style->layers[i]);
-		EsPaintTarget paintTarget = { 0 };
-		EsPainter themePainter = { 0 };
-		themePainter.target = &paintTarget;
-		EsBuffer themeData = { 0 };
-		void *dataBuffer = PrepareThemeDataForLayer(&themeData, &themePainter, layer, applyOverrides, i, painter);
-
-		if (i) {
-			UIRectangle bounds = UIRectangleAdd(generalBounds, *globalOffset);
-			EsRectangle bounds2 = { bounds.l, bounds.r, bounds.t, bounds.b };
-			ThemeDrawLayer(&themePainter, bounds2, &themeData, scale, *(EsRectangle *) opaqueRegion);
-		} else {
-			EsBufferRead(&themeData, sizeof(ThemeLayer));
-			globalOffset->l = 0;
-			globalOffset->r = 0;
-			globalOffset->t = 0;
-			globalOffset->b = 0;
-		}
-
-		free(dataBuffer);
-	}
-
-	if (editPoints->e.flags & UI_BUTTON_CHECKED) {
-		for (uintptr_t i = 0; i < arrlenu(style->layers); i++) {
-			Layer *_layer = LayerLookup(style->layers[i]);
-
-			if (_layer->base.tag != LayerBase_path + 1 || _layer != selected.layer) {
-				continue;
-			}
-
-			EsPaintTarget paintTarget = { 0 };
-			EsPainter themePainter = { 0 };
-			themePainter.target = &paintTarget;
-			EsBuffer themeData = { 0 };
-			void *dataBuffer = PrepareThemeDataForLayer(&themeData, &themePainter, _layer, applyOverrides, i, painter);
-
-			const ThemeLayer *layer = (const ThemeLayer *) EsBufferRead(&themeData, sizeof(ThemeLayer));
-			UIRectangle _bounds = UIRectangleAdd(generalBounds, *globalOffset), bounds;
-			bounds.l = _bounds.l + (int) (scale * layer->offset.l) + THEME_RECT_WIDTH(_bounds)  * layer->position.l / 100;
-			bounds.r = _bounds.l + (int) (scale * layer->offset.r) + THEME_RECT_WIDTH(_bounds)  * layer->position.r / 100;
-			bounds.t = _bounds.t + (int) (scale * layer->offset.t) + THEME_RECT_HEIGHT(_bounds) * layer->position.t / 100;
-			bounds.b = _bounds.t + (int) (scale * layer->offset.b) + THEME_RECT_HEIGHT(_bounds) * layer->position.b / 100;
-
-			const ThemeLayerPath *path = (const ThemeLayerPath *) EsBufferRead(&themeData, sizeof(ThemeLayerPath));
-			const float *points = (const float *) EsBufferRead(&themeData, sizeof(float) * 6 * path->pointCount);
-
-#if 0
-			for (uintptr_t i = 0; i < path->pointCount * 6; i += 2) {
-				intptr_t k = (i / 2) % 3;
-				if (k == 0) continue;
-				float x = points[i + 0], y = points[i + 1];
-				intptr_t m = k == 2 ? (i + 2) : (i - 2);
-				if (m < 0) m += path->pointCount * 6;
-				if (m == path->pointCount * 6) m = 0;
-				float mx = points[m + 0], my = points[m + 1];
-
-				x = UI_RECT_WIDTH(bounds) * x / 100.0f + bounds.l;
-				y = UI_RECT_HEIGHT(bounds) * y / 100.0f + bounds.t;
-				mx = UI_RECT_WIDTH(bounds) * mx / 100.0f + bounds.l;
-				my = UI_RECT_HEIGHT(bounds) * my / 100.0f + bounds.t;
-
-				UIDrawLine(painter, x, y, mx, my, 0xFF000000);
-			}
-
-			for (uintptr_t i = 0; i < path->pointCount * 6; i += 2) {
-				intptr_t k = (i / 2) % 3;
-				if (k == 0) continue;
-				float x = points[i + 0], y = points[i + 1];
-
-				x = UI_RECT_WIDTH(bounds) * x / 100.0f + bounds.l;
-				y = UI_RECT_HEIGHT(bounds) * y / 100.0f + bounds.t;
-
-				UIDrawRectangle(painter, UI_RECT_4(x - 6, x + 6, y - 6, y + 6), 
-						0xFFE0E0E0, 0xFFA8A8A8, UI_RECT_1(1));
-			}
-#endif
-
-			for (uintptr_t i = 0; i < path->pointCount * 6; i += 2) {
-				intptr_t k = (i / 2) % 3;
-				if (k != 0) continue;
-				float x = points[i + 0], y = points[i + 1];
-
-				x = UI_RECT_WIDTH(bounds) * x / 100.0f + bounds.l;
-				y = UI_RECT_HEIGHT(bounds) * y / 100.0f + bounds.t;
-
-				UIDrawRectangle(painter, UI_RECT_4(x - 6, x + 6, y - 6, y + 6), 
-						0xFFFFFFFF, 0xFFA8A8A8, UI_RECT_1(1));
-			}
-
-			free(dataBuffer);
-		}
-	}
-}
-
-int CanvasMessage(UIElement *element, UIMessage message, int di, void *dp) {
-#define CANVAS_DRAW_BOUNDS() \
-	float scale = previewScale->position * 4 + 1; \
-	int drawX = 100 + elementCanvas->bounds.l; \
-	int drawY = 100 + elementCanvas->bounds.t; \
-	int drawWidth = 1000 * previewWidth->position * scale; \
-	int drawHeight = 1000 * previewHeight->position * scale
-
-	if (message == UI_MSG_PAINT) {
-		UIPainter *painter = (UIPainter *) dp;
-		uint32_t background;
-		UIColorToRGB(previewBackgroundColor->hue, previewBackgroundColor->saturation, previewBackgroundColor->value, &background);
-		UIDrawBlock(painter, element->bounds, background | 0xFF000000);
-
-		CANVAS_DRAW_BOUNDS();
-
-		UIRectangle generalBounds = UI_RECT_4(drawX, drawX + drawWidth, drawY, drawY + drawHeight);
-
-		if (!selected.style) {
-			for (uintptr_t i = 0; i < arrlenu(styleSet.styles); i++) {
-				UIRectangle iBounds = UIRectangleAdd(generalBounds, UI_RECT_1I(-10));
-
-				if (UIRectangleContains(iBounds, element->window->cursorX, element->window->cursorY)) {
-					UIDrawString(painter, UI_RECT_4(element->bounds.l, element->bounds.r, element->bounds.t, element->bounds.t + 25), 
-						styleSet.styles[i]->name.buffer, styleSet.styles[i]->name.byteCount, 0x000000, UI_ALIGN_LEFT, NULL);
-					UIDrawBlock(painter, iBounds, 0xFFA2A0A4);
-				}
-
-				UIRectangle opaqueRegion = { 0 };
-				UIRectangle globalOffset = { 0 };
-				DrawStyle(painter, generalBounds, &globalOffset, scale, &opaqueRegion, false, styleSet.styles[i]);
-
-				if (generalBounds.r + drawWidth > elementCanvas->bounds.r - 100) {
-					generalBounds.l = 100 + elementCanvas->bounds.l;
-					generalBounds.r = generalBounds.l + drawWidth;
-					generalBounds.t += drawHeight + 20;
-					generalBounds.b += drawHeight + 20;
-				} else {
-					generalBounds.l += drawWidth + 20;
-					generalBounds.r += drawWidth + 20;
-				}
-			}
-
-			// UIDrawString(painter, element->bounds, "(select a style to preview it)", -1, 0x000000, UI_ALIGN_CENTER, NULL);
-			return 0;
-		}
-
-		if (!arrlenu(selected.style->layers)) {
-			UIDrawString(painter, element->bounds, "(selected style has no layers)", -1, 0x000000, UI_ALIGN_CENTER, NULL);
-			return 0;
-		}
-
-		if (previewShowGuides->e.flags & UI_BUTTON_CHECKED) {
-			UIDrawBlock(painter, UIRectangleAdd(generalBounds, UI_RECT_1I(-2)), 0xFFA2A0A4);
-			UIDrawBlock(painter, UIRectangleAdd(generalBounds, UI_RECT_1I(0)), 0xFFC2C0C4);
-		}
-
-		Rectangle8 opaqueInsets = StyleCalculateOpaqueInsets(selected.style->layers);
-		UIRectangle opaqueRegion = { 0 };
-		
-		if (opaqueInsets.l != 0x7F && opaqueInsets.r != 0x7F
-				&& opaqueInsets.t != 0x7F && opaqueInsets.b != 0x7F) {
-			opaqueRegion = UIRectangleAdd(generalBounds, UI_RECT_4(opaqueInsets.l * scale, -opaqueInsets.r * scale, 
-					opaqueInsets.t * scale, -opaqueInsets.b * scale));
-		}
-
-		UIRectangle globalOffset = { 0 };
-
-		DrawStyle(painter, generalBounds, &globalOffset, scale, &opaqueRegion, true, selected.style);
-
-		if (previewShowComputed->e.flags & UI_BUTTON_CHECKED) {
-			Rectangle8 paintOutsets8 = StyleCalculatePaintOutsets(selected.style->layers);
-			UIRectangle paintOutsets = UI_RECT_4(-paintOutsets8.l * scale, paintOutsets8.r * scale, -paintOutsets8.t * scale, paintOutsets8.b * scale);
-			UIDrawBlock(painter, UIRectangleAdd(generalBounds, paintOutsets), 0xFF0000);
-
-			Rectangle8 opaqueInsets8 = StyleCalculateOpaqueInsets(selected.style->layers);
-
-			if (opaqueInsets8.l != 0x7F && opaqueInsets8.r != 0x7F && opaqueInsets8.t != 0x7F && opaqueInsets8.b != 0x7F) {
-				UIRectangle opaqueInsets = UI_RECT_4(opaqueInsets8.l * scale, -opaqueInsets8.r * scale, opaqueInsets8.t * scale, -opaqueInsets8.b * scale);
-				UIDrawBlock(painter, UIRectangleAdd(generalBounds, opaqueInsets), 0x00FF00);
-			}
-		}
-
-		{
-			char buffer[128];
-			snprintf(buffer, 128, "%dx%dpx at %d%% scale", (int) (1000 * previewWidth->position), (int) (1000 * previewHeight->position), (int) (100 * scale));
-			UIRectangle bounds = element->bounds;
-			bounds.b = bounds.t + 16;
-			UIDrawString(painter, bounds, buffer, -1, 0x000000, UI_ALIGN_LEFT, NULL);
-		}
-	} else if (message == UI_MSG_ANIMATE) {
-		uint64_t current = UIAnimateClock();
-		uint64_t delta = current - previewTransitionLastTime;
-
-		for (uintptr_t i = 0; i < arrlenu(animatingValues); i++) {
-			animatingValues[i].elapsed += delta;
-
-			if (animatingValues[i].elapsed >= animatingValues[i].duration) {
-				animatingValues[i].elapsed = animatingValues[i].duration;
-			}
-		}
-
-		previewTransitionLastTime = current;
-		UIElementRepaint(element, NULL);
-	} else if (message == UI_MSG_MOUSE_MOVE) {
-		if (!selected.style) {
-			UIElementRepaint(element, NULL);
-		}
-	}
-
-	return 0;
-}
-
-void PreviewTransitionInvoke(void *_unused) {
-	previewTransition->e.flags ^= UI_BUTTON_CHECKED;
-
-	if (previewTransition->e.flags & UI_BUTTON_CHECKED) {
-		UIElementAnimate(elementCanvas, false);
-		previewTransitionLastTime = UIAnimateClock();
-	} else {
-		UIElementAnimate(elementCanvas, true);
-		arrfree(animatingValues);
-	}
-
-	UIElementRepaint(&previewTransition->e, NULL);
-	UIElementRepaint(elementCanvas, NULL);
-}
-
-void PreviewPreferredSizeInvoke(void *_unused) {
-	previewWidth->position = selected.style ? LayerLookup(selected.style->layers[0])->base.metrics.preferredSize.width / 1000.0f : 0.1f;
-	previewHeight->position = selected.style ? LayerLookup(selected.style->layers[0])->base.metrics.preferredSize.height / 1000.0f : 0.1f;
-
-	UIElementRefresh(&previewWidth->e);
-	UIElementRefresh(&previewHeight->e);
-	UIElementRepaint(elementCanvas, NULL);
-}
-
-void PreviewFixAspectRatioInvoke(void *_unused) {
-	previewHeight->position = previewWidth->position;
-	previewFixAspectRatio->e.flags ^= UI_BUTTON_CHECKED;
-	previewHeight->e.flags ^= UI_ELEMENT_HIDE;
-	UIElementRefresh(&previewFixAspectRatio->e);
-	UIElementRefresh(&previewHeight->e);
-	UIElementRepaint(elementCanvas, NULL);
-}
-
-void PreviewShowGuidesInvoke(void *_unused) {
-	previewShowGuides->e.flags ^= UI_BUTTON_CHECKED;
-	UIElementRefresh(&previewShowGuides->e);
-	UIElementRepaint(elementCanvas, NULL);
-}
-
-void PreviewShowComputedInvoke(void *_unused) {
-	previewShowComputed->e.flags ^= UI_BUTTON_CHECKED;
-	UIElementRefresh(&previewShowComputed->e);
-	UIElementRepaint(elementCanvas, NULL);
-}
-
-void EditPointsInvoke(void *_unused) {
-	editPoints->e.flags ^= UI_BUTTON_CHECKED;
-	UIElementRefresh(&editPoints->e);
-	UIElementRepaint(elementCanvas, NULL);
-}
-
-int PreviewSliderMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_VALUE_CHANGED) {
-		if (previewFixAspectRatio->e.flags & UI_BUTTON_CHECKED) {
-			previewHeight->position = previewWidth->position;
-		}
-		UIElementRepaint(elementCanvas, NULL);
-	}
-
-	return 0;
-}
-
-void UpdateAnimationListWithSequence(Sequence *sequence, int layer, PathToOffset *pathToOffsetList, uint8_t *data) {
-	for (uintptr_t i = 0; i < arrlenu(sequence->keyframes); i++) {
-		Keyframe *keyframe = sequence->keyframes[i];
-
-		for (uintptr_t j = 0; j < arrlenu(keyframe->properties); j++) {
-			Property *property = keyframe->properties + j;
-
-			uint16_t offset = 0xFFFF;
-
-			for (uintptr_t k = 0; k < arrlenu(pathToOffsetList); k++) {
-				const PathToOffset *pathToOffset = pathToOffsetList + k;
-
-				if (!ArePathsEqual(pathToOffset->path, property->path + 1)) {
-					continue;
-				}
-
-				offset = pathToOffset->offset;
-				assert(pathToOffset->offset < 0xFFFF);
-				break;
-			}
-
-			if (offset == 0xFFFF) {
-				continue;
-			}
-
-			// TODO Binary search.
-
-			uintptr_t point = 0;
-			bool found = false;
-
-			for (uintptr_t k = 0; k < arrlenu(animatingValues); k++) {
-				if (animatingValues[k].layer < layer || animatingValues[k].offset < offset) {
-					point = k + 1;
-				} else if (animatingValues[k].layer == layer && animatingValues[k].offset == offset) {
-					found = true;
-					point = k;
-					break;
-				}
-			}
-
-			AnimatingValue *value;
-
-			if (found) {
-				value = animatingValues + point;
-				value->elapsed = 0;
-				value->duration = sequence->duration;
-			} else {
-				AnimatingValue _value = { 0 };
-				_value.offset = offset;
-				_value.layer = layer;
-				_value.duration = sequence->duration;
-				arrins(animatingValues, point, _value);
-				value = animatingValues + point;
-			}
-
-			RfItem item;
-			void *source;
-
-			{
-				Keyframe *previousKeyframe = selected.keyframe;
-				selected.keyframe = keyframe; // HACK!
-				source = ResolveDataObject((RfPath *) property->path, &item);
-				selected.keyframe = previousKeyframe;
-			}
-
-			if (item.type == &StyleI8_Type) {
-				value->type = ANIMATING_VALUE_TYPE_I8;
-				if (!found) value->from.i8 = *(int8_t *) (data + offset);
-				value->to.i8 = *(int8_t *) source;
-			} else if (item.type == &StyleI16_Type) {
-				value->type = ANIMATING_VALUE_TYPE_I16;
-				if (!found) value->from.i16 = *(int16_t *) (data + offset);
-				value->to.i16 = *(int16_t *) source;
-			} else if (item.type == &StyleColor_Type) {
-				value->type = ANIMATING_VALUE_TYPE_COLOR;
-				if (!found) value->from.u32 = *(uint32_t *) (data + offset);
-				value->to.u32 = ColorLookup(*(uint32_t *) source);
-			} else if (item.type == &StyleFloat_Type) {
-				value->type = ANIMATING_VALUE_TYPE_FLOAT;
-				if (!found) value->from.f32 = *(float *) (data + offset);
-				value->to.f32 = *(float *) source;
-			} else {
-				assert(false);
-			}
-		}
-	}
-}
-
-void UpdateAnimationList() {
-	if (!selected.style) return;
-
-	SequenceStateSelector oldStateSelector = currentStateSelector;
-	currentStateSelector = GetCurrentSequenceStateSelector();
-
-	for (uintptr_t i = 0; i < arrlenu(animatingValues); i++) {
-		AnimatingValueCalculate(animatingValues + i);
-		animatingValues[i].type |= ANIMATING_VALUE_TYPE_UNUSED;
-	}
-
-	for (uintptr_t i = 0; i < arrlenu(selected.style->layers); i++) {
-		Layer *layer = LayerLookup(selected.style->layers[i]);
-
-		PathToOffset *pathToOffsetList = NULL;
-		RfData data = ExportToGrowableBuffer(&Layer_Type, sizeof(Layer), NULL, layer, &pathToOffsetList);
-		ApplySequenceOverrides(layer, pathToOffsetList, data, oldStateSelector); 
-
-		PathToOffset *pathToOffsetList2 = NULL;
-		RfData data2 = ExportToGrowableBuffer(&Layer_Type, sizeof(Layer), NULL, layer, &pathToOffsetList2);
-		ApplySequenceOverrides(layer, pathToOffsetList2, data2, currentStateSelector); 
-
-		for (uintptr_t j = 0; j < arrlenu(layer->sequences); j++) {
-			Sequence *sequence = layer->sequences[j];
-
-			if (SequenceMatchesPreviewState(sequence, currentStateSelector)) {
-				Layer *previousLayer = selected.layer;
-				selected.layer = layer; // HACK!
-				UpdateAnimationListWithSequence(sequence, i, pathToOffsetList, (uint8_t *) data.buffer);
-				selected.layer = previousLayer;
-			}
-		}
-
-		for (uintptr_t j = 0; j < arrlenu(animatingValues); j++) {
-			AnimatingValue *value = animatingValues + j;
-
-			if ((value->type & ANIMATING_VALUE_TYPE_UNUSED) && value->layer == i) {
-				// Return values to base.
-
-				value->type &= ~ANIMATING_VALUE_TYPE_UNUSED;
-				value->elapsed = 0;
-				uint8_t *source = ((uint8_t *) data2.buffer + value->offset);
-
-				if (value->type == ANIMATING_VALUE_TYPE_I8) {
-					value->to.i8 = *(int8_t *) source;
-				} else if (value->type == ANIMATING_VALUE_TYPE_I16) {
-					value->to.i16 = *(int16_t *) source;
-				} else if (value->type == ANIMATING_VALUE_TYPE_COLOR) {
-					value->to.u32 = *(uint32_t *) source;
-				} else if (value->type == ANIMATING_VALUE_TYPE_FLOAT) {
-					value->to.f32 = *(float *) source;
-				} else {
-					assert(false);
-				}
-			}
-		}
-
-		ExportFreePathToOffsetList(pathToOffsetList);
-		ExportFreePathToOffsetList(pathToOffsetList2);
-		free(data.buffer);
-		free(data2.buffer);
-	}
-}
-
-void PreviewSetPrimaryState(void *cp) {
-	previewPrimaryState = (uintptr_t) cp;
-
-	UIElement *child = previewPrimaryStatePanel->e.children;
-
-	while (child) {
-		if (child->cp == cp) child->flags |= UI_BUTTON_CHECKED;
-		else child->flags &= ~UI_BUTTON_CHECKED;
-		child = child->next;
-	}
-
-	UIElementRefresh(&previewPrimaryStatePanel->e);
-	UpdateAnimationList();
-}
-
-int PreviewToggleState(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_CLICKED) {
-		element->flags ^= UI_BUTTON_CHECKED;
-		UIElementRefresh(element);
-		UpdateAnimationList();
-	}
-
-	return 0;
-}
-
-int PreviewChangeBackgroundColor(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_VALUE_CHANGED) {
-		UIElementRefresh(elementCanvas);
-	}
-
-	return 0;
-}
-
-// ------------------- Paths -------------------
-
-void MakeUI(MakeUIState *state, RfItem *item, void *pointer) {
-	RfIterator iterator = { 0 };
-	iterator.includeRemovedFields = true;
-	iterator.s.op = RF_OP_COUNT;
-	item->type->op(&iterator.s, item, pointer);
-	iterator.s.op = RF_OP_ITERATE;
-	uint32_t count = iterator.index;
-
-	for (uint32_t i = 0; i < count; i++) {
-		iterator.index = i;
-		item->type->op(&iterator.s, item, pointer);
-		if (iterator.s.error) return;
-		MakeUIState s = { 0 };
-		s.s = state->s;
-		s.index = i;
-		s.parent = state;
-		s.inKeyframe = state->inKeyframe;
-		s.recurse = true;
-		if (iterator.isRemoved) continue;
-		iterator.item.type->op(&s.s, &iterator.item, iterator.pointer);
-		if (s.recurse) MakeUI(&s, &iterator.item, iterator.pointer);
-	}
-}
-
-void MakeHeaderAndIndentUI(const char *format, RfState *state, RfItem *item, void *pointer) {
-	char buffer[64];
-	snprintf(buffer, sizeof(buffer), format, ((MakeUIState *) state)->index + 1);
-
-#if 0
-	UILabelCreate(0, 0, buffer, -1);
-	UIPanel *subPanel = UIPanelCreate(0, UI_PANEL_EXPAND | UI_ELEMENT_PARENT_PUSH);
-	subPanel->gap = 5;
-	subPanel->border.l = 20;
-	subPanel->border.b = 20;
-#endif
-
-	UIExpandPane *pane = UIExpandPaneCreate(0, UI_ELEMENT_PARENT_PUSH, buffer, -1, UI_PANEL_EXPAND);
-	pane->panel->gap = 5;
-	pane->panel->border.l = 20;
-	pane->panel->border.t = 5;
-	pane->panel->border.r = 5;
-	pane->panel->border.b = 20;
-
-	MakeUI((MakeUIState *) state, item, pointer);
-	UIParentPop();
-
-	((MakeUIState *) state)->recurse = false;
-}
-
-void InspectorUnsubscribe(UIElement *element) {
-	for (uintptr_t i = 0; i < arrlenu(inspectorSubscriptions); i++) {
-		if (inspectorSubscriptions[i] == element) {
-			arrdel(inspectorSubscriptions, i);
-			free(element->cp);
-			return;
-		}
-	}
-
-	assert(false);
-}
-
-void BuildPathForUI(MakeUIState *state, uint32_t last, UIElement *element) {
-	int count = 1;
-
-	if (state->inKeyframe) {
-		count++;
-	}
-
-	MakeUIState *s = state;
-
-	while (s) {
-		count++;
-
-		if (!s->parent && s->basePath) {
-			for (uintptr_t i = 0; s->basePath[i] != RF_PATH_TERMINATOR; i++) {
-				count++;
-			}
-		}
-
-		s = s->parent;
-	}
-
-	RfPath *path = (RfPath *) malloc((count + 1) * sizeof(uint32_t));
-
-	path->indices[count - 1] = last;
-	path->indices[count] = RF_PATH_TERMINATOR;
-
-	s = state;
-	count--;
-
-	while (s) {
-		path->indices[--count] = s->index;
-
-		if (!s->parent && s->basePath) {
-			for (uintptr_t i = 0; s->basePath[i] != RF_PATH_TERMINATOR; i++) {
-				path->indices[state->inKeyframe ? (i + 1) : i] = s->basePath[i];
-				count--;
-			}
-		}
-
-		s = s->parent;
-	}
-
-	if (state->inKeyframe) {
-		path->indices[--count] = PATH_IN_KEYFRAME;
-	}
-
-	assert(!count);
-	element->cp = path;
-	arrput(inspectorSubscriptions, element);
-	UIElementMessage(element, MSG_PROPERTY_CHANGED, 0, ResolveDataObject(path, NULL));
-}
-
-// ------------------- Inspector -------------------
-
-int StyleI8Message(UIElement *element, UIMessage message, int di, void *dp) {
-	UITextbox *textbox = (UITextbox *) element;
-
-	if (message == UI_MSG_DESTROY) {
-		InspectorUnsubscribe(element);
-	} else if (message == UI_MSG_VALUE_CHANGED) {
-		textbox->string = realloc(textbox->string, textbox->bytes + 1);
-		textbox->string[textbox->bytes] = 0;
-		int8_t newValue = atoi(textbox->string);
-
-		ModData mod = { 0 };
-		mod.tag = ModData_changeProperty + 1;
-		mod.changeProperty.property.path = DuplicatePath((uint32_t *) element->cp);
-		mod.changeProperty.property.data = SaveToGrowableBuffer(&StyleI8_Type, sizeof(newValue), NULL, &newValue);
-		mod.changeProperty.source = element;
-		ModApply(&mod);
-	} else if (message == MSG_PROPERTY_CHANGED) {
-		char buffer[16];
-		textbox->carets[0] = 0;
-		textbox->carets[1] = textbox->bytes;
-		UITextboxReplace(textbox, buffer, snprintf(buffer, 16, "%d", *(int8_t *) dp), false);
-		UIElementRepaint(element, NULL);
-	} else if (message == UI_MSG_UPDATE && di == UI_UPDATE_FOCUSED) {
-		if (element->window->focused == element) {
-			textbox->carets[0] = 0;
-			textbox->carets[1] = textbox->bytes;
-		} else {
-			StyleI8Message(element, MSG_PROPERTY_CHANGED, 0, ResolveDataObject((RfPath *) element->cp, NULL));
-		}
-	}
-
-	return 0;
-}
-
-int StyleI16Message(UIElement *element, UIMessage message, int di, void *dp) {
-	UITextbox *textbox = (UITextbox *) element;
-
-	if (message == UI_MSG_DESTROY) {
-		InspectorUnsubscribe(element);
-	} else if (message == UI_MSG_VALUE_CHANGED) {
-		textbox->string = realloc(textbox->string, textbox->bytes + 1);
-		textbox->string[textbox->bytes] = 0;
-		int16_t newValue = atoi(textbox->string);
-
-		ModData mod = { 0 };
-		mod.tag = ModData_changeProperty + 1;
-		mod.changeProperty.property.path = DuplicatePath((uint32_t *) element->cp);
-		mod.changeProperty.property.data = SaveToGrowableBuffer(&StyleI16_Type, sizeof(newValue), NULL, &newValue);
-		mod.changeProperty.source = element;
-		ModApply(&mod);
-	} else if (message == MSG_PROPERTY_CHANGED) {
-		char buffer[16];
-		textbox->carets[0] = 0;
-		textbox->carets[1] = textbox->bytes;
-		UITextboxReplace(textbox, buffer, snprintf(buffer, 16, "%d", *(int16_t *) dp), false);
-		UIElementRepaint(element, NULL);
-	} else if (message == UI_MSG_UPDATE && di == UI_UPDATE_FOCUSED) {
-		if (element->window->focused == element) {
-			textbox->carets[0] = 0;
-			textbox->carets[1] = textbox->bytes;
-		} else {
-			StyleI16Message(element, MSG_PROPERTY_CHANGED, 0, ResolveDataObject((RfPath *) element->cp, NULL));
-		}
-	}
-
-	return 0;
-}
-
-int StyleFloatMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	UITextbox *textbox = (UITextbox *) element;
-
-	if (message == UI_MSG_DESTROY) {
-		InspectorUnsubscribe(element);
-	} else if (message == UI_MSG_VALUE_CHANGED) {
-		textbox->string = realloc(textbox->string, textbox->bytes + 1);
-		textbox->string[textbox->bytes] = 0;
-		float newValue = strtof(textbox->string, NULL);
-
-		ModData mod = { 0 };
-		mod.tag = ModData_changeProperty + 1;
-		mod.changeProperty.property.path = DuplicatePath((uint32_t *) element->cp);
-		mod.changeProperty.property.data = SaveToGrowableBuffer(&StyleFloat_Type, sizeof(newValue), NULL, &newValue);
-		mod.changeProperty.source = element;
-		ModApply(&mod);
-	} else if (message == MSG_PROPERTY_CHANGED) {
-		char buffer[16];
-		textbox->carets[0] = 0;
-		textbox->carets[1] = textbox->bytes;
-		UITextboxReplace(textbox, buffer, snprintf(buffer, 16, "%.2f", *(float *) dp), false);
-		UIElementRepaint(element, NULL);
-	} else if (message == UI_MSG_UPDATE && di == UI_UPDATE_FOCUSED) {
-		if (element->window->focused == element) {
-			textbox->carets[0] = 0;
-			textbox->carets[1] = textbox->bytes;
-		} else {
-			StyleFloatMessage(element, MSG_PROPERTY_CHANGED, 0, ResolveDataObject((RfPath *) element->cp, NULL));
-		}
-	}
-
-	return 0;
-}
-
-int StyleStringMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	UITextbox *textbox = (UITextbox *) element;
-
-	if (message == UI_MSG_DESTROY) {
-		InspectorUnsubscribe(element);
-	} else if (message == UI_MSG_VALUE_CHANGED) {
-		RfData data = { 0 };
-		data.buffer = textbox->string;
-		data.byteCount = textbox->bytes;
-
-		ModData mod = { 0 };
-		mod.tag = ModData_changeProperty + 1;
-		mod.changeProperty.property.path = DuplicatePath((uint32_t *) element->cp);
-		mod.changeProperty.property.data = SaveToGrowableBuffer(&StyleString_Type, sizeof(data), NULL, &data);
-		mod.changeProperty.source = element;
-		ModApply(&mod);
-	} else if (message == MSG_PROPERTY_CHANGED) {
-		RfData *data = (RfData *) dp;
-		textbox->carets[0] = 0;
-		textbox->carets[1] = textbox->bytes;
-		UITextboxReplace(textbox, data->buffer, data->byteCount, false);
-		UIElementRepaint(element, NULL);
-	} else if (message == UI_MSG_UPDATE && di == UI_UPDATE_FOCUSED && element->window->focused == element) {
-		textbox->carets[0] = 0;
-		textbox->carets[1] = textbox->bytes;
-	}
-
-	return 0;
-}
-
-int StyleBoolMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_DESTROY) {
-		InspectorUnsubscribe(element);
-	} else if (message == UI_MSG_CLICKED) {
-		element->flags ^= UI_BUTTON_CHECKED;
-		bool newValue = element->flags & UI_BUTTON_CHECKED;
-
-		ModData mod = { 0 };
-		mod.tag = ModData_changeProperty + 1;
-		mod.changeProperty.property.path = DuplicatePath((uint32_t *) element->cp);
-		mod.changeProperty.property.data = SaveToGrowableBuffer(&StyleBool_Type, sizeof(newValue), NULL, &newValue);
-		mod.changeProperty.source = element;
-		ModApply(&mod);
-	} else if (message == MSG_PROPERTY_CHANGED) {
-		if (*(bool *) dp) element->flags |= UI_BUTTON_CHECKED;
-		else element->flags &= ~UI_BUTTON_CHECKED;
-		UIElementRepaint(element, NULL);
-	}
-
-	return 0;
-}
-
-int StyleUnionButtonMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	// Also used for enums.
-
-	if (message == UI_MSG_CLICKED && (~element->flags & UI_BUTTON_CHECKED)) {
-		uint32_t newValue = (uint32_t) (uintptr_t) element->cp;
-
-		ModData mod = { 0 };
-		mod.tag = ModData_changeProperty + 1;
-		mod.changeProperty.property.path = DuplicatePath((uint32_t *) element->parent->cp);
-		mod.changeProperty.property.data = SaveToGrowableBuffer(&rfU32, sizeof(newValue), NULL, &newValue);
-		ModApply(&mod);
-	}
-
-	return 0;
-}
-
-void StyleEnumMenuItemInvoke(void *cp) {
-	uint32_t newValue = (uint32_t) (uintptr_t) cp;
-
-	ModData mod = { 0 };
-	mod.tag = ModData_changeProperty + 1;
-	mod.changeProperty.property.path = DuplicatePath(menuPath);
-	mod.changeProperty.property.data = SaveToGrowableBuffer(&rfU32, sizeof(newValue), NULL, &newValue);
-	ModApply(&mod);
-}
-
-int StyleUnionButtonPanelMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	// Also used for enums.
-
-	if (message == UI_MSG_DESTROY) {
-		InspectorUnsubscribe(element);
-	} else if (message == MSG_PROPERTY_CHANGED) {
-		UIElement *child = element->children;
-
-		while (child) {
-			if ((uint32_t) (uintptr_t) child->cp == *(uint32_t *) dp) {
-				child->flags |= UI_BUTTON_CHECKED;
-			} else {
-				child->flags &= ~UI_BUTTON_CHECKED;
-			}
-
-			UIElementRepaint(child, NULL);
-			child = child->next;
-		}
-	}
-
-	return 0;
-}
-
-void RefreshAncestorsUntilInspector(UIElement *element) {
-	while (element != &panelInspector->e) {
-		element->clip = UI_RECT_1(0);
-		element = element->parent;
-	}
-
-	UIElementRefresh(&panelInspector->e);
-}
-
-int StyleChoiceButtonMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_DESTROY) {
-		InspectorUnsubscribe(element);
-	} else if (message == UI_MSG_GET_WIDTH) {
-		return 240 * element->window->scale;
-	} else if (message == MSG_PROPERTY_CHANGED) {
-		RfItem item;
-		void *_dp = ResolveDataObject((RfPath *) element->cp, &item);
-		assert(dp == _dp);
-		UIButton *button = (UIButton *) element;
-		free(button->label);
-		button->label = UIStringCopy(((StringOption *) item.type->fields[*(uint32_t *) dp].item.options)->string, -1);
-		UIElementRefresh(element);
-	} else if (message == UI_MSG_CLICKED) {
-		RfItem item;
-		ResolveDataObject((RfPath *) element->cp, &item);
-		UIMenu *menu = UIMenuCreate(element, 0);
-		menuPath = (uint32_t *) element->cp;
-
-		for (uintptr_t i = 0; i < item.type->fieldCount; i++) {
-			UIMenuAddItem(menu, 0, ((StringOption *) item.type->fields[i].item.options)->string, -1, StyleEnumMenuItemInvoke, (void *) i);
-		}
-
-		UIMenuShow(menu);
-	}
-
-	return 0;
-}
-
-int StyleUnionPanelMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_DESTROY) {
-		InspectorUnsubscribe(element);
-	} else if (message == MSG_PROPERTY_CHANGED) {
-		UIElementDestroyDescendents(element);
-
-		uint32_t *path = (uint32_t *) element->cp;
-		uintptr_t last = 0;
-		for (; path[last] != RF_PATH_TERMINATOR; last++);
-		assert(last && path[last - 1] == 0);
-		path[last - 1] = RF_PATH_TERMINATOR;
-		RfItem item;
-		void *_dp = ResolveDataObject((RfPath *) path, &item);
-		assert(dp == _dp);
-
-		MakeUIState state = { 0 }; 
-		state.s.op = OP_MAKE_UI; 
-		state.index = *(uint32_t *) dp; 
-		state.basePath = element->cp;
-		state.inKeyframe = state.basePath[0] == PATH_IN_KEYFRAME;
-		if (state.inKeyframe) state.basePath++;
-		if (state.index) state.index--;
-		RfField *field = item.type->fields + state.index;
-		RfItem fieldItem = field->item; 
-		uint8_t *pointer = (uint8_t *) dp + field->offset;
-		UIParentPush(element);
-		fieldItem.type->op(&state.s, &fieldItem, pointer); 
-		MakeUI(&state, &fieldItem, pointer); 
-		UIParentPop();
-
-		path[last - 1] = 0; // Restore previous path.
-		RefreshAncestorsUntilInspector(element);
-	}
-
-	return 0;
-}
-
-int StyleArrayPanelMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_DESTROY) {
-		InspectorUnsubscribe(element);
-	} else if (message == MSG_PROPERTY_CHANGED) {
-		UIElementDestroyDescendents(element);
-
-		uint32_t *path = (uint32_t *) element->cp;
-		RfItem item;
-		void *_dp = ResolveDataObject((RfPath *) path, &item);
-		assert(dp == _dp);
-
-		RfArrayHeader *header = *(RfArrayHeader **) dp;
-		UIParentPush(element);
-
-		for (uintptr_t i = 0; header && i < header[-1].length; i++) {
-			MakeUIState state = { 0 }; 
-			state.s.op = OP_MAKE_UI; 
-			state.index = i; 
-			state.basePath = element->cp;
-			state.inKeyframe = state.basePath[0] == PATH_IN_KEYFRAME;
-			if (state.inKeyframe) state.basePath++;
-			RfItem *fieldItem = (RfItem *) item.options; 
-			uint8_t *pointer = (*(uint8_t **) dp) + i * fieldItem->byteCount;
-			state.recurse = true;
-			fieldItem->type->op(&state.s, fieldItem, pointer); 
-			if (state.recurse) MakeUI(&state, fieldItem, pointer); 
-		}
-
-		UIParentPop();
-		RefreshAncestorsUntilInspector(element);
-	}
-
-	return 0;
-}
-
-int StyleArrayAddMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_CLICKED) {
-		ModData mod = { 0 };
-		mod.tag = ModData_array + 1;
-		mod.array.property.path = DuplicatePath((uint32_t *) ((UIElement *) element->cp)->cp);
-		RfItem item;
-		ResolveDataObject((RfPath *) mod.array.property.path, &item);
-		RfItem *elementItem = (RfItem *) item.options;
-		void *temporary = malloc(elementItem->byteCount);
-		memset(temporary, 0, elementItem->byteCount);
-		mod.changeProperty.property.data = SaveToGrowableBuffer(elementItem->type, elementItem->byteCount, elementItem->options, temporary);
-		free(temporary);
-		ModApply(&mod);
-	}
-
-	return 0;
-}
-
-int StyleArrayDeleteMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_CLICKED) {
-		uint32_t *path = (uint32_t *) ((UIElement *) element->cp)->cp;
-		RfItem item;
-		RfArrayHeader *pointer = *(RfArrayHeader **) ResolveDataObject((RfPath *) path, &item);
-
-		if (pointer && pointer[-1].length) {
-			ModData mod = { 0 };
-			mod.tag = ModData_array + 1;
-			mod.array.property.path = DuplicatePath(path);
-			mod.array.isDelete = true;
-			ModApply(&mod);
-		}
-	}
-
-	return 0;
-}
-
-int RemoveOverrideButtonMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_CLICKED) {
-		ModData mod = { 0 };
-		mod.tag = ModData_deleteOverride + 1;
-		mod.deleteOverride.property.path = DuplicatePath(element->cp);
-		ModApply(&mod);
-	} else if (message == MSG_PROPERTY_CHANGED) {
-		if (dp == temporaryOverride) {
-			element->flags &= ~UI_ELEMENT_DISABLED;
-		} else {
-			element->flags |= UI_ELEMENT_DISABLED;
-		}
-
-		UIElementRepaint(element, NULL);
-	} else if (message == UI_MSG_DESTROY) {
-		InspectorUnsubscribe(element);
-	}
-
-	return 0;
-}
-
-void MakeOverrideButton(UIElement *parent, RfState *state, uint32_t last) {
-	if (!((MakeUIState *) state)->inKeyframe) return;
-	UIButton *removeOverride = UIButtonCreate(parent, UI_BUTTON_SMALL, "X", -1);
-	removeOverride->e.messageUser = RemoveOverrideButtonMessage;
-	BuildPathForUI((MakeUIState *) state, last, &removeOverride->e);
-}
-
-void StyleColorMenuItemInvoke(void *cp) {
-	uint32_t newValue = (uint32_t) (uintptr_t) cp;
-
-	ModData mod = { 0 };
-	mod.tag = ModData_changeProperty + 1;
-	mod.changeProperty.property.path = DuplicatePath((uint32_t *) menuPath);
-	mod.changeProperty.property.data = SaveToGrowableBuffer(&StyleColor_Type, sizeof(newValue), NULL, &newValue);
-	ModApply(&mod);
-}
-
-int StyleColorButtonMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_DESTROY) {
-		InspectorUnsubscribe(element);
-	} else if (message == UI_MSG_GET_WIDTH) {
-		return 240 * element->window->scale;
-	} else if (message == MSG_PROPERTY_CHANGED) {
-		RfItem item;
-		void *_dp = ResolveDataObject((RfPath *) element->cp, &item);
-		assert(dp == _dp);
-		UIButton *button = (UIButton *) element;
-		free(button->label);
-
-		RfData key = ColorLookupPointer(*(uint32_t *) dp)->key;
-		button->label = UIStringCopy(key.buffer, key.byteCount);
-
-		UIElementRefresh(element);
-	} else if (message == UI_MSG_CLICKED) {
-		RfItem item;
-		ResolveDataObject((RfPath *) element->cp, &item);
-		UIMenu *menu = UIMenuCreate(element, 0);
-		menuPath = (uint32_t *) element->cp;
-
-		for (uintptr_t i = 0; i < arrlenu(styleSet.colors); i++) {
-			UIMenuAddItem(menu, 0, styleSet.colors[i]->key.buffer, styleSet.colors[i]->key.byteCount, 
-					StyleColorMenuItemInvoke, (void *) (uintptr_t) styleSet.colors[i]->id);
-		}
-
-		UIMenuShow(menu);
-	}
-
-	return 0;
-}
-
-void StyleColorRenameButtonCommand(void *cp) {
-	UIButton *button = (UIButton *) cp;
-	RfItem item;
-	void *dp = ResolveDataObject((RfPath *) button->e.cp, &item);
-	uint32_t id = *(uint32_t *) dp;
-	Color *color = ColorLookupPointer(id);
-	char buffer[128];
-	snprintf(buffer, sizeof(buffer), "%.*s", (int) color->key.byteCount, (char *) color->key.buffer);
-	char *key = strdup(buffer);
-	UIDialogShow(window, 0, "Rename color             \n%t\n%f%b", &key, "Rename");
-	free(color->key.buffer);
-	color->key.buffer = key;
-	color->key.byteCount = strlen(key);
-	free(button->label);
-	button->label = UIStringCopy(key, -1);
-	UIElementRefresh(&button->e);
-	ColorListRefresh();
-}
-
-void StyleColorOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_MAKE_UI) {
-		UIPanel *labelPanel = UIPanelCreate(0, UI_PANEL_HORIZONTAL);
-		UILabelCreate(&labelPanel->e, UI_ELEMENT_H_FILL, ((StringOption *) item->options)->string, -1);
-		MakeOverrideButton(&labelPanel->e, state, RF_PATH_TERMINATOR);
-		UIPanel *row = UIPanelCreate(0, UI_PANEL_HORIZONTAL);
-		UIButton *button = UIButtonCreate(&row->e, UI_BUTTON_DROP_DOWN, "", -1);
-		button->e.messageUser = StyleColorButtonMessage;
-		UIButton *rename = UIButtonCreate(&row->e, 0, "Rename", -1);
-		rename->e.cp = button;
-		rename->invoke = StyleColorRenameButtonCommand;
-		BuildPathForUI((MakeUIState *) state, RF_PATH_TERMINATOR, &button->e);
-	} else if (state->op == OP_GET_PALETTE) {
-		uint32_t color = *(uint32_t *) pointer;
-		int count = hmget(palette, color);
-		count++;
-		hmput(palette, color, count);
-	} else if (state->op == OP_REPLACE_COLOR) {
-		assert(*(uint32_t *) pointer != replaceColorTo);
-
-		if (*(uint32_t *) pointer == replaceColorFrom) {
-			*(uint32_t *) pointer = replaceColorTo;
-		}
-	} else if (state->op == OP_FIND_COLOR_USERS) {
-		uint32_t id = *(uint32_t *) pointer;
-		Style **list = hmget(colorUsers, id);
-		Layer *layer = (Layer *) currentPaletteOpLayer;
-
-		for (uintptr_t i = 0; i < arrlenu(styleSet.styles); i++) {
-			for (uintptr_t j = 0; j < arrlenu(styleSet.styles[i]->layers); j++) {
-				if (styleSet.styles[i]->layers[j] == layer->id) {
-					for (uintptr_t k = 0; k < arrlenu(list); k++) {
-						if (list[k] == styleSet.styles[i]) {
-							goto found;
-						}
-					}
-
-					arrput(list, styleSet.styles[i]);
-					goto found;
-				}
-			}
-		}
-
-		found:;
-		hmput(colorUsers, id, list);
-	} else {
-		RfEndianOp(state, item, pointer);
-	}
-}
-
-void StyleBoolOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_MAKE_UI) {
-		if (((MakeUIState *) state)->inKeyframe) return;
-		UIButton *button = UIButtonCreate(&UIPanelCreate(0, UI_PANEL_HORIZONTAL)->e, 
-			0, ((StringOption *) item->options)->string, -1);
-		button->e.messageUser = StyleBoolMessage;
-		BuildPathForUI((MakeUIState *) state, RF_PATH_TERMINATOR, &button->e);
-	} else {
-		RfEndianOp(state, item, pointer);
-	}
-}
-
-void StyleStringOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_MAKE_UI) {
-		if (((MakeUIState *) state)->inKeyframe) return;
-		UILabelCreate(0, 0, ((StringOption *) item->options)->string, -1);
-		UITextbox *textbox = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-		textbox->e.messageUser = StyleStringMessage;
-		BuildPathForUI((MakeUIState *) state, RF_PATH_TERMINATOR, &textbox->e);
-	} else {
-		RfDataOp(state, item, pointer);
-	}
-}
-
-void Rectangle8Op(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_MAKE_UI) {
-		UILabelCreate(0, 0, ((StringOption *) item->options)->string, -1);
-		UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-
-		MakeOverrideButton(0, state, Rectangle8_l);
-		UITextbox *l = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-		l->e.messageUser = StyleI8Message;
-		BuildPathForUI((MakeUIState *) state, Rectangle8_l, &l->e);
-		MakeOverrideButton(0, state, Rectangle8_r);
-		UITextbox *r = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-		r->e.messageUser = StyleI8Message;
-		BuildPathForUI((MakeUIState *) state, Rectangle8_r, &r->e);
-		MakeOverrideButton(0, state, Rectangle8_t);
-		UITextbox *t = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-		t->e.messageUser = StyleI8Message;
-		BuildPathForUI((MakeUIState *) state, Rectangle8_t, &t->e);
-		MakeOverrideButton(0, state, Rectangle8_b);
-		UITextbox *b = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-		b->e.messageUser = StyleI8Message;
-		BuildPathForUI((MakeUIState *) state, Rectangle8_b, &b->e);
-
-		UIParentPop();
-		((MakeUIState *) state)->recurse = false;
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void Rectangle16Op(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_MAKE_UI) {
-		UILabelCreate(0, 0, ((StringOption *) item->options)->string, -1);
-		UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-
-		MakeOverrideButton(0, state, Rectangle16_l);
-		UITextbox *l = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-		l->e.messageUser = StyleI16Message;
-		BuildPathForUI((MakeUIState *) state, Rectangle16_l, &l->e);
-		MakeOverrideButton(0, state, Rectangle16_r);
-		UITextbox *r = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-		r->e.messageUser = StyleI16Message;
-		BuildPathForUI((MakeUIState *) state, Rectangle16_r, &r->e);
-		MakeOverrideButton(0, state, Rectangle16_t);
-		UITextbox *t = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-		t->e.messageUser = StyleI16Message;
-		BuildPathForUI((MakeUIState *) state, Rectangle16_t, &t->e);
-		MakeOverrideButton(0, state, Rectangle16_b);
-		UITextbox *b = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-		b->e.messageUser = StyleI16Message;
-		BuildPathForUI((MakeUIState *) state, Rectangle16_b, &b->e);
-
-		UIParentPop();
-		((MakeUIState *) state)->recurse = false;
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void PathPointOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_EXPORT) {
-		PathPoint *point = (PathPoint *) pointer;
-		float themePoint[6];
-		themePoint[0] = point->x0; ExportAddPathToOffset((ExportState *) state, PathPoint_x0, 0 * sizeof(float));
-		themePoint[1] = point->y0; ExportAddPathToOffset((ExportState *) state, PathPoint_y0, 1 * sizeof(float));
-		themePoint[2] = point->x1; ExportAddPathToOffset((ExportState *) state, PathPoint_x1, 2 * sizeof(float));
-		themePoint[3] = point->y1; ExportAddPathToOffset((ExportState *) state, PathPoint_y1, 3 * sizeof(float));
-		themePoint[4] = point->x2; ExportAddPathToOffset((ExportState *) state, PathPoint_x2, 4 * sizeof(float));
-		themePoint[5] = point->y2; ExportAddPathToOffset((ExportState *) state, PathPoint_y2, 5 * sizeof(float));
-		state->access(state, themePoint, sizeof(themePoint));
-	} else if (state->op == OP_MAKE_UI) {
-		UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-
-		for (int i = 0; i < 6; i++) {
-			MakeOverrideButton(0, state, PathPoint_x0 + i);
-			UITextbox *textbox = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-			textbox->e.messageUser = StyleFloatMessage;
-			BuildPathForUI((MakeUIState *) state, PathPoint_x0 + i, &textbox->e);
-			if (i == 1 || i == 3) UISpacerCreate(0, 0, 5, 0);
-		}
-
-		UIParentPop();
-		((MakeUIState *) state)->recurse = false;
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void Gaps8Op(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_MAKE_UI) {
-		UILabelCreate(0, 0, ((StringOption *) item->options)->string, -1);
-		UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-
-		MakeOverrideButton(0, state, Gaps8_major);
-		UITextbox *major = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-		major->e.messageUser = StyleI8Message;
-		BuildPathForUI((MakeUIState *) state, Gaps8_major, &major->e);
-		MakeOverrideButton(0, state, Gaps8_minor);
-		UITextbox *minor = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-		minor->e.messageUser = StyleI8Message;
-		BuildPathForUI((MakeUIState *) state, Gaps8_minor, &minor->e);
-		MakeOverrideButton(0, state, Gaps8_wrap);
-		UITextbox *wrap = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-		wrap->e.messageUser = StyleI8Message;
-		BuildPathForUI((MakeUIState *) state, Gaps8_wrap, &wrap->e);
-
-		UIParentPop();
-		((MakeUIState *) state)->recurse = false;
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void Size16Op(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_MAKE_UI) {
-		UILabelCreate(0, 0, ((StringOption *) item->options)->string, -1);
-		UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-
-		MakeOverrideButton(0, state, Size16_width);
-		UITextbox *width = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-		width->e.messageUser = StyleI16Message;
-		BuildPathForUI((MakeUIState *) state, Size16_width, &width->e);
-		MakeOverrideButton(0, state, Size16_height);
-		UITextbox *height = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-		height->e.messageUser = StyleI16Message;
-		BuildPathForUI((MakeUIState *) state, Size16_height, &height->e);
-
-		UIParentPop();
-		((MakeUIState *) state)->recurse = false;
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void Corners8Op(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_MAKE_UI) {
-		UILabelCreate(0, 0, ((StringOption *) item->options)->string, -1);
-		UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-		MakeOverrideButton(0, state, Corners8_tl);
-		UITextbox *l = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-		l->e.messageUser = StyleI8Message;
-		BuildPathForUI((MakeUIState *) state, Corners8_tl, &l->e);
-		MakeOverrideButton(0, state, Corners8_tr);
-		UITextbox *r = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-		r->e.messageUser = StyleI8Message;
-		BuildPathForUI((MakeUIState *) state, Corners8_tr, &r->e);
-		MakeOverrideButton(0, state, Corners8_bl);
-		UITextbox *t = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-		t->e.messageUser = StyleI8Message;
-		BuildPathForUI((MakeUIState *) state, Corners8_bl, &t->e);
-		MakeOverrideButton(0, state, Corners8_br);
-		UITextbox *b = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-		b->e.messageUser = StyleI8Message;
-		BuildPathForUI((MakeUIState *) state, Corners8_br, &b->e);
-		UIParentPop();
-		((MakeUIState *) state)->recurse = false;
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void StyleI8Op(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_MAKE_UI) {
-		UIPanel *labelPanel = UIPanelCreate(0, UI_PANEL_HORIZONTAL);
-		UILabelCreate(&labelPanel->e, UI_ELEMENT_H_FILL, ((StringOption *) item->options)->string, -1);
-		MakeOverrideButton(&labelPanel->e, state, RF_PATH_TERMINATOR);
-		UITextbox *textbox = UITextboxCreate(0, 0);
-		textbox->e.messageUser = StyleI8Message;
-		BuildPathForUI((MakeUIState *) state, RF_PATH_TERMINATOR, &textbox->e);
-	} else {
-		rfI8.op(state, item, pointer);
-	}
-}
-
-void StyleI16Op(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_MAKE_UI) {
-		UIPanel *labelPanel = UIPanelCreate(0, UI_PANEL_HORIZONTAL);
-		UILabelCreate(&labelPanel->e, UI_ELEMENT_H_FILL, ((StringOption *) item->options)->string, -1);
-		MakeOverrideButton(&labelPanel->e, state, RF_PATH_TERMINATOR);
-		UITextbox *textbox = UITextboxCreate(0, 0);
-		textbox->e.messageUser = StyleI16Message;
-		BuildPathForUI((MakeUIState *) state, RF_PATH_TERMINATOR, &textbox->e);
-	} else {
-		rfI16.op(state, item, pointer);
-	}
-}
-
-void StyleFloatOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_MAKE_UI) {
-		UIPanel *labelPanel = UIPanelCreate(0, UI_PANEL_HORIZONTAL);
-		UILabelCreate(&labelPanel->e, UI_ELEMENT_H_FILL, ((StringOption *) item->options)->string, -1);
-		MakeOverrideButton(&labelPanel->e, state, RF_PATH_TERMINATOR);
-		UITextbox *textbox = UITextboxCreate(0, 0);
-		textbox->e.messageUser = StyleFloatMessage;
-		BuildPathForUI((MakeUIState *) state, RF_PATH_TERMINATOR, &textbox->e);
-	} else {
-		rfF32.op(state, item, pointer);
-	}
-}
-
-void StyleUnionOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_MAKE_UI) {
-		MakeUIState *makeUI = (MakeUIState *) state;
-
-		UILabelCreate(0, 0, ((StringOption *) item->options)->string, -1);
-
-		if (!makeUI->inKeyframe) {
-			UIPanel *buttonPanel = UIPanelCreate(0, UI_PANEL_HORIZONTAL);
-			buttonPanel->e.messageUser = StyleUnionButtonPanelMessage;
-
-			for (uintptr_t i = 0; i < item->type->fieldCount; i++) {
-				uint32_t tag = i ? i + 1 : 0;
-				const char *name = i ? ((StringOption *) item->type->fields[i].item.options)->string : "(none)";
-				UIButton *button = UIButtonCreate(&buttonPanel->e, UI_BUTTON_SMALL, name, -1);
-				button->e.cp = (void *) (uintptr_t) tag;
-				button->e.messageUser = StyleUnionButtonMessage;
-			}
-
-			BuildPathForUI(makeUI, 0, &buttonPanel->e);
-		}
-
-		UIPanel *subPanel = UIPanelCreate(0, UI_PANEL_WHITE | UI_PANEL_EXPAND);
-		subPanel->gap = 5;
-		subPanel->e.messageUser = StyleUnionPanelMessage;
-		BuildPathForUI(makeUI, 0, &subPanel->e);
-		makeUI->recurse = false;
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void StyleEnumOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_MAKE_UI) {
-		MakeUIState *makeUI = (MakeUIState *) state;
-
-		if (makeUI->inKeyframe) {
-			return;
-		}
-
-		if (item->type->fieldCount > 5) {
-			UIPanel *panel = UIPanelCreate(0, UI_PANEL_HORIZONTAL);
-			UILabelCreate(&panel->e, UI_ELEMENT_H_FILL, ((StringOption *) item->options)->string, -1);
-			UIButton *button = UIButtonCreate(&panel->e, UI_BUTTON_DROP_DOWN, "", -1);
-			button->e.messageUser = StyleChoiceButtonMessage;
-			BuildPathForUI(makeUI, RF_PATH_TERMINATOR, &button->e);
-		} else {
-			UIPanel *buttonPanel = UIPanelCreate(0, UI_PANEL_HORIZONTAL);
-			UILabelCreate(&buttonPanel->e, 0, ((StringOption *) item->options)->string, -1);
-			buttonPanel->e.messageUser = StyleUnionButtonPanelMessage;
-
-			for (uintptr_t i = 0; i < item->type->fieldCount; i++) {
-				uint32_t tag = i;
-				const char *name = ((StringOption *) item->type->fields[i].item.options)->string;
-				UIButton *button = UIButtonCreate(&buttonPanel->e, UI_BUTTON_SMALL, name, -1);
-				button->e.cp = (void *) (uintptr_t) tag;
-				button->e.messageUser = StyleUnionButtonMessage;
-			}
-
-			BuildPathForUI(makeUI, RF_PATH_TERMINATOR, &buttonPanel->e);
-		}
-	} else {
-		RfEnumOp(state, item, pointer);
-	}
-}
-
-void StyleArrayOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_MAKE_UI) {
-		MakeUIState *makeUI = (MakeUIState *) state;
-
-		UIPanel *buttonPanel = UIPanelCreate(0, UI_PANEL_HORIZONTAL);
-		UILabelCreate(&buttonPanel->e, 0, ((StringOption *) ((RfItem *) item->options)->options)->string, -1);
-
-		UIPanel *subPanel = UIPanelCreate(0, UI_PANEL_WHITE | UI_PANEL_EXPAND);
-		subPanel->gap = 5;
-		subPanel->e.messageUser = StyleArrayPanelMessage;
-		BuildPathForUI(makeUI, RF_PATH_TERMINATOR, &subPanel->e);
-		makeUI->recurse = false;
-
-		if (!makeUI->inKeyframe) {
-			UIButton *addButton = UIButtonCreate(&buttonPanel->e, UI_BUTTON_SMALL, "Add", -1);
-			addButton->e.cp = subPanel;
-			addButton->e.messageUser = StyleArrayAddMessage;
-			UIButton *deleteButton = UIButtonCreate(&buttonPanel->e, UI_BUTTON_SMALL, "Delete", -1);
-			deleteButton->e.cp = subPanel;
-			deleteButton->e.messageUser = StyleArrayDeleteMessage;
-		}
-	} else {
-		RfArrayOp(state, item, pointer);
-	}
-}
-
-void ButtonMoveLayerUp(void *_unused) {
-	uintptr_t index = 0;
-
-	for (uintptr_t i = 0; i < arrlenu(selected.style->layers); i++) {
-		if (selected.style->layers[i] == selected.layer->id) {
-			index = i;
-			break;
-		}
-	}
-
-	if (index <= 1) {
-		return;
-	}
-
-	ModData mod = { 0 };
-	mod.tag = ModData_swapLayers + 1;
-	mod.swapLayers.index = index - 1;
-	ModApply(&mod);
-}
-
-void ButtonMoveLayerDown(void *_unused) {
-	uintptr_t index = 0;
-
-	for (uintptr_t i = 0; i < arrlenu(selected.style->layers); i++) {
-		if (selected.style->layers[i] == selected.layer->id) {
-			index = i;
-			break;
-		}
-	}
-
-	if (index == arrlenu(selected.style->layers) - 1) {
-		return;
-	}
-
-	ModData mod = { 0 };
-	mod.tag = ModData_swapLayers + 1;
-	mod.swapLayers.index = index;
-	ModApply(&mod);
-}
-
-void ForkLayer(void *_unused) {
-	// TODO Undo.
-	// TODO Memory leak of state.data?
-
-	if (!selected.layer) return;
-
-	RfGrowableBuffer state = { 0 };
-	state.data = SaveToGrowableBuffer(&Layer_Type, sizeof(Layer), NULL, selected.layer);
-
-	ModData mod = { 0 };
-	mod.tag = ModData_deleteLayer + 1;
-	bool found = false;
-
-	for (uintptr_t i = 0; i < arrlenu(selected.style->layers); i++) {
-		if (selected.style->layers[i] == selected.layer->id) {
-			mod.deleteLayer.index = i;
-			found = true;
-			break;
-		}
-	}
-
-	assert(found);
-	ModApply(&mod);
-
-	state.s.version = saveFormatVersion;
-	state.data.byteCount -= sizeof(uint32_t);
-	state.s.allocate = RfRealloc;
-	state.s.access = RfReadGrowableBuffer;
-
-	RfItem item = { 0 };
-	item.type = &Layer_Type;
-	item.byteCount = sizeof(Layer);
-	state.s.op = RF_OP_LOAD;
-
-	Layer *layer = calloc(1, sizeof(Layer));
-	item.type->op(&state.s, &item, layer);
-	layer->id = ++styleSet.lastID;
-
-	mod.tag = ModData_addLayer + 1;
-	mod.addLayer.index = mod.deleteLayer.index;
-	mod.addLayer.layer = layer;
-	ModApply(&mod);
-}
-
-void RebuildInspector() {
-	UIElementDestroyDescendents(&panelInspector->e);
-	UIParentPush(&panelInspector->e);
-
-	if (selected.keyframe) {
-		MAKE_UI(Keyframe, selected.keyframe, false);
-		MAKE_UI(Layer, selected.layer, true);
-	} else if (selected.sequence) {
-		MAKE_UI(Sequence, selected.sequence, false);
-	} else if (selected.layer) {
-		char buffer[256];
-		snprintf(buffer, 256, "Layer ID: %ld", selected.layer->id);
-		UILabelCreate(0, 0, buffer, -1);
-
-		uintptr_t layerUsageCount = 0;
-
-		for (uintptr_t i = 0; i < arrlenu(styleSet.styles); i++) {
-			Style *style = styleSet.styles[i];
-
-			for (uintptr_t j = 0; j < arrlenu(style->layers); j++) {
-				if (style->layers[j] == selected.layer->id) {
-					layerUsageCount++;
-				}
-			}
-		}
-
-		if (layerUsageCount > 1) {
-			snprintf(buffer, 256, "This layer is used %ld times.", layerUsageCount);
-			UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH)->gap = 10;
-			UILabelCreate(0, 0, buffer, -1);
-			UIButtonCreate(0, 0, "Fork", -1)->invoke = ForkLayer;
-			UIParentPop();
-		}
-
-		if (selected.layer->isMetricsLayer) {
-			assert(selected.layer->base.tag == LayerBase_metrics + 1);
-			MakeUIState state = { 0 };
-			state.s.op = OP_MAKE_UI;
-			state.index = Layer_base;
-			state.recurse = true;
-			RfItem item = Layer_Type.fields[Layer_base].item;
-			void *pointer = (uint8_t *) selected.layer + Layer_Type.fields[Layer_base].offset;
-			MakeUI(&state, &item, pointer);
-		} else {
-			UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-				UIButtonCreate(0, UI_BUTTON_SMALL, "Move up", -1)->invoke = ButtonMoveLayerUp;
-				UIButtonCreate(0, UI_BUTTON_SMALL, "Move down", -1)->invoke = ButtonMoveLayerDown;
-			UIParentPop();
-
-			MAKE_UI(Layer, selected.layer, false);
-		}
-	}
-
-	UIParentPop();
-	UIElementRefresh(&panelInspector->e);
-	UIElementRepaint(elementCanvas, NULL);
-}
-
-void StyleListRefresh();
-
-void SetSelectedItems(ModContext context) {
-	if (0 == memcmp(&selected, &context, sizeof(context))) {
-		return;
-	}
-
-	arrfree(animatingValues);
-
-	selected = context;
-
-	tableLayers->itemCount = selected.style ? arrlen(selected.style->layers) : 0;
-	UITableResizeColumns(tableLayers);
-	UIElementRefresh(&tableLayers->e);
-
-	tableSequences->itemCount = selected.layer ? arrlen(selected.layer->sequences) : 0;
-	UITableResizeColumns(tableSequences);
-	UIElementRefresh(&tableSequences->e);
-
-	tableKeyframes->itemCount = selected.sequence ? arrlen(selected.sequence->keyframes) : 0;
-	UITableResizeColumns(tableKeyframes);
-	UIElementRefresh(&tableKeyframes->e);
-
-	static uint32_t layerNamePath[] = { Layer_name, RF_PATH_TERMINATOR };
-	if (selected.layer && !selected.sequence) tableLayers->e.cp = layerNamePath;
-	else tableLayers->e.cp = NULL;
-
-	static uint32_t sequencePath[] = { PATH_ANY, RF_PATH_TERMINATOR };
-	if (selected.sequence && !selected.keyframe) tableSequences->e.cp = sequencePath;
-	else tableSequences->e.cp = NULL;
-
-	static uint32_t keyframeProgressPath[] = { Keyframe_progress, RF_PATH_TERMINATOR };
-	if (selected.keyframe) tableSequences->e.cp = keyframeProgressPath;
-	else tableKeyframes->e.cp = NULL;
-
-	StyleListRefresh();
-	RebuildInspector();
-}
-
-// ------------------- Modifications -------------------
-
-void ModPushUndo(ModData *data) {
-	Mod mod = { 0 };
-	mod.context = selected;
-	mod.data = *data;
-
-	if (modApplyUndo) {
-		arrput(redoStack, mod);
-	} else {
-		arrput(undoStack, mod);
-	}
-}
-
-void _ModApply(Mod *mod) {
-	if (memcmp(&mod->context, &selected, sizeof(ModContext))) {
-		SetSelectedItems(mod->context);
-	}
-
-	RfIterator iterator = { 0 };
-	iterator.s.op = RF_OP_ITERATE;
-	RfItem item = { 0 };
-	item.type = &ModData_Type;
-	item.byteCount = sizeof(Mod);
-	item.type->op(&iterator.s, &item, &mod->data);
-
-	RfState state = { 0 };
-	state.op = OP_DO_MOD;
-	iterator.item.type->op(&state, &iterator.item, iterator.pointer);
-
-	UIElementRepaint(elementCanvas, NULL);
-}
-
-void ClearUndoRedo() {
-	RfState state = { 0 };
-	state.op = RF_OP_FREE;
-	state.allocate = RfRealloc;
-	RfItem item = { 0 };
-	item.type = &Mod_Type;
-	item.byteCount = sizeof(Mod);
-
-	for (uintptr_t i = 0; i < arrlenu(redoStack); i++) {
-		item.type->op(&state, &item, redoStack + i);
-	}
-
-	for (uintptr_t i = 0; i < arrlenu(undoStack); i++) {
-		item.type->op(&state, &item, undoStack + i);
-	}
-
-	arrfree(redoStack);
-	arrfree(undoStack);
-}
-
-void ModApply(ModData *data) {
-	arrfree(animatingValues);
-
-	Mod mod = { 0 };
-	mod.context = selected;
-	mod.data = *data;
-
-	modApplyUndo = false;
-	_ModApply(&mod);
-
-	RfState state = { 0 };
-	state.op = RF_OP_FREE;
-	state.allocate = RfRealloc;
-	RfItem item = { 0 };
-	item.type = &Mod_Type;
-	item.byteCount = sizeof(Mod);
-
-	for (uintptr_t i = 0; i < arrlenu(redoStack); i++) {
-		item.type->op(&state, &item, redoStack + i);
-	}
-
-	arrfree(redoStack);
-
-	if (arrlenu(undoStack) >= 2 && data->tag != ModData_deleteOverride + 1) {
-		Mod *undo1 = undoStack + arrlen(undoStack) - 1;
-		Mod *undo2 = undoStack + arrlen(undoStack) - 2;
-
-		if (0 == memcmp(&undo1->context, &undo2->context, sizeof(ModContext))) {
-			if ((undo1->data.tag == ModData_changeProperty + 1 && undo2->data.tag == ModData_changeProperty + 1
-						&& ArePathsEqual(undo1->data.changeProperty.property.path, undo2->data.changeProperty.property.path))
-					|| (undo1->data.tag == ModData_changeProperty + 1 && undo2->data.tag == ModData_deleteOverride + 1
-						&& ArePathsEqual(undo1->data.changeProperty.property.path, undo2->data.deleteOverride.property.path))) {
-				item.type->op(&state, &item, undo1);
-				(void) arrpop(undoStack);
-			}
-		}
-	}
-}
-
-void NotifySubscriptions(UIElement *source, uint32_t *path, void *pointer) {
-	for (uintptr_t i = 0; i < arrlenu(inspectorSubscriptions); i++) {
-		if (inspectorSubscriptions[i] == source) {
-			continue;
-		}
-
-		uint32_t *subscription = (uint32_t *) inspectorSubscriptions[i]->cp;
-
-		if (!subscription) {
-			continue;
-		}
-
-		if (subscription[0] == PATH_ANY || ArePathsEqual(subscription, path)) {
-			UIElementMessage(inspectorSubscriptions[i], MSG_PROPERTY_CHANGED, 0, pointer);
-		}
-	}
-}
-
-void ModChangePropertyOp(RfState *state, RfItem *item, void *pointer) {
-	ModChangeProperty *mod = (ModChangeProperty *) pointer;
-
-	if (state->op == OP_DO_MOD) {
-		// Do we need to use an override?
-		bool isOverride = mod->property.path[0] == PATH_IN_KEYFRAME;
-		void *pointer = NULL;
-
-		if (isOverride) {
-			bool foundOverride = false;
-
-			// Does an override exist?
-			for (uintptr_t i = 0; i < arrlenu(selected.keyframe->properties); i++) {
-				if (ArePathsEqual(mod->property.path, selected.keyframe->properties[i].path)) {
-					// Save the old value for undo.
-					ModData undo = { 0 };
-					undo.tag = ModData_changeProperty + 1;
-					undo.changeProperty.property.path = mod->property.path;
-					undo.changeProperty.property.data = selected.keyframe->properties[i].data;
-					ModPushUndo(&undo);
-
-					// Load the new value.
-					selected.keyframe->properties[i].data = mod->property.data;
-					foundOverride = true;
-					break;
-				}
-			}
-
-			if (!foundOverride) {
-				// Create the undo mod.
-				ModData undo = { 0 };
-				undo.tag = ModData_deleteOverride + 1;
-				undo.deleteOverride.property.path = mod->property.path;
-				ModPushUndo(&undo);
-
-				// Add the override.
-				Property property = mod->property;
-				property.path = DuplicatePath(property.path); 
-				arrput(selected.keyframe->properties, property);
-			}
-
-			// Load the object so we can notify subscribers.
-			RfItem item;
-			pointer = ResolveDataObject((RfPath *) mod->property.path, &item);
-		} else {
-			// Resolve the pointer.
-			RfItem item;
-			pointer = ResolveDataObject((RfPath *) mod->property.path, &item);
-
-			// Save the old value for undo.
-			ModData undo = { 0 };
-			undo.tag = ModData_changeProperty + 1;
-			undo.changeProperty.property.path = mod->property.path;
-			undo.changeProperty.property.data = SaveToGrowableBuffer(item.type, item.byteCount, item.options, pointer);
-			ModPushUndo(&undo);
-
-			// Free the old value.
-			RfGrowableBuffer state = { 0 };
-			state.s.allocate = RfRealloc;
-			state.s.op = RF_OP_FREE;
-			item.type->op(&state.s, &item, pointer);
-
-			// Load the new value.
-			state.s.op = RF_OP_LOAD;
-			state.s.access = RfReadGrowableBuffer;
-			state.data = mod->property.data;
-			state.position = 0;
-			item.type->op(&state.s, &item, pointer);
-			free(state.data.buffer);
-		}
-
-		// Notify subscribed elements in the inspector.
-		NotifySubscriptions(mod->source, mod->property.path, pointer);
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void ModDeleteOverrideOp(RfState *state, RfItem *item, void *pointer) {
-	ModDeleteOverride *mod = (ModDeleteOverride *) pointer;
-
-	if (state->op == OP_DO_MOD) {
-		// Find the override.
-		for (uintptr_t i = 0; i < arrlenu(selected.keyframe->properties); i++) {
-			if (ArePathsEqual(mod->property.path, selected.keyframe->properties[i].path)) {
-				// Save the old value for undo.
-				ModData undo = { 0 };
-				undo.tag = ModData_changeProperty + 1;
-				undo.changeProperty.property.path = mod->property.path;
-				undo.changeProperty.property.data = selected.keyframe->properties[i].data;
-				ModPushUndo(&undo);
-
-				// Delete the override.
-				free(selected.keyframe->properties[i].path);
-				arrdel(selected.keyframe->properties, i);
-
-				// Notify subscribed elements in the inspector.
-				RfItem item;
-				NotifySubscriptions(NULL, mod->property.path, 
-					ResolveDataObject((RfPath *) mod->property.path, &item));
-
-				return;
-			}
-		}
-
-		assert(false);
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void ModArrayOp(RfState *state, RfItem *item, void *pointer) {
-	ModArray *mod = (ModArray *) pointer;
-
-	if (state->op == OP_DO_MOD) {
-		// Resolve the pointer.
-		RfItem item;
-		void **pointer = (void **) ResolveDataObject((RfPath *) mod->property.path, &item);
-		RfItem *elementItem = (RfItem *) item.options;
-
-		if (mod->isDelete) {
-			size_t length = --((RfArrayHeader *) *pointer)[-1].length;
-
-			ModData undo = { 0 };
-			undo.tag = ModData_array + 1;
-			undo.array.property.path = mod->property.path;
-			undo.array.property.data = SaveToGrowableBuffer(elementItem->type, elementItem->byteCount, elementItem->options, 
-				(uint8_t *) *pointer + elementItem->byteCount * length);
-			ModPushUndo(&undo);
-		} else {
-			*pointer = stbds_arrgrowf(*pointer, elementItem->byteCount, 1, 0);
-
-			size_t length = ++((RfArrayHeader *) *pointer)[-1].length;
-			RfGrowableBuffer state = { 0 };
-			state.s.allocate = RfRealloc;
-			state.s.op = RF_OP_LOAD;
-			state.s.access = RfReadGrowableBuffer;
-			state.data = mod->property.data;
-			state.position = 0;
-			uint8_t *p = (uint8_t *) *pointer + elementItem->byteCount * (length - 1);
-			memset(p, 0, elementItem->byteCount);
-			elementItem->type->op(&state.s, elementItem, p);
-			free(state.data.buffer);
-
-			ModData undo = { 0 };
-			undo.tag = ModData_array + 1;
-			undo.array.property.path = mod->property.path;
-			undo.array.isDelete = true;
-			ModPushUndo(&undo);
-		}
-
-		// Notify subscribed elements in the inspector.
-		NotifySubscriptions(NULL, mod->property.path, pointer);
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-// ------------------- Styles -------------------
-
-UITextbox *stylesTextbox;
-UITable *stylesTable;
-UIButton *stylesShowWithSelectedLayer;
-Style **stylesInList;
-
-int StylesTableMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_TABLE_GET_ITEM) {
-		UITableGetItem *m = (UITableGetItem *) dp;
-		Style *style = stylesInList[m->index];
-		m->isSelected = style == selected.style;
-
-		if (!style) {
-			return snprintf(m->buffer, m->bufferBytes, "View all");
-		} else if (style->name.byteCount) {
-			return snprintf(m->buffer, m->bufferBytes, "%.*s", (int) style->name.byteCount, (char *) style->name.buffer);
-		} else {
-			return snprintf(m->buffer, m->bufferBytes, "(default)");
-		}
-	} else if (message == UI_MSG_CLICKED || message == UI_MSG_MOUSE_DRAG) {
-		int index = UITableHitTest(stylesTable, element->window->cursorX, element->window->cursorY);
-		assert(index <= arrlen(stylesInList));
-
-		if (index >= 0) {
-			bool keepExistingLayer = false;
-
-			if (selected.layer && stylesInList[index]) {
-				for (uintptr_t i = 0; i < arrlenu(stylesInList[index]->layers); i++) {
-					if (stylesInList[index]->layers[i] == selected.layer->id) {
-						keepExistingLayer = true;
-						break;
-					}
-				}
-			}
-
-			SetSelectedItems(MOD_CONTEXT(stylesInList[index], keepExistingLayer ? selected.layer 
-						: LayerLookup(stylesInList[index]->layers[0]), NULL, NULL));
-			UIElementRefresh(&stylesTable->e);
-		}
-	}
-
-	return 0;
-}
-
-int StyleCompare(const void *_left, const void *_right) {
-	Style *left = *(Style **) _left; 
-	Style *right = *(Style **) _right; 
-	return StringCompareRaw(left->name.buffer, left->name.byteCount, right->name.buffer, right->name.byteCount);
-}
-
-void StyleListRefresh() {
-	qsort(styleSet.styles, arrlenu(styleSet.styles), sizeof(Style *), StyleCompare);
-
-	arrfree(stylesInList);
-
-	bool showWithSelectedLayer = stylesShowWithSelectedLayer->e.flags & UI_BUTTON_CHECKED;
-
-	if (!showWithSelectedLayer) {
-		arrput(stylesInList, NULL);
-	}
-
-	for (uintptr_t i = 0; i < arrlenu(styleSet.styles); i++) {
-		bool include = false;
-
-		if (showWithSelectedLayer && selected.layer) {
-			for (uintptr_t j = 0; j < arrlenu(styleSet.styles[i]->layers); j++) {
-				if (styleSet.styles[i]->layers[j] == selected.layer->id) {
-					include = true;
-					break;
-				}
-			}
-		} else {
-			include = true;
-		}
-
-		if (include) {
-			arrput(stylesInList, styleSet.styles[i]);
-		}
-	}
-
-	stylesTable->itemCount = arrlenu(stylesInList);
-	UITableResizeColumns(stylesTable);
-	UIElementRefresh(&stylesTable->e);
-
-	UITextboxClear(stylesTextbox, false);
-	UIElementRefresh(&stylesTextbox->e);
-
-	if (selected.style && selected.style->publicStyle) buttonPublicStyle->e.flags |= UI_BUTTON_CHECKED;
-	else buttonPublicStyle->e.flags &= ~UI_BUTTON_CHECKED;
-}
-
-void ButtonCreateStyle(void *_unused) {
-	if (!stylesTextbox->bytes) {
-		return;
-	}
-
-	for (uintptr_t i = 0; i < arrlenu(styleSet.styles); i++) {
-		if (styleSet.styles[i]->name.byteCount == (size_t) stylesTextbox->bytes
-				&& 0 == memcmp(styleSet.styles[i]->name.buffer, stylesTextbox->string, stylesTextbox->bytes)) {
-			return;
-		}
-	}
-
-	Style *style = calloc(1, sizeof(Style));
-	style->name.buffer = malloc((style->name.byteCount = stylesTextbox->bytes));
-	style->id = ++styleSet.lastID;
-	memcpy(style->name.buffer, stylesTextbox->string, style->name.byteCount);
-	Layer *metrics = calloc(1, sizeof(Layer));
-	metrics->id = ++styleSet.lastID;
-	metrics->name.buffer = malloc(16);
-	metrics->name.byteCount = snprintf(metrics->name.buffer, 16, "Metrics");
-	metrics->base.tag = LayerBase_metrics + 1;
-	metrics->isMetricsLayer = true;
-	arrput(style->layers, metrics->id);
-	arrput(styleSet.layers, metrics);
-	arrput(styleSet.styles, style);
-	SetSelectedItems(MOD_CONTEXT(style, NULL, NULL, NULL));
-
-	ClearUndoRedo();
-	StyleListRefresh();
-}
-
-void ButtonDeleteStyle(void *_unused) {
-	if (!selected.style) return;
-	Style *style = selected.style;
-	SetSelectedItems(MOD_CONTEXT(NULL, NULL, NULL, NULL));
-
-	bool found = false;
-
-	for (uintptr_t i = 0; i < arrlenu(styleSet.styles); i++) {
-		if (styleSet.styles[i] == style) {
-			RfState state = { 0 };
-			state.op = RF_OP_FREE;
-			state.allocate = RfRealloc;
-			RfItem item = { 0 };
-			item.type = &Style_Type;
-			item.byteCount = sizeof(Style);
-			item.type->op(&state, &item, style);
-			arrdel(styleSet.styles, i);
-			found = true;
-			break;
-		}
-	}
-
-	assert(found);
-
-	ClearUndoRedo();
-	StyleListRefresh();
-}
-
-void ButtonTogglePublicStyle(void *_unused) {
-	if (!selected.style) {
-		return;
-	}
-
-	selected.style->publicStyle = !selected.style->publicStyle;
-
-	if (selected.style->publicStyle) buttonPublicStyle->e.flags |= UI_BUTTON_CHECKED;
-	else buttonPublicStyle->e.flags &= ~UI_BUTTON_CHECKED;
-}
-
-void ButtonRenameStyle(void *_unused) {
-	if (!stylesTextbox->bytes || !selected.style) {
-		return;
-	}
-
-	selected.style->name.buffer = realloc(selected.style->name.buffer, (selected.style->name.byteCount = stylesTextbox->bytes));
-	memcpy(selected.style->name.buffer, stylesTextbox->string, selected.style->name.byteCount);
-
-	ClearUndoRedo();
-	StyleListRefresh();
-}
-
-void ButtonShowStylesWithSelectedLayer(void *_unused) {
-	stylesShowWithSelectedLayer->e.flags ^= UI_BUTTON_CHECKED;
-	StyleListRefresh();
-}
-
-// ------------------- Constants -------------------
-
-UITextbox *constantsTextbox;
-UITextbox *constantsValue;
-UIButton *constantsScale;
-UITable *constantsTable;
-Constant *selectedConstant;
-
-void ConstantsDialogSetSelected(Constant *constant) {
-	selectedConstant = constant;
-	UIElementRefresh(&constantsTable->e);
-
-	UITextboxClear(constantsValue, false);
-
-	if (constant) {
-		UITextboxReplace(constantsValue, constant->value.buffer, constant->value.byteCount, false);
-		constantsValue->e.flags &= ~UI_ELEMENT_DISABLED;
-		UIElementRefresh(&constantsValue->e);
-
-		if (constant->scale) constantsScale->e.flags |= UI_BUTTON_CHECKED;
-		else constantsScale->e.flags &= ~UI_BUTTON_CHECKED;
-		UIElementRefresh(&constantsScale->e);
-	} else {
-		constantsValue->e.flags |= UI_ELEMENT_DISABLED;
-		UIElementRefresh(&constantsValue->e);
-
-		constantsScale->e.flags &= ~UI_BUTTON_CHECKED;
-		UIElementRefresh(&constantsScale->e);
-	}
-}
-
-int ConstantsTableMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_TABLE_GET_ITEM) {
-		UITableGetItem *m = (UITableGetItem *) dp;
-		Constant *constant = styleSet.constants[m->index];
-		m->isSelected = constant == selectedConstant;
-		return snprintf(m->buffer, m->bufferBytes, "%.*s", (int) constant->key.byteCount, (char *) constant->key.buffer);
-	} else if (message == UI_MSG_CLICKED || message == UI_MSG_MOUSE_DRAG) {
-		int index = UITableHitTest(constantsTable, element->window->cursorX, element->window->cursorY);
-
-		if (index != -1) {
-			ConstantsDialogSetSelected(styleSet.constants[index]);
-		}
-	}
-
-	return 0;
-}
-
-int ConstantsValueMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_VALUE_CHANGED) {
-		if (selectedConstant) {
-			free(selectedConstant->value.buffer);
-			selectedConstant->value.buffer = malloc(constantsValue->bytes);
-			selectedConstant->value.byteCount = constantsValue->bytes;
-			memcpy(selectedConstant->value.buffer, constantsValue->string, constantsValue->bytes);
-		}
-	}
-
-	return 0;
-}
-
-int ConstantsScaleMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_CLICKED) {
-		if (selectedConstant) {
-			element->flags ^= UI_BUTTON_CHECKED;
-			selectedConstant->scale = element->flags & UI_BUTTON_CHECKED;
-		}
-	}
-
-	return 0;
-}
-
-int ConstantCompare(const void *_left, const void *_right) {
-	Constant *left = *(Constant **) _left; 
-	Constant *right = *(Constant **) _right; 
-	return StringCompareRaw(left->key.buffer, left->key.byteCount, right->key.buffer, right->key.byteCount);
-}
-
-void ConstantListRefresh() {
-	constantsTable->itemCount = arrlenu(styleSet.constants);
-	qsort(styleSet.constants, constantsTable->itemCount, sizeof(Constant *), ConstantCompare);
-	UITableResizeColumns(constantsTable);
-	UIElementRefresh(&constantsTable->e);
-
-	UITextboxClear(constantsTextbox, false);
-	UIElementRefresh(&constantsTextbox->e);
-}
-
-void ButtonAddConstant(void *_unused) {
-	if (!constantsTextbox->bytes) {
-		return;
-	}
-
-	for (uintptr_t i = 0; i < arrlenu(styleSet.constants); i++) {
-		if (styleSet.constants[i]->key.byteCount == (size_t) constantsTextbox->bytes
-				&& 0 == memcmp(styleSet.constants[i]->key.buffer, constantsTextbox->string, constantsTextbox->bytes)) {
-			return;
-		}
-	}
-
-	Constant *constant = calloc(1, sizeof(Constant));
-	constant->key.buffer = malloc((constant->key.byteCount = constantsTextbox->bytes));
-	memcpy(constant->key.buffer, constantsTextbox->string, constant->key.byteCount);
-	arrput(styleSet.constants, constant);
-
-	ConstantsDialogSetSelected(constant);
-	ConstantListRefresh();
-}
-
-void ButtonDeleteConstant(void *_unused) {
-	if (!selectedConstant) {
-		return;
-	}
-
-	for (uintptr_t i = 0; i < arrlenu(styleSet.constants); i++) {
-		if (styleSet.constants[i] == selectedConstant) {
-			arrdel(styleSet.constants, i);
-			ConstantsDialogSetSelected(NULL);
-			ConstantListRefresh();
-		}
-	}
-}
-
-// ------------------- Colors -------------------
-
-UITextbox *colorsTextbox;
-UITextbox *colorsValue;
-UIColorPicker *colorsValue2;
-UITable *colorsTable;
-UIElement *colorsPreview;
-Color *selectedColor;
-
-int ColorsPreviewMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_PAINT) {
-		UIDrawRectangle((UIPainter *) dp, element->bounds, selectedColor ? selectedColor->value : 0, 0xFF000000, UI_RECT_1(1));
-	} else if (message == UI_MSG_GET_WIDTH || message == UI_MSG_GET_HEIGHT) {
-		return 20;
-	}
-
-	return 0;
-}
-
-void ColorsDialogSetSelected(Color *color) {
-	selectedColor = color;
-	UIElementRefresh(&colorsTable->e);
-
-	UITextboxClear(colorsValue, false);
-
-	if (color) {
-		char buffer[16];
-		sprintf(buffer, "%.8X", color->value);
-		UITextboxReplace(colorsValue, buffer, -1, false);
-		colorsValue->e.flags &= ~UI_ELEMENT_DISABLED;
-		UIColorToHSV(color->value, &colorsValue2->hue, &colorsValue2->saturation, &colorsValue2->value);
-		colorsValue2->opacity = (selectedColor->value >> 24) / 255.0f;
-		UIElementRefresh(&colorsValue->e);
-		UIElementRefresh(&colorsValue2->e);
-		UIElementRefresh(colorsPreview);
-	} else {
-		colorsValue->e.flags |= UI_ELEMENT_DISABLED;
-		UIElementRefresh(&colorsValue->e);
-	}
-}
-
-int ColorsTableMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_TABLE_GET_ITEM) {
-		UITableGetItem *m = (UITableGetItem *) dp;
-		Color *color = styleSet.colors[m->index];
-		m->isSelected = color == selectedColor;
-
-		if (m->column == 1) {
-			return snprintf(m->buffer, m->bufferBytes, "%.8X", color->value);
-		} else {
-			return snprintf(m->buffer, m->bufferBytes, "%.*s", (int) color->key.byteCount, (char *) color->key.buffer);
-		}
-	} else if (message == UI_MSG_CLICKED || message == UI_MSG_MOUSE_DRAG) {
-		int index = UITableHitTest(colorsTable, element->window->cursorX, element->window->cursorY);
-
-		if (index != -1) {
-			ColorsDialogSetSelected(styleSet.colors[index]);
-		}
-	}
-
-	return 0;
-}
-
-int ColorsValue2Message(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_VALUE_CHANGED) {
-		if (selectedColor) {
-			uint32_t newValue;
-			UIColorToRGB(colorsValue2->hue, colorsValue2->saturation, colorsValue2->value, &newValue);
-			newValue |= (uint32_t) (colorsValue2->opacity * 255.0f) << 24;
-			selectedColor->value = newValue;
-			char buffer[16];
-			sprintf(buffer, "%.8X", selectedColor->value);
-			UITextboxClear(colorsValue, false);
-			UITextboxReplace(colorsValue, buffer, -1, false);
-			UIElementRefresh(&colorsValue->e);
-			UIElementRefresh(colorsPreview);
-			UIElementRepaint(elementCanvas, NULL);
-		}
-	}
-
-	return 0;
-}
-
-int ColorsValueMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_VALUE_CHANGED) {
-		if (selectedColor) {
-			char buffer[16];
-			int length = 15 > colorsValue->bytes ? colorsValue->bytes : 15;
-			memcpy(buffer, colorsValue->string, length);
-			buffer[length] = 0;
-			selectedColor->value = strtol(buffer, NULL, 16);
-			UITableResizeColumns(colorsTable);
-			UIElementRefresh(&colorsTable->e);
-			UIColorToHSV(selectedColor->value, &colorsValue2->hue, &colorsValue2->saturation, &colorsValue2->value);
-			colorsValue2->opacity = (selectedColor->value >> 24) / 255.0f;
-			UIElementRefresh(&colorsValue2->e);
-			UIElementRefresh(colorsPreview);
-			UIElementRepaint(elementCanvas, NULL);
-		}
-	}
-
-	return 0;
-}
-
-int ColorCompare(const void *_left, const void *_right) {
-	Color *left = *(Color **) _left; 
-	Color *right = *(Color **) _right; 
-	return StringCompareRaw(left->key.buffer, left->key.byteCount, right->key.buffer, right->key.byteCount);
-}
-
-void ColorListRefresh() {
-	colorsTable->itemCount = arrlenu(styleSet.colors);
-	qsort(styleSet.colors, colorsTable->itemCount, sizeof(Color *), ColorCompare);
-	UITableResizeColumns(colorsTable);
-	UIElementRefresh(&colorsTable->e);
-
-	UITextboxClear(colorsTextbox, false);
-	UIElementRefresh(&colorsTextbox->e);
-}
-
-void ButtonAddColor(void *_unused) {
-	if (!colorsTextbox->bytes) {
-		return;
-	}
-
-	for (uintptr_t i = 0; i < arrlenu(styleSet.colors); i++) {
-		if (styleSet.colors[i]->key.byteCount == (size_t) colorsTextbox->bytes
-				&& 0 == memcmp(styleSet.colors[i]->key.buffer, colorsTextbox->string, colorsTextbox->bytes)) {
-			return;
-		}
-	}
-
-	Color *color = calloc(1, sizeof(Color));
-	color->id = ++styleSet.lastID;
-	color->key.buffer = malloc((color->key.byteCount = colorsTextbox->bytes));
-	memcpy(color->key.buffer, colorsTextbox->string, color->key.byteCount);
-	arrput(styleSet.colors, color);
-
-	ColorsDialogSetSelected(color);
-	ColorListRefresh();
-}
-
-void ButtonDeleteColor(void *_unused) {
-	if (!selectedColor) {
-		return;
-	}
-
-	for (uintptr_t i = 0; i < arrlenu(styleSet.colors); i++) {
-		if (styleSet.colors[i] == selectedColor) {
-			arrdel(styleSet.colors, i);
-			ColorsDialogSetSelected(NULL);
-			ColorListRefresh();
-		}
-	}
-}
-
-// ------------------- Item lists -------------------
-
-void DoAddItemMod(void ***array, void *item, int index, int undoField) {
-	ModData undo = { 0 };
-	undo.tag = undoField + 1;
-	undo.deleteLayer.index = index;
-	ModPushUndo(&undo);
-	arrins(*array, index, item);
-}
-
-void DoDeleteItemMod(void ***array, int index, int undoField) {
-	ModData undo = { 0 };
-	undo.tag = undoField + 1;
-	undo.addLayer.layer = (Layer *) (*array)[index];
-	undo.addLayer.index = index;
-	ModPushUndo(&undo);
-	arrdel(*array, index);
-}
-
-void ButtonDeleteItem(void *selected, void **array, int field) {
-	if (!selected) return;
-
-	ModData mod = { 0 };
-	mod.tag = field + 1;
-	bool found = false;
-
-	for (uintptr_t i = 0; i < arrlenu(array); i++) {
-		if (array[i] == selected) {
-			mod.deleteLayer.index = i;
-			found = true;
-			break;
-		}
-	}
-
-	assert(found);
-	ModApply(&mod);
-}
-
-void DesignerArrayOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == OP_MAKE_UI) {
-		((MakeUIState *) state)->recurse = false;
-	} else {
-		RfArrayOp(state, item, pointer);
-	}
-}
-
-// ------------------- Fonts -------------------
-
-BasicFontKerningEntry *kerningEntries;
-
-bool ImportFont(char *fileData) {
-	arrfree(kerningEntries);
-	stbtt_fontinfo font = {};
-
-	if (!stbtt_InitFont(&font, (uint8_t *) fileData, 0)) {
-		return false;
-	}
-
-	float scale = stbtt_ScaleForPixelHeight(&font, 100);
-
-	const char *charactersToImport = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
-
-	for (uintptr_t character = 0; charactersToImport[character]; character++) {
-		int glyphIndex = stbtt_FindGlyphIndex(&font, charactersToImport[character]);
-		stbtt_vertex *vertices;
-		int vertexCount = stbtt_GetGlyphShape(&font, glyphIndex, &vertices);
-		// printf("importing glyph for character '%c'\n", charactersToImport[character]);
-
-		int x0, y0, x1, y1, advanceWidth, leftSideBearing;
-		stbtt_GetGlyphHMetrics(&font, glyphIndex, &advanceWidth, &leftSideBearing);
-		stbtt_GetGlyphBox(&font, glyphIndex, &x0, &y0, &x1, &y1);
-		// printf("\t%d, %d, %d, %d, %d, %d\n", x0, x1, y0, y1, advanceWidth, leftSideBearing);
-
-		Style *style = calloc(1, sizeof(Style));
-		style->name.buffer = malloc(32);
-		style->name.byteCount = snprintf(style->name.buffer, 32, "Glyph %c", charactersToImport[character]);
-		style->id = ++styleSet.lastID;
-		Layer *metrics = calloc(1, sizeof(Layer));
-		metrics->id = ++styleSet.lastID;
-		metrics->name.buffer = malloc(16);
-		metrics->name.byteCount = snprintf(metrics->name.buffer, 16, "Metrics");
-		metrics->base.tag = LayerBase_metrics + 1;
-		metrics->base.metrics.minimumSize.width = x0; // Put these somewhere with 16 bits of space...
-		metrics->base.metrics.maximumSize.width = x1;
-		metrics->base.metrics.minimumSize.height = y0;
-		metrics->base.metrics.maximumSize.height = y1;
-		metrics->base.metrics.preferredSize.width = advanceWidth;
-		metrics->isMetricsLayer = true;
-		arrput(style->layers, metrics->id);
-		arrput(styleSet.layers, metrics);
-		arrput(styleSet.styles, style);
-
-		Layer *layer = calloc(1, sizeof(Layer));
-		layer->id = ++styleSet.lastID;
-		layer->name.buffer = malloc(16);
-		layer->name.byteCount = snprintf(layer->name.buffer, 16, "Path");
-		layer->base.tag = LayerBase_path + 1;
-		layer->mode = LAYER_MODE_BACKGROUND;
-		layer->position.r = 100;
-		layer->position.b = 100;
-		arrput(style->layers, layer->id);
-		arrput(styleSet.layers, layer);
-
-		LayerPath *layerPath = &layer->base.path;
-		layerPath->closed = true;
-		layerPath->alpha = 255;
-
-		PathFill fill = {};
-		fill.mode.tag = PathFillMode_solid + 1;
-		fill.paint.tag = Paint_solid + 1;
-		fill.paint.solid.color = 0xFF000000;
-		arrput(layerPath->fills, fill);
-
-		int i = 0;
-
-		while (i < vertexCount) {
-			if (vertices[i].type == STBTT_vmove) {
-				// printf("move to %d, %d\n", vertices[i].x, vertices[i].y);
-
-				if (arrlen(layerPath->points)) {
-					PathPoint *p = &arrlast(layerPath->points);
-					p->x1 = -1e6;
-				}
-
-				PathPoint point = {};
-				point.x0 = vertices[i].x;
-				point.y0 = vertices[i].y;
-				arrput(layerPath->points, point);
-			} else if (vertices[i].type == STBTT_vline) {
-				// printf("line to %d, %d\n", vertices[i].x, vertices[i].y);
-
-				if (!arrlen(layerPath->points)) return false;
-				PathPoint *p = &arrlast(layerPath->points);
-				p->x1 = p->x0;
-				p->y1 = p->y0;
-				p->x2 = vertices[i].x;
-				p->y2 = vertices[i].y;
-				PathPoint point = {};
-				point.x0 = vertices[i].x;
-				point.y0 = vertices[i].y;
-				arrput(layerPath->points, point);
-			} else if (vertices[i].type == STBTT_vcurve) {
-				// printf("curve to %d, %d via %d, %d\n", vertices[i].x, vertices[i].y, vertices[i].cx, vertices[i].cy);
-
-				if (!arrlen(layerPath->points)) return false;
-				PathPoint *p = &arrlast(layerPath->points);
-				p->x1 = (p->x0 + 0.6667f * (vertices[i].cx - p->x0));
-				p->y1 = (p->y0 + 0.6667f * (vertices[i].cy - p->y0));
-				p->x2 = (vertices[i].x + 0.6667f * (vertices[i].cx - vertices[i].x));
-				p->y2 = (vertices[i].y + 0.6667f * (vertices[i].cy - vertices[i].y));
-				PathPoint point = {};
-				point.x0 = vertices[i].x;
-				point.y0 = vertices[i].y;
-				arrput(layerPath->points, point);
-			} else if (vertices[i].type == STBTT_vcubic) {
-				// printf("cubic to %d, %d\n", vertices[i].x, vertices[i].y);
-
-				if (!arrlen(layerPath->points)) return false;
-				PathPoint *p = &arrlast(layerPath->points);
-				p->x1 = vertices[i].cx;
-				p->y1 = vertices[i].cy;
-				p->x2 = vertices[i].cx1;
-				p->y2 = vertices[i].cy1;
-				PathPoint point = {};
-				point.x0 = vertices[i].x;
-				point.y0 = vertices[i].y;
-				arrput(layerPath->points, point);
-			} else {
-				return false;
-			}
-
-			i++;
-		}
-
-		for (uintptr_t i = 0; i < arrlenu(layerPath->points); i++) {
-			layerPath->points[i].x0 *= scale;
-			if (layerPath->points[i].x1 != -1e6) layerPath->points[i].x1 *= scale;
-			layerPath->points[i].x2 *= scale;
-			layerPath->points[i].y0 *= scale;
-			layerPath->points[i].y1 *= scale;
-			layerPath->points[i].y2 *= scale;
-		}
-
-		stbtt_FreeShape(&font, vertices);
-	}
-
-	int ascent, descent, lineGap;
-	stbtt_GetFontVMetrics(&font, &ascent, &descent, &lineGap);
-
-	{
-		Constant *constant = calloc(1, sizeof(Constant));
-		constant->key.buffer = malloc(16);
-		constant->key.byteCount = strlen(strcpy((char *) constant->key.buffer, "ascent"));
-		constant->value.buffer = malloc(16);
-		constant->value.byteCount = snprintf((char *) constant->value.buffer, 16, "%d", ascent);
-		arrput(styleSet.constants, constant);
-	}
-
-	{
-		Constant *constant = calloc(1, sizeof(Constant));
-		constant->key.buffer = malloc(16);
-		constant->key.byteCount = strlen(strcpy((char *) constant->key.buffer, "descent"));
-		constant->value.buffer = malloc(16);
-		constant->value.byteCount = snprintf((char *) constant->value.buffer, 16, "%d", descent);
-		arrput(styleSet.constants, constant);
-	}
-
-	{
-		Constant *constant = calloc(1, sizeof(Constant));
-		constant->key.buffer = malloc(16);
-		constant->key.byteCount = strlen(strcpy((char *) constant->key.buffer, "lineGap"));
-		constant->value.buffer = malloc(16);
-		constant->value.byteCount = snprintf((char *) constant->value.buffer, 16, "%d", lineGap);
-		arrput(styleSet.constants, constant);
-	}
-
-	for (uintptr_t c1 = 0; charactersToImport[c1]; c1++) {
-		for (uintptr_t c2 = 0; charactersToImport[c2]; c2++) {
-			int xAdvance = stbtt_GetGlyphKernAdvance(&font, 
-					stbtt_FindGlyphIndex(&font, charactersToImport[c1]), 
-					stbtt_FindGlyphIndex(&font, charactersToImport[c2]));
-			if (!xAdvance) continue;
-
-			BasicFontKerningEntry entry = { 0 };
-			entry.leftGlyphIndex = charactersToImport[c1];
-			entry.rightGlyphIndex = charactersToImport[c2];
-			entry.xAdvance = xAdvance;
-			arrput(kerningEntries, entry);
-		}
-	}
-
-	return true;
-}
-
-void ExportFont(const char *path) {
-	FILE *f = fopen(path, "wb");
-
-	BasicFontHeader header = { BASIC_FONT_SIGNATURE };
-
-	for (uintptr_t i = 0; i < arrlenu(styleSet.constants); i++) {
-		if (styleSet.constants[i]->key.byteCount == 6 && 0 == memcmp(styleSet.constants[i]->key.buffer, "ascent", 6)) {
-			header.ascender = atoi((char *) styleSet.constants[i]->value.buffer);
-		} else if (styleSet.constants[i]->key.byteCount == 7 && 0 == memcmp(styleSet.constants[i]->key.buffer, "descent", 7)) {
-			header.descender = atoi((char *) styleSet.constants[i]->value.buffer);
-		}
-	}
-
-	for (uintptr_t i = 0; i < arrlenu(styleSet.styles); i++) {
-		Style *style = styleSet.styles[i];
-
-		if (style->name.byteCount > 6 && 0 == memcmp(style->name.buffer, "Glyph ", 6)) {
-			header.glyphCount++;
-		}
-	}
-
-	header.kerningEntries = arrlenu(kerningEntries);
-
-	fwrite(&header, 1, sizeof(BasicFontHeader), f);
-
-	uint32_t offsetToPoints = sizeof(BasicFontHeader) 
-		+ sizeof(BasicFontGlyph) * header.glyphCount 
-		+ sizeof(BasicFontKerningEntry) * header.kerningEntries;
-
-	uint32_t *characters = NULL;
-
-	for (uintptr_t i = 0; i < arrlenu(styleSet.styles); i++) {
-		Style *style = styleSet.styles[i];
-
-		if (style->name.byteCount > 6 && 0 == memcmp(style->name.buffer, "Glyph ", 6)) {
-			BasicFontGlyph glyph = { 0 };
-			assert(arrlenu(style->layers) == 2);
-			glyph.codepoint = ((char *) style->name.buffer)[6];
-			LayerMetrics *metrics = &LayerLookup(style->layers[0])->base.metrics;
-			LayerPath *path = &LayerLookup(style->layers[1])->base.path;
-			glyph.xAdvance = metrics->preferredSize.width;
-			glyph.xOffset = metrics->minimumSize.width;
-			glyph.yOffset = metrics->minimumSize.height;
-			glyph.width = metrics->maximumSize.width - metrics->minimumSize.width;
-			glyph.height = metrics->maximumSize.height - metrics->minimumSize.height;
-			glyph.pointCount = arrlenu(path->points);
-			glyph.offsetToPoints = offsetToPoints;
-			offsetToPoints += glyph.pointCount * 6 * sizeof(float);
-			fwrite(&glyph, 1, sizeof(BasicFontGlyph), f);
-			arrput(characters, glyph.codepoint);
-		}
-	}
-
-	arrfree(characters);
-
-	for (uintptr_t i = 0; i < arrlenu(kerningEntries); i++) {
-		BasicFontKerningEntry *entry = kerningEntries + i;
-		BasicFontKerningEntry copy = *entry;
-
-		for (uintptr_t i = 0; i < arrlenu(characters); i++) {
-			if (entry->leftGlyphIndex == characters[i]) {
-				copy.leftGlyphIndex = i;
-			}
-
-			if (entry->rightGlyphIndex == characters[i]) {
-				copy.rightGlyphIndex = i;
-			}
-		}
-
-		fwrite(&copy, 1, sizeof(BasicFontKerningEntry), f);
-	}
-
-	for (uintptr_t i = 0; i < arrlenu(styleSet.styles); i++) {
-		Style *style = styleSet.styles[i];
-
-		if (style->name.byteCount > 6 && 0 == memcmp(style->name.buffer, "Glyph ", 6)) {
-			LayerPath *path = &LayerLookup(style->layers[1])->base.path;
-			// printf("%ld at %ld, %ld\n", i, ftell(f), arrlenu(path->points));
-			fwrite(path->points, 1, arrlenu(path->points) * 6 * sizeof(float), f);
-		}
-	}
-
-	fclose(f);
-}
-
-void ButtonImportFontConfirm(void *_unused) {
-	if (!importDialog) {
-		return;
-	}
-
-	char path[4096];
-
-	if (importPathTextbox->bytes > (ptrdiff_t) sizeof(path) - 1) {
-		UILabelSetContent(importPathMessage, "Path too long", -1);
-		UIElementRefresh(&importPathMessage->e);
-		return;
-	}
-
-	memcpy(path, importPathTextbox->string, importPathTextbox->bytes);
-	path[importPathTextbox->bytes] = 0;
-
-	char *fileData = LoadFile(path, NULL);
-
-	if (fileData && ImportFont(fileData)) {
-		ConstantListRefresh();
-		ClearUndoRedo();
-		StyleListRefresh();
-		UIElementDestroy(&importDialog->e);
-		importDialog = NULL;
-	} else {
-		UILabelSetContent(importPathMessage, "Invalid or unsupported font file.", -1);
-		UIElementRefresh(&importPathMessage->e);
-	}
-
-	free(fileData);
-}
-
-void ActionImportFont(void *_unused) {
-	if (importDialog) {
-		return;
-	}
-
-	importDialog = UIWindowCreate(window, UI_WINDOW_CENTER_IN_OWNER, "Import Font", 400, 100);
-
-	UIPanelCreate(&importDialog->e, UI_PANEL_GRAY | UI_ELEMENT_PARENT_PUSH | UI_PANEL_EXPAND | UI_PANEL_MEDIUM_SPACING);
-		UILabelCreate(0, 0, "Enter path to font file:", -1);
-		importPathTextbox = UITextboxCreate(0, 0);
-
-		UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-			importPathMessage = UILabelCreate(0, UI_ELEMENT_H_FILL, "", -1);
-			UIButtonCreate(0, 0, "Import", -1)->invoke = ButtonImportFontConfirm;
-		UIParentPop();
-	UIParentPop();
-
-	UIElementFocus(&importPathTextbox->e);
-}
-
-// ------------------- Importing SVG -------------------
-
-void ImportSVGPath(NSVGimage *image, NSVGshape *shape) {
-	// TODO If shape has multiple paths, and has a contour fills,
-	// 	then the contours need to use each path separately.
-
-	if ((~shape->flags & NSVG_FLAGS_VISIBLE) || shape->opacity == 0) {
-		return;
-	}
-
-	ModData mod = { 0 };
-	mod.tag = ModData_addLayer + 1;
-	mod.addLayer.index = arrlen(selected.style->layers);
-
-	Layer *layer = calloc(1, sizeof(Layer));
-	layer->id = ++styleSet.lastID;
-	layer->name.buffer = malloc(16);
-	layer->name.byteCount = snprintf(layer->name.buffer, 16, "path %d", mod.addLayer.index);
-	layer->base.tag = LayerBase_path + 1;
-	layer->mode = LAYER_MODE_BACKGROUND;
-	layer->position.r = 100;
-	layer->position.b = 100;
-
-	LayerPath *layerPath = &layer->base.path;
-	layerPath->evenOdd = shape->fillRule == NSVG_FILLRULE_EVENODD;
-	layerPath->closed = shape->paths->closed; // TODO See comment about multiple paths above.
-	layerPath->alpha = shape->opacity * 255;
-
-	float scale = 100.0f / image->width;
-
-	NSVGpath *path = shape->paths;
-
-	while (path) {
-		for (int i = 0; i < path->npts; i += 3) {
-			PathPoint point = {};
-
-			point.x0 = path->pts[i * 2 + 0] * scale;
-			point.y0 = path->pts[i * 2 + 1] * scale;
-
-			if (i + 1 < path->npts) {
-				point.x1 = path->pts[i * 2 + 2] * scale;
-				point.y1 = path->pts[i * 2 + 3] * scale;
-			} else {
-				point.x1 = path->pts[i * 2 + 0] * scale;
-				point.y1 = path->pts[i * 2 + 1] * scale;
-			}
-
-			if (i + 2 < path->npts) {
-				point.x2 = path->pts[i * 2 + 4] * scale;
-				point.y2 = path->pts[i * 2 + 5] * scale;
-			} else {
-				point.x2 = path->pts[i * 2 + 0] * scale;
-				point.y2 = path->pts[i * 2 + 1] * scale;
-			}
-
-			arrput(layerPath->points, point);
-		}
-
-		if (arrlenu(layerPath->points) && (path->closed || path->next)) {
-			PathPoint point = layerPath->points[0];
-			point.x1 = -1e6;
-			point.x2 = point.y1 = point.y2 = 0;
-			arrput(layerPath->points, point);
-		}
-
-		path = path->next;
-	}
-
-	for (uintptr_t i = 0; i < 2; i++) {
-		NSVGpaint *paint = i ? &shape->stroke : &shape->fill;
-
-		PathFill fill = {};
-
-		if (i) {
-			// TODO Dashes.
-			
-			fill.mode.tag = PathFillMode_contour + 1;
-			fill.mode.contour.internalWidth = shape->strokeWidth * 0.5f + 0.5f; // TODO Floating-point contour widths?
-			fill.mode.contour.externalWidth = shape->strokeWidth * 0.5f;
-			fill.mode.contour.joinMode = shape->strokeLineJoin == NSVG_JOIN_ROUND ? JOIN_MODE_ROUND 
-				: shape->strokeLineJoin == NSVG_JOIN_BEVEL ? JOIN_MODE_BEVEL : JOIN_MODE_MITER;
-			fill.mode.contour.capMode = shape->strokeLineCap == NSVG_CAP_BUTT ? CAP_MODE_FLAT 
-				: shape->strokeLineCap == NSVG_CAP_ROUND ? CAP_MODE_ROUND : CAP_MODE_SQUARE;
-			fill.mode.contour.miterLimit = shape->strokeWidth * shape->miterLimit; 
-		} else {
-			fill.mode.tag = PathFillMode_solid + 1;
-		}
-
-		if (paint->type == NSVG_PAINT_COLOR) {
-			fill.paint.tag = Paint_solid + 1;
-			fill.paint.solid.color = (paint->color & 0xFF00FF00) | ((paint->color & 0xFF) << 16) | ((paint->color & 0xFF0000) >> 16);
-		} else if (paint->type == NSVG_PAINT_LINEAR_GRADIENT) {
-			NSVGgradient *gradient = paint->gradient;
-			fill.paint.tag = Paint_linearGradient + 1;
-
-			fill.paint.linearGradient.transformX = gradient->xform[1] * image->width;
-			fill.paint.linearGradient.transformY = gradient->xform[3] * image->height;
-			fill.paint.linearGradient.transformStart = gradient->xform[5];
-
-			size_t stopCount = gradient->nstops;
-			if (stopCount > 16) stopCount = 16;
-
-			for (int i = 0; i < gradient->nstops; i++) {
-				GradientStop stop = {};
-				uint32_t color = gradient->stops[i].color;
-				stop.color = (color & 0xFF00FF00) | ((color & 0xFF) << 16) | ((color & 0xFF0000) >> 16);
-				stop.position = gradient->stops[i].offset * 100;
-				arrput(fill.paint.linearGradient.stops, stop);
-			}
-
-			if (gradient->spread == NSVG_SPREAD_PAD) {
-				fill.paint.linearGradient.repeat = GRADIENT_REPEAT_CLAMP;
-			} else if (gradient->spread == NSVG_SPREAD_REFLECT) {
-				fill.paint.linearGradient.repeat = GRADIENT_REPEAT_MIRROR;
-			} else if (gradient->spread == NSVG_SPREAD_REPEAT) {
-				fill.paint.linearGradient.repeat = GRADIENT_REPEAT_NORMAL;
-			}
-		} else if (paint->type == NSVG_PAINT_RADIAL_GRADIENT) {
-			NSVGgradient *gradient = paint->gradient;
-			fill.paint.tag = Paint_radialGradient + 1;
-
-			fill.paint.radialGradient.transform0 = gradient->xform[0] * image->width;
-			fill.paint.radialGradient.transform1 = gradient->xform[2] * image->width;
-			fill.paint.radialGradient.transform2 = gradient->xform[4];
-			fill.paint.radialGradient.transform3 = gradient->xform[1] * image->height;
-			fill.paint.radialGradient.transform4 = gradient->xform[3] * image->height;
-			fill.paint.radialGradient.transform5 = gradient->xform[5];
-
-			size_t stopCount = gradient->nstops;
-			if (stopCount > 16) stopCount = 16;
-
-			for (int i = 0; i < gradient->nstops; i++) {
-				GradientStop stop = {};
-				uint32_t color = gradient->stops[i].color;
-				stop.color = (color & 0xFF00FF00) | ((color & 0xFF) << 16) | ((color & 0xFF0000) >> 16);
-				stop.position = gradient->stops[i].offset * 100;
-				arrput(fill.paint.radialGradient.stops, stop);
-			}
-
-			if (gradient->spread == NSVG_SPREAD_PAD) {
-				fill.paint.radialGradient.repeat = GRADIENT_REPEAT_CLAMP;
-			} else if (gradient->spread == NSVG_SPREAD_REFLECT) {
-				fill.paint.radialGradient.repeat = GRADIENT_REPEAT_MIRROR;
-			} else if (gradient->spread == NSVG_SPREAD_REPEAT) {
-				fill.paint.radialGradient.repeat = GRADIENT_REPEAT_NORMAL;
-			}
-		}
-
-		arrput(layerPath->fills, fill);
-	}
-
-	mod.addLayer.layer = layer;
-	ModApply(&mod);
-}
-
-void ImportSVGImage(NSVGimage *image) {
-	previewWidth->position = image->width / 1000.0f;
-	previewHeight->position = image->height / 1000.0f;
-
-	NSVGshape *shape = image->shapes;
-
-	while (shape) {
-		if (shape->paths) {
-			ImportSVGPath(image, shape);
-		}
-
-		shape = shape->next;
-	}
-}
-
-void ButtonImportSVGConfirm(void *_unused) {
-	if (!importDialog) {
-		return;
-	}
-
-	char path[4096];
-
-	if (importPathTextbox->bytes > (ptrdiff_t) sizeof(path) - 1) {
-		UILabelSetContent(importPathMessage, "Path too long", -1);
-		UIElementRefresh(&importPathMessage->e);
-		return;
-	}
-
-	memcpy(path, importPathTextbox->string, importPathTextbox->bytes);
-	path[importPathTextbox->bytes] = 0;
-
-	NSVGimage *image = nsvgParseFromFile(path, "px", 96.0f);
-
-	if (image) {
-		UIElementDestroy(&importDialog->e);
-		importDialog = NULL;
-		ImportSVGImage(image);
-		nsvgDelete(image);
-	} else {
-		UILabelSetContent(importPathMessage, "Invalid or unsupported SVG file.", -1);
-		UIElementRefresh(&importPathMessage->e);
-	}
-}
-
-void ButtonImportSVG(void *_unused) {
-	if (importDialog || !selected.style) {
-		return;
-	}
-
-	importDialog = UIWindowCreate(window, UI_WINDOW_CENTER_IN_OWNER, "Import SVG", 400, 100);
-
-	UIPanelCreate(&importDialog->e, UI_PANEL_GRAY | UI_ELEMENT_PARENT_PUSH | UI_PANEL_EXPAND | UI_PANEL_MEDIUM_SPACING);
-		UILabelCreate(0, 0, "Enter path to SVG file:", -1);
-		importPathTextbox = UITextboxCreate(0, 0);
-
-		UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-			importPathMessage = UILabelCreate(0, UI_ELEMENT_H_FILL, "", -1);
-			UIButtonCreate(0, 0, "Import", -1)->invoke = ButtonImportSVGConfirm;
-		UIParentPop();
-	UIParentPop();
-
-	UIElementFocus(&importPathTextbox->e);
-}
-
-// ------------------- Layers -------------------
-
-void CleanupUnusedLayers(void *_unused) {
-	ClearUndoRedo();
-
-	struct {
-		uint64_t key;
-		bool value;
-	} *usedLayers = NULL;
-
-	for (uintptr_t i = 0; i < arrlenu(styleSet.styles); i++) {
-		for (uintptr_t j = 0; j < arrlenu(styleSet.styles[i]->layers); j++) {
-			hmput(usedLayers, styleSet.styles[i]->layers[j], true);
-		}
-	}
-
-	for (uintptr_t j = 0; j < arrlenu(styleSet.layers); j++) {
-		if (hmget(usedLayers, styleSet.layers[j]->id)) {
-			continue;
-		}
-
-		printf("remove %ld\n", styleSet.layers[j]->id);
-
-		RfState state = { 0 };
-		state.op = RF_OP_FREE;
-		state.allocate = RfRealloc;
-		RfItem item = { 0 };
-		item.type = &Layer_Type;
-		item.byteCount = sizeof(Layer);
-		item.type->op(&state, &item, styleSet.layers[j]);
-
-		arrdel(styleSet.layers, j);
-		j--;
-	}
-}
-
-int TableLayersMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_TABLE_GET_ITEM) {
-		UITableGetItem *m = (UITableGetItem *) dp;
-		Layer *l = LayerLookup(selected.style->layers[m->index]);
-		m->isSelected = selected.layer == l;
-		return snprintf(m->buffer, m->bufferBytes, "%.*s", (int) l->name.byteCount, (char *) l->name.buffer);
-	} else if (message == UI_MSG_CLICKED || message == UI_MSG_MOUSE_DRAG) {
-		int index = UITableHitTest(tableLayers, element->window->cursorX, element->window->cursorY);
-
-		if (index != -1) {
-			SetSelectedItems(MOD_CONTEXT(selected.style, LayerLookup(selected.style->layers[index]), NULL, NULL));
-		}
-	} else if (message == MSG_PROPERTY_CHANGED) {
-		UITableResizeColumns(tableLayers);
-		UIElementRefresh(&tableLayers->e);
-	}
-
-	return 0;
-}
-
-void ModAddLayerOp(RfState *state, RfItem *item, void *pointer) {
-	ModAddLayer *mod = (ModAddLayer *) pointer;
-
-	if (state->op == OP_DO_MOD) {
-		ModData undo = { 0 };
-		undo.tag = ModData_deleteLayer + 1;
-		undo.deleteLayer.index = mod->index;
-		ModPushUndo(&undo);
-
-		arrins(selected.style->layers, mod->index, mod->layer->id);
-		arrput(styleSet.layers, mod->layer);
-
-		SetSelectedItems(MOD_CONTEXT(selected.style, mod->layer, NULL, NULL));
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void ButtonAddBoxLayer(void *_unused) {
-	if (!selected.style) return;
-
-	ModData mod = { 0 };
-	mod.tag = ModData_addLayer + 1;
-	mod.addLayer.index = arrlen(selected.style->layers);
-
-	Layer *layer = calloc(1, sizeof(Layer));
-	layer->id = ++styleSet.lastID;
-	layer->name.buffer = malloc(16);
-	layer->name.byteCount = snprintf(layer->name.buffer, 16, "box %d", mod.addLayer.index);
-	layer->base.tag = LayerBase_box + 1;
-	layer->position.r = 100;
-	layer->position.b = 100;
-
-	mod.addLayer.layer = layer;
-
-	ModApply(&mod);
-}
-
-void ButtonAddTextLayer(void *_unused) {
-	if (!selected.style) return;
-
-	ModData mod = { 0 };
-	mod.tag = ModData_addLayer + 1;
-	mod.addLayer.index = arrlen(selected.style->layers);
-
-	Layer *layer = calloc(1, sizeof(Layer));
-	layer->id = ++styleSet.lastID;
-	layer->name.buffer = malloc(16);
-	layer->name.byteCount = snprintf(layer->name.buffer, 16, "text %d", mod.addLayer.index);
-	layer->base.tag = LayerBase_text + 1;
-	layer->mode = LAYER_MODE_CONTENT;
-
-	mod.addLayer.layer = layer;
-
-	ModApply(&mod);
-}
-
-void ButtonAddPathLayer(void *_unused) {
-	if (!selected.style) return;
-
-	ModData mod = { 0 };
-	mod.tag = ModData_addLayer + 1;
-	mod.addLayer.index = arrlen(selected.style->layers);
-
-	Layer *layer = calloc(1, sizeof(Layer));
-	layer->id = ++styleSet.lastID;
-	layer->name.buffer = malloc(16);
-	layer->name.byteCount = snprintf(layer->name.buffer, 16, "path %d", mod.addLayer.index);
-	layer->base.tag = LayerBase_path + 1;
-	layer->mode = LAYER_MODE_BACKGROUND;
-
-	mod.addLayer.layer = layer;
-
-	ModApply(&mod);
-}
-
-void ButtonAddLayer(void *_unused) {
-	UIMenu *menu = UIMenuCreate(&buttonAddLayer->e, 0);
-	UIMenuAddItem(menu, 0, "Add box...", -1, ButtonAddBoxLayer, NULL);
-	UIMenuAddItem(menu, 0, "Add text...", -1, ButtonAddTextLayer, NULL);
-	UIMenuAddItem(menu, 0, "Add path...", -1, ButtonAddPathLayer, NULL);
-	UIMenuShow(menu);
-}
-
-void ButtonDuplicateLayer(void *_unused) {
-	if (!selected.layer) return;
-	RfGrowableBuffer state = { 0 };
-	state.data = SaveToGrowableBuffer(&Layer_Type, sizeof(Layer), NULL, selected.layer);
-	ModData mod = { 0 };
-	mod.tag = ModData_addLayer + 1;
-	mod.addLayer.index = arrlen(selected.style->layers);
-	Layer *layer = calloc(1, sizeof(Layer));
-	state.s.version = saveFormatVersion;
-	state.data.byteCount -= sizeof(uint32_t);
-	state.s.allocate = RfRealloc;
-	state.s.access = RfReadGrowableBuffer;
-	RfItem item = { 0 };
-	item.type = &Layer_Type;
-	item.byteCount = sizeof(Layer);
-	state.s.op = RF_OP_LOAD;
-	item.type->op(&state.s, &item, layer);
-	layer->id = ++styleSet.lastID;
-	mod.addLayer.layer = layer;
-	ModApply(&mod);
-}
-
-void ButtonAddExistingLayer2(void *_layer) {
-	if (!selected.style) return;
-
-	ModData mod = { 0 };
-	mod.tag = ModData_addLayer + 1;
-	mod.addLayer.index = arrlen(selected.style->layers);
-	mod.addLayer.layer = (Layer *) _layer;
-
-	ModApply(&mod);
-}
-
-void ButtonAddExistingLayer(void *_unused) {
-	UIMenu *menu = UIMenuCreate(&buttonAddExistingLayer->e, 0);
-
-	for (uintptr_t i = 0; i < arrlenu(styleSet.styles); i++) {
-		Style *style = styleSet.styles[i];
-
-		for (uintptr_t j = 0; j < arrlenu(style->layers); j++) {
-			Layer *layer = LayerLookup(style->layers[j]);
-			if (!layer) continue;
-			char name[64];
-			snprintf(name, sizeof(name), "%.*s:%.*s", (int) style->name.byteCount, (char *) style->name.buffer, 
-					(int) layer->name.byteCount, (char *) layer->name.buffer);
-			UIMenuAddItem(menu, 0, name, -1, ButtonAddExistingLayer2, layer);
-		}
-	}
-
-	UIMenuShow(menu);
-}
-
-void ModDeleteLayerOp(RfState *state, RfItem *item, void *pointer) {
-	ModDeleteLayer *mod = (ModDeleteLayer *) pointer;
-
-	if (state->op == OP_DO_MOD) {
-		ModData undo = { 0 };
-		undo.tag = ModData_addLayer + 1;
-		undo.addLayer.layer = LayerLookup(selected.style->layers[mod->index]);
-		undo.addLayer.index = mod->index;
-		ModPushUndo(&undo);
-		arrdel(selected.style->layers, mod->index);
-
-		if ((uintptr_t) mod->index < arrlenu(selected.style->layers)) {
-			SetSelectedItems(MOD_CONTEXT(selected.style, LayerLookup(selected.style->layers[mod->index]), NULL, NULL));
-		} else if (arrlenu(selected.style->layers)) {
-			SetSelectedItems(MOD_CONTEXT(selected.style, LayerLookup(arrlast(selected.style->layers)), NULL, NULL));
-		} else {
-			SetSelectedItems(MOD_CONTEXT(selected.style, NULL, NULL, NULL));
-		}
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void ButtonDeleteLayer(void *_unused) {
-	if (!selected.layer) return;
-
-	ModData mod = { 0 };
-	mod.tag = ModData_deleteLayer + 1;
-	bool found = false;
-
-	for (uintptr_t i = 0; i < arrlenu(selected.style->layers); i++) {
-		if (selected.style->layers[i] == selected.layer->id) {
-			mod.deleteLayer.index = i;
-			found = true;
-			break;
-		}
-	}
-
-	assert(found);
-	ModApply(&mod);
-}
-
-void ButtonDeleteLayerInAllStyles(void *_unused) {
-	if (!selected.layer) {
-		return;
-	}
-
-	ClearUndoRedo();
-	
-	for (uintptr_t i = 0; i < arrlenu(styleSet.styles); i++) {
-		for (uintptr_t j = 0; j < arrlenu(styleSet.styles[i]->layers); j++) {
-			if (styleSet.styles[i]->layers[j] == selected.layer->id) {
-				arrdel(styleSet.styles[i]->layers, j);
-				j--;
-			}
-		}
-	}
-
-	bool found = false;
-
-	for (uintptr_t i = 0; i < arrlenu(styleSet.layers); i++) {
-		if (styleSet.layers[i] == selected.layer) {
-			arrdel(styleSet.layers, i);
-			found = true;
-			break;
-		}
-	}
-
-	assert(found);
-
-	RfState state = { 0 };
-	state.op = RF_OP_FREE;
-	state.allocate = RfRealloc;
-	RfItem item = { 0 };
-	item.type = &Layer_Type;
-	item.byteCount = sizeof(Layer);
-	item.type->op(&state, &item, selected.layer);
-
-	StyleListRefresh();
-	SetSelectedItems(MOD_CONTEXT(selected.style, NULL, NULL, NULL));
-}
-
-void ModSwapLayersOp(RfState *state, RfItem *item, void *pointer) {
-	ModSwapLayers *mod = (ModSwapLayers *) pointer;
-
-	if (state->op == OP_DO_MOD) {
-		assert(mod->index >= 0 && mod->index < arrlen(selected.style->layers) - 1);
-
-		ModData undo = { 0 };
-		undo.tag = ModData_swapLayers + 1;
-		undo.swapLayers.index = mod->index;
-		ModPushUndo(&undo);
-
-		uint64_t temporary = selected.style->layers[mod->index];
-		selected.style->layers[mod->index] = selected.style->layers[mod->index + 1];
-		selected.style->layers[mod->index + 1] = temporary;
-
-		UIElementRefresh(&tableLayers->e);
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-// ------------------- Sequences -------------------
-
-int TableSequencesMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_TABLE_GET_ITEM) {
-		UITableGetItem *m = (UITableGetItem *) dp;
-		Sequence *s = selected.layer->sequences[m->index];
-		m->isSelected = selected.sequence == s;
-		return snprintf(m->buffer, m->bufferBytes, "%s%s%s%s%s%s%s%s%s%s", 
-			((StringOption *) PrimaryState_Type.fields[s->primaryState].item.options)->string,
-			s->flagFocused ? " (focused)" : "",
-			s->flagChecked ? " (checked)" : "",
-			s->flagIndeterminate ? " (indeterminate)" : "",
-			s->flagDefault ? " (default)" : "",
-			s->flagItemFocus ? " (list item focus)" : "",
-			s->flagListFocus ? " (list focus)" : "",
-			s->flagBeforeEnter ? " (before enter)" : "",
-			s->flagAfterExit ? " (after exit)" : "",
-			s->flagSelected ? " (selected)" : "");
-	} else if (message == UI_MSG_CLICKED || message == UI_MSG_MOUSE_DRAG) {
-		int index = UITableHitTest(tableSequences, element->window->cursorX, element->window->cursorY);
-
-		if (index != -1) {
-			SetSelectedItems(MOD_CONTEXT(selected.style, selected.layer, selected.layer->sequences[index], NULL));
-		}
-	} else if (message == MSG_PROPERTY_CHANGED) {
-		UITableResizeColumns(tableSequences);
-		UIElementRefresh(&tableSequences->e);
-	}
-
-	return 0;
-}
-
-void ModAddSequenceOp(RfState *state, RfItem *item, void *pointer) {
-	ModAddSequence *mod = (ModAddSequence *) pointer;
-
-	if (state->op == OP_DO_MOD) {
-		DoAddItemMod((void ***) &selected.layer->sequences, mod->sequence, mod->index, ModData_deleteSequence);
-		SetSelectedItems(MOD_CONTEXT(selected.style, selected.layer, mod->sequence, NULL));
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void ButtonAddSequence(void *_unused) {
-	if (!selected.layer) return;
-	ModData mod = { 0 };
-	mod.tag = ModData_addSequence + 1;
-	mod.addSequence.sequence = calloc(1, sizeof(Sequence));
-	mod.addSequence.index = arrlen(selected.layer->sequences);
-	ModApply(&mod);
-}
-
-void ModDeleteSequenceOp(RfState *state, RfItem *item, void *pointer) {
-	ModDeleteSequence *mod = (ModDeleteSequence *) pointer;
-
-	if (state->op == OP_DO_MOD) {
-		DoDeleteItemMod((void ***) &selected.layer->sequences, mod->index, ModData_addSequence);
-		SetSelectedItems(MOD_CONTEXT(selected.style, selected.layer, NULL, NULL));
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void ButtonDeleteSequence(void *_unused) {
-	if (!selected.sequence) return;
-	ButtonDeleteItem(selected.sequence, (void **) selected.layer->sequences, ModData_deleteSequence);
-}
-
-void ButtonMoveSequenceUp(void *_unused) {
-	if (!selected.sequence) {
-		return;
-	}
-
-	uintptr_t index = 0;
-
-	for (uintptr_t i = 0; i < arrlenu(selected.layer->sequences); i++) {
-		if (selected.layer->sequences[i] == selected.sequence) {
-			index = i;
-			break;
-		}
-	}
-
-	if (index <= 1) {
-		return;
-	}
-
-	ModData mod = { 0 };
-	mod.tag = ModData_swapSequences + 1;
-	mod.swapSequences.index = index - 1;
-	ModApply(&mod);
-}
-
-void ButtonMoveSequenceDown(void *_unused) {
-	if (!selected.sequence) {
-		return;
-	}
-
-	uintptr_t index = 0;
-
-	for (uintptr_t i = 0; i < arrlenu(selected.layer->sequences); i++) {
-		if (selected.layer->sequences[i] == selected.sequence) {
-			index = i;
-			break;
-		}
-	}
-
-	if (index == arrlenu(selected.layer->sequences) - 1) {
-		return;
-	}
-
-	ModData mod = { 0 };
-	mod.tag = ModData_swapSequences + 1;
-	mod.swapSequences.index = index;
-	ModApply(&mod);
-}
-
-void ModSwapSequencesOp(RfState *state, RfItem *item, void *pointer) {
-	ModSwapSequences *mod = (ModSwapSequences *) pointer;
-
-	if (state->op == OP_DO_MOD) {
-		assert(mod->index >= 0 && mod->index < arrlen(selected.layer->sequences) - 1);
-
-		ModData undo = { 0 };
-		undo.tag = ModData_swapSequences + 1;
-		undo.swapSequences.index = mod->index;
-		ModPushUndo(&undo);
-
-		Sequence *temporary = selected.layer->sequences[mod->index];
-		selected.layer->sequences[mod->index] = selected.layer->sequences[mod->index + 1];
-		selected.layer->sequences[mod->index + 1] = temporary;
-
-		UIElementRefresh(&tableSequences->e);
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-// ------------------- Keyframes -------------------
-
-int TableKeyframesMessage(UIElement *element, UIMessage message, int di, void *dp) {
-	if (message == UI_MSG_TABLE_GET_ITEM) {
-		UITableGetItem *m = (UITableGetItem *) dp;
-		Keyframe *k = selected.sequence->keyframes[m->index];
-		m->isSelected = selected.keyframe == k;
-		return snprintf(m->buffer, m->bufferBytes, "%d%%", k->progress);
-	} else if (message == UI_MSG_CLICKED || message == UI_MSG_MOUSE_DRAG) {
-		int index = UITableHitTest(tableKeyframes, element->window->cursorX, element->window->cursorY);
-
-		if (index != -1) {
-			SetSelectedItems(MOD_CONTEXT(selected.style, selected.layer, selected.sequence, selected.sequence->keyframes[index]));
-		}
-	} else if (message == MSG_PROPERTY_CHANGED) {
-		UITableResizeColumns(tableKeyframes);
-		UIElementRefresh(&tableKeyframes->e);
-	}
-
-	return 0;
-}
-
-void ModAddKeyframeOp(RfState *state, RfItem *item, void *pointer) {
-	ModAddKeyframe *mod = (ModAddKeyframe *) pointer;
-
-	if (state->op == OP_DO_MOD) {
-		DoAddItemMod((void ***) &selected.sequence->keyframes, mod->keyframe, mod->index, ModData_deleteKeyframe);
-		SetSelectedItems(MOD_CONTEXT(selected.style, selected.layer, selected.sequence, mod->keyframe));
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void ButtonAddKeyframe(void *_unused) {
-	if (!selected.sequence) return;
-	ModData mod = { 0 };
-	mod.tag = ModData_addKeyframe + 1;
-	mod.addKeyframe.keyframe = calloc(1, sizeof(Keyframe));
-	mod.addKeyframe.index = arrlen(selected.sequence->keyframes);
-	mod.addKeyframe.keyframe->progress = 100;
-	ModApply(&mod);
-}
-
-void ModDeleteKeyframeOp(RfState *state, RfItem *item, void *pointer) {
-	ModDeleteKeyframe *mod = (ModDeleteKeyframe *) pointer;
-
-	if (state->op == OP_DO_MOD) {
-		DoDeleteItemMod((void ***) &selected.sequence->keyframes, mod->index, ModData_addKeyframe);
-		SetSelectedItems(MOD_CONTEXT(selected.style, selected.layer, selected.sequence, NULL));
-	} else {
-		RfStructOp(state, item, pointer);
-	}
-}
-
-void ButtonDeleteKeyframe(void *_unused) {
-	if (!selected.keyframe) return;
-	ButtonDeleteItem(selected.keyframe, (void **) selected.sequence->keyframes, ModData_deleteKeyframe);
-}
-
-// ------------------- Actions -------------------
-
-void ActionSave(void *_unused) {
-	RfData data = SaveToGrowableBuffer(&StyleSet_Type, sizeof(styleSet), NULL, &styleSet);
-	FILE *f = fopen(filePath, "wb");
-	fwrite(&saveFormatVersion, 1, sizeof(uint32_t), f);
-	fwrite(data.buffer, 1, data.byteCount, f);
-	fclose(f);
-	free(data.buffer);
-}
-
-char *LoadFile(const char *inputFileName, size_t *byteCount) {
-	FILE *inputFile = fopen(inputFileName, "rb");
-	
-	if (!inputFile) {
-		return NULL;
-	}
-	
-	fseek(inputFile, 0, SEEK_END);
-	size_t inputFileBytes = ftell(inputFile);
-	fseek(inputFile, 0, SEEK_SET);
-	
-	char *inputBuffer = (char *) malloc(inputFileBytes + 1);
-	size_t inputBytesRead = fread(inputBuffer, 1, inputFileBytes, inputFile);
-	inputBuffer[inputBytesRead] = 0;
-	fclose(inputFile);
-	
-	if (byteCount) *byteCount = inputBytesRead;
-	return inputBuffer;
-}
-
-void ActionLoad(void *_unused) {
-	selectedConstant = NULL;
-
-	RfGrowableBuffer state = { 0 };
-	uint32_t *buffer = (uint32_t *) LoadFile(filePath, &state.data.byteCount);
-
-	if (state.data.byteCount > sizeof(uint32_t)) {
-		state.s.version = *buffer;
-		state.data.buffer = buffer + 1;
-		state.data.byteCount -= sizeof(uint32_t);
-		state.s.allocate = RfRealloc;
-		state.s.access = RfReadGrowableBuffer;
-
-		RfItem item = { 0 };
-		item.type = &StyleSet_Type;
-		item.byteCount = sizeof(styleSet);
-		state.s.op = RF_OP_FREE;
-		item.type->op(&state.s, &item, &styleSet);
-		state.s.op = RF_OP_LOAD;
-		item.type->op(&state.s, &item, &styleSet);
-
-		if (state.s.error) {
-			state.s.op = RF_OP_FREE;
-			state.s.error = false;
-			item.type->op(&state.s, &item, &styleSet);
-		} else {
-			if (state.s.version <= 17) {
-				RfState state = { 0 };
-				state.op = OP_GET_PALETTE;
-				RfItem item = { 0 };
-				item.type = &StyleSet_Type;
-				item.byteCount = sizeof(StyleSet);
-				item.options = NULL;
-				RfBroadcast(&state, &item, &styleSet, true);
-
-				for (uintptr_t i = 0; i < hmlenu(palette); i++) {
-					fprintf(stderr, "%.8X (%d)\n", palette[i].key, palette[i].value);
-
-					char name[16];
-					snprintf(name, sizeof(name), "Color %d", (int) i + 1);
-					Color *color = calloc(1, sizeof(Color));
-					color->key.buffer = strdup(name);
-					color->key.byteCount = strlen(name);
-					color->value = palette[i].key;
-					color->id = i + 1;
-					arrput(styleSet.colors, color);
-
-					state.op = OP_REPLACE_COLOR;
-					replaceColorFrom = color->value;
-					replaceColorTo = color->id;
-					RfBroadcast(&state, &item, &styleSet, true);
-				}
-
-				hmfree(palette);
-			} 
-
-			if (state.s.version <= 18) {
-				char name[16];
-				snprintf(name, sizeof(name), "Uninitialised");
-				Color *color = calloc(1, sizeof(Color));
-				color->key.buffer = strdup(name);
-				color->key.byteCount = strlen(name);
-				color->value = 0;
-				color->id = 0;
-				arrput(styleSet.colors, color);
-			}
-		}
-	}
-
-	free(buffer);
-	SetSelectedItems(MOD_CONTEXT(NULL, NULL, NULL, NULL));
-	StyleListRefresh();
-	ConstantListRefresh();
-	ColorListRefresh();
-	UIElementRepaint(elementCanvas, NULL);
-}
-
-void ActionUndo(void *_unused) {
-	if (!arrlen(undoStack)) return;
-	modApplyUndo = true;
-	Mod mod = arrpop(undoStack);
-	_ModApply(&mod);
-}
-
-void ActionRedo(void *_unused) {
-	if (!arrlen(redoStack)) return;
-	modApplyUndo = false;
-	Mod mod = arrpop(redoStack);
-	_ModApply(&mod);
-}
-
-// ------------------- Initialisation -------------------
-
-#ifdef _WIN32
-int WinMain(void *, void *, char *, int)
-#else
-int main(int argc, char **argv)
-#endif
-{
-	if (argc == 4 && 0 == strcmp(argv[1], "--make-font")) {
-		bool success = ImportFont(LoadFile(argv[2], NULL));
-		if (success) ExportFont(argv[3]);
-		return success ? 0 : 1;
-	}
-
-	if (argc < 3 || argc > 5) {
-		fprintf(stderr, "Usage: %s <source path> <export path> <optional: embed bitmap path> <optional: styles header path>\n", argv[0]);
-		exit(1);
-	}
-
-	filePath = argv[1];
-	exportPath = argv[2];
-	stylesPath = argc >= 4 ? argv[3] : NULL;
-
-	UIInitialise();
-
-	window = UIWindowCreate(0, 0, "Designer", 1600, 900);
-
-	UIWindowRegisterShortcut(window, UI_SHORTCUT(UI_KEYCODE_LETTER('S'), true, false, false, ActionSave, NULL));
-	UIWindowRegisterShortcut(window, UI_SHORTCUT(UI_KEYCODE_LETTER('E'), true, false, false, ActionExport, NULL));
-	UIWindowRegisterShortcut(window, UI_SHORTCUT(UI_KEYCODE_LETTER('Z'), true, false, false, ActionUndo, NULL));
-	UIWindowRegisterShortcut(window, UI_SHORTCUT(UI_KEYCODE_LETTER('Y'), true, false, false, ActionRedo, NULL));
-
-	UISplitPane *splitPane1 = UISplitPaneCreate(&window->e, UI_ELEMENT_V_FILL, 0.25f);
-
-	UIPanel *panel1 = UIPanelCreate(&splitPane1->e, UI_PANEL_EXPAND);
-
-	UIPanelCreate(&panel1->e, UI_PANEL_GRAY | UI_PANEL_HORIZONTAL | UI_PANEL_MEDIUM_SPACING | UI_ELEMENT_PARENT_PUSH);
-		UIButtonCreate(0, 0, "Save", -1)->invoke = ActionSave;
-		UIButtonCreate(0, 0, "Load", -1)->invoke = ActionLoad;
-		UIButtonCreate(0, 0, "Export", -1)->invoke = ActionExport;
-		UIButtonCreate(0, 0, "Export for Designer2", -1)->invoke = ActionExportDesigner2;
-		UIButtonCreate(0, 0, "Import font", -1)->invoke = ActionImportFont;
-	UIParentPop();
-
-	UITabPaneCreate(&panel1->e, UI_ELEMENT_PARENT_PUSH | UI_ELEMENT_V_FILL, "Layers\tAnimation\tConstants\tColors");
-		UISplitPaneCreate(0, UI_SPLIT_PANE_VERTICAL | UI_ELEMENT_PARENT_PUSH, 0.5f);
-			UIPanelCreate(0, UI_PANEL_GRAY | UI_PANEL_EXPAND | UI_PANEL_MEDIUM_SPACING | UI_ELEMENT_PARENT_PUSH);
-				UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-					stylesTextbox = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-					UIButtonCreate(0, UI_BUTTON_SMALL, "Create", -1)->invoke = ButtonCreateStyle;
-					UIButtonCreate(0, UI_BUTTON_SMALL, "Rename", -1)->invoke = ButtonRenameStyle;
-					UIButtonCreate(0, UI_BUTTON_SMALL, "Delete", -1)->invoke = ButtonDeleteStyle;
-					buttonPublicStyle = UIButtonCreate(0, UI_BUTTON_SMALL, "Public", -1);
-					buttonPublicStyle->invoke = ButtonTogglePublicStyle;
-				UIParentPop();
-
-				stylesShowWithSelectedLayer = UIButtonCreate(0, 0, "Show styles with selected layer", -1);
-				stylesShowWithSelectedLayer->invoke = ButtonShowStylesWithSelectedLayer;
-
-				UIButtonCreate(0, 0, "Cleanup unused layers", -1)->invoke = CleanupUnusedLayers;
-
-				stylesTable = UITableCreate(0, UI_ELEMENT_V_FILL, "Name");
-				stylesTable->e.messageUser = StylesTableMessage;
-				UITableResizeColumns(stylesTable);
-			UIParentPop();
-
-			UIPanelCreate(0, UI_PANEL_GRAY | UI_PANEL_EXPAND | UI_ELEMENT_PARENT_PUSH);
-				tableLayers = UITableCreate(0, UI_ELEMENT_V_FILL, "Layers");
-				tableLayers->e.messageUser = TableLayersMessage;
-				UITableResizeColumns(tableLayers);
-				arrput(inspectorSubscriptions, &tableLayers->e);
-
-				UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_PANEL_SMALL_SPACING | UI_ELEMENT_PARENT_PUSH);
-					buttonAddLayer = UIButtonCreate(0, UI_BUTTON_SMALL, "Add...", -1);
-					buttonAddLayer->invoke = ButtonAddLayer;
-					UIButtonCreate(0, UI_BUTTON_SMALL, "Delete", -1)->invoke = ButtonDeleteLayer;
-					buttonAddExistingLayer = UIButtonCreate(0, UI_BUTTON_SMALL, "Use existing", -1);
-					buttonAddExistingLayer->invoke = ButtonAddExistingLayer;
-					UIButtonCreate(0, UI_BUTTON_SMALL, "Duplicate", -1)->invoke = ButtonDuplicateLayer;
-					UIButtonCreate(0, UI_BUTTON_SMALL, "Import SVG", -1)->invoke = ButtonImportSVG;
-				UIParentPop();
-
-				UIButtonCreate(0, UI_BUTTON_SMALL, "Delete layer in all styles", -1)->invoke = ButtonDeleteLayerInAllStyles;
-			UIParentPop();
-		UIParentPop();
-
-		UISplitPaneCreate(0, UI_SPLIT_PANE_VERTICAL | UI_ELEMENT_PARENT_PUSH, 0.5f);
-			UIPanelCreate(0, UI_PANEL_GRAY | UI_PANEL_EXPAND | UI_ELEMENT_PARENT_PUSH);
-				tableSequences = UITableCreate(0, UI_ELEMENT_V_FILL, "Sequences");
-				tableSequences->e.messageUser = TableSequencesMessage;
-				UITableResizeColumns(tableSequences);
-				arrput(inspectorSubscriptions, &tableSequences->e);
-
-				UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_PANEL_SMALL_SPACING | UI_ELEMENT_PARENT_PUSH);
-					UIButtonCreate(0, UI_BUTTON_SMALL, "Add", -1)->invoke = ButtonAddSequence;
-					UIButtonCreate(0, UI_BUTTON_SMALL, "Delete", -1)->invoke = ButtonDeleteSequence;
-					UISpacerCreate(0, 0, 10, 0);
-					UIButtonCreate(0, UI_BUTTON_SMALL, "Move up", -1)->invoke = ButtonMoveSequenceUp;
-					UIButtonCreate(0, UI_BUTTON_SMALL, "Move down", -1)->invoke = ButtonMoveSequenceDown;
-				UIParentPop();
-			UIParentPop();
-
-			UIPanelCreate(0, UI_PANEL_GRAY | UI_PANEL_EXPAND | UI_ELEMENT_PARENT_PUSH);
-				tableKeyframes = UITableCreate(0, UI_ELEMENT_V_FILL, "Keyframes");
-				tableKeyframes->e.messageUser = TableKeyframesMessage;
-				UITableResizeColumns(tableKeyframes);
-				arrput(inspectorSubscriptions, &tableKeyframes->e);
-
-				UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_PANEL_SMALL_SPACING | UI_ELEMENT_PARENT_PUSH);
-					UIButtonCreate(0, UI_BUTTON_SMALL, "Add", -1)->invoke = ButtonAddKeyframe;
-					UIButtonCreate(0, UI_BUTTON_SMALL, "Delete", -1)->invoke = ButtonDeleteKeyframe;
-				UIParentPop();
-			UIParentPop();
-		UIParentPop();
-
-		UIPanelCreate(0, UI_PANEL_GRAY | UI_PANEL_MEDIUM_SPACING | UI_ELEMENT_PARENT_PUSH | UI_PANEL_EXPAND);
-			UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-				constantsTextbox = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-				UIButtonCreate(0, 0, "Add", -1)->invoke = ButtonAddConstant;
-				UIButtonCreate(0, 0, "Delete", -1)->invoke = ButtonDeleteConstant;
-			UIParentPop();
-
-			constantsTable = UITableCreate(0, UI_ELEMENT_V_FILL, "Name");
-			constantsTable->e.messageUser = ConstantsTableMessage;
-			constantsTable->itemCount = arrlenu(styleSet.constants);
-			UITableResizeColumns(constantsTable);
-
-			UIPanelCreate(0, UI_PANEL_WHITE | UI_PANEL_EXPAND | UI_PANEL_MEDIUM_SPACING | UI_ELEMENT_PARENT_PUSH);
-				UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-					UILabelCreate(0, UI_ELEMENT_H_FILL, "Value", -1);
-					constantsScale = UIButtonCreate(0, 0, "Scale", -1);
-					constantsScale->e.messageUser = ConstantsScaleMessage;
-				UIParentPop();
-
-				constantsValue = UITextboxCreate(0, UI_ELEMENT_DISABLED);
-				constantsValue->e.messageUser = ConstantsValueMessage;
-			UIParentPop();
-		UIParentPop();
-
-		UIPanelCreate(0, UI_PANEL_GRAY | UI_PANEL_MEDIUM_SPACING | UI_ELEMENT_PARENT_PUSH | UI_PANEL_EXPAND);
-			UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-				colorsTextbox = UITextboxCreate(0, UI_ELEMENT_H_FILL);
-				UIButtonCreate(0, 0, "Add", -1)->invoke = ButtonAddColor;
-				UIButtonCreate(0, 0, "Delete", -1)->invoke = ButtonDeleteColor;
-			UIParentPop();
-
-			colorsTable = UITableCreate(0, UI_ELEMENT_V_FILL, "Name\tValue");
-			colorsTable->e.messageUser = ColorsTableMessage;
-			colorsTable->itemCount = arrlenu(styleSet.colors);
-			UITableResizeColumns(colorsTable);
-
-			UIPanelCreate(0, UI_PANEL_WHITE | UI_PANEL_EXPAND | UI_PANEL_MEDIUM_SPACING | UI_ELEMENT_PARENT_PUSH);
-				colorsValue2 = UIColorPickerCreate(&UIPanelCreate(0, 0)->e, UI_COLOR_PICKER_HAS_OPACITY);
-				colorsValue2->e.messageUser = ColorsValue2Message;
-				colorsValue = UITextboxCreate(0, UI_ELEMENT_DISABLED);
-				colorsValue->e.messageUser = ColorsValueMessage;
-				colorsPreview = UIElementCreate(sizeof(UIElement), 0, 0, ColorsPreviewMessage, "color preview");
-			UIParentPop();
-		UIParentPop();
-	UIParentPop();
-
-	UISplitPane *splitPane2 = UISplitPaneCreate(&splitPane1->e, 0, 0.7f);
-	UIPanel *panel6 = UIPanelCreate(&splitPane2->e, UI_PANEL_EXPAND);
-	elementCanvas = UIElementCreate(sizeof(UIElement), &panel6->e, UI_ELEMENT_V_FILL, CanvasMessage, "Canvas");
-
-	UIPanelCreate(&panel6->e, UI_PANEL_GRAY | UI_PANEL_EXPAND | UI_ELEMENT_PARENT_PUSH | UI_PANEL_HORIZONTAL | UI_ELEMENT_H_FILL);
-		UIPanelCreate(0, UI_PANEL_EXPAND | UI_PANEL_MEDIUM_SPACING | UI_ELEMENT_PARENT_PUSH | UI_ELEMENT_H_FILL);
-			UILabelCreate(0, 0, "Preview options", -1);
-
-			UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-				UISpacerCreate(0, 0, 20, 0);
-				UILabelCreate(0, 0, "Width: ", -1);
-				UISpacerCreate(0, 0, 5, 0);
-				previewWidth = UISliderCreate(0, 0);
-				previewWidth->position = 0.1f;
-				previewWidth->e.messageUser = PreviewSliderMessage;
-				UISpacerCreate(0, 0, 20, 0);
-				previewFixAspectRatio = UIButtonCreate(0, UI_BUTTON_SMALL, "=", -1);
-				previewFixAspectRatio->invoke = PreviewFixAspectRatioInvoke;
-				UISpacerCreate(0, 0, 20, 0);
-				UILabelCreate(0, 0, "Height:", -1);
-				UISpacerCreate(0, 0, 5, 0);
-				previewHeight = UISliderCreate(0, 0);
-				previewHeight->position = 0.1f;
-				previewHeight->e.messageUser = PreviewSliderMessage;
-			UIParentPop();
-
-			UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-				UISpacerCreate(0, 0, 20, 0);
-				UILabelCreate(0, 0, "Scale: ", -1);
-				UISpacerCreate(0, 0, 5, 0);
-				previewScale = UISliderCreate(0, 0);
-				previewScale->steps = 17;
-				previewScale->e.messageUser = PreviewSliderMessage;
-			UIParentPop();
-
-			UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-				previewTransition = UIButtonCreate(0, 0, "Preview transition", -1);
-				previewTransition->invoke = PreviewTransitionInvoke;
-				UIButtonCreate(0, 0, "Preferred size", -1)->invoke = PreviewPreferredSizeInvoke;
-				previewShowGuides = UIButtonCreate(0, 0, "Show guides", -1);
-				previewShowGuides->invoke = PreviewShowGuidesInvoke;
-				previewShowComputed = UIButtonCreate(0, 0, "Show computed rectangles", -1);
-				previewShowComputed->invoke = PreviewShowComputedInvoke;
-				editPoints = UIButtonCreate(0, 0, "View points", -1);
-				editPoints->invoke = EditPointsInvoke;
-			UIParentPop();
-
-			previewPrimaryStatePanel = UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-				previewPrimaryStateIdle = UIButtonCreate(0, UI_BUTTON_SMALL | UI_BUTTON_CHECKED, "Idle", -1);
-				previewPrimaryStateIdle->invoke = PreviewSetPrimaryState;
-				previewPrimaryStateIdle->e.cp = (void *) PRIMARY_STATE_IDLE;
-				previewPrimaryStateHovered = UIButtonCreate(0, UI_BUTTON_SMALL, "Hovered", -1);
-				previewPrimaryStateHovered->invoke = PreviewSetPrimaryState;
-				previewPrimaryStateHovered->e.cp = (void *) PRIMARY_STATE_HOVERED;
-				previewPrimaryStatePressed = UIButtonCreate(0, UI_BUTTON_SMALL, "Pressed", -1);
-				previewPrimaryStatePressed->invoke = PreviewSetPrimaryState;
-				previewPrimaryStatePressed->e.cp = (void *) PRIMARY_STATE_PRESSED;
-				previewPrimaryStateDisabled = UIButtonCreate(0, UI_BUTTON_SMALL, "Disabled", -1);
-				previewPrimaryStateDisabled->invoke = PreviewSetPrimaryState;
-				previewPrimaryStateDisabled->e.cp = (void *) PRIMARY_STATE_DISABLED;
-				previewPrimaryStateInactive = UIButtonCreate(0, UI_BUTTON_SMALL, "Inactive", -1);
-				previewPrimaryStateInactive->invoke = PreviewSetPrimaryState;
-				previewPrimaryStateInactive->e.cp = (void *) PRIMARY_STATE_INACTIVE;
-			UIParentPop();
-
-			UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-				previewStateFocused = UIButtonCreate(0, UI_BUTTON_SMALL, "Focused", -1);
-				previewStateFocused->e.messageUser = PreviewToggleState;
-				previewStateChecked = UIButtonCreate(0, UI_BUTTON_SMALL, "Checked", -1);
-				previewStateChecked->e.messageUser = PreviewToggleState;
-				previewStateIndeterminate = UIButtonCreate(0, UI_BUTTON_SMALL, "Indeterminate", -1);
-				previewStateIndeterminate->e.messageUser = PreviewToggleState;
-				previewStateDefault = UIButtonCreate(0, UI_BUTTON_SMALL, "Default", -1);
-				previewStateDefault->e.messageUser = PreviewToggleState;
-				previewStateItemFocus = UIButtonCreate(0, UI_BUTTON_SMALL, "Item focus", -1);
-				previewStateItemFocus->e.messageUser = PreviewToggleState;
-				previewStateListFocus = UIButtonCreate(0, UI_BUTTON_SMALL, "List focus", -1);
-				previewStateListFocus->e.messageUser = PreviewToggleState;
-				previewStateSelected = UIButtonCreate(0, UI_BUTTON_SMALL, "Selected", -1);
-				previewStateSelected->e.messageUser = PreviewToggleState;
-				previewStateBeforeEnter = UIButtonCreate(0, UI_BUTTON_SMALL, "Before enter", -1);
-				previewStateBeforeEnter->e.messageUser = PreviewToggleState;
-				previewStateAfterExit = UIButtonCreate(0, UI_BUTTON_SMALL, "After exit", -1);
-				previewStateAfterExit->e.messageUser = PreviewToggleState;
-			UIParentPop();
-		UIParentPop();
-
-		UIPanelCreate(0, UI_PANEL_HORIZONTAL | UI_ELEMENT_PARENT_PUSH);
-			previewBackgroundColor = UIColorPickerCreate(0, 0);
-			previewBackgroundColor->e.messageUser = PreviewChangeBackgroundColor;
-			UIColorToHSV(0xC0C0C0, &previewBackgroundColor->hue, &previewBackgroundColor->saturation, &previewBackgroundColor->value);
-		UIParentPop();
-	UIParentPop();
-
-	panelInspector = UIPanelCreate(&splitPane2->e, UI_PANEL_GRAY | UI_PANEL_EXPAND | UI_PANEL_SCROLL);
-	panelInspector->border = UI_RECT_1(10);
-	panelInspector->gap = 5;
-
-	ActionLoad(NULL);
-
-	return UIMessageLoop();
-}
diff --git a/util/designer/designer.rf b/util/designer/designer.rf
deleted file mode 100644
index 1dd8098..0000000
--- a/util/designer/designer.rf
+++ /dev/null
@@ -1,432 +0,0 @@
-type StyleColor_Type StyleColorOp;
-type StyleString_Type StyleStringOp;
-type StyleBool_Type StyleBoolOp;
-type StyleArray_Type StyleArrayOp;
-type StyleFloat_Type StyleFloatOp;
-type StyleI8_Type StyleI8Op;
-type StyleI16_Type StyleI16Op;
-
-struct Property Property_Type PropertyOp {
-	rfNone "uint32_t *" path;
-	rfData RfData data;
-};
-
-struct Rectangle8 Rectangle8_Type Rectangle8Op {
-	StyleI8_Type int8_t l;
-	StyleI8_Type int8_t r;
-	StyleI8_Type int8_t t;
-	StyleI8_Type int8_t b;
-};
-
-struct Rectangle16 Rectangle16_Type Rectangle16Op {
-	StyleI16_Type int16_t l;
-	StyleI16_Type int16_t r;
-	StyleI16_Type int16_t t;
-	StyleI16_Type int16_t b;
-};
-
-struct Corners8 Corners8_Type Corners8Op {
-	StyleI8_Type int8_t tl;
-	StyleI8_Type int8_t tr;
-	StyleI8_Type int8_t bl;
-	StyleI8_Type int8_t br;
-};
-
-struct Gaps8 Gaps8_Type Gaps8Op {
-	StyleI8_Type int8_t major;
-	StyleI8_Type int8_t minor;
-	StyleI8_Type int8_t wrap;
-};
-
-struct Size16 Size16_Type Size16Op {
-	StyleI16_Type int16_t width;
-	StyleI16_Type int16_t height;
-};
-
-struct PaintSolid PaintSolid_Type PaintSolidOp {
-	StyleColor_Type uint32_t color #StringOption { "Color" };
-};
-
-struct PaintOverwrite PaintOverwrite_Type PaintOverwriteOp {
-	StyleColor_Type uint32_t color #StringOption { "Color" };
-};
-
-struct GradientStop GradientStop_Type GradientStopOp {
-	StyleColor_Type uint32_t color #StringOption { "Color" };
-	StyleI8_Type int8_t position #StringOption { "Position" };
-};
-
-enum GradientRepeat_Type StyleEnumOp {
-	GRADIENT_REPEAT_CLAMP #StringOption { "Clamp" };
-	GRADIENT_REPEAT_NORMAL #StringOption { "Normal" };
-	GRADIENT_REPEAT_MIRROR #StringOption { "Mirror" };
-};
-
-struct PaintLinearGradient PaintLinearGradient_Type PaintLinearGradientOp {
-	Rectangle16_Type Rectangle16 range #StringOption { "Range" } to 6;
-	StyleBool_Type bool useGammaInterpolation #StringOption { "Use gamma-accurate color interpolation" };
-	StyleArray_Type "GradientStop *" stops #RfItem { &GradientStop_Type, sizeof(GradientStop), &(StringOption) { "Stops: " } };
-	StyleBool_Type bool useDithering #StringOption { "Use dithering" } from 4;
-	GradientRepeat_Type uint8_t repeat #StringOption { "Repeat mode" } from 5;
-	StyleFloat_Type float transformX #StringOption { "Transform X" } from 6;
-	StyleFloat_Type float transformY #StringOption { "Transform Y" } from 6;
-	StyleFloat_Type float transformStart #StringOption { "Transform start" } from 6;
-	StyleBool_Type bool useSystemHue #StringOption { "Use system hue" } from 20;
-	StyleBool_Type bool _unused #StringOption { "_unused" } from 21;
-};
-
-struct PaintRadialGradient PaintRadialGradient_Type PaintRadialGradientOp {
-	StyleBool_Type bool useGammaInterpolation #StringOption { "Use gamma-accurate color interpolation" };
-	StyleArray_Type "GradientStop *" stops #RfItem { &GradientStop_Type, sizeof(GradientStop), &(StringOption) { "Stops: " } };
-	GradientRepeat_Type uint8_t repeat #StringOption { "Repeat mode" };
-	StyleFloat_Type float transform0 #StringOption { "Transform X scale" };
-	StyleFloat_Type float transform2 #StringOption { "Transform X offset" };
-	StyleFloat_Type float transform4 #StringOption { "Transform Y scale" };
-	StyleFloat_Type float transform5 #StringOption { "Transform Y offset" };
-	StyleFloat_Type float transform1 #StringOption { "Transform X skew" } from 8;
-	StyleFloat_Type float transform3 #StringOption { "Transform Y skew" } from 8;
-};
-
-struct Paint Paint_Type StyleUnionOp {
-	rfU32 uint32_t tag;
-	PaintSolid_Type PaintSolid solid #StringOption { "Solid" };
-	PaintLinearGradient_Type PaintLinearGradient linearGradient #StringOption { "Linear gradient" };
-	PaintOverwrite_Type PaintOverwrite overwrite #StringOption { "Overwrite" } from 2;
-	PaintRadialGradient_Type PaintRadialGradient radialGradient #StringOption { "Radial gradient" } from 7;
-};
-
-struct LayerBox LayerBox_Type LayerBoxOp {
-	Rectangle8_Type Rectangle8 borders #StringOption { "Borders" };
-	Corners8_Type Corners8 corners #StringOption { "Corners" };
-	Paint_Type Paint mainPaint #StringOption { "Main paint" };
-	Paint_Type Paint borderPaint #StringOption { "Border paint" };
-	StyleBool_Type bool blurred #StringOption { "Blurred" };
-	StyleBool_Type bool autoCorners #StringOption { "Auto corners" };
-	StyleBool_Type bool autoBorders #StringOption { "Auto borders" };
-	StyleBool_Type bool shadowHiding #StringOption { "Shadow hiding" };
-	StyleBool_Type bool shadowCut #StringOption { "Shadow cut (obsolete)" } from 13;
-};
-
-enum ClipMode_Type StyleEnumOp {
-	CLIP_MODE_ENABLED #StringOption { "Enabled" };
-	CLIP_MODE_DISABLED #StringOption { "Disabled" };
-};
-
-enum Cursor_Type StyleEnumOp {
-	CURSOR_NORMAL #StringOption { "Normal" };
-	CURSOR_TEXT #StringOption { "Text" };
-	CURSOR_RESIZE_VERTICAL #StringOption { "Resize vertical" };
-	CURSOR_RESIZE_HORIZONTAL #StringOption { "Resize horizontal" };
-	CURSOR_RESIZE_DIAGONAL_1 #StringOption { "Diagonal 1" };
-	CURSOR_RESIZE_DIAGONAL_2 #StringOption { "Diagonal 2" };
-	CURSOR_SPLIT_VERTICAL #StringOption { "Split vertical" };
-	CURSOR_SPLIT_HORIZONTAL #StringOption { "Split horizontal" };
-	CURSOR_HAND_HOVER #StringOption { "Hand hover" };
-	CURSOR_HAND_DRAG #StringOption { "Hand drag" };
-	CURSOR_HAND_POINT #StringOption { "Hand point" };
-	CURSOR_SCROLL_UP_LEFT #StringOption { "Scroll up-left" };
-	CURSOR_SCROLL_UP #StringOption { "Scroll up" };
-	CURSOR_SCROLL_UP_RIGHT #StringOption { "Scroll up-right" };
-	CURSOR_SCROLL_LEFT #StringOption { "Scroll left" };
-	CURSOR_SCROLL_CENTER #StringOption { "Scroll center" };
-	CURSOR_SCROLL_RIGHT #StringOption { "Scroll right" };
-	CURSOR_SCROLL_DOWN_LEFT #StringOption { "Scroll down-left" };
-	CURSOR_SCROLL_DOWN #StringOption { "Scroll down" };
-	CURSOR_SCROLL_DOWN_RIGHT #StringOption { "Scroll down-right" };
-	CURSOR_SELECT_LINES #StringOption { "Select lines" };
-	CURSOR_DROP_TEXT #StringOption { "Drop text" };
-	CURSOR_CROSS_HAIR_PICK #StringOption { "Cross hair pick" };
-	CURSOR_CROSS_HAIR_RESIZE #StringOption { "Cross hair resize" };
-	CURSOR_MOVE_HOVER #StringOption { "Move hover" };
-	CURSOR_MOVE_DRAG #StringOption { "Move drag" };
-	CURSOR_ROTATE_HOVER #StringOption { "Rotate hover" };
-	CURSOR_ROTATE_DRAG #StringOption { "Rotate drag" };
-	CURSOR_BLANK #StringOption { "Blank" };
-};
-
-enum Transition_Type StyleEnumOp {
-	TRANSITION_NONE #StringOption { "None" };
-	TRANSITION_SLIDE_UP #StringOption { "Slide up" };
-	TRANSITION_SLIDE_DOWN #StringOption { "Slide down" };
-	TRANSITION_COVER_UP #StringOption { "Cover up" };
-	TRANSITION_COVER_DOWN #StringOption { "Cover down" };
-	TRANSITION_SQUISH_UP #StringOption { "Squish up" };
-	TRANSITION_SQUISH_DOWN #StringOption { "Squish down" };
-	TRANSITION_REVEAL_UP #StringOption { "Reveal up" };
-	TRANSITION_REVEAL_DOWN #StringOption { "Reveal down" };
-	TRANSITION_ZOOM_OUT #StringOption { "Zoom out" };
-	TRANSITION_ZOOM_IN #StringOption { "Zoom in" };
-	TRANSITION_ZOOM_OUT_LIGHT #StringOption { "Zoom out (light)" };
-	TRANSITION_ZOOM_IN_LIGHT #StringOption { "Zoom in (light)" };
-	TRANSITION_FADE_OUT #StringOption { "Fade out" };
-	TRANSITION_FADE_IN #StringOption { "Fade in" };
-};
-
-enum Align_Type StyleEnumOp {
-	ALIGN_START #StringOption { "Start" };
-	ALIGN_CENTER #StringOption { "Center" };
-	ALIGN_END #StringOption { "End" };
-};
-
-enum FontFamily_Type StyleEnumOp {
-	FONT_FAMILY_SANS #StringOption { "Sans" };
-	FONT_FAMILY_SERIF #StringOption { "Serif" };
-	FONT_FAMILY_MONO #StringOption { "Mono" };
-};
-
-struct LayerMetrics LayerMetrics_Type LayerMetricsOp {
-	Rectangle8_Type Rectangle8 insets #StringOption { "Insets" };
-	ClipMode_Type uint32_t clipEnabled #StringOption { "Clipping: " };
-	Rectangle8_Type Rectangle8 clipInsets #StringOption { "Clip insets" };
-	Size16_Type Size16 preferredSize #StringOption { "Preferred size" };
-	Size16_Type Size16 minimumSize #StringOption { "Minimum size" };
-	Size16_Type Size16 maximumSize #StringOption { "Maximum size" };
-	Gaps8_Type Gaps8 gaps #StringOption { "Gaps" };
-	Cursor_Type uint32_t cursor #StringOption { "Cursor" };
-	Transition_Type uint32_t entranceTransition #StringOption { "Entrance transition" } to 21;
-	StyleI16_Type int16_t entranceDuration #StringOption { "Entrance duration (ms)" } to 21;
-	Transition_Type uint32_t exitTransition #StringOption { "Exit transition" } to 21;
-	StyleI16_Type int16_t exitDuration #StringOption { "Exit duration (ms)" } to 21;
-	Rectangle8_Type Rectangle8 globalOffset #StringOption { "Global offset" };
-	Align_Type uint32_t textVerticalAlign #StringOption { "Text vertical align: " };
-	Align_Type uint32_t textHorizontalAlign #StringOption { "Text horizontal align: " };
-	StyleI8_Type int8_t textSize #StringOption { "Font size" };
-	FontFamily_Type uint32_t fontFamily #StringOption { "Font family: " };
-	StyleI8_Type int8_t fontWeight #StringOption { "Font weight" };
-	StyleBool_Type bool italic #StringOption { "Italic" };
-	StyleBool_Type bool wrapText #StringOption { "Wrap text" };
-	StyleBool_Type bool ellipsis #StringOption { "Ellipsis" };
-	StyleColor_Type uint32_t textColor #StringOption { "Text color" };
-	StyleColor_Type uint32_t selectedBackground #StringOption { "Selected background" };
-	StyleColor_Type uint32_t selectedText #StringOption { "Selected text" };
-	StyleI8_Type int8_t iconSize #StringOption { "Icon size" };
-	StyleColor_Type uint32_t iconColor #StringOption { "Icon color" };
-	StyleString_Type RfData inheritText #StringOption { "Inherit text properties from:" } from 11;
-};
-
-struct LayerText LayerText_Type LayerTextOp {
-	StyleColor_Type uint32_t color #StringOption { "Color" };
-	StyleI8_Type int8_t blur #StringOption { "Blur radius" };
-};
-
-struct PathPoint PathPoint_Type PathPointOp {
-	StyleFloat_Type float x0 #StringOption { "x0" };
-	StyleFloat_Type float y0 #StringOption { "y0" };
-	StyleFloat_Type float x1 #StringOption { "x1" };
-	StyleFloat_Type float y1 #StringOption { "y1" };
-	StyleFloat_Type float x2 #StringOption { "x2" };
-	StyleFloat_Type float y2 #StringOption { "y2" };
-};
-
-struct PathFillSolid PathFillSolid_Type PathFillSolidOp {
-};
-
-enum JoinMode StyleEnumOp {
-	JOIN_MODE_MITER #StringOption { "Miter" };
-	JOIN_MODE_ROUND #StringOption { "Round" };
-	JOIN_MODE_BEVEL #StringOption { "Bevel" };
-};
-
-enum CapMode StyleEnumOp {
-	CAP_MODE_FLAT #StringOption { "Flat" };
-	CAP_MODE_ROUND #StringOption { "Round" };
-	CAP_MODE_SQUARE #StringOption { "Square" };
-};
-
-struct PathFillContour PathFillContour_Type PathFillContourOp {
-	StyleI8_Type int8_t internalWidth #StringOption { "Internal width" };
-	StyleI8_Type int8_t externalWidth #StringOption { "External width" };
-	StyleFloat_Type float miterLimit #StringOption { "Miter limit" };
-	JoinMode uint8_t joinMode #StringOption { "Join mode: " };
-	CapMode uint8_t capMode #StringOption { "Cap mode: " };
-	StyleBool_Type bool integerWidthsOnly #StringOption { "Integer widths only when scaling" } from 14;
-};
-
-struct PathFillDash PathFillDash_Type PathFillDashOp {
-	PathFillContour_Type PathFillContour contour;
-	StyleI8_Type int8_t length #StringOption { "Length" };
-	StyleI8_Type int8_t gap #StringOption { "Gap" };
-};
-
-struct PathFillDashed PathFillDashed_Type PathFillDashedOp {
-	StyleArray_Type "PathFillDash *" dashes #RfItem { &PathFillDash_Type, sizeof(PathFillDash), &(StringOption) { "Dashes: " } };
-};
-
-struct PathFillMode PathFillMode_Type StyleUnionOp {
-	rfU32 uint32_t tag;
-	PathFillSolid_Type PathFillSolid solid #StringOption { "Solid" };
-	PathFillContour_Type PathFillContour contour #StringOption { "Contour" };
-	PathFillDashed_Type PathFillDashed dashed #StringOption { "Dashed" };
-};
-
-struct PathFill PathFill_Type PathFillOp {
-	PathFillMode_Type PathFillMode mode #StringOption { "Mode" };
-	Paint_Type Paint paint #StringOption { "Paint" };
-};
-
-struct LayerPath LayerPath_Type LayerPathOp {
-	StyleBool_Type bool evenOdd #StringOption { "Even-odd fill rule" };
-	StyleBool_Type bool closed #StringOption { "Closed path" };
-	StyleI16_Type int16_t alpha #StringOption { "Alpha" };
-	StyleArray_Type "PathPoint *" points #RfItem { &PathPoint_Type, sizeof(PathPoint), &(StringOption) { "Points: " } };
-	StyleArray_Type "PathFill *" fills #RfItem { &PathFill_Type, sizeof(PathFill), &(StringOption) { "Fills: " } };
-};
-
-union LayerBase LayerBase_Type RfUnionOp {
-	LayerBox_Type LayerBox box;
-	LayerMetrics_Type LayerMetrics metrics;
-	rfNone bool _removed0;
-	rfNone bool _removed1;
-	LayerText_Type LayerText text;
-	LayerPath_Type LayerPath path;
-};
-
-type DesignerArray_Type DesignerArrayOp;
-
-struct Keyframe Keyframe_Type RfStructOp {
-	StyleI8_Type int8_t progress #StringOption { "Progress (%)" };
-	DesignerArray_Type "Property *" properties #RfItem { &Property_Type, sizeof(Property), NULL };
-};
-
-enum PrimaryState_Type StyleEnumOp {
-	PRIMARY_STATE_ANY #StringOption { "Any" };
-	PRIMARY_STATE_IDLE #StringOption { "Idle" };
-	PRIMARY_STATE_HOVERED #StringOption { "Hovered" };
-	PRIMARY_STATE_PRESSED #StringOption { "Pressed" };
-	PRIMARY_STATE_DISABLED #StringOption { "Disabled" };
-	PRIMARY_STATE_INACTIVE #StringOption { "Inactive" };
-};
-
-struct Sequence Sequence_Type RfStructOp {
-	DesignerArray_Type "Keyframe **" keyframes #RfItem { &rfObject, sizeof(Keyframe *), &(RfItem) { &Keyframe_Type, sizeof(Keyframe), NULL } };
-	StyleI16_Type int16_t duration #StringOption { "Duration (ms)" };
-	PrimaryState_Type uint32_t primaryState #StringOption { "Primary state" };
-	StyleBool_Type bool flagFocused #StringOption { "Focused?" };
-	StyleBool_Type bool flagChecked #StringOption { "Checked?" };
-	StyleBool_Type bool flagIndeterminate #StringOption { "Indeterminate?" };
-	StyleBool_Type bool flagDefault #StringOption { "Default?" };
-	StyleBool_Type bool flagItemFocus #StringOption { "List item focus?" };
-	StyleBool_Type bool flagListFocus #StringOption { "List focus?" };
-	StyleBool_Type bool flagSelected #StringOption { "Selected?" };
-	StyleBool_Type bool flagBeforeEnter #StringOption { "Before enter?" };
-	StyleBool_Type bool flagAfterExit #StringOption { "After exit?" };
-};
-
-enum LayerMode_Type StyleEnumOp {
-	LAYER_MODE_BACKGROUND #StringOption { "Background" };
-	LAYER_MODE_SHADOW #StringOption { "Shadow" };
-	LAYER_MODE_CONTENT #StringOption { "Content" };
-	LAYER_MODE_OVERLAY #StringOption { "Overlay" };
-};
-
-struct Layer Layer_Type LayerOp {
-	rfU64 uint64_t id;
-	rfNone uint32_t exportOffset;
-
-	DesignerArray_Type "Sequence **" sequences #RfItem { &rfObject, sizeof(Sequence *), &(RfItem) { &Sequence_Type, sizeof(Sequence), NULL } };
-	StyleString_Type RfData name #StringOption { "Layer name" };
-	rfBool bool isMetricsLayer;
-
-	Rectangle8_Type Rectangle8 offset #StringOption { "Offset (dpx)" };
-	Rectangle8_Type Rectangle8 position #StringOption { "Position (%)" };
-	LayerMode_Type uint8_t mode #StringOption { "Mode: " };
-	LayerBase_Type LayerBase base;
-};
-
-struct Style Style_Type RfStructOp {
-	DesignerArray_Type "uint64_t *" layers #RfItem { &rfU64, sizeof(uint64_t), NULL };
-	rfData RfData name;
-	rfU64 uint64_t id from 15;
-	rfBool bool publicStyle from 16;
-};
-
-struct Constant Constant_Type RfStructOp {
-	rfData RfData key;
-	StyleString_Type RfData value;
-	StyleBool_Type bool scale from 9;
-};
-
-struct Color Color_Type RfStructOp {
-	rfData RfData key;
-	rfU32 uint32_t value;
-	rfU32 uint32_t id;
-	rfNone uint64_t object2ID;
-};
-
-struct StyleSet StyleSet_Type StyleSetOp {
-	rfU64 uint64_t lastID;
-	DesignerArray_Type "Style **" styles #RfItem { &rfObject, sizeof(Style *), &(RfItem) { &Style_Type, sizeof(Style), NULL } };
-	DesignerArray_Type "Layer **" layers #RfItem { &rfObject, sizeof(Layer *), &(RfItem) { &Layer_Type, sizeof(Layer), NULL } };
-	DesignerArray_Type "Constant **" constants #RfItem { &rfObject, sizeof(Constant *), &(RfItem) { &Constant_Type, sizeof(Constant), NULL } };
-	DesignerArray_Type "Color **" colors #RfItem { &rfObject, sizeof(Color *), &(RfItem) { &Color_Type, sizeof(Color), NULL } } from 17;
-};
-
-/////////////////////////////////////////////////////
-
-struct ModAddLayer ModAddLayer_Type ModAddLayerOp {
-	rfObject "Layer *" layer #RfItem { &Layer_Type, sizeof(Layer), NULL };
-	rfI32 int index;
-};
-
-struct ModDeleteLayer ModDeleteLayer_Type ModDeleteLayerOp {
-	rfI32 int index;
-};
-
-struct ModSwapLayers ModSwapLayers_Type ModSwapLayersOp {
-	rfI32 int index;
-};
-
-struct ModSwapSequences ModSwapSequences_Type ModSwapSequencesOp {
-	rfI32 int index;
-};
-
-struct ModAddSequence ModAddSequence_Type ModAddSequenceOp {
-	rfObject "Sequence *" sequence #RfItem { &Sequence_Type, sizeof(Sequence), NULL };
-	rfI32 int index;
-};
-
-struct ModDeleteSequence ModDeleteSequence_Type ModDeleteSequenceOp {
-	rfI32 int index;
-};
-
-struct ModAddKeyframe ModAddKeyframe_Type ModAddKeyframeOp {
-	rfObject "Keyframe *" keyframe #RfItem { &Keyframe_Type, sizeof(Keyframe), NULL };
-	rfI32 int index;
-};
-
-struct ModDeleteKeyframe ModDeleteKeyframe_Type ModDeleteKeyframeOp {
-	rfI32 int index;
-};
-
-struct ModChangeProperty ModChangeProperty_Type ModChangePropertyOp {
-	Property_Type Property property;
-	rfNone "struct UIElement *" source;
-};
-
-struct ModArray ModArray_Type ModArrayOp {
-	Property_Type Property property;
-	rfBool bool isDelete;
-};
-
-struct ModDeleteOverride ModDeleteOverride_Type ModDeleteOverrideOp {
-	Property_Type Property property;
-};
-
-union ModData ModData_Type RfUnionOp {
-	ModAddLayer_Type ModAddLayer addLayer;
-	ModDeleteLayer_Type ModDeleteLayer deleteLayer;
-	ModSwapLayers_Type ModSwapLayers swapLayers;
-	ModSwapSequences_Type ModSwapSequences swapSequences;
-	ModAddSequence_Type ModAddSequence addSequence;
-	ModDeleteSequence_Type ModDeleteSequence deleteSequence;
-	ModAddKeyframe_Type ModAddKeyframe addKeyframe;
-	ModDeleteKeyframe_Type ModDeleteKeyframe deleteKeyframe;
-	ModChangeProperty_Type ModChangeProperty changeProperty;
-	ModArray_Type ModArray array;
-	ModDeleteOverride_Type ModDeleteOverride deleteOverride;
-};
-
-struct Mod Mod_Type RfStructOp {
-	rfNone ModContext context;
-	ModData_Type ModData data;
-};
diff --git a/util/designer/designer_luigi.c b/util/designer/designer_luigi.c
deleted file mode 100644
index 1bbe37d..0000000
--- a/util/designer/designer_luigi.c
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifdef _WIN32
-#define UI_WINDOWS
-#else
-#define UI_LINUX
-#endif
-
-#define UI_IMPLEMENTATION
-#include "../luigi.h"
diff --git a/util/designer/reflect.h b/util/designer/reflect.h
deleted file mode 100644
index a9bd283..0000000
--- a/util/designer/reflect.h
+++ /dev/null
@@ -1,654 +0,0 @@
-// TODO Bitsets.
-// TODO Versioning support for unions and enums.
-
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#ifndef RF_ASSERT
-#include <assert.h>
-#define RF_ASSERT assert
-#endif
-
-#ifndef RF_MEMZERO
-#include <string.h>
-#define RF_MEMZERO(pointer, byteCount) memset((pointer), 0, (byteCount))
-#endif
-
-#ifndef RF_MEMCPY
-#include <string.h>
-#define RF_MEMCPY(destination, source, byteCount) memcpy((destination), (source), (byteCount))
-#endif
-
-#ifndef RF_REALLOC
-#include <stdlib.h>
-#define RF_REALLOC(previous, byteCount) realloc((previous), (byteCount))
-#endif
-
-#define RF_SIZE_OF(containerType, field) sizeof(((containerType *) NULL)->field)
-#define RF_FIELD(containerType, field, fieldRfType, ...) \
-	{ \
-		.item.type = &fieldRfType, \
-		.item.byteCount = RF_SIZE_OF(containerType, field), \
-		.cName = #field, \
-		.offset = offsetof(containerType, field), \
-		__VA_ARGS__ \
-	}
-
-#define RF_OP_SAVE    (-1) // Set access in RfState.
-#define RF_OP_LOAD    (-2) // Set access and allocate in RfState.
-#define RF_OP_FREE    (-3) // Set allocate in RfState.
-#define RF_OP_ITERATE (-4) // Pass RfIterator; set index.
-#define RF_OP_COUNT   (-5) // Pass RfIterator; result saved in index.
-// User-defined operations use positive integers.
-
-typedef struct RfState {
-	bool error;
-	int16_t op;
-	uint32_t version, flags;
-	void *(*allocate)(struct RfState *state, void *previous, size_t byteCount);
-	void (*access)(struct RfState *state, void *buffer, size_t byteCount);
-} RfState;
-
-typedef struct RfItem {
-	struct RfType *type;
-	size_t byteCount;
-	void *options;
-} RfItem;
-
-typedef struct RfField {
-	RfItem item;
-	const char *cName;
-	ptrdiff_t offset;
-	uint32_t firstVersion, lastVersion;
-	uint32_t flagsInclude, flagsExclude;
-} RfField;
-
-typedef struct RfType {
-	void (*op)(RfState *state, RfItem *field, void *pointer);
-	const char *cName;
-	size_t fieldCount;
-	RfField *fields; 
-} RfType;
-
-typedef struct RfIterator {
-	RfState s;
-	void *pointer;
-	RfItem item;
-	uint32_t index;
-	bool includeRemovedFields;
-	bool isRemoved;
-} RfIterator;
-
-typedef struct RfPath {
-#define RF_PATH_TERMINATOR (0xFFFFFFFF)
-	uint32_t indices[1]; 
-} RfPath;
-
-typedef struct RfUnionHeader {
-	uint32_t tag;
-} RfUnionHeader;
-
-typedef struct RfArrayHeader {
-	size_t length, capacity;
-	
-	// For compatability with stb_ds.h.
-	void *hashTable;
-	ptrdiff_t temporary;
-} RfArrayHeader;
-
-typedef struct RfData {
-	void *buffer;
-	size_t byteCount;
-} RfData;
-
-typedef struct RfGrowableBuffer {
-	RfState s;
-	RfData data;
-
-	// When writing - allocated space in data.
-	// When reading - position in data.
-	size_t position; 
-} RfGrowableBuffer;
-
-extern RfType rfI8, rfI16, rfI32, rfI64,
-	rfU8, rfU16, rfU32, rfU64,
-	rfF32, rfF64,
-	rfChar, rfBool, 
-	rfData, rfNone,
-	rfObject /* options - RfItem */, 
-	rfArray /* options - RfItem */;
-
-bool RfPathResolve(RfPath *path, RfItem *item, void **pointer); // Returns true if successful.
-void RfBroadcast(RfState *state, RfItem *item, void *pointer, bool recurse);
-void RfUnionSelect(RfState *state, RfItem *item, RfUnionHeader *header, uint32_t tag);
-
-void RfReadGrowableBuffer(RfState /* RfGrowableBuffer */ *state, void *buffer, size_t byteCount);
-void RfWriteGrowableBuffer(RfState /* RfGrowableBuffer */ *state, void *buffer, size_t byteCount);
-void *RfRealloc(RfState *state, void *previous, size_t byteCount);
-
-void RfStructOp(RfState *state, RfItem *item, void *pointer);
-void RfUnionOp(RfState *state, RfItem *item, void *pointer);
-void RfEnumOp(RfState *state, RfItem *item, void *pointer);
-void RfBitSetOp(RfState *state, RfItem *item, void *pointer);
-void RfEndianOp(RfState *state, RfItem *item, void *pointer);
-void RfIntegerOp(RfState *state, RfItem *item, void *pointer);
-void RfNoneOp(RfState *state, RfItem *item, void *pointer);
-
-#ifdef REFLECT_IMPLEMENTATION
-
-void RfIntegerSave(RfState *state, void *pointer, size_t inByteCount) {
-	uint8_t in[16];
-	RF_ASSERT(inByteCount < 16);
-	RF_MEMCPY(in, pointer, inByteCount);
-	
-	bool negative = in[inByteCount - 1] & 0x80;
-	size_t inBitCount = 1;
-	
-	for (int i = inByteCount - 1; i >= 0; i--) {
-		for (int j = 7; j >= 0; j--) {
-			if (((in[i] >> j) & 1) != negative) {
-				inBitCount = i * 8 + j + 2;
-				goto gotBitCount;
-			}
-		}
-	}
-	
-	gotBitCount:;
-	
-	size_t outByteCount = (inBitCount + 6) / 7;
-	uint8_t out[16];	
-	RF_ASSERT(outByteCount < 16);
-
-	for (uintptr_t i = 0; i < outByteCount; i++) {
-		uint8_t b = 0;
-		
-		for (uintptr_t j = 0; j < 7; j++) {
-			uintptr_t bitIndex = i * 7 + j;
-			bool inBit = negative;
-			if (bitIndex < 8 * inByteCount) inBit = in[bitIndex >> 3] & (1 << (bitIndex & 7));
-			if (inBit) b |= 1 << j;
-		}
-		
-		out[i] = b;
-	}
-	
-	out[outByteCount - 1] |= 0x80;
-	state->access(state, out, outByteCount);
-}
-
-void RfIntegerLoad(RfState *state, void *pointer, size_t byteCount) {
-	uint8_t out[16];
-	uintptr_t outIndex = 0;
-	RF_ASSERT(byteCount < 16);
-	RF_MEMZERO(out, byteCount);
-	
-	while (!state->error) {
-		uint8_t b;
-		state->access(state, &b, 1); 
-		
-		for (uintptr_t i = 0; i < 7; i++) {
-			if (outIndex == byteCount * 8) break;
-			if (b & (1 << i)) out[outIndex >> 3] |= 1 << (outIndex & 7);
-			outIndex++;
-		}
-		
-		if (b & 0x80) {
-			if (b & 0x40) {
-				for (uintptr_t i = outIndex; i < byteCount * 8; i++) {
-					out[i >> 3] |= 1 << (i & 7);
-				}
-			}
-			
-			break;
-		}
-	}
-	
-	if (!state->error && pointer) {
-		RF_MEMCPY(pointer, out, byteCount);
-	}
-}
-
-void RfStructOp(RfState *state, RfItem *item, void *pointer) {
-	RfType *type = item->type;
-
-	if (state->op == RF_OP_SAVE || state->op == RF_OP_LOAD || state->op == RF_OP_FREE) {
-		for (uintptr_t i = 0; i < type->fieldCount && !state->error; i++) {
-			RfField *field = type->fields + i;
-
-			if (state->flags & field->flagsExclude) continue;
-			if ((state->flags & field->flagsInclude) != field->flagsInclude) continue;
-
-			void *fieldPointer = pointer ? ((uint8_t *) pointer + field->offset) : NULL;
-
-			if (state->op == RF_OP_LOAD) {
-				if (state->version < field->firstVersion) {
-					// The field exists, but we're loading from a version where it did not exist.
-					// Ignore it.
-					continue;
-				} else if (field->lastVersion && state->version > field->lastVersion) {
-					// The field no longer exists, and we're from loading a version where it did not exist.
-					// Ignore it.
-					continue;
-				} else if (field->lastVersion) {
-					// The field no longer exists, but we're loading a version where it did. 
-					// Skip over it.
-					fieldPointer = NULL;
-				} else {
-					// The field exists, and we're loading from a version where it exists.
-				}
-			} else {
-				if (field->lastVersion) {
-					continue;
-				}
-			}
-
-			field->item.type->op(state, &field->item, fieldPointer);
-
-			if (state->op == RF_OP_FREE) {
-				RF_MEMZERO(fieldPointer, field->item.byteCount);
-			}
-		}
-	} else if (state->op == RF_OP_COUNT) {
-		RfIterator *iterator = (RfIterator *) state;
-		uint32_t count = 0;
-
-		for (uintptr_t i = 0; i < type->fieldCount; i++) {
-			if (!type->fields[i].lastVersion) {
-				count++;
-			}
-		}
-
-		iterator->index = count;
-	} else if (state->op == RF_OP_ITERATE) {
-		RfIterator *iterator = (RfIterator *) state;
-		uint32_t count = 0;
-
-		for (uintptr_t i = 0; i < type->fieldCount; i++) {
-			iterator->isRemoved = type->fields[i].lastVersion;
-
-			if (!iterator->isRemoved || iterator->includeRemovedFields) {
-				if (iterator->index == count) {
-					iterator->pointer = (uint8_t *) pointer + type->fields[i].offset;
-					iterator->item = type->fields[i].item;
-					return;
-				}
-
-				count++;
-			}
-		}
-
-		state->error = true;
-	}
-}
-
-void RfUnionOp(RfState *state, RfItem *item, void *pointer) {
-	RfType *type = item->type;
-	RfUnionHeader *header = (RfUnionHeader *) pointer;
-
-	if (state->op == RF_OP_SAVE) {
-		state->access(state, &header->tag, sizeof(uint32_t));
-
-		if (header->tag) {
-			RfField *field = type->fields + header->tag - 1;
-			field->item.type->op(state, &field->item, (uint8_t *) pointer + field->offset);
-		}
-	} else if (state->op == RF_OP_LOAD) {
-		uint32_t tag = 0;
-		state->access(state, &tag, sizeof(uint32_t));
-
-		if (tag > type->fieldCount) {
-			tag = 0;
-			state->error = true;
-		} else if (tag) {
-			RfField *field = type->fields + tag - 1;
-			if (field->lastVersion) tag = 0;
-			field->item.type->op(state, &field->item, pointer && !field->lastVersion ? ((uint8_t *) pointer + field->offset) : NULL);
-		}
-
-		if (header) {
-			header->tag = tag;
-		}
-	} else if (state->op == RF_OP_FREE) {
-		if (header->tag) {
-			RfField *field = type->fields + header->tag - 1;
-			field->item.type->op(state, &field->item, (uint8_t *) pointer + field->offset);
-		}
-
-		header->tag = 0;
-	} else if (state->op == RF_OP_COUNT) {
-		RfIterator *iterator = (RfIterator *) state;
-		iterator->index = header->tag ? 1 : 0;
-	} else if (state->op == RF_OP_ITERATE) {
-		RfIterator *iterator = (RfIterator *) state;
-
-		if (!header->tag || iterator->index > 1) {
-			state->error = true;
-		} else {
-			RfField *field = type->fields + header->tag - 1;
-			iterator->pointer = (uint8_t *) pointer + field->offset;
-			iterator->item = field->item;
-		}
-	}
-}
-
-void RfUnionSelect(RfState *state, RfItem *item, RfUnionHeader *header, uint32_t tag) {
-	RF_ASSERT(header->tag < item->type->fieldCount && !item->type->fields[header->tag].lastVersion);
-	RF_ASSERT(tag < item->type->fieldCount && !item->type->fields[tag].lastVersion);
-
-	if (header->tag) {
-		RfField *field = item->type->fields + header->tag - 1;
-		field->item.type->op(state, &field->item, (uint8_t *) header + field->offset);
-	}
-
-	header->tag = tag + 1;
-}
-
-void RfEnumOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == RF_OP_SAVE) {
-		RfIntegerSave(state, pointer, item->byteCount);
-	} else if (state->op == RF_OP_LOAD) {
-		RfIntegerLoad(state, pointer, item->byteCount);
-
-		if (pointer) {
-			uint32_t value = 0;
-
-			if (item->byteCount == 1) {
-				value = *(uint8_t *) pointer;
-			} else if (item->byteCount == 2) {
-				value = *(uint16_t *) pointer;
-			} else if (item->byteCount == 4) {
-				value = *(uint32_t *) pointer;
-			} else {
-				RF_ASSERT(false);
-			}
-
-			if (value >= item->type->fieldCount) {
-				state->error = true;
-			}
-		}
-	}
-}
-
-void RfEndianOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == RF_OP_SAVE || state->op == RF_OP_LOAD) {
-		state->access(state, pointer, item->byteCount);
-	}
-}
-
-void RfBoolOp(RfState *state, RfItem *item, void *pointer) {
-	RfEndianOp(state, item, pointer);
-
-	if (state->op == RF_OP_LOAD) {
-		if (pointer && *(uint8_t *) pointer > 1) {
-			state->error = true;
-		}
-	}
-}
-
-void RfIntegerOp(RfState *state, RfItem *item, void *pointer) {
-	if (state->op == RF_OP_SAVE) {
-		RfIntegerSave(state, pointer, item->byteCount);
-	} else if (state->op == RF_OP_LOAD) {
-		RfIntegerLoad(state, pointer, item->byteCount);
-	}
-}
-
-void RfDataOp(RfState *state, RfItem *item, void *pointer) {
-	(void) item;
-	RfData *data = (RfData *) pointer;
-
-	if (state->op == RF_OP_SAVE) {
-		uint32_t byteCount = data->byteCount;
-		RfIntegerSave(state, &byteCount, sizeof(uint32_t));
-		state->access(state, data->buffer, data->byteCount);
-	} else if (state->op == RF_OP_LOAD) {
-		uint32_t byteCount = 0;
-		RfIntegerLoad(state, &byteCount, sizeof(uint32_t));
-
-		if (data) {
-			RF_ASSERT(!data->buffer);
-			data->buffer = state->allocate(state, NULL, byteCount);
-			if (!data->buffer) { state->error = true; return; }
-			data->byteCount = byteCount;
-		}
-
-		state->access(state, data ? data->buffer : NULL, data->byteCount);
-	} else if (state->op == RF_OP_FREE) {
-		state->allocate(state, data->buffer, 0);
-		data->buffer = NULL;
-	}
-}
-
-void RfObjectOp(RfState *state, RfItem *item, void *pointer) {
-	RfItem *objectItem = (RfItem *) item->options;
-	void **object = (void **) pointer;
-
-	if (state->op == RF_OP_SAVE) {
-		uint8_t present = *object != NULL;
-		state->access(state, &present, sizeof(uint8_t));
-		if (present) objectItem->type->op(state, objectItem, *object);
-	} else if (state->op == RF_OP_LOAD) {
-		uint8_t present = 0;
-		state->access(state, &present, sizeof(uint8_t));
-
-		if (object) {
-			RF_ASSERT(!(*object));
-
-			if (present) {
-				*object = state->allocate(state, NULL, objectItem->byteCount);
-				if (!(*object)) { state->error = true; return; }
-				RF_MEMZERO(*object, objectItem->byteCount);
-				objectItem->type->op(state, objectItem, *object);
-			}
-		} else if (present) {
-			objectItem->type->op(state, objectItem, NULL);
-		}
-	} else if (state->op == RF_OP_FREE) {
-		if (*object) {
-			objectItem->type->op(state, objectItem, *object);
-			state->allocate(state, *object, 0);
-			*object = NULL;
-		}
-	} else if (state->op == RF_OP_COUNT) {
-		RfIterator *iterator = (RfIterator *) state;
-		iterator->index = *object ? 1 : 0;
-	} else if (state->op == RF_OP_ITERATE) {
-		RfIterator *iterator = (RfIterator *) state;
-
-		if (!(*object) || iterator->index > 1) {
-			state->error = true;
-		} else {
-			iterator->pointer = *object;
-			iterator->item = *objectItem;
-		}
-	}
-}
-
-void RfNoneOp(RfState *state, RfItem *item, void *pointer) {
-	(void) state;
-	(void) item;
-	(void) pointer;
-}
-
-void RfArrayOp(RfState *state, RfItem *item, void *_pointer) {
-	RfArrayHeader **pointer = (RfArrayHeader **) _pointer;
-	RfItem *objectItem = (RfItem *) item->options;
-
-	if (state->op == RF_OP_SAVE) {
-		uint32_t length = 0;
-
-		if (*pointer) {
-			length = (*pointer)[-1].length;
-		}
-
-		RfIntegerSave(state, &length, sizeof(uint32_t));
-
-		for (uint32_t i = 0; i < length; i++) {
-			objectItem->type->op(state, objectItem, (uint8_t *) (*pointer) + i * objectItem->byteCount);
-		}
-	} else if (state->op == RF_OP_LOAD) {
-		RF_ASSERT(!pointer || !(*pointer));
-
-		uint32_t length = 0;
-		RfIntegerLoad(state, &length, sizeof(uint32_t));
-
-		if (length >= 0xFFFFFFFF / objectItem->byteCount) {
-			state->error = true;
-			return;
-		}
-
-		if (!length) {
-			return;
-		}
-
-		if (pointer) {
-			void *allocation = state->allocate(state, NULL, length * objectItem->byteCount + sizeof(RfArrayHeader));
-
-			if (!allocation) {
-				state->error = true;
-				return;
-			}
-
-			*pointer = (RfArrayHeader *) allocation + 1;
-
-			(*pointer)[-1].length = 0;
-			(*pointer)[-1].capacity = length;
-			(*pointer)[-1].hashTable = NULL;
-			(*pointer)[-1].temporary = 0;
-		}
-
-		for (uint32_t i = 0; i < length && !state->error; i++) {
-			if (pointer) {
-				uint32_t index = (*pointer)[-1].length;
-				(*pointer)[-1].length++;
-				uint8_t *p = (uint8_t *) (*pointer) + index * objectItem->byteCount;
-				RF_MEMZERO(p, objectItem->byteCount);
-				objectItem->type->op(state, objectItem, p);
-			} else {
-				objectItem->type->op(state, objectItem, NULL);
-			}
-		}
-	} else if (state->op == RF_OP_FREE) {
-		if (*pointer) {
-			state->allocate(state, (*pointer) - 1, 0);
-			(*pointer) = NULL;
-		}
-	} else if (state->op == RF_OP_COUNT) {
-		RfIterator *iterator = (RfIterator *) state;
-		iterator->index = *pointer ? (*pointer)[-1].length : 0;
-	} else if (state->op == RF_OP_ITERATE) {
-		RfIterator *iterator = (RfIterator *) state;
-
-		if (!(*pointer) || iterator->index > (*pointer)[-1].length) {
-			state->error = true;
-		} else {
-			iterator->pointer = (uint8_t *) (*pointer) + iterator->index * objectItem->byteCount;
-			iterator->item = *objectItem;
-		}
-	}
-}
-
-bool RfPathResolve(RfPath *path, RfItem *item, void **pointer) {
-	RfIterator iterator = { 0 };
-	iterator.s.op = RF_OP_ITERATE;
-	iterator.item = *item;
-	iterator.pointer = *pointer;
-	iterator.includeRemovedFields = true;
-
-	for (int i = 0; path->indices[i] != RF_PATH_TERMINATOR && !iterator.s.error; i++) {
-		iterator.index = path->indices[i];
-		RfItem _item = iterator.item;
-		_item.type->op(&iterator.s, &_item, iterator.pointer);
-	}
-
-	*item = iterator.item;
-	*pointer = iterator.pointer;
-	return !iterator.s.error;
-}
-
-void RfBroadcast(RfState *state, RfItem *item, void *pointer, bool recurse) {
-	RfIterator iterator = { 0 };
-	iterator.s.op = RF_OP_COUNT;
-	item->type->op(&iterator.s, item, pointer);
-	iterator.s.op = RF_OP_ITERATE;
-	uint32_t count = iterator.index;
-
-	for (uint32_t i = 0; i < count; i++) {
-		iterator.index = i;
-		item->type->op(&iterator.s, item, pointer);
-		if (iterator.s.error) return;
-
-		iterator.item.type->op(state, &iterator.item, iterator.pointer);
-
-		if (recurse) {
-			RfBroadcast(state, &iterator.item, iterator.pointer, true);
-		}
-	}
-}
-
-void RfWriteGrowableBuffer(RfState *state, void *source, size_t byteCount) {
-	if (state->error) return;
-
-	RfGrowableBuffer *destination = (RfGrowableBuffer *) state;
-
-	if (destination->data.byteCount + byteCount > destination->position) {
-		destination->position = destination->position * 2;
-
-		if (destination->data.byteCount + byteCount > destination->position) {
-			destination->position = destination->data.byteCount + byteCount + 64;
-		}
-
-		void *old = destination->data.buffer;
-		destination->data.buffer = state->allocate(state, destination->data.buffer, destination->position);
-		
-		if (!destination->data.buffer) {
-			state->allocate(state, old, 0);
-			state->error = true;
-			return;
-		}
-	}
-	
-	RF_MEMCPY((uint8_t *) destination->data.buffer + destination->data.byteCount, source, byteCount);
-	destination->data.byteCount += byteCount;
-}
-
-void RfReadGrowableBuffer(RfState *state, void *destination, size_t byteCount) {
-	if (state->error) return;
-
-	RfGrowableBuffer *source = (RfGrowableBuffer *) state;
-
-	if (source->position + byteCount > source->data.byteCount) {
-		state->error = true;
-	} else {
-		if (destination) {
-			RF_MEMCPY(destination, (uint8_t *) source->data.buffer + source->position, byteCount);
-		}
-
-		source->position += byteCount;
-	}
-}
-
-void *RfRealloc(RfState *state, void *previous, size_t byteCount) {
-	(void) state;
-	return RF_REALLOC(previous, byteCount);
-}
-
-RfType rfI8     = { .op = RfEndianOp,  .cName = "I8"     };
-RfType rfI16    = { .op = RfIntegerOp, .cName = "I16"    };
-RfType rfI32    = { .op = RfIntegerOp, .cName = "I32"    };
-RfType rfI64    = { .op = RfIntegerOp, .cName = "I64"    };
-RfType rfU8     = { .op = RfEndianOp,  .cName = "U8"     };
-RfType rfU16    = { .op = RfIntegerOp, .cName = "U16"    };
-RfType rfU32    = { .op = RfIntegerOp, .cName = "U32"    };
-RfType rfU64    = { .op = RfIntegerOp, .cName = "U64"    };
-RfType rfChar   = { .op = RfEndianOp,  .cName = "Char"   };
-RfType rfBool   = { .op = RfBoolOp,    .cName = "Bool"   };
-RfType rfF32    = { .op = RfEndianOp,  .cName = "F32"    };
-RfType rfF64    = { .op = RfEndianOp,  .cName = "F64"    };
-RfType rfData   = { .op = RfDataOp,    .cName = "Data"   };
-RfType rfObject = { .op = RfObjectOp,  .cName = "Object" };
-RfType rfArray  = { .op = RfArrayOp,   .cName = "Array"  };
-RfType rfNone   = { .op = RfNoneOp,    .cName = "None"   };
-
-#endif
diff --git a/util/designer/reflect_gen.c b/util/designer/reflect_gen.c
deleted file mode 100644
index 22d67ab..0000000
--- a/util/designer/reflect_gen.c
+++ /dev/null
@@ -1,531 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#define STB_DS_IMPLEMENTATION
-#include "../stb_ds.h"
-
-#define TOKEN_BLOCK		(0)
-#define TOKEN_LEFT_BRACE 	(1)
-#define TOKEN_RIGHT_BRACE 	(2)
-#define TOKEN_LEFT_PAREN 	(3)
-#define TOKEN_RIGHT_PAREN 	(4)
-#define TOKEN_SEMICOLON 	(5)
-#define TOKEN_IDENTIFIER 	(6)
-#define TOKEN_STRING 		(7)
-#define TOKEN_EOF 		(8)
-#define TOKEN_COMMA 		(9)
-#define TOKEN_NUMBER 		(10)
-#define TOKEN_HASH 		(11)
-#define TOKEN_QUESTION 		(12)
-#define TOKEN_EQUALS		(13)
-#define TOKEN_LEFT_BRACKET 	(14)
-#define TOKEN_RIGHT_BRACKET 	(15)
-#define TOKEN_COLON 		(16)
-#define TOKEN_EXCLAMATION	(17)
-
-typedef struct Parse {
-	const char *position;
-	int line;
-	bool success;
-} Parse;
-
-typedef struct Token {
-	int type;
-	double number;
-	char *string;
-} Token;
-
-typedef struct ParsedField {
-	char *rfType, *cTypeBefore, *fieldName, *cTypeAfter;
-	int firstVersion, lastVersion;
-	char **flagsInclude, **flagsExclude;
-	char *optionsType, *optionsBlock;
-} ParsedField;
-
-typedef struct ParsedType {
-	bool isStruct, isUnion, isCustom, isEnum;
-	char *cName, *rfName, *opFunction;
-	ParsedField *fields;
-} ParsedType;
-
-ParsedType *parsedTypes;
-
-bool IsDigit(char c) { return (c >= '0' && c <= '9'); }
-bool IsAlpha(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
-bool IsAlnum(char c) { return (IsDigit(c) || IsAlpha(c)); }
-
-bool _NextToken(Parse *parse, Token *token) {
-	if (!parse->success) return false;
-	
-	while (true) {
-		char c = *parse->position;
-		parse->position++;
-		
-		if (c == ' ' || c == '\t' || c == '\r') {
-			continue;
-		} else if (c == '\n') {
-			parse->line++;
-			continue;
-		} else if (c == '/' && *parse->position == '/') {
-			while (*parse->position != '\n') parse->position++;
-			continue;
-		} else if (c == '/' && *parse->position == '*') {
-			while (parse->position[0] != '*' || parse->position[1] != '/') parse->position++;
-			parse->position += 2;
-			continue;
-#define PARSE_CHARACTER(a, b) \
-		} else if (c == a) { \
-			Token t = {}; \
-			t.type = b; \
-			*token = t; \
-			return true
-		PARSE_CHARACTER('{', TOKEN_LEFT_BRACE );
-		PARSE_CHARACTER('}', TOKEN_RIGHT_BRACE );
-		PARSE_CHARACTER('(', TOKEN_LEFT_PAREN );
-		PARSE_CHARACTER(')', TOKEN_RIGHT_PAREN );
-		PARSE_CHARACTER('[', TOKEN_LEFT_BRACKET );
-		PARSE_CHARACTER(']', TOKEN_RIGHT_BRACKET );
-		PARSE_CHARACTER(';', TOKEN_SEMICOLON );
-		PARSE_CHARACTER(':', TOKEN_COLON );
-		PARSE_CHARACTER(',', TOKEN_COMMA );
-		PARSE_CHARACTER('#', TOKEN_HASH );
-		PARSE_CHARACTER('?', TOKEN_QUESTION );
-		PARSE_CHARACTER('=', TOKEN_EQUALS );
-		PARSE_CHARACTER('!', TOKEN_EXCLAMATION );
-		} else if (IsAlpha(c) || c == '_') {
-			const char *start = parse->position - 1;
-			
-			while (true) {
-				char c2 = *parse->position;
-				if (c2 == 0) break;
-				if (!IsAlnum(c2) && c2 != '_') break;
-				parse->position++;
-			}
-			
-			Token _token = { 0 };
-			_token.type = TOKEN_IDENTIFIER;
-			_token.string = malloc(parse->position - start + 1);
-			if (!_token.string) { parse->success = false; return false; }
-			memcpy(_token.string, start, parse->position - start);
-			_token.string[parse->position - start] = 0;
-			*token = _token;
-			return true;
-		} else if (IsDigit(c) || c == '-') {
-			const char *start = parse->position - 1;
-			
-			while (true) {
-				char c2 = *parse->position;
-				if (c2 == 0) break;
-				if (!IsAlnum(c2) && c2 != '.') break;
-				parse->position++;
-			}
-			
-			Token _token = { 0 };
-			_token.type = TOKEN_NUMBER;
-			_token.string = malloc(parse->position - start + 1);
-			if (!_token.string) { parse->success = false; return false; }
-			memcpy(_token.string, start, parse->position - start);
-			_token.string[parse->position - start] = 0;
-			
-			bool negate = _token.string[0] == '-';
-			bool afterDot = false;
-			bool hexadecimal = false;
-			double fraction = 0.1;
-			
-			for (uintptr_t i = negate ? 1 : 0; _token.string[i]; i++) {
-				char c = _token.string[i];
-				
-				if (c == '.') {
-					if (hexadecimal) {
-						parse->success = false; 
-						return false;
-					}
-
-					afterDot = true;
-				} else if (c == 'x') {
-					if (i != 1 || _token.string[0] != '0' || afterDot) {
-						parse->success = false; 
-						return false;
-					}
-
-					hexadecimal = true;
-				} else if (afterDot) {
-					if (!IsDigit(c)) {
-						parse->success = false; 
-						return false;
-					}
-
-					_token.number += (c - '0') * fraction;
-					fraction *= 0.1;
-				} else {
-					if (hexadecimal) {
-						_token.number *= 16;
-
-						if (c >= '0' && c <= '9') {
-							_token.number += c - '0';
-						} else if (c >= 'a' && c <= 'f') {
-							_token.number += c - 'a' + 10;
-						} else if (c >= 'A' && c <= 'F') {
-							_token.number += c - 'A' + 10;
-						} else {
-							parse->success = false; 
-							return false;
-						}
-					} else {
-						if (!IsDigit(c)) {
-							parse->success = false; 
-							return false;
-						}
-
-						_token.number *= 10;
-						_token.number += c - '0';
-					}
-				}
-				
-			}
-			
-			if (negate) _token.number = -_token.number;
-			*token = _token;
-			return true;
-		} else if (c == '"') {
-			const char *start = parse->position;
-			
-			while (true) {
-				char c2 = *parse->position;
-				if (c2 == 0) break;
-				parse->position++;
-				if (c2 == '"') break;
-			}
-			
-			Token _token = { 0 };
-			_token.type = TOKEN_STRING;
-			_token.string = (char *) malloc(parse->position - start);
-			if (!_token.string) { parse->success = false; return false; }
-			memcpy(_token.string, start, parse->position - start - 1);
-			_token.string[parse->position - start - 1] = 0;
-			*token = _token;
-			return true;
-		} else if (c == 0) {
-			Token t = {};
-			t.type = TOKEN_EOF;
-			*token = t;
-			return true;
-		} else {
-			parse->success = false;
-			return false;
-		}
-	}
-}
-
-Token NextToken(Parse *parse) {
-	Token token = { 0 };
-	bool success = _NextToken(parse, &token);
-
-	if (!success) {
-		fprintf(stderr, "error: invalid token on line %d\n", parse->line);
-		exit(1);
-	}
-
-	return token;
-}
-
-char *NextString(Parse *parse) {
-	Token token = NextToken(parse);
-
-	if (token.type == TOKEN_IDENTIFIER || token.type == TOKEN_STRING) {
-		return token.string;
-	} else {
-		fprintf(stderr, "error: expected string or identifier on line %d\n", parse->line);
-		exit(1);
-		return NULL;
-	}
-}
-
-Token ExpectToken(Parse *parse, int type) {
-	Token token = NextToken(parse);
-
-	if (token.type == type) {
-		return token;
-	} else {
-		fprintf(stderr, "error: expected token of type %d of line %d\n", type, parse->line);
-		exit(1);
-		return (Token) { 0 };
-	}
-}
-
-char *NextBlock(Parse *parse) {
-	ExpectToken(parse, TOKEN_LEFT_BRACE);
-
-	int depth = 1;
-	const char *start = parse->position;
-
-	while (depth) {
-		if (*parse->position == '{') {
-			depth++;
-			parse->position++;
-		} else if (*parse->position == '}') {
-			depth--;
-			parse->position++;
-		} else if (*parse->position == '"') {
-			ExpectToken(parse, TOKEN_STRING);
-		} else if (*parse->position == 0) {
-			fprintf(stderr, "error: unexpected end of file during block\n");
-			exit(1);
-		} else {
-			parse->position++;
-		}
-	}
-
-	char *result = malloc(parse->position - start);
-	memcpy(result, start, parse->position - start - 1);
-	result[parse->position - start - 1] = 0;
-	return result;
-}
-
-Token PeekToken(Parse *parse) {
-	Parse old = *parse;
-	Token token = NextToken(parse);
-	*parse = old;
-	return token;
-}
-
-char *LoadFile(const char *inputFileName, size_t *byteCount) {
-	FILE *inputFile = fopen(inputFileName, "rb");
-	
-	if (!inputFile) {
-		return NULL;
-	}
-	
-	fseek(inputFile, 0, SEEK_END);
-	size_t inputFileBytes = ftell(inputFile);
-	fseek(inputFile, 0, SEEK_SET);
-	
-	char *inputBuffer = (char *) malloc(inputFileBytes + 1);
-	size_t inputBytesRead = fread(inputBuffer, 1, inputFileBytes, inputFile);
-	inputBuffer[inputBytesRead] = 0;
-	fclose(inputFile);
-	
-	if (byteCount) *byteCount = inputBytesRead;
-	return inputBuffer;
-}
-
-void ParseAdditionalFieldInformation(Parse *parse, ParsedField *field) {
-	while (PeekToken(parse).type != TOKEN_SEMICOLON) {
-		Token token = NextToken(parse);
-
-		if (token.type == TOKEN_IDENTIFIER && 0 == strcmp(token.string, "from") && !field->firstVersion) {
-			field->firstVersion = ExpectToken(parse, TOKEN_NUMBER).number;
-		} else if (token.type == TOKEN_IDENTIFIER && 0 == strcmp(token.string, "to") && !field->lastVersion) {
-			field->lastVersion = ExpectToken(parse, TOKEN_NUMBER).number;
-		} else if (token.type == TOKEN_IDENTIFIER && 0 == strcmp(token.string, "if") && !field->flagsInclude && !field->flagsExclude) {
-			ExpectToken(parse, TOKEN_LEFT_PAREN);
-
-			while (PeekToken(parse).type != TOKEN_RIGHT_PAREN) {
-				if (PeekToken(parse).type == TOKEN_EXCLAMATION) {
-					ExpectToken(parse, TOKEN_EXCLAMATION);
-					arrput(field->flagsExclude, NextString(parse));
-				} else {
-					arrput(field->flagsInclude, NextString(parse));
-				}
-			}
-
-			ExpectToken(parse, TOKEN_RIGHT_PAREN);
-		} else if (token.type == TOKEN_HASH) {
-			field->optionsType = NextString(parse);
-			field->optionsBlock = NextBlock(parse);
-		} else {
-			fprintf(stderr, "error: unexpected token in field on line %d\n", parse->line);
-			exit(1);
-		}
-	}
-}
-
-int main(int argc, char **argv) {
-	if (argc != 2) {
-		fprintf(stderr, "usage: reflect_gen <input file>\n");
-		return 1;
-	}
-
-	char *input = LoadFile(argv[1], NULL);
-
-	if (!input) {
-		fprintf(stderr, "error: could not open input file '%s'\n", argv[1]);
-		return 1;
-	}
-
-	Parse parse = { 0 };
-	parse.position = input;
-	parse.line = 1;
-	parse.success = true;
-
-	while (true) {
-		Token token = NextToken(&parse);
-
-		if (token.type == TOKEN_EOF) {
-			break;
-		}
-
-		if (token.type == TOKEN_IDENTIFIER && (0 == strcmp(token.string, "struct") || 0 == strcmp(token.string, "union"))) {
-			ParsedType type = { 0 };
-			type.isStruct = 0 == strcmp(token.string, "struct");
-			type.isUnion = 0 == strcmp(token.string, "union");
-			type.cName = NextString(&parse);
-			type.rfName = NextString(&parse);
-			type.opFunction = NextString(&parse);
-			ExpectToken(&parse, TOKEN_LEFT_BRACE);
-
-			while (PeekToken(&parse).type != TOKEN_RIGHT_BRACE) {
-				ParsedField field = { 0 };
-				field.rfType = NextString(&parse);
-				field.cTypeBefore = NextString(&parse);
-				field.fieldName = NextString(&parse);
-
-				if (PeekToken(&parse).type == TOKEN_STRING) {
-					field.cTypeAfter = NextString(&parse);
-				}
-
-				ParseAdditionalFieldInformation(&parse, &field);
-				ExpectToken(&parse, TOKEN_SEMICOLON);
-				arrput(type.fields, field);
-			}
-
-			ExpectToken(&parse, TOKEN_RIGHT_BRACE);
-			ExpectToken(&parse, TOKEN_SEMICOLON);
-			arrput(parsedTypes, type);
-		} else if (token.type == TOKEN_IDENTIFIER && 0 == strcmp(token.string, "type")) {
-			ParsedType type = { 0 };
-			type.isCustom = true;
-			type.rfName = NextString(&parse);
-			type.opFunction = NextString(&parse);
-			ExpectToken(&parse, TOKEN_SEMICOLON);
-			arrput(parsedTypes, type);
-		} else if (token.type == TOKEN_IDENTIFIER && 0 == strcmp(token.string, "enum")) {
-			ParsedType type = { 0 };
-			type.isEnum = true;
-			type.rfName = NextString(&parse);
-			type.opFunction = NextString(&parse);
-			ExpectToken(&parse, TOKEN_LEFT_BRACE);
-
-			while (PeekToken(&parse).type != TOKEN_RIGHT_BRACE) {
-				ParsedField field = { 0 };
-				field.fieldName = NextString(&parse);
-				ParseAdditionalFieldInformation(&parse, &field);
-				ExpectToken(&parse, TOKEN_SEMICOLON);
-				arrput(type.fields, field);
-			}
-
-			ExpectToken(&parse, TOKEN_RIGHT_BRACE);
-			ExpectToken(&parse, TOKEN_SEMICOLON);
-			arrput(parsedTypes, type);
-		} else {
-			fprintf(stderr, "error: unexpected token at root on line %d\n", parse.line);
-			exit(1);
-		}
-	}
-
-	// Output C type declarations.
-
-	for (uintptr_t i = 0; i < arrlenu(parsedTypes); i++) {
-		ParsedType *type = parsedTypes + i;
-
-		if (type->isCustom) {
-			continue;
-		} else if (type->isEnum) {
-			for (uintptr_t j = 0; j < arrlenu(type->fields); j++) {
-				ParsedField *field = type->fields + j;
-				printf("#define %s (%d)\n", field->fieldName, (int) j);
-			}
-
-			printf("\n");
-			continue;
-		}
-
-		printf("typedef struct %s {\n", type->cName);
-		const char *indent = "\t";
-
-		if (type->isUnion) {
-			printf("\tuint32_t tag;\n\n\tunion {\n");
-			indent = "\t\t";
-		}
-
-		for (uintptr_t j = 0; j < arrlenu(type->fields); j++) {
-			ParsedField *field = type->fields + j;
-			printf("%s%s %s %s;\n", indent, field->cTypeBefore, field->fieldName, field->cTypeAfter ? field->cTypeAfter : "");
-		}
-
-		if (type->isUnion) {
-			printf("\t};\n");
-		}
-
-		printf("} %s;\n\n", type->cName);
-	}
-
-	// Forward-declare op functions.
-
-	for (uintptr_t i = 0; i < arrlenu(parsedTypes); i++) {
-		ParsedType *type = parsedTypes + i;
-		printf("void %s(RfState *state, RfItem *item, void *pointer);\n", type->opFunction);
-	}
-
-	// Output reflect type information.
-	
-	for (uintptr_t i = 0; i < arrlenu(parsedTypes); i++) {
-		ParsedType *type = parsedTypes + i;
-
-		printf("#ifdef REFLECT_IMPLEMENTATION\n");
-		printf("RfType %s = {\n", type->rfName);
-		printf("\t.op = %s,\n\t.cName = \"%s\",\n\t.fieldCount = %d,\n", type->opFunction, type->cName, (int) arrlen(type->fields));
-
-		if (arrlenu(type->fields)) {
-			printf("\n\t.fields = (RfField []) {\n");
-
-			for (uintptr_t j = 0; j < arrlenu(type->fields); j++) {
-				ParsedField *field = type->fields + j;
-
-				if (type->isEnum) {
-					printf("\t\t{ .cName = \"%s\", ", field->fieldName);
-				} else {
-					printf("\t\t{ .item.type = &%s, .item.byteCount = RF_SIZE_OF(%s, %s), .cName = \"%s\", .offset = offsetof(%s, %s), ",
-						field->rfType, type->cName, field->fieldName, field->fieldName, type->cName, field->fieldName);
-				}
-
-				printf(".firstVersion = %d, .lastVersion = %d, .flagsInclude = 0", field->firstVersion, field->lastVersion);
-
-				for (uintptr_t k = 0; k < arrlenu(field->flagsInclude); k++) {
-					printf(" | %s", field->flagsInclude[k]);
-				}
-
-				printf(", .flagsExclude = 0");
-
-				for (uintptr_t k = 0; k < arrlenu(field->flagsExclude); k++) {
-					printf(" | %s", field->flagsExclude[k]);
-				}
-
-				if (field->optionsType) {
-					printf(", .item.options = &(%s) { %s }", field->optionsType, field->optionsBlock);
-				}
-
-				printf(" },\n");
-			}
-
-			printf("\t},\n");
-		}
-
-		printf("};\n\n");
-		printf("#else\nextern RfType %s;\n#endif\n", type->rfName);
-
-		if (!type->isEnum) {
-			for (uintptr_t j = 0; j < arrlenu(type->fields); j++) {
-				ParsedField *field = type->fields + j;
-
-				printf("#define %s_%s (%d)\n", type->cName, field->fieldName, (int) j);
-			}
-		}
-	}
-
-	return 0;
-}