From c2b3978d4f0ac1f01c6474cb36c6d266ad80aeae Mon Sep 17 00:00:00 2001 From: Laila Los <44241786+ElectronicBlueberry@users.noreply.github.com> Date: Mon, 6 May 2024 16:56:33 +0200 Subject: [PATCH 01/17] add stack ui --- .../src/components/UndoRedo/UndoRedoStack.vue | 41 +++++++++++++++++++ client/src/stores/undoRedoStore/index.ts | 35 ++++++++++++++++ .../stores/undoRedoStore/undoRedoAction.ts | 7 ++++ 3 files changed, 83 insertions(+) create mode 100644 client/src/components/UndoRedo/UndoRedoStack.vue diff --git a/client/src/components/UndoRedo/UndoRedoStack.vue b/client/src/components/UndoRedo/UndoRedoStack.vue new file mode 100644 index 000000000000..5a4c15480e03 --- /dev/null +++ b/client/src/components/UndoRedo/UndoRedoStack.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/client/src/stores/undoRedoStore/index.ts b/client/src/stores/undoRedoStore/index.ts index da8c354e513f..2c7fc7773f76 100644 --- a/client/src/stores/undoRedoStore/index.ts +++ b/client/src/stores/undoRedoStore/index.ts @@ -8,6 +8,15 @@ export { LazyUndoRedoAction, UndoRedoAction } from "./undoRedoAction"; export type UndoRedoStore = ReturnType; +export class ActionOutOfBoundsError extends Error { + public action: UndoRedoAction; + + constructor(action: UndoRedoAction, bounds: "undo" | "redo") { + super(`The action "${action.name}" is not in the ${bounds} stack`); + this.action = action; + } +} + export const useUndoRedoStore = defineScopedStore("undoRedoStore", () => { const undoActionStack = ref([]); const redoActionStack = ref([]); @@ -141,6 +150,30 @@ export const useUndoRedoStore = defineScopedStore("undoRedoStore", () => { } }); + function rollBackTo(action: UndoRedoAction) { + const undoSet = new Set(undoActionStack.value); + + if (!undoSet.has(action)) { + throw new ActionOutOfBoundsError(action, "undo"); + } + + while (nextRedoAction.value !== action) { + undo(); + } + } + + function rollForwardTo(action: UndoRedoAction) { + const redoSet = new Set(redoActionStack.value); + + if (!redoSet.has(action)) { + throw new ActionOutOfBoundsError(action, "redo"); + } + + while (nextUndoAction.value !== action) { + redo(); + } + } + return { undoActionStack, redoActionStack, @@ -162,6 +195,8 @@ export const useUndoRedoStore = defineScopedStore("undoRedoStore", () => { hasUndo, hasRedo, $reset, + rollBackTo, + rollForwardTo, }; }); diff --git a/client/src/stores/undoRedoStore/undoRedoAction.ts b/client/src/stores/undoRedoStore/undoRedoAction.ts index 63a54f5e4ff4..88a42a5a0971 100644 --- a/client/src/stores/undoRedoStore/undoRedoAction.ts +++ b/client/src/stores/undoRedoStore/undoRedoAction.ts @@ -1,5 +1,12 @@ +let idCounter = 0; + export class UndoRedoAction { protected internalName?: string; + public id: number; + + constructor() { + this.id = idCounter++; + } get name(): string | undefined { return this.internalName; From bf3b7353958d8fd3c34f88d272b5ba8edf884f15 Mon Sep 17 00:00:00 2001 From: Laila Los <44241786+ElectronicBlueberry@users.noreply.github.com> Date: Mon, 6 May 2024 17:23:49 +0200 Subject: [PATCH 02/17] allow clicking actions to change current state --- .../src/components/UndoRedo/UndoRedoStack.vue | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/client/src/components/UndoRedo/UndoRedoStack.vue b/client/src/components/UndoRedo/UndoRedoStack.vue index 5a4c15480e03..8166433ba945 100644 --- a/client/src/components/UndoRedo/UndoRedoStack.vue +++ b/client/src/components/UndoRedo/UndoRedoStack.vue @@ -1,17 +1,12 @@ + diff --git a/client/src/stores/undoRedoStore/index.ts b/client/src/stores/undoRedoStore/index.ts index 2c7fc7773f76..ea5ea277c3d4 100644 --- a/client/src/stores/undoRedoStore/index.ts +++ b/client/src/stores/undoRedoStore/index.ts @@ -151,6 +151,7 @@ export const useUndoRedoStore = defineScopedStore("undoRedoStore", () => { }); function rollBackTo(action: UndoRedoAction) { + flushLazyAction(); const undoSet = new Set(undoActionStack.value); if (!undoSet.has(action)) { @@ -163,6 +164,7 @@ export const useUndoRedoStore = defineScopedStore("undoRedoStore", () => { } function rollForwardTo(action: UndoRedoAction) { + flushLazyAction(); const redoSet = new Set(redoActionStack.value); if (!redoSet.has(action)) { From 98696a5bffa546ba82437198f6bd707f0efe6a53 Mon Sep 17 00:00:00 2001 From: Laila Los <44241786+ElectronicBlueberry@users.noreply.github.com> Date: Mon, 15 Jul 2024 11:36:10 +0200 Subject: [PATCH 04/17] move changes button to top bar --- client/src/components/Workflow/Editor/Index.vue | 13 +++++++++---- client/src/components/Workflow/Editor/Options.vue | 13 +------------ 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/client/src/components/Workflow/Editor/Index.vue b/client/src/components/Workflow/Editor/Index.vue index b336174caf0a..74cdc9382529 100644 --- a/client/src/components/Workflow/Editor/Index.vue +++ b/client/src/components/Workflow/Editor/Index.vue @@ -59,6 +59,12 @@ @click="undoRedoStore.redo()"> + + + + @onUpgrade="onUpgrade" />
@@ -182,7 +187,7 @@ diff --git a/client/src/composables/math.ts b/client/src/composables/math.ts new file mode 100644 index 000000000000..7703c203f1fe --- /dev/null +++ b/client/src/composables/math.ts @@ -0,0 +1,49 @@ +/** + * There are similar functions to those in this module in vue-use, but they only work one-way. + * Unlike vue-use, these composables return refs which can be set. + */ + +import { type MaybeRefOrGetter, toValue } from "@vueuse/core"; +import { computed, type Ref } from "vue"; + +/** + * Wraps a number ref, restricting it's values to a given range + * + * @param ref ref containing a number to wrap + * @param min lowest possible value of range + * @param max highest possible value of range + * @returns clamped ref + */ +export function useClamp(ref: Ref, min: MaybeRefOrGetter, max: MaybeRefOrGetter): Ref { + const clamp = (value: number) => { + return Math.min(Math.max(value, toValue(min)), toValue(max)); + }; + + const clampedRef = computed({ + get: () => clamp(ref.value), + set: (value) => (ref.value = clamp(value)), + }); + + return clampedRef; +} + +/** + * Wraps a number ref, restricting it's values to align to a given step size + * + * @param ref ref containing a number to wrap + * @param stepSize size of steps to restrict value to + * @returns wrapped red + */ +export function useStep(ref: Ref, stepSize: MaybeRefOrGetter = 1): Ref { + const step = (value: number) => { + const stepSizeValue = toValue(stepSize); + return Math.round(value / stepSizeValue) * stepSizeValue; + }; + + const steppedRef = computed({ + get: () => step(ref.value), + set: (value) => (ref.value = step(value)), + }); + + return steppedRef; +} diff --git a/client/src/stores/undoRedoStore/index.ts b/client/src/stores/undoRedoStore/index.ts index e0bcb3577266..e1f4c9bf65cb 100644 --- a/client/src/stores/undoRedoStore/index.ts +++ b/client/src/stores/undoRedoStore/index.ts @@ -1,5 +1,6 @@ import { computed, ref } from "vue"; +import { useClamp, useStep } from "@/composables/math"; import { useUserLocalStorage } from "@/composables/userLocalStorage"; import { defineScopedStore } from "@/stores/scopedStore"; @@ -22,9 +23,13 @@ export const useUndoRedoStore = defineScopedStore("undoRedoStore", () => { const undoActionStack = ref([]); const redoActionStack = ref([]); - const maxUndoActions = useUserLocalStorage(`undoRedoStore-maxUndoActions`, 100); + const minUndoActions = ref(10); + const maxUndoActions = ref(10000); - /** names of actions which were deleted due to maxUndoActions being exceeded */ + const savedUndoActionsValue = useUserLocalStorage(`undoRedoStore-savedUndoActions`, 100); + const savedUndoActions = useClamp(useStep(savedUndoActionsValue), minUndoActions, maxUndoActions); + + /** names of actions which were deleted due to savedUndoActions being exceeded */ const deletedActions = ref([]); function $reset() { @@ -59,7 +64,7 @@ export const useUndoRedoStore = defineScopedStore("undoRedoStore", () => { clearRedoStack(); undoActionStack.value.push(action); - while (undoActionStack.value.length > maxUndoActions.value && undoActionStack.value.length > 0) { + while (undoActionStack.value.length > savedUndoActions.value && undoActionStack.value.length > 0) { const action = undoActionStack.value.shift(); deletedActions.value.push(action?.name ?? "unnamed action"); action?.destroy(); @@ -186,7 +191,9 @@ export const useUndoRedoStore = defineScopedStore("undoRedoStore", () => { return { undoActionStack, redoActionStack, + minUndoActions, maxUndoActions, + savedUndoActions, deletedActions, undo, redo, From 15b353e660b8d1fc1cc6352e1003412ea7750a98 Mon Sep 17 00:00:00 2001 From: Laila Los <44241786+ElectronicBlueberry@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:15:05 +0200 Subject: [PATCH 16/17] make best practice panel hide changes --- client/src/components/Workflow/Editor/Index.vue | 1 + client/src/composables/math.ts | 2 +- client/src/stores/undoRedoStore/index.ts | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/client/src/components/Workflow/Editor/Index.vue b/client/src/components/Workflow/Editor/Index.vue index c0ef58b0406a..1258bbca0a0a 100644 --- a/client/src/components/Workflow/Editor/Index.vue +++ b/client/src/components/Workflow/Editor/Index.vue @@ -798,6 +798,7 @@ export default { this.ensureParametersSet(); this.stateStore.activeNodeId = null; this.showInPanel = "lint"; + this.showChanges = false; }, onUpgrade() { this.onAttemptRefactor([{ action_type: "upgrade_all_steps" }]); diff --git a/client/src/composables/math.ts b/client/src/composables/math.ts index 7703c203f1fe..421a9ca24aa5 100644 --- a/client/src/composables/math.ts +++ b/client/src/composables/math.ts @@ -31,7 +31,7 @@ export function useClamp(ref: Ref, min: MaybeRefOrGetter, max: M * Wraps a number ref, restricting it's values to align to a given step size * * @param ref ref containing a number to wrap - * @param stepSize size of steps to restrict value to + * @param [stepSize = 1] size of steps to restrict value to. defaults to 1 * @returns wrapped red */ export function useStep(ref: Ref, stepSize: MaybeRefOrGetter = 1): Ref { diff --git a/client/src/stores/undoRedoStore/index.ts b/client/src/stores/undoRedoStore/index.ts index e1f4c9bf65cb..f83dd6f6d103 100644 --- a/client/src/stores/undoRedoStore/index.ts +++ b/client/src/stores/undoRedoStore/index.ts @@ -36,6 +36,8 @@ export const useUndoRedoStore = defineScopedStore("undoRedoStore", () => { undoActionStack.value.forEach((action) => action.destroy()); undoActionStack.value = []; deletedActions.value = []; + minUndoActions.value = 10; + maxUndoActions.value = 10000; clearRedoStack(); } From bb43a51ef5661c0e10381116e76e3b7bb17c3291 Mon Sep 17 00:00:00 2001 From: Laila Los <44241786+ElectronicBlueberry@users.noreply.github.com> Date: Fri, 2 Aug 2024 14:32:55 +0200 Subject: [PATCH 17/17] add info --- client/src/components/UndoRedo/UndoRedoStack.vue | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/client/src/components/UndoRedo/UndoRedoStack.vue b/client/src/components/UndoRedo/UndoRedoStack.vue index 33d7e4f13cde..e0b89b1cbe73 100644 --- a/client/src/components/UndoRedo/UndoRedoStack.vue +++ b/client/src/components/UndoRedo/UndoRedoStack.vue @@ -72,6 +72,8 @@ function updateSavedUndoActions() { start of session
+ click an action to undo/redo multiple changes +