From f3f272a47b07c4888f21509499ca1c9aace2bc3c Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Tue, 30 Jul 2024 10:59:53 -0700 Subject: [PATCH] Add action for magnifying a block (#172) Adds the implementation for the "Magnify Block" context menu item. This will pop a block out of the layout and bring it to the foreground. This also cleans up some block styling to make radii more consistent. image --- frontend/app/block/block.less | 12 +- frontend/app/block/block.tsx | 89 ++++++---- frontend/app/tab/tabcontent.tsx | 6 +- frontend/app/theme.less | 2 + frontend/layout/lib/TileLayout.tsx | 265 ++++++++++++++++++---------- frontend/layout/lib/layoutAtom.ts | 13 +- frontend/layout/lib/layoutState.ts | 22 +++ frontend/layout/lib/model.ts | 16 ++ frontend/layout/lib/tilelayout.less | 12 +- frontend/types/gotypes.d.ts | 3 +- pkg/wstore/wstore_types.go | 11 +- 11 files changed, 295 insertions(+), 156 deletions(-) diff --git a/frontend/app/block/block.less b/frontend/app/block/block.less index a13a6f0fb..00445f572 100644 --- a/frontend/app/block/block.less +++ b/frontend/app/block/block.less @@ -11,7 +11,7 @@ overflow: hidden; min-height: 0; position: relative; - border-radius: 8px; + border-radius: var(--block-border-radius); .block-frame-icon { margin-right: 0.5em; @@ -62,7 +62,7 @@ background-color: var(--block-bg-color); width: 100%; height: 100%; - border-radius: 8px; + border-radius: var(--block-border-radius); display: flex; flex-direction: column; @@ -75,7 +75,7 @@ align-self: stretch; font: var(--header-font); border-bottom: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 8px 8px 0 0; + border-radius: var(--block-border-radius) var(--block-border-radius) 0 0; .block-frame-preicon-button { opacity: 0.7; @@ -251,8 +251,8 @@ background-color: rgba(0, 0, 0, 0.7); width: 100%; flex-grow: 1; - border-bottom-left-radius: 8px; - border-bottom-right-radius: 8px; + border-bottom-left-radius: var(--block-border-radius); + border-bottom-right-radius: var(--block-border-radius); } } @@ -265,7 +265,7 @@ border: 2px solid transparent; pointer-events: none; padding: 2px; - border-radius: 8px; + border-radius: var(--block-border-radius); } &.block-focused { diff --git a/frontend/app/block/block.tsx b/frontend/app/block/block.tsx index 01e1650a1..8fc09edfe 100644 --- a/frontend/app/block/block.tsx +++ b/frontend/app/block/block.tsx @@ -21,8 +21,9 @@ import * as React from "react"; import "./block.less"; -interface LayoutComponentModel { +export interface LayoutComponentModel { onClose?: () => void; + onMagnifyToggle?: () => void; dragHandleRef?: React.RefObject; } @@ -163,51 +164,52 @@ function handleHeaderContextMenu( e: React.MouseEvent, blockData: Block, viewModel: ViewModel, + onMagnifyToggle: () => void, onClose: () => void ) { e.preventDefault(); e.stopPropagation(); - let menu: ContextMenuItem[] = []; - menu.push({ - label: "Focus Block", - click: () => { - alert("Not Implemented"); + let menu: ContextMenuItem[] = [ + { + label: "Magnify Block", + click: () => { + onMagnifyToggle(); + }, }, - }); - menu.push({ - label: "Minimize", - click: () => { - alert("Not Implemented"); + { + label: "Minimize", + click: () => { + alert("Not Implemented"); + }, }, - }); - menu.push({ - label: "Move to New Window", - click: () => { - let currentTabId = globalStore.get(atoms.activeTabId); - try { - services.WindowService.MoveBlockToNewWindow(currentTabId, blockData.oid); - } catch (e) { - console.error("error moving block to new window", e); - } + { + label: "Move to New Window", + click: () => { + const currentTabId = globalStore.get(atoms.activeTabId); + try { + services.WindowService.MoveBlockToNewWindow(currentTabId, blockData.oid); + } catch (e) { + console.error("error moving block to new window", e); + } + }, }, - }); - menu.push({ type: "separator" }); - menu.push({ - label: "Copy BlockId", - click: () => { - navigator.clipboard.writeText(blockData.oid); + { type: "separator" }, + { + label: "Copy BlockId", + click: () => { + navigator.clipboard.writeText(blockData.oid); + }, }, - }); + ]; const extraItems = viewModel?.getSettingsMenuItems?.(); - if (extraItems && extraItems.length > 0) { - menu.push({ type: "separator" }); - menu.push(...extraItems); - } - menu.push({ type: "separator" }); - menu.push({ - label: "Close Block", - click: onClose, - }); + if (extraItems && extraItems.length > 0) menu.push({ type: "separator" }, ...extraItems); + menu.push( + { type: "separator" }, + { + label: "Close Block", + click: onClose, + } + ); ContextMenuModel.showContextMenu(menu, e); } @@ -294,7 +296,8 @@ const BlockFrame_Default_Component = ({ elemtype: "iconbutton", icon: "cog", title: "Settings", - click: (e) => handleHeaderContextMenu(e, blockData, viewModel, layoutModel?.onClose), + click: (e) => + handleHeaderContextMenu(e, blockData, viewModel, layoutModel?.onMagnifyToggle, layoutModel?.onClose), }; endIconsElem.push( @@ -394,7 +397,15 @@ const BlockFrame_Default_Component = ({
handleHeaderContextMenu(e, blockData, viewModel, layoutModel?.onClose)} + onContextMenu={(e) => + handleHeaderContextMenu( + e, + blockData, + viewModel, + layoutModel?.onMagnifyToggle, + layoutModel?.onClose + ) + } > {preIconButtonElem}
diff --git a/frontend/app/tab/tabcontent.tsx b/frontend/app/tab/tabcontent.tsx index 549be9f0c..fe8fa13d6 100644 --- a/frontend/app/tab/tabcontent.tsx +++ b/frontend/app/tab/tabcontent.tsx @@ -1,7 +1,7 @@ // Copyright 2023, Command Line Inc. // SPDX-License-Identifier: Apache-2.0 -import { Block } from "@/app/block/block"; +import { Block, LayoutComponentModel } from "@/app/block/block"; import { getApi } from "@/store/global"; import * as services from "@/store/services"; import * as WOS from "@/store/wos"; @@ -26,14 +26,16 @@ const TabContent = React.memo(({ tabId }: { tabId: string }) => { function renderBlock( tabData: TabLayoutData, ready: boolean, + onMagnifyToggle: () => void, onClose: () => void, dragHandleRef: React.RefObject ) { if (!tabData.blockId || !ready) { return null; } - const layoutModel = { + const layoutModel: LayoutComponentModel = { onClose: onClose, + onMagnifyToggle: onMagnifyToggle, dragHandleRef: dragHandleRef, }; return ; diff --git a/frontend/app/theme.less b/frontend/app/theme.less index 629ee9168..5ea6f6cb0 100644 --- a/frontend/app/theme.less +++ b/frontend/app/theme.less @@ -20,6 +20,8 @@ --success-color: rgb(78, 154, 6); --hover-bg-color: rgba(255, 255, 255, 0.1); --block-bg-color: rgba(0, 0, 0, 0.5); + --block-bg-solid-color: rgb(0, 0, 0); + --block-border-radius: 8px; /* scrollbar colors */ --scrollbar-background-color: transparent; diff --git a/frontend/layout/lib/TileLayout.tsx b/frontend/layout/lib/TileLayout.tsx index cc61fee37..e4ee2c895 100644 --- a/frontend/layout/lib/TileLayout.tsx +++ b/frontend/layout/lib/TileLayout.tsx @@ -40,7 +40,7 @@ import { } from "./model"; import { NodeRefMap } from "./nodeRefMap"; import "./tilelayout.less"; -import { Dimensions, FlexDirection, setTransform as createTransform, determineDropDirection } from "./utils"; +import { Dimensions, FlexDirection, determineDropDirection, setTransform } from "./utils"; /** * contains callbacks and information about the contents (or styling) of of the TileLayout @@ -181,14 +181,28 @@ function TileLayoutComponent({ layoutTreeStateAtom, contents, getCursorPoint const leafRef = nodeRefs.get(leaf.id); // console.log("current leafRef", leafRef.current); if (leafRef?.current) { + if (leaf.id === layoutTreeState.magnifiedNodeId) { + const transform = setTransform( + { + top: displayBoundingRect.height * 0.05, + left: displayBoundingRect.width * 0.05, + width: displayBoundingRect.width * 0.9, + height: displayBoundingRect.height * 0.9, + }, + true + ); + newLayoutLeafTransforms[leaf.id] = transform; + continue; + } + const leafBounding = leafRef.current.getBoundingClientRect(); - const transform = createTransform({ + const transform = setTransform({ top: leafBounding.top - overlayBoundingRect.top, left: leafBounding.left - overlayBoundingRect.left, width: leafBounding.width, height: leafBounding.height, }); - newLayoutLeafTransforms[leafRef.current.id] = transform; + newLayoutLeafTransforms[leaf.id] = transform; } else { console.warn("missing leaf", leaf.id); } @@ -199,7 +213,7 @@ function TileLayoutComponent({ layoutTreeStateAtom, contents, getCursorPoint const newOverlayOffset = displayBoundingRect.top + 2 * displayBoundingRect.height; // console.log("overlayOffset", newOverlayOffset); setOverlayTransform( - createTransform( + setTransform( { top: activeDrag || showOverlay ? 0 : newOverlayOffset, left: 0, @@ -246,6 +260,18 @@ function TileLayoutComponent({ layoutTreeStateAtom, contents, getCursorPoint [contents.onNodeDelete, dispatch] ); + const onLeafMagnifyToggle = useCallback( + (node: LayoutNode) => { + const action = { + type: LayoutTreeActionType.MagnifyNodeToggle, + nodeId: node.id, + }; + + dispatch(action); + }, + [dispatch] + ); + return (
@@ -253,6 +279,7 @@ function TileLayoutComponent({ layoutTreeStateAtom, contents, getCursorPoint { * @param node The node that is closed. */ onLeafClose: (node: LayoutNode) => void; + + /** + * A callback that is called when a leaf's magnification is being toggled. + * @param node The node that is being magnified or un-magnified. + */ + onLeafMagnifyToggle: (node: LayoutNode) => void; + /** * A series of CSS properties used to display a leaf node with the correct dimensions and position, as determined from its corresponding OverlayNode. */ @@ -314,18 +348,27 @@ interface DisplayNodesWrapperProps { } const DisplayNodesWrapper = memo( - ({ layoutTreeState, contents, onLeafClose, layoutLeafTransforms, ready }: DisplayNodesWrapperProps) => { + ({ + layoutTreeState, + contents, + onLeafClose, + onLeafMagnifyToggle, + layoutLeafTransforms, + ready, + }: DisplayNodesWrapperProps) => { if (!layoutLeafTransforms) { return null; } return layoutTreeState.leafs.map((leaf) => { return ( ); @@ -344,6 +387,12 @@ interface DisplayNodeProps { */ contents: TileLayoutContents; + /** + * A callback that is called when a leaf's magnification is being toggled. + * @param node The node that is being magnified or unmagnified. + */ + onLeafMagnifyToggle: (node: LayoutNode) => void; + /** * A callback that is called when a leaf node gets closed. * @param node The node that is closed. @@ -353,6 +402,12 @@ interface DisplayNodeProps { * Determines whether a leaf's contents should be displayed to the user. */ ready: boolean; + + /** + * Any class names to add to the component. + */ + className?: string; + /** * A series of CSS properties used to display a leaf node with the correct dimensions and position, as determined from its corresponding OverlayNode. */ @@ -364,104 +419,118 @@ const dragItemType = "TILE_ITEM"; /** * The draggable and displayable portion of a leaf node in a layout tree. */ -const DisplayNode = memo(({ layoutNode, contents, transform, onLeafClose, ready }: DisplayNodeProps) => { - const tileNodeRef = useRef(null); - const dragHandleRef = useRef(null); - const previewRef = useRef(null); +const DisplayNode = memo( + ({ + layoutNode, + contents, + transform, + onLeafMagnifyToggle, + onLeafClose, + ready, + className, + }: DisplayNodeProps) => { + const tileNodeRef = useRef(null); + const dragHandleRef = useRef(null); + const previewRef = useRef(null); - const devicePixelRatio = useDevicePixelRatio(); + const devicePixelRatio = useDevicePixelRatio(); - const [{ isDragging }, drag, dragPreview] = useDrag( - () => ({ - type: dragItemType, - item: () => layoutNode, - collect: (monitor) => ({ - isDragging: monitor.isDragging(), + const [{ isDragging }, drag, dragPreview] = useDrag( + () => ({ + type: dragItemType, + item: () => layoutNode, + collect: (monitor) => ({ + isDragging: monitor.isDragging(), + }), }), - }), - [layoutNode] - ); + [layoutNode] + ); - const [previewElementGeneration, setPreviewElementGeneration] = useState(0); - const previewElement = useMemo(() => { - setPreviewElementGeneration(previewElementGeneration + 1); - return ( -
-
- {contents.renderPreview?.(layoutNode.data)} + const [previewElementGeneration, setPreviewElementGeneration] = useState(0); + const previewElement = useMemo(() => { + setPreviewElementGeneration(previewElementGeneration + 1); + return ( +
+
+ {contents.renderPreview?.(layoutNode.data)} +
+ ); + }, [contents.renderPreview, devicePixelRatio]); + + const [previewImage, setPreviewImage] = useState(null); + const [previewImageGeneration, setPreviewImageGeneration] = useState(0); + const generatePreviewImage = useCallback(() => { + const offsetX = (DragPreviewWidth * devicePixelRatio - DragPreviewWidth) / 2 + 10; + const offsetY = (DragPreviewHeight * devicePixelRatio - DragPreviewHeight) / 2 + 10; + if (previewImage !== null && previewElementGeneration === previewImageGeneration) { + dragPreview(previewImage, { offsetY, offsetX }); + } else if (previewRef.current) { + setPreviewImageGeneration(previewElementGeneration); + toPng(previewRef.current).then((url) => { + const img = new Image(); + img.src = url; + setPreviewImage(img); + dragPreview(img, { offsetY, offsetX }); + }); + } + }, [ + dragPreview, + previewRef.current, + previewElementGeneration, + previewImageGeneration, + previewImage, + devicePixelRatio, + ]); + + // Register the tile item as a draggable component + useEffect(() => { + drag(dragHandleRef); + }, [drag, dragHandleRef.current]); + + const onClose = useCallback(() => { + onLeafClose(layoutNode); + }, [layoutNode, onLeafClose]); + + const onMagnifyToggle = useCallback(() => { + onLeafMagnifyToggle(layoutNode); + }, [layoutNode, onLeafMagnifyToggle]); + + const leafContent = useMemo(() => { + return ( + layoutNode.data && ( +
+ {contents.renderContent(layoutNode.data, ready, onMagnifyToggle, onClose, dragHandleRef)} +
+ ) + ); + }, [layoutNode.data, ready, onClose]); + + return ( +
event.stopPropagation()} + > + {leafContent} + {previewElement}
); - }, [contents.renderPreview, devicePixelRatio]); - - const [previewImage, setPreviewImage] = useState(null); - const [previewImageGeneration, setPreviewImageGeneration] = useState(0); - const generatePreviewImage = useCallback(() => { - const offsetX = (DragPreviewWidth * devicePixelRatio - DragPreviewWidth) / 2 + 10; - const offsetY = (DragPreviewHeight * devicePixelRatio - DragPreviewHeight) / 2 + 10; - if (previewImage !== null && previewElementGeneration === previewImageGeneration) { - dragPreview(previewImage, { offsetY, offsetX }); - } else if (previewRef.current) { - setPreviewImageGeneration(previewElementGeneration); - toPng(previewRef.current).then((url) => { - const img = new Image(); - img.src = url; - setPreviewImage(img); - dragPreview(img, { offsetY, offsetX }); - }); - } - }, [ - dragPreview, - previewRef.current, - previewElementGeneration, - previewImageGeneration, - previewImage, - devicePixelRatio, - ]); - - // Register the tile item as a draggable component - useEffect(() => { - drag(dragHandleRef); - }, [drag, dragHandleRef.current]); - - const onClose = useCallback(() => { - onLeafClose(layoutNode); - }, [layoutNode, onLeafClose]); - - const leafContent = useMemo(() => { - return ( - layoutNode.data && ( -
- {contents.renderContent(layoutNode.data, ready, onClose, dragHandleRef)} -
- ) - ); - }, [layoutNode.data, ready, onClose]); - - return ( -
event.stopPropagation()} - > - {leafContent} - {previewElement} -
- ); -}); + } +); interface OverlayNodeProps { /** @@ -886,7 +955,7 @@ const Placeholder = ({ layoutTreeState, overlayContainerRef, nodeRefsAtom, s } } - const placeholderTransform = createTransform(placeholderDimensions); + const placeholderTransform = setTransform(placeholderDimensions); newPlaceholderOverlay =
; } } @@ -907,7 +976,7 @@ const Placeholder = ({ layoutTreeState, overlayContainerRef, nodeRefsAtom, s width: targetBoundingRect.width, }; - const placeholderTransform = createTransform(placeholderDimensions); + const placeholderTransform = setTransform(placeholderDimensions); newPlaceholderOverlay =
; } break; diff --git a/frontend/layout/lib/layoutAtom.ts b/frontend/layout/lib/layoutAtom.ts index 3088acb7d..5e9030f44 100644 --- a/frontend/layout/lib/layoutAtom.ts +++ b/frontend/layout/lib/layoutAtom.ts @@ -78,7 +78,7 @@ function getLayoutNodeWaveObjAtomFromTab( const tabValue = get(tabAtom); // console.log("getLayoutNodeWaveObjAtomFromTab tabValue", tabValue); if (!tabValue) return; - const layoutNodeOref = WOS.makeORef("layout", tabValue.layoutNode); + const layoutNodeOref = WOS.makeORef("layout", tabValue.layoutnode); // console.log("getLayoutNodeWaveObjAtomFromTab oref", layoutNodeOref); return WOS.getWaveObjectAtom>(layoutNodeOref); } @@ -86,13 +86,16 @@ function getLayoutNodeWaveObjAtomFromTab( export function withLayoutStateAtomFromTab(tabAtom: Atom): WritableLayoutTreeStateAtom { const pendingActionAtom = atom(null) as PrimitiveAtom; const generationAtom = atom(0) as PrimitiveAtom; + return atom( (get) => { const waveObjAtom = getLayoutNodeWaveObjAtomFromTab(tabAtom, get); if (!waveObjAtom) return null; - const layoutState = newLayoutTreeState(get(waveObjAtom)?.node); + const waveObj = get(waveObjAtom); + const layoutState = newLayoutTreeState(waveObj?.node); layoutState.pendingAction = get(pendingActionAtom); layoutState.generation = get(generationAtom); + layoutState.magnifiedNodeId = waveObj?.magnifiednodeid; return layoutState; }, (get, set, value) => { @@ -100,7 +103,11 @@ export function withLayoutStateAtomFromTab(tabAtom: Atom): WritableLayou if (get(generationAtom) !== value.generation) { const waveObjAtom = getLayoutNodeWaveObjAtomFromTab(tabAtom, get); if (!waveObjAtom) return; - const newWaveObj = { ...get(waveObjAtom), node: value.rootNode }; + const newWaveObj = { + ...get(waveObjAtom), + node: value.rootNode, + magnifiednodeid: value.magnifiedNodeId, + }; set(generationAtom, value.generation); set(waveObjAtom, newWaveObj); } diff --git a/frontend/layout/lib/layoutState.ts b/frontend/layout/lib/layoutState.ts index 385300045..ddeee765e 100644 --- a/frontend/layout/lib/layoutState.ts +++ b/frontend/layout/lib/layoutState.ts @@ -19,6 +19,7 @@ import { LayoutTreeComputeMoveNodeAction, LayoutTreeDeleteNodeAction, LayoutTreeInsertNodeAction, + LayoutTreeMagnifyNodeToggleAction, LayoutTreeMoveNodeAction, LayoutTreeResizeNodeAction, LayoutTreeSetPendingAction, @@ -108,6 +109,10 @@ function layoutTreeStateReducerInner(layoutTreeState: LayoutTreeState, act resizeNode(layoutTreeState, action as LayoutTreeResizeNodeAction); layoutTreeState.generation++; break; + case LayoutTreeActionType.MagnifyNodeToggle: + magnifyNodeToggle(layoutTreeState, action as LayoutTreeMagnifyNodeToggleAction); + layoutTreeState.generation++; + break; default: { console.error("Invalid reducer action", layoutTreeState, action); } @@ -460,3 +465,20 @@ function resizeNode(layoutTreeState: LayoutTreeState, action: LayoutTreeRe node.size = resize.size; } } + +function magnifyNodeToggle(layoutTreeState: LayoutTreeState, action: LayoutTreeMagnifyNodeToggleAction) { + console.log("magnifyNodeToggle", layoutTreeState, action); + if (!action.nodeId) { + console.error("invalid magnifyNodeToggle operation. nodeId must be defined."); + return; + } + if (layoutTreeState.rootNode.id === action.nodeId) { + console.warn(`cannot toggle magnification of node ${action.nodeId} because it is the root node.`); + return; + } + if (layoutTreeState.magnifiedNodeId === action.nodeId) { + layoutTreeState.magnifiedNodeId = undefined; + } else { + layoutTreeState.magnifiedNodeId = action.nodeId; + } +} diff --git a/frontend/layout/lib/model.ts b/frontend/layout/lib/model.ts index 096e60f11..7642441e5 100644 --- a/frontend/layout/lib/model.ts +++ b/frontend/layout/lib/model.ts @@ -42,6 +42,7 @@ export enum LayoutTreeActionType { ResizeNode = "resize", InsertNode = "insert", DeleteNode = "delete", + MagnifyNodeToggle = "magnify", } /** @@ -163,6 +164,18 @@ export interface LayoutTreeResizeNodeAction extends LayoutTreeAction { resizeOperations: ResizeNodeOperation[]; } +/** + * Action for toggling magnification of a node from the layout tree. + */ +export interface LayoutTreeMagnifyNodeToggleAction extends LayoutTreeAction { + type: LayoutTreeActionType.MagnifyNodeToggle; + + /** + * The id of the node to maximize; + */ + nodeId: string; +} + /** * Represents the state of a layout tree. * @@ -173,6 +186,7 @@ export type LayoutTreeState = { leafs: LayoutNode[]; pendingAction: LayoutTreeAction; generation: number; + magnifiedNodeId?: string; }; /** @@ -200,6 +214,7 @@ export type WritableLayoutTreeStateAtom = WritableAtom, [v export type ContentRenderer = ( data: T, ready: boolean, + onMagnifyToggle: () => void, onClose: () => void, dragHandleRef: React.RefObject ) => React.ReactNode; @@ -208,6 +223,7 @@ export type PreviewRenderer = (data: T) => React.ReactElement; export interface LayoutNodeWaveObj extends WaveObj { node: LayoutNode; + magnifiednodeid: string; } export const DefaultNodeSize = 10; diff --git a/frontend/layout/lib/tilelayout.less b/frontend/layout/lib/tilelayout.less index 2e72b10bc..a6dc4e5e3 100644 --- a/frontend/layout/lib/tilelayout.less +++ b/frontend/layout/lib/tilelayout.less @@ -66,10 +66,18 @@ } .tile-node { + border-radius: var(--block-border-radius); + overflow: hidden; + &.dragging { filter: blur(8px); } + &.magnified { + background-color: var(--block-bg-solid-color); + z-index: 10; + } + .tile-preview-container { position: absolute; top: 10000px; @@ -89,7 +97,7 @@ .placeholder { transition-duration: 0.15s; transition-timing-function: ease-in; - transition-property: transform, width, height; + transition-property: transform, width, height, background-color; } } @@ -106,6 +114,6 @@ .placeholder { background-color: var(--accent-color); opacity: 0.5; - border-radius: 5px; + border-radius: calc(var(--block-border-radius) + 2px); } } diff --git a/frontend/types/gotypes.d.ts b/frontend/types/gotypes.d.ts index ed1401142..588af2e7c 100644 --- a/frontend/types/gotypes.d.ts +++ b/frontend/types/gotypes.d.ts @@ -176,6 +176,7 @@ declare global { // wstore.LayoutNode type LayoutNode = WaveObj & { node?: any; + magnifiednodeid?: string; meta?: MetaType; }; @@ -315,7 +316,7 @@ declare global { // wstore.Tab type Tab = WaveObj & { name: string; - layoutNode: string; + layoutnode: string; blockids: string[]; meta: MetaType; }; diff --git a/pkg/wstore/wstore_types.go b/pkg/wstore/wstore_types.go index 3217f568c..593d970d2 100644 --- a/pkg/wstore/wstore_types.go +++ b/pkg/wstore/wstore_types.go @@ -207,7 +207,7 @@ type Tab struct { OID string `json:"oid"` Version int `json:"version"` Name string `json:"name"` - LayoutNode string `json:"layoutNode"` + LayoutNode string `json:"layoutnode"` BlockIds []string `json:"blockids"` Meta map[string]any `json:"meta"` } @@ -225,10 +225,11 @@ func (t *Tab) GetBlockORefs() []waveobj.ORef { } type LayoutNode struct { - OID string `json:"oid"` - Version int `json:"version"` - Node any `json:"node,omitempty"` - Meta map[string]any `json:"meta,omitempty"` + OID string `json:"oid"` + Version int `json:"version"` + Node any `json:"node,omitempty"` + MagnifiedNodeId string `json:"magnifiednodeid,omitempty"` + Meta map[string]any `json:"meta,omitempty"` } func (*LayoutNode) GetOType() string {