import type { Layout, LayoutGroup, WidgetDiff, WidgetInstance } from "/wrapper/pkg/graphite_wasm_wrapper"; type UIItem = Layout | LayoutGroup | WidgetInstance[][] | WidgetInstance[] | WidgetInstance; // Updates a widget layout based on a list of updates, giving the new layout by mutating the `layout` argument export function patchLayout(layout: /* &mut */ Layout, diffs: WidgetDiff[]) { diffs.forEach((update) => { // Extract the actual content from the DiffUpdate tagged enum const { newValue } = update; let newContent: Layout | LayoutGroup | WidgetInstance; if ("layout" in newValue) newContent = newValue.layout; else if ("layoutGroup" in newValue) newContent = newValue.layoutGroup; else if ("widget" in newValue) newContent = newValue.widget; else throw new Error("DiffUpdate invalid"); // Find the object where the diff applies to const diffObject = update.widgetPath.reduce((targetLayout, index: bigint): UIItem | undefined => { const i = Number(index); if (targetLayout && "Column" in targetLayout) return targetLayout.Column.columnWidgets[i]; if (targetLayout && "Row" in targetLayout) return targetLayout.Row.rowWidgets[i]; if (targetLayout && "Table" in targetLayout) return targetLayout.Table.tableWidgets[i]; if (targetLayout && "Section" in targetLayout) return targetLayout.Section.layout[i]; if (targetLayout && "widget" in targetLayout && "widgetId" in targetLayout) { if ("PopoverButton" in targetLayout.widget && targetLayout.widget.PopoverButton.popoverLayout) { return targetLayout.widget.PopoverButton.popoverLayout[i]; } // eslint-disable-next-line no-console console.error("Tried to index widget"); return targetLayout; } return targetLayout?.[i]; }, layout); // Exit if we failed to produce a valid patch for the existing layout. // This means that the backend assumed an existing layout that doesn't exist in the frontend. This can happen, for // example, if a panel is destroyed in the frontend but was never cleared in the backend, so the next time the backend // tries to update the layout, it attempts to insert only the changes against the old layout that no longer exists. if (diffObject === undefined) { // eslint-disable-next-line no-console console.error("In `patchLayout`, the `diffObject` is undefined. The layout has not been updated. See the source code comment above this error for hints."); return; } // If this is a list with a length, then set the length to 0 to clear the list if ("length" in diffObject) { diffObject.length = 0; } // Remove all of the keys from the old object Object.keys(diffObject).forEach((key) => Reflect.deleteProperty(diffObject, key)); // Assign keys to the new object // `Object.assign` works but `diffObject = update.newValue;` doesn't. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign Object.assign(diffObject, newContent); }); }