一键导入
logseq-whiteboard-creation
Create and manage whiteboards in LogSeq graph using EDN format
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Create and manage whiteboards in LogSeq graph using EDN format
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | logseq-whiteboard-creation |
| description | Create and manage whiteboards in LogSeq graph using EDN format |
| license | MIT |
| compatibility | opencode |
| metadata | {"audience":"knowledge-workers","workflow":"logseq-graph"} |
This skill teaches LLM agents how to create and manage whiteboards in LogSeq graph folders using the EDN (Extensible Data Notation) format.
Use this skill when you need to:
LogSeq whiteboards are stored as .edn files in the whiteboards/ directory. The root structure contains two main keys:
{:blocks (shape1 shape2 shape3 ...) ; All shapes in the whiteboard
:pages (page1 page2 page3 ...)} ; Whiteboard page metadata
Clojure EDN Syntax:
{:key value :key2 value2} (key-value pairs)[1 2 3] (ordered arrays):keyword (prefixed with colon)#uuid "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; prefixCreate a new file in the whiteboards/ directory with a meaningful name:
whiteboards/my-diagram.edn
Start with an empty whiteboard structure:
{:blocks ()
:pages ({:block/uuid #uuid "YOUR-UUID-HERE"
:block/properties
{:ls-type :whiteboard-page
:logseq.tldraw.page
{:id "YOUR-UUID-HERE"
:name "YOUR-WHITEBOARD-NAME"
:bindings {}
:nonce 1
:assets []
:shapes-index []}}
:block/updated-at TIMESTAMP
:block/created-at TIMESTAMP
:block/type "whiteboard"
:block/name "YOUR-WHITEBOARD-NAME"
:block/original-name "YOUR-WHITEBOARD-NAME"})}
UUID Generation:
crypto.randomUUID()):block/uuid, :id, and shape :parentId#uuid "550e8400-e29b-41d4-a716-446655440000"Timestamp Generation - CRITICAL:
Date.now() in JavaScript1768625722664 (January 17, 2026 at 4:55 AM UTC):block/created-at and :block/updated-at should match initially1768625462664, 1768625472664, 1768625482664, etc.Why Timestamp Matters:
1768611047550), LogSeq displays "created 56 years ago"Timestamp Calculation Tool:
// Generate current timestamp in milliseconds
const now = Date.now();
console.log(now); // e.g., 1768625722664
// For multiple shapes, increment by 10-60 seconds
const timestamps = Array(10).fill(0).map((_, i) => now - 100000 + i * 10000);
CRITICAL: The page UUID must be used correctly in THREE different places with THREE different formats:
:block/uuid (with #uuid prefix) - page identifier in the :pages block
#uuid "550e8400-e29b-41d4-a716-446655440004":id inside :logseq.tldraw.page (plain string) - internal tldraw reference
"550e8400-e29b-41d4-a716-446655440004" (NO #uuid prefix!):parentId in EVERY shape (plain string) - links shapes to their parent page
"550e8400-e29b-41d4-a716-446655440004" (NO #uuid prefix!)Common Mistake - Using a placeholder string:
❌ WRONG:
:pages ({:block/uuid #uuid "550e8400-e29b-41d4-a716-446655440004"
:block/properties
{:ls-type :whiteboard-page
:logseq.tldraw.page
{:id "page-001-uuid" ; WRONG - not the actual UUID!
:name "my-whiteboard"}}})
{:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "box-001"
:parentId "page-001-uuid" ; WRONG - not the actual UUID!
}}}
✅ CORRECT:
:pages ({:block/uuid #uuid "550e8400-e29b-41d4-a716-446655440004"
:block/properties
{:ls-type :whiteboard-page
:logseq.tldraw.page
{:id "550e8400-e29b-41d4-a716-446655440004" ; CORRECT - actual UUID string
:name "my-whiteboard"}}})
{:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "box-001"
:parentId "550e8400-e29b-41d4-a716-446655440004" ; CORRECT - actual UUID string
}}}
Why this matters: LogSeq uses the :parentId to link shapes to their parent page. If the :parentId doesn't match the page's :id in :logseq.tldraw.page, the shapes won't be recognized as belonging to that page, and bindings will not work.
Every LogSeq whiteboard page has metadata that affects how it displays in the graph:
:pages (
{:block/uuid #uuid "228d6029-0710-4a4b-943a-cc9dab11092f"
:block/properties {...}
:block/updated-at 1768625722664 ; Last modification time
:block/created-at 1768625462664 ; Creation time
:block/type "whiteboard" ; Always "whiteboard" for diagram files
:block/name "OAuth2-Authorization-Code-Flow" ; Display name in LogSeq
:block/original-name "OAuth2-Authorization-Code-Flow"})
Problem: "Created 56 years ago"
If your whiteboards show "created 56 years ago" in LogSeq metadata, it's because:
1768611047550 from past sessions)Solution:
Always use current timestamps when generating new whiteboards:
// ✅ CORRECT: Use current time
const now = Date.now();
console.log(now); // ~1768625722664 (January 2026)
// ❌ WRONG: Using old static timestamps
const old = 1768611047550; // This is from past date
For a cohesive diagram, use timestamps that are close together but slightly different:
; All created in same session, moments apart
:block/created-at 1768625462664 ; Page created
...shapes...
:block/created-at 1768625462664 ; First shape (same time)
:block/created-at 1768625472664 ; Second shape (10 seconds later)
:block/created-at 1768625482664 ; Third shape (20 seconds later)
:block/created-at 1768625492664 ; Fourth shape (30 seconds later)
JavaScript:
const now = Date.now(); // Current time in milliseconds
Node.js One-liner:
node -e "console.log(Date.now())"
Manual Calculation:
date +%sdate +%s000{:blocks (
{:block/created-at 1768625462664 ; Page creation time
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "title-shape"
:parentId "228d6029-0710-4a4b-943a-cc9dab11092f"}}
:block/updated-at 1768625462664}
{:block/created-at 1768625472664 ; 10 seconds later
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "box-shape-1"
:parentId "228d6029-0710-4a4b-943a-cc9dab11092f"}}
:block/updated-at 1768625472664}
{:block/created-at 1768625482664 ; 20 seconds later
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "box-shape-2"
:parentId "228d6029-0710-4a4b-943a-cc9dab11092f"}}
:block/updated-at 1768625482664})
:pages (
{:block/uuid #uuid "228d6029-0710-4a4b-943a-cc9dab11092f"
:block/properties
{:ls-type :whiteboard-page
:logseq.tldraw.page
{:id "228d6029-0710-4a4b-943a-cc9dab11092f"
:name "My-Diagram"}}
:block/updated-at 1768625482664 ; Page last updated (should be time of last shape)
:block/created-at 1768625462664 ; Page first created
:block/type "whiteboard"
:block/name "My-Diagram"
:block/original-name "My-Diagram"})}
Each shape is a block with standard LogSeq properties plus tldraw-specific geometry:
{:block/created-at TIMESTAMP
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "SHAPE-UUID" ; unique identifier for this shape
:type "SHAPE-TYPE" ; shape type: "text", "box", "ellipse", "line", "polygon", "pencil", "highlighter", "logseq-portal"
:point [X Y] ; [left, top] position in pixels
:size [WIDTH HEIGHT] ; [width, height] in pixels
:parentId "PAGE-UUID" ; links to parent page
:index INDEX ; z-order (0, 1, 2...) - higher numbers appear on top
:scale [1 1] ; scale multiplier [x y]
:scaleLevel "md" ; scale level: "sm", "md", "lg", "xl", "xxl"
:opacity 1 ; 0.0 to 1.0 (transparency)
:nonce TIMESTAMP ; creation time in milliseconds
; Stroke/Border Properties
:stroke "" ; stroke color: "" (none), "blue", "green", "red", "purple", or hex "#333333"
:strokeWidth 2 ; border thickness in pixels (affected by scaleLevel)
:strokeType "line" ; "line", "dotted", "dashed"
; Fill Properties
:noFill true ; true = transparent, false = filled
:fill "" ; fill color: "" (none), "blue", "green", "red", "purple", or hex "#e3f2fd"
; Text Properties (for text shapes)
:text "Shape Text" ; the displayed text content
:fontSize 20 ; font size in pixels (affected by scaleLevel)
:fontFamily "var(--ls-font-family)" ; font family (use this default)
:fontWeight 400 ; 400 (normal), 700 (bold)
:italic false ; true or false
:lineHeight 1.2 ; line spacing multiplier
:padding 4 ; internal text padding
:isSizeLocked true ; prevent resizing (text shapes)
; Labels (for shapes like ellipse, box)
:label "Shape Label" ; optional label/text displayed on shape
; Geometry Properties (shape-specific)
:borderRadius 2 ; corner radius for box shapes (0-8+ pixels)
; References (optional)
:refs [] ; array of page/tag references ["TODO", "cheatsheet"]
; Rotation (optional)
:rotation 0}} ; rotation in degrees
:block/updated-at TIMESTAMP}
The :scaleLevel property affects visual rendering. Common values:
| Scale Level | Stroke Width Multiplier | Font Size Multiplier | Use Case |
|---|---|---|---|
"sm" | 0.8x (1.6px for strokeWidth 2) | 0.5x (16px for fontSize 32) | Small annotations |
"md" | 1.0x (2px for strokeWidth 2) | 1.0x (32px for fontSize 32) | Default shapes |
"lg" | 1.6x (3.2px for strokeWidth 2) | 1.0x (32px for fontSize 32) | Emphasized shapes |
"xl" | 2.4x | 1.5x | Large diagrams |
"xxl" | 3.0x (6px for strokeWidth 2) | 1.875x (60px for fontSize 32) | Titles, headers |
Important: strokeWidth and fontSize in the shape definition are BASE values. The actual rendered size = base × scaleLevel multiplier.
LogSeq uses tldraw shape types. All supported types:
| Type | Purpose | Key Properties | Notes |
|---|---|---|---|
text | Text boxes | :text, :fontSize, :fontWeight, :italic, :isSizeLocked | Use for labels and annotations |
box | Rectangular boxes | :label, :borderRadius, :fill, :noFill | Default borderRadius is 2 |
ellipse | Circles and ovals | :label, :fill, :noFill | Use equal width/height for circles |
line | Connector lines with arrows | :handles, :decorations | Requires binding configuration |
polygon | Multi-sided shapes | :sides, :ratio, :isFlippedY | Use :sides 3 for triangle |
pencil | Freehand drawings | :points, :isComplete | Array of [x, y, pressure] points |
highlighter | Highlight overlays | :points, :isComplete, :opacity 0.5 | Use opacity 0.5 for transparency |
logseq-portal | Embedded page blocks | :pageId, :blockType, :collapsed | Links to LogSeq pages |
Line shapes in LogSeq whiteboards support sophisticated connections between shapes using handles and bindings. This allows arrows to snap to shapes and maintain connections when shapes are moved.
{:block/created-at TIMESTAMP
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "LINE-UUID"
:type "line"
:point [X Y] ; starting position
:strokeType "line" ; "line", "dotted", "dashed"
:strokeWidth 1 ; line thickness
:opacity 1
:label "" ; empty for connectors
:fontWeight 400
:noFill true
:fontSize 20
:parentId "PAGE-UUID"
:index INDEX
:nonce TIMESTAMP
:italic false
; Handles define connection points
:handles
{:start
{:id "start"
:canBind true ; allows binding to shapes
:point [X Y] ; relative offset from shape :point
:bindingId "BINDING-UUID"} ; links to binding in page
:end
{:id "end"
:canBind true
:point [X Y]
:bindingId "BINDING-UUID"}}
; Decorations add arrowheads
:decorations
{:start "arrow" ; arrowhead at start (optional)
:end "arrow"}}} ; arrowhead at end (optional)
:block/updated-at TIMESTAMP}
Lines that connect to shapes require bindings in the :logseq.tldraw.page section. Each binding links a line's handle to a target shape:
:pages (
{:block/uuid #uuid "PAGE-UUID"
:block/properties
{:ls-type :whiteboard-page
:logseq.tldraw.page
{:id "PAGE-UUID"
:name "my-whiteboard"
:bindings
{; Binding for line start handle
:BINDING-START-UUID
{:id "BINDING-START-UUID"
:type "line" ; binding type
:fromId "LINE-UUID" ; the line shape ID
:toId "SHAPE-UUID" ; target shape to connect to
:handleId "start" ; which handle: "start" or "end"
:point [0.5 0.5] ; attachment point on target (0-1, center = 0.5)
:distance 4} ; snap distance
; Binding for line end handle
:BINDING-END-UUID
{:id "BINDING-END-UUID"
:type "line"
:fromId "LINE-UUID"
:toId "OTHER-SHAPE-UUID"
:handleId "end"
:point [0.5 0.5]
:distance 4}}
:shapes-index [...]}}})
Here's a complete example of a line connecting two boxes:
Line Shape:
{:block/created-at 1768621811641
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "line-001"
:type "line"
:point [799.66 676]
:strokeType "line"
:strokeWidth 1
:opacity 1
:label ""
:fontWeight 700
:noFill true
:fontSize 20
:parentId "page-uuid"
:index 3
:nonce 1768621811641
:italic false
:handles
{:start
{:id "start"
:canBind true
:point [24.68 0] ; offset from line :point
:bindingId "binding-start-001"}
:end
{:id "end"
:canBind true
:point [0 144] ; offset from line :point
:bindingId "binding-end-001"}}
:decorations
{:start "arrow" ; arrowhead at both ends
:end "arrow"}}}
:block/updated-at 1768621811641}
Page Bindings:
:bindings
{:binding-start-001
{:id "binding-start-001"
:type "line"
:fromId "line-001" ; the line
:toId "box-001" ; connects to first box
:handleId "start"
:point [0.5 0.5] ; center of box
:distance 4}
:binding-end-001
{:id "binding-end-001"
:type "line"
:fromId "line-001"
:toId "box-002" ; connects to second box
:handleId "end"
:point [0.5 0.5]
:distance 4}}
Control arrowhead appearance with :decorations:
| Configuration | Visual Result |
|---|---|
{:end "arrow"} | Arrow at end only → |
{:start "arrow"} | ← Arrow at start only |
{:start "arrow" :end "arrow"} | ↔ Arrows at both ends |
{} or omit | Plain line (no arrows) |
:point near the midpoint between shapes:handles :start :point and :end :point are relative to line :point
handle_offset = target_position - line_point:bindings map with matching IDs:toId[0.5 0.5] for center, [0 0.5] for left-center, [1 0.5] for right-center, etc.:point + handle :point should roughly equal target shape edge positionUnderstanding the Binding System:
Line bindings in LogSeq whiteboards work through a coordinate-based attachment system. Each line has TWO handles (start and end), and each handle needs:
:point in the handle) relative to the line's :point:point [x y] where x,y are 0-1 ratios)The Key Insight: The line's handle :point plus the line's base :point should roughly equal the position where you want the connection to occur on the target shape.
Step-by-Step Line Binding Process:
; Example: Connect "box-step2" at [80 420] to "box-step3" at [480 300]
; STEP 1: Choose where to position your line
; Pick a point roughly between the two shapes
:point [320 455] ; somewhere in the middle
; STEP 2: Calculate handle offsets
; For the START handle (connecting to box-step2 at [80 420]):
; We want to connect to the RIGHT edge of box-step2
; box-step2 is at [80 420], size [240 70]
; Right edge center = [80 + 240, 420 + 35] = [320, 455]
; Offset from line point [320 455] = [320 - 320, 455 - 455] = [0, 0]
:handles {
:start {
:point [0 0] ; This means: line point + [0 0] = [320 455]
:bindingId "binding-1-start"}}
; For the END handle (connecting to box-step3 at [480 300]):
; We want to connect to the LEFT edge of box-step3
; box-step3 is at [480 300], size [240 70]
; Left edge center = [480, 300 + 35] = [480, 335]
; Offset from line point [320 455] = [480 - 320, 335 - 455] = [160, -120]
:handles {
:end {
:point [160 -120] ; This means: line point + [160 -120] = [480 335]
:bindingId "binding-1-end"}}
; STEP 3: Create the bindings in :pages :bindings
:bindings {
:binding-1-start
{:id "binding-1-start"
:type "line"
:fromId "arrow-1"
:toId "box-step2" ; Must be the ACTUAL shape ID you want to connect
:handleId "start"
:point [1 0.5] ; [1 0.5] = right edge, middle (1=right, 0.5=center vertically)
:distance 4}
:binding-1-end
{:id "binding-1-end"
:type "line"
:fromId "arrow-1"
:toId "box-step3" ; Must be the ACTUAL shape ID you want to connect
:handleId "end"
:point [0 0.5] ; [0 0.5] = left edge, middle (0=left, 0.5=center vertically)
:distance 4}}
Attachment Point Reference:
The :point [x y] in the binding specifies WHERE on the target shape to attach:
| Point Value | Location on Shape |
|---|---|
[0 0] | Top-left corner |
[0.5 0] | Top-center |
[1 0] | Top-right corner |
[0 0.5] | Left-center |
[0.5 0.5] | Center |
[1 0.5] | Right-center |
[0 1] | Bottom-left corner |
[0.5 1] | Bottom-center |
[1 1] | Bottom-right corner |
Common Mistakes to Avoid:
❌ MISTAKE 1: Wrong shape ID in binding
; You have arrow-1 at [320 455] connecting box-step2 to box-step3
; But you bind to box-step1 instead of box-step2
:binding-1-start {:toId "box-step1"} ; WRONG - not the shape you want!
❌ MISTAKE 2: Incorrect handle offset calculation
; Line at [320 455], connecting to box at [480 335]
; Offset should be [160 -120]
:handles {:end {:point [0 0]}} ; WRONG - this would point to [320 455] not [480 335]
❌ MISTAKE 3: Mismatched binding IDs
; Handle references binding-1-start
:handles {:start {:bindingId "binding-1-start"}}
; But you create binding-start-1 instead
:bindings {:binding-start-1 {...}} ; WRONG - ID doesn't match!
Troubleshooting Checklist:
:parentId set to the actual UUID string (not a placeholder)?
:parentId values = page :id in :logseq.tldraw.page"550e8400-e29b-41d4-a716-446655440004" NOT "page-001-uuid":point offsets calculated correctly?
:point + handle :point should equal target location:bindingId and :bindings key?:toId the correct shape ID you want to connect to?:point [x y] on the correct edge (0=left/top, 1=right/bottom)?:bindings map?Polygons support multi-sided shapes like triangles, pentagons, hexagons:
{:block/created-at TIMESTAMP
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "POLYGON-UUID"
:type "polygon"
:point [X Y]
:size [WIDTH HEIGHT]
:sides 3 ; number of sides (3=triangle, 5=pentagon, 6=hexagon, etc.)
:ratio 1 ; aspect ratio
:isFlippedY false ; flip vertically
:stroke "green"
:strokeWidth 6
:strokeType "dashed"
:fill "green"
:noFill true
:opacity 1
:label "Triangle"
:fontSize 60
:fontWeight 700
:italic false
:scaleLevel "xxl"
:parentId "PAGE-UUID"
:index INDEX
:nonce TIMESTAMP}}
:block/updated-at TIMESTAMP}
Common Polygon Sides:
:sides 3 - Triangle:sides 4 - Square/Diamond:sides 5 - Pentagon:sides 6 - Hexagon:sides 8 - OctagonFor freehand drawing and highlighting:
Pencil (Freehand Drawing):
{:type "pencil"
:points [[0 57.01875 0.5] [1.606 57.553 0.5] ...] ; [x, y, pressure] array
:isComplete true
:stroke ""
:strokeWidth 2
:opacity 1
:noFill true}
Highlighter (Overlay Highlight):
{:type "highlighter"
:points [[0 0 0.5] [130.46 16.95 0.5] ...]
:isComplete true
:stroke "green"
:strokeWidth 6
:opacity 0.5 ; IMPORTANT: use 0.5 for transparency
:noFill true}
Note: Pencil and highlighter shapes use :points arrays with [x, y, pressure] values. The third value (pressure) is typically 0.5 for consistent line width.
Embed LogSeq page content directly in whiteboards:
{:type "logseq-portal"
:pageId "696a2938-dfed-4caf-84e3-fc363a316477" ; UUID of LogSeq page
:blockType "B" ; block type
:collapsed false ; expand/collapse state
:collapsedHeight 0
:compact true
:isAutoResizing true
:borderRadius 8
:fill "green"
:noFill false
:stroke "green"
:strokeWidth 2
:size [400 161]
:scaleLevel "md"}
Arrow shapes are used to show flow, relationships, and directional connections between shapes. Key properties:
{:block/created-at TIMESTAMP
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "ARROW-UUID"
:type "arrow" ; connector shape
:point [X Y] ; starting point [left, top]
:size [WIDTH HEIGHT] ; size of connector bounds (can be [0 0])
:stroke "#333333" ; arrow color (hex)
:strokeWidth 2 ; line thickness in pixels
:strokeType "line" ; "line", "dotted", "dashed"
:parentId "PAGE-UUID" ; links to parent page
:index INDEX ; z-order
:opacity 1 ; transparency 0.0-1.0
:nonce TIMESTAMP}} ; creation time
:block/updated-at TIMESTAMP}
Arrow Positioning Tips:
:point is the starting coordinate of the arrow:strokeWidth 2-3 for visibilityArrow Examples by Use Case:
Sequential Flow Arrow (between steps):
{:point [450 270] ; positioned between Step 1 and Step 2
:size [0 0] ; minimal bounds
:stroke "#333333"
:strokeWidth 2}
Feedback/Loop Arrow (curved flow back):
{:point [600 300] ; positioned for return path
:size [0 0]
:stroke "#666666" ; darker for emphasis
:strokeWidth 2}
Conditional Arrow (with different styling):
{:point [500 400]
:size [0 0]
:stroke "#ff9800" ; orange for "then" path
:strokeWidth 2
:strokeType "dashed"} ; dashed for optional/alternative flow
{:block/created-at 1768621679114
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "box-transparent-001"
:type "box"
:label "Transparent Rectangle"
:point [640 544]
:size [392 128]
:borderRadius 2
:stroke "" ; empty = default black border
:strokeWidth 3.2 ; actual width = 3.2 with scaleLevel lg
:strokeType "line"
:fill "" ; empty = no fill color
:noFill true ; transparent interior
:opacity 1
:fontSize 32
:fontWeight 400
:italic false
:scale [1 1]
:scaleLevel "lg" ; large scale multiplier
:parentId "page-uuid"
:index 1
:nonce 1768621679114}}
:block/updated-at 1768621679114}
{:block/created-at 1768621811640
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "box-filled-001"
:type "box"
:label "Colored Rectangle"
:point [592 824]
:size [392 128]
:borderRadius 2
:stroke "blue" ; named color
:strokeWidth 3.2
:strokeType "line"
:fill "blue" ; match stroke color
:noFill false ; filled interior
:opacity 1
:fontSize 32
:fontWeight 400
:italic false
:refs [] ; can reference tags/pages
:scale [1 1]
:scaleLevel "lg"
:parentId "page-uuid"
:index 2
:nonce 1768621811640}}
:block/updated-at 1768621811640}
{:block/created-at 1768621993112
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "box-dashed-001"
:type "box"
:label "Dashed Rectangle"
:point [528 1032]
:size [392 128]
:borderRadius 2
:stroke ""
:strokeWidth 3.2
:strokeType "dashed" ; dashed border
:fill ""
:noFill false
:opacity 0.6 ; 60% opacity for subtle effect
:fontSize 32
:fontWeight 400
:italic false
:refs []
:scale [1 1]
:scaleLevel "lg"
:parentId "page-uuid"
:index 10
:nonce 1768621993112}}
:block/updated-at 1768621993112}
{:block/created-at 1768621886060
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "circle-001"
:type "ellipse"
:label "Circle"
:point [1144 552]
:size [384 128] ; unequal dimensions = oval
:stroke "green"
:strokeWidth 3.2
:strokeType "line"
:fill "green"
:noFill false
:opacity 1
:fontSize 32
:fontWeight 700 ; bold label
:italic false
:refs ["TODO"] ; tag reference
:scale [1 1]
:scaleLevel "lg"
:parentId "page-uuid"
:index 4
:nonce 1768621886060}}
:block/updated-at 1768621886060}
{:block/created-at 1768621866247
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "circle-transparent-001"
:type "ellipse"
:label "Transparent Circle"
:point [1136 848]
:size [384 128]
:stroke "green"
:strokeWidth 1.6 ; actual width with scaleLevel sm
:strokeType "line"
:fill "green"
:noFill true ; transparent
:opacity 1
:fontSize 16 ; smaller font with scaleLevel sm
:fontWeight 400
:italic true ; italic label
:refs []
:scale [1 1]
:scaleLevel "sm" ; small scale
:parentId "page-uuid"
:index 5
:nonce 1768621866247}}
:block/updated-at 1768621866247}
{:block/created-at 1768621921302
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "circle-dashed-001"
:type "ellipse"
:label "Dashed Circle"
:point [1628 552]
:size [384 128]
:stroke "purple"
:strokeWidth 3.2
:strokeType "dashed" ; dashed border
:fill "purple"
:noFill false
:opacity 1
:fontSize 32
:fontWeight 400
:italic false
:refs []
:scale [1 1]
:scaleLevel "lg"
:parentId "page-uuid"
:index 7
:nonce 1768621921302}}
:block/updated-at 1768621921302}
{:block/created-at 1768621953661
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "triangle-001"
:type "polygon"
:label "Triangle"
:point [1688 816]
:size [256 184]
:sides 3 ; triangle
:ratio 1
:isFlippedY false
:stroke "green"
:strokeWidth 6 ; actual width with scaleLevel xxl
:strokeType "dashed"
:fill "green"
:noFill true
:opacity 1
:fontSize 60 ; large font with scaleLevel xxl
:fontWeight 700
:italic false
:refs []
:scale [1 1]
:scaleLevel "xxl" ; extra large
:parentId "page-uuid"
:index 9
:nonce 1768621953661}}
:block/updated-at 1768621953661}
{:block/created-at 1768622000065
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "text-001"
:type "text"
:text "Text"
:point [1208 1096]
:size [134 82]
:isSizeLocked true ; prevent resizing
:borderRadius 0
:stroke "green"
:strokeWidth 2
:strokeType "line"
:fill "green"
:noFill true
:opacity 1
:fontSize 60 ; base font size
:fontFamily "var(--ls-font-family)"
:fontWeight 400
:italic false
:lineHeight 1.2
:padding 4
:scale [1 1]
:scaleLevel "xxl" ; xxl multiplier applied
:parentId "page-uuid"
:index 12
:nonce 1768622000065}}
:block/updated-at 1768622000065}
{:block/created-at 1768621811641
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "line-001"
:type "line"
:point [799.66 676]
:strokeType "line"
:strokeWidth 1
:stroke ""
:fill ""
:noFill true
:opacity 1
:label ""
:fontSize 20
:fontWeight 700
:italic false
:refs ["cheatsheet"] ; optional reference
:parentId "page-uuid"
:index 3
:nonce 1768621811641
:handles
{:start
{:id "start"
:canBind true
:point [24.68 0]
:bindingId "binding-start-001"}
:end
{:id "end"
:canBind true
:point [0 144]
:bindingId "binding-end-001"}}
:decorations
{:start "arrow" ; arrow at both ends
:end "arrow"}}}
:block/updated-at 1768621811641}
Here's a minimal complete whiteboard showing two boxes connected by a line:
{:blocks (
; First box
{:block/created-at 1768621679114
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "box-001"
:type "box"
:label "Box A"
:point [640 544]
:size [392 128]
:borderRadius 2
:stroke ""
:strokeWidth 3.2
:strokeType "line"
:fill ""
:noFill true
:opacity 1
:fontSize 32
:fontWeight 400
:italic false
:scale [1 1]
:scaleLevel "lg"
:parentId "page-uuid-001"
:index 0
:nonce 1768621679114}}
:block/updated-at 1768621679114}
; Second box
{:block/created-at 1768621811640
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "box-002"
:type "box"
:label "Box B"
:point [592 824]
:size [392 128]
:borderRadius 2
:stroke "blue"
:strokeWidth 3.2
:strokeType "line"
:fill "blue"
:noFill false
:opacity 1
:fontSize 32
:fontWeight 400
:italic false
:refs []
:scale [1 1]
:scaleLevel "lg"
:parentId "page-uuid-001"
:index 1
:nonce 1768621811640}}
:block/updated-at 1768621811640}
; Connecting line
{:block/created-at 1768621811641
:block/properties
{:ls-type :whiteboard-shape
:logseq.tldraw.shape
{:id "line-001"
:type "line"
:point [800 676]
:strokeType "line"
:strokeWidth 1
:stroke ""
:fill ""
:noFill true
:opacity 1
:label ""
:fontSize 20
:fontWeight 700
:italic false
:refs []
:parentId "page-uuid-001"
:index 2
:nonce 1768621811641
:handles
{:start
{:id "start"
:canBind true
:point [24 0]
:bindingId "binding-start-001"}
:end
{:id "end"
:canBind true
:point [0 144]
:bindingId "binding-end-001"}}
:decorations
{:start "arrow"
:end "arrow"}}}
:block/updated-at 1768621811641})
:pages (
{:block/uuid #uuid "page-uuid-001"
:block/properties
{:ls-type :whiteboard-page
:logseq.tldraw.page
{:id "page-uuid-001"
:name "my-connected-diagram"
:bindings
{; Line connects box-001 at start
:binding-start-001
{:id "binding-start-001"
:type "line"
:fromId "line-001"
:toId "box-001"
:handleId "start"
:point [0.5 0.5] ; center attachment
:distance 4}
; Line connects box-002 at end
:binding-end-001
{:id "binding-end-001"
:type "line"
:fromId "line-001"
:toId "box-002"
:handleId "end"
:point [0.5 0.5]
:distance 4}}
:nonce 1
:assets []
:shapes-index ["box-001" "box-002" "line-001"]}}
:block/updated-at 1768621811641
:block/created-at 1768621679114
:block/type "whiteboard"
:block/name "my-connected-diagram"
:block/original-name "my-connected-diagram"})}
:size [0 0]
:strokeType "line"
:strokeWidth 2
:opacity 1
:id "shape-004"
:point [350 260]
:parentId "page-001-uuid"
:nonce 1768611080000}}
:block/updated-at 1768611080000})
:pages ( {:block/uuid #uuid "550e8400-e29b-41d4-a716-446655440000" :block/properties {:ls-type :whiteboard-page :logseq.tldraw.page {:id "page-001-uuid" :name "page-001-uuid" :bindings {} :nonce 1 :assets [] :shapes-index ["shape-001" "shape-002" "shape-003" "shape-004"]}} :block/updated-at 1768611080000 :block/created-at 1768611047550 :block/type "whiteboard" :block/name "my-diagram" :block/original-name "my-diagram"})}
---
## Part 4: Color Reference
Common color hex values for whiteboard shapes:
| Color | Hex | Usage |
|-------|-----|-------|
| Red | `#f44336` | Warnings, errors, critical paths |
| Blue | `#2196f3` | Primary concepts, information |
| Green | `#4caf50` | Success, completed tasks |
| Yellow | `#ffc107` | Caution, attention needed |
| Orange | `#ff9800` | Secondary emphasis |
| Purple | `#9c27b0` | Special topics, ideas |
| Gray | `#9e9e9e` | Neutral, disabled states |
| Light Blue | `#e3f2fd` | Soft backgrounds |
| Light Orange | `#fff3e0` | Soft backgrounds |
---
## Part 5: Best Practices for EDN Editing
### Validation Rules
1. **UUID Format**: Must be valid v4 format with hyphens
- Valid: `#uuid "550e8400-e29b-41d4-a716-446655440000"`
- Invalid: `#uuid "550e8400e29b41d4a716446655440000"` (no hyphens)
2. **Map Closing**: Every opening `{` must have matching `}`
- Track opening/closing brackets carefully
- Use proper indentation for readability
3. **Keyword Consistency**: Keywords must start with `:` and use kebab-case
- Valid: `:ls-type`, `:block/uuid`, `:logseq.tldraw.page`
- Invalid: `:lsType`, `:block_uuid`
4. **Vectors**: Coordinates and sizes must be in `[x y]` format
- Valid: `:point [100 200]`, `:size [300 150]`
- Invalid: `:point {x: 100, y: 200}`
5. **String Escaping**: Escape special characters in text
- Valid: `"Quote: \"Hello\""`
- Invalid: `"Quote: "Hello""`
### Editing Tips
- **Use a JSON/EDN formatter**: Many tools can validate EDN syntax
- **Test incrementally**: Create simple shapes first, then add complexity
- **Copy-paste patterns**: Use existing shapes as templates
- **Maintain index order**: `:shapes-index` should list shapes in Z-order (back to front)
- **Comments are safe**: Use `;` to add notes without breaking syntax
### Common Mistakes to Avoid
```clojure
; ❌ WRONG: Missing closing bracket
{:block/created-at 1768611047550
:block/properties {...
; ❌ WRONG: Unmatched quotes
:text "Unmatched quote
; ❌ WRONG: Missing colons on keywords
{block/uuid #uuid "..."
; ✅ CORRECT: Properly formatted
{:block/created-at 1768611047550
:block/properties {...}}
.edn file in whiteboards/ directory:shapes-index with all shape IDs in Z-order.edn file:blocks vector:shapes-indexWhiteboard not appearing in LogSeq?
:feature/enable-whiteboards? true in logseq/config.ednwhiteboards/ directory with .edn extension:id in :logseq.tldraw.page is the actual UUID string (e.g., "550e8400-e29b-41d4-a716-446655440004"), NOT a placeholder like "page-001-uuid":parentId values match the page :id exactlyShapes not visible?
:point and :size are within viewport bounds:opacity is > 0:shapes-index array:type is a valid shape type:parentId matches page :id in :logseq.tldraw.pageLines/Arrows not connecting to shapes?
:parentId in ALL shapes equals the page :id (the actual UUID string)
:parentId "page-001-uuid" when page :id "550e8400...":parentId "550e8400-e29b-41d4-a716-446655440004" matching page :id:bindings map with matching :bindingId from handles:toId references valid shape IDs that exist in :blocks:point + handle :point = target position)Styling not applied?
:fontSize is reasonable (10-100 pixels typical):fontWeight is valid (400, 700, etc.):scale is [1 1] for normal displaylogseq/config.edn for configuration options