LiveBoard / docsProject documentation

Reference

Dashboard and Folder Tree

Look up syntax, contracts, layouts, algorithms, and exact behavior.

Dashboard file structure. The dashboard presents owned canvases as a mixed folder/canvas tree and shared canvases as a separate searchable section.

The dashboard model intentionally resembles a small Drive-like file manager. Owned canvases and folders are siblings inside an implicit root or a parent folder. Shared canvases are not allowed to appear inside the owner's folder tree because folders are an owner-local organization primitive, not an access-control primitive.

  • Owned canvases and folders render under Your canvases as one mixed, nested tree.
  • Shared canvases render separately under Shared with You and can be searched by canvas name or owner username.
  • Shared canvases can also be filtered by owner, which keeps several collaborators' boards from becoming one undifferentiated list.
  • Folders can be created at the root, from empty list space, or from an existing folder row to create a nested folder.
  • Folders and canvases can be selected together, including single-click, Ctrl/Cmd toggle selection, Shift range selection, and Ctrl/Cmd+A across the owned list.
  • Owned canvases and folders support context-menu open, share, rename, create nested folder, delete, and destructive confirmation flows where applicable.

This distinction prevents the folder system from becoming a second permissions system. A folder answers the question, "Where did the owner put this canvas?" Membership answers the question, "Who can open this canvas?" Keeping those questions separate makes deletion, moving, sharing, and rendering easier to reason about. Deleting a folder can delete an owned subtree, while removing a member only removes an access edge.

Fo=(VF∪VC,Eparent,order)F_o = (V_F \cup V_C, E_{parent}, order)
Folder tree. For owner o, the dashboard tree contains folder vertices, canvas vertices, parent edges, and a sibling order relation.
type CanvasFolder = {
  id: string;
  name: string;
  parentId?: string | null;
  sortOrder: number;
  updatedAt: string;
};

type CanvasSummary = {
  id: string;
  name: string;
  ownerId: string;
  folderId?: string | null;
  sortOrder: number;
  revision: number;
};
Dashboard item model. Folders and canvases share enough ordering shape to be rendered, selected, dragged, and reordered as one mixed sibling list.

Drag and drop is not just a visual affordance. Dropping an owned canvas onto the center of a folder row changes its parent folder through PATCH /api/canvases/{canvas_id}/folder. Dropping a folder onto another folder changes the folder's parent through PATCH /api/folders/{folder_id}/parent, with the backend rejecting moves into itself or one of its descendants. Dropping a canvas or folder into an insertion zone before, between, or after siblings sends the complete desired sibling order to PATCH /api/dashboard/order.

drag owned item
  -> hover center of folder row
     -> PATCH canvas folder or folder parent
     -> backend verifies ownership and cycle rules
     -> frontend refreshes owned tree placement

drag owned item
  -> hover insertion zone before/between/after siblings
     -> PATCH /api/dashboard/order with full mixed order
     -> backend rewrites sort_order for that parent
     -> frontend renders arbitrary sibling order
Dashboard drag/drop flow. The dashboard supports both parent changes and arbitrary sibling reordering. The backend stores order explicitly instead of inferring it from names or creation time.

The frontend computes tree rail segments statically from the sibling relationship at each level. A row receives one rail segment per indent: a straight rail when an ancestor has a following sibling, a T rail when the row itself has following siblings, and an L rail when it is the last child for its parent. This avoids CSS guessing and makes nested folder drawings deterministic from the tree data.

rail(d,i)∈{none,straight,tee,elbow}rail(d, i) \in \{none, straight, tee, elbow\}
Rail classifier. Each row computes a rail token for depth d and sibling index i, then renders static segments rather than relying on cascading pseudo-element state.

Deletion is also folder-aware. Deleting a canvas removes that canvas and cascades its membership, operations, and history rows. Deleting a folder removes the entire owned folder subtree, including nested folders and owned canvases inside it. The frontend routes destructive actions through the shared confirmation modal, and the backend closes live sockets for any deleted canvases so open editors do not keep editing a resource that no longer exists.