From 5eb3ba477fac6bfa2ef6ad7dfd3df3d1b18d47cd Mon Sep 17 00:00:00 2001 From: ascibisz Date: Wed, 11 Feb 2026 10:24:01 -0800 Subject: [PATCH 1/4] remove irrelevant gradient data from final recipe string --- src/App.tsx | 4 ++-- src/state/store.ts | 4 ++-- src/test/recipeLoader.test.ts | 6 +++--- src/utils/recipeLoader.ts | 28 +++++++++++++++++++++++++--- 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index b3d8cf77..8d68e80f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { Layout, Typography } from "antd"; import { getJobStatus, updateJobStatusTimestamp } from "./utils/firebase"; -import { getFirebaseRecipe, jsonToString } from "./utils/recipeLoader"; +import { getFirebaseRecipe, recipeToString } from "./utils/recipeLoader"; import { getSubmitPackingUrl, JOB_STATUS } from "./constants/aws"; import { useJobId, @@ -43,7 +43,7 @@ function App() { recipeString: string ): Promise => { const originalRecipe = await getFirebaseRecipe(recipeId); - return !(jsonToString(originalRecipe) == recipeString); + return !(recipeToString(originalRecipe) == recipeString); }; const submitRecipe = async ( diff --git a/src/state/store.ts b/src/state/store.ts index 48382aed..252dba26 100644 --- a/src/state/store.ts +++ b/src/state/store.ts @@ -2,7 +2,7 @@ import { create } from "zustand"; import { subscribeWithSelector } from "zustand/middleware"; import { isEmpty, isEqual, get as lodashGet } from "lodash-es"; import { PackingResult, RecipeData, RecipeManifest } from "../types"; -import { jsonToString } from "../utils/recipeLoader"; +import { recipeToString } from "../utils/recipeLoader"; import { getRecipeDataFromFirebase, getRecipeManifestFromFirebase, @@ -236,7 +236,7 @@ export const useRecipeStore = create()( edits ); if (!recipeObject) return; - const recipeString = jsonToString(recipeObject); + const recipeString = recipeToString(recipeObject); set({ isPacking: true }); try { await callback(s.selectedRecipeId, configId, recipeString); diff --git a/src/test/recipeLoader.test.ts b/src/test/recipeLoader.test.ts index e1e01c07..d8d95404 100644 --- a/src/test/recipeLoader.test.ts +++ b/src/test/recipeLoader.test.ts @@ -1,6 +1,6 @@ import { expect, test } from 'vitest'; import fs from 'fs'; -import { isFirebaseRef, getFirebaseRecipe, jsonToString } from '../utils/recipeLoader'; +import { isFirebaseRef, getFirebaseRecipe, recipeToString } from '../utils/recipeLoader'; test('isFirebaseRef detects Firebase references correctly', () => { expect(isFirebaseRef('firebase:recipes/some_id')).toBe(true); @@ -14,7 +14,7 @@ test('isFirebaseRef detects Firebase references correctly', () => { test('getFirebaseRecipe works as expected for ER_peroxisome_v_struct_gradient_370574', async () => { const recipeId = 'ER_peroxisome_v_struct_gradient_370574'; const recipeJson = await getFirebaseRecipe(recipeId); - const recipeString = jsonToString(recipeJson); + const recipeString = recipeToString(recipeJson); expect(recipeString).toBeDefined(); expect(typeof recipeString).toBe('string'); @@ -30,7 +30,7 @@ test('getFirebaseRecipe works as expected for ER_peroxisome_v_struct_gradient_37 test('getFirebaseRecipe works as expected for one_sphere', async () => { const recipeId = 'one_sphere_v_1.0.0'; const recipeJson = await getFirebaseRecipe(recipeId); - const recipeString = jsonToString(recipeJson); + const recipeString = recipeToString(recipeJson); expect(recipeString).toBeDefined(); expect(typeof recipeString).toBe('string'); diff --git a/src/utils/recipeLoader.ts b/src/utils/recipeLoader.ts index c133f037..884b9dfa 100644 --- a/src/utils/recipeLoader.ts +++ b/src/utils/recipeLoader.ts @@ -328,9 +328,31 @@ const getFirebaseRecipe = async (name: string): Promise => { return unpackedRecipe; } -const jsonToString = (json: ViewableRecipe): string => { - return JSON.stringify(json, null, 2); +const recipeToString = (recipe: ViewableRecipe): string => { + // Collect a list of gradients that are referenced by objects the recipe + const referencedGradients: string[] = []; + if (recipe.objects) { + for (const obj of Object.values(recipe.objects)) { + if (obj.packing_mode === "gradient" && obj.gradient) { + referencedGradients.push(obj.gradient); + } else if (obj.packing_mode === "random" && obj.gradient) { + // If packing mode is random, gradient field is irrelevant + // and should be cleared + delete obj.gradient; + } + } + } + + // If the recipe has gradients that aren't referenced in any objects, delete them + if (recipe.gradients) { + for (const gradientName of Object.keys(recipe.gradients)) { + if (!referencedGradients.includes(gradientName)) { + delete recipe.gradients[gradientName]; + } + } + } + return JSON.stringify(recipe, null, 2); } -export { getFirebaseRecipe, isFirebaseRef, jsonToString }; \ No newline at end of file +export { getFirebaseRecipe, isFirebaseRef, recipeToString }; \ No newline at end of file From 3aa1664fe80e971472d3e972fd8f2160a11d264d Mon Sep 17 00:00:00 2001 From: ascibisz Date: Wed, 11 Feb 2026 11:24:44 -0800 Subject: [PATCH 2/4] round radius to 2 decimal points --- src/components/InputSwitch/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/InputSwitch/index.tsx b/src/components/InputSwitch/index.tsx index 952574bb..bc2e1332 100644 --- a/src/components/InputSwitch/index.tsx +++ b/src/components/InputSwitch/index.tsx @@ -49,7 +49,7 @@ const InputSwitch = (props: InputSwitchProps): JSX.Element => { } if (typeof value == "number") { value = value * conversion; - value = Number(value.toFixed(4)); + value = Number(value.toFixed(2)); } return value; }, [getCurrentValue, id, min, conversion, dataType]); From 2006f3484dc18c523ad0d02b9111d25471cb7fd9 Mon Sep 17 00:00:00 2001 From: ascibisz Date: Wed, 11 Feb 2026 11:33:39 -0800 Subject: [PATCH 3/4] update test file to remove unnecessary gradients --- src/test/test-files/ER_peroxisome.json | 48 -------------------------- 1 file changed, 48 deletions(-) diff --git a/src/test/test-files/ER_peroxisome.json b/src/test/test-files/ER_peroxisome.json index 96dad222..b41cb835 100644 --- a/src/test/test-files/ER_peroxisome.json +++ b/src/test/test-files/ER_peroxisome.json @@ -132,54 +132,6 @@ } }, "gradients": { - "apical_gradient": { - "description": "gradient based on distance from a plane", - "invert": null, - "mode": "vector", - "mode_settings": { - "center": [0, 0, 106.875], - "direction": [0, 0, 1] - }, - "name": "apical_gradient", - "pick_mode": "linear", - "reversed": false, - "weight_mode": "exponential", - "weight_mode_settings": { - "decay_length": 0.1 - } - }, - "membrane_gradient": { - "description": "gradient based on distance from the surface of the membrane mesh", - "invert": null, - "mode": "surface", - "mode_settings": { - "object": "membrane", - "scale_to_next_surface": false - }, - "name": "membrane_gradient", - "pick_mode": "linear", - "reversed": false, - "weight_mode": "exponential", - "weight_mode_settings": { - "decay_length": 0.01 - } - }, - "nucleus_gradient": { - "description": "gradient based on distance from the surface of the nucleus mesh", - "invert": null, - "mode": "surface", - "mode_settings": { - "object": "nucleus", - "scale_to_next_surface": false - }, - "name": "nucleus_gradient", - "pick_mode": "linear", - "reversed": false, - "weight_mode": "exponential", - "weight_mode_settings": { - "decay_length": 0.1 - } - }, "struct_gradient": { "name": "struct_gradient", "invert": null, From 90ab06531b677097496057b075911312668ea1f5 Mon Sep 17 00:00:00 2001 From: ascibisz Date: Wed, 11 Feb 2026 14:16:54 -0800 Subject: [PATCH 4/4] copilot suggestions --- src/utils/recipeLoader.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/utils/recipeLoader.ts b/src/utils/recipeLoader.ts index 884b9dfa..a2eea95c 100644 --- a/src/utils/recipeLoader.ts +++ b/src/utils/recipeLoader.ts @@ -328,13 +328,16 @@ const getFirebaseRecipe = async (name: string): Promise => { return unpackedRecipe; } -const recipeToString = (recipe: ViewableRecipe): string => { +const recipeToString = (rec: ViewableRecipe): string => { + // Deep copy recipe to avoid mutating original object + const recipe: ViewableRecipe = structuredClone(rec); + // Collect a list of gradients that are referenced by objects the recipe - const referencedGradients: string[] = []; + const referencedGradients: Set = new Set(); if (recipe.objects) { for (const obj of Object.values(recipe.objects)) { if (obj.packing_mode === "gradient" && obj.gradient) { - referencedGradients.push(obj.gradient); + referencedGradients.add(obj.gradient); } else if (obj.packing_mode === "random" && obj.gradient) { // If packing mode is random, gradient field is irrelevant // and should be cleared @@ -346,7 +349,7 @@ const recipeToString = (recipe: ViewableRecipe): string => { // If the recipe has gradients that aren't referenced in any objects, delete them if (recipe.gradients) { for (const gradientName of Object.keys(recipe.gradients)) { - if (!referencedGradients.includes(gradientName)) { + if (!referencedGradients.has(gradientName)) { delete recipe.gradients[gradientName]; } }