From 23261a7a9896d0d0e189d2230934000a516bd0e1 Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Tue, 20 Aug 2024 20:14:14 -0700 Subject: [PATCH] Add flag for specifying a new node as magnified on insert (#253) --- cmd/wsh/cmd/wshcmd-edit.go | 4 ++++ cmd/wsh/cmd/wshcmd-term.go | 4 ++++ cmd/wsh/cmd/wshcmd-view.go | 4 ++++ frontend/app/store/global.ts | 2 ++ frontend/layout/lib/TileLayout.tsx | 11 +++++------ frontend/layout/lib/layoutModel.ts | 17 ++++++++++------- frontend/layout/lib/layoutModelHooks.ts | 2 ++ frontend/layout/lib/layoutTree.ts | 18 ++++++++---------- frontend/layout/lib/types.ts | 21 ++++++++++++++------- frontend/types/gotypes.d.ts | 4 +++- pkg/eventbus/eventbus.go | 1 + pkg/wshrpc/wshrpctypes.go | 7 ++++--- pkg/wshrpc/wshserver/wshserver.go | 1 + 13 files changed, 62 insertions(+), 34 deletions(-) diff --git a/cmd/wsh/cmd/wshcmd-edit.go b/cmd/wsh/cmd/wshcmd-edit.go index 4640cf9f2..bdb051740 100644 --- a/cmd/wsh/cmd/wshcmd-edit.go +++ b/cmd/wsh/cmd/wshcmd-edit.go @@ -14,6 +14,8 @@ import ( "github.com/wavetermdev/thenextwave/pkg/wshrpc/wshclient" ) +var editMagnified bool + var editCmd = &cobra.Command{ Use: "edit", Short: "edit a file", @@ -23,6 +25,7 @@ var editCmd = &cobra.Command{ } func init() { + editCmd.Flags().BoolVarP(&editMagnified, "magnified", "m", false, "open view in magnified mode") rootCmd.AddCommand(editCmd) } @@ -35,6 +38,7 @@ func editRun(cmd *cobra.Command, args []string) { waveobj.MetaKey_File: fileArg, }, }, + Magnified: editMagnified, } if RpcContext.Conn != "" { wshCmd.BlockDef.Meta[waveobj.MetaKey_Connection] = RpcContext.Conn diff --git a/cmd/wsh/cmd/wshcmd-term.go b/cmd/wsh/cmd/wshcmd-term.go index 52371167c..066e0e942 100644 --- a/cmd/wsh/cmd/wshcmd-term.go +++ b/cmd/wsh/cmd/wshcmd-term.go @@ -14,6 +14,8 @@ import ( "github.com/wavetermdev/thenextwave/pkg/wshrpc/wshclient" ) +var termMagnified bool + var termCmd = &cobra.Command{ Use: "term", Short: "open a terminal in directory", @@ -23,6 +25,7 @@ var termCmd = &cobra.Command{ } func init() { + termCmd.Flags().BoolVarP(&termMagnified, "magnified", "m", false, "open view in magnified mode") rootCmd.AddCommand(termCmd) } @@ -53,6 +56,7 @@ func termRun(cmd *cobra.Command, args []string) { waveobj.MetaKey_Controller: "shell", }, }, + Magnified: termMagnified, } if RpcContext.Conn != "" { createBlockData.BlockDef.Meta[waveobj.MetaKey_Connection] = RpcContext.Conn diff --git a/cmd/wsh/cmd/wshcmd-view.go b/cmd/wsh/cmd/wshcmd-view.go index 2e7192b79..2ac6f8504 100644 --- a/cmd/wsh/cmd/wshcmd-view.go +++ b/cmd/wsh/cmd/wshcmd-view.go @@ -15,6 +15,7 @@ import ( ) var viewNewBlock bool +var viewMagnified bool var viewCmd = &cobra.Command{ Use: "view", @@ -26,6 +27,7 @@ var viewCmd = &cobra.Command{ func init() { viewCmd.Flags().BoolVarP(&viewNewBlock, "newblock", "n", false, "open view in a new block") + viewCmd.Flags().BoolVarP(&viewMagnified, "magnified", "m", false, "open view in magnified mode") rootCmd.AddCommand(viewCmd) } @@ -41,6 +43,7 @@ func viewRun(cmd *cobra.Command, args []string) { waveobj.MetaKey_Url: fileArg, }, }, + Magnified: viewMagnified, } } else { absFile, err := filepath.Abs(fileArg) @@ -64,6 +67,7 @@ func viewRun(cmd *cobra.Command, args []string) { waveobj.MetaKey_File: absFile, }, }, + Magnified: viewMagnified, } if conn != "" { wshCmd.BlockDef.Meta[waveobj.MetaKey_Connection] = conn diff --git a/frontend/app/store/global.ts b/frontend/app/store/global.ts index 23e4f761f..697ac94d0 100644 --- a/frontend/app/store/global.ts +++ b/frontend/app/store/global.ts @@ -277,6 +277,7 @@ function handleWSEventMessage(msg: WSEventType) { node: newLayoutNode(undefined, undefined, undefined, { blockId: layoutAction.blockid, }), + magnified: layoutAction.magnified, }; layoutModel.treeReducer(insertNodeAction); break; @@ -304,6 +305,7 @@ function handleWSEventMessage(msg: WSEventType) { blockId: layoutAction.blockid, }), indexArr: layoutAction.indexarr, + magnified: layoutAction.magnified, }; layoutModel.treeReducer(insertAction); break; diff --git a/frontend/layout/lib/TileLayout.tsx b/frontend/layout/lib/TileLayout.tsx index d0c273afd..fa27edbe6 100644 --- a/frontend/layout/lib/TileLayout.tsx +++ b/frontend/layout/lib/TileLayout.tsx @@ -175,7 +175,6 @@ const DisplayNode = ({ layoutModel, layoutNode, contents }: DisplayNodeProps) => const addlProps = useLayoutNode(layoutModel, layoutNode); const activeDrag = useAtomValue(layoutModel.activeDrag); const globalReady = useAtomValue(layoutModel.ready); - const layoutGeneration = useAtomValue(layoutModel.generationAtom); const devicePixelRatio = useDevicePixelRatio(); @@ -183,12 +182,12 @@ const DisplayNode = ({ layoutModel, layoutNode, contents }: DisplayNodeProps) => () => ({ type: dragItemType, item: () => layoutNode, - canDrag: () => !layoutModel?.treeState?.magnifiedNodeId, + canDrag: () => !addlProps?.isMagnifiedNode, collect: (monitor) => ({ isDragging: monitor.isDragging(), }), }), - [layoutNode, layoutGeneration] + [layoutNode, addlProps] ); const [previewElementGeneration, setPreviewElementGeneration] = useState(0); @@ -249,7 +248,7 @@ const DisplayNode = ({ layoutModel, layoutNode, contents }: DisplayNodeProps) => {contents.renderContent( layoutNode.data, globalReady, - layoutNode.id === layoutModel.treeState.magnifiedNodeId, + addlProps?.isMagnifiedNode ?? false, activeDrag, () => layoutModel.magnifyNodeToggle(layoutNode), () => layoutModel.closeNode(layoutNode), @@ -258,13 +257,13 @@ const DisplayNode = ({ layoutModel, layoutNode, contents }: DisplayNodeProps) => ) ); - }, [layoutNode, globalReady, layoutGeneration, activeDrag, addlProps]); + }, [layoutNode, globalReady, activeDrag, addlProps]); return (
; + /** + * The currently magnified node. + */ + magnifiedNodeId: string; /** * The last node to be magnified, other than the current magnified node, if set. This node should sit at a higher z-index than the others so that it floats above the other nodes as it returns to its original position. */ @@ -314,6 +318,10 @@ export class LayoutModel { } if (stateChanged) { console.log("state changed", this.treeState); + if (this.magnifiedNodeId !== this.treeState.magnifiedNodeId) { + this.lastMagnifiedNodeId = this.magnifiedNodeId; + this.magnifiedNodeId = this.treeState.magnifiedNodeId; + } this.updateTree(); this.treeState.generation++; this.setter(this.treeStateAtom, this.treeState); @@ -393,7 +401,7 @@ export class LayoutModel { leafs.push(node); const addlProps = additionalPropsMap[node.id]; if (addlProps) { - if (this.treeState.magnifiedNodeId === node.id) { + if (this.magnifiedNodeId === node.id) { const boundingRect = getBoundingRect(); const transform = setTransform( { @@ -405,6 +413,7 @@ export class LayoutModel { true ); addlProps.transform = transform; + addlProps.isMagnifiedNode = true; } addlProps.isLastMagnifiedNode = this.lastMagnifiedNodeId === node.id; } @@ -578,12 +587,6 @@ export class LayoutModel { nodeId: node.id, }; - // If the node is already magnified, then it is being un-magnified and should be set as the last-magnified node to ensure it has a higher z-index as it transitions back to its original position. - if (this.treeState.magnifiedNodeId === node.id) { - console.log("new last-magnified-node", node.id); - this.lastMagnifiedNodeId = node.id; - } - this.treeReducer(action); } diff --git a/frontend/layout/lib/layoutModelHooks.ts b/frontend/layout/lib/layoutModelHooks.ts index 5cdbb01af..bd2c236f5 100644 --- a/frontend/layout/lib/layoutModelHooks.ts +++ b/frontend/layout/lib/layoutModelHooks.ts @@ -43,6 +43,8 @@ export function useLayoutModel(tabAtom: Atom): LayoutModel { } export function useTileLayout(tabAtom: Atom, tileContent: TileLayoutContents): LayoutModel { + // Use tab data to ensure we can reload if the tab is disposed and remade (such as during Hot Module Reloading) + useAtomValue(tabAtom); const layoutModel = useLayoutModel(tabAtom); useResizeObserver(layoutModel?.displayContainerRef, layoutModel?.onContainerResize); useEffect(() => layoutModel.registerTileLayout(tileContent), [tileContent]); diff --git a/frontend/layout/lib/layoutTree.ts b/frontend/layout/lib/layoutTree.ts index b7963de17..be115b4dd 100644 --- a/frontend/layout/lib/layoutTree.ts +++ b/frontend/layout/lib/layoutTree.ts @@ -5,7 +5,6 @@ import { lazy } from "@/util/util"; import { addChildAt, addIntermediateNode, - balanceNode, findInsertLocationFromIndexArr, findNextInsertLocation, findNode, @@ -256,8 +255,6 @@ export function moveNode(layoutState: LayoutTreeState, action: LayoutTreeMoveNod if (oldParent) { removeChild(oldParent, node, startingIndex); } - - layoutState.rootNode = balanceNode(layoutState.rootNode); } export function insertNode(layoutState: LayoutTreeState, action: LayoutTreeInsertNodeAction) { @@ -266,12 +263,14 @@ export function insertNode(layoutState: LayoutTreeState, action: LayoutTreeInser return; } if (!layoutState.rootNode) { - layoutState.rootNode = balanceNode(action.node); + layoutState.rootNode = action.node; return; } const insertLoc = findNextInsertLocation(layoutState.rootNode, 5); addChildAt(insertLoc.node, insertLoc.index, action.node); - layoutState.rootNode = balanceNode(layoutState.rootNode); + if (action.magnified) { + layoutState.magnifiedNodeId = action.node.id; + } } export function insertNodeAtIndex(layoutState: LayoutTreeState, action: LayoutTreeInsertNodeAtIndexAction) { @@ -280,7 +279,7 @@ export function insertNodeAtIndex(layoutState: LayoutTreeState, action: LayoutTr return; } if (!layoutState.rootNode) { - layoutState.rootNode = balanceNode(action.node); + layoutState.rootNode = action.node; return; } const insertLoc = findInsertLocationFromIndexArr(layoutState.rootNode, action.indexArr); @@ -289,7 +288,9 @@ export function insertNodeAtIndex(layoutState: LayoutTreeState, action: LayoutTr return; } addChildAt(insertLoc.node, insertLoc.index + 1, action.node); - layoutState.rootNode = balanceNode(layoutState.rootNode); + if (action.magnified) { + layoutState.magnifiedNodeId = action.node.id; + } } export function swapNode(layoutState: LayoutTreeState, action: LayoutTreeSwapNodeAction) { @@ -323,8 +324,6 @@ export function swapNode(layoutState: LayoutTreeState, action: LayoutTreeSwapNod parentNode1.children[parentNode1Index] = node2; parentNode2.children[parentNode2Index] = node1; - - layoutState.rootNode = balanceNode(layoutState.rootNode); } export function deleteNode(layoutState: LayoutTreeState, action: LayoutTreeDeleteNodeAction) { @@ -349,7 +348,6 @@ export function deleteNode(layoutState: LayoutTreeState, action: LayoutTreeDelet } else { console.error("unable to delete node, not found in tree"); } - layoutState.rootNode = balanceNode(layoutState.rootNode); } export function resizeNode(layoutState: LayoutTreeState, action: LayoutTreeResizeNodeAction) { diff --git a/frontend/layout/lib/types.ts b/frontend/layout/lib/types.ts index 01ec0e07e..e18001e2f 100644 --- a/frontend/layout/lib/types.ts +++ b/frontend/layout/lib/types.ts @@ -93,24 +93,30 @@ export interface LayoutTreeSwapNodeAction extends LayoutTreeAction { node2Id: string; } +interface InsertNodeOperation { + /** + * The node to insert. + */ + node: LayoutNode; + /** + * Whether the inserted node should be magnified. + */ + magnified?: boolean; +} + /** * Action for inserting a new node to the layout tree. * */ -export interface LayoutTreeInsertNodeAction extends LayoutTreeAction { +export interface LayoutTreeInsertNodeAction extends LayoutTreeAction, InsertNodeOperation { type: LayoutTreeActionType.InsertNode; - node: LayoutNode; } /** * Action for inserting a node into the layout tree at the specified index. */ -export interface LayoutTreeInsertNodeAtIndexAction extends LayoutTreeAction { +export interface LayoutTreeInsertNodeAtIndexAction extends LayoutTreeAction, InsertNodeOperation { type: LayoutTreeActionType.InsertNodeAtIndex; - /** - * The node to insert. - */ - node: LayoutNode; /** * The array of indices to traverse when inserting the node. * The last index is the index within the parent node where the node should be inserted. @@ -274,5 +280,6 @@ export interface LayoutNodeAdditionalProps { rect?: Dimensions; pixelToSizeRatio?: number; resizeHandles?: ResizeHandleProps[]; + isMagnifiedNode?: boolean; isLastMagnifiedNode?: boolean; } diff --git a/frontend/types/gotypes.d.ts b/frontend/types/gotypes.d.ts index 2fd95895d..df52f7872 100644 --- a/frontend/types/gotypes.d.ts +++ b/frontend/types/gotypes.d.ts @@ -95,7 +95,8 @@ declare global { type CommandCreateBlockData = { tabid: string; blockdef: BlockDef; - rtopts: RuntimeOpts; + rtopts?: RuntimeOpts; + magnified?: boolean; }; // wshrpc.CommandDeleteBlockData @@ -522,6 +523,7 @@ declare global { blockid: string; nodesize?: number; indexarr?: number[]; + magnified?: boolean; }; // webcmd.WSRpcCommand diff --git a/pkg/eventbus/eventbus.go b/pkg/eventbus/eventbus.go index 7c63a31ef..6353aa809 100644 --- a/pkg/eventbus/eventbus.go +++ b/pkg/eventbus/eventbus.go @@ -62,6 +62,7 @@ type WSLayoutActionData struct { BlockId string `json:"blockid"` NodeSize uint `json:"nodesize,omitempty"` IndexArr []int `json:"indexarr,omitempty"` + Magnified bool `json:"magnified,omitempty"` } var globalLock = &sync.Mutex{} diff --git a/pkg/wshrpc/wshrpctypes.go b/pkg/wshrpc/wshrpctypes.go index 875fb0e21..dcd7d7cb4 100644 --- a/pkg/wshrpc/wshrpctypes.go +++ b/pkg/wshrpc/wshrpctypes.go @@ -185,9 +185,10 @@ type CommandResolveIdsRtnData struct { } type CommandCreateBlockData struct { - TabId string `json:"tabid" wshcontext:"TabId"` - BlockDef *waveobj.BlockDef `json:"blockdef"` - RtOpts *waveobj.RuntimeOpts `json:"rtopts"` + TabId string `json:"tabid" wshcontext:"TabId"` + BlockDef *waveobj.BlockDef `json:"blockdef"` + RtOpts *waveobj.RuntimeOpts `json:"rtopts,omitempty"` + Magnified bool `json:"magnified,omitempty"` } type CommandBlockSetViewData struct { diff --git a/pkg/wshrpc/wshserver/wshserver.go b/pkg/wshrpc/wshserver/wshserver.go index 498ea3e1c..392f41342 100644 --- a/pkg/wshrpc/wshserver/wshserver.go +++ b/pkg/wshrpc/wshserver/wshserver.go @@ -293,6 +293,7 @@ func (ws *WshServer) CreateBlockCommand(ctx context.Context, data wshrpc.Command ActionType: "insert", TabId: tabId, BlockId: blockData.OID, + Magnified: data.Magnified, }, }) return &waveobj.ORef{OType: waveobj.OType_Block, OID: blockData.OID}, nil