fix: make document context reactive

This commit is contained in:
Smit 2025-06-28 03:35:41 +05:30
parent f1e0af6595
commit 692b17ffd3
5 changed files with 48 additions and 128 deletions

View file

@ -10,7 +10,7 @@
import { createPanicManager } from "@graphite/io-managers/panic";
import { createPersistenceManager } from "@graphite/io-managers/persistence";
import { createDialogState } from "@graphite/state-providers/dialog";
import { createDocumentState } from "@graphite/state-providers/document";
import { createDocumentState } from "@graphite/state-providers/document.svelte";
import { createFontsState } from "@graphite/state-providers/fonts";
import { createFullscreenState } from "@graphite/state-providers/fullscreen";
import { createNodeGraphState } from "@graphite/state-providers/node-graph";

View file

@ -16,7 +16,7 @@
UpdateMouseCursor,
isWidgetSpanRow,
} from "@graphite/messages.svelte";
import type { DocumentState } from "@graphite/state-providers/document";
import type { DocumentState } from "@graphite/state-providers/document.svelte";
import { textInputCleanup } from "@graphite/utility-functions/keyboard-entry";
import { extractPixelData, rasterizeSVGCanvas } from "@graphite/utility-functions/rasterization";
import { updateBoundsOfViewports } from "@graphite/utility-functions/viewports";
@ -124,7 +124,7 @@
totalToolRowsFor2Columns,
totalToolRowsFor3Columns,
};
})($document.toolShelfLayout.layout[0]));
})(document.toolShelfLayout.layout[0]));
function dropFile(e: DragEvent) {
const { dataTransfer } = e;
@ -462,14 +462,14 @@
</script>
<LayoutCol class="document" ondragover={(e) => e.preventDefault()} ondrop={dropFile}>
<LayoutRow class="control-bar" classes={{ "for-graph": $document.graphViewOverlayOpen }} scrollableX={true}>
{#if !$document.graphViewOverlayOpen}
<WidgetLayout layout={$document.documentModeLayout} />
<WidgetLayout layout={$document.toolOptionsLayout} />
<LayoutRow class="control-bar" classes={{ "for-graph": document.graphViewOverlayOpen }} scrollableX={true}>
{#if !document.graphViewOverlayOpen}
<WidgetLayout layout={document.documentModeLayout} />
<WidgetLayout layout={document.toolOptionsLayout} />
<LayoutRow class="spacer" />
<WidgetLayout layout={$document.documentBarLayout} />
<WidgetLayout layout={document.documentBarLayout} />
{:else}
<WidgetLayout layout={$document.nodeGraphControlBarLayout} />
<WidgetLayout layout={document.nodeGraphControlBarLayout} />
{/if}
</LayoutRow>
<LayoutRow
@ -482,15 +482,15 @@
}}
>
<LayoutCol class="tool-shelf">
{#if !$document.graphViewOverlayOpen}
{#if !document.graphViewOverlayOpen}
<LayoutCol class="tools" scrollableY={true}>
<WidgetLayout layout={$document.toolShelfLayout} />
<WidgetLayout layout={document.toolShelfLayout} />
</LayoutCol>
{:else}
<LayoutRow class="spacer" />
{/if}
<LayoutCol class="tool-shelf-bottom-widgets">
<WidgetLayout class={"working-colors-input-area"} layout={$document.workingColorsLayout} />
<WidgetLayout class={"working-colors-input-area"} layout={document.workingColorsLayout} />
</LayoutCol>
</LayoutCol>
<LayoutCol class="viewport-container">
@ -536,7 +536,7 @@
>
</canvas>
</div>
<div class="graph-view" class:open={$document.graphViewOverlayOpen} style:--fade-artwork={`${$document.fadeArtwork}%`} data-graph>
<div class="graph-view" class:open={document.graphViewOverlayOpen} style:--fade-artwork={`${document.fadeArtwork}%`} data-graph>
<Graph />
</div>
</LayoutCol>

View file

@ -3,7 +3,8 @@ import { get } from "svelte/store";
import { type Editor } from "@graphite/editor";
import { TriggerPaste } from "@graphite/messages.svelte";
import { type DialogState } from "@graphite/state-providers/dialog";
import { type DocumentState } from "@graphite/state-providers/document";
import { type DocumentState } from "@graphite/state-providers/document.svelte";
import { documentContextState } from "@graphite/state-providers/document.svelte";
import { type FullscreenState } from "@graphite/state-providers/fullscreen";
import { type PortfolioState } from "@graphite/state-providers/portfolio";
import { makeKeyboardModifiersBitfield, textInputCleanup, getLocalizedScanCode } from "@graphite/utility-functions/keyboard-entry";
@ -155,7 +156,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
// TODO: This would allow it to properly decide to act on removing hover focus from something that was hovered in the canvas before moving over the GUI.
// TODO: Further explanation: https://github.com/GraphiteEditor/Graphite/pull/623#discussion_r866436197
const inFloatingMenu = e.target instanceof Element && e.target.closest("[data-floating-menu-content]");
const inGraphOverlay = get(document).graphViewOverlayOpen;
const inGraphOverlay = documentContextState.graphViewOverlayOpen;
if (!viewportPointerInteractionOngoing && (inFloatingMenu || inGraphOverlay)) return;
const modifiers = makeKeyboardModifiersBitfield(e);

View file

@ -1495,62 +1495,6 @@ export function patchWidgetLayout(layout: /* &mut */ WidgetLayout, updates: Widg
});
}
// export function patchWidgetLayout(layout: /* &mut */ WidgetLayout, updates: WidgetDiffUpdate) {
// // Use Reflect.set to ensure proxy reactivity
// Reflect.set(layout, "layoutTarget", updates.layoutTarget);
// updates.diff.forEach((update) => {
// // Find the object where the diff applies to
// let diffObject = update.widgetPath.reduce((targetLayout, index) => {
// // Use Reflect.get to ensure we're going through the proxy
// if (Reflect.has(targetLayout, "columnWidgets")) {
// return Reflect.get(Reflect.get(targetLayout, "columnWidgets"), index);
// }
// if (Reflect.has(targetLayout, "rowWidgets")) {
// return Reflect.get(Reflect.get(targetLayout, "rowWidgets"), index);
// }
// if (Reflect.has(targetLayout, "tableWidgets")) {
// return Reflect.get(Reflect.get(targetLayout, "tableWidgets"), index);
// }
// if (Reflect.has(targetLayout, "layout")) {
// return Reflect.get(Reflect.get(targetLayout, "layout"), index);
// }
// if (targetLayout instanceof Widget) {
// if (targetLayout.props.kind === "PopoverButton" && targetLayout.props instanceof PopoverButton && targetLayout.props.popoverLayout) {
// return Reflect.get(targetLayout.props.popoverLayout, index);
// }
// console.error("Tried to index widget");
// return targetLayout;
// }
// if (Reflect.has(targetLayout, "action")) {
// const children = Reflect.get(targetLayout, "children");
// return children ? Reflect.get(children, index) : undefined;
// }
// return Reflect.get(targetLayout, index);
// }, layout.layout as UIItem);
// // Clear array length using Reflect to trigger reactivity
// if (Reflect.has(diffObject, "length")) {
// Reflect.set(diffObject, "length", 0);
// }
// // Remove all keys using Reflect.deleteProperty to ensure proxy notifications
// Reflect.ownKeys(diffObject).forEach((key) => {
// if (key !== "length") {
// // Don't delete length property on arrays
// Reflect.deleteProperty(diffObject, key);
// }
// });
// // Assign new properties using Reflect.set
// if (update.newValue && typeof update.newValue === "object") {
// Object.entries(update.newValue).forEach(([key, value]) => {
// Reflect.set(diffObject, key, value);
// });
// }
// });
// }
export type LayoutGroup = WidgetSpanRow | WidgetSpanColumn | WidgetTable | WidgetSection;
export type WidgetSpanColumn = { columnWidgets: Widget[] };

View file

@ -1,5 +1,4 @@
import { tick } from "svelte";
import { writable } from "svelte/store";
import { type Editor } from "@graphite/editor";
@ -17,83 +16,59 @@ import {
UpdateGraphFadeArtwork,
} from "@graphite/messages.svelte";
export const documentContextState = $state({
// Layouts - these are already $state objects from defaultWidgetLayout()
documentModeLayout: defaultWidgetLayout(),
toolOptionsLayout: defaultWidgetLayout(),
documentBarLayout: defaultWidgetLayout(),
toolShelfLayout: defaultWidgetLayout(),
workingColorsLayout: defaultWidgetLayout(),
nodeGraphControlBarLayout: defaultWidgetLayout(),
// Graph view overlay
graphViewOverlayOpen: false,
fadeArtwork: 100,
});
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function createDocumentState(editor: Editor) {
const state = writable({
// Layouts
documentModeLayout: defaultWidgetLayout(),
toolOptionsLayout: defaultWidgetLayout(),
documentBarLayout: defaultWidgetLayout(),
toolShelfLayout: defaultWidgetLayout(),
workingColorsLayout: defaultWidgetLayout(),
nodeGraphControlBarLayout: defaultWidgetLayout(),
// Graph view overlay
graphViewOverlayOpen: false,
fadeArtwork: 100,
});
const { subscribe, update } = state;
// Update layouts
// Set up subscriptions (these run once when the function is called)
editor.subscriptions.subscribeJsMessage(UpdateGraphFadeArtwork, (updateGraphFadeArtwork) => {
update((state) => {
state.fadeArtwork = updateGraphFadeArtwork.percentage;
return state;
});
documentContextState.fadeArtwork = updateGraphFadeArtwork.percentage;
});
editor.subscriptions.subscribeJsMessage(UpdateDocumentModeLayout, async (updateDocumentModeLayout) => {
await tick();
update((state) => {
patchWidgetLayout(state.documentModeLayout, updateDocumentModeLayout);
return state;
});
patchWidgetLayout(documentContextState.documentModeLayout, updateDocumentModeLayout);
});
editor.subscriptions.subscribeJsMessage(UpdateToolOptionsLayout, async (updateToolOptionsLayout) => {
await tick();
update((state) => {
patchWidgetLayout(state.toolOptionsLayout, updateToolOptionsLayout);
return state;
});
patchWidgetLayout(documentContextState.toolOptionsLayout, updateToolOptionsLayout);
});
editor.subscriptions.subscribeJsMessage(UpdateDocumentBarLayout, async (updateDocumentBarLayout) => {
await tick();
update((state) => {
patchWidgetLayout(state.documentBarLayout, updateDocumentBarLayout);
return state;
});
patchWidgetLayout(documentContextState.documentBarLayout, updateDocumentBarLayout);
});
editor.subscriptions.subscribeJsMessage(UpdateToolShelfLayout, async (updateToolShelfLayout) => {
await tick();
update((state) => {
patchWidgetLayout(state.toolShelfLayout, updateToolShelfLayout);
return state;
});
patchWidgetLayout(documentContextState.toolShelfLayout, updateToolShelfLayout);
});
editor.subscriptions.subscribeJsMessage(UpdateWorkingColorsLayout, async (updateWorkingColorsLayout) => {
await tick();
update((state) => {
patchWidgetLayout(state.workingColorsLayout, updateWorkingColorsLayout);
return state;
});
patchWidgetLayout(documentContextState.workingColorsLayout, updateWorkingColorsLayout);
});
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphControlBarLayout, (updateNodeGraphControlBarLayout) => {
update((state) => {
patchWidgetLayout(state.nodeGraphControlBarLayout, updateNodeGraphControlBarLayout);
return state;
});
patchWidgetLayout(documentContextState.nodeGraphControlBarLayout, updateNodeGraphControlBarLayout);
});
// Show or hide the graph view overlay
editor.subscriptions.subscribeJsMessage(UpdateGraphViewOverlay, (updateGraphViewOverlay) => {
update((state) => {
state.graphViewOverlayOpen = updateGraphViewOverlay.open;
return state;
});
documentContextState.graphViewOverlayOpen = updateGraphViewOverlay.open;
});
editor.subscriptions.subscribeJsMessage(TriggerDelayedZoomCanvasToFitAll, () => {
// TODO: This is horribly hacky
[0, 1, 10, 50, 100, 200, 300, 400, 500].forEach((delay) => {
@ -101,8 +76,8 @@ export function createDocumentState(editor: Editor) {
});
});
return {
subscribe,
};
// Return the reactive state object directly
return documentContextState;
}
export type DocumentState = ReturnType<typeof createDocumentState>;